backend/schema/printer/print_ddl.cc (759 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/printer/print_ddl.h"
#include <algorithm>
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include "zetasql/public/function_signature.h"
#include "zetasql/public/options.pb.h"
#include "zetasql/public/strings.h"
#include "zetasql/public/types/struct_type.h"
#include "zetasql/public/types/type.h"
#include "absl/container/flat_hash_set.h"
#include "absl/log/check.h"
#include "absl/log/log.h"
#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_join.h"
#include "absl/strings/string_view.h"
#include "absl/strings/strip.h"
#include "absl/strings/substitute.h"
#include "absl/types/span.h"
#include "backend/common/utils.h"
#include "backend/schema/catalog/change_stream.h"
#include "backend/schema/catalog/check_constraint.h"
#include "backend/schema/catalog/column.h"
#include "backend/schema/catalog/database_options.h"
#include "backend/schema/catalog/foreign_key.h"
#include "backend/schema/catalog/locality_group.h"
#include "backend/schema/catalog/model.h"
#include "backend/schema/catalog/named_schema.h"
#include "backend/schema/catalog/placement.h"
#include "backend/schema/catalog/property_graph.h"
#include "backend/schema/catalog/proto_bundle.h"
#include "backend/schema/catalog/schema.h"
#include "backend/schema/catalog/sequence.h"
#include "backend/schema/catalog/udf.h"
#include "backend/schema/catalog/view.h"
#include "backend/schema/ddl/operations.pb.h"
#include "backend/schema/graph/schema_node.h"
#include "backend/schema/parser/ddl_parser.h"
#include "backend/schema/parser/ddl_reserved_words.h"
#include "third_party/spanner_pg/ddl/spangres_direct_schema_printer_impl.h"
#include "third_party/spanner_pg/ddl/spangres_schema_printer.h"
#include "google/protobuf/repeated_ptr_field.h"
#include "zetasql/base/status_macros.h"
namespace google {
namespace spanner {
namespace emulator {
namespace backend {
using ::postgres_translator::spangres::SpangresSchemaPrinter;
namespace {
std::string PrintQualifiedName(const std::string& name) {
return absl::Substitute("`$0`", name);
}
std::string PrintName(const std::string& name) {
return ddl::IsReservedWord(name) ? PrintQualifiedName(name) : name;
}
std::string PrintColumnNameList(absl::Span<const Column* const> columns) {
return absl::StrJoin(columns, ", ",
[](std::string* out, const Column* column) {
absl::StrAppend(out, PrintName(column->Name()));
});
}
// Converts a column type and it's length (if applicable) to the corresponding
// DDL type string.
std::string TypeToString(const zetasql::Type* type,
std::optional<int64_t> max_length) {
std::string type_name = type->TypeName(zetasql::PRODUCT_EXTERNAL,
/*use_external_float32=*/true);
if (type->IsString() || type->IsBytes()) {
absl::StrAppend(
&type_name, "(",
(max_length.has_value() ? absl::StrCat(max_length.value()) : "MAX"),
")");
}
return type_name;
}
} // namespace
std::string OnDeleteActionToString(Table::OnDeleteAction action) {
switch (action) {
case Table::OnDeleteAction::kNoAction:
return "NO ACTION";
case Table::OnDeleteAction::kCascade:
return "CASCADE";
}
}
std::string RowDeletionPolicyToString(const ddl::RowDeletionPolicy& policy) {
std::string str;
absl::StrAppend(&str, "OLDER_THAN(");
absl::StrAppend(&str, policy.column_name());
absl::StrAppend(&str, ", ");
absl::StrAppend(&str, "INTERVAL ");
absl::StrAppend(&str, policy.older_than().count());
absl::StrAppend(&str, " DAY)");
return str;
}
std::string ColumnTypeToString(const zetasql::Type* type,
std::optional<int64_t> max_length) {
if (type->IsStruct()) {
std::vector<std::string> fields;
for (const zetasql::StructField& field : type->AsStruct()->fields()) {
fields.push_back(absl::StrCat(
field.name, " ", ColumnTypeToString(field.type, std::nullopt)));
}
return absl::StrCat("STRUCT<", absl::StrJoin(fields, ", "), ">");
} else if (type->IsArray()) {
return "ARRAY<" +
ColumnTypeToString(type->AsArray()->element_type(), max_length) +
">";
} else {
return TypeToString(type, max_length);
}
}
std::string PrintColumn(const Column* column) {
std::string ddl_string = PrintName(column->Name());
absl::StrAppend(
&ddl_string, " ",
ColumnTypeToString(column->GetType(), column->declared_max_length()));
if (!column->is_nullable()) {
absl::StrAppend(&ddl_string, " NOT NULL");
}
if (column->is_placement_key()) {
absl::StrAppend(&ddl_string, " PLACEMENT KEY");
}
if (column->is_generated()) {
absl::StrAppend(&ddl_string,
absl::Substitute(" AS $0", column->expression().value()));
if (column->is_stored()) {
absl::StrAppend(&ddl_string, " STORED");
}
} else if (column->is_identity_column()) {
// This needs to check before the default value.
absl::StrAppend(&ddl_string, " GENERATED BY DEFAULT AS IDENTITY");
if (!column->sequences_used().empty()) {
const Sequence* seq =
static_cast<const Sequence*>(column->sequences_used().at(0));
std::vector<std::string> options;
if (!seq->use_default_sequence_kind_option()) {
options.push_back(seq->sequence_kind_name());
}
if (seq->skip_range_min().has_value()) {
options.push_back(absl::StrCat("SKIP RANGE ", *seq->skip_range_min(),
", ", *seq->skip_range_max()));
}
if (seq->start_with_counter().has_value()) {
options.push_back(
absl::StrCat("START COUNTER WITH ", *seq->start_with_counter()));
}
if (!options.empty()) {
absl::StrAppend(&ddl_string, " (", absl::StrJoin(options, " "), ")");
}
}
} else if (column->has_default_value()) {
absl::StrAppend(
&ddl_string,
absl::Substitute(" DEFAULT ($0)", column->expression().value()));
}
if (column->GetType()->IsTimestamp() &&
column->has_allows_commit_timestamp()) {
absl::StrAppend(&ddl_string, " OPTIONS (\n allow_commit_timestamp = ",
column->allows_commit_timestamp() ? "true" : "false",
"\n )");
}
if (column->hidden()) {
absl::StrAppend(&ddl_string, " HIDDEN");
}
return ddl_string;
}
std::string PrintKeyColumn(const KeyColumn* column) {
return absl::Substitute("$0$1", PrintName(column->column()->Name()),
(column->is_descending() ? " DESC" : ""));
}
std::string PrintIndex(const Index* index) {
std::string ddl_string;
absl::StrAppend(&ddl_string, "CREATE", (index->is_unique() ? " UNIQUE" : ""),
(index->is_search_index() ? " SEARCH" : ""),
(index->is_null_filtered() ? " NULL_FILTERED" : ""),
" INDEX ", PrintName(index->Name()), " ON ",
PrintName(index->indexed_table()->Name()), "(");
std::vector<std::string> pk_clause;
pk_clause.reserve(index->key_columns().size());
for (int i = 0; i < index->key_columns().size(); ++i) {
pk_clause.push_back(PrintKeyColumn(index->key_columns()[i]));
}
absl::StrAppend(&ddl_string, absl::StrJoin(pk_clause, ", "), ")");
if (!index->stored_columns().empty()) {
absl::StrAppend(&ddl_string, " STORING (");
std::vector<std::string> storing_clause;
storing_clause.reserve(index->stored_columns().size());
for (int i = 0; i < index->stored_columns().size(); ++i) {
storing_clause.push_back(PrintName(index->stored_columns()[i]->Name()));
}
absl::StrAppend(&ddl_string, absl::StrJoin(storing_clause, ", "), ")");
}
if (index->is_search_index()) {
if (!index->partition_by().empty()) {
absl::StrAppend(&ddl_string, " PARTITION BY ");
std::vector<std::string> partition_by_clause;
partition_by_clause.reserve(index->partition_by().size());
for (int i = 0; i < index->partition_by().size(); ++i) {
partition_by_clause.push_back(
PrintName(index->partition_by()[i]->Name()));
}
absl::StrAppend(&ddl_string, absl::StrJoin(partition_by_clause, ", "));
}
if (!index->order_by().empty()) {
absl::StrAppend(&ddl_string, " ORDER BY ");
std::vector<std::string> order_by_clause;
order_by_clause.reserve(index->order_by().size());
for (int i = 0; i < index->order_by().size(); ++i) {
order_by_clause.push_back(PrintName(index->order_by()[i]->Name()));
}
absl::StrAppend(&ddl_string, absl::StrJoin(order_by_clause, ", "));
}
}
if (index->parent()) {
absl::StrAppend(&ddl_string, ", INTERLEAVE IN ", index->parent()->Name());
}
return ddl_string;
}
std::string PrintView(const View* view) {
std::string view_string =
absl::Substitute("CREATE VIEW $0", PrintName(view->Name()));
if (view->security() == View::SqlSecurity::INVOKER) {
absl::StrAppend(&view_string, " SQL SECURITY INVOKER");
}
absl::StrAppend(&view_string, " AS ", view->body());
return view_string;
}
std::string PrintOptions(::google::protobuf::RepeatedPtrField<ddl::SetOption> options) {
return absl::StrJoin(
options, ", ", [](std::string* out, const ddl::SetOption& option) {
absl::StrAppend(out, option.option_name(), " = ");
if (option.has_null_value()) {
absl::StrAppend(out, "NULL");
} else if (option.has_bool_value()) {
absl::StrAppend(out, option.bool_value() ? "true" : "false");
} else if (option.has_int64_value()) {
absl::StrAppend(out, option.int64_value());
} else if (option.has_double_value()) {
absl::StrAppend(out, option.double_value());
} else if (option.has_string_value()) {
absl::StrAppend(out, zetasql::ToSingleQuotedStringLiteral(
option.string_value()));
} else if (!option.string_list_value().empty()) {
absl::StrAppend(
out, "[",
absl::StrJoin(
option.string_list_value(), ", ",
[](std::string* out, const std::string& option) {
absl::StrAppend(
out, zetasql::ToSingleQuotedStringLiteral(option));
}),
"]");
} else {
}
});
}
std::string PrintChangeStream(const ChangeStream* change_stream) {
std::string change_stream_string = absl::Substitute(
"CREATE CHANGE STREAM $0", PrintName(change_stream->Name()));
if (change_stream->for_clause() != nullptr) {
absl::StrAppend(&change_stream_string, " ", "FOR ");
const ddl::ChangeStreamForClause* for_clause = change_stream->for_clause();
if (for_clause->has_all()) {
absl::StrAppend(&change_stream_string, "ALL");
} else if (for_clause->has_tracked_tables()) {
absl::StrAppend(
&change_stream_string,
absl::StrJoin(
for_clause->tracked_tables().table_entry(), ", ",
[](std::string* out,
ddl::ChangeStreamForClause_TrackedTables_Entry entry) {
absl::StrAppend(out, PrintName(entry.table_name()));
if (entry.has_tracked_columns()) {
absl::StrAppend(
out, "(",
absl::StrJoin(
entry.tracked_columns().column_name(), ", ",
[](std::string* out, std::string column_name) {
absl::StrAppend(out, PrintName(column_name));
}),
")");
}
}));
}
}
// Options with null values shouldn't be printed out.
if (change_stream->HasExplicitValidOptions()) {
absl::StrAppend(&change_stream_string, " ", "OPTIONS ( ",
PrintOptions(change_stream->options()), " )");
}
return change_stream_string;
}
std::string PrintPlacement(const Placement* placement) {
std::string placement_string = absl::Substitute(
"CREATE PLACEMENT $0", PrintName(placement->PlacementName()));
// Options with null values shouldn't be printed out.
if (placement->HasExplicitValidOptions()) {
absl::StrAppend(&placement_string, " ", "OPTIONS ( ",
PrintOptions(placement->options()), " )");
}
return placement_string;
}
std::string PrintModelColumnOptions(const Model::ModelColumn& model_column) {
std::vector<std::string> options;
if (model_column.is_required.has_value()) {
options.push_back(absl::StrCat(
"required = ", *model_column.is_required ? "true" : "false"));
}
std::string options_string;
if (!options.empty()) {
options_string =
absl::StrCat(" OPTIONS ( ", absl::StrJoin(options, ", "), " )");
}
return options_string;
}
std::string PrintModelColumn(const Model::ModelColumn& model_column) {
return absl::StrCat(model_column.name, " ",
ColumnTypeToString(model_column.type, std::nullopt),
PrintModelColumnOptions(model_column));
}
std::string PrintModelOptions(const Model* model) {
std::vector<std::string> options;
if (model->endpoint().has_value()) {
options.push_back(absl::StrCat(
"endpoint = ",
zetasql::ToSingleQuotedStringLiteral(*model->endpoint())));
}
if (!model->endpoints().empty()) {
options.push_back(absl::StrCat(
"endpoints = [ ",
absl::StrJoin(model->endpoints(), ", ",
[](std::string* out, const std::string& endpoint) {
absl::StrAppend(
out,
zetasql::ToSingleQuotedStringLiteral(endpoint));
}),
" ]"));
}
if (model->default_batch_size().has_value()) {
options.push_back(
absl::StrCat("default_batch_size = ", *model->default_batch_size()));
}
std::string options_string;
if (!options.empty()) {
options_string =
absl::StrCat(" OPTIONS ( ", absl::StrJoin(options, ", "), " )");
}
return options_string;
}
std::string PrintModel(const Model* model) {
std::string statement = absl::Substitute("CREATE MODEL $0\n", model->Name());
absl::StrAppend(&statement, "INPUT(\n");
for (const Model::ModelColumn& model_column : model->input()) {
absl::StrAppend(&statement, " ", PrintModelColumn(model_column), ",\n");
}
absl::StrAppend(&statement, ")\n");
absl::StrAppend(&statement, "OUTPUT(\n");
for (const Model::ModelColumn& model_column : model->output()) {
absl::StrAppend(&statement, " ", PrintModelColumn(model_column), ",\n");
}
absl::StrAppend(&statement, ")\n");
if (model->is_remote()) {
absl::StrAppend(&statement, "REMOTE\n");
}
absl::StrAppend(&statement, PrintModelOptions(model));
return statement;
}
std::string PrintLabel(const PropertyGraph::Label& label) {
std::string result = label.name;
if (!label.property_names.empty()) {
absl::StrAppend(&result, " PROPERTIES (",
absl::StrJoin(label.property_names, ", "), ")");
}
return result;
}
std::string PrintGraphElementTable(
const PropertyGraph* property_graph,
const PropertyGraph::GraphElementTable& graph_element_table) {
std::string statement = absl::Substitute("$0", graph_element_table.name());
if (!graph_element_table.key_clause_columns().empty()) {
absl::StrAppend(
&statement, " KEY(",
absl::StrJoin(graph_element_table.key_clause_columns(), ", "), ")");
}
// Print labels and their properties
for (absl::string_view label_name : graph_element_table.label_names()) {
const PropertyGraph::Label* label;
absl::Status find_status =
property_graph->FindLabelByName(label_name, label);
ABSL_CHECK_OK(find_status);
absl::StrAppend(&statement, " LABEL ", PrintLabel(*label), "\n");
}
if (graph_element_table.element_kind() ==
PropertyGraph::GraphElementKind::EDGE) {
absl::StrAppend(
&statement, " SOURCE KEY(",
absl::StrJoin(
graph_element_table.source_node_reference().edge_table_column_names,
", "),
") REFERENCES ",
graph_element_table.source_node_reference().node_table_name, "(",
absl::StrJoin(
graph_element_table.source_node_reference().node_table_column_names,
", "),
")");
absl::StrAppend(
&statement, " DESTINATION KEY(",
absl::StrJoin(
graph_element_table.target_node_reference().edge_table_column_names,
", "),
") REFERENCES ",
graph_element_table.target_node_reference().node_table_name, "(",
absl::StrJoin(
graph_element_table.target_node_reference().node_table_column_names,
", "),
")");
}
return statement;
}
std::string PrintPropertyGraph(const PropertyGraph* property_graph) {
std::string statement =
absl::Substitute("CREATE PROPERTY GRAPH $0\n", property_graph->Name());
absl::StrAppend(&statement, "NODE TABLES(\n");
for (const PropertyGraph::GraphElementTable& node_table :
property_graph->NodeTables()) {
absl::StrAppend(&statement, " ",
PrintGraphElementTable(property_graph, node_table), ",\n");
}
absl::StrAppend(&statement, ")\n");
absl::StrAppend(&statement, "EDGE TABLES(\n");
for (const PropertyGraph::GraphElementTable& edge_table :
property_graph->EdgeTables()) {
absl::StrAppend(&statement, " ",
PrintGraphElementTable(property_graph, edge_table), ",\n");
}
absl::StrAppend(&statement, ")\n");
return statement;
}
std::string PrintTable(const Table* table) {
std::string table_string =
absl::Substitute("CREATE TABLE $0 (\n", PrintName(table->Name()));
for (const Column* column : table->columns()) {
absl::StrAppend(&table_string, " ", PrintColumn(column), ",\n");
}
for (const ForeignKey* foreign_key : table->foreign_keys()) {
absl::StrAppend(&table_string, " ", PrintForeignKey(foreign_key), ",\n");
}
for (const CheckConstraint* check_constraint : table->check_constraints()) {
absl::StrAppend(&table_string, " ", PrintCheckConstraint(check_constraint),
",\n");
}
if (!table->synonym().empty()) {
absl::StrAppend(&table_string, " SYNONYM(", PrintName(table->synonym()),
"),\n");
}
absl::StrAppend(&table_string, ") PRIMARY KEY(");
std::vector<std::string> pk_clause;
pk_clause.reserve(table->primary_key().size());
for (int i = 0; i < table->primary_key().size(); ++i) {
pk_clause.push_back(PrintKeyColumn(table->primary_key()[i]));
}
absl::StrAppend(&table_string, absl::StrJoin(pk_clause, ", "));
if (table->parent() != nullptr) {
absl::StrAppend(&table_string, "),\n");
absl::StrAppend(&table_string, " INTERLEAVE IN PARENT ",
PrintName(table->parent()->Name()), " ON DELETE ",
OnDeleteActionToString(table->on_delete_action()));
} else {
absl::StrAppend(&table_string, ")");
}
if (table->row_deletion_policy().has_value()) {
absl::StrAppend(&table_string, ", ROW DELETION POLICY (");
absl::StrAppend(&table_string, RowDeletionPolicyToString(
table->row_deletion_policy().value()));
absl::StrAppend(&table_string, ")");
}
return table_string;
}
// Moves the nodes with no dependencies to the front of the list.
void TopologicalOrderSchemaNodes(
const SchemaNode* node, absl::flat_hash_set<const SchemaNode*>* visited,
std::vector<std::string>* statements) {
if (visited->find(node) != visited->end()) {
return;
}
visited->insert(node);
if (const View* view = node->As<const View>(); view != nullptr) {
for (auto dependency : view->dependencies()) {
TopologicalOrderSchemaNodes(dependency, visited, statements);
}
statements->push_back(PrintView(view));
}
if (const Table* table = node->As<const Table>(); table != nullptr) {
for (const Column* column : table->columns()) {
for (const Column* column_dep : column->dependent_columns()) {
TopologicalOrderSchemaNodes(column_dep->table(), visited, statements);
}
for (const SchemaNode* sequence_dep : column->sequences_used()) {
TopologicalOrderSchemaNodes(sequence_dep, visited, statements);
}
for (const SchemaNode* udf_dep : column->udf_dependencies()) {
TopologicalOrderSchemaNodes(udf_dep, visited, statements);
}
// Omitting indexes since their dependencies to UDFs are through columns.
}
for (const CheckConstraint* check_constraint : table->check_constraints()) {
for (const SchemaNode* udf_dep : check_constraint->udf_dependencies()) {
TopologicalOrderSchemaNodes(udf_dep, visited, statements);
}
}
statements->push_back(PrintTable(table));
std::vector<const Index*> indexes{table->indexes().begin(),
table->indexes().end()};
std::sort(indexes.begin(), indexes.end(),
[](const Index* i1, const Index* i2) {
return i1->Name() < i2->Name();
});
for (const Index* index : indexes) {
if (!index->is_managed()) {
statements->push_back(PrintIndex(index));
}
}
}
if (const Udf* udf = node->As<const Udf>(); udf != nullptr) {
for (const SchemaNode* udf_dep : udf->dependencies()) {
TopologicalOrderSchemaNodes(udf_dep, visited, statements);
}
statements->push_back(PrintUdf(udf));
}
if (const Sequence* sequence = node->As<const Sequence>();
sequence != nullptr) {
// Not an internal sequence, e.g., used by an identity column.
if (!absl::StartsWith(sequence->Name(), "_")) {
statements->push_back(PrintSequence(sequence));
}
}
if (const ChangeStream* change_stream = node->As<ChangeStream>();
change_stream != nullptr) {
statements->push_back(PrintChangeStream(change_stream));
}
if (const Placement* placement = node->As<Placement>();
placement != nullptr) {
statements->push_back(PrintPlacement(placement));
}
if (const Model* model = node->As<const Model>(); model != nullptr) {
statements->push_back(PrintModel(model));
}
if (const PropertyGraph* property_graph = node->As<const PropertyGraph>();
property_graph != nullptr) {
statements->push_back(PrintPropertyGraph(property_graph));
}
}
std::string PrintCheckConstraint(const CheckConstraint* check_constraint) {
std::string out;
if (!check_constraint->has_generated_name()) {
absl::StrAppend(&out, "CONSTRAINT ", PrintName(check_constraint->Name()),
" ");
}
absl::StrAppend(&out, "CHECK(", check_constraint->expression(), ")");
return out;
}
std::string PrintForeignKey(const ForeignKey* foreign_key) {
std::string out;
if (!foreign_key->constraint_name().empty()) {
absl::StrAppend(&out, "CONSTRAINT ",
PrintName(foreign_key->constraint_name()), " ");
}
absl::StrAppend(&out, "FOREIGN KEY(",
PrintColumnNameList(foreign_key->referencing_columns()),
") REFERENCES ",
PrintName(foreign_key->referenced_table()->Name()), "(",
PrintColumnNameList(foreign_key->referenced_columns()), ")");
if (foreign_key->on_delete_action() !=
ForeignKey::Action::kActionUnspecified) {
absl::StrAppend(&out, " ", kDeleteAction, " ",
ForeignKey::ActionName(foreign_key->on_delete_action()));
}
if (!foreign_key->enforced()) {
absl::StrAppend(&out, " NOT ENFORCED");
}
return out;
}
std::string PrintSequence(const Sequence* sequence) {
std::string sequence_string =
absl::Substitute("CREATE SEQUENCE $0", PrintName(sequence->Name()));
if (sequence->created_from_options()) {
absl::StrAppend(&sequence_string, " OPTIONS (\n");
std::vector<std::string> options;
if (!sequence->use_default_sequence_kind_option()) {
options.push_back(" sequence_kind = 'bit_reversed_positive'");
}
if (sequence->skip_range_min().has_value() &&
sequence->skip_range_max().has_value()) {
options.push_back(absl::StrCat(" skip_range_min = ",
sequence->skip_range_min().value()));
options.push_back(absl::StrCat(" skip_range_max = ",
sequence->skip_range_max().value()));
}
if (sequence->start_with_counter().has_value()) {
options.push_back(absl::StrCat(" start_with_counter = ",
sequence->start_with_counter().value()));
}
absl::StrAppend(&sequence_string, absl::StrJoin(options, ",\n"), ")");
} else if (sequence->created_from_syntax()) {
if (!sequence->use_default_sequence_kind_option()) {
absl::StrAppend(&sequence_string, " BIT_REVERSED_POSITIVE");
}
if (sequence->skip_range_min().has_value() &&
sequence->skip_range_max().has_value()) {
absl::StrAppend(&sequence_string, " SKIP RANGE ",
sequence->skip_range_min().value(), ", ",
sequence->skip_range_max().value());
}
if (sequence->start_with_counter().has_value()) {
absl::StrAppend(&sequence_string, " START COUNTER WITH ",
sequence->start_with_counter().value());
}
}
return sequence_string;
}
std::string PrintUdf(const Udf* udf) {
if (udf->body_origin().has_value()) {
return udf->body_origin().value();
}
std::string udf_string =
absl::Substitute("CREATE FUNCTION $0", PrintName(udf->Name()));
const zetasql::FunctionSignature* signature = udf->signature();
if (signature) {
const std::vector<zetasql::FunctionArgumentType>& arguments =
signature->arguments();
std::vector<std::string> arg_strings;
for (int i = 0; i < arguments.size(); ++i) {
const zetasql::FunctionArgumentType& arg = arguments[i];
std::string arg_string =
absl::StrCat(arg.argument_name(), " ",
arg.type()->TypeName(zetasql::PRODUCT_EXTERNAL));
if (arg.HasDefault()) {
absl::StrAppend(&arg_string, " DEFAULT ",
arg.GetDefault().value().GetSQLLiteral(
zetasql::PRODUCT_EXTERNAL));
}
arg_strings.push_back(arg_string);
}
absl::StrAppend(&udf_string, "(", absl::StrJoin(arg_strings, ", "), ")");
absl::StrAppend(
&udf_string, " RETURNS ",
signature->result_type().type()->TypeName(zetasql::PRODUCT_EXTERNAL));
}
if (udf->security() == Udf::SqlSecurity::INVOKER) {
absl::StrAppend(&udf_string, " SQL SECURITY INVOKER");
}
absl::StrAppend(&udf_string, " AS (", udf->body(), ")");
return udf_string;
}
std::string PrintNamedSchema(const NamedSchema* named_schema) {
return absl::Substitute("CREATE SCHEMA $0", PrintName(named_schema->Name()));
}
std::string PrintProtoBundle(std::shared_ptr<const ProtoBundle> proto_bundle) {
std::string proto_bundle_statement = "CREATE PROTO BUNDLE (\n";
for (const auto& type : proto_bundle->types()) {
absl::StrAppend(&proto_bundle_statement, " ", type, ",\n");
}
absl::StrAppend(&proto_bundle_statement, ")");
return proto_bundle_statement;
}
std::string PrintLocalityGroup(const LocalityGroup* locality_group) {
std::string locality_group_statement = absl::Substitute(
"CREATE LOCALITY GROUP $0", PrintName(locality_group->Name()));
if (!locality_group->options().empty()) {
absl::StrAppend(&locality_group_statement, " ", "OPTIONS ( ",
PrintLocalityGroupOptions(locality_group->options()), " )");
}
return locality_group_statement;
}
std::string PrintLocalityGroupOptions(
::google::protobuf::RepeatedPtrField<ddl::SetOption> options) {
return absl::StrJoin(
options, ", ", [](std::string* out, const ddl::SetOption& option) {
if (option.option_name() ==
ddl::kInternalLocalityGroupStorageOptionName) {
absl::StrAppend(out, ddl::kLocalityGroupStorageOptionName, " = ");
absl::StrAppend(out,
zetasql::ToSingleQuotedStringLiteral(
option.bool_value()
? ddl::kLocalityGroupStorageOptionSSDVal
: ddl::kLocalityGroupStorageOptionHDDVal));
} else if (option.option_name() ==
ddl::kInternalLocalityGroupSpillTimeSpanOptionName) {
for (const auto& timeSpan : option.string_list_value()) {
absl::string_view raw_time_span = timeSpan;
if (absl::ConsumePrefix(&raw_time_span, "disk:")) {
absl::StrAppend(out, ddl::kLocalityGroupSpillTimeSpanOptionName,
" = ");
absl::StrAppend(
out, zetasql::ToSingleQuotedStringLiteral(raw_time_span));
}
}
}
});
}
absl::StatusOr<std::vector<std::string>> PrintDDLStatements(
const Schema* schema) {
std::vector<std::string> statements;
if (schema->dialect() == database_api::DatabaseDialect::POSTGRESQL) {
absl::StatusOr<std::unique_ptr<SpangresSchemaPrinter>> printer =
postgres_translator::spangres::CreateSpangresDirectSchemaPrinter();
ZETASQL_RETURN_IF_ERROR(printer.status());
ddl::DDLStatementList ddl_statements = schema->Dump();
for (const ddl::DDLStatement& statement : ddl_statements.statement()) {
absl::StatusOr<std::vector<std::string>> printed_statements =
(*printer)->PrintDDLStatementForEmulator(statement);
ZETASQL_RETURN_IF_ERROR(printed_statements.status());
statements.insert(statements.end(), (*printed_statements).begin(),
(*printed_statements).end());
}
return statements;
}
// Print database options.
const DatabaseOptions* options = schema->options();
if (options != nullptr) {
for (const auto& option : options->options()) {
statements.push_back(absl::Substitute(
"ALTER DATABASE $0 SET OPTIONS ($1 = '$2')", options->Name(),
option.option_name(), option.string_value()));
}
}
// Print proto bundle.
std::shared_ptr<const ProtoBundle> proto_bundle = schema->proto_bundle();
if (proto_bundle != nullptr && !proto_bundle->types().empty()) {
statements.push_back(PrintProtoBundle(proto_bundle));
}
// Print schema nodes while ensuring that dependencies are printed first.
absl::flat_hash_set<const SchemaNode*> visited;
for (const Placement* placement : schema->placements()) {
TopologicalOrderSchemaNodes(placement, &visited, &statements);
}
for (const Sequence* sequence : schema->user_visible_sequences()) {
TopologicalOrderSchemaNodes(sequence, &visited, &statements);
}
for (const Table* table : schema->tables()) {
TopologicalOrderSchemaNodes(table, &visited, &statements);
}
for (const ChangeStream* change_stream : schema->change_streams()) {
TopologicalOrderSchemaNodes(change_stream, &visited, &statements);
}
for (const Model* model : schema->models()) {
TopologicalOrderSchemaNodes(model, &visited, &statements);
}
for (const View* view : schema->views()) {
TopologicalOrderSchemaNodes(view, &visited, &statements);
}
for (const Udf* udf : schema->udfs()) {
TopologicalOrderSchemaNodes(udf, &visited, &statements);
}
for (const PropertyGraph* property_graph : schema->property_graphs()) {
TopologicalOrderSchemaNodes(property_graph, &visited, &statements);
}
for (auto locality_group : schema->locality_groups()) {
// The default locality group will be added to the schema
// only when it has been altered by the user.
if (!IsSystemLocalityGroup(locality_group->Name())) {
statements.push_back(PrintLocalityGroup(locality_group));
}
}
return statements;
}
} // namespace backend
} // namespace emulator
} // namespace spanner
} // namespace google