1 13 14 package mondrian.rolap.sql; 15 16 import java.io.PrintWriter ; 17 import java.io.StringWriter ; 18 import java.sql.DatabaseMetaData ; 19 import java.sql.SQLException ; 20 import java.sql.Connection ; 21 import java.util.ArrayList ; 22 import java.util.Iterator ; 23 import java.util.List ; 24 25 import mondrian.olap.MondrianDef; 26 import mondrian.olap.MondrianProperties; 27 import mondrian.olap.Util; 28 29 import org.eigenbase.util.property.Property; 30 import org.eigenbase.util.property.Trigger; 31 32 import javax.sql.DataSource ; 33 34 83 public class SqlQuery { 84 85 private static boolean generateFormattedSql = 86 MondrianProperties.instance().GenerateFormattedSql.get(); 87 88 static { 89 MondrianProperties.instance().GenerateFormattedSql.addTrigger( 93 new Trigger() { 94 public boolean isPersistent() { 95 return true; 96 } 97 public int phase() { 98 return Trigger.PRIMARY_PHASE; 99 } 100 public void execute(Property property, String value) { 101 generateFormattedSql = property.booleanValue(); 102 } 103 } 104 ); 105 } 106 107 private boolean distinct; 108 109 private final ClauseList select; 110 private final ClauseList from; 111 private final ClauseList where; 112 private final ClauseList groupBy; 113 private final ClauseList having; 114 private final ClauseList orderBy; 115 116 123 private final List <String > fromAliases; 124 125 126 private final Dialect dialect; 127 128 129 private final StringBuilder buf; 130 131 135 public SqlQuery(Dialect dialect) { 136 137 this.select = new ClauseList(true); 139 this.from = new ClauseList(true); 140 141 this.where = new ClauseList(false); 142 this.groupBy = new ClauseList(false); 143 this.having = new ClauseList(false); 144 this.orderBy = new ClauseList(false); 145 this.fromAliases = new ArrayList <String >(); 146 this.buf = new StringBuilder (128); 147 148 this.dialect = dialect; 149 } 150 151 157 public SqlQuery(final DatabaseMetaData databaseMetaData) { 158 this(Dialect.create(databaseMetaData)); 159 } 160 161 165 public SqlQuery cloneEmpty() 166 { 167 return new SqlQuery(dialect); 168 } 169 170 public void setDistinct(final boolean distinct) { 171 this.distinct = distinct; 172 } 173 174 178 private static final int SINGLE_QUOTE_SIZE = 10; 179 182 private static final int DOUBLE_QUOTE_SIZE = 2 * SINGLE_QUOTE_SIZE + 1; 183 184 197 public boolean addFromQuery( 198 final String query, 199 final String alias, 200 final boolean failIfExists) 201 { 202 assert alias != null; 203 204 if (fromAliases.contains(alias)) { 205 if (failIfExists) { 206 throw Util.newInternal( 207 "query already contains alias '" + alias + "'"); 208 } else { 209 return false; 210 } 211 } 212 213 buf.setLength(0); 214 215 buf.append('('); 216 buf.append(query); 217 buf.append(')'); 218 if (alias != null) { 219 Util.assertTrue(alias.length() > 0); 220 221 if (dialect.allowsAs()) { 222 buf.append(" as "); 223 } else { 224 buf.append(' '); 225 } 226 dialect.quoteIdentifier(alias, buf); 227 fromAliases.add(alias); 228 } 229 230 from.add(buf.toString()); 231 return true; 232 } 233 234 247 private boolean addFromTable( 248 final String schema, 249 final String table, 250 final String alias, 251 final String filter, 252 final boolean failIfExists) 253 { 254 if (fromAliases.contains(alias)) { 255 if (failIfExists) { 256 throw Util.newInternal( 257 "query already contains alias '" + alias + "'"); 258 } else { 259 return false; 260 } 261 } 262 263 buf.setLength(0); 264 dialect.quoteIdentifier(schema, table, buf); 265 if (alias != null) { 266 Util.assertTrue(alias.length() > 0); 267 268 if (dialect.allowsAs()) { 269 buf.append(" as "); 270 } else { 271 buf.append(' '); 272 } 273 dialect.quoteIdentifier(alias, buf); 274 fromAliases.add(alias); 275 } 276 277 from.add(buf.toString()); 278 279 if (filter != null) { 280 addWhere("(", filter, ")"); 282 } 283 return true; 284 } 285 286 public void addFrom(final SqlQuery sqlQuery, 287 final String alias, 288 final boolean failIfExists) 289 { 290 addFromQuery(sqlQuery.toString(), alias, failIfExists); 291 } 292 293 304 public boolean addFrom(final MondrianDef.Relation relation, 305 final String alias, 306 final boolean failIfExists) 307 { 308 if (relation instanceof MondrianDef.View) { 309 final MondrianDef.View view = (MondrianDef.View) relation; 310 final String viewAlias = (alias == null) 311 ? view.getAlias() 312 : alias; 313 final String sqlString = dialect.chooseQuery(view.selects); 314 315 return addFromQuery(sqlString, viewAlias, false); 316 317 } else if (relation instanceof MondrianDef.Table) { 318 final MondrianDef.Table table = (MondrianDef.Table) relation; 319 final String tableAlias = (alias == null) 320 ? table.getAlias() 321 : alias; 322 323 return addFromTable(table.schema, table.name, tableAlias, 324 table.getFilter(), failIfExists); 325 326 } else if (relation instanceof MondrianDef.Join) { 327 final MondrianDef.Join join = (MondrianDef.Join) relation; 328 final String leftAlias = join.getLeftAlias(); 329 final String rightAlias = join.getRightAlias(); 330 331 boolean addLeft = addFrom(join.left, leftAlias, failIfExists); 332 boolean addRight = addFrom(join.right, rightAlias, failIfExists); 333 334 boolean added = addLeft || addRight; 335 if (added) { 336 buf.setLength(0); 337 338 dialect.quoteIdentifier(leftAlias, join.leftKey, buf); 339 buf.append(" = "); 340 dialect.quoteIdentifier(rightAlias, join.rightKey, buf); 341 342 addWhere(buf.toString()); 343 } 344 return added; 345 346 } else { 347 throw Util.newInternal("bad relation type " + relation); 348 } 349 } 350 351 355 public void addSelect(final String expression) { 356 if (dialect.isAS400() || dialect.isDerby()) { 360 addSelect(expression, null); 361 } else { 362 addSelect(expression, nextColumnAlias()); 363 } 364 } 365 366 public int getCurrentSelectListSize() 367 { 368 return select.size(); 369 } 370 371 public String nextColumnAlias() { 372 return "c" + select.size(); 373 } 374 375 377 public void addSelect(final String expression, final String alias) { 378 buf.setLength(0); 379 380 buf.append(expression); 381 if (alias != null) { 382 buf.append(" as "); 383 dialect.quoteIdentifier(alias, buf); 384 } 385 386 select.add(buf.toString()); 387 } 388 389 public void addWhere( 390 final String exprLeft, 391 final String exprMid, 392 final String exprRight) 393 { 394 int len = exprLeft.length() + exprMid.length() + exprRight.length(); 395 StringBuilder buf = new StringBuilder (len); 396 397 buf.append(exprLeft); 398 buf.append(exprMid); 399 buf.append(exprRight); 400 401 addWhere(buf.toString()); 402 } 403 404 public void addWhere(final String expression) 405 { 406 where.add(expression); 407 } 408 409 public void addGroupBy(final String expression) 410 { 411 groupBy.add(expression); 412 } 413 414 public void addHaving(final String expression) 415 { 416 having.add(expression); 417 } 418 419 427 public void addOrderBy( 428 String expr, 429 boolean ascending, 430 boolean prepend, 431 boolean nullable) 432 { 433 if (nullable && !dialect.isNullsCollateLast()) { 434 expr = dialect.forceNullsCollateLast(expr); 435 } 436 437 if (ascending) { 438 expr = expr + " ASC"; 439 } else { 440 expr = expr + " DESC"; 441 } 442 if (prepend) { 443 orderBy.add(0, expr); 444 } else { 445 orderBy.add(expr); 446 } 447 } 448 449 public String toString() 450 { 451 if (generateFormattedSql) { 452 StringWriter sw = new StringWriter (256); 453 PrintWriter pw = new PrintWriter (sw); 454 print(pw, ""); 455 pw.flush(); 456 return sw.toString(); 457 458 } else { 459 buf.setLength(0); 460 461 select.toBuffer(buf, 462 distinct ? "select distinct " : "select ", ", "); 463 from.toBuffer(buf, " from ", ", "); 464 where.toBuffer(buf, " where ", " and "); 465 groupBy.toBuffer(buf, " group by ", ", "); 466 having.toBuffer(buf, " having ", " and "); 467 orderBy.toBuffer(buf, " order by ", ", "); 468 469 return buf.toString(); 470 } 471 } 472 473 480 public void print(PrintWriter pw, String prefix) { 481 select.print(pw, prefix, 482 distinct ? "select distinct " : "select ", ", "); 483 from.print(pw, prefix, "from ", ", "); 484 where.print(pw, prefix, "where ", " and "); 485 groupBy.print(pw, prefix, "group by ", ", "); 486 having.print(pw, prefix, "having ", " and "); 487 orderBy.print(pw, prefix, "order by ", ", "); 488 } 489 490 public Dialect getDialect() { 491 return dialect; 492 } 493 494 public static SqlQuery newQuery(Connection jdbcConnection, String err) { 495 try { 496 final Dialect dialect = 497 Dialect.create(jdbcConnection.getMetaData()); 498 return new SqlQuery(dialect); 499 } catch (SQLException e) { 500 throw Util.newInternal(e, err); 501 } 502 } 503 504 public static SqlQuery newQuery(DataSource dataSource, String err) { 505 Connection jdbcConnection = null; 506 try { 507 jdbcConnection = dataSource.getConnection(); 508 final Dialect dialect = 509 Dialect.create(jdbcConnection.getMetaData()); 510 return new SqlQuery(dialect); 511 } catch (SQLException e) { 512 throw Util.newInternal(e, err); 513 } finally { 514 if (jdbcConnection != null) { 515 try { 516 jdbcConnection.close(); 517 } catch (SQLException e) { 518 } 520 } 521 } 522 } 523 524 private class ClauseList extends ArrayList <String > { 525 private final boolean allowDups; 526 527 ClauseList(final boolean allowDups) { 528 this.allowDups = allowDups; 529 } 530 531 532 540 public boolean add(final String element) { 541 if (allowDups || !contains(element)) { 542 return super.add(element); 543 } 544 return false; 545 } 546 547 void toBuffer(final StringBuilder buf, 548 final String first, 549 final String sep) { 550 boolean firstTime = true; 551 for (String s : this) { 552 if (firstTime) { 553 buf.append(first); 554 firstTime = false; 555 } else { 556 buf.append(sep); 557 } 558 buf.append(s); 559 } 560 } 561 562 void print(final PrintWriter pw, 563 final String prefix, 564 final String first, 565 final String sep) { 566 String subprefix = prefix + " "; 567 boolean firstTime = true; 568 for (Iterator it = iterator(); it.hasNext(); ) { 569 String s = (String ) it.next(); 570 571 if (firstTime) { 572 pw.print(prefix); 573 pw.print(first); 574 firstTime = false; 575 } else { 576 pw.print(sep); 577 } 578 pw.println(); 579 pw.print(subprefix); 580 pw.print(s); 581 } 582 if (! firstTime) { 583 pw.println(); 584 } 585 } 586 } 587 588 591 public static class Dialect { 592 private final String quoteIdentifierString; 593 private final String productName; 594 private final String productVersion; 595 596 Dialect( 597 String quoteIdentifierString, 598 String productName, 599 String productVersion) { 600 this.quoteIdentifierString = quoteIdentifierString; 601 this.productName = productName; 602 this.productVersion = productVersion; 603 } 604 605 608 public static Dialect create(final DatabaseMetaData databaseMetaData) { 609 String productName; 610 try { 611 productName = databaseMetaData.getDatabaseProductName(); 612 } catch (SQLException e1) { 613 throw Util.newInternal(e1, "while detecting database product"); 614 } 615 616 String quoteIdentifierString; 617 try { 618 quoteIdentifierString = 619 databaseMetaData.getIdentifierQuoteString(); 620 } catch (SQLException e) { 621 throw Util.newInternal(e, "while quoting identifier"); 622 } 623 624 if ((quoteIdentifierString == null) || 625 (quoteIdentifierString.trim().length() == 0)) { 626 if (productName.toUpperCase().equals("MYSQL")) { 627 quoteIdentifierString = "`"; 629 } else { 630 quoteIdentifierString = null; 632 } 633 } 634 635 String productVersion; 636 try { 637 productVersion = databaseMetaData.getDatabaseProductVersion(); 638 } catch (SQLException e11) { 639 throw Util.newInternal(e11, 640 "while detecting database product version"); 641 } 642 643 return new Dialect( 644 quoteIdentifierString, 645 productName, 646 productVersion); 647 } 648 649 658 public static Dialect create(DataSource dataSource) { 659 Connection conn = null; 660 try { 661 conn = dataSource.getConnection(); 662 return create(conn.getMetaData()); 663 } catch (SQLException e) { 664 throw Util.newInternal( 665 e, "Error while creating SQL dialect"); 666 } finally { 667 try { 668 if (conn != null) { 669 conn.close(); 670 } 671 } catch (SQLException e) { 672 } 674 } 675 } 676 677 679 public boolean isAccess() { 680 return productName.equals("ACCESS"); 681 } 682 683 public boolean isDerby() { 684 return productName.trim().toUpperCase().equals("APACHE DERBY"); 685 } 686 687 public boolean isCloudscape() { 688 return productName.trim().toUpperCase().equals("DBMS:CLOUDSCAPE"); 689 } 690 691 public boolean isDB2() { 692 return productName.startsWith("DB2"); 694 } 695 696 public boolean isAS400() { 697 return productName.startsWith("DB2 UDB for AS/400"); 699 } 700 701 public boolean isOldAS400() { 702 if (!isAS400()) { 703 return false; 704 } 705 String [] version_release = productVersion.split("\\.", 3); 709 716 return ("04".compareTo(version_release[0]) >= 0); 718 } 719 720 private String getBestName() { 723 String best; 724 if (isOracle()) { 725 best = "oracle"; 726 } else if (isMSSQL()) { 727 best = "mssql"; 728 } else if (isMySQL()) { 729 best = "mysql"; 730 } else if (isAccess()) { 731 best = "access"; 732 } else if (isPostgres()) { 733 best = "postgres"; 734 } else if (isSybase()) { 735 best = "sybase"; 736 } else if (isCloudscape() || isDerby()) { 737 best = "derby"; 738 } else if (isDB2()) { 739 best = "db2"; 740 } else if (isFirebird()) { 741 best = "firebird"; 742 } else if (isInterbase()) { 743 best = "interbase"; 744 } else if (isIngres()) { 745 best = "ingres"; 746 } else if (isLucidDB()) { 747 best = "luciddb"; 748 } else if (isTeradata()) { 749 best = "teradata"; 750 } else { 751 best = "generic"; 752 } 753 return best; 754 } 755 756 760 public String toUpper(String expr) { 761 if (isDB2() || isAccess()) 762 return "UCASE(" + expr + ")"; 763 return "UPPER(" + expr + ")"; 764 } 765 766 public String caseWhenElse(String cond, String thenExpr, String elseExpr) { 767 if (isAccess()) { 768 return "IIF(" + cond + "," + thenExpr + "," + elseExpr + ")"; 769 } 770 return "CASE WHEN " + cond + " THEN " + thenExpr + " ELSE " + elseExpr + " END"; 771 } 772 773 780 public String quoteIdentifier(final String val) { 781 int size = val.length() + SINGLE_QUOTE_SIZE; 782 StringBuilder buf = new StringBuilder (size); 783 784 quoteIdentifier(val, buf); 785 786 return buf.toString(); 787 } 788 789 796 public void quoteIdentifier(final String val, final StringBuilder buf) { 797 String q = getQuoteIdentifierString(); 798 if (q == null) { 799 buf.append(val); 801 return; 802 } 803 if (val.startsWith(q) && val.endsWith(q)) { 808 buf.append(val); 810 return; 811 } 812 813 int k = val.indexOf('.'); 814 if (k > 0) { 815 String val1 = Util.replace(val.substring(0,k), q, q + q); 817 String val2 = Util.replace(val.substring(k+1), q, q + q); 818 buf.append(q); 819 buf.append(val1); 820 buf.append(q); 821 buf.append("."); 822 buf.append(q); 823 buf.append(val2); 824 buf.append(q); 825 826 } else { 827 String val2 = Util.replace(val, q, q + q); 829 buf.append(q); 830 buf.append(val2); 831 buf.append(q); 832 } 833 } 834 835 846 public String quoteIdentifier(final String qual, final String name) { 847 int size = name.length() 850 + ((qual == null) 851 ? SINGLE_QUOTE_SIZE 852 : (qual.length() + DOUBLE_QUOTE_SIZE)); 853 StringBuilder buf = new StringBuilder (size); 854 855 quoteIdentifier(qual, name, buf); 856 857 return buf.toString(); 858 } 859 860 868 public void quoteIdentifier( 869 final String qual, 870 final String name, 871 final StringBuilder buf) 872 { 873 if (qual == null) { 874 quoteIdentifier(name, buf); 875 876 } else { 877 Util.assertTrue( 878 (qual.length() != 0), 879 "qual should probably be null, not empty"); 880 881 quoteIdentifier(qual, buf); 882 buf.append('.'); 883 quoteIdentifier(name, buf); 884 } 885 } 886 887 891 public String getQuoteIdentifierString() { 892 if (isDB2()) { 893 return ""; 894 } else { 895 return quoteIdentifierString; 896 } 897 } 898 899 906 public void quoteStringLiteral(StringBuilder buf, String s) { 907 Util.singleQuoteString(s, buf); 908 } 909 910 915 public void quoteNumericLiteral(StringBuilder buf, String value) { 916 buf.append(value); 917 } 918 919 924 public void quoteBooleanLiteral(StringBuilder buf, String value) { 925 if (!value.equalsIgnoreCase("TRUE") 930 && !(value.equalsIgnoreCase("FALSE"))) { 931 throw new NumberFormatException ( 932 "Illegal BOOLEAN literal: " + value); 933 } 934 buf.append(value); 935 } 936 937 944 public void quoteDateLiteral(StringBuilder buf, String value) { 945 try { 951 java.sql.Date.valueOf(value); 952 } catch (IllegalArgumentException ex) { 953 throw new NumberFormatException ( 954 "Illegal DATE literal: " + value); 955 } 956 buf.append("DATE "); 957 Util.singleQuoteString(value, buf); 958 } 959 960 967 public void quoteTimeLiteral(StringBuilder buf, String value) { 968 try { 970 java.sql.Time.valueOf(value); 971 } catch (IllegalArgumentException ex) { 972 throw new NumberFormatException ( 973 "Illegal TIME literal: " + value); 974 } 975 buf.append("TIME "); 976 Util.singleQuoteString(value, buf); 977 } 978 979 986 public void quoteTimestampLiteral(StringBuilder buf, String value) { 987 try { 989 java.sql.Timestamp.valueOf(value); 990 } catch (IllegalArgumentException ex) { 991 throw new NumberFormatException ( 992 "Illegal TIMESTAMP literal: " + value); 993 } 994 buf.append("TIMESTAMP "); 995 Util.singleQuoteString(value, buf); 996 } 997 998 1001 public boolean isFirebird() { 1002 return productName.toUpperCase().indexOf("FIREBIRD") >= 0; 1003 } 1004 1005 1008 public boolean isInformix() { 1009 return productName.startsWith("Informix"); 1010 } 1011 1012 1015 public boolean isIngres() { 1016 return productName.toUpperCase().equals("INGRES"); 1017 } 1018 1019 1022 public boolean isInterbase() { 1023 return productName.equals("Interbase"); 1024 } 1025 1026 1029 public boolean isLucidDB() { 1030 return productName.toUpperCase().equals("LUCIDDB"); 1031 } 1032 1033 1036 public boolean isMSSQL() { 1037 return productName.toUpperCase().indexOf("SQL SERVER") >= 0; 1038 } 1039 1040 1043 public boolean isOracle() { 1044 return productName.equals("Oracle"); 1045 } 1046 1047 1050 public boolean isPostgres() { 1051 return productName.toUpperCase().indexOf("POSTGRE") >= 0; 1052 } 1053 1054 1057 public boolean isMySQL() { 1058 return productName.toUpperCase().equals("MYSQL"); 1059 } 1060 1061 1064 public boolean isSybase() { 1065 return productName.toUpperCase().indexOf("SYBASE") >= 0; 1066 } 1067 1068 1071 public boolean isTeradata() { 1072 return productName.toUpperCase().indexOf("SYBASE") >= 0; 1073 } 1074 1075 protected boolean requiresAliasForFromItems() { 1077 return isPostgres(); 1078 } 1079 1080 1084 protected boolean allowsAs() { 1085 return !isOracle() && !isSybase() && !isFirebird() && 1086 !isInterbase(); 1087 } 1088 1089 1092 public boolean allowsFromQuery() { 1093 return !isMySQL() && !isOldAS400() && !isInformix() && 1095 !isSybase() && !isInterbase(); 1096 } 1097 1098 1101 public boolean allowsCompoundCountDistinct() { 1102 return isMySQL(); 1103 } 1104 1105 1113 public boolean allowsCountDistinct() { 1114 return !isAccess(); 1115 } 1116 1117 1134 public boolean allowsMultipleCountDistinct() { 1135 return allowsCountDistinct() && 1136 !isDerby(); 1137 } 1138 1139 1144 public String chooseQuery(final MondrianDef.SQL[] sqls) { 1145 String best = getBestName(); 1146 1147 String generic = null; 1148 for (MondrianDef.SQL sql : sqls) { 1149 if (sql.dialect.equals(best)) { 1150 return sql.cdata; 1151 } 1152 if (sql.dialect.equals("generic")) { 1153 generic = sql.cdata; 1154 } 1155 } 1156 if (generic == null) { 1157 throw Util.newError("View has no 'generic' variant"); 1158 } 1159 return generic; 1160 } 1161 1162 1184 public String generateInline( 1185 List <String > columnNames, 1186 List <String > columnTypes, 1187 List <String []> valueList) { 1188 if (isOracle()) { 1189 return generateInlineGeneric( 1190 columnNames, columnTypes, valueList, 1191 "from dual"); 1192 } else if (isAccess()) { 1193 return generateInlineGeneric( 1196 columnNames, columnTypes, valueList, 1197 "from [days] where [day] = 1"); 1198 } else if (isMySQL() || isIngres()) { 1199 return generateInlineGeneric( 1200 columnNames, columnTypes, valueList, null); 1201 } else if (isLucidDB()) { 1202 return generateInlineGeneric( 1206 columnNames, columnTypes, valueList, 1207 " from (values(0))"); 1208 } else { 1209 return generateInlineForAnsi( 1210 "t", columnNames, columnTypes, valueList); 1211 } 1212 } 1213 1214 1219 private String generateInlineGeneric( 1220 List <String > columnNames, 1221 List <String > columnTypes, 1222 List <String []> valueList, 1223 String fromClause) { 1224 final StringBuilder buf = new StringBuilder (); 1225 for (int i = 0; i < valueList.size(); i++) { 1226 if (i > 0) { 1227 buf.append(" union all "); 1228 } 1229 String [] values = valueList.get(i); 1230 buf.append("select "); 1231 for (int j = 0; j < values.length; j++) { 1232 String value = values[j]; 1233 if (j > 0) { 1234 buf.append(", "); 1235 } 1236 final String columnType = columnTypes.get(j); 1237 final String columnName = columnNames.get(j); 1238 Datatype datatype = Datatype.valueOf(columnType); 1239 quote(buf, value, datatype); 1240 if (allowsAs()) { 1241 buf.append(" as "); 1242 } else { 1243 buf.append(' '); 1244 } 1245 quoteIdentifier(columnName, buf); 1246 } 1247 if (fromClause != null) { 1248 buf.append(fromClause); 1249 } 1250 } 1251 return buf.toString(); 1252 } 1253 1254 1271 private String generateInlineForAnsi( 1272 String alias, 1273 List <String > columnNames, 1274 List <String > columnTypes, 1275 List <String []> valueList) { 1276 final StringBuilder buf = new StringBuilder (); 1277 buf.append("SELECT * FROM (VALUES "); 1278 String [] castTypes = null; 1282 if (isDerby()) { 1283 castTypes = new String [columnNames.size()]; 1284 for (int i = 0; i < columnNames.size(); i++) { 1285 String columnType = columnTypes.get(i); 1286 if (columnType.equals("String")) { 1287 castTypes[i] = 1288 guessSqlType(columnType, valueList, i); 1289 } 1290 } 1291 } 1292 for (int i = 0; i < valueList.size(); i++) { 1293 if (i > 0) { 1294 buf.append(", "); 1295 } 1296 String [] values = valueList.get(i); 1297 buf.append("("); 1298 for (int j = 0; j < values.length; j++) { 1299 String value = values[j]; 1300 if (j > 0) { 1301 buf.append(", "); 1302 } 1303 final String columnType = columnTypes.get(j); 1304 Datatype datatype = Datatype.valueOf(columnType); 1305 if (value == null) { 1306 String sqlType = 1307 guessSqlType(columnType, valueList, j); 1308 buf.append("CAST(NULL AS ") 1309 .append(sqlType) 1310 .append(")"); 1311 } else if (isDerby() && castTypes[j] != null) { 1312 buf.append("CAST("); 1313 quote(buf, value, datatype); 1314 buf.append(" AS ") 1315 .append(castTypes[j]) 1316 .append(")"); 1317 } else { 1318 quote(buf, value, datatype); 1319 } 1320 } 1321 buf.append(")"); 1322 } 1323 buf.append(") AS "); 1324 quoteIdentifier(alias, buf); 1325 buf.append(" ("); 1326 for (int j = 0; j < columnNames.size(); j++) { 1327 final String columnName = columnNames.get(j); 1328 if (j > 0) { 1329 buf.append(", "); 1330 } 1331 quoteIdentifier(columnName, buf); 1332 } 1333 buf.append(")"); 1334 return buf.toString(); 1335 } 1336 1337 1340 public void quote(StringBuilder buf, Object value, Datatype datatype) { 1341 if (value == null) { 1342 buf.append("null"); 1343 } else { 1344 datatype.quoteValue(buf, this, value.toString()); 1345 } 1346 } 1347 1348 1352 private static String guessSqlType( 1353 String basicType, List <String []> valueList, int column) { 1354 if (basicType.equals("String")) { 1355 int maxLen = 1; 1356 for (String [] values : valueList) { 1357 final String value = values[column]; 1358 if (value == null) { 1359 continue; 1360 } 1361 maxLen = Math.max(maxLen, value.length()); 1362 } 1363 return "VARCHAR(" + maxLen + ")"; 1364 } else { 1365 return "INTEGER"; 1366 } 1367 } 1368 1369 1374 public boolean allowsDdl() { 1375 return !isAccess(); 1376 } 1377 1378 1382 public boolean isNullsCollateLast() { 1383 if (isMySQL()) { 1384 return false; 1385 } 1386 return true; 1387 } 1388 1389 1394 public String forceNullsCollateLast(String expr) { 1395 if (isMySQL()) { 1398 String addIsNull = "ISNULL(" + expr + "), "; 1399 expr = addIsNull + expr; 1400 } 1401 return expr; 1402 } 1403 1404 1411 public boolean supportsGroupByExpressions() { 1412 return !(isDerby() || isCloudscape()); 1413 } 1414 1415 1437 public boolean requiresOrderByAlias() { 1438 return isMySQL() || isDB2() || isIngres(); 1439 } 1440 1441 1449 public boolean supportsMultiValueInExpr() { 1450 return isLucidDB() || isMySQL(); 1451 } 1452 } 1453 1454 1457 public enum Datatype { 1458 String { 1459 public void quoteValue(StringBuilder buf, Dialect dialect, String value) { 1460 dialect.quoteStringLiteral(buf, value); 1461 } 1462 }, 1463 1464 Numeric { 1465 public void quoteValue(StringBuilder buf, Dialect dialect, String value) { 1466 dialect.quoteNumericLiteral(buf, value); 1467 } 1468 1469 public boolean isNumeric() { 1470 return true; 1471 } 1472 }, 1473 1474 Integer { 1475 public void quoteValue(StringBuilder buf, Dialect dialect, String value) { 1476 dialect.quoteNumericLiteral(buf, value); 1477 } 1478 1479 public boolean isNumeric() { 1480 return true; 1481 } 1482 }, 1483 1484 Boolean { 1485 public void quoteValue(StringBuilder buf, Dialect dialect, String value) { 1486 dialect.quoteBooleanLiteral(buf, value); 1487 } 1488 }, 1489 1490 Date { 1491 public void quoteValue(StringBuilder buf, Dialect dialect, String value) { 1492 dialect.quoteDateLiteral(buf, value); 1493 } 1494 }, 1495 1496 Time { 1497 public void quoteValue(StringBuilder buf, Dialect dialect, String value) { 1498 dialect.quoteTimeLiteral(buf, value); 1499 } 1500 }, 1501 1502 Timestamp { 1503 public void quoteValue(StringBuilder buf, Dialect dialect, String value) { 1504 dialect.quoteTimestampLiteral(buf, value); 1505 } 1506 }; 1507 1508 1516 public abstract void quoteValue( 1517 StringBuilder buf, 1518 Dialect dialect, 1519 String value); 1520 1521 public boolean isNumeric() { 1522 return false; 1523 } 1524 } 1525} 1526 1527 | Popular Tags |