0001: /*
0002: Copyright (C) 2002-2007 MySQL AB
0003:
0004: This program is free software; you can redistribute it and/or modify
0005: it under the terms of version 2 of the GNU General Public License as
0006: published by the Free Software Foundation.
0007:
0008: There are special exceptions to the terms and conditions of the GPL
0009: as it is applied to this software. View the full text of the
0010: exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
0011: software distribution.
0012:
0013: This program is distributed in the hope that it will be useful,
0014: but WITHOUT ANY WARRANTY; without even the implied warranty of
0015: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0016: GNU General Public License for more details.
0017:
0018: You should have received a copy of the GNU General Public License
0019: along with this program; if not, write to the Free Software
0020: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0021:
0022:
0023:
0024: */
0025: package com.mysql.jdbc;
0026:
0027: import java.io.UnsupportedEncodingException;
0028: import java.lang.reflect.Constructor;
0029: import java.lang.reflect.InvocationTargetException;
0030:
0031: import java.sql.ResultSet;
0032: import java.sql.SQLException;
0033: import java.sql.Statement;
0034: import java.sql.Types;
0035:
0036: import java.util.ArrayList;
0037: import java.util.Collections;
0038: import java.util.HashMap;
0039: import java.util.Iterator;
0040: import java.util.List;
0041: import java.util.Locale;
0042: import java.util.Map;
0043: import java.util.Properties;
0044: import java.util.StringTokenizer;
0045: import java.util.TreeMap;
0046:
0047: /**
0048: * JDBC Interface to Mysql functions
0049: * <p>
0050: * This class provides information about the database as a whole.
0051: * </p>
0052: * <p>
0053: * Many of the methods here return lists of information in ResultSets. You can
0054: * use the normal ResultSet methods such as getString and getInt to retrieve the
0055: * data from these ResultSets. If a given form of metadata is not available,
0056: * these methods show throw a SQLException.
0057: * </p>
0058: * <p>
0059: * Some of these methods take arguments that are String patterns. These methods
0060: * all have names such as fooPattern. Within a pattern String "%" means match
0061: * any substring of 0 or more characters and "_" means match any one character.
0062: * </p>
0063: *
0064: * @author Mark Matthews
0065: * @version $Id: DatabaseMetaData.java,v 1.27.4.66 2005/05/03 18:40:39 mmatthews
0066: * Exp $
0067: */
0068: public class DatabaseMetaData implements java.sql.DatabaseMetaData {
0069: protected abstract class IterateBlock {
0070: IteratorWithCleanup iterator;
0071:
0072: IterateBlock(IteratorWithCleanup i) {
0073: iterator = i;
0074: }
0075:
0076: public void doForAll() throws SQLException {
0077: try {
0078: while (iterator.hasNext()) {
0079: forEach(iterator.next());
0080: }
0081: } finally {
0082: iterator.close();
0083: }
0084: }
0085:
0086: abstract void forEach(Object each) throws SQLException;
0087: }
0088:
0089: protected abstract class IteratorWithCleanup {
0090: abstract void close() throws SQLException;
0091:
0092: abstract boolean hasNext() throws SQLException;
0093:
0094: abstract Object next() throws SQLException;
0095: }
0096:
0097: class LocalAndReferencedColumns {
0098: String constraintName;
0099:
0100: List localColumnsList;
0101:
0102: String referencedCatalog;
0103:
0104: List referencedColumnsList;
0105:
0106: String referencedTable;
0107:
0108: LocalAndReferencedColumns(List localColumns, List refColumns,
0109: String constName, String refCatalog, String refTable) {
0110: this .localColumnsList = localColumns;
0111: this .referencedColumnsList = refColumns;
0112: this .constraintName = constName;
0113: this .referencedTable = refTable;
0114: this .referencedCatalog = refCatalog;
0115: }
0116: }
0117:
0118: protected class ResultSetIterator extends IteratorWithCleanup {
0119: int colIndex;
0120:
0121: ResultSet resultSet;
0122:
0123: ResultSetIterator(ResultSet rs, int index) {
0124: resultSet = rs;
0125: colIndex = index;
0126: }
0127:
0128: void close() throws SQLException {
0129: resultSet.close();
0130: }
0131:
0132: boolean hasNext() throws SQLException {
0133: return resultSet.next();
0134: }
0135:
0136: Object next() throws SQLException {
0137: return resultSet.getObject(colIndex);
0138: }
0139: }
0140:
0141: protected class SingleStringIterator extends IteratorWithCleanup {
0142: boolean onFirst = true;
0143:
0144: String value;
0145:
0146: SingleStringIterator(String s) {
0147: value = s;
0148: }
0149:
0150: void close() throws SQLException {
0151: // not needed
0152:
0153: }
0154:
0155: boolean hasNext() throws SQLException {
0156: return onFirst;
0157: }
0158:
0159: Object next() throws SQLException {
0160: onFirst = false;
0161: return value;
0162: }
0163: }
0164:
0165: /**
0166: * Parses and represents common data type information used by various
0167: * column/parameter methods.
0168: */
0169: class TypeDescriptor {
0170: int bufferLength;
0171:
0172: int charOctetLength;
0173:
0174: Integer columnSize;
0175:
0176: short dataType;
0177:
0178: Integer decimalDigits;
0179:
0180: String isNullable;
0181:
0182: int nullability;
0183:
0184: int numPrecRadix = 10;
0185:
0186: String typeName;
0187:
0188: TypeDescriptor(String typeInfo, String nullabilityInfo)
0189: throws SQLException {
0190: if (typeInfo == null) {
0191: throw SQLError.createSQLException(
0192: "NULL typeinfo not supported.",
0193: SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
0194: }
0195:
0196: String mysqlType = "";
0197: String fullMysqlType = null;
0198:
0199: if (typeInfo.indexOf("(") != -1) {
0200: mysqlType = typeInfo
0201: .substring(0, typeInfo.indexOf("("));
0202: } else {
0203: mysqlType = typeInfo;
0204: }
0205:
0206: int indexOfUnsignedInMysqlType = StringUtils
0207: .indexOfIgnoreCase(mysqlType, "unsigned");
0208:
0209: if (indexOfUnsignedInMysqlType != -1) {
0210: mysqlType = mysqlType.substring(0,
0211: (indexOfUnsignedInMysqlType - 1));
0212: }
0213:
0214: // Add unsigned to typename reported to enduser as 'native type', if
0215: // present
0216:
0217: if (StringUtils.indexOfIgnoreCase(typeInfo, "unsigned") != -1) {
0218: fullMysqlType = mysqlType + " unsigned";
0219: } else {
0220: fullMysqlType = mysqlType;
0221: }
0222:
0223: if (conn.getCapitalizeTypeNames()) {
0224: fullMysqlType = fullMysqlType
0225: .toUpperCase(Locale.ENGLISH);
0226: }
0227:
0228: this .dataType = (short) MysqlDefs
0229: .mysqlToJavaType(mysqlType);
0230:
0231: this .typeName = fullMysqlType;
0232:
0233: // Figure Out the Size
0234:
0235: if (StringUtils.startsWithIgnoreCase(typeInfo, "enum")) {
0236: String temp = typeInfo.substring(typeInfo.indexOf("("),
0237: typeInfo.lastIndexOf(")"));
0238: java.util.StringTokenizer tokenizer = new java.util.StringTokenizer(
0239: temp, ",");
0240: int maxLength = 0;
0241:
0242: while (tokenizer.hasMoreTokens()) {
0243: maxLength = Math.max(maxLength, (tokenizer
0244: .nextToken().length() - 2));
0245: }
0246:
0247: this .columnSize = Constants.integerValueOf(maxLength);
0248: this .decimalDigits = null;
0249: } else if (StringUtils
0250: .startsWithIgnoreCase(typeInfo, "set")) {
0251: String temp = typeInfo.substring(typeInfo.indexOf("("),
0252: typeInfo.lastIndexOf(")"));
0253: java.util.StringTokenizer tokenizer = new java.util.StringTokenizer(
0254: temp, ",");
0255: int maxLength = 0;
0256:
0257: while (tokenizer.hasMoreTokens()) {
0258: String setMember = tokenizer.nextToken().trim();
0259:
0260: if (setMember.startsWith("'")
0261: && setMember.endsWith("'")) {
0262: maxLength += setMember.length() - 2;
0263: } else {
0264: maxLength += setMember.length();
0265: }
0266: }
0267:
0268: this .columnSize = Constants.integerValueOf(maxLength);
0269: this .decimalDigits = null;
0270: } else if (typeInfo.indexOf(",") != -1) {
0271: // Numeric with decimals
0272: this .columnSize = Integer.valueOf(typeInfo.substring(
0273: (typeInfo.indexOf("(") + 1),
0274: (typeInfo.indexOf(","))).trim());
0275: this .decimalDigits = Integer.valueOf(typeInfo
0276: .substring((typeInfo.indexOf(",") + 1),
0277: (typeInfo.indexOf(")"))).trim());
0278: } else {
0279: this .columnSize = null;
0280: this .decimalDigits = null;
0281:
0282: /* If the size is specified with the DDL, use that */
0283: if ((StringUtils.indexOfIgnoreCase(typeInfo, "char") != -1
0284: || StringUtils.indexOfIgnoreCase(typeInfo,
0285: "text") != -1
0286: || StringUtils.indexOfIgnoreCase(typeInfo,
0287: "blob") != -1
0288: || StringUtils.indexOfIgnoreCase(typeInfo,
0289: "binary") != -1 || StringUtils
0290: .indexOfIgnoreCase(typeInfo, "bit") != -1)
0291: && typeInfo.indexOf("(") != -1) {
0292: int endParenIndex = typeInfo.indexOf(")");
0293:
0294: if (endParenIndex == -1) {
0295: endParenIndex = typeInfo.length();
0296: }
0297:
0298: this .columnSize = Integer.valueOf(typeInfo
0299: .substring((typeInfo.indexOf("(") + 1),
0300: endParenIndex).trim());
0301:
0302: // Adjust for pseudo-boolean
0303: if (conn.getTinyInt1isBit()
0304: && this .columnSize.intValue() == 1
0305: && StringUtils.startsWithIgnoreCase(
0306: typeInfo, 0, "tinyint")) {
0307: if (conn.getTransformedBitIsBoolean()) {
0308: this .dataType = Types.BOOLEAN;
0309: this .typeName = "BOOLEAN";
0310: } else {
0311: this .dataType = Types.BIT;
0312: this .typeName = "BIT";
0313: }
0314: }
0315: } else if (StringUtils.startsWithIgnoreCaseAndWs(
0316: typeInfo, "tinyint")) {
0317: if (conn.getTinyInt1isBit()
0318: && typeInfo.indexOf("(1)") != -1) {
0319: if (conn.getTransformedBitIsBoolean()) {
0320: this .dataType = Types.BOOLEAN;
0321: this .typeName = "BOOLEAN";
0322: } else {
0323: this .dataType = Types.BIT;
0324: this .typeName = "BIT";
0325: }
0326: } else {
0327: this .columnSize = Constants.integerValueOf(3);
0328: this .decimalDigits = Constants
0329: .integerValueOf(0);
0330: }
0331: } else if (StringUtils.startsWithIgnoreCaseAndWs(
0332: typeInfo, "smallint")) {
0333: this .columnSize = Constants.integerValueOf(5);
0334: this .decimalDigits = Constants.integerValueOf(0);
0335: } else if (StringUtils.startsWithIgnoreCaseAndWs(
0336: typeInfo, "mediumint")) {
0337: this .columnSize = Constants.integerValueOf(7);
0338: this .decimalDigits = Constants.integerValueOf(0);
0339: } else if (StringUtils.startsWithIgnoreCaseAndWs(
0340: typeInfo, "int")) {
0341: this .columnSize = Constants.integerValueOf(10);
0342: this .decimalDigits = Constants.integerValueOf(0);
0343: } else if (StringUtils.startsWithIgnoreCaseAndWs(
0344: typeInfo, "integer")) {
0345: this .columnSize = Constants.integerValueOf(10);
0346: this .decimalDigits = Constants.integerValueOf(0);
0347: } else if (StringUtils.startsWithIgnoreCaseAndWs(
0348: typeInfo, "bigint")) {
0349: this .columnSize = Constants.integerValueOf(19);
0350: this .decimalDigits = Constants.integerValueOf(0);
0351: } else if (StringUtils.startsWithIgnoreCaseAndWs(
0352: typeInfo, "int24")) {
0353: this .columnSize = Constants.integerValueOf(19);
0354: this .decimalDigits = Constants.integerValueOf(0);
0355: } else if (StringUtils.startsWithIgnoreCaseAndWs(
0356: typeInfo, "real")) {
0357: this .columnSize = Constants.integerValueOf(12);
0358: } else if (StringUtils.startsWithIgnoreCaseAndWs(
0359: typeInfo, "float")) {
0360: this .columnSize = Constants.integerValueOf(12);
0361: } else if (StringUtils.startsWithIgnoreCaseAndWs(
0362: typeInfo, "decimal")) {
0363: this .columnSize = Constants.integerValueOf(12);
0364: } else if (StringUtils.startsWithIgnoreCaseAndWs(
0365: typeInfo, "numeric")) {
0366: this .columnSize = Constants.integerValueOf(12);
0367: } else if (StringUtils.startsWithIgnoreCaseAndWs(
0368: typeInfo, "double")) {
0369: this .columnSize = Constants.integerValueOf(22);
0370: } else if (StringUtils.startsWithIgnoreCaseAndWs(
0371: typeInfo, "char")) {
0372: this .columnSize = Constants.integerValueOf(1);
0373: } else if (StringUtils.startsWithIgnoreCaseAndWs(
0374: typeInfo, "varchar")) {
0375: this .columnSize = Constants.integerValueOf(255);
0376: } else if (StringUtils.startsWithIgnoreCaseAndWs(
0377: typeInfo, "date")) {
0378: this .columnSize = null;
0379: } else if (StringUtils.startsWithIgnoreCaseAndWs(
0380: typeInfo, "time")) {
0381: this .columnSize = null;
0382: } else if (StringUtils.startsWithIgnoreCaseAndWs(
0383: typeInfo, "timestamp")) {
0384: this .columnSize = null;
0385: } else if (StringUtils.startsWithIgnoreCaseAndWs(
0386: typeInfo, "datetime")) {
0387: this .columnSize = null;
0388: } else if (StringUtils.startsWithIgnoreCaseAndWs(
0389: typeInfo, "tinyblob")) {
0390: this .columnSize = Constants.integerValueOf(255);
0391: } else if (StringUtils.startsWithIgnoreCaseAndWs(
0392: typeInfo, "blob")) {
0393: this .columnSize = Constants.integerValueOf(65535);
0394: } else if (StringUtils.startsWithIgnoreCaseAndWs(
0395: typeInfo, "mediumblob")) {
0396: this .columnSize = Constants
0397: .integerValueOf(16777215);
0398: } else if (StringUtils.startsWithIgnoreCaseAndWs(
0399: typeInfo, "longblob")) {
0400: this .columnSize = Constants
0401: .integerValueOf(Integer.MAX_VALUE);
0402: } else if (StringUtils.startsWithIgnoreCaseAndWs(
0403: typeInfo, "tinytext")) {
0404: this .columnSize = Constants.integerValueOf(255);
0405: } else if (StringUtils.startsWithIgnoreCaseAndWs(
0406: typeInfo, "text")) {
0407: this .columnSize = Constants.integerValueOf(65535);
0408: } else if (StringUtils.startsWithIgnoreCaseAndWs(
0409: typeInfo, "mediumtext")) {
0410: this .columnSize = Constants
0411: .integerValueOf(16777215);
0412: } else if (StringUtils.startsWithIgnoreCaseAndWs(
0413: typeInfo, "longtext")) {
0414: this .columnSize = Constants
0415: .integerValueOf(Integer.MAX_VALUE);
0416: } else if (StringUtils.startsWithIgnoreCaseAndWs(
0417: typeInfo, "enum")) {
0418: this .columnSize = Constants.integerValueOf(255);
0419: } else if (StringUtils.startsWithIgnoreCaseAndWs(
0420: typeInfo, "set")) {
0421: this .columnSize = Constants.integerValueOf(255);
0422: }
0423:
0424: }
0425:
0426: // BUFFER_LENGTH
0427: this .bufferLength = MysqlIO.getMaxBuf();
0428:
0429: // NUM_PREC_RADIX (is this right for char?)
0430: this .numPrecRadix = 10;
0431:
0432: // Nullable?
0433: if (nullabilityInfo != null) {
0434: if (nullabilityInfo.equals("YES")) {
0435: this .nullability = java.sql.DatabaseMetaData.columnNullable;
0436: this .isNullable = "YES";
0437:
0438: // IS_NULLABLE
0439: } else {
0440: this .nullability = java.sql.DatabaseMetaData.columnNoNulls;
0441: this .isNullable = "NO";
0442: }
0443: } else {
0444: this .nullability = java.sql.DatabaseMetaData.columnNoNulls;
0445: this .isNullable = "NO";
0446: }
0447: }
0448: }
0449:
0450: private static String mysqlKeywordsThatArentSQL92;
0451:
0452: protected static final int MAX_IDENTIFIER_LENGTH = 64;
0453:
0454: private static final int DEFERRABILITY = 13;
0455:
0456: private static final int DELETE_RULE = 10;
0457:
0458: private static final int FK_NAME = 11;
0459:
0460: private static final int FKCOLUMN_NAME = 7;
0461:
0462: private static final int FKTABLE_CAT = 4;
0463:
0464: private static final int FKTABLE_NAME = 6;
0465:
0466: private static final int FKTABLE_SCHEM = 5;
0467:
0468: private static final int KEY_SEQ = 8;
0469:
0470: private static final int PK_NAME = 12;
0471:
0472: private static final int PKCOLUMN_NAME = 3;
0473:
0474: //
0475: // Column indexes used by all DBMD foreign key
0476: // ResultSets
0477: //
0478: private static final int PKTABLE_CAT = 0;
0479:
0480: private static final int PKTABLE_NAME = 2;
0481:
0482: private static final int PKTABLE_SCHEM = 1;
0483:
0484: /** The table type for generic tables that support foreign keys. */
0485: private static final String SUPPORTS_FK = "SUPPORTS_FK";
0486:
0487: private static final byte[] TABLE_AS_BYTES = "TABLE".getBytes();
0488:
0489: private static final int UPDATE_RULE = 9;
0490:
0491: private static final byte[] VIEW_AS_BYTES = "VIEW".getBytes();
0492:
0493: private static final Constructor JDBC_4_DBMD_SHOW_CTOR;
0494:
0495: private static final Constructor JDBC_4_DBMD_IS_CTOR;
0496:
0497: static {
0498: if (Util.isJdbc4()) {
0499: try {
0500: JDBC_4_DBMD_SHOW_CTOR = Class
0501: .forName("com.mysql.jdbc.JDBC4DatabaseMetaData")
0502: .getConstructor(
0503: new Class[] {
0504: com.mysql.jdbc.ConnectionImpl.class,
0505: String.class });
0506: JDBC_4_DBMD_IS_CTOR = Class
0507: .forName(
0508: "com.mysql.jdbc.JDBC4DatabaseMetaDataUsingInfoSchema")
0509: .getConstructor(
0510: new Class[] {
0511: com.mysql.jdbc.ConnectionImpl.class,
0512: String.class });
0513: } catch (SecurityException e) {
0514: throw new RuntimeException(e);
0515: } catch (NoSuchMethodException e) {
0516: throw new RuntimeException(e);
0517: } catch (ClassNotFoundException e) {
0518: throw new RuntimeException(e);
0519: }
0520: } else {
0521: JDBC_4_DBMD_IS_CTOR = null;
0522: JDBC_4_DBMD_SHOW_CTOR = null;
0523: }
0524:
0525: // Current as-of MySQL-5.1.16
0526: String[] allMySQLKeywords = new String[] { "ACCESSIBLE", "ADD",
0527: "ALL", "ALTER", "ANALYZE", "AND", "AS", "ASC",
0528: "ASENSITIVE", "BEFORE", "BETWEEN", "BIGINT", "BINARY",
0529: "BLOB", "BOTH", "BY", "CALL", "CASCADE", "CASE",
0530: "CHANGE", "CHAR", "CHARACTER", "CHECK", "COLLATE",
0531: "COLUMN", "CONDITION", "CONNECTION", "CONSTRAINT",
0532: "CONTINUE", "CONVERT", "CREATE", "CROSS",
0533: "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP",
0534: "CURRENT_USER", "CURSOR", "DATABASE", "DATABASES",
0535: "DAY_HOUR", "DAY_MICROSECOND", "DAY_MINUTE",
0536: "DAY_SECOND", "DEC", "DECIMAL", "DECLARE", "DEFAULT",
0537: "DELAYED", "DELETE", "DESC", "DESCRIBE",
0538: "DETERMINISTIC", "DISTINCT", "DISTINCTROW", "DIV",
0539: "DOUBLE", "DROP", "DUAL", "EACH", "ELSE", "ELSEIF",
0540: "ENCLOSED", "ESCAPED", "EXISTS", "EXIT", "EXPLAIN",
0541: "FALSE", "FETCH", "FLOAT", "FLOAT4", "FLOAT8", "FOR",
0542: "FORCE", "FOREIGN", "FROM", "FULLTEXT", "GRANT",
0543: "GROUP", "HAVING", "HIGH_PRIORITY", "HOUR_MICROSECOND",
0544: "HOUR_MINUTE", "HOUR_SECOND", "IF", "IGNORE", "IN",
0545: "INDEX", "INFILE", "INNER", "INOUT", "INSENSITIVE",
0546: "INSERT", "INT", "INT1", "INT2", "INT3", "INT4",
0547: "INT8", "INTEGER", "INTERVAL", "INTO", "IS", "ITERATE",
0548: "JOIN", "KEY", "KEYS", "KILL", "LEADING", "LEAVE",
0549: "LEFT", "LIKE", "LIMIT", "LINEAR", "LINES", "LOAD",
0550: "LOCALTIME", "LOCALTIMESTAMP", "LOCK", "LONG",
0551: "LONGBLOB", "LONGTEXT", "LOOP", "LOW_PRIORITY",
0552: "MATCH", "MEDIUMBLOB", "MEDIUMINT", "MEDIUMTEXT",
0553: "MIDDLEINT", "MINUTE_MICROSECOND", "MINUTE_SECOND",
0554: "MOD", "MODIFIES", "NATURAL", "NOT",
0555: "NO_WRITE_TO_BINLOG", "NULL", "NUMERIC", "ON",
0556: "OPTIMIZE", "OPTION", "OPTIONALLY", "OR", "ORDER",
0557: "OUT", "OUTER", "OUTFILE", "PRECISION", "PRIMARY",
0558: "PROCEDURE", "PURGE", "RANGE", "READ", "READS",
0559: "READ_ONLY", "READ_WRITE", "REAL", "REFERENCES",
0560: "REGEXP", "RELEASE", "RENAME", "REPEAT", "REPLACE",
0561: "REQUIRE", "RESTRICT", "RETURN", "REVOKE", "RIGHT",
0562: "RLIKE", "SCHEMA", "SCHEMAS", "SECOND_MICROSECOND",
0563: "SELECT", "SENSITIVE", "SEPARATOR", "SET", "SHOW",
0564: "SMALLINT", "SPATIAL", "SPECIFIC", "SQL",
0565: "SQLEXCEPTION", "SQLSTATE", "SQLWARNING",
0566: "SQL_BIG_RESULT", "SQL_CALC_FOUND_ROWS",
0567: "SQL_SMALL_RESULT", "SSL", "STARTING", "STRAIGHT_JOIN",
0568: "TABLE", "TERMINATED", "THEN", "TINYBLOB", "TINYINT",
0569: "TINYTEXT", "TO", "TRAILING", "TRIGGER", "TRUE",
0570: "UNDO", "UNION", "UNIQUE", "UNLOCK", "UNSIGNED",
0571: "UPDATE", "USAGE", "USE", "USING", "UTC_DATE",
0572: "UTC_TIME", "UTC_TIMESTAMP", "VALUES", "VARBINARY",
0573: "VARCHAR", "VARCHARACTER", "VARYING", "WHEN", "WHERE",
0574: "WHILE", "WITH", "WRITE", "X509", "XOR", "YEAR_MONTH",
0575: "ZEROFILL" };
0576:
0577: String[] sql92Keywords = new String[] { "ABSOLUTE", "EXEC",
0578: "OVERLAPS", "ACTION", "EXECUTE", "PAD", "ADA",
0579: "EXISTS", "PARTIAL", "ADD", "EXTERNAL", "PASCAL",
0580: "ALL", "EXTRACT", "POSITION", "ALLOCATE", "FALSE",
0581: "PRECISION", "ALTER", "FETCH", "PREPARE", "AND",
0582: "FIRST", "PRESERVE", "ANY", "FLOAT", "PRIMARY", "ARE",
0583: "FOR", "PRIOR", "AS", "FOREIGN", "PRIVILEGES", "ASC",
0584: "FORTRAN", "PROCEDURE", "ASSERTION", "FOUND", "PUBLIC",
0585: "AT", "FROM", "READ", "AUTHORIZATION", "FULL", "REAL",
0586: "AVG", "GET", "REFERENCES", "BEGIN", "GLOBAL",
0587: "RELATIVE", "BETWEEN", "GO", "RESTRICT", "BIT", "GOTO",
0588: "REVOKE", "BIT_LENGTH", "GRANT", "RIGHT", "BOTH",
0589: "GROUP", "ROLLBACK", "BY", "HAVING", "ROWS", "CASCADE",
0590: "HOUR", "SCHEMA", "CASCADED", "IDENTITY", "SCROLL",
0591: "CASE", "IMMEDIATE", "SECOND", "CAST", "IN", "SECTION",
0592: "CATALOG", "INCLUDE", "SELECT", "CHAR", "INDEX",
0593: "SESSION", "CHAR_LENGTH", "INDICATOR", "SESSION_USER",
0594: "CHARACTER", "INITIALLY", "SET", "CHARACTER_LENGTH",
0595: "INNER", "SIZE", "CHECK", "INPUT", "SMALLINT", "CLOSE",
0596: "INSENSITIVE", "SOME", "COALESCE", "INSERT", "SPACE",
0597: "COLLATE", "INT", "SQL", "COLLATION", "INTEGER",
0598: "SQLCA", "COLUMN", "INTERSECT", "SQLCODE", "COMMIT",
0599: "INTERVAL", "SQLERROR", "CONNECT", "INTO", "SQLSTATE",
0600: "CONNECTION", "IS", "SQLWARNING", "CONSTRAINT",
0601: "ISOLATION", "SUBSTRING", "CONSTRAINTS", "JOIN", "SUM",
0602: "CONTINUE", "KEY", "SYSTEM_USER", "CONVERT",
0603: "LANGUAGE", "TABLE", "CORRESPONDING", "LAST",
0604: "TEMPORARY", "COUNT", "LEADING", "THEN", "CREATE",
0605: "LEFT", "TIME", "CROSS", "LEVEL", "TIMESTAMP",
0606: "CURRENT", "LIKE", "TIMEZONE_HOUR", "CURRENT_DATE",
0607: "LOCAL", "TIMEZONE_MINUTE", "CURRENT_TIME", "LOWER",
0608: "TO", "CURRENT_TIMESTAMP", "MATCH", "TRAILING",
0609: "CURRENT_USER", "MAX", "TRANSACTION", "CURSOR", "MIN",
0610: "TRANSLATE", "DATE", "MINUTE", "TRANSLATION", "DAY",
0611: "MODULE", "TRIM", "DEALLOCATE", "MONTH", "TRUE", "DEC",
0612: "NAMES", "UNION", "DECIMAL", "NATIONAL", "UNIQUE",
0613: "DECLARE", "NATURAL", "UNKNOWN", "DEFAULT", "NCHAR",
0614: "UPDATE", "DEFERRABLE", "NEXT", "UPPER", "DEFERRED",
0615: "NO", "USAGE", "DELETE", "NONE", "USER", "DESC", "NOT",
0616: "USING", "DESCRIBE", "NULL", "VALUE", "DESCRIPTOR",
0617: "NULLIF", "VALUES", "DIAGNOSTICS", "NUMERIC",
0618: "VARCHAR", "DISCONNECT", "OCTET_LENGTH", "VARYING",
0619: "DISTINCT", "OF", "VIEW", "DOMAIN", "ON", "WHEN",
0620: "DOUBLE", "ONLY", "WHENEVER", "DROP", "OPEN", "WHERE",
0621: "ELSE", "OPTION", "WITH", "END", "OR", "WORK",
0622: "END-EXEC", "ORDER", "WRITE", "ESCAPE", "OUTER",
0623: "YEAR", "EXCEPT", "OUTPUT", "ZONE", "EXCEPTION" };
0624:
0625: TreeMap mySQLKeywordMap = new TreeMap();
0626:
0627: for (int i = 0; i < allMySQLKeywords.length; i++) {
0628: mySQLKeywordMap.put(allMySQLKeywords[i], null);
0629: }
0630:
0631: HashMap sql92KeywordMap = new HashMap(sql92Keywords.length);
0632:
0633: for (int i = 0; i < sql92Keywords.length; i++) {
0634: sql92KeywordMap.put(sql92Keywords[i], null);
0635: }
0636:
0637: Iterator it = sql92KeywordMap.keySet().iterator();
0638:
0639: while (it.hasNext()) {
0640: mySQLKeywordMap.remove(it.next());
0641: }
0642:
0643: StringBuffer keywordBuf = new StringBuffer();
0644:
0645: it = mySQLKeywordMap.keySet().iterator();
0646:
0647: if (it.hasNext()) {
0648: keywordBuf.append(it.next().toString());
0649: }
0650:
0651: while (it.hasNext()) {
0652: keywordBuf.append(",");
0653: keywordBuf.append(it.next().toString());
0654: }
0655:
0656: mysqlKeywordsThatArentSQL92 = keywordBuf.toString();
0657: }
0658:
0659: /** The connection to the database */
0660: protected ConnectionImpl conn;
0661:
0662: /** The 'current' database name being used */
0663: protected String database = null;
0664:
0665: /** What character to use when quoting identifiers */
0666: protected String quotedId = null;
0667:
0668: // We need to provide factory-style methods so we can support both JDBC3 (and older)
0669: // and JDBC4 runtimes, otherwise the class verifier complains...
0670:
0671: protected static DatabaseMetaData getInstance(
0672: ConnectionImpl connToSet, String databaseToSet)
0673: throws SQLException {
0674: if (!Util.isJdbc4()) {
0675: if (connToSet != null
0676: && connToSet.getUseInformationSchema()
0677: && connToSet.versionMeetsMinimum(5, 0, 7)) {
0678: return new DatabaseMetaDataUsingInfoSchema(connToSet,
0679: databaseToSet);
0680: }
0681:
0682: return new DatabaseMetaData(connToSet, databaseToSet);
0683: }
0684:
0685: if (connToSet != null && connToSet.getUseInformationSchema()
0686: && connToSet.versionMeetsMinimum(5, 0, 7)) {
0687:
0688: return (DatabaseMetaData) Util.handleNewInstance(
0689: JDBC_4_DBMD_IS_CTOR, new Object[] { connToSet,
0690: databaseToSet });
0691: }
0692:
0693: return (DatabaseMetaData) Util.handleNewInstance(
0694: JDBC_4_DBMD_SHOW_CTOR, new Object[] { connToSet,
0695: databaseToSet });
0696: }
0697:
0698: /**
0699: * Creates a new DatabaseMetaData object.
0700: *
0701: * @param connToSet
0702: * DOCUMENT ME!
0703: * @param databaseToSet
0704: * DOCUMENT ME!
0705: */
0706: protected DatabaseMetaData(ConnectionImpl connToSet,
0707: String databaseToSet) {
0708: this .conn = connToSet;
0709: this .database = databaseToSet;
0710:
0711: try {
0712: this .quotedId = this .conn.supportsQuotedIdentifiers() ? getIdentifierQuoteString()
0713: : "";
0714: } catch (SQLException sqlEx) {
0715: // Forced by API, never thrown from getIdentifierQuoteString() in
0716: // this
0717: // implementation.
0718: AssertionFailedException.shouldNotHappen(sqlEx);
0719: }
0720: }
0721:
0722: /**
0723: * Can all the procedures returned by getProcedures be called by the current
0724: * user?
0725: *
0726: * @return true if so
0727: * @throws SQLException
0728: * DOCUMENT ME!
0729: */
0730: public boolean allProceduresAreCallable() throws SQLException {
0731: return false;
0732: }
0733:
0734: /**
0735: * Can all the tables returned by getTable be SELECTed by the current user?
0736: *
0737: * @return true if so
0738: * @throws SQLException
0739: * DOCUMENT ME!
0740: */
0741: public boolean allTablesAreSelectable() throws SQLException {
0742: return false;
0743: }
0744:
0745: private java.sql.ResultSet buildResultSet(
0746: com.mysql.jdbc.Field[] fields, java.util.ArrayList rows)
0747: throws SQLException {
0748: return buildResultSet(fields, rows, this .conn);
0749: }
0750:
0751: static java.sql.ResultSet buildResultSet(
0752: com.mysql.jdbc.Field[] fields, java.util.ArrayList rows,
0753: ConnectionImpl c) throws SQLException {
0754: int fieldsLength = fields.length;
0755:
0756: for (int i = 0; i < fieldsLength; i++) {
0757: fields[i].setConnection(c);
0758: fields[i].setUseOldNameMetadata(true);
0759: }
0760:
0761: return com.mysql.jdbc.ResultSetImpl.getInstance(c.getCatalog(),
0762: fields, new RowDataStatic(rows), c, null, false);
0763: }
0764:
0765: private void convertToJdbcFunctionList(String catalog,
0766: ResultSet proceduresRs, boolean needsClientFiltering,
0767: String db, Map procedureRowsOrderedByName, int nameIndex,
0768: Field[] fields) throws SQLException {
0769: while (proceduresRs.next()) {
0770: boolean shouldAdd = true;
0771:
0772: if (needsClientFiltering) {
0773: shouldAdd = false;
0774:
0775: String procDb = proceduresRs.getString(1);
0776:
0777: if (db == null && procDb == null) {
0778: shouldAdd = true;
0779: } else if (db != null && db.equals(procDb)) {
0780: shouldAdd = true;
0781: }
0782: }
0783:
0784: if (shouldAdd) {
0785: String functionName = proceduresRs.getString(nameIndex);
0786:
0787: byte[][] rowData = null;
0788:
0789: if (fields != null && fields.length == 8) {
0790:
0791: rowData = new byte[8][];
0792: rowData[0] = catalog == null ? null : s2b(catalog); // PROCEDURE_CAT
0793: rowData[1] = null; // PROCEDURE_SCHEM
0794: rowData[2] = s2b(functionName); // PROCEDURE_NAME
0795: rowData[3] = null; // reserved1
0796: rowData[4] = null; // reserved2
0797: rowData[5] = null; // reserved3
0798: rowData[6] = s2b(proceduresRs.getString("comment")); // REMARKS
0799: rowData[7] = s2b(Integer
0800: .toString(procedureReturnsResult)); // PROCEDURE_TYPE
0801: } else {
0802:
0803: rowData = new byte[6][];
0804:
0805: rowData[0] = catalog == null ? null : s2b(catalog); // FUNCTION_CAT
0806: rowData[1] = null; // FUNCTION_SCHEM
0807: rowData[2] = s2b(functionName); // FUNCTION_NAME
0808: rowData[3] = s2b(proceduresRs.getString("comment")); // REMARKS
0809: rowData[4] = s2b(Integer
0810: .toString(getJDBC4FunctionNoTableConstant())); // FUNCTION_TYPE
0811: rowData[5] = s2b(functionName); // SPECFIC NAME
0812: }
0813:
0814: procedureRowsOrderedByName.put(functionName,
0815: new ByteArrayRow(rowData));
0816: }
0817: }
0818: }
0819:
0820: protected int getJDBC4FunctionNoTableConstant() {
0821: return 0;
0822: }
0823:
0824: private void convertToJdbcProcedureList(boolean fromSelect,
0825: String catalog, ResultSet proceduresRs,
0826: boolean needsClientFiltering, String db,
0827: Map procedureRowsOrderedByName, int nameIndex)
0828: throws SQLException {
0829: while (proceduresRs.next()) {
0830: boolean shouldAdd = true;
0831:
0832: if (needsClientFiltering) {
0833: shouldAdd = false;
0834:
0835: String procDb = proceduresRs.getString(1);
0836:
0837: if (db == null && procDb == null) {
0838: shouldAdd = true;
0839: } else if (db != null && db.equals(procDb)) {
0840: shouldAdd = true;
0841: }
0842: }
0843:
0844: if (shouldAdd) {
0845: String procedureName = proceduresRs
0846: .getString(nameIndex);
0847: byte[][] rowData = new byte[8][];
0848: rowData[0] = catalog == null ? null : s2b(catalog);
0849: rowData[1] = null;
0850: rowData[2] = s2b(procedureName);
0851: rowData[3] = null;
0852: rowData[4] = null;
0853: rowData[5] = null;
0854: rowData[6] = null;
0855:
0856: boolean isFunction = fromSelect ? "FUNCTION"
0857: .equalsIgnoreCase(proceduresRs
0858: .getString("type")) : false;
0859: rowData[7] = s2b(isFunction ? Integer
0860: .toString(procedureReturnsResult) : Integer
0861: .toString(procedureResultUnknown));
0862:
0863: procedureRowsOrderedByName.put(procedureName,
0864: new ByteArrayRow(rowData));
0865: }
0866: }
0867: }
0868:
0869: private ResultSetRow convertTypeDescriptorToProcedureRow(
0870: byte[] procNameAsBytes, String paramName,
0871: boolean isOutParam, boolean isInParam,
0872: boolean isReturnParam, TypeDescriptor typeDesc,
0873: boolean forGetFunctionColumns, int ordinal)
0874: throws SQLException {
0875: byte[][] row = forGetFunctionColumns ? new byte[17][]
0876: : new byte[14][];
0877: row[0] = null; // PROCEDURE_CAT
0878: row[1] = null; // PROCEDURE_SCHEM
0879: row[2] = procNameAsBytes; // PROCEDURE/NAME
0880: row[3] = s2b(paramName); // COLUMN_NAME
0881: // COLUMN_TYPE
0882:
0883: // NOTE: For JDBC-4.0, we luck out here for functions
0884: // because the values are the same for functionColumn....
0885: // and they're not using Enumerations....
0886:
0887: if (isInParam && isOutParam) {
0888: row[4] = s2b(String.valueOf(procedureColumnInOut));
0889: } else if (isInParam) {
0890: row[4] = s2b(String.valueOf(procedureColumnIn));
0891: } else if (isOutParam) {
0892: row[4] = s2b(String.valueOf(procedureColumnOut));
0893: } else if (isReturnParam) {
0894: row[4] = s2b(String.valueOf(procedureColumnReturn));
0895: } else {
0896: row[4] = s2b(String.valueOf(procedureColumnUnknown));
0897: }
0898: row[5] = s2b(Short.toString(typeDesc.dataType)); // DATA_TYPE
0899: row[6] = s2b(typeDesc.typeName); // TYPE_NAME
0900: row[7] = typeDesc.columnSize == null ? null
0901: : s2b(typeDesc.columnSize.toString()); // PRECISION
0902: row[8] = s2b(Integer.toString(typeDesc.bufferLength)); // LENGTH
0903: row[9] = typeDesc.decimalDigits == null ? null
0904: : s2b(typeDesc.decimalDigits.toString()); // SCALE
0905: row[10] = s2b(Integer.toString(typeDesc.numPrecRadix)); // RADIX
0906: // Map 'column****' to 'procedure****'
0907: switch (typeDesc.nullability) {
0908: case columnNoNulls:
0909: row[11] = s2b(String.valueOf(procedureNoNulls)); // NULLABLE
0910:
0911: break;
0912:
0913: case columnNullable:
0914: row[11] = s2b(String.valueOf(procedureNullable)); // NULLABLE
0915:
0916: break;
0917:
0918: case columnNullableUnknown:
0919: row[11] = s2b(String.valueOf(procedureNullableUnknown)); // nullable
0920:
0921: break;
0922:
0923: default:
0924: throw SQLError
0925: .createSQLException(
0926: "Internal error while parsing callable statement metadata (unknown nullability value fount)",
0927: SQLError.SQL_STATE_GENERAL_ERROR);
0928: }
0929:
0930: row[12] = null;
0931:
0932: if (forGetFunctionColumns) {
0933: // CHAR_OCTECT_LENGTH
0934: row[13] = null;
0935:
0936: // ORDINAL_POSITION
0937: row[14] = s2b(String.valueOf(ordinal));
0938:
0939: // IS_NULLABLE
0940: row[15] = Constants.EMPTY_BYTE_ARRAY;
0941:
0942: row[16] = s2b(paramName);
0943: }
0944:
0945: return new ByteArrayRow(row);
0946: }
0947:
0948: /**
0949: * Does a data definition statement within a transaction force the
0950: * transaction to commit?
0951: *
0952: * @return true if so
0953: * @throws SQLException
0954: * DOCUMENT ME!
0955: */
0956: public boolean dataDefinitionCausesTransactionCommit()
0957: throws SQLException {
0958: return true;
0959: }
0960:
0961: /**
0962: * Is a data definition statement within a transaction ignored?
0963: *
0964: * @return true if so
0965: * @throws SQLException
0966: * DOCUMENT ME!
0967: */
0968: public boolean dataDefinitionIgnoredInTransactions()
0969: throws SQLException {
0970: return false;
0971: }
0972:
0973: /**
0974: * JDBC 2.0 Determine whether or not a visible row delete can be detected by
0975: * calling ResultSet.rowDeleted(). If deletesAreDetected() returns false,
0976: * then deleted rows are removed from the result set.
0977: *
0978: * @param type
0979: * set type, i.e. ResultSet.TYPE_XXX
0980: * @return true if changes are detected by the resultset type
0981: * @exception SQLException
0982: * if a database-access error occurs.
0983: */
0984: public boolean deletesAreDetected(int type) throws SQLException {
0985: return false;
0986: }
0987:
0988: // ----------------------------------------------------------------------
0989:
0990: /**
0991: * Did getMaxRowSize() include LONGVARCHAR and LONGVARBINARY blobs?
0992: *
0993: * @return true if so
0994: * @throws SQLException
0995: * DOCUMENT ME!
0996: */
0997: public boolean doesMaxRowSizeIncludeBlobs() throws SQLException {
0998: return true;
0999: }
1000:
1001: /**
1002: * Extracts foreign key info for one table.
1003: *
1004: * @param rows
1005: * the list of rows to add to
1006: * @param rs
1007: * the result set from 'SHOW CREATE TABLE'
1008: * @param catalog
1009: * the database name
1010: * @return the list of rows with new rows added
1011: * @throws SQLException
1012: * if a database access error occurs
1013: */
1014: public List extractForeignKeyForTable(ArrayList rows,
1015: java.sql.ResultSet rs, String catalog) throws SQLException {
1016: byte[][] row = new byte[3][];
1017: row[0] = rs.getBytes(1);
1018: row[1] = s2b(SUPPORTS_FK);
1019:
1020: String createTableString = rs.getString(2);
1021: StringTokenizer lineTokenizer = new StringTokenizer(
1022: createTableString, "\n");
1023: StringBuffer commentBuf = new StringBuffer("comment; ");
1024: boolean firstTime = true;
1025:
1026: String quoteChar = getIdentifierQuoteString();
1027:
1028: if (quoteChar == null) {
1029: quoteChar = "`";
1030: }
1031:
1032: while (lineTokenizer.hasMoreTokens()) {
1033: String line = lineTokenizer.nextToken().trim();
1034:
1035: String constraintName = null;
1036:
1037: if (StringUtils.startsWithIgnoreCase(line, "CONSTRAINT")) {
1038: boolean usingBackTicks = true;
1039: int beginPos = line.indexOf(quoteChar);
1040:
1041: if (beginPos == -1) {
1042: beginPos = line.indexOf("\"");
1043: usingBackTicks = false;
1044: }
1045:
1046: if (beginPos != -1) {
1047: int endPos = -1;
1048:
1049: if (usingBackTicks) {
1050: endPos = line.indexOf(quoteChar, beginPos + 1);
1051: } else {
1052: endPos = line.indexOf("\"", beginPos + 1);
1053: }
1054:
1055: if (endPos != -1) {
1056: constraintName = line.substring(beginPos + 1,
1057: endPos);
1058: line = line
1059: .substring(endPos + 1, line.length())
1060: .trim();
1061: }
1062: }
1063: }
1064:
1065: if (line.startsWith("FOREIGN KEY")) {
1066: if (line.endsWith(",")) {
1067: line = line.substring(0, line.length() - 1);
1068: }
1069:
1070: char quote = this .quotedId.charAt(0);
1071:
1072: int indexOfFK = line.indexOf("FOREIGN KEY");
1073:
1074: String localColumnName = null;
1075: String referencedCatalogName = this .quotedId + catalog
1076: + this .quotedId;
1077: String referencedTableName = null;
1078: String referencedColumnName = null;
1079:
1080: if (indexOfFK != -1) {
1081: int afterFk = indexOfFK + "FOREIGN KEY".length();
1082:
1083: int indexOfRef = StringUtils
1084: .indexOfIgnoreCaseRespectQuotes(afterFk,
1085: line, "REFERENCES", quote, true);
1086:
1087: if (indexOfRef != -1) {
1088:
1089: int indexOfParenOpen = line.indexOf('(',
1090: afterFk);
1091: int indexOfParenClose = StringUtils
1092: .indexOfIgnoreCaseRespectQuotes(
1093: indexOfParenOpen, line, ")",
1094: quote, true);
1095:
1096: if (indexOfParenOpen == -1
1097: || indexOfParenClose == -1) {
1098: // throw SQLError.createSQLException();
1099: }
1100:
1101: localColumnName = line
1102: .substring(indexOfParenOpen + 1,
1103: indexOfParenClose);
1104:
1105: int afterRef = indexOfRef
1106: + "REFERENCES".length();
1107:
1108: int referencedColumnBegin = StringUtils
1109: .indexOfIgnoreCaseRespectQuotes(
1110: afterRef, line, "(", quote,
1111: true);
1112:
1113: if (referencedColumnBegin != -1) {
1114: referencedTableName = line.substring(
1115: afterRef, referencedColumnBegin);
1116:
1117: int referencedColumnEnd = StringUtils
1118: .indexOfIgnoreCaseRespectQuotes(
1119: referencedColumnBegin + 1,
1120: line, ")", quote, true);
1121:
1122: if (referencedColumnEnd != -1) {
1123: referencedColumnName = line.substring(
1124: referencedColumnBegin + 1,
1125: referencedColumnEnd);
1126: }
1127:
1128: int indexOfCatalogSep = StringUtils
1129: .indexOfIgnoreCaseRespectQuotes(0,
1130: referencedTableName, ".",
1131: quote, true);
1132:
1133: if (indexOfCatalogSep != -1) {
1134: referencedCatalogName = referencedTableName
1135: .substring(0, indexOfCatalogSep);
1136: referencedTableName = referencedTableName
1137: .substring(indexOfCatalogSep + 1);
1138: }
1139: }
1140: }
1141: }
1142:
1143: if (!firstTime) {
1144: commentBuf.append("; ");
1145: } else {
1146: firstTime = false;
1147: }
1148:
1149: if (constraintName != null) {
1150: commentBuf.append(constraintName);
1151: } else {
1152: commentBuf.append("not_available");
1153: }
1154:
1155: commentBuf.append("(");
1156: commentBuf.append(localColumnName);
1157: commentBuf.append(") REFER ");
1158: commentBuf.append(referencedCatalogName);
1159: commentBuf.append("/");
1160: commentBuf.append(referencedTableName);
1161: commentBuf.append("(");
1162: commentBuf.append(referencedColumnName);
1163: commentBuf.append(")");
1164:
1165: int lastParenIndex = line.lastIndexOf(")");
1166:
1167: if (lastParenIndex != (line.length() - 1)) {
1168: String cascadeOptions = cascadeOptions = line
1169: .substring(lastParenIndex + 1);
1170: commentBuf.append(" ");
1171: commentBuf.append(cascadeOptions);
1172: }
1173: }
1174: }
1175:
1176: row[2] = s2b(commentBuf.toString());
1177: rows.add(new ByteArrayRow(row));
1178:
1179: return rows;
1180: }
1181:
1182: /**
1183: * Creates a result set similar enough to 'SHOW TABLE STATUS' to allow the
1184: * same code to work on extracting the foreign key data
1185: *
1186: * @param connToUse
1187: * the database connection to use
1188: * @param metadata
1189: * the DatabaseMetaData instance calling this method
1190: * @param catalog
1191: * the database name to extract foreign key info for
1192: * @param tableName
1193: * the table to extract foreign key info for
1194: * @return A result set that has the structure of 'show table status'
1195: * @throws SQLException
1196: * if a database access error occurs.
1197: */
1198: public ResultSet extractForeignKeyFromCreateTable(String catalog,
1199: String tableName) throws SQLException {
1200: ArrayList tableList = new ArrayList();
1201: java.sql.ResultSet rs = null;
1202: java.sql.Statement stmt = null;
1203:
1204: if (tableName != null) {
1205: tableList.add(tableName);
1206: } else {
1207: try {
1208: rs = getTables(catalog, "", "%",
1209: new String[] { "TABLE" });
1210:
1211: while (rs.next()) {
1212: tableList.add(rs.getString("TABLE_NAME"));
1213: }
1214: } finally {
1215: if (rs != null) {
1216: rs.close();
1217: }
1218:
1219: rs = null;
1220: }
1221: }
1222:
1223: ArrayList rows = new ArrayList();
1224: Field[] fields = new Field[3];
1225: fields[0] = new Field("", "Name", Types.CHAR, Integer.MAX_VALUE);
1226: fields[1] = new Field("", "Type", Types.CHAR, 255);
1227: fields[2] = new Field("", "Comment", Types.CHAR,
1228: Integer.MAX_VALUE);
1229:
1230: int numTables = tableList.size();
1231: stmt = this .conn.getMetadataSafeStatement();
1232:
1233: String quoteChar = getIdentifierQuoteString();
1234:
1235: if (quoteChar == null) {
1236: quoteChar = "`";
1237: }
1238:
1239: try {
1240: for (int i = 0; i < numTables; i++) {
1241: String tableToExtract = (String) tableList.get(i);
1242:
1243: String query = new StringBuffer("SHOW CREATE TABLE ")
1244: .append(quoteChar).append(catalog).append(
1245: quoteChar).append(".")
1246: .append(quoteChar).append(tableToExtract)
1247: .append(quoteChar).toString();
1248:
1249: try {
1250: rs = stmt.executeQuery(query);
1251: } catch (SQLException sqlEx) {
1252: // Table might've disappeared on us, not really an error
1253: String sqlState = sqlEx.getSQLState();
1254:
1255: if (!"42S02".equals(sqlState)
1256: && sqlEx.getErrorCode() != MysqlErrorNumbers.ER_NO_SUCH_TABLE) {
1257: throw sqlEx;
1258: }
1259:
1260: continue;
1261: }
1262:
1263: while (rs.next()) {
1264: extractForeignKeyForTable(rows, rs, catalog);
1265: }
1266: }
1267: } finally {
1268: if (rs != null) {
1269: rs.close();
1270: }
1271:
1272: rs = null;
1273:
1274: if (stmt != null) {
1275: stmt.close();
1276: }
1277:
1278: stmt = null;
1279: }
1280:
1281: return buildResultSet(fields, rows);
1282: }
1283:
1284: /**
1285: * @see DatabaseMetaData#getAttributes(String, String, String, String)
1286: */
1287: public java.sql.ResultSet getAttributes(String arg0, String arg1,
1288: String arg2, String arg3) throws SQLException {
1289: Field[] fields = new Field[21];
1290: fields[0] = new Field("", "TYPE_CAT", Types.CHAR, 32);
1291: fields[1] = new Field("", "TYPE_SCHEM", Types.CHAR, 32);
1292: fields[2] = new Field("", "TYPE_NAME", Types.CHAR, 32);
1293: fields[3] = new Field("", "ATTR_NAME", Types.CHAR, 32);
1294: fields[4] = new Field("", "DATA_TYPE", Types.SMALLINT, 32);
1295: fields[5] = new Field("", "ATTR_TYPE_NAME", Types.CHAR, 32);
1296: fields[6] = new Field("", "ATTR_SIZE", Types.INTEGER, 32);
1297: fields[7] = new Field("", "DECIMAL_DIGITS", Types.INTEGER, 32);
1298: fields[8] = new Field("", "NUM_PREC_RADIX", Types.INTEGER, 32);
1299: fields[9] = new Field("", "NULLABLE ", Types.INTEGER, 32);
1300: fields[10] = new Field("", "REMARKS", Types.CHAR, 32);
1301: fields[11] = new Field("", "ATTR_DEF", Types.CHAR, 32);
1302: fields[12] = new Field("", "SQL_DATA_TYPE", Types.INTEGER, 32);
1303: fields[13] = new Field("", "SQL_DATETIME_SUB", Types.INTEGER,
1304: 32);
1305: fields[14] = new Field("", "CHAR_OCTET_LENGTH", Types.INTEGER,
1306: 32);
1307: fields[15] = new Field("", "ORDINAL_POSITION", Types.INTEGER,
1308: 32);
1309: fields[16] = new Field("", "IS_NULLABLE", Types.CHAR, 32);
1310: fields[17] = new Field("", "SCOPE_CATALOG", Types.CHAR, 32);
1311: fields[18] = new Field("", "SCOPE_SCHEMA", Types.CHAR, 32);
1312: fields[19] = new Field("", "SCOPE_TABLE", Types.CHAR, 32);
1313: fields[20] = new Field("", "SOURCE_DATA_TYPE", Types.SMALLINT,
1314: 32);
1315:
1316: return buildResultSet(fields, new ArrayList());
1317: }
1318:
1319: /**
1320: * Get a description of a table's optimal set of columns that uniquely
1321: * identifies a row. They are ordered by SCOPE.
1322: * <P>
1323: * Each column description has the following columns:
1324: * <OL>
1325: * <li> <B>SCOPE</B> short => actual scope of result
1326: * <UL>
1327: * <li> bestRowTemporary - very temporary, while using row </li>
1328: * <li> bestRowTransaction - valid for remainder of current transaction
1329: * </li>
1330: * <li> bestRowSession - valid for remainder of current session </li>
1331: * </ul>
1332: * </li>
1333: * <li> <B>COLUMN_NAME</B> String => column name </li>
1334: * <li> <B>DATA_TYPE</B> short => SQL data type from java.sql.Types </li>
1335: * <li> <B>TYPE_NAME</B> String => Data source dependent type name </li>
1336: * <li> <B>COLUMN_SIZE</B> int => precision </li>
1337: * <li> <B>BUFFER_LENGTH</B> int => not used </li>
1338: * <li> <B>DECIMAL_DIGITS</B> short => scale </li>
1339: * <li> <B>PSEUDO_COLUMN</B> short => is this a pseudo column like an
1340: * Oracle ROWID
1341: * <UL>
1342: * <li> bestRowUnknown - may or may not be pseudo column </li>
1343: * <li> bestRowNotPseudo - is NOT a pseudo column </li>
1344: * <li> bestRowPseudo - is a pseudo column </li>
1345: * </ul>
1346: * </li>
1347: * </ol>
1348: * </p>
1349: *
1350: * @param catalog
1351: * a catalog name; "" retrieves those without a catalog
1352: * @param schema
1353: * a schema name; "" retrieves those without a schema
1354: * @param table
1355: * a table name
1356: * @param scope
1357: * the scope of interest; use same values as SCOPE
1358: * @param nullable
1359: * include columns that are nullable?
1360: * @return ResultSet each row is a column description
1361: * @throws SQLException
1362: * DOCUMENT ME!
1363: */
1364: public java.sql.ResultSet getBestRowIdentifier(String catalog,
1365: String schema, final String table, int scope,
1366: boolean nullable) throws SQLException {
1367: if (table == null) {
1368: throw SQLError.createSQLException("Table not specified.",
1369: SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
1370: }
1371:
1372: Field[] fields = new Field[8];
1373: fields[0] = new Field("", "SCOPE", Types.SMALLINT, 5);
1374: fields[1] = new Field("", "COLUMN_NAME", Types.CHAR, 32);
1375: fields[2] = new Field("", "DATA_TYPE", Types.SMALLINT, 32);
1376: fields[3] = new Field("", "TYPE_NAME", Types.CHAR, 32);
1377: fields[4] = new Field("", "COLUMN_SIZE", Types.INTEGER, 10);
1378: fields[5] = new Field("", "BUFFER_LENGTH", Types.INTEGER, 10);
1379: fields[6] = new Field("", "DECIMAL_DIGITS", Types.INTEGER, 10);
1380: fields[7] = new Field("", "PSEUDO_COLUMN", Types.SMALLINT, 5);
1381:
1382: final ArrayList rows = new ArrayList();
1383: final Statement stmt = this .conn.getMetadataSafeStatement();
1384:
1385: try {
1386:
1387: new IterateBlock(getCatalogIterator(catalog)) {
1388: void forEach(Object catalogStr) throws SQLException {
1389: ResultSet results = null;
1390:
1391: try {
1392: StringBuffer queryBuf = new StringBuffer(
1393: "SHOW COLUMNS FROM ");
1394: queryBuf.append(quotedId);
1395: queryBuf.append(table);
1396: queryBuf.append(quotedId);
1397: queryBuf.append(" FROM ");
1398: queryBuf.append(quotedId);
1399: queryBuf.append(catalogStr.toString());
1400: queryBuf.append(quotedId);
1401:
1402: results = stmt
1403: .executeQuery(queryBuf.toString());
1404:
1405: while (results.next()) {
1406: String keyType = results.getString("Key");
1407:
1408: if (keyType != null) {
1409: if (StringUtils.startsWithIgnoreCase(
1410: keyType, "PRI")) {
1411: byte[][] rowVal = new byte[8][];
1412: rowVal[0] = Integer
1413: .toString(
1414: java.sql.DatabaseMetaData.bestRowSession)
1415: .getBytes();
1416: rowVal[1] = results
1417: .getBytes("Field");
1418:
1419: String type = results
1420: .getString("Type");
1421: int size = MysqlIO.getMaxBuf();
1422: int decimals = 0;
1423:
1424: /*
1425: * Parse the Type column from MySQL
1426: */
1427: if (type.indexOf("enum") != -1) {
1428: String temp = type.substring(
1429: type.indexOf("("), type
1430: .indexOf(")"));
1431: java.util.StringTokenizer tokenizer = new java.util.StringTokenizer(
1432: temp, ",");
1433: int maxLength = 0;
1434:
1435: while (tokenizer
1436: .hasMoreTokens()) {
1437: maxLength = Math
1438: .max(
1439: maxLength,
1440: (tokenizer
1441: .nextToken()
1442: .length() - 2));
1443: }
1444:
1445: size = maxLength;
1446: decimals = 0;
1447: type = "enum";
1448: } else if (type.indexOf("(") != -1) {
1449: if (type.indexOf(",") != -1) {
1450: size = Integer
1451: .parseInt(type
1452: .substring(
1453: type
1454: .indexOf("(") + 1,
1455: type
1456: .indexOf(",")));
1457: decimals = Integer
1458: .parseInt(type
1459: .substring(
1460: type
1461: .indexOf(",") + 1,
1462: type
1463: .indexOf(")")));
1464: } else {
1465: size = Integer
1466: .parseInt(type
1467: .substring(
1468: type
1469: .indexOf("(") + 1,
1470: type
1471: .indexOf(")")));
1472: }
1473:
1474: type = type.substring(0, type
1475: .indexOf("("));
1476: }
1477:
1478: rowVal[2] = s2b(String
1479: .valueOf(MysqlDefs
1480: .mysqlToJavaType(type)));
1481: rowVal[3] = s2b(type);
1482: rowVal[4] = Integer.toString(
1483: size + decimals).getBytes();
1484: rowVal[5] = Integer.toString(
1485: size + decimals).getBytes();
1486: rowVal[6] = Integer.toString(
1487: decimals).getBytes();
1488: rowVal[7] = Integer
1489: .toString(
1490: java.sql.DatabaseMetaData.bestRowNotPseudo)
1491: .getBytes();
1492:
1493: rows.add(new ByteArrayRow(rowVal));
1494: }
1495: }
1496: }
1497:
1498: } finally {
1499: if (results != null) {
1500: try {
1501: results.close();
1502: } catch (Exception ex) {
1503: ;
1504: }
1505:
1506: results = null;
1507: }
1508: }
1509: }
1510: }.doForAll();
1511: } finally {
1512: if (stmt != null) {
1513: stmt.close();
1514: }
1515: }
1516:
1517: java.sql.ResultSet results = buildResultSet(fields, rows);
1518:
1519: return results;
1520:
1521: }
1522:
1523: /*
1524: * * Each row in the ResultSet is a parameter desription or column
1525: * description with the following fields: <OL> <li> <B>PROCEDURE_CAT</B>
1526: * String => procedure catalog (may be null) </li> <li> <B>PROCEDURE_SCHEM</B>
1527: * String => procedure schema (may be null) </li> <li> <B>PROCEDURE_NAME</B>
1528: * String => procedure name </li> <li> <B>COLUMN_NAME</B> String =>
1529: * column/parameter name </li> <li> <B>COLUMN_TYPE</B> Short => kind of
1530: * column/parameter: <UL> <li> procedureColumnUnknown - nobody knows </li>
1531: * <li> procedureColumnIn - IN parameter </li> <li> procedureColumnInOut -
1532: * INOUT parameter </li> <li> procedureColumnOut - OUT parameter </li> <li>
1533: * procedureColumnReturn - procedure return value </li> <li>
1534: * procedureColumnResult - result column in ResultSet </li> </ul> </li> <li>
1535: * <B>DATA_TYPE</B> short => SQL type from java.sql.Types </li> <li>
1536: * <B>TYPE_NAME</B> String => SQL type name </li> <li> <B>PRECISION</B>
1537: * int => precision </li> <li> <B>LENGTH</B> int => length in bytes of data
1538: * </li> <li> <B>SCALE</B> short => scale </li> <li> <B>RADIX</B> short =>
1539: * radix </li> <li> <B>NULLABLE</B> short => can it contain NULL? <UL> <li>
1540: * procedureNoNulls - does not allow NULL values </li> <li>
1541: * procedureNullable - allows NULL values </li> <li>
1542: * procedureNullableUnknown - nullability unknown </li> </ul> </li> <li>
1543: * <B>REMARKS</B> String => comment describing parameter/column </li> </ol>
1544: * </p> <P> <B>Note:</B> Some databases may not return the column
1545: * descriptions for a procedure. Additional columns beyond REMARKS can be
1546: * defined by the database. </p> @param catalog a catalog name; "" retrieves
1547: * those without a catalog @param schemaPattern a schema name pattern; ""
1548: * retrieves those without a schema @param procedureNamePattern a procedure
1549: * name pattern @param columnNamePattern a column name pattern @return
1550: * ResultSet each row is a stored procedure parameter or column description
1551: * @throws SQLException if a database access error occurs
1552: *
1553: * @see #getSearchStringEscape
1554: */
1555: private void getCallStmtParameterTypes(String catalog,
1556: String procName, String parameterNamePattern,
1557: List resultRows) throws SQLException {
1558: getCallStmtParameterTypes(catalog, procName,
1559: parameterNamePattern, resultRows, false);
1560: }
1561:
1562: private void getCallStmtParameterTypes(String catalog,
1563: String procName, String parameterNamePattern,
1564: List resultRows, boolean forGetFunctionColumns)
1565: throws SQLException {
1566: java.sql.Statement paramRetrievalStmt = null;
1567: java.sql.ResultSet paramRetrievalRs = null;
1568:
1569: if (parameterNamePattern == null) {
1570: if (this .conn.getNullNamePatternMatchesAll()) {
1571: parameterNamePattern = "%";
1572: } else {
1573: throw SQLError
1574: .createSQLException(
1575: "Parameter/Column name pattern can not be NULL or empty.",
1576: SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
1577: }
1578: }
1579:
1580: byte[] procNameAsBytes = null;
1581:
1582: try {
1583: procNameAsBytes = procName.getBytes("UTF-8");
1584: } catch (UnsupportedEncodingException ueEx) {
1585: procNameAsBytes = s2b(procName);
1586:
1587: // Set all fields to connection encoding
1588: }
1589:
1590: String quoteChar = getIdentifierQuoteString();
1591:
1592: String parameterDef = null;
1593:
1594: boolean isProcedureInAnsiMode = false;
1595: String storageDefnDelims = null;
1596: String storageDefnClosures = null;
1597:
1598: try {
1599: paramRetrievalStmt = this .conn.getMetadataSafeStatement();
1600:
1601: if (this .conn.lowerCaseTableNames() && catalog != null
1602: && catalog.length() != 0) {
1603: // Workaround for bug in server wrt. to
1604: // SHOW CREATE PROCEDURE not respecting
1605: // lower-case table names
1606:
1607: String oldCatalog = this .conn.getCatalog();
1608: ResultSet rs = null;
1609:
1610: try {
1611: this .conn.setCatalog(catalog);
1612: rs = paramRetrievalStmt
1613: .executeQuery("SELECT DATABASE()");
1614: rs.next();
1615:
1616: catalog = rs.getString(1);
1617:
1618: } finally {
1619:
1620: this .conn.setCatalog(oldCatalog);
1621:
1622: if (rs != null) {
1623: rs.close();
1624: }
1625: }
1626: }
1627:
1628: if (paramRetrievalStmt.getMaxRows() != 0) {
1629: paramRetrievalStmt.setMaxRows(0);
1630: }
1631:
1632: int dotIndex = -1;
1633:
1634: if (!" ".equals(quoteChar)) {
1635: dotIndex = StringUtils.indexOfIgnoreCaseRespectQuotes(
1636: 0, procName, ".", quoteChar.charAt(0),
1637: !this .conn.isNoBackslashEscapesSet());
1638: } else {
1639: dotIndex = procName.indexOf(".");
1640: }
1641:
1642: String dbName = null;
1643:
1644: if (dotIndex != -1 && (dotIndex + 1) < procName.length()) {
1645: dbName = procName.substring(0, dotIndex);
1646: procName = procName.substring(dotIndex + 1);
1647: } else {
1648: dbName = catalog;
1649: }
1650:
1651: StringBuffer procNameBuf = new StringBuffer();
1652:
1653: if (dbName != null) {
1654: if (!" ".equals(quoteChar)
1655: && !dbName.startsWith(quoteChar)) {
1656: procNameBuf.append(quoteChar);
1657: }
1658:
1659: procNameBuf.append(dbName);
1660:
1661: if (!" ".equals(quoteChar)
1662: && !dbName.startsWith(quoteChar)) {
1663: procNameBuf.append(quoteChar);
1664: }
1665:
1666: procNameBuf.append(".");
1667: }
1668:
1669: boolean procNameIsNotQuoted = !procName
1670: .startsWith(quoteChar);
1671:
1672: if (!" ".equals(quoteChar) && procNameIsNotQuoted) {
1673: procNameBuf.append(quoteChar);
1674: }
1675:
1676: procNameBuf.append(procName);
1677:
1678: if (!" ".equals(quoteChar) && procNameIsNotQuoted) {
1679: procNameBuf.append(quoteChar);
1680: }
1681:
1682: boolean parsingFunction = false;
1683:
1684: try {
1685: paramRetrievalRs = paramRetrievalStmt
1686: .executeQuery("SHOW CREATE PROCEDURE "
1687: + procNameBuf.toString());
1688: parsingFunction = false;
1689: } catch (SQLException sqlEx) {
1690: paramRetrievalRs = paramRetrievalStmt
1691: .executeQuery("SHOW CREATE FUNCTION "
1692: + procNameBuf.toString());
1693: parsingFunction = true;
1694: }
1695:
1696: if (paramRetrievalRs.next()) {
1697: String procedureDef = parsingFunction ? paramRetrievalRs
1698: .getString("Create Function")
1699: : paramRetrievalRs
1700: .getString("Create Procedure");
1701:
1702: if (procedureDef == null || procedureDef.length() == 0) {
1703: throw SQLError
1704: .createSQLException(
1705: "User does not have access to metadata required to determine "
1706: + "stored procedure parameter types. If rights can not be granted, configure connection with \"noAccessToProcedureBodies=true\" "
1707: + "to have driver generate parameters that represent INOUT strings irregardless of actual parameter types.",
1708: SQLError.SQL_STATE_GENERAL_ERROR);
1709: }
1710:
1711: try {
1712: String sqlMode = paramRetrievalRs
1713: .getString("sql_mode");
1714:
1715: if (StringUtils.indexOfIgnoreCase(sqlMode, "ANSI") != -1) {
1716: isProcedureInAnsiMode = true;
1717: }
1718: } catch (SQLException sqlEx) {
1719: // doesn't exist
1720: }
1721:
1722: String identifierMarkers = isProcedureInAnsiMode ? "`\""
1723: : "`";
1724: String identifierAndStringMarkers = "'"
1725: + identifierMarkers;
1726: storageDefnDelims = "(" + identifierMarkers;
1727: storageDefnClosures = ")" + identifierMarkers;
1728:
1729: // sanitize/normalize by stripping out comments
1730: procedureDef = StringUtils.stripComments(procedureDef,
1731: identifierAndStringMarkers,
1732: identifierAndStringMarkers, true, false, true,
1733: true);
1734:
1735: int openParenIndex = StringUtils
1736: .indexOfIgnoreCaseRespectQuotes(0,
1737: procedureDef, "(", quoteChar.charAt(0),
1738: !this .conn.isNoBackslashEscapesSet());
1739: int endOfParamDeclarationIndex = 0;
1740:
1741: endOfParamDeclarationIndex = endPositionOfParameterDeclaration(
1742: openParenIndex, procedureDef, quoteChar);
1743:
1744: if (parsingFunction) {
1745:
1746: // Grab the return column since it needs
1747: // to go first in the output result set
1748: int returnsIndex = StringUtils
1749: .indexOfIgnoreCaseRespectQuotes(0,
1750: procedureDef, " RETURNS ",
1751: quoteChar.charAt(0), !this .conn
1752: .isNoBackslashEscapesSet());
1753:
1754: int endReturnsDef = findEndOfReturnsClause(
1755: procedureDef, quoteChar, returnsIndex);
1756:
1757: // Trim off whitespace after "RETURNS"
1758:
1759: int declarationStart = returnsIndex
1760: + "RETURNS ".length();
1761:
1762: while (declarationStart < procedureDef.length()) {
1763: if (Character.isWhitespace(procedureDef
1764: .charAt(declarationStart))) {
1765: declarationStart++;
1766: } else {
1767: break;
1768: }
1769: }
1770:
1771: String returnsDefn = procedureDef.substring(
1772: declarationStart, endReturnsDef).trim();
1773: TypeDescriptor returnDescriptor = new TypeDescriptor(
1774: returnsDefn, null);
1775:
1776: resultRows
1777: .add(convertTypeDescriptorToProcedureRow(
1778: procNameAsBytes, "", false, false,
1779: true, returnDescriptor,
1780: forGetFunctionColumns, 0));
1781: }
1782:
1783: if ((openParenIndex == -1)
1784: || (endOfParamDeclarationIndex == -1)) {
1785: // parse error?
1786: throw SQLError
1787: .createSQLException(
1788: "Internal error when parsing callable statement metadata",
1789: SQLError.SQL_STATE_GENERAL_ERROR);
1790: }
1791:
1792: parameterDef = procedureDef.substring(
1793: openParenIndex + 1, endOfParamDeclarationIndex);
1794: }
1795: } finally {
1796: SQLException sqlExRethrow = null;
1797:
1798: if (paramRetrievalRs != null) {
1799: try {
1800: paramRetrievalRs.close();
1801: } catch (SQLException sqlEx) {
1802: sqlExRethrow = sqlEx;
1803: }
1804:
1805: paramRetrievalRs = null;
1806: }
1807:
1808: if (paramRetrievalStmt != null) {
1809: try {
1810: paramRetrievalStmt.close();
1811: } catch (SQLException sqlEx) {
1812: sqlExRethrow = sqlEx;
1813: }
1814:
1815: paramRetrievalStmt = null;
1816: }
1817:
1818: if (sqlExRethrow != null) {
1819: throw sqlExRethrow;
1820: }
1821: }
1822:
1823: if (parameterDef != null) {
1824: int ordinal = 1;
1825:
1826: List parseList = StringUtils.split(parameterDef, ",",
1827: storageDefnDelims, storageDefnClosures, true);
1828:
1829: int parseListLen = parseList.size();
1830:
1831: for (int i = 0; i < parseListLen; i++) {
1832: String declaration = (String) parseList.get(i);
1833:
1834: if (declaration.trim().length() == 0) {
1835: break; // no parameters actually declared, but whitespace spans lines
1836: }
1837:
1838: StringTokenizer declarationTok = new StringTokenizer(
1839: declaration, " \t");
1840:
1841: String paramName = null;
1842: boolean isOutParam = false;
1843: boolean isInParam = false;
1844:
1845: if (declarationTok.hasMoreTokens()) {
1846: String possibleParamName = declarationTok
1847: .nextToken();
1848:
1849: if (possibleParamName.equalsIgnoreCase("OUT")) {
1850: isOutParam = true;
1851:
1852: if (declarationTok.hasMoreTokens()) {
1853: paramName = declarationTok.nextToken();
1854: } else {
1855: throw SQLError
1856: .createSQLException(
1857: "Internal error when parsing callable statement metadata (missing parameter name)",
1858: SQLError.SQL_STATE_GENERAL_ERROR);
1859: }
1860: } else if (possibleParamName
1861: .equalsIgnoreCase("INOUT")) {
1862: isOutParam = true;
1863: isInParam = true;
1864:
1865: if (declarationTok.hasMoreTokens()) {
1866: paramName = declarationTok.nextToken();
1867: } else {
1868: throw SQLError
1869: .createSQLException(
1870: "Internal error when parsing callable statement metadata (missing parameter name)",
1871: SQLError.SQL_STATE_GENERAL_ERROR);
1872: }
1873: } else if (possibleParamName.equalsIgnoreCase("IN")) {
1874: isOutParam = false;
1875: isInParam = true;
1876:
1877: if (declarationTok.hasMoreTokens()) {
1878: paramName = declarationTok.nextToken();
1879: } else {
1880: throw SQLError
1881: .createSQLException(
1882: "Internal error when parsing callable statement metadata (missing parameter name)",
1883: SQLError.SQL_STATE_GENERAL_ERROR);
1884: }
1885: } else {
1886: isOutParam = false;
1887: isInParam = true;
1888:
1889: paramName = possibleParamName;
1890: }
1891:
1892: TypeDescriptor typeDesc = null;
1893:
1894: if (declarationTok.hasMoreTokens()) {
1895: StringBuffer typeInfoBuf = new StringBuffer(
1896: declarationTok.nextToken());
1897:
1898: while (declarationTok.hasMoreTokens()) {
1899: typeInfoBuf.append(" ");
1900: typeInfoBuf.append(declarationTok
1901: .nextToken());
1902: }
1903:
1904: String typeInfo = typeInfoBuf.toString();
1905:
1906: typeDesc = new TypeDescriptor(typeInfo, null);
1907: } else {
1908: throw SQLError
1909: .createSQLException(
1910: "Internal error when parsing callable statement metadata (missing parameter type)",
1911: SQLError.SQL_STATE_GENERAL_ERROR);
1912: }
1913:
1914: if ((paramName.startsWith("`") && paramName
1915: .endsWith("`"))
1916: || (isProcedureInAnsiMode
1917: && paramName.startsWith("\"") && paramName
1918: .endsWith("\""))) {
1919: paramName = paramName.substring(1, paramName
1920: .length() - 1);
1921: }
1922:
1923: int wildCompareRes = StringUtils.wildCompare(
1924: paramName, parameterNamePattern);
1925:
1926: if (wildCompareRes != StringUtils.WILD_COMPARE_NO_MATCH) {
1927: ResultSetRow row = convertTypeDescriptorToProcedureRow(
1928: procNameAsBytes, paramName, isOutParam,
1929: isInParam, false, typeDesc,
1930: forGetFunctionColumns, ordinal++);
1931:
1932: resultRows.add(row);
1933: }
1934: } else {
1935: throw SQLError
1936: .createSQLException(
1937: "Internal error when parsing callable statement metadata (unknown output from 'SHOW CREATE PROCEDURE')",
1938: SQLError.SQL_STATE_GENERAL_ERROR);
1939: }
1940: }
1941: } else {
1942: // Is this an error? JDBC spec doesn't make it clear if stored
1943: // procedure doesn't
1944: // exist, is it an error....
1945: }
1946: }
1947:
1948: /**
1949: * Finds the end of the parameter declaration from the output of "SHOW
1950: * CREATE PROCEDURE".
1951: *
1952: * @param beginIndex
1953: * should be the index of the procedure body that contains the
1954: * first "(".
1955: * @param procedureDef
1956: * the procedure body
1957: * @param quoteChar
1958: * the identifier quote character in use
1959: * @return the ending index of the parameter declaration, not including the
1960: * closing ")"
1961: * @throws SQLException
1962: * if a parse error occurs.
1963: */
1964: private int endPositionOfParameterDeclaration(int beginIndex,
1965: String procedureDef, String quoteChar) throws SQLException {
1966: int currentPos = beginIndex + 1;
1967: int parenDepth = 1; // counting the first openParen
1968:
1969: while (parenDepth > 0 && currentPos < procedureDef.length()) {
1970: int closedParenIndex = StringUtils
1971: .indexOfIgnoreCaseRespectQuotes(currentPos,
1972: procedureDef, ")", quoteChar.charAt(0),
1973: !this .conn.isNoBackslashEscapesSet());
1974:
1975: if (closedParenIndex != -1) {
1976: int nextOpenParenIndex = StringUtils
1977: .indexOfIgnoreCaseRespectQuotes(currentPos,
1978: procedureDef, "(", quoteChar.charAt(0),
1979: !this .conn.isNoBackslashEscapesSet());
1980:
1981: if (nextOpenParenIndex != -1
1982: && nextOpenParenIndex < closedParenIndex) {
1983: parenDepth++;
1984: currentPos = closedParenIndex + 1; // set after closed
1985: // paren that increases
1986: // depth
1987: } else {
1988: parenDepth--;
1989: currentPos = closedParenIndex; // start search from same
1990: // position
1991: }
1992: } else {
1993: // we should always get closed paren of some sort
1994: throw SQLError
1995: .createSQLException(
1996: "Internal error when parsing callable statement metadata",
1997: SQLError.SQL_STATE_GENERAL_ERROR);
1998: }
1999: }
2000:
2001: return currentPos;
2002: }
2003:
2004: /**
2005: * Finds the end of the RETURNS clause for SQL Functions by using any of the
2006: * keywords allowed after the RETURNS clause, or a label.
2007: *
2008: * @param procedureDefn
2009: * the function body containing the definition of the function
2010: * @param quoteChar
2011: * the identifier quote string in use
2012: * @param positionOfReturnKeyword
2013: * the position of "RETRUNS" in the definition
2014: * @return the end of the returns clause
2015: * @throws SQLException
2016: * if a parse error occurs
2017: */
2018: private int findEndOfReturnsClause(String procedureDefn,
2019: String quoteChar, int positionOfReturnKeyword)
2020: throws SQLException {
2021: /*
2022: * characteristic: LANGUAGE SQL | [NOT] DETERMINISTIC | { CONTAINS SQL |
2023: * NO SQL | READS SQL DATA | MODIFIES SQL DATA } | SQL SECURITY {
2024: * DEFINER | INVOKER } | COMMENT 'string'
2025: */
2026:
2027: String[] tokens = new String[] { "LANGUAGE", "NOT",
2028: "DETERMINISTIC", "CONTAINS", "NO", "READ", "MODIFIES",
2029: "SQL", "COMMENT", "BEGIN", "RETURN" };
2030:
2031: int startLookingAt = positionOfReturnKeyword
2032: + "RETURNS".length() + 1;
2033:
2034: for (int i = 0; i < tokens.length; i++) {
2035: int endOfReturn = StringUtils
2036: .indexOfIgnoreCaseRespectQuotes(startLookingAt,
2037: procedureDefn, tokens[i], quoteChar
2038: .charAt(0), !this .conn
2039: .isNoBackslashEscapesSet());
2040:
2041: if (endOfReturn != -1) {
2042: return endOfReturn;
2043: }
2044: }
2045:
2046: // Label?
2047: int endOfReturn = StringUtils.indexOfIgnoreCaseRespectQuotes(
2048: startLookingAt, procedureDefn, ":",
2049: quoteChar.charAt(0), !this .conn
2050: .isNoBackslashEscapesSet());
2051:
2052: if (endOfReturn != -1) {
2053: // seek back until whitespace
2054: for (int i = endOfReturn; i > 0; i--) {
2055: if (Character.isWhitespace(procedureDefn.charAt(i))) {
2056: return i;
2057: }
2058: }
2059: }
2060:
2061: // We can't parse it.
2062:
2063: throw SQLError
2064: .createSQLException(
2065: "Internal error when parsing callable statement metadata",
2066: SQLError.SQL_STATE_GENERAL_ERROR);
2067: }
2068:
2069: /**
2070: * Parses the cascade option string and returns the DBMD constant that
2071: * represents it (for deletes)
2072: *
2073: * @param cascadeOptions
2074: * the comment from 'SHOW TABLE STATUS'
2075: * @return the DBMD constant that represents the cascade option
2076: */
2077: private int getCascadeDeleteOption(String cascadeOptions) {
2078: int onDeletePos = cascadeOptions.indexOf("ON DELETE");
2079:
2080: if (onDeletePos != -1) {
2081: String deleteOptions = cascadeOptions.substring(
2082: onDeletePos, cascadeOptions.length());
2083:
2084: if (deleteOptions.startsWith("ON DELETE CASCADE")) {
2085: return java.sql.DatabaseMetaData.importedKeyCascade;
2086: } else if (deleteOptions.startsWith("ON DELETE SET NULL")) {
2087: return java.sql.DatabaseMetaData.importedKeySetNull;
2088: } else if (deleteOptions.startsWith("ON DELETE RESTRICT")) {
2089: return java.sql.DatabaseMetaData.importedKeyRestrict;
2090: } else if (deleteOptions.startsWith("ON DELETE NO ACTION")) {
2091: return java.sql.DatabaseMetaData.importedKeyNoAction;
2092: }
2093: }
2094:
2095: return java.sql.DatabaseMetaData.importedKeyNoAction;
2096: }
2097:
2098: /**
2099: * Parses the cascade option string and returns the DBMD constant that
2100: * represents it (for Updates)
2101: *
2102: * @param cascadeOptions
2103: * the comment from 'SHOW TABLE STATUS'
2104: * @return the DBMD constant that represents the cascade option
2105: */
2106: private int getCascadeUpdateOption(String cascadeOptions) {
2107: int onUpdatePos = cascadeOptions.indexOf("ON UPDATE");
2108:
2109: if (onUpdatePos != -1) {
2110: String updateOptions = cascadeOptions.substring(
2111: onUpdatePos, cascadeOptions.length());
2112:
2113: if (updateOptions.startsWith("ON UPDATE CASCADE")) {
2114: return java.sql.DatabaseMetaData.importedKeyCascade;
2115: } else if (updateOptions.startsWith("ON UPDATE SET NULL")) {
2116: return java.sql.DatabaseMetaData.importedKeySetNull;
2117: } else if (updateOptions.startsWith("ON UPDATE RESTRICT")) {
2118: return java.sql.DatabaseMetaData.importedKeyRestrict;
2119: } else if (updateOptions.startsWith("ON UPDATE NO ACTION")) {
2120: return java.sql.DatabaseMetaData.importedKeyNoAction;
2121: }
2122: }
2123:
2124: return java.sql.DatabaseMetaData.importedKeyNoAction;
2125: }
2126:
2127: protected IteratorWithCleanup getCatalogIterator(String catalogSpec)
2128: throws SQLException {
2129: IteratorWithCleanup allCatalogsIter;
2130: if (catalogSpec != null) {
2131: if (!catalogSpec.equals("")) {
2132: allCatalogsIter = new SingleStringIterator(catalogSpec);
2133: } else {
2134: // legacy mode of operation
2135: allCatalogsIter = new SingleStringIterator(
2136: this .database);
2137: }
2138: } else if (this .conn.getNullCatalogMeansCurrent()) {
2139: allCatalogsIter = new SingleStringIterator(this .database);
2140: } else {
2141: allCatalogsIter = new ResultSetIterator(getCatalogs(), 1);
2142: }
2143:
2144: return allCatalogsIter;
2145: }
2146:
2147: /**
2148: * Get the catalog names available in this database. The results are ordered
2149: * by catalog name.
2150: * <P>
2151: * The catalog column is:
2152: * <OL>
2153: * <li> <B>TABLE_CAT</B> String => catalog name </li>
2154: * </ol>
2155: * </p>
2156: *
2157: * @return ResultSet each row has a single String column that is a catalog
2158: * name
2159: * @throws SQLException
2160: * DOCUMENT ME!
2161: */
2162: public java.sql.ResultSet getCatalogs() throws SQLException {
2163: java.sql.ResultSet results = null;
2164: java.sql.Statement stmt = null;
2165:
2166: try {
2167: stmt = this .conn.createStatement();
2168: stmt.setEscapeProcessing(false);
2169: results = stmt.executeQuery("SHOW DATABASES");
2170:
2171: java.sql.ResultSetMetaData resultsMD = results
2172: .getMetaData();
2173: Field[] fields = new Field[1];
2174: fields[0] = new Field("", "TABLE_CAT", Types.VARCHAR,
2175: resultsMD.getColumnDisplaySize(1));
2176:
2177: ArrayList tuples = new ArrayList();
2178:
2179: while (results.next()) {
2180: byte[][] rowVal = new byte[1][];
2181: rowVal[0] = results.getBytes(1);
2182: tuples.add(new ByteArrayRow(rowVal));
2183: }
2184:
2185: return buildResultSet(fields, tuples);
2186: } finally {
2187: if (results != null) {
2188: try {
2189: results.close();
2190: } catch (SQLException sqlEx) {
2191: AssertionFailedException.shouldNotHappen(sqlEx);
2192: }
2193:
2194: results = null;
2195: }
2196:
2197: if (stmt != null) {
2198: try {
2199: stmt.close();
2200: } catch (SQLException sqlEx) {
2201: AssertionFailedException.shouldNotHappen(sqlEx);
2202: }
2203:
2204: stmt = null;
2205: }
2206: }
2207: }
2208:
2209: /**
2210: * What's the separator between catalog and table name?
2211: *
2212: * @return the separator string
2213: * @throws SQLException
2214: * DOCUMENT ME!
2215: */
2216: public String getCatalogSeparator() throws SQLException {
2217: return ".";
2218: }
2219:
2220: // ----------------------------------------------------------------------
2221: // The following group of methods exposes various limitations
2222: // based on the target database with the current driver.
2223: // Unless otherwise specified, a result of zero means there is no
2224: // limit, or the limit is not known.
2225:
2226: /**
2227: * What's the database vendor's preferred term for "catalog"?
2228: *
2229: * @return the vendor term
2230: * @throws SQLException
2231: * DOCUMENT ME!
2232: */
2233: public String getCatalogTerm() throws SQLException {
2234: return "database";
2235: }
2236:
2237: /**
2238: * Get a description of the access rights for a table's columns.
2239: * <P>
2240: * Only privileges matching the column name criteria are returned. They are
2241: * ordered by COLUMN_NAME and PRIVILEGE.
2242: * </p>
2243: * <P>
2244: * Each privilige description has the following columns:
2245: * <OL>
2246: * <li> <B>TABLE_CAT</B> String => table catalog (may be null) </li>
2247: * <li> <B>TABLE_SCHEM</B> String => table schema (may be null) </li>
2248: * <li> <B>TABLE_NAME</B> String => table name </li>
2249: * <li> <B>COLUMN_NAME</B> String => column name </li>
2250: * <li> <B>GRANTOR</B> => grantor of access (may be null) </li>
2251: * <li> <B>GRANTEE</B> String => grantee of access </li>
2252: * <li> <B>PRIVILEGE</B> String => name of access (SELECT, INSERT, UPDATE,
2253: * REFRENCES, ...) </li>
2254: * <li> <B>IS_GRANTABLE</B> String => "YES" if grantee is permitted to
2255: * grant to others; "NO" if not; null if unknown </li>
2256: * </ol>
2257: * </p>
2258: *
2259: * @param catalog
2260: * a catalog name; "" retrieves those without a catalog
2261: * @param schema
2262: * a schema name; "" retrieves those without a schema
2263: * @param table
2264: * a table name
2265: * @param columnNamePattern
2266: * a column name pattern
2267: * @return ResultSet each row is a column privilege description
2268: * @throws SQLException
2269: * if a database access error occurs
2270: * @see #getSearchStringEscape
2271: */
2272: public java.sql.ResultSet getColumnPrivileges(String catalog,
2273: String schema, String table, String columnNamePattern)
2274: throws SQLException {
2275: Field[] fields = new Field[8];
2276: fields[0] = new Field("", "TABLE_CAT", Types.CHAR, 64);
2277: fields[1] = new Field("", "TABLE_SCHEM", Types.CHAR, 1);
2278: fields[2] = new Field("", "TABLE_NAME", Types.CHAR, 64);
2279: fields[3] = new Field("", "COLUMN_NAME", Types.CHAR, 64);
2280: fields[4] = new Field("", "GRANTOR", Types.CHAR, 77);
2281: fields[5] = new Field("", "GRANTEE", Types.CHAR, 77);
2282: fields[6] = new Field("", "PRIVILEGE", Types.CHAR, 64);
2283: fields[7] = new Field("", "IS_GRANTABLE", Types.CHAR, 3);
2284:
2285: StringBuffer grantQuery = new StringBuffer(
2286: "SELECT c.host, c.db, t.grantor, c.user, "
2287: + "c.table_name, c.column_name, c.column_priv "
2288: + "from mysql.columns_priv c, mysql.tables_priv t "
2289: + "where c.host = t.host and c.db = t.db and "
2290: + "c.table_name = t.table_name ");
2291:
2292: if ((catalog != null) && (catalog.length() != 0)) {
2293: grantQuery.append(" AND c.db='");
2294: grantQuery.append(catalog);
2295: grantQuery.append("' ");
2296: ;
2297: }
2298:
2299: grantQuery.append(" AND c.table_name ='");
2300: grantQuery.append(table);
2301: grantQuery.append("' AND c.column_name like '");
2302: grantQuery.append(columnNamePattern);
2303: grantQuery.append("'");
2304:
2305: Statement stmt = null;
2306: ResultSet results = null;
2307: ArrayList grantRows = new ArrayList();
2308:
2309: try {
2310: stmt = this .conn.createStatement();
2311: stmt.setEscapeProcessing(false);
2312: results = stmt.executeQuery(grantQuery.toString());
2313:
2314: while (results.next()) {
2315: String host = results.getString(1);
2316: String db = results.getString(2);
2317: String grantor = results.getString(3);
2318: String user = results.getString(4);
2319:
2320: if ((user == null) || (user.length() == 0)) {
2321: user = "%";
2322: }
2323:
2324: StringBuffer fullUser = new StringBuffer(user);
2325:
2326: if ((host != null)
2327: && this .conn.getUseHostsInPrivileges()) {
2328: fullUser.append("@");
2329: fullUser.append(host);
2330: }
2331:
2332: String columnName = results.getString(6);
2333: String allPrivileges = results.getString(7);
2334:
2335: if (allPrivileges != null) {
2336: allPrivileges = allPrivileges
2337: .toUpperCase(Locale.ENGLISH);
2338:
2339: StringTokenizer st = new StringTokenizer(
2340: allPrivileges, ",");
2341:
2342: while (st.hasMoreTokens()) {
2343: String privilege = st.nextToken().trim();
2344: byte[][] tuple = new byte[8][];
2345: tuple[0] = s2b(db);
2346: tuple[1] = null;
2347: tuple[2] = s2b(table);
2348: tuple[3] = s2b(columnName);
2349:
2350: if (grantor != null) {
2351: tuple[4] = s2b(grantor);
2352: } else {
2353: tuple[4] = null;
2354: }
2355:
2356: tuple[5] = s2b(fullUser.toString());
2357: tuple[6] = s2b(privilege);
2358: tuple[7] = null;
2359: grantRows.add(new ByteArrayRow(tuple));
2360: }
2361: }
2362: }
2363: } finally {
2364: if (results != null) {
2365: try {
2366: results.close();
2367: } catch (Exception ex) {
2368: ;
2369: }
2370:
2371: results = null;
2372: }
2373:
2374: if (stmt != null) {
2375: try {
2376: stmt.close();
2377: } catch (Exception ex) {
2378: ;
2379: }
2380:
2381: stmt = null;
2382: }
2383: }
2384:
2385: return buildResultSet(fields, grantRows);
2386: }
2387:
2388: /**
2389: * Get a description of table columns available in a catalog.
2390: * <P>
2391: * Only column descriptions matching the catalog, schema, table and column
2392: * name criteria are returned. They are ordered by TABLE_SCHEM, TABLE_NAME
2393: * and ORDINAL_POSITION.
2394: * </p>
2395: * <P>
2396: * Each column description has the following columns:
2397: * <OL>
2398: * <li> <B>TABLE_CAT</B> String => table catalog (may be null) </li>
2399: * <li> <B>TABLE_SCHEM</B> String => table schema (may be null) </li>
2400: * <li> <B>TABLE_NAME</B> String => table name </li>
2401: * <li> <B>COLUMN_NAME</B> String => column name </li>
2402: * <li> <B>DATA_TYPE</B> short => SQL type from java.sql.Types </li>
2403: * <li> <B>TYPE_NAME</B> String => Data source dependent type name </li>
2404: * <li> <B>COLUMN_SIZE</B> int => column size. For char or date types this
2405: * is the maximum number of characters, for numeric or decimal types this is
2406: * precision. </li>
2407: * <li> <B>BUFFER_LENGTH</B> is not used. </li>
2408: * <li> <B>DECIMAL_DIGITS</B> int => the number of fractional digits </li>
2409: * <li> <B>NUM_PREC_RADIX</B> int => Radix (typically either 10 or 2) </li>
2410: * <li> <B>NULLABLE</B> int => is NULL allowed?
2411: * <UL>
2412: * <li> columnNoNulls - might not allow NULL values </li>
2413: * <li> columnNullable - definitely allows NULL values </li>
2414: * <li> columnNullableUnknown - nullability unknown </li>
2415: * </ul>
2416: * </li>
2417: * <li> <B>REMARKS</B> String => comment describing column (may be null)
2418: * </li>
2419: * <li> <B>COLUMN_DEF</B> String => default value (may be null) </li>
2420: * <li> <B>SQL_DATA_TYPE</B> int => unused </li>
2421: * <li> <B>SQL_DATETIME_SUB</B> int => unused </li>
2422: * <li> <B>CHAR_OCTET_LENGTH</B> int => for char types the maximum number
2423: * of bytes in the column </li>
2424: * <li> <B>ORDINAL_POSITION</B> int => index of column in table (starting
2425: * at 1) </li>
2426: * <li> <B>IS_NULLABLE</B> String => "NO" means column definitely does not
2427: * allow NULL values; "YES" means the column might allow NULL values. An
2428: * empty string means nobody knows. </li>
2429: * </ol>
2430: * </p>
2431: *
2432: * @param catalog
2433: * a catalog name; "" retrieves those without a catalog
2434: * @param schemaPattern
2435: * a schema name pattern; "" retrieves those without a schema
2436: * @param tableNamePattern
2437: * a table name pattern
2438: * @param columnNamePattern
2439: * a column name pattern
2440: * @return ResultSet each row is a column description
2441: * @throws SQLException
2442: * if a database access error occurs
2443: * @see #getSearchStringEscape
2444: */
2445: public java.sql.ResultSet getColumns(final String catalog,
2446: final String schemaPattern, final String tableNamePattern,
2447: String columnNamePattern) throws SQLException {
2448:
2449: if (columnNamePattern == null) {
2450: if (this .conn.getNullNamePatternMatchesAll()) {
2451: columnNamePattern = "%";
2452: } else {
2453: throw SQLError
2454: .createSQLException(
2455: "Column name pattern can not be NULL or empty.",
2456: SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
2457: }
2458: }
2459:
2460: final String colPattern = columnNamePattern;
2461:
2462: Field[] fields = new Field[23];
2463: fields[0] = new Field("", "TABLE_CAT", Types.CHAR, 255);
2464: fields[1] = new Field("", "TABLE_SCHEM", Types.CHAR, 0);
2465: fields[2] = new Field("", "TABLE_NAME", Types.CHAR, 255);
2466: fields[3] = new Field("", "COLUMN_NAME", Types.CHAR, 32);
2467: fields[4] = new Field("", "DATA_TYPE", Types.SMALLINT, 5);
2468: fields[5] = new Field("", "TYPE_NAME", Types.CHAR, 16);
2469: fields[6] = new Field("", "COLUMN_SIZE", Types.INTEGER, Integer
2470: .toString(Integer.MAX_VALUE).length());
2471: fields[7] = new Field("", "BUFFER_LENGTH", Types.INTEGER, 10);
2472: fields[8] = new Field("", "DECIMAL_DIGITS", Types.INTEGER, 10);
2473: fields[9] = new Field("", "NUM_PREC_RADIX", Types.INTEGER, 10);
2474: fields[10] = new Field("", "NULLABLE", Types.INTEGER, 10);
2475: fields[11] = new Field("", "REMARKS", Types.CHAR, 0);
2476: fields[12] = new Field("", "COLUMN_DEF", Types.CHAR, 0);
2477: fields[13] = new Field("", "SQL_DATA_TYPE", Types.INTEGER, 10);
2478: fields[14] = new Field("", "SQL_DATETIME_SUB", Types.INTEGER,
2479: 10);
2480: fields[15] = new Field("", "CHAR_OCTET_LENGTH", Types.INTEGER,
2481: Integer.toString(Integer.MAX_VALUE).length());
2482: fields[16] = new Field("", "ORDINAL_POSITION", Types.INTEGER,
2483: 10);
2484: fields[17] = new Field("", "IS_NULLABLE", Types.CHAR, 3);
2485: fields[18] = new Field("", "SCOPE_CATALOG", Types.CHAR, 255);
2486: fields[19] = new Field("", "SCOPE_SCHEMA", Types.CHAR, 255);
2487: fields[20] = new Field("", "SCOPE_TABLE", Types.CHAR, 255);
2488: fields[21] = new Field("", "SOURCE_DATA_TYPE", Types.SMALLINT,
2489: 10);
2490: fields[22] = new Field("", "IS_AUTOINCREMENT", Types.CHAR, 3);
2491:
2492: final ArrayList rows = new ArrayList();
2493: final Statement stmt = this .conn.getMetadataSafeStatement();
2494:
2495: try {
2496:
2497: new IterateBlock(getCatalogIterator(catalog)) {
2498: void forEach(Object catalogStr) throws SQLException {
2499:
2500: ArrayList tableNameList = new ArrayList();
2501:
2502: if (tableNamePattern == null) {
2503: // Select from all tables
2504: java.sql.ResultSet tables = null;
2505:
2506: try {
2507: tables = getTables(catalog, schemaPattern,
2508: "%", new String[0]);
2509:
2510: while (tables.next()) {
2511: String tableNameFromList = tables
2512: .getString("TABLE_NAME");
2513: tableNameList.add(tableNameFromList);
2514: }
2515: } finally {
2516: if (tables != null) {
2517: try {
2518: tables.close();
2519: } catch (Exception sqlEx) {
2520: AssertionFailedException
2521: .shouldNotHappen(sqlEx);
2522: }
2523:
2524: tables = null;
2525: }
2526: }
2527: } else {
2528: java.sql.ResultSet tables = null;
2529:
2530: try {
2531: tables = getTables(catalog, schemaPattern,
2532: tableNamePattern, new String[0]);
2533:
2534: while (tables.next()) {
2535: String tableNameFromList = tables
2536: .getString("TABLE_NAME");
2537: tableNameList.add(tableNameFromList);
2538: }
2539: } finally {
2540: if (tables != null) {
2541: try {
2542: tables.close();
2543: } catch (SQLException sqlEx) {
2544: AssertionFailedException
2545: .shouldNotHappen(sqlEx);
2546: }
2547:
2548: tables = null;
2549: }
2550: }
2551: }
2552:
2553: java.util.Iterator tableNames = tableNameList
2554: .iterator();
2555:
2556: while (tableNames.hasNext()) {
2557: String tableName = (String) tableNames.next();
2558:
2559: ResultSet results = null;
2560:
2561: try {
2562: StringBuffer queryBuf = new StringBuffer(
2563: "SHOW ");
2564:
2565: if (conn.versionMeetsMinimum(4, 1, 0)) {
2566: queryBuf.append("FULL ");
2567: }
2568:
2569: queryBuf.append("COLUMNS FROM ");
2570: queryBuf.append(quotedId);
2571: queryBuf.append(tableName);
2572: queryBuf.append(quotedId);
2573: queryBuf.append(" FROM ");
2574: queryBuf.append(quotedId);
2575: queryBuf.append(catalogStr.toString());
2576: queryBuf.append(quotedId);
2577: queryBuf.append(" LIKE '");
2578: queryBuf.append(colPattern);
2579: queryBuf.append("'");
2580:
2581: // Return correct ordinals if column name pattern is
2582: // not '%'
2583: // Currently, MySQL doesn't show enough data to do
2584: // this, so we do it the 'hard' way...Once _SYSTEM
2585: // tables are in, this should be much easier
2586: boolean fixUpOrdinalsRequired = false;
2587: Map ordinalFixUpMap = null;
2588:
2589: if (!colPattern.equals("%")) {
2590: fixUpOrdinalsRequired = true;
2591:
2592: StringBuffer fullColumnQueryBuf = new StringBuffer(
2593: "SHOW ");
2594:
2595: if (conn.versionMeetsMinimum(4, 1, 0)) {
2596: fullColumnQueryBuf.append("FULL ");
2597: }
2598:
2599: fullColumnQueryBuf
2600: .append("COLUMNS FROM ");
2601: fullColumnQueryBuf.append(quotedId);
2602: fullColumnQueryBuf.append(tableName);
2603: fullColumnQueryBuf.append(quotedId);
2604: fullColumnQueryBuf.append(" FROM ");
2605: fullColumnQueryBuf.append(quotedId);
2606: fullColumnQueryBuf.append(catalogStr
2607: .toString());
2608: fullColumnQueryBuf.append(quotedId);
2609:
2610: results = stmt
2611: .executeQuery(fullColumnQueryBuf
2612: .toString());
2613:
2614: ordinalFixUpMap = new HashMap();
2615:
2616: int fullOrdinalPos = 1;
2617:
2618: while (results.next()) {
2619: String fullOrdColName = results
2620: .getString("Field");
2621:
2622: ordinalFixUpMap
2623: .put(
2624: fullOrdColName,
2625: Constants
2626: .integerValueOf(fullOrdinalPos++));
2627: }
2628: }
2629:
2630: results = stmt.executeQuery(queryBuf
2631: .toString());
2632:
2633: int ordPos = 1;
2634:
2635: while (results.next()) {
2636: byte[][] rowVal = new byte[23][];
2637: rowVal[0] = s2b(catalog); // TABLE_CAT
2638: rowVal[1] = null; // TABLE_SCHEM (No schemas
2639: // in MySQL)
2640:
2641: rowVal[2] = s2b(tableName); // TABLE_NAME
2642: rowVal[3] = results.getBytes("Field");
2643:
2644: TypeDescriptor typeDesc = new TypeDescriptor(
2645: results.getString("Type"),
2646: results.getString("Null"));
2647:
2648: rowVal[4] = Short.toString(
2649: typeDesc.dataType).getBytes();
2650:
2651: // DATA_TYPE (jdbc)
2652: rowVal[5] = s2b(typeDesc.typeName); // TYPE_NAME
2653: // (native)
2654: rowVal[6] = typeDesc.columnSize == null ? null
2655: : s2b(typeDesc.columnSize
2656: .toString());
2657: rowVal[7] = s2b(Integer
2658: .toString(typeDesc.bufferLength));
2659: rowVal[8] = typeDesc.decimalDigits == null ? null
2660: : s2b(typeDesc.decimalDigits
2661: .toString());
2662: rowVal[9] = s2b(Integer
2663: .toString(typeDesc.numPrecRadix));
2664: rowVal[10] = s2b(Integer
2665: .toString(typeDesc.nullability));
2666:
2667: //
2668: // Doesn't always have this field, depending on
2669: // version
2670: //
2671: //
2672: // REMARK column
2673: //
2674: try {
2675: if (conn.versionMeetsMinimum(4, 1,
2676: 0)) {
2677: rowVal[11] = results
2678: .getBytes("Comment");
2679: } else {
2680: rowVal[11] = results
2681: .getBytes("Extra");
2682: }
2683: } catch (Exception E) {
2684: rowVal[11] = new byte[0];
2685: }
2686:
2687: // COLUMN_DEF
2688: rowVal[12] = results
2689: .getBytes("Default");
2690:
2691: rowVal[13] = new byte[] { (byte) '0' }; // SQL_DATA_TYPE
2692: rowVal[14] = new byte[] { (byte) '0' }; // SQL_DATE_TIME_SUB
2693:
2694: if (StringUtils.indexOfIgnoreCase(
2695: typeDesc.typeName, "CHAR") != -1
2696: || StringUtils
2697: .indexOfIgnoreCase(
2698: typeDesc.typeName,
2699: "BLOB") != -1
2700: || StringUtils
2701: .indexOfIgnoreCase(
2702: typeDesc.typeName,
2703: "TEXT") != -1
2704: || StringUtils
2705: .indexOfIgnoreCase(
2706: typeDesc.typeName,
2707: "BINARY") != -1) {
2708: rowVal[15] = rowVal[6]; // CHAR_OCTET_LENGTH
2709: } else {
2710: rowVal[15] = null;
2711: }
2712:
2713: // ORDINAL_POSITION
2714: if (!fixUpOrdinalsRequired) {
2715: rowVal[16] = Integer.toString(
2716: ordPos++).getBytes();
2717: } else {
2718: String origColName = results
2719: .getString("Field");
2720: Integer realOrdinal = (Integer) ordinalFixUpMap
2721: .get(origColName);
2722:
2723: if (realOrdinal != null) {
2724: rowVal[16] = realOrdinal
2725: .toString().getBytes();
2726: } else {
2727: throw SQLError
2728: .createSQLException(
2729: "Can not find column in full column list to determine true ordinal position.",
2730: SQLError.SQL_STATE_GENERAL_ERROR);
2731: }
2732: }
2733:
2734: rowVal[17] = s2b(typeDesc.isNullable);
2735:
2736: // We don't support REF or DISTINCT types
2737: rowVal[18] = null;
2738: rowVal[19] = null;
2739: rowVal[20] = null;
2740: rowVal[21] = null;
2741:
2742: rowVal[22] = s2b("");
2743:
2744: String extra = results
2745: .getString("Extra");
2746:
2747: if (extra != null) {
2748: rowVal[22] = s2b(StringUtils
2749: .indexOfIgnoreCase(extra,
2750: "auto_increment") != -1 ? "YES"
2751: : "NO");
2752: }
2753:
2754: rows.add(new ByteArrayRow(rowVal));
2755: }
2756: } finally {
2757: if (results != null) {
2758: try {
2759: results.close();
2760: } catch (Exception ex) {
2761: ;
2762: }
2763:
2764: results = null;
2765: }
2766: }
2767: }
2768: }
2769: }.doForAll();
2770: } finally {
2771: if (stmt != null) {
2772: stmt.close();
2773: }
2774: }
2775:
2776: java.sql.ResultSet results = buildResultSet(fields, rows);
2777:
2778: return results;
2779: }
2780:
2781: /**
2782: * JDBC 2.0 Return the connection that produced this metadata object.
2783: *
2784: * @return the connection that produced this metadata object.
2785: * @throws SQLException
2786: * if a database error occurs
2787: */
2788: public java.sql.Connection getConnection() throws SQLException {
2789: return this .conn;
2790: }
2791:
2792: /**
2793: * Get a description of the foreign key columns in the foreign key table
2794: * that reference the primary key columns of the primary key table (describe
2795: * how one table imports another's key.) This should normally return a
2796: * single foreign key/primary key pair (most tables only import a foreign
2797: * key from a table once.) They are ordered by FKTABLE_CAT, FKTABLE_SCHEM,
2798: * FKTABLE_NAME, and KEY_SEQ.
2799: * <P>
2800: * Each foreign key column description has the following columns:
2801: * <OL>
2802: * <li> <B>PKTABLE_CAT</B> String => primary key table catalog (may be
2803: * null) </li>
2804: * <li> <B>PKTABLE_SCHEM</B> String => primary key table schema (may be
2805: * null) </li>
2806: * <li> <B>PKTABLE_NAME</B> String => primary key table name </li>
2807: * <li> <B>PKCOLUMN_NAME</B> String => primary key column name </li>
2808: * <li> <B>FKTABLE_CAT</B> String => foreign key table catalog (may be
2809: * null) being exported (may be null) </li>
2810: * <li> <B>FKTABLE_SCHEM</B> String => foreign key table schema (may be
2811: * null) being exported (may be null) </li>
2812: * <li> <B>FKTABLE_NAME</B> String => foreign key table name being exported
2813: * </li>
2814: * <li> <B>FKCOLUMN_NAME</B> String => foreign key column name being
2815: * exported </li>
2816: * <li> <B>KEY_SEQ</B> short => sequence number within foreign key </li>
2817: * <li> <B>UPDATE_RULE</B> short => What happens to foreign key when
2818: * primary is updated:
2819: * <UL>
2820: * <li> importedKeyCascade - change imported key to agree with primary key
2821: * update </li>
2822: * <li> importedKeyRestrict - do not allow update of primary key if it has
2823: * been imported </li>
2824: * <li> importedKeySetNull - change imported key to NULL if its primary key
2825: * has been updated </li>
2826: * </ul>
2827: * </li>
2828: * <li> <B>DELETE_RULE</B> short => What happens to the foreign key when
2829: * primary is deleted.
2830: * <UL>
2831: * <li> importedKeyCascade - delete rows that import a deleted key </li>
2832: * <li> importedKeyRestrict - do not allow delete of primary key if it has
2833: * been imported </li>
2834: * <li> importedKeySetNull - change imported key to NULL if its primary key
2835: * has been deleted </li>
2836: * </ul>
2837: * </li>
2838: * <li> <B>FK_NAME</B> String => foreign key identifier (may be null) </li>
2839: * <li> <B>PK_NAME</B> String => primary key identifier (may be null) </li>
2840: * </ol>
2841: * </p>
2842: *
2843: * @param primaryCatalog
2844: * a catalog name; "" retrieves those without a catalog
2845: * @param primarySchema
2846: * a schema name pattern; "" retrieves those without a schema
2847: * @param primaryTable
2848: * a table name
2849: * @param foreignCatalog
2850: * a catalog name; "" retrieves those without a catalog
2851: * @param foreignSchema
2852: * a schema name pattern; "" retrieves those without a schema
2853: * @param foreignTable
2854: * a table name
2855: * @return ResultSet each row is a foreign key column description
2856: * @throws SQLException
2857: * if a database access error occurs
2858: */
2859: public java.sql.ResultSet getCrossReference(
2860: final String primaryCatalog, final String primarySchema,
2861: final String primaryTable, final String foreignCatalog,
2862: final String foreignSchema, final String foreignTable)
2863: throws SQLException {
2864: if (primaryTable == null) {
2865: throw SQLError.createSQLException("Table not specified.",
2866: SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
2867: }
2868:
2869: Field[] fields = new Field[14];
2870: fields[0] = new Field("", "PKTABLE_CAT", Types.CHAR, 255);
2871: fields[1] = new Field("", "PKTABLE_SCHEM", Types.CHAR, 0);
2872: fields[2] = new Field("", "PKTABLE_NAME", Types.CHAR, 255);
2873: fields[3] = new Field("", "PKCOLUMN_NAME", Types.CHAR, 32);
2874: fields[4] = new Field("", "FKTABLE_CAT", Types.CHAR, 255);
2875: fields[5] = new Field("", "FKTABLE_SCHEM", Types.CHAR, 0);
2876: fields[6] = new Field("", "FKTABLE_NAME", Types.CHAR, 255);
2877: fields[7] = new Field("", "FKCOLUMN_NAME", Types.CHAR, 32);
2878: fields[8] = new Field("", "KEY_SEQ", Types.SMALLINT, 2);
2879: fields[9] = new Field("", "UPDATE_RULE", Types.SMALLINT, 2);
2880: fields[10] = new Field("", "DELETE_RULE", Types.SMALLINT, 2);
2881: fields[11] = new Field("", "FK_NAME", Types.CHAR, 0);
2882: fields[12] = new Field("", "PK_NAME", Types.CHAR, 0);
2883: fields[13] = new Field("", "DEFERRABILITY", Types.INTEGER, 2);
2884:
2885: final ArrayList tuples = new ArrayList();
2886:
2887: if (this .conn.versionMeetsMinimum(3, 23, 0)) {
2888:
2889: final Statement stmt = this .conn.getMetadataSafeStatement();
2890:
2891: try {
2892:
2893: new IterateBlock(getCatalogIterator(foreignCatalog)) {
2894: void forEach(Object catalogStr) throws SQLException {
2895:
2896: ResultSet fkresults = null;
2897:
2898: try {
2899:
2900: /*
2901: * Get foreign key information for table
2902: */
2903: if (conn.versionMeetsMinimum(3, 23, 50)) {
2904: fkresults = extractForeignKeyFromCreateTable(
2905: catalogStr.toString(), null);
2906: } else {
2907: StringBuffer queryBuf = new StringBuffer(
2908: "SHOW TABLE STATUS FROM ");
2909: queryBuf.append(quotedId);
2910: queryBuf.append(catalogStr.toString());
2911: queryBuf.append(quotedId);
2912:
2913: fkresults = stmt.executeQuery(queryBuf
2914: .toString());
2915: }
2916:
2917: String foreignTableWithCase = getTableNameWithCase(foreignTable);
2918: String primaryTableWithCase = getTableNameWithCase(primaryTable);
2919:
2920: /*
2921: * Parse imported foreign key information
2922: */
2923:
2924: String dummy;
2925:
2926: while (fkresults.next()) {
2927: String tableType = fkresults
2928: .getString("Type");
2929:
2930: if ((tableType != null)
2931: && (tableType
2932: .equalsIgnoreCase("innodb") || tableType
2933: .equalsIgnoreCase(SUPPORTS_FK))) {
2934: String comment = fkresults
2935: .getString("Comment")
2936: .trim();
2937:
2938: if (comment != null) {
2939: StringTokenizer commentTokens = new StringTokenizer(
2940: comment, ";", false);
2941:
2942: if (commentTokens
2943: .hasMoreTokens()) {
2944: dummy = commentTokens
2945: .nextToken();
2946:
2947: // Skip InnoDB comment
2948: }
2949:
2950: while (commentTokens
2951: .hasMoreTokens()) {
2952: String keys = commentTokens
2953: .nextToken();
2954: LocalAndReferencedColumns parsedInfo = parseTableStatusIntoLocalAndReferencedColumns(keys);
2955:
2956: int keySeq = 0;
2957:
2958: Iterator referencingColumns = parsedInfo.localColumnsList
2959: .iterator();
2960: Iterator referencedColumns = parsedInfo.referencedColumnsList
2961: .iterator();
2962:
2963: while (referencingColumns
2964: .hasNext()) {
2965: String referencingColumn = removeQuotedId(referencingColumns
2966: .next()
2967: .toString());
2968:
2969: // one tuple for each table
2970: // between
2971: // parenthesis
2972: byte[][] tuple = new byte[14][];
2973: tuple[4] = ((foreignCatalog == null) ? null
2974: : s2b(foreignCatalog));
2975: tuple[5] = ((foreignSchema == null) ? null
2976: : s2b(foreignSchema));
2977: dummy = fkresults
2978: .getString("Name"); // FKTABLE_NAME
2979:
2980: if (dummy
2981: .compareTo(foreignTableWithCase) != 0) {
2982: continue;
2983: }
2984:
2985: tuple[6] = s2b(dummy);
2986:
2987: tuple[7] = s2b(referencingColumn); // FKCOLUMN_NAME
2988: tuple[0] = ((primaryCatalog == null) ? null
2989: : s2b(primaryCatalog));
2990: tuple[1] = ((primarySchema == null) ? null
2991: : s2b(primarySchema));
2992:
2993: // Skip foreign key if it
2994: // doesn't refer to
2995: // the right table
2996: if (parsedInfo.referencedTable
2997: .compareTo(primaryTableWithCase) != 0) {
2998: continue;
2999: }
3000:
3001: tuple[2] = s2b(parsedInfo.referencedTable); // PKTABLE_NAME
3002: tuple[3] = s2b(removeQuotedId(referencedColumns
3003: .next()
3004: .toString())); // PKCOLUMN_NAME
3005: tuple[8] = Integer
3006: .toString(
3007: keySeq)
3008: .getBytes(); // KEY_SEQ
3009:
3010: int[] actions = getForeignKeyActions(keys);
3011:
3012: tuple[9] = Integer
3013: .toString(
3014: actions[1])
3015: .getBytes();
3016: tuple[10] = Integer
3017: .toString(
3018: actions[0])
3019: .getBytes();
3020: tuple[11] = null; // FK_NAME
3021: tuple[12] = null; // PK_NAME
3022: tuple[13] = Integer
3023: .toString(
3024: java.sql.DatabaseMetaData.importedKeyNotDeferrable)
3025: .getBytes();
3026: tuples
3027: .add(new ByteArrayRow(
3028: tuple));
3029: keySeq++;
3030: }
3031: }
3032: }
3033: }
3034: }
3035:
3036: } finally {
3037: if (fkresults != null) {
3038: try {
3039: fkresults.close();
3040: } catch (Exception sqlEx) {
3041: AssertionFailedException
3042: .shouldNotHappen(sqlEx);
3043: }
3044:
3045: fkresults = null;
3046: }
3047: }
3048: }
3049: }.doForAll();
3050: } finally {
3051: if (stmt != null) {
3052: stmt.close();
3053: }
3054: }
3055: }
3056:
3057: java.sql.ResultSet results = buildResultSet(fields, tuples);
3058:
3059: return results;
3060: }
3061:
3062: /**
3063: * @see DatabaseMetaData#getDatabaseMajorVersion()
3064: */
3065: public int getDatabaseMajorVersion() throws SQLException {
3066: return this .conn.getServerMajorVersion();
3067: }
3068:
3069: /**
3070: * @see DatabaseMetaData#getDatabaseMinorVersion()
3071: */
3072: public int getDatabaseMinorVersion() throws SQLException {
3073: return this .conn.getServerMinorVersion();
3074: }
3075:
3076: /**
3077: * What's the name of this database product?
3078: *
3079: * @return database product name
3080: * @throws SQLException
3081: * DOCUMENT ME!
3082: */
3083: public String getDatabaseProductName() throws SQLException {
3084: return "MySQL";
3085: }
3086:
3087: /**
3088: * What's the version of this database product?
3089: *
3090: * @return database version
3091: * @throws SQLException
3092: * DOCUMENT ME!
3093: */
3094: public String getDatabaseProductVersion() throws SQLException {
3095: return this .conn.getServerVersion();
3096: }
3097:
3098: /**
3099: * What's the database's default transaction isolation level? The values are
3100: * defined in java.sql.Connection.
3101: *
3102: * @return the default isolation level
3103: * @throws SQLException
3104: * if a database access error occurs
3105: * @see Connection
3106: */
3107: public int getDefaultTransactionIsolation() throws SQLException {
3108: if (this .conn.supportsIsolationLevel()) {
3109: return java.sql.Connection.TRANSACTION_READ_COMMITTED;
3110: }
3111:
3112: return java.sql.Connection.TRANSACTION_NONE;
3113: }
3114:
3115: /**
3116: * What's this JDBC driver's major version number?
3117: *
3118: * @return JDBC driver major version
3119: */
3120: public int getDriverMajorVersion() {
3121: return NonRegisteringDriver.getMajorVersionInternal();
3122: }
3123:
3124: /**
3125: * What's this JDBC driver's minor version number?
3126: *
3127: * @return JDBC driver minor version number
3128: */
3129: public int getDriverMinorVersion() {
3130: return NonRegisteringDriver.getMinorVersionInternal();
3131: }
3132:
3133: /**
3134: * What's the name of this JDBC driver?
3135: *
3136: * @return JDBC driver name
3137: * @throws SQLException
3138: * DOCUMENT ME!
3139: */
3140: public String getDriverName() throws SQLException {
3141: return "MySQL-AB JDBC Driver";
3142: }
3143:
3144: /**
3145: * What's the version of this JDBC driver?
3146: *
3147: * @return JDBC driver version
3148: * @throws java.sql.SQLException
3149: * DOCUMENT ME!
3150: */
3151: public String getDriverVersion() throws java.sql.SQLException {
3152: return "@MYSQL_CJ_FULL_PROD_NAME@ ( Revision: @MYSQL_CJ_REVISION@ )";
3153: }
3154:
3155: /**
3156: * Get a description of a foreign key columns that reference a table's
3157: * primary key columns (the foreign keys exported by a table). They are
3158: * ordered by FKTABLE_CAT, FKTABLE_SCHEM, FKTABLE_NAME, and KEY_SEQ.
3159: * <P>
3160: * Each foreign key column description has the following columns:
3161: * <OL>
3162: * <li> <B>PKTABLE_CAT</B> String => primary key table catalog (may be
3163: * null) </li>
3164: * <li> <B>PKTABLE_SCHEM</B> String => primary key table schema (may be
3165: * null) </li>
3166: * <li> <B>PKTABLE_NAME</B> String => primary key table name </li>
3167: * <li> <B>PKCOLUMN_NAME</B> String => primary key column name </li>
3168: * <li> <B>FKTABLE_CAT</B> String => foreign key table catalog (may be
3169: * null) being exported (may be null) </li>
3170: * <li> <B>FKTABLE_SCHEM</B> String => foreign key table schema (may be
3171: * null) being exported (may be null) </li>
3172: * <li> <B>FKTABLE_NAME</B> String => foreign key table name being exported
3173: * </li>
3174: * <li> <B>FKCOLUMN_NAME</B> String => foreign key column name being
3175: * exported </li>
3176: * <li> <B>KEY_SEQ</B> short => sequence number within foreign key </li>
3177: * <li> <B>UPDATE_RULE</B> short => What happens to foreign key when
3178: * primary is updated:
3179: * <UL>
3180: * <li> importedKeyCascade - change imported key to agree with primary key
3181: * update </li>
3182: * <li> importedKeyRestrict - do not allow update of primary key if it has
3183: * been imported </li>
3184: * <li> importedKeySetNull - change imported key to NULL if its primary key
3185: * has been updated </li>
3186: * </ul>
3187: * </li>
3188: * <li> <B>DELETE_RULE</B> short => What happens to the foreign key when
3189: * primary is deleted.
3190: * <UL>
3191: * <li> importedKeyCascade - delete rows that import a deleted key </li>
3192: * <li> importedKeyRestrict - do not allow delete of primary key if it has
3193: * been imported </li>
3194: * <li> importedKeySetNull - change imported key to NULL if its primary key
3195: * has been deleted </li>
3196: * </ul>
3197: * </li>
3198: * <li> <B>FK_NAME</B> String => foreign key identifier (may be null) </li>
3199: * <li> <B>PK_NAME</B> String => primary key identifier (may be null) </li>
3200: * </ol>
3201: * </p>
3202: *
3203: * @param catalog
3204: * a catalog name; "" retrieves those without a catalog
3205: * @param schema
3206: * a schema name pattern; "" retrieves those without a schema
3207: * @param table
3208: * a table name
3209: * @return ResultSet each row is a foreign key column description
3210: * @throws SQLException
3211: * if a database access error occurs
3212: * @see #getImportedKeys
3213: */
3214: public java.sql.ResultSet getExportedKeys(String catalog,
3215: String schema, final String table) throws SQLException {
3216: if (table == null) {
3217: throw SQLError.createSQLException("Table not specified.",
3218: SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
3219: }
3220:
3221: Field[] fields = new Field[14];
3222: fields[0] = new Field("", "PKTABLE_CAT", Types.CHAR, 255);
3223: fields[1] = new Field("", "PKTABLE_SCHEM", Types.CHAR, 0);
3224: fields[2] = new Field("", "PKTABLE_NAME", Types.CHAR, 255);
3225: fields[3] = new Field("", "PKCOLUMN_NAME", Types.CHAR, 32);
3226: fields[4] = new Field("", "FKTABLE_CAT", Types.CHAR, 255);
3227: fields[5] = new Field("", "FKTABLE_SCHEM", Types.CHAR, 0);
3228: fields[6] = new Field("", "FKTABLE_NAME", Types.CHAR, 255);
3229: fields[7] = new Field("", "FKCOLUMN_NAME", Types.CHAR, 32);
3230: fields[8] = new Field("", "KEY_SEQ", Types.SMALLINT, 2);
3231: fields[9] = new Field("", "UPDATE_RULE", Types.SMALLINT, 2);
3232: fields[10] = new Field("", "DELETE_RULE", Types.SMALLINT, 2);
3233: fields[11] = new Field("", "FK_NAME", Types.CHAR, 255);
3234: fields[12] = new Field("", "PK_NAME", Types.CHAR, 0);
3235: fields[13] = new Field("", "DEFERRABILITY", Types.INTEGER, 2);
3236:
3237: final ArrayList rows = new ArrayList();
3238:
3239: if (this .conn.versionMeetsMinimum(3, 23, 0)) {
3240:
3241: final Statement stmt = this .conn.getMetadataSafeStatement();
3242:
3243: try {
3244:
3245: new IterateBlock(getCatalogIterator(catalog)) {
3246: void forEach(Object catalogStr) throws SQLException {
3247: ResultSet fkresults = null;
3248:
3249: try {
3250:
3251: /*
3252: * Get foreign key information for table
3253: */
3254: if (conn.versionMeetsMinimum(3, 23, 50)) {
3255: // we can use 'SHOW CREATE TABLE'
3256:
3257: fkresults = extractForeignKeyFromCreateTable(
3258: catalogStr.toString(), null);
3259: } else {
3260: StringBuffer queryBuf = new StringBuffer(
3261: "SHOW TABLE STATUS FROM ");
3262: queryBuf.append(quotedId);
3263: queryBuf.append(catalogStr.toString());
3264: queryBuf.append(quotedId);
3265:
3266: fkresults = stmt.executeQuery(queryBuf
3267: .toString());
3268: }
3269:
3270: // lower-case table name might be turned on
3271: String tableNameWithCase = getTableNameWithCase(table);
3272:
3273: /*
3274: * Parse imported foreign key information
3275: */
3276:
3277: while (fkresults.next()) {
3278: String tableType = fkresults
3279: .getString("Type");
3280:
3281: if ((tableType != null)
3282: && (tableType
3283: .equalsIgnoreCase("innodb") || tableType
3284: .equalsIgnoreCase(SUPPORTS_FK))) {
3285: String comment = fkresults
3286: .getString("Comment")
3287: .trim();
3288:
3289: if (comment != null) {
3290: StringTokenizer commentTokens = new StringTokenizer(
3291: comment, ";", false);
3292:
3293: if (commentTokens
3294: .hasMoreTokens()) {
3295: commentTokens.nextToken(); // Skip
3296: // InnoDB
3297: // comment
3298:
3299: while (commentTokens
3300: .hasMoreTokens()) {
3301: String keys = commentTokens
3302: .nextToken();
3303: getExportKeyResults(
3304: catalogStr
3305: .toString(),
3306: tableNameWithCase,
3307: keys,
3308: rows,
3309: fkresults
3310: .getString("Name"));
3311: }
3312: }
3313: }
3314: }
3315: }
3316:
3317: } finally {
3318: if (fkresults != null) {
3319: try {
3320: fkresults.close();
3321: } catch (SQLException sqlEx) {
3322: AssertionFailedException
3323: .shouldNotHappen(sqlEx);
3324: }
3325:
3326: fkresults = null;
3327: }
3328: }
3329: }
3330: }.doForAll();
3331: } finally {
3332: if (stmt != null) {
3333: stmt.close();
3334: }
3335: }
3336: }
3337:
3338: java.sql.ResultSet results = buildResultSet(fields, rows);
3339:
3340: return results;
3341: }
3342:
3343: /**
3344: * Adds to the tuples list the exported keys of exportingTable based on the
3345: * keysComment from the 'show table status' sql command. KeysComment is that
3346: * part of the comment field that follows the "InnoDB free ...;" prefix.
3347: *
3348: * @param catalog
3349: * the database to use
3350: * @param exportingTable
3351: * the table keys are being exported from
3352: * @param keysComment
3353: * the comment from 'show table status'
3354: * @param tuples
3355: * the rows to add results to
3356: * @param fkTableName
3357: * the foreign key table name
3358: * @throws SQLException
3359: * if a database access error occurs
3360: */
3361: private void getExportKeyResults(String catalog,
3362: String exportingTable, String keysComment, List tuples,
3363: String fkTableName) throws SQLException {
3364: getResultsImpl(catalog, exportingTable, keysComment, tuples,
3365: fkTableName, true);
3366: }
3367:
3368: /**
3369: * Get all the "extra" characters that can be used in unquoted identifier
3370: * names (those beyond a-z, 0-9 and _).
3371: *
3372: * @return the string containing the extra characters
3373: * @throws SQLException
3374: * DOCUMENT ME!
3375: */
3376: public String getExtraNameCharacters() throws SQLException {
3377: return "#@";
3378: }
3379:
3380: /**
3381: * Returns the DELETE and UPDATE foreign key actions from the given 'SHOW
3382: * TABLE STATUS' string, with the DELETE action being the first item in the
3383: * array, and the UPDATE action being the second.
3384: *
3385: * @param commentString
3386: * the comment from 'SHOW TABLE STATUS'
3387: * @return int[] [0] = delete action, [1] = update action
3388: */
3389: private int[] getForeignKeyActions(String commentString) {
3390: int[] actions = new int[] {
3391: java.sql.DatabaseMetaData.importedKeyNoAction,
3392: java.sql.DatabaseMetaData.importedKeyNoAction };
3393:
3394: int lastParenIndex = commentString.lastIndexOf(")");
3395:
3396: if (lastParenIndex != (commentString.length() - 1)) {
3397: String cascadeOptions = commentString.substring(
3398: lastParenIndex + 1).trim().toUpperCase(
3399: Locale.ENGLISH);
3400:
3401: actions[0] = getCascadeDeleteOption(cascadeOptions);
3402: actions[1] = getCascadeUpdateOption(cascadeOptions);
3403: }
3404:
3405: return actions;
3406: }
3407:
3408: /**
3409: * What's the string used to quote SQL identifiers? This returns a space " "
3410: * if identifier quoting isn't supported. A JDBC compliant driver always
3411: * uses a double quote character.
3412: *
3413: * @return the quoting string
3414: * @throws SQLException
3415: * DOCUMENT ME!
3416: */
3417: public String getIdentifierQuoteString() throws SQLException {
3418: if (this .conn.supportsQuotedIdentifiers()) {
3419: if (!this .conn.useAnsiQuotedIdentifiers()) {
3420: return "`";
3421: }
3422:
3423: return "\"";
3424: }
3425:
3426: return " ";
3427: }
3428:
3429: /**
3430: * Get a description of the primary key columns that are referenced by a
3431: * table's foreign key columns (the primary keys imported by a table). They
3432: * are ordered by PKTABLE_CAT, PKTABLE_SCHEM, PKTABLE_NAME, and KEY_SEQ.
3433: * <P>
3434: * Each primary key column description has the following columns:
3435: * <OL>
3436: * <li> <B>PKTABLE_CAT</B> String => primary key table catalog being
3437: * imported (may be null) </li>
3438: * <li> <B>PKTABLE_SCHEM</B> String => primary key table schema being
3439: * imported (may be null) </li>
3440: * <li> <B>PKTABLE_NAME</B> String => primary key table name being imported
3441: * </li>
3442: * <li> <B>PKCOLUMN_NAME</B> String => primary key column name being
3443: * imported </li>
3444: * <li> <B>FKTABLE_CAT</B> String => foreign key table catalog (may be
3445: * null) </li>
3446: * <li> <B>FKTABLE_SCHEM</B> String => foreign key table schema (may be
3447: * null) </li>
3448: * <li> <B>FKTABLE_NAME</B> String => foreign key table name </li>
3449: * <li> <B>FKCOLUMN_NAME</B> String => foreign key column name </li>
3450: * <li> <B>KEY_SEQ</B> short => sequence number within foreign key </li>
3451: * <li> <B>UPDATE_RULE</B> short => What happens to foreign key when
3452: * primary is updated:
3453: * <UL>
3454: * <li> importedKeyCascade - change imported key to agree with primary key
3455: * update </li>
3456: * <li> importedKeyRestrict - do not allow update of primary key if it has
3457: * been imported </li>
3458: * <li> importedKeySetNull - change imported key to NULL if its primary key
3459: * has been updated </li>
3460: * </ul>
3461: * </li>
3462: * <li> <B>DELETE_RULE</B> short => What happens to the foreign key when
3463: * primary is deleted.
3464: * <UL>
3465: * <li> importedKeyCascade - delete rows that import a deleted key </li>
3466: * <li> importedKeyRestrict - do not allow delete of primary key if it has
3467: * been imported </li>
3468: * <li> importedKeySetNull - change imported key to NULL if its primary key
3469: * has been deleted </li>
3470: * </ul>
3471: * </li>
3472: * <li> <B>FK_NAME</B> String => foreign key name (may be null) </li>
3473: * <li> <B>PK_NAME</B> String => primary key name (may be null) </li>
3474: * </ol>
3475: * </p>
3476: *
3477: * @param catalog
3478: * a catalog name; "" retrieves those without a catalog
3479: * @param schema
3480: * a schema name pattern; "" retrieves those without a schema
3481: * @param table
3482: * a table name
3483: * @return ResultSet each row is a primary key column description
3484: * @throws SQLException
3485: * if a database access error occurs
3486: * @see #getExportedKeys
3487: */
3488: public java.sql.ResultSet getImportedKeys(String catalog,
3489: String schema, final String table) throws SQLException {
3490: if (table == null) {
3491: throw SQLError.createSQLException("Table not specified.",
3492: SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
3493: }
3494:
3495: Field[] fields = new Field[14];
3496: fields[0] = new Field("", "PKTABLE_CAT", Types.CHAR, 255);
3497: fields[1] = new Field("", "PKTABLE_SCHEM", Types.CHAR, 0);
3498: fields[2] = new Field("", "PKTABLE_NAME", Types.CHAR, 255);
3499: fields[3] = new Field("", "PKCOLUMN_NAME", Types.CHAR, 32);
3500: fields[4] = new Field("", "FKTABLE_CAT", Types.CHAR, 255);
3501: fields[5] = new Field("", "FKTABLE_SCHEM", Types.CHAR, 0);
3502: fields[6] = new Field("", "FKTABLE_NAME", Types.CHAR, 255);
3503: fields[7] = new Field("", "FKCOLUMN_NAME", Types.CHAR, 32);
3504: fields[8] = new Field("", "KEY_SEQ", Types.SMALLINT, 2);
3505: fields[9] = new Field("", "UPDATE_RULE", Types.SMALLINT, 2);
3506: fields[10] = new Field("", "DELETE_RULE", Types.SMALLINT, 2);
3507: fields[11] = new Field("", "FK_NAME", Types.CHAR, 255);
3508: fields[12] = new Field("", "PK_NAME", Types.CHAR, 0);
3509: fields[13] = new Field("", "DEFERRABILITY", Types.INTEGER, 2);
3510:
3511: final ArrayList rows = new ArrayList();
3512:
3513: if (this .conn.versionMeetsMinimum(3, 23, 0)) {
3514:
3515: final Statement stmt = this .conn.getMetadataSafeStatement();
3516:
3517: try {
3518:
3519: new IterateBlock(getCatalogIterator(catalog)) {
3520: void forEach(Object catalogStr) throws SQLException {
3521: ResultSet fkresults = null;
3522:
3523: try {
3524:
3525: /*
3526: * Get foreign key information for table
3527: */
3528: if (conn.versionMeetsMinimum(3, 23, 50)) {
3529: // we can use 'SHOW CREATE TABLE'
3530:
3531: fkresults = extractForeignKeyFromCreateTable(
3532: catalogStr.toString(), table);
3533: } else {
3534: StringBuffer queryBuf = new StringBuffer(
3535: "SHOW TABLE STATUS ");
3536: queryBuf.append(" FROM ");
3537: queryBuf.append(quotedId);
3538: queryBuf.append(catalogStr.toString());
3539: queryBuf.append(quotedId);
3540: queryBuf.append(" LIKE '");
3541: queryBuf.append(table);
3542: queryBuf.append("'");
3543:
3544: fkresults = stmt.executeQuery(queryBuf
3545: .toString());
3546: }
3547:
3548: /*
3549: * Parse imported foreign key information
3550: */
3551:
3552: while (fkresults.next()) {
3553: String tableType = fkresults
3554: .getString("Type");
3555:
3556: if ((tableType != null)
3557: && (tableType
3558: .equalsIgnoreCase("innodb") || tableType
3559: .equalsIgnoreCase(SUPPORTS_FK))) {
3560: String comment = fkresults
3561: .getString("Comment")
3562: .trim();
3563:
3564: if (comment != null) {
3565: StringTokenizer commentTokens = new StringTokenizer(
3566: comment, ";", false);
3567:
3568: if (commentTokens
3569: .hasMoreTokens()) {
3570: commentTokens.nextToken(); // Skip
3571: // InnoDB
3572: // comment
3573:
3574: while (commentTokens
3575: .hasMoreTokens()) {
3576: String keys = commentTokens
3577: .nextToken();
3578: getImportKeyResults(
3579: catalogStr
3580: .toString(),
3581: table, keys,
3582: rows);
3583: }
3584: }
3585: }
3586: }
3587: }
3588: } finally {
3589: if (fkresults != null) {
3590: try {
3591: fkresults.close();
3592: } catch (SQLException sqlEx) {
3593: AssertionFailedException
3594: .shouldNotHappen(sqlEx);
3595: }
3596:
3597: fkresults = null;
3598: }
3599: }
3600: }
3601: }.doForAll();
3602: } finally {
3603: if (stmt != null) {
3604: stmt.close();
3605: }
3606: }
3607: }
3608:
3609: java.sql.ResultSet results = buildResultSet(fields, rows);
3610:
3611: return results;
3612: }
3613:
3614: /**
3615: * Populates the tuples list with the imported keys of importingTable based
3616: * on the keysComment from the 'show table status' sql command. KeysComment
3617: * is that part of the comment field that follows the "InnoDB free ...;"
3618: * prefix.
3619: *
3620: * @param catalog
3621: * the database to use
3622: * @param importingTable
3623: * the table keys are being imported to
3624: * @param keysComment
3625: * the comment from 'show table status'
3626: * @param tuples
3627: * the rows to add results to
3628: * @throws SQLException
3629: * if a database access error occurs
3630: */
3631: private void getImportKeyResults(String catalog,
3632: String importingTable, String keysComment, List tuples)
3633: throws SQLException {
3634: getResultsImpl(catalog, importingTable, keysComment, tuples,
3635: null, false);
3636: }
3637:
3638: /**
3639: * Get a description of a table's indices and statistics. They are ordered
3640: * by NON_UNIQUE, TYPE, INDEX_NAME, and ORDINAL_POSITION.
3641: * <P>
3642: * Each index column description has the following columns:
3643: * <OL>
3644: * <li> <B>TABLE_CAT</B> String => table catalog (may be null) </li>
3645: * <li> <B>TABLE_SCHEM</B> String => table schema (may be null) </li>
3646: * <li> <B>TABLE_NAME</B> String => table name </li>
3647: * <li> <B>NON_UNIQUE</B> boolean => Can index values be non-unique? false
3648: * when TYPE is tableIndexStatistic </li>
3649: * <li> <B>INDEX_QUALIFIER</B> String => index catalog (may be null); null
3650: * when TYPE is tableIndexStatistic </li>
3651: * <li> <B>INDEX_NAME</B> String => index name; null when TYPE is
3652: * tableIndexStatistic </li>
3653: * <li> <B>TYPE</B> short => index type:
3654: * <UL>
3655: * <li> tableIndexStatistic - this identifies table statistics that are
3656: * returned in conjuction with a table's index descriptions </li>
3657: * <li> tableIndexClustered - this is a clustered index </li>
3658: * <li> tableIndexHashed - this is a hashed index </li>
3659: * <li> tableIndexOther - this is some other style of index </li>
3660: * </ul>
3661: * </li>
3662: * <li> <B>ORDINAL_POSITION</B> short => column sequence number within
3663: * index; zero when TYPE is tableIndexStatistic </li>
3664: * <li> <B>COLUMN_NAME</B> String => column name; null when TYPE is
3665: * tableIndexStatistic </li>
3666: * <li> <B>ASC_OR_DESC</B> String => column sort sequence, "A" =>
3667: * ascending, "D" => descending, may be null if sort sequence is not
3668: * supported; null when TYPE is tableIndexStatistic </li>
3669: * <li> <B>CARDINALITY</B> int => When TYPE is tableIndexStatisic then this
3670: * is the number of rows in the table; otherwise it is the number of unique
3671: * values in the index. </li>
3672: * <li> <B>PAGES</B> int => When TYPE is tableIndexStatisic then this is
3673: * the number of pages used for the table, otherwise it is the number of
3674: * pages used for the current index. </li>
3675: * <li> <B>FILTER_CONDITION</B> String => Filter condition, if any. (may be
3676: * null) </li>
3677: * </ol>
3678: * </p>
3679: *
3680: * @param catalog
3681: * a catalog name; "" retrieves those without a catalog
3682: * @param schema
3683: * a schema name pattern; "" retrieves those without a schema
3684: * @param table
3685: * a table name
3686: * @param unique
3687: * when true, return only indices for unique values; when false,
3688: * return indices regardless of whether unique or not
3689: * @param approximate
3690: * when true, result is allowed to reflect approximate or out of
3691: * data values; when false, results are requested to be accurate
3692: * @return ResultSet each row is an index column description
3693: * @throws SQLException
3694: * DOCUMENT ME!
3695: */
3696: public java.sql.ResultSet getIndexInfo(String catalog,
3697: String schema, final String table, final boolean unique,
3698: boolean approximate) throws SQLException {
3699: /*
3700: * MySQL stores index information in the following fields: Table
3701: * Non_unique Key_name Seq_in_index Column_name Collation Cardinality
3702: * Sub_part
3703: */
3704:
3705: Field[] fields = new Field[13];
3706: fields[0] = new Field("", "TABLE_CAT", Types.CHAR, 255);
3707: fields[1] = new Field("", "TABLE_SCHEM", Types.CHAR, 0);
3708: fields[2] = new Field("", "TABLE_NAME", Types.CHAR, 255);
3709: fields[3] = new Field("", "NON_UNIQUE", Types.CHAR, 4);
3710: fields[4] = new Field("", "INDEX_QUALIFIER", Types.CHAR, 1);
3711: fields[5] = new Field("", "INDEX_NAME", Types.CHAR, 32);
3712: fields[6] = new Field("", "TYPE", Types.CHAR, 32);
3713: fields[7] = new Field("", "ORDINAL_POSITION", Types.SMALLINT, 5);
3714: fields[8] = new Field("", "COLUMN_NAME", Types.CHAR, 32);
3715: fields[9] = new Field("", "ASC_OR_DESC", Types.CHAR, 1);
3716: fields[10] = new Field("", "CARDINALITY", Types.INTEGER, 10);
3717: fields[11] = new Field("", "PAGES", Types.INTEGER, 10);
3718: fields[12] = new Field("", "FILTER_CONDITION", Types.CHAR, 32);
3719:
3720: final ArrayList rows = new ArrayList();
3721: final Statement stmt = this .conn.getMetadataSafeStatement();
3722:
3723: try {
3724:
3725: new IterateBlock(getCatalogIterator(catalog)) {
3726: void forEach(Object catalogStr) throws SQLException {
3727:
3728: ResultSet results = null;
3729:
3730: try {
3731: StringBuffer queryBuf = new StringBuffer(
3732: "SHOW INDEX FROM ");
3733: queryBuf.append(quotedId);
3734: queryBuf.append(table);
3735: queryBuf.append(quotedId);
3736: queryBuf.append(" FROM ");
3737: queryBuf.append(quotedId);
3738: queryBuf.append(catalogStr.toString());
3739: queryBuf.append(quotedId);
3740:
3741: try {
3742: results = stmt.executeQuery(queryBuf
3743: .toString());
3744: } catch (SQLException sqlEx) {
3745: int errorCode = sqlEx.getErrorCode();
3746:
3747: // If SQLState is 42S02, ignore this SQLException
3748: // it means the table doesn't exist....
3749: if (!"42S02".equals(sqlEx.getSQLState())) {
3750: // Sometimes not mapped correctly for pre-4.1
3751: // so use error code instead.
3752: if (errorCode != MysqlErrorNumbers.ER_NO_SUCH_TABLE) {
3753: throw sqlEx;
3754: }
3755: }
3756: }
3757:
3758: while (results != null && results.next()) {
3759: byte[][] row = new byte[14][];
3760: row[0] = ((catalogStr.toString() == null) ? new byte[0]
3761: : s2b(catalogStr.toString()));
3762: ;
3763: row[1] = null;
3764: row[2] = results.getBytes("Table");
3765:
3766: boolean indexIsUnique = results
3767: .getInt("Non_unique") == 0;
3768:
3769: row[3] = (!indexIsUnique ? s2b("true")
3770: : s2b("false"));
3771: row[4] = new byte[0];
3772: row[5] = results.getBytes("Key_name");
3773: row[6] = Integer
3774: .toString(
3775: java.sql.DatabaseMetaData.tableIndexOther)
3776: .getBytes();
3777: row[7] = results.getBytes("Seq_in_index");
3778: row[8] = results.getBytes("Column_name");
3779: row[9] = results.getBytes("Collation");
3780: row[10] = results.getBytes("Cardinality");
3781: row[11] = s2b("0");
3782: row[12] = null;
3783:
3784: if (unique) {
3785: if (indexIsUnique) {
3786: rows.add(new ByteArrayRow(row));
3787: }
3788: } else {
3789: // All rows match
3790: rows.add(new ByteArrayRow(row));
3791: }
3792: }
3793: } finally {
3794: if (results != null) {
3795: try {
3796: results.close();
3797: } catch (Exception ex) {
3798: ;
3799: }
3800:
3801: results = null;
3802: }
3803: }
3804: }
3805: }.doForAll();
3806:
3807: java.sql.ResultSet indexInfo = buildResultSet(fields, rows);
3808:
3809: return indexInfo;
3810: } finally {
3811: if (stmt != null) {
3812: stmt.close();
3813: }
3814: }
3815: }
3816:
3817: /**
3818: * @see DatabaseMetaData#getJDBCMajorVersion()
3819: */
3820: public int getJDBCMajorVersion() throws SQLException {
3821: return 3;
3822: }
3823:
3824: /**
3825: * @see DatabaseMetaData#getJDBCMinorVersion()
3826: */
3827: public int getJDBCMinorVersion() throws SQLException {
3828: return 0;
3829: }
3830:
3831: /**
3832: * How many hex characters can you have in an inline binary literal?
3833: *
3834: * @return max literal length
3835: * @throws SQLException
3836: * DOCUMENT ME!
3837: */
3838: public int getMaxBinaryLiteralLength() throws SQLException {
3839: return 16777208;
3840: }
3841:
3842: /**
3843: * What's the maximum length of a catalog name?
3844: *
3845: * @return max name length in bytes
3846: * @throws SQLException
3847: * DOCUMENT ME!
3848: */
3849: public int getMaxCatalogNameLength() throws SQLException {
3850: return 32;
3851: }
3852:
3853: /**
3854: * What's the max length for a character literal?
3855: *
3856: * @return max literal length
3857: * @throws SQLException
3858: * DOCUMENT ME!
3859: */
3860: public int getMaxCharLiteralLength() throws SQLException {
3861: return 16777208;
3862: }
3863:
3864: /**
3865: * What's the limit on column name length?
3866: *
3867: * @return max literal length
3868: * @throws SQLException
3869: * DOCUMENT ME!
3870: */
3871: public int getMaxColumnNameLength() throws SQLException {
3872: return 64;
3873: }
3874:
3875: /**
3876: * What's the maximum number of columns in a "GROUP BY" clause?
3877: *
3878: * @return max number of columns
3879: * @throws SQLException
3880: * DOCUMENT ME!
3881: */
3882: public int getMaxColumnsInGroupBy() throws SQLException {
3883: return 64;
3884: }
3885:
3886: /**
3887: * What's the maximum number of columns allowed in an index?
3888: *
3889: * @return max columns
3890: * @throws SQLException
3891: * DOCUMENT ME!
3892: */
3893: public int getMaxColumnsInIndex() throws SQLException {
3894: return 16;
3895: }
3896:
3897: /**
3898: * What's the maximum number of columns in an "ORDER BY" clause?
3899: *
3900: * @return max columns
3901: * @throws SQLException
3902: * DOCUMENT ME!
3903: */
3904: public int getMaxColumnsInOrderBy() throws SQLException {
3905: return 64;
3906: }
3907:
3908: /**
3909: * What's the maximum number of columns in a "SELECT" list?
3910: *
3911: * @return max columns
3912: * @throws SQLException
3913: * DOCUMENT ME!
3914: */
3915: public int getMaxColumnsInSelect() throws SQLException {
3916: return 256;
3917: }
3918:
3919: /**
3920: * What's maximum number of columns in a table?
3921: *
3922: * @return max columns
3923: * @throws SQLException
3924: * DOCUMENT ME!
3925: */
3926: public int getMaxColumnsInTable() throws SQLException {
3927: return 512;
3928: }
3929:
3930: /**
3931: * How many active connections can we have at a time to this database?
3932: *
3933: * @return max connections
3934: * @throws SQLException
3935: * DOCUMENT ME!
3936: */
3937: public int getMaxConnections() throws SQLException {
3938: return 0;
3939: }
3940:
3941: /**
3942: * What's the maximum cursor name length?
3943: *
3944: * @return max cursor name length in bytes
3945: * @throws SQLException
3946: * DOCUMENT ME!
3947: */
3948: public int getMaxCursorNameLength() throws SQLException {
3949: return 64;
3950: }
3951:
3952: /**
3953: * What's the maximum length of an index (in bytes)?
3954: *
3955: * @return max index length in bytes
3956: * @throws SQLException
3957: * DOCUMENT ME!
3958: */
3959: public int getMaxIndexLength() throws SQLException {
3960: return 256;
3961: }
3962:
3963: /**
3964: * What's the maximum length of a procedure name?
3965: *
3966: * @return max name length in bytes
3967: * @throws SQLException
3968: * DOCUMENT ME!
3969: */
3970: public int getMaxProcedureNameLength() throws SQLException {
3971: return 0;
3972: }
3973:
3974: /**
3975: * What's the maximum length of a single row?
3976: *
3977: * @return max row size in bytes
3978: * @throws SQLException
3979: * DOCUMENT ME!
3980: */
3981: public int getMaxRowSize() throws SQLException {
3982: return Integer.MAX_VALUE - 8; // Max buffer size - HEADER
3983: }
3984:
3985: /**
3986: * What's the maximum length allowed for a schema name?
3987: *
3988: * @return max name length in bytes
3989: * @throws SQLException
3990: * DOCUMENT ME!
3991: */
3992: public int getMaxSchemaNameLength() throws SQLException {
3993: return 0;
3994: }
3995:
3996: /**
3997: * What's the maximum length of a SQL statement?
3998: *
3999: * @return max length in bytes
4000: * @throws SQLException
4001: * DOCUMENT ME!
4002: */
4003: public int getMaxStatementLength() throws SQLException {
4004: return MysqlIO.getMaxBuf() - 4; // Max buffer - header
4005: }
4006:
4007: /**
4008: * How many active statements can we have open at one time to this database?
4009: *
4010: * @return the maximum
4011: * @throws SQLException
4012: * DOCUMENT ME!
4013: */
4014: public int getMaxStatements() throws SQLException {
4015: return 0;
4016: }
4017:
4018: /**
4019: * What's the maximum length of a table name?
4020: *
4021: * @return max name length in bytes
4022: * @throws SQLException
4023: * DOCUMENT ME!
4024: */
4025: public int getMaxTableNameLength() throws SQLException {
4026: return 64;
4027: }
4028:
4029: /**
4030: * What's the maximum number of tables in a SELECT?
4031: *
4032: * @return the maximum
4033: * @throws SQLException
4034: * DOCUMENT ME!
4035: */
4036: public int getMaxTablesInSelect() throws SQLException {
4037: return 256;
4038: }
4039:
4040: /**
4041: * What's the maximum length of a user name?
4042: *
4043: * @return max name length in bytes
4044: * @throws SQLException
4045: * DOCUMENT ME!
4046: */
4047: public int getMaxUserNameLength() throws SQLException {
4048: return 16;
4049: }
4050:
4051: /**
4052: * Get a comma separated list of math functions.
4053: *
4054: * @return the list
4055: * @throws SQLException
4056: * DOCUMENT ME!
4057: */
4058: public String getNumericFunctions() throws SQLException {
4059: return "ABS,ACOS,ASIN,ATAN,ATAN2,BIT_COUNT,CEILING,COS,"
4060: + "COT,DEGREES,EXP,FLOOR,LOG,LOG10,MAX,MIN,MOD,PI,POW,"
4061: + "POWER,RADIANS,RAND,ROUND,SIN,SQRT,TAN,TRUNCATE";
4062: }
4063:
4064: /**
4065: * Get a description of a table's primary key columns. They are ordered by
4066: * COLUMN_NAME.
4067: * <P>
4068: * Each column description has the following columns:
4069: * <OL>
4070: * <li> <B>TABLE_CAT</B> String => table catalog (may be null) </li>
4071: * <li> <B>TABLE_SCHEM</B> String => table schema (may be null) </li>
4072: * <li> <B>TABLE_NAME</B> String => table name </li>
4073: * <li> <B>COLUMN_NAME</B> String => column name </li>
4074: * <li> <B>KEY_SEQ</B> short => sequence number within primary key </li>
4075: * <li> <B>PK_NAME</B> String => primary key name (may be null) </li>
4076: * </ol>
4077: * </p>
4078: *
4079: * @param catalog
4080: * a catalog name; "" retrieves those without a catalog
4081: * @param schema
4082: * a schema name pattern; "" retrieves those without a schema
4083: * @param table
4084: * a table name
4085: * @return ResultSet each row is a primary key column description
4086: * @throws SQLException
4087: * DOCUMENT ME!
4088: */
4089: public java.sql.ResultSet getPrimaryKeys(String catalog,
4090: String schema, final String table) throws SQLException {
4091: Field[] fields = new Field[6];
4092: fields[0] = new Field("", "TABLE_CAT", Types.CHAR, 255);
4093: fields[1] = new Field("", "TABLE_SCHEM", Types.CHAR, 0);
4094: fields[2] = new Field("", "TABLE_NAME", Types.CHAR, 255);
4095: fields[3] = new Field("", "COLUMN_NAME", Types.CHAR, 32);
4096: fields[4] = new Field("", "KEY_SEQ", Types.SMALLINT, 5);
4097: fields[5] = new Field("", "PK_NAME", Types.CHAR, 32);
4098:
4099: if (table == null) {
4100: throw SQLError.createSQLException("Table not specified.",
4101: SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
4102: }
4103:
4104: final ArrayList rows = new ArrayList();
4105: final Statement stmt = this .conn.getMetadataSafeStatement();
4106:
4107: try {
4108:
4109: new IterateBlock(getCatalogIterator(catalog)) {
4110: void forEach(Object catalogStr) throws SQLException {
4111: ResultSet rs = null;
4112:
4113: try {
4114:
4115: StringBuffer queryBuf = new StringBuffer(
4116: "SHOW KEYS FROM ");
4117: queryBuf.append(quotedId);
4118: queryBuf.append(table);
4119: queryBuf.append(quotedId);
4120: queryBuf.append(" FROM ");
4121: queryBuf.append(quotedId);
4122: queryBuf.append(catalogStr.toString());
4123: queryBuf.append(quotedId);
4124:
4125: rs = stmt.executeQuery(queryBuf.toString());
4126:
4127: TreeMap sortMap = new TreeMap();
4128:
4129: while (rs.next()) {
4130: String keyType = rs.getString("Key_name");
4131:
4132: if (keyType != null) {
4133: if (keyType.equalsIgnoreCase("PRIMARY")
4134: || keyType
4135: .equalsIgnoreCase("PRI")) {
4136: byte[][] tuple = new byte[6][];
4137: tuple[0] = ((catalogStr.toString() == null) ? new byte[0]
4138: : s2b(catalogStr.toString()));
4139: tuple[1] = null;
4140: tuple[2] = s2b(table);
4141:
4142: String columnName = rs
4143: .getString("Column_name");
4144: tuple[3] = s2b(columnName);
4145: tuple[4] = s2b(rs
4146: .getString("Seq_in_index"));
4147: tuple[5] = s2b(keyType);
4148: sortMap.put(columnName, tuple);
4149: }
4150: }
4151: }
4152:
4153: // Now pull out in column name sorted order
4154: Iterator sortedIterator = sortMap.values()
4155: .iterator();
4156:
4157: while (sortedIterator.hasNext()) {
4158: rows.add(new ByteArrayRow(
4159: (byte[][]) sortedIterator.next()));
4160: }
4161:
4162: } finally {
4163: if (rs != null) {
4164: try {
4165: rs.close();
4166: } catch (Exception ex) {
4167: ;
4168: }
4169:
4170: rs = null;
4171: }
4172: }
4173: }
4174: }.doForAll();
4175: } finally {
4176: if (stmt != null) {
4177: stmt.close();
4178: }
4179: }
4180:
4181: java.sql.ResultSet results = buildResultSet(fields, rows);
4182:
4183: return results;
4184: }
4185:
4186: /**
4187: * Get a description of a catalog's stored procedure parameters and result
4188: * columns.
4189: * <P>
4190: * Only descriptions matching the schema, procedure and parameter name
4191: * criteria are returned. They are ordered by PROCEDURE_SCHEM and
4192: * PROCEDURE_NAME. Within this, the return value, if any, is first. Next are
4193: * the parameter descriptions in call order. The column descriptions follow
4194: * in column number order.
4195: * </p>
4196: * <P>
4197: * Each row in the ResultSet is a parameter desription or column description
4198: * with the following fields:
4199: * <OL>
4200: * <li> <B>PROCEDURE_CAT</B> String => procedure catalog (may be null)
4201: * </li>
4202: * <li> <B>PROCEDURE_SCHEM</B> String => procedure schema (may be null)
4203: * </li>
4204: * <li> <B>PROCEDURE_NAME</B> String => procedure name </li>
4205: * <li> <B>COLUMN_NAME</B> String => column/parameter name </li>
4206: * <li> <B>COLUMN_TYPE</B> Short => kind of column/parameter:
4207: * <UL>
4208: * <li> procedureColumnUnknown - nobody knows </li>
4209: * <li> procedureColumnIn - IN parameter </li>
4210: * <li> procedureColumnInOut - INOUT parameter </li>
4211: * <li> procedureColumnOut - OUT parameter </li>
4212: * <li> procedureColumnReturn - procedure return value </li>
4213: * <li> procedureColumnResult - result column in ResultSet </li>
4214: * </ul>
4215: * </li>
4216: * <li> <B>DATA_TYPE</B> short => SQL type from java.sql.Types </li>
4217: * <li> <B>TYPE_NAME</B> String => SQL type name </li>
4218: * <li> <B>PRECISION</B> int => precision </li>
4219: * <li> <B>LENGTH</B> int => length in bytes of data </li>
4220: * <li> <B>SCALE</B> short => scale </li>
4221: * <li> <B>RADIX</B> short => radix </li>
4222: * <li> <B>NULLABLE</B> short => can it contain NULL?
4223: * <UL>
4224: * <li> procedureNoNulls - does not allow NULL values </li>
4225: * <li> procedureNullable - allows NULL values </li>
4226: * <li> procedureNullableUnknown - nullability unknown </li>
4227: * </ul>
4228: * </li>
4229: * <li> <B>REMARKS</B> String => comment describing parameter/column </li>
4230: * </ol>
4231: * </p>
4232: * <P>
4233: * <B>Note:</B> Some databases may not return the column descriptions for a
4234: * procedure. Additional columns beyond REMARKS can be defined by the
4235: * database.
4236: * </p>
4237: *
4238: * @param catalog
4239: * a catalog name; "" retrieves those without a catalog
4240: * @param schemaPattern
4241: * a schema name pattern; "" retrieves those without a schema
4242: * @param procedureNamePattern
4243: * a procedure name pattern
4244: * @param columnNamePattern
4245: * a column name pattern
4246: * @return ResultSet each row is a stored procedure parameter or column
4247: * description
4248: * @throws SQLException
4249: * if a database access error occurs
4250: * @see #getSearchStringEscape
4251: */
4252: public java.sql.ResultSet getProcedureColumns(String catalog,
4253: String schemaPattern, String procedureNamePattern,
4254: String columnNamePattern) throws SQLException {
4255: Field[] fields = new Field[13];
4256:
4257: fields[0] = new Field("", "PROCEDURE_CAT", Types.CHAR, 0);
4258: fields[1] = new Field("", "PROCEDURE_SCHEM", Types.CHAR, 0);
4259: fields[2] = new Field("", "PROCEDURE_NAME", Types.CHAR, 0);
4260: fields[3] = new Field("", "COLUMN_NAME", Types.CHAR, 0);
4261: fields[4] = new Field("", "COLUMN_TYPE", Types.CHAR, 0);
4262: fields[5] = new Field("", "DATA_TYPE", Types.SMALLINT, 0);
4263: fields[6] = new Field("", "TYPE_NAME", Types.CHAR, 0);
4264: fields[7] = new Field("", "PRECISION", Types.INTEGER, 0);
4265: fields[8] = new Field("", "LENGTH", Types.INTEGER, 0);
4266: fields[9] = new Field("", "SCALE", Types.SMALLINT, 0);
4267: fields[10] = new Field("", "RADIX", Types.SMALLINT, 0);
4268: fields[11] = new Field("", "NULLABLE", Types.SMALLINT, 0);
4269: fields[12] = new Field("", "REMARKS", Types.CHAR, 0);
4270:
4271: return getProcedureOrFunctionColumns(fields, catalog,
4272: schemaPattern, procedureNamePattern, columnNamePattern,
4273: true, true);
4274: }
4275:
4276: protected java.sql.ResultSet getProcedureOrFunctionColumns(
4277: Field[] fields, String catalog, String schemaPattern,
4278: String procedureOrFunctionNamePattern,
4279: String columnNamePattern, boolean returnProcedures,
4280: boolean returnFunctions) throws SQLException {
4281:
4282: List proceduresToExtractList = new ArrayList();
4283:
4284: if (supportsStoredProcedures()) {
4285: if ((procedureOrFunctionNamePattern.indexOf("%") == -1)
4286: && (procedureOrFunctionNamePattern.indexOf("?") == -1)) {
4287: proceduresToExtractList
4288: .add(procedureOrFunctionNamePattern);
4289: } else {
4290:
4291: ResultSet procedureNameRs = null;
4292:
4293: try {
4294:
4295: procedureNameRs = getProceduresAndOrFunctions(
4296: createFieldMetadataForGetProcedures(),
4297: catalog, schemaPattern,
4298: procedureOrFunctionNamePattern,
4299: returnProcedures, returnFunctions);
4300:
4301: while (procedureNameRs.next()) {
4302: proceduresToExtractList.add(procedureNameRs
4303: .getString(3));
4304: }
4305:
4306: // Required to be sorted in name-order by JDBC spec,
4307: // in 'normal' case getProcedures takes care of this for us,
4308: // but if system tables are inaccessible, we need to sort...
4309: // so just do this to be safe...
4310: Collections.sort(proceduresToExtractList);
4311: } finally {
4312: SQLException rethrowSqlEx = null;
4313:
4314: if (procedureNameRs != null) {
4315: try {
4316: procedureNameRs.close();
4317: } catch (SQLException sqlEx) {
4318: rethrowSqlEx = sqlEx;
4319: }
4320: }
4321:
4322: if (rethrowSqlEx != null) {
4323: throw rethrowSqlEx;
4324: }
4325: }
4326: }
4327: }
4328:
4329: ArrayList resultRows = new ArrayList();
4330:
4331: for (Iterator iter = proceduresToExtractList.iterator(); iter
4332: .hasNext();) {
4333: String procName = (String) iter.next();
4334:
4335: getCallStmtParameterTypes(catalog, procName,
4336: columnNamePattern, resultRows, fields.length == 17 /* for getFunctionColumns */);
4337: }
4338:
4339: return buildResultSet(fields, resultRows);
4340: }
4341:
4342: /**
4343: * Get a description of stored procedures available in a catalog.
4344: * <P>
4345: * Only procedure descriptions matching the schema and procedure name
4346: * criteria are returned. They are ordered by PROCEDURE_SCHEM, and
4347: * PROCEDURE_NAME.
4348: * </p>
4349: * <P>
4350: * Each procedure description has the the following columns:
4351: * <OL>
4352: * <li> <B>PROCEDURE_CAT</B> String => procedure catalog (may be null)
4353: * </li>
4354: * <li> <B>PROCEDURE_SCHEM</B> String => procedure schema (may be null)
4355: * </li>
4356: * <li> <B>PROCEDURE_NAME</B> String => procedure name </li>
4357: * <li> reserved for future use </li>
4358: * <li> reserved for future use </li>
4359: * <li> reserved for future use </li>
4360: * <li> <B>REMARKS</B> String => explanatory comment on the procedure </li>
4361: * <li> <B>PROCEDURE_TYPE</B> short => kind of procedure:
4362: * <UL>
4363: * <li> procedureResultUnknown - May return a result </li>
4364: * <li> procedureNoResult - Does not return a result </li>
4365: * <li> procedureReturnsResult - Returns a result </li>
4366: * </ul>
4367: * </li>
4368: * </ol>
4369: * </p>
4370: *
4371: * @param catalog
4372: * a catalog name; "" retrieves those without a catalog
4373: * @param schemaPattern
4374: * a schema name pattern; "" retrieves those without a schema
4375: * @param procedureNamePattern
4376: * a procedure name pattern
4377: * @return ResultSet each row is a procedure description
4378: * @throws SQLException
4379: * if a database access error occurs
4380: * @see #getSearchStringEscape
4381: */
4382: public java.sql.ResultSet getProcedures(String catalog,
4383: String schemaPattern, String procedureNamePattern)
4384: throws SQLException {
4385: Field[] fields = createFieldMetadataForGetProcedures();
4386:
4387: return getProceduresAndOrFunctions(fields, catalog,
4388: schemaPattern, procedureNamePattern, true, true);
4389: }
4390:
4391: private Field[] createFieldMetadataForGetProcedures() {
4392: Field[] fields = new Field[8];
4393: fields[0] = new Field("", "PROCEDURE_CAT", Types.CHAR, 0);
4394: fields[1] = new Field("", "PROCEDURE_SCHEM", Types.CHAR, 0);
4395: fields[2] = new Field("", "PROCEDURE_NAME", Types.CHAR, 0);
4396: fields[3] = new Field("", "reserved1", Types.CHAR, 0);
4397: fields[4] = new Field("", "reserved2", Types.CHAR, 0);
4398: fields[5] = new Field("", "reserved3", Types.CHAR, 0);
4399: fields[6] = new Field("", "REMARKS", Types.CHAR, 0);
4400: fields[7] = new Field("", "PROCEDURE_TYPE", Types.SMALLINT, 0);
4401: return fields;
4402: }
4403:
4404: protected java.sql.ResultSet getProceduresAndOrFunctions(
4405: final Field[] fields, String catalog, String schemaPattern,
4406: String procedureNamePattern,
4407: final boolean returnProcedures,
4408: final boolean returnFunctions) throws SQLException {
4409: if ((procedureNamePattern == null)
4410: || (procedureNamePattern.length() == 0)) {
4411: if (this .conn.getNullNamePatternMatchesAll()) {
4412: procedureNamePattern = "%";
4413: } else {
4414: throw SQLError
4415: .createSQLException(
4416: "Procedure name pattern can not be NULL or empty.",
4417: SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
4418: }
4419: }
4420:
4421: final ArrayList procedureRows = new ArrayList();
4422:
4423: if (supportsStoredProcedures()) {
4424: final String procNamePattern = procedureNamePattern;
4425:
4426: final Map procedureRowsOrderedByName = new TreeMap();
4427:
4428: new IterateBlock(getCatalogIterator(catalog)) {
4429: void forEach(Object catalogStr) throws SQLException {
4430: String db = catalogStr.toString();
4431:
4432: boolean fromSelect = false;
4433: ResultSet proceduresRs = null;
4434: boolean needsClientFiltering = true;
4435: PreparedStatement proceduresStmt = conn
4436: .clientPrepareStatement("SELECT name, type, comment FROM mysql.proc WHERE name like ? and db <=> ? ORDER BY name");
4437:
4438: try {
4439: //
4440: // Try using system tables first, as this is a little
4441: // bit more efficient....
4442: //
4443:
4444: boolean hasTypeColumn = false;
4445:
4446: if (db != null) {
4447: proceduresStmt.setString(2, db);
4448: } else {
4449: proceduresStmt.setNull(2, Types.VARCHAR);
4450: }
4451:
4452: int nameIndex = 1;
4453:
4454: if (proceduresStmt.getMaxRows() != 0) {
4455: proceduresStmt.setMaxRows(0);
4456: }
4457:
4458: proceduresStmt.setString(1, procNamePattern);
4459:
4460: try {
4461: proceduresRs = proceduresStmt
4462: .executeQuery();
4463: fromSelect = true;
4464: needsClientFiltering = false;
4465: hasTypeColumn = true;
4466: } catch (SQLException sqlEx) {
4467:
4468: //
4469: // Okay, system tables aren't accessible, so use
4470: // 'SHOW
4471: // ....'....
4472: //
4473: proceduresStmt.close();
4474:
4475: fromSelect = false;
4476:
4477: if (conn.versionMeetsMinimum(5, 0, 1)) {
4478: nameIndex = 2;
4479: } else {
4480: nameIndex = 1;
4481: }
4482:
4483: proceduresStmt = conn
4484: .clientPrepareStatement("SHOW PROCEDURE STATUS LIKE ?");
4485:
4486: if (proceduresStmt.getMaxRows() != 0) {
4487: proceduresStmt.setMaxRows(0);
4488: }
4489:
4490: proceduresStmt
4491: .setString(1, procNamePattern);
4492:
4493: proceduresRs = proceduresStmt
4494: .executeQuery();
4495: }
4496:
4497: if (returnProcedures) {
4498: convertToJdbcProcedureList(fromSelect, db,
4499: proceduresRs, needsClientFiltering,
4500: db, procedureRowsOrderedByName,
4501: nameIndex);
4502: }
4503:
4504: if (!hasTypeColumn) {
4505: // need to go after functions too...
4506: if (proceduresStmt != null) {
4507: proceduresStmt.close();
4508: }
4509:
4510: proceduresStmt = conn
4511: .clientPrepareStatement("SHOW FUNCTION STATUS LIKE ?");
4512:
4513: if (proceduresStmt.getMaxRows() != 0) {
4514: proceduresStmt.setMaxRows(0);
4515: }
4516:
4517: proceduresStmt
4518: .setString(1, procNamePattern);
4519:
4520: proceduresRs = proceduresStmt
4521: .executeQuery();
4522:
4523: if (returnFunctions) {
4524: convertToJdbcFunctionList(db,
4525: proceduresRs,
4526: needsClientFiltering, db,
4527: procedureRowsOrderedByName,
4528: nameIndex, fields);
4529: }
4530: }
4531:
4532: // Now, sort them
4533:
4534: Iterator proceduresIter = procedureRowsOrderedByName
4535: .values().iterator();
4536:
4537: while (proceduresIter.hasNext()) {
4538: procedureRows.add(proceduresIter.next());
4539: }
4540: } finally {
4541: SQLException rethrowSqlEx = null;
4542:
4543: if (proceduresRs != null) {
4544: try {
4545: proceduresRs.close();
4546: } catch (SQLException sqlEx) {
4547: rethrowSqlEx = sqlEx;
4548: }
4549: }
4550:
4551: if (proceduresStmt != null) {
4552: try {
4553: proceduresStmt.close();
4554: } catch (SQLException sqlEx) {
4555: rethrowSqlEx = sqlEx;
4556: }
4557: }
4558:
4559: if (rethrowSqlEx != null) {
4560: throw rethrowSqlEx;
4561: }
4562: }
4563: }
4564: }.doForAll();
4565: }
4566:
4567: return buildResultSet(fields, procedureRows);
4568: }
4569:
4570: /**
4571: * What's the database vendor's preferred term for "procedure"?
4572: *
4573: * @return the vendor term
4574: * @throws SQLException
4575: * if an error occurs (don't know why it would in this case...)
4576: */
4577: public String getProcedureTerm() throws SQLException {
4578: return "PROCEDURE";
4579: }
4580:
4581: /**
4582: * @see DatabaseMetaData#getResultSetHoldability()
4583: */
4584: public int getResultSetHoldability() throws SQLException {
4585: return ResultSet.HOLD_CURSORS_OVER_COMMIT;
4586: }
4587:
4588: private void getResultsImpl(String catalog, String table,
4589: String keysComment, List tuples, String fkTableName,
4590: boolean isExport) throws SQLException {
4591:
4592: LocalAndReferencedColumns parsedInfo = parseTableStatusIntoLocalAndReferencedColumns(keysComment);
4593:
4594: if (isExport && !parsedInfo.referencedTable.equals(table)) {
4595: return;
4596: }
4597:
4598: if (parsedInfo.localColumnsList.size() != parsedInfo.referencedColumnsList
4599: .size()) {
4600: throw SQLError
4601: .createSQLException(
4602: "Error parsing foreign keys definition,"
4603: + "number of local and referenced columns is not the same.",
4604: SQLError.SQL_STATE_GENERAL_ERROR);
4605: }
4606:
4607: Iterator localColumnNames = parsedInfo.localColumnsList
4608: .iterator();
4609: Iterator referColumnNames = parsedInfo.referencedColumnsList
4610: .iterator();
4611:
4612: int keySeqIndex = 1;
4613:
4614: while (localColumnNames.hasNext()) {
4615: byte[][] tuple = new byte[14][];
4616: String lColumnName = removeQuotedId(localColumnNames.next()
4617: .toString());
4618: String rColumnName = removeQuotedId(referColumnNames.next()
4619: .toString());
4620: tuple[FKTABLE_CAT] = ((catalog == null) ? new byte[0]
4621: : s2b(catalog));
4622: tuple[FKTABLE_SCHEM] = null;
4623: tuple[FKTABLE_NAME] = s2b((isExport) ? fkTableName : table);
4624: tuple[FKCOLUMN_NAME] = s2b(lColumnName);
4625: tuple[PKTABLE_CAT] = s2b(parsedInfo.referencedCatalog);
4626: tuple[PKTABLE_SCHEM] = null;
4627: tuple[PKTABLE_NAME] = s2b((isExport) ? table
4628: : parsedInfo.referencedTable);
4629: tuple[PKCOLUMN_NAME] = s2b(rColumnName);
4630: tuple[KEY_SEQ] = s2b(Integer.toString(keySeqIndex++));
4631:
4632: int[] actions = getForeignKeyActions(keysComment);
4633:
4634: tuple[UPDATE_RULE] = s2b(Integer.toString(actions[1]));
4635: tuple[DELETE_RULE] = s2b(Integer.toString(actions[0]));
4636: tuple[FK_NAME] = s2b(parsedInfo.constraintName);
4637: tuple[PK_NAME] = null; // not available from show table status
4638: tuple[DEFERRABILITY] = s2b(Integer
4639: .toString(java.sql.DatabaseMetaData.importedKeyNotDeferrable));
4640: tuples.add(new ByteArrayRow(tuple));
4641: }
4642: }
4643:
4644: /**
4645: * Get the schema names available in this database. The results are ordered
4646: * by schema name.
4647: * <P>
4648: * The schema column is:
4649: * <OL>
4650: * <li> <B>TABLE_SCHEM</B> String => schema name </li>
4651: * </ol>
4652: * </p>
4653: *
4654: * @return ResultSet each row has a single String column that is a schema
4655: * name
4656: * @throws SQLException
4657: * DOCUMENT ME!
4658: */
4659: public java.sql.ResultSet getSchemas() throws SQLException {
4660: Field[] fields = new Field[2];
4661: fields[0] = new Field("", "TABLE_SCHEM", java.sql.Types.CHAR, 0);
4662: fields[1] = new Field("", "TABLE_CATALOG", java.sql.Types.CHAR,
4663: 0);
4664:
4665: ArrayList tuples = new ArrayList();
4666: java.sql.ResultSet results = buildResultSet(fields, tuples);
4667:
4668: return results;
4669: }
4670:
4671: /**
4672: * What's the database vendor's preferred term for "schema"?
4673: *
4674: * @return the vendor term
4675: * @throws SQLException
4676: * DOCUMENT ME!
4677: */
4678: public String getSchemaTerm() throws SQLException {
4679: return "";
4680: }
4681:
4682: /**
4683: * This is the string that can be used to escape '_' or '%' in the string
4684: * pattern style catalog search parameters.
4685: * <P>
4686: * The '_' character represents any single character.
4687: * </p>
4688: * <P>
4689: * The '%' character represents any sequence of zero or more characters.
4690: * </p>
4691: *
4692: * @return the string used to escape wildcard characters
4693: * @throws SQLException
4694: * DOCUMENT ME!
4695: */
4696: public String getSearchStringEscape() throws SQLException {
4697: return "\\";
4698: }
4699:
4700: /**
4701: * Get a comma separated list of all a database's SQL keywords that are NOT
4702: * also SQL92 keywords.
4703: *
4704: * @return the list
4705: * @throws SQLException
4706: * DOCUMENT ME!
4707: */
4708: public String getSQLKeywords() throws SQLException {
4709: return mysqlKeywordsThatArentSQL92;
4710: }
4711:
4712: /**
4713: * @see DatabaseMetaData#getSQLStateType()
4714: */
4715: public int getSQLStateType() throws SQLException {
4716: if (this .conn.versionMeetsMinimum(4, 1, 0)) {
4717: return DatabaseMetaData.sqlStateSQL99;
4718: }
4719:
4720: if (this .conn.getUseSqlStateCodes()) {
4721: return DatabaseMetaData.sqlStateSQL99;
4722: }
4723:
4724: return DatabaseMetaData.sqlStateXOpen;
4725: }
4726:
4727: /**
4728: * Get a comma separated list of string functions.
4729: *
4730: * @return the list
4731: * @throws SQLException
4732: * DOCUMENT ME!
4733: */
4734: public String getStringFunctions() throws SQLException {
4735: return "ASCII,BIN,BIT_LENGTH,CHAR,CHARACTER_LENGTH,CHAR_LENGTH,CONCAT,"
4736: + "CONCAT_WS,CONV,ELT,EXPORT_SET,FIELD,FIND_IN_SET,HEX,INSERT,"
4737: + "INSTR,LCASE,LEFT,LENGTH,LOAD_FILE,LOCATE,LOCATE,LOWER,LPAD,"
4738: + "LTRIM,MAKE_SET,MATCH,MID,OCT,OCTET_LENGTH,ORD,POSITION,"
4739: + "QUOTE,REPEAT,REPLACE,REVERSE,RIGHT,RPAD,RTRIM,SOUNDEX,"
4740: + "SPACE,STRCMP,SUBSTRING,SUBSTRING,SUBSTRING,SUBSTRING,"
4741: + "SUBSTRING_INDEX,TRIM,UCASE,UPPER";
4742: }
4743:
4744: /**
4745: * @see DatabaseMetaData#getSuperTables(String, String, String)
4746: */
4747: public java.sql.ResultSet getSuperTables(String arg0, String arg1,
4748: String arg2) throws SQLException {
4749: Field[] fields = new Field[4];
4750: fields[0] = new Field("", "TABLE_CAT", Types.CHAR, 32);
4751: fields[1] = new Field("", "TABLE_SCHEM", Types.CHAR, 32);
4752: fields[2] = new Field("", "TABLE_NAME", Types.CHAR, 32);
4753: fields[3] = new Field("", "SUPERTABLE_NAME", Types.CHAR, 32);
4754:
4755: return buildResultSet(fields, new ArrayList());
4756: }
4757:
4758: /**
4759: * @see DatabaseMetaData#getSuperTypes(String, String, String)
4760: */
4761: public java.sql.ResultSet getSuperTypes(String arg0, String arg1,
4762: String arg2) throws SQLException {
4763: Field[] fields = new Field[6];
4764: fields[0] = new Field("", "TABLE_CAT", Types.CHAR, 32);
4765: fields[1] = new Field("", "TABLE_SCHEM", Types.CHAR, 32);
4766: fields[2] = new Field("", "TYPE_NAME", Types.CHAR, 32);
4767: fields[3] = new Field("", "SUPERTYPE_CAT", Types.CHAR, 32);
4768: fields[4] = new Field("", "SUPERTYPE_SCHEM", Types.CHAR, 32);
4769: fields[5] = new Field("", "SUPERTYPE_NAME", Types.CHAR, 32);
4770:
4771: return buildResultSet(fields, new ArrayList());
4772: }
4773:
4774: /**
4775: * Get a comma separated list of system functions.
4776: *
4777: * @return the list
4778: * @throws SQLException
4779: * DOCUMENT ME!
4780: */
4781: public String getSystemFunctions() throws SQLException {
4782: return "DATABASE,USER,SYSTEM_USER,SESSION_USER,PASSWORD,ENCRYPT,LAST_INSERT_ID,VERSION";
4783: }
4784:
4785: private String getTableNameWithCase(String table) {
4786: String tableNameWithCase = (this .conn.lowerCaseTableNames() ? table
4787: .toLowerCase()
4788: : table);
4789:
4790: return tableNameWithCase;
4791: }
4792:
4793: /**
4794: * Get a description of the access rights for each table available in a
4795: * catalog.
4796: * <P>
4797: * Only privileges matching the schema and table name criteria are returned.
4798: * They are ordered by TABLE_SCHEM, TABLE_NAME, and PRIVILEGE.
4799: * </p>
4800: * <P>
4801: * Each privilige description has the following columns:
4802: * <OL>
4803: * <li> <B>TABLE_CAT</B> String => table catalog (may be null) </li>
4804: * <li> <B>TABLE_SCHEM</B> String => table schema (may be null) </li>
4805: * <li> <B>TABLE_NAME</B> String => table name </li>
4806: * <li> <B>COLUMN_NAME</B> String => column name </li>
4807: * <li> <B>GRANTOR</B> => grantor of access (may be null) </li>
4808: * <li> <B>GRANTEE</B> String => grantee of access </li>
4809: * <li> <B>PRIVILEGE</B> String => name of access (SELECT, INSERT, UPDATE,
4810: * REFRENCES, ...) </li>
4811: * <li> <B>IS_GRANTABLE</B> String => "YES" if grantee is permitted to
4812: * grant to others; "NO" if not; null if unknown </li>
4813: * </ol>
4814: * </p>
4815: *
4816: * @param catalog
4817: * a catalog name; "" retrieves those without a catalog
4818: * @param schemaPattern
4819: * a schema name pattern; "" retrieves those without a schema
4820: * @param tableNamePattern
4821: * a table name pattern
4822: * @return ResultSet each row is a table privilege description
4823: * @throws SQLException
4824: * if a database access error occurs
4825: * @see #getSearchStringEscape
4826: */
4827: public java.sql.ResultSet getTablePrivileges(String catalog,
4828: String schemaPattern, String tableNamePattern)
4829: throws SQLException {
4830:
4831: if (tableNamePattern == null) {
4832: if (this .conn.getNullNamePatternMatchesAll()) {
4833: tableNamePattern = "%";
4834: } else {
4835: throw SQLError.createSQLException(
4836: "Table name pattern can not be NULL or empty.",
4837: SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
4838: }
4839: }
4840:
4841: Field[] fields = new Field[7];
4842: fields[0] = new Field("", "TABLE_CAT", Types.CHAR, 64);
4843: fields[1] = new Field("", "TABLE_SCHEM", Types.CHAR, 1);
4844: fields[2] = new Field("", "TABLE_NAME", Types.CHAR, 64);
4845: fields[3] = new Field("", "GRANTOR", Types.CHAR, 77);
4846: fields[4] = new Field("", "GRANTEE", Types.CHAR, 77);
4847: fields[5] = new Field("", "PRIVILEGE", Types.CHAR, 64);
4848: fields[6] = new Field("", "IS_GRANTABLE", Types.CHAR, 3);
4849:
4850: StringBuffer grantQuery = new StringBuffer(
4851: "SELECT host,db,table_name,grantor,user,table_priv from mysql.tables_priv ");
4852: grantQuery.append(" WHERE ");
4853:
4854: if ((catalog != null) && (catalog.length() != 0)) {
4855: grantQuery.append(" db='");
4856: grantQuery.append(catalog);
4857: grantQuery.append("' AND ");
4858: }
4859:
4860: grantQuery.append("table_name like '");
4861: grantQuery.append(tableNamePattern);
4862: grantQuery.append("'");
4863:
4864: ResultSet results = null;
4865: ArrayList grantRows = new ArrayList();
4866: Statement stmt = null;
4867:
4868: try {
4869: stmt = this .conn.createStatement();
4870: stmt.setEscapeProcessing(false);
4871:
4872: results = stmt.executeQuery(grantQuery.toString());
4873:
4874: while (results.next()) {
4875: String host = results.getString(1);
4876: String db = results.getString(2);
4877: String table = results.getString(3);
4878: String grantor = results.getString(4);
4879: String user = results.getString(5);
4880:
4881: if ((user == null) || (user.length() == 0)) {
4882: user = "%";
4883: }
4884:
4885: StringBuffer fullUser = new StringBuffer(user);
4886:
4887: if ((host != null)
4888: && this .conn.getUseHostsInPrivileges()) {
4889: fullUser.append("@");
4890: fullUser.append(host);
4891: }
4892:
4893: String allPrivileges = results.getString(6);
4894:
4895: if (allPrivileges != null) {
4896: allPrivileges = allPrivileges
4897: .toUpperCase(Locale.ENGLISH);
4898:
4899: StringTokenizer st = new StringTokenizer(
4900: allPrivileges, ",");
4901:
4902: while (st.hasMoreTokens()) {
4903: String privilege = st.nextToken().trim();
4904:
4905: // Loop through every column in the table
4906: java.sql.ResultSet columnResults = null;
4907:
4908: try {
4909: columnResults = getColumns(catalog,
4910: schemaPattern, table, "%");
4911:
4912: while (columnResults.next()) {
4913: byte[][] tuple = new byte[8][];
4914: tuple[0] = s2b(db);
4915: tuple[1] = null;
4916: tuple[2] = s2b(table);
4917:
4918: if (grantor != null) {
4919: tuple[3] = s2b(grantor);
4920: } else {
4921: tuple[3] = null;
4922: }
4923:
4924: tuple[4] = s2b(fullUser.toString());
4925: tuple[5] = s2b(privilege);
4926: tuple[6] = null;
4927: grantRows.add(new ByteArrayRow(tuple));
4928: }
4929: } finally {
4930: if (columnResults != null) {
4931: try {
4932: columnResults.close();
4933: } catch (Exception ex) {
4934: ;
4935: }
4936: }
4937: }
4938: }
4939: }
4940: }
4941: } finally {
4942: if (results != null) {
4943: try {
4944: results.close();
4945: } catch (Exception ex) {
4946: ;
4947: }
4948:
4949: results = null;
4950: }
4951:
4952: if (stmt != null) {
4953: try {
4954: stmt.close();
4955: } catch (Exception ex) {
4956: ;
4957: }
4958:
4959: stmt = null;
4960: }
4961: }
4962:
4963: return buildResultSet(fields, grantRows);
4964: }
4965:
4966: /**
4967: * Get a description of tables available in a catalog.
4968: * <P>
4969: * Only table descriptions matching the catalog, schema, table name and type
4970: * criteria are returned. They are ordered by TABLE_TYPE, TABLE_SCHEM and
4971: * TABLE_NAME.
4972: * </p>
4973: * <P>
4974: * Each table description has the following columns:
4975: * <OL>
4976: * <li> <B>TABLE_CAT</B> String => table catalog (may be null) </li>
4977: * <li> <B>TABLE_SCHEM</B> String => table schema (may be null) </li>
4978: * <li> <B>TABLE_NAME</B> String => table name </li>
4979: * <li> <B>TABLE_TYPE</B> String => table type. Typical types are "TABLE",
4980: * "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", "LOCAL TEMPORARY", "ALIAS",
4981: * "SYNONYM". </li>
4982: * <li> <B>REMARKS</B> String => explanatory comment on the table </li>
4983: * </ol>
4984: * </p>
4985: * <P>
4986: * <B>Note:</B> Some databases may not return information for all tables.
4987: * </p>
4988: *
4989: * @param catalog
4990: * a catalog name; "" retrieves those without a catalog
4991: * @param schemaPattern
4992: * a schema name pattern; "" retrieves those without a schema
4993: * @param tableNamePattern
4994: * a table name pattern
4995: * @param types
4996: * a list of table types to include; null returns all types
4997: * @return ResultSet each row is a table description
4998: * @throws SQLException
4999: * DOCUMENT ME!
5000: * @see #getSearchStringEscape
5001: */
5002: public java.sql.ResultSet getTables(String catalog,
5003: String schemaPattern, String tableNamePattern,
5004: final String[] types) throws SQLException {
5005:
5006: if (tableNamePattern == null) {
5007: if (this .conn.getNullNamePatternMatchesAll()) {
5008: tableNamePattern = "%";
5009: } else {
5010: throw SQLError.createSQLException(
5011: "Table name pattern can not be NULL or empty.",
5012: SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
5013: }
5014: }
5015:
5016: Field[] fields = new Field[5];
5017: fields[0] = new Field("", "TABLE_CAT", java.sql.Types.VARCHAR,
5018: 255);
5019: fields[1] = new Field("", "TABLE_SCHEM",
5020: java.sql.Types.VARCHAR, 0);
5021: fields[2] = new Field("", "TABLE_NAME", java.sql.Types.VARCHAR,
5022: 255);
5023: fields[3] = new Field("", "TABLE_TYPE", java.sql.Types.VARCHAR,
5024: 5);
5025: fields[4] = new Field("", "REMARKS", java.sql.Types.VARCHAR, 0);
5026:
5027: final ArrayList tuples = new ArrayList();
5028:
5029: final Statement stmt = this .conn.getMetadataSafeStatement();
5030:
5031: final String tableNamePat = tableNamePattern;
5032:
5033: try {
5034:
5035: new IterateBlock(getCatalogIterator(catalog)) {
5036: void forEach(Object catalogStr) throws SQLException {
5037: ResultSet results = null;
5038:
5039: try {
5040:
5041: if (!conn.versionMeetsMinimum(5, 0, 2)) {
5042: try {
5043: results = stmt
5044: .executeQuery("SHOW TABLES FROM "
5045: + quotedId
5046: + catalogStr.toString()
5047: + quotedId
5048: + " LIKE '"
5049: + tableNamePat + "'");
5050: } catch (SQLException sqlEx) {
5051: if (SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE
5052: .equals(sqlEx.getSQLState())) {
5053: throw sqlEx;
5054: }
5055:
5056: return;
5057: }
5058: } else {
5059: try {
5060: results = stmt
5061: .executeQuery("SHOW FULL TABLES FROM "
5062: + quotedId
5063: + catalogStr.toString()
5064: + quotedId
5065: + " LIKE '"
5066: + tableNamePat + "'");
5067: } catch (SQLException sqlEx) {
5068: if (SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE
5069: .equals(sqlEx.getSQLState())) {
5070: throw sqlEx;
5071: }
5072:
5073: return;
5074: }
5075: }
5076:
5077: boolean shouldReportTables = false;
5078: boolean shouldReportViews = false;
5079:
5080: if (types == null || types.length == 0) {
5081: shouldReportTables = true;
5082: shouldReportViews = true;
5083: } else {
5084: for (int i = 0; i < types.length; i++) {
5085: if ("TABLE".equalsIgnoreCase(types[i])) {
5086: shouldReportTables = true;
5087: }
5088:
5089: if ("VIEW".equalsIgnoreCase(types[i])) {
5090: shouldReportViews = true;
5091: }
5092: }
5093: }
5094:
5095: int typeColumnIndex = 0;
5096: boolean hasTableTypes = false;
5097:
5098: if (conn.versionMeetsMinimum(5, 0, 2)) {
5099: try {
5100: // Both column names have been in use in the
5101: // source tree
5102: // so far....
5103: typeColumnIndex = results
5104: .findColumn("table_type");
5105: hasTableTypes = true;
5106: } catch (SQLException sqlEx) {
5107:
5108: // We should probably check SQLState here, but
5109: // that
5110: // can change depending on the server version
5111: // and
5112: // user properties, however, we'll get a 'true'
5113: // SQLException when we actually try to find the
5114: // 'Type' column
5115: //
5116: try {
5117: typeColumnIndex = results
5118: .findColumn("Type");
5119: hasTableTypes = true;
5120: } catch (SQLException sqlEx2) {
5121: hasTableTypes = false;
5122: }
5123: }
5124: }
5125:
5126: TreeMap tablesOrderedByName = null;
5127: TreeMap viewsOrderedByName = null;
5128:
5129: while (results.next()) {
5130: byte[][] row = new byte[5][];
5131: row[0] = (catalogStr.toString() == null) ? null
5132: : s2b(catalogStr.toString());
5133: row[1] = null;
5134: row[2] = results.getBytes(1);
5135: row[4] = new byte[0];
5136:
5137: if (hasTableTypes) {
5138: String tableType = results
5139: .getString(typeColumnIndex);
5140:
5141: if (("table"
5142: .equalsIgnoreCase(tableType) || "base table"
5143: .equalsIgnoreCase(tableType))
5144: && shouldReportTables) {
5145: row[3] = TABLE_AS_BYTES;
5146:
5147: if (tablesOrderedByName == null) {
5148: tablesOrderedByName = new TreeMap();
5149: }
5150:
5151: tablesOrderedByName.put(results
5152: .getString(1), row);
5153: } else if ("view"
5154: .equalsIgnoreCase(tableType)
5155: && shouldReportViews) {
5156: row[3] = VIEW_AS_BYTES;
5157:
5158: if (viewsOrderedByName == null) {
5159: viewsOrderedByName = new TreeMap();
5160: }
5161:
5162: viewsOrderedByName.put(results
5163: .getString(1), row);
5164: } else if (!hasTableTypes) {
5165: // punt?
5166: row[3] = TABLE_AS_BYTES;
5167:
5168: if (tablesOrderedByName == null) {
5169: tablesOrderedByName = new TreeMap();
5170: }
5171:
5172: tablesOrderedByName.put(results
5173: .getString(1), row);
5174: }
5175: } else {
5176: if (shouldReportTables) {
5177: // Pre-MySQL-5.0.1, tables only
5178: row[3] = TABLE_AS_BYTES;
5179:
5180: if (tablesOrderedByName == null) {
5181: tablesOrderedByName = new TreeMap();
5182: }
5183:
5184: tablesOrderedByName.put(results
5185: .getString(1), row);
5186: }
5187: }
5188: }
5189:
5190: // They are ordered by TABLE_TYPE,
5191: // * TABLE_SCHEM and TABLE_NAME.
5192:
5193: if (tablesOrderedByName != null) {
5194: Iterator tablesIter = tablesOrderedByName
5195: .values().iterator();
5196:
5197: while (tablesIter.hasNext()) {
5198: tuples.add(new ByteArrayRow(
5199: (byte[][]) tablesIter.next()));
5200: }
5201: }
5202:
5203: if (viewsOrderedByName != null) {
5204: Iterator viewsIter = viewsOrderedByName
5205: .values().iterator();
5206:
5207: while (viewsIter.hasNext()) {
5208: tuples.add(new ByteArrayRow(
5209: (byte[][]) viewsIter.next()));
5210: }
5211: }
5212:
5213: } finally {
5214: if (results != null) {
5215: try {
5216: results.close();
5217: } catch (Exception ex) {
5218: ;
5219: }
5220:
5221: results = null;
5222: }
5223:
5224: }
5225: }
5226: }.doForAll();
5227: } finally {
5228: if (stmt != null) {
5229: stmt.close();
5230: }
5231: }
5232:
5233: java.sql.ResultSet tables = buildResultSet(fields, tuples);
5234:
5235: return tables;
5236: }
5237:
5238: /**
5239: * Get the table types available in this database. The results are ordered
5240: * by table type.
5241: * <P>
5242: * The table type is:
5243: * <OL>
5244: * <li> <B>TABLE_TYPE</B> String => table type. Typical types are "TABLE",
5245: * "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", "LOCAL TEMPORARY", "ALIAS",
5246: * "SYNONYM". </li>
5247: * </ol>
5248: * </p>
5249: *
5250: * @return ResultSet each row has a single String column that is a table
5251: * type
5252: * @throws SQLException
5253: * DOCUMENT ME!
5254: */
5255: public java.sql.ResultSet getTableTypes() throws SQLException {
5256: ArrayList tuples = new ArrayList();
5257: Field[] fields = new Field[1];
5258: fields[0] = new Field("", "TABLE_TYPE", Types.VARCHAR, 5);
5259:
5260: byte[][] tableTypeRow = new byte[1][];
5261: tableTypeRow[0] = TABLE_AS_BYTES;
5262: tuples.add(new ByteArrayRow(tableTypeRow));
5263:
5264: if (this .conn.versionMeetsMinimum(5, 0, 1)) {
5265: byte[][] viewTypeRow = new byte[1][];
5266: viewTypeRow[0] = VIEW_AS_BYTES;
5267: tuples.add(new ByteArrayRow(viewTypeRow));
5268: }
5269:
5270: byte[][] tempTypeRow = new byte[1][];
5271: tempTypeRow[0] = s2b("LOCAL TEMPORARY");
5272: tuples.add(new ByteArrayRow(tempTypeRow));
5273:
5274: return buildResultSet(fields, tuples);
5275: }
5276:
5277: /**
5278: * Get a comma separated list of time and date functions.
5279: *
5280: * @return the list
5281: * @throws SQLException
5282: * DOCUMENT ME!
5283: */
5284: public String getTimeDateFunctions() throws SQLException {
5285: return "DAYOFWEEK,WEEKDAY,DAYOFMONTH,DAYOFYEAR,MONTH,DAYNAME,"
5286: + "MONTHNAME,QUARTER,WEEK,YEAR,HOUR,MINUTE,SECOND,PERIOD_ADD,"
5287: + "PERIOD_DIFF,TO_DAYS,FROM_DAYS,DATE_FORMAT,TIME_FORMAT,"
5288: + "CURDATE,CURRENT_DATE,CURTIME,CURRENT_TIME,NOW,SYSDATE,"
5289: + "CURRENT_TIMESTAMP,UNIX_TIMESTAMP,FROM_UNIXTIME,"
5290: + "SEC_TO_TIME,TIME_TO_SEC";
5291: }
5292:
5293: /**
5294: * Get a description of all the standard SQL types supported by this
5295: * database. They are ordered by DATA_TYPE and then by how closely the data
5296: * type maps to the corresponding JDBC SQL type.
5297: * <P>
5298: * Each type description has the following columns:
5299: * <OL>
5300: * <li> <B>TYPE_NAME</B> String => Type name </li>
5301: * <li> <B>DATA_TYPE</B> short => SQL data type from java.sql.Types </li>
5302: * <li> <B>PRECISION</B> int => maximum precision </li>
5303: * <li> <B>LITERAL_PREFIX</B> String => prefix used to quote a literal (may
5304: * be null) </li>
5305: * <li> <B>LITERAL_SUFFIX</B> String => suffix used to quote a literal (may
5306: * be null) </li>
5307: * <li> <B>CREATE_PARAMS</B> String => parameters used in creating the type
5308: * (may be null) </li>
5309: * <li> <B>NULLABLE</B> short => can you use NULL for this type?
5310: * <UL>
5311: * <li> typeNoNulls - does not allow NULL values </li>
5312: * <li> typeNullable - allows NULL values </li>
5313: * <li> typeNullableUnknown - nullability unknown </li>
5314: * </ul>
5315: * </li>
5316: * <li> <B>CASE_SENSITIVE</B> boolean=> is it case sensitive? </li>
5317: * <li> <B>SEARCHABLE</B> short => can you use "WHERE" based on this type:
5318: * <UL>
5319: * <li> typePredNone - No support </li>
5320: * <li> typePredChar - Only supported with WHERE .. LIKE </li>
5321: * <li> typePredBasic - Supported except for WHERE .. LIKE </li>
5322: * <li> typeSearchable - Supported for all WHERE .. </li>
5323: * </ul>
5324: * </li>
5325: * <li> <B>UNSIGNED_ATTRIBUTE</B> boolean => is it unsigned? </li>
5326: * <li> <B>FIXED_PREC_SCALE</B> boolean => can it be a money value? </li>
5327: * <li> <B>AUTO_INCREMENT</B> boolean => can it be used for an
5328: * auto-increment value? </li>
5329: * <li> <B>LOCAL_TYPE_NAME</B> String => localized version of type name
5330: * (may be null) </li>
5331: * <li> <B>MINIMUM_SCALE</B> short => minimum scale supported </li>
5332: * <li> <B>MAXIMUM_SCALE</B> short => maximum scale supported </li>
5333: * <li> <B>SQL_DATA_TYPE</B> int => unused </li>
5334: * <li> <B>SQL_DATETIME_SUB</B> int => unused </li>
5335: * <li> <B>NUM_PREC_RADIX</B> int => usually 2 or 10 </li>
5336: * </ol>
5337: * </p>
5338: *
5339: * @return ResultSet each row is a SQL type description
5340: * @throws SQLException
5341: * DOCUMENT ME!
5342: */
5343: /**
5344: * Get a description of all the standard SQL types supported by this
5345: * database. They are ordered by DATA_TYPE and then by how closely the data
5346: * type maps to the corresponding JDBC SQL type.
5347: * <P>
5348: * Each type description has the following columns:
5349: * <OL>
5350: * <li> <B>TYPE_NAME</B> String => Type name </li>
5351: * <li> <B>DATA_TYPE</B> short => SQL data type from java.sql.Types </li>
5352: * <li> <B>PRECISION</B> int => maximum precision </li>
5353: * <li> <B>LITERAL_PREFIX</B> String => prefix used to quote a literal (may
5354: * be null) </li>
5355: * <li> <B>LITERAL_SUFFIX</B> String => suffix used to quote a literal (may
5356: * be null) </li>
5357: * <li> <B>CREATE_PARAMS</B> String => parameters used in creating the type
5358: * (may be null) </li>
5359: * <li> <B>NULLABLE</B> short => can you use NULL for this type?
5360: * <UL>
5361: * <li> typeNoNulls - does not allow NULL values </li>
5362: * <li> typeNullable - allows NULL values </li>
5363: * <li> typeNullableUnknown - nullability unknown </li>
5364: * </ul>
5365: * </li>
5366: * <li> <B>CASE_SENSITIVE</B> boolean=> is it case sensitive? </li>
5367: * <li> <B>SEARCHABLE</B> short => can you use "WHERE" based on this type:
5368: * <UL>
5369: * <li> typePredNone - No support </li>
5370: * <li> typePredChar - Only supported with WHERE .. LIKE </li>
5371: * <li> typePredBasic - Supported except for WHERE .. LIKE </li>
5372: * <li> typeSearchable - Supported for all WHERE .. </li>
5373: * </ul>
5374: * </li>
5375: * <li> <B>UNSIGNED_ATTRIBUTE</B> boolean => is it unsigned? </li>
5376: * <li> <B>FIXED_PREC_SCALE</B> boolean => can it be a money value? </li>
5377: * <li> <B>AUTO_INCREMENT</B> boolean => can it be used for an
5378: * auto-increment value? </li>
5379: * <li> <B>LOCAL_TYPE_NAME</B> String => localized version of type name
5380: * (may be null) </li>
5381: * <li> <B>MINIMUM_SCALE</B> short => minimum scale supported </li>
5382: * <li> <B>MAXIMUM_SCALE</B> short => maximum scale supported </li>
5383: * <li> <B>SQL_DATA_TYPE</B> int => unused </li>
5384: * <li> <B>SQL_DATETIME_SUB</B> int => unused </li>
5385: * <li> <B>NUM_PREC_RADIX</B> int => usually 2 or 10 </li>
5386: * </ol>
5387: * </p>
5388: *
5389: * @return ResultSet each row is a SQL type description
5390: * @throws SQLException
5391: * DOCUMENT ME!
5392: */
5393: public java.sql.ResultSet getTypeInfo() throws SQLException {
5394: Field[] fields = new Field[18];
5395: fields[0] = new Field("", "TYPE_NAME", Types.CHAR, 32);
5396: fields[1] = new Field("", "DATA_TYPE", Types.SMALLINT, 5);
5397: fields[2] = new Field("", "PRECISION", Types.INTEGER, 10);
5398: fields[3] = new Field("", "LITERAL_PREFIX", Types.CHAR, 4);
5399: fields[4] = new Field("", "LITERAL_SUFFIX", Types.CHAR, 4);
5400: fields[5] = new Field("", "CREATE_PARAMS", Types.CHAR, 32);
5401: fields[6] = new Field("", "NULLABLE", Types.SMALLINT, 5);
5402: fields[7] = new Field("", "CASE_SENSITIVE", Types.CHAR, 3);
5403: fields[8] = new Field("", "SEARCHABLE", Types.SMALLINT, 3);
5404: fields[9] = new Field("", "UNSIGNED_ATTRIBUTE", Types.CHAR, 3);
5405: fields[10] = new Field("", "FIXED_PREC_SCALE", Types.CHAR, 3);
5406: fields[11] = new Field("", "AUTO_INCREMENT", Types.CHAR, 3);
5407: fields[12] = new Field("", "LOCAL_TYPE_NAME", Types.CHAR, 32);
5408: fields[13] = new Field("", "MINIMUM_SCALE", Types.SMALLINT, 5);
5409: fields[14] = new Field("", "MAXIMUM_SCALE", Types.SMALLINT, 5);
5410: fields[15] = new Field("", "SQL_DATA_TYPE", Types.INTEGER, 10);
5411: fields[16] = new Field("", "SQL_DATETIME_SUB", Types.INTEGER,
5412: 10);
5413: fields[17] = new Field("", "NUM_PREC_RADIX", Types.INTEGER, 10);
5414:
5415: byte[][] rowVal = null;
5416: ArrayList tuples = new ArrayList();
5417:
5418: /*
5419: * The following are ordered by java.sql.Types, and then by how closely
5420: * the MySQL type matches the JDBC Type (per spec)
5421: */
5422: /*
5423: * MySQL Type: BIT (silently converted to TINYINT(1)) JDBC Type: BIT
5424: */
5425: rowVal = new byte[18][];
5426: rowVal[0] = s2b("BIT");
5427: rowVal[1] = Integer.toString(java.sql.Types.BIT).getBytes();
5428:
5429: // JDBC Data type
5430: rowVal[2] = s2b("1"); // Precision
5431: rowVal[3] = s2b(""); // Literal Prefix
5432: rowVal[4] = s2b(""); // Literal Suffix
5433: rowVal[5] = s2b(""); // Create Params
5434: rowVal[6] = Integer.toString(
5435: java.sql.DatabaseMetaData.typeNullable).getBytes();
5436:
5437: // Nullable
5438: rowVal[7] = s2b("true"); // Case Sensitive
5439: rowVal[8] = Integer.toString(
5440: java.sql.DatabaseMetaData.typeSearchable).getBytes();
5441:
5442: // Searchable
5443: rowVal[9] = s2b("false"); // Unsignable
5444: rowVal[10] = s2b("false"); // Fixed Prec Scale
5445: rowVal[11] = s2b("false"); // Auto Increment
5446: rowVal[12] = s2b("BIT"); // Locale Type Name
5447: rowVal[13] = s2b("0"); // Minimum Scale
5448: rowVal[14] = s2b("0"); // Maximum Scale
5449: rowVal[15] = s2b("0"); // SQL Data Type (not used)
5450: rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5451: rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5452: tuples.add(new ByteArrayRow(rowVal));
5453:
5454: /*
5455: * MySQL Type: BOOL (silently converted to TINYINT(1)) JDBC Type: BIT
5456: */
5457: rowVal = new byte[18][];
5458: rowVal[0] = s2b("BOOL");
5459: rowVal[1] = Integer.toString(java.sql.Types.BIT).getBytes();
5460:
5461: // JDBC Data type
5462: rowVal[2] = s2b("1"); // Precision
5463: rowVal[3] = s2b(""); // Literal Prefix
5464: rowVal[4] = s2b(""); // Literal Suffix
5465: rowVal[5] = s2b(""); // Create Params
5466: rowVal[6] = Integer.toString(
5467: java.sql.DatabaseMetaData.typeNullable).getBytes();
5468:
5469: // Nullable
5470: rowVal[7] = s2b("true"); // Case Sensitive
5471: rowVal[8] = Integer.toString(
5472: java.sql.DatabaseMetaData.typeSearchable).getBytes();
5473:
5474: // Searchable
5475: rowVal[9] = s2b("false"); // Unsignable
5476: rowVal[10] = s2b("false"); // Fixed Prec Scale
5477: rowVal[11] = s2b("false"); // Auto Increment
5478: rowVal[12] = s2b("BOOL"); // Locale Type Name
5479: rowVal[13] = s2b("0"); // Minimum Scale
5480: rowVal[14] = s2b("0"); // Maximum Scale
5481: rowVal[15] = s2b("0"); // SQL Data Type (not used)
5482: rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5483: rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5484: tuples.add(new ByteArrayRow(rowVal));
5485:
5486: /*
5487: * MySQL Type: TINYINT JDBC Type: TINYINT
5488: */
5489: rowVal = new byte[18][];
5490: rowVal[0] = s2b("TINYINT");
5491: rowVal[1] = Integer.toString(java.sql.Types.TINYINT).getBytes();
5492:
5493: // JDBC Data type
5494: rowVal[2] = s2b("3"); // Precision
5495: rowVal[3] = s2b(""); // Literal Prefix
5496: rowVal[4] = s2b(""); // Literal Suffix
5497: rowVal[5] = s2b("[(M)] [UNSIGNED] [ZEROFILL]"); // Create Params
5498: rowVal[6] = Integer.toString(
5499: java.sql.DatabaseMetaData.typeNullable).getBytes();
5500:
5501: // Nullable
5502: rowVal[7] = s2b("false"); // Case Sensitive
5503: rowVal[8] = Integer.toString(
5504: java.sql.DatabaseMetaData.typeSearchable).getBytes();
5505:
5506: // Searchable
5507: rowVal[9] = s2b("true"); // Unsignable
5508: rowVal[10] = s2b("false"); // Fixed Prec Scale
5509: rowVal[11] = s2b("true"); // Auto Increment
5510: rowVal[12] = s2b("TINYINT"); // Locale Type Name
5511: rowVal[13] = s2b("0"); // Minimum Scale
5512: rowVal[14] = s2b("0"); // Maximum Scale
5513: rowVal[15] = s2b("0"); // SQL Data Type (not used)
5514: rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5515: rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5516: tuples.add(new ByteArrayRow(rowVal));
5517:
5518: /*
5519: * MySQL Type: BIGINT JDBC Type: BIGINT
5520: */
5521: rowVal = new byte[18][];
5522: rowVal[0] = s2b("BIGINT");
5523: rowVal[1] = Integer.toString(java.sql.Types.BIGINT).getBytes();
5524:
5525: // JDBC Data type
5526: rowVal[2] = s2b("19"); // Precision
5527: rowVal[3] = s2b(""); // Literal Prefix
5528: rowVal[4] = s2b(""); // Literal Suffix
5529: rowVal[5] = s2b("[(M)] [UNSIGNED] [ZEROFILL]"); // Create Params
5530: rowVal[6] = Integer.toString(
5531: java.sql.DatabaseMetaData.typeNullable).getBytes();
5532:
5533: // Nullable
5534: rowVal[7] = s2b("false"); // Case Sensitive
5535: rowVal[8] = Integer.toString(
5536: java.sql.DatabaseMetaData.typeSearchable).getBytes();
5537:
5538: // Searchable
5539: rowVal[9] = s2b("true"); // Unsignable
5540: rowVal[10] = s2b("false"); // Fixed Prec Scale
5541: rowVal[11] = s2b("true"); // Auto Increment
5542: rowVal[12] = s2b("BIGINT"); // Locale Type Name
5543: rowVal[13] = s2b("0"); // Minimum Scale
5544: rowVal[14] = s2b("0"); // Maximum Scale
5545: rowVal[15] = s2b("0"); // SQL Data Type (not used)
5546: rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5547: rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5548: tuples.add(new ByteArrayRow(rowVal));
5549:
5550: /*
5551: * MySQL Type: LONG VARBINARY JDBC Type: LONGVARBINARY
5552: */
5553: rowVal = new byte[18][];
5554: rowVal[0] = s2b("LONG VARBINARY");
5555: rowVal[1] = Integer.toString(java.sql.Types.LONGVARBINARY)
5556: .getBytes();
5557:
5558: // JDBC Data type
5559: rowVal[2] = s2b("16777215"); // Precision
5560: rowVal[3] = s2b("'"); // Literal Prefix
5561: rowVal[4] = s2b("'"); // Literal Suffix
5562: rowVal[5] = s2b(""); // Create Params
5563: rowVal[6] = Integer.toString(
5564: java.sql.DatabaseMetaData.typeNullable).getBytes();
5565:
5566: // Nullable
5567: rowVal[7] = s2b("true"); // Case Sensitive
5568: rowVal[8] = Integer.toString(
5569: java.sql.DatabaseMetaData.typeSearchable).getBytes();
5570:
5571: // Searchable
5572: rowVal[9] = s2b("false"); // Unsignable
5573: rowVal[10] = s2b("false"); // Fixed Prec Scale
5574: rowVal[11] = s2b("false"); // Auto Increment
5575: rowVal[12] = s2b("LONG VARBINARY"); // Locale Type Name
5576: rowVal[13] = s2b("0"); // Minimum Scale
5577: rowVal[14] = s2b("0"); // Maximum Scale
5578: rowVal[15] = s2b("0"); // SQL Data Type (not used)
5579: rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5580: rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5581: tuples.add(new ByteArrayRow(rowVal));
5582:
5583: /*
5584: * MySQL Type: MEDIUMBLOB JDBC Type: LONGVARBINARY
5585: */
5586: rowVal = new byte[18][];
5587: rowVal[0] = s2b("MEDIUMBLOB");
5588: rowVal[1] = Integer.toString(java.sql.Types.LONGVARBINARY)
5589: .getBytes();
5590:
5591: // JDBC Data type
5592: rowVal[2] = s2b("16777215"); // Precision
5593: rowVal[3] = s2b("'"); // Literal Prefix
5594: rowVal[4] = s2b("'"); // Literal Suffix
5595: rowVal[5] = s2b(""); // Create Params
5596: rowVal[6] = Integer.toString(
5597: java.sql.DatabaseMetaData.typeNullable).getBytes();
5598:
5599: // Nullable
5600: rowVal[7] = s2b("true"); // Case Sensitive
5601: rowVal[8] = Integer.toString(
5602: java.sql.DatabaseMetaData.typeSearchable).getBytes();
5603:
5604: // Searchable
5605: rowVal[9] = s2b("false"); // Unsignable
5606: rowVal[10] = s2b("false"); // Fixed Prec Scale
5607: rowVal[11] = s2b("false"); // Auto Increment
5608: rowVal[12] = s2b("MEDIUMBLOB"); // Locale Type Name
5609: rowVal[13] = s2b("0"); // Minimum Scale
5610: rowVal[14] = s2b("0"); // Maximum Scale
5611: rowVal[15] = s2b("0"); // SQL Data Type (not used)
5612: rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5613: rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5614: tuples.add(new ByteArrayRow(rowVal));
5615:
5616: /*
5617: * MySQL Type: LONGBLOB JDBC Type: LONGVARBINARY
5618: */
5619: rowVal = new byte[18][];
5620: rowVal[0] = s2b("LONGBLOB");
5621: rowVal[1] = Integer.toString(java.sql.Types.LONGVARBINARY)
5622: .getBytes();
5623:
5624: // JDBC Data type
5625: rowVal[2] = Integer.toString(Integer.MAX_VALUE).getBytes();
5626:
5627: // Precision
5628: rowVal[3] = s2b("'"); // Literal Prefix
5629: rowVal[4] = s2b("'"); // Literal Suffix
5630: rowVal[5] = s2b(""); // Create Params
5631: rowVal[6] = Integer.toString(
5632: java.sql.DatabaseMetaData.typeNullable).getBytes();
5633:
5634: // Nullable
5635: rowVal[7] = s2b("true"); // Case Sensitive
5636: rowVal[8] = Integer.toString(
5637: java.sql.DatabaseMetaData.typeSearchable).getBytes();
5638:
5639: // Searchable
5640: rowVal[9] = s2b("false"); // Unsignable
5641: rowVal[10] = s2b("false"); // Fixed Prec Scale
5642: rowVal[11] = s2b("false"); // Auto Increment
5643: rowVal[12] = s2b("LONGBLOB"); // Locale Type Name
5644: rowVal[13] = s2b("0"); // Minimum Scale
5645: rowVal[14] = s2b("0"); // Maximum Scale
5646: rowVal[15] = s2b("0"); // SQL Data Type (not used)
5647: rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5648: rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5649: tuples.add(new ByteArrayRow(rowVal));
5650:
5651: /*
5652: * MySQL Type: BLOB JDBC Type: LONGVARBINARY
5653: */
5654: rowVal = new byte[18][];
5655: rowVal[0] = s2b("BLOB");
5656: rowVal[1] = Integer.toString(java.sql.Types.LONGVARBINARY)
5657: .getBytes();
5658:
5659: // JDBC Data type
5660: rowVal[2] = s2b("65535"); // Precision
5661: rowVal[3] = s2b("'"); // Literal Prefix
5662: rowVal[4] = s2b("'"); // Literal Suffix
5663: rowVal[5] = s2b(""); // Create Params
5664: rowVal[6] = Integer.toString(
5665: java.sql.DatabaseMetaData.typeNullable).getBytes();
5666:
5667: // Nullable
5668: rowVal[7] = s2b("true"); // Case Sensitive
5669: rowVal[8] = Integer.toString(
5670: java.sql.DatabaseMetaData.typeSearchable).getBytes();
5671:
5672: // Searchable
5673: rowVal[9] = s2b("false"); // Unsignable
5674: rowVal[10] = s2b("false"); // Fixed Prec Scale
5675: rowVal[11] = s2b("false"); // Auto Increment
5676: rowVal[12] = s2b("BLOB"); // Locale Type Name
5677: rowVal[13] = s2b("0"); // Minimum Scale
5678: rowVal[14] = s2b("0"); // Maximum Scale
5679: rowVal[15] = s2b("0"); // SQL Data Type (not used)
5680: rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5681: rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5682: tuples.add(new ByteArrayRow(rowVal));
5683:
5684: /*
5685: * MySQL Type: TINYBLOB JDBC Type: LONGVARBINARY
5686: */
5687: rowVal = new byte[18][];
5688: rowVal[0] = s2b("TINYBLOB");
5689: rowVal[1] = Integer.toString(java.sql.Types.LONGVARBINARY)
5690: .getBytes();
5691:
5692: // JDBC Data type
5693: rowVal[2] = s2b("255"); // Precision
5694: rowVal[3] = s2b("'"); // Literal Prefix
5695: rowVal[4] = s2b("'"); // Literal Suffix
5696: rowVal[5] = s2b(""); // Create Params
5697: rowVal[6] = Integer.toString(
5698: java.sql.DatabaseMetaData.typeNullable).getBytes();
5699:
5700: // Nullable
5701: rowVal[7] = s2b("true"); // Case Sensitive
5702: rowVal[8] = Integer.toString(
5703: java.sql.DatabaseMetaData.typeSearchable).getBytes();
5704:
5705: // Searchable
5706: rowVal[9] = s2b("false"); // Unsignable
5707: rowVal[10] = s2b("false"); // Fixed Prec Scale
5708: rowVal[11] = s2b("false"); // Auto Increment
5709: rowVal[12] = s2b("TINYBLOB"); // Locale Type Name
5710: rowVal[13] = s2b("0"); // Minimum Scale
5711: rowVal[14] = s2b("0"); // Maximum Scale
5712: rowVal[15] = s2b("0"); // SQL Data Type (not used)
5713: rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5714: rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5715: tuples.add(new ByteArrayRow(rowVal));
5716:
5717: /*
5718: * MySQL Type: VARBINARY (sliently converted to VARCHAR(M) BINARY) JDBC
5719: * Type: VARBINARY
5720: */
5721: rowVal = new byte[18][];
5722: rowVal[0] = s2b("VARBINARY");
5723: rowVal[1] = Integer.toString(java.sql.Types.VARBINARY)
5724: .getBytes();
5725:
5726: // JDBC Data type
5727: rowVal[2] = s2b("255"); // Precision
5728: rowVal[3] = s2b("'"); // Literal Prefix
5729: rowVal[4] = s2b("'"); // Literal Suffix
5730: rowVal[5] = s2b("(M)"); // Create Params
5731: rowVal[6] = Integer.toString(
5732: java.sql.DatabaseMetaData.typeNullable).getBytes();
5733:
5734: // Nullable
5735: rowVal[7] = s2b("true"); // Case Sensitive
5736: rowVal[8] = Integer.toString(
5737: java.sql.DatabaseMetaData.typeSearchable).getBytes();
5738:
5739: // Searchable
5740: rowVal[9] = s2b("false"); // Unsignable
5741: rowVal[10] = s2b("false"); // Fixed Prec Scale
5742: rowVal[11] = s2b("false"); // Auto Increment
5743: rowVal[12] = s2b("VARBINARY"); // Locale Type Name
5744: rowVal[13] = s2b("0"); // Minimum Scale
5745: rowVal[14] = s2b("0"); // Maximum Scale
5746: rowVal[15] = s2b("0"); // SQL Data Type (not used)
5747: rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5748: rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5749: tuples.add(new ByteArrayRow(rowVal));
5750:
5751: /*
5752: * MySQL Type: BINARY (silently converted to CHAR(M) BINARY) JDBC Type:
5753: * BINARY
5754: */
5755: rowVal = new byte[18][];
5756: rowVal[0] = s2b("BINARY");
5757: rowVal[1] = Integer.toString(java.sql.Types.BINARY).getBytes();
5758:
5759: // JDBC Data type
5760: rowVal[2] = s2b("255"); // Precision
5761: rowVal[3] = s2b("'"); // Literal Prefix
5762: rowVal[4] = s2b("'"); // Literal Suffix
5763: rowVal[5] = s2b("(M)"); // Create Params
5764: rowVal[6] = Integer.toString(
5765: java.sql.DatabaseMetaData.typeNullable).getBytes();
5766:
5767: // Nullable
5768: rowVal[7] = s2b("true"); // Case Sensitive
5769: rowVal[8] = Integer.toString(
5770: java.sql.DatabaseMetaData.typeSearchable).getBytes();
5771:
5772: // Searchable
5773: rowVal[9] = s2b("false"); // Unsignable
5774: rowVal[10] = s2b("false"); // Fixed Prec Scale
5775: rowVal[11] = s2b("false"); // Auto Increment
5776: rowVal[12] = s2b("BINARY"); // Locale Type Name
5777: rowVal[13] = s2b("0"); // Minimum Scale
5778: rowVal[14] = s2b("0"); // Maximum Scale
5779: rowVal[15] = s2b("0"); // SQL Data Type (not used)
5780: rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5781: rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5782: tuples.add(new ByteArrayRow(rowVal));
5783:
5784: /*
5785: * MySQL Type: LONG VARCHAR JDBC Type: LONGVARCHAR
5786: */
5787: rowVal = new byte[18][];
5788: rowVal[0] = s2b("LONG VARCHAR");
5789: rowVal[1] = Integer.toString(java.sql.Types.LONGVARCHAR)
5790: .getBytes();
5791:
5792: // JDBC Data type
5793: rowVal[2] = s2b("16777215"); // Precision
5794: rowVal[3] = s2b("'"); // Literal Prefix
5795: rowVal[4] = s2b("'"); // Literal Suffix
5796: rowVal[5] = s2b(""); // Create Params
5797: rowVal[6] = Integer.toString(
5798: java.sql.DatabaseMetaData.typeNullable).getBytes();
5799:
5800: // Nullable
5801: rowVal[7] = s2b("false"); // Case Sensitive
5802: rowVal[8] = Integer.toString(
5803: java.sql.DatabaseMetaData.typeSearchable).getBytes();
5804:
5805: // Searchable
5806: rowVal[9] = s2b("false"); // Unsignable
5807: rowVal[10] = s2b("false"); // Fixed Prec Scale
5808: rowVal[11] = s2b("false"); // Auto Increment
5809: rowVal[12] = s2b("LONG VARCHAR"); // Locale Type Name
5810: rowVal[13] = s2b("0"); // Minimum Scale
5811: rowVal[14] = s2b("0"); // Maximum Scale
5812: rowVal[15] = s2b("0"); // SQL Data Type (not used)
5813: rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5814: rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5815: tuples.add(new ByteArrayRow(rowVal));
5816:
5817: /*
5818: * MySQL Type: MEDIUMTEXT JDBC Type: LONGVARCHAR
5819: */
5820: rowVal = new byte[18][];
5821: rowVal[0] = s2b("MEDIUMTEXT");
5822: rowVal[1] = Integer.toString(java.sql.Types.LONGVARCHAR)
5823: .getBytes();
5824:
5825: // JDBC Data type
5826: rowVal[2] = s2b("16777215"); // Precision
5827: rowVal[3] = s2b("'"); // Literal Prefix
5828: rowVal[4] = s2b("'"); // Literal Suffix
5829: rowVal[5] = s2b(""); // Create Params
5830: rowVal[6] = Integer.toString(
5831: java.sql.DatabaseMetaData.typeNullable).getBytes();
5832:
5833: // Nullable
5834: rowVal[7] = s2b("false"); // Case Sensitive
5835: rowVal[8] = Integer.toString(
5836: java.sql.DatabaseMetaData.typeSearchable).getBytes();
5837:
5838: // Searchable
5839: rowVal[9] = s2b("false"); // Unsignable
5840: rowVal[10] = s2b("false"); // Fixed Prec Scale
5841: rowVal[11] = s2b("false"); // Auto Increment
5842: rowVal[12] = s2b("MEDIUMTEXT"); // Locale Type Name
5843: rowVal[13] = s2b("0"); // Minimum Scale
5844: rowVal[14] = s2b("0"); // Maximum Scale
5845: rowVal[15] = s2b("0"); // SQL Data Type (not used)
5846: rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5847: rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5848: tuples.add(new ByteArrayRow(rowVal));
5849:
5850: /*
5851: * MySQL Type: LONGTEXT JDBC Type: LONGVARCHAR
5852: */
5853: rowVal = new byte[18][];
5854: rowVal[0] = s2b("LONGTEXT");
5855: rowVal[1] = Integer.toString(java.sql.Types.LONGVARCHAR)
5856: .getBytes();
5857:
5858: // JDBC Data type
5859: rowVal[2] = Integer.toString(Integer.MAX_VALUE).getBytes();
5860:
5861: // Precision
5862: rowVal[3] = s2b("'"); // Literal Prefix
5863: rowVal[4] = s2b("'"); // Literal Suffix
5864: rowVal[5] = s2b(""); // Create Params
5865: rowVal[6] = Integer.toString(
5866: java.sql.DatabaseMetaData.typeNullable).getBytes();
5867:
5868: // Nullable
5869: rowVal[7] = s2b("false"); // Case Sensitive
5870: rowVal[8] = Integer.toString(
5871: java.sql.DatabaseMetaData.typeSearchable).getBytes();
5872:
5873: // Searchable
5874: rowVal[9] = s2b("false"); // Unsignable
5875: rowVal[10] = s2b("false"); // Fixed Prec Scale
5876: rowVal[11] = s2b("false"); // Auto Increment
5877: rowVal[12] = s2b("LONGTEXT"); // Locale Type Name
5878: rowVal[13] = s2b("0"); // Minimum Scale
5879: rowVal[14] = s2b("0"); // Maximum Scale
5880: rowVal[15] = s2b("0"); // SQL Data Type (not used)
5881: rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5882: rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5883: tuples.add(new ByteArrayRow(rowVal));
5884:
5885: /*
5886: * MySQL Type: TEXT JDBC Type: LONGVARCHAR
5887: */
5888: rowVal = new byte[18][];
5889: rowVal[0] = s2b("TEXT");
5890: rowVal[1] = Integer.toString(java.sql.Types.LONGVARCHAR)
5891: .getBytes();
5892:
5893: // JDBC Data type
5894: rowVal[2] = s2b("65535"); // Precision
5895: rowVal[3] = s2b("'"); // Literal Prefix
5896: rowVal[4] = s2b("'"); // Literal Suffix
5897: rowVal[5] = s2b(""); // Create Params
5898: rowVal[6] = Integer.toString(
5899: java.sql.DatabaseMetaData.typeNullable).getBytes();
5900:
5901: // Nullable
5902: rowVal[7] = s2b("false"); // Case Sensitive
5903: rowVal[8] = Integer.toString(
5904: java.sql.DatabaseMetaData.typeSearchable).getBytes();
5905:
5906: // Searchable
5907: rowVal[9] = s2b("false"); // Unsignable
5908: rowVal[10] = s2b("false"); // Fixed Prec Scale
5909: rowVal[11] = s2b("false"); // Auto Increment
5910: rowVal[12] = s2b("TEXT"); // Locale Type Name
5911: rowVal[13] = s2b("0"); // Minimum Scale
5912: rowVal[14] = s2b("0"); // Maximum Scale
5913: rowVal[15] = s2b("0"); // SQL Data Type (not used)
5914: rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5915: rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5916: tuples.add(new ByteArrayRow(rowVal));
5917:
5918: /*
5919: * MySQL Type: TINYTEXT JDBC Type: LONGVARCHAR
5920: */
5921: rowVal = new byte[18][];
5922: rowVal[0] = s2b("TINYTEXT");
5923: rowVal[1] = Integer.toString(java.sql.Types.LONGVARCHAR)
5924: .getBytes();
5925:
5926: // JDBC Data type
5927: rowVal[2] = s2b("255"); // Precision
5928: rowVal[3] = s2b("'"); // Literal Prefix
5929: rowVal[4] = s2b("'"); // Literal Suffix
5930: rowVal[5] = s2b(""); // Create Params
5931: rowVal[6] = Integer.toString(
5932: java.sql.DatabaseMetaData.typeNullable).getBytes();
5933:
5934: // Nullable
5935: rowVal[7] = s2b("false"); // Case Sensitive
5936: rowVal[8] = Integer.toString(
5937: java.sql.DatabaseMetaData.typeSearchable).getBytes();
5938:
5939: // Searchable
5940: rowVal[9] = s2b("false"); // Unsignable
5941: rowVal[10] = s2b("false"); // Fixed Prec Scale
5942: rowVal[11] = s2b("false"); // Auto Increment
5943: rowVal[12] = s2b("TINYTEXT"); // Locale Type Name
5944: rowVal[13] = s2b("0"); // Minimum Scale
5945: rowVal[14] = s2b("0"); // Maximum Scale
5946: rowVal[15] = s2b("0"); // SQL Data Type (not used)
5947: rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5948: rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5949: tuples.add(new ByteArrayRow(rowVal));
5950:
5951: /*
5952: * MySQL Type: CHAR JDBC Type: CHAR
5953: */
5954: rowVal = new byte[18][];
5955: rowVal[0] = s2b("CHAR");
5956: rowVal[1] = Integer.toString(java.sql.Types.CHAR).getBytes();
5957:
5958: // JDBC Data type
5959: rowVal[2] = s2b("255"); // Precision
5960: rowVal[3] = s2b("'"); // Literal Prefix
5961: rowVal[4] = s2b("'"); // Literal Suffix
5962: rowVal[5] = s2b("(M)"); // Create Params
5963: rowVal[6] = Integer.toString(
5964: java.sql.DatabaseMetaData.typeNullable).getBytes();
5965:
5966: // Nullable
5967: rowVal[7] = s2b("false"); // Case Sensitive
5968: rowVal[8] = Integer.toString(
5969: java.sql.DatabaseMetaData.typeSearchable).getBytes();
5970:
5971: // Searchable
5972: rowVal[9] = s2b("false"); // Unsignable
5973: rowVal[10] = s2b("false"); // Fixed Prec Scale
5974: rowVal[11] = s2b("false"); // Auto Increment
5975: rowVal[12] = s2b("CHAR"); // Locale Type Name
5976: rowVal[13] = s2b("0"); // Minimum Scale
5977: rowVal[14] = s2b("0"); // Maximum Scale
5978: rowVal[15] = s2b("0"); // SQL Data Type (not used)
5979: rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
5980: rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
5981: tuples.add(new ByteArrayRow(rowVal));
5982:
5983: // The maximum number of digits for DECIMAL or NUMERIC is 65 (64 from MySQL 5.0.3 to 5.0.5).
5984:
5985: int decimalPrecision = 254;
5986:
5987: if (this .conn.versionMeetsMinimum(5, 0, 3)) {
5988: if (this .conn.versionMeetsMinimum(5, 0, 6)) {
5989: decimalPrecision = 65;
5990: } else {
5991: decimalPrecision = 64;
5992: }
5993: }
5994:
5995: /*
5996: * MySQL Type: NUMERIC (silently converted to DECIMAL) JDBC Type:
5997: * NUMERIC
5998: */
5999: rowVal = new byte[18][];
6000: rowVal[0] = s2b("NUMERIC");
6001: rowVal[1] = Integer.toString(java.sql.Types.NUMERIC).getBytes();
6002:
6003: // JDBC Data type
6004: rowVal[2] = s2b(String.valueOf(decimalPrecision)); // Precision
6005: rowVal[3] = s2b(""); // Literal Prefix
6006: rowVal[4] = s2b(""); // Literal Suffix
6007: rowVal[5] = s2b("[(M[,D])] [ZEROFILL]"); // Create Params
6008: rowVal[6] = Integer.toString(
6009: java.sql.DatabaseMetaData.typeNullable).getBytes();
6010:
6011: // Nullable
6012: rowVal[7] = s2b("false"); // Case Sensitive
6013: rowVal[8] = Integer.toString(
6014: java.sql.DatabaseMetaData.typeSearchable).getBytes();
6015:
6016: // Searchable
6017: rowVal[9] = s2b("false"); // Unsignable
6018: rowVal[10] = s2b("false"); // Fixed Prec Scale
6019: rowVal[11] = s2b("true"); // Auto Increment
6020: rowVal[12] = s2b("NUMERIC"); // Locale Type Name
6021: rowVal[13] = s2b("-308"); // Minimum Scale
6022: rowVal[14] = s2b("308"); // Maximum Scale
6023: rowVal[15] = s2b("0"); // SQL Data Type (not used)
6024: rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
6025: rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
6026: tuples.add(new ByteArrayRow(rowVal));
6027:
6028: /*
6029: * MySQL Type: DECIMAL JDBC Type: DECIMAL
6030: */
6031: rowVal = new byte[18][];
6032: rowVal[0] = s2b("DECIMAL");
6033: rowVal[1] = Integer.toString(java.sql.Types.DECIMAL).getBytes();
6034:
6035: // JDBC Data type
6036: rowVal[2] = s2b(String.valueOf(decimalPrecision)); // Precision
6037: rowVal[3] = s2b(""); // Literal Prefix
6038: rowVal[4] = s2b(""); // Literal Suffix
6039: rowVal[5] = s2b("[(M[,D])] [ZEROFILL]"); // Create Params
6040: rowVal[6] = Integer.toString(
6041: java.sql.DatabaseMetaData.typeNullable).getBytes();
6042:
6043: // Nullable
6044: rowVal[7] = s2b("false"); // Case Sensitive
6045: rowVal[8] = Integer.toString(
6046: java.sql.DatabaseMetaData.typeSearchable).getBytes();
6047:
6048: // Searchable
6049: rowVal[9] = s2b("false"); // Unsignable
6050: rowVal[10] = s2b("false"); // Fixed Prec Scale
6051: rowVal[11] = s2b("true"); // Auto Increment
6052: rowVal[12] = s2b("DECIMAL"); // Locale Type Name
6053: rowVal[13] = s2b("-308"); // Minimum Scale
6054: rowVal[14] = s2b("308"); // Maximum Scale
6055: rowVal[15] = s2b("0"); // SQL Data Type (not used)
6056: rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
6057: rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
6058: tuples.add(new ByteArrayRow(rowVal));
6059:
6060: /*
6061: * MySQL Type: INTEGER JDBC Type: INTEGER
6062: */
6063: rowVal = new byte[18][];
6064: rowVal[0] = s2b("INTEGER");
6065: rowVal[1] = Integer.toString(java.sql.Types.INTEGER).getBytes();
6066:
6067: // JDBC Data type
6068: rowVal[2] = s2b("10"); // Precision
6069: rowVal[3] = s2b(""); // Literal Prefix
6070: rowVal[4] = s2b(""); // Literal Suffix
6071: rowVal[5] = s2b("[(M)] [UNSIGNED] [ZEROFILL]"); // Create Params
6072: rowVal[6] = Integer.toString(
6073: java.sql.DatabaseMetaData.typeNullable).getBytes();
6074:
6075: // Nullable
6076: rowVal[7] = s2b("false"); // Case Sensitive
6077: rowVal[8] = Integer.toString(
6078: java.sql.DatabaseMetaData.typeSearchable).getBytes();
6079:
6080: // Searchable
6081: rowVal[9] = s2b("true"); // Unsignable
6082: rowVal[10] = s2b("false"); // Fixed Prec Scale
6083: rowVal[11] = s2b("true"); // Auto Increment
6084: rowVal[12] = s2b("INTEGER"); // Locale Type Name
6085: rowVal[13] = s2b("0"); // Minimum Scale
6086: rowVal[14] = s2b("0"); // Maximum Scale
6087: rowVal[15] = s2b("0"); // SQL Data Type (not used)
6088: rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
6089: rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
6090: tuples.add(new ByteArrayRow(rowVal));
6091:
6092: /*
6093: * MySQL Type: INT JDBC Type: INTEGER
6094: */
6095: rowVal = new byte[18][];
6096: rowVal[0] = s2b("INT");
6097: rowVal[1] = Integer.toString(java.sql.Types.INTEGER).getBytes();
6098:
6099: // JDBC Data type
6100: rowVal[2] = s2b("10"); // Precision
6101: rowVal[3] = s2b(""); // Literal Prefix
6102: rowVal[4] = s2b(""); // Literal Suffix
6103: rowVal[5] = s2b("[(M)] [UNSIGNED] [ZEROFILL]"); // Create Params
6104: rowVal[6] = Integer.toString(
6105: java.sql.DatabaseMetaData.typeNullable).getBytes();
6106:
6107: // Nullable
6108: rowVal[7] = s2b("false"); // Case Sensitive
6109: rowVal[8] = Integer.toString(
6110: java.sql.DatabaseMetaData.typeSearchable).getBytes();
6111:
6112: // Searchable
6113: rowVal[9] = s2b("true"); // Unsignable
6114: rowVal[10] = s2b("false"); // Fixed Prec Scale
6115: rowVal[11] = s2b("true"); // Auto Increment
6116: rowVal[12] = s2b("INT"); // Locale Type Name
6117: rowVal[13] = s2b("0"); // Minimum Scale
6118: rowVal[14] = s2b("0"); // Maximum Scale
6119: rowVal[15] = s2b("0"); // SQL Data Type (not used)
6120: rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
6121: rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
6122: tuples.add(new ByteArrayRow(rowVal));
6123:
6124: /*
6125: * MySQL Type: MEDIUMINT JDBC Type: INTEGER
6126: */
6127: rowVal = new byte[18][];
6128: rowVal[0] = s2b("MEDIUMINT");
6129: rowVal[1] = Integer.toString(java.sql.Types.INTEGER).getBytes();
6130:
6131: // JDBC Data type
6132: rowVal[2] = s2b("7"); // Precision
6133: rowVal[3] = s2b(""); // Literal Prefix
6134: rowVal[4] = s2b(""); // Literal Suffix
6135: rowVal[5] = s2b("[(M)] [UNSIGNED] [ZEROFILL]"); // Create Params
6136: rowVal[6] = Integer.toString(
6137: java.sql.DatabaseMetaData.typeNullable).getBytes();
6138:
6139: // Nullable
6140: rowVal[7] = s2b("false"); // Case Sensitive
6141: rowVal[8] = Integer.toString(
6142: java.sql.DatabaseMetaData.typeSearchable).getBytes();
6143:
6144: // Searchable
6145: rowVal[9] = s2b("true"); // Unsignable
6146: rowVal[10] = s2b("false"); // Fixed Prec Scale
6147: rowVal[11] = s2b("true"); // Auto Increment
6148: rowVal[12] = s2b("MEDIUMINT"); // Locale Type Name
6149: rowVal[13] = s2b("0"); // Minimum Scale
6150: rowVal[14] = s2b("0"); // Maximum Scale
6151: rowVal[15] = s2b("0"); // SQL Data Type (not used)
6152: rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
6153: rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
6154: tuples.add(new ByteArrayRow(rowVal));
6155:
6156: /*
6157: * MySQL Type: SMALLINT JDBC Type: SMALLINT
6158: */
6159: rowVal = new byte[18][];
6160: rowVal[0] = s2b("SMALLINT");
6161: rowVal[1] = Integer.toString(java.sql.Types.SMALLINT)
6162: .getBytes();
6163:
6164: // JDBC Data type
6165: rowVal[2] = s2b("5"); // Precision
6166: rowVal[3] = s2b(""); // Literal Prefix
6167: rowVal[4] = s2b(""); // Literal Suffix
6168: rowVal[5] = s2b("[(M)] [UNSIGNED] [ZEROFILL]"); // Create Params
6169: rowVal[6] = Integer.toString(
6170: java.sql.DatabaseMetaData.typeNullable).getBytes();
6171:
6172: // Nullable
6173: rowVal[7] = s2b("false"); // Case Sensitive
6174: rowVal[8] = Integer.toString(
6175: java.sql.DatabaseMetaData.typeSearchable).getBytes();
6176:
6177: // Searchable
6178: rowVal[9] = s2b("true"); // Unsignable
6179: rowVal[10] = s2b("false"); // Fixed Prec Scale
6180: rowVal[11] = s2b("true"); // Auto Increment
6181: rowVal[12] = s2b("SMALLINT"); // Locale Type Name
6182: rowVal[13] = s2b("0"); // Minimum Scale
6183: rowVal[14] = s2b("0"); // Maximum Scale
6184: rowVal[15] = s2b("0"); // SQL Data Type (not used)
6185: rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
6186: rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
6187: tuples.add(new ByteArrayRow(rowVal));
6188:
6189: /*
6190: * MySQL Type: FLOAT JDBC Type: REAL (this is the SINGLE PERCISION
6191: * floating point type)
6192: */
6193: rowVal = new byte[18][];
6194: rowVal[0] = s2b("FLOAT");
6195: rowVal[1] = Integer.toString(java.sql.Types.REAL).getBytes();
6196:
6197: // JDBC Data type
6198: rowVal[2] = s2b("10"); // Precision
6199: rowVal[3] = s2b(""); // Literal Prefix
6200: rowVal[4] = s2b(""); // Literal Suffix
6201: rowVal[5] = s2b("[(M,D)] [ZEROFILL]"); // Create Params
6202: rowVal[6] = Integer.toString(
6203: java.sql.DatabaseMetaData.typeNullable).getBytes();
6204:
6205: // Nullable
6206: rowVal[7] = s2b("false"); // Case Sensitive
6207: rowVal[8] = Integer.toString(
6208: java.sql.DatabaseMetaData.typeSearchable).getBytes();
6209:
6210: // Searchable
6211: rowVal[9] = s2b("false"); // Unsignable
6212: rowVal[10] = s2b("false"); // Fixed Prec Scale
6213: rowVal[11] = s2b("true"); // Auto Increment
6214: rowVal[12] = s2b("FLOAT"); // Locale Type Name
6215: rowVal[13] = s2b("-38"); // Minimum Scale
6216: rowVal[14] = s2b("38"); // Maximum Scale
6217: rowVal[15] = s2b("0"); // SQL Data Type (not used)
6218: rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
6219: rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
6220: tuples.add(new ByteArrayRow(rowVal));
6221:
6222: /*
6223: * MySQL Type: DOUBLE JDBC Type: DOUBLE
6224: */
6225: rowVal = new byte[18][];
6226: rowVal[0] = s2b("DOUBLE");
6227: rowVal[1] = Integer.toString(java.sql.Types.DOUBLE).getBytes();
6228:
6229: // JDBC Data type
6230: rowVal[2] = s2b("17"); // Precision
6231: rowVal[3] = s2b(""); // Literal Prefix
6232: rowVal[4] = s2b(""); // Literal Suffix
6233: rowVal[5] = s2b("[(M,D)] [ZEROFILL]"); // Create Params
6234: rowVal[6] = Integer.toString(
6235: java.sql.DatabaseMetaData.typeNullable).getBytes();
6236:
6237: // Nullable
6238: rowVal[7] = s2b("false"); // Case Sensitive
6239: rowVal[8] = Integer.toString(
6240: java.sql.DatabaseMetaData.typeSearchable).getBytes();
6241:
6242: // Searchable
6243: rowVal[9] = s2b("false"); // Unsignable
6244: rowVal[10] = s2b("false"); // Fixed Prec Scale
6245: rowVal[11] = s2b("true"); // Auto Increment
6246: rowVal[12] = s2b("DOUBLE"); // Locale Type Name
6247: rowVal[13] = s2b("-308"); // Minimum Scale
6248: rowVal[14] = s2b("308"); // Maximum Scale
6249: rowVal[15] = s2b("0"); // SQL Data Type (not used)
6250: rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
6251: rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
6252: tuples.add(new ByteArrayRow(rowVal));
6253:
6254: /*
6255: * MySQL Type: DOUBLE PRECISION JDBC Type: DOUBLE
6256: */
6257: rowVal = new byte[18][];
6258: rowVal[0] = s2b("DOUBLE PRECISION");
6259: rowVal[1] = Integer.toString(java.sql.Types.DOUBLE).getBytes();
6260:
6261: // JDBC Data type
6262: rowVal[2] = s2b("17"); // Precision
6263: rowVal[3] = s2b(""); // Literal Prefix
6264: rowVal[4] = s2b(""); // Literal Suffix
6265: rowVal[5] = s2b("[(M,D)] [ZEROFILL]"); // Create Params
6266: rowVal[6] = Integer.toString(
6267: java.sql.DatabaseMetaData.typeNullable).getBytes();
6268:
6269: // Nullable
6270: rowVal[7] = s2b("false"); // Case Sensitive
6271: rowVal[8] = Integer.toString(
6272: java.sql.DatabaseMetaData.typeSearchable).getBytes();
6273:
6274: // Searchable
6275: rowVal[9] = s2b("false"); // Unsignable
6276: rowVal[10] = s2b("false"); // Fixed Prec Scale
6277: rowVal[11] = s2b("true"); // Auto Increment
6278: rowVal[12] = s2b("DOUBLE PRECISION"); // Locale Type Name
6279: rowVal[13] = s2b("-308"); // Minimum Scale
6280: rowVal[14] = s2b("308"); // Maximum Scale
6281: rowVal[15] = s2b("0"); // SQL Data Type (not used)
6282: rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
6283: rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
6284: tuples.add(new ByteArrayRow(rowVal));
6285:
6286: /*
6287: * MySQL Type: REAL (does not map to Types.REAL) JDBC Type: DOUBLE
6288: */
6289: rowVal = new byte[18][];
6290: rowVal[0] = s2b("REAL");
6291: rowVal[1] = Integer.toString(java.sql.Types.DOUBLE).getBytes();
6292:
6293: // JDBC Data type
6294: rowVal[2] = s2b("17"); // Precision
6295: rowVal[3] = s2b(""); // Literal Prefix
6296: rowVal[4] = s2b(""); // Literal Suffix
6297: rowVal[5] = s2b("[(M,D)] [ZEROFILL]"); // Create Params
6298: rowVal[6] = Integer.toString(
6299: java.sql.DatabaseMetaData.typeNullable).getBytes();
6300:
6301: // Nullable
6302: rowVal[7] = s2b("false"); // Case Sensitive
6303: rowVal[8] = Integer.toString(
6304: java.sql.DatabaseMetaData.typeSearchable).getBytes();
6305:
6306: // Searchable
6307: rowVal[9] = s2b("false"); // Unsignable
6308: rowVal[10] = s2b("false"); // Fixed Prec Scale
6309: rowVal[11] = s2b("true"); // Auto Increment
6310: rowVal[12] = s2b("REAL"); // Locale Type Name
6311: rowVal[13] = s2b("-308"); // Minimum Scale
6312: rowVal[14] = s2b("308"); // Maximum Scale
6313: rowVal[15] = s2b("0"); // SQL Data Type (not used)
6314: rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
6315: rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
6316: tuples.add(new ByteArrayRow(rowVal));
6317:
6318: /*
6319: * MySQL Type: VARCHAR JDBC Type: VARCHAR
6320: */
6321: rowVal = new byte[18][];
6322: rowVal[0] = s2b("VARCHAR");
6323: rowVal[1] = Integer.toString(java.sql.Types.VARCHAR).getBytes();
6324:
6325: // JDBC Data type
6326: rowVal[2] = s2b("255"); // Precision
6327: rowVal[3] = s2b("'"); // Literal Prefix
6328: rowVal[4] = s2b("'"); // Literal Suffix
6329: rowVal[5] = s2b("(M)"); // Create Params
6330: rowVal[6] = Integer.toString(
6331: java.sql.DatabaseMetaData.typeNullable).getBytes();
6332:
6333: // Nullable
6334: rowVal[7] = s2b("false"); // Case Sensitive
6335: rowVal[8] = Integer.toString(
6336: java.sql.DatabaseMetaData.typeSearchable).getBytes();
6337:
6338: // Searchable
6339: rowVal[9] = s2b("false"); // Unsignable
6340: rowVal[10] = s2b("false"); // Fixed Prec Scale
6341: rowVal[11] = s2b("false"); // Auto Increment
6342: rowVal[12] = s2b("VARCHAR"); // Locale Type Name
6343: rowVal[13] = s2b("0"); // Minimum Scale
6344: rowVal[14] = s2b("0"); // Maximum Scale
6345: rowVal[15] = s2b("0"); // SQL Data Type (not used)
6346: rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
6347: rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
6348: tuples.add(new ByteArrayRow(rowVal));
6349:
6350: /*
6351: * MySQL Type: ENUM JDBC Type: VARCHAR
6352: */
6353: rowVal = new byte[18][];
6354: rowVal[0] = s2b("ENUM");
6355: rowVal[1] = Integer.toString(java.sql.Types.VARCHAR).getBytes();
6356:
6357: // JDBC Data type
6358: rowVal[2] = s2b("65535"); // Precision
6359: rowVal[3] = s2b("'"); // Literal Prefix
6360: rowVal[4] = s2b("'"); // Literal Suffix
6361: rowVal[5] = s2b(""); // Create Params
6362: rowVal[6] = Integer.toString(
6363: java.sql.DatabaseMetaData.typeNullable).getBytes();
6364:
6365: // Nullable
6366: rowVal[7] = s2b("false"); // Case Sensitive
6367: rowVal[8] = Integer.toString(
6368: java.sql.DatabaseMetaData.typeSearchable).getBytes();
6369:
6370: // Searchable
6371: rowVal[9] = s2b("false"); // Unsignable
6372: rowVal[10] = s2b("false"); // Fixed Prec Scale
6373: rowVal[11] = s2b("false"); // Auto Increment
6374: rowVal[12] = s2b("ENUM"); // Locale Type Name
6375: rowVal[13] = s2b("0"); // Minimum Scale
6376: rowVal[14] = s2b("0"); // Maximum Scale
6377: rowVal[15] = s2b("0"); // SQL Data Type (not used)
6378: rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
6379: rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
6380: tuples.add(new ByteArrayRow(rowVal));
6381:
6382: /*
6383: * MySQL Type: SET JDBC Type: VARCHAR
6384: */
6385: rowVal = new byte[18][];
6386: rowVal[0] = s2b("SET");
6387: rowVal[1] = Integer.toString(java.sql.Types.VARCHAR).getBytes();
6388:
6389: // JDBC Data type
6390: rowVal[2] = s2b("64"); // Precision
6391: rowVal[3] = s2b("'"); // Literal Prefix
6392: rowVal[4] = s2b("'"); // Literal Suffix
6393: rowVal[5] = s2b(""); // Create Params
6394: rowVal[6] = Integer.toString(
6395: java.sql.DatabaseMetaData.typeNullable).getBytes();
6396:
6397: // Nullable
6398: rowVal[7] = s2b("false"); // Case Sensitive
6399: rowVal[8] = Integer.toString(
6400: java.sql.DatabaseMetaData.typeSearchable).getBytes();
6401:
6402: // Searchable
6403: rowVal[9] = s2b("false"); // Unsignable
6404: rowVal[10] = s2b("false"); // Fixed Prec Scale
6405: rowVal[11] = s2b("false"); // Auto Increment
6406: rowVal[12] = s2b("SET"); // Locale Type Name
6407: rowVal[13] = s2b("0"); // Minimum Scale
6408: rowVal[14] = s2b("0"); // Maximum Scale
6409: rowVal[15] = s2b("0"); // SQL Data Type (not used)
6410: rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
6411: rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
6412: tuples.add(new ByteArrayRow(rowVal));
6413:
6414: /*
6415: * MySQL Type: DATE JDBC Type: DATE
6416: */
6417: rowVal = new byte[18][];
6418: rowVal[0] = s2b("DATE");
6419: rowVal[1] = Integer.toString(java.sql.Types.DATE).getBytes();
6420:
6421: // JDBC Data type
6422: rowVal[2] = s2b("0"); // Precision
6423: rowVal[3] = s2b("'"); // Literal Prefix
6424: rowVal[4] = s2b("'"); // Literal Suffix
6425: rowVal[5] = s2b(""); // Create Params
6426: rowVal[6] = Integer.toString(
6427: java.sql.DatabaseMetaData.typeNullable).getBytes();
6428:
6429: // Nullable
6430: rowVal[7] = s2b("false"); // Case Sensitive
6431: rowVal[8] = Integer.toString(
6432: java.sql.DatabaseMetaData.typeSearchable).getBytes();
6433:
6434: // Searchable
6435: rowVal[9] = s2b("false"); // Unsignable
6436: rowVal[10] = s2b("false"); // Fixed Prec Scale
6437: rowVal[11] = s2b("false"); // Auto Increment
6438: rowVal[12] = s2b("DATE"); // Locale Type Name
6439: rowVal[13] = s2b("0"); // Minimum Scale
6440: rowVal[14] = s2b("0"); // Maximum Scale
6441: rowVal[15] = s2b("0"); // SQL Data Type (not used)
6442: rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
6443: rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
6444: tuples.add(new ByteArrayRow(rowVal));
6445:
6446: /*
6447: * MySQL Type: TIME JDBC Type: TIME
6448: */
6449: rowVal = new byte[18][];
6450: rowVal[0] = s2b("TIME");
6451: rowVal[1] = Integer.toString(java.sql.Types.TIME).getBytes();
6452:
6453: // JDBC Data type
6454: rowVal[2] = s2b("0"); // Precision
6455: rowVal[3] = s2b("'"); // Literal Prefix
6456: rowVal[4] = s2b("'"); // Literal Suffix
6457: rowVal[5] = s2b(""); // Create Params
6458: rowVal[6] = Integer.toString(
6459: java.sql.DatabaseMetaData.typeNullable).getBytes();
6460:
6461: // Nullable
6462: rowVal[7] = s2b("false"); // Case Sensitive
6463: rowVal[8] = Integer.toString(
6464: java.sql.DatabaseMetaData.typeSearchable).getBytes();
6465:
6466: // Searchable
6467: rowVal[9] = s2b("false"); // Unsignable
6468: rowVal[10] = s2b("false"); // Fixed Prec Scale
6469: rowVal[11] = s2b("false"); // Auto Increment
6470: rowVal[12] = s2b("TIME"); // Locale Type Name
6471: rowVal[13] = s2b("0"); // Minimum Scale
6472: rowVal[14] = s2b("0"); // Maximum Scale
6473: rowVal[15] = s2b("0"); // SQL Data Type (not used)
6474: rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
6475: rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
6476: tuples.add(new ByteArrayRow(rowVal));
6477:
6478: /*
6479: * MySQL Type: DATETIME JDBC Type: TIMESTAMP
6480: */
6481: rowVal = new byte[18][];
6482: rowVal[0] = s2b("DATETIME");
6483: rowVal[1] = Integer.toString(java.sql.Types.TIMESTAMP)
6484: .getBytes();
6485:
6486: // JDBC Data type
6487: rowVal[2] = s2b("0"); // Precision
6488: rowVal[3] = s2b("'"); // Literal Prefix
6489: rowVal[4] = s2b("'"); // Literal Suffix
6490: rowVal[5] = s2b(""); // Create Params
6491: rowVal[6] = Integer.toString(
6492: java.sql.DatabaseMetaData.typeNullable).getBytes();
6493:
6494: // Nullable
6495: rowVal[7] = s2b("false"); // Case Sensitive
6496: rowVal[8] = Integer.toString(
6497: java.sql.DatabaseMetaData.typeSearchable).getBytes();
6498:
6499: // Searchable
6500: rowVal[9] = s2b("false"); // Unsignable
6501: rowVal[10] = s2b("false"); // Fixed Prec Scale
6502: rowVal[11] = s2b("false"); // Auto Increment
6503: rowVal[12] = s2b("DATETIME"); // Locale Type Name
6504: rowVal[13] = s2b("0"); // Minimum Scale
6505: rowVal[14] = s2b("0"); // Maximum Scale
6506: rowVal[15] = s2b("0"); // SQL Data Type (not used)
6507: rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
6508: rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
6509: tuples.add(new ByteArrayRow(rowVal));
6510:
6511: /*
6512: * MySQL Type: TIMESTAMP JDBC Type: TIMESTAMP
6513: */
6514: rowVal = new byte[18][];
6515: rowVal[0] = s2b("TIMESTAMP");
6516: rowVal[1] = Integer.toString(java.sql.Types.TIMESTAMP)
6517: .getBytes();
6518:
6519: // JDBC Data type
6520: rowVal[2] = s2b("0"); // Precision
6521: rowVal[3] = s2b("'"); // Literal Prefix
6522: rowVal[4] = s2b("'"); // Literal Suffix
6523: rowVal[5] = s2b("[(M)]"); // Create Params
6524: rowVal[6] = Integer.toString(
6525: java.sql.DatabaseMetaData.typeNullable).getBytes();
6526:
6527: // Nullable
6528: rowVal[7] = s2b("false"); // Case Sensitive
6529: rowVal[8] = Integer.toString(
6530: java.sql.DatabaseMetaData.typeSearchable).getBytes();
6531:
6532: // Searchable
6533: rowVal[9] = s2b("false"); // Unsignable
6534: rowVal[10] = s2b("false"); // Fixed Prec Scale
6535: rowVal[11] = s2b("false"); // Auto Increment
6536: rowVal[12] = s2b("TIMESTAMP"); // Locale Type Name
6537: rowVal[13] = s2b("0"); // Minimum Scale
6538: rowVal[14] = s2b("0"); // Maximum Scale
6539: rowVal[15] = s2b("0"); // SQL Data Type (not used)
6540: rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
6541: rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
6542: tuples.add(new ByteArrayRow(rowVal));
6543:
6544: return buildResultSet(fields, tuples);
6545: }
6546:
6547: /**
6548: * JDBC 2.0 Get a description of the user-defined types defined in a
6549: * particular schema. Schema specific UDTs may have type JAVA_OBJECT,
6550: * STRUCT, or DISTINCT.
6551: * <P>
6552: * Only types matching the catalog, schema, type name and type criteria are
6553: * returned. They are ordered by DATA_TYPE, TYPE_SCHEM and TYPE_NAME. The
6554: * type name parameter may be a fully qualified name. In this case, the
6555: * catalog and schemaPattern parameters are ignored.
6556: * </p>
6557: * <P>
6558: * Each type description has the following columns:
6559: * <OL>
6560: * <li> <B>TYPE_CAT</B> String => the type's catalog (may be null) </li>
6561: * <li> <B>TYPE_SCHEM</B> String => type's schema (may be null) </li>
6562: * <li> <B>TYPE_NAME</B> String => type name </li>
6563: * <li> <B>CLASS_NAME</B> String => Java class name </li>
6564: * <li> <B>DATA_TYPE</B> String => type value defined in java.sql.Types.
6565: * One of JAVA_OBJECT, STRUCT, or DISTINCT </li>
6566: * <li> <B>REMARKS</B> String => explanatory comment on the type </li>
6567: * </ol>
6568: * </p>
6569: * <P>
6570: * <B>Note:</B> If the driver does not support UDTs then an empty result
6571: * set is returned.
6572: * </p>
6573: *
6574: * @param catalog
6575: * a catalog name; "" retrieves those without a catalog; null
6576: * means drop catalog name from the selection criteria
6577: * @param schemaPattern
6578: * a schema name pattern; "" retrieves those without a schema
6579: * @param typeNamePattern
6580: * a type name pattern; may be a fully qualified name
6581: * @param types
6582: * a list of user-named types to include (JAVA_OBJECT, STRUCT, or
6583: * DISTINCT); null returns all types
6584: * @return ResultSet - each row is a type description
6585: * @exception SQLException
6586: * if a database-access error occurs.
6587: */
6588: public java.sql.ResultSet getUDTs(String catalog,
6589: String schemaPattern, String typeNamePattern, int[] types)
6590: throws SQLException {
6591: Field[] fields = new Field[6];
6592: fields[0] = new Field("", "TYPE_CAT", Types.VARCHAR, 32);
6593: fields[1] = new Field("", "TYPE_SCHEM", Types.VARCHAR, 32);
6594: fields[2] = new Field("", "TYPE_NAME", Types.VARCHAR, 32);
6595: fields[3] = new Field("", "CLASS_NAME", Types.VARCHAR, 32);
6596: fields[4] = new Field("", "DATA_TYPE", Types.VARCHAR, 32);
6597: fields[5] = new Field("", "REMARKS", Types.VARCHAR, 32);
6598:
6599: ArrayList tuples = new ArrayList();
6600:
6601: return buildResultSet(fields, tuples);
6602: }
6603:
6604: /**
6605: * What's the url for this database?
6606: *
6607: * @return the url or null if it can't be generated
6608: * @throws SQLException
6609: * DOCUMENT ME!
6610: */
6611: public String getURL() throws SQLException {
6612: return this .conn.getURL();
6613: }
6614:
6615: /**
6616: * What's our user name as known to the database?
6617: *
6618: * @return our database user name
6619: * @throws SQLException
6620: * DOCUMENT ME!
6621: */
6622: public String getUserName() throws SQLException {
6623: if (this .conn.getUseHostsInPrivileges()) {
6624: Statement stmt = null;
6625: ResultSet rs = null;
6626:
6627: try {
6628: stmt = this .conn.createStatement();
6629: stmt.setEscapeProcessing(false);
6630:
6631: rs = stmt.executeQuery("SELECT USER()");
6632: rs.next();
6633:
6634: return rs.getString(1);
6635: } finally {
6636: if (rs != null) {
6637: try {
6638: rs.close();
6639: } catch (Exception ex) {
6640: AssertionFailedException.shouldNotHappen(ex);
6641: }
6642:
6643: rs = null;
6644: }
6645:
6646: if (stmt != null) {
6647: try {
6648: stmt.close();
6649: } catch (Exception ex) {
6650: AssertionFailedException.shouldNotHappen(ex);
6651: }
6652:
6653: stmt = null;
6654: }
6655: }
6656: }
6657:
6658: return this .conn.getUser();
6659: }
6660:
6661: /**
6662: * Get a description of a table's columns that are automatically updated
6663: * when any value in a row is updated. They are unordered.
6664: * <P>
6665: * Each column description has the following columns:
6666: * <OL>
6667: * <li> <B>SCOPE</B> short => is not used </li>
6668: * <li> <B>COLUMN_NAME</B> String => column name </li>
6669: * <li> <B>DATA_TYPE</B> short => SQL data type from java.sql.Types </li>
6670: * <li> <B>TYPE_NAME</B> String => Data source dependent type name </li>
6671: * <li> <B>COLUMN_SIZE</B> int => precision </li>
6672: * <li> <B>BUFFER_LENGTH</B> int => length of column value in bytes </li>
6673: * <li> <B>DECIMAL_DIGITS</B> short => scale </li>
6674: * <li> <B>PSEUDO_COLUMN</B> short => is this a pseudo column like an
6675: * Oracle ROWID
6676: * <UL>
6677: * <li> versionColumnUnknown - may or may not be pseudo column </li>
6678: * <li> versionColumnNotPseudo - is NOT a pseudo column </li>
6679: * <li> versionColumnPseudo - is a pseudo column </li>
6680: * </ul>
6681: * </li>
6682: * </ol>
6683: * </p>
6684: *
6685: * @param catalog
6686: * a catalog name; "" retrieves those without a catalog
6687: * @param schema
6688: * a schema name; "" retrieves those without a schema
6689: * @param table
6690: * a table name
6691: * @return ResultSet each row is a column description
6692: * @throws SQLException
6693: * DOCUMENT ME!
6694: */
6695: public java.sql.ResultSet getVersionColumns(String catalog,
6696: String schema, String table) throws SQLException {
6697: Field[] fields = new Field[8];
6698: fields[0] = new Field("", "SCOPE", Types.SMALLINT, 5);
6699: fields[1] = new Field("", "COLUMN_NAME", Types.CHAR, 32);
6700: fields[2] = new Field("", "DATA_TYPE", Types.SMALLINT, 5);
6701: fields[3] = new Field("", "TYPE_NAME", Types.CHAR, 16);
6702: fields[4] = new Field("", "COLUMN_SIZE", Types.CHAR, 16);
6703: fields[5] = new Field("", "BUFFER_LENGTH", Types.CHAR, 16);
6704: fields[6] = new Field("", "DECIMAL_DIGITS", Types.CHAR, 16);
6705: fields[7] = new Field("", "PSEUDO_COLUMN", Types.SMALLINT, 5);
6706:
6707: return buildResultSet(fields, new ArrayList());
6708:
6709: // do TIMESTAMP columns count?
6710: }
6711:
6712: /**
6713: * JDBC 2.0 Determine whether or not a visible row insert can be detected by
6714: * calling ResultSet.rowInserted().
6715: *
6716: * @param type
6717: * set type, i.e. ResultSet.TYPE_XXX
6718: * @return true if changes are detected by the resultset type
6719: * @exception SQLException
6720: * if a database-access error occurs.
6721: */
6722: public boolean insertsAreDetected(int type) throws SQLException {
6723: return false;
6724: }
6725:
6726: /**
6727: * Does a catalog appear at the start of a qualified table name? (Otherwise
6728: * it appears at the end)
6729: *
6730: * @return true if it appears at the start
6731: * @throws SQLException
6732: * DOCUMENT ME!
6733: */
6734: public boolean isCatalogAtStart() throws SQLException {
6735: return true;
6736: }
6737:
6738: /**
6739: * Is the database in read-only mode?
6740: *
6741: * @return true if so
6742: * @throws SQLException
6743: * DOCUMENT ME!
6744: */
6745: public boolean isReadOnly() throws SQLException {
6746: return false;
6747: }
6748:
6749: /**
6750: * @see DatabaseMetaData#locatorsUpdateCopy()
6751: */
6752: public boolean locatorsUpdateCopy() throws SQLException {
6753: return !this .conn.getEmulateLocators();
6754: }
6755:
6756: /**
6757: * Are concatenations between NULL and non-NULL values NULL? A JDBC
6758: * compliant driver always returns true.
6759: *
6760: * @return true if so
6761: * @throws SQLException
6762: * DOCUMENT ME!
6763: */
6764: public boolean nullPlusNonNullIsNull() throws SQLException {
6765: return true;
6766: }
6767:
6768: /**
6769: * Are NULL values sorted at the end regardless of sort order?
6770: *
6771: * @return true if so
6772: * @throws SQLException
6773: * DOCUMENT ME!
6774: */
6775: public boolean nullsAreSortedAtEnd() throws SQLException {
6776: return false;
6777: }
6778:
6779: /**
6780: * Are NULL values sorted at the start regardless of sort order?
6781: *
6782: * @return true if so
6783: * @throws SQLException
6784: * DOCUMENT ME!
6785: */
6786: public boolean nullsAreSortedAtStart() throws SQLException {
6787: return (this .conn.versionMeetsMinimum(4, 0, 2) && !this .conn
6788: .versionMeetsMinimum(4, 0, 11));
6789: }
6790:
6791: /**
6792: * Are NULL values sorted high?
6793: *
6794: * @return true if so
6795: * @throws SQLException
6796: * DOCUMENT ME!
6797: */
6798: public boolean nullsAreSortedHigh() throws SQLException {
6799: return false;
6800: }
6801:
6802: /**
6803: * Are NULL values sorted low?
6804: *
6805: * @return true if so
6806: * @throws SQLException
6807: * DOCUMENT ME!
6808: */
6809: public boolean nullsAreSortedLow() throws SQLException {
6810: return !nullsAreSortedHigh();
6811: }
6812:
6813: /**
6814: * DOCUMENT ME!
6815: *
6816: * @param type
6817: * DOCUMENT ME!
6818: * @return DOCUMENT ME!
6819: * @throws SQLException
6820: * DOCUMENT ME!
6821: */
6822: public boolean othersDeletesAreVisible(int type)
6823: throws SQLException {
6824: return false;
6825: }
6826:
6827: /**
6828: * DOCUMENT ME!
6829: *
6830: * @param type
6831: * DOCUMENT ME!
6832: * @return DOCUMENT ME!
6833: * @throws SQLException
6834: * DOCUMENT ME!
6835: */
6836: public boolean othersInsertsAreVisible(int type)
6837: throws SQLException {
6838: return false;
6839: }
6840:
6841: /**
6842: * JDBC 2.0 Determine whether changes made by others are visible.
6843: *
6844: * @param type
6845: * set type, i.e. ResultSet.TYPE_XXX
6846: * @return true if changes are visible for the result set type
6847: * @exception SQLException
6848: * if a database-access error occurs.
6849: */
6850: public boolean othersUpdatesAreVisible(int type)
6851: throws SQLException {
6852: return false;
6853: }
6854:
6855: /**
6856: * DOCUMENT ME!
6857: *
6858: * @param type
6859: * DOCUMENT ME!
6860: * @return DOCUMENT ME!
6861: * @throws SQLException
6862: * DOCUMENT ME!
6863: */
6864: public boolean ownDeletesAreVisible(int type) throws SQLException {
6865: return false;
6866: }
6867:
6868: /**
6869: * DOCUMENT ME!
6870: *
6871: * @param type
6872: * DOCUMENT ME!
6873: * @return DOCUMENT ME!
6874: * @throws SQLException
6875: * DOCUMENT ME!
6876: */
6877: public boolean ownInsertsAreVisible(int type) throws SQLException {
6878: return false;
6879: }
6880:
6881: /**
6882: * JDBC 2.0 Determine whether a result set's own changes visible.
6883: *
6884: * @param type
6885: * set type, i.e. ResultSet.TYPE_XXX
6886: * @return true if changes are visible for the result set type
6887: * @exception SQLException
6888: * if a database-access error occurs.
6889: */
6890: public boolean ownUpdatesAreVisible(int type) throws SQLException {
6891: return false;
6892: }
6893:
6894: private LocalAndReferencedColumns parseTableStatusIntoLocalAndReferencedColumns(
6895: String keysComment) throws SQLException {
6896: // keys will equal something like this:
6897: // (parent_service_id child_service_id) REFER
6898: // ds/subservices(parent_service_id child_service_id)
6899: //
6900: // simple-columned keys: (m) REFER
6901: // airline/tt(a)
6902: //
6903: // multi-columned keys : (m n) REFER
6904: // airline/vv(a b)
6905: //
6906: // parse of the string into three phases:
6907: // 1: parse the opening parentheses to determine how many results there
6908: // will be
6909: // 2: read in the schema name/table name
6910: // 3: parse the closing parentheses
6911:
6912: String columnsDelimitter = ","; // what version did this change in?
6913:
6914: char quoteChar = this .quotedId.length() == 0 ? 0
6915: : this .quotedId.charAt(0);
6916:
6917: int indexOfOpenParenLocalColumns = StringUtils
6918: .indexOfIgnoreCaseRespectQuotes(0, keysComment, "(",
6919: quoteChar, true);
6920:
6921: if (indexOfOpenParenLocalColumns == -1) {
6922: throw SQLError
6923: .createSQLException(
6924: "Error parsing foreign keys definition,"
6925: + " couldn't find start of local columns list.",
6926: SQLError.SQL_STATE_GENERAL_ERROR);
6927: }
6928:
6929: String constraintName = removeQuotedId(keysComment.substring(0,
6930: indexOfOpenParenLocalColumns).trim());
6931: keysComment = keysComment.substring(
6932: indexOfOpenParenLocalColumns, keysComment.length());
6933:
6934: String keysCommentTrimmed = keysComment.trim();
6935:
6936: int indexOfCloseParenLocalColumns = StringUtils
6937: .indexOfIgnoreCaseRespectQuotes(0, keysCommentTrimmed,
6938: ")", quoteChar, true);
6939:
6940: if (indexOfCloseParenLocalColumns == -1) {
6941: throw SQLError
6942: .createSQLException(
6943: "Error parsing foreign keys definition,"
6944: + " couldn't find end of local columns list.",
6945: SQLError.SQL_STATE_GENERAL_ERROR);
6946: }
6947:
6948: String localColumnNamesString = keysCommentTrimmed.substring(1,
6949: indexOfCloseParenLocalColumns);
6950:
6951: int indexOfRefer = StringUtils.indexOfIgnoreCaseRespectQuotes(
6952: 0, keysCommentTrimmed, "REFER ", this .quotedId
6953: .charAt(0), true);
6954:
6955: if (indexOfRefer == -1) {
6956: throw SQLError
6957: .createSQLException(
6958: "Error parsing foreign keys definition,"
6959: + " couldn't find start of referenced tables list.",
6960: SQLError.SQL_STATE_GENERAL_ERROR);
6961: }
6962:
6963: int indexOfOpenParenReferCol = StringUtils
6964: .indexOfIgnoreCaseRespectQuotes(indexOfRefer,
6965: keysCommentTrimmed, "(", quoteChar, false);
6966:
6967: if (indexOfOpenParenReferCol == -1) {
6968: throw SQLError
6969: .createSQLException(
6970: "Error parsing foreign keys definition,"
6971: + " couldn't find start of referenced columns list.",
6972: SQLError.SQL_STATE_GENERAL_ERROR);
6973: }
6974:
6975: String referCatalogTableString = keysCommentTrimmed.substring(
6976: indexOfRefer + "REFER ".length(),
6977: indexOfOpenParenReferCol);
6978:
6979: int indexOfSlash = StringUtils.indexOfIgnoreCaseRespectQuotes(
6980: 0, referCatalogTableString, "/", this .quotedId
6981: .charAt(0), false);
6982:
6983: if (indexOfSlash == -1) {
6984: throw SQLError
6985: .createSQLException(
6986: "Error parsing foreign keys definition,"
6987: + " couldn't find name of referenced catalog.",
6988: SQLError.SQL_STATE_GENERAL_ERROR);
6989: }
6990:
6991: String referCatalog = removeQuotedId(referCatalogTableString
6992: .substring(0, indexOfSlash));
6993: String referTable = removeQuotedId(referCatalogTableString
6994: .substring(indexOfSlash + 1).trim());
6995:
6996: int indexOfCloseParenRefer = StringUtils
6997: .indexOfIgnoreCaseRespectQuotes(
6998: indexOfOpenParenReferCol, keysCommentTrimmed,
6999: ")", quoteChar, true);
7000:
7001: if (indexOfCloseParenRefer == -1) {
7002: throw SQLError
7003: .createSQLException(
7004: "Error parsing foreign keys definition,"
7005: + " couldn't find end of referenced columns list.",
7006: SQLError.SQL_STATE_GENERAL_ERROR);
7007: }
7008:
7009: String referColumnNamesString = keysCommentTrimmed.substring(
7010: indexOfOpenParenReferCol + 1, indexOfCloseParenRefer);
7011:
7012: List referColumnsList = StringUtils.split(
7013: referColumnNamesString, columnsDelimitter,
7014: this .quotedId, this .quotedId, false);
7015: List localColumnsList = StringUtils.split(
7016: localColumnNamesString, columnsDelimitter,
7017: this .quotedId, this .quotedId, false);
7018:
7019: return new LocalAndReferencedColumns(localColumnsList,
7020: referColumnsList, constraintName, referCatalog,
7021: referTable);
7022: }
7023:
7024: private String removeQuotedId(String s) {
7025: if (s == null) {
7026: return null;
7027: }
7028:
7029: if (this .quotedId.equals("")) {
7030: return s;
7031: }
7032:
7033: s = s.trim();
7034:
7035: int frontOffset = 0;
7036: int backOffset = s.length();
7037: int quoteLength = this .quotedId.length();
7038:
7039: if (s.startsWith(this .quotedId)) {
7040: frontOffset = quoteLength;
7041: }
7042:
7043: if (s.endsWith(this .quotedId)) {
7044: backOffset -= quoteLength;
7045: }
7046:
7047: return s.substring(frontOffset, backOffset);
7048: }
7049:
7050: /**
7051: * Converts the given string to bytes, using the connection's character
7052: * encoding, or if not available, the JVM default encoding.
7053: *
7054: * @param s
7055: * DOCUMENT ME!
7056: * @return DOCUMENT ME!
7057: */
7058: protected byte[] s2b(String s) throws SQLException {
7059: return StringUtils.s2b(s, this .conn);
7060: }
7061:
7062: /**
7063: * Does the database store mixed case unquoted SQL identifiers in lower
7064: * case?
7065: *
7066: * @return true if so
7067: * @throws SQLException
7068: * DOCUMENT ME!
7069: */
7070: public boolean storesLowerCaseIdentifiers() throws SQLException {
7071: return this .conn.lowerCaseTableNames();
7072: }
7073:
7074: /**
7075: * Does the database store mixed case quoted SQL identifiers in lower case?
7076: * A JDBC compliant driver will always return false.
7077: *
7078: * @return true if so
7079: * @throws SQLException
7080: * DOCUMENT ME!
7081: */
7082: public boolean storesLowerCaseQuotedIdentifiers()
7083: throws SQLException {
7084: return this .conn.lowerCaseTableNames();
7085: }
7086:
7087: /**
7088: * Does the database store mixed case unquoted SQL identifiers in mixed
7089: * case?
7090: *
7091: * @return true if so
7092: * @throws SQLException
7093: * DOCUMENT ME!
7094: */
7095: public boolean storesMixedCaseIdentifiers() throws SQLException {
7096: return !this .conn.lowerCaseTableNames();
7097: }
7098:
7099: /**
7100: * Does the database store mixed case quoted SQL identifiers in mixed case?
7101: * A JDBC compliant driver will always return false.
7102: *
7103: * @return true if so
7104: * @throws SQLException
7105: * DOCUMENT ME!
7106: */
7107: public boolean storesMixedCaseQuotedIdentifiers()
7108: throws SQLException {
7109: return !this .conn.lowerCaseTableNames();
7110: }
7111:
7112: /**
7113: * Does the database store mixed case unquoted SQL identifiers in upper
7114: * case?
7115: *
7116: * @return true if so
7117: * @throws SQLException
7118: * DOCUMENT ME!
7119: */
7120: public boolean storesUpperCaseIdentifiers() throws SQLException {
7121: return false;
7122: }
7123:
7124: /**
7125: * Does the database store mixed case quoted SQL identifiers in upper case?
7126: * A JDBC compliant driver will always return true.
7127: *
7128: * @return true if so
7129: * @throws SQLException
7130: * DOCUMENT ME!
7131: */
7132: public boolean storesUpperCaseQuotedIdentifiers()
7133: throws SQLException {
7134: return true; // not actually true, but required by JDBC spec!?
7135: }
7136:
7137: /**
7138: * Is "ALTER TABLE" with add column supported?
7139: *
7140: * @return true if so
7141: * @throws SQLException
7142: * DOCUMENT ME!
7143: */
7144: public boolean supportsAlterTableWithAddColumn()
7145: throws SQLException {
7146: return true;
7147: }
7148:
7149: /**
7150: * Is "ALTER TABLE" with drop column supported?
7151: *
7152: * @return true if so
7153: * @throws SQLException
7154: * DOCUMENT ME!
7155: */
7156: public boolean supportsAlterTableWithDropColumn()
7157: throws SQLException {
7158: return true;
7159: }
7160:
7161: /**
7162: * Is the ANSI92 entry level SQL grammar supported? All JDBC compliant
7163: * drivers must return true.
7164: *
7165: * @return true if so
7166: * @throws SQLException
7167: * DOCUMENT ME!
7168: */
7169: public boolean supportsANSI92EntryLevelSQL() throws SQLException {
7170: return true;
7171: }
7172:
7173: /**
7174: * Is the ANSI92 full SQL grammar supported?
7175: *
7176: * @return true if so
7177: * @throws SQLException
7178: * DOCUMENT ME!
7179: */
7180: public boolean supportsANSI92FullSQL() throws SQLException {
7181: return false;
7182: }
7183:
7184: /**
7185: * Is the ANSI92 intermediate SQL grammar supported?
7186: *
7187: * @return true if so
7188: * @throws SQLException
7189: * DOCUMENT ME!
7190: */
7191: public boolean supportsANSI92IntermediateSQL() throws SQLException {
7192: return false;
7193: }
7194:
7195: /**
7196: * JDBC 2.0 Return true if the driver supports batch updates, else return
7197: * false.
7198: *
7199: * @return DOCUMENT ME!
7200: * @throws SQLException
7201: * DOCUMENT ME!
7202: */
7203: public boolean supportsBatchUpdates() throws SQLException {
7204: return true;
7205: }
7206:
7207: /**
7208: * Can a catalog name be used in a data manipulation statement?
7209: *
7210: * @return true if so
7211: * @throws SQLException
7212: * DOCUMENT ME!
7213: */
7214: public boolean supportsCatalogsInDataManipulation()
7215: throws SQLException {
7216: // Servers before 3.22 could not do this
7217: return this .conn.versionMeetsMinimum(3, 22, 0);
7218: }
7219:
7220: /**
7221: * Can a catalog name be used in a index definition statement?
7222: *
7223: * @return true if so
7224: * @throws SQLException
7225: * DOCUMENT ME!
7226: */
7227: public boolean supportsCatalogsInIndexDefinitions()
7228: throws SQLException {
7229: // Servers before 3.22 could not do this
7230: return this .conn.versionMeetsMinimum(3, 22, 0);
7231: }
7232:
7233: /**
7234: * Can a catalog name be used in a privilege definition statement?
7235: *
7236: * @return true if so
7237: * @throws SQLException
7238: * DOCUMENT ME!
7239: */
7240: public boolean supportsCatalogsInPrivilegeDefinitions()
7241: throws SQLException {
7242: // Servers before 3.22 could not do this
7243: return this .conn.versionMeetsMinimum(3, 22, 0);
7244: }
7245:
7246: /**
7247: * Can a catalog name be used in a procedure call statement?
7248: *
7249: * @return true if so
7250: * @throws SQLException
7251: * DOCUMENT ME!
7252: */
7253: public boolean supportsCatalogsInProcedureCalls()
7254: throws SQLException {
7255: // Servers before 3.22 could not do this
7256: return this .conn.versionMeetsMinimum(3, 22, 0);
7257: }
7258:
7259: /**
7260: * Can a catalog name be used in a table definition statement?
7261: *
7262: * @return true if so
7263: * @throws SQLException
7264: * DOCUMENT ME!
7265: */
7266: public boolean supportsCatalogsInTableDefinitions()
7267: throws SQLException {
7268: // Servers before 3.22 could not do this
7269: return this .conn.versionMeetsMinimum(3, 22, 0);
7270: }
7271:
7272: /**
7273: * Is column aliasing supported?
7274: * <P>
7275: * If so, the SQL AS clause can be used to provide names for computed
7276: * columns or to provide alias names for columns as required. A JDBC
7277: * compliant driver always returns true.
7278: * </p>
7279: *
7280: * @return true if so
7281: * @throws SQLException
7282: * DOCUMENT ME!
7283: */
7284: public boolean supportsColumnAliasing() throws SQLException {
7285: return true;
7286: }
7287:
7288: /**
7289: * Is the CONVERT function between SQL types supported?
7290: *
7291: * @return true if so
7292: * @throws SQLException
7293: * DOCUMENT ME!
7294: */
7295: public boolean supportsConvert() throws SQLException {
7296: return false;
7297: }
7298:
7299: /**
7300: * Is CONVERT between the given SQL types supported?
7301: *
7302: * @param fromType
7303: * the type to convert from
7304: * @param toType
7305: * the type to convert to
7306: * @return true if so
7307: * @throws SQLException
7308: * if an error occurs
7309: * @see Types
7310: */
7311: public boolean supportsConvert(int fromType, int toType)
7312: throws SQLException {
7313: switch (fromType) {
7314: /*
7315: * The char/binary types can be converted to pretty much anything.
7316: */
7317: case java.sql.Types.CHAR:
7318: case java.sql.Types.VARCHAR:
7319: case java.sql.Types.LONGVARCHAR:
7320: case java.sql.Types.BINARY:
7321: case java.sql.Types.VARBINARY:
7322: case java.sql.Types.LONGVARBINARY:
7323:
7324: switch (toType) {
7325: case java.sql.Types.DECIMAL:
7326: case java.sql.Types.NUMERIC:
7327: case java.sql.Types.REAL:
7328: case java.sql.Types.TINYINT:
7329: case java.sql.Types.SMALLINT:
7330: case java.sql.Types.INTEGER:
7331: case java.sql.Types.BIGINT:
7332: case java.sql.Types.FLOAT:
7333: case java.sql.Types.DOUBLE:
7334: case java.sql.Types.CHAR:
7335: case java.sql.Types.VARCHAR:
7336: case java.sql.Types.LONGVARCHAR:
7337: case java.sql.Types.BINARY:
7338: case java.sql.Types.VARBINARY:
7339: case java.sql.Types.LONGVARBINARY:
7340: case java.sql.Types.OTHER:
7341: case java.sql.Types.DATE:
7342: case java.sql.Types.TIME:
7343: case java.sql.Types.TIMESTAMP:
7344: return true;
7345:
7346: default:
7347: return false;
7348: }
7349:
7350: /*
7351: * We don't handle the BIT type yet.
7352: */
7353: case java.sql.Types.BIT:
7354: return false;
7355:
7356: /*
7357: * The numeric types. Basically they can convert among themselves, and
7358: * with char/binary types.
7359: */
7360: case java.sql.Types.DECIMAL:
7361: case java.sql.Types.NUMERIC:
7362: case java.sql.Types.REAL:
7363: case java.sql.Types.TINYINT:
7364: case java.sql.Types.SMALLINT:
7365: case java.sql.Types.INTEGER:
7366: case java.sql.Types.BIGINT:
7367: case java.sql.Types.FLOAT:
7368: case java.sql.Types.DOUBLE:
7369:
7370: switch (toType) {
7371: case java.sql.Types.DECIMAL:
7372: case java.sql.Types.NUMERIC:
7373: case java.sql.Types.REAL:
7374: case java.sql.Types.TINYINT:
7375: case java.sql.Types.SMALLINT:
7376: case java.sql.Types.INTEGER:
7377: case java.sql.Types.BIGINT:
7378: case java.sql.Types.FLOAT:
7379: case java.sql.Types.DOUBLE:
7380: case java.sql.Types.CHAR:
7381: case java.sql.Types.VARCHAR:
7382: case java.sql.Types.LONGVARCHAR:
7383: case java.sql.Types.BINARY:
7384: case java.sql.Types.VARBINARY:
7385: case java.sql.Types.LONGVARBINARY:
7386: return true;
7387:
7388: default:
7389: return false;
7390: }
7391:
7392: /* MySQL doesn't support a NULL type. */
7393: case java.sql.Types.NULL:
7394: return false;
7395:
7396: /*
7397: * With this driver, this will always be a serialized object, so the
7398: * char/binary types will work.
7399: */
7400: case java.sql.Types.OTHER:
7401:
7402: switch (toType) {
7403: case java.sql.Types.CHAR:
7404: case java.sql.Types.VARCHAR:
7405: case java.sql.Types.LONGVARCHAR:
7406: case java.sql.Types.BINARY:
7407: case java.sql.Types.VARBINARY:
7408: case java.sql.Types.LONGVARBINARY:
7409: return true;
7410:
7411: default:
7412: return false;
7413: }
7414:
7415: /* Dates can be converted to char/binary types. */
7416: case java.sql.Types.DATE:
7417:
7418: switch (toType) {
7419: case java.sql.Types.CHAR:
7420: case java.sql.Types.VARCHAR:
7421: case java.sql.Types.LONGVARCHAR:
7422: case java.sql.Types.BINARY:
7423: case java.sql.Types.VARBINARY:
7424: case java.sql.Types.LONGVARBINARY:
7425: return true;
7426:
7427: default:
7428: return false;
7429: }
7430:
7431: /* Time can be converted to char/binary types */
7432: case java.sql.Types.TIME:
7433:
7434: switch (toType) {
7435: case java.sql.Types.CHAR:
7436: case java.sql.Types.VARCHAR:
7437: case java.sql.Types.LONGVARCHAR:
7438: case java.sql.Types.BINARY:
7439: case java.sql.Types.VARBINARY:
7440: case java.sql.Types.LONGVARBINARY:
7441: return true;
7442:
7443: default:
7444: return false;
7445: }
7446:
7447: /*
7448: * Timestamp can be converted to char/binary types and date/time types
7449: * (with loss of precision).
7450: */
7451: case java.sql.Types.TIMESTAMP:
7452:
7453: switch (toType) {
7454: case java.sql.Types.CHAR:
7455: case java.sql.Types.VARCHAR:
7456: case java.sql.Types.LONGVARCHAR:
7457: case java.sql.Types.BINARY:
7458: case java.sql.Types.VARBINARY:
7459: case java.sql.Types.LONGVARBINARY:
7460: case java.sql.Types.TIME:
7461: case java.sql.Types.DATE:
7462: return true;
7463:
7464: default:
7465: return false;
7466: }
7467:
7468: /* We shouldn't get here! */
7469: default:
7470: return false; // not sure
7471: }
7472: }
7473:
7474: /**
7475: * Is the ODBC Core SQL grammar supported?
7476: *
7477: * @return true if so
7478: * @throws SQLException
7479: * DOCUMENT ME!
7480: */
7481: public boolean supportsCoreSQLGrammar() throws SQLException {
7482: return true;
7483: }
7484:
7485: /**
7486: * Are correlated subqueries supported? A JDBC compliant driver always
7487: * returns true.
7488: *
7489: * @return true if so
7490: * @throws SQLException
7491: * DOCUMENT ME!
7492: */
7493: public boolean supportsCorrelatedSubqueries() throws SQLException {
7494: return this .conn.versionMeetsMinimum(4, 1, 0);
7495: }
7496:
7497: /**
7498: * Are both data definition and data manipulation statements within a
7499: * transaction supported?
7500: *
7501: * @return true if so
7502: * @throws SQLException
7503: * DOCUMENT ME!
7504: */
7505: public boolean supportsDataDefinitionAndDataManipulationTransactions()
7506: throws SQLException {
7507: return false;
7508: }
7509:
7510: /**
7511: * Are only data manipulation statements within a transaction supported?
7512: *
7513: * @return true if so
7514: * @throws SQLException
7515: * DOCUMENT ME!
7516: */
7517: public boolean supportsDataManipulationTransactionsOnly()
7518: throws SQLException {
7519: return false;
7520: }
7521:
7522: /**
7523: * If table correlation names are supported, are they restricted to be
7524: * different from the names of the tables? A JDBC compliant driver always
7525: * returns true.
7526: *
7527: * @return true if so
7528: * @throws SQLException
7529: * DOCUMENT ME!
7530: */
7531: public boolean supportsDifferentTableCorrelationNames()
7532: throws SQLException {
7533: return true;
7534: }
7535:
7536: /**
7537: * Are expressions in "ORDER BY" lists supported?
7538: *
7539: * @return true if so
7540: * @throws SQLException
7541: * DOCUMENT ME!
7542: */
7543: public boolean supportsExpressionsInOrderBy() throws SQLException {
7544: return true;
7545: }
7546:
7547: /**
7548: * Is the ODBC Extended SQL grammar supported?
7549: *
7550: * @return true if so
7551: * @throws SQLException
7552: * DOCUMENT ME!
7553: */
7554: public boolean supportsExtendedSQLGrammar() throws SQLException {
7555: return false;
7556: }
7557:
7558: /**
7559: * Are full nested outer joins supported?
7560: *
7561: * @return true if so
7562: * @throws SQLException
7563: * DOCUMENT ME!
7564: */
7565: public boolean supportsFullOuterJoins() throws SQLException {
7566: return false;
7567: }
7568:
7569: /**
7570: * JDBC 3.0
7571: *
7572: * @return DOCUMENT ME!
7573: */
7574: public boolean supportsGetGeneratedKeys() {
7575: return true;
7576: }
7577:
7578: /**
7579: * Is some form of "GROUP BY" clause supported?
7580: *
7581: * @return true if so
7582: * @throws SQLException
7583: * DOCUMENT ME!
7584: */
7585: public boolean supportsGroupBy() throws SQLException {
7586: return true;
7587: }
7588:
7589: /**
7590: * Can a "GROUP BY" clause add columns not in the SELECT provided it
7591: * specifies all the columns in the SELECT?
7592: *
7593: * @return true if so
7594: * @throws SQLException
7595: * DOCUMENT ME!
7596: */
7597: public boolean supportsGroupByBeyondSelect() throws SQLException {
7598: return true;
7599: }
7600:
7601: /**
7602: * Can a "GROUP BY" clause use columns not in the SELECT?
7603: *
7604: * @return true if so
7605: * @throws SQLException
7606: * DOCUMENT ME!
7607: */
7608: public boolean supportsGroupByUnrelated() throws SQLException {
7609: return true;
7610: }
7611:
7612: /**
7613: * Is the SQL Integrity Enhancement Facility supported?
7614: *
7615: * @return true if so
7616: * @throws SQLException
7617: * DOCUMENT ME!
7618: */
7619: public boolean supportsIntegrityEnhancementFacility()
7620: throws SQLException {
7621: if (!this .conn
7622: .getOverrideSupportsIntegrityEnhancementFacility()) {
7623: return false;
7624: }
7625:
7626: return true;
7627: }
7628:
7629: /**
7630: * Is the escape character in "LIKE" clauses supported? A JDBC compliant
7631: * driver always returns true.
7632: *
7633: * @return true if so
7634: * @throws SQLException
7635: * DOCUMENT ME!
7636: */
7637: public boolean supportsLikeEscapeClause() throws SQLException {
7638: return true;
7639: }
7640:
7641: /**
7642: * Is there limited support for outer joins? (This will be true if
7643: * supportFullOuterJoins is true.)
7644: *
7645: * @return true if so
7646: * @throws SQLException
7647: * DOCUMENT ME!
7648: */
7649: public boolean supportsLimitedOuterJoins() throws SQLException {
7650: return true;
7651: }
7652:
7653: /**
7654: * Is the ODBC Minimum SQL grammar supported? All JDBC compliant drivers
7655: * must return true.
7656: *
7657: * @return true if so
7658: * @throws SQLException
7659: * DOCUMENT ME!
7660: */
7661: public boolean supportsMinimumSQLGrammar() throws SQLException {
7662: return true;
7663: }
7664:
7665: /**
7666: * Does the database support mixed case unquoted SQL identifiers?
7667: *
7668: * @return true if so
7669: * @throws SQLException
7670: * DOCUMENT ME!
7671: */
7672: public boolean supportsMixedCaseIdentifiers() throws SQLException {
7673: return !this .conn.lowerCaseTableNames();
7674: }
7675:
7676: /**
7677: * Does the database support mixed case quoted SQL identifiers? A JDBC
7678: * compliant driver will always return true.
7679: *
7680: * @return true if so
7681: * @throws SQLException
7682: * DOCUMENT ME!
7683: */
7684: public boolean supportsMixedCaseQuotedIdentifiers()
7685: throws SQLException {
7686: return !this .conn.lowerCaseTableNames();
7687: }
7688:
7689: /**
7690: * @see DatabaseMetaData#supportsMultipleOpenResults()
7691: */
7692: public boolean supportsMultipleOpenResults() throws SQLException {
7693: return true;
7694: }
7695:
7696: /**
7697: * Are multiple ResultSets from a single execute supported?
7698: *
7699: * @return true if so
7700: * @throws SQLException
7701: * DOCUMENT ME!
7702: */
7703: public boolean supportsMultipleResultSets() throws SQLException {
7704: return false;
7705: }
7706:
7707: /**
7708: * Can we have multiple transactions open at once (on different
7709: * connections)?
7710: *
7711: * @return true if so
7712: * @throws SQLException
7713: * DOCUMENT ME!
7714: */
7715: public boolean supportsMultipleTransactions() throws SQLException {
7716: return true;
7717: }
7718:
7719: /**
7720: * @see DatabaseMetaData#supportsNamedParameters()
7721: */
7722: public boolean supportsNamedParameters() throws SQLException {
7723: return false;
7724: }
7725:
7726: /**
7727: * Can columns be defined as non-nullable? A JDBC compliant driver always
7728: * returns true.
7729: *
7730: * @return true if so
7731: * @throws SQLException
7732: * DOCUMENT ME!
7733: */
7734: public boolean supportsNonNullableColumns() throws SQLException {
7735: return true;
7736: }
7737:
7738: /**
7739: * Can cursors remain open across commits?
7740: *
7741: * @return true if so
7742: * @throws SQLException
7743: * if a database access error occurs
7744: * @see Connection#disableAutoClose
7745: */
7746: public boolean supportsOpenCursorsAcrossCommit()
7747: throws SQLException {
7748: return false;
7749: }
7750:
7751: /**
7752: * Can cursors remain open across rollbacks?
7753: *
7754: * @return true if so
7755: * @throws SQLException
7756: * if an error occurs
7757: * @see Connection#disableAutoClose
7758: */
7759: public boolean supportsOpenCursorsAcrossRollback()
7760: throws SQLException {
7761: return false;
7762: }
7763:
7764: /**
7765: * Can statements remain open across commits?
7766: *
7767: * @return true if so
7768: * @throws SQLException
7769: * if an error occurs
7770: * @see Connection#disableAutoClose
7771: */
7772: public boolean supportsOpenStatementsAcrossCommit()
7773: throws SQLException {
7774: return false;
7775: }
7776:
7777: /**
7778: * Can statements remain open across rollbacks?
7779: *
7780: * @return true if so
7781: * @throws SQLException
7782: * if an error occurs
7783: * @see Connection#disableAutoClose
7784: */
7785: public boolean supportsOpenStatementsAcrossRollback()
7786: throws SQLException {
7787: return false;
7788: }
7789:
7790: /**
7791: * Can an "ORDER BY" clause use columns not in the SELECT?
7792: *
7793: * @return true if so
7794: * @throws SQLException
7795: * DOCUMENT ME!
7796: */
7797: public boolean supportsOrderByUnrelated() throws SQLException {
7798: return false;
7799: }
7800:
7801: /**
7802: * Is some form of outer join supported?
7803: *
7804: * @return true if so
7805: * @throws SQLException
7806: * DOCUMENT ME!
7807: */
7808: public boolean supportsOuterJoins() throws SQLException {
7809: return true;
7810: }
7811:
7812: /**
7813: * Is positioned DELETE supported?
7814: *
7815: * @return true if so
7816: * @throws SQLException
7817: * DOCUMENT ME!
7818: */
7819: public boolean supportsPositionedDelete() throws SQLException {
7820: return false;
7821: }
7822:
7823: /**
7824: * Is positioned UPDATE supported?
7825: *
7826: * @return true if so
7827: * @throws SQLException
7828: * DOCUMENT ME!
7829: */
7830: public boolean supportsPositionedUpdate() throws SQLException {
7831: return false;
7832: }
7833:
7834: /**
7835: * JDBC 2.0 Does the database support the concurrency type in combination
7836: * with the given result set type?
7837: *
7838: * @param type
7839: * defined in java.sql.ResultSet
7840: * @param concurrency
7841: * type defined in java.sql.ResultSet
7842: * @return true if so
7843: * @exception SQLException
7844: * if a database-access error occurs.
7845: * @see Connection
7846: */
7847: public boolean supportsResultSetConcurrency(int type,
7848: int concurrency) throws SQLException {
7849: switch (type) {
7850: case ResultSet.TYPE_SCROLL_INSENSITIVE:
7851: if ((concurrency == ResultSet.CONCUR_READ_ONLY)
7852: || (concurrency == ResultSet.CONCUR_UPDATABLE)) {
7853: return true;
7854: } else {
7855: throw SQLError
7856: .createSQLException(
7857: "Illegal arguments to supportsResultSetConcurrency()",
7858: SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
7859: }
7860: case ResultSet.TYPE_FORWARD_ONLY:
7861: if ((concurrency == ResultSet.CONCUR_READ_ONLY)
7862: || (concurrency == ResultSet.CONCUR_UPDATABLE)) {
7863: return true;
7864: } else {
7865: throw SQLError
7866: .createSQLException(
7867: "Illegal arguments to supportsResultSetConcurrency()",
7868: SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
7869: }
7870: case ResultSet.TYPE_SCROLL_SENSITIVE:
7871: return false;
7872: default:
7873: throw SQLError
7874: .createSQLException(
7875: "Illegal arguments to supportsResultSetConcurrency()",
7876: SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
7877: }
7878:
7879: }
7880:
7881: /**
7882: * @see DatabaseMetaData#supportsResultSetHoldability(int)
7883: */
7884: public boolean supportsResultSetHoldability(int holdability)
7885: throws SQLException {
7886: return (holdability == ResultSet.HOLD_CURSORS_OVER_COMMIT);
7887: }
7888:
7889: /**
7890: * JDBC 2.0 Does the database support the given result set type?
7891: *
7892: * @param type
7893: * defined in java.sql.ResultSet
7894: * @return true if so
7895: * @exception SQLException
7896: * if a database-access error occurs.
7897: * @see Connection
7898: */
7899: public boolean supportsResultSetType(int type) throws SQLException {
7900: return (type == ResultSet.TYPE_SCROLL_INSENSITIVE);
7901: }
7902:
7903: /**
7904: * @see DatabaseMetaData#supportsSavepoints()
7905: */
7906: public boolean supportsSavepoints() throws SQLException {
7907:
7908: return (this .conn.versionMeetsMinimum(4, 0, 14) || this .conn
7909: .versionMeetsMinimum(4, 1, 1));
7910: }
7911:
7912: /**
7913: * Can a schema name be used in a data manipulation statement?
7914: *
7915: * @return true if so
7916: * @throws SQLException
7917: * DOCUMENT ME!
7918: */
7919: public boolean supportsSchemasInDataManipulation()
7920: throws SQLException {
7921: return false;
7922: }
7923:
7924: /**
7925: * Can a schema name be used in an index definition statement?
7926: *
7927: * @return true if so
7928: * @throws SQLException
7929: * DOCUMENT ME!
7930: */
7931: public boolean supportsSchemasInIndexDefinitions()
7932: throws SQLException {
7933: return false;
7934: }
7935:
7936: /**
7937: * Can a schema name be used in a privilege definition statement?
7938: *
7939: * @return true if so
7940: * @throws SQLException
7941: * DOCUMENT ME!
7942: */
7943: public boolean supportsSchemasInPrivilegeDefinitions()
7944: throws SQLException {
7945: return false;
7946: }
7947:
7948: /**
7949: * Can a schema name be used in a procedure call statement?
7950: *
7951: * @return true if so
7952: * @throws SQLException
7953: * DOCUMENT ME!
7954: */
7955: public boolean supportsSchemasInProcedureCalls()
7956: throws SQLException {
7957: return false;
7958: }
7959:
7960: /**
7961: * Can a schema name be used in a table definition statement?
7962: *
7963: * @return true if so
7964: * @throws SQLException
7965: * DOCUMENT ME!
7966: */
7967: public boolean supportsSchemasInTableDefinitions()
7968: throws SQLException {
7969: return false;
7970: }
7971:
7972: /**
7973: * Is SELECT for UPDATE supported?
7974: *
7975: * @return true if so
7976: * @throws SQLException
7977: * DOCUMENT ME!
7978: */
7979: public boolean supportsSelectForUpdate() throws SQLException {
7980: return this .conn.versionMeetsMinimum(4, 0, 0);
7981: }
7982:
7983: /**
7984: * @see DatabaseMetaData#supportsStatementPooling()
7985: */
7986: public boolean supportsStatementPooling() throws SQLException {
7987: return false;
7988: }
7989:
7990: /**
7991: * Are stored procedure calls using the stored procedure escape syntax
7992: * supported?
7993: *
7994: * @return true if so
7995: * @throws SQLException
7996: * DOCUMENT ME!
7997: */
7998: public boolean supportsStoredProcedures() throws SQLException {
7999: return this .conn.versionMeetsMinimum(5, 0, 0);
8000: }
8001:
8002: /**
8003: * Are subqueries in comparison expressions supported? A JDBC compliant
8004: * driver always returns true.
8005: *
8006: * @return true if so
8007: * @throws SQLException
8008: * DOCUMENT ME!
8009: */
8010: public boolean supportsSubqueriesInComparisons()
8011: throws SQLException {
8012: return this .conn.versionMeetsMinimum(4, 1, 0);
8013: }
8014:
8015: /**
8016: * Are subqueries in exists expressions supported? A JDBC compliant driver
8017: * always returns true.
8018: *
8019: * @return true if so
8020: * @throws SQLException
8021: * DOCUMENT ME!
8022: */
8023: public boolean supportsSubqueriesInExists() throws SQLException {
8024: return this .conn.versionMeetsMinimum(4, 1, 0);
8025: }
8026:
8027: /**
8028: * Are subqueries in "in" statements supported? A JDBC compliant driver
8029: * always returns true.
8030: *
8031: * @return true if so
8032: * @throws SQLException
8033: * DOCUMENT ME!
8034: */
8035: public boolean supportsSubqueriesInIns() throws SQLException {
8036: return this .conn.versionMeetsMinimum(4, 1, 0);
8037: }
8038:
8039: /**
8040: * Are subqueries in quantified expressions supported? A JDBC compliant
8041: * driver always returns true.
8042: *
8043: * @return true if so
8044: * @throws SQLException
8045: * DOCUMENT ME!
8046: */
8047: public boolean supportsSubqueriesInQuantifieds()
8048: throws SQLException {
8049: return this .conn.versionMeetsMinimum(4, 1, 0);
8050: }
8051:
8052: /**
8053: * Are table correlation names supported? A JDBC compliant driver always
8054: * returns true.
8055: *
8056: * @return true if so
8057: * @throws SQLException
8058: * DOCUMENT ME!
8059: */
8060: public boolean supportsTableCorrelationNames() throws SQLException {
8061: return true;
8062: }
8063:
8064: /**
8065: * Does the database support the given transaction isolation level?
8066: *
8067: * @param level
8068: * the values are defined in java.sql.Connection
8069: * @return true if so
8070: * @throws SQLException
8071: * if a database access error occurs
8072: * @see Connection
8073: */
8074: public boolean supportsTransactionIsolationLevel(int level)
8075: throws SQLException {
8076: if (this .conn.supportsIsolationLevel()) {
8077: switch (level) {
8078: case java.sql.Connection.TRANSACTION_READ_COMMITTED:
8079: case java.sql.Connection.TRANSACTION_READ_UNCOMMITTED:
8080: case java.sql.Connection.TRANSACTION_REPEATABLE_READ:
8081: case java.sql.Connection.TRANSACTION_SERIALIZABLE:
8082: return true;
8083:
8084: default:
8085: return false;
8086: }
8087: }
8088:
8089: return false;
8090: }
8091:
8092: /**
8093: * Are transactions supported? If not, commit is a noop and the isolation
8094: * level is TRANSACTION_NONE.
8095: *
8096: * @return true if transactions are supported
8097: * @throws SQLException
8098: * DOCUMENT ME!
8099: */
8100: public boolean supportsTransactions() throws SQLException {
8101: return this .conn.supportsTransactions();
8102: }
8103:
8104: /**
8105: * Is SQL UNION supported? A JDBC compliant driver always returns true.
8106: *
8107: * @return true if so
8108: * @throws SQLException
8109: * DOCUMENT ME!
8110: */
8111: public boolean supportsUnion() throws SQLException {
8112: return this .conn.versionMeetsMinimum(4, 0, 0);
8113: }
8114:
8115: /**
8116: * Is SQL UNION ALL supported? A JDBC compliant driver always returns true.
8117: *
8118: * @return true if so
8119: * @throws SQLException
8120: * DOCUMENT ME!
8121: */
8122: public boolean supportsUnionAll() throws SQLException {
8123: return this .conn.versionMeetsMinimum(4, 0, 0);
8124: }
8125:
8126: /**
8127: * JDBC 2.0 Determine whether or not a visible row update can be detected by
8128: * calling ResultSet.rowUpdated().
8129: *
8130: * @param type
8131: * set type, i.e. ResultSet.TYPE_XXX
8132: * @return true if changes are detected by the resultset type
8133: * @exception SQLException
8134: * if a database-access error occurs.
8135: */
8136: public boolean updatesAreDetected(int type) throws SQLException {
8137: return false;
8138: }
8139:
8140: /**
8141: * Does the database use a file for each table?
8142: *
8143: * @return true if the database uses a local file for each table
8144: * @throws SQLException
8145: * DOCUMENT ME!
8146: */
8147: public boolean usesLocalFilePerTable() throws SQLException {
8148: return false;
8149: }
8150:
8151: /**
8152: * Does the database store tables in a local file?
8153: *
8154: * @return true if so
8155: * @throws SQLException
8156: * DOCUMENT ME!
8157: */
8158: public boolean usesLocalFiles() throws SQLException {
8159: return false;
8160: }
8161:
8162: //
8163: // JDBC-4.0 functions that aren't reliant on Java6
8164: /**
8165: * Retrieves a description of the given catalog's system or user
8166: * function parameters and return type.
8167: *
8168: * @see java.sql.DatabaseMetaData#getFunctionColumns(String, String, String, String)
8169: * @since 1.6
8170: */
8171: public ResultSet getFunctionColumns(String catalog,
8172: String schemaPattern, String functionNamePattern,
8173: String columnNamePattern) throws SQLException {
8174: Field[] fields = {
8175: new Field("", "FUNCTION_CAT", Types.VARCHAR, 0),
8176: new Field("", "FUNCTION_SCHEM", Types.VARCHAR, 0),
8177: new Field("", "FUNCTION_NAME", Types.VARCHAR, 0),
8178: new Field("", "COLUMN_NAME", Types.VARCHAR, 0),
8179: new Field("", "COLUMN_TYPE", Types.VARCHAR, 0),
8180: new Field("", "DATA_TYPE", Types.SMALLINT, 0),
8181: new Field("", "TYPE_NAME", Types.VARCHAR, 0),
8182: new Field("", "PRECISION", Types.INTEGER, 0),
8183: new Field("", "LENGTH", Types.INTEGER, 0),
8184: new Field("", "SCALE", Types.SMALLINT, 0),
8185: new Field("", "RADIX", Types.SMALLINT, 0),
8186: new Field("", "NULLABLE", Types.SMALLINT, 0),
8187: new Field("", "REMARKS", Types.VARCHAR, 0),
8188: new Field("", "CHAR_OCTET_LENGTH", Types.INTEGER, 0),
8189: new Field("", "ORDINAL_POSITION", Types.INTEGER, 0),
8190: new Field("", "IS_NULLABLE", Types.VARCHAR, 3),
8191: new Field("", "SPECIFIC_NAME", Types.VARCHAR, 0) };
8192:
8193: return getProcedureOrFunctionColumns(fields, catalog,
8194: schemaPattern, functionNamePattern, columnNamePattern,
8195: false, true);
8196: }
8197:
8198: public boolean providesQueryObjectGenerator() throws SQLException {
8199: return false;
8200: }
8201:
8202: public ResultSet getSchemas(String catalog, String schemaPattern)
8203: throws SQLException {
8204: Field[] fields = {
8205: new Field("", "TABLE_SCHEM", Types.VARCHAR, 255),
8206: new Field("", "TABLE_CATALOG", Types.VARCHAR, 255) };
8207:
8208: return buildResultSet(fields, new ArrayList());
8209: }
8210:
8211: public boolean supportsStoredFunctionsUsingCallSyntax()
8212: throws SQLException {
8213: return true;
8214: }
8215: }
|