EMMA Coverage Report (generated Fri May 26 15:35:26 CDT 2006)
[all classes][com.mysql.jdbc]

COVERAGE SUMMARY FOR SOURCE FILE [DatabaseMetaData.java]

nameclass, %method, %block, %line, %
DatabaseMetaData.java94%  (15/16)91%  (197/216)87%  (11314/12995)86%  (2171.8/2534)

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

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