// Copyright 2025 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.

syntax = "proto3";

package google.cloud.bigquery.v2;

import "google/api/annotations.proto";
import "google/api/client.proto";
import "google/api/field_behavior.proto";
import "google/cloud/bigquery/v2/routine_reference.proto";
import "google/cloud/bigquery/v2/standard_sql.proto";
import "google/protobuf/empty.proto";
import "google/protobuf/field_mask.proto";
import "google/protobuf/wrappers.proto";

option go_package = "cloud.google.com/go/bigquery/apiv2/bigquerypb;bigquerypb";
option java_outer_classname = "RoutineProto";
option java_package = "com.google.cloud.bigquery.v2";

// RoutineService provides management access to BigQuery routines.
service RoutineService {
  option (google.api.default_host) = "bigquery.googleapis.com";
  option (google.api.oauth_scopes) =
      "https://www.googleapis.com/auth/bigquery,"
      "https://www.googleapis.com/auth/cloud-platform,"
      "https://www.googleapis.com/auth/cloud-platform.read-only";

  // Gets the specified routine resource by routine ID.
  rpc GetRoutine(GetRoutineRequest) returns (Routine) {
    option (google.api.http) = {
      get: "/bigquery/v2/projects/{project_id=*}/datasets/{dataset_id=*}/routines/{routine_id=*}"
    };
  }

  // Creates a new routine in the dataset.
  rpc InsertRoutine(InsertRoutineRequest) returns (Routine) {
    option (google.api.http) = {
      post: "/bigquery/v2/projects/{project_id=*}/datasets/{dataset_id=*}/routines"
      body: "routine"
    };
  }

  // Updates information in an existing routine. The update method replaces the
  // entire Routine resource.
  rpc UpdateRoutine(UpdateRoutineRequest) returns (Routine) {
    option (google.api.http) = {
      put: "/bigquery/v2/projects/{project_id=*}/datasets/{dataset_id=*}/routines/{routine_id=*}"
      body: "routine"
    };
  }

  // Patches information in an existing routine. The patch method does a partial
  // update to an existing Routine resource.
  rpc PatchRoutine(PatchRoutineRequest) returns (Routine) {}

  // Deletes the routine specified by routineId from the dataset.
  rpc DeleteRoutine(DeleteRoutineRequest) returns (google.protobuf.Empty) {
    option (google.api.http) = {
      delete: "/bigquery/v2/projects/{project_id=*}/datasets/{dataset_id=*}/routines/{routine_id=*}"
    };
  }

  // Lists all routines in the specified dataset. Requires the READER dataset
  // role.
  rpc ListRoutines(ListRoutinesRequest) returns (ListRoutinesResponse) {
    option (google.api.http) = {
      get: "/bigquery/v2/projects/{project_id=*}/datasets/{dataset_id=*}/routines"
    };
  }
}

// A user-defined function or a stored procedure.
message Routine {
  // The fine-grained type of the routine.
  enum RoutineType {
    // Default value.
    ROUTINE_TYPE_UNSPECIFIED = 0;

    // Non-built-in persistent scalar function.
    SCALAR_FUNCTION = 1;

    // Stored procedure.
    PROCEDURE = 2;

    // Non-built-in persistent TVF.
    TABLE_VALUED_FUNCTION = 3;

    // Non-built-in persistent aggregate function.
    AGGREGATE_FUNCTION = 4;
  }

  // The language of the routine.
  enum Language {
    // Default value.
    LANGUAGE_UNSPECIFIED = 0;

    // SQL language.
    SQL = 1;

    // JavaScript language.
    JAVASCRIPT = 2;

    // Python language.
    PYTHON = 3;

    // Java language.
    JAVA = 4;

    // Scala language.
    SCALA = 5;
  }

  // Input/output argument of a function or a stored procedure.
  message Argument {
    // Represents the kind of a given argument.
    enum ArgumentKind {
      // Default value.
      ARGUMENT_KIND_UNSPECIFIED = 0;

      // The argument is a variable with fully specified type, which can be a
      // struct or an array, but not a table.
      FIXED_TYPE = 1;

      // The argument is any type, including struct or array, but not a table.
      ANY_TYPE = 2;
    }

    // The input/output mode of the argument.
    enum Mode {
      // Default value.
      MODE_UNSPECIFIED = 0;

      // The argument is input-only.
      IN = 1;

      // The argument is output-only.
      OUT = 2;

      // The argument is both an input and an output.
      INOUT = 3;
    }

    // Optional. The name of this argument. Can be absent for function return
    // argument.
    string name = 1 [(google.api.field_behavior) = OPTIONAL];

    // Optional. Defaults to FIXED_TYPE.
    ArgumentKind argument_kind = 2 [(google.api.field_behavior) = OPTIONAL];

    // Optional. Specifies whether the argument is input or output.
    // Can be set for procedures only.
    Mode mode = 3;

    // Set if argument_kind == FIXED_TYPE.
    StandardSqlDataType data_type = 4;

    // Optional. Whether the argument is an aggregate function parameter.
    // Must be Unset for routine types other than AGGREGATE_FUNCTION.
    // For AGGREGATE_FUNCTION, if set to false, it is equivalent to adding "NOT
    // AGGREGATE" clause in DDL; Otherwise, it is equivalent to omitting "NOT
    // AGGREGATE" clause in DDL.
    google.protobuf.BoolValue is_aggregate = 6
        [(google.api.field_behavior) = OPTIONAL];
  }

  // JavaScript UDF determinism levels.
  //
  // If all JavaScript UDFs are DETERMINISTIC, the query result is
  // potentially cacheable (see below). If any JavaScript UDF is
  // NOT_DETERMINISTIC, the query result is not cacheable.
  //
  // Even if a JavaScript UDF is deterministic, many other factors can prevent
  // usage of cached query results. Example factors include but not limited to:
  // DDL/DML, non-deterministic SQL function calls, update of referenced
  // tables/views/UDFs or imported JavaScript libraries.
  //
  // SQL UDFs cannot have determinism specified. Their determinism is
  // automatically determined.
  enum DeterminismLevel {
    // The determinism of the UDF is unspecified.
    DETERMINISM_LEVEL_UNSPECIFIED = 0;

    // The UDF is deterministic, meaning that 2 function calls with the same
    // inputs always produce the same result, even across 2 query runs.
    DETERMINISTIC = 1;

    // The UDF is not deterministic.
    NOT_DETERMINISTIC = 2;
  }

  // Security mode.
  enum SecurityMode {
    // The security mode of the routine is unspecified.
    SECURITY_MODE_UNSPECIFIED = 0;

    // The routine is to be executed with the privileges of the user who
    // defines it.
    DEFINER = 1;

    // The routine is to be executed with the privileges of the user who
    // invokes it.
    INVOKER = 2;
  }

  // Options for a remote user-defined function.
  message RemoteFunctionOptions {
    // Endpoint of the user-provided remote service, e.g.
    // ```https://us-east1-my_gcf_project.cloudfunctions.net/remote_add```
    string endpoint = 1;

    // Fully qualified name of the user-provided connection object which holds
    // the authentication information to send requests to the remote service.
    // Format:
    // ```"projects/{projectId}/locations/{locationId}/connections/{connectionId}"```
    string connection = 2;

    // User-defined context as a set of key/value pairs, which will be sent as
    // function invocation context together with batched arguments in the
    // requests to the remote service. The total number of bytes of keys and
    // values must be less than 8KB.
    map<string, string> user_defined_context = 3;

    // Max number of rows in each batch sent to the remote service.
    // If absent or if 0, BigQuery dynamically decides the number of rows in a
    // batch.
    int64 max_batching_rows = 4;
  }

  // Data governance type values. Only supports `DATA_MASKING`.
  enum DataGovernanceType {
    // The data governance type is unspecified.
    DATA_GOVERNANCE_TYPE_UNSPECIFIED = 0;

    // The data governance type is data masking.
    DATA_MASKING = 1;
  }

  // Output only. A hash of this resource.
  string etag = 1 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Required. Reference describing the ID of this routine.
  RoutineReference routine_reference = 2
      [(google.api.field_behavior) = REQUIRED];

  // Required. The type of routine.
  RoutineType routine_type = 3 [(google.api.field_behavior) = REQUIRED];

  // Output only. The time when this routine was created, in milliseconds since
  // the epoch.
  int64 creation_time = 4 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Output only. The time when this routine was last modified, in milliseconds
  // since the epoch.
  int64 last_modified_time = 5 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Optional. Defaults to "SQL" if remote_function_options field is absent, not
  // set otherwise.
  Language language = 6 [(google.api.field_behavior) = OPTIONAL];

  // Optional.
  repeated Argument arguments = 7;

  // Optional if language = "SQL"; required otherwise.
  // Cannot be set if routine_type = "TABLE_VALUED_FUNCTION".
  //
  // If absent, the return type is inferred from definition_body at query time
  // in each query that references this routine. If present, then the evaluated
  // result will be cast to the specified returned type at query time.
  //
  // For example, for the functions created with the following statements:
  //
  // * `CREATE FUNCTION Add(x FLOAT64, y FLOAT64) RETURNS FLOAT64 AS (x + y);`
  //
  // * `CREATE FUNCTION Increment(x FLOAT64) AS (Add(x, 1));`
  //
  // * `CREATE FUNCTION Decrement(x FLOAT64) RETURNS FLOAT64 AS (Add(x, -1));`
  //
  // The return_type is `{type_kind: "FLOAT64"}` for `Add` and `Decrement`, and
  // is absent for `Increment` (inferred as FLOAT64 at query time).
  //
  // Suppose the function `Add` is replaced by
  //   `CREATE OR REPLACE FUNCTION Add(x INT64, y INT64) AS (x + y);`
  //
  // Then the inferred return type of `Increment` is automatically changed to
  // INT64 at query time, while the return type of `Decrement` remains FLOAT64.
  StandardSqlDataType return_type = 10;

  // Optional. Can be set only if routine_type = "TABLE_VALUED_FUNCTION".
  //
  // If absent, the return table type is inferred from definition_body at query
  // time in each query that references this routine. If present, then the
  // columns in the evaluated table result will be cast to match the column
  // types specified in return table type, at query time.
  StandardSqlTableType return_table_type = 13
      [(google.api.field_behavior) = OPTIONAL];

  // Optional. If language = "JAVASCRIPT", this field stores the path of the
  // imported JAVASCRIPT libraries.
  repeated string imported_libraries = 8;

  // Required. The body of the routine.
  //
  // For functions, this is the expression in the AS clause.
  //
  // If language=SQL, it is the substring inside (but excluding) the
  // parentheses. For example, for the function created with the following
  // statement:
  //
  // `CREATE FUNCTION JoinLines(x string, y string) as (concat(x, "\n", y))`
  //
  // The definition_body is `concat(x, "\n", y)` (\n is not replaced with
  // linebreak).
  //
  // If language=JAVASCRIPT, it is the evaluated string in the AS clause.
  // For example, for the function created with the following statement:
  //
  // `CREATE FUNCTION f() RETURNS STRING LANGUAGE js AS 'return "\n";\n'`
  //
  // The definition_body is
  //
  // `return "\n";\n`
  //
  // Note that both \n are replaced with linebreaks.
  string definition_body = 9;

  // Optional. The description of the routine, if defined.
  string description = 11 [(google.api.field_behavior) = OPTIONAL];

  // Optional. The determinism level of the JavaScript UDF, if defined.
  DeterminismLevel determinism_level = 12
      [(google.api.field_behavior) = OPTIONAL];

  // Optional. The security mode of the routine, if defined. If not defined, the
  // security mode is automatically determined from the routine's configuration.
  SecurityMode security_mode = 18 [(google.api.field_behavior) = OPTIONAL];

  // Optional. Use this option to catch many common errors. Error checking is
  // not exhaustive, and successfully creating a procedure doesn't guarantee
  // that the procedure will successfully execute at runtime. If `strictMode` is
  // set to `TRUE`, the procedure body is further checked for errors such as
  // non-existent tables or columns. The `CREATE PROCEDURE` statement fails if
  // the body fails any of these checks.
  //
  // If `strictMode` is set to `FALSE`, the procedure body is checked only for
  // syntax. For procedures that invoke themselves recursively, specify
  // `strictMode=FALSE` to avoid non-existent procedure errors during
  // validation.
  //
  // Default value is `TRUE`.
  google.protobuf.BoolValue strict_mode = 14
      [(google.api.field_behavior) = OPTIONAL];

  // Optional. Remote function specific options.
  RemoteFunctionOptions remote_function_options = 15
      [(google.api.field_behavior) = OPTIONAL];

  // Optional. Spark specific options.
  SparkOptions spark_options = 16 [(google.api.field_behavior) = OPTIONAL];

  // Optional. If set to `DATA_MASKING`, the function is validated and made
  // available as a masking function. For more information, see [Create custom
  // masking
  // routines](https://cloud.google.com/bigquery/docs/user-defined-functions#custom-mask).
  DataGovernanceType data_governance_type = 17
      [(google.api.field_behavior) = OPTIONAL];
}

// Options for a user-defined Spark routine.
message SparkOptions {
  // Fully qualified name of the user-provided Spark connection object. Format:
  // ```"projects/{project_id}/locations/{location_id}/connections/{connection_id}"```
  string connection = 1;

  // Runtime version. If not specified, the default runtime version is used.
  string runtime_version = 2;

  // Custom container image for the runtime environment.
  string container_image = 3;

  // Configuration properties as a set of key/value pairs, which will be passed
  // on to the Spark application. For more information, see
  // [Apache Spark](https://spark.apache.org/docs/latest/index.html) and the
  // [procedure option
  // list](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#procedure_option_list).
  map<string, string> properties = 4;

  // The main file/jar URI of the Spark application. Exactly one of the
  // definition_body field and the main_file_uri field must be set for Python.
  // Exactly one of main_class and main_file_uri field
  // should be set for Java/Scala language type.
  string main_file_uri = 5;

  // Python files to be placed on the PYTHONPATH for PySpark application.
  // Supported file types: `.py`, `.egg`, and `.zip`. For more information
  // about Apache Spark, see
  // [Apache Spark](https://spark.apache.org/docs/latest/index.html).
  repeated string py_file_uris = 6;

  // JARs to include on the driver and executor CLASSPATH.
  // For more information about Apache Spark, see
  // [Apache Spark](https://spark.apache.org/docs/latest/index.html).
  repeated string jar_uris = 7;

  // Files to be placed in the working directory of each executor.
  // For more information about Apache Spark, see
  // [Apache Spark](https://spark.apache.org/docs/latest/index.html).
  repeated string file_uris = 8;

  // Archive files to be extracted into the working directory of each executor.
  // For more information about Apache Spark, see
  // [Apache Spark](https://spark.apache.org/docs/latest/index.html).
  repeated string archive_uris = 9;

  // The fully qualified name of a class in jar_uris, for example,
  // com.example.wordcount. Exactly one of main_class and main_jar_uri field
  //  should be set for Java/Scala language type.
  string main_class = 10;
}

// Describes the format for getting information about a routine.
message GetRoutineRequest {
  // Required. Project ID of the requested routine
  string project_id = 1 [(google.api.field_behavior) = REQUIRED];

  // Required. Dataset ID of the requested routine
  string dataset_id = 2 [(google.api.field_behavior) = REQUIRED];

  // Required. Routine ID of the requested routine
  string routine_id = 3 [(google.api.field_behavior) = REQUIRED];
}

// Describes the format for inserting a routine.
message InsertRoutineRequest {
  // Required. Project ID of the new routine
  string project_id = 1 [(google.api.field_behavior) = REQUIRED];

  // Required. Dataset ID of the new routine
  string dataset_id = 2 [(google.api.field_behavior) = REQUIRED];

  // Required. A routine resource to insert
  Routine routine = 3 [(google.api.field_behavior) = REQUIRED];
}

// Describes the format for updating a routine.
message UpdateRoutineRequest {
  // Required. Project ID of the routine to update
  string project_id = 1 [(google.api.field_behavior) = REQUIRED];

  // Required. Dataset ID of the routine to update
  string dataset_id = 2 [(google.api.field_behavior) = REQUIRED];

  // Required. Routine ID of the routine to update
  string routine_id = 3 [(google.api.field_behavior) = REQUIRED];

  // Required. A routine resource which will replace the specified routine
  Routine routine = 4 [(google.api.field_behavior) = REQUIRED];
}

// Describes the format for the partial update (patch) of a routine.
message PatchRoutineRequest {
  // Required. Project ID of the routine to update
  string project_id = 1 [(google.api.field_behavior) = REQUIRED];

  // Required. Dataset ID of the routine to update
  string dataset_id = 2 [(google.api.field_behavior) = REQUIRED];

  // Required. Routine ID of the routine to update
  string routine_id = 3 [(google.api.field_behavior) = REQUIRED];

  // Required. A routine resource which will be used to partially
  // update the specified routine
  Routine routine = 4 [(google.api.field_behavior) = REQUIRED];

  // Only the Routine fields in the field mask are updated
  // by the given routine. Repeated routine fields will be fully replaced
  // if contained in the field mask.
  google.protobuf.FieldMask field_mask = 5;
}

// Describes the format for deleting a routine.
message DeleteRoutineRequest {
  // Required. Project ID of the routine to delete
  string project_id = 1 [(google.api.field_behavior) = REQUIRED];

  // Required. Dataset ID of the routine to delete
  string dataset_id = 2 [(google.api.field_behavior) = REQUIRED];

  // Required. Routine ID of the routine to delete
  string routine_id = 3 [(google.api.field_behavior) = REQUIRED];
}

// Describes the format for listing routines.
message ListRoutinesRequest {
  // Required. Project ID of the routines to list
  string project_id = 1 [(google.api.field_behavior) = REQUIRED];

  // Required. Dataset ID of the routines to list
  string dataset_id = 2 [(google.api.field_behavior) = REQUIRED];

  // The maximum number of results to return in a single response page.
  // Leverage the page tokens to iterate through the entire collection.
  google.protobuf.UInt32Value max_results = 3;

  // Page token, returned by a previous call, to request the next page of
  // results
  string page_token = 4;

  // If set, then only the Routines matching this filter are returned.
  // The supported format is `routineType:{RoutineType}`, where `{RoutineType}`
  // is a RoutineType enum. For example: `routineType:SCALAR_FUNCTION`.
  string filter = 6;
}

// Describes the format of a single result page when listing routines.
message ListRoutinesResponse {
  // Routines in the requested dataset. Unless read_mask is set in the request,
  // only the following fields are populated:
  // etag, project_id, dataset_id, routine_id, routine_type, creation_time,
  // last_modified_time, language, and remote_function_options.
  repeated Routine routines = 1;

  // A token to request the next page of results.
  string next_page_token = 2;
}
