All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.mariadb.jdbc.DatabaseMetaData Maven / Gradle / Ivy

The newest version!
// SPDX-License-Identifier: LGPL-2.1-or-later
// Copyright (c) 2012-2014 Monty Program Ab
// Copyright (c) 2015-2024 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.util.VersionFactory;
import org.mariadb.jdbc.util.constants.CatalogTerm;
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 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);
      }
    }
  }

  private void parseShowCreateTable(
      String tableDef,
      String database,
      String tableName,
      boolean importedKeysWithConstraintNames,
      List data)
      throws ParseException, SQLException {
    String[] parts = tableDef.split("\n");
    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 foreignKeyCols = new ArrayList<>();
      pos = parseIdentifierList(partChar, pos, foreignKeyCols);
      pos = skipKeyword(partChar, pos, "REFERENCES");
      Identifier pkTable = new Identifier();
      pos = parseIdentifier(partChar, pos, pkTable);
      List 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> externalInfos = new HashMap<>();
      for (int i = 0; i < primaryKeyCols.size(); i++) {

        String[] row = new String[14];
        row[0] =
            conf.useCatalogTerm() == CatalogTerm.UseCatalog
                ? (pkTable.schema == null ? database : pkTable.schema)
                : "def"; // PKTABLE_CAT
        row[1] =
            conf.useCatalogTerm() == CatalogTerm.UseSchema
                ? (pkTable.schema == null ? database : pkTable.schema)
                : null; // PKTABLE_SCHEM
        row[2] = pkTable.name; // PKTABLE_NAME
        row[3] = primaryKeyCols.get(i).name; // PKCOLUMN_NAME
        row[4] = conf.useCatalogTerm() == CatalogTerm.UseCatalog ? database : "def"; // FKTABLE_CAT
        row[5] = conf.useCatalogTerm() == CatalogTerm.UseSchema ? database : 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 =
              quoteIdentifier(pkTable.schema == null ? database : pkTable.schema)
                  + "."
                  + quoteIdentifier(pkTable.name);
          if (!externalInfos.containsKey(ext)) {
            externalInfos.put(ext, getExtImportedKeys(ext, connection));
          }
          row[12] = null; // PK_NAME
          Map externalInfo = externalInfos.get(ext);
          if (externalInfo != null) {
            for (Map.Entry entry : externalInfo.entrySet()) {
              boolean foundAll = true;
              List pkKeysFound = new ArrayList<>();

              for (String keyPart : entry.getKey()) {
                boolean foundKey = false;
                for (Identifier keyCol : primaryKeyCols) {
                  if (keyCol.name.equals(keyPart)) {
                    foundKey = true;
                    pkKeysFound.add(keyCol);
                    break;
                  }
                }
                if (!foundKey) {
                  foundAll = false;
                  break;
                }
              }
              // either found all the keys, or found at least all the one required
              if (foundAll || pkKeysFound.size() == primaryKeyCols.size()) {
                row[12] = entry.getValue();
              }
            }
          }
        } else {
          row[12] = null; // PK_NAME
        }
        row[13] = Integer.toString(DatabaseMetaData.importedKeyNotDeferrable); // DEFERRABILITY
        data.add(row);
      }
    }
  }

  private Map 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 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")
          || part.startsWith("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 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.
   *
   * 

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

    *
  1. PKTABLE_CAT String {@code =>} primary key table catalog being imported (may be * null) *
  2. PKTABLE_SCHEM String {@code =>} primary key table schema being imported (may be * null) *
  3. PKTABLE_NAME String {@code =>} primary key table name being imported *
  4. PKCOLUMN_NAME String {@code =>} primary key column name being imported *
  5. FKTABLE_CAT String {@code =>} foreign key table catalog (may be null) *
  6. FKTABLE_SCHEM String {@code =>} foreign key table schema (may be null * ) *
  7. FKTABLE_NAME String {@code =>} foreign key table name *
  8. FKCOLUMN_NAME String {@code =>} foreign key column name *
  9. KEY_SEQ 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). *
  10. UPDATE_RULE short {@code =>} What happens to a foreign key when the primary key is * updated: *
      *
    • importedNoAction - do not allow update of primary key if it has been imported *
    • importedKeyCascade - change imported key to agree with primary key update *
    • importedKeySetNull - change imported key to NULL if its primary key * has been updated *
    • importedKeySetDefault - change imported key to default values if its primary key * has been updated *
    • importedKeyRestrict - same as importedKeyNoAction (for ODBC 2.x compatibility) *
    *
  11. DELETE_RULE short {@code =>} What happens to the foreign key when primary is * deleted. *
      *
    • importedKeyNoAction - do not allow delete of primary key if it has been imported *
    • importedKeyCascade - delete rows that import a deleted key *
    • importedKeySetNull - change imported key to NULL if its primary key has been * deleted *
    • importedKeyRestrict - same as importedKeyNoAction (for ODBC 2.x compatibility) *
    • importedKeySetDefault - change imported key to default if its primary key has been * deleted *
    *
  12. FK_NAME String {@code =>} foreign key name (may be null) *
  13. PK_NAME String {@code =>} primary key name (may be null) *
  14. DEFERRABILITY short {@code =>} can the evaluation of foreign key constraints be * deferred until commit *
      *
    • importedKeyInitiallyDeferred - see SQL92 for definition *
    • importedKeyInitiallyImmediate - see SQL92 for definition *
    • importedKeyNotDeferrable - see SQL92 for definition *
    *
* * @param catalog a catalog name; must match the catalog name as it is stored in the database; "" * retrieves those without a catalog; null 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; null 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 ResultSet - 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 || table.isEmpty()) { throw new SQLException("'table' parameter in getImportedKeys cannot be null"); } String database = conf.useCatalogTerm() == CatalogTerm.UseCatalog ? catalog : schema; boolean getImportedKeysUsingIs = Boolean.parseBoolean( conf.nonMappedOptions().getProperty("getImportedKeysUsingIs", "false")); if (getImportedKeysUsingIs) return getImportedKeysUsingInformationSchema(database, table); try { return getImportedKeysUsingShowCreateTable(database, table); } catch (Exception e) { // since 3.4.1 show create is now used by default, and there is no reason to have parsing // exception, but cannot remove that try-catch in a correction release. // TODO to be removed next minor version return getImportedKeysUsingInformationSchema(database, 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(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); // statement is required when streaming, but hibernate rely on statement tracking. // so in case of defaultFetchSize set, force metadata to load result-set stmt.setFetchSize(0); CompleteResult rs = (CompleteResult) stmt.executeQuery(sql); CompleteResult newRes = rs.newResultsetWithUseAliasAsName(); newRes.setStatement(null); // bypass Hibernate statement tracking (CONJ-49) return newRes; } 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 firstCondition is the first condition ( must be a 'WHERE' or an 'AND') * @param columnName - column name in the information schema table * @param database - database name. This driver does not (always) follow JDBC standard for * following special values, due to ConnectorJ compatibility 1. empty string ("") - matches * current database. JDBC standard says only tables without database should be returned - such * tables do not exist in MariaDB. If there is no current database, then empty string matches * any database. 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 database with null parameter. This can be switched with * nullCatalogMeansCurrent=false in the connection URL. * @param usePattern can use pattern, or must it be strictly equal * @return part of SQL query ,that restricts search for the database. */ private boolean databaseCond( boolean firstCondition, StringBuilder sb, String columnName, String database, boolean usePattern) { // null database => searching without any database restriction if ((database == null && !conf.nullDatabaseMeansCurrent()) || ("%".equals(database) && usePattern)) return firstCondition; // empty database => search restricting to current database if ((database == null && conf.nullDatabaseMeansCurrent()) || database.isEmpty()) { sb.append(firstCondition ? " WHERE " : " AND ").append(columnName).append(" = database()"); return false; } // search with specified database sb.append(firstCondition ? " WHERE " : " AND ") .append(columnName) .append( !usePattern || (database.indexOf('%') == -1 && database.indexOf('_') == -1) ? "=" : " LIKE ") .append(escapeQuote(database)); 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. * *

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

    *
  1. TABLE_CAT String {@code =>} table catalog *
  2. TABLE_SCHEM String {@code =>} table schema (may be null) *
  3. TABLE_NAME String {@code =>} table name *
  4. COLUMN_NAME String {@code =>} column name *
  5. KEY_SEQ 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). *
  6. PK_NAME String {@code =>} primary key name *
* * @param catalog a catalog name; must match the catalog name as it is stored in the database; "" * retrieves those without a catalog; null 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; null 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 ResultSet - 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 { if (table == null || table.isEmpty()) { throw new SQLException("'table' parameter is mandatory in getPrimaryKeys()"); } StringBuilder sb = new StringBuilder("SELECT ") .append( conf.useCatalogTerm() == CatalogTerm.UseCatalog ? "TABLE_SCHEMA TABLE_CAT, NULL TABLE_SCHEM" : "TABLE_CATALOG TABLE_CAT, TABLE_SCHEMA TABLE_SCHEM") .append( ", TABLE_NAME, COLUMN_NAME, SEQ_IN_INDEX KEY_SEQ, INDEX_NAME PK_NAME " + " FROM INFORMATION_SCHEMA.STATISTICS" + " WHERE INDEX_NAME='PRIMARY'"); String database = conf.useCatalogTerm() == CatalogTerm.UseCatalog ? catalog : schema; databaseCond(false, sb, "TABLE_SCHEMA", database, false); sb.append(" AND TABLE_NAME = ").append(escapeQuote(table)).append(" ORDER BY 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 * TABLE_TYPE, TABLE_CAT, TABLE_SCHEM and TABLE_NAME * . Each table description has the following columns: * *
    *
  1. TABLE_CAT String {@code =>} table catalog (may be null) *
  2. TABLE_SCHEM String {@code =>} table schema (may be null) *
  3. TABLE_NAME String {@code =>} table name *
  4. TABLE_TYPE String {@code =>} table type. Typical types are "TABLE", "VIEW", * "SYSTEM TABLE", "GLOBAL TEMPORARY", "LOCAL TEMPORARY", "ALIAS", "SYNONYM". *
  5. REMARKS String {@code =>} explanatory comment on the table *
  6. TYPE_CAT String {@code =>} the types catalog (may be null) *
  7. TYPE_SCHEM String {@code =>} the types schema (may be null) *
  8. TYPE_NAME String {@code =>} type name (may be null) *
  9. SELF_REFERENCING_COL_NAME String {@code =>} name of the designated "identifier" * column of a typed table (may be null) *
  10. REF_GENERATION String {@code =>} specifies how values in SELF_REFERENCING_COL_NAME * are created. Values are "SYSTEM", "USER", "DERIVED". (may be null) *
* *

Note: 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; null 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; null 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; null returns all types * @return ResultSet - 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 " + (conf.useCatalogTerm() == CatalogTerm.UseCatalog ? "TABLE_SCHEMA TABLE_CAT, NULL TABLE_SCHEM," : "TABLE_CATALOG TABLE_CAT, TABLE_SCHEMA TABLE_SCHEM,") + " TABLE_NAME, IF(TABLE_SCHEMA IN ('mysql', 'performance_schema', 'sys')," + " IF(TABLE_TYPE='BASE TABLE', 'SYSTEM TABLE', IF(TABLE_TYPE='VIEW', 'SYSTEM" + " VIEW', TABLE_TYPE)), IF(TABLE_TYPE='BASE TABLE' or TABLE_TYPE='SYSTEM" + " VERSIONED', 'TABLE', IF(TABLE_TYPE='TEMPORARY', 'LOCAL TEMPORARY'," + " 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"); String database = conf.useCatalogTerm() == CatalogTerm.UseCatalog ? catalog : schemaPattern; boolean firstCondition = databaseCond( true, sb, "TABLE_SCHEMA", database, conf.useCatalogTerm() == CatalogTerm.UseSchema); 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 (s == null) { continue; } if (mustAddType) sqlType.append(","); mustAddType = true; switch (s) { case "TABLE": sqlType.append("'BASE TABLE','SYSTEM VERSIONED'"); break; case "LOCAL TEMPORARY": sqlType.append("'TEMPORARY'"); break; default: sqlType.append(escapeQuote(s)); break; } } sqlType.append(")"); if (mustAddType) sb.append(sqlType); } sb.append(" ORDER BY TABLE_TYPE,TABLE_CAT,TABLE_SCHEMA,TABLE_NAME"); return executeQuery(sb.toString()); } /** * Retrieves a description of table columns available in the specified catalog. * *

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

Each column description has the following columns: * *

    *
  1. TABLE_CAT String {@code =>} table catalog (may be null) *
  2. TABLE_SCHEM String {@code =>} table schema (may be null) *
  3. TABLE_NAME String {@code =>} table name *
  4. COLUMN_NAME String {@code =>} column name *
  5. DATA_TYPE int {@code =>} SQL type from java.sql.Types *
  6. TYPE_NAME String {@code =>} Data source dependent type name, for a UDT the type * name is fully qualified *
  7. COLUMN_SIZE int {@code =>} column size. *
  8. BUFFER_LENGTH is not used. *
  9. DECIMAL_DIGITS int {@code =>} the number of fractional digits. Null is returned * for data types where DECIMAL_DIGITS is not applicable. *
  10. NUM_PREC_RADIX int {@code =>} Radix (typically either 10 or 2) *
  11. NULLABLE int {@code =>} is NULL allowed. *
      *
    • columnNoNulls - might not allow NULL values *
    • columnNullable - definitely allows NULL values *
    • columnNullableUnknown - nullability unknown *
    *
  12. REMARKS String {@code =>} comment describing column (may be null) *
  13. COLUMN_DEF String {@code =>} default value for the column, which should be * interpreted as a string when the value is enclosed in single quotes (may be null * ) *
  14. SQL_DATA_TYPE int {@code =>} unused *
  15. SQL_DATETIME_SUB int {@code =>} unused *
  16. CHAR_OCTET_LENGTH int {@code =>} for char types the maximum number of bytes in the * column *
  17. ORDINAL_POSITION int {@code =>} index of column in table (starting at 1) *
  18. IS_NULLABLE String {@code =>} ISO rules are used to determine the nullability for * a column. *
      *
    • YES --- if the column can include NULLs *
    • NO --- if the column cannot include NULLs *
    • empty string --- if the nullability for the column is unknown *
    *
  19. SCOPE_CATALOG String {@code =>} catalog of table that is the scope of a reference * attribute (null if DATA_TYPE isn't REF) *
  20. SCOPE_SCHEMA String {@code =>} schema of table that is the scope of a reference * attribute (null if the DATA_TYPE isn't REF) *
  21. SCOPE_TABLE String {@code =>} table name that this the scope of a reference * attribute (null if the DATA_TYPE isn't REF) *
  22. SOURCE_DATA_TYPE short {@code =>} source type of a distinct type or user-generated * Ref type, SQL type from java.sql.Types (null if DATA_TYPE isn't DISTINCT or * user-generated REF) *
  23. IS_AUTOINCREMENT String {@code =>} Indicates whether this column is auto * incremented *
      *
    • YES --- if the column is auto incremented *
    • NO --- if the column is not auto incremented *
    • empty string --- if it cannot be determined whether the column is auto incremented *
    *
  24. IS_GENERATEDCOLUMN String {@code =>} Indicates whether this is a generated column *
      *
    • YES --- if this a generated column *
    • NO --- if this not a generated column *
    • empty string --- if it cannot be determined whether this is a generated column *
    *
* *

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; null 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; null 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 ResultSet - 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 ") .append( conf.useCatalogTerm() == CatalogTerm.UseCatalog ? "TABLE_SCHEMA TABLE_CAT, NULL TABLE_SCHEM" : "TABLE_CATALOG TABLE_CAT, TABLE_SCHEMA TABLE_SCHEM") .append(", TABLE_NAME, COLUMN_NAME,") .append(dataTypeClause("COLUMN_TYPE")) .append(" DATA_TYPE,") .append(DataTypeClause(conf)) .append(" 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")) .append( " 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 ") .append((conf.yearIsDateType() ? "NUMERIC_SCALE" : "0")) .append(" WHEN 'tinyint' THEN ") .append(conf.tinyInt1isBit() ? "0" : "NUMERIC_SCALE") .append( " 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"); String database = conf.useCatalogTerm() == CatalogTerm.UseCatalog ? catalog : schemaPattern; boolean firstCondition = databaseCond( true, sb, "TABLE_SCHEMA", database, conf.useCatalogTerm() == CatalogTerm.UseSchema); firstCondition = patternCond(firstCondition, sb, "TABLE_NAME", tableNamePattern); 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. * *

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

    *
  1. PKTABLE_CAT String {@code =>} primary key table catalog (may be null) *
  2. PKTABLE_SCHEM String {@code =>} primary key table schema (may be null * ) *
  3. PKTABLE_NAME String {@code =>} primary key table name *
  4. PKCOLUMN_NAME String {@code =>} primary key column name *
  5. FKTABLE_CAT String {@code =>} foreign key table catalog (may be null) * being exported (may be null) *
  6. FKTABLE_SCHEM String {@code =>} foreign key table schema (may be null * ) being exported (may be null) *
  7. FKTABLE_NAME String {@code =>} foreign key table name being exported *
  8. FKCOLUMN_NAME String {@code =>} foreign key column name being exported *
  9. KEY_SEQ 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). *
  10. UPDATE_RULE short {@code =>} What happens to foreign key when primary is updated: *
      *
    • importedNoAction - do not allow update of primary key if it has been imported *
    • importedKeyCascade - change imported key to agree with primary key update *
    • importedKeySetNull - change imported key to NULL if its primary key * has been updated *
    • importedKeySetDefault - change imported key to default values if its primary key * has been updated *
    • importedKeyRestrict - same as importedKeyNoAction (for ODBC 2.x compatibility) *
    *
  11. DELETE_RULE short {@code =>} What happens to the foreign key when primary is * deleted. *
      *
    • importedKeyNoAction - do not allow delete of primary key if it has been imported *
    • importedKeyCascade - delete rows that import a deleted key *
    • importedKeySetNull - change imported key to NULL if its primary key * has been deleted *
    • importedKeyRestrict - same as importedKeyNoAction (for ODBC 2.x compatibility) *
    • importedKeySetDefault - change imported key to default if its primary key has been * deleted *
    *
  12. FK_NAME String {@code =>} foreign key name (may be null) *
  13. PK_NAME String {@code =>} primary key name (may be null) *
  14. DEFERRABILITY short {@code =>} can the evaluation of foreign key constraints be * deferred until commit *
      *
    • importedKeyInitiallyDeferred - see SQL92 for definition *
    • importedKeyInitiallyImmediate - see SQL92 for definition *
    • importedKeyNotDeferrable - see SQL92 for definition *
    *
* * @param catalog a catalog name; must match the catalog name as it is stored in this database; "" * retrieves those without a catalog; null 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; null 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 ResultSet 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 { if (table == null || table.isEmpty()) { throw new SQLException("'table' parameter in getExportedKeys cannot be null"); } boolean getExportedKeysUsingIs = Boolean.parseBoolean( conf.nonMappedOptions().getProperty("getExportedKeysUsingIs", "false")); if (getExportedKeysUsingIs) return getExportedKeysUsingInformationSchema(catalog, schema, table); try { return getExportedKeysUsingShowCreateTable(catalog, schema, table); } catch (Exception e) { // since 3.4.1 show create is now used by default, and there is no reason to have parsing // exception, but cannot remove that try-catch in a correction release. // TODO to be removed next minor version return getExportedKeysUsingInformationSchema(catalog, schema, table); } } private ResultSet getExportedKeysUsingShowCreateTable(String catalog, String schema, String table) throws Exception { 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 database = conf.useCatalogTerm() == CatalogTerm.UseCatalog ? catalog : schema; List data = new ArrayList<>(); Statement stmt = connection.createStatement(); List databases = new ArrayList<>(); ResultSet rs = stmt.executeQuery("SHOW DATABASES"); while (rs.next()) databases.add(rs.getString(1)); String ext = (database == null ? "" : quoteIdentifier(database) + ".") + quoteIdentifier(table); Map externalInfo = getExtImportedKeys(ext, connection); for (String db : databases) { if (db.equalsIgnoreCase("sys") || db.equalsIgnoreCase("information_schema") || db.equalsIgnoreCase("mysql") || db.equalsIgnoreCase("performance_schema")) continue; List tables = new ArrayList<>(); ResultSet rsTables = stmt.executeQuery("SHOW TABLES FROM " + quoteIdentifier(db)); while (rsTables.next()) tables.add(rsTables.getString(1)); for (String tableName : tables) { try { rs = stmt.executeQuery( "SHOW CREATE TABLE " + quoteIdentifier(db) + "." + quoteIdentifier(tableName)); rs.next(); String tableDef = rs.getString(2); String[] parts = tableDef.split("\n"); 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 foreignKeyCols = new ArrayList<>(); pos = parseIdentifierList(partChar, pos, foreignKeyCols); pos = skipKeyword(partChar, pos, "REFERENCES"); Identifier pkTable = new Identifier(); pos = parseIdentifier(partChar, pos, pkTable); List primaryKeyCols = new ArrayList<>(); parseIdentifierList(partChar, pos, primaryKeyCols); String pkTableSchema = pkTable.schema != null ? pkTable.schema : db; if (database != null && !database.equals(pkTableSchema)) continue; if (!pkTable.name.equals(table)) continue; 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); } } for (int i = 0; i < primaryKeyCols.size(); i++) { String[] row = new String[14]; row[0] = conf.useCatalogTerm() == CatalogTerm.UseCatalog ? (pkTable.schema == null ? database : pkTable.schema) : "def"; // PKTABLE_CAT row[1] = conf.useCatalogTerm() == CatalogTerm.UseSchema ? (pkTable.schema == null ? database : pkTable.schema) : null; // PKTABLE_SCHEM row[2] = pkTable.name; // PKTABLE_NAME row[3] = primaryKeyCols.get(i).name; // PKCOLUMN_NAME row[4] = conf.useCatalogTerm() == CatalogTerm.UseCatalog ? database : "def"; // FKTABLE_CAT row[5] = conf.useCatalogTerm() == CatalogTerm.UseSchema ? database : 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 row[12] = null; // PK_NAME for (Map.Entry entry : externalInfo.entrySet()) { boolean foundAll = true; List pkKeysFound = new ArrayList<>(); for (String keyPart : entry.getKey()) { boolean foundKey = false; for (Identifier keyCol : primaryKeyCols) { if (keyCol.name.equals(keyPart)) { foundKey = true; pkKeysFound.add(keyCol); break; } } if (!foundKey) { foundAll = false; break; } } // either found all the keys, or found at least all the one required if (foundAll || pkKeysFound.size() == primaryKeyCols.size()) { row[12] = entry.getValue(); } } row[13] = Integer.toString(DatabaseMetaData.importedKeyNotDeferrable); // DEFERRABILITY data.add(row); } } } catch (SQLException e) { // eat } } } String[][] arr = data.toArray(new String[0][]); /* Sort array by FKTABLE_CAT, FKTABLE_SCHEM, FKTABLE_NAME, PK_NAME, KEY_SEQ.*/ Arrays.sort( arr, (row1, row2) -> { int result = 0; if (row1[4] != null) result = row1[4].compareTo(row2[4]); // FKTABLE_CAT if (result == 0) { if (row1[5] != null) result = row1[5].compareTo(row2[5]); // FKTABLE_SCHEM if (result == 0) { result = row1[6].compareTo(row2[6]); // PKTABLE_NAME if (result == 0) { if (row1[12] != null) result = row1[12].compareTo(row2[12]); // adding PK_NAME for reliability 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, ResultSet.TYPE_SCROLL_INSENSITIVE); } private ResultSet getExportedKeysUsingInformationSchema( String catalog, String schema, String table) throws SQLException { StringBuilder sb = new StringBuilder("SELECT ") .append( conf.useCatalogTerm() == CatalogTerm.UseCatalog ? "KCU.REFERENCED_TABLE_SCHEMA PKTABLE_CAT, NULL PKTABLE_SCHEM" : "KCU.CONSTRAINT_CATALOG PKTABLE_CAT, KCU.REFERENCED_TABLE_SCHEMA" + " PKTABLE_SCHEM") .append( ", KCU.REFERENCED_TABLE_NAME PKTABLE_NAME," + " KCU.REFERENCED_COLUMN_NAME PKCOLUMN_NAME, ") .append( conf.useCatalogTerm() == CatalogTerm.UseCatalog ? "KCU.TABLE_SCHEMA FKTABLE_CAT, NULL FKTABLE_SCHEM" : " TABLE_CATALOG FKTABLE_CAT, KCU.TABLE_SCHEMA FKTABLE_SCHEM") .append( ", 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_CATALOG = RC.CONSTRAINT_CATALOG" + " AND KCU.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA" + " AND KCU.CONSTRAINT_NAME = RC.CONSTRAINT_NAME"); String database = conf.useCatalogTerm() == CatalogTerm.UseCatalog ? catalog : schema; boolean firstCondition = databaseCond( true, sb, "KCU.REFERENCED_TABLE_SCHEMA", database, conf.useCatalogTerm() == CatalogTerm.UseSchema); sb.append(firstCondition ? " WHERE " : " AND ") .append("KCU.REFERENCED_TABLE_NAME = ") .append(escapeQuote(table)); sb.append(" ORDER BY FKTABLE_CAT, FKTABLE_SCHEM, FKTABLE_NAME, PK_NAME, KEY_SEQ"); return executeQuery(sb.toString()); } /** * GetImportedKeysUsingInformationSchema. * * @param database database * @param table table * @return resultset * @throws SQLException exception */ public ResultSet getImportedKeysUsingInformationSchema(final String database, String table) throws SQLException { StringBuilder sb = new StringBuilder("SELECT ") .append( conf.useCatalogTerm() == CatalogTerm.UseCatalog ? "KCU.REFERENCED_TABLE_SCHEMA PKTABLE_CAT, NULL PKTABLE_SCHEM" : "KCU.TABLE_CATALOG PKTABLE_CAT, KCU.REFERENCED_TABLE_SCHEMA PKTABLE_SCHEM") .append( ", KCU.REFERENCED_TABLE_NAME PKTABLE_NAME," + " KCU.REFERENCED_COLUMN_NAME PKCOLUMN_NAME, ") .append( conf.useCatalogTerm() == CatalogTerm.UseCatalog ? "KCU.TABLE_SCHEMA FKTABLE_CAT, NULL FKTABLE_SCHEM" : "KCU.TABLE_CATALOG FKTABLE_CAT, KCU.TABLE_SCHEMA FKTABLE_SCHEM") .append( ", 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_CATALOG = RC.CONSTRAINT_CATALOG" + " AND KCU.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA" + " AND KCU.CONSTRAINT_NAME = RC.CONSTRAINT_NAME "); boolean firstCondition = databaseCond(true, sb, "KCU.TABLE_SCHEMA", database, false); 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 database database * @param table table * @return resultset * @throws Exception exception */ public ResultSet getImportedKeysUsingShowCreateTable(final String database, String table) throws Exception { 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 }; List data = new ArrayList<>(); Statement stmt = connection.createStatement(); if (database != null) { ResultSet rs = stmt.executeQuery( "SHOW CREATE TABLE " + quoteIdentifier(database) + "." + quoteIdentifier(table)); rs.next(); String tableDef = rs.getString(2); parseShowCreateTable(tableDef, database, table, importedKeysWithConstraintNames, data); } else { List databases = new ArrayList<>(); ResultSet rs = stmt.executeQuery("SHOW DATABASES"); while (rs.next()) databases.add(rs.getString(1)); for (String db : databases) { if (db.equalsIgnoreCase("sys") || db.equalsIgnoreCase("information_schema") || db.equalsIgnoreCase("mysql") || db.equalsIgnoreCase("performance_schema")) continue; try { rs = stmt.executeQuery( "SHOW CREATE TABLE " + quoteIdentifier(db) + "." + quoteIdentifier(table)); rs.next(); String tableDef = rs.getString(2); parseShowCreateTable(tableDef, db, table, importedKeysWithConstraintNames, data); } catch (SQLException e) { // eat } } } String[][] arr = data.toArray(new String[0][]); /* Sort array by PKTABLE_CAT, PKTABLE_SCHEM, PKTABLE_NAME, PK_NAME, and KEY_SEQ.*/ Arrays.sort( arr, (row1, row2) -> { int result = 0; if (row1[0] != null) result = row1[0].compareTo(row2[0]); // PKTABLE_CAT if (result == 0) { if (row1[1] != null) result = row1[1].compareTo(row2[1]); // PKTABLE_SCHEM if (result == 0) { result = row1[2].compareTo(row2[2]); // PKTABLE_NAME if (result == 0) { if (row1[12] != null) result = row1[12].compareTo(row2[12]); // adding PK_NAME for reliability 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, ResultSet.TYPE_SCROLL_INSENSITIVE); } /** * Retrieves a description of a table's optimal set of columns that uniquely identifies a row. * They are ordered by SCOPE. * *

Each column description has the following columns: * *

    *
  1. SCOPE short {@code =>} actual scope of result *
      *
    • bestRowTemporary - very temporary, while using row *
    • bestRowTransaction - valid for remainder of current transaction *
    • bestRowSession - valid for remainder of current session *
    *
  2. COLUMN_NAME String {@code =>} column name *
  3. DATA_TYPE int {@code =>} SQL data type from java.sql.Types *
  4. TYPE_NAME String {@code =>} Data source dependent type name, for a UDT the type * name is fully qualified *
  5. COLUMN_SIZE int {@code =>} precision *
  6. BUFFER_LENGTH int {@code =>} not used *
  7. DECIMAL_DIGITS short {@code =>} scale - Null is returned for data types where * DECIMAL_DIGITS is not applicable. *
  8. PSEUDO_COLUMN short {@code =>} is this a pseudo column like an Oracle ROWID *
      *
    • bestRowUnknown - may or may not be pseudo column *
    • bestRowNotPseudo - is NOT a pseudo column *
    • bestRowPseudo - is a pseudo column *
    *
* *

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; null 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; null 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 ResultSet - 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'"); String database = conf.useCatalogTerm() == CatalogTerm.UseCatalog ? catalog : schema; databaseCond(false, sbInner, "TABLE_SCHEMA", database, false); 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 + " )))"); databaseCond(false, sb, "TABLE_SCHEMA", database, false); 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. * *

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

Each column description has the following columns: * *

    *
  1. TABLE_CAT String {@code =>} table catalog (may be null) *
  2. TABLE_SCHEM String {@code =>} table schema (may be null) *
  3. TABLE_NAME String {@code =>} table name *
  4. COLUMN_NAME String {@code =>} column name *
  5. DATA_TYPE int {@code =>} SQL type from java.sql.Types *
  6. COLUMN_SIZE int {@code =>} column size. *
  7. DECIMAL_DIGITS int {@code =>} the number of fractional digits. Null is returned * for data types where DECIMAL_DIGITS is not applicable. *
  8. NUM_PREC_RADIX int {@code =>} Radix (typically either 10 or 2) *
  9. COLUMN_USAGE String {@code =>} The allowed usage for the column. The value * returned will correspond to the enum name returned by PseudoColumnUsage.name() *
  10. REMARKS String {@code =>} comment describing column (may be null) *
  11. CHAR_OCTET_LENGTH int {@code =>} for char types the maximum number of bytes in the * column *
  12. IS_NULLABLE String {@code =>} ISO rules are used to determine the nullability for * a column. *
      *
    • YES --- if the column can include NULLs *
    • NO --- if the column cannot include NULLs *
    • empty string --- if the nullability for the column is unknown *
    *
* *

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; null 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; null 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 ResultSet - 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() { // taken from https://mariadb.com/kb/en/reserved-words/ return "ACCESSIBLE,ADD,ALL,ALTER,ANALYZE,AND,AS,ASC,ASENSITIVE,BEFORE,BETWEEN,BIGINT,BINARY,BLOB,BOTH,BY,CALL,CASCADE,CASE,CHANGE,CHAR,CHARACTER,CHECK,COLLATE,COLUMN,CONDITION,CONSTRAINT,CONTINUE,CONVERT,CREATE,CROSS,CURRENT_DATE,CURRENT_ROLE,CURRENT_TIME,CURRENT_TIMESTAMP,CURRENT_USER,CURSOR,DATABASE,DATABASES,DAY_HOUR,DAY_MICROSECOND,DAY_MINUTE,DAY_SECOND,DEC,DECIMAL,DECLARE,DEFAULT,DELAYED,DELETE,DELETE_DOMAIN_ID,DESC,DESCRIBE,DETERMINISTIC,DISTINCT,DISTINCTROW,DIV,DO_DOMAIN_IDS,DOUBLE,DROP,DUAL,EACH,ELSE,ELSEIF,ENCLOSEDESCAPED,EXCEPTEXISTS,EXIT,EXPLAIN,FALSE,FETCH,FLOAT,FLOAT4,FLOAT8,FOR,FORCE,FOREIGN,FROMFULLTEXT,GENERAL,GRANT,GROUP,HAVING,HIGH_PRIORITY,HOUR_MICROSECOND,HOUR_MINUTE,HOUR_SECOND,IF,IGNORE,IGNORE_DOMAIN_IDS,IGNORE_SERVER_IDS,IN,INDEX,INFILE,INNER,INOUT,INSENSITIVE,INSERT,INT,INT1,INT2,INT3,INT4,INT8,INTEGER,INTERSECTINTERVAL,INTO,IS,ITERATE,JOIN,KEY,KEYS,KILL,LEADING,LEAVE,LEFT,LIKE,LIMIT,LINEAR,LINES,LOAD,LOCALTIME,LOCALTIMESTAMP,LOCK,LONG,LONGBLOB,LONGTEXT,LOOP,LOW_PRIORITY,MASTER_HEARTBEAT_PERIOD,MASTER_SSL_VERIFY_SERVER_CERT,MATCH,MAXVALUE,MEDIUMBLOB,MEDIUMINT,MEDIUMTEXT,MIDDLEINT,MINUTE_MICROSECOND,MINUTE_SECOND,MOD,MODIFIES,NATURAL,NOT,NO_WRITE_TO_BINLOG,NULL,NUMERIC,OFFSETON,OPTIMIZE,OPTION,OPTIONALLY,OR,ORDER,OUT,OUTER,OUTFILE,OVER,PAGE_CHECKSUM,PARSE_VCOL_EXPR,PARTITION,PRECISION,PRIMARY,PROCEDURE,PURGE,RANGE,READ,READS,READ_WRITE,REALRECURSIVE,REF_SYSTEM_ID,REFERENCES,REGEXP,RELEASE,RENAME,REPEAT,REPLACE,REQUIRE,RESIGNAL,RESTRICT,RETURN,RETURNING,REVOKE,RIGHT,RLIKE,ROW_NUMBERROWS,SCHEMA,SCHEMAS,SECOND_MICROSECOND,SELECT,SENSITIVE,SEPARATOR,SET,SHOW,SIGNAL,SLOW,SMALLINT,SPATIAL,SPECIFIC,SQL,SQLEXCEPTION,SQLSTATE,SQLWARNING,SQL_BIG_RESULT,SQL_CALC_FOUND_ROWS,SQL_SMALL_RESULT,SSL,STARTING,STATS_AUTO_RECALC,STATS_PERSISTENT,STATS_SAMPLE_PAGES,STRAIGHT_JOIN,TABLE,TERMINATED,THEN,TINYBLOB,TINYINT,TINYTEXT,TO,TRAILING,TRIGGER,TRUE,UNDO,UNION,UNIQUE,UNLOCK,UNSIGNED,UPDATE,USAGE,USE,USING,UTC_DATE,UTC_TIME,UTC_TIMESTAMP,VALUES,VARBINARY,VARCHAR,VARCHARACTER,VARYING,WHEN,WHERE,WHILE,WINDOWWITH,WRITE,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 commit * is a noop, and the isolation level is TRANSACTION_NONE. * * @return true if transactions are supported; false 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 java.sql.Connection * * @return true if so; false 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 * PROCEDURE_CAT, PROCEDURE_SCHEM, PROCEDURE_NAME and * SPECIFIC_ NAME. * *

Each procedure description has the following columns: * *

    *
  1. PROCEDURE_CAT String {@code =>} procedure catalog (may be null) *
  2. PROCEDURE_SCHEM String {@code =>} procedure schema (may be null) *
  3. PROCEDURE_NAME String {@code =>} procedure name *
  4. reserved for future use *
  5. reserved for future use *
  6. reserved for future use *
  7. REMARKS String {@code =>} explanatory comment on the procedure *
  8. PROCEDURE_TYPE short {@code =>} kind of procedure: *
      *
    • procedureResultUnknown - Cannot determine if a return value will be returned *
    • procedureNoResult - Does not return a return value *
    • procedureReturnsResult - Returns a return value *
    *
  9. SPECIFIC_NAME String {@code =>} The name which uniquely identifies this procedure * within its schema. *
* *

A user may not have permissions to execute any of the procedures that are returned by * getProcedures * * @param catalog a catalog name; must match the catalog name as it is stored in the database; "" * retrieves those without a catalog; null 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; null 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 ResultSet - each row is a procedure description * @throws SQLException if a database access error occurs * @see #getSearchStringEscape */ public ResultSet getProcedures(String catalog, String schemaPattern, String procedureNamePattern) throws SQLException { StringBuilder sb = new StringBuilder("SELECT ") .append( conf.useCatalogTerm() == CatalogTerm.UseCatalog ? "ROUTINE_SCHEMA PROCEDURE_CAT, NULL PROCEDURE_SCHEM" : "ROUTINE_CATALOG PROCEDURE_CAT, ROUTINE_SCHEMA PROCEDURE_SCHEM") .append( ", 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 "); String database = conf.useCatalogTerm() == CatalogTerm.UseCatalog ? catalog : schemaPattern; boolean firstCondition = databaseCond( true, sb, "ROUTINE_SCHEMA", database, conf.useCatalogTerm() == CatalogTerm.UseSchema); patternCond(firstCondition, sb, "ROUTINE_NAME", procedureNamePattern); return executeQuery(sb.toString()); } /** * Retrieves a description of the given catalog's stored procedure parameter and result columns. * *

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. * *

Each row in the ResultSet is a parameter description or column description with * the following fields: * *

    *
  1. PROCEDURE_CAT String {@code =>} procedure catalog (may be null) *
  2. PROCEDURE_SCHEM String {@code =>} procedure schema (may be null) *
  3. PROCEDURE_NAME String {@code =>} procedure name *
  4. COLUMN_NAME String {@code =>} column/parameter name *
  5. COLUMN_TYPE Short {@code =>} kind of column/parameter: *
      *
    • procedureColumnUnknown - nobody knows *
    • procedureColumnIn - IN parameter *
    • procedureColumnInOut - INOUT parameter *
    • procedureColumnOut - OUT parameter *
    • procedureColumnReturn - procedure return value *
    • procedureColumnResult - result column in ResultSet *
    *
  6. DATA_TYPE int {@code =>} SQL type from java.sql.Types *
  7. TYPE_NAME String {@code =>} SQL type name, for a UDT type the type name is fully * qualified *
  8. PRECISION int {@code =>} precision *
  9. LENGTH int {@code =>} length in bytes of data *
  10. SCALE short {@code =>} scale - null is returned for data types where SCALE is not * applicable. *
  11. RADIX short {@code =>} radix *
  12. NULLABLE short {@code =>} can it contain NULL. *
      *
    • procedureNoNulls - does not allow NULL values *
    • procedureNullable - allows NULL values *
    • procedureNullableUnknown - nullability unknown *
    *
  13. REMARKS String {@code =>} comment describing parameter/column *
  14. COLUMN_DEF String {@code =>} default value for the column, which should be * interpreted as a string when the value is enclosed in single quotes (may be null * ) *
      *
    • The string NULL (not enclosed in quotes) - if NULL was specified as the default * value *
    • TRUNCATE (not enclosed in quotes) - if the specified default value cannot be * represented without truncation *
    • NULL - if a default value was not specified *
    *
  15. SQL_DATA_TYPE int {@code =>} reserved for future use *
  16. SQL_DATETIME_SUB int {@code =>} reserved for future use *
  17. CHAR_OCTET_LENGTH int {@code =>} the maximum length of binary and character based * columns. For any other datatype the returned value is a NULL *
  18. ORDINAL_POSITION 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. *
  19. IS_NULLABLE String {@code =>} ISO rules are used to determine the nullability for * a column. *
      *
    • YES --- if the column can include NULLs *
    • NO --- if the column cannot include NULLs *
    • empty string --- if the nullability for the column is unknown *
    *
  20. SPECIFIC_NAME String {@code =>} the name which uniquely identifies this procedure * within its schema. *
* *

Note: Some databases may not return the column descriptions for a procedure. * *

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; null 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; null 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 ResultSet - 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"); String database = conf.useCatalogTerm() == CatalogTerm.UseCatalog ? catalog : schemaPattern; boolean firstCondition = databaseCond( true, sb, "SPECIFIC_SCHEMA", database, conf.useCatalogTerm() == CatalogTerm.UseCatalog); firstCondition = patternCond(firstCondition, sb, "SPECIFIC_NAME", procedureNamePattern); 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. * *

Only descriptions matching the schema, function and parameter name criteria are returned. * They are ordered by FUNCTION_CAT, FUNCTION_SCHEM, FUNCTION_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. * *

Each row in the ResultSet is a parameter description, column description or * return type description with the following fields: * *

    *
  1. FUNCTION_CAT String {@code =>} function catalog (may be null) *
  2. FUNCTION_SCHEM String {@code =>} function schema (may be null) *
  3. FUNCTION_NAME String {@code =>} function name. This is the name used to invoke the * function *
  4. COLUMN_NAME String {@code =>} column/parameter name *
  5. COLUMN_TYPE Short {@code =>} kind of column/parameter: *
      *
    • functionColumnUnknown - nobody knows *
    • functionColumnIn - IN parameter *
    • functionColumnInOut - INOUT parameter *
    • functionColumnOut - OUT parameter *
    • functionColumnReturn - function return value *
    • functionColumnResult - Indicates that the parameter or column is a column in the * ResultSet *
    *
  6. DATA_TYPE int {@code =>} SQL type from java.sql.Types *
  7. TYPE_NAME String {@code =>} SQL type name, for a UDT type the type name is fully * qualified *
  8. PRECISION int {@code =>} precision *
  9. LENGTH int {@code =>} length in bytes of data *
  10. SCALE short {@code =>} scale - null is returned for data types where SCALE is not * applicable. *
  11. RADIX short {@code =>} radix *
  12. NULLABLE short {@code =>} can it contain NULL. *
      *
    • functionNoNulls - does not allow NULL values *
    • functionNullable - allows NULL values *
    • functionNullableUnknown - nullability unknown *
    *
  13. REMARKS String {@code =>} comment describing column/parameter *
  14. CHAR_OCTET_LENGTH int {@code =>} the maximum length of binary and character based * parameters or columns. For any other datatype the returned value is a NULL *
  15. ORDINAL_POSITION 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. *
  16. IS_NULLABLE String {@code =>} ISO rules are used to determine the nullability for * a parameter or column. *
      *
    • YES --- if the parameter or column can include NULLs *
    • NO --- if the parameter or column cannot include NULLs *
    • empty string --- if the nullability for the parameter or column is unknown *
    *
  17. SPECIFIC_NAME 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 FUNCTION_NAME for example with overload functions *
* *

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; null 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; null 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 ResultSet - 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 ") .append( conf.useCatalogTerm() == CatalogTerm.UseCatalog ? "SPECIFIC_SCHEMA `FUNCTION_CAT`, NULL `FUNCTION_SCHEM`" : "SPECIFIC_CATALOG `FUNCTION_CAT`, SPECIFIC_SCHEMA `FUNCTION_SCHEM`") .append( ", 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,") .append(dataTypeClause("DTD_IDENTIFIER")) .append( " 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"); String database = conf.useCatalogTerm() == CatalogTerm.UseCatalog ? catalog : schemaPattern; boolean firstCondition = databaseCond( true, sb, "SPECIFIC_SCHEMA", database, conf.useCatalogTerm() == CatalogTerm.UseSchema); 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 { if (conf.useCatalogTerm() == CatalogTerm.UseSchema) { return executeQuery( "SELECT SCHEMA_NAME as TABLE_SCHEM, CATALOG_NAME as TABLE_CATALOG FROM" + " information_schema.SCHEMATA ORDER BY SCHEMA_NAME"); } return executeQuery("SELECT '' TABLE_SCHEM, '' TABLE_CATALOG FROM DUAL WHERE 1=0"); } public ResultSet getSchemas(String catalog, String schemaPattern) throws SQLException { if (conf.useCatalogTerm() == CatalogTerm.UseSchema) { StringBuilder sb = new StringBuilder( "SELECT SCHEMA_NAME as TABLE_SCHEM, CATALOG_NAME as TABLE_CATALOG FROM" + " information_schema.SCHEMATA "); databaseCond(true, sb, "SCHEMA_NAME", schemaPattern, true); sb.append(" ORDER BY SCHEMA_NAME"); return executeQuery(sb.toString()); } return executeQuery("SELECT '' table_schem, '' table_catalog FROM DUAL WHERE 1=0"); } public ResultSet getCatalogs() throws SQLException { if (conf.useCatalogTerm() == CatalogTerm.UseSchema) { return executeQuery("SELECT null TABLE_CAT FROM DUAL WHERE 1=0"); } return executeQuery( "SELECT SCHEMA_NAME TABLE_CAT FROM INFORMATION_SCHEMA.SCHEMATA ORDER BY SCHEMA_NAME"); } public ResultSet getTableTypes() throws SQLException { return executeQuery( "SELECT 'LOCAL TEMPORARY' TABLE_TYPE " + "UNION SELECT 'SYSTEM TABLE' TABLE_TYPE " + "UNION SELECT 'SYSTEM VIEW' TABLE_TYPE " + "UNION SELECT 'TABLE' TABLE_TYPE " + "UNION SELECT 'VIEW' TABLE_TYPE"); } /** * Retrieves a description of the access rights for a table's columns. * *

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

Each privilege description has the following columns: * *

    *
  1. TABLE_CAT String {@code =>} table catalog (may be null) *
  2. TABLE_SCHEM String {@code =>} table schema (may be null) *
  3. TABLE_NAME String {@code =>} table name *
  4. COLUMN_NAME String {@code =>} column name *
  5. GRANTOR String {@code =>} grantor of access (may be null) *
  6. GRANTEE String {@code =>} grantee of access *
  7. PRIVILEGE String {@code =>} name of access (SELECT, INSERT, UPDATE, REFRENCES, * ...) *
  8. IS_GRANTABLE String {@code =>} "YES" if grantee is permitted to grant to others; * "NO" if not; null if unknown *
* * @param catalog a catalog name; must match the catalog name as it is stored in the database; "" * retrieves those without a catalog; null 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; null 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 ResultSet - 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"); String database = conf.useCatalogTerm() == CatalogTerm.UseCatalog ? catalog : schema; boolean firstCondition = databaseCond(true, sb, "TABLE_SCHEMA", database, false); 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.) * *

Only privileges matching the schema and table name criteria are returned. They are ordered * by TABLE_CAT, TABLE_SCHEM, TABLE_NAME, and * PRIVILEGE. * *

Each privilege description has the following columns: * *

    *
  1. TABLE_CAT String {@code =>} table catalog (may be null) *
  2. TABLE_SCHEM String {@code =>} table schema (may be null) *
  3. TABLE_NAME String {@code =>} table name *
  4. GRANTOR String {@code =>} grantor of access (may be null) *
  5. GRANTEE String {@code =>} grantee of access *
  6. PRIVILEGE String {@code =>} name of access (SELECT, INSERT, UPDATE, REFRENCES, * ...) *
  7. IS_GRANTABLE String {@code =>} "YES" if grantee is permitted to grant to others; * "NO" if not; null if unknown *
* * @param catalog a catalog name; must match the catalog name as it is stored in the database; "" * retrieves those without a catalog; null 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; null 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 ResultSet - 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"); String database = conf.useCatalogTerm() == CatalogTerm.UseCatalog ? catalog : schemaPattern; boolean firstCondition = databaseCond( true, sb, "TABLE_SCHEMA", database, conf.useCatalogTerm() == CatalogTerm.UseSchema); 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. * *

Each column description has the following columns: * *

    *
  1. SCOPE short {@code =>} is not used *
  2. COLUMN_NAME String {@code =>} column name *
  3. DATA_TYPE int {@code =>} SQL data type from java.sql.Types *
  4. TYPE_NAME String {@code =>} Data source-dependent type name *
  5. COLUMN_SIZE int {@code =>} precision *
  6. BUFFER_LENGTH int {@code =>} length of column value in bytes *
  7. DECIMAL_DIGITS short {@code =>} scale - Null is returned for data types where * DECIMAL_DIGITS is not applicable. *
  8. PSEUDO_COLUMN short {@code =>} whether this is pseudo column like an Oracle ROWID *
      *
    • versionColumnUnknown - may or may not be pseudo column *
    • versionColumnNotPseudo - is NOT a pseudo column *
    • versionColumnPseudo - is a pseudo column *
    *
* *

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;null 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; null 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 ResultSet 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. * *

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

    *
  1. PKTABLE_CAT String {@code =>} parent key table catalog (may be null) *
  2. PKTABLE_SCHEM String {@code =>} parent key table schema (may be null) *
  3. PKTABLE_NAME String {@code =>} parent key table name *
  4. PKCOLUMN_NAME String {@code =>} parent key column name *
  5. FKTABLE_CAT String {@code =>} foreign key table catalog (may be null) * being exported (may be null) *
  6. FKTABLE_SCHEM String {@code =>} foreign key table schema (may be null * ) being exported (may be null) *
  7. FKTABLE_NAME String {@code =>} foreign key table name being exported *
  8. FKCOLUMN_NAME String {@code =>} foreign key column name being exported *
  9. KEY_SEQ 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). *
  10. UPDATE_RULE short {@code =>} What happens to foreign key when parent key is * updated: *
      *
    • importedNoAction - do not allow update of parent key if it has been imported *
    • importedKeyCascade - change imported key to agree with parent key update *
    • importedKeySetNull - change imported key to NULL if its parent key has * been updated *
    • importedKeySetDefault - change imported key to default values if its parent key has * been updated *
    • importedKeyRestrict - same as importedKeyNoAction (for ODBC 2.x compatibility) *
    *
  11. DELETE_RULE short {@code =>} What happens to the foreign key when parent key is * deleted. *
      *
    • importedKeyNoAction - do not allow delete of parent key if it has been imported *
    • importedKeyCascade - delete rows that import a deleted key *
    • importedKeySetNull - change imported key to NULL if its primary key * has been deleted *
    • importedKeyRestrict - same as importedKeyNoAction (for ODBC 2.x compatibility) *
    • importedKeySetDefault - change imported key to default if its parent key has been * deleted *
    *
  12. FK_NAME String {@code =>} foreign key name (may be null) *
  13. PK_NAME String {@code =>} parent key name (may be null) *
  14. DEFERRABILITY short {@code =>} can the evaluation of foreign key constraints be * deferred until commit *
      *
    • importedKeyInitiallyDeferred - see SQL92 for definition *
    • importedKeyInitiallyImmediate - see SQL92 for definition *
    • importedKeyNotDeferrable - see SQL92 for definition *
    *
* * @param parentCatalog a catalog name; must match the catalog name as it is stored in the * database; "" retrieves those without a catalog; null 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; null 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; null 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; null 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 ResultSet - 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 ") .append( conf.useCatalogTerm() == CatalogTerm.UseCatalog ? "KCU.REFERENCED_TABLE_SCHEMA PKTABLE_CAT, NULL PKTABLE_SCHEM" : "KCU.TABLE_CATALOG PKTABLE_CAT, KCU.REFERENCED_TABLE_SCHEMA PKTABLE_SCHEM") .append( ", KCU.REFERENCED_TABLE_NAME PKTABLE_NAME," + " KCU.REFERENCED_COLUMN_NAME PKCOLUMN_NAME, ") .append( conf.useCatalogTerm() == CatalogTerm.UseCatalog ? "KCU.TABLE_SCHEMA FKTABLE_CAT, NULL FKTABLE_SCHEM" : "KCU.TABLE_CATALOG FKTABLE_CAT, KCU.TABLE_SCHEMA FKTABLE_SCHEM") .append( ", 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_CATALOG = RC.CONSTRAINT_CATALOG" + " AND KCU.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA" + " AND KCU.CONSTRAINT_NAME = RC.CONSTRAINT_NAME "); String parentDatabase = conf.useCatalogTerm() == CatalogTerm.UseCatalog ? parentCatalog : parentSchema; String foreignDatabase = conf.useCatalogTerm() == CatalogTerm.UseCatalog ? foreignCatalog : foreignSchema; boolean firstCondition = databaseCond(true, sb, "KCU.REFERENCED_TABLE_SCHEMA", parentDatabase, false); firstCondition = databaseCond(firstCondition, sb, "KCU.TABLE_SCHEMA", foreignDatabase, false); firstCondition = patternCond(firstCondition, sb, "KCU.REFERENCED_TABLE_NAME", parentTable); 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. * *

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. * *

If SQL distinct or structured types are supported, then information on the individual types * may be obtained from the getUDTs() method. * *

Each type description has the following columns: * *

    *
  1. TYPE_NAME String {@code =>} Type name *
  2. DATA_TYPE int {@code =>} SQL data type from java.sql.Types *
  3. PRECISION int {@code =>} maximum precision *
  4. LITERAL_PREFIX String {@code =>} prefix used to quote a literal (may be null * ) *
  5. LITERAL_SUFFIX String {@code =>} suffix used to quote a literal (may be null * ) *
  6. CREATE_PARAMS String {@code =>} parameters used in creating the type (may be * null) *
  7. NULLABLE short {@code =>} can you use NULL for this type. *
      *
    • typeNoNulls - does not allow NULL values *
    • typeNullable - allows NULL values *
    • typeNullableUnknown - nullability unknown *
    *
  8. CASE_SENSITIVE boolean{@code =>} is it case sensitive. *
  9. SEARCHABLE short {@code =>} can you use "WHERE" based on this type: *
      *
    • typePredNone - No support *
    • typePredChar - Only supported with WHERE .. LIKE *
    • typePredBasic - Supported except for WHERE .. LIKE *
    • typeSearchable - Supported for all WHERE .. *
    *
  10. UNSIGNED_ATTRIBUTE boolean {@code =>} is it unsigned. *
  11. FIXED_PREC_SCALE boolean {@code =>} can it be a money value. *
  12. AUTO_INCREMENT boolean {@code =>} can it be used for an auto-increment value. *
  13. LOCAL_TYPE_NAME String {@code =>} localized version of type name (may be * null) *
  14. MINIMUM_SCALE short {@code =>} minimum scale supported *
  15. MAXIMUM_SCALE short {@code =>} maximum scale supported *
  16. SQL_DATA_TYPE int {@code =>} unused *
  17. SQL_DATETIME_SUB int {@code =>} unused *
  18. NUM_PREC_RADIX int {@code =>} usually 2 or 10 *
* *

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 ResultSet 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", "\0", "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", "\0", "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", "\0", "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", "\0", "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", "\0", "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", "\0", "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, ResultSet.TYPE_SCROLL_INSENSITIVE); } /** * Retrieves a description of the given table's indices and statistics. They are ordered by * NON_UNIQUE, TYPE, INDEX_NAME, and ORDINAL_POSITION. * *

Each index column description has the following columns: * *

    *
  1. TABLE_CAT String {@code =>} table catalog (may be null) *
  2. TABLE_SCHEM String {@code =>} table schema (may be null) *
  3. TABLE_NAME String {@code =>} table name *
  4. NON_UNIQUE boolean {@code =>} Can index values be non-unique. false when TYPE is * tableIndexStatistic *
  5. INDEX_QUALIFIER String {@code =>} index catalog (may be null); * null when TYPE is tableIndexStatistic *
  6. INDEX_NAME String {@code =>} index name; null when TYPE is * tableIndexStatistic *
  7. TYPE short {@code =>} index type: *
      *
    • tableIndexStatistic - this identifies table statistics that are returned in * conjuction with a table's index descriptions *
    • tableIndexClustered - this is a clustered index *
    • tableIndexHashed - this is a hashed index *
    • tableIndexOther - this is some other style of index *
    *
  8. ORDINAL_POSITION short {@code =>} column sequence number within index; zero when * TYPE is tableIndexStatistic *
  9. COLUMN_NAME String {@code =>} column name; null when TYPE is * tableIndexStatistic *
  10. ASC_OR_DESC String {@code =>} column sort sequence, "A" {@code =>} ascending, "D" * {@code =>} descending, may be null if sort sequence is not supported; * null when TYPE is tableIndexStatistic *
  11. CARDINALITY 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. *
  12. PAGES 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. *
  13. FILTER_CONDITION String {@code =>} Filter condition, if any. (may be null * ) *
* * @param catalog a catalog name; must match the catalog name as it is stored in this database; "" * retrieves those without a catalog; null 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; null 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 ResultSet - 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 ") .append( conf.useCatalogTerm() == CatalogTerm.UseCatalog ? "TABLE_SCHEMA TABLE_CAT, NULL TABLE_SCHEM" : "TABLE_CATALOG TABLE_CAT, TABLE_SCHEMA TABLE_SCHEM") .append(", TABLE_NAME, " + "NON_UNIQUE, ") .append( conf.useCatalogTerm() == CatalogTerm.UseCatalog ? "TABLE_SCHEMA INDEX_QUALIFIER" : "TABLE_CATALOG INDEX_QUALIFIER") .append( ", 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"); String database = conf.useCatalogTerm() == CatalogTerm.UseCatalog ? catalog : schema; boolean firstCondition = databaseCond(true, sb, "TABLE_SCHEMA", database, false); 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 ResultSet constants: *
    *
  • ResultSet.TYPE_FORWARD_ONLY *
  • ResultSet.TYPE_SCROLL_INSENSITIVE *
  • ResultSet.TYPE_SCROLL_SENSITIVE *
* * @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 ResultSet constants: *
    *
  • ResultSet.TYPE_FORWARD_ONLY *
  • ResultSet.TYPE_SCROLL_INSENSITIVE *
  • ResultSet.TYPE_SCROLL_SENSITIVE *
* * @param concurrency one of the following ResultSet constants: *
    *
  • ResultSet.CONCUR_READ_ONLY *
  • ResultSet.CONCUR_UPDATABLE *
* * @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 JAVA_OBJECT, STRUCT, or * DISTINCT. * *

Only types matching the catalog, schema, type name and type criteria are returned. They are * ordered by DATA_TYPE, TYPE_CAT, TYPE_SCHEM and * TYPE_NAME. The type name parameter may be a fully-qualified name. In this case, the * catalog and schemaPattern parameters are ignored. * *

Each type description has the following columns: * *

    *
  1. TYPE_CAT String {@code =>} the type's catalog (may be null) *
  2. TYPE_SCHEM String {@code =>} type's schema (may be null) *
  3. TYPE_NAME String {@code =>} type name *
  4. CLASS_NAME String {@code =>} Java class name *
  5. DATA_TYPE int {@code =>} type value defined in java.sql.Types. One of JAVA_OBJECT, * STRUCT, or DISTINCT *
  6. REMARKS String {@code =>} explanatory comment on the type *
  7. BASE_TYPE 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 (null if DATA_TYPE is not * DISTINCT or not STRUCT with REFERENCE_GENERATION = USER_DEFINED) *
* *

Note: 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; null 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; null 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; * null returns all types * @return ResultSet 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 ResultSet object * returned by this method describes the designated UDT and a direct supertype. A row has the * following columns: * *

    *
  1. TYPE_CAT String {@code =>} the UDT's catalog (may be null) *
  2. TYPE_SCHEM String {@code =>} UDT's schema (may be null) *
  3. TYPE_NAME String {@code =>} type name of the UDT *
  4. SUPERTYPE_CAT String {@code =>} the direct super type's catalog (may be null * ) *
  5. SUPERTYPE_SCHEM String {@code =>} the direct super type's schema (may be * null) *
  6. SUPERTYPE_NAME String {@code =>} the direct super type's name *
* *

Note: If the driver does not support type hierarchies, an empty result set is * returned. * * @param catalog a catalog name; "" retrieves those without a catalog; null 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 ResultSet 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. * *

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. * *

Each type description has the following columns: * *

    *
  1. TABLE_CAT String {@code =>} the type's catalog (may be null) *
  2. TABLE_SCHEM String {@code =>} type's schema (may be null) *
  3. TABLE_NAME String {@code =>} type name *
  4. SUPERTABLE_NAME String {@code =>} the direct super type's name *
* *

Note: If the driver does not support type hierarchies, an empty result set is * returned. * * @param catalog a catalog name; "" retrieves those without a catalog; null 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 ResultSet 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 TYPE_CAT, TYPE_SCHEM, TYPE_NAME and * ORDINAL_POSITION. This description does not contain inherited attributes. The * ResultSet object that is returned has the following columns: * *

    *
  1. TYPE_CAT String {@code =>} type catalog (may be null) *
  2. TYPE_SCHEM String {@code =>} type schema (may be null) *
  3. TYPE_NAME String {@code =>} type name *
  4. ATTR_NAME String {@code =>} attribute name *
  5. DATA_TYPE int {@code =>} attribute type SQL type from java.sql.Types *
  6. ATTR_TYPE_NAME 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. *
  7. ATTR_SIZE int {@code =>} column size. For char or date types this is the maximum * number of characters; for numeric or decimal types this is precision. *
  8. DECIMAL_DIGITS int {@code =>} the number of fractional digits. Null is returned * for data types where DECIMAL_DIGITS is not applicable. *
  9. NUM_PREC_RADIX int {@code =>} Radix (typically either 10 or 2) *
  10. NULLABLE int {@code =>} whether NULL is allowed *
      *
    • attributeNoNulls - might not allow NULL values *
    • attributeNullable - definitely allows NULL values *
    • attributeNullableUnknown - nullability unknown *
    *
  11. REMARKS String {@code =>} comment describing column (may be null) *
  12. ATTR_DEF String {@code =>} default value (may benull) *
  13. SQL_DATA_TYPE int {@code =>} unused *
  14. SQL_DATETIME_SUB int {@code =>} unused *
  15. CHAR_OCTET_LENGTH int {@code =>} for char types the maximum number of bytes in the * column *
  16. ORDINAL_POSITION int {@code =>} index of the attribute in the UDT (starting at 1) *
  17. IS_NULLABLE String {@code =>} ISO rules are used to determine the nullability for * a attribute. *
      *
    • YES --- if the attribute can include NULLs *
    • NO --- if the attribute cannot include NULLs *
    • empty string --- if the nullability for the attribute is unknown *
    *
  18. SCOPE_CATALOG String {@code =>} catalog of table that is the scope of a reference * attribute (null if DATA_TYPE isn't REF) *
  19. SCOPE_SCHEMA String {@code =>} schema of table that is the scope of a reference * attribute (null if DATA_TYPE isn't REF) *
  20. SCOPE_TABLE String {@code =>} table name that is the scope of a reference * attribute (null if the DATA_TYPE isn't REF) *
  21. SOURCE_DATA_TYPE short {@code =>} source type of a distinct type or user-generated * Ref type,SQL type from java.sql.Types (null if DATA_TYPE isn't DISTINCT or * user-generated REF) *
* * @param catalog a catalog name; must match the catalog name as it is stored in the database; "" * retrieves those without a catalog; null 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; null 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 ResultSet 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 * *
    *
  1. NAME String : The name of the client info property *
  2. MAX_LEN int : The maximum length of the value for the property *
  3. DEFAULT_VALUE String : The default value of the property *
  4. DESCRIPTION String : A description of the property. This will typically contain * information as to where this property is stored in the database. *
* *

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[] { "ClientHostname", "16777215", "", "The hostname of the computer the application using the connection is running on" }, 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." } }; return CompleteResult.createResultSet( columnNames, types, data, connection.getContext(), 0, ResultSet.TYPE_SCROLL_INSENSITIVE); } /** * 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 FUNCTION_CAT, FUNCTION_SCHEM, * FUNCTION_NAME and SPECIFIC_ NAME. * *

Each function description has the following columns: * *

    *
  1. FUNCTION_CAT String {@code =>} function catalog (may be null) *
  2. FUNCTION_SCHEM String {@code =>} function schema (may be null) *
  3. FUNCTION_NAME String {@code =>} function name. This is the name used to invoke the * function *
  4. REMARKS String {@code =>} explanatory comment on the function *
  5. FUNCTION_TYPE short {@code =>} kind of function: *
      *
    • functionResultUnknown - Cannot determine if a return value or table will be * returned *
    • functionNoTable- Does not return a table *
    • functionReturnsTable - Returns a table *
    *
  6. SPECIFIC_NAME 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 FUNCTION_NAME for example with overload functions *
* *

A user may not have permission to execute any of the functions that are returned by * getFunctions * * @param catalog a catalog name; must match the catalog name as it is stored in the database; "" * retrieves those without a catalog; null 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; null 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 ResultSet - 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"); String database = conf.useCatalogTerm() == CatalogTerm.UseCatalog ? catalog : schemaPattern; boolean firstCondition = databaseCond( true, sb, "ROUTINE_SCHEMA", database, conf.useCatalogTerm() == CatalogTerm.UseSchema); firstCondition = patternCond(firstCondition, sb, "ROUTINE_NAME", functionNamePattern); sb.append(firstCondition ? " WHERE " : " AND ").append(" ROUTINE_TYPE='FUNCTION'"); sb.append(" ORDER BY FUNCTION_CAT,FUNCTION_SCHEM,FUNCTION_NAME,SPECIFIC_NAME"); return executeQuery(sb.toString()); } @Override public T unwrap(Class 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; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy