backend/query/function_catalog.cc (561 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/query/function_catalog.h"
#include <memory>
#include <optional>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
#include "zetasql/public/builtin_function.h"
#include "zetasql/public/builtin_function_options.h"
#include "zetasql/public/function.h"
#include "zetasql/public/function_signature.h"
#include "zetasql/public/table_valued_function.h"
#include "zetasql/public/type.h"
#include "zetasql/public/types/type_factory.h"
#include "zetasql/public/value.h"
#include "absl/container/flat_hash_map.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/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
#include "absl/strings/strip.h"
#include "absl/types/span.h"
#include "backend/query/analyzer_options.h"
#include "backend/query/ml/ml_predict_row_function.h"
#include "backend/query/ml/ml_predict_table_valued_function.h"
#include "backend/query/search/search_function_catalog.h"
#include "backend/schema/catalog/column.h"
#include "backend/schema/catalog/table.h"
#include "third_party/spanner_pg/datatypes/extended/pg_jsonb_type.h"
#include "backend/schema/catalog/schema.h"
#include "backend/schema/catalog/sequence.h"
#include "common/bit_reverse.h"
#include "common/constants.h"
#include "common/errors.h"
#include "common/feature_flags.h"
#include "third_party/spanner_pg/catalog/emulator_function_evaluators.h"
#include "third_party/spanner_pg/catalog/emulator_functions.h"
#include "third_party/spanner_pg/datatypes/extended/pg_numeric_type.h"
#include "third_party/spanner_pg/interface/datetime_evaluators.h"
#include "third_party/spanner_pg/interface/formatting_evaluators.h"
#include "third_party/spanner_pg/interface/pg_timezone.h"
#include "zetasql/base/ret_check.h"
namespace google {
namespace spanner {
namespace emulator {
namespace backend {
namespace {
using postgres_translator::GetSpannerPGFunctions;
using postgres_translator::GetSpannerPGTVFs;
using postgres_translator::SpannerPGFunctions;
using postgres_translator::SpannerPGTVFs;
using postgres_translator::function_evaluators::CleanupPostgresDateTimeCache;
using postgres_translator::function_evaluators::CleanupPostgresNumberCache;
const zetasql::Type* gsql_float = zetasql::types::FloatType();
const zetasql::Type* gsql_double = zetasql::types::DoubleType();
const zetasql::Type* gsql_int64 = zetasql::types::Int64Type();
const zetasql::Type* gsql_string = zetasql::types::StringType();
const zetasql::Type* gsql_timestamp = zetasql::types::TimestampType();
const zetasql::Type* gsql_interval = zetasql::types::IntervalType();
absl::StatusOr<zetasql::Value> EvalPendingCommitTimestamp(
absl::Span<const zetasql::Value> args) {
ZETASQL_RET_CHECK(args.empty());
// Timestamp returned by this function is ignored later by query engine and is
// replaced by kCommitTimestampIdentifier sentinel string as expected by cloud
// spanner. Note that this function cannot return a string sentinel here since
// googlesql evaluator expects a timestamp value for the corresponding column.
return zetasql::Value::Timestamp(zetasql::types::TimestampMinBaseTime());
}
std::unique_ptr<zetasql::Function> PendingCommitTimestampFunction(
const std::string& catalog_name) {
zetasql::FunctionOptions function_options;
function_options.set_evaluator(
zetasql::FunctionEvaluator(EvalPendingCommitTimestamp));
return std::make_unique<zetasql::Function>(
kPendingCommitTimestampFunctionName, catalog_name,
zetasql::Function::SCALAR,
std::vector<zetasql::FunctionSignature>{zetasql::FunctionSignature{
zetasql::types::TimestampType(), {}, nullptr}},
function_options);
}
absl::StatusOr<zetasql::Value> EvalBitReverse(
absl::Span<const zetasql::Value> args) {
if (!EmulatorFeatureFlags::instance()
.flags()
.enable_bit_reversed_positive_sequences) {
return error::UnsupportedFunction(kBitReverseFunctionName);
}
ZETASQL_RET_CHECK(args.size() == 2);
if (args[0].is_null() || args[1].is_null()) {
return zetasql::Value::NullInt64();
}
ZETASQL_RET_CHECK(args[0].type()->IsInt64() && args[1].type()->IsBool());
return zetasql::Value::Int64(
BitReverse(args[0].int64_value(), args[1].bool_value()));
}
std::unique_ptr<zetasql::Function> BitReverseFunction(
const std::string& catalog_name) {
zetasql::FunctionOptions function_options;
function_options.set_evaluator(zetasql::FunctionEvaluator(EvalBitReverse));
return std::make_unique<zetasql::Function>(
kBitReverseFunctionName, catalog_name, zetasql::Function::SCALAR,
std::vector<zetasql::FunctionSignature>{zetasql::FunctionSignature{
zetasql::types::Int64Type(),
{zetasql::types::Int64Type(), zetasql::types::BoolType()},
nullptr}},
function_options);
}
std::unique_ptr<zetasql::Function> MlPredictRowFunction(
const std::string& catalog_name) {
auto pg_jsonb = postgres_translator::spangres::datatypes::GetPgJsonbType();
auto gsql_string = zetasql::types::StringType();
zetasql::FunctionArgumentTypeOptions model_endpoint_opt;
model_endpoint_opt.set_argument_name(kMlPredictRowParamModelEndpoint,
zetasql::kPositionalOrNamed);
zetasql::FunctionArgumentTypeOptions arg_opt;
arg_opt.set_argument_name(kMlPredictRowParamArgs,
zetasql::kPositionalOrNamed);
return std::make_unique<zetasql::Function>(
kMlPredictRowFunctionName, catalog_name, zetasql::Function::SCALAR,
std::vector<zetasql::FunctionSignature>{
zetasql::FunctionSignature{
pg_jsonb,
{{gsql_string, model_endpoint_opt}, {pg_jsonb, arg_opt}},
nullptr},
zetasql::FunctionSignature{
pg_jsonb,
{{pg_jsonb, model_endpoint_opt}, {pg_jsonb, arg_opt}},
nullptr}},
zetasql::FunctionOptions().set_evaluator({EvalMlPredictRow}));
}
std::optional<std::tuple<std::string, std::string, std::string>>
ParseFullyQualifiedColumnPath(const std::string& qualified_column_path) {
std::vector<std::string> parts = absl::StrSplit(qualified_column_path, '.');
if (parts.size() == 2) {
return std::make_tuple(/*schema_name=*/"", /*table_name=*/parts[0],
/*column_name=*/parts[1]);
} else if (parts.size() == 3) {
return std::make_tuple(/*schema_name=*/parts[0], /*table_name=*/parts[1],
/*column_name=*/parts[2]);
}
return std::nullopt;
}
} // namespace
FunctionCatalog::FunctionCatalog(zetasql::TypeFactory* type_factory,
const std::string& catalog_name,
const backend::Schema* schema)
: catalog_name_(catalog_name), latest_schema_(schema) {
// Add the subset of ZetaSQL built-in functions supported by Cloud Spanner.
AddZetaSQLBuiltInFunctions(type_factory);
// Add Cloud Spanner specific functions.
AddSpannerFunctions();
// Add aliases for the functions.
AddFunctionAliases();
AddMlFunctions();
AddSpannerPGFunctions();
AddSearchFunctions(type_factory);
}
void FunctionCatalog::AddZetaSQLBuiltInFunctions(
zetasql::TypeFactory* type_factory) {
// Get all the ZetaSQL built-in functions.
absl::flat_hash_map<std::string, std::unique_ptr<zetasql::Function>>
function_map;
absl::flat_hash_map<std::string, const zetasql::Type*> type_map_unused;
absl::Status status = zetasql::GetBuiltinFunctionsAndTypes(
MakeGoogleSqlBuiltinFunctionOptions(), *type_factory, function_map,
type_map_unused);
// `status` can be an error when `BuiltinFunctionOptions` is misconfigured.
// The call above only supplies a `LangaugeOptions` and is low risk. If that
// configuration becomes more complex, then this `status` should probably be
// propagated out, which requires changing `FunctionCatalog` to use a factory
// function rather than a constructor that is doing work.
ABSL_DCHECK_OK(status);
// Move the data from the temporary function_map into functions_, keeping only
// the functions that are available in Cloud Spanner.
for (auto& [name, function] : function_map) {
functions_.emplace(name, std::move(function));
}
}
void FunctionCatalog::AddSpannerFunctions() {
// Add pending commit timestamp function to the list of known functions.
auto pending_commit_ts_func = PendingCommitTimestampFunction(catalog_name_);
functions_[pending_commit_ts_func->Name()] =
std::move(pending_commit_ts_func);
auto bit_reverse_func = BitReverseFunction(catalog_name_);
functions_[bit_reverse_func->Name()] = std::move(bit_reverse_func);
auto get_internal_sequence_state_func =
GetInternalSequenceStateFunction(catalog_name_);
functions_[get_internal_sequence_state_func->Name()] =
std::move(get_internal_sequence_state_func);
auto get_table_column_identity_state_func =
GetTableColumnIdentityStateFunction(catalog_name_);
functions_[get_table_column_identity_state_func->Name()] =
std::move(get_table_column_identity_state_func);
auto get_next_sequence_value_func =
GetNextSequenceValueFunction(catalog_name_);
functions_[get_next_sequence_value_func->Name()] =
std::move(get_next_sequence_value_func);
auto ml_predict_row_func = MlPredictRowFunction(catalog_name_);
functions_[ml_predict_row_func->Name()] = std::move(ml_predict_row_func);
}
void FunctionCatalog::AddMlFunctions() {
{
auto ml_predict =
std::make_unique<MlPredictTableValuedFunction>(/*safe=*/false);
table_valued_functions_.insert(
{ml_predict->FullName(), std::move(ml_predict)});
}
{
auto safe_ml_predict =
std::make_unique<MlPredictTableValuedFunction>(/*safe=*/true);
table_valued_functions_.insert(
{safe_ml_predict->FullName(), std::move(safe_ml_predict)});
}
}
void FunctionCatalog::AddSearchFunctions(zetasql::TypeFactory* type_factory) {
auto dialect = database_api::DatabaseDialect::GOOGLE_STANDARD_SQL;
if (latest_schema_ != nullptr) {
dialect = latest_schema_->dialect();
}
auto search_functions =
query::search::GetSearchFunctions(type_factory, catalog_name_, dialect);
for (auto& [name, function] : search_functions) {
functions_.emplace(name, std::move(function));
}
}
// Adds Spanner PG-specific functions to the list of known functions.
void FunctionCatalog::AddSpannerPGFunctions() {
SpannerPGFunctions spanner_pg_functions =
GetSpannerPGFunctions(catalog_name_);
for (auto& function : spanner_pg_functions) {
// Since some date/timestamp functions depend on the default time zone in
// the schema, we need to replace them with a new version which can access
// the latest schema.
auto replaced_func = GetReplacedPGFunction(function->Name());
if (replaced_func != nullptr) {
functions_[function->Name()] = std::move(replaced_func);
continue;
}
// If function exists, add extra signatures instead of overwriting.
// Needed for JSONB.
if (auto f = functions_.find(function->Name()); f != functions_.end()) {
for (auto& sig : function->signatures()) {
f->second->AddSignature(sig);
}
} else {
functions_[function->Name()] = std::move(function);
}
}
SpannerPGTVFs spanner_pg_tvfs = GetSpannerPGTVFs(catalog_name_);
for (auto& tvf : spanner_pg_tvfs) {
table_valued_functions_[tvf->FullName()] = std::move(tvf);
}
}
void FunctionCatalog::GetFunction(const std::string& name,
const zetasql::Function** output) const {
auto function_iter = functions_.find(name);
*output =
function_iter == functions_.end() ? nullptr : function_iter->second.get();
}
void FunctionCatalog::GetFunctions(
absl::flat_hash_set<const zetasql::Function*>* output) const {
for (const auto& [name, function] : functions_) {
output->insert(function.get());
}
}
void FunctionCatalog::GetTableValuedFunction(
const std::string& name,
const zetasql::TableValuedFunction** output) const {
auto i = table_valued_functions_.find(name);
*output = i == table_valued_functions_.end() ? nullptr : i->second.get();
}
void FunctionCatalog::AddFunctionAliases() {
std::vector<std::pair<std::string, std::unique_ptr<zetasql::Function>>>
aliases;
for (auto it = functions_.begin(); it != functions_.end(); ++it) {
const zetasql::Function* original_function = it->second.get();
if (!original_function->alias_name().empty()) {
zetasql::FunctionOptions function_options =
original_function->function_options();
std::string alias_name = function_options.alias_name;
function_options.set_alias_name("");
auto alias_function = std::make_unique<zetasql::Function>(
original_function->Name(), original_function->GetGroup(),
original_function->mode(), original_function->signatures(),
function_options);
aliases.emplace_back(
std::make_pair(alias_name, std::move(alias_function)));
}
}
for (auto& alias : aliases) {
functions_.insert(std::move(alias));
}
}
std::unique_ptr<zetasql::Function> FunctionCatalog::GetReplacedPGFunction(
const std::string& function_name) {
if (function_name == postgres_translator::kPGToCharFunctionName) {
return GetPGToCharFunction(catalog_name_);
} else if (function_name == postgres_translator::kPGExtractFunctionName) {
return GetPGExtractFunction(catalog_name_);
} else if (function_name ==
postgres_translator::kPGCastToTimestampFunctionName) {
return GetPGCastToTimestampFunction(catalog_name_);
} else if (function_name ==
postgres_translator::kPGCastToStringFunctionName) {
return GetPGCastToStringFunction(catalog_name_);
}
return nullptr;
}
std::unique_ptr<zetasql::Function> FunctionCatalog::GetPGToCharFunction(
const std::string& catalog_name) {
static const zetasql::Type* gsql_pg_numeric =
postgres_translator::spangres::datatypes::GetPgNumericType();
// Defines the function as a lambda, so it has access to the schema.
auto initialize_pg_timezone = [&]() {
std::string default_time_zone = latest_schema_ != nullptr
? latest_schema_->default_time_zone()
: kDefaultTimeZone;
absl::Status status = postgres_translator::interfaces::InitPGTimezone(
default_time_zone.c_str());
if (!status.ok()) {
ABSL_LOG(ERROR) << "Failed to initialize PG timezone for to_char function: "
<< status;
}
};
zetasql::FunctionOptions function_options;
function_options.set_evaluator(postgres_translator::PGFunctionEvaluator(
postgres_translator::EvalToChar, initialize_pg_timezone, [] {
CleanupPostgresNumberCache();
CleanupPostgresDateTimeCache();
}));
return std::make_unique<zetasql::Function>(
postgres_translator::kPGToCharFunctionName, catalog_name,
zetasql::Function::SCALAR,
std::vector<zetasql::FunctionSignature>{
zetasql::FunctionSignature{
gsql_string, {gsql_int64, gsql_string}, /*context_ptr=*/nullptr},
zetasql::FunctionSignature{gsql_string,
{gsql_timestamp, gsql_string},
/*context_ptr=*/nullptr},
zetasql::FunctionSignature{gsql_string,
{gsql_double, gsql_string},
/*context_ptr=*/nullptr},
zetasql::FunctionSignature{gsql_string,
{gsql_float, gsql_string},
/*context_ptr=*/nullptr},
zetasql::FunctionSignature{gsql_string,
{gsql_pg_numeric, gsql_string},
/*context_ptr=*/nullptr},
zetasql::FunctionSignature{gsql_string,
{gsql_interval, gsql_string},
/*context_ptr=*/nullptr},
},
function_options);
}
std::unique_ptr<zetasql::Function> FunctionCatalog::GetPGExtractFunction(
const std::string& catalog_name) {
static const zetasql::Type* gsql_pg_numeric =
postgres_translator::spangres::datatypes::GetPgNumericType();
// Defines the function as a lambda, so it has access to the schema.
auto initialize_pg_timezone = [&]() {
std::string default_time_zone = latest_schema_ != nullptr
? latest_schema_->default_time_zone()
: kDefaultTimeZone;
absl::Status status = postgres_translator::interfaces::InitPGTimezone(
default_time_zone.c_str());
if (!status.ok()) {
ABSL_LOG(ERROR) << "Failed to initialize PG timezone for to_char function: "
<< status;
}
};
zetasql::FunctionOptions function_options;
function_options.set_evaluator(postgres_translator::PGFunctionEvaluator(
postgres_translator::EvalExtract, initialize_pg_timezone,
CleanupPostgresDateTimeCache));
return std::make_unique<zetasql::Function>(
postgres_translator::kPGExtractFunctionName, catalog_name,
zetasql::Function::SCALAR,
std::vector<zetasql::FunctionSignature>{
zetasql::FunctionSignature{gsql_pg_numeric,
{zetasql::types::StringType(),
zetasql::types::TimestampType()},
nullptr},
zetasql::FunctionSignature{
gsql_pg_numeric,
{zetasql::types::StringType(), zetasql::types::DateType()},
nullptr}},
function_options);
}
std::unique_ptr<zetasql::Function>
FunctionCatalog::GetPGCastToTimestampFunction(const std::string& catalog_name) {
// Defines the function as a lambda, so it has access to the schema.
auto initialize_pg_timezone = [&]() {
std::string default_time_zone = latest_schema_ != nullptr
? latest_schema_->default_time_zone()
: kDefaultTimeZone;
absl::Status status = postgres_translator::interfaces::InitPGTimezone(
default_time_zone.c_str());
if (!status.ok()) {
ABSL_LOG(ERROR) << "Failed to initialize PG timezone for to_char function: "
<< status;
}
};
zetasql::FunctionOptions function_options;
function_options.set_evaluator(postgres_translator::PGFunctionEvaluator(
postgres_translator::EvalCastToTimestamp, initialize_pg_timezone,
CleanupPostgresDateTimeCache));
return std::make_unique<zetasql::Function>(
postgres_translator::kPGCastToTimestampFunctionName, catalog_name,
zetasql::Function::SCALAR,
std::vector<zetasql::FunctionSignature>{
zetasql::FunctionSignature{zetasql::types::TimestampType(),
{zetasql::types::StringType()},
nullptr}},
function_options);
}
std::unique_ptr<zetasql::Function> FunctionCatalog::GetPGCastToStringFunction(
const std::string& catalog_name) {
static const zetasql::Type* gsql_pg_numeric =
postgres_translator::spangres::datatypes::GetPgNumericType();
// Defines the function as a lambda, so it has access to the schema.
auto initialize_pg_timezone = [&]() {
std::string default_time_zone = latest_schema_ != nullptr
? latest_schema_->default_time_zone()
: kDefaultTimeZone;
absl::Status status = postgres_translator::interfaces::InitPGTimezone(
default_time_zone.c_str());
if (!status.ok()) {
ABSL_LOG(ERROR) << "Failed to initialize PG timezone for to_char function: "
<< status;
}
};
zetasql::FunctionOptions function_options;
function_options.set_evaluator(postgres_translator::PGFunctionEvaluator(
postgres_translator::EvalCastToString, initialize_pg_timezone));
return std::make_unique<zetasql::Function>(
postgres_translator::kPGCastToStringFunctionName, catalog_name,
zetasql::Function::SCALAR,
std::vector<zetasql::FunctionSignature>{
zetasql::FunctionSignature{
gsql_string, {gsql_pg_numeric}, /*context_ptr=*/nullptr},
zetasql::FunctionSignature{
gsql_string, {gsql_interval}, /*context_ptr=*/nullptr},
},
function_options);
}
std::unique_ptr<zetasql::Function>
FunctionCatalog::GetInternalSequenceStateFunction(
const std::string& catalog_name) {
// Defines the function evaluator as a lambda, so it has access to the schema.
auto evaluator = [&](absl::Span<const zetasql::Value> args)
-> absl::StatusOr<zetasql::Value> {
ZETASQL_RET_CHECK(args.size() == 1 && args[0].type()->IsString());
if (!EmulatorFeatureFlags::instance()
.flags()
.enable_bit_reversed_positive_sequences) {
return error::UnsupportedFunction(kGetInternalSequenceStateFunctionName);
}
if (latest_schema_ == nullptr) {
return error::SequenceNeedsAccessToSchema();
}
std::string sequence_name;
if (latest_schema_->dialect() ==
database_api::DatabaseDialect::POSTGRESQL) {
sequence_name = args[0].string_value();
} else {
// ZetaSQL algebrizer prepends a prefix to the sequence name.
sequence_name =
std::string(absl::StripPrefix(args[0].string_value(), "_sequence_"));
}
const backend::Sequence* sequence =
latest_schema_->FindSequence(sequence_name);
if (sequence == nullptr) {
return error::SequenceNotFound(sequence_name);
}
return sequence->GetInternalSequenceState();
};
zetasql::FunctionOptions function_options;
function_options.set_evaluator(zetasql::FunctionEvaluator(evaluator));
return std::make_unique<zetasql::Function>(
kGetInternalSequenceStateFunctionName, catalog_name,
zetasql::Function::SCALAR,
std::vector<zetasql::FunctionSignature>{
zetasql::FunctionSignature{
zetasql::types::Int64Type(),
{zetasql::FunctionArgumentType::AnySequence()},
nullptr},
zetasql::FunctionSignature{zetasql::types::Int64Type(),
{zetasql::types::StringType()},
nullptr}},
function_options);
}
std::unique_ptr<zetasql::Function>
FunctionCatalog::GetTableColumnIdentityStateFunction(
const std::string& catalog_name) {
// Defines the function evaluator as a lambda, so it has access to the schema.
auto evaluator = [&](absl::Span<const zetasql::Value> args)
-> absl::StatusOr<zetasql::Value> {
ZETASQL_RET_CHECK(args.size() == 1 && args[0].type()->IsString());
if (!EmulatorFeatureFlags::instance().flags().enable_identity_columns) {
return error::UnsupportedFunction(
kGetTableColumnIdentityStateFunctionName);
}
if (latest_schema_ == nullptr) {
return error::SequenceNeedsAccessToSchema();
}
std::string column_path = args[0].string_value();
auto parsed_column_path = ParseFullyQualifiedColumnPath(column_path);
if (!parsed_column_path.has_value()) {
return error::InvalidColumnIdentifierFormat(column_path);
}
auto [schema_name, table_name, column_name] = *parsed_column_path;
std::string full_table_name =
schema_name.empty() ? table_name
: absl::StrCat(schema_name, ".", table_name);
const Table* table = latest_schema_->FindTable(full_table_name);
if (table == nullptr) {
return error::TableNotFoundInIdentityFunction(full_table_name);
}
const Column* column = table->FindColumn(column_name);
if (column == nullptr || !column->is_identity_column()) {
return error::ColumnNotFoundInIdentityFunction(full_table_name,
column_name);
}
ZETASQL_RET_CHECK(column->sequences_used().size() == 1);
const Sequence* sequence =
static_cast<const Sequence*>(column->sequences_used().at(0));
return sequence->GetInternalSequenceState();
};
zetasql::FunctionOptions function_options;
function_options.set_evaluator(zetasql::FunctionEvaluator(evaluator));
return std::make_unique<zetasql::Function>(
kGetTableColumnIdentityStateFunctionName, catalog_name,
zetasql::Function::SCALAR,
std::vector<zetasql::FunctionSignature>{
zetasql::FunctionSignature{zetasql::types::Int64Type(),
{zetasql::types::StringType()},
nullptr}},
function_options);
}
std::unique_ptr<zetasql::Function>
FunctionCatalog::GetNextSequenceValueFunction(const std::string& catalog_name) {
// Defines the function evaluator as a lambda, so it has access to the schema.
auto evaluator = [&](absl::Span<const zetasql::Value> args)
-> absl::StatusOr<zetasql::Value> {
ZETASQL_RET_CHECK(args.size() == 1 && args[0].type()->IsString());
if (!EmulatorFeatureFlags::instance()
.flags()
.enable_bit_reversed_positive_sequences) {
return error::UnsupportedFunction(kGetNextSequenceValueFunctionName);
}
if (latest_schema_ == nullptr) {
return error::SequenceNeedsAccessToSchema();
}
// ZetaSQL algebrizer prepends a prefix to the sequence name.
std::string sequence_name;
if (latest_schema_->dialect() ==
database_api::DatabaseDialect::POSTGRESQL) {
sequence_name = args[0].string_value();
} else {
sequence_name =
std::string(absl::StripPrefix(args[0].string_value(), "_sequence_"));
}
const backend::Sequence* sequence =
latest_schema_->FindSequence(sequence_name);
if (sequence == nullptr) {
return error::SequenceNotFound(sequence_name);
}
return sequence->GetNextSequenceValue();
};
zetasql::FunctionOptions function_options;
function_options.set_evaluator(zetasql::FunctionEvaluator(evaluator));
return std::make_unique<zetasql::Function>(
kGetNextSequenceValueFunctionName, catalog_name,
zetasql::Function::SCALAR,
std::vector<zetasql::FunctionSignature>{
zetasql::FunctionSignature{
zetasql::types::Int64Type(),
{zetasql::FunctionArgumentType::AnySequence()},
nullptr},
zetasql::FunctionSignature{zetasql::types::Int64Type(),
{zetasql::types::StringType()},
nullptr}},
function_options);
}
} // namespace backend
} // namespace emulator
} // namespace spanner
} // namespace google