Mercurial > hg > monetdb-java
comparison src/main/java/nl/cwi/monetdb/jdbc/MonetDatabaseMetaData.java @ 331:6ed8f5b1f9ed
Corrected method DatabaseMetaData.getBestRowIdentifier(). It used to return columns of both primary key and unique constraints. Now it only returns the columns of the primary key if it has one, else columns of a unique constraint.
author | Martin van Dinther <martin.van.dinther@monetdbsolutions.com> |
---|---|
date | Wed, 18 Sep 2019 20:02:44 +0200 (2019-09-18) |
parents | 8701024a9bb0 |
children | e8962bdaa206 |
comparison
equal
deleted
inserted
replaced
330:98ae44c5fd56 | 331:6ed8f5b1f9ed |
---|---|
458 } catch (SQLException e) { | 458 } catch (SQLException e) { |
459 /* ignore */ | 459 /* ignore */ |
460 } finally { | 460 } finally { |
461 MonetConnection.closeResultsetStatement(rs, st); | 461 MonetConnection.closeResultsetStatement(rs, st); |
462 } | 462 } |
463 // for debug: System.out.println("SQL query: " + query + "\nResult string: " + sb.toString()); | 463 // for debug: System.out.println("SQL (len " + query.length() + "): " + query + "\nResult string: " + sb.toString()); |
464 return sb.toString(); | 464 return sb.toString(); |
465 } | 465 } |
466 | 466 |
467 // SQL query parts shared by four get<Type>Functions() below | 467 // SQL query parts shared by four get<Type>Functions() below |
468 private static final String FunctionsSelect = "SELECT DISTINCT CASE WHEN \"language\" > 0 THEN s.\"name\"||'.'||f.\"name\" ELSE f.\"name\" END FROM \"sys\".\"functions\" f JOIN \"sys\".\"schemas\" s ON f.\"schema_id\" = s.\"id\" WHERE "; | 468 private static final String FunctionsSelect = "SELECT DISTINCT CASE WHEN \"language\" > 0 THEN s.\"name\"||'.'||f.\"name\" ELSE f.\"name\" END FROM \"sys\".\"functions\" f JOIN \"sys\".\"schemas\" s ON f.\"schema_id\" = s.\"id\" WHERE "; |
1924 { | 1924 { |
1925 // 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). | 1925 // 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). |
1926 // as of Jul2015 release we also have a new table: sys.table_types with names for the new table types | 1926 // as of Jul2015 release we also have a new table: sys.table_types with names for the new table types |
1927 // for correct behavior we need to know if the server is using the old (pre Jul2015) or new sys.tables.type values | 1927 // for correct behavior we need to know if the server is using the old (pre Jul2015) or new sys.tables.type values |
1928 final boolean preJul2015 = ("11.19.15".compareTo(getDatabaseProductVersion()) >= 0); | 1928 final boolean preJul2015 = ("11.19.15".compareTo(getDatabaseProductVersion()) >= 0); |
1929 /* for debug: System.out.println("getDatabaseProductVersion() is " + getDatabaseProductVersion() + " preJul2015 is " + preJul2015); */ | 1929 // for debug: System.out.println("getDatabaseProductVersion() is " + getDatabaseProductVersion() + " preJul2015 is " + preJul2015); |
1930 | 1930 |
1931 final boolean useCommentsTable = ((MonetConnection)con).commentsTableExists(); | 1931 final boolean useCommentsTable = ((MonetConnection)con).commentsTableExists(); |
1932 final StringBuilder query = new StringBuilder(1600); | 1932 final StringBuilder query = new StringBuilder(1600); |
1933 if (preJul2015 && types != null && types.length > 0) { | 1933 if (preJul2015 && types != null && types.length > 0) { |
1934 // we need to filter on the constructed "TABLE_TYPE" expression, this is only possible when we use a subquery in the FROM | 1934 // we need to filter on the constructed "TABLE_TYPE" expression, this is only possible when we use a subquery in the FROM |
2184 "t.\"name\" AS \"TABLE_NAME\", " + | 2184 "t.\"name\" AS \"TABLE_NAME\", " + |
2185 "c.\"name\" AS \"COLUMN_NAME\", " + | 2185 "c.\"name\" AS \"COLUMN_NAME\", " + |
2186 "cast(").append(MonetDriver.getSQLTypeMap("c.\"type\"")).append(" AS int) AS \"DATA_TYPE\", " + | 2186 "cast(").append(MonetDriver.getSQLTypeMap("c.\"type\"")).append(" AS int) AS \"DATA_TYPE\", " + |
2187 "c.\"type\" AS \"TYPE_NAME\", " + | 2187 "c.\"type\" AS \"TYPE_NAME\", " + |
2188 "c.\"type_digits\" AS \"COLUMN_SIZE\", " + | 2188 "c.\"type_digits\" AS \"COLUMN_SIZE\", " + |
2189 "0 AS \"BUFFER_LENGTH\", " + | 2189 "cast(0 as int) AS \"BUFFER_LENGTH\", " + |
2190 "c.\"type_scale\" AS \"DECIMAL_DIGITS\", " + | 2190 "c.\"type_scale\" AS \"DECIMAL_DIGITS\", " + |
2191 "cast(CASE WHEN c.\"type\" IN ('decimal', 'numeric', 'sec_interval') THEN 10 " + | 2191 "cast(CASE WHEN c.\"type\" IN ('decimal', 'numeric', 'sec_interval') THEN 10 " + |
2192 "WHEN c.\"type\" IN ('int', 'smallint', 'tinyint', 'bigint', 'hugeint', 'float', 'real', 'double', 'oid', 'wrd') THEN 2 " + | 2192 "WHEN c.\"type\" IN ('int', 'smallint', 'tinyint', 'bigint', 'hugeint', 'float', 'real', 'double', 'oid', 'wrd') THEN 2 " + |
2193 "ELSE 0 END AS int) AS \"NUM_PREC_RADIX\", " + | 2193 "ELSE 0 END AS int) AS \"NUM_PREC_RADIX\", " + |
2194 "cast(CASE c.\"null\" WHEN true THEN ").append(ResultSetMetaData.columnNullable) | 2194 "cast(CASE c.\"null\" WHEN true THEN ").append(ResultSetMetaData.columnNullable) |
2458 final String table, | 2458 final String table, |
2459 final int scope, | 2459 final int scope, |
2460 final boolean nullable | 2460 final boolean nullable |
2461 ) throws SQLException | 2461 ) throws SQLException |
2462 { | 2462 { |
2463 // first find out if the table has a Primary Key, If it does, we should return only those columns | |
2464 boolean hasPK = false; | |
2465 ResultSet pkey = null; | |
2466 try { | |
2467 pkey = getPrimaryKeys(catalog, schema, table); | |
2468 if (pkey != null && pkey.next()) { | |
2469 hasPK = true; | |
2470 } | |
2471 } catch (SQLException e) { | |
2472 // ignore | |
2473 } finally { | |
2474 MonetConnection.closeResultsetStatement(pkey, null); | |
2475 } | |
2476 | |
2463 final StringBuilder query = new StringBuilder(1500); | 2477 final StringBuilder query = new StringBuilder(1500); |
2464 query.append("SELECT cast(").append(DatabaseMetaData.bestRowSession).append(" AS smallint) AS \"SCOPE\", " + | 2478 // Note: DISTINCT is needed to filter out possible duplicate column names from multiple unique constraints |
2479 query.append("SELECT DISTINCT cast(").append(DatabaseMetaData.bestRowSession).append(" AS smallint) AS \"SCOPE\", " + | |
2465 "c.\"name\" AS \"COLUMN_NAME\", " + | 2480 "c.\"name\" AS \"COLUMN_NAME\", " + |
2466 "cast(").append(MonetDriver.getSQLTypeMap("c.\"type\"")).append(" AS int) AS \"DATA_TYPE\", " + | 2481 "cast(").append(MonetDriver.getSQLTypeMap("c.\"type\"")).append(" AS int) AS \"DATA_TYPE\", " + |
2467 "c.\"type\" AS \"TYPE_NAME\", " + | 2482 "c.\"type\" AS \"TYPE_NAME\", " + |
2468 "c.\"type_digits\" AS \"COLUMN_SIZE\", " + | 2483 "c.\"type_digits\" AS \"COLUMN_SIZE\", " + |
2469 "cast(0 as int) AS \"BUFFER_LENGTH\", " + | 2484 "cast(0 as int) AS \"BUFFER_LENGTH\", " + |
2472 "FROM \"sys\".\"keys\" k " + | 2487 "FROM \"sys\".\"keys\" k " + |
2473 "JOIN \"sys\".\"objects\" o ON k.\"id\" = o.\"id\" " + | 2488 "JOIN \"sys\".\"objects\" o ON k.\"id\" = o.\"id\" " + |
2474 "JOIN \"sys\".\"columns\" c ON (k.\"table_id\" = c.\"table_id\" AND o.\"name\" = c.\"name\") " + | 2489 "JOIN \"sys\".\"columns\" c ON (k.\"table_id\" = c.\"table_id\" AND o.\"name\" = c.\"name\") " + |
2475 "JOIN \"sys\".\"tables\" t ON k.\"table_id\" = t.\"id\" " + | 2490 "JOIN \"sys\".\"tables\" t ON k.\"table_id\" = t.\"id\" " + |
2476 "JOIN \"sys\".\"schemas\" s ON t.\"schema_id\" = s.\"id\" " + | 2491 "JOIN \"sys\".\"schemas\" s ON t.\"schema_id\" = s.\"id\" " + |
2477 "WHERE k.\"type\" IN (0, 1)"); // only primary keys (type = 0) and unique keys (type = 1), not fkeys (type = 2) | 2492 "WHERE k.\"type\" = ").append(hasPK ? "0" : "1"); // the primary key (type = 0) or else any unique key (type = 1) |
2493 // TODO: when there is no PK and there are multiple unique constraints, pick only the unique constraint which has a) the least number of columns and b) the smallest total(size in bytes) | |
2478 | 2494 |
2479 if (catalog != null && !catalog.isEmpty()) { | 2495 if (catalog != null && !catalog.isEmpty()) { |
2480 // non-empty catalog selection. | 2496 // non-empty catalog selection. |
2481 // as we do not support catalogs this always results in no rows returned | 2497 // as we do not support catalogs this always results in no rows returned |
2482 query.append(" AND 1 = 0"); | 2498 query.append(" AND 1 = 0"); |
2483 } else { | 2499 } else { |
2484 if (scope != DatabaseMetaData.bestRowSession | 2500 if (scope == DatabaseMetaData.bestRowSession |
2485 && scope != DatabaseMetaData.bestRowTransaction | 2501 || scope == DatabaseMetaData.bestRowTransaction |
2486 && scope != DatabaseMetaData.bestRowTemporary) { | 2502 || scope == DatabaseMetaData.bestRowTemporary) { |
2487 query.append(" AND 1 = 0"); | |
2488 } else { | |
2489 if (schema != null && !schema.equals("%")) { | 2503 if (schema != null && !schema.equals("%")) { |
2490 query.append(" AND s.\"name\" ").append(composeMatchPart(schema)); | 2504 query.append(" AND s.\"name\" ").append(composeMatchPart(schema)); |
2491 } | 2505 } |
2492 if (table != null && !table.equals("%")) { | 2506 if (table != null && !table.equals("%")) { |
2493 query.append(" AND t.\"name\" ").append(composeMatchPart(table)); | 2507 query.append(" AND t.\"name\" ").append(composeMatchPart(table)); |
2494 } | 2508 } |
2495 if (!nullable) { | 2509 if (!nullable) { |
2496 query.append(" AND c.\"null\" = false"); | 2510 query.append(" AND c.\"null\" = false"); |
2497 } | 2511 } |
2512 } else { | |
2513 query.append(" AND 1 = 0"); | |
2498 } | 2514 } |
2499 } | 2515 } |
2500 | 2516 |
2501 query.append(" ORDER BY k.\"type\", c.\"name\""); | 2517 query.append(" ORDER BY \"SCOPE\", o.\"nr\", \"COLUMN_NAME\""); |
2502 | 2518 |
2503 return executeMetaDataQuery(query.toString()); | 2519 return executeMetaDataQuery(query.toString()); |
2504 } | 2520 } |
2505 | 2521 |
2506 /** | 2522 /** |
3265 "t.\"sqlname\" AS \"TYPE_NAME\", " + | 3281 "t.\"sqlname\" AS \"TYPE_NAME\", " + |
3266 "CASE t.\"sqlname\"" + | 3282 "CASE t.\"sqlname\"" + |
3267 // next 4 UDTs are standard | 3283 // next 4 UDTs are standard |
3268 " WHEN 'inet' THEN 'nl.cwi.monetdb.jdbc.types.INET'" + | 3284 " WHEN 'inet' THEN 'nl.cwi.monetdb.jdbc.types.INET'" + |
3269 " WHEN 'json' THEN 'java.lang.String'" + | 3285 " WHEN 'json' THEN 'java.lang.String'" + |
3270 " WHEN 'url' THEN 'nl.cwi.monetdb.jdbc.types.URL'" + | 3286 " WHEN 'url' THEN 'nl.cwi.monetdb.jdbc.types.URL'" + |
3271 " WHEN 'uuid' THEN 'java.lang.String'" + | 3287 " WHEN 'uuid' THEN 'java.lang.String'" + |
3272 " ELSE 'java.lang.Object' END AS \"CLASS_NAME\", " + | 3288 " ELSE 'java.lang.Object' END AS \"CLASS_NAME\", " + |
3273 "cast(CASE WHEN t.\"sqlname\" IN ('inet', 'json', 'url', 'uuid') THEN ").append(Types.JAVA_OBJECT) | 3289 "cast(CASE WHEN t.\"sqlname\" IN ('inet', 'json', 'url', 'uuid') THEN ").append(Types.JAVA_OBJECT) |
3274 .append(" ELSE ").append(Types.STRUCT).append(" END AS int) AS \"DATA_TYPE\", " + | 3290 .append(" ELSE ").append(Types.STRUCT).append(" END AS int) AS \"DATA_TYPE\", " + |
3275 "t.\"systemname\" AS \"REMARKS\", " + | 3291 "t.\"systemname\" AS \"REMARKS\", " + |