DatabaseMetaData.java
上传用户:sxlinghang
上传日期:2022-07-20
资源大小:1405k
文件大小:231k
源码类别:

数据库编程

开发平台:

Java

  1. /*
  2.    Copyright (C) 2002 MySQL AB
  3.       This program is free software; you can redistribute it and/or modify
  4.       it under the terms of the GNU General Public License as published by
  5.       the Free Software Foundation; either version 2 of the License, or
  6.       (at your option) any later version.
  7.       This program is distributed in the hope that it will be useful,
  8.       but WITHOUT ANY WARRANTY; without even the implied warranty of
  9.       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  10.       GNU General Public License for more details.
  11.       You should have received a copy of the GNU General Public License
  12.       along with this program; if not, write to the Free Software
  13.       Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  14.  */
  15. package com.mysql.jdbc;
  16. import java.sql.ResultSet;
  17. import java.sql.SQLException;
  18. import java.sql.Statement;
  19. import java.sql.Types;
  20. import java.util.ArrayList;
  21. import java.util.Iterator;
  22. import java.util.List;
  23. import java.util.StringTokenizer;
  24. import java.util.TreeMap;
  25. /**
  26.  * JDBC Interface to Mysql functions
  27.  * 
  28.  * <p>
  29.  * This class provides information about the database as a whole.
  30.  * </p>
  31.  * 
  32.  * <p>
  33.  * Many of the methods here return lists of information in ResultSets. You can
  34.  * use the normal ResultSet methods such as getString and getInt to retrieve
  35.  * the data from these ResultSets.  If a given form of metadata is not
  36.  * available, these methods show throw a java.sql.SQLException.
  37.  * </p>
  38.  * 
  39.  * <p>
  40.  * Some of these methods take arguments that are String patterns.  These
  41.  * methods all have names such as fooPattern.  Within a pattern String "%"
  42.  * means match any substring of 0 or more characters and "_" means match any
  43.  * one character.
  44.  * </p>
  45.  *
  46.  * @author Mark Matthews
  47.  * @version $Id: DatabaseMetaData.java,v 1.27.2.34 2004/02/04 02:47:36 mmatthew Exp $
  48.  */
  49. public class DatabaseMetaData implements java.sql.DatabaseMetaData {
  50.     private static final byte[] TABLE_AS_BYTES = "TABLE".getBytes();
  51.     /** The table type for generic tables that support foreign keys. */
  52.     private static final String SUPPORTS_FK = "SUPPORTS_FK";
  53.     //
  54.     // Column indexes used by all DBMD foreign key
  55.     // ResultSets
  56.     //
  57.     private static final int PKTABLE_CAT = 0;
  58.     private static final int PKTABLE_SCHEM = 1;
  59.     private static final int PKTABLE_NAME = 2;
  60.     private static final int PKCOLUMN_NAME = 3;
  61.     private static final int FKTABLE_CAT = 4;
  62.     private static final int FKTABLE_SCHEM = 5;
  63.     private static final int FKTABLE_NAME = 6;
  64.     private static final int FKCOLUMN_NAME = 7;
  65.     private static final int KEY_SEQ = 8;
  66.     private static final int UPDATE_RULE = 9;
  67.     private static final int DELETE_RULE = 10;
  68.     private static final int FK_NAME = 11;
  69.     private static final int PK_NAME = 12;
  70.     private static final int DEFERRABILITY = 13;
  71.     /** The connection to the database */
  72.     private Connection conn;
  73.     /** The 'current' database name being used */
  74.     private String database = null;
  75.     /** What character to use when quoting identifiers */
  76.     private String quotedId = null;
  77.     /**
  78.      * Creates a new DatabaseMetaData object.
  79.      *
  80.      * @param conn DOCUMENT ME!
  81.      * @param database DOCUMENT ME!
  82.      */
  83.     public DatabaseMetaData(Connection conn, String database) {
  84.         this.conn = conn;
  85.         this.database = database;
  86.         try {
  87.             this.quotedId = this.conn.supportsQuotedIdentifiers()
  88.                 ? getIdentifierQuoteString() : "";
  89.         } catch (SQLException sqlEx) {
  90.             // Forced by API, never thrown from getIdentifierQuoteString() in this
  91.             // implementation.
  92.             AssertionFailedException.shouldNotHappen(sqlEx);
  93.         }
  94.     }
  95.     /**
  96.      * @see DatabaseMetaData#getAttributes(String, String, String, String)
  97.      */
  98.     public java.sql.ResultSet getAttributes(String arg0, String arg1,
  99.         String arg2, String arg3) throws SQLException {
  100.         Field[] fields = new Field[21];
  101.         fields[0] = new Field("", "TYPE_CAT", Types.CHAR, 32);
  102.         fields[1] = new Field("", "TYPE_SCHEM", Types.CHAR, 32);
  103.         fields[2] = new Field("", "TYPE_NAME", Types.CHAR, 32);
  104.         fields[3] = new Field("", "ATTR_NAME", Types.CHAR, 32);
  105.         fields[4] = new Field("", "DATA_TYPE", Types.SMALLINT, 32);
  106.         fields[5] = new Field("", "ATTR_TYPE_NAME", Types.CHAR, 32);
  107.         fields[6] = new Field("", "ATTR_SIZE", Types.INTEGER, 32);
  108.         fields[7] = new Field("", "DECIMAL_DIGITS", Types.INTEGER, 32);
  109.         fields[8] = new Field("", "NUM_PREC_RADIX", Types.INTEGER, 32);
  110.         fields[9] = new Field("", "NULLABLE ", Types.INTEGER, 32);
  111.         fields[10] = new Field("", "REMARKS", Types.CHAR, 32);
  112.         fields[11] = new Field("", "ATTR_DEF", Types.CHAR, 32);
  113.         fields[12] = new Field("", "SQL_DATA_TYPE", Types.INTEGER, 32);
  114.         fields[13] = new Field("", "SQL_DATETIME_SUB", Types.INTEGER, 32);
  115.         fields[14] = new Field("", "CHAR_OCTET_LENGTH", Types.INTEGER, 32);
  116.         fields[15] = new Field("", "ORDINAL_POSITION", Types.INTEGER, 32);
  117.         fields[16] = new Field("", "IS_NULLABLE", Types.CHAR, 32);
  118.         fields[17] = new Field("", "SCOPE_CATALOG", Types.CHAR, 32);
  119.         fields[18] = new Field("", "SCOPE_SCHEMA", Types.CHAR, 32);
  120.         fields[19] = new Field("", "SCOPE_TABLE", Types.CHAR, 32);
  121.         fields[20] = new Field("", "SOURCE_DATA_TYPE", Types.SMALLINT, 32);
  122.         return buildResultSet(fields, new ArrayList());
  123.     }
  124.     /**
  125.      * Get a description of a table's optimal set of columns that uniquely
  126.      * identifies a row. They are ordered by SCOPE.
  127.      * 
  128.      * <P>
  129.      * Each column description has the following columns:
  130.      * 
  131.      * <OL>
  132.      * <li>
  133.      * <B>SCOPE</B> short => actual scope of result
  134.      * 
  135.      * <UL>
  136.      * <li>
  137.      * bestRowTemporary - very temporary, while using row
  138.      * </li>
  139.      * <li>
  140.      * bestRowTransaction - valid for remainder of current transaction
  141.      * </li>
  142.      * <li>
  143.      * bestRowSession - valid for remainder of current session
  144.      * </li>
  145.      * </ul>
  146.      * 
  147.      * </li>
  148.      * <li>
  149.      * <B>COLUMN_NAME</B> String => column name
  150.      * </li>
  151.      * <li>
  152.      * <B>DATA_TYPE</B> short => SQL data type from java.sql.Types
  153.      * </li>
  154.      * <li>
  155.      * <B>TYPE_NAME</B> String => Data source dependent type name
  156.      * </li>
  157.      * <li>
  158.      * <B>COLUMN_SIZE</B> int => precision
  159.      * </li>
  160.      * <li>
  161.      * <B>BUFFER_LENGTH</B> int => not used
  162.      * </li>
  163.      * <li>
  164.      * <B>DECIMAL_DIGITS</B> short  => scale
  165.      * </li>
  166.      * <li>
  167.      * <B>PSEUDO_COLUMN</B> short => is this a pseudo column like an Oracle
  168.      * ROWID
  169.      * 
  170.      * <UL>
  171.      * <li>
  172.      * bestRowUnknown - may or may not be pseudo column
  173.      * </li>
  174.      * <li>
  175.      * bestRowNotPseudo - is NOT a pseudo column
  176.      * </li>
  177.      * <li>
  178.      * bestRowPseudo - is a pseudo column
  179.      * </li>
  180.      * </ul>
  181.      * 
  182.      * </li>
  183.      * </ol>
  184.      * </p>
  185.      *
  186.      * @param catalog a catalog name; "" retrieves those without a catalog
  187.      * @param schema a schema name; "" retrieves those without a schema
  188.      * @param table a table name
  189.      * @param scope the scope of interest; use same values as SCOPE
  190.      * @param nullable include columns that are nullable?
  191.      *
  192.      * @return ResultSet each row is a column description
  193.      *
  194.      * @throws java.sql.SQLException DOCUMENT ME!
  195.      */
  196.     public java.sql.ResultSet getBestRowIdentifier(String catalog,
  197.         String schema, String table, int scope, boolean nullable)
  198.         throws java.sql.SQLException {
  199.         Field[] fields = new Field[8];
  200.         fields[0] = new Field("", "SCOPE", Types.SMALLINT, 5);
  201.         fields[1] = new Field("", "COLUMN_NAME", Types.CHAR, 32);
  202.         fields[2] = new Field("", "DATA_TYPE", Types.SMALLINT, 32);
  203.         fields[3] = new Field("", "TYPE_NAME", Types.CHAR, 32);
  204.         fields[4] = new Field("", "COLUMN_SIZE", Types.INTEGER, 10);
  205.         fields[5] = new Field("", "BUFFER_LENGTH", Types.INTEGER, 10);
  206.         fields[6] = new Field("", "DECIMAL_DIGITS", Types.INTEGER, 10);
  207.         fields[7] = new Field("", "PSEUDO_COLUMN", Types.SMALLINT, 5);
  208.         String databasePart = "";
  209.         if (catalog != null) {
  210.             if (!catalog.equals("")) {
  211.                 databasePart = " FROM " + this.quotedId + catalog
  212.                     + this.quotedId;
  213.             }
  214.         } else {
  215.             databasePart = " FROM " + this.quotedId + this.database
  216.                 + this.quotedId;
  217.         }
  218.         if (table == null) {
  219.             throw new java.sql.SQLException("Table not specified.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
  220.         }
  221.         ResultSet results = null;
  222.         Statement stmt = null;
  223.         try {
  224.             stmt = this.conn.createStatement();
  225. if (stmt.getMaxRows() != 0) {
  226. stmt.setMaxRows(0);
  227. }
  228.             StringBuffer queryBuf = new StringBuffer("SHOW COLUMNS FROM ");
  229.             queryBuf.append(this.quotedId);
  230.             queryBuf.append(table);
  231.             queryBuf.append(this.quotedId);
  232.             queryBuf.append(databasePart);
  233.             results = stmt.executeQuery(queryBuf.toString());
  234.             ArrayList tuples = new ArrayList();
  235.             while (results.next()) {
  236.                 String keyType = results.getString("Key");
  237.                 if (keyType != null) {
  238.                     if (StringUtils.startsWithIgnoreCase(keyType, "PRI")) {
  239.                         byte[][] rowVal = new byte[8][];
  240.                         rowVal[0] = Integer.toString(java.sql.DatabaseMetaData.bestRowSession)
  241.                                            .getBytes();
  242.                         rowVal[1] = results.getBytes("Field");
  243.                         String type = results.getString("Type");
  244.                         int size = MysqlIO.getMaxBuf();
  245.                         int decimals = 0;
  246.                         /*
  247.                          * Parse the Type column from MySQL
  248.                          */
  249.                         if (type.indexOf("enum") != -1) {
  250.                             String temp = type.substring(type.indexOf("("),
  251.                                     type.indexOf(")"));
  252.                             java.util.StringTokenizer tokenizer = new java.util.StringTokenizer(temp,
  253.                                     ",");
  254.                             int maxLength = 0;
  255.                             while (tokenizer.hasMoreTokens()) {
  256.                                 maxLength = Math.max(maxLength,
  257.                                         (tokenizer.nextToken().length() - 2));
  258.                             }
  259.                             size = maxLength;
  260.                             decimals = 0;
  261.                             type = "enum";
  262.                         } else if (type.indexOf("(") != -1) {
  263.                             if (type.indexOf(",") != -1) {
  264.                                 size = Integer.parseInt(type.substring(type
  265.                                             .indexOf("(") + 1, type.indexOf(",")));
  266.                                 decimals = Integer.parseInt(type.substring(type
  267.                                             .indexOf(",") + 1, type.indexOf(")")));
  268.                             } else {
  269.                                 size = Integer.parseInt(type.substring(type
  270.                                             .indexOf("(") + 1, type.indexOf(")")));
  271.                             }
  272.                             type = type.substring(type.indexOf("("));
  273.                         }
  274.                         rowVal[2] = new byte[0]; // FIXME!
  275.                         rowVal[3] = s2b(type);
  276.                         rowVal[4] = Integer.toString(size + decimals).getBytes();
  277.                         rowVal[5] = Integer.toString(size + decimals).getBytes();
  278.                         rowVal[6] = Integer.toString(decimals).getBytes();
  279.                         rowVal[7] = Integer.toString(java.sql.DatabaseMetaData.bestRowNotPseudo)
  280.                                            .getBytes();
  281.                         tuples.add(rowVal);
  282.                     }
  283.                 }
  284.             }
  285.             return buildResultSet(fields, tuples);
  286.         } finally {
  287.             if (results != null) {
  288.                 try {
  289.                     results.close();
  290.                 } catch (Exception ex) {
  291.                     ;
  292.                 }
  293.                 results = null;
  294.             }
  295.             if (stmt != null) {
  296.                 try {
  297.                     stmt.close();
  298.                 } catch (Exception ex) {
  299.                     ;
  300.                 }
  301.                 stmt = null;
  302.             }
  303.         }
  304.     }
  305.     /**
  306.      * Does a catalog appear at the start of a qualified table name? (Otherwise
  307.      * it appears at the end)
  308.      *
  309.      * @return true if it appears at the start
  310.      *
  311.      * @throws java.sql.SQLException DOCUMENT ME!
  312.      */
  313.     public boolean isCatalogAtStart() throws java.sql.SQLException {
  314.         return true;
  315.     }
  316.     /**
  317.      * What's the separator between catalog and table name?
  318.      *
  319.      * @return the separator string
  320.      *
  321.      * @throws java.sql.SQLException DOCUMENT ME!
  322.      */
  323.     public String getCatalogSeparator() throws java.sql.SQLException {
  324.         return ".";
  325.     }
  326.     /**
  327.      * What's the database vendor's preferred term for "catalog"?
  328.      *
  329.      * @return the vendor term
  330.      *
  331.      * @throws java.sql.SQLException DOCUMENT ME!
  332.      */
  333.     public String getCatalogTerm() throws java.sql.SQLException {
  334.         return "database";
  335.     }
  336.     /**
  337.      * Get the catalog names available in this database.  The results are
  338.      * ordered by catalog name.
  339.      * 
  340.      * <P>
  341.      * The catalog column is:
  342.      * 
  343.      * <OL>
  344.      * <li>
  345.      * <B>TABLE_CAT</B> String => catalog name
  346.      * </li>
  347.      * </ol>
  348.      * </p>
  349.      *
  350.      * @return ResultSet each row has a single String column that is a catalog
  351.      *         name
  352.      *
  353.      * @throws java.sql.SQLException DOCUMENT ME!
  354.      */
  355.     public java.sql.ResultSet getCatalogs() throws java.sql.SQLException {
  356.         java.sql.ResultSet results = null;
  357.         java.sql.Statement stmt = null;
  358.         try {
  359.             stmt = this.conn.createStatement();
  360.             
  361. if (stmt.getMaxRows() != 0) {
  362. stmt.setMaxRows(0);
  363. }
  364.             results = stmt.executeQuery("SHOW DATABASES");
  365.             java.sql.ResultSetMetaData resultsMD = results.getMetaData();
  366.             Field[] fields = new Field[1];
  367.             fields[0] = new Field("", "TABLE_CAT", Types.VARCHAR,
  368.                     resultsMD.getColumnDisplaySize(1));
  369.             ArrayList tuples = new ArrayList();
  370.             while (results.next()) {
  371.                 byte[][] rowVal = new byte[1][];
  372.                 rowVal[0] = results.getBytes(1);
  373.                 tuples.add(rowVal);
  374.             }
  375.             return buildResultSet(fields, tuples);
  376.         } finally {
  377.             if (results != null) {
  378.                 try {
  379.                     results.close();
  380.                 } catch (SQLException sqlEx) {
  381.                     AssertionFailedException.shouldNotHappen(sqlEx);
  382.                 }
  383.                 results = null;
  384.             }
  385.             if (stmt != null) {
  386.                 try {
  387.                     stmt.close();
  388.                 } catch (SQLException sqlEx) {
  389.                     AssertionFailedException.shouldNotHappen(sqlEx);
  390.                 }
  391.                 stmt = null;
  392.             }
  393.         }
  394.     }
  395.     /**
  396.      * Get a description of the access rights for a table's columns.
  397.      * 
  398.      * <P>
  399.      * Only privileges matching the column name criteria are returned.  They
  400.      * are ordered by COLUMN_NAME and PRIVILEGE.
  401.      * </p>
  402.      * 
  403.      * <P>
  404.      * Each privilige description has the following columns:
  405.      * 
  406.      * <OL>
  407.      * <li>
  408.      * <B>TABLE_CAT</B> String => table catalog (may be null)
  409.      * </li>
  410.      * <li>
  411.      * <B>TABLE_SCHEM</B> String => table schema (may be null)
  412.      * </li>
  413.      * <li>
  414.      * <B>TABLE_NAME</B> String => table name
  415.      * </li>
  416.      * <li>
  417.      * <B>COLUMN_NAME</B> String => column name
  418.      * </li>
  419.      * <li>
  420.      * <B>GRANTOR</B> => grantor of access (may be null)
  421.      * </li>
  422.      * <li>
  423.      * <B>GRANTEE</B> String => grantee of access
  424.      * </li>
  425.      * <li>
  426.      * <B>PRIVILEGE</B> String => name of access (SELECT, INSERT, UPDATE,
  427.      * REFRENCES, ...)
  428.      * </li>
  429.      * <li>
  430.      * <B>IS_GRANTABLE</B> String => "YES" if grantee is permitted to grant to
  431.      * others; "NO" if not; null if unknown
  432.      * </li>
  433.      * </ol>
  434.      * </p>
  435.      *
  436.      * @param catalog a catalog name; "" retrieves those without a catalog
  437.      * @param schema a schema name; "" retrieves those without a schema
  438.      * @param table a table name
  439.      * @param columnNamePattern a column name pattern
  440.      *
  441.      * @return ResultSet each row is a column privilege description
  442.      *
  443.      * @throws java.sql.SQLException if a database access error occurs
  444.      *
  445.      * @see #getSearchStringEscape
  446.      */
  447.     public java.sql.ResultSet getColumnPrivileges(String catalog,
  448.         String schema, String table, String columnNamePattern)
  449.         throws java.sql.SQLException {
  450.         Field[] fields = new Field[8];
  451.         fields[0] = new Field("", "TABLE_CAT", Types.CHAR, 64);
  452.         fields[1] = new Field("", "TABLE_SCHEM", Types.CHAR, 1);
  453.         fields[2] = new Field("", "TABLE_NAME", Types.CHAR, 64);
  454.         fields[3] = new Field("", "COLUMN_NAME", Types.CHAR, 64);
  455.         fields[4] = new Field("", "GRANTOR", Types.CHAR, 77);
  456.         fields[5] = new Field("", "GRANTEE", Types.CHAR, 77);
  457.         fields[6] = new Field("", "PRIVILEGE", Types.CHAR, 64);
  458.         fields[7] = new Field("", "IS_GRANTABLE", Types.CHAR, 3);
  459.         StringBuffer grantQuery = new StringBuffer(
  460.                 "SELECT c.host, c.db, t.grantor, c.user, "
  461.                 + "c.table_name, c.column_name, c.column_priv "
  462.                 + "from mysql.columns_priv c, mysql.tables_priv t "
  463.                 + "where c.host = t.host and c.db = t.db and "
  464.                 + "c.table_name = t.table_name ");
  465.         if ((catalog != null) && (catalog.length() != 0)) {
  466.             grantQuery.append(" AND c.db='");
  467.             grantQuery.append(catalog);
  468.             grantQuery.append("' ");
  469.             ;
  470.         }
  471.         grantQuery.append(" AND c.table_name ='");
  472.         grantQuery.append(table);
  473.         grantQuery.append("' AND c.column_name like '");
  474.         grantQuery.append(columnNamePattern);
  475.         grantQuery.append("'");
  476.         Statement stmt = null;
  477.         ResultSet results = null;
  478.         ArrayList grantRows = new ArrayList();
  479.         try {
  480.             stmt = this.conn.createStatement();
  481.             
  482. if (stmt.getMaxRows() != 0) {
  483. stmt.setMaxRows(0);
  484. }
  485.             results = stmt.executeQuery(grantQuery.toString());
  486.             while (results.next()) {
  487.                 String host = results.getString(1);
  488.                 String database = results.getString(2);
  489.                 String grantor = results.getString(3);
  490.                 String user = results.getString(4);
  491.                 if ((user == null) || (user.length() == 0)) {
  492.                     user = "%";
  493.                 }
  494.                 StringBuffer fullUser = new StringBuffer(user);
  495.                 if ((host != null) && this.conn.useHostsInPrivileges()) {
  496.                     fullUser.append("@");
  497.                     fullUser.append(host);
  498.                 }
  499.                 String columnName = results.getString(6);
  500.                 String allPrivileges = results.getString(7);
  501.                 if (allPrivileges != null) {
  502.                     allPrivileges = allPrivileges.toUpperCase();
  503.                     StringTokenizer st = new StringTokenizer(allPrivileges, ",");
  504.                     while (st.hasMoreTokens()) {
  505.                         String privilege = st.nextToken().trim();
  506.                         byte[][] tuple = new byte[8][];
  507.                         tuple[0] = s2b(database);
  508.                         tuple[1] = null;
  509.                         tuple[2] = s2b(table);
  510.                         tuple[3] = s2b(columnName);
  511.                         if (grantor != null) {
  512.                             tuple[4] = s2b(grantor);
  513.                         } else {
  514.                             tuple[4] = null;
  515.                         }
  516.                         tuple[5] = s2b(fullUser.toString());
  517.                         tuple[6] = s2b(privilege);
  518.                         tuple[7] = null;
  519.                         grantRows.add(tuple);
  520.                     }
  521.                 }
  522.             }
  523.         } finally {
  524.             if (results != null) {
  525.                 try {
  526.                     results.close();
  527.                 } catch (Exception ex) {
  528.                     ;
  529.                 }
  530.                 results = null;
  531.             }
  532.             if (stmt != null) {
  533.                 try {
  534.                     stmt.close();
  535.                 } catch (Exception ex) {
  536.                     ;
  537.                 }
  538.                 stmt = null;
  539.             }
  540.         }
  541.         return buildResultSet(fields, grantRows);
  542.     }
  543.     /**
  544.      * Get a description of table columns available in a catalog.
  545.      * 
  546.      * <P>
  547.      * Only column descriptions matching the catalog, schema, table and column
  548.      * name criteria are returned.  They are ordered by TABLE_SCHEM,
  549.      * TABLE_NAME and ORDINAL_POSITION.
  550.      * </p>
  551.      * 
  552.      * <P>
  553.      * Each column description has the following columns:
  554.      * 
  555.      * <OL>
  556.      * <li>
  557.      * <B>TABLE_CAT</B> String => table catalog (may be null)
  558.      * </li>
  559.      * <li>
  560.      * <B>TABLE_SCHEM</B> String => table schema (may be null)
  561.      * </li>
  562.      * <li>
  563.      * <B>TABLE_NAME</B> String => table name
  564.      * </li>
  565.      * <li>
  566.      * <B>COLUMN_NAME</B> String => column name
  567.      * </li>
  568.      * <li>
  569.      * <B>DATA_TYPE</B> short => SQL type from java.sql.Types
  570.      * </li>
  571.      * <li>
  572.      * <B>TYPE_NAME</B> String => Data source dependent type name
  573.      * </li>
  574.      * <li>
  575.      * <B>COLUMN_SIZE</B> int => column size.  For char or date types this is
  576.      * the maximum number of characters, for numeric or decimal types this is
  577.      * precision.
  578.      * </li>
  579.      * <li>
  580.      * <B>BUFFER_LENGTH</B> is not used.
  581.      * </li>
  582.      * <li>
  583.      * <B>DECIMAL_DIGITS</B> int => the number of fractional digits
  584.      * </li>
  585.      * <li>
  586.      * <B>NUM_PREC_RADIX</B> int => Radix (typically either 10 or 2)
  587.      * </li>
  588.      * <li>
  589.      * <B>NULLABLE</B> int => is NULL allowed?
  590.      * 
  591.      * <UL>
  592.      * <li>
  593.      * columnNoNulls - might not allow NULL values
  594.      * </li>
  595.      * <li>
  596.      * columnNullable - definitely allows NULL values
  597.      * </li>
  598.      * <li>
  599.      * columnNullableUnknown - nullability unknown
  600.      * </li>
  601.      * </ul>
  602.      * 
  603.      * </li>
  604.      * <li>
  605.      * <B>REMARKS</B> String => comment describing column (may be null)
  606.      * </li>
  607.      * <li>
  608.      * <B>COLUMN_DEF</B> String => default value (may be null)
  609.      * </li>
  610.      * <li>
  611.      * <B>SQL_DATA_TYPE</B> int => unused
  612.      * </li>
  613.      * <li>
  614.      * <B>SQL_DATETIME_SUB</B> int => unused
  615.      * </li>
  616.      * <li>
  617.      * <B>CHAR_OCTET_LENGTH</B> int => for char types the maximum number of
  618.      * bytes in the column
  619.      * </li>
  620.      * <li>
  621.      * <B>ORDINAL_POSITION</B> int => index of column in table (starting at 1)
  622.      * </li>
  623.      * <li>
  624.      * <B>IS_NULLABLE</B> String => "NO" means column definitely does not allow
  625.      * NULL values; "YES" means the column might allow NULL values.  An empty
  626.      * string means nobody knows.
  627.      * </li>
  628.      * </ol>
  629.      * </p>
  630.      *
  631.      * @param catalog a catalog name; "" retrieves those without a catalog
  632.      * @param schemaPattern a schema name pattern; "" retrieves those without a
  633.      *        schema
  634.      * @param tableName a table name pattern
  635.      * @param columnNamePattern a column name pattern
  636.      *
  637.      * @return ResultSet each row is a column description
  638.      *
  639.      * @throws java.sql.SQLException if a database access error occurs
  640.      *
  641.      * @see #getSearchStringEscape
  642.      */
  643.     public java.sql.ResultSet getColumns(String catalog, String schemaPattern,
  644.         String tableName, String columnNamePattern)
  645.         throws java.sql.SQLException {
  646.         String databasePart = "";
  647.         if (columnNamePattern == null) {
  648.             columnNamePattern = "%";
  649.         }
  650.         if (catalog != null) {
  651.             if (!catalog.equals("")) {
  652.                 databasePart = " FROM " + this.quotedId + catalog
  653.                     + this.quotedId;
  654.             }
  655.         } else {
  656.             databasePart = " FROM " + this.quotedId + this.database
  657.                 + this.quotedId;
  658.         }
  659.         ArrayList tableNameList = new ArrayList();
  660.         int tablenameLength = 0;
  661.         if (tableName == null) {
  662.             // Select from all tables
  663.             java.sql.ResultSet tables = null;
  664.             try {
  665.                 tables = getTables(catalog, schemaPattern, "%", new String[0]);
  666.                 while (tables.next()) {
  667.                     String tableNameFromList = tables.getString("TABLE_NAME");
  668.                     tableNameList.add(tableNameFromList);
  669.                     if (tableNameFromList.length() > tablenameLength) {
  670.                         tablenameLength = tableNameFromList.length();
  671.                     }
  672.                 }
  673.             } finally {
  674.                 if (tables != null) {
  675.                     try {
  676.                         tables.close();
  677.                     } catch (Exception sqlEx) {
  678.                         AssertionFailedException.shouldNotHappen(sqlEx);
  679.                     }
  680.                     tables = null;
  681.                 }
  682.             }
  683.         } else {
  684.             java.sql.ResultSet tables = null;
  685.             try {
  686.                 tables = getTables(catalog, schemaPattern, tableName,
  687.                         new String[0]);
  688.                 while (tables.next()) {
  689.                     String tableNameFromList = tables.getString("TABLE_NAME");
  690.                     tableNameList.add(tableNameFromList);
  691.                     if (tableNameFromList.length() > tablenameLength) {
  692.                         tablenameLength = tableNameFromList.length();
  693.                     }
  694.                 }
  695.             } finally {
  696.                 if (tables != null) {
  697.                     try {
  698.                         tables.close();
  699.                     } catch (SQLException sqlEx) {
  700.                         AssertionFailedException.shouldNotHappen(sqlEx);
  701.                     }
  702.                     tables = null;
  703.                 }
  704.             }
  705.         }
  706.         int catalogLength = 0;
  707.         if (catalog != null) {
  708.             catalogLength = catalog.length();
  709.         } else {
  710.             catalog = "";
  711.             catalogLength = 0;
  712.         }
  713.         java.util.Iterator tableNames = tableNameList.iterator();
  714.         Field[] fields = new Field[18];
  715.         fields[0] = new Field("", "TABLE_CAT", Types.CHAR, catalogLength);
  716.         fields[1] = new Field("", "TABLE_SCHEM", Types.CHAR, 0);
  717.         fields[2] = new Field("", "TABLE_NAME", Types.CHAR, tablenameLength);
  718.         fields[3] = new Field("", "COLUMN_NAME", Types.CHAR, 32);
  719.         fields[4] = new Field("", "DATA_TYPE", Types.SMALLINT, 5);
  720.         fields[5] = new Field("", "TYPE_NAME", Types.CHAR, 16);
  721.         fields[6] = new Field("", "COLUMN_SIZE", Types.INTEGER,
  722.                 Integer.toString(Integer.MAX_VALUE).length());
  723.         fields[7] = new Field("", "BUFFER_LENGTH", Types.INTEGER, 10);
  724.         fields[8] = new Field("", "DECIMAL_DIGITS", Types.INTEGER, 10);
  725.         fields[9] = new Field("", "NUM_PREC_RADIX", Types.INTEGER, 10);
  726.         fields[10] = new Field("", "NULLABLE", Types.INTEGER, 10);
  727.         fields[11] = new Field("", "REMARKS", Types.CHAR, 0);
  728.         fields[12] = new Field("", "COLUMN_DEF", Types.CHAR, 0);
  729.         fields[13] = new Field("", "SQL_DATA_TYPE", Types.INTEGER, 10);
  730.         fields[14] = new Field("", "SQL_DATETIME_SUB", Types.INTEGER, 10);
  731.         fields[15] = new Field("", "CHAR_OCTET_LENGTH", Types.INTEGER,
  732.                 Integer.toString(Integer.MAX_VALUE).length());
  733.         fields[16] = new Field("", "ORDINAL_POSITION", Types.INTEGER, 10);
  734.         fields[17] = new Field("", "IS_NULLABLE", Types.CHAR, 3);
  735.         ArrayList tuples = new ArrayList();
  736.         byte[] connectionCatalogAsBytes = null;
  737.         if (catalog == null) {
  738.             connectionCatalogAsBytes = s2b(this.conn.getCatalog());
  739.         } else {
  740.             connectionCatalogAsBytes = s2b(catalog);
  741.         }
  742.         while (tableNames.hasNext()) {
  743.             String tableNamePattern = (String) tableNames.next();
  744.             Statement stmt = null;
  745.             ResultSet results = null;
  746.             try {
  747.                 stmt = this.conn.createStatement();
  748.                 
  749. if (stmt.getMaxRows() != 0) {
  750. stmt.setMaxRows(0);
  751. }
  752.                 StringBuffer queryBuf = new StringBuffer("SHOW COLUMNS FROM ");
  753.                 queryBuf.append(this.quotedId);
  754.                 queryBuf.append(tableNamePattern);
  755.                 queryBuf.append(this.quotedId);
  756.                 queryBuf.append(databasePart);
  757.                 queryBuf.append(" LIKE '");
  758.                 queryBuf.append(columnNamePattern);
  759.                 queryBuf.append("'");
  760.                 results = stmt.executeQuery(queryBuf.toString());
  761.                 int ordPos = 1;
  762.                 while (results.next()) {
  763.                     byte[][] rowVal = new byte[18][];
  764.                     rowVal[0] = connectionCatalogAsBytes; // TABLE_CAT
  765.                     rowVal[1] = null;
  766.                     // TABLE_SCHEM (No schemas in MySQL)
  767.                     rowVal[2] = s2b(tableNamePattern); // TABLE_NAME
  768.                     rowVal[3] = results.getBytes("Field");
  769.                     String typeInfo = results.getString("Type");
  770.                     if (Driver.DEBUG) {
  771.                         System.out.println("Type: " + typeInfo);
  772.                     }
  773.                     String mysqlType = "";
  774.                     if (typeInfo.indexOf("(") != -1) {
  775.                         mysqlType = typeInfo.substring(0, typeInfo.indexOf("("));
  776.                     } else {
  777.                         mysqlType = typeInfo;
  778.                     }
  779.                     if (this.conn.capitalizeDBMDTypes()) {
  780.                         mysqlType = mysqlType.toUpperCase();
  781.                     }
  782.                     /*
  783.                      * Convert to XOPEN (thanks JK)
  784.                      */
  785.                     rowVal[4] = Integer.toString(MysqlDefs.mysqlToJavaType(
  786.                                 mysqlType)).getBytes();
  787.                     // DATA_TYPE (jdbc)
  788.                     rowVal[5] = s2b(mysqlType); // TYPE_NAME (native)
  789.                     // Figure Out the Size
  790.                     if (typeInfo != null) {
  791.                         if (StringUtils.startsWithIgnoreCase(typeInfo, "enum")
  792.                                 || StringUtils.startsWithIgnoreCase(typeInfo,
  793.                                     "set")) {
  794.                             String temp = typeInfo.substring(typeInfo.indexOf(
  795.                                         "("), typeInfo.lastIndexOf(")"));
  796.                             java.util.StringTokenizer tokenizer = new java.util.StringTokenizer(temp,
  797.                                     ",");
  798.                             int maxLength = 0;
  799.                             while (tokenizer.hasMoreTokens()) {
  800.                                 maxLength = Math.max(maxLength,
  801.                                         (tokenizer.nextToken().length() - 2));
  802.                             }
  803.                             rowVal[6] = Integer.toString(maxLength).getBytes();
  804.                             rowVal[8] = new byte[] { (byte) '0' };
  805.                         } else if (typeInfo.indexOf(",") != -1) {
  806.                             // Numeric with decimals
  807.                             String size = typeInfo.substring((typeInfo.indexOf(
  808.                                         "(") + 1), (typeInfo.indexOf(",")));
  809.                             String decimals = typeInfo.substring((typeInfo
  810.                                     .indexOf(",") + 1), (typeInfo.indexOf(")")));
  811.                             rowVal[6] = s2b(size);
  812.                             rowVal[8] = s2b(decimals);
  813.                         } else {
  814.                             String size = "0";
  815.                             /* If the size is specified with the DDL, use that */
  816.                             if (typeInfo.indexOf("(") != -1) {
  817.                                 size = typeInfo.substring((typeInfo.indexOf("(")
  818.                                         + 1), (typeInfo.indexOf(")")));
  819.                             } else if (typeInfo.equalsIgnoreCase("tinyint")) {
  820.                                 size = "1";
  821.                             } else if (typeInfo.equalsIgnoreCase("smallint")) {
  822.                                 size = "6";
  823.                             } else if (typeInfo.equalsIgnoreCase("mediumint")) {
  824.                                 size = "6";
  825.                             } else if (typeInfo.equalsIgnoreCase("int")) {
  826.                                 size = "11";
  827.                             } else if (typeInfo.equalsIgnoreCase("integer")) {
  828.                                 size = "11";
  829.                             } else if (typeInfo.equalsIgnoreCase("bigint")) {
  830.                                 size = "25";
  831.                             } else if (typeInfo.equalsIgnoreCase("int24")) {
  832.                                 size = "25";
  833.                             } else if (typeInfo.equalsIgnoreCase("real")) {
  834.                                 size = "12";
  835.                             } else if (typeInfo.equalsIgnoreCase("float")) {
  836.                                 size = "12";
  837.                             } else if (typeInfo.equalsIgnoreCase("decimal")) {
  838.                                 size = "12";
  839.                             } else if (typeInfo.equalsIgnoreCase("numeric")) {
  840.                                 size = "12";
  841.                             } else if (typeInfo.equalsIgnoreCase("double")) {
  842.                                 size = "22";
  843.                             } else if (typeInfo.equalsIgnoreCase("char")) {
  844.                                 size = "1";
  845.                             } else if (typeInfo.equalsIgnoreCase("varchar")) {
  846.                                 size = "255";
  847.                             } else if (typeInfo.equalsIgnoreCase("date")) {
  848.                                 size = "10";
  849.                             } else if (typeInfo.equalsIgnoreCase("time")) {
  850.                                 size = "8";
  851.                             } else if (typeInfo.equalsIgnoreCase("timestamp")) {
  852.                                 size = "19";
  853.                             } else if (typeInfo.equalsIgnoreCase("datetime")) {
  854.                                 size = "19";
  855.                             } else if (typeInfo.equalsIgnoreCase("tinyblob")) {
  856.                                 size = "255";
  857.                             } else if (typeInfo.equalsIgnoreCase("blob")) {
  858.                                 size = "65535";
  859.                             } else if (typeInfo.equalsIgnoreCase("mediumblob")) {
  860.                                 size = "16277215";
  861.                             } else if (typeInfo.equalsIgnoreCase("longblob")) {
  862.                                 size = Integer.toString(Integer.MAX_VALUE);
  863.                             } else if (typeInfo.equalsIgnoreCase("tinytext")) {
  864.                                 size = "255";
  865.                             } else if (typeInfo.equalsIgnoreCase("text")) {
  866.                                 size = "65535";
  867.                             } else if (typeInfo.equalsIgnoreCase("mediumtext")) {
  868.                                 size = "16277215";
  869.                             } else if (typeInfo.equalsIgnoreCase("longtext")) {
  870.                                 size = Integer.toString(Integer.MAX_VALUE);
  871.                             } else if (typeInfo.equalsIgnoreCase("enum")) {
  872.                                 size = "255";
  873.                             } else if (typeInfo.equalsIgnoreCase("set")) {
  874.                                 size = "255";
  875.                             }
  876.                             rowVal[6] = size.getBytes();
  877.                             rowVal[8] = new byte[] { (byte) '0' };
  878.                         }
  879.                     } else {
  880.                         rowVal[8] = new byte[] { (byte) '0' };
  881.                         rowVal[6] = new byte[] { (byte) '0' };
  882.                     }
  883.                     rowVal[7] = Integer.toString(MysqlIO.getMaxBuf()).getBytes();
  884.                     // BUFFER_LENGTH
  885.                     rowVal[9] = new byte[] { (byte) '1', (byte) '0' };
  886.                     // NUM_PREC_RADIX (is this right for char?)
  887.                     String nullable = results.getString("Null");
  888.                     // Nullable?
  889.                     if (nullable != null) {
  890.                         if (nullable.equals("YES")) {
  891.                             rowVal[10] = Integer.toString(java.sql.DatabaseMetaData.columnNullable)
  892.                                                 .getBytes();
  893.                             rowVal[17] = new String("YES").getBytes();
  894.                             // IS_NULLABLE
  895.                         } else {
  896.                             rowVal[10] = Integer.toString(java.sql.DatabaseMetaData.columnNoNulls)
  897.                                                 .getBytes();
  898.                             rowVal[17] = "NO".getBytes();
  899.                         }
  900.                     } else {
  901.                         rowVal[10] = Integer.toString(java.sql.DatabaseMetaData.columnNoNulls)
  902.                                             .getBytes();
  903.                         rowVal[17] = "NO".getBytes();
  904.                     }
  905.                     //
  906.                     // Doesn't always have this field, depending on version
  907.                     //
  908.                     //
  909.                     // REMARK column
  910.                     //
  911.                     try {
  912.                         rowVal[11] = results.getBytes("Extra");
  913.                     } catch (Exception E) {
  914.                         rowVal[11] = new byte[0];
  915.                     }
  916.                     // COLUMN_DEF
  917.                     rowVal[12] = results.getBytes("Default");
  918.                     rowVal[13] = new byte[] { (byte) '0' }; // SQL_DATA_TYPE
  919.                     rowVal[14] = new byte[] { (byte) '0' }; // SQL_DATE_TIME_SUB
  920.                     rowVal[15] = rowVal[6]; // CHAR_OCTET_LENGTH
  921.                     rowVal[16] = Integer.toString(ordPos++).getBytes();
  922.                     // ORDINAL_POSITION
  923.                     tuples.add(rowVal);
  924.                 }
  925.             } finally {
  926.                 if (results != null) {
  927.                     try {
  928.                         results.close();
  929.                     } catch (Exception ex) {
  930.                         ;
  931.                     }
  932.                     results = null;
  933.                 }
  934.                 if (stmt != null) {
  935.                     try {
  936.                         stmt.close();
  937.                     } catch (Exception ex) {
  938.                         ;
  939.                     }
  940.                     stmt = null;
  941.                 }
  942.             }
  943.         }
  944.         java.sql.ResultSet results = buildResultSet(fields, tuples);
  945.         return results;
  946.     }
  947.     /**
  948.      * JDBC 2.0 Return the connection that produced this metadata object.
  949.      *
  950.      * @return the connection that produced this metadata object.
  951.      *
  952.      * @throws SQLException if a database error occurs
  953.      */
  954.     public java.sql.Connection getConnection() throws SQLException {
  955.         return (java.sql.Connection) this.conn;
  956.     }
  957.     /**
  958.      * Get a description of the foreign key columns in the foreign key table
  959.      * that reference the primary key columns of the primary key table
  960.      * (describe how one table imports another's key.) This should normally
  961.      * return a single foreign key/primary key pair (most tables only import a
  962.      * foreign key from a table once.)  They are ordered by FKTABLE_CAT,
  963.      * FKTABLE_SCHEM, FKTABLE_NAME, and KEY_SEQ.
  964.      * 
  965.      * <P>
  966.      * Each foreign key column description has the following columns:
  967.      * 
  968.      * <OL>
  969.      * <li>
  970.      * <B>PKTABLE_CAT</B> String => primary key table catalog (may be null)
  971.      * </li>
  972.      * <li>
  973.      * <B>PKTABLE_SCHEM</B> String => primary key table schema (may be null)
  974.      * </li>
  975.      * <li>
  976.      * <B>PKTABLE_NAME</B> String => primary key table name
  977.      * </li>
  978.      * <li>
  979.      * <B>PKCOLUMN_NAME</B> String => primary key column name
  980.      * </li>
  981.      * <li>
  982.      * <B>FKTABLE_CAT</B> String => foreign key table catalog (may be null)
  983.      * being exported (may be null)
  984.      * </li>
  985.      * <li>
  986.      * <B>FKTABLE_SCHEM</B> String => foreign key table schema (may be null)
  987.      * being exported (may be null)
  988.      * </li>
  989.      * <li>
  990.      * <B>FKTABLE_NAME</B> String => foreign key table name being exported
  991.      * </li>
  992.      * <li>
  993.      * <B>FKCOLUMN_NAME</B> String => foreign key column name being exported
  994.      * </li>
  995.      * <li>
  996.      * <B>KEY_SEQ</B> short => sequence number within foreign key
  997.      * </li>
  998.      * <li>
  999.      * <B>UPDATE_RULE</B> short => What happens to foreign key when primary is
  1000.      * updated:
  1001.      * 
  1002.      * <UL>
  1003.      * <li>
  1004.      * importedKeyCascade - change imported key to agree with primary key
  1005.      * update
  1006.      * </li>
  1007.      * <li>
  1008.      * importedKeyRestrict - do not allow update of primary key if it has been
  1009.      * imported
  1010.      * </li>
  1011.      * <li>
  1012.      * importedKeySetNull - change imported key to NULL if its primary key has
  1013.      * been updated
  1014.      * </li>
  1015.      * </ul>
  1016.      * 
  1017.      * </li>
  1018.      * <li>
  1019.      * <B>DELETE_RULE</B> short => What happens to the foreign key when primary
  1020.      * is deleted.
  1021.      * 
  1022.      * <UL>
  1023.      * <li>
  1024.      * importedKeyCascade - delete rows that import a deleted key
  1025.      * </li>
  1026.      * <li>
  1027.      * importedKeyRestrict - do not allow delete of primary key if it has been
  1028.      * imported
  1029.      * </li>
  1030.      * <li>
  1031.      * importedKeySetNull - change imported key to NULL if its primary key has
  1032.      * been deleted
  1033.      * </li>
  1034.      * </ul>
  1035.      * 
  1036.      * </li>
  1037.      * <li>
  1038.      * <B>FK_NAME</B> String => foreign key identifier (may be null)
  1039.      * </li>
  1040.      * <li>
  1041.      * <B>PK_NAME</B> String => primary key identifier (may be null)
  1042.      * </li>
  1043.      * </ol>
  1044.      * </p>
  1045.      *
  1046.      * @param primaryCatalog a catalog name; "" retrieves those without a
  1047.      *        catalog
  1048.      * @param primarySchema a schema name pattern; "" retrieves those without a
  1049.      *        schema
  1050.      * @param primaryTable a table name
  1051.      * @param foreignCatalog a catalog name; "" retrieves those without a
  1052.      *        catalog
  1053.      * @param foreignSchema a schema name pattern; "" retrieves those without a
  1054.      *        schema
  1055.      * @param foreignTable a table name
  1056.      *
  1057.      * @return ResultSet each row is a foreign key column description
  1058.      *
  1059.      * @throws java.sql.SQLException if a database access error occurs
  1060.      */
  1061.     public java.sql.ResultSet getCrossReference(String primaryCatalog,
  1062.         String primarySchema, String primaryTable, String foreignCatalog,
  1063.         String foreignSchema, String foreignTable) throws java.sql.SQLException {
  1064.         if (Driver.TRACE) {
  1065.             Object[] args = {
  1066.                 primaryCatalog, primarySchema, primaryTable, foreignCatalog,
  1067.                 foreignSchema, foreignTable
  1068.             };
  1069.             Debug.methodCall(this, "getCrossReference", args);
  1070.         }
  1071.         if (primaryTable == null) {
  1072.             throw new java.sql.SQLException("Table not specified.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
  1073.         }
  1074.         Field[] fields = new Field[14];
  1075.         fields[0] = new Field("", "PKTABLE_CAT", Types.CHAR, 255);
  1076.         fields[1] = new Field("", "PKTABLE_SCHEM", Types.CHAR, 0);
  1077.         fields[2] = new Field("", "PKTABLE_NAME", Types.CHAR, 255);
  1078.         fields[3] = new Field("", "PKCOLUMN_NAME", Types.CHAR, 32);
  1079.         fields[4] = new Field("", "FKTABLE_CAT", Types.CHAR, 255);
  1080.         fields[5] = new Field("", "FKTABLE_SCHEM", Types.CHAR, 0);
  1081.         fields[6] = new Field("", "FKTABLE_NAME", Types.CHAR, 255);
  1082.         fields[7] = new Field("", "FKCOLUMN_NAME", Types.CHAR, 32);
  1083.         fields[8] = new Field("", "KEY_SEQ", Types.SMALLINT, 2);
  1084.         fields[9] = new Field("", "UPDATE_RULE", Types.SMALLINT, 2);
  1085.         fields[10] = new Field("", "DELETE_RULE", Types.SMALLINT, 2);
  1086.         fields[11] = new Field("", "FK_NAME", Types.CHAR, 255);
  1087.         fields[12] = new Field("", "PK_NAME", Types.CHAR, 0);
  1088.         fields[13] = new Field("", "DEFERRABILITY", Types.INTEGER, 2);
  1089.         if (this.conn.getIO().versionMeetsMinimum(3, 23, 0)) {
  1090.             Statement stmt = null;
  1091.             ResultSet fkresults = null;
  1092.             try {
  1093.                 /*
  1094.                  * Get foreign key information for table
  1095.                  */
  1096.                 if (this.conn.getIO().versionMeetsMinimum(3, 23, 50)) {
  1097.                     // we can use 'SHOW CREATE TABLE'
  1098.                     String database = this.database;
  1099.                     if (foreignCatalog != null) {
  1100.                         if (!foreignCatalog.equals("")) {
  1101.                             database = foreignCatalog;
  1102.                         }
  1103.                     }
  1104.                     fkresults = extractForeignKeyFromCreateTable(this.conn,
  1105.                             this, database, null);
  1106.                 } else {
  1107.                     String databasePart = "";
  1108.                     if (foreignCatalog != null) {
  1109.                         if (!foreignCatalog.equals("")) {
  1110.                             databasePart = " FROM " + foreignCatalog;
  1111.                         }
  1112.                     } else {
  1113.                         databasePart = " FROM " + this.database;
  1114.                     }
  1115.                     stmt = this.conn.createStatement();
  1116.                     
  1117. if (stmt.getMaxRows() != 0) {
  1118. stmt.setMaxRows(0);
  1119. }
  1120.                     fkresults = stmt.executeQuery("show table status "
  1121.                             + databasePart);
  1122.                 }
  1123.                 String foreignTableWithCase = getTableNameWithCase(foreignTable);
  1124.                 String primaryTableWithCase = getTableNameWithCase(primaryTable);
  1125.                 /*
  1126.                 * Parse imported foreign key information
  1127.                 */
  1128.                 ArrayList tuples = new ArrayList();
  1129.                 String dummy;
  1130.                 while (fkresults.next()) {
  1131.                     String tableType = fkresults.getString("Type");
  1132.                     if ((tableType != null)
  1133.                             && (tableType.equalsIgnoreCase("innodb")
  1134.                             || tableType.equalsIgnoreCase(SUPPORTS_FK))) {
  1135.                         String comment = fkresults.getString("Comment").trim();
  1136.                         if (comment != null) {
  1137.                             StringTokenizer commentTokens = new StringTokenizer(comment,
  1138.                                     ";", false);
  1139.                             if (commentTokens.hasMoreTokens()) {
  1140.                                 dummy = commentTokens.nextToken();
  1141.                                 // Skip InnoDB comment
  1142.                             }
  1143.                             while (commentTokens.hasMoreTokens()) {
  1144.                                 String keys = commentTokens.nextToken();
  1145.                                 // simple-columned keys: (m) REFER airline/tt(a)
  1146.                                 // multi-columned keys : (m n) REFER airline/vv(a b)
  1147.                                 int firstLeftParenIndex = keys.indexOf('(');
  1148.                                 int firstRightParenIndex = keys.indexOf(')');
  1149.                                 String constraintName = keys.substring(0,
  1150.                                         firstLeftParenIndex);
  1151.                                 String referencingColumns = keys.substring(firstLeftParenIndex
  1152.                                         + 1, firstRightParenIndex);
  1153.                                 StringTokenizer referencingColumnsTokenizer = new StringTokenizer(referencingColumns,
  1154.                                         ", ");
  1155.                                 int secondLeftParenIndex = keys.indexOf('(',
  1156.                                         firstRightParenIndex + 1);
  1157.                                 int secondRightParenIndex = keys.indexOf(')',
  1158.                                         firstRightParenIndex + 1);
  1159.                                 String referencedColumns = keys.substring(secondLeftParenIndex
  1160.                                         + 1, secondRightParenIndex);
  1161.                                 StringTokenizer referencedColumnsTokenizer = new StringTokenizer(referencedColumns,
  1162.                                         ", ");
  1163.                                 int slashIndex = keys.indexOf('/');
  1164.                                 String referencedTable = keys.substring(slashIndex
  1165.                                         + 1, secondLeftParenIndex);
  1166.                                 int keySeq = 0;
  1167.                                 while (referencingColumnsTokenizer
  1168.                                         .hasMoreTokens()) {
  1169.                                     String referencingColumn = referencingColumnsTokenizer
  1170.                                         .nextToken();
  1171.                                     // one tuple for each table between parenthesis
  1172.                                     byte[][] tuple = new byte[14][];
  1173.                                     tuple[4] = ((foreignCatalog == null) ? null
  1174.                                                                          : s2b(foreignCatalog));
  1175.                                     tuple[5] = ((foreignSchema == null) ? null
  1176.                                                                         : s2b(foreignSchema));
  1177.                                     dummy = fkresults.getString("Name"); // FKTABLE_NAME
  1178.                                     if (dummy.compareTo(foreignTableWithCase) != 0) {
  1179.                                         continue;
  1180.                                     } else {
  1181.                                         tuple[6] = s2b(dummy);
  1182.                                     }
  1183.                                     tuple[7] = s2b(referencingColumn); // FKCOLUMN_NAME
  1184.                                     tuple[0] = ((primaryCatalog == null) ? null
  1185.                                                                          : s2b(primaryCatalog));
  1186.                                     tuple[1] = ((primarySchema == null) ? null
  1187.                                                                         : s2b(primarySchema));
  1188.                                     // Skip foreign key if it doesn't refer to the right table
  1189.                                     if (referencedTable.compareTo(
  1190.                                                 primaryTableWithCase) != 0) {
  1191.                                         continue;
  1192.                                     }
  1193.                                     tuple[2] = s2b(referencedTable); // PKTABLE_NAME
  1194.                                     tuple[3] = s2b(referencedColumnsTokenizer
  1195.                                             .nextToken()); // PKCOLUMN_NAME
  1196.                                     tuple[8] = Integer.toString(keySeq)
  1197.                                                       .getBytes(); // KEY_SEQ
  1198.                                     int[] actions = getForeignKeyActions(keys);
  1199.                                     tuple[9] = Integer.toString(actions[1])
  1200.                                                       .getBytes();
  1201.                                     tuple[10] = Integer.toString(actions[0])
  1202.                                                        .getBytes();
  1203.                                     tuple[11] = s2b(constraintName); // FK_NAME
  1204.                                     tuple[12] = null; // PK_NAME
  1205.                                     tuple[13] = Integer.toString(java.sql.DatabaseMetaData.importedKeyNotDeferrable)
  1206.                                                        .getBytes();
  1207.                                     tuples.add(tuple);
  1208.                                     keySeq++;
  1209.                                 }
  1210.                             }
  1211.                         }
  1212.                     }
  1213.                 }
  1214.                 if (Driver.TRACE) {
  1215.                     StringBuffer rows = new StringBuffer();
  1216.                     rows.append("n");
  1217.                     for (int i = 0; i < tuples.size(); i++) {
  1218.                         byte[][] b = (byte[][]) tuples.get(i);
  1219.                         rows.append("[Row] ");
  1220.                         boolean firstTime = true;
  1221.                         for (int j = 0; j < b.length; j++) {
  1222.                             if (!firstTime) {
  1223.                                 rows.append(", ");
  1224.                             } else {
  1225.                                 firstTime = false;
  1226.                             }
  1227.                             if (b[j] == null) {
  1228.                                 rows.append("null");
  1229.                             } else {
  1230.                                 rows.append(new String(b[j]));
  1231.                             }
  1232.                         }
  1233.                         rows.append("n");
  1234.                     }
  1235.                     Debug.returnValue(this, "getCrossReference", rows.toString());
  1236.                 }
  1237.                 return buildResultSet(fields, tuples);
  1238.             } finally {
  1239.                 if (fkresults != null) {
  1240.                     try {
  1241.                         fkresults.close();
  1242.                     } catch (Exception sqlEx) {
  1243.                         AssertionFailedException.shouldNotHappen(sqlEx);
  1244.                     }
  1245.                     fkresults = null;
  1246.                 }
  1247.                 if (stmt != null) {
  1248.                     try {
  1249.                         stmt.close();
  1250.                     } catch (Exception ex) {
  1251.                         ;
  1252.                     }
  1253.                     stmt = null;
  1254.                 }
  1255.             }
  1256.         } else {
  1257.             return buildResultSet(fields, new ArrayList());
  1258.         }
  1259.     }
  1260.     /**
  1261.      * @see DatabaseMetaData#getDatabaseMajorVersion()
  1262.      */
  1263.     public int getDatabaseMajorVersion() throws SQLException {
  1264.         return this.conn.getServerMajorVersion();
  1265.     }
  1266.     /**
  1267.      * @see DatabaseMetaData#getDatabaseMinorVersion()
  1268.      */
  1269.     public int getDatabaseMinorVersion() throws SQLException {
  1270.         return this.conn.getServerMinorVersion();
  1271.     }
  1272.     /**
  1273.      * What's the name of this database product?
  1274.      *
  1275.      * @return database product name
  1276.      *
  1277.      * @throws java.sql.SQLException DOCUMENT ME!
  1278.      */
  1279.     public String getDatabaseProductName() throws java.sql.SQLException {
  1280.         return "MySQL";
  1281.     }
  1282.     /**
  1283.      * What's the version of this database product?
  1284.      *
  1285.      * @return database version
  1286.      *
  1287.      * @throws java.sql.SQLException DOCUMENT ME!
  1288.      */
  1289.     public String getDatabaseProductVersion() throws java.sql.SQLException {
  1290.         return this.conn.getServerVersion();
  1291.     }
  1292.     //----------------------------------------------------------------------
  1293.     /**
  1294.      * What's the database's default transaction isolation level?  The values
  1295.      * are defined in java.sql.Connection.
  1296.      *
  1297.      * @return the default isolation level
  1298.      *
  1299.      * @throws java.sql.SQLException if a database access error occurs
  1300.      *
  1301.      * @see Connection
  1302.      */
  1303.     public int getDefaultTransactionIsolation() throws java.sql.SQLException {
  1304.         if (this.conn.supportsIsolationLevel()) {
  1305.             return java.sql.Connection.TRANSACTION_READ_COMMITTED;
  1306.         } else {
  1307.             return java.sql.Connection.TRANSACTION_NONE;
  1308.         }
  1309.     }
  1310.     /**
  1311.      * What's this JDBC driver's major version number?
  1312.      *
  1313.      * @return JDBC driver major version
  1314.      */
  1315.     public int getDriverMajorVersion() {
  1316.         return Driver.getMajorVersionInternal();
  1317.     }
  1318.     /**
  1319.      * What's this JDBC driver's minor version number?
  1320.      *
  1321.      * @return JDBC driver minor version number
  1322.      */
  1323.     public int getDriverMinorVersion() {
  1324.         return Driver.getMinorVersionInternal();
  1325.     }
  1326.     /**
  1327.      * What's the name of this JDBC driver?
  1328.      *
  1329.      * @return JDBC driver name
  1330.      *
  1331.      * @throws java.sql.SQLException DOCUMENT ME!
  1332.      */
  1333.     public String getDriverName() throws java.sql.SQLException {
  1334.         return "MySQL-AB JDBC Driver";
  1335.     }
  1336.     /**
  1337.      * What's the version of this JDBC driver?
  1338.      *
  1339.      * @return JDBC driver version
  1340.      *
  1341.      * @throws java.sql.SQLException DOCUMENT ME!
  1342.      */
  1343.     public String getDriverVersion() throws java.sql.SQLException {
  1344.         return "mysql-connector-java-3.0.11-stable ( $Date: 2004/02/04 02:47:36 $, $Revision: 1.27.2.34 $ )";
  1345.     }
  1346.     /**
  1347.      * Get a description of a foreign key columns that reference a table's
  1348.      * primary key columns (the foreign keys exported by a table).  They are
  1349.      * ordered by FKTABLE_CAT, FKTABLE_SCHEM, FKTABLE_NAME, and KEY_SEQ.
  1350.      * 
  1351.      * <P>
  1352.      * Each foreign key column description has the following columns:
  1353.      * 
  1354.      * <OL>
  1355.      * <li>
  1356.      * <B>PKTABLE_CAT</B> String => primary key table catalog (may be null)
  1357.      * </li>
  1358.      * <li>
  1359.      * <B>PKTABLE_SCHEM</B> String => primary key table schema (may be null)
  1360.      * </li>
  1361.      * <li>
  1362.      * <B>PKTABLE_NAME</B> String => primary key table name
  1363.      * </li>
  1364.      * <li>
  1365.      * <B>PKCOLUMN_NAME</B> String => primary key column name
  1366.      * </li>
  1367.      * <li>
  1368.      * <B>FKTABLE_CAT</B> String => foreign key table catalog (may be null)
  1369.      * being exported (may be null)
  1370.      * </li>
  1371.      * <li>
  1372.      * <B>FKTABLE_SCHEM</B> String => foreign key table schema (may be null)
  1373.      * being exported (may be null)
  1374.      * </li>
  1375.      * <li>
  1376.      * <B>FKTABLE_NAME</B> String => foreign key table name being exported
  1377.      * </li>
  1378.      * <li>
  1379.      * <B>FKCOLUMN_NAME</B> String => foreign key column name being exported
  1380.      * </li>
  1381.      * <li>
  1382.      * <B>KEY_SEQ</B> short => sequence number within foreign key
  1383.      * </li>
  1384.      * <li>
  1385.      * <B>UPDATE_RULE</B> short => What happens to foreign key when primary is
  1386.      * updated:
  1387.      * 
  1388.      * <UL>
  1389.      * <li>
  1390.      * importedKeyCascade - change imported key to agree with primary key
  1391.      * update
  1392.      * </li>
  1393.      * <li>
  1394.      * importedKeyRestrict - do not allow update of primary key if it has been
  1395.      * imported
  1396.      * </li>
  1397.      * <li>
  1398.      * importedKeySetNull - change imported key to NULL if its primary key has
  1399.      * been updated
  1400.      * </li>
  1401.      * </ul>
  1402.      * 
  1403.      * </li>
  1404.      * <li>
  1405.      * <B>DELETE_RULE</B> short => What happens to the foreign key when primary
  1406.      * is deleted.
  1407.      * 
  1408.      * <UL>
  1409.      * <li>
  1410.      * importedKeyCascade - delete rows that import a deleted key
  1411.      * </li>
  1412.      * <li>
  1413.      * importedKeyRestrict - do not allow delete of primary key if it has been
  1414.      * imported
  1415.      * </li>
  1416.      * <li>
  1417.      * importedKeySetNull - change imported key to NULL if its primary key has
  1418.      * been deleted
  1419.      * </li>
  1420.      * </ul>
  1421.      * 
  1422.      * </li>
  1423.      * <li>
  1424.      * <B>FK_NAME</B> String => foreign key identifier (may be null)
  1425.      * </li>
  1426.      * <li>
  1427.      * <B>PK_NAME</B> String => primary key identifier (may be null)
  1428.      * </li>
  1429.      * </ol>
  1430.      * </p>
  1431.      *
  1432.      * @param catalog a catalog name; "" retrieves those without a catalog
  1433.      * @param schema a schema name pattern; "" retrieves those without a schema
  1434.      * @param table a table name
  1435.      *
  1436.      * @return ResultSet each row is a foreign key column description
  1437.      *
  1438.      * @throws java.sql.SQLException if a database access error occurs
  1439.      *
  1440.      * @see #getImportedKeys
  1441.      */
  1442.     public java.sql.ResultSet getExportedKeys(String catalog, String schema,
  1443.         String table) throws java.sql.SQLException {
  1444.         if (Driver.TRACE) {
  1445.             Object[] args = { catalog, schema, table };
  1446.             Debug.methodCall(this, "getExportedKeys", args);
  1447.         }
  1448.         if (table == null) {
  1449.             throw new java.sql.SQLException("Table not specified.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
  1450.         }
  1451.         Field[] fields = new Field[14];
  1452.         fields[0] = new Field("", "PKTABLE_CAT", Types.CHAR, 255);
  1453.         fields[1] = new Field("", "PKTABLE_SCHEM", Types.CHAR, 0);
  1454.         fields[2] = new Field("", "PKTABLE_NAME", Types.CHAR, 255);
  1455.         fields[3] = new Field("", "PKCOLUMN_NAME", Types.CHAR, 32);
  1456.         fields[4] = new Field("", "FKTABLE_CAT", Types.CHAR, 255);
  1457.         fields[5] = new Field("", "FKTABLE_SCHEM", Types.CHAR, 0);
  1458.         fields[6] = new Field("", "FKTABLE_NAME", Types.CHAR, 255);
  1459.         fields[7] = new Field("", "FKCOLUMN_NAME", Types.CHAR, 32);
  1460.         fields[8] = new Field("", "KEY_SEQ", Types.SMALLINT, 2);
  1461.         fields[9] = new Field("", "UPDATE_RULE", Types.SMALLINT, 2);
  1462.         fields[10] = new Field("", "DELETE_RULE", Types.SMALLINT, 2);
  1463.         fields[11] = new Field("", "FK_NAME", Types.CHAR, 255);
  1464.         fields[12] = new Field("", "PK_NAME", Types.CHAR, 0);
  1465.         fields[13] = new Field("", "DEFERRABILITY", Types.INTEGER, 2);
  1466.         if (this.conn.getIO().versionMeetsMinimum(3, 23, 0)) {
  1467.             Statement stmt = null;
  1468.             ResultSet fkresults = null;
  1469.             try {
  1470.                 /*
  1471.                  * Get foreign key information for table
  1472.                  */
  1473.                 if (this.conn.getIO().versionMeetsMinimum(3, 23, 50)) {
  1474.                     // we can use 'SHOW CREATE TABLE'
  1475.                     String database = this.database;
  1476.                     if (catalog != null) {
  1477.                         if (!catalog.equals("")) {
  1478.                             database = catalog;
  1479.                         }
  1480.                     }
  1481.                     fkresults = extractForeignKeyFromCreateTable(this.conn,
  1482.                             this, database, null);
  1483.                 } else {
  1484.                     String databasePart = "";
  1485.                     if (catalog != null) {
  1486.                         if (!catalog.equals("")) {
  1487.                             databasePart = " FROM " + catalog;
  1488.                         }
  1489.                     } else {
  1490.                         databasePart = " FROM " + this.database;
  1491.                     }
  1492.                     stmt = this.conn.createStatement();
  1493.                     
  1494. if (stmt.getMaxRows() != 0) {
  1495. stmt.setMaxRows(0);
  1496. }
  1497.                     fkresults = stmt.executeQuery("show table status "
  1498.                             + databasePart);
  1499.                 }
  1500.                 // lower-case table name might be turned on
  1501.                 String tableNameWithCase = getTableNameWithCase(table);
  1502.                 /*
  1503.                 * Parse imported foreign key information
  1504.                 */
  1505.                 ArrayList tuples = new ArrayList();
  1506.                 while (fkresults.next()) {
  1507.                     String tableType = fkresults.getString("Type");
  1508.                     if ((tableType != null)
  1509.                             && (tableType.equalsIgnoreCase("innodb")
  1510.                             || tableType.equalsIgnoreCase(SUPPORTS_FK))) {
  1511.                         String comment = fkresults.getString("Comment").trim();
  1512.                         if (comment != null) {
  1513.                             StringTokenizer commentTokens = new StringTokenizer(comment,
  1514.                                     ";", false);
  1515.                             if (commentTokens.hasMoreTokens()) {
  1516.                                 commentTokens.nextToken(); // Skip InnoDB comment
  1517.                                 while (commentTokens.hasMoreTokens()) {
  1518.                                     String keys = commentTokens.nextToken();
  1519.                                     getExportKeyResults(catalog,
  1520.                                         tableNameWithCase, keys, tuples,
  1521.                                         fkresults.getString("Name"));
  1522.                                 }
  1523.                             }
  1524.                         }
  1525.                     }
  1526.                 }
  1527.                 if (Driver.TRACE) {
  1528.                     StringBuffer rows = new StringBuffer();
  1529.                     rows.append("n");
  1530.                     for (int i = 0; i < tuples.size(); i++) {
  1531.                         byte[][] b = (byte[][]) tuples.get(i);
  1532.                         rows.append("[Row] ");
  1533.                         boolean firstTime = true;
  1534.                         for (int j = 0; j < b.length; j++) {
  1535.                             if (!firstTime) {
  1536.                                 rows.append(", ");
  1537.                             } else {
  1538.                                 firstTime = false;
  1539.                             }
  1540.                             if (b[j] == null) {
  1541.                                 rows.append("null");
  1542.                             } else {
  1543.                                 rows.append(new String(b[j]));
  1544.                             }
  1545.                         }
  1546.                         rows.append("n");
  1547.                     }
  1548.                     Debug.returnValue(this, "getExportedKeys", rows.toString());
  1549.                 }
  1550.                 return buildResultSet(fields, tuples);
  1551.             } finally {
  1552.                 if (fkresults != null) {
  1553.                     try {
  1554.                         fkresults.close();
  1555.                     } catch (SQLException sqlEx) {
  1556.                         AssertionFailedException.shouldNotHappen(sqlEx);
  1557.                     }
  1558.                     fkresults = null;
  1559.                 }
  1560.                 if (stmt != null) {
  1561.                     try {
  1562.                         stmt.close();
  1563.                     } catch (Exception ex) {
  1564.                         AssertionFailedException.shouldNotHappen(ex);
  1565.                     }
  1566.                     stmt = null;
  1567.                 }
  1568.             }
  1569.         } else {
  1570.             return buildResultSet(fields, new ArrayList());
  1571.         }
  1572.     }
  1573.     /**
  1574.      * Get all the "extra" characters that can be used in unquoted identifier
  1575.      * names (those beyond a-z, 0-9 and _).
  1576.      *
  1577.      * @return the string containing the extra characters
  1578.      *
  1579.      * @throws java.sql.SQLException DOCUMENT ME!
  1580.      */
  1581.     public String getExtraNameCharacters() throws java.sql.SQLException {
  1582.         return "#@";
  1583.     }
  1584.     /**
  1585.      * What's the string used to quote SQL identifiers? This returns a space "
  1586.      * " if identifier quoting isn't supported. A JDBC compliant driver always
  1587.      * uses a double quote character.
  1588.      *
  1589.      * @return the quoting string
  1590.      *
  1591.      * @throws java.sql.SQLException DOCUMENT ME!
  1592.      */
  1593.     public String getIdentifierQuoteString() throws java.sql.SQLException {
  1594.         if (this.conn.supportsQuotedIdentifiers()) {
  1595.             if (!this.conn.useAnsiQuotedIdentifiers()) {
  1596.                 return "`";
  1597.             } else {
  1598.                 return """;
  1599.             }
  1600.         } else {
  1601.             return " ";
  1602.         }
  1603.     }
  1604.     /**
  1605.      * Get a description of the primary key columns that are referenced by a
  1606.      * table's foreign key columns (the primary keys imported by a table).
  1607.      * They are ordered by PKTABLE_CAT, PKTABLE_SCHEM, PKTABLE_NAME, and
  1608.      * KEY_SEQ.
  1609.      * 
  1610.      * <P>
  1611.      * Each primary key column description has the following columns:
  1612.      * 
  1613.      * <OL>
  1614.      * <li>
  1615.      * <B>PKTABLE_CAT</B> String => primary key table catalog being imported
  1616.      * (may be null)
  1617.      * </li>
  1618.      * <li>
  1619.      * <B>PKTABLE_SCHEM</B> String => primary key table schema being imported
  1620.      * (may be null)
  1621.      * </li>
  1622.      * <li>
  1623.      * <B>PKTABLE_NAME</B> String => primary key table name being imported
  1624.      * </li>
  1625.      * <li>
  1626.      * <B>PKCOLUMN_NAME</B> String => primary key column name being imported
  1627.      * </li>
  1628.      * <li>
  1629.      * <B>FKTABLE_CAT</B> String => foreign key table catalog (may be null)
  1630.      * </li>
  1631.      * <li>
  1632.      * <B>FKTABLE_SCHEM</B> String => foreign key table schema (may be null)
  1633.      * </li>
  1634.      * <li>
  1635.      * <B>FKTABLE_NAME</B> String => foreign key table name
  1636.      * </li>
  1637.      * <li>
  1638.      * <B>FKCOLUMN_NAME</B> String => foreign key column name
  1639.      * </li>
  1640.      * <li>
  1641.      * <B>KEY_SEQ</B> short => sequence number within foreign key
  1642.      * </li>
  1643.      * <li>
  1644.      * <B>UPDATE_RULE</B> short => What happens to foreign key when primary is
  1645.      * updated:
  1646.      * 
  1647.      * <UL>
  1648.      * <li>
  1649.      * importedKeyCascade - change imported key to agree with primary key
  1650.      * update
  1651.      * </li>
  1652.      * <li>
  1653.      * importedKeyRestrict - do not allow update of primary key if it has been
  1654.      * imported
  1655.      * </li>
  1656.      * <li>
  1657.      * importedKeySetNull - change imported key to NULL if its primary key has
  1658.      * been updated
  1659.      * </li>
  1660.      * </ul>
  1661.      * 
  1662.      * </li>
  1663.      * <li>
  1664.      * <B>DELETE_RULE</B> short => What happens to the foreign key when primary
  1665.      * is deleted.
  1666.      * 
  1667.      * <UL>
  1668.      * <li>
  1669.      * importedKeyCascade - delete rows that import a deleted key
  1670.      * </li>
  1671.      * <li>
  1672.      * importedKeyRestrict - do not allow delete of primary key if it has been
  1673.      * imported
  1674.      * </li>
  1675.      * <li>
  1676.      * importedKeySetNull - change imported key to NULL if its primary key has
  1677.      * been deleted
  1678.      * </li>
  1679.      * </ul>
  1680.      * 
  1681.      * </li>
  1682.      * <li>
  1683.      * <B>FK_NAME</B> String => foreign key name (may be null)
  1684.      * </li>
  1685.      * <li>
  1686.      * <B>PK_NAME</B> String => primary key name (may be null)
  1687.      * </li>
  1688.      * </ol>
  1689.      * </p>
  1690.      *
  1691.      * @param catalog a catalog name; "" retrieves those without a catalog
  1692.      * @param schema a schema name pattern; "" retrieves those without a schema
  1693.      * @param table a table name
  1694.      *
  1695.      * @return ResultSet each row is a primary key column description
  1696.      *
  1697.      * @throws java.sql.SQLException if a database access error occurs
  1698.      *
  1699.      * @see #getExportedKeys
  1700.      */
  1701.     public java.sql.ResultSet getImportedKeys(String catalog, String schema,
  1702.         String table) throws java.sql.SQLException {
  1703.         if (Driver.TRACE) {
  1704.             Object[] args = { catalog, schema, table };
  1705.             Debug.methodCall(this, "getImportedKeys", args);
  1706.         }
  1707.         if (table == null) {
  1708.             throw new java.sql.SQLException("Table not specified.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
  1709.         }
  1710.         Field[] fields = new Field[14];
  1711.         fields[0] = new Field("", "PKTABLE_CAT", Types.CHAR, 255);
  1712.         fields[1] = new Field("", "PKTABLE_SCHEM", Types.CHAR, 0);
  1713.         fields[2] = new Field("", "PKTABLE_NAME", Types.CHAR, 255);
  1714.         fields[3] = new Field("", "PKCOLUMN_NAME", Types.CHAR, 32);
  1715.         fields[4] = new Field("", "FKTABLE_CAT", Types.CHAR, 255);
  1716.         fields[5] = new Field("", "FKTABLE_SCHEM", Types.CHAR, 0);
  1717.         fields[6] = new Field("", "FKTABLE_NAME", Types.CHAR, 255);
  1718.         fields[7] = new Field("", "FKCOLUMN_NAME", Types.CHAR, 32);
  1719.         fields[8] = new Field("", "KEY_SEQ", Types.SMALLINT, 2);
  1720.         fields[9] = new Field("", "UPDATE_RULE", Types.SMALLINT, 2);
  1721.         fields[10] = new Field("", "DELETE_RULE", Types.SMALLINT, 2);
  1722.         fields[11] = new Field("", "FK_NAME", Types.CHAR, 255);
  1723.         fields[12] = new Field("", "PK_NAME", Types.CHAR, 0);
  1724.         fields[13] = new Field("", "DEFERRABILITY", Types.INTEGER, 2);
  1725.         if (this.conn.getIO().versionMeetsMinimum(3, 23, 0)) {
  1726.             Statement stmt = null;
  1727.             ResultSet fkresults = null;
  1728.             try {
  1729.                 /*
  1730.                  * Get foreign key information for table
  1731.                  */
  1732.                 if (this.conn.getIO().versionMeetsMinimum(3, 23, 50)) {
  1733.                     // we can use 'SHOW CREATE TABLE'
  1734.                     String database = this.database;
  1735.                     if (catalog != null) {
  1736.                         if (!catalog.equals("")) {
  1737.                             database = catalog;
  1738.                         }
  1739.                     }
  1740.                     fkresults = extractForeignKeyFromCreateTable(this.conn,
  1741.                             this, database, table);
  1742.                 } else {
  1743.                     String databasePart = "";
  1744.                     if (catalog != null) {
  1745.                         if (!catalog.equals("")) {
  1746.                             databasePart = " FROM " + catalog;
  1747.                         }
  1748.                     } else {
  1749.                         databasePart = " FROM " + this.database;
  1750.                     }
  1751.                     stmt = this.conn.createStatement();
  1752.                     
  1753. if (stmt.getMaxRows() != 0) {
  1754. stmt.setMaxRows(0);
  1755. }
  1756.                     fkresults = stmt.executeQuery("show table status "
  1757.                             + databasePart + " like '" + table + "'");
  1758.                 }
  1759.                 /*
  1760.                 * Parse imported foreign key information
  1761.                 */
  1762.                 ArrayList tuples = new ArrayList();
  1763.                 while (fkresults.next()) {
  1764.                     String tableType = fkresults.getString("Type");
  1765.                     if ((tableType != null)
  1766.                             && (tableType.equalsIgnoreCase("innodb")
  1767.                             || tableType.equalsIgnoreCase(SUPPORTS_FK))) {
  1768.                         String comment = fkresults.getString("Comment").trim();
  1769.                         if (comment != null) {
  1770.                             StringTokenizer commentTokens = new StringTokenizer(comment,
  1771.                                     ";", false);
  1772.                             if (commentTokens.hasMoreTokens()) {
  1773.                                 commentTokens.nextToken(); // Skip InnoDB comment
  1774.                                 while (commentTokens.hasMoreTokens()) {
  1775.                                     String keys = commentTokens.nextToken();
  1776.                                     getImportKeyResults(catalog, table, keys,
  1777.                                         tuples);
  1778.                                 }
  1779.                             }
  1780.                         }
  1781.                     }
  1782.                 }
  1783.                 if (Driver.TRACE) {
  1784.                     StringBuffer rows = new StringBuffer();
  1785.                     rows.append("n");
  1786.                     for (int i = 0; i < tuples.size(); i++) {
  1787.                         byte[][] b = (byte[][]) tuples.get(i);
  1788.                         rows.append("[Row] ");
  1789.                         boolean firstTime = true;
  1790.                         for (int j = 0; j < b.length; j++) {
  1791.                             if (!firstTime) {
  1792.                                 rows.append(", ");
  1793.                             } else {
  1794.                                 firstTime = false;
  1795.                             }
  1796.                             if (b[j] == null) {
  1797.                                 rows.append("null");
  1798.                             } else {
  1799.                                 rows.append(new String(b[j]));
  1800.                             }
  1801.                         }
  1802.                         rows.append("n");
  1803.                     }
  1804.                     Debug.returnValue(this, "getImportedKeys", rows.toString());
  1805.                 }
  1806.                 return buildResultSet(fields, tuples);
  1807.             } finally {
  1808.                 if (fkresults != null) {
  1809.                     try {
  1810.                         fkresults.close();
  1811.                     } catch (SQLException sqlEx) {
  1812.                         AssertionFailedException.shouldNotHappen(sqlEx);
  1813.                     }
  1814.                     fkresults = null;
  1815.                 }
  1816.                 if (stmt != null) {
  1817.                     try {
  1818.                         stmt.close();
  1819.                     } catch (Exception ex) {
  1820.                         AssertionFailedException.shouldNotHappen(ex);
  1821.                     }
  1822.                     stmt = null;
  1823.                 }
  1824.             }
  1825.         } else {
  1826.             return buildResultSet(fields, new ArrayList());
  1827.         }
  1828.     }
  1829.     /**
  1830.      * Get a description of a table's indices and statistics. They are ordered
  1831.      * by NON_UNIQUE, TYPE, INDEX_NAME, and ORDINAL_POSITION.
  1832.      * 
  1833.      * <P>
  1834.      * Each index column description has the following columns:
  1835.      * 
  1836.      * <OL>
  1837.      * <li>
  1838.      * <B>TABLE_CAT</B> String => table catalog (may be null)
  1839.      * </li>
  1840.      * <li>
  1841.      * <B>TABLE_SCHEM</B> String => table schema (may be null)
  1842.      * </li>
  1843.      * <li>
  1844.      * <B>TABLE_NAME</B> String => table name
  1845.      * </li>
  1846.      * <li>
  1847.      * <B>NON_UNIQUE</B> boolean => Can index values be non-unique? false when
  1848.      * TYPE is tableIndexStatistic
  1849.      * </li>
  1850.      * <li>
  1851.      * <B>INDEX_QUALIFIER</B> String => index catalog (may be null); null when
  1852.      * TYPE is tableIndexStatistic
  1853.      * </li>
  1854.      * <li>
  1855.      * <B>INDEX_NAME</B> String => index name; null when TYPE is
  1856.      * tableIndexStatistic
  1857.      * </li>
  1858.      * <li>
  1859.      * <B>TYPE</B> short => index type:
  1860.      * 
  1861.      * <UL>
  1862.      * <li>
  1863.      * tableIndexStatistic - this identifies table statistics that are returned
  1864.      * in conjuction with a table's index descriptions
  1865.      * </li>
  1866.      * <li>
  1867.      * tableIndexClustered - this is a clustered index
  1868.      * </li>
  1869.      * <li>
  1870.      * tableIndexHashed - this is a hashed index
  1871.      * </li>
  1872.      * <li>
  1873.      * tableIndexOther - this is some other style of index
  1874.      * </li>
  1875.      * </ul>
  1876.      * 
  1877.      * </li>
  1878.      * <li>
  1879.      * <B>ORDINAL_POSITION</B> short => column sequence number within index;
  1880.      * zero when TYPE is tableIndexStatistic
  1881.      * </li>
  1882.      * <li>
  1883.      * <B>COLUMN_NAME</B> String => column name; null when TYPE is
  1884.      * tableIndexStatistic
  1885.      * </li>
  1886.      * <li>
  1887.      * <B>ASC_OR_DESC</B> String => column sort sequence, "A" => ascending, "D"
  1888.      * => descending, may be null if sort sequence is not supported; null when
  1889.      * TYPE is tableIndexStatistic
  1890.      * </li>
  1891.      * <li>
  1892.      * <B>CARDINALITY</B> int => When TYPE is tableIndexStatisic then this is
  1893.      * the number of rows in the table; otherwise it is the number of unique
  1894.      * values in the index.
  1895.      * </li>
  1896.      * <li>
  1897.      * <B>PAGES</B> int => When TYPE is  tableIndexStatisic then this is the
  1898.      * number of pages used for the table, otherwise it is the number of pages
  1899.      * used for the current index.
  1900.      * </li>
  1901.      * <li>
  1902.      * <B>FILTER_CONDITION</B> String => Filter condition, if any. (may be
  1903.      * null)
  1904.      * </li>
  1905.      * </ol>
  1906.      * </p>
  1907.      *
  1908.      * @param catalog a catalog name; "" retrieves those without a catalog
  1909.      * @param schema a schema name pattern; "" retrieves those without a schema
  1910.      * @param table a table name
  1911.      * @param unique when true, return only indices for unique values; when
  1912.      *        false, return indices regardless of whether unique or not
  1913.      * @param approximate when true, result is allowed to reflect approximate
  1914.      *        or out of data values; when false, results are requested to be
  1915.      *        accurate
  1916.      *
  1917.      * @return ResultSet each row is an index column description
  1918.      *
  1919.      * @throws java.sql.SQLException DOCUMENT ME!
  1920.      */
  1921.     public java.sql.ResultSet getIndexInfo(String catalog, String schema,
  1922.         String table, boolean unique, boolean approximate)
  1923.         throws java.sql.SQLException {
  1924.         /*
  1925.          * MySQL stores index information in the following fields:
  1926.          *
  1927.          * Table
  1928.          * Non_unique
  1929.          * Key_name
  1930.          * Seq_in_index
  1931.          * Column_name
  1932.          * Collation
  1933.          * Cardinality
  1934.          * Sub_part
  1935.          */
  1936.         String databasePart = "";
  1937.         if (catalog != null) {
  1938.             if (!catalog.equals("")) {
  1939.                 databasePart = " FROM " + this.quotedId + catalog
  1940.                     + this.quotedId;
  1941.             }
  1942.         } else {
  1943.             databasePart = " FROM " + this.quotedId + this.database
  1944.                 + this.quotedId;
  1945.         }
  1946.         Statement stmt = null;
  1947.         ResultSet results = null;
  1948.         try {
  1949.             stmt = this.conn.createStatement();
  1950.             
  1951. if (stmt.getMaxRows() != 0) {
  1952. stmt.setMaxRows(0);
  1953. }
  1954.             StringBuffer queryBuf = new StringBuffer("SHOW INDEX FROM ");
  1955.             queryBuf.append(this.quotedId);
  1956.             queryBuf.append(table);
  1957.             queryBuf.append(this.quotedId);
  1958.             queryBuf.append(databasePart);
  1959.             results = stmt.executeQuery(queryBuf.toString());
  1960.             Field[] fields = new Field[13];
  1961.             fields[0] = new Field("", "TABLE_CAT", Types.CHAR, 255);
  1962.             fields[1] = new Field("", "TABLE_SCHEM", Types.CHAR, 0);
  1963.             fields[2] = new Field("", "TABLE_NAME", Types.CHAR, 255);
  1964.             fields[3] = new Field("", "NON_UNIQUE", Types.CHAR, 3);
  1965.             fields[4] = new Field("", "INDEX_QUALIFIER", Types.CHAR, 1);
  1966.             fields[5] = new Field("", "INDEX_NAME", Types.CHAR, 32);
  1967.             fields[6] = new Field("", "TYPE", Types.CHAR, 32);
  1968.             fields[7] = new Field("", "ORDINAL_POSITION", Types.SMALLINT, 5);
  1969.             fields[8] = new Field("", "COLUMN_NAME", Types.CHAR, 32);
  1970.             fields[9] = new Field("", "ASC_OR_DESC", Types.CHAR, 1);
  1971.             fields[10] = new Field("", "CARDINALITY", Types.INTEGER, 10);
  1972.             fields[11] = new Field("", "PAGES", Types.INTEGER, 10);
  1973.             fields[12] = new Field("", "FILTER_CONDITION", Types.CHAR, 32);
  1974.             byte[] connectionCatalogAsBytes;
  1975.             if (catalog == null) {
  1976.                 connectionCatalogAsBytes = s2b(this.conn.getCatalog());
  1977.             } else {
  1978.                 connectionCatalogAsBytes = s2b(catalog);
  1979.             }
  1980.             ArrayList rows = new ArrayList();
  1981.             while (results.next()) {
  1982.                 byte[][] row = new byte[14][];
  1983.                 row[0] = connectionCatalogAsBytes;
  1984.                 row[1] = null;
  1985.                 row[2] = results.getBytes("Table");
  1986.                 row[3] = ((results.getInt("Non_unique") != 0) ? s2b("true")
  1987.                                                               : s2b("false"));
  1988.                 row[4] = new byte[0];
  1989.                 row[5] = results.getBytes("Key_name");
  1990.                 row[6] = Integer.toString(java.sql.DatabaseMetaData.tableIndexOther)
  1991.                                 .getBytes();
  1992.                 row[7] = results.getBytes("Seq_in_index");
  1993.                 row[8] = results.getBytes("Column_name");
  1994.                 row[9] = results.getBytes("Collation");
  1995.                 row[10] = results.getBytes("Cardinality");
  1996.                 row[11] = s2b("0");
  1997.                 row[12] = null;
  1998.                 rows.add(row);
  1999.             }
  2000.             java.sql.ResultSet indexInfo = buildResultSet(fields, rows);
  2001.             return indexInfo;
  2002.         } finally {
  2003.             if (results != null) {
  2004.                 try {
  2005.                     results.close();
  2006.                 } catch (Exception ex) {
  2007.                     ;
  2008.                 }
  2009.                 results = null;
  2010.             }
  2011.             if (stmt != null) {
  2012.                 try {
  2013.                     stmt.close();
  2014.                 } catch (Exception ex) {
  2015.                     ;
  2016.                 }
  2017.                 stmt = null;
  2018.             }
  2019.         }
  2020.     }
  2021.     /**
  2022.      * @see DatabaseMetaData#getJDBCMajorVersion()
  2023.      */
  2024.     public int getJDBCMajorVersion() throws SQLException {
  2025.         return 3;
  2026.     }
  2027.     /**
  2028.      * @see DatabaseMetaData#getJDBCMinorVersion()
  2029.      */
  2030.     public int getJDBCMinorVersion() throws SQLException {
  2031.         return 0;
  2032.     }
  2033.     //----------------------------------------------------------------------
  2034.     // The following group of methods exposes various limitations
  2035.     // based on the target database with the current driver.
  2036.     // Unless otherwise specified, a result of zero means there is no
  2037.     // limit, or the limit is not known.
  2038.     /**
  2039.      * How many hex characters can you have in an inline binary literal?
  2040.      *
  2041.      * @return max literal length
  2042.      *
  2043.      * @throws java.sql.SQLException DOCUMENT ME!
  2044.      */
  2045.     public int getMaxBinaryLiteralLength() throws java.sql.SQLException {
  2046.         return 16777208;
  2047.     }
  2048.     /**
  2049.      * What's the maximum length of a catalog name?
  2050.      *
  2051.      * @return max name length in bytes
  2052.      *
  2053.      * @throws java.sql.SQLException DOCUMENT ME!
  2054.      */
  2055.     public int getMaxCatalogNameLength() throws java.sql.SQLException {
  2056.         return 32;
  2057.     }
  2058.     /**
  2059.      * What's the max length for a character literal?
  2060.      *
  2061.      * @return max literal length
  2062.      *
  2063.      * @throws java.sql.SQLException DOCUMENT ME!
  2064.      */
  2065.     public int getMaxCharLiteralLength() throws java.sql.SQLException {
  2066.         return 16777208;
  2067.     }
  2068.     /**
  2069.      * What's the limit on column name length?
  2070.      *
  2071.      * @return max literal length
  2072.      *
  2073.      * @throws java.sql.SQLException DOCUMENT ME!
  2074.      */
  2075.     public int getMaxColumnNameLength() throws java.sql.SQLException {
  2076.         return 64;
  2077.     }
  2078.     /**
  2079.      * What's the maximum number of columns in a "GROUP BY" clause?
  2080.      *
  2081.      * @return max number of columns
  2082.      *
  2083.      * @throws java.sql.SQLException DOCUMENT ME!
  2084.      */
  2085.     public int getMaxColumnsInGroupBy() throws java.sql.SQLException {
  2086.         return 64;
  2087.     }
  2088.     /**
  2089.      * What's the maximum number of columns allowed in an index?
  2090.      *
  2091.      * @return max columns
  2092.      *
  2093.      * @throws java.sql.SQLException DOCUMENT ME!
  2094.      */
  2095.     public int getMaxColumnsInIndex() throws java.sql.SQLException {
  2096.         return 16;
  2097.     }
  2098.     /**
  2099.      * What's the maximum number of columns in an "ORDER BY" clause?
  2100.      *
  2101.      * @return max columns
  2102.      *
  2103.      * @throws java.sql.SQLException DOCUMENT ME!
  2104.      */
  2105.     public int getMaxColumnsInOrderBy() throws java.sql.SQLException {
  2106.         return 64;
  2107.     }
  2108.     /**
  2109.      * What's the maximum number of columns in a "SELECT" list?
  2110.      *
  2111.      * @return max columns
  2112.      *
  2113.      * @throws java.sql.SQLException DOCUMENT ME!
  2114.      */
  2115.     public int getMaxColumnsInSelect() throws java.sql.SQLException {
  2116.         return 256;
  2117.     }
  2118.     /**
  2119.      * What's maximum number of columns in a table?
  2120.      *
  2121.      * @return max columns
  2122.      *
  2123.      * @throws java.sql.SQLException DOCUMENT ME!
  2124.      */
  2125.     public int getMaxColumnsInTable() throws java.sql.SQLException {
  2126.         return 512;
  2127.     }
  2128.     /**
  2129.      * How many active connections can we have at a time to this database?
  2130.      *
  2131.      * @return max connections
  2132.      *
  2133.      * @throws java.sql.SQLException DOCUMENT ME!
  2134.      */
  2135.     public int getMaxConnections() throws java.sql.SQLException {
  2136.         return 0;
  2137.     }
  2138.     /**
  2139.      * What's the maximum cursor name length?
  2140.      *
  2141.      * @return max cursor name length in bytes
  2142.      *
  2143.      * @throws java.sql.SQLException DOCUMENT ME!
  2144.      */
  2145.     public int getMaxCursorNameLength() throws java.sql.SQLException {
  2146.         return 64;
  2147.     }
  2148.     /**
  2149.      * What's the maximum length of an index (in bytes)?
  2150.      *
  2151.      * @return max index length in bytes
  2152.      *
  2153.      * @throws java.sql.SQLException DOCUMENT ME!
  2154.      */
  2155.     public int getMaxIndexLength() throws java.sql.SQLException {
  2156.         return 256;
  2157.     }
  2158.     /**
  2159.      * What's the maximum length of a procedure name?
  2160.      *
  2161.      * @return max name length in bytes
  2162.      *
  2163.      * @throws java.sql.SQLException DOCUMENT ME!
  2164.      */
  2165.     public int getMaxProcedureNameLength() throws java.sql.SQLException {
  2166.         return 0;
  2167.     }
  2168.     /**
  2169.      * What's the maximum length of a single row?
  2170.      *
  2171.      * @return max row size in bytes
  2172.      *
  2173.      * @throws java.sql.SQLException DOCUMENT ME!
  2174.      */
  2175.     public int getMaxRowSize() throws java.sql.SQLException {
  2176.         return Integer.MAX_VALUE - 8; // Max buffer size - HEADER
  2177.     }
  2178.     /**
  2179.      * What's the maximum length allowed for a schema name?
  2180.      *
  2181.      * @return max name length in bytes
  2182.      *
  2183.      * @throws java.sql.SQLException DOCUMENT ME!
  2184.      */
  2185.     public int getMaxSchemaNameLength() throws java.sql.SQLException {
  2186.         return 0;
  2187.     }
  2188.     /**
  2189.      * What's the maximum length of a SQL statement?
  2190.      *
  2191.      * @return max length in bytes
  2192.      *
  2193.      * @throws java.sql.SQLException DOCUMENT ME!
  2194.      */
  2195.     public int getMaxStatementLength() throws java.sql.SQLException {
  2196.         return MysqlIO.getMaxBuf() - 4; // Max buffer - header
  2197.     }
  2198.     /**
  2199.      * How many active statements can we have open at one time to this
  2200.      * database?
  2201.      *
  2202.      * @return the maximum
  2203.      *
  2204.      * @throws java.sql.SQLException DOCUMENT ME!
  2205.      */
  2206.     public int getMaxStatements() throws java.sql.SQLException {
  2207.         return 0;
  2208.     }
  2209.     /**
  2210.      * What's the maximum length of a table name?
  2211.      *
  2212.      * @return max name length in bytes
  2213.      *
  2214.      * @throws java.sql.SQLException DOCUMENT ME!
  2215.      */
  2216.     public int getMaxTableNameLength() throws java.sql.SQLException {
  2217.         return 64;
  2218.     }
  2219.     /**
  2220.      * What's the maximum number of tables in a SELECT?
  2221.      *
  2222.      * @return the maximum
  2223.      *
  2224.      * @throws java.sql.SQLException DOCUMENT ME!
  2225.      */
  2226.     public int getMaxTablesInSelect() throws java.sql.SQLException {
  2227.         return 256;
  2228.     }
  2229.     /**
  2230.      * What's the maximum length of a user name?
  2231.      *
  2232.      * @return max name length  in bytes
  2233.      *
  2234.      * @throws java.sql.SQLException DOCUMENT ME!
  2235.      */
  2236.     public int getMaxUserNameLength() throws java.sql.SQLException {
  2237.         return 16;
  2238.     }
  2239.     /**
  2240.      * Get a comma separated list of math functions.
  2241.      *
  2242.      * @return the list
  2243.      *
  2244.      * @throws java.sql.SQLException DOCUMENT ME!
  2245.      */
  2246.     public String getNumericFunctions() throws java.sql.SQLException {
  2247.         return "ABS,ACOS,ASIN,ATAN,ATAN2,BIT_COUNT,CEILING,COS,"
  2248.         + "COT,DEGREES,EXP,FLOOR,LOG,LOG10,MAX,MIN,MOD,PI,POW,"
  2249.         + "POWER,RADIANS,RAND,ROUND,SIN,SQRT,TAN,TRUNCATE";
  2250.     }
  2251.     /**
  2252.      * Get a description of a table's primary key columns.  They are ordered by
  2253.      * COLUMN_NAME.
  2254.      * 
  2255.      * <P>
  2256.      * Each column description has the following columns:
  2257.      * 
  2258.      * <OL>
  2259.      * <li>
  2260.      * <B>TABLE_CAT</B> String => table catalog (may be null)
  2261.      * </li>
  2262.      * <li>
  2263.      * <B>TABLE_SCHEM</B> String => table schema (may be null)
  2264.      * </li>
  2265.      * <li>
  2266.      * <B>TABLE_NAME</B> String => table name
  2267.      * </li>
  2268.      * <li>
  2269.      * <B>COLUMN_NAME</B> String => column name
  2270.      * </li>
  2271.      * <li>
  2272.      * <B>KEY_SEQ</B> short => sequence number within primary key
  2273.      * </li>
  2274.      * <li>
  2275.      * <B>PK_NAME</B> String => primary key name (may be null)
  2276.      * </li>
  2277.      * </ol>
  2278.      * </p>
  2279.      *
  2280.      * @param catalog a catalog name; "" retrieves those without a catalog
  2281.      * @param schema a schema name pattern; "" retrieves those without a schema
  2282.      * @param table a table name
  2283.      *
  2284.      * @return ResultSet each row is a primary key column description
  2285.      *
  2286.      * @throws java.sql.SQLException DOCUMENT ME!
  2287.      */
  2288.     public java.sql.ResultSet getPrimaryKeys(String catalog, String schema,
  2289.         String table) throws java.sql.SQLException {
  2290.         Field[] fields = new Field[6];
  2291.         fields[0] = new Field("", "TABLE_CAT", Types.CHAR, 255);
  2292.         fields[1] = new Field("", "TABLE_SCHEM", Types.CHAR, 0);
  2293.         fields[2] = new Field("", "TABLE_NAME", Types.CHAR, 255);
  2294.         fields[3] = new Field("", "COLUMN_NAME", Types.CHAR, 32);
  2295.         fields[4] = new Field("", "KEY_SEQ", Types.SMALLINT, 5);
  2296.         fields[5] = new Field("", "PK_NAME", Types.CHAR, 32);
  2297.         String dbSub = "";
  2298.         if (catalog != null) {
  2299.             if (!catalog.equals("")) {
  2300.                 dbSub = " FROM " + this.quotedId + catalog + this.quotedId;
  2301.             }
  2302.         } else {
  2303.             dbSub = " FROM " + this.quotedId + this.database + this.quotedId;
  2304.         }
  2305.         if (table == null) {
  2306.             throw new java.sql.SQLException("Table not specified.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
  2307.         }
  2308.         Statement stmt = null;
  2309.         ResultSet rs = null;
  2310.         try {
  2311.             stmt = this.conn.createStatement();
  2312.             
  2313. if (stmt.getMaxRows() != 0) {
  2314. stmt.setMaxRows(0);
  2315. }
  2316.             StringBuffer queryBuf = new StringBuffer("SHOW KEYS FROM ");
  2317.             queryBuf.append(this.quotedId);
  2318.             queryBuf.append(table);
  2319.             queryBuf.append(this.quotedId);
  2320.             queryBuf.append(dbSub);
  2321.             rs = stmt.executeQuery(queryBuf.toString());
  2322.             byte[] connectionCatalogAsBytes;
  2323.             if (catalog == null) {
  2324.                 connectionCatalogAsBytes = s2b(this.conn.getCatalog());
  2325.             } else {
  2326.                 connectionCatalogAsBytes = s2b(catalog);
  2327.             }
  2328.             ArrayList tuples = new ArrayList();
  2329.             TreeMap sortMap = new TreeMap();
  2330.             while (rs.next()) {
  2331.                 String keyType = rs.getString("Key_name");
  2332.                 if (keyType != null) {
  2333.                     if (keyType.equalsIgnoreCase("PRIMARY")
  2334.                             || keyType.equalsIgnoreCase("PRI")) {
  2335.                         byte[][] tuple = new byte[6][];
  2336.                         tuple[0] = connectionCatalogAsBytes;
  2337.                         tuple[1] = null;
  2338.                         tuple[2] = s2b(table);
  2339.                         String columnName = rs.getString("Column_name");
  2340.                         tuple[3] = s2b(columnName);
  2341.                         tuple[4] = s2b(rs.getString("Seq_in_index"));
  2342.                         tuple[5] = s2b(keyType);
  2343.                         sortMap.put(columnName, tuple);
  2344.                     }
  2345.                 }
  2346.             }
  2347.             // Now pull out in column name sorted order
  2348.             Iterator sortedIterator = sortMap.values().iterator();
  2349.             while (sortedIterator.hasNext()) {
  2350.                 tuples.add(sortedIterator.next());
  2351.             }
  2352.             return buildResultSet(fields, tuples);
  2353.         } finally {
  2354.             if (rs != null) {
  2355.                 try {
  2356.                     rs.close();
  2357.                 } catch (Exception ex) {
  2358.                     ;
  2359.                 }
  2360.                 rs = null;
  2361.             }
  2362.             if (stmt != null) {
  2363.                 try {
  2364.                     stmt.close();
  2365.                 } catch (Exception ex) {
  2366.                     ;
  2367.                 }
  2368.                 stmt = null;
  2369.             }
  2370.         }
  2371.     }
  2372.     /**
  2373.      * Get a description of a catalog's stored procedure parameters and result
  2374.      * columns.
  2375.      * 
  2376.      * <P>
  2377.      * Only descriptions matching the schema, procedure and parameter name
  2378.      * criteria are returned.  They are ordered by PROCEDURE_SCHEM and
  2379.      * PROCEDURE_NAME. Within this, the return value, if any, is first. Next
  2380.      * are the parameter descriptions in call order. The column descriptions
  2381.      * follow in column number order.
  2382.      * </p>
  2383.      * 
  2384.      * <P>
  2385.      * Each row in the ResultSet is a parameter desription or column
  2386.      * description with the following fields:
  2387.      * 
  2388.      * <OL>
  2389.      * <li>
  2390.      * <B>PROCEDURE_CAT</B> String => procedure catalog (may be null)
  2391.      * </li>
  2392.      * <li>
  2393.      * <B>PROCEDURE_SCHEM</B> String => procedure schema (may be null)
  2394.      * </li>
  2395.      * <li>
  2396.      * <B>PROCEDURE_NAME</B> String => procedure name
  2397.      * </li>
  2398.      * <li>
  2399.      * <B>COLUMN_NAME</B> String => column/parameter name
  2400.      * </li>
  2401.      * <li>
  2402.      * <B>COLUMN_TYPE</B> Short => kind of column/parameter:
  2403.      * 
  2404.      * <UL>
  2405.      * <li>
  2406.      * procedureColumnUnknown - nobody knows
  2407.      * </li>
  2408.      * <li>
  2409.      * procedureColumnIn - IN parameter
  2410.      * </li>
  2411.      * <li>
  2412.      * procedureColumnInOut - INOUT parameter
  2413.      * </li>
  2414.      * <li>
  2415.      * procedureColumnOut - OUT parameter
  2416.      * </li>
  2417.      * <li>
  2418.      * procedureColumnReturn - procedure return value
  2419.      * </li>
  2420.      * <li>
  2421.      * procedureColumnResult - result column in ResultSet
  2422.      * </li>
  2423.      * </ul>
  2424.      * 
  2425.      * </li>
  2426.      * <li>
  2427.      * <B>DATA_TYPE</B> short => SQL type from java.sql.Types
  2428.      * </li>
  2429.      * <li>
  2430.      * <B>TYPE_NAME</B> String => SQL type name
  2431.      * </li>
  2432.      * <li>
  2433.      * <B>PRECISION</B> int => precision
  2434.      * </li>
  2435.      * <li>
  2436.      * <B>LENGTH</B> int => length in bytes of data
  2437.      * </li>
  2438.      * <li>
  2439.      * <B>SCALE</B> short => scale
  2440.      * </li>
  2441.      * <li>
  2442.      * <B>RADIX</B> short => radix
  2443.      * </li>
  2444.      * <li>
  2445.      * <B>NULLABLE</B> short => can it contain NULL?
  2446.      * 
  2447.      * <UL>
  2448.      * <li>
  2449.      * procedureNoNulls - does not allow NULL values
  2450.      * </li>
  2451.      * <li>
  2452.      * procedureNullable - allows NULL values
  2453.      * </li>
  2454.      * <li>
  2455.      * procedureNullableUnknown - nullability unknown
  2456.      * </li>
  2457.      * </ul>
  2458.      * 
  2459.      * </li>
  2460.      * <li>
  2461.      * <B>REMARKS</B> String => comment describing parameter/column
  2462.      * </li>
  2463.      * </ol>
  2464.      * </p>
  2465.      * 
  2466.      * <P>
  2467.      * <B>Note:</B> Some databases may not return the column descriptions for a
  2468.      * procedure. Additional columns beyond REMARKS can be defined by the
  2469.      * database.
  2470.      * </p>
  2471.      *
  2472.      * @param catalog a catalog name; "" retrieves those without a catalog
  2473.      * @param schemaPattern a schema name pattern; "" retrieves those without a
  2474.      *        schema
  2475.      * @param procedureNamePattern a procedure name pattern
  2476.      * @param columnNamePattern a column name pattern
  2477.      *
  2478.      * @return ResultSet each row is a stored procedure parameter or column
  2479.      *         description
  2480.      *
  2481.      * @throws java.sql.SQLException if a database access error occurs
  2482.      *
  2483.      * @see #getSearchStringEscape
  2484.      */
  2485.     public java.sql.ResultSet getProcedureColumns(String catalog,
  2486.         String schemaPattern, String procedureNamePattern,
  2487.         String columnNamePattern) throws java.sql.SQLException {
  2488.         Field[] fields = new Field[14];
  2489.         fields[0] = new Field("", "TABLE_CAT", Types.CHAR, 0);
  2490.         fields[1] = new Field("", "PROCEDURE_CAT", Types.CHAR, 0);
  2491.         fields[2] = new Field("", "PROCEDURE_SCHEM", Types.CHAR, 0);
  2492.         fields[3] = new Field("", "PROCEDURE_NAME", Types.CHAR, 0);
  2493.         fields[4] = new Field("", "COLUMN_NAME", Types.CHAR, 0);
  2494.         fields[5] = new Field("", "COLUMN_TYPE", Types.CHAR, 0);
  2495.         fields[6] = new Field("", "DATA_TYPE", Types.SMALLINT, 0);
  2496.         fields[7] = new Field("", "TYPE_NAME", Types.CHAR, 0);
  2497.         fields[8] = new Field("", "PRECISION", Types.INTEGER, 0);
  2498.         fields[9] = new Field("", "LENGTH", Types.INTEGER, 0);
  2499.         fields[10] = new Field("", "SCALE", Types.SMALLINT, 0);
  2500.         fields[11] = new Field("", "RADIX", Types.SMALLINT, 0);
  2501.         fields[12] = new Field("", "NULLABLE", Types.SMALLINT, 0);
  2502.         fields[13] = new Field("", "REMARKS", Types.CHAR, 0);
  2503.         return buildResultSet(fields, new ArrayList());
  2504.     }
  2505.     /**
  2506.      * What's the database vendor's preferred term for "procedure"?
  2507.      *
  2508.      * @return the vendor term
  2509.      *
  2510.      * @throws java.sql.SQLException DOCUMENT ME!
  2511.      */
  2512.     public String getProcedureTerm() throws java.sql.SQLException {
  2513.         return "";
  2514.     }
  2515.     /**
  2516.      * Get a description of stored procedures available in a catalog.
  2517.      * 
  2518.      * <P>
  2519.      * Only procedure descriptions matching the schema and procedure name
  2520.      * criteria are returned.  They are ordered by PROCEDURE_SCHEM, and
  2521.      * PROCEDURE_NAME.
  2522.      * </p>
  2523.      * 
  2524.      * <P>
  2525.      * Each procedure description has the the following columns:
  2526.      * 
  2527.      * <OL>
  2528.      * <li>
  2529.      * <B>PROCEDURE_CAT</B> String => procedure catalog (may be null)
  2530.      * </li>
  2531.      * <li>
  2532.      * <B>PROCEDURE_SCHEM</B> String => procedure schema (may be null)
  2533.      * </li>
  2534.      * <li>
  2535.      * <B>PROCEDURE_NAME</B> String => procedure name
  2536.      * </li>
  2537.      * <li>
  2538.      * reserved for future use
  2539.      * </li>
  2540.      * <li>
  2541.      * reserved for future use
  2542.      * </li>
  2543.      * <li>
  2544.      * reserved for future use
  2545.      * </li>
  2546.      * <li>
  2547.      * <B>REMARKS</B> String => explanatory comment on the procedure
  2548.      * </li>
  2549.      * <li>
  2550.      * <B>PROCEDURE_TYPE</B> short => kind of procedure:
  2551.      * 
  2552.      * <UL>
  2553.      * <li>
  2554.      * procedureResultUnknown - May return a result
  2555.      * </li>
  2556.      * <li>
  2557.      * procedureNoResult - Does not return a result
  2558.      * </li>
  2559.      * <li>
  2560.      * procedureReturnsResult - Returns a result
  2561.      * </li>
  2562.      * </ul>
  2563.      * 
  2564.      * </li>
  2565.      * </ol>
  2566.      * </p>
  2567.      *
  2568.      * @param catalog a catalog name; "" retrieves those without a catalog
  2569.      * @param schemaPattern a schema name pattern; "" retrieves those without a
  2570.      *        schema
  2571.      * @param procedureNamePattern a procedure name pattern
  2572.      *
  2573.      * @return ResultSet each row is a procedure description
  2574.      *
  2575.      * @throws java.sql.SQLException if a database access error occurs
  2576.      *
  2577.      * @see #getSearchStringEscape
  2578.      */
  2579.     public java.sql.ResultSet getProcedures(String catalog,
  2580.         String schemaPattern, String procedureNamePattern)
  2581.         throws java.sql.SQLException {
  2582.         Field[] fields = new Field[8];
  2583.         fields[0] = new Field("", "PROCEDURE_CAT", Types.CHAR, 0);
  2584.         fields[1] = new Field("", "PROCEDURE_SCHEM", Types.CHAR, 0);
  2585.         fields[2] = new Field("", "PROCEDURE_NAME", Types.CHAR, 0);
  2586.         fields[3] = new Field("", "resTABLE_CAT", Types.CHAR, 0);
  2587.         fields[4] = new Field("", "resTABLE_CAT", Types.CHAR, 0);
  2588.         fields[5] = new Field("", "resTABLE_CAT", Types.CHAR, 0);
  2589.         fields[6] = new Field("", "REMARKS", Types.CHAR, 0);
  2590.         fields[7] = new Field("", "PROCEDURE_TYPE", Types.SMALLINT, 0);
  2591.         return buildResultSet(fields, new ArrayList());
  2592.     }
  2593.     /**
  2594.      * Is the database in read-only mode?
  2595.      *
  2596.      * @return true if so
  2597.      *
  2598.      * @throws java.sql.SQLException DOCUMENT ME!
  2599.      */
  2600.     public boolean isReadOnly() throws java.sql.SQLException {
  2601.         return false;
  2602.     }
  2603.     /**
  2604.      * @see DatabaseMetaData#getResultSetHoldability()
  2605.      */
  2606.     public int getResultSetHoldability() throws SQLException {
  2607.         return ResultSet.CLOSE_CURSORS_AT_COMMIT;
  2608.     }
  2609.     /**
  2610.      * Get a comma separated list of all a database's SQL keywords that are NOT
  2611.      * also SQL92 keywords.
  2612.      *
  2613.      * @return the list
  2614.      *
  2615.      * @throws java.sql.SQLException DOCUMENT ME!
  2616.      */
  2617.     public String getSQLKeywords() throws java.sql.SQLException {
  2618.         return "AUTO_INCREMENT,BINARY,BLOB,ENUM,INFILE,LOAD,MEDIUMINT,OPTION,OUTFILE,REPLACE,SET,TEXT,UNSIGNED,ZEROFILL";
  2619.     }
  2620.     /**
  2621.      * @see DatabaseMetaData#getSQLStateType()
  2622.      */
  2623.     public int getSQLStateType() throws SQLException {
  2624.         return 0;
  2625.     }
  2626.     /**
  2627.      * What's the database vendor's preferred term for "schema"?
  2628.      *
  2629.      * @return the vendor term
  2630.      *
  2631.      * @throws java.sql.SQLException DOCUMENT ME!
  2632.      */
  2633.     public String getSchemaTerm() throws java.sql.SQLException {
  2634.         return "";
  2635.     }
  2636.     /**
  2637.      * Get the schema names available in this database.  The results are
  2638.      * ordered by schema name.
  2639.      * 
  2640.      * <P>
  2641.      * The schema column is:
  2642.      * 
  2643.      * <OL>
  2644.      * <li>
  2645.      * <B>TABLE_SCHEM</B> String => schema name
  2646.      * </li>
  2647.      * </ol>
  2648.      * </p>
  2649.      *
  2650.      * @return ResultSet each row has a single String column that is a schema
  2651.      *         name
  2652.      *
  2653.      * @throws java.sql.SQLException DOCUMENT ME!
  2654.      */
  2655.     public java.sql.ResultSet getSchemas() throws java.sql.SQLException {
  2656.         Field[] fields = new Field[1];
  2657.         fields[0] = new Field("", "TABLE_SCHEM", java.sql.Types.CHAR, 0);
  2658.         ArrayList tuples = new ArrayList();
  2659.         java.sql.ResultSet results = buildResultSet(fields, tuples);
  2660.         return results;
  2661.     }
  2662.     /**
  2663.      * This is the string that can be used to escape '_' or '%' in the string
  2664.      * pattern style catalog search parameters.
  2665.      * 
  2666.      * <P>
  2667.      * The '_' character represents any single character.
  2668.      * </p>
  2669.      * 
  2670.      * <P>
  2671.      * The '%' character represents any sequence of zero or more characters.
  2672.      * </p>
  2673.      *
  2674.      * @return the string used to escape wildcard characters
  2675.      *
  2676.      * @throws java.sql.SQLException DOCUMENT ME!
  2677.      */
  2678.     public String getSearchStringEscape() throws java.sql.SQLException {
  2679.         return "\";
  2680.     }
  2681.     /**
  2682.      * Get a comma separated list of string functions.
  2683.      *
  2684.      * @return the list
  2685.      *
  2686.      * @throws java.sql.SQLException DOCUMENT ME!
  2687.      */
  2688.     public String getStringFunctions() throws java.sql.SQLException {
  2689.         return "ASCII,BIN,BIT_LENGTH,CHAR,CHARACTER_LENGTH,CHAR_LENGTH,CONCAT,"
  2690.         + "CONCAT_WS,CONV,ELT,EXPORT_SET,FIELD,FIND_IN_SET,HEX,INSERT,"
  2691.         + "INSTR,LCASE,LEFT,LENGTH,LOAD_FILE,LOCATE,LOCATE,LOWER,LPAD,"
  2692.         + "LTRIM,MAKE_SET,MATCH,MID,OCT,OCTET_LENGTH,ORD,POSITION,"
  2693.         + "QUOTE,REPEAT,REPLACE,REVERSE,RIGHT,RPAD,RTRIM,SOUNDEX,"
  2694.         + "SPACE,STRCMP,SUBSTRING,SUBSTRING,SUBSTRING,SUBSTRING,"
  2695.         + "SUBSTRING_INDEX,TRIM,UCASE,UPPER";
  2696.     }
  2697.     /**
  2698.      * @see DatabaseMetaData#getSuperTables(String, String, String)
  2699.      */
  2700.     public java.sql.ResultSet getSuperTables(String arg0, String arg1,
  2701.         String arg2) throws SQLException {
  2702.         Field[] fields = new Field[4];
  2703.         fields[0] = new Field("", "TABLE_CAT", Types.CHAR, 32);
  2704.         fields[1] = new Field("", "TABLE_SCHEM", Types.CHAR, 32);
  2705.         fields[2] = new Field("", "TABLE_NAME", Types.CHAR, 32);
  2706.         fields[3] = new Field("", "SUPERTABLE_NAME", Types.CHAR, 32);
  2707.         return buildResultSet(fields, new ArrayList());
  2708.     }
  2709.     /**
  2710.      * @see DatabaseMetaData#getSuperTypes(String, String, String)
  2711.      */
  2712.     public java.sql.ResultSet getSuperTypes(String arg0, String arg1,
  2713.         String arg2) throws SQLException {
  2714.         Field[] fields = new Field[6];
  2715.         fields[0] = new Field("", "TABLE_CAT", Types.CHAR, 32);
  2716.         fields[1] = new Field("", "TABLE_SCHEM", Types.CHAR, 32);
  2717.         fields[2] = new Field("", "TYPE_NAME", Types.CHAR, 32);
  2718.         fields[3] = new Field("", "SUPERTYPE_CAT", Types.CHAR, 32);
  2719.         fields[4] = new Field("", "SUPERTYPE_SCHEM", Types.CHAR, 32);
  2720.         fields[5] = new Field("", "SUPERTYPE_NAME", Types.CHAR, 32);
  2721.         return buildResultSet(fields, new ArrayList());
  2722.     }
  2723.     /**
  2724.      * Get a comma separated list of system functions.
  2725.      *
  2726.      * @return the list
  2727.      *
  2728.      * @throws java.sql.SQLException DOCUMENT ME!
  2729.      */
  2730.     public String getSystemFunctions() throws java.sql.SQLException {
  2731.         return "DATABASE,USER,SYSTEM_USER,SESSION_USER,PASSWORD,ENCRYPT,LAST_INSERT_ID,VERSION";
  2732.     }
  2733.     /**
  2734.      * Get a description of the access rights for each table available in a
  2735.      * catalog.
  2736.      * 
  2737.      * <P>
  2738.      * Only privileges matching the schema and table name criteria are
  2739.      * returned.  They are ordered by TABLE_SCHEM, TABLE_NAME, and PRIVILEGE.
  2740.      * </p>
  2741.      * 
  2742.      * <P>
  2743.      * Each privilige description has the following columns:
  2744.      * 
  2745.      * <OL>
  2746.      * <li>
  2747.      * <B>TABLE_CAT</B> String => table catalog (may be null)
  2748.      * </li>
  2749.      * <li>
  2750.      * <B>TABLE_SCHEM</B> String => table schema (may be null)
  2751.      * </li>
  2752.      * <li>
  2753.      * <B>TABLE_NAME</B> String => table name
  2754.      * </li>
  2755.      * <li>
  2756.      * <B>COLUMN_NAME</B> String => column name
  2757.      * </li>
  2758.      * <li>
  2759.      * <B>GRANTOR</B> => grantor of access (may be null)
  2760.      * </li>
  2761.      * <li>
  2762.      * <B>GRANTEE</B> String => grantee of access
  2763.      * </li>
  2764.      * <li>
  2765.      * <B>PRIVILEGE</B> String => name of access (SELECT, INSERT, UPDATE,
  2766.      * REFRENCES, ...)
  2767.      * </li>
  2768.      * <li>
  2769.      * <B>IS_GRANTABLE</B> String => "YES" if grantee is permitted to grant to
  2770.      * others; "NO" if not; null if unknown
  2771.      * </li>
  2772.      * </ol>
  2773.      * </p>
  2774.      *
  2775.      * @param catalog a catalog name; "" retrieves those without a catalog
  2776.      * @param schemaPattern a schema name pattern; "" retrieves those without a
  2777.      *        schema
  2778.      * @param tableNamePattern a table name pattern
  2779.      *
  2780.      * @return ResultSet each row is a table privilege description
  2781.      *
  2782.      * @throws java.sql.SQLException if a database access error occurs
  2783.      *
  2784.      * @see #getSearchStringEscape
  2785.      */
  2786.     public java.sql.ResultSet getTablePrivileges(String catalog,
  2787.         String schemaPattern, String tableNamePattern)
  2788.         throws java.sql.SQLException {
  2789.         Field[] fields = new Field[7];
  2790.         fields[0] = new Field("", "TABLE_CAT", Types.CHAR, 64);
  2791.         fields[1] = new Field("", "TABLE_SCHEM", Types.CHAR, 1);
  2792.         fields[2] = new Field("", "TABLE_NAME", Types.CHAR, 64);
  2793.         fields[3] = new Field("", "GRANTOR", Types.CHAR, 77);
  2794.         fields[4] = new Field("", "GRANTEE", Types.CHAR, 77);
  2795.         fields[5] = new Field("", "PRIVILEGE", Types.CHAR, 64);
  2796.         fields[6] = new Field("", "IS_GRANTABLE", Types.CHAR, 3);
  2797.         StringBuffer grantQuery = new StringBuffer(
  2798.                 "SELECT host,db,table_name,grantor,user,table_priv from mysql.tables_priv ");
  2799.         grantQuery.append(" WHERE ");
  2800.         if ((catalog != null) && (catalog.length() != 0)) {
  2801.             grantQuery.append(" db='");
  2802.             grantQuery.append(catalog);
  2803.             grantQuery.append("' AND ");
  2804.         }
  2805.         grantQuery.append("table_name like '");
  2806.         grantQuery.append(tableNamePattern);
  2807.         grantQuery.append("'");
  2808.         ResultSet results = null;
  2809.         ArrayList grantRows = new ArrayList();
  2810.         Statement stmt = null;
  2811.         try {
  2812.             stmt = this.conn.createStatement();
  2813.             
  2814. if (stmt.getMaxRows() != 0) {
  2815. stmt.setMaxRows(0);
  2816. }
  2817.             results = stmt.executeQuery(grantQuery.toString());
  2818.             while (results.next()) {
  2819.                 String host = results.getString(1);
  2820.                 String database = results.getString(2);
  2821.                 String table = results.getString(3);
  2822.                 String grantor = results.getString(4);
  2823.                 String user = results.getString(5);
  2824.                 if ((user == null) || (user.length() == 0)) {
  2825.                     user = "%";
  2826.                 }
  2827.                 StringBuffer fullUser = new StringBuffer(user);
  2828.                 if ((host != null) && this.conn.useHostsInPrivileges()) {
  2829.                     fullUser.append("@");
  2830.                     fullUser.append(host);
  2831.                 }
  2832.                 String allPrivileges = results.getString(6);
  2833.                 if (allPrivileges != null) {
  2834.                     allPrivileges = allPrivileges.toUpperCase();
  2835.                     StringTokenizer st = new StringTokenizer(allPrivileges, ",");
  2836.                     while (st.hasMoreTokens()) {
  2837.                         String privilege = st.nextToken().trim();
  2838.                         // Loop through every column in the table
  2839.                         java.sql.ResultSet columnResults = null;
  2840.                         try {
  2841.                             columnResults = getColumns(catalog, schemaPattern,
  2842.                                     table, "%");
  2843.                             while (columnResults.next()) {
  2844.                                 byte[][] tuple = new byte[8][];
  2845.                                 tuple[0] = s2b(database);
  2846.                                 tuple[1] = null;
  2847.                                 tuple[2] = s2b(table);
  2848.                                 if (grantor != null) {
  2849.                                     tuple[3] = s2b(grantor);
  2850.                                 } else {
  2851.                                     tuple[3] = null;
  2852.                                 }
  2853.                                 tuple[4] = s2b(fullUser.toString());
  2854.                                 tuple[5] = s2b(privilege);
  2855.                                 tuple[6] = null;
  2856.                                 grantRows.add(tuple);
  2857.                             }
  2858.                         } finally {
  2859.                             if (columnResults != null) {
  2860.                                 try {
  2861.                                     columnResults.close();
  2862.                                 } catch (Exception ex) {
  2863.                                     ;
  2864.                                 }
  2865.                             }
  2866.                         }
  2867.                     }
  2868.                 }
  2869.             }
  2870.         } finally {
  2871.             if (results != null) {
  2872.                 try {
  2873.                     results.close();
  2874.                 } catch (Exception ex) {
  2875.                     ;
  2876.                 }
  2877.                 results = null;
  2878.             }
  2879.             if (stmt != null) {
  2880.                 try {
  2881.                     stmt.close();
  2882.                 } catch (Exception ex) {
  2883.                     ;
  2884.                 }
  2885.                 stmt = null;
  2886.             }
  2887.         }
  2888.         return buildResultSet(fields, grantRows);
  2889.     }
  2890.     /**
  2891.      * Get the table types available in this database.  The results are ordered
  2892.      * by table type.
  2893.      * 
  2894.      * <P>
  2895.      * The table type is:
  2896.      * 
  2897.      * <OL>
  2898.      * <li>
  2899.      * <B>TABLE_TYPE</B> String => table type.  Typical types are "TABLE",
  2900.      * "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", "LOCAL TEMPORARY", "ALIAS",
  2901.      * "SYNONYM".
  2902.      * </li>
  2903.      * </ol>
  2904.      * </p>
  2905.      *
  2906.      * @return ResultSet each row has a single String column that is a table
  2907.      *         type
  2908.      *
  2909.      * @throws java.sql.SQLException DOCUMENT ME!
  2910.      */
  2911.     public java.sql.ResultSet getTableTypes() throws java.sql.SQLException {
  2912.         ArrayList tuples = new ArrayList();
  2913.         Field[] fields = new Field[1];
  2914.         fields[0] = new Field("", "TABLE_TYPE", Types.VARCHAR, 5);
  2915.         byte[][] tableTypeRow = new byte[1][];
  2916.         tableTypeRow[0] = TABLE_AS_BYTES;
  2917.         tuples.add(tableTypeRow);
  2918.         byte[][] tempTypeRow = new byte[1][];
  2919.         tempTypeRow[0] = s2b("LOCAL TEMPORARY");
  2920.         tuples.add(tempTypeRow);
  2921.         return buildResultSet(fields, tuples);
  2922.     }
  2923.     /**
  2924.      * Get a description of tables available in a catalog.
  2925.      * 
  2926.      * <P>
  2927.      * Only table descriptions matching the catalog, schema, table name and
  2928.      * type criteria are returned.  They are ordered by TABLE_TYPE,
  2929.      * TABLE_SCHEM and TABLE_NAME.
  2930.      * </p>
  2931.      * 
  2932.      * <P>
  2933.      * Each table description has the following columns:
  2934.      * 
  2935.      * <OL>
  2936.      * <li>
  2937.      * <B>TABLE_CAT</B> String => table catalog (may be null)
  2938.      * </li>
  2939.      * <li>
  2940.      * <B>TABLE_SCHEM</B> String => table schema (may be null)
  2941.      * </li>
  2942.      * <li>
  2943.      * <B>TABLE_NAME</B> String => table name
  2944.      * </li>
  2945.      * <li>
  2946.      * <B>TABLE_TYPE</B> String => table type.  Typical types are "TABLE",
  2947.      * "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", "LOCAL TEMPORARY", "ALIAS",
  2948.      * "SYNONYM".
  2949.      * </li>
  2950.      * <li>
  2951.      * <B>REMARKS</B> String => explanatory comment on the table
  2952.      * </li>
  2953.      * </ol>
  2954.      * </p>
  2955.      * 
  2956.      * <P>
  2957.      * <B>Note:</B> Some databases may not return information for all tables.
  2958.      * </p>
  2959.      *
  2960.      * @param catalog a catalog name; "" retrieves those without a catalog
  2961.      * @param schemaPattern a schema name pattern; "" retrieves those without a
  2962.      *        schema
  2963.      * @param tableNamePattern a table name pattern
  2964.      * @param types a list of table types to include; null returns all types
  2965.      *
  2966.      * @return ResultSet each row is a table description
  2967.      *
  2968.      * @throws java.sql.SQLException DOCUMENT ME!
  2969.      *
  2970.      * @see #getSearchStringEscape
  2971.      */
  2972.     public java.sql.ResultSet getTables(String catalog, String schemaPattern,
  2973.         String tableNamePattern, String[] types) throws java.sql.SQLException {
  2974.         String databasePart = "";
  2975.         if (catalog != null) {
  2976.             if (!catalog.equals("")) {
  2977.                 databasePart = " FROM " + this.quotedId + catalog
  2978.                     + this.quotedId;
  2979.             }
  2980.         } else {
  2981.             databasePart = " FROM " + this.quotedId + this.database
  2982.                 + this.quotedId;
  2983.         }
  2984.         if (tableNamePattern == null) {
  2985.             tableNamePattern = "%";
  2986.         }
  2987.         Statement stmt = null;
  2988.         ResultSet results = null;
  2989.         try {
  2990.             stmt = this.conn.createStatement();
  2991.             
  2992. if (stmt.getMaxRows() != 0) {
  2993. stmt.setMaxRows(0);
  2994. }
  2995.             results = stmt.executeQuery("SHOW TABLES " + databasePart
  2996.                     + " LIKE '" + tableNamePattern + "'");
  2997.             Field[] fields = new Field[5];
  2998.             fields[0] = new Field("", "TABLE_CAT", java.sql.Types.VARCHAR,
  2999.                     (catalog == null) ? 0 : catalog.length());
  3000.             fields[1] = new Field("", "TABLE_SCHEM", java.sql.Types.VARCHAR, 0);
  3001.             fields[2] = new Field("", "TABLE_NAME", java.sql.Types.VARCHAR, 255);
  3002.             fields[3] = new Field("", "TABLE_TYPE", java.sql.Types.VARCHAR, 5);
  3003.             fields[4] = new Field("", "REMARKS", java.sql.Types.VARCHAR, 0);
  3004.             ArrayList tuples = new ArrayList();
  3005.             byte[][] row = null;
  3006.             byte[] connectionCatalogAsBytes = null;
  3007.             if (catalog == null) {
  3008.                 connectionCatalogAsBytes = s2b(this.conn.getCatalog());
  3009.             } else {
  3010.                 connectionCatalogAsBytes = s2b(catalog);
  3011.             }
  3012.             while (results.next()) {
  3013.                 row = new byte[5][];
  3014.                 row[0] = connectionCatalogAsBytes;
  3015.                 row[1] = null;
  3016.                 row[2] = results.getBytes(1);
  3017.                 row[3] = TABLE_AS_BYTES;
  3018.                 row[4] = new byte[0];
  3019.                 tuples.add(row);
  3020.             }
  3021.             java.sql.ResultSet tables = buildResultSet(fields, tuples);
  3022.             return tables;
  3023.         } finally {
  3024.             if (results != null) {
  3025.                 try {
  3026.                     results.close();
  3027.                 } catch (Exception ex) {
  3028.                     ;
  3029.                 }
  3030.                 results = null;
  3031.             }
  3032.             if (stmt != null) {
  3033.                 try {
  3034.                     stmt.close();
  3035.                 } catch (Exception ex) {
  3036.                     ;
  3037.                 }
  3038.                 stmt = null;
  3039.             }
  3040.         }