Changeset: 060347aa81ea for monetdb-java URL: https://dev.monetdb.org/hg/monetdb-java/rev/060347aa81ea Modified Files: src/main/java/org/monetdb/jdbc/MonetDatabaseMetaData.java tests/JDBC_API_Tester.java Branch: default Log Message:
By fixing methods getPrimaryKeys(), getBestRowIdentifier() and getIndexInfo() for local temporary tables, it also caused it to fail for global temporary tables in schema tmp. Corrected this, such that it now works for local and global temporary tables. Added tests. diffs (truncated from 351 to 300 lines): diff --git a/src/main/java/org/monetdb/jdbc/MonetDatabaseMetaData.java b/src/main/java/org/monetdb/jdbc/MonetDatabaseMetaData.java --- a/src/main/java/org/monetdb/jdbc/MonetDatabaseMetaData.java +++ b/src/main/java/org/monetdb/jdbc/MonetDatabaseMetaData.java @@ -2409,8 +2409,6 @@ public class MonetDatabaseMetaData final boolean nullable ) throws SQLException { - final String sysORtmp = (schema != null && "tmp".equals(schema)) ? "\"tmp\"" : "\"sys\""; - // first find out if the table has a Primary Key. If it does, we should return only those columns boolean hasPK = false; ResultSet pkey = null; @@ -2423,12 +2421,13 @@ public class MonetDatabaseMetaData // ignore } finally { MonetConnection.closeResultsetStatement(pkey, null); + pkey = null; } // 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) // TODO: when there is No PK and No unique constraints, we potentially should return all columns of the table (else in SQuirreL no header is shown in the "Row IDs" tab) - final StringBuilder query = new StringBuilder(1500); + final StringBuilder query = new StringBuilder(3000); query.append("SELECT "); if (!hasPK) { // Note: currently DISTINCT is needed to filter out possible duplicate column names when there are multiple unique constraints !! @@ -2443,10 +2442,10 @@ public class MonetDatabaseMetaData "cast(0 as int) AS \"BUFFER_LENGTH\", " + "cast(c.\"type_scale\" AS smallint) AS \"DECIMAL_DIGITS\", " + "cast(").append(DatabaseMetaData.bestRowNotPseudo).append(" AS smallint) AS \"PSEUDO_COLUMN\" " + - "FROM ").append(sysORtmp).append(".\"keys\" k " + - "JOIN ").append(sysORtmp).append(".\"objects\" o ON k.\"id\" = o.\"id\" " + - "JOIN ").append(sysORtmp).append(".\"_columns\" c ON (k.\"table_id\" = c.\"table_id\" AND o.\"name\" = c.\"name\") " + - "JOIN ").append(sysORtmp).append(".\"_tables\" t ON k.\"table_id\" = t.\"id\" " + + "FROM \"sys\".\"keys\" k " + + "JOIN \"sys\".\"objects\" o ON k.\"id\" = o.\"id\" " + + "JOIN \"sys\".\"_columns\" c ON (k.\"table_id\" = c.\"table_id\" AND o.\"name\" = c.\"name\") " + + "JOIN \"sys\".\"_tables\" t ON k.\"table_id\" = t.\"id\" " + "JOIN \"sys\".\"schemas\" s ON t.\"schema_id\" = s.\"id\" " + "WHERE k.\"type\" = ").append(hasPK ? "0" : "1"); // the primary key (type = 0) or else any unique key (type = 1) @@ -2474,11 +2473,62 @@ public class MonetDatabaseMetaData } } + final boolean includetmp = (schema == null) + || (schema != null && ("tmp".equals(schema) || schema.contains("%") || schema.contains("_"))); + if (includetmp) { + // we must also include the primary key or unique constraint of local temporary tables which are stored in tmp.keys, tmp.objects, tmp._tables and tmp._columns + query.append(" UNION ALL "); + query.append("SELECT "); + if (!hasPK) { + // Note: currently DISTINCT is needed to filter out possible duplicate column names when there are multiple unique constraints !! + // TODO: when no PK and there are multiple unique constraints determine which one to select such that DISTINCT keyword is not needed anymore + query.append("DISTINCT "); + } + query.append("cast(").append(DatabaseMetaData.bestRowSession).append(" AS smallint) AS \"SCOPE\", " + + "c.\"name\" AS \"COLUMN_NAME\", " + + "cast(").append(MonetDriver.getSQLTypeMap("c.\"type\"")).append(" AS int) AS \"DATA_TYPE\", " + + "c.\"type\" AS \"TYPE_NAME\", " + + "c.\"type_digits\" AS \"COLUMN_SIZE\", " + + "cast(0 as int) AS \"BUFFER_LENGTH\", " + + "cast(c.\"type_scale\" AS smallint) AS \"DECIMAL_DIGITS\", " + + "cast(").append(DatabaseMetaData.bestRowNotPseudo).append(" AS smallint) AS \"PSEUDO_COLUMN\" " + + "FROM \"tmp\".\"keys\" k " + + "JOIN \"tmp\".\"objects\" o ON k.\"id\" = o.\"id\" " + + "JOIN \"tmp\".\"_columns\" c ON (k.\"table_id\" = c.\"table_id\" AND o.\"name\" = c.\"name\") " + + "JOIN \"tmp\".\"_tables\" t ON k.\"table_id\" = t.\"id\" " + + "JOIN \"sys\".\"schemas\" s ON t.\"schema_id\" = s.\"id\" " + + "WHERE k.\"type\" = ").append(hasPK ? "0" : "1"); // the primary key (type = 0) or else any unique key (type = 1) + + if (catalog != null && !catalog.isEmpty()) { + // non-empty catalog selection. + // as we do not support catalogs this always results in no rows returned + query.append(" AND 1=0"); + } else { + if (scope == DatabaseMetaData.bestRowSession + || scope == DatabaseMetaData.bestRowTransaction + || scope == DatabaseMetaData.bestRowTemporary) { + if (schema != null) { + // do not allow wildcard matching with LIKE, as the resultset does not include the schema info + query.append(" AND s.\"name\" = ").append(MonetWrapper.sq(schema)); + } + if (table != null) { + // do not allow wildcard matching with LIKE, as the resultset does not include the table info + query.append(" AND t.\"name\" = ").append(MonetWrapper.sq(table)); + } + if (!nullable) { + query.append(" AND c.\"null\" = false"); + } + } else { + query.append(" AND 1=0"); + } + } + } + // was: query.append(" ORDER BY \"SCOPE\", o.\"nr\", \"COLUMN_NAME\""); // But as of Jan2022 this ordering returns error: SELECT: with DISTINCT ORDER BY expressions must appear in select list // so had to remove the o.\"nr\", part when there is No PKey. This means the columns are than ordered on names instead of creation order in their unique constraint definition query.append(" ORDER BY \"SCOPE\", "); - if (hasPK) + if (hasPK && !includetmp) query.append("o.\"nr\", "); query.append("\"COLUMN_NAME\""); @@ -2563,17 +2613,16 @@ public class MonetDatabaseMetaData final String table ) throws SQLException { - final String sysORtmp = (schema != null && "tmp".equals(schema)) ? "\"tmp\"" : "\"sys\""; - final StringBuilder query = new StringBuilder(600); + final StringBuilder query = new StringBuilder(1200); query.append("SELECT cast(null AS char(1)) AS \"TABLE_CAT\", " + "s.\"name\" AS \"TABLE_SCHEM\", " + "t.\"name\" AS \"TABLE_NAME\", " + "o.\"name\" AS \"COLUMN_NAME\", " + "cast(1 + o.\"nr\" AS smallint) AS \"KEY_SEQ\", " + - " k.\"name\" AS \"PK_NAME\" " + - "FROM ").append(sysORtmp).append(".\"keys\" k " + - "JOIN ").append(sysORtmp).append(".\"objects\" o ON k.\"id\" = o.\"id\" " + - "JOIN ").append(sysORtmp).append(".\"_tables\" t ON k.\"table_id\" = t.\"id\" " + + "k.\"name\" AS \"PK_NAME\" " + + "FROM \"sys\".\"keys\" k " + + "JOIN \"sys\".\"objects\" o ON k.\"id\" = o.\"id\" " + + "JOIN \"sys\".\"_tables\" t ON k.\"table_id\" = t.\"id\" " + "JOIN \"sys\".\"schemas\" s ON t.\"schema_id\" = s.\"id\" " + "WHERE k.\"type\" = 0"); // only primary keys (type = 0) @@ -2590,6 +2639,37 @@ public class MonetDatabaseMetaData } } + final boolean includetmp = (schema == null) + || (schema != null && ("tmp".equals(schema) || schema.contains("%") || schema.contains("_"))); + if (includetmp) { + // we must also include the keys of local temporary tables which are stored in tmp.keys, tmp.objects and tmp._tables + query.append(" UNION ALL "); + query.append("SELECT cast(null AS char(1)) AS \"TABLE_CAT\", " + + "s.\"name\" AS \"TABLE_SCHEM\", " + + "t.\"name\" AS \"TABLE_NAME\", " + + "o.\"name\" AS \"COLUMN_NAME\", " + + "cast(1 + o.\"nr\" AS smallint) AS \"KEY_SEQ\", " + + "k.\"name\" AS \"PK_NAME\" " + + "FROM \"tmp\".\"keys\" k " + + "JOIN \"tmp\".\"objects\" o ON k.\"id\" = o.\"id\" " + + "JOIN \"tmp\".\"_tables\" t ON k.\"table_id\" = t.\"id\" " + + "JOIN \"sys\".\"schemas\" s ON t.\"schema_id\" = s.\"id\" " + + "WHERE k.\"type\" = 0"); // only primary keys (type = 0) + + if (catalog != null && !catalog.isEmpty()) { + // non-empty catalog selection. + // as we do not support catalogs this always results in no rows returned + query.append(" AND 1=0"); + } else { + if (schema != null && !schema.equals("%")) { + query.append(" AND s.\"name\" ").append(composeMatchPart(schema)); + } + if (table != null && !table.equals("%")) { + query.append(" AND t.\"name\" ").append(composeMatchPart(table)); + } + } + } + query.append(" ORDER BY \"TABLE_SCHEM\", \"TABLE_NAME\", \"COLUMN_NAME\""); return executeMetaDataQuery(query.toString()); @@ -3081,8 +3161,7 @@ public class MonetDatabaseMetaData } } - final String sysORtmp = (schema != null && "tmp".equals(schema)) ? "\"tmp\"" : "\"sys\""; - final StringBuilder query = new StringBuilder(1250); + final StringBuilder query = new StringBuilder(2500); query.append( "SELECT cast(null AS char(1)) AS \"TABLE_CAT\", " + "s.\"name\" AS \"TABLE_SCHEM\", " + @@ -3097,12 +3176,12 @@ public class MonetDatabaseMetaData "cast(").append(table_row_count).append(" AS int) AS \"CARDINALITY\", " + "cast(0 AS int) AS \"PAGES\", " + "cast(null AS char(1)) AS \"FILTER_CONDITION\" " + - "FROM ").append(sysORtmp).append(".\"idxs\" i " + - "JOIN ").append(sysORtmp).append(".\"_tables\" t ON i.\"table_id\" = t.\"id\" " + + "FROM \"sys\".\"idxs\" i " + + "JOIN \"sys\".\"_tables\" t ON i.\"table_id\" = t.\"id\" " + "JOIN \"sys\".\"schemas\" s ON t.\"schema_id\" = s.\"id\" " + - "JOIN ").append(sysORtmp).append(".\"objects\" o ON i.\"id\" = o.\"id\" " + - "JOIN ").append(sysORtmp).append(".\"_columns\" c ON (t.\"id\" = c.\"table_id\" AND o.\"name\" = c.\"name\") " + - "LEFT OUTER JOIN ").append(sysORtmp).append(".\"keys\" k ON (i.\"name\" = k.\"name\" AND i.\"table_id\" = k.\"table_id\" AND k.\"type\" IN (0,1)) "); // primary (0) and unique keys (1) only + "JOIN \"sys\".\"objects\" o ON i.\"id\" = o.\"id\" " + + "JOIN \"sys\".\"_columns\" c ON (t.\"id\" = c.\"table_id\" AND o.\"name\" = c.\"name\") " + + "LEFT OUTER JOIN \"sys\".\"keys\" k ON (i.\"name\" = k.\"name\" AND i.\"table_id\" = k.\"table_id\" AND k.\"type\" IN (0,1)) "); // primary (0) and unique keys (1) only if (catalog != null && !catalog.isEmpty()) { // non-empty catalog selection. @@ -3125,6 +3204,54 @@ public class MonetDatabaseMetaData } } + final boolean includetmp = (schema == null) + || (schema != null && ("tmp".equals(schema) || schema.contains("%") || schema.contains("_"))); + if (includetmp) { + // we must also include the indexes of local temporary tables which are stored in tmp.idxs, tmp._tables, tmp._columns, tmp.objects and tmp.keys + query.append(" UNION ALL "); + query.append( + "SELECT cast(null AS char(1)) AS \"TABLE_CAT\", " + + "s.\"name\" AS \"TABLE_SCHEM\", " + + "t.\"name\" AS \"TABLE_NAME\", " + + "CASE WHEN k.\"name\" IS NULL THEN true ELSE false END AS \"NON_UNIQUE\", " + + "cast(null AS char(1)) AS \"INDEX_QUALIFIER\", " + + "i.\"name\" AS \"INDEX_NAME\", " + + "CASE i.\"type\" WHEN 0 THEN ").append(DatabaseMetaData.tableIndexHashed).append(" ELSE ").append(DatabaseMetaData.tableIndexOther).append(" END AS \"TYPE\", " + + "cast(o.\"nr\" +1 AS smallint) AS \"ORDINAL_POSITION\", "+ + "c.\"name\" AS \"COLUMN_NAME\", " + + "cast(null AS char(1)) AS \"ASC_OR_DESC\", " + // sort sequence currently not supported in keys or indexes in MonetDB + "cast(").append(table_row_count).append(" AS int) AS \"CARDINALITY\", " + + "cast(0 AS int) AS \"PAGES\", " + + "cast(null AS char(1)) AS \"FILTER_CONDITION\" " + + "FROM \"tmp\".\"idxs\" i " + + "JOIN \"tmp\".\"_tables\" t ON i.\"table_id\" = t.\"id\" " + + "JOIN \"sys\".\"schemas\" s ON t.\"schema_id\" = s.\"id\" " + + "JOIN \"tmp\".\"objects\" o ON i.\"id\" = o.\"id\" " + + "JOIN \"tmp\".\"_columns\" c ON (t.\"id\" = c.\"table_id\" AND o.\"name\" = c.\"name\") " + + "LEFT OUTER JOIN \"tmp\".\"keys\" k ON (i.\"name\" = k.\"name\" AND i.\"table_id\" = k.\"table_id\" AND k.\"type\" IN (0,1)) "); // primary (0) and unique keys (1) only + + if (catalog != null && !catalog.isEmpty()) { + // non-empty catalog selection. + // as we do not support catalogs this always results in no rows returned + query.append("WHERE 1=0"); + } else { + boolean needWhere = true; + if (schema != null && !schema.equals("%")) { + query.append("WHERE s.\"name\" ").append(composeMatchPart(schema)); + needWhere = false; + } + if (table != null && !table.equals("%")) { + query.append(needWhere ? "WHERE" : " AND") + .append(" t.\"name\" ").append(composeMatchPart(table)); + needWhere = false; + } + if (unique) { + query.append(needWhere ? "WHERE" : " AND") + .append(" k.\"name\" IS NOT NULL"); + } + } + } + query.append(" ORDER BY \"NON_UNIQUE\", \"TYPE\", \"INDEX_NAME\", \"ORDINAL_POSITION\""); return executeMetaDataQuery(query.toString()); diff --git a/tests/JDBC_API_Tester.java b/tests/JDBC_API_Tester.java --- a/tests/JDBC_API_Tester.java +++ b/tests/JDBC_API_Tester.java @@ -698,6 +698,16 @@ final public class JDBC_API_Tester { response = stmt.executeUpdate("CREATE LOCAL TEMP TABLE tmp_pk_uc (id1 INT NOT NULL PRIMARY KEY, name1 VARCHAR(99) UNIQUE)"); if (response != Statement.SUCCESS_NO_INFO) sb.append("Creating table ").append(tablename).append(" failed to return -2!! It returned: ").append(response).append("\n"); + + tablename = "glbl_nopk_twoucs"; + response = stmt.executeUpdate("CREATE GLOBAL TEMP TABLE glbl_nopk_twoucs (id2 INT NOT NULL UNIQUE, name2 VARCHAR(99) UNIQUE)"); + if (response != Statement.SUCCESS_NO_INFO) + sb.append("Creating table ").append(tablename).append(" failed to return -2!! It returned: ").append(response).append("\n"); + + tablename = "glbl_pk_uc"; + response = stmt.executeUpdate("CREATE GLOBAL TEMP TABLE glbl_pk_uc (id1 INT NOT NULL PRIMARY KEY, name1 VARCHAR(99) UNIQUE)"); + if (response != Statement.SUCCESS_NO_INFO) + sb.append("Creating table ").append(tablename).append(" failed to return -2!! It returned: ").append(response).append("\n"); } catch (SQLException e) { sb.append("failed to create test table ").append(tablename).append(": ").append(e.getMessage()).append("\n"); } @@ -723,9 +733,11 @@ final public class JDBC_API_Tester { "TABLE_SCHEM TABLE_CATALOG\n" + "sys null\n"); - compareResultSet(dbmd.getTables(null, "tmp", null, null), "getTables(null, tmp, null, null)", // schema tmp has 6 tables + compareResultSet(dbmd.getTables(null, "tmp", null, null), "getTables(null, tmp, null, null)", // schema tmp has 6 system tables and 4 temporary test tables "Resultset with 10 columns\n" + "TABLE_CAT TABLE_SCHEM TABLE_NAME TABLE_TYPE REMARKS TYPE_CAT TYPE_SCHEM TYPE_NAME SELF_REFERENCING_COL_NAME REF_GENERATION\n" + + "null tmp glbl_nopk_twoucs GLOBAL TEMPORARY TABLE null null null null null null\n" + + "null tmp glbl_pk_uc GLOBAL TEMPORARY TABLE null null null null null null\n" + "null tmp tmp_nopk_twoucs LOCAL TEMPORARY TABLE null null null null null null\n" + "null tmp tmp_pk_uc LOCAL TEMPORARY TABLE null null null null null null\n" + "null tmp _columns SYSTEM TABLE null null null null null null\n" + @@ -756,6 +768,11 @@ final public class JDBC_API_Tester { "TABLE_CAT TABLE_SCHEM TABLE_NAME COLUMN_NAME KEY_SEQ PK_NAME\n" + "null tmp tmp_pk_uc id1 1 tmp_pk_uc_id1_pkey\n"); + compareResultSet(dbmd.getPrimaryKeys(null, "tmp", "glbl_pk_uc"), "getPrimaryKeys(null, tmp, glbl_pk_uc)", + "Resultset with 6 columns\n" + + "TABLE_CAT TABLE_SCHEM TABLE_NAME COLUMN_NAME KEY_SEQ PK_NAME\n" + + "null tmp glbl_pk_uc id1 1 glbl_pk_uc_id1_pkey\n"); + compareResultSet(dbmd.getExportedKeys(null, "sys", "table\\_types"), "getExportedKeys(null, sys, table\\_types)", "Resultset with 14 columns\n" + "PKTABLE_CAT PKTABLE_SCHEM PKTABLE_NAME PKCOLUMN_NAME FKTABLE_CAT FKTABLE_SCHEM FKTABLE_NAME FKCOLUMN_NAME KEY_SEQ UPDATE_RULE DELETE_RULE FK_NAME PK_NAME DEFERRABILITY\n"); @@ -780,6 +797,12 @@ final public class JDBC_API_Tester { "null tmp tmp_pk_uc false null tmp_pk_uc_id1_pkey 2 1 id1 null 0 0 null\n" + "null tmp tmp_pk_uc false null tmp_pk_uc_name1_unique 2 1 name1 null 0 0 null\n"); + compareResultSet(dbmd.getIndexInfo(null, "tmp", "glbl_pk_uc", false, false), "getIndexInfo(null, tmp, glbl_pk_uc, false, false)", _______________________________________________ checkin-list mailing list -- checkin-list@monetdb.org To unsubscribe send an email to checkin-list-le...@monetdb.org