comparison src/main/java/nl/cwi/monetdb/jdbc/MonetDatabaseMetaData.java @ 0:a5a898f6886c

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