1 | /* |
2 | Copyright (C) 2005 MySQL AB |
3 | |
4 | This program is free software; you can redistribute it and/or modify |
5 | it under the terms of version 2 of the GNU General Public License as |
6 | published by the Free Software Foundation. |
7 | |
8 | There are special exceptions to the terms and conditions of the GPL |
9 | as it is applied to this software. View the full text of the |
10 | exception in file EXCEPTIONS-CONNECTOR-J in the directory of this |
11 | software distribution. |
12 | |
13 | This program is distributed in the hope that it will be useful, |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | GNU General Public License for more details. |
17 | |
18 | You should have received a copy of the GNU General Public License |
19 | along with this program; if not, write to the Free Software |
20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
21 | |
22 | */ |
23 | package com.mysql.jdbc; |
24 | |
25 | import java.sql.ResultSet; |
26 | import java.sql.SQLException; |
27 | import java.sql.Types; |
28 | |
29 | /** |
30 | * DatabaseMetaData implementation that uses INFORMATION_SCHEMA available in |
31 | * MySQL-5.0 and newer. |
32 | * |
33 | * The majority of the queries in this code were built for Connector/OO.org by |
34 | * Georg Richter (georg_at_mysql.com). |
35 | */ |
36 | public class DatabaseMetaDataUsingInfoSchema extends DatabaseMetaData { |
37 | |
38 | public DatabaseMetaDataUsingInfoSchema(Connection connToSet, |
39 | String databaseToSet) { |
40 | super(connToSet, databaseToSet); |
41 | } |
42 | |
43 | private ResultSet executeMetadataQuery(PreparedStatement pStmt) |
44 | throws SQLException { |
45 | ResultSet rs = pStmt.executeQuery(); |
46 | ((com.mysql.jdbc.ResultSet) rs).setOwningStatement(null); |
47 | |
48 | return rs; |
49 | } |
50 | |
51 | /** |
52 | * Get a description of the access rights for a table's columns. |
53 | * <P> |
54 | * Only privileges matching the column name criteria are returned. They are |
55 | * ordered by COLUMN_NAME and PRIVILEGE. |
56 | * </p> |
57 | * <P> |
58 | * Each privilige description has the following columns: |
59 | * <OL> |
60 | * <li> <B>TABLE_CAT</B> String => table catalog (may be null) </li> |
61 | * <li> <B>TABLE_SCHEM</B> String => table schema (may be null) </li> |
62 | * <li> <B>TABLE_NAME</B> String => table name </li> |
63 | * <li> <B>COLUMN_NAME</B> String => column name </li> |
64 | * <li> <B>GRANTOR</B> => grantor of access (may be null) </li> |
65 | * <li> <B>GRANTEE</B> String => grantee of access </li> |
66 | * <li> <B>PRIVILEGE</B> String => name of access (SELECT, INSERT, UPDATE, |
67 | * REFRENCES, ...) </li> |
68 | * <li> <B>IS_GRANTABLE</B> String => "YES" if grantee is permitted to |
69 | * grant to others; "NO" if not; null if unknown </li> |
70 | * </ol> |
71 | * </p> |
72 | * |
73 | * @param catalog |
74 | * a catalog name; "" retrieves those without a catalog |
75 | * @param schema |
76 | * a schema name; "" retrieves those without a schema |
77 | * @param table |
78 | * a table name |
79 | * @param columnNamePattern |
80 | * a column name pattern |
81 | * @return ResultSet each row is a column privilege description |
82 | * @throws SQLException |
83 | * if a database access error occurs |
84 | * @see #getSearchStringEscape |
85 | */ |
86 | public java.sql.ResultSet getColumnPrivileges(String catalog, |
87 | String schema, String table, String columnNamePattern) |
88 | throws SQLException { |
89 | if (columnNamePattern == null) { |
90 | if (this.conn.getNullNamePatternMatchesAll()) { |
91 | columnNamePattern = "%"; |
92 | } else { |
93 | throw SQLError.createSQLException( |
94 | "Column name pattern can not be NULL or empty.", |
95 | SQLError.SQL_STATE_ILLEGAL_ARGUMENT); |
96 | } |
97 | } |
98 | |
99 | if (catalog == null) { |
100 | if (!this.conn.getNullCatalogMeansCurrent()) { |
101 | throw SQLError.createSQLException("'catalog' parameter can not be null", |
102 | SQLError.SQL_STATE_ILLEGAL_ARGUMENT); |
103 | } |
104 | |
105 | catalog = this.database; |
106 | } |
107 | |
108 | String sql = "SELECT TABLE_SCHEMA AS TABLE_CAT, NULL AS TABLE_SCHEM, TABLE_NAME," |
109 | +"COLUMN_NAME, NULL AS GRANTOR, GRANTEE, PRIVILEGE_TYPE AS PRIVILEGE, IS_GRANTABLE FROM " |
110 | + "INFORMATION_SCHEMA.COLUMN_PRIVILEGES WHERE " |
111 | + "TABLE_SCHEMA = ? AND " |
112 | + "TABLE_NAME =? AND COLUMN_NAME LIKE ? ORDER BY " |
113 | + "COLUMN_NAME, PRIVILEGE_TYPE"; |
114 | |
115 | PreparedStatement pStmt = null; |
116 | |
117 | try { |
118 | pStmt = prepareMetaDataSafeStatement(sql); |
119 | pStmt.setString(1, catalog); |
120 | pStmt.setString(2, table); |
121 | pStmt.setString(3, columnNamePattern); |
122 | |
123 | ResultSet rs = executeMetadataQuery(pStmt); |
124 | ((com.mysql.jdbc.ResultSet) rs).redefineFieldsForDBMD(new Field[] { |
125 | new Field("", "TABLE_CAT", Types.CHAR, 64), |
126 | new Field("", "TABLE_SCHEM", Types.CHAR, 1), |
127 | new Field("", "TABLE_NAME", Types.CHAR, 64), |
128 | new Field("", "COLUMN_NAME", Types.CHAR, 64), |
129 | new Field("", "GRANTOR", Types.CHAR, 77), |
130 | new Field("", "GRANTEE", Types.CHAR, 77), |
131 | new Field("", "PRIVILEGE", Types.CHAR, 64), |
132 | new Field("", "IS_GRANTABLE", Types.CHAR, 3)}); |
133 | |
134 | return rs; |
135 | } finally { |
136 | if (pStmt != null) { |
137 | pStmt.close(); |
138 | } |
139 | } |
140 | } |
141 | |
142 | /** |
143 | * Get a description of table columns available in a catalog. |
144 | * <P> |
145 | * Only column descriptions matching the catalog, schema, table and column |
146 | * name criteria are returned. They are ordered by TABLE_SCHEM, TABLE_NAME |
147 | * and ORDINAL_POSITION. |
148 | * </p> |
149 | * <P> |
150 | * Each column description has the following columns: |
151 | * <OL> |
152 | * <li> <B>TABLE_CAT</B> String => table catalog (may be null) </li> |
153 | * <li> <B>TABLE_SCHEM</B> String => table schema (may be null) </li> |
154 | * <li> <B>TABLE_NAME</B> String => table name </li> |
155 | * <li> <B>COLUMN_NAME</B> String => column name </li> |
156 | * <li> <B>DATA_TYPE</B> short => SQL type from java.sql.Types </li> |
157 | * <li> <B>TYPE_NAME</B> String => Data source dependent type name </li> |
158 | * <li> <B>COLUMN_SIZE</B> int => column size. For char or date types this |
159 | * is the maximum number of characters, for numeric or decimal types this is |
160 | * precision. </li> |
161 | * <li> <B>BUFFER_LENGTH</B> is not used. </li> |
162 | * <li> <B>DECIMAL_DIGITS</B> int => the number of fractional digits </li> |
163 | * <li> <B>NUM_PREC_RADIX</B> int => Radix (typically either 10 or 2) </li> |
164 | * <li> <B>NULLABLE</B> int => is NULL allowed? |
165 | * <UL> |
166 | * <li> columnNoNulls - might not allow NULL values </li> |
167 | * <li> columnNullable - definitely allows NULL values </li> |
168 | * <li> columnNullableUnknown - nullability unknown </li> |
169 | * </ul> |
170 | * </li> |
171 | * <li> <B>REMARKS</B> String => comment describing column (may be null) |
172 | * </li> |
173 | * <li> <B>COLUMN_DEF</B> String => default value (may be null) </li> |
174 | * <li> <B>SQL_DATA_TYPE</B> int => unused </li> |
175 | * <li> <B>SQL_DATETIME_SUB</B> int => unused </li> |
176 | * <li> <B>CHAR_OCTET_LENGTH</B> int => for char types the maximum number |
177 | * of bytes in the column </li> |
178 | * <li> <B>ORDINAL_POSITION</B> int => index of column in table (starting |
179 | * at 1) </li> |
180 | * <li> <B>IS_NULLABLE</B> String => "NO" means column definitely does not |
181 | * allow NULL values; "YES" means the column might allow NULL values. An |
182 | * empty string means nobody knows. </li> |
183 | * </ol> |
184 | * </p> |
185 | */ |
186 | public ResultSet getColumns(String catalog, String schemaPattern, |
187 | String tableName, String columnNamePattern) throws SQLException { |
188 | if (columnNamePattern == null) { |
189 | if (this.conn.getNullNamePatternMatchesAll()) { |
190 | columnNamePattern = "%"; |
191 | } else { |
192 | throw SQLError.createSQLException( |
193 | "Column name pattern can not be NULL or empty.", |
194 | SQLError.SQL_STATE_ILLEGAL_ARGUMENT); |
195 | } |
196 | } |
197 | |
198 | if (catalog == null) { |
199 | if (!this.conn.getNullCatalogMeansCurrent()) { |
200 | throw SQLError.createSQLException("'catalog' parameter can not be null", |
201 | SQLError.SQL_STATE_ILLEGAL_ARGUMENT); |
202 | } |
203 | |
204 | catalog = this.database; |
205 | } |
206 | |
207 | StringBuffer sqlBuf = new StringBuffer("SELECT " |
208 | + "TABLE_SCHEMA AS TABLE_CAT, " + "NULL AS TABLE_SCHEM," |
209 | + "TABLE_NAME," + "COLUMN_NAME,"); |
210 | MysqlDefs.appendJdbcTypeMappingQuery(sqlBuf, "DATA_TYPE"); |
211 | |
212 | sqlBuf.append(" AS DATA_TYPE, "); |
213 | |
214 | if (conn.getCapitalizeTypeNames()) { |
215 | sqlBuf.append("UPPER(CASE WHEN LOCATE('unsigned', COLUMN_TYPE) != 0 AND LOCATE('unsigned', DATA_TYPE) = 0 THEN CONCAT(DATA_TYPE, ' unsigned') ELSE DATA_TYPE END) AS TYPE_NAME,"); |
216 | } else { |
217 | sqlBuf.append("CASE WHEN LOCATE('unsigned', COLUMN_TYPE) != 0 AND LOCATE('unsigned', DATA_TYPE) = 0 THEN CONCAT(DATA_TYPE, ' unsigned') ELSE DATA_TYPE END AS TYPE_NAME,"); |
218 | } |
219 | |
220 | sqlBuf |
221 | .append("CASE WHEN CHARACTER_MAXIMUM_LENGTH IS NULL THEN NUMERIC_PRECISION ELSE CHARACTER_MAXIMUM_LENGTH END AS COLUMN_SIZE, " |
222 | + " NULL AS BUFFER_LENGTH," |
223 | + "NUMERIC_SCALE AS DECIMAL_DIGITS," |
224 | + "10 AS NUM_PREC_RADIX," |
225 | + "NULL AS NULLABLE," |
226 | + "COLUMN_COMMENT AS REMARKS," |
227 | + "COLUMN_DEFAULT AS COLUMN_DEF," |
228 | + "NULL AS SQL_DATA_TYPE," |
229 | + "NULL AS SQL_DATETIME_SUB," |
230 | + "CHARACTER_OCTET_LENGTH AS CHAR_OCTET_LENGTH," |
231 | + "ORDINAL_POSITION," |
232 | + "IS_NULLABLE " |
233 | + "FROM INFORMATION_SCHEMA.COLUMNS WHERE " |
234 | + "TABLE_SCHEMA LIKE ? AND TABLE_NAME LIKE ? AND COLUMN_NAME LIKE ? " |
235 | + "ORDER BY TABLE_SCHEMA, TABLE_NAME, ORDINAL_POSITION"); |
236 | |
237 | PreparedStatement pStmt = null; |
238 | |
239 | try { |
240 | pStmt = prepareMetaDataSafeStatement(sqlBuf.toString()); |
241 | pStmt.setString(1, catalog); |
242 | pStmt.setString(2, tableName); |
243 | pStmt.setString(3, columnNamePattern); |
244 | |
245 | ResultSet rs = executeMetadataQuery(pStmt); |
246 | |
247 | ((com.mysql.jdbc.ResultSet) rs).redefineFieldsForDBMD(new Field[] { |
248 | new Field("", "TABLE_CAT", Types.CHAR, 255), |
249 | new Field("", "TABLE_SCHEM", Types.CHAR, 0), |
250 | new Field("", "TABLE_NAME", Types.CHAR, 255), |
251 | new Field("", "COLUMN_NAME", Types.CHAR, 32), |
252 | new Field("", "DATA_TYPE", Types.SMALLINT, 5), |
253 | new Field("", "TYPE_NAME", Types.CHAR, 16), |
254 | new Field("", "COLUMN_SIZE", Types.INTEGER, Integer |
255 | .toString(Integer.MAX_VALUE).length()), |
256 | new Field("", "BUFFER_LENGTH", Types.INTEGER, 10), |
257 | new Field("", "DECIMAL_DIGITS", Types.INTEGER, 10), |
258 | new Field("", "NUM_PREC_RADIX", Types.INTEGER, 10), |
259 | new Field("", "NULLABLE", Types.INTEGER, 10), |
260 | new Field("", "REMARKS", Types.CHAR, 0), |
261 | new Field("", "COLUMN_DEF", Types.CHAR, 0), |
262 | new Field("", "SQL_DATA_TYPE", Types.INTEGER, 10), |
263 | new Field("", "SQL_DATETIME_SUB", Types.INTEGER, 10), |
264 | new Field("", "CHAR_OCTET_LENGTH", Types.INTEGER, Integer |
265 | .toString(Integer.MAX_VALUE).length()), |
266 | new Field("", "ORDINAL_POSITION", Types.INTEGER, 10), |
267 | new Field("", "IS_NULLABLE", Types.CHAR, 3) }); |
268 | |
269 | return rs; |
270 | } finally { |
271 | if (pStmt != null) { |
272 | pStmt.close(); |
273 | } |
274 | } |
275 | } |
276 | |
277 | /** |
278 | * Get a description of the foreign key columns in the foreign key table |
279 | * that reference the primary key columns of the primary key table (describe |
280 | * how one table imports another's key.) This should normally return a |
281 | * single foreign key/primary key pair (most tables only import a foreign |
282 | * key from a table once.) They are ordered by FKTABLE_CAT, FKTABLE_SCHEM, |
283 | * FKTABLE_NAME, and KEY_SEQ. |
284 | * <P> |
285 | * Each foreign key column description has the following columns: |
286 | * <OL> |
287 | * <li> <B>PKTABLE_CAT</B> String => primary key table catalog (may be |
288 | * null) </li> |
289 | * <li> <B>PKTABLE_SCHEM</B> String => primary key table schema (may be |
290 | * null) </li> |
291 | * <li> <B>PKTABLE_NAME</B> String => primary key table name </li> |
292 | * <li> <B>PKCOLUMN_NAME</B> String => primary key column name </li> |
293 | * <li> <B>FKTABLE_CAT</B> String => foreign key table catalog (may be |
294 | * null) being exported (may be null) </li> |
295 | * <li> <B>FKTABLE_SCHEM</B> String => foreign key table schema (may be |
296 | * null) being exported (may be null) </li> |
297 | * <li> <B>FKTABLE_NAME</B> String => foreign key table name being exported |
298 | * </li> |
299 | * <li> <B>FKCOLUMN_NAME</B> String => foreign key column name being |
300 | * exported </li> |
301 | * <li> <B>KEY_SEQ</B> short => sequence number within foreign key </li> |
302 | * <li> <B>UPDATE_RULE</B> short => What happens to foreign key when |
303 | * primary is updated: |
304 | * <UL> |
305 | * <li> importedKeyCascade - change imported key to agree with primary key |
306 | * update </li> |
307 | * <li> importedKeyRestrict - do not allow update of primary key if it has |
308 | * been imported </li> |
309 | * <li> importedKeySetNull - change imported key to NULL if its primary key |
310 | * has been updated </li> |
311 | * </ul> |
312 | * </li> |
313 | * <li> <B>DELETE_RULE</B> short => What happens to the foreign key when |
314 | * primary is deleted. |
315 | * <UL> |
316 | * <li> importedKeyCascade - delete rows that import a deleted key </li> |
317 | * <li> importedKeyRestrict - do not allow delete of primary key if it has |
318 | * been imported </li> |
319 | * <li> importedKeySetNull - change imported key to NULL if its primary key |
320 | * has been deleted </li> |
321 | * </ul> |
322 | * </li> |
323 | * <li> <B>FK_NAME</B> String => foreign key identifier (may be null) </li> |
324 | * <li> <B>PK_NAME</B> String => primary key identifier (may be null) </li> |
325 | * </ol> |
326 | * </p> |
327 | * |
328 | * @param primaryCatalog |
329 | * a catalog name; "" retrieves those without a catalog |
330 | * @param primarySchema |
331 | * a schema name pattern; "" retrieves those without a schema |
332 | * @param primaryTable |
333 | * a table name |
334 | * @param foreignCatalog |
335 | * a catalog name; "" retrieves those without a catalog |
336 | * @param foreignSchema |
337 | * a schema name pattern; "" retrieves those without a schema |
338 | * @param foreignTable |
339 | * a table name |
340 | * @return ResultSet each row is a foreign key column description |
341 | * @throws SQLException |
342 | * if a database access error occurs |
343 | */ |
344 | public java.sql.ResultSet getCrossReference(String primaryCatalog, |
345 | String primarySchema, String primaryTable, String foreignCatalog, |
346 | String foreignSchema, String foreignTable) throws SQLException { |
347 | if (primaryTable == null) { |
348 | throw SQLError.createSQLException("Table not specified.", |
349 | SQLError.SQL_STATE_ILLEGAL_ARGUMENT); |
350 | } |
351 | |
352 | if (primaryCatalog == null) { |
353 | if (!this.conn.getNullCatalogMeansCurrent()) { |
354 | throw SQLError.createSQLException("'catalog' parameter can not be null", |
355 | SQLError.SQL_STATE_ILLEGAL_ARGUMENT); |
356 | } |
357 | |
358 | primaryCatalog = this.database; |
359 | } |
360 | |
361 | if (foreignCatalog == null) { |
362 | if (!this.conn.getNullCatalogMeansCurrent()) { |
363 | throw SQLError.createSQLException("'catalog' parameter can not be null", |
364 | SQLError.SQL_STATE_ILLEGAL_ARGUMENT); |
365 | } |
366 | |
367 | foreignCatalog = this.database; |
368 | } |
369 | |
370 | Field[] fields = new Field[14]; |
371 | fields[0] = new Field("", "PKTABLE_CAT", Types.CHAR, 255); |
372 | fields[1] = new Field("", "PKTABLE_SCHEM", Types.CHAR, 0); |
373 | fields[2] = new Field("", "PKTABLE_NAME", Types.CHAR, 255); |
374 | fields[3] = new Field("", "PKCOLUMN_NAME", Types.CHAR, 32); |
375 | fields[4] = new Field("", "FKTABLE_CAT", Types.CHAR, 255); |
376 | fields[5] = new Field("", "FKTABLE_SCHEM", Types.CHAR, 0); |
377 | fields[6] = new Field("", "FKTABLE_NAME", Types.CHAR, 255); |
378 | fields[7] = new Field("", "FKCOLUMN_NAME", Types.CHAR, 32); |
379 | fields[8] = new Field("", "KEY_SEQ", Types.SMALLINT, 2); |
380 | fields[9] = new Field("", "UPDATE_RULE", Types.SMALLINT, 2); |
381 | fields[10] = new Field("", "DELETE_RULE", Types.SMALLINT, 2); |
382 | fields[11] = new Field("", "FK_NAME", Types.CHAR, 0); |
383 | fields[12] = new Field("", "PK_NAME", Types.CHAR, 0); |
384 | fields[13] = new Field("", "DEFERRABILITY", Types.INTEGER, 2); |
385 | |
386 | String sql = "SELECT " |
387 | + "A.REFERENCED_TABLE_SCHEMA AS PKTABLE_CAT," |
388 | + "NULL AS PKTABLE_SCHEM," |
389 | + "A.REFERENCED_TABLE_NAME AS PKTABLE_NAME," |
390 | + "A.REFERENCED_COLUMN_NAME AS PKCOLUMN_NAME," |
391 | + "A.TABLE_SCHEMA AS FKTABLE_CAT," |
392 | + "NULL AS FKTABLE_SCHEM," |
393 | + "A.TABLE_NAME AS FKTABLE_NAME, " |
394 | + "A.COLUMN_NAME AS FKCOLUMN_NAME, " |
395 | + "A.ORDINAL_POSITION AS KEY_SEQ," |
396 | + importedKeyRestrict |
397 | + " AS UPDATE_RULE," |
398 | + importedKeyRestrict |
399 | + " AS DELETE_RULE," |
400 | + "A.CONSTRAINT_NAME AS FK_NAME," |
401 | + "NULL AS PK_NAME," |
402 | + importedKeyNotDeferrable |
403 | + " AS DEFERRABILITY " |
404 | + "FROM " |
405 | + "INFORMATION_SCHEMA.KEY_COLUMN_USAGE A," |
406 | + "INFORMATION_SCHEMA.TABLE_CONSTRAINTS B " |
407 | + "WHERE " |
408 | + "A.TABLE_SCHEMA=B.TABLE_SCHEMA AND A.TABLE_NAME=B.TABLE_NAME " |
409 | + "AND " |
410 | + "A.CONSTRAINT_NAME=B.CONSTRAINT_NAME AND B.CONSTRAINT_TYPE IS NOT NULL " |
411 | + "AND A.REFERENCED_TABLE_SCHEMA=? AND A.REFERENCED_TABLE_NAME=? " |
412 | + "AND A.TABLE_SCHEMA=? AND A.TABLE_NAME=? " + "ORDER BY " |
413 | + "A.TABLE_SCHEMA, A.TABLE_NAME, A.ORDINAL_POSITION"; |
414 | |
415 | PreparedStatement pStmt = null; |
416 | |
417 | try { |
418 | pStmt = prepareMetaDataSafeStatement(sql); |
419 | pStmt.setString(1, primaryCatalog); |
420 | pStmt.setString(2, primaryTable); |
421 | pStmt.setString(3, foreignCatalog); |
422 | pStmt.setString(4, foreignTable); |
423 | |
424 | ResultSet rs = executeMetadataQuery(pStmt); |
425 | ((com.mysql.jdbc.ResultSet) rs).redefineFieldsForDBMD(new Field[] { |
426 | new Field("", "PKTABLE_CAT", Types.CHAR, 255), |
427 | new Field("", "PKTABLE_SCHEM", Types.CHAR, 0), |
428 | new Field("", "PKTABLE_NAME", Types.CHAR, 255), |
429 | new Field("", "PKCOLUMN_NAME", Types.CHAR, 32), |
430 | new Field("", "FKTABLE_CAT", Types.CHAR, 255), |
431 | new Field("", "FKTABLE_SCHEM", Types.CHAR, 0), |
432 | new Field("", "FKTABLE_NAME", Types.CHAR, 255), |
433 | new Field("", "FKCOLUMN_NAME", Types.CHAR, 32), |
434 | new Field("", "KEY_SEQ", Types.SMALLINT, 2), |
435 | new Field("", "UPDATE_RULE", Types.SMALLINT, 2), |
436 | new Field("", "DELETE_RULE", Types.SMALLINT, 2), |
437 | new Field("", "FK_NAME", Types.CHAR, 0), |
438 | new Field("", "PK_NAME", Types.CHAR, 0), |
439 | new Field("", "DEFERRABILITY", Types.INTEGER, 2) }); |
440 | |
441 | return rs; |
442 | } finally { |
443 | if (pStmt != null) { |
444 | pStmt.close(); |
445 | } |
446 | } |
447 | } |
448 | |
449 | /** |
450 | * Get a description of a foreign key columns that reference a table's |
451 | * primary key columns (the foreign keys exported by a table). They are |
452 | * ordered by FKTABLE_CAT, FKTABLE_SCHEM, FKTABLE_NAME, and KEY_SEQ. |
453 | * <P> |
454 | * Each foreign key column description has the following columns: |
455 | * <OL> |
456 | * <li> <B>PKTABLE_CAT</B> String => primary key table catalog (may be |
457 | * null) </li> |
458 | * <li> <B>PKTABLE_SCHEM</B> String => primary key table schema (may be |
459 | * null) </li> |
460 | * <li> <B>PKTABLE_NAME</B> String => primary key table name </li> |
461 | * <li> <B>PKCOLUMN_NAME</B> String => primary key column name </li> |
462 | * <li> <B>FKTABLE_CAT</B> String => foreign key table catalog (may be |
463 | * null) being exported (may be null) </li> |
464 | * <li> <B>FKTABLE_SCHEM</B> String => foreign key table schema (may be |
465 | * null) being exported (may be null) </li> |
466 | * <li> <B>FKTABLE_NAME</B> String => foreign key table name being exported |
467 | * </li> |
468 | * <li> <B>FKCOLUMN_NAME</B> String => foreign key column name being |
469 | * exported </li> |
470 | * <li> <B>KEY_SEQ</B> short => sequence number within foreign key </li> |
471 | * <li> <B>UPDATE_RULE</B> short => What happens to foreign key when |
472 | * primary is updated: |
473 | * <UL> |
474 | * <li> importedKeyCascade - change imported key to agree with primary key |
475 | * update </li> |
476 | * <li> importedKeyRestrict - do not allow update of primary key if it has |
477 | * been imported </li> |
478 | * <li> importedKeySetNull - change imported key to NULL if its primary key |
479 | * has been updated </li> |
480 | * </ul> |
481 | * </li> |
482 | * <li> <B>DELETE_RULE</B> short => What happens to the foreign key when |
483 | * primary is deleted. |
484 | * <UL> |
485 | * <li> importedKeyCascade - delete rows that import a deleted key </li> |
486 | * <li> importedKeyRestrict - do not allow delete of primary key if it has |
487 | * been imported </li> |
488 | * <li> importedKeySetNull - change imported key to NULL if its primary key |
489 | * has been deleted </li> |
490 | * </ul> |
491 | * </li> |
492 | * <li> <B>FK_NAME</B> String => foreign key identifier (may be null) </li> |
493 | * <li> <B>PK_NAME</B> String => primary key identifier (may be null) </li> |
494 | * </ol> |
495 | * </p> |
496 | * |
497 | * @param catalog |
498 | * a catalog name; "" retrieves those without a catalog |
499 | * @param schema |
500 | * a schema name pattern; "" retrieves those without a schema |
501 | * @param table |
502 | * a table name |
503 | * @return ResultSet each row is a foreign key column description |
504 | * @throws SQLException |
505 | * if a database access error occurs |
506 | * @see #getImportedKeys |
507 | */ |
508 | public java.sql.ResultSet getExportedKeys(String catalog, String schema, |
509 | String table) throws SQLException { |
510 | // TODO: Can't determine actions using INFORMATION_SCHEMA yet... |
511 | |
512 | if (table == null) { |
513 | throw SQLError.createSQLException("Table not specified.", |
514 | SQLError.SQL_STATE_ILLEGAL_ARGUMENT); |
515 | } |
516 | |
517 | if (catalog == null) { |
518 | if (!this.conn.getNullCatalogMeansCurrent()) { |
519 | throw SQLError.createSQLException("'catalog' parameter can not be null", |
520 | SQLError.SQL_STATE_ILLEGAL_ARGUMENT); |
521 | } |
522 | |
523 | catalog = this.database; |
524 | } |
525 | |
526 | String sql = "SELECT " |
527 | + "A.REFERENCED_TABLE_SCHEMA AS PKTABLE_CAT," |
528 | + "NULL AS PKTABLE_SCHEM," |
529 | + "A.REFERENCED_TABLE_NAME AS PKTABLE_NAME, " |
530 | + "A.REFERENCED_COLUMN_NAME AS PKCOLUMN_NAME, " |
531 | + "A.TABLE_SCHEMA AS FKTABLE_CAT," |
532 | + "NULL AS FKTABLE_SCHEM," |
533 | + "A.TABLE_NAME AS FKTABLE_NAME," |
534 | + "A.COLUMN_NAME AS FKCOLUMN_NAME, " |
535 | + "A.ORDINAL_POSITION AS KEY_SEQ," |
536 | + importedKeyRestrict |
537 | + " AS UPDATE_RULE," |
538 | + importedKeyRestrict |
539 | + " AS DELETE_RULE," |
540 | + "A.CONSTRAINT_NAME AS FK_NAME," |
541 | + "NULL AS PK_NAME," |
542 | + importedKeyNotDeferrable |
543 | + " AS DEFERRABILITY " |
544 | + "FROM " |
545 | + "INFORMATION_SCHEMA.KEY_COLUMN_USAGE A," |
546 | + "INFORMATION_SCHEMA.TABLE_CONSTRAINTS B " |
547 | + "WHERE " |
548 | + "A.TABLE_SCHEMA=B.TABLE_SCHEMA AND A.TABLE_NAME=B.TABLE_NAME " |
549 | + "AND " |
550 | + "A.CONSTRAINT_NAME=B.CONSTRAINT_NAME AND B.CONSTRAINT_TYPE IS NOT NULL " |
551 | + "AND A.REFERENCED_TABLE_SCHEMA=? AND A.REFERENCED_TABLE_NAME=? " |
552 | + "ORDER BY A.TABLE_SCHEMA, A.TABLE_NAME, A.ORDINAL_POSITION"; |
553 | |
554 | PreparedStatement pStmt = null; |
555 | |
556 | try { |
557 | pStmt = prepareMetaDataSafeStatement(sql); |
558 | pStmt.setString(1, catalog); |
559 | pStmt.setString(2, table); |
560 | |
561 | ResultSet rs = executeMetadataQuery(pStmt); |
562 | |
563 | ((com.mysql.jdbc.ResultSet) rs).redefineFieldsForDBMD(new Field[] { |
564 | new Field("", "PKTABLE_CAT", Types.CHAR, 255), |
565 | new Field("", "PKTABLE_SCHEM", Types.CHAR, 0), |
566 | new Field("", "PKTABLE_NAME", Types.CHAR, 255), |
567 | new Field("", "PKCOLUMN_NAME", Types.CHAR, 32), |
568 | new Field("", "FKTABLE_CAT", Types.CHAR, 255), |
569 | new Field("", "FKTABLE_SCHEM", Types.CHAR, 0), |
570 | new Field("", "FKTABLE_NAME", Types.CHAR, 255), |
571 | new Field("", "FKCOLUMN_NAME", Types.CHAR, 32), |
572 | new Field("", "KEY_SEQ", Types.SMALLINT, 2), |
573 | new Field("", "UPDATE_RULE", Types.SMALLINT, 2), |
574 | new Field("", "DELETE_RULE", Types.SMALLINT, 2), |
575 | new Field("", "FK_NAME", Types.CHAR, 255), |
576 | new Field("", "PK_NAME", Types.CHAR, 0), |
577 | new Field("", "DEFERRABILITY", Types.INTEGER, 2) }); |
578 | |
579 | return rs; |
580 | } finally { |
581 | if (pStmt != null) { |
582 | pStmt.close(); |
583 | } |
584 | } |
585 | |
586 | } |
587 | |
588 | /* |
589 | * |
590 | * getTablePrivileges |
591 | * |
592 | * if (getMysqlVersion() > 49999) { if (!strcasecmp("localhost", |
593 | * m_pSettings->pConnection->host)) { sprintf(user, "A.GRANTEE = |
594 | * \"'%s'@'localhost'\" OR A.GRANTEE LIKE \"'%'@'localhost'\"", |
595 | * m_pSettings->pConnection->user, m_pSettings->pConnection->user); } else { |
596 | * sprintf(user, "\"'%s'@'%s'\" LIKE A.GRANTEE", |
597 | * m_pSettings->pConnection->user, m_pSettings->pConnection->host); } |
598 | * |
599 | * sprintf(query, "SELECT DISTINCT A.TABLE_CATALOG, B.TABLE_SCHEMA, |
600 | * B.TABLE_NAME, CURRENT_USER(), " \ "A.PRIVILEGE_TYPE FROM |
601 | * INFORMATION_SCHEMA.USER_PRIVILEGES A, INFORMATION_SCHEMA.TABLES B " \ |
602 | * "WHERE B.TABLE_SCHEMA LIKE '%s' AND B.TABLE_NAME LIKE '%s' AND (%s) " \ |
603 | * "UNION " \ "SELECT DISTINCT A.TABLE_CATALOG, B.TABLE_SCHEMA, |
604 | * B.TABLE_NAME, CURRENT_USER(), A.PRIVILEGE_TYPE " \ "FROM |
605 | * INFORMATION_SCHEMA.SCHEMA_PRIVILEGES A, INFORMATION_SCHEMA.TABLES B WHERE " \ |
606 | * "B.TABLE_SCHEMA LIKE '%s' AND B.TABLE_NAME LIKE '%s' AND (%s) " \ "UNION "\ |
607 | * "SELECT DISTINCT A.TABLE_CATALOG, A.TABLE_SCHEMA, A.TABLE_NAME, |
608 | * CURRENT_USER, A.PRIVILEGE_TYPE FROM " \ |
609 | * "INFORMATION_SCHEMA.TABLE_PRIVILEGES A WHERE A.TABLE_SCHEMA LIKE '%s' AND |
610 | * A.TABLE_NAME LIKE '%s' " \ "AND (%s)", schemaName, tableName, user, |
611 | * schemaName, tableName, user, schemaName, tableName, user ); |
612 | */ |
613 | |
614 | /** |
615 | * Get a description of the primary key columns that are referenced by a |
616 | * table's foreign key columns (the primary keys imported by a table). They |
617 | * are ordered by PKTABLE_CAT, PKTABLE_SCHEM, PKTABLE_NAME, and KEY_SEQ. |
618 | * <P> |
619 | * Each primary key column description has the following columns: |
620 | * <OL> |
621 | * <li> <B>PKTABLE_CAT</B> String => primary key table catalog being |
622 | * imported (may be null) </li> |
623 | * <li> <B>PKTABLE_SCHEM</B> String => primary key table schema being |
624 | * imported (may be null) </li> |
625 | * <li> <B>PKTABLE_NAME</B> String => primary key table name being imported |
626 | * </li> |
627 | * <li> <B>PKCOLUMN_NAME</B> String => primary key column name being |
628 | * imported </li> |
629 | * <li> <B>FKTABLE_CAT</B> String => foreign key table catalog (may be |
630 | * null) </li> |
631 | * <li> <B>FKTABLE_SCHEM</B> String => foreign key table schema (may be |
632 | * null) </li> |
633 | * <li> <B>FKTABLE_NAME</B> String => foreign key table name </li> |
634 | * <li> <B>FKCOLUMN_NAME</B> String => foreign key column name </li> |
635 | * <li> <B>KEY_SEQ</B> short => sequence number within foreign key </li> |
636 | * <li> <B>UPDATE_RULE</B> short => What happens to foreign key when |
637 | * primary is updated: |
638 | * <UL> |
639 | * <li> importedKeyCascade - change imported key to agree with primary key |
640 | * update </li> |
641 | * <li> importedKeyRestrict - do not allow update of primary key if it has |
642 | * been imported </li> |
643 | * <li> importedKeySetNull - change imported key to NULL if its primary key |
644 | * has been updated </li> |
645 | * </ul> |
646 | * </li> |
647 | * <li> <B>DELETE_RULE</B> short => What happens to the foreign key when |
648 | * primary is deleted. |
649 | * <UL> |
650 | * <li> importedKeyCascade - delete rows that import a deleted key </li> |
651 | * <li> importedKeyRestrict - do not allow delete of primary key if it has |
652 | * been imported </li> |
653 | * <li> importedKeySetNull - change imported key to NULL if its primary key |
654 | * has been deleted </li> |
655 | * </ul> |
656 | * </li> |
657 | * <li> <B>FK_NAME</B> String => foreign key name (may be null) </li> |
658 | * <li> <B>PK_NAME</B> String => primary key name (may be null) </li> |
659 | * </ol> |
660 | * </p> |
661 | * |
662 | * @param catalog |
663 | * a catalog name; "" retrieves those without a catalog |
664 | * @param schema |
665 | * a schema name pattern; "" retrieves those without a schema |
666 | * @param table |
667 | * a table name |
668 | * @return ResultSet each row is a primary key column description |
669 | * @throws SQLException |
670 | * if a database access error occurs |
671 | * @see #getExportedKeys |
672 | */ |
673 | public java.sql.ResultSet getImportedKeys(String catalog, String schema, |
674 | String table) throws SQLException { |
675 | if (table == null) { |
676 | throw SQLError.createSQLException("Table not specified.", |
677 | SQLError.SQL_STATE_ILLEGAL_ARGUMENT); |
678 | } |
679 | |
680 | if (catalog == null) { |
681 | if (!this.conn.getNullCatalogMeansCurrent()) { |
682 | throw SQLError.createSQLException("'catalog' parameter can not be null", |
683 | SQLError.SQL_STATE_ILLEGAL_ARGUMENT); |
684 | } |
685 | |
686 | catalog = this.database; |
687 | } |
688 | |
689 | String sql = "SELECT " |
690 | + "A.REFERENCED_TABLE_SCHEMA AS PKTABLE_CAT," |
691 | + "NULL AS PKTABLE_SCHEM," |
692 | + "A.REFERENCED_TABLE_NAME AS PKTABLE_NAME," |
693 | + "A.REFERENCED_COLUMN_NAME AS PKCOLUMN_NAME," |
694 | + "A.TABLE_SCHEMA AS FKTABLE_CAT," |
695 | + "NULL AS FKTABLE_SCHEM," |
696 | + "A.TABLE_NAME AS FKTABLE_NAME, " |
697 | + "A.COLUMN_NAME AS FKCOLUMN_NAME, " |
698 | + "A.ORDINAL_POSITION AS KEY_SEQ," |
699 | + importedKeyRestrict |
700 | + " AS UPDATE_RULE," |
701 | + importedKeyRestrict |
702 | + " AS DELETE_RULE," |
703 | + "A.CONSTRAINT_NAME AS FK_NAME," |
704 | + "NULL AS PK_NAME, " |
705 | + importedKeyNotDeferrable |
706 | + " AS DEFERRABILITY " |
707 | + "FROM " |
708 | + "INFORMATION_SCHEMA.KEY_COLUMN_USAGE A, " |
709 | + "INFORMATION_SCHEMA.TABLE_CONSTRAINTS B WHERE A.TABLE_SCHEMA=? " |
710 | + "AND A.CONSTRAINT_NAME=B.CONSTRAINT_NAME AND A.TABLE_NAME=? " |
711 | + "AND " |
712 | + "B.TABLE_NAME=? AND A.REFERENCED_TABLE_SCHEMA IS NOT NULL " |
713 | + " ORDER BY " |
714 | + "A.REFERENCED_TABLE_SCHEMA, A.REFERENCED_TABLE_NAME, " |
715 | + "A.ORDINAL_POSITION"; |
716 | |
717 | PreparedStatement pStmt = null; |
718 | |
719 | try { |
720 | pStmt = prepareMetaDataSafeStatement(sql); |
721 | pStmt.setString(1, catalog); |
722 | pStmt.setString(2, table); |
723 | pStmt.setString(3, table); |
724 | |
725 | ResultSet rs = executeMetadataQuery(pStmt); |
726 | |
727 | ((com.mysql.jdbc.ResultSet) rs).redefineFieldsForDBMD(new Field[] { |
728 | new Field("", "PKTABLE_CAT", Types.CHAR, 255), |
729 | new Field("", "PKTABLE_SCHEM", Types.CHAR, 0), |
730 | new Field("", "PKTABLE_NAME", Types.CHAR, 255), |
731 | new Field("", "PKCOLUMN_NAME", Types.CHAR, 32), |
732 | new Field("", "FKTABLE_CAT", Types.CHAR, 255), |
733 | new Field("", "FKTABLE_SCHEM", Types.CHAR, 0), |
734 | new Field("", "FKTABLE_NAME", Types.CHAR, 255), |
735 | new Field("", "FKCOLUMN_NAME", Types.CHAR, 32), |
736 | new Field("", "KEY_SEQ", Types.SMALLINT, 2), |
737 | new Field("", "UPDATE_RULE", Types.SMALLINT, 2), |
738 | new Field("", "DELETE_RULE", Types.SMALLINT, 2), |
739 | new Field("", "FK_NAME", Types.CHAR, 255), |
740 | new Field("", "PK_NAME", Types.CHAR, 0), |
741 | new Field("", "DEFERRABILITY", Types.INTEGER, 2) }); |
742 | |
743 | return rs; |
744 | } finally { |
745 | if (pStmt != null) { |
746 | pStmt.close(); |
747 | } |
748 | } |
749 | } |
750 | |
751 | /** |
752 | * Get a description of a table's indices and statistics. They are ordered |
753 | * by NON_UNIQUE, TYPE, INDEX_NAME, and ORDINAL_POSITION. |
754 | * <P> |
755 | * Each index column description has the following columns: |
756 | * <OL> |
757 | * <li> <B>TABLE_CAT</B> String => table catalog (may be null) </li> |
758 | * <li> <B>TABLE_SCHEM</B> String => table schema (may be null) </li> |
759 | * <li> <B>TABLE_NAME</B> String => table name </li> |
760 | * <li> <B>NON_UNIQUE</B> boolean => Can index values be non-unique? false |
761 | * when TYPE is tableIndexStatistic </li> |
762 | * <li> <B>INDEX_QUALIFIER</B> String => index catalog (may be null); null |
763 | * when TYPE is tableIndexStatistic </li> |
764 | * <li> <B>INDEX_NAME</B> String => index name; null when TYPE is |
765 | * tableIndexStatistic </li> |
766 | * <li> <B>TYPE</B> short => index type: |
767 | * <UL> |
768 | * <li> tableIndexStatistic - this identifies table statistics that are |
769 | * returned in conjuction with a table's index descriptions </li> |
770 | * <li> tableIndexClustered - this is a clustered index </li> |
771 | * <li> tableIndexHashed - this is a hashed index </li> |
772 | * <li> tableIndexOther - this is some other style of index </li> |
773 | * </ul> |
774 | * </li> |
775 | * <li> <B>ORDINAL_POSITION</B> short => column sequence number within |
776 | * index; zero when TYPE is tableIndexStatistic </li> |
777 | * <li> <B>COLUMN_NAME</B> String => column name; null when TYPE is |
778 | * tableIndexStatistic </li> |
779 | * <li> <B>ASC_OR_DESC</B> String => column sort sequence, "A" => |
780 | * ascending, "D" => descending, may be null if sort sequence is not |
781 | * supported; null when TYPE is tableIndexStatistic </li> |
782 | * <li> <B>CARDINALITY</B> int => When TYPE is tableIndexStatisic then this |
783 | * is the number of rows in the table; otherwise it is the number of unique |
784 | * values in the index. </li> |
785 | * <li> <B>PAGES</B> int => When TYPE is tableIndexStatisic then this is |
786 | * the number of pages used for the table, otherwise it is the number of |
787 | * pages used for the current index. </li> |
788 | * <li> <B>FILTER_CONDITION</B> String => Filter condition, if any. (may be |
789 | * null) </li> |
790 | * </ol> |
791 | * </p> |
792 | * |
793 | * @param catalog |
794 | * a catalog name; "" retrieves those without a catalog |
795 | * @param schema |
796 | * a schema name pattern; "" retrieves those without a schema |
797 | * @param table |
798 | * a table name |
799 | * @param unique |
800 | * when true, return only indices for unique values; when false, |
801 | * return indices regardless of whether unique or not |
802 | * @param approximate |
803 | * when true, result is allowed to reflect approximate or out of |
804 | * data values; when false, results are requested to be accurate |
805 | * @return ResultSet each row is an index column description |
806 | * @throws SQLException |
807 | * DOCUMENT ME! |
808 | */ |
809 | public ResultSet getIndexInfo(String catalog, String schema, String table, |
810 | boolean unique, boolean approximate) throws SQLException { |
811 | StringBuffer sqlBuf = new StringBuffer("SELECT " |
812 | + "TABLE_SCHEMA AS TABLE_CAT, " + "NULL AS TABLE_SCHEM," |
813 | + "TABLE_NAME," + "NON_UNIQUE," |
814 | + "TABLE_SCHEMA AS INDEX_QUALIFIER," + "INDEX_NAME," |
815 | + tableIndexOther + " AS TYPE," |
816 | + "SEQ_IN_INDEX AS ORDINAL_POSITION," + "COLUMN_NAME," |
817 | + "COLLATION AS ASC_OR_DESC," + "CARDINALITY," |
818 | + "NULL AS PAGES," + "NULL AS FILTER_CONDITION " |
819 | + "FROM INFORMATION_SCHEMA.STATISTICS WHERE " |
820 | + "TABLE_SCHEMA LIKE ? AND " + "TABLE_NAME LIKE ?"); |
821 | |
822 | if (unique) { |
823 | sqlBuf.append(" AND NON_UNIQUE=0 "); |
824 | } |
825 | |
826 | sqlBuf.append("ORDER BY NON_UNIQUE, INDEX_NAME, SEQ_IN_INDEX"); |
827 | |
828 | PreparedStatement pStmt = null; |
829 | |
830 | try { |
831 | pStmt = prepareMetaDataSafeStatement(sqlBuf.toString()); |
832 | |
833 | pStmt.setString(1, catalog); |
834 | pStmt.setString(2, table); |
835 | |
836 | ResultSet rs = executeMetadataQuery(pStmt); |
837 | |
838 | ((com.mysql.jdbc.ResultSet) rs).redefineFieldsForDBMD(new Field[] { |
839 | new Field("", "TABLE_CAT", Types.CHAR, 255), |
840 | new Field("", "TABLE_SCHEM", Types.CHAR, 0), |
841 | new Field("", "TABLE_NAME", Types.CHAR, 255), |
842 | new Field("", "NON_UNIQUE", Types.CHAR, 4), |
843 | new Field("", "INDEX_QUALIFIER", Types.CHAR, 1), |
844 | new Field("", "INDEX_NAME", Types.CHAR, 32), |
845 | new Field("", "TYPE", Types.CHAR, 32), |
846 | new Field("", "ORDINAL_POSITION", Types.SMALLINT, 5), |
847 | new Field("", "COLUMN_NAME", Types.CHAR, 32), |
848 | new Field("", "ASC_OR_DESC", Types.CHAR, 1), |
849 | new Field("", "CARDINALITY", Types.INTEGER, 10), |
850 | new Field("", "PAGES", Types.INTEGER, 10), |
851 | new Field("", "FILTER_CONDITION", Types.CHAR, 32) }); |
852 | |
853 | return rs; |
854 | } finally { |
855 | if (pStmt != null) { |
856 | pStmt.close(); |
857 | } |
858 | } |
859 | } |
860 | |
861 | /** |
862 | * Get a description of a table's primary key columns. They are ordered by |
863 | * COLUMN_NAME. |
864 | * <P> |
865 | * Each column description has the following columns: |
866 | * <OL> |
867 | * <li> <B>TABLE_CAT</B> String => table catalog (may be null) </li> |
868 | * <li> <B>TABLE_SCHEM</B> String => table schema (may be null) </li> |
869 | * <li> <B>TABLE_NAME</B> String => table name </li> |
870 | * <li> <B>COLUMN_NAME</B> String => column name </li> |
871 | * <li> <B>KEY_SEQ</B> short => sequence number within primary key </li> |
872 | * <li> <B>PK_NAME</B> String => primary key name (may be null) </li> |
873 | * </ol> |
874 | * </p> |
875 | * |
876 | * @param catalog |
877 | * a catalog name; "" retrieves those without a catalog |
878 | * @param schema |
879 | * a schema name pattern; "" retrieves those without a schema |
880 | * @param table |
881 | * a table name |
882 | * @return ResultSet each row is a primary key column description |
883 | * @throws SQLException |
884 | * DOCUMENT ME! |
885 | */ |
886 | public java.sql.ResultSet getPrimaryKeys(String catalog, String schema, |
887 | String table) throws SQLException { |
888 | |
889 | if (catalog == null) { |
890 | if (!this.conn.getNullCatalogMeansCurrent()) { |
891 | throw SQLError.createSQLException("'catalog' parameter can not be null", |
892 | SQLError.SQL_STATE_ILLEGAL_ARGUMENT); |
893 | } |
894 | |
895 | catalog = this.database; |
896 | } |
897 | |
898 | if (table == null) { |
899 | throw SQLError.createSQLException("Table not specified.", |
900 | SQLError.SQL_STATE_ILLEGAL_ARGUMENT); |
901 | } |
902 | |
903 | String sql = "SELECT TABLE_SCHEMA AS TABLE_CAT, NULL AS TABLE_SCHEM, TABLE_NAME, " |
904 | + "COLUMN_NAME, SEQ_IN_INDEX AS KEY_SEQ, 'PRIMARY' AS PK_NAME FROM INFORMATION_SCHEMA.STATISTICS " |
905 | + "WHERE TABLE_SCHEMA LIKE ? AND TABLE_NAME LIKE ? AND " |
906 | + "INDEX_NAME='PRIMARY' ORDER BY TABLE_SCHEMA, TABLE_NAME, INDEX_NAME, SEQ_IN_INDEX"; |
907 | |
908 | PreparedStatement pStmt = null; |
909 | |
910 | try { |
911 | pStmt = prepareMetaDataSafeStatement(sql); |
912 | |
913 | pStmt.setString(1, catalog); |
914 | pStmt.setString(2, table); |
915 | |
916 | ResultSet rs = executeMetadataQuery(pStmt); |
917 | ((com.mysql.jdbc.ResultSet) rs).redefineFieldsForDBMD(new Field[] { |
918 | new Field("", "TABLE_CAT", Types.CHAR, 255), |
919 | new Field("", "TABLE_SCHEM", Types.CHAR, 0), |
920 | new Field("", "TABLE_NAME", Types.CHAR, 255), |
921 | new Field("", "COLUMN_NAME", Types.CHAR, 32), |
922 | new Field("", "KEY_SEQ", Types.SMALLINT, 5), |
923 | new Field("", "PK_NAME", Types.CHAR, 32) }); |
924 | |
925 | return rs; |
926 | } finally { |
927 | if (pStmt != null) { |
928 | pStmt.close(); |
929 | } |
930 | } |
931 | } |
932 | |
933 | /** |
934 | * Get a description of stored procedures available in a catalog. |
935 | * <P> |
936 | * Only procedure descriptions matching the schema and procedure name |
937 | * criteria are returned. They are ordered by PROCEDURE_SCHEM, and |
938 | * PROCEDURE_NAME. |
939 | * </p> |
940 | * <P> |
941 | * Each procedure description has the the following columns: |
942 | * <OL> |
943 | * <li> <B>PROCEDURE_CAT</B> String => procedure catalog (may be null) |
944 | * </li> |
945 | * <li> <B>PROCEDURE_SCHEM</B> String => procedure schema (may be null) |
946 | * </li> |
947 | * <li> <B>PROCEDURE_NAME</B> String => procedure name </li> |
948 | * <li> reserved for future use </li> |
949 | * <li> reserved for future use </li> |
950 | * <li> reserved for future use </li> |
951 | * <li> <B>REMARKS</B> String => explanatory comment on the procedure </li> |
952 | * <li> <B>PROCEDURE_TYPE</B> short => kind of procedure: |
953 | * <UL> |
954 | * <li> procedureResultUnknown - May return a result </li> |
955 | * <li> procedureNoResult - Does not return a result </li> |
956 | * <li> procedureReturnsResult - Returns a result </li> |
957 | * </ul> |
958 | * </li> |
959 | * </ol> |
960 | * </p> |
961 | * |
962 | * @param catalog |
963 | * a catalog name; "" retrieves those without a catalog |
964 | * @param schemaPattern |
965 | * a schema name pattern; "" retrieves those without a schema |
966 | * @param procedureNamePattern |
967 | * a procedure name pattern |
968 | * @return ResultSet each row is a procedure description |
969 | * @throws SQLException |
970 | * if a database access error occurs |
971 | * @see #getSearchStringEscape |
972 | */ |
973 | public ResultSet getProcedures(String catalog, String schemaPattern, |
974 | String procedureNamePattern) throws SQLException { |
975 | |
976 | if ((procedureNamePattern == null) |
977 | || (procedureNamePattern.length() == 0)) { |
978 | if (this.conn.getNullNamePatternMatchesAll()) { |
979 | procedureNamePattern = "%"; |
980 | } else { |
981 | throw SQLError.createSQLException( |
982 | "Procedure name pattern can not be NULL or empty.", |
983 | SQLError.SQL_STATE_ILLEGAL_ARGUMENT); |
984 | } |
985 | } |
986 | |
987 | String db = null; |
988 | |
989 | if (catalog == null) { |
990 | db = this.database; |
991 | } else if (catalog.length() > 0) { |
992 | db = catalog; |
993 | } else { |
994 | if (!this.conn.getNullCatalogMeansCurrent()) { |
995 | throw SQLError.createSQLException("'catalog' parameter can not be null", |
996 | SQLError.SQL_STATE_ILLEGAL_ARGUMENT); |
997 | } |
998 | |
999 | catalog = null; |
1000 | db = null; |
1001 | } |
1002 | |
1003 | String sql = "SELECT ROUTINE_SCHEMA AS PROCEDURE_CAT, " |
1004 | + "NULL AS PROCEDURE_SCHEM, " |
1005 | + "ROUTINE_NAME AS PROCEDURE_NAME, " + "NULL AS RESERVED_1, " |
1006 | + "NULL AS RESERVED_2, " + "NULL AS RESERVED_3, " |
1007 | + "ROUTINE_COMMENT AS REMARKS, " |
1008 | + "CASE WHEN ROUTINE_TYPE = 'PROCEDURE' THEN " |
1009 | + procedureNoResult + " WHEN ROUTINE_TYPE='FUNCTION' THEN " |
1010 | + procedureReturnsResult + " ELSE " + procedureResultUnknown |
1011 | + " END AS PROCEDURE_TYPE " |
1012 | + "FROM INFORMATION_SCHEMA.ROUTINES WHERE " |
1013 | + "ROUTINE_SCHEMA LIKE ? AND ROUTINE_NAME LIKE ? " |
1014 | + "ORDER BY ROUTINE_SCHEMA, ROUTINE_NAME"; |
1015 | |
1016 | PreparedStatement pStmt = null; |
1017 | |
1018 | try { |
1019 | pStmt = prepareMetaDataSafeStatement(sql); |
1020 | pStmt.setString(1, db); |
1021 | pStmt.setString(2, procedureNamePattern); |
1022 | |
1023 | ResultSet rs = executeMetadataQuery(pStmt); |
1024 | ((com.mysql.jdbc.ResultSet) rs).redefineFieldsForDBMD(new Field[] { |
1025 | new Field("", "PROCEDURE_CAT", Types.CHAR, 0), |
1026 | new Field("", "PROCEDURE_SCHEM", Types.CHAR, 0), |
1027 | new Field("", "PROCEDURE_NAME", Types.CHAR, 0), |
1028 | new Field("", "reserved1", Types.CHAR, 0), |
1029 | new Field("", "reserved2", Types.CHAR, 0), |
1030 | new Field("", "reserved3", Types.CHAR, 0), |
1031 | new Field("", "REMARKS", Types.CHAR, 0), |
1032 | new Field("", "PROCEDURE_TYPE", Types.SMALLINT, 0) }); |
1033 | |
1034 | return rs; |
1035 | } finally { |
1036 | if (pStmt != null) { |
1037 | pStmt.close(); |
1038 | } |
1039 | } |
1040 | } |
1041 | |
1042 | /** |
1043 | * Get a description of tables available in a catalog. |
1044 | * <P> |
1045 | * Only table descriptions matching the catalog, schema, table name and type |
1046 | * criteria are returned. They are ordered by TABLE_TYPE, TABLE_SCHEM and |
1047 | * TABLE_NAME. |
1048 | * </p> |
1049 | * <P> |
1050 | * Each table description has the following columns: |
1051 | * <OL> |
1052 | * <li> <B>TABLE_CAT</B> String => table catalog (may be null) </li> |
1053 | * <li> <B>TABLE_SCHEM</B> String => table schema (may be null) </li> |
1054 | * <li> <B>TABLE_NAME</B> String => table name </li> |
1055 | * <li> <B>TABLE_TYPE</B> String => table type. Typical types are "TABLE", |
1056 | * "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", "LOCAL TEMPORARY", "ALIAS", |
1057 | * "SYNONYM". </li> |
1058 | * <li> <B>REMARKS</B> String => explanatory comment on the table </li> |
1059 | * </ol> |
1060 | * </p> |
1061 | * <P> |
1062 | * <B>Note:</B> Some databases may not return information for all tables. |
1063 | * </p> |
1064 | * |
1065 | * @param catalog |
1066 | * a catalog name; "" retrieves those without a catalog |
1067 | * @param schemaPattern |
1068 | * a schema name pattern; "" retrieves those without a schema |
1069 | * @param tableNamePattern |
1070 | * a table name pattern |
1071 | * @param types |
1072 | * a list of table types to include; null returns all types |
1073 | * @return ResultSet each row is a table description |
1074 | * @throws SQLException |
1075 | * DOCUMENT ME! |
1076 | * @see #getSearchStringEscape |
1077 | */ |
1078 | public ResultSet getTables(String catalog, String schemaPattern, |
1079 | String tableNamePattern, String[] types) throws SQLException { |
1080 | if (catalog == null) { |
1081 | if (!this.conn.getNullCatalogMeansCurrent()) { |
1082 | throw SQLError.createSQLException("'catalog' parameter can not be null", |
1083 | SQLError.SQL_STATE_ILLEGAL_ARGUMENT); |
1084 | } |
1085 | |
1086 | catalog = this.database; |
1087 | } |
1088 | |
1089 | if (tableNamePattern == null) { |
1090 | if (this.conn.getNullNamePatternMatchesAll()) { |
1091 | tableNamePattern = "%"; |
1092 | } else { |
1093 | throw SQLError.createSQLException( |
1094 | "Table name pattern can not be NULL or empty.", |
1095 | SQLError.SQL_STATE_ILLEGAL_ARGUMENT); |
1096 | } |
1097 | } |
1098 | |
1099 | PreparedStatement pStmt = null; |
1100 | |
1101 | String sql = "SELECT TABLE_SCHEMA AS TABLE_CAT, " |
1102 | + "NULL AS TABLE_SCHEM, TABLE_NAME, " |
1103 | + "CASE WHEN TABLE_TYPE='BASE TABLE' THEN 'TABLE' WHEN TABLE_TYPE='TEMPORARY' THEN 'LOCAL_TEMPORARY' ELSE TABLE_TYPE END AS TABLE_TYPE, " |
1104 | + "TABLE_COMMENT AS REMARKS " |
1105 | + "FROM INFORMATION_SCHEMA.TABLES WHERE " |
1106 | + "TABLE_SCHEMA LIKE ? AND TABLE_NAME LIKE ? AND TABLE_TYPE IN (?,?,?) " |
1107 | + "ORDER BY TABLE_TYPE, TABLE_SCHEMA, TABLE_NAME"; |
1108 | try { |
1109 | pStmt = prepareMetaDataSafeStatement(sql); |
1110 | pStmt.setString(1, catalog); |
1111 | pStmt.setString(2, tableNamePattern); |
1112 | |
1113 | // This overloading of IN (...) allows us to cache this |
1114 | // prepared statement |
1115 | if (types == null || types.length == 0) { |
1116 | pStmt.setString(3, "BASE TABLE"); |
1117 | pStmt.setString(4, "VIEW"); |
1118 | pStmt.setString(5, "TEMPORARY"); |
1119 | } else { |
1120 | pStmt.setNull(3, Types.VARCHAR); |
1121 | pStmt.setNull(4, Types.VARCHAR); |
1122 | pStmt.setNull(5, Types.VARCHAR); |
1123 | |
1124 | for (int i = 0; i < types.length; i++) { |
1125 | if ("TABLE".equalsIgnoreCase(types[i])) { |
1126 | pStmt.setString(3, "BASE TABLE"); |
1127 | } |
1128 | |
1129 | if ("VIEW".equalsIgnoreCase(types[i])) { |
1130 | pStmt.setString(4, "VIEW"); |
1131 | } |
1132 | |
1133 | if ("LOCAL TEMPORARY".equalsIgnoreCase(types[i])) { |
1134 | pStmt.setString(5, "TEMPORARY"); |
1135 | } |
1136 | } |
1137 | } |
1138 | |
1139 | ResultSet rs = executeMetadataQuery(pStmt); |
1140 | |
1141 | ((com.mysql.jdbc.ResultSet) rs).redefineFieldsForDBMD(new Field[] { |
1142 | new Field("", "TABLE_CAT", java.sql.Types.VARCHAR, |
1143 | (catalog == null) ? 0 : catalog.length()), |
1144 | new Field("", "TABLE_SCHEM", java.sql.Types.VARCHAR, 0), |
1145 | new Field("", "TABLE_NAME", java.sql.Types.VARCHAR, 255), |
1146 | new Field("", "TABLE_TYPE", java.sql.Types.VARCHAR, 5), |
1147 | new Field("", "REMARKS", java.sql.Types.VARCHAR, 0) }); |
1148 | |
1149 | return rs; |
1150 | } finally { |
1151 | if (pStmt != null) { |
1152 | pStmt.close(); |
1153 | } |
1154 | } |
1155 | } |
1156 | |
1157 | private PreparedStatement prepareMetaDataSafeStatement(String sql) |
1158 | throws SQLException { |
1159 | // Can't use server-side here as we coerce a lot of types to match |
1160 | // the spec. |
1161 | PreparedStatement pStmt = this.conn.clientPrepareStatement(sql); |
1162 | |
1163 | if (pStmt.getMaxRows() != 0) { |
1164 | pStmt.setMaxRows(0); |
1165 | } |
1166 | |
1167 | pStmt.setHoldResultsOpenOverClose(true); |
1168 | |
1169 | return pStmt; |
1170 | } |
1171 | } |