diff --git a/api/src/main/java/org/apache/iceberg/view/View.java b/api/src/main/java/org/apache/iceberg/view/View.java index 6a26f4e4a3ef..779592d03104 100644 --- a/api/src/main/java/org/apache/iceberg/view/View.java +++ b/api/src/main/java/org/apache/iceberg/view/View.java @@ -121,4 +121,15 @@ default UpdateLocation updateLocation() { default UUID uuid() { throw new UnsupportedOperationException("Retrieving a view's uuid is not supported"); } + + /** + * Returns the view representation for the given SQL dialect + * + * @return the view representation for the given SQL dialect, or null if no representation could + * be resolved + */ + default SQLViewRepresentation sqlFor(String dialect) { + throw new UnsupportedOperationException( + "Resolving a sql with a given dialect is not supported"); + } } diff --git a/core/src/main/java/org/apache/iceberg/view/BaseView.java b/core/src/main/java/org/apache/iceberg/view/BaseView.java index 89f15d02b595..57d35c9ffc54 100644 --- a/core/src/main/java/org/apache/iceberg/view/BaseView.java +++ b/core/src/main/java/org/apache/iceberg/view/BaseView.java @@ -24,6 +24,7 @@ import java.util.UUID; import org.apache.iceberg.Schema; import org.apache.iceberg.UpdateLocation; +import org.apache.iceberg.relocated.com.google.common.base.Preconditions; public class BaseView implements View, Serializable { @@ -103,4 +104,28 @@ public UpdateLocation updateLocation() { public UUID uuid() { return UUID.fromString(ops.current().uuid()); } + + /** + * This implementation of sqlFor will resolve what is considered the "closest" dialect. If an + * exact match is found, then that is returned. Otherwise, the first representation would be + * returned. If no SQL representation is found, null is returned. + */ + @Override + public SQLViewRepresentation sqlFor(String dialect) { + Preconditions.checkArgument(dialect != null, "Invalid dialect: null"); + Preconditions.checkArgument(!dialect.isEmpty(), "Invalid dialect: (empty string)"); + SQLViewRepresentation closest = null; + for (ViewRepresentation representation : currentVersion().representations()) { + if (representation instanceof SQLViewRepresentation) { + SQLViewRepresentation sqlViewRepresentation = (SQLViewRepresentation) representation; + if (sqlViewRepresentation.dialect().equals(dialect)) { + return sqlViewRepresentation; + } else if (closest == null) { + closest = sqlViewRepresentation; + } + } + } + + return closest; + } } diff --git a/core/src/test/java/org/apache/iceberg/view/ViewCatalogTests.java b/core/src/test/java/org/apache/iceberg/view/ViewCatalogTests.java index 10d2d7039ce8..795e3aa2ef86 100644 --- a/core/src/test/java/org/apache/iceberg/view/ViewCatalogTests.java +++ b/core/src/test/java/org/apache/iceberg/view/ViewCatalogTests.java @@ -1669,4 +1669,52 @@ public void concurrentReplaceViewVersion() { .build()); } } + + @Test + public void testSqlForMultipleDialects() { + TableIdentifier identifier = TableIdentifier.of("ns", "view"); + + if (requiresNamespaceCreate()) { + catalog().createNamespace(identifier.namespace()); + } + + View view = + catalog() + .buildView(identifier) + .withSchema(SCHEMA) + .withDefaultNamespace(identifier.namespace()) + .withDefaultCatalog(catalog().name()) + .withQuery("spark", "select * from ns.tbl") + .withQuery("trino", "select * from ns.tbl using X") + .create(); + + assertThat(view.sqlFor("spark").sql()).isEqualTo("select * from ns.tbl"); + assertThat(view.sqlFor("trino").sql()).isEqualTo("select * from ns.tbl using X"); + assertThat(view.sqlFor("unknown-dialect").sql()).isEqualTo("select * from ns.tbl"); + } + + @Test + public void testSqlForInvalidArguments() { + TableIdentifier identifier = TableIdentifier.of("ns", "view"); + + if (requiresNamespaceCreate()) { + catalog().createNamespace(identifier.namespace()); + } + + View view = + catalog() + .buildView(identifier) + .withSchema(SCHEMA) + .withDefaultNamespace(identifier.namespace()) + .withDefaultCatalog(catalog().name()) + .withQuery("spark", "select * from ns.tbl") + .create(); + + assertThatThrownBy(() -> view.sqlFor(null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Invalid dialect: null"); + assertThatThrownBy(() -> view.sqlFor("")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Invalid dialect: (empty string)"); + } }