com.google.zetasql.resolvedast.DebugStrings Maven / Gradle / Ivy
/*
* Copyright 2019 Google LLC
*
* 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.resolvedast;
import static java.util.stream.Collectors.joining;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.Descriptors.OneofDescriptor;
import com.google.protobuf.ProtocolMessageEnum;
import com.google.zetasql.Column;
import com.google.zetasql.Connection;
import com.google.zetasql.DebugPrintableNode.DebugStringField;
import com.google.zetasql.DescriptorPool.ZetaSQLFieldDescriptor;
import com.google.zetasql.DescriptorPool.ZetaSQLOneofDescriptor;
import com.google.zetasql.FunctionSignature;
import com.google.zetasql.ZetaSQLStrings;
import com.google.zetasql.Model;
import com.google.zetasql.Procedure;
import com.google.zetasql.ResolvedFunctionCallInfo;
import com.google.zetasql.Sequence;
import com.google.zetasql.TVFSignature;
import com.google.zetasql.Table;
import com.google.zetasql.TableValuedFunction;
import com.google.zetasql.Type;
import com.google.zetasql.TypeAnnotationProto.FieldFormat;
import com.google.zetasql.TypeParameters;
import com.google.zetasql.Value;
import com.google.zetasql.resolvedast.ResolvedFunctionCallBaseEnums.ErrorMode;
import com.google.zetasql.resolvedast.ResolvedInsertStmtEnums.InsertMode;
import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedCast;
import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedComputedColumn;
import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedConstant;
import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedDeferredComputedColumn;
import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedExtendedCastElement;
import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedFunctionCallBase;
import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedMakeProtoField;
import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedOption;
import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedOutputColumn;
import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedStaticDescribeScan;
import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedSystemVariable;
import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedWindowFrame;
import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedWindowFrameExpr;
import com.google.zetasql.resolvedast.ResolvedOptionEnums.AssignmentOp;
import com.google.zetasql.resolvedast.ResolvedSetOperationScanEnums.SetOperationColumnMatchMode;
import com.google.zetasql.resolvedast.ResolvedSetOperationScanEnums.SetOperationColumnPropagationMode;
import com.google.zetasql.resolvedast.ResolvedStatementEnums.ObjectAccess;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;
/**
* Helper functions for generating debug strings for {@link ResolvedNode}s.
*/
class DebugStrings {
// isDefaultValue functions for different node field types, similar to the C++ implementations in
// zetasql/resolved_ast/resolved_ast.cc.template
static boolean isDefaultValue(ProtocolMessageEnum e) {
return e.getNumber() == 0;
}
static boolean isDefaultValue(List> l) {
return l.isEmpty();
}
static boolean isDefaultValue(boolean b) {
return !b;
}
static boolean isDefaultValue(String s) {
return s.isEmpty();
}
@SuppressWarnings("unused")
static boolean isDefaultValue(Type t) {
return false;
}
static boolean isDefaultValue(Value v) {
return !v.isValid();
}
static boolean isDefaultValue(@Nullable FunctionSignature signature) {
return signature == null || signature.isDefaultValue();
}
static boolean isDefaultValue(ResolvedFunctionCallInfo call) {
return call.isDefaultValue();
}
static boolean isDefaultValue(ResolvedColumn column) {
return column.isDefaultValue();
}
// Used for positional parameters.
static boolean isDefaultValue(long position) {
return position == 0;
}
static boolean isDefaultValue(AnnotationMap annotationMap) {
return annotationMap == null;
}
static boolean isDefaultValue(ResolvedCollation resolvedCollation) {
// TODO: implement ResolvedCollation in Java.
return resolvedCollation == null || resolvedCollation.debugString().isEmpty();
}
static boolean isDefaultValue(TypeModifiers typeModifiers) {
return typeModifiers == null || typeModifiers.isEmpty();
}
static boolean isDefaultValue(TypeParameters typeParameters) {
// TODO: Modify once type parameters are made non-optional in the resolved AST and
// the default value is an empty type parameters object.
return (typeParameters == null || typeParameters.isEmpty());
}
static boolean isDefaultValue(Table table) {
return table == null || table.getName().isEmpty();
}
static boolean isDefaultValue(SetOperationColumnMatchMode mode) {
return mode == SetOperationColumnMatchMode.BY_POSITION;
}
static boolean isDefaultValue(SetOperationColumnPropagationMode mode) {
return mode == SetOperationColumnPropagationMode.STRICT;
}
static boolean isDefaultValue(AssignmentOp assignmentOp) {
return assignmentOp == AssignmentOp.DEFAULT_ASSIGN;
}
// toStringImpl functions for different node field types, similar to the C++ implementation in
// zetasql/resolved_ast/resolved_ast.cc.template
static String toStringImpl(String s) {
return ZetaSQLStrings.toStringLiteral(s);
}
static String toStringImpl(boolean b) {
return b ? "TRUE" : "FALSE";
}
static String toStringImpl(long i) {
return Long.toString(i);
}
static String toStringImpl(InsertMode mode) {
return mode.name().replace('_', ' ');
}
static String toStringImpl(Enum> value) {
return value.name();
}
static String toStringImpl(Model model) {
return model.getFullName();
}
static String toStringImpl(Connection connection) {
return connection.getFullName();
}
static String toStringImpl(Sequence sequence) {
return sequence.getFullName();
}
static String toStringImpl(Table table) {
return table.getFullName();
}
static String toStringImpl(Type type) {
return type.debugString();
}
static String toStringImpl(ZetaSQLFieldDescriptor field) {
FieldDescriptor descriptor = field.getDescriptor();
return descriptor.isExtension() ? "[" + descriptor.getFullName() + "]" : descriptor.getName();
}
static String toStringImpl(ZetaSQLOneofDescriptor field) {
OneofDescriptor descriptor = field.getDescriptor();
return descriptor.getName();
}
static String toStringImpl(ResolvedColumn column) {
return column.debugString();
}
static String toStringImpl(FunctionSignature signature) {
return signature.toString();
}
static String toStringImpl(ResolvedFunctionCallInfo call) {
return call.toString();
}
static String toStringImpl(TableValuedFunction tvf) {
return tvf.toString();
}
static String toStringImpl(TVFSignature signature) {
return signature.toString();
}
static String toStringImpl(Procedure procedure) {
return procedure.toString();
}
static String toStringImpl(Column column) {
return column.getFullName();
}
// To deal with type erasure, every toString function for repeated fields
// must have a different signature or different name. For now, it's fine to
// use ImmutableList here and List below. This is
// hacky but it works. A third function could use Iterable or we could use
// different names for each type.
static String toStringImpl(ImmutableList columns) {
return ResolvedColumn.toString(columns);
}
static String toStringImpl(Value value) {
return value.shortDebugString();
}
static String toStringImpl(List values, String separator) {
StringBuilder sb = new StringBuilder();
for (String value : values) {
if (sb.length() > 0) {
sb.append(separator);
}
sb.append(ZetaSQLStrings.toIdentifierLiteral(value));
}
return sb.toString();
}
// Most vector fields are identifier paths so we format
// the value that way by default.
// For other vector fields, we can override this with to_string_method.
static String toStringImpl(List values) {
return toStringImpl(values, ".");
}
static String toStringImpl(AnnotationMap annotationMap) {
return annotationMap.debugString();
}
static String toStringImpl(ResolvedCollation resolvedCollation) {
return resolvedCollation.debugString();
}
static String toStringImpl(TypeModifiers typeModifiers) {
return typeModifiers.debugString();
}
static String toStringImpl(TypeParameters typeParameters) {
return typeParameters.debugString();
}
static String toStringPeriodSeparatedForFieldDescriptors(List fields) {
StringBuilder sb = new StringBuilder();
for (ZetaSQLFieldDescriptor field : fields) {
if (sb.length() > 0) {
sb.append(".");
}
sb.append(toStringImpl(field));
}
return sb.toString();
}
static String toStringCommaSeparatedForInt(List values) {
return "[" + Joiner.on(", ").join(values) + "]";
}
static String toStringVerbose(FunctionSignature signature) {
return signature.debugString(/*functionName=*/"", /*verbose=*/true);
}
// Custom implementation for the list of enums. Named uniquely to avoid collisions with any other
// method.
static String toStringObjectAccess(ImmutableList values) {
StringBuilder sb = new StringBuilder();
for (ObjectAccess value : values) {
if (sb.length() > 0) {
sb.append(",");
}
sb.append(value.name());
}
return sb.toString();
}
// This formats a list of identifiers (quoting if needed).
static String toStringCommaSeparated(List values) {
return "[" + toStringImpl(values, ", ") + "]";
}
static String toStringCommaSeparated(ImmutableList resolvedCollations) {
return resolvedCollations.stream().map(ResolvedCollation::debugString).collect(joining(","));
}
// Functions for classes in the generated code with customized debugStrings. These functions
// should only be called from the generated code in ResolvedNodes.java. The implementations are
// nearly identical to the C++ implementations in
// ResolvedComputedColumn::CollectDebugStringFields
/**
* ResolvedComputedColumn gets formatted as "name := expr" with expr's children printed as its own
* children.
*/
static void collectDebugStringFields(ResolvedComputedColumn node, List fields) {
fields.clear();
node.getExpr().collectDebugStringFieldsWithNameFormat(fields);
}
/**
* ResolvedDeferredComputedColumn gets formatted as "name := expr" with expr's children printed as
* its own children.
*/
static void collectDebugStringFields(
ResolvedDeferredComputedColumn node, List fields) {
fields.clear();
node.getExpr().collectDebugStringFieldsWithNameFormat(fields);
}
/** ResolvedOutputColumn gets formatted as "column AS name [column.type]". */
@SuppressWarnings("unused")
static void collectDebugStringFields(ResolvedOutputColumn node, List fields) {
Preconditions.checkState(fields.isEmpty());
}
/** ResolvedConstant gets formatted as Constant(name(constant), type[, value]). */
static void collectDebugStringFields(ResolvedConstant node, List fields) {
Preconditions.checkArgument(fields.size() <= 1);
// The base class, ResolvedExpr, has its implementation called first, so when we get here,
// is already populated with the type. Insert the name first, then the value last,
// to match the behavior of the C++ implementation, so that tests which rely on the debug string
// output can pass.
fields.add(0, new DebugStringField("", node.getConstant().getFullName()));
fields.add(new DebugStringField("value", node.getConstant().getValue().debugString()));
}
/**
* ResolvedFunctionCall gets formatted as "FunctionCall(name(arg_types) -> type)" with only
* arguments printed as children.
*/
static void collectDebugStringFields(
ResolvedFunctionCallBase node, List fields) {
Preconditions.checkArgument(fields.size() <= 2);
fields.clear();
Preconditions.checkArgument(
node.getArgumentList().isEmpty() || node.getGenericArgumentList().isEmpty());
if (!node.getArgumentList().isEmpty()) {
// Use empty name to avoid printing "arguments=" with extra indentation.
fields.add(new DebugStringField(/*name=*/ "", node.getArgumentList()));
}
if (!node.getGenericArgumentList().isEmpty()) {
// Use empty name to avoid printing "generic_arguments=" with extra indentation.
fields.add(new DebugStringField(/*name=*/ "", node.getGenericArgumentList()));
}
if (!node.getHintList().isEmpty()) {
fields.add(new DebugStringField("hint_list", node.getHintList()));
}
}
/**
* ResolvedCast gets formatted as "Cast(from_type -> to_type)" with only from_expr printed as a
* child.
*/
static void collectDebugStringFields(ResolvedCast node, List fields) {
Preconditions.checkArgument(fields.size() <= 1);
fields.clear();
if (node.getExpr() != null) {
// Use empty name to avoid printing "arguments=" with extra indentation.
fields.add(new DebugStringField("", node.getExpr()));
}
if (node.getReturnNullOnError()) {
fields.add(new DebugStringField("return_null_on_error", "TRUE"));
}
if (node.getExtendedCast() != null) {
fields.add(new DebugStringField("extended_cast", node.getExtendedCast()));
}
if (node.getFormat() != null) {
fields.add(new DebugStringField("format", node.getFormat()));
}
if (node.getTimeZone() != null) {
fields.add(new DebugStringField("time_zone", node.getTimeZone()));
}
if (node.getTypeModifiers() != null && !node.getTypeModifiers().isEmpty()) {
fields.add(new DebugStringField("type_modifiers", node.getTypeModifiers().debugString()));
}
}
/**
* ResolvedExtendedCastElement gets formatted as "ResolvedExtendedCastElement(from_type ->
* to_type, function=name)".
*/
static void collectDebugStringFields(
ResolvedExtendedCastElement node, List fields) {
Preconditions.checkArgument(fields.size() <= 1);
}
/**
* ResolvedMakeProtoField gets formatted as "field[(format=TIMESTAMP_MILLIS)] := expr" with expr's
* children printed as its own children. The required proto format is shown in parentheses when
* present. expr is normally just a ResolvedColumnRef, but could be a cast expr.
*/
static void collectDebugStringFields(ResolvedMakeProtoField node, List fields) {
node.getExpr().collectDebugStringFieldsWithNameFormat(fields);
}
/**
* ResolvedOption gets formatted as "[.] := value" if the assignment operator is
* "=". Otherwise, it is formatted as [.] +-
*/
static void collectDebugStringFields(ResolvedOption node, List fields) {
if (isDefaultValue(node.getAssignmentOp())) {
node.getValue().collectDebugStringFieldsWithNameFormat(fields);
} else {
fields.add(new DebugStringField("", node.getValue()));
}
}
static void collectDebugStringFields(ResolvedWindowFrame node, List fields) {
fields.add(new DebugStringField("start_expr", node.getStartExpr()));
fields.add(new DebugStringField("end_expr", node.getEndExpr()));
}
static void collectDebugStringFields(
ResolvedWindowFrameExpr node, List fields) {
if (node.getExpression() != null) {
// Use empty name to avoid printing "expression=" with extra indentation.
fields.add(new DebugStringField("", node.getExpression()));
}
}
static void collectDebugStringFields(ResolvedSystemVariable node, List fields) {
fields.clear();
ArrayList pathParts = new ArrayList<>();
for (String pathPart : node.getNamePath()) {
pathParts.add(ZetaSQLStrings.toIdentifierLiteral(pathPart));
}
fields.add(new DebugStringField("", String.join(".", pathParts)));
fields.add(new DebugStringField("type", node.getType().toString()));
}
static void collectDebugStringFields(
ResolvedStaticDescribeScan node, List fields) {
if (!node.getDescribeText().isEmpty()) {
fields.add(new DebugStringField("describe_text", node.getDescribeText()));
}
if (node.getInputScan() != null) {
fields.add(new DebugStringField("input_scan", node.getInputScan()));
}
}
static String getNameForDebugString(ResolvedStaticDescribeScan node) {
return node.nodeKindString();
}
static String getNameForDebugString(ResolvedComputedColumn node) {
String name = node.getColumn().shortDebugString();
return node.getExpr().getNameForDebugStringWithNameFormat(name);
}
static String getNameForDebugString(ResolvedDeferredComputedColumn node) {
String name =
node.getColumn().shortDebugString()
+ " [side_effect_column="
+ ((ResolvedDeferredComputedColumn) node).getSideEffectColumn().shortDebugString()
+ "]";
return node.getExpr().getNameForDebugStringWithNameFormat(name);
}
static String getNameForDebugString(ResolvedOutputColumn node) {
return node.getColumn().debugString()
+ " AS "
+ ZetaSQLStrings.toIdentifierLiteral(node.getName())
+ " ["
+ node.getColumn().getType().debugString()
+ "]";
}
static String getNameForDebugString(ResolvedConstant node) {
return "Constant";
}
static String getNameForDebugString(ResolvedFunctionCallBase node) {
return node.nodeKindString()
+ "("
+ (node.getErrorMode() == ErrorMode.SAFE_ERROR_MODE ? "{SAFE_ERROR_MODE} " : "")
+ (node.getFunction() != null ? node.getFunction().toString() : "")
+ node.getSignature().toString()
+ ")";
}
static String getNameForDebugString(ResolvedCast node) {
return "Cast("
+ node.getExpr().getType().debugString()
+ " -> "
+ node.getType().debugString()
+ ")";
}
static String getNameForDebugString(ResolvedExtendedCastElement node) {
return node.nodeKindString()
+ "("
+ node.getToType().debugString()
+ " -> "
+ node.getFromType().debugString()
+ ", (function="
+ (node.getFunction() != null ? node.getFunction().toString() : "")
+ ")";
}
static String getNameForDebugString(ResolvedMakeProtoField node) {
String name;
FieldDescriptor descriptor = node.getFieldDescriptor().getDescriptor();
if (descriptor.isExtension()) {
name = "[" + descriptor.getFullName() + "]";
} else {
name = descriptor.getName();
}
// If the MakeProtoFieldNode has any modifiers present, add them
// in parentheses on the field name.
List modifiers = new ArrayList<>();
if (node.getFormat() != FieldFormat.Format.DEFAULT_FORMAT) {
modifiers.add("format=" + node.getFormat().name());
}
if (!modifiers.isEmpty()) {
name = name + "(" + Joiner.on(",").join(modifiers) + ")";
}
return node.getExpr().getNameForDebugStringWithNameFormat(name);
}
static String getNameForDebugString(ResolvedOption node) {
String prefix =
node.getQualifier().isEmpty()
? ""
: ZetaSQLStrings.toIdentifierLiteral(node.getQualifier()) + ".";
String nameWithPrefix = prefix + ZetaSQLStrings.toIdentifierLiteral(node.getName());
if (isDefaultValue(node.getAssignmentOp())) {
return node.getValue().getNameForDebugStringWithNameFormat(nameWithPrefix);
}
return nameWithPrefix + (node.getAssignmentOp() == AssignmentOp.ADD_ASSIGN ? "+=" : "-=");
}
static String getNameForDebugString(ResolvedWindowFrame node) {
return node.nodeKindString() + "(frame_unit=" + node.getFrameUnit().name() + ")";
}
static String getNameForDebugString(ResolvedWindowFrameExpr node) {
return node.nodeKindString()
+ "(boundary_type="
+ node.getBoundaryType().name().replace('_', ' ')
+ ")";
}
static String getNameForDebugString(ResolvedSystemVariable node) {
return node.nodeKindString();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy