📄 odbcmetadatagenerator.java
字号:
* (SELECT T1.i, T1.j + T2.a, T2.a FROM T1, T2) VT * * Which specific columns are added to the subquery * depends on the query in question. * * @param queryName Name of the query in question. * @param subqueryText text of the subquery in question. * @param insertPos Index into the received buffer * marking the position where the helper columns * should be inserted. */ private void addHelperColsToSubquery(String queryName, StringBuffer subqueryText, int insertPos) { if (queryName.equals("getColumns")) { subqueryText.insert(insertPos, getFragment("GET_COLS_HELPER_COLS")); } else if (queryName.startsWith("getBestRowIdentifier")) { subqueryText.insert(insertPos, getFragment("BEST_ROW_ID_HELPER_COLS")); } } /* **** * extractColName * Takes a single column definition from a SELECT clause * and returns only the unqualified name of the column. * Assumption here is that any column definition we see * here will either 1) end with an "AS <COLUMN_NAME>" * clause, or 2) consist of ONLY a column name, such * as "A" or "A.B". At the time of writing, these * assumptions were true for all relevant metadata * queries. * * Ex. If colDef is "A", this method will return "A". * If colDef is "A.B", this method will return "B". * If colDef is "<bunch of SQL> AS C", this method * will return "C". * * @param colDef Column definition from which we're * trying to extract the name. * @return Name of the column that is referenced in * the received column definition. */ private String extractColName(String colDef) { // Find out where the column name starts. int pos = colDef.lastIndexOf("AS "); if (pos == -1) { // we assume that the col def is _just_ a column name, // so start at the beginning. pos = 0; } else { // Move beyond the "AS". pos += 2; // Skip any non-important whitespace or backslashes. char c = colDef.charAt(pos); while ((c == '\\') || Character.isWhitespace(c)) c = colDef.charAt(++pos); } // Check to see if it's a qualified name. int pos2 = colDef.indexOf(".", pos); if (pos2 == -1) // it's not a qualified name, so just return it. return colDef.substring(pos, colDef.length()); // Else, strip off the schema and just return the col name. return colDef.substring(pos2+1, colDef.length()); } /* **** * getCastInfoForCol * Returns the target type for a result set column that * needs to be cast into an ODBC type. This is usually * for casting integers to "SMALLINT". * @param queryName Name of query being processed. * @param colName Name of the specific column for which * we are trying to find the target type. * @return The target type if one exists, or else null * if the received column in the received query has * no known target type. */ private String getCastInfoForCol(String queryName, String colName) { if (queryName.equals("getTypeInfo")) { if (colName.equals("NULLABLE") || colName.equals("CASE_SENSITIVE") || colName.equals("SEARCHABLE") || colName.equals("UNSIGNED_ATTRIBUTE") || colName.equals("FIXED_PREC_SCALE") || colName.equals("AUTO_UNIQUE_VAL") || colName.equals("SQL_DATA_TYPE") || colName.equals("SQL_DATETIME_SUB") || colName.equals("MINIMUM_SCALE") || colName.equals("MAXIMUM_SCALE")) { return "SMALLINT"; } } else if (queryName.equals("getColumns")) { if (colName.equals("DECIMAL_DIGITS") || colName.equals("NULLABLE") || colName.equals("NUM_PREC_RADIX") || colName.equals("SQL_DATA_TYPE") || colName.equals("SQL_DATETIME_SUB")) { return "SMALLINT"; } } else if (queryName.equals("getVersionColumns")) { if (colName.equals("SCOPE") || colName.equals("DATA_TYPE") || colName.equals("DECIMAL_DIGITS") || colName.equals("PSEUDO_COLUMN")) { return "SMALLINT"; } } else if (queryName.equals("getPrimaryKeys")) { if (colName.equals("KEY_SEQ")) return "SMALLINT"; } else if (queryName.equals("getIndexInfo")) { if (colName.equals("NON_UNIQUE") || colName.equals("TYPE") || colName.equals("ORDINAL_POSITION")) { return "SMALLINT"; } } // No target type for the received column // in the received query (leave it unchanged). return null; } /* **** * markNewColPosition * In effect, "marks" the position at which additional * columns are to be added for ODBC compliance. This * is accomplished by adding a dummy column name to * the list of SELECT columns. Later, in the method * that actually adds the columns, we'll do a find- * replace on this dummy value. * @param queryName Name of the query. * @param selectColDefs Array list of the SELECT * columns that exist in the ODBC version of * the query thus far. * @return A dummy column name has been added to * the received list of columns at the position * at which new ODBC columns should be added. * If a query doesn't require additional * columns to be ODBC compliant, this method * leaves the received column list unchanged. */ private void markNewColPosition(String queryName, ArrayList selectColDefs) { if (!stmtNeedsChange(queryName, ADD_COLUMN_CHANGE)) return; if (queryName.equals("getProcedureColumns")) { // Add the new columns in front of the Derby-specific ones. // The "-2" in the next line is because there are 2 Derby- // specific columns in the JDBC version of getProcedureCols // (PARAMETER_ID and METHOD_ID). selectColDefs.add(selectColDefs.size() - 2, NEW_COL_PLACEHOLDER); } else if (queryName.equals("getTypeInfo")) { // just add the new column to the end. selectColDefs.add(NEW_COL_PLACEHOLDER); } } /* **** * addNewColumnsForODBC * Adds new columns to the ODBC version of a metadata * query (the ODBC version is at this point being * built up in newQueryText). Before this method * was called, a dummy placeholder should have been * placed in the newQueryText buffer (by a call to * "markNewColPosition"). This method simply replaces * that dummy placeholder with the SQL text for the * new columns. * @param queryName Name of query being processed. * @newQueryText The buffer in which we want to * add the new column. * @return The dummy placeholder in the received * buffer has been replaced with any ODBC columns * that need to be added to the query in question * for ODBC compliance. */ private void addNewColumnsForODBC(String queryName, StringBuffer newQueryText) { if (!stmtNeedsChange(queryName, ADD_COLUMN_CHANGE)) return; changeColValueToODBC(queryName, NEW_COL_PLACEHOLDER, newQueryText); // It's possible that the new column fragments we added // have placeholders in them for _other_ fragments. We // need to do the substitution here. if (queryName.equals("getProcedureColumns")) { fragSubstitution("SQL_DATA_TYPE_FOR_ODBC", newQueryText); fragSubstitution("DATETIME_SUB_FOR_ODBC", newQueryText); } return; } /* **** * fragSubstitution * Replaces a single occurrence of the received * fragment key with the text corresponding to * that key. * @param fragKey The fragment key for which we are * going to do the substitution. * @queryText The buffer in which we are going to do * the substitution. * @return fragKey has been substituted (IN PLACE) * with the fragment corresponding to it in the * received buffer. If the fragment key could not * be found, the buffer remains unchanged. */ private void fragSubstitution(String fragKey, StringBuffer queryText) { int pos = queryText.toString().indexOf(fragKey); if (pos != -1) { // NOTE: the " + 1" and " - 1" in the next line // are needed because the fragment key is // enclosed within curly braces ("{}"). queryText.replace(pos - 1, pos + fragKey.length() + 1, getFragment(fragKey)); } } /* **** * readLine * Reads a line from the received input stream and stores it * into the received character array. In this method, we * consider the end of the line to be either 1) "\n" char, or * 2) a single backslash "\", which is used in metadata * queries to indicate line continuation. After reading * a line, we append an EOL to it for formatting purposes, * but that last EOL is NOT included in the count of * characters. * @param is The input stream from which we're reading. * @param line The char array into which we're reading. * @return the number of characters read from the * stream; -1 if we reached end of the stream. */ private int readLine(InputStream is, char [] line) throws IOException { int count = 0; boolean atLeastOneNonWSChar = false; char ch; int byteRead; for (byteRead = is.read(); (byteRead != -1) && (count < line.length); byteRead = is.read()) { ch = (char)byteRead; line[count++] = ch; atLeastOneNonWSChar = true; if ((ch == '\\') || (ch == '\n')) break; } if ((byteRead == -1) && (count == 0)) // end of file. return -1; // Take off trailing whitespace. while ((count > 0) && Character.isWhitespace(line[count-1])) count--; // Add an EOL for ease of reading, but don't include it in // "count" total. line[count] = '\n'; return count; } /* **** * trimIgnorable * Removes all 'ignorable' chars that immediately precede or * follow (depending on the direction) the character at * the received index. "Ignorable" here means whitespace * OR a single backslash ("\"), which is used in the * metadata.properties file to indicate line continuation. * @param direction +1 if we want to trim following, -1 * if we want to trim preceding. * @param chars The character array being processed. * @param index The point before/after which to start * trimming. * @return The index into the received char array of the * "last" ignorable character w.r.t the received index * and direction. In other words, if we're trimming * the chars FOLLOWING, the returned index will be of * the last (reading left-to-right) ignorable char; if * we're trimming the chars PRECEDING, the returned index * will be of the first (reading left-to-right) ignorable * character. */ private int trimIgnorable(short direction, char [] chars, int index) { index += direction; while ((index >= 0) && (index < chars.length) && ((chars[index] == '\\') || Character.isWhitespace(chars[index]))) { index += direction; } // index is now on the final non-ignorable character // in the given direction. Move it back one so that // it's on the "last" ignorable character (with // respect to direction). index -= direction; return index; } /* **** * trimIgnorable * Same as trimIgnorable above, except with String argument * instead of char[]. */ private int trimIgnorable(short direction, String str, int index) { index += direction; while ((index >= 0) && (index < str.length()) && ((str.charAt(index) == '\\') || Character.isWhitespace(str.charAt(index)))) { index += direction; } // index is now on the final non-ignorable character // in the given direction. Move it back one so that // it's on the "first" ignorable character (with // respect to direction). index -= direction; return index; } /* **** * stmtNeedsChange * Returns whether or not a specific metadata statement * requires the received type of change. This is determined * based on the info stored in the "changeMaps" mapping. * @param queryName Name of the query in question. * @param changeType The type of change in question. */ private boolean stmtNeedsChange(String queryName, byte changeType) { Byte changeByte = (Byte)changeMap.get(queryName); if (changeByte == null) // No entry means change is not needed. return false; return ((changeByte.byteValue() & changeType) == changeType); } /* **** * getFragment * Looks up an SQL fragment and returns the value as a String. * @param String fragId id of the fragment to look up. * @return The string fragment corresponding to the received * fragment id. */ private String getFragment(String fragId) { return (String)(odbcFragments.get(fragId)); } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -