📄 odbcmetadatagenerator.java
字号:
} odbcMetaFile.write("\n\n"); return; } /* **** * renameColsForODBC * Renames any columns in the received query so that they are * ODBC-compliant. * @param queryName Name of the query being processed. * @param queryText Text of the query being processed. * @return All columns requiring renaming have been renamed IN * PLACE in the received StringBuffer. True is returned if * at least one column was renamed; false otherwise. */ private boolean renameColsForODBC(String queryName, StringBuffer queryText) { // If we know the received query doesn't have any columns to // be renamed, then there's nothing to do here. if (!stmtNeedsChange(queryName, COL_RENAME_CHANGE)) return false; // Which columns are renamed, and what the new names are, // depends on which query we're processing. if (queryName.equals("getProcedures")) { renameColForODBC(queryText, "RESERVED1", "NUM_INPUT_PARAMS"); renameColForODBC(queryText, "RESERVED2", "NUM_OUTPUT_PARAMS"); renameColForODBC(queryText, "RESERVED3", "NUM_RESULT_SETS"); return true; } else if (queryName.equals("getProcedureColumns")) { renameColForODBC(queryText, "PRECISION", "COLUMN_SIZE"); renameColForODBC(queryText, "LENGTH", "BUFFER_LENGTH"); renameColForODBC(queryText, "SCALE", "DECIMAL_DIGITS"); renameColForODBC(queryText, "RADIX", "NUM_PREC_RADIX"); return true; } else if (queryName.equals("getTypeInfo")) { renameColForODBC(queryText, "PRECISION", "COLUMN_SIZE"); renameColForODBC(queryText, "AUTO_INCREMENT", "AUTO_UNIQUE_VAL"); return true; } // No renaming was necessary. return false; } /* **** * renameColForODBC * Searches for the old column name in the received String * buffer and replaces it with the new column name. Note * that we only replace the old column name where it is * preceded by "AS", because this is the instance that * determines the column name in the final result set. * @param queryText The query text in which we're doing the * rename operation. * @param oldVal The old column name. * @param newVal The new column name. * @return Occurence of <"AS " + oldVal> in the query text * has been changed IN PLACE to newVal. */ private void renameColForODBC(StringBuffer queryText, String oldVal, String newVal) { String queryString = queryText.toString(); int pos = queryString.indexOf(oldVal); while (pos != -1) { // Next line will set pos2 to be the index of the // first (reading left-to-right) ignorable char // preceding the old column name. That means // that the letters immediately preceding this // position should be "AS". If not, don't // replace this instance. int pos2 = trimIgnorable(PRECEDING, queryString, pos); if (((pos2 - 2) > 0) && (queryString.charAt(pos2-2) == 'A') && (queryString.charAt(pos2-1) == 'S')) { // then this is the one we want to replace. break; } else { // look for next occurrence. pos = queryString.indexOf(oldVal, pos+1); } } if (pos == -1) { // couldn't find the one to replace; leave unchanged. return; } // Do the renaming. queryText.replace(pos, pos + oldVal.length(), newVal); } /* **** * generateSELECTClause * Generates an outer SELECT clause that is then wrapped around a * JDBC query to change the types and/or values of the JDBC * result set. The JDBC query thus becomes a subquery. * * Ex. if we have a JDBC query "SELECT A, B FROM T1" and ODBC * requires that "A" be a smallint, this method will generate * a select clause "SELECT CAST (T2.A AS SMALLINT), T2.B FROM" * that is then used to wrap the JDBC query, as follows: * * SELECT CAST (T2.A AS SMALLINT), T2.B FROM * (SELECT A, B FROM T1) T2 * * @param queryName Name of the query being processed. * @param selectColDefs Array list of the SELECT columns that * exist for the JDBC version of the query. For the above * example, this would be an array list with two String * elements, "A" and "B". * @param newQueryText StringBuffer to which the generated * outer SELECT will be appended. * @return An outer SELECT clause has been generated and * appended to the received buffer. The "FROM" keyword * has been appended, but the subquery itself is NOT * added here. */ private void generateSELECTClause(String queryName, ArrayList selectColDefs, StringBuffer newQueryText) { if (!stmtNeedsChange(queryName, TYPE_VALUE_CHANGE) && !stmtNeedsChange(queryName, ADD_COLUMN_CHANGE)) { // then we don't need to generate a SELECT, because we // don't need to use a subquery (we're only renaming). return; } // Begin the SELECT clause. newQueryText.append("SELECT \\\n\\\n"); // For each of the SELECT columns in JDBC, either // just grab the column name and use it directly in // the generated clause, or else cast the column // to the required type, if appropriate. String colName; String castInfo; for (int i = 0; i < selectColDefs.size(); i++) { if (i > 0) newQueryText.append(", \\\n"); colName = extractColName((String)selectColDefs.get(i)); castInfo = getCastInfoForCol(queryName, colName); if (castInfo != null) newQueryText.append("CAST ("); newQueryText.append(SUBQUERY_NAME); newQueryText.append("."); newQueryText.append(colName); if (castInfo != null) { newQueryText.append(" AS "); newQueryText.append(castInfo); newQueryText.append(")"); } if (!colName.equals(NEW_COL_PLACEHOLDER)) { // don't append the "AS" clause if this is just our // place-holder for adding new columns. newQueryText.append(" AS "); newQueryText.append(colName); } } if (newQueryText.charAt(newQueryText.length() - 1) != '\\') newQueryText.append(" \\"); // End the SELECT clause. newQueryText.append("\nFROM ( "); return; } /* **** * changeValuesForODBC * Searches for a JDBC column name in the received String * buffer and replaces the first occurrence with an ODBC- * compliant value. This method determines what specific * columns need updated values for a given query, and then * makes the appropriate call for each column. * @param queryName Name of the query being processed. * @param newQueryText The query text in which we're doing the * change-value operation. * @return All relevant columns have been updated IN PLACE * to return the required ODBC-compliant values. */ private void changeValuesForODBC(String queryName, StringBuffer newQueryText) { if (!stmtNeedsChange(queryName, TYPE_VALUE_CHANGE)) return; // Which column values are changed, and what the new // values are, depends on which query we're processing. if (queryName.equals("getColumns")) { changeColValueToODBC(queryName, "BUFFER_LENGTH", newQueryText); changeColValueToODBC(queryName, "DECIMAL_DIGITS", newQueryText); changeColValueToODBC(queryName, "NUM_PREC_RADIX", newQueryText); changeColValueToODBC(queryName, "SQL_DATA_TYPE", newQueryText); changeColValueToODBC(queryName, "SQL_DATETIME_SUB", newQueryText); changeColValueToODBC(queryName, "CHAR_OCTET_LENGTH", newQueryText); } else if (queryName.startsWith("getBestRowIdentifier")) { changeColValueToODBC(queryName, "BUFFER_LENGTH", newQueryText); changeColValueToODBC(queryName, "DECIMAL_DIGITS", newQueryText); } else if (queryName.equals("getTypeInfo")) { changeColValueToODBC(queryName, "NUM_PREC_RADIX", newQueryText); changeColValueToODBC(queryName, "SQL_DATA_TYPE", newQueryText); changeColValueToODBC(queryName, "SQL_DATETIME_SUB", newQueryText); changeColValueToODBC(queryName, "UNSIGNED_ATTRIBUTE", newQueryText); changeColValueToODBC(queryName, "AUTO_UNIQUE_VAL", newQueryText); } else if (queryName.equals("getProcedureColumns")) { changeColValueToODBC(queryName, "NUM_PREC_RADIX", newQueryText); changeColValueToODBC(queryName, "DECIMAL_DIGITS", newQueryText); } } /* **** * changeColValueToODBC * Searches for the received column name in the received String * buffer and replaces it with an ODBC-compliant value. * @param queryName Name of the query being processed. * @param colName Name of the specific column to update. * @param newQueryText The query text in which we're doing * the change-value operation. * @return The received column has been updated IN PLACE * to return the required ODBC-compliant value. */ private void changeColValueToODBC(String queryName, String colName, StringBuffer newQueryText) { colName = SUBQUERY_NAME + "." + colName; int pos = newQueryText.toString().indexOf(colName); if (pos == -1) // column we're supposed to change isn't in the query. return; if (colName.endsWith("CHAR_OCTET_LENGTH")) { newQueryText.replace(pos, pos + colName.length(), getFragment("CHAR_OCTET_FOR_ODBC")); } else if (colName.endsWith("BUFFER_LENGTH")) { newQueryText.replace(pos, pos + colName.length(), getFragment("BUFFER_LEN_FOR_ODBC")); } else if (colName.endsWith("SQL_DATA_TYPE")) { newQueryText.replace(pos, pos + colName.length(), getFragment("SQL_DATA_TYPE_FOR_ODBC")); } else if (colName.endsWith("SQL_DATETIME_SUB")) { newQueryText.replace(pos, pos + colName.length(), getFragment("DATETIME_SUB_FOR_ODBC")); } else if (colName.endsWith("UNSIGNED_ATTRIBUTE")) { newQueryText.replace(pos, pos + colName.length(), getFragment("UNSIGNED_ATTR_FOR_ODBC")); } else if (colName.endsWith("AUTO_UNIQUE_VAL")) { newQueryText.replace(pos, pos + colName.length(), getFragment("AUTO_UNIQUE_FOR_ODBC")); } else if (colName.endsWith("DECIMAL_DIGITS")) { newQueryText.replace(pos, pos + colName.length(), getFragment("DECIMAL_DIGITS_FOR_ODBC")); } else if (colName.endsWith("NUM_PREC_RADIX")) { newQueryText.replace(pos, pos + colName.length(), getFragment("RADIX_FOR_ODBC")); } else if (colName.endsWith(NEW_COL_PLACEHOLDER)) { // This is a special case indication that we need to add new columns. if (queryName.equals("getProcedureColumns")) { newQueryText.replace(pos, pos + colName.length(), getFragment("GET_PROC_COLS_NEW_COLS")); } else if (queryName.equals("getTypeInfo")) { newQueryText.replace(pos, pos + colName.length(), getFragment("GET_TYPE_INFO_NEW_COLS")); } } } /* **** * getSelectColDefinitions * Parses the SELECT clause of a JDBC metadata SQL query * and returns a list of the columns being selected. For * example, if the received statement was "SELECT A, * B AS C, D * 2 FROM T1", this method will return an * ArrayList with three string elements: 1) "A", 2) "B * AS C", and 3) "D * 2". * @param query The query from which we are extracting * the SELECT columns. * @param colDefList ArrayList in which we want to * store the column definitions that we find. * @return Received ArrayList has one string value for * each of the columns found in the received query. * Also, an integer is returned indicating the index * in the received query of the start of the FROM * clause, for later use by the calling method. */ private int getSelectColDefinitions(StringBuffer queryText, ArrayList colDefList) { // Create a string for purposes of using "indexOf" // calls, which aren't allowed on a StringBuffer // for JDBC 2.0. String query = queryText.toString().trim(); char [] queryChars = query.toCharArray(); // Move beyond the "SELECT" keyword, if there is one. int start = query.indexOf("SELECT"); if (start != -1) // "+6" in the next line is length of "SELECT". start += 6; else // just start at the first character. start = 0; // Have to read character-by-character in order to // figure out where each column description ends. int fromClauseIndex = -1; int parenDepth = 0; for (int i = start; i < queryChars.length; i++) { if (queryChars[i] == '(') parenDepth++; else if (queryChars[i] == ')') parenDepth--; else if ((queryChars[i] == ',') && (parenDepth == 0)) { // this is a naive way of determining the end of a // column definition (it'll work so long as there are no // string constants in the query that have commas in them, // which was true at the time of writing. colDefList.add(new String(queryChars, start, (i - start)).trim()); // Skip over non-important whitespace to find start // of next column definition. Next line will set i to // just before the next non-whitespace character. i = trimIgnorable(FOLLOWING, queryChars, i); start = i + 1; } else if (((i+3) < queryChars.length) && (parenDepth == 0) && (queryChars[i] == 'F') && (queryChars[i+1] == 'R') && (queryChars[i+2] == 'O') && (queryChars[i+3] == 'M')) { // this is the end of final column definition; store it // and then exit the loop, after trimming off non-important // whitespace. Next line will set i to just after the // last (reading left-to-right) non-whitespace character // before the FROM. i = trimIgnorable(PRECEDING, queryChars, i); fromClauseIndex = i; colDefList.add(new String(queryChars, start, (i - start)).trim()); break; } } return fromClauseIndex; } /* **** * addHelperColsToSubquery * For some of the metadata queries, the ODBC version * needs to access values that are only available in * the JDBC subquery. In such cases, we want to add * those values as additional "helper" columns to * the subquery result set, so that they can be * referenced from the new ODBC outer query (without * requiring a join). For example, assume we have 2 * tables T1(int i, int j) and T2 (int a), and a * subquery "SELECT T1.i, T1.j + T2.a from T1, T2)". * Then we have an outer query that, instead of * returning "T1.j + T2.a", needs to return the * value of "2 * T2.a": * * SELECT VT.i, 2 * T2.a FROM * (SELECT T1.i, T1.j + T2.a FROM T1, T2) VT * * The above statement WON'T work, because the outer * query can't see the value "T2.a". So in such a * a case, this method will add "T2.a" to the list * of columns returned by the subquery, so that the * outer query can then access it: * * SELECT VT.i, 2 * VT.a FROM
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -