org.firebirdsql.jdbc.AbstractDatabaseMetaData Maven / Gradle / Ivy
Show all versions of jaybird-jdk15 Show documentation
/*
* $Id: AbstractDatabaseMetaData.java 56970 2012-05-06 15:53:55Z mrotteveel $
*
* Firebird Open Source J2ee connector - jdbc driver
*
* Distributable under LGPL license.
* You may obtain a copy of the License at http://www.gnu.org/copyleft/lgpl.html
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* LGPL License for more details.
*
* This file was created by members of the firebird development team.
* All individual contributions remain the Copyright (C) of those
* individuals. Contributors to this file are either listed here or
* can be obtained from a CVS history command.
*
* All rights reserved.
*/
package org.firebirdsql.jdbc;
import java.sql.*;
import java.util.*;
import org.firebirdsql.gds.*;
import org.firebirdsql.gds.impl.AbstractGDS;
import org.firebirdsql.gds.impl.GDSFactory;
import org.firebirdsql.gds.impl.GDSHelper;
import org.firebirdsql.logging.Logger;
import org.firebirdsql.logging.LoggerFactory;
/**
* Comprehensive information about the database as a whole.
*
* Many of the methods here return lists of information in
* the form of ResultSet
objects.
* You can use the normal ResultSet
methods such as getString and getInt
* to retrieve the data from these ResultSet
. If a given form of
* metadata is not available, these methods should throw an SQLException.
*
*
Some of these methods take arguments that are String patterns. These
* arguments all have names such as fooPattern. Within a pattern String, "%"
* means match any substring of 0 or more characters, and "_" means match
* any one character. Only metadata entries matching the search pattern
* are returned. If a search pattern argument is set to a null ref,
* that argument's criteria will be dropped from the search.
*
*
An SQLException
will be thrown if a driver does not support a meta
* data method. In the case of methods that return a ResultSet
,
* either a ResultSet
(which may be empty) is returned or a
* SQLException is thrown.
*
* @author David Jencks
* @author Mark Rotteveel
*/
public abstract class AbstractDatabaseMetaData implements FirebirdDatabaseMetaData {
private final static Logger log = LoggerFactory.getLogger(FBDatabaseMetaData.class,false);
public static final String SPACES = " ";//31 spaces
private GDSHelper gdsHelper;
private AbstractConnection connection;
HashMap statements = new HashMap();
//PreparedStatement tables = null;
protected AbstractDatabaseMetaData(GDSHelper gdsHelper) {
this.gdsHelper = gdsHelper;
}
protected AbstractDatabaseMetaData(AbstractConnection c) throws GDSException {
this.gdsHelper = c.getGDSHelper();
this.connection = c;
}
protected void close() {
try {
Iterator i = statements.values().iterator();
while(i.hasNext()) {
AbstractStatement stmt = (AbstractPreparedStatement)i.next();
if (!stmt.isClosed())
stmt.close();
}
statements.clear();
}
catch (SQLException e) {
if (log!=null) log.warn("error in DatabaseMetaData.close", e);
}
}
//----------------------------------------------------------------------
// First, a variety of minor information about the target database.
/**
* Can all the procedures returned by getProcedures be called by the
* current user?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean allProceduresAreCallable() throws SQLException {
//returns all procedures whether or not you have execute permission
return false;
}
/**
* Can all the tables returned by getTable be SELECTed by the
* current user?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean allTablesAreSelectable() throws SQLException {
//returns all tables matching criteria independent of access permissions.
return false;
}
/**
* What's the url for this database?
*
* @return the url or null if it cannot be generated
* @exception SQLException if a database access error occurs
*/
public String getURL() throws SQLException {
AbstractGDS gds = ((AbstractGDS) connection.getInternalAPIHandler());
return GDSFactory.getJdbcUrl(gds.getType(), connection.mc.getDatabase());
}
/**
* What's our user name as known to the database?
*
* @return our database user name
* @exception SQLException
* if a database access error occurs
*/
public String getUserName() throws SQLException {
return gdsHelper.getUserName();
}
/**
* Is the database in read-only mode?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean isReadOnly() throws SQLException {
return false;//could be true, not yetimplemented
}
/**
* Are NULL values sorted high?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean nullsAreSortedHigh() throws SQLException {
// in Firebird 1.5.x NULLs are always sorted at the end
// in Firebird 2.0.x NULLs are sorted low
return false;
}
/**
* Are NULL values sorted low?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean nullsAreSortedLow() throws SQLException {
// in Firebird 1.5.x NULLs are always sorted at the end
// in Firebird 2.0.x NULLs are sorted low
return gdsHelper.compareToVersion(2, 0) >= 0;
}
/**
* Are NULL values sorted at the start regardless of sort order?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean nullsAreSortedAtStart() throws SQLException {
// in Firebird 1.5.x NULLs are always sorted at the end
// in Firebird 2.0.x NULLs are sorted low
return false;
}
/**
* Are NULL values sorted at the end regardless of sort order?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean nullsAreSortedAtEnd() throws SQLException {
// in Firebird 1.5.x NULLs are always sorted at the end
// in Firebird 2.0.x NULLs are sorted low
return gdsHelper.compareToVersion(2, 0) < 0;
}
/**
* What's the name of this database product?
*
* @return database product name
* @exception SQLException if a database access error occurs
*/
public String getDatabaseProductName() throws SQLException {
return gdsHelper.getDatabaseProductName();
}
/**
* What's the version of this database product?
*
* @return database version
* @exception SQLException if a database access error occurs
*/
public String getDatabaseProductVersion() throws SQLException {
return gdsHelper.getDatabaseProductVersion();
}
/**
* What's the name of this JDBC driver?
*
* @return JDBC driver name
* @exception SQLException if a database access error occurs
*/
public String getDriverName() throws SQLException {
return "Jaybird JCA/JDBC driver";
}
/**
* What's the version of this JDBC driver?
*
* @return JDBC driver version
* @exception SQLException if a database access error occurs
*/
public String getDriverVersion() throws SQLException {
return "2.2";
}
/**
* What's this JDBC driver's major version number?
*
* @return JDBC driver major version
*/
public int getDriverMajorVersion() {
return 2;
}
/**
* What's this JDBC driver's minor version number?
*
* @return JDBC driver minor version number
*/
public int getDriverMinorVersion() {
return 2;
}
/**
* Does the database store tables in a local file?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean usesLocalFiles() throws SQLException {
return false;
}
/**
* Does the database use a file for each table?
*
* @return true if the database uses a local file for each table
* @exception SQLException if a database access error occurs
*/
public boolean usesLocalFilePerTable() throws SQLException {
return false;
}
/**
* Does the database treat mixed case unquoted SQL identifiers as
* case sensitive and as a result store them in mixed case?
*
* A JDBC CompliantTM driver will always return false.
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsMixedCaseIdentifiers() throws SQLException {
return false;
}
/**
*
* @return a boolean
value
* @exception SQLException if an error occurs
* TODO implement statement pooling on the server.. then in the driver
*/
public boolean supportsStatementPooling() throws SQLException {
return false;
}
public boolean locatorsUpdateCopy() throws SQLException {
// Firebird creates a new blob when making changes
return true;
}
/**
* Does the database treat mixed case unquoted SQL identifiers as
* case insensitive and store them in upper case?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean storesUpperCaseIdentifiers() throws SQLException {
return true;
}
/**
* Does the database treat mixed case unquoted SQL identifiers as
* case insensitive and store them in lower case?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean storesLowerCaseIdentifiers() throws SQLException {
return false;
}
/**
* Does the database treat mixed case unquoted SQL identifiers as
* case insensitive and store them in mixed case?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean storesMixedCaseIdentifiers() throws SQLException {
return false;
}
/**
* Does the database treat mixed case quoted SQL identifiers as
* case sensitive and as a result store them in mixed case?
*
* A JDBC CompliantTM driver will always return true.
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsMixedCaseQuotedIdentifiers() throws SQLException {
return true;
}
/**
* Does the database treat mixed case quoted SQL identifiers as
* case insensitive and store them in upper case?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean storesUpperCaseQuotedIdentifiers() throws SQLException {
return false;
}
/**
* Does the database treat mixed case quoted SQL identifiers as
* case insensitive and store them in lower case?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean storesLowerCaseQuotedIdentifiers() throws SQLException {
return false;
}
/**
* Does the database treat mixed case quoted SQL identifiers as
* case insensitive and store them in mixed case?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean storesMixedCaseQuotedIdentifiers() throws SQLException {
return false;
}
/**
* What's the string used to quote SQL identifiers?
* This returns a space " " if identifier quoting isn't supported.
*
* A JDBC CompliantTM
* driver always uses a double quote character.
*
* @return the quoting string
* @exception SQLException if a database access error occurs
*/
public String getIdentifierQuoteString() throws SQLException {
return "\"";
}
/**
* Describe constant fbSQLKeywords
here.
* Derived from firebird2/src/dsql/keywords.cpp.
* Exclusions from list of sql-92 keywords in SQL Instant Reference,
* Martin Gruber (1993) Sybex.
*
*/
private final static String fbSQLKeywords =
//"ACTION," +
"ACTIVE," +
//"ADD," +
"ADMIN," +
"AFTER," +
//"ALL," +
//"ALTER," +
//"AND," +
//"ANY," +
//"AS," +
//"ASC," + /* Alias of ASCENDING */
"ASCENDING," +
//"AT," +
"AUTO," +
//"AVG," +
"BASE_NAME," +
"BEFORE," +
//"BEGIN," +
//"BETWEEN," +
"BIGINT," +
"BLOB," +
"BREAK," +
//"BY," +
"CACHE," +
//"CASCADE," +
//"CASE," +
//"CAST," +
//"CHAR," +
//"CHARACTER," +
//"CHECK," +
"CHECK_POINT_LENGTH," +
//"COALESCE," +
//"COLLATE," +
//"COLUMN," +
//"COMMIT," +
//"COMMITTED," +
"COMPUTED," +
"CONDITIONAL," +
"CONNECTION_ID," +
//"CONSTRAINT," +
"CONTAINING," +
//"COUNT," +
//"CREATE," +
"CSTRING," +
//"CURRENT," +
//"CURRENT_DATE," +
"CURRENT_ROLE," +
//"CURRENT_TIME," +
//"CURRENT_TIMESTAMP," +
//"CURRENT_USER," +
//"CURSOR," +
"DATABASE," +
//"DATE," +
//"DAY," +
"DEBUG," +
//"DEC," +
//"DECIMAL," +
//"DECLARE," +
//"DEFAULT," +
//"DELETE," +
//"DESC," + /* Alias of DESCENDING */
"DESCENDING," +
//"DESCRIPTOR," +
//"DISTINCT," +
"DO," +
//"DOMAIN," +
//"DOUBLE," +
//"DROP," +
//"ELSE," +
//"END," +
"ENTRY_POINT," +
//"ESCAPE," +
//"EXCEPTION," +
//"EXECUTE," +
//"EXISTS," +
"EXIT," +
//"EXTERNAL," +
//"EXTRACT," +
"FILE," +
"FILTER," +
//"FIRST," +
//"FLOAT," +
//"FOR," +
//"FOREIGN," +
"FREE_IT," +
//"FROM," +
//"FULL," +
"FUNCTION," +
"GDSCODE," +
"GENERATOR," +
"GEN_ID," +
//"GRANT," +
//"GROUP," +
"GROUP_COMMIT_WAIT_TIME," +
//"HAVING," +
//"HOUR," +
"IF," +
//"IN," +
"INACTIVE," +
"INDEX," +
//"INNER," +
"INPUT_TYPE," +
//"INSERT," +
//"INT," +
//"INTEGER," +
//"INTO," +
//"IS," +
//"ISOLATION," +
//"JOIN," +
//"KEY," +
//"LAST," +
//"LEFT," +
//"LENGTH," +
//"LEVEL," +
//"LIKE," +
"LOGFILE," +
"LOG_BUFFER_SIZE," +
"LONG," +
"MANUAL," +
//"MAX," +
"MAXIMUM_SEGMENT," +
"MERGE," +
"MESSAGE," +
//"MIN," +
//"MINUTE," +
"MODULE_NAME," +
//"MONTH," +
//"NAMES," +
//"NATIONAL," +
//"NATURAL," +
//"NCHAR," +
//"NO," +
//"NOT," +
//"NULLIF," +
//"NULL," +
"NULLS," +
"LOCK," +
//"NUMERIC," +
"NUM_LOG_BUFFERS," +
//"OF," +
//"ON," +
//"ONLY," +
//"OPTION," +
//"OR," +
//"ORDER," +
//"OUTER," +
"OUTPUT_TYPE," +
"OVERFLOW," +
"PAGE," +
"PAGES," +
"PAGE_SIZE," +
"PARAMETER," +
"PASSWORD," +
"PLAN," +
//"POSITION," +
"POST_EVENT," +
//"PRECISION," +
//"PRIMARY," +
//"PRIVILEGES," +
//"PROCEDURE," +
"PROTECTED," +
"RAW_PARTITIONS," +
"RDB$DB_KEY," +
//"READ," +
//"REAL," +
"RECORD_VERSION," +
"RECREATE," +
//"REFERENCES," +
"RESERV," + /* Alias of RESERVING */
"RESERVING," +
//"RESTRICT," +
"RETAIN," +
"RETURNING_VALUES," +
"RETURNS," +
//"REVOKE," +
//"RIGHT," +
"ROLE," +
//"ROLLBACK," +
"ROWS_AFFECTED," +
"SAVEPOINT," +
//"SCHEMA," + /* Alias of DATABASE */
//"SECOND," +
"SEGMENT," +
//"SELECT," +
//"SET," +
"SHADOW," +
"SHARED," +
"SINGULAR," +
//"SIZE," +
"SKIP," +
//"SMALLINT," +
"SNAPSHOT," +
//"SOME," +
"SORT," +
//"SQLCODE," +
"STABILITY," +
"STARTING," +
"STARTS," + /* Alias of STARTING */
"STATISTICS," +
//"SUBSTRING," +
"SUB_TYPE," +
//"SUM," +
"SUSPEND," +
//"TABLE," +
//"THEN," +
//"TIME," +
//"TIMESTAMP," +
//"TO," +
//"TRANSACTION," +
"TRANSACTION_ID," +
"TRIGGER," +
//"TYPE," +
//"UNCOMMITTED," +
//"UNION," +
//"UNIQUE," +
//"UPDATE," +
//"UPPER," +
//"USER," +
//"USING," +
//"VALUE," +
//"VALUES," +
//"VARCHAR," +
"VARIABLE," +
//"VARYING," +
//"VIEW," +
"WAIT," +
"WEEKDAY," +
//"WHEN," +
//"WHERE," +
"WHILE," +
//"WITH," +
//"WORK," +
//"WRITE," +
//"YEAR," +
"YEARDAY";
/**
* Gets a comma-separated list of all a database's SQL keywords
* that are NOT also SQL92 keywords.
*
* @return the list
* @exception SQLException if a database access error occurs
*/
public String getSQLKeywords() throws SQLException {
return fbSQLKeywords;
}
/**
* Gets a comma-separated list of math functions. These are the
* X/Open CLI math function names used in the JDBC function escape
* clause.
*
* @return the list
* @exception SQLException if a database access error occurs
*/
public String getNumericFunctions() throws SQLException {
return "";//udf's
}
/**
* Gets a comma-separated list of string functions. These are the
* X/Open CLI string function names used in the JDBC function escape
* clause.
*
* @return the list
* @exception SQLException if a database access error occurs
*/
public String getStringFunctions() throws SQLException {
return "SUBSTRING,UCASE,CONCAT";
}
/**
* Gets a comma-separated list of system functions. These are the
* X/Open CLI system function names used in the JDBC function escape
* clause.
*
* @return the list
* @exception SQLException if a database access error occurs
*/
public String getSystemFunctions() throws SQLException {
return "";
}
/**
* Gets a comma-separated list of time and date functions.
*
* @return the list
* @exception SQLException if a database access error occurs
*/
public String getTimeDateFunctions() throws SQLException {
return "CURDATE,CURTIME,DAYOFYEAR,HOUR,MINUTE,MONTH,NOW,SECOND,YEAR";
}
/**
* Gets the string that can be used to escape wildcard characters.
* This is the string that can be used to escape '_' or '%' in
* the string pattern style catalog search parameters.
*
*
The '_' character represents any single character.
*
The '%' character represents any sequence of zero or
* more characters.
*
* @return the string used to escape wildcard characters
* @exception SQLException if a database access error occurs
*/
public String getSearchStringEscape() throws SQLException {
return "\\";
}
/**
* Gets all the "extra" characters that can be used in unquoted
* identifier names (those beyond a-z, A-Z, 0-9 and _).
*
* @return the string containing the extra characters
* @exception SQLException if a database access error occurs
*/
public String getExtraNameCharacters() throws SQLException {
return "$";
}
//--------------------------------------------------------------------
// Functions describing which features are supported.
/**
* Is "ALTER TABLE" with add column supported?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsAlterTableWithAddColumn() throws SQLException {
return true;
}
/**
* Is "ALTER TABLE" with drop column supported?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsAlterTableWithDropColumn() throws SQLException {
return true;
}
/**
* Is column aliasing supported?
*
*
If so, the SQL AS clause can be used to provide names for
* computed columns or to provide alias names for columns as
* required.
*
* A JDBC CompliantTM driver always returns true.
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsColumnAliasing() throws SQLException {
return true;
}
/**
* Retrieves whether concatenations between NULL and non-NULL values
* equal NULL. For SQL-92 compliance, a JDBC technology-enabled driver will
* return true
.
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean nullPlusNonNullIsNull() throws SQLException {
return true;
}
/**
* Is the CONVERT function between SQL types supported?
*
* What is this??? my sql ref says CONVERT is a string encoding map!
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsConvert() throws SQLException {
return false;//don't know
}
/**
* Retrieves whether CONVERT between the given SQL types supported.
*
* @param fromType the type to convert from
* @param toType the type to convert to
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
* @see Types
*/
public boolean supportsConvert(int fromType, int toType) throws SQLException {
return false;//don't know
}
/**
* Are table correlation names supported?
*
* A JDBC CompliantTM driver always returns true.
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsTableCorrelationNames() throws SQLException {
return true;
}
/**
* If table correlation names are supported, are they restricted
* to be different from the names of the tables?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsDifferentTableCorrelationNames() throws SQLException {
return false; //I think
}
/**
* Are expressions in "ORDER BY" lists supported?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsExpressionsInOrderBy() throws SQLException {
return false; //coming soon
}
/**
* Can an "ORDER BY" clause use columns not in the SELECT statement?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsOrderByUnrelated() throws SQLException {
return true;
}
/**
* Is some form of "GROUP BY" clause supported?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsGroupBy() throws SQLException {
return true;
}
/**
* Can a "GROUP BY" clause use columns not in the SELECT?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsGroupByUnrelated() throws SQLException {
return false;
}
/**
* Can a "GROUP BY" clause add columns not in the SELECT
* provided it specifies all the columns in the SELECT?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsGroupByBeyondSelect() throws SQLException {
return false;
}
/**
* Is the escape character in "LIKE" clauses supported?
*
* A JDBC CompliantTM driver always returns true.
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsLikeEscapeClause() throws SQLException {
return true;
}
/**
* Are multiple ResultSet
from a single execute supported?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsMultipleResultSets() throws SQLException {
return false;
}
/**
* Can we have multiple transactions open at once (on different
* connections)?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsMultipleTransactions() throws SQLException {
return true;
}
/**
* Can columns be defined as non-nullable?
*
* A JDBC CompliantTM driver always returns true.
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsNonNullableColumns() throws SQLException {
return true;
}
/**
* Is the ODBC Minimum SQL grammar supported?
*
* All JDBC CompliantTM drivers must return true.
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsMinimumSQLGrammar() throws SQLException {
return true; //lets see what the tests say
}
/**
* Is the ODBC Core SQL grammar supported?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsCoreSQLGrammar() throws SQLException {
return true; //lets see what the tests say
}
/**
* Is the ODBC Extended SQL grammar supported?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsExtendedSQLGrammar() throws SQLException {
return true; //lets see what the tests say
}
/**
* Is the ANSI92 entry level SQL grammar supported?
*
* All JDBC CompliantTM drivers must return true.
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsANSI92EntryLevelSQL() throws SQLException {
return true; //lets see what the tests say
}
/**
* Is the ANSI92 intermediate SQL grammar supported?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsANSI92IntermediateSQL() throws SQLException {
return false; //lets see what the tests say
}
/**
* Is the ANSI92 full SQL grammar supported?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsANSI92FullSQL() throws SQLException {
return false; //Nah, but lets see what the tests say
}
/**
* Is the SQL Integrity Enhancement Facility supported?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsIntegrityEnhancementFacility() throws SQLException {
return true; // rrokytskyy: yep, they call so foreign keys + cascade deletes
}
/**
* Is some form of outer join supported?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsOuterJoins() throws SQLException {
return true;
}
/**
* Are full nested outer joins supported?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsFullOuterJoins() throws SQLException {
return true;
}
/**
* Is there limited support for outer joins? (This will be true
* if supportFullOuterJoins is true.)
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsLimitedOuterJoins() throws SQLException {
return true;
}
/**
* What's the database vendor's preferred term for "schema"?
*
* @return the vendor term, always null
because
* schemas are not supported by database server (see JDBC CTS
* for details).
* @exception SQLException if a database access error occurs
*/
public String getSchemaTerm() throws SQLException {
return null;
}
/**
* What's the database vendor's preferred term for "procedure"?
*
* @return the vendor term
* @exception SQLException if a database access error occurs
*/
public String getProcedureTerm() throws SQLException {
return "PROCEDURE";
}
/**
* What's the database vendor's preferred term for "catalog"?
*
* @return the vendor term, always null
because
* catalogs are not supported by database server (see JDBC CTS
* for details).
*
* @exception SQLException if a database access error occurs
*/
public String getCatalogTerm() throws SQLException {
return null;
}
/**
* Does a catalog appear at the start of a qualified table name?
* (Otherwise it appears at the end)
*
* @return true if it appears at the start
* @exception SQLException if a database access error occurs
*/
public boolean isCatalogAtStart() throws SQLException {
return false;
}
/**
* What's the separator between catalog and table name?
*
* @return the separator string, always null
because
* catalogs are not supported by database server (see JDBC CTS
* for details).
* @exception SQLException if a database access error occurs
*/
public String getCatalogSeparator() throws SQLException {
return null;
}
/**
* Can a schema name be used in a data manipulation statement?
*
* @return true
if so; false
otherwise
*
* @exception SQLException if a database access error occurs
*/
public boolean supportsSchemasInDataManipulation() throws SQLException {
return false;
}
/**
* Can a schema name be used in a procedure call statement?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsSchemasInProcedureCalls() throws SQLException {
return false;
}
/**
* Can a schema name be used in a table definition statement?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsSchemasInTableDefinitions() throws SQLException {
return false;
}
/**
* Can a schema name be used in an index definition statement?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsSchemasInIndexDefinitions() throws SQLException {
return false;
}
/**
* Can a schema name be used in a privilege definition statement?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsSchemasInPrivilegeDefinitions() throws SQLException {
return false;
}
/**
* Can a catalog name be used in a data manipulation statement?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsCatalogsInDataManipulation() throws SQLException {
return false;
}
/**
* Can a catalog name be used in a procedure call statement?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsCatalogsInProcedureCalls() throws SQLException {
return false;
}
/**
* Can a catalog name be used in a table definition statement?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsCatalogsInTableDefinitions() throws SQLException {
return false;
}
/**
* Can a catalog name be used in an index definition statement?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsCatalogsInIndexDefinitions() throws SQLException {
return false;
}
/**
* Can a catalog name be used in a privilege definition statement?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsCatalogsInPrivilegeDefinitions() throws SQLException {
return false;
}
/**
* Is positioned DELETE supported?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsPositionedDelete() throws SQLException {
return true;
}
/**
* Is positioned UPDATE supported?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsPositionedUpdate() throws SQLException {
return true;
}
/**
* Is SELECT for UPDATE supported?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsSelectForUpdate() throws SQLException {
return true;
}
/**
* Are stored procedure calls using the stored procedure escape
* syntax supported?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsStoredProcedures() throws SQLException {
return true;
}
/**
* Are subqueries in comparison expressions supported?
*
* A JDBC CompliantTM driver always returns true.
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsSubqueriesInComparisons() throws SQLException {
return true;
}
/**
* Are subqueries in 'exists' expressions supported?
*
* A JDBC CompliantTM driver always returns true.
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsSubqueriesInExists() throws SQLException {
return true;
}
/**
* Are subqueries in 'in' statements supported?
*
* A JDBC CompliantTM driver always returns true.
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsSubqueriesInIns() throws SQLException {
return true;
}
/**
* Are subqueries in quantified expressions supported?
*
* A JDBC CompliantTM driver always returns true.
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsSubqueriesInQuantifieds() throws SQLException {
return true;
}
/**
* Are correlated subqueries supported?
*
* A JDBC CompliantTM driver always returns true.
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsCorrelatedSubqueries() throws SQLException {
return true;
}
/**
* Is SQL UNION supported?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsUnion() throws SQLException {
return true;
}
/**
* Is SQL UNION ALL supported?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsUnionAll() throws SQLException {
return true;
}
/**
* Can cursors remain open across commits?
*
* @return true
if cursors always remain open;
* false
if they might not remain open
* @exception SQLException if a database access error occurs
*/
public boolean supportsOpenCursorsAcrossCommit() throws SQLException {
return false;//only when commit retaining is executed I think
}
/**
* Can cursors remain open across rollbacks?
*
* @return true
if cursors always remain open;
* false
if they might not remain open
* @exception SQLException if a database access error occurs
*/
public boolean supportsOpenCursorsAcrossRollback() throws SQLException {
return false;//commit retaining only.
}
/**
* Can statements remain open across commits?
*
* @return true
if statements always remain open;
* false
if they might not remain open
* @exception SQLException if a database access error occurs
*/
public boolean supportsOpenStatementsAcrossCommit() throws SQLException {
return true;//commit retaining only.
}
/**
* Can statements remain open across rollbacks?
*
* @return true
if statements always remain open;
* false
if they might not remain open
* @exception SQLException if a database access error occurs
*/
public boolean supportsOpenStatementsAcrossRollback() throws SQLException {
return true;//commit retaining only.
}
//----------------------------------------------------------------------
// The following group of methods exposes various limitations
// based on the target database with the current driver.
// Unless otherwise specified, a result of zero means there is no
// limit, or the limit is not known.
/**
* How many hex characters can you have in an inline binary literal?
*
* @return max binary literal length in hex characters;
* a result of zero means that there is no limit or the limit is not known
* @exception SQLException if a database access error occurs
*/
public int getMaxBinaryLiteralLength() throws SQLException {
return 0;//anyone know for sure?
}
/**
* What's the max length for a character literal?
*
* @return max literal length;
* a result of zero means that there is no limit or the limit is not known
* @exception SQLException if a database access error occurs
*/
public int getMaxCharLiteralLength() throws SQLException {
return 32767;
}
/**
* What's the limit on column name length?
*
* @return max column name length;
* a result of zero means that there is no limit or the limit is not known
* @exception SQLException if a database access error occurs
*/
public int getMaxColumnNameLength() throws SQLException {
return 31;
}
/**
* What's the maximum number of columns in a "GROUP BY" clause?
*
* @return max number of columns;
* a result of zero means that there is no limit or the limit is not known
* @exception SQLException if a database access error occurs
*/
public int getMaxColumnsInGroupBy() throws SQLException {
return 0; //I don't know
}
/**
* What's the maximum number of columns allowed in an index?
*
* @return max number of columns;
* a result of zero means that there is no limit or the limit is not known
* @exception SQLException if a database access error occurs
*/
public int getMaxColumnsInIndex() throws SQLException {
return 0; //I don't know
}
/**
* What's the maximum number of columns in an "ORDER BY" clause?
*
* @return max number of columns;
* a result of zero means that there is no limit or the limit is not known
* @exception SQLException if a database access error occurs
*/
public int getMaxColumnsInOrderBy() throws SQLException {
return 0; //I don't know
}
/**
* What's the maximum number of columns in a "SELECT" list?
*
* @return max number of columns;
* a result of zero means that there is no limit or the limit is not known
* @exception SQLException if a database access error occurs
*/
public int getMaxColumnsInSelect() throws SQLException {
return 0; //I don't know
}
/**
* What's the maximum number of columns in a table?
*
* @return max number of columns;
* a result of zero means that there is no limit or the limit is not known
* @exception SQLException if a database access error occurs
*/
public int getMaxColumnsInTable() throws SQLException {
return 32767; // Depends on datatypes and sizes, at most 64 kbyte excluding blobs (but including blob ids)
}
/**
* How many active connections can we have at a time to this database?
*
* @return max number of active connections;
* a result of zero means that there is no limit or the limit is not known
* @exception SQLException if a database access error occurs
*/
public int getMaxConnections() throws SQLException {
return 0; //I don't know
}
/**
* What's the maximum cursor name length?
*
* @return max cursor name length in bytes;
* a result of zero means that there is no limit or the limit is not known
* @exception SQLException if a database access error occurs
*/
public int getMaxCursorNameLength() throws SQLException {
return 31;
}
/**
* Retrieves the maximum number of bytes for an index, including all
* of the parts of the index.
*
* @return max index length in bytes, which includes the composite of all
* the constituent parts of the index;
* a result of zero means that there is no limit or the limit is not known
* @exception SQLException if a database access error occurs
*/
public int getMaxIndexLength() throws SQLException {
if (gdsHelper.compareToVersion(2, 0) < 0) {
return 252; // See http://www.firebirdsql.org/en/firebird-technical-specifications/
} else {
return 0; // 1/4 of page size, maybe retrieve page size and use that?
}
}
/**
* What's the maximum length allowed for a schema name?
*
* @return max name length in bytes;
* a result of zero means that there is no limit or the limit is not known
* @exception SQLException if a database access error occurs
*/
public int getMaxSchemaNameLength() throws SQLException {
return 0; //No schemas
}
/**
* What's the maximum length of a procedure name?
*
* @return max name length in bytes;
* a result of zero means that there is no limit or the limit is not known
* @exception SQLException if a database access error occurs
*/
public int getMaxProcedureNameLength() throws SQLException {
return 31;
}
/**
* What's the maximum length of a catalog name?
*
* @return max name length in bytes;
* a result of zero means that there is no limit or the limit is not known
* @exception SQLException if a database access error occurs
*/
public int getMaxCatalogNameLength() throws SQLException {
return 0; //No catalogs
}
/**
* What's the maximum length of a single row?
*
* @return max row size in bytes;
* a result of zero means that there is no limit or the limit is not known
* @exception SQLException if a database access error occurs
*/
public int getMaxRowSize() throws SQLException {
if (gdsHelper.compareToVersion(1, 5) >= 0)
return 65531;
else
return 0;
}
/**
* Did getMaxRowSize() include LONGVARCHAR and LONGVARBINARY
* blobs?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean doesMaxRowSizeIncludeBlobs() throws SQLException {
return false; // Blob sizes are not included in rowsize
}
/**
* What's the maximum length of an SQL statement?
*
* @return max length in bytes;
* a result of zero means that there is no limit or the limit is not known
* @exception SQLException if a database access error occurs
*/
public int getMaxStatementLength() throws SQLException {
return 65536;
}
/**
* How many active statements can we have open at one time to this
* database?
*
* @return the maximum number of statements that can be open at one time;
* a result of zero means that there is no limit or the limit is not known
* @exception SQLException if a database access error occurs
*/
public int getMaxStatements() throws SQLException {
return 0;
}
/**
* What's the maximum length of a table name?
*
* @return max name length in bytes;
* a result of zero means that there is no limit or the limit is not known
* @exception SQLException if a database access error occurs
*/
public int getMaxTableNameLength() throws SQLException {
return 31;
}
/**
* What's the maximum number of tables in a SELECT statement?
*
* @return the maximum number of tables allowed in a SELECT statement;
* a result of zero means that there is no limit or the limit is not known
* @exception SQLException if a database access error occurs
*/
public int getMaxTablesInSelect() throws SQLException {
return 0;
}
/**
* What's the maximum length of a user name?
*
* @return max user name length in bytes;
* a result of zero means that there is no limit or the limit is not known
* @exception SQLException if a database access error occurs
*/
public int getMaxUserNameLength() throws SQLException {
return 31;//I don't know??
}
//----------------------------------------------------------------------
/**
* What's the database's default transaction isolation level? The
* values are defined in java.sql.Connection
.
*
* @return the default isolation level
* @exception SQLException if a database access error occurs
* @see Connection
*/
public int getDefaultTransactionIsolation() throws SQLException {
return Connection.TRANSACTION_READ_COMMITTED;//close enough to snapshot.
}
/**
* Are transactions supported? If not, invoking the method
* commit
is a noop and the
* isolation level is TRANSACTION_NONE.
*
* @return true
if transactions are supported; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsTransactions() throws SQLException {
return true;
}
/**
* Does this database support the given transaction isolation level?
*
* @param level the values are defined in java.sql.Connection
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
* @see Connection
*/
public boolean supportsTransactionIsolationLevel(int level) throws SQLException {
switch (level) {
case Connection.TRANSACTION_NONE: return false;
case Connection.TRANSACTION_READ_COMMITTED: return true;//true soon
case Connection.TRANSACTION_READ_UNCOMMITTED: return false;
case Connection.TRANSACTION_REPEATABLE_READ: return true;//??
case Connection.TRANSACTION_SERIALIZABLE: return true;//????
default: return false;
}
}
/**
* Are both data definition and data manipulation statements
* within a transaction supported?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsDataDefinitionAndDataManipulationTransactions() throws SQLException {
return true;//but not on the tables you defined in the transaction!
}
/**
* Are only data manipulation statements within a transaction
* supported?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsDataManipulationTransactionsOnly() throws SQLException {
return false;
}
/**
* Does a data definition statement within a transaction force the
* transaction to commit?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean dataDefinitionCausesTransactionCommit() throws SQLException {
return false;//but you can't use the table till the transaction is committed.
}
/**
* Is a data definition statement within a transaction ignored?
*
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean dataDefinitionIgnoredInTransactions() throws SQLException {
return false;
}
private static final String GET_PROCEDURES_START = "select "
+ " RDB$PROCEDURE_NAME as PROCEDURE_NAME,"
+ " RDB$DESCRIPTION as REMARKS,"
+ " RDB$PROCEDURE_OUTPUTS as PROCEDURE_TYPE "
+ "from"
+ " RDB$PROCEDURES "
+ "where ";
private static final String GET_PROCEDURES_END = "1 = 1 order by 1";
/**
* Gets a description of the stored procedures available in a
* catalog.
*
*
Only procedure descriptions matching the schema and
* procedure name criteria are returned. They are ordered by
* PROCEDURE_SCHEM, and PROCEDURE_NAME.
*
*
Each procedure description has the the following columns:
*
* - PROCEDURE_CAT String => procedure catalog (may be null)
*
- PROCEDURE_SCHEM String => procedure schema (may be null)
*
- PROCEDURE_NAME String => procedure name
*
- reserved for future use
*
- reserved for future use
*
- reserved for future use
*
- REMARKS String => explanatory comment on the procedure
*
- PROCEDURE_TYPE short => kind of procedure:
*
* - procedureResultUnknown - May return a result
*
- procedureNoResult - Does not return a result
*
- procedureReturnsResult - Returns a result
*
* - SPECIFIC_NAME String => The name which uniquely identifies this procedure within its schema.
*
*
* @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 procedureNamePattern a procedure name pattern
* @return ResultSet
- each row is a procedure description
* @exception SQLException if a database access error occurs
* @see #getSearchStringEscape
*/
public ResultSet getProcedures(String catalog, String schemaPattern,
String procedureNamePattern) throws SQLException {
checkCatalogAndSchema(catalog, schemaPattern);
if (procedureNamePattern == null || procedureNamePattern.equals("")) {
procedureNamePattern = "%";
}
XSQLVAR[] xsqlvars = new XSQLVAR[9];
xsqlvars[0] = new XSQLVAR();
xsqlvars[0].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[0].sqllen = 31;
xsqlvars[0].sqlname = "PROCEDURE_CAT";
xsqlvars[0].relname = "RDB$PROCEDURES";
xsqlvars[1] = new XSQLVAR();
xsqlvars[1].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[1].sqllen = 31;
xsqlvars[1].sqlname = "PROCEDURE_SCHEM";
xsqlvars[1].relname = "RDB$PROCEDURES";
xsqlvars[2] = new XSQLVAR();
xsqlvars[2].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[2].sqllen = 31;
xsqlvars[2].sqlname = "PROCEDURE_NAME";
xsqlvars[2].relname = "RDB$PROCEDURES";
xsqlvars[3] = new XSQLVAR();
xsqlvars[3].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[3].sqllen = 31;
xsqlvars[3].sqlname = "FUTURE1";
xsqlvars[3].relname = "RDB$PROCEDURES";
xsqlvars[4] = new XSQLVAR();
xsqlvars[4].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[4].sqllen = 31;
xsqlvars[4].sqlname = "FUTURE2";
xsqlvars[4].relname = "RDB$PROCEDURES";
xsqlvars[5] = new XSQLVAR();
xsqlvars[5].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[5].sqllen = 31;
xsqlvars[5].sqlname = "FUTURE3";
xsqlvars[5].relname = "RDB$PROCEDURES";
xsqlvars[6] = new XSQLVAR();
xsqlvars[6].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[6].sqllen = 80; // gets updated if there are longer remarks.
xsqlvars[6].sqlname = "REMARKS";
xsqlvars[6].relname = "RDB$PROCEDURES";
xsqlvars[7] = new XSQLVAR();
xsqlvars[7].sqltype = ISCConstants.SQL_SHORT;
xsqlvars[7].sqlname = "PROCEDURE_TYPE";
xsqlvars[7].relname = "RDB$PROCEDURES";
xsqlvars[8] = new XSQLVAR();
xsqlvars[8].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[8].sqllen = 31;
xsqlvars[8].sqlname = "SPECIFIC_NAME";
xsqlvars[8].relname = "RDB$PROCEDURES";
Clause procedureClause = new Clause("RDB$PROCEDURE_NAME", procedureNamePattern);
String sql = GET_PROCEDURES_START;
sql += procedureClause.getCondition();
sql += GET_PROCEDURES_END;
// check the original case identifiers first
ArrayList params = new ArrayList();
if (!procedureClause.getCondition().equals("")) {
params.add(procedureClause.getOriginalCaseValue());
}
ResultSet rs = doQuery(sql, params);
ArrayList rows = new ArrayList();
// if nothing found, check the uppercased identifiers
if (!rs.next()) {
params.clear();
if (!procedureClause.getCondition().equals("")) {
params.add(procedureClause.getValue());
}
rs = doQuery(sql, params);
// if nothing found, return an empty result set
if (!rs.next())
return new FBResultSet(xsqlvars, rows);
}
do {
byte[][] row = new byte[9][];
row[0] = null;
row[1] = null;
row[2] = getBytes(rs.getString("PROCEDURE_NAME"));
row[3] = null;
row[4] = null;
row[5] = null;
String remarks = rs.getString("REMARKS");
row[6] = getBytes(remarks);
if (remarks != null && remarks.length() > xsqlvars[6].sqllen)
xsqlvars[6].sqllen = remarks.length();
short procedureType = rs.getShort("PROCEDURE_TYPE");
row[7] = (procedureType == 0) ? xsqlvars[0].encodeShort((short)procedureNoResult) : xsqlvars[0].encodeShort((short)procedureReturnsResult);
row[8] = row[2];
rows.add(row);
} while (rs.next());
return new FBResultSet(xsqlvars, rows);
}
private static final String GET_PROCEDURE_COLUMNS_START = "select"
+ " PP.RDB$PROCEDURE_NAME as PROCEDURE_NAME,"
+ " PP.RDB$PARAMETER_NAME as COLUMN_NAME,"
+ " PP.RDB$PARAMETER_TYPE as COLUMN_TYPE,"
+ " F.RDB$FIELD_TYPE as FIELD_TYPE,"
+ " F.RDB$FIELD_SUB_TYPE as FIELD_SUB_TYPE,"
+ " F.RDB$FIELD_PRECISION as FIELD_PRECISION,"
+ " F.RDB$FIELD_SCALE as FIELD_SCALE,"
+ " F.RDB$FIELD_LENGTH as FIELD_LENGTH,"
+ " F.RDB$NULL_FLAG as NULL_FLAG,"
+ " PP.RDB$DESCRIPTION as REMARKS,"
+ " F.RDB$CHARACTER_LENGTH AS CHAR_LEN,"
+ " PP.RDB$PARAMETER_NUMBER + 1 AS PARAMETER_NUMBER "
+ "from"
+ " RDB$PROCEDURE_PARAMETERS PP,"
+ " RDB$FIELDS F "
+ "where ";
private static final String GET_PROCEDURE_COLUMNS_END = " PP.RDB$FIELD_SOURCE = F.RDB$FIELD_NAME "
+ "order by"
+ " PP.RDB$PROCEDURE_NAME,"
+ " PP.RDB$PARAMETER_TYPE desc,"
+ " PP.RDB$PARAMETER_NUMBER ";
/**
* 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:
*
* - PROCEDURE_CAT String => procedure catalog (may be
null
)
* - PROCEDURE_SCHEM String => procedure schema (may be
null
)
* - PROCEDURE_NAME String => procedure name
*
- COLUMN_NAME String => column/parameter name
*
- COLUMN_TYPE Short => 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
*
* - DATA_TYPE int => SQL type from java.sql.Types
*
- TYPE_NAME String => SQL type name, for a UDT type the
* type name is fully qualified
*
- PRECISION int => precision
*
- LENGTH int => length in bytes of data
*
- SCALE short => scale - null is returned for data types where
* SCALE is not applicable.
*
- RADIX short => radix
*
- NULLABLE short => can it contain NULL.
*
* - procedureNoNulls - does not allow NULL values
*
- procedureNullable - allows NULL values
*
- procedureNullableUnknown - nullability unknown
*
* - REMARKS String => comment describing parameter/column
*
- COLUMN_DEF String => 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
*
* - SQL_DATA_TYPE int => reserved for future use
*
- SQL_DATETIME_SUB int => reserved for future use
*
- CHAR_OCTET_LENGTH int => the maximum length of binary and character based columns. For any other datatype the returned value is a
* NULL
*
- ORDINAL_POSITION int => 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.
*
- IS_NULLABLE String => ISO rules are used to determine the nullability for a column.
*
* - YES --- if the parameter can include NULLs
*
- NO --- if the parameter cannot include NULLs
*
- empty string --- if the nullability for the
* parameter is unknown
*
* - SPECIFIC_NAME String => 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
* @exception SQLException if a database access error occurs
* @see #getSearchStringEscape
*/
public ResultSet getProcedureColumns(String catalog,
String schemaPattern,
String procedureNamePattern,
String columnNamePattern) throws SQLException {
checkCatalogAndSchema(catalog, schemaPattern);
XSQLVAR[] xsqlvars = new XSQLVAR[20];
xsqlvars[0] = new XSQLVAR();
xsqlvars[0].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[0].sqllen = 31;
xsqlvars[0].sqlname = "PROCEDURE_CAT";
xsqlvars[0].relname = "COLUMNINFO";
xsqlvars[1] = new XSQLVAR();
xsqlvars[1].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[1].sqllen = 31;
xsqlvars[1].sqlname = "PROCEDURE_SCHEM";
xsqlvars[1].relname = "COLUMNINFO";
xsqlvars[2] = new XSQLVAR();
xsqlvars[2].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[2].sqllen = 31;
xsqlvars[2].sqlname = "PROCEDURE_NAME";
xsqlvars[2].relname = "COLUMNINFO";
xsqlvars[3] = new XSQLVAR();
xsqlvars[3].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[3].sqllen = 31;
xsqlvars[3].sqlname = "COLUMN_NAME";
xsqlvars[3].relname = "COLUMNINFO";
xsqlvars[4] = new XSQLVAR();
xsqlvars[4].sqltype = ISCConstants.SQL_SHORT;
xsqlvars[4].sqlname = "COLUMN_TYPE";
xsqlvars[4].relname = "COLUMNINFO";
xsqlvars[5] = new XSQLVAR();
xsqlvars[5].sqltype = ISCConstants.SQL_LONG;
xsqlvars[5].sqlname = "DATA_TYPE";
xsqlvars[5].relname = "COLUMNINFO";
xsqlvars[6] = new XSQLVAR();
xsqlvars[6].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[6].sqllen = 31;
xsqlvars[6].sqlname = "TYPE_NAME";
xsqlvars[6].relname = "COLUMNINFO";
xsqlvars[7] = new XSQLVAR();
xsqlvars[7].sqltype = ISCConstants.SQL_LONG;
xsqlvars[7].sqlname = "PRECISION";
xsqlvars[7].relname = "COLUMNINFO";
xsqlvars[8] = new XSQLVAR();
xsqlvars[8].sqltype = ISCConstants.SQL_LONG;
xsqlvars[8].sqlname = "LENGTH";
xsqlvars[8].relname = "COLUMNINFO";
xsqlvars[9] = new XSQLVAR();
xsqlvars[9].sqltype = ISCConstants.SQL_SHORT;
xsqlvars[9].sqlname = "SCALE";
xsqlvars[9].relname = "COLUMNINFO";
xsqlvars[10] = new XSQLVAR();
xsqlvars[10].sqltype = ISCConstants.SQL_SHORT;
xsqlvars[10].sqlname = "RADIX";
xsqlvars[10].relname = "COLUMNINFO";
xsqlvars[11] = new XSQLVAR();
xsqlvars[11].sqltype = ISCConstants.SQL_SHORT;
xsqlvars[11].sqlname = "NULLABLE";
xsqlvars[11].relname = "COLUMNINFO";
xsqlvars[12] = new XSQLVAR();
xsqlvars[12].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[12].sqllen = 80; // gets updated if we get a longer description
xsqlvars[12].sqlname = "REMARKS";
xsqlvars[12].relname = "COLUMNINFO";
xsqlvars[13] = new XSQLVAR();
xsqlvars[13].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[13].sqllen = 31;
xsqlvars[13].sqlname = "COLUMN_DEF";
xsqlvars[13].relname = "COLUMNINFO";
xsqlvars[14] = new XSQLVAR();
xsqlvars[14].sqltype = ISCConstants.SQL_LONG;
xsqlvars[14].sqlname = "SQL_DATA_TYPE";
xsqlvars[14].relname = "COLUMNINFO";
xsqlvars[15] = new XSQLVAR();
xsqlvars[15].sqltype = ISCConstants.SQL_LONG;
xsqlvars[15].sqlname = "SQL_DATETIME_SUB";
xsqlvars[15].relname = "COLUMNINFO";
xsqlvars[16] = new XSQLVAR();
xsqlvars[16].sqltype = ISCConstants.SQL_LONG;
xsqlvars[16].sqlname = "CHAR_OCTET_LENGTH";
xsqlvars[16].relname = "COLUMNINFO";
xsqlvars[17] = new XSQLVAR();
xsqlvars[17].sqltype = ISCConstants.SQL_LONG;
xsqlvars[17].sqlname = "ORDINAL_POSITION";
xsqlvars[17].relname = "COLUMNINFO";
xsqlvars[18] = new XSQLVAR();
xsqlvars[18].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[18].sqllen = 3;
xsqlvars[18].sqlname = "IS_NULLABLE";
xsqlvars[18].relname = "COLUMNINFO";
xsqlvars[19] = new XSQLVAR();
xsqlvars[19].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[19].sqllen = 31;
xsqlvars[19].sqlname = "SPECIFIC_NAME";
xsqlvars[19].relname = "COLUMNINFO";
Clause procedureClause = new Clause("PP.RDB$PROCEDURE_NAME", procedureNamePattern);
Clause columnClause = new Clause("PP.RDB$PARAMETER_NAME", columnNamePattern);
String sql = GET_PROCEDURE_COLUMNS_START;
sql += procedureClause.getCondition();
sql += columnClause.getCondition();
sql += GET_PROCEDURE_COLUMNS_END;
// check the original case identifiers first
ArrayList params = new ArrayList();
if (!procedureClause.getCondition().equals("")) {
params.add(procedureClause.getOriginalCaseValue());
}
if (!columnClause.getCondition().equals("")) {
params.add(columnClause.getOriginalCaseValue());
}
ResultSet rs = doQuery(sql, params);
ArrayList rows = new ArrayList();
// if nothing found, check the uppercased identifiers
if (!rs.next()) {
params.clear();
if (!procedureClause.getCondition().equals("")) {
params.add(procedureClause.getValue());
}
if (!columnClause.getCondition().equals("")) {
params.add(columnClause.getValue());
}
rs = doQuery(sql, params);
// if nothing found, return an empty result set
if (!rs.next())
return new FBResultSet(xsqlvars, rows);
}
do {
byte[][] row = new byte[20][];
row[0] = null;
row[1] = null;
row[2] = getBytes(rs.getString("PROCEDURE_NAME"));
row[3] = getBytes(rs.getString("COLUMN_NAME"));
short columnType = rs.getShort("COLUMN_TYPE");
// TODO: Unsure if procedureColumnOut is correct, maybe procedureColumnResult, or need ODS dependent use of RDB$PROCEDURE_TYPE to decide on selectable or executable?
// TODO: ResultSet columns should not be first according to JDBC 4.1 description
row[4] = (columnType == 0) ? xsqlvars[0].encodeShort((short)procedureColumnIn) : xsqlvars[0].encodeShort((short)procedureColumnOut);
short fieldType = rs.getShort("FIELD_TYPE");
short fieldSubType = rs.getShort("FIELD_SUB_TYPE");
short fieldScale = rs.getShort("FIELD_SCALE");
int dataType = getDataType(fieldType, fieldSubType, fieldScale);
row[5] = xsqlvars[0].encodeInt(dataType);
row[6] = getBytes(getDataTypeName(fieldType, fieldSubType, fieldScale));
row[8] = xsqlvars[0].encodeInt(rs.getShort("FIELD_LENGTH"));
// Defaults: some are overridden in the switch
row[7] = null;
row[9] = null;
row[10] = null;
row[16] = null;
switch (dataType){
case Types.DECIMAL:
case Types.NUMERIC:
row[7] = xsqlvars[0].encodeInt(rs.getShort("FIELD_PRECISION"));
row[9] = xsqlvars[0].encodeShort((short)(fieldScale * (-1)));
row[10] = xsqlvars[0].encodeShort((short)10);
break;
case Types.CHAR:
case Types.VARCHAR:
short charLen = rs.getShort("CHAR_LEN");
if (!rs.wasNull()) {
row[7] = xsqlvars[0].encodeInt(charLen);
} else {
row[7] = row[8];
}
row[16] = row[8];
break;
case Types.FLOAT:
row[7] = xsqlvars[0].encodeInt(7);
row[10] = xsqlvars[0].encodeShort((short)10);
break;
case Types.DOUBLE:
row[7] = xsqlvars[0].encodeInt(15);
row[10] = xsqlvars[0].encodeShort((short)10);
break;
case Types.BIGINT:
row[7] = xsqlvars[0].encodeInt(19);
row[9] = xsqlvars[0].encodeShort((short)0);
row[10] = xsqlvars[0].encodeShort((short)10);
break;
case Types.INTEGER:
row[7] = xsqlvars[0].encodeInt(10);
row[9] = xsqlvars[0].encodeShort((short)0);
row[10] = xsqlvars[0].encodeShort((short)10);
break;
case Types.SMALLINT:
row[7] = xsqlvars[0].encodeInt(5);
row[9] = xsqlvars[0].encodeShort((short)0);
row[10] = xsqlvars[0].encodeShort((short)10);
break;
case Types.DATE:
row[7] = xsqlvars[0].encodeInt(10);
break;
case Types.TIME:
row[7] = xsqlvars[0].encodeInt(8);
break;
case Types.TIMESTAMP:
row[7] = xsqlvars[0].encodeInt(19);
break;
default:
row[7] = null;
}
// TODO: Find out what the difference is with NULL_FLAG in RDB$PROCEDURE_PARAMETERS (might be ODS dependent)
short nullFlag = rs.getShort("NULL_FLAG");
row[11] = (nullFlag == 1) ? xsqlvars[0].encodeShort((short)procedureNoNulls) :
xsqlvars[0].encodeShort((short)procedureNullable);
String remarks = rs.getString("REMARKS");
row[12] = getBytes(remarks);
if (remarks != null && remarks.length() > xsqlvars[12].sqllen)
xsqlvars[12].sqllen = remarks.length();
// TODO: Need to write ODS version dependent method to retrieve some of the info
row[13] = null; // TODO From 2.0 defaults for procedure parameters
row[14] = null;
row[15] = null;
// TODO: Find correct value for ORDINAL_POSITION (+ order of columns and intent, see JDBC-229)
row[17] = xsqlvars[0].encodeInt(rs.getInt("PARAMETER_NUMBER"));
// TODO: Find out if there is a conceptual difference with NULLABLE (idx 11)
row[18] = (nullFlag == 1) ? getBytes("NO") : getBytes("YES");
row[19] = row[2];
rows.add(row);
} while (rs.next());
return new FBResultSet(xsqlvars, rows);
}
// TODO: Include GLOBAL TEMPORARY
public static final String TABLE = "TABLE";
public static final String SYSTEM_TABLE = "SYSTEM TABLE";
public static final String VIEW = "VIEW";
public static final String[] ALL_TYPES = {TABLE, SYSTEM_TABLE, VIEW};
private static final String TABLE_COLUMNS_FORMAT =
" select cast(null as varchar(31)) as TABLE_CAT,"
+ " cast(null as varchar(31)) as TABLE_SCHEM,"
+ " RDB$RELATION_NAME as TABLE_NAME,"
+ " cast('%s' as varchar(31)) as TABLE_TYPE,"
+ " RDB$DESCRIPTION as REMARKS,"
+ " cast(null as varchar(31)) as TYPE_CAT,"
+ " cast(null as varchar(31)) as TYPE_SCHEM,"
+ " cast(null as varchar(31)) as TYPE_NAME,"
+ " cast(null as varchar(31)) as SELF_REFERENCING_COL_NAME,"
+ " cast(null as varchar(31)) as REF_GENERATION,"
+ " RDB$OWNER_NAME as OWNER_NAME"
+ " from RDB$RELATIONS";
private static final String TABLE_COLUMNS_SYSTEM =
String.format(TABLE_COLUMNS_FORMAT, SYSTEM_TABLE);
private static final String TABLE_COLUMNS_NORMAL =
String.format(TABLE_COLUMNS_FORMAT, TABLE);
private static final String TABLE_COLUMNS_VIEW =
String.format(TABLE_COLUMNS_FORMAT, VIEW);
private static final String GET_TABLES_ALL =
TABLE_COLUMNS_SYSTEM
+ " where ? = 'T' and RDB$SYSTEM_FLAG = 1 and RDB$VIEW_SOURCE is null"
+ " union"
+ TABLE_COLUMNS_NORMAL
+ " where ? = 'T' and RDB$SYSTEM_FLAG = 0 and RDB$VIEW_SOURCE is null"
+ " union"
+ TABLE_COLUMNS_VIEW
+ " where ? = 'T' and RDB$VIEW_SOURCE is not null "
+ " order by 3 ";
private static final String GET_TABLES_EXACT =
TABLE_COLUMNS_SYSTEM
+ " where ? = 'T' and RDB$SYSTEM_FLAG = 1 and RDB$VIEW_SOURCE is null"
+ " and ? = RDB$RELATION_NAME"
+ " union"
+ TABLE_COLUMNS_NORMAL
+ " where ? = 'T' and RDB$SYSTEM_FLAG = 0 and RDB$VIEW_SOURCE is null"
+ " and ? = RDB$RELATION_NAME"
+ " union"
+ TABLE_COLUMNS_VIEW
+ " where ? = 'T' and RDB$VIEW_SOURCE is not null"
+ " and ? = RDB$RELATION_NAME";
private static final String GET_TABLES_LIKE =
TABLE_COLUMNS_SYSTEM
+ " where ? = 'T' and RDB$SYSTEM_FLAG = 1 and RDB$VIEW_SOURCE is null"
+ " and RDB$RELATION_NAME || '" + SPACES + "' like ? escape '\\'"
+ " union"
+ TABLE_COLUMNS_NORMAL
+ " where ? = 'T' and RDB$SYSTEM_FLAG = 0 and RDB$VIEW_SOURCE is null"
+ " and RDB$RELATION_NAME || '" + SPACES + "' like ? escape '\\'"
+ " union"
+ TABLE_COLUMNS_VIEW
+ " where ? = 'T' and RDB$VIEW_SOURCE is not null"
+ " and RDB$RELATION_NAME || '" + SPACES + "' like ? escape '\\' "
+ " order by 3 ";
/**
* 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:
*
* - TABLE_CAT String => table catalog (may be
null
)
* - TABLE_SCHEM String => table schema (may be
null
)
* - TABLE_NAME String => table name
*
- TABLE_TYPE String => table type. Typical types are "TABLE",
* "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY",
* "LOCAL TEMPORARY", "ALIAS", "SYNONYM".
*
- REMARKS String => explanatory comment on the table
*
- TYPE_CAT String => the types catalog (may be
null
)
* - TYPE_SCHEM String => the types schema (may be
null
)
* - TYPE_NAME String => type name (may be
null
)
* - SELF_REFERENCING_COL_NAME String => name of the designated
* "identifier" column of a typed table (may be
null
)
* - REF_GENERATION String => specifies how values in
* SELF_REFERENCING_COL_NAME are created. Values are
* "SYSTEM", "USER", "DERIVED". (may be
null
)
* - OWNER_NAME String => Username of the owner of the table (Jaybird-specific)
*
*
* 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
* @exception SQLException if a database access error occurs
* @see #getSearchStringEscape
*/
public ResultSet getTables(String catalog, String schemaPattern,
String tableNamePattern, String types[]) throws SQLException {
if (tableNamePattern == null || "".equals(tableNamePattern))
tableNamePattern = "%";
checkCatalogAndSchema(catalog, schemaPattern);
if (types == null) {
types = ALL_TYPES;
}
String sql;
ArrayList params = new ArrayList();
if (isAllCondition(tableNamePattern)) {
sql = GET_TABLES_ALL;
params.add(getWantsSystemTables(types));
params.add(getWantsTables(types));
params.add(getWantsViews(types));
}
else if (hasNoWildcards(tableNamePattern)) {
tableNamePattern = stripQuotes(stripEscape(tableNamePattern), true);
sql = GET_TABLES_EXACT;
params.add(getWantsSystemTables(types));
params.add(tableNamePattern);
params.add(getWantsTables(types));
params.add(tableNamePattern);
params.add(getWantsViews(types));
params.add(tableNamePattern);
}
else {
// TODO Usages of 1) uppercase and 2) SPACES + % might be wrong
tableNamePattern = stripQuotes(tableNamePattern, true) + SPACES + "%";
sql = GET_TABLES_LIKE;
params.add(getWantsSystemTables(types));
params.add(tableNamePattern);
params.add(getWantsTables(types));
params.add(tableNamePattern);
params.add(getWantsViews(types));
params.add(tableNamePattern);
}
return doQuery(sql, params);
}
/**
* Gets the schema names available in this database. The results
* are ordered by schema name.
*
*
The schema columns are:
*
* - TABLE_SCHEM String => schema name
*
- TABLE_CATALOG String => catalog name (may be
null
)
*
*
* @return ResultSet
- each row has a single String column that is a
* schema name
* @exception SQLException if a database access error occurs
*/
public ResultSet getSchemas() throws SQLException {
return getSchemas(null, null);
}
/**
* Gets the catalog names available in this database. The results
* are ordered by catalog name.
*
* The catalog column is:
*
* - TABLE_CAT String => catalog name
*
*
* @return ResultSet
- each row has a single String column that is a
* catalog name
* @exception SQLException if a database access error occurs
*/
public ResultSet getCatalogs() throws SQLException {
XSQLVAR[] xsqlvars = new XSQLVAR[1];
xsqlvars[0] = new XSQLVAR();
xsqlvars[0].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[0].sqllen = 31;
xsqlvars[0].sqlname = "TABLE_CAT";
xsqlvars[0].relname = "TABLECATALOGS";
ArrayList rows = new ArrayList(0);
return new FBResultSet(xsqlvars, rows);
}
/**
* Gets the table types available in this database. The results
* are ordered by table type.
*
* The table type is:
*
* - TABLE_TYPE String => table type. Typical types are "TABLE",
* "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY",
* "LOCAL TEMPORARY", "ALIAS", "SYNONYM".
*
*
* @return ResultSet
- each row has a single String column that is a
* table type
* @exception SQLException if a database access error occurs
*/
public ResultSet getTableTypes() throws SQLException {
XSQLVAR[] xsqlvars = new XSQLVAR[1];
xsqlvars[0] = new XSQLVAR();
xsqlvars[0].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[0].sqllen = 31;
xsqlvars[0].sqlname = "TABLE_TYPE";
xsqlvars[0].relname = "TABLETYPES";
ArrayList rows = new ArrayList(ALL_TYPES.length);
for(int i = 0; i < ALL_TYPES.length; i++)
rows.add(new byte[][] {getBytes(ALL_TYPES[i])});
return new FBResultSet(xsqlvars, rows);
}
private static final String GET_COLUMNS_START =
"SELECT RF.RDB$RELATION_NAME AS RELATION_NAME ," +
" RF.RDB$FIELD_NAME AS FIELD_NAME ," +
" F.RDB$FIELD_TYPE AS FIELD_TYPE ," +
" F.RDB$FIELD_SUB_TYPE AS FIELD_SUB_TYPE ," +
" F.RDB$FIELD_PRECISION AS FIELD_PRECISION ," +
" F.RDB$FIELD_SCALE AS FIELD_SCALE ," +
" F.RDB$FIELD_LENGTH AS FIELD_LENGTH ," +
" F.RDB$CHARACTER_LENGTH AS CHAR_LEN ," +
" RF.RDB$DESCRIPTION AS REMARKS ," +
" RF.RDB$DEFAULT_SOURCE AS DEFAULT_SOURCE ," +
" RF.RDB$FIELD_POSITION + 1 AS FIELD_POSITION ," +
" RF.RDB$NULL_FLAG AS NULL_FLAG ," +
" F.RDB$NULL_FLAG AS SOURCE_NULL_FLAG," +
" F.RDB$COMPUTED_BLR AS COMPUTED_BLR " +
"FROM RDB$RELATION_FIELDS RF," +
" RDB$FIELDS F " +
"WHERE ";
public static final String GET_COLUMNS_END = " RF.RDB$FIELD_SOURCE = F.RDB$FIELD_NAME " +
"order by 1, 11";
/**
* 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:
*
* - TABLE_CAT String => table catalog (may be
null
)
* - TABLE_SCHEM String => table schema (may be
null
)
* - TABLE_NAME String => table name
*
- COLUMN_NAME String => column name
*
- DATA_TYPE int => SQL type from java.sql.Types
*
- TYPE_NAME String => Data source dependent type name,
* for a UDT the type name is fully qualified
*
- COLUMN_SIZE int => column size.
*
- BUFFER_LENGTH is not used.
*
- DECIMAL_DIGITS int => the number of fractional digits. Null is returned for data types where
* DECIMAL_DIGITS is not applicable.
*
- NUM_PREC_RADIX int => Radix (typically either 10 or 2)
*
- NULLABLE int => is NULL allowed.
*
* - columnNoNulls - might not allow
NULL
values
* - columnNullable - definitely allows
NULL
values
* - columnNullableUnknown - nullability unknown
*
* - REMARKS String => comment describing column (may be
null
)
* - COLUMN_DEF String => default value for the column, which should be interpreted as a string when the value is enclosed in single quotes (may be
null
)
* - SQL_DATA_TYPE int => unused
*
- SQL_DATETIME_SUB int => unused
*
- CHAR_OCTET_LENGTH int => for char types the
* maximum number of bytes in the column
*
- ORDINAL_POSITION int => index of column in table
* (starting at 1)
*
- IS_NULLABLE String => 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
*
* - SCOPE_CATALOG String => catalog of table that is the scope
* of a reference attribute (
null
if DATA_TYPE isn't REF)
* - SCOPE_SCHEMA String => schema of table that is the scope
* of a reference attribute (
null
if the DATA_TYPE isn't REF)
* - SCOPE_TABLE String => table name that this the scope
* of a reference attribute (
null
if the DATA_TYPE isn't REF)
* - SOURCE_DATA_TYPE short => 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)
* - IS_AUTOINCREMENT String => 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
*
* - IS_GENERATEDCOLUMN String => 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
* @exception SQLException if a database access error occurs
* @see #getSearchStringEscape
*/
public ResultSet getColumns(String catalog, String schemaPattern,
String tableNamePattern, String columnNamePattern) throws SQLException {
checkCatalogAndSchema(catalog, schemaPattern);
XSQLVAR[] xsqlvars = new XSQLVAR[24];
xsqlvars[0] = new XSQLVAR();
xsqlvars[0].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[0].sqllen = 31;
xsqlvars[0].sqlname = "TABLE_CAT";
xsqlvars[0].relname = "COLUMNINFO";
xsqlvars[1] = new XSQLVAR();
xsqlvars[1].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[1].sqllen = 31;
xsqlvars[1].sqlname = "TABLE_SCHEM";
xsqlvars[1].relname = "COLUMNINFO";
xsqlvars[2] = new XSQLVAR();
xsqlvars[2].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[2].sqllen = 31;
xsqlvars[2].sqlname = "TABLE_NAME";
xsqlvars[2].relname = "COLUMNINFO";
xsqlvars[3] = new XSQLVAR();
xsqlvars[3].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[3].sqllen = 31;
xsqlvars[3].sqlname = "COLUMN_NAME";
xsqlvars[3].relname = "COLUMNINFO";
xsqlvars[4] = new XSQLVAR();
xsqlvars[4].sqltype = ISCConstants.SQL_LONG;
xsqlvars[4].sqlname = "DATA_TYPE";
xsqlvars[4].relname = "COLUMNINFO";
xsqlvars[5] = new XSQLVAR();
xsqlvars[5].sqltype = ISCConstants.SQL_VARYING | 1;
xsqlvars[5].sqllen = 31;
xsqlvars[5].sqlname = "TYPE_NAME";
xsqlvars[5].relname = "COLUMNINFO";
xsqlvars[6] = new XSQLVAR();
xsqlvars[6].sqltype = ISCConstants.SQL_LONG;
xsqlvars[6].sqlname = "COLUMN_SIZE";
xsqlvars[6].relname = "COLUMNINFO";
xsqlvars[7] = new XSQLVAR();
xsqlvars[7].sqltype = ISCConstants.SQL_LONG;
xsqlvars[7].sqlname = "BUFFER_LENGTH";
xsqlvars[7].relname = "COLUMNINFO";
xsqlvars[8] = new XSQLVAR();
xsqlvars[8].sqltype = ISCConstants.SQL_LONG;
xsqlvars[8].sqlname = "DECIMAL_DIGITS";
xsqlvars[8].relname = "COLUMNINFO";
xsqlvars[9] = new XSQLVAR();
xsqlvars[9].sqltype = ISCConstants.SQL_LONG;
xsqlvars[9].sqlname = "NUM_PREC_RADIX";
xsqlvars[9].relname = "COLUMNINFO";
xsqlvars[10] = new XSQLVAR();
xsqlvars[10].sqltype = ISCConstants.SQL_LONG;
xsqlvars[10].sqlname = "NULLABLE";
xsqlvars[10].relname = "COLUMNINFO";
xsqlvars[11] = new XSQLVAR();
xsqlvars[11].sqltype = ISCConstants.SQL_VARYING | 1;
xsqlvars[11].sqllen = 80;
xsqlvars[11].sqlname = "REMARKS";
xsqlvars[11].relname = "COLUMNINFO";
xsqlvars[12] = new XSQLVAR();
xsqlvars[12].sqltype = ISCConstants.SQL_VARYING | 1;
xsqlvars[12].sqllen = 31;
xsqlvars[12].sqlname = "COLUMN_DEF";
xsqlvars[12].relname = "COLUMNINFO";
xsqlvars[13] = new XSQLVAR();
xsqlvars[13].sqltype = ISCConstants.SQL_LONG;
xsqlvars[13].sqlname = "SQL_DATA_TYPE";
xsqlvars[13].relname = "COLUMNINFO";
xsqlvars[14] = new XSQLVAR();
xsqlvars[14].sqltype = ISCConstants.SQL_LONG;
xsqlvars[14].sqlname = "SQL_DATETIME_SUB";
xsqlvars[14].relname = "COLUMNINFO";
xsqlvars[15] = new XSQLVAR();
xsqlvars[15].sqltype = ISCConstants.SQL_LONG;
xsqlvars[15].sqlname = "CHAR_OCTET_LENGTH";
xsqlvars[15].relname = "COLUMNINFO";
xsqlvars[16] = new XSQLVAR();
xsqlvars[16].sqltype = ISCConstants.SQL_LONG;
xsqlvars[16].sqlname = "ORDINAL_POSITION";
xsqlvars[16].relname = "COLUMNINFO";
xsqlvars[17] = new XSQLVAR();
xsqlvars[17].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[17].sqllen = 3;
xsqlvars[17].sqlname = "IS_NULLABLE";
xsqlvars[17].relname = "COLUMNINFO";
xsqlvars[18] = new XSQLVAR();
xsqlvars[18].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[18].sqllen = 31;
if (getJDBCMajorVersion() > 4 || getJDBCMajorVersion() == 4 && getJDBCMinorVersion() >= 1) {
xsqlvars[18].sqlname = "SCOPE_CATALOG";
} else {
xsqlvars[18].sqlname = "SCOPE_CATLOG";
}
xsqlvars[18].relname = "COLUMNINFO";
xsqlvars[19] = new XSQLVAR();
xsqlvars[19].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[19].sqllen = 31;
xsqlvars[19].sqlname = "SCOPE_SCHEMA";
xsqlvars[19].relname = "COLUMNINFO";
xsqlvars[20] = new XSQLVAR();
xsqlvars[20].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[20].sqllen = 31;
xsqlvars[20].sqlname = "SCOPE_TABLE";
xsqlvars[20].relname = "COLUMNINFO";
xsqlvars[21] = new XSQLVAR();
xsqlvars[21].sqltype = ISCConstants.SQL_SHORT;
xsqlvars[21].sqlname = "SOURCE_DATA_TYPE";
xsqlvars[21].relname = "COLUMNINFO";
xsqlvars[22] = new XSQLVAR();
xsqlvars[22].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[22].sqllen = 3;
xsqlvars[22].sqlname = "IS_AUTOINCREMENT";
xsqlvars[22].relname = "COLUMNINFO";
xsqlvars[23] = new XSQLVAR();
xsqlvars[23].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[23].sqllen = 3;
xsqlvars[23].sqlname = "IS_GENERATEDCOLUMN";
xsqlvars[23].relname = "COLUMNINFO";
Clause tableClause = new Clause("RF.RDB$RELATION_NAME", tableNamePattern);
Clause columnClause = new Clause("RF.RDB$FIELD_NAME", columnNamePattern);
String sql = GET_COLUMNS_START;
sql += tableClause.getCondition();
sql += columnClause.getCondition();
sql += GET_COLUMNS_END;
ArrayList params = new ArrayList();
// check first original case values
if (!tableClause.getCondition().equals("")) {
params.add(tableClause.getOriginalCaseValue());
}
if (!columnClause.getCondition().equals("")) {
params.add(columnClause.getOriginalCaseValue());
}
ResultSet rs = doQuery(sql, params);
ArrayList rows = new ArrayList();
// if no direct match happened, check the uppercased match
if (!rs.next()) {
params.clear();
if (!tableClause.getCondition().equals("")) {
params.add(tableClause.getValue());
}
if (!columnClause.getCondition().equals("")) {
params.add(columnClause.getValue());
}
rs = doQuery(sql, params);
// open the second result set and check whether we have rows
// if no rows are available, we have to exit now, otherwise the
// following do/while loop will throw SQLException that the
// result set is not positioned on a row
if (!rs.next())
return new FBResultSet(xsqlvars, rows);
}
do {
byte[][] row = new byte[24][];
row[0] = null;
row[1] = null;
row[2] = getBytes(rs.getString("RELATION_NAME"));
row[3] = getBytes(rs.getString("FIELD_NAME"));
short fieldType = rs.getShort("FIELD_TYPE");
short fieldSubType = rs.getShort("FIELD_SUB_TYPE");
short fieldScale = rs.getShort("FIELD_SCALE");
int dataType = getDataType(fieldType, fieldSubType, fieldScale);
row[4] = xsqlvars[0].encodeInt(dataType);
row[5] = getBytes(getDataTypeName(fieldType, fieldSubType, fieldScale));
row[7] = null;
// Defaults: some are overridden in the switch
row[8] = null;
row[9] = null;
row[15] = null;
switch (dataType){
case Types.DECIMAL:
case Types.NUMERIC:
row[6] = xsqlvars[0].encodeInt(rs.getShort("FIELD_PRECISION"));
row[8] = xsqlvars[0].encodeInt(fieldScale * (-1));
row[9] = xsqlvars[0].encodeInt(10);
break;
case Types.CHAR:
case Types.VARCHAR:
row[15] = xsqlvars[0].encodeInt(rs.getShort("FIELD_LENGTH"));
short charLen = rs.getShort("CHAR_LEN");
if (!rs.wasNull()) {
row[6] = xsqlvars[0].encodeInt(charLen);
} else {
row[6] = row[15];
}
break;
case Types.FLOAT:
row[6] = xsqlvars[0].encodeInt(7);
row[9] = xsqlvars[0].encodeInt(10);
break;
case Types.DOUBLE:
row[6] = xsqlvars[0].encodeInt(15);
row[9] = xsqlvars[0].encodeInt(10);
break;
case Types.BIGINT:
row[6] = xsqlvars[0].encodeInt(19);
row[8] = xsqlvars[0].encodeInt(0);
row[9] = xsqlvars[0].encodeInt(10);
break;
case Types.INTEGER:
row[6] = xsqlvars[0].encodeInt(10);
row[8] = xsqlvars[0].encodeInt(0);
row[9] = xsqlvars[0].encodeInt(10);
break;
case Types.SMALLINT:
row[6] = xsqlvars[0].encodeInt(5);
row[8] = xsqlvars[0].encodeInt(0);
row[9] = xsqlvars[0].encodeInt(10);
break;
case Types.DATE:
row[6] = xsqlvars[0].encodeInt(10);
break;
case Types.TIME:
row[6] = xsqlvars[0].encodeInt(8);
break;
case Types.TIMESTAMP:
row[6] = xsqlvars[0].encodeInt(19);
break;
default:
row[6] = null;
}
short nullFlag = rs.getShort("NULL_FLAG");
short sourceNullFlag = rs.getShort("SOURCE_NULL_FLAG");
row[10] = (nullFlag == 1 || sourceNullFlag == 1) ?
xsqlvars[0].encodeInt(columnNoNulls) :
xsqlvars[0].encodeInt(columnNullable);
String remarks = rs.getString("REMARKS");
row[11] = getBytes(remarks);
if (remarks != null && remarks.length() > xsqlvars[11].sqllen)
xsqlvars[11].sqllen = remarks.length();
String column_def = rs.getString("DEFAULT_SOURCE");
if (column_def != null) {
int defaultPos = column_def.toUpperCase().indexOf("DEFAULT");
if (defaultPos >= 0)
column_def = column_def.substring(7).trim();
row[12] = getBytes(column_def);
} else
row[12] = null;
row[13] = null;
row[14] = null;
row[16] = xsqlvars[0].encodeInt(rs.getInt("FIELD_POSITION"));
row[17] = (nullFlag == 1 || sourceNullFlag == 1) ?
getBytes("NO") : getBytes("YES");
row[18] = null;
row[19] = null;
row[20] = null;
row[21] = null;
switch (dataType) {
case Types.INTEGER:
case Types.TINYINT:
case Types.BIGINT:
case Types.SMALLINT:
// Could be autoincrement, but we simply don't know
row[22] = getBytes("");
break;
case Types.NUMERIC:
case Types.DECIMAL:
if (fieldScale == 0) {
// Could be autoincrement, but we simply don't know
row[22] = getBytes("");
} else {
// Scaled NUMERIC/DECIMAL: definitely not autoincrement
row[22] = getBytes("NO");
}
break;
default:
// All other types are never autoincrement
row[22] = getBytes("NO");
}
rs.getString("COMPUTED_BLR");
row[23] = getBytes(rs.wasNull() ? "NO" : "YES");
rows.add(row);
} while (rs.next());
return new FBResultSet(xsqlvars, rows);
}
private static final short smallint_type = 7;
private static final short integer_type = 8;
private static final short quad_type = 9;
private static final short float_type = 10;
private static final short d_float_type = 11;
private static final short date_type = 12;
private static final short time_type = 13;
private static final short char_type = 14;
private static final short int64_type = 16;
private static final short double_type = 27;
private static final short timestamp_type = 35;
private static final short varchar_type = 37;
// private static final short cstring_type = 40;
private static final short blob_type = 261;
private static int getDataType (short fieldType, short fieldSubType, short fieldScale) {
switch (fieldType) {
case smallint_type:
if (fieldSubType == 1 || (fieldSubType == 0 && fieldScale < 0))
return Types.NUMERIC;
else if (fieldSubType == 2)
return Types.DECIMAL;
else
return Types.SMALLINT;
case integer_type:
if (fieldSubType == 1 || (fieldSubType == 0 && fieldScale < 0))
return Types.NUMERIC;
else if (fieldSubType == 2)
return Types.DECIMAL;
else
return Types.INTEGER;
case double_type:
case d_float_type:
return Types.DOUBLE;
case float_type:
return Types.FLOAT;
case char_type:
return Types.CHAR;
case varchar_type:
return Types.VARCHAR;
case timestamp_type:
return Types.TIMESTAMP;
case time_type:
return Types.TIME;
case date_type:
return Types.DATE;
case int64_type:
if (fieldSubType == 1 || (fieldSubType == 0 && fieldScale < 0))
return Types.NUMERIC;
else if (fieldSubType == 2)
return Types.DECIMAL;
else
return Types.BIGINT;
case blob_type:
if (fieldSubType < 0)
return Types.BLOB;
else if (fieldSubType == 0)
return Types.LONGVARBINARY;
else if (fieldSubType == 1)
return Types.LONGVARCHAR;
else
return Types.OTHER;
case quad_type:
return Types.OTHER;
default:
return Types.NULL;
}
}
private static String getDataTypeName(short sqltype, short sqlsubtype, short sqlscale) {
switch (sqltype) {
case smallint_type:
if (sqlsubtype == 1 || (sqlsubtype == 0 && sqlscale < 0))
return "NUMERIC";
else if (sqlsubtype == 2)
return "DECIMAL";
else
return "SMALLINT";
case integer_type:
if (sqlsubtype == 1 || (sqlsubtype == 0 && sqlscale < 0))
return "NUMERIC";
else if (sqlsubtype == 2)
return "DECIMAL";
else
return "INTEGER";
case double_type:
case d_float_type:
return "DOUBLE PRECISION";
case float_type:
return "FLOAT";
case char_type:
return "CHAR";
case varchar_type:
return "VARCHAR";
case timestamp_type:
return "TIMESTAMP";
case time_type:
return "TIME";
case date_type:
return "DATE";
case int64_type:
if (sqlsubtype == 1 || (sqlsubtype == 0 && sqlscale < 0))
return "NUMERIC";
else if (sqlsubtype == 2)
return "DECIMAL";
else
return "BIGINT";
case blob_type:
if (sqlsubtype < 0)
return "BLOB SUB_TYPE <0";
else if (sqlsubtype == 0)
return "BLOB SUB_TYPE 0";
else if (sqlsubtype == 1)
return "BLOB SUB_TYPE 1";
else
return "BLOB SUB_TYPE " + sqlsubtype;
case quad_type:
return "ARRAY";
default:
return "NULL";
}
}
private static final String GET_COLUMN_PRIVILEGES_START = "select "
+ "null as TABLE_CAT,"
+ "null as TABLE_SCHEM,"
+ "RF.RDB$RELATION_NAME as TABLE_NAME, "
+ "RF.RDB$FIELD_NAME as COLUMN_NAME, "
+ "UP.RDB$GRANTOR as GRANTOR, "
+ "UP.RDB$USER as GRANTEE, "
+ "UP.RDB$PRIVILEGE as PRIVILEGE, "
+ "UP.RDB$GRANT_OPTION as IS_GRANTABLE "
+ "from "
+ "RDB$RELATION_FIELDS RF, "
+ "RDB$FIELDS F, "
+ "RDB$USER_PRIVILEGES UP "
+ "where "
+ "RF.RDB$RELATION_NAME = UP.RDB$RELATION_NAME and "
+ "RF.RDB$FIELD_SOURCE = F.RDB$FIELD_NAME and "
+ "(UP.RDB$FIELD_NAME is null or "
+ "UP.RDB$FIELD_NAME = RF.RDB$FIELD_NAME) and "
+ "UP.RDB$RELATION_NAME = ? and ((";
private static final String GET_COLUMN_PRIVILEGES_END = " UP.RDB$OBJECT_TYPE = 0) or "
+ "(RF.RDB$FIELD_NAME is null and UP.RDB$OBJECT_TYPE = 0)) "
+ "order by 4,7 ";
/**
* Gets a description of the access rights for a table's columns.
*
*
Only privileges matching the column name criteria are
* returned. They are ordered by COLUMN_NAME and PRIVILEGE.
*
*
Each privilige description has the following columns:
*
* - TABLE_CAT String => table catalog (may be null)
*
- TABLE_SCHEM String => table schema (may be null)
*
- TABLE_NAME String => table name
*
- COLUMN_NAME String => column name
*
- GRANTOR => grantor of access (may be null)
*
- GRANTEE String => grantee of access
*
- PRIVILEGE String => name of access (SELECT,
* INSERT, UPDATE, REFRENCES, ...)
*
- IS_GRANTABLE String => "YES" if grantee is permitted
* to grant to others; "NO" if not; null if unknown
*
*
* @param catalog a catalog name; "" retrieves those without a
* catalog; null means drop catalog name from the selection criteria
* @param schema a schema name; "" retrieves those without a schema
* @param table a table name
* @param columnNamePattern a column name pattern
* @return ResultSet
- each row is a column privilege description
* @exception SQLException if a database access error occurs
* @see #getSearchStringEscape
*/
public ResultSet getColumnPrivileges(String catalog, String schema,
String table, String columnNamePattern) throws SQLException {
checkCatalogAndSchema(catalog, schema);
XSQLVAR[] xsqlvars = new XSQLVAR[8];
xsqlvars[0] = new XSQLVAR();
xsqlvars[0].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[0].sqllen = 31;
xsqlvars[0].sqlname = "TABLE_CAT";
xsqlvars[0].relname = "COLUMNPRIV";
xsqlvars[1] = new XSQLVAR();
xsqlvars[1].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[1].sqllen = 31;
xsqlvars[1].sqlname = "TABLE_SCHEM";
xsqlvars[1].relname = "COLUMNPRIV";
xsqlvars[2] = new XSQLVAR();
xsqlvars[2].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[2].sqllen = 31;
xsqlvars[2].sqlname = "TABLE_NAME";
xsqlvars[2].relname = "COLUMNPRIV";
xsqlvars[3] = new XSQLVAR();
xsqlvars[3].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[3].sqllen = 31;
xsqlvars[3].sqlname = "COLUMN_NAME";
xsqlvars[3].relname = "COLUMNPRIV";
xsqlvars[4] = new XSQLVAR();
xsqlvars[4].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[4].sqllen = 31;
xsqlvars[4].sqlname = "GRANTOR";
xsqlvars[4].relname = "COLUMNPRIV";
xsqlvars[5] = new XSQLVAR();
xsqlvars[5].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[5].sqllen = 31;
xsqlvars[5].sqlname = "GRANTEE";
xsqlvars[5].relname = "COLUMNPRIV";
xsqlvars[6] = new XSQLVAR();
xsqlvars[6].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[6].sqllen = 31;
xsqlvars[6].sqlname = "PRIVILEGE";
xsqlvars[6].relname = "COLUMNPRIV";
xsqlvars[7] = new XSQLVAR();
xsqlvars[7].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[7].sqllen = 31;
xsqlvars[7].sqlname = "IS_GRANTABLE";
xsqlvars[7].relname = "COLUMNPRIV";
Clause columnClause = new Clause("RF.RDB$FIELD_NAME", columnNamePattern);
String sql = GET_COLUMN_PRIVILEGES_START;
sql += columnClause.getCondition();
sql += GET_COLUMN_PRIVILEGES_END;
ArrayList params = new ArrayList();
// check the original case first
table = stripQuotes(stripEscape(table), false);
params.add(table);
if (!columnClause.getCondition().equals("")) {
params.add(columnClause.getOriginalCaseValue());
}
ArrayList rows = new ArrayList();
ResultSet rs = doQuery(sql, params);
// if nothing was found, check the uppercased identifiers
if (!rs.next()) {
params.clear();
if (!columnClause.getCondition().equals("")) {
params.add(stripQuotes(stripEscape(table), true));
params.add(columnClause.getValue());
}
rs = doQuery(sql, params);
// return empty result set
if (!rs.next())
return new FBResultSet(xsqlvars, rows);
}
do {
byte[][] row = new byte[8][];
row[0] = null;
row[1] = null;
row[2] = getBytes(rs.getString("TABLE_NAME"));
row[3] = getBytes(rs.getString("COLUMN_NAME"));
row[4] = getBytes(rs.getString("GRANTOR"));
row[5] = getBytes(rs.getString("GRANTEE"));
String privilege = rs.getString("PRIVILEGE");
if (privilege.equals("A"))
row[6] = getBytes("ALL");
else if (privilege.equals("S"))
row[6] = getBytes("SELECT");
else if (privilege.equals("D"))
row[6] = getBytes("DELETE");
else if (privilege.equals("I"))
row[6] = getBytes("INSERT");
else if (privilege.equals("U"))
row[6] = getBytes("UPDATE");
else if (privilege.equals("R"))
row[6] = getBytes("REFERENCE");
else if (privilege.equals("M"))
row[6] = getBytes("MEMBEROF");
int isGrantable = rs.getShort("IS_GRANTABLE");
if (isGrantable==0)
row[7] = getBytes("NO");
else
row[7] = getBytes("YES");
rows.add(row);
} while(rs.next());
return new FBResultSet(xsqlvars, rows);
}
private static final String GET_TABLE_PRIVILEGES_START = "select"
+ " null as TABLE_CAT, "
+ " null as TABLE_SCHEM,"
+ " RDB$RELATION_NAME as TABLE_NAME,"
+ " RDB$GRANTOR as GRANTOR, "
+ " RDB$USER as GRANTEE, "
+ " RDB$PRIVILEGE as PRIVILEGE, "
+ " RDB$GRANT_OPTION as IS_GRANTABLE "
+ "from"
+ " RDB$USER_PRIVILEGES "
+ "where ";
private static final String GET_TABLE_PRIVILEGES_END = " RDB$OBJECT_TYPE = 0 and"
+ " RDB$FIELD_NAME is null "
+ "order by 3, 6";
/**
* Gets 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 priviledge 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_SCHEM,
* TABLE_NAME, and PRIVILEGE.
*
*
Each privilige description has the following columns:
*
* - TABLE_CAT String => table catalog (may be null)
*
- TABLE_SCHEM String => table schema (may be null)
*
- TABLE_NAME String => table name
*
- GRANTOR => grantor of access (may be null)
*
- GRANTEE String => grantee of access
*
- PRIVILEGE String => name of access (SELECT,
* INSERT, UPDATE, REFRENCES, ...)
*
- IS_GRANTABLE String => "YES" if grantee is permitted
* to grant to others; "NO" if not; null if unknown
*
*
* @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
* @return ResultSet
- each row is a table privilege description
* @exception SQLException if a database access error occurs
* @see #getSearchStringEscape
*/
public ResultSet getTablePrivileges(String catalog, String schemaPattern,
String tableNamePattern) throws SQLException {
checkCatalogAndSchema(catalog, schemaPattern);
tableNamePattern = stripQuotes(stripEscape(tableNamePattern), true);
XSQLVAR[] xsqlvars = buildTablePrivilegeRSMetaData();
Clause tableClause = new Clause("RDB$RELATION_NAME", tableNamePattern);
String sql = GET_TABLE_PRIVILEGES_START;
sql += tableClause.getCondition();
sql += GET_TABLE_PRIVILEGES_END;
// check the original case identifiers first
ArrayList params = new ArrayList();
if (!tableClause.getCondition().equals("")) {
params.add(tableClause.getOriginalCaseValue());
}
ResultSet rs = doQuery(sql, params);
// if nothing found, check the uppercased identifiers
if (!rs.next()) {
params.clear();
if (!tableClause.getCondition().equals("")) {
params.add(tableClause.getValue());
}
rs = doQuery(sql, params);
// if nothing found, return an empty result set
if (!rs.next())
return new FBResultSet(xsqlvars, new ArrayList());
}
return processTablePrivileges(xsqlvars, rs);
}
protected final XSQLVAR[] buildTablePrivilegeRSMetaData() {
XSQLVAR[] xsqlvars = new XSQLVAR[7];
xsqlvars[0] = new XSQLVAR();
xsqlvars[0].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[0].sqllen = 31;
xsqlvars[0].sqlname = "TABLE_CAT";
xsqlvars[0].relname = "TABLEPRIV";
xsqlvars[1] = new XSQLVAR();
xsqlvars[1].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[1].sqllen = 31;
xsqlvars[1].sqlname = "TABLE_SCHEM";
xsqlvars[1].relname = "TABLEPRIV";
xsqlvars[2] = new XSQLVAR();
xsqlvars[2].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[2].sqllen = 31;
xsqlvars[2].sqlname = "TABLE_NAME";
xsqlvars[2].relname = "TABLEPRIV";
xsqlvars[3] = new XSQLVAR();
xsqlvars[3].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[3].sqllen = 31;
xsqlvars[3].sqlname = "GRANTOR";
xsqlvars[3].relname = "TABLEPRIV";
xsqlvars[4] = new XSQLVAR();
xsqlvars[4].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[4].sqllen = 31;
xsqlvars[4].sqlname = "GRANTEE";
xsqlvars[4].relname = "TABLEPRIV";
xsqlvars[5] = new XSQLVAR();
xsqlvars[5].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[5].sqllen = 31;
xsqlvars[5].sqlname = "PRIVILEGE";
xsqlvars[5].relname = "TABLEPRIV";
xsqlvars[6] = new XSQLVAR();
xsqlvars[6].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[6].sqllen = 31;
xsqlvars[6].sqlname = "IS_GRANTABLE";
xsqlvars[6].relname = "TABLEPRIV";
return xsqlvars;
}
protected final FBResultSet processTablePrivileges(XSQLVAR[] xsqlvars, ResultSet fbTablePrivileges) throws SQLException {
ArrayList rows = new ArrayList();
do {
byte[][] row = new byte[7][];
row[0] = null;
row[1] = null;
row[2] = getBytes(fbTablePrivileges.getString("TABLE_NAME"));
row[3] = getBytes(fbTablePrivileges.getString("GRANTOR"));
row[4] = getBytes(fbTablePrivileges.getString("GRANTEE"));
String privilege = fbTablePrivileges.getString("PRIVILEGE");
if (privilege.equals("A"))
row[5] = getBytes("ALL");
else if (privilege.equals("S"))
row[5] = getBytes("SELECT");
else if (privilege.equals("D"))
row[5] = getBytes("DELETE");
else if (privilege.equals("I"))
row[5] = getBytes("INSERT");
else if (privilege.equals("U"))
row[5] = getBytes("UPDATE");
else if (privilege.equals("R"))
row[5] = getBytes("REFERENCE"); // TODO: JDBC spec specifies REFRENCES (yes: typo and + S)
else if (privilege.equals("M"))
row[5] = getBytes("MEMBEROF");
int isGrantable = fbTablePrivileges.getShort("IS_GRANTABLE");
if (isGrantable==0)
row[6] = getBytes("NO");
else
row[6] = getBytes("YES");
rows.add(row);
} while (fbTablePrivileges.next());
return new FBResultSet(xsqlvars, rows);
}
private static final String GET_BEST_ROW_IDENT = "" +
"select " +
" rf.rdb$field_name as column_name, " +
" f.rdb$field_type as field_type, " +
" f.rdb$field_sub_type as field_sub_type, " +
" f.rdb$field_scale as field_scale, " +
" f.rdb$field_precision as field_precision " +
"from " +
" rdb$relation_constraints rc," +
" rdb$index_segments idx," +
" rdb$relation_fields rf," +
" rdb$fields f " +
"where " +
" rc.rdb$relation_name = ? " +
"and " +
" rc.rdb$constraint_type = 'PRIMARY KEY' " +
"and " +
" idx.rdb$index_name = rc.rdb$index_name " +
"and " +
" rf.rdb$field_name = idx.rdb$field_name " +
"and " +
" rf.rdb$relation_name = ? " +
"and " +
" f.rdb$field_name = rf.rdb$field_source"
;
/**
* Gets 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:
*
* - SCOPE short => actual scope of result
*
* - bestRowTemporary - very temporary, while using row
*
- bestRowTransaction - valid for remainder of current transaction
*
- bestRowSession - valid for remainder of current session
*
* - COLUMN_NAME String => column name
*
- DATA_TYPE short => SQL data type from java.sql.Types
*
- TYPE_NAME String => Data source dependent type name,
* for a UDT the type name is fully qualified
*
- COLUMN_SIZE int => precision
*
- BUFFER_LENGTH int => not used
*
- DECIMAL_DIGITS short => scale
*
- PSEUDO_COLUMN short => 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
*
*
*
* @param catalog a catalog name; "" retrieves those without a
* catalog; null means drop catalog name from the selection criteria
* @param schema a schema name; "" retrieves those without a schema
* @param table a table name
* @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
* @exception SQLException if a database access error occurs
*/
public ResultSet getBestRowIdentifier(String catalog, String schema,
String table, int scope, boolean nullable) throws SQLException {
XSQLVAR[] xsqlvars = new XSQLVAR[8];
xsqlvars[0] = new XSQLVAR();
xsqlvars[0].sqltype = ISCConstants.SQL_SHORT;
xsqlvars[0].sqlname = "SCOPE";
xsqlvars[0].relname = "ROWIDENTIFIER";
xsqlvars[1] = new XSQLVAR();
xsqlvars[1].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[1].sqllen = 31;
xsqlvars[1].sqlname = "COLUMN_NAME";
xsqlvars[1].relname = "ROWIDENTIFIER";
xsqlvars[2] = new XSQLVAR();
xsqlvars[2].sqltype = ISCConstants.SQL_SHORT;
xsqlvars[2].sqlname = "DATA_TYPE";
xsqlvars[2].relname = "ROWIDENTIFIER";
xsqlvars[3] = new XSQLVAR();
xsqlvars[3].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[3].sqllen = 31;
xsqlvars[3].sqlname = "TYPE_NAME";
xsqlvars[3].relname = "ROWIDENTIFIER";
xsqlvars[4] = new XSQLVAR();
xsqlvars[4].sqltype = ISCConstants.SQL_LONG;
xsqlvars[4].sqlname = "COLUMN_SIZE";
xsqlvars[4].relname = "ROWIDENTIFIER";
xsqlvars[5] = new XSQLVAR();
xsqlvars[5].sqltype = ISCConstants.SQL_LONG;
xsqlvars[5].sqlname = "BUFFER_LENGTH";
xsqlvars[5].relname = "ROWIDENTIFIER";
xsqlvars[6] = new XSQLVAR();
xsqlvars[6].sqltype = ISCConstants.SQL_SHORT;
xsqlvars[6].sqlname = "DECIMAL_DIGITS";
xsqlvars[6].relname = "ROWIDENTIFIER";
xsqlvars[7] = new XSQLVAR();
xsqlvars[7].sqltype = ISCConstants.SQL_SHORT;
xsqlvars[7].sqlname = "PSEUDO_COLUMN";
xsqlvars[7].relname = "ROWIDENTIFIER";
ResultSet tables = getTables(catalog, schema, table, null);
if (!tables.next())
return new FBResultSet(xsqlvars, new ArrayList());
ArrayList rows = getPrimaryKeyIdentifier(tables.getString(3), scope, xsqlvars);
// if no primary key exists, add RDB$DB_KEY as pseudo-column
if (rows.size() == 0) {
byte[][] row = new byte[8][];
row[0] = xsqlvars[0].encodeShort((short)scope);
row[1] = getBytes("RDB$DB_KEY");
row[2] = xsqlvars[0].encodeShort((short)getDataType(char_type, (short)0, (short)0));
row[3] = getBytes(getDataTypeName(char_type, (short)0, (short)0));
row[4] = xsqlvars[0].encodeInt(0);
row[5] = null;
row[6] = xsqlvars[0].encodeShort((short)0);
row[7] = xsqlvars[0].encodeShort((short)bestRowPseudo);
rows.add(row);
}
return new FBResultSet(xsqlvars, rows);
}
/**
* Get primary key of the table as best row identifier.
*
* @param table name of the table.
* @param scope scope, we just include it in the result set.
* @param xsqlvars array of {@link XSQLVAR} instances describing result set.
*
* @return list of result set values, when size is 0, no primary key has
* been defined for a table.
*
* @throws SQLException if something went wrong.
*/
private ArrayList getPrimaryKeyIdentifier(String table, int scope, XSQLVAR[] xsqlvars) throws SQLException {
ArrayList rows = new ArrayList(0);
ArrayList params = new ArrayList(2);
params.add(table);
params.add(table);
ResultSet rs = doQuery(GET_BEST_ROW_IDENT, params);
while (rs.next()) {
byte[][] row = new byte[8][];
row[0] = xsqlvars[0].encodeShort((short)scope);
row[1] = getBytes(rs.getString("COLUMN_NAME"));
row[2] = xsqlvars[0].encodeShort((short)getDataType(rs.getShort("FIELD_TYPE"),
rs.getShort("FIELD_SUB_TYPE"), rs.getShort("FIELD_SCALE")));
row[3] = getBytes(getDataTypeName(rs.getShort("FIELD_TYPE"),
rs.getShort("FIELD_SUB_TYPE"), rs.getShort("FIELD_SCALE")));
row[4] = xsqlvars[0].encodeInt(rs.getInt("FIELD_PRECISION"));
row[5] = null;
row[6] = xsqlvars[0].encodeShort(rs.getShort("FIELD_SCALE"));
row[7] = xsqlvars[0].encodeShort((short)bestRowNotPseudo);
rows.add(row);
}
return rows;
}
/**
* Gets 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:
*
* - SCOPE short => is not used
*
- COLUMN_NAME String => column name
*
- DATA_TYPE short => SQL data type from java.sql.Types
*
- TYPE_NAME String => Data source dependent type name
*
- COLUMN_SIZE int => precision
*
- BUFFER_LENGTH int => length of column value in bytes
*
- DECIMAL_DIGITS short => scale
*
- PSEUDO_COLUMN short => is this a 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
*
*
*
* @param catalog a catalog name; "" retrieves those without a
* catalog; null means drop catalog name from the selection criteria
* @param schema a schema name; "" retrieves those without a schema
* @param table a table name
* @return ResultSet
- each row is a column description
* @exception SQLException if a database access error occurs
*/
public ResultSet getVersionColumns(String catalog, String schema,
String table) throws SQLException {
XSQLVAR[] xsqlvars = new XSQLVAR[8];
xsqlvars[0] = new XSQLVAR();
xsqlvars[0].sqltype = ISCConstants.SQL_SHORT;
xsqlvars[0].sqlname = "SCOPE";
xsqlvars[0].relname = "VERSIONCOL";
xsqlvars[1] = new XSQLVAR();
xsqlvars[1].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[1].sqllen = 31;
xsqlvars[1].sqlname = "COLUMN_NAME";
xsqlvars[1].relname = "VERSIONCOL";
xsqlvars[2] = new XSQLVAR();
xsqlvars[2].sqltype = ISCConstants.SQL_SHORT;
xsqlvars[2].sqlname = "DATA_TYPE";
xsqlvars[2].relname = "VERSIONCOL";
xsqlvars[3] = new XSQLVAR();
xsqlvars[3].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[3].sqllen = 31;
xsqlvars[3].sqlname = "TYPE_NAME";
xsqlvars[3].relname = "VERSIONCOL";
xsqlvars[4] = new XSQLVAR();
xsqlvars[4].sqltype = ISCConstants.SQL_LONG;
xsqlvars[4].sqlname = "COLUMN_SIZE";
xsqlvars[4].relname = "VERSIONCOL";
xsqlvars[5] = new XSQLVAR();
xsqlvars[5].sqltype = ISCConstants.SQL_LONG;
xsqlvars[5].sqlname = "BUFFER_LENGTH";
xsqlvars[5].relname = "VERSIONCOL";
xsqlvars[6] = new XSQLVAR();
xsqlvars[6].sqltype = ISCConstants.SQL_SHORT;
xsqlvars[6].sqlname = "DECIMAL_DIGITS";
xsqlvars[6].relname = "VERSIONCOL";
xsqlvars[7] = new XSQLVAR();
xsqlvars[7].sqltype = ISCConstants.SQL_SHORT;
xsqlvars[7].sqlname = "PSEUDO_COLUMN";
xsqlvars[7].relname = "VERSIONCOL";
ArrayList rows = new ArrayList(0);
return new FBResultSet(xsqlvars, rows);
}
private static final String GET_PRIMARY_KEYS_START = "select "
+ " null as TABLE_CAT, "
+ " null as TABLE_SCHEM, "
+ "RC.RDB$RELATION_NAME as TABLE_NAME, "
+ "ISGMT.RDB$FIELD_NAME as COLUMN_NAME, "
+ "CAST ((ISGMT.RDB$FIELD_POSITION + 1) as SMALLINT) as KEY_SEQ, "
+ "RC.RDB$CONSTRAINT_NAME as PK_NAME "
+ "from "
+ "RDB$RELATION_CONSTRAINTS RC, "
+ "RDB$INDEX_SEGMENTS ISGMT "
+ "where ";
private static final String GET_PRIMARY_KEYS_END =
"RC.RDB$INDEX_NAME = ISGMT.RDB$INDEX_NAME and "
+ "RC.RDB$CONSTRAINT_TYPE = 'PRIMARY KEY' "
+ "order by ISGMT.RDB$FIELD_NAME ";
/**
* Gets a description of a table's primary key columns. They
* are ordered by COLUMN_NAME.
*
* Each primary key column description has the following columns:
*
* - TABLE_CAT String => table catalog (may be null)
*
- TABLE_SCHEM String => table schema (may be null)
*
- TABLE_NAME String => table name
*
- COLUMN_NAME String => column name
*
- KEY_SEQ short => sequence number within primary key
*
- PK_NAME String => primary key name (may be null)
*
*
* @param catalog a catalog name; "" retrieves those without a
* catalog; null means drop catalog name from the selection criteria
* @param schema a schema name; "" retrieves those
* without a schema
* @param table a table name
* @return ResultSet
- each row is a primary key column description
* @exception SQLException if a database access error occurs
*/
public ResultSet getPrimaryKeys(String catalog, String schema,
String table) throws SQLException {
checkCatalogAndSchema(catalog, schema);
XSQLVAR[] xsqlvars = new XSQLVAR[6];
xsqlvars[0] = new XSQLVAR();
xsqlvars[0].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[0].sqllen = 31;
xsqlvars[0].sqlname = "TABLE_CAT";
xsqlvars[0].relname = "COLUMNINFO";
xsqlvars[1] = new XSQLVAR();
xsqlvars[1].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[1].sqllen = 31;
xsqlvars[1].sqlname = "TABLE_SCHEM";
xsqlvars[1].relname = "COLUMNINFO";
xsqlvars[2] = new XSQLVAR();
xsqlvars[2].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[2].sqllen = 31;
xsqlvars[2].sqlname = "TABLE_NAME";
xsqlvars[2].relname = "COLUMNINFO";
xsqlvars[3] = new XSQLVAR();
xsqlvars[3].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[3].sqllen = 31;
xsqlvars[3].sqlname = "COLUMN_NAME";
xsqlvars[3].relname = "COLUMNINFO";
xsqlvars[4] = new XSQLVAR();
xsqlvars[4].sqltype = ISCConstants.SQL_SHORT;
xsqlvars[4].sqlname = "KEY_SEQ";
xsqlvars[4].relname = "COLUMNINFO";
xsqlvars[5] = new XSQLVAR();
xsqlvars[5].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[5].sqllen = 31;
xsqlvars[5].sqlname = "PK_NAME";
xsqlvars[5].relname = "COLUMNINFO";
Clause tableClause = new Clause("RC.RDB$RELATION_NAME", table);
String sql = GET_PRIMARY_KEYS_START;
sql += tableClause.getCondition();
sql += GET_PRIMARY_KEYS_END;
// check the original case identifiers
ArrayList params = new ArrayList();
if (!tableClause.getCondition().equals("")) {
params.add(tableClause.getOriginalCaseValue());
}
ArrayList rows = new ArrayList();
ResultSet rs = doQuery(sql, params);
// if nothing found, check the uppercased identifier
if (!rs.next()) {
params.clear();
if (!tableClause.getCondition().equals("")) {
params.add(tableClause.getValue());
}
rs = doQuery(sql, params);
// if nothing found, return empty result set
if (!rs.next())
return new FBResultSet(xsqlvars, rows);
}
do {
byte[][] row = new byte[6][];
row[0] = null;
row[1] = null;
row[2] = getBytes(rs.getString("TABLE_NAME"));
row[3] = getBytes(rs.getString("COLUMN_NAME"));
row[4] = xsqlvars[0].encodeShort(rs.getShort("KEY_SEQ"));
row[5] = getBytes(rs.getString("PK_NAME"));
rows.add(row);
} while(rs.next());
return new FBResultSet(xsqlvars, rows);
}
private static final String GET_IMPORTED_KEYS_START = "select"
+" null as PKTABLE_CAT "
+" ,null as PKTABLE_SCHEM "
+" ,PK.RDB$RELATION_NAME as PKTABLE_NAME "
+" ,ISP.RDB$FIELD_NAME as PKCOLUMN_NAME "
+" ,null as FKTABLE_CAT "
+" ,null as FKTABLE_SCHEM "
+" ,FK.RDB$RELATION_NAME as FKTABLE_NAME "
+" ,ISF.RDB$FIELD_NAME as FKCOLUMN_NAME "
+" ,CAST ((ISP.RDB$FIELD_POSITION + 1) as SMALLINT) as KEY_SEQ "
+" ,RC.RDB$UPDATE_RULE as UPDATE_RULE "
+" ,RC.RDB$DELETE_RULE as DELETE_RULE "
+" ,PK.RDB$CONSTRAINT_NAME as PK_NAME "
+" ,FK.RDB$CONSTRAINT_NAME as FK_NAME "
+" ,null as DEFERRABILITY "
+" from "
+" RDB$RELATION_CONSTRAINTS PK "
+" ,RDB$RELATION_CONSTRAINTS FK "
+" ,RDB$REF_CONSTRAINTS RC "
+" ,RDB$INDEX_SEGMENTS ISP "
+" ,RDB$INDEX_SEGMENTS ISF "
+" WHERE ";
private static final String GET_IMPORTED_KEYS_END =
" FK.RDB$CONSTRAINT_NAME = RC.RDB$CONSTRAINT_NAME "
+" and PK.RDB$CONSTRAINT_NAME = RC.RDB$CONST_NAME_UQ "
+" and ISP.RDB$INDEX_NAME = PK.RDB$INDEX_NAME "
+" and ISF.RDB$INDEX_NAME = FK.RDB$INDEX_NAME "
+" and ISP.RDB$FIELD_POSITION = ISF.RDB$FIELD_POSITION "
+" order by 3, 9 ";
/**
* Gets a description of the primary key columns that are
* referenced by a table's foreign key columns (the primary keys
* imported by a table). They are ordered by PKTABLE_CAT,
* PKTABLE_SCHEM, PKTABLE_NAME, and KEY_SEQ.
*
* Each primary key column description has the following columns:
*
* - PKTABLE_CAT String => primary key table catalog
* being imported (may be null)
*
- PKTABLE_SCHEM String => primary key table schema
* being imported (may be null)
*
- PKTABLE_NAME String => primary key table name
* being imported
*
- PKCOLUMN_NAME String => primary key column name
* being imported
*
- FKTABLE_CAT String => foreign key table catalog (may be null)
*
- FKTABLE_SCHEM String => foreign key table schema (may be null)
*
- FKTABLE_NAME String => foreign key table name
*
- FKCOLUMN_NAME String => foreign key column name
*
- KEY_SEQ short => sequence number within foreign key
*
- UPDATE_RULE short => 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)
*
* - DELETE_RULE short => 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
*
* - FK_NAME String => foreign key name (may be null)
*
- PK_NAME String => primary key name (may be null)
*
- DEFERRABILITY short => 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; "" retrieves those without a
* catalog; null means drop catalog name from the selection criteria
* @param schema a schema name; "" retrieves those
* without a schema
* @param table a table name
* @return ResultSet
- each row is a primary key column description
* @exception SQLException if a database access error occurs
* @see #getExportedKeys
*/
public ResultSet getImportedKeys(String catalog, String schema,
String table) throws SQLException {
checkCatalogAndSchema(catalog, schema);
XSQLVAR[] xsqlvars = new XSQLVAR[14];
xsqlvars[0] = new XSQLVAR();
xsqlvars[0].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[0].sqllen = 31;
xsqlvars[0].sqlname = "PKTABLE_CAT";
xsqlvars[0].relname = "COLUMNINFO";
xsqlvars[1] = new XSQLVAR();
xsqlvars[1].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[1].sqllen = 31;
xsqlvars[1].sqlname = "PKTABLE_SCHEM";
xsqlvars[1].relname = "COLUMNINFO";
xsqlvars[2] = new XSQLVAR();
xsqlvars[2].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[2].sqllen = 31;
xsqlvars[2].sqlname = "PKTABLE_NAME";
xsqlvars[2].relname = "COLUMNINFO";
xsqlvars[3] = new XSQLVAR();
xsqlvars[3].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[3].sqllen = 31;
xsqlvars[3].sqlname = "PKCOLUMN_NAME";
xsqlvars[3].relname = "COLUMNINFO";
xsqlvars[4] = new XSQLVAR();
xsqlvars[4].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[4].sqllen = 31;
xsqlvars[4].sqlname = "FKTABLE_CAT";
xsqlvars[4].relname = "COLUMNINFO";
xsqlvars[5] = new XSQLVAR();
xsqlvars[5].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[5].sqllen = 31;
xsqlvars[5].sqlname = "FKTABLE_SCHEM";
xsqlvars[5].relname = "COLUMNINFO";
xsqlvars[6] = new XSQLVAR();
xsqlvars[6].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[6].sqllen = 31;
xsqlvars[6].sqlname = "FKTABLE_NAME";
xsqlvars[6].relname = "COLUMNINFO";
xsqlvars[7] = new XSQLVAR();
xsqlvars[7].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[7].sqllen = 31;
xsqlvars[7].sqlname = "FKCOLUMN_NAME";
xsqlvars[7].relname = "COLUMNINFO";
xsqlvars[8] = new XSQLVAR();
xsqlvars[8].sqltype = ISCConstants.SQL_SHORT;
xsqlvars[8].sqlname = "KEY_SEQ";
xsqlvars[8].relname = "COLUMNINFO";
xsqlvars[9] = new XSQLVAR();
xsqlvars[9].sqltype = ISCConstants.SQL_SHORT;
xsqlvars[9].sqlname = "UPDATE_RULE";
xsqlvars[9].relname = "COLUMNINFO";
xsqlvars[10] = new XSQLVAR();
xsqlvars[10].sqltype = ISCConstants.SQL_SHORT;
xsqlvars[10].sqlname = "DELETE_RULE";
xsqlvars[10].relname = "COLUMNINFO";
xsqlvars[11] = new XSQLVAR();
xsqlvars[11].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[11].sqllen = 31;
xsqlvars[11].sqlname = "FK_NAME";
xsqlvars[11].relname = "COLUMNINFO";
xsqlvars[12] = new XSQLVAR();
xsqlvars[12].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[12].sqllen = 31;
xsqlvars[12].sqlname = "PK_NAME";
xsqlvars[12].relname = "COLUMNINFO";
xsqlvars[13] = new XSQLVAR();
xsqlvars[13].sqltype = ISCConstants.SQL_SHORT;
xsqlvars[13].sqlname = "DEFERRABILITY";
xsqlvars[13].relname = "COLUMNINFO";
Clause tableClause = new Clause("FK.RDB$RELATION_NAME", table);
String sql = GET_IMPORTED_KEYS_START;
sql += tableClause.getCondition();
sql += GET_IMPORTED_KEYS_END;
// check the original case identifiers first
ArrayList params = new ArrayList();
if (!tableClause.getCondition().equals("")) {
params.add(tableClause.getOriginalCaseValue());
}
ArrayList rows = new ArrayList();
ResultSet rs = doQuery(sql, params);
// if nothing found, check the uppercased identifiers
if (!rs.next()) {
params.clear();
if (!tableClause.getCondition().equals("")) {
params.add(tableClause.getValue());
}
rs = doQuery(sql, params);
// if nothing found, return an empty result set
if (!rs.next())
return new FBResultSet(xsqlvars, rows);
}
do {
byte[][] row = new byte[14][];
row[0] = null;
row[1] = null;
row[2] = getBytes(rs.getString("PKTABLE_NAME"));
row[3] = getBytes(rs.getString("PKCOLUMN_NAME"));
row[4] = null;
row[5] = null;
row[6] = getBytes(rs.getString("FKTABLE_NAME"));
row[7] = getBytes(rs.getString("FKCOLUMN_NAME"));
row[8] = xsqlvars[0].encodeShort(rs.getShort("KEY_SEQ"));
String updateRule = rs.getString("UPDATE_RULE");
if (updateRule.equals("NO ACTION") || updateRule.equals("RESTRICT"))
row[9] = xsqlvars[0].encodeShort((short) DatabaseMetaData.importedKeyNoAction);
else if (updateRule.equals("CASCADE"))
row[9] = xsqlvars[0].encodeShort((short) DatabaseMetaData.importedKeyCascade);
else if (updateRule.equals("SET NULL"))
row[9] = xsqlvars[0].encodeShort((short) DatabaseMetaData.importedKeySetNull);
else if (updateRule.equals("SET DEFAULT"))
row[9] = xsqlvars[0].encodeShort((short) DatabaseMetaData.importedKeySetDefault);
String deleteRule = rs.getString("DELETE_RULE");
if (deleteRule.equals("NO ACTION") || deleteRule.equals("RESTRICT"))
row[10] = xsqlvars[0].encodeShort((short) DatabaseMetaData.importedKeyNoAction);
else if (deleteRule.equals("CASCADE"))
row[10] = xsqlvars[0].encodeShort((short) DatabaseMetaData.importedKeyCascade);
else if (deleteRule.equals("SET NULL"))
row[10] = xsqlvars[0].encodeShort((short) DatabaseMetaData.importedKeySetNull);
else if (deleteRule.equals("SET DEFAULT"))
row[10] = xsqlvars[0].encodeShort((short) DatabaseMetaData.importedKeySetDefault);
row[11] = getBytes(rs.getString("FK_NAME"));
row[12] = getBytes(rs.getString("PK_NAME"));
row[13] = xsqlvars[0].encodeShort((short) DatabaseMetaData.importedKeyNotDeferrable);
rows.add(row);
} while (rs.next());
return new FBResultSet(xsqlvars, rows);
}
private static final String GET_EXPORTED_KEYS_START = "select"
+" null as PKTABLE_CAT "
+" ,null as PKTABLE_SCHEM "
+" ,PK.RDB$RELATION_NAME as PKTABLE_NAME "
+" ,ISP.RDB$FIELD_NAME as PKCOLUMN_NAME "
+" ,null as FKTABLE_CAT "
+" ,null as FKTABLE_SCHEM "
+" ,FK.RDB$RELATION_NAME as FKTABLE_NAME "
+" ,ISF.RDB$FIELD_NAME as FKCOLUMN_NAME "
+" ,CAST ((ISP.RDB$FIELD_POSITION + 1) as SMALLINT) as KEY_SEQ "
+" ,RC.RDB$UPDATE_RULE as UPDATE_RULE "
+" ,RC.RDB$DELETE_RULE as DELETE_RULE "
+" ,PK.RDB$CONSTRAINT_NAME as PK_NAME "
+" ,FK.RDB$CONSTRAINT_NAME as FK_NAME "
+" ,null as DEFERRABILITY "
+" from "
+" RDB$RELATION_CONSTRAINTS PK "
+" ,RDB$RELATION_CONSTRAINTS FK "
+" ,RDB$REF_CONSTRAINTS RC "
+" ,RDB$INDEX_SEGMENTS ISP "
+" ,RDB$INDEX_SEGMENTS ISF "
+" WHERE ";
private static final String GET_EXPORTED_KEYS_END =
" FK.RDB$CONSTRAINT_NAME = RC.RDB$CONSTRAINT_NAME "
+" and PK.RDB$CONSTRAINT_NAME = RC.RDB$CONST_NAME_UQ "
+" and ISP.RDB$INDEX_NAME = PK.RDB$INDEX_NAME "
+" and ISF.RDB$INDEX_NAME = FK.RDB$INDEX_NAME "
+" and ISP.RDB$FIELD_POSITION = ISF.RDB$FIELD_POSITION "
+" order by 7, 9 ";
/**
* Gets a description of the foreign key columns that reference a
* table's primary key columns (the foreign keys exported by a
* table). They are ordered by FKTABLE_CAT, FKTABLE_SCHEM,
* FKTABLE_NAME, and KEY_SEQ.
*
* Each foreign key column description has the following columns:
*
* - PKTABLE_CAT String => primary key table catalog (may be null)
*
- PKTABLE_SCHEM String => primary key table schema (may be null)
*
- PKTABLE_NAME String => primary key table name
*
- PKCOLUMN_NAME String => primary key column name
*
- FKTABLE_CAT String => foreign key table catalog (may be null)
* being exported (may be null)
*
- FKTABLE_SCHEM String => foreign key table schema (may be null)
* being exported (may be null)
*
- FKTABLE_NAME String => foreign key table name
* being exported
*
- FKCOLUMN_NAME String => foreign key column name
* being exported
*
- KEY_SEQ short => sequence number within foreign key
*
- UPDATE_RULE short => 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)
*
* - DELETE_RULE short => 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
*
* - FK_NAME String => foreign key name (may be null)
*
- PK_NAME String => primary key name (may be null)
*
- DEFERRABILITY short => 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; "" retrieves those without a
* catalog; null means drop catalog name from the selection criteria
* @param schema a schema name; "" retrieves those
* without a schema
* @param table a table name
* @return ResultSet
- each row is a foreign key column description
* @exception SQLException if a database access error occurs
* @see #getImportedKeys
*/
public ResultSet getExportedKeys(String catalog, String schema,
String table) throws SQLException {
checkCatalogAndSchema(catalog, schema);
XSQLVAR[] xsqlvars = new XSQLVAR[14];
xsqlvars[0] = new XSQLVAR();
xsqlvars[0].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[0].sqllen = 31;
xsqlvars[0].sqlname = "PKTABLE_CAT";
xsqlvars[0].relname = "COLUMNINFO";
xsqlvars[1] = new XSQLVAR();
xsqlvars[1].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[1].sqllen = 31;
xsqlvars[1].sqlname = "PKTABLE_SCHEM";
xsqlvars[1].relname = "COLUMNINFO";
xsqlvars[2] = new XSQLVAR();
xsqlvars[2].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[2].sqllen = 31;
xsqlvars[2].sqlname = "PKTABLE_NAME";
xsqlvars[2].relname = "COLUMNINFO";
xsqlvars[3] = new XSQLVAR();
xsqlvars[3].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[3].sqllen = 31;
xsqlvars[3].sqlname = "PKCOLUMN_NAME";
xsqlvars[3].relname = "COLUMNINFO";
xsqlvars[4] = new XSQLVAR();
xsqlvars[4].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[4].sqllen = 31;
xsqlvars[4].sqlname = "FKTABLE_CAT";
xsqlvars[4].relname = "COLUMNINFO";
xsqlvars[5] = new XSQLVAR();
xsqlvars[5].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[5].sqllen = 31;
xsqlvars[5].sqlname = "FKTABLE_SCHEM";
xsqlvars[5].relname = "COLUMNINFO";
xsqlvars[6] = new XSQLVAR();
xsqlvars[6].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[6].sqllen = 31;
xsqlvars[6].sqlname = "FKTABLE_NAME";
xsqlvars[6].relname = "COLUMNINFO";
xsqlvars[7] = new XSQLVAR();
xsqlvars[7].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[7].sqllen = 31;
xsqlvars[7].sqlname = "FKCOLUMN_NAME";
xsqlvars[7].relname = "COLUMNINFO";
xsqlvars[8] = new XSQLVAR();
xsqlvars[8].sqltype = ISCConstants.SQL_SHORT;
xsqlvars[8].sqlname = "KEY_SEQ";
xsqlvars[8].relname = "COLUMNINFO";
xsqlvars[9] = new XSQLVAR();
xsqlvars[9].sqltype = ISCConstants.SQL_SHORT;
xsqlvars[9].sqlname = "UPDATE_RULE";
xsqlvars[9].relname = "COLUMNINFO";
xsqlvars[10] = new XSQLVAR();
xsqlvars[10].sqltype = ISCConstants.SQL_SHORT;
xsqlvars[10].sqlname = "DELETE_RULE";
xsqlvars[10].relname = "COLUMNINFO";
xsqlvars[11] = new XSQLVAR();
xsqlvars[11].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[11].sqllen = 31;
xsqlvars[11].sqlname = "FK_NAME";
xsqlvars[11].relname = "COLUMNINFO";
xsqlvars[12] = new XSQLVAR();
xsqlvars[12].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[12].sqllen = 31;
xsqlvars[12].sqlname = "PK_NAME";
xsqlvars[12].relname = "COLUMNINFO";
xsqlvars[13] = new XSQLVAR();
xsqlvars[13].sqltype = ISCConstants.SQL_SHORT;
xsqlvars[13].sqlname = "DEFERRABILITY";
xsqlvars[13].relname = "COLUMNINFO";
Clause tableClause = new Clause("PK.RDB$RELATION_NAME", table);
String sql = GET_EXPORTED_KEYS_START;
sql += tableClause.getCondition();
sql += GET_EXPORTED_KEYS_END;
// check the original case identifiers first
ArrayList params = new ArrayList();
if (!tableClause.getCondition().equals("")) {
params.add(tableClause.getOriginalCaseValue());
}
ArrayList rows = new ArrayList();
ResultSet rs = doQuery(sql, params);
// if nothing found, check the uppercased identifiers
if (!rs.next()) {
params.clear();
if (!tableClause.getCondition().equals("")) {
params.add(tableClause.getValue());
}
rs = doQuery(sql, params);
// if nothing found, return an empty result set
if (!rs.next())
return new FBResultSet(xsqlvars, rows);
}
do {
byte[][] row = new byte[14][];
row[0] = null;
row[1] = null;
row[2] = getBytes(rs.getString("PKTABLE_NAME"));
row[3] = getBytes(rs.getString("PKCOLUMN_NAME"));
row[4] = null;
row[5] = null;
row[6] = getBytes(rs.getString("FKTABLE_NAME"));
row[7] = getBytes(rs.getString("FKCOLUMN_NAME"));
row[8] = xsqlvars[0].encodeShort(rs.getShort("KEY_SEQ"));
String updateRule = rs.getString("UPDATE_RULE");
if (updateRule.equals("NO ACTION") || updateRule.equals("RESTRICT"))
row[9] = xsqlvars[0].encodeShort((short) DatabaseMetaData.importedKeyNoAction);
else if (updateRule.equals("CASCADE"))
row[9] = xsqlvars[0].encodeShort((short) DatabaseMetaData.importedKeyCascade);
else if (updateRule.equals("SET NULL"))
row[9] = xsqlvars[0].encodeShort((short) DatabaseMetaData.importedKeySetNull);
else if (updateRule.equals("SET DEFAULT"))
row[9] = xsqlvars[0].encodeShort((short) DatabaseMetaData.importedKeySetDefault);
String deleteRule = rs.getString("DELETE_RULE");
if (deleteRule.equals("NO ACTION") || deleteRule.equals("RESTRICT"))
row[10] = xsqlvars[0].encodeShort((short) DatabaseMetaData.importedKeyNoAction);
else if (deleteRule.equals("CASCADE"))
row[10] = xsqlvars[0].encodeShort((short) DatabaseMetaData.importedKeyCascade);
else if (deleteRule.equals("SET NULL"))
row[10] = xsqlvars[0].encodeShort((short) DatabaseMetaData.importedKeySetNull);
else if (deleteRule.equals("SET DEFAULT"))
row[10] = xsqlvars[0].encodeShort((short) DatabaseMetaData.importedKeySetDefault);
row[11] = getBytes(rs.getString("FK_NAME"));
row[12] = getBytes(rs.getString("PK_NAME"));
row[13] = xsqlvars[0].encodeShort((short) DatabaseMetaData.importedKeyNotDeferrable);
rows.add(row);
} while(rs.next());
return new FBResultSet(xsqlvars, rows);
}
private static final String GET_CROSS_KEYS_START = "select"
+" null as PKTABLE_CAT "
+" ,null as PKTABLE_SCHEM "
+" ,PK.RDB$RELATION_NAME as PKTABLE_NAME "
+" ,ISP.RDB$FIELD_NAME as PKCOLUMN_NAME "
+" ,null as FKTABLE_CAT "
+" ,null as FKTABLE_SCHEM "
+" ,FK.RDB$RELATION_NAME as FKTABLE_NAME "
+" ,ISF.RDB$FIELD_NAME as FKCOLUMN_NAME "
+" ,CAST ((ISP.RDB$FIELD_POSITION + 1) as SMALLINT) as KEY_SEQ "
+" ,RC.RDB$UPDATE_RULE as UPDATE_RULE "
+" ,RC.RDB$DELETE_RULE as DELETE_RULE "
+" ,PK.RDB$CONSTRAINT_NAME as PK_NAME "
+" ,FK.RDB$CONSTRAINT_NAME as FK_NAME "
+" ,null as DEFERRABILITY "
+" from "
+" RDB$RELATION_CONSTRAINTS PK "
+" ,RDB$RELATION_CONSTRAINTS FK "
+" ,RDB$REF_CONSTRAINTS RC "
+" ,RDB$INDEX_SEGMENTS ISP "
+" ,RDB$INDEX_SEGMENTS ISF "
+" WHERE ";
private static final String GET_CROSS_KEYS_END =
" FK.RDB$CONSTRAINT_NAME = RC.RDB$CONSTRAINT_NAME "
+" and PK.RDB$CONSTRAINT_NAME = RC.RDB$CONST_NAME_UQ "
+" and ISP.RDB$INDEX_NAME = PK.RDB$INDEX_NAME "
+" and ISF.RDB$INDEX_NAME = FK.RDB$INDEX_NAME "
+" and ISP.RDB$FIELD_POSITION = ISF.RDB$FIELD_POSITION "
+" order by 7, 9 ";
/**
* Gets a description of the foreign key columns in the foreign key
* table that reference the primary key columns of the primary key
* table (describe how one table imports another's key). This
* should normally return a single foreign key/primary key pair
* (most tables only import a foreign key from a table once.) They
* are ordered by FKTABLE_CAT, FKTABLE_SCHEM, FKTABLE_NAME, and
* KEY_SEQ.
*
* Each foreign key column description has the following columns:
*
* - PKTABLE_CAT String => primary key table catalog (may be null)
*
- PKTABLE_SCHEM String => primary key table schema (may be null)
*
- PKTABLE_NAME String => primary key table name
*
- PKCOLUMN_NAME String => primary key column name
*
- FKTABLE_CAT String => foreign key table catalog (may be null)
* being exported (may be null)
*
- FKTABLE_SCHEM String => foreign key table schema (may be null)
* being exported (may be null)
*
- FKTABLE_NAME String => foreign key table name
* being exported
*
- FKCOLUMN_NAME String => foreign key column name
* being exported
*
- KEY_SEQ short => sequence number within foreign key
*
- UPDATE_RULE short => 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)
*
* - DELETE_RULE short => 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
*
* - FK_NAME String => foreign key name (may be null)
*
- PK_NAME String => primary key name (may be null)
*
- DEFERRABILITY short => 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 primaryCatalog a catalog name; "" retrieves those without a
* catalog; null means drop catalog name from the selection criteria
* @param primarySchema a schema name; "" retrieves those
* without a schema
* @param primaryTable the table name that exports the key
* @param foreignCatalog a catalog name; "" retrieves those without a
* catalog; null means drop catalog name from the selection criteria
* @param foreignSchema a schema name; "" retrieves those
* without a schema
* @param foreignTable the table name that imports the key
* @return ResultSet
- each row is a foreign key column description
* @exception SQLException if a database access error occurs
* @see #getImportedKeys
*/
public ResultSet getCrossReference(
String primaryCatalog, String primarySchema, String primaryTable,
String foreignCatalog, String foreignSchema, String foreignTable
) throws SQLException {
checkCatalogAndSchema(primaryCatalog, primarySchema);
checkCatalogAndSchema(foreignCatalog, foreignSchema);
XSQLVAR[] xsqlvars = new XSQLVAR[14];
xsqlvars[0] = new XSQLVAR();
xsqlvars[0].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[0].sqllen = 31;
xsqlvars[0].sqlname = "PKTABLE_CAT";
xsqlvars[0].relname = "COLUMNINFO";
xsqlvars[1] = new XSQLVAR();
xsqlvars[1].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[1].sqllen = 31;
xsqlvars[1].sqlname = "PKTABLE_SCHEM";
xsqlvars[1].relname = "COLUMNINFO";
xsqlvars[2] = new XSQLVAR();
xsqlvars[2].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[2].sqllen = 31;
xsqlvars[2].sqlname = "PKTABLE_NAME";
xsqlvars[2].relname = "COLUMNINFO";
xsqlvars[3] = new XSQLVAR();
xsqlvars[3].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[3].sqllen = 31;
xsqlvars[3].sqlname = "PKCOLUMN_NAME";
xsqlvars[3].relname = "COLUMNINFO";
xsqlvars[4] = new XSQLVAR();
xsqlvars[4].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[4].sqllen = 31;
xsqlvars[4].sqlname = "FKTABLE_CAT";
xsqlvars[4].relname = "COLUMNINFO";
xsqlvars[5] = new XSQLVAR();
xsqlvars[5].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[5].sqllen = 31;
xsqlvars[5].sqlname = "FKTABLE_SCHEM";
xsqlvars[5].relname = "COLUMNINFO";
xsqlvars[6] = new XSQLVAR();
xsqlvars[6].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[6].sqllen = 31;
xsqlvars[6].sqlname = "FKTABLE_NAME";
xsqlvars[6].relname = "COLUMNINFO";
xsqlvars[7] = new XSQLVAR();
xsqlvars[7].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[7].sqllen = 31;
xsqlvars[7].sqlname = "FKCOLUMN_NAME";
xsqlvars[7].relname = "COLUMNINFO";
xsqlvars[8] = new XSQLVAR();
xsqlvars[8].sqltype = ISCConstants.SQL_SHORT;
xsqlvars[8].sqlname = "KEY_SEQ";
xsqlvars[8].relname = "COLUMNINFO";
xsqlvars[9] = new XSQLVAR();
xsqlvars[9].sqltype = ISCConstants.SQL_SHORT;
xsqlvars[9].sqlname = "UPDATE_RULE";
xsqlvars[9].relname = "COLUMNINFO";
xsqlvars[10] = new XSQLVAR();
xsqlvars[10].sqltype = ISCConstants.SQL_SHORT;
xsqlvars[10].sqlname = "DELETE_RULE";
xsqlvars[10].relname = "COLUMNINFO";
xsqlvars[11] = new XSQLVAR();
xsqlvars[11].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[11].sqllen = 31;
xsqlvars[11].sqlname = "FK_NAME";
xsqlvars[11].relname = "COLUMNINFO";
xsqlvars[12] = new XSQLVAR();
xsqlvars[12].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[12].sqllen = 31;
xsqlvars[12].sqlname = "PK_NAME";
xsqlvars[12].relname = "COLUMNINFO";
xsqlvars[13] = new XSQLVAR();
xsqlvars[13].sqltype = ISCConstants.SQL_SHORT;
xsqlvars[13].sqlname = "DEFERRABILITY";
xsqlvars[13].relname = "COLUMNINFO";
Clause primaryTableClause = new Clause("PK.RDB$RELATION_NAME", primaryTable);
Clause foreignTableClause = new Clause("FK.RDB$RELATION_NAME", foreignTable);
String sql = GET_CROSS_KEYS_START;
sql += primaryTableClause.getCondition();
sql += foreignTableClause.getCondition();
sql += GET_CROSS_KEYS_END;
ArrayList params = new ArrayList();
// check the original case first
if (!primaryTableClause.getCondition().equals("")) {
params.add(primaryTableClause.getOriginalCaseValue());
}
if (!foreignTableClause.getCondition().equals("")) {
params.add(foreignTableClause.getOriginalCaseValue());
}
ArrayList rows = new ArrayList();
ResultSet rs = doQuery(sql, params);
// if nothing found, check the uppercased identifiers
if (!rs.next()) {
params.clear();
if (!primaryTableClause.getCondition().equals("")) {
params.add(primaryTableClause.getValue());
}
if (!foreignTableClause.getCondition().equals("")) {
params.add(foreignTableClause.getValue());
}
rs = doQuery(sql, params);
// return empty result set if nothing found
if (!rs.next())
return new FBResultSet(xsqlvars, rows);
}
do {
byte[][] row = new byte[14][];
row[0] = null;
row[1] = null;
row[2] = getBytes(rs.getString("PKTABLE_NAME"));
row[3] = getBytes(rs.getString("PKCOLUMN_NAME"));
row[4] = null;
row[5] = null;
row[6] = getBytes(rs.getString("FKTABLE_NAME"));
row[7] = getBytes(rs.getString("FKCOLUMN_NAME"));
row[8] = xsqlvars[0].encodeShort(rs.getShort("KEY_SEQ"));
String updateRule = rs.getString("UPDATE_RULE");
if (updateRule.equals("NO ACTION") || updateRule.equals("RESTRICT"))
row[9] = xsqlvars[0].encodeShort((short) DatabaseMetaData.importedKeyNoAction);
else if (updateRule.equals("CASCADE"))
row[9] = xsqlvars[0].encodeShort((short) DatabaseMetaData.importedKeyCascade);
else if (updateRule.equals("SET NULL"))
row[9] = xsqlvars[0].encodeShort((short) DatabaseMetaData.importedKeySetNull);
else if (updateRule.equals("SET DEFAULT"))
row[9] = xsqlvars[0].encodeShort((short) DatabaseMetaData.importedKeySetDefault);
String deleteRule = rs.getString("DELETE_RULE");
if (deleteRule.equals("NO ACTION") || deleteRule.equals("RESTRICT"))
row[10] = xsqlvars[0].encodeShort((short) DatabaseMetaData.importedKeyNoAction);
else if (deleteRule.equals("CASCADE"))
row[10] = xsqlvars[0].encodeShort((short) DatabaseMetaData.importedKeyCascade);
else if (deleteRule.equals("SET NULL"))
row[10] = xsqlvars[0].encodeShort((short) DatabaseMetaData.importedKeySetNull);
else if (deleteRule.equals("SET DEFAULT"))
row[10] = xsqlvars[0].encodeShort((short) DatabaseMetaData.importedKeySetDefault);
row[11] = getBytes(rs.getString("FK_NAME"));
row[12] = getBytes(rs.getString("PK_NAME"));
row[13] = xsqlvars[0].encodeShort((short) DatabaseMetaData.importedKeyNotDeferrable);
rows.add(row);
} while(rs.next());
return new FBResultSet(xsqlvars, rows);
}
/**
* Simple convertor function to convert integer values to Short objects.
* Used in {@link #getTypeInfo()} for values of {@link java.sql.Types} class.
*
* @param value integer value to convert
* @return instance of java.lang.Short representing the value
*/
private byte[] createShort(int value) throws SQLException {
if (value > Short.MAX_VALUE)
throw new FBSQLException("Cannot convert integer to short.",
FBSQLException.SQL_STATE_INVALID_ARG_VALUE);
return new XSQLVAR().encodeShort((short)value);
}
/**
* Gets a description of all the standard SQL 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.
*
* Each type description has the following columns:
*
* - TYPE_NAME String => Type name
*
- DATA_TYPE short => SQL data type from java.sql.Types
*
- PRECISION int => maximum precision
*
- LITERAL_PREFIX String => prefix used to quote a literal
* (may be null)
*
- LITERAL_SUFFIX String => suffix used to quote a literal
(may be null)
*
- CREATE_PARAMS String => parameters used in creating
* the type (may be null)
*
- NULLABLE short => can you use NULL for this type?
*
* - typeNoNulls - does not allow NULL values
*
- typeNullable - allows NULL values
*
- typeNullableUnknown - nullability unknown
*
* - CASE_SENSITIVE boolean=> is it case sensitive?
*
- SEARCHABLE short => 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 ..
*
* - UNSIGNED_ATTRIBUTE boolean => is it unsigned?
*
- FIXED_PREC_SCALE boolean => can it be a money value?
*
- AUTO_INCREMENT boolean => can it be used for an
* auto-increment value?
*
- LOCAL_TYPE_NAME String => localized version of type name
* (may be null)
*
- MINIMUM_SCALE short => minimum scale supported
*
- MAXIMUM_SCALE short => maximum scale supported
*
- SQL_DATA_TYPE int => unused
*
- SQL_DATETIME_SUB int => unused
*
- NUM_PREC_RADIX int => usually 2 or 10
*
*
* @return ResultSet
- each row is an SQL type description
* @exception SQLException if a database access error occurs
*/
public ResultSet getTypeInfo() throws SQLException {
final XSQLVAR anXSQLVAR = new XSQLVAR();
byte[] shortZero = anXSQLVAR.encodeShort((short)0);
byte[] CASESENSITIVE = getBytes("T");
byte[] CASEINSENSITIVE = getBytes("F");
byte[] UNSIGNED = getBytes("T");
byte[] SIGNED = getBytes("F");
byte[] FIXEDSCALE = getBytes("T");
byte[] VARIABLESCALE = getBytes("F");
byte[] NOTAUTOINC = getBytes("F");
byte[] BINARY = anXSQLVAR.encodeInt(2);
byte[] PREDNONE = anXSQLVAR.encodeShort((short) DatabaseMetaData.typePredNone);
// TODO Find out why unused
byte[] PREDBASIC = anXSQLVAR.encodeShort((short) DatabaseMetaData.typePredBasic);
byte[] SEARCHABLE = anXSQLVAR.encodeShort((short) DatabaseMetaData.typeSearchable);
byte[] NULLABLE = anXSQLVAR.encodeShort((short) DatabaseMetaData.typeNullable);
//need to construct xsqlvar[] for ResultSetMetaData.
XSQLVAR[] xsqlvars = new XSQLVAR[18];
xsqlvars[0] = new XSQLVAR();
xsqlvars[0].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[0].sqllen = 31;
xsqlvars[0].sqlname = "TYPE_NAME";
xsqlvars[0].relname = "TYPEINFO";
xsqlvars[1] = new XSQLVAR();
xsqlvars[1].sqltype = ISCConstants.SQL_SHORT;
xsqlvars[1].sqlname = "DATA_TYPE";
xsqlvars[1].relname = "TYPEINFO";
xsqlvars[2] = new XSQLVAR();
xsqlvars[2].sqltype = ISCConstants.SQL_LONG;
xsqlvars[2].sqlname = "PRECISION";
xsqlvars[2].relname = "TYPEINFO";
xsqlvars[3] = new XSQLVAR();
xsqlvars[3].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[3].sqllen = 1;
xsqlvars[3].sqlname = "LITERAL_PREFIX";
xsqlvars[3].relname = "TYPEINFO";
xsqlvars[4] = new XSQLVAR();
xsqlvars[4].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[4].sqllen = 1;
xsqlvars[4].sqlname = "LITERAL_SUFFIX";
xsqlvars[4].relname = "TYPEINFO";
xsqlvars[5] = new XSQLVAR();
xsqlvars[5].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[5].sqllen = 31;
xsqlvars[5].sqlname = "CREATE_PARAMS";
xsqlvars[5].relname = "TYPEINFO";
xsqlvars[6] = new XSQLVAR();
xsqlvars[6].sqltype = ISCConstants.SQL_SHORT;
xsqlvars[6].sqlname = "NULLABLE";
xsqlvars[6].relname = "TYPEINFO";
xsqlvars[7] = new XSQLVAR();
xsqlvars[7].sqltype = ISCConstants.SQL_TEXT;
xsqlvars[7].sqllen = 1;
xsqlvars[7].sqlname = "CASE_SENSITIVE";
xsqlvars[7].relname = "TYPEINFO";
xsqlvars[8] = new XSQLVAR();
xsqlvars[8].sqltype = ISCConstants.SQL_SHORT;
xsqlvars[8].sqlname = "SEARCHABLE";
xsqlvars[8].relname = "TYPEINFO";
xsqlvars[9] = new XSQLVAR();
xsqlvars[9].sqltype = ISCConstants.SQL_TEXT;
xsqlvars[9].sqllen = 1;
xsqlvars[9].sqlname = "UNSIGNED_ATTRIBUTE";
xsqlvars[9].relname = "TYPEINFO";
xsqlvars[10] = new XSQLVAR();
xsqlvars[10].sqltype = ISCConstants.SQL_TEXT;
xsqlvars[10].sqllen = 1;
xsqlvars[10].sqlname = "FIXED_PREC_SCALE";
xsqlvars[10].relname = "TYPEINFO";
xsqlvars[11] = new XSQLVAR();
xsqlvars[11].sqltype = ISCConstants.SQL_TEXT;
xsqlvars[11].sqllen = 1;
xsqlvars[11].sqlname = "AUTO_INCREMENT";
xsqlvars[11].relname = "TYPEINFO";
xsqlvars[12] = new XSQLVAR();
xsqlvars[12].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[12].sqllen = 31;
xsqlvars[12].sqlname = "LOCAL_TYPE_NAME";
xsqlvars[12].relname = "TYPEINFO";
xsqlvars[13] = new XSQLVAR();
xsqlvars[13].sqltype = ISCConstants.SQL_SHORT;
xsqlvars[13].sqlname = "MINIMUM_SCALE";
xsqlvars[13].relname = "TYPEINFO";
xsqlvars[14] = new XSQLVAR();
xsqlvars[14].sqltype = ISCConstants.SQL_SHORT;
xsqlvars[14].sqlname = "MAXIMUM_SCALE";
xsqlvars[14].relname = "TYPEINFO";
xsqlvars[15] = new XSQLVAR();
xsqlvars[15].sqltype = ISCConstants.SQL_LONG;
xsqlvars[15].sqlname = "SQL_DATA_TYPE";
xsqlvars[15].relname = "TYPEINFO";
xsqlvars[16] = new XSQLVAR();
xsqlvars[16].sqltype = ISCConstants.SQL_LONG;
xsqlvars[16].sqlname = "SQL_DATETIME_SUB";
xsqlvars[16].relname = "TYPEINFO";
xsqlvars[17] = new XSQLVAR();
xsqlvars[17].sqltype = ISCConstants.SQL_LONG;
xsqlvars[17].sqlname = "NUM_PREC_RADIX";
xsqlvars[17].relname = "TYPEINFO";
//dialect 3 only
ArrayList rows = new ArrayList();
//BIGINT=-5
rows.add(new byte[][] {getBytes("BIGINT"), createShort(Types.BIGINT)
, anXSQLVAR.encodeInt(64), null, null, null,
NULLABLE, CASEINSENSITIVE, SEARCHABLE, SIGNED, FIXEDSCALE,
NOTAUTOINC, null, shortZero, shortZero, anXSQLVAR.encodeInt(ISCConstants.SQL_INT64), null, BINARY});
//LONGVARBINARY=-4
rows.add(new byte[][] {getBytes("BLOB SUB_TYPE 0"), createShort(Types.LONGVARBINARY)
, anXSQLVAR.encodeInt(0), null, null, null,
NULLABLE, CASESENSITIVE, PREDNONE, UNSIGNED, FIXEDSCALE,
NOTAUTOINC, null, shortZero, shortZero, anXSQLVAR.encodeInt(ISCConstants.SQL_BLOB), null, BINARY});
//LONGVARCHAR=-1
rows.add(new byte[][] {getBytes("BLOB SUB_TYPE 1"), createShort(Types.LONGVARCHAR)
, anXSQLVAR.encodeInt(0), null, null, null,
NULLABLE, CASESENSITIVE, PREDNONE, UNSIGNED, FIXEDSCALE,
NOTAUTOINC, null, shortZero, shortZero, anXSQLVAR.encodeInt(ISCConstants.SQL_BLOB), null, BINARY});
//CHAR=1
rows.add(new byte[][] {getBytes("CHAR"), createShort(Types.CHAR)
, anXSQLVAR.encodeInt(32664), getBytes("'"), getBytes("'"), getBytes("length"),
NULLABLE, CASESENSITIVE, SEARCHABLE, UNSIGNED, FIXEDSCALE,
NOTAUTOINC, null, shortZero, shortZero, anXSQLVAR.encodeInt(ISCConstants.SQL_TEXT), null, BINARY});
//NUMERIC=2
rows.add(new byte[][] {getBytes("NUMERIC"), createShort(Types.NUMERIC)
, anXSQLVAR.encodeInt(18), null, null, getBytes("precision,scale"),
NULLABLE, CASEINSENSITIVE, SEARCHABLE, SIGNED, FIXEDSCALE,
NOTAUTOINC, null, shortZero, createShort(18), anXSQLVAR.encodeInt(ISCConstants.SQL_INT64), null, BINARY});
//DECIMAL=3
rows.add(new byte[][] {getBytes("DECIMAL"), createShort(Types.DECIMAL)
, anXSQLVAR.encodeInt(18), null, null, getBytes("precision,scale"),
NULLABLE, CASEINSENSITIVE, SEARCHABLE, SIGNED, FIXEDSCALE,
NOTAUTOINC, null, shortZero, createShort(18), anXSQLVAR.encodeInt(ISCConstants.SQL_INT64), null, BINARY});
//INTEGER=4
rows.add(new byte[][] {getBytes("INTEGER"), createShort(Types.INTEGER)
, anXSQLVAR.encodeInt(32), null, null, null,
NULLABLE, CASEINSENSITIVE, SEARCHABLE, SIGNED, FIXEDSCALE,
NOTAUTOINC, null, shortZero, shortZero, anXSQLVAR.encodeInt(ISCConstants.SQL_LONG), null, BINARY});
//SMALLINT=5
rows.add(new byte[][] {getBytes("SMALLINT"), createShort(Types.SMALLINT)
, anXSQLVAR.encodeInt(16), null, null, null,
NULLABLE, CASEINSENSITIVE, SEARCHABLE, SIGNED, FIXEDSCALE,
NOTAUTOINC, null, shortZero, shortZero, anXSQLVAR.encodeInt(ISCConstants.SQL_SHORT), null, BINARY});
//FLOAT=6
rows.add(new byte[][] {getBytes("FLOAT"), createShort(Types.FLOAT)
, anXSQLVAR.encodeInt(7), null, null, null,
NULLABLE, CASEINSENSITIVE, SEARCHABLE, SIGNED, VARIABLESCALE,
NOTAUTOINC, null, createShort(0), createShort(7), anXSQLVAR.encodeInt(ISCConstants.SQL_FLOAT), null, BINARY});
//DOUBLE=8
rows.add(new byte[][] {getBytes("DOUBLE PRECISION"), createShort(Types.DOUBLE)
, anXSQLVAR.encodeInt(15), null, null, null,
NULLABLE, CASEINSENSITIVE, SEARCHABLE, SIGNED, VARIABLESCALE,
NOTAUTOINC, null, createShort(0), createShort(15), anXSQLVAR.encodeInt(ISCConstants.SQL_DOUBLE), null, BINARY});
//VARCHAR=12
rows.add(new byte[][] {getBytes("VARCHAR"), createShort(Types.VARCHAR)
, anXSQLVAR.encodeInt(32664), getBytes("'"), getBytes("'"), getBytes("length"),
NULLABLE, CASESENSITIVE, SEARCHABLE, UNSIGNED, FIXEDSCALE,
NOTAUTOINC, null, shortZero, shortZero, anXSQLVAR.encodeInt(ISCConstants.SQL_VARYING), null, BINARY});
//DATE=91
rows.add(new byte[][] {getBytes("DATE"), createShort(Types.DATE)
, anXSQLVAR.encodeInt(0), null, null, null,
NULLABLE, CASEINSENSITIVE, SEARCHABLE, UNSIGNED, FIXEDSCALE,
NOTAUTOINC, null, shortZero, shortZero, anXSQLVAR.encodeInt(ISCConstants.SQL_TYPE_DATE), null, BINARY});
//TIME=92
rows.add(new byte[][] {getBytes("TIME"), createShort(Types.TIME)
, anXSQLVAR.encodeInt(0), null, null, null,
NULLABLE, CASEINSENSITIVE, SEARCHABLE, UNSIGNED, FIXEDSCALE,
NOTAUTOINC, null, shortZero, shortZero, anXSQLVAR.encodeInt(ISCConstants.SQL_TYPE_TIME), null, BINARY});
//TIMESTAMP=93
rows.add(new byte[][] {getBytes("TIMESTAMP"), createShort(Types.TIMESTAMP)
, anXSQLVAR.encodeInt(0), null, null, null,
NULLABLE, CASEINSENSITIVE, SEARCHABLE, UNSIGNED, FIXEDSCALE,
NOTAUTOINC, null, shortZero, shortZero, anXSQLVAR.encodeInt(ISCConstants.SQL_TIMESTAMP), null, BINARY});
//OTHER=1111
rows.add(new byte[][] {getBytes("ARRAY"), createShort(Types.OTHER)
, anXSQLVAR.encodeInt(0), null, null, null,
NULLABLE, CASESENSITIVE, PREDNONE, UNSIGNED, FIXEDSCALE,
NOTAUTOINC, null, shortZero, shortZero, anXSQLVAR.encodeInt(ISCConstants.SQL_ARRAY), null, BINARY});
//BLOB=2004
rows.add(new byte[][] {getBytes("BLOB SUB_TYPE <0 "), createShort(Types.BLOB)
, anXSQLVAR.encodeInt(0), null, null, null,
NULLABLE, CASESENSITIVE, PREDNONE, UNSIGNED, FIXEDSCALE,
NOTAUTOINC, null, shortZero, shortZero, anXSQLVAR.encodeInt(ISCConstants.SQL_BLOB), null, BINARY});
return new FBResultSet(xsqlvars, rows);
}
private static final String GET_INDEX_INFO_START = "SELECT "
+ " ind.RDB$RELATION_NAME AS TABLE_NAME"
+ ", ind.RDB$UNIQUE_FLAG AS UNIQUE_FLAG"
+ ", ind.RDB$INDEX_NAME as INDEX_NAME"
+ ", ise.rdb$field_position + 1 as ORDINAL_POSITION"
+ ", ise.rdb$field_name as COLUMN_NAME"
+ ", ind.RDB$EXPRESSION_SOURCE as EXPRESSION_SOURCE"
+ ", ind.RDB$INDEX_TYPE as ASC_OR_DESC "
+ "FROM "
+ " rdb$indices ind "
+ " LEFT JOIN rdb$index_segments ise ON ind.rdb$index_name = ise.rdb$index_name "
;
private static final String GET_INDEX_INFO =
GET_INDEX_INFO_START
+ "WHERE "
+ " ind.rdb$relation_name = ? "
+ "ORDER BY 2, 3, 4"
;
private static final String GET_INDEX_INFO_UPPER =
GET_INDEX_INFO_START
+ "WHERE "
+ " UPPER(ind.rdb$relation_name) = ? "
+ "ORDER BY 2, 3, 4"
;
/**
* Gets a description of a table's indices and statistics. They are
* ordered by NON_UNIQUE, TYPE, INDEX_NAME, and ORDINAL_POSITION.
*
* Each index column description has the following columns:
*
* - TABLE_CAT String => table catalog (may be null)
*
- TABLE_SCHEM String => table schema (may be null)
*
- TABLE_NAME String => table name
*
- NON_UNIQUE boolean => Can index values be non-unique?
* false when TYPE is tableIndexStatistic
*
- INDEX_QUALIFIER String => index catalog (may be null);
* null when TYPE is tableIndexStatistic
*
- INDEX_NAME String => index name; null when TYPE is
* tableIndexStatistic
*
- TYPE short => 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
*
* - ORDINAL_POSITION short => column sequence number
* within index; zero when TYPE is tableIndexStatistic
*
- COLUMN_NAME String => column name; null when TYPE is
* tableIndexStatistic
*
- ASC_OR_DESC String => column sort sequence, "A" => ascending,
* "D" => descending, may be null if sort sequence is not supported;
* null when TYPE is tableIndexStatistic
*
- CARDINALITY int => 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.
*
- PAGES int => When TYPE is tableIndexStatisic then
* this is the number of pages used for the table, otherwise it
* is the number of pages used for the current index.
*
- FILTER_CONDITION String => Filter condition, if any.
* (may be null)
*
*
* @param catalog a catalog name; "" retrieves those without a
* catalog; null means drop catalog name from the selection criteria
* @param schema a schema name; "" retrieves those without a schema
* @param table a table name
* @param unique when true, return only indices for unique values;
* when false, return indices regardless of whether unique or not
* @param approximate when true, result is allowed to reflect approximate
* or out of data values; when false, results are requested to be
* accurate
* @return ResultSet
- each row is an index column description
* @exception SQLException if a database access error occurs
*/
public ResultSet getIndexInfo(String catalog, String schema, String table,
boolean unique, boolean approximate) throws SQLException {
checkCatalogAndSchema(catalog, schema);
XSQLVAR[] xsqlvars = new XSQLVAR[13];
xsqlvars[0] = new XSQLVAR();
xsqlvars[0].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[0].sqllen = 31;
xsqlvars[0].sqlname = "TABLE_CAT";
xsqlvars[0].relname = "INDEXINFO";
xsqlvars[1] = new XSQLVAR();
xsqlvars[1].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[1].sqllen = 31;
xsqlvars[1].sqlname = "TABLE_SCHEM";
xsqlvars[1].relname = "INDEXINFO";
xsqlvars[2] = new XSQLVAR();
xsqlvars[2].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[2].sqllen = 31;
xsqlvars[2].sqlname = "TABLE_NAME";
xsqlvars[2].relname = "INDEXINFO";
xsqlvars[3] = new XSQLVAR();
xsqlvars[3].sqltype = ISCConstants.SQL_TEXT;
xsqlvars[3].sqllen = 1;
xsqlvars[3].sqlname = "NON_UNIQUE";
xsqlvars[3].relname = "INDEXINFO";
xsqlvars[4] = new XSQLVAR();
xsqlvars[4].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[4].sqllen = 31;
xsqlvars[4].sqlname = "INDEX_QUALIFIER";
xsqlvars[4].relname = "INDEXINFO";
xsqlvars[5] = new XSQLVAR();
xsqlvars[5].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[5].sqllen = 31;
xsqlvars[5].sqlname = "INDEX_NAME";
xsqlvars[5].relname = "INDEXINFO";
xsqlvars[6] = new XSQLVAR();
xsqlvars[6].sqltype = ISCConstants.SQL_SHORT;
xsqlvars[6].sqlname = "TYPE";
xsqlvars[6].relname = "INDEXINFO";
xsqlvars[7] = new XSQLVAR();
xsqlvars[7].sqltype = ISCConstants.SQL_SHORT;
xsqlvars[7].sqlname = "ORDINAL_POSITION";
xsqlvars[7].relname = "INDEXINFO";
xsqlvars[8] = new XSQLVAR();
xsqlvars[8].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[8].sqllen = 31;
xsqlvars[8].sqlname = "COLUMN_NAME";
xsqlvars[8].relname = "INDEXINFO";
xsqlvars[9] = new XSQLVAR();
xsqlvars[9].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[9].sqllen = 31;
xsqlvars[9].sqlname = "ASC_OR_DESC";
xsqlvars[9].relname = "INDEXINFO";
xsqlvars[10] = new XSQLVAR();
xsqlvars[10].sqltype = ISCConstants.SQL_LONG;
xsqlvars[10].sqlname = "CARDINALITY";
xsqlvars[10].relname = "INDEXINFO";
xsqlvars[11] = new XSQLVAR();
xsqlvars[11].sqltype = ISCConstants.SQL_LONG;
xsqlvars[11].sqlname = "PAGES";
xsqlvars[11].relname = "INDEXINFO";
xsqlvars[12] = new XSQLVAR();
xsqlvars[12].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[12].sqllen = 31;
xsqlvars[12].sqlname = "FILTER_CONDITION";
xsqlvars[12].relname = "INDEXINFO";
ArrayList rows = new ArrayList();
if (table == null) {
return new FBResultSet(xsqlvars, rows);
}
ArrayList params = new ArrayList();
params.add(table);
ResultSet rs = doQuery(GET_INDEX_INFO, params);
// if no direct match happened, check the uppercased match
if (!rs.next()) {
params.set(0, table.toUpperCase());
rs = doQuery(GET_INDEX_INFO_UPPER, params);
// open the second result set and check whether we have rows
// if no rows are available, we have to exit now, otherwise the
// following do/while loop will throw SQLException that the
// result set is not positioned on a row
if (!rs.next())
return new FBResultSet(xsqlvars, rows);
}
do {
byte[][] row = new byte[13][];
row[0] = null;
row[1] = null;
row[2] = getBytes(rs.getString("TABLE_NAME"));
boolean isNotUnique = (rs.getInt("UNIQUE_FLAG") == 0);
if (unique && isNotUnique) {
// Skip indices that are not unique, as requested
continue;
}
row[3] = getBytes(isNotUnique ? "T" : "F");
row[4] = null;
row[5] = getBytes(rs.getString("INDEX_NAME"));
row[6] = xsqlvars[0].encodeShort((short) DatabaseMetaData.tableIndexOther);
String columnName = rs.getString("COLUMN_NAME");
if (rs.wasNull()) {
row[7] = xsqlvars[0].encodeShort((short)1);
String expressionSource = rs.getString("EXPRESSION_SOURCE");
if (expressionSource != null) {
row[8] = getBytes(expressionSource);
if (expressionSource.length() > xsqlvars[8].sqllen) {
xsqlvars[8].sqllen = expressionSource.length();
}
} else {
row[8] = null;
}
} else {
row[7] = xsqlvars[0].encodeShort(rs.getShort("ORDINAL_POSITION"));
row[8] = getBytes(columnName);
}
int ascOrDesc = rs.getInt("ASC_OR_DESC");
if (ascOrDesc == 0) {
row[9] = getBytes("A");
} else if (ascOrDesc == 1) {
row[9] = getBytes("D");
} else {
row[9] = null;
}
// NOTE: We are setting CARDINALITY and PAGES to NULL as we don't have this info; might contravene JDBC spec
// TODO use 1 / RDB$STATISTICS for approximation of CARDINALITY?
row[10] = null;
// TODO query RDB$PAGES for PAGES information?
row[11] = null;
row[12] = null;
rows.add(row);
} while (rs.next());
return new FBResultSet(xsqlvars, rows);
}
//--------------------------JDBC 2.0-----------------------------
/**
* Does the database support the given result set type?
*
* @param type defined in java.sql.ResultSet
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
* @see Connection
* @since 1.2
* @see What Is in the JDBC 2.0 API
*/
public boolean supportsResultSetType(int type) throws SQLException {
switch (type){
case ResultSet.TYPE_FORWARD_ONLY:
case ResultSet.TYPE_SCROLL_INSENSITIVE :
case ResultSet.TYPE_SCROLL_SENSITIVE :
return true;
default:
return false;
}
}
/**
* Does the database support the concurrency type in combination
* with the given result set type?
*
* @param type defined in java.sql.ResultSet
* @param concurrency type defined in java.sql.ResultSet
* @return true
if so; false
otherwise
* @exception SQLException if a database access error occurs
* @see Connection
* @since 1.2
* @see What Is in the JDBC 2.0 API
*/
public boolean supportsResultSetConcurrency(int type, int concurrency) throws SQLException {
switch(type) {
case ResultSet.TYPE_FORWARD_ONLY:
case ResultSet.TYPE_SCROLL_INSENSITIVE :
case ResultSet.TYPE_SCROLL_SENSITIVE :
return concurrency == ResultSet.CONCUR_READ_ONLY ||
concurrency == ResultSet.CONCUR_UPDATABLE;
default:
return false;
}
}
/**
*
* Indicates whether a result set's own updates are visible.
*
* @param type result set type, i.e. ResultSet.TYPE_XXX
* @return true
if updates are visible for the result set type;
* false
otherwise
* @exception SQLException if a database access error occurs
* @since 1.2
* @see What Is in the JDBC 2.0 API
*/
public boolean ownUpdatesAreVisible(int type) throws SQLException {
return ResultSet.TYPE_SCROLL_INSENSITIVE == type ||
ResultSet.TYPE_SCROLL_SENSITIVE == type;
}
/**
*
* Indicates whether a result set's own deletes are visible.
*
* @param type result set type, i.e. ResultSet.TYPE_XXX
* @return true
if deletes are visible for the result set type;
* false
otherwise
* @exception SQLException if a database access error occurs
* @since 1.2
* @see What Is in the JDBC 2.0 API
*/
public boolean ownDeletesAreVisible(int type) throws SQLException {
return ResultSet.TYPE_SCROLL_INSENSITIVE == type ||
ResultSet.TYPE_SCROLL_SENSITIVE == type;
}
/**
*
* Indicates whether a result set's own inserts are visible.
*
* @param type result set type, i.e. ResultSet.TYPE_XXX
* @return true
if inserts are visible for the result set type;
* false
otherwise
* @exception SQLException if a database access error occurs
* @since 1.2
* @see What Is in the JDBC 2.0 API
*/
public boolean ownInsertsAreVisible(int type) throws SQLException {
return ResultSet.TYPE_SCROLL_INSENSITIVE == type ||
ResultSet.TYPE_SCROLL_SENSITIVE == type;
}
/**
*
* Indicates whether updates made by others are visible.
*
* @param type result set type, i.e. ResultSet.TYPE_XXX
* @return true
if updates made by others
* are visible for the result set type;
* false
otherwise
* @exception SQLException if a database access error occurs
* @since 1.2
* @see What Is in the JDBC 2.0 API
*/
public boolean othersUpdatesAreVisible(int type) throws SQLException {
return false;
}
/**
*
* Indicates whether deletes made by others are visible.
*
* @param type result set type, i.e. ResultSet.TYPE_XXX
* @return true
if deletes made by others
* are visible for the result set type;
* false
otherwise
* @exception SQLException if a database access error occurs
* @since 1.2
* @see What Is in the JDBC 2.0 API
*/
public boolean othersDeletesAreVisible(int type) throws SQLException {
return false;
}
/**
*
* Indicates whether inserts made by others are visible.
*
* @param type result set type, i.e. ResultSet.TYPE_XXX
* @return true
if inserts made by others
* are visible for the result set type;
* false
otherwise
* @exception SQLException if a database access error occurs
* @since 1.2
* @see What Is in the JDBC 2.0 API
*/
public boolean othersInsertsAreVisible(int type) throws SQLException {
return false;
}
/**
*
* Indicates whether or not a visible row update can be detected by
* calling the method ResultSet.rowUpdated
.
*
* @param type result set type, i.e. ResultSet.TYPE_XXX
* @return true
if changes are detected by the result set type;
* false
otherwise
* @exception SQLException if a database access error occurs
* @since 1.2
* @see What Is in the JDBC 2.0 API
*/
public boolean updatesAreDetected(int type) throws SQLException {
return false;
}
/**
*
* Indicates whether or not a visible row delete can be detected by
* calling ResultSet.rowDeleted(). If deletesAreDetected()
* returns false, then deleted rows are removed from the result set.
*
* @param type result set type, i.e. ResultSet.TYPE_XXX
* @return true if changes are detected by the resultset type
* @exception SQLException if a database access error occurs
* @since 1.2
* @see What Is in the JDBC 2.0 API
*/
public boolean deletesAreDetected(int type) throws SQLException {
return false;
}
/**
*
* Indicates whether or not a visible row insert can be detected
* by calling ResultSet.rowInserted().
*
* @param type result set type, i.e. ResultSet.TYPE_XXX
* @return true if changes are detected by the resultset type
* @exception SQLException if a database access error occurs
* @since 1.2
* @see What Is in the JDBC 2.0 API
*/
public boolean insertsAreDetected(int type) throws SQLException {
return false;
}
/**
*
* Indicates whether the driver supports batch updates.
* @return true if the driver supports batch updates; false otherwise
* @since 1.2
* @see What Is in the JDBC 2.0 API
*/
public boolean supportsBatchUpdates() throws SQLException {
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:
*
* - TYPE_CAT String => the type's catalog (may be
null
)
* - TYPE_SCHEM String => type's schema (may be
null
)
* - TYPE_NAME String => type name
*
- CLASS_NAME String => Java class name
*
- DATA_TYPE int => type value defined in java.sql.Types.
* One of JAVA_OBJECT, STRUCT, or DISTINCT
*
- REMARKS String => explanatory comment on the type
*
- BASE_TYPE short => 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
* @exception SQLException if a database access error occurs
* @see #getSearchStringEscape
* @since 1.2
*/
public ResultSet getUDTs(String catalog, String schemaPattern,
String typeNamePattern, int[] types) throws SQLException {
XSQLVAR[] xsqlvars = new XSQLVAR[7];
xsqlvars[0] = new XSQLVAR();
xsqlvars[0].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[0].sqllen = 31;
xsqlvars[0].sqlname = "TYPE_CAT";
xsqlvars[0].relname = "UDT";
xsqlvars[1] = new XSQLVAR();
xsqlvars[1].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[1].sqllen = 31;
xsqlvars[1].sqlname = "TYPE_SCHEM";
xsqlvars[1].relname = "UDT";
xsqlvars[2] = new XSQLVAR();
xsqlvars[2].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[2].sqllen = 31;
xsqlvars[2].sqlname = "TYPE_NAME";
xsqlvars[2].relname = "UDT";
xsqlvars[3] = new XSQLVAR();
xsqlvars[3].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[3].sqllen = 31;
xsqlvars[3].sqlname = "CLASS_NAME";
xsqlvars[3].relname = "UDT";
xsqlvars[4] = new XSQLVAR();
xsqlvars[4].sqltype = ISCConstants.SQL_LONG;
xsqlvars[4].sqlname = "DATA_TYPE";
xsqlvars[4].relname = "UDT";
xsqlvars[5] = new XSQLVAR();
xsqlvars[5].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[5].sqllen = 31;
xsqlvars[5].sqlname = "REMARKS";
xsqlvars[5].relname = "UDT";
xsqlvars[6] = new XSQLVAR();
xsqlvars[6].sqltype = ISCConstants.SQL_SHORT;
xsqlvars[6].sqlname = "BASE_TYPE";
xsqlvars[6].relname = "UDT";
ArrayList rows = new ArrayList(0);
return new FBResultSet(xsqlvars, rows);
}
/**
* Retrieves the connection that produced this metadata object.
*
* @return the connection that produced this metadata object
* @since 1.2
* @see What Is in the JDBC 2.0 API
*/
public Connection getConnection() throws SQLException {
return connection;
}
//jdbc 3 methods
/**
* This operation is not supported
*
* @param param1
* @param param2
* @param param3
* @param param4
* @return
* @exception java.sql.SQLException
*/
public ResultSet getAttributes(String param1, String param2, String param3, String param4) throws SQLException {
throw new FBDriverNotCapableException();
}
/**
* Retrieves whether this database supports savepoints.
*
* @return true if savepoints are supported; false otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsSavepoints() throws SQLException {
return gdsHelper.compareToVersion(1, 5) >= 0;
}
/**
* Retrieve whether this database supports named parameters.
*
* @return true if named parameters are supported, false otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsNamedParameters() throws SQLException {
return false;
}
/**
* Retrieves whether it is possible to have multiple ResultSet
* objects returned from a CallableStatement
object
* simultaneously.
*
* @return true if multiple open ResultSets are supported, false otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsMultipleOpenResults() throws SQLException {
return false;
}
/**
* Retrieves whether auto-generated keys can be retrieved after creation.
*
* @return true if auto-generated keys can be retrieved, false otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsGetGeneratedKeys() throws SQLException {
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/sub type 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:
*
* - TYPE_CAT String => the UDT's catalog (may be
*
null
)
* - TYPE_SCHEM String => UDT's schema (may be
null
)
* - TYPE_NAME String => type name of the UDT
*
- SUPERTYPE_CAT String => the direct super type's catalog
* (may be
null
)
* - SUPERTYPE_SCHEM String => the direct super type's schema
* (may be
null
)
* - SUPERTYPE_NAME String => 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 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
*/
public ResultSet getSuperTypes(String catalog, String schemaPattern, String tableNamePattern) throws SQLException {
XSQLVAR[] xsqlvars = new XSQLVAR[6];
xsqlvars[0] = new XSQLVAR();
xsqlvars[0].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[0].sqllen = 31;
xsqlvars[0].sqlname = "TYPE_CAT";
xsqlvars[0].relname = "SUPERTYPES";
xsqlvars[1] = new XSQLVAR();
xsqlvars[1].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[1].sqllen = 31;
xsqlvars[1].sqlname = "TYPE_SCHEM";
xsqlvars[1].relname = "SUPERTYPES";
xsqlvars[2] = new XSQLVAR();
xsqlvars[2].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[2].sqllen = 31;
xsqlvars[2].sqlname = "TYPE_NAME";
xsqlvars[2].relname = "SUPERTYPES";
xsqlvars[3].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[3].sqllen = 31;
xsqlvars[3].sqlname = "SUPERTYPE_CAT";
xsqlvars[3].relname = "SUPERTYPES";
xsqlvars[4] = new XSQLVAR();
xsqlvars[4].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[4].sqllen = 31;
xsqlvars[4].sqlname = "SUPERTYPE_SCHEM";
xsqlvars[4].relname = "SUPERTYPES";
xsqlvars[5] = new XSQLVAR();
xsqlvars[5].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[5].sqllen = 31;
xsqlvars[5].sqlname = "SUPERTYPE_NAME";
xsqlvars[5].relname = "SUPERTYPES";
ArrayList rows = new ArrayList(0);
return new FBResultSet(xsqlvars, rows);
}
/**
* 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:
*
* - TABLE_CAT String => the type's catalog (may be
*
null
)
* - TABLE_SCHEM String => type's schema (may be
*
null
)
* - TABLE_NAME String => type name
*
- SUPERTABLE_NAME String => 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
*/
public ResultSet getSuperTables(String catalog, String schemaPattern, String tableNamePattern) throws SQLException {
XSQLVAR[] xsqlvars = new XSQLVAR[4];
xsqlvars[0] = new XSQLVAR();
xsqlvars[0].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[0].sqllen = 31;
xsqlvars[0].sqlname = "TABLE_CAT";
xsqlvars[0].relname = "SUPERTABLES";
xsqlvars[1] = new XSQLVAR();
xsqlvars[1].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[1].sqllen = 31;
xsqlvars[1].sqlname = "TABLE_SCHEM";
xsqlvars[1].relname = "SUPERTABLES";
xsqlvars[2] = new XSQLVAR();
xsqlvars[2].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[2].sqllen = 31;
xsqlvars[2].sqlname = "TABLE_NAME";
xsqlvars[2].relname = "SUPERTABLES";
xsqlvars[3] = new XSQLVAR();
xsqlvars[3].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[3].sqllen = 31;
xsqlvars[3].sqlname = "SUPERTABLE_NAME";
xsqlvars[3].relname = "SUPERTABLES";
ArrayList rows = new ArrayList(0);
return new FBResultSet(xsqlvars, rows);
}
/**
* Retrieves whether this database supports the given results holdability.
*
* @param holdability one of the following constants:
* ResultSet.HOLD_CURSORS_OVER_COMMIT
or
* ResultSet.CLOSE_CURSORS_AT_COMMIT
* @return true
if the holdability is supported,
* false
otherwise
* @exception SQLException if a database access error occurs
*/
public boolean supportsResultSetHoldability(int holdability) throws SQLException {
return holdability == FirebirdResultSet.CLOSE_CURSORS_AT_COMMIT ||
holdability == FirebirdResultSet.HOLD_CURSORS_OVER_COMMIT;
}
/**
* Retrieves the default holdability of this ResultSet
.
*
* @return the default holdability
* @exception SQLException if a database access error occurs
*/
public int getResultSetHoldability() throws SQLException {
return FirebirdResultSet.CLOSE_CURSORS_AT_COMMIT;
}
/**
* Get the major version number of the database.
*
* @return The major version number
* @exception SQLException if a database access error occurs
*/
public int getDatabaseMajorVersion() throws SQLException {
return gdsHelper.getDatabaseProductMajorVersion();
}
/**
* Get the minor version number of the database.
* @return The minor version number
* @exception SQLException if a database access error occurs
*/
public int getDatabaseMinorVersion() throws SQLException {
return gdsHelper.getDatabaseProductMinorVersion();
}
/**
* Get the major version of the ODS (On-Disk Structure) of the database.
* @return The major version number
* @exception SQLException if a database access error occurs
*/
public int getOdsMajorVersion() throws SQLException {
return gdsHelper.getIscDBHandle().getODSMajorVersion();
}
/**
* Get the minor version of the ODS (On-Disk Structure) of the database.
* @return The minor version number
* @exception SQLException if a database access error occurs
*/
public int getOdsMinorVersion() throws SQLException {
return gdsHelper.getIscDBHandle().getODSMinorVersion();
}
/**
* Indicates whether the SQLSTATEs returned by SQLException.getSQLState is
* X/Open (now known as Open Group) SQL CLI or SQL99
*
* @return the type of SQLSTATEs
* @exception SQLException should never be thrown in this implementation
*/
public int getSQLStateType() throws SQLException {
return DatabaseMetaData.sqlStateSQL99;
}
//-------------------------- JDBC 4.0 -------------------------------------
public boolean supportsStoredFunctionsUsingCallSyntax() throws SQLException {
return false;
}
public boolean autoCommitFailureClosesAllResultSets() throws SQLException {
// the holdable result sets remain open, others are closed, but this
// happens before the statement is executed
return false;
}
/**
* Retrieves a list of the client info properties
* that the driver supports. The result set contains the following columns
*
*
* - NAME String=> The name of the client info property
* - MAX_LEN int=> The maximum length of the value for the property
* - DEFAULT_VALUE String=> The default value of the property
* - 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
*
* @exception SQLException if a database access error occurs
*
* @since 1.6
*/
public ResultSet getClientInfoProperties() throws SQLException {
XSQLVAR[] xsqlvars = new XSQLVAR[4];
xsqlvars[0] = new XSQLVAR();
xsqlvars[0].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[0].sqllen = 31;
xsqlvars[0].sqlname = "NAME";
xsqlvars[0].relname = "UDT";
xsqlvars[1] = new XSQLVAR();
xsqlvars[1].sqltype = ISCConstants.SQL_LONG;
xsqlvars[1].sqllen = 4;
xsqlvars[1].sqlname = "MAX_LEN";
xsqlvars[1].relname = "UDT";
xsqlvars[2] = new XSQLVAR();
xsqlvars[2].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[2].sqllen = 31;
xsqlvars[2].sqlname = "DEFAULT";
xsqlvars[2].relname = "UDT";
xsqlvars[3] = new XSQLVAR();
xsqlvars[3].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[3].sqllen = 31;
xsqlvars[3].sqlname = "DESCRIPTION";
xsqlvars[3].relname = "UDT";
ArrayList rows = new ArrayList(0);
return new FBResultSet(xsqlvars, rows);
}
/**
* 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:
*
* - FUNCTION_CAT String => function catalog (may be
null
)
* - FUNCTION_SCHEM String => function schema (may be
null
)
* - FUNCTION_NAME String => function name. This is the name
* used to invoke the function
*
- COLUMN_NAME String => column/parameter name
*
- COLUMN_TYPE Short => 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
*
* - DATA_TYPE int => SQL type from java.sql.Types
*
- TYPE_NAME String => SQL type name, for a UDT type the
* type name is fully qualified
*
- PRECISION int => precision
*
- LENGTH int => length in bytes of data
*
- SCALE short => scale - null is returned for data types where
* SCALE is not applicable.
*
- RADIX short => radix
*
- NULLABLE short => can it contain NULL.
*
* - functionNoNulls - does not allow NULL values
*
- functionNullable - allows NULL values
*
- functionNullableUnknown - nullability unknown
*
* - REMARKS String => comment describing column/parameter
*
- CHAR_OCTET_LENGTH int => the maximum length of binary
* and character based parameters or columns. For any other datatype the returned value
* is a NULL
*
- ORDINAL_POSITION int => 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.
*
- IS_NULLABLE String => 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
*
* - SPECIFIC_NAME String => 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
*
* @exception 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 {
// FIXME implement this method
throw new FBDriverNotCapableException();
}
/**
* 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 the following columns:
*
* - FUNCTION_CAT String => function catalog (may be
null
)
* - FUNCTION_SCHEM String => function schema (may be
null
)
* - FUNCTION_NAME String => function name. This is the name
* used to invoke the function
*
- REMARKS String => explanatory comment on the function
*
- FUNCTION_TYPE short => 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
*
* - SPECIFIC_NAME String => 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
* @exception SQLException if a database access error occurs
* @see #getSearchStringEscape
* @since 1.6
*/
public ResultSet getFunctions(String catalog, String schemaPattern, String functionNamePattern) throws SQLException {
throw new FBDriverNotCapableException();
}
/**
* Retrieves the schema names available in this database. The results
* are ordered by TABLE_CATALOG
and
* TABLE_SCHEM
.
*
*
The schema columns are:
*
* - TABLE_SCHEM String => schema name
*
- TABLE_CATALOG String => catalog name (may be
null
)
*
*
*
* @param catalog a catalog name; must match the catalog name as it is stored
* in the database;"" retrieves those without a catalog; null means catalog
* name should not be used to narrow down the search.
* @param schemaPattern a schema name; must match the schema name as it is
* stored in the database; null means
* schema name should not be used to narrow down the search.
* @return a ResultSet
object in which each row is a
* schema description
* @exception SQLException if a database access error occurs
* @see #getSearchStringEscape
* @since 1.6
*/
public ResultSet getSchemas(String catalog, String schemaPattern) throws SQLException {
XSQLVAR[] xsqlvars = new XSQLVAR[2];
xsqlvars[0] = new XSQLVAR();
xsqlvars[0].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[0].sqllen = 31;
xsqlvars[0].sqlname = "TABLE_SCHEM";
xsqlvars[0].relname = "TABLESCHEMAS";
xsqlvars[1] = new XSQLVAR();
xsqlvars[1].sqltype = ISCConstants.SQL_VARYING;
xsqlvars[1].sqllen = 31;
xsqlvars[1].sqlname = "TABLE_CATALOG";
xsqlvars[1].relname = "TABLESCHEMAS";
ArrayList rows = new ArrayList(0);
return new FBResultSet(xsqlvars, rows);
}
public boolean isWrapperFor(Class arg0) throws SQLException {
return arg0 != null && arg0.isAssignableFrom(FBDatabaseMetaData.class);
}
public Object unwrap(Class arg0) throws SQLException {
if (!isWrapperFor(arg0))
throw new FBSQLException("No compatible class found.");
return this;
}
public boolean isAllCondition(String pattern) {
if ("%".equals(pattern)) {
//asks for everything, no condition needed
return true;
}
else {
return false;
}
}
/**
* Determine if there are no SQL wildcard characters ('%' or '_') in the
* given pattern.
*
* @param pattern The pattern to be checked for wildcards
* @return true
if there are no wildcards in the pattern,
* false
otherwise
*/
public boolean hasNoWildcards(String pattern) {
if (pattern == null)
return true;
int scannedTo = 0;
int pos;
while ((pos = pattern.indexOf('%', scannedTo)) < pattern.length()) {
if (pos == -1) {
break;
}
if ((pos == 0) || (pattern.charAt(pos - 1) != '\\')) {
return false;
}
scannedTo = ++pos;
}
scannedTo = 0;
while ((pos = pattern.indexOf('_', scannedTo)) < pattern.length()) {
if (pos == -1) {
break;
}
if ((pos == 0) || (pattern.charAt(pos - 1) != '\\')) {
return false;
}
scannedTo = ++pos;
}
return true;
}
/**
* Strips all backslash-escapes from a string.
*
* @param pattern The string to be stripped
* @return pattern with all backslash-escapes removed
*/
public String stripEscape(String pattern) {
StringBuffer stripped = new StringBuffer(pattern.length());
for (int pos = 0; pos < pattern.length(); pos++) {
if (pattern.charAt(pos) != '\\') {
stripped.append(pattern.charAt(pos));
}
}
return stripped.toString();
}
protected String getWantsSystemTables(String[] types) {
for (int i = 0; i < types.length; i++) {
if (SYSTEM_TABLE.equals(types[i])) {
return "T";
}
}
return "F";
}
protected String getWantsTables(String[] types) {
for (int i = 0; i < types.length; i++) {
if (TABLE.equals(types[i])) {
return "T";
}
}
return "F";
}
protected String getWantsViews(String[] types) {
for (int i = 0; i < types.length; i++) {
if (VIEW.equals(types[i])) {
return "T";
}
}
return "F";
}
/**
* Strips a leading and trailing quote (double or single) from a string.
*
* @param pattern the string to be stripped
* @return a copy of pattern
with leading and trailing quote
* removed
*/
public String stripQuotes(String pattern, boolean uppercase) {
if ((pattern.length() >= 2)
&& (pattern.charAt(0) == '\"')
&& (pattern.charAt(pattern.length() - 1) == '\"'))
{
return pattern.substring(1, pattern.length() - 1);
}
else {
if (uppercase)
return pattern.toUpperCase();
else
return pattern;
}
}
public ResultSet getPseudoColumns(String catalog, String schemaPattern,
String tableNamePattern, String columnNamePattern) throws SQLException {
// TODO Write implementation
throw new FBDriverNotCapableException();
}
public boolean generatedKeyAlwaysReturned() throws SQLException {
// TODO Double check if this is correct
return false;
}
/*
* (non-Javadoc)
*
* @see org.firebirdsql.jdbc.FirebirdDatabaseMetaData#getProcedureSourceCode(java.lang.String)
*/
public String getProcedureSourceCode(String procedureName)
throws SQLException {
String sResult = null;
String sql = "Select RDB$PROCEDURE_SOURCE From RDB$PROCEDURES Where "
+ "RDB$PROCEDURE_NAME = ?";
ArrayList params = new ArrayList();
params.add(procedureName);
ResultSet rs = doQuery(sql, params);
if (rs.next()) sResult = rs.getString(1);
rs.close();
return sResult;
}
/*
* (non-Javadoc)
*
* @see org.firebirdsql.jdbc.FirebirdDatabaseMetaData#getTriggerSourceCode(java.lang.String)
*/
public String getTriggerSourceCode(String triggerName) throws SQLException {
String sResult = null;
String sql = "Select RDB$TRIGGER_SOURCE From RDB$TRIGGERS Where "
+ "RDB$TRIGGER_NAME = ?";
ArrayList params = new ArrayList();
params.add(triggerName);
ResultSet rs = doQuery(sql, params);
if (rs.next()) sResult = rs.getString(1);
rs.close();
return sResult;
}
/*
* (non-Javadoc)
*
* @see org.firebirdsql.jdbc.FirebirdDatabaseMetaData#getViewSourceCode(java.lang.String)
*/
public String getViewSourceCode(String viewName) throws SQLException {
String sResult = null;
String sql = "Select RDB$VIEW_SOURCE From RDB$RELATIONS Where "
+ "RDB$RELATION_NAME = ?";
ArrayList params = new ArrayList();
params.add(viewName);
ResultSet rs = doQuery(sql, params);
if (rs.next()) sResult = rs.getString(1);
rs.close();
return sResult;
}
protected void checkCatalogAndSchema(String catalog, String schema) throws SQLException {
/*
* we ignore incorrect catalog and schema specification as
* suggested by Thomas Kellerer in JDBC Forum
*/
}
protected class Clause {
private String condition = "";
private String value;
private String originalCaseValue;
public Clause (String columnName, String pattern) {
if (pattern == null) {
return;
}
else if (isAllCondition(pattern)) {
//do nothing to tableCondition
return;
}
else if (hasNoWildcards(pattern)) {
value = stripQuotes(stripEscape(pattern), true);
originalCaseValue = stripQuotes(stripEscape(pattern), false);
condition = columnName + " = ? and ";
}
else {
value = stripQuotes(pattern, true) + SPACES + "%";
originalCaseValue = stripQuotes(pattern, false) + SPACES + "%";
condition = columnName + " || '" + SPACES + "' like ? escape '\\' and ";
}
}
public String getCondition() {
return condition;
}
public String getValue() {
return value;
}
public String getOriginalCaseValue() {
return originalCaseValue;
}
}
protected byte[] getBytes(String value){
if (value !=null)
return value.getBytes();
else
return null;
}
private AbstractPreparedStatement getStatement(String sql) throws SQLException {
AbstractPreparedStatement s =
(AbstractPreparedStatement)statements.get(sql);
if (s != null && s.isClosed()) {
statements.remove(sql);
s = null;
}
if (s != null)
return s;
if (connection == null) {
InternalTransactionCoordinator.MetaDataTransactionCoordinator metaDataTransactionCoordinator =
new InternalTransactionCoordinator.MetaDataTransactionCoordinator();
s = FBStatementFactory.createPreparedStatement(gdsHelper, sql,
ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY,
FirebirdResultSet.CLOSE_CURSORS_AT_COMMIT,
metaDataTransactionCoordinator, metaDataTransactionCoordinator,
true, true, false);
} else {
s = (AbstractPreparedStatement)connection.prepareMetaDataStatement(
sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
}
statements.put(sql, s);
return s;
}
/**
* Execute an sql query with a given set of parameters.
*
* @param sql
* The sql statement to be used for the query
* @param params
* The parameters to be used in the query
* @throws SQLException
* if a database access error occurs
*/
public ResultSet doQuery(String sql, List params)
throws SQLException {
AbstractPreparedStatement s = getStatement(sql);
for (int i = 0; i < params.size(); i++)
s.setStringForced(i + 1, (String) params.get(i));
return s.executeMetaDataQuery();
}
}