EMMA Coverage Report (generated Wed Jul 26 14:28:59 CDT 2006)
[all classes][com.mysql.jdbc]

COVERAGE SUMMARY FOR SOURCE FILE [DatabaseMetaData.java]

nameclass, %method, %block, %line, %
DatabaseMetaData.java94%  (15/16)90%  (195/216)88%  (11413/12946)86%  (2187.7/2537)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class DatabaseMetaData$ResultSetIterator0%   (0/1)0%   (0/4)0%   (0/27)0%   (0/8)
DatabaseMetaData$ResultSetIterator (DatabaseMetaData, ResultSet, int): void 0%   (0/1)0%   (0/13)0%   (0/4)
close (): void 0%   (0/1)0%   (0/4)0%   (0/2)
hasNext (): boolean 0%   (0/1)0%   (0/4)0%   (0/1)
next (): Object 0%   (0/1)0%   (0/6)0%   (0/1)
     
class DatabaseMetaData$5100% (1/1)100% (2/2)69%  (107/155)67%  (22/33)
forEach (Object): void 100% (1/1)65%  (90/138)66%  (21/32)
DatabaseMetaData$5 (DatabaseMetaData, DatabaseMetaData$IteratorWithCleanup, S... 100% (1/1)100% (17/17)100% (1/1)
     
class DatabaseMetaData$1100% (1/1)100% (2/2)77%  (204/266)78%  (40/51)
forEach (Object): void 100% (1/1)75%  (187/249)78%  (39/50)
DatabaseMetaData$1 (DatabaseMetaData, DatabaseMetaData$IteratorWithCleanup, S... 100% (1/1)100% (17/17)100% (1/1)
     
class DatabaseMetaData$4100% (1/1)100% (2/2)79%  (114/145)77%  (23/30)
forEach (Object): void 100% (1/1)76%  (97/128)76%  (22/29)
DatabaseMetaData$4 (DatabaseMetaData, DatabaseMetaData$IteratorWithCleanup, S... 100% (1/1)100% (17/17)100% (1/1)
     
class DatabaseMetaData$8100% (1/1)100% (2/2)86%  (172/199)83%  (48.9/59)
forEach (Object): void 100% (1/1)85%  (155/182)83%  (47.9/58)
DatabaseMetaData$8 (DatabaseMetaData, DatabaseMetaData$IteratorWithCleanup, S... 100% (1/1)100% (17/17)100% (1/1)
     
class DatabaseMetaData$3100% (1/1)100% (2/2)88%  (290/331)88%  (51.7/59)
forEach (Object): void 100% (1/1)86%  (258/299)87%  (50.7/58)
DatabaseMetaData$3 (DatabaseMetaData, DatabaseMetaData$IteratorWithCleanup, S... 100% (1/1)100% (32/32)100% (1/1)
     
class DatabaseMetaData100% (1/1)91%  (168/185)88%  (8817/9973)87%  (1644.3/1896)
getAttributes (String, String, String, String): ResultSet 0%   (0/1)0%   (0/220)0%   (0/23)
getConnection (): Connection 0%   (0/1)0%   (0/3)0%   (0/1)
getDatabaseMajorVersion (): int 0%   (0/1)0%   (0/4)0%   (0/1)
getDatabaseMinorVersion (): int 0%   (0/1)0%   (0/4)0%   (0/1)
getJDBCMajorVersion (): int 0%   (0/1)0%   (0/2)0%   (0/1)
getJDBCMinorVersion (): int 0%   (0/1)0%   (0/2)0%   (0/1)
getResultSetHoldability (): int 0%   (0/1)0%   (0/2)0%   (0/1)
getSQLStateType (): int 0%   (0/1)0%   (0/17)0%   (0/5)
getSuperTables (String, String, String): ResultSet 0%   (0/1)0%   (0/50)0%   (0/6)
getSuperTypes (String, String, String): ResultSet 0%   (0/1)0%   (0/70)0%   (0/8)
locatorsUpdateCopy (): boolean 0%   (0/1)0%   (0/8)0%   (0/1)
supportsGetGeneratedKeys (): boolean 0%   (0/1)0%   (0/2)0%   (0/1)
supportsMultipleOpenResults (): boolean 0%   (0/1)0%   (0/2)0%   (0/1)
supportsNamedParameters (): boolean 0%   (0/1)0%   (0/2)0%   (0/1)
supportsResultSetHoldability (int): boolean 0%   (0/1)0%   (0/7)0%   (0/1)
supportsStatementPooling (): boolean 0%   (0/1)0%   (0/2)0%   (0/1)
usesLocalFiles (): boolean 0%   (0/1)0%   (0/2)0%   (0/1)
getCascadeDeleteOption (String): int 100% (1/1)23%  (9/39)25%  (3/12)
getTablePrivileges (String, String, String): ResultSet 100% (1/1)44%  (144/327)42%  (32.4/78)
getCascadeUpdateOption (String): int 100% (1/1)49%  (19/39)42%  (5/12)
getColumnPrivileges (String, String, String, String): ResultSet 100% (1/1)51%  (156/307)49%  (33.4/68)
supportsSavepoints (): boolean 100% (1/1)56%  (10/18)56%  (0.6/1)
s2b (String): byte [] 100% (1/1)67%  (28/42)62%  (8/13)
getDefaultTransactionIsolation (): int 100% (1/1)75%  (6/8)67%  (2/3)
getUserName (): String 100% (1/1)76%  (45/59)73%  (15.4/21)
supportsConvert (int, int): boolean 100% (1/1)77%  (34/44)77%  (17/22)
getCatalogIterator (String): DatabaseMetaData$IteratorWithCleanup 100% (1/1)81%  (35/43)88%  (7/8)
getCallStmtParameterTypes (String, String, String, List): void 100% (1/1)82%  (547/666)81%  (127.6/157)
convertTypeDescriptorToProcedureRow (byte [], String, boolean, boolean, boole... 100% (1/1)82%  (127/154)80%  (24/30)
getTables (String, String, String, String []): ResultSet 100% (1/1)83%  (89/107)80%  (14.4/18)
supportsTransactionIsolationLevel (int): boolean 100% (1/1)83%  (10/12)80%  (4/5)
getIdentifierQuoteString (): String 100% (1/1)86%  (12/14)80%  (4/5)
nullsAreSortedLow (): boolean 100% (1/1)86%  (6/7)86%  (0.9/1)
parseTableStatusIntoLocalAndReferencedColumns (String): DatabaseMetaData$Loca... 100% (1/1)86%  (158/184)80%  (24.9/31)
DatabaseMetaData (Connection, String): void 100% (1/1)86%  (25/29)80%  (8/10)
getCatalogs (): ResultSet 100% (1/1)89%  (78/88)82%  (21.4/26)
nullsAreSortedAtStart (): boolean 100% (1/1)89%  (16/18)89%  (0.9/1)
removeQuotedId (String): String 100% (1/1)91%  (40/44)85%  (11/13)
convertToJdbcFunctionList (String, ResultSet, boolean, String, Map, int): void 100% (1/1)91%  (83/91)94%  (20.7/22)
extractForeignKeyForTable (ArrayList, ResultSet, String): List 100% (1/1)91%  (321/351)91%  (71/78)
convertToJdbcProcedureList (boolean, String, ResultSet, boolean, String, Map,... 100% (1/1)92%  (98/106)94%  (21.7/23)
getProcedureColumns (String, String, String, String): ResultSet 100% (1/1)94%  (218/233)84%  (36.9/44)
getResultsImpl (String, String, String, List, String, boolean): void 100% (1/1)96%  (157/164)96%  (27.9/29)
getPrimaryKeys (String, String, String): ResultSet 100% (1/1)96%  (103/107)94%  (15/16)
extractForeignKeyFromCreateTable (String, String): ResultSet 100% (1/1)97%  (167/173)96%  (36.4/38)
getBestRowIdentifier (String, String, String, int, boolean): ResultSet 100% (1/1)97%  (123/127)94%  (17/18)
getProcedures (String, String, String): ResultSet 100% (1/1)97%  (124/128)95%  (18/19)
getExportedKeys (String, String, String): ResultSet 100% (1/1)98%  (190/194)96%  (24/25)
getImportedKeys (String, String, String): ResultSet 100% (1/1)98%  (190/194)96%  (24/25)
getCrossReference (String, String, String, String, String, String): ResultSet 100% (1/1)98%  (195/199)96%  (24/25)
getColumns (String, String, String, String): ResultSet 100% (1/1)98%  (239/243)97%  (30/31)
<static initializer> 100% (1/1)100% (7/7)100% (2/2)
allProceduresAreCallable (): boolean 100% (1/1)100% (2/2)100% (1/1)
allTablesAreSelectable (): boolean 100% (1/1)100% (2/2)100% (1/1)
buildResultSet (Field [], ArrayList): ResultSet 100% (1/1)100% (36/36)100% (5/5)
dataDefinitionCausesTransactionCommit (): boolean 100% (1/1)100% (2/2)100% (1/1)
dataDefinitionIgnoredInTransactions (): boolean 100% (1/1)100% (2/2)100% (1/1)
deletesAreDetected (int): boolean 100% (1/1)100% (2/2)100% (1/1)
doesMaxRowSizeIncludeBlobs (): boolean 100% (1/1)100% (2/2)100% (1/1)
getCatalogSeparator (): String 100% (1/1)100% (2/2)100% (1/1)
getCatalogTerm (): String 100% (1/1)100% (2/2)100% (1/1)
getDatabaseProductName (): String 100% (1/1)100% (2/2)100% (1/1)
getDatabaseProductVersion (): String 100% (1/1)100% (4/4)100% (1/1)
getDriverMajorVersion (): int 100% (1/1)100% (2/2)100% (1/1)
getDriverMinorVersion (): int 100% (1/1)100% (2/2)100% (1/1)
getDriverName (): String 100% (1/1)100% (2/2)100% (1/1)
getDriverVersion (): String 100% (1/1)100% (2/2)100% (1/1)
getExportKeyResults (String, String, String, List, String): void 100% (1/1)100% (9/9)100% (2/2)
getExtraNameCharacters (): String 100% (1/1)100% (2/2)100% (1/1)
getForeignKeyActions (String): int [] 100% (1/1)100% (44/44)100% (7/7)
getImportKeyResults (String, String, String, List): void 100% (1/1)100% (9/9)100% (2/2)
getIndexInfo (String, String, String, boolean, boolean): ResultSet 100% (1/1)100% (173/173)100% (21/21)
getMaxBinaryLiteralLength (): int 100% (1/1)100% (2/2)100% (1/1)
getMaxCatalogNameLength (): int 100% (1/1)100% (2/2)100% (1/1)
getMaxCharLiteralLength (): int 100% (1/1)100% (2/2)100% (1/1)
getMaxColumnNameLength (): int 100% (1/1)100% (2/2)100% (1/1)
getMaxColumnsInGroupBy (): int 100% (1/1)100% (2/2)100% (1/1)
getMaxColumnsInIndex (): int 100% (1/1)100% (2/2)100% (1/1)
getMaxColumnsInOrderBy (): int 100% (1/1)100% (2/2)100% (1/1)
getMaxColumnsInSelect (): int 100% (1/1)100% (2/2)100% (1/1)
getMaxColumnsInTable (): int 100% (1/1)100% (2/2)100% (1/1)
getMaxConnections (): int 100% (1/1)100% (2/2)100% (1/1)
getMaxCursorNameLength (): int 100% (1/1)100% (2/2)100% (1/1)
getMaxIndexLength (): int 100% (1/1)100% (2/2)100% (1/1)
getMaxProcedureNameLength (): int 100% (1/1)100% (2/2)100% (1/1)
getMaxRowSize (): int 100% (1/1)100% (2/2)100% (1/1)
getMaxSchemaNameLength (): int 100% (1/1)100% (2/2)100% (1/1)
getMaxStatementLength (): int 100% (1/1)100% (4/4)100% (1/1)
getMaxStatements (): int 100% (1/1)100% (2/2)100% (1/1)
getMaxTableNameLength (): int 100% (1/1)100% (2/2)100% (1/1)
getMaxTablesInSelect (): int 100% (1/1)100% (2/2)100% (1/1)
getMaxUserNameLength (): int 100% (1/1)100% (2/2)100% (1/1)
getNumericFunctions (): String 100% (1/1)100% (2/2)100% (1/1)
getProcedureTerm (): String 100% (1/1)100% (2/2)100% (1/1)
getSQLKeywords (): String 100% (1/1)100% (2/2)100% (1/1)
getSchemaTerm (): String 100% (1/1)100% (2/2)100% (1/1)
getSchemas (): ResultSet 100% (1/1)100% (24/24)100% (5/5)
getSearchStringEscape (): String 100% (1/1)100% (2/2)100% (1/1)
getStringFunctions (): String 100% (1/1)100% (2/2)100% (1/1)
getSystemFunctions (): String 100% (1/1)100% (2/2)100% (1/1)
getTableNameWithCase (String): String 100% (1/1)100% (11/11)100% (2/2)
getTableTypes (): ResultSet 100% (1/1)100% (64/64)100% (14/14)
getTimeDateFunctions (): String 100% (1/1)100% (2/2)100% (1/1)
getTypeInfo (): ResultSet 100% (1/1)100% (4105/4105)100% (702/702)
getUDTs (String, String, String, int []): ResultSet 100% (1/1)100% (72/72)100% (9/9)
getURL (): String 100% (1/1)100% (4/4)100% (1/1)
getVersionColumns (String, String, String): ResultSet 100% (1/1)100% (90/90)100% (10/10)
insertsAreDetected (int): boolean 100% (1/1)100% (2/2)100% (1/1)
isCatalogAtStart (): boolean 100% (1/1)100% (2/2)100% (1/1)
isReadOnly (): boolean 100% (1/1)100% (2/2)100% (1/1)
nullPlusNonNullIsNull (): boolean 100% (1/1)100% (2/2)100% (1/1)
nullsAreSortedAtEnd (): boolean 100% (1/1)100% (2/2)100% (1/1)
nullsAreSortedHigh (): boolean 100% (1/1)100% (2/2)100% (1/1)
othersDeletesAreVisible (int): boolean 100% (1/1)100% (2/2)100% (1/1)
othersInsertsAreVisible (int): boolean 100% (1/1)100% (2/2)100% (1/1)
othersUpdatesAreVisible (int): boolean 100% (1/1)100% (2/2)100% (1/1)
ownDeletesAreVisible (int): boolean 100% (1/1)100% (2/2)100% (1/1)
ownInsertsAreVisible (int): boolean 100% (1/1)100% (2/2)100% (1/1)
ownUpdatesAreVisible (int): boolean 100% (1/1)100% (2/2)100% (1/1)
storesLowerCaseIdentifiers (): boolean 100% (1/1)100% (4/4)100% (1/1)
storesLowerCaseQuotedIdentifiers (): boolean 100% (1/1)100% (4/4)100% (1/1)
storesMixedCaseIdentifiers (): boolean 100% (1/1)100% (8/8)100% (1/1)
storesMixedCaseQuotedIdentifiers (): boolean 100% (1/1)100% (8/8)100% (1/1)
storesUpperCaseIdentifiers (): boolean 100% (1/1)100% (2/2)100% (1/1)
storesUpperCaseQuotedIdentifiers (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsANSI92EntryLevelSQL (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsANSI92FullSQL (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsANSI92IntermediateSQL (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsAlterTableWithAddColumn (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsAlterTableWithDropColumn (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsBatchUpdates (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsCatalogsInDataManipulation (): boolean 100% (1/1)100% (7/7)100% (1/1)
supportsCatalogsInIndexDefinitions (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsCatalogsInPrivilegeDefinitions (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsCatalogsInProcedureCalls (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsCatalogsInTableDefinitions (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsColumnAliasing (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsConvert (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsCoreSQLGrammar (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsCorrelatedSubqueries (): boolean 100% (1/1)100% (7/7)100% (1/1)
supportsDataDefinitionAndDataManipulationTransactions (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsDataManipulationTransactionsOnly (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsDifferentTableCorrelationNames (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsExpressionsInOrderBy (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsExtendedSQLGrammar (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsFullOuterJoins (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsGroupBy (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsGroupByBeyondSelect (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsGroupByUnrelated (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsIntegrityEnhancementFacility (): boolean 100% (1/1)100% (8/8)100% (3/3)
supportsLikeEscapeClause (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsLimitedOuterJoins (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsMinimumSQLGrammar (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsMixedCaseIdentifiers (): boolean 100% (1/1)100% (8/8)100% (1/1)
supportsMixedCaseQuotedIdentifiers (): boolean 100% (1/1)100% (8/8)100% (1/1)
supportsMultipleResultSets (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsMultipleTransactions (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsNonNullableColumns (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsOpenCursorsAcrossCommit (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsOpenCursorsAcrossRollback (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsOpenStatementsAcrossCommit (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsOpenStatementsAcrossRollback (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsOrderByUnrelated (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsOuterJoins (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsPositionedDelete (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsPositionedUpdate (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsResultSetConcurrency (int, int): boolean 100% (1/1)100% (32/32)100% (9/9)
supportsResultSetType (int): boolean 100% (1/1)100% (7/7)100% (1/1)
supportsSchemasInDataManipulation (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsSchemasInIndexDefinitions (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsSchemasInPrivilegeDefinitions (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsSchemasInProcedureCalls (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsSchemasInTableDefinitions (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsSelectForUpdate (): boolean 100% (1/1)100% (7/7)100% (1/1)
supportsStoredProcedures (): boolean 100% (1/1)100% (7/7)100% (1/1)
supportsSubqueriesInComparisons (): boolean 100% (1/1)100% (7/7)100% (1/1)
supportsSubqueriesInExists (): boolean 100% (1/1)100% (7/7)100% (1/1)
supportsSubqueriesInIns (): boolean 100% (1/1)100% (7/7)100% (1/1)
supportsSubqueriesInQuantifieds (): boolean 100% (1/1)100% (7/7)100% (1/1)
supportsTableCorrelationNames (): boolean 100% (1/1)100% (2/2)100% (1/1)
supportsTransactions (): boolean 100% (1/1)100% (4/4)100% (1/1)
supportsUnion (): boolean 100% (1/1)100% (7/7)100% (1/1)
supportsUnionAll (): boolean 100% (1/1)100% (7/7)100% (1/1)
updatesAreDetected (int): boolean 100% (1/1)100% (2/2)100% (1/1)
usesLocalFilePerTable (): boolean 100% (1/1)100% (2/2)100% (1/1)
     
class DatabaseMetaData$2100% (1/1)100% (2/2)88%  (473/535)82%  (91.4/111)
forEach (Object): void 100% (1/1)88%  (447/509)82%  (90.4/110)
DatabaseMetaData$2 (DatabaseMetaData, DatabaseMetaData$IteratorWithCleanup, S... 100% (1/1)100% (26/26)100% (1/1)
     
class DatabaseMetaData$9100% (1/1)100% (2/2)90%  (310/345)84%  (61.5/73)
forEach (Object): void 100% (1/1)89%  (290/325)84%  (60.5/72)
DatabaseMetaData$9 (DatabaseMetaData, DatabaseMetaData$IteratorWithCleanup, S... 100% (1/1)100% (20/20)100% (1/1)
     
class DatabaseMetaData$TypeDescriptor100% (1/1)100% (1/1)92%  (442/478)91%  (101/111)
DatabaseMetaData$TypeDescriptor (DatabaseMetaData, String, String): void 100% (1/1)92%  (442/478)91%  (101/111)
     
class DatabaseMetaData$7100% (1/1)100% (2/2)98%  (182/186)97%  (36/37)
forEach (Object): void 100% (1/1)98%  (165/169)97%  (35/36)
DatabaseMetaData$7 (DatabaseMetaData, DatabaseMetaData$IteratorWithCleanup, S... 100% (1/1)100% (17/17)100% (1/1)
     
class DatabaseMetaData$6100% (1/1)100% (2/2)98%  (222/226)98%  (43.9/45)
forEach (Object): void 100% (1/1)98%  (202/206)98%  (42.9/44)
DatabaseMetaData$6 (DatabaseMetaData, DatabaseMetaData$IteratorWithCleanup, S... 100% (1/1)100% (20/20)100% (1/1)
     
class DatabaseMetaData$IterateBlock100% (1/1)100% (2/2)100% (30/30)100% (8/8)
DatabaseMetaData$IterateBlock (DatabaseMetaData, DatabaseMetaData$IteratorWit... 100% (1/1)100% (9/9)100% (3/3)
doForAll (): void 100% (1/1)100% (21/21)100% (5/5)
     
class DatabaseMetaData$IteratorWithCleanup100% (1/1)100% (1/1)100% (6/6)100% (1/1)
DatabaseMetaData$IteratorWithCleanup (DatabaseMetaData): void 100% (1/1)100% (6/6)100% (1/1)
     
class DatabaseMetaData$LocalAndReferencedColumns100% (1/1)100% (1/1)100% (21/21)100% (7/7)
DatabaseMetaData$LocalAndReferencedColumns (DatabaseMetaData, List, List, Str... 100% (1/1)100% (21/21)100% (7/7)
     
class DatabaseMetaData$SingleStringIterator100% (1/1)100% (4/4)100% (23/23)100% (8/8)
DatabaseMetaData$SingleStringIterator (DatabaseMetaData, String): void 100% (1/1)100% (13/13)100% (4/4)
close (): void 100% (1/1)100% (1/1)100% (1/1)
hasNext (): boolean 100% (1/1)100% (3/3)100% (1/1)
next (): Object 100% (1/1)100% (6/6)100% (2/2)

1/*
2 Copyright (C) 2002-2004 MySQL AB
3 
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of version 2 of the GNU General Public License as 
6 published by the Free Software Foundation.
7 
8 There are special exceptions to the terms and conditions of the GPL 
9 as it is applied to this software. View the full text of the 
10 exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
11 software distribution.
12 
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21 
22 
23 
24 */
25package com.mysql.jdbc;
26 
27import java.io.UnsupportedEncodingException;
28 
29import java.sql.ResultSet;
30import java.sql.SQLException;
31import java.sql.Statement;
32import java.sql.Types;
33 
34import java.util.ArrayList;
35import java.util.Collections;
36import java.util.HashMap;
37import java.util.Iterator;
38import java.util.List;
39import java.util.Locale;
40import java.util.Map;
41import java.util.StringTokenizer;
42import java.util.TreeMap;
43 
44/**
45 * JDBC Interface to Mysql functions
46 * <p>
47 * This class provides information about the database as a whole.
48 * </p>
49 * <p>
50 * Many of the methods here return lists of information in ResultSets. You can
51 * use the normal ResultSet methods such as getString and getInt to retrieve the
52 * data from these ResultSets. If a given form of metadata is not available,
53 * these methods show throw a SQLException.
54 * </p>
55 * <p>
56 * Some of these methods take arguments that are String patterns. These methods
57 * all have names such as fooPattern. Within a pattern String "%" means match
58 * any substring of 0 or more characters and "_" means match any one character.
59 * </p>
60 * 
61 * @author Mark Matthews
62 * @version $Id: DatabaseMetaData.java,v 1.27.4.66 2005/05/03 18:40:39 mmatthews
63 *          Exp $
64 */
65public class DatabaseMetaData implements java.sql.DatabaseMetaData {
66        protected abstract class IterateBlock {
67                IteratorWithCleanup iterator;
68 
69                IterateBlock(IteratorWithCleanup i) {
70                        iterator = i;
71                }
72 
73                public void doForAll() throws SQLException {
74                        try {
75                                while (iterator.hasNext()) {
76                                        forEach(iterator.next());
77                                }
78                        } finally {
79                                iterator.close();
80                        }
81                }
82 
83                abstract void forEach(Object each) throws SQLException;
84        }
85 
86        protected abstract class IteratorWithCleanup {
87                abstract void close() throws SQLException;
88 
89                abstract boolean hasNext() throws SQLException;
90 
91                abstract Object next() throws SQLException;
92        }
93 
94        class LocalAndReferencedColumns {
95                String constraintName;
96 
97                List localColumnsList;
98 
99                String referencedCatalog;
100 
101                List referencedColumnsList;
102 
103                String referencedTable;
104 
105                LocalAndReferencedColumns(List localColumns, List refColumns,
106                                String constName, String refCatalog, String refTable) {
107                        this.localColumnsList = localColumns;
108                        this.referencedColumnsList = refColumns;
109                        this.constraintName = constName;
110                        this.referencedTable = refTable;
111                        this.referencedCatalog = refCatalog;
112                }
113        }
114 
115        protected class ResultSetIterator extends IteratorWithCleanup {
116                int colIndex;
117 
118                ResultSet resultSet;
119 
120                ResultSetIterator(ResultSet rs, int index) {
121                        resultSet = rs;
122                        colIndex = index;
123                }
124 
125                void close() throws SQLException {
126                        resultSet.close();
127                }
128 
129                boolean hasNext() throws SQLException {
130                        return resultSet.next();
131                }
132 
133                Object next() throws SQLException {
134                        return resultSet.getObject(colIndex);
135                }
136        }
137 
138        protected class SingleStringIterator extends IteratorWithCleanup {
139                boolean onFirst = true;
140 
141                String value;
142 
143                SingleStringIterator(String s) {
144                        value = s;
145                }
146 
147                void close() throws SQLException {
148                        // not needed
149 
150                }
151 
152                boolean hasNext() throws SQLException {
153                        return onFirst;
154                }
155 
156                Object next() throws SQLException {
157                        onFirst = false;
158                        return value;
159                }
160        }
161 
162        /**
163         * Parses and represents common data type information used by various
164         * column/parameter methods.
165         */
166        class TypeDescriptor {
167                int bufferLength;
168 
169                int charOctetLength;
170 
171                int columnSize;
172 
173                short dataType;
174 
175                int decimalDigits;
176 
177                String isNullable;
178 
179                int nullability;
180 
181                int numPrecRadix = 10;
182 
183                String typeName;
184 
185                TypeDescriptor(String typeInfo, String nullabilityInfo)
186                                throws SQLException {
187                        String mysqlType = "";
188                        String fullMysqlType = null;
189 
190                        if (typeInfo.indexOf("(") != -1) {
191                                mysqlType = typeInfo.substring(0, typeInfo.indexOf("("));
192                        } else {
193                                mysqlType = typeInfo;
194                        }
195 
196                        int indexOfUnsignedInMysqlType = StringUtils.indexOfIgnoreCase(
197                                        mysqlType, "unsigned");
198 
199                        if (indexOfUnsignedInMysqlType != -1) {
200                                mysqlType = mysqlType.substring(0,
201                                                (indexOfUnsignedInMysqlType - 1));
202                        }
203 
204                        // Add unsigned to typename reported to enduser as 'native type', if
205                        // present
206 
207                        if (StringUtils.indexOfIgnoreCase(typeInfo, "unsigned") != -1) {
208                                fullMysqlType = mysqlType + " unsigned";
209                        } else {
210                                fullMysqlType = mysqlType;
211                        }
212 
213                        if (conn.getCapitalizeTypeNames()) {
214                                fullMysqlType = fullMysqlType.toUpperCase(Locale.ENGLISH);
215                        }
216 
217                        this.dataType = (short) MysqlDefs.mysqlToJavaType(mysqlType);
218 
219                        this.typeName = fullMysqlType;
220 
221                        // Figure Out the Size
222                        if (typeInfo != null) {
223                                if (StringUtils.startsWithIgnoreCase(typeInfo, "enum")
224                                                || StringUtils.startsWithIgnoreCase(typeInfo, "set")) {
225                                        String temp = typeInfo.substring(typeInfo.indexOf("("),
226                                                        typeInfo.lastIndexOf(")"));
227                                        java.util.StringTokenizer tokenizer = new java.util.StringTokenizer(
228                                                        temp, ",");
229                                        int maxLength = 0;
230 
231                                        while (tokenizer.hasMoreTokens()) {
232                                                maxLength = Math.max(maxLength, (tokenizer.nextToken()
233                                                                .length() - 2));
234                                        }
235 
236                                        this.columnSize = maxLength;
237                                        this.decimalDigits = 0;
238                                } else if (typeInfo.indexOf(",") != -1) {
239                                        // Numeric with decimals
240                                        this.columnSize = Integer.parseInt(typeInfo.substring(
241                                                        (typeInfo.indexOf("(") + 1),
242                                                        (typeInfo.indexOf(","))));
243                                        this.decimalDigits = Integer.parseInt(typeInfo.substring(
244                                                        (typeInfo.indexOf(",") + 1),
245                                                        (typeInfo.indexOf(")"))));
246                                } else {
247                                        this.columnSize = 0;
248 
249                                        /* If the size is specified with the DDL, use that */
250                                        if (typeInfo.indexOf("(") != -1) {
251                                                int endParenIndex = typeInfo.indexOf(")");
252 
253                                                if (endParenIndex == -1) {
254                                                        endParenIndex = typeInfo.length();
255                                                }
256 
257                                                this.columnSize = Integer.parseInt(typeInfo.substring(
258                                                                (typeInfo.indexOf("(") + 1), endParenIndex));
259 
260                                                // Adjust for pseudo-boolean
261                                                if (conn.getTinyInt1isBit()
262                                                                && this.columnSize == 1
263                                                                && StringUtils.startsWithIgnoreCase(typeInfo,
264                                                                                0, "tinyint")) {
265                                                        if (conn.getTransformedBitIsBoolean()) {
266                                                                this.dataType = Types.BOOLEAN;
267                                                                this.typeName = "BOOLEAN";
268                                                        } else {
269                                                                this.dataType = Types.BIT;
270                                                                this.typeName = "BIT";
271                                                        }
272                                                }
273                                        } else if (typeInfo.equalsIgnoreCase("tinyint")) {
274                                                this.columnSize = 1;
275                                        } else if (typeInfo.equalsIgnoreCase("smallint")) {
276                                                this.columnSize = 6;
277                                        } else if (typeInfo.equalsIgnoreCase("mediumint")) {
278                                                this.columnSize = 6;
279                                        } else if (typeInfo.equalsIgnoreCase("int")) {
280                                                this.columnSize = 11;
281                                        } else if (typeInfo.equalsIgnoreCase("integer")) {
282                                                this.columnSize = 11;
283                                        } else if (typeInfo.equalsIgnoreCase("bigint")) {
284                                                this.columnSize = 25;
285                                        } else if (typeInfo.equalsIgnoreCase("int24")) {
286                                                this.columnSize = 25;
287                                        } else if (typeInfo.equalsIgnoreCase("real")) {
288                                                this.columnSize = 12;
289                                        } else if (typeInfo.equalsIgnoreCase("float")) {
290                                                this.columnSize = 12;
291                                        } else if (typeInfo.equalsIgnoreCase("decimal")) {
292                                                this.columnSize = 12;
293                                        } else if (typeInfo.equalsIgnoreCase("numeric")) {
294                                                this.columnSize = 12;
295                                        } else if (typeInfo.equalsIgnoreCase("double")) {
296                                                this.columnSize = 22;
297                                        } else if (typeInfo.equalsIgnoreCase("char")) {
298                                                this.columnSize = 1;
299                                        } else if (typeInfo.equalsIgnoreCase("varchar")) {
300                                                this.columnSize = 255;
301                                        } else if (typeInfo.equalsIgnoreCase("date")) {
302                                                this.columnSize = 10;
303                                        } else if (typeInfo.equalsIgnoreCase("time")) {
304                                                this.columnSize = 8;
305                                        } else if (typeInfo.equalsIgnoreCase("timestamp")) {
306                                                this.columnSize = 19;
307                                        } else if (typeInfo.equalsIgnoreCase("datetime")) {
308                                                this.columnSize = 19;
309                                        } else if (typeInfo.equalsIgnoreCase("tinyblob")) {
310                                                this.columnSize = 255;
311                                        } else if (typeInfo.equalsIgnoreCase("blob")) {
312                                                this.columnSize = 65535;
313                                        } else if (typeInfo.equalsIgnoreCase("mediumblob")) {
314                                                this.columnSize = 16277215;
315                                        } else if (typeInfo.equalsIgnoreCase("longblob")) {
316                                                this.columnSize = Integer.MAX_VALUE;
317                                        } else if (typeInfo.equalsIgnoreCase("tinytext")) {
318                                                this.columnSize = 255;
319                                        } else if (typeInfo.equalsIgnoreCase("text")) {
320                                                this.columnSize = 65535;
321                                        } else if (typeInfo.equalsIgnoreCase("mediumtext")) {
322                                                this.columnSize = 16277215;
323                                        } else if (typeInfo.equalsIgnoreCase("longtext")) {
324                                                this.columnSize = Integer.MAX_VALUE;
325                                        } else if (typeInfo.equalsIgnoreCase("enum")) {
326                                                this.columnSize = 255;
327                                        } else if (typeInfo.equalsIgnoreCase("set")) {
328                                                this.columnSize = 255;
329                                        }
330 
331                                        this.decimalDigits = 0;
332                                }
333                        } else {
334                                this.decimalDigits = 0;
335                                this.columnSize = 0;
336                        }
337 
338                        // BUFFER_LENGTH
339                        this.bufferLength = MysqlIO.getMaxBuf();
340 
341                        // NUM_PREC_RADIX (is this right for char?)
342                        this.numPrecRadix = 10;
343 
344                        // Nullable?
345                        if (nullabilityInfo != null) {
346                                if (nullabilityInfo.equals("YES")) {
347                                        this.nullability = java.sql.DatabaseMetaData.columnNullable;
348                                        this.isNullable = "YES";
349 
350                                        // IS_NULLABLE
351                                } else {
352                                        this.nullability = java.sql.DatabaseMetaData.columnNoNulls;
353                                        this.isNullable = "NO";
354                                }
355                        } else {
356                                this.nullability = java.sql.DatabaseMetaData.columnNoNulls;
357                                this.isNullable = "NO";
358                        }
359                }
360        }
361 
362        private static final int DEFERRABILITY = 13;
363 
364        private static final int DELETE_RULE = 10;
365 
366        private static final int FK_NAME = 11;
367 
368        private static final int FKCOLUMN_NAME = 7;
369 
370        private static final int FKTABLE_CAT = 4;
371 
372        private static final int FKTABLE_NAME = 6;
373 
374        private static final int FKTABLE_SCHEM = 5;
375 
376        private static final int KEY_SEQ = 8;
377 
378        private static final int PK_NAME = 12;
379 
380        private static final int PKCOLUMN_NAME = 3;
381 
382        //
383        // Column indexes used by all DBMD foreign key
384        // ResultSets
385        //
386        private static final int PKTABLE_CAT = 0;
387 
388        private static final int PKTABLE_NAME = 2;
389 
390        private static final int PKTABLE_SCHEM = 1;
391 
392        /** The table type for generic tables that support foreign keys. */
393        private static final String SUPPORTS_FK = "SUPPORTS_FK";
394 
395        private static final byte[] TABLE_AS_BYTES = "TABLE".getBytes();
396 
397        private static final int UPDATE_RULE = 9;
398 
399        private static final byte[] VIEW_AS_BYTES = "VIEW".getBytes();
400 
401        /** The connection to the database */
402        protected Connection conn;
403 
404        /** The 'current' database name being used */
405        protected String database = null;
406 
407        /** What character to use when quoting identifiers */
408        protected String quotedId = null;
409 
410        /**
411         * Creates a new DatabaseMetaData object.
412         * 
413         * @param connToSet
414         *            DOCUMENT ME!
415         * @param databaseToSet
416         *            DOCUMENT ME!
417         */
418        public DatabaseMetaData(Connection connToSet, String databaseToSet) {
419                this.conn = connToSet;
420                this.database = databaseToSet;
421 
422                try {
423                        this.quotedId = this.conn.supportsQuotedIdentifiers() ? getIdentifierQuoteString()
424                                        : "";
425                } catch (SQLException sqlEx) {
426                        // Forced by API, never thrown from getIdentifierQuoteString() in
427                        // this
428                        // implementation.
429                        AssertionFailedException.shouldNotHappen(sqlEx);
430                }
431        }
432 
433        /**
434         * Can all the procedures returned by getProcedures be called by the current
435         * user?
436         * 
437         * @return true if so
438         * @throws SQLException
439         *             DOCUMENT ME!
440         */
441        public boolean allProceduresAreCallable() throws SQLException {
442                return false;
443        }
444 
445        /**
446         * Can all the tables returned by getTable be SELECTed by the current user?
447         * 
448         * @return true if so
449         * @throws SQLException
450         *             DOCUMENT ME!
451         */
452        public boolean allTablesAreSelectable() throws SQLException {
453                return false;
454        }
455 
456        private java.sql.ResultSet buildResultSet(com.mysql.jdbc.Field[] fields,
457                        java.util.ArrayList rows) throws SQLException {
458                return buildResultSet(fields, rows, this.conn);
459        }
460        
461        static java.sql.ResultSet buildResultSet(com.mysql.jdbc.Field[] fields,
462                        java.util.ArrayList rows, Connection c) throws SQLException {
463                int fieldsLength = fields.length;
464 
465                for (int i = 0; i < fieldsLength; i++) {
466                        fields[i].setConnection(c);
467                        fields[i].setUseOldNameMetadata(true);
468                }
469 
470                return new com.mysql.jdbc.ResultSet(c.getCatalog(), fields,
471                                new RowDataStatic(rows), c, null);
472        }
473 
474        private void convertToJdbcFunctionList(String catalog,
475                        ResultSet proceduresRs, boolean needsClientFiltering, String db,
476                        Map procedureRowsOrderedByName, int nameIndex) throws SQLException {
477                while (proceduresRs.next()) {
478                        boolean shouldAdd = true;
479 
480                        if (needsClientFiltering) {
481                                shouldAdd = false;
482 
483                                String procDb = proceduresRs.getString(1);
484 
485                                if (db == null && procDb == null) {
486                                        shouldAdd = true;
487                                } else if (db != null & db.equals(procDb)) {
488                                        shouldAdd = true;
489                                }
490                        }
491 
492                        if (shouldAdd) {
493                                String functionName = proceduresRs.getString(nameIndex);
494                                byte[][] rowData = new byte[8][];
495                                rowData[0] = catalog == null ? null : s2b(catalog);
496                                rowData[1] = null;
497                                rowData[2] = s2b(functionName);
498                                rowData[3] = null;
499                                rowData[4] = null;
500                                rowData[5] = null;
501                                rowData[6] = null;
502                                rowData[7] = s2b(Integer.toString(procedureReturnsResult));
503 
504                                procedureRowsOrderedByName.put(functionName, rowData);
505                        }
506                }
507        }
508 
509        private void convertToJdbcProcedureList(boolean fromSelect, String catalog,
510                        ResultSet proceduresRs, boolean needsClientFiltering, String db,
511                        Map procedureRowsOrderedByName, int nameIndex) throws SQLException {
512                while (proceduresRs.next()) {
513                        boolean shouldAdd = true;
514 
515                        if (needsClientFiltering) {
516                                shouldAdd = false;
517 
518                                String procDb = proceduresRs.getString(1);
519 
520                                if (db == null && procDb == null) {
521                                        shouldAdd = true;
522                                } else if (db != null & db.equals(procDb)) {
523                                        shouldAdd = true;
524                                }
525                        }
526 
527                        if (shouldAdd) {
528                                String procedureName = proceduresRs.getString(nameIndex);
529                                byte[][] rowData = new byte[8][];
530                                rowData[0] = catalog == null ? null : s2b(catalog);
531                                rowData[1] = null;
532                                rowData[2] = s2b(procedureName);
533                                rowData[3] = null;
534                                rowData[4] = null;
535                                rowData[5] = null;
536                                rowData[6] = null;
537 
538                                boolean isFunction = fromSelect ? "FUNCTION"
539                                                .equalsIgnoreCase(proceduresRs.getString("type"))
540                                                : false;
541                                rowData[7] = s2b(isFunction ? Integer
542                                                .toString(procedureReturnsResult) : Integer
543                                                .toString(procedureResultUnknown));
544 
545                                procedureRowsOrderedByName.put(procedureName, rowData);
546                        }
547                }
548        }
549 
550        private byte[][] convertTypeDescriptorToProcedureRow(
551                        byte[] procNameAsBytes, String paramName, boolean isOutParam,
552                        boolean isInParam, boolean isReturnParam, TypeDescriptor typeDesc)
553                        throws SQLException {
554                byte[][] row = new byte[14][];
555                row[0] = null; // PROCEDURE_CAT
556                row[1] = null; // PROCEDURE_SCHEM
557                row[2] = procNameAsBytes; // PROCEDURE/NAME
558                row[3] = s2b(paramName); // COLUMN_NAME
559                // COLUMN_TYPE
560                if (isInParam && isOutParam) {
561                        row[4] = s2b(String.valueOf(procedureColumnInOut));
562                } else if (isInParam) {
563                        row[4] = s2b(String.valueOf(procedureColumnIn));
564                } else if (isOutParam) {
565                        row[4] = s2b(String.valueOf(procedureColumnOut));
566                } else if (isReturnParam) {
567                        row[4] = s2b(String.valueOf(procedureColumnReturn));
568                } else {
569                        row[4] = s2b(String.valueOf(procedureColumnUnknown));
570                }
571                row[5] = s2b(Short.toString(typeDesc.dataType)); // DATA_TYPE
572                row[6] = s2b(typeDesc.typeName); // TYPE_NAME
573                row[7] = s2b(Integer.toString(typeDesc.columnSize)); // PRECISION
574                row[8] = s2b(Integer.toString(typeDesc.bufferLength)); // LENGTH
575                row[9] = s2b(Integer.toString(typeDesc.decimalDigits)); // SCALE
576                row[10] = s2b(Integer.toString(typeDesc.numPrecRadix)); // RADIX
577                // Map 'column****' to 'procedure****'
578                switch (typeDesc.nullability) {
579                case columnNoNulls:
580                        row[11] = s2b(Integer.toString(procedureNoNulls)); // NULLABLE
581 
582                        break;
583 
584                case columnNullable:
585                        row[11] = s2b(Integer.toString(procedureNullable)); // NULLABLE
586 
587                        break;
588 
589                case columnNullableUnknown:
590                        row[11] = s2b(Integer.toString(procedureNullableUnknown)); // nullable
591 
592                        break;
593 
594                default:
595                        throw SQLError.createSQLException(
596                                        "Internal error while parsing callable statement metadata (unknown nullability value fount)",
597                                        SQLError.SQL_STATE_GENERAL_ERROR);
598                }
599                row[12] = null;
600                return row;
601        }
602 
603        /**
604         * Does a data definition statement within a transaction force the
605         * transaction to commit?
606         * 
607         * @return true if so
608         * @throws SQLException
609         *             DOCUMENT ME!
610         */
611        public boolean dataDefinitionCausesTransactionCommit() throws SQLException {
612                return true;
613        }
614 
615        /**
616         * Is a data definition statement within a transaction ignored?
617         * 
618         * @return true if so
619         * @throws SQLException
620         *             DOCUMENT ME!
621         */
622        public boolean dataDefinitionIgnoredInTransactions() throws SQLException {
623                return false;
624        }
625 
626        /**
627         * JDBC 2.0 Determine whether or not a visible row delete can be detected by
628         * calling ResultSet.rowDeleted(). If deletesAreDetected() returns false,
629         * then deleted rows are removed from the result set.
630         * 
631         * @param type
632         *            set type, i.e. ResultSet.TYPE_XXX
633         * @return true if changes are detected by the resultset type
634         * @exception SQLException
635         *                if a database-access error occurs.
636         */
637        public boolean deletesAreDetected(int type) throws SQLException {
638                return false;
639        }
640 
641        // ----------------------------------------------------------------------
642 
643        /**
644         * Did getMaxRowSize() include LONGVARCHAR and LONGVARBINARY blobs?
645         * 
646         * @return true if so
647         * @throws SQLException
648         *             DOCUMENT ME!
649         */
650        public boolean doesMaxRowSizeIncludeBlobs() throws SQLException {
651                return true;
652        }
653 
654        /**
655         * Extracts foreign key info for one table.
656         * 
657         * @param rows
658         *            the list of rows to add to
659         * @param rs
660         *            the result set from 'SHOW CREATE TABLE'
661         * @param catalog
662         *            the database name
663         * @return the list of rows with new rows added
664         * @throws SQLException
665         *             if a database access error occurs
666         */
667        public List extractForeignKeyForTable(ArrayList rows,
668                        java.sql.ResultSet rs, String catalog) throws SQLException {
669                byte[][] row = new byte[3][];
670                row[0] = rs.getBytes(1);
671                row[1] = s2b(SUPPORTS_FK);
672        
673                String createTableString = rs.getString(2);
674                StringTokenizer lineTokenizer = new StringTokenizer(createTableString,
675                                "\n");
676                StringBuffer commentBuf = new StringBuffer("comment; ");
677                boolean firstTime = true;
678        
679                String quoteChar = getIdentifierQuoteString();
680        
681                if (quoteChar == null) {
682                        quoteChar = "`";
683                }
684        
685                while (lineTokenizer.hasMoreTokens()) {
686                        String line = lineTokenizer.nextToken().trim();
687        
688                        String constraintName = null;
689        
690                        if (StringUtils.startsWithIgnoreCase(line, "CONSTRAINT")) {
691                                boolean usingBackTicks = true;
692                                int beginPos = line.indexOf(quoteChar);
693        
694                                if (beginPos == -1) {
695                                        beginPos = line.indexOf("\"");
696                                        usingBackTicks = false;
697                                }
698        
699                                if (beginPos != -1) {
700                                        int endPos = -1;
701        
702                                        if (usingBackTicks) {
703                                                endPos = line.indexOf(quoteChar, beginPos + 1);
704                                        } else {
705                                                endPos = line.indexOf("\"", beginPos + 1);
706                                        }
707        
708                                        if (endPos != -1) {
709                                                constraintName = line.substring(beginPos + 1, endPos);
710                                                line = line.substring(endPos + 1, line.length()).trim();
711                                        }
712                                }
713                        }
714        
715                        
716                        if (line.startsWith("FOREIGN KEY")) {
717                                if (line.endsWith(",")) {
718                                        line = line.substring(0, line.length() - 1);
719                                }
720        
721                                char quote = this.quotedId.charAt(0);
722                                
723                                int indexOfFK = line.indexOf("FOREIGN KEY");
724                                
725                                String localColumnName = null;
726                                String referencedCatalogName = this.quotedId + catalog + this.quotedId;
727                                String referencedTableName = null;
728                                String referencedColumnName = null;
729                                
730                                
731                                if (indexOfFK != -1) {
732                                        int afterFk = indexOfFK + "FOREIGN KEY".length();
733                                        
734                                        int indexOfRef = StringUtils.indexOfIgnoreCaseRespectQuotes(afterFk, line, "REFERENCES", quote, true);
735                                        
736                                        if (indexOfRef != -1) {
737                                                
738                                                int indexOfParenOpen = line.indexOf('(', afterFk);
739                                                int indexOfParenClose = StringUtils.indexOfIgnoreCaseRespectQuotes(indexOfParenOpen, line, ")", quote, true);
740                                                
741                                                if (indexOfParenOpen == -1 || indexOfParenClose == -1) {
742                                                        // throw SQLError.createSQLException();
743                                                }
744                                                
745                                                localColumnName = line.substring(indexOfParenOpen + 1, indexOfParenClose);
746                                                
747                                                int afterRef = indexOfRef + "REFERENCES".length();
748                                                
749                                                int referencedColumnBegin = StringUtils.indexOfIgnoreCaseRespectQuotes(afterRef, line, "(", quote, true);
750                                                
751                                                if (referencedColumnBegin != -1) {
752                                                        referencedTableName = line.substring(afterRef, referencedColumnBegin);
753        
754                                                        int referencedColumnEnd = StringUtils.indexOfIgnoreCaseRespectQuotes(referencedColumnBegin + 1, line, ")", quote, true);
755                                                        
756                                                        if (referencedColumnEnd != -1) {
757                                                                referencedColumnName = line.substring(referencedColumnBegin + 1, referencedColumnEnd);
758                                                        }
759                                                        
760                                                        int indexOfCatalogSep = StringUtils.indexOfIgnoreCaseRespectQuotes(0, referencedTableName, ".", quote, true);
761                                                        
762                                                        if (indexOfCatalogSep != -1) {
763                                                                referencedCatalogName = referencedTableName.substring(0, indexOfCatalogSep);
764                                                                referencedTableName = referencedTableName.substring(indexOfCatalogSep + 1);
765                                                        }
766                                                }
767                                        }
768                                }
769                                
770                                
771                                if (!firstTime) {
772                                        commentBuf.append("; ");
773                                } else {
774                                        firstTime = false;
775                                }
776        
777                                if (constraintName != null) {
778                                        commentBuf.append(constraintName);
779                                } else {
780                                        commentBuf.append("not_available");
781                                }
782        
783                                commentBuf.append("(");
784                                commentBuf.append(localColumnName);
785                                commentBuf.append(") REFER ");
786                                commentBuf.append(referencedCatalogName);
787                                commentBuf.append("/");
788                                commentBuf.append(referencedTableName);
789                                commentBuf.append("(");
790                                commentBuf.append(referencedColumnName);
791                                commentBuf.append(")");
792        
793                                int lastParenIndex = line.lastIndexOf(")");
794        
795                                if (lastParenIndex != (line.length() - 1)) {
796                                        String cascadeOptions = cascadeOptions = line
797                                                        .substring(lastParenIndex + 1);
798                                        commentBuf.append(" ");
799                                        commentBuf.append(cascadeOptions);
800                                }
801                        }
802                }
803        
804                row[2] = s2b(commentBuf.toString());
805                rows.add(row);
806        
807                return rows;
808        }
809 
810        /**
811         * Creates a result set similar enough to 'SHOW TABLE STATUS' to allow the
812         * same code to work on extracting the foreign key data
813         * 
814         * @param connToUse
815         *            the database connection to use
816         * @param metadata
817         *            the DatabaseMetaData instance calling this method
818         * @param catalog
819         *            the database name to extract foreign key info for
820         * @param tableName
821         *            the table to extract foreign key info for
822         * @return A result set that has the structure of 'show table status'
823         * @throws SQLException
824         *             if a database access error occurs.
825         */
826        public ResultSet extractForeignKeyFromCreateTable(String catalog,
827                        String tableName) throws SQLException {
828                ArrayList tableList = new ArrayList();
829                java.sql.ResultSet rs = null;
830                java.sql.Statement stmt = null;
831 
832                if (tableName != null) {
833                        tableList.add(tableName);
834                } else {
835                        try {
836                                rs = getTables(catalog, "", "%", new String[] { "TABLE" });
837 
838                                while (rs.next()) {
839                                        tableList.add(rs.getString("TABLE_NAME"));
840                                }
841                        } finally {
842                                if (rs != null) {
843                                        rs.close();
844                                }
845 
846                                rs = null;
847                        }
848                }
849 
850                ArrayList rows = new ArrayList();
851                Field[] fields = new Field[3];
852                fields[0] = new Field("", "Name", Types.CHAR, Integer.MAX_VALUE);
853                fields[1] = new Field("", "Type", Types.CHAR, 255);
854                fields[2] = new Field("", "Comment", Types.CHAR, Integer.MAX_VALUE);
855 
856                int numTables = tableList.size();
857                stmt = this.conn.getMetadataSafeStatement();
858 
859                String quoteChar = getIdentifierQuoteString();
860 
861                if (quoteChar == null) {
862                        quoteChar = "`";
863                }
864 
865                try {
866                        for (int i = 0; i < numTables; i++) {
867                                String tableToExtract = (String) tableList.get(i);
868 
869                                String query = new StringBuffer("SHOW CREATE TABLE ").append(
870                                                quoteChar).append(catalog).append(quoteChar)
871                                                .append(".").append(quoteChar).append(tableToExtract)
872                                                .append(quoteChar).toString();
873                                rs = stmt.executeQuery(query);
874 
875                                while (rs.next()) {
876                                        extractForeignKeyForTable(rows, rs, catalog);
877                                }
878                        }
879                } finally {
880                        if (rs != null) {
881                                rs.close();
882                        }
883 
884                        rs = null;
885 
886                        if (stmt != null) {
887                                stmt.close();
888                        }
889 
890                        stmt = null;
891                }
892 
893                return buildResultSet(fields, rows);
894        }
895 
896        /**
897         * @see DatabaseMetaData#getAttributes(String, String, String, String)
898         */
899        public java.sql.ResultSet getAttributes(String arg0, String arg1,
900                        String arg2, String arg3) throws SQLException {
901                Field[] fields = new Field[21];
902                fields[0] = new Field("", "TYPE_CAT", Types.CHAR, 32);
903                fields[1] = new Field("", "TYPE_SCHEM", Types.CHAR, 32);
904                fields[2] = new Field("", "TYPE_NAME", Types.CHAR, 32);
905                fields[3] = new Field("", "ATTR_NAME", Types.CHAR, 32);
906                fields[4] = new Field("", "DATA_TYPE", Types.SMALLINT, 32);
907                fields[5] = new Field("", "ATTR_TYPE_NAME", Types.CHAR, 32);
908                fields[6] = new Field("", "ATTR_SIZE", Types.INTEGER, 32);
909                fields[7] = new Field("", "DECIMAL_DIGITS", Types.INTEGER, 32);
910                fields[8] = new Field("", "NUM_PREC_RADIX", Types.INTEGER, 32);
911                fields[9] = new Field("", "NULLABLE ", Types.INTEGER, 32);
912                fields[10] = new Field("", "REMARKS", Types.CHAR, 32);
913                fields[11] = new Field("", "ATTR_DEF", Types.CHAR, 32);
914                fields[12] = new Field("", "SQL_DATA_TYPE", Types.INTEGER, 32);
915                fields[13] = new Field("", "SQL_DATETIME_SUB", Types.INTEGER, 32);
916                fields[14] = new Field("", "CHAR_OCTET_LENGTH", Types.INTEGER, 32);
917                fields[15] = new Field("", "ORDINAL_POSITION", Types.INTEGER, 32);
918                fields[16] = new Field("", "IS_NULLABLE", Types.CHAR, 32);
919                fields[17] = new Field("", "SCOPE_CATALOG", Types.CHAR, 32);
920                fields[18] = new Field("", "SCOPE_SCHEMA", Types.CHAR, 32);
921                fields[19] = new Field("", "SCOPE_TABLE", Types.CHAR, 32);
922                fields[20] = new Field("", "SOURCE_DATA_TYPE", Types.SMALLINT, 32);
923 
924                return buildResultSet(fields, new ArrayList());
925        }
926 
927        /**
928         * Get a description of a table's optimal set of columns that uniquely
929         * identifies a row. They are ordered by SCOPE.
930         * <P>
931         * Each column description has the following columns:
932         * <OL>
933         * <li> <B>SCOPE</B> short => actual scope of result
934         * <UL>
935         * <li> bestRowTemporary - very temporary, while using row </li>
936         * <li> bestRowTransaction - valid for remainder of current transaction
937         * </li>
938         * <li> bestRowSession - valid for remainder of current session </li>
939         * </ul>
940         * </li>
941         * <li> <B>COLUMN_NAME</B> String => column name </li>
942         * <li> <B>DATA_TYPE</B> short => SQL data type from java.sql.Types </li>
943         * <li> <B>TYPE_NAME</B> String => Data source dependent type name </li>
944         * <li> <B>COLUMN_SIZE</B> int => precision </li>
945         * <li> <B>BUFFER_LENGTH</B> int => not used </li>
946         * <li> <B>DECIMAL_DIGITS</B> short => scale </li>
947         * <li> <B>PSEUDO_COLUMN</B> short => is this a pseudo column like an
948         * Oracle ROWID
949         * <UL>
950         * <li> bestRowUnknown - may or may not be pseudo column </li>
951         * <li> bestRowNotPseudo - is NOT a pseudo column </li>
952         * <li> bestRowPseudo - is a pseudo column </li>
953         * </ul>
954         * </li>
955         * </ol>
956         * </p>
957         * 
958         * @param catalog
959         *            a catalog name; "" retrieves those without a catalog
960         * @param schema
961         *            a schema name; "" retrieves those without a schema
962         * @param table
963         *            a table name
964         * @param scope
965         *            the scope of interest; use same values as SCOPE
966         * @param nullable
967         *            include columns that are nullable?
968         * @return ResultSet each row is a column description
969         * @throws SQLException
970         *             DOCUMENT ME!
971         */
972        public java.sql.ResultSet getBestRowIdentifier(String catalog,
973                        String schema, final String table, int scope, boolean nullable)
974                        throws SQLException {
975                if (table == null) {
976                        throw SQLError.createSQLException("Table not specified.",
977                                        SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
978                }
979 
980                Field[] fields = new Field[8];
981                fields[0] = new Field("", "SCOPE", Types.SMALLINT, 5);
982                fields[1] = new Field("", "COLUMN_NAME", Types.CHAR, 32);
983                fields[2] = new Field("", "DATA_TYPE", Types.SMALLINT, 32);
984                fields[3] = new Field("", "TYPE_NAME", Types.CHAR, 32);
985                fields[4] = new Field("", "COLUMN_SIZE", Types.INTEGER, 10);
986                fields[5] = new Field("", "BUFFER_LENGTH", Types.INTEGER, 10);
987                fields[6] = new Field("", "DECIMAL_DIGITS", Types.INTEGER, 10);
988                fields[7] = new Field("", "PSEUDO_COLUMN", Types.SMALLINT, 5);
989 
990                final ArrayList rows = new ArrayList();
991                final Statement stmt = this.conn.getMetadataSafeStatement();
992 
993                try {
994 
995                        new IterateBlock(getCatalogIterator(catalog)) {
996                                void forEach(Object catalogStr) throws SQLException {
997                                        ResultSet results = null;
998 
999                                        try {
1000                                                StringBuffer queryBuf = new StringBuffer(
1001                                                                "SHOW COLUMNS FROM ");
1002                                                queryBuf.append(quotedId);
1003                                                queryBuf.append(table);
1004                                                queryBuf.append(quotedId);
1005                                                queryBuf.append(" FROM ");
1006                                                queryBuf.append(quotedId);
1007                                                queryBuf.append(catalogStr.toString());
1008                                                queryBuf.append(quotedId);
1009 
1010                                                results = stmt.executeQuery(queryBuf.toString());
1011 
1012                                                while (results.next()) {
1013                                                        String keyType = results.getString("Key");
1014 
1015                                                        if (keyType != null) {
1016                                                                if (StringUtils.startsWithIgnoreCase(keyType,
1017                                                                                "PRI")) {
1018                                                                        byte[][] rowVal = new byte[8][];
1019                                                                        rowVal[0] = Integer
1020                                                                                        .toString(
1021                                                                                                        java.sql.DatabaseMetaData.bestRowSession)
1022                                                                                        .getBytes();
1023                                                                        rowVal[1] = results.getBytes("Field");
1024 
1025                                                                        String type = results.getString("Type");
1026                                                                        int size = MysqlIO.getMaxBuf();
1027                                                                        int decimals = 0;
1028 
1029                                                                        /*
1030                                                                         * Parse the Type column from MySQL
1031                                                                         */
1032                                                                        if (type.indexOf("enum") != -1) {
1033                                                                                String temp = type.substring(type
1034                                                                                                .indexOf("("), type
1035                                                                                                .indexOf(")"));
1036                                                                                java.util.StringTokenizer tokenizer = new java.util.StringTokenizer(
1037                                                                                                temp, ",");
1038                                                                                int maxLength = 0;
1039 
1040                                                                                while (tokenizer.hasMoreTokens()) {
1041                                                                                        maxLength = Math.max(maxLength,
1042                                                                                                        (tokenizer.nextToken()
1043                                                                                                                        .length() - 2));
1044                                                                                }
1045 
1046                                                                                size = maxLength;
1047                                                                                decimals = 0;
1048                                                                                type = "enum";
1049                                                                        } else if (type.indexOf("(") != -1) {
1050                                                                                if (type.indexOf(",") != -1) {
1051                                                                                        size = Integer.parseInt(type
1052                                                                                                        .substring(type
1053                                                                                                                        .indexOf("(") + 1,
1054                                                                                                                        type.indexOf(",")));
1055                                                                                        decimals = Integer.parseInt(type
1056                                                                                                        .substring(type
1057                                                                                                                        .indexOf(",") + 1,
1058                                                                                                                        type.indexOf(")")));
1059                                                                                } else {
1060                                                                                        size = Integer.parseInt(type
1061                                                                                                        .substring(type
1062                                                                                                                        .indexOf("(") + 1,
1063                                                                                                                        type.indexOf(")")));
1064                                                                                }
1065 
1066                                                                                type = type.substring(0, type
1067                                                                                                .indexOf("("));
1068                                                                        }
1069 
1070                                                                        rowVal[2] = s2b(String.valueOf(MysqlDefs
1071                                                                                        .mysqlToJavaType(type)));
1072                                                                        rowVal[3] = s2b(type);
1073                                                                        rowVal[4] = Integer.toString(
1074                                                                                        size + decimals).getBytes();
1075                                                                        rowVal[5] = Integer.toString(
1076                                                                                        size + decimals).getBytes();
1077                                                                        rowVal[6] = Integer.toString(decimals)
1078                                                                                        .getBytes();
1079                                                                        rowVal[7] = Integer
1080                                                                                        .toString(
1081                                                                                                        java.sql.DatabaseMetaData.bestRowNotPseudo)
1082                                                                                        .getBytes();
1083 
1084                                                                        rows.add(rowVal);
1085                                                                }
1086                                                        }
1087                                                }
1088 
1089                                        } finally {
1090                                                if (results != null) {
1091                                                        try {
1092                                                                results.close();
1093                                                        } catch (Exception ex) {
1094                                                                ;
1095                                                        }
1096 
1097                                                        results = null;
1098                                                }
1099                                        }
1100                                }
1101                        }.doForAll();
1102                } finally {
1103                        if (stmt != null) {
1104                                stmt.close();
1105                        }
1106                }
1107 
1108                java.sql.ResultSet results = buildResultSet(fields, rows);
1109 
1110                return results;
1111 
1112        }
1113 
1114        /*
1115         * * Each row in the ResultSet is a parameter desription or column
1116         * description with the following fields: <OL> <li> <B>PROCEDURE_CAT</B>
1117         * String => procedure catalog (may be null) </li> <li> <B>PROCEDURE_SCHEM</B>
1118         * String => procedure schema (may be null) </li> <li> <B>PROCEDURE_NAME</B>
1119         * String => procedure name </li> <li> <B>COLUMN_NAME</B> String =>
1120         * column/parameter name </li> <li> <B>COLUMN_TYPE</B> Short => kind of
1121         * column/parameter: <UL> <li> procedureColumnUnknown - nobody knows </li>
1122         * <li> procedureColumnIn - IN parameter </li> <li> procedureColumnInOut -
1123         * INOUT parameter </li> <li> procedureColumnOut - OUT parameter </li> <li>
1124         * procedureColumnReturn - procedure return value </li> <li>
1125         * procedureColumnResult - result column in ResultSet </li> </ul> </li> <li>
1126         * <B>DATA_TYPE</B> short => SQL type from java.sql.Types </li> <li>
1127         * <B>TYPE_NAME</B> String => SQL type name </li> <li> <B>PRECISION</B>
1128         * int => precision </li> <li> <B>LENGTH</B> int => length in bytes of data
1129         * </li> <li> <B>SCALE</B> short => scale </li> <li> <B>RADIX</B> short =>
1130         * radix </li> <li> <B>NULLABLE</B> short => can it contain NULL? <UL> <li>
1131         * procedureNoNulls - does not allow NULL values </li> <li>
1132         * procedureNullable - allows NULL values </li> <li>
1133         * procedureNullableUnknown - nullability unknown </li> </ul> </li> <li>
1134         * <B>REMARKS</B> String => comment describing parameter/column </li> </ol>
1135         * </p> <P> <B>Note:</B> Some databases may not return the column
1136         * descriptions for a procedure. Additional columns beyond REMARKS can be
1137         * defined by the database. </p> @param catalog a catalog name; "" retrieves
1138         * those without a catalog @param schemaPattern a schema name pattern; ""
1139         * retrieves those without a schema @param procedureNamePattern a procedure
1140         * name pattern @param columnNamePattern a column name pattern @return
1141         * ResultSet each row is a stored procedure parameter or column description
1142         * @throws SQLException if a database access error occurs
1143         * 
1144         * @see #getSearchStringEscape
1145         */
1146        private void getCallStmtParameterTypes(String catalog, String procName,
1147                        String parameterNamePattern, List resultRows) throws SQLException {
1148                java.sql.Statement paramRetrievalStmt = null;
1149                java.sql.ResultSet paramRetrievalRs = null;
1150 
1151                if (parameterNamePattern == null) {
1152                        if (this.conn.getNullNamePatternMatchesAll()) {
1153                                parameterNamePattern = "%";
1154                        } else {
1155                                throw SQLError.createSQLException(
1156                                                "Parameter/Column name pattern can not be NULL or empty.",
1157                                                SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
1158                        }
1159                }
1160 
1161                byte[] procNameAsBytes = null;
1162 
1163                try {
1164                        procNameAsBytes = procName.getBytes("UTF-8");
1165                } catch (UnsupportedEncodingException ueEx) {
1166                        procNameAsBytes = s2b(procName);
1167 
1168                        // Set all fields to connection encoding
1169                }
1170 
1171                String quoteChar = getIdentifierQuoteString();
1172 
1173                String storageDefnDelims = "(" + quoteChar;
1174                String storageDefnClosures = ")" + quoteChar;
1175 
1176                // First try 'select from mysql.proc, as this is easier to parse...
1177                String parameterDef = null;
1178                
1179                PreparedStatement paramRetrievalPreparedStatement = null;
1180                
1181                try {
1182                        paramRetrievalStmt = this.conn.getMetadataSafeStatement();
1183                        
1184                        if (this.conn.lowerCaseTableNames() && catalog != null 
1185                                        && catalog.length() != 0) {
1186                                // Workaround for bug in server wrt. to 
1187                                // SHOW CREATE PROCEDURE not respecting
1188                                // lower-case table names
1189                                
1190                                String oldCatalog = this.conn.getCatalog();
1191                                ResultSet rs = null;
1192                                
1193                                try {
1194                                        this.conn.setCatalog(catalog);
1195                                        rs = paramRetrievalStmt.executeQuery("SELECT DATABASE()");
1196                                        rs.next();
1197                                        
1198                                        catalog = rs.getString(1);
1199                                        
1200                                } finally {
1201                                        
1202                                        this.conn.setCatalog(oldCatalog);
1203                                        
1204                                        if (rs != null) {
1205                                                rs.close();
1206                                        }
1207                                }
1208                        }
1209                        
1210                        if (paramRetrievalStmt.getMaxRows() != 0) {
1211                                paramRetrievalStmt.setMaxRows(0);
1212                        }
1213 
1214                        int dotIndex = -1;
1215 
1216                        if (!" ".equals(quoteChar)) {
1217                                dotIndex = StringUtils.indexOfIgnoreCaseRespectQuotes(0,
1218                                                procName, ".", quoteChar.charAt(0), !this.conn
1219                                                                .isNoBackslashEscapesSet());
1220                        } else {
1221                                dotIndex = procName.indexOf(".");
1222                        }
1223 
1224                        String dbName = null;
1225 
1226                        if (dotIndex != -1 && (dotIndex + 1) < procName.length()) {
1227                                dbName = procName.substring(0, dotIndex);
1228                                procName = procName.substring(dotIndex + 1);
1229                        } else {
1230                                dbName = catalog;
1231                        }
1232 
1233                        StringBuffer procNameBuf = new StringBuffer();
1234 
1235                        if (dbName != null) {
1236                                if (!" ".equals(quoteChar) && !dbName.startsWith(quoteChar)) {
1237                                        procNameBuf.append(quoteChar);
1238                                }
1239 
1240                                procNameBuf.append(dbName);
1241 
1242                                if (!" ".equals(quoteChar) && !dbName.startsWith(quoteChar)) {
1243                                        procNameBuf.append(quoteChar);
1244                                }
1245 
1246                                procNameBuf.append(".");
1247                        }
1248 
1249                        boolean procNameIsNotQuoted = !procName.startsWith(quoteChar);
1250 
1251                        if (!" ".equals(quoteChar) && procNameIsNotQuoted) {
1252                                procNameBuf.append(quoteChar);
1253                        }
1254 
1255                        procNameBuf.append(procName);
1256 
1257                        if (!" ".equals(quoteChar) && procNameIsNotQuoted) {
1258                                procNameBuf.append(quoteChar);
1259                        }
1260 
1261                        boolean parsingFunction = false;
1262 
1263                        try {
1264                                paramRetrievalRs = paramRetrievalStmt
1265                                                .executeQuery("SHOW CREATE PROCEDURE "
1266                                                                + procNameBuf.toString());
1267                                parsingFunction = false;
1268                        } catch (SQLException sqlEx) {
1269                                paramRetrievalRs = paramRetrievalStmt
1270                                                .executeQuery("SHOW CREATE FUNCTION "
1271                                                                + procNameBuf.toString());
1272                                parsingFunction = true;
1273                        }
1274 
1275                        if (paramRetrievalRs.next()) {
1276                                String procedureDef = parsingFunction ? paramRetrievalRs
1277                                                .getString("Create Function") : paramRetrievalRs
1278                                                .getString("Create Procedure");
1279 
1280                                int openParenIndex = StringUtils
1281                                                .indexOfIgnoreCaseRespectQuotes(0, procedureDef, "(",
1282                                                                quoteChar.charAt(0), !this.conn
1283                                                                                .isNoBackslashEscapesSet());
1284 
1285                                String beforeBegin = null;
1286 
1287                                // Try and fudge this with the 'begin' statement
1288                                int beginIndex = 0;
1289 
1290                                if (!parsingFunction) {
1291                                        beginIndex = StringUtils.indexOfIgnoreCaseRespectQuotes(0,
1292                                                        procedureDef, "\nbegin", quoteChar.charAt(0),
1293                                                        !this.conn.isNoBackslashEscapesSet());
1294                                } else {
1295                                        // Grab the return column first, since it needs
1296                                        // to go first in the output result set
1297                                        int returnsIndex = StringUtils
1298                                                        .indexOfIgnoreCaseRespectQuotes(0, procedureDef,
1299                                                                        " RETURNS ", quoteChar.charAt(0),
1300                                                                        !this.conn.isNoBackslashEscapesSet());
1301 
1302                                        beginIndex = StringUtils.indexOfIgnoreCaseRespectQuotes(
1303                                                        returnsIndex, procedureDef, "\nbegin", quoteChar
1304                                                                        .charAt(0), !this.conn
1305                                                                        .isNoBackslashEscapesSet());
1306 
1307                                        if (beginIndex == -1) {
1308                                                beginIndex = StringUtils
1309                                                                .indexOfIgnoreCaseRespectQuotes(0,
1310                                                                                procedureDef, "\n",
1311                                                                                quoteChar.charAt(0), !this.conn
1312                                                                                                .isNoBackslashEscapesSet());
1313                                        }
1314 
1315                                        // Okay, give up...
1316 
1317                                        if (beginIndex == -1) {
1318                                                throw SQLError.createSQLException(
1319                                                                "Driver requires declaration of procedure to either contain a '\\nbegin' or '\\n' to follow argument declaration, or SELECT privilege on mysql.proc to parse column types.",
1320                                                                SQLError.SQL_STATE_GENERAL_ERROR);
1321                                        }
1322 
1323                                        String returnsDefn = procedureDef.substring(returnsIndex
1324                                                        + "RETURNS ".length(), beginIndex);
1325                                        TypeDescriptor returnDescriptor = new TypeDescriptor(
1326                                                        returnsDefn, null);
1327 
1328                                        resultRows.add(convertTypeDescriptorToProcedureRow(
1329                                                        procNameAsBytes, "", false, false, true,
1330                                                        returnDescriptor));
1331 
1332                                        beginIndex = returnsIndex; // further processing needs to
1333                                        // look before "RETURNS" token
1334                                }
1335 
1336                                // Bah, we _really_ need information schema here
1337 
1338                                if (beginIndex != -1) {
1339                                        beforeBegin = procedureDef.substring(0, beginIndex);
1340                                } else {
1341                                        beginIndex = StringUtils.indexOfIgnoreCaseRespectQuotes(0,
1342                                                        procedureDef, "\n", quoteChar.charAt(0), !this.conn
1343                                                                        .isNoBackslashEscapesSet());
1344 
1345                                        if (beginIndex != -1) {
1346                                                beforeBegin = procedureDef.substring(0, beginIndex);
1347                                        } else {
1348                                                throw SQLError.createSQLException(
1349                                                                "Driver requires declaration of procedure to either contain a '\\nbegin' or '\\n' to follow argument declaration, or SELECT privilege on mysql.proc to parse column types.",
1350                                                                SQLError.SQL_STATE_GENERAL_ERROR);
1351                                        }
1352 
1353                                }
1354 
1355                                int endParenIndex = beforeBegin.lastIndexOf(')');
1356 
1357                                if ((openParenIndex == -1) || (endParenIndex == -1)) {
1358                                        // parse error?
1359                                        throw SQLError.createSQLException(
1360                                                        "Internal error when parsing callable statement metadata",
1361                                                        SQLError.SQL_STATE_GENERAL_ERROR);
1362                                }
1363 
1364                                parameterDef = procedureDef.substring(openParenIndex + 1,
1365                                                endParenIndex);
1366                        }
1367                } finally {
1368                        SQLException sqlExRethrow = null;
1369 
1370                        if (paramRetrievalRs != null) {
1371                                try {
1372                                        paramRetrievalRs.close();
1373                                } catch (SQLException sqlEx) {
1374                                        sqlExRethrow = sqlEx;
1375                                }
1376 
1377                                paramRetrievalRs = null;
1378                        }
1379 
1380                        if (paramRetrievalPreparedStatement != null) {
1381                                try {
1382                                        paramRetrievalPreparedStatement.close();
1383                                } catch (SQLException sqlEx) {
1384                                        sqlExRethrow = sqlEx;
1385                                }
1386 
1387                                paramRetrievalPreparedStatement = null;
1388                        }
1389 
1390                        if (paramRetrievalStmt != null) {
1391                                try {
1392                                        paramRetrievalStmt.close();
1393                                } catch (SQLException sqlEx) {
1394                                        sqlExRethrow = sqlEx;
1395                                }
1396 
1397                                paramRetrievalStmt = null;
1398                        }
1399 
1400                        if (sqlExRethrow != null) {
1401                                throw sqlExRethrow;
1402                        }
1403                }
1404 
1405                if (parameterDef != null) {
1406                        List parseList = StringUtils.split(parameterDef, ",",
1407                                        storageDefnDelims, storageDefnClosures, true);
1408 
1409                        int parseListLen = parseList.size();
1410 
1411                        for (int i = 0; i < parseListLen; i++) {
1412                                String declaration = (String) parseList.get(i);
1413 
1414                                StringTokenizer declarationTok = new StringTokenizer(
1415                                                declaration, " \t");
1416 
1417                                String paramName = null;
1418                                boolean isOutParam = false;
1419                                boolean isInParam = false;
1420 
1421                                if (declarationTok.hasMoreTokens()) {
1422                                        String possibleParamName = declarationTok.nextToken();
1423 
1424                                        if (possibleParamName.equalsIgnoreCase("OUT")) {
1425                                                isOutParam = true;
1426 
1427                                                if (declarationTok.hasMoreTokens()) {
1428                                                        paramName = declarationTok.nextToken();
1429                                                } else {
1430                                                        throw SQLError.createSQLException(
1431                                                                        "Internal error when parsing callable statement metadata (missing parameter name)",
1432                                                                        SQLError.SQL_STATE_GENERAL_ERROR);
1433                                                }
1434                                        } else if (possibleParamName.equalsIgnoreCase("INOUT")) {
1435                                                isOutParam = true;
1436                                                isInParam = true;
1437 
1438                                                if (declarationTok.hasMoreTokens()) {
1439                                                        paramName = declarationTok.nextToken();
1440                                                } else {
1441                                                        throw SQLError.createSQLException(
1442                                                                        "Internal error when parsing callable statement metadata (missing parameter name)",
1443                                                                        SQLError.SQL_STATE_GENERAL_ERROR);
1444                                                }
1445                                        } else if (possibleParamName.equalsIgnoreCase("IN")) {
1446                                                isOutParam = false;
1447                                                isInParam = true;
1448 
1449                                                if (declarationTok.hasMoreTokens()) {
1450                                                        paramName = declarationTok.nextToken();
1451                                                } else {
1452                                                        throw SQLError.createSQLException(
1453                                                                        "Internal error when parsing callable statement metadata (missing parameter name)",
1454                                                                        SQLError.SQL_STATE_GENERAL_ERROR);
1455                                                }
1456                                        } else {
1457                                                isOutParam = false;
1458                                                isInParam = true;
1459 
1460                                                paramName = possibleParamName;
1461                                        }
1462 
1463                                        TypeDescriptor typeDesc = null;
1464 
1465                                        if (declarationTok.hasMoreTokens()) {
1466                                                StringBuffer typeInfoBuf = new StringBuffer(
1467                                                                declarationTok.nextToken());
1468 
1469                                                while (declarationTok.hasMoreTokens()) {
1470                                                        typeInfoBuf.append(" ");
1471                                                        typeInfoBuf.append(declarationTok.nextToken());
1472                                                }
1473 
1474                                                String typeInfo = typeInfoBuf.toString();
1475 
1476                                                typeDesc = new TypeDescriptor(typeInfo, null);
1477                                        } else {
1478                                                throw SQLError.createSQLException(
1479                                                                "Internal error when parsing callable statement metadata (missing parameter type)",
1480                                                                SQLError.SQL_STATE_GENERAL_ERROR);
1481                                        }
1482 
1483                                        int wildCompareRes = StringUtils.wildCompare(paramName,
1484                                                        parameterNamePattern);
1485 
1486                                        if (wildCompareRes != StringUtils.WILD_COMPARE_NO_MATCH) {
1487                                                byte[][] row = convertTypeDescriptorToProcedureRow(
1488                                                                procNameAsBytes, paramName, isOutParam,
1489                                                                isInParam, false, typeDesc);
1490 
1491                                                resultRows.add(row);
1492                                        }
1493                                } else {
1494                                        throw SQLError.createSQLException(
1495                                                        "Internal error when parsing callable statement metadata (unknown output from 'SHOW CREATE PROCEDURE')",
1496                                                        SQLError.SQL_STATE_GENERAL_ERROR);
1497                                }
1498                        }
1499                } else {
1500                        // Is this an error? JDBC spec doesn't make it clear if stored
1501                        // procedure doesn't
1502                        // exist, is it an error....
1503                }
1504        }
1505 
1506        /**
1507         * Parses the cascade option string and returns the DBMD constant that
1508         * represents it (for deletes)
1509         * 
1510         * @param cascadeOptions
1511         *            the comment from 'SHOW TABLE STATUS'
1512         * @return the DBMD constant that represents the cascade option
1513         */
1514        private int getCascadeDeleteOption(String cascadeOptions) {
1515                int onDeletePos = cascadeOptions.indexOf("ON DELETE");
1516 
1517                if (onDeletePos != -1) {
1518                        String deleteOptions = cascadeOptions.substring(onDeletePos,
1519                                        cascadeOptions.length());
1520 
1521                        if (deleteOptions.startsWith("ON DELETE CASCADE")) {
1522                                return java.sql.DatabaseMetaData.importedKeyCascade;
1523                        } else if (deleteOptions.startsWith("ON DELETE SET NULL")) {
1524                                return java.sql.DatabaseMetaData.importedKeySetNull;
1525                        } else if (deleteOptions.startsWith("ON DELETE RESTRICT")) {
1526                                return java.sql.DatabaseMetaData.importedKeyRestrict;
1527                        } else if (deleteOptions.startsWith("ON DELETE NO ACTION")) {
1528                                return java.sql.DatabaseMetaData.importedKeyNoAction;
1529                        }
1530                }
1531 
1532                return java.sql.DatabaseMetaData.importedKeyNoAction;
1533        }
1534 
1535        /**
1536         * Parses the cascade option string and returns the DBMD constant that
1537         * represents it (for Updates)
1538         * 
1539         * @param cascadeOptions
1540         *            the comment from 'SHOW TABLE STATUS'
1541         * @return the DBMD constant that represents the cascade option
1542         */
1543        private int getCascadeUpdateOption(String cascadeOptions) {
1544                int onUpdatePos = cascadeOptions.indexOf("ON UPDATE");
1545 
1546                if (onUpdatePos != -1) {
1547                        String updateOptions = cascadeOptions.substring(onUpdatePos,
1548                                        cascadeOptions.length());
1549 
1550                        if (updateOptions.startsWith("ON UPDATE CASCADE")) {
1551                                return java.sql.DatabaseMetaData.importedKeyCascade;
1552                        } else if (updateOptions.startsWith("ON UPDATE SET NULL")) {
1553                                return java.sql.DatabaseMetaData.importedKeySetNull;
1554                        } else if (updateOptions.startsWith("ON UPDATE RESTRICT")) {
1555                                return java.sql.DatabaseMetaData.importedKeyRestrict;
1556                        } else if (updateOptions.startsWith("ON UPDATE NO ACTION")) {
1557                                return java.sql.DatabaseMetaData.importedKeyNoAction;
1558                        }
1559                }
1560 
1561                return java.sql.DatabaseMetaData.importedKeyNoAction;
1562        }
1563 
1564        protected IteratorWithCleanup getCatalogIterator(String catalogSpec)
1565                        throws SQLException {
1566                IteratorWithCleanup allCatalogsIter;
1567                if (catalogSpec != null) {
1568                        if (!catalogSpec.equals("")) {
1569                                allCatalogsIter = new SingleStringIterator(catalogSpec);
1570                        } else {
1571                                // legacy mode of operation
1572                                allCatalogsIter = new SingleStringIterator(this.database);
1573                        }
1574                } else if (this.conn.getNullCatalogMeansCurrent()) {
1575                        allCatalogsIter = new SingleStringIterator(this.database);
1576                } else {
1577                        allCatalogsIter = new ResultSetIterator(getCatalogs(), 1);
1578                }
1579 
1580                return allCatalogsIter;
1581        }
1582 
1583        /**
1584         * Get the catalog names available in this database. The results are ordered
1585         * by catalog name.
1586         * <P>
1587         * The catalog column is:
1588         * <OL>
1589         * <li> <B>TABLE_CAT</B> String => catalog name </li>
1590         * </ol>
1591         * </p>
1592         * 
1593         * @return ResultSet each row has a single String column that is a catalog
1594         *         name
1595         * @throws SQLException
1596         *             DOCUMENT ME!
1597         */
1598        public java.sql.ResultSet getCatalogs() throws SQLException {
1599                java.sql.ResultSet results = null;
1600                java.sql.Statement stmt = null;
1601 
1602                try {
1603                        stmt = this.conn.createStatement();
1604                        stmt.setEscapeProcessing(false);
1605                        results = stmt.executeQuery("SHOW DATABASES");
1606 
1607                        java.sql.ResultSetMetaData resultsMD = results.getMetaData();
1608                        Field[] fields = new Field[1];
1609                        fields[0] = new Field("", "TABLE_CAT", Types.VARCHAR, resultsMD
1610                                        .getColumnDisplaySize(1));
1611 
1612                        ArrayList tuples = new ArrayList();
1613 
1614                        while (results.next()) {
1615                                byte[][] rowVal = new byte[1][];
1616                                rowVal[0] = results.getBytes(1);
1617                                tuples.add(rowVal);
1618                        }
1619 
1620                        return buildResultSet(fields, tuples);
1621                } finally {
1622                        if (results != null) {
1623                                try {
1624                                        results.close();
1625                                } catch (SQLException sqlEx) {
1626                                        AssertionFailedException.shouldNotHappen(sqlEx);
1627                                }
1628 
1629                                results = null;
1630                        }
1631 
1632                        if (stmt != null) {
1633                                try {
1634                                        stmt.close();
1635                                } catch (SQLException sqlEx) {
1636                                        AssertionFailedException.shouldNotHappen(sqlEx);
1637                                }
1638 
1639                                stmt = null;
1640                        }
1641                }
1642        }
1643 
1644        /**
1645         * What's the separator between catalog and table name?
1646         * 
1647         * @return the separator string
1648         * @throws SQLException
1649         *             DOCUMENT ME!
1650         */
1651        public String getCatalogSeparator() throws SQLException {
1652                return ".";
1653        }
1654 
1655        // ----------------------------------------------------------------------
1656        // The following group of methods exposes various limitations
1657        // based on the target database with the current driver.
1658        // Unless otherwise specified, a result of zero means there is no
1659        // limit, or the limit is not known.
1660 
1661        /**
1662         * What's the database vendor's preferred term for "catalog"?
1663         * 
1664         * @return the vendor term
1665         * @throws SQLException
1666         *             DOCUMENT ME!
1667         */
1668        public String getCatalogTerm() throws SQLException {
1669                return "database";
1670        }
1671 
1672        /**
1673         * Get a description of the access rights for a table's columns.
1674         * <P>
1675         * Only privileges matching the column name criteria are returned. They are
1676         * ordered by COLUMN_NAME and PRIVILEGE.
1677         * </p>
1678         * <P>
1679         * Each privilige description has the following columns:
1680         * <OL>
1681         * <li> <B>TABLE_CAT</B> String => table catalog (may be null) </li>
1682         * <li> <B>TABLE_SCHEM</B> String => table schema (may be null) </li>
1683         * <li> <B>TABLE_NAME</B> String => table name </li>
1684         * <li> <B>COLUMN_NAME</B> String => column name </li>
1685         * <li> <B>GRANTOR</B> => grantor of access (may be null) </li>
1686         * <li> <B>GRANTEE</B> String => grantee of access </li>
1687         * <li> <B>PRIVILEGE</B> String => name of access (SELECT, INSERT, UPDATE,
1688         * REFRENCES, ...) </li>
1689         * <li> <B>IS_GRANTABLE</B> String => "YES" if grantee is permitted to
1690         * grant to others; "NO" if not; null if unknown </li>
1691         * </ol>
1692         * </p>
1693         * 
1694         * @param catalog
1695         *            a catalog name; "" retrieves those without a catalog
1696         * @param schema
1697         *            a schema name; "" retrieves those without a schema
1698         * @param table
1699         *            a table name
1700         * @param columnNamePattern
1701         *            a column name pattern
1702         * @return ResultSet each row is a column privilege description
1703         * @throws SQLException
1704         *             if a database access error occurs
1705         * @see #getSearchStringEscape
1706         */
1707        public java.sql.ResultSet getColumnPrivileges(String catalog,
1708                        String schema, String table, String columnNamePattern)
1709                        throws SQLException {
1710                Field[] fields = new Field[8];
1711                fields[0] = new Field("", "TABLE_CAT", Types.CHAR, 64);
1712                fields[1] = new Field("", "TABLE_SCHEM", Types.CHAR, 1);
1713                fields[2] = new Field("", "TABLE_NAME", Types.CHAR, 64);
1714                fields[3] = new Field("", "COLUMN_NAME", Types.CHAR, 64);
1715                fields[4] = new Field("", "GRANTOR", Types.CHAR, 77);
1716                fields[5] = new Field("", "GRANTEE", Types.CHAR, 77);
1717                fields[6] = new Field("", "PRIVILEGE", Types.CHAR, 64);
1718                fields[7] = new Field("", "IS_GRANTABLE", Types.CHAR, 3);
1719 
1720                StringBuffer grantQuery = new StringBuffer(
1721                                "SELECT c.host, c.db, t.grantor, c.user, "
1722                                                + "c.table_name, c.column_name, c.column_priv "
1723                                                + "from mysql.columns_priv c, mysql.tables_priv t "
1724                                                + "where c.host = t.host and c.db = t.db and "
1725                                                + "c.table_name = t.table_name ");
1726 
1727                if ((catalog != null) && (catalog.length() != 0)) {
1728                        grantQuery.append(" AND c.db='");
1729                        grantQuery.append(catalog);
1730                        grantQuery.append("' ");
1731                        ;
1732                }
1733 
1734                grantQuery.append(" AND c.table_name ='");
1735                grantQuery.append(table);
1736                grantQuery.append("' AND c.column_name like '");
1737                grantQuery.append(columnNamePattern);
1738                grantQuery.append("'");
1739 
1740                Statement stmt = null;
1741                ResultSet results = null;
1742                ArrayList grantRows = new ArrayList();
1743 
1744                try {
1745                        stmt = this.conn.createStatement();
1746                        stmt.setEscapeProcessing(false);
1747                        results = stmt.executeQuery(grantQuery.toString());
1748 
1749                        while (results.next()) {
1750                                String host = results.getString(1);
1751                                String db = results.getString(2);
1752                                String grantor = results.getString(3);
1753                                String user = results.getString(4);
1754 
1755                                if ((user == null) || (user.length() == 0)) {
1756                                        user = "%";
1757                                }
1758 
1759                                StringBuffer fullUser = new StringBuffer(user);
1760 
1761                                if ((host != null) && this.conn.getUseHostsInPrivileges()) {
1762                                        fullUser.append("@");
1763                                        fullUser.append(host);
1764                                }
1765 
1766                                String columnName = results.getString(6);
1767                                String allPrivileges = results.getString(7);
1768 
1769                                if (allPrivileges != null) {
1770                                        allPrivileges = allPrivileges.toUpperCase(Locale.ENGLISH);
1771 
1772                                        StringTokenizer st = new StringTokenizer(allPrivileges, ",");
1773 
1774                                        while (st.hasMoreTokens()) {
1775                                                String privilege = st.nextToken().trim();
1776                                                byte[][] tuple = new byte[8][];
1777                                                tuple[0] = s2b(db);
1778                                                tuple[1] = null;
1779                                                tuple[2] = s2b(table);
1780                                                tuple[3] = s2b(columnName);
1781 
1782                                                if (grantor != null) {
1783                                                        tuple[4] = s2b(grantor);
1784                                                } else {
1785                                                        tuple[4] = null;
1786                                                }
1787 
1788                                                tuple[5] = s2b(fullUser.toString());
1789                                                tuple[6] = s2b(privilege);
1790                                                tuple[7] = null;
1791                                                grantRows.add(tuple);
1792                                        }
1793                                }
1794                        }
1795                } finally {
1796                        if (results != null) {
1797                                try {
1798                                        results.close();
1799                                } catch (Exception ex) {
1800                                        ;
1801                                }
1802 
1803                                results = null;
1804                        }
1805 
1806                        if (stmt != null) {
1807                                try {
1808                                        stmt.close();
1809                                } catch (Exception ex) {
1810                                        ;
1811                                }
1812 
1813                                stmt = null;
1814                        }
1815                }
1816 
1817                return buildResultSet(fields, grantRows);
1818        }
1819 
1820        /**
1821         * Get a description of table columns available in a catalog.
1822         * <P>
1823         * Only column descriptions matching the catalog, schema, table and column
1824         * name criteria are returned. They are ordered by TABLE_SCHEM, TABLE_NAME
1825         * and ORDINAL_POSITION.
1826         * </p>
1827         * <P>
1828         * Each column description has the following columns:
1829         * <OL>
1830         * <li> <B>TABLE_CAT</B> String => table catalog (may be null) </li>
1831         * <li> <B>TABLE_SCHEM</B> String => table schema (may be null) </li>
1832         * <li> <B>TABLE_NAME</B> String => table name </li>
1833         * <li> <B>COLUMN_NAME</B> String => column name </li>
1834         * <li> <B>DATA_TYPE</B> short => SQL type from java.sql.Types </li>
1835         * <li> <B>TYPE_NAME</B> String => Data source dependent type name </li>
1836         * <li> <B>COLUMN_SIZE</B> int => column size. For char or date types this
1837         * is the maximum number of characters, for numeric or decimal types this is
1838         * precision. </li>
1839         * <li> <B>BUFFER_LENGTH</B> is not used. </li>
1840         * <li> <B>DECIMAL_DIGITS</B> int => the number of fractional digits </li>
1841         * <li> <B>NUM_PREC_RADIX</B> int => Radix (typically either 10 or 2) </li>
1842         * <li> <B>NULLABLE</B> int => is NULL allowed?
1843         * <UL>
1844         * <li> columnNoNulls - might not allow NULL values </li>
1845         * <li> columnNullable - definitely allows NULL values </li>
1846         * <li> columnNullableUnknown - nullability unknown </li>
1847         * </ul>
1848         * </li>
1849         * <li> <B>REMARKS</B> String => comment describing column (may be null)
1850         * </li>
1851         * <li> <B>COLUMN_DEF</B> String => default value (may be null) </li>
1852         * <li> <B>SQL_DATA_TYPE</B> int => unused </li>
1853         * <li> <B>SQL_DATETIME_SUB</B> int => unused </li>
1854         * <li> <B>CHAR_OCTET_LENGTH</B> int => for char types the maximum number
1855         * of bytes in the column </li>
1856         * <li> <B>ORDINAL_POSITION</B> int => index of column in table (starting
1857         * at 1) </li>
1858         * <li> <B>IS_NULLABLE</B> String => "NO" means column definitely does not
1859         * allow NULL values; "YES" means the column might allow NULL values. An
1860         * empty string means nobody knows. </li>
1861         * </ol>
1862         * </p>
1863         * 
1864         * @param catalog
1865         *            a catalog name; "" retrieves those without a catalog
1866         * @param schemaPattern
1867         *            a schema name pattern; "" retrieves those without a schema
1868         * @param tableNamePattern
1869         *            a table name pattern
1870         * @param columnNamePattern
1871         *            a column name pattern
1872         * @return ResultSet each row is a column description
1873         * @throws SQLException
1874         *             if a database access error occurs
1875         * @see #getSearchStringEscape
1876         */
1877        public java.sql.ResultSet getColumns(final String catalog,
1878                        final String schemaPattern, final String tableNamePattern,
1879                        String columnNamePattern) throws SQLException {
1880 
1881                if (columnNamePattern == null) {
1882                        if (this.conn.getNullNamePatternMatchesAll()) {
1883                                columnNamePattern = "%";
1884                        } else {
1885                                throw SQLError.createSQLException(
1886                                                "Column name pattern can not be NULL or empty.",
1887                                                SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
1888                        }
1889                }
1890 
1891                final String colPattern = columnNamePattern;
1892 
1893                Field[] fields = new Field[18];
1894                fields[0] = new Field("", "TABLE_CAT", Types.CHAR, 255);
1895                fields[1] = new Field("", "TABLE_SCHEM", Types.CHAR, 0);
1896                fields[2] = new Field("", "TABLE_NAME", Types.CHAR, 255);
1897                fields[3] = new Field("", "COLUMN_NAME", Types.CHAR, 32);
1898                fields[4] = new Field("", "DATA_TYPE", Types.SMALLINT, 5);
1899                fields[5] = new Field("", "TYPE_NAME", Types.CHAR, 16);
1900                fields[6] = new Field("", "COLUMN_SIZE", Types.INTEGER, Integer
1901                                .toString(Integer.MAX_VALUE).length());
1902                fields[7] = new Field("", "BUFFER_LENGTH", Types.INTEGER, 10);
1903                fields[8] = new Field("", "DECIMAL_DIGITS", Types.INTEGER, 10);
1904                fields[9] = new Field("", "NUM_PREC_RADIX", Types.INTEGER, 10);
1905                fields[10] = new Field("", "NULLABLE", Types.INTEGER, 10);
1906                fields[11] = new Field("", "REMARKS", Types.CHAR, 0);
1907                fields[12] = new Field("", "COLUMN_DEF", Types.CHAR, 0);
1908                fields[13] = new Field("", "SQL_DATA_TYPE", Types.INTEGER, 10);
1909                fields[14] = new Field("", "SQL_DATETIME_SUB", Types.INTEGER, 10);
1910                fields[15] = new Field("", "CHAR_OCTET_LENGTH", Types.INTEGER, Integer
1911                                .toString(Integer.MAX_VALUE).length());
1912                fields[16] = new Field("", "ORDINAL_POSITION", Types.INTEGER, 10);
1913                fields[17] = new Field("", "IS_NULLABLE", Types.CHAR, 3);
1914 
1915                final ArrayList rows = new ArrayList();
1916                final Statement stmt = this.conn.getMetadataSafeStatement();
1917 
1918                try {
1919 
1920                        new IterateBlock(getCatalogIterator(catalog)) {
1921                                void forEach(Object catalogStr) throws SQLException {
1922 
1923                                        ArrayList tableNameList = new ArrayList();
1924 
1925                                        if (tableNamePattern == null) {
1926                                                // Select from all tables
1927                                                java.sql.ResultSet tables = null;
1928 
1929                                                try {
1930                                                        tables = getTables(catalog, schemaPattern, "%",
1931                                                                        new String[0]);
1932 
1933                                                        while (tables.next()) {
1934                                                                String tableNameFromList = tables
1935                                                                                .getString("TABLE_NAME");
1936                                                                tableNameList.add(tableNameFromList);
1937                                                        }
1938                                                } finally {
1939                                                        if (tables != null) {
1940                                                                try {
1941                                                                        tables.close();
1942                                                                } catch (Exception sqlEx) {
1943                                                                        AssertionFailedException
1944                                                                                        .shouldNotHappen(sqlEx);
1945                                                                }
1946 
1947                                                                tables = null;
1948                                                        }
1949                                                }
1950                                        } else {
1951                                                java.sql.ResultSet tables = null;
1952 
1953                                                try {
1954                                                        tables = getTables(catalog, schemaPattern,
1955                                                                        tableNamePattern, new String[0]);
1956 
1957                                                        while (tables.next()) {
1958                                                                String tableNameFromList = tables
1959                                                                                .getString("TABLE_NAME");
1960                                                                tableNameList.add(tableNameFromList);
1961                                                        }
1962                                                } finally {
1963                                                        if (tables != null) {
1964                                                                try {
1965                                                                        tables.close();
1966                                                                } catch (SQLException sqlEx) {
1967                                                                        AssertionFailedException
1968                                                                                        .shouldNotHappen(sqlEx);
1969                                                                }
1970 
1971                                                                tables = null;
1972                                                        }
1973                                                }
1974                                        }
1975 
1976                                        java.util.Iterator tableNames = tableNameList.iterator();
1977 
1978                                        while (tableNames.hasNext()) {
1979                                                String tableName = (String) tableNames.next();
1980 
1981                                                ResultSet results = null;
1982 
1983                                                try {
1984                                                        StringBuffer queryBuf = new StringBuffer("SHOW ");
1985 
1986                                                        if (conn.versionMeetsMinimum(4, 1, 0)) {
1987                                                                queryBuf.append("FULL ");
1988                                                        }
1989 
1990                                                        queryBuf.append("COLUMNS FROM ");
1991                                                        queryBuf.append(quotedId);
1992                                                        queryBuf.append(tableName);
1993                                                        queryBuf.append(quotedId);
1994                                                        queryBuf.append(" FROM ");
1995                                                        queryBuf.append(quotedId);
1996                                                        queryBuf.append(catalogStr.toString());
1997                                                        queryBuf.append(quotedId);
1998                                                        queryBuf.append(" LIKE '");
1999                                                        queryBuf.append(colPattern);
2000                                                        queryBuf.append("'");
2001 
2002                                                        // Return correct ordinals if column name pattern is
2003                                                        // not '%'
2004                                                        // Currently, MySQL doesn't show enough data to do
2005                                                        // this, so we do it the 'hard' way...Once _SYSTEM
2006                                                        // tables are in, this should be much easier
2007                                                        boolean fixUpOrdinalsRequired = false;
2008                                                        Map ordinalFixUpMap = null;
2009 
2010                                                        if (!colPattern.equals("%")) {
2011                                                                fixUpOrdinalsRequired = true;
2012 
2013                                                                StringBuffer fullColumnQueryBuf = new StringBuffer(
2014                                                                                "SHOW ");
2015 
2016                                                                if (conn.versionMeetsMinimum(4, 1, 0)) {
2017                                                                        fullColumnQueryBuf.append("FULL ");
2018                                                                }
2019 
2020                                                                fullColumnQueryBuf.append("COLUMNS FROM ");
2021                                                                fullColumnQueryBuf.append(quotedId);
2022                                                                fullColumnQueryBuf.append(tableName);
2023                                                                fullColumnQueryBuf.append(quotedId);
2024                                                                fullColumnQueryBuf.append(" FROM ");
2025                                                                fullColumnQueryBuf.append(quotedId);
2026                                                                fullColumnQueryBuf
2027                                                                                .append(catalogStr.toString());
2028                                                                fullColumnQueryBuf.append(quotedId);
2029 
2030                                                                results = stmt.executeQuery(fullColumnQueryBuf
2031                                                                                .toString());
2032 
2033                                                                ordinalFixUpMap = new HashMap();
2034 
2035                                                                int fullOrdinalPos = 1;
2036 
2037                                                                while (results.next()) {
2038                                                                        String fullOrdColName = results
2039                                                                                        .getString("Field");
2040 
2041                                                                        ordinalFixUpMap.put(fullOrdColName,
2042                                                                                        new Integer(fullOrdinalPos++));
2043                                                                }
2044                                                        }
2045 
2046                                                        results = stmt.executeQuery(queryBuf.toString());
2047 
2048                                                        int ordPos = 1;
2049 
2050                                                        while (results.next()) {
2051                                                                byte[][] rowVal = new byte[18][];
2052                                                                rowVal[0] = s2b(catalog); // TABLE_CAT
2053                                                                rowVal[1] = null; // TABLE_SCHEM (No schemas
2054                                                                // in MySQL)
2055 
2056                                                                rowVal[2] = s2b(tableName); // TABLE_NAME
2057                                                                rowVal[3] = results.getBytes("Field");
2058 
2059                                                                TypeDescriptor typeDesc = new TypeDescriptor(
2060                                                                                results.getString("Type"), results
2061                                                                                                .getString("Null"));
2062 
2063                                                                rowVal[4] = Short.toString(typeDesc.dataType)
2064                                                                                .getBytes();
2065 
2066                                                                // DATA_TYPE (jdbc)
2067                                                                rowVal[5] = s2b(typeDesc.typeName); // TYPE_NAME
2068                                                                // (native)
2069                                                                rowVal[6] = s2b(Integer
2070                                                                                .toString(typeDesc.columnSize));
2071                                                                rowVal[7] = s2b(Integer
2072                                                                                .toString(typeDesc.bufferLength));
2073                                                                rowVal[8] = s2b(Integer
2074                                                                                .toString(typeDesc.decimalDigits));
2075                                                                rowVal[9] = s2b(Integer
2076                                                                                .toString(typeDesc.numPrecRadix));
2077                                                                rowVal[10] = s2b(Integer
2078                                                                                .toString(typeDesc.nullability));
2079 
2080                                                                //
2081                                                                // Doesn't always have this field, depending on
2082                                                                // version
2083                                                                //
2084                                                                //
2085                                                                // REMARK column
2086                                                                //
2087                                                                try {
2088                                                                        if (conn.versionMeetsMinimum(4, 1, 0)) {
2089                                                                                rowVal[11] = results
2090                                                                                                .getBytes("Comment");
2091                                                                        } else {
2092                                                                                rowVal[11] = results.getBytes("Extra");
2093                                                                        }
2094                                                                } catch (Exception E) {
2095                                                                        rowVal[11] = new byte[0];
2096                                                                }
2097 
2098                                                                // COLUMN_DEF
2099                                                                rowVal[12] = results.getBytes("Default");
2100 
2101                                                                rowVal[13] = new byte[] { (byte) '0' }; // SQL_DATA_TYPE
2102                                                                rowVal[14] = new byte[] { (byte) '0' }; // SQL_DATE_TIME_SUB
2103                                                                rowVal[15] = rowVal[6]; // CHAR_OCTET_LENGTH
2104 
2105                                                                // ORDINAL_POSITION
2106                                                                if (!fixUpOrdinalsRequired) {
2107                                                                        rowVal[16] = Integer.toString(ordPos++)
2108                                                                                        .getBytes();
2109                                                                } else {
2110                                                                        String origColName = results
2111                                                                                        .getString("Field");
2112                                                                        Integer realOrdinal = (Integer) ordinalFixUpMap
2113                                                                                        .get(origColName);
2114 
2115                                                                        if (realOrdinal != null) {
2116                                                                                rowVal[16] = realOrdinal.toString()
2117                                                                                                .getBytes();
2118                                                                        } else {
2119                                                                                throw SQLError.createSQLException(
2120                                                                                                "Can not find column in full column list to determine true ordinal position.",
2121                                                                                                SQLError.SQL_STATE_GENERAL_ERROR);
2122                                                                        }
2123                                                                }
2124 
2125                                                                rowVal[17] = s2b(typeDesc.isNullable);
2126 
2127                                                                rows.add(rowVal);
2128                                                        }
2129                                                } finally {
2130                                                        if (results != null) {
2131                                                                try {
2132                                                                        results.close();
2133                                                                } catch (Exception ex) {
2134                                                                        ;
2135                                                                }
2136 
2137                                                                results = null;
2138                                                        }
2139                                                }
2140                                        }
2141                                }
2142                        }.doForAll();
2143                } finally {
2144                        if (stmt != null) {
2145                                stmt.close();
2146                        }
2147                }
2148 
2149                java.sql.ResultSet results = buildResultSet(fields, rows);
2150 
2151                return results;
2152        }
2153 
2154        /**
2155         * JDBC 2.0 Return the connection that produced this metadata object.
2156         * 
2157         * @return the connection that produced this metadata object.
2158         * @throws SQLException
2159         *             if a database error occurs
2160         */
2161        public java.sql.Connection getConnection() throws SQLException {
2162                return this.conn;
2163        }
2164 
2165        /**
2166         * Get a description of the foreign key columns in the foreign key table
2167         * that reference the primary key columns of the primary key table (describe
2168         * how one table imports another's key.) This should normally return a
2169         * single foreign key/primary key pair (most tables only import a foreign
2170         * key from a table once.) They are ordered by FKTABLE_CAT, FKTABLE_SCHEM,
2171         * FKTABLE_NAME, and KEY_SEQ.
2172         * <P>
2173         * Each foreign key column description has the following columns:
2174         * <OL>
2175         * <li> <B>PKTABLE_CAT</B> String => primary key table catalog (may be
2176         * null) </li>
2177         * <li> <B>PKTABLE_SCHEM</B> String => primary key table schema (may be
2178         * null) </li>
2179         * <li> <B>PKTABLE_NAME</B> String => primary key table name </li>
2180         * <li> <B>PKCOLUMN_NAME</B> String => primary key column name </li>
2181         * <li> <B>FKTABLE_CAT</B> String => foreign key table catalog (may be
2182         * null) being exported (may be null) </li>
2183         * <li> <B>FKTABLE_SCHEM</B> String => foreign key table schema (may be
2184         * null) being exported (may be null) </li>
2185         * <li> <B>FKTABLE_NAME</B> String => foreign key table name being exported
2186         * </li>
2187         * <li> <B>FKCOLUMN_NAME</B> String => foreign key column name being
2188         * exported </li>
2189         * <li> <B>KEY_SEQ</B> short => sequence number within foreign key </li>
2190         * <li> <B>UPDATE_RULE</B> short => What happens to foreign key when
2191         * primary is updated:
2192         * <UL>
2193         * <li> importedKeyCascade - change imported key to agree with primary key
2194         * update </li>
2195         * <li> importedKeyRestrict - do not allow update of primary key if it has
2196         * been imported </li>
2197         * <li> importedKeySetNull - change imported key to NULL if its primary key
2198         * has been updated </li>
2199         * </ul>
2200         * </li>
2201         * <li> <B>DELETE_RULE</B> short => What happens to the foreign key when
2202         * primary is deleted.
2203         * <UL>
2204         * <li> importedKeyCascade - delete rows that import a deleted key </li>
2205         * <li> importedKeyRestrict - do not allow delete of primary key if it has
2206         * been imported </li>
2207         * <li> importedKeySetNull - change imported key to NULL if its primary key
2208         * has been deleted </li>
2209         * </ul>
2210         * </li>
2211         * <li> <B>FK_NAME</B> String => foreign key identifier (may be null) </li>
2212         * <li> <B>PK_NAME</B> String => primary key identifier (may be null) </li>
2213         * </ol>
2214         * </p>
2215         * 
2216         * @param primaryCatalog
2217         *            a catalog name; "" retrieves those without a catalog
2218         * @param primarySchema
2219         *            a schema name pattern; "" retrieves those without a schema
2220         * @param primaryTable
2221         *            a table name
2222         * @param foreignCatalog
2223         *            a catalog name; "" retrieves those without a catalog
2224         * @param foreignSchema
2225         *            a schema name pattern; "" retrieves those without a schema
2226         * @param foreignTable
2227         *            a table name
2228         * @return ResultSet each row is a foreign key column description
2229         * @throws SQLException
2230         *             if a database access error occurs
2231         */
2232        public java.sql.ResultSet getCrossReference(final String primaryCatalog,
2233                        final String primarySchema, final String primaryTable,
2234                        final String foreignCatalog, final String foreignSchema,
2235                        final String foreignTable) throws SQLException {
2236                if (primaryTable == null) {
2237                        throw SQLError.createSQLException("Table not specified.",
2238                                        SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
2239                }
2240 
2241                Field[] fields = new Field[14];
2242                fields[0] = new Field("", "PKTABLE_CAT", Types.CHAR, 255);
2243                fields[1] = new Field("", "PKTABLE_SCHEM", Types.CHAR, 0);
2244                fields[2] = new Field("", "PKTABLE_NAME", Types.CHAR, 255);
2245                fields[3] = new Field("", "PKCOLUMN_NAME", Types.CHAR, 32);
2246                fields[4] = new Field("", "FKTABLE_CAT", Types.CHAR, 255);
2247                fields[5] = new Field("", "FKTABLE_SCHEM", Types.CHAR, 0);
2248                fields[6] = new Field("", "FKTABLE_NAME", Types.CHAR, 255);
2249                fields[7] = new Field("", "FKCOLUMN_NAME", Types.CHAR, 32);
2250                fields[8] = new Field("", "KEY_SEQ", Types.SMALLINT, 2);
2251                fields[9] = new Field("", "UPDATE_RULE", Types.SMALLINT, 2);
2252                fields[10] = new Field("", "DELETE_RULE", Types.SMALLINT, 2);
2253                fields[11] = new Field("", "FK_NAME", Types.CHAR, 0);
2254                fields[12] = new Field("", "PK_NAME", Types.CHAR, 0);
2255                fields[13] = new Field("", "DEFERRABILITY", Types.INTEGER, 2);
2256 
2257                final ArrayList tuples = new ArrayList();
2258 
2259                if (this.conn.versionMeetsMinimum(3, 23, 0)) {
2260 
2261                        final Statement stmt = this.conn.getMetadataSafeStatement();
2262 
2263                        try {
2264 
2265                                new IterateBlock(getCatalogIterator(foreignCatalog)) {
2266                                        void forEach(Object catalogStr) throws SQLException {
2267 
2268                                                ResultSet fkresults = null;
2269 
2270                                                try {
2271 
2272                                                        /*
2273                                                         * Get foreign key information for table
2274                                                         */
2275                                                        if (conn.versionMeetsMinimum(3, 23, 50)) {
2276                                                                fkresults = extractForeignKeyFromCreateTable(
2277                                                                                catalogStr.toString(), null);
2278                                                        } else {
2279                                                                StringBuffer queryBuf = new StringBuffer(
2280                                                                                "SHOW TABLE STATUS FROM ");
2281                                                                queryBuf.append(quotedId);
2282                                                                queryBuf.append(catalogStr.toString());
2283                                                                queryBuf.append(quotedId);
2284 
2285                                                                fkresults = stmt.executeQuery(queryBuf
2286                                                                                .toString());
2287                                                        }
2288 
2289                                                        String foreignTableWithCase = getTableNameWithCase(foreignTable);
2290                                                        String primaryTableWithCase = getTableNameWithCase(primaryTable);
2291 
2292                                                        /*
2293                                                         * Parse imported foreign key information
2294                                                         */
2295 
2296                                                        String dummy;
2297 
2298                                                        while (fkresults.next()) {
2299                                                                String tableType = fkresults.getString("Type");
2300 
2301                                                                if ((tableType != null)
2302                                                                                && (tableType
2303                                                                                                .equalsIgnoreCase("innodb") || tableType
2304                                                                                                .equalsIgnoreCase(SUPPORTS_FK))) {
2305                                                                        String comment = fkresults.getString(
2306                                                                                        "Comment").trim();
2307 
2308                                                                        if (comment != null) {
2309                                                                                StringTokenizer commentTokens = new StringTokenizer(
2310                                                                                                comment, ";", false);
2311 
2312                                                                                if (commentTokens.hasMoreTokens()) {
2313                                                                                        dummy = commentTokens.nextToken();
2314 
2315                                                                                        // Skip InnoDB comment
2316                                                                                }
2317 
2318                                                                                while (commentTokens.hasMoreTokens()) {
2319                                                                                        String keys = commentTokens
2320                                                                                                        .nextToken();
2321                                                                                        LocalAndReferencedColumns parsedInfo = parseTableStatusIntoLocalAndReferencedColumns(keys);
2322 
2323                                                                                        int keySeq = 0;
2324 
2325                                                                                        Iterator referencingColumns = parsedInfo.localColumnsList
2326                                                                                                        .iterator();
2327                                                                                        Iterator referencedColumns = parsedInfo.referencedColumnsList
2328                                                                                                        .iterator();
2329 
2330                                                                                        while (referencingColumns.hasNext()) {
2331                                                                                                String referencingColumn = removeQuotedId(referencingColumns
2332                                                                                                                .next().toString());
2333 
2334                                                                                                // one tuple for each table
2335                                                                                                // between
2336                                                                                                // parenthesis
2337                                                                                                byte[][] tuple = new byte[14][];
2338                                                                                                tuple[4] = ((foreignCatalog == null) ? null
2339                                                                                                                : s2b(foreignCatalog));
2340                                                                                                tuple[5] = ((foreignSchema == null) ? null
2341                                                                                                                : s2b(foreignSchema));
2342                                                                                                dummy = fkresults
2343                                                                                                                .getString("Name"); // FKTABLE_NAME
2344 
2345                                                                                                if (dummy
2346                                                                                                                .compareTo(foreignTableWithCase) != 0) {
2347                                                                                                        continue;
2348                                                                                                }
2349 
2350                                                                                                tuple[6] = s2b(dummy);
2351 
2352                                                                                                tuple[7] = s2b(referencingColumn); // FKCOLUMN_NAME
2353                                                                                                tuple[0] = ((primaryCatalog == null) ? null
2354                                                                                                                : s2b(primaryCatalog));
2355                                                                                                tuple[1] = ((primarySchema == null) ? null
2356                                                                                                                : s2b(primarySchema));
2357 
2358                                                                                                // Skip foreign key if it
2359                                                                                                // doesn't refer to
2360                                                                                                // the right table
2361                                                                                                if (parsedInfo.referencedTable
2362                                                                                                                .compareTo(primaryTableWithCase) != 0) {
2363                                                                                                        continue;
2364                                                                                                }
2365 
2366                                                                                                tuple[2] = s2b(parsedInfo.referencedTable); // PKTABLE_NAME
2367                                                                                                tuple[3] = s2b(removeQuotedId(referencedColumns
2368                                                                                                                .next().toString())); // PKCOLUMN_NAME
2369                                                                                                tuple[8] = Integer.toString(
2370                                                                                                                keySeq).getBytes(); // KEY_SEQ
2371 
2372                                                                                                int[] actions = getForeignKeyActions(keys);
2373 
2374                                                                                                tuple[9] = Integer.toString(
2375                                                                                                                actions[1]).getBytes();
2376                                                                                                tuple[10] = Integer.toString(
2377                                                                                                                actions[0]).getBytes();
2378                                                                                                tuple[11] = null; // FK_NAME
2379                                                                                                tuple[12] = null; // PK_NAME
2380                                                                                                tuple[13] = Integer
2381                                                                                                                .toString(
2382                                                                                                                                java.sql.DatabaseMetaData.importedKeyNotDeferrable)
2383                                                                                                                .getBytes();
2384                                                                                                tuples.add(tuple);
2385                                                                                                keySeq++;
2386                                                                                        }
2387                                                                                }
2388                                                                        }
2389                                                                }
2390                                                        }
2391 
2392                                                } finally {
2393                                                        if (fkresults != null) {
2394                                                                try {
2395                                                                        fkresults.close();
2396                                                                } catch (Exception sqlEx) {
2397                                                                        AssertionFailedException
2398                                                                                        .shouldNotHappen(sqlEx);
2399                                                                }
2400 
2401                                                                fkresults = null;
2402                                                        }
2403                                                }
2404                                        }
2405                                }.doForAll();
2406                        } finally {
2407                                if (stmt != null) {
2408                                        stmt.close();
2409                                }
2410                        }
2411                }
2412 
2413                java.sql.ResultSet results = buildResultSet(fields, tuples);
2414 
2415                return results;
2416        }
2417 
2418        /**
2419         * @see DatabaseMetaData#getDatabaseMajorVersion()
2420         */
2421        public int getDatabaseMajorVersion() throws SQLException {
2422                return this.conn.getServerMajorVersion();
2423        }
2424 
2425        /**
2426         * @see DatabaseMetaData#getDatabaseMinorVersion()
2427         */
2428        public int getDatabaseMinorVersion() throws SQLException {
2429                return this.conn.getServerMinorVersion();
2430        }
2431 
2432        /**
2433         * What's the name of this database product?
2434         * 
2435         * @return database product name
2436         * @throws SQLException
2437         *             DOCUMENT ME!
2438         */
2439        public String getDatabaseProductName() throws SQLException {
2440                return "MySQL";
2441        }
2442 
2443        /**
2444         * What's the version of this database product?
2445         * 
2446         * @return database version
2447         * @throws SQLException
2448         *             DOCUMENT ME!
2449         */
2450        public String getDatabaseProductVersion() throws SQLException {
2451                return this.conn.getServerVersion();
2452        }
2453 
2454        /**
2455         * What's the database's default transaction isolation level? The values are
2456         * defined in java.sql.Connection.
2457         * 
2458         * @return the default isolation level
2459         * @throws SQLException
2460         *             if a database access error occurs
2461         * @see Connection
2462         */
2463        public int getDefaultTransactionIsolation() throws SQLException {
2464                if (this.conn.supportsIsolationLevel()) {
2465                        return java.sql.Connection.TRANSACTION_READ_COMMITTED;
2466                }
2467 
2468                return java.sql.Connection.TRANSACTION_NONE;
2469        }
2470 
2471        /**
2472         * What's this JDBC driver's major version number?
2473         * 
2474         * @return JDBC driver major version
2475         */
2476        public int getDriverMajorVersion() {
2477                return NonRegisteringDriver.getMajorVersionInternal();
2478        }
2479 
2480        /**
2481         * What's this JDBC driver's minor version number?
2482         * 
2483         * @return JDBC driver minor version number
2484         */
2485        public int getDriverMinorVersion() {
2486                return NonRegisteringDriver.getMinorVersionInternal();
2487        }
2488 
2489        /**
2490         * What's the name of this JDBC driver?
2491         * 
2492         * @return JDBC driver name
2493         * @throws SQLException
2494         *             DOCUMENT ME!
2495         */
2496        public String getDriverName() throws SQLException {
2497                return "MySQL-AB JDBC Driver";
2498        }
2499 
2500        /**
2501         * What's the version of this JDBC driver?
2502         * 
2503         * @return JDBC driver version
2504         * @throws java.sql.SQLException
2505         *             DOCUMENT ME!
2506         */
2507        public String getDriverVersion() throws java.sql.SQLException {
2508                return "@MYSQL_CJ_FULL_PROD_NAME@ ( $Date: 2006-07-26 15:26:47 +0000 (Wed, 26 Jul 2006) $, $Revision: 5553 $ )";
2509        }
2510 
2511        /**
2512         * Get a description of a foreign key columns that reference a table's
2513         * primary key columns (the foreign keys exported by a table). They are
2514         * ordered by FKTABLE_CAT, FKTABLE_SCHEM, FKTABLE_NAME, and KEY_SEQ.
2515         * <P>
2516         * Each foreign key column description has the following columns:
2517         * <OL>
2518         * <li> <B>PKTABLE_CAT</B> String => primary key table catalog (may be
2519         * null) </li>
2520         * <li> <B>PKTABLE_SCHEM</B> String => primary key table schema (may be
2521         * null) </li>
2522         * <li> <B>PKTABLE_NAME</B> String => primary key table name </li>
2523         * <li> <B>PKCOLUMN_NAME</B> String => primary key column name </li>
2524         * <li> <B>FKTABLE_CAT</B> String => foreign key table catalog (may be
2525         * null) being exported (may be null) </li>
2526         * <li> <B>FKTABLE_SCHEM</B> String => foreign key table schema (may be
2527         * null) being exported (may be null) </li>
2528         * <li> <B>FKTABLE_NAME</B> String => foreign key table name being exported
2529         * </li>
2530         * <li> <B>FKCOLUMN_NAME</B> String => foreign key column name being
2531         * exported </li>
2532         * <li> <B>KEY_SEQ</B> short => sequence number within foreign key </li>
2533         * <li> <B>UPDATE_RULE</B> short => What happens to foreign key when
2534         * primary is updated:
2535         * <UL>
2536         * <li> importedKeyCascade - change imported key to agree with primary key
2537         * update </li>
2538         * <li> importedKeyRestrict - do not allow update of primary key if it has
2539         * been imported </li>
2540         * <li> importedKeySetNull - change imported key to NULL if its primary key
2541         * has been updated </li>
2542         * </ul>
2543         * </li>
2544         * <li> <B>DELETE_RULE</B> short => What happens to the foreign key when
2545         * primary is deleted.
2546         * <UL>
2547         * <li> importedKeyCascade - delete rows that import a deleted key </li>
2548         * <li> importedKeyRestrict - do not allow delete of primary key if it has
2549         * been imported </li>
2550         * <li> importedKeySetNull - change imported key to NULL if its primary key
2551         * has been deleted </li>
2552         * </ul>
2553         * </li>
2554         * <li> <B>FK_NAME</B> String => foreign key identifier (may be null) </li>
2555         * <li> <B>PK_NAME</B> String => primary key identifier (may be null) </li>
2556         * </ol>
2557         * </p>
2558         * 
2559         * @param catalog
2560         *            a catalog name; "" retrieves those without a catalog
2561         * @param schema
2562         *            a schema name pattern; "" retrieves those without a schema
2563         * @param table
2564         *            a table name
2565         * @return ResultSet each row is a foreign key column description
2566         * @throws SQLException
2567         *             if a database access error occurs
2568         * @see #getImportedKeys
2569         */
2570        public java.sql.ResultSet getExportedKeys(String catalog, String schema,
2571                        final String table) throws SQLException {
2572                if (table == null) {
2573                        throw SQLError.createSQLException("Table not specified.",
2574                                        SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
2575                }
2576 
2577                Field[] fields = new Field[14];
2578                fields[0] = new Field("", "PKTABLE_CAT", Types.CHAR, 255);
2579                fields[1] = new Field("", "PKTABLE_SCHEM", Types.CHAR, 0);
2580                fields[2] = new Field("", "PKTABLE_NAME", Types.CHAR, 255);
2581                fields[3] = new Field("", "PKCOLUMN_NAME", Types.CHAR, 32);
2582                fields[4] = new Field("", "FKTABLE_CAT", Types.CHAR, 255);
2583                fields[5] = new Field("", "FKTABLE_SCHEM", Types.CHAR, 0);
2584                fields[6] = new Field("", "FKTABLE_NAME", Types.CHAR, 255);
2585                fields[7] = new Field("", "FKCOLUMN_NAME", Types.CHAR, 32);
2586                fields[8] = new Field("", "KEY_SEQ", Types.SMALLINT, 2);
2587                fields[9] = new Field("", "UPDATE_RULE", Types.SMALLINT, 2);
2588                fields[10] = new Field("", "DELETE_RULE", Types.SMALLINT, 2);
2589                fields[11] = new Field("", "FK_NAME", Types.CHAR, 255);
2590                fields[12] = new Field("", "PK_NAME", Types.CHAR, 0);
2591                fields[13] = new Field("", "DEFERRABILITY", Types.INTEGER, 2);
2592 
2593                final ArrayList rows = new ArrayList();
2594 
2595                if (this.conn.versionMeetsMinimum(3, 23, 0)) {
2596 
2597                        final Statement stmt = this.conn.getMetadataSafeStatement();
2598 
2599                        try {
2600 
2601                                new IterateBlock(getCatalogIterator(catalog)) {
2602                                        void forEach(Object catalogStr) throws SQLException {
2603                                                ResultSet fkresults = null;
2604 
2605                                                try {
2606 
2607                                                        /*
2608                                                         * Get foreign key information for table
2609                                                         */
2610                                                        if (conn.versionMeetsMinimum(3, 23, 50)) {
2611                                                                // we can use 'SHOW CREATE TABLE'
2612 
2613                                                                fkresults = extractForeignKeyFromCreateTable(
2614                                                                                catalogStr.toString(), null);
2615                                                        } else {
2616                                                                StringBuffer queryBuf = new StringBuffer(
2617                                                                                "SHOW TABLE STATUS FROM ");
2618                                                                queryBuf.append(quotedId);
2619                                                                queryBuf.append(catalogStr.toString());
2620                                                                queryBuf.append(quotedId);
2621 
2622                                                                fkresults = stmt.executeQuery(queryBuf
2623                                                                                .toString());
2624                                                        }
2625 
2626                                                        // lower-case table name might be turned on
2627                                                        String tableNameWithCase = getTableNameWithCase(table);
2628 
2629                                                        /*
2630                                                         * Parse imported foreign key information
2631                                                         */
2632 
2633                                                        while (fkresults.next()) {
2634                                                                String tableType = fkresults.getString("Type");
2635 
2636                                                                if ((tableType != null)
2637                                                                                && (tableType
2638                                                                                                .equalsIgnoreCase("innodb") || tableType
2639                                                                                                .equalsIgnoreCase(SUPPORTS_FK))) {
2640                                                                        String comment = fkresults.getString(
2641                                                                                        "Comment").trim();
2642 
2643                                                                        if (comment != null) {
2644                                                                                StringTokenizer commentTokens = new StringTokenizer(
2645                                                                                                comment, ";", false);
2646 
2647                                                                                if (commentTokens.hasMoreTokens()) {
2648                                                                                        commentTokens.nextToken(); // Skip
2649                                                                                        // InnoDB
2650                                                                                        // comment
2651 
2652                                                                                        while (commentTokens
2653                                                                                                        .hasMoreTokens()) {
2654                                                                                                String keys = commentTokens
2655                                                                                                                .nextToken();
2656                                                                                                getExportKeyResults(
2657                                                                                                                catalogStr.toString(),
2658                                                                                                                tableNameWithCase,
2659                                                                                                                keys,
2660                                                                                                                rows,
2661                                                                                                                fkresults
2662                                                                                                                                .getString("Name"));
2663                                                                                        }
2664                                                                                }
2665                                                                        }
2666                                                                }
2667                                                        }
2668 
2669                                                } finally {
2670                                                        if (fkresults != null) {
2671                                                                try {
2672                                                                        fkresults.close();
2673                                                                } catch (SQLException sqlEx) {
2674                                                                        AssertionFailedException
2675                                                                                        .shouldNotHappen(sqlEx);
2676                                                                }
2677 
2678                                                                fkresults = null;
2679                                                        }
2680                                                }
2681                                        }
2682                                }.doForAll();
2683                        } finally {
2684                                if (stmt != null) {
2685                                        stmt.close();
2686                                }
2687                        }
2688                }
2689 
2690                java.sql.ResultSet results = buildResultSet(fields, rows);
2691 
2692                return results;
2693        }
2694 
2695        /**
2696         * Adds to the tuples list the exported keys of exportingTable based on the
2697         * keysComment from the 'show table status' sql command. KeysComment is that
2698         * part of the comment field that follows the "InnoDB free ...;" prefix.
2699         * 
2700         * @param catalog
2701         *            the database to use
2702         * @param exportingTable
2703         *            the table keys are being exported from
2704         * @param keysComment
2705         *            the comment from 'show table status'
2706         * @param tuples
2707         *            the rows to add results to
2708         * @param fkTableName
2709         *            the foreign key table name
2710         * @throws SQLException
2711         *             if a database access error occurs
2712         */
2713        private void getExportKeyResults(String catalog, String exportingTable,
2714                        String keysComment, List tuples, String fkTableName)
2715                        throws SQLException {
2716                getResultsImpl(catalog, exportingTable, keysComment, tuples,
2717                                fkTableName, true);
2718        }
2719 
2720        /**
2721         * Get all the "extra" characters that can be used in unquoted identifier
2722         * names (those beyond a-z, 0-9 and _).
2723         * 
2724         * @return the string containing the extra characters
2725         * @throws SQLException
2726         *             DOCUMENT ME!
2727         */
2728        public String getExtraNameCharacters() throws SQLException {
2729                return "#@";
2730        }
2731 
2732        /**
2733         * Returns the DELETE and UPDATE foreign key actions from the given 'SHOW
2734         * TABLE STATUS' string, with the DELETE action being the first item in the
2735         * array, and the UPDATE action being the second.
2736         * 
2737         * @param commentString
2738         *            the comment from 'SHOW TABLE STATUS'
2739         * @return int[] [0] = delete action, [1] = update action
2740         */
2741        private int[] getForeignKeyActions(String commentString) {
2742                int[] actions = new int[] {
2743                                java.sql.DatabaseMetaData.importedKeyNoAction,
2744                                java.sql.DatabaseMetaData.importedKeyNoAction };
2745 
2746                int lastParenIndex = commentString.lastIndexOf(")");
2747 
2748                if (lastParenIndex != (commentString.length() - 1)) {
2749                        String cascadeOptions = commentString.substring(lastParenIndex + 1)
2750                                        .trim().toUpperCase(Locale.ENGLISH);
2751 
2752                        actions[0] = getCascadeDeleteOption(cascadeOptions);
2753                        actions[1] = getCascadeUpdateOption(cascadeOptions);
2754                }
2755 
2756                return actions;
2757        }
2758 
2759        /**
2760         * What's the string used to quote SQL identifiers? This returns a space " "
2761         * if identifier quoting isn't supported. A JDBC compliant driver always
2762         * uses a double quote character.
2763         * 
2764         * @return the quoting string
2765         * @throws SQLException
2766         *             DOCUMENT ME!
2767         */
2768        public String getIdentifierQuoteString() throws SQLException {
2769                if (this.conn.supportsQuotedIdentifiers()) {
2770                        if (!this.conn.useAnsiQuotedIdentifiers()) {
2771                                return "`";
2772                        }
2773 
2774                        return "\"";
2775                }
2776 
2777                return " ";
2778        }
2779 
2780        /**
2781         * Get a description of the primary key columns that are referenced by a
2782         * table's foreign key columns (the primary keys imported by a table). They
2783         * are ordered by PKTABLE_CAT, PKTABLE_SCHEM, PKTABLE_NAME, and KEY_SEQ.
2784         * <P>
2785         * Each primary key column description has the following columns:
2786         * <OL>
2787         * <li> <B>PKTABLE_CAT</B> String => primary key table catalog being
2788         * imported (may be null) </li>
2789         * <li> <B>PKTABLE_SCHEM</B> String => primary key table schema being
2790         * imported (may be null) </li>
2791         * <li> <B>PKTABLE_NAME</B> String => primary key table name being imported
2792         * </li>
2793         * <li> <B>PKCOLUMN_NAME</B> String => primary key column name being
2794         * imported </li>
2795         * <li> <B>FKTABLE_CAT</B> String => foreign key table catalog (may be
2796         * null) </li>
2797         * <li> <B>FKTABLE_SCHEM</B> String => foreign key table schema (may be
2798         * null) </li>
2799         * <li> <B>FKTABLE_NAME</B> String => foreign key table name </li>
2800         * <li> <B>FKCOLUMN_NAME</B> String => foreign key column name </li>
2801         * <li> <B>KEY_SEQ</B> short => sequence number within foreign key </li>
2802         * <li> <B>UPDATE_RULE</B> short => What happens to foreign key when
2803         * primary is updated:
2804         * <UL>
2805         * <li> importedKeyCascade - change imported key to agree with primary key
2806         * update </li>
2807         * <li> importedKeyRestrict - do not allow update of primary key if it has
2808         * been imported </li>
2809         * <li> importedKeySetNull - change imported key to NULL if its primary key
2810         * has been updated </li>
2811         * </ul>
2812         * </li>
2813         * <li> <B>DELETE_RULE</B> short => What happens to the foreign key when
2814         * primary is deleted.
2815         * <UL>
2816         * <li> importedKeyCascade - delete rows that import a deleted key </li>
2817         * <li> importedKeyRestrict - do not allow delete of primary key if it has
2818         * been imported </li>
2819         * <li> importedKeySetNull - change imported key to NULL if its primary key
2820         * has been deleted </li>
2821         * </ul>
2822         * </li>
2823         * <li> <B>FK_NAME</B> String => foreign key name (may be null) </li>
2824         * <li> <B>PK_NAME</B> String => primary key name (may be null) </li>
2825         * </ol>
2826         * </p>
2827         * 
2828         * @param catalog
2829         *            a catalog name; "" retrieves those without a catalog
2830         * @param schema
2831         *            a schema name pattern; "" retrieves those without a schema
2832         * @param table
2833         *            a table name
2834         * @return ResultSet each row is a primary key column description
2835         * @throws SQLException
2836         *             if a database access error occurs
2837         * @see #getExportedKeys
2838         */
2839        public java.sql.ResultSet getImportedKeys(String catalog, String schema,
2840                        final String table) throws SQLException {
2841                if (table == null) {
2842                        throw SQLError.createSQLException("Table not specified.",
2843                                        SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
2844                }
2845 
2846                Field[] fields = new Field[14];
2847                fields[0] = new Field("", "PKTABLE_CAT", Types.CHAR, 255);
2848                fields[1] = new Field("", "PKTABLE_SCHEM", Types.CHAR, 0);
2849                fields[2] = new Field("", "PKTABLE_NAME", Types.CHAR, 255);
2850                fields[3] = new Field("", "PKCOLUMN_NAME", Types.CHAR, 32);
2851                fields[4] = new Field("", "FKTABLE_CAT", Types.CHAR, 255);
2852                fields[5] = new Field("", "FKTABLE_SCHEM", Types.CHAR, 0);
2853                fields[6] = new Field("", "FKTABLE_NAME", Types.CHAR, 255);
2854                fields[7] = new Field("", "FKCOLUMN_NAME", Types.CHAR, 32);
2855                fields[8] = new Field("", "KEY_SEQ", Types.SMALLINT, 2);
2856                fields[9] = new Field("", "UPDATE_RULE", Types.SMALLINT, 2);
2857                fields[10] = new Field("", "DELETE_RULE", Types.SMALLINT, 2);
2858                fields[11] = new Field("", "FK_NAME", Types.CHAR, 255);
2859                fields[12] = new Field("", "PK_NAME", Types.CHAR, 0);
2860                fields[13] = new Field("", "DEFERRABILITY", Types.INTEGER, 2);
2861 
2862                final ArrayList rows = new ArrayList();
2863 
2864                if (this.conn.versionMeetsMinimum(3, 23, 0)) {
2865 
2866                        final Statement stmt = this.conn.getMetadataSafeStatement();
2867 
2868                        try {
2869 
2870                                new IterateBlock(getCatalogIterator(catalog)) {
2871                                        void forEach(Object catalogStr) throws SQLException {
2872                                                ResultSet fkresults = null;
2873 
2874                                                try {
2875 
2876                                                        /*
2877                                                         * Get foreign key information for table
2878                                                         */
2879                                                        if (conn.versionMeetsMinimum(3, 23, 50)) {
2880                                                                // we can use 'SHOW CREATE TABLE'
2881 
2882                                                                fkresults = extractForeignKeyFromCreateTable(
2883                                                                                catalogStr.toString(), table);
2884                                                        } else {
2885                                                                StringBuffer queryBuf = new StringBuffer(
2886                                                                                "SHOW TABLE STATUS ");
2887                                                                queryBuf.append(" FROM ");
2888                                                                queryBuf.append(quotedId);
2889                                                                queryBuf.append(catalogStr.toString());
2890                                                                queryBuf.append(quotedId);
2891                                                                queryBuf.append(" LIKE '");
2892                                                                queryBuf.append(table);
2893                                                                queryBuf.append("'");
2894 
2895                                                                fkresults = stmt.executeQuery(queryBuf
2896                                                                                .toString());
2897                                                        }
2898 
2899                                                        /*
2900                                                         * Parse imported foreign key information
2901                                                         */
2902 
2903                                                        while (fkresults.next()) {
2904                                                                String tableType = fkresults.getString("Type");
2905 
2906                                                                if ((tableType != null)
2907                                                                                && (tableType
2908                                                                                                .equalsIgnoreCase("innodb") || tableType
2909                                                                                                .equalsIgnoreCase(SUPPORTS_FK))) {
2910                                                                        String comment = fkresults.getString(
2911                                                                                        "Comment").trim();
2912 
2913                                                                        if (comment != null) {
2914                                                                                StringTokenizer commentTokens = new StringTokenizer(
2915                                                                                                comment, ";", false);
2916 
2917                                                                                if (commentTokens.hasMoreTokens()) {
2918                                                                                        commentTokens.nextToken(); // Skip
2919                                                                                        // InnoDB
2920                                                                                        // comment
2921 
2922                                                                                        while (commentTokens
2923                                                                                                        .hasMoreTokens()) {
2924                                                                                                String keys = commentTokens
2925                                                                                                                .nextToken();
2926                                                                                                getImportKeyResults(catalogStr
2927                                                                                                                .toString(), table,
2928                                                                                                                keys, rows);
2929                                                                                        }
2930                                                                                }
2931                                                                        }
2932                                                                }
2933                                                        }
2934                                                } finally {
2935                                                        if (fkresults != null) {
2936                                                                try {
2937                                                                        fkresults.close();
2938                                                                } catch (SQLException sqlEx) {
2939                                                                        AssertionFailedException
2940                                                                                        .shouldNotHappen(sqlEx);
2941                                                                }
2942 
2943                                                                fkresults = null;
2944                                                        }
2945                                                }
2946                                        }
2947                                }.doForAll();
2948                        } finally {
2949                                if (stmt != null) {
2950                                        stmt.close();
2951                                }
2952                        }
2953                }
2954 
2955                java.sql.ResultSet results = buildResultSet(fields, rows);
2956 
2957                return results;
2958        }
2959 
2960        /**
2961         * Populates the tuples list with the imported keys of importingTable based
2962         * on the keysComment from the 'show table status' sql command. KeysComment
2963         * is that part of the comment field that follows the "InnoDB free ...;"
2964         * prefix.
2965         * 
2966         * @param catalog
2967         *            the database to use
2968         * @param importingTable
2969         *            the table keys are being imported to
2970         * @param keysComment
2971         *            the comment from 'show table status'
2972         * @param tuples
2973         *            the rows to add results to
2974         * @throws SQLException
2975         *             if a database access error occurs
2976         */
2977        private void getImportKeyResults(String catalog, String importingTable,
2978                        String keysComment, List tuples) throws SQLException {
2979                getResultsImpl(catalog, importingTable, keysComment, tuples, null,
2980                                false);
2981        }
2982 
2983        /**
2984         * Get a description of a table's indices and statistics. They are ordered
2985         * by NON_UNIQUE, TYPE, INDEX_NAME, and ORDINAL_POSITION.
2986         * <P>
2987         * Each index column description has the following columns:
2988         * <OL>
2989         * <li> <B>TABLE_CAT</B> String => table catalog (may be null) </li>
2990         * <li> <B>TABLE_SCHEM</B> String => table schema (may be null) </li>
2991         * <li> <B>TABLE_NAME</B> String => table name </li>
2992         * <li> <B>NON_UNIQUE</B> boolean => Can index values be non-unique? false
2993         * when TYPE is tableIndexStatistic </li>
2994         * <li> <B>INDEX_QUALIFIER</B> String => index catalog (may be null); null
2995         * when TYPE is tableIndexStatistic </li>
2996         * <li> <B>INDEX_NAME</B> String => index name; null when TYPE is
2997         * tableIndexStatistic </li>
2998         * <li> <B>TYPE</B> short => index type:
2999         * <UL>
3000         * <li> tableIndexStatistic - this identifies table statistics that are
3001         * returned in conjuction with a table's index descriptions </li>
3002         * <li> tableIndexClustered - this is a clustered index </li>
3003         * <li> tableIndexHashed - this is a hashed index </li>
3004         * <li> tableIndexOther - this is some other style of index </li>
3005         * </ul>
3006         * </li>
3007         * <li> <B>ORDINAL_POSITION</B> short => column sequence number within
3008         * index; zero when TYPE is tableIndexStatistic </li>
3009         * <li> <B>COLUMN_NAME</B> String => column name; null when TYPE is
3010         * tableIndexStatistic </li>
3011         * <li> <B>ASC_OR_DESC</B> String => column sort sequence, "A" =>
3012         * ascending, "D" => descending, may be null if sort sequence is not
3013         * supported; null when TYPE is tableIndexStatistic </li>
3014         * <li> <B>CARDINALITY</B> int => When TYPE is tableIndexStatisic then this
3015         * is the number of rows in the table; otherwise it is the number of unique
3016         * values in the index. </li>
3017         * <li> <B>PAGES</B> int => When TYPE is tableIndexStatisic then this is
3018         * the number of pages used for the table, otherwise it is the number of
3019         * pages used for the current index. </li>
3020         * <li> <B>FILTER_CONDITION</B> String => Filter condition, if any. (may be
3021         * null) </li>
3022         * </ol>
3023         * </p>
3024         * 
3025         * @param catalog
3026         *            a catalog name; "" retrieves those without a catalog
3027         * @param schema
3028         *            a schema name pattern; "" retrieves those without a schema
3029         * @param table
3030         *            a table name
3031         * @param unique
3032         *            when true, return only indices for unique values; when false,
3033         *            return indices regardless of whether unique or not
3034         * @param approximate
3035         *            when true, result is allowed to reflect approximate or out of
3036         *            data values; when false, results are requested to be accurate
3037         * @return ResultSet each row is an index column description
3038         * @throws SQLException
3039         *             DOCUMENT ME!
3040         */
3041        public java.sql.ResultSet getIndexInfo(String catalog, String schema,
3042                        final String table, final boolean unique, boolean approximate)
3043                        throws SQLException {
3044                /*
3045                 * MySQL stores index information in the following fields: Table
3046                 * Non_unique Key_name Seq_in_index Column_name Collation Cardinality
3047                 * Sub_part
3048                 */
3049 
3050                Field[] fields = new Field[13];
3051                fields[0] = new Field("", "TABLE_CAT", Types.CHAR, 255);
3052                fields[1] = new Field("", "TABLE_SCHEM", Types.CHAR, 0);
3053                fields[2] = new Field("", "TABLE_NAME", Types.CHAR, 255);
3054                fields[3] = new Field("", "NON_UNIQUE", Types.CHAR, 4);
3055                fields[4] = new Field("", "INDEX_QUALIFIER", Types.CHAR, 1);
3056                fields[5] = new Field("", "INDEX_NAME", Types.CHAR, 32);
3057                fields[6] = new Field("", "TYPE", Types.CHAR, 32);
3058                fields[7] = new Field("", "ORDINAL_POSITION", Types.SMALLINT, 5);
3059                fields[8] = new Field("", "COLUMN_NAME", Types.CHAR, 32);
3060                fields[9] = new Field("", "ASC_OR_DESC", Types.CHAR, 1);
3061                fields[10] = new Field("", "CARDINALITY", Types.INTEGER, 10);
3062                fields[11] = new Field("", "PAGES", Types.INTEGER, 10);
3063                fields[12] = new Field("", "FILTER_CONDITION", Types.CHAR, 32);
3064 
3065                final ArrayList rows = new ArrayList();
3066                final Statement stmt = this.conn.getMetadataSafeStatement();
3067 
3068                try {
3069 
3070                        new IterateBlock(getCatalogIterator(catalog)) {
3071                                void forEach(Object catalogStr) throws SQLException {
3072 
3073                                        ResultSet results = null;
3074 
3075                                        try {
3076                                                StringBuffer queryBuf = new StringBuffer(
3077                                                                "SHOW INDEX FROM ");
3078                                                queryBuf.append(quotedId);
3079                                                queryBuf.append(table);
3080                                                queryBuf.append(quotedId);
3081                                                queryBuf.append(" FROM ");
3082                                                queryBuf.append(quotedId);
3083                                                queryBuf.append(catalogStr.toString());
3084                                                queryBuf.append(quotedId);
3085 
3086                                                try {
3087                                                        results = stmt.executeQuery(queryBuf.toString());
3088                                                } catch (SQLException sqlEx) {
3089                                                        int errorCode = sqlEx.getErrorCode();
3090 
3091                                                        // If SQLState is 42S02, ignore this SQLException
3092                                                        // it means the table doesn't exist....
3093                                                        if (!"42S02".equals(sqlEx.getSQLState())) {
3094                                                                // Sometimes not mapped correctly for pre-4.1
3095                                                                // so use error code instead.
3096                                                                if (errorCode != MysqlErrorNumbers.ER_NO_SUCH_TABLE) {
3097                                                                        throw sqlEx;
3098                                                                }
3099                                                        }
3100                                                }
3101 
3102                                                while (results != null && results.next()) {
3103                                                        byte[][] row = new byte[14][];
3104                                                        row[0] = ((catalogStr.toString() == null) ? new byte[0]
3105                                                                        : s2b(catalogStr.toString()));
3106                                                        ;
3107                                                        row[1] = null;
3108                                                        row[2] = results.getBytes("Table");
3109 
3110                                                        boolean indexIsUnique = results
3111                                                                        .getInt("Non_unique") == 0;
3112 
3113                                                        row[3] = (!indexIsUnique ? s2b("true")
3114                                                                        : s2b("false"));
3115                                                        row[4] = new byte[0];
3116                                                        row[5] = results.getBytes("Key_name");
3117                                                        row[6] = Integer.toString(
3118                                                                        java.sql.DatabaseMetaData.tableIndexOther)
3119                                                                        .getBytes();
3120                                                        row[7] = results.getBytes("Seq_in_index");
3121                                                        row[8] = results.getBytes("Column_name");
3122                                                        row[9] = results.getBytes("Collation");
3123                                                        row[10] = results.getBytes("Cardinality");
3124                                                        row[11] = s2b("0");
3125                                                        row[12] = null;
3126 
3127                                                        if (unique) {
3128                                                                if (indexIsUnique) {
3129                                                                        rows.add(row);
3130                                                                }
3131                                                        } else {
3132                                                                // All rows match
3133                                                                rows.add(row);
3134                                                        }
3135                                                }
3136                                        } finally {
3137                                                if (results != null) {
3138                                                        try {
3139                                                                results.close();
3140                                                        } catch (Exception ex) {
3141                                                                ;
3142                                                        }
3143 
3144                                                        results = null;
3145                                                }
3146                                        }
3147                                }
3148                        }.doForAll();
3149 
3150                        java.sql.ResultSet indexInfo = buildResultSet(fields, rows);
3151 
3152                        return indexInfo;
3153                } finally {
3154                        if (stmt != null) {
3155                                stmt.close();
3156                        }
3157                }
3158        }
3159 
3160        /**
3161         * @see DatabaseMetaData#getJDBCMajorVersion()
3162         */
3163        public int getJDBCMajorVersion() throws SQLException {
3164                return 3;
3165        }
3166 
3167        /**
3168         * @see DatabaseMetaData#getJDBCMinorVersion()
3169         */
3170        public int getJDBCMinorVersion() throws SQLException {
3171                return 0;
3172        }
3173 
3174        /**
3175         * How many hex characters can you have in an inline binary literal?
3176         * 
3177         * @return max literal length
3178         * @throws SQLException
3179         *             DOCUMENT ME!
3180         */
3181        public int getMaxBinaryLiteralLength() throws SQLException {
3182                return 16777208;
3183        }
3184 
3185        /**
3186         * What's the maximum length of a catalog name?
3187         * 
3188         * @return max name length in bytes
3189         * @throws SQLException
3190         *             DOCUMENT ME!
3191         */
3192        public int getMaxCatalogNameLength() throws SQLException {
3193                return 32;
3194        }
3195 
3196        /**
3197         * What's the max length for a character literal?
3198         * 
3199         * @return max literal length
3200         * @throws SQLException
3201         *             DOCUMENT ME!
3202         */
3203        public int getMaxCharLiteralLength() throws SQLException {
3204                return 16777208;
3205        }
3206 
3207        /**
3208         * What's the limit on column name length?
3209         * 
3210         * @return max literal length
3211         * @throws SQLException
3212         *             DOCUMENT ME!
3213         */
3214        public int getMaxColumnNameLength() throws SQLException {
3215                return 64;
3216        }
3217 
3218        /**
3219         * What's the maximum number of columns in a "GROUP BY" clause?
3220         * 
3221         * @return max number of columns
3222         * @throws SQLException
3223         *             DOCUMENT ME!
3224         */
3225        public int getMaxColumnsInGroupBy() throws SQLException {
3226                return 64;
3227        }
3228 
3229        /**
3230         * What's the maximum number of columns allowed in an index?
3231         * 
3232         * @return max columns
3233         * @throws SQLException
3234         *             DOCUMENT ME!
3235         */
3236        public int getMaxColumnsInIndex() throws SQLException {
3237                return 16;
3238        }
3239 
3240        /**
3241         * What's the maximum number of columns in an "ORDER BY" clause?
3242         * 
3243         * @return max columns
3244         * @throws SQLException
3245         *             DOCUMENT ME!
3246         */
3247        public int getMaxColumnsInOrderBy() throws SQLException {
3248                return 64;
3249        }
3250 
3251        /**
3252         * What's the maximum number of columns in a "SELECT" list?
3253         * 
3254         * @return max columns
3255         * @throws SQLException
3256         *             DOCUMENT ME!
3257         */
3258        public int getMaxColumnsInSelect() throws SQLException {
3259                return 256;
3260        }
3261 
3262        /**
3263         * What's maximum number of columns in a table?
3264         * 
3265         * @return max columns
3266         * @throws SQLException
3267         *             DOCUMENT ME!
3268         */
3269        public int getMaxColumnsInTable() throws SQLException {
3270                return 512;
3271        }
3272 
3273        /**
3274         * How many active connections can we have at a time to this database?
3275         * 
3276         * @return max connections
3277         * @throws SQLException
3278         *             DOCUMENT ME!
3279         */
3280        public int getMaxConnections() throws SQLException {
3281                return 0;
3282        }
3283 
3284        /**
3285         * What's the maximum cursor name length?
3286         * 
3287         * @return max cursor name length in bytes
3288         * @throws SQLException
3289         *             DOCUMENT ME!
3290         */
3291        public int getMaxCursorNameLength() throws SQLException {
3292                return 64;
3293        }
3294 
3295        /**
3296         * What's the maximum length of an index (in bytes)?
3297         * 
3298         * @return max index length in bytes
3299         * @throws SQLException
3300         *             DOCUMENT ME!
3301         */
3302        public int getMaxIndexLength() throws SQLException {
3303                return 256;
3304        }
3305 
3306        /**
3307         * What's the maximum length of a procedure name?
3308         * 
3309         * @return max name length in bytes
3310         * @throws SQLException
3311         *             DOCUMENT ME!
3312         */
3313        public int getMaxProcedureNameLength() throws SQLException {
3314                return 0;
3315        }
3316 
3317        /**
3318         * What's the maximum length of a single row?
3319         * 
3320         * @return max row size in bytes
3321         * @throws SQLException
3322         *             DOCUMENT ME!
3323         */
3324        public int getMaxRowSize() throws SQLException {
3325                return Integer.MAX_VALUE - 8; // Max buffer size - HEADER
3326        }
3327 
3328        /**
3329         * What's the maximum length allowed for a schema name?
3330         * 
3331         * @return max name length in bytes
3332         * @throws SQLException
3333         *             DOCUMENT ME!
3334         */
3335        public int getMaxSchemaNameLength() throws SQLException {
3336                return 0;
3337        }
3338 
3339        /**
3340         * What's the maximum length of a SQL statement?
3341         * 
3342         * @return max length in bytes
3343         * @throws SQLException
3344         *             DOCUMENT ME!
3345         */
3346        public int getMaxStatementLength() throws SQLException {
3347                return MysqlIO.getMaxBuf() - 4; // Max buffer - header
3348        }
3349 
3350        /**
3351         * How many active statements can we have open at one time to this database?
3352         * 
3353         * @return the maximum
3354         * @throws SQLException
3355         *             DOCUMENT ME!
3356         */
3357        public int getMaxStatements() throws SQLException {
3358                return 0;
3359        }
3360 
3361        /**
3362         * What's the maximum length of a table name?
3363         * 
3364         * @return max name length in bytes
3365         * @throws SQLException
3366         *             DOCUMENT ME!
3367         */
3368        public int getMaxTableNameLength() throws SQLException {
3369                return 64;
3370        }
3371 
3372        /**
3373         * What's the maximum number of tables in a SELECT?
3374         * 
3375         * @return the maximum
3376         * @throws SQLException
3377         *             DOCUMENT ME!
3378         */
3379        public int getMaxTablesInSelect() throws SQLException {
3380                return 256;
3381        }
3382 
3383        /**
3384         * What's the maximum length of a user name?
3385         * 
3386         * @return max name length in bytes
3387         * @throws SQLException
3388         *             DOCUMENT ME!
3389         */
3390        public int getMaxUserNameLength() throws SQLException {
3391                return 16;
3392        }
3393 
3394        /**
3395         * Get a comma separated list of math functions.
3396         * 
3397         * @return the list
3398         * @throws SQLException
3399         *             DOCUMENT ME!
3400         */
3401        public String getNumericFunctions() throws SQLException {
3402                return "ABS,ACOS,ASIN,ATAN,ATAN2,BIT_COUNT,CEILING,COS,"
3403                                + "COT,DEGREES,EXP,FLOOR,LOG,LOG10,MAX,MIN,MOD,PI,POW,"
3404                                + "POWER,RADIANS,RAND,ROUND,SIN,SQRT,TAN,TRUNCATE";
3405        }
3406 
3407        /**
3408         * Get a description of a table's primary key columns. They are ordered by
3409         * COLUMN_NAME.
3410         * <P>
3411         * Each column description has the following columns:
3412         * <OL>
3413         * <li> <B>TABLE_CAT</B> String => table catalog (may be null) </li>
3414         * <li> <B>TABLE_SCHEM</B> String => table schema (may be null) </li>
3415         * <li> <B>TABLE_NAME</B> String => table name </li>
3416         * <li> <B>COLUMN_NAME</B> String => column name </li>
3417         * <li> <B>KEY_SEQ</B> short => sequence number within primary key </li>
3418         * <li> <B>PK_NAME</B> String => primary key name (may be null) </li>
3419         * </ol>
3420         * </p>
3421         * 
3422         * @param catalog
3423         *            a catalog name; "" retrieves those without a catalog
3424         * @param schema
3425         *            a schema name pattern; "" retrieves those without a schema
3426         * @param table
3427         *            a table name
3428         * @return ResultSet each row is a primary key column description
3429         * @throws SQLException
3430         *             DOCUMENT ME!
3431         */
3432        public java.sql.ResultSet getPrimaryKeys(String catalog, String schema,
3433                        final String table) throws SQLException {
3434                Field[] fields = new Field[6];
3435                fields[0] = new Field("", "TABLE_CAT", Types.CHAR, 255);
3436                fields[1] = new Field("", "TABLE_SCHEM", Types.CHAR, 0);
3437                fields[2] = new Field("", "TABLE_NAME", Types.CHAR, 255);
3438                fields[3] = new Field("", "COLUMN_NAME", Types.CHAR, 32);
3439                fields[4] = new Field("", "KEY_SEQ", Types.SMALLINT, 5);
3440                fields[5] = new Field("", "PK_NAME", Types.CHAR, 32);
3441 
3442                if (table == null) {
3443                        throw SQLError.createSQLException("Table not specified.",
3444                                        SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
3445                }
3446 
3447                final ArrayList rows = new ArrayList();
3448                final Statement stmt = this.conn.getMetadataSafeStatement();
3449 
3450                try {
3451 
3452                        new IterateBlock(getCatalogIterator(catalog)) {
3453                                void forEach(Object catalogStr) throws SQLException {
3454                                        ResultSet rs = null;
3455 
3456                                        try {
3457 
3458                                                StringBuffer queryBuf = new StringBuffer(
3459                                                                "SHOW KEYS FROM ");
3460                                                queryBuf.append(quotedId);
3461                                                queryBuf.append(table);
3462                                                queryBuf.append(quotedId);
3463                                                queryBuf.append(" FROM ");
3464                                                queryBuf.append(quotedId);
3465                                                queryBuf.append(catalogStr.toString());
3466                                                queryBuf.append(quotedId);
3467 
3468                                                rs = stmt.executeQuery(queryBuf.toString());
3469 
3470                                                ArrayList tuples = new ArrayList();
3471                                                TreeMap sortMap = new TreeMap();
3472 
3473                                                while (rs.next()) {
3474                                                        String keyType = rs.getString("Key_name");
3475 
3476                                                        if (keyType != null) {
3477                                                                if (keyType.equalsIgnoreCase("PRIMARY")
3478                                                                                || keyType.equalsIgnoreCase("PRI")) {
3479                                                                        byte[][] tuple = new byte[6][];
3480                                                                        tuple[0] = ((catalogStr.toString() == null) ? new byte[0]
3481                                                                                        : s2b(catalogStr.toString()));
3482                                                                        tuple[1] = null;
3483                                                                        tuple[2] = s2b(table);
3484 
3485                                                                        String columnName = rs
3486                                                                                        .getString("Column_name");
3487                                                                        tuple[3] = s2b(columnName);
3488                                                                        tuple[4] = s2b(rs.getString("Seq_in_index"));
3489                                                                        tuple[5] = s2b(keyType);
3490                                                                        sortMap.put(columnName, tuple);
3491                                                                }
3492                                                        }
3493                                                }
3494 
3495                                                // Now pull out in column name sorted order
3496                                                Iterator sortedIterator = sortMap.values().iterator();
3497 
3498                                                while (sortedIterator.hasNext()) {
3499                                                        rows.add(sortedIterator.next());
3500                                                }
3501 
3502                                        } finally {
3503                                                if (rs != null) {
3504                                                        try {
3505                                                                rs.close();
3506                                                        } catch (Exception ex) {
3507                                                                ;
3508                                                        }
3509 
3510                                                        rs = null;
3511                                                }
3512                                        }
3513                                }
3514                        }.doForAll();
3515                } finally {
3516                        if (stmt != null) {
3517                                stmt.close();
3518                        }
3519                }
3520 
3521                java.sql.ResultSet results = buildResultSet(fields, rows);
3522 
3523                return results;
3524        }
3525 
3526        /**
3527         * Get a description of a catalog's stored procedure parameters and result
3528         * columns.
3529         * <P>
3530         * Only descriptions matching the schema, procedure and parameter name
3531         * criteria are returned. They are ordered by PROCEDURE_SCHEM and
3532         * PROCEDURE_NAME. Within this, the return value, if any, is first. Next are
3533         * the parameter descriptions in call order. The column descriptions follow
3534         * in column number order.
3535         * </p>
3536         * <P>
3537         * Each row in the ResultSet is a parameter desription or column description
3538         * with the following fields:
3539         * <OL>
3540         * <li> <B>PROCEDURE_CAT</B> String => procedure catalog (may be null)
3541         * </li>
3542         * <li> <B>PROCEDURE_SCHEM</B> String => procedure schema (may be null)
3543         * </li>
3544         * <li> <B>PROCEDURE_NAME</B> String => procedure name </li>
3545         * <li> <B>COLUMN_NAME</B> String => column/parameter name </li>
3546         * <li> <B>COLUMN_TYPE</B> Short => kind of column/parameter:
3547         * <UL>
3548         * <li> procedureColumnUnknown - nobody knows </li>
3549         * <li> procedureColumnIn - IN parameter </li>
3550         * <li> procedureColumnInOut - INOUT parameter </li>
3551         * <li> procedureColumnOut - OUT parameter </li>
3552         * <li> procedureColumnReturn - procedure return value </li>
3553         * <li> procedureColumnResult - result column in ResultSet </li>
3554         * </ul>
3555         * </li>
3556         * <li> <B>DATA_TYPE</B> short => SQL type from java.sql.Types </li>
3557         * <li> <B>TYPE_NAME</B> String => SQL type name </li>
3558         * <li> <B>PRECISION</B> int => precision </li>
3559         * <li> <B>LENGTH</B> int => length in bytes of data </li>
3560         * <li> <B>SCALE</B> short => scale </li>
3561         * <li> <B>RADIX</B> short => radix </li>
3562         * <li> <B>NULLABLE</B> short => can it contain NULL?
3563         * <UL>
3564         * <li> procedureNoNulls - does not allow NULL values </li>
3565         * <li> procedureNullable - allows NULL values </li>
3566         * <li> procedureNullableUnknown - nullability unknown </li>
3567         * </ul>
3568         * </li>
3569         * <li> <B>REMARKS</B> String => comment describing parameter/column </li>
3570         * </ol>
3571         * </p>
3572         * <P>
3573         * <B>Note:</B> Some databases may not return the column descriptions for a
3574         * procedure. Additional columns beyond REMARKS can be defined by the
3575         * database.
3576         * </p>
3577         * 
3578         * @param catalog
3579         *            a catalog name; "" retrieves those without a catalog
3580         * @param schemaPattern
3581         *            a schema name pattern; "" retrieves those without a schema
3582         * @param procedureNamePattern
3583         *            a procedure name pattern
3584         * @param columnNamePattern
3585         *            a column name pattern
3586         * @return ResultSet each row is a stored procedure parameter or column
3587         *         description
3588         * @throws SQLException
3589         *             if a database access error occurs
3590         * @see #getSearchStringEscape
3591         */
3592        public java.sql.ResultSet getProcedureColumns(String catalog,
3593                        String schemaPattern, String procedureNamePattern,
3594                        String columnNamePattern) throws SQLException {
3595 
3596                Field[] fields = new Field[13];
3597 
3598                fields[0] = new Field("", "PROCEDURE_CAT", Types.CHAR, 0);
3599                fields[1] = new Field("", "PROCEDURE_SCHEM", Types.CHAR, 0);
3600                fields[2] = new Field("", "PROCEDURE_NAME", Types.CHAR, 0);
3601                fields[3] = new Field("", "COLUMN_NAME", Types.CHAR, 0);
3602                fields[4] = new Field("", "COLUMN_TYPE", Types.CHAR, 0);
3603                fields[5] = new Field("", "DATA_TYPE", Types.SMALLINT, 0);
3604                fields[6] = new Field("", "TYPE_NAME", Types.CHAR, 0);
3605                fields[7] = new Field("", "PRECISION", Types.INTEGER, 0);
3606                fields[8] = new Field("", "LENGTH", Types.INTEGER, 0);
3607                fields[9] = new Field("", "SCALE", Types.SMALLINT, 0);
3608                fields[10] = new Field("", "RADIX", Types.SMALLINT, 0);
3609                fields[11] = new Field("", "NULLABLE", Types.SMALLINT, 0);
3610                fields[12] = new Field("", "REMARKS", Types.CHAR, 0);
3611 
3612                List proceduresToExtractList = new ArrayList();
3613 
3614                if (supportsStoredProcedures()) {
3615                        if ((procedureNamePattern.indexOf("%") == -1)
3616                                        && (procedureNamePattern.indexOf("?") == -1)) {
3617                                proceduresToExtractList.add(procedureNamePattern);
3618                        } else {
3619                                PreparedStatement procedureNameStmt = null;
3620                                ResultSet procedureNameRs = null;
3621 
3622                                try {
3623 
3624                                        procedureNameRs = getProcedures(catalog, schemaPattern,
3625                                                        procedureNamePattern);
3626 
3627                                        while (procedureNameRs.next()) {
3628                                                proceduresToExtractList.add(procedureNameRs
3629                                                                .getString(3));
3630                                        }
3631 
3632                                        // Required to be sorted in name-order by JDBC spec,
3633                                        // in 'normal' case getProcedures takes care of this for us,
3634                                        // but if system tables are inaccessible, we need to sort...
3635                                        // so just do this to be safe...
3636                                        Collections.sort(proceduresToExtractList);
3637                                } finally {
3638                                        SQLException rethrowSqlEx = null;
3639 
3640                                        if (procedureNameRs != null) {
3641                                                try {
3642                                                        procedureNameRs.close();
3643                                                } catch (SQLException sqlEx) {
3644                                                        rethrowSqlEx = sqlEx;
3645                                                }
3646                                        }
3647 
3648                                        if (procedureNameStmt != null) {
3649                                                try {
3650                                                        procedureNameStmt.close();
3651                                                } catch (SQLException sqlEx) {
3652                                                        rethrowSqlEx = sqlEx;
3653                                                }
3654                                        }
3655 
3656                                        if (rethrowSqlEx != null) {
3657                                                throw rethrowSqlEx;
3658                                        }
3659                                }
3660                        }
3661                }
3662 
3663                ArrayList resultRows = new ArrayList();
3664 
3665                for (Iterator iter = proceduresToExtractList.iterator(); iter.hasNext();) {
3666                        String procName = (String) iter.next();
3667 
3668                        getCallStmtParameterTypes(catalog, procName, columnNamePattern,
3669                                        resultRows);
3670                }
3671 
3672                return buildResultSet(fields, resultRows);
3673        }
3674 
3675        /**
3676         * Get a description of stored procedures available in a catalog.
3677         * <P>
3678         * Only procedure descriptions matching the schema and procedure name
3679         * criteria are returned. They are ordered by PROCEDURE_SCHEM, and
3680         * PROCEDURE_NAME.
3681         * </p>
3682         * <P>
3683         * Each procedure description has the the following columns:
3684         * <OL>
3685         * <li> <B>PROCEDURE_CAT</B> String => procedure catalog (may be null)
3686         * </li>
3687         * <li> <B>PROCEDURE_SCHEM</B> String => procedure schema (may be null)
3688         * </li>
3689         * <li> <B>PROCEDURE_NAME</B> String => procedure name </li>
3690         * <li> reserved for future use </li>
3691         * <li> reserved for future use </li>
3692         * <li> reserved for future use </li>
3693         * <li> <B>REMARKS</B> String => explanatory comment on the procedure </li>
3694         * <li> <B>PROCEDURE_TYPE</B> short => kind of procedure:
3695         * <UL>
3696         * <li> procedureResultUnknown - May return a result </li>
3697         * <li> procedureNoResult - Does not return a result </li>
3698         * <li> procedureReturnsResult - Returns a result </li>
3699         * </ul>
3700         * </li>
3701         * </ol>
3702         * </p>
3703         * 
3704         * @param catalog
3705         *            a catalog name; "" retrieves those without a catalog
3706         * @param schemaPattern
3707         *            a schema name pattern; "" retrieves those without a schema
3708         * @param procedureNamePattern
3709         *            a procedure name pattern
3710         * @return ResultSet each row is a procedure description
3711         * @throws SQLException
3712         *             if a database access error occurs
3713         * @see #getSearchStringEscape
3714         */
3715        public java.sql.ResultSet getProcedures(String catalog,
3716                        String schemaPattern, String procedureNamePattern)
3717                        throws SQLException {
3718                if ((procedureNamePattern == null)
3719                                || (procedureNamePattern.length() == 0)) {
3720                        if (this.conn.getNullNamePatternMatchesAll()) {
3721                                procedureNamePattern = "%";
3722                        } else {
3723                                throw SQLError.createSQLException(
3724                                                "Procedure name pattern can not be NULL or empty.",
3725                                                SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
3726                        }
3727                }
3728 
3729                Field[] fields = new Field[8];
3730                fields[0] = new Field("", "PROCEDURE_CAT", Types.CHAR, 0);
3731                fields[1] = new Field("", "PROCEDURE_SCHEM", Types.CHAR, 0);
3732                fields[2] = new Field("", "PROCEDURE_NAME", Types.CHAR, 0);
3733                fields[3] = new Field("", "reserved1", Types.CHAR, 0);
3734                fields[4] = new Field("", "reserved2", Types.CHAR, 0);
3735                fields[5] = new Field("", "reserved3", Types.CHAR, 0);
3736                fields[6] = new Field("", "REMARKS", Types.CHAR, 0);
3737                fields[7] = new Field("", "PROCEDURE_TYPE", Types.SMALLINT, 0);
3738 
3739                final ArrayList procedureRows = new ArrayList();
3740 
3741                if (supportsStoredProcedures()) {
3742                        final String procNamePattern = procedureNamePattern;
3743 
3744                        final Map procedureRowsOrderedByName = new TreeMap();
3745 
3746                        new IterateBlock(getCatalogIterator(catalog)) {
3747                                void forEach(Object catalogStr) throws SQLException {
3748                                        String db = catalogStr.toString();
3749 
3750                                        boolean fromSelect = false;
3751                                        ResultSet proceduresRs = null;
3752                                        boolean needsClientFiltering = true;
3753                                        PreparedStatement proceduresStmt = conn
3754                                                        .clientPrepareStatement("SELECT name, type FROM mysql.proc WHERE name like ? and db <=> ? ORDER BY name");
3755 
3756                                        try {
3757                                                //
3758                                                // Try using system tables first, as this is a little
3759                                                // bit more efficient....
3760                                                //
3761 
3762                                                boolean hasTypeColumn = false;
3763 
3764                                                if (db != null) {
3765                                                        proceduresStmt.setString(2, db);
3766                                                } else {
3767                                                        proceduresStmt.setNull(2, Types.VARCHAR);
3768                                                }
3769 
3770                                                int nameIndex = 1;
3771 
3772                                                if (proceduresStmt.getMaxRows() != 0) {
3773                                                        proceduresStmt.setMaxRows(0);
3774                                                }
3775 
3776                                                proceduresStmt.setString(1, procNamePattern);
3777 
3778                                                try {
3779                                                        proceduresRs = proceduresStmt.executeQuery();
3780                                                        fromSelect = true;
3781                                                        needsClientFiltering = false;
3782                                                        hasTypeColumn = true;
3783                                                } catch (SQLException sqlEx) {
3784 
3785                                                        //
3786                                                        // Okay, system tables aren't accessible, so use
3787                                                        // 'SHOW
3788                                                        // ....'....
3789                                                        //
3790                                                        proceduresStmt.close();
3791 
3792                                                        fromSelect = false;
3793 
3794                                                        if (conn.versionMeetsMinimum(5, 0, 1)) {
3795                                                                nameIndex = 2;
3796                                                        } else {
3797                                                                nameIndex = 1;
3798                                                        }
3799 
3800                                                        proceduresStmt = conn
3801                                                                        .clientPrepareStatement("SHOW PROCEDURE STATUS LIKE ?");
3802 
3803                                                        if (proceduresStmt.getMaxRows() != 0) {
3804                                                                proceduresStmt.setMaxRows(0);
3805                                                        }
3806 
3807                                                        proceduresStmt.setString(1, procNamePattern);
3808 
3809                                                        proceduresRs = proceduresStmt.executeQuery();
3810                                                }
3811 
3812                                                convertToJdbcProcedureList(fromSelect, db,
3813                                                                proceduresRs, needsClientFiltering, db,
3814                                                                procedureRowsOrderedByName, nameIndex);
3815 
3816                                                if (!hasTypeColumn) {
3817                                                        // need to go after functions too...
3818                                                        if (proceduresStmt != null) {
3819                                                                proceduresStmt.close();
3820                                                        }
3821 
3822                                                        proceduresStmt = conn
3823                                                                        .clientPrepareStatement("SHOW FUNCTION STATUS LIKE ?");
3824 
3825                                                        if (proceduresStmt.getMaxRows() != 0) {
3826                                                                proceduresStmt.setMaxRows(0);
3827                                                        }
3828 
3829                                                        proceduresStmt.setString(1, procNamePattern);
3830 
3831                                                        proceduresRs = proceduresStmt.executeQuery();
3832 
3833                                                        convertToJdbcFunctionList(db, proceduresRs,
3834                                                                        needsClientFiltering, db,
3835                                                                        procedureRowsOrderedByName, nameIndex);
3836 
3837                                                }
3838 
3839                                                // Now, sort them
3840 
3841                                                Iterator proceduresIter = procedureRowsOrderedByName
3842                                                                .values().iterator();
3843 
3844                                                while (proceduresIter.hasNext()) {
3845                                                        procedureRows.add(proceduresIter.next());
3846                                                }
3847                                        } finally {
3848                                                SQLException rethrowSqlEx = null;
3849 
3850                                                if (proceduresRs != null) {
3851                                                        try {
3852                                                                proceduresRs.close();
3853                                                        } catch (SQLException sqlEx) {
3854                                                                rethrowSqlEx = sqlEx;
3855                                                        }
3856                                                }
3857 
3858                                                if (proceduresStmt != null) {
3859                                                        try {
3860                                                                proceduresStmt.close();
3861                                                        } catch (SQLException sqlEx) {
3862                                                                rethrowSqlEx = sqlEx;
3863                                                        }
3864                                                }
3865 
3866                                                if (rethrowSqlEx != null) {
3867                                                        throw rethrowSqlEx;
3868                                                }
3869                                        }
3870                                }
3871                        }.doForAll();
3872                }
3873 
3874                return buildResultSet(fields, procedureRows);
3875        }
3876 
3877        /**
3878         * What's the database vendor's preferred term for "procedure"?
3879         * 
3880         * @return the vendor term
3881         * @throws SQLException
3882         *             if an error occurs (don't know why it would in this case...)
3883         */
3884        public String getProcedureTerm() throws SQLException {
3885                return "PROCEDURE";
3886        }
3887 
3888        /**
3889         * @see DatabaseMetaData#getResultSetHoldability()
3890         */
3891        public int getResultSetHoldability() throws SQLException {
3892                return ResultSet.HOLD_CURSORS_OVER_COMMIT;
3893        }
3894 
3895        private void getResultsImpl(String catalog, String table,
3896                        String keysComment, List tuples, String fkTableName,
3897                        boolean isExport) throws SQLException {
3898 
3899                LocalAndReferencedColumns parsedInfo = parseTableStatusIntoLocalAndReferencedColumns(keysComment);
3900 
3901                if (isExport && !parsedInfo.referencedTable.equals(table)) {
3902                        return;
3903                }
3904 
3905                if (parsedInfo.localColumnsList.size() != parsedInfo.referencedColumnsList
3906                                .size()) {
3907                        throw SQLError.createSQLException(
3908                                        "Error parsing foreign keys definition,"
3909                                                        + "number of local and referenced columns is not the same.",
3910                                        SQLError.SQL_STATE_GENERAL_ERROR);
3911                }
3912 
3913                Iterator localColumnNames = parsedInfo.localColumnsList.iterator();
3914                Iterator referColumnNames = parsedInfo.referencedColumnsList.iterator();
3915 
3916                int keySeqIndex = 1;
3917 
3918                while (localColumnNames.hasNext()) {
3919                        byte[][] tuple = new byte[14][];
3920                        String lColumnName = removeQuotedId(localColumnNames.next()
3921                                        .toString());
3922                        String rColumnName = removeQuotedId(referColumnNames.next()
3923                                        .toString());
3924                        tuple[FKTABLE_CAT] = ((catalog == null) ? new byte[0]
3925                                        : s2b(catalog));
3926                        tuple[FKTABLE_SCHEM] = null;
3927                        tuple[FKTABLE_NAME] = s2b((isExport) ? fkTableName : table);
3928                        tuple[FKCOLUMN_NAME] = s2b(lColumnName);
3929                        tuple[PKTABLE_CAT] = s2b(parsedInfo.referencedCatalog);
3930                        tuple[PKTABLE_SCHEM] = null;
3931                        tuple[PKTABLE_NAME] = s2b((isExport) ? table
3932                                        : parsedInfo.referencedTable);
3933                        tuple[PKCOLUMN_NAME] = s2b(rColumnName);
3934                        tuple[KEY_SEQ] = s2b(Integer.toString(keySeqIndex++));
3935 
3936                        int[] actions = getForeignKeyActions(keysComment);
3937 
3938                        tuple[UPDATE_RULE] = s2b(Integer.toString(actions[1]));
3939                        tuple[DELETE_RULE] = s2b(Integer.toString(actions[0]));
3940                        tuple[FK_NAME] = s2b(parsedInfo.constraintName);
3941                        tuple[PK_NAME] = null; // not available from show table status
3942                        tuple[DEFERRABILITY] = s2b(Integer
3943                                        .toString(java.sql.DatabaseMetaData.importedKeyNotDeferrable));
3944                        tuples.add(tuple);
3945                }
3946        }
3947 
3948        /**
3949         * Get the schema names available in this database. The results are ordered
3950         * by schema name.
3951         * <P>
3952         * The schema column is:
3953         * <OL>
3954         * <li> <B>TABLE_SCHEM</B> String => schema name </li>
3955         * </ol>
3956         * </p>
3957         * 
3958         * @return ResultSet each row has a single String column that is a schema
3959         *         name
3960         * @throws SQLException
3961         *             DOCUMENT ME!
3962         */
3963        public java.sql.ResultSet getSchemas() throws SQLException {
3964                Field[] fields = new Field[1];
3965                fields[0] = new Field("", "TABLE_SCHEM", java.sql.Types.CHAR, 0);
3966 
3967                ArrayList tuples = new ArrayList();
3968                java.sql.ResultSet results = buildResultSet(fields, tuples);
3969 
3970                return results;
3971        }
3972 
3973        /**
3974         * What's the database vendor's preferred term for "schema"?
3975         * 
3976         * @return the vendor term
3977         * @throws SQLException
3978         *             DOCUMENT ME!
3979         */
3980        public String getSchemaTerm() throws SQLException {
3981                return "";
3982        }
3983 
3984        /**
3985         * This is the string that can be used to escape '_' or '%' in the string
3986         * pattern style catalog search parameters.
3987         * <P>
3988         * The '_' character represents any single character.
3989         * </p>
3990         * <P>
3991         * The '%' character represents any sequence of zero or more characters.
3992         * </p>
3993         * 
3994         * @return the string used to escape wildcard characters
3995         * @throws SQLException
3996         *             DOCUMENT ME!
3997         */
3998        public String getSearchStringEscape() throws SQLException {
3999                return "\\";
4000        }
4001 
4002        /**
4003         * Get a comma separated list of all a database's SQL keywords that are NOT
4004         * also SQL92 keywords.
4005         * 
4006         * @return the list
4007         * @throws SQLException
4008         *             DOCUMENT ME!
4009         */
4010        public String getSQLKeywords() throws SQLException {
4011                return "AUTO_INCREMENT,BINARY,BLOB,ENUM,INFILE,LOAD,MEDIUMINT,OPTION,OUTFILE,REPLACE,SET,TEXT,UNSIGNED,ZEROFILL";
4012 
4013                /*
4014                 * [20:44] root@test> select GROUP_CONCAT(reserved.a) from reserved left
4015                 * join sql92 on (reserved.a=sql92.a) where sql92.a IS NULL GROUP BY
4016                 * (reserved.b) \G ************************** 1. row
4017                 * *************************** GROUP_CONCAT(reserved.a):
4018                 * RETURN,REQUIRE,REPLACE,REPEAT,RENAME,
4019                 * REGEXP,PURGE,SPECIFIC,SPATIAL,SONAME,SHOW,SEPARATOR,SENSITIVE,
4020                 * SECOND_MICROSECOND,RLIKE,MOD,MINUTE_SECOND,MINUTE_MICROSECOND,
4021                 * MIDDLEINT,MEDIUMTEXT,MEDIUMINT,MEDIUMBLOB,MASTER_SERVER_ID,
4022                 * LOW_PRIORITY,LOOP,LONGTEXT,OUTFILE,OUT,OPTIONALLY,OPTIMIZE,
4023                 * NO_WRITE_TO_BINLOG,LONGBLOB,ZEROFILL,UTC_DATE,USER_RESOURCES,USE,
4024                 * UNSIGNED,UNLOCK,UNDO,UTC_TIME,UTC_TIMESTAMP,YEAR_MONTH,XOR,WHILE,
4025                 * VARCHARACTER,VARBINARY,TINYTEXT,SQL_T
4026                 */
4027        }
4028 
4029        /**
4030         * @see DatabaseMetaData#getSQLStateType()
4031         */
4032        public int getSQLStateType() throws SQLException {
4033                if (this.conn.versionMeetsMinimum(4, 1, 0)) {
4034                        return DatabaseMetaData.sqlStateSQL99;
4035                }
4036 
4037                if (this.conn.getUseSqlStateCodes()) {
4038                        return DatabaseMetaData.sqlStateSQL99;
4039                }
4040 
4041                return DatabaseMetaData.sqlStateXOpen;
4042        }
4043 
4044        /**
4045         * Get a comma separated list of string functions.
4046         * 
4047         * @return the list
4048         * @throws SQLException
4049         *             DOCUMENT ME!
4050         */
4051        public String getStringFunctions() throws SQLException {
4052                return "ASCII,BIN,BIT_LENGTH,CHAR,CHARACTER_LENGTH,CHAR_LENGTH,CONCAT,"
4053                                + "CONCAT_WS,CONV,ELT,EXPORT_SET,FIELD,FIND_IN_SET,HEX,INSERT,"
4054                                + "INSTR,LCASE,LEFT,LENGTH,LOAD_FILE,LOCATE,LOCATE,LOWER,LPAD,"
4055                                + "LTRIM,MAKE_SET,MATCH,MID,OCT,OCTET_LENGTH,ORD,POSITION,"
4056                                + "QUOTE,REPEAT,REPLACE,REVERSE,RIGHT,RPAD,RTRIM,SOUNDEX,"
4057                                + "SPACE,STRCMP,SUBSTRING,SUBSTRING,SUBSTRING,SUBSTRING,"
4058                                + "SUBSTRING_INDEX,TRIM,UCASE,UPPER";
4059        }
4060 
4061        /**
4062         * @see DatabaseMetaData#getSuperTables(String, String, String)
4063         */
4064        public java.sql.ResultSet getSuperTables(String arg0, String arg1,
4065                        String arg2) throws SQLException {
4066                Field[] fields = new Field[4];
4067                fields[0] = new Field("", "TABLE_CAT", Types.CHAR, 32);
4068                fields[1] = new Field("", "TABLE_SCHEM", Types.CHAR, 32);
4069                fields[2] = new Field("", "TABLE_NAME", Types.CHAR, 32);
4070                fields[3] = new Field("", "SUPERTABLE_NAME", Types.CHAR, 32);
4071 
4072                return buildResultSet(fields, new ArrayList());
4073        }
4074 
4075        /**
4076         * @see DatabaseMetaData#getSuperTypes(String, String, String)
4077         */
4078        public java.sql.ResultSet getSuperTypes(String arg0, String arg1,
4079                        String arg2) throws SQLException {
4080                Field[] fields = new Field[6];
4081                fields[0] = new Field("", "TABLE_CAT", Types.CHAR, 32);
4082                fields[1] = new Field("", "TABLE_SCHEM", Types.CHAR, 32);
4083                fields[2] = new Field("", "TYPE_NAME", Types.CHAR, 32);
4084                fields[3] = new Field("", "SUPERTYPE_CAT", Types.CHAR, 32);
4085                fields[4] = new Field("", "SUPERTYPE_SCHEM", Types.CHAR, 32);
4086                fields[5] = new Field("", "SUPERTYPE_NAME", Types.CHAR, 32);
4087 
4088                return buildResultSet(fields, new ArrayList());
4089        }
4090 
4091        /**
4092         * Get a comma separated list of system functions.
4093         * 
4094         * @return the list
4095         * @throws SQLException
4096         *             DOCUMENT ME!
4097         */
4098        public String getSystemFunctions() throws SQLException {
4099                return "DATABASE,USER,SYSTEM_USER,SESSION_USER,PASSWORD,ENCRYPT,LAST_INSERT_ID,VERSION";
4100        }
4101 
4102        private String getTableNameWithCase(String table) {
4103                String tableNameWithCase = (this.conn.lowerCaseTableNames() ? table
4104                                .toLowerCase() : table);
4105 
4106                return tableNameWithCase;
4107        }
4108 
4109        /**
4110         * Get a description of the access rights for each table available in a
4111         * catalog.
4112         * <P>
4113         * Only privileges matching the schema and table name criteria are returned.
4114         * They are ordered by TABLE_SCHEM, TABLE_NAME, and PRIVILEGE.
4115         * </p>
4116         * <P>
4117         * Each privilige description has the following columns:
4118         * <OL>
4119         * <li> <B>TABLE_CAT</B> String => table catalog (may be null) </li>
4120         * <li> <B>TABLE_SCHEM</B> String => table schema (may be null) </li>
4121         * <li> <B>TABLE_NAME</B> String => table name </li>
4122         * <li> <B>COLUMN_NAME</B> String => column name </li>
4123         * <li> <B>GRANTOR</B> => grantor of access (may be null) </li>
4124         * <li> <B>GRANTEE</B> String => grantee of access </li>
4125         * <li> <B>PRIVILEGE</B> String => name of access (SELECT, INSERT, UPDATE,
4126         * REFRENCES, ...) </li>
4127         * <li> <B>IS_GRANTABLE</B> String => "YES" if grantee is permitted to
4128         * grant to others; "NO" if not; null if unknown </li>
4129         * </ol>
4130         * </p>
4131         * 
4132         * @param catalog
4133         *            a catalog name; "" retrieves those without a catalog
4134         * @param schemaPattern
4135         *            a schema name pattern; "" retrieves those without a schema
4136         * @param tableNamePattern
4137         *            a table name pattern
4138         * @return ResultSet each row is a table privilege description
4139         * @throws SQLException
4140         *             if a database access error occurs
4141         * @see #getSearchStringEscape
4142         */
4143        public java.sql.ResultSet getTablePrivileges(String catalog,
4144                        String schemaPattern, String tableNamePattern) throws SQLException {
4145 
4146                if (tableNamePattern == null) {
4147                        if (this.conn.getNullNamePatternMatchesAll()) {
4148                                tableNamePattern = "%";
4149                        } else {
4150                                throw SQLError.createSQLException(
4151                                                "Table name pattern can not be NULL or empty.",
4152                                                SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
4153                        }
4154                }
4155 
4156                Field[] fields = new Field[7];
4157                fields[0] = new Field("", "TABLE_CAT", Types.CHAR, 64);
4158                fields[1] = new Field("", "TABLE_SCHEM", Types.CHAR, 1);
4159                fields[2] = new Field("", "TABLE_NAME", Types.CHAR, 64);
4160                fields[3] = new Field("", "GRANTOR", Types.CHAR, 77);
4161                fields[4] = new Field("", "GRANTEE", Types.CHAR, 77);
4162                fields[5] = new Field("", "PRIVILEGE", Types.CHAR, 64);
4163                fields[6] = new Field("", "IS_GRANTABLE", Types.CHAR, 3);
4164 
4165                StringBuffer grantQuery = new StringBuffer(
4166                                "SELECT host,db,table_name,grantor,user,table_priv from mysql.tables_priv ");
4167                grantQuery.append(" WHERE ");
4168 
4169                if ((catalog != null) && (catalog.length() != 0)) {
4170                        grantQuery.append(" db='");
4171                        grantQuery.append(catalog);
4172                        grantQuery.append("' AND ");
4173                }
4174 
4175                grantQuery.append("table_name like '");
4176                grantQuery.append(tableNamePattern);
4177                grantQuery.append("'");
4178 
4179                ResultSet results = null;
4180                ArrayList grantRows = new ArrayList();
4181                Statement stmt = null;
4182 
4183                try {
4184                        stmt = this.conn.createStatement();
4185                        stmt.setEscapeProcessing(false);
4186 
4187                        results = stmt.executeQuery(grantQuery.toString());
4188 
4189                        while (results.next()) {
4190                                String host = results.getString(1);
4191                                String db = results.getString(2);
4192                                String table = results.getString(3);
4193                                String grantor = results.getString(4);
4194                                String user = results.getString(5);
4195 
4196                                if ((user == null) || (user.length() == 0)) {
4197                                        user = "%";
4198                                }
4199 
4200                                StringBuffer fullUser = new StringBuffer(user);
4201 
4202                                if ((host != null) && this.conn.getUseHostsInPrivileges()) {
4203                                        fullUser.append("@");
4204                                        fullUser.append(host);
4205                                }
4206 
4207                                String allPrivileges = results.getString(6);
4208 
4209                                if (allPrivileges != null) {
4210                                        allPrivileges = allPrivileges.toUpperCase(Locale.ENGLISH);
4211 
4212                                        StringTokenizer st = new StringTokenizer(allPrivileges, ",");
4213 
4214                                        while (st.hasMoreTokens()) {
4215                                                String privilege = st.nextToken().trim();
4216 
4217                                                // Loop through every column in the table
4218                                                java.sql.ResultSet columnResults = null;
4219 
4220                                                try {
4221                                                        columnResults = getColumns(catalog, schemaPattern,
4222                                                                        table, "%");
4223 
4224                                                        while (columnResults.next()) {
4225                                                                byte[][] tuple = new byte[8][];
4226                                                                tuple[0] = s2b(db);
4227                                                                tuple[1] = null;
4228                                                                tuple[2] = s2b(table);
4229 
4230                                                                if (grantor != null) {
4231                                                                        tuple[3] = s2b(grantor);
4232                                                                } else {
4233                                                                        tuple[3] = null;
4234                                                                }
4235 
4236                                                                tuple[4] = s2b(fullUser.toString());
4237                                                                tuple[5] = s2b(privilege);
4238                                                                tuple[6] = null;
4239                                                                grantRows.add(tuple);
4240                                                        }
4241                                                } finally {
4242                                                        if (columnResults != null) {
4243                                                                try {
4244                                                                        columnResults.close();
4245                                                                } catch (Exception ex) {
4246                                                                        ;
4247                                                                }
4248                                                        }
4249                                                }
4250                                        }
4251                                }
4252                        }
4253                } finally {
4254                        if (results != null) {
4255                                try {
4256                                        results.close();
4257                                } catch (Exception ex) {
4258                                        ;
4259                                }
4260 
4261                                results = null;
4262                        }
4263 
4264                        if (stmt != null) {
4265                                try {
4266                                        stmt.close();
4267                                } catch (Exception ex) {
4268                                        ;
4269                                }
4270 
4271                                stmt = null;
4272                        }
4273                }
4274 
4275                return buildResultSet(fields, grantRows);
4276        }
4277 
4278        /**
4279         * Get a description of tables available in a catalog.
4280         * <P>
4281         * Only table descriptions matching the catalog, schema, table name and type
4282         * criteria are returned. They are ordered by TABLE_TYPE, TABLE_SCHEM and
4283         * TABLE_NAME.
4284         * </p>
4285         * <P>
4286         * Each table description has the following columns:
4287         * <OL>
4288         * <li> <B>TABLE_CAT</B> String => table catalog (may be null) </li>
4289         * <li> <B>TABLE_SCHEM</B> String => table schema (may be null) </li>
4290         * <li> <B>TABLE_NAME</B> String => table name </li>
4291         * <li> <B>TABLE_TYPE</B> String => table type. Typical types are "TABLE",
4292         * "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", "LOCAL TEMPORARY", "ALIAS",
4293         * "SYNONYM". </li>
4294         * <li> <B>REMARKS</B> String => explanatory comment on the table </li>
4295         * </ol>
4296         * </p>
4297         * <P>
4298         * <B>Note:</B> Some databases may not return information for all tables.
4299         * </p>
4300         * 
4301         * @param catalog
4302         *            a catalog name; "" retrieves those without a catalog
4303         * @param schemaPattern
4304         *            a schema name pattern; "" retrieves those without a schema
4305         * @param tableNamePattern
4306         *            a table name pattern
4307         * @param types
4308         *            a list of table types to include; null returns all types
4309         * @return ResultSet each row is a table description
4310         * @throws SQLException
4311         *             DOCUMENT ME!
4312         * @see #getSearchStringEscape
4313         */
4314        public java.sql.ResultSet getTables(String catalog, String schemaPattern,
4315                        String tableNamePattern, final String[] types) throws SQLException {
4316 
4317                if (tableNamePattern == null) {
4318                        if (this.conn.getNullNamePatternMatchesAll()) {
4319                                tableNamePattern = "%";
4320                        } else {
4321                                throw SQLError.createSQLException(
4322                                                "Table name pattern can not be NULL or empty.",
4323                                                SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
4324                        }
4325                }
4326 
4327                Field[] fields = new Field[5];
4328                fields[0] = new Field("", "TABLE_CAT", java.sql.Types.VARCHAR, 255);
4329                fields[1] = new Field("", "TABLE_SCHEM", java.sql.Types.VARCHAR, 0);
4330                fields[2] = new Field("", "TABLE_NAME", java.sql.Types.VARCHAR, 255);
4331                fields[3] = new Field("", "TABLE_TYPE", java.sql.Types.VARCHAR, 5);
4332                fields[4] = new Field("", "REMARKS", java.sql.Types.VARCHAR, 0);
4333 
4334                final ArrayList tuples = new ArrayList();
4335 
4336                final Statement stmt = this.conn.getMetadataSafeStatement();
4337 
4338                final String tableNamePat = tableNamePattern;
4339 
4340                try {
4341 
4342                        new IterateBlock(getCatalogIterator(catalog)) {
4343                                void forEach(Object catalogStr) throws SQLException {
4344                                        ResultSet results = null;
4345 
4346                                        try {
4347 
4348                                                if (!conn.versionMeetsMinimum(5, 0, 2)) {
4349                                                        results = stmt
4350                                                                        .executeQuery("SHOW TABLES FROM "
4351                                                                                        + quotedId + catalogStr.toString()
4352                                                                                        + quotedId + " LIKE '"
4353                                                                                        + tableNamePat + "'");
4354                                                } else {
4355                                                        results = stmt
4356                                                                        .executeQuery("SHOW FULL TABLES FROM "
4357                                                                                        + quotedId + catalogStr.toString()
4358                                                                                        + quotedId + " LIKE '"
4359                                                                                        + tableNamePat + "'");
4360                                                }
4361 
4362                                                boolean shouldReportTables = false;
4363                                                boolean shouldReportViews = false;
4364 
4365                                                if (types == null || types.length == 0) {
4366                                                        shouldReportTables = true;
4367                                                        shouldReportViews = true;
4368                                                } else {
4369                                                        for (int i = 0; i < types.length; i++) {
4370                                                                if ("TABLE".equalsIgnoreCase(types[i])) {
4371                                                                        shouldReportTables = true;
4372                                                                }
4373 
4374                                                                if ("VIEW".equalsIgnoreCase(types[i])) {
4375                                                                        shouldReportViews = true;
4376                                                                }
4377                                                        }
4378                                                }
4379 
4380                                                int typeColumnIndex = 0;
4381                                                boolean hasTableTypes = false;
4382 
4383                                                if (conn.versionMeetsMinimum(5, 0, 2)) {
4384                                                        try {
4385                                                                // Both column names have been in use in the
4386                                                                // source tree
4387                                                                // so far....
4388                                                                typeColumnIndex = results
4389                                                                                .findColumn("table_type");
4390                                                                hasTableTypes = true;
4391                                                        } catch (SQLException sqlEx) {
4392 
4393                                                                // We should probably check SQLState here, but
4394                                                                // that
4395                                                                // can change depending on the server version
4396                                                                // and
4397                                                                // user properties, however, we'll get a 'true'
4398                                                                // SQLException when we actually try to find the
4399                                                                // 'Type' column
4400                                                                // 
4401                                                                try {
4402                                                                        typeColumnIndex = results
4403                                                                                        .findColumn("Type");
4404                                                                        hasTableTypes = true;
4405                                                                } catch (SQLException sqlEx2) {
4406                                                                        hasTableTypes = false;
4407                                                                }
4408                                                        }
4409                                                }
4410 
4411                                                TreeMap tablesOrderedByName = null;
4412                                                TreeMap viewsOrderedByName = null;
4413 
4414                                                while (results.next()) {
4415                                                        byte[][] row = new byte[5][];
4416                                                        row[0] = (catalogStr.toString() == null) ? null
4417                                                                        : s2b(catalogStr.toString());
4418                                                        row[1] = null;
4419                                                        row[2] = results.getBytes(1);
4420                                                        row[4] = new byte[0];
4421 
4422                                                        if (hasTableTypes) {
4423                                                                String tableType = results
4424                                                                                .getString(typeColumnIndex);
4425 
4426                                                                if (("table".equalsIgnoreCase(tableType) || "base table"
4427                                                                                .equalsIgnoreCase(tableType))
4428                                                                                && shouldReportTables) {
4429                                                                        row[3] = TABLE_AS_BYTES;
4430 
4431                                                                        if (tablesOrderedByName == null) {
4432                                                                                tablesOrderedByName = new TreeMap();
4433                                                                        }
4434 
4435                                                                        tablesOrderedByName.put(results
4436                                                                                        .getString(1), row);
4437                                                                } else if ("view".equalsIgnoreCase(tableType)
4438                                                                                && shouldReportViews) {
4439                                                                        row[3] = VIEW_AS_BYTES;
4440 
4441                                                                        if (viewsOrderedByName == null) {
4442                                                                                viewsOrderedByName = new TreeMap();
4443                                                                        }
4444 
4445                                                                        viewsOrderedByName.put(
4446                                                                                        results.getString(1), row);
4447                                                                } else if (!hasTableTypes) {
4448                                                                        // punt?
4449                                                                        row[3] = TABLE_AS_BYTES;
4450 
4451                                                                        if (tablesOrderedByName == null) {
4452                                                                                tablesOrderedByName = new TreeMap();
4453                                                                        }
4454 
4455                                                                        tablesOrderedByName.put(results
4456                                                                                        .getString(1), row);
4457                                                                }
4458                                                        } else {
4459                                                                if (shouldReportTables) {
4460                                                                        // Pre-MySQL-5.0.1, tables only
4461                                                                        row[3] = TABLE_AS_BYTES;
4462 
4463                                                                        if (tablesOrderedByName == null) {
4464                                                                                tablesOrderedByName = new TreeMap();
4465                                                                        }
4466 
4467                                                                        tablesOrderedByName.put(results
4468                                                                                        .getString(1), row);
4469                                                                }
4470                                                        }
4471                                                }
4472 
4473                                                // They are ordered by TABLE_TYPE,
4474                                                // * TABLE_SCHEM and TABLE_NAME.
4475 
4476                                                if (tablesOrderedByName != null) {
4477                                                        Iterator tablesIter = tablesOrderedByName.values()
4478                                                                        .iterator();
4479 
4480                                                        while (tablesIter.hasNext()) {
4481                                                                tuples.add(tablesIter.next());
4482                                                        }
4483                                                }
4484 
4485                                                if (viewsOrderedByName != null) {
4486                                                        Iterator viewsIter = viewsOrderedByName.values()
4487                                                                        .iterator();
4488 
4489                                                        while (viewsIter.hasNext()) {
4490                                                                tuples.add(viewsIter.next());
4491                                                        }
4492                                                }
4493 
4494                                        } finally {
4495                                                if (results != null) {
4496                                                        try {
4497                                                                results.close();
4498                                                        } catch (Exception ex) {
4499                                                                ;
4500                                                        }
4501 
4502                                                        results = null;
4503                                                }
4504 
4505                                        }
4506                                }
4507                        }.doForAll();
4508                } finally {
4509                        if (stmt != null) {
4510                                stmt.close();
4511                        }
4512                }
4513 
4514                java.sql.ResultSet tables = buildResultSet(fields, tuples);
4515 
4516                return tables;
4517        }
4518 
4519        /**
4520         * Get the table types available in this database. The results are ordered
4521         * by table type.
4522         * <P>
4523         * The table type is:
4524         * <OL>
4525         * <li> <B>TABLE_TYPE</B> String => table type. Typical types are "TABLE",
4526         * "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", "LOCAL TEMPORARY", "ALIAS",
4527         * "SYNONYM". </li>
4528         * </ol>
4529         * </p>
4530         * 
4531         * @return ResultSet each row has a single String column that is a table
4532         *         type
4533         * @throws SQLException
4534         *             DOCUMENT ME!
4535         */
4536        public java.sql.ResultSet getTableTypes() throws SQLException {
4537                ArrayList tuples = new ArrayList();
4538                Field[] fields = new Field[1];
4539                fields[0] = new Field("", "TABLE_TYPE", Types.VARCHAR, 5);
4540 
4541                byte[][] tableTypeRow = new byte[1][];
4542                tableTypeRow[0] = TABLE_AS_BYTES;
4543                tuples.add(tableTypeRow);
4544 
4545                if (this.conn.versionMeetsMinimum(5, 0, 1)) {
4546                        byte[][] viewTypeRow = new byte[1][];
4547                        viewTypeRow[0] = VIEW_AS_BYTES;
4548                        tuples.add(viewTypeRow);
4549                }
4550 
4551                byte[][] tempTypeRow = new byte[1][];
4552                tempTypeRow[0] = s2b("LOCAL TEMPORARY");
4553                tuples.add(tempTypeRow);
4554 
4555                return buildResultSet(fields, tuples);
4556        }
4557 
4558        /**
4559         * Get a comma separated list of time and date functions.
4560         * 
4561         * @return the list
4562         * @throws SQLException
4563         *             DOCUMENT ME!
4564         */
4565        public String getTimeDateFunctions() throws SQLException {
4566                return "DAYOFWEEK,WEEKDAY,DAYOFMONTH,DAYOFYEAR,MONTH,DAYNAME,"
4567                                + "MONTHNAME,QUARTER,WEEK,YEAR,HOUR,MINUTE,SECOND,PERIOD_ADD,"
4568                                + "PERIOD_DIFF,TO_DAYS,FROM_DAYS,DATE_FORMAT,TIME_FORMAT,"
4569                                + "CURDATE,CURRENT_DATE,CURTIME,CURRENT_TIME,NOW,SYSDATE,"
4570                                + "CURRENT_TIMESTAMP,UNIX_TIMESTAMP,FROM_UNIXTIME,"
4571                                + "SEC_TO_TIME,TIME_TO_SEC";
4572        }
4573 
4574        /**
4575         * Get a description of all the standard SQL types supported by this
4576         * database. They are ordered by DATA_TYPE and then by how closely the data
4577         * type maps to the corresponding JDBC SQL type.
4578         * <P>
4579         * Each type description has the following columns:
4580         * <OL>
4581         * <li> <B>TYPE_NAME</B> String => Type name </li>
4582         * <li> <B>DATA_TYPE</B> short => SQL data type from java.sql.Types </li>
4583         * <li> <B>PRECISION</B> int => maximum precision </li>
4584         * <li> <B>LITERAL_PREFIX</B> String => prefix used to quote a literal (may
4585         * be null) </li>
4586         * <li> <B>LITERAL_SUFFIX</B> String => suffix used to quote a literal (may
4587         * be null) </li>
4588         * <li> <B>CREATE_PARAMS</B> String => parameters used in creating the type
4589         * (may be null) </li>
4590         * <li> <B>NULLABLE</B> short => can you use NULL for this type?
4591         * <UL>
4592         * <li> typeNoNulls - does not allow NULL values </li>
4593         * <li> typeNullable - allows NULL values </li>
4594         * <li> typeNullableUnknown - nullability unknown </li>
4595         * </ul>
4596         * </li>
4597         * <li> <B>CASE_SENSITIVE</B> boolean=> is it case sensitive? </li>
4598         * <li> <B>SEARCHABLE</B> short => can you use "WHERE" based on this type:
4599         * <UL>
4600         * <li> typePredNone - No support </li>
4601         * <li> typePredChar - Only supported with WHERE .. LIKE </li>
4602         * <li> typePredBasic - Supported except for WHERE .. LIKE </li>
4603         * <li> typeSearchable - Supported for all WHERE .. </li>
4604         * </ul>
4605         * </li>
4606         * <li> <B>UNSIGNED_ATTRIBUTE</B> boolean => is it unsigned? </li>
4607         * <li> <B>FIXED_PREC_SCALE</B> boolean => can it be a money value? </li>
4608         * <li> <B>AUTO_INCREMENT</B> boolean => can it be used for an
4609         * auto-increment value? </li>
4610         * <li> <B>LOCAL_TYPE_NAME</B> String => localized version of type name
4611         * (may be null) </li>
4612         * <li> <B>MINIMUM_SCALE</B> short => minimum scale supported </li>
4613         * <li> <B>MAXIMUM_SCALE</B> short => maximum scale supported </li>
4614         * <li> <B>SQL_DATA_TYPE</B> int => unused </li>
4615         * <li> <B>SQL_DATETIME_SUB</B> int => unused </li>
4616         * <li> <B>NUM_PREC_RADIX</B> int => usually 2 or 10 </li>
4617         * </ol>
4618         * </p>
4619         * 
4620         * @return ResultSet each row is a SQL type description
4621         * @throws SQLException
4622         *             DOCUMENT ME!
4623         */
4624        /**
4625         * Get a description of all the standard SQL types supported by this
4626         * database. They are ordered by DATA_TYPE and then by how closely the data
4627         * type maps to the corresponding JDBC SQL type.
4628         * <P>
4629         * Each type description has the following columns:
4630         * <OL>
4631         * <li> <B>TYPE_NAME</B> String => Type name </li>
4632         * <li> <B>DATA_TYPE</B> short => SQL data type from java.sql.Types </li>
4633         * <li> <B>PRECISION</B> int => maximum precision </li>
4634         * <li> <B>LITERAL_PREFIX</B> String => prefix used to quote a literal (may
4635         * be null) </li>
4636         * <li> <B>LITERAL_SUFFIX</B> String => suffix used to quote a literal (may
4637         * be null) </li>
4638         * <li> <B>CREATE_PARAMS</B> String => parameters used in creating the type
4639         * (may be null) </li>
4640         * <li> <B>NULLABLE</B> short => can you use NULL for this type?
4641         * <UL>
4642         * <li> typeNoNulls - does not allow NULL values </li>
4643         * <li> typeNullable - allows NULL values </li>
4644         * <li> typeNullableUnknown - nullability unknown </li>
4645         * </ul>
4646         * </li>
4647         * <li> <B>CASE_SENSITIVE</B> boolean=> is it case sensitive? </li>
4648         * <li> <B>SEARCHABLE</B> short => can you use "WHERE" based on this type:
4649         * <UL>
4650         * <li> typePredNone - No support </li>
4651         * <li> typePredChar - Only supported with WHERE .. LIKE </li>
4652         * <li> typePredBasic - Supported except for WHERE .. LIKE </li>
4653         * <li> typeSearchable - Supported for all WHERE .. </li>
4654         * </ul>
4655         * </li>
4656         * <li> <B>UNSIGNED_ATTRIBUTE</B> boolean => is it unsigned? </li>
4657         * <li> <B>FIXED_PREC_SCALE</B> boolean => can it be a money value? </li>
4658         * <li> <B>AUTO_INCREMENT</B> boolean => can it be used for an
4659         * auto-increment value? </li>
4660         * <li> <B>LOCAL_TYPE_NAME</B> String => localized version of type name
4661         * (may be null) </li>
4662         * <li> <B>MINIMUM_SCALE</B> short => minimum scale supported </li>
4663         * <li> <B>MAXIMUM_SCALE</B> short => maximum scale supported </li>
4664         * <li> <B>SQL_DATA_TYPE</B> int => unused </li>
4665         * <li> <B>SQL_DATETIME_SUB</B> int => unused </li>
4666         * <li> <B>NUM_PREC_RADIX</B> int => usually 2 or 10 </li>
4667         * </ol>
4668         * </p>
4669         * 
4670         * @return ResultSet each row is a SQL type description
4671         * @throws SQLException
4672         *             DOCUMENT ME!
4673         */
4674        public java.sql.ResultSet getTypeInfo() throws SQLException {
4675                Field[] fields = new Field[18];
4676                fields[0] = new Field("", "TYPE_NAME", Types.CHAR, 32);
4677                fields[1] = new Field("", "DATA_TYPE", Types.SMALLINT, 5);
4678                fields[2] = new Field("", "PRECISION", Types.INTEGER, 10);
4679                fields[3] = new Field("", "LITERAL_PREFIX", Types.CHAR, 4);
4680                fields[4] = new Field("", "LITERAL_SUFFIX", Types.CHAR, 4);
4681                fields[5] = new Field("", "CREATE_PARAMS", Types.CHAR, 32);
4682                fields[6] = new Field("", "NULLABLE", Types.SMALLINT, 5);
4683                fields[7] = new Field("", "CASE_SENSITIVE", Types.CHAR, 3);
4684                fields[8] = new Field("", "SEARCHABLE", Types.SMALLINT, 3);
4685                fields[9] = new Field("", "UNSIGNED_ATTRIBUTE", Types.CHAR, 3);
4686                fields[10] = new Field("", "FIXED_PREC_SCALE", Types.CHAR, 3);
4687                fields[11] = new Field("", "AUTO_INCREMENT", Types.CHAR, 3);
4688                fields[12] = new Field("", "LOCAL_TYPE_NAME", Types.CHAR, 32);
4689                fields[13] = new Field("", "MINIMUM_SCALE", Types.SMALLINT, 5);
4690                fields[14] = new Field("", "MAXIMUM_SCALE", Types.SMALLINT, 5);
4691                fields[15] = new Field("", "SQL_DATA_TYPE", Types.INTEGER, 10);
4692                fields[16] = new Field("", "SQL_DATETIME_SUB", Types.INTEGER, 10);
4693                fields[17] = new Field("", "NUM_PREC_RADIX", Types.INTEGER, 10);
4694 
4695                byte[][] rowVal = null;
4696                ArrayList tuples = new ArrayList();
4697 
4698                /*
4699                 * The following are ordered by java.sql.Types, and then by how closely
4700                 * the MySQL type matches the JDBC Type (per spec)
4701                 */
4702                /*
4703                 * MySQL Type: BIT (silently converted to TINYINT(1)) JDBC Type: BIT
4704                 */
4705                rowVal = new byte[18][];
4706                rowVal[0] = s2b("BIT");
4707                rowVal[1] = Integer.toString(java.sql.Types.BIT).getBytes();
4708 
4709                // JDBC Data type
4710                rowVal[2] = s2b("1"); // Precision
4711                rowVal[3] = s2b(""); // Literal Prefix
4712                rowVal[4] = s2b(""); // Literal Suffix
4713                rowVal[5] = s2b(""); // Create Params
4714                rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
4715                                .getBytes();
4716 
4717                // Nullable
4718                rowVal[7] = s2b("true"); // Case Sensitive
4719                rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
4720                                .getBytes();
4721 
4722                // Searchable
4723                rowVal[9] = s2b("false"); // Unsignable
4724                rowVal[10] = s2b("false"); // Fixed Prec Scale
4725                rowVal[11] = s2b("false"); // Auto Increment
4726                rowVal[12] = s2b("BIT"); // Locale Type Name
4727                rowVal[13] = s2b("0"); // Minimum Scale
4728                rowVal[14] = s2b("0"); // Maximum Scale
4729                rowVal[15] = s2b("0"); // SQL Data Type (not used)
4730                rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
4731                rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
4732                tuples.add(rowVal);
4733 
4734                /*
4735                 * MySQL Type: BOOL (silently converted to TINYINT(1)) JDBC Type: BIT
4736                 */
4737                rowVal = new byte[18][];
4738                rowVal[0] = s2b("BOOL");
4739                rowVal[1] = Integer.toString(java.sql.Types.BIT).getBytes();
4740 
4741                // JDBC Data type
4742                rowVal[2] = s2b("1"); // Precision
4743                rowVal[3] = s2b(""); // Literal Prefix
4744                rowVal[4] = s2b(""); // Literal Suffix
4745                rowVal[5] = s2b(""); // Create Params
4746                rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
4747                                .getBytes();
4748 
4749                // Nullable
4750                rowVal[7] = s2b("true"); // Case Sensitive
4751                rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
4752                                .getBytes();
4753 
4754                // Searchable
4755                rowVal[9] = s2b("false"); // Unsignable
4756                rowVal[10] = s2b("false"); // Fixed Prec Scale
4757                rowVal[11] = s2b("false"); // Auto Increment
4758                rowVal[12] = s2b("BOOL"); // Locale Type Name
4759                rowVal[13] = s2b("0"); // Minimum Scale
4760                rowVal[14] = s2b("0"); // Maximum Scale
4761                rowVal[15] = s2b("0"); // SQL Data Type (not used)
4762                rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
4763                rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
4764                tuples.add(rowVal);
4765 
4766                /*
4767                 * MySQL Type: TINYINT JDBC Type: TINYINT
4768                 */
4769                rowVal = new byte[18][];
4770                rowVal[0] = s2b("TINYINT");
4771                rowVal[1] = Integer.toString(java.sql.Types.TINYINT).getBytes();
4772 
4773                // JDBC Data type
4774                rowVal[2] = s2b("3"); // Precision
4775                rowVal[3] = s2b(""); // Literal Prefix
4776                rowVal[4] = s2b(""); // Literal Suffix
4777                rowVal[5] = s2b("[(M)] [UNSIGNED] [ZEROFILL]"); // Create Params
4778                rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
4779                                .getBytes();
4780 
4781                // Nullable
4782                rowVal[7] = s2b("false"); // Case Sensitive
4783                rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
4784                                .getBytes();
4785 
4786                // Searchable
4787                rowVal[9] = s2b("true"); // Unsignable
4788                rowVal[10] = s2b("false"); // Fixed Prec Scale
4789                rowVal[11] = s2b("true"); // Auto Increment
4790                rowVal[12] = s2b("TINYINT"); // Locale Type Name
4791                rowVal[13] = s2b("0"); // Minimum Scale
4792                rowVal[14] = s2b("0"); // Maximum Scale
4793                rowVal[15] = s2b("0"); // SQL Data Type (not used)
4794                rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
4795                rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
4796                tuples.add(rowVal);
4797 
4798                /*
4799                 * MySQL Type: BIGINT JDBC Type: BIGINT
4800                 */
4801                rowVal = new byte[18][];
4802                rowVal[0] = s2b("BIGINT");
4803                rowVal[1] = Integer.toString(java.sql.Types.BIGINT).getBytes();
4804 
4805                // JDBC Data type
4806                rowVal[2] = s2b("19"); // Precision
4807                rowVal[3] = s2b(""); // Literal Prefix
4808                rowVal[4] = s2b(""); // Literal Suffix
4809                rowVal[5] = s2b("[(M)] [UNSIGNED] [ZEROFILL]"); // Create Params
4810                rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
4811                                .getBytes();
4812 
4813                // Nullable
4814                rowVal[7] = s2b("false"); // Case Sensitive
4815                rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
4816                                .getBytes();
4817 
4818                // Searchable
4819                rowVal[9] = s2b("true"); // Unsignable
4820                rowVal[10] = s2b("false"); // Fixed Prec Scale
4821                rowVal[11] = s2b("true"); // Auto Increment
4822                rowVal[12] = s2b("BIGINT"); // Locale Type Name
4823                rowVal[13] = s2b("0"); // Minimum Scale
4824                rowVal[14] = s2b("0"); // Maximum Scale
4825                rowVal[15] = s2b("0"); // SQL Data Type (not used)
4826                rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
4827                rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
4828                tuples.add(rowVal);
4829 
4830                /*
4831                 * MySQL Type: LONG VARBINARY JDBC Type: LONGVARBINARY
4832                 */
4833                rowVal = new byte[18][];
4834                rowVal[0] = s2b("LONG VARBINARY");
4835                rowVal[1] = Integer.toString(java.sql.Types.LONGVARBINARY).getBytes();
4836 
4837                // JDBC Data type
4838                rowVal[2] = s2b("16777215"); // Precision
4839                rowVal[3] = s2b("'"); // Literal Prefix
4840                rowVal[4] = s2b("'"); // Literal Suffix
4841                rowVal[5] = s2b(""); // Create Params
4842                rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
4843                                .getBytes();
4844 
4845                // Nullable
4846                rowVal[7] = s2b("true"); // Case Sensitive
4847                rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
4848                                .getBytes();
4849 
4850                // Searchable
4851                rowVal[9] = s2b("false"); // Unsignable
4852                rowVal[10] = s2b("false"); // Fixed Prec Scale
4853                rowVal[11] = s2b("false"); // Auto Increment
4854                rowVal[12] = s2b("LONG VARBINARY"); // Locale Type Name
4855                rowVal[13] = s2b("0"); // Minimum Scale
4856                rowVal[14] = s2b("0"); // Maximum Scale
4857                rowVal[15] = s2b("0"); // SQL Data Type (not used)
4858                rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
4859                rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
4860                tuples.add(rowVal);
4861 
4862                /*
4863                 * MySQL Type: MEDIUMBLOB JDBC Type: LONGVARBINARY
4864                 */
4865                rowVal = new byte[18][];
4866                rowVal[0] = s2b("MEDIUMBLOB");
4867                rowVal[1] = Integer.toString(java.sql.Types.LONGVARBINARY).getBytes();
4868 
4869                // JDBC Data type
4870                rowVal[2] = s2b("16777215"); // Precision
4871                rowVal[3] = s2b("'"); // Literal Prefix
4872                rowVal[4] = s2b("'"); // Literal Suffix
4873                rowVal[5] = s2b(""); // Create Params
4874                rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
4875                                .getBytes();
4876 
4877                // Nullable
4878                rowVal[7] = s2b("true"); // Case Sensitive
4879                rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
4880                                .getBytes();
4881 
4882                // Searchable
4883                rowVal[9] = s2b("false"); // Unsignable
4884                rowVal[10] = s2b("false"); // Fixed Prec Scale
4885                rowVal[11] = s2b("false"); // Auto Increment
4886                rowVal[12] = s2b("MEDIUMBLOB"); // Locale Type Name
4887                rowVal[13] = s2b("0"); // Minimum Scale
4888                rowVal[14] = s2b("0"); // Maximum Scale
4889                rowVal[15] = s2b("0"); // SQL Data Type (not used)
4890                rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
4891                rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
4892                tuples.add(rowVal);
4893 
4894                /*
4895                 * MySQL Type: LONGBLOB JDBC Type: LONGVARBINARY
4896                 */
4897                rowVal = new byte[18][];
4898                rowVal[0] = s2b("LONGBLOB");
4899                rowVal[1] = Integer.toString(java.sql.Types.LONGVARBINARY).getBytes();
4900 
4901                // JDBC Data type
4902                rowVal[2] = Integer.toString(Integer.MAX_VALUE).getBytes();
4903 
4904                // Precision
4905                rowVal[3] = s2b("'"); // Literal Prefix
4906                rowVal[4] = s2b("'"); // Literal Suffix
4907                rowVal[5] = s2b(""); // Create Params
4908                rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
4909                                .getBytes();
4910 
4911                // Nullable
4912                rowVal[7] = s2b("true"); // Case Sensitive
4913                rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
4914                                .getBytes();
4915 
4916                // Searchable
4917                rowVal[9] = s2b("false"); // Unsignable
4918                rowVal[10] = s2b("false"); // Fixed Prec Scale
4919                rowVal[11] = s2b("false"); // Auto Increment
4920                rowVal[12] = s2b("LONGBLOB"); // Locale Type Name
4921                rowVal[13] = s2b("0"); // Minimum Scale
4922                rowVal[14] = s2b("0"); // Maximum Scale
4923                rowVal[15] = s2b("0"); // SQL Data Type (not used)
4924                rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
4925                rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
4926                tuples.add(rowVal);
4927 
4928                /*
4929                 * MySQL Type: BLOB JDBC Type: LONGVARBINARY
4930                 */
4931                rowVal = new byte[18][];
4932                rowVal[0] = s2b("BLOB");
4933                rowVal[1] = Integer.toString(java.sql.Types.LONGVARBINARY).getBytes();
4934 
4935                // JDBC Data type
4936                rowVal[2] = s2b("65535"); // Precision
4937                rowVal[3] = s2b("'"); // Literal Prefix
4938                rowVal[4] = s2b("'"); // Literal Suffix
4939                rowVal[5] = s2b(""); // Create Params
4940                rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
4941                                .getBytes();
4942 
4943                // Nullable
4944                rowVal[7] = s2b("true"); // Case Sensitive
4945                rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
4946                                .getBytes();
4947 
4948                // Searchable
4949                rowVal[9] = s2b("false"); // Unsignable
4950                rowVal[10] = s2b("false"); // Fixed Prec Scale
4951                rowVal[11] = s2b("false"); // Auto Increment
4952                rowVal[12] = s2b("BLOB"); // Locale Type Name
4953                rowVal[13] = s2b("0"); // Minimum Scale
4954                rowVal[14] = s2b("0"); // Maximum Scale
4955                rowVal[15] = s2b("0"); // SQL Data Type (not used)
4956                rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
4957                rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
4958                tuples.add(rowVal);
4959 
4960                /*
4961                 * MySQL Type: TINYBLOB JDBC Type: LONGVARBINARY
4962                 */
4963                rowVal = new byte[18][];
4964                rowVal[0] = s2b("TINYBLOB");
4965                rowVal[1] = Integer.toString(java.sql.Types.LONGVARBINARY).getBytes();
4966 
4967                // JDBC Data type
4968                rowVal[2] = s2b("255"); // Precision
4969                rowVal[3] = s2b("'"); // Literal Prefix
4970                rowVal[4] = s2b("'"); // Literal Suffix
4971                rowVal[5] = s2b(""); // Create Params
4972                rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
4973                                .getBytes();
4974 
4975                // Nullable
4976                rowVal[7] = s2b("true"); // Case Sensitive
4977                rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
4978                                .getBytes();
4979 
4980                // Searchable
4981                rowVal[9] = s2b("false"); // Unsignable
4982                rowVal[10] = s2b("false"); // Fixed Prec Scale
4983                rowVal[11] = s2b("false"); // Auto Increment
4984                rowVal[12] = s2b("TINYBLOB"); // Locale Type Name
4985                rowVal[13] = s2b("0"); // Minimum Scale
4986                rowVal[14] = s2b("0"); // Maximum Scale
4987                rowVal[15] = s2b("0"); // SQL Data Type (not used)
4988                rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
4989                rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
4990                tuples.add(rowVal);
4991 
4992                /*
4993                 * MySQL Type: VARBINARY (sliently converted to VARCHAR(M) BINARY) JDBC
4994                 * Type: VARBINARY
4995                 */
4996                rowVal = new byte[18][];
4997                rowVal[0] = s2b("VARBINARY");
4998                rowVal[1] = Integer.toString(java.sql.Types.VARBINARY).getBytes();
4999 
5000                // JDBC Data type
5001                rowVal[2] = s2b("255"); // Precision
5002                rowVal[3] = s2b("'"); // Literal Prefix
5003                rowVal[4] = s2b("'"); // Literal Suffix
5004                rowVal[5] = s2b("(M)"); // Create Params
5005                rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
5006                                .getBytes();
5007 
5008                // Nullable
5009                rowVal[7] = s2b("true"); // Case Sensitive
5010                rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
5011                                .getBytes();
5012 
5013                // Searchable
5014                rowVal[9] = s2b("false"); // Unsignable
5015                rowVal[10] = s2b("false"); // Fixed Prec Scale
5016                rowVal[11] = s2b("false"); // Auto Increment
5017                rowVal[12] = s2b("VARBINARY"); // Locale Type Name
5018                rowVal[13] = s2b("0"); // Minimum Scale
5019                rowVal[14] = s2b("0"); // Maximum Scale
5020                rowVal[15] = s2b("0"); // SQL Data Type (not used)
5021                rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5022                rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5023                tuples.add(rowVal);
5024 
5025                /*
5026                 * MySQL Type: BINARY (silently converted to CHAR(M) BINARY) JDBC Type:
5027                 * BINARY
5028                 */
5029                rowVal = new byte[18][];
5030                rowVal[0] = s2b("BINARY");
5031                rowVal[1] = Integer.toString(java.sql.Types.BINARY).getBytes();
5032 
5033                // JDBC Data type
5034                rowVal[2] = s2b("255"); // Precision
5035                rowVal[3] = s2b("'"); // Literal Prefix
5036                rowVal[4] = s2b("'"); // Literal Suffix
5037                rowVal[5] = s2b("(M)"); // Create Params
5038                rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
5039                                .getBytes();
5040 
5041                // Nullable
5042                rowVal[7] = s2b("true"); // Case Sensitive
5043                rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
5044                                .getBytes();
5045 
5046                // Searchable
5047                rowVal[9] = s2b("false"); // Unsignable
5048                rowVal[10] = s2b("false"); // Fixed Prec Scale
5049                rowVal[11] = s2b("false"); // Auto Increment
5050                rowVal[12] = s2b("BINARY"); // Locale Type Name
5051                rowVal[13] = s2b("0"); // Minimum Scale
5052                rowVal[14] = s2b("0"); // Maximum Scale
5053                rowVal[15] = s2b("0"); // SQL Data Type (not used)
5054                rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5055                rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5056                tuples.add(rowVal);
5057 
5058                /*
5059                 * MySQL Type: LONG VARCHAR JDBC Type: LONGVARCHAR
5060                 */
5061                rowVal = new byte[18][];
5062                rowVal[0] = s2b("LONG VARCHAR");
5063                rowVal[1] = Integer.toString(java.sql.Types.LONGVARCHAR).getBytes();
5064 
5065                // JDBC Data type
5066                rowVal[2] = s2b("16777215"); // Precision
5067                rowVal[3] = s2b("'"); // Literal Prefix
5068                rowVal[4] = s2b("'"); // Literal Suffix
5069                rowVal[5] = s2b(""); // Create Params
5070                rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
5071                                .getBytes();
5072 
5073                // Nullable
5074                rowVal[7] = s2b("false"); // Case Sensitive
5075                rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
5076                                .getBytes();
5077 
5078                // Searchable
5079                rowVal[9] = s2b("false"); // Unsignable
5080                rowVal[10] = s2b("false"); // Fixed Prec Scale
5081                rowVal[11] = s2b("false"); // Auto Increment
5082                rowVal[12] = s2b("LONG VARCHAR"); // Locale Type Name
5083                rowVal[13] = s2b("0"); // Minimum Scale
5084                rowVal[14] = s2b("0"); // Maximum Scale
5085                rowVal[15] = s2b("0"); // SQL Data Type (not used)
5086                rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5087                rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5088                tuples.add(rowVal);
5089 
5090                /*
5091                 * MySQL Type: MEDIUMTEXT JDBC Type: LONGVARCHAR
5092                 */
5093                rowVal = new byte[18][];
5094                rowVal[0] = s2b("MEDIUMTEXT");
5095                rowVal[1] = Integer.toString(java.sql.Types.LONGVARCHAR).getBytes();
5096 
5097                // JDBC Data type
5098                rowVal[2] = s2b("16777215"); // Precision
5099                rowVal[3] = s2b("'"); // Literal Prefix
5100                rowVal[4] = s2b("'"); // Literal Suffix
5101                rowVal[5] = s2b(""); // Create Params
5102                rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
5103                                .getBytes();
5104 
5105                // Nullable
5106                rowVal[7] = s2b("false"); // Case Sensitive
5107                rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
5108                                .getBytes();
5109 
5110                // Searchable
5111                rowVal[9] = s2b("false"); // Unsignable
5112                rowVal[10] = s2b("false"); // Fixed Prec Scale
5113                rowVal[11] = s2b("false"); // Auto Increment
5114                rowVal[12] = s2b("MEDIUMTEXT"); // Locale Type Name
5115                rowVal[13] = s2b("0"); // Minimum Scale
5116                rowVal[14] = s2b("0"); // Maximum Scale
5117                rowVal[15] = s2b("0"); // SQL Data Type (not used)
5118                rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5119                rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5120                tuples.add(rowVal);
5121 
5122                /*
5123                 * MySQL Type: LONGTEXT JDBC Type: LONGVARCHAR
5124                 */
5125                rowVal = new byte[18][];
5126                rowVal[0] = s2b("LONGTEXT");
5127                rowVal[1] = Integer.toString(java.sql.Types.LONGVARCHAR).getBytes();
5128 
5129                // JDBC Data type
5130                rowVal[2] = Integer.toString(Integer.MAX_VALUE).getBytes();
5131 
5132                // Precision
5133                rowVal[3] = s2b("'"); // Literal Prefix
5134                rowVal[4] = s2b("'"); // Literal Suffix
5135                rowVal[5] = s2b(""); // Create Params
5136                rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
5137                                .getBytes();
5138 
5139                // Nullable
5140                rowVal[7] = s2b("false"); // Case Sensitive
5141                rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
5142                                .getBytes();
5143 
5144                // Searchable
5145                rowVal[9] = s2b("false"); // Unsignable
5146                rowVal[10] = s2b("false"); // Fixed Prec Scale
5147                rowVal[11] = s2b("false"); // Auto Increment
5148                rowVal[12] = s2b("LONGTEXT"); // Locale Type Name
5149                rowVal[13] = s2b("0"); // Minimum Scale
5150                rowVal[14] = s2b("0"); // Maximum Scale
5151                rowVal[15] = s2b("0"); // SQL Data Type (not used)
5152                rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5153                rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5154                tuples.add(rowVal);
5155 
5156                /*
5157                 * MySQL Type: TEXT JDBC Type: LONGVARCHAR
5158                 */
5159                rowVal = new byte[18][];
5160                rowVal[0] = s2b("TEXT");
5161                rowVal[1] = Integer.toString(java.sql.Types.LONGVARCHAR).getBytes();
5162 
5163                // JDBC Data type
5164                rowVal[2] = s2b("65535"); // Precision
5165                rowVal[3] = s2b("'"); // Literal Prefix
5166                rowVal[4] = s2b("'"); // Literal Suffix
5167                rowVal[5] = s2b(""); // Create Params
5168                rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
5169                                .getBytes();
5170 
5171                // Nullable
5172                rowVal[7] = s2b("false"); // Case Sensitive
5173                rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
5174                                .getBytes();
5175 
5176                // Searchable
5177                rowVal[9] = s2b("false"); // Unsignable
5178                rowVal[10] = s2b("false"); // Fixed Prec Scale
5179                rowVal[11] = s2b("false"); // Auto Increment
5180                rowVal[12] = s2b("TEXT"); // Locale Type Name
5181                rowVal[13] = s2b("0"); // Minimum Scale
5182                rowVal[14] = s2b("0"); // Maximum Scale
5183                rowVal[15] = s2b("0"); // SQL Data Type (not used)
5184                rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5185                rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5186                tuples.add(rowVal);
5187 
5188                /*
5189                 * MySQL Type: TINYTEXT JDBC Type: LONGVARCHAR
5190                 */
5191                rowVal = new byte[18][];
5192                rowVal[0] = s2b("TINYTEXT");
5193                rowVal[1] = Integer.toString(java.sql.Types.LONGVARCHAR).getBytes();
5194 
5195                // JDBC Data type
5196                rowVal[2] = s2b("255"); // Precision
5197                rowVal[3] = s2b("'"); // Literal Prefix
5198                rowVal[4] = s2b("'"); // Literal Suffix
5199                rowVal[5] = s2b(""); // Create Params
5200                rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
5201                                .getBytes();
5202 
5203                // Nullable
5204                rowVal[7] = s2b("false"); // Case Sensitive
5205                rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
5206                                .getBytes();
5207 
5208                // Searchable
5209                rowVal[9] = s2b("false"); // Unsignable
5210                rowVal[10] = s2b("false"); // Fixed Prec Scale
5211                rowVal[11] = s2b("false"); // Auto Increment
5212                rowVal[12] = s2b("TINYTEXT"); // Locale Type Name
5213                rowVal[13] = s2b("0"); // Minimum Scale
5214                rowVal[14] = s2b("0"); // Maximum Scale
5215                rowVal[15] = s2b("0"); // SQL Data Type (not used)
5216                rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5217                rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5218                tuples.add(rowVal);
5219 
5220                /*
5221                 * MySQL Type: CHAR JDBC Type: CHAR
5222                 */
5223                rowVal = new byte[18][];
5224                rowVal[0] = s2b("CHAR");
5225                rowVal[1] = Integer.toString(java.sql.Types.CHAR).getBytes();
5226 
5227                // JDBC Data type
5228                rowVal[2] = s2b("255"); // Precision
5229                rowVal[3] = s2b("'"); // Literal Prefix
5230                rowVal[4] = s2b("'"); // Literal Suffix
5231                rowVal[5] = s2b("(M)"); // Create Params
5232                rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
5233                                .getBytes();
5234 
5235                // Nullable
5236                rowVal[7] = s2b("false"); // Case Sensitive
5237                rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
5238                                .getBytes();
5239 
5240                // Searchable
5241                rowVal[9] = s2b("false"); // Unsignable
5242                rowVal[10] = s2b("false"); // Fixed Prec Scale
5243                rowVal[11] = s2b("false"); // Auto Increment
5244                rowVal[12] = s2b("CHAR"); // Locale Type Name
5245                rowVal[13] = s2b("0"); // Minimum Scale
5246                rowVal[14] = s2b("0"); // Maximum Scale
5247                rowVal[15] = s2b("0"); // SQL Data Type (not used)
5248                rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5249                rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5250                tuples.add(rowVal);
5251 
5252                /*
5253                 * MySQL Type: NUMERIC (silently converted to DECIMAL) JDBC Type:
5254                 * NUMERIC
5255                 */
5256                rowVal = new byte[18][];
5257                rowVal[0] = s2b("NUMERIC");
5258                rowVal[1] = Integer.toString(java.sql.Types.NUMERIC).getBytes();
5259 
5260                // JDBC Data type
5261                rowVal[2] = s2b("17"); // Precision
5262                rowVal[3] = s2b(""); // Literal Prefix
5263                rowVal[4] = s2b(""); // Literal Suffix
5264                rowVal[5] = s2b("[(M[,D])] [ZEROFILL]"); // Create Params
5265                rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
5266                                .getBytes();
5267 
5268                // Nullable
5269                rowVal[7] = s2b("false"); // Case Sensitive
5270                rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
5271                                .getBytes();
5272 
5273                // Searchable
5274                rowVal[9] = s2b("false"); // Unsignable
5275                rowVal[10] = s2b("false"); // Fixed Prec Scale
5276                rowVal[11] = s2b("true"); // Auto Increment
5277                rowVal[12] = s2b("NUMERIC"); // Locale Type Name
5278                rowVal[13] = s2b("-308"); // Minimum Scale
5279                rowVal[14] = s2b("308"); // Maximum Scale
5280                rowVal[15] = s2b("0"); // SQL Data Type (not used)
5281                rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5282                rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5283                tuples.add(rowVal);
5284 
5285                /*
5286                 * MySQL Type: DECIMAL JDBC Type: DECIMAL
5287                 */
5288                rowVal = new byte[18][];
5289                rowVal[0] = s2b("DECIMAL");
5290                rowVal[1] = Integer.toString(java.sql.Types.DECIMAL).getBytes();
5291 
5292                // JDBC Data type
5293                rowVal[2] = s2b("17"); // Precision
5294                rowVal[3] = s2b(""); // Literal Prefix
5295                rowVal[4] = s2b(""); // Literal Suffix
5296                rowVal[5] = s2b("[(M[,D])] [ZEROFILL]"); // Create Params
5297                rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
5298                                .getBytes();
5299 
5300                // Nullable
5301                rowVal[7] = s2b("false"); // Case Sensitive
5302                rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
5303                                .getBytes();
5304 
5305                // Searchable
5306                rowVal[9] = s2b("false"); // Unsignable
5307                rowVal[10] = s2b("false"); // Fixed Prec Scale
5308                rowVal[11] = s2b("true"); // Auto Increment
5309                rowVal[12] = s2b("DECIMAL"); // Locale Type Name
5310                rowVal[13] = s2b("-308"); // Minimum Scale
5311                rowVal[14] = s2b("308"); // Maximum Scale
5312                rowVal[15] = s2b("0"); // SQL Data Type (not used)
5313                rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5314                rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5315                tuples.add(rowVal);
5316 
5317                /*
5318                 * MySQL Type: INTEGER JDBC Type: INTEGER
5319                 */
5320                rowVal = new byte[18][];
5321                rowVal[0] = s2b("INTEGER");
5322                rowVal[1] = Integer.toString(java.sql.Types.INTEGER).getBytes();
5323 
5324                // JDBC Data type
5325                rowVal[2] = s2b("10"); // Precision
5326                rowVal[3] = s2b(""); // Literal Prefix
5327                rowVal[4] = s2b(""); // Literal Suffix
5328                rowVal[5] = s2b("[(M)] [UNSIGNED] [ZEROFILL]"); // Create Params
5329                rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
5330                                .getBytes();
5331 
5332                // Nullable
5333                rowVal[7] = s2b("false"); // Case Sensitive
5334                rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
5335                                .getBytes();
5336 
5337                // Searchable
5338                rowVal[9] = s2b("true"); // Unsignable
5339                rowVal[10] = s2b("false"); // Fixed Prec Scale
5340                rowVal[11] = s2b("true"); // Auto Increment
5341                rowVal[12] = s2b("INTEGER"); // Locale Type Name
5342                rowVal[13] = s2b("0"); // Minimum Scale
5343                rowVal[14] = s2b("0"); // Maximum Scale
5344                rowVal[15] = s2b("0"); // SQL Data Type (not used)
5345                rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5346                rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5347                tuples.add(rowVal);
5348 
5349                /*
5350                 * MySQL Type: INT JDBC Type: INTEGER
5351                 */
5352                rowVal = new byte[18][];
5353                rowVal[0] = s2b("INT");
5354                rowVal[1] = Integer.toString(java.sql.Types.INTEGER).getBytes();
5355 
5356                // JDBC Data type
5357                rowVal[2] = s2b("10"); // Precision
5358                rowVal[3] = s2b(""); // Literal Prefix
5359                rowVal[4] = s2b(""); // Literal Suffix
5360                rowVal[5] = s2b("[(M)] [UNSIGNED] [ZEROFILL]"); // Create Params
5361                rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
5362                                .getBytes();
5363 
5364                // Nullable
5365                rowVal[7] = s2b("false"); // Case Sensitive
5366                rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
5367                                .getBytes();
5368 
5369                // Searchable
5370                rowVal[9] = s2b("true"); // Unsignable
5371                rowVal[10] = s2b("false"); // Fixed Prec Scale
5372                rowVal[11] = s2b("true"); // Auto Increment
5373                rowVal[12] = s2b("INT"); // Locale Type Name
5374                rowVal[13] = s2b("0"); // Minimum Scale
5375                rowVal[14] = s2b("0"); // Maximum Scale
5376                rowVal[15] = s2b("0"); // SQL Data Type (not used)
5377                rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5378                rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5379                tuples.add(rowVal);
5380 
5381                /*
5382                 * MySQL Type: MEDIUMINT JDBC Type: INTEGER
5383                 */
5384                rowVal = new byte[18][];
5385                rowVal[0] = s2b("MEDIUMINT");
5386                rowVal[1] = Integer.toString(java.sql.Types.INTEGER).getBytes();
5387 
5388                // JDBC Data type
5389                rowVal[2] = s2b("7"); // Precision
5390                rowVal[3] = s2b(""); // Literal Prefix
5391                rowVal[4] = s2b(""); // Literal Suffix
5392                rowVal[5] = s2b("[(M)] [UNSIGNED] [ZEROFILL]"); // Create Params
5393                rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
5394                                .getBytes();
5395 
5396                // Nullable
5397                rowVal[7] = s2b("false"); // Case Sensitive
5398                rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
5399                                .getBytes();
5400 
5401                // Searchable
5402                rowVal[9] = s2b("true"); // Unsignable
5403                rowVal[10] = s2b("false"); // Fixed Prec Scale
5404                rowVal[11] = s2b("true"); // Auto Increment
5405                rowVal[12] = s2b("MEDIUMINT"); // Locale Type Name
5406                rowVal[13] = s2b("0"); // Minimum Scale
5407                rowVal[14] = s2b("0"); // Maximum Scale
5408                rowVal[15] = s2b("0"); // SQL Data Type (not used)
5409                rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5410                rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5411                tuples.add(rowVal);
5412 
5413                /*
5414                 * MySQL Type: SMALLINT JDBC Type: SMALLINT
5415                 */
5416                rowVal = new byte[18][];
5417                rowVal[0] = s2b("SMALLINT");
5418                rowVal[1] = Integer.toString(java.sql.Types.SMALLINT).getBytes();
5419 
5420                // JDBC Data type
5421                rowVal[2] = s2b("5"); // Precision
5422                rowVal[3] = s2b(""); // Literal Prefix
5423                rowVal[4] = s2b(""); // Literal Suffix
5424                rowVal[5] = s2b("[(M)] [UNSIGNED] [ZEROFILL]"); // Create Params
5425                rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
5426                                .getBytes();
5427 
5428                // Nullable
5429                rowVal[7] = s2b("false"); // Case Sensitive
5430                rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
5431                                .getBytes();
5432 
5433                // Searchable
5434                rowVal[9] = s2b("true"); // Unsignable
5435                rowVal[10] = s2b("false"); // Fixed Prec Scale
5436                rowVal[11] = s2b("true"); // Auto Increment
5437                rowVal[12] = s2b("SMALLINT"); // Locale Type Name
5438                rowVal[13] = s2b("0"); // Minimum Scale
5439                rowVal[14] = s2b("0"); // Maximum Scale
5440                rowVal[15] = s2b("0"); // SQL Data Type (not used)
5441                rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5442                rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5443                tuples.add(rowVal);
5444 
5445                /*
5446                 * MySQL Type: FLOAT JDBC Type: REAL (this is the SINGLE PERCISION
5447                 * floating point type)
5448                 */
5449                rowVal = new byte[18][];
5450                rowVal[0] = s2b("FLOAT");
5451                rowVal[1] = Integer.toString(java.sql.Types.REAL).getBytes();
5452 
5453                // JDBC Data type
5454                rowVal[2] = s2b("10"); // Precision
5455                rowVal[3] = s2b(""); // Literal Prefix
5456                rowVal[4] = s2b(""); // Literal Suffix
5457                rowVal[5] = s2b("[(M,D)] [ZEROFILL]"); // Create Params
5458                rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
5459                                .getBytes();
5460 
5461                // Nullable
5462                rowVal[7] = s2b("false"); // Case Sensitive
5463                rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
5464                                .getBytes();
5465 
5466                // Searchable
5467                rowVal[9] = s2b("false"); // Unsignable
5468                rowVal[10] = s2b("false"); // Fixed Prec Scale
5469                rowVal[11] = s2b("true"); // Auto Increment
5470                rowVal[12] = s2b("FLOAT"); // Locale Type Name
5471                rowVal[13] = s2b("-38"); // Minimum Scale
5472                rowVal[14] = s2b("38"); // Maximum Scale
5473                rowVal[15] = s2b("0"); // SQL Data Type (not used)
5474                rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5475                rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5476                tuples.add(rowVal);
5477 
5478                /*
5479                 * MySQL Type: DOUBLE JDBC Type: DOUBLE
5480                 */
5481                rowVal = new byte[18][];
5482                rowVal[0] = s2b("DOUBLE");
5483                rowVal[1] = Integer.toString(java.sql.Types.DOUBLE).getBytes();
5484 
5485                // JDBC Data type
5486                rowVal[2] = s2b("17"); // Precision
5487                rowVal[3] = s2b(""); // Literal Prefix
5488                rowVal[4] = s2b(""); // Literal Suffix
5489                rowVal[5] = s2b("[(M,D)] [ZEROFILL]"); // Create Params
5490                rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
5491                                .getBytes();
5492 
5493                // Nullable
5494                rowVal[7] = s2b("false"); // Case Sensitive
5495                rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
5496                                .getBytes();
5497 
5498                // Searchable
5499                rowVal[9] = s2b("false"); // Unsignable
5500                rowVal[10] = s2b("false"); // Fixed Prec Scale
5501                rowVal[11] = s2b("true"); // Auto Increment
5502                rowVal[12] = s2b("DOUBLE"); // Locale Type Name
5503                rowVal[13] = s2b("-308"); // Minimum Scale
5504                rowVal[14] = s2b("308"); // Maximum Scale
5505                rowVal[15] = s2b("0"); // SQL Data Type (not used)
5506                rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5507                rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5508                tuples.add(rowVal);
5509 
5510                /*
5511                 * MySQL Type: DOUBLE PRECISION JDBC Type: DOUBLE
5512                 */
5513                rowVal = new byte[18][];
5514                rowVal[0] = s2b("DOUBLE PRECISION");
5515                rowVal[1] = Integer.toString(java.sql.Types.DOUBLE).getBytes();
5516 
5517                // JDBC Data type
5518                rowVal[2] = s2b("17"); // Precision
5519                rowVal[3] = s2b(""); // Literal Prefix
5520                rowVal[4] = s2b(""); // Literal Suffix
5521                rowVal[5] = s2b("[(M,D)] [ZEROFILL]"); // Create Params
5522                rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
5523                                .getBytes();
5524 
5525                // Nullable
5526                rowVal[7] = s2b("false"); // Case Sensitive
5527                rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
5528                                .getBytes();
5529 
5530                // Searchable
5531                rowVal[9] = s2b("false"); // Unsignable
5532                rowVal[10] = s2b("false"); // Fixed Prec Scale
5533                rowVal[11] = s2b("true"); // Auto Increment
5534                rowVal[12] = s2b("DOUBLE PRECISION"); // Locale Type Name
5535                rowVal[13] = s2b("-308"); // Minimum Scale
5536                rowVal[14] = s2b("308"); // Maximum Scale
5537                rowVal[15] = s2b("0"); // SQL Data Type (not used)
5538                rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5539                rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5540                tuples.add(rowVal);
5541 
5542                /*
5543                 * MySQL Type: REAL (does not map to Types.REAL) JDBC Type: DOUBLE
5544                 */
5545                rowVal = new byte[18][];
5546                rowVal[0] = s2b("REAL");
5547                rowVal[1] = Integer.toString(java.sql.Types.DOUBLE).getBytes();
5548 
5549                // JDBC Data type
5550                rowVal[2] = s2b("17"); // Precision
5551                rowVal[3] = s2b(""); // Literal Prefix
5552                rowVal[4] = s2b(""); // Literal Suffix
5553                rowVal[5] = s2b("[(M,D)] [ZEROFILL]"); // Create Params
5554                rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
5555                                .getBytes();
5556 
5557                // Nullable
5558                rowVal[7] = s2b("false"); // Case Sensitive
5559                rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
5560                                .getBytes();
5561 
5562                // Searchable
5563                rowVal[9] = s2b("false"); // Unsignable
5564                rowVal[10] = s2b("false"); // Fixed Prec Scale
5565                rowVal[11] = s2b("true"); // Auto Increment
5566                rowVal[12] = s2b("REAL"); // Locale Type Name
5567                rowVal[13] = s2b("-308"); // Minimum Scale
5568                rowVal[14] = s2b("308"); // Maximum Scale
5569                rowVal[15] = s2b("0"); // SQL Data Type (not used)
5570                rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5571                rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5572                tuples.add(rowVal);
5573 
5574                /*
5575                 * MySQL Type: VARCHAR JDBC Type: VARCHAR
5576                 */
5577                rowVal = new byte[18][];
5578                rowVal[0] = s2b("VARCHAR");
5579                rowVal[1] = Integer.toString(java.sql.Types.VARCHAR).getBytes();
5580 
5581                // JDBC Data type
5582                rowVal[2] = s2b("255"); // Precision
5583                rowVal[3] = s2b("'"); // Literal Prefix
5584                rowVal[4] = s2b("'"); // Literal Suffix
5585                rowVal[5] = s2b("(M)"); // Create Params
5586                rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
5587                                .getBytes();
5588 
5589                // Nullable
5590                rowVal[7] = s2b("false"); // Case Sensitive
5591                rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
5592                                .getBytes();
5593 
5594                // Searchable
5595                rowVal[9] = s2b("false"); // Unsignable
5596                rowVal[10] = s2b("false"); // Fixed Prec Scale
5597                rowVal[11] = s2b("false"); // Auto Increment
5598                rowVal[12] = s2b("VARCHAR"); // Locale Type Name
5599                rowVal[13] = s2b("0"); // Minimum Scale
5600                rowVal[14] = s2b("0"); // Maximum Scale
5601                rowVal[15] = s2b("0"); // SQL Data Type (not used)
5602                rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5603                rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5604                tuples.add(rowVal);
5605 
5606                /*
5607                 * MySQL Type: ENUM JDBC Type: VARCHAR
5608                 */
5609                rowVal = new byte[18][];
5610                rowVal[0] = s2b("ENUM");
5611                rowVal[1] = Integer.toString(java.sql.Types.VARCHAR).getBytes();
5612 
5613                // JDBC Data type
5614                rowVal[2] = s2b("65535"); // Precision
5615                rowVal[3] = s2b("'"); // Literal Prefix
5616                rowVal[4] = s2b("'"); // Literal Suffix
5617                rowVal[5] = s2b(""); // Create Params
5618                rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
5619                                .getBytes();
5620 
5621                // Nullable
5622                rowVal[7] = s2b("false"); // Case Sensitive
5623                rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
5624                                .getBytes();
5625 
5626                // Searchable
5627                rowVal[9] = s2b("false"); // Unsignable
5628                rowVal[10] = s2b("false"); // Fixed Prec Scale
5629                rowVal[11] = s2b("false"); // Auto Increment
5630                rowVal[12] = s2b("ENUM"); // Locale Type Name
5631                rowVal[13] = s2b("0"); // Minimum Scale
5632                rowVal[14] = s2b("0"); // Maximum Scale
5633                rowVal[15] = s2b("0"); // SQL Data Type (not used)
5634                rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5635                rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5636                tuples.add(rowVal);
5637 
5638                /*
5639                 * MySQL Type: SET JDBC Type: VARCHAR
5640                 */
5641                rowVal = new byte[18][];
5642                rowVal[0] = s2b("SET");
5643                rowVal[1] = Integer.toString(java.sql.Types.VARCHAR).getBytes();
5644 
5645                // JDBC Data type
5646                rowVal[2] = s2b("64"); // Precision
5647                rowVal[3] = s2b("'"); // Literal Prefix
5648                rowVal[4] = s2b("'"); // Literal Suffix
5649                rowVal[5] = s2b(""); // Create Params
5650                rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
5651                                .getBytes();
5652 
5653                // Nullable
5654                rowVal[7] = s2b("false"); // Case Sensitive
5655                rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
5656                                .getBytes();
5657 
5658                // Searchable
5659                rowVal[9] = s2b("false"); // Unsignable
5660                rowVal[10] = s2b("false"); // Fixed Prec Scale
5661                rowVal[11] = s2b("false"); // Auto Increment
5662                rowVal[12] = s2b("SET"); // Locale Type Name
5663                rowVal[13] = s2b("0"); // Minimum Scale
5664                rowVal[14] = s2b("0"); // Maximum Scale
5665                rowVal[15] = s2b("0"); // SQL Data Type (not used)
5666                rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5667                rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5668                tuples.add(rowVal);
5669 
5670                /*
5671                 * MySQL Type: DATE JDBC Type: DATE
5672                 */
5673                rowVal = new byte[18][];
5674                rowVal[0] = s2b("DATE");
5675                rowVal[1] = Integer.toString(java.sql.Types.DATE).getBytes();
5676 
5677                // JDBC Data type
5678                rowVal[2] = s2b("0"); // Precision
5679                rowVal[3] = s2b("'"); // Literal Prefix
5680                rowVal[4] = s2b("'"); // Literal Suffix
5681                rowVal[5] = s2b(""); // Create Params
5682                rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
5683                                .getBytes();
5684 
5685                // Nullable
5686                rowVal[7] = s2b("false"); // Case Sensitive
5687                rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
5688                                .getBytes();
5689 
5690                // Searchable
5691                rowVal[9] = s2b("false"); // Unsignable
5692                rowVal[10] = s2b("false"); // Fixed Prec Scale
5693                rowVal[11] = s2b("false"); // Auto Increment
5694                rowVal[12] = s2b("DATE"); // Locale Type Name
5695                rowVal[13] = s2b("0"); // Minimum Scale
5696                rowVal[14] = s2b("0"); // Maximum Scale
5697                rowVal[15] = s2b("0"); // SQL Data Type (not used)
5698                rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5699                rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5700                tuples.add(rowVal);
5701 
5702                /*
5703                 * MySQL Type: TIME JDBC Type: TIME
5704                 */
5705                rowVal = new byte[18][];
5706                rowVal[0] = s2b("TIME");
5707                rowVal[1] = Integer.toString(java.sql.Types.TIME).getBytes();
5708 
5709                // JDBC Data type
5710                rowVal[2] = s2b("0"); // Precision
5711                rowVal[3] = s2b("'"); // Literal Prefix
5712                rowVal[4] = s2b("'"); // Literal Suffix
5713                rowVal[5] = s2b(""); // Create Params
5714                rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
5715                                .getBytes();
5716 
5717                // Nullable
5718                rowVal[7] = s2b("false"); // Case Sensitive
5719                rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
5720                                .getBytes();
5721 
5722                // Searchable
5723                rowVal[9] = s2b("false"); // Unsignable
5724                rowVal[10] = s2b("false"); // Fixed Prec Scale
5725                rowVal[11] = s2b("false"); // Auto Increment
5726                rowVal[12] = s2b("TIME"); // Locale Type Name
5727                rowVal[13] = s2b("0"); // Minimum Scale
5728                rowVal[14] = s2b("0"); // Maximum Scale
5729                rowVal[15] = s2b("0"); // SQL Data Type (not used)
5730                rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5731                rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5732                tuples.add(rowVal);
5733 
5734                /*
5735                 * MySQL Type: DATETIME JDBC Type: TIMESTAMP
5736                 */
5737                rowVal = new byte[18][];
5738                rowVal[0] = s2b("DATETIME");
5739                rowVal[1] = Integer.toString(java.sql.Types.TIMESTAMP).getBytes();
5740 
5741                // JDBC Data type
5742                rowVal[2] = s2b("0"); // Precision
5743                rowVal[3] = s2b("'"); // Literal Prefix
5744                rowVal[4] = s2b("'"); // Literal Suffix
5745                rowVal[5] = s2b(""); // Create Params
5746                rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
5747                                .getBytes();
5748 
5749                // Nullable
5750                rowVal[7] = s2b("false"); // Case Sensitive
5751                rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
5752                                .getBytes();
5753 
5754                // Searchable
5755                rowVal[9] = s2b("false"); // Unsignable
5756                rowVal[10] = s2b("false"); // Fixed Prec Scale
5757                rowVal[11] = s2b("false"); // Auto Increment
5758                rowVal[12] = s2b("DATETIME"); // Locale Type Name
5759                rowVal[13] = s2b("0"); // Minimum Scale
5760                rowVal[14] = s2b("0"); // Maximum Scale
5761                rowVal[15] = s2b("0"); // SQL Data Type (not used)
5762                rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5763                rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5764                tuples.add(rowVal);
5765 
5766                /*
5767                 * MySQL Type: TIMESTAMP JDBC Type: TIMESTAMP
5768                 */
5769                rowVal = new byte[18][];
5770                rowVal[0] = s2b("TIMESTAMP");
5771                rowVal[1] = Integer.toString(java.sql.Types.TIMESTAMP).getBytes();
5772 
5773                // JDBC Data type
5774                rowVal[2] = s2b("0"); // Precision
5775                rowVal[3] = s2b("'"); // Literal Prefix
5776                rowVal[4] = s2b("'"); // Literal Suffix
5777                rowVal[5] = s2b("[(M)]"); // Create Params
5778                rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
5779                                .getBytes();
5780 
5781                // Nullable
5782                rowVal[7] = s2b("false"); // Case Sensitive
5783                rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
5784                                .getBytes();
5785 
5786                // Searchable
5787                rowVal[9] = s2b("false"); // Unsignable
5788                rowVal[10] = s2b("false"); // Fixed Prec Scale
5789                rowVal[11] = s2b("false"); // Auto Increment
5790                rowVal[12] = s2b("TIMESTAMP"); // Locale Type Name
5791                rowVal[13] = s2b("0"); // Minimum Scale
5792                rowVal[14] = s2b("0"); // Maximum Scale
5793                rowVal[15] = s2b("0"); // SQL Data Type (not used)
5794                rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5795                rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5796                tuples.add(rowVal);
5797 
5798                return buildResultSet(fields, tuples);
5799        }
5800 
5801        /**
5802         * JDBC 2.0 Get a description of the user-defined types defined in a
5803         * particular schema. Schema specific UDTs may have type JAVA_OBJECT,
5804         * STRUCT, or DISTINCT.
5805         * <P>
5806         * Only types matching the catalog, schema, type name and type criteria are
5807         * returned. They are ordered by DATA_TYPE, TYPE_SCHEM and TYPE_NAME. The
5808         * type name parameter may be a fully qualified name. In this case, the
5809         * catalog and schemaPattern parameters are ignored.
5810         * </p>
5811         * <P>
5812         * Each type description has the following columns:
5813         * <OL>
5814         * <li> <B>TYPE_CAT</B> String => the type's catalog (may be null) </li>
5815         * <li> <B>TYPE_SCHEM</B> String => type's schema (may be null) </li>
5816         * <li> <B>TYPE_NAME</B> String => type name </li>
5817         * <li> <B>CLASS_NAME</B> String => Java class name </li>
5818         * <li> <B>DATA_TYPE</B> String => type value defined in java.sql.Types.
5819         * One of JAVA_OBJECT, STRUCT, or DISTINCT </li>
5820         * <li> <B>REMARKS</B> String => explanatory comment on the type </li>
5821         * </ol>
5822         * </p>
5823         * <P>
5824         * <B>Note:</B> If the driver does not support UDTs then an empty result
5825         * set is returned.
5826         * </p>
5827         * 
5828         * @param catalog
5829         *            a catalog name; "" retrieves those without a catalog; null
5830         *            means drop catalog name from the selection criteria
5831         * @param schemaPattern
5832         *            a schema name pattern; "" retrieves those without a schema
5833         * @param typeNamePattern
5834         *            a type name pattern; may be a fully qualified name
5835         * @param types
5836         *            a list of user-named types to include (JAVA_OBJECT, STRUCT, or
5837         *            DISTINCT); null returns all types
5838         * @return ResultSet - each row is a type description
5839         * @exception SQLException
5840         *                if a database-access error occurs.
5841         */
5842        public java.sql.ResultSet getUDTs(String catalog, String schemaPattern,
5843                        String typeNamePattern, int[] types) throws SQLException {
5844                Field[] fields = new Field[6];
5845                fields[0] = new Field("", "TYPE_CAT", Types.VARCHAR, 32);
5846                fields[1] = new Field("", "TYPE_SCHEM", Types.VARCHAR, 32);
5847                fields[2] = new Field("", "TYPE_NAME", Types.VARCHAR, 32);
5848                fields[3] = new Field("", "CLASS_NAME", Types.VARCHAR, 32);
5849                fields[4] = new Field("", "DATA_TYPE", Types.VARCHAR, 32);
5850                fields[5] = new Field("", "REMARKS", Types.VARCHAR, 32);
5851 
5852                ArrayList tuples = new ArrayList();
5853 
5854                return buildResultSet(fields, tuples);
5855        }
5856 
5857        /**
5858         * What's the url for this database?
5859         * 
5860         * @return the url or null if it can't be generated
5861         * @throws SQLException
5862         *             DOCUMENT ME!
5863         */
5864        public String getURL() throws SQLException {
5865                return this.conn.getURL();
5866        }
5867 
5868        /**
5869         * What's our user name as known to the database?
5870         * 
5871         * @return our database user name
5872         * @throws SQLException
5873         *             DOCUMENT ME!
5874         */
5875        public String getUserName() throws SQLException {
5876                if (this.conn.getUseHostsInPrivileges()) {
5877                        Statement stmt = null;
5878                        ResultSet rs = null;
5879 
5880                        try {
5881                                stmt = this.conn.createStatement();
5882                                stmt.setEscapeProcessing(false);
5883 
5884                                rs = stmt.executeQuery("SELECT USER()");
5885                                rs.next();
5886 
5887                                return rs.getString(1);
5888                        } finally {
5889                                if (rs != null) {
5890                                        try {
5891                                                rs.close();
5892                                        } catch (Exception ex) {
5893                                                AssertionFailedException.shouldNotHappen(ex);
5894                                        }
5895 
5896                                        rs = null;
5897                                }
5898 
5899                                if (stmt != null) {
5900                                        try {
5901                                                stmt.close();
5902                                        } catch (Exception ex) {
5903                                                AssertionFailedException.shouldNotHappen(ex);
5904                                        }
5905 
5906                                        stmt = null;
5907                                }
5908                        }
5909                }
5910 
5911                return this.conn.getUser();
5912        }
5913 
5914        /**
5915         * Get a description of a table's columns that are automatically updated
5916         * when any value in a row is updated. They are unordered.
5917         * <P>
5918         * Each column description has the following columns:
5919         * <OL>
5920         * <li> <B>SCOPE</B> short => is not used </li>
5921         * <li> <B>COLUMN_NAME</B> String => column name </li>
5922         * <li> <B>DATA_TYPE</B> short => SQL data type from java.sql.Types </li>
5923         * <li> <B>TYPE_NAME</B> String => Data source dependent type name </li>
5924         * <li> <B>COLUMN_SIZE</B> int => precision </li>
5925         * <li> <B>BUFFER_LENGTH</B> int => length of column value in bytes </li>
5926         * <li> <B>DECIMAL_DIGITS</B> short => scale </li>
5927         * <li> <B>PSEUDO_COLUMN</B> short => is this a pseudo column like an
5928         * Oracle ROWID
5929         * <UL>
5930         * <li> versionColumnUnknown - may or may not be pseudo column </li>
5931         * <li> versionColumnNotPseudo - is NOT a pseudo column </li>
5932         * <li> versionColumnPseudo - is a pseudo column </li>
5933         * </ul>
5934         * </li>
5935         * </ol>
5936         * </p>
5937         * 
5938         * @param catalog
5939         *            a catalog name; "" retrieves those without a catalog
5940         * @param schema
5941         *            a schema name; "" retrieves those without a schema
5942         * @param table
5943         *            a table name
5944         * @return ResultSet each row is a column description
5945         * @throws SQLException
5946         *             DOCUMENT ME!
5947         */
5948        public java.sql.ResultSet getVersionColumns(String catalog, String schema,
5949                        String table) throws SQLException {
5950                Field[] fields = new Field[8];
5951                fields[0] = new Field("", "SCOPE", Types.SMALLINT, 5);
5952                fields[1] = new Field("", "COLUMN_NAME", Types.CHAR, 32);
5953                fields[2] = new Field("", "DATA_TYPE", Types.SMALLINT, 5);
5954                fields[3] = new Field("", "TYPE_NAME", Types.CHAR, 16);
5955                fields[4] = new Field("", "COLUMN_SIZE", Types.CHAR, 16);
5956                fields[5] = new Field("", "BUFFER_LENGTH", Types.CHAR, 16);
5957                fields[6] = new Field("", "DECIMAL_DIGITS", Types.CHAR, 16);
5958                fields[7] = new Field("", "PSEUDO_COLUMN", Types.SMALLINT, 5);
5959 
5960                return buildResultSet(fields, new ArrayList());
5961 
5962                // do TIMESTAMP columns count?
5963        }
5964 
5965        /**
5966         * JDBC 2.0 Determine whether or not a visible row insert can be detected by
5967         * calling ResultSet.rowInserted().
5968         * 
5969         * @param type
5970         *            set type, i.e. ResultSet.TYPE_XXX
5971         * @return true if changes are detected by the resultset type
5972         * @exception SQLException
5973         *                if a database-access error occurs.
5974         */
5975        public boolean insertsAreDetected(int type) throws SQLException {
5976                return false;
5977        }
5978 
5979        /**
5980         * Does a catalog appear at the start of a qualified table name? (Otherwise
5981         * it appears at the end)
5982         * 
5983         * @return true if it appears at the start
5984         * @throws SQLException
5985         *             DOCUMENT ME!
5986         */
5987        public boolean isCatalogAtStart() throws SQLException {
5988                return true;
5989        }
5990 
5991        /**
5992         * Is the database in read-only mode?
5993         * 
5994         * @return true if so
5995         * @throws SQLException
5996         *             DOCUMENT ME!
5997         */
5998        public boolean isReadOnly() throws SQLException {
5999                return false;
6000        }
6001 
6002        /**
6003         * @see DatabaseMetaData#locatorsUpdateCopy()
6004         */
6005        public boolean locatorsUpdateCopy() throws SQLException {
6006                return !this.conn.getEmulateLocators();
6007        }
6008 
6009        /**
6010         * Are concatenations between NULL and non-NULL values NULL? A JDBC
6011         * compliant driver always returns true.
6012         * 
6013         * @return true if so
6014         * @throws SQLException
6015         *             DOCUMENT ME!
6016         */
6017        public boolean nullPlusNonNullIsNull() throws SQLException {
6018                return true;
6019        }
6020 
6021        /**
6022         * Are NULL values sorted at the end regardless of sort order?
6023         * 
6024         * @return true if so
6025         * @throws SQLException
6026         *             DOCUMENT ME!
6027         */
6028        public boolean nullsAreSortedAtEnd() throws SQLException {
6029                return false;
6030        }
6031 
6032        /**
6033         * Are NULL values sorted at the start regardless of sort order?
6034         * 
6035         * @return true if so
6036         * @throws SQLException
6037         *             DOCUMENT ME!
6038         */
6039        public boolean nullsAreSortedAtStart() throws SQLException {
6040                return (this.conn.versionMeetsMinimum(4, 0, 2) && !this.conn
6041                                .versionMeetsMinimum(4, 0, 11));
6042        }
6043 
6044        /**
6045         * Are NULL values sorted high?
6046         * 
6047         * @return true if so
6048         * @throws SQLException
6049         *             DOCUMENT ME!
6050         */
6051        public boolean nullsAreSortedHigh() throws SQLException {
6052                return false;
6053        }
6054 
6055        /**
6056         * Are NULL values sorted low?
6057         * 
6058         * @return true if so
6059         * @throws SQLException
6060         *             DOCUMENT ME!
6061         */
6062        public boolean nullsAreSortedLow() throws SQLException {
6063                return !nullsAreSortedHigh();
6064        }
6065 
6066        /**
6067         * DOCUMENT ME!
6068         * 
6069         * @param type
6070         *            DOCUMENT ME!
6071         * @return DOCUMENT ME!
6072         * @throws SQLException
6073         *             DOCUMENT ME!
6074         */
6075        public boolean othersDeletesAreVisible(int type) throws SQLException {
6076                return false;
6077        }
6078 
6079        /**
6080         * DOCUMENT ME!
6081         * 
6082         * @param type
6083         *            DOCUMENT ME!
6084         * @return DOCUMENT ME!
6085         * @throws SQLException
6086         *             DOCUMENT ME!
6087         */
6088        public boolean othersInsertsAreVisible(int type) throws SQLException {
6089                return false;
6090        }
6091 
6092        /**
6093         * JDBC 2.0 Determine whether changes made by others are visible.
6094         * 
6095         * @param type
6096         *            set type, i.e. ResultSet.TYPE_XXX
6097         * @return true if changes are visible for the result set type
6098         * @exception SQLException
6099         *                if a database-access error occurs.
6100         */
6101        public boolean othersUpdatesAreVisible(int type) throws SQLException {
6102                return false;
6103        }
6104 
6105        /**
6106         * DOCUMENT ME!
6107         * 
6108         * @param type
6109         *            DOCUMENT ME!
6110         * @return DOCUMENT ME!
6111         * @throws SQLException
6112         *             DOCUMENT ME!
6113         */
6114        public boolean ownDeletesAreVisible(int type) throws SQLException {
6115                return false;
6116        }
6117 
6118        /**
6119         * DOCUMENT ME!
6120         * 
6121         * @param type
6122         *            DOCUMENT ME!
6123         * @return DOCUMENT ME!
6124         * @throws SQLException
6125         *             DOCUMENT ME!
6126         */
6127        public boolean ownInsertsAreVisible(int type) throws SQLException {
6128                return false;
6129        }
6130 
6131        /**
6132         * JDBC 2.0 Determine whether a result set's own changes visible.
6133         * 
6134         * @param type
6135         *            set type, i.e. ResultSet.TYPE_XXX
6136         * @return true if changes are visible for the result set type
6137         * @exception SQLException
6138         *                if a database-access error occurs.
6139         */
6140        public boolean ownUpdatesAreVisible(int type) throws SQLException {
6141                return false;
6142        }
6143 
6144        private LocalAndReferencedColumns parseTableStatusIntoLocalAndReferencedColumns(
6145                        String keysComment) throws SQLException {
6146                // keys will equal something like this:
6147                // (parent_service_id child_service_id) REFER
6148                // ds/subservices(parent_service_id child_service_id)
6149                //
6150                // simple-columned keys: (m) REFER
6151                // airline/tt(a)
6152                //
6153                // multi-columned keys : (m n) REFER
6154                // airline/vv(a b)
6155                //
6156                // parse of the string into three phases:
6157                // 1: parse the opening parentheses to determine how many results there
6158                // will be
6159                // 2: read in the schema name/table name
6160                // 3: parse the closing parentheses
6161 
6162                String columnsDelimitter = ","; // what version did this change in?
6163 
6164                char quoteChar = this.quotedId.length() == 0 ? 0 : this.quotedId
6165                                .charAt(0);
6166 
6167                int indexOfOpenParenLocalColumns = StringUtils
6168                                .indexOfIgnoreCaseRespectQuotes(0, keysComment, "(", quoteChar,
6169                                                true);
6170 
6171                if (indexOfOpenParenLocalColumns == -1) {
6172                        throw SQLError.createSQLException("Error parsing foreign keys definition,"
6173                                        + " couldn't find start of local columns list.",
6174                                        SQLError.SQL_STATE_GENERAL_ERROR);
6175                }
6176 
6177                String constraintName = removeQuotedId(keysComment.substring(0,
6178                                indexOfOpenParenLocalColumns).trim());
6179                keysComment = keysComment.substring(indexOfOpenParenLocalColumns,
6180                                keysComment.length());
6181 
6182                String keysCommentTrimmed = keysComment.trim();
6183 
6184                int indexOfCloseParenLocalColumns = StringUtils
6185                                .indexOfIgnoreCaseRespectQuotes(0, keysCommentTrimmed, ")",
6186                                                quoteChar, true);
6187 
6188                if (indexOfCloseParenLocalColumns == -1) {
6189                        throw SQLError.createSQLException("Error parsing foreign keys definition,"
6190                                        + " couldn't find end of local columns list.",
6191                                        SQLError.SQL_STATE_GENERAL_ERROR);
6192                }
6193 
6194                String localColumnNamesString = keysCommentTrimmed.substring(1,
6195                                indexOfCloseParenLocalColumns);
6196 
6197                int indexOfRefer = StringUtils.indexOfIgnoreCaseRespectQuotes(0,
6198                                keysCommentTrimmed, "REFER ", this.quotedId.charAt(0), true);
6199 
6200                if (indexOfRefer == -1) {
6201                        throw SQLError.createSQLException("Error parsing foreign keys definition,"
6202                                        + " couldn't find start of referenced tables list.",
6203                                        SQLError.SQL_STATE_GENERAL_ERROR);
6204                }
6205 
6206                int indexOfOpenParenReferCol = StringUtils
6207                                .indexOfIgnoreCaseRespectQuotes(indexOfRefer,
6208                                                keysCommentTrimmed, "(", quoteChar, false);
6209 
6210                if (indexOfOpenParenReferCol == -1) {
6211                        throw SQLError.createSQLException("Error parsing foreign keys definition,"
6212                                        + " couldn't find start of referenced columns list.",
6213                                        SQLError.SQL_STATE_GENERAL_ERROR);
6214                }
6215 
6216                String referCatalogTableString = keysCommentTrimmed.substring(
6217                                indexOfRefer + "REFER ".length(), indexOfOpenParenReferCol);
6218 
6219                int indexOfSlash = StringUtils.indexOfIgnoreCaseRespectQuotes(0,
6220                                referCatalogTableString, "/", this.quotedId.charAt(0), false);
6221 
6222                if (indexOfSlash == -1) {
6223                        throw SQLError.createSQLException("Error parsing foreign keys definition,"
6224                                        + " couldn't find name of referenced catalog.",
6225                                        SQLError.SQL_STATE_GENERAL_ERROR);
6226                }
6227 
6228                String referCatalog = removeQuotedId(referCatalogTableString.substring(
6229                                0, indexOfSlash));
6230                String referTable = removeQuotedId(referCatalogTableString.substring(
6231                                indexOfSlash + 1).trim());
6232 
6233                int indexOfCloseParenRefer = StringUtils
6234                                .indexOfIgnoreCaseRespectQuotes(indexOfOpenParenReferCol,
6235                                                keysCommentTrimmed, ")", quoteChar, true);
6236 
6237                if (indexOfCloseParenRefer == -1) {
6238                        throw SQLError.createSQLException("Error parsing foreign keys definition,"
6239                                        + " couldn't find end of referenced columns list.",
6240                                        SQLError.SQL_STATE_GENERAL_ERROR);
6241                }
6242 
6243                String referColumnNamesString = keysCommentTrimmed.substring(
6244                                indexOfOpenParenReferCol + 1, indexOfCloseParenRefer);
6245 
6246                List referColumnsList = StringUtils.split(referColumnNamesString,
6247                                columnsDelimitter, this.quotedId, this.quotedId, false);
6248                List localColumnsList = StringUtils.split(localColumnNamesString,
6249                                columnsDelimitter, this.quotedId, this.quotedId, false);
6250 
6251                return new LocalAndReferencedColumns(localColumnsList,
6252                                referColumnsList, constraintName, referCatalog, referTable);
6253        }
6254 
6255        private String removeQuotedId(String s) {
6256                if (s == null) {
6257                        return null;
6258                }
6259 
6260                if (this.quotedId.equals("")) {
6261                        return s;
6262                }
6263 
6264                s = s.trim();
6265 
6266                int frontOffset = 0;
6267                int backOffset = s.length();
6268                int quoteLength = this.quotedId.length();
6269 
6270                if (s.startsWith(this.quotedId)) {
6271                        frontOffset = quoteLength;
6272                }
6273 
6274                if (s.endsWith(this.quotedId)) {
6275                        backOffset -= quoteLength;
6276                }
6277 
6278                return s.substring(frontOffset, backOffset);
6279        }
6280 
6281        /**
6282         * Converts the given string to bytes, using the connection's character
6283         * encoding, or if not available, the JVM default encoding.
6284         * 
6285         * @param s
6286         *            DOCUMENT ME!
6287         * @return DOCUMENT ME!
6288         */
6289        private byte[] s2b(String s) throws SQLException {
6290                return StringUtils.s2b(s, this.conn);
6291        }
6292        
6293 
6294        /**
6295         * Does the database store mixed case unquoted SQL identifiers in lower
6296         * case?
6297         * 
6298         * @return true if so
6299         * @throws SQLException
6300         *             DOCUMENT ME!
6301         */
6302        public boolean storesLowerCaseIdentifiers() throws SQLException {
6303                return this.conn.lowerCaseTableNames();
6304        }
6305 
6306        /**
6307         * Does the database store mixed case quoted SQL identifiers in lower case?
6308         * A JDBC compliant driver will always return false.
6309         * 
6310         * @return true if so
6311         * @throws SQLException
6312         *             DOCUMENT ME!
6313         */
6314        public boolean storesLowerCaseQuotedIdentifiers() throws SQLException {
6315                return this.conn.lowerCaseTableNames();
6316        }
6317 
6318        /**
6319         * Does the database store mixed case unquoted SQL identifiers in mixed
6320         * case?
6321         * 
6322         * @return true if so
6323         * @throws SQLException
6324         *             DOCUMENT ME!
6325         */
6326        public boolean storesMixedCaseIdentifiers() throws SQLException {
6327                return !this.conn.lowerCaseTableNames();
6328        }
6329 
6330        /**
6331         * Does the database store mixed case quoted SQL identifiers in mixed case?
6332         * A JDBC compliant driver will always return false.
6333         * 
6334         * @return true if so
6335         * @throws SQLException
6336         *             DOCUMENT ME!
6337         */
6338        public boolean storesMixedCaseQuotedIdentifiers() throws SQLException {
6339                return !this.conn.lowerCaseTableNames();
6340        }
6341 
6342        /**
6343         * Does the database store mixed case unquoted SQL identifiers in upper
6344         * case?
6345         * 
6346         * @return true if so
6347         * @throws SQLException
6348         *             DOCUMENT ME!
6349         */
6350        public boolean storesUpperCaseIdentifiers() throws SQLException {
6351                return false;
6352        }
6353 
6354        /**
6355         * Does the database store mixed case quoted SQL identifiers in upper case?
6356         * A JDBC compliant driver will always return true.
6357         * 
6358         * @return true if so
6359         * @throws SQLException
6360         *             DOCUMENT ME!
6361         */
6362        public boolean storesUpperCaseQuotedIdentifiers() throws SQLException {
6363                return true; // not actually true, but required by JDBC spec!?
6364        }
6365 
6366        /**
6367         * Is "ALTER TABLE" with add column supported?
6368         * 
6369         * @return true if so
6370         * @throws SQLException
6371         *             DOCUMENT ME!
6372         */
6373        public boolean supportsAlterTableWithAddColumn() throws SQLException {
6374                return true;
6375        }
6376 
6377        /**
6378         * Is "ALTER TABLE" with drop column supported?
6379         * 
6380         * @return true if so
6381         * @throws SQLException
6382         *             DOCUMENT ME!
6383         */
6384        public boolean supportsAlterTableWithDropColumn() throws SQLException {
6385                return true;
6386        }
6387 
6388        /**
6389         * Is the ANSI92 entry level SQL grammar supported? All JDBC compliant
6390         * drivers must return true.
6391         * 
6392         * @return true if so
6393         * @throws SQLException
6394         *             DOCUMENT ME!
6395         */
6396        public boolean supportsANSI92EntryLevelSQL() throws SQLException {
6397                return true;
6398        }
6399 
6400        /**
6401         * Is the ANSI92 full SQL grammar supported?
6402         * 
6403         * @return true if so
6404         * @throws SQLException
6405         *             DOCUMENT ME!
6406         */
6407        public boolean supportsANSI92FullSQL() throws SQLException {
6408                return false;
6409        }
6410 
6411        /**
6412         * Is the ANSI92 intermediate SQL grammar supported?
6413         * 
6414         * @return true if so
6415         * @throws SQLException
6416         *             DOCUMENT ME!
6417         */
6418        public boolean supportsANSI92IntermediateSQL() throws SQLException {
6419                return false;
6420        }
6421 
6422        /**
6423         * JDBC 2.0 Return true if the driver supports batch updates, else return
6424         * false.
6425         * 
6426         * @return DOCUMENT ME!
6427         * @throws SQLException
6428         *             DOCUMENT ME!
6429         */
6430        public boolean supportsBatchUpdates() throws SQLException {
6431                return true;
6432        }
6433 
6434        /**
6435         * Can a catalog name be used in a data manipulation statement?
6436         * 
6437         * @return true if so
6438         * @throws SQLException
6439         *             DOCUMENT ME!
6440         */
6441        public boolean supportsCatalogsInDataManipulation() throws SQLException {
6442                // Servers before 3.22 could not do this
6443                return this.conn.versionMeetsMinimum(3, 22, 0);
6444        }
6445 
6446        /**
6447         * Can a catalog name be used in a index definition statement?
6448         * 
6449         * @return true if so
6450         * @throws SQLException
6451         *             DOCUMENT ME!
6452         */
6453        public boolean supportsCatalogsInIndexDefinitions() throws SQLException {
6454                return false;
6455        }
6456 
6457        /**
6458         * Can a catalog name be used in a privilege definition statement?
6459         * 
6460         * @return true if so
6461         * @throws SQLException
6462         *             DOCUMENT ME!
6463         */
6464        public boolean supportsCatalogsInPrivilegeDefinitions() throws SQLException {
6465                return false;
6466        }
6467 
6468        /**
6469         * Can a catalog name be used in a procedure call statement?
6470         * 
6471         * @return true if so
6472         * @throws SQLException
6473         *             DOCUMENT ME!
6474         */
6475        public boolean supportsCatalogsInProcedureCalls() throws SQLException {
6476                return false;
6477        }
6478 
6479        /**
6480         * Can a catalog name be used in a table definition statement?
6481         * 
6482         * @return true if so
6483         * @throws SQLException
6484         *             DOCUMENT ME!
6485         */
6486        public boolean supportsCatalogsInTableDefinitions() throws SQLException {
6487                return false;
6488        }
6489 
6490        /**
6491         * Is column aliasing supported?
6492         * <P>
6493         * If so, the SQL AS clause can be used to provide names for computed
6494         * columns or to provide alias names for columns as required. A JDBC
6495         * compliant driver always returns true.
6496         * </p>
6497         * 
6498         * @return true if so
6499         * @throws SQLException
6500         *             DOCUMENT ME!
6501         */
6502        public boolean supportsColumnAliasing() throws SQLException {
6503                return true;
6504        }
6505 
6506        /**
6507         * Is the CONVERT function between SQL types supported?
6508         * 
6509         * @return true if so
6510         * @throws SQLException
6511         *             DOCUMENT ME!
6512         */
6513        public boolean supportsConvert() throws SQLException {
6514                return false;
6515        }
6516 
6517        /**
6518         * Is CONVERT between the given SQL types supported?
6519         * 
6520         * @param fromType
6521         *            the type to convert from
6522         * @param toType
6523         *            the type to convert to
6524         * @return true if so
6525         * @throws SQLException
6526         *             if an error occurs
6527         * @see Types
6528         */
6529        public boolean supportsConvert(int fromType, int toType)
6530                        throws SQLException {
6531                switch (fromType) {
6532                /*
6533                 * The char/binary types can be converted to pretty much anything.
6534                 */
6535                case java.sql.Types.CHAR:
6536                case java.sql.Types.VARCHAR:
6537                case java.sql.Types.LONGVARCHAR:
6538                case java.sql.Types.BINARY:
6539                case java.sql.Types.VARBINARY:
6540                case java.sql.Types.LONGVARBINARY:
6541 
6542                        switch (toType) {
6543                        case java.sql.Types.DECIMAL:
6544                        case java.sql.Types.NUMERIC:
6545                        case java.sql.Types.REAL:
6546                        case java.sql.Types.TINYINT:
6547                        case java.sql.Types.SMALLINT:
6548                        case java.sql.Types.INTEGER:
6549                        case java.sql.Types.BIGINT:
6550                        case java.sql.Types.FLOAT:
6551                        case java.sql.Types.DOUBLE:
6552                        case java.sql.Types.CHAR:
6553                        case java.sql.Types.VARCHAR:
6554                        case java.sql.Types.LONGVARCHAR:
6555                        case java.sql.Types.BINARY:
6556                        case java.sql.Types.VARBINARY:
6557                        case java.sql.Types.LONGVARBINARY:
6558                        case java.sql.Types.OTHER:
6559                        case java.sql.Types.DATE:
6560                        case java.sql.Types.TIME:
6561                        case java.sql.Types.TIMESTAMP:
6562                                return true;
6563 
6564                        default:
6565                                return false;
6566                        }
6567 
6568                /*
6569                 * We don't handle the BIT type yet.
6570                 */
6571                case java.sql.Types.BIT:
6572                        return false;
6573 
6574                /*
6575                 * The numeric types. Basically they can convert among themselves, and
6576                 * with char/binary types.
6577                 */
6578                case java.sql.Types.DECIMAL:
6579                case java.sql.Types.NUMERIC:
6580                case java.sql.Types.REAL:
6581                case java.sql.Types.TINYINT:
6582                case java.sql.Types.SMALLINT:
6583                case java.sql.Types.INTEGER:
6584                case java.sql.Types.BIGINT:
6585                case java.sql.Types.FLOAT:
6586                case java.sql.Types.DOUBLE:
6587 
6588                        switch (toType) {
6589                        case java.sql.Types.DECIMAL:
6590                        case java.sql.Types.NUMERIC:
6591                        case java.sql.Types.REAL:
6592                        case java.sql.Types.TINYINT:
6593                        case java.sql.Types.SMALLINT:
6594                        case java.sql.Types.INTEGER:
6595                        case java.sql.Types.BIGINT:
6596                        case java.sql.Types.FLOAT:
6597                        case java.sql.Types.DOUBLE:
6598                        case java.sql.Types.CHAR:
6599                        case java.sql.Types.VARCHAR:
6600                        case java.sql.Types.LONGVARCHAR:
6601                        case java.sql.Types.BINARY:
6602                        case java.sql.Types.VARBINARY:
6603                        case java.sql.Types.LONGVARBINARY:
6604                                return true;
6605 
6606                        default:
6607                                return false;
6608                        }
6609 
6610                /* MySQL doesn't support a NULL type. */
6611                case java.sql.Types.NULL:
6612                        return false;
6613 
6614                /*
6615                 * With this driver, this will always be a serialized object, so the
6616                 * char/binary types will work.
6617                 */
6618                case java.sql.Types.OTHER:
6619 
6620                        switch (toType) {
6621                        case java.sql.Types.CHAR:
6622                        case java.sql.Types.VARCHAR:
6623                        case java.sql.Types.LONGVARCHAR:
6624                        case java.sql.Types.BINARY:
6625                        case java.sql.Types.VARBINARY:
6626                        case java.sql.Types.LONGVARBINARY:
6627                                return true;
6628 
6629                        default:
6630                                return false;
6631                        }
6632 
6633                /* Dates can be converted to char/binary types. */
6634                case java.sql.Types.DATE:
6635 
6636                        switch (toType) {
6637                        case java.sql.Types.CHAR:
6638                        case java.sql.Types.VARCHAR:
6639                        case java.sql.Types.LONGVARCHAR:
6640                        case java.sql.Types.BINARY:
6641                        case java.sql.Types.VARBINARY:
6642                        case java.sql.Types.LONGVARBINARY:
6643                                return true;
6644 
6645                        default:
6646                                return false;
6647                        }
6648 
6649                /* Time can be converted to char/binary types */
6650                case java.sql.Types.TIME:
6651 
6652                        switch (toType) {
6653                        case java.sql.Types.CHAR:
6654                        case java.sql.Types.VARCHAR:
6655                        case java.sql.Types.LONGVARCHAR:
6656                        case java.sql.Types.BINARY:
6657                        case java.sql.Types.VARBINARY:
6658                        case java.sql.Types.LONGVARBINARY:
6659                                return true;
6660 
6661                        default:
6662                                return false;
6663                        }
6664 
6665                /*
6666                 * Timestamp can be converted to char/binary types and date/time types
6667                 * (with loss of precision).
6668                 */
6669                case java.sql.Types.TIMESTAMP:
6670 
6671                        switch (toType) {
6672                        case java.sql.Types.CHAR:
6673                        case java.sql.Types.VARCHAR:
6674                        case java.sql.Types.LONGVARCHAR:
6675                        case java.sql.Types.BINARY:
6676                        case java.sql.Types.VARBINARY:
6677                        case java.sql.Types.LONGVARBINARY:
6678                        case java.sql.Types.TIME:
6679                        case java.sql.Types.DATE:
6680                                return true;
6681 
6682                        default:
6683                                return false;
6684                        }
6685 
6686                /* We shouldn't get here! */
6687                default:
6688                        return false; // not sure
6689                }
6690        }
6691 
6692        /**
6693         * Is the ODBC Core SQL grammar supported?
6694         * 
6695         * @return true if so
6696         * @throws SQLException
6697         *             DOCUMENT ME!
6698         */
6699        public boolean supportsCoreSQLGrammar() throws SQLException {
6700                return true;
6701        }
6702 
6703        /**
6704         * Are correlated subqueries supported? A JDBC compliant driver always
6705         * returns true.
6706         * 
6707         * @return true if so
6708         * @throws SQLException
6709         *             DOCUMENT ME!
6710         */
6711        public boolean supportsCorrelatedSubqueries() throws SQLException {
6712                return this.conn.versionMeetsMinimum(4, 1, 0);
6713        }
6714 
6715        /**
6716         * Are both data definition and data manipulation statements within a
6717         * transaction supported?
6718         * 
6719         * @return true if so
6720         * @throws SQLException
6721         *             DOCUMENT ME!
6722         */
6723        public boolean supportsDataDefinitionAndDataManipulationTransactions()
6724                        throws SQLException {
6725                return false;
6726        }
6727 
6728        /**
6729         * Are only data manipulation statements within a transaction supported?
6730         * 
6731         * @return true if so
6732         * @throws SQLException
6733         *             DOCUMENT ME!
6734         */
6735        public boolean supportsDataManipulationTransactionsOnly()
6736                        throws SQLException {
6737                return false;
6738        }
6739 
6740        /**
6741         * If table correlation names are supported, are they restricted to be
6742         * different from the names of the tables? A JDBC compliant driver always
6743         * returns true.
6744         * 
6745         * @return true if so
6746         * @throws SQLException
6747         *             DOCUMENT ME!
6748         */
6749        public boolean supportsDifferentTableCorrelationNames() throws SQLException {
6750                return true;
6751        }
6752 
6753        /**
6754         * Are expressions in "ORDER BY" lists supported?
6755         * 
6756         * @return true if so
6757         * @throws SQLException
6758         *             DOCUMENT ME!
6759         */
6760        public boolean supportsExpressionsInOrderBy() throws SQLException {
6761                return true;
6762        }
6763 
6764        /**
6765         * Is the ODBC Extended SQL grammar supported?
6766         * 
6767         * @return true if so
6768         * @throws SQLException
6769         *             DOCUMENT ME!
6770         */
6771        public boolean supportsExtendedSQLGrammar() throws SQLException {
6772                return false;
6773        }
6774 
6775        /**
6776         * Are full nested outer joins supported?
6777         * 
6778         * @return true if so
6779         * @throws SQLException
6780         *             DOCUMENT ME!
6781         */
6782        public boolean supportsFullOuterJoins() throws SQLException {
6783                return false;
6784        }
6785 
6786        /**
6787         * JDBC 3.0
6788         * 
6789         * @return DOCUMENT ME!
6790         */
6791        public boolean supportsGetGeneratedKeys() {
6792                return true;
6793        }
6794 
6795        /**
6796         * Is some form of "GROUP BY" clause supported?
6797         * 
6798         * @return true if so
6799         * @throws SQLException
6800         *             DOCUMENT ME!
6801         */
6802        public boolean supportsGroupBy() throws SQLException {
6803                return true;
6804        }
6805 
6806        /**
6807         * Can a "GROUP BY" clause add columns not in the SELECT provided it
6808         * specifies all the columns in the SELECT?
6809         * 
6810         * @return true if so
6811         * @throws SQLException
6812         *             DOCUMENT ME!
6813         */
6814        public boolean supportsGroupByBeyondSelect() throws SQLException {
6815                return true;
6816        }
6817 
6818        /**
6819         * Can a "GROUP BY" clause use columns not in the SELECT?
6820         * 
6821         * @return true if so
6822         * @throws SQLException
6823         *             DOCUMENT ME!
6824         */
6825        public boolean supportsGroupByUnrelated() throws SQLException {
6826                return true;
6827        }
6828 
6829        /**
6830         * Is the SQL Integrity Enhancement Facility supported?
6831         * 
6832         * @return true if so
6833         * @throws SQLException
6834         *             DOCUMENT ME!
6835         */
6836        public boolean supportsIntegrityEnhancementFacility() throws SQLException {
6837                if (!this.conn.getOverrideSupportsIntegrityEnhancementFacility()) {
6838                        return false;
6839                } 
6840                
6841                return true;
6842        }
6843 
6844        /**
6845         * Is the escape character in "LIKE" clauses supported? A JDBC compliant
6846         * driver always returns true.
6847         * 
6848         * @return true if so
6849         * @throws SQLException
6850         *             DOCUMENT ME!
6851         */
6852        public boolean supportsLikeEscapeClause() throws SQLException {
6853                return true;
6854        }
6855 
6856        /**
6857         * Is there limited support for outer joins? (This will be true if
6858         * supportFullOuterJoins is true.)
6859         * 
6860         * @return true if so
6861         * @throws SQLException
6862         *             DOCUMENT ME!
6863         */
6864        public boolean supportsLimitedOuterJoins() throws SQLException {
6865                return true;
6866        }
6867 
6868        /**
6869         * Is the ODBC Minimum SQL grammar supported? All JDBC compliant drivers
6870         * must return true.
6871         * 
6872         * @return true if so
6873         * @throws SQLException
6874         *             DOCUMENT ME!
6875         */
6876        public boolean supportsMinimumSQLGrammar() throws SQLException {
6877                return true;
6878        }
6879 
6880        /**
6881         * Does the database support mixed case unquoted SQL identifiers?
6882         * 
6883         * @return true if so
6884         * @throws SQLException
6885         *             DOCUMENT ME!
6886         */
6887        public boolean supportsMixedCaseIdentifiers() throws SQLException {
6888                return !this.conn.lowerCaseTableNames();
6889        }
6890 
6891        /**
6892         * Does the database support mixed case quoted SQL identifiers? A JDBC
6893         * compliant driver will always return true.
6894         * 
6895         * @return true if so
6896         * @throws SQLException
6897         *             DOCUMENT ME!
6898         */
6899        public boolean supportsMixedCaseQuotedIdentifiers() throws SQLException {
6900                return !this.conn.lowerCaseTableNames();
6901        }
6902 
6903        /**
6904         * @see DatabaseMetaData#supportsMultipleOpenResults()
6905         */
6906        public boolean supportsMultipleOpenResults() throws SQLException {
6907                return true;
6908        }
6909 
6910        /**
6911         * Are multiple ResultSets from a single execute supported?
6912         * 
6913         * @return true if so
6914         * @throws SQLException
6915         *             DOCUMENT ME!
6916         */
6917        public boolean supportsMultipleResultSets() throws SQLException {
6918                return false;
6919        }
6920 
6921        /**
6922         * Can we have multiple transactions open at once (on different
6923         * connections)?
6924         * 
6925         * @return true if so
6926         * @throws SQLException
6927         *             DOCUMENT ME!
6928         */
6929        public boolean supportsMultipleTransactions() throws SQLException {
6930                return true;
6931        }
6932 
6933        /**
6934         * @see DatabaseMetaData#supportsNamedParameters()
6935         */
6936        public boolean supportsNamedParameters() throws SQLException {
6937                return false;
6938        }
6939 
6940        /**
6941         * Can columns be defined as non-nullable? A JDBC compliant driver always
6942         * returns true.
6943         * 
6944         * @return true if so
6945         * @throws SQLException
6946         *             DOCUMENT ME!
6947         */
6948        public boolean supportsNonNullableColumns() throws SQLException {
6949                return true;
6950        }
6951 
6952        /**
6953         * Can cursors remain open across commits?
6954         * 
6955         * @return true if so
6956         * @throws SQLException
6957         *             if a database access error occurs
6958         * @see Connection#disableAutoClose
6959         */
6960        public boolean supportsOpenCursorsAcrossCommit() throws SQLException {
6961                return false;
6962        }
6963 
6964        /**
6965         * Can cursors remain open across rollbacks?
6966         * 
6967         * @return true if so
6968         * @throws SQLException
6969         *             if an error occurs
6970         * @see Connection#disableAutoClose
6971         */
6972        public boolean supportsOpenCursorsAcrossRollback() throws SQLException {
6973                return false;
6974        }
6975 
6976        /**
6977         * Can statements remain open across commits?
6978         * 
6979         * @return true if so
6980         * @throws SQLException
6981         *             if an error occurs
6982         * @see Connection#disableAutoClose
6983         */
6984        public boolean supportsOpenStatementsAcrossCommit() throws SQLException {
6985                return false;
6986        }
6987 
6988        /**
6989         * Can statements remain open across rollbacks?
6990         * 
6991         * @return true if so
6992         * @throws SQLException
6993         *             if an error occurs
6994         * @see Connection#disableAutoClose
6995         */
6996        public boolean supportsOpenStatementsAcrossRollback() throws SQLException {
6997                return false;
6998        }
6999 
7000        /**
7001         * Can an "ORDER BY" clause use columns not in the SELECT?
7002         * 
7003         * @return true if so
7004         * @throws SQLException
7005         *             DOCUMENT ME!
7006         */
7007        public boolean supportsOrderByUnrelated() throws SQLException {
7008                return false;
7009        }
7010 
7011        /**
7012         * Is some form of outer join supported?
7013         * 
7014         * @return true if so
7015         * @throws SQLException
7016         *             DOCUMENT ME!
7017         */
7018        public boolean supportsOuterJoins() throws SQLException {
7019                return true;
7020        }
7021 
7022        /**
7023         * Is positioned DELETE supported?
7024         * 
7025         * @return true if so
7026         * @throws SQLException
7027         *             DOCUMENT ME!
7028         */
7029        public boolean supportsPositionedDelete() throws SQLException {
7030                return false;
7031        }
7032 
7033        /**
7034         * Is positioned UPDATE supported?
7035         * 
7036         * @return true if so
7037         * @throws SQLException
7038         *             DOCUMENT ME!
7039         */
7040        public boolean supportsPositionedUpdate() throws SQLException {
7041                return false;
7042        }
7043 
7044        /**
7045         * JDBC 2.0 Does the database support the concurrency type in combination
7046         * with the given result set type?
7047         * 
7048         * @param type
7049         *            defined in java.sql.ResultSet
7050         * @param concurrency
7051         *            type defined in java.sql.ResultSet
7052         * @return true if so
7053         * @exception SQLException
7054         *                if a database-access error occurs.
7055         * @see Connection
7056         */
7057        public boolean supportsResultSetConcurrency(int type, int concurrency)
7058                        throws SQLException {
7059                switch (type) {
7060                case ResultSet.TYPE_SCROLL_INSENSITIVE:
7061                        if ((concurrency == ResultSet.CONCUR_READ_ONLY)
7062                                        || (concurrency == ResultSet.CONCUR_UPDATABLE)) {
7063                                return true;
7064                        } else {
7065                                throw SQLError.createSQLException(
7066                                                "Illegal arguments to supportsResultSetConcurrency()",
7067                                                SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
7068                        }
7069                case ResultSet.TYPE_FORWARD_ONLY:
7070                        if ((concurrency == ResultSet.CONCUR_READ_ONLY)
7071                                        || (concurrency == ResultSet.CONCUR_UPDATABLE)) {
7072                                return true;
7073                        } else {
7074                                throw SQLError.createSQLException(
7075                                                "Illegal arguments to supportsResultSetConcurrency()",
7076                                                SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
7077                        }
7078                case ResultSet.TYPE_SCROLL_SENSITIVE:
7079                        return false;
7080                default:
7081                        throw SQLError.createSQLException(
7082                                        "Illegal arguments to supportsResultSetConcurrency()",
7083                                        SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
7084                }
7085 
7086        }
7087 
7088        /**
7089         * @see DatabaseMetaData#supportsResultSetHoldability(int)
7090         */
7091        public boolean supportsResultSetHoldability(int holdability)
7092                        throws SQLException {
7093                return (holdability == ResultSet.HOLD_CURSORS_OVER_COMMIT);
7094        }
7095 
7096        /**
7097         * JDBC 2.0 Does the database support the given result set type?
7098         * 
7099         * @param type
7100         *            defined in java.sql.ResultSet
7101         * @return true if so
7102         * @exception SQLException
7103         *                if a database-access error occurs.
7104         * @see Connection
7105         */
7106        public boolean supportsResultSetType(int type) throws SQLException {
7107                return (type == ResultSet.TYPE_SCROLL_INSENSITIVE);
7108        }
7109 
7110        /**
7111         * @see DatabaseMetaData#supportsSavepoints()
7112         */
7113        public boolean supportsSavepoints() throws SQLException {
7114 
7115                return (this.conn.versionMeetsMinimum(4, 0, 14) || this.conn
7116                                .versionMeetsMinimum(4, 1, 1));
7117        }
7118 
7119        /**
7120         * Can a schema name be used in a data manipulation statement?
7121         * 
7122         * @return true if so
7123         * @throws SQLException
7124         *             DOCUMENT ME!
7125         */
7126        public boolean supportsSchemasInDataManipulation() throws SQLException {
7127                return false;
7128        }
7129 
7130        /**
7131         * Can a schema name be used in an index definition statement?
7132         * 
7133         * @return true if so
7134         * @throws SQLException
7135         *             DOCUMENT ME!
7136         */
7137        public boolean supportsSchemasInIndexDefinitions() throws SQLException {
7138                return false;
7139        }
7140 
7141        /**
7142         * Can a schema name be used in a privilege definition statement?
7143         * 
7144         * @return true if so
7145         * @throws SQLException
7146         *             DOCUMENT ME!
7147         */
7148        public boolean supportsSchemasInPrivilegeDefinitions() throws SQLException {
7149                return false;
7150        }
7151 
7152        /**
7153         * Can a schema name be used in a procedure call statement?
7154         * 
7155         * @return true if so
7156         * @throws SQLException
7157         *             DOCUMENT ME!
7158         */
7159        public boolean supportsSchemasInProcedureCalls() throws SQLException {
7160                return false;
7161        }
7162 
7163        /**
7164         * Can a schema name be used in a table definition statement?
7165         * 
7166         * @return true if so
7167         * @throws SQLException
7168         *             DOCUMENT ME!
7169         */
7170        public boolean supportsSchemasInTableDefinitions() throws SQLException {
7171                return false;
7172        }
7173 
7174        /**
7175         * Is SELECT for UPDATE supported?
7176         * 
7177         * @return true if so
7178         * @throws SQLException
7179         *             DOCUMENT ME!
7180         */
7181        public boolean supportsSelectForUpdate() throws SQLException {
7182                return this.conn.versionMeetsMinimum(4, 0, 0);
7183        }
7184 
7185        /**
7186         * @see DatabaseMetaData#supportsStatementPooling()
7187         */
7188        public boolean supportsStatementPooling() throws SQLException {
7189                return false;
7190        }
7191 
7192        /**
7193         * Are stored procedure calls using the stored procedure escape syntax
7194         * supported?
7195         * 
7196         * @return true if so
7197         * @throws SQLException
7198         *             DOCUMENT ME!
7199         */
7200        public boolean supportsStoredProcedures() throws SQLException {
7201                return this.conn.versionMeetsMinimum(5, 0, 0);
7202        }
7203 
7204        /**
7205         * Are subqueries in comparison expressions supported? A JDBC compliant
7206         * driver always returns true.
7207         * 
7208         * @return true if so
7209         * @throws SQLException
7210         *             DOCUMENT ME!
7211         */
7212        public boolean supportsSubqueriesInComparisons() throws SQLException {
7213                return this.conn.versionMeetsMinimum(4, 1, 0);
7214        }
7215 
7216        /**
7217         * Are subqueries in exists expressions supported? A JDBC compliant driver
7218         * always returns true.
7219         * 
7220         * @return true if so
7221         * @throws SQLException
7222         *             DOCUMENT ME!
7223         */
7224        public boolean supportsSubqueriesInExists() throws SQLException {
7225                return this.conn.versionMeetsMinimum(4, 1, 0);
7226        }
7227 
7228        /**
7229         * Are subqueries in "in" statements supported? A JDBC compliant driver
7230         * always returns true.
7231         * 
7232         * @return true if so
7233         * @throws SQLException
7234         *             DOCUMENT ME!
7235         */
7236        public boolean supportsSubqueriesInIns() throws SQLException {
7237                return this.conn.versionMeetsMinimum(4, 1, 0);
7238        }
7239 
7240        /**
7241         * Are subqueries in quantified expressions supported? A JDBC compliant
7242         * driver always returns true.
7243         * 
7244         * @return true if so
7245         * @throws SQLException
7246         *             DOCUMENT ME!
7247         */
7248        public boolean supportsSubqueriesInQuantifieds() throws SQLException {
7249                return this.conn.versionMeetsMinimum(4, 1, 0);
7250        }
7251 
7252        /**
7253         * Are table correlation names supported? A JDBC compliant driver always
7254         * returns true.
7255         * 
7256         * @return true if so
7257         * @throws SQLException
7258         *             DOCUMENT ME!
7259         */
7260        public boolean supportsTableCorrelationNames() throws SQLException {
7261                return true;
7262        }
7263 
7264        /**
7265         * Does the database support the given transaction isolation level?
7266         * 
7267         * @param level
7268         *            the values are defined in java.sql.Connection
7269         * @return true if so
7270         * @throws SQLException
7271         *             if a database access error occurs
7272         * @see Connection
7273         */
7274        public boolean supportsTransactionIsolationLevel(int level)
7275                        throws SQLException {
7276                if (this.conn.supportsIsolationLevel()) {
7277                        switch (level) {
7278                        case java.sql.Connection.TRANSACTION_READ_COMMITTED:
7279                        case java.sql.Connection.TRANSACTION_READ_UNCOMMITTED:
7280                        case java.sql.Connection.TRANSACTION_REPEATABLE_READ:
7281                        case java.sql.Connection.TRANSACTION_SERIALIZABLE:
7282                                return true;
7283 
7284                        default:
7285                                return false;
7286                        }
7287                }
7288 
7289                return false;
7290        }
7291 
7292        /**
7293         * Are transactions supported? If not, commit is a noop and the isolation
7294         * level is TRANSACTION_NONE.
7295         * 
7296         * @return true if transactions are supported
7297         * @throws SQLException
7298         *             DOCUMENT ME!
7299         */
7300        public boolean supportsTransactions() throws SQLException {
7301                return this.conn.supportsTransactions();
7302        }
7303 
7304        /**
7305         * Is SQL UNION supported? A JDBC compliant driver always returns true.
7306         * 
7307         * @return true if so
7308         * @throws SQLException
7309         *             DOCUMENT ME!
7310         */
7311        public boolean supportsUnion() throws SQLException {
7312                return this.conn.versionMeetsMinimum(4, 0, 0);
7313        }
7314 
7315        /**
7316         * Is SQL UNION ALL supported? A JDBC compliant driver always returns true.
7317         * 
7318         * @return true if so
7319         * @throws SQLException
7320         *             DOCUMENT ME!
7321         */
7322        public boolean supportsUnionAll() throws SQLException {
7323                return this.conn.versionMeetsMinimum(4, 0, 0);
7324        }
7325 
7326        /**
7327         * JDBC 2.0 Determine whether or not a visible row update can be detected by
7328         * calling ResultSet.rowUpdated().
7329         * 
7330         * @param type
7331         *            set type, i.e. ResultSet.TYPE_XXX
7332         * @return true if changes are detected by the resultset type
7333         * @exception SQLException
7334         *                if a database-access error occurs.
7335         */
7336        public boolean updatesAreDetected(int type) throws SQLException {
7337                return false;
7338        }
7339 
7340        /**
7341         * Does the database use a file for each table?
7342         * 
7343         * @return true if the database uses a local file for each table
7344         * @throws SQLException
7345         *             DOCUMENT ME!
7346         */
7347        public boolean usesLocalFilePerTable() throws SQLException {
7348                return false;
7349        }
7350 
7351        /**
7352         * Does the database store tables in a local file?
7353         * 
7354         * @return true if so
7355         * @throws SQLException
7356         *             DOCUMENT ME!
7357         */
7358        public boolean usesLocalFiles() throws SQLException {
7359                return false;
7360        }
7361}

[all classes][com.mysql.jdbc]
EMMA 2.0.4217 (C) Vladimir Roubtsov