backend/schema/validators/udf_validator.cc (143 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/validators/udf_validator.h"
#include <memory>
#include <string>
#include <vector>
#include "zetasql/public/function_signature.h"
#include "zetasql/public/types/type_factory.h"
#include "absl/container/flat_hash_set.h"
#include "absl/status/status.h"
#include "absl/strings/ascii.h"
#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "backend/schema/catalog/column.h"
#include "backend/schema/catalog/table.h"
#include "backend/schema/catalog/udf.h"
#include "backend/schema/catalog/view.h"
#include "backend/schema/graph/schema_node.h"
#include "backend/schema/updater/global_schema_names.h"
#include "backend/schema/updater/schema_validation_context.h"
#include "backend/schema/updater/sql_expression_validators.h"
#include "common/errors.h"
#include "zetasql/base/ret_check.h"
#include "zetasql/base/status_macros.h"
namespace google {
namespace spanner {
namespace emulator {
namespace backend {
namespace {
absl::Status ValidateUdfSignatureChange(absl::string_view modify_action,
absl::string_view dependency_name,
absl::string_view param_list,
const Udf* dependent_udf,
const Schema* temp_new_schema,
zetasql::TypeFactory* type_factory) {
// Re-analyze the dependent udf based on the new definition of the dependency
// in the temporary new schema.
absl::flat_hash_set<const SchemaNode*> unused_new_deps;
std::unique_ptr<zetasql::FunctionSignature> unused_signature;
Udf::Determinism determinism_level =
Udf::Determinism::DETERMINISM_UNSPECIFIED;
auto status = AnalyzeUdfDefinition(
dependent_udf->Name(), param_list, dependent_udf->body(), temp_new_schema,
type_factory, &unused_new_deps, &unused_signature, &determinism_level);
if (!status.ok()) {
return error::DependentFunctionBecomesInvalid(
modify_action, dependency_name, dependent_udf->Name(),
status.message());
}
return absl::OkStatus();
}
} // namespace
absl::Status UdfValidator::Validate(const Udf* udf,
SchemaValidationContext* context) {
ZETASQL_RET_CHECK(!udf->name_.empty());
ZETASQL_RET_CHECK(!udf->body_.empty());
if (context->is_postgresql_dialect()) {
ZETASQL_RET_CHECK(udf->postgresql_oid().has_value());
} else {
ZETASQL_RET_CHECK(!udf->postgresql_oid().has_value());
}
ZETASQL_RETURN_IF_ERROR(GlobalSchemaNames::ValidateSchemaName(
udf->GetSchemaNameInfo()->kind, udf->Name()));
for (const SchemaNode* dependency : udf->dependencies()) {
ZETASQL_RET_CHECK(!dependency->is_deleted());
}
return absl::OkStatus();
}
// TODO Implement this function once UDFs are added to the catalog.
absl::Status UdfValidator::ValidateUpdate(const Udf* udf, const Udf* old_udf,
SchemaValidationContext* context) {
// During a REPLACE, the udf name's case can change.
if (context->IsModifiedNode(udf)) {
ZETASQL_RET_CHECK(absl::EqualsIgnoreCase(udf->Name(), old_udf->Name()));
} else {
ZETASQL_RET_CHECK_EQ(udf->Name(), old_udf->Name());
}
if (udf->is_deleted()) {
context->global_names()->RemoveName(udf->Name());
return absl::OkStatus();
}
if (context->is_postgresql_dialect()) {
ZETASQL_RET_CHECK(udf->postgresql_oid().has_value());
ZETASQL_RET_CHECK(old_udf->postgresql_oid().has_value());
ZETASQL_RET_CHECK_EQ(udf->postgresql_oid().value(),
old_udf->postgresql_oid().value());
} else {
ZETASQL_RET_CHECK(!udf->postgresql_oid().has_value());
ZETASQL_RET_CHECK(!old_udf->postgresql_oid().has_value());
}
for (const SchemaNode* dependency : udf->dependencies()) {
// Cannot drop dependencies of the udf.
if (dependency->is_deleted()) {
// If the deleted dependency is a column that was marked as deleted
// as a result of a table drop, then we use the table to report the error
// message.
if (auto dep_column = dependency->As<const Column>();
dep_column != nullptr) {
const Table* dep_table = dep_column->table();
if (dep_table->is_deleted()) {
dependency = dep_table;
}
}
const auto& dep_info = dependency->GetSchemaNameInfo();
std::string dependency_type =
(dep_info->global ? absl::AsciiStrToUpper(dep_info->kind)
: absl::AsciiStrToLower(dep_info->kind));
return error::InvalidDropDependentFunction(dependency_type,
dep_info->name, udf->Name());
}
// If a dependency was updated during the schema change then we need to
// re-analyze *this.
if (context->IsModifiedNode(dependency)) {
const auto& dep_info = dependency->GetSchemaNameInfo();
std::string dependency_type =
(dep_info->global ? absl::AsciiStrToUpper(dep_info->kind)
: absl::AsciiStrToLower(dep_info->kind));
std::string modify_action = absl::StrCat("alter ", dependency_type);
std::string dependency_name;
if (auto dep_view = dependency->As<const View>(); dep_view != nullptr) {
dependency_name = dep_view->Name();
}
if (auto dep_table = dependency->As<const Table>();
dep_table != nullptr) {
dependency_name = dep_table->Name();
}
if (auto dep_column = dependency->As<const Column>();
dep_column != nullptr) {
dependency_name = dep_column->FullName();
}
if (auto dep_udf = dependency->As<const Udf>(); dep_udf != nullptr) {
dependency_name = dep_udf->Name();
}
// No need to check modifications on index dependencies as indexes
// cannot currently be altered.
std::string param_list = "";
auto args = udf->signature()->arguments();
for (int i = 0; i < args.size(); i++) {
const auto& param = args[i];
param_list += param.argument_name() + " " +
param.type()->TypeName(zetasql::PRODUCT_EXTERNAL);
if (i < args.size() - 1) {
param_list += ", ";
}
}
ZETASQL_RETURN_IF_ERROR(ValidateUdfSignatureChange(
modify_action, dependency_name, param_list, udf,
context->tmp_new_schema(), context->type_factory()));
}
}
return absl::OkStatus();
}
} // namespace backend
} // namespace emulator
} // namespace spanner
} // namespace google