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