diff --git a/guard/jsqltranspiler/src/main/java/org/eclipse/daanse/sql/guard/jsqltranspiler/DeparserColumResolver.java b/guard/jsqltranspiler/src/main/java/org/eclipse/daanse/sql/guard/jsqltranspiler/DeparserColumResolver.java index 5a842f7..eb444d3 100644 --- a/guard/jsqltranspiler/src/main/java/org/eclipse/daanse/sql/guard/jsqltranspiler/DeparserColumResolver.java +++ b/guard/jsqltranspiler/src/main/java/org/eclipse/daanse/sql/guard/jsqltranspiler/DeparserColumResolver.java @@ -21,9 +21,11 @@ import ai.starlake.transpiler.schema.JdbcResultSetMetaData; import net.sf.jsqlparser.JSQLParserException; import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import net.sf.jsqlparser.schema.Table; import net.sf.jsqlparser.statement.Statement; import net.sf.jsqlparser.statement.select.Select; import net.sf.jsqlparser.statement.select.SelectVisitor; +import net.sf.jsqlparser.util.TablesNamesFinder; public class DeparserColumResolver extends JSQLColumResolver { @@ -44,9 +46,29 @@ public String getResolvedStatementText(String sqlStr) throws JSQLParserException if (st instanceof Select) { Select select = (Select) st; select.accept((SelectVisitor) this, JdbcMetaData.copyOf(metaData)); + stripCurrentCatalogQualifier(select); } return dialectDeparser.deparse(st, dialect); } + // The resolver only ever fills in a catalog qualifier that is missing; it never clears one that + // was already explicit in the query, even when it names the current catalog. The database + // connection is already scoped to that catalog, so only the schema-qualified name may be sent. + private void stripCurrentCatalogQualifier(Select select) { + String currentCatalogName = metaData.getCurrentCatalogName(); + if (currentCatalogName == null || currentCatalogName.isEmpty()) { + return; + } + new TablesNamesFinder() { + @Override + public Void visit(Table table, S context) { + if (currentCatalogName.equalsIgnoreCase(table.getUnquotedDatabaseName())) { + table.setDatabaseName(null); + } + return super.visit(table, context); + } + }.getTables((Statement) select); + } + } diff --git a/guard/jsqltranspiler/src/test/java/org/eclipse/daanse/sql/guard/jsqltranspiler/integration/SqlGuardTest.java b/guard/jsqltranspiler/src/test/java/org/eclipse/daanse/sql/guard/jsqltranspiler/integration/SqlGuardTest.java index 215e244..71a3812 100644 --- a/guard/jsqltranspiler/src/test/java/org/eclipse/daanse/sql/guard/jsqltranspiler/integration/SqlGuardTest.java +++ b/guard/jsqltranspiler/src/test/java/org/eclipse/daanse/sql/guard/jsqltranspiler/integration/SqlGuardTest.java @@ -377,6 +377,12 @@ WHERE foo.id IN (SELECT fooFact.id FROM fooFact) private static Dialect dialect; + private static final String CAT = "FoodMart"; + + private static final String SELECT_FROM_QUALIFIED_FOO = "select * from sch.foo"; + + private static final String SELECT_FROM_CATALOG_QUALIFIED_FOO = "select * from FoodMart.sch.foo"; + @BeforeAll public static void setUp() { dialect = new H2Dialect(); @@ -1410,6 +1416,45 @@ void testValidOrderBy(@InjectService SqlGuardFactory sqlGuardFactory) throws Exc } } + @Nested + @DisplayName("current catalog and schema") + class CurrentCatalogAndSchema { + + @Test + void aBareNameResolvesInTheCurrentSchema(@InjectService SqlGuardFactory sqlGuardFactory) throws Exception { + SqlGuard guard = sqlGuardFactory.create(CAT, SCH, schemaWithTwoTableTwoCol(), List.of(), dialect); + assertThat(guard.guard(SELECT_FROM_FOO)).isEqualTo(SELECT_FROM_FOO_RESULT); + } + + @Test + void aSchemaQualifiedNameResolvesTheSameWay(@InjectService SqlGuardFactory sqlGuardFactory) throws Exception { + SqlGuard guard = sqlGuardFactory.create(CAT, SCH, schemaWithTwoTableTwoCol(), List.of(), dialect); + assertThat(guard.guard(SELECT_FROM_QUALIFIED_FOO)).isEqualTo(SELECT_FROM_FOO_RESULT); + } + + @Test + void aCatalogQualifiedNameResolvesWhenTheCatalogIsTheCurrentOne(@InjectService SqlGuardFactory sqlGuardFactory) + throws Exception { + SqlGuard guard = sqlGuardFactory.create(CAT, SCH, schemaWithTwoTableTwoCol(), List.of(), dialect); + String emitted = guard.guard(SELECT_FROM_CATALOG_QUALIFIED_FOO); + // Settles ยง4.3: the database must receive the two-part name. + assertThat(emitted).isEqualTo(SELECT_FROM_FOO_RESULT); + } + + @Test + void aBareNameWithNoCurrentSchemaIsReportedAsUnknown(@InjectService SqlGuardFactory sqlGuardFactory) { + SqlGuard guard = sqlGuardFactory.create(CAT, "", schemaWithTwoTableTwoCol(), List.of(), dialect); + assertThatThrownBy(() -> guard.guard(SELECT_FROM_FOO)).isInstanceOf(UnresolvableObjectsGuardException.class); + } + + @Test + void aForeignCatalogIsRefused(@InjectService SqlGuardFactory sqlGuardFactory) { + SqlGuard guard = sqlGuardFactory.create(CAT, SCH, schemaWithTwoTableTwoCol(), List.of(), dialect); + assertThatThrownBy(() -> guard.guard("select * from Other.sch.foo")) + .isInstanceOf(UnresolvableObjectsGuardException.class); + } + } + private static void assertNotNull(Object obj) { if (obj == null) { throw new AssertionError("Expected non-null value");