backend/schema/parser/ddl_parser.cc (3,063 lines of code) (raw):
//
// Copyright 2020 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.
//
#include "backend/schema/parser/ddl_parser.h"
#include <cstdint>
#include <functional>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "absl/algorithm/container.h"
#include "zetasql/base/no_destructor.h"
#include "absl/container/flat_hash_set.h"
#include "absl/log/check.h"
#include "absl/log/log.h"
#include "absl/status/status.h"
#include "absl/strings/ascii.h"
#include "absl/strings/escaping.h"
#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "absl/strings/substitute.h"
#include "backend/common/utils.h"
#include "backend/schema/ddl/operations.pb.h"
#include "backend/schema/parser/DDLParserTokenManager.h"
#include "backend/schema/parser/DDLParserTreeConstants.h"
#include "backend/schema/parser/JavaCC.h"
#include "backend/schema/parser/ddl_char_stream.h"
#include "backend/schema/parser/ddl_includes.h"
#include "backend/schema/parser/ddl_token_validation_utils.h"
#include "common/constants.h"
#include "common/errors.h"
#include "common/feature_flags.h"
#include "common/limits.h"
#include "google/protobuf/repeated_ptr_field.h"
namespace google {
namespace spanner {
namespace emulator {
namespace backend {
namespace ddl {
typedef google::protobuf::RepeatedPtrField<SetOption> OptionList;
const char kCommitTimestampOptionName[] = "allow_commit_timestamp";
const char kPGCommitTimestampOptionName[] = "commit_timestamp";
const char kChangeStreamValueCaptureTypeOptionName[] = "value_capture_type";
const char kChangeStreamRetentionPeriodOptionName[] = "retention_period";
const char kChangeStreamExcludeInsertOptionName[] = "exclude_insert";
const char kChangeStreamExcludeDeleteOptionName[] = "exclude_delete";
const char kChangeStreamExcludeUpdateOptionName[] = "exclude_update";
const char kChangeStreamExcludeTtlDeletesOptionName[] = "exclude_ttl_deletes";
const zetasql_base::NoDestructor<absl::flat_hash_set<std::string>>
kChangeStreamBooleanOptions{{kChangeStreamExcludeInsertOptionName,
kChangeStreamExcludeDeleteOptionName,
kChangeStreamExcludeUpdateOptionName,
kChangeStreamExcludeTtlDeletesOptionName}};
const zetasql_base::NoDestructor<absl::flat_hash_set<std::string>>
kChangeStreamStringOptions{{kChangeStreamValueCaptureTypeOptionName,
kChangeStreamRetentionPeriodOptionName}};
const char kSearchIndexOptionSortOrderShardingName[] = "sort_order_sharding";
const char kSearchIndexOptionsDisableAutomaticUidName[] =
"disable_automatic_uid_column";
const char kModelColumnRequiredOptionName[] = "required";
const char kModelDefaultBatchSizeOptionName[] = "default_batch_size";
const char kModelEndpointOptionName[] = "endpoint";
const char kModelEndpointsOptionName[] = "endpoints";
const char kWitnessLocationOptionName[] = "witness_location";
const char kDefaultLeaderOptionName[] = "default_leader";
const char kVersionRetentionPeriodOptionName[] = "version_retention_period";
const char kDefaultSequenceKindOptionName[] = "default_sequence_kind";
const char kDefaultTimeZoneOptionName[] = "default_time_zone";
const char kVectorIndexTreeDepth[] = "tree_depth";
const char kVectorIndexNumberOfLeaves[] = "num_leaves";
const char kVectorIndexNumberOfBranches[] = "num_branches";
const char kVectorIndexDistanceType[] = "distance_type";
const char kVectorIndexLeafScatterFactor[] = "leaf_scatter_factor";
const char kVectorIndexMinBranchSplits[] = "min_branch_splits";
const char kVectorIndexMinLeafSplits[] = "min_leaf_splits";
const char kPlacementDefaultLeaderOptionName[] = "default_leader";
const char kPlacementInstancePartitionOptionName[] = "instance_partition";
const char kLocalityGroupOptionName[] = "locality_group";
const char kLocalityGroupStorageOptionName[] = "storage";
const char kLocalityGroupStorageOptionSSDVal[] = "ssd";
const char kLocalityGroupStorageOptionHDDVal[] = "hdd";
const char kLocalityGroupSpillTimeSpanOptionName[] =
"ssd_to_hdd_spill_timespan";
const char kInternalLocalityGroupStorageOptionName[] = "inflash";
const char kInternalLocalityGroupSpillTimeSpanOptionName[] =
"age_based_spill_policy";
const char kDefaultLocalityGroupName[] = "default";
typedef google::protobuf::RepeatedPtrField<SetOption> OptionList;
typedef google::protobuf::RepeatedPtrField<Grantee> Grantees;
typedef google::protobuf::RepeatedPtrField<Privilege> Privileges;
namespace {
bool IsPrint(absl::string_view str) {
const char* strp = str.data();
const char* end = strp + str.size();
while (strp < end) {
if (!absl::ascii_isprint(*strp++)) {
return false;
}
}
return true;
}
bool UnescapeStringLiteral(absl::string_view val, std::string* result,
std::string* error) {
if (val.size() <= 2) {
*error = absl::StrCat("Invalid string literal: ", val);
return false;
}
ABSL_CHECK_EQ(val[0], val[val.size() - 1]);
ZETASQL_VLOG(val[0] == '\'' || val[0] == '"');
if (!absl::CUnescape(absl::ClippedSubstr(val, 1, val.size() - 2), result)) {
*error = absl::StrCat("Cannot parse string literal: ", val);
return false;
}
return true;
}
// Build a string representing a syntax error for the given token.
std::string SyntaxError(const Token* token, absl::string_view detail) {
return absl::StrCat("Syntax error on line ", token->beginLine, ", column ",
token->beginColumn, ": ", detail);
}
// Returns the image of the token with special handling of EOF.
std::string GetTokenRepresentation(Token* token) {
if (token->kind == _EOF) {
// token->image is empty string which is not helpful.
return "'EOF'";
}
if (token->kind == UNEXPECTED_CHARACTER) {
// The next character is not any of the known kinds of whitespace, and not
// something we expected in any production. If the character is non-ASCII,
// we produce an error message suggesting one common source of such
// characters. Whatever the case, we suppress further error messages.
std::string token_str = token->toString();
if (token_str[0] & 0x80) {
return "a non-ASCII UTF-8 character. Did you perhaps copy the Spanner "
"Cloud DDL statements from a word-processed document, including "
"non-breaking spaces or smart quotes?";
} else if (!IsPrint(token_str)) {
return absl::StrCat("a non-printable ASCII character ('",
absl::CEscape(token_str), "').");
}
return absl::StrCat("an unknown character ('", token_str, "').");
}
if (token->kind == ILLEGAL_STRING_ESCAPE) {
// Revalidate the token image to get an error message.
std::string error_string;
absl::Status status =
ValidateStringLiteralImage(token->image,
/*force=*/true, &error_string);
if (status.ok()) {
return "Internal error: revalidation of string failed";
}
return error_string;
}
if (token->kind == ILLEGAL_BYTES_ESCAPE) {
// Revalidate the token image to get an error message.
std::string error_string;
absl::Status status =
ValidateBytesLiteralImage(token->image, &error_string);
if (status.ok()) {
return "Internal error: revalidation of bytes failed";
}
return error_string;
}
if (token->kind == UNCLOSED_SQ3 || token->kind == UNCLOSED_DQ3) {
return SyntaxError(token, "Encountered an unclosed triple quoted string.");
}
return absl::StrCat("'", token->image, "'");
}
// Note that the methods in this class have unusual names because we are
// implementing JavaCC's ErrorHandler interface, which uses these names.
class CloudDDLErrorHandler : public ErrorHandler {
public:
explicit CloudDDLErrorHandler(std::vector<std::string>* errors)
: errors_(errors), ignore_further_errors_(false) {}
~CloudDDLErrorHandler() override = default;
// Called when the parser encounters a different token when expecting to
// consume a specific kind of token.
// expected_kind - token kind that the parser was trying to consume.
// expected_token - the image of the token - tokenImages[expected_kind].
// actual - the actual token that the parser got instead.
void handleUnexpectedToken(int expected_kind, const JJString& expected_token,
Token* actual, DDLParser* parser) override {
if (ignore_further_errors_) return;
// expected_kind is -1 when the next token is not expected, when choosing
// the next rule based on next token. Every invocation of
// handleUnexpectedToken with expeced_kind=-1 is followed by a call to
// handleParserError. We process the error there.
if (expected_kind == -1) {
return;
}
// The parser would continue to through unexpected token at us but only the
// first error is the cause.
ignore_further_errors_ = true;
errors_->push_back(
absl::StrCat("Syntax error on line ", actual->beginLine, ", column ",
actual->beginColumn, ": Expecting '",
absl::AsciiStrToUpper(expected_token), "' but found ",
GetTokenRepresentation(actual)));
}
// Called when the parser cannot continue parsing.
// last - the last token successfully parsed.
// unexpected - the token at which the error occurs.
// production - the production in which this error occurs.
void handleParseError(Token* last, Token* unexpected,
const JJSimpleString& production,
DDLParser* parser) override {
if (ignore_further_errors_) return;
ignore_further_errors_ = true;
errors_->push_back(absl::StrCat(
"Syntax error on line ", unexpected->beginLine, ", column ",
unexpected->beginColumn, ": Encountered ",
GetTokenRepresentation(unexpected), " while parsing: ", production));
}
int getErrorCount() override { return errors_->size(); }
private:
// List of errors found during the parse. Will be empty IFF
// there were no problems parsing.
std::vector<std::string>* errors_;
bool ignore_further_errors_;
};
//////////////////////////////////////////////////////////////////////////
// Node and Child helper functions
// Return child node of "parent" by position.
SimpleNode* GetChildNode(const SimpleNode* parent, int pos) {
ABSL_CHECK_LT(pos, parent->jjtGetNumChildren())
<< "[" << pos << "] vs " << parent->jjtGetNumChildren();
return dynamic_cast<SimpleNode*>(parent->jjtGetChild(pos));
}
void CheckNode(const SimpleNode* node, int expected_type) {
ABSL_CHECK_EQ(node->getId(), expected_type)
<< "Expected '" << jjtNodeName[expected_type] << "' but was '"
<< jjtNodeName[node->getId()] << "'";
}
SimpleNode* GetChildNode(const SimpleNode* parent, int pos, int expected_type) {
SimpleNode* child = GetChildNode(parent, pos);
CheckNode(child, expected_type);
return child;
}
// Returns the first child node of the type or NULL if not present
SimpleNode* GetFirstChildNode(const SimpleNode* parent, int type) {
for (int i = 0; i < parent->jjtGetNumChildren(); i++) {
SimpleNode* child = GetChildNode(parent, i);
if (child->getId() == type) {
return child;
}
}
return nullptr;
}
// Returns the text in ddl_text that was used to parse node.
absl::string_view ExtractTextForNode(const SimpleNode* node,
absl::string_view ddl_text) {
ABSL_DCHECK(node);
int node_offset = node->absolute_begin_column();
int node_length = node->absolute_end_column() - node_offset;
return absl::ClippedSubstr(ddl_text, node_offset, node_length);
}
std::string GetQualifiedIdentifier(const SimpleNode* node) {
std::string rv;
for (int i = 0; i < node->jjtGetNumChildren(); ++i) {
absl::StrAppend(&rv, i != 0 ? "." : "",
GetChildNode(node, i, JJTPART)->image());
}
return rv;
}
////////////////////////////////////////////////////////////////////////////
// Visit functions each take a const SimpleNode* representing the "root" AST
// node for the given structure, parse its contents, and put the results
// in a peer proto node passed in as second argument.
std::string CheckOptionKeyValNodeAndGetName(const SimpleNode* node) {
CheckNode(node, JJTOPTION_KEY_VAL);
const int num_children = node->jjtGetNumChildren();
ABSL_CHECK_GE(num_children, 2);
const SimpleNode* key = GetChildNode(node, 0, JJTKEY);
return key->image();
}
void VisitLocalityGroupName(const SimpleNode* node, OptionList* options,
std::vector<std::string>* errors) {
std::string option_name = kLocalityGroupOptionName;
SetOption* option = options->Add();
option->set_option_name(option_name);
const SimpleNode* child = GetChildNode(node, 1);
if (child->getId() == JJTSTR_VAL &&
ValidateStringLiteralImage(child->image(), /*force=*/true, nullptr)
.ok()) {
std::string string_value;
std::string error = "";
if (!UnescapeStringLiteral(child->image(), &string_value, &error)) {
errors->push_back(error);
return;
}
if (string_value.empty()) {
errors->push_back("Empty string is an invalid value for locality_group.");
return;
}
option->set_string_value(string_value);
}
/* NULL locality group name implies inheriting from parent locality group */
if (child->getId() == JJTNULLL) {
option->set_null_value(true);
}
}
void VisitTableOptionKeyValNode(const SimpleNode* node, OptionList* options,
std::vector<std::string>* errors) {
const std::string option_name = CheckOptionKeyValNodeAndGetName(node);
// If this is an invalid option, return error.
if (option_name != kLocalityGroupOptionName) {
errors->push_back(
absl::StrCat("Option: ", option_name, " is unknown in Table Options."));
return;
}
if (option_name == kLocalityGroupOptionName) {
VisitLocalityGroupName(node, options, errors);
}
}
void VisitTableOptionListNode(const SimpleNode* node, int option_list_offset,
OptionList* options,
std::vector<std::string>* errors) {
CheckNode(node, JJTOPTIONS_CLAUSE);
// The children of this node are OPTION_KEY_VALs.
for (int i = option_list_offset; i < node->jjtGetNumChildren(); ++i) {
VisitTableOptionKeyValNode(GetChildNode(node, i, JJTOPTION_KEY_VAL),
options, errors);
}
}
void VisitIndexOptionKeyValNode(const SimpleNode* node, OptionList* options,
std::vector<std::string>* errors) {
const std::string option_name = CheckOptionKeyValNodeAndGetName(node);
// If this is an invalid option, return error.
if (option_name != kLocalityGroupOptionName) {
errors->push_back(
absl::StrCat("Option: ", option_name, " is unknown in Index Options."));
return;
}
if (option_name == kLocalityGroupOptionName) {
VisitLocalityGroupName(node, options, errors);
}
}
void VisitIndexOptionListNode(const SimpleNode* node, int option_list_offset,
OptionList* options,
std::vector<std::string>* errors) {
CheckNode(node, JJTOPTIONS_CLAUSE);
// The children of this node are OPTION_KEY_VALs.
for (int i = option_list_offset; i < node->jjtGetNumChildren(); ++i) {
VisitIndexOptionKeyValNode(GetChildNode(node, i, JJTOPTION_KEY_VAL),
options, errors);
}
}
void VisitColumnOptionKeyValNode(const SimpleNode* node, OptionList* options,
std::vector<std::string>* errors) {
std::string option_name = CheckOptionKeyValNodeAndGetName(node);
// If this is an invalid option, return error. Later during schema
// change, we will verify the valid option against the
// column type.
if (option_name == kLocalityGroupOptionName) {
VisitLocalityGroupName(node, options, errors);
return;
}
if (option_name != kCommitTimestampOptionName &&
option_name != kModelColumnRequiredOptionName) {
errors->push_back(absl::StrCat("Option: ", option_name, " is unknown."));
return;
}
SetOption* option = options->Add();
option->set_option_name(option_name);
const SimpleNode* child = GetChildNode(node, 1);
switch (child->getId()) {
case JJTNULLL:
option->set_null_value(true);
break;
case JJTBOOL_TRUE_VAL:
option->set_bool_value(true);
break;
case JJTBOOL_FALSE_VAL:
option->set_bool_value(false);
break;
default: {
// handleUnexpectedToken() should have already caught this case
// and added an error.
errors->push_back(
absl::StrCat("Unexpected value for option: ", option_name,
". "
"Supported option values are true, false, and null."));
break;
}
}
}
void VisitColumnOptionListNode(const SimpleNode* node, int option_list_offset,
OptionList* options,
std::vector<std::string>* errors) {
CheckNode(node, JJTOPTIONS_CLAUSE);
// The option_list node is suppressed (#void) so it is not
// created. The children of this node are OPTION_KEY_VALs.
for (int i = option_list_offset; i < node->jjtGetNumChildren(); ++i) {
VisitColumnOptionKeyValNode(GetChildNode(node, i, JJTOPTION_KEY_VAL),
options, errors);
}
}
void VisitCreateDatabaseNode(const SimpleNode* node, CreateDatabase* database,
std::vector<std::string>* errors) {
CheckNode(node, JJTCREATE_DATABASE_STATEMENT);
for (int i = 0; i < node->jjtGetNumChildren(); i++) {
SimpleNode* child = GetChildNode(node, i);
switch (child->getId()) {
case JJTDB_NAME:
database->set_db_name(child->image());
break;
default:
ABSL_LOG(FATAL) << "Unknown node type: " << node->toString();
}
}
}
void VisitOnDeleteClause(const SimpleNode* node,
InterleaveClause::Action* on_delete_action) {
CheckNode(node, JJTON_DELETE_CLAUSE);
if (GetFirstChildNode(node, JJTNO_ACTION) != nullptr) {
*on_delete_action = InterleaveClause::NO_ACTION;
} else if (GetFirstChildNode(node, JJTCASCADE) != nullptr) {
*on_delete_action = InterleaveClause::CASCADE;
} else {
ABSL_LOG(FATAL) << "ON DELETE does not specify a valid behavior: "
<< node->toString();
}
}
void VisitInterleaveNode(const SimpleNode* node, InterleaveClause* interleave) {
interleave->set_table_name(
GetQualifiedIdentifier(GetChildNode(node, 0, JJTINTERLEAVE_IN)));
// Default behavior is ON DELETE NO ACTION.
InterleaveClause::Action on_delete_action = InterleaveClause::NO_ACTION;
SimpleNode* on_delete_node = GetFirstChildNode(node, JJTON_DELETE_CLAUSE);
if (on_delete_node != nullptr) {
VisitOnDeleteClause(on_delete_node, &on_delete_action);
}
interleave->set_on_delete(on_delete_action);
}
void VisitTableInterleaveNode(const SimpleNode* node,
InterleaveClause* interleave) {
CheckNode(node, JJTTABLE_INTERLEAVE_CLAUSE);
VisitInterleaveNode(node, interleave);
}
void VisitIndexInterleaveNode(const SimpleNode* node,
std::string* interleave_in_table) {
CheckNode(node, JJTINDEX_INTERLEAVE_CLAUSE);
ABSL_CHECK_EQ(1, node->jjtGetNumChildren());
*interleave_in_table =
GetQualifiedIdentifier(GetChildNode(node, 0, JJTINTERLEAVE_IN));
}
void VisitIntervalExpressionNode(const SimpleNode* node, int64_t* days) {
CheckNode(node, JJTINTERVAL_EXPRESSION);
ABSL_CHECK_EQ(1, node->jjtGetNumChildren());
*days = GetChildNode(node, 0)->image_as_int64();
}
void VisitRowDeletionPolicyExpressionNode(const SimpleNode* node,
RowDeletionPolicy* policy,
std::vector<std::string>* errors) {
CheckNode(node, JJTROW_DELETION_POLICY_EXPRESSION);
ABSL_CHECK_EQ(3, node->jjtGetNumChildren());
SimpleNode* function = GetChildNode(node, 0, JJTROW_DELETION_POLICY_FUNCTION);
if (!absl::EqualsIgnoreCase(function->image(), "OLDER_THAN")) {
errors->push_back("Only OLDER_THAN is supported.");
return;
}
SimpleNode* column = GetChildNode(node, 1, JJTROW_DELETION_POLICY_COLUMN);
policy->set_column_name(column->image());
SimpleNode* interval_expr = GetChildNode(node, 2, JJTINTERVAL_EXPRESSION);
int64_t days;
VisitIntervalExpressionNode(interval_expr, &days);
policy->mutable_older_than()->set_count(days);
policy->mutable_older_than()->set_unit(DDLTimeLengthProto::DAYS);
}
void VisitTableRowDeletionPolicyNode(const SimpleNode* node,
RowDeletionPolicy* policy,
std::vector<std::string>* errors) {
CheckNode(node, JJTROW_DELETION_POLICY_CLAUSE);
ABSL_CHECK_EQ(1, node->jjtGetNumChildren());
VisitRowDeletionPolicyExpressionNode(GetChildNode(node, 0), policy, errors);
}
void SetSortOrder(const SimpleNode* key_part_node, KeyPartClause* key_part,
std::vector<std::string>* errors,
bool set_default_asc = false) {
if (GetFirstChildNode(key_part_node, JJTDESC) != nullptr) {
key_part->set_order(KeyPartClause::DESC);
}
if (set_default_asc && !key_part->has_order()) {
if (GetFirstChildNode(key_part_node, JJTASC) != nullptr) {
key_part->set_order(KeyPartClause::ASC);
}
}
}
// Visit a node that defines a key.
void VisitKeyNode(const SimpleNode* node,
google::protobuf::RepeatedPtrField<KeyPartClause>* key,
std::vector<std::string>* errors) {
for (int i = 0; i < node->jjtGetNumChildren(); i++) {
SimpleNode* child = GetChildNode(node, i, JJTKEY_PART);
KeyPartClause* key_part = key->Add();
key_part->set_key_name(GetChildNode(child, 0, JJTPATH)->image());
SetSortOrder(child, key_part, errors);
}
}
void VisitStoredColumnNode(const SimpleNode* node, StoredColumnDefinition* def,
std::vector<std::string>* errors) {
CheckNode(node, JJTSTORED_COLUMN);
const int num_children = node->jjtGetNumChildren();
ABSL_CHECK_EQ(num_children, 1);
def->set_name(GetChildNode(node, 0, JJTPATH)->image());
}
void VisitStoredColumnListNode(
const SimpleNode* node,
google::protobuf::RepeatedPtrField<StoredColumnDefinition>* stored_columns,
std::vector<std::string>* errors) {
CheckNode(node, JJTSTORED_COLUMN_LIST);
ABSL_CHECK_GT(node->jjtGetNumChildren(), 0);
for (int i = 0; i < node->jjtGetNumChildren(); i++) {
SimpleNode* stored_column = GetChildNode(node, i, JJTSTORED_COLUMN);
VisitStoredColumnNode(stored_column, stored_columns->Add(), errors);
}
}
void VisitCreateIndexWhereClause(
const SimpleNode* node, google::protobuf::RepeatedPtrField<std::string>* columns) {
CheckNode(node, JJTCREATE_INDEX_WHERE_CLAUSE);
ABSL_CHECK_GT(node->jjtGetNumChildren(), 0); // Crash ok.
for (int i = 0; i < node->jjtGetNumChildren(); i++) {
SimpleNode* child = GetChildNode(node, i, JJTPATH);
*columns->Add() = child->image();
}
}
// All length requirements/restrictions will be enforced by the validator code.
void SetColumnLength(const SimpleNode* length_node, ColumnDefinition* column) {
// If the length is MAX, then we leave off the length from ColumnDefinition.
// The server will use whatever maximum length it is willing to allow, which
// may be universe- or database-specific.
if (length_node != nullptr &&
!absl::EqualsIgnoreCase(length_node->image(), "MAX")) {
column->set_length(length_node->image_as_int64());
}
}
std::string JoinDottedPath(const SimpleNode* dotted_path) {
CheckNode(dotted_path, JJTDOTTED_PATH);
std::string rv;
for (int i = 0; i < dotted_path->jjtGetNumChildren(); ++i) {
absl::StrAppend(&rv, i != 0 ? "." : "",
GetChildNode(dotted_path, i, JJTPART)->image());
}
return rv;
}
void SetTypeDefinitionLength(const SimpleNode* length_node,
TypeDefinition* type_definition) {
if (length_node != nullptr &&
!absl::EqualsIgnoreCase(length_node->image(), "MAX")) {
type_definition->set_length(length_node->image_as_int64());
}
}
void VisitTypeDefinitionNode(const SimpleNode* type_node,
TypeDefinition* type_definition,
int recursion_depth,
std::vector<std::string>* errors) {
if (recursion_depth > limits::kMaxComplexTypeNestingDepth) {
errors->push_back(
absl::StrCat("DDL parser exceeded complex type nesting limit of ",
limits::kMaxComplexTypeNestingDepth));
return;
}
const std::string type_name = absl::AsciiStrToUpper(type_node->image());
TypeDefinition::Type type;
if (type_name == "PG") {
std::string pg_type = absl::AsciiStrToUpper(
GetQualifiedIdentifier(GetChildNode(type_node, 0, JJTPGTYPE)));
if (pg_type == "NUMERIC") {
type = TypeDefinition::PG_NUMERIC;
} else if (pg_type == "JSONB") {
type = TypeDefinition::PG_JSONB;
} else {
errors->push_back(absl::Substitute(
"Syntax error on line $0 column $1: Encountered '$2' while parsing: "
"column_type",
type_node->begin_line(), type_node->begin_column(),
absl::StrCat(type_name, ".", pg_type)));
return;
}
} else if (type_name == "FLOAT32") {
// FLOAT32 => FLOAT.
type = TypeDefinition::FLOAT;
} else if (type_name == "FLOAT64") {
// FLOAT64 => DOUBLE.
type = TypeDefinition::DOUBLE;
} else if (type_name == "TOKENLIST") {
type = TypeDefinition::TOKENLIST;
} else if (!TypeDefinition::Type_Parse(type_name, &type)) {
errors->push_back(absl::StrCat("Unrecognized type: ", type_name));
return;
}
type_definition->set_type(type);
if (type == TypeDefinition::ARRAY) {
SimpleNode* column_subtype = GetChildNode(type_node, 0, JJTCOLUMN_TYPE);
VisitTypeDefinitionNode(column_subtype,
type_definition->mutable_array_subtype(),
recursion_depth + 1, errors);
} else if (type == TypeDefinition::STRUCT) {
SimpleNode* struct_fields_node =
GetFirstChildNode(type_node, JJTSTRUCT_FIELDS);
// Initialization in case the struct is empty.
type_definition->mutable_struct_descriptor();
if (struct_fields_node != nullptr) {
for (int i = 0; i < struct_fields_node->jjtGetNumChildren(); ++i) {
SimpleNode* field_node = GetChildNode(struct_fields_node, i);
TypeDefinition::StructDescriptor::Field field;
SimpleNode* name_node = GetFirstChildNode(field_node, JJTNAME);
if (name_node != nullptr) {
field.set_name(name_node->image());
}
VisitTypeDefinitionNode(GetFirstChildNode(field_node, JJTCOLUMN_TYPE),
field.mutable_type(), recursion_depth + 1,
errors);
type_definition->mutable_struct_descriptor()->mutable_field()->Add(
std::move(field));
}
}
}
SimpleNode* vector_length = GetFirstChildNode(type_node, JJTVECTOR_LENGTH);
if (vector_length != nullptr) {
errors->push_back(
absl::StrCat("'vector_length' is not supported in STRUCT of ARRAY."));
}
const SimpleNode* length_node = GetFirstChildNode(type_node, JJTLENGTH);
SetTypeDefinitionLength(length_node, type_definition);
if (type_definition->length() < 0) {
errors->push_back(absl::StrCat("Invalid length for type: ", type_name,
", found: ", length_node->image()));
}
}
void VisitColumnTypeNode(const SimpleNode* column_type,
ColumnDefinition* column, int recursion_depth,
std::vector<std::string>* errors) {
if (recursion_depth > limits::kMaxComplexTypeNestingDepth) {
errors->push_back(
absl::StrCat("DDL parser exceeded complex type nesting limit of ",
limits::kMaxComplexTypeNestingDepth));
return;
}
// Proto/enum type names don't have an entry in type registry so we handle
// them first.
if (GetFirstChildNode(column_type, JJTDOTTED_PATH) != nullptr) {
column->set_type(ColumnDefinition::NONE);
column->set_proto_type_name(
JoinDottedPath(GetChildNode(column_type, 0, JJTDOTTED_PATH)));
return;
}
std::string type_name = absl::AsciiStrToUpper(column_type->image());
ColumnDefinition::Type type;
if (type_name == "PG") {
std::string pg_type = absl::AsciiStrToUpper(
GetQualifiedIdentifier(GetChildNode(column_type, 0, JJTPGTYPE)));
if (pg_type == "NUMERIC") {
type = ColumnDefinition::PG_NUMERIC;
} else if (pg_type == "JSONB") {
type = ColumnDefinition::PG_JSONB;
} else {
errors->push_back(absl::Substitute(
"Syntax error on line $0 column $1: Encountered '$2' while parsing: "
"column_type",
column_type->begin_line(), column_type->begin_column(),
absl::StrCat(type_name, ".", pg_type)));
return;
}
} else {
if (type_name == "FLOAT64") {
type = ColumnDefinition::DOUBLE;
} else if (type_name == "FLOAT32") {
// FLOAT32 => FLOAT.
type = ColumnDefinition::FLOAT;
} else if (type_name == "TOKENLIST") {
type = ColumnDefinition::TOKENLIST;
} else if (!ColumnDefinition::Type_Parse(type_name, &type)) {
ABSL_LOG(FATAL) << "Unrecognized type: " << type_name;
}
}
column->set_type(type);
if (type == ColumnDefinition::ARRAY) {
// Read the subtype.
SimpleNode* column_subtype = GetChildNode(column_type, 0, JJTCOLUMN_TYPE);
VisitColumnTypeNode(column_subtype, column->mutable_array_subtype(),
recursion_depth + 1, errors);
SimpleNode* vector_length =
GetFirstChildNode(column_type, JJTVECTOR_LENGTH);
if (vector_length != nullptr) {
if (vector_length->image_as_int64() < 0) {
errors->push_back(
absl::StrCat("Invalid length for column: ", column->column_name(),
", found: ", vector_length->image()));
}
column->set_vector_length(vector_length->image_as_int64());
}
} else if (type == ColumnDefinition::STRUCT) {
VisitTypeDefinitionNode(column_type, column->mutable_type_definition(),
recursion_depth, errors);
}
const SimpleNode* length_node = GetFirstChildNode(column_type, JJTLENGTH);
SetColumnLength(length_node, column);
if (column->length() < 0) {
errors->push_back(
absl::StrCat("Invalid length for column: ", column->column_name(),
", found: ", length_node->image()));
}
}
void VisitGenerationClauseNode(const SimpleNode* node, ColumnDefinition* column,
absl::string_view ddl_text) {
CheckNode(node, JJTGENERATION_CLAUSE);
for (int i = 0; i < node->jjtGetNumChildren(); ++i) {
SimpleNode* child = GetChildNode(node, i);
switch (child->getId()) {
case JJTEXPRESSION: {
column->mutable_generated_column()->set_expression(
absl::StrCat("(", ExtractTextForNode(child, ddl_text), ")"));
break;
}
case JJTSTORED: {
column->mutable_generated_column()->set_stored(true);
break;
}
default:
ABSL_LOG(FATAL) << "Unexpected generated column info: " << child->toString();
}
}
}
template <typename T>
void VisitSequenceParamNode(const SimpleNode* node, T* definition,
std::vector<std::string>* errors) {
// For SKIP RANGE, there will be two children.
for (int i = 0; i < node->jjtGetNumChildren(); ++i) {
SimpleNode* child = GetChildNode(node, i);
switch (child->getId()) {
case JJTBIT_REVERSED_POSITIVE: {
if (definition->has_type()) {
errors->push_back("The sequence kind is set more than once.");
return;
}
definition->set_type(T::BIT_REVERSED_POSITIVE);
break;
}
case JJTSTART_WITH_COUNTER: {
ABSL_DCHECK_EQ(child->jjtGetNumChildren(), 1);
if (definition->has_start_with_counter()) {
errors->push_back("START WITH COUNTER is set more than once.");
return;
}
definition->set_start_with_counter(
GetChildNode(child, 0)->image_as_int64());
break;
}
case JJTSKIP_RANGE_MIN: {
if (definition->has_skip_range_min()) {
errors->push_back("SKIP RANGE is set more than once.");
return;
}
definition->set_skip_range_min(child->image_as_int64());
break;
}
case JJTSKIP_RANGE_MAX: {
if (definition->has_skip_range_max()) {
errors->push_back("SKIP RANGE is set more than once.");
return;
}
definition->set_skip_range_max(child->image_as_int64());
break;
}
default:
errors->push_back(absl::StrCat("Unexpected sequence options info: ",
child->toString()));
return;
}
}
}
template <typename T>
void VisitSequenceParamListNode(const SimpleNode* node, T* definition,
std::vector<std::string>* errors) {
CheckNode(node, JJTSEQUENCE_PARAM_LIST);
for (int i = 0; i < node->jjtGetNumChildren(); ++i) {
VisitSequenceParamNode(GetChildNode(node, i, JJTSEQUENCE_PARAM), definition,
errors);
}
}
void VisitIdentityColumnClauseNode(const SimpleNode* node,
ColumnDefinition* column,
std::vector<std::string>* errors) {
CheckNode(node, JJTIDENTITY_COLUMN_CLAUSE);
if (!EmulatorFeatureFlags::instance().flags().enable_identity_columns) {
errors->push_back("Identity columns are not supported.");
return;
}
if (node->jjtGetNumChildren() != 1) {
// There is no () clause. Make the side effect of creating the submessage
// and return. We let the sdl schema to validate if a sequence kind is not
// specified.
column->mutable_identity_column();
return;
}
SimpleNode* child = GetChildNode(node, 0, JJTSEQUENCE_PARAM_LIST);
VisitSequenceParamListNode(child, column->mutable_identity_column(), errors);
}
void VisitColumnDefaultClauseNode(const SimpleNode* node,
ColumnDefinition* column,
absl::string_view ddl_text) {
CheckNode(node, JJTCOLUMN_DEFAULT_CLAUSE);
ABSL_DCHECK_EQ(node->jjtGetNumChildren(), 1);
SimpleNode* child = GetChildNode(node, 0, JJTCOLUMN_DEFAULT_EXPRESSION);
column->mutable_column_default()->set_expression(
absl::StrCat(ExtractTextForNode(child, ddl_text)));
}
// If containing_table is not null, then we support parsing a PRIMARY KEY clause
// attached to this column; if present, this column's name will be set as the
// sole primary key component in the containing table, or a syntax error will be
// generated if the table's primary key has already been set by a previous
// column. It is expected that higher-level parsing code using this feature will
// also check that there is no table-level PRIMARY KEY clause in that case.
void VisitColumnNode(const SimpleNode* node, absl::string_view ddl_text,
ColumnDefinition* column, CreateTable* containing_table,
std::vector<std::string>* errors) {
CheckNode(node, JJTCOLUMN_DEF);
column->set_column_name(GetChildNode(node, 0, JJTNAME)->image());
SimpleNode* column_type = GetChildNode(node, 1, JJTCOLUMN_TYPE);
VisitColumnTypeNode(column_type, column, 0, errors);
// Handle NOT NULL, and OPTIONS
for (int i = 2; i < node->jjtGetNumChildren(); ++i) {
SimpleNode* child = GetChildNode(node, i);
switch (child->getId()) {
case JJTHIDDEN:
column->set_hidden(true);
break;
case JJTNOT_NULL:
column->set_not_null(true);
break;
case JJTPRIMARY_KEY:
if (containing_table != nullptr) {
if (containing_table->primary_key().empty()) {
containing_table->add_primary_key()->set_key_name(
column->column_name());
} else {
errors->push_back("Multiple columns declared as PRIMARY KEY");
}
} else {
errors->push_back("Unexpected PRIMARY KEY clause");
}
break;
case JJTGENERATION_CLAUSE:
VisitGenerationClauseNode(child, column, ddl_text);
break;
case JJTOPTIONS_CLAUSE:
VisitColumnOptionListNode(child, 0 /* option_list_offset */,
column->mutable_set_options(), errors);
break;
case JJTCOLUMN_DEFAULT_CLAUSE:
VisitColumnDefaultClauseNode(child, column, ddl_text);
break;
case JJTIDENTITY_COLUMN_CLAUSE:
VisitIdentityColumnClauseNode(child, column, errors);
break;
case JJTAUTO_INCREMENT: {
if (!EmulatorFeatureFlags::instance()
.flags()
.enable_serial_auto_increment) {
errors->push_back("AUTO_INCREMENT is not supported.");
return;
}
// Basically, AUTO_INCREMENT is a syntax sugar for IDENTITY COLUMN.
column->mutable_identity_column();
break;
}
case JJTPLACEMENT_KEY:
column->set_placement_key(true);
break;
default:
ABSL_LOG(FATAL) << "Unexpected column info: " << child->toString();
}
}
}
void VisitColumnNodeAlterAttrs(const SimpleNode* node,
const std::string& column_name,
ColumnDefinition* column,
absl::string_view ddl_text,
std::vector<std::string>* errors) {
CheckNode(node, JJTCOLUMN_DEF_ALTER_ATTRS);
column->set_column_name(column_name);
SimpleNode* column_type = GetChildNode(node, 0, JJTCOLUMN_TYPE);
VisitColumnTypeNode(column_type, column, 0, errors);
// Handle NOT NULL.
for (int i = 1; i < node->jjtGetNumChildren(); ++i) {
SimpleNode* child = GetChildNode(node, i);
switch (child->getId()) {
case JJTNOT_NULL:
column->set_not_null(true);
break;
case JJTGENERATION_CLAUSE:
VisitGenerationClauseNode(child, column, ddl_text);
break;
case JJTCOLUMN_DEFAULT_CLAUSE:
VisitColumnDefaultClauseNode(child, column, ddl_text);
break;
case JJTIDENTITY_COLUMN_CLAUSE:
VisitIdentityColumnClauseNode(child, column, errors);
break;
default:
ABSL_LOG(FATAL) << "Unexpected column info: " << child->toString();
}
}
}
void VisitColumnNodeAlter(const std::string& table_name,
const std::string& column_name,
const SimpleNode* node, absl::string_view ddl_text,
DDLStatement* statement,
std::vector<std::string>* errors) {
CheckNode(node, JJTCOLUMN_DEF_ALTER);
const SimpleNode* child = GetChildNode(node, 0);
AlterTable* alter_table = statement->mutable_alter_table();
if (child->getId() == JJTOPTIONS_CLAUSE) {
// "ALTER COLUMN c SET OPTIONS (...)" does not contain the
// column TYPE or NOT NULL attributes. Translate this into a
// SetColumnOptions statement which does not take these
// attributes as input, and the schema change code will keep
// these attributes unchanged.
SetColumnOptions* set_options = statement->mutable_set_column_options();
SetColumnOptions::ColumnPath* path = set_options->add_column_path();
path->set_table_name(table_name);
path->set_column_name(column_name);
VisitColumnOptionListNode(child, 0 /* option_list_offset */,
set_options->mutable_options(), errors);
return;
}
if (child->getId() == JJTCOLUMN_DEF_ALTER_ATTRS) {
// For "ALTER COLUMN c TYPE NOT NULL"
alter_table->set_table_name(table_name);
ColumnDefinition* column =
alter_table->mutable_alter_column()->mutable_column();
VisitColumnNodeAlterAttrs(child, column_name, column, ddl_text, errors);
return;
}
alter_table->set_table_name(table_name);
AlterTable::AlterColumn* alter_column = alter_table->mutable_alter_column();
ColumnDefinition* column = alter_column->mutable_column();
column->set_column_name(column_name);
// `type` is required in ColumnDefinition, so set it to NONE here.
column->set_type(ColumnDefinition::NONE);
switch (child->getId()) {
case JJTCOLUMN_DEFAULT_CLAUSE: {
// "ALTER COLUMN c SET DEFAULT " does not contain the column TYPE or
// NOT NULL attributes.
alter_column->set_operation(AlterTable::AlterColumn::SET_DEFAULT);
VisitColumnDefaultClauseNode(child, column, ddl_text);
break;
}
case JJTDROP_COLUMN_DEFAULT: {
// "ALTER COLUMN c DROP DEFAULT " does not contain the column TYPE or
// NOT NULL attributes.
alter_column->set_operation(AlterTable::AlterColumn::DROP_DEFAULT);
column->clear_column_default();
break;
}
case JJTSET_NOT_NULL: {
errors->push_back(
"ALTER COLUMN SET NOT NULL not supported without a column type");
break;
}
case JJTDROP_NOT_NULL: {
errors->push_back(
"ALTER COLUMN DROP NOT NULL not supported without a column type");
break;
}
case JJTRESTART_COUNTER: {
if (!EmulatorFeatureFlags::instance().flags().enable_identity_columns) {
errors->push_back("Identity columns are not supported.");
return;
}
// The reason to use two separate fields is because we would like to have
// the capability to support updating multiple properties in a single
// statement. Also, if we supported RESTART COUNTER without WITH, we can
// set this bool to true but not set the start_with_counter field.
alter_column->set_operation(AlterTable::AlterColumn::ALTER_IDENTITY);
alter_column->set_identity_alter_start_with_counter(true);
column->mutable_identity_column()->set_start_with_counter(
child->image_as_int64());
break;
}
case JJTSKIP_RANGE_MIN: {
if (!EmulatorFeatureFlags::instance().flags().enable_identity_columns) {
errors->push_back("Identity columns are not supported.");
return;
}
if (node->jjtGetNumChildren() != 2) {
// It should theoretically never happen.
errors->push_back(
"skip_range_max is missing in ALTER IDENTITY SET SKIP RANGE.");
return;
}
const SimpleNode* next_child = GetChildNode(node, 1);
CheckNode(next_child, JJTSKIP_RANGE_MAX);
alter_column->set_operation(AlterTable::AlterColumn::ALTER_IDENTITY);
alter_column->set_identity_alter_skip_range(true);
column->mutable_identity_column()->set_skip_range_min(
child->image_as_int64());
column->mutable_identity_column()->set_skip_range_max(
next_child->image_as_int64());
break;
}
case JJTNO_SKIP_RANGE: {
if (!EmulatorFeatureFlags::instance().flags().enable_identity_columns) {
errors->push_back("Identity columns are not supported.");
return;
}
alter_column->set_operation(AlterTable::AlterColumn::ALTER_IDENTITY);
alter_column->set_identity_alter_skip_range(true);
column->mutable_identity_column()->clear_skip_range_min();
column->mutable_identity_column()->clear_skip_range_max();
break;
}
default:
ABSL_LOG(FATAL) << "Unexpected alter column type: "
<< GetChildNode(node, 1)->toString();
}
}
void AddForeignKeyColumnNames(SimpleNode* child,
std::function<void(const std::string&)> add) {
const SimpleNode* names = GetChildNode(child, 0, JJTIDENTIFIER_LIST);
for (int i = 0; i < names->jjtGetNumChildren(); i++) {
const SimpleNode* name = GetChildNode(names, i, JJTIDENTIFIER);
add(name->image());
}
}
ForeignKey::Action GetForeignKeyAction(const SimpleNode* node) {
CheckNode(node, JJTON_DELETE);
SimpleNode* action = GetChildNode(node, 0, JJTREFERENTIAL_ACTION);
SimpleNode* child = GetChildNode(action, 0);
switch (child->getId()) {
case JJTNO_ACTION:
return ForeignKey::NO_ACTION;
case JJTCASCADE:
return ForeignKey::CASCADE;
default:
ABSL_LOG(FATAL) << "Unexpected foreign key action: " << child->toString();
return ForeignKey::ACTION_UNSPECIFIED;
}
}
void VisitForeignKeyEnforcementNode(const SimpleNode* node,
ForeignKey* foreign_key,
std::vector<std::string>* errors) {
CheckNode(node, JJTENFORCEMENT);
if (!EmulatorFeatureFlags::instance().flags().enable_fk_enforcement_option) {
errors->push_back("Foreign key enforcement is not supported.");
return;
}
SimpleNode* child = GetChildNode(node, 0);
switch (child->getId()) {
case JJTENFORCED:
foreign_key->set_enforced(true);
return;
case JJTNOT_ENFORCED:
foreign_key->set_enforced(false);
// Relies on the fact that JJTON_DELETE, if exists, is parsed before this.
if (foreign_key->on_delete() != ForeignKey::ACTION_UNSPECIFIED &&
foreign_key->on_delete() != ForeignKey::NO_ACTION) {
errors->push_back(
"ON DELETE actions are not supported for NOT ENFORCED foreign "
"keys.");
}
return;
default:
// Should never happen since the parser rule only defines the two nodes
// above.
ABSL_LOG(FATAL) << "Unexpected foreign key enforcement: "
<< child->toString(); // Crash OK
return;
}
}
void VisitForeignKeyNode(const SimpleNode* node, ForeignKey* foreign_key,
std::vector<std::string>* errors) {
CheckNode(node, JJTFOREIGN_KEY);
// Default enforcement is true, when not specified.
foreign_key->set_enforced(true);
for (int i = 0; i < node->jjtGetNumChildren(); i++) {
SimpleNode* child = GetChildNode(node, i);
switch (child->getId()) {
case JJTCONSTRAINT_NAME:
foreign_key->set_constraint_name(child->image());
break;
case JJTREFERENCING_COLUMNS:
AddForeignKeyColumnNames(
child, [&foreign_key](const std::string& name) {
foreign_key->add_constrained_column_name(name);
});
break;
case JJTREFERENCED_TABLE:
foreign_key->set_referenced_table_name(GetQualifiedIdentifier(child));
break;
case JJTREFERENCED_COLUMNS:
AddForeignKeyColumnNames(
child, [&foreign_key](const std::string& name) {
foreign_key->add_referenced_column_name(name);
});
break;
case JJTON_DELETE:
foreign_key->set_on_delete(GetForeignKeyAction(child));
break;
case JJTENFORCEMENT:
VisitForeignKeyEnforcementNode(child, foreign_key, errors);
break;
default:
// We can only get here if there is a bug in the grammar or parser.
ABSL_LOG(FATAL) << "Unexpected foreign key attribute: " << child->toString();
}
}
}
void VisitCheckConstraintNode(const SimpleNode* node,
absl::string_view ddl_text,
CheckConstraint* check_constraint,
std::vector<std::string>* errors) {
CheckNode(node, JJTCHECK_CONSTRAINT);
check_constraint->set_enforced(true);
for (int i = 0; i < node->jjtGetNumChildren(); i++) {
SimpleNode* child = GetChildNode(node, i);
switch (child->getId()) {
case JJTCONSTRAINT_NAME: {
check_constraint->set_name(child->image());
continue;
}
case JJTCHECK_CONSTRAINT_EXPRESSION: {
check_constraint->set_expression(
absl::StrCat(ExtractTextForNode(child, ddl_text)));
continue;
}
default: {
ABSL_LOG(FATAL) << "Unexpected check constraint attribute: "
<< child->toString();
}
}
}
}
void VisitCreateTableNode(const SimpleNode* node, CreateTable* table,
absl::string_view ddl_text,
std::vector<std::string>* errors) {
CheckNode(node, JJTCREATE_TABLE_STATEMENT);
int offset = 0;
// We may have an optional IF NOT EXISTS node before the name.
if (GetChildNode(node, offset)->getId() == JJTIF_NOT_EXISTS) {
table->set_existence_modifier(IF_NOT_EXISTS);
offset++;
}
table->set_table_name(
GetQualifiedIdentifier(GetChildNode(node, offset, JJTNAME)));
offset++;
bool has_primary_key = false;
for (int i = offset; i < node->jjtGetNumChildren(); i++) {
SimpleNode* child = GetChildNode(node, i);
switch (child->getId()) {
case JJTCOLUMN_DEF:
VisitColumnNode(child, ddl_text, table->add_column(), table, errors);
break;
case JJTFOREIGN_KEY:
VisitForeignKeyNode(child, table->add_foreign_key(), errors);
break;
case JJTCHECK_CONSTRAINT:
VisitCheckConstraintNode(child, ddl_text, table->add_check_constraint(),
errors);
break;
case JJTPRIMARY_KEY:
if (table->primary_key().empty()) {
VisitKeyNode(child, table->mutable_primary_key(), errors);
} else {
errors->push_back("Cannot specify both table and column PRIMARY KEY");
}
has_primary_key = true;
break;
case JJTSYNONYM_CLAUSE:
table->set_synonym(GetChildNode(child, 0)->image());
break;
case JJTTABLE_INTERLEAVE_CLAUSE:
VisitTableInterleaveNode(child, table->mutable_interleave_clause());
break;
case JJTROW_DELETION_POLICY_CLAUSE:
VisitTableRowDeletionPolicyNode(
child, table->mutable_row_deletion_policy(), errors);
break;
case JJTOPTIONS_CLAUSE:
VisitTableOptionListNode(child, 0 /* option_list_offset */,
table->mutable_set_options(), errors);
break;
default:
ABSL_LOG(FATAL) << "Unexpected table info: " << child->toString();
}
}
if (table->primary_key().empty() && !has_primary_key) {
// Even for singleton tables, a table-level PRIMARY KEY() must be specified.
errors->push_back("Must specify either table or column PRIMARY KEY");
}
}
void VisitCreateFunctionNode(const SimpleNode* node,
CreateFunction* create_function,
bool is_or_replace, absl::string_view ddl_text,
std::vector<std::string>* errors) {
if (!EmulatorFeatureFlags::instance().flags().enable_user_defined_functions) {
errors->push_back("User defined functions are not supported.");
return;
}
CheckNode(node, JJTCREATE_FUNCTION_STATEMENT);
const SimpleNode* name_node = GetFirstChildNode(node, JJTNAME);
ABSL_DCHECK(name_node);
bool prev_param_has_default = false;
const SimpleNode* param_list_node =
GetFirstChildNode(node, JJTFUNCTION_PARAMETER_LIST);
if (param_list_node) {
for (int i = 0; i < param_list_node->jjtGetNumChildren(); i++) {
SimpleNode* function_param_node = GetChildNode(param_list_node, i);
Function::Parameter* param = create_function->add_param();
param->set_name(ExtractTextForNode(
GetFirstChildNode(function_param_node, JJTNAME), ddl_text));
param->set_param_typename(ExtractTextForNode(
GetFirstChildNode(function_param_node, JJTFUNCTION_DATA_TYPE),
ddl_text));
SimpleNode* default_value_node =
GetFirstChildNode(function_param_node, JJTPARAM_DEFAULT_EXPRESSION);
if (default_value_node) {
prev_param_has_default = true;
param->set_default_value(
absl::StrCat(ExtractTextForNode(default_value_node, ddl_text)));
} else if (prev_param_has_default) {
errors->push_back(
"Function parameters must have default values if any previous "
"parameter has a default value.");
return;
}
}
}
create_function->set_function_name(GetQualifiedIdentifier(name_node));
create_function->set_function_kind(Function::FUNCTION);
create_function->set_language(Function::SQL);
if (is_or_replace) {
create_function->set_is_or_replace(true);
}
const SimpleNode* return_type_node = GetFirstChildNode(node, JJTRETURN_TYPE);
if (return_type_node) {
create_function->set_return_typename(ExtractTextForNode(
GetFirstChildNode(return_type_node, JJTFUNCTION_DATA_TYPE), ddl_text));
}
const SimpleNode* security_node = GetFirstChildNode(node, JJTSQL_SECURITY);
if (security_node) {
create_function->set_sql_security(Function::INVOKER);
}
const SimpleNode* definition_node =
GetFirstChildNode(node, JJTFUNCTION_DEFINITION);
ABSL_DCHECK(definition_node);
create_function->set_sql_body(
std::string(ExtractTextForNode(definition_node, ddl_text)));
}
void VisitCreateViewNode(const SimpleNode* node, CreateFunction* function,
bool is_or_replace, absl::string_view ddl_text,
std::vector<std::string>* errors) {
CheckNode(node, JJTCREATE_VIEW_STATEMENT);
const SimpleNode* name = GetFirstChildNode(node, JJTNAME);
ABSL_DCHECK(name);
function->set_function_name(GetQualifiedIdentifier(name));
function->set_language(Function::SQL);
function->set_function_kind(Function::VIEW);
if (is_or_replace) {
function->set_is_or_replace(true);
}
if (GetFirstChildNode(node, JJTSQL_SECURITY)) {
function->set_sql_security(Function::INVOKER);
}
const SimpleNode* view_definition =
GetFirstChildNode(node, JJTVIEW_DEFINITION);
function->set_sql_body(
absl::StrCat((ExtractTextForNode(view_definition, ddl_text))));
}
void VisitCreateIndexNode(const SimpleNode* node, CreateIndex* index,
std::vector<std::string>* errors) {
CheckNode(node, JJTCREATE_INDEX_STATEMENT);
for (int i = 0; i < node->jjtGetNumChildren(); i++) {
SimpleNode* child = GetChildNode(node, i);
switch (child->getId()) {
case JJTUNIQUE_INDEX:
index->set_unique(true);
break;
case JJTNULL_FILTERED:
index->set_null_filtered(true);
break;
case JJTNAME:
index->set_index_name(GetQualifiedIdentifier(child));
break;
case JJTTABLE:
index->set_index_base_name(GetQualifiedIdentifier(child));
break;
case JJTCOLUMNS:
VisitKeyNode(child, index->mutable_key(), errors);
break;
case JJTSTORED_COLUMN_LIST:
VisitStoredColumnListNode(
child, index->mutable_stored_column_definition(), errors);
break;
case JJTINDEX_INTERLEAVE_CLAUSE:
VisitIndexInterleaveNode(child, index->mutable_interleave_in_table());
break;
case JJTCREATE_INDEX_WHERE_CLAUSE:
VisitCreateIndexWhereClause(child,
index->mutable_null_filtered_column());
break;
case JJTIF_NOT_EXISTS:
index->set_existence_modifier(IF_NOT_EXISTS);
break;
case JJTOPTIONS_CLAUSE:
VisitIndexOptionListNode(child, 0 /* option_list_offset */,
index->mutable_set_options(), errors);
break;
default:
ABSL_LOG(FATAL) << "Unexpected index info: " << child->toString();
}
}
}
void VisitTokenKeyListNode(
const SimpleNode* node,
google::protobuf::RepeatedPtrField<TokenColumnDefinition>* token_columns,
std::vector<std::string>* errors) {
CheckNode(node, JJTTOKEN_KEY_LIST);
ABSL_CHECK_GT(node->jjtGetNumChildren(), 0);
for (int i = 0; i < node->jjtGetNumChildren(); i++) {
SimpleNode* token_key = GetChildNode(node, i, JJTKEY_PART);
KeyPartClause* key_part = token_columns->Add()->mutable_token_column();
key_part->set_key_name(GetChildNode(token_key, 0, JJTPATH)->image());
SetSortOrder(token_key, key_part, errors, /*set_default_asc=*/true);
}
}
void VisitSearchIndexOptionKeyValNode(const SimpleNode* node,
absl::string_view option_name,
OptionList* options,
std::vector<std::string>* errors) {
if (option_name != kSearchIndexOptionSortOrderShardingName &&
option_name != kSearchIndexOptionsDisableAutomaticUidName) {
errors->push_back(absl::StrCat("Option: ", option_name, " is unknown."));
return;
}
SetOption* option = options->Add();
option->set_option_name(option_name);
const SimpleNode* child = GetChildNode(node, 1);
switch (child->getId()) {
case JJTBOOL_TRUE_VAL:
option->set_bool_value(true);
break;
case JJTBOOL_FALSE_VAL:
option->set_bool_value(false);
break;
default: {
errors->push_back(
absl::StrCat("Unexpected value for option: ", option_name,
". "
"Supported option values are true and false."));
break;
}
}
}
void VisitSearchIndexOptionsClause(const SimpleNode* node, OptionList* options,
std::vector<std::string>* errors) {
CheckNode(node, JJTOPTIONS_CLAUSE);
absl::flat_hash_set<std::string> options_names;
for (int i = 0; i < node->jjtGetNumChildren(); ++i) {
const auto* child = GetChildNode(node, i, JJTOPTION_KEY_VAL);
std::string option_name = CheckOptionKeyValNodeAndGetName(child);
if (options_names.contains(option_name)) {
errors->push_back(absl::StrCat("Duplicate option: ", option_name));
return;
}
options_names.insert(option_name);
VisitSearchIndexOptionKeyValNode(child, option_name, options, errors);
}
}
void VisitVectorIndexOptionKeyValNode(const SimpleNode* node,
OptionList* options,
std::vector<std::string>* errors) {
std::string option_name = CheckOptionKeyValNodeAndGetName(node);
// If this is an invalid option, return error. Later during schema
// change, we will verify the valid option against the column type.
if (option_name == kVectorIndexTreeDepth ||
option_name == kVectorIndexNumberOfLeaves ||
option_name == kVectorIndexNumberOfBranches ||
option_name == kVectorIndexLeafScatterFactor ||
option_name == kVectorIndexMinBranchSplits ||
option_name == kVectorIndexMinLeafSplits) {
SetOption* option = options->Add();
option->set_option_name(option_name);
const SimpleNode* value_node = GetChildNode(node, 1);
if (value_node->getId() == JJTINTEGER_VAL) {
const int64_t value = value_node->image_as_int64();
option->set_int64_value(value);
} else {
errors->push_back(
absl::StrCat("Unexpected value for option: ", option->option_name(),
". Supported option values are integers."));
return;
}
} else if (option_name == kVectorIndexDistanceType) {
SetOption* option = options->Add();
option->set_option_name(option_name);
const SimpleNode* value_node = GetChildNode(node, 1);
if (value_node->getId() == JJTSTR_VAL &&
ValidateStringLiteralImage(value_node->image(), /*force=*/true, nullptr)
.ok()) {
std::string string_value;
std::string error = "";
if (!UnescapeStringLiteral(value_node->image(), &string_value, &error)) {
errors->push_back(error);
return;
}
option->set_string_value(string_value);
} else {
errors->push_back(
absl::StrCat("Unexpected value for option: ", option->option_name(),
". Supported option values are strings."));
return;
}
} else if (option_name == kLocalityGroupOptionName) {
VisitLocalityGroupName(node, options, errors);
} else {
// Unknown option.
errors->push_back(absl::StrCat("Option: ", option_name, " is unknown."));
return;
}
}
void VisitVectorIndexOptionsClause(const SimpleNode* node, OptionList* options,
std::vector<std::string>* errors) {
CheckNode(node, JJTOPTIONS_CLAUSE);
for (int i = 0; i < node->jjtGetNumChildren(); ++i) {
VisitVectorIndexOptionKeyValNode(GetChildNode(node, i, JJTOPTION_KEY_VAL),
options, errors);
}
}
void VisitCreateVectorIndexNode(const SimpleNode* node,
CreateVectorIndex* index,
std::vector<std::string>* errors) {
CheckNode(node, JJTCREATE_VECTOR_INDEX_STATEMENT);
for (int i = 0; i < node->jjtGetNumChildren(); i++) {
SimpleNode* child = GetChildNode(node, i);
switch (child->getId()) {
case JJTNAME:
index->set_index_name(GetQualifiedIdentifier(child));
break;
case JJTINDEX_BASE:
index->set_index_base_name(GetQualifiedIdentifier(child));
break;
case JJTINDEX_KEY: {
SimpleNode* key_part_node = GetChildNode(child, 0, JJTKEY_PART);
KeyPartClause* key_part = index->mutable_key();
key_part->set_key_name(
GetChildNode(key_part_node, 0, JJTPATH)->image());
SetSortOrder(key_part_node, key_part, errors);
break;
}
case JJTSTORED_COLUMN_LIST:
VisitStoredColumnListNode(
child, index->mutable_stored_column_definition(), errors);
break;
case JJTPARTITION_KEY:
VisitKeyNode(child, index->mutable_partition_by(), errors);
break;
case JJTCREATE_INDEX_WHERE_CLAUSE:
VisitCreateIndexWhereClause(child,
index->mutable_null_filtered_column());
break;
case JJTOPTIONS_CLAUSE:
VisitVectorIndexOptionsClause(child, index->mutable_set_options(),
errors);
break;
case JJTIF_NOT_EXISTS: {
index->set_existence_modifier(IF_NOT_EXISTS);
break;
}
default:
ABSL_LOG(FATAL) << "Unexpected index info: " // Crash OK.
<< child->toString();
}
}
}
void VisitCreateSearchIndexNode(const SimpleNode* node,
CreateSearchIndex* index,
std::vector<std::string>* errors) {
CheckNode(node, JJTCREATE_SEARCH_INDEX_STATEMENT);
for (int i = 0; i < node->jjtGetNumChildren(); i++) {
SimpleNode* child = GetChildNode(node, i);
switch (child->getId()) {
case JJTNAME:
index->set_index_name(GetQualifiedIdentifier(child));
break;
case JJTTABLE:
index->set_index_base_name(GetQualifiedIdentifier(child));
break;
case JJTTOKEN_KEY_LIST:
VisitTokenKeyListNode(child, index->mutable_token_column_definition(),
errors);
break;
case JJTPARTITION_KEY:
VisitKeyNode(child, index->mutable_partition_by(), errors);
break;
case JJTORDER_BY_KEY:
VisitKeyNode(child, index->mutable_order_by(), errors);
break;
case JJTSTORED_COLUMN_LIST:
VisitStoredColumnListNode(
child, index->mutable_stored_column_definition(), errors);
break;
case JJTCREATE_INDEX_WHERE_CLAUSE:
VisitCreateIndexWhereClause(child,
index->mutable_null_filtered_column());
break;
case JJTINDEX_INTERLEAVE_CLAUSE:
VisitIndexInterleaveNode(child, index->mutable_interleave_in_table());
break;
case JJTOPTIONS_CLAUSE:
VisitSearchIndexOptionsClause(child, index->mutable_set_options(),
errors);
break;
default:
ABSL_LOG(FATAL) << "Unexpected search index info: " << child->toString();
}
}
}
void VisitChangeStreamExplicitColumns(
const SimpleNode* node,
ChangeStreamForClause::TrackedTables::Entry::TrackedColumns*
tracked_columns,
std::vector<std::string>* errors) {
CheckNode(node, JJTEXPLICIT_COLUMNS);
for (int i = 0; i < node->jjtGetNumChildren(); i++) {
SimpleNode* child = GetChildNode(node, i);
switch (child->getId()) {
case JJTCOLUMN:
tracked_columns->add_column_name(child->image());
break;
default:
ABSL_LOG(FATAL) << "Unexpected change streams tracked column: "
<< child->toString();
}
}
}
void VisitChangeStreamTrackedTablesEntry(
const SimpleNode* node,
ChangeStreamForClause::TrackedTables::Entry* table_entry,
std::vector<std::string>* errors) {
CheckNode(node, JJTCHANGE_STREAM_TRACKED_TABLES_ENTRY);
table_entry->set_table_name(
GetQualifiedIdentifier(GetChildNode(node, 0, JJTTABLE)));
bool all_columns = true;
for (int i = 1; i < node->jjtGetNumChildren(); i++) {
SimpleNode* child = GetChildNode(node, i);
switch (child->getId()) {
case JJTEXPLICIT_COLUMNS: {
all_columns = false;
VisitChangeStreamExplicitColumns(
child, table_entry->mutable_tracked_columns(), errors);
break;
}
default:
ABSL_LOG(FATAL) << "Unexpected change streams tracked tables entry: "
<< child->toString();
}
}
if (all_columns) {
// `all_columns` is part of a oneof, so we only set it if it is true.
table_entry->set_all_columns(true);
}
}
void VisitChangeStreamTrackedTables(
const SimpleNode* node,
ChangeStreamForClause::TrackedTables* tracked_tables,
std::vector<std::string>* errors) {
CheckNode(node, JJTCHANGE_STREAM_TRACKED_TABLES);
// The parser does not accept a FOR clause without anything following.
ABSL_CHECK_GE(node->jjtGetNumChildren(), 1);
google::protobuf::RepeatedPtrField<ChangeStreamForClause::TrackedTables::Entry>*
table_entry = tracked_tables->mutable_table_entry();
ChangeStreamForClause::TrackedTables::Entry* last = nullptr;
for (int i = 0; i < node->jjtGetNumChildren(); i++) {
SimpleNode* child = GetChildNode(node, i);
switch (child->getId()) {
case JJTCHANGE_STREAM_TRACKED_TABLES_ENTRY: {
last = table_entry->Add();
VisitChangeStreamTrackedTablesEntry(child, last, errors);
break;
}
default:
ABSL_LOG(FATAL) << "Unexpected change stream tracked tables: "
<< child->toString();
}
}
}
void VisitChangeStreamForClause(const SimpleNode* node,
ChangeStreamForClause* for_clause,
std::vector<std::string>* errors) {
CheckNode(node, JJTCHANGE_STREAM_FOR_CLAUSE);
ABSL_CHECK_EQ(1, node->jjtGetNumChildren());
SimpleNode* child = GetChildNode(node, 0);
switch (child->getId()) {
case JJTALL:
for_clause->set_all(true);
break;
case JJTCHANGE_STREAM_TRACKED_TABLES:
VisitChangeStreamTrackedTables(
child, for_clause->mutable_tracked_tables(), errors);
break;
default:
ABSL_LOG(FATAL) << "Unexpected change stream for clause: "
<< child->toString();
}
}
// Build a string representing a basic logical error for the given node.
// Errors reported with LogicalError should be things that can only be detected
// during parsing, and not later during canonicalization. If an error can be
// detected during canonicalization, defer it to then, because we expect that
// some customers may want to generate the DDL statements themselves and so
// we'll have to check for it there anyway.
std::string LogicalError(const SimpleNode* node, const std::string& detail) {
return absl::StrCat("Error on line ", node->begin_line(), ", column ",
node->begin_column(), ": ", detail);
}
// Handle witness_location for database options
void VisitWitnessLocationDatabaseOptionValNode(
const SimpleNode* value_node, SetOption* option,
std::vector<std::string>* errors) {
if (value_node->getId() == JJTNULLL) {
option->set_null_value(true);
return;
}
if (value_node->getId() != JJTSTR_VAL ||
!ValidateStringLiteralImage(value_node->image(), /*force=*/true, nullptr)
.ok()) {
errors->push_back(
absl::StrCat("Unexpected value for option: witness_location."
" Supported values are non-empty strings only."));
return;
}
std::string string_value;
std::string error = "";
if (!UnescapeStringLiteral(value_node->image(), &string_value, &error)) {
errors->push_back(error);
return;
}
option->set_string_value(string_value);
}
void VisitDefaultLeaderDatabaseOptionValNode(const SimpleNode* value_node,
SetOption* option,
std::vector<std::string>* errors) {
ABSL_DCHECK_EQ(option->option_name(), "default_leader");
if (value_node->getId() == JJTSTR_VAL &&
ValidateStringLiteralImage(value_node->image(), /*force=*/true, nullptr)
.ok()) {
std::string string_value;
std::string error = "";
if (!UnescapeStringLiteral(value_node->image(), &string_value, &error)) {
errors->push_back(error);
return;
}
if (string_value.empty()) {
errors->push_back(
"Empty string is an invalid value for default_leader. If you'd like "
"to clear a previously set value, use NULL.");
return;
}
option->set_string_value(string_value);
} else if (value_node->getId() == JJTNULLL) {
option->set_null_value(true);
} else {
errors->push_back(
absl::StrCat("Unexpected value for option: ", "default_leader",
". Supported option values are strings and NULL."));
return;
}
}
void VisitDefaultSequenceKindDatabaseOptionValNode(
const SimpleNode* value_node, SetOption* option,
std::vector<std::string>* errors) {
ABSL_DCHECK_EQ(option->option_name(), kDefaultSequenceKindOptionName);
if (value_node->getId() == JJTSTR_VAL &&
ValidateStringLiteralImage(value_node->image(), /*force=*/true, nullptr)
.ok()) {
std::string string_value;
std::string error = "";
if (!UnescapeStringLiteral(value_node->image(), &string_value, &error)) {
errors->push_back(error);
return;
}
option->set_string_value(string_value);
} else if (value_node->getId() == JJTNULLL) {
option->set_null_value(true);
} else {
errors->push_back(absl::StrCat(
"Unexpected value for option: ", kDefaultSequenceKindOptionName,
". Supported option values are strings and NULL."));
return;
}
}
void VisitDefaultTimeZoneDatabaseOptionValNode(
const SimpleNode* value_node, SetOption* option,
std::vector<std::string>* errors) {
ABSL_DCHECK_EQ(option->option_name(), kDefaultTimeZoneOptionName);
if (value_node->getId() == JJTSTR_VAL &&
ValidateStringLiteralImage(value_node->image(), /*force=*/true, nullptr)
.ok()) {
std::string string_value;
std::string error = "";
if (!UnescapeStringLiteral(value_node->image(), &string_value, &error)) {
errors->push_back(error);
return;
}
option->set_string_value(string_value);
} else if (value_node->getId() == JJTNULLL) {
option->set_null_value(true);
} else {
errors->push_back(absl::StrCat(
"Unexpected value for option: ", kDefaultTimeZoneOptionName,
". Supported option values are strings and NULL."));
return;
}
}
void VisitVersionRetentionPeriodDatabaseOptionValNode(
const SimpleNode* value_node, SetOption* option,
std::vector<std::string>* errors) {
ABSL_DCHECK_EQ(option->option_name(), kVersionRetentionPeriodOptionName);
if (value_node->getId() == JJTNULLL) {
option->set_null_value(true);
return;
}
if (value_node->getId() != JJTSTR_VAL ||
!ValidateStringLiteralImage(value_node->image(), /*force=*/true, nullptr)
.ok()) {
errors->push_back(
absl::StrCat("Unexpected value for option: version_retention_period."
" Supported values are non-empty strings only."));
return;
}
std::string string_value;
std::string error = "";
if (!UnescapeStringLiteral(value_node->image(), &string_value, &error)) {
errors->push_back(error);
return;
}
option->set_string_value(string_value);
}
void VisitDatabaseOptionKeyValNode(const SimpleNode* node, OptionList* options,
std::vector<std::string>* errors) {
std::string option_name = CheckOptionKeyValNodeAndGetName(node);
if (absl::c_find_if(*options, [&option_name](const SetOption& option) {
return option.option_name() == option_name;
}) != options->end()) {
errors->push_back(absl::StrCat("Duplicate option: ", option_name));
return;
}
const SimpleNode* value_node = GetChildNode(node, 1);
if (option_name == kWitnessLocationOptionName) {
SetOption* option = options->Add();
option->set_option_name("witness_location");
VisitWitnessLocationDatabaseOptionValNode(value_node, option, errors);
if (option->has_null_value()) {
errors->push_back("Option: witness_location is null.");
return;
}
} else if (option_name == "witness_location_type") {
SetOption* option = options->Add();
option->set_option_name("spanner.internal.cloud_witness_location_type");
VisitWitnessLocationDatabaseOptionValNode(value_node, option, errors);
} else if (option_name == kDefaultLeaderOptionName) {
SetOption* option = options->Add();
option->set_option_name("default_leader");
VisitDefaultLeaderDatabaseOptionValNode(value_node, option, errors);
if (option->has_null_value()) {
errors->push_back("Option: default_leader is null.");
return;
}
} else if (option_name == kDefaultSequenceKindOptionName &&
EmulatorFeatureFlags::instance().flags().enable_identity_columns) {
SetOption* option = options->Add();
option->set_option_name(kDefaultSequenceKindOptionName);
VisitDefaultSequenceKindDatabaseOptionValNode(value_node, option, errors);
} else if (option_name == kDefaultTimeZoneOptionName &&
EmulatorFeatureFlags::instance()
.flags()
.enable_default_time_zone) {
SetOption* option = options->Add();
option->set_option_name(kDefaultTimeZoneOptionName);
VisitDefaultTimeZoneDatabaseOptionValNode(value_node, option, errors);
} else if (option_name == kVersionRetentionPeriodOptionName) {
SetOption* option = options->Add();
option->set_option_name(kVersionRetentionPeriodOptionName);
VisitVersionRetentionPeriodDatabaseOptionValNode(value_node, option,
errors);
} else {
errors->push_back(absl::StrCat("Option: ", option_name, " is unknown."));
}
}
void VisitDatabaseOptionListNode(const SimpleNode* node, int option_list_offset,
OptionList* options,
std::vector<std::string>* errors) {
CheckNode(node, JJTOPTIONS_CLAUSE);
// The option_list node is suppressed (defined #void in .jjt) so it is not
// created. The children of this node are OPTION_KEY_VALs.
for (int i = option_list_offset; i < node->jjtGetNumChildren(); ++i) {
VisitDatabaseOptionKeyValNode(GetChildNode(node, i, JJTOPTION_KEY_VAL),
options, errors);
}
}
void VisitAlterDatabaseNode(const SimpleNode* node, AlterDatabase* database,
std::vector<std::string>* errors) {
CheckNode(node, JJTALTER_DATABASE_STATEMENT);
// Alter Database DDL doesn't take db_name, setting it for the ability to
// print the DDL statement in sdl_printer
database->set_db_name(GetChildNode(node, 0, JJTDATABASE_NAME)->image());
VisitDatabaseOptionListNode(
GetChildNode(node, 1, JJTOPTIONS_CLAUSE), /*option_list_offset=*/0,
database->mutable_set_options()->mutable_options(), errors);
}
void VisitStringOrNullOptionValNode(const SimpleNode* value_node,
SetOption* option,
std::vector<std::string>* errors) {
if (value_node->getId() == JJTNULLL) {
option->set_null_value(true);
return;
}
if (value_node->getId() != JJTSTR_VAL ||
!ValidateStringLiteralImage(value_node->image(), /*force=*/true, nullptr)
.ok()) {
errors->push_back(
absl::StrCat("Unexpected value for option: ", option->option_name(),
". Supported option values are strings and NULL."));
return;
}
std::string string_value;
std::string error = "";
if (!UnescapeStringLiteral(value_node->image(), &string_value, &error)) {
errors->push_back(error);
return;
}
option->set_string_value(string_value);
}
void VisitStringArrayOrNullOptionValNode(const SimpleNode* value_node,
SetOption* option,
std::vector<std::string>* errors) {
if (value_node->getId() == JJTNULLL) {
option->set_null_value(true);
return;
}
if (value_node->getId() != JJTSTR_VAL_LIST) {
errors->push_back(
absl::StrCat("Unexpected value for option: ", option->option_name(),
". Supported option values are string array and NULL."));
return;
}
auto* values = option->mutable_string_list_value();
for (int i = 0; i < value_node->jjtGetNumChildren(); ++i) {
const SimpleNode* value = GetChildNode(value_node, i);
CheckNode(value, JJTSTR_VAL);
std::string error = "";
if (!UnescapeStringLiteral(value->image(), values->Add(), &error)) {
errors->push_back(error);
return;
}
}
}
void VisitInt64OrNullOptionValNode(const SimpleNode* value_node,
SetOption* option,
std::vector<std::string>* errors) {
if (value_node->getId() == JJTINTEGER_VAL) {
const int64_t value = value_node->image_as_int64();
option->set_int64_value(value);
} else if (value_node->getId() == JJTNULLL) {
option->set_null_value(true);
} else {
errors->push_back(
absl::StrCat("Unexpected value for option: ", option->option_name(),
". Supported option values are integers and NULL."));
return;
}
}
void VisitBoolOrNullOptionValNode(const SimpleNode* value_node,
SetOption* option,
std::vector<std::string>* errors) {
if (value_node->getId() == JJTBOOL_TRUE_VAL) {
option->set_bool_value(true);
} else if (value_node->getId() == JJTBOOL_FALSE_VAL) {
option->set_bool_value(false);
} else if (value_node->getId() == JJTNULLL) {
option->set_null_value(true);
} else {
errors->push_back(
absl::StrCat("Unexpected value for option: ", option->option_name(),
". Supported option values are booleans and NULL."));
return;
}
}
void VisitChangeStreamOptionKeyValNode(const SimpleNode* node,
OptionList* options,
std::vector<std::string>* errors) {
std::string name = CheckOptionKeyValNodeAndGetName(node);
for (const SetOption& option : *options) {
if (option.option_name() == name) {
errors->push_back(absl::StrCat("Duplicate option: ", name));
return;
}
}
const SimpleNode* value_node = GetChildNode(node, 1);
if (kChangeStreamStringOptions->contains(name)) {
SetOption* option = options->Add();
option->set_option_name(name);
VisitStringOrNullOptionValNode(value_node, option, errors);
} else if (kChangeStreamBooleanOptions->contains(name)) {
SetOption* option = options->Add();
option->set_option_name(name);
VisitBoolOrNullOptionValNode(value_node, option, errors);
} else {
errors->push_back(absl::StrCat("Option: ", name, " is unknown."));
}
}
void VisitChangeStreamOptionsClause(const SimpleNode* node,
int option_list_offset, OptionList* options,
std::vector<std::string>* errors) {
CheckNode(node, JJTOPTIONS_CLAUSE);
// The option_list node is suppressed (defined #void in .jjt) so it is not
// created. The children of this node are OPTION_KEY_VALs.
for (int i = option_list_offset; i < node->jjtGetNumChildren(); ++i) {
VisitChangeStreamOptionKeyValNode(GetChildNode(node, i, JJTOPTION_KEY_VAL),
options, errors);
}
}
void VisitCreateChangeStreamNode(const SimpleNode* node,
CreateChangeStream* change_stream,
std::vector<std::string>* errors) {
CheckNode(node, JJTCREATE_CHANGE_STREAM_STATEMENT);
change_stream->set_change_stream_name(
GetQualifiedIdentifier(GetChildNode(node, 0, JJTNAME)));
for (int i = 1; i < node->jjtGetNumChildren(); i++) {
const SimpleNode* child = GetChildNode(node, i);
switch (child->getId()) {
case JJTCHANGE_STREAM_FOR_CLAUSE:
VisitChangeStreamForClause(child, change_stream->mutable_for_clause(),
errors);
break;
case JJTOPTIONS_CLAUSE:
VisitChangeStreamOptionsClause(child, /*option_list_offset=*/0,
change_stream->mutable_set_options(),
errors);
break;
default:
ABSL_LOG(FATAL) << "Unexpected create change stream clause: "
<< child->toString();
}
}
}
bool IsSupportedSequenceOption(const std::string& option_name) {
if (option_name == kSequenceKindOptionName ||
option_name == kSequenceSkipRangeMinOptionName ||
option_name == kSequenceSkipRangeMaxOptionName ||
option_name == kSequenceStartWithCounterOptionName) {
return true;
}
return false;
}
void VisitSequenceOptionKeyValNode(const SimpleNode* node, OptionList* options,
std::vector<std::string>* errors,
bool* sequence_kind_visited) {
std::string name = CheckOptionKeyValNodeAndGetName(node);
if (absl::c_find_if(*options, [&name](const SetOption& option) {
return option.option_name() == name;
}) != options->end()) {
errors->push_back(absl::StrCat("Duplicate option: ", name));
return;
}
const SimpleNode* value_node = GetChildNode(node, 1);
if (!IsSupportedSequenceOption(name)) {
errors->push_back(absl::StrCat("Option: ", name, " is unknown."));
return;
}
*sequence_kind_visited = false;
SetOption* option = options->Add();
option->set_option_name(name);
if (name == kSequenceKindOptionName) {
VisitStringOrNullOptionValNode(value_node, option, errors);
*sequence_kind_visited = true;
if (option->has_null_value()) {
errors->push_back(
"The only supported sequence kind is `bit_reversed_positive`");
return;
}
if (option->has_string_value() &&
option->string_value() != kSequenceKindBitReversedPositive) {
errors->push_back(
absl::StrCat("Unsupported sequence kind: ", option->string_value()));
}
} else {
VisitInt64OrNullOptionValNode(value_node, option, errors);
}
}
void VisitCreateSequenceNode(const SimpleNode* node, CreateSequence* sequence,
std::vector<std::string>* errors) {
CheckNode(node, JJTCREATE_SEQUENCE_STATEMENT);
int offset = 0;
if (GetFirstChildNode(node, JJTIF_NOT_EXISTS) != nullptr) {
sequence->set_existence_modifier(IF_NOT_EXISTS);
++offset;
}
sequence->set_sequence_name(
GetQualifiedIdentifier(GetChildNode(node, offset++, JJTNAME)));
while (offset < node->jjtGetNumChildren()) {
const SimpleNode* child = GetChildNode(node, offset++);
switch (child->getId()) {
case JJTSEQUENCE_PARAM_LIST:
if (!EmulatorFeatureFlags::instance().flags().enable_identity_columns) {
errors->push_back(
"Using SQL clauses to configure sequence options is not "
"supported in CREATE SEQUENCE statements.");
return;
}
VisitSequenceParamListNode(child, sequence, errors);
break;
case JJTOPTIONS_CLAUSE: {
OptionList* options = sequence->mutable_set_options();
bool sequence_kind_visited = false;
bool has_valid_sequence_kind = false;
for (int i = 0; i < child->jjtGetNumChildren(); ++i) {
VisitSequenceOptionKeyValNode(
GetChildNode(child, i, JJTOPTION_KEY_VAL), options, errors,
&sequence_kind_visited);
if (sequence_kind_visited) {
has_valid_sequence_kind = sequence_kind_visited;
}
}
if (!has_valid_sequence_kind &&
!EmulatorFeatureFlags::instance().flags().enable_identity_columns) {
errors->push_back(
"CREATE SEQUENCE statements require option `sequence_kind` to "
"be set");
return;
}
break;
}
default:
errors->push_back(absl::StrCat("Unexpected create sequence clause: ",
child->toString()));
return;
}
}
}
void VisitCreateSchemaNode(const SimpleNode* node, CreateSchema* schema,
std::vector<std::string>* errors) {
CheckNode(node, JJTCREATE_SCHEMA_STATEMENT);
int offset = 0;
if (GetFirstChildNode(node, JJTIF_NOT_EXISTS) != nullptr) {
schema->set_existence_modifier(CreateSchema::IF_NOT_EXISTS);
++offset;
}
schema->set_schema_name(
GetQualifiedIdentifier(GetChildNode(node, offset++, JJTNAME)));
// No options for CreateSchema yet.
}
void VisitAlterSequenceNode(const SimpleNode* node, AlterSequence* sequence,
std::vector<std::string>* errors) {
CheckNode(node, JJTALTER_SEQUENCE_STATEMENT);
int offset = 0;
if (GetFirstChildNode(node, JJTIF_EXISTS) != nullptr) {
sequence->set_existence_modifier(IF_EXISTS);
++offset;
}
sequence->set_sequence_name(
GetQualifiedIdentifier(GetChildNode(node, offset++, JJTNAME)));
const SimpleNode* child = GetChildNode(node, offset);
switch (child->getId()) {
case JJTOPTIONS_CLAUSE: {
OptionList* options = sequence->mutable_set_options()->mutable_options();
for (int i = 0; i < child->jjtGetNumChildren(); ++i) {
bool sequence_kind_visited; // Unused
VisitSequenceOptionKeyValNode(GetChildNode(child, i, JJTOPTION_KEY_VAL),
options, errors, &sequence_kind_visited);
}
break;
}
case JJTRESTART_COUNTER: {
if (!EmulatorFeatureFlags::instance().flags().enable_identity_columns) {
errors->push_back(
"RESTART COUNTER WITH is not supported in ALTER SEQUENCE "
"statements.");
return;
}
sequence->mutable_set_start_with_counter()->set_counter_value(
child->image_as_int64());
break;
}
case JJTSKIP_RANGE_MIN: {
if (!EmulatorFeatureFlags::instance().flags().enable_identity_columns) {
errors->push_back(
"SKIP RANGE is not supported in ALTER SEQUENCE statements.");
return;
}
if (node->jjtGetNumChildren() != 3) {
// It should theoretically never happen.
errors->push_back(
"skip_range_max is missing for SET SKIP RANGE in ALTER SEQUENCE "
"statements.");
return;
}
const SimpleNode* next_child = GetChildNode(node, offset + 1);
CheckNode(next_child, JJTSKIP_RANGE_MAX);
sequence->mutable_set_skip_range()->set_min_value(
child->image_as_int64());
sequence->mutable_set_skip_range()->set_max_value(
next_child->image_as_int64());
break;
}
case JJTNO_SKIP_RANGE: {
if (!EmulatorFeatureFlags::instance().flags().enable_identity_columns) {
errors->push_back(
"NO SKIP RANGE is not supported in ALTER SEQUENCE statements.");
return;
}
sequence->mutable_set_skip_range()->clear_min_value();
sequence->mutable_set_skip_range()->clear_max_value();
break;
}
default:
errors->push_back(absl::StrCat("Unexpected alter sequence clause: ",
child->toString()));
return;
}
}
void VisitAlterSchemaNode(const SimpleNode* node, AlterSchema* schema,
std::vector<std::string>* errors) {
CheckNode(node, JJTALTER_SCHEMA_STATEMENT);
int offset = 0;
if (GetFirstChildNode(node, JJTIF_EXISTS) != nullptr) {
schema->set_if_exists(true);
++offset;
}
schema->set_schema_name(
GetQualifiedIdentifier(GetChildNode(node, offset++, JJTNAME)));
// No options for AlterSchema yet.
}
void VisitAlterChangeStreamNode(const SimpleNode* node,
AlterChangeStream* change_stream,
std::vector<std::string>* errors) {
CheckNode(node, JJTALTER_CHANGE_STREAM_STATEMENT);
change_stream->set_change_stream_name(
GetQualifiedIdentifier(GetChildNode(node, 0, JJTNAME)));
const SimpleNode* child = GetChildNode(node, 1);
switch (child->getId()) {
case JJTCHANGE_STREAM_FOR_CLAUSE:
VisitChangeStreamForClause(child, change_stream->mutable_set_for_clause(),
errors);
break;
case JJTOPTIONS_CLAUSE:
VisitChangeStreamOptionsClause(
child, /*option_list_offset=*/0,
change_stream->mutable_set_options()->mutable_options(), errors);
break;
case JJTDROP_FOR_ALL: {
ChangeStreamForClause* drop_for_clause =
change_stream->mutable_drop_for_clause();
drop_for_clause->set_all(true);
break;
}
default:
ABSL_LOG(FATAL) << "Unexpected alter change stream clause: "
<< child->toString();
}
}
void VisitAlterProtoBundleNode(const SimpleNode* node,
AlterProtoBundle* alter_proto_bundle,
std::vector<std::string>* errors) {
CheckNode(node, JJTALTER_PROTO_BUNDLE_STATEMENT);
google::protobuf::RepeatedPtrField<ProtoType>* type_source_names = nullptr;
bool delete_type = false;
for (int i = 0; i < node->jjtGetNumChildren(); i++) {
SimpleNode* child = GetChildNode(node, i);
switch (child->getId()) {
case JJTINSERT:
type_source_names = alter_proto_bundle->mutable_insert_type();
break;
case JJTUPDATE:
type_source_names = alter_proto_bundle->mutable_update_type();
break;
case JJTDELETE:
delete_type = true;
break;
case JJTDOTTED_PATH: {
const std::string type = JoinDottedPath(child);
if (!delete_type) {
type_source_names->Add()->set_source_name(type);
} else {
alter_proto_bundle->add_delete_type(type);
}
break;
}
default:
ABSL_LOG(FATAL) << "Unexpected alter proto bundle type: "
<< child->toString();
}
}
}
void VisitAlterTableNode(const SimpleNode* node, absl::string_view ddl_text,
DDLStatement* statement,
std::vector<std::string>* errors) {
CheckNode(node, JJTALTER_TABLE_STATEMENT);
const SimpleNode* child = GetChildNode(node, 1);
std::string table_name =
GetQualifiedIdentifier(GetFirstChildNode(node, JJTTABLE_NAME));
if (child->getId() == JJTALTER_COLUMN) {
// Depending on the ALTER COLUMN variant, we may generate either
// an AlterTable or SetColumnOptions statement.
std::string column_name = GetChildNode(node, 2, JJTNAME)->image();
VisitColumnNodeAlter(table_name, column_name,
GetChildNode(node, 3, JJTCOLUMN_DEF_ALTER), ddl_text,
statement, errors);
} else {
// These will generate an AlterTable statement.
AlterTable* alter_table = statement->mutable_alter_table();
alter_table->set_table_name(table_name);
switch (child->getId()) {
case JJTRENAME_TO: {
alter_table->mutable_rename_to()->set_name(
GetQualifiedIdentifier(child));
if (node->jjtGetNumChildren() > 2) {
SimpleNode* synonym = GetChildNode(node, 2, JJTSYNONYM);
alter_table->mutable_rename_to()->set_synonym(
GetQualifiedIdentifier(synonym));
}
break;
}
case JJTADD_COLUMN: {
int offset = 2;
if (GetChildNode(node, offset)->getId() == JJTIF_NOT_EXISTS) {
offset++;
alter_table->mutable_add_column()->set_existence_modifier(
IF_NOT_EXISTS);
}
VisitColumnNode(GetChildNode(node, offset, JJTCOLUMN_DEF), ddl_text,
alter_table->mutable_add_column()->mutable_column(),
/*containing_table=*/nullptr, errors);
} break;
case JJTDROP_COLUMN: {
SimpleNode* column = GetChildNode(node, 2, JJTCOLUMN_NAME);
alter_table->set_drop_column(column->image());
break;
}
case JJTSET_ON_DELETE: {
SimpleNode* on_delete_node = GetChildNode(node, 2, JJTON_DELETE_CLAUSE);
InterleaveClause::Action on_delete_action;
VisitOnDeleteClause(on_delete_node, &on_delete_action);
alter_table->mutable_set_on_delete()->set_action(on_delete_action);
break;
}
case JJTFOREIGN_KEY:
VisitForeignKeyNode(
child,
alter_table->mutable_add_foreign_key()->mutable_foreign_key(),
errors);
break;
case JJTCHECK_CONSTRAINT:
VisitCheckConstraintNode(child, ddl_text,
alter_table->mutable_add_check_constraint()
->mutable_check_constraint(),
errors);
break;
case JJTDROP_CONSTRAINT: {
SimpleNode* constraint_name = GetChildNode(node, 2, JJTCONSTRAINT_NAME);
alter_table->mutable_drop_constraint()->set_name(
constraint_name->image());
break;
}
case JJTADD_ROW_DELETION_POLICY: {
VisitTableRowDeletionPolicyNode(
GetChildNode(child, 0, JJTROW_DELETION_POLICY_CLAUSE),
alter_table->mutable_add_row_deletion_policy(), errors);
break;
}
case JJTREPLACE_ROW_DELETION_POLICY: {
VisitTableRowDeletionPolicyNode(
GetChildNode(child, 0, JJTROW_DELETION_POLICY_CLAUSE),
alter_table->mutable_alter_row_deletion_policy(), errors);
break;
}
case JJTDROP_ROW_DELETION_POLICY: {
alter_table->mutable_drop_row_deletion_policy();
break;
}
case JJTADD_SYNONYM: {
alter_table->mutable_add_synonym()->set_synonym(
GetQualifiedIdentifier(child));
break;
}
case JJTDROP_SYNONYM: {
alter_table->mutable_drop_synonym()->set_synonym(
GetQualifiedIdentifier(child));
break;
}
case JJTOPTIONS_CLAUSE: {
VisitTableOptionListNode(
child, 0 /* option_list_offset */,
alter_table->mutable_set_options()->mutable_options(), errors);
break;
}
default:
ABSL_LOG(FATAL) << "Unexpected alter table type: "
<< GetChildNode(node, 1)->toString();
}
}
}
void VisitAlterIndexNode(const SimpleNode* node, AlterIndex* alter_index,
std::vector<std::string>* errors) {
CheckNode(node, JJTALTER_INDEX_STATEMENT);
const SimpleNode* alter_type = GetChildNode(node, 1);
alter_index->set_index_name(
GetQualifiedIdentifier(GetFirstChildNode(node, JJTNAME)));
switch (alter_type->getId()) {
case JJTADD: {
const SimpleNode* column_name = GetFirstChildNode(node, JJTCOLUMN_NAME);
alter_index->mutable_add_stored_column()->set_column_name(
column_name->image());
break;
}
case JJTDROP: {
const SimpleNode* column_name = GetFirstChildNode(node, JJTCOLUMN_NAME);
alter_index->set_drop_stored_column(column_name->image());
break;
}
case JJTOPTIONS_CLAUSE: {
VisitIndexOptionListNode(
alter_type, 0 /* option_list_offset */,
alter_index->mutable_set_options()->mutable_options(), errors);
break;
}
default: {
errors->push_back(LogicalError(
alter_type, absl::StrCat("Unexpected value for alter index type: ",
alter_type->image())));
break;
}
}
}
void VisitAddStoredColumnNode(const SimpleNode* node,
AlterVectorIndex* alter_index,
std::vector<std::string>* errors) {
CheckNode(node, JJTADD_STORED_COLUMN);
ABSL_CHECK_EQ(node->jjtGetNumChildren(), 1); // Crash OK
VisitStoredColumnNode(
GetChildNode(node, 0, JJTSTORED_COLUMN),
alter_index->mutable_add_stored_column()->mutable_column(), errors);
}
void VisitAlterVectorIndexNode(const SimpleNode* node,
AlterVectorIndex* alter_vector_index,
std::vector<std::string>* errors) {
CheckNode(node, JJTALTER_VECTOR_INDEX_STATEMENT);
const SimpleNode* alter_type = GetChildNode(node, 1);
alter_vector_index->set_index_name(
GetQualifiedIdentifier(GetFirstChildNode(node, JJTNAME)));
const SimpleNode* child = GetChildNode(node, 1);
switch (alter_type->getId()) {
case JJTADD_STORED_COLUMN:
VisitAddStoredColumnNode(child, alter_vector_index, errors);
break;
case JJTDROP_STORED_COLUMN_NAME:
alter_vector_index->set_drop_stored_column(child->image());
break;
default: {
errors->push_back(LogicalError(
alter_type,
absl::StrCat("Unexpected value for alter vector index type: ",
alter_type->image())));
break;
}
}
}
void VisitPrivilegeNode(const SimpleNode* node, Privilege* privilege,
std::vector<std::string>* errors) {
CheckNode(node, JJTPRIVILEGE);
std::string privilege_name = node->image();
if (absl::EqualsIgnoreCase("SELECT", privilege_name)) {
privilege->set_type(Privilege::SELECT);
} else if (absl::EqualsIgnoreCase("INSERT", privilege_name)) {
privilege->set_type(Privilege::INSERT);
} else if (absl::EqualsIgnoreCase("UPDATE", privilege_name)) {
privilege->set_type(Privilege::UPDATE);
} else if (absl::EqualsIgnoreCase("DELETE", privilege_name)) {
privilege->set_type(Privilege::DELETE);
} else if (absl::EqualsIgnoreCase("EXECUTE", privilege_name)) {
privilege->set_type(Privilege::EXECUTE);
} else if (absl::EqualsIgnoreCase("USAGE", privilege_name)) {
privilege->set_type(Privilege::USAGE);
} else {
errors->push_back(
absl::StrCat("Unexpected privilege type: ", node->image(), "."));
return;
}
// TODO: Add support for column-level FGAC
if (node->jjtGetNumChildren() != 0) {
errors->push_back(
"Emulator does not yet support column level access controls");
}
}
void VisitPrivilegesNode(const SimpleNode* node, Privileges* privileges,
std::vector<std::string>* errors) {
CheckNode(node, JJTPRIVILEGES);
for (int i = 0; i < node->jjtGetNumChildren(); i++) {
SimpleNode* privilege = GetChildNode(node, i);
VisitPrivilegeNode(privilege, privileges->Add(), errors);
}
}
void VisitPrivilegeTargetsNode(const SimpleNode* node, PrivilegeTarget* target,
std::vector<std::string>* errors) {
CheckNode(node, JJTPRIVILEGE_TARGET);
SimpleNode* child = GetChildNode(node, 0);
CheckNode(child, JJTTARGET_TYPE);
child = GetChildNode(child, 0);
switch (child->getId()) {
case JJTTABLE: {
target->set_type(PrivilegeTarget::TABLE);
break;
}
case JJTCHANGE_STREAM: {
target->set_type(PrivilegeTarget::CHANGE_STREAM);
break;
}
case JJTVIEW: {
target->set_type(PrivilegeTarget::VIEW);
break;
}
case JJTFUNCTION: {
target->set_type(PrivilegeTarget::FUNCTION);
break;
}
case JJTTABLE_FUNCTION: {
target->set_type(PrivilegeTarget::TABLE_FUNCTION);
break;
}
case JJTSEQUENCE: {
target->set_type(PrivilegeTarget::SEQUENCE);
break;
}
default: {
errors->push_back(
absl::StrCat("Unexpected privilege target: ", child->image()));
}
}
SimpleNode* names = GetChildNode(node, 1);
for (int i = 0; i < names->jjtGetNumChildren(); ++i) {
SimpleNode* name = GetChildNode(names, i);
target->add_name(GetQualifiedIdentifier(name));
}
}
void VisitGranteesNode(const SimpleNode* node, Grantees* grantees,
std::vector<std::string>* errors) {
for (int i = 0; i < node->jjtGetNumChildren(); i++) {
SimpleNode* grantee_node = GetChildNode(node, i);
Grantee* grantee = grantees->Add();
grantee->set_name(grantee_node->image());
grantee->set_type(Grantee::ROLE);
}
}
void VisitGrantPrivilegeNode(const SimpleNode* node, GrantPrivilege* statement,
std::vector<std::string>* errors) {
CheckNode(node, JJTGRANT_STATEMENT);
VisitPrivilegesNode(GetChildNode(node, 0), statement->mutable_privilege(),
errors);
VisitPrivilegeTargetsNode(GetChildNode(node, 1), statement->mutable_target(),
errors);
SimpleNode* grantees_node = GetChildNode(node, 2);
VisitGranteesNode(GetChildNode(grantees_node, 0),
statement->mutable_grantee(), errors);
}
void VisitGrantMembershipNode(const SimpleNode* node, GrantMembership* grant,
std::vector<std::string>* errors) {
CheckNode(node, JJTGRANT_STATEMENT);
SimpleNode* roles_node = GetChildNode(node, 0);
VisitGranteesNode(GetChildNode(roles_node, 0), grant->mutable_role(), errors);
SimpleNode* grantees_node = GetChildNode(node, 1);
VisitGranteesNode(GetChildNode(grantees_node, 0), grant->mutable_grantee(),
errors);
}
void VisitRevokePrivilegeNode(const SimpleNode* node, RevokePrivilege* revoke,
std::vector<std::string>* errors) {
CheckNode(node, JJTREVOKE_STATEMENT);
VisitPrivilegesNode(GetChildNode(node, 0), revoke->mutable_privilege(),
errors);
VisitPrivilegeTargetsNode(GetChildNode(node, 1), revoke->mutable_target(),
errors);
SimpleNode* grantees_node = GetChildNode(node, 2);
VisitGranteesNode(GetChildNode(grantees_node, 0), revoke->mutable_grantee(),
errors);
}
void VisitRevokeMembershipNode(const SimpleNode* node, RevokeMembership* revoke,
std::vector<std::string>* errors) {
CheckNode(node, JJTREVOKE_STATEMENT);
SimpleNode* roles_node = GetChildNode(node, 0);
VisitGranteesNode(GetChildNode(roles_node, 0), revoke->mutable_role(),
errors);
SimpleNode* grantees_node = GetChildNode(node, 1);
VisitGranteesNode(GetChildNode(grantees_node, 0), revoke->mutable_grantee(),
errors);
}
void VisitCreatePropertyGraphNode(const SimpleNode* node,
CreatePropertyGraph* property_graph,
bool is_or_replace, absl::string_view ddl,
std::vector<std::string>* errors) {
CheckNode(node, JJTCREATE_PROPERTY_GRAPH_STATEMENT);
if (is_or_replace) {
property_graph->set_existence_modifier(OR_REPLACE);
property_graph->set_ddl_body(
absl::StrCat("CREATE OR REPLACE ", ExtractTextForNode(node, ddl)));
} else {
property_graph->set_ddl_body(
absl::StrCat("CREATE ", ExtractTextForNode(node, ddl)));
}
for (int i = 0; i < node->jjtGetNumChildren(); i++) {
SimpleNode* child = GetChildNode(node, i);
switch (child->getId()) {
case JJTNAME:
property_graph->set_name(GetQualifiedIdentifier(child));
break;
case JJTIF_NOT_EXISTS:
if (property_graph->existence_modifier() != NONE) {
errors->push_back(
"CREATE PROPERTY GRAPH IF NOT EXISTS cannot "
"be used with other existence modifiers such as `OR REPLACE`.");
return;
}
property_graph->set_existence_modifier(IF_NOT_EXISTS);
break;
case JJTELEMENT_TABLES:
// Do nothing: the `element_tables` part is already
// captured in `ddl_text`.
break;
default:
ABSL_LOG(FATAL) << "Unexpected CREATE PROPERTY GRAPH statement parts: "
<< child->toString();
break;
}
}
}
void VisitModelOptionKeyValNode(const SimpleNode* node, OptionList* options,
std::vector<std::string>* errors) {
std::string name = CheckOptionKeyValNodeAndGetName(node);
// Options is an accumulator that contains already visited options. If the
// current node contains option name that was already seen, it's a duplicate.
if (absl::c_find_if(*options, [&name](const SetOption& option) {
return option.option_name() == name;
}) != options->end()) {
errors->push_back(absl::StrCat("Duplicate option: ", name));
return;
}
SetOption* option = options->Add();
option->set_option_name(name);
const SimpleNode* value_node = GetChildNode(node, 1);
if (name == kModelDefaultBatchSizeOptionName) {
VisitInt64OrNullOptionValNode(value_node, option, errors);
} else if (name == kModelEndpointOptionName) {
VisitStringOrNullOptionValNode(value_node, option, errors);
} else if (name == kModelEndpointsOptionName) {
VisitStringArrayOrNullOptionValNode(value_node, option, errors);
} else {
errors->push_back(absl::StrCat("Option: ", name, " is unknown."));
}
}
void VisitModelOptionListNode(const SimpleNode* node, OptionList* options,
std::vector<std::string>* errors) {
CheckNode(node, JJTOPTIONS_CLAUSE);
// The option_list node is suppressed (defined #void in .jjt) so it is not
// created. The children of this node are OPTION_KEY_VALs.
for (int i = 0; i < node->jjtGetNumChildren(); ++i) {
VisitModelOptionKeyValNode(GetChildNode(node, i, JJTOPTION_KEY_VAL),
options, errors);
}
}
void VisitModelColumnList(const SimpleNode* node,
google::protobuf::RepeatedPtrField<ColumnDefinition>* columns,
absl::string_view ddl_text,
std::vector<std::string>* errors) {
for (int i = 0; i < node->jjtGetNumChildren(); i++) {
SimpleNode* child = GetChildNode(node, i);
CheckNode(child, JJTCOLUMN_DEF);
VisitColumnNode(child, ddl_text, columns->Add(),
/*containing_table=*/nullptr, errors);
}
}
void VisitCreateModelNode(const SimpleNode* node, CreateModel* create_model,
bool is_or_replace, absl::string_view ddl_text,
std::vector<std::string>* errors) {
CheckNode(node, JJTCREATE_MODEL_STATEMENT);
create_model->set_model_name(
GetQualifiedIdentifier(GetFirstChildNode(node, JJTNAME)));
if (GetFirstChildNode(node, JJTIF_NOT_EXISTS) != nullptr) {
create_model->set_existence_modifier(IF_NOT_EXISTS);
if (is_or_replace) {
errors->push_back(
absl::StrCat("CREATE MODEL statement cannot have both OR REPLACE and "
"IF NOT EXISTS set: ",
create_model->model_name()));
return;
}
}
if (is_or_replace) {
create_model->set_existence_modifier(OR_REPLACE);
}
SimpleNode* input = GetFirstChildNode(node, JJTINPUT);
if (input != nullptr) {
VisitModelColumnList(input, create_model->mutable_input(), ddl_text,
errors);
}
SimpleNode* output = GetFirstChildNode(node, JJTOUTPUT);
if (output != nullptr) {
VisitModelColumnList(output, create_model->mutable_output(), ddl_text,
errors);
}
const SimpleNode* remote = GetFirstChildNode(node, JJTREMOTE);
if (remote != nullptr) {
create_model->set_remote(true);
}
const SimpleNode* options = GetFirstChildNode(node, JJTOPTIONS_CLAUSE);
if (options != nullptr) {
VisitModelOptionListNode(options, create_model->mutable_set_options(),
errors);
}
}
void VisitAlterModelNode(const SimpleNode* node, AlterModel* alter_model,
std::vector<std::string>* errors) {
CheckNode(node, JJTALTER_MODEL_STATEMENT);
alter_model->set_model_name(
GetQualifiedIdentifier(GetFirstChildNode(node, JJTNAME)));
if (GetFirstChildNode(node, JJTIF_EXISTS) != nullptr) {
alter_model->set_if_exists(true);
}
const SimpleNode* options = GetFirstChildNode(node, JJTOPTIONS_CLAUSE);
if (options != nullptr) {
VisitModelOptionListNode(
options, alter_model->mutable_set_options()->mutable_options(), errors);
}
}
void VisitRenameTableNode(const SimpleNode* node, RenameTable* rename_table,
std::vector<std::string>* errors) {
CheckNode(node, JJTRENAME_STATEMENT);
for (int i = 0; i < node->jjtGetNumChildren(); ++i) {
SimpleNode* op = GetChildNode(node, i);
CheckNode(op, JJTRENAME_OP);
ABSL_DCHECK_EQ(op->jjtGetNumChildren(), 2);
RenameTable::RenameOp* rename_op = rename_table->add_rename_op();
rename_op->set_from_name(GetChildNode(op, 0)->image());
rename_op->set_to_name(GetChildNode(op, 1)->image());
}
}
void VisitCreateProtoBundleNode(const SimpleNode* node,
CreateProtoBundle* proto_bundle,
std::vector<std::string>* errors) {
CheckNode(node, JJTCREATE_PROTO_BUNDLE_STATEMENT);
for (int i = 0; i < node->jjtGetNumChildren(); i++) {
proto_bundle->add_insert_type()->set_source_name(
JoinDottedPath(GetChildNode(node, i, JJTDOTTED_PATH)));
}
}
std::string getLocalityGroupName(const SimpleNode* node, int offset) {
const SimpleNode* name_node = GetChildNode(node, offset);
if (name_node->getId() == JJTDEFAULTT) {
return kDefaultLocalityGroupName;
} else {
CheckNode(name_node, JJTNAME);
return GetQualifiedIdentifier(name_node);
}
}
void VisitLocalityGroupOptionKeyValNode(const SimpleNode* node,
OptionList* options,
std::vector<std::string>* errors) {
const std::string name = CheckOptionKeyValNodeAndGetName(node);
if (absl::c_find_if(*options, [&name](const SetOption& option) {
return option.option_name() == name;
}) != options->end()) {
errors->push_back(absl::StrCat("Duplicate option: ", name));
return;
}
SetOption* option = options->Add();
if (name == kLocalityGroupStorageOptionName) {
option->set_option_name(kInternalLocalityGroupStorageOptionName);
} else if (name == kLocalityGroupSpillTimeSpanOptionName) {
option->set_option_name(kInternalLocalityGroupSpillTimeSpanOptionName);
} else {
errors->push_back(absl::StrCat("Option: ", name, " is unknown."));
return;
}
const SimpleNode* value_node = GetChildNode(node, 1);
if (value_node->getId() == JJTNULLL) {
option->set_null_value(true);
return;
}
std::optional<std::string> string_value;
if (value_node->getId() == JJTSTR_VAL) {
std::string error = "";
if (!ValidateStringLiteralImage(value_node->image(), /*force=*/true,
/*error=*/nullptr)
.ok() ||
!UnescapeStringLiteral(value_node->image(), &string_value.emplace(),
&error)) {
errors->push_back(LogicalError(
value_node,
absl::StrCat("Cannot parse string literal: ", value_node->image())));
return;
}
}
if (name == kLocalityGroupStorageOptionName &&
value_node->getId() == JJTSTR_VAL) {
if (*string_value != kLocalityGroupStorageOptionSSDVal &&
*string_value != kLocalityGroupStorageOptionHDDVal) {
errors->push_back(absl::StrCat(
"Unexpected value for option: ", name,
". Supported option values are \"", kLocalityGroupStorageOptionSSDVal,
"\" and \"", kLocalityGroupStorageOptionHDDVal, "\"."));
return;
}
option->set_bool_value(*string_value == kLocalityGroupStorageOptionSSDVal);
} else if (name == kLocalityGroupSpillTimeSpanOptionName &&
value_node->getId() == JJTSTR_VAL) {
// TODO: Move the below if logic to sdl_options.cc.
if (ParseSchemaTimeSpec(*string_value) == -1) {
errors->push_back(absl::StrCat("Cannot parse ", *string_value,
" as a valid timestamp"));
return;
}
option->add_string_list_value(absl::StrCat("disk", ":", *string_value));
} else {
errors->push_back(
absl::StrCat("Unexpected value type for option: ", name, "."));
return;
}
}
void VisitCreateLocalityGroupNode(const SimpleNode* node,
CreateLocalityGroup* create_locality_group,
std::vector<std::string>* errors) {
CheckNode(node, JJTCREATE_LOCALITY_GROUP_STATEMENT);
int offset = 0;
// We may have an optional IF NOT EXISTS node before the name.
if (GetChildNode(node, offset)->getId() == JJTIF_NOT_EXISTS) {
create_locality_group->set_existence_modifier(IF_NOT_EXISTS);
offset++;
}
create_locality_group->set_locality_group_name(
getLocalityGroupName(node, offset));
offset++;
if (node->jjtGetNumChildren() == offset + 1) {
const SimpleNode* options_clause =
GetChildNode(node, offset, JJTOPTIONS_CLAUSE);
OptionList* options = create_locality_group->mutable_set_options();
for (int i = 0; i < options_clause->jjtGetNumChildren(); ++i) {
VisitLocalityGroupOptionKeyValNode(
GetChildNode(options_clause, i, JJTOPTION_KEY_VAL), options, errors);
}
}
}
void VisitAlterLocalityGroupNode(const SimpleNode* node,
AlterLocalityGroup* alter_locality_group,
std::vector<std::string>* errors) {
CheckNode(node, JJTALTER_LOCALITY_GROUP_STATEMENT);
int offset = 0;
// We may have an optional IF EXISTS node before the name.
if (GetChildNode(node, offset)->getId() == JJTIF_EXISTS) {
alter_locality_group->set_existence_modifier(IF_EXISTS);
offset++;
}
alter_locality_group->set_locality_group_name(
getLocalityGroupName(node, offset));
offset++;
if (node->jjtGetNumChildren() == offset + 1) {
const SimpleNode* options_clause =
GetChildNode(node, offset, JJTOPTIONS_CLAUSE);
OptionList* options =
alter_locality_group->mutable_set_options()->mutable_options();
for (int i = 0; i < options_clause->jjtGetNumChildren(); ++i) {
VisitLocalityGroupOptionKeyValNode(
GetChildNode(options_clause, i, JJTOPTION_KEY_VAL), options, errors);
}
}
}
void VisitInstancePartitionPlacementOptionValNode(
const SimpleNode* value_node, SetOption* option,
std::vector<std::string>* errors) {
ABSL_DCHECK_EQ(option->option_name(), kPlacementInstancePartitionOptionName);
std::string error = "";
if (value_node->getId() == JJTSTR_VAL &&
ValidateStringLiteralImage(value_node->image(), /*force=*/true, nullptr)
.ok()) {
std::string string_value;
if (!UnescapeStringLiteral(value_node->image(), &string_value, &error)) {
errors->push_back(LogicalError(
value_node,
absl::StrCat("Cannot parse string literal: ", value_node->image())));
return;
}
if (string_value.empty()) {
errors->push_back(
"Empty string is an invalid value for instance_partition. If you'd "
"like to clear a previously set value, use NULL.");
return;
}
option->set_string_value(string_value);
} else if (value_node->getId() == JJTNULLL) {
errors->push_back("Placements must have a non-NULL instance_partition.");
return;
} else {
errors->push_back(absl::StrCat(
"Unexpected value for option: ", kPlacementInstancePartitionOptionName,
". Supported option values are strings and NULL."));
return;
}
}
// Handle default_leader for PLACEMENT statement options
void VisitDefaultLeaderPlacementOptionValNode(
const SimpleNode* value_node, SetOption* option,
std::vector<std::string>* errors) {
VisitDefaultLeaderDatabaseOptionValNode(value_node, option, errors);
}
void VisitPlacementOptionKeyValNode(
const SimpleNode* node, OptionList* options,
std::vector<std::string>* errors,
bool* instance_partition_visited = nullptr) {
std::string name = CheckOptionKeyValNodeAndGetName(node);
if (absl::c_find_if(*options, [&name](const SetOption& option) {
return option.option_name() == name;
}) != options->end()) {
errors->push_back(absl::StrCat("Duplicate option: ", name));
return;
}
const SimpleNode* value_node = GetChildNode(node, 1);
if (name == kPlacementInstancePartitionOptionName) {
SetOption* option = options->Add();
option->set_option_name(name);
VisitInstancePartitionPlacementOptionValNode(value_node, option, errors);
if (instance_partition_visited != nullptr) {
*instance_partition_visited = true;
}
} else if (name == kPlacementDefaultLeaderOptionName) {
SetOption* option = options->Add();
option->set_option_name(name);
VisitDefaultLeaderPlacementOptionValNode(value_node, option, errors);
} else {
errors->push_back(absl::StrCat("Option: ", name, " is unknown."));
}
}
void VisitCreatePlacementNode(const SimpleNode* node,
CreatePlacement* placement,
std::vector<std::string>* errors) {
CheckNode(node, JJTCREATE_PLACEMENT_STATEMENT);
int offset = 0;
// We may have an optional IF NOT EXISTS node before the name.
if (GetChildNode(node, offset)->getId() == JJTIF_NOT_EXISTS) {
placement->set_existence_modifier(IF_NOT_EXISTS);
offset++;
}
placement->set_placement_name(
GetQualifiedIdentifier(GetChildNode(node, offset, JJTNAME)));
offset++;
bool has_instance_partition = false;
if (node->jjtGetNumChildren() == offset + 1) {
const SimpleNode* options_clause =
GetChildNode(node, offset, JJTOPTIONS_CLAUSE);
OptionList* options = placement->mutable_set_options();
bool instance_partition_visited = false;
for (int i = 0; i < options_clause->jjtGetNumChildren(); ++i) {
VisitPlacementOptionKeyValNode(
GetChildNode(options_clause, i, JJTOPTION_KEY_VAL), options, errors,
&instance_partition_visited);
if (instance_partition_visited) {
has_instance_partition = true;
}
}
}
if (!has_instance_partition) {
errors->push_back(
"CREATE PLACEMENT statements require option `instance_partition` to be "
"set");
}
}
void VisitAlterPlacementNode(const SimpleNode* node, AlterPlacement* placement,
std::vector<std::string>* errors) {
CheckNode(node, JJTALTER_PLACEMENT_STATEMENT);
int offset = 0;
// We may have an optional IF EXISTS node before the name.
if (GetChildNode(node, offset)->getId() == JJTIF_EXISTS) {
placement->set_existence_modifier(IF_EXISTS);
offset++;
}
placement->set_placement_name(
GetQualifiedIdentifier(GetChildNode(node, offset, JJTNAME)));
offset++;
if (node->jjtGetNumChildren() == offset + 1) {
const SimpleNode* options_clause =
GetChildNode(node, offset, JJTOPTIONS_CLAUSE);
OptionList* options = placement->mutable_set_options();
for (int i = 0; i < options_clause->jjtGetNumChildren(); ++i) {
VisitPlacementOptionKeyValNode(
GetChildNode(options_clause, i, JJTOPTION_KEY_VAL), options, errors);
}
}
}
// End Visit functions
//////////////////////////////////////////////////////////////////////////
// Walk over AST to build up an un-validated DDLStatement.
void BuildCloudDDLStatement(const SimpleNode* root, absl::string_view ddl_text,
DDLStatement* statement,
std::vector<std::string>* errors) {
CheckNode(root, JJTDDL_STATEMENT);
const SimpleNode* stmt = GetChildNode(root, 0);
switch (stmt->getId()) {
case JJTCREATE_DATABASE_STATEMENT:
VisitCreateDatabaseNode(stmt, statement->mutable_create_database(),
errors);
break;
case JJTCREATE_PROTO_BUNDLE_STATEMENT: {
CreateProtoBundle* create_proto_bundle =
statement->mutable_create_proto_bundle();
VisitCreateProtoBundleNode(stmt, create_proto_bundle, errors);
break;
}
case JJTCREATE_TABLE_STATEMENT:
VisitCreateTableNode(stmt, statement->mutable_create_table(), ddl_text,
errors);
break;
case JJTCREATE_INDEX_STATEMENT:
VisitCreateIndexNode(stmt, statement->mutable_create_index(), errors);
break;
case JJTCREATE_PLACEMENT_STATEMENT:
VisitCreatePlacementNode(stmt, statement->mutable_create_placement(),
errors);
break;
case JJTALTER_PLACEMENT_STATEMENT:
VisitAlterPlacementNode(stmt, statement->mutable_alter_placement(),
errors);
break;
case JJTALTER_INDEX_STATEMENT: {
VisitAlterIndexNode(stmt, statement->mutable_alter_index(), errors);
break;
}
case JJTALTER_VECTOR_INDEX_STATEMENT: {
VisitAlterVectorIndexNode(stmt, statement->mutable_alter_vector_index(),
errors);
break;
}
case JJTGRANT_STATEMENT: {
// For grant privilege statement, first child node represents privileges.
if (GetChildNode(stmt, 0)->getId() == JJTPRIVILEGES) {
VisitGrantPrivilegeNode(stmt, statement->mutable_grant_privilege(),
errors);
} else if (GetChildNode(stmt, 0)->getId() == JJTGRANTEES) {
// For grant membership statement, first child node represents grantees.
VisitGrantMembershipNode(stmt, statement->mutable_grant_membership(),
errors);
} else {
ABSL_LOG(FATAL) << "Unexpected statement: " << stmt->toString();
}
break;
}
case JJTREVOKE_STATEMENT: {
// For revoke privilege statement, first child node represents privileges.
if (GetChildNode(stmt, 0)->getId() == JJTPRIVILEGES) {
VisitRevokePrivilegeNode(stmt, statement->mutable_revoke_privilege(),
errors);
} else if (GetChildNode(stmt, 0)->getId() == JJTGRANTEES) {
// For revoke membership statement, first child node represents
// grantees.
VisitRevokeMembershipNode(stmt, statement->mutable_revoke_membership(),
errors);
} else {
ABSL_LOG(FATAL) << "Unexpected statement: " << stmt->toString();
}
break;
}
case JJTRENAME_STATEMENT: {
VisitRenameTableNode(stmt, statement->mutable_rename_table(), errors);
break;
}
case JJTCREATE_SEARCH_INDEX_STATEMENT:
VisitCreateSearchIndexNode(stmt, statement->mutable_create_search_index(),
errors);
break;
case JJTCREATE_VECTOR_INDEX_STATEMENT:
VisitCreateVectorIndexNode(stmt, statement->mutable_create_vector_index(),
errors);
break;
case JJTCREATE_CHANGE_STREAM_STATEMENT:
VisitCreateChangeStreamNode(
stmt, statement->mutable_create_change_stream(), errors);
break;
case JJTCREATE_SEQUENCE_STATEMENT:
VisitCreateSequenceNode(stmt, statement->mutable_create_sequence(),
errors);
break;
case JJTCREATE_LOCALITY_GROUP_STATEMENT:
VisitCreateLocalityGroupNode(
stmt, statement->mutable_create_locality_group(), errors);
break;
case JJTDROP_STATEMENT: {
const SimpleNode* drop_stmt = GetChildNode(stmt, 0);
const SimpleNode* name_node = GetFirstChildNode(stmt, JJTNAME);
std::string name;
if (drop_stmt->getId() == JJTLOCALITY_GROUP &&
GetFirstChildNode(stmt, JJTDEFAULTT) != nullptr) {
name = kDefaultLocalityGroupName;
} else {
name = GetQualifiedIdentifier(name_node);
}
switch (drop_stmt->getId()) {
case JJTTABLE:
if (GetFirstChildNode(stmt, JJTIF_EXISTS) != nullptr) {
statement->mutable_drop_table()->set_existence_modifier(IF_EXISTS);
}
statement->mutable_drop_table()->set_table_name(name);
break;
case JJTINDEX:
if (GetFirstChildNode(stmt, JJTIF_EXISTS) != nullptr) {
statement->mutable_drop_index()->set_existence_modifier(IF_EXISTS);
}
statement->mutable_drop_index()->set_index_name(name);
break;
case JJTVECTOR_INDEX:
if (GetFirstChildNode(stmt, JJTIF_EXISTS) != nullptr) {
statement->mutable_drop_vector_index()->set_existence_modifier(
IF_EXISTS);
}
statement->mutable_drop_vector_index()->set_index_name(name);
break;
case JJTSEARCH_INDEX:
if (GetFirstChildNode(stmt, JJTIF_EXISTS) != nullptr) {
statement->mutable_drop_search_index()->set_existence_modifier(
IF_EXISTS);
}
statement->mutable_drop_search_index()->set_index_name(name);
break;
case JJTCHANGE_STREAM:
statement->mutable_drop_change_stream()->set_change_stream_name(name);
break;
case JJTROLE:
statement->mutable_drop_role()->set_role_name(name);
break;
case JJTVIEW:
if (GetFirstChildNode(stmt, JJTIF_EXISTS) != nullptr) {
statement->mutable_drop_function()->set_existence_modifier(
IF_EXISTS);
}
statement->mutable_drop_function()->set_function_kind(Function::VIEW);
statement->mutable_drop_function()->set_function_name(name);
break;
case JJTFUNCTION:
if (!EmulatorFeatureFlags::instance()
.flags()
.enable_user_defined_functions) {
errors->push_back("User defined functions are not supported.");
return;
}
if (GetFirstChildNode(stmt, JJTIF_EXISTS) != nullptr) {
statement->mutable_drop_function()->set_existence_modifier(
IF_EXISTS);
}
statement->mutable_drop_function()->set_function_kind(
Function::FUNCTION);
statement->mutable_drop_function()->set_function_name(name);
break;
case JJTMODEL: {
if (GetFirstChildNode(stmt, JJTIF_EXISTS) != nullptr) {
statement->mutable_drop_model()->set_if_exists(true);
}
statement->mutable_drop_model()->set_model_name(name);
break;
}
case JJTPROTO_BUNDLE:
statement->mutable_drop_proto_bundle(); // No fields.
break;
case JJTSEQUENCE:
if (GetFirstChildNode(stmt, JJTIF_EXISTS) != nullptr) {
statement->mutable_drop_sequence()->set_existence_modifier(
IF_EXISTS);
}
statement->mutable_drop_sequence()->set_sequence_name(name);
break;
case JJTSCHEMA:
if (GetFirstChildNode(stmt, JJTIF_EXISTS) != nullptr) {
statement->mutable_drop_schema()->set_if_exists(true);
}
statement->mutable_drop_schema()->set_schema_name(name);
break;
case JJTGRAPH:
if (GetFirstChildNode(stmt, JJTIF_EXISTS) != nullptr) {
statement->mutable_drop_property_graph()->set_existence_modifier(
IF_EXISTS);
}
statement->mutable_drop_property_graph()->set_name(name);
break;
case JJTLOCALITY_GROUP:
if (GetFirstChildNode(stmt, JJTIF_EXISTS) != nullptr) {
statement->mutable_drop_locality_group()->set_existence_modifier(
IF_EXISTS);
}
statement->mutable_drop_locality_group()->set_locality_group_name(
name);
break;
case JJTPLACEMENT:
statement->mutable_drop_placement()->set_placement_name(name);
break;
default:
ABSL_LOG(FATAL) << "Unexpected object type: "
<< GetChildNode(stmt, 0)->toString();
}
} break;
case JJTALTER_PROTO_BUNDLE_STATEMENT: {
AlterProtoBundle* alter_proto_bundle =
statement->mutable_alter_proto_bundle();
VisitAlterProtoBundleNode(stmt, alter_proto_bundle, errors);
break;
}
case JJTALTER_DATABASE_STATEMENT:
VisitAlterDatabaseNode(stmt, statement->mutable_alter_database(), errors);
break;
case JJTALTER_TABLE_STATEMENT:
VisitAlterTableNode(stmt, ddl_text, statement, errors);
break;
case JJTALTER_MODEL_STATEMENT:
VisitAlterModelNode(stmt, statement->mutable_alter_model(), errors);
break;
case JJTALTER_CHANGE_STREAM_STATEMENT:
VisitAlterChangeStreamNode(stmt, statement->mutable_alter_change_stream(),
errors);
break;
case JJTALTER_SEQUENCE_STATEMENT:
VisitAlterSequenceNode(stmt, statement->mutable_alter_sequence(), errors);
break;
case JJTALTER_SCHEMA_STATEMENT:
VisitAlterSchemaNode(stmt, statement->mutable_alter_schema(), errors);
break;
case JJTALTER_LOCALITY_GROUP_STATEMENT:
VisitAlterLocalityGroupNode(
stmt, statement->mutable_alter_locality_group(), errors);
break;
case JJTANALYZE_STATEMENT:
CheckNode(stmt, JJTANALYZE_STATEMENT);
statement->mutable_analyze();
break;
case JJTCREATE_OR_REPLACE_STATEMENT: {
bool has_or_replace = (GetFirstChildNode(stmt, JJTOR_REPLACE) != nullptr);
const SimpleNode* actual_stmt =
GetChildNode(stmt, has_or_replace ? 1 : 0);
switch (actual_stmt->getId()) {
case JJTCREATE_VIEW_STATEMENT:
VisitCreateViewNode(actual_stmt, statement->mutable_create_function(),
has_or_replace, ddl_text, errors);
break;
case JJTCREATE_MODEL_STATEMENT:
VisitCreateModelNode(actual_stmt, statement->mutable_create_model(),
has_or_replace, ddl_text, errors);
break;
case JJTCREATE_SCHEMA_STATEMENT:
VisitCreateSchemaNode(actual_stmt, statement->mutable_create_schema(),
errors);
break;
case JJTCREATE_FUNCTION_STATEMENT:
VisitCreateFunctionNode(actual_stmt,
statement->mutable_create_function(),
has_or_replace, ddl_text, errors);
break;
case JJTCREATE_PROPERTY_GRAPH_STATEMENT:
VisitCreatePropertyGraphNode(
actual_stmt, statement->mutable_create_property_graph(),
has_or_replace, ddl_text, errors);
break;
default:
ABSL_LOG(FATAL) << "Unexpected statement: " << stmt->toString();
}
break;
}
case JJTCREATE_ROLE_STATEMENT: {
ABSL_CHECK_EQ(stmt->jjtGetNumChildren(), 1);
auto* create_role = statement->mutable_create_role();
SimpleNode* name = GetChildNode(stmt, 0);
ABSL_CHECK_EQ(name->getId(), JJTNAME);
create_role->set_role_name(name->image());
break;
}
default:
ABSL_LOG(FATAL) << "Unexpected statement: " << stmt->toString();
}
}
absl::Status UnvalidatedParseCloudDDLStatement(absl::string_view ddl,
DDLStatement* statement) {
// Special case: JavaCC doesn't like parsing a completely empty string. Return
// an error immediately instead.
if (ddl.empty()) {
return error::EmptyDDLStatement();
}
// Create the JavaCC generated parser.
DDLCharStream char_stream(ddl);
DDLParserTokenManager token_manager(&char_stream);
DDLParser parser(&token_manager);
std::vector<std::string> errors;
// The parser owns the error handler and deletes it.
parser.setErrorHandler(new CloudDDLErrorHandler(&errors));
SimpleNode* node = parser.ParseDDL();
if (node == nullptr) {
// NULL means error from JavaCC. "errors" contains parse issues.
if (errors.empty()) errors.push_back("Unknown error.");
return error::DDLStatementWithErrors(ddl, errors);
}
statement->Clear();
BuildCloudDDLStatement(node, ddl, statement, &errors);
return error::DDLStatementWithErrors(ddl, errors);
}
} // namespace
absl::Status ParseDDLStatement(absl::string_view ddl, DDLStatement* statement) {
return UnvalidatedParseCloudDDLStatement(ddl, statement);
}
} // namespace ddl
} // namespace backend
} // namespace emulator
} // namespace spanner
} // namespace google