📄 odbcmetadatagenerator.java
字号:
/* Derby - Class org.apache.derby.catalog.ODBCProcedureColsVTI Copyright 2000, 2004 The Apache Software Foundation or its licensors, as applicable. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */package org.apache.derbyBuild;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.FileWriter;import java.util.Properties;import java.util.HashMap;import java.util.ArrayList;import org.apache.derby.iapi.services.sanity.SanityManager;/* **** * This class is used at COMPILE TIME ONLY. It is responsible for generating * ODBC metadata queries based on existing JDBC queries. In a word, * this class reads from the org/apache/derby/impl/jdbc/metadata.properties * file (which is where the JDBC queries are stored), and for each query, * performs the changes/additions required to make it comply with ODBC * standards. The generated ODBC queries are written to an output file * that is then used, at build time, to create a full set of both JDBC and * ODBC queries, all of which are then loaded into the database system * tables at creation time. * * For more on the ODBC specification of the metadata methods in question, * see: * * "http://msdn.microsoft.com/library/default.asp?url=/library/en-us/odbc/ * htm/odbcsqlprocedures.asp" * * For more on how the generated queries are used at execution time, see * EmbedDatabaseMetadata.java and SystemProcedures.java in the codeline. * */public class ODBCMetadataGenerator { // Types of changes that are possible. There are three // types that we handle here: // // 1. Column rename: // Rename a column to have an ODBC-specified name. // For ex. change "SCALE" to "DECIMAL_DIGITS" // 2. Type and/or value change: // Cast a column to an OBDC-specified type. At time // of writing, this was just for casting INTs to // SMALLINTs; OR modify an existing JDBC value // to match the ODBC specification. // 3. Additional column(s): // Add a new, ODBC-specified column to an existing // result set. private final byte COL_RENAME_CHANGE = 0x01; private final byte TYPE_VALUE_CHANGE = 0x02; private final byte ADD_COLUMN_CHANGE = 0x04; // Notice written before each generated ODBC statement. private final String ODBC_QUERY_NOTICE = "#\n# *** NOTE! *** The following query was generated\n" + "# AUTOMATICALLY at build time based on the existing\n" + "# JDBC version of the query. DO NOT MODIFY this\n" + "# generated query by hand. Instead, modify either\n" + "# 1) the JDBC version of the query in the codeline \n" + "# file \"metadata.properties\" (which will then get\n" + "# propagated at build time), 2) the relevant SQL\n" + "# fragments in 'odbcgen_fragments.properties' in\n" + "# the codleine, or 3) the ODBCMetadataGenerator\n" + "# class in the org/apache/derbyBuild directory.\n"; // Prefix to append to all ODBC queries. NOTE: if you change // this value, you'll have to modify EmbedDatabaseMetadata.java // to reflect the change. private final String ODBC_QUERY_PREFIX = "odbc_"; // Name to use when making JDBC queries into subqueries // (loaded from odbcFragments). NOTE: if you change this value, // you'll have to modify "odbcgen_fragments.properties" to // reflect the change. private final String SUBQUERY_NAME = "JDBC_SUBQUERY"; // Mock value used to accomplish insertion of new columns. private final String NEW_COL_PLACEHOLDER = "COLUMN_POSITION_HOLDER"; // Used for trimming 'whitespace'. private final short FOLLOWING = 1; private final short PRECEDING = -1; // List of what types of changes are required for a given // metadata procedure. private HashMap changeMap; // SQL fragments and keywords that are used in composing // ODBC metadata queries. These are loaded from a file // once and then used throughout the generation process // to build the ODBC queries piece-by-piece. private Properties odbcFragments; // Output file; all processed statements are written to this // file. At BUILD TIME, this file will clobber the copy of // "metadata.properties" that is in the BUILD/CLASSES // directory. NOTE: this will NOT clobber the metadata // properties file that is in the SOURCE/CODELINE. private FileWriter odbcMetaFile; /* **** * Constructor. * Initializes SQL fragments used for generation, and * then opens the output file, */ public ODBCMetadataGenerator() throws IOException { // SQL fragments. odbcFragments = new Properties(); odbcFragments.load(this.getClass().getResourceAsStream( "odbcgen_fragments.properties")); // Prep output file. odbcMetaFile = new FileWriter("odbc_metadata.properties"); } /* **** * main: * Open the metadata.properties file (the copy that is in the * build directory, NOT the one in the source directory), * figure out what changes are needed for the various metadata * queries, and then generate the ODBC-compliant versions * where needed. * @param args Ignored. * @return ODBC-compliant metadata statements have been * generated and written out to "odbc_metadata.properties" * in the running directory. */ public static void main(String [] args) throws IOException { ODBCMetadataGenerator odbcGen = new ODBCMetadataGenerator(); odbcGen.initChanges(); odbcGen.generateODBCQueries(odbcGen.getClass().getResourceAsStream( "/org/apache/derby/impl/jdbc/metadata.properties")); } /* **** * initChanges * Create a listing of the types of changes that need to be * made for each metadata query to be ODBC-compliant. * If a metadata query has no entry in this map, then * it is left unchanged and no ODBC-version will be created. * Having this mapping allows us to skip over String * parsing (which can be slow) when it's not required. * For details on the changes, see the appropriate methods * below. * @return Map holding the list of changes to be made for * each metadata query has been initialized. */ private void initChanges() { changeMap = new HashMap(); changeMap.put("getProcedures", new Byte(COL_RENAME_CHANGE)); changeMap.put("getProcedureColumns", new Byte((byte)(COL_RENAME_CHANGE | TYPE_VALUE_CHANGE | ADD_COLUMN_CHANGE))); changeMap.put("getColumns", new Byte(TYPE_VALUE_CHANGE)); changeMap.put("getVersionColumns", new Byte(TYPE_VALUE_CHANGE)); changeMap.put("getBestRowIdentifierPrimaryKeyColumns", new Byte(TYPE_VALUE_CHANGE)); changeMap.put("getBestRowIdentifierUniqueKeyColumns", new Byte(TYPE_VALUE_CHANGE)); changeMap.put("getBestRowIdentifierUniqueIndexColumns", new Byte(TYPE_VALUE_CHANGE)); changeMap.put("getBestRowIdentifierAllColumns", new Byte(TYPE_VALUE_CHANGE)); changeMap.put("getPrimaryKeys", new Byte(TYPE_VALUE_CHANGE)); changeMap.put("getTypeInfo", new Byte((byte)(COL_RENAME_CHANGE | TYPE_VALUE_CHANGE | ADD_COLUMN_CHANGE))); changeMap.put("getIndexInfo", new Byte(TYPE_VALUE_CHANGE)); return; } /* **** * generateODBCQueries: * Reads the existing (JDBC) metadata queries from * metadata.properties and, for each one, makes a call * to generate an ODBC-compliant version. * @param is InputStream for reading metadata.properties. */ public void generateODBCQueries(InputStream is) throws IOException { // JDBC query that we read from metadata.properties. StringBuffer query = new StringBuffer(); // We assume no single line/query is greater than 1K in // length, and we'll fail if this isn't the case. The // limit of 1K was just picked arbitrarily; this can be // increased if needed at a later time. char [] line = new char[1024]; for (int count = readLine(is, line); count != -1; count = readLine(is, line)) { if (count == 0) // blank line; ignore continue; else if (line[0] == '#') { // comment; write it to file. odbcMetaFile.write(line, 0, count); odbcMetaFile.write("\n"); continue; } // Verify that we haven't passed our limit. if (count >= line.length) { throw new IOException( "Encountered line longer than expected when reading metadata " + "file; either shorten the line, or increase the limit..."); } // "+1" in next line because we added a "\n" at the end and // we want to include that, for sake of easier reading. query.append(line, 0, count+1); if (line[count-1] == '\\') // then continue building the query. continue; // Take the query and see if we need to generate an ODBC- // compliant version. generateODBCQuery(query); // Prep for another query. query.delete(0, query.length()); } // Make sure we didn't end up with an incomplete query somewhere. if (query.length() > 0) { throw new IOException( "Encountered non-terminated query while reading metadata file."); } // Close out. odbcMetaFile.flush(); odbcMetaFile.close(); } /* **** * generateODBCQuery * Takes a specific JDBC query, writes it to the output file, * and then creates an ODBC-compliant version of that * query (if needed) and writes that to the output file, * as well. * @param queryText SQL text from a JDBC metadata query * that was read from metadata.properties. */ private void generateODBCQuery(StringBuffer queryText) throws IOException { // Create a string for purposes of using "indexOf" // calls, which aren't allowed on a StringBuffer // for JDBC 2.0. String queryAsString = queryText.toString().trim(); if (queryAsString.startsWith(ODBC_QUERY_PREFIX)) // this query was automatically generated (presumably // by this class), so ignore it now. return; // Write the original (JDBC) query. odbcMetaFile.write(queryAsString, 0, queryAsString.length()); odbcMetaFile.write("\n\n"); // Parse out the name of this particular query. int pos = queryAsString.indexOf("="); if (pos == -1) { throw new IOException( "Failed to extract query name from a JDBC metadata query."); } String queryName = queryText.substring(0, pos); // Parse out the ORDER BY clause since they are not allowed // in subqueries; we'll re-attach it later. String orderBy = ""; int orderByPos = queryAsString.lastIndexOf("ORDER BY"); if (orderByPos != -1) orderBy = queryAsString.substring(orderByPos, queryAsString.length()); // Isolate query text (remove ORDER BY clause and then query name, // in that order). if (orderByPos != -1) queryText.delete(orderByPos, queryText.length()); queryText.delete(0, pos+1); // Three types of modifications that we may need to do. // -- #1: Column renaming. StringBuffer outerQueryText = new StringBuffer(); boolean haveODBCChanges = renameColsForODBC(queryName, queryText); // Get a list of the column definitions in the subquery, for // use by subsequent operations. ArrayList colDefs = new ArrayList(); pos = getSelectColDefinitions(queryText, colDefs); // In some cases, we need to add "helper" columns to the // subquery so that we can use them in calculations for // the outer query. addHelperColsToSubquery(queryName, queryText, pos); // -- #2.A: Prep to add new ODBC columns. Note: we need // to do this BEFORE we generate the outer SELECT statement. markNewColPosition(queryName, colDefs); // If we're going to use a subquery, generate the outer // SELECT statement. This is where we enforce column // types (via CAST) if needed. generateSELECTClause(queryName, colDefs, outerQueryText); // -- #3: Alter column values, where needed. changeValuesForODBC(queryName, outerQueryText); // -- #2.B: Add new ODBC columns. addNewColumnsForODBC(queryName, outerQueryText); haveODBCChanges = (haveODBCChanges || (outerQueryText.length() > 0)); if (!haveODBCChanges) // we didn't change anything, so nothing left to do. return; // Write out the new, ODBC version of the query. odbcMetaFile.write(ODBC_QUERY_NOTICE); odbcMetaFile.write(ODBC_QUERY_PREFIX); odbcMetaFile.write(queryName); odbcMetaFile.write("="); if (outerQueryText.length() == 0) { // all we did was change column names, so just write out the // original query with the new column names. odbcMetaFile.write(queryText.toString()); odbcMetaFile.write("\n\n"); return; } // Else, we need to make the original query a subquery so that we // can change types/values and/or add columns. queryAsString = queryText.toString().trim(); odbcMetaFile.write(outerQueryText.toString()); odbcMetaFile.write(queryAsString); if (queryText.charAt(queryAsString.length()-1) == '\\') odbcMetaFile.write("\n\\\n) "); else odbcMetaFile.write(" \\\n\\\n) "); odbcMetaFile.write(SUBQUERY_NAME); if (orderBy.length() == 0) odbcMetaFile.write("\n"); else { // re-attach ORDER BY clause. odbcMetaFile.write(" \\\n"); odbcMetaFile.write(orderBy);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -