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 =&gt; 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 =&gt; procedure catalog (may be null)
1613 * <li><b>PROCEDURE_SCHEM</b> String =&gt; procedure schema (may be null)
1614 * <li><b>PROCEDURE_NAME</b> String =&gt; 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 =&gt; explanatory comment on the procedure
1619 * <li><b>PROCEDURE_TYPE</b> short =&gt; 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 =&gt; 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 =&gt; procedure catalog (may be null)
1698 * <li><b>PROCEDURE_SCHEM</b> String =&gt; procedure schema (may be null)
1699 * <li><b>PROCEDURE_NAME</b> String =&gt; procedure name
1700 * <li><b>COLUMN_NAME</b> String =&gt; column/parameter name
1701 * <li><b>COLUMN_TYPE</b> Short =&gt; 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 =&gt; SQL type from java.sql.Types
1710 * <li><b>TYPE_NAME</b> String =&gt; SQL type name, for a UDT type the type name is fully qualified
1711 * <li><b>PRECISION</b> int =&gt; precision
1712 * <li><b>LENGTH</b> int =&gt; length in bytes of data
1713 * <li><b>SCALE</b> short =&gt; scale - null is returned for data types where SCALE is not applicable.
1714 * <li><b>RADIX</b> short =&gt; radix
1715 * <li><b>NULLABLE</b> short =&gt; 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 =&gt; comment describing parameter/column
1721 * <li><b>COLUMN_DEF</b> String =&gt; 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 =&gt; reserved for future use
1726 * <li><b>SQL_DATETIME_SUB</b> int =&gt; reserved for future use
1727 * <li><b>CHAR_OCTET_LENGTH</b> int =&gt; 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 =&gt; 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 =&gt; 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 =&gt; 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 =&gt; table catalog (may be null)
1825 * <li><b>TABLE_SCHEM</b> String =&gt; table schema (may be null)
1826 * <li><b>TABLE_NAME</b> String =&gt; table name
1827 * <li><b>TABLE_TYPE</b> String =&gt; table type. Typical types are "TABLE",
1828 * "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", "LOCAL TEMPORARY", "ALIAS", "SYNONYM".
1829 * <li><b>REMARKS</b> String =&gt; explanatory comment on the table
1830 * <li><b>TYPE_CAT</b> String =&gt; the types catalog (may be null)
1831 * <li><b>TYPE_SCHEM</b> String =&gt; the types schema (may be null)
1832 * <li><b>TYPE_NAME</b> String =&gt; type name (may be null)
1833 * <li><b>SELF_REFERENCING_COL_NAME</b> String =&gt; name of the designated "identifier" column of a typed table (may be null)
1834 * <li><b>REF_GENERATION</b> String =&gt; 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 =&gt; schema name
1949 * <LI><B>TABLE_CATALOG</B> String =&gt; 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 =&gt; 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 =&gt; 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 =&gt; table catalog (may be null)
2050 * <LI><B>TABLE_SCHEM</B> String =&gt; table schema (may be null)
2051 * <LI><B>TABLE_NAME</B> String =&gt; table name
2052 * <LI><B>COLUMN_NAME</B> String =&gt; column name
2053 * <LI><B>DATA_TYPE</B> int =&gt; SQL type from java.sql.Types
2054 * <LI><B>TYPE_NAME</B> String =&gt; Data source dependent type name
2055 * <LI><B>COLUMN_SIZE</B> int =&gt; 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 =&gt; the number of fractional digits
2060 * <LI><B>NUM_PREC_RADIX</B> int =&gt; Radix (typically either 10 or 2)
2061 * <LI><B>NULLABLE</B> int =&gt; 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 =&gt; comment describing column (may be null)
2068 * <LI><B>COLUMN_DEF</B> String =&gt; default value (may be null)
2069 * <LI><B>SQL_DATA_TYPE</B> int =&gt; unused
2070 * <LI><B>SQL_DATETIME_SUB</B> int =&gt; unused
2071 * <LI><B>CHAR_OCTET_LENGTH</B> int =&gt; for char types the
2072 * maximum number of bytes in the column
2073 * <LI><B>ORDINAL_POSITION</B> int =&gt; index of column in table
2074 * (starting at 1)
2075 * <LI><B>IS_NULLABLE</B> String =&gt; "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 =&gt; 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 =&gt; 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 =&gt; 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 =&gt; 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 =&gt; 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 =&gt; 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 =&gt; table catalog (may be null)
2191 * <LI><B>TABLE_SCHEM</B> String =&gt; table schema (may be null)
2192 * <LI><B>TABLE_NAME</B> String =&gt; table name
2193 * <LI><B>COLUMN_NAME</B> String =&gt; column name
2194 * <LI><B>GRANTOR</B> =&gt; grantor of access (may be null)
2195 * <LI><B>GRANTEE</B> String =&gt; grantee of access
2196 * <LI><B>PRIVILEGE</B> String =&gt; name of access (SELECT,
2197 * INSERT, UPDATE, REFRENCES, ...)
2198 * <LI><B>IS_GRANTABLE</B> String =&gt; "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 =&gt; table catalog (may be null)
2290 * <LI><B>TABLE_SCHEM</B> String =&gt; table schema (may be null)
2291 * <LI><B>TABLE_NAME</B> String =&gt; table name
2292 * <LI><B>GRANTOR</B> =&gt; grantor of access (may be null)
2293 * <LI><B>GRANTEE</B> String =&gt; grantee of access
2294 * <LI><B>PRIVILEGE</B> String =&gt; name of access (SELECT,
2295 * INSERT, UPDATE, REFRENCES, ...)
2296 * <LI><B>IS_GRANTABLE</B> String =&gt; "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 =&gt; 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 =&gt; column name
2380 * <LI><B>DATA_TYPE</B> int =&gt; SQL data type from java.sql.Types
2381 * <LI><B>TYPE_NAME</B> String =&gt; Data source dependent type name
2382 * <LI><B>COLUMN_SIZE</B> int =&gt; precision
2383 * <LI><B>BUFFER_LENGTH</B> int =&gt; not used
2384 * <LI><B>DECIMAL_DIGITS</B> short =&gt; scale
2385 * <LI><B>PSEUDO_COLUMN</B> short =&gt; 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 =&gt; is not used
2478 * <LI><B>COLUMN_NAME</B> String =&gt; column name
2479 * <LI><B>DATA_TYPE</B> int =&gt; SQL data type from java.sql.Types
2480 * <LI><B>TYPE_NAME</B> String =&gt; Data source dependent type name
2481 * <LI><B>COLUMN_SIZE</B> int =&gt; precision
2482 * <LI><B>BUFFER_LENGTH</B> int =&gt; length of column value in bytes
2483 * <LI><B>DECIMAL_DIGITS</B> short =&gt; scale
2484 * <LI><B>PSEUDO_COLUMN</B> short =&gt; 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 =&gt; table catalog (may be null)
2528 * <LI><B>TABLE_SCHEM</B> String =&gt; table schema (may be null)
2529 * <LI><B>TABLE_NAME</B> String =&gt; table name
2530 * <LI><B>COLUMN_NAME</B> String =&gt; column name
2531 * <LI><B>KEY_SEQ</B> short =&gt; sequence number within primary key
2532 * <LI><B>PK_NAME</B> String =&gt; 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 =&gt; primary key table catalog
2615 * being imported (may be null)
2616 * <LI><B>PKTABLE_SCHEM</B> String =&gt; primary key table schema
2617 * being imported (may be null)
2618 * <LI><B>PKTABLE_NAME</B> String =&gt; primary key table name
2619 * being imported
2620 * <LI><B>PKCOLUMN_NAME</B> String =&gt; primary key column name
2621 * being imported
2622 * <LI><B>FKTABLE_CAT</B> String =&gt; foreign key table catalog (may be null)
2623 * <LI><B>FKTABLE_SCHEM</B> String =&gt; foreign key table schema (may be null)
2624 * <LI><B>FKTABLE_NAME</B> String =&gt; foreign key table name
2625 * <LI><B>FKCOLUMN_NAME</B> String =&gt; foreign key column name
2626 * <LI><B>KEY_SEQ</B> short =&gt; 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 =&gt; 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 =&gt; 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 =&gt; foreign key name (may be null)
2650 * <LI><B>PK_NAME</B> String =&gt; primary key name (may be null)
2651 * <LI><B>DEFERRABILITY</B> short =&gt; 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 =&gt; primary key table catalog (may be null)
2703 * <LI><B>PKTABLE_SCHEM</B> String =&gt; primary key table schema (may be null)
2704 * <LI><B>PKTABLE_NAME</B> String =&gt; primary key table name
2705 * <LI><B>PKCOLUMN_NAME</B> String =&gt; primary key column name
2706 * <LI><B>FKTABLE_CAT</B> String =&gt; foreign key table catalog (may be null)
2707 * being exported (may be null)
2708 * <LI><B>FKTABLE_SCHEM</B> String =&gt; foreign key table schema (may be null)
2709 * being exported (may be null)
2710 * <LI><B>FKTABLE_NAME</B> String =&gt; foreign key table name
2711 * being exported
2712 * <LI><B>FKCOLUMN_NAME</B> String =&gt; foreign key column name
2713 * being exported
2714 * <LI><B>KEY_SEQ</B> short =&gt; 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 =&gt; 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 =&gt; 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 =&gt; foreign key identifier (may be null)
2738 * <LI><B>PK_NAME</B> String =&gt; primary key identifier (may be null)
2739 * <LI><B>DEFERRABILITY</B> short =&gt; 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 =&gt; primary key table catalog (may be null)
2794 * <LI><B>PKTABLE_SCHEM</B> String =&gt; primary key table schema (may be null)
2795 * <LI><B>PKTABLE_NAME</B> String =&gt; primary key table name
2796 * <LI><B>PKCOLUMN_NAME</B> String =&gt; primary key column name
2797 * <LI><B>FKTABLE_CAT</B> String =&gt; foreign key table catalog (may be null)
2798 * being exported (may be null)
2799 * <LI><B>FKTABLE_SCHEM</B> String =&gt; foreign key table schema (may be null)
2800 * being exported (may be null)
2801 * <LI><B>FKTABLE_NAME</B> String =&gt; foreign key table name
2802 * being exported
2803 * <LI><B>FKCOLUMN_NAME</B> String =&gt; foreign key column name
2804 * being exported
2805 * <LI><B>KEY_SEQ</B> short =&gt; 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 =&gt; 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 =&gt; 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 =&gt; foreign key identifier (may be null)
2829 * <LI><B>PK_NAME</B> String =&gt; primary key identifier (may be null)
2830 * <LI><B>DEFERRABILITY</B> short =&gt; 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 =&gt; Type name
2904 * <LI><B>DATA_TYPE</B> int =&gt; SQL data type from java.sql.Types
2905 * <LI><B>PRECISION</B> int =&gt; maximum precision
2906 * <LI><B>LITERAL_PREFIX</B> String =&gt; prefix used to quote a literal (may be null)
2907 * <LI><B>LITERAL_SUFFIX</B> String =&gt; suffix used to quote a literal (may be null)
2908 * <LI><B>CREATE_PARAMS</B> String =&gt; parameters used in creating
2909 * the type (may be null)
2910 * <LI><B>NULLABLE</B> short =&gt; 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=&gt; is it case sensitive?
2917 * <LI><B>SEARCHABLE</B> short =&gt; 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 =&gt; is it unsigned?
2925 * <LI><B>FIXED_PREC_SCALE</B> boolean =&gt; can it be a money value?
2926 * <LI><B>AUTO_INCREMENT</B> boolean =&gt; can it be used for an
2927 * auto-increment value?
2928 * <LI><B>LOCAL_TYPE_NAME</B> String =&gt; localized version of type name
2929 * (may be null)
2930 * <LI><B>MINIMUM_SCALE</B> short =&gt; minimum scale supported
2931 * <LI><B>MAXIMUM_SCALE</B> short =&gt; maximum scale supported
2932 * <LI><B>SQL_DATA_TYPE</B> int =&gt; unused
2933 * <LI><B>SQL_DATETIME_SUB</B> int =&gt; unused
2934 * <LI><B>NUM_PREC_RADIX</B> int =&gt; 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 =&gt; table catalog (may be null)
2983 * <LI><B>TABLE_SCHEM</B> String =&gt; table schema (may be null)
2984 * <LI><B>TABLE_NAME</B> String =&gt; table name
2985 * <LI><B>NON_UNIQUE</B> boolean =&gt; Can index values be non-unique?
2986 * false when TYPE is tableIndexStatistic
2987 * <LI><B>INDEX_QUALIFIER</B> String =&gt; index catalog (may be null);
2988 * null when TYPE is tableIndexStatistic
2989 * <LI><B>INDEX_NAME</B> String =&gt; index name; null when TYPE is
2990 * tableIndexStatistic
2991 * <LI><B>TYPE</B> short =&gt; 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 =&gt; column sequence number
3000 * within index; zero when TYPE is tableIndexStatistic
3001 * <LI><B>COLUMN_NAME</B> String =&gt; column name; null when TYPE is
3002 * tableIndexStatistic
3003 * <LI><B>ASC_OR_DESC</B> String =&gt; column sort sequence, "A" =&gt; ascending
3004 * "D" =&gt; descending, may be null if sort sequence is not supported;
3005 * null when TYPE is tableIndexStatistic
3006 * <LI><B>CARDINALITY</B> int =&gt; 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 =&gt; 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 =&gt; 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 =&gt; the type's catalog (may be null)
3210 * 2 TYPE_SCHEM String =&gt; type's schema (may be null)
3211 * 3 TYPE_NAME String =&gt; type name
3212 * 4 CLASS_NAME String =&gt; Java class name
3213 * 5 DATA_TYPE int =&gt; type value defined in java.sql.Types. One of JAVA_OBJECT, STRUCT, or DISTINCT
3214 * 6 REMARKS String =&gt; explanatory comment on the type
3215 * 7 BASE_TYPE short =&gt; 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 =&gt; the UDT's catalog (may be <code>null</code>)
3368 * <LI><B>TYPE_SCHEM</B> String =&gt; UDT's schema (may be <code>null</code>)
3369 * <LI><B>TYPE_NAME</B> String =&gt; type name of the UDT
3370 * <LI><B>SUPERTYPE_CAT</B> String =&gt; the direct super type's catalog
3371 * (may be <code>null</code>)
3372 * <LI><B>SUPERTYPE_SCHEM</B> String =&gt; the direct super type's schema
3373 * (may be <code>null</code>)
3374 * <LI><B>SUPERTYPE_NAME</B> String =&gt; 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 =&gt; the type's catalog (may be <code>null</code>)
3418 * <LI><B>TABLE_SCHEM</B> String =&gt; type's schema (may be <code>null</code>)
3419 * <LI><B>TABLE_NAME</B> String =&gt; type name
3420 * <LI><B>SUPERTABLE_NAME</B> String =&gt; 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 =&gt; type catalog (may be <code>null</code>)
3462 * <LI><B>TYPE_SCHEM</B> String =&gt; type schema (may be <code>null</code>)
3463 * <LI><B>TYPE_NAME</B> String =&gt; type name
3464 * <LI><B>ATTR_NAME</B> String =&gt; attribute name
3465 * <LI><B>DATA_TYPE</B> int =&gt; attribute type SQL type from java.sql.Types
3466 * <LI><B>ATTR_TYPE_NAME</B> String =&gt; 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 =&gt; 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 =&gt; the number of fractional digits
3473 * <LI><B>NUM_PREC_RADIX</B> int =&gt; Radix (typically either 10 or 2)
3474 * <LI><B>NULLABLE</B> int =&gt; 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 =&gt; comment describing column (may be <code>null</code>)
3481 * <LI><B>ATTR_DEF</B> String =&gt; default value (may be <code>null</code>)
3482 * <LI><B>SQL_DATA_TYPE</B> int =&gt; unused
3483 * <LI><B>SQL_DATETIME_SUB</B> int =&gt; unused
3484 * <LI><B>CHAR_OCTET_LENGTH</B> int =&gt; for char types the
3485 * maximum number of bytes in the column
3486 * <LI><B>ORDINAL_POSITION</B> int =&gt; index of column in table
3487 * (starting at 1)
3488 * <LI><B>IS_NULLABLE</B> String =&gt; "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 =&gt; 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 =&gt; 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 =&gt; 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 =&gt; 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 =&gt; schema name
3669 * <LI><B>TABLE_CATALOG</B> String =&gt; 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 =&gt; The name of the client info property
3716 * 2. MAX_LEN int =&gt; The maximum length of the value for the
3717 * property
3718 * 3. DEFAULT_VALUE String =&gt; The default value of the
3719 * property
3720 * 4. DESCRIPTION String =&gt; 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 =&gt; function catalog (may be null)
3761 * 2. FUNCTION_SCHEM String =&gt; function schema (may be null)
3762 * 3. FUNCTION_NAME String =&gt; function name. This is the
3763 * name used to invoke the function
3764 * 4. REMARKS String =&gt; explanatory comment on the function
3765 * 5. FUNCTION_TYPE short =&gt; 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 =&gt; 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 =&gt; function catalog (may be null)
3846 * 2. FUNCTION_SCHEM String =&gt; function schema (may be null)
3847 * 3. FUNCTION_NAME String =&gt; function name. This is the name used to invoke the function
3848 * 4. COLUMN_NAME String =&gt; column/parameter name
3849 * 5. COLUMN_TYPE Short =&gt; 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 =&gt; SQL type from java.sql.Types
3857 * 7. TYPE_NAME String =&gt; SQL type name, for a UDT type the type name is fully qualified
3858 * 8. PRECISION int =&gt; precision
3859 * 9. LENGTH int =&gt; length in bytes of data
3860 * 10. SCALE short =&gt; scale - null is returned for data types where SCALE is not applicable.
3861 * 11. RADIX short =&gt; radix
3862 * 12. NULLABLE short =&gt; can it contain NULL.
3863 * functionNoNulls - does not allow NULL values
3864 * functionNullable - allows NULL values
3865 * functionNullableUnknown - nullability unknown
3866 * 13. REMARKS String =&gt; comment describing column/parameter
3867 * 14. CHAR_OCTET_LENGTH int =&gt; 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 =&gt; 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 =&gt; 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 =&gt; 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 =&gt; table catalog (may be null)
3974 * 2. TABLE_SCHEM String =&gt; table schema (may be null)
3975 * 3. TABLE_NAME String =&gt; table name
3976 * 4. COLUMN_NAME String =&gt; column name
3977 * 5. DATA_TYPE int =&gt; SQL type from java.sql.Types
3978 * 6. COLUMN_SIZE int =&gt; column size.
3979 * 7. DECIMAL_DIGITS int =&gt; the number of fractional digits. Null is returned for data types where DECIMAL_DIGITS is not applicable.
3980 * 8. NUM_PREC_RADIX int =&gt; Radix (typically either 10 or 2)
3981 * 9. COLUMN_USAGE String =&gt; The allowed usage for the column. The value returned will correspond to the enum name returned by PseudoColumnUsage.name()
3982 * 10. REMARKS String =&gt; comment describing column (may be null)
3983 * 11. CHAR_OCTET_LENGTH int =&gt; for char types the maximum number of bytes in the column
3984 * 12. IS_NULLABLE String =&gt; 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 }