absl::Status SchemaUpdaterImpl::CreateFunction()

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
}