| // SPDX-License-Identifier: LGPL-2.1-or-later |
| // Copyright (c) 2012-2014 Monty Program Ab |
| // Copyright (c) 2015-2021 MariaDB Corporation Ab |
| |
| package org.mariadb.jdbc; |
| |
| import java.sql.*; |
| import java.sql.Statement; |
| import java.text.ParseException; |
| import java.util.*; |
| import org.mariadb.jdbc.client.DataType; |
| import org.mariadb.jdbc.client.ServerVersion; |
| import org.mariadb.jdbc.client.result.CompleteResult; |
| import org.mariadb.jdbc.client.result.Result; |
| import org.mariadb.jdbc.util.VersionFactory; |
| import org.mariadb.jdbc.util.constants.ColumnFlags; |
| import org.mariadb.jdbc.util.constants.ServerStatus; |
| |
| /** Mariadb Database metadata */ |
| public class DatabaseMetaData implements java.sql.DatabaseMetaData { |
| |
| /** MariaDB driver name */ |
| public static final String DRIVER_NAME = "MariaDB Connector/J"; |
| |
| private final org.mariadb.jdbc.Connection connection; |
| private final Configuration conf; |
| |
| /** |
| * Constructor. |
| * |
| * @param connection connection |
| * @param conf configuration |
| */ |
| public DatabaseMetaData(org.mariadb.jdbc.Connection connection, Configuration conf) { |
| this.connection = connection; |
| this.conf = conf; |
| } |
| |
| private static String DataTypeClause(Configuration conf) { |
| String upperCaseWithoutSize = |
| " UCASE(IF( COLUMN_TYPE LIKE '%(%)%', CONCAT(SUBSTRING( COLUMN_TYPE,1, LOCATE('('," |
| + "COLUMN_TYPE) - 1 ), SUBSTRING(COLUMN_TYPE ,1+locate(')', COLUMN_TYPE))), " |
| + "COLUMN_TYPE))"; |
| |
| if (conf.tinyInt1isBit()) { |
| upperCaseWithoutSize = |
| " IF(COLUMN_TYPE like 'tinyint(1)%', '" |
| + (conf.transformedBitIsBoolean() ? "BOOLEAN" : "BIT") |
| + "', " |
| + upperCaseWithoutSize |
| + ")"; |
| } |
| |
| if (!conf.yearIsDateType()) { |
| return " IF(COLUMN_TYPE IN ('year(2)', 'year(4)'), 'SMALLINT', " + upperCaseWithoutSize + ")"; |
| } |
| |
| return upperCaseWithoutSize; |
| } |
| |
| // Extract identifier quoted string from input String. |
| // Return new position, or -1 on error |
| private static int skipWhiteSpace(char[] part, int startPos) { |
| for (int i = startPos; i < part.length; i++) { |
| if (!Character.isWhitespace(part[i])) { |
| return i; |
| } |
| } |
| return part.length; |
| } |
| |
| private static int parseIdentifier(char[] part, int startPos, Identifier identifier) |
| throws ParseException { |
| int pos = skipWhiteSpace(part, startPos); |
| if (part[pos] != '`') { |
| throw new ParseException(new String(part), pos); |
| } |
| pos++; |
| StringBuilder sb = new StringBuilder(); |
| int quotes = 0; |
| for (; pos < part.length; pos++) { |
| char ch = part[pos]; |
| if (ch == '`') { |
| quotes++; |
| } else { |
| for (int j = 0; j < quotes / 2; j++) { |
| sb.append('`'); |
| } |
| if (quotes % 2 == 1) { |
| if (ch == '.') { |
| if (identifier.schema != null) { |
| throw new ParseException(new String(part), pos); |
| } |
| identifier.schema = sb.toString(); |
| return parseIdentifier(part, pos + 1, identifier); |
| } |
| identifier.name = sb.toString(); |
| return pos; |
| } |
| quotes = 0; |
| sb.append(ch); |
| } |
| } |
| throw new ParseException(new String(part), startPos); |
| } |
| |
| private static int skipKeyword(char[] part, int startPos, String keyword) throws ParseException { |
| int pos = skipWhiteSpace(part, startPos); |
| for (int i = 0; i < keyword.length(); i++, pos++) { |
| if (part[pos] != keyword.charAt(i)) { |
| throw new ParseException(new String(part), pos); |
| } |
| } |
| return pos; |
| } |
| |
| private static int getImportedKeyAction(String actionKey) { |
| if (actionKey == null) { |
| return java.sql.DatabaseMetaData.importedKeyRestrict; |
| } |
| switch (actionKey) { |
| case "NO ACTION": |
| return java.sql.DatabaseMetaData.importedKeyNoAction; |
| |
| case "CASCADE": |
| return java.sql.DatabaseMetaData.importedKeyCascade; |
| |
| case "SET NULL": |
| return java.sql.DatabaseMetaData.importedKeySetNull; |
| |
| case "SET DEFAULT": |
| return java.sql.DatabaseMetaData.importedKeySetDefault; |
| |
| case "RESTRICT": |
| return java.sql.DatabaseMetaData.importedKeyRestrict; |
| |
| default: |
| throw new IllegalArgumentException("Illegal key action '" + actionKey + "' specified."); |
| } |
| } |
| |
| private static String quoteIdentifier(String string) { |
| return "`" + string.replaceAll("`", "``") + "`"; |
| } |
| |
| /** |
| * Escape String. |
| * |
| * @param value value to escape |
| * @param noBackslashEscapes must backslash be escaped |
| * @return escaped string. |
| */ |
| public static String escapeString(String value, boolean noBackslashEscapes) { |
| if (noBackslashEscapes) { |
| return value.replace("'", "''"); |
| } |
| return value |
| .replace("\\", "\\\\") |
| .replace("'", "\\'") |
| .replace("\0", "\\0") |
| .replace("\"", "\\\""); |
| } |
| |
| private int parseIdentifierList(char[] part, int startPos, List<Identifier> list) |
| throws ParseException { |
| int pos = skipWhiteSpace(part, startPos); |
| if (part[pos] != '(') { |
| throw new ParseException(new String(part), pos); |
| } |
| pos++; |
| for (; ; ) { |
| pos = skipWhiteSpace(part, pos); |
| char ch = part[pos]; |
| switch (ch) { |
| case ')': |
| return pos + 1; |
| case '`': |
| Identifier id = new Identifier(); |
| pos = parseIdentifier(part, pos, id); |
| list.add(id); |
| break; |
| case ',': |
| pos++; |
| break; |
| default: |
| throw new ParseException(new String(part, startPos, part.length - startPos), startPos); |
| } |
| } |
| } |
| |
| /** |
| * Get imported keys. |
| * |
| * @param tableDef table definition |
| * @param tableName table name |
| * @param catalog catalog |
| * @param connection connection |
| * @return resultset resultset |
| * @throws ParseException exception |
| */ |
| private ResultSet getImportedKeys( |
| String tableDef, String tableName, String catalog, org.mariadb.jdbc.Connection connection) |
| throws Exception, SQLException { |
| boolean importedKeysWithConstraintNames = |
| Boolean.parseBoolean( |
| conf.nonMappedOptions().getProperty("importedKeysWithConstraintNames", "true")); |
| String[] columnNames = { |
| "PKTABLE_CAT", "PKTABLE_SCHEM", "PKTABLE_NAME", |
| "PKCOLUMN_NAME", "FKTABLE_CAT", "FKTABLE_SCHEM", |
| "FKTABLE_NAME", "FKCOLUMN_NAME", "KEY_SEQ", |
| "UPDATE_RULE", "DELETE_RULE", "FK_NAME", |
| "PK_NAME", "DEFERRABILITY" |
| }; |
| DataType[] dataTypes = { |
| DataType.VARCHAR, DataType.NULL, DataType.VARCHAR, |
| DataType.VARCHAR, DataType.VARCHAR, DataType.NULL, |
| DataType.VARCHAR, DataType.VARCHAR, DataType.SMALLINT, |
| DataType.SMALLINT, DataType.SMALLINT, DataType.VARCHAR, |
| DataType.VARCHAR, DataType.SMALLINT |
| }; |
| |
| String[] parts = tableDef.split("\n"); |
| |
| List<String[]> data = new ArrayList<>(); |
| |
| for (String part : parts) { |
| part = part.trim(); |
| if (!part.toUpperCase(Locale.ROOT).startsWith("CONSTRAINT") |
| && !part.toUpperCase(Locale.ROOT).contains("FOREIGN KEY")) { |
| continue; |
| } |
| char[] partChar = part.toCharArray(); |
| |
| Identifier constraintName = new Identifier(); |
| |
| int pos = skipKeyword(partChar, 0, "CONSTRAINT"); |
| pos = parseIdentifier(partChar, pos, constraintName); |
| pos = skipKeyword(partChar, pos, "FOREIGN KEY"); |
| List<Identifier> foreignKeyCols = new ArrayList<>(); |
| pos = parseIdentifierList(partChar, pos, foreignKeyCols); |
| pos = skipKeyword(partChar, pos, "REFERENCES"); |
| Identifier pkTable = new Identifier(); |
| pos = parseIdentifier(partChar, pos, pkTable); |
| List<Identifier> primaryKeyCols = new ArrayList<>(); |
| parseIdentifierList(partChar, pos, primaryKeyCols); |
| |
| int onUpdateReferenceAction = java.sql.DatabaseMetaData.importedKeyRestrict; |
| int onDeleteReferenceAction = java.sql.DatabaseMetaData.importedKeyRestrict; |
| |
| for (String referenceAction : |
| new String[] {"RESTRICT", "CASCADE", "SET NULL", "NO ACTION", "SET DEFAULT"}) { |
| if (part.toUpperCase(Locale.ROOT).contains("ON UPDATE " + referenceAction)) { |
| onUpdateReferenceAction = getImportedKeyAction(referenceAction); |
| } |
| if (part.toUpperCase(Locale.ROOT).contains("ON DELETE " + referenceAction)) { |
| onDeleteReferenceAction = getImportedKeyAction(referenceAction); |
| } |
| } |
| Map<String, Map<String[], String>> externalInfos = new HashMap<>(); |
| for (int i = 0; i < primaryKeyCols.size(); i++) { |
| |
| String[] row = new String[columnNames.length]; |
| row[0] = pkTable.schema; // PKTABLE_CAT |
| if (row[0] == null) { |
| row[0] = catalog; |
| } |
| row[1] = null; // PKTABLE_SCHEM |
| row[2] = pkTable.name; // PKTABLE_NAME |
| row[3] = primaryKeyCols.get(i).name; // PKCOLUMN_NAME |
| row[4] = catalog; // FKTABLE_CAT |
| row[5] = null; // FKTABLE_SCHEM |
| row[6] = tableName; // FKTABLE_NAME |
| row[7] = foreignKeyCols.get(i).name; // FKCOLUMN_NAME |
| row[8] = Integer.toString(i + 1); // KEY_SEQ |
| row[9] = Integer.toString(onUpdateReferenceAction); // UPDATE_RULE |
| row[10] = Integer.toString(onDeleteReferenceAction); // DELETE_RULE |
| row[11] = constraintName.name; // FK_NAME |
| if (importedKeysWithConstraintNames) { |
| String ext = |
| (pkTable.schema == null ? "" : quoteIdentifier(pkTable.schema) + ".") |
| + quoteIdentifier(pkTable.name); |
| if (!externalInfos.containsKey(ext)) { |
| externalInfos.put(ext, getExtImportedKeys(ext, connection)); |
| } |
| row[12] = null; // PK_NAME |
| Map<String[], String> externalInfo = externalInfos.get(ext); |
| if (externalInfo != null) { |
| for (Map.Entry<String[], String> entry : externalInfo.entrySet()) { |
| boolean foundAll = true; |
| |
| for (String keyPart : entry.getKey()) { |
| boolean foundKey = false; |
| for (Identifier keyCol : primaryKeyCols) { |
| if (keyCol.name.equals(keyPart)) { |
| foundKey = true; |
| break; |
| } |
| } |
| if (!foundKey) foundAll = false; |
| } |
| if (foundAll) { |
| row[12] = entry.getValue(); |
| } |
| } |
| } |
| } else { |
| row[12] = null; // PK_NAME |
| } |
| row[13] = Integer.toString(DatabaseMetaData.importedKeyNotDeferrable); // DEFERRABILITY |
| data.add(row); |
| } |
| } |
| String[][] arr = data.toArray(new String[0][]); |
| |
| /* Sort array by PKTABLE_CAT, PKTABLE_NAME, and KEY_SEQ.*/ |
| Arrays.sort( |
| arr, |
| (row1, row2) -> { |
| int result = row1[0].compareTo(row2[0]); // PKTABLE_CAT |
| if (result == 0) { |
| result = row1[2].compareTo(row2[2]); // PKTABLE_NAME |
| if (result == 0) { |
| result = row1[8].length() - row2[8].length(); // KEY_SEQ |
| if (result == 0) { |
| result = row1[8].compareTo(row2[8]); |
| } |
| } |
| } |
| return result; |
| }); |
| return CompleteResult.createResultSet( |
| columnNames, dataTypes, arr, connection.getContext(), ColumnFlags.PRIMARY_KEY); |
| } |
| |
| private Map<String[], String> getExtImportedKeys( |
| String tableName, org.mariadb.jdbc.Connection connection) throws SQLException { |
| ResultSet rs = connection.createStatement().executeQuery("SHOW CREATE TABLE " + tableName); |
| rs.next(); |
| String refTableDef = rs.getString(2); |
| Map<String[], String> res = new HashMap<>(); |
| String[] parts = refTableDef.split("\n"); |
| for (int i = 1; i < parts.length - 1; i++) { |
| String part = parts[i].trim(); |
| if (part.startsWith("`")) { |
| // field |
| continue; |
| } |
| if (part.startsWith("PRIMARY KEY") || part.startsWith("UNIQUE KEY")) { |
| String name = "PRIMARY"; |
| if (part.indexOf("`") < part.indexOf("(")) { |
| int offset = part.indexOf("`"); |
| name = part.substring(offset + 1, part.indexOf("`", offset + 1)); |
| } |
| |
| String subPart = part.substring(part.indexOf("(") + 1, part.lastIndexOf(")")); |
| List<String> cols = new ArrayList<>(); |
| int pos = 0; |
| while (pos < subPart.length()) { |
| pos = subPart.indexOf("`", pos); |
| int endpos = subPart.indexOf("`", pos + 1); |
| cols.add(subPart.substring(pos + 1, endpos)); |
| pos = endpos + 1; |
| } |
| res.put(cols.toArray(new String[0]), name); |
| } |
| } |
| return res; |
| } |
| |
| /** |
| * Retrieves a description of the primary key columns that are referenced by the given 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. |
| * |
| * <p>Each primary key column description has the following columns: |
| * |
| * <OL> |
| * <LI><B>PKTABLE_CAT</B> String {@code =>} primary key table catalog being imported (may be |
| * <code>null</code>) |
| * <LI><B>PKTABLE_SCHEM</B> String {@code =>} primary key table schema being imported (may be |
| * <code>null</code>) |
| * <LI><B>PKTABLE_NAME</B> String {@code =>} primary key table name being imported |
| * <LI><B>PKCOLUMN_NAME</B> String {@code =>} primary key column name being imported |
| * <LI><B>FKTABLE_CAT</B> String {@code =>} foreign key table catalog (may be <code>null</code>) |
| * <LI><B>FKTABLE_SCHEM</B> String {@code =>} foreign key table schema (may be <code>null</code> |
| * ) |
| * <LI><B>FKTABLE_NAME</B> String {@code =>} foreign key table name |
| * <LI><B>FKCOLUMN_NAME</B> String {@code =>} foreign key column name |
| * <LI><B>KEY_SEQ</B> short {@code =>} sequence number within a foreign key( a value of 1 |
| * represents the first column of the foreign key, a value of 2 would represent the second |
| * column within the foreign key). |
| * <LI><B>UPDATE_RULE</B> short {@code =>} What happens to a foreign key when the primary key is |
| * updated: |
| * <UL> |
| * <LI>importedNoAction - do not allow update of primary key if it has been imported |
| * <LI>importedKeyCascade - change imported key to agree with primary key update |
| * <LI>importedKeySetNull - change imported key to <code>NULL</code> if its primary key |
| * has been updated |
| * <LI>importedKeySetDefault - change imported key to default values if its primary key |
| * has been updated |
| * <LI>importedKeyRestrict - same as importedKeyNoAction (for ODBC 2.x compatibility) |
| * </UL> |
| * <LI><B>DELETE_RULE</B> short {@code =>} What happens to the foreign key when primary is |
| * deleted. |
| * <UL> |
| * <LI>importedKeyNoAction - do not allow delete of primary key if it has been imported |
| * <LI>importedKeyCascade - delete rows that import a deleted key |
| * <LI>importedKeySetNull - change imported key to NULL if its primary key has been |
| * deleted |
| * <LI>importedKeyRestrict - same as importedKeyNoAction (for ODBC 2.x compatibility) |
| * <LI>importedKeySetDefault - change imported key to default if its primary key has been |
| * deleted |
| * </UL> |
| * <LI><B>FK_NAME</B> String {@code =>} foreign key name (may be <code>null</code>) |
| * <LI><B>PK_NAME</B> String {@code =>} primary key name (may be <code>null</code>) |
| * <LI><B>DEFERRABILITY</B> short {@code =>} can the evaluation of foreign key constraints be |
| * deferred until commit |
| * <UL> |
| * <LI>importedKeyInitiallyDeferred - see SQL92 for definition |
| * <LI>importedKeyInitiallyImmediate - see SQL92 for definition |
| * <LI>importedKeyNotDeferrable - see SQL92 for definition |
| * </UL> |
| * </OL> |
| * |
| * @param catalog a catalog name; must match the catalog name as it is stored in the database; "" |
| * retrieves those without a catalog; <code>null</code> means that the catalog name should not |
| * be used to narrow the search |
| * @param schema a schema name; must match the schema name as it is stored in the database; "" |
| * retrieves those without a schema; <code>null</code> means that the schema name should not |
| * be used to narrow the search |
| * @param table a table name; must match the table name as it is stored in the database |
| * @return <code>ResultSet</code> - each row is a primary key column description |
| * @throws SQLException if a database access error occurs |
| * @see #getExportedKeys |
| */ |
| public ResultSet getImportedKeys(String catalog, String schema, String table) |
| throws SQLException { |
| |
| // We avoid using information schema queries by default, because this appears to be an expensive |
| // query (CONJ-41). |
| if (table == null) { |
| throw new SQLException("'table' parameter in getImportedKeys cannot be null"); |
| } |
| |
| if (catalog == null || catalog.isEmpty()) { |
| return getImportedKeysUsingInformationSchema(catalog, table); |
| } |
| |
| try { |
| return getImportedKeysUsingShowCreateTable(catalog, table); |
| } catch (Exception e) { |
| // Likely, parsing failed, try out I_S query. |
| return getImportedKeysUsingInformationSchema(catalog, table); |
| } |
| } |
| |
| private String dataTypeClause(String fullTypeColumnName) { |
| return " CASE data_type" |
| + " WHEN 'bit' THEN " |
| + Types.BIT |
| + " WHEN 'tinyblob' THEN " |
| + Types.VARBINARY |
| + " WHEN 'mediumblob' THEN " |
| + Types.LONGVARBINARY |
| + " WHEN 'longblob' THEN " |
| + Types.LONGVARBINARY |
| + " WHEN 'blob' THEN " |
| + Types.LONGVARBINARY |
| + " WHEN 'tinytext' THEN " |
| + Types.VARCHAR |
| + " WHEN 'mediumtext' THEN " |
| + Types.LONGVARCHAR |
| + " WHEN 'longtext' THEN " |
| + Types.LONGVARCHAR |
| + " WHEN 'text' THEN " |
| + Types.LONGVARCHAR |
| + " WHEN 'date' THEN " |
| + Types.DATE |
| + " WHEN 'datetime' THEN " |
| + Types.TIMESTAMP |
| + " WHEN 'decimal' THEN " |
| + Types.DECIMAL |
| + " WHEN 'double' THEN " |
| + Types.DOUBLE |
| + " WHEN 'enum' THEN " |
| + Types.VARCHAR |
| + " WHEN 'float' THEN " |
| + Types.REAL |
| + " WHEN 'int' THEN IF( " |
| + fullTypeColumnName |
| + " like '%unsigned%', " |
| + Types.INTEGER |
| + "," |
| + Types.INTEGER |
| + ")" |
| + " WHEN 'bigint' THEN " |
| + Types.BIGINT |
| + " WHEN 'mediumint' THEN " |
| + Types.INTEGER |
| + " WHEN 'null' THEN " |
| + Types.NULL |
| + " WHEN 'set' THEN " |
| + Types.VARCHAR |
| + " WHEN 'smallint' THEN IF( " |
| + fullTypeColumnName |
| + " like '%unsigned%', " |
| + Types.SMALLINT |
| + "," |
| + Types.SMALLINT |
| + ")" |
| + " WHEN 'varchar' THEN " |
| + Types.VARCHAR |
| + " WHEN 'varbinary' THEN " |
| + Types.VARBINARY |
| + " WHEN 'char' THEN " |
| + Types.CHAR |
| + " WHEN 'binary' THEN " |
| + Types.BINARY |
| + " WHEN 'time' THEN " |
| + Types.TIME |
| + " WHEN 'timestamp' THEN " |
| + Types.TIMESTAMP |
| + " WHEN 'tinyint' THEN " |
| + (conf.tinyInt1isBit() |
| ? "IF(" |
| + fullTypeColumnName |
| + " like 'tinyint(1)%'," |
| + (conf.transformedBitIsBoolean() ? Types.BOOLEAN : Types.BIT) |
| + "," |
| + Types.TINYINT |
| + ") " |
| : Types.TINYINT) |
| + " WHEN 'year' THEN " |
| + (conf.yearIsDateType() ? Types.DATE : Types.SMALLINT) |
| + " ELSE " |
| + Types.OTHER |
| + " END "; |
| } |
| |
| private ResultSet executeQuery(String sql) throws SQLException { |
| Statement stmt = connection.createStatement(); |
| Result rs = (Result) stmt.executeQuery(sql); |
| rs.setStatement(null); // bypass Hibernate statement tracking (CONJ-49) |
| rs.useAliasAsName(); |
| return rs; |
| } |
| |
| private String escapeQuote(String value) { |
| return value == null |
| ? "null" |
| : "'" |
| + escapeString( |
| value, |
| (connection.getContext().getServerStatus() & ServerStatus.NO_BACKSLASH_ESCAPES) > 0) |
| + "'"; |
| } |
| |
| /** |
| * Generate part of the information schema query that restricts catalog names In the driver, |
| * catalogs is the equivalent to MariaDB schemas. |
| * |
| * @param columnName - column name in the information schema table |
| * @param catalog - catalog name. This driver does not (always) follow JDBC standard for following |
| * special values, due to ConnectorJ compatibility 1. empty string ("") - matches current |
| * catalog (i.e. database). JDBC standard says only tables without catalog should be returned |
| * - such tables do not exist in MariaDB. If there is no current catalog, then empty string |
| * matches any catalog. 2. null - if nullCatalogMeansCurrent=true (which is the default), then |
| * the handling is the same as for "" . i.e. return current catalog.JDBC-conforming way would |
| * be to match any catalog with null parameter. This can be switched with |
| * nullCatalogMeansCurrent=false in the connection URL. |
| * @return part of SQL query ,that restricts search for the catalog. |
| */ |
| private boolean catalogCond( |
| boolean firstCondition, StringBuilder sb, String columnName, String catalog) { |
| // null catalog => searching without any catalog restriction |
| if (catalog == null) return firstCondition; |
| |
| // empty catalog => search restricting to current catalog |
| if (catalog.isEmpty()) { |
| sb.append(firstCondition ? " WHERE " : " AND ").append(columnName).append(" = database()"); |
| return false; |
| } |
| |
| // search with specified catalog |
| sb.append(firstCondition ? " WHERE " : " AND ") |
| .append(columnName) |
| .append("=") |
| .append(escapeQuote(catalog)); |
| return false; |
| } |
| |
| // Helper to generate information schema queries with "like" or "equals" condition (typically on |
| // table name) |
| private boolean patternCond( |
| boolean firstCondition, StringBuilder sb, String columnName, String tableName) { |
| if (tableName == null || "%".equals(tableName)) { |
| return firstCondition; |
| } |
| sb.append(firstCondition ? " WHERE " : " AND ") |
| .append(columnName) |
| .append((tableName.indexOf('%') == -1 && tableName.indexOf('_') == -1) ? "=" : " LIKE ") |
| .append("'") |
| .append( |
| escapeString( |
| tableName, |
| (connection.getContext().getServerStatus() & ServerStatus.NO_BACKSLASH_ESCAPES) |
| != 0)) |
| .append("'"); |
| return false; |
| } |
| |
| /** |
| * Retrieves a description of the given table's primary key columns. They are ordered by |
| * COLUMN_NAME. |
| * |
| * <p>Each primary key column description has the following columns: |
| * |
| * <OL> |
| * <li><B>TABLE_CAT</B> String {@code =>} table catalog |
| * <li><B>TABLE_SCHEM</B> String {@code =>} table schema (may be <code>null</code>) |
| * <li><B>TABLE_NAME</B> String {@code =>} table name |
| * <li><B>COLUMN_NAME</B> String {@code =>} column name |
| * <li><B>KEY_SEQ</B> short {@code =>} sequence number within primary key( a value of 1 |
| * represents the first column of the primary key, a value of 2 would represent the second |
| * column within the primary key). |
| * <li><B>PK_NAME</B> String {@code =>} primary key name |
| * </OL> |
| * |
| * @param catalog a catalog name; must match the catalog name as it is stored in the database; "" |
| * retrieves those without a catalog; <code>null</code> means that the catalog name should not |
| * be used to narrow the search |
| * @param schema a schema name; must match the schema name as it is stored in the database; "" |
| * retrieves those without a schema; <code>null</code> means that the schema name should not |
| * be used to narrow the search |
| * @param table a table name; must match the table name as it is stored in the database |
| * @return <code>ResultSet</code> - each row is a primary key column description |
| * @throws SQLException if a database access error occurs |
| */ |
| public ResultSet getPrimaryKeys(String catalog, String schema, String table) throws SQLException { |
| // MySQL 8 now use 'PRI' in place of 'pri' |
| StringBuilder sb = |
| new StringBuilder( |
| "SELECT A.TABLE_SCHEMA TABLE_CAT, NULL TABLE_SCHEM, A.TABLE_NAME, A.COLUMN_NAME," |
| + " B.SEQ_IN_INDEX KEY_SEQ, B.INDEX_NAME PK_NAME FROM INFORMATION_SCHEMA.COLUMNS" |
| + " A, INFORMATION_SCHEMA.STATISTICS B WHERE A.COLUMN_KEY in ('PRI','pri') AND" |
| + " B.INDEX_NAME='PRIMARY'"); |
| |
| catalogCond(false, sb, "A.TABLE_SCHEMA", catalog); |
| catalogCond(false, sb, "B.TABLE_SCHEMA", catalog); |
| patternCond(false, sb, "A.TABLE_NAME", table); |
| patternCond(false, sb, "B.TABLE_NAME", table); |
| sb.append( |
| " AND A.TABLE_SCHEMA = B.TABLE_SCHEMA AND A.TABLE_NAME = B.TABLE_NAME AND A.COLUMN_NAME =" |
| + " B.COLUMN_NAME ORDER BY A.COLUMN_NAME"); |
| |
| return executeQuery(sb.toString()); |
| } |
| |
| /** |
| * Retrieves a description of the tables available in the given catalog. Only table descriptions |
| * matching the catalog, schema, table name and type criteria are returned. They are ordered by |
| * <code>TABLE_TYPE</code>, <code>TABLE_CAT</code>, <code>TABLE_SCHEM</code> and <code>TABLE_NAME |
| * </code>. Each table description has the following columns: |
| * |
| * <OL> |
| * <LI><B>TABLE_CAT</B> String {@code =>} table catalog (may be <code>null</code>) |
| * <LI><B>TABLE_SCHEM</B> String {@code =>} table schema (may be <code>null</code>) |
| * <LI><B>TABLE_NAME</B> String {@code =>} table name |
| * <LI><B>TABLE_TYPE</B> String {@code =>} table type. Typical types are "TABLE", "VIEW", |
| * "SYSTEM TABLE", "GLOBAL TEMPORARY", "LOCAL TEMPORARY", "ALIAS", "SYNONYM". |
| * <LI><B>REMARKS</B> String {@code =>} explanatory comment on the table |
| * <LI><B>TYPE_CAT</B> String {@code =>} the types catalog (may be <code>null</code>) |
| * <LI><B>TYPE_SCHEM</B> String {@code =>} the types schema (may be <code>null</code>) |
| * <LI><B>TYPE_NAME</B> String {@code =>} type name (may be <code>null</code>) |
| * <LI><B>SELF_REFERENCING_COL_NAME</B> String {@code =>} name of the designated "identifier" |
| * column of a typed table (may be <code>null</code>) |
| * <LI><B>REF_GENERATION</B> String {@code =>} specifies how values in SELF_REFERENCING_COL_NAME |
| * are created. Values are "SYSTEM", "USER", "DERIVED". (may be <code>null</code>) |
| * </OL> |
| * |
| * <p><B>Note:</B> Some databases may not return information for all tables. |
| * |
| * @param catalog a catalog name; must match the catalog name as it is stored in the database; "" |
| * retrieves those without a catalog; <code>null</code> means that the catalog name should not |
| * be used to narrow the search |
| * @param schemaPattern a schema name pattern; must match the schema name as it is stored in the |
| * database; "" retrieves those without a schema; <code>null</code> means that the schema name |
| * should not be used to narrow the search |
| * @param tableNamePattern a table name pattern; must match the table name as it is stored in the |
| * database |
| * @param types a list of table types, which must be from the list of table types returned from |
| * {@link #getTableTypes},to include; <code>null</code> returns all types |
| * @return <code>ResultSet</code> - each row is a table description |
| * @throws SQLException if a database access error occurs |
| * @see #getSearchStringEscape |
| */ |
| public ResultSet getTables( |
| String catalog, String schemaPattern, String tableNamePattern, String[] types) |
| throws SQLException { |
| |
| StringBuilder sb = |
| new StringBuilder( |
| "SELECT TABLE_SCHEMA TABLE_CAT, NULL TABLE_SCHEM, TABLE_NAME, IF(TABLE_TYPE='BASE" |
| + " TABLE' or TABLE_TYPE='SYSTEM VERSIONED', 'TABLE', TABLE_TYPE) as TABLE_TYPE," |
| + " TABLE_COMMENT REMARKS, NULL TYPE_CAT, NULL TYPE_SCHEM, NULL TYPE_NAME, NULL" |
| + " SELF_REFERENCING_COL_NAME, NULL REF_GENERATION FROM" |
| + " INFORMATION_SCHEMA.TABLES"); |
| boolean firstCondition = true; |
| firstCondition = catalogCond(true, sb, "TABLE_SCHEMA", catalog); |
| firstCondition = patternCond(firstCondition, sb, "TABLE_NAME", tableNamePattern); |
| |
| if (types != null && types.length > 0) { |
| boolean mustAddType = false; |
| |
| StringBuilder sqlType = |
| new StringBuilder(((firstCondition) ? " WHERE " : " AND ") + " TABLE_TYPE IN ("); |
| for (String s : types) { |
| if (mustAddType) sqlType.append(","); |
| mustAddType = true; |
| if (s == null) { |
| mustAddType = false; |
| continue; |
| } |
| String type = "TABLE".equals(s) ? "'BASE TABLE','SYSTEM VERSIONED'" : escapeQuote(s); |
| sqlType.append(type); |
| } |
| sqlType.append(")"); |
| if (mustAddType) sb.append(sqlType); |
| } |
| |
| sb.append(" ORDER BY TABLE_TYPE, TABLE_SCHEMA, TABLE_NAME"); |
| |
| return executeQuery(sb.toString()); |
| } |
| |
| /** |
| * Retrieves a description of table columns available in the specified catalog. |
| * |
| * <p>Only column descriptions matching the catalog, schema, table and column name criteria are |
| * returned. They are ordered by <code>TABLE_CAT</code>,<code>TABLE_SCHEM</code>, <code>TABLE_NAME |
| * </code>, and <code>ORDINAL_POSITION</code>. |
| * |
| * <p>Each column description has the following columns: |
| * |
| * <OL> |
| * <LI><B>TABLE_CAT</B> String {@code =>} table catalog (may be <code>null</code>) |
| * <LI><B>TABLE_SCHEM</B> String {@code =>} table schema (may be <code>null</code>) |
| * <LI><B>TABLE_NAME</B> String {@code =>} table name |
| * <LI><B>COLUMN_NAME</B> String {@code =>} column name |
| * <LI><B>DATA_TYPE</B> int {@code =>} SQL type from java.sql.Types |
| * <LI><B>TYPE_NAME</B> String {@code =>} Data source dependent type name, for a UDT the type |
| * name is fully qualified |
| * <LI><B>COLUMN_SIZE</B> int {@code =>} column size. |
| * <LI><B>BUFFER_LENGTH</B> is not used. |
| * <LI><B>DECIMAL_DIGITS</B> int {@code =>} the number of fractional digits. Null is returned |
| * for data types where DECIMAL_DIGITS is not applicable. |
| * <LI><B>NUM_PREC_RADIX</B> int {@code =>} Radix (typically either 10 or 2) |
| * <LI><B>NULLABLE</B> int {@code =>} is NULL allowed. |
| * <UL> |
| * <LI>columnNoNulls - might not allow <code>NULL</code> values |
| * <LI>columnNullable - definitely allows <code>NULL</code> values |
| * <LI>columnNullableUnknown - nullability unknown |
| * </UL> |
| * <LI><B>REMARKS</B> String {@code =>} comment describing column (may be <code>null</code>) |
| * <LI><B>COLUMN_DEF</B> String {@code =>} default value for the column, which should be |
| * interpreted as a string when the value is enclosed in single quotes (may be <code>null |
| * </code>) |
| * <LI><B>SQL_DATA_TYPE</B> int {@code =>} unused |
| * <LI><B>SQL_DATETIME_SUB</B> int {@code =>} unused |
| * <LI><B>CHAR_OCTET_LENGTH</B> int {@code =>} for char types the maximum number of bytes in the |
| * column |
| * <LI><B>ORDINAL_POSITION</B> int {@code =>} index of column in table (starting at 1) |
| * <LI><B>IS_NULLABLE</B> String {@code =>} ISO rules are used to determine the nullability for |
| * a column. |
| * <UL> |
| * <LI>YES --- if the column can include NULLs |
| * <LI>NO --- if the column cannot include NULLs |
| * <LI>empty string --- if the nullability for the column is unknown |
| * </UL> |
| * <LI><B>SCOPE_CATALOG</B> String {@code =>} catalog of table that is the scope of a reference |
| * attribute (<code>null</code> if DATA_TYPE isn't REF) |
| * <LI><B>SCOPE_SCHEMA</B> String {@code =>} schema of table that is the scope of a reference |
| * attribute (<code>null</code> if the DATA_TYPE isn't REF) |
| * <LI><B>SCOPE_TABLE</B> String {@code =>} table name that this the scope of a reference |
| * attribute (<code>null</code> if the DATA_TYPE isn't REF) |
| * <LI><B>SOURCE_DATA_TYPE</B> short {@code =>} source type of a distinct type or user-generated |
| * Ref type, SQL type from java.sql.Types (<code>null</code> if DATA_TYPE isn't DISTINCT or |
| * user-generated REF) |
| * <LI><B>IS_AUTOINCREMENT</B> String {@code =>} Indicates whether this column is auto |
| * incremented |
| * <UL> |
| * <LI>YES --- if the column is auto incremented |
| * <LI>NO --- if the column is not auto incremented |
| * <LI>empty string --- if it cannot be determined whether the column is auto incremented |
| * </UL> |
| * <LI><B>IS_GENERATEDCOLUMN</B> String {@code =>} Indicates whether this is a generated column |
| * <UL> |
| * <LI>YES --- if this a generated column |
| * <LI>NO --- if this not a generated column |
| * <LI>empty string --- if it cannot be determined whether this is a generated column |
| * </UL> |
| * </OL> |
| * |
| * <p>The COLUMN_SIZE column specifies the column size for the given column. For numeric data, |
| * this is the maximum precision. For character data, this is the length in characters. For |
| * datetime datatypes, this is the length in characters of the String representation (assuming the |
| * maximum allowed precision of the fractional seconds component). For binary data, this is the |
| * length in bytes. For the ROWID datatype, this is the length in bytes. Null is returned for data |
| * types where the column size is not applicable. |
| * |
| * @param catalog a catalog name; must match the catalog name as it is stored in the database; "" |
| * retrieves those without a catalog; <code>null</code> means that the catalog name should not |
| * be used to narrow the search |
| * @param schemaPattern a schema name pattern; must match the schema name as it is stored in the |
| * database; "" retrieves those without a schema; <code>null</code> means that the schema name |
| * should not be used to narrow the search |
| * @param tableNamePattern a table name pattern; must match the table name as it is stored in the |
| * database |
| * @param columnNamePattern a column name pattern; must match the column name as it is stored in |
| * the database |
| * @return <code>ResultSet</code> - each row is a column description |
| * @throws SQLException if a database access error occurs |
| * @see #getSearchStringEscape |
| */ |
| public ResultSet getColumns( |
| String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern) |
| throws SQLException { |
| |
| ServerVersion version = connection.getContext().getVersion(); |
| boolean supportsFractionalSeconds = |
| version.isMariaDBServer() |
| /* "In MariaDB 5.3 and later, the TIME, DATETIME, and TIMESTAMP types, along with the temporal |
| functions, CAST and dynamic columns, now support microseconds." |
| https://web.archive.org/web/20130928042640/https://mariadb.com/kb/en/microseconds-in-mariadb/ |
| */ |
| ? version.versionGreaterOrEqual(5, 3, 0) |
| // See https://dev.mysql.com/doc/relnotes/mysql/5.6/en/news-5-6-4.html |
| : version.versionGreaterOrEqual(5, 6, 4); |
| StringBuilder sb = new StringBuilder(); |
| sb.append( |
| "SELECT TABLE_SCHEMA TABLE_CAT, NULL TABLE_SCHEM, TABLE_NAME, COLUMN_NAME," |
| + dataTypeClause("COLUMN_TYPE") |
| + " DATA_TYPE," |
| + DataTypeClause(conf) |
| + " TYPE_NAME, " |
| + " CASE DATA_TYPE" |
| + " WHEN 'date' THEN 10"); |
| if (supportsFractionalSeconds) { |
| sb.append( |
| " WHEN 'time' THEN " |
| + "IF(DATETIME_PRECISION = 0, 10, CAST(11 + DATETIME_PRECISION as signed integer))" |
| + " WHEN 'datetime' THEN " |
| + "IF(DATETIME_PRECISION = 0, 19, CAST(20 + DATETIME_PRECISION as signed integer))" |
| + " WHEN 'timestamp' THEN " |
| + "IF(DATETIME_PRECISION = 0, 19, CAST(20 + DATETIME_PRECISION as signed integer))"); |
| } else { |
| // Older versions do not include the DATETIME_PRECISION column in INFORMATION_SCHEMA.COLUMNS. |
| sb.append(" WHEN 'time' THEN 10 WHEN 'datetime' THEN 19 WHEN 'timestamp' THEN 19"); |
| } |
| sb.append( |
| (conf.yearIsDateType() ? "" : " WHEN 'year' THEN 5") |
| + " ELSE " |
| + " IF(NUMERIC_PRECISION IS NULL, LEAST(CHARACTER_MAXIMUM_LENGTH," |
| + Integer.MAX_VALUE |
| + "), NUMERIC_PRECISION) " |
| + " END" |
| + " COLUMN_SIZE, 65535 BUFFER_LENGTH, " |
| + " CONVERT (CASE DATA_TYPE" |
| + " WHEN 'year' THEN " |
| + (conf.yearIsDateType() ? "NUMERIC_SCALE" : "0") |
| + " WHEN 'tinyint' THEN " |
| + (conf.tinyInt1isBit() ? "0" : "NUMERIC_SCALE") |
| + " ELSE NUMERIC_SCALE END, UNSIGNED INTEGER) DECIMAL_DIGITS," |
| + " 10 NUM_PREC_RADIX, IF(IS_NULLABLE = 'yes',1,0) NULLABLE,COLUMN_COMMENT REMARKS," |
| + " COLUMN_DEFAULT COLUMN_DEF, 0 SQL_DATA_TYPE, 0 SQL_DATETIME_SUB, " |
| + " LEAST(CHARACTER_OCTET_LENGTH," |
| + Integer.MAX_VALUE |
| + ") CHAR_OCTET_LENGTH, ORDINAL_POSITION, IS_NULLABLE, NULL SCOPE_CATALOG, NULL" |
| + " SCOPE_SCHEMA, NULL SCOPE_TABLE, NULL SOURCE_DATA_TYPE, IF(EXTRA =" |
| + " 'auto_increment','YES','NO') IS_AUTOINCREMENT, IF(EXTRA in ('VIRTUAL'," |
| + " 'PERSISTENT', 'VIRTUAL GENERATED', 'STORED GENERATED') ,'YES','NO')" |
| + " IS_GENERATEDCOLUMN FROM INFORMATION_SCHEMA.COLUMNS"); |
| boolean firstCondition = catalogCond(true, sb, "TABLE_SCHEMA", catalog); |
| firstCondition = patternCond(firstCondition, sb, "TABLE_NAME", tableNamePattern); |
| firstCondition = patternCond(firstCondition, sb, "COLUMN_NAME", columnNamePattern); |
| sb.append(" ORDER BY TABLE_CAT, TABLE_SCHEM, TABLE_NAME, ORDINAL_POSITION"); |
| return executeQuery(sb.toString()); |
| } |
| |
| /** |
| * Retrieves a description of the foreign key columns that reference the given 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. |
| * |
| * <p>Each foreign key column description has the following columns: |
| * |
| * <OL> |
| * <LI><B>PKTABLE_CAT</B> String {@code =>} primary key table catalog (may be <code>null</code>) |
| * <LI><B>PKTABLE_SCHEM</B> String {@code =>} primary key table schema (may be <code>null</code> |
| * ) |
| * <LI><B>PKTABLE_NAME</B> String {@code =>} primary key table name |
| * <LI><B>PKCOLUMN_NAME</B> String {@code =>} primary key column name |
| * <LI><B>FKTABLE_CAT</B> String {@code =>} foreign key table catalog (may be <code>null</code>) |
| * being exported (may be <code>null</code>) |
| * <LI><B>FKTABLE_SCHEM</B> String {@code =>} foreign key table schema (may be <code>null</code> |
| * ) being exported (may be <code>null</code>) |
| * <LI><B>FKTABLE_NAME</B> String {@code =>} foreign key table name being exported |
| * <LI><B>FKCOLUMN_NAME</B> String {@code =>} foreign key column name being exported |
| * <LI><B>KEY_SEQ</B> short {@code =>} sequence number within foreign key( a value of 1 |
| * represents the first column of the foreign key, a value of 2 would represent the second |
| * column within the foreign key). |
| * <LI><B>UPDATE_RULE</B> short {@code =>} What happens to foreign key when primary is updated: |
| * <UL> |
| * <LI>importedNoAction - do not allow update of primary key if it has been imported |
| * <LI>importedKeyCascade - change imported key to agree with primary key update |
| * <LI>importedKeySetNull - change imported key to <code>NULL</code> if its primary key |
| * has been updated |
| * <LI>importedKeySetDefault - change imported key to default values if its primary key |
| * has been updated |
| * <LI>importedKeyRestrict - same as importedKeyNoAction (for ODBC 2.x compatibility) |
| * </UL> |
| * <LI><B>DELETE_RULE</B> short {@code =>} What happens to the foreign key when primary is |
| * deleted. |
| * <UL> |
| * <LI>importedKeyNoAction - do not allow delete of primary key if it has been imported |
| * <LI>importedKeyCascade - delete rows that import a deleted key |
| * <LI>importedKeySetNull - change imported key to <code>NULL</code> if its primary key |
| * has been deleted |
| * <LI>importedKeyRestrict - same as importedKeyNoAction (for ODBC 2.x compatibility) |
| * <LI>importedKeySetDefault - change imported key to default if its primary key has been |
| * deleted |
| * </UL> |
| * <LI><B>FK_NAME</B> String {@code =>} foreign key name (may be <code>null</code>) |
| * <LI><B>PK_NAME</B> String {@code =>} primary key name (may be <code>null</code>) |
| * <LI><B>DEFERRABILITY</B> short {@code =>} can the evaluation of foreign key constraints be |
| * deferred until commit |
| * <UL> |
| * <LI>importedKeyInitiallyDeferred - see SQL92 for definition |
| * <LI>importedKeyInitiallyImmediate - see SQL92 for definition |
| * <LI>importedKeyNotDeferrable - see SQL92 for definition |
| * </UL> |
| * </OL> |
| * |
| * @param catalog a catalog name; must match the catalog name as it is stored in this database; "" |
| * retrieves those without a catalog; <code>null</code> means that the catalog name should not |
| * be used to narrow the search |
| * @param schema a schema name; must match the schema name as it is stored in the database; "" |
| * retrieves those without a schema; <code>null</code> means that the schema name should not |
| * be used to narrow the search |
| * @param table a table name; must match the table name as it is stored in this database |
| * @return a <code>ResultSet</code> object in which each row is a foreign key column description |
| * @throws SQLException if a database access error occurs |
| * @see #getImportedKeys |
| */ |
| public ResultSet getExportedKeys(String catalog, String schema, String table) |
| throws SQLException { |
| StringBuilder sb = |
| new StringBuilder( |
| "SELECT KCU.REFERENCED_TABLE_SCHEMA PKTABLE_CAT, NULL PKTABLE_SCHEM, " |
| + " KCU.REFERENCED_TABLE_NAME PKTABLE_NAME, KCU.REFERENCED_COLUMN_NAME" |
| + " PKCOLUMN_NAME, KCU.TABLE_SCHEMA FKTABLE_CAT, NULL FKTABLE_SCHEM, " |
| + " KCU.TABLE_NAME FKTABLE_NAME, KCU.COLUMN_NAME FKCOLUMN_NAME," |
| + " KCU.POSITION_IN_UNIQUE_CONSTRAINT KEY_SEQ, CASE update_rule WHEN 'RESTRICT'" |
| + " THEN 1 WHEN 'NO ACTION' THEN 3 WHEN 'CASCADE' THEN 0 WHEN 'SET NULL' THEN" |
| + " 2 WHEN 'SET DEFAULT' THEN 4 END UPDATE_RULE, CASE DELETE_RULE WHEN" |
| + " 'RESTRICT' THEN 1 WHEN 'NO ACTION' THEN 3 WHEN 'CASCADE' THEN 0 WHEN 'SET" |
| + " NULL' THEN 2 WHEN 'SET DEFAULT' THEN 4 END DELETE_RULE, RC.CONSTRAINT_NAME" |
| + " FK_NAME, RC.UNIQUE_CONSTRAINT_NAME PK_NAME," |
| + importedKeyNotDeferrable |
| + " DEFERRABILITY" |
| + " FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU" |
| + " INNER JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC" |
| + " ON KCU.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA" |
| + " AND KCU.CONSTRAINT_NAME = RC.CONSTRAINT_NAME"); |
| boolean firstCondition = true; |
| firstCondition = catalogCond(firstCondition, sb, "KCU.REFERENCED_TABLE_SCHEMA", catalog); |
| firstCondition = patternCond(firstCondition, sb, "KCU.REFERENCED_TABLE_NAME", table); |
| sb.append(" ORDER BY FKTABLE_CAT, FKTABLE_SCHEM, FKTABLE_NAME, KEY_SEQ"); |
| |
| return executeQuery(sb.toString()); |
| } |
| |
| /** |
| * GetImportedKeysUsingInformationSchema. |
| * |
| * @param catalog catalog |
| * @param table table |
| * @return resultset |
| * @throws SQLException exception |
| */ |
| public ResultSet getImportedKeysUsingInformationSchema(final String catalog, String table) |
| throws SQLException { |
| if (table == null) { |
| throw new SQLException("'table' parameter in getImportedKeys cannot be null"); |
| } |
| StringBuilder sb = |
| new StringBuilder( |
| "SELECT KCU.REFERENCED_TABLE_SCHEMA PKTABLE_CAT, NULL PKTABLE_SCHEM, " |
| + " KCU.REFERENCED_TABLE_NAME PKTABLE_NAME, KCU.REFERENCED_COLUMN_NAME" |
| + " PKCOLUMN_NAME, KCU.TABLE_SCHEMA FKTABLE_CAT, NULL FKTABLE_SCHEM, " |
| + " KCU.TABLE_NAME FKTABLE_NAME, KCU.COLUMN_NAME FKCOLUMN_NAME," |
| + " KCU.POSITION_IN_UNIQUE_CONSTRAINT KEY_SEQ, CASE update_rule WHEN 'RESTRICT'" |
| + " THEN 1 WHEN 'NO ACTION' THEN 3 WHEN 'CASCADE' THEN 0 WHEN 'SET NULL' THEN" |
| + " 2 WHEN 'SET DEFAULT' THEN 4 END UPDATE_RULE, CASE DELETE_RULE WHEN" |
| + " 'RESTRICT' THEN 1 WHEN 'NO ACTION' THEN 3 WHEN 'CASCADE' THEN 0 WHEN 'SET" |
| + " NULL' THEN 2 WHEN 'SET DEFAULT' THEN 4 END DELETE_RULE, RC.CONSTRAINT_NAME" |
| + " FK_NAME, RC.UNIQUE_CONSTRAINT_NAME PK_NAME," |
| + importedKeyNotDeferrable |
| + " DEFERRABILITY" |
| + " FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU" |
| + " INNER JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC" |
| + " ON KCU.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA" |
| + " AND KCU.CONSTRAINT_NAME = RC.CONSTRAINT_NAME "); |
| |
| boolean firstCondition = catalogCond(true, sb, "KCU.TABLE_SCHEMA", catalog); |
| sb.append(firstCondition ? " WHERE " : " AND ") |
| .append("KCU.TABLE_NAME = ") |
| .append(escapeQuote(table)); |
| |
| sb.append(" ORDER BY PKTABLE_CAT, PKTABLE_SCHEM, PKTABLE_NAME, KEY_SEQ"); |
| |
| return executeQuery(sb.toString()); |
| } |
| |
| /** |
| * GetImportedKeysUsingShowCreateTable. |
| * |
| * @param catalog catalog |
| * @param table table |
| * @return resultset |
| * @throws Exception exception |
| */ |
| public ResultSet getImportedKeysUsingShowCreateTable(final String catalog, String table) |
| throws Exception { |
| ResultSet rs = |
| connection |
| .createStatement() |
| .executeQuery( |
| "SHOW CREATE TABLE " + quoteIdentifier(catalog) + "." + quoteIdentifier(table)); |
| rs.next(); |
| String tableDef = rs.getString(2); |
| return getImportedKeys(tableDef, table, catalog, connection); |
| } |
| |
| /** |
| * Retrieves a description of a table's optimal set of columns that uniquely identifies a row. |
| * They are ordered by SCOPE. |
| * |
| * <p>Each column description has the following columns: |
| * |
| * <OL> |
| * <LI><B>SCOPE</B> short {@code =>} actual scope of result |
| * <UL> |
| * <LI>bestRowTemporary - very temporary, while using row |
| * <LI>bestRowTransaction - valid for remainder of current transaction |
| * <LI>bestRowSession - valid for remainder of current session |
| * </UL> |
| * <LI><B>COLUMN_NAME</B> String {@code =>} column name |
| * <LI><B>DATA_TYPE</B> int {@code =>} SQL data type from java.sql.Types |
| * <LI><B>TYPE_NAME</B> String {@code =>} Data source dependent type name, for a UDT the type |
| * name is fully qualified |
| * <LI><B>COLUMN_SIZE</B> int {@code =>} precision |
| * <LI><B>BUFFER_LENGTH</B> int {@code =>} not used |
| * <LI><B>DECIMAL_DIGITS</B> short {@code =>} scale - Null is returned for data types where |
| * DECIMAL_DIGITS is not applicable. |
| * <LI><B>PSEUDO_COLUMN</B> short {@code =>} is this a pseudo column like an Oracle ROWID |
| * <UL> |
| * <LI>bestRowUnknown - may or may not be pseudo column |
| * <LI>bestRowNotPseudo - is NOT a pseudo column |
| * <LI>bestRowPseudo - is a pseudo column |
| * </UL> |
| * </OL> |
| * |
| * <p>The COLUMN_SIZE column represents the specified column size for the given column. For |
| * numeric data, this is the maximum precision. For character data, this is the length in |
| * characters. For datetime datatypes, this is the length in characters of the String |
| * representation (assuming the maximum allowed precision of the fractional seconds component). |
| * For binary data, this is the length in bytes. For the ROWID datatype, this is the length in |
| * bytes. Null is returned for data types where the column size is not applicable. |
| * |
| * @param catalog a catalog name; must match the catalog name as it is stored in the database; "" |
| * retrieves those without a catalog; <code>null</code> means that the catalog name should not |
| * be used to narrow the search |
| * @param schema a schema name; must match the schema name as it is stored in the database; "" |
| * retrieves those without a schema; <code>null</code> means that the schema name should not |
| * be used to narrow the search |
| * @param table a table name; must match the table name as it is stored in the database |
| * @param scope the scope of interest; use same values as SCOPE |
| * @param nullable include columns that are nullable. |
| * @return <code>ResultSet</code> - each row is a column description |
| * @throws SQLException if a database access error occurs |
| */ |
| public ResultSet getBestRowIdentifier( |
| String catalog, String schema, String table, int scope, final boolean nullable) |
| throws SQLException { |
| |
| if (table == null) { |
| throw new SQLException("'table' parameter cannot be null in getBestRowIdentifier()"); |
| } |
| boolean hasIsGeneratedCol = |
| (connection.getContext().getVersion().isMariaDBServer() |
| && connection.getContext().getVersion().versionGreaterOrEqual(10, 2, 0)); |
| |
| StringBuilder sbInner = |
| new StringBuilder("SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE COLUMN_KEY = 'PRI'"); |
| catalogCond(false, sbInner, "TABLE_SCHEMA", catalog); |
| sbInner.append(" AND TABLE_NAME = ").append(escapeQuote(table)); |
| |
| StringBuilder sb = |
| new StringBuilder( |
| "SELECT " |
| + bestRowSession |
| + " SCOPE, COLUMN_NAME," |
| + dataTypeClause("COLUMN_TYPE") |
| + " DATA_TYPE, DATA_TYPE TYPE_NAME, IF(NUMERIC_PRECISION IS NULL," |
| + " CHARACTER_MAXIMUM_LENGTH, NUMERIC_PRECISION) COLUMN_SIZE, 0 BUFFER_LENGTH," |
| + " NUMERIC_SCALE DECIMAL_DIGITS," |
| + (hasIsGeneratedCol |
| ? ("IF(IS_GENERATED='NEVER'," + bestRowNotPseudo + "," + bestRowPseudo + ")") |
| : bestRowNotPseudo) |
| + " PSEUDO_COLUMN" |
| + " FROM INFORMATION_SCHEMA.COLUMNS" |
| + " WHERE (COLUMN_KEY = 'PRI'" |
| + " OR (COLUMN_KEY = 'UNI' AND NOT EXISTS (" |
| + sbInner |
| + " )))"); |
| catalogCond(false, sb, "TABLE_SCHEMA", catalog); |
| sb.append(" AND TABLE_NAME = ").append(escapeQuote(table)); |
| if (!nullable) sb.append(" AND IS_NULLABLE = 'NO'"); |
| |
| return executeQuery(sb.toString()); |
| } |
| |
| public boolean generatedKeyAlwaysReturned() { |
| return true; |
| } |
| |
| /** |
| * Retrieves a description of the pseudo or hidden columns available in a given table within the |
| * specified catalog and schema. Pseudo or hidden columns may not always be stored within a table |
| * and are not visible in a ResultSet unless they are specified in the query's outermost SELECT |
| * list. Pseudo or hidden columns may not necessarily be able to be modified. If there are no |
| * pseudo or hidden columns, an empty ResultSet is returned. |
| * |
| * <p>Only column descriptions matching the catalog, schema, table and column name criteria are |
| * returned. They are ordered by <code>TABLE_CAT</code>,<code>TABLE_SCHEM</code>, <code>TABLE_NAME |
| * </code> and <code>COLUMN_NAME</code>. |
| * |
| * <p>Each column description has the following columns: |
| * |
| * <OL> |
| * <LI><B>TABLE_CAT</B> String {@code =>} table catalog (may be <code>null</code>) |
| * <LI><B>TABLE_SCHEM</B> String {@code =>} table schema (may be <code>null</code>) |
| * <LI><B>TABLE_NAME</B> String {@code =>} table name |
| * <LI><B>COLUMN_NAME</B> String {@code =>} column name |
| * <LI><B>DATA_TYPE</B> int {@code =>} SQL type from java.sql.Types |
| * <LI><B>COLUMN_SIZE</B> int {@code =>} column size. |
| * <LI><B>DECIMAL_DIGITS</B> int {@code =>} the number of fractional digits. Null is returned |
| * for data types where DECIMAL_DIGITS is not applicable. |
| * <LI><B>NUM_PREC_RADIX</B> int {@code =>} Radix (typically either 10 or 2) |
| * <LI><B>COLUMN_USAGE</B> String {@code =>} The allowed usage for the column. The value |
| * returned will correspond to the enum name returned by PseudoColumnUsage.name() |
| * <LI><B>REMARKS</B> String {@code =>} comment describing column (may be <code>null</code>) |
| * <LI><B>CHAR_OCTET_LENGTH</B> int {@code =>} for char types the maximum number of bytes in the |
| * column |
| * <LI><B>IS_NULLABLE</B> String {@code =>} ISO rules are used to determine the nullability for |
| * a column. |
| * <UL> |
| * <LI>YES --- if the column can include NULLs |
| * <LI>NO --- if the column cannot include NULLs |
| * <LI>empty string --- if the nullability for the column is unknown |
| * </UL> |
| * </OL> |
| * |
| * <p>The COLUMN_SIZE column specifies the column size for the given column. For numeric data, |
| * this is the maximum precision. For character data, this is the length in characters. For |
| * datetime datatypes, this is the length in characters of the String representation (assuming the |
| * maximum allowed precision of the fractional seconds component). For binary data, this is the |
| * length in bytes. For the ROWID datatype, this is the length in bytes. Null is returned for data |
| * types where the column size is not applicable. |
| * |
| * @param catalog a catalog name; must match the catalog name as it is stored in the database; "" |
| * retrieves those without a catalog; <code>null</code> means that the catalog name should not |
| * be used to narrow the search |
| * @param schemaPattern a schema name pattern; must match the schema name as it is stored in the |
| * database; "" retrieves those without a schema; <code>null</code> means that the schema name |
| * should not be used to narrow the search |
| * @param tableNamePattern a table name pattern; must match the table name as it is stored in the |
| * database |
| * @param columnNamePattern a column name pattern; must match the column name as it is stored in |
| * the database |
| * @return <code>ResultSet</code> - each row is a column description |
| * @throws SQLException if a database access error occurs |
| * @see PseudoColumnUsage |
| * @since 1.7 |
| */ |
| public ResultSet getPseudoColumns( |
| String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern) |
| throws SQLException { |
| return connection |
| .createStatement() |
| .executeQuery( |
| "SELECT ' ' TABLE_CAT, ' ' TABLE_SCHEM, ' ' TABLE_NAME, ' ' COLUMN_NAME, 0 DATA_TYPE, 0" |
| + " COLUMN_SIZE, 0 DECIMAL_DIGITS, 10 NUM_PREC_RADIX, ' ' COLUMN_USAGE, ' '" |
| + " REMARKS, 0 CHAR_OCTET_LENGTH, 'YES' IS_NULLABLE FROM DUAL WHERE 1=0"); |
| } |
| |
| public boolean allProceduresAreCallable() { |
| return true; |
| } |
| |
| public boolean allTablesAreSelectable() { |
| return true; |
| } |
| |
| @Override |
| public String getURL() { |
| return conf.initialUrl(); |
| } |
| |
| public String getUserName() { |
| return conf.user(); |
| } |
| |
| public boolean isReadOnly() throws SQLException { |
| java.sql.Statement st = connection.createStatement(); |
| ResultSet rs = st.executeQuery("SELECT @@READ_ONLY"); |
| rs.next(); |
| return rs.getInt(1) == 1; |
| } |
| |
| public boolean nullsAreSortedHigh() { |
| return false; |
| } |
| |
| public boolean nullsAreSortedLow() { |
| return true; |
| } |
| |
| public boolean nullsAreSortedAtStart() { |
| return false; |
| } |
| |
| public boolean nullsAreSortedAtEnd() { |
| return true; |
| } |
| |
| /** |
| * Return Server type. MySQL or MariaDB. MySQL can be forced for compatibility with option |
| * "useMysqlMetadata" |
| * |
| * @return server type |
| */ |
| public String getDatabaseProductName() { |
| if (!conf.useMysqlMetadata() && connection.getContext().getVersion().isMariaDBServer()) { |
| return "MariaDB"; |
| } |
| return "MySQL"; |
| } |
| |
| public String getDatabaseProductVersion() { |
| return connection.getContext().getVersion().getVersion(); |
| } |
| |
| public String getDriverName() { |
| return DRIVER_NAME; |
| } |
| |
| public String getDriverVersion() { |
| return VersionFactory.getInstance().getVersion(); |
| } |
| |
| public int getDriverMajorVersion() { |
| return VersionFactory.getInstance().getMajorVersion(); |
| } |
| |
| public int getDriverMinorVersion() { |
| return VersionFactory.getInstance().getMinorVersion(); |
| } |
| |
| public boolean usesLocalFiles() { |
| return false; |
| } |
| |
| public boolean usesLocalFilePerTable() { |
| return false; |
| } |
| |
| public boolean supportsMixedCaseIdentifiers() throws SQLException { |
| return (connection.getLowercaseTableNames() == 0); |
| } |
| |
| public boolean storesUpperCaseIdentifiers() { |
| return false; |
| } |
| |
| public boolean storesLowerCaseIdentifiers() throws SQLException { |
| return (connection.getLowercaseTableNames() == 1); |
| } |
| |
| public boolean storesMixedCaseIdentifiers() throws SQLException { |
| return (connection.getLowercaseTableNames() == 2); |
| } |
| |
| public boolean supportsMixedCaseQuotedIdentifiers() throws SQLException { |
| return supportsMixedCaseIdentifiers(); |
| } |
| |
| public boolean storesUpperCaseQuotedIdentifiers() { |
| return storesUpperCaseIdentifiers(); |
| } |
| |
| public boolean storesLowerCaseQuotedIdentifiers() throws SQLException { |
| return storesLowerCaseIdentifiers(); |
| } |
| |
| public boolean storesMixedCaseQuotedIdentifiers() throws SQLException { |
| return storesMixedCaseIdentifiers(); |
| } |
| |
| public String getIdentifierQuoteString() { |
| return "`"; |
| } |
| |
| /** |
| * Retrieves a comma-separated list of all of this database's SQL keywords that are NOT also |
| * SQL:2003 keywords. |
| * |
| * @return the list of this database's keywords that are not also SQL:2003 keywords |
| */ |
| @Override |
| public String getSQLKeywords() { |
| return "ACCESSIBLE,ANALYZE,ASENSITIVE,BEFORE,BIGINT,BINARY,BLOB,CALL,CHANGE,CONDITION,DATABASE,DATABASES," |
| + "DAY_HOUR,DAY_MICROSECOND,DAY_MINUTE,DAY_SECOND,DELAYED,DETERMINISTIC,DISTINCTROW,DIV,DUAL,EACH," |
| + "ELSEIF,ENCLOSED,ESCAPED,EXIT,EXPLAIN,FLOAT4,FLOAT8,FORCE,FULLTEXT,GENERAL,HIGH_PRIORITY," |
| + "HOUR_MICROSECOND,HOUR_MINUTE,HOUR_SECOND,IF,IGNORE,IGNORE_SERVER_IDS,INDEX,INFILE,INOUT,INT1,INT2," |
| + "INT3,INT4,INT8,ITERATE,KEY,KEYS,KILL,LEAVE,LIMIT,LINEAR,LINES,LOAD,LOCALTIME,LOCALTIMESTAMP,LOCK," |
| + "LONG,LONGBLOB,LONGTEXT,LOOP,LOW_PRIORITY,MASTER_HEARTBEAT_PERIOD,MASTER_SSL_VERIFY_SERVER_CERT," |
| + "MAXVALUE,MEDIUMBLOB,MEDIUMINT,MEDIUMTEXT,MIDDLEINT,MINUTE_MICROSECOND,MINUTE_SECOND,MOD,MODIFIES," |
| + "NO_WRITE_TO_BINLOG,OPTIMIZE,OPTIONALLY,OUT,OUTFILE,PURGE,RANGE,READ_WRITE,READS,REGEXP,RELEASE," |
| + "RENAME,REPEAT,REPLACE,REQUIRE,RESIGNAL,RESTRICT,RETURN,RLIKE,SCHEMAS,SECOND_MICROSECOND,SENSITIVE," |
| + "SEPARATOR,SHOW,SIGNAL,SLOW,SPATIAL,SPECIFIC,SQL_BIG_RESULT,SQL_CALC_FOUND_ROWS,SQL_SMALL_RESULT," |
| + "SQLEXCEPTION,SSL,STARTING,STRAIGHT_JOIN,TERMINATED,TINYBLOB,TINYINT,TINYTEXT,TRIGGER,UNDO,UNLOCK," |
| + "UNSIGNED,USE,UTC_DATE,UTC_TIME,UTC_TIMESTAMP,VARBINARY,VARCHARACTER,WHILE,XOR,YEAR_MONTH,ZEROFILL"; |
| } |
| |
| /** |
| * List of numeric functions. |
| * |
| * @return List of numeric functions. |
| */ |
| @Override |
| public String getNumericFunctions() { |
| return "DIV,ABS,ACOS,ASIN,ATAN,ATAN2,CEIL,CEILING,CONV,COS,COT,CRC32,DEGREES,EXP,FLOOR,GREATEST,LEAST,LN,LOG," |
| + "LOG10,LOG2,MOD,OCT,PI,POW,POWER,RADIANS,RAND,ROUND,SIGN,SIN,SQRT,TAN,TRUNCATE"; |
| } |
| |
| /** |
| * List of string functions. |
| * |
| * @return List of string functions. |
| */ |
| @Override |
| public String getStringFunctions() { |
| return "ASCII,BIN,BIT_LENGTH,CAST,CHARACTER_LENGTH,CHAR_LENGTH,CONCAT,CONCAT_WS,CONVERT,ELT,EXPORT_SET," |
| + "EXTRACTVALUE,FIELD,FIND_IN_SET,FORMAT,FROM_BASE64,HEX,INSTR,LCASE,LEFT,LENGTH,LIKE,LOAD_FILE,LOCATE,LOWER,LPAD,LTRIM,MAKE_SET,MATCH" |
| + " AGAINST,MID,NOT LIKE,NOT" |
| + " REGEXP,OCTET_LENGTH,ORD,POSITION,QUOTE,REPEAT,REPLACE,REVERSE,RIGHT,RPAD,RTRIM,SOUNDEX,SOUNDS" |
| + " LIKE,SPACE,STRCMP,SUBSTR,SUBSTRING," |
| + "SUBSTRING_INDEX,TO_BASE64,TRIM,UCASE,UNHEX,UPDATEXML,UPPER,WEIGHT_STRING"; |
| } |
| |
| /** |
| * List of system functions. |
| * |
| * @return List of system functions. |
| */ |
| @Override |
| public String getSystemFunctions() { |
| return "DATABASE,USER,SYSTEM_USER,SESSION_USER,LAST_INSERT_ID,VERSION"; |
| } |
| |
| /** |
| * List of time and date functions. |
| * |
| * @return List of time and date functions. |
| */ |
| @Override |
| public String getTimeDateFunctions() { |
| return "ADDDATE,ADDTIME,CONVERT_TZ,CURDATE,CURRENT_DATE,CURRENT_TIME,CURRENT_TIMESTAMP,CURTIME,DATEDIFF," |
| + "DATE_ADD,DATE_FORMAT,DATE_SUB,DAY,DAYNAME,DAYOFMONTH,DAYOFWEEK,DAYOFYEAR,EXTRACT,FROM_DAYS," |
| + "FROM_UNIXTIME,GET_FORMAT,HOUR,LAST_DAY,LOCALTIME,LOCALTIMESTAMP,MAKEDATE,MAKETIME,MICROSECOND," |
| + "MINUTE,MONTH,MONTHNAME,NOW,PERIOD_ADD,PERIOD_DIFF,QUARTER,SECOND,SEC_TO_TIME,STR_TO_DATE,SUBDATE," |
| + "SUBTIME,SYSDATE,TIMEDIFF,TIMESTAMPADD,TIMESTAMPDIFF,TIME_FORMAT,TIME_TO_SEC,TO_DAYS,TO_SECONDS," |
| + "UNIX_TIMESTAMP,UTC_DATE,UTC_TIME,UTC_TIMESTAMP,WEEK,WEEKDAY,WEEKOFYEAR,YEAR,YEARWEEK"; |
| } |
| |
| public String getSearchStringEscape() { |
| return "\\"; |
| } |
| |
| public String getExtraNameCharacters() { |
| return "#@"; |
| } |
| |
| public boolean supportsAlterTableWithAddColumn() { |
| return true; |
| } |
| |
| public boolean supportsAlterTableWithDropColumn() { |
| return true; |
| } |
| |
| public boolean supportsColumnAliasing() { |
| return true; |
| } |
| |
| public boolean nullPlusNonNullIsNull() { |
| return true; |
| } |
| |
| public boolean supportsConvert() { |
| return true; |
| } |
| |
| /** |
| * Retrieves whether this database supports the JDBC scalar function CONVERT for conversions |
| * between the JDBC types fromType and toType. The JDBC types are the generic SQL data types |
| * defined in java.sql.Types. |
| * |
| * @param fromType the type to convert from; one of the type codes from the class java.sql.Types |
| * @param toType the type to convert to; one of the type codes from the class java.sql.Types |
| * @return true if so; false otherwise |
| */ |
| public boolean supportsConvert(int fromType, int toType) { |
| switch (fromType) { |
| case Types.TINYINT: |
| case Types.SMALLINT: |
| case Types.INTEGER: |
| case Types.BIGINT: |
| case Types.REAL: |
| case Types.FLOAT: |
| case Types.DECIMAL: |
| case Types.NUMERIC: |
| case Types.DOUBLE: |
| case Types.BIT: |
| case Types.BOOLEAN: |
| switch (toType) { |
| case Types.TINYINT: |
| case Types.SMALLINT: |
| case Types.INTEGER: |
| case Types.BIGINT: |
| case Types.REAL: |
| case Types.FLOAT: |
| case Types.DECIMAL: |
| case Types.NUMERIC: |
| case Types.DOUBLE: |
| case Types.BIT: |
| case Types.BOOLEAN: |
| case Types.CHAR: |
| case Types.VARCHAR: |
| case Types.LONGVARCHAR: |
| case Types.BINARY: |
| case Types.VARBINARY: |
| case Types.LONGVARBINARY: |
| return true; |
| default: |
| return false; |
| } |
| |
| case Types.BLOB: |
| switch (toType) { |
| case Types.BINARY: |
| case Types.VARBINARY: |
| case Types.LONGVARBINARY: |
| case Types.CHAR: |
| case Types.VARCHAR: |
| case Types.LONGVARCHAR: |
| case Types.TINYINT: |
| case Types.SMALLINT: |
| case Types.INTEGER: |
| case Types.BIGINT: |
| case Types.REAL: |
| case Types.FLOAT: |
| case Types.DECIMAL: |
| case Types.NUMERIC: |
| case Types.DOUBLE: |
| case Types.BIT: |
| case Types.BOOLEAN: |
| return true; |
| default: |
| return false; |
| } |
| |
| case Types.CHAR: |
| case Types.CLOB: |
| case Types.VARCHAR: |
| case Types.LONGVARCHAR: |
| case Types.BINARY: |
| case Types.VARBINARY: |
| case Types.LONGVARBINARY: |
| switch (toType) { |
| case Types.BIT: |
| case Types.TINYINT: |
| case Types.SMALLINT: |
| case Types.INTEGER: |
| case Types.BIGINT: |
| case Types.FLOAT: |
| case Types.REAL: |
| case Types.DOUBLE: |
| case Types.NUMERIC: |
| case Types.DECIMAL: |
| case Types.CHAR: |
| case Types.VARCHAR: |
| case Types.LONGVARCHAR: |
| case Types.BINARY: |
| case Types.VARBINARY: |
| case Types.LONGVARBINARY: |
| case Types.DATE: |
| case Types.TIME: |
| case Types.TIMESTAMP: |
| case Types.BLOB: |
| case Types.CLOB: |
| case Types.BOOLEAN: |
| case Types.NCHAR: |
| case Types.LONGNVARCHAR: |
| case Types.NCLOB: |
| return true; |
| default: |
| return false; |
| } |
| |
| case Types.DATE: |
| switch (toType) { |
| case Types.DATE: |
| case Types.CHAR: |
| case Types.VARCHAR: |
| case Types.LONGVARCHAR: |
| case Types.BINARY: |
| case Types.VARBINARY: |
| case Types.LONGVARBINARY: |
| return true; |
| |
| default: |
| return false; |
| } |
| |
| case Types.TIME: |
| switch (toType) { |
| case Types.TIME: |
| case Types.CHAR: |
| case Types.VARCHAR: |
| case Types.LONGVARCHAR: |
| case Types.BINARY: |
| case Types.VARBINARY: |
| case Types.LONGVARBINARY: |
| return true; |
| default: |
| return false; |
| } |
| |
| case Types.TIMESTAMP: |
| switch (toType) { |
| case Types.TIMESTAMP: |
| case Types.CHAR: |
| case Types.VARCHAR: |
| case Types.LONGVARCHAR: |
| case Types.BINARY: |
| case Types.VARBINARY: |
| case Types.LONGVARBINARY: |
| case Types.TIME: |
| case Types.DATE: |
| return true; |
| default: |
| return false; |
| } |
| default: |
| return false; |
| } |
| } |
| |
| public boolean supportsTableCorrelationNames() { |
| return true; |
| } |
| |
| public boolean supportsDifferentTableCorrelationNames() { |
| return true; |
| } |
| |
| public boolean supportsExpressionsInOrderBy() { |
| return true; |
| } |
| |
| public boolean supportsOrderByUnrelated() { |
| return true; |
| } |
| |
| public boolean supportsGroupBy() { |
| return true; |
| } |
| |
| public boolean supportsGroupByUnrelated() { |
| return true; |
| } |
| |
| public boolean supportsGroupByBeyondSelect() { |
| return true; |
| } |
| |
| public boolean supportsLikeEscapeClause() { |
| return true; |
| } |
| |
| public boolean supportsMultipleResultSets() { |
| return true; |
| } |
| |
| public boolean supportsMultipleTransactions() { |
| return true; |
| } |
| |
| public boolean supportsNonNullableColumns() { |
| return true; |
| } |
| |
| @Override |
| public boolean supportsMinimumSQLGrammar() { |
| return true; |
| } |
| |
| @Override |
| public boolean supportsCoreSQLGrammar() { |
| return true; |
| } |
| |
| @Override |
| public boolean supportsExtendedSQLGrammar() { |
| return true; |
| } |
| |
| @Override |
| public boolean supportsANSI92EntryLevelSQL() { |
| return true; |
| } |
| |
| @Override |
| public boolean supportsANSI92IntermediateSQL() { |
| return true; |
| } |
| |
| @Override |
| public boolean supportsANSI92FullSQL() { |
| return true; |
| } |
| |
| public boolean supportsIntegrityEnhancementFacility() { |
| return true; |
| } |
| |
| public boolean supportsOuterJoins() { |
| return true; |
| } |
| |
| public boolean supportsFullOuterJoins() { |
| return true; |
| } |
| |
| public boolean supportsLimitedOuterJoins() { |
| return true; |
| } |
| |
| public String getSchemaTerm() { |
| return "schema"; |
| } |
| |
| public String getProcedureTerm() { |
| return "procedure"; |
| } |
| |
| public String getCatalogTerm() { |
| return "database"; |
| } |
| |
| public boolean isCatalogAtStart() { |
| return true; |
| } |
| |
| public String getCatalogSeparator() { |
| return "."; |
| } |
| |
| public boolean supportsSchemasInDataManipulation() { |
| return false; |
| } |
| |
| public boolean supportsSchemasInProcedureCalls() { |
| return false; |
| } |
| |
| public boolean supportsSchemasInTableDefinitions() { |
| return false; |
| } |
| |
| public boolean supportsSchemasInIndexDefinitions() { |
| return false; |
| } |
| |
| public boolean supportsSchemasInPrivilegeDefinitions() { |
| return false; |
| } |
| |
| public boolean supportsCatalogsInDataManipulation() { |
| return true; |
| } |
| |
| public boolean supportsCatalogsInProcedureCalls() { |
| return true; |
| } |
| |
| public boolean supportsCatalogsInTableDefinitions() { |
| return true; |
| } |
| |
| public boolean supportsCatalogsInIndexDefinitions() { |
| return true; |
| } |
| |
| public boolean supportsCatalogsInPrivilegeDefinitions() { |
| return true; |
| } |
| |
| public boolean supportsPositionedDelete() { |
| return false; |
| } |
| |
| public boolean supportsPositionedUpdate() { |
| return false; |
| } |
| |
| public boolean supportsSelectForUpdate() { |
| return true; |
| } |
| |
| public boolean supportsStoredProcedures() { |
| return true; |
| } |
| |
| public boolean supportsSubqueriesInComparisons() { |
| return true; |
| } |
| |
| public boolean supportsSubqueriesInExists() { |
| return true; |
| } |
| |
| public boolean supportsSubqueriesInIns() { |
| return true; |
| } |
| |
| public boolean supportsSubqueriesInQuantifieds() { |
| return true; |
| } |
| |
| public boolean supportsCorrelatedSubqueries() { |
| return true; |
| } |
| |
| public boolean supportsUnion() { |
| return true; |
| } |
| |
| public boolean supportsUnionAll() { |
| return true; |
| } |
| |
| public boolean supportsOpenCursorsAcrossCommit() { |
| return true; |
| } |
| |
| public boolean supportsOpenCursorsAcrossRollback() { |
| return true; |
| } |
| |
| public boolean supportsOpenStatementsAcrossCommit() { |
| return true; |
| } |
| |
| public boolean supportsOpenStatementsAcrossRollback() { |
| return true; |
| } |
| |
| public int getMaxBinaryLiteralLength() { |
| return Integer.MAX_VALUE; |
| } |
| |
| public int getMaxCharLiteralLength() { |
| return Integer.MAX_VALUE; |
| } |
| |
| public int getMaxColumnNameLength() { |
| return 64; |
| } |
| |
| public int getMaxColumnsInGroupBy() { |
| return 64; |
| } |
| |
| public int getMaxColumnsInIndex() { |
| return 16; |
| } |
| |
| public int getMaxColumnsInOrderBy() { |
| return 64; |
| } |
| |
| public int getMaxColumnsInSelect() { |
| return Short.MAX_VALUE; |
| } |
| |
| public int getMaxColumnsInTable() { |
| return 0; |
| } |
| |
| public int getMaxConnections() { |
| return 0; |
| } |
| |
| public int getMaxCursorNameLength() { |
| return 0; |
| } |
| |
| public int getMaxIndexLength() { |
| return 256; |
| } |
| |
| public int getMaxSchemaNameLength() { |
| return 0; |
| } |
| |
| public int getMaxProcedureNameLength() { |
| return 64; |
| } |
| |
| public int getMaxCatalogNameLength() { |
| return 0; |
| } |
| |
| public int getMaxRowSize() { |
| return 0; |
| } |
| |
| public boolean doesMaxRowSizeIncludeBlobs() { |
| return false; |
| } |
| |
| public int getMaxStatementLength() { |
| return 0; |
| } |
| |
| public int getMaxStatements() { |
| return 0; |
| } |
| |
| public int getMaxTableNameLength() { |
| return 64; |
| } |
| |
| public int getMaxTablesInSelect() { |
| return 256; |
| } |
| |
| public int getMaxUserNameLength() { |
| return 0; |
| } |
| |
| public int getDefaultTransactionIsolation() { |
| return java.sql.Connection.TRANSACTION_REPEATABLE_READ; |
| } |
| |
| /** |
| * Retrieves whether this database supports transactions. If not, invoking the method <code>commit |
| * </code> is a noop, and the isolation level is <code>TRANSACTION_NONE</code>. |
| * |
| * @return <code>true</code> if transactions are supported; <code>false</code> otherwise |
| */ |
| public boolean supportsTransactions() { |
| return true; |
| } |
| |
| /* Helper to generate information schema with "equality" condition (typically on catalog name) |
| */ |
| |
| /** |
| * Retrieves whether this database supports the given transaction isolation level. |
| * |
| * @param level one of the transaction isolation levels defined in <code>java.sql.Connection |
| * </code> |
| * @return <code>true</code> if so; <code>false</code> otherwise |
| * @see java.sql.Connection |
| */ |
| public boolean supportsTransactionIsolationLevel(int level) { |
| switch (level) { |
| case java.sql.Connection.TRANSACTION_READ_UNCOMMITTED: |
| case java.sql.Connection.TRANSACTION_READ_COMMITTED: |
| case java.sql.Connection.TRANSACTION_REPEATABLE_READ: |
| case java.sql.Connection.TRANSACTION_SERIALIZABLE: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| public boolean supportsDataDefinitionAndDataManipulationTransactions() { |
| return true; |
| } |
| |
| public boolean supportsDataManipulationTransactionsOnly() { |
| return false; |
| } |
| |
| public boolean dataDefinitionCausesTransactionCommit() { |
| return true; |
| } |
| |
| public boolean dataDefinitionIgnoredInTransactions() { |
| return false; |
| } |
| |
| /** |
| * Retrieves a description of the stored procedures available in the given catalog. Only procedure |
| * descriptions matching the schema and procedure name criteria are returned. They are ordered by |
| * <code>PROCEDURE_CAT</code>, <code>PROCEDURE_SCHEM</code>, <code>PROCEDURE_NAME</code> and |
| * <code>SPECIFIC_ NAME</code>. |
| * |
| * <p>Each procedure description has the following columns: |
| * |
| * <OL> |
| * <LI><B>PROCEDURE_CAT</B> String {@code =>} procedure catalog (may be <code>null</code>) |
| * <LI><B>PROCEDURE_SCHEM</B> String {@code =>} procedure schema (may be <code>null</code>) |
| * <LI><B>PROCEDURE_NAME</B> String {@code =>} procedure name |
| * <LI>reserved for future use |
| * <LI>reserved for future use |
| * <LI>reserved for future use |
| * <LI><B>REMARKS</B> String {@code =>} explanatory comment on the procedure |
| * <LI><B>PROCEDURE_TYPE</B> short {@code =>} kind of procedure: |
| * <UL> |
| * <LI>procedureResultUnknown - Cannot determine if a return value will be returned |
| * <LI>procedureNoResult - Does not return a return value |
| * <LI>procedureReturnsResult - Returns a return value |
| * </UL> |
| * <LI><B>SPECIFIC_NAME</B> String {@code =>} The name which uniquely identifies this procedure |
| * within its schema. |
| * </OL> |
| * |
| * <p>A user may not have permissions to execute any of the procedures that are returned by <code> |
| * getProcedures</code> |
| * |
| * @param catalog a catalog name; must match the catalog name as it is stored in the database; "" |
| * retrieves those without a catalog; <code>null</code> means that the catalog name should not |
| * be used to narrow the search |
| * @param schemaPattern a schema name pattern; must match the schema name as it is stored in the |
| * database; "" retrieves those without a schema; <code>null</code> means that the schema name |
| * should not be used to narrow the search |
| * @param procedureNamePattern a procedure name pattern; must match the procedure name as it is |
| * stored in the database |
| * @return <code>ResultSet</code> - 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 { |
| |
| StringBuilder sb = |
| new StringBuilder( |
| "SELECT ROUTINE_SCHEMA PROCEDURE_CAT," |
| + "NULL PROCEDURE_SCHEM, " |
| + "ROUTINE_NAME PROCEDURE_NAME," |
| + " NULL RESERVED1," |
| + " NULL RESERVED2," |
| + " NULL RESERVED3," |
| + " ROUTINE_COMMENT REMARKS," |
| + " CASE ROUTINE_TYPE " |
| + " WHEN 'FUNCTION' THEN " |
| + procedureReturnsResult |
| + " WHEN 'PROCEDURE' THEN " |
| + procedureNoResult |
| + " ELSE " |
| + procedureResultUnknown |
| + " END PROCEDURE_TYPE," |
| + " SPECIFIC_NAME " |
| + " FROM INFORMATION_SCHEMA.ROUTINES "); |
| |
| boolean firstCondition = catalogCond(true, sb, "ROUTINE_SCHEMA", catalog); |
| firstCondition = patternCond(firstCondition, sb, "ROUTINE_NAME", procedureNamePattern); |
| |
| return executeQuery(sb.toString()); |
| } |
| |
| /** |
| * Retrieves a description of the given catalog's stored procedure parameter and result columns. |
| * |
| * <p>Only descriptions matching the schema, procedure and parameter name criteria are returned. |
| * They are ordered by PROCEDURE_CAT, PROCEDURE_SCHEM, PROCEDURE_NAME and SPECIFIC_NAME. Within |
| * this, the return value, if any, is first. Next are the parameter descriptions in call order. |
| * The column descriptions follow in column number order. |
| * |
| * <p>Each row in the <code>ResultSet</code> is a parameter description or column description with |
| * the following fields: |
| * |
| * <OL> |
| * <LI><B>PROCEDURE_CAT</B> String {@code =>} procedure catalog (may be <code>null</code>) |
| * <LI><B>PROCEDURE_SCHEM</B> String {@code =>} procedure schema (may be <code>null</code>) |
| * <LI><B>PROCEDURE_NAME</B> String {@code =>} procedure name |
| * <LI><B>COLUMN_NAME</B> String {@code =>} column/parameter name |
| * <LI><B>COLUMN_TYPE</B> Short {@code =>} kind of column/parameter: |
| * <UL> |
| * <LI>procedureColumnUnknown - nobody knows |
| * <LI>procedureColumnIn - IN parameter |
| * <LI>procedureColumnInOut - INOUT parameter |
| * <LI>procedureColumnOut - OUT parameter |
| * <LI>procedureColumnReturn - procedure return value |
| * <LI>procedureColumnResult - result column in <code>ResultSet</code> |
| * </UL> |
| * <LI><B>DATA_TYPE</B> int {@code =>} SQL type from java.sql.Types |
| * <LI><B>TYPE_NAME</B> String {@code =>} SQL type name, for a UDT type the type name is fully |
| * qualified |
| * <LI><B>PRECISION</B> int {@code =>} precision |
| * <LI><B>LENGTH</B> int {@code =>} length in bytes of data |
| * <LI><B>SCALE</B> short {@code =>} scale - null is returned for data types where SCALE is not |
| * applicable. |
| * <LI><B>RADIX</B> short {@code =>} radix |
| * <LI><B>NULLABLE</B> short {@code =>} can it contain NULL. |
| * <UL> |
| * <LI>procedureNoNulls - does not allow NULL values |
| * <LI>procedureNullable - allows NULL values |
| * <LI>procedureNullableUnknown - nullability unknown |
| * </UL> |
| * <LI><B>REMARKS</B> String {@code =>} comment describing parameter/column |
| * <LI><B>COLUMN_DEF</B> String {@code =>} default value for the column, which should be |
| * interpreted as a string when the value is enclosed in single quotes (may be <code>null |
| * </code>) |
| * <UL> |
| * <LI>The string NULL (not enclosed in quotes) - if NULL was specified as the default |
| * value |
| * <LI>TRUNCATE (not enclosed in quotes) - if the specified default value cannot be |
| * represented without truncation |
| * <LI>NULL - if a default value was not specified |
| * </UL> |
| * <LI><B>SQL_DATA_TYPE</B> int {@code =>} reserved for future use |
| * <LI><B>SQL_DATETIME_SUB</B> int {@code =>} reserved for future use |
| * <LI><B>CHAR_OCTET_LENGTH</B> int {@code =>} the maximum length of binary and character based |
| * columns. For any other datatype the returned value is a NULL |
| * <LI><B>ORDINAL_POSITION</B> int {@code =>} the ordinal position, starting from 1, for the |
| * input and output parameters for a procedure. A value of 0 is returned if this row |
| * describes the procedure's return value. For result set columns, it is the ordinal |
| * position of the column in the result set starting from 1. If there are multiple result |
| * sets, the column ordinal positions are implementation defined. |
| * <LI><B>IS_NULLABLE</B> String {@code =>} ISO rules are used to determine the nullability for |
| * a column. |
| * <UL> |
| * <LI>YES --- if the column can include NULLs |
| * <LI>NO --- if the column cannot include NULLs |
| * <LI>empty string --- if the nullability for the column is unknown |
| * </UL> |
| * <LI><B>SPECIFIC_NAME</B> String {@code =>} the name which uniquely identifies this procedure |
| * within its schema. |
| * </OL> |
| * |
| * <p><B>Note:</B> Some databases may not return the column descriptions for a procedure. |
| * |
| * <p>The PRECISION column represents the specified column size for the given column. For numeric |
| * data, this is the maximum precision. For character data, this is the length in characters. For |
| * datetime datatypes, this is the length in characters of the String representation (assuming the |
| * maximum allowed precision of the fractional seconds component). For binary data, this is the |
| * length in bytes. For the ROWID datatype, this is the length in bytes. Null is returned for data |
| * types where the column size is not applicable. |
| * |
| * @param catalog a catalog name; must match the catalog name as it is stored in the database; "" |
| * retrieves those without a catalog; <code>null</code> means that the catalog name should not |
| * be used to narrow the search |
| * @param schemaPattern a schema name pattern; must match the schema name as it is stored in the |
| * database; "" retrieves those without a schema; <code>null</code> means that the schema name |
| * should not be used to narrow the search |
| * @param procedureNamePattern a procedure name pattern; must match the procedure name as it is |
| * stored in the database |
| * @param columnNamePattern a column name pattern; must match the column name as it is stored in |
| * the database |
| * @return <code>ResultSet</code> - each row describes a stored procedure parameter or column |
| * @throws SQLException if a database access error occurs |
| * @see #getSearchStringEscape |
| */ |
| public ResultSet getProcedureColumns( |
| String catalog, String schemaPattern, String procedureNamePattern, String columnNamePattern) |
| throws SQLException { |
| /* |
| * Get info from information_schema.parameters |
| */ |
| StringBuilder sb = |
| new StringBuilder( |
| "SELECT SPECIFIC_SCHEMA PROCEDURE_CAT, NULL PROCEDURE_SCHEM, SPECIFIC_NAME" |
| + " PROCEDURE_NAME, PARAMETER_NAME COLUMN_NAME, CASE PARAMETER_MODE WHEN 'IN'" |
| + " THEN " |
| + procedureColumnIn |
| + " WHEN 'OUT' THEN " |
| + procedureColumnOut |
| + " WHEN 'INOUT' THEN " |
| + procedureColumnInOut |
| + " ELSE IF(PARAMETER_MODE IS NULL," |
| + procedureColumnReturn |
| + "," |
| + procedureColumnUnknown |
| + ")" |
| + " END COLUMN_TYPE," |
| + dataTypeClause("DTD_IDENTIFIER") |
| + " DATA_TYPE," |
| + "DATA_TYPE TYPE_NAME," |
| + " CASE DATA_TYPE" |
| + " WHEN 'time' THEN " |
| + "IF(DATETIME_PRECISION = 0, 10, CAST(11 + DATETIME_PRECISION as signed integer))" |
| + " WHEN 'date' THEN 10" |
| + " WHEN 'datetime' THEN " |
| + "IF(DATETIME_PRECISION = 0, 19, CAST(20 + DATETIME_PRECISION as signed integer))" |
| + " WHEN 'timestamp' THEN " |
| + "IF(DATETIME_PRECISION = 0, 19, CAST(20 + DATETIME_PRECISION as signed integer))" |
| + " ELSE " |
| + " IF(NUMERIC_PRECISION IS NULL, LEAST(CHARACTER_MAXIMUM_LENGTH," |
| + Integer.MAX_VALUE |
| + "), NUMERIC_PRECISION) " |
| + " END `PRECISION`," |
| + " CASE DATA_TYPE" |
| + " WHEN 'time' THEN " |
| + "IF(DATETIME_PRECISION = 0, 10, CAST(11 + DATETIME_PRECISION as signed integer))" |
| + " WHEN 'date' THEN 10" |
| + " WHEN 'datetime' THEN " |
| + "IF(DATETIME_PRECISION = 0, 19, CAST(20 + DATETIME_PRECISION as signed integer))" |
| + " WHEN 'timestamp' THEN " |
| + "IF(DATETIME_PRECISION = 0, 19, CAST(20 + DATETIME_PRECISION as signed integer))" |
| + " ELSE " |
| + " IF(NUMERIC_PRECISION IS NULL, LEAST(CHARACTER_MAXIMUM_LENGTH," |
| + Integer.MAX_VALUE |
| + "), NUMERIC_PRECISION) " |
| + " END `LENGTH`," |
| + " CASE DATA_TYPE" |
| + " WHEN 'time' THEN CAST(DATETIME_PRECISION as signed integer)" |
| + " WHEN 'datetime' THEN CAST(DATETIME_PRECISION as signed integer)" |
| + " WHEN 'timestamp' THEN CAST(DATETIME_PRECISION as signed integer)" |
| + " ELSE NUMERIC_SCALE " |
| + " END `SCALE`," |
| + "10 RADIX," |
| + procedureNullableUnknown |
| + " NULLABLE,NULL REMARKS,NULL COLUMN_DEF,0 SQL_DATA_TYPE,0" |
| + " SQL_DATETIME_SUB,CHARACTER_OCTET_LENGTH CHAR_OCTET_LENGTH ,ORDINAL_POSITION, ''" |
| + " IS_NULLABLE, SPECIFIC_NAME FROM INFORMATION_SCHEMA.PARAMETERS"); |
| |
| boolean firstCondition = catalogCond(true, sb, "SPECIFIC_SCHEMA", catalog); |
| firstCondition = patternCond(firstCondition, sb, "SPECIFIC_NAME", procedureNamePattern); |
| firstCondition = patternCond(firstCondition, sb, "PARAMETER_NAME", columnNamePattern); |
| sb.append(" ORDER BY SPECIFIC_SCHEMA, SPECIFIC_NAME, ORDINAL_POSITION"); |
| |
| return executeQuery(sb.toString()); |
| } |
| |
| /** |
| * Retrieves a description of the given catalog's system or user function parameters and return |
| * type. |
| * |
| * <p>Only descriptions matching the schema, function and parameter name criteria are returned. |
| * They are ordered by <code>FUNCTION_CAT</code>, <code>FUNCTION_SCHEM</code>, <code>FUNCTION_NAME |
| * </code> and <code>SPECIFIC_ NAME</code>. Within this, the return value, if any, is first. Next |
| * are the parameter descriptions in call order. The column descriptions follow in column number |
| * order. |
| * |
| * <p>Each row in the <code>ResultSet</code> is a parameter description, column description or |
| * return type description with the following fields: |
| * |
| * <OL> |
| * <LI><B>FUNCTION_CAT</B> String {@code =>} function catalog (may be <code>null</code>) |
| * <LI><B>FUNCTION_SCHEM</B> String {@code =>} function schema (may be <code>null</code>) |
| * <LI><B>FUNCTION_NAME</B> String {@code =>} function name. This is the name used to invoke the |
| * function |
| * <LI><B>COLUMN_NAME</B> String {@code =>} column/parameter name |
| * <LI><B>COLUMN_TYPE</B> Short {@code =>} kind of column/parameter: |
| * <UL> |
| * <LI>functionColumnUnknown - nobody knows |
| * <LI>functionColumnIn - IN parameter |
| * <LI>functionColumnInOut - INOUT parameter |
| * <LI>functionColumnOut - OUT parameter |
| * <LI>functionColumnReturn - function return value |
| * <LI>functionColumnResult - Indicates that the parameter or column is a column in the |
| * <code>ResultSet</code> |
| * </UL> |
| * <LI><B>DATA_TYPE</B> int {@code =>} SQL type from java.sql.Types |
| * <LI><B>TYPE_NAME</B> String {@code =>} SQL type name, for a UDT type the type name is fully |
| * qualified |
| * <LI><B>PRECISION</B> int {@code =>} precision |
| * <LI><B>LENGTH</B> int {@code =>} length in bytes of data |
| * <LI><B>SCALE</B> short {@code =>} scale - null is returned for data types where SCALE is not |
| * applicable. |
| * <LI><B>RADIX</B> short {@code =>} radix |
| * <LI><B>NULLABLE</B> short {@code =>} can it contain NULL. |
| * <UL> |
| * <LI>functionNoNulls - does not allow NULL values |
| * <LI>functionNullable - allows NULL values |
| * <LI>functionNullableUnknown - nullability unknown |
| * </UL> |
| * <LI><B>REMARKS</B> String {@code =>} comment describing column/parameter |
| * <LI><B>CHAR_OCTET_LENGTH</B> int {@code =>} the maximum length of binary and character based |
| * parameters or columns. For any other datatype the returned value is a NULL |
| * <LI><B>ORDINAL_POSITION</B> int {@code =>} the ordinal position, starting from 1, for the |
| * input and output parameters. A value of 0 is returned if this row describes the |
| * function's return value. For result set columns, it is the ordinal position of the column |
| * in the result set starting from 1. |
| * <LI><B>IS_NULLABLE</B> String {@code =>} ISO rules are used to determine the nullability for |
| * a parameter or column. |
| * <UL> |
| * <LI>YES --- if the parameter or column can include NULLs |
| * <LI>NO --- if the parameter or column cannot include NULLs |
| * <LI>empty string --- if the nullability for the parameter or column is unknown |
| * </UL> |
| * <LI><B>SPECIFIC_NAME</B> String {@code =>} the name which uniquely identifies this function |
| * within its schema. This is a user specified, or DBMS generated, name that may be |
| * different then the <code>FUNCTION_NAME</code> for example with overload functions |
| * </OL> |
| * |
| * <p>The PRECISION column represents the specified column size for the given parameter or column. |
| * For numeric data, this is the maximum precision. For character data, this is the length in |
| * characters. For datetime datatypes, this is the length in characters of the String |
| * representation (assuming the maximum allowed precision of the fractional seconds component). |
| * For binary data, this is the length in bytes. For the ROWID datatype, this is the length in |
| * bytes. Null is returned for data types where the column size is not applicable. |
| * |
| * @param catalog a catalog name; must match the catalog name as it is stored in the database; "" |
| * retrieves those without a catalog; <code>null</code> means that the catalog name should not |
| * be used to narrow the search |
| * @param schemaPattern a schema name pattern; must match the schema name as it is stored in the |
| * database; "" retrieves those without a schema; <code>null</code> means that the schema name |
| * should not be used to narrow the search |
| * @param functionNamePattern a procedure name pattern; must match the function name as it is |
| * stored in the database |
| * @param columnNamePattern a parameter name pattern; must match the parameter or column name as |
| * it is stored in the database |
| * @return <code>ResultSet</code> - each row describes a user function parameter, column or return |
| * type |
| * @throws SQLException if a database access error occurs |
| * @see #getSearchStringEscape |
| * @since 1.6 |
| */ |
| public ResultSet getFunctionColumns( |
| String catalog, String schemaPattern, String functionNamePattern, String columnNamePattern) |
| throws SQLException { |
| |
| StringBuilder sb = |
| new StringBuilder( |
| "SELECT SPECIFIC_SCHEMA `FUNCTION_CAT`, NULL `FUNCTION_SCHEM`, SPECIFIC_NAME" |
| + " FUNCTION_NAME, PARAMETER_NAME COLUMN_NAME, CASE PARAMETER_MODE WHEN 'IN'" |
| + " THEN " |
| + functionColumnIn |
| + " WHEN 'OUT' THEN " |
| + functionColumnOut |
| + " WHEN 'INOUT' THEN " |
| + functionColumnInOut |
| + " ELSE " |
| + functionReturn |
| + " END COLUMN_TYPE," |
| + dataTypeClause("DTD_IDENTIFIER") |
| + " DATA_TYPE,DATA_TYPE TYPE_NAME,NUMERIC_PRECISION" |
| + " `PRECISION`,CHARACTER_MAXIMUM_LENGTH LENGTH,NUMERIC_SCALE SCALE,10 RADIX," |
| + procedureNullableUnknown |
| + " NULLABLE,NULL REMARKS,CHARACTER_OCTET_LENGTH CHAR_OCTET_LENGTH" |
| + " ,ORDINAL_POSITION, '' IS_NULLABLE, SPECIFIC_NAME FROM" |
| + " INFORMATION_SCHEMA.PARAMETERS"); |
| |
| boolean firstCondition = catalogCond(true, sb, "SPECIFIC_SCHEMA", catalog); |
| firstCondition = patternCond(firstCondition, sb, "SPECIFIC_NAME", functionNamePattern); |
| firstCondition = patternCond(firstCondition, sb, "PARAMETER_NAME", columnNamePattern); |
| sb.append(firstCondition ? " WHERE " : " AND ") |
| .append(" ROUTINE_TYPE='FUNCTION' ORDER BY FUNCTION_CAT, SPECIFIC_NAME, ORDINAL_POSITION"); |
| |
| return executeQuery(sb.toString()); |
| } |
| |
| public ResultSet getSchemas() throws SQLException { |
| return executeQuery("SELECT '' TABLE_SCHEM, '' TABLE_catalog FROM DUAL WHERE 1=0"); |
| } |
| |
| public ResultSet getSchemas(String catalog, String schemaPattern) throws SQLException { |
| return executeQuery("SELECT ' ' table_schem, ' ' table_catalog FROM DUAL WHERE 1=0"); |
| } |
| |
| public ResultSet getCatalogs() throws SQLException { |
| return executeQuery("SELECT SCHEMA_NAME TABLE_CAT FROM INFORMATION_SCHEMA.SCHEMATA ORDER BY 1"); |
| } |
| |
| public ResultSet getTableTypes() throws SQLException { |
| return executeQuery( |
| "SELECT 'TABLE' TABLE_TYPE UNION SELECT 'SYSTEM VIEW' TABLE_TYPE UNION SELECT 'VIEW'" |
| + " TABLE_TYPE"); |
| } |
| |
| /** |
| * Retrieves a description of the access rights for a table's columns. |
| * |
| * <p>Only privileges matching the column name criteria are returned. They are ordered by |
| * COLUMN_NAME and PRIVILEGE. |
| * |
| * <p>Each privilege description has the following columns: |
| * |
| * <OL> |
| * <LI><B>TABLE_CAT</B> String {@code =>} table catalog (may be <code>null</code>) |
| * <LI><B>TABLE_SCHEM</B> String {@code =>} table schema (may be <code>null</code>) |
| * <LI><B>TABLE_NAME</B> String {@code =>} table name |
| * <LI><B>COLUMN_NAME</B> String {@code =>} column name |
| * <LI><B>GRANTOR</B> String {@code =>} grantor of access (may be <code>null</code>) |
| * <LI><B>GRANTEE</B> String {@code =>} grantee of access |
| * <LI><B>PRIVILEGE</B> String {@code =>} name of access (SELECT, INSERT, UPDATE, REFRENCES, |
| * ...) |
| * <LI><B>IS_GRANTABLE</B> String {@code =>} "YES" if grantee is permitted to grant to others; |
| * "NO" if not; <code>null</code> if unknown |
| * </OL> |
| * |
| * @param catalog a catalog name; must match the catalog name as it is stored in the database; "" |
| * retrieves those without a catalog; <code>null</code> means that the catalog name should not |
| * be used to narrow the search |
| * @param schema a schema name; must match the schema name as it is stored in the database; "" |
| * retrieves those without a schema; <code>null</code> means that the schema name should not |
| * be used to narrow the search |
| * @param table a table name; must match the table name as it is stored in the database |
| * @param columnNamePattern a column name pattern; must match the column name as it is stored in |
| * the database |
| * @return <code>ResultSet</code> - each row is a column privilege description |
| * @throws SQLException if a database access error occurs |
| * @see #getSearchStringEscape |
| */ |
| public ResultSet getColumnPrivileges( |
| String catalog, String schema, String table, String columnNamePattern) throws SQLException { |
| |
| if (table == null) { |
| throw new SQLException("'table' parameter must not be null"); |
| } |
| StringBuilder sb = |
| new StringBuilder( |
| "SELECT TABLE_SCHEMA TABLE_CAT, " |
| + "NULL TABLE_SCHEM, " |
| + "TABLE_NAME, " |
| + "COLUMN_NAME, " |
| + "NULL AS GRANTOR, " |
| + "GRANTEE, " |
| + "PRIVILEGE_TYPE AS PRIVILEGE, " |
| + "IS_GRANTABLE " |
| + "FROM INFORMATION_SCHEMA.COLUMN_PRIVILEGES"); |
| boolean firstCondition = catalogCond(true, sb, "TABLE_SCHEMA", catalog); |
| sb.append(firstCondition ? " WHERE " : " AND ") |
| .append(" TABLE_NAME = ") |
| .append(escapeQuote(table)); |
| patternCond(false, sb, "COLUMN_NAME", columnNamePattern); |
| sb.append(" ORDER BY COLUMN_NAME, PRIVILEGE_TYPE"); |
| |
| return executeQuery(sb.toString()); |
| } |
| |
| /** |
| * Retrieves a description of the access rights for each table available in a catalog. Note that a |
| * table privilege applies to one or more columns in the table. It would be wrong to assume that |
| * this privilege applies to all columns (this may be true for some systems but is not true for |
| * all.) |
| * |
| * <p>Only privileges matching the schema and table name criteria are returned. They are ordered |
| * by <code>TABLE_CAT</code>, <code>TABLE_SCHEM</code>, <code>TABLE_NAME</code>, and <code> |
| * PRIVILEGE</code>. |
| * |
| * <p>Each privilege description has the following columns: |
| * |
| * <OL> |
| * <LI><B>TABLE_CAT</B> String {@code =>} table catalog (may be <code>null</code>) |
| * <LI><B>TABLE_SCHEM</B> String {@code =>} table schema (may be <code>null</code>) |
| * <LI><B>TABLE_NAME</B> String {@code =>} table name |
| * <LI><B>GRANTOR</B> String {@code =>} grantor of access (may be <code>null</code>) |
| * <LI><B>GRANTEE</B> String {@code =>} grantee of access |
| * <LI><B>PRIVILEGE</B> String {@code =>} name of access (SELECT, INSERT, UPDATE, REFRENCES, |
| * ...) |
| * <LI><B>IS_GRANTABLE</B> String {@code =>} "YES" if grantee is permitted to grant to others; |
| * "NO" if not; <code>null</code> if unknown |
| * </OL> |
| * |
| * @param catalog a catalog name; must match the catalog name as it is stored in the database; "" |
| * retrieves those without a catalog; <code>null</code> means that the catalog name should not |
| * be used to narrow the search |
| * @param schemaPattern a schema name pattern; must match the schema name as it is stored in the |
| * database; "" retrieves those without a schema; <code>null</code> means that the schema name |
| * should not be used to narrow the search |
| * @param tableNamePattern a table name pattern; must match the table name as it is stored in the |
| * database |
| * @return <code>ResultSet</code> - each row is a table privilege description |
| * @throws SQLException if a database access error occurs |
| * @see #getSearchStringEscape |
| */ |
| public ResultSet getTablePrivileges(String catalog, String schemaPattern, String tableNamePattern) |
| throws SQLException { |
| StringBuilder sb = |
| new StringBuilder( |
| "SELECT TABLE_SCHEMA TABLE_CAT, " |
| + "NULL TABLE_SCHEM, " |
| + "TABLE_NAME, " |
| + "NULL GRANTOR," |
| + "GRANTEE, " |
| + "PRIVILEGE_TYPE PRIVILEGE, " |
| + "IS_GRANTABLE " |
| + "FROM INFORMATION_SCHEMA.TABLE_PRIVILEGES"); |
| boolean firstCondition = catalogCond(true, sb, "TABLE_SCHEMA", catalog); |
| firstCondition = patternCond(firstCondition, sb, "TABLE_NAME", tableNamePattern); |
| sb.append(" ORDER BY TABLE_SCHEMA, TABLE_NAME, PRIVILEGE_TYPE "); |
| |
| return executeQuery(sb.toString()); |
| } |
| |
| /** |
| * Retrieves a description of a table's columns that are automatically updated when any value in a |
| * row is updated. They are unordered. |
| * |
| * <p>Each column description has the following columns: |
| * |
| * <OL> |
| * <LI><B>SCOPE</B> short {@code =>} is not used |
| * <LI><B>COLUMN_NAME</B> String {@code =>} column name |
| * <LI><B>DATA_TYPE</B> int {@code =>} SQL data type from <code>java.sql.Types</code> |
| * <LI><B>TYPE_NAME</B> String {@code =>} Data source-dependent type name |
| * <LI><B>COLUMN_SIZE</B> int {@code =>} precision |
| * <LI><B>BUFFER_LENGTH</B> int {@code =>} length of column value in bytes |
| * <LI><B>DECIMAL_DIGITS</B> short {@code =>} scale - Null is returned for data types where |
| * DECIMAL_DIGITS is not applicable. |
| * <LI><B>PSEUDO_COLUMN</B> short {@code =>} whether this is pseudo column like an Oracle ROWID |
| * <UL> |
| * <LI>versionColumnUnknown - may or may not be pseudo column |
| * <LI>versionColumnNotPseudo - is NOT a pseudo column |
| * <LI>versionColumnPseudo - is a pseudo column |
| * </UL> |
| * </OL> |
| * |
| * <p>The COLUMN_SIZE column represents the specified column size for the given column. For |
| * numeric data, this is the maximum precision. For character data, this is the length in |
| * characters. For datetime datatypes, this is the length in characters of the String |
| * representation (assuming the maximum allowed precision of the fractional seconds component). |
| * For binary data, this is the length in bytes. For the ROWID datatype, this is the length in |
| * bytes. Null is returned for data types where the column size is not applicable. |
| * |
| * @param catalog a catalog name; must match the catalog name as it is stored in the database; "" |
| * retrieves those without a catalog;<code>null</code> means that the catalog name should not |
| * be used to narrow the search |
| * @param schema a schema name; must match the schema name as it is stored in the database; "" |
| * retrieves those without a schema; <code>null</code> means that the schema name should not |
| * be used to narrow the search |
| * @param table a table name; must match the table name as it is stored in the database |
| * @return a <code>ResultSet</code> object in which each row is a column description |
| * @throws SQLException if a database access error occurs |
| */ |
| public ResultSet getVersionColumns(String catalog, String schema, String table) |
| throws SQLException { |
| String sql = |
| "SELECT 0 SCOPE, ' ' COLUMN_NAME, 0 DATA_TYPE," |
| + " ' ' TYPE_NAME, 0 COLUMN_SIZE, 0 BUFFER_LENGTH," |
| + " 0 DECIMAL_DIGITS, 0 PSEUDO_COLUMN " |
| + " FROM DUAL WHERE 1 = 0"; |
| return executeQuery(sql); |
| } |
| |
| /** |
| * Retrieves a description of the foreign key columns in the given foreign key table that |
| * reference the primary key or the columns representing a unique constraint of the parent table |
| * (could be the same or a different table). The number of columns returned from the parent table |
| * must match the number of columns that make up the foreign key. They are ordered by FKTABLE_CAT, |
| * FKTABLE_SCHEM, FKTABLE_NAME, and KEY_SEQ. |
| * |
| * <p>Each foreign key column description has the following columns: |
| * |
| * <OL> |
| * <LI><B>PKTABLE_CAT</B> String {@code =>} parent key table catalog (may be <code>null</code>) |
| * <LI><B>PKTABLE_SCHEM</B> String {@code =>} parent key table schema (may be <code>null</code>) |
| * <LI><B>PKTABLE_NAME</B> String {@code =>} parent key table name |
| * <LI><B>PKCOLUMN_NAME</B> String {@code =>} parent key column name |
| * <LI><B>FKTABLE_CAT</B> String {@code =>} foreign key table catalog (may be <code>null</code>) |
| * being exported (may be <code>null</code>) |
| * <LI><B>FKTABLE_SCHEM</B> String {@code =>} foreign key table schema (may be <code>null</code> |
| * ) being exported (may be <code>null</code>) |
| * <LI><B>FKTABLE_NAME</B> String {@code =>} foreign key table name being exported |
| * <LI><B>FKCOLUMN_NAME</B> String {@code =>} foreign key column name being exported |
| * <LI><B>KEY_SEQ</B> short {@code =>} sequence number within foreign key( a value of 1 |
| * represents the first column of the foreign key, a value of 2 would represent the second |
| * column within the foreign key). |
| * <LI><B>UPDATE_RULE</B> short {@code =>} What happens to foreign key when parent key is |
| * updated: |
| * <UL> |
| * <LI>importedNoAction - do not allow update of parent key if it has been imported |
| * <LI>importedKeyCascade - change imported key to agree with parent key update |
| * <LI>importedKeySetNull - change imported key to <code>NULL</code> if its parent key has |
| * been updated |
| * <LI>importedKeySetDefault - change imported key to default values if its parent key has |
| * been updated |
| * <LI>importedKeyRestrict - same as importedKeyNoAction (for ODBC 2.x compatibility) |
| * </UL> |
| * <LI><B>DELETE_RULE</B> short {@code =>} What happens to the foreign key when parent key is |
| * deleted. |
| * <UL> |
| * <LI>importedKeyNoAction - do not allow delete of parent key if it has been imported |
| * <LI>importedKeyCascade - delete rows that import a deleted key |
| * <LI>importedKeySetNull - change imported key to <code>NULL</code> if its primary key |
| * has been deleted |
| * <LI>importedKeyRestrict - same as importedKeyNoAction (for ODBC 2.x compatibility) |
| * <LI>importedKeySetDefault - change imported key to default if its parent key has been |
| * deleted |
| * </UL> |
| * <LI><B>FK_NAME</B> String {@code =>} foreign key name (may be <code>null</code>) |
| * <LI><B>PK_NAME</B> String {@code =>} parent key name (may be <code>null</code>) |
| * <LI><B>DEFERRABILITY</B> short {@code =>} can the evaluation of foreign key constraints be |
| * deferred until commit |
| * <UL> |
| * <LI>importedKeyInitiallyDeferred - see SQL92 for definition |
| * <LI>importedKeyInitiallyImmediate - see SQL92 for definition |
| * <LI>importedKeyNotDeferrable - see SQL92 for definition |
| * </UL> |
| * </OL> |
| * |
| * @param parentCatalog a catalog name; must match the catalog name as it is stored in the |
| * database; "" retrieves those without a catalog; <code>null</code> means drop catalog name |
| * from the selection criteria |
| * @param parentSchema a schema name; must match the schema name as it is stored in the database; |
| * "" retrieves those without a schema; <code>null</code> means drop schema name from the |
| * selection criteria |
| * @param parentTable the name of the table that exports the key; pattern, or null (means any |
| * table) value |
| * @param foreignCatalog a catalog name; must match the catalog name as it is stored in the |
| * database; "" retrieves those without a catalog; <code>null</code> means drop catalog name |
| * from the selection criteria |
| * @param foreignSchema a schema name; must match the schema name as it is stored in the database; |
| * "" retrieves those without a schema; <code>null</code> means drop schema name from the |
| * selection criteria |
| * @param foreignTable the name of the table that imports the key; pattern, or null (means any |
| * table) value is stored in the database |
| * @return <code>ResultSet</code> - each row is a foreign key column description |
| * @throws SQLException if a database access error occurs |
| * @see #getImportedKeys |
| */ |
| public ResultSet getCrossReference( |
| String parentCatalog, |
| String parentSchema, |
| String parentTable, |
| String foreignCatalog, |
| String foreignSchema, |
| String foreignTable) |
| throws SQLException { |
| |
| StringBuilder sb = |
| new StringBuilder( |
| "SELECT KCU.REFERENCED_TABLE_SCHEMA PKTABLE_CAT, NULL PKTABLE_SCHEM, " |
| + " KCU.REFERENCED_TABLE_NAME PKTABLE_NAME, KCU.REFERENCED_COLUMN_NAME" |
| + " PKCOLUMN_NAME, KCU.TABLE_SCHEMA FKTABLE_CAT, NULL FKTABLE_SCHEM, " |
| + " KCU.TABLE_NAME FKTABLE_NAME, KCU.COLUMN_NAME FKCOLUMN_NAME," |
| + " KCU.POSITION_IN_UNIQUE_CONSTRAINT KEY_SEQ, CASE update_rule WHEN 'RESTRICT'" |
| + " THEN 1 WHEN 'NO ACTION' THEN 3 WHEN 'CASCADE' THEN 0 WHEN 'SET NULL' THEN" |
| + " 2 WHEN 'SET DEFAULT' THEN 4 END UPDATE_RULE, CASE DELETE_RULE WHEN" |
| + " 'RESTRICT' THEN 1 WHEN 'NO ACTION' THEN 3 WHEN 'CASCADE' THEN 0 WHEN 'SET" |
| + " NULL' THEN 2 WHEN 'SET DEFAULT' THEN 4 END DELETE_RULE, RC.CONSTRAINT_NAME" |
| + " FK_NAME, RC.UNIQUE_CONSTRAINT_NAME PK_NAME," |
| + importedKeyNotDeferrable |
| + " DEFERRABILITY " |
| + "FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU" |
| + " INNER JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC" |
| + " ON KCU.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA" |
| + " AND KCU.CONSTRAINT_NAME = RC.CONSTRAINT_NAME "); |
| |
| boolean firstCondition = catalogCond(true, sb, "KCU.REFERENCED_TABLE_SCHEMA", parentCatalog); |
| firstCondition = catalogCond(firstCondition, sb, "KCU.TABLE_SCHEMA", foreignCatalog); |
| firstCondition = patternCond(firstCondition, sb, "KCU.REFERENCED_TABLE_NAME", parentTable); |
| firstCondition = patternCond(firstCondition, sb, "KCU.TABLE_NAME", foreignTable); |
| sb.append("ORDER BY FKTABLE_CAT, FKTABLE_SCHEM, FKTABLE_NAME, KEY_SEQ"); |
| |
| return executeQuery(sb.toString()); |
| } |
| |
| /** |
| * Retrieves a description of all the data types supported by this database. They are ordered by |
| * DATA_TYPE and then by how closely the data type maps to the corresponding JDBC SQL type. |
| * |
| * <p>If the database supports SQL distinct types, then getTypeInfo() will return a single row |
| * with a TYPE_NAME of DISTINCT and a DATA_TYPE of Types.DISTINCT. If the database supports SQL |
| * structured types, then getTypeInfo() will return a single row with a TYPE_NAME of STRUCT and a |
| * DATA_TYPE of Types.STRUCT. |
| * |
| * <p>If SQL distinct or structured types are supported, then information on the individual types |
| * may be obtained from the getUDTs() method. |
| * |
| * <p>Each type description has the following columns: |
| * |
| * <OL> |
| * <LI><B>TYPE_NAME</B> String {@code =>} Type name |
| * <LI><B>DATA_TYPE</B> int {@code =>} SQL data type from java.sql.Types |
| * <LI><B>PRECISION</B> int {@code =>} maximum precision |
| * <LI><B>LITERAL_PREFIX</B> String {@code =>} prefix used to quote a literal (may be <code>null |
| * </code>) |
| * <LI><B>LITERAL_SUFFIX</B> String {@code =>} suffix used to quote a literal (may be <code>null |
| * </code>) |
| * <LI><B>CREATE_PARAMS</B> String {@code =>} parameters used in creating the type (may be |
| * <code>null</code>) |
| * <LI><B>NULLABLE</B> short {@code =>} can you use NULL for this type. |
| * <UL> |
| * <LI>typeNoNulls - does not allow NULL values |
| * <LI>typeNullable - allows NULL values |
| * <LI>typeNullableUnknown - nullability unknown |
| * </UL> |
| * <LI><B>CASE_SENSITIVE</B> boolean{@code =>} is it case sensitive. |
| * <LI><B>SEARCHABLE</B> short {@code =>} can you use "WHERE" based on this type: |
| * <UL> |
| * <LI>typePredNone - No support |
| * <LI>typePredChar - Only supported with WHERE .. LIKE |
| * <LI>typePredBasic - Supported except for WHERE .. LIKE |
| * <LI>typeSearchable - Supported for all WHERE .. |
| * </UL> |
| * <LI><B>UNSIGNED_ATTRIBUTE</B> boolean {@code =>} is it unsigned. |
| * <LI><B>FIXED_PREC_SCALE</B> boolean {@code =>} can it be a money value. |
| * <LI><B>AUTO_INCREMENT</B> boolean {@code =>} can it be used for an auto-increment value. |
| * <LI><B>LOCAL_TYPE_NAME</B> String {@code =>} localized version of type name (may be <code> |
| * null</code>) |
| * <LI><B>MINIMUM_SCALE</B> short {@code =>} minimum scale supported |
| * <LI><B>MAXIMUM_SCALE</B> short {@code =>} maximum scale supported |
| * <LI><B>SQL_DATA_TYPE</B> int {@code =>} unused |
| * <LI><B>SQL_DATETIME_SUB</B> int {@code =>} unused |
| * <LI><B>NUM_PREC_RADIX</B> int {@code =>} usually 2 or 10 |
| * </OL> |
| * |
| * <p>The PRECISION column represents the maximum column size that the server supports for the |
| * given datatype. For numeric data, this is the maximum precision. For character data, this is |
| * the length in characters. For datetime datatypes, this is the length in characters of the |
| * String representation (assuming the maximum allowed precision of the fractional seconds |
| * component). For binary data, this is the length in bytes. For the ROWID datatype, this is the |
| * length in bytes. Null is returned for data types where the column size is not applicable. |
| * |
| * @return a <code>ResultSet</code> object in which each row is an SQL type description |
| */ |
| public ResultSet getTypeInfo() { |
| String[] columnNames = { |
| "TYPE_NAME", "DATA_TYPE", "PRECISION", "LITERAL_PREFIX", "LITERAL_SUFFIX", |
| "CREATE_PARAMS", "NULLABLE", "CASE_SENSITIVE", "SEARCHABLE", "UNSIGNED_ATTRIBUTE", |
| "FIXED_PREC_SCALE", "AUTO_INCREMENT", "LOCAL_TYPE_NAME", "MINIMUM_SCALE", "MAXIMUM_SCALE", |
| "SQL_DATA_TYPE", "SQL_DATETIME_SUB", "NUM_PREC_RADIX" |
| }; |
| DataType[] dataTypes = { |
| DataType.VARCHAR, |
| DataType.INTEGER, |
| DataType.INTEGER, |
| DataType.VARCHAR, |
| DataType.VARCHAR, |
| DataType.VARCHAR, |
| DataType.INTEGER, |
| DataType.BIT, |
| DataType.SMALLINT, |
| DataType.BIT, |
| DataType.BIT, |
| DataType.BIT, |
| DataType.VARCHAR, |
| DataType.SMALLINT, |
| DataType.SMALLINT, |
| DataType.INTEGER, |
| DataType.INTEGER, |
| DataType.INTEGER |
| }; |
| |
| String[][] data = { |
| {"BIT", "-7", "1", "", "", "", "1", "1", "3", "0", "0", "0", "BIT", "0", "0", "0", "0", "10"}, |
| { |
| "BOOL", "-7", "1", "", "", "", "1", "1", "3", "0", "0", "0", "BOOL", "0", "0", "0", "0", |
| "10" |
| }, |
| { |
| "TINYINT", |
| "-6", |
| "3", |
| "", |
| "", |
| "[(M)] [UNSIGNED] [ZEROFILL]", |
| "1", |
| "0", |
| "3", |
| "1", |
| "0", |
| "1", |
| "TINYINT", |
| "0", |
| "0", |
| "0", |
| "0", |
| "10" |
| }, |
| { |
| "TINYINT UNSIGNED", |
| "-6", |
| "3", |
| "", |
| "", |
| "[(M)] [UNSIGNED] [ZEROFILL]", |
| "1", |
| "0", |
| "3", |
| "1", |
| "0", |
| "1", |
| "TINYINT UNSIGNED", |
| "0", |
| "0", |
| "0", |
| "0", |
| "10" |
| }, |
| { |
| "BIGINT", |
| "-5", |
| "19", |
| "", |
| "", |
| "[(M)] [UNSIGNED] [ZEROFILL]", |
| "1", |
| "0", |
| "3", |
| "1", |
| "0", |
| "1", |
| "BIGINT", |
| "0", |
| "0", |
| "0", |
| "0", |
| "10" |
| }, |
| { |
| "BIGINT UNSIGNED", |
| "-5", |
| "20", |
| "", |
| "", |
| "[(M)] [ZEROFILL]", |
| "1", |
| "0", |
| "3", |
| "1", |
| "0", |
| "1", |
| "BIGINT UNSIGNED", |
| "0", |
| "0", |
| "0", |
| "0", |
| "10" |
| }, |
| { |
| "LONG VARBINARY", |
| "-4", |
| "16777215", |
| "'", |
| "'", |
| "", |
| "1", |
| "1", |
| "3", |
| "0", |
| "0", |
| "0", |
| "LONG VARBINARY", |
| "0", |
| "0", |
| "0", |
| "0", |
| "10" |
| }, |
| { |
| "MEDIUMBLOB", |
| "-4", |
| "16777215", |
| "'", |
| "'", |
| "", |
| "1", |
| "1", |
| "3", |
| "0", |
| "0", |
| "0", |
| "MEDIUMBLOB", |
| "0", |
| "0", |
| "0", |
| "0", |
| "10" |
| }, |
| { |
| "LONGBLOB", |
| "-4", |
| "2147483647", |
| "'", |
| "'", |
| "", |
| "1", |
| "1", |
| "3", |
| "0", |
| "0", |
| "0", |
| "LONGBLOB", |
| "0", |
| "0", |
| "0", |
| "0", |
| "10" |
| }, |
| { |
| "BLOB", "-4", "65535", "'", "'", "", "1", "1", "3", "0", "0", "0", "BLOB", "0", "0", "0", |
| "0", "10" |
| }, |
| { |
| "TINYBLOB", |
| "-4", |
| "255", |
| "'", |
| "'", |
| "", |
| "1", |
| "1", |
| "3", |
| "0", |
| "0", |
| "0", |
| "TINYBLOB", |
| "0", |
| "0", |
| "0", |
| "0", |
| "10" |
| }, |
| { |
| "VARBINARY", |
| "-3", |
| "255", |
| "'", |
| "'", |
| "(M)", |
| "1", |
| "1", |
| "3", |
| "0", |
| "0", |
| "0", |
| "VARBINARY", |
| "0", |
| "0", |
| "0", |
| "0", |
| "10" |
| }, |
| { |
| "BINARY", "-2", "255", "'", "'", "(M)", "1", "1", "3", "0", "0", "0", "BINARY", "0", "0", |
| "0", "0", "10" |
| }, |
| { |
| "LONG VARCHAR", |
| "-1", |
| "16777215", |
| "'", |
| "'", |
| "", |
| "1", |
| "0", |
| "3", |
| "0", |
| "0", |
| "0", |
| "LONG VARCHAR", |
| "0", |
| "0", |
| "0", |
| "0", |
| "10" |
| }, |
| { |
| "MEDIUMTEXT", |
| "-1", |
| "16777215", |
| "'", |
| "'", |
| "", |
| "1", |
| "0", |
| "3", |
| "0", |
| "0", |
| "0", |
| "MEDIUMTEXT", |
| "0", |
| "0", |
| "0", |
| "0", |
| "10" |
| }, |
| { |
| "LONGTEXT", |
| "-1", |
| "2147483647", |
| "'", |
| "'", |
| "", |
| "1", |
| "0", |
| "3", |
| "0", |
| "0", |
| "0", |
| "LONGTEXT", |
| "0", |
| "0", |
| "0", |
| "0", |
| "10" |
| }, |
| { |
| "TEXT", "-1", "65535", "'", "'", "", "1", "0", "3", "0", "0", "0", "TEXT", "0", "0", "0", |
| "0", "10" |
| }, |
| { |
| "TINYTEXT", |
| "-1", |
| "255", |
| "'", |
| "'", |
| "", |
| "1", |
| "0", |
| "3", |
| "0", |
| "0", |
| "0", |
| "TINYTEXT", |
| "0", |
| "0", |
| "0", |
| "0", |
| "10" |
| }, |
| { |
| "CHAR", "1", "255", "'", "'", "(M)", "1", "0", "3", "0", "0", "0", "CHAR", "0", "0", "0", |
| "0", "10" |
| }, |
| { |
| "NUMERIC", |
| "2", |
| "65", |
| "", |
| "", |
| "[(M,D])] [ZEROFILL]", |
| "1", |
| "0", |
| "3", |
| "0", |
| "0", |
| "1", |
| "NUMERIC", |
| "-308", |
| "308", |
| "0", |
| "0", |
| "10" |
| }, |
| { |
| "DECIMAL", |
| "3", |
| "65", |
| "", |
| "", |
| "[(M,D])] [ZEROFILL]", |
| "1", |
| "0", |
| "3", |
| "0", |
| "0", |
| "1", |
| "DECIMAL", |
| "-308", |
| "308", |
| "0", |
| "0", |
| "10" |
| }, |
| { |
| "INTEGER", |
| "4", |
| "10", |
| "", |
| "", |
| "[(M)] [UNSIGNED] [ZEROFILL]", |
| "1", |
| "0", |
| "3", |
| "1", |
| "0", |
| "1", |
| "INTEGER", |
| "0", |
| "0", |
| "0", |
| "0", |
| "10" |
| }, |
| { |
| "INTEGER UNSIGNED", |
| "4", |
| "10", |
| "", |
| "", |
| "[(M)] [ZEROFILL]", |
| "1", |
| "0", |
| "3", |
| "1", |
| "0", |
| "1", |
| "INTEGER UNSIGNED", |
| "0", |
| "0", |
| "0", |
| "0", |
| "10" |
| }, |
| { |
| "INT", |
| "4", |
| "10", |
| "", |
| "", |
| "[(M)] [UNSIGNED] [ZEROFILL]", |
| "1", |
| "0", |
| "3", |
| "1", |
| "0", |
| "1", |
| "INT", |
| "0", |
| "0", |
| "0", |
| "0", |
| "10" |
| }, |
| { |
| "INT UNSIGNED", |
| "4", |
| "10", |
| "", |
| "", |
| "[(M)] [ZEROFILL]", |
| "1", |
| "0", |
| "3", |
| "1", |
| "0", |
| "1", |
| "INT UNSIGNED", |
| "0", |
| "0", |
| "0", |
| "0", |
| "10" |
| }, |
| { |
| "MEDIUMINT", |
| "4", |
| "7", |
| "", |
| "", |
| "[(M)] [UNSIGNED] [ZEROFILL]", |
| "1", |
| "0", |
| "3", |
| "1", |
| "0", |
| "1", |
| "MEDIUMINT", |
| "0", |
| "0", |
| "0", |
| "0", |
| "10" |
| }, |
| { |
| "MEDIUMINT UNSIGNED", |
| "4", |
| "8", |
| "", |
| "", |
| "[(M)] [ZEROFILL]", |
| "1", |
| "0", |
| "3", |
| "1", |
| "0", |
| "1", |
| "MEDIUMINT UNSIGNED", |
| "0", |
| "0", |
| "0", |
| "0", |
| "10" |
| }, |
| { |
| "SMALLINT", |
| "5", |
| "5", |
| "", |
| "", |
| "[(M)] [UNSIGNED] [ZEROFILL]", |
| "1", |
| "0", |
| "3", |
| "1", |
| "0", |
| "1", |
| "SMALLINT", |
| "0", |
| "0", |
| "0", |
| "0", |
| "10" |
| }, |
| { |
| "SMALLINT UNSIGNED", |
| "5", |
| "5", |
| "", |
| "", |
| "[(M)] [ZEROFILL]", |
| "1", |
| "0", |
| "3", |
| "1", |
| "0", |
| "1", |
| "SMALLINT UNSIGNED", |
| "0", |
| "0", |
| "0", |
| "0", |
| "10" |
| }, |
| { |
| "FLOAT", |
| "7", |
| "10", |
| "", |
| "", |
| "[(M|D)] [ZEROFILL]", |
| "1", |
| "0", |
| "3", |
| "0", |
| "0", |
| "1", |
| "FLOAT", |
| "-38", |
| "38", |
| "0", |
| "0", |
| "10" |
| }, |
| { |
| "DOUBLE", |
| "8", |
| "17", |
| "", |
| "", |
| "[(M|D)] [ZEROFILL]", |
| "1", |
| "0", |
| "3", |
| "0", |
| "0", |
| "1", |
| "DOUBLE", |
| "-308", |
| "308", |
| "0", |
| "0", |
| "10" |
| }, |
| { |
| "DOUBLE PRECISION", |
| "8", |
| "17", |
| "", |
| "", |
| "[(M,D)] [ZEROFILL]", |
| "1", |
| "0", |
| "3", |
| "0", |
| "0", |
| "1", |
| "DOUBLE PRECISION", |
| "-308", |
| "308", |
| "0", |
| "0", |
| "10" |
| }, |
| { |
| "REAL", |
| "8", |
| "17", |
| "", |
| "", |
| "[(M,D)] [ZEROFILL]", |
| "1", |
| "0", |
| "3", |
| "0", |
| "0", |
| "1", |
| "REAL", |
| "-308", |
| "308", |
| "0", |
| "0", |
| "10" |
| }, |
| { |
| "VARCHAR", "12", "255", "'", "'", "(M)", "1", "0", "3", "0", "0", "0", "VARCHAR", "0", "0", |
| "0", "0", "10" |
| }, |
| { |
| "ENUM", "12", "65535", "'", "'", "", "1", "0", "3", "0", "0", "0", "ENUM", "0", "0", "0", |
| "0", "10" |
| }, |
| { |
| "SET", "12", "64", "'", "'", "", "1", "0", "3", "0", "0", "0", "SET", "0", "0", "0", "0", |
| "10" |
| }, |
| { |
| "DATE", "91", "10", "'", "'", "", "1", "0", "3", "0", "0", "0", "DATE", "0", "0", "0", "0", |
| "10" |
| }, |
| { |
| "TIME", "92", "18", "'", "'", "[(M)]", "1", "0", "3", "0", "0", "0", "TIME", "0", "0", "0", |
| "0", "10" |
| }, |
| { |
| "DATETIME", |
| "93", |
| "27", |
| "'", |
| "'", |
| "[(M)]", |
| "1", |
| "0", |
| "3", |
| "0", |
| "0", |
| "0", |
| "DATETIME", |
| "0", |
| "0", |
| "0", |
| "0", |
| "10" |
| }, |
| { |
| "TIMESTAMP", |
| "93", |
| "27", |
| "'", |
| "'", |
| "[(M)]", |
| "1", |
| "0", |
| "3", |
| "0", |
| "0", |
| "0", |
| "TIMESTAMP", |
| "0", |
| "0", |
| "0", |
| "0", |
| "10" |
| } |
| }; |
| |
| return CompleteResult.createResultSet(columnNames, dataTypes, data, connection.getContext(), 0); |
| } |
| |
| /** |
| * Retrieves a description of the given table's indices and statistics. They are ordered by |
| * NON_UNIQUE, TYPE, INDEX_NAME, and ORDINAL_POSITION. |
| * |
| * <p>Each index column description has the following columns: |
| * |
| * <ol> |
| * <li><B>TABLE_CAT</B> String {@code =>} table catalog (may be <code>null</code>) |
| * <li><B>TABLE_SCHEM</B> String {@code =>} table schema (may be <code>null</code>) |
| * <li><B>TABLE_NAME</B> String {@code =>} table name |
| * <li><B>NON_UNIQUE</B> boolean {@code =>} Can index values be non-unique. false when TYPE is |
| * tableIndexStatistic |
| * <li><B>INDEX_QUALIFIER</B> String {@code =>} index catalog (may be <code>null</code>); <code> |
| * null</code> when TYPE is tableIndexStatistic |
| * <li><B>INDEX_NAME</B> String {@code =>} index name; <code>null</code> when TYPE is |
| * tableIndexStatistic |
| * <li><B>TYPE</B> short {@code =>} index type: |
| * <ul> |
| * <li>tableIndexStatistic - this identifies table statistics that are returned in |
| * conjuction with a table's index descriptions |
| * <li>tableIndexClustered - this is a clustered index |
| * <li>tableIndexHashed - this is a hashed index |
| * <li>tableIndexOther - this is some other style of index |
| * </ul> |
| * <li><B>ORDINAL_POSITION</B> short {@code =>} column sequence number within index; zero when |
| * TYPE is tableIndexStatistic |
| * <li><B>COLUMN_NAME</B> String {@code =>} column name; <code>null</code> when TYPE is |
| * tableIndexStatistic |
| * <li><B>ASC_OR_DESC</B> String {@code =>} column sort sequence, "A" {@code =>} ascending, "D" |
| * {@code =>} descending, may be <code>null</code> if sort sequence is not supported; <code> |
| * null</code> when TYPE is tableIndexStatistic |
| * <li><B>CARDINALITY</B> long {@code =>} When TYPE is tableIndexStatistic, then this is the |
| * number of rows in the table; otherwise, it is the number of unique values in the index. |
| * <li><B>PAGES</B> long {@code =>} 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. |
| * <li><B>FILTER_CONDITION</B> String {@code =>} Filter condition, if any. (may be <code>null |
| * </code>) |
| * </ol> |
| * |
| * @param catalog a catalog name; must match the catalog name as it is stored in this database; "" |
| * retrieves those without a catalog; <code>null</code> means that the catalog name should not |
| * be used to narrow the search |
| * @param schema a schema name; must match the schema name as it is stored in this database; "" |
| * retrieves those without a schema; <code>null</code> means that the schema name should not |
| * be used to narrow the search |
| * @param table a table name; must match the table name as it is stored in this database |
| * @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 <code>ResultSet</code> - each row is an index column description |
| * @throws SQLException if a database access error occurs |
| */ |
| public ResultSet getIndexInfo( |
| String catalog, String schema, String table, boolean unique, boolean approximate) |
| throws SQLException { |
| if (table == null) { |
| throw new SQLException("'table' parameter must not be null"); |
| } |
| StringBuilder sb = |
| new StringBuilder( |
| "SELECT TABLE_SCHEMA TABLE_CAT, " |
| + "NULL TABLE_SCHEM, " |
| + "TABLE_NAME, " |
| + "NON_UNIQUE, " |
| + "TABLE_SCHEMA INDEX_QUALIFIER, " |
| + "INDEX_NAME, " |
| + tableIndexOther |
| + " TYPE, " |
| + "SEQ_IN_INDEX ORDINAL_POSITION, " |
| + "COLUMN_NAME, " |
| + "COLLATION ASC_OR_DESC, " |
| + "CARDINALITY, " |
| + "NULL PAGES, " |
| + "NULL FILTER_CONDITION" |
| + " FROM INFORMATION_SCHEMA.STATISTICS"); |
| |
| boolean firstCondition = catalogCond(true, sb, "TABLE_SCHEMA", catalog); |
| sb.append(firstCondition ? " WHERE " : " AND ") |
| .append("TABLE_NAME = ") |
| .append(escapeQuote(table)); |
| if (unique) sb.append(" AND NON_UNIQUE = 0"); |
| sb.append(" ORDER BY NON_UNIQUE, TYPE, INDEX_NAME, ORDINAL_POSITION"); |
| |
| return executeQuery(sb.toString()); |
| } |
| |
| /** |
| * Retrieves whether this database supports the given result set type. ResultSet.TYPE_FORWARD_ONLY |
| * and ResultSet.TYPE_SCROLL_INSENSITIVE are supported. |
| * |
| * @param type one of the following <code>ResultSet</code> constants: |
| * <ul> |
| * <li><code>ResultSet.TYPE_FORWARD_ONLY</code> |
| * <li><code>ResultSet.TYPE_SCROLL_INSENSITIVE</code> |
| * <li><code>ResultSet.TYPE_SCROLL_SENSITIVE</code> |
| * </ul> |
| * |
| * @return true if supported |
| */ |
| public boolean supportsResultSetType(int type) { |
| return (type == ResultSet.TYPE_SCROLL_INSENSITIVE || type == ResultSet.TYPE_FORWARD_ONLY); |
| } |
| |
| /** |
| * Retrieves whether this database supports the given concurrency type in combination with the |
| * given result set type. All are supported, but combination that use |
| * ResultSet.TYPE_SCROLL_INSENSITIVE. |
| * |
| * @param type one of the following <code>ResultSet</code> constants: |
| * <ul> |
| * <li><code>ResultSet.TYPE_FORWARD_ONLY</code> |
| * <li><code>ResultSet.TYPE_SCROLL_INSENSITIVE</code> |
| * <li><code>ResultSet.TYPE_SCROLL_SENSITIVE</code> |
| * </ul> |
| * |
| * @param concurrency one of the following <code>ResultSet</code> constants: |
| * <ul> |
| * <li><code>ResultSet.CONCUR_READ_ONLY</code> |
| * <li><code>ResultSet.CONCUR_UPDATABLE</code> |
| * </ul> |
| * |
| * @return true if supported |
| */ |
| public boolean supportsResultSetConcurrency(int type, int concurrency) { |
| // Support all concurrency (ResultSet.CONCUR_READ_ONLY and ResultSet.CONCUR_UPDATABLE) |
| // so just return scroll type |
| return type == ResultSet.TYPE_SCROLL_INSENSITIVE || type == ResultSet.TYPE_FORWARD_ONLY; |
| } |
| |
| public boolean ownUpdatesAreVisible(int type) { |
| return supportsResultSetType(type); |
| } |
| |
| public boolean ownDeletesAreVisible(int type) { |
| return supportsResultSetType(type); |
| } |
| |
| public boolean ownInsertsAreVisible(int type) { |
| return supportsResultSetType(type); |
| } |
| |
| public boolean othersUpdatesAreVisible(int type) { |
| return false; |
| } |
| |
| public boolean othersDeletesAreVisible(int type) { |
| return false; |
| } |
| |
| public boolean othersInsertsAreVisible(int type) { |
| return false; |
| } |
| |
| public boolean updatesAreDetected(int type) { |
| return false; |
| } |
| |
| public boolean deletesAreDetected(int type) { |
| return false; |
| } |
| |
| public boolean insertsAreDetected(int type) { |
| return false; |
| } |
| |
| public boolean supportsBatchUpdates() { |
| return true; |
| } |
| |
| /** |
| * Retrieves a description of the user-defined types (UDTs) defined in a particular schema. |
| * Schema-specific UDTs may have type <code>JAVA_OBJECT</code>, <code>STRUCT</code>, or <code> |
| * DISTINCT</code>. |
| * |
| * <p>Only types matching the catalog, schema, type name and type criteria are returned. They are |
| * ordered by <code>DATA_TYPE</code>, <code>TYPE_CAT</code>, <code>TYPE_SCHEM</code> and <code> |
| * TYPE_NAME</code>. The type name parameter may be a fully-qualified name. In this case, the |
| * catalog and schemaPattern parameters are ignored. |
| * |
| * <p>Each type description has the following columns: |
| * |
| * <ol> |
| * <li><B>TYPE_CAT</B> String {@code =>} the type's catalog (may be <code>null</code>) |
| * <li><B>TYPE_SCHEM</B> String {@code =>} type's schema (may be <code>null</code>) |
| * <li><B>TYPE_NAME</B> String {@code =>} type name |
| * <li><B>CLASS_NAME</B> String {@code =>} Java class name |
| * <li><B>DATA_TYPE</B> int {@code =>} type value defined in java.sql.Types. One of JAVA_OBJECT, |
| * STRUCT, or DISTINCT |
| * <li><B>REMARKS</B> String {@code =>} explanatory comment on the type |
| * <li><B>BASE_TYPE</B> short {@code =>} type code of the source type of a DISTINCT type or the |
| * type that implements the user-generated reference type of the SELF_REFERENCING_COLUMN of |
| * a structured type as defined in java.sql.Types (<code>null</code> if DATA_TYPE is not |
| * DISTINCT or not STRUCT with REFERENCE_GENERATION = USER_DEFINED) |
| * </ol> |
| * |
| * <p><B>Note:</B> If the driver does not support UDTs, an empty result set is returned. |
| * |
| * @param catalog a catalog name; must match the catalog name as it is stored in the database; "" |
| * retrieves those without a catalog; <code>null</code> means that the catalog name should not |
| * be used to narrow the search |
| * @param schemaPattern a schema pattern name; must match the schema name as it is stored in the |
| * database; "" retrieves those without a schema; <code>null</code> means that the schema name |
| * should not be used to narrow the search |
| * @param typeNamePattern a type name pattern; must match the type name as it is stored in the |
| * database; may be a fully qualified name |
| * @param types a list of user-defined types (JAVA_OBJECT, STRUCT, or DISTINCT) to include; <code> |
| * null</code> returns all types |
| * @return <code>ResultSet</code> object in which each row describes a UDT |
| * @throws SQLException if a database access error occurs |
| * @see #getSearchStringEscape |
| * @since 1.2 |
| */ |
| @Override |
| public ResultSet getUDTs( |
| String catalog, String schemaPattern, String typeNamePattern, int[] types) |
| throws SQLException { |
| String sql = |
| "SELECT ' ' TYPE_CAT, NULL TYPE_SCHEM, ' ' TYPE_NAME, ' ' CLASS_NAME, 0 DATA_TYPE, ' '" |
| + " REMARKS, 0 BASE_TYPE FROM DUAL WHERE 1=0"; |
| |
| return executeQuery(sql); |
| } |
| |
| public org.mariadb.jdbc.Connection getConnection() { |
| return connection; |
| } |
| |
| public boolean supportsSavepoints() { |
| return true; |
| } |
| |
| public boolean supportsNamedParameters() { |
| return false; |
| } |
| |
| public boolean supportsMultipleOpenResults() { |
| return false; |
| } |
| |
| public boolean supportsGetGeneratedKeys() { |
| return true; |
| } |
| |
| /** |
| * Retrieves a description of the user-defined type (UDT) hierarchies defined in a particular |
| * schema in this database. Only the immediate super type/ subtype relationship is modeled. Only |
| * supertype information for UDTs matching the catalog, schema, and type name is returned. The |
| * type name parameter may be a fully-qualified name. When the UDT name supplied is a |
| * fully-qualified name, the catalog and schemaPattern parameters are ignored. If a UDT does not |
| * have a direct super type, it is not listed here. A row of the <code>ResultSet</code> object |
| * returned by this method describes the designated UDT and a direct supertype. A row has the |
| * following columns: |
| * |
| * <OL> |
| * <li><B>TYPE_CAT</B> String {@code =>} the UDT's catalog (may be <code>null</code>) |
| * <li><B>TYPE_SCHEM</B> String {@code =>} UDT's schema (may be <code>null</code>) |
| * <li><B>TYPE_NAME</B> String {@code =>} type name of the UDT |
| * <li><B>SUPERTYPE_CAT</B> String {@code =>} the direct super type's catalog (may be <code>null |
| * </code>) |
| * <li><B>SUPERTYPE_SCHEM</B> String {@code =>} the direct super type's schema (may be <code> |
| * null</code>) |
| * <li><B>SUPERTYPE_NAME</B> String {@code =>} the direct super type's name |
| * </OL> |
| * |
| * <p><B>Note:</B> If the driver does not support type hierarchies, an empty result set is |
| * returned. |
| * |
| * @param catalog a catalog name; "" retrieves those without a catalog; <code>null</code> means |
| * drop catalog name from the selection criteria |
| * @param schemaPattern a schema name pattern; "" retrieves those without a schema |
| * @param typeNamePattern a UDT name pattern; may be a fully-qualified name |
| * @return a <code>ResultSet</code> object in which a row gives information about the designated |
| * UDT |
| * @throws SQLException if a database access error occurs |
| * @see #getSearchStringEscape |
| * @since 1.4 |
| */ |
| public ResultSet getSuperTypes(String catalog, String schemaPattern, String typeNamePattern) |
| throws SQLException { |
| String sql = |
| "SELECT ' ' TYPE_CAT, NULL TYPE_SCHEM, ' ' TYPE_NAME, ' ' SUPERTYPE_CAT, ' '" |
| + " SUPERTYPE_SCHEM, ' ' SUPERTYPE_NAME FROM DUAL WHERE 1=0"; |
| |
| return executeQuery(sql); |
| } |
| |
| /** |
| * Retrieves a description of the table hierarchies defined in a particular schema in this |
| * database. |
| * |
| * <p>Only supertable information for tables matching the catalog, schema and table name are |
| * returned. The table name parameter may be a fully-qualified name, in which case, the catalog |
| * and schemaPattern parameters are ignored. If a table does not have a super table, it is not |
| * listed here. Supertables have to be defined in the same catalog and schema as the sub tables. |
| * Therefore, the type description does not need to include this information for the supertable. |
| * |
| * <p>Each type description has the following columns: |
| * |
| * <OL> |
| * <li><B>TABLE_CAT</B> String {@code =>} the type's catalog (may be <code>null</code>) |
| * <li><B>TABLE_SCHEM</B> String {@code =>} type's schema (may be <code>null</code>) |
| * <li><B>TABLE_NAME</B> String {@code =>} type name |
| * <li><B>SUPERTABLE_NAME</B> String {@code =>} the direct super type's name |
| * </OL> |
| * |
| * <p><B>Note:</B> If the driver does not support type hierarchies, an empty result set is |
| * returned. |
| * |
| * @param catalog a catalog name; "" retrieves those without a catalog; <code>null</code> means |
| * drop catalog name from the selection criteria |
| * @param schemaPattern a schema name pattern; "" retrieves those without a schema |
| * @param tableNamePattern a table name pattern; may be a fully-qualified name |
| * @return a <code>ResultSet</code> object in which each row is a type description |
| * @throws SQLException if a database access error occurs |
| * @see #getSearchStringEscape |
| * @since 1.4 |
| */ |
| public ResultSet getSuperTables(String catalog, String schemaPattern, String tableNamePattern) |
| throws SQLException { |
| String sql = |
| "SELECT ' ' TABLE_CAT, ' ' TABLE_SCHEM, ' ' TABLE_NAME, ' ' SUPERTABLE_NAME FROM DUAL" |
| + " WHERE 1=0"; |
| return executeQuery(sql); |
| } |
| |
| /** |
| * Retrieves a description of the given attribute of the given type for a user-defined type (UDT) |
| * that is available in the given schema and catalog. Descriptions are returned only for |
| * attributes of UDTs matching the catalog, schema, type, and attribute name criteria. They are |
| * ordered by <code>TYPE_CAT</code>, <code>TYPE_SCHEM</code>, <code>TYPE_NAME</code> and <code> |
| * ORDINAL_POSITION</code>. This description does not contain inherited attributes. The <code> |
| * ResultSet</code> object that is returned has the following columns: |
| * |
| * <OL> |
| * <li><B>TYPE_CAT</B> String {@code =>} type catalog (may be <code>null</code>) |
| * <li><B>TYPE_SCHEM</B> String {@code =>} type schema (may be <code>null</code>) |
| * <li><B>TYPE_NAME</B> String {@code =>} type name |
| * <li><B>ATTR_NAME</B> String {@code =>} attribute name |
| * <li><B>DATA_TYPE</B> int {@code =>} attribute type SQL type from java.sql.Types |
| * <li><B>ATTR_TYPE_NAME</B> String {@code =>} Data source dependent type name. For a UDT, the |
| * type name is fully qualified. For a REF, the type name is fully qualified and represents |
| * the target type of the reference type. |
| * <li><B>ATTR_SIZE</B> int {@code =>} column size. For char or date types this is the maximum |
| * number of characters; for numeric or decimal types this is precision. |
| * <li><B>DECIMAL_DIGITS</B> int {@code =>} the number of fractional digits. Null is returned |
| * for data types where DECIMAL_DIGITS is not applicable. |
| * <li><B>NUM_PREC_RADIX</B> int {@code =>} Radix (typically either 10 or 2) |
| * <li><B>NULLABLE</B> int {@code =>} whether NULL is allowed |
| * <UL> |
| * <li>attributeNoNulls - might not allow NULL values |
| * <li>attributeNullable - definitely allows NULL values |
| * <li>attributeNullableUnknown - nullability unknown |
| * </UL> |
| * <li><B>REMARKS</B> String {@code =>} comment describing column (may be <code>null</code>) |
| * <li><B>ATTR_DEF</B> String {@code =>} default value (may be<code>null</code>) |
| * <li><B>SQL_DATA_TYPE</B> int {@code =>} unused |
| * <li><B>SQL_DATETIME_SUB</B> int {@code =>} unused |
| * <li><B>CHAR_OCTET_LENGTH</B> int {@code =>} for char types the maximum number of bytes in the |
| * column |
| * <li><B>ORDINAL_POSITION</B> int {@code =>} index of the attribute in the UDT (starting at 1) |
| * <li><B>IS_NULLABLE</B> String {@code =>} ISO rules are used to determine the nullability for |
| * a attribute. |
| * <UL> |
| * <li>YES --- if the attribute can include NULLs |
| * <li>NO --- if the attribute cannot include NULLs |
| * <li>empty string --- if the nullability for the attribute is unknown |
| * </UL> |
| * <li><B>SCOPE_CATALOG</B> String {@code =>} catalog of table that is the scope of a reference |
| * attribute (<code>null</code> if DATA_TYPE isn't REF) |
| * <li><B>SCOPE_SCHEMA</B> String {@code =>} schema of table that is the scope of a reference |
| * attribute (<code>null</code> if DATA_TYPE isn't REF) |
| * <li><B>SCOPE_TABLE</B> String {@code =>} table name that is the scope of a reference |
| * attribute (<code>null</code> if the DATA_TYPE isn't REF) |
| * <li><B>SOURCE_DATA_TYPE</B> short {@code =>} source type of a distinct type or user-generated |
| * Ref type,SQL type from java.sql.Types (<code>null</code> if DATA_TYPE isn't DISTINCT or |
| * user-generated REF) |
| * </OL> |
| * |
| * @param catalog a catalog name; must match the catalog name as it is stored in the database; "" |
| * retrieves those without a catalog; <code>null</code> means that the catalog name should not |
| * be used to narrow the search |
| * @param schemaPattern a schema name pattern; must match the schema name as it is stored in the |
| * database; "" retrieves those without a schema; <code>null</code> means that the schema name |
| * should not be used to narrow the search |
| * @param typeNamePattern a type name pattern; must match the type name as it is stored in the |
| * database |
| * @param attributeNamePattern an attribute name pattern; must match the attribute name as it is |
| * declared in the database |
| * @return a <code>ResultSet</code> object in which each row is an attribute description |
| * @throws SQLException if a database access error occurs |
| * @see #getSearchStringEscape |
| * @since 1.4 |
| */ |
| public ResultSet getAttributes( |
| String catalog, String schemaPattern, String typeNamePattern, String attributeNamePattern) |
| throws SQLException { |
| |
| String sql = |
| "SELECT ' ' TYPE_CAT, ' ' TYPE_SCHEM, ' ' TYPE_NAME, ' ' ATTR_NAME, 0 DATA_TYPE, ' '" |
| + " ATTR_TYPE_NAME, 0 ATTR_SIZE, 0 DECIMAL_DIGITS, 0 NUM_PREC_RADIX, 0 NULLABLE, ' '" |
| + " REMARKS, ' ' ATTR_DEF, 0 SQL_DATA_TYPE, 0 SQL_DATETIME_SUB, 0 CHAR_OCTET_LENGTH, 0" |
| + " ORDINAL_POSITION, ' ' IS_NULLABLE, ' ' SCOPE_CATALOG, ' ' SCOPE_SCHEMA, ' '" |
| + " SCOPE_TABLE, 0 SOURCE_DATA_TYPE FROM DUAL WHERE 1=0"; |
| |
| return executeQuery(sql); |
| } |
| |
| public boolean supportsResultSetHoldability(int holdability) { |
| return holdability == ResultSet.HOLD_CURSORS_OVER_COMMIT; |
| } |
| |
| public int getResultSetHoldability() { |
| return ResultSet.HOLD_CURSORS_OVER_COMMIT; |
| } |
| |
| public int getDatabaseMajorVersion() { |
| return connection.getContext().getVersion().getMajorVersion(); |
| } |
| |
| public int getDatabaseMinorVersion() { |
| return connection.getContext().getVersion().getMinorVersion(); |
| } |
| |
| @Override |
| public int getJDBCMajorVersion() { |
| return 4; |
| } |
| |
| @Override |
| public int getJDBCMinorVersion() { |
| return 2; |
| } |
| |
| @Override |
| public int getSQLStateType() { |
| return sqlStateSQL99; |
| } |
| |
| public boolean locatorsUpdateCopy() { |
| return false; |
| } |
| |
| public boolean supportsStatementPooling() { |
| return false; |
| } |
| |
| public RowIdLifetime getRowIdLifetime() { |
| return RowIdLifetime.ROWID_UNSUPPORTED; |
| } |
| |
| public boolean supportsStoredFunctionsUsingCallSyntax() { |
| return true; |
| } |
| |
| public boolean autoCommitFailureClosesAllResultSets() { |
| return false; |
| } |
| |
| /** |
| * Retrieves a list of the client info properties that the driver supports. The result set |
| * contains the following columns |
| * |
| * <ol> |
| * <li>NAME String : The name of the client info property |
| * <li>MAX_LEN int : The maximum length of the value for the property |
| * <li>DEFAULT_VALUE String : The default value of the property |
| * <li>DESCRIPTION String : A description of the property. This will typically contain |
| * information as to where this property is stored in the database. |
| * </ol> |
| * |
| * <p>The ResultSet is sorted by the NAME column |
| * |
| * @return A ResultSet object; each row is a supported client info property |
| */ |
| public ResultSet getClientInfoProperties() { |
| String[] columnNames = new String[] {"NAME", "MAX_LEN", "DEFAULT_VALUE", "DESCRIPTION"}; |
| |
| DataType[] types = |
| new DataType[] { |
| DataType.VARSTRING, DataType.INTEGER, DataType.VARSTRING, DataType.VARSTRING |
| }; |
| String[][] data = |
| new String[][] { |
| new String[] { |
| "ApplicationName", |
| "16777215", |
| "", |
| "The name of the application currently utilizing the connection" |
| }, |
| new String[] { |
| "ClientUser", |
| "16777215", |
| "", |
| "The name of the user that the application using the connection is performing work for." |
| + " This may not be the same as the user name that was used in establishing the" |
| + " connection." |
| }, |
| new String[] { |
| "ClientHostname", |
| "16777215", |
| "", |
| "The hostname of the computer the application using the connection is running on" |
| } |
| }; |
| |
| return CompleteResult.createResultSet(columnNames, types, data, connection.getContext(), 0); |
| } |
| |
| /** |
| * Retrieves a description of the system and user functions available in the given catalog. Only |
| * system and user function descriptions matching the schema and function name criteria are |
| * returned. They are ordered by <code>FUNCTION_CAT</code>, <code>FUNCTION_SCHEM</code>, <code> |
| * FUNCTION_NAME</code> and <code>SPECIFIC_ NAME</code>. |
| * |
| * <p>Each function description has the following columns: |
| * |
| * <OL> |
| * <li><B>FUNCTION_CAT</B> String {@code =>} function catalog (may be <code>null</code>) |
| * <li><B>FUNCTION_SCHEM</B> String {@code =>} function schema (may be <code>null</code>) |
| * <li><B>FUNCTION_NAME</B> String {@code =>} function name. This is the name used to invoke the |
| * function |
| * <li><B>REMARKS</B> String {@code =>} explanatory comment on the function |
| * <li><B>FUNCTION_TYPE</B> short {@code =>} kind of function: |
| * <UL> |
| * <li>functionResultUnknown - Cannot determine if a return value or table will be |
| * returned |
| * <li>functionNoTable- Does not return a table |
| * <li>functionReturnsTable - Returns a table |
| * </UL> |
| * <li><B>SPECIFIC_NAME</B> String {@code =>} the name which uniquely identifies this function |
| * within its schema. This is a user specified, or DBMS generated, name that may be |
| * different then the <code>FUNCTION_NAME</code> for example with overload functions |
| * </OL> |
| * |
| * <p>A user may not have permission to execute any of the functions that are returned by <code> |
| * getFunctions</code> |
| * |
| * @param catalog a catalog name; must match the catalog name as it is stored in the database; "" |
| * retrieves those without a catalog; <code>null</code> means that the catalog name should not |
| * be used to narrow the search |
| * @param schemaPattern a schema name pattern; must match the schema name as it is stored in the |
| * database; "" retrieves those without a schema; <code>null</code> means that the schema name |
| * should not be used to narrow the search |
| * @param functionNamePattern a function name pattern; must match the function name as it is |
| * stored in the database |
| * @return <code>ResultSet</code> - each row is a function description |
| * @throws SQLException if a database access error occurs |
| * @see #getSearchStringEscape |
| * @since 1.6 |
| */ |
| public ResultSet getFunctions(String catalog, String schemaPattern, String functionNamePattern) |
| throws SQLException { |
| StringBuilder sb = |
| new StringBuilder( |
| "SELECT ROUTINE_SCHEMA FUNCTION_CAT," |
| + "NULL FUNCTION_SCHEM, " |
| + "ROUTINE_NAME FUNCTION_NAME, " |
| + "ROUTINE_COMMENT REMARKS, " |
| + functionNoTable |
| + " FUNCTION_TYPE, " |
| + "SPECIFIC_NAME " |
| + " FROM INFORMATION_SCHEMA.ROUTINES"); |
| boolean firstCondition = catalogCond(true, sb, "ROUTINE_SCHEMA", catalog); |
| firstCondition = patternCond(firstCondition, sb, "ROUTINE_NAME", functionNamePattern); |
| sb.append(firstCondition ? " WHERE " : " AND ").append(" ROUTINE_TYPE='FUNCTION'"); |
| |
| return executeQuery(sb.toString()); |
| } |
| |
| @Override |
| public <T> T unwrap(Class<T> iface) throws SQLException { |
| if (isWrapperFor(iface)) { |
| return iface.cast(this); |
| } |
| throw new SQLException("The receiver is not a wrapper for " + iface.getName()); |
| } |
| |
| @Override |
| public boolean isWrapperFor(Class<?> iface) { |
| return iface.isInstance(this); |
| } |
| |
| @Override |
| public long getMaxLogicalLobSize() { |
| return 4294967295L; |
| } |
| |
| private static class Identifier { |
| public String schema; |
| public String name; |
| } |
| } |