All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.zetasql.resolvedast.DebugStrings Maven / Gradle / Ivy

There is a newer version: 2024.11.1
Show newest version
/*
 * Copyright 2019 ZetaSQL Authors
 *
 * 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 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.Constant;
import com.google.zetasql.FunctionSignature;
import com.google.zetasql.ZetaSQLDescriptorPool.ZetaSQLFieldDescriptor;
import com.google.zetasql.ZetaSQLStrings;
import com.google.zetasql.Model;
import com.google.zetasql.Procedure;
import com.google.zetasql.ResolvedFunctionCallInfo;
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.Value;
import com.google.zetasql.resolvedast.ResolvedFunctionCallBaseEnums.ErrorMode;
import com.google.zetasql.resolvedast.ResolvedInsertStmtEnums.InsertMode;
import com.google.zetasql.resolvedast.ResolvedNode.DebugStringField;
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.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.ResolvedSystemVariable;
import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedWindowFrame;
import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedWindowFrameExpr;
import com.google.zetasql.resolvedast.ResolvedStatementEnums.ObjectAccess;
import java.util.ArrayList;
import java.util.List;

/**
 * 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(FunctionSignature signature) {
    return signature.isDefaultValue();
  }

  static boolean isDefaultValue(ResolvedFunctionCallInfo call) {
    return call.isDefaultValue();
  }

  static boolean isDefaultValue(TableValuedFunction tvf) {
    return tvf.isDefaultValue();
  }

  static boolean isDefaultValue(TVFSignature signature) {
    return signature.isDefaultValue();
  }

  static boolean isDefaultValue(ResolvedColumn column) {
    return column.isDefaultValue();
  }

  // Used for positional parameters.
  static boolean isDefaultValue(long position) {
    return position == 0;
  }

  // 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(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(ResolvedColumn column) {
    return column.debugString();
  }

  static String toStringImpl(Constant constant) {
    return constant.toString();
  }

  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();
  }

  // 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 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, ", ") + "]";
  }

  // 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) {
      node.getExpr().collectDebugStringFieldsWithNameFormat(fields);
  }

  static String getNameForDebugString(ResolvedComputedColumn node) {
    return node.getExpr().getNameForDebugStringWithNameFormat(node.getColumn().shortDebugString());
  }

  /**
   * ResolvedOutputColumn gets formatted as "column AS name [column.type]".
   */
  @SuppressWarnings("unused")
  static void collectDebugStringFields(ResolvedOutputColumn node, List fields) {
    Preconditions.checkState(fields.isEmpty());
  }

  static String getNameForDebugString(ResolvedOutputColumn node) {
    return node.getColumn().debugString() + " AS "
        + ZetaSQLStrings.toIdentifierLiteral(node.getName())
        + " [" + node.getColumn().getType().debugString() + "]";
  }

  /**
   * 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()));
  }

  static String getNameForDebugString(ResolvedConstant node) {
    return "Constant";
  }

  /**
   * 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() <= 1);

    fields.clear();
    if (!node.getArgumentList().isEmpty()) {
      // Use empty name to avoid printing "arguments=" with extra indentation.
      fields.add(new DebugStringField("", node.getArgumentList()));
    }
  }

  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() + ")";
  }

  /**
   * 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"));
   }
  }

  static String getNameForDebugString(ResolvedCast node) {
   return "Cast(" + node.getExpr().getType().debugString() + " -> "
       + node.getType().debugString() + ")";
  }

  /**
   * 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);
  }

  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);
  }

  /** ResolvedOption gets formatted as "[qualifier.]name := value" */
  static void collectDebugStringFields(ResolvedOption node, List fields) {
    node.getValue().collectDebugStringFieldsWithNameFormat(fields);
  }

  static String getNameForDebugString(ResolvedOption node) {
    String prefix = node.getQualifier().isEmpty() ? ""
        : ZetaSQLStrings.toIdentifierLiteral(node.getQualifier()) + ".";

    return node.getValue().getNameForDebugStringWithNameFormat(
        prefix + ZetaSQLStrings.toIdentifierLiteral(node.getName()));
  }

  static void collectDebugStringFields(ResolvedWindowFrame node, List fields) {
    fields.add(new DebugStringField("start_expr", node.getStartExpr()));
    fields.add(new DebugStringField("end_expr", node.getEndExpr()));
  }

  static String getNameForDebugString(ResolvedWindowFrame node) {
    return node.nodeKindString() + "(frame_unit=" + node.getFrameUnit().name() + ")";
  }

  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 String getNameForDebugString(ResolvedWindowFrameExpr node) {
    return node.nodeKindString()
        + "(boundary_type=" + node.getBoundaryType().name().replace('_', ' ') + ")";
  }

  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 String getNameForDebugString(ResolvedSystemVariable node) {
    return node.nodeKindString();
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy