absl::StatusOr ValueFromProto()

in frontend/converters/values.cc [110:410]


absl::StatusOr<zetasql::Value> ValueFromProto(
    const google::protobuf::Value& value_pb, const zetasql::Type* type) {
  if (value_pb.kind_case() == google::protobuf::Value::kNullValue) {
    return zetasql::values::Null(type);
  }

  switch (type->kind()) {
    case zetasql::TypeKind::TYPE_BOOL: {
      if (value_pb.kind_case() != google::protobuf::Value::kBoolValue) {
        return error::ValueProtoTypeMismatch(value_pb.DebugString(),
                                             type->DebugString());
      }
      return zetasql::values::Bool(value_pb.bool_value());
    }

    case zetasql::TypeKind::TYPE_INT64: {
      int64_t num = 0;
      if (value_pb.kind_case() != google::protobuf::Value::kStringValue) {
        return error::ValueProtoTypeMismatch(value_pb.DebugString(),
                                             type->DebugString());
      }
      if (!absl::SimpleAtoi(value_pb.string_value(), &num)) {
        return error::CouldNotParseStringAsInteger(value_pb.string_value());
      }
      return zetasql::values::Int64(num);
    }

    case zetasql::TypeKind::TYPE_FLOAT: {
      double val = 0;
      if (value_pb.kind_case() == google::protobuf::Value::kStringValue) {
        if (value_pb.string_value() == "Infinity") {
          val = std::numeric_limits<float>::infinity();
        } else if (value_pb.string_value() == "-Infinity") {
          val = -std::numeric_limits<float>::infinity();
        } else if (value_pb.string_value() == "NaN") {
          val = std::numeric_limits<float>::quiet_NaN();
        } else {
          return error::CouldNotParseStringAsFloat(value_pb.string_value());
        }
      } else if (ABSL_PREDICT_TRUE(IsValidFloat(value_pb.number_value()))) {
        val = value_pb.number_value();
      } else {
        return error::ValueProtoTypeMismatch(value_pb.DebugString(),
                                             type->DebugString());
      }
      return zetasql::values::Float(val);
    }

    case zetasql::TypeKind::TYPE_DOUBLE: {
      double val = 0;
      if (value_pb.kind_case() == google::protobuf::Value::kStringValue) {
        if (value_pb.string_value() == "Infinity") {
          val = std::numeric_limits<double>::infinity();
        } else if (value_pb.string_value() == "-Infinity") {
          val = -std::numeric_limits<double>::infinity();
        } else if (value_pb.string_value() == "NaN") {
          val = std::numeric_limits<double>::quiet_NaN();
        } else {
          return error::CouldNotParseStringAsDouble(value_pb.string_value());
        }
      } else if (value_pb.kind_case() ==
                 google::protobuf::Value::kNumberValue) {
        val = value_pb.number_value();
      } else {
        return error::ValueProtoTypeMismatch(value_pb.DebugString(),
                                             type->DebugString());
      }
      return zetasql::values::Double(val);
    }

    case zetasql::TypeKind::TYPE_EXTENDED: {
      auto type_code = static_cast<const SpannerExtendedType*>(type)->code();
      switch (type_code) {
        case TypeAnnotationCode::PG_JSONB: {
          if (value_pb.kind_case() != google::protobuf::Value::kStringValue) {
            return error::ValueProtoTypeMismatch(value_pb.DebugString(),
                                                 type->DebugString());
          }
          auto pg_jsonb =
              CreatePgJsonbValueWithMemoryContext(value_pb.string_value());
          if (!pg_jsonb.ok()) {
            return error::CouldNotParseStringAsPgJsonb(value_pb.string_value());
          }
          return *pg_jsonb;
        }
        case TypeAnnotationCode::PG_NUMERIC: {
          if (value_pb.kind_case() != google::protobuf::Value::kStringValue) {
            return error::ValueProtoTypeMismatch(value_pb.DebugString(),
                                                 type->DebugString());
          }
          auto pg_numeric =
              CreatePgNumericValueWithMemoryContext(value_pb.string_value());
          if (!pg_numeric.ok()) {
            return error::CouldNotParseStringAsPgNumeric(
                value_pb.string_value());
          }
          return *pg_numeric;
        }
        case TypeAnnotationCode::PG_OID: {
          int64_t oid = 0;
          if (value_pb.kind_case() != google::protobuf::Value::kStringValue) {
            return error::ValueProtoTypeMismatch(value_pb.DebugString(),
                                                 type->DebugString());
          }
          if (!absl::SimpleAtoi(value_pb.string_value(), &oid)) {
            return error::CouldNotParseStringAsPgOid(value_pb.string_value());
          }
          return CreatePgOidValue(oid);
        }
        default:
          return error::Internal(absl::StrCat(
              "Cloud Spanner unsupported type ", type->DebugString(),
              " passed to ValueFromProto when parsing ",
              value_pb.DebugString()));
      }
      break;
    }

    case zetasql::TypeKind::TYPE_TIMESTAMP: {
      if (value_pb.kind_case() != google::protobuf::Value::kStringValue) {
        return error::ValueProtoTypeMismatch(value_pb.DebugString(),
                                             type->DebugString());
      }
      if (value_pb.string_value() == kCommitTimestampIdentifier) {
        return zetasql::values::String(value_pb.string_value());
      }
      absl::string_view time_str(value_pb.string_value());
      if (!absl::ConsumeSuffix(&time_str, "Z")) {
        return error::TimestampMustBeInUTCTimeZone(value_pb.string_value());
      }
      absl::Time time;
      std::string error;
      if (!absl::ParseTime(kRFC3339TimeFormatNoOffset, time_str, &time,
                           &error)) {
        return error::CouldNotParseStringAsTimestamp(value_pb.string_value(),
                                                     error);
      }
      if (!zetasql::functions::IsValidTime(time)) {
        return error::TimestampOutOfRange(
            absl::FormatTime(time, absl::UTCTimeZone()));
      }
      return zetasql::values::Timestamp(time);
    }

    case zetasql::TypeKind::TYPE_DATE: {
      if (value_pb.kind_case() != google::protobuf::Value::kStringValue) {
        return error::ValueProtoTypeMismatch(value_pb.DebugString(),
                                             type->DebugString());
      }
      absl::CivilDay date;
      if (!absl::ParseCivilTime(value_pb.string_value(), &date)) {
        return error::CouldNotParseStringAsDate(value_pb.string_value());
      }
      if (date.year() < 1 || date.year() > 9999) {
        return error::InvalidDate(value_pb.string_value());
      }
      absl::CivilDay epoch_date(1970, 1, 1);
      return zetasql::values::Date(static_cast<int32_t>(date - epoch_date));
    }

    case zetasql::TypeKind::TYPE_STRING: {
      if (value_pb.kind_case() != google::protobuf::Value::kStringValue) {
        return error::ValueProtoTypeMismatch(value_pb.DebugString(),
                                             type->DebugString());
      }
      return zetasql::values::String(value_pb.string_value());
    }

    case zetasql::TypeKind::TYPE_BYTES: {
      if (value_pb.kind_case() != google::protobuf::Value::kStringValue) {
        return error::ValueProtoTypeMismatch(value_pb.DebugString(),
                                             type->DebugString());
      }
      std::string bytes;
      if (!absl::Base64Unescape(value_pb.string_value(), &bytes)) {
        return error::CouldNotParseStringAsBytes(value_pb.string_value());
      }
      return zetasql::values::Bytes(bytes);
    }

    case zetasql::TypeKind::TYPE_NUMERIC: {
      if (value_pb.kind_case() != google::protobuf::Value::kStringValue) {
        return error::ValueProtoTypeMismatch(value_pb.DebugString(),
                                             type->DebugString());
      }
      auto status_or_numeric =
          zetasql::NumericValue::FromStringStrict(value_pb.string_value());
      if (!status_or_numeric.ok()) {
        return error::CouldNotParseStringAsNumeric(value_pb.string_value());
      }
      return zetasql::values::Numeric(status_or_numeric.value());
    }

    case zetasql::TypeKind::TYPE_JSON: {
      if (value_pb.kind_case() != google::protobuf::Value::kStringValue) {
        return error::ValueProtoTypeMismatch(value_pb.DebugString(),
                                             type->DebugString());
      }
      auto status_or_json =
          zetasql::JSONValue::ParseJSONString(value_pb.string_value());
      if (!status_or_json.ok()) {
        return error::CouldNotParseStringAsJson(value_pb.string_value());
      }
      return zetasql::values::Json(std::move(status_or_json.value()));
    }

    case zetasql::TypeKind::TYPE_TOKENLIST: {
      if (value_pb.kind_case() != google::protobuf::Value::kStringValue) {
        return error::ValueProtoTypeMismatch(value_pb.DebugString(),
                                             type->DebugString());
      }
      std::string bytes;
      if (!absl::Base64Unescape(value_pb.string_value(), &bytes)) {
        return error::CouldNotParseStringAsBytes(value_pb.string_value());
      }
      return TokenListFromBytes(bytes);
    }

    case zetasql::TypeKind::TYPE_INTERVAL: {
      if (value_pb.kind_case() != google::protobuf::Value::kStringValue) {
        return error::ValueProtoTypeMismatch(value_pb.DebugString(),
                                             type->DebugString());
      }

      absl::StatusOr<zetasql::IntervalValue> interval_value =
          zetasql::IntervalValue::Parse(value_pb.string_value(),
                                          /*allow_nanos=*/true);
      if (!interval_value.ok()) {
        return error::CouldNotParseStringAsInterval(
            value_pb.string_value(), interval_value.status().message());
      }
      return zetasql::values::Interval(interval_value.value());
    }

    case zetasql::TypeKind::TYPE_ARRAY: {
      if (value_pb.kind_case() != google::protobuf::Value::kListValue) {
        return error::ValueProtoTypeMismatch(value_pb.DebugString(),
                                             type->DebugString());
      }
      std::vector<zetasql::Value> values(value_pb.list_value().values_size());
      for (int i = 0; i < value_pb.list_value().values_size(); ++i) {
        const google::protobuf::Value& element_pb =
            value_pb.list_value().values(i);
        ZETASQL_ASSIGN_OR_RETURN(
            values[i],
            ValueFromProto(element_pb, type->AsArray()->element_type()),
            _ << "\nWhen parsing array element #" << i << ": {"
              << element_pb.DebugString() << "} in " << value_pb.DebugString());
      }
      return zetasql::values::Array(type->AsArray(), values);
    }

    case zetasql::TypeKind::TYPE_STRUCT: {
      if (value_pb.kind_case() != google::protobuf::Value::kListValue) {
        return error::ValueProtoTypeMismatch(value_pb.DebugString(),
                                             type->DebugString());
      }
      std::vector<zetasql::Value> values(value_pb.list_value().values_size());
      for (int i = 0; i < value_pb.list_value().values_size(); ++i) {
        const google::protobuf::Value& field_pb =
            value_pb.list_value().values(i);
        ZETASQL_ASSIGN_OR_RETURN(
            values[i],
            ValueFromProto(field_pb, type->AsStruct()->field(i).type),
            _ << "\nWhen parsing struct element #" << i << ": {"
              << field_pb.DebugString() << "} in " << value_pb.DebugString());
      }
      return zetasql::values::Struct(type->AsStruct(), values);
    }

    case zetasql::TypeKind::TYPE_PROTO: {
      if (value_pb.kind_case() != google::protobuf::Value::kStringValue) {
        return error::ValueProtoTypeMismatch(value_pb.DebugString(),
                                             type->DebugString());
      }
      std::string bytes;
      if (!absl::Base64Unescape(value_pb.string_value(), &bytes)) {
        return error::CouldNotParseStringAsBytes(value_pb.string_value());
      }
      return zetasql::values::Proto(type->AsProto(), absl::Cord(bytes));
    }
    case zetasql::TypeKind::TYPE_ENUM: {
      if (value_pb.kind_case() != google::protobuf::Value::kStringValue) {
        return error::ValueProtoTypeMismatch(value_pb.DebugString(),
                                             type->DebugString());
      }
      int num = 0;
      if (!absl::SimpleAtoi(value_pb.string_value(), &num)) {
        return error::CouldNotParseStringAsInteger(value_pb.string_value());
      }

      return zetasql::values::Enum(type->AsEnum(), num);
    }

    default: {
      return error::Internal(absl::StrCat(
          "Cloud Spanner unsupported type ", type->DebugString(),
          " passed to ValueFromProto when parsing ", value_pb.DebugString()));
    }
  }
}