Skip to content
Open
29 changes: 29 additions & 0 deletions src/main/java/org/sqlite/core/CoreDatabaseMetaData.java
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down
115 changes: 65 additions & 50 deletions src/main/java/org/sqlite/jdbc3/JDBC3DatabaseMetaData.java
Original file line number Diff line number Diff line change
Expand Up @@ -681,7 +681,7 @@ public boolean supportsSavepoints() {

/** @see java.sql.DatabaseMetaData#supportsSchemasInDataManipulation() */
public boolean supportsSchemasInDataManipulation() {
return false;
return true;
}

/** @see java.sql.DatabaseMetaData#supportsSchemasInIndexDefinitions() */
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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 {
Expand All @@ -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)) {

Expand Down Expand Up @@ -1173,9 +1172,8 @@ 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;");
getSchemas = conn.prepareStatement(
"select name as TABLE_SCHEM, null as TABLE_CATALOG from pragma_database_list;");
}

return getSchemas.executeQuery();
Expand All @@ -1195,12 +1193,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 (");

Expand Down Expand Up @@ -1245,12 +1243,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);

Expand All @@ -1259,8 +1258,11 @@ public ResultSet getExportedKeys(String catalog, String schema, String table)
if (pkColumns != null) {
// retrieve table list
ArrayList<String> 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()) {
Expand All @@ -1276,7 +1278,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<ForeignKey> fkNames = impFkFinder.getFkList();

for (ForeignKey foreignKey : fkNames) {
Expand Down Expand Up @@ -1340,15 +1342,15 @@ 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, ")
.append(hasImportedKey ? "pcn" : "''")
.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, ")
Expand Down Expand Up @@ -1426,7 +1428,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<ForeignKey> fkNames = impFkFinder.getFkList();

int i = 0;
Expand All @@ -1439,7 +1441,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];
Expand Down Expand Up @@ -1523,7 +1525,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, ")
Expand All @@ -1533,7 +1537,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<ArrayList<Object>> indexList = new ArrayList<>();
while (rs.next()) {
Expand All @@ -1557,7 +1561,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()) {

Expand Down Expand Up @@ -1704,7 +1708,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");
Expand All @@ -1719,7 +1724,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");
Expand Down Expand Up @@ -1962,6 +1968,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;

Expand All @@ -1971,15 +1978,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 + "'");
}
Expand All @@ -1988,10 +2000,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 + "'");

Expand All @@ -2007,8 +2019,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)};
}
Expand Down Expand Up @@ -2046,21 +2060,26 @@ class ImportedKeyFinder {
private final List<ForeignKey> 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 + "'");
}

this.fkTableName = table;

List<String> fkNames = getForeignKeyNames(this.fkTableName);
List<String> 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;
Expand Down Expand Up @@ -2097,19 +2116,15 @@ public ImportedKeyFinder(String table) throws SQLException {
}
}

private List<String> getForeignKeyNames(String tbl) throws SQLException {
private List<String> getForeignKeyNames(String tbl, String schema) throws SQLException {
List<String> 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));

Expand Down
Loading