This is an automated email from the ASF dual-hosted git repository.

damccorm pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/beam.git


The following commit(s) were added to refs/heads/master by this push:
     new 6c083df5f8e SQL Database DDL & Usability Improvements (#38952)
6c083df5f8e is described below

commit 6c083df5f8ed39b46aa02e2c90594cc3a7599a21
Author: Danny McCormick <[email protected]>
AuthorDate: Fri Jun 12 17:02:12 2026 -0400

    SQL Database DDL & Usability Improvements (#38952)
    
    * SQL Database DDL & Usability Improvements: Support CREATE/DROP DATABASE 
and USE short syntax
    
    * Address code review feedback: add guard in InMemoryCatalog.dropDatabase 
and add tests for DROP DATABASE IF EXISTS
    
    * Address new code review feedback: optimize InMemoryCatalog, add null 
check in SqlDdlNodes, and make tests more precise
---
 .../sql/src/main/codegen/includes/parserImpls.ftl  |  4 +-
 .../extensions/sql/impl/parser/SqlDdlNodes.java    |  7 ++-
 .../sql/meta/catalog/InMemoryCatalog.java          | 12 +++--
 .../sdk/extensions/sql/BeamSqlCliDatabaseTest.java | 52 ++++++++++++++++++++++
 4 files changed, 68 insertions(+), 7 deletions(-)

diff --git a/sdks/java/extensions/sql/src/main/codegen/includes/parserImpls.ftl 
b/sdks/java/extensions/sql/src/main/codegen/includes/parserImpls.ftl
index 94c0161c492..cb8eec43872 100644
--- a/sdks/java/extensions/sql/src/main/codegen/includes/parserImpls.ftl
+++ b/sdks/java/extensions/sql/src/main/codegen/includes/parserImpls.ftl
@@ -381,7 +381,7 @@ SqlCreate SqlCreateDatabase(Span s, boolean replace) :
 }
 
 /**
- * USE DATABASE ( catalog_name '.' )? database_name
+ * USE [ DATABASE ] ( catalog_name '.' )? database_name
  */
 SqlCall SqlUseDatabase(Span s, String scope) :
 {
@@ -391,7 +391,7 @@ SqlCall SqlUseDatabase(Span s, String scope) :
     <USE> {
         s.add(this);
     }
-    <DATABASE>
+    [ <DATABASE> ]
     databaseName = CompoundIdentifier()
     {
         return new SqlUseDatabase(
diff --git 
a/sdks/java/extensions/sql/src/main/java/org/apache/beam/sdk/extensions/sql/impl/parser/SqlDdlNodes.java
 
b/sdks/java/extensions/sql/src/main/java/org/apache/beam/sdk/extensions/sql/impl/parser/SqlDdlNodes.java
index 6d6be5d5a12..f8d7e6f7385 100644
--- 
a/sdks/java/extensions/sql/src/main/java/org/apache/beam/sdk/extensions/sql/impl/parser/SqlDdlNodes.java
+++ 
b/sdks/java/extensions/sql/src/main/java/org/apache/beam/sdk/extensions/sql/impl/parser/SqlDdlNodes.java
@@ -55,7 +55,7 @@ public class SqlDdlNodes {
   }
 
   /** Returns the schema in which to create an object. */
-  static Pair<CalciteSchema, String> schema(
+  public static Pair<CalciteSchema, String> schema(
       CalcitePrepare.Context context, boolean mutable, SqlIdentifier id) {
     CalciteSchema rootSchema = mutable ? context.getMutableRootSchema() : 
context.getRootSchema();
     @Nullable CalciteSchema schema = null;
@@ -72,7 +72,10 @@ public class SqlDdlNodes {
     return Pair.of(checkStateNotNull(schema, "Got null sub-schema for path 
'%s'", path), name(id));
   }
 
-  private static @Nullable CalciteSchema childSchema(CalciteSchema rootSchema, 
List<String> path) {
+  public static @Nullable CalciteSchema childSchema(CalciteSchema rootSchema, 
List<String> path) {
+    if (path == null) {
+      return null;
+    }
     @Nullable CalciteSchema schema = rootSchema;
     for (String p : path) {
       if (schema == null) {
diff --git 
a/sdks/java/extensions/sql/src/main/java/org/apache/beam/sdk/extensions/sql/meta/catalog/InMemoryCatalog.java
 
b/sdks/java/extensions/sql/src/main/java/org/apache/beam/sdk/extensions/sql/meta/catalog/InMemoryCatalog.java
index cdee6c93022..68e80c2340f 100644
--- 
a/sdks/java/extensions/sql/src/main/java/org/apache/beam/sdk/extensions/sql/meta/catalog/InMemoryCatalog.java
+++ 
b/sdks/java/extensions/sql/src/main/java/org/apache/beam/sdk/extensions/sql/meta/catalog/InMemoryCatalog.java
@@ -18,7 +18,6 @@
 package org.apache.beam.sdk.extensions.sql.meta.catalog;
 
 import static 
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Preconditions.checkArgument;
-import static 
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Preconditions.checkState;
 
 import java.util.Collection;
 import java.util.Collections;
@@ -111,13 +110,20 @@ public class InMemoryCatalog implements Catalog {
 
   @Override
   public boolean dropDatabase(String database, boolean cascade) {
-    checkState(!cascade, "%s does not support CASCADE.", 
getClass().getSimpleName());
+    MetaStore metaStore = metaStores.get(database);
+    if (!cascade && metaStore != null && !metaStore.getTables().isEmpty()) {
+      throw new IllegalStateException("Database '" + database + "' is not 
empty.");
+    }
 
     boolean removed = databases.remove(database);
+    if (!removed) {
+      return false;
+    }
     if (database.equals(currentDatabase)) {
       currentDatabase = null;
     }
-    return removed;
+    metaStores.remove(database);
+    return true;
   }
 
   @Override
diff --git 
a/sdks/java/extensions/sql/src/test/java/org/apache/beam/sdk/extensions/sql/BeamSqlCliDatabaseTest.java
 
b/sdks/java/extensions/sql/src/test/java/org/apache/beam/sdk/extensions/sql/BeamSqlCliDatabaseTest.java
index 588caa78a2b..54682911fe1 100644
--- 
a/sdks/java/extensions/sql/src/test/java/org/apache/beam/sdk/extensions/sql/BeamSqlCliDatabaseTest.java
+++ 
b/sdks/java/extensions/sql/src/test/java/org/apache/beam/sdk/extensions/sql/BeamSqlCliDatabaseTest.java
@@ -83,6 +83,15 @@ public class BeamSqlCliDatabaseTest {
     assertEquals("my_database2", 
catalogManager.currentCatalog().currentDatabase());
   }
 
+  @Test
+  public void testUseDatabaseWithoutDatabaseKeyword() {
+    assertEquals(DEFAULT, catalogManager.currentCatalog().currentDatabase());
+    cli.execute("CREATE DATABASE my_database");
+    assertEquals(DEFAULT, catalogManager.currentCatalog().currentDatabase());
+    cli.execute("USE my_database");
+    assertEquals("my_database", 
catalogManager.currentCatalog().currentDatabase());
+  }
+
   @Test
   public void testUseDatabase_doesNotExist() {
     assertEquals(DEFAULT, catalogManager.currentCatalog().currentDatabase());
@@ -126,6 +135,49 @@ public class BeamSqlCliDatabaseTest {
     cli.execute("DROP DATABASE my_database");
   }
 
+  @Test
+  public void testDropDatabase_ifExists_nonexistent() {
+    assertFalse(catalogManager.currentCatalog().databaseExists("my_database"));
+    // Should not throw exception
+    cli.execute("DROP DATABASE IF EXISTS my_database");
+    assertFalse(catalogManager.currentCatalog().databaseExists("my_database"));
+  }
+
+  @Test
+  public void testDropDatabase_ifExists_exists() {
+    cli.execute("CREATE DATABASE my_database");
+    assertTrue(catalogManager.currentCatalog().databaseExists("my_database"));
+    cli.execute("DROP DATABASE IF EXISTS my_database");
+    assertFalse(catalogManager.currentCatalog().databaseExists("my_database"));
+  }
+
+  @Test
+  public void testDropDatabase_notEmpty_restrict() {
+    cli.execute("CREATE DATABASE db_1");
+    cli.execute("USE db_1");
+
+    TestTableProvider testTableProvider = new TestTableProvider();
+    catalogManager.registerTableProvider(testTableProvider);
+    cli.execute("CREATE EXTERNAL TABLE person(id int, name varchar, age int) 
TYPE 'test'");
+
+    thrown.expect(CalciteContextException.class);
+    thrown.expectMessage("Database 'db_1' is not empty.");
+    cli.execute("DROP DATABASE db_1");
+  }
+
+  @Test
+  public void testDropDatabase_notEmpty_cascade() {
+    cli.execute("CREATE DATABASE db_1");
+    cli.execute("USE db_1");
+
+    TestTableProvider testTableProvider = new TestTableProvider();
+    catalogManager.registerTableProvider(testTableProvider);
+    cli.execute("CREATE EXTERNAL TABLE person(id int, name varchar, age int) 
TYPE 'test'");
+
+    cli.execute("DROP DATABASE db_1 CASCADE");
+    assertFalse(catalogManager.currentCatalog().databaseExists("db_1"));
+  }
+
   @Test
   public void testCreateInsertDropTableUsingDefaultDatabase() {
     Catalog catalog = catalogManager.currentCatalog();

Reply via email to