Mercurial > hg > monetdb-java
comparison src/main/java/org/monetdb/jdbc/MonetDatabaseMetaData.java @ 391:f523727db392
Moved Java classes from packages starting with nl.cwi.monetdb.* to package org.monetdb.*
This naming complies to the Java Package Naming convention as MonetDB's main website is www.monetdb.org.
author | Martin van Dinther <martin.van.dinther@monetdbsolutions.com> |
---|---|
date | Thu, 12 Nov 2020 22:02:01 +0100 (2020-11-12) |
parents | src/main/java/nl/cwi/monetdb/jdbc/MonetDatabaseMetaData.java@3c224b31bd29 |
children | 6242351e8802 |
comparison
equal
deleted
inserted
replaced
390:6199e0be3c6e | 391:f523727db392 |
---|---|
1 /* | |
2 * This Source Code Form is subject to the terms of the Mozilla Public | |
3 * License, v. 2.0. If a copy of the MPL was not distributed with this | |
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. | |
5 * | |
6 * Copyright 1997 - July 2008 CWI, August 2008 - 2020 MonetDB B.V. | |
7 */ | |
8 | |
9 package org.monetdb.jdbc; | |
10 | |
11 import java.sql.Connection; | |
12 import java.sql.DatabaseMetaData; | |
13 import java.sql.Statement; | |
14 import java.sql.SQLException; | |
15 import java.sql.ResultSet; | |
16 import java.sql.ResultSetMetaData; | |
17 import java.sql.RowIdLifetime; | |
18 import java.sql.Types; | |
19 | |
20 /** | |
21 * A DatabaseMetaData object suitable for the MonetDB database. | |
22 * | |
23 * @author Fabian Groffen | |
24 * @author Martin van Dinther | |
25 * @version 1.0 | |
26 */ | |
27 public class MonetDatabaseMetaData | |
28 extends MonetWrapper | |
29 implements DatabaseMetaData | |
30 { | |
31 private final MonetConnection con; | |
32 | |
33 public MonetDatabaseMetaData(final MonetConnection parent) { | |
34 con = parent; | |
35 } | |
36 | |
37 /** | |
38 * Retrieves whether the current user can call all the procedures | |
39 * returned by the method getProcedures. | |
40 * | |
41 * @return false because we currently return all procedures from sys.functions | |
42 * and do not filter on EXECUTE privilege or procedure ownership. | |
43 */ | |
44 @Override | |
45 public boolean allProceduresAreCallable() { | |
46 return false; | |
47 } | |
48 | |
49 /** | |
50 * Retrieves whether the current user can use all the tables | |
51 * returned by the method getTables in a SELECT statement. | |
52 * | |
53 * @return false because we currently return all tables from sys.tables | |
54 * and do not filter on SELECT privilege or table ownership. | |
55 */ | |
56 @Override | |
57 public boolean allTablesAreSelectable() { | |
58 return false; | |
59 } | |
60 | |
61 /** | |
62 * What is the URL for this database? | |
63 * | |
64 * @return a reconstructed connection string | |
65 * @throws SQLException if a database access error occurs | |
66 */ | |
67 @Override | |
68 public String getURL() throws SQLException { | |
69 return con.getJDBCURL(); | |
70 } | |
71 | |
72 /** | |
73 * What is our user name as known to the database? | |
74 * | |
75 * @return sql user | |
76 * @throws SQLException if a database access error occurs | |
77 */ | |
78 @Override | |
79 public String getUserName() throws SQLException { | |
80 return con.getUserName(); | |
81 } | |
82 | |
83 /** | |
84 * Is the database in read-only mode? | |
85 * | |
86 * @return always false for now | |
87 */ | |
88 @Override | |
89 public boolean isReadOnly() { | |
90 return false; | |
91 } | |
92 | |
93 /** | |
94 * Retrieves whether NULL values are sorted high. Sorted high means | |
95 * that NULL values sort higher than any other value in a domain. | |
96 * In an ascending order, if this method returns true, NULL values will appear at the end. | |
97 * By contrast, the method nullsAreSortedAtEnd indicates whether NULL values are sorted at the end regardless of sort order. | |
98 * | |
99 * @return false because MonetDB shows NULL values at the beginning upon ORDER BY .. ASC | |
100 */ | |
101 @Override | |
102 public boolean nullsAreSortedHigh() { | |
103 return false; | |
104 } | |
105 | |
106 /** | |
107 * Retrieves whether NULL values are sorted low. Sorted low means | |
108 * that NULL values sort lower than any other value in a domain. | |
109 * In an ascending order, if this method returns true, NULL values will appear at the beginning. | |
110 * By contrast, the method nullsAreSortedAtStart indicates whether NULL values are sorted at the beginning regardless of sort order. | |
111 * | |
112 * @return true because MonetDB shows NULL values at the beginning upon ORDER BY .. ASC | |
113 * | |
114 * @return negative of nullsAreSortedHigh() | |
115 * @see #nullsAreSortedHigh() | |
116 */ | |
117 @Override | |
118 public boolean nullsAreSortedLow() { | |
119 return true; | |
120 } | |
121 | |
122 /** | |
123 * Are NULL values sorted at the start regardless of sort order? | |
124 * | |
125 * @return false, since MonetDB doesn't do this | |
126 */ | |
127 @Override | |
128 public boolean nullsAreSortedAtStart() { | |
129 return false; | |
130 } | |
131 | |
132 /** | |
133 * Are NULL values sorted at the end regardless of sort order? | |
134 * | |
135 * @return false, since MonetDB doesn't do this | |
136 */ | |
137 @Override | |
138 public boolean nullsAreSortedAtEnd() { | |
139 return false; | |
140 } | |
141 | |
142 /** | |
143 * What is the name of this database product - this should be MonetDB | |
144 * of course, so we return that explicitly. | |
145 * | |
146 * @return the database product name | |
147 */ | |
148 @Override | |
149 public String getDatabaseProductName() { | |
150 return "MonetDB"; | |
151 } | |
152 | |
153 /** | |
154 * What is the version of this database product. | |
155 * | |
156 * @return the mserver5 version number string | |
157 * @throws SQLException if a database access error occurs | |
158 */ | |
159 @Override | |
160 public String getDatabaseProductVersion() throws SQLException { | |
161 return con.getDatabaseProductVersion(); | |
162 } | |
163 | |
164 /** | |
165 * What is the name of this JDBC driver? | |
166 * | |
167 * @return the JDBC driver name | |
168 */ | |
169 @Override | |
170 public String getDriverName() { | |
171 return "MonetDB Native Driver"; | |
172 } | |
173 | |
174 /** | |
175 * Retrieves the version number of this JDBC driver as a String. | |
176 * | |
177 * @return the JDBC driver version string | |
178 */ | |
179 @Override | |
180 public String getDriverVersion() { | |
181 return MonetDriver.getDriverVersion(); | |
182 } | |
183 | |
184 /** | |
185 * What is this JDBC driver's major version number? | |
186 * | |
187 * @return the JDBC driver major version number | |
188 */ | |
189 @Override | |
190 public int getDriverMajorVersion() { | |
191 return MonetDriver.getDriverMajorVersion(); | |
192 } | |
193 | |
194 /** | |
195 * What is this JDBC driver's minor version number? | |
196 * | |
197 * @return the JDBC driver minor version number | |
198 */ | |
199 @Override | |
200 public int getDriverMinorVersion() { | |
201 return MonetDriver.getDriverMinorVersion(); | |
202 } | |
203 | |
204 /** | |
205 * Does the database store tables in a local file? | |
206 * No, it stores them in files on the server. | |
207 * | |
208 * @return false because that's what MonetDB is for | |
209 */ | |
210 @Override | |
211 public boolean usesLocalFiles() { | |
212 return false; | |
213 } | |
214 | |
215 /** | |
216 * Does the database use a local file for each table? | |
217 * | |
218 * @return false for it doesn't | |
219 */ | |
220 @Override | |
221 public boolean usesLocalFilePerTable() { | |
222 return false; | |
223 } | |
224 | |
225 /** | |
226 * Does the database treat mixed case unquoted SQL identifiers | |
227 * as case sensitive and as a result store them in mixed case? | |
228 * A JDBC Compliant <sup>TM</sup> driver always returns false. | |
229 * | |
230 * @return false | |
231 */ | |
232 @Override | |
233 public boolean supportsMixedCaseIdentifiers() { | |
234 return false; | |
235 } | |
236 | |
237 /** | |
238 * Does the database treat mixed case unquoted SQL identifiers as | |
239 * case insensitive and store them in upper case? | |
240 * | |
241 * @return true if so | |
242 */ | |
243 @Override | |
244 public boolean storesUpperCaseIdentifiers() { | |
245 return false; | |
246 } | |
247 | |
248 /** | |
249 * Does the database treat mixed case unquoted SQL identifiers as | |
250 * case insensitive and store them in lower case? | |
251 * | |
252 * @return true if so | |
253 */ | |
254 @Override | |
255 public boolean storesLowerCaseIdentifiers() { | |
256 return true; | |
257 } | |
258 | |
259 /** | |
260 * Does the database treat mixed case unquoted SQL identifiers as | |
261 * case insensitive and store them in mixed case? | |
262 * | |
263 * @return true if so | |
264 */ | |
265 @Override | |
266 public boolean storesMixedCaseIdentifiers() { | |
267 return false; | |
268 } | |
269 | |
270 /** | |
271 * Does the database treat mixed case quoted SQL identifiers as | |
272 * case sensitive and as a result store them in mixed case? | |
273 * A JDBC Compliant <sup>TM</sup> driver always returns true. | |
274 * | |
275 * @return true if so | |
276 */ | |
277 @Override | |
278 public boolean supportsMixedCaseQuotedIdentifiers() { | |
279 return true; | |
280 } | |
281 | |
282 /** | |
283 * Does the database treat mixed case quoted SQL identifiers as | |
284 * case insensitive and store them in upper case? | |
285 * | |
286 * @return true if so | |
287 */ | |
288 @Override | |
289 public boolean storesUpperCaseQuotedIdentifiers() { | |
290 return false; | |
291 } | |
292 | |
293 /** | |
294 * Does the database treat mixed case quoted SQL identifiers as case | |
295 * insensitive and store them in lower case? | |
296 * | |
297 * @return true if so | |
298 */ | |
299 @Override | |
300 public boolean storesLowerCaseQuotedIdentifiers() { | |
301 return false; | |
302 } | |
303 | |
304 /** | |
305 * Does the database treat mixed case quoted SQL identifiers as case | |
306 * insensitive and store them in mixed case? | |
307 * | |
308 * @return true if so | |
309 */ | |
310 @Override | |
311 public boolean storesMixedCaseQuotedIdentifiers() { | |
312 return false; | |
313 } | |
314 | |
315 /** | |
316 * What is the string used to quote SQL identifiers? | |
317 * This returns a space if identifier quoting isn't supported. | |
318 * A JDBC Compliant <sup>TM</sup> driver | |
319 * will always use a double quote character. | |
320 * | |
321 * @return the quoting string | |
322 */ | |
323 @Override | |
324 public String getIdentifierQuoteString() { | |
325 return "\""; | |
326 } | |
327 | |
328 /** | |
329 * Get a comma separated list of all a database's SQL keywords that | |
330 * are NOT also SQL:2003 keywords. | |
331 * | |
332 * @return a comma separated list of MonetDB keywords | |
333 */ | |
334 @Override | |
335 public String getSQLKeywords() { | |
336 final String keywords = getConcatenatedStringFromQuery("SELECT \"keyword\" FROM \"sys\".\"keywords\" ORDER BY 1"); | |
337 | |
338 /* An old MonetDB server (pre Jul2015 release) will not have a table sys.keywords and return an empty String */ | |
339 return (keywords.isEmpty()) ? | |
340 /* for old servers return static list (as returned in clients/odbc/driver/SQLGetInfo.c case SQL_KEYWORDS:) */ | |
341 "ADMIN,AFTER,AGGREGATE,ALWAYS,ASYMMETRIC,ATOMIC," + | |
342 "AUTO_INCREMENT,BEFORE,BIGINT,BIGSERIAL,BINARY,BLOB," + | |
343 "CALL,CHAIN,CLOB,COMMITTED,COPY,CORR,CUME_DIST," + | |
344 "CURRENT_ROLE,CYCLE,DATABASE,DELIMITERS,DENSE_RANK," + | |
345 "DO,EACH,ELSEIF,ENCRYPTED,EVERY,EXCLUDE,FOLLOWING," + | |
346 "FUNCTION,GENERATED,IF,ILIKE,INCREMENT,LAG,LEAD," + | |
347 "LIMIT,LOCALTIME,LOCALTIMESTAMP,LOCKED,MAXVALUE," + | |
348 "MEDIAN,MEDIUMINT,MERGE,MINVALUE,NEW,NOCYCLE," + | |
349 "NOMAXVALUE,NOMINVALUE,NOW,OFFSET,OLD,OTHERS,OVER," + | |
350 "PARTITION,PERCENT_RANK,PLAN,PRECEDING,PROD,QUANTILE," + | |
351 "RANGE,RANK,RECORDS,REFERENCING,REMOTE,RENAME," + | |
352 "REPEATABLE,REPLICA,RESTART,RETURN,RETURNS," + | |
353 "ROW_NUMBER,ROWS,SAMPLE,SAVEPOINT,SCHEMA,SEQUENCE," + | |
354 "SERIAL,SERIALIZABLE,SIMPLE,START,STATEMENT,STDIN," + | |
355 "STDOUT,STREAM,STRING,SYMMETRIC,TIES,TINYINT,TRIGGER," + | |
356 "UNBOUNDED,UNCOMMITTED,UNENCRYPTED,WHILE,XMLAGG," + | |
357 "XMLATTRIBUTES,XMLCOMMENT,XMLCONCAT,XMLDOCUMENT," + | |
358 "XMLELEMENT,XMLFOREST,XMLNAMESPACES,XMLPARSE,XMLPI," + | |
359 "XMLQUERY,XMLSCHEMA,XMLTEXT,XMLVALIDATE" | |
360 : keywords; | |
361 } | |
362 | |
363 /** | |
364 * Internal utility method getConcatenatedStringFromQuery(String query) | |
365 * args: query: SQL SELECT query. Only the output of the first column is concatenated. | |
366 * @return a String of query result values concatenated into one string, and values separated by comma's | |
367 */ | |
368 private String getConcatenatedStringFromQuery(final String query) { | |
369 final StringBuilder sb = new StringBuilder(1024); | |
370 Statement st = null; | |
371 ResultSet rs = null; | |
372 try { | |
373 st = con.createStatement(); | |
374 rs = st.executeQuery(query); | |
375 // Fetch the first column output and concatenate the values into a StringBuilder separated by comma's | |
376 boolean isfirst = true; | |
377 while (rs.next()) { | |
378 String value = rs.getString(1); | |
379 if (value != null) { | |
380 if (isfirst) { | |
381 isfirst = false; | |
382 } else { | |
383 sb.append(','); | |
384 } | |
385 sb.append(value); | |
386 } | |
387 } | |
388 } catch (SQLException e) { | |
389 /* ignore */ | |
390 } finally { | |
391 MonetConnection.closeResultsetStatement(rs, st); | |
392 } | |
393 // for debug: System.out.println("SQL (len " + query.length() + "): " + query + "\nResult string: " + sb.toString()); | |
394 return sb.toString(); | |
395 } | |
396 | |
397 // SQL query parts shared by four get<Type>Functions() below | |
398 private static final String FunctionsSelect = "SELECT DISTINCT CASE WHEN \"language\" > 0 THEN s.\"name\"||'.'||f.\"name\" ELSE f.\"name\" END FROM \"sys\".\"functions\" f JOIN \"sys\".\"schemas\" s ON f.\"schema_id\" = s.\"id\" WHERE "; | |
399 private static final String FunctionsWhere = "(f.\"id\" IN (SELECT \"func_id\" FROM \"sys\".\"args\" WHERE \"number\" = 1 AND \"name\" = 'arg_1' AND \"type\" IN "; | |
400 // Scalar functions sql_max(x,y), sql_min(x,y), greatest(x,y) and least(x,y) are defined in sys.args for type 'any' and usable as num, str and timedate functions. | |
401 private static final String OrFunctionsMaxMin = " OR f.\"name\" IN ('sql_max','sql_min','least','greatest')"; | |
402 private static final String FunctionsOrderBy1 = " ORDER BY 1"; | |
403 | |
404 @Override | |
405 public String getNumericFunctions() { | |
406 final String match = | |
407 "('tinyint','smallint','int','bigint','hugeint','decimal','double','real'))" + | |
408 " AND \"type\" = 1" + // only scalar functions | |
409 // exclude functions which do not work or belong to string functions (code(int) and space(int)) | |
410 " AND f.\"name\" NOT IN ('code','not_uniques','rotate_xor_hash','space'))" + | |
411 // include specific functions which have no arguments (pi()) or are numeric. Actually ms_str is a conversion function from float to str. | |
412 " OR f.\"name\" IN ('alpha','degrees','fuse','ms_round','ms_str','ms_trunc','pi','radians')"; | |
413 return getConcatenatedStringFromQuery(FunctionsSelect + FunctionsWhere + match + OrFunctionsMaxMin + FunctionsOrderBy1); | |
414 } | |
415 | |
416 @Override | |
417 public String getStringFunctions() { | |
418 final String match = | |
419 "('char','varchar','clob','json','url'))" + | |
420 " AND \"type\" = 1" + // only scalar functions | |
421 // exclude sql functions: get_value_for, next_value_for, restart | |
422 " AND \"mod\" <> 'sql')" + | |
423 // include specific functions code(int) and space(int) which belong to the 'str' module | |
424 " OR \"mod\" = 'str'" + | |
425 // include 3 specific json functions, md5(), ms_stuff() and udf reverse() which all accept a string arg | |
426 " OR f.\"name\" IN ('isarray','isobject','isvalid','md5','ms_stuff','reverse')"; | |
427 final String unionPart = | |
428 // add functions which are not listed in sys.functions but implemented in the SQL parser (see sql/server/sql_parser.y) | |
429 " UNION SELECT 'position'"; | |
430 return getConcatenatedStringFromQuery(FunctionsSelect + FunctionsWhere + match + OrFunctionsMaxMin + unionPart + FunctionsOrderBy1); | |
431 } | |
432 | |
433 @Override | |
434 public String getSystemFunctions() { | |
435 final String wherePart = | |
436 "f.\"name\" IN ('columnsize','debug','get_value_for','hash','hashsize','heapsize'" + | |
437 ",'ifthenelse','imprintsize','isaurl','isauuid','isnull','masterclock','mastertick'" + | |
438 ",'newurl','next_value_for','password_hash','replicaclock','replicatick','uuid')" + | |
439 // add functions which are not listed in sys.functions but implemented in the SQL parser (see sql/server/sql_parser.y) | |
440 " UNION ALL SELECT * FROM (VALUES('cast'),('coalesce'),('convert'),('nullif')) as sf"; | |
441 return getConcatenatedStringFromQuery(FunctionsSelect + wherePart + FunctionsOrderBy1); | |
442 } | |
443 | |
444 @Override | |
445 public String getTimeDateFunctions() { | |
446 final String wherePart = | |
447 "\"mod\" IN ('mtime','timestamp')" + | |
448 // include Date/Time functions which are located in 'str' module | |
449 " OR f.\"name\" IN ('localtime','localtimestamp','date_trunc')"; | |
450 final String unionPart = | |
451 // add time date functions which are not listed in sys.functions but implemented in the SQL parser (see sql/server/sql_parser.y) | |
452 " UNION SELECT 'extract'" + | |
453 " UNION SELECT 'now'"; | |
454 return getConcatenatedStringFromQuery(FunctionsSelect + wherePart + OrFunctionsMaxMin + unionPart + FunctionsOrderBy1); | |
455 } | |
456 | |
457 /** | |
458 * This is the string that can be used to escape '_' and '%' in | |
459 * a search string pattern style catalog search parameters | |
460 * | |
461 * @return the string used to escape wildcard characters | |
462 */ | |
463 @Override | |
464 public String getSearchStringEscape() { | |
465 return "\\"; | |
466 } | |
467 | |
468 /** | |
469 * Get all the "extra" characters that can be used in unquoted | |
470 * identifier names (those beyond a-zA-Z0-9 and _) | |
471 * | |
472 * @return a string containing the extra characters | |
473 */ | |
474 @Override | |
475 public String getExtraNameCharacters() { | |
476 // MonetDB has no extra characters. Verified it for chars: !@#$%^&*()~{}[]? | |
477 return ""; | |
478 } | |
479 | |
480 /** | |
481 * Is "ALTER TABLE" with an add column supported? | |
482 * | |
483 * @return true if so | |
484 */ | |
485 @Override | |
486 public boolean supportsAlterTableWithAddColumn() { | |
487 return true; | |
488 } | |
489 | |
490 /** | |
491 * Is "ALTER TABLE" with a drop column supported? | |
492 * | |
493 * @return true if so | |
494 */ | |
495 @Override | |
496 public boolean supportsAlterTableWithDropColumn() { | |
497 return true; | |
498 } | |
499 | |
500 /** | |
501 * Is column aliasing supported? | |
502 * | |
503 * <p>If so, the SQL AS clause can be used to provide names for | |
504 * computed columns or to provide alias names for columns as required. | |
505 * A JDBC Compliant <sup>TM</sup> driver always returns true. | |
506 * | |
507 * <p>e.g. | |
508 * | |
509 * <br><pre> | |
510 * select count(C) as C_COUNT from T group by C; | |
511 * | |
512 * </pre><br> | |
513 * should return a column named as C_COUNT instead of count(C) | |
514 * | |
515 * @return true if so | |
516 */ | |
517 @Override | |
518 public boolean supportsColumnAliasing() { | |
519 return true; | |
520 } | |
521 | |
522 /** | |
523 * Are concatenations between NULL and non-NULL values NULL? | |
524 * A JDBC Compliant <sup>TM</sup> driver always returns true. | |
525 * | |
526 * @return true if so | |
527 */ | |
528 @Override | |
529 public boolean nullPlusNonNullIsNull() { | |
530 return true; | |
531 } | |
532 | |
533 /** | |
534 * Retrieves whether this database supports the JDBC scalar function | |
535 * CONVERT for the conversion of one JDBC type to another. | |
536 * The JDBC types are the generic SQL data types defined in java.sql.Types. | |
537 * | |
538 * @return true if so; false otherwise | |
539 */ | |
540 @Override | |
541 public boolean supportsConvert() { | |
542 return true; | |
543 } | |
544 | |
545 /** | |
546 * Retrieves whether this database supports the JDBC scalar function | |
547 * CONVERT for conversions between the JDBC types fromType and toType. | |
548 * The JDBC types are the generic SQL data types defined in java.sql.Types. | |
549 * | |
550 * @return true if so; false otherwise | |
551 */ | |
552 @Override | |
553 public boolean supportsConvert(final int fromType, final int toType) { | |
554 switch (fromType) { | |
555 case Types.BOOLEAN: | |
556 switch (toType) { | |
557 case Types.BOOLEAN: | |
558 /* case Types.BIT: is not supported by MonetDB and will fail */ | |
559 case Types.TINYINT: | |
560 case Types.SMALLINT: | |
561 case Types.INTEGER: | |
562 case Types.BIGINT: | |
563 /* conversion to FLOAT, REAL, DOUBLE, NUMERIC and DECIMAL is not supported by MonetDB */ | |
564 case Types.CHAR: | |
565 case Types.VARCHAR: | |
566 /* case Types.LONGVARCHAR: is not supported by MonetDB and will fail */ | |
567 case Types.CLOB: | |
568 return true; | |
569 } | |
570 // conversion to all other types is not supported | |
571 return false; | |
572 /* case Types.BIT: is not supported by MonetDB and will fail */ | |
573 /* case Types.BINARY: is not supported by MonetDB and will fail */ | |
574 /* case Types.VARBINARY: is not supported by MonetDB and will fail */ | |
575 /* case Types.LONGVARBINARY: is not supported by MonetDB and will fail */ | |
576 case Types.BLOB: | |
577 switch (toType) { | |
578 /* case Types.BINARY: is not supported by MonetDB and will fail */ | |
579 /* case Types.VARBINARY: is not supported by MonetDB and will fail */ | |
580 /* case Types.LONGVARBINARY: is not supported by MonetDB and will fail */ | |
581 case Types.BLOB: | |
582 case Types.VARCHAR: | |
583 /* case Types.LONGVARCHAR: is not supported by MonetDB and will fail */ | |
584 case Types.CLOB: | |
585 return true; | |
586 } | |
587 // conversion to all other types is not supported | |
588 return false; | |
589 case Types.TINYINT: | |
590 case Types.SMALLINT: | |
591 case Types.INTEGER: | |
592 case Types.BIGINT: | |
593 case Types.FLOAT: | |
594 case Types.REAL: | |
595 case Types.DOUBLE: | |
596 case Types.NUMERIC: | |
597 case Types.DECIMAL: | |
598 switch (toType) { | |
599 case Types.BOOLEAN: | |
600 /* case Types.BIT: is not supported by MonetDB and will fail */ | |
601 case Types.TINYINT: | |
602 case Types.SMALLINT: | |
603 case Types.INTEGER: | |
604 case Types.BIGINT: | |
605 case Types.FLOAT: | |
606 case Types.REAL: | |
607 case Types.DOUBLE: | |
608 case Types.NUMERIC: | |
609 case Types.DECIMAL: | |
610 case Types.CHAR: | |
611 case Types.VARCHAR: | |
612 /* case Types.LONGVARCHAR: is not supported by MonetDB and will fail */ | |
613 case Types.CLOB: | |
614 return true; | |
615 } | |
616 // conversion to all other types is not supported | |
617 return false; | |
618 case Types.CHAR: | |
619 case Types.VARCHAR: | |
620 /* case Types.LONGVARCHAR: is not supported by MonetDB and will fail */ | |
621 case Types.CLOB: | |
622 switch (toType) { | |
623 case Types.BOOLEAN: | |
624 /* case Types.BIT: is not supported by MonetDB and will fail */ | |
625 case Types.TINYINT: | |
626 case Types.SMALLINT: | |
627 case Types.INTEGER: | |
628 case Types.BIGINT: | |
629 case Types.FLOAT: | |
630 case Types.REAL: | |
631 case Types.DOUBLE: | |
632 case Types.NUMERIC: | |
633 case Types.DECIMAL: | |
634 case Types.CHAR: | |
635 case Types.VARCHAR: | |
636 /* case Types.LONGVARCHAR: is not supported by MonetDB and will fail */ | |
637 case Types.CLOB: | |
638 case Types.BLOB: | |
639 case Types.DATE: | |
640 case Types.TIME: | |
641 case Types.TIME_WITH_TIMEZONE: | |
642 case Types.TIMESTAMP: | |
643 case Types.TIMESTAMP_WITH_TIMEZONE: | |
644 return true; | |
645 } | |
646 // conversion to all other types is not supported | |
647 return false; | |
648 case Types.DATE: | |
649 switch (toType) { | |
650 case Types.CHAR: | |
651 case Types.VARCHAR: | |
652 /* case Types.LONGVARCHAR: is not supported by MonetDB and will fail */ | |
653 case Types.CLOB: | |
654 case Types.DATE: | |
655 case Types.TIMESTAMP: | |
656 case Types.TIMESTAMP_WITH_TIMEZONE: | |
657 return true; | |
658 } | |
659 // conversion to all other types is not supported | |
660 return false; | |
661 case Types.TIME: | |
662 case Types.TIME_WITH_TIMEZONE: | |
663 switch (toType) { | |
664 case Types.CHAR: | |
665 case Types.VARCHAR: | |
666 /* case Types.LONGVARCHAR: is not supported by MonetDB and will fail */ | |
667 case Types.CLOB: | |
668 case Types.TIME: | |
669 case Types.TIME_WITH_TIMEZONE: | |
670 return true; | |
671 } | |
672 // conversion to all other types is not supported | |
673 return false; | |
674 case Types.TIMESTAMP: | |
675 case Types.TIMESTAMP_WITH_TIMEZONE: | |
676 switch (toType) { | |
677 case Types.CHAR: | |
678 case Types.VARCHAR: | |
679 /* case Types.LONGVARCHAR: is not supported by MonetDB and will fail */ | |
680 case Types.CLOB: | |
681 case Types.DATE: | |
682 case Types.TIME: | |
683 case Types.TIME_WITH_TIMEZONE: | |
684 case Types.TIMESTAMP: | |
685 case Types.TIMESTAMP_WITH_TIMEZONE: | |
686 return true; | |
687 } | |
688 // conversion to all other types is not supported | |
689 return false; | |
690 } | |
691 | |
692 // conversion from all other JDBC SQL types are not supported | |
693 return false; | |
694 } | |
695 | |
696 /** | |
697 * Are table correlation names supported? | |
698 * A JDBC Compliant <sup>TM</sup> driver always returns true. | |
699 * | |
700 * @return true if so | |
701 */ | |
702 @Override | |
703 public boolean supportsTableCorrelationNames() { | |
704 return true; | |
705 } | |
706 | |
707 /** | |
708 * If table correlation names are supported, are they restricted to | |
709 * be different from the names of the tables? | |
710 * | |
711 * @return true if so; false otherwise | |
712 */ | |
713 @Override | |
714 public boolean supportsDifferentTableCorrelationNames() { | |
715 return false; | |
716 } | |
717 | |
718 /** | |
719 * Are expressions in "ORDER BY" lists supported? | |
720 * e.g. select * from t order by a + b; | |
721 * | |
722 * MonetDB supports this, try: | |
723 * select (radix * 1000) + digits as comp, * from types order by (radix * 1000) + digits, -id; | |
724 * | |
725 * @return true if so | |
726 */ | |
727 @Override | |
728 public boolean supportsExpressionsInOrderBy() { | |
729 return true; | |
730 } | |
731 | |
732 /** | |
733 * Can an "ORDER BY" clause use columns not in the SELECT? | |
734 * MonetDB differs from SQL03 => true | |
735 * | |
736 * @return true if so | |
737 */ | |
738 @Override | |
739 public boolean supportsOrderByUnrelated() { | |
740 return true; | |
741 } | |
742 | |
743 /** | |
744 * Is some form of "GROUP BY" clause supported? | |
745 * | |
746 * @return true since MonetDB supports it | |
747 */ | |
748 @Override | |
749 public boolean supportsGroupBy() { | |
750 return true; | |
751 } | |
752 | |
753 /** | |
754 * Can a "GROUP BY" clause use columns not in the SELECT? | |
755 * | |
756 * @return true since that also is supported | |
757 */ | |
758 @Override | |
759 public boolean supportsGroupByUnrelated() { | |
760 return true; | |
761 } | |
762 | |
763 /** | |
764 * Can a "GROUP BY" clause add columns not in the SELECT provided | |
765 * it specifies all the columns in the SELECT? | |
766 * | |
767 * (MonetDB already supports the more difficult supportsGroupByUnrelated(), | |
768 * so this is a piece of cake) | |
769 * | |
770 * @return true if so | |
771 */ | |
772 @Override | |
773 public boolean supportsGroupByBeyondSelect() { | |
774 return true; | |
775 } | |
776 | |
777 /** | |
778 * Is the escape character in "LIKE" clauses supported? | |
779 * A JDBC Compliant <sup>TM</sup> driver always returns true. | |
780 * | |
781 * @return true if so | |
782 */ | |
783 @Override | |
784 public boolean supportsLikeEscapeClause() { | |
785 return true; | |
786 } | |
787 | |
788 /** | |
789 * Are multiple ResultSets from a single execute supported? | |
790 * | |
791 * @return true if so | |
792 */ | |
793 @Override | |
794 public boolean supportsMultipleResultSets() { | |
795 return true; | |
796 } | |
797 | |
798 /** | |
799 * Can we have multiple transactions open at once (on different | |
800 * connections?) | |
801 * This is the main idea behind the Connection, is it? | |
802 * | |
803 * @return true if so | |
804 */ | |
805 @Override | |
806 public boolean supportsMultipleTransactions() { | |
807 return true; | |
808 } | |
809 | |
810 /** | |
811 * Can columns be defined as non-nullable. | |
812 * A JDBC Compliant <sup>TM</sup> driver always returns true. | |
813 * | |
814 * @return true if so | |
815 */ | |
816 @Override | |
817 public boolean supportsNonNullableColumns() { | |
818 return true; | |
819 } | |
820 | |
821 /** | |
822 * Does this driver support the minimum ODBC SQL grammar. This | |
823 * grammar is defined at: | |
824 * | |
825 * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/odbc/htm/odappcpr.asp | |
826 * From this description, we seem to support the ODBC minimal (Level 0) grammar. | |
827 * | |
828 * @return true if so | |
829 */ | |
830 @Override | |
831 public boolean supportsMinimumSQLGrammar() { | |
832 return true; | |
833 } | |
834 | |
835 /** | |
836 * Does this driver support the Core ODBC SQL grammar. We need | |
837 * SQL-92 conformance for this. | |
838 * | |
839 * @return true if so | |
840 */ | |
841 @Override | |
842 public boolean supportsCoreSQLGrammar() { | |
843 return true; | |
844 } | |
845 | |
846 /** | |
847 * Does this driver support the Extended (Level 2) ODBC SQL | |
848 * grammar. We don't conform to the Core (Level 1), so we can't | |
849 * conform to the Extended SQL Grammar. | |
850 * | |
851 * @return true if so | |
852 */ | |
853 @Override | |
854 public boolean supportsExtendedSQLGrammar() { | |
855 return false; | |
856 } | |
857 | |
858 /** | |
859 * Does this driver support the ANSI-92 entry level SQL grammar? | |
860 * All JDBC Compliant <sup>TM</sup> drivers must return true. | |
861 * | |
862 * @return true if so | |
863 */ | |
864 @Override | |
865 public boolean supportsANSI92EntryLevelSQL() { | |
866 return true; | |
867 } | |
868 | |
869 /** | |
870 * Does this driver support the ANSI-92 intermediate level SQL | |
871 * grammar? | |
872 * probably not | |
873 * | |
874 * @return true if so | |
875 */ | |
876 @Override | |
877 public boolean supportsANSI92IntermediateSQL() { | |
878 return false; | |
879 } | |
880 | |
881 /** | |
882 * Does this driver support the ANSI-92 full SQL grammar? | |
883 * Would be good if it was like that | |
884 * | |
885 * @return true if so | |
886 */ | |
887 @Override | |
888 public boolean supportsANSI92FullSQL() { | |
889 return false; | |
890 } | |
891 | |
892 /** | |
893 * Is the SQL Integrity Enhancement Facility supported? | |
894 * | |
895 * The SQL Integrity Enhancement facility offers additional tools for referential integrity, | |
896 * CHECK constraint clauses, and DEFAULT clauses. Referential integrity allows specification of | |
897 * primary and foreign keys with the requirement that no foreign key row may be inserted or | |
898 * updated unless a matching primary key row exists. Check clauses allow specification of | |
899 * inter-column constraints to be maintained by the database system. | |
900 * Default clauses provide optional default values for missing data. | |
901 * | |
902 * We currently do not supprt CHECK constraints (see bug 3568) nor deferrable FK constraints. | |
903 * | |
904 * @return true if so | |
905 */ | |
906 @Override | |
907 public boolean supportsIntegrityEnhancementFacility() { | |
908 return false; | |
909 } | |
910 | |
911 /** | |
912 * Is some form of outer join supported? | |
913 * | |
914 * @return true if so | |
915 */ | |
916 @Override | |
917 public boolean supportsOuterJoins(){ | |
918 return true; | |
919 } | |
920 | |
921 /** | |
922 * Are full nested outer joins supported? | |
923 * | |
924 * @return true if so | |
925 */ | |
926 @Override | |
927 public boolean supportsFullOuterJoins() { | |
928 return true; | |
929 } | |
930 | |
931 /** | |
932 * Is there limited support for outer joins? | |
933 * | |
934 * @return true if so | |
935 */ | |
936 @Override | |
937 public boolean supportsLimitedOuterJoins() { | |
938 return false; | |
939 } | |
940 | |
941 /** | |
942 * What is the database vendor's preferred term for "schema"? | |
943 * MonetDB uses the term "schema". | |
944 * | |
945 * @return the vendor term | |
946 */ | |
947 @Override | |
948 public String getSchemaTerm() { | |
949 return "schema"; | |
950 } | |
951 | |
952 /** | |
953 * What is the database vendor's preferred term for "procedure"? | |
954 * | |
955 * @return the vendor term | |
956 */ | |
957 @Override | |
958 public String getProcedureTerm() { | |
959 return "procedure"; | |
960 } | |
961 | |
962 /** | |
963 * What is the database vendor's preferred term for "catalog"? | |
964 * | |
965 * MonetDB doesn't support the SQL catalog naming concept. | |
966 * Only SQL schemas are supported. | |
967 * We also do not allow the name of a database to be included in | |
968 * a fully qualified name, so we shouldn't return "database" here. | |
969 * A database is a different concept than an SQL catalog. | |
970 * | |
971 * @return the vendor term | |
972 */ | |
973 @Override | |
974 public String getCatalogTerm() { | |
975 // MonetDB does NOT support catalogs, so also no catalog term | |
976 // Some generic SQL clients (e.g. DbVisualiser) use this string in their GUI | |
977 // To avoid NPE, just return an abrev string: "cat" (we like cats) | |
978 return "cat"; | |
979 } | |
980 | |
981 /** | |
982 * Does a catalog appear at the start of a qualified table name? | |
983 * (Otherwise it appears at the end). | |
984 * Currently there is no catalog support at all in MonetDB | |
985 * | |
986 * @return true if so | |
987 */ | |
988 @Override | |
989 public boolean isCatalogAtStart() { | |
990 // return true here; we return false for every other catalog function | |
991 // so it won't matter what we return here | |
992 return true; | |
993 } | |
994 | |
995 /** | |
996 * What is the Catalog separator. | |
997 * | |
998 * @return the catalog separator string | |
999 */ | |
1000 @Override | |
1001 public String getCatalogSeparator() { | |
1002 // MonetDB does NOT support catalogs, so also no catalog separator | |
1003 return null; | |
1004 } | |
1005 | |
1006 /** | |
1007 * Can a schema name be used in a data manipulation statement? | |
1008 * | |
1009 * @return true if so | |
1010 */ | |
1011 @Override | |
1012 public boolean supportsSchemasInDataManipulation() { | |
1013 return true; | |
1014 } | |
1015 | |
1016 /** | |
1017 * Can a schema name be used in a procedure call statement? | |
1018 * Ohw probably, but I don't know of procedures in MonetDB | |
1019 * | |
1020 * @return true if so | |
1021 */ | |
1022 @Override | |
1023 public boolean supportsSchemasInProcedureCalls() { | |
1024 return true; | |
1025 } | |
1026 | |
1027 /** | |
1028 * Can a schema be used in a table definition statement? | |
1029 * | |
1030 * @return true if so | |
1031 */ | |
1032 @Override | |
1033 public boolean supportsSchemasInTableDefinitions() { | |
1034 return true; | |
1035 } | |
1036 | |
1037 /** | |
1038 * Can a schema name be used in an index definition statement? | |
1039 * | |
1040 * @return true if so | |
1041 */ | |
1042 @Override | |
1043 public boolean supportsSchemasInIndexDefinitions() { | |
1044 // we currently do NOT support: create index sch.tblidx on sch.tbl(col); | |
1045 // only: create index tblidx on sch.tbl(col); | |
1046 return false; | |
1047 } | |
1048 | |
1049 /** | |
1050 * Can a schema name be used in a privilege definition statement? | |
1051 * | |
1052 * @return true if so | |
1053 */ | |
1054 @Override | |
1055 public boolean supportsSchemasInPrivilegeDefinitions() { | |
1056 return true; | |
1057 } | |
1058 | |
1059 /** | |
1060 * Can a catalog name be used in a data manipulation statement? | |
1061 * | |
1062 * @return true if so | |
1063 */ | |
1064 @Override | |
1065 public boolean supportsCatalogsInDataManipulation() { | |
1066 return false; | |
1067 } | |
1068 | |
1069 /** | |
1070 * Can a catalog name be used in a procedure call statement? | |
1071 * | |
1072 * @return true if so | |
1073 */ | |
1074 @Override | |
1075 public boolean supportsCatalogsInProcedureCalls() { | |
1076 return false; | |
1077 } | |
1078 | |
1079 /** | |
1080 * Can a catalog name be used in a table definition statement? | |
1081 * | |
1082 * @return true if so | |
1083 */ | |
1084 @Override | |
1085 public boolean supportsCatalogsInTableDefinitions() { | |
1086 return false; | |
1087 } | |
1088 | |
1089 /** | |
1090 * Can a catalog name be used in an index definition? | |
1091 * | |
1092 * @return true if so | |
1093 */ | |
1094 @Override | |
1095 public boolean supportsCatalogsInIndexDefinitions() { | |
1096 return false; | |
1097 } | |
1098 | |
1099 /** | |
1100 * Can a catalog name be used in a privilege definition statement? | |
1101 * | |
1102 * @return true if so | |
1103 */ | |
1104 @Override | |
1105 public boolean supportsCatalogsInPrivilegeDefinitions() { | |
1106 return false; | |
1107 } | |
1108 | |
1109 /** | |
1110 * MonetDB doesn't support positioned DELETEs I guess | |
1111 * | |
1112 * @return true if so | |
1113 */ | |
1114 @Override | |
1115 public boolean supportsPositionedDelete() { | |
1116 return false; | |
1117 } | |
1118 | |
1119 /** | |
1120 * Is positioned UPDATE supported? (same as above) | |
1121 * | |
1122 * @return true if so | |
1123 */ | |
1124 @Override | |
1125 public boolean supportsPositionedUpdate() { | |
1126 return false; | |
1127 } | |
1128 | |
1129 /** | |
1130 * Is SELECT FOR UPDATE supported? | |
1131 * My test resulted in a negative answer | |
1132 * | |
1133 * @return true if so; false otherwise | |
1134 */ | |
1135 @Override | |
1136 public boolean supportsSelectForUpdate(){ | |
1137 return false; | |
1138 } | |
1139 | |
1140 /** | |
1141 * Are stored procedure calls using the stored procedure escape | |
1142 * syntax supported? | |
1143 * | |
1144 * @return true if so; false otherwise | |
1145 */ | |
1146 @Override | |
1147 public boolean supportsStoredProcedures() { | |
1148 return true; | |
1149 } | |
1150 | |
1151 /** | |
1152 * Are subqueries in comparison expressions supported? | |
1153 * A JDBC Compliant <sup>TM</sup> driver always returns true. | |
1154 * MonetDB also supports this | |
1155 * | |
1156 * @return true if so; false otherwise | |
1157 */ | |
1158 @Override | |
1159 public boolean supportsSubqueriesInComparisons() { | |
1160 return true; | |
1161 } | |
1162 | |
1163 /** | |
1164 * Are subqueries in 'exists' expressions supported? | |
1165 * A JDBC Compliant <sup>TM</sup> driver always returns true. | |
1166 * | |
1167 * @return true if so; false otherwise | |
1168 */ | |
1169 @Override | |
1170 public boolean supportsSubqueriesInExists() { | |
1171 return true; | |
1172 } | |
1173 | |
1174 /** | |
1175 * Are subqueries in 'in' statements supported? | |
1176 * A JDBC Compliant <sup>TM</sup> driver always returns true. | |
1177 * | |
1178 * @return true if so; false otherwise | |
1179 */ | |
1180 @Override | |
1181 public boolean supportsSubqueriesInIns() { | |
1182 return true; | |
1183 } | |
1184 | |
1185 /** | |
1186 * Are subqueries in quantified expressions supported? | |
1187 * A JDBC Compliant <sup>TM</sup> driver always returns true. | |
1188 * | |
1189 * (No idea what this is, but we support a good deal of | |
1190 * subquerying.) | |
1191 * | |
1192 * @return true if so; false otherwise | |
1193 */ | |
1194 @Override | |
1195 public boolean supportsSubqueriesInQuantifieds() { | |
1196 return true; | |
1197 } | |
1198 | |
1199 /** | |
1200 * Are correlated subqueries supported? | |
1201 * A JDBC Compliant <sup>TM</sup> driver always returns true. | |
1202 * | |
1203 * (a.k.a. subselect in from?) | |
1204 * | |
1205 * @return true if so; false otherwise | |
1206 */ | |
1207 @Override | |
1208 public boolean supportsCorrelatedSubqueries() { | |
1209 return true; | |
1210 } | |
1211 | |
1212 /** | |
1213 * Is SQL UNION supported? | |
1214 * since 2004-03-20 | |
1215 * | |
1216 * @return true if so | |
1217 */ | |
1218 @Override | |
1219 public boolean supportsUnion() { | |
1220 return true; | |
1221 } | |
1222 | |
1223 /** | |
1224 * Is SQL UNION ALL supported? | |
1225 * since 2004-03-20 | |
1226 * | |
1227 * @return true if so | |
1228 */ | |
1229 @Override | |
1230 public boolean supportsUnionAll() { | |
1231 return true; | |
1232 } | |
1233 | |
1234 /** | |
1235 * ResultSet objects (cursors) are not closed upon explicit or | |
1236 * implicit commit. | |
1237 * | |
1238 * @return true if so | |
1239 */ | |
1240 @Override | |
1241 public boolean supportsOpenCursorsAcrossCommit() { | |
1242 return true; | |
1243 } | |
1244 | |
1245 /** | |
1246 * Same as above | |
1247 * | |
1248 * @return true if so | |
1249 */ | |
1250 @Override | |
1251 public boolean supportsOpenCursorsAcrossRollback() { | |
1252 return true; | |
1253 } | |
1254 | |
1255 /** | |
1256 * Can statements remain open across commits? They may, but | |
1257 * this driver cannot guarentee that. In further reflection. | |
1258 * we are taking a Statement object here, so the answer is | |
1259 * yes, since the Statement is only a vehicle to execute some SQL | |
1260 * | |
1261 * @return true if they always remain open; false otherwise | |
1262 */ | |
1263 @Override | |
1264 public boolean supportsOpenStatementsAcrossCommit() { | |
1265 return true; | |
1266 } | |
1267 | |
1268 /** | |
1269 * Can statements remain open across rollbacks? They may, but | |
1270 * this driver cannot guarentee that. In further contemplation, | |
1271 * we are taking a Statement object here, so the answer is yes again. | |
1272 * | |
1273 * @return true if they always remain open; false otherwise | |
1274 */ | |
1275 @Override | |
1276 public boolean supportsOpenStatementsAcrossRollback() { | |
1277 return true; | |
1278 } | |
1279 | |
1280 /** | |
1281 * How many hex characters can you have in an inline binary literal | |
1282 * | |
1283 * @return the max literal length | |
1284 */ | |
1285 @Override | |
1286 public int getMaxBinaryLiteralLength() { | |
1287 return 2*1024*1024*1024 - 2; // MonetDB supports null terminated strings of max 2GB, see function: int UTF8_strlen() | |
1288 } | |
1289 | |
1290 /** | |
1291 * What is the maximum length for a character literal | |
1292 * | |
1293 * @return the max literal length | |
1294 */ | |
1295 @Override | |
1296 public int getMaxCharLiteralLength() { | |
1297 return 2*1024*1024*1024 - 2; // MonetDB supports null terminated strings of max 2GB, see function: int UTF8_strlen() | |
1298 } | |
1299 | |
1300 /** | |
1301 * Whats the limit on column name length. | |
1302 * I take some safety here, but it's just a varchar in MonetDB | |
1303 * | |
1304 * @return the maximum column name length | |
1305 */ | |
1306 @Override | |
1307 public int getMaxColumnNameLength() { | |
1308 return 1024; // In MonetDB the max length of column sys._columns.name is defined as 1024 | |
1309 } | |
1310 | |
1311 /** | |
1312 * What is the maximum number of columns in a "GROUP BY" clause? | |
1313 * | |
1314 * @return the max number of columns | |
1315 */ | |
1316 @Override | |
1317 public int getMaxColumnsInGroupBy() { | |
1318 return 0; // no specific limit known | |
1319 } | |
1320 | |
1321 /** | |
1322 * What's the maximum number of columns allowed in an index? | |
1323 * | |
1324 * @return max number of columns | |
1325 */ | |
1326 @Override | |
1327 public int getMaxColumnsInIndex() { | |
1328 return 0; // no specific limit known | |
1329 } | |
1330 | |
1331 /** | |
1332 * What's the maximum number of columns in an "ORDER BY clause? | |
1333 * | |
1334 * @return the max columns | |
1335 */ | |
1336 @Override | |
1337 public int getMaxColumnsInOrderBy() { | |
1338 return 0; // no specific limit known | |
1339 } | |
1340 | |
1341 /** | |
1342 * What is the maximum number of columns in a "SELECT" list? | |
1343 * | |
1344 * @return the max columns | |
1345 */ | |
1346 @Override | |
1347 public int getMaxColumnsInSelect() { | |
1348 return 0; // no specific limit known | |
1349 } | |
1350 | |
1351 /** | |
1352 * What is the maximum number of columns in a table? | |
1353 * | |
1354 * The theoretical max value of int column sys._columns.id is 2^31 -1 | |
1355 * but this is for all columns of all tables in all schemas (including all data dictionary columns). | |
1356 * For one table we should reduce it to a more practical soft limit of say 100 thousand | |
1357 * | |
1358 * @return the max columns | |
1359 */ | |
1360 @Override | |
1361 public int getMaxColumnsInTable() { | |
1362 return 100*1000; // soft limit it to 100 thousand | |
1363 } | |
1364 | |
1365 /** | |
1366 * Retrieves the maximum number of concurrent connections to this database that are possible. | |
1367 * | |
1368 * @return the maximum number of active connections possible at one time; a result of zero means that there is no limit or the limit is not known | |
1369 */ | |
1370 @Override | |
1371 public int getMaxConnections() throws SQLException { | |
1372 return con.getMaxConnections(); | |
1373 } | |
1374 | |
1375 /** | |
1376 * What is the maximum cursor name length | |
1377 * Actually we do not do named cursors, so I keep the value small as | |
1378 * a precaution for maybe the future. | |
1379 * | |
1380 * @return max cursor name length in bytes | |
1381 */ | |
1382 @Override | |
1383 public int getMaxCursorNameLength() { | |
1384 return 0; // no specific limit known | |
1385 } | |
1386 | |
1387 /** | |
1388 * Retrieves the maximum number of bytes for an index, including all | |
1389 * of the parts of the index. | |
1390 * | |
1391 * @return max index length in bytes, which includes the composite | |
1392 * of all the constituent parts of the index; a result of zero | |
1393 * means that there is no limit or the limit is not known | |
1394 */ | |
1395 @Override | |
1396 public int getMaxIndexLength() { | |
1397 return 0; // no specific limit known | |
1398 } | |
1399 | |
1400 /** | |
1401 * Retrieves the maximum number of characters that this database | |
1402 * allows in a schema name. | |
1403 * | |
1404 * @return the number of characters or 0 if there is no limit, or the | |
1405 * limit is unknown. | |
1406 */ | |
1407 @Override | |
1408 public int getMaxSchemaNameLength() { | |
1409 return 1024; // In MonetDB the max length of column sys.schemas.name is defined as 1024 | |
1410 } | |
1411 | |
1412 /** | |
1413 * What is the maximum length of a procedure name | |
1414 * | |
1415 * @return the max name length in bytes | |
1416 */ | |
1417 @Override | |
1418 public int getMaxProcedureNameLength() { | |
1419 return 256; // In MonetDB the max length of column sys.functions.name is defined as 256 | |
1420 } | |
1421 | |
1422 /** | |
1423 * What is the maximum length of a catalog | |
1424 * | |
1425 * @return the maximum number of characters allowed in a catalog name; | |
1426 * a result of zero means that there is no limit or the limit is not known | |
1427 */ | |
1428 @Override | |
1429 public int getMaxCatalogNameLength() { | |
1430 return 0; // MonetDB does not support catalog names | |
1431 } | |
1432 | |
1433 /** | |
1434 * What is the maximum length of a single row? | |
1435 * | |
1436 * @return max row size in bytes | |
1437 */ | |
1438 @Override | |
1439 public int getMaxRowSize() { | |
1440 return 0; // very long I hope... | |
1441 } | |
1442 | |
1443 /** | |
1444 * Did getMaxRowSize() include the SQL data types LONGVARCHAR and LONGVARBINARY | |
1445 * blobs? | |
1446 * Yes I thought so... | |
1447 * | |
1448 * @return true if so | |
1449 */ | |
1450 @Override | |
1451 public boolean doesMaxRowSizeIncludeBlobs() { | |
1452 return true; | |
1453 } | |
1454 | |
1455 /** | |
1456 * What is the maximum length of a SQL statement? | |
1457 * | |
1458 * @return max length in bytes | |
1459 */ | |
1460 @Override | |
1461 public int getMaxStatementLength() { | |
1462 return 2*1024*1024*1024 - 2; // MonetDB supports null terminated strings of max 2GB, see function: int UTF8_strlen() | |
1463 } | |
1464 | |
1465 /** | |
1466 * How many active statements can we have open at one time to | |
1467 * this database? Basically, since each Statement downloads | |
1468 * the results as the query is executed, we can have many. | |
1469 * | |
1470 * @return the maximum | |
1471 */ | |
1472 @Override | |
1473 public int getMaxStatements() { | |
1474 return 0; // no specific limit known | |
1475 } | |
1476 | |
1477 /** | |
1478 * What is the maximum length of a table name | |
1479 * | |
1480 * @return max name length in bytes | |
1481 */ | |
1482 @Override | |
1483 public int getMaxTableNameLength() { | |
1484 return 1024; // In MonetDB the max length of column sys._tables.name is defined as 1024 | |
1485 } | |
1486 | |
1487 /** | |
1488 * What is the maximum number of tables that can be specified | |
1489 * in a SELECT? | |
1490 * | |
1491 * @return the maximum | |
1492 */ | |
1493 @Override | |
1494 public int getMaxTablesInSelect() { | |
1495 return 0; // no specific limit known | |
1496 } | |
1497 | |
1498 /** | |
1499 * What is the maximum length of a user name | |
1500 * | |
1501 * @return the max name length in bytes | |
1502 */ | |
1503 @Override | |
1504 public int getMaxUserNameLength() { | |
1505 return 1024; // In MonetDB the max length of column sys.db_user_info.name is defined as 1024 | |
1506 } | |
1507 | |
1508 /** | |
1509 * What is the database's default transaction isolation level? | |
1510 * We only see commited data, nonrepeatable reads and phantom | |
1511 * reads can occur. | |
1512 * | |
1513 * @return the default isolation level | |
1514 * @see Connection | |
1515 */ | |
1516 @Override | |
1517 public int getDefaultTransactionIsolation() { | |
1518 return Connection.TRANSACTION_SERIALIZABLE; | |
1519 } | |
1520 | |
1521 /** | |
1522 * Are transactions supported? If not, commit and rollback are noops | |
1523 * and the isolation level is TRANSACTION_NONE. We do support | |
1524 * transactions. | |
1525 * | |
1526 * @return true if transactions are supported | |
1527 */ | |
1528 @Override | |
1529 public boolean supportsTransactions() { | |
1530 return true; | |
1531 } | |
1532 | |
1533 /** | |
1534 * Does the database support the given transaction isolation level? | |
1535 * We only support TRANSACTION_SERIALIZABLE as far as I know | |
1536 * | |
1537 * @param level the values are defined in java.sql.Connection | |
1538 * @return true if so | |
1539 * @see Connection | |
1540 */ | |
1541 @Override | |
1542 public boolean supportsTransactionIsolationLevel(final int level) { | |
1543 return level == Connection.TRANSACTION_SERIALIZABLE; | |
1544 } | |
1545 | |
1546 /** | |
1547 * Are both data definition and data manipulation transactions | |
1548 * supported? | |
1549 * Supposedly that data definition is like CREATE or ALTER TABLE | |
1550 * yes it is. | |
1551 * | |
1552 * @return true if so | |
1553 */ | |
1554 @Override | |
1555 public boolean supportsDataDefinitionAndDataManipulationTransactions() { | |
1556 return true; | |
1557 } | |
1558 | |
1559 /** | |
1560 * Are only data manipulation statements within a transaction | |
1561 * supported? | |
1562 * | |
1563 * @return true if so | |
1564 */ | |
1565 @Override | |
1566 public boolean supportsDataManipulationTransactionsOnly() { | |
1567 return false; | |
1568 } | |
1569 | |
1570 /** | |
1571 * Does a data definition statement within a transaction force | |
1572 * the transaction to commit? I think this means something like: | |
1573 * | |
1574 * <pre> | |
1575 * CREATE TABLE T (A INT); | |
1576 * INSERT INTO T (A) VALUES (2); | |
1577 * BEGIN; | |
1578 * UPDATE T SET A = A + 1; | |
1579 * CREATE TABLE X (A INT); | |
1580 * SELECT A FROM T INTO X; | |
1581 * COMMIT; | |
1582 * </pre> | |
1583 * | |
1584 * does the CREATE TABLE call cause a commit? The answer is no. | |
1585 * | |
1586 * @return true if so | |
1587 */ | |
1588 @Override | |
1589 public boolean dataDefinitionCausesTransactionCommit() { | |
1590 return false; | |
1591 } | |
1592 | |
1593 /** | |
1594 * Is a data definition statement within a transaction ignored? | |
1595 * | |
1596 * @return true if so | |
1597 */ | |
1598 @Override | |
1599 public boolean dataDefinitionIgnoredInTransactions() { | |
1600 return false; | |
1601 } | |
1602 | |
1603 /** | |
1604 * Get a description of stored procedures available in a catalog | |
1605 * | |
1606 * <p>Only procedure descriptions matching the schema and procedure | |
1607 * name criteria are returned. They are ordered by PROCEDURE_SCHEM, | |
1608 * PROCEDURE_NAME and SPECIFIC_NAME. | |
1609 * | |
1610 * <p>Each procedure description has the following columns: | |
1611 * <ol> | |
1612 * <li><b>PROCEDURE_CAT</b> String => procedure catalog (may be null) | |
1613 * <li><b>PROCEDURE_SCHEM</b> String => procedure schema (may be null) | |
1614 * <li><b>PROCEDURE_NAME</b> String => procedure name | |
1615 * <li><b>Field4</b> reserved (make it null) | |
1616 * <li><b>Field5</b> reserved (make it null) | |
1617 * <li><b>Field6</b> reserved (make it null) | |
1618 * <li><b>REMARKS</b> String => explanatory comment on the procedure | |
1619 * <li><b>PROCEDURE_TYPE</b> short => kind of procedure | |
1620 * <ul> | |
1621 * <li> procedureResultUnknown - May return a result | |
1622 * <li> procedureNoResult - Does not return a result | |
1623 * <li> procedureReturnsResult - Returns a result | |
1624 * </ul> | |
1625 * <li><b>SPECIFIC_NAME</b> String => The name which uniquely identifies this procedure within its schema. | |
1626 * </ol> | |
1627 * | |
1628 * @param catalog - a catalog name; must match the catalog name as it is stored in the database; | |
1629 * "" retrieves those without a catalog; | |
1630 * null means that the catalog name should not be used to narrow the search | |
1631 * @param schemaPattern - a schema name pattern; must match the schema name as it is stored in the database; | |
1632 * "" retrieves those without a schema; | |
1633 * null means that the schema name should not be used to narrow the search | |
1634 * @param procedureNamePattern - a procedure name pattern; must match the procedure name as it is stored in the database | |
1635 * Note that our implementation allows this param to be null also (for efficiency as no extra LIKE '%' condition is added to be evaluated). | |
1636 * @return ResultSet - each row is a procedure description | |
1637 * @throws SQLException if a database access error occurs | |
1638 */ | |
1639 @Override | |
1640 public ResultSet getProcedures( | |
1641 final String catalog, | |
1642 final String schemaPattern, | |
1643 final String procedureNamePattern | |
1644 ) throws SQLException | |
1645 { | |
1646 final boolean useCommentsTable = con.commentsTableExists(); | |
1647 final StringBuilder query = new StringBuilder(980); | |
1648 query.append("SELECT cast(null as char(1)) AS \"PROCEDURE_CAT\", " + | |
1649 "s.\"name\" AS \"PROCEDURE_SCHEM\", " + | |
1650 "f.\"name\" AS \"PROCEDURE_NAME\", " + | |
1651 "cast(null as char(1)) AS \"Field4\", " + | |
1652 "cast(null as char(1)) AS \"Field5\", " + | |
1653 "cast(null as char(1)) AS \"Field6\", ") | |
1654 .append(useCommentsTable ? "COALESCE(cm.\"remark\", cast(f.\"func\" as varchar(9999)))" : "cast(f.\"func\" as varchar(9999))").append(" AS \"REMARKS\", " + | |
1655 // in MonetDB procedures have no return value by design. | |
1656 "cast(").append(DatabaseMetaData.procedureNoResult).append(" AS smallint) AS \"PROCEDURE_TYPE\", " + | |
1657 // only the id value uniquely identifies a procedure. Include it to be able to differentiate between multiple overloaded procedures with the same name | |
1658 "cast(f.\"id\" as varchar(10)) AS \"SPECIFIC_NAME\" " + | |
1659 "FROM \"sys\".\"functions\" f JOIN \"sys\".\"schemas\" s ON f.\"schema_id\" = s.\"id\" "); | |
1660 if (useCommentsTable) { | |
1661 query.append("LEFT OUTER JOIN \"sys\".\"comments\" cm ON f.\"id\" = cm.\"id\" "); | |
1662 } | |
1663 // include procedures only (type = 2). Others will be returned via getFunctions() | |
1664 query.append("WHERE f.\"type\" = 2"); | |
1665 | |
1666 if (catalog != null && !catalog.isEmpty()) { | |
1667 // non-empty catalog selection. | |
1668 // as we do not support catalogs this always results in no rows returned | |
1669 query.append(" AND 1=0"); | |
1670 } else { | |
1671 if (schemaPattern != null && !schemaPattern.equals("%")) { | |
1672 query.append(" AND s.\"name\" ").append(composeMatchPart(schemaPattern)); | |
1673 } | |
1674 if (procedureNamePattern != null && !procedureNamePattern.equals("%")) { | |
1675 query.append(" AND f.\"name\" ").append(composeMatchPart(procedureNamePattern)); | |
1676 } | |
1677 } | |
1678 | |
1679 query.append(" ORDER BY \"PROCEDURE_SCHEM\", \"PROCEDURE_NAME\", \"SPECIFIC_NAME\""); | |
1680 | |
1681 return executeMetaDataQuery(query.toString()); | |
1682 } | |
1683 | |
1684 /** | |
1685 * Get a description of a catalog's stored procedure parameters | |
1686 * and result columns. | |
1687 * | |
1688 * <p>Only descriptions matching the schema, procedure and parameter name | |
1689 * criteria are returned. They are ordered by PROCEDURE_SCHEM, PROCEDURE_NAME | |
1690 * and SPECIFIC_NAME. Within this, the return value, if any, is first. | |
1691 * Next are the parameter descriptions in call order. The | |
1692 * column descriptions follow in column number order. | |
1693 * | |
1694 * <p>Each row in the ResultSet is a parameter description or column | |
1695 * description with the following fields: | |
1696 * <ol> | |
1697 * <li><b>PROCEDURE_CAT</b> String => procedure catalog (may be null) | |
1698 * <li><b>PROCEDURE_SCHEM</b> String => procedure schema (may be null) | |
1699 * <li><b>PROCEDURE_NAME</b> String => procedure name | |
1700 * <li><b>COLUMN_NAME</b> String => column/parameter name | |
1701 * <li><b>COLUMN_TYPE</b> Short => kind of column/parameter: | |
1702 * <ul><li>procedureColumnUnknown - nobody knows | |
1703 * <li>procedureColumnIn - IN parameter | |
1704 * <li>procedureColumnInOut - INOUT parameter | |
1705 * <li>procedureColumnOut - OUT parameter | |
1706 * <li>procedureColumnReturn - procedure return value | |
1707 * <li>procedureColumnResult - result column in ResultSet | |
1708 * </ul> | |
1709 * <li><b>DATA_TYPE</b> int => SQL type from java.sql.Types | |
1710 * <li><b>TYPE_NAME</b> String => SQL type name, for a UDT type the type name is fully qualified | |
1711 * <li><b>PRECISION</b> int => precision | |
1712 * <li><b>LENGTH</b> int => length in bytes of data | |
1713 * <li><b>SCALE</b> short => scale - null is returned for data types where SCALE is not applicable. | |
1714 * <li><b>RADIX</b> short => radix | |
1715 * <li><b>NULLABLE</b> short => can it contain NULL? | |
1716 * <ul><li>procedureNoNulls - does not allow NULL values | |
1717 * <li>procedureNullable - allows NULL values | |
1718 * <li>procedureNullableUnknown - nullability unknown | |
1719 * </ul> | |
1720 * <li><b>REMARKS</b> String => comment describing parameter/column | |
1721 * <li><b>COLUMN_DEF</b> String => default value for the column, which should be interpreted as a string when the value is enclosed in single quotes (may be null) | |
1722 * The string NULL (not enclosed in quotes) - if NULL was specified as the default value | |
1723 * TRUNCATE (not enclosed in quotes) - if the specified default value cannot be represented without truncation | |
1724 * NULL - if a default value was not specified | |
1725 * <li><b>SQL_DATA_TYPE</b> int => reserved for future use | |
1726 * <li><b>SQL_DATETIME_SUB</b> int => reserved for future use | |
1727 * <li><b>CHAR_OCTET_LENGTH</b> int => the maximum length of binary and character based columns. For any other datatype the returned value is a NULL | |
1728 * <li><b>ORDINAL_POSITION</b> int => the ordinal position, starting from 1, for the input and output parameters for a procedure. | |
1729 * A value of 0 is returned if this row describes the procedure's return value. For result set columns, it is the ordinal position of the | |
1730 * column in the result set starting from 1. If there are multiple result sets, the column ordinal positions are implementation defined. | |
1731 * <li><b>IS_NULLABLE</b> String => ISO rules are used to determine the nullability for a column. | |
1732 * <ul><li>YES --- if the parameter can include NULLs | |
1733 * <li>NO --- if the parameter cannot include NULLs | |
1734 * <li>empty string --- if the nullability for the parameter is unknown | |
1735 * </ul> | |
1736 * <li><b>SPECIFIC_NAME</b> String => the name which uniquely identifies this procedure within its schema. | |
1737 * </ol> | |
1738 * @param catalog - a catalog name; must match the catalog name as it is stored in the database; | |
1739 * "" retrieves those without a catalog; | |
1740 * null means that the catalog name should not be used to narrow the search | |
1741 * @param schemaPattern - a schema name pattern; must match the schema name as it is stored in the database; | |
1742 * "" retrieves those without a schema; | |
1743 * null means that the schema name should not be used to narrow the search | |
1744 * @param procedureNamePattern - a procedure name pattern; must match the procedure name as it is stored in the database | |
1745 * Note that our implementation allows this param to be null also (for efficiency as no extra LIKE '%' condition is added to be evaluated). | |
1746 * @param columnNamePattern - a column name pattern; must match the column name as it is stored in the database | |
1747 * Note that our implementation allows this param to be null also (for efficiency as no extra LIKE '%' condition is added to be evaluated). | |
1748 * @return ResultSet - each row describes a stored procedure parameter or column | |
1749 * @throws SQLException if a database-access error occurs | |
1750 * @see #getSearchStringEscape | |
1751 */ | |
1752 @Override | |
1753 public ResultSet getProcedureColumns( | |
1754 final String catalog, | |
1755 final String schemaPattern, | |
1756 final String procedureNamePattern, | |
1757 final String columnNamePattern | |
1758 ) throws SQLException { | |
1759 final StringBuilder query = new StringBuilder(2900); | |
1760 query.append("SELECT cast(null as char(1)) AS \"PROCEDURE_CAT\", " + | |
1761 "s.\"name\" AS \"PROCEDURE_SCHEM\", " + | |
1762 "f.\"name\" AS \"PROCEDURE_NAME\", " + | |
1763 "a.\"name\" AS \"COLUMN_NAME\", " + | |
1764 "cast(CASE a.\"inout\"" + | |
1765 " WHEN 0 THEN (CASE a.\"number\" WHEN 0 THEN ").append(DatabaseMetaData.procedureColumnReturn).append(" ELSE ").append(DatabaseMetaData.procedureColumnOut).append(" END)" + | |
1766 " WHEN 1 THEN ").append(DatabaseMetaData.procedureColumnIn) | |
1767 .append(" ELSE ").append(DatabaseMetaData.procedureColumnUnknown).append(" END AS smallint) AS \"COLUMN_TYPE\", " + | |
1768 "cast(").append(MonetDriver.getSQLTypeMap("a.\"type\"")).append(" AS int) AS \"DATA_TYPE\", " + | |
1769 "a.\"type\" AS \"TYPE_NAME\", " + | |
1770 "CASE a.\"type\" WHEN 'tinyint' THEN 3 WHEN 'smallint' THEN 5 WHEN 'int' THEN 10 WHEN 'bigint' THEN 19" + | |
1771 " WHEN 'hugeint' THEN 38 WHEN 'oid' THEN 19 WHEN 'wrd' THEN 19 ELSE a.\"type_digits\" END AS \"PRECISION\", " + | |
1772 "CASE a.\"type\" WHEN 'tinyint' THEN 1 WHEN 'smallint' THEN 2 WHEN 'int' THEN 4 WHEN 'bigint' THEN 8" + | |
1773 " WHEN 'hugeint' THEN 16 WHEN 'oid' THEN 8 WHEN 'wrd' THEN 8 ELSE a.\"type_digits\" END AS \"LENGTH\", " + | |
1774 "cast(CASE WHEN a.\"type\" IN ('tinyint','smallint','int','bigint','hugeint','oid','wrd','decimal','numeric'," + | |
1775 "'time','timetz','timestamp','timestamptz','day_interval','month_interval','sec_interval') THEN a.\"type_scale\" ELSE NULL END AS smallint) AS \"SCALE\", " + | |
1776 "cast(CASE WHEN a.\"type\" IN ('tinyint','smallint','int','bigint','hugeint','oid','wrd','decimal','numeric') THEN 10" + | |
1777 " WHEN a.\"type\" IN ('real','float','double') THEN 2 ELSE NULL END AS smallint) AS \"RADIX\", " + | |
1778 "cast(").append(DatabaseMetaData.procedureNullableUnknown).append(" AS smallint) AS \"NULLABLE\", " + | |
1779 "cast(null as char(1)) AS \"REMARKS\", " + | |
1780 "cast(null as char(1)) AS \"COLUMN_DEF\", " + | |
1781 "cast(0 as int) AS \"SQL_DATA_TYPE\", " + | |
1782 "cast(0 as int) AS \"SQL_DATETIME_SUB\", " + | |
1783 "cast(CASE WHEN a.\"type\" IN ('char','varchar','clob') THEN a.\"type_digits\" ELSE NULL END as int) AS \"CHAR_OCTET_LENGTH\", " + | |
1784 // in MonetDB procedures have no return value by design. The arguments in sys.args are numbered from 0 so we must add 1 to comply with the API specification. | |
1785 "cast(a.\"number\" + 1 as int) AS \"ORDINAL_POSITION\", " + | |
1786 "cast('' as varchar(3)) AS \"IS_NULLABLE\", " + | |
1787 // the specific name contains the function id, in order to be able to match the args to the correct overloaded procedure name | |
1788 "cast(f.\"id\" as varchar(10)) AS \"SPECIFIC_NAME\" " + | |
1789 "FROM \"sys\".\"args\" a " + | |
1790 "JOIN \"sys\".\"functions\" f ON a.\"func_id\" = f.\"id\" " + | |
1791 "JOIN \"sys\".\"schemas\" s ON f.\"schema_id\" = s.\"id\" " + | |
1792 // include procedures only (type = 2). Others will be returned via getFunctionColumns() | |
1793 "WHERE f.\"type\" = 2"); | |
1794 | |
1795 if (catalog != null && !catalog.isEmpty()) { | |
1796 // non-empty catalog selection. | |
1797 // as we do not support catalogs this always results in no rows returned | |
1798 query.append(" AND 1=0"); | |
1799 } else { | |
1800 if (schemaPattern != null && !schemaPattern.equals("%")) { | |
1801 query.append(" AND s.\"name\" ").append(composeMatchPart(schemaPattern)); | |
1802 } | |
1803 if (procedureNamePattern != null && !procedureNamePattern.equals("%")) { | |
1804 query.append(" AND f.\"name\" ").append(composeMatchPart(procedureNamePattern)); | |
1805 } | |
1806 if (columnNamePattern != null && !columnNamePattern.equals("%")) { | |
1807 query.append(" AND a.\"name\" ").append(composeMatchPart(columnNamePattern)); | |
1808 } | |
1809 } | |
1810 | |
1811 query.append(" ORDER BY \"PROCEDURE_SCHEM\", \"PROCEDURE_NAME\", \"SPECIFIC_NAME\", \"ORDINAL_POSITION\""); | |
1812 | |
1813 return executeMetaDataQuery(query.toString()); | |
1814 } | |
1815 | |
1816 /** | |
1817 * Retrieves a description of the tables available in the given catalog. | |
1818 * Only table descriptions matching the catalog, schema, table name and type criteria are returned. | |
1819 * They are ordered by TABLE_TYPE, TABLE_CAT, TABLE_SCHEM and TABLE_NAME. | |
1820 * | |
1821 * <p>Each table description has the following columns: | |
1822 * | |
1823 * <ol> | |
1824 * <li><b>TABLE_CAT</b> String => table catalog (may be null) | |
1825 * <li><b>TABLE_SCHEM</b> String => table schema (may be null) | |
1826 * <li><b>TABLE_NAME</b> String => table name | |
1827 * <li><b>TABLE_TYPE</b> String => table type. Typical types are "TABLE", | |
1828 * "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", "LOCAL TEMPORARY", "ALIAS", "SYNONYM". | |
1829 * <li><b>REMARKS</b> String => explanatory comment on the table | |
1830 * <li><b>TYPE_CAT</b> String => the types catalog (may be null) | |
1831 * <li><b>TYPE_SCHEM</b> String => the types schema (may be null) | |
1832 * <li><b>TYPE_NAME</b> String => type name (may be null) | |
1833 * <li><b>SELF_REFERENCING_COL_NAME</b> String => name of the designated "identifier" column of a typed table (may be null) | |
1834 * <li><b>REF_GENERATION</b> String => specifies how values in SELF_REFERENCING_COL_NAME are created. Values are "SYSTEM", "USER", "DERIVED". (may be null) | |
1835 * </ol> | |
1836 * | |
1837 * @param catalog - a catalog name; must match the catalog name as it is stored in the database; | |
1838 * "" retrieves those without a catalog; null means that the | |
1839 * catalog name should not be used to narrow the search | |
1840 * @param schemaPattern - a schema name pattern; must match the schema name as it is stored | |
1841 * in the database; "" retrieves those without a schema; | |
1842 * null means that the schema name should not be used to narrow the search | |
1843 * @param tableNamePattern - a table name pattern; must match the table name as it is stored in the database | |
1844 * For all tables this should be "%" | |
1845 * Note that our implementation allows this param to be null also (for efficiency as no extra LIKE '%' condition is added to be evaluated). | |
1846 * @param types - a list of table types, which must be from the list of table types returned | |
1847 * from getTableTypes(),to include; null returns all types | |
1848 * @return ResultSet - each row is a table description | |
1849 * @throws SQLException if a database-access error occurs. | |
1850 */ | |
1851 @Override | |
1852 public ResultSet getTables( | |
1853 final String catalog, | |
1854 final String schemaPattern, | |
1855 final String tableNamePattern, | |
1856 final String types[] | |
1857 ) throws SQLException | |
1858 { | |
1859 // as of Jul2015 release the sys.tables.type values (0 through 6) is extended with new values 10, 11, 20, and 30 (for system and temp tables/views). | |
1860 // as of Jul2015 release we also have a new table: sys.table_types with names for the new table types | |
1861 // for correct behavior we need to know if the server is using the old (pre Jul2015) or new sys.tables.type values | |
1862 final boolean preJul2015 = ("11.19.15".compareTo(getDatabaseProductVersion()) >= 0); | |
1863 // for debug: System.out.println("getDatabaseProductVersion() is " + getDatabaseProductVersion() + " preJul2015 is " + preJul2015); | |
1864 | |
1865 final boolean useCommentsTable = con.commentsTableExists(); | |
1866 final StringBuilder query = new StringBuilder(1600); | |
1867 if (preJul2015 && types != null && types.length > 0) { | |
1868 // we need to filter on the constructed "TABLE_TYPE" expression, this is only possible when we use a subquery in the FROM | |
1869 query.append("SELECT * FROM ("); | |
1870 } | |
1871 query.append("SELECT cast(null as char(1)) AS \"TABLE_CAT\", " + | |
1872 "s.\"name\" AS \"TABLE_SCHEM\", " + | |
1873 "t.\"name\" AS \"TABLE_NAME\", "); | |
1874 if (preJul2015) { | |
1875 query.append( | |
1876 "CASE WHEN t.\"system\" = true AND t.\"type\" IN (0, 10) AND t.\"temporary\" = 0 THEN 'SYSTEM TABLE' " + | |
1877 "WHEN t.\"system\" = true AND t.\"type\" IN (1, 11) AND t.\"temporary\" = 0 THEN 'SYSTEM VIEW' " + | |
1878 "WHEN t.\"system\" = false AND t.\"type\" = 0 AND t.\"temporary\" = 0 THEN 'TABLE' " + | |
1879 "WHEN t.\"system\" = false AND t.\"type\" = 1 AND t.\"temporary\" = 0 THEN 'VIEW' " + | |
1880 "WHEN t.\"system\" = true AND t.\"type\" IN (0, 20) AND t.\"temporary\" = 1 THEN 'SYSTEM SESSION TABLE' " + | |
1881 "WHEN t.\"system\" = true AND t.\"type\" IN (1, 21) AND t.\"temporary\" = 1 THEN 'SYSTEM SESSION VIEW' " + | |
1882 "WHEN t.\"system\" = false AND t.\"type\" IN (0, 30) AND t.\"temporary\" = 1 THEN 'SESSION TABLE' " + | |
1883 "WHEN t.\"system\" = false AND t.\"type\" IN (1, 31) AND t.\"temporary\" = 1 THEN 'SESSION VIEW' " + | |
1884 "END AS \"TABLE_TYPE\", "); | |
1885 } else { | |
1886 query.append("tt.\"table_type_name\" AS \"TABLE_TYPE\", "); | |
1887 } | |
1888 query.append(useCommentsTable ? "COALESCE(cm.\"remark\", t.\"query\")" : "t.\"query\"").append(" AS \"REMARKS\", " + | |
1889 "cast(null as char(1)) AS \"TYPE_CAT\", " + | |
1890 "cast(null as char(1)) AS \"TYPE_SCHEM\", " + | |
1891 "cast(null as char(1)) AS \"TYPE_NAME\", " + | |
1892 "cast(null as char(1)) AS \"SELF_REFERENCING_COL_NAME\", " + | |
1893 "cast(null as char(1)) AS \"REF_GENERATION\" " + | |
1894 "FROM \"sys\".\"tables\" t "); | |
1895 if (!preJul2015) { | |
1896 query.append("JOIN \"sys\".\"table_types\" tt ON t.\"type\" = tt.\"table_type_id\" "); | |
1897 } | |
1898 query.append("JOIN \"sys\".\"schemas\" s ON t.\"schema_id\" = s.\"id\" "); | |
1899 if (useCommentsTable) { | |
1900 query.append("LEFT OUTER JOIN \"sys\".\"comments\" cm ON t.\"id\" = cm.\"id\" "); | |
1901 } | |
1902 | |
1903 boolean needWhere = true; | |
1904 if (catalog != null && !catalog.isEmpty()) { | |
1905 // non-empty catalog selection. | |
1906 // as we do not support catalogs this always results in no rows returned | |
1907 query.append("WHERE 1=0"); | |
1908 needWhere = false; | |
1909 } else { | |
1910 if (schemaPattern != null && !schemaPattern.equals("%")) { | |
1911 query.append("WHERE s.\"name\" ").append(composeMatchPart(schemaPattern)); | |
1912 needWhere = false; | |
1913 } | |
1914 if (tableNamePattern != null && !tableNamePattern.equals("%")) { | |
1915 query.append(needWhere ? "WHERE" : " AND") | |
1916 .append(" t.\"name\" ").append(composeMatchPart(tableNamePattern)); | |
1917 needWhere = false; | |
1918 } | |
1919 } | |
1920 | |
1921 if (types != null && types.length > 0) { | |
1922 if (preJul2015) { | |
1923 query.append(") AS \"getTables\" WHERE \"TABLE_TYPE\" IN ("); | |
1924 } else { | |
1925 query.append(needWhere ? "WHERE" : " AND") | |
1926 .append(" tt.\"table_type_name\" IN ("); | |
1927 } | |
1928 for (int i = 0; i < types.length; i++) { | |
1929 if (i > 0) { | |
1930 query.append(", "); | |
1931 } | |
1932 query.append("'").append(types[i]).append("'"); | |
1933 } | |
1934 query.append(")"); | |
1935 } | |
1936 | |
1937 query.append(" ORDER BY \"TABLE_TYPE\", \"TABLE_SCHEM\", \"TABLE_NAME\""); | |
1938 | |
1939 return executeMetaDataQuery(query.toString()); | |
1940 } | |
1941 | |
1942 /** | |
1943 * Get the schema names available in this database. The results | |
1944 * are ordered by schema name. | |
1945 * | |
1946 * <P>The schema column is: | |
1947 * <OL> | |
1948 * <LI><B>TABLE_SCHEM</B> String => schema name | |
1949 * <LI><B>TABLE_CATALOG</B> String => catalog name (may be null) | |
1950 * </OL> | |
1951 * | |
1952 * @param catalog a catalog name; must match the catalog name as it | |
1953 * is stored in the database;"" retrieves those without a | |
1954 * catalog; null means catalog name should not be used to | |
1955 * narrow down the search. | |
1956 * @param schemaPattern a schema name; must match the schema name as | |
1957 * it is stored in the database; null means schema name | |
1958 * should not be used to narrow down the search. | |
1959 * @return ResultSet each row has a single String column that is a | |
1960 * schema name | |
1961 * @throws SQLException if a database error occurs | |
1962 */ | |
1963 @Override | |
1964 public ResultSet getSchemas(final String catalog, final String schemaPattern) | |
1965 throws SQLException | |
1966 { | |
1967 final StringBuilder query = new StringBuilder(170); | |
1968 query.append("SELECT \"name\" AS \"TABLE_SCHEM\", " + | |
1969 "cast(null as char(1)) AS \"TABLE_CATALOG\" " + | |
1970 "FROM \"sys\".\"schemas\" "); | |
1971 | |
1972 if (catalog != null && !catalog.isEmpty()) { | |
1973 // non-empty catalog selection. | |
1974 // as we do not support catalogs this always results in no rows returned | |
1975 query.append("WHERE 1=0"); | |
1976 } else { | |
1977 if (schemaPattern != null && !schemaPattern.equals("%")) { | |
1978 query.append("WHERE \"name\" ").append(composeMatchPart(schemaPattern)); | |
1979 } | |
1980 } | |
1981 query.append(" ORDER BY \"TABLE_SCHEM\""); | |
1982 | |
1983 return executeMetaDataQuery(query.toString()); | |
1984 } | |
1985 | |
1986 /** | |
1987 * Get the catalog names available in this database. The results | |
1988 * are ordered by catalog name. | |
1989 * | |
1990 * <P>The catalog column is: | |
1991 * <OL> | |
1992 * <LI><B>TABLE_CAT</B> String => catalog name | |
1993 * </OL> | |
1994 * | |
1995 * | |
1996 * @return ResultSet each row has a single String column that is a | |
1997 * catalog name | |
1998 * @throws SQLException if a database error occurs | |
1999 */ | |
2000 @Override | |
2001 public ResultSet getCatalogs() throws SQLException { | |
2002 // MonetDB does NOT support catalogs. | |
2003 // Return a resultset with no rows | |
2004 return executeMetaDataQuery("SELECT cast(null as char(1)) AS \"TABLE_CAT\" WHERE 1=0"); | |
2005 } | |
2006 | |
2007 /** | |
2008 * Get the table types available in this database. | |
2009 * The results are ordered by table type. | |
2010 * | |
2011 * <P>The table type is: | |
2012 * <OL> | |
2013 * <LI><B>TABLE_TYPE</B> String => table type. Typical types are "TABLE", | |
2014 * "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", | |
2015 * "LOCAL TEMPORARY", "ALIAS", "SYNONYM". | |
2016 * </OL> | |
2017 * | |
2018 * @return ResultSet each row has a single String column that is a table type | |
2019 * @throws SQLException if a database error occurs | |
2020 */ | |
2021 @Override | |
2022 public ResultSet getTableTypes() throws SQLException { | |
2023 // as of Jul2015 release we have a new table: sys.table_types with more table types | |
2024 String query = "SELECT \"table_type_name\" AS \"TABLE_TYPE\" FROM \"sys\".\"table_types\" ORDER BY 1"; | |
2025 | |
2026 // For old (pre jul2015) servers fall back to old behavior. | |
2027 if ("11.19.15".compareTo(getDatabaseProductVersion()) >= 0) | |
2028 query = "SELECT 'SESSION TABLE' AS \"TABLE_TYPE\" UNION ALL " + | |
2029 "SELECT 'SESSION VIEW' UNION ALL " + | |
2030 "SELECT 'SYSTEM SESSION TABLE' UNION ALL " + | |
2031 "SELECT 'SYSTEM SESSION VIEW' UNION ALL " + | |
2032 "SELECT 'SYSTEM TABLE' UNION ALL " + | |
2033 "SELECT 'SYSTEM VIEW' UNION ALL " + | |
2034 "SELECT 'TABLE' UNION ALL " + | |
2035 "SELECT 'VIEW' ORDER BY 1"; | |
2036 | |
2037 return executeMetaDataQuery(query); | |
2038 } | |
2039 | |
2040 /** | |
2041 * Get a description of table columns available in a catalog. | |
2042 * | |
2043 * <P>Only column descriptions matching the catalog, schema, table | |
2044 * and column name criteria are returned. They are ordered by | |
2045 * TABLE_SCHEM, TABLE_NAME and ORDINAL_POSITION. | |
2046 * | |
2047 * <P>Each column description has the following columns: | |
2048 * <OL> | |
2049 * <LI><B>TABLE_CAT</B> String => table catalog (may be null) | |
2050 * <LI><B>TABLE_SCHEM</B> String => table schema (may be null) | |
2051 * <LI><B>TABLE_NAME</B> String => table name | |
2052 * <LI><B>COLUMN_NAME</B> String => column name | |
2053 * <LI><B>DATA_TYPE</B> int => SQL type from java.sql.Types | |
2054 * <LI><B>TYPE_NAME</B> String => Data source dependent type name | |
2055 * <LI><B>COLUMN_SIZE</B> int => column size. For char or date | |
2056 * types this is the maximum number of characters, for numeric or | |
2057 * decimal types this is precision. | |
2058 * <LI><B>BUFFER_LENGTH</B> is not used. | |
2059 * <LI><B>DECIMAL_DIGITS</B> int => the number of fractional digits | |
2060 * <LI><B>NUM_PREC_RADIX</B> int => Radix (typically either 10 or 2) | |
2061 * <LI><B>NULLABLE</B> int => is NULL allowed? | |
2062 * <UL> | |
2063 * <LI> columnNoNulls - might not allow NULL values | |
2064 * <LI> columnNullable - definitely allows NULL values | |
2065 * <LI> columnNullableUnknown - nullability unknown | |
2066 * </UL> | |
2067 * <LI><B>REMARKS</B> String => comment describing column (may be null) | |
2068 * <LI><B>COLUMN_DEF</B> String => default value (may be null) | |
2069 * <LI><B>SQL_DATA_TYPE</B> int => unused | |
2070 * <LI><B>SQL_DATETIME_SUB</B> int => unused | |
2071 * <LI><B>CHAR_OCTET_LENGTH</B> int => for char types the | |
2072 * maximum number of bytes in the column | |
2073 * <LI><B>ORDINAL_POSITION</B> int => index of column in table | |
2074 * (starting at 1) | |
2075 * <LI><B>IS_NULLABLE</B> String => "NO" means column definitely | |
2076 * does not allow NULL values; "YES" means the column might | |
2077 * allow NULL values. An empty string means nobody knows. | |
2078 * <LI><B>SCOPE_CATALOG</B> String => catalog of table that is the scope of a reference attribute (null if DATA_TYPE isn't REF) | |
2079 * <LI><B>SCOPE_SCHEMA</B> String => schema of table that is the scope of a reference attribute (null if the DATA_TYPE isn't REF) | |
2080 * <LI><B>SCOPE_TABLE</B> String => table name that this the scope of a reference attribute (null if the DATA_TYPE isn't REF) | |
2081 * <LI><B>SOURCE_DATA_TYPE</B> short => source type of a distinct type or user-generated Ref type, SQL type from java.sql.Types (null if DATA_TYPE isn't DISTINCT or user-generated REF) | |
2082 * <LI><B>IS_AUTOINCREMENT</B> String => Indicates whether this column is auto incremented | |
2083 * <UL> | |
2084 * <LI> YES --- if the column is auto incremented | |
2085 * <LI> NO --- if the column is not auto incremented | |
2086 * <LI> empty string --- if it cannot be determined whether the column is auto incremented | |
2087 * </UL> | |
2088 * <LI><B>IS_GENERATEDCOLUMN</B> String => Indicates whether this is a generated column | |
2089 * <UL> | |
2090 * <LI> YES --- if this a generated column | |
2091 * <LI> NO --- if this not a generated column | |
2092 * <LI> empty string --- if it cannot be determined whether this is a generated column | |
2093 * </UL> | |
2094 * </OL> | |
2095 * | |
2096 * @param catalog - a catalog name; must match the catalog name as it is stored in the database; | |
2097 * "" retrieves those without a catalog; null means that the | |
2098 * catalog name should not be used to narrow the search | |
2099 * @param schemaPattern - a schema name pattern; must match the schema name as it is stored | |
2100 * in the database; "" retrieves those without a schema; | |
2101 * null means that the schema name should not be used to narrow the search | |
2102 * @param tableNamePattern - a table name pattern; must match the table name as it is stored in the database | |
2103 * For all tables this should be "%" | |
2104 * Note that our implementation allows this param to be null also (for efficiency as no extra LIKE '%' condition is added to be evaluated). | |
2105 * @param columnNamePattern - a column name pattern; must match the column name as it is stored in the database | |
2106 * Note that our implementation allows this param to be null also (for efficiency as no extra LIKE '%' condition is added to be evaluated). | |
2107 * @return ResultSet - each row is a column description | |
2108 * @throws SQLException if a database error occurs | |
2109 * @see #getSearchStringEscape | |
2110 */ | |
2111 @Override | |
2112 public ResultSet getColumns( | |
2113 final String catalog, | |
2114 final String schemaPattern, | |
2115 final String tableNamePattern, | |
2116 final String columnNamePattern | |
2117 ) throws SQLException | |
2118 { | |
2119 final boolean useCommentsTable = con.commentsTableExists(); | |
2120 final StringBuilder query = new StringBuilder(2450); | |
2121 query.append("SELECT cast(null as char(1)) AS \"TABLE_CAT\", " + | |
2122 "s.\"name\" AS \"TABLE_SCHEM\", " + | |
2123 "t.\"name\" AS \"TABLE_NAME\", " + | |
2124 "c.\"name\" AS \"COLUMN_NAME\", " + | |
2125 "cast(").append(MonetDriver.getSQLTypeMap("c.\"type\"")).append(" AS int) AS \"DATA_TYPE\", " + | |
2126 "c.\"type\" AS \"TYPE_NAME\", " + | |
2127 "c.\"type_digits\" AS \"COLUMN_SIZE\", " + | |
2128 "cast(0 as int) AS \"BUFFER_LENGTH\", " + | |
2129 "c.\"type_scale\" AS \"DECIMAL_DIGITS\", " + | |
2130 "cast(CASE WHEN c.\"type\" IN ('decimal','numeric','day_interval','month_interval','sec_interval') THEN 10 " + | |
2131 "WHEN c.\"type\" IN ('int','smallint','tinyint','bigint','hugeint','float','real','double','oid','wrd') THEN 2 " + | |
2132 "ELSE 0 END AS int) AS \"NUM_PREC_RADIX\", " + | |
2133 "cast(CASE c.\"null\" WHEN true THEN ").append(ResultSetMetaData.columnNullable) | |
2134 .append(" WHEN false THEN ").append(ResultSetMetaData.columnNoNulls).append(" END AS int) AS \"NULLABLE\", ") | |
2135 .append(useCommentsTable ? "cm.\"remark\"" : "cast(null AS varchar(9999))").append(" AS \"REMARKS\", " + | |
2136 "c.\"default\" AS \"COLUMN_DEF\", " + | |
2137 "cast(0 as int) AS \"SQL_DATA_TYPE\", " + | |
2138 "cast(0 as int) AS \"SQL_DATETIME_SUB\", " + | |
2139 "cast(CASE WHEN c.\"type\" IN ('char','varchar','clob') THEN c.\"type_digits\" ELSE NULL END as int) AS \"CHAR_OCTET_LENGTH\", " + | |
2140 "cast(c.\"number\" + 1 as int) AS \"ORDINAL_POSITION\", " + | |
2141 "cast(CASE c.\"null\" WHEN true THEN 'YES' WHEN false THEN 'NO' ELSE '' END AS varchar(3)) AS \"IS_NULLABLE\", " + | |
2142 "cast(null AS char(1)) AS \"SCOPE_CATALOG\", " + | |
2143 "cast(null AS char(1)) AS \"SCOPE_SCHEMA\", " + | |
2144 "cast(null AS char(1)) AS \"SCOPE_TABLE\", " + | |
2145 "cast(null AS smallint) AS \"SOURCE_DATA_TYPE\", " + | |
2146 "cast(CASE WHEN c.\"default\" IS NOT NULL AND c.\"default\" LIKE 'next value for %' THEN 'YES' ELSE 'NO' END AS varchar(3)) AS \"IS_AUTOINCREMENT\", " + | |
2147 "cast('NO' AS varchar(3)) AS \"IS_GENERATEDCOLUMN\" " + | |
2148 "FROM \"sys\".\"columns\" c " + | |
2149 "JOIN \"sys\".\"tables\" t ON c.\"table_id\" = t.\"id\" " + | |
2150 "JOIN \"sys\".\"schemas\" s ON t.\"schema_id\" = s.\"id\" "); | |
2151 if (useCommentsTable) { | |
2152 query.append("LEFT OUTER JOIN \"sys\".\"comments\" cm ON c.\"id\" = cm.\"id\" "); | |
2153 } | |
2154 | |
2155 if (catalog != null && !catalog.isEmpty()) { | |
2156 // non-empty catalog selection. | |
2157 // as we do not support catalogs this always results in no rows returned | |
2158 query.append("WHERE 1=0"); | |
2159 } else { | |
2160 boolean needWhere = true; | |
2161 if (schemaPattern != null && !schemaPattern.equals("%")) { | |
2162 query.append("WHERE s.\"name\" ").append(composeMatchPart(schemaPattern)); | |
2163 needWhere = false; | |
2164 } | |
2165 if (tableNamePattern != null && !tableNamePattern.equals("%")) { | |
2166 query.append(needWhere ? "WHERE" : " AND") | |
2167 .append(" t.\"name\" ").append(composeMatchPart(tableNamePattern)); | |
2168 needWhere = false; | |
2169 } | |
2170 if (columnNamePattern != null && !columnNamePattern.equals("%")) { | |
2171 query.append(needWhere ? "WHERE" : " AND") | |
2172 .append(" c.\"name\" ").append(composeMatchPart(columnNamePattern)); | |
2173 } | |
2174 } | |
2175 | |
2176 query.append(" ORDER BY \"TABLE_SCHEM\", \"TABLE_NAME\", \"ORDINAL_POSITION\""); | |
2177 | |
2178 return executeMetaDataQuery(query.toString()); | |
2179 } | |
2180 | |
2181 /** | |
2182 * Get a description of the access rights for a table's columns. | |
2183 * MonetDB doesn't have this level of access rights. | |
2184 * | |
2185 * <P>Only privileges matching the column name criteria are | |
2186 * returned. They are ordered by COLUMN_NAME and PRIVILEGE. | |
2187 * | |
2188 * <P>Each privilige description has the following columns: | |
2189 * <OL> | |
2190 * <LI><B>TABLE_CAT</B> String => table catalog (may be null) | |
2191 * <LI><B>TABLE_SCHEM</B> String => table schema (may be null) | |
2192 * <LI><B>TABLE_NAME</B> String => table name | |
2193 * <LI><B>COLUMN_NAME</B> String => column name | |
2194 * <LI><B>GRANTOR</B> => grantor of access (may be null) | |
2195 * <LI><B>GRANTEE</B> String => grantee of access | |
2196 * <LI><B>PRIVILEGE</B> String => name of access (SELECT, | |
2197 * INSERT, UPDATE, REFRENCES, ...) | |
2198 * <LI><B>IS_GRANTABLE</B> String => "YES" if grantee is permitted | |
2199 * to grant to others; "NO" if not; null if unknown | |
2200 * </OL> | |
2201 * | |
2202 * @param catalog a catalog name; "" retrieves those without a catalog | |
2203 * @param schemaPattern a schema name; "" retrieves those without a schema | |
2204 * @param tableNamePattern a table name | |
2205 * Note that our implementation allows this param to be null also (for efficiency as no extra LIKE '%' condition is added to be evaluated). | |
2206 * @param columnNamePattern a column name pattern | |
2207 * Note that our implementation allows this param to be null also (for efficiency as no extra LIKE '%' condition is added to be evaluated). | |
2208 * @return ResultSet each row is a column privilege description | |
2209 * @see #getSearchStringEscape | |
2210 * @throws SQLException if a database error occurs | |
2211 */ | |
2212 @Override | |
2213 public ResultSet getColumnPrivileges( | |
2214 final String catalog, | |
2215 final String schemaPattern, | |
2216 final String tableNamePattern, | |
2217 final String columnNamePattern | |
2218 ) throws SQLException | |
2219 { | |
2220 final boolean usePrivilege_codesTable = con.privilege_codesTableExists(); | |
2221 final StringBuilder query = new StringBuilder(1100); | |
2222 query.append("SELECT cast(null as char(1)) AS \"TABLE_CAT\", " + | |
2223 "s.\"name\" AS \"TABLE_SCHEM\", " + | |
2224 "t.\"name\" AS \"TABLE_NAME\", " + | |
2225 "c.\"name\" AS \"COLUMN_NAME\", " + | |
2226 "grantors.\"name\" AS \"GRANTOR\", " + | |
2227 "grantees.\"name\" AS \"GRANTEE\", ") | |
2228 .append(usePrivilege_codesTable ? "pc.\"privilege_code_name\"" : | |
2229 "cast(CASE p.\"privileges\" " + | |
2230 "WHEN 1 THEN 'SELECT' " + | |
2231 "WHEN 2 THEN 'UPDATE' " + | |
2232 "WHEN 4 THEN 'INSERT' " + | |
2233 "WHEN 8 THEN 'DELETE' " + | |
2234 "WHEN 16 THEN 'EXECUTE' " + | |
2235 "WHEN 32 THEN 'GRANT' " + | |
2236 "ELSE NULL " + | |
2237 "END AS varchar(7))").append(" AS \"PRIVILEGE\", " + | |
2238 "cast(CASE p.\"grantable\" " + | |
2239 "WHEN 0 THEN 'NO' " + | |
2240 "WHEN 1 THEN 'YES' " + | |
2241 "ELSE NULL " + | |
2242 "END AS varchar(3)) AS \"IS_GRANTABLE\" " + | |
2243 "FROM \"sys\".\"privileges\" p " + | |
2244 "JOIN \"sys\".\"columns\" c ON p.\"obj_id\" = c.\"id\" " + | |
2245 "JOIN \"sys\".\"tables\" t ON c.\"table_id\" = t.\"id\" " + | |
2246 "JOIN \"sys\".\"schemas\" s ON t.\"schema_id\" = s.\"id\" " + | |
2247 "JOIN \"sys\".\"auths\" grantors ON p.\"grantor\" = grantors.\"id\" " + | |
2248 "JOIN \"sys\".\"auths\" grantees ON p.\"auth_id\" = grantees.\"id\" "); | |
2249 if (usePrivilege_codesTable) { | |
2250 query.append("JOIN \"sys\".\"privilege_codes\" pc ON p.\"privileges\" = pc.\"privilege_code_id\" "); | |
2251 } | |
2252 | |
2253 if (catalog != null && !catalog.isEmpty()) { | |
2254 // non-empty catalog selection. | |
2255 // as we do not support catalogs this always results in no rows returned | |
2256 query.append("WHERE 1=0"); | |
2257 } else { | |
2258 boolean needWhere = true; | |
2259 if (schemaPattern != null && !schemaPattern.equals("%")) { | |
2260 query.append("WHERE s.\"name\" ").append(composeMatchPart(schemaPattern)); | |
2261 needWhere = false; | |
2262 } | |
2263 if (tableNamePattern != null && !tableNamePattern.equals("%")) { | |
2264 query.append(needWhere ? "WHERE" : " AND") | |
2265 .append(" t.\"name\" ").append(composeMatchPart(tableNamePattern)); | |
2266 needWhere = false; | |
2267 } | |
2268 if (columnNamePattern != null && !columnNamePattern.equals("%")) { | |
2269 query.append(needWhere ? "WHERE" : " AND") | |
2270 .append(" c.\"name\" ").append(composeMatchPart(columnNamePattern)); | |
2271 } | |
2272 } | |
2273 | |
2274 query.append(" ORDER BY \"TABLE_SCHEM\", \"TABLE_NAME\", \"COLUMN_NAME\", \"PRIVILEGE\""); | |
2275 | |
2276 return executeMetaDataQuery(query.toString()); | |
2277 } | |
2278 | |
2279 /** | |
2280 * Get a description of the access rights for each table available | |
2281 * in a catalog. | |
2282 * | |
2283 * <P>Only privileges matching the schema and table name | |
2284 * criteria are returned. They are ordered by TABLE_SCHEM, | |
2285 * TABLE_NAME, and PRIVILEGE. | |
2286 * | |
2287 * <P>Each privilege description has the following columns: | |
2288 * <OL> | |
2289 * <LI><B>TABLE_CAT</B> String => table catalog (may be null) | |
2290 * <LI><B>TABLE_SCHEM</B> String => table schema (may be null) | |
2291 * <LI><B>TABLE_NAME</B> String => table name | |
2292 * <LI><B>GRANTOR</B> => grantor of access (may be null) | |
2293 * <LI><B>GRANTEE</B> String => grantee of access | |
2294 * <LI><B>PRIVILEGE</B> String => name of access (SELECT, | |
2295 * INSERT, UPDATE, REFRENCES, ...) | |
2296 * <LI><B>IS_GRANTABLE</B> String => "YES" if grantee is permitted | |
2297 * to grant to others; "NO" if not; null if unknown | |
2298 * </OL> | |
2299 * | |
2300 * @param catalog a catalog name; "" retrieves those without a catalog | |
2301 * @param schemaPattern a schema name pattern; "" retrieves those without a schema | |
2302 * @param tableNamePattern a table name pattern | |
2303 * Note that our implementation allows this param to be null also (for efficiency as no extra LIKE '%' condition is added to be evaluated). | |
2304 * @return ResultSet each row is a table privilege description | |
2305 * @see #getSearchStringEscape | |
2306 * @throws SQLException if a database error occurs | |
2307 */ | |
2308 @Override | |
2309 public ResultSet getTablePrivileges( | |
2310 final String catalog, | |
2311 final String schemaPattern, | |
2312 final String tableNamePattern | |
2313 ) throws SQLException | |
2314 { | |
2315 final boolean usePrivilege_codesTable = con.privilege_codesTableExists(); | |
2316 final StringBuilder query = new StringBuilder(1000); | |
2317 query.append("SELECT cast(null as char(1)) AS \"TABLE_CAT\", " + | |
2318 "s.\"name\" AS \"TABLE_SCHEM\", " + | |
2319 "t.\"name\" AS \"TABLE_NAME\", " + | |
2320 "grantors.\"name\" AS \"GRANTOR\", " + | |
2321 "grantees.\"name\" AS \"GRANTEE\", ") | |
2322 .append(usePrivilege_codesTable ? "pc.\"privilege_code_name\"" : | |
2323 "cast(CASE p.\"privileges\" " + | |
2324 "WHEN 1 THEN 'SELECT' " + | |
2325 "WHEN 2 THEN 'UPDATE' " + | |
2326 "WHEN 4 THEN 'INSERT' " + | |
2327 "WHEN 8 THEN 'DELETE' " + | |
2328 "WHEN 16 THEN 'EXECUTE' " + | |
2329 "WHEN 32 THEN 'GRANT' " + | |
2330 "ELSE NULL " + | |
2331 "END AS varchar(7))").append(" AS \"PRIVILEGE\", " + | |
2332 "cast(CASE p.\"grantable\" " + | |
2333 "WHEN 0 THEN 'NO' " + | |
2334 "WHEN 1 THEN 'YES' " + | |
2335 "ELSE NULL " + | |
2336 "END AS varchar(3)) AS \"IS_GRANTABLE\" " + | |
2337 "FROM \"sys\".\"privileges\" p " + | |
2338 "JOIN \"sys\".\"tables\" t ON p.\"obj_id\" = t.\"id\" " + | |
2339 "JOIN \"sys\".\"schemas\" s ON t.\"schema_id\" = s.\"id\" " + | |
2340 "JOIN \"sys\".\"auths\" grantors ON p.\"grantor\" = grantors.\"id\" " + | |
2341 "JOIN \"sys\".\"auths\" grantees ON p.\"auth_id\" = grantees.\"id\" "); | |
2342 if (usePrivilege_codesTable) { | |
2343 query.append("JOIN \"sys\".\"privilege_codes\" pc ON p.\"privileges\" = pc.\"privilege_code_id\" "); | |
2344 } | |
2345 | |
2346 if (catalog != null && !catalog.isEmpty()) { | |
2347 // non-empty catalog selection. | |
2348 // as we do not support catalogs this always results in no rows returned | |
2349 query.append("WHERE 1=0"); | |
2350 } else { | |
2351 boolean needWhere = true; | |
2352 if (schemaPattern != null && !schemaPattern.equals("%")) { | |
2353 query.append("WHERE s.\"name\" ").append(composeMatchPart(schemaPattern)); | |
2354 needWhere = false; | |
2355 } | |
2356 if (tableNamePattern != null && !tableNamePattern.equals("%")) { | |
2357 query.append(needWhere ? "WHERE" : " AND") | |
2358 .append(" t.\"name\" ").append(composeMatchPart(tableNamePattern)); | |
2359 } | |
2360 } | |
2361 | |
2362 query.append(" ORDER BY \"TABLE_SCHEM\", \"TABLE_NAME\", \"PRIVILEGE\""); | |
2363 | |
2364 return executeMetaDataQuery(query.toString()); | |
2365 } | |
2366 | |
2367 /** | |
2368 * Get a description of a table's optimal set of columns that | |
2369 * uniquely identifies a row. They are ordered by SCOPE. | |
2370 * | |
2371 * <P>Each column description has the following columns: | |
2372 * <OL> | |
2373 * <LI><B>SCOPE</B> short => actual scope of result | |
2374 * <UL> | |
2375 * <LI> bestRowTemporary - very temporary, while using row | |
2376 * <LI> bestRowTransaction - valid for remainder of current transaction | |
2377 * <LI> bestRowSession - valid for remainder of current session | |
2378 * </UL> | |
2379 * <LI><B>COLUMN_NAME</B> String => column name | |
2380 * <LI><B>DATA_TYPE</B> int => SQL data type from java.sql.Types | |
2381 * <LI><B>TYPE_NAME</B> String => Data source dependent type name | |
2382 * <LI><B>COLUMN_SIZE</B> int => precision | |
2383 * <LI><B>BUFFER_LENGTH</B> int => not used | |
2384 * <LI><B>DECIMAL_DIGITS</B> short => scale | |
2385 * <LI><B>PSEUDO_COLUMN</B> short => is this a pseudo column | |
2386 * like an Oracle ROWID | |
2387 * <UL> | |
2388 * <LI> bestRowUnknown - may or may not be pseudo column | |
2389 * <LI> bestRowNotPseudo - is NOT a pseudo column | |
2390 * <LI> bestRowPseudo - is a pseudo column | |
2391 * </UL> | |
2392 * </OL> | |
2393 * | |
2394 * @param catalog a catalog name; "" retrieves those without a catalog | |
2395 * @param schema a schema name; "" retrieves those without a schema | |
2396 * @param table a table name | |
2397 * Note that our implementation allows this param to be null also (for efficiency as no extra LIKE '%' condition is added to be evaluated). | |
2398 * @param scope the scope of interest; use same values as SCOPE | |
2399 * @param nullable include columns that are nullable? | |
2400 * @return ResultSet each row is a column description | |
2401 * @throws SQLException if a database error occurs | |
2402 */ | |
2403 @Override | |
2404 public ResultSet getBestRowIdentifier( | |
2405 final String catalog, | |
2406 final String schema, | |
2407 final String table, | |
2408 final int scope, | |
2409 final boolean nullable | |
2410 ) throws SQLException | |
2411 { | |
2412 // first find out if the table has a Primary Key, If it does, we should return only those columns | |
2413 boolean hasPK = false; | |
2414 ResultSet pkey = null; | |
2415 try { | |
2416 pkey = getPrimaryKeys(catalog, schema, table); | |
2417 if (pkey != null && pkey.next()) { | |
2418 hasPK = true; | |
2419 } | |
2420 } catch (SQLException e) { | |
2421 // ignore | |
2422 } finally { | |
2423 MonetConnection.closeResultsetStatement(pkey, null); | |
2424 } | |
2425 | |
2426 final StringBuilder query = new StringBuilder(1500); | |
2427 // Note: DISTINCT is needed to filter out possible duplicate column names from multiple unique constraints | |
2428 query.append("SELECT DISTINCT cast(").append(DatabaseMetaData.bestRowSession).append(" AS smallint) AS \"SCOPE\", " + | |
2429 "c.\"name\" AS \"COLUMN_NAME\", " + | |
2430 "cast(").append(MonetDriver.getSQLTypeMap("c.\"type\"")).append(" AS int) AS \"DATA_TYPE\", " + | |
2431 "c.\"type\" AS \"TYPE_NAME\", " + | |
2432 "c.\"type_digits\" AS \"COLUMN_SIZE\", " + | |
2433 "cast(0 as int) AS \"BUFFER_LENGTH\", " + | |
2434 "cast(c.\"type_scale\" AS smallint) AS \"DECIMAL_DIGITS\", " + | |
2435 "cast(").append(DatabaseMetaData.bestRowNotPseudo).append(" AS smallint) AS \"PSEUDO_COLUMN\" " + | |
2436 "FROM \"sys\".\"keys\" k " + | |
2437 "JOIN \"sys\".\"objects\" o ON k.\"id\" = o.\"id\" " + | |
2438 "JOIN \"sys\".\"columns\" c ON (k.\"table_id\" = c.\"table_id\" AND o.\"name\" = c.\"name\") " + | |
2439 "JOIN \"sys\".\"tables\" t ON k.\"table_id\" = t.\"id\" " + | |
2440 "JOIN \"sys\".\"schemas\" s ON t.\"schema_id\" = s.\"id\" " + | |
2441 "WHERE k.\"type\" = ").append(hasPK ? "0" : "1"); // the primary key (type = 0) or else any unique key (type = 1) | |
2442 // TODO: when there is no PK and there are multiple unique constraints, pick only the unique constraint which has a) the least number of columns and b) the smallest total(size in bytes) | |
2443 | |
2444 if (catalog != null && !catalog.isEmpty()) { | |
2445 // non-empty catalog selection. | |
2446 // as we do not support catalogs this always results in no rows returned | |
2447 query.append(" AND 1=0"); | |
2448 } else { | |
2449 if (scope == DatabaseMetaData.bestRowSession | |
2450 || scope == DatabaseMetaData.bestRowTransaction | |
2451 || scope == DatabaseMetaData.bestRowTemporary) { | |
2452 if (schema != null && !schema.equals("%")) { | |
2453 query.append(" AND s.\"name\" ").append(composeMatchPart(schema)); | |
2454 } | |
2455 if (table != null && !table.equals("%")) { | |
2456 query.append(" AND t.\"name\" ").append(composeMatchPart(table)); | |
2457 } | |
2458 if (!nullable) { | |
2459 query.append(" AND c.\"null\" = false"); | |
2460 } | |
2461 } else { | |
2462 query.append(" AND 1=0"); | |
2463 } | |
2464 } | |
2465 | |
2466 query.append(" ORDER BY \"SCOPE\", o.\"nr\", \"COLUMN_NAME\""); | |
2467 | |
2468 return executeMetaDataQuery(query.toString()); | |
2469 } | |
2470 | |
2471 /** | |
2472 * Get a description of a table's columns that are automatically | |
2473 * updated when any value in a row is updated. They are unordered. | |
2474 * | |
2475 * <P>Each column description has the following columns: | |
2476 * <OL> | |
2477 * <LI><B>SCOPE</B> short => is not used | |
2478 * <LI><B>COLUMN_NAME</B> String => column name | |
2479 * <LI><B>DATA_TYPE</B> int => SQL data type from java.sql.Types | |
2480 * <LI><B>TYPE_NAME</B> String => Data source dependent type name | |
2481 * <LI><B>COLUMN_SIZE</B> int => precision | |
2482 * <LI><B>BUFFER_LENGTH</B> int => length of column value in bytes | |
2483 * <LI><B>DECIMAL_DIGITS</B> short => scale | |
2484 * <LI><B>PSEUDO_COLUMN</B> short => is this a pseudo column like an Oracle ROWID | |
2485 * <UL> | |
2486 * <LI> versionColumnUnknown - may or may not be pseudo column | |
2487 * <LI> versionColumnNotPseudo - is NOT a pseudo column | |
2488 * <LI> versionColumnPseudo - is a pseudo column | |
2489 * </UL> | |
2490 * </OL> | |
2491 * | |
2492 * @param catalog a catalog name; "" retrieves those without a catalog | |
2493 * @param schema a schema name; "" retrieves those without a schema | |
2494 * @param table a table name | |
2495 * Note that our implementation allows this param to be null also (for efficiency as no extra LIKE '%' condition is added to be evaluated). | |
2496 * @return ResultSet each row is a column description | |
2497 * @throws SQLException if a database error occurs | |
2498 */ | |
2499 @Override | |
2500 public ResultSet getVersionColumns( | |
2501 final String catalog, | |
2502 final String schema, | |
2503 final String table | |
2504 ) throws SQLException | |
2505 { | |
2506 // MonetDB currently does not have columns which update themselves, so return an empty ResultSet | |
2507 final String query = | |
2508 "SELECT cast(0 as smallint) AS \"SCOPE\", " + | |
2509 "cast(null as char(1)) AS \"COLUMN_NAME\", " + | |
2510 "cast(0 as int) AS \"DATA_TYPE\", " + | |
2511 "cast(null as char(1)) AS \"TYPE_NAME\", " + | |
2512 "cast(0 as int) AS \"COLUMN_SIZE\", " + | |
2513 "cast(0 as int) AS \"BUFFER_LENGTH\", " + | |
2514 "cast(0 as smallint) AS \"DECIMAL_DIGITS\", " + | |
2515 "cast(0 as smallint) AS \"PSEUDO_COLUMN\" " + | |
2516 "WHERE 1=0"; | |
2517 | |
2518 return executeMetaDataQuery(query); | |
2519 } | |
2520 | |
2521 /** | |
2522 * Get a description of a table's primary key columns. They | |
2523 * are ordered by COLUMN_NAME. | |
2524 * | |
2525 * <P>Each column description has the following columns: | |
2526 * <OL> | |
2527 * <LI><B>TABLE_CAT</B> String => table catalog (may be null) | |
2528 * <LI><B>TABLE_SCHEM</B> String => table schema (may be null) | |
2529 * <LI><B>TABLE_NAME</B> String => table name | |
2530 * <LI><B>COLUMN_NAME</B> String => column name | |
2531 * <LI><B>KEY_SEQ</B> short => sequence number within primary key | |
2532 * <LI><B>PK_NAME</B> String => primary key name (may be null) | |
2533 * </OL> | |
2534 * | |
2535 * @param catalog a catalog name; "" retrieves those without a catalog | |
2536 * @param schema a schema name pattern; "" retrieves those without a schema | |
2537 * @param table a table name | |
2538 * Note that our implementation allows this param to be null also (for efficiency as no extra LIKE '%' condition is added to be evaluated). | |
2539 * @return ResultSet each row is a primary key column description | |
2540 * @throws SQLException if a database error occurs | |
2541 */ | |
2542 @Override | |
2543 public ResultSet getPrimaryKeys( | |
2544 final String catalog, | |
2545 final String schema, | |
2546 final String table | |
2547 ) throws SQLException | |
2548 { | |
2549 final StringBuilder query = new StringBuilder(600); | |
2550 query.append("SELECT cast(null AS char(1)) AS \"TABLE_CAT\", " + | |
2551 "s.\"name\" AS \"TABLE_SCHEM\", " + | |
2552 "t.\"name\" AS \"TABLE_NAME\", " + | |
2553 "o.\"name\" AS \"COLUMN_NAME\", " + | |
2554 "cast(1 + o.\"nr\" AS smallint) AS \"KEY_SEQ\", " + | |
2555 " k.\"name\" AS \"PK_NAME\" " + | |
2556 "FROM \"sys\".\"keys\" k " + | |
2557 "JOIN \"sys\".\"objects\" o ON k.\"id\" = o.\"id\" " + | |
2558 "JOIN \"sys\".\"tables\" t ON k.\"table_id\" = t.\"id\" " + | |
2559 "JOIN \"sys\".\"schemas\" s ON t.\"schema_id\" = s.\"id\" " + | |
2560 "WHERE k.\"type\" = 0"); // only primary keys (type = 0) | |
2561 | |
2562 if (catalog != null && !catalog.isEmpty()) { | |
2563 // non-empty catalog selection. | |
2564 // as we do not support catalogs this always results in no rows returned | |
2565 query.append(" AND 1=0"); | |
2566 } else { | |
2567 if (schema != null && !schema.equals("%")) { | |
2568 query.append(" AND s.\"name\" ").append(composeMatchPart(schema)); | |
2569 } | |
2570 if (table != null && !table.equals("%")) { | |
2571 query.append(" AND t.\"name\" ").append(composeMatchPart(table)); | |
2572 } | |
2573 } | |
2574 | |
2575 query.append(" ORDER BY \"TABLE_SCHEM\", \"TABLE_NAME\", \"COLUMN_NAME\""); | |
2576 | |
2577 return executeMetaDataQuery(query.toString()); | |
2578 } | |
2579 | |
2580 // same SQL query used by getImportedKeys(), getExportedKeys() and getCrossReference() | |
2581 private static final String keyQuery = | |
2582 "SELECT cast(null AS char(1)) AS \"PKTABLE_CAT\", " + | |
2583 "pkschema.\"name\" AS \"PKTABLE_SCHEM\", " + | |
2584 "pktable.\"name\" AS \"PKTABLE_NAME\", " + | |
2585 "pkkeycol.\"name\" AS \"PKCOLUMN_NAME\", " + | |
2586 "cast(null AS char(1)) AS \"FKTABLE_CAT\", " + | |
2587 "fkschema.\"name\" AS \"FKTABLE_SCHEM\", " + | |
2588 "fktable.\"name\" AS \"FKTABLE_NAME\", " + | |
2589 "fkkeycol.\"name\" AS \"FKCOLUMN_NAME\", " + | |
2590 "cast(1 + \"pkkeycol\".\"nr\" AS smallint) AS \"KEY_SEQ\", " + | |
2591 DatabaseMetaData.importedKeyNoAction + " AS \"UPDATE_RULE\", " + | |
2592 DatabaseMetaData.importedKeyNoAction + " AS \"DELETE_RULE\", " + | |
2593 "fkkey.\"name\" AS \"FK_NAME\", " + | |
2594 "pkkey.\"name\" AS \"PK_NAME\", " + | |
2595 DatabaseMetaData.importedKeyNotDeferrable + " AS \"DEFERRABILITY\" " + | |
2596 "FROM \"sys\".\"keys\" pkkey " + | |
2597 "JOIN \"sys\".\"objects\" pkkeycol ON pkkey.\"id\" = pkkeycol.\"id\" " + | |
2598 "JOIN \"sys\".\"tables\" pktable ON pktable.\"id\" = pkkey.\"table_id\" " + | |
2599 "JOIN \"sys\".\"schemas\" pkschema ON pkschema.\"id\" = pktable.\"schema_id\" " + | |
2600 "JOIN \"sys\".\"keys\" fkkey ON fkkey.\"rkey\" = pkkey.\"id\" " + | |
2601 "JOIN \"sys\".\"objects\" fkkeycol ON (fkkey.\"id\" = fkkeycol.\"id\" AND fkkeycol.\"nr\" = pkkeycol.\"nr\") " + | |
2602 "JOIN \"sys\".\"tables\" fktable ON fktable.\"id\" = fkkey.\"table_id\" " + | |
2603 "JOIN \"sys\".\"schemas\" fkschema ON fkschema.\"id\" = fktable.\"schema_id\" " + | |
2604 "WHERE fkkey.\"rkey\" > 0"; // exclude invalid key references, such as -1 | |
2605 | |
2606 /** | |
2607 * Get a description of the primary key columns that are | |
2608 * referenced by a table's foreign key columns (the primary keys | |
2609 * imported by a table). They are ordered by PKTABLE_CAT, | |
2610 * PKTABLE_SCHEM, PKTABLE_NAME, and KEY_SEQ. | |
2611 * | |
2612 * <P>Each primary key column description has the following columns: | |
2613 * <OL> | |
2614 * <LI><B>PKTABLE_CAT</B> String => primary key table catalog | |
2615 * being imported (may be null) | |
2616 * <LI><B>PKTABLE_SCHEM</B> String => primary key table schema | |
2617 * being imported (may be null) | |
2618 * <LI><B>PKTABLE_NAME</B> String => primary key table name | |
2619 * being imported | |
2620 * <LI><B>PKCOLUMN_NAME</B> String => primary key column name | |
2621 * being imported | |
2622 * <LI><B>FKTABLE_CAT</B> String => foreign key table catalog (may be null) | |
2623 * <LI><B>FKTABLE_SCHEM</B> String => foreign key table schema (may be null) | |
2624 * <LI><B>FKTABLE_NAME</B> String => foreign key table name | |
2625 * <LI><B>FKCOLUMN_NAME</B> String => foreign key column name | |
2626 * <LI><B>KEY_SEQ</B> short => sequence number within foreign key | |
2627 * (a value of 1 represents the first column of the foreign key, a value of 2 would represent the second column within the foreign key). | |
2628 * <LI><B>UPDATE_RULE</B> short => What happens to | |
2629 * foreign key when primary is updated: | |
2630 * <UL> | |
2631 * <LI> importedKeyNoAction - do not allow update of primary key if it has been imported | |
2632 * <LI> importedKeyCascade - change imported key to agree | |
2633 * with primary key update | |
2634 * <LI> importedKeyRestrict - do not allow update of primary | |
2635 * key if it has been imported | |
2636 * <LI> importedKeySetNull - change imported key to NULL if | |
2637 * its primary key has been updated | |
2638 * </UL> | |
2639 * <LI><B>DELETE_RULE</B> short => What happens to | |
2640 * the foreign key when primary is deleted. | |
2641 * <UL> | |
2642 * <LI> importedKeyNoAction - do not allow delete of primary key if it has been imported | |
2643 * <LI> importedKeyCascade - delete rows that import a deleted key | |
2644 * <LI> importedKeyRestrict - do not allow delete of primary | |
2645 * key if it has been imported | |
2646 * <LI> importedKeySetNull - change imported key to NULL if | |
2647 * its primary key has been deleted | |
2648 * </UL> | |
2649 * <LI><B>FK_NAME</B> String => foreign key name (may be null) | |
2650 * <LI><B>PK_NAME</B> String => primary key name (may be null) | |
2651 * <LI><B>DEFERRABILITY</B> short => can the evaluation of foreign key constraints be deferred until commit | |
2652 * <UL> | |
2653 * <LI> importedKeyInitiallyDeferred - see SQL92 for definition | |
2654 * <LI> importedKeyInitiallyImmediate - see SQL92 for definition | |
2655 * <LI> importedKeyNotDeferrable - see SQL92 for definition | |
2656 * </UL> | |
2657 * </OL> | |
2658 * | |
2659 * @param catalog a catalog name; "" retrieves those without a catalog | |
2660 * @param schema a schema name pattern; "" retrieves those without a schema | |
2661 * @param table a table name | |
2662 * Note that our implementation allows this param to be null also (for efficiency as no extra LIKE '%' condition is added to be evaluated). | |
2663 * @return ResultSet each row is a primary key column description | |
2664 * @see #getExportedKeys | |
2665 * @throws SQLException if a database error occurs | |
2666 */ | |
2667 @Override | |
2668 public ResultSet getImportedKeys( | |
2669 final String catalog, | |
2670 final String schema, | |
2671 final String table | |
2672 ) throws SQLException | |
2673 { | |
2674 final StringBuilder query = new StringBuilder(keyQuery.length() + 250); | |
2675 query.append(keyQuery); | |
2676 | |
2677 if (catalog != null && !catalog.isEmpty()) { | |
2678 // non-empty catalog selection. | |
2679 // as we do not support catalogs this always results in no rows returned | |
2680 query.append(" AND 1=0"); | |
2681 } else { | |
2682 if (schema != null && !schema.equals("%")) { | |
2683 query.append(" AND fkschema.\"name\" ").append(composeMatchPart(schema)); | |
2684 } | |
2685 if (table != null && !table.equals("%")) { | |
2686 query.append(" AND fktable.\"name\" ").append(composeMatchPart(table)); | |
2687 } | |
2688 } | |
2689 | |
2690 query.append(" ORDER BY \"PKTABLE_SCHEM\", \"PKTABLE_NAME\", \"PK_NAME\", \"KEY_SEQ\""); | |
2691 | |
2692 return executeMetaDataQuery(query.toString()); | |
2693 } | |
2694 | |
2695 /** | |
2696 * Get a description of a foreign key columns that reference a | |
2697 * table's primary key columns (the foreign keys exported by a table). | |
2698 * They are ordered by FKTABLE_CAT, FKTABLE_SCHEM, FKTABLE_NAME, and KEY_SEQ. | |
2699 * | |
2700 * <P>Each foreign key column description has the following columns: | |
2701 * <OL> | |
2702 * <LI><B>PKTABLE_CAT</B> String => primary key table catalog (may be null) | |
2703 * <LI><B>PKTABLE_SCHEM</B> String => primary key table schema (may be null) | |
2704 * <LI><B>PKTABLE_NAME</B> String => primary key table name | |
2705 * <LI><B>PKCOLUMN_NAME</B> String => primary key column name | |
2706 * <LI><B>FKTABLE_CAT</B> String => foreign key table catalog (may be null) | |
2707 * being exported (may be null) | |
2708 * <LI><B>FKTABLE_SCHEM</B> String => foreign key table schema (may be null) | |
2709 * being exported (may be null) | |
2710 * <LI><B>FKTABLE_NAME</B> String => foreign key table name | |
2711 * being exported | |
2712 * <LI><B>FKCOLUMN_NAME</B> String => foreign key column name | |
2713 * being exported | |
2714 * <LI><B>KEY_SEQ</B> short => sequence number within foreign key | |
2715 * (a value of 1 represents the first column of the foreign key, a value of 2 would represent the second column within the foreign key). | |
2716 * <LI><B>UPDATE_RULE</B> short => What happens to | |
2717 * foreign key when primary is updated: | |
2718 * <UL> | |
2719 * <LI> importedKeyNoAction - do not allow update of primary key if it has been imported | |
2720 * <LI> importedKeyCascade - change imported key to agree | |
2721 * with primary key update | |
2722 * <LI> importedKeyRestrict - do not allow update of primary | |
2723 * key if it has been imported | |
2724 * <LI> importedKeySetNull - change imported key to NULL if | |
2725 * its primary key has been updated | |
2726 * </UL> | |
2727 * <LI><B>DELETE_RULE</B> short => What happens to | |
2728 * the foreign key when primary is deleted. | |
2729 * <UL> | |
2730 * <LI> importedKeyNoAction - do not allow delete of primary key if it has been imported | |
2731 * <LI> importedKeyCascade - delete rows that import a deleted key | |
2732 * <LI> importedKeyRestrict - do not allow delete of primary | |
2733 * key if it has been imported | |
2734 * <LI> importedKeySetNull - change imported key to NULL if | |
2735 * its primary key has been deleted | |
2736 * </UL> | |
2737 * <LI><B>FK_NAME</B> String => foreign key identifier (may be null) | |
2738 * <LI><B>PK_NAME</B> String => primary key identifier (may be null) | |
2739 * <LI><B>DEFERRABILITY</B> short => can the evaluation of foreign key constraints be deferred until commit | |
2740 * <UL> | |
2741 * <LI> importedKeyInitiallyDeferred - see SQL92 for definition | |
2742 * <LI> importedKeyInitiallyImmediate - see SQL92 for definition | |
2743 * <LI> importedKeyNotDeferrable - see SQL92 for definition | |
2744 * </UL> | |
2745 * </OL> | |
2746 * | |
2747 * @param catalog a catalog name; "" retrieves those without a catalog | |
2748 * @param schema a schema name pattern; "" retrieves those without a schema | |
2749 * @param table a table name | |
2750 * Note that our implementation allows this param to be null also (for efficiency as no extra LIKE '%' condition is added to be evaluated). | |
2751 * @return ResultSet each row is a foreign key column description | |
2752 * @see #getImportedKeys | |
2753 * @throws SQLException if a database error occurs | |
2754 */ | |
2755 @Override | |
2756 public ResultSet getExportedKeys( | |
2757 final String catalog, | |
2758 final String schema, | |
2759 final String table | |
2760 ) throws SQLException | |
2761 { | |
2762 final StringBuilder query = new StringBuilder(keyQuery.length() + 250); | |
2763 query.append(keyQuery); | |
2764 | |
2765 if (catalog != null && !catalog.isEmpty()) { | |
2766 // non-empty catalog selection. | |
2767 // as we do not support catalogs this always results in no rows returned | |
2768 query.append(" AND 1=0"); | |
2769 } else { | |
2770 if (schema != null && !schema.equals("%")) { | |
2771 query.append(" AND pkschema.\"name\" ").append(composeMatchPart(schema)); | |
2772 } | |
2773 if (table != null && !table.equals("%")) { | |
2774 query.append(" AND pktable.\"name\" ").append(composeMatchPart(table)); | |
2775 } | |
2776 } | |
2777 | |
2778 query.append(" ORDER BY \"FKTABLE_SCHEM\", \"FKTABLE_NAME\", \"FK_NAME\", \"KEY_SEQ\""); | |
2779 | |
2780 return executeMetaDataQuery(query.toString()); | |
2781 } | |
2782 | |
2783 /** | |
2784 * Get a description of the foreign key columns in the foreign key | |
2785 * table that reference the primary key columns of the primary key | |
2786 * table. (describe how one table imports another's key) This | |
2787 * should normally return a single foreign key/primary key pair | |
2788 * (most tables only import a foreign key from a table once.) | |
2789 * They are ordered by FKTABLE_CAT, FKTABLE_SCHEM, FKTABLE_NAME, and KEY_SEQ. | |
2790 * | |
2791 * <P>Each foreign key column description has the following columns: | |
2792 * <OL> | |
2793 * <LI><B>PKTABLE_CAT</B> String => primary key table catalog (may be null) | |
2794 * <LI><B>PKTABLE_SCHEM</B> String => primary key table schema (may be null) | |
2795 * <LI><B>PKTABLE_NAME</B> String => primary key table name | |
2796 * <LI><B>PKCOLUMN_NAME</B> String => primary key column name | |
2797 * <LI><B>FKTABLE_CAT</B> String => foreign key table catalog (may be null) | |
2798 * being exported (may be null) | |
2799 * <LI><B>FKTABLE_SCHEM</B> String => foreign key table schema (may be null) | |
2800 * being exported (may be null) | |
2801 * <LI><B>FKTABLE_NAME</B> String => foreign key table name | |
2802 * being exported | |
2803 * <LI><B>FKCOLUMN_NAME</B> String => foreign key column name | |
2804 * being exported | |
2805 * <LI><B>KEY_SEQ</B> short => sequence number within foreign key | |
2806 * (a value of 1 represents the first column of the foreign key, a value of 2 would represent the second column within the foreign key). | |
2807 * <LI><B>UPDATE_RULE</B> short => What happens to | |
2808 * foreign key when primary is updated: | |
2809 * <UL> | |
2810 * <LI> importedKeyNoAction - do not allow update of primary key if it has been imported | |
2811 * <LI> importedKeyCascade - change imported key to agree | |
2812 * with primary key update | |
2813 * <LI> importedKeyRestrict - do not allow update of primary | |
2814 * key if it has been imported | |
2815 * <LI> importedKeySetNull - change imported key to NULL if | |
2816 * its primary key has been updated | |
2817 * </UL> | |
2818 * <LI><B>DELETE_RULE</B> short => What happens to | |
2819 * the foreign key when primary is deleted. | |
2820 * <UL> | |
2821 * <LI> importedKeyNoAction - do not allow delete of primary key if it has been imported | |
2822 * <LI> importedKeyCascade - delete rows that import a deleted key | |
2823 * <LI> importedKeyRestrict - do not allow delete of primary | |
2824 * key if it has been imported | |
2825 * <LI> importedKeySetNull - change imported key to NULL if | |
2826 * its primary key has been deleted | |
2827 * </UL> | |
2828 * <LI><B>FK_NAME</B> String => foreign key identifier (may be null) | |
2829 * <LI><B>PK_NAME</B> String => primary key identifier (may be null) | |
2830 * <LI><B>DEFERRABILITY</B> short => can the evaluation of foreign key constraints be deferred until commit | |
2831 * <UL> | |
2832 * <LI> importedKeyInitiallyDeferred - see SQL92 for definition | |
2833 * <LI> importedKeyInitiallyImmediate - see SQL92 for definition | |
2834 * <LI> importedKeyNotDeferrable - see SQL92 for definition | |
2835 * </UL> | |
2836 * </OL> | |
2837 * | |
2838 * @param pcatalog primary key catalog name; "" retrieves those without a catalog | |
2839 * @param pschema primary key schema name pattern; "" retrieves those without a schema | |
2840 * @param ptable primary key table name | |
2841 * Note that our implementation allows this param to be null also (for efficiency as no extra LIKE '%' condition is added to be evaluated). | |
2842 * @param fcatalog foreign key catalog name; "" retrieves those without a catalog | |
2843 * @param fschema foreign key schema name pattern; "" retrieves those without a schema | |
2844 * @param ftable koreign key table name | |
2845 * Note that our implementation allows this param to be null also (for efficiency as no extra LIKE '%' condition is added to be evaluated). | |
2846 * @return ResultSet each row is a foreign key column description | |
2847 * @throws SQLException if a database error occurs | |
2848 * @see #getImportedKeys | |
2849 */ | |
2850 @Override | |
2851 public ResultSet getCrossReference( | |
2852 final String pcatalog, | |
2853 final String pschema, | |
2854 final String ptable, | |
2855 final String fcatalog, | |
2856 final String fschema, | |
2857 final String ftable | |
2858 ) throws SQLException | |
2859 { | |
2860 final StringBuilder query = new StringBuilder(keyQuery.length() + 350); | |
2861 query.append(keyQuery); | |
2862 | |
2863 if ((pcatalog != null && !pcatalog.isEmpty()) | |
2864 || (fcatalog != null && !fcatalog.isEmpty())) { | |
2865 // non-empty catalog selection. | |
2866 // as we do not support catalogs this always results in no rows returned | |
2867 query.append(" AND 1=0"); | |
2868 } else { | |
2869 if (pschema != null && !pschema.equals("%")) { | |
2870 query.append(" AND pkschema.\"name\" ").append(composeMatchPart(pschema)); | |
2871 } | |
2872 if (ptable != null && !ptable.equals("%")) { | |
2873 query.append(" AND pktable.\"name\" ").append(composeMatchPart(ptable)); | |
2874 } | |
2875 | |
2876 if (fschema != null && !fschema.equals("%")) { | |
2877 query.append(" AND fkschema.\"name\" ").append(composeMatchPart(fschema)); | |
2878 } | |
2879 if (ftable != null && !ftable.equals("%")) { | |
2880 query.append(" AND fktable.\"name\" ").append(composeMatchPart(ftable)); | |
2881 } | |
2882 } | |
2883 | |
2884 query.append(" ORDER BY \"FKTABLE_SCHEM\", \"FKTABLE_NAME\", \"FK_NAME\", \"KEY_SEQ\""); | |
2885 | |
2886 return executeMetaDataQuery(query.toString()); | |
2887 } | |
2888 | |
2889 /** | |
2890 * Get a description of all the SQL data types supported by | |
2891 * this database. They are ordered by DATA_TYPE and then by how | |
2892 * closely the data type maps to the corresponding JDBC SQL type. | |
2893 * | |
2894 * If the database supports SQL distinct types, then getTypeInfo() will | |
2895 * return a single row with a TYPE_NAME of DISTINCT and a DATA_TYPE of Types.DISTINCT. | |
2896 * If the database supports SQL structured types, then getTypeInfo() will | |
2897 * return a single row with a TYPE_NAME of STRUCT and a DATA_TYPE of Types.STRUCT. | |
2898 * If SQL distinct or structured types are supported, then information on | |
2899 * the individual types may be obtained from the getUDTs() method. | |
2900 * | |
2901 * <P>Each type description has the following columns: | |
2902 * <OL> | |
2903 * <LI><B>TYPE_NAME</B> String => Type name | |
2904 * <LI><B>DATA_TYPE</B> int => SQL data type from java.sql.Types | |
2905 * <LI><B>PRECISION</B> int => maximum precision | |
2906 * <LI><B>LITERAL_PREFIX</B> String => prefix used to quote a literal (may be null) | |
2907 * <LI><B>LITERAL_SUFFIX</B> String => suffix used to quote a literal (may be null) | |
2908 * <LI><B>CREATE_PARAMS</B> String => parameters used in creating | |
2909 * the type (may be null) | |
2910 * <LI><B>NULLABLE</B> short => can you use NULL for this type? | |
2911 * <UL> | |
2912 * <LI> typeNoNulls - does not allow NULL values | |
2913 * <LI> typeNullable - allows NULL values | |
2914 * <LI> typeNullableUnknown - nullability unknown | |
2915 * </UL> | |
2916 * <LI><B>CASE_SENSITIVE</B> boolean=> is it case sensitive? | |
2917 * <LI><B>SEARCHABLE</B> short => can you use "WHERE" based on this type: | |
2918 * <UL> | |
2919 * <LI> typePredNone - No support | |
2920 * <LI> typePredChar - Only supported with WHERE .. LIKE | |
2921 * <LI> typePredBasic - Supported except for WHERE .. LIKE | |
2922 * <LI> typeSearchable - Supported for all WHERE .. | |
2923 * </UL> | |
2924 * <LI><B>UNSIGNED_ATTRIBUTE</B> boolean => is it unsigned? | |
2925 * <LI><B>FIXED_PREC_SCALE</B> boolean => can it be a money value? | |
2926 * <LI><B>AUTO_INCREMENT</B> boolean => can it be used for an | |
2927 * auto-increment value? | |
2928 * <LI><B>LOCAL_TYPE_NAME</B> String => localized version of type name | |
2929 * (may be null) | |
2930 * <LI><B>MINIMUM_SCALE</B> short => minimum scale supported | |
2931 * <LI><B>MAXIMUM_SCALE</B> short => maximum scale supported | |
2932 * <LI><B>SQL_DATA_TYPE</B> int => unused | |
2933 * <LI><B>SQL_DATETIME_SUB</B> int => unused | |
2934 * <LI><B>NUM_PREC_RADIX</B> int => usually 2 or 10 | |
2935 * </OL> | |
2936 * | |
2937 * @return ResultSet each row is a SQL type description | |
2938 * @throws SQLException if a database error occurs | |
2939 */ | |
2940 @Override | |
2941 public ResultSet getTypeInfo() throws SQLException { | |
2942 final StringBuilder query = new StringBuilder(2300); | |
2943 query.append("SELECT \"sqlname\" AS \"TYPE_NAME\", " + | |
2944 "cast(").append(MonetDriver.getSQLTypeMap("\"sqlname\"")).append(" AS int) AS \"DATA_TYPE\", " + | |
2945 "\"digits\" AS \"PRECISION\", " + // note that when radix is 2 the precision shows the number of bits | |
2946 "cast(CASE WHEN \"sqlname\" IN ('char','varchar') THEN ''''" + | |
2947 " WHEN \"sqlname\" IN ('clob','inet','json','url','uuid','blob','sqlblob') THEN \"sqlname\"||' '''" + | |
2948 " ELSE NULL END AS varchar(9)) AS \"LITERAL_PREFIX\", " + | |
2949 "cast(CASE WHEN \"systemname\" IN ('str','inet','json','url','uuid','blob','sqlblob') THEN ''''" + | |
2950 " ELSE NULL END AS varchar(2)) AS \"LITERAL_SUFFIX\", " + | |
2951 "CASE WHEN \"sqlname\" IN ('char','varchar') THEN 'max length'" + | |
2952 " WHEN \"sqlname\" = 'decimal' THEN 'precision, scale'" + | |
2953 " WHEN \"sqlname\" IN ('time','timetz','timestamp','timestamptz','sec_interval') THEN 'precision'" + | |
2954 " ELSE NULL END AS \"CREATE_PARAMS\", " + | |
2955 "cast(CASE WHEN \"systemname\" = 'oid' THEN ").append(DatabaseMetaData.typeNoNulls) | |
2956 .append(" ELSE ").append(DatabaseMetaData.typeNullable).append(" END AS smallint) AS \"NULLABLE\", " + | |
2957 "CASE WHEN \"systemname\" IN ('str','json','url') THEN true ELSE false END AS \"CASE_SENSITIVE\", " + | |
2958 "cast(CASE WHEN \"systemname\" IN ('str','inet','json','url','uuid') THEN ").append(DatabaseMetaData.typeSearchable) | |
2959 .append(" ELSE ").append(DatabaseMetaData.typePredBasic).append(" END AS smallint) AS \"SEARCHABLE\", " + | |
2960 "CASE WHEN \"sqlname\" IN ('tinyint','smallint','int','bigint','hugeint','decimal','real','double','day_interval','month_interval','sec_interval') THEN false ELSE true END AS \"UNSIGNED_ATTRIBUTE\", " + | |
2961 "CASE \"sqlname\" WHEN 'decimal' THEN true ELSE false END AS \"FIXED_PREC_SCALE\", " + | |
2962 "CASE WHEN \"sqlname\" IN ('tinyint','smallint','int','bigint') THEN true ELSE false END AS \"AUTO_INCREMENT\", " + | |
2963 "\"systemname\" AS \"LOCAL_TYPE_NAME\", " + | |
2964 "cast(0 AS smallint) AS \"MINIMUM_SCALE\", " + | |
2965 "cast(CASE WHEN \"sqlname\" = 'decimal' THEN (CASE \"systemname\" WHEN 'int' THEN 9 WHEN 'lng' THEN 18 WHEN 'sht' THEN 4 WHEN 'hge' THEN 38 WHEN 'bte' THEN 2 ELSE 0 END)" + | |
2966 " WHEN \"sqlname\" IN ('time','timetz','timestamp','timestamptz','sec_interval') THEN 6 ELSE 0 END AS smallint) AS \"MAXIMUM_SCALE\", " + | |
2967 "cast(0 AS int) AS \"SQL_DATA_TYPE\", " + | |
2968 "cast(0 AS int) AS \"SQL_DATETIME_SUB\", " + | |
2969 "cast(\"radix\" as int) AS \"NUM_PREC_RADIX\" " + | |
2970 "FROM \"sys\".\"types\" " + | |
2971 "ORDER BY \"DATA_TYPE\", \"sqlname\", \"id\""); | |
2972 | |
2973 return executeMetaDataQuery(query.toString()); | |
2974 } | |
2975 | |
2976 /** | |
2977 * Retrieves a description of the given table's indices and statistics. | |
2978 * They are ordered by NON_UNIQUE, TYPE, INDEX_NAME, and ORDINAL_POSITION. | |
2979 * | |
2980 * <P>Each index column description has the following columns: | |
2981 * <OL> | |
2982 * <LI><B>TABLE_CAT</B> String => table catalog (may be null) | |
2983 * <LI><B>TABLE_SCHEM</B> String => table schema (may be null) | |
2984 * <LI><B>TABLE_NAME</B> String => table name | |
2985 * <LI><B>NON_UNIQUE</B> boolean => Can index values be non-unique? | |
2986 * false when TYPE is tableIndexStatistic | |
2987 * <LI><B>INDEX_QUALIFIER</B> String => index catalog (may be null); | |
2988 * null when TYPE is tableIndexStatistic | |
2989 * <LI><B>INDEX_NAME</B> String => index name; null when TYPE is | |
2990 * tableIndexStatistic | |
2991 * <LI><B>TYPE</B> short => index type: | |
2992 * <UL> | |
2993 * <LI> tableIndexStatistic - this identifies table statistics that are | |
2994 * returned in conjuction with a table's index descriptions | |
2995 * <LI> tableIndexClustered - this is a clustered index | |
2996 * <LI> tableIndexHashed - this is a hashed index | |
2997 * <LI> tableIndexOther - this is some other style of index | |
2998 * </UL> | |
2999 * <LI><B>ORDINAL_POSITION</B> short => column sequence number | |
3000 * within index; zero when TYPE is tableIndexStatistic | |
3001 * <LI><B>COLUMN_NAME</B> String => column name; null when TYPE is | |
3002 * tableIndexStatistic | |
3003 * <LI><B>ASC_OR_DESC</B> String => column sort sequence, "A" => ascending | |
3004 * "D" => descending, may be null if sort sequence is not supported; | |
3005 * null when TYPE is tableIndexStatistic | |
3006 * <LI><B>CARDINALITY</B> int => When TYPE is tableIndexStatisic then | |
3007 * this is the number of rows in the table; otherwise it is the | |
3008 * number of unique values in the index. | |
3009 * <LI><B>PAGES</B> int => When TYPE is tableIndexStatisic then | |
3010 * this is the number of pages used for the table, otherwise it | |
3011 * is the number of pages used for the current index. | |
3012 * <LI><B>FILTER_CONDITION</B> String => Filter condition, if any. | |
3013 * (may be null) | |
3014 * </OL> | |
3015 * | |
3016 * @param catalog a catalog name; "" retrieves those without a catalog | |
3017 * @param schema a schema name pattern; "" retrieves those without a schema | |
3018 * @param table a table name | |
3019 * Note that our implementation allows this param to be null also (for efficiency as no extra LIKE '%' condition is added to be evaluated). | |
3020 * @param unique when true, return only indices for unique values; | |
3021 * when false, return indices regardless of whether unique or not | |
3022 * @param approximate when true, result is allowed to reflect approximate | |
3023 * or out of data values; when false, results are requested to be | |
3024 * accurate | |
3025 * @return ResultSet each row is an index column description | |
3026 * @throws SQLException if a database occurs | |
3027 */ | |
3028 @Override | |
3029 public ResultSet getIndexInfo( | |
3030 final String catalog, | |
3031 final String schema, | |
3032 final String table, | |
3033 final boolean unique, | |
3034 final boolean approximate | |
3035 ) throws SQLException | |
3036 { | |
3037 String table_row_count = "0"; | |
3038 | |
3039 if (!approximate | |
3040 && schema != null && !schema.isEmpty() && !schema.contains("%") | |
3041 && table != null && !table.isEmpty() && !table.contains("%")) { | |
3042 // we need the exact cardinality for one specific fully qualified table | |
3043 ResultSet count = null; | |
3044 try { | |
3045 count = executeMetaDataQuery("SELECT COUNT(*) FROM \"" + schema + "\".\"" + table + "\""); | |
3046 if (count != null && count.next()) { | |
3047 String count_value = count.getString(1); | |
3048 if (count_value != null && !count_value.isEmpty()) | |
3049 table_row_count = count_value; | |
3050 } | |
3051 } catch (SQLException e) { | |
3052 // ignore | |
3053 } finally { | |
3054 MonetConnection.closeResultsetStatement(count, null); | |
3055 } | |
3056 } | |
3057 | |
3058 final StringBuilder query = new StringBuilder(1250); | |
3059 query.append( | |
3060 "SELECT cast(null AS char(1)) AS \"TABLE_CAT\", " + | |
3061 "s.\"name\" AS \"TABLE_SCHEM\", " + | |
3062 "t.\"name\" AS \"TABLE_NAME\", " + | |
3063 "CASE WHEN k.\"name\" IS NULL THEN true ELSE false END AS \"NON_UNIQUE\", " + | |
3064 "cast(null AS char(1)) AS \"INDEX_QUALIFIER\", " + | |
3065 "i.\"name\" AS \"INDEX_NAME\", " + | |
3066 "CASE i.\"type\" WHEN 0 THEN ").append(DatabaseMetaData.tableIndexHashed).append(" ELSE ").append(DatabaseMetaData.tableIndexOther).append(" END AS \"TYPE\", " + | |
3067 "cast(o.\"nr\" +1 AS smallint) AS \"ORDINAL_POSITION\", "+ | |
3068 "c.\"name\" AS \"COLUMN_NAME\", " + | |
3069 "cast(null AS char(1)) AS \"ASC_OR_DESC\", " + // sort sequence currently not supported in keys or indexes in MonetDB | |
3070 "cast(").append(table_row_count).append(" AS int) AS \"CARDINALITY\", " + | |
3071 "cast(0 AS int) AS \"PAGES\", " + | |
3072 "cast(null AS char(1)) AS \"FILTER_CONDITION\" " + | |
3073 "FROM \"sys\".\"idxs\" i " + | |
3074 "JOIN \"sys\".\"tables\" t ON i.\"table_id\" = t.\"id\" " + | |
3075 "JOIN \"sys\".\"schemas\" s ON t.\"schema_id\" = s.\"id\" " + | |
3076 "JOIN \"sys\".\"objects\" o ON i.\"id\" = o.\"id\" " + | |
3077 "JOIN \"sys\".\"columns\" c ON (t.\"id\" = c.\"table_id\" AND o.\"name\" = c.\"name\") " + | |
3078 "LEFT OUTER JOIN \"sys\".\"keys\" k ON (i.\"name\" = k.\"name\" AND i.\"table_id\" = k.\"table_id\" AND k.\"type\" IN (0,1)) "); // primary (0) and unique keys (1) only | |
3079 | |
3080 if (catalog != null && !catalog.isEmpty()) { | |
3081 // non-empty catalog selection. | |
3082 // as we do not support catalogs this always results in no rows returned | |
3083 query.append("WHERE 1=0"); | |
3084 } else { | |
3085 boolean needWhere = true; | |
3086 if (schema != null && !schema.equals("%")) { | |
3087 query.append("WHERE s.\"name\" ").append(composeMatchPart(schema)); | |
3088 needWhere = false; | |
3089 } | |
3090 if (table != null && !table.equals("%")) { | |
3091 query.append(needWhere ? "WHERE" : " AND") | |
3092 .append(" t.\"name\" ").append(composeMatchPart(table)); | |
3093 needWhere = false; | |
3094 } | |
3095 if (unique) { | |
3096 query.append(needWhere ? "WHERE" : " AND") | |
3097 .append(" k.\"name\" IS NOT NULL"); | |
3098 } | |
3099 } | |
3100 | |
3101 query.append(" ORDER BY \"NON_UNIQUE\", \"TYPE\", \"INDEX_NAME\", \"ORDINAL_POSITION\""); | |
3102 | |
3103 return executeMetaDataQuery(query.toString()); | |
3104 } | |
3105 | |
3106 //== 1.2 methods (JDBC 2) | |
3107 | |
3108 /** | |
3109 * Does the database support the given result set type? | |
3110 * | |
3111 * @param type - defined in java.sql.ResultSet | |
3112 * @return true if so; false otherwise | |
3113 * @throws SQLException - if a database access error occurs | |
3114 */ | |
3115 @Override | |
3116 public boolean supportsResultSetType(final int type) throws SQLException { | |
3117 // The only type we don't support | |
3118 return type != ResultSet.TYPE_SCROLL_SENSITIVE; | |
3119 } | |
3120 | |
3121 /** | |
3122 * Does the database support the concurrency type in combination | |
3123 * with the given result set type? | |
3124 * | |
3125 * @param type - defined in java.sql.ResultSet | |
3126 * @param concurrency - type defined in java.sql.ResultSet | |
3127 * @return true if so; false otherwise | |
3128 * @throws SQLException - if a database access error occurs | |
3129 */ | |
3130 @Override | |
3131 public boolean supportsResultSetConcurrency(final int type, final int concurrency) | |
3132 throws SQLException | |
3133 { | |
3134 // These combinations are not supported! | |
3135 if (type == ResultSet.TYPE_SCROLL_SENSITIVE) | |
3136 return false; | |
3137 | |
3138 // We do only support Read Only ResultSets | |
3139 if (concurrency != ResultSet.CONCUR_READ_ONLY) | |
3140 return false; | |
3141 | |
3142 // Everything else we do (well, what's left of it :) ) | |
3143 return true; | |
3144 } | |
3145 | |
3146 /* lots of unsupported stuff... (no updatable ResultSet!) */ | |
3147 @Override | |
3148 public boolean ownUpdatesAreVisible(final int type) { | |
3149 return false; | |
3150 } | |
3151 | |
3152 @Override | |
3153 public boolean ownDeletesAreVisible(final int type) { | |
3154 return false; | |
3155 } | |
3156 | |
3157 @Override | |
3158 public boolean ownInsertsAreVisible(final int type) { | |
3159 return false; | |
3160 } | |
3161 | |
3162 @Override | |
3163 public boolean othersUpdatesAreVisible(final int type) { | |
3164 return false; | |
3165 } | |
3166 | |
3167 @Override | |
3168 public boolean othersDeletesAreVisible(final int i) { | |
3169 return false; | |
3170 } | |
3171 | |
3172 @Override | |
3173 public boolean othersInsertsAreVisible(final int type) { | |
3174 return false; | |
3175 } | |
3176 | |
3177 @Override | |
3178 public boolean updatesAreDetected(final int type) { | |
3179 return false; | |
3180 } | |
3181 | |
3182 @Override | |
3183 public boolean deletesAreDetected(final int i) { | |
3184 return false; | |
3185 } | |
3186 | |
3187 @Override | |
3188 public boolean insertsAreDetected(final int type) { | |
3189 return false; | |
3190 } | |
3191 | |
3192 /** | |
3193 * Indicates whether the driver supports batch updates. | |
3194 */ | |
3195 @Override | |
3196 public boolean supportsBatchUpdates() { | |
3197 return true; | |
3198 } | |
3199 | |
3200 /** | |
3201 * Retrieves a description of the user-defined types (UDTs) defined in a particular schema. | |
3202 * Schema-specific UDTs may have type JAVA_OBJECT, STRUCT, or DISTINCT. | |
3203 * Only types matching the catalog, schema, type name and type criteria are returned. | |
3204 * They are ordered by DATA_TYPE, TYPE_CAT, TYPE_SCHEM and TYPE_NAME. | |
3205 * The type name parameter may be a fully-qualified name. In this case, the catalog and schemaPattern parameters are ignored. | |
3206 * | |
3207 * Each type description has the following columns: | |
3208 * | |
3209 * 1 TYPE_CAT String => the type's catalog (may be null) | |
3210 * 2 TYPE_SCHEM String => type's schema (may be null) | |
3211 * 3 TYPE_NAME String => type name | |
3212 * 4 CLASS_NAME String => Java class name | |
3213 * 5 DATA_TYPE int => type value defined in java.sql.Types. One of JAVA_OBJECT, STRUCT, or DISTINCT | |
3214 * 6 REMARKS String => explanatory comment on the type | |
3215 * 7 BASE_TYPE short => type code of the source type of a DISTINCT type or the type that implements the | |
3216 * user-generated reference type of the SELF_REFERENCING_COLUMN of a structured type as defined | |
3217 * in java.sql.Types (null if DATA_TYPE is not DISTINCT or not STRUCT with REFERENCE_GENERATION = USER_DEFINED) | |
3218 * | |
3219 * @throws SQLException - if a database access error occurs | |
3220 */ | |
3221 @Override | |
3222 public ResultSet getUDTs( | |
3223 final String catalog, | |
3224 final String schemaPattern, | |
3225 final String typeNamePattern, | |
3226 final int[] types | |
3227 ) throws SQLException | |
3228 { | |
3229 final StringBuilder query = new StringBuilder(990); | |
3230 if (types != null && types.length > 0) { | |
3231 query.append("SELECT * FROM ("); | |
3232 } | |
3233 query.append("SELECT cast(null as char(1)) AS \"TYPE_CAT\", " + | |
3234 "s.\"name\" AS \"TYPE_SCHEM\", " + | |
3235 "t.\"sqlname\" AS \"TYPE_NAME\", " + | |
3236 "CASE t.\"sqlname\"" + | |
3237 // next 4 UDTs are standard | |
3238 " WHEN 'inet' THEN 'org.monetdb.jdbc.types.INET'" + | |
3239 " WHEN 'json' THEN 'java.lang.String'" + | |
3240 " WHEN 'url' THEN 'org.monetdb.jdbc.types.URL'" + | |
3241 " WHEN 'uuid' THEN 'java.lang.String'" + | |
3242 " ELSE 'java.lang.Object' END AS \"CLASS_NAME\", " + | |
3243 "cast(CASE WHEN t.\"sqlname\" IN ('inet', 'json', 'url', 'uuid') THEN ").append(Types.JAVA_OBJECT) | |
3244 .append(" ELSE ").append(Types.STRUCT).append(" END AS int) AS \"DATA_TYPE\", " + | |
3245 "t.\"systemname\" AS \"REMARKS\", " + | |
3246 "cast(null as smallint) AS \"BASE_TYPE\" " + | |
3247 "FROM \"sys\".\"types\" t " + | |
3248 "JOIN \"sys\".\"schemas\" s ON t.\"schema_id\" = s.\"id\" " + | |
3249 // exclude the built-in types (I assume they always have id <= 99 and eclass < 15) | |
3250 "WHERE t.\"id\" > 99 AND t.\"eclass\" >= 15"); | |
3251 | |
3252 if (catalog != null && !catalog.isEmpty()) { | |
3253 // non-empty catalog selection. | |
3254 // as we do not support catalogs this always results in no rows returned | |
3255 query.append(" AND 1=0"); | |
3256 } else { | |
3257 if (schemaPattern != null && !schemaPattern.equals("%")) { | |
3258 query.append(" AND s.\"name\" ").append(composeMatchPart(schemaPattern)); | |
3259 } | |
3260 if (typeNamePattern != null && !typeNamePattern.equals("%")) { | |
3261 query.append(" AND t.\"sqlname\" ").append(composeMatchPart(typeNamePattern)); | |
3262 } | |
3263 } | |
3264 | |
3265 if (types != null && types.length > 0) { | |
3266 query.append(") AS getUDTs WHERE \"DATA_TYPE\" IN ("); | |
3267 for (int i = 0; i < types.length; i++) { | |
3268 if (i > 0) { | |
3269 query.append(", "); | |
3270 } | |
3271 query.append(types[i]); | |
3272 } | |
3273 query.append(")"); | |
3274 } | |
3275 | |
3276 query.append(" ORDER BY \"DATA_TYPE\", \"TYPE_SCHEM\", \"TYPE_NAME\""); | |
3277 | |
3278 return executeMetaDataQuery(query.toString()); | |
3279 } | |
3280 | |
3281 /** | |
3282 * Retrieves the connection that produced this metadata object. | |
3283 * | |
3284 * @return the connection that produced this metadata object | |
3285 */ | |
3286 @Override | |
3287 public Connection getConnection() { | |
3288 return con; | |
3289 } | |
3290 | |
3291 /* I don't find these in the spec!?! */ | |
3292 public boolean rowChangesAreDetected(final int type) { | |
3293 return false; | |
3294 } | |
3295 | |
3296 public boolean rowChangesAreVisible(final int type) { | |
3297 return false; | |
3298 } | |
3299 | |
3300 //== 1.4 methods (JDBC 3) | |
3301 | |
3302 /** | |
3303 * Retrieves whether this database supports savepoints. | |
3304 * | |
3305 * @return <code>true</code> if savepoints are supported; | |
3306 * <code>false</code> otherwise | |
3307 */ | |
3308 @Override | |
3309 public boolean supportsSavepoints() { | |
3310 return true; | |
3311 } | |
3312 | |
3313 /** | |
3314 * Retrieves whether this database supports named parameters to callable | |
3315 * statements. | |
3316 * | |
3317 * @return <code>true</code> if named parameters are supported; | |
3318 * <code>false</code> otherwise | |
3319 */ | |
3320 @Override | |
3321 public boolean supportsNamedParameters() { | |
3322 return false; | |
3323 } | |
3324 | |
3325 /** | |
3326 * Retrieves whether it is possible to have multiple <code>ResultSet</code> objects | |
3327 * returned from a <code>CallableStatement</code> object | |
3328 * simultaneously. | |
3329 * | |
3330 * @return <code>true</code> if a <code>CallableStatement</code> object | |
3331 * can return multiple <code>ResultSet</code> objects | |
3332 * simultaneously; <code>false</code> otherwise | |
3333 */ | |
3334 @Override | |
3335 public boolean supportsMultipleOpenResults() { | |
3336 return true; | |
3337 } | |
3338 | |
3339 /** | |
3340 * Retrieves whether auto-generated keys can be retrieved after | |
3341 * a statement has been executed. | |
3342 * | |
3343 * @return <code>true</code> if auto-generated keys can be retrieved | |
3344 * after a statement has executed; <code>false</code> otherwise | |
3345 */ | |
3346 @Override | |
3347 public boolean supportsGetGeneratedKeys() { | |
3348 return true; | |
3349 } | |
3350 | |
3351 /** | |
3352 * Retrieves a description of the user-defined type (UDT) | |
3353 * hierarchies defined in a particular schema in this database. Only | |
3354 * the immediate super type/ sub type relationship is modeled. | |
3355 * <P> | |
3356 * Only supertype information for UDTs matching the catalog, | |
3357 * schema, and type name is returned. The type name parameter | |
3358 * may be a fully-qualified name. When the UDT name supplied is a | |
3359 * fully-qualified name, the catalog and schemaPattern parameters are | |
3360 * ignored. | |
3361 * <P> | |
3362 * If a UDT does not have a direct super type, it is not listed here. | |
3363 * A row of the <code>ResultSet</code> object returned by this method | |
3364 * describes the designated UDT and a direct supertype. A row has the following | |
3365 * columns: | |
3366 * <OL> | |
3367 * <LI><B>TYPE_CAT</B> String => the UDT's catalog (may be <code>null</code>) | |
3368 * <LI><B>TYPE_SCHEM</B> String => UDT's schema (may be <code>null</code>) | |
3369 * <LI><B>TYPE_NAME</B> String => type name of the UDT | |
3370 * <LI><B>SUPERTYPE_CAT</B> String => the direct super type's catalog | |
3371 * (may be <code>null</code>) | |
3372 * <LI><B>SUPERTYPE_SCHEM</B> String => the direct super type's schema | |
3373 * (may be <code>null</code>) | |
3374 * <LI><B>SUPERTYPE_NAME</B> String => the direct super type's name | |
3375 * </OL> | |
3376 * | |
3377 * <P><B>Note:</B> If the driver does not support type hierarchies, an | |
3378 * empty result set is returned. | |
3379 * | |
3380 * @param catalog a catalog name; "" retrieves those without a catalog; | |
3381 * <code>null</code> means drop catalog name from the selection criteria | |
3382 * @param schemaPattern a schema name pattern; "" retrieves those without a schema | |
3383 * @param typeNamePattern a UDT name pattern; may be a fully-qualified name | |
3384 * @return a <code>ResultSet</code> object in which a row gives information | |
3385 * about the designated UDT | |
3386 * @throws SQLException if a database access error occurs | |
3387 */ | |
3388 @Override | |
3389 public ResultSet getSuperTypes( | |
3390 final String catalog, | |
3391 final String schemaPattern, | |
3392 final String typeNamePattern | |
3393 ) throws SQLException | |
3394 { | |
3395 final String query = | |
3396 "SELECT cast(null as char(1)) AS \"TYPE_CAT\", '' AS \"TYPE_SCHEM\", '' AS \"TYPE_NAME\", " + | |
3397 "cast(null as char(1)) AS \"SUPERTYPE_CAT\", '' AS \"SUPERTYPE_SCHEM\", '' AS \"SUPERTYPE_NAME\" " + | |
3398 "WHERE 1=0"; | |
3399 | |
3400 return executeMetaDataQuery(query); | |
3401 } | |
3402 | |
3403 /** | |
3404 * Retrieves a description of the table hierarchies defined in a particular | |
3405 * schema in this database. | |
3406 * | |
3407 * <P>Only supertable information for tables matching the catalog, schema | |
3408 * and table name are returned. The table name parameter may be a fully- | |
3409 * qualified name, in which case, the catalog and schemaPattern parameters | |
3410 * are ignored. If a table does not have a super table, it is not listed here. | |
3411 * Supertables have to be defined in the same catalog and schema as the | |
3412 * sub tables. Therefore, the type description does not need to include | |
3413 * this information for the supertable. | |
3414 * | |
3415 * <P>Each type description has the following columns: | |
3416 * <OL> | |
3417 * <LI><B>TABLE_CAT</B> String => the type's catalog (may be <code>null</code>) | |
3418 * <LI><B>TABLE_SCHEM</B> String => type's schema (may be <code>null</code>) | |
3419 * <LI><B>TABLE_NAME</B> String => type name | |
3420 * <LI><B>SUPERTABLE_NAME</B> String => the direct super type's name | |
3421 * </OL> | |
3422 * | |
3423 * <P><B>Note:</B> If the driver does not support type hierarchies, an | |
3424 * empty result set is returned. | |
3425 * | |
3426 * @param catalog a catalog name; "" retrieves those without a catalog; | |
3427 * <code>null</code> means drop catalog name from the selection criteria | |
3428 * @param schemaPattern a schema name pattern; "" retrieves those without a schema | |
3429 * @param tableNamePattern a table name pattern; may be a fully-qualified name | |
3430 * @return a <code>ResultSet</code> object in which each row is a type description | |
3431 * @throws SQLException if a database access error occurs | |
3432 */ | |
3433 @Override | |
3434 public ResultSet getSuperTables( | |
3435 final String catalog, | |
3436 final String schemaPattern, | |
3437 final String tableNamePattern | |
3438 ) throws SQLException | |
3439 { | |
3440 final String query = | |
3441 "SELECT cast(null as char(1)) AS \"TABLE_CAT\", " + | |
3442 "'' AS \"TABLE_SCHEM\", '' AS \"TABLE_NAME\", '' AS \"SUPERTABLE_NAME\" " + | |
3443 "WHERE 1=0"; | |
3444 | |
3445 return executeMetaDataQuery(query); | |
3446 } | |
3447 | |
3448 /** | |
3449 * Retrieves a description of the given attribute of the given type | |
3450 * for a user-defined type (UDT) that is available in the given schema | |
3451 * and catalog. | |
3452 * <P> | |
3453 * Descriptions are returned only for attributes of UDTs matching the | |
3454 * catalog, schema, type, and attribute name criteria. They are ordered by | |
3455 * TYPE_SCHEM, TYPE_NAME and ORDINAL_POSITION. This description | |
3456 * does not contain inherited attributes. | |
3457 * <P> | |
3458 * The <code>ResultSet</code> object that is returned has the following | |
3459 * columns: | |
3460 * <OL> | |
3461 * <LI><B>TYPE_CAT</B> String => type catalog (may be <code>null</code>) | |
3462 * <LI><B>TYPE_SCHEM</B> String => type schema (may be <code>null</code>) | |
3463 * <LI><B>TYPE_NAME</B> String => type name | |
3464 * <LI><B>ATTR_NAME</B> String => attribute name | |
3465 * <LI><B>DATA_TYPE</B> int => attribute type SQL type from java.sql.Types | |
3466 * <LI><B>ATTR_TYPE_NAME</B> String => Data source dependent type name. | |
3467 * For a UDT, the type name is fully qualified. For a REF, the type name is | |
3468 * fully qualified and represents the target type of the reference type. | |
3469 * <LI><B>ATTR_SIZE</B> int => column size. For char or date | |
3470 * types this is the maximum number of characters; for numeric or | |
3471 * decimal types this is precision. | |
3472 * <LI><B>DECIMAL_DIGITS</B> int => the number of fractional digits | |
3473 * <LI><B>NUM_PREC_RADIX</B> int => Radix (typically either 10 or 2) | |
3474 * <LI><B>NULLABLE</B> int => whether NULL is allowed | |
3475 * <UL> | |
3476 * <LI> attributeNoNulls - might not allow NULL values | |
3477 * <LI> attributeNullable - definitely allows NULL values | |
3478 * <LI> attributeNullableUnknown - nullability unknown | |
3479 * </UL> | |
3480 * <LI><B>REMARKS</B> String => comment describing column (may be <code>null</code>) | |
3481 * <LI><B>ATTR_DEF</B> String => default value (may be <code>null</code>) | |
3482 * <LI><B>SQL_DATA_TYPE</B> int => unused | |
3483 * <LI><B>SQL_DATETIME_SUB</B> int => unused | |
3484 * <LI><B>CHAR_OCTET_LENGTH</B> int => for char types the | |
3485 * maximum number of bytes in the column | |
3486 * <LI><B>ORDINAL_POSITION</B> int => index of column in table | |
3487 * (starting at 1) | |
3488 * <LI><B>IS_NULLABLE</B> String => "NO" means column definitely | |
3489 * does not allow NULL values; "YES" means the column might | |
3490 * allow NULL values. An empty string means unknown. | |
3491 * <LI><B>SCOPE_CATALOG</B> String => catalog of table that is the | |
3492 * scope of a reference attribute (<code>null</code> if DATA_TYPE isn't REF) | |
3493 * <LI><B>SCOPE_SCHEMA</B> String => schema of table that is the | |
3494 * scope of a reference attribute (<code>null</code> if DATA_TYPE isn't REF) | |
3495 * <LI><B>SCOPE_TABLE</B> String => table name that is the scope of a | |
3496 * reference attribute (<code>null</code> if the DATA_TYPE isn't REF) | |
3497 * <LI><B>SOURCE_DATA_TYPE</B> short => source type of a distinct type or user-generated | |
3498 * Ref type,SQL type from java.sql.Types (<code>null</code> if DATA_TYPE | |
3499 * isn't DISTINCT or user-generated REF) | |
3500 * </OL> | |
3501 * @param catalog a catalog name; must match the catalog name as it | |
3502 * is stored in the database; "" retrieves those without a catalog; | |
3503 * <code>null</code> means that the catalog name should not be used to narrow | |
3504 * the search | |
3505 * @param schemaPattern a schema name pattern; must match the schema name | |
3506 * as it is stored in the database; "" retrieves those without a schema; | |
3507 * <code>null</code> means that the schema name should not be used to narrow | |
3508 * the search | |
3509 * @param typeNamePattern a type name pattern; must match the | |
3510 * type name as it is stored in the database | |
3511 * @param attributeNamePattern an attribute name pattern; must match the attribute | |
3512 * name as it is declared in the database | |
3513 * @return a <code>ResultSet</code> object in which each row is an | |
3514 * attribute description | |
3515 * @throws SQLException if a database access error occurs | |
3516 */ | |
3517 @Override | |
3518 public ResultSet getAttributes( | |
3519 final String catalog, | |
3520 final String schemaPattern, | |
3521 final String typeNamePattern, | |
3522 final String attributeNamePattern | |
3523 ) throws SQLException | |
3524 { | |
3525 final String query = | |
3526 "SELECT cast(null as char(1)) AS \"TYPE_CAT\", '' AS \"TYPE_SCHEM\", '' AS \"TYPE_NAME\", " + | |
3527 "'' AS \"ATTR_NAME\", cast(0 as int) AS \"DATA_TYPE\", '' AS \"ATTR_TYPE_NAME\", cast(0 as int) AS \"ATTR_SIZE\", " + | |
3528 "cast(0 as int) AS \"DECIMAL_DIGITS\", cast(0 as int) AS \"NUM_PREC_RADIX\", cast(0 as int) AS \"NULLABLE\", " + | |
3529 "'' AS \"REMARKS\", '' AS \"ATTR_DEF\", cast(0 as int) AS \"SQL_DATA_TYPE\", " + | |
3530 "cast(0 as int) AS \"SQL_DATETIME_SUB\", cast(0 as int) AS \"CHAR_OCTET_LENGTH\", " + | |
3531 "cast(0 as int) AS \"ORDINAL_POSITION\", 'YES' AS \"IS_NULLABLE\", " + | |
3532 "'' AS \"SCOPE_CATALOG\", '' AS \"SCOPE_SCHEMA\", '' AS \"SCOPE_TABLE\", " + | |
3533 "cast(0 as smallint) AS \"SOURCE_DATA_TYPE\" " + | |
3534 "WHERE 1=0"; | |
3535 | |
3536 return executeMetaDataQuery(query); | |
3537 } | |
3538 | |
3539 /** | |
3540 * Retrieves whether this database supports the given result set holdability. | |
3541 * | |
3542 * @param holdability one of the following constants: | |
3543 * <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code> or | |
3544 * <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code> | |
3545 * @return <code>true</code> if so; <code>false</code> otherwise | |
3546 * @see Connection | |
3547 */ | |
3548 @Override | |
3549 public boolean supportsResultSetHoldability(final int holdability) { | |
3550 // we don't close ResultSets at commit; and we don't do updateable | |
3551 // result sets, so comes closest to hold cursors over commit | |
3552 return holdability == ResultSet.HOLD_CURSORS_OVER_COMMIT; | |
3553 } | |
3554 | |
3555 /** | |
3556 * Retrieves the default holdability of this <code>ResultSet</code> | |
3557 * object. | |
3558 * | |
3559 * @return the default holdability; either | |
3560 * <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code> or | |
3561 * <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code> | |
3562 */ | |
3563 @Override | |
3564 public int getResultSetHoldability() { | |
3565 return ResultSet.HOLD_CURSORS_OVER_COMMIT; | |
3566 } | |
3567 | |
3568 /** | |
3569 * Retrieves the major version number of the underlying database. | |
3570 * | |
3571 * @return the underlying database's major version | |
3572 * @throws SQLException if a database access error occurs | |
3573 */ | |
3574 @Override | |
3575 public int getDatabaseMajorVersion() throws SQLException { | |
3576 return con.getDatabaseMajorVersion(); | |
3577 } | |
3578 | |
3579 /** | |
3580 * Retrieves the minor version number of the underlying database. | |
3581 * | |
3582 * @return underlying database's minor version | |
3583 * @throws SQLException if a database access error occurs | |
3584 */ | |
3585 @Override | |
3586 public int getDatabaseMinorVersion() throws SQLException { | |
3587 return con.getDatabaseMinorVersion(); | |
3588 } | |
3589 | |
3590 /** | |
3591 * Retrieves the major JDBC version number for this driver. | |
3592 * | |
3593 * @return JDBC version major number | |
3594 */ | |
3595 @Override | |
3596 public int getJDBCMajorVersion() { | |
3597 return 4; // This class implements JDBC 4.2 (at least we try to) | |
3598 } | |
3599 | |
3600 /** | |
3601 * Retrieves the minor JDBC version number for this driver. | |
3602 * | |
3603 * @return JDBC version minor number | |
3604 */ | |
3605 @Override | |
3606 public int getJDBCMinorVersion() { | |
3607 return 2; // This class implements JDBC 4.2 (at least we try to) | |
3608 } | |
3609 | |
3610 /** | |
3611 * Indicates whether the SQLSTATEs returned by <code>SQLException.getSQLState</code> | |
3612 * is X/Open (now known as Open Group) SQL CLI or SQL:2003. | |
3613 * @return the type of SQLSTATEs, one of: | |
3614 * sqlStateXOpen or | |
3615 * sqlStateSQL | |
3616 */ | |
3617 @Override | |
3618 public int getSQLStateType() { | |
3619 // At least this driver conforms with SQLSTATE to the SQL:2003 standard | |
3620 return DatabaseMetaData.sqlStateSQL; | |
3621 } | |
3622 | |
3623 /** | |
3624 * Indicates whether updates made to a LOB are made on a copy or directly | |
3625 * to the LOB. | |
3626 * @return <code>true</code> if updates are made to a copy of the LOB; | |
3627 * <code>false</code> if updates are made directly to the LOB | |
3628 */ | |
3629 @Override | |
3630 public boolean locatorsUpdateCopy() { | |
3631 // not that we have it, but in a transaction it will be copy-on-write | |
3632 return true; | |
3633 } | |
3634 | |
3635 /** | |
3636 * Retrieves whether this database supports statement pooling. | |
3637 * | |
3638 * @return <code>true</code> is so; | |
3639 <code>false</code> otherwise | |
3640 */ | |
3641 @Override | |
3642 public boolean supportsStatementPooling() { | |
3643 // For the moment, I don't think so | |
3644 return false; | |
3645 } | |
3646 | |
3647 //== 1.6 methods (JDBC 4) | |
3648 | |
3649 /** | |
3650 * Indicates whether or not this data source supports the SQL ROWID | |
3651 * type, and if so the lifetime for which a RowId object remains | |
3652 * valid. | |
3653 * | |
3654 * @return ROWID_UNSUPPORTED for now | |
3655 */ | |
3656 @Override | |
3657 public RowIdLifetime getRowIdLifetime() { | |
3658 // I believe we don't do rowids | |
3659 return RowIdLifetime.ROWID_UNSUPPORTED; | |
3660 } | |
3661 | |
3662 /** | |
3663 * Get the schema names available in this database. The results | |
3664 * are ordered by schema name. | |
3665 * | |
3666 * <P>The schema column is: | |
3667 * <OL> | |
3668 * <LI><B>TABLE_SCHEM</B> String => schema name | |
3669 * <LI><B>TABLE_CATALOG</B> String => catalog name (may be null) | |
3670 * </OL> | |
3671 * | |
3672 * @return ResultSet each row has a single String column that is a | |
3673 * schema name | |
3674 * @throws SQLException if a database error occurs | |
3675 */ | |
3676 @Override | |
3677 public ResultSet getSchemas() throws SQLException { | |
3678 return getSchemas(null, null); | |
3679 } | |
3680 | |
3681 /** | |
3682 * Retrieves whether this database supports invoking user-defined or | |
3683 * vendor functions using the stored procedure escape syntax. | |
3684 * | |
3685 * @return true if so; false otherwise | |
3686 */ | |
3687 @Override | |
3688 public boolean supportsStoredFunctionsUsingCallSyntax() { | |
3689 return false; | |
3690 } | |
3691 | |
3692 /** | |
3693 * Retrieves whether a SQLException while autoCommit is true | |
3694 * inidcates that all open ResultSets are closed, even ones that are | |
3695 * holdable. When a SQLException occurs while autocommit is true, it | |
3696 * is vendor specific whether the JDBC driver responds with a commit | |
3697 * operation, a rollback operation, or by doing neither a commit nor | |
3698 * a rollback. A potential result of this difference is in whether | |
3699 * or not holdable ResultSets are closed. | |
3700 * | |
3701 * @return true if so; false otherwise | |
3702 */ | |
3703 @Override | |
3704 public boolean autoCommitFailureClosesAllResultSets() { | |
3705 // The driver caches most of it, and as far as I knoww the | |
3706 // server doesn't close outstanding result handles on commit | |
3707 // failure either. | |
3708 return false; | |
3709 } | |
3710 | |
3711 /** | |
3712 * Retrieves a list of the client info properties that the driver | |
3713 * supports. The result set contains the following columns | |
3714 * | |
3715 * 1. NAME String => The name of the client info property | |
3716 * 2. MAX_LEN int => The maximum length of the value for the | |
3717 * property | |
3718 * 3. DEFAULT_VALUE String => The default value of the | |
3719 * property | |
3720 * 4. DESCRIPTION String => A description of the | |
3721 * property. This will typically contain information as | |
3722 * to where this property is stored in the database. | |
3723 * | |
3724 * The ResultSet is sorted by the NAME column | |
3725 * | |
3726 * @return A ResultSet object; each row is a supported client info property | |
3727 * @throws SQLException if a database access error occurs | |
3728 */ | |
3729 @Override | |
3730 public ResultSet getClientInfoProperties() throws SQLException { | |
3731 // for a list of connection properties see also MonetConnection.java constructor MonetConnection(Properties props) | |
3732 final String query = | |
3733 "SELECT 'host' AS \"NAME\", cast(1024 as int) AS \"MAX_LEN\", 'localhost' AS \"DEFAULT_VALUE\", 'DSN or IP-address of machine running MonetDB' AS \"DESCRIPTION\" UNION ALL " + | |
3734 "SELECT 'port', 5, '50000', 'communication port number of MonetDB server process' UNION ALL " + | |
3735 "SELECT 'user', 1024, '', 'user name to login to MonetDB server' UNION ALL " + | |
3736 "SELECT 'password', 128, '', 'password for user name to login to MonetDB server' UNION ALL " + | |
3737 "SELECT 'language', 16, 'sql', 'language (sql or mal) used to parse commands in MonetDB server' UNION ALL " + | |
3738 "SELECT 'database', 1024, 'demo', 'name of database. It matches the dbfarm subdirectory name' UNION ALL " + | |
3739 "SELECT 'debug', 5, 'false', 'boolean flag true or false' UNION ALL " + | |
3740 "SELECT 'logfile', 1024, 'monet_######.log', 'name of logfile used when debug is enabled' UNION ALL " + | |
3741 "SELECT 'hash', 128, '', 'hash methods list to use in server connection. Supported are SHA512, SHA384, SHA256 and SHA1' UNION ALL " + | |
3742 "SELECT 'treat_blob_as_binary', 5, 'false', 'should blob columns be mapped to Types.VARBINARY instead of default Types.BLOB in ResultSets and PreparedStatements' UNION ALL " + | |
3743 "SELECT 'treat_clob_as_varchar', 5, 'false', 'should clob columns be mapped to Types.VARCHAR instead of default Types.CLOB in ResultSets and PreparedStatements' UNION ALL " + | |
3744 "SELECT 'so_timeout', 10, '0', 'timeout (in milliseconds) of communication socket. 0 means no timeout is set' " + | |
3745 "ORDER BY \"NAME\""; | |
3746 | |
3747 return executeMetaDataQuery(query); | |
3748 } | |
3749 | |
3750 /** | |
3751 * Retrieves a description of the system and user functions | |
3752 * available in the given catalog. | |
3753 * | |
3754 * Only system and user function descriptions matching the schema | |
3755 * and function name criteria are returned. They are ordered by | |
3756 * FUNCTION_CAT, FUNCTION_SCHEM, FUNCTION_NAME and SPECIFIC_ NAME. | |
3757 * | |
3758 * Each function description has the the following columns: | |
3759 * | |
3760 * 1. FUNCTION_CAT String => function catalog (may be null) | |
3761 * 2. FUNCTION_SCHEM String => function schema (may be null) | |
3762 * 3. FUNCTION_NAME String => function name. This is the | |
3763 * name used to invoke the function | |
3764 * 4. REMARKS String => explanatory comment on the function | |
3765 * 5. FUNCTION_TYPE short => kind of function: | |
3766 * * functionResultUnknown - Cannot determine if a return | |
3767 * value or table will be returned | |
3768 * * functionNoTable- Does not return a table | |
3769 * * functionReturnsTable - Returns a table | |
3770 * 6. SPECIFIC_NAME String => the name which uniquely identifies | |
3771 * this function within its schema. This is a user specified, | |
3772 * or DBMS generated, name that may be different then the | |
3773 * FUNCTION_NAME for example with overload functions | |
3774 * | |
3775 * A user may not have permission to execute any of the functions | |
3776 * that are returned by getFunctions. | |
3777 * | |
3778 * @param catalog a catalog name; must match the catalog name as it | |
3779 * is stored in the database; "" retrieves those without a | |
3780 * catalog; null means that the catalog name should not be | |
3781 * used to narrow the search | |
3782 * @param schemaPattern a schema name pattern; must match the schema | |
3783 * name as it is stored in the database; "" retrieves those | |
3784 * without a schema; null means that the schema name should | |
3785 * not be used to narrow the search | |
3786 * @param functionNamePattern a function name pattern; must match | |
3787 * the function name as it is stored in the database | |
3788 * Note that our implementation allows this param to be null also (for efficiency as no extra LIKE '%' condition is added to be evaluated). | |
3789 * @return ResultSet - each row is a function description | |
3790 * @throws SQLException if a database access error occurs | |
3791 */ | |
3792 @Override | |
3793 public ResultSet getFunctions( | |
3794 final String catalog, | |
3795 final String schemaPattern, | |
3796 final String functionNamePattern | |
3797 ) throws SQLException | |
3798 { | |
3799 final boolean useCommentsTable = con.commentsTableExists(); | |
3800 final StringBuilder query = new StringBuilder(800); | |
3801 query.append("SELECT cast(null as char(1)) AS \"FUNCTION_CAT\", " + | |
3802 "s.\"name\" AS \"FUNCTION_SCHEM\", " + | |
3803 "f.\"name\" AS \"FUNCTION_NAME\", ") | |
3804 .append(useCommentsTable ? "COALESCE(cm.\"remark\", cast(f.\"func\" as varchar(9999)))" : "cast(f.\"func\" as varchar(9999))").append(" AS \"REMARKS\", " + | |
3805 "CASE WHEN f.\"type\" IN (1,2,3,4,6) THEN ").append(DatabaseMetaData.functionNoTable) | |
3806 .append(" WHEN f.\"type\" IN (5,7) THEN ").append(DatabaseMetaData.functionReturnsTable) | |
3807 .append(" ELSE ").append(DatabaseMetaData.functionResultUnknown).append(" END AS \"FUNCTION_TYPE\", " + | |
3808 // only the id value uniquely identifies a function. Include it to be able to differentiate between multiple overloaded functions with the same name | |
3809 "cast(f.\"id\" as varchar(10)) AS \"SPECIFIC_NAME\" " + | |
3810 "FROM \"sys\".\"functions\" f " + | |
3811 "JOIN \"sys\".\"schemas\" s ON f.\"schema_id\" = s.\"id\" "); | |
3812 if (useCommentsTable) { | |
3813 query.append("LEFT OUTER JOIN \"sys\".\"comments\" cm ON (f.\"id\" = cm.\"id\") "); | |
3814 } | |
3815 // only functions, so exclude procedures (type = 2). Those will to be returned via getProcedures() | |
3816 query.append("WHERE f.\"type\" <> 2"); | |
3817 | |
3818 if (catalog != null && !catalog.isEmpty()) { | |
3819 // non-empty catalog selection. | |
3820 // as we do not support catalogs this always results in no rows returned | |
3821 query.append(" AND 1=0"); | |
3822 } else { | |
3823 if (schemaPattern != null && !schemaPattern.equals("%")) { | |
3824 query.append(" AND s.\"name\" ").append(composeMatchPart(schemaPattern)); | |
3825 } | |
3826 if (functionNamePattern != null && !functionNamePattern.equals("%")) { | |
3827 query.append(" AND f.\"name\" ").append(composeMatchPart(functionNamePattern)); | |
3828 } | |
3829 } | |
3830 | |
3831 query.append(" ORDER BY \"FUNCTION_SCHEM\", \"FUNCTION_NAME\", \"SPECIFIC_NAME\""); | |
3832 | |
3833 return executeMetaDataQuery(query.toString()); | |
3834 } | |
3835 | |
3836 /** | |
3837 * Retrieves a description of the given catalog's system or user | |
3838 * function parameters and return type. | |
3839 * | |
3840 * Only descriptions matching the schema, function and parameter name criteria are returned. | |
3841 * They are ordered by FUNCTION_CAT, FUNCTION_SCHEM, FUNCTION_NAME and SPECIFIC_ NAME. | |
3842 * Within this, the return value, if any, is first. Next are the parameter descriptions in call order. | |
3843 * The column descriptions follow in column number order. | |
3844 * | |
3845 * 1. FUNCTION_CAT String => function catalog (may be null) | |
3846 * 2. FUNCTION_SCHEM String => function schema (may be null) | |
3847 * 3. FUNCTION_NAME String => function name. This is the name used to invoke the function | |
3848 * 4. COLUMN_NAME String => column/parameter name | |
3849 * 5. COLUMN_TYPE Short => kind of column/parameter: | |
3850 * functionColumnUnknown - nobody knows | |
3851 * functionColumnIn - IN parameter | |
3852 * functionColumnInOut - INOUT parameter | |
3853 * functionColumnOut - OUT parameter | |
3854 * functionColumnReturn - function return value | |
3855 * functionColumnResult - Indicates that the parameter or column is a column in the ResultSet | |
3856 * 6. DATA_TYPE int => SQL type from java.sql.Types | |
3857 * 7. TYPE_NAME String => SQL type name, for a UDT type the type name is fully qualified | |
3858 * 8. PRECISION int => precision | |
3859 * 9. LENGTH int => length in bytes of data | |
3860 * 10. SCALE short => scale - null is returned for data types where SCALE is not applicable. | |
3861 * 11. RADIX short => radix | |
3862 * 12. NULLABLE short => can it contain NULL. | |
3863 * functionNoNulls - does not allow NULL values | |
3864 * functionNullable - allows NULL values | |
3865 * functionNullableUnknown - nullability unknown | |
3866 * 13. REMARKS String => comment describing column/parameter | |
3867 * 14. CHAR_OCTET_LENGTH int => the maximum length of binary and character based parameters or columns. For any other datatype the returned value is a NULL | |
3868 * 15. ORDINAL_POSITION int => the ordinal position, starting from 1, for the input and output parameters. | |
3869 * A value of 0 is returned if this row describes the function's return value. For result set columns, it is the ordinal position of the column in the result set starting from 1. | |
3870 * 16. IS_NULLABLE String => ISO rules are used to determine the nullability for a parameter or column. | |
3871 * YES --- if the parameter or column can include NULLs | |
3872 * NO --- if the parameter or column cannot include NULLs | |
3873 * empty string --- if the nullability for the parameter or column is unknown | |
3874 * 17. SPECIFIC_NAME String => the name which uniquely identifies this function within its schema. | |
3875 * This is a user specified, or DBMS generated, name that may be different then the FUNCTION_NAME for example with overload functions | |
3876 * | |
3877 * @param catalog a catalog name; must match the catalog name as | |
3878 * it is stored in the database; "" retrieves those without a | |
3879 * catalog; null means that the catalog name should not be | |
3880 * used to narrow the search | |
3881 * @param schemaPattern a schema name pattern; must match the schema | |
3882 * name as it is stored in the database; "" retrieves those | |
3883 * without a schema; null means that the schema name should | |
3884 * not be used to narrow the search | |
3885 * @param functionNamePattern a procedure name pattern; must match the | |
3886 * function name as it is stored in the database | |
3887 * Note that our implementation allows this param to be null also (for efficiency as no extra LIKE '%' condition is added to be evaluated). | |
3888 * @param columnNamePattern a parameter name pattern; must match the | |
3889 * parameter or column name as it is stored in the database | |
3890 * Note that our implementation allows this param to be null also (for efficiency as no extra LIKE '%' condition is added to be evaluated). | |
3891 * @return ResultSet - each row describes a user function parameter, | |
3892 * column or return type | |
3893 * @throws SQLException - if a database access error occurs | |
3894 */ | |
3895 @Override | |
3896 public ResultSet getFunctionColumns( | |
3897 final String catalog, | |
3898 final String schemaPattern, | |
3899 final String functionNamePattern, | |
3900 final String columnNamePattern | |
3901 ) throws SQLException | |
3902 { | |
3903 final StringBuilder query = new StringBuilder(2600); | |
3904 query.append("SELECT cast(null as char(1)) AS \"FUNCTION_CAT\", " + | |
3905 "s.\"name\" AS \"FUNCTION_SCHEM\", " + | |
3906 "f.\"name\" AS \"FUNCTION_NAME\", " + | |
3907 "a.\"name\" AS \"COLUMN_NAME\", " + | |
3908 "cast(CASE a.\"inout\"" + | |
3909 " WHEN 0 THEN (CASE a.\"number\" WHEN 0 THEN ") | |
3910 .append(DatabaseMetaData.functionReturn).append(" ELSE ").append(DatabaseMetaData.functionColumnOut).append(" END)" + | |
3911 " WHEN 1 THEN ").append(DatabaseMetaData.functionColumnIn) | |
3912 .append(" ELSE ").append(DatabaseMetaData.functionColumnUnknown).append(" END AS smallint) AS \"COLUMN_TYPE\", " + | |
3913 "cast(").append(MonetDriver.getSQLTypeMap("a.\"type\"")).append(" AS int) AS \"DATA_TYPE\", " + | |
3914 "a.\"type\" AS \"TYPE_NAME\", " + | |
3915 "CASE a.\"type\" WHEN 'tinyint' THEN 3 WHEN 'smallint' THEN 5 WHEN 'int' THEN 10 WHEN 'bigint' THEN 19" + | |
3916 " WHEN 'hugeint' THEN 38 WHEN 'oid' THEN 19 WHEN 'wrd' THEN 19 ELSE a.\"type_digits\" END AS \"PRECISION\", " + | |
3917 "CASE a.\"type\" WHEN 'tinyint' THEN 1 WHEN 'smallint' THEN 2 WHEN 'int' THEN 4 WHEN 'bigint' THEN 8" + | |
3918 " WHEN 'hugeint' THEN 16 WHEN 'oid' THEN 8 WHEN 'wrd' THEN 8 ELSE a.\"type_digits\" END AS \"LENGTH\", " + | |
3919 "cast(CASE WHEN a.\"type\" IN ('tinyint','smallint','int','bigint','hugeint','oid','wrd','decimal','numeric'," + | |
3920 "'time','timetz','timestamp','timestamptz','day_interval','month_interval','sec_interval') THEN a.\"type_scale\" ELSE NULL END AS smallint) AS \"SCALE\", " + | |
3921 "cast(CASE WHEN a.\"type\" IN ('tinyint','smallint','int','bigint','hugeint','oid','wrd','decimal','numeric') THEN 10" + | |
3922 " WHEN a.\"type\" IN ('real','float','double') THEN 2 ELSE NULL END AS smallint) AS \"RADIX\", " + | |
3923 "cast(").append(DatabaseMetaData.functionNullableUnknown).append(" AS smallint) AS \"NULLABLE\", " + | |
3924 "cast(null as char(1)) AS \"REMARKS\", " + | |
3925 "cast(CASE WHEN a.\"type\" IN ('char','varchar','clob') THEN a.\"type_digits\" ELSE NULL END as int) AS \"CHAR_OCTET_LENGTH\", " + | |
3926 "cast(a.\"number\" as int) AS \"ORDINAL_POSITION\", " + | |
3927 "cast('' as varchar(3)) AS \"IS_NULLABLE\", " + | |
3928 // the specific name contains the function id, in order to be able to match the args to the correct overloaded function name | |
3929 "cast(f.\"id\" as varchar(10)) AS \"SPECIFIC_NAME\" " + | |
3930 "FROM \"sys\".\"args\" a " + | |
3931 "JOIN \"sys\".\"functions\" f ON a.\"func_id\" = f.\"id\" " + | |
3932 "JOIN \"sys\".\"schemas\" s ON f.\"schema_id\" = s.\"id\" " + | |
3933 // only functions, so exclude procedures (type = 2). Those will to be returned via getProcedureColumns() | |
3934 "WHERE f.\"type\" <> 2"); | |
3935 | |
3936 if (catalog != null && !catalog.isEmpty()) { | |
3937 // non-empty catalog selection. | |
3938 // as we do not support catalogs this always results in no rows returned | |
3939 query.append(" AND 1=0"); | |
3940 } else { | |
3941 if (schemaPattern != null && !schemaPattern.equals("%")) { | |
3942 query.append(" AND s.\"name\" ").append(composeMatchPart(schemaPattern)); | |
3943 } | |
3944 if (functionNamePattern != null && !functionNamePattern.equals("%")) { | |
3945 query.append(" AND f.\"name\" ").append(composeMatchPart(functionNamePattern)); | |
3946 } | |
3947 if (columnNamePattern != null && !columnNamePattern.equals("%")) { | |
3948 query.append(" AND a.\"name\" ").append(composeMatchPart(columnNamePattern)); | |
3949 } | |
3950 } | |
3951 | |
3952 query.append(" ORDER BY \"FUNCTION_SCHEM\", \"FUNCTION_NAME\", \"SPECIFIC_NAME\", \"ORDINAL_POSITION\""); | |
3953 | |
3954 return executeMetaDataQuery(query.toString()); | |
3955 } | |
3956 | |
3957 //== 1.7 methods (JDBC 4.1) | |
3958 | |
3959 /** | |
3960 * Retrieves a description of the pseudo or hidden columns available | |
3961 * in a given table within the specified catalog and schema. Pseudo | |
3962 * or hidden columns may not always be stored within a table and are | |
3963 * not visible in a ResultSet unless they are specified in the | |
3964 * query's outermost SELECT list. Pseudo or hidden columns may not | |
3965 * necessarily be able to be modified. | |
3966 * If there are no pseudo or hidden columns, an empty ResultSet is returned. | |
3967 * | |
3968 * Only column descriptions matching the catalog, schema, table and column name criteria are returned. | |
3969 * They are ordered by TABLE_CAT,TABLE_SCHEM, TABLE_NAME and COLUMN_NAME. | |
3970 * | |
3971 * Each column description has the following columns: | |
3972 * | |
3973 * 1. TABLE_CAT String => table catalog (may be null) | |
3974 * 2. TABLE_SCHEM String => table schema (may be null) | |
3975 * 3. TABLE_NAME String => table name | |
3976 * 4. COLUMN_NAME String => column name | |
3977 * 5. DATA_TYPE int => SQL type from java.sql.Types | |
3978 * 6. COLUMN_SIZE int => column size. | |
3979 * 7. DECIMAL_DIGITS int => the number of fractional digits. Null is returned for data types where DECIMAL_DIGITS is not applicable. | |
3980 * 8. NUM_PREC_RADIX int => Radix (typically either 10 or 2) | |
3981 * 9. COLUMN_USAGE String => The allowed usage for the column. The value returned will correspond to the enum name returned by PseudoColumnUsage.name() | |
3982 * 10. REMARKS String => comment describing column (may be null) | |
3983 * 11. CHAR_OCTET_LENGTH int => for char types the maximum number of bytes in the column | |
3984 * 12. IS_NULLABLE String => ISO rules are used to determine the nullability for a column. | |
3985 * YES --- if the column can include NULLs | |
3986 * NO --- if the column cannot include NULLs | |
3987 * empty string --- if the nullability for the column is unknown | |
3988 * | |
3989 * @param catalog a catalog name | |
3990 * @param schemaPattern a schema name pattern | |
3991 * @param tableNamePattern a table name pattern | |
3992 * @param columnNamePattern a column name pattern | |
3993 * @return ResultSet where each row is a column description | |
3994 * @throws SQLException if a database access error occurs | |
3995 */ | |
3996 @Override | |
3997 public ResultSet getPseudoColumns( | |
3998 final String catalog, | |
3999 final String schemaPattern, | |
4000 final String tableNamePattern, | |
4001 final String columnNamePattern | |
4002 ) throws SQLException | |
4003 { | |
4004 // MonetDB currently does not support pseudo or hidden columns, so return an empty ResultSet | |
4005 final String query = | |
4006 "SELECT cast(null as char(1)) AS \"TABLE_CAT\", " + | |
4007 "'' AS \"TABLE_SCHEM\", " + | |
4008 "'' AS \"TABLE_NAME\", " + | |
4009 "'' AS \"COLUMN_NAME\", " + | |
4010 "cast(0 as int) AS \"DATA_TYPE\", " + | |
4011 "cast(0 as int) AS \"COLUMN_SIZE\", " + | |
4012 "cast(0 as int) AS \"DECIMAL_DIGITS\", " + | |
4013 "cast(0 as int) AS \"NUM_PREC_RADIX\", " + | |
4014 "'' AS \"COLUMN_USAGE\", " + | |
4015 "'' AS \"REMARKS\", " + | |
4016 "cast(0 as int) AS \"CHAR_OCTET_LENGTH\", " + | |
4017 "'' AS \"IS_NULLABLE\" " + | |
4018 "WHERE 1=0"; | |
4019 | |
4020 return executeMetaDataQuery(query); | |
4021 } | |
4022 | |
4023 /** | |
4024 * Retrieves whether a generated key will always be returned if the | |
4025 * column name(s) or index(es) specified for the auto generated key | |
4026 * column(s) are valid and the statement succeeds. The key that is | |
4027 * returned may or may not be based on the column(s) for the auto | |
4028 * generated key. | |
4029 * | |
4030 * @return true if so, false otherwise | |
4031 * @throws SQLException - if a database access error occurs | |
4032 */ | |
4033 @Override | |
4034 public boolean generatedKeyAlwaysReturned() throws SQLException { | |
4035 return true; | |
4036 } | |
4037 | |
4038 //== Java 1.8 methods (JDBC 4.2) | |
4039 | |
4040 /** | |
4041 * Retrieves the maximum number of bytes this database allows for the logical size for a LOB. | |
4042 * The default implementation will return 0 | |
4043 * @return the maximum number of bytes allowed; a result of zero means that there is no limit or the limit is not known | |
4044 */ | |
4045 @Override | |
4046 public long getMaxLogicalLobSize() { | |
4047 return 0; | |
4048 } | |
4049 | |
4050 /** | |
4051 * Retrieves whether this database supports REF CURSOR. | |
4052 * The default implementation will return false | |
4053 * @return true if this database supports REF CURSOR; false otherwise | |
4054 */ | |
4055 @Override | |
4056 public boolean supportsRefCursors() { | |
4057 return false; | |
4058 } | |
4059 | |
4060 //== end methods interface DatabaseMetaData | |
4061 | |
4062 | |
4063 //== internal helper methods which do not belong to the JDBC interface | |
4064 | |
4065 /** | |
4066 * Internal utility method to create a Statement object, execute a query and return the ResulSet object which allows scrolling. | |
4067 * As the Statement object is created internally (the caller does not see it and thus can not close it), | |
4068 * we set it to close (and free server resources) when the ResultSet object is closed by the caller. | |
4069 */ | |
4070 private final ResultSet executeMetaDataQuery(final String query) throws SQLException { | |
4071 final Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); | |
4072 ResultSet rs = null; | |
4073 if (stmt != null) { | |
4074 // for debug: System.out.println("SQL (len " + query.length() + "): " + query); | |
4075 rs = stmt.executeQuery(query); | |
4076 if (rs != null) { | |
4077 /* we want the statement object to be closed also when the resultset is closed by the caller */ | |
4078 stmt.closeOnCompletion(); | |
4079 } else { | |
4080 /* failed to produce a resultset, so release resources for created statement object now */ | |
4081 stmt.close(); | |
4082 } | |
4083 } | |
4084 return rs; | |
4085 } | |
4086 | |
4087 /** | |
4088 * Returns a SQL match part string where depending on the input value we | |
4089 * compose an exact match (use =) or match with wildcards (use LIKE) or IS NULL | |
4090 * | |
4091 * @param in the string to match | |
4092 * @return the SQL match part string | |
4093 */ | |
4094 private final String composeMatchPart(final String in) { | |
4095 if (in == null) | |
4096 return "IS NULL"; | |
4097 | |
4098 String cmp = "= '"; | |
4099 // check if SQL wildcards are used in the input, if so use LIKE | |
4100 if (in.contains("%") || in.contains("_")) | |
4101 cmp = "LIKE '"; | |
4102 | |
4103 return cmp + con.escapeSpecialChars(in) + "'"; | |
4104 } | |
4105 | |
4106 /** | |
4107 * Returns the given string between two double quotes for usage as | |
4108 * exact column or table name in SQL queries. | |
4109 * | |
4110 * @param in the string to quote | |
4111 * @return the quoted string | |
4112 */ | |
4113 // @SuppressWarnings("unused") | |
4114 // private static final String dq(String in) { | |
4115 // return "\"" + in.replaceAll("\\\\", "\\\\\\\\").replaceAll("\"", "\\\\\"") + "\""; | |
4116 // } | |
4117 | |
4118 //== end helper methods | |
4119 } |