// 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/field_behavior.proto";

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

// Represents privacy policy associated with "aggregation threshold" method.
message AggregationThresholdPolicy {
  // Optional. The threshold for the "aggregation threshold" policy.
  optional int64 threshold = 1 [(google.api.field_behavior) = OPTIONAL];

  // Optional. The privacy unit column(s) associated with this policy.
  // For now, only one column per data source object (table, view) is allowed as
  // a privacy unit column.
  // Representing as a repeated field in metadata for extensibility to
  // multiple columns in future.
  // Duplicates and Repeated struct fields are not allowed.
  // For nested fields, use dot notation ("outer.inner")
  repeated string privacy_unit_columns = 2
      [(google.api.field_behavior) = OPTIONAL];
}

// Represents privacy policy associated with "differential privacy" method.
message DifferentialPrivacyPolicy {
  // Optional. The maximum epsilon value that a query can consume. If the
  // subscriber specifies epsilon as a parameter in a SELECT query, it must be
  // less than or equal to this value. The epsilon parameter controls the amount
  // of noise that is added to the groups — a higher epsilon means less noise.
  optional double max_epsilon_per_query = 1
      [(google.api.field_behavior) = OPTIONAL];

  // Optional. The delta value that is used per query. Delta represents the
  // probability that any row will fail to be epsilon differentially private.
  // Indicates the risk associated with exposing aggregate rows in the result of
  // a query.
  optional double delta_per_query = 2 [(google.api.field_behavior) = OPTIONAL];

  // Optional. The maximum groups contributed value that is used per query.
  // Represents the maximum number of groups to which each protected entity can
  // contribute. Changing this value does not improve or worsen privacy. The
  // best value for accuracy and utility depends on the query and data.
  optional int64 max_groups_contributed = 3
      [(google.api.field_behavior) = OPTIONAL];

  // Optional. The privacy unit column associated with this policy. Differential
  // privacy policies can only have one privacy unit column per data source
  // object (table, view).
  optional string privacy_unit_column = 4
      [(google.api.field_behavior) = OPTIONAL];

  // Optional. The total epsilon budget for all queries against the
  // privacy-protected view. Each subscriber query against this view charges the
  // amount of epsilon they request in their query. If there is sufficient
  // budget, then the subscriber query attempts to complete. It might still fail
  // due to other reasons, in which case the charge is refunded. If there is
  // insufficient budget the query is rejected. There might be multiple charge
  // attempts if a single query references multiple views. In this case there
  // must be sufficient budget for all charges or the query is rejected and
  // charges are refunded in best effort. The budget does not have a refresh
  // policy and can only be updated via ALTER VIEW or circumvented by creating a
  // new view that can be queried with a fresh budget.
  optional double epsilon_budget = 5 [(google.api.field_behavior) = OPTIONAL];

  // Optional. The total delta budget for all queries against the
  // privacy-protected view. Each subscriber query against this view charges the
  // amount of delta that is pre-defined by the contributor through the privacy
  // policy delta_per_query field. If there is sufficient budget, then the
  // subscriber query attempts to complete. It might still fail due to other
  // reasons, in which case the charge is refunded. If there is insufficient
  // budget the query is rejected. There might be multiple charge attempts if a
  // single query references multiple views. In this case there must be
  // sufficient budget for all charges or the query is rejected and charges are
  // refunded in best effort. The budget does not have a refresh policy and can
  // only be updated via ALTER VIEW or circumvented by creating a new view that
  // can be queried with a fresh budget.
  optional double delta_budget = 6 [(google.api.field_behavior) = OPTIONAL];

  // Output only. The epsilon budget remaining. If budget is exhausted, no more
  // queries are allowed. Note that the budget for queries that are in progress
  // is deducted before the query executes. If the query fails or is cancelled
  // then the budget is refunded. In this case the amount of budget remaining
  // can increase.
  optional double epsilon_budget_remaining = 7
      [(google.api.field_behavior) = OUTPUT_ONLY];

  // Output only. The delta budget remaining. If budget is exhausted, no more
  // queries are allowed. Note that the budget for queries that are in progress
  // is deducted before the query executes. If the query fails or is cancelled
  // then the budget is refunded. In this case the amount of budget remaining
  // can increase.
  optional double delta_budget_remaining = 8
      [(google.api.field_behavior) = OUTPUT_ONLY];
}

// Represents privacy policy associated with "join restrictions". Join
// restriction gives data providers the ability to enforce joins on the
// 'join_allowed_columns' when data is queried from a privacy protected view.
message JoinRestrictionPolicy {
  // Enum for Join Restrictions policy.
  enum JoinCondition {
    // A join is neither required nor restricted on any column. Default value.
    JOIN_CONDITION_UNSPECIFIED = 0;

    // A join is required on at least one of the specified columns.
    JOIN_ANY = 1;

    // A join is required on all specified columns.
    JOIN_ALL = 2;

    // A join is not required, but if present it is only permitted on
    // 'join_allowed_columns'
    JOIN_NOT_REQUIRED = 3;

    // Joins are blocked for all queries.
    JOIN_BLOCKED = 4;
  }

  // Optional. Specifies if a join is required or not on queries for the view.
  // Default is JOIN_CONDITION_UNSPECIFIED.
  optional JoinCondition join_condition = 1
      [(google.api.field_behavior) = OPTIONAL];

  // Optional. The only columns that joins are allowed on.
  // This field is must be specified for join_conditions JOIN_ANY and JOIN_ALL
  // and it cannot be set for JOIN_BLOCKED.
  repeated string join_allowed_columns = 2
      [(google.api.field_behavior) = OPTIONAL];
}

// Represents privacy policy that contains the privacy requirements specified by
// the data owner. Currently, this is only supported on views.
message PrivacyPolicy {
  // Privacy policy associated with this requirement specification. Only one of
  // the privacy methods is allowed per data source object.
  oneof privacy_policy {
    // Optional. Policy used for aggregation thresholds.
    AggregationThresholdPolicy aggregation_threshold_policy = 2
        [(google.api.field_behavior) = OPTIONAL];

    // Optional. Policy used for differential privacy.
    DifferentialPrivacyPolicy differential_privacy_policy = 3
        [(google.api.field_behavior) = OPTIONAL];
  }

  // Optional. Join restriction policy is outside of the one of policies, since
  // this policy can be set along with other policies. This policy gives data
  // providers the ability to enforce joins on the 'join_allowed_columns' when
  // data is queried from a privacy protected view.
  optional JoinRestrictionPolicy join_restriction_policy = 1
      [(google.api.field_behavior) = OPTIONAL];
}
