KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > mysql > jdbc > DatabaseMetaData


1 /*
2    Copyright (C) 2002 MySQL AB
3
4       This program is free software; you can redistribute it and/or modify
5       it under the terms of the GNU General Public License as published by
6       the Free Software Foundation; either version 2 of the License, or
7       (at your option) any later version.
8
9       This program is distributed in the hope that it will be useful,
10       but WITHOUT ANY WARRANTY; without even the implied warranty of
11       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12       GNU General Public License for more details.
13
14       You should have received a copy of the GNU General Public License
15       along with this program; if not, write to the Free Software
16       Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
18  */

19 package com.mysql.jdbc;
20
21 import java.sql.ResultSet JavaDoc;
22 import java.sql.SQLException JavaDoc;
23 import java.sql.Statement JavaDoc;
24 import java.sql.Types JavaDoc;
25
26 import java.util.ArrayList JavaDoc;
27 import java.util.Iterator JavaDoc;
28 import java.util.List JavaDoc;
29 import java.util.StringTokenizer JavaDoc;
30 import java.util.TreeMap JavaDoc;
31
32
33 /**
34  * JDBC Interface to Mysql functions
35  *
36  * <p>
37  * This class provides information about the database as a whole.
38  * </p>
39  *
40  * <p>
41  * Many of the methods here return lists of information in ResultSets. You can
42  * use the normal ResultSet methods such as getString and getInt to retrieve
43  * the data from these ResultSets. If a given form of metadata is not
44  * available, these methods show throw a java.sql.SQLException.
45  * </p>
46  *
47  * <p>
48  * Some of these methods take arguments that are String patterns. These
49  * methods all have names such as fooPattern. Within a pattern String "%"
50  * means match any substring of 0 or more characters and "_" means match any
51  * one character.
52  * </p>
53  *
54  * @author Mark Matthews
55  * @version $Id: DatabaseMetaData.java,v 1.27.2.34 2004/02/04 02:47:36 mmatthew Exp $
56  */

57 public class DatabaseMetaData implements java.sql.DatabaseMetaData JavaDoc {
58     private static final byte[] TABLE_AS_BYTES = "TABLE".getBytes();
59
60     /** The table type for generic tables that support foreign keys. */
61     private static final String JavaDoc SUPPORTS_FK = "SUPPORTS_FK";
62
63     //
64
// Column indexes used by all DBMD foreign key
65
// ResultSets
66
//
67
private static final int PKTABLE_CAT = 0;
68     private static final int PKTABLE_SCHEM = 1;
69     private static final int PKTABLE_NAME = 2;
70     private static final int PKCOLUMN_NAME = 3;
71     private static final int FKTABLE_CAT = 4;
72     private static final int FKTABLE_SCHEM = 5;
73     private static final int FKTABLE_NAME = 6;
74     private static final int FKCOLUMN_NAME = 7;
75     private static final int KEY_SEQ = 8;
76     private static final int UPDATE_RULE = 9;
77     private static final int DELETE_RULE = 10;
78     private static final int FK_NAME = 11;
79     private static final int PK_NAME = 12;
80     private static final int DEFERRABILITY = 13;
81
82     /** The connection to the database */
83     private Connection conn;
84
85     /** The 'current' database name being used */
86     private String JavaDoc database = null;
87
88     /** What character to use when quoting identifiers */
89     private String JavaDoc quotedId = null;
90
91     /**
92      * Creates a new DatabaseMetaData object.
93      *
94      * @param conn DOCUMENT ME!
95      * @param database DOCUMENT ME!
96      */

97     public DatabaseMetaData(Connection conn, String JavaDoc database) {
98         this.conn = conn;
99         this.database = database;
100
101         try {
102             this.quotedId = this.conn.supportsQuotedIdentifiers()
103                 ? getIdentifierQuoteString() : "";
104         } catch (SQLException JavaDoc sqlEx) {
105             // Forced by API, never thrown from getIdentifierQuoteString() in this
106
// implementation.
107
AssertionFailedException.shouldNotHappen(sqlEx);
108         }
109     }
110
111     /**
112      * @see DatabaseMetaData#getAttributes(String, String, String, String)
113      */

114     public java.sql.ResultSet JavaDoc getAttributes(String JavaDoc arg0, String JavaDoc arg1,
115         String JavaDoc arg2, String JavaDoc arg3) throws SQLException JavaDoc {
116         Field[] fields = new Field[21];
117         fields[0] = new Field("", "TYPE_CAT", Types.CHAR, 32);
118         fields[1] = new Field("", "TYPE_SCHEM", Types.CHAR, 32);
119         fields[2] = new Field("", "TYPE_NAME", Types.CHAR, 32);
120         fields[3] = new Field("", "ATTR_NAME", Types.CHAR, 32);
121         fields[4] = new Field("", "DATA_TYPE", Types.SMALLINT, 32);
122         fields[5] = new Field("", "ATTR_TYPE_NAME", Types.CHAR, 32);
123         fields[6] = new Field("", "ATTR_SIZE", Types.INTEGER, 32);
124         fields[7] = new Field("", "DECIMAL_DIGITS", Types.INTEGER, 32);
125         fields[8] = new Field("", "NUM_PREC_RADIX", Types.INTEGER, 32);
126         fields[9] = new Field("", "NULLABLE ", Types.INTEGER, 32);
127         fields[10] = new Field("", "REMARKS", Types.CHAR, 32);
128         fields[11] = new Field("", "ATTR_DEF", Types.CHAR, 32);
129         fields[12] = new Field("", "SQL_DATA_TYPE", Types.INTEGER, 32);
130         fields[13] = new Field("", "SQL_DATETIME_SUB", Types.INTEGER, 32);
131         fields[14] = new Field("", "CHAR_OCTET_LENGTH", Types.INTEGER, 32);
132         fields[15] = new Field("", "ORDINAL_POSITION", Types.INTEGER, 32);
133         fields[16] = new Field("", "IS_NULLABLE", Types.CHAR, 32);
134         fields[17] = new Field("", "SCOPE_CATALOG", Types.CHAR, 32);
135         fields[18] = new Field("", "SCOPE_SCHEMA", Types.CHAR, 32);
136         fields[19] = new Field("", "SCOPE_TABLE", Types.CHAR, 32);
137         fields[20] = new Field("", "SOURCE_DATA_TYPE", Types.SMALLINT, 32);
138
139         return buildResultSet(fields, new ArrayList JavaDoc());
140     }
141
142     /**
143      * Get a description of a table's optimal set of columns that uniquely
144      * identifies a row. They are ordered by SCOPE.
145      *
146      * <P>
147      * Each column description has the following columns:
148      *
149      * <OL>
150      * <li>
151      * <B>SCOPE</B> short => actual scope of result
152      *
153      * <UL>
154      * <li>
155      * bestRowTemporary - very temporary, while using row
156      * </li>
157      * <li>
158      * bestRowTransaction - valid for remainder of current transaction
159      * </li>
160      * <li>
161      * bestRowSession - valid for remainder of current session
162      * </li>
163      * </ul>
164      *
165      * </li>
166      * <li>
167      * <B>COLUMN_NAME</B> String => column name
168      * </li>
169      * <li>
170      * <B>DATA_TYPE</B> short => SQL data type from java.sql.Types
171      * </li>
172      * <li>
173      * <B>TYPE_NAME</B> String => Data source dependent type name
174      * </li>
175      * <li>
176      * <B>COLUMN_SIZE</B> int => precision
177      * </li>
178      * <li>
179      * <B>BUFFER_LENGTH</B> int => not used
180      * </li>
181      * <li>
182      * <B>DECIMAL_DIGITS</B> short => scale
183      * </li>
184      * <li>
185      * <B>PSEUDO_COLUMN</B> short => is this a pseudo column like an Oracle
186      * ROWID
187      *
188      * <UL>
189      * <li>
190      * bestRowUnknown - may or may not be pseudo column
191      * </li>
192      * <li>
193      * bestRowNotPseudo - is NOT a pseudo column
194      * </li>
195      * <li>
196      * bestRowPseudo - is a pseudo column
197      * </li>
198      * </ul>
199      *
200      * </li>
201      * </ol>
202      * </p>
203      *
204      * @param catalog a catalog name; "" retrieves those without a catalog
205      * @param schema a schema name; "" retrieves those without a schema
206      * @param table a table name
207      * @param scope the scope of interest; use same values as SCOPE
208      * @param nullable include columns that are nullable?
209      *
210      * @return ResultSet each row is a column description
211      *
212      * @throws java.sql.SQLException DOCUMENT ME!
213      */

214     public java.sql.ResultSet JavaDoc getBestRowIdentifier(String JavaDoc catalog,
215         String JavaDoc schema, String JavaDoc table, int scope, boolean nullable)
216         throws java.sql.SQLException JavaDoc {
217         Field[] fields = new Field[8];
218         fields[0] = new Field("", "SCOPE", Types.SMALLINT, 5);
219         fields[1] = new Field("", "COLUMN_NAME", Types.CHAR, 32);
220         fields[2] = new Field("", "DATA_TYPE", Types.SMALLINT, 32);
221         fields[3] = new Field("", "TYPE_NAME", Types.CHAR, 32);
222         fields[4] = new Field("", "COLUMN_SIZE", Types.INTEGER, 10);
223         fields[5] = new Field("", "BUFFER_LENGTH", Types.INTEGER, 10);
224         fields[6] = new Field("", "DECIMAL_DIGITS", Types.INTEGER, 10);
225         fields[7] = new Field("", "PSEUDO_COLUMN", Types.SMALLINT, 5);
226
227         String JavaDoc databasePart = "";
228
229         if (catalog != null) {
230             if (!catalog.equals("")) {
231                 databasePart = " FROM " + this.quotedId + catalog
232                     + this.quotedId;
233             }
234         } else {
235             databasePart = " FROM " + this.quotedId + this.database
236                 + this.quotedId;
237         }
238
239         if (table == null) {
240             throw new java.sql.SQLException JavaDoc("Table not specified.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
241         }
242
243         ResultSet results = null;
244         Statement stmt = null;
245
246         try {
247             stmt = this.conn.createStatement();
248
249             if (stmt.getMaxRows() != 0) {
250                 stmt.setMaxRows(0);
251             }
252             
253             StringBuffer JavaDoc queryBuf = new StringBuffer JavaDoc("SHOW COLUMNS FROM ");
254             queryBuf.append(this.quotedId);
255             queryBuf.append(table);
256             queryBuf.append(this.quotedId);
257             queryBuf.append(databasePart);
258
259             results = stmt.executeQuery(queryBuf.toString());
260
261             ArrayList JavaDoc tuples = new ArrayList JavaDoc();
262
263             while (results.next()) {
264                 String JavaDoc keyType = results.getString("Key");
265
266                 if (keyType != null) {
267                     if (StringUtils.startsWithIgnoreCase(keyType, "PRI")) {
268                         byte[][] rowVal = new byte[8][];
269                         rowVal[0] = Integer.toString(java.sql.DatabaseMetaData.bestRowSession)
270                                            .getBytes();
271                         rowVal[1] = results.getBytes("Field");
272
273                         String JavaDoc type = results.getString("Type");
274                         int size = MysqlIO.getMaxBuf();
275                         int decimals = 0;
276
277                         /*
278                          * Parse the Type column from MySQL
279                          */

280                         if (type.indexOf("enum") != -1) {
281                             String JavaDoc temp = type.substring(type.indexOf("("),
282                                     type.indexOf(")"));
283                             java.util.StringTokenizer JavaDoc tokenizer = new java.util.StringTokenizer JavaDoc(temp,
284                                     ",");
285                             int maxLength = 0;
286
287                             while (tokenizer.hasMoreTokens()) {
288                                 maxLength = Math.max(maxLength,
289                                         (tokenizer.nextToken().length() - 2));
290                             }
291
292                             size = maxLength;
293                             decimals = 0;
294                             type = "enum";
295                         } else if (type.indexOf("(") != -1) {
296                             if (type.indexOf(",") != -1) {
297                                 size = Integer.parseInt(type.substring(type
298                                             .indexOf("(") + 1, type.indexOf(",")));
299                                 decimals = Integer.parseInt(type.substring(type
300                                             .indexOf(",") + 1, type.indexOf(")")));
301                             } else {
302                                 size = Integer.parseInt(type.substring(type
303                                             .indexOf("(") + 1, type.indexOf(")")));
304                             }
305
306                             type = type.substring(type.indexOf("("));
307                         }
308
309                         rowVal[2] = new byte[0]; // FIXME!
310
rowVal[3] = s2b(type);
311                         rowVal[4] = Integer.toString(size + decimals).getBytes();
312                         rowVal[5] = Integer.toString(size + decimals).getBytes();
313                         rowVal[6] = Integer.toString(decimals).getBytes();
314                         rowVal[7] = Integer.toString(java.sql.DatabaseMetaData.bestRowNotPseudo)
315                                            .getBytes();
316                         tuples.add(rowVal);
317                     }
318                 }
319             }
320
321             return buildResultSet(fields, tuples);
322         } finally {
323             if (results != null) {
324                 try {
325                     results.close();
326                 } catch (Exception JavaDoc ex) {
327                     ;
328                 }
329
330                 results = null;
331             }
332
333             if (stmt != null) {
334                 try {
335                     stmt.close();
336                 } catch (Exception JavaDoc ex) {
337                     ;
338                 }
339
340                 stmt = null;
341             }
342         }
343     }
344
345     /**
346      * Does a catalog appear at the start of a qualified table name? (Otherwise
347      * it appears at the end)
348      *
349      * @return true if it appears at the start
350      *
351      * @throws java.sql.SQLException DOCUMENT ME!
352      */

353     public boolean isCatalogAtStart() throws java.sql.SQLException JavaDoc {
354         return true;
355     }
356
357     /**
358      * What's the separator between catalog and table name?
359      *
360      * @return the separator string
361      *
362      * @throws java.sql.SQLException DOCUMENT ME!
363      */

364     public String JavaDoc getCatalogSeparator() throws java.sql.SQLException JavaDoc {
365         return ".";
366     }
367
368     /**
369      * What's the database vendor's preferred term for "catalog"?
370      *
371      * @return the vendor term
372      *
373      * @throws java.sql.SQLException DOCUMENT ME!
374      */

375     public String JavaDoc getCatalogTerm() throws java.sql.SQLException JavaDoc {
376         return "database";
377     }
378
379     /**
380      * Get the catalog names available in this database. The results are
381      * ordered by catalog name.
382      *
383      * <P>
384      * The catalog column is:
385      *
386      * <OL>
387      * <li>
388      * <B>TABLE_CAT</B> String => catalog name
389      * </li>
390      * </ol>
391      * </p>
392      *
393      * @return ResultSet each row has a single String column that is a catalog
394      * name
395      *
396      * @throws java.sql.SQLException DOCUMENT ME!
397      */

398     public java.sql.ResultSet JavaDoc getCatalogs() throws java.sql.SQLException JavaDoc {
399         java.sql.ResultSet JavaDoc results = null;
400         java.sql.Statement JavaDoc stmt = null;
401
402         try {
403             stmt = this.conn.createStatement();
404             
405             if (stmt.getMaxRows() != 0) {
406                 stmt.setMaxRows(0);
407             }
408             
409             results = stmt.executeQuery("SHOW DATABASES");
410
411             java.sql.ResultSetMetaData JavaDoc resultsMD = results.getMetaData();
412             Field[] fields = new Field[1];
413             fields[0] = new Field("", "TABLE_CAT", Types.VARCHAR,
414                     resultsMD.getColumnDisplaySize(1));
415
416             ArrayList JavaDoc tuples = new ArrayList JavaDoc();
417
418             while (results.next()) {
419                 byte[][] rowVal = new byte[1][];
420                 rowVal[0] = results.getBytes(1);
421                 tuples.add(rowVal);
422             }
423
424             return buildResultSet(fields, tuples);
425         } finally {
426             if (results != null) {
427                 try {
428                     results.close();
429                 } catch (SQLException JavaDoc sqlEx) {
430                     AssertionFailedException.shouldNotHappen(sqlEx);
431                 }
432
433                 results = null;
434             }
435
436             if (stmt != null) {
437                 try {
438                     stmt.close();
439                 } catch (SQLException JavaDoc sqlEx) {
440                     AssertionFailedException.shouldNotHappen(sqlEx);
441                 }
442
443                 stmt = null;
444             }
445         }
446     }
447
448     /**
449      * Get a description of the access rights for a table's columns.
450      *
451      * <P>
452      * Only privileges matching the column name criteria are returned. They
453      * are ordered by COLUMN_NAME and PRIVILEGE.
454      * </p>
455      *
456      * <P>
457      * Each privilige description has the following columns:
458      *
459      * <OL>
460      * <li>
461      * <B>TABLE_CAT</B> String => table catalog (may be null)
462      * </li>
463      * <li>
464      * <B>TABLE_SCHEM</B> String => table schema (may be null)
465      * </li>
466      * <li>
467      * <B>TABLE_NAME</B> String => table name
468      * </li>
469      * <li>
470      * <B>COLUMN_NAME</B> String => column name
471      * </li>
472      * <li>
473      * <B>GRANTOR</B> => grantor of access (may be null)
474      * </li>
475      * <li>
476      * <B>GRANTEE</B> String => grantee of access
477      * </li>
478      * <li>
479      * <B>PRIVILEGE</B> String => name of access (SELECT, INSERT, UPDATE,
480      * REFRENCES, ...)
481      * </li>
482      * <li>
483      * <B>IS_GRANTABLE</B> String => "YES" if grantee is permitted to grant to
484      * others; "NO" if not; null if unknown
485      * </li>
486      * </ol>
487      * </p>
488      *
489      * @param catalog a catalog name; "" retrieves those without a catalog
490      * @param schema a schema name; "" retrieves those without a schema
491      * @param table a table name
492      * @param columnNamePattern a column name pattern
493      *
494      * @return ResultSet each row is a column privilege description
495      *
496      * @throws java.sql.SQLException if a database access error occurs
497      *
498      * @see #getSearchStringEscape
499      */

500     public java.sql.ResultSet JavaDoc getColumnPrivileges(String JavaDoc catalog,
501         String JavaDoc schema, String JavaDoc table, String JavaDoc columnNamePattern)
502         throws java.sql.SQLException JavaDoc {
503         Field[] fields = new Field[8];
504         fields[0] = new Field("", "TABLE_CAT", Types.CHAR, 64);
505         fields[1] = new Field("", "TABLE_SCHEM", Types.CHAR, 1);
506         fields[2] = new Field("", "TABLE_NAME", Types.CHAR, 64);
507         fields[3] = new Field("", "COLUMN_NAME", Types.CHAR, 64);
508         fields[4] = new Field("", "GRANTOR", Types.CHAR, 77);
509         fields[5] = new Field("", "GRANTEE", Types.CHAR, 77);
510         fields[6] = new Field("", "PRIVILEGE", Types.CHAR, 64);
511         fields[7] = new Field("", "IS_GRANTABLE", Types.CHAR, 3);
512
513         StringBuffer JavaDoc grantQuery = new StringBuffer JavaDoc(
514                 "SELECT c.host, c.db, t.grantor, c.user, "
515                 + "c.table_name, c.column_name, c.column_priv "
516                 + "from mysql.columns_priv c, mysql.tables_priv t "
517                 + "where c.host = t.host and c.db = t.db and "
518                 + "c.table_name = t.table_name ");
519
520         if ((catalog != null) && (catalog.length() != 0)) {
521             grantQuery.append(" AND c.db='");
522             grantQuery.append(catalog);
523             grantQuery.append("' ");
524             ;
525         }
526
527         grantQuery.append(" AND c.table_name ='");
528         grantQuery.append(table);
529         grantQuery.append("' AND c.column_name like '");
530         grantQuery.append(columnNamePattern);
531         grantQuery.append("'");
532
533         Statement stmt = null;
534         ResultSet results = null;
535         ArrayList JavaDoc grantRows = new ArrayList JavaDoc();
536
537         try {
538             stmt = this.conn.createStatement();
539             
540             if (stmt.getMaxRows() != 0) {
541                 stmt.setMaxRows(0);
542             }
543             
544             results = stmt.executeQuery(grantQuery.toString());
545
546             while (results.next()) {
547                 String JavaDoc host = results.getString(1);
548                 String JavaDoc database = results.getString(2);
549                 String JavaDoc grantor = results.getString(3);
550                 String JavaDoc user = results.getString(4);
551
552                 if ((user == null) || (user.length() == 0)) {
553                     user = "%";
554                 }
555
556                 StringBuffer JavaDoc fullUser = new StringBuffer JavaDoc(user);
557
558                 if ((host != null) && this.conn.useHostsInPrivileges()) {
559                     fullUser.append("@");
560                     fullUser.append(host);
561                 }
562
563                 String JavaDoc columnName = results.getString(6);
564                 String JavaDoc allPrivileges = results.getString(7);
565
566                 if (allPrivileges != null) {
567                     allPrivileges = allPrivileges.toUpperCase();
568
569                     StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(allPrivileges, ",");
570
571                     while (st.hasMoreTokens()) {
572                         String JavaDoc privilege = st.nextToken().trim();
573                         byte[][] tuple = new byte[8][];
574                         tuple[0] = s2b(database);
575                         tuple[1] = null;
576                         tuple[2] = s2b(table);
577                         tuple[3] = s2b(columnName);
578
579                         if (grantor != null) {
580                             tuple[4] = s2b(grantor);
581                         } else {
582                             tuple[4] = null;
583                         }
584
585                         tuple[5] = s2b(fullUser.toString());
586                         tuple[6] = s2b(privilege);
587                         tuple[7] = null;
588                         grantRows.add(tuple);
589                     }
590                 }
591             }
592         } finally {
593             if (results != null) {
594                 try {
595                     results.close();
596                 } catch (Exception JavaDoc ex) {
597                     ;
598                 }
599
600                 results = null;
601             }
602
603             if (stmt != null) {
604                 try {
605                     stmt.close();
606                 } catch (Exception JavaDoc ex) {
607                     ;
608                 }
609
610                 stmt = null;
611             }
612         }
613
614         return buildResultSet(fields, grantRows);
615     }
616
617     /**
618      * Get a description of table columns available in a catalog.
619      *
620      * <P>
621      * Only column descriptions matching the catalog, schema, table and column
622      * name criteria are returned. They are ordered by TABLE_SCHEM,
623      * TABLE_NAME and ORDINAL_POSITION.
624      * </p>
625      *
626      * <P>
627      * Each column description has the following columns:
628      *
629      * <OL>
630      * <li>
631      * <B>TABLE_CAT</B> String => table catalog (may be null)
632      * </li>
633      * <li>
634      * <B>TABLE_SCHEM</B> String => table schema (may be null)
635      * </li>
636      * <li>
637      * <B>TABLE_NAME</B> String => table name
638      * </li>
639      * <li>
640      * <B>COLUMN_NAME</B> String => column name
641      * </li>
642      * <li>
643      * <B>DATA_TYPE</B> short => SQL type from java.sql.Types
644      * </li>
645      * <li>
646      * <B>TYPE_NAME</B> String => Data source dependent type name
647      * </li>
648      * <li>
649      * <B>COLUMN_SIZE</B> int => column size. For char or date types this is
650      * the maximum number of characters, for numeric or decimal types this is
651      * precision.
652      * </li>
653      * <li>
654      * <B>BUFFER_LENGTH</B> is not used.
655      * </li>
656      * <li>
657      * <B>DECIMAL_DIGITS</B> int => the number of fractional digits
658      * </li>
659      * <li>
660      * <B>NUM_PREC_RADIX</B> int => Radix (typically either 10 or 2)
661      * </li>
662      * <li>
663      * <B>NULLABLE</B> int => is NULL allowed?
664      *
665      * <UL>
666      * <li>
667      * columnNoNulls - might not allow NULL values
668      * </li>
669      * <li>
670      * columnNullable - definitely allows NULL values
671      * </li>
672      * <li>
673      * columnNullableUnknown - nullability unknown
674      * </li>
675      * </ul>
676      *
677      * </li>
678      * <li>
679      * <B>REMARKS</B> String => comment describing column (may be null)
680      * </li>
681      * <li>
682      * <B>COLUMN_DEF</B> String => default value (may be null)
683      * </li>
684      * <li>
685      * <B>SQL_DATA_TYPE</B> int => unused
686      * </li>
687      * <li>
688      * <B>SQL_DATETIME_SUB</B> int => unused
689      * </li>
690      * <li>
691      * <B>CHAR_OCTET_LENGTH</B> int => for char types the maximum number of
692      * bytes in the column
693      * </li>
694      * <li>
695      * <B>ORDINAL_POSITION</B> int => index of column in table (starting at 1)
696      * </li>
697      * <li>
698      * <B>IS_NULLABLE</B> String => "NO" means column definitely does not allow
699      * NULL values; "YES" means the column might allow NULL values. An empty
700      * string means nobody knows.
701      * </li>
702      * </ol>
703      * </p>
704      *
705      * @param catalog a catalog name; "" retrieves those without a catalog
706      * @param schemaPattern a schema name pattern; "" retrieves those without a
707      * schema
708      * @param tableName a table name pattern
709      * @param columnNamePattern a column name pattern
710      *
711      * @return ResultSet each row is a column description
712      *
713      * @throws java.sql.SQLException if a database access error occurs
714      *
715      * @see #getSearchStringEscape
716      */

717     public java.sql.ResultSet JavaDoc getColumns(String JavaDoc catalog, String JavaDoc schemaPattern,
718         String JavaDoc tableName, String JavaDoc columnNamePattern)
719         throws java.sql.SQLException JavaDoc {
720         String JavaDoc databasePart = "";
721
722         if (columnNamePattern == null) {
723             columnNamePattern = "%";
724         }
725
726         if (catalog != null) {
727             if (!catalog.equals("")) {
728                 databasePart = " FROM " + this.quotedId + catalog
729                     + this.quotedId;
730             }
731         } else {
732             databasePart = " FROM " + this.quotedId + this.database
733                 + this.quotedId;
734         }
735
736         ArrayList JavaDoc tableNameList = new ArrayList JavaDoc();
737         int tablenameLength = 0;
738
739         if (tableName == null) {
740             // Select from all tables
741
java.sql.ResultSet JavaDoc tables = null;
742
743             try {
744                 tables = getTables(catalog, schemaPattern, "%", new String JavaDoc[0]);
745
746                 while (tables.next()) {
747                     String JavaDoc tableNameFromList = tables.getString("TABLE_NAME");
748                     tableNameList.add(tableNameFromList);
749
750                     if (tableNameFromList.length() > tablenameLength) {
751                         tablenameLength = tableNameFromList.length();
752                     }
753                 }
754             } finally {
755                 if (tables != null) {
756                     try {
757                         tables.close();
758                     } catch (Exception JavaDoc sqlEx) {
759                         AssertionFailedException.shouldNotHappen(sqlEx);
760                     }
761
762                     tables = null;
763                 }
764             }
765         } else {
766 </