/* Copyright (C) 2005-2007 MySQL AB This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. There are special exceptions to the terms and conditions of the GPL as it is applied to this software. View the full text of the exception in file EXCEPTIONS-CONNECTOR-J in the directory of this software distribution. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package com.mysql.jdbc; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; /** * DatabaseMetaData implementation that uses INFORMATION_SCHEMA available in * MySQL-5.0 and newer. * * The majority of the queries in this code were built for Connector/OO.org by * Georg Richter (georg_at_mysql.com). */ public class DatabaseMetaDataUsingInfoSchema extends DatabaseMetaData { private boolean hasReferentialConstraintsView; protected DatabaseMetaDataUsingInfoSchema(ConnectionImpl connToSet, String databaseToSet) throws SQLException { super(connToSet, databaseToSet); this.hasReferentialConstraintsView = this.conn.versionMeetsMinimum(5, 1, 10); } private ResultSet executeMetadataQuery(PreparedStatement pStmt) throws SQLException { ResultSet rs = pStmt.executeQuery(); ((com.mysql.jdbc.ResultSetInternalMethods) rs).setOwningStatement(null); return rs; } /** * Get a description of the access rights for a table's columns. *

* Only privileges matching the column name criteria are returned. They are * ordered by COLUMN_NAME and PRIVILEGE. *

*

* Each privilige description has the following columns: *

    *
  1. TABLE_CAT String => table catalog (may be null)
  2. *
  3. TABLE_SCHEM String => table schema (may be null)
  4. *
  5. TABLE_NAME String => table name
  6. *
  7. COLUMN_NAME String => column name
  8. *
  9. GRANTOR => grantor of access (may be null)
  10. *
  11. GRANTEE String => grantee of access
  12. *
  13. PRIVILEGE String => name of access (SELECT, INSERT, UPDATE, * REFRENCES, ...)
  14. *
  15. IS_GRANTABLE String => "YES" if grantee is permitted to * grant to others; "NO" if not; null if unknown
  16. *
*

* * @param catalog * a catalog name; "" retrieves those without a catalog * @param schema * a schema name; "" retrieves those without a schema * @param table * a table name * @param columnNamePattern * a column name pattern * @return ResultSet each row is a column privilege description * @throws SQLException * if a database access error occurs * @see #getSearchStringEscape */ public java.sql.ResultSet getColumnPrivileges(String catalog, String schema, String table, String columnNamePattern) throws SQLException { if (columnNamePattern == null) { if (this.conn.getNullNamePatternMatchesAll()) { columnNamePattern = "%"; } else { throw SQLError.createSQLException( "Column name pattern can not be NULL or empty.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } } if (catalog == null) { if (this.conn.getNullCatalogMeansCurrent()) { catalog = this.database; } } String sql = "SELECT TABLE_SCHEMA AS TABLE_CAT, NULL AS TABLE_SCHEM, TABLE_NAME," +"COLUMN_NAME, NULL AS GRANTOR, GRANTEE, PRIVILEGE_TYPE AS PRIVILEGE, IS_GRANTABLE FROM " + "INFORMATION_SCHEMA.COLUMN_PRIVILEGES WHERE " + "TABLE_SCHEMA LIKE ? AND " + "TABLE_NAME =? AND COLUMN_NAME LIKE ? ORDER BY " + "COLUMN_NAME, PRIVILEGE_TYPE"; PreparedStatement pStmt = null; try { pStmt = prepareMetaDataSafeStatement(sql); if (catalog != null) { pStmt.setString(1, catalog); } else { pStmt.setString(1, "%"); } pStmt.setString(2, table); pStmt.setString(3, columnNamePattern); ResultSet rs = executeMetadataQuery(pStmt); ((com.mysql.jdbc.ResultSetInternalMethods) rs).redefineFieldsForDBMD(new Field[] { new Field("", "TABLE_CAT", Types.CHAR, 64), new Field("", "TABLE_SCHEM", Types.CHAR, 1), new Field("", "TABLE_NAME", Types.CHAR, 64), new Field("", "COLUMN_NAME", Types.CHAR, 64), new Field("", "GRANTOR", Types.CHAR, 77), new Field("", "GRANTEE", Types.CHAR, 77), new Field("", "PRIVILEGE", Types.CHAR, 64), new Field("", "IS_GRANTABLE", Types.CHAR, 3)}); return rs; } finally { if (pStmt != null) { pStmt.close(); } } } /** * Get a description of table columns available in a catalog. *

* Only column descriptions matching the catalog, schema, table and column * name criteria are returned. They are ordered by TABLE_SCHEM, TABLE_NAME * and ORDINAL_POSITION. *

*

* Each column description has the following columns: *

    *
  1. TABLE_CAT String => table catalog (may be null)
  2. *
  3. TABLE_SCHEM String => table schema (may be null)
  4. *
  5. TABLE_NAME String => table name
  6. *
  7. COLUMN_NAME String => column name
  8. *
  9. DATA_TYPE short => SQL type from java.sql.Types
  10. *
  11. TYPE_NAME String => Data source dependent type name
  12. *
  13. COLUMN_SIZE int => column size. For char or date types this * is the maximum number of characters, for numeric or decimal types this is * precision.
  14. *
  15. BUFFER_LENGTH is not used.
  16. *
  17. DECIMAL_DIGITS int => the number of fractional digits
  18. *
  19. NUM_PREC_RADIX int => Radix (typically either 10 or 2)
  20. *
  21. NULLABLE int => is NULL allowed? * *
  22. *
  23. REMARKS String => comment describing column (may be null) *
  24. *
  25. COLUMN_DEF String => default value (may be null)
  26. *
  27. SQL_DATA_TYPE int => unused
  28. *
  29. SQL_DATETIME_SUB int => unused
  30. *
  31. CHAR_OCTET_LENGTH int => for char types the maximum number * of bytes in the column
  32. *
  33. ORDINAL_POSITION int => index of column in table (starting * at 1)
  34. *
  35. IS_NULLABLE String => "NO" means column definitely does not * allow NULL values; "YES" means the column might allow NULL values. An * empty string means nobody knows.
  36. *
*

*/ public ResultSet getColumns(String catalog, String schemaPattern, String tableName, String columnNamePattern) throws SQLException { if (columnNamePattern == null) { if (this.conn.getNullNamePatternMatchesAll()) { columnNamePattern = "%"; } else { throw SQLError.createSQLException( "Column name pattern can not be NULL or empty.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } } if (catalog == null) { if (this.conn.getNullCatalogMeansCurrent()) { catalog = this.database; } } StringBuffer sqlBuf = new StringBuffer("SELECT " + "TABLE_SCHEMA AS TABLE_CAT, " + "NULL AS TABLE_SCHEM," + "TABLE_NAME," + "COLUMN_NAME,"); MysqlDefs.appendJdbcTypeMappingQuery(sqlBuf, "DATA_TYPE"); sqlBuf.append(" AS DATA_TYPE, "); if (conn.getCapitalizeTypeNames()) { sqlBuf.append("UPPER(CASE WHEN LOCATE('unsigned', COLUMN_TYPE) != 0 AND LOCATE('unsigned', DATA_TYPE) = 0 THEN CONCAT(DATA_TYPE, ' unsigned') ELSE DATA_TYPE END) AS TYPE_NAME,"); } else { sqlBuf.append("CASE WHEN LOCATE('unsigned', COLUMN_TYPE) != 0 AND LOCATE('unsigned', DATA_TYPE) = 0 THEN CONCAT(DATA_TYPE, ' unsigned') ELSE DATA_TYPE END AS TYPE_NAME,"); } sqlBuf .append("CASE WHEN LCASE(DATA_TYPE)='date' THEN 10 WHEN LCASE(DATA_TYPE)='time' THEN 8 WHEN LCASE(DATA_TYPE)='datetime' THEN 19 WHEN LCASE(DATA_TYPE)='timestamp' THEN 19 WHEN CHARACTER_MAXIMUM_LENGTH IS NULL THEN NUMERIC_PRECISION WHEN CHARACTER_MAXIMUM_LENGTH > " + Integer.MAX_VALUE + " THEN " + Integer.MAX_VALUE + " ELSE CHARACTER_MAXIMUM_LENGTH END AS COLUMN_SIZE, " + MysqlIO.getMaxBuf() + " AS BUFFER_LENGTH," + "NUMERIC_SCALE AS DECIMAL_DIGITS," + "10 AS NUM_PREC_RADIX," + "CASE WHEN IS_NULLABLE='NO' THEN " + columnNoNulls + " ELSE CASE WHEN IS_NULLABLE='YES' THEN " + columnNullable + " ELSE " + columnNullableUnknown + " END END AS NULLABLE," + "COLUMN_COMMENT AS REMARKS," + "COLUMN_DEFAULT AS COLUMN_DEF," + "0 AS SQL_DATA_TYPE," + "0 AS SQL_DATETIME_SUB," + "CASE WHEN CHARACTER_OCTET_LENGTH > " + Integer.MAX_VALUE + " THEN " + Integer.MAX_VALUE + " ELSE CHARACTER_OCTET_LENGTH END AS CHAR_OCTET_LENGTH," + "ORDINAL_POSITION," + "IS_NULLABLE," + "NULL AS SCOPE_CATALOG," + "NULL AS SCOPE_SCHEMA," + "NULL AS SCOPE_TABLE," + "NULL AS SOURCE_DATA_TYPE," + "IF (EXTRA LIKE '%auto_increment%','YES','NO') AS IS_AUTOINCREMENT " + "FROM INFORMATION_SCHEMA.COLUMNS WHERE " + "TABLE_SCHEMA LIKE ? AND " + "TABLE_NAME LIKE ? AND COLUMN_NAME LIKE ? " + "ORDER BY TABLE_SCHEMA, TABLE_NAME, ORDINAL_POSITION"); PreparedStatement pStmt = null; try { pStmt = prepareMetaDataSafeStatement(sqlBuf.toString()); if (catalog != null) { pStmt.setString(1, catalog); } else { pStmt.setString(1, "%"); } pStmt.setString(2, tableName); pStmt.setString(3, columnNamePattern); ResultSet rs = executeMetadataQuery(pStmt); ((com.mysql.jdbc.ResultSetInternalMethods) rs).redefineFieldsForDBMD(new Field[] { new Field("", "TABLE_CAT", Types.CHAR, 255), new Field("", "TABLE_SCHEM", Types.CHAR, 0), new Field("", "TABLE_NAME", Types.CHAR, 255), new Field("", "COLUMN_NAME", Types.CHAR, 32), new Field("", "DATA_TYPE", Types.SMALLINT, 5), new Field("", "TYPE_NAME", Types.CHAR, 16), new Field("", "COLUMN_SIZE", Types.INTEGER, Integer .toString(Integer.MAX_VALUE).length()), new Field("", "BUFFER_LENGTH", Types.INTEGER, 10), new Field("", "DECIMAL_DIGITS", Types.INTEGER, 10), new Field("", "NUM_PREC_RADIX", Types.INTEGER, 10), new Field("", "NULLABLE", Types.INTEGER, 10), new Field("", "REMARKS", Types.CHAR, 0), new Field("", "COLUMN_DEF", Types.CHAR, 0), new Field("", "SQL_DATA_TYPE", Types.INTEGER, 10), new Field("", "SQL_DATETIME_SUB", Types.INTEGER, 10), new Field("", "CHAR_OCTET_LENGTH", Types.INTEGER, Integer .toString(Integer.MAX_VALUE).length()), new Field("", "ORDINAL_POSITION", Types.INTEGER, 10), new Field("", "IS_NULLABLE", Types.CHAR, 3), new Field("", "SCOPE_CATALOG", Types.CHAR, 255), new Field("", "SCOPE_SCHEMA", Types.CHAR, 255), new Field("", "SCOPE_TABLE", Types.CHAR, 255), new Field("", "SOURCE_DATA_TYPE", Types.SMALLINT, 10), new Field("", "IS_AUTOINCREMENT", Types.CHAR, 3) }); return rs; } finally { if (pStmt != null) { pStmt.close(); } } } /** * Get a description of the foreign key columns in the foreign key table * that reference the primary key columns of the primary key table (describe * how one table imports another's key.) This should normally return a * single foreign key/primary key pair (most tables only import a foreign * key from a table once.) They are ordered by FKTABLE_CAT, FKTABLE_SCHEM, * FKTABLE_NAME, and KEY_SEQ. *

* Each foreign key column description has the following columns: *

    *
  1. PKTABLE_CAT String => primary key table catalog (may be * null)
  2. *
  3. PKTABLE_SCHEM String => primary key table schema (may be * null)
  4. *
  5. PKTABLE_NAME String => primary key table name
  6. *
  7. PKCOLUMN_NAME String => primary key column name
  8. *
  9. FKTABLE_CAT String => foreign key table catalog (may be * null) being exported (may be null)
  10. *
  11. FKTABLE_SCHEM String => foreign key table schema (may be * null) being exported (may be null)
  12. *
  13. FKTABLE_NAME String => foreign key table name being exported *
  14. *
  15. FKCOLUMN_NAME String => foreign key column name being * exported
  16. *
  17. KEY_SEQ short => sequence number within foreign key
  18. *
  19. UPDATE_RULE short => What happens to foreign key when * primary is updated: * *
  20. *
  21. DELETE_RULE short => What happens to the foreign key when * primary is deleted. * *
  22. *
  23. FK_NAME String => foreign key identifier (may be null)
  24. *
  25. PK_NAME String => primary key identifier (may be null)
  26. *
*

* * @param primaryCatalog * a catalog name; "" retrieves those without a catalog * @param primarySchema * a schema name pattern; "" retrieves those without a schema * @param primaryTable * a table name * @param foreignCatalog * a catalog name; "" retrieves those without a catalog * @param foreignSchema * a schema name pattern; "" retrieves those without a schema * @param foreignTable * a table name * @return ResultSet each row is a foreign key column description * @throws SQLException * if a database access error occurs */ public java.sql.ResultSet getCrossReference(String primaryCatalog, String primarySchema, String primaryTable, String foreignCatalog, String foreignSchema, String foreignTable) throws SQLException { if (primaryTable == null) { throw SQLError.createSQLException("Table not specified.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } if (primaryCatalog == null) { if (this.conn.getNullCatalogMeansCurrent()) { primaryCatalog = this.database; } } if (foreignCatalog == null) { if (this.conn.getNullCatalogMeansCurrent()) { foreignCatalog = this.database; } } Field[] fields = new Field[14]; fields[0] = new Field("", "PKTABLE_CAT", Types.CHAR, 255); fields[1] = new Field("", "PKTABLE_SCHEM", Types.CHAR, 0); fields[2] = new Field("", "PKTABLE_NAME", Types.CHAR, 255); fields[3] = new Field("", "PKCOLUMN_NAME", Types.CHAR, 32); fields[4] = new Field("", "FKTABLE_CAT", Types.CHAR, 255); fields[5] = new Field("", "FKTABLE_SCHEM", Types.CHAR, 0); fields[6] = new Field("", "FKTABLE_NAME", Types.CHAR, 255); fields[7] = new Field("", "FKCOLUMN_NAME", Types.CHAR, 32); fields[8] = new Field("", "KEY_SEQ", Types.SMALLINT, 2); fields[9] = new Field("", "UPDATE_RULE", Types.SMALLINT, 2); fields[10] = new Field("", "DELETE_RULE", Types.SMALLINT, 2); fields[11] = new Field("", "FK_NAME", Types.CHAR, 0); fields[12] = new Field("", "PK_NAME", Types.CHAR, 0); fields[13] = new Field("", "DEFERRABILITY", Types.INTEGER, 2); String sql = "SELECT " + "A.REFERENCED_TABLE_SCHEMA AS PKTABLE_CAT," + "NULL AS PKTABLE_SCHEM," + "A.REFERENCED_TABLE_NAME AS PKTABLE_NAME," + "A.REFERENCED_COLUMN_NAME AS PKCOLUMN_NAME," + "A.TABLE_SCHEMA AS FKTABLE_CAT," + "NULL AS FKTABLE_SCHEM," + "A.TABLE_NAME AS FKTABLE_NAME, " + "A.COLUMN_NAME AS FKCOLUMN_NAME, " + "A.ORDINAL_POSITION AS KEY_SEQ," + generateUpdateRuleClause() + " AS UPDATE_RULE," + generateDeleteRuleClause() + " AS DELETE_RULE," + "A.CONSTRAINT_NAME AS FK_NAME," + "(SELECT CONSTRAINT_NAME FROM" + " INFORMATION_SCHEMA.TABLE_CONSTRAINTS" + " WHERE TABLE_SCHEMA = REFERENCED_TABLE_SCHEMA AND" + " TABLE_NAME = REFERENCED_TABLE_NAME AND" + " CONSTRAINT_TYPE IN ('UNIQUE','PRIMARY KEY') LIMIT 1)" + " AS PK_NAME," + importedKeyNotDeferrable + " AS DEFERRABILITY " + "FROM " + "INFORMATION_SCHEMA.KEY_COLUMN_USAGE A JOIN " + "INFORMATION_SCHEMA.TABLE_CONSTRAINTS B " + "USING (TABLE_SCHEMA, TABLE_NAME, CONSTRAINT_NAME) " + generateOptionalRefContraintsJoin() + "WHERE " + "B.CONSTRAINT_TYPE = 'FOREIGN KEY' " + "AND A.REFERENCED_TABLE_SCHEMA LIKE ? AND A.REFERENCED_TABLE_NAME=? " + "AND A.TABLE_SCHEMA LIKE ? AND A.TABLE_NAME=? " + "ORDER BY " + "A.TABLE_SCHEMA, A.TABLE_NAME, A.ORDINAL_POSITION"; PreparedStatement pStmt = null; try { pStmt = prepareMetaDataSafeStatement(sql); if (primaryCatalog != null) { pStmt.setString(1, primaryCatalog); } else { pStmt.setString(1, "%"); } pStmt.setString(2, primaryTable); if (foreignCatalog != null) { pStmt.setString(3, foreignCatalog); } else { pStmt.setString(3, "%"); } pStmt.setString(4, foreignTable); ResultSet rs = executeMetadataQuery(pStmt); ((com.mysql.jdbc.ResultSetInternalMethods) rs).redefineFieldsForDBMD(new Field[] { new Field("", "PKTABLE_CAT", Types.CHAR, 255), new Field("", "PKTABLE_SCHEM", Types.CHAR, 0), new Field("", "PKTABLE_NAME", Types.CHAR, 255), new Field("", "PKCOLUMN_NAME", Types.CHAR, 32), new Field("", "FKTABLE_CAT", Types.CHAR, 255), new Field("", "FKTABLE_SCHEM", Types.CHAR, 0), new Field("", "FKTABLE_NAME", Types.CHAR, 255), new Field("", "FKCOLUMN_NAME", Types.CHAR, 32), new Field("", "KEY_SEQ", Types.SMALLINT, 2), new Field("", "UPDATE_RULE", Types.SMALLINT, 2), new Field("", "DELETE_RULE", Types.SMALLINT, 2), new Field("", "FK_NAME", Types.CHAR, 0), new Field("", "PK_NAME", Types.CHAR, 0), new Field("", "DEFERRABILITY", Types.INTEGER, 2) }); return rs; } finally { if (pStmt != null) { pStmt.close(); } } } /** * Get a description of a foreign key columns that reference a table's * primary key columns (the foreign keys exported by a table). They are * ordered by FKTABLE_CAT, FKTABLE_SCHEM, FKTABLE_NAME, and KEY_SEQ. *

* Each foreign key column description has the following columns: *

    *
  1. PKTABLE_CAT String => primary key table catalog (may be * null)
  2. *
  3. PKTABLE_SCHEM String => primary key table schema (may be * null)
  4. *
  5. PKTABLE_NAME String => primary key table name
  6. *
  7. PKCOLUMN_NAME String => primary key column name
  8. *
  9. FKTABLE_CAT String => foreign key table catalog (may be * null) being exported (may be null)
  10. *
  11. FKTABLE_SCHEM String => foreign key table schema (may be * null) being exported (may be null)
  12. *
  13. FKTABLE_NAME String => foreign key table name being exported *
  14. *
  15. FKCOLUMN_NAME String => foreign key column name being * exported
  16. *
  17. KEY_SEQ short => sequence number within foreign key
  18. *
  19. UPDATE_RULE short => What happens to foreign key when * primary is updated: * *
  20. *
  21. DELETE_RULE short => What happens to the foreign key when * primary is deleted. * *
  22. *
  23. FK_NAME String => foreign key identifier (may be null)
  24. *
  25. PK_NAME String => primary key identifier (may be null)
  26. *
*

* * @param catalog * a catalog name; "" retrieves those without a catalog * @param schema * a schema name pattern; "" retrieves those without a schema * @param table * a table name * @return ResultSet each row is a foreign key column description * @throws SQLException * if a database access error occurs * @see #getImportedKeys */ public java.sql.ResultSet getExportedKeys(String catalog, String schema, String table) throws SQLException { // TODO: Can't determine actions using INFORMATION_SCHEMA yet... if (table == null) { throw SQLError.createSQLException("Table not specified.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } if (catalog == null) { if (this.conn.getNullCatalogMeansCurrent()) { catalog = this.database; } } //CASCADE, SET NULL, SET DEFAULT, RESTRICT, NO ACTION String sql = "SELECT " + "A.REFERENCED_TABLE_SCHEMA AS PKTABLE_CAT," + "NULL AS PKTABLE_SCHEM," + "A.REFERENCED_TABLE_NAME AS PKTABLE_NAME, " + "A.REFERENCED_COLUMN_NAME AS PKCOLUMN_NAME, " + "A.TABLE_SCHEMA AS FKTABLE_CAT," + "NULL AS FKTABLE_SCHEM," + "A.TABLE_NAME AS FKTABLE_NAME," + "A.COLUMN_NAME AS FKCOLUMN_NAME, " + "A.ORDINAL_POSITION AS KEY_SEQ," + generateUpdateRuleClause() + " AS UPDATE_RULE," + generateDeleteRuleClause() + " AS DELETE_RULE," + "A.CONSTRAINT_NAME AS FK_NAME," + "(SELECT CONSTRAINT_NAME FROM" + " INFORMATION_SCHEMA.TABLE_CONSTRAINTS" + " WHERE TABLE_SCHEMA = REFERENCED_TABLE_SCHEMA AND" + " TABLE_NAME = REFERENCED_TABLE_NAME AND" + " CONSTRAINT_TYPE IN ('UNIQUE','PRIMARY KEY') LIMIT 1)" + " AS PK_NAME," + importedKeyNotDeferrable + " AS DEFERRABILITY " + "FROM " + "INFORMATION_SCHEMA.KEY_COLUMN_USAGE A JOIN " + "INFORMATION_SCHEMA.TABLE_CONSTRAINTS B " + "USING (TABLE_SCHEMA, TABLE_NAME, CONSTRAINT_NAME) " + generateOptionalRefContraintsJoin() + "WHERE " + "B.CONSTRAINT_TYPE = 'FOREIGN KEY' " + "AND A.REFERENCED_TABLE_SCHEMA LIKE ? AND A.REFERENCED_TABLE_NAME=? " + "ORDER BY A.TABLE_SCHEMA, A.TABLE_NAME, A.ORDINAL_POSITION"; PreparedStatement pStmt = null; try { pStmt = prepareMetaDataSafeStatement(sql); if (catalog != null) { pStmt.setString(1, catalog); } else { pStmt.setString(1, "%"); } pStmt.setString(2, table); ResultSet rs = executeMetadataQuery(pStmt); ((com.mysql.jdbc.ResultSetInternalMethods) rs).redefineFieldsForDBMD(new Field[] { new Field("", "PKTABLE_CAT", Types.CHAR, 255), new Field("", "PKTABLE_SCHEM", Types.CHAR, 0), new Field("", "PKTABLE_NAME", Types.CHAR, 255), new Field("", "PKCOLUMN_NAME", Types.CHAR, 32), new Field("", "FKTABLE_CAT", Types.CHAR, 255), new Field("", "FKTABLE_SCHEM", Types.CHAR, 0), new Field("", "FKTABLE_NAME", Types.CHAR, 255), new Field("", "FKCOLUMN_NAME", Types.CHAR, 32), new Field("", "KEY_SEQ", Types.SMALLINT, 2), new Field("", "UPDATE_RULE", Types.SMALLINT, 2), new Field("", "DELETE_RULE", Types.SMALLINT, 2), new Field("", "FK_NAME", Types.CHAR, 255), new Field("", "PK_NAME", Types.CHAR, 0), new Field("", "DEFERRABILITY", Types.INTEGER, 2) }); return rs; } finally { if (pStmt != null) { pStmt.close(); } } } private String generateOptionalRefContraintsJoin() { return ((this.hasReferentialConstraintsView) ? "JOIN " + "INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS R " + "ON (R.CONSTRAINT_NAME = B.CONSTRAINT_NAME " + "AND R.TABLE_NAME = B.TABLE_NAME AND " + "R.CONSTRAINT_SCHEMA = B.TABLE_SCHEMA) " : ""); } private String generateDeleteRuleClause() { return ((this.hasReferentialConstraintsView) ? "CASE WHEN R.DELETE_RULE='CASCADE' THEN " + String.valueOf(importedKeyCascade) + " WHEN R.DELETE_RULE='SET NULL' THEN " + String.valueOf(importedKeySetNull) + " WHEN R.DELETE_RULE='SET DEFAULT' THEN " + String.valueOf(importedKeySetDefault) + " WHEN R.DELETE_RULE='RESTRICT' THEN " + String.valueOf(importedKeyRestrict) + " WHEN R.DELETE_RULE='NO ACTION' THEN " + String.valueOf(importedKeyNoAction) + " ELSE " + String.valueOf(importedKeyNoAction) + " END " : String.valueOf(importedKeyRestrict)); } private String generateUpdateRuleClause() { return ((this.hasReferentialConstraintsView) ? "CASE WHEN R.UPDATE_RULE='CASCADE' THEN " + String.valueOf(importedKeyCascade) + " WHEN R.UPDATE_RULE='SET NULL' THEN " + String.valueOf(importedKeySetNull) + " WHEN R.UPDATE_RULE='SET DEFAULT' THEN " + String.valueOf(importedKeySetDefault) + " WHEN R.UPDATE_RULE='RESTRICT' THEN " + String.valueOf(importedKeyRestrict) + " WHEN R.UPDATE_RULE='NO ACTION' THEN " + String.valueOf(importedKeyNoAction) + " ELSE " + String.valueOf(importedKeyNoAction) + " END " : String.valueOf(importedKeyRestrict)); } /** * Get a description of the primary key columns that are referenced by a * table's foreign key columns (the primary keys imported by a table). They * are ordered by PKTABLE_CAT, PKTABLE_SCHEM, PKTABLE_NAME, and KEY_SEQ. *

* Each primary key column description has the following columns: *

    *
  1. PKTABLE_CAT String => primary key table catalog being * imported (may be null)
  2. *
  3. PKTABLE_SCHEM String => primary key table schema being * imported (may be null)
  4. *
  5. PKTABLE_NAME String => primary key table name being imported *
  6. *
  7. PKCOLUMN_NAME String => primary key column name being * imported
  8. *
  9. FKTABLE_CAT String => foreign key table catalog (may be * null)
  10. *
  11. FKTABLE_SCHEM String => foreign key table schema (may be * null)
  12. *
  13. FKTABLE_NAME String => foreign key table name
  14. *
  15. FKCOLUMN_NAME String => foreign key column name
  16. *
  17. KEY_SEQ short => sequence number within foreign key
  18. *
  19. UPDATE_RULE short => What happens to foreign key when * primary is updated: * *
  20. *
  21. DELETE_RULE short => What happens to the foreign key when * primary is deleted. * *
  22. *
  23. FK_NAME String => foreign key name (may be null)
  24. *
  25. PK_NAME String => primary key name (may be null)
  26. *
*

* * @param catalog * a catalog name; "" retrieves those without a catalog * @param schema * a schema name pattern; "" retrieves those without a schema * @param table * a table name * @return ResultSet each row is a primary key column description * @throws SQLException * if a database access error occurs * @see #getExportedKeys */ public java.sql.ResultSet getImportedKeys(String catalog, String schema, String table) throws SQLException { if (table == null) { throw SQLError.createSQLException("Table not specified.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } if (catalog == null) { if (this.conn.getNullCatalogMeansCurrent()) { catalog = this.database; } } String sql = "SELECT " + "A.REFERENCED_TABLE_SCHEMA AS PKTABLE_CAT," + "NULL AS PKTABLE_SCHEM," + "A.REFERENCED_TABLE_NAME AS PKTABLE_NAME," + "A.REFERENCED_COLUMN_NAME AS PKCOLUMN_NAME," + "A.TABLE_SCHEMA AS FKTABLE_CAT," + "NULL AS FKTABLE_SCHEM," + "A.TABLE_NAME AS FKTABLE_NAME, " + "A.COLUMN_NAME AS FKCOLUMN_NAME, " + "A.ORDINAL_POSITION AS KEY_SEQ," + generateUpdateRuleClause() + " AS UPDATE_RULE," + generateDeleteRuleClause() + " AS DELETE_RULE," + "A.CONSTRAINT_NAME AS FK_NAME," + "(SELECT CONSTRAINT_NAME FROM" + " INFORMATION_SCHEMA.TABLE_CONSTRAINTS" + " WHERE TABLE_SCHEMA = REFERENCED_TABLE_SCHEMA AND" + " TABLE_NAME = REFERENCED_TABLE_NAME AND" + " CONSTRAINT_TYPE IN ('UNIQUE','PRIMARY KEY') LIMIT 1)" + " AS PK_NAME," + importedKeyNotDeferrable + " AS DEFERRABILITY " + "FROM " + "INFORMATION_SCHEMA.KEY_COLUMN_USAGE A " + "JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS B USING " + "(CONSTRAINT_NAME, TABLE_NAME) " + generateOptionalRefContraintsJoin() + "WHERE " + "B.CONSTRAINT_TYPE = 'FOREIGN KEY' " + "AND A.TABLE_SCHEMA LIKE ? " + "AND A.TABLE_NAME=? " + "AND A.REFERENCED_TABLE_SCHEMA IS NOT NULL " + "ORDER BY " + "A.REFERENCED_TABLE_SCHEMA, A.REFERENCED_TABLE_NAME, " + "A.ORDINAL_POSITION"; PreparedStatement pStmt = null; try { pStmt = prepareMetaDataSafeStatement(sql); if (catalog != null) { pStmt.setString(1, catalog); } else { pStmt.setString(1, "%"); } pStmt.setString(2, table); ResultSet rs = executeMetadataQuery(pStmt); ((com.mysql.jdbc.ResultSetInternalMethods) rs).redefineFieldsForDBMD(new Field[] { new Field("", "PKTABLE_CAT", Types.CHAR, 255), new Field("", "PKTABLE_SCHEM", Types.CHAR, 0), new Field("", "PKTABLE_NAME", Types.CHAR, 255), new Field("", "PKCOLUMN_NAME", Types.CHAR, 32), new Field("", "FKTABLE_CAT", Types.CHAR, 255), new Field("", "FKTABLE_SCHEM", Types.CHAR, 0), new Field("", "FKTABLE_NAME", Types.CHAR, 255), new Field("", "FKCOLUMN_NAME", Types.CHAR, 32), new Field("", "KEY_SEQ", Types.SMALLINT, 2), new Field("", "UPDATE_RULE", Types.SMALLINT, 2), new Field("", "DELETE_RULE", Types.SMALLINT, 2), new Field("", "FK_NAME", Types.CHAR, 255), new Field("", "PK_NAME", Types.CHAR, 0), new Field("", "DEFERRABILITY", Types.INTEGER, 2) }); return rs; } finally { if (pStmt != null) { pStmt.close(); } } } /** * Get a description of a table's indices and statistics. They are ordered * by NON_UNIQUE, TYPE, INDEX_NAME, and ORDINAL_POSITION. *

* Each index column description has the following columns: *

    *
  1. TABLE_CAT String => table catalog (may be null)
  2. *
  3. TABLE_SCHEM String => table schema (may be null)
  4. *
  5. TABLE_NAME String => table name
  6. *
  7. NON_UNIQUE boolean => Can index values be non-unique? false * when TYPE is tableIndexStatistic
  8. *
  9. INDEX_QUALIFIER String => index catalog (may be null); null * when TYPE is tableIndexStatistic
  10. *
  11. INDEX_NAME String => index name; null when TYPE is * tableIndexStatistic
  12. *
  13. TYPE short => index type: * *
  14. *
  15. ORDINAL_POSITION short => column sequence number within * index; zero when TYPE is tableIndexStatistic
  16. *
  17. COLUMN_NAME String => column name; null when TYPE is * tableIndexStatistic
  18. *
  19. ASC_OR_DESC String => column sort sequence, "A" => * ascending, "D" => descending, may be null if sort sequence is not * supported; null when TYPE is tableIndexStatistic
  20. *
  21. CARDINALITY int => When TYPE is tableIndexStatisic then this * is the number of rows in the table; otherwise it is the number of unique * values in the index.
  22. *
  23. PAGES int => When TYPE is tableIndexStatisic then this is * the number of pages used for the table, otherwise it is the number of * pages used for the current index.
  24. *
  25. FILTER_CONDITION String => Filter condition, if any. (may be * null)
  26. *
*

* * @param catalog * a catalog name; "" retrieves those without a catalog * @param schema * a schema name pattern; "" retrieves those without a schema * @param table * a table name * @param unique * when true, return only indices for unique values; when false, * return indices regardless of whether unique or not * @param approximate * when true, result is allowed to reflect approximate or out of * data values; when false, results are requested to be accurate * @return ResultSet each row is an index column description * @throws SQLException * DOCUMENT ME! */ public ResultSet getIndexInfo(String catalog, String schema, String table, boolean unique, boolean approximate) throws SQLException { StringBuffer sqlBuf = new StringBuffer("SELECT " + "TABLE_SCHEMA AS TABLE_CAT, " + "NULL AS TABLE_SCHEM," + "TABLE_NAME," + "NON_UNIQUE," + "TABLE_SCHEMA AS INDEX_QUALIFIER," + "INDEX_NAME," + tableIndexOther + " AS TYPE," + "SEQ_IN_INDEX AS ORDINAL_POSITION," + "COLUMN_NAME," + "COLLATION AS ASC_OR_DESC," + "CARDINALITY," + "NULL AS PAGES," + "NULL AS FILTER_CONDITION " + "FROM INFORMATION_SCHEMA.STATISTICS WHERE " + "TABLE_SCHEMA LIKE ? AND " + "TABLE_NAME LIKE ?"); if (unique) { sqlBuf.append(" AND NON_UNIQUE=0 "); } sqlBuf.append("ORDER BY NON_UNIQUE, INDEX_NAME, SEQ_IN_INDEX"); PreparedStatement pStmt = null; try { if (catalog == null) { if (this.conn.getNullCatalogMeansCurrent()) { catalog = this.database; } } pStmt = prepareMetaDataSafeStatement(sqlBuf.toString()); if (catalog != null) { pStmt.setString(1, catalog); } else { pStmt.setString(1, "%"); } pStmt.setString(2, table); ResultSet rs = executeMetadataQuery(pStmt); ((com.mysql.jdbc.ResultSetInternalMethods) rs).redefineFieldsForDBMD(new Field[] { new Field("", "TABLE_CAT", Types.CHAR, 255), new Field("", "TABLE_SCHEM", Types.CHAR, 0), new Field("", "TABLE_NAME", Types.CHAR, 255), new Field("", "NON_UNIQUE", Types.CHAR, 4), new Field("", "INDEX_QUALIFIER", Types.CHAR, 1), new Field("", "INDEX_NAME", Types.CHAR, 32), new Field("", "TYPE", Types.CHAR, 32), new Field("", "ORDINAL_POSITION", Types.SMALLINT, 5), new Field("", "COLUMN_NAME", Types.CHAR, 32), new Field("", "ASC_OR_DESC", Types.CHAR, 1), new Field("", "CARDINALITY", Types.INTEGER, 10), new Field("", "PAGES", Types.INTEGER, 10), new Field("", "FILTER_CONDITION", Types.CHAR, 32) }); return rs; } finally { if (pStmt != null) { pStmt.close(); } } } /** * Get a description of a table's primary key columns. They are ordered by * COLUMN_NAME. *

* Each column description has the following columns: *

    *
  1. TABLE_CAT String => table catalog (may be null)
  2. *
  3. TABLE_SCHEM String => table schema (may be null)
  4. *
  5. TABLE_NAME String => table name
  6. *
  7. COLUMN_NAME String => column name
  8. *
  9. KEY_SEQ short => sequence number within primary key
  10. *
  11. PK_NAME String => primary key name (may be null)
  12. *
*

* * @param catalog * a catalog name; "" retrieves those without a catalog * @param schema * a schema name pattern; "" retrieves those without a schema * @param table * a table name * @return ResultSet each row is a primary key column description * @throws SQLException * DOCUMENT ME! */ public java.sql.ResultSet getPrimaryKeys(String catalog, String schema, String table) throws SQLException { if (catalog == null) { if (this.conn.getNullCatalogMeansCurrent()) { catalog = this.database; } } if (table == null) { throw SQLError.createSQLException("Table not specified.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } String sql = "SELECT TABLE_SCHEMA AS TABLE_CAT, NULL AS TABLE_SCHEM, TABLE_NAME, " + "COLUMN_NAME, SEQ_IN_INDEX AS KEY_SEQ, 'PRIMARY' AS PK_NAME FROM INFORMATION_SCHEMA.STATISTICS " + "WHERE TABLE_SCHEMA LIKE ? AND TABLE_NAME LIKE ? AND " + "INDEX_NAME='PRIMARY' ORDER BY TABLE_SCHEMA, TABLE_NAME, INDEX_NAME, SEQ_IN_INDEX"; PreparedStatement pStmt = null; try { pStmt = prepareMetaDataSafeStatement(sql); if (catalog != null) { pStmt.setString(1, catalog); } else { pStmt.setString(1, "%"); } pStmt.setString(2, table); ResultSet rs = executeMetadataQuery(pStmt); ((com.mysql.jdbc.ResultSetInternalMethods) rs).redefineFieldsForDBMD(new Field[] { new Field("", "TABLE_CAT", Types.CHAR, 255), new Field("", "TABLE_SCHEM", Types.CHAR, 0), new Field("", "TABLE_NAME", Types.CHAR, 255), new Field("", "COLUMN_NAME", Types.CHAR, 32), new Field("", "KEY_SEQ", Types.SMALLINT, 5), new Field("", "PK_NAME", Types.CHAR, 32) }); return rs; } finally { if (pStmt != null) { pStmt.close(); } } } /** * Get a description of stored procedures available in a catalog. *

* Only procedure descriptions matching the schema and procedure name * criteria are returned. They are ordered by PROCEDURE_SCHEM, and * PROCEDURE_NAME. *

*

* Each procedure description has the the following columns: *

    *
  1. PROCEDURE_CAT String => procedure catalog (may be null) *
  2. *
  3. PROCEDURE_SCHEM String => procedure schema (may be null) *
  4. *
  5. PROCEDURE_NAME String => procedure name
  6. *
  7. reserved for future use
  8. *
  9. reserved for future use
  10. *
  11. reserved for future use
  12. *
  13. REMARKS String => explanatory comment on the procedure
  14. *
  15. PROCEDURE_TYPE short => kind of procedure: * *
  16. *
*

* * @param catalog * a catalog name; "" retrieves those without a catalog * @param schemaPattern * a schema name pattern; "" retrieves those without a schema * @param procedureNamePattern * a procedure name pattern * @return ResultSet each row is a procedure description * @throws SQLException * if a database access error occurs * @see #getSearchStringEscape */ public ResultSet getProcedures(String catalog, String schemaPattern, String procedureNamePattern) throws SQLException { if ((procedureNamePattern == null) || (procedureNamePattern.length() == 0)) { if (this.conn.getNullNamePatternMatchesAll()) { procedureNamePattern = "%"; } else { throw SQLError.createSQLException( "Procedure name pattern can not be NULL or empty.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } } String db = null; if (catalog == null) { if (this.conn.getNullCatalogMeansCurrent()) { db = this.database; } } String sql = "SELECT ROUTINE_SCHEMA AS PROCEDURE_CAT, " + "NULL AS PROCEDURE_SCHEM, " + "ROUTINE_NAME AS PROCEDURE_NAME, " + "NULL AS RESERVED_1, " + "NULL AS RESERVED_2, " + "NULL AS RESERVED_3, " + "ROUTINE_COMMENT AS REMARKS, " + "CASE WHEN ROUTINE_TYPE = 'PROCEDURE' THEN " + procedureNoResult + " WHEN ROUTINE_TYPE='FUNCTION' THEN " + procedureReturnsResult + " ELSE " + procedureResultUnknown + " END AS PROCEDURE_TYPE " + "FROM INFORMATION_SCHEMA.ROUTINES WHERE " + "ROUTINE_SCHEMA LIKE ? AND ROUTINE_NAME LIKE ? " + "ORDER BY ROUTINE_SCHEMA, ROUTINE_NAME"; PreparedStatement pStmt = null; try { pStmt = prepareMetaDataSafeStatement(sql); if (db != null) { pStmt.setString(1, db); } else { pStmt.setString(1, "%"); } pStmt.setString(2, procedureNamePattern); ResultSet rs = executeMetadataQuery(pStmt); ((com.mysql.jdbc.ResultSetInternalMethods) rs).redefineFieldsForDBMD(new Field[] { new Field("", "PROCEDURE_CAT", Types.CHAR, 0), new Field("", "PROCEDURE_SCHEM", Types.CHAR, 0), new Field("", "PROCEDURE_NAME", Types.CHAR, 0), new Field("", "reserved1", Types.CHAR, 0), new Field("", "reserved2", Types.CHAR, 0), new Field("", "reserved3", Types.CHAR, 0), new Field("", "REMARKS", Types.CHAR, 0), new Field("", "PROCEDURE_TYPE", Types.SMALLINT, 0) }); return rs; } finally { if (pStmt != null) { pStmt.close(); } } } /** * Get a description of tables available in a catalog. *

* Only table descriptions matching the catalog, schema, table name and type * criteria are returned. They are ordered by TABLE_TYPE, TABLE_SCHEM and * TABLE_NAME. *

*

* Each table description has the following columns: *

    *
  1. TABLE_CAT String => table catalog (may be null)
  2. *
  3. TABLE_SCHEM String => table schema (may be null)
  4. *
  5. TABLE_NAME String => table name
  6. *
  7. TABLE_TYPE String => table type. Typical types are "TABLE", * "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", "LOCAL TEMPORARY", "ALIAS", * "SYNONYM".
  8. *
  9. REMARKS String => explanatory comment on the table
  10. *
*

*

* Note: Some databases may not return information for all tables. *

* * @param catalog * a catalog name; "" retrieves those without a catalog * @param schemaPattern * a schema name pattern; "" retrieves those without a schema * @param tableNamePattern * a table name pattern * @param types * a list of table types to include; null returns all types * @return ResultSet each row is a table description * @throws SQLException * DOCUMENT ME! * @see #getSearchStringEscape */ public ResultSet getTables(String catalog, String schemaPattern, String tableNamePattern, String[] types) throws SQLException { if (catalog == null) { if (this.conn.getNullCatalogMeansCurrent()) { catalog = this.database; } } if (tableNamePattern == null) { if (this.conn.getNullNamePatternMatchesAll()) { tableNamePattern = "%"; } else { throw SQLError.createSQLException( "Table name pattern can not be NULL or empty.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } } PreparedStatement pStmt = null; String sql = "SELECT TABLE_SCHEMA AS TABLE_CAT, " + "NULL AS TABLE_SCHEM, TABLE_NAME, " + "CASE WHEN TABLE_TYPE='BASE TABLE' THEN 'TABLE' WHEN TABLE_TYPE='TEMPORARY' THEN 'LOCAL_TEMPORARY' ELSE TABLE_TYPE END AS TABLE_TYPE, " + "TABLE_COMMENT AS REMARKS " + "FROM INFORMATION_SCHEMA.TABLES WHERE " + "TABLE_SCHEMA LIKE ? AND TABLE_NAME LIKE ? AND TABLE_TYPE IN (?,?,?) " + "ORDER BY TABLE_TYPE, TABLE_SCHEMA, TABLE_NAME"; try { pStmt = prepareMetaDataSafeStatement(sql); if (catalog != null) { pStmt.setString(1, catalog); } else { pStmt.setString(1, "%"); } pStmt.setString(2, tableNamePattern); // This overloading of IN (...) allows us to cache this // prepared statement if (types == null || types.length == 0) { pStmt.setString(3, "BASE TABLE"); pStmt.setString(4, "VIEW"); pStmt.setString(5, "TEMPORARY"); } else { pStmt.setNull(3, Types.VARCHAR); pStmt.setNull(4, Types.VARCHAR); pStmt.setNull(5, Types.VARCHAR); for (int i = 0; i < types.length; i++) { if ("TABLE".equalsIgnoreCase(types[i])) { pStmt.setString(3, "BASE TABLE"); } if ("VIEW".equalsIgnoreCase(types[i])) { pStmt.setString(4, "VIEW"); } if ("LOCAL TEMPORARY".equalsIgnoreCase(types[i])) { pStmt.setString(5, "TEMPORARY"); } } } ResultSet rs = executeMetadataQuery(pStmt); ((com.mysql.jdbc.ResultSetInternalMethods) rs).redefineFieldsForDBMD(new Field[] { new Field("", "TABLE_CAT", java.sql.Types.VARCHAR, (catalog == null) ? 0 : catalog.length()), new Field("", "TABLE_SCHEM", java.sql.Types.VARCHAR, 0), new Field("", "TABLE_NAME", java.sql.Types.VARCHAR, 255), new Field("", "TABLE_TYPE", java.sql.Types.VARCHAR, 5), new Field("", "REMARKS", java.sql.Types.VARCHAR, 0) }); return rs; } finally { if (pStmt != null) { pStmt.close(); } } } private PreparedStatement prepareMetaDataSafeStatement(String sql) throws SQLException { // Can't use server-side here as we coerce a lot of types to match // the spec. PreparedStatement pStmt = (PreparedStatement) this.conn.clientPrepareStatement(sql); if (pStmt.getMaxRows() != 0) { pStmt.setMaxRows(0); } pStmt.setHoldResultsOpenOverClose(true); return pStmt; } }