From e7fe0a97452e1e6f11d751833a2223e1f922531e Mon Sep 17 00:00:00 2001
From: Georgii Gvinepadze
Date: Mon, 16 Jan 2023 15:18:22 +0400
Subject: [PATCH 01/10] feat: Add an ability to read metadata for attached
databases
---
src/main/java/org/sqlite/SQLiteConfig.java | 33 +-
.../org/sqlite/core/CoreDatabaseMetaData.java | 29 ++
.../sqlite/jdbc3/JDBC3DatabaseMetaData.java | 119 ++++---
src/test/java/org/sqlite/DBMetaDataTest.java | 329 +++++++++++++++++-
4 files changed, 449 insertions(+), 61 deletions(-)
diff --git a/src/main/java/org/sqlite/SQLiteConfig.java b/src/main/java/org/sqlite/SQLiteConfig.java
index 56b8bc26f..04b9c66dd 100755
--- a/src/main/java/org/sqlite/SQLiteConfig.java
+++ b/src/main/java/org/sqlite/SQLiteConfig.java
@@ -56,6 +56,7 @@ public class SQLiteConfig {
private final int busyTimeout;
private boolean explicitReadOnly;
+ private boolean readAttachedDatabases;
private final SQLiteConnectionConfig defaultConnectionConfig;
@@ -93,6 +94,10 @@ public SQLiteConfig(Properties prop) {
this.explicitReadOnly =
Boolean.parseBoolean(
pragmaTable.getProperty(Pragma.JDBC_EXPLICIT_READONLY.pragmaName, "false"));
+ this.readAttachedDatabases =
+ Boolean.parseBoolean(
+ pragmaTable.getProperty(
+ Pragma.METADATA_READ_ATTACHED_DATABASES.pragmaName, "false"));
}
public SQLiteConnectionConfig newConnectionConfig() {
@@ -186,8 +191,9 @@ public void apply(Connection conn) throws SQLException {
pragmaParams.remove(Pragma.LIMIT_WORKER_THREADS.pragmaName);
pragmaParams.remove(Pragma.LIMIT_PAGE_COUNT.pragmaName);
- // exclude this "fake" pragma from execution
+ // exclude these "fake" pragmas from execution
pragmaParams.remove(Pragma.JDBC_EXPLICIT_READONLY.pragmaName);
+ pragmaParams.remove(Pragma.METADATA_READ_ATTACHED_DATABASES.pragmaName);
Statement stat = conn.createStatement();
try {
@@ -330,6 +336,10 @@ public Properties toProperties() {
defaultConnectionConfig.getDateStringFormat());
pragmaTable.setProperty(
Pragma.JDBC_EXPLICIT_READONLY.pragmaName, this.explicitReadOnly ? "true" : "false");
+ pragmaTable.setProperty(
+ Pragma.METADATA_READ_ATTACHED_DATABASES.pragmaName,
+ this.readAttachedDatabases ? "true" : "false"
+ );
return pragmaTable;
}
@@ -373,6 +383,20 @@ public void setExplicitReadOnly(boolean readOnly) {
this.explicitReadOnly = readOnly;
}
+ /** @return true if reading attached databases is allowed */
+ public boolean isReadAttachedDatabases() {
+ return readAttachedDatabases;
+ }
+
+ /**
+ * Enable reading attached databases in metadata, they will be shown as schemas
+ *
+ * @param readAttachedDatabases whether to read attached databases
+ */
+ public void setReadAttachedDatabases(boolean readAttachedDatabases) {
+ this.readAttachedDatabases = readAttachedDatabases;
+ }
+
public enum Pragma {
// Parameters requiring SQLite3 API invocation
@@ -534,8 +558,11 @@ public enum Pragma {
// extensions: "fake" pragmas to allow conformance with JDBC
JDBC_EXPLICIT_READONLY(
- "jdbc.explicit_readonly", "Set explicit read only transactions", null);
-
+ "jdbc.explicit_readonly", "Set explicit read only transactions", null),
+ // "fake" pragma to allow configurating metadata reading by driver
+ METADATA_READ_ATTACHED_DATABASES("metadata.read_attached_databases",
+ "Set read attached databases as schemas",
+ OnOff);
public final String pragmaName;
public final String[] choices;
public final String description;
diff --git a/src/main/java/org/sqlite/core/CoreDatabaseMetaData.java b/src/main/java/org/sqlite/core/CoreDatabaseMetaData.java
index 1c6a4fdf6..fdb8eabef 100644
--- a/src/main/java/org/sqlite/core/CoreDatabaseMetaData.java
+++ b/src/main/java/org/sqlite/core/CoreDatabaseMetaData.java
@@ -184,6 +184,35 @@ protected String escape(final String val) {
return buf.toString();
}
+ /**
+ * Returns line without changes or with escaped schema prefix
+ * @param schema schema name
+ * @param line of text to prepend to
+ * @return The SQL escaped schema name with dot or empty string
+ */
+ protected String prependSchemaPrefix(String schema, String line) {
+ if (schema == null) {
+ return line;
+ } else {
+ return escape(schema) + "." + line;
+ }
+ }
+
+ /**
+ * Adds line without changes or with escaped schema prefix
+ * @param sql String builder for sql request
+ * @param schema schema name
+ * @param line line to prepend schema prefix to
+ */
+ protected void prependSchemaPrefix(StringBuilder sql, String schema, String line) {
+ if (schema == null) {
+ sql.append(line);
+ } else {
+ sql.append(schema).append('.').append(line);
+ }
+ }
+
+
// inner classes
/** Pattern used to extract column order for an unnamed primary key. */
diff --git a/src/main/java/org/sqlite/jdbc3/JDBC3DatabaseMetaData.java b/src/main/java/org/sqlite/jdbc3/JDBC3DatabaseMetaData.java
index 4416d239b..2a8c0c358 100644
--- a/src/main/java/org/sqlite/jdbc3/JDBC3DatabaseMetaData.java
+++ b/src/main/java/org/sqlite/jdbc3/JDBC3DatabaseMetaData.java
@@ -681,7 +681,7 @@ public boolean supportsSavepoints() {
/** @see java.sql.DatabaseMetaData#supportsSchemasInDataManipulation() */
public boolean supportsSchemasInDataManipulation() {
- return false;
+ return conn.getDatabase().getConfig().isReadAttachedDatabases();
}
/** @see java.sql.DatabaseMetaData#supportsSchemasInIndexDefinitions() */
@@ -915,7 +915,7 @@ public ResultSet getColumns(String c, String s, String tblNamePattern, String co
checkOpen();
StringBuilder sql = new StringBuilder(700);
- sql.append("select null as TABLE_CAT, null as TABLE_SCHEM, tblname as TABLE_NAME, ")
+ sql.append("select null as TABLE_CAT, ").append(quote(s)).append(" as TABLE_SCHEM, tblname as TABLE_NAME, ")
.append(
"cn as COLUMN_NAME, ct as DATA_TYPE, tn as TYPE_NAME, colSize as COLUMN_SIZE, ")
.append(
@@ -948,12 +948,10 @@ public ResultSet getColumns(String c, String s, String tblNamePattern, String co
ResultSet rsColAutoinc = null;
try {
statColAutoinc = conn.createStatement();
- rsColAutoinc =
- statColAutoinc.executeQuery(
- "SELECT LIKE('%autoincrement%', LOWER(sql)) FROM sqlite_master "
- + "WHERE LOWER(name) = LOWER('"
- + escape(tableName)
- + "') AND TYPE IN ('table', 'view')");
+ rsColAutoinc = statColAutoinc.executeQuery(
+ "SELECT LIKE('%autoincrement%', LOWER(sql)) FROM " + prependSchemaPrefix(s,
+ "sqlite_master WHERE LOWER(name) = LOWER('") + escape(tableName)
+ + "') AND TYPE IN ('table', 'view')");
rsColAutoinc.next();
isAutoIncrement = rsColAutoinc.getInt(1) == 1;
} finally {
@@ -974,7 +972,8 @@ public ResultSet getColumns(String c, String s, String tblNamePattern, String co
}
// For each table, get the column info and build into overall SQL
- String pragmaStatement = "PRAGMA table_xinfo('" + escape(tableName) + "')";
+ String pragmaStatement = "PRAGMA " + prependSchemaPrefix(s, "table_xinfo('" + escape(tableName) +
+ "')");
try (Statement colstat = conn.createStatement();
ResultSet rscol = colstat.executeQuery(pragmaStatement)) {
@@ -1173,9 +1172,12 @@ public ResultSet getCrossReference(
/** @see java.sql.DatabaseMetaData#getSchemas() */
public ResultSet getSchemas() throws SQLException {
if (getSchemas == null) {
- getSchemas =
- conn.prepareStatement(
- "select null as TABLE_SCHEM, null as TABLE_CATALOG limit 0;");
+ if (conn.getDatabase().getConfig().isReadAttachedDatabases()) {
+ getSchemas = conn.prepareStatement(
+ "select name as TABLE_SCHEM, null as TABLE_CATALOG from pragma_database_list;");
+ } else {
+ getSchemas = conn.prepareStatement("select null as TABLE_SCHEM, null as TABLE_CATALOG limit 0;");
+ }
}
return getSchemas.executeQuery();
@@ -1195,12 +1197,12 @@ public ResultSet getCatalogs() throws SQLException {
* java.lang.String)
*/
public ResultSet getPrimaryKeys(String c, String s, String table) throws SQLException {
- PrimaryKeyFinder pkFinder = new PrimaryKeyFinder(table);
+ PrimaryKeyFinder pkFinder = new PrimaryKeyFinder(table, s);
String[] columns = pkFinder.getColumns();
Statement stat = conn.createStatement();
StringBuilder sql = new StringBuilder(512);
- sql.append("select null as TABLE_CAT, null as TABLE_SCHEM, '")
+ sql.append("select null as TABLE_CAT, ").append(quote(s)).append(" as TABLE_SCHEM, '")
.append(escape(table))
.append("' as TABLE_NAME, cn as COLUMN_NAME, ks as KEY_SEQ, pk as PK_NAME from (");
@@ -1245,12 +1247,13 @@ public ResultSet getPrimaryKeys(String c, String s, String table) throws SQLExce
*/
public ResultSet getExportedKeys(String catalog, String schema, String table)
throws SQLException {
- PrimaryKeyFinder pkFinder = new PrimaryKeyFinder(table);
+ PrimaryKeyFinder pkFinder = new PrimaryKeyFinder(table, schema);
String[] pkColumns = pkFinder.getColumns();
Statement stat = conn.createStatement();
catalog = (catalog != null) ? quote(catalog) : null;
- schema = (schema != null) ? quote(schema) : null;
+
+ String quotedSchema = (schema != null) ? quote(schema) : null;
StringBuilder exportedKeysQuery = new StringBuilder(512);
@@ -1259,8 +1262,11 @@ public ResultSet getExportedKeys(String catalog, String schema, String table)
if (pkColumns != null) {
// retrieve table list
ArrayList tableList;
- try (ResultSet rs =
- stat.executeQuery("select name from sqlite_master where type = 'table'")) {
+ try (
+ ResultSet rs = stat.executeQuery(
+ "select name from " + prependSchemaPrefix(schema, "sqlite_master where type = " +
+ "'table'"))
+ ) {
tableList = new ArrayList<>();
while (rs.next()) {
@@ -1276,7 +1282,7 @@ public ResultSet getExportedKeys(String catalog, String schema, String table)
// find imported keys for each table
for (String tbl : tableList) {
- final ImportedKeyFinder impFkFinder = new ImportedKeyFinder(tbl);
+ final ImportedKeyFinder impFkFinder = new ImportedKeyFinder(tbl, schema);
List fkNames = impFkFinder.getFkList();
for (ForeignKey foreignKey : fkNames) {
@@ -1340,7 +1346,7 @@ public ResultSet getExportedKeys(String catalog, String schema, String table)
sql.append("select ")
.append(catalog)
.append(" as PKTABLE_CAT, ")
- .append(schema)
+ .append(quotedSchema)
.append(" as PKTABLE_SCHEM, ")
.append(quote(target))
.append(" as PKTABLE_NAME, ")
@@ -1348,7 +1354,7 @@ public ResultSet getExportedKeys(String catalog, String schema, String table)
.append(" as PKCOLUMN_NAME, ")
.append(catalog)
.append(" as FKTABLE_CAT, ")
- .append(schema)
+ .append(quotedSchema)
.append(" as FKTABLE_SCHEM, ")
.append(hasImportedKey ? "fkt" : "''")
.append(" as FKTABLE_NAME, ")
@@ -1426,7 +1432,7 @@ public ResultSet getImportedKeys(String catalog, String schema, String table)
return ((CoreStatement) stat).executeQuery(sql.toString(), true);
}
- final ImportedKeyFinder impFkFinder = new ImportedKeyFinder(table);
+ final ImportedKeyFinder impFkFinder = new ImportedKeyFinder(table, schema);
List fkNames = impFkFinder.getFkList();
int i = 0;
@@ -1439,7 +1445,7 @@ public ResultSet getImportedKeys(String catalog, String schema, String table)
String pkName = null;
try {
- PrimaryKeyFinder pkFinder = new PrimaryKeyFinder(PKTabName);
+ PrimaryKeyFinder pkFinder = new PrimaryKeyFinder(PKTabName, schema);
pkName = pkFinder.getName();
if (PKColName == null) {
PKColName = pkFinder.getColumns()[0];
@@ -1523,7 +1529,9 @@ public ResultSet getIndexInfo(String c, String s, String table, boolean u, boole
// define the column header
// this is from the JDBC spec, it is part of the driver protocol
- sql.append("select null as TABLE_CAT, null as TABLE_SCHEM, '")
+ sql.append("select null as TABLE_CAT,")
+ .append(quote(s))
+ .append(" as TABLE_SCHEM, '")
.append(escape(table))
.append(
"' as TABLE_NAME, un as NON_UNIQUE, null as INDEX_QUALIFIER, n as INDEX_NAME, ")
@@ -1533,7 +1541,7 @@ public ResultSet getIndexInfo(String c, String s, String table, boolean u, boole
"cn as COLUMN_NAME, null as ASC_OR_DESC, 0 as CARDINALITY, 0 as PAGES, null as FILTER_CONDITION from (");
// this always returns a result set now, previously threw exception
- rs = stat.executeQuery("pragma index_list('" + escape(table) + "');");
+ rs = stat.executeQuery("pragma " + prependSchemaPrefix(s, "index_list('" + escape(table) + "');"));
ArrayList> indexList = new ArrayList<>();
while (rs.next()) {
@@ -1557,7 +1565,7 @@ public ResultSet getIndexInfo(String c, String s, String table, boolean u, boole
while (indexIterator.hasNext()) {
currentIndex = indexIterator.next();
String indexName = currentIndex.get(0).toString();
- rs = stat.executeQuery("pragma index_info('" + escape(indexName) + "');");
+ rs = stat.executeQuery("pragma " + prependSchemaPrefix(s, "index_info('" + escape(indexName) + "');"));
while (rs.next()) {
@@ -1704,7 +1712,8 @@ public synchronized ResultSet getTables(
sql.append(" NAME,").append("\n");
sql.append(" UPPER(TYPE) AS TYPE").append("\n");
sql.append(" FROM").append("\n");
- sql.append(" sqlite_master").append("\n");
+ sql.append(" ");
+ prependSchemaPrefix(sql, s, "sqlite_master\n");
sql.append(" WHERE").append("\n");
sql.append(" NAME NOT LIKE 'sqlite\\_%' ESCAPE '\\'").append("\n");
sql.append(" AND UPPER(TYPE) IN ('TABLE', 'VIEW')").append("\n");
@@ -1719,7 +1728,8 @@ public synchronized ResultSet getTables(
sql.append(" NAME,").append("\n");
sql.append(" 'SYSTEM TABLE' AS TYPE").append("\n");
sql.append(" FROM").append("\n");
- sql.append(" sqlite_master").append("\n");
+ sql.append(" ");
+ prependSchemaPrefix(sql, s, "sqlite_master\n");
sql.append(" WHERE").append("\n");
sql.append(" NAME LIKE 'sqlite\\_%' ESCAPE '\\'").append("\n");
sql.append(" )").append("\n");
@@ -1962,6 +1972,7 @@ public ResultSet getFunctionColumns(String a, String b, String c, String d)
/** Parses the sqlite_master table for a table's primary key */
class PrimaryKeyFinder {
+ String schema;
/** The table name. */
String table;
@@ -1971,15 +1982,20 @@ class PrimaryKeyFinder {
/** The column(s) for the primary key. */
String[] pkColumns = null;
+ public PrimaryKeyFinder(String table) throws SQLException {
+ this(table, null);
+ }
+
/**
* Constructor.
*
- * @param table The table for which to get find a primary key.
+ * @param table The table for which to get find a primary key.
+ * @param schema Schema in which table is located
* @throws SQLException
*/
- public PrimaryKeyFinder(String table) throws SQLException {
+ public PrimaryKeyFinder(String table, String schema) throws SQLException {
this.table = table;
-
+ this.schema = schema;
if (table == null || table.trim().length() == 0) {
throw new SQLException("Invalid table name: '" + this.table + "'");
}
@@ -1988,10 +2004,10 @@ public PrimaryKeyFinder(String table) throws SQLException {
// read create SQL script for table
ResultSet rs =
stat.executeQuery(
- "select sql from sqlite_master where"
+ "select sql from " + prependSchemaPrefix(schema, "sqlite_master where"
+ " lower(name) = lower('"
+ escape(table)
- + "') and type in ('table', 'view')")) {
+ + "') and type in ('table', 'view')"))) {
if (!rs.next()) throw new SQLException("Table not found: '" + table + "'");
@@ -2007,8 +2023,10 @@ public PrimaryKeyFinder(String table) throws SQLException {
}
if (pkColumns == null) {
- try (ResultSet rs2 =
- stat.executeQuery("pragma table_info('" + escape(table) + "');")) {
+ try (
+ ResultSet rs2 = stat.executeQuery(
+ "pragma " + prependSchemaPrefix(schema, "table_info('" + escape(table) + "');"))
+ ) {
while (rs2.next()) {
if (rs2.getBoolean(6)) pkColumns = new String[] {rs2.getString(2)};
}
@@ -2046,6 +2064,10 @@ class ImportedKeyFinder {
private final List fkList = new ArrayList<>();
public ImportedKeyFinder(String table) throws SQLException {
+ this(table, null);
+ }
+
+ public ImportedKeyFinder(String table, String schema) throws SQLException {
if (table == null || table.trim().length() == 0) {
throw new SQLException("Invalid table name: '" + table + "'");
@@ -2053,14 +2075,15 @@ public ImportedKeyFinder(String table) throws SQLException {
this.fkTableName = table;
- List fkNames = getForeignKeyNames(this.fkTableName);
+ List fkNames = getForeignKeyNames(this.fkTableName, schema);
- try (Statement stat = conn.createStatement();
- ResultSet rs =
- stat.executeQuery(
- "pragma foreign_key_list('"
- + escape(this.fkTableName.toLowerCase())
- + "')")) {
+ try (
+ Statement stat = conn.createStatement();
+ ResultSet rs = stat.executeQuery("pragma " + prependSchemaPrefix(
+ schema,
+ "foreign_key_list('" + escape(this.fkTableName.toLowerCase()) + "')"
+ ))
+ ) {
int prevFkId = -1;
int count = 0;
@@ -2097,19 +2120,15 @@ public ImportedKeyFinder(String table) throws SQLException {
}
}
- private List getForeignKeyNames(String tbl) throws SQLException {
+ private List getForeignKeyNames(String tbl, String schema) throws SQLException {
List fkNames = new ArrayList<>();
if (tbl == null) {
return fkNames;
}
try (Statement stat2 = conn.createStatement();
- ResultSet rs =
- stat2.executeQuery(
- "select sql from sqlite_master where"
- + " lower(name) = lower('"
- + escape(tbl)
- + "')")) {
-
+ ResultSet rs = stat2.executeQuery("select sql from " + prependSchemaPrefix(schema,
+ "sqlite_master where" + " lower(name) = lower('" + escape(tbl) + "')"
+ ))) {
if (rs.next()) {
Matcher matcher = FK_NAMED_PATTERN.matcher(rs.getString(1));
diff --git a/src/test/java/org/sqlite/DBMetaDataTest.java b/src/test/java/org/sqlite/DBMetaDataTest.java
index dae70ba41..560511a96 100644
--- a/src/test/java/org/sqlite/DBMetaDataTest.java
+++ b/src/test/java/org/sqlite/DBMetaDataTest.java
@@ -3,7 +3,10 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assumptions.assumeThat;
+import java.io.File;
+import java.io.IOException;
import java.io.InputStream;
+import java.nio.file.Files;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
@@ -16,9 +19,8 @@
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
+
+import org.junit.jupiter.api.*;
/** These tests are designed to stress Statements on memory databases. */
public class DBMetaDataTest {
@@ -1398,10 +1400,10 @@ public void columnOrderOfgetImportedKeys() throws SQLException {
stat.executeUpdate(
"create table address (pid integer, name, foreign key(pid) references person(id))");
- ResultSet importedKeys = meta.getImportedKeys("default", "global", "address");
+ ResultSet importedKeys = meta.getImportedKeys("default", null, "address");
assertThat(importedKeys.next()).isTrue();
assertThat(importedKeys.getString("PKTABLE_CAT")).isEqualTo("default");
- assertThat(importedKeys.getString("PKTABLE_SCHEM")).isEqualTo("global");
+ assertThat(importedKeys.getString("PKTABLE_SCHEM")).isEqualTo(null);
assertThat(importedKeys.getString("FKTABLE_CAT")).isEqualTo("default");
assertThat(importedKeys.getString("PKTABLE_NAME")).isEqualTo("person");
assertThat(importedKeys.getString("PKCOLUMN_NAME")).isEqualTo("id");
@@ -1423,12 +1425,12 @@ public void columnOrderOfgetExportedKeys() throws SQLException {
stat.executeUpdate(
"create table address (pid integer, name, foreign key(pid) references person(id))");
- ResultSet exportedKeys = meta.getExportedKeys("default", "global", "person");
+ ResultSet exportedKeys = meta.getExportedKeys("default", null, "person");
assertThat(exportedKeys.next()).isTrue();
assertThat(exportedKeys.getString("PKTABLE_CAT")).isEqualTo("default");
- assertThat(exportedKeys.getString("PKTABLE_SCHEM")).isEqualTo("global");
+ assertThat(exportedKeys.getString("PKTABLE_SCHEM")).isEqualTo(null);
assertThat(exportedKeys.getString("FKTABLE_CAT")).isEqualTo("default");
- assertThat(exportedKeys.getString("FKTABLE_SCHEM")).isEqualTo("global");
+ assertThat(exportedKeys.getString("FKTABLE_SCHEM")).isEqualTo(null);
assertThat(exportedKeys.getString("PK_NAME")).isNotNull();
assertThat(exportedKeys.getString("FK_NAME")).isNotNull();
@@ -1568,4 +1570,315 @@ public void version() throws Exception {
assertThat(meta.getDatabaseMinorVersion()).as("db minor version").isEqualTo(minorVersion);
assertThat(meta.getUserName()).as("user name").isNull();
}
+
+ @Nested
+ class DBMetadataTestWithAttachedDatabases {
+ File testDB;
+
+ @BeforeEach
+ public void init() throws IOException, SQLException {
+ ((SQLiteConnection) conn).getDatabase().getConfig().setReadAttachedDatabases(true);
+ testDB = Files.createTempFile("temp", ".sqlite").toFile();
+ stat.executeUpdate("attach database \"" + testDB.toURI().toURL() + "\" as db2;");
+ stat.executeUpdate(
+ "create table db2.test2 (id integer primary key, fn float default 0.0, sn not null, intvalue integer"
+ + "(5), realvalue real(8,3));");
+ stat.executeUpdate("create view db2.testView2 as select * from db2.test2;");
+ }
+
+ @Test
+ public void readSchemaWithAttachedDatabases() throws SQLException {
+ ResultSet rs = meta.getSchemas();
+ assertThat(rs.next()).isTrue();
+ ResultSetMetaData rsMeta = rs.getMetaData();
+ assertThat(rsMeta.getColumnCount()).isEqualTo(2);
+ assertThat(rsMeta.getColumnName(1)).isEqualTo("TABLE_SCHEM");
+ assertThat(rs.getString("TABLE_SCHEM")).isEqualTo("main");
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.getString("TABLE_SCHEM")).isEqualTo("db2");
+ assertThat(rsMeta.getColumnName(2)).isEqualTo("TABLE_CATALOG");
+ }
+
+ @Test
+ public void getTablesForAttachedDatabase() throws SQLException {
+ ResultSet rs = meta.getTables(null, "db2", null, null);
+ assertThat(rs).isNotNull();
+
+ stat.getGeneratedKeys().close();
+ stat.close();
+
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.getString("TABLE_NAME")).isEqualTo("sqlite_schema");
+ assertThat(rs.getString("TABLE_TYPE")).isEqualTo("SYSTEM TABLE");
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.getString("TABLE_NAME")).isEqualTo("test2"); // 3
+ assertThat(rs.getString("TABLE_TYPE")).isEqualTo("TABLE"); // 4
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.getString("TABLE_NAME")).isEqualTo("testView2");
+ assertThat(rs.getString("TABLE_TYPE")).isEqualTo("VIEW");
+ assertThat(rs.next()).isFalse();
+ rs.close();
+
+ rs = meta.getTables(null, null, "bob", null);
+ assertThat(rs.next()).isFalse();
+ rs.close();
+ rs = meta.getTables(null, null, "test", null);
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.next()).isFalse();
+ rs.close();
+ rs = meta.getTables(null, null, "test%", null);
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.next()).isTrue();
+ rs.close();
+
+ rs = meta.getTables(null, null, null, new String[]{"table"});
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.getString("TABLE_NAME")).isEqualTo("test");
+ assertThat(rs.next()).isFalse();
+ rs.close();
+
+ rs = meta.getTables(null, null, null, new String[]{"view"});
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.getString("TABLE_NAME")).isEqualTo("testView");
+ assertThat(rs.next()).isFalse();
+ rs.close();
+
+ rs = meta.getTables(null, null, null, new String[]{"system table"});
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.getString("TABLE_NAME")).isEqualTo("sqlite_schema");
+ assertThat(rs.next()).isFalse();
+ rs.close();
+ }
+
+ @Test
+ public void getColumnsForAttachedDatabaseTables() throws SQLException {
+ ResultSet rs = meta.getColumns(null, "db2", "test2", "id");
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.getString("TABLE_NAME")).isEqualTo("test2");
+ assertThat(rs.getString("COLUMN_NAME")).isEqualTo("id");
+ assertThat(rs.getString("IS_NULLABLE")).isEqualTo("YES");
+ assertThat(rs.getString("COLUMN_DEF")).isNull();
+ assertThat(rs.getInt("DATA_TYPE")).isEqualTo(Types.INTEGER);
+ assertThat(rs.getInt("COLUMN_SIZE")).isEqualTo(2000000000);
+ assertThat(rs.getInt("DECIMAL_DIGITS")).isEqualTo(0);
+ assertThat(rs.getString("IS_AUTOINCREMENT")).isEqualTo("NO");
+ assertThat(rs.next()).isFalse();
+
+ rs = meta.getColumns(null, "db2", "test2", "fn");
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.getString("COLUMN_NAME")).isEqualTo("fn");
+ assertThat(rs.getInt("DATA_TYPE")).isEqualTo(Types.FLOAT);
+ assertThat(rs.getString("IS_NULLABLE")).isEqualTo("YES");
+ assertThat(rs.getString("COLUMN_DEF")).isEqualTo("0.0");
+ assertThat(rs.getInt("COLUMN_SIZE")).isEqualTo(2000000000);
+ assertThat(rs.getInt("DECIMAL_DIGITS")).isEqualTo(10);
+ assertThat(rs.getString("IS_AUTOINCREMENT")).isEqualTo("NO");
+ assertThat(rs.next()).isFalse();
+
+ rs = meta.getColumns(null, "db2", "test2", "sn");
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.getString("COLUMN_NAME")).isEqualTo("sn");
+ assertThat(rs.getString("IS_NULLABLE")).isEqualTo("NO");
+ assertThat(rs.getInt("COLUMN_SIZE")).isEqualTo(2000000000);
+ assertThat(rs.getInt("DECIMAL_DIGITS")).isEqualTo(10);
+ assertThat(rs.getString("COLUMN_DEF")).isNull();
+ assertThat(rs.next()).isFalse();
+
+ rs = meta.getColumns(null, "db2", "test2", "intvalue");
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.getString("COLUMN_NAME")).isEqualTo("intvalue");
+ assertThat(rs.getInt("DATA_TYPE")).isEqualTo(Types.INTEGER);
+ assertThat(rs.getString("IS_NULLABLE")).isEqualTo("YES");
+ assertThat(rs.getInt("COLUMN_SIZE")).isEqualTo(5);
+ assertThat(rs.getInt("DECIMAL_DIGITS")).isEqualTo(0);
+ assertThat(rs.getString("COLUMN_DEF")).isNull();
+ assertThat(rs.next()).isFalse();
+
+ rs = meta.getColumns(null, "db2", "test2", "realvalue");
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.getString("COLUMN_NAME")).isEqualTo("realvalue");
+ assertThat(rs.getInt("DATA_TYPE")).isEqualTo(Types.FLOAT);
+ assertThat(rs.getString("IS_NULLABLE")).isEqualTo("YES");
+ assertThat(rs.getInt("COLUMN_SIZE")).isEqualTo(11);
+ assertThat(rs.getInt("DECIMAL_DIGITS")).isEqualTo(3);
+ assertThat(rs.getString("COLUMN_DEF")).isNull();
+ assertThat(rs.next()).isFalse();
+
+ rs = meta.getColumns(null, "db2", "test2", "%");
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.getString("COLUMN_NAME")).isEqualTo("id");
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.getString("COLUMN_NAME")).isEqualTo("fn");
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.getString("COLUMN_NAME")).isEqualTo("sn");
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.getString("COLUMN_NAME")).isEqualTo("intvalue");
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.getString("COLUMN_NAME")).isEqualTo("realvalue");
+ assertThat(rs.next()).isFalse();
+
+ rs = meta.getColumns(null, "db2", "test2", "%n");
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.getString("COLUMN_NAME")).isEqualTo("fn");
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.getString("COLUMN_NAME")).isEqualTo("sn");
+ assertThat(rs.next()).isFalse();
+
+ rs = meta.getColumns(null, "db2", "test%", "%");
+ // TABLE "test2"
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.getString("TABLE_NAME")).isEqualTo("test2");
+ assertThat(rs.getString("COLUMN_NAME")).isEqualTo("id");
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.getString("TABLE_NAME")).isEqualTo("test2");
+ assertThat(rs.getString("COLUMN_NAME")).isEqualTo("fn");
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.getString("TABLE_NAME")).isEqualTo("test2");
+ assertThat(rs.getString("COLUMN_NAME")).isEqualTo("sn");
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.getString("TABLE_NAME")).isEqualTo("test2");
+ assertThat(rs.getString("COLUMN_NAME")).isEqualTo("intvalue");
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.getString("TABLE_NAME")).isEqualTo("test2");
+ assertThat(rs.getString("COLUMN_NAME")).isEqualTo("realvalue");
+ // VIEW "testView2"
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.getString("TABLE_NAME")).isEqualTo("testView2");
+ assertThat(rs.getString("COLUMN_NAME")).isEqualTo("id");
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.getString("TABLE_NAME")).isEqualTo("testView2");
+ assertThat(rs.getString("COLUMN_NAME")).isEqualTo("fn");
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.getString("TABLE_NAME")).isEqualTo("testView2");
+ assertThat(rs.getString("COLUMN_NAME")).isEqualTo("sn");
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.getString("TABLE_NAME")).isEqualTo("testView2");
+ assertThat(rs.getString("COLUMN_NAME")).isEqualTo("intvalue");
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.getString("TABLE_NAME")).isEqualTo("testView2");
+ assertThat(rs.getString("COLUMN_NAME")).isEqualTo("realvalue");
+ assertThat(rs.next()).isFalse();
+
+ rs = meta.getColumns(null, "db2", "%", "%");
+ // TABLE "test2"
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.getString("TABLE_NAME")).isEqualTo("test2");
+ assertThat(rs.getString("COLUMN_NAME")).isEqualTo("id");
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.getString("COLUMN_NAME")).isEqualTo("fn");
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.getString("COLUMN_NAME")).isEqualTo("sn");
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.getString("COLUMN_NAME")).isEqualTo("intvalue");
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.getString("COLUMN_NAME")).isEqualTo("realvalue");
+ // VIEW "testView2"
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.getString("TABLE_NAME")).isEqualTo("testView2");
+ assertThat(rs.getString("COLUMN_NAME")).isEqualTo("id");
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.getString("COLUMN_NAME")).isEqualTo("fn");
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.getString("COLUMN_NAME")).isEqualTo("sn");
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.getString("COLUMN_NAME")).isEqualTo("intvalue");
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.getString("COLUMN_NAME")).isEqualTo("realvalue");
+ assertThat(rs.next()).isFalse();
+
+ rs = meta.getColumns(null, "db2", "doesnotexist", "%");
+ assertThat(rs.next()).isFalse();
+ assertThat(rs.getMetaData().getColumnCount()).isEqualTo(24);
+ }
+
+ @Test
+ public void columnOrderOfgetExportedKeysForAttachedDatabase() throws SQLException {
+
+ stat.executeUpdate("create table db2.person (id integer primary key)");
+ stat.executeUpdate("create table db2.address (pid integer, name, foreign key(pid) references person(id))");
+
+ ResultSet exportedKeys = meta.getExportedKeys("default", "db2", "person");
+ assertThat(exportedKeys.next()).isTrue();
+ assertThat(exportedKeys.getString("PKTABLE_CAT")).isEqualTo("default");
+ assertThat(exportedKeys.getString("PKTABLE_SCHEM")).isEqualTo("db2");
+ assertThat(exportedKeys.getString("FKTABLE_CAT")).isEqualTo("default");
+ assertThat(exportedKeys.getString("FKTABLE_SCHEM")).isEqualTo("db2");
+ assertThat(exportedKeys.getString("PK_NAME")).isNotNull();
+ assertThat(exportedKeys.getString("FK_NAME")).isNotNull();
+
+ assertThat(exportedKeys.getString("PKTABLE_NAME")).isEqualTo("person");
+ assertThat(exportedKeys.getString("PKCOLUMN_NAME")).isEqualTo("id");
+ assertThat(exportedKeys.getString("FKTABLE_NAME")).isEqualTo("address");
+ assertThat(exportedKeys.getString("FKCOLUMN_NAME")).isEqualTo("pid");
+
+ exportedKeys.close();
+
+ exportedKeys = meta.getExportedKeys(null, "db2", "address");
+ assertThat(exportedKeys.next()).isFalse();
+ exportedKeys.close();
+
+ // With explicit primary column defined.
+ stat.executeUpdate("create table db2.REFERRED (ID integer primary key not null)");
+ stat.executeUpdate(
+ "create table db2.REFERRING (ID integer, RID integer, constraint fk\r\n foreign\tkey\r\n(RID) "
+ + "references REFERRED(id))");
+
+ exportedKeys = meta.getExportedKeys(null, "db2", "referred");
+ assertThat(exportedKeys.getString("PKTABLE_NAME")).isEqualTo("REFERRED");
+ assertThat(exportedKeys.getString("FKTABLE_NAME")).isEqualTo("REFERRING");
+ assertThat(exportedKeys.getString("FK_NAME")).isEqualTo("fk");
+ exportedKeys.close();
+ }
+
+ @Test
+ public void getIndexInfoOnTestForAttachedDatabase() throws SQLException {
+ ResultSet rs = meta.getIndexInfo(null, "db2", "test2", false, false);
+ assertThat(rs).isNotNull();
+ }
+
+ @Test
+ public void getIndexInfoIndexedSingleForAttachedDatabase() throws SQLException {
+ stat.executeUpdate("create table db2.testindex (id integer primary key, fn float default 0.0, sn not null);");
+ stat.executeUpdate("create index db2.testindex_idx on testindex (sn);");
+
+ ResultSet rs = meta.getIndexInfo(null, "db2", "testindex", false, false);
+ ResultSetMetaData rsmd = rs.getMetaData();
+
+ assertThat(rs).isNotNull();
+ assertThat(rsmd).isNotNull();
+ }
+
+ @Test
+ public void getIndexInfoIndexedSingleExprForAttachedDatabase() throws SQLException {
+ stat.executeUpdate("create table db2.testindex (id integer primary key, fn float default 0.0, sn not null);");
+ stat.executeUpdate("create index db2.testindex_idx on testindex (sn, fn/2);");
+
+ ResultSet rs = meta.getIndexInfo(null, "db2", "testindex", false, false);
+ ResultSetMetaData rsmd = rs.getMetaData();
+
+ assertThat(rs).isNotNull();
+ assertThat(rsmd).isNotNull();
+ }
+
+ @Test
+ public void getIndexInfoIndexedMultiForAttachedDatabase() throws SQLException {
+ stat.executeUpdate("create table db2.testindex (id integer primary key, fn float default 0.0, sn not null);");
+ stat.executeUpdate("create index db2.testindex_idx on testindex (sn);");
+ stat.executeUpdate("create index db2.testindex_pk_idx on testindex (id);");
+
+ ResultSet rs = meta.getIndexInfo(null, "db2", "testindex", false, false);
+ ResultSetMetaData rsmd = rs.getMetaData();
+
+ assertThat(rs).isNotNull();
+ assertThat(rsmd).isNotNull();
+ }
+
+ @AfterEach
+ public void exit() throws SQLException {
+ ((SQLiteConnection) conn).getDatabase().getConfig().setReadAttachedDatabases(false);
+ stat.executeUpdate("Detach database db2;");
+ testDB.deleteOnExit();
+ }
+
+ }
}
From 8ccbf02bc14ab89e75d16f361dbbe66fb2d4d0c2 Mon Sep 17 00:00:00 2001
From: Georgii Gvinepadze
Date: Tue, 17 Jan 2023 13:58:51 +0400
Subject: [PATCH 02/10] remove read attached databases pragma
---
src/main/java/org/sqlite/SQLiteConfig.java | 33 ++-----------------
.../sqlite/jdbc3/JDBC3DatabaseMetaData.java | 10 ++----
src/test/java/org/sqlite/DBMetaDataTest.java | 4 +--
3 files changed, 7 insertions(+), 40 deletions(-)
diff --git a/src/main/java/org/sqlite/SQLiteConfig.java b/src/main/java/org/sqlite/SQLiteConfig.java
index 04b9c66dd..56b8bc26f 100755
--- a/src/main/java/org/sqlite/SQLiteConfig.java
+++ b/src/main/java/org/sqlite/SQLiteConfig.java
@@ -56,7 +56,6 @@ public class SQLiteConfig {
private final int busyTimeout;
private boolean explicitReadOnly;
- private boolean readAttachedDatabases;
private final SQLiteConnectionConfig defaultConnectionConfig;
@@ -94,10 +93,6 @@ public SQLiteConfig(Properties prop) {
this.explicitReadOnly =
Boolean.parseBoolean(
pragmaTable.getProperty(Pragma.JDBC_EXPLICIT_READONLY.pragmaName, "false"));
- this.readAttachedDatabases =
- Boolean.parseBoolean(
- pragmaTable.getProperty(
- Pragma.METADATA_READ_ATTACHED_DATABASES.pragmaName, "false"));
}
public SQLiteConnectionConfig newConnectionConfig() {
@@ -191,9 +186,8 @@ public void apply(Connection conn) throws SQLException {
pragmaParams.remove(Pragma.LIMIT_WORKER_THREADS.pragmaName);
pragmaParams.remove(Pragma.LIMIT_PAGE_COUNT.pragmaName);
- // exclude these "fake" pragmas from execution
+ // exclude this "fake" pragma from execution
pragmaParams.remove(Pragma.JDBC_EXPLICIT_READONLY.pragmaName);
- pragmaParams.remove(Pragma.METADATA_READ_ATTACHED_DATABASES.pragmaName);
Statement stat = conn.createStatement();
try {
@@ -336,10 +330,6 @@ public Properties toProperties() {
defaultConnectionConfig.getDateStringFormat());
pragmaTable.setProperty(
Pragma.JDBC_EXPLICIT_READONLY.pragmaName, this.explicitReadOnly ? "true" : "false");
- pragmaTable.setProperty(
- Pragma.METADATA_READ_ATTACHED_DATABASES.pragmaName,
- this.readAttachedDatabases ? "true" : "false"
- );
return pragmaTable;
}
@@ -383,20 +373,6 @@ public void setExplicitReadOnly(boolean readOnly) {
this.explicitReadOnly = readOnly;
}
- /** @return true if reading attached databases is allowed */
- public boolean isReadAttachedDatabases() {
- return readAttachedDatabases;
- }
-
- /**
- * Enable reading attached databases in metadata, they will be shown as schemas
- *
- * @param readAttachedDatabases whether to read attached databases
- */
- public void setReadAttachedDatabases(boolean readAttachedDatabases) {
- this.readAttachedDatabases = readAttachedDatabases;
- }
-
public enum Pragma {
// Parameters requiring SQLite3 API invocation
@@ -558,11 +534,8 @@ public enum Pragma {
// extensions: "fake" pragmas to allow conformance with JDBC
JDBC_EXPLICIT_READONLY(
- "jdbc.explicit_readonly", "Set explicit read only transactions", null),
- // "fake" pragma to allow configurating metadata reading by driver
- METADATA_READ_ATTACHED_DATABASES("metadata.read_attached_databases",
- "Set read attached databases as schemas",
- OnOff);
+ "jdbc.explicit_readonly", "Set explicit read only transactions", null);
+
public final String pragmaName;
public final String[] choices;
public final String description;
diff --git a/src/main/java/org/sqlite/jdbc3/JDBC3DatabaseMetaData.java b/src/main/java/org/sqlite/jdbc3/JDBC3DatabaseMetaData.java
index 2a8c0c358..419ecafd7 100644
--- a/src/main/java/org/sqlite/jdbc3/JDBC3DatabaseMetaData.java
+++ b/src/main/java/org/sqlite/jdbc3/JDBC3DatabaseMetaData.java
@@ -681,7 +681,7 @@ public boolean supportsSavepoints() {
/** @see java.sql.DatabaseMetaData#supportsSchemasInDataManipulation() */
public boolean supportsSchemasInDataManipulation() {
- return conn.getDatabase().getConfig().isReadAttachedDatabases();
+ return true;
}
/** @see java.sql.DatabaseMetaData#supportsSchemasInIndexDefinitions() */
@@ -1172,12 +1172,8 @@ public ResultSet getCrossReference(
/** @see java.sql.DatabaseMetaData#getSchemas() */
public ResultSet getSchemas() throws SQLException {
if (getSchemas == null) {
- if (conn.getDatabase().getConfig().isReadAttachedDatabases()) {
- getSchemas = conn.prepareStatement(
- "select name as TABLE_SCHEM, null as TABLE_CATALOG from pragma_database_list;");
- } else {
- getSchemas = conn.prepareStatement("select null as TABLE_SCHEM, null as TABLE_CATALOG limit 0;");
- }
+ getSchemas = conn.prepareStatement(
+ "select name as TABLE_SCHEM, null as TABLE_CATALOG from pragma_database_list;");
}
return getSchemas.executeQuery();
diff --git a/src/test/java/org/sqlite/DBMetaDataTest.java b/src/test/java/org/sqlite/DBMetaDataTest.java
index 560511a96..d02352515 100644
--- a/src/test/java/org/sqlite/DBMetaDataTest.java
+++ b/src/test/java/org/sqlite/DBMetaDataTest.java
@@ -1087,7 +1087,7 @@ public void columnOrderOfgetProcedurColumns() throws SQLException {
@Test
public void columnOrderOfgetSchemas() throws SQLException {
ResultSet rs = meta.getSchemas();
- assertThat(rs.next()).isFalse();
+ assertThat(rs.next()).isTrue();
ResultSetMetaData rsmeta = rs.getMetaData();
assertThat(rsmeta.getColumnCount()).isEqualTo(2);
assertThat(rsmeta.getColumnName(1)).isEqualTo("TABLE_SCHEM");
@@ -1577,7 +1577,6 @@ class DBMetadataTestWithAttachedDatabases {
@BeforeEach
public void init() throws IOException, SQLException {
- ((SQLiteConnection) conn).getDatabase().getConfig().setReadAttachedDatabases(true);
testDB = Files.createTempFile("temp", ".sqlite").toFile();
stat.executeUpdate("attach database \"" + testDB.toURI().toURL() + "\" as db2;");
stat.executeUpdate(
@@ -1875,7 +1874,6 @@ public void getIndexInfoIndexedMultiForAttachedDatabase() throws SQLException {
@AfterEach
public void exit() throws SQLException {
- ((SQLiteConnection) conn).getDatabase().getConfig().setReadAttachedDatabases(false);
stat.executeUpdate("Detach database db2;");
testDB.deleteOnExit();
}
From 24fcc05fbdd4d55e4f180823e2506252c5ecd44b Mon Sep 17 00:00:00 2001
From: Georgii Gvinepadze
Date: Tue, 17 Jan 2023 14:37:37 +0400
Subject: [PATCH 03/10] suggestions from code review
---
src/main/java/org/sqlite/jdbc3/JDBC3DatabaseMetaData.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/main/java/org/sqlite/jdbc3/JDBC3DatabaseMetaData.java b/src/main/java/org/sqlite/jdbc3/JDBC3DatabaseMetaData.java
index 419ecafd7..a3301f873 100644
--- a/src/main/java/org/sqlite/jdbc3/JDBC3DatabaseMetaData.java
+++ b/src/main/java/org/sqlite/jdbc3/JDBC3DatabaseMetaData.java
@@ -681,7 +681,7 @@ public boolean supportsSavepoints() {
/** @see java.sql.DatabaseMetaData#supportsSchemasInDataManipulation() */
public boolean supportsSchemasInDataManipulation() {
- return true;
+ return false;
}
/** @see java.sql.DatabaseMetaData#supportsSchemasInIndexDefinitions() */
From 6e5922714edc189a9d64831178ecdd79b9ab3c25 Mon Sep 17 00:00:00 2001
From: Georgii Gvinepadze
Date: Wed, 18 Jan 2023 15:01:04 +0400
Subject: [PATCH 04/10] additional test cases & improve metadata reading &
formatting
---
.../org/sqlite/core/CoreDatabaseMetaData.java | 3 +-
.../sqlite/jdbc3/JDBC3DatabaseMetaData.java | 114 +++++++++++-------
src/test/java/org/sqlite/DBMetaDataTest.java | 57 +++++++--
3 files changed, 119 insertions(+), 55 deletions(-)
diff --git a/src/main/java/org/sqlite/core/CoreDatabaseMetaData.java b/src/main/java/org/sqlite/core/CoreDatabaseMetaData.java
index fdb8eabef..33646fd8a 100644
--- a/src/main/java/org/sqlite/core/CoreDatabaseMetaData.java
+++ b/src/main/java/org/sqlite/core/CoreDatabaseMetaData.java
@@ -186,6 +186,7 @@ protected String escape(final String val) {
/**
* Returns line without changes or with escaped schema prefix
+ *
* @param schema schema name
* @param line of text to prepend to
* @return The SQL escaped schema name with dot or empty string
@@ -200,6 +201,7 @@ protected String prependSchemaPrefix(String schema, String line) {
/**
* Adds line without changes or with escaped schema prefix
+ *
* @param sql String builder for sql request
* @param schema schema name
* @param line line to prepend schema prefix to
@@ -212,7 +214,6 @@ protected void prependSchemaPrefix(StringBuilder sql, String schema, String line
}
}
-
// inner classes
/** Pattern used to extract column order for an unnamed primary key. */
diff --git a/src/main/java/org/sqlite/jdbc3/JDBC3DatabaseMetaData.java b/src/main/java/org/sqlite/jdbc3/JDBC3DatabaseMetaData.java
index a3301f873..651818af9 100644
--- a/src/main/java/org/sqlite/jdbc3/JDBC3DatabaseMetaData.java
+++ b/src/main/java/org/sqlite/jdbc3/JDBC3DatabaseMetaData.java
@@ -915,7 +915,9 @@ public ResultSet getColumns(String c, String s, String tblNamePattern, String co
checkOpen();
StringBuilder sql = new StringBuilder(700);
- sql.append("select null as TABLE_CAT, ").append(quote(s)).append(" as TABLE_SCHEM, tblname as TABLE_NAME, ")
+ sql.append("select null as TABLE_CAT, ")
+ .append(quote(s == null ? "main" : s))
+ .append(" as TABLE_SCHEM, tblname as TABLE_NAME, ")
.append(
"cn as COLUMN_NAME, ct as DATA_TYPE, tn as TYPE_NAME, colSize as COLUMN_SIZE, ")
.append(
@@ -948,10 +950,13 @@ public ResultSet getColumns(String c, String s, String tblNamePattern, String co
ResultSet rsColAutoinc = null;
try {
statColAutoinc = conn.createStatement();
- rsColAutoinc = statColAutoinc.executeQuery(
- "SELECT LIKE('%autoincrement%', LOWER(sql)) FROM " + prependSchemaPrefix(s,
- "sqlite_master WHERE LOWER(name) = LOWER('") + escape(tableName)
- + "') AND TYPE IN ('table', 'view')");
+ rsColAutoinc =
+ statColAutoinc.executeQuery(
+ "SELECT LIKE('%autoincrement%', LOWER(sql)) FROM "
+ + prependSchemaPrefix(
+ s, "sqlite_master WHERE LOWER(name) = LOWER('")
+ + escape(tableName)
+ + "') AND TYPE IN ('table', 'view')");
rsColAutoinc.next();
isAutoIncrement = rsColAutoinc.getInt(1) == 1;
} finally {
@@ -972,8 +977,10 @@ public ResultSet getColumns(String c, String s, String tblNamePattern, String co
}
// For each table, get the column info and build into overall SQL
- String pragmaStatement = "PRAGMA " + prependSchemaPrefix(s, "table_xinfo('" + escape(tableName) +
- "')");
+ String pragmaStatement =
+ "PRAGMA "
+ + prependSchemaPrefix(
+ s, "table_xinfo('" + escape(tableName) + "')");
try (Statement colstat = conn.createStatement();
ResultSet rscol = colstat.executeQuery(pragmaStatement)) {
@@ -1172,8 +1179,9 @@ public ResultSet getCrossReference(
/** @see java.sql.DatabaseMetaData#getSchemas() */
public ResultSet getSchemas() throws SQLException {
if (getSchemas == null) {
- getSchemas = conn.prepareStatement(
- "select name as TABLE_SCHEM, null as TABLE_CATALOG from pragma_database_list;");
+ getSchemas =
+ conn.prepareStatement(
+ "select name as TABLE_SCHEM, null as TABLE_CATALOG from pragma_database_list;");
}
return getSchemas.executeQuery();
@@ -1198,7 +1206,9 @@ public ResultSet getPrimaryKeys(String c, String s, String table) throws SQLExce
Statement stat = conn.createStatement();
StringBuilder sql = new StringBuilder(512);
- sql.append("select null as TABLE_CAT, ").append(quote(s)).append(" as TABLE_SCHEM, '")
+ sql.append("select null as TABLE_CAT, ")
+ .append(quote(s == null ? "main" : s))
+ .append(" as TABLE_SCHEM, '")
.append(escape(table))
.append("' as TABLE_NAME, cn as COLUMN_NAME, ks as KEY_SEQ, pk as PK_NAME from (");
@@ -1249,7 +1259,7 @@ public ResultSet getExportedKeys(String catalog, String schema, String table)
catalog = (catalog != null) ? quote(catalog) : null;
- String quotedSchema = (schema != null) ? quote(schema) : null;
+ String quotedSchema = (schema != null) ? quote(schema) : quote("main");
StringBuilder exportedKeysQuery = new StringBuilder(512);
@@ -1258,11 +1268,11 @@ public ResultSet getExportedKeys(String catalog, String schema, String table)
if (pkColumns != null) {
// retrieve table list
ArrayList tableList;
- try (
- ResultSet rs = stat.executeQuery(
- "select name from " + prependSchemaPrefix(schema, "sqlite_master where type = " +
- "'table'"))
- ) {
+ try (ResultSet rs =
+ stat.executeQuery(
+ "select name from "
+ + prependSchemaPrefix(
+ schema, "sqlite_master where type = " + "'table'"))) {
tableList = new ArrayList<>();
while (rs.next()) {
@@ -1406,12 +1416,12 @@ public ResultSet getImportedKeys(String catalog, String schema, String table)
sql.append("select ")
.append(quote(catalog))
.append(" as PKTABLE_CAT, ")
- .append(quote(schema))
+ .append(quote(schema == null ? "main" : schema))
.append(" as PKTABLE_SCHEM, ")
.append("ptn as PKTABLE_NAME, pcn as PKCOLUMN_NAME, ")
.append(quote(catalog))
.append(" as FKTABLE_CAT, ")
- .append(quote(schema))
+ .append(quote(schema == null ? "main" : schema))
.append(" as FKTABLE_SCHEM, ")
.append(quote(table))
.append(" as FKTABLE_NAME, ")
@@ -1526,7 +1536,7 @@ public ResultSet getIndexInfo(String c, String s, String table, boolean u, boole
// define the column header
// this is from the JDBC spec, it is part of the driver protocol
sql.append("select null as TABLE_CAT,")
- .append(quote(s))
+ .append(quote(s == null ? "main" : s))
.append(" as TABLE_SCHEM, '")
.append(escape(table))
.append(
@@ -1537,7 +1547,9 @@ public ResultSet getIndexInfo(String c, String s, String table, boolean u, boole
"cn as COLUMN_NAME, null as ASC_OR_DESC, 0 as CARDINALITY, 0 as PAGES, null as FILTER_CONDITION from (");
// this always returns a result set now, previously threw exception
- rs = stat.executeQuery("pragma " + prependSchemaPrefix(s, "index_list('" + escape(table) + "');"));
+ rs =
+ stat.executeQuery(
+ "pragma " + prependSchemaPrefix(s, "index_list('" + escape(table) + "');"));
ArrayList> indexList = new ArrayList<>();
while (rs.next()) {
@@ -1561,7 +1573,11 @@ public ResultSet getIndexInfo(String c, String s, String table, boolean u, boole
while (indexIterator.hasNext()) {
currentIndex = indexIterator.next();
String indexName = currentIndex.get(0).toString();
- rs = stat.executeQuery("pragma " + prependSchemaPrefix(s, "index_info('" + escape(indexName) + "');"));
+ rs =
+ stat.executeQuery(
+ "pragma "
+ + prependSchemaPrefix(
+ s, "index_info('" + escape(indexName) + "');"));
while (rs.next()) {
@@ -1689,7 +1705,10 @@ public synchronized ResultSet getTables(
StringBuilder sql = new StringBuilder();
sql.append("SELECT").append("\n");
sql.append(" NULL AS TABLE_CAT,").append("\n");
- sql.append(" NULL AS TABLE_SCHEM,").append("\n");
+ sql.append(" ")
+ .append(quote(s == null ? "main" : s))
+ .append(" AS TABLE_SCHEM,")
+ .append("\n");
sql.append(" NAME AS TABLE_NAME,").append("\n");
sql.append(" TYPE AS TABLE_TYPE,").append("\n");
sql.append(" NULL AS REMARKS,").append("\n");
@@ -1985,7 +2004,7 @@ public PrimaryKeyFinder(String table) throws SQLException {
/**
* Constructor.
*
- * @param table The table for which to get find a primary key.
+ * @param table The table for which to get find a primary key.
* @param schema Schema in which table is located
* @throws SQLException
*/
@@ -2000,10 +2019,13 @@ public PrimaryKeyFinder(String table, String schema) throws SQLException {
// read create SQL script for table
ResultSet rs =
stat.executeQuery(
- "select sql from " + prependSchemaPrefix(schema, "sqlite_master where"
- + " lower(name) = lower('"
- + escape(table)
- + "') and type in ('table', 'view')"))) {
+ "select sql from "
+ + prependSchemaPrefix(
+ schema,
+ "sqlite_master where"
+ + " lower(name) = lower('"
+ + escape(table)
+ + "') and type in ('table', 'view')"))) {
if (!rs.next()) throw new SQLException("Table not found: '" + table + "'");
@@ -2019,10 +2041,12 @@ public PrimaryKeyFinder(String table, String schema) throws SQLException {
}
if (pkColumns == null) {
- try (
- ResultSet rs2 = stat.executeQuery(
- "pragma " + prependSchemaPrefix(schema, "table_info('" + escape(table) + "');"))
- ) {
+ try (ResultSet rs2 =
+ stat.executeQuery(
+ "pragma "
+ + prependSchemaPrefix(
+ schema,
+ "table_info('" + escape(table) + "');"))) {
while (rs2.next()) {
if (rs2.getBoolean(6)) pkColumns = new String[] {rs2.getString(2)};
}
@@ -2073,13 +2097,15 @@ public ImportedKeyFinder(String table, String schema) throws SQLException {
List fkNames = getForeignKeyNames(this.fkTableName, schema);
- try (
- Statement stat = conn.createStatement();
- ResultSet rs = stat.executeQuery("pragma " + prependSchemaPrefix(
- schema,
- "foreign_key_list('" + escape(this.fkTableName.toLowerCase()) + "')"
- ))
- ) {
+ try (Statement stat = conn.createStatement();
+ ResultSet rs =
+ stat.executeQuery(
+ "pragma "
+ + prependSchemaPrefix(
+ schema,
+ "foreign_key_list('"
+ + escape(this.fkTableName.toLowerCase())
+ + "')"))) {
int prevFkId = -1;
int count = 0;
@@ -2122,9 +2148,15 @@ private List getForeignKeyNames(String tbl, String schema) throws SQLExc
return fkNames;
}
try (Statement stat2 = conn.createStatement();
- ResultSet rs = stat2.executeQuery("select sql from " + prependSchemaPrefix(schema,
- "sqlite_master where" + " lower(name) = lower('" + escape(tbl) + "')"
- ))) {
+ ResultSet rs =
+ stat2.executeQuery(
+ "select sql from "
+ + prependSchemaPrefix(
+ schema,
+ "sqlite_master where"
+ + " lower(name) = lower('"
+ + escape(tbl)
+ + "')"))) {
if (rs.next()) {
Matcher matcher = FK_NAMED_PATTERN.matcher(rs.getString(1));
diff --git a/src/test/java/org/sqlite/DBMetaDataTest.java b/src/test/java/org/sqlite/DBMetaDataTest.java
index d02352515..3d26aa0a2 100644
--- a/src/test/java/org/sqlite/DBMetaDataTest.java
+++ b/src/test/java/org/sqlite/DBMetaDataTest.java
@@ -19,7 +19,6 @@
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
-
import org.junit.jupiter.api.*;
/** These tests are designed to stress Statements on memory databases. */
@@ -1372,7 +1371,7 @@ private void assertPrimaryKey(
.isNull();
assertThat(rs.getString("TABLE_SCHEM"))
.as("DatabaseMetaData.getPrimaryKeys: TABLE_SCHEM")
- .isNull();
+ .isEqualTo("main");
assertThat(rs.getString("TABLE_NAME"))
.as("DatabaseMetaData.getPrimaryKeys: TABLE_NAME")
.isEqualTo(tableName);
@@ -1403,7 +1402,7 @@ public void columnOrderOfgetImportedKeys() throws SQLException {
ResultSet importedKeys = meta.getImportedKeys("default", null, "address");
assertThat(importedKeys.next()).isTrue();
assertThat(importedKeys.getString("PKTABLE_CAT")).isEqualTo("default");
- assertThat(importedKeys.getString("PKTABLE_SCHEM")).isEqualTo(null);
+ assertThat(importedKeys.getString("PKTABLE_SCHEM")).isEqualTo("main");
assertThat(importedKeys.getString("FKTABLE_CAT")).isEqualTo("default");
assertThat(importedKeys.getString("PKTABLE_NAME")).isEqualTo("person");
assertThat(importedKeys.getString("PKCOLUMN_NAME")).isEqualTo("id");
@@ -1428,9 +1427,9 @@ public void columnOrderOfgetExportedKeys() throws SQLException {
ResultSet exportedKeys = meta.getExportedKeys("default", null, "person");
assertThat(exportedKeys.next()).isTrue();
assertThat(exportedKeys.getString("PKTABLE_CAT")).isEqualTo("default");
- assertThat(exportedKeys.getString("PKTABLE_SCHEM")).isEqualTo(null);
+ assertThat(exportedKeys.getString("PKTABLE_SCHEM")).isEqualTo("main");
assertThat(exportedKeys.getString("FKTABLE_CAT")).isEqualTo("default");
- assertThat(exportedKeys.getString("FKTABLE_SCHEM")).isEqualTo(null);
+ assertThat(exportedKeys.getString("FKTABLE_SCHEM")).isEqualTo("main");
assertThat(exportedKeys.getString("PK_NAME")).isNotNull();
assertThat(exportedKeys.getString("FK_NAME")).isNotNull();
@@ -1630,25 +1629,44 @@ public void getTablesForAttachedDatabase() throws SQLException {
assertThat(rs.next()).isTrue();
rs.close();
- rs = meta.getTables(null, null, null, new String[]{"table"});
+ rs = meta.getTables(null, null, null, new String[] {"table"});
assertThat(rs.next()).isTrue();
assertThat(rs.getString("TABLE_NAME")).isEqualTo("test");
assertThat(rs.next()).isFalse();
rs.close();
- rs = meta.getTables(null, null, null, new String[]{"view"});
+ rs = meta.getTables(null, null, null, new String[] {"view"});
assertThat(rs.next()).isTrue();
assertThat(rs.getString("TABLE_NAME")).isEqualTo("testView");
assertThat(rs.next()).isFalse();
rs.close();
- rs = meta.getTables(null, null, null, new String[]{"system table"});
+ rs = meta.getTables(null, null, null, new String[] {"system table"});
assertThat(rs.next()).isTrue();
assertThat(rs.getString("TABLE_NAME")).isEqualTo("sqlite_schema");
assertThat(rs.next()).isFalse();
rs.close();
}
+ @Test
+ public void testDynamicDatabaseAttachment() throws IOException, SQLException {
+ ResultSet schemas = meta.getSchemas();
+ schemas.close();
+ File thirdDB = Files.createTempFile("db3", ".sqlite").toFile();
+ stat.executeUpdate("attach database \"" + thirdDB.toURI().toURL() + "\" as db3;");
+ try {
+ schemas = meta.getSchemas();
+ boolean schemaFound = false;
+ while (schemas.next()) {
+ schemaFound = "db3".equals(schemas.getString("TABLE_SCHEM"));
+ }
+ assertThat(schemaFound).isTrue();
+ } finally {
+ stat.executeUpdate("detach database db3;");
+ thirdDB.deleteOnExit();
+ }
+ }
+
@Test
public void getColumnsForAttachedDatabaseTables() throws SQLException {
ResultSet rs = meta.getColumns(null, "db2", "test2", "id");
@@ -1790,11 +1808,22 @@ public void getColumnsForAttachedDatabaseTables() throws SQLException {
assertThat(rs.getMetaData().getColumnCount()).isEqualTo(24);
}
+ @Test
+ public void getTablesForDefaultSchema() throws SQLException {
+ ResultSet rs = meta.getTables(null, null, null, new String[] {"table"});
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.getString("TABLE_NAME")).isEqualTo("test");
+ assertThat(rs.getString("TABLE_SCHEM")).isEqualTo("main");
+ assertThat(rs.next()).isFalse();
+ rs.close();
+ }
+
@Test
public void columnOrderOfgetExportedKeysForAttachedDatabase() throws SQLException {
stat.executeUpdate("create table db2.person (id integer primary key)");
- stat.executeUpdate("create table db2.address (pid integer, name, foreign key(pid) references person(id))");
+ stat.executeUpdate(
+ "create table db2.address (pid integer, name, foreign key(pid) references person(id))");
ResultSet exportedKeys = meta.getExportedKeys("default", "db2", "person");
assertThat(exportedKeys.next()).isTrue();
@@ -1837,7 +1866,8 @@ public void getIndexInfoOnTestForAttachedDatabase() throws SQLException {
@Test
public void getIndexInfoIndexedSingleForAttachedDatabase() throws SQLException {
- stat.executeUpdate("create table db2.testindex (id integer primary key, fn float default 0.0, sn not null);");
+ stat.executeUpdate(
+ "create table db2.testindex (id integer primary key, fn float default 0.0, sn not null);");
stat.executeUpdate("create index db2.testindex_idx on testindex (sn);");
ResultSet rs = meta.getIndexInfo(null, "db2", "testindex", false, false);
@@ -1849,7 +1879,8 @@ public void getIndexInfoIndexedSingleForAttachedDatabase() throws SQLException {
@Test
public void getIndexInfoIndexedSingleExprForAttachedDatabase() throws SQLException {
- stat.executeUpdate("create table db2.testindex (id integer primary key, fn float default 0.0, sn not null);");
+ stat.executeUpdate(
+ "create table db2.testindex (id integer primary key, fn float default 0.0, sn not null);");
stat.executeUpdate("create index db2.testindex_idx on testindex (sn, fn/2);");
ResultSet rs = meta.getIndexInfo(null, "db2", "testindex", false, false);
@@ -1861,7 +1892,8 @@ public void getIndexInfoIndexedSingleExprForAttachedDatabase() throws SQLExcepti
@Test
public void getIndexInfoIndexedMultiForAttachedDatabase() throws SQLException {
- stat.executeUpdate("create table db2.testindex (id integer primary key, fn float default 0.0, sn not null);");
+ stat.executeUpdate(
+ "create table db2.testindex (id integer primary key, fn float default 0.0, sn not null);");
stat.executeUpdate("create index db2.testindex_idx on testindex (sn);");
stat.executeUpdate("create index db2.testindex_pk_idx on testindex (id);");
@@ -1877,6 +1909,5 @@ public void exit() throws SQLException {
stat.executeUpdate("Detach database db2;");
testDB.deleteOnExit();
}
-
}
}
From e5e4f1d6eed98404037dc3ecd6143feac7704a4d Mon Sep 17 00:00:00 2001
From: Georgii Gvinepadze
Date: Mon, 13 Feb 2023 21:39:57 +0400
Subject: [PATCH 05/10] getSchemas(String, String) support & schema pattern
matching
---
.../org/sqlite/core/CoreDatabaseMetaData.java | 15 +
.../sqlite/jdbc3/JDBC3DatabaseMetaData.java | 305 +++++++++++-------
.../sqlite/jdbc4/JDBC4DatabaseMetaData.java | 4 -
src/test/java/org/sqlite/DBMetaDataTest.java | 15 +-
4 files changed, 208 insertions(+), 131 deletions(-)
diff --git a/src/main/java/org/sqlite/core/CoreDatabaseMetaData.java b/src/main/java/org/sqlite/core/CoreDatabaseMetaData.java
index 33646fd8a..c1a80fa40 100644
--- a/src/main/java/org/sqlite/core/CoreDatabaseMetaData.java
+++ b/src/main/java/org/sqlite/core/CoreDatabaseMetaData.java
@@ -163,6 +163,21 @@ protected static String quote(String tableName) {
}
}
+ /**
+ * Escapes all wildcards, to prevent pattern matching for
+ * functions which should not support it
+ * @param val The string to escape
+ * @return The string with escaped wildcards
+ */
+ protected static String escapeWildcards(final String val) {
+ if (val == null) {
+ return null;
+ }
+ String replacement = val.replace("%", "\\%");
+ replacement = replacement.replace("_", "\\_");
+ return replacement;
+ }
+
/**
* Applies SQL escapes for special characters in a given string.
*
diff --git a/src/main/java/org/sqlite/jdbc3/JDBC3DatabaseMetaData.java b/src/main/java/org/sqlite/jdbc3/JDBC3DatabaseMetaData.java
index 651818af9..f04cd2bc5 100644
--- a/src/main/java/org/sqlite/jdbc3/JDBC3DatabaseMetaData.java
+++ b/src/main/java/org/sqlite/jdbc3/JDBC3DatabaseMetaData.java
@@ -1187,6 +1187,20 @@ public ResultSet getSchemas() throws SQLException {
return getSchemas.executeQuery();
}
+ /** @see java.sql.DatabaseMetaData#getSchemas(String, String) */
+ public ResultSet getSchemas(String catalog, String schemaPattern) throws SQLException {
+ if (schemaPattern == null) {
+ return getSchemas();
+ }
+ if ("".equals(schemaPattern)) {
+ schemaPattern = "main";
+ }
+ Statement stat = conn.createStatement();
+ String sql = "select name as TABLE_SCHEM, null as TABLE_CATALOG from pragma_database_list\n"
+ + "where TABLE_SCHEM like '" + schemaPattern + "' escape '" + getSearchStringEscape() + "';";
+ return stat.executeQuery(sql);
+ }
+
/** @see java.sql.DatabaseMetaData#getCatalogs() */
public ResultSet getCatalogs() throws SQLException {
if (getCatalogs == null) {
@@ -1201,21 +1215,40 @@ public ResultSet getCatalogs() throws SQLException {
* java.lang.String)
*/
public ResultSet getPrimaryKeys(String c, String s, String table) throws SQLException {
- PrimaryKeyFinder pkFinder = new PrimaryKeyFinder(table, s);
- String[] columns = pkFinder.getColumns();
Statement stat = conn.createStatement();
StringBuilder sql = new StringBuilder(512);
+ ResultSet schemas = getSchemas(c, escapeWildcards(s));
+ ArrayList schemaNames = getSchemasNames(schemas);
+ for (int i = 0; i < schemaNames.size(); i++) {
+ createSchemaPrimaryKeysQuery(schemaNames.get(i), table, sql);
+ if (i != schemaNames.size() - 1) {
+ sql.append(" union all ");
+ }
+ }
+ return ((CoreStatement) stat).executeQuery(sql.toString(), true);
+ }
+
+ private ArrayList getSchemasNames(ResultSet schemas) throws SQLException {
+ ArrayList schemaNames = new ArrayList<>();
+ while (schemas.next()) {
+ schemaNames.add(schemas.getString("TABLE_SCHEM"));
+ }
+ return schemaNames;
+ }
+
+ private void createSchemaPrimaryKeysQuery(String s, String table, StringBuilder sql) throws SQLException {
+ PrimaryKeyFinder pkFinder = new PrimaryKeyFinder(table, s);
+ String[] columns = pkFinder.getColumns();
sql.append("select null as TABLE_CAT, ")
- .append(quote(s == null ? "main" : s))
+ .append(quote(s))
.append(" as TABLE_SCHEM, '")
.append(escape(table))
.append("' as TABLE_NAME, cn as COLUMN_NAME, ks as KEY_SEQ, pk as PK_NAME from (");
if (columns == null) {
sql.append("select null as cn, null as pk, 0 as ks) limit 0;");
-
- return ((CoreStatement) stat).executeQuery(sql.toString(), true);
+ return;
}
String pkName = pkFinder.getName();
@@ -1233,8 +1266,7 @@ public ResultSet getPrimaryKeys(String c, String s, String table) throws SQLExce
.append(i + 1)
.append(" as ks");
}
-
- return ((CoreStatement) stat).executeQuery(sql.append(") order by cn;").toString(), true);
+ sql.append(") order by cn;");
}
private static final Map RULE_MAP = new HashMap<>();
@@ -1253,70 +1285,75 @@ public ResultSet getPrimaryKeys(String c, String s, String table) throws SQLExce
*/
public ResultSet getExportedKeys(String catalog, String schema, String table)
throws SQLException {
- PrimaryKeyFinder pkFinder = new PrimaryKeyFinder(table, schema);
- String[] pkColumns = pkFinder.getColumns();
Statement stat = conn.createStatement();
+ StringBuilder sql = new StringBuilder(2048);
- catalog = (catalog != null) ? quote(catalog) : null;
+ ResultSet schemas = getSchemas(catalog, escapeWildcards(schema));
+ ArrayList schemasNames = getSchemasNames(schemas);
+ for (int i = 0; i < schemasNames.size(); i++) {
+ PrimaryKeyFinder pkFinder = new PrimaryKeyFinder(table, schemasNames.get(i));
+ String[] pkColumns = pkFinder.getColumns();
- String quotedSchema = (schema != null) ? quote(schema) : quote("main");
+ catalog = (catalog != null) ? quote(catalog) : null;
- StringBuilder exportedKeysQuery = new StringBuilder(512);
+ String quotedSchema = (schema != null) ? quote(schemasNames.get(i)) : quote("main");
- String target = null;
- int count = 0;
- if (pkColumns != null) {
- // retrieve table list
- ArrayList tableList;
- try (ResultSet rs =
- stat.executeQuery(
- "select name from "
- + prependSchemaPrefix(
- schema, "sqlite_master where type = " + "'table'"))) {
- tableList = new ArrayList<>();
+ StringBuilder exportedKeysQuery = new StringBuilder(512);
- while (rs.next()) {
- String tblname = rs.getString(1);
- tableList.add(tblname);
- if (tblname.equalsIgnoreCase(table)) {
- // get the correct case as in the database
- // (not uppercase nor lowercase)
- target = tblname;
+ String target = null;
+ int count = 0;
+ if (pkColumns != null) {
+ // retrieve table list
+ ArrayList tableList;
+ try (ResultSet rs =
+ stat.executeQuery(
+ "select name from "
+ + prependSchemaPrefix(
+ schema, "sqlite_master where type = " + "'table'"))) {
+ tableList = new ArrayList<>();
+
+ while (rs.next()) {
+ String tblname = rs.getString(1);
+ tableList.add(tblname);
+ if (tblname.equalsIgnoreCase(table)) {
+ // get the correct case as in the database
+ // (not uppercase nor lowercase)
+ target = tblname;
+ }
}
}
- }
- // find imported keys for each table
- for (String tbl : tableList) {
- final ImportedKeyFinder impFkFinder = new ImportedKeyFinder(tbl, schema);
- List fkNames = impFkFinder.getFkList();
+ // find imported keys for each table
+ for (String tbl : tableList) {
+ final ImportedKeyFinder impFkFinder = new ImportedKeyFinder(tbl, schema);
+ List fkNames = impFkFinder.getFkList();
- for (ForeignKey foreignKey : fkNames) {
- String PKTabName = foreignKey.getPkTableName();
+ for (ForeignKey foreignKey : fkNames) {
+ String PKTabName = foreignKey.getPkTableName();
- if (PKTabName == null || !PKTabName.equalsIgnoreCase(target)) {
- continue;
- }
+ if (PKTabName == null || !PKTabName.equalsIgnoreCase(target)) {
+ continue;
+ }
- for (int j = 0; j < foreignKey.getColumnMappingCount(); j++) {
- int keySeq = j + 1;
- String[] columnMapping = foreignKey.getColumnMapping(j);
- String PKColName = columnMapping[1];
- PKColName = (PKColName == null) ? "" : PKColName;
- String FKColName = columnMapping[0];
- FKColName = (FKColName == null) ? "" : FKColName;
-
- boolean usePkName = false;
- for (String pkColumn : pkColumns) {
- if (pkColumn != null && pkColumn.equalsIgnoreCase(PKColName)) {
- usePkName = true;
- break;
+ for (int j = 0; j < foreignKey.getColumnMappingCount(); j++) {
+ int keySeq = j + 1;
+ String[] columnMapping = foreignKey.getColumnMapping(j);
+ String PKColName = columnMapping[1];
+ PKColName = (PKColName == null) ? "" : PKColName;
+ String FKColName = columnMapping[0];
+ FKColName = (FKColName == null) ? "" : FKColName;
+
+ boolean usePkName = false;
+ for (String pkColumn : pkColumns) {
+ if (pkColumn != null && pkColumn.equalsIgnoreCase(PKColName)) {
+ usePkName = true;
+ break;
+ }
}
- }
- String pkName =
+ String pkName =
(usePkName && pkFinder.getName() != null) ? pkFinder.getName() : "";
- exportedKeysQuery
+ exportedKeysQuery
.append(count > 0 ? " union all select " : "select ")
.append(keySeq)
.append(" as ks, '")
@@ -1333,23 +1370,22 @@ public ResultSet getExportedKeys(String catalog, String schema, String table)
.append(RULE_MAP.get(foreignKey.getOnDelete()))
.append(" as dr, ");
- String fkName = foreignKey.getFkName();
+ String fkName = foreignKey.getFkName();
- if (fkName != null) {
- exportedKeysQuery.append("'").append(escape(fkName)).append("' as fkn");
- } else {
- exportedKeysQuery.append("'' as fkn");
- }
+ if (fkName != null) {
+ exportedKeysQuery.append("'").append(escape(fkName)).append("' as fkn");
+ } else {
+ exportedKeysQuery.append("'' as fkn");
+ }
- count++;
+ count++;
+ }
}
}
}
- }
- boolean hasImportedKey = (count > 0);
- StringBuilder sql = new StringBuilder(512);
- sql.append("select ")
+ boolean hasImportedKey = (count > 0);
+ sql.append("select ")
.append(catalog)
.append(" as PKTABLE_CAT, ")
.append(quotedSchema)
@@ -1380,12 +1416,18 @@ public ResultSet getExportedKeys(String catalog, String schema, String table)
// foreign_keys = true ?
.append(" as DEFERRABILITY ");
- if (hasImportedKey) {
- sql.append("from (")
+ if (hasImportedKey) {
+ sql.append("from (")
.append(exportedKeysQuery)
.append(") ORDER BY FKTABLE_CAT, FKTABLE_SCHEM, FKTABLE_NAME, KEY_SEQ");
- } else {
- sql.append("limit 0");
+ } else {
+ sql.append("limit 0");
+ }
+ if (i != schemasNames.size() - 1) {
+ sql.append(" union all ");
+ } else {
+ sql.append(";");
+ }
}
return ((CoreStatement) stat).executeQuery(sql.toString(), true);
@@ -1412,64 +1454,70 @@ public ResultSet getImportedKeys(String catalog, String schema, String table)
ResultSet rs;
Statement stat = conn.createStatement();
StringBuilder sql = new StringBuilder(700);
-
- sql.append("select ")
+ ResultSet schemas = getSchemas(null, escapeWildcards(schema));
+ List schemasNames = getSchemasNames(schemas);
+ for (int i = 0; i < schemasNames.size(); i++) {
+ String schemaName = schemasNames.get(i);
+ sql.append("select ")
.append(quote(catalog))
.append(" as PKTABLE_CAT, ")
- .append(quote(schema == null ? "main" : schema))
+ .append(quote(schemaName))
.append(" as PKTABLE_SCHEM, ")
.append("ptn as PKTABLE_NAME, pcn as PKCOLUMN_NAME, ")
.append(quote(catalog))
.append(" as FKTABLE_CAT, ")
- .append(quote(schema == null ? "main" : schema))
+ .append(quote(schemaName))
.append(" as FKTABLE_SCHEM, ")
.append(quote(table))
.append(" as FKTABLE_NAME, ")
.append(
- "fcn as FKCOLUMN_NAME, ks as KEY_SEQ, ur as UPDATE_RULE, dr as DELETE_RULE, fkn as FK_NAME, pkn as PK_NAME, ")
+ "fcn as FKCOLUMN_NAME, ks as KEY_SEQ, ur as UPDATE_RULE, dr as DELETE_RULE, fkn as FK_NAME, pkn as PK_NAME, ")
.append(DatabaseMetaData.importedKeyInitiallyDeferred)
.append(" as DEFERRABILITY from (");
- // Use a try catch block to avoid "query does not return ResultSet" error
- try {
- rs = stat.executeQuery("pragma foreign_key_list('" + escape(table) + "');");
- } catch (SQLException e) {
- sql = appendDummyForeignKeyList(sql);
- return ((CoreStatement) stat).executeQuery(sql.toString(), true);
- }
+ // Use a try catch block to avoid "query does not return ResultSet" error
+ try {
+ rs = stat.executeQuery("pragma foreign_key_list('" + escape(table) + "');");
+ } catch (SQLException e) {
+ appendDummyForeignKeyList(sql);
+ if (i != schemasNames.size() - 1) {
+ sql.append(" union all ");
+ }
+ continue;
+ }
- final ImportedKeyFinder impFkFinder = new ImportedKeyFinder(table, schema);
- List fkNames = impFkFinder.getFkList();
+ final ImportedKeyFinder impFkFinder = new ImportedKeyFinder(table, schemaName);
+ List fkNames = impFkFinder.getFkList();
- int i = 0;
- for (; rs.next(); i++) {
- int keySeq = rs.getInt(2) + 1;
- int keyId = rs.getInt(1);
- String PKTabName = rs.getString(3);
- String FKColName = rs.getString(4);
- String PKColName = rs.getString(5);
+ int j = 0;
+ for (; rs.next(); j++) {
+ int keySeq = rs.getInt(2) + 1;
+ int keyId = rs.getInt(1);
+ String PKTabName = rs.getString(3);
+ String FKColName = rs.getString(4);
+ String PKColName = rs.getString(5);
- String pkName = null;
- try {
- PrimaryKeyFinder pkFinder = new PrimaryKeyFinder(PKTabName, schema);
- pkName = pkFinder.getName();
- if (PKColName == null) {
- PKColName = pkFinder.getColumns()[0];
+ String pkName = null;
+ try {
+ PrimaryKeyFinder pkFinder = new PrimaryKeyFinder(PKTabName, schemaName);
+ pkName = pkFinder.getName();
+ if (PKColName == null) {
+ PKColName = pkFinder.getColumns()[0];
+ }
+ } catch (SQLException ignored) {
}
- } catch (SQLException ignored) {
- }
- String updateRule = rs.getString(6);
- String deleteRule = rs.getString(7);
+ String updateRule = rs.getString(6);
+ String deleteRule = rs.getString(7);
- if (i > 0) {
- sql.append(" union all ");
- }
+ if (j > 0) {
+ sql.append(" union all ");
+ }
- String fkName = null;
- if (fkNames.size() > keyId) fkName = fkNames.get(keyId).getFkName();
+ String fkName = null;
+ if (fkNames.size() > keyId) fkName = fkNames.get(keyId).getFkName();
- sql.append("select ")
+ sql.append("select ")
.append(keySeq)
.append(" as ks,")
.append("'")
@@ -1511,13 +1559,18 @@ public ResultSet getImportedKeys(String catalog, String schema, String table)
.append(" as fkn, ")
.append(pkName == null ? "''" : quote(pkName))
.append(" as pkn");
- }
- rs.close();
+ }
+ rs.close();
+
+ if (j == 0) {
+ appendDummyForeignKeyList(sql);
+ }
+ if (i != schemasNames.size() - 1) {
+ sql.append(") UNION ALL ");
+ } else {
+ sql.append(") ORDER BY PKTABLE_CAT, PKTABLE_SCHEM, PKTABLE_NAME, KEY_SEQ;");
+ }
- if (i == 0) {
- sql = appendDummyForeignKeyList(sql);
- } else {
- sql.append(") ORDER BY PKTABLE_CAT, PKTABLE_SCHEM, PKTABLE_NAME, KEY_SEQ;");
}
return ((CoreStatement) stat).executeQuery(sql.toString(), true);
@@ -1652,7 +1705,7 @@ public ResultSet getSuperTables(String c, String s, String t) throws SQLExceptio
if (getSuperTables == null) {
getSuperTables =
conn.prepareStatement(
- "select null as TABLE_CAT, null as TABLE_SCHEM, "
+ "select null as TABLE_CAT, s as TABLE_SCHEM, "
+ "null as TABLE_NAME, null as SUPERTABLE_NAME limit 0;");
}
return getSuperTables.executeQuery();
@@ -1696,13 +1749,26 @@ public synchronized ResultSet getTables(
String c, String s, String tblNamePattern, String[] types) throws SQLException {
checkOpen();
+ ArrayList schemasNames = getSchemasNames(getSchemas(c, s));
+ StringBuilder sql = new StringBuilder();
+ for (int i = 0; i < schemasNames.size(); i++) {
+ appendSchemaTablesRequests(sql, schemasNames.get(i), tblNamePattern, types);
+ if (i != schemasNames.size() - 1) {
+ sql.append("\nUNION ALL\n");
+ } else {
+ sql.append(" ORDER BY TABLE_TYPE, TABLE_NAME;");
+ }
+ }
+ return ((CoreStatement) conn.createStatement()).executeQuery(sql.toString(), true);
+ }
+ private StringBuilder appendSchemaTablesRequests(
+ StringBuilder sql, String s, String tblNamePattern, String[] types
+ ) {
tblNamePattern =
(tblNamePattern == null || "".equals(tblNamePattern))
? "%"
: escape(tblNamePattern);
-
- StringBuilder sql = new StringBuilder();
sql.append("SELECT").append("\n");
sql.append(" NULL AS TABLE_CAT,").append("\n");
sql.append(" ")
@@ -1762,10 +1828,7 @@ public synchronized ResultSet getTables(
.collect(Collectors.joining(",")));
sql.append(")");
}
-
- sql.append(" ORDER BY TABLE_TYPE, TABLE_NAME;");
-
- return ((CoreStatement) conn.createStatement()).executeQuery(sql.toString(), true);
+ return sql;
}
/** @see java.sql.DatabaseMetaData#getTableTypes() */
diff --git a/src/main/java/org/sqlite/jdbc4/JDBC4DatabaseMetaData.java b/src/main/java/org/sqlite/jdbc4/JDBC4DatabaseMetaData.java
index 703eb5360..5abd4f349 100644
--- a/src/main/java/org/sqlite/jdbc4/JDBC4DatabaseMetaData.java
+++ b/src/main/java/org/sqlite/jdbc4/JDBC4DatabaseMetaData.java
@@ -25,10 +25,6 @@ public RowIdLifetime getRowIdLifetime() throws SQLException {
throw new SQLFeatureNotSupportedException();
}
- public ResultSet getSchemas(String catalog, String schemaPattern) throws SQLException {
- throw new SQLFeatureNotSupportedException();
- }
-
public boolean supportsStoredFunctionsUsingCallSyntax() throws SQLException {
throw new SQLFeatureNotSupportedException();
}
diff --git a/src/test/java/org/sqlite/DBMetaDataTest.java b/src/test/java/org/sqlite/DBMetaDataTest.java
index 3d26aa0a2..759ecf96b 100644
--- a/src/test/java/org/sqlite/DBMetaDataTest.java
+++ b/src/test/java/org/sqlite/DBMetaDataTest.java
@@ -91,7 +91,10 @@ public void getTables() throws SQLException {
rs = meta.getTables(null, null, null, new String[] {"system table"});
assertThat(rs.next()).isTrue();
assertThat(rs.getString("TABLE_NAME")).isEqualTo("sqlite_schema");
- assertThat(rs.next()).isFalse();
+ assertThat(rs.getString("TABLE_SCHEM")).isEqualTo("main");
+ assertThat(rs.next()).isTrue();
+ assertThat(rs.getString("TABLE_NAME")).isEqualTo("sqlite_schema");
+ assertThat(rs.getString("TABLE_SCHEM")).isEqualTo("temp");
rs.close();
}
@@ -928,7 +931,7 @@ public void columnOrderOfgetTables() throws SQLException {
ResultSet rsTables =
meta.getTables(
null,
- null,
+ "",
null,
new String[] {"TABLE", "VIEW", "GLOBAL TEMPORARY", "SYSTEM TABLE"});
@@ -1629,19 +1632,19 @@ public void getTablesForAttachedDatabase() throws SQLException {
assertThat(rs.next()).isTrue();
rs.close();
- rs = meta.getTables(null, null, null, new String[] {"table"});
+ rs = meta.getTables(null, "main", null, new String[] {"table"});
assertThat(rs.next()).isTrue();
assertThat(rs.getString("TABLE_NAME")).isEqualTo("test");
assertThat(rs.next()).isFalse();
rs.close();
- rs = meta.getTables(null, null, null, new String[] {"view"});
+ rs = meta.getTables(null, "main", null, new String[] {"view"});
assertThat(rs.next()).isTrue();
assertThat(rs.getString("TABLE_NAME")).isEqualTo("testView");
assertThat(rs.next()).isFalse();
rs.close();
- rs = meta.getTables(null, null, null, new String[] {"system table"});
+ rs = meta.getTables(null, "main", null, new String[] {"system table"});
assertThat(rs.next()).isTrue();
assertThat(rs.getString("TABLE_NAME")).isEqualTo("sqlite_schema");
assertThat(rs.next()).isFalse();
@@ -1810,7 +1813,7 @@ public void getColumnsForAttachedDatabaseTables() throws SQLException {
@Test
public void getTablesForDefaultSchema() throws SQLException {
- ResultSet rs = meta.getTables(null, null, null, new String[] {"table"});
+ ResultSet rs = meta.getTables(null, "", null, new String[] {"table"});
assertThat(rs.next()).isTrue();
assertThat(rs.getString("TABLE_NAME")).isEqualTo("test");
assertThat(rs.getString("TABLE_SCHEM")).isEqualTo("main");
From 6e6eea89705f1211054c3021374b5ff1e414d5c8 Mon Sep 17 00:00:00 2001
From: Georgii Gvinepadze
Date: Mon, 13 Feb 2023 23:09:21 +0400
Subject: [PATCH 06/10] update getColumns to support patterns
---
.../sqlite/jdbc3/JDBC3DatabaseMetaData.java | 46 +++++++++++++------
src/test/java/org/sqlite/DBMetaDataTest.java | 6 +--
2 files changed, 36 insertions(+), 16 deletions(-)
diff --git a/src/main/java/org/sqlite/jdbc3/JDBC3DatabaseMetaData.java b/src/main/java/org/sqlite/jdbc3/JDBC3DatabaseMetaData.java
index f04cd2bc5..233270fca 100644
--- a/src/main/java/org/sqlite/jdbc3/JDBC3DatabaseMetaData.java
+++ b/src/main/java/org/sqlite/jdbc3/JDBC3DatabaseMetaData.java
@@ -913,8 +913,28 @@ public ResultSet getColumns(String c, String s, String tblNamePattern, String co
// empty string --- if it cannot be determined whether the column is auto incremented
// parameter is unknown
checkOpen();
-
+ ResultSet schemas = getSchemas(c, s);
+ ArrayList schemasNames = getSchemasNames(schemas);
StringBuilder sql = new StringBuilder(700);
+ sql.append("SELECT * FROM (");
+ for (int i = 0; i < schemasNames.size(); i++) {
+ appendGetSchemaColumns(sql ,c, schemasNames.get(i), tblNamePattern, colNamePattern);
+ if (i != schemasNames.size() - 1) {
+ sql.append(" UNION ALL ");
+ } else {
+ sql.append("\n) order by TABLE_SCHEM, TABLE_NAME, ORDINAL_POSITION;");
+ }
+ }
+
+ Statement stat = conn.createStatement();
+ return ((CoreStatement) stat).executeQuery(sql.toString(), true);
+ }
+
+ private StringBuilder appendGetSchemaColumns(
+ StringBuilder sql, String c, String s, String tblNamePattern, String colNamePattern
+ )
+ throws SQLException {
+
sql.append("select null as TABLE_CAT, ")
.append(quote(s == null ? "main" : s))
.append(" as TABLE_SCHEM, tblname as TABLE_NAME, ")
@@ -953,8 +973,7 @@ public ResultSet getColumns(String c, String s, String tblNamePattern, String co
rsColAutoinc =
statColAutoinc.executeQuery(
"SELECT LIKE('%autoincrement%', LOWER(sql)) FROM "
- + prependSchemaPrefix(
- s, "sqlite_master WHERE LOWER(name) = LOWER('")
+ + prependSchemaPrefix(s, "sqlite_master WHERE LOWER(name) = LOWER('")
+ escape(tableName)
+ "') AND TYPE IN ('table', 'view')");
rsColAutoinc.next();
@@ -979,8 +998,7 @@ public ResultSet getColumns(String c, String s, String tblNamePattern, String co
// For each table, get the column info and build into overall SQL
String pragmaStatement =
"PRAGMA "
- + prependSchemaPrefix(
- s, "table_xinfo('" + escape(tableName) + "')");
+ + prependSchemaPrefix(s, "table_xinfo('" + escape(tableName) + "')");
try (Statement colstat = conn.createStatement();
ResultSet rscol = colstat.executeQuery(pragmaStatement)) {
@@ -1130,14 +1148,14 @@ public ResultSet getColumns(String c, String s, String tblNamePattern, String co
}
if (colFound) {
- sql.append(") order by TABLE_SCHEM, TABLE_NAME, ORDINAL_POSITION;");
+ sql.append(") ");
} else {
sql.append(
- "select null as ordpos, null as colnullable, null as ct, null as colsize, null as colDecimalDigits, null as tblname, null as cn, null as tn, null as colDefault, null as colautoincrement, null as colgenerated) limit 0;");
+ "select null as ordpos, null as colnullable, null as ct, null as colsize, null as "
+ + "colDecimalDigits, null as tblname, null as cn, null as tn, null as colDefault, null as "
+ + "colautoincrement, null as colgenerated limit 0)");
}
-
- Statement stat = conn.createStatement();
- return ((CoreStatement) stat).executeQuery(sql.toString(), true);
+ return sql;
}
/**
@@ -1224,6 +1242,8 @@ public ResultSet getPrimaryKeys(String c, String s, String table) throws SQLExce
createSchemaPrimaryKeysQuery(schemaNames.get(i), table, sql);
if (i != schemaNames.size() - 1) {
sql.append(" union all ");
+ } else {
+ sql.append(" order by cn;");
}
}
return ((CoreStatement) stat).executeQuery(sql.toString(), true);
@@ -1266,7 +1286,7 @@ private void createSchemaPrimaryKeysQuery(String s, String table, StringBuilder
.append(i + 1)
.append(" as ks");
}
- sql.append(") order by cn;");
+ sql.append(") ");
}
private static final Map RULE_MAP = new HashMap<>();
@@ -1772,7 +1792,7 @@ private StringBuilder appendSchemaTablesRequests(
sql.append("SELECT").append("\n");
sql.append(" NULL AS TABLE_CAT,").append("\n");
sql.append(" ")
- .append(quote(s == null ? "main" : s))
+ .append(quote(s))
.append(" AS TABLE_SCHEM,")
.append("\n");
sql.append(" NAME AS TABLE_NAME,").append("\n");
@@ -1787,7 +1807,7 @@ private StringBuilder appendSchemaTablesRequests(
sql.append(" (").append("\n");
sql.append(" SELECT\n");
sql.append(" 'sqlite_schema' AS NAME,\n");
- sql.append(" 'SYSTEM TABLE' AS TYPE");
+ sql.append(" 'SYSTEM TABLE' AS TYPE\n");
sql.append(" UNION ALL").append("\n");
sql.append(" SELECT").append("\n");
sql.append(" NAME,").append("\n");
diff --git a/src/test/java/org/sqlite/DBMetaDataTest.java b/src/test/java/org/sqlite/DBMetaDataTest.java
index 759ecf96b..b76b0168e 100644
--- a/src/test/java/org/sqlite/DBMetaDataTest.java
+++ b/src/test/java/org/sqlite/DBMetaDataTest.java
@@ -92,9 +92,9 @@ public void getTables() throws SQLException {
assertThat(rs.next()).isTrue();
assertThat(rs.getString("TABLE_NAME")).isEqualTo("sqlite_schema");
assertThat(rs.getString("TABLE_SCHEM")).isEqualTo("main");
- assertThat(rs.next()).isTrue();
- assertThat(rs.getString("TABLE_NAME")).isEqualTo("sqlite_schema");
- assertThat(rs.getString("TABLE_SCHEM")).isEqualTo("temp");
+// assertThat(rs.next()).isTrue();
+// assertThat(rs.getString("TABLE_NAME")).isEqualTo("sqlite_schema");
+// assertThat(rs.getString("TABLE_SCHEM")).isEqualTo("temp");
rs.close();
}
From b0eab66f48d00422a99da49ece44aece8dd5e687 Mon Sep 17 00:00:00 2001
From: Georgii Gvinepadze
Date: Tue, 14 Feb 2023 15:38:21 +0400
Subject: [PATCH 07/10] add handling when schema is not found
---
.../sqlite/jdbc3/JDBC3DatabaseMetaData.java | 215 +++++++++++++-----
src/test/java/org/sqlite/DBMetaDataTest.java | 116 ++++++++++
2 files changed, 270 insertions(+), 61 deletions(-)
diff --git a/src/main/java/org/sqlite/jdbc3/JDBC3DatabaseMetaData.java b/src/main/java/org/sqlite/jdbc3/JDBC3DatabaseMetaData.java
index 233270fca..21bf914b6 100644
--- a/src/main/java/org/sqlite/jdbc3/JDBC3DatabaseMetaData.java
+++ b/src/main/java/org/sqlite/jdbc3/JDBC3DatabaseMetaData.java
@@ -916,8 +916,10 @@ public ResultSet getColumns(String c, String s, String tblNamePattern, String co
ResultSet schemas = getSchemas(c, s);
ArrayList schemasNames = getSchemasNames(schemas);
StringBuilder sql = new StringBuilder(700);
- sql.append("SELECT * FROM (");
for (int i = 0; i < schemasNames.size(); i++) {
+ if (i == 0) {
+ sql.append("SELECT * FROM (");
+ }
appendGetSchemaColumns(sql ,c, schemasNames.get(i), tblNamePattern, colNamePattern);
if (i != schemasNames.size() - 1) {
sql.append(" UNION ALL ");
@@ -925,7 +927,34 @@ public ResultSet getColumns(String c, String s, String tblNamePattern, String co
sql.append("\n) order by TABLE_SCHEM, TABLE_NAME, ORDINAL_POSITION;");
}
}
-
+ if (schemasNames.size() == 0) {
+ sql.append("select ");
+ sql.append("\tnull as TABLE_CAT,\n")
+ .append("\tnull as TABLE_SCHEM,\n")
+ .append("\tnull as TABLE_NAME,\n")
+ .append("\tnull as COLUMN_NAME,\n")
+ .append("\tnull as DATA_TYPE,\n")
+ .append("\tnull as TYPE_NAME,\n")
+ .append("\tnull as COLUMN_SIZE,\n")
+ .append("\tnull as BUFFER_LENGTH,\n")
+ .append("\tnull as DECIMAL_DIGITS,\n")
+ .append("\tnull as NUM_PREC_RADIX,\n")
+ .append("\tnull as NULLABLE,\n")
+ .append("\tnull as REMARKS,\n")
+ .append("\tnull as COLUMN_DEF,\n")
+ .append("\tnull as SQL_DATA_TYPE,\n")
+ .append("\tnull as SQL_DATETIME_SUB,\n")
+ .append("\tnull as CHAR_OCTET_LENGTH,\n")
+ .append("\tnull as ORDINAL_POSITION,\n")
+ .append("\tnull as IS_NULLABLE,\n")
+ .append("\tnull as SCOPE_CATLOG,\n")
+ .append("\tnull as SCOPE_SCHEMA,\n")
+ .append("\tnull as SCOPE_TABLE,\n")
+ .append("\tnull as SOURCE_DATA_TYPE,\n")
+ .append("\tnull as IS_AUTOINCREMENT,\n")
+ .append("\tnull as IS_GENERATEDCOLUMN\n")
+ .append("\t limit 0");
+ }
Statement stat = conn.createStatement();
return ((CoreStatement) stat).executeQuery(sql.toString(), true);
}
@@ -1246,6 +1275,14 @@ public ResultSet getPrimaryKeys(String c, String s, String table) throws SQLExce
sql.append(" order by cn;");
}
}
+ if (schemaNames.size() == 0) {
+ sql.append("select null as TABLE_CAT, ")
+ .append("null")
+ .append(" as TABLE_SCHEM, '")
+ .append("null")
+ .append("' as TABLE_NAME, null as COLUMN_NAME, null as KEY_SEQ, null as PK_NAME limit 0;");
+ }
+
return ((CoreStatement) stat).executeQuery(sql.toString(), true);
}
@@ -1449,7 +1486,24 @@ public ResultSet getExportedKeys(String catalog, String schema, String table)
sql.append(";");
}
}
-
+ if (schemasNames.size() == 0) {
+ sql.append("select\n")
+ .append("\tnull as PKTABLE_CAT,\n")
+ .append("\tnull as PKTABLE_SCHEM,\n")
+ .append("\tnull as PKTABLE_NAME,\n")
+ .append("\tnull as PKCOLUMN_NAME,\n")
+ .append("\tnull as FKTABLE_CAT,\n")
+ .append("\tnull as FKTABLE_SCHEM,\n")
+ .append("\t'null' as FKTABLE_NAME,\n")
+ .append("\tnull as FKCOLUMN_NAME,\n")
+ .append("\tnull as KEY_SEQ,\n")
+ .append("\tnull as UPDATE_RULE,\n")
+ .append("\tnull as DELETE_RULE,\n")
+ .append("\tnull as FK_NAME,\n")
+ .append("\tnull as PK_NAME,\n")
+ .append("\tnull as DEFERRABILITY\n")
+ .append("limit 0;");
+ }
return ((CoreStatement) stat).executeQuery(sql.toString(), true);
}
@@ -1461,7 +1515,7 @@ private StringBuilder appendDummyForeignKeyList(StringBuilder sql) {
.append(" as dr, ")
.append(" '' as fkn, ")
.append(" '' as pkn ")
- .append(") limit 0;");
+ .append(" limit 0");
return sql;
}
@@ -1502,6 +1556,8 @@ public ResultSet getImportedKeys(String catalog, String schema, String table)
appendDummyForeignKeyList(sql);
if (i != schemasNames.size() - 1) {
sql.append(" union all ");
+ } else {
+ sql.append(";");
}
continue;
}
@@ -1590,6 +1646,23 @@ public ResultSet getImportedKeys(String catalog, String schema, String table)
} else {
sql.append(") ORDER BY PKTABLE_CAT, PKTABLE_SCHEM, PKTABLE_NAME, KEY_SEQ;");
}
+ }
+ if (schemasNames.size() == 0) {
+ sql.append("select\n")
+ .append("\tnull as PKTABLE_CAT,\n")
+ .append("\tnull as PKTABLE_SCHEM,\n")
+ .append("\tnull as PKTABLE_NAME,\n")
+ .append("\tnull as PKCOLUMN_NAME,\n")
+ .append("\tnull as FKTABLE_CAT,\n")
+ .append("\tnull as FKTABLE_SCHEM,\n")
+ .append("\tnull as FKTABLE_NAME,\n")
+ .append("\tnull as FKCOLUMN_NAME,\n")
+ .append("\tnull as KEY_SEQ,\n")
+ .append("\tnull as UPDATE_RULE,\n")
+ .append("\tnull as DELETE_RULE,\n")
+ .append("\tnull as FK_NAME,\n")
+ .append("\tnull as PK_NAME,\n")
+ .append("\tnull as DEFERRABILITY limit 0;");
}
@@ -1602,86 +1675,91 @@ public ResultSet getImportedKeys(String catalog, String schema, String table)
*/
public ResultSet getIndexInfo(String c, String s, String table, boolean u, boolean approximate)
throws SQLException {
- ResultSet rs;
Statement stat = conn.createStatement();
- StringBuilder sql = new StringBuilder(500);
-
- // define the column header
- // this is from the JDBC spec, it is part of the driver protocol
- sql.append("select null as TABLE_CAT,")
- .append(quote(s == null ? "main" : s))
- .append(" as TABLE_SCHEM, '")
+ ResultSet schemas = getSchemas(c, escapeWildcards(s));
+ ArrayList schemasNames = getSchemasNames(schemas);
+ StringBuilder sql = new StringBuilder(1000);
+ for (int i = 0; i < schemasNames.size(); i++) {
+ ResultSet rs;
+ // define the column header
+ // this is from the JDBC spec, it is part of the driver protocol
+ sql.append("select null as TABLE_CAT,'")
+ .append(escape(schemasNames.get(i)))
+ .append("' as TABLE_SCHEM, '")
.append(escape(table))
- .append(
- "' as TABLE_NAME, un as NON_UNIQUE, null as INDEX_QUALIFIER, n as INDEX_NAME, ")
+ .append("' as TABLE_NAME, un as NON_UNIQUE, null as INDEX_QUALIFIER, n as INDEX_NAME, ")
.append(Integer.toString(DatabaseMetaData.tableIndexOther))
.append(" as TYPE, op as ORDINAL_POSITION, ")
.append(
- "cn as COLUMN_NAME, null as ASC_OR_DESC, 0 as CARDINALITY, 0 as PAGES, null as FILTER_CONDITION from (");
-
- // this always returns a result set now, previously threw exception
- rs =
- stat.executeQuery(
- "pragma " + prependSchemaPrefix(s, "index_list('" + escape(table) + "');"));
-
- ArrayList> indexList = new ArrayList<>();
- while (rs.next()) {
- indexList.add(new ArrayList<>());
- indexList.get(indexList.size() - 1).add(rs.getString(2));
- indexList.get(indexList.size() - 1).add(rs.getInt(3));
- }
- rs.close();
- if (indexList.size() == 0) {
- // if pragma index_list() returns no information, use this null block
- sql.append("select null as un, null as n, null as op, null as cn) limit 0;");
- return ((CoreStatement) stat).executeQuery(sql.toString(), true);
- } else {
- // loop over results from pragma call, getting specific info for each index
+ "cn as COLUMN_NAME, null as ASC_OR_DESC, 0 as CARDINALITY, 0 as PAGES, null as FILTER_CONDITION from (");
- Iterator> indexIterator = indexList.iterator();
- ArrayList