Merge 'bindings/java: Implement JDBC4DatabaseMetadata getTables ' from Kim Seon Woo

## Purpose
Implement `getTables` which is used to extract metadata about the
database
## Changes
- Implement `JDBC4DatabaseMetaData's` `getTables` method
- Extract `JDBC4ResultSet` as field in `JDBC4PreparedStatement`
## Related Issue
https://github.com/tursodatabase/limbo/issues/615

Closes #1687
This commit is contained in:
Pekka Enberg
2025-06-09 10:46:18 +03:00
5 changed files with 204 additions and 11 deletions

View File

@@ -119,8 +119,17 @@ public final class LimboResultSet {
this.open = false;
}
// Note that columnIndex starts from 1
@Nullable
public Object get(String columnName) throws SQLException {
final int columnsLength = this.columnNames.length;
for (int i = 0; i < columnsLength; i++) {
if (this.columnNames[i].equals(columnName)) {
return get(i + 1);
}
}
throw new SQLException("column name " + columnName + " not found");
}
public Object get(int columnIndex) throws SQLException {
if (!this.isOpen()) {
throw new SQLException("ResultSet is not open");

View File

@@ -687,16 +687,85 @@ public final class JDBC4DatabaseMetaData implements DatabaseMetaData {
return null;
}
// TODO: make use of getSearchStringEscape
@Override
@SkipNullableCheck
public ResultSet getTables(
@Nullable String catalog,
@Nullable String schemaPattern,
String tableNamePattern,
@Nullable String[] types)
throws SQLException {
// TODO: after union is supported
return null;
// SQLite doesn't support catalogs or schemas — reject if non-empty values provided
if (catalog != null && !catalog.isEmpty()) {
return connection.prepareStatement("SELECT * FROM sqlite_schema WHERE 1=0").executeQuery();
}
if (schemaPattern != null && !schemaPattern.isEmpty()) {
return connection.prepareStatement("SELECT * FROM sqlite_schema WHERE 1=0").executeQuery();
}
// Start building query
StringBuilder sql =
new StringBuilder(
"SELECT "
+ "NULL AS TABLE_CAT, "
+ "NULL AS TABLE_SCHEM, "
+ "name AS TABLE_NAME, "
+ "CASE type "
+ " WHEN 'table' THEN 'TABLE' "
+ " WHEN 'view' THEN 'VIEW' "
+ " ELSE UPPER(type) "
+ "END AS TABLE_TYPE, "
+ "NULL AS REMARKS, "
+ "NULL AS TYPE_CAT, "
+ "NULL AS TYPE_SCHEM, "
+ "NULL AS TYPE_NAME, "
+ "NULL AS SELF_REFERENCING_COL_NAME, "
+ "NULL AS REF_GENERATION "
+ "FROM sqlite_schema "
+ "WHERE 1=1");
// Apply type filtering if needed
if (types != null && types.length > 0) {
sql.append(" AND type IN (");
for (int i = 0; i < types.length; i++) {
if (i > 0) sql.append(", ");
sql.append("?");
}
sql.append(")");
}
// Apply table name pattern filtering
if (tableNamePattern != null) {
sql.append(" AND name LIKE ?");
}
// Comply with spec: sort by TABLE_TYPE, TABLE_CAT, TABLE_SCHEM, TABLE_NAME
sql.append(" ORDER BY TABLE_TYPE, TABLE_CAT, TABLE_SCHEM, TABLE_NAME");
// Prepare and bind statement
PreparedStatement stmt = connection.prepareStatement(sql.toString());
int paramIndex = 1;
if (types != null && types.length > 0) {
for (String type : types) {
String sqliteType;
if ("TABLE".equalsIgnoreCase(type)) {
sqliteType = "table";
} else if ("VIEW".equalsIgnoreCase(type)) {
sqliteType = "view";
} else {
sqliteType = type.toLowerCase();
}
stmt.setString(paramIndex++, sqliteType);
}
}
if (tableNamePattern != null) {
stmt.setString(paramIndex, tableNamePattern);
}
return stmt.executeQuery();
}
@Override

View File

@@ -28,6 +28,7 @@ import tech.turso.core.LimboResultSet;
public final class JDBC4PreparedStatement extends JDBC4Statement implements PreparedStatement {
private final String sql;
private final JDBC4ResultSet resultSet;
public JDBC4PreparedStatement(JDBC4Connection connection, String sql) throws SQLException {
super(connection);
@@ -35,13 +36,13 @@ public final class JDBC4PreparedStatement extends JDBC4Statement implements Prep
this.sql = sql;
this.statement = connection.prepare(sql);
this.statement.initializeColumnMetadata();
this.resultSet = new JDBC4ResultSet(this.statement.getResultSet());
}
@Override
public ResultSet executeQuery() throws SQLException {
// TODO: check bindings etc
requireNonNull(this.statement);
return new JDBC4ResultSet(this.statement.getResultSet());
return this.resultSet;
}
@Override
@@ -202,9 +203,8 @@ public final class JDBC4PreparedStatement extends JDBC4Statement implements Prep
}
@Override
@SkipNullableCheck
public ResultSetMetaData getMetaData() throws SQLException {
return null;
return this.resultSet;
}
@Override

View File

@@ -190,8 +190,12 @@ public final class JDBC4ResultSet implements ResultSet, ResultSetMetaData {
@Override
public String getString(String columnLabel) throws SQLException {
// TODO
return "";
final Object result = this.resultSet.get(columnLabel);
if (result == null) {
return "";
}
return wrapTypeConversion(() -> (String) result);
}
@Override