in backend/schema/updater/schema_updater.cc [4118:4248]
absl::Status SchemaUpdaterImpl::CreateFunction(
const ddl::CreateFunction& ddl_function) {
if (ddl_function.function_kind() == ddl::Function::VIEW &&
latest_schema_->views().size() >= limits::kMaxViewsPerDatabase) {
return error::TooManyViewsPerDatabase(ddl_function.function_name(),
limits::kMaxViewsPerDatabase);
}
if (!ddl_function.has_sql_security() ||
ddl_function.sql_security() != ddl::Function::INVOKER) {
return ddl_function.function_kind() == ddl::Function::FUNCTION
? error::FunctionRequiresInvokerSecurity(
ddl_function.function_name())
: error::ViewRequiresInvokerSecurity(
ddl_function.function_name());
}
bool replace = false;
if (global_names_.HasName(ddl_function.function_name())) {
replace = CanFunctionReplaceTakenName(ddl_function);
if (!replace) {
return error::SchemaObjectAlreadyExists(
GetFunctionKindAsString(ddl_function), ddl_function.function_name());
}
}
if (!replace) {
ZETASQL_RETURN_IF_ERROR(global_names_.AddName(GetFunctionKindAsString(ddl_function),
ddl_function.function_name()));
}
if (IsBuiltInFunction(ddl_function.function_name())) {
return error::ReplacingBuiltInFunction(
ddl_function.is_or_replace() ? "create or replace" : "create",
GetFunctionKindAsString(ddl_function), ddl_function.function_name());
}
absl::Status error;
absl::flat_hash_set<const SchemaNode*> dependencies;
if (ddl_function.function_kind() == ddl::Function::FUNCTION) {
std::unique_ptr<zetasql::FunctionSignature> function_signature;
Udf::Determinism determinism_level = Udf::Determinism::DETERMINISTIC;
ZETASQL_RETURN_IF_ERROR(
AnalyzeFunctionDefinition(ddl_function, replace, &dependencies,
&function_signature, &determinism_level));
ZETASQL_ASSIGN_OR_RETURN(
Udf::Builder builder,
CreateFunctionBuilder(ddl_function, std::move(function_signature),
determinism_level, dependencies));
if (replace) {
const Udf* existing_udf =
latest_schema_->FindUdf(ddl_function.function_name());
// Check for a recursive view by analyzing the transitive set of
// dependencies, i.e., if the view is a dependency of itself.
auto transitive_deps =
GatherTransitiveDependenciesForSchemaNode(dependencies);
if (std::find_if(transitive_deps.begin(), transitive_deps.end(),
[existing_udf](const SchemaNode* dep) {
return (dep->As<const Udf>() != nullptr &&
dep->As<const Udf>()->Name() ==
existing_udf->Name());
}) != transitive_deps.end()) {
return error::ViewReplaceRecursive(existing_udf->Name());
}
return AlterNode<Udf>(existing_udf,
[&](Udf::Editor* editor) -> absl::Status {
// Just replace the udf definition completely.
// The temp instance inside builder will be
// cleaned up when the builder goes out of scope.
editor->copy_from(builder.get());
return absl::OkStatus();
});
}
if (SDLObjectName::IsFullyQualifiedName(ddl_function.function_name())) {
ZETASQL_RETURN_IF_ERROR(AlterInNamedSchema(
ddl_function.function_name(),
[&builder](NamedSchema::Editor* editor) -> absl::Status {
editor->add_udf(builder.get());
return absl::OkStatus();
}));
}
return AddNode(builder.build());
} else if (ddl_function.function_kind() == ddl::Function::VIEW) {
std::vector<View::Column> output_columns;
ZETASQL_RETURN_IF_ERROR(AnalyzeFunctionDefinition(ddl_function, replace,
&output_columns, &dependencies));
ZETASQL_ASSIGN_OR_RETURN(
View::Builder builder,
CreateFunctionBuilder(ddl_function, std::move(output_columns),
dependencies));
if (replace) {
const View* existing_view =
latest_schema_->FindView(ddl_function.function_name());
// Check for a recursive view by analyzing the transitive set of
// dependencies, i.e., if the view is a dependency of itself.
auto transitive_deps =
GatherTransitiveDependenciesForSchemaNode(dependencies);
if (std::find_if(transitive_deps.begin(), transitive_deps.end(),
[existing_view](const SchemaNode* dep) {
return (dep->As<const View>() != nullptr &&
dep->As<const View>()->Name() ==
existing_view->Name());
}) != transitive_deps.end()) {
return error::ViewReplaceRecursive(existing_view->Name());
}
return AlterNode<View>(existing_view,
[&](View::Editor* editor) -> absl::Status {
// Just replace the view definition completely.
// The temp instance inside builder will be
// cleaned up when the builder goes out of scope.
editor->copy_from(builder.get());
return absl::OkStatus();
});
}
// No need to account for the named schema in the replace case.
if (SDLObjectName::IsFullyQualifiedName(ddl_function.function_name())) {
ZETASQL_RETURN_IF_ERROR(AlterInNamedSchema(
ddl_function.function_name(),
[&builder](NamedSchema::Editor* editor) -> absl::Status {
editor->add_view(builder.get());
return absl::OkStatus();
}));
}
return AddNode(builder.build());
}
return absl::OkStatus(); // ERROR FOR UNSUPPORTED VIEW TYPE
}