1 21 22 package org.apache.derbyBuild; 23 24 import java.io.IOException ; 25 import java.io.InputStream ; 26 import java.io.InputStreamReader ; 27 import java.io.LineNumberReader ; 28 import java.io.FileWriter ; 29 30 import java.util.Properties ; 31 import java.util.HashMap ; 32 import java.util.ArrayList ; 33 34 import org.apache.derby.iapi.services.sanity.SanityManager; 35 36 57 public class ODBCMetadataGenerator { 58 59 private final byte COL_RENAME_CHANGE = 0x01; 74 private final byte TYPE_VALUE_CHANGE = 0x02; 75 private final byte ADD_COLUMN_CHANGE = 0x04; 76 77 private final String ODBC_QUERY_NOTICE = 79 "#\n# *** NOTE! *** The following query was generated\n" + 80 "# AUTOMATICALLY at build time based on the existing\n" + 81 "# JDBC version of the query. DO NOT MODIFY this\n" + 82 "# generated query by hand. Instead, modify either\n" + 83 "# 1) the JDBC version of the query in the codeline \n" + 84 "# file \"metadata.properties\" (which will then get\n" + 85 "# propagated at build time), 2) the relevant SQL\n" + 86 "# fragments in 'odbcgen_fragments.properties' in\n" + 87 "# the codleine, or 3) the ODBCMetadataGenerator\n" + 88 "# class in the org/apache/derbyBuild directory.\n"; 89 90 private final String ODBC_QUERY_PREFIX = "odbc_"; 94 95 private final String SUBQUERY_NAME = "JDBC_SUBQUERY"; 100 101 private final String NEW_COL_PLACEHOLDER = "COLUMN_POSITION_HOLDER"; 103 104 private final short FOLLOWING = 1; 106 private final short PRECEDING = -1; 107 108 private HashMap changeMap; 111 112 private Properties odbcFragments; 117 118 private FileWriter odbcMetaFile; 124 125 130 public ODBCMetadataGenerator() throws IOException { 131 132 odbcFragments = new Properties (); 134 odbcFragments.load(this.getClass().getResourceAsStream( 135 "odbcgen_fragments.properties")); 136 137 odbcMetaFile = new FileWriter ("odbc_metadata.properties"); 139 140 } 141 142 154 public static void main(String [] args) throws IOException { 155 156 ODBCMetadataGenerator odbcGen = new ODBCMetadataGenerator(); 157 odbcGen.initChanges(); 158 odbcGen.generateODBCQueries(odbcGen.getClass().getResourceAsStream( 159 "/org/apache/derby/impl/jdbc/metadata.properties")); 160 161 } 162 163 176 private void initChanges() { 177 178 changeMap = new HashMap (); 179 180 changeMap.put("getProcedures", 181 new Byte (COL_RENAME_CHANGE)); 182 183 changeMap.put("getProcedureColumns", 184 new Byte ((byte)(COL_RENAME_CHANGE 185 | TYPE_VALUE_CHANGE 186 | ADD_COLUMN_CHANGE))); 187 188 changeMap.put("getColumns", 189 new Byte (TYPE_VALUE_CHANGE)); 190 191 changeMap.put("getVersionColumns", 192 new Byte (TYPE_VALUE_CHANGE)); 193 194 changeMap.put("getBestRowIdentifierPrimaryKeyColumns", 195 new Byte (TYPE_VALUE_CHANGE)); 196 197 changeMap.put("getBestRowIdentifierUniqueKeyColumns", 198 new Byte (TYPE_VALUE_CHANGE)); 199 200 changeMap.put("getBestRowIdentifierUniqueIndexColumns", 201 new Byte (TYPE_VALUE_CHANGE)); 202 203 changeMap.put("getBestRowIdentifierAllColumns", 204 new Byte (TYPE_VALUE_CHANGE)); 205 206 changeMap.put("getTypeInfo", 207 new Byte ((byte)(COL_RENAME_CHANGE 208 | TYPE_VALUE_CHANGE 209 | ADD_COLUMN_CHANGE))); 210 211 changeMap.put("getIndexInfo", 212 new Byte (TYPE_VALUE_CHANGE)); 213 214 return; 215 216 } 217 218 225 public void generateODBCQueries(InputStream is) 226 throws IOException 227 { 228 229 StringBuffer query = new StringBuffer (); 231 232 LineNumberReader reader = 235 new LineNumberReader (new InputStreamReader (is, "ISO-8859-1")); 236 237 String line = null; 238 for (line = reader.readLine(); line != null; 239 line = reader.readLine()) 240 { 241 242 if (line.length() == 0) 243 continue; 245 else if (line.charAt(0) == '#') { 246 odbcMetaFile.write(line); 248 odbcMetaFile.write("\n"); 249 continue; 250 } 251 252 query.append(line); 255 query.append("\n"); 256 257 boolean done = true; 259 for (int lastNonWS = line.length() - 1; 260 lastNonWS >= 0; lastNonWS--) 261 { 262 char ch = line.charAt(lastNonWS); 263 if (!Character.isWhitespace(ch)) { 264 if (ch == '\\') { 268 done = false; 270 } 271 break; 272 } 273 } 274 275 if (!done) 276 continue; 278 279 generateODBCQuery(query); 282 283 query.delete(0, query.length()); 285 286 } 287 288 if (query.length() > 0) { 290 throw new IOException ( 291 "Encountered non-terminated query while reading metadata file."); 292 } 293 294 odbcMetaFile.flush(); 296 odbcMetaFile.close(); 297 298 } 299 300 309 private void generateODBCQuery(StringBuffer queryText) 310 throws IOException 311 { 312 313 String queryAsString = queryText.toString().trim(); 317 318 if (queryAsString.startsWith(ODBC_QUERY_PREFIX)) 319 return; 322 323 odbcMetaFile.write(queryAsString, 0, queryAsString.length()); 325 odbcMetaFile.write("\n\n"); 326 327 int pos = queryAsString.indexOf("="); 329 if (pos == -1) { 330 throw new IOException ( 331 "Failed to extract query name from a JDBC metadata query."); 332 } 333 String queryName = queryText.substring(0, pos); 334 335 String orderBy = ""; 338 int orderByPos = queryAsString.lastIndexOf("ORDER BY"); 339 if (orderByPos != -1) 340 orderBy = queryAsString.substring(orderByPos, queryAsString.length()); 341 342 if (orderByPos != -1) 345 queryText.delete(orderByPos, queryText.length()); 346 queryText.delete(0, pos+1); 347 348 350 StringBuffer outerQueryText = new StringBuffer (); 352 boolean haveODBCChanges = renameColsForODBC(queryName, queryText); 353 354 ArrayList colDefs = new ArrayList (); 357 pos = getSelectColDefinitions(queryText, colDefs); 358 359 addHelperColsToSubquery(queryName, queryText, pos); 363 364 markNewColPosition(queryName, colDefs); 367 368 generateSELECTClause(queryName, colDefs, outerQueryText); 372 373 changeValuesForODBC(queryName, outerQueryText); 375 376 addNewColumnsForODBC(queryName, outerQueryText); 378 379 haveODBCChanges = (haveODBCChanges || (outerQueryText.length() > 0)); 380 if (!haveODBCChanges) 381 return; 383 384 386 odbcMetaFile.write(ODBC_QUERY_NOTICE); 387 odbcMetaFile.write(ODBC_QUERY_PREFIX); 388 odbcMetaFile.write(queryName); 389 odbcMetaFile.write("="); 390 391 if (outerQueryText.length() == 0) { 392 odbcMetaFile.write(queryText.toString()); 395 odbcMetaFile.write("\n\n"); 396 return; 397 } 398 399 queryAsString = queryText.toString().trim(); 402 odbcMetaFile.write(outerQueryText.toString()); 403 odbcMetaFile.write(queryAsString); 404 if (queryText.charAt(queryAsString.length()-1) == '\\') 405 odbcMetaFile.write("\n\\\n) "); 406 else 407 odbcMetaFile.write(" \\\n\\\n) "); 408 odbcMetaFile.write(SUBQUERY_NAME); 409 if (orderBy.length() == 0) 410 odbcMetaFile.write("\n"); 411 else { 412 odbcMetaFile.write(" \\\n"); 414 odbcMetaFile.write(orderBy); 415 } 416 odbcMetaFile.write("\n\n"); 417 return; 418 419 } 420 421 431 private boolean renameColsForODBC(String queryName, StringBuffer queryText) { 432 433 if (!stmtNeedsChange(queryName, COL_RENAME_CHANGE)) 436 return false; 437 438 441 if (queryName.equals("getProcedures")) { 442 renameColForODBC(queryText, "RESERVED1", "NUM_INPUT_PARAMS"); 443 renameColForODBC(queryText, "RESERVED2", "NUM_OUTPUT_PARAMS"); 444 renameColForODBC(queryText, "RESERVED3", "NUM_RESULT_SETS"); 445 return true; 446 } 447 else if (queryName.equals("getProcedureColumns")) { 448 renameColForODBC(queryText, "PRECISION", "COLUMN_SIZE"); 449 renameColForODBC(queryText, "LENGTH", "BUFFER_LENGTH"); 450 renameColForODBC(queryText, "SCALE", "DECIMAL_DIGITS"); 451 renameColForODBC(queryText, "RADIX", "NUM_PREC_RADIX"); 452 return true; 453 } 454 else if (queryName.equals("getTypeInfo")) { 455 renameColForODBC(queryText, "PRECISION", "COLUMN_SIZE"); 456 renameColForODBC(queryText, "AUTO_INCREMENT", "AUTO_UNIQUE_VAL"); 457 return true; 458 } 459 460 return false; 462 463 } 464 465 479 private void renameColForODBC(StringBuffer queryText, 480 String oldVal, String newVal) 481 { 482 483 String queryString = queryText.toString(); 484 int pos = queryString.indexOf(oldVal); 485 while (pos != -1) { 486 487 int pos2 = trimIgnorable(PRECEDING, queryString, pos); 494 if (((pos2 - 2) > 0) && (queryString.charAt(pos2-2) == 'A') 495 && (queryString.charAt(pos2-1) == 'S')) 496 { break; 498 } 499 else { 500 pos = queryString.indexOf(oldVal, pos+1); 502 } 503 504 } 505 506 if (pos == -1) { 507 return; 509 } 510 511 queryText.replace(pos, pos + oldVal.length(), newVal); 513 514 } 515 516 542 private void generateSELECTClause(String queryName, 543 ArrayList selectColDefs, StringBuffer newQueryText) 544 { 545 546 if (!stmtNeedsChange(queryName, TYPE_VALUE_CHANGE) && 547 !stmtNeedsChange(queryName, ADD_COLUMN_CHANGE)) 548 { return; 551 } 552 553 newQueryText.append("SELECT \\\n\\\n"); 555 556 String colName; 561 String castInfo; 562 for (int i = 0; i < selectColDefs.size(); i++) { 563 if (i > 0) 564 newQueryText.append(", \\\n"); 565 colName = extractColName((String )selectColDefs.get(i)); 566 castInfo = getCastInfoForCol(queryName, colName); 567 if (castInfo != null) 568 newQueryText.append("CAST ("); 569 newQueryText.append(SUBQUERY_NAME); 570 newQueryText.append("."); 571 newQueryText.append(colName); 572 if (castInfo != null) { 573 newQueryText.append(" AS "); 574 newQueryText.append(castInfo); 575 newQueryText.append(")"); 576 } 577 if (!colName.equals(NEW_COL_PLACEHOLDER)) { 578 newQueryText.append(" AS "); 581 newQueryText.append(colName); 582 } 583 } 584 585 if (newQueryText.charAt(newQueryText.length() - 1) != '\\') 586 newQueryText.append(" \\"); 587 588 newQueryText.append("\nFROM ( "); 590 return; 591 592 } 593 594 607 private void changeValuesForODBC(String queryName, 608 StringBuffer newQueryText) 609 { 610 611 if (!stmtNeedsChange(queryName, TYPE_VALUE_CHANGE)) 612 return; 613 614 617 if (queryName.equals("getColumns")) { 618 changeColValueToODBC(queryName, "BUFFER_LENGTH", newQueryText); 619 changeColValueToODBC(queryName, "DECIMAL_DIGITS", newQueryText); 620 changeColValueToODBC(queryName, "NUM_PREC_RADIX", newQueryText); 621 changeColValueToODBC(queryName, "SQL_DATA_TYPE", newQueryText); 622 changeColValueToODBC(queryName, "SQL_DATETIME_SUB", newQueryText); 623 changeColValueToODBC(queryName, "CHAR_OCTET_LENGTH", newQueryText); 624 } 625 else if (queryName.startsWith("getBestRowIdentifier")) { 626 changeColValueToODBC(queryName, "BUFFER_LENGTH", newQueryText); 627 changeColValueToODBC(queryName, "DECIMAL_DIGITS", newQueryText); 628 } 629 else if (queryName.equals("getTypeInfo")) { 630 changeColValueToODBC(queryName, "NUM_PREC_RADIX", newQueryText); 631 changeColValueToODBC(queryName, "SQL_DATA_TYPE", newQueryText); 632 changeColValueToODBC(queryName, "SQL_DATETIME_SUB", newQueryText); 633 changeColValueToODBC(queryName, "UNSIGNED_ATTRIBUTE", newQueryText); 634 changeColValueToODBC(queryName, "AUTO_UNIQUE_VAL", newQueryText); 635 } 636 else if (queryName.equals("getProcedureColumns")) { 637 changeColValueToODBC(queryName, "NUM_PREC_RADIX", newQueryText); 638 changeColValueToODBC(queryName, "DECIMAL_DIGITS", newQueryText); 639 } 640 641 } 642 643 654 private void changeColValueToODBC(String queryName, String colName, 655 StringBuffer newQueryText) 656 { 657 658 colName = SUBQUERY_NAME + "." + colName; 659 int pos = newQueryText.toString().indexOf(colName); 660 if (pos == -1) 661 return; 663 664 if (colName.endsWith("CHAR_OCTET_LENGTH")) { 665 newQueryText.replace(pos, pos + colName.length(), 666 getFragment("CHAR_OCTET_FOR_ODBC")); 667 } 668 else if (colName.endsWith("BUFFER_LENGTH")) { 669 newQueryText.replace(pos, pos + colName.length(), 670 getFragment("BUFFER_LEN_FOR_ODBC")); 671 } 672 else if (colName.endsWith("SQL_DATA_TYPE")) { 673 newQueryText.replace(pos, pos + colName.length(), 674 getFragment("SQL_DATA_TYPE_FOR_ODBC")); 675 } 676 else if (colName.endsWith("SQL_DATETIME_SUB")) { 677 newQueryText.replace(pos, pos + colName.length(), 678 getFragment("DATETIME_SUB_FOR_ODBC")); 679 } 680 else if (colName.endsWith("UNSIGNED_ATTRIBUTE")) { 681 newQueryText.replace(pos, pos + colName.length(), 682 getFragment("UNSIGNED_ATTR_FOR_ODBC")); 683 } 684 else if (colName.endsWith("AUTO_UNIQUE_VAL")) { 685 newQueryText.replace(pos, pos + colName.length(), 686 getFragment("AUTO_UNIQUE_FOR_ODBC")); 687 } 688 else if (colName.endsWith("DECIMAL_DIGITS")) { 689 newQueryText.replace(pos, pos + colName.length(), 690 getFragment("DECIMAL_DIGITS_FOR_ODBC")); 691 } 692 else if (colName.endsWith("NUM_PREC_RADIX")) { 693 newQueryText.replace(pos, pos + colName.length(), 694 getFragment("RADIX_FOR_ODBC")); 695 } 696 else if (colName.endsWith(NEW_COL_PLACEHOLDER)) { 697 if (queryName.equals("getProcedureColumns")) { 699 newQueryText.replace(pos, pos + colName.length(), 700 getFragment("GET_PROC_COLS_NEW_COLS")); 701 } 702 else if (queryName.equals("getTypeInfo")) { 703 newQueryText.replace(pos, pos + colName.length(), 704 getFragment("GET_TYPE_INFO_NEW_COLS")); 705 } 706 } 707 708 } 709 710 728 private int getSelectColDefinitions(StringBuffer queryText, 729 ArrayList colDefList) 730 { 731 732 String query = queryText.toString().trim(); 736 char [] queryChars = query.toCharArray(); 737 738 int start = query.indexOf("SELECT"); 740 if (start != -1) 741 start += 6; 743 else 744 start = 0; 746 747 int fromClauseIndex = -1; 750 int parenDepth = 0; 751 for (int i = start; i < queryChars.length; i++) { 752 753 if (queryChars[i] == '(') 754 parenDepth++; 755 else if (queryChars[i] == ')') 756 parenDepth--; 757 else if ((queryChars[i] == ',') && (parenDepth == 0)) { 758 colDefList.add(new String (queryChars, start, (i - start)).trim()); 763 i = trimIgnorable(FOLLOWING, queryChars, i); 767 start = i + 1; 768 } 769 else if (((i+3) < queryChars.length) 770 && (parenDepth == 0) 771 && (queryChars[i] == 'F') 772 && (queryChars[i+1] == 'R') 773 && (queryChars[i+2] == 'O') 774 && (queryChars[i+3] == 'M')) 775 { i = trimIgnorable(PRECEDING, queryChars, i); 781 fromClauseIndex = i; 782 colDefList.add(new String (queryChars, start, (i - start)).trim()); 783 break; 784 785 } 786 787 } 788 789 return fromClauseIndex; 790 791 } 792 793 829 private void addHelperColsToSubquery(String queryName, 830 StringBuffer subqueryText, int insertPos) 831 { 832 833 if (queryName.equals("getColumns")) { 834 subqueryText.insert(insertPos, 835 getFragment("GET_COLS_HELPER_COLS")); 836 } 837 else if (queryName.startsWith("getBestRowIdentifier")) { 838 subqueryText.insert(insertPos, 839 getFragment("BEST_ROW_ID_HELPER_COLS")); 840 } 841 842 } 843 844 865 private String extractColName(String colDef) { 866 867 int pos = colDef.lastIndexOf("AS "); 869 if (pos == -1) { 870 pos = 0; 873 } 874 else { 875 pos += 2; 877 878 char c = colDef.charAt(pos); 880 while ((c == '\\') || Character.isWhitespace(c)) 881 c = colDef.charAt(++pos); 882 } 883 884 int pos2 = colDef.indexOf(".", pos); 886 if (pos2 == -1) 887 return colDef.substring(pos, colDef.length()); 889 890 return colDef.substring(pos2+1, colDef.length()); 892 893 } 894 895 907 private String getCastInfoForCol(String queryName, 908 String colName) 909 { 910 911 if (queryName.equals("getTypeInfo")) { 912 if (colName.equals("DATA_TYPE") || 913 colName.equals("CASE_SENSITIVE") || 914 colName.equals("UNSIGNED_ATTRIBUTE") || 915 colName.equals("FIXED_PREC_SCALE") || 916 colName.equals("AUTO_UNIQUE_VAL") || 917 colName.equals("SQL_DATA_TYPE") || 918 colName.equals("SQL_DATETIME_SUB")) 919 { 920 return "SMALLINT"; 921 } 922 } 923 else if (queryName.equals("getColumns")) { 924 if (colName.equals("DECIMAL_DIGITS") || 925 colName.equals("NULLABLE") || 926 colName.equals("DATA_TYPE") || 927 colName.equals("NUM_PREC_RADIX") || 928 colName.equals("SQL_DATA_TYPE") || 929 colName.equals("SQL_DATETIME_SUB")) 930 { 931 return "SMALLINT"; 932 } 933 } 934 else if (queryName.equals("getProcedureColumns")) { 935 if (colName.equals("DATA_TYPE")) { 936 return "SMALLINT"; 937 } 938 } 939 else if (queryName.equals("getVersionColumns")) { 940 if (colName.equals("DATA_TYPE")) { 941 return "SMALLINT"; 942 } 943 } 944 else if (queryName.startsWith("getBestRowIdentifier")) { 945 if (colName.equals("DATA_TYPE")) { 946 return "SMALLINT"; 947 } 948 } 949 else if (queryName.equals("getIndexInfo")) { 950 if (colName.equals("NON_UNIQUE") || 951 colName.equals("TYPE")) 952 { 953 return "SMALLINT"; 954 } 955 } 956 957 return null; 960 961 } 962 963 982 private void markNewColPosition(String queryName, 983 ArrayList selectColDefs) 984 { 985 986 if (!stmtNeedsChange(queryName, ADD_COLUMN_CHANGE)) 987 return; 988 989 if (queryName.equals("getProcedureColumns")) { 990 selectColDefs.add(selectColDefs.size() - 2, NEW_COL_PLACEHOLDER); 995 } 996 else if (queryName.equals("getTypeInfo")) { 997 selectColDefs.add(NEW_COL_PLACEHOLDER); 999 } 1000 1001 } 1002 1003 1021 private void addNewColumnsForODBC(String queryName, 1022 StringBuffer newQueryText) 1023 { 1024 1025 if (!stmtNeedsChange(queryName, ADD_COLUMN_CHANGE)) 1026 return; 1027 1028 changeColValueToODBC(queryName, NEW_COL_PLACEHOLDER, newQueryText); 1029 1030 if (queryName.equals("getProcedureColumns")) { 1034 fragSubstitution("SQL_DATA_TYPE_FOR_ODBC", newQueryText); 1035 fragSubstitution("DATETIME_SUB_FOR_ODBC", newQueryText); 1036 } 1037 1038 return; 1039 1040 } 1041 1042 1056 private void fragSubstitution(String fragKey, 1057 StringBuffer queryText) 1058 { 1059 1060 int pos = queryText.toString().indexOf(fragKey); 1061 if (pos != -1) { 1062 queryText.replace(pos - 1, pos + fragKey.length() + 1, 1066 getFragment(fragKey)); 1067 } 1068 1069 } 1070 1071 1092 private int trimIgnorable(short direction, char [] chars, int index) { 1093 1094 index += direction; 1095 while ((index >= 0) && (index < chars.length) && 1096 ((chars[index] == '\\') || 1097 Character.isWhitespace(chars[index]))) 1098 { 1099 index += direction; 1100 } 1101 1102 index -= direction; 1107 1108 return index; 1109 1110 } 1111 1112 1117 private int trimIgnorable(short direction, String str, int index) { 1118 1119 index += direction; 1120 while ((index >= 0) && (index < str.length()) && 1121 ((str.charAt(index) == '\\') || 1122 Character.isWhitespace(str.charAt(index)))) 1123 { 1124 index += direction; 1125 } 1126 1127 index -= direction; 1132 1133 return index; 1134 1135 } 1136 1137 1145 private boolean stmtNeedsChange(String queryName, byte changeType) { 1146 1147 Byte changeByte = (Byte )changeMap.get(queryName); 1148 if (changeByte == null) 1149 return false; 1151 1152 return ((changeByte.byteValue() & changeType) == changeType); 1153 1154 } 1155 1156 1163 private String getFragment(String fragId) { 1164 return (String )(odbcFragments.get(fragId)); 1165 } 1166 1167} 1168 | Popular Tags |