diff --git a/README.md b/README.md index 975f2f5..1d82b33 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ When analyzing queries using BigQuery semantics, you need to: com.google.zetasql.toolkit zetasql-toolkit-bigquery - 0.4.0 + 0.4.1 ``` @@ -113,7 +113,7 @@ Similarly, when analyzing queries using Spanner semantics, you need to: com.google.zetasql.toolkit zetasql-toolkit-spanner - 0.4.0 + 0.4.1 ``` diff --git a/pom.xml b/pom.xml index 4333e61..595c5a6 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ com.google.zetasql.toolkit zetasql-toolkit - 0.4.1-SNAPSHOT + 0.4.1 pom ${project.groupId}:${project.artifactId} @@ -57,8 +57,8 @@ - 11 - 11 + 8 + 8 UTF-8 false true @@ -68,11 +68,11 @@ 1.7.25 5.9.3 - 5.3.1 + 4.11.0 3.3.0 3.5.0 - 1.6 + 3.1.0 3.1.0 @@ -137,7 +137,14 @@ + + + + org.apache.maven.plugins + maven-gpg-plugin + + @@ -202,4 +209,16 @@ + + + default + + true + + + + release + + + diff --git a/zetasql-toolkit-bigquery/pom.xml b/zetasql-toolkit-bigquery/pom.xml index 071e70f..f444ba7 100644 --- a/zetasql-toolkit-bigquery/pom.xml +++ b/zetasql-toolkit-bigquery/pom.xml @@ -24,19 +24,17 @@ com.google.zetasql.toolkit zetasql-toolkit - 0.4.1-SNAPSHOT + 0.4.1 ../pom.xml zetasql-toolkit-bigquery - 0.4.1-SNAPSHOT + 0.4.1 ${project.groupId}:${project.artifactId} ${project.version} - 11 - 11 UTF-8 false @@ -113,13 +111,6 @@ - - com.google.zetasql.toolkit - zetasql-toolkit-core - ${zetasql.toolkit.version} - test-jar - test - org.junit.jupiter junit-jupiter-engine @@ -158,4 +149,25 @@ + + + default + + + com.google.zetasql.toolkit + zetasql-toolkit-core + ${zetasql.toolkit.version} + test-jar + test + + + + true + + + + release + + + \ No newline at end of file diff --git a/zetasql-toolkit-bigquery/src/main/java/com/google/zetasql/toolkit/catalog/bigquery/BigQueryAPIResourceProvider.java b/zetasql-toolkit-bigquery/src/main/java/com/google/zetasql/toolkit/catalog/bigquery/BigQueryAPIResourceProvider.java index 8c1675f..0c492cc 100644 --- a/zetasql-toolkit-bigquery/src/main/java/com/google/zetasql/toolkit/catalog/bigquery/BigQueryAPIResourceProvider.java +++ b/zetasql-toolkit-bigquery/src/main/java/com/google/zetasql/toolkit/catalog/bigquery/BigQueryAPIResourceProvider.java @@ -135,6 +135,8 @@ private TypeKind convertBigqueryTypeNameToTypeKind(StandardSQLTypeName bigqueryT return TypeKind.TYPE_DATETIME; case GEOGRAPHY: return TypeKind.TYPE_GEOGRAPHY; + case JSON: + return TypeKind.TYPE_JSON; default: return TypeKind.TYPE_UNKNOWN; } @@ -201,7 +203,7 @@ private List extractColumnsFromBigQueryTable(Table table) { if (table.getDefinition().getSchema() == null) { // BigQuery tables can have no columns, in which case the schema is null // One such table is bigquery-public-data.america_health_rankings.america_health_rankings - return List.of(); + return ImmutableList.of(); } ArrayList columns = @@ -216,7 +218,7 @@ private List extractColumnsFromBigQueryTable(Table table) { if (this.tableHasTimePartitioningPseudoColumns(table)) { columns.addAll( - List.of( + ImmutableList.of( new SimpleColumn( tableId.getTable(), "_PARTITIONTIME", @@ -380,7 +382,7 @@ private FunctionArgumentType parseRoutineArgument(RoutineArgument argument) { */ private List parseRoutineArguments(List arguments) { if (arguments == null) { - return List.of(); + return ImmutableList.of(); } return arguments.stream().map(this::parseRoutineArgument).collect(Collectors.toList()); @@ -430,7 +432,7 @@ private FunctionInfo buildFunction(Routine routine) { .setNamePath(bigQueryReference.getNamePath()) .setGroup("UDF") .setMode(Mode.SCALAR) - .setSignatures(List.of(signature)) + .setSignatures(ImmutableList.of(signature)) .setLanguage(FunctionInfo.Language.valueOfOrUnspecified(routine.getLanguage())) .setBody(Optional.ofNullable(routine.getBody())) .build(); diff --git a/zetasql-toolkit-bigquery/src/main/java/com/google/zetasql/toolkit/catalog/bigquery/BigQueryBuiltIns.java b/zetasql-toolkit-bigquery/src/main/java/com/google/zetasql/toolkit/catalog/bigquery/BigQueryBuiltIns.java index 1b31e25..2eb5501 100644 --- a/zetasql-toolkit-bigquery/src/main/java/com/google/zetasql/toolkit/catalog/bigquery/BigQueryBuiltIns.java +++ b/zetasql-toolkit-bigquery/src/main/java/com/google/zetasql/toolkit/catalog/bigquery/BigQueryBuiltIns.java @@ -33,6 +33,7 @@ import com.google.zetasql.resolvedast.ResolvedCreateStatementEnums.CreateMode; import com.google.zetasql.toolkit.catalog.CatalogOperations; import com.google.zetasql.toolkit.catalog.ProcedureInfo; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -44,30 +45,33 @@ */ class BigQueryBuiltIns { - public static final Map TYPE_ALIASES = - Map.of( - "INT", TypeFactory.createSimpleType(ZetaSQLType.TypeKind.TYPE_INT64), - "SMALLINT", TypeFactory.createSimpleType(ZetaSQLType.TypeKind.TYPE_INT64), - "INTEGER", TypeFactory.createSimpleType(ZetaSQLType.TypeKind.TYPE_INT64), - "BIGINT", TypeFactory.createSimpleType(ZetaSQLType.TypeKind.TYPE_INT64), - "TINYINT", TypeFactory.createSimpleType(ZetaSQLType.TypeKind.TYPE_INT64), - "BYTEINT", TypeFactory.createSimpleType(ZetaSQLType.TypeKind.TYPE_INT64), - "DECIMAL", TypeFactory.createSimpleType(ZetaSQLType.TypeKind.TYPE_NUMERIC), - "BIGDECIMAL", TypeFactory.createSimpleType(ZetaSQLType.TypeKind.TYPE_BIGNUMERIC)); + public static final Map TYPE_ALIASES = new HashMap<>(); + + static { + TYPE_ALIASES.put("INT", TypeFactory.createSimpleType(ZetaSQLType.TypeKind.TYPE_INT64)); + TYPE_ALIASES.put("SMALLINT", TypeFactory.createSimpleType(ZetaSQLType.TypeKind.TYPE_INT64)); + TYPE_ALIASES.put("INTEGER", TypeFactory.createSimpleType(ZetaSQLType.TypeKind.TYPE_INT64)); + TYPE_ALIASES.put("BIGINT", TypeFactory.createSimpleType(ZetaSQLType.TypeKind.TYPE_INT64)); + TYPE_ALIASES.put("TINYINT", TypeFactory.createSimpleType(ZetaSQLType.TypeKind.TYPE_INT64)); + TYPE_ALIASES.put("BYTEINT", TypeFactory.createSimpleType(ZetaSQLType.TypeKind.TYPE_INT64)); + TYPE_ALIASES.put("DECIMAL", TypeFactory.createSimpleType(ZetaSQLType.TypeKind.TYPE_NUMERIC)); + TYPE_ALIASES.put( + "BIGDECIMAL", TypeFactory.createSimpleType(ZetaSQLType.TypeKind.TYPE_BIGNUMERIC)); + } private static final String BIGQUERY_FUNCTION_GROUP = "BigQuery"; public static final List FUNCTIONS = - List.of( + ImmutableList.of( // CONTAINS_SUBSTR(STRING, ANY, [STRING]) -> BOOL new Function( "CONTAINS_SUBSTR", BIGQUERY_FUNCTION_GROUP, Mode.SCALAR, - List.of( + ImmutableList.of( new FunctionSignature( new FunctionArgumentType(TypeFactory.createSimpleType(TypeKind.TYPE_BOOL)), - List.of( + ImmutableList.of( new FunctionArgumentType( TypeFactory.createSimpleType(TypeKind.TYPE_STRING), FunctionArgumentTypeOptions.builder() @@ -94,10 +98,10 @@ class BigQueryBuiltIns { "SEARCH", BIGQUERY_FUNCTION_GROUP, Mode.SCALAR, - List.of( + ImmutableList.of( new FunctionSignature( new FunctionArgumentType(TypeFactory.createSimpleType(TypeKind.TYPE_BOOL)), - List.of( + ImmutableList.of( new FunctionArgumentType( SignatureArgumentKind.ARG_TYPE_ANY_1, FunctionArgumentTypeOptions.builder() @@ -128,13 +132,13 @@ class BigQueryBuiltIns { -1)))); public static final List PROCEDURES = - List.of( + ImmutableList.of( // BQ.ABORT_SESSION([STRING]) new ProcedureInfo( ImmutableList.of("BQ", "ABORT_SESSION"), new FunctionSignature( new FunctionArgumentType(TypeFactory.createSimpleType(TypeKind.TYPE_STRING)), - List.of( + ImmutableList.of( new FunctionArgumentType( TypeFactory.createSimpleType(TypeKind.TYPE_STRING), FunctionArgumentTypeOptions.builder() @@ -148,7 +152,7 @@ class BigQueryBuiltIns { ImmutableList.of("BQ", "JOBS", "CANCEL"), new FunctionSignature( new FunctionArgumentType(TypeFactory.createSimpleType(TypeKind.TYPE_STRING)), - List.of( + ImmutableList.of( new FunctionArgumentType( TypeFactory.createSimpleType(TypeKind.TYPE_STRING), FunctionArgumentTypeOptions.builder() @@ -162,7 +166,7 @@ class BigQueryBuiltIns { ImmutableList.of("BQ", "REFRESH_EXTERNAL_METADATA_CACHE"), new FunctionSignature( new FunctionArgumentType(TypeFactory.createSimpleType(TypeKind.TYPE_STRING)), - List.of( + ImmutableList.of( new FunctionArgumentType( TypeFactory.createSimpleType(TypeKind.TYPE_STRING), FunctionArgumentTypeOptions.builder() @@ -176,7 +180,7 @@ class BigQueryBuiltIns { ImmutableList.of("BQ", "REFRESH_MATERIALIZED_VIEW"), new FunctionSignature( new FunctionArgumentType(TypeFactory.createSimpleType(TypeKind.TYPE_STRING)), - List.of( + ImmutableList.of( new FunctionArgumentType( TypeFactory.createSimpleType(TypeKind.TYPE_STRING), FunctionArgumentTypeOptions.builder() @@ -200,7 +204,7 @@ public static void addToCatalog(SimpleCatalog catalog) { List namePath = procedureInfo.getNamePath(); String procedureName = namePath.get(namePath.size() - 1); List> procedurePaths = - List.of(namePath, List.of(procedureName), List.of(String.join(".", namePath))); + ImmutableList.of(namePath, ImmutableList.of(procedureName), ImmutableList.of(String.join(".", namePath))); CatalogOperations.createProcedureInCatalog( catalog, procedurePaths, procedureInfo, CreateMode.CREATE_OR_REPLACE); } diff --git a/zetasql-toolkit-bigquery/src/main/java/com/google/zetasql/toolkit/catalog/bigquery/BigQueryCatalog.java b/zetasql-toolkit-bigquery/src/main/java/com/google/zetasql/toolkit/catalog/bigquery/BigQueryCatalog.java index 4d3732a..839fa2a 100644 --- a/zetasql-toolkit-bigquery/src/main/java/com/google/zetasql/toolkit/catalog/bigquery/BigQueryCatalog.java +++ b/zetasql-toolkit-bigquery/src/main/java/com/google/zetasql/toolkit/catalog/bigquery/BigQueryCatalog.java @@ -17,6 +17,7 @@ package com.google.zetasql.toolkit.catalog.bigquery; import com.google.cloud.bigquery.BigQuery; +import com.google.common.collect.ImmutableList; import com.google.gson.JsonParseException; import com.google.zetasql.*; import com.google.zetasql.resolvedast.ResolvedCreateStatementEnums.CreateMode; @@ -37,7 +38,6 @@ import java.util.Objects; import java.util.Set; import java.util.function.Function; -import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -61,7 +61,7 @@ public class BigQueryCatalog implements CatalogWrapper { * @param defaultProjectId The BigQuery default project id, queries are assumed to be running on * this project */ - @Deprecated(since = "0.4.0", forRemoval = true) + @Deprecated public BigQueryCatalog(String defaultProjectId) { this(defaultProjectId, BigQueryAPIResourceProvider.buildDefault()); } @@ -76,7 +76,7 @@ public BigQueryCatalog(String defaultProjectId) { * this project * @param bigQueryClient The BigQuery client to use for accessing the API */ - @Deprecated(since = "0.4.0", forRemoval = true) + @Deprecated public BigQueryCatalog(String defaultProjectId, BigQuery bigQueryClient) { this(defaultProjectId, BigQueryAPIResourceProvider.build(bigQueryClient)); } @@ -175,7 +175,7 @@ private boolean tableExistsInCatalog(String reference) { */ private boolean functionExistsInCatalog(String reference) { try { - this.catalog.findFunction(List.of(reference)); + this.catalog.findFunction(ImmutableList.of(reference)); return true; } catch (NotFoundException err) { return false; @@ -202,7 +202,7 @@ private boolean tvfExistsInCatalog(String reference) { */ private boolean procedureExistsInCatalog(String reference) { try { - this.catalog.findProcedure(List.of(reference)); + this.catalog.findProcedure(ImmutableList.of(reference)); return true; } catch (NotFoundException err) { return false; @@ -299,25 +299,25 @@ private List> buildCatalogPathsForResource(BigQueryReference refere String resourceName = reference.getResourceName(); List> resourcePaths = - List.of( - List.of(projectId, datasetName, resourceName), // format: project.dataset.table format - List.of( + ImmutableList.of( + ImmutableList.of(projectId, datasetName, resourceName), // format: project.dataset.table format + ImmutableList.of( projectId + "." + datasetName + "." + resourceName), // format: `project.dataset.table` - List.of(projectId + "." + datasetName, resourceName), // format: `project.dataset`.table - List.of(projectId, datasetName + "." + resourceName) // format: project.`dataset.table` + ImmutableList.of(projectId + "." + datasetName, resourceName), // format: `project.dataset`.table + ImmutableList.of(projectId, datasetName + "." + resourceName) // format: project.`dataset.table` ); - List> resourcePathsWithImplicitProject = List.of(); + List> resourcePathsWithImplicitProject = ImmutableList.of(); if (projectId.equals(this.defaultProjectId)) { resourcePathsWithImplicitProject = - List.of( - List.of(datasetName, resourceName), // format: dataset.table (project implied) - List.of(datasetName + "." + resourceName) // format: `dataset.table` (project implied) + ImmutableList.of( + ImmutableList.of(datasetName, resourceName), // format: dataset.table (project implied) + ImmutableList.of(datasetName + "." + resourceName) // format: `dataset.table` (project implied) ); } @@ -363,14 +363,14 @@ private CatalogResourceAlreadyExists addCaseInsensitivityWarning( public void register(SimpleTable table, CreateMode createMode, CreateScope createScope) { this.validateCreateScope( createScope, - List.of(CreateScope.CREATE_DEFAULT_SCOPE, CreateScope.CREATE_TEMP), + ImmutableList.of(CreateScope.CREATE_DEFAULT_SCOPE, CreateScope.CREATE_TEMP), table.getFullName(), "table"); - this.validateNamePathForCreation(List.of(table.getFullName()), createScope, "table"); + this.validateNamePathForCreation(ImmutableList.of(table.getFullName()), createScope, "table"); List> tablePaths = createScope.equals(CreateScope.CREATE_TEMP) - ? List.of(List.of(table.getName())) + ? ImmutableList.of(ImmutableList.of(table.getName())) : this.buildCatalogPathsForResource(table.getFullName()); try { @@ -405,7 +405,7 @@ public void register(FunctionInfo function, CreateMode createMode, CreateScope c this.validateCreateScope( createScope, - List.of(CreateScope.CREATE_DEFAULT_SCOPE, CreateScope.CREATE_TEMP), + ImmutableList.of(CreateScope.CREATE_DEFAULT_SCOPE, CreateScope.CREATE_TEMP), fullName, "function"); this.validateNamePathForCreation(functionNamePath, createScope, "function"); @@ -416,7 +416,7 @@ public void register(FunctionInfo function, CreateMode createMode, CreateScope c List> functionPaths = createScope.equals(CreateScope.CREATE_TEMP) - ? List.of(functionNamePath) + ? ImmutableList.of(functionNamePath) : this.buildCatalogPathsForResource(functionNamePath); try { @@ -439,7 +439,7 @@ public void register(TVFInfo tvfInfo, CreateMode createMode, CreateScope createS String fullName = String.join(".", tvfInfo.getNamePath()); this.validateCreateScope( - createScope, List.of(CreateScope.CREATE_DEFAULT_SCOPE), fullName, "TVF"); + createScope, ImmutableList.of(CreateScope.CREATE_DEFAULT_SCOPE), fullName, "TVF"); this.validateNamePathForCreation(tvfInfo.getNamePath(), createScope, "TVF"); TVFInfo resolvedTvfInfo = @@ -473,8 +473,8 @@ public void register( String fullName = String.join(".", procedureInfo.getNamePath()); this.validateCreateScope( - createScope, List.of(CreateScope.CREATE_DEFAULT_SCOPE), fullName, "procedure"); - this.validateNamePathForCreation(List.of(fullName), createScope, "procedure"); + createScope, ImmutableList.of(CreateScope.CREATE_DEFAULT_SCOPE), fullName, "procedure"); + this.validateNamePathForCreation(ImmutableList.of(fullName), createScope, "procedure"); List> procedurePaths = this.buildCatalogPathsForResource(fullName); @@ -519,7 +519,7 @@ public void removeTable(String tableReference) { List> tablePaths = !isQualified - ? List.of(List.of(tableReference)) + ? ImmutableList.of(ImmutableList.of(tableReference)) : this.buildCatalogPathsForResource(tableReference); CatalogOperations.deleteTableFromCatalog(this.catalog, tablePaths); @@ -531,7 +531,7 @@ public void removeFunction(String functionReference) { List> functionPaths = !isQualified - ? List.of(List.of(functionReference)) + ? ImmutableList.of(ImmutableList.of(functionReference)) : this.buildCatalogPathsForResource(functionReference); CatalogOperations.deleteFunctionFromCatalog(this.catalog, functionPaths); @@ -557,7 +557,7 @@ public void removeProcedure(String procedureReference) { @Override public void addTables(List tableReferences) { List tablesNotInCatalog = tableReferences.stream() - .filter(Predicate.not(this::tableExistsInCatalog)) + .filter(tableRef -> !this.tableExistsInCatalog(tableRef)) .collect(Collectors.toList()); this.bigQueryResourceProvider @@ -613,7 +613,7 @@ public void addAllTablesUsedInQuery(String query, AnalyzerOptions options) { .map(tablePath -> String.join(".", tablePath)) .filter(BigQueryReference::isQualified) // Remove non-qualified tables .collect(Collectors.toSet()); - this.addTables(List.copyOf(tables)); + this.addTables(ImmutableList.copyOf((tables))); } /** @@ -628,7 +628,7 @@ public void addAllTablesUsedInQuery(String query, AnalyzerOptions options) { @Override public void addFunctions(List functionReferences) { List functionsNotInCatalog = functionReferences.stream() - .filter(Predicate.not(this::functionExistsInCatalog)) + .filter(functionRef -> !this.functionExistsInCatalog(functionRef)) .collect(Collectors.toList()); this.bigQueryResourceProvider @@ -708,7 +708,7 @@ public void addAllFunctionsUsedInQuery(String query) { .map(functionPath -> String.join(".", functionPath)) .filter(BigQueryReference::isQualified) // Remove non-qualified functions .collect(Collectors.toSet()); - this.addFunctions(List.copyOf(functions)); + this.addFunctions(ImmutableList.copyOf((functions))); } /** @@ -719,7 +719,7 @@ public void addAllFunctionsUsedInQuery(String query) { @Override public void addTVFs(List functionReferences) { List functionsNotInCatalog = functionReferences.stream() - .filter(Predicate.not(this::tvfExistsInCatalog)) + .filter(functionRef -> !this.tvfExistsInCatalog(functionRef)) .collect(Collectors.toList()); this.bigQueryResourceProvider @@ -794,7 +794,7 @@ public void addAllTVFsUsedInQuery(String query) { .map(functionPath -> String.join(".", functionPath)) .filter(BigQueryReference::isQualified) // Remove non-qualified functions .collect(Collectors.toSet()); - this.addTVFs(List.copyOf(functions)); + this.addTVFs(ImmutableList.copyOf((functions))); } /** @@ -806,7 +806,7 @@ public void addAllTVFsUsedInQuery(String query) { @Override public void addProcedures(List procedureReferences) { List proceduresNotInCatalog = procedureReferences.stream() - .filter(Predicate.not(this::procedureExistsInCatalog)) + .filter(procedureRef -> !this.procedureExistsInCatalog(procedureRef)) .collect(Collectors.toList()); this.bigQueryResourceProvider @@ -862,7 +862,7 @@ public void addAllProceduresUsedInQuery(String query) { .filter(BigQueryReference::isQualified) // Remove non-qualified functions .collect(Collectors.toSet()); - this.addProcedures(List.copyOf(procedures)); + this.addProcedures(ImmutableList.copyOf((procedures))); } /** diff --git a/zetasql-toolkit-bigquery/src/main/java/com/google/zetasql/toolkit/catalog/bigquery/BigQueryReference.java b/zetasql-toolkit-bigquery/src/main/java/com/google/zetasql/toolkit/catalog/bigquery/BigQueryReference.java index 8351127..5ebbc41 100644 --- a/zetasql-toolkit-bigquery/src/main/java/com/google/zetasql/toolkit/catalog/bigquery/BigQueryReference.java +++ b/zetasql-toolkit-bigquery/src/main/java/com/google/zetasql/toolkit/catalog/bigquery/BigQueryReference.java @@ -23,7 +23,6 @@ import com.google.zetasql.toolkit.catalog.bigquery.exceptions.InvalidBigQueryReference; import java.util.Arrays; import java.util.LinkedList; -import java.util.List; import java.util.Objects; import java.util.regex.Pattern; @@ -70,7 +69,7 @@ public static BigQueryReference from(String projectId, String referenceString) { LinkedList elements = new LinkedList<>(Arrays.asList(referenceString.split("\\."))); int numberOfElements = elements.size(); - if (!List.of(2, 3).contains(numberOfElements)) { + if (!ImmutableList.of(2, 3).contains(numberOfElements)) { throw new InvalidBigQueryReference(referenceString); } diff --git a/zetasql-toolkit-bigquery/src/main/java/com/google/zetasql/toolkit/options/BigQueryLanguageOptions.java b/zetasql-toolkit-bigquery/src/main/java/com/google/zetasql/toolkit/options/BigQueryLanguageOptions.java index c0e8f98..cae90bd 100644 --- a/zetasql-toolkit-bigquery/src/main/java/com/google/zetasql/toolkit/options/BigQueryLanguageOptions.java +++ b/zetasql-toolkit-bigquery/src/main/java/com/google/zetasql/toolkit/options/BigQueryLanguageOptions.java @@ -16,12 +16,12 @@ package com.google.zetasql.toolkit.options; +import com.google.common.collect.ImmutableSet; import com.google.zetasql.LanguageOptions; import com.google.zetasql.ZetaSQLOptions.LanguageFeature; import com.google.zetasql.ZetaSQLOptions.NameResolutionMode; import com.google.zetasql.ZetaSQLOptions.ProductMode; import com.google.zetasql.ZetaSQLResolvedNodeKind.ResolvedNodeKind; -import java.util.Set; public class BigQueryLanguageOptions { @@ -32,7 +32,7 @@ public class BigQueryLanguageOptions { languageOptions.setProductMode(ProductMode.PRODUCT_EXTERNAL); languageOptions.setEnabledLanguageFeatures( - Set.of( + ImmutableSet.of( LanguageFeature.FEATURE_ALLOW_MISSING_PATH_EXPRESSION_IN_ALTER_DDL, LanguageFeature.FEATURE_ALTER_COLUMN_SET_DATA_TYPE, LanguageFeature.FEATURE_ALTER_TABLE_RENAME_COLUMN, @@ -117,7 +117,7 @@ public class BigQueryLanguageOptions { LanguageFeature.FEATURE_V_1_3_WITH_RECURSIVE)); languageOptions.setSupportedStatementKinds( - Set.of( + ImmutableSet.of( ResolvedNodeKind.RESOLVED_ADD_CONSTRAINT_ACTION, ResolvedNodeKind.RESOLVED_ALTER_ENTITY_STMT, ResolvedNodeKind.RESOLVED_ALTER_MATERIALIZED_VIEW_STMT, diff --git a/zetasql-toolkit-bigquery/src/test/java/com/google/zetasql/toolkit/catalog/bigquery/BigQueryAPIResourceProviderTest.java b/zetasql-toolkit-bigquery/src/test/java/com/google/zetasql/toolkit/catalog/bigquery/BigQueryAPIResourceProviderTest.java index fb5878b..7e124bf 100644 --- a/zetasql-toolkit-bigquery/src/test/java/com/google/zetasql/toolkit/catalog/bigquery/BigQueryAPIResourceProviderTest.java +++ b/zetasql-toolkit-bigquery/src/test/java/com/google/zetasql/toolkit/catalog/bigquery/BigQueryAPIResourceProviderTest.java @@ -24,6 +24,7 @@ import com.google.cloud.bigquery.*; import com.google.cloud.bigquery.Field.Mode; import com.google.cloud.bigquery.Table; +import com.google.common.collect.ImmutableList; import com.google.zetasql.*; import com.google.zetasql.FunctionArgumentType.FunctionArgumentTypeOptions; import com.google.zetasql.StructType.StructField; @@ -42,7 +43,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Answers; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; @@ -59,37 +59,35 @@ void init() { } Table createMockTable(boolean timePartitioned) { - Table mockTable = mock(Table.class); - StandardTableDefinition mockTableDefinition = - mock(StandardTableDefinition.class, Answers.RETURNS_DEEP_STUBS); - TableId tableId = TableId.of("project", "dataset", "table"); FieldList fields = FieldList.of( Field.of("col1", StandardSQLTypeName.INT64), Field.newBuilder("col2", StandardSQLTypeName.STRING).setMode(Mode.REPEATED).build(), Field.of( - "col3", StandardSQLTypeName.STRUCT, Field.of("field1", StandardSQLTypeName.INT64))); + "col3", StandardSQLTypeName.STRUCT, + Field.of("field1", StandardSQLTypeName.INT64))); - when(mockTable.getTableId()).thenReturn(tableId); - when(mockTable.getDefinition()).thenReturn(mockTableDefinition); - when(mockTableDefinition.getSchema().getFields()).thenReturn(fields); + StandardTableDefinition.Builder tableDefinitionBuilder = StandardTableDefinition.newBuilder(); + tableDefinitionBuilder.setSchema(Schema.of(fields)); if (timePartitioned) { TimePartitioning timePartitioning = TimePartitioning.newBuilder(TimePartitioning.Type.DAY) .setField(null) // A null field means the table in ingestion-time partitioned .build(); - when(mockTableDefinition.getTimePartitioning()).thenReturn(timePartitioning); - } else { - when(mockTableDefinition.getTimePartitioning()).thenReturn(null); + tableDefinitionBuilder.setTimePartitioning(timePartitioning); } + Table mockTable = mock(Table.class); + when(mockTable.getTableId()).thenReturn(tableId); + when(mockTable.getDefinition()).thenReturn(tableDefinitionBuilder.build()); + return mockTable; } List expectedColumnsForMockTable() { - return List.of( + return ImmutableList.of( new SimpleColumn("table", "col1", TypeFactory.createSimpleType(TypeKind.TYPE_INT64)), new SimpleColumn( "table", @@ -99,7 +97,7 @@ List expectedColumnsForMockTable() { "table", "col3", TypeFactory.createStructType( - List.of( + ImmutableList.of( new StructField( "field1", TypeFactory.createSimpleType(TypeKind.TYPE_INT64)))))); } @@ -112,7 +110,7 @@ void testGetTables() { List expectedSchemaForMockTable = expectedColumnsForMockTable(); - List tables = bigqueryResourceProvider.getTables("project", List.of("reference")); + List tables = bigqueryResourceProvider.getTables("project", ImmutableList.of("reference")); assertEquals(1, tables.size()); assertTrue( @@ -126,7 +124,7 @@ void testGetTimePartitionedTable() { when(bigQueryServiceMock.fetchTable(anyString(), anyString())) .thenReturn(Result.success(mockTable)); - List tables = bigqueryResourceProvider.getTables("project", List.of("reference")); + List tables = bigqueryResourceProvider.getTables("project", ImmutableList.of("reference")); assertEquals(1, tables.size()); @@ -159,7 +157,7 @@ void testGetAllTablesInDataset() { TableId tableId = TableId.of("project", "dataset", "table"); Table mockTable = createMockTable(false); when(bigQueryServiceMock.listTables(anyString(), anyString())) - .thenReturn(Result.success(List.of(tableId))); + .thenReturn(Result.success(ImmutableList.of(tableId))); when(bigQueryServiceMock.fetchTable(anyString(), anyString())) .thenReturn(Result.success(mockTable)); @@ -179,9 +177,9 @@ void testGetAllTablesInProject() { TableId tableId = TableId.of("project", "dataset", "table"); Table mockTable = createMockTable(false); when(bigQueryServiceMock.listDatasets(anyString())) - .thenReturn(Result.success(List.of(datasetId))); + .thenReturn(Result.success(ImmutableList.of(datasetId))); when(bigQueryServiceMock.listTables(anyString(), anyString())) - .thenReturn(Result.success(List.of(tableId))); + .thenReturn(Result.success(ImmutableList.of(tableId))); when(bigQueryServiceMock.fetchTable(anyString(), anyString())) .thenReturn(Result.success(mockTable)); @@ -203,7 +201,7 @@ Routine createMockUDF() { when(routine.getRoutineType()).thenReturn(BigQueryAPIRoutineType.UDF.getLabel()); when(routine.getArguments()) .thenReturn( - List.of( + ImmutableList.of( RoutineArgument.newBuilder() .setName("x") .setDataType(StandardSQLDataType.newBuilder(StandardSQLTypeName.INT64).build()) @@ -227,7 +225,7 @@ FunctionSignature expectedSignatureForMockUDF() { .build(), 1); - return new FunctionSignature(returnType, List.of(argument), -1); + return new FunctionSignature(returnType, ImmutableList.of(argument), -1); } @Test @@ -239,7 +237,7 @@ void testGetFunctions() { FunctionSignature expectedSignatureForMockUDF = expectedSignatureForMockUDF(); List functions = - bigqueryResourceProvider.getFunctions("project", List.of("reference")); + bigqueryResourceProvider.getFunctions("project", ImmutableList.of("reference")); assertEquals(1, functions.size()); assertTrue( @@ -255,7 +253,7 @@ Routine createMockTVF() { when(routine.getRoutineType()).thenReturn(BigQueryAPIRoutineType.TVF.getLabel()); when(routine.getArguments()) .thenReturn( - List.of( + ImmutableList.of( RoutineArgument.newBuilder() .setName("x") .setDataType(StandardSQLDataType.newBuilder(StandardSQLTypeName.INT64).build()) @@ -264,7 +262,7 @@ Routine createMockTVF() { .thenReturn( StandardSQLTableType.newBuilder() .setColumns( - List.of( + ImmutableList.of( StandardSQLField.newBuilder() .setName("out") .setDataType( @@ -277,7 +275,7 @@ Routine createMockTVF() { TVFRelation expectedOutputSchemaForMockTVF() { return TVFRelation.createColumnBased( - List.of(Column.create("out", TypeFactory.createSimpleType(TypeKind.TYPE_STRING)))); + ImmutableList.of(Column.create("out", TypeFactory.createSimpleType(TypeKind.TYPE_STRING)))); } FunctionSignature expectedSignatureForMockTVF() { @@ -298,7 +296,7 @@ FunctionSignature expectedSignatureForMockTVF() { .build(), 1); - return new FunctionSignature(returnType, List.of(argument), -1); + return new FunctionSignature(returnType, ImmutableList.of(argument), -1); } @Test @@ -310,7 +308,7 @@ void testGetTVFs() { FunctionSignature expectedSignatureForMockTVF = expectedSignatureForMockTVF(); TVFRelation expectedOutputSchemaForMockTVF = expectedOutputSchemaForMockTVF(); - List functions = bigqueryResourceProvider.getTVFs("project", List.of("reference")); + List functions = bigqueryResourceProvider.getTVFs("project", ImmutableList.of("reference")); assertEquals(1, functions.size()); assertTrue( @@ -329,7 +327,7 @@ Routine createMockProcedure() { when(routine.getRoutineType()).thenReturn(BigQueryAPIRoutineType.PROCEDURE.getLabel()); when(routine.getArguments()) .thenReturn( - List.of( + ImmutableList.of( RoutineArgument.newBuilder() .setName("x") .setDataType(StandardSQLDataType.newBuilder(StandardSQLTypeName.INT64).build()) @@ -351,7 +349,7 @@ FunctionSignature expectedSignatureForMockProcedure() { .build(), 1); - return new FunctionSignature(returnType, List.of(argument), -1); + return new FunctionSignature(returnType, ImmutableList.of(argument), -1); } @Test @@ -363,7 +361,7 @@ void testGetProcedures() { FunctionSignature expectedSignatureForMockProcedure = expectedSignatureForMockProcedure(); List procedures = - bigqueryResourceProvider.getProcedures("project", List.of("reference")); + bigqueryResourceProvider.getProcedures("project", ImmutableList.of("reference")); assertEquals(1, procedures.size()); assertTrue( diff --git a/zetasql-toolkit-bigquery/src/test/java/com/google/zetasql/toolkit/catalog/bigquery/BigQueryCatalogTest.java b/zetasql-toolkit-bigquery/src/test/java/com/google/zetasql/toolkit/catalog/bigquery/BigQueryCatalogTest.java index 5700b05..9d4f20c 100644 --- a/zetasql-toolkit-bigquery/src/test/java/com/google/zetasql/toolkit/catalog/bigquery/BigQueryCatalogTest.java +++ b/zetasql-toolkit-bigquery/src/test/java/com/google/zetasql/toolkit/catalog/bigquery/BigQueryCatalogTest.java @@ -58,14 +58,14 @@ public class BigQueryCatalogTest { FunctionInfo exampleFunction = FunctionInfo.newBuilder() - .setNamePath(List.of(testProjectId, "dataset", "examplefunction")) + .setNamePath(ImmutableList.of(testProjectId, "dataset", "examplefunction")) .setGroup("UDF") .setMode(Mode.SCALAR) .setSignatures( - List.of( + ImmutableList.of( new FunctionSignature( new FunctionArgumentType(TypeFactory.createSimpleType(TypeKind.TYPE_STRING)), - List.of(), + ImmutableList.of(), -1))) .build(); @@ -74,7 +74,7 @@ public class BigQueryCatalogTest { .setNamePath(ImmutableList.of(testProjectId, "dataset", "exampletvf")) .setSignature( new FunctionSignature( - new FunctionArgumentType(SignatureArgumentKind.ARG_TYPE_RELATION), List.of(), -1)) + new FunctionArgumentType(SignatureArgumentKind.ARG_TYPE_RELATION), ImmutableList.of(), -1)) .setOutputSchema( TVFRelation.createValueTableBased(TypeFactory.createSimpleType(TypeKind.TYPE_STRING))) .build(); @@ -84,7 +84,7 @@ public class BigQueryCatalogTest { ImmutableList.of(testProjectId, "dataset", "exampleprocedure"), new FunctionSignature( new FunctionArgumentType(TypeFactory.createSimpleType(TypeKind.TYPE_STRING)), - List.of(), + ImmutableList.of(), -1)); @BeforeEach @@ -99,7 +99,7 @@ private void setupExampleTables() { exampleTableInDefaultProject = new SimpleTable( exampleTableName, - List.of( + ImmutableList.of( new SimpleColumn( exampleTableName, "col1", TypeFactory.createSimpleType(TypeKind.TYPE_STRING)))); exampleTableInDefaultProject.setFullName( @@ -114,7 +114,7 @@ private void setupExampleTables() { replacementTableInDefaultProject = new SimpleTable( exampleTableName, - List.of( + ImmutableList.of( new SimpleColumn( exampleTableName, "col1", TypeFactory.createSimpleType(TypeKind.TYPE_STRING)), new SimpleColumn( @@ -127,19 +127,19 @@ private List> buildPathsWhereResourceShouldBe(String resourceRefere BigQueryReference ref = BigQueryReference.from(this.testProjectId, resourceReference); List> fullyQualifiedPaths = - List.of( - List.of(ref.getProjectId() + "." + ref.getDatasetId() + "." + ref.getResourceName()), - List.of(ref.getProjectId(), ref.getDatasetId() + "." + ref.getResourceName()), - List.of(ref.getProjectId() + "." + ref.getDatasetId(), ref.getResourceName()), - List.of(ref.getProjectId(), ref.getDatasetId(), ref.getResourceName())); + ImmutableList.of( + ImmutableList.of(ref.getProjectId() + "." + ref.getDatasetId() + "." + ref.getResourceName()), + ImmutableList.of(ref.getProjectId(), ref.getDatasetId() + "." + ref.getResourceName()), + ImmutableList.of(ref.getProjectId() + "." + ref.getDatasetId(), ref.getResourceName()), + ImmutableList.of(ref.getProjectId(), ref.getDatasetId(), ref.getResourceName())); List> result = new ArrayList<>(fullyQualifiedPaths); if (ref.getProjectId().equals(this.testProjectId)) { List> implicitProjectPaths = - List.of( - List.of(ref.getDatasetId() + "." + ref.getResourceName()), - List.of(ref.getDatasetId(), ref.getResourceName())); + ImmutableList.of( + ImmutableList.of(ref.getDatasetId() + "." + ref.getResourceName()), + ImmutableList.of(ref.getDatasetId(), ref.getResourceName())); result.addAll(implicitProjectPaths); } @@ -199,11 +199,11 @@ void testCatalogSupportsBigQueryTypeNames() { Type integerType = assertDoesNotThrow( - () -> underlyingCatalog.findType(List.of("INTEGER")), + () -> underlyingCatalog.findType(ImmutableList.of("INTEGER")), "BigQuery catalogs should support the INTEGER type name"); Type decimalType = assertDoesNotThrow( - () -> underlyingCatalog.findType(List.of("DECIMAL")), + () -> underlyingCatalog.findType(ImmutableList.of("DECIMAL")), "BigQuery catalogs should support the DECIMAL type name"); assertEquals( @@ -224,13 +224,13 @@ void testRegisterInvalidTableName() { SimpleTable table = new SimpleTable( tableName, - List.of( + ImmutableList.of( new SimpleColumn( tableName, "column", TypeFactory.createSimpleType(TypeKind.TYPE_STRING)))); table.setFullName(invalidFullName); when(this.bigQueryResourceProviderMock.getTables(anyString(), anyList())) - .thenReturn(List.of(table)); + .thenReturn(ImmutableList.of(table)); assertThrows( InvalidBigQueryReference.class, @@ -274,7 +274,7 @@ void testRegisterTableFromNonDefaultProject() { BigQueryReference ref = BigQueryReference.from(this.testProjectId, exampleTableInNonDefaultProject.getFullName()); - List pathWhereTableShouldNotBe = List.of(ref.getDatasetId(), ref.getResourceName()); + List pathWhereTableShouldNotBe = ImmutableList.of(ref.getDatasetId(), ref.getResourceName()); assertThrows( NotFoundException.class, @@ -338,10 +338,10 @@ void testTableAlreadyExists() { void testAddTablesByName() { // When BigQueryResourceProvider.getTables() is called, return the test table when(bigQueryResourceProviderMock.getTables(anyString(), anyList())) - .thenReturn(List.of(exampleTableInDefaultProject)); + .thenReturn(ImmutableList.of(exampleTableInDefaultProject)); // Add the tables by name - bigQueryCatalog.addTables(List.of(exampleTableInDefaultProject.getFullName())); + bigQueryCatalog.addTables(ImmutableList.of(exampleTableInDefaultProject.getFullName())); // Verify the BigQueryCatalog got the tables from the BigQueryResourceProvider verify(bigQueryResourceProviderMock, times(1)).getTables(anyString(), anyList()); @@ -355,7 +355,7 @@ void testAddTablesByName() { @Test void testAddAllTablesInDataset() { when(bigQueryResourceProviderMock.getAllTablesInDataset(anyString(), anyString())) - .thenReturn(List.of(exampleTableInDefaultProject)); + .thenReturn(ImmutableList.of(exampleTableInDefaultProject)); bigQueryCatalog.addAllTablesInDataset(testProjectId, "dataset"); @@ -369,7 +369,7 @@ void testAddAllTablesInDataset() { @Test void testAddAllTablesInProject() { when(bigQueryResourceProviderMock.getAllTablesInProject(anyString())) - .thenReturn(List.of(exampleTableInDefaultProject)); + .thenReturn(ImmutableList.of(exampleTableInDefaultProject)); bigQueryCatalog.addAllTablesInProject(testProjectId); @@ -397,15 +397,15 @@ void testRegisterFunction() { void testInferFunctionReturnType() { FunctionInfo functionWithUnknownReturnType = FunctionInfo.newBuilder() - .setNamePath(List.of(testProjectId, "dataset", "function")) + .setNamePath(ImmutableList.of(testProjectId, "dataset", "function")) .setGroup("UDF") .setMode(Mode.SCALAR) .setSignatures( - List.of( + ImmutableList.of( new FunctionSignature( new FunctionArgumentType( TypeFactory.createSimpleType(TypeKind.TYPE_UNKNOWN)), - List.of(), + ImmutableList.of(), -1))) .setLanguage(FunctionInfo.Language.SQL) .setBody("5.6 + 5") diff --git a/zetasql-toolkit-bigquery/src/test/java/com/google/zetasql/toolkit/catalog/bigquery/BigQueryServiceTest.java b/zetasql-toolkit-bigquery/src/test/java/com/google/zetasql/toolkit/catalog/bigquery/BigQueryServiceTest.java index babcc6e..6de73ea 100644 --- a/zetasql-toolkit-bigquery/src/test/java/com/google/zetasql/toolkit/catalog/bigquery/BigQueryServiceTest.java +++ b/zetasql-toolkit-bigquery/src/test/java/com/google/zetasql/toolkit/catalog/bigquery/BigQueryServiceTest.java @@ -21,6 +21,7 @@ import com.google.api.gax.paging.Page; import com.google.cloud.bigquery.*; +import com.google.common.collect.ImmutableList; import com.google.zetasql.toolkit.catalog.bigquery.BigQueryService.Result; import com.google.zetasql.toolkit.catalog.bigquery.exceptions.BigQueryAPIError; import java.util.List; @@ -64,7 +65,7 @@ void testListDatasets() { Dataset mockDataset = mock(Dataset.class); when(mockDataset.getDatasetId()).thenReturn(datasetId); - Page mockPage = createMockPage(List.of(mockDataset)); + Page mockPage = createMockPage(ImmutableList.of(mockDataset)); when(bigqueryClientMock.listDatasets(anyString(), any())).thenReturn(mockPage); Result> listDatasetsResult = bigQueryService.listDatasets(projectId); @@ -73,7 +74,7 @@ void testListDatasets() { List listedDatasetIds = listDatasetsResult.get(); - assertIterableEquals(List.of(datasetId), listedDatasetIds); + assertIterableEquals(ImmutableList.of(datasetId), listedDatasetIds); } @Test @@ -86,7 +87,7 @@ void testListTables() { Table mockTable = mock(Table.class); when(mockTable.getTableId()).thenReturn(tableId); - Page mockPage = createMockPage(List.of(mockTable)); + Page
mockPage = createMockPage(ImmutableList.of(mockTable)); when(bigqueryClientMock.listTables(any(DatasetId.class), any())).thenReturn(mockPage); Result> listTablesResult = bigQueryService.listTables(projectId, datasetName); @@ -95,7 +96,7 @@ void testListTables() { List listedTableIds = listTablesResult.get(); - assertIterableEquals(List.of(tableId), listedTableIds); + assertIterableEquals(ImmutableList.of(tableId), listedTableIds); } @Test @@ -108,7 +109,7 @@ void testListRoutines() { Routine mockRoutine = mock(Routine.class); when(mockRoutine.getRoutineId()).thenReturn(routineId); - Page mockPage = createMockPage(List.of(mockRoutine)); + Page mockPage = createMockPage(ImmutableList.of(mockRoutine)); when(bigqueryClientMock.listRoutines(any(DatasetId.class), any())).thenReturn(mockPage); Result> listRoutinesResult = @@ -118,7 +119,7 @@ void testListRoutines() { List listedRoutineIds = listRoutinesResult.get(); - assertIterableEquals(List.of(routineId), listedRoutineIds); + assertIterableEquals(ImmutableList.of(routineId), listedRoutineIds); } @Test diff --git a/zetasql-toolkit-bigquery/src/test/java/com/google/zetasql/toolkit/catalog/bigquery/FunctionReturnTypeResolverTest.java b/zetasql-toolkit-bigquery/src/test/java/com/google/zetasql/toolkit/catalog/bigquery/FunctionReturnTypeResolverTest.java index 66336e2..22638d8 100644 --- a/zetasql-toolkit-bigquery/src/test/java/com/google/zetasql/toolkit/catalog/bigquery/FunctionReturnTypeResolverTest.java +++ b/zetasql-toolkit-bigquery/src/test/java/com/google/zetasql/toolkit/catalog/bigquery/FunctionReturnTypeResolverTest.java @@ -33,7 +33,6 @@ import com.google.zetasql.toolkit.catalog.FunctionInfo; import com.google.zetasql.toolkit.catalog.TVFInfo; import com.google.zetasql.toolkit.options.BigQueryLanguageOptions; -import java.util.List; import java.util.Optional; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -53,15 +52,15 @@ void init() { void testInferFunctionReturnType() { FunctionInfo functionInfo = FunctionInfo.newBuilder() - .setNamePath(List.of("f")) + .setNamePath(ImmutableList.of("f")) .setGroup("UDF") .setMode(Mode.SCALAR) .setSignatures( - List.of( + ImmutableList.of( new FunctionSignature( new FunctionArgumentType( TypeFactory.createSimpleType(TypeKind.TYPE_UNKNOWN)), - List.of(), + ImmutableList.of(), -1))) .setLanguage(FunctionInfo.Language.SQL) .setBody("CAST(FLOOR(5.5) AS INT64) + 1") @@ -90,7 +89,7 @@ void testInferTVFOutputSchema() { .setSignature( new FunctionSignature( new FunctionArgumentType(SignatureArgumentKind.ARG_TYPE_RELATION), - List.of(), + ImmutableList.of(), -1)) .setOutputSchema(Optional.empty()) .setBody("SELECT 1 AS column1, CONCAT('a', 'b') AS column2") diff --git a/zetasql-toolkit-core/pom.xml b/zetasql-toolkit-core/pom.xml index 9df6273..73c153e 100644 --- a/zetasql-toolkit-core/pom.xml +++ b/zetasql-toolkit-core/pom.xml @@ -22,21 +22,19 @@ com.google.zetasql.toolkit zetasql-toolkit - 0.4.1-SNAPSHOT + 0.4.1 ../pom.xml zetasql-toolkit-core - 0.4.1-SNAPSHOT + 0.4.1 ${project.groupId}:${project.artifactId} ${project.version} - 11 - 11 UTF-8 false - 4.13.0 + 4.9.3 1.1.0 3.3.0 diff --git a/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/AnalyzerExtensions.java b/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/AnalyzerExtensions.java index 97f9800..59c06b8 100644 --- a/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/AnalyzerExtensions.java +++ b/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/AnalyzerExtensions.java @@ -35,8 +35,9 @@ import com.google.zetasql.parser.ASTNodes.ASTTVF; import com.google.zetasql.parser.ParseTreeVisitor; import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedExpr; +import java.util.LinkedHashSet; import java.util.List; -import org.antlr.v4.runtime.misc.OrderedHashSet; +import java.util.Set; /** Implements extensions to ZetaSQL's built-in {@link com.google.zetasql.Analyzer} */ public class AnalyzerExtensions { @@ -96,7 +97,7 @@ public static List> extractFunctionNamesFromNextStatement( * will be returned multiple times. */ private static List> extractFunctionNamesFromASTNode(ASTNode node) { - OrderedHashSet> result = new OrderedHashSet<>(); + Set> result = new LinkedHashSet<>(); ParseTreeVisitor extractFunctionNamesVisitor = new ParseTreeVisitor() { @@ -113,7 +114,7 @@ public void visit(ASTFunctionCall functionCall) { node.accept(extractFunctionNamesVisitor); - return List.copyOf(result); + return ImmutableList.copyOf(result); } /** @@ -170,7 +171,7 @@ public static List> extractTVFNamesFromNextStatement( * will be returned multiple times. */ private static List> extractTVFNamesFromASTNode(ASTNode node) { - OrderedHashSet> result = new OrderedHashSet<>(); + Set> result = new LinkedHashSet<>(); ParseTreeVisitor extractFunctionNamesVisitor = new ParseTreeVisitor() { @@ -187,7 +188,7 @@ public void visit(ASTTVF tvfCall) { node.accept(extractFunctionNamesVisitor); - return List.copyOf(result); + return ImmutableList.copyOf(result); } /** @@ -247,7 +248,7 @@ public static List> extractProcedureNamesFromNextStatement( * it will be returned multiple times. */ private static List> extractProcedureNamesFromASTNode(ASTNode node) { - OrderedHashSet> result = new OrderedHashSet<>(); + Set> result = new LinkedHashSet<>(); ParseTreeVisitor extractProcedureNamesVisitor = new ParseTreeVisitor() { @@ -264,7 +265,7 @@ public void visit(ASTCallStatement procedureCall) { node.accept(extractProcedureNamesVisitor); - return List.copyOf(result); + return ImmutableList.copyOf(result); } /** diff --git a/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/CatalogUpdaterVisitor.java b/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/CatalogUpdaterVisitor.java index 0365fdf..0f2d907 100644 --- a/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/CatalogUpdaterVisitor.java +++ b/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/CatalogUpdaterVisitor.java @@ -16,6 +16,7 @@ package com.google.zetasql.toolkit; +import com.google.common.collect.ImmutableList; import com.google.zetasql.*; import com.google.zetasql.TVFRelation.Column; import com.google.zetasql.ZetaSQLFunctions.FunctionEnums.Mode; @@ -191,7 +192,7 @@ public void visit(ResolvedCreateFunctionStmt createFunctionStmt) { .setNamePath(createFunctionStmt.getNamePath()) .setGroup("UDF") .setMode(Mode.SCALAR) - .setSignatures(List.of(createFunctionStmt.getSignature())) + .setSignatures(ImmutableList.of(createFunctionStmt.getSignature())) .build(); CreateMode createMode = createFunctionStmt.getCreateMode(); diff --git a/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/Coercer.java b/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/Coercer.java index cc80e2b..9eac516 100644 --- a/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/Coercer.java +++ b/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/Coercer.java @@ -356,7 +356,7 @@ public boolean coercesTo( .map(typeCoercion -> typeCoercion.coercionMode) .findFirst(); - if(maybeCoercionMode.isEmpty()) { + if(!maybeCoercionMode.isPresent()) { return false; } diff --git a/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/ZetaSQLToolkitAnalyzer.java b/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/ZetaSQLToolkitAnalyzer.java index 25ad291..077a6f7 100644 --- a/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/ZetaSQLToolkitAnalyzer.java +++ b/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/ZetaSQLToolkitAnalyzer.java @@ -288,7 +288,7 @@ private void applyVariableDeclaration(ASTVariableDeclaration declaration) { .map(expression -> AnalyzerExtensions.analyzeExpression( query, expression, analyzerOptions, catalog.getZetaSQLCatalog())); - if (explicitType.isEmpty() && defaultValueExpr.isEmpty()) { + if (!explicitType.isPresent() && !defaultValueExpr.isPresent()) { // Should not happen, since this is enforced by the parser throw new AnalysisException( "Either the type or the default value must be present for variable declarations"); @@ -316,7 +316,7 @@ private void validateSingleAssignment(ASTSingleAssignment singleAssignment) { query, expression, analyzerOptions, catalog.getZetaSQLCatalog()); try { - Constant constant = catalog.getZetaSQLCatalog().findConstant(List.of(assignmentTarget)); + Constant constant = catalog.getZetaSQLCatalog().findConstant(ImmutableList.of(assignmentTarget)); this.coerceExpressionToType(constant.getType(), analyzedExpression); } catch (NotFoundException e) { throw new AnalysisException("Undeclared variable: " + assignmentTarget); diff --git a/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/catalog/CatalogOperations.java b/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/catalog/CatalogOperations.java index 66c29da..3a60225 100644 --- a/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/catalog/CatalogOperations.java +++ b/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/catalog/CatalogOperations.java @@ -85,7 +85,7 @@ private static boolean tableExists(SimpleCatalog catalog, List tablePath /** Returns true if a table named tableName exists in the SimpleCatalog */ private static boolean tableExists(SimpleCatalog catalog, String tableName) { - return tableExists(catalog, List.of(tableName)); + return tableExists(catalog, ImmutableList.of(tableName)); } private static String removeGroupFromFunctionName(String functionName) { @@ -312,7 +312,7 @@ public static void createFunctionInCatalog( Function finalFunction = new Function( - List.of(functionName), + ImmutableList.of(functionName), functionInfo.getGroup(), functionInfo.getMode(), functionInfo.getSignatures()); diff --git a/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/catalog/CatalogWrapper.java b/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/catalog/CatalogWrapper.java index 4ba69ae..da4db26 100644 --- a/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/catalog/CatalogWrapper.java +++ b/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/catalog/CatalogWrapper.java @@ -16,6 +16,7 @@ package com.google.zetasql.toolkit.catalog; +import com.google.common.collect.ImmutableList; import com.google.zetasql.Constant; import com.google.zetasql.SimpleCatalog; import com.google.zetasql.SimpleTable; @@ -180,7 +181,7 @@ default void removeProcedures(List procedures) { * @param table The reference to the table to add */ default void addTable(String table) { - this.addTables(List.of(table)); + this.addTables(ImmutableList.of(table)); } /** @@ -189,7 +190,7 @@ default void addTable(String table) { * @param function The reference to the function to add */ default void addFunction(String function) { - this.addFunctions(List.of(function)); + this.addFunctions(ImmutableList.of(function)); } /** @@ -198,7 +199,7 @@ default void addFunction(String function) { * @param function The reference to the TVF to add */ default void addTVF(String function) { - this.addTVFs(List.of(function)); + this.addTVFs(ImmutableList.of(function)); } /** @@ -207,7 +208,7 @@ default void addTVF(String function) { * @param procedure The reference to the procedure to add */ default void addProcedure(String procedure) { - this.addProcedures(List.of(procedure)); + this.addProcedures(ImmutableList.of(procedure)); } /** diff --git a/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/catalog/basic/BasicCatalogWrapper.java b/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/catalog/basic/BasicCatalogWrapper.java index 6fe5b2f..4e5f660 100644 --- a/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/catalog/basic/BasicCatalogWrapper.java +++ b/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/catalog/basic/BasicCatalogWrapper.java @@ -16,6 +16,7 @@ package com.google.zetasql.toolkit.catalog.basic; +import com.google.common.collect.ImmutableList; import com.google.zetasql.Constant; import com.google.zetasql.SimpleCatalog; import com.google.zetasql.SimpleTable; @@ -60,7 +61,7 @@ public BasicCatalogWrapper(SimpleCatalog initialCatalog) { public void register(SimpleTable table, CreateMode createMode, CreateScope createScope) { CatalogOperations.createTableInCatalog( this.catalog, - List.of(List.of(table.getFullName())), + ImmutableList.of(ImmutableList.of(table.getFullName())), table.getFullName(), table.getColumnList(), createMode); @@ -69,13 +70,13 @@ public void register(SimpleTable table, CreateMode createMode, CreateScope creat @Override public void register(FunctionInfo function, CreateMode createMode, CreateScope createScope) { CatalogOperations.createFunctionInCatalog( - this.catalog, List.of(function.getNamePath()), function, createMode); + this.catalog, ImmutableList.of(function.getNamePath()), function, createMode); } @Override public void register(TVFInfo tvfInfo, CreateMode createMode, CreateScope createScope) { CatalogOperations.createTVFInCatalog( - this.catalog, List.of(tvfInfo.getNamePath()), tvfInfo, createMode); + this.catalog, ImmutableList.of(tvfInfo.getNamePath()), tvfInfo, createMode); } /** @@ -99,7 +100,7 @@ public void register( List> procedurePaths = new ArrayList<>(); procedurePaths.add(namePath); if (namePath.size() > 1) { - procedurePaths.add(List.of(fullName)); + procedurePaths.add(ImmutableList.of(fullName)); } CatalogOperations.createProcedureInCatalog( @@ -129,22 +130,22 @@ public void register(Constant constant) { @Override public void removeTable(String fullTableName) { - CatalogOperations.deleteTableFromCatalog(this.catalog, List.of(List.of(fullTableName))); + CatalogOperations.deleteTableFromCatalog(this.catalog, ImmutableList.of(ImmutableList.of(fullTableName))); } @Override public void removeFunction(String function) { - CatalogOperations.deleteFunctionFromCatalog(this.catalog, List.of(List.of(function))); + CatalogOperations.deleteFunctionFromCatalog(this.catalog, ImmutableList.of(ImmutableList.of(function))); } @Override public void removeTVF(String function) { - CatalogOperations.deleteTVFFromCatalog(this.catalog, List.of(List.of(function))); + CatalogOperations.deleteTVFFromCatalog(this.catalog, ImmutableList.of(ImmutableList.of(function))); } @Override public void removeProcedure(String procedure) { - CatalogOperations.deleteProcedureFromCatalog(this.catalog, List.of(List.of(procedure))); + CatalogOperations.deleteProcedureFromCatalog(this.catalog, ImmutableList.of(ImmutableList.of(procedure))); } @Override diff --git a/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/catalog/io/CatalogResources.java b/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/catalog/io/CatalogResources.java index 61590ef..e894933 100644 --- a/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/catalog/io/CatalogResources.java +++ b/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/catalog/io/CatalogResources.java @@ -16,6 +16,7 @@ package com.google.zetasql.toolkit.catalog.io; +import com.google.common.collect.ImmutableList; import com.google.gson.JsonParseException; import com.google.zetasql.SimpleTable; import com.google.zetasql.toolkit.catalog.FunctionInfo; @@ -45,19 +46,19 @@ public CatalogResources( } public List getTables() { - return tables != null ? tables : List.of(); + return tables != null ? tables : ImmutableList.of(); } public List getFunctions() { - return functions != null ? functions : List.of(); + return functions != null ? functions : ImmutableList.of(); } public List getTVFs() { - return tvfs != null ? tvfs : List.of(); + return tvfs != null ? tvfs : ImmutableList.of(); } public List getProcedures() { - return procedures != null ? procedures : List.of(); + return procedures != null ? procedures : ImmutableList.of(); } /** diff --git a/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/catalog/io/JsonCatalogDeserializer.java b/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/catalog/io/JsonCatalogDeserializer.java index e3d3f68..f4a4abc 100644 --- a/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/catalog/io/JsonCatalogDeserializer.java +++ b/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/catalog/io/JsonCatalogDeserializer.java @@ -167,7 +167,7 @@ private static boolean getFieldAsBoolean( JsonPrimitive primitive = field.getAsJsonPrimitive(); boolean isTrueOrFalseString = primitive.isString() - && List.of("true", "false").contains(primitive.getAsString().toLowerCase()); + && ImmutableList.of("true", "false").contains(primitive.getAsString().toLowerCase()); if (!primitive.isBoolean() && !isTrueOrFalseString) { throw new JsonParseException(errorMessage); @@ -272,7 +272,7 @@ public FunctionInfo deserialize( "Invalid JSON function: " + jsonElement + ". Signatures missing.")); return FunctionInfo.newBuilder() - .setNamePath(List.of(functionName)) + .setNamePath(ImmutableList.of(functionName)) .setGroup("UDF") .setMode(Mode.SCALAR) .setSignatures(Arrays.asList(signatures)) diff --git a/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/catalog/typeparser/ZetaSQLTypeParser.java b/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/catalog/typeparser/ZetaSQLTypeParser.java index 04d8ec6..fa75492 100644 --- a/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/catalog/typeparser/ZetaSQLTypeParser.java +++ b/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/catalog/typeparser/ZetaSQLTypeParser.java @@ -22,6 +22,7 @@ import com.google.zetasql.ZetaSQLType.TypeKind; import com.google.zetasql.toolkit.catalog.typeparser.ZetaSQLTypeGrammarParser.*; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Stack; @@ -83,28 +84,30 @@ public static Type parse(String type) { */ private static class ZetaSQLTypeParserListener extends ZetaSQLTypeGrammarBaseListener { - private static final Map simpleTypeMapping = - Map.ofEntries( - Map.entry("STRING", TypeKind.TYPE_STRING), - Map.entry("BYTES", TypeKind.TYPE_BYTES), - Map.entry("INT32", TypeKind.TYPE_INT32), - Map.entry("INT64", TypeKind.TYPE_INT64), - Map.entry("UINT32", TypeKind.TYPE_UINT32), - Map.entry("UINT64", TypeKind.TYPE_UINT64), - Map.entry("FLOAT64", TypeKind.TYPE_DOUBLE), - Map.entry("DECIMAL", TypeKind.TYPE_NUMERIC), - Map.entry("NUMERIC", TypeKind.TYPE_NUMERIC), - Map.entry("BIGNUMERIC", TypeKind.TYPE_BIGNUMERIC), - Map.entry("INTERVAL", TypeKind.TYPE_INTERVAL), - Map.entry("BOOL", TypeKind.TYPE_BOOL), - Map.entry("TIMESTAMP", TypeKind.TYPE_TIMESTAMP), - Map.entry("DATE", TypeKind.TYPE_DATE), - Map.entry("TIME", TypeKind.TYPE_TIME), - Map.entry("DATETIME", TypeKind.TYPE_DATETIME), - Map.entry("GEOGRAPHY", TypeKind.TYPE_GEOGRAPHY), - Map.entry("JSON", TypeKind.TYPE_JSON)); + private static final Map simpleTypeMapping = new HashMap<>(); private final Stack typeStack = new Stack<>(); private final Stack> structFieldStack = new Stack<>(); + + static { + simpleTypeMapping.put("STRING", TypeKind.TYPE_STRING); + simpleTypeMapping.put("BYTES", TypeKind.TYPE_BYTES); + simpleTypeMapping.put("INT32", TypeKind.TYPE_INT32); + simpleTypeMapping.put("INT64", TypeKind.TYPE_INT64); + simpleTypeMapping.put("UINT32", TypeKind.TYPE_UINT32); + simpleTypeMapping.put("UINT64", TypeKind.TYPE_UINT64); + simpleTypeMapping.put("FLOAT64", TypeKind.TYPE_DOUBLE); + simpleTypeMapping.put("DECIMAL", TypeKind.TYPE_NUMERIC); + simpleTypeMapping.put("NUMERIC", TypeKind.TYPE_NUMERIC); + simpleTypeMapping.put("BIGNUMERIC", TypeKind.TYPE_BIGNUMERIC); + simpleTypeMapping.put("INTERVAL", TypeKind.TYPE_INTERVAL); + simpleTypeMapping.put("BOOL", TypeKind.TYPE_BOOL); + simpleTypeMapping.put("TIMESTAMP", TypeKind.TYPE_TIMESTAMP); + simpleTypeMapping.put("DATE", TypeKind.TYPE_DATE); + simpleTypeMapping.put("TIME", TypeKind.TYPE_TIME); + simpleTypeMapping.put("DATETIME", TypeKind.TYPE_DATETIME); + simpleTypeMapping.put("GEOGRAPHY", TypeKind.TYPE_GEOGRAPHY); + simpleTypeMapping.put("JSON", TypeKind.TYPE_JSON); + } public Type getResult() { return this.typeStack.pop(); diff --git a/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/tools/lineage/ColumnLineageExtractor.java b/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/tools/lineage/ColumnLineageExtractor.java index 97487c0..5e54a2d 100644 --- a/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/tools/lineage/ColumnLineageExtractor.java +++ b/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/tools/lineage/ColumnLineageExtractor.java @@ -16,12 +16,16 @@ package com.google.zetasql.toolkit.tools.lineage; -import com.google.protobuf.ExperimentalApi; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; import com.google.zetasql.Table; +import com.google.zetasql.Type; import com.google.zetasql.resolvedast.ResolvedColumn; import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedColumnRef; import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedCreateTableAsSelectStmt; +import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedCreateViewBase; import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedExpr; +import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedGetStructField; import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedInsertRow; import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedInsertStmt; import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedMergeStmt; @@ -31,115 +35,252 @@ import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedStatement; import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedUpdateItem; import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedUpdateStmt; +import java.util.AbstractMap.SimpleEntry; +import java.util.Collection; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; - +import java.util.stream.IntStream; + +/** + * Implements extraction of column-level lineage from ZetaSQL {@link ResolvedStatement}s. + * Supported statements: + *
    + *
  • CREATE TABLE AS SELECT + *
  • CREATE [MATERIALIZED] VIEW AS SELECT + *
  • INSERT + *
  • UPDATE + *
  • MERGE + *
+ */ public class ColumnLineageExtractor { + private static ColumnLineage buildColumnLineage( + String targetTableName, String targetColumnName, Collection parentColumns) { + ColumnEntity target = new ColumnEntity(targetTableName, targetColumnName); + Set parents = parentColumns + .stream() + .map(ColumnEntity::forResolvedColumn) + .collect(Collectors.toSet()); + return new ColumnLineage(target, parents); + } + + // TODO: expandColumns() is also defined in ParentColumnFinder. Generalize. + private static List expandColumn(ResolvedColumn column) { + Type type = column.getType(); + + if (type.isStruct()) { + return type.asStruct().getFieldList() + .stream() + .map(field -> buildColumnSubfield(column, field.getName(), field.getType())) + .map(ColumnLineageExtractor::expandColumn) + .flatMap(List::stream) + .collect(Collectors.toList()); + } + + return ImmutableList.of(column); + } + + /** + * Extracts the column-level lineage entries for a set of {@link ResolvedOutputColumn}s, + * given the {@link ResolvedStatement} they belong to. + * + * @param targetTableName The name of the table the output columns write to + * @param outputColumns The output columns to find lineage for + * @param statement The ResolvedStatement the output columns belong to + * @return The set of resulting {@link ColumnLineage} objects + */ + private static Set extractColumnLevelLineageForOutputColumns( + String targetTableName, + List outputColumns, + ResolvedStatement statement) { + + HashSet result = new HashSet<>(); + + for (ResolvedOutputColumn outputColumn : outputColumns) { + List expandedResolvedColumns = expandColumn(outputColumn.getColumn()); + + for (ResolvedColumn expandedResolvedColumn : expandedResolvedColumns) { + List parentColumns = + ParentColumnFinder.findParentsForColumn(statement, expandedResolvedColumn); + ColumnLineage lineageEntry = + buildColumnLineage(targetTableName, outputColumn.getName(), parentColumns); + result.add(lineageEntry); + } + } + + return result; + + } + + /** + * Extracts the column-level lineage entries for a {@link ResolvedCreateTableAsSelectStmt} + * + * @param createTableAsSelectStmt The ResolvedCreateTableAsSelectStmt for which to extract lineage + * @return The set of resulting {@link ColumnLineage} objects + */ private static Set extractColumnLevelLineage( ResolvedCreateTableAsSelectStmt createTableAsSelectStmt) { - String tablePath = String.join(".", createTableAsSelectStmt.getNamePath()); + String fullTableName = String.join(".", createTableAsSelectStmt.getNamePath()); List outputColumns = createTableAsSelectStmt.getOutputColumnList(); - Map> outputColumnToParentColumns = outputColumns.stream() - .collect(Collectors.toMap( - ResolvedOutputColumn::getName, - outputColumn -> - ParentColumnFinder.find(createTableAsSelectStmt, outputColumn.getColumn()) - )); + return extractColumnLevelLineageForOutputColumns( + fullTableName, outputColumns, createTableAsSelectStmt); + } - return outputColumnToParentColumns.entrySet() - .stream() - .map(entry -> { - ColumnEntity target = new ColumnEntity(tablePath, entry.getKey()); - Set parents = entry.getValue() - .stream() - .map(ColumnEntity::forResolvedColumn) - .collect(Collectors.toSet()); - return new ColumnLineage(target, parents); - }) - .collect(Collectors.toSet()); + /** + * Extracts the column-level lineage entries for a {@link ResolvedCreateViewBase} statement + * + * @param createViewBase The ResolvedCreateViewBase statement for which to extract lineage + * @return The set of resulting {@link ColumnLineage} objects + */ + private static Set extractColumnLevelLineage( + ResolvedCreateViewBase createViewBase) { + String fullViewName = String.join(".", createViewBase.getNamePath()); + + List outputColumns = createViewBase.getOutputColumnList(); + + return extractColumnLevelLineageForOutputColumns( + fullViewName, outputColumns, createViewBase); } + /** + * Extracts the column-level lineage entries for a {@link ResolvedInsertStmt} + * + * @param insertStmt The ResolvedInsertStmt for which to extract lineage + * @return The set of resulting {@link ColumnLineage} objects + */ private static Set extractColumnLevelLineage(ResolvedInsertStmt insertStmt) { if (Objects.isNull(insertStmt.getQuery())) { // The statement is inserting rows manually using "INSERT INTO ... VALUES ..." // Since it does not query any tables, it does not produce lineage - return Set.of(); + return ImmutableSet.of(); } Table targetTable = insertStmt.getTableScan().getTable(); - List insertedColumns = insertStmt.getInsertColumnList(); ResolvedScan query = insertStmt.getQuery(); + List insertedColumns = insertStmt.getInsertColumnList() + .stream() + .map(ColumnLineageExtractor::expandColumn) + .flatMap(List::stream) + .collect(Collectors.toList()); + List matchingColumnsInQuery = query.getColumnList() + .stream() + .map(ColumnLineageExtractor::expandColumn) + .flatMap(List::stream) + .collect(Collectors.toList()); + + return IntStream.range(0, insertedColumns.size()) + .mapToObj(index -> new SimpleEntry<>( + insertedColumns.get(index), + ParentColumnFinder.findParentsForColumn(insertStmt, matchingColumnsInQuery.get(index)))) + .map(entry -> buildColumnLineage( + targetTable.getFullName(), entry.getKey().getName(), entry.getValue())) + .collect(Collectors.toSet()); + } - Set result = new HashSet<>(insertedColumns.size()); - - for (int i = 0; i < insertedColumns.size(); i++) { - ResolvedColumn insertedColumn = insertedColumns.get(i); - ResolvedColumn matchingColumnInQuery = query.getColumnList().get(i); - List parents = ParentColumnFinder.find(insertStmt, matchingColumnInQuery); + // TODO: buildColumnSubfield() is also defined in ParentColumnFinder. Generalize. + private static ResolvedColumn buildColumnSubfield( + ResolvedColumn column, String fieldName, Type fieldType) { + return new ResolvedColumn( + column.getId(), + column.getTableName(), + column.getName() + "." + fieldName, + fieldType); + } - ColumnEntity target = new ColumnEntity(targetTable.getFullName(), insertedColumn.getName()); - Set parentEntities = parents - .stream() - .map(ColumnEntity::forResolvedColumn) - .collect(Collectors.toSet()); - result.add(new ColumnLineage(target, parentEntities)); + private static Optional resolveUpdateItemTarget(ResolvedExpr updateTarget) { + if (updateTarget instanceof ResolvedColumnRef) { + ResolvedColumnRef columnRef = (ResolvedColumnRef) updateTarget; + return Optional.of(columnRef.getColumn()); + } else if (updateTarget instanceof ResolvedGetStructField) { + ResolvedGetStructField getStructField = (ResolvedGetStructField) updateTarget; + int structFieldIdx = (int) getStructField.getFieldIdx(); + String fieldName = getStructField.getExpr() + .getType() + .asStruct() + .getField(structFieldIdx) + .getName(); + return resolveUpdateItemTarget(getStructField.getExpr()) + .map(target -> buildColumnSubfield(target, fieldName, getStructField.getType())); } - return result; + return Optional.empty(); } - private static Optional extractColumnLevelLineage( + /** + * Extracts the column-level lineage entry for a {@link ResolvedUpdateItem}. ResolvedUpdateItems + * represent a "SET column = expression" clause and are used in UPDATE and MERGE statements. + * + * @param targetTable The {@link Table} this update item writes to + * @param updateItem The ResolvedUpdateItem to return lineage for + * @param originalStatement The {@link ResolvedStatement} the update item belongs to. Used + * * to resolve the parent columns of the update expression. + * @return An optional instance of {@link ColumnLineage}, empty if the update item assigns to + * something other than a column directly. + */ + private static Optional extractColumnLevelLineageForUpdateItem( Table targetTable, - ResolvedStatement originalStatement, - ResolvedUpdateItem updateItem) { + ResolvedUpdateItem updateItem, + ResolvedStatement originalStatement) { - ResolvedExpr target = updateItem.getTarget(); + ResolvedExpr targetExpression = updateItem.getTarget(); ResolvedExpr updateExpression = updateItem.getSetValue().getValue(); - if (!(target instanceof ResolvedColumnRef)) { - // TODO: Do we need to handle other types of inserts? - // See: https://github.com/google/zetasql/blob/5133c6e373a3f67f7f40b0619a2913c3fcab8171/zetasql/resolved_ast/gen_resolved_ast.py#L5564 + Optional maybeTargetColumn = resolveUpdateItemTarget(targetExpression); + + if (!maybeTargetColumn.isPresent()) { return Optional.empty(); } - ResolvedColumnRef targetColumnRef = (ResolvedColumnRef) target; - List parents = ParentColumnFinder.find(originalStatement, updateExpression); + ResolvedColumn targetColumn = maybeTargetColumn.get(); - ColumnEntity targetColumnEntity = - new ColumnEntity(targetTable.getFullName(), targetColumnRef.getColumn().getName()); - Set parentEntities = parents.stream() - .map(ColumnEntity::forResolvedColumn) - .collect(Collectors.toSet()); + List parents = + ParentColumnFinder.findParentsForExpression(originalStatement, updateExpression); - ColumnLineage result = new ColumnLineage(targetColumnEntity, parentEntities); + ColumnLineage result = buildColumnLineage( + targetTable.getFullName(), targetColumn.getName(), parents); return Optional.of(result); } + /** + * Extracts the column-level lineage entries for a {@link ResolvedUpdateStmt} + * + * @param updateStmt The ResolvedUpdateStmt for which to extract lineage + * @return The set of resulting {@link ColumnLineage} objects + */ private static Set extractColumnLevelLineage(ResolvedUpdateStmt updateStmt) { Table targetTable = updateStmt.getTableScan().getTable(); List updateItems = updateStmt.getUpdateItemList(); return updateItems.stream() - .map(updateItem -> extractColumnLevelLineage(targetTable, updateStmt, updateItem)) + .map(updateItem -> + extractColumnLevelLineageForUpdateItem(targetTable, updateItem, updateStmt)) .filter(Optional::isPresent) .map(Optional::get) .collect(Collectors.toSet()); } + /** + * Extracts the column-level lineage entry for a {@link ResolvedMergeWhen}. ResolvedMergeWhens + * represent a "WHEN [NOT] MATCHED [BY SOURCE|TARGET] THEN ..." clause. + * + * @param targetTable The {@link Table} this merge statement item writes to. + * @param mergeWhen The ResolvedMergeWhen to return lineage for. + * @param originalStatement The {@link ResolvedMergeStmt} the ResolvedMergeWhen belongs to. Used + * to resolve the lineage of the INSERT/UPDATE operations the ResolvedMergeWhen contains. + * @return The set of resulting {@link ColumnLineage} objects. + */ private static Set extractColumnLevelLineage( Table targetTable, - ResolvedStatement originalStatement, - ResolvedMergeWhen mergeWhen) { + ResolvedMergeWhen mergeWhen, + ResolvedMergeStmt originalStatement) { List insertedColumns = mergeWhen.getInsertColumnList(); ResolvedInsertRow insertRow = mergeWhen.getInsertRow(); @@ -147,45 +288,59 @@ private static Set extractColumnLevelLineage( if (Objects.nonNull(insertRow)) { // WHEN ... THEN INSERT - Set result = new HashSet<>(insertedColumns.size()); - - for (int i = 0; i < insertedColumns.size(); i++) { - ResolvedColumn insertedColumn = insertedColumns.get(i); - ResolvedExpr rowColumn = insertRow.getValueList().get(i).getValue(); - List parents = ParentColumnFinder.find(originalStatement, rowColumn); - - ColumnEntity target = new ColumnEntity(targetTable.getFullName(), insertedColumn.getName()); - Set parentEntities = parents - .stream() - .map(ColumnEntity::forResolvedColumn) - .collect(Collectors.toSet()); - result.add(new ColumnLineage(target, parentEntities)); - } - - return result; + return IntStream.range(0, insertedColumns.size()) + .mapToObj(index -> new SimpleEntry<>( + insertedColumns.get(index), + insertRow.getValueList().get(index).getValue())) + .map(entry -> new SimpleEntry<>( + entry.getKey(), + ParentColumnFinder.findParentsForExpression(originalStatement, entry.getValue()))) + .map(entry -> buildColumnLineage( + targetTable.getFullName(), entry.getKey().getName(), entry.getValue())) + .collect(Collectors.toSet()); } else if (Objects.nonNull(updateItems)) { // WHEN ... THEN UPDATE return updateItems.stream() - .map(updateItem -> extractColumnLevelLineage(targetTable, originalStatement, updateItem)) + .map(updateItem -> + extractColumnLevelLineageForUpdateItem(targetTable, updateItem, originalStatement)) .filter(Optional::isPresent) .map(Optional::get) .collect(Collectors.toSet()); } - return Set.of(); + return ImmutableSet.of(); } + /** + * Extracts the column-level lineage entries for a {@link ResolvedMergeStmt} + * + * @param mergeStmt The ResolvedMergeStmt for which to extract lineage + * @return The set of resulting {@link ColumnLineage} objects + */ private static Set extractColumnLevelLineage(ResolvedMergeStmt mergeStmt) { Table targetTable = mergeStmt.getTableScan().getTable(); return mergeStmt.getWhenClauseList() .stream() - .map(mergeWhen -> extractColumnLevelLineage(targetTable, mergeStmt, mergeWhen)) + .map(mergeWhen -> extractColumnLevelLineage(targetTable, mergeWhen, mergeStmt)) .flatMap(Set::stream) .collect(Collectors.toSet()); } - @ExperimentalApi + /** + * Extracts the column-level lineage entries for a {@link ResolvedStatement}. + * Supported statements: + *
    + *
  • CREATE TABLE AS SELECT + *
  • CREATE [MATERIALIZED] VIEW AS SELECT + *
  • INSERT + *
  • UPDATE + *
  • MERGE + *
+ * + * @param statement The ResolvedStatement for which to extract lineage + * @return The set of resulting {@link ColumnLineage} objects. Empty for unsupported statements. + */ public static Set extractColumnLevelLineage(ResolvedStatement statement) { if (statement instanceof ResolvedCreateTableAsSelectStmt) { return extractColumnLevelLineage((ResolvedCreateTableAsSelectStmt) statement); @@ -195,9 +350,11 @@ public static Set extractColumnLevelLineage(ResolvedStatement sta return extractColumnLevelLineage((ResolvedUpdateStmt) statement); } else if (statement instanceof ResolvedMergeStmt) { return extractColumnLevelLineage((ResolvedMergeStmt) statement); + } else if (statement instanceof ResolvedCreateViewBase) { + return extractColumnLevelLineage((ResolvedCreateViewBase) statement); } - return Set.of(); + return ImmutableSet.of(); } } diff --git a/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/tools/lineage/ColumnReferenceExtractor.java b/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/tools/lineage/ColumnReferenceExtractor.java deleted file mode 100644 index ab91207..0000000 --- a/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/tools/lineage/ColumnReferenceExtractor.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2023 Google LLC All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zetasql.toolkit.tools.lineage; - -import com.google.zetasql.resolvedast.ResolvedColumn; -import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedColumnRef; -import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedExpr; -import com.google.zetasql.resolvedast.ResolvedNodes.Visitor; -import java.util.ArrayList; -import java.util.List; - -class ColumnReferenceExtractor extends Visitor { - // TODO: Handle different types of expressions. E.g. special cases for functions, - // subqueries, etc. - - private final ArrayList result = new ArrayList<>(); - - public static List extractFromExpression(ResolvedExpr expression) { - ColumnReferenceExtractor extractor = new ColumnReferenceExtractor(); - expression.accept(extractor); - return extractor.result; - } - - public void visit(ResolvedColumnRef columnRef) { - result.add(columnRef.getColumn()); - } - - private ColumnReferenceExtractor() {} - -} diff --git a/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/tools/lineage/ExpressionParentFinder.java b/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/tools/lineage/ExpressionParentFinder.java new file mode 100644 index 0000000..984e179 --- /dev/null +++ b/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/tools/lineage/ExpressionParentFinder.java @@ -0,0 +1,167 @@ +/* + * Copyright 2023 Google LLC All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.zetasql.toolkit.tools.lineage; + +import com.google.common.collect.ImmutableList; +import com.google.zetasql.Function; +import com.google.zetasql.StructType; +import com.google.zetasql.resolvedast.ResolvedColumn; +import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedAggregateFunctionCall; +import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedAnalyticFunctionCall; +import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedCast; +import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedColumnRef; +import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedExpr; +import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedFunctionCall; +import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedFunctionCallBase; +import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedGetStructField; +import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedMakeStruct; +import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedScan; +import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedSubqueryExpr; +import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedWithExpr; +import com.google.zetasql.resolvedast.ResolvedNodes.Visitor; +import com.google.zetasql.resolvedast.ResolvedSubqueryExprEnums.SubqueryType; +import java.util.List; +import java.util.Stack; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +/** + * Implements finding the direct {@link ResolvedColumn} parent of a {@link ResolvedExpr}. + */ +class ExpressionParentFinder extends Visitor { + private final Stack result = new Stack<>(); + public static List findDirectParentsForExpression(ResolvedExpr expression) { + ExpressionParentFinder extractor = new ExpressionParentFinder(); + expression.accept(extractor); + return extractor.result; + } + + public void visit(ResolvedColumnRef columnRef) { + result.push(columnRef.getColumn()); + } + + public void visit(ResolvedWithExpr withExpr) { + withExpr.getExpr().accept(this); + } + + public void visit(ResolvedSubqueryExpr subqueryExpr) { + List scalarOrArray = ImmutableList.of(SubqueryType.SCALAR, SubqueryType.ARRAY); + if (scalarOrArray.contains(subqueryExpr.getSubqueryType())) { + ResolvedScan subquery = subqueryExpr.getSubquery(); + ResolvedColumn subqueryOutputColumn = subquery.getColumnList().get(0); + result.push(subqueryOutputColumn); + } + } + + public void visitResolvedFunctionCallBase(ResolvedFunctionCallBase functionCallBase) { + Function function = functionCallBase.getFunction(); + List arguments = functionCallBase.getArgumentList(); + int numberOfArguments = arguments.size(); + + List expressionsToVisit; + + switch (function.getName().toLowerCase()) { + case "$case_no_value": + // Must keep all odd arguments (the WHEN expressions), plus the last one (the ELSE expr) + expressionsToVisit = IntStream.range(0, numberOfArguments) + .filter(i -> i % 2 == 1 || i == numberOfArguments - 1) + .mapToObj(arguments::get) + .collect(Collectors.toList()); + break; + case "$case_with_value": + // Must keep all even arguments (the WHEN expressions) but the first one (the CASE value), + // plus the last one (the ELSE expr) + expressionsToVisit = IntStream.range(0, numberOfArguments) + .filter(i -> (i != 0 && i % 2 == 0) || i == numberOfArguments - 1) + .mapToObj(arguments::get) + .collect(Collectors.toList()); + break; + case "if": + // Remove the first argument (the condition) + expressionsToVisit = arguments.subList(1, arguments.size()); + break; + case "nullif": + // Keep only the first argument (the value) + expressionsToVisit = ImmutableList.of(arguments.get(0)); + break; + default: + expressionsToVisit = arguments; + } + + expressionsToVisit.forEach(expression -> expression.accept(this)); + } + + public void visit(ResolvedFunctionCall functionCall) { + visitResolvedFunctionCallBase(functionCall); + } + + public void visit(ResolvedAggregateFunctionCall aggregateFunctionCall) { + visitResolvedFunctionCallBase(aggregateFunctionCall); + } + + public void visit(ResolvedAnalyticFunctionCall analyticFunctionCall) { + visitResolvedFunctionCallBase(analyticFunctionCall); + } + + public void visit(ResolvedMakeStruct makeStruct) { + makeStruct.getFieldList().forEach(fieldExpr -> fieldExpr.accept(this)); + } + + public void visit(ResolvedGetStructField getStructField) { + ResolvedExpr structExpression = getStructField.getExpr(); + StructType structExpressionType = structExpression.getType().asStruct(); + int accessedFieldIndex = (int) getStructField.getFieldIdx(); + String accessedFieldName = structExpressionType.getField(accessedFieldIndex).getName(); + + if (structExpression instanceof ResolvedMakeStruct) { + // If the user made a STRUCT and immediately accessed a field in it, only consider the parent + // columns of the corresponding field. + ResolvedMakeStruct makeStructExpression = (ResolvedMakeStruct) structExpression; + StructType makeStructExpressionType = makeStructExpression.getType().asStruct(); + int numberOfFields = makeStructExpressionType.getFieldCount(); + + int fieldIndex = IntStream.range(0, numberOfFields) + .filter(index -> + makeStructExpressionType.getField(index).getName().equals(accessedFieldName)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException( + "Field " + accessedFieldName + " does not exist in STRUCT of type: " + + makeStructExpressionType)); + ResolvedExpr fieldExpression = makeStructExpression.getFieldList().get(fieldIndex); + + List parentColumns = ExpressionParentFinder.findDirectParentsForExpression(fieldExpression); + parentColumns.forEach(result::push); + } else { + structExpression.accept(this); + + ResolvedColumn structColumn = result.pop(); + ResolvedColumn parentColumn = new ResolvedColumn( + structColumn.getId(), + structColumn.getTableName(), + structColumn.getName() + "." + accessedFieldName, + getStructField.getType()); + this.result.push(parentColumn); + } + } + + public void visit(ResolvedCast cast) { + cast.getExpr().accept(this); + } + + private ExpressionParentFinder() {} + +} diff --git a/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/tools/lineage/ParentColumnFinder.java b/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/tools/lineage/ParentColumnFinder.java index 1811522..ee43e46 100644 --- a/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/tools/lineage/ParentColumnFinder.java +++ b/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/tools/lineage/ParentColumnFinder.java @@ -16,14 +16,21 @@ package com.google.zetasql.toolkit.tools.lineage; +import com.google.common.collect.ImmutableList; +import com.google.zetasql.StructType; +import com.google.zetasql.Type; import com.google.zetasql.resolvedast.ResolvedColumn; import com.google.zetasql.resolvedast.ResolvedNode; -import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedAggregateScan; import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedAnalyticScan; +import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedArrayScan; import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedComputedColumn; import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedExpr; -import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedProjectScan; +import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedMakeStruct; +import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedSetOperationItem; +import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedSetOperationScan; import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedStatement; +import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedTVFScan; +import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedTableScan; import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedWithEntry; import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedWithRefScan; import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedWithScan; @@ -31,45 +38,101 @@ import java.util.ArrayDeque; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Queue; +import java.util.Set; +import java.util.Stack; import java.util.stream.Collectors; +/** + * Implements finding the parent columns for other columns or for expressions within a resolved + * statement. Only "terminal" columns are considered to be parents, meaning columns that read + * directly from a table. Intermediate computed columns in the statement are not considered. + * + *

Column A being a parent of column B means the content of A is (either directly or indirectly) + * used to write to B. For example: "SELECT A AS B", "UPDATE ... SET B = UPPER(A)", + * "INSERT INTO t(B) SELECT something FROM (SELECT A AS something)", etc. + * + *

The parent columns of an expression E are the terminal parents of all the columns E uses + * as output. For example, the parents of expression E = "UPPER(A, B)" are A and B. If A or B happen + * to not be terminal columns, the parents of E are the terminal parents of A and B themselves. + * Another example; the parents for "IF(condition, trueCase, falseCase)" are all the parents from + * the trueCase and the falseCase, but not the parents from the condition since the condition + * is not used as output. + * + *

Finding parent columns is implemented by traversing the statement the column or expression + * belongs to. When a {@link ResolvedComputedColumn} node is found; it is registered in the + * columnsToParents Map, together with its direct parents. After traversal, the columnsToParents + * map is used to find all terminal parents for the column or expression in question. + * + *

There's a special case for WITH statements, since each reference to a WITH entry creates + * a new unique set of {@link ResolvedColumn}s instead of referencing the ones created in the WITH + * subquery. While traversing the statement, this visitor maintains a stack of the + * {@link ResolvedWithEntry}s in scope. When visiting a {@link ResolvedWithRefScan} it uses + * those scopes to correlate the ResolvedWithRefScan columns to their parents in the corresponding + * ResolvedWithEntry. + */ class ParentColumnFinder extends Visitor { private final HashMap> columnsToParents = new HashMap<>(); - private final List inScopeWithEntries = new ArrayList<>(); + private final Set terminalColumns = new HashSet<>(); + private final Stack> withEntryScopes = new Stack<>(); + private final Stack columnsBeingComputed = new Stack<>(); private ParentColumnFinder() {} - public static List find(ResolvedStatement statement, ResolvedColumn column) { + /** + * Finds the terminal parents for a particular {@link ResolvedColumn}. + * + * @param statement The {@link ResolvedStatement} the column belongs to. + * @param column The ResolvedColumn to find parents for. + * @return A list of ResolvedColumns containing the terminal parents on the provided column. + */ + public static List findParentsForColumn( + ResolvedStatement statement, ResolvedColumn column) { return new ParentColumnFinder().findImpl(statement, column); } - public static List find(ResolvedStatement statement, ResolvedExpr expression) { + /** + * Finds the terminal parents for a particular {@link ResolvedExpr}. + * + * @param statement The {@link ResolvedStatement} the expression belongs to. + * @param expression The ResolvedExpr to find parents for. + * @return A list of ResolvedColumns containing the terminal parents on the provided expression. + */ + public static List findParentsForExpression( + ResolvedStatement statement, ResolvedExpr expression) { List parentsReferenced = - ColumnReferenceExtractor.extractFromExpression(expression); + ExpressionParentFinder.findDirectParentsForExpression(expression); return parentsReferenced.stream() - .map(parent -> ParentColumnFinder.find(statement, parent)) + .map(parent -> ParentColumnFinder.findParentsForColumn(statement, parent)) .flatMap(List::stream) .collect(Collectors.toList()); } - public List findImpl(ResolvedNode containingNode, ResolvedColumn column) { - containingNode.accept(this); + public List findImpl(ResolvedNode containerNode, ResolvedColumn column) { + // 1. Traverse the containerNode. + // This will populate this.columnsToParents with the ResolvedColumns in the containerNode and + // the direct parents for each of them. + // columnsToParents can be thought of as a tree where the root node is the original + // ResolvedColumn and the leaves are its terminal parents. + containerNode.accept(this); + // 2. Use this.columnsToParents to find the terminal parents for the desired column. + // Traverses the tree-like structured mentioned above using breadth-first search. ArrayList result = new ArrayList<>(); - Queue resolutionQueue = new ArrayDeque<>(List.of(column)); + Queue resolutionQueue = new ArrayDeque<>(ImmutableList.of(column)); while (resolutionQueue.peek() != null) { - ResolvedColumn current = resolutionQueue.remove(); - List parents = columnsToParents.get(current.shortDebugString()); + ResolvedColumn currentColumn = resolutionQueue.remove(); + String currentColumnKey = makeColumnKey(currentColumn); + List parents = getParentsOfColumn(currentColumn); - if (parents == null) { - // If it does not have any parents, it is terminal - result.add(current); + if (parents.isEmpty() && terminalColumns.contains(currentColumnKey)) { + result.add(currentColumn); } else { resolutionQueue.addAll(parents); } @@ -78,58 +141,201 @@ public List findImpl(ResolvedNode containingNode, ResolvedColumn return result; } - public void visit(ResolvedProjectScan projectScan) { - projectScan.getExprList().forEach(computedColumn -> computedColumn.accept(this)); - projectScan.getInputScan().accept(this); + private String makeColumnKey(ResolvedColumn column) { + return String.format("%s.%s#%d", column.getTableName(), column.getName(), column.getId()); + } + + private List getParentsOfColumn(ResolvedColumn column) { + String key = makeColumnKey(column); + return columnsToParents.computeIfAbsent(key, k -> new ArrayList<>()); + } + + private void addParentsToColumn(ResolvedColumn column, List newParents) { + List parents = getParentsOfColumn(column); + parents.addAll(newParents); + } + + private void addParentToColumn(ResolvedColumn column, ResolvedColumn newParent) { + addParentsToColumn(column, ImmutableList.of(newParent)); + } + + private ResolvedColumn buildColumnSubfield( + ResolvedColumn column, String fieldName, Type fieldType) { + return new ResolvedColumn( + column.getId(), + column.getTableName(), + column.getName() + "." + fieldName, + fieldType); + } + + private List expandColumn(ResolvedColumn column) { + ArrayList result = new ArrayList<>(); + result.add(column); + + Type type = column.getType(); + + if (type.isStruct()) { + type.asStruct().getFieldList() + .stream() + .map(field -> buildColumnSubfield(column, field.getName(), field.getType())) + .flatMap(subColumn -> expandColumn(subColumn).stream()) + .forEachOrdered(result::add); + } + + return result; + } + + public void visit(ResolvedComputedColumn computedColumn) { + // When visiting a resolved column, register it in the columnsToParents Map together with + // its direct parents. + + ResolvedColumn column = computedColumn.getColumn(); + ResolvedExpr expression = computedColumn.getExpr(); + + columnsBeingComputed.push(column); + + if (expression instanceof ResolvedMakeStruct) { + expandMakeStruct(column, (ResolvedMakeStruct) expression); + } else { + List expressionParents = ExpressionParentFinder.findDirectParentsForExpression(expression); + columnsBeingComputed.forEach(columnBeingComputed -> + addParentsToColumn(columnBeingComputed, expressionParents)); + } + + columnsBeingComputed.pop(); + } + + public void expandMakeStruct(ResolvedColumn targetColumn, ResolvedMakeStruct makeStruct) { + StructType structType = makeStruct.getType().asStruct(); + int numberOfFields = structType.getFieldCount(); + + for (int i = 0; i < numberOfFields; i++) { + String fieldName = structType.getField(i).getName(); + ResolvedExpr fieldExpression = makeStruct.getFieldList().get(i); + ResolvedColumn fieldColumn = + buildColumnSubfield(targetColumn, fieldName, fieldExpression.getType()); + + columnsBeingComputed.push(fieldColumn); + List expressionParents = + ExpressionParentFinder.findDirectParentsForExpression(fieldExpression); + columnsBeingComputed.forEach(columnBeingComputed -> + addParentsToColumn(columnBeingComputed, expressionParents)); + columnsBeingComputed.pop(); + + if (fieldExpression instanceof ResolvedMakeStruct) { + expandMakeStruct(fieldColumn, (ResolvedMakeStruct) fieldExpression); + } + } + } + + private void registerTerminalColumns(List newTerminalColumns) { + newTerminalColumns.stream() + .map(this::expandColumn) + .flatMap(List::stream) + .map(this::makeColumnKey) + .forEach(terminalColumns::add); } - public void visit(ResolvedAggregateScan aggregateScan) { - aggregateScan.getGroupByList().forEach(computedColumn -> computedColumn.accept(this)); - aggregateScan.getAggregateList().forEach(computedColumn -> computedColumn.accept(this)); - aggregateScan.getInputScan().accept(this); + public void visit(ResolvedTableScan tableScan) { + registerTerminalColumns(tableScan.getColumnList()); } - public void visit(ResolvedAnalyticScan analyticScan) { - analyticScan.getFunctionGroupList() - .forEach(analyticFunctionGroup -> analyticFunctionGroup.accept(this)); - analyticScan.getInputScan().accept(this); + public void visit(ResolvedTVFScan tvfScan) { + registerTerminalColumns(tvfScan.getColumnList()); } public void visit(ResolvedWithScan withScan) { - this.inScopeWithEntries.addAll(withScan.getWithEntryList()); + // When visiting a WITH scan, push the WITH entries to the scope stack + // and traverse the scan body + withEntryScopes.push(withScan.getWithEntryList()); withScan.getWithEntryList().forEach(withEntry -> withEntry.accept(this)); withScan.getQuery().accept(this); - this.inScopeWithEntries.removeAll(withScan.getWithEntryList()); + + // The WITH entries go out of scope when we exit the WITH scan + withEntryScopes.pop(); + } + + private Optional findInScopeWithEntryByName(String name) { + Optional maybeWithEntry = Optional.empty(); + + // Traverse the scopes stack top-to-bottom and use the first matching WITH in scope + for (int i = withEntryScopes.size() - 1; i >= 0; i--) { + List inScopeWithEntries = withEntryScopes.get(i); + + maybeWithEntry = inScopeWithEntries.stream() + .filter(withEntry -> withEntry.getWithQueryName().equalsIgnoreCase(name)) + .findFirst(); + + if (maybeWithEntry.isPresent()) { + break; + } + } + + return maybeWithEntry; } public void visit(ResolvedWithRefScan withRefScan) { - Optional maybeWithEntry = inScopeWithEntries.stream() - .filter(withEntry -> withEntry.getWithQueryName().equals(withRefScan.getWithQueryName())) - .findFirst(); + // WithRefScans create new ResolvedColumns for each column in the WITH entry instead + // of referencing the WITH entry directly. + // Here we find the corresponding with entry which is in scope and register the original + // WITH entry columns a parents of their corresponding columns in the WithRefScan. + Optional maybeWithEntry = + findInScopeWithEntryByName(withRefScan.getWithQueryName()); - if (maybeWithEntry.isEmpty()) { + if (!maybeWithEntry.isPresent()) { + // Should never happen, since the query would be invalid. return; } ResolvedWithEntry withEntry = maybeWithEntry.get(); + // Columns in the WITH entry and the WithRefScan map 1:1. + // Register each column in the WITH entry as a parent of its corresponding column in this + // WithRefScan + // If a column is a STRUCT, also register the 1:1 parent relationship between fields for (int i = 0; i < withRefScan.getColumnList().size(); i++) { ResolvedColumn withRefScanColumn = withRefScan.getColumnList().get(i); ResolvedColumn matchingWithEntryColumn = withEntry.getWithSubquery().getColumnList().get(i); - List parentsToWithRefScanColumn = columnsToParents.computeIfAbsent( - withRefScanColumn.shortDebugString(), key -> new ArrayList<>()); - parentsToWithRefScanColumn.add(matchingWithEntryColumn); + + List expandedRefScanColumn = expandColumn(withRefScanColumn); + List expandedMatchingWithEntryColumn = expandColumn(matchingWithEntryColumn); + + for (int j = 0; j < expandedRefScanColumn.size(); j++) { + addParentToColumn(expandedRefScanColumn.get(j), expandedMatchingWithEntryColumn.get(j)); + } + } } - public void visit(ResolvedComputedColumn computedColumn) { - ResolvedColumn column = computedColumn.getColumn(); - List knownColumnParents = columnsToParents.computeIfAbsent( - column.shortDebugString(), key -> new ArrayList<>()); - List expressionParents = - ColumnReferenceExtractor.extractFromExpression(computedColumn.getExpr()); - knownColumnParents.addAll(expressionParents); + public void visit(ResolvedArrayScan arrayScan) { + ResolvedColumn elementColumn = arrayScan.getElementColumn(); + columnsBeingComputed.push(elementColumn); + arrayScan.getArrayExpr().accept(this); + columnsBeingComputed.pop(); + + if (arrayScan.getInputScan() != null) { + arrayScan.getInputScan().accept(this); + } + } + + public void visit(ResolvedSetOperationScan setOperationScan) { + List generatedColumns = setOperationScan.getColumnList(); + List setOperationItems = setOperationScan.getInputItemList(); + + for (int i = 0; i < generatedColumns.size(); i++) { + int columnIndex = i; + ResolvedColumn generatedColumn = generatedColumns.get(columnIndex); + List parentColumns = setOperationItems.stream() + .map(ResolvedSetOperationItem::getOutputColumnList) + .map(outputColumnList -> outputColumnList.get(columnIndex)) + .collect(Collectors.toList()); + addParentsToColumn(generatedColumn, parentColumns); + } + + setOperationItems.stream() + .map(ResolvedSetOperationItem::getScan) + .forEach(innerScan -> innerScan.accept(this)); } } diff --git a/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/usage/UsageTracking.java b/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/usage/UsageTracking.java index 401f49f..61f79ed 100644 --- a/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/usage/UsageTracking.java +++ b/zetasql-toolkit-core/src/main/java/com/google/zetasql/toolkit/usage/UsageTracking.java @@ -21,6 +21,7 @@ import com.google.common.collect.ImmutableMap; import java.io.IOException; import java.io.InputStream; +import java.util.Objects; import java.util.Properties; public class UsageTracking { @@ -32,15 +33,15 @@ public class UsageTracking { static { String revision = "UNSET"; - InputStream propertiesInputStream = - UsageTracking.class.getResourceAsStream("/zetasql-toolkit-core.properties"); + try (InputStream propertiesInputStream = + UsageTracking.class.getResourceAsStream("/zetasql-toolkit-core.properties")) { - try (propertiesInputStream) { - Properties properties = new Properties(); - properties.load(propertiesInputStream); - revision = properties.getProperty("zetasql.toolkit.version", "UNSET"); - } catch (IOException ignored) { - } + if (Objects.nonNull(propertiesInputStream)) { + Properties properties = new Properties(); + properties.load(propertiesInputStream); + revision = properties.getProperty("zetasql.toolkit.version", "UNSET"); + } + } catch (IOException ignored) {} CURRENT_REVISION = revision; USER_AGENT_VALUE = String.format("google-pso-tool/zetasql-helper/%s", revision); diff --git a/zetasql-toolkit-core/src/test/java/com/google/zetasql/toolkit/AnalyzerExtensionsTest.java b/zetasql-toolkit-core/src/test/java/com/google/zetasql/toolkit/AnalyzerExtensionsTest.java index ffbc448..77d6d29 100644 --- a/zetasql-toolkit-core/src/test/java/com/google/zetasql/toolkit/AnalyzerExtensionsTest.java +++ b/zetasql-toolkit-core/src/test/java/com/google/zetasql/toolkit/AnalyzerExtensionsTest.java @@ -18,6 +18,7 @@ import static org.junit.jupiter.api.Assertions.*; +import com.google.common.collect.ImmutableList; import com.google.zetasql.LanguageOptions; import com.google.zetasql.ParseResumeLocation; import java.util.List; @@ -36,7 +37,7 @@ public class AnalyzerExtensionsTest { void testExtractFunctionNamesFromStatement() { String query = "SELECT f1(), c.f2(), `c.f3`();"; - List> expected = List.of(List.of("f1"), List.of("c", "f2"), List.of("c.f3")); + List> expected = ImmutableList.of(ImmutableList.of("f1"), ImmutableList.of("c", "f2"), ImmutableList.of("c.f3")); List> extractedFunctions = AnalyzerExtensions.extractFunctionNamesFromStatement(query, languageOptions); @@ -48,7 +49,7 @@ void testExtractFunctionNamesFromStatement() { void testExtractFunctionNamesFromScript() { String script = "INSERT INTO t(column) VALUES (f1(1));\n" + "SELECT c.f2(column) FROM t;\n"; - List> expected = List.of(List.of("f1"), List.of("c", "f2")); + List> expected = ImmutableList.of(ImmutableList.of("f1"), ImmutableList.of("c", "f2")); List> extractedFunctions = AnalyzerExtensions.extractFunctionNamesFromScript(script, languageOptions); @@ -62,7 +63,7 @@ void testExtractFunctionNamesFromNextStatement() { ParseResumeLocation parseResumeLocation = new ParseResumeLocation(script); - List> expected = List.of(List.of("f1")); + List> expected = ImmutableList.of(ImmutableList.of("f1")); List> extractedFunctions = AnalyzerExtensions.extractFunctionNamesFromNextStatement( @@ -75,7 +76,7 @@ void testExtractFunctionNamesFromNextStatement() { void testExtractTVFNamesFromStatement() { String query = "SELECT * FROM f1(), c.f2(), `c.f3`();"; - List> expected = List.of(List.of("f1"), List.of("c", "f2"), List.of("c.f3")); + List> expected = ImmutableList.of(ImmutableList.of("f1"), ImmutableList.of("c", "f2"), ImmutableList.of("c.f3")); List> extractedFunctions = AnalyzerExtensions.extractTVFNamesFromStatement(query, languageOptions); @@ -88,7 +89,7 @@ void testExtractTVFNamesFromScript() { String script = "INSERT INTO t(column) SELECT column FROM f1(1);\n" + "SELECT * FROM c.f2(column);\n"; - List> expected = List.of(List.of("f1"), List.of("c", "f2")); + List> expected = ImmutableList.of(ImmutableList.of("f1"), ImmutableList.of("c", "f2")); List> extractedFunctions = AnalyzerExtensions.extractTVFNamesFromScript(script, languageOptions); @@ -103,7 +104,7 @@ void testExtractTVFNamesFromNextStatement() { ParseResumeLocation parseResumeLocation = new ParseResumeLocation(script); - List> expected = List.of(List.of("f1")); + List> expected = ImmutableList.of(ImmutableList.of("f1")); List> extractedFunctions = AnalyzerExtensions.extractTVFNamesFromNextStatement(parseResumeLocation, languageOptions); @@ -115,7 +116,7 @@ void testExtractTVFNamesFromNextStatement() { void testExtractProcedureNamesFromStatement() { String query = "CALL p1();"; - List> expected = List.of(List.of("p1")); + List> expected = ImmutableList.of(ImmutableList.of("p1")); List> extractedProcedures = AnalyzerExtensions.extractProcedureNamesFromStatement(query, languageOptions); @@ -127,7 +128,7 @@ void testExtractProcedureNamesFromStatement() { void testExtractProcedureNamesFromScript() { String script = "CALL c.p1(); CALL `c.p2`();"; - List> expected = List.of(List.of("c", "p1"), List.of("c.p2")); + List> expected = ImmutableList.of(ImmutableList.of("c", "p1"), ImmutableList.of("c.p2")); List> extractedProcedures = AnalyzerExtensions.extractProcedureNamesFromScript(script, languageOptions); @@ -141,7 +142,7 @@ void testExtractProcedureNamesFromNextStatement() { ParseResumeLocation parseResumeLocation = new ParseResumeLocation(query); - List> expected = List.of(List.of("p1")); + List> expected = ImmutableList.of(ImmutableList.of("p1")); List> extractedProcedures = AnalyzerExtensions.extractProcedureNamesFromNextStatement( diff --git a/zetasql-toolkit-core/src/test/java/com/google/zetasql/toolkit/CatalogUpdaterVisitorTest.java b/zetasql-toolkit-core/src/test/java/com/google/zetasql/toolkit/CatalogUpdaterVisitorTest.java index ed99a92..f380e9b 100644 --- a/zetasql-toolkit-core/src/test/java/com/google/zetasql/toolkit/CatalogUpdaterVisitorTest.java +++ b/zetasql-toolkit-core/src/test/java/com/google/zetasql/toolkit/CatalogUpdaterVisitorTest.java @@ -53,7 +53,7 @@ public class CatalogUpdaterVisitorTest { SimpleTable exampleTable = new SimpleTable( "table", - List.of( + ImmutableList.of( new SimpleColumn( "table", "column", TypeFactory.createSimpleType(TypeKind.TYPE_STRING)))); @@ -74,11 +74,11 @@ private void assertTableEqualsExample(SimpleTable table) { void testCreateTableStmt() { ResolvedCreateTableStmt resolvedCreateTableStmt = ResolvedCreateTableStmt.builder() - .setNamePath(List.of("table")) + .setNamePath(ImmutableList.of("table")) .setCreateScope(CreateScope.CREATE_DEFAULT_SCOPE) .setCreateMode(CreateMode.CREATE_DEFAULT) .setColumnDefinitionList( - List.of( + ImmutableList.of( ResolvedColumnDefinition.builder() .setName("column") .setType(TypeFactory.createSimpleType(TypeKind.TYPE_STRING)) @@ -107,19 +107,19 @@ void testCreateTableAsSelectStmt() { ResolvedCreateTableAsSelectStmt resolvedCreateTableAsSelectStmt = ResolvedCreateTableAsSelectStmt.builder() - .setNamePath(List.of("table")) + .setNamePath(ImmutableList.of("table")) .setCreateScope(CreateScope.CREATE_DEFAULT_SCOPE) .setCreateMode(CreateMode.CREATE_DEFAULT) .setColumnDefinitionList( - List.of( + ImmutableList.of( ResolvedColumnDefinition.builder() .setName("column") .setType(TypeFactory.createSimpleType(TypeKind.TYPE_STRING)) .setIsHidden(false) .build())) - .setOutputColumnList(List.of(resolvedOutputColumn)) + .setOutputColumnList(ImmutableList.of(resolvedOutputColumn)) .setQuery( - ResolvedSingleRowScan.builder().setColumnList(List.of(resolvedColumn)).build()) + ResolvedSingleRowScan.builder().setColumnList(ImmutableList.of(resolvedColumn)).build()) .setIsValueTable(false) .build(); @@ -137,11 +137,11 @@ void testCreateTableAsSelectStmt() { void testCreateExternalTableStmt() { ResolvedCreateExternalTableStmt resolvedCreateExternalTableStmt = ResolvedCreateExternalTableStmt.builder() - .setNamePath(List.of("table")) + .setNamePath(ImmutableList.of("table")) .setCreateScope(CreateScope.CREATE_DEFAULT_SCOPE) .setCreateMode(CreateMode.CREATE_DEFAULT) .setColumnDefinitionList( - List.of( + ImmutableList.of( ResolvedColumnDefinition.builder() .setName("column") .setType(TypeFactory.createSimpleType(TypeKind.TYPE_STRING)) @@ -170,12 +170,12 @@ void testCreateViewStmt() { ResolvedCreateViewStmt resolvedCreateViewStmt = ResolvedCreateViewStmt.builder() - .setNamePath(List.of("table")) + .setNamePath(ImmutableList.of("table")) .setCreateScope(CreateScope.CREATE_DEFAULT_SCOPE) .setCreateMode(CreateMode.CREATE_DEFAULT) - .setOutputColumnList(List.of(resolvedOutputColumn)) + .setOutputColumnList(ImmutableList.of(resolvedOutputColumn)) .setQuery( - ResolvedSingleRowScan.builder().setColumnList(List.of(resolvedColumn)).build()) + ResolvedSingleRowScan.builder().setColumnList(ImmutableList.of(resolvedColumn)).build()) .setIsValueTable(false) .setHasExplicitColumns(true) .setRecursive(false) @@ -201,12 +201,12 @@ void testCreateMaterializedViewStmt() { ResolvedCreateMaterializedViewStmt resolvedCreateMaterializedViewStmt = ResolvedCreateMaterializedViewStmt.builder() - .setNamePath(List.of("table")) + .setNamePath(ImmutableList.of("table")) .setCreateScope(CreateScope.CREATE_DEFAULT_SCOPE) .setCreateMode(CreateMode.CREATE_DEFAULT) - .setOutputColumnList(List.of(resolvedOutputColumn)) + .setOutputColumnList(ImmutableList.of(resolvedOutputColumn)) .setQuery( - ResolvedSingleRowScan.builder().setColumnList(List.of(resolvedColumn)).build()) + ResolvedSingleRowScan.builder().setColumnList(ImmutableList.of(resolvedColumn)).build()) .setIsValueTable(false) .setHasExplicitColumns(true) .setRecursive(false) @@ -227,15 +227,15 @@ void testCreateFunction() { FunctionSignature signature = new FunctionSignature( new FunctionArgumentType(TypeFactory.createSimpleType(TypeKind.TYPE_STRING)), - List.of(new FunctionArgumentType(TypeFactory.createSimpleType(TypeKind.TYPE_INT64))), + ImmutableList.of(new FunctionArgumentType(TypeFactory.createSimpleType(TypeKind.TYPE_INT64))), -1); Function expectedFunction = - new Function(List.of("function"), "UDF", Mode.SCALAR, List.of(signature)); + new Function(ImmutableList.of("function"), "UDF", Mode.SCALAR, ImmutableList.of(signature)); ResolvedCreateFunctionStmt resolvedCreateFunctionStmt = ResolvedCreateFunctionStmt.builder() - .setNamePath(List.of("function")) + .setNamePath(ImmutableList.of("function")) .setCreateScope(CreateScope.CREATE_DEFAULT_SCOPE) .setCreateMode(CreateMode.CREATE_DEFAULT) .setSignature(signature) @@ -269,12 +269,12 @@ void testCreateTVF() { FunctionSignature signature = new FunctionSignature( new FunctionArgumentType(SignatureArgumentKind.ARG_TYPE_RELATION), - List.of(new FunctionArgumentType(TypeFactory.createSimpleType(TypeKind.TYPE_INT64))), + ImmutableList.of(new FunctionArgumentType(TypeFactory.createSimpleType(TypeKind.TYPE_INT64))), -1); TVFRelation tvfOutputSchema = TVFRelation.createColumnBased( - List.of(Column.create("output", TypeFactory.createSimpleType(TypeKind.TYPE_STRING)))); + ImmutableList.of(Column.create("output", TypeFactory.createSimpleType(TypeKind.TYPE_STRING)))); TVFInfo expectedFunction = TVFInfo.newBuilder() @@ -285,11 +285,11 @@ void testCreateTVF() { ResolvedCreateTableFunctionStmt resolvedCreateTableFunctionStmt = ResolvedCreateTableFunctionStmt.builder() - .setNamePath(List.of("tvf")) + .setNamePath(ImmutableList.of("tvf")) .setCreateScope(CreateScope.CREATE_DEFAULT_SCOPE) .setCreateMode(CreateMode.CREATE_DEFAULT) .setOutputColumnList( - List.of( + ImmutableList.of( ResolvedOutputColumn.builder() .setName("output") .setColumn( @@ -325,7 +325,7 @@ void testDropStmt() { ResolvedDropStmt dropStmt = ResolvedDropStmt.builder() .setDropMode(ResolvedDropStmtEnums.DropMode.DROP_MODE_UNSPECIFIED) - .setNamePath(List.of("dataset", "table")) + .setNamePath(ImmutableList.of("dataset", "table")) .setObjectType("TABLE") .setIsIfExists(false) .build(); diff --git a/zetasql-toolkit-core/src/test/java/com/google/zetasql/toolkit/CoercerTest.java b/zetasql-toolkit-core/src/test/java/com/google/zetasql/toolkit/CoercerTest.java index 1351e2e..40caff2 100644 --- a/zetasql-toolkit-core/src/test/java/com/google/zetasql/toolkit/CoercerTest.java +++ b/zetasql-toolkit-core/src/test/java/com/google/zetasql/toolkit/CoercerTest.java @@ -18,13 +18,13 @@ import static org.junit.jupiter.api.Assertions.*; +import com.google.common.collect.ImmutableList; import com.google.zetasql.LanguageOptions; import com.google.zetasql.StructType.StructField; import com.google.zetasql.Type; import com.google.zetasql.TypeFactory; import com.google.zetasql.ZetaSQLOptions.LanguageFeature; import com.google.zetasql.ZetaSQLType.TypeKind; -import java.util.List; import org.junit.jupiter.api.Test; public class CoercerTest { @@ -100,10 +100,10 @@ void testArrayCoercions() { @Test void testStructCoercions() { - Type structWithInt32Field = TypeFactory.createStructType(List.of( + Type structWithInt32Field = TypeFactory.createStructType(ImmutableList.of( new StructField("field", TypeFactory.createSimpleType(TypeKind.TYPE_INT32)) )); - Type structWithInt64Field = TypeFactory.createStructType(List.of( + Type structWithInt64Field = TypeFactory.createStructType(ImmutableList.of( new StructField("field", TypeFactory.createSimpleType(TypeKind.TYPE_INT64)) )); @@ -120,12 +120,12 @@ void testStructCoercions() { @Test void testComplexCoercions() { Type int32StructArray = TypeFactory.createArrayType( - TypeFactory.createStructType(List.of( + TypeFactory.createStructType(ImmutableList.of( new StructField("field", TypeFactory.createSimpleType(TypeKind.TYPE_INT32)) )) ); Type int64StructArray = TypeFactory.createArrayType( - TypeFactory.createStructType(List.of( + TypeFactory.createStructType(ImmutableList.of( new StructField("field", TypeFactory.createSimpleType(TypeKind.TYPE_INT64)) )) ); diff --git a/zetasql-toolkit-core/src/test/java/com/google/zetasql/toolkit/catalog/CatalogOperationsTest.java b/zetasql-toolkit-core/src/test/java/com/google/zetasql/toolkit/catalog/CatalogOperationsTest.java index 5638ea7..ece9b01 100644 --- a/zetasql-toolkit-core/src/test/java/com/google/zetasql/toolkit/catalog/CatalogOperationsTest.java +++ b/zetasql-toolkit-core/src/test/java/com/google/zetasql/toolkit/catalog/CatalogOperationsTest.java @@ -36,26 +36,26 @@ private SimpleCatalog createSampleCatalog(String name) { SimpleTable sampleTable = new SimpleTable( "sample", - List.of( + ImmutableList.of( new SimpleColumn( "sample", "column", TypeFactory.createSimpleType(TypeKind.TYPE_STRING)))); catalog.addSimpleTable(sampleTable); Function function = new Function( - List.of("function"), + ImmutableList.of("function"), "UDF", ZetaSQLFunctions.FunctionEnums.Mode.SCALAR, - List.of( + ImmutableList.of( new FunctionSignature( new FunctionArgumentType(TypeFactory.createSimpleType(TypeKind.TYPE_STRING)), - List.of(), + ImmutableList.of(), -1))); catalog.addFunction(function); TVFRelation tvfOutputSchema = TVFRelation.createColumnBased( - List.of( + ImmutableList.of( TVFRelation.Column.create( "output", TypeFactory.createSimpleType(TypeKind.TYPE_STRING)))); TableValuedFunction tvf = @@ -68,17 +68,17 @@ private SimpleCatalog createSampleCatalog(String name) { .setRelationInputSchema(tvfOutputSchema) .build(), 1), - List.of(), + ImmutableList.of(), -1), tvfOutputSchema); catalog.addTableValuedFunction(tvf); Procedure procedure = new Procedure( - List.of("procedure"), + ImmutableList.of("procedure"), new FunctionSignature( new FunctionArgumentType(ZetaSQLFunctions.SignatureArgumentKind.ARG_TYPE_VOID), - List.of(), + ImmutableList.of(), -1)); catalog.addProcedure(procedure); @@ -107,14 +107,14 @@ void testCreateTableInCatalog() { SimpleTable newTable = new SimpleTable( tableName, - List.of( + ImmutableList.of( new SimpleColumn( tableName, "column", TypeFactory.createSimpleType(TypeKind.TYPE_STRING)))); newTable.setFullName(fullTableName); - List newTablePath1 = List.of("newTable"); - List newTablePath2 = List.of("qualified", "newTable"); - List> newTablePaths = List.of(newTablePath1, newTablePath2); + List newTablePath1 = ImmutableList.of("newTable"); + List newTablePath2 = ImmutableList.of("qualified", "newTable"); + List> newTablePaths = ImmutableList.of(newTablePath1, newTablePath2); CatalogOperations.createTableInCatalog( this.testCatalog, @@ -131,10 +131,10 @@ void testCreateTableInCatalog() { @Test void testDeleteTableFromCatalog() { - List sampleTablePath = List.of("sample"); - List nestedSampleTablePath = List.of("nested", "sample"); + List sampleTablePath = ImmutableList.of("sample"); + List nestedSampleTablePath = ImmutableList.of("nested", "sample"); - List> tablePathsToDelete = List.of(sampleTablePath, nestedSampleTablePath); + List> tablePathsToDelete = ImmutableList.of(sampleTablePath, nestedSampleTablePath); CatalogOperations.deleteTableFromCatalog(this.testCatalog, tablePathsToDelete); assertAll( @@ -152,18 +152,18 @@ void testTableAlreadyExists() { SimpleTable newTable = new SimpleTable( tableName, - List.of( + ImmutableList.of( new SimpleColumn( tableName, "column", TypeFactory.createSimpleType(TypeKind.TYPE_INT64)))); - List tablePath = List.of("sample"); + List tablePath = ImmutableList.of("sample"); assertThrows( CatalogResourceAlreadyExists.class, () -> CatalogOperations.createTableInCatalog( this.testCatalog, - List.of(tablePath), + ImmutableList.of(tablePath), "sample", newTable.getColumnList(), CreateMode.CREATE_DEFAULT)); @@ -175,15 +175,15 @@ void testReplaceTable() { SimpleTable newTable = new SimpleTable( tableName, - List.of( + ImmutableList.of( new SimpleColumn( tableName, "column", TypeFactory.createSimpleType(TypeKind.TYPE_INT64)))); - List tablePath = List.of("sample"); + List tablePath = ImmutableList.of("sample"); CatalogOperations.createTableInCatalog( this.testCatalog, - List.of(tablePath), + ImmutableList.of(tablePath), "sample", newTable.getColumnList(), CreateMode.CREATE_OR_REPLACE); @@ -203,17 +203,17 @@ void testCreateTableIfNotExists_ExistingTable() throws NotFoundException { SimpleTable newTable = new SimpleTable( tableName, - List.of( + ImmutableList.of( new SimpleColumn( tableName, "column", TypeFactory.createSimpleType(TypeKind.TYPE_INT64)))); - List sampleTablePath = List.of("sample"); + List sampleTablePath = ImmutableList.of("sample"); Table originalTable = this.testCatalog.findTable(sampleTablePath); CatalogOperations.createTableInCatalog( this.testCatalog, - List.of(sampleTablePath), + ImmutableList.of(sampleTablePath), "sample", newTable.getColumnList(), CreateMode.CREATE_IF_NOT_EXISTS); @@ -236,15 +236,15 @@ void testCreateTableIfNotExists_NewTable() { SimpleTable newTable = new SimpleTable( tableName, - List.of( + ImmutableList.of( new SimpleColumn( tableName, "column", TypeFactory.createSimpleType(TypeKind.TYPE_INT64)))); - List newTablePath = List.of("newTable"); + List newTablePath = ImmutableList.of("newTable"); CatalogOperations.createTableInCatalog( this.testCatalog, - List.of(newTablePath), + ImmutableList.of(newTablePath), "newTable", newTable.getColumnList(), CreateMode.CREATE_IF_NOT_EXISTS); @@ -268,21 +268,21 @@ private void assertFunctionDoesNotExist(SimpleCatalog catalog, String fullName, void testCreateFunctionInCatalog() { FunctionInfo newFunction = FunctionInfo.newBuilder() - .setNamePath(List.of("newFunction")) + .setNamePath(ImmutableList.of("newFunction")) .setGroup("UDF") .setMode(ZetaSQLFunctions.FunctionEnums.Mode.SCALAR) .setSignatures( - List.of( + ImmutableList.of( new FunctionSignature( new FunctionArgumentType( TypeFactory.createSimpleType(TypeKind.TYPE_STRING)), - List.of(), + ImmutableList.of(), -1))) .build(); - List newFunctionPath1 = List.of("newFunction"); - List newFunctionPath2 = List.of("qualified", "newFunction"); - List> newFunctionPaths = List.of(newFunctionPath1, newFunctionPath2); + List newFunctionPath1 = ImmutableList.of("newFunction"); + List newFunctionPath2 = ImmutableList.of("qualified", "newFunction"); + List> newFunctionPaths = ImmutableList.of(newFunctionPath1, newFunctionPath2); CatalogOperations.createFunctionInCatalog( this.testCatalog, newFunctionPaths, newFunction, CreateMode.CREATE_DEFAULT); @@ -304,11 +304,11 @@ void testCreateFunctionInCatalog() { @Test void testDeleteFunctionFromCatalog() { - List sampleFunctionPath = List.of("function"); - List nestedSampleFunctionPath = List.of("nested", "function"); + List sampleFunctionPath = ImmutableList.of("function"); + List nestedSampleFunctionPath = ImmutableList.of("nested", "function"); List> functionPathsToDelete = - List.of(sampleFunctionPath, nestedSampleFunctionPath); + ImmutableList.of(sampleFunctionPath, nestedSampleFunctionPath); CatalogOperations.deleteFunctionFromCatalog(this.testCatalog, functionPathsToDelete); assertAll( @@ -343,16 +343,16 @@ void testCreateTVFInCatalog() { new FunctionSignature( new FunctionArgumentType( ZetaSQLFunctions.SignatureArgumentKind.ARG_TYPE_RELATION), - List.of(), + ImmutableList.of(), -1)) .setOutputSchema( TVFRelation.createValueTableBased( TypeFactory.createSimpleType(TypeKind.TYPE_STRING))) .build(); - List newFunctionPath1 = List.of("newTVF"); - List newFunctionPath2 = List.of("qualified", "newTVF"); - List> newFunctionPaths = List.of(newFunctionPath1, newFunctionPath2); + List newFunctionPath1 = ImmutableList.of("newTVF"); + List newFunctionPath2 = ImmutableList.of("qualified", "newTVF"); + List> newFunctionPaths = ImmutableList.of(newFunctionPath1, newFunctionPath2); CatalogOperations.createTVFInCatalog( this.testCatalog, newFunctionPaths, newTVF, CreateMode.CREATE_DEFAULT); @@ -372,11 +372,11 @@ void testCreateTVFInCatalog() { @Test void testDeleteTVFFromCatalog() { - List sampleFunctionPath = List.of("tvf"); - List nestedSampleFunctionPath = List.of("nested", "tvf"); + List sampleFunctionPath = ImmutableList.of("tvf"); + List nestedSampleFunctionPath = ImmutableList.of("nested", "tvf"); List> functionPathsToDelete = - List.of(sampleFunctionPath, nestedSampleFunctionPath); + ImmutableList.of(sampleFunctionPath, nestedSampleFunctionPath); CatalogOperations.deleteTVFFromCatalog(this.testCatalog, functionPathsToDelete); assertAll( @@ -407,12 +407,12 @@ void testCreateProcedureInCatalog() { ImmutableList.of("newProcedure"), new FunctionSignature( new FunctionArgumentType(ZetaSQLFunctions.SignatureArgumentKind.ARG_TYPE_VOID), - List.of(), + ImmutableList.of(), -1)); - List newProcedurePath1 = List.of("newProcedure"); - List newProcedurePath2 = List.of("qualified", "newProcedure"); - List> newProcedurePaths = List.of(newProcedurePath1, newProcedurePath2); + List newProcedurePath1 = ImmutableList.of("newProcedure"); + List newProcedurePath2 = ImmutableList.of("qualified", "newProcedure"); + List> newProcedurePaths = ImmutableList.of(newProcedurePath1, newProcedurePath2); CatalogOperations.createProcedureInCatalog( this.testCatalog, newProcedurePaths, newProcedure, CreateMode.CREATE_DEFAULT); @@ -428,10 +428,10 @@ void testCreateProcedureInCatalog() { @Test void testDeleteProcedureFromCatalog() { - List sampleProcedurePath = List.of("procedure"); - List nestedSampleProcedurePath = List.of("nested", "procedure"); + List sampleProcedurePath = ImmutableList.of("procedure"); + List nestedSampleProcedurePath = ImmutableList.of("nested", "procedure"); - List> pathsToDelete = List.of(sampleProcedurePath, nestedSampleProcedurePath); + List> pathsToDelete = ImmutableList.of(sampleProcedurePath, nestedSampleProcedurePath); CatalogOperations.deleteProcedureFromCatalog(this.testCatalog, pathsToDelete); assertAll( @@ -449,8 +449,8 @@ void testDeleteProcedureFromCatalog() { void testCopyCatalog() { SimpleCatalog copiedCatalog = CatalogOperations.copyCatalog(this.testCatalog); - List sampleTablePath = List.of("sample"); - List nestedTablePath = List.of("nested", "sample"); + List sampleTablePath = ImmutableList.of("sample"); + List nestedTablePath = ImmutableList.of("nested", "sample"); Table copiedTable = assertTableExists( diff --git a/zetasql-toolkit-core/src/test/java/com/google/zetasql/toolkit/catalog/typeparser/ZetaSQLTypeParserTest.java b/zetasql-toolkit-core/src/test/java/com/google/zetasql/toolkit/catalog/typeparser/ZetaSQLTypeParserTest.java index 0f00be8..88de699 100644 --- a/zetasql-toolkit-core/src/test/java/com/google/zetasql/toolkit/catalog/typeparser/ZetaSQLTypeParserTest.java +++ b/zetasql-toolkit-core/src/test/java/com/google/zetasql/toolkit/catalog/typeparser/ZetaSQLTypeParserTest.java @@ -18,11 +18,12 @@ import static org.junit.jupiter.api.Assertions.*; +import com.google.common.collect.ImmutableList; import com.google.zetasql.StructType.StructField; import com.google.zetasql.Type; import com.google.zetasql.TypeFactory; import com.google.zetasql.ZetaSQLType.TypeKind; -import java.util.List; +import java.util.HashMap; import java.util.Map; import java.util.stream.Stream; import org.junit.jupiter.api.Test; @@ -32,13 +33,12 @@ class ZetaSQLTypeParserTest { @Test void parseSimpleTypes() { - Map inputsToExpectedKinds = - Map.of( - "STRING", TypeKind.TYPE_STRING, - "INT64", TypeKind.TYPE_INT64, - "NUMERIC", TypeKind.TYPE_NUMERIC, - "INTERVAL", TypeKind.TYPE_INTERVAL, - "JSON", TypeKind.TYPE_JSON); + Map inputsToExpectedKinds = new HashMap<>(); + inputsToExpectedKinds.put("STRING", TypeKind.TYPE_STRING); + inputsToExpectedKinds.put("INT64", TypeKind.TYPE_INT64); + inputsToExpectedKinds.put("NUMERIC", TypeKind.TYPE_NUMERIC); + inputsToExpectedKinds.put("INTERVAL", TypeKind.TYPE_INTERVAL); + inputsToExpectedKinds.put("JSON", TypeKind.TYPE_JSON); Stream assertions = inputsToExpectedKinds.entrySet().stream() @@ -83,7 +83,7 @@ void parseStruct() { String typeStr = "STRUCT"; Type expectedType = TypeFactory.createStructType( - List.of( + ImmutableList.of( new StructField("f1", TypeFactory.createSimpleType(TypeKind.TYPE_STRING)), new StructField("f2", TypeFactory.createSimpleType(TypeKind.TYPE_INT64)))); @@ -98,7 +98,7 @@ void parseStructWithParameterType() { String typeStr = "STRUCT"; Type expectedType = TypeFactory.createStructType( - List.of( + ImmutableList.of( new StructField("f1", TypeFactory.createSimpleType(TypeKind.TYPE_STRING)), new StructField("f2", TypeFactory.createSimpleType(TypeKind.TYPE_NUMERIC)))); @@ -113,7 +113,7 @@ void parseArrayOfStructs() { String typeStr = "ARRAY>"; Type structType = TypeFactory.createStructType( - List.of( + ImmutableList.of( new StructField("f1", TypeFactory.createSimpleType(TypeKind.TYPE_STRING)), new StructField("f2", TypeFactory.createSimpleType(TypeKind.TYPE_INT64)))); Type expectedType = TypeFactory.createArrayType(structType); @@ -131,12 +131,12 @@ void parseStructWithMultipleNestingLevels() { TypeFactory.createArrayType(TypeFactory.createSimpleType(TypeKind.TYPE_STRING)); Type innerStructType = TypeFactory.createStructType( - List.of( + ImmutableList.of( new StructField("f1_1", stringArrayType), new StructField("f1_2", TypeFactory.createSimpleType(TypeKind.TYPE_NUMERIC)))); Type expectedType = TypeFactory.createStructType( - List.of(new StructField("f1", TypeFactory.createArrayType(innerStructType)))); + ImmutableList.of(new StructField("f1", TypeFactory.createArrayType(innerStructType)))); assertEquals( ZetaSQLTypeParser.parse(typeStr), diff --git a/zetasql-toolkit-examples/pom.xml b/zetasql-toolkit-examples/pom.xml index a0f74e2..3cad956 100644 --- a/zetasql-toolkit-examples/pom.xml +++ b/zetasql-toolkit-examples/pom.xml @@ -22,18 +22,16 @@ com.google.zetasql.toolkit zetasql-toolkit - 0.4.1-SNAPSHOT + 0.4.1 ../pom.xml zetasql-toolkit-examples - 0.4.1-SNAPSHOT + 0.4.1 ${project.groupId}:${project.artifactId} ${project.version} - 11 - 11 UTF-8 true 3.3.2 diff --git a/zetasql-toolkit-examples/src/main/java/com/google/zetasql/toolkit/examples/AddResourcesToBigQueryCatalog.java b/zetasql-toolkit-examples/src/main/java/com/google/zetasql/toolkit/examples/AddResourcesToBigQueryCatalog.java index d23bf7a..6932c07 100644 --- a/zetasql-toolkit-examples/src/main/java/com/google/zetasql/toolkit/examples/AddResourcesToBigQueryCatalog.java +++ b/zetasql-toolkit-examples/src/main/java/com/google/zetasql/toolkit/examples/AddResourcesToBigQueryCatalog.java @@ -16,8 +16,8 @@ package com.google.zetasql.toolkit.examples; +import com.google.common.collect.ImmutableList; import com.google.zetasql.toolkit.catalog.bigquery.BigQueryCatalog; -import java.util.List; public class AddResourcesToBigQueryCatalog { @@ -28,7 +28,7 @@ public static void main(String[] args) { // Views are considered tables as well, so they can be added this way to the catalog catalog.addTable("bigquery-public-data.samples.wikipedia"); catalog.addTables( - List.of( + ImmutableList.of( "bigquery-public-data.samples.wikipedia", "bigquery-public-data.samples.github_nested")); @@ -41,7 +41,7 @@ public static void main(String[] args) { // For the time being, functions must have an explicit return type (i.e. creating with // a RETURNS clause); otherwise adding them will fail. catalog.addFunction("project.dataset.function"); - catalog.addFunctions(List.of("project.dataset.function2", "project.dataset.function3")); + catalog.addFunctions(ImmutableList.of("project.dataset.function2", "project.dataset.function3")); // Add all functions in a dataset or project // For the time being, functions without an explicit return type are silently ignored @@ -52,7 +52,7 @@ public static void main(String[] args) { // For the time being, TVFs must have an explicit return type (i.e. creating with // a RETURNS clause); otherwise adding them will fail. catalog.addTVF("project.dataset.tvf"); - catalog.addTVFs(List.of("project.dataset.tvf2", "project.dataset.tvf3")); + catalog.addTVFs(ImmutableList.of("project.dataset.tvf2", "project.dataset.tvf3")); // Add all TVFs in a dataset or project // For the time being, TVFs without an explicit return type are silently ignored @@ -61,7 +61,7 @@ public static void main(String[] args) { // Add a procedure or a set of procedures by name catalog.addProcedure("project.dataset.procedure"); - catalog.addProcedures(List.of("project.dataset.procedure1", "project.dataset.procedure2")); + catalog.addProcedures(ImmutableList.of("project.dataset.procedure1", "project.dataset.procedure2")); // Add all procedures in a dataset or project catalog.addAllProceduresInDataset("projectId", "datasetName"); diff --git a/zetasql-toolkit-examples/src/main/java/com/google/zetasql/toolkit/examples/AddResourcesToSpannerCatalog.java b/zetasql-toolkit-examples/src/main/java/com/google/zetasql/toolkit/examples/AddResourcesToSpannerCatalog.java index 02670c7..67e2036 100644 --- a/zetasql-toolkit-examples/src/main/java/com/google/zetasql/toolkit/examples/AddResourcesToSpannerCatalog.java +++ b/zetasql-toolkit-examples/src/main/java/com/google/zetasql/toolkit/examples/AddResourcesToSpannerCatalog.java @@ -16,8 +16,8 @@ package com.google.zetasql.toolkit.examples; +import com.google.common.collect.ImmutableList; import com.google.zetasql.toolkit.catalog.spanner.SpannerCatalog; -import java.util.List; public class AddResourcesToSpannerCatalog { @@ -29,7 +29,7 @@ public static void main(String[] args) { // Views are considered tables as well, so they can be added this way to the catalog catalog.addTable("bigquery-public-data.samples.wikipedia"); catalog.addTables( - List.of( + ImmutableList.of( "bigquery-public-data.samples.wikipedia", "bigquery-public-data.samples.github_nested")); diff --git a/zetasql-toolkit-examples/src/main/java/com/google/zetasql/toolkit/examples/ExtractColumnLevelLineage.java b/zetasql-toolkit-examples/src/main/java/com/google/zetasql/toolkit/examples/ExtractColumnLevelLineage.java index b79ec6a..5ac74c5 100644 --- a/zetasql-toolkit-examples/src/main/java/com/google/zetasql/toolkit/examples/ExtractColumnLevelLineage.java +++ b/zetasql-toolkit-examples/src/main/java/com/google/zetasql/toolkit/examples/ExtractColumnLevelLineage.java @@ -16,6 +16,7 @@ package com.google.zetasql.toolkit.examples; +import com.google.common.collect.ImmutableList; import com.google.zetasql.AnalyzerOptions; import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedStatement; import com.google.zetasql.toolkit.AnalyzedStatement; @@ -26,7 +27,6 @@ import com.google.zetasql.toolkit.tools.lineage.ColumnEntity; import com.google.zetasql.toolkit.tools.lineage.ColumnLineage; import java.util.Iterator; -import java.util.List; import java.util.Set; public class ExtractColumnLevelLineage { @@ -130,7 +130,7 @@ private static void lineageForMergeStatement( public static void main(String[] args) { BigQueryCatalog catalog = BigQueryCatalog.usingBigQueryAPI("bigquery-public-data"); - catalog.addTables(List.of( + catalog.addTables(ImmutableList.of( "bigquery-public-data.samples.wikipedia", "bigquery-public-data.samples.shakespeare" )); diff --git a/zetasql-toolkit-spanner/pom.xml b/zetasql-toolkit-spanner/pom.xml index 0de0e51..3238a71 100644 --- a/zetasql-toolkit-spanner/pom.xml +++ b/zetasql-toolkit-spanner/pom.xml @@ -6,19 +6,17 @@ com.google.zetasql.toolkit zetasql-toolkit - 0.4.1-SNAPSHOT + 0.4.1 ../pom.xml zetasql-toolkit-spanner - 0.4.1-SNAPSHOT + 0.4.1 ${project.groupId}:${project.artifactId} ${project.version} - 11 - 11 UTF-8 false @@ -89,13 +87,6 @@ - - com.google.zetasql.toolkit - zetasql-toolkit-core - ${zetasql.toolkit.version} - test-jar - test - org.junit.jupiter junit-jupiter-engine @@ -134,4 +125,25 @@ + + + default + + + com.google.zetasql.toolkit + zetasql-toolkit-core + ${zetasql.toolkit.version} + test-jar + test + + + + true + + + + release + + + \ No newline at end of file diff --git a/zetasql-toolkit-spanner/src/main/java/com/google/zetasql/toolkit/catalog/spanner/SpannerBuiltIns.java b/zetasql-toolkit-spanner/src/main/java/com/google/zetasql/toolkit/catalog/spanner/SpannerBuiltIns.java index c392b40..813f3bd 100644 --- a/zetasql-toolkit-spanner/src/main/java/com/google/zetasql/toolkit/catalog/spanner/SpannerBuiltIns.java +++ b/zetasql-toolkit-spanner/src/main/java/com/google/zetasql/toolkit/catalog/spanner/SpannerBuiltIns.java @@ -16,6 +16,7 @@ package com.google.zetasql.toolkit.catalog.spanner; +import com.google.common.collect.ImmutableList; import com.google.zetasql.Function; import com.google.zetasql.FunctionArgumentType; import com.google.zetasql.FunctionSignature; @@ -35,17 +36,17 @@ class SpannerBuiltIns { private static final String SPANNER_FUNCTION_GROUP = "CloudSpanner"; public static final List FUNCTIONS = - List.of( + ImmutableList.of( // PENDING_COMMIT_TIMESTAMP() -> TIMESTAMP new Function( "PENDING_COMMIT_TIMESTAMP", SPANNER_FUNCTION_GROUP, Mode.SCALAR, - List.of( + ImmutableList.of( new FunctionSignature( new FunctionArgumentType( TypeFactory.createSimpleType(TypeKind.TYPE_TIMESTAMP)), - List.of(), + ImmutableList.of(), -1)))); /** diff --git a/zetasql-toolkit-spanner/src/main/java/com/google/zetasql/toolkit/catalog/spanner/SpannerCatalog.java b/zetasql-toolkit-spanner/src/main/java/com/google/zetasql/toolkit/catalog/spanner/SpannerCatalog.java index 589e745..27b7d6b 100644 --- a/zetasql-toolkit-spanner/src/main/java/com/google/zetasql/toolkit/catalog/spanner/SpannerCatalog.java +++ b/zetasql-toolkit-spanner/src/main/java/com/google/zetasql/toolkit/catalog/spanner/SpannerCatalog.java @@ -18,6 +18,7 @@ import com.google.cloud.spanner.DatabaseClient; import com.google.cloud.spanner.Spanner; +import com.google.common.collect.ImmutableList; import com.google.zetasql.Analyzer; import com.google.zetasql.AnalyzerOptions; import com.google.zetasql.Constant; @@ -70,7 +71,7 @@ public SpannerCatalog(SpannerResourceProvider spannerResourceProvider) { * @param instance The Spanner instance name * @param database The Spanner database name */ - @Deprecated(since = "0.4.0", forRemoval = true) + @Deprecated public SpannerCatalog(String projectId, String instance, String database) { this(SpannerResourceProviderImpl.buildDefault(projectId, instance, database)); } @@ -86,7 +87,7 @@ public SpannerCatalog(String projectId, String instance, String database) { * @param database The Spanner database name * @param spannerClient The Spanner client to use */ - @Deprecated(since = "0.4.0", forRemoval = true) + @Deprecated public SpannerCatalog(String projectId, String instance, String database, Spanner spannerClient) { this(SpannerResourceProviderImpl.build(projectId, instance, database, spannerClient)); } @@ -158,7 +159,7 @@ public void register(SimpleTable table, CreateMode createMode, CreateScope creat CatalogOperations.createTableInCatalog( this.catalog, - List.of(List.of(table.getName())), + ImmutableList.of(ImmutableList.of(table.getName())), table.getName(), table.getColumnList(), createMode); @@ -189,8 +190,8 @@ public void register(Constant constant) { @Override public void removeTable(String table) { - this.validateSpannerTableNames(List.of(table)); - CatalogOperations.deleteTableFromCatalog(this.catalog, List.of(List.of(table))); + this.validateSpannerTableNames(ImmutableList.of(table)); + CatalogOperations.deleteTableFromCatalog(this.catalog, ImmutableList.of(ImmutableList.of(table))); } @Override @@ -265,7 +266,7 @@ public void addAllTablesUsedInQuery(String query, AnalyzerOptions options) { Analyzer.extractTableNamesFromScript(query, options).stream() .map(tablePath -> String.join(".", tablePath)) .collect(Collectors.toSet()); - this.addTables(List.copyOf(tables)); + this.addTables(ImmutableList.copyOf(tables)); } @Override diff --git a/zetasql-toolkit-spanner/src/main/java/com/google/zetasql/toolkit/catalog/spanner/SpannerResourceProviderImpl.java b/zetasql-toolkit-spanner/src/main/java/com/google/zetasql/toolkit/catalog/spanner/SpannerResourceProviderImpl.java index 7e2d2c2..0e090b1 100644 --- a/zetasql-toolkit-spanner/src/main/java/com/google/zetasql/toolkit/catalog/spanner/SpannerResourceProviderImpl.java +++ b/zetasql-toolkit-spanner/src/main/java/com/google/zetasql/toolkit/catalog/spanner/SpannerResourceProviderImpl.java @@ -24,8 +24,11 @@ import com.google.zetasql.toolkit.catalog.spanner.exceptions.SpannerTablesNotFound; import com.google.zetasql.toolkit.catalog.typeparser.ZetaSQLTypeParser; import com.google.zetasql.toolkit.usage.UsageTracking; -import java.util.*; -import java.util.function.Predicate; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; /** @@ -106,7 +109,7 @@ public List getTables(List tableNames) { List notFoundTables = tableNames.stream() - .filter(Predicate.not(namesOfFetchedTables::contains)) + .filter(tableName -> !namesOfFetchedTables.contains(tableName)) .collect(Collectors.toList()); throw new SpannerTablesNotFound(notFoundTables); diff --git a/zetasql-toolkit-spanner/src/main/java/com/google/zetasql/toolkit/options/SpannerLanguageOptions.java b/zetasql-toolkit-spanner/src/main/java/com/google/zetasql/toolkit/options/SpannerLanguageOptions.java index e943b3e..d1ff94a 100644 --- a/zetasql-toolkit-spanner/src/main/java/com/google/zetasql/toolkit/options/SpannerLanguageOptions.java +++ b/zetasql-toolkit-spanner/src/main/java/com/google/zetasql/toolkit/options/SpannerLanguageOptions.java @@ -16,12 +16,12 @@ package com.google.zetasql.toolkit.options; +import com.google.common.collect.ImmutableSet; import com.google.zetasql.LanguageOptions; import com.google.zetasql.ZetaSQLOptions.LanguageFeature; import com.google.zetasql.ZetaSQLOptions.NameResolutionMode; import com.google.zetasql.ZetaSQLOptions.ProductMode; import com.google.zetasql.ZetaSQLResolvedNodeKind.ResolvedNodeKind; -import java.util.Set; public class SpannerLanguageOptions { @@ -32,7 +32,7 @@ public class SpannerLanguageOptions { languageOptions.setProductMode(ProductMode.PRODUCT_EXTERNAL); languageOptions.setEnabledLanguageFeatures( - Set.of( + ImmutableSet.of( LanguageFeature.FEATURE_ANALYTIC_FUNCTIONS, LanguageFeature.FEATURE_NUMERIC_TYPE, LanguageFeature.FEATURE_TABLESAMPLE, @@ -56,7 +56,7 @@ public class SpannerLanguageOptions { LanguageFeature.FEATURE_V_1_3_DML_RETURNING)); languageOptions.setSupportedStatementKinds( - Set.of( + ImmutableSet.of( ResolvedNodeKind.RESOLVED_QUERY_STMT, ResolvedNodeKind.RESOLVED_INSERT_STMT, ResolvedNodeKind.RESOLVED_UPDATE_STMT, diff --git a/zetasql-toolkit-spanner/src/test/java/com/google/zetasql/toolkit/catalog/spanner/SpannerCatalogTest.java b/zetasql-toolkit-spanner/src/test/java/com/google/zetasql/toolkit/catalog/spanner/SpannerCatalogTest.java index bad6a09..2dc5b72 100644 --- a/zetasql-toolkit-spanner/src/test/java/com/google/zetasql/toolkit/catalog/spanner/SpannerCatalogTest.java +++ b/zetasql-toolkit-spanner/src/test/java/com/google/zetasql/toolkit/catalog/spanner/SpannerCatalogTest.java @@ -19,6 +19,7 @@ import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; +import com.google.common.collect.ImmutableList; import com.google.zetasql.*; import com.google.zetasql.ZetaSQLType.TypeKind; import com.google.zetasql.resolvedast.ResolvedCreateStatementEnums.CreateMode; @@ -45,14 +46,14 @@ public class SpannerCatalogTest { SimpleTable exampleTable = new SimpleTable( "SpannerTable", - List.of( + ImmutableList.of( new SimpleColumn( "SpannerTable", "col1", TypeFactory.createSimpleType(TypeKind.TYPE_STRING)))); SimpleTable replacementTable = new SimpleTable( "SpannerTable", - List.of( + ImmutableList.of( new SimpleColumn( "SpannerTable", "col1", TypeFactory.createSimpleType(TypeKind.TYPE_STRING)), new SimpleColumn( @@ -67,13 +68,13 @@ void init() { private Table assertTableExistsInCatalog(SpannerCatalog catalog, SimpleTable table) { SimpleCatalog underlyingCatalog = catalog.getZetaSQLCatalog(); - return assertDoesNotThrow(() -> underlyingCatalog.findTable(List.of(table.getName()))); + return assertDoesNotThrow(() -> underlyingCatalog.findTable(ImmutableList.of(table.getName()))); } private void assertTableDoesNotExistInCatalog(SpannerCatalog catalog, String tableName) { SimpleCatalog underlyingCatalog = catalog.getZetaSQLCatalog(); - assertThrows(NotFoundException.class, () -> underlyingCatalog.findTable(List.of(tableName))); + assertThrows(NotFoundException.class, () -> underlyingCatalog.findTable(ImmutableList.of(tableName))); } @Test @@ -150,10 +151,10 @@ void testTableAlreadyExists() { @Test void testAddTablesByName() { // When SpannerResourceProvider.getTables() is called, return the test table - when(spannerResourceProviderMock.getTables(anyList())).thenReturn(List.of(exampleTable)); + when(spannerResourceProviderMock.getTables(anyList())).thenReturn(ImmutableList.of(exampleTable)); // Add the tables by name - spannerCatalog.addTables(List.of(exampleTable.getName())); + spannerCatalog.addTables(ImmutableList.of(exampleTable.getName())); // Verify the SpannerCatalog got the tables from the SpannerResourceProvider verify(spannerResourceProviderMock, times(1)).getTables(anyList()); @@ -169,7 +170,7 @@ void testAddTablesByName() { @Test void testAddAllTablesInDatabase() { // When SpannerResourceProvider.getAllTablesInDatabase() is called, return the test table - when(spannerResourceProviderMock.getAllTablesInDatabase()).thenReturn(List.of(exampleTable)); + when(spannerResourceProviderMock.getAllTablesInDatabase()).thenReturn(ImmutableList.of(exampleTable)); // Add the tables by name spannerCatalog.addAllTablesInDatabase(); diff --git a/zetasql-toolkit-spanner/src/test/java/com/google/zetasql/toolkit/catalog/spanner/SpannerResourceProviderImplTest.java b/zetasql-toolkit-spanner/src/test/java/com/google/zetasql/toolkit/catalog/spanner/SpannerResourceProviderImplTest.java index 76f3028..1dd1373 100644 --- a/zetasql-toolkit-spanner/src/test/java/com/google/zetasql/toolkit/catalog/spanner/SpannerResourceProviderImplTest.java +++ b/zetasql-toolkit-spanner/src/test/java/com/google/zetasql/toolkit/catalog/spanner/SpannerResourceProviderImplTest.java @@ -22,6 +22,7 @@ import com.google.cloud.spanner.DatabaseClient; import com.google.cloud.spanner.ResultSet; import com.google.cloud.spanner.Spanner; +import com.google.common.collect.ImmutableList; import com.google.zetasql.SimpleColumn; import com.google.zetasql.SimpleTable; import com.google.zetasql.TypeFactory; @@ -49,7 +50,7 @@ public class SpannerResourceProviderImplTest { SimpleTable exampleTable = new SimpleTable( "table", - List.of( + ImmutableList.of( new SimpleColumn( "table", "column", TypeFactory.createSimpleType(TypeKind.TYPE_STRING)))); @@ -74,7 +75,7 @@ void testGetTables() { ResultSet resultSetForExampleTable = resultSetForExampleTable(); when(dbClient.singleUse().executeQuery(any())).thenReturn(resultSetForExampleTable); - List tables = spannerResourceProvider.getTables(List.of("table")); + List tables = spannerResourceProvider.getTables(ImmutableList.of("table")); assertEquals(1, tables.size()); assertTrue(CatalogTestUtils.tableEquals(exampleTable, tables.get(0))); @@ -87,6 +88,6 @@ void testTableNotFound() { when(dbClient.singleUse().executeQuery(any())).thenReturn(resultSet); assertThrows( - SpannerTablesNotFound.class, () -> spannerResourceProvider.getTables(List.of("table"))); + SpannerTablesNotFound.class, () -> spannerResourceProvider.getTables(ImmutableList.of("table"))); } }