backend/schema/ddl/operations.proto (891 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. // syntax = "proto2"; package google.spanner.emulator.backend.ddl; // Represents the AST of set of Spanner DDL statements. message DDLStatementList { repeated DDLStatement statement = 1; } message DDLStatement { oneof statement { // Database statements. CreateDatabase create_database = 1; AlterDatabase alter_database = 4; // Global statements (other than "* DATABASE" statements). CreateProtoBundle create_proto_bundle = 20; AlterProtoBundle alter_proto_bundle = 21; DropProtoBundle drop_proto_bundle = 22; // Table statements. CreateTable create_table = 8; SetColumnOptions set_column_options = 10; AlterTable alter_table = 11; DropTable drop_table = 12; RenameTable rename_table = 88; // Index statements. CreateIndex create_index = 13; AlterIndex alter_index = 15; DropIndex drop_index = 16; CreateSearchIndex create_search_index = 76; DropSearchIndex drop_search_index = 77; CreateVectorIndex create_vector_index = 92; DropVectorIndex drop_vector_index = 97; AlterVectorIndex alter_vector_index = 99; // Change Stream statements. CreateChangeStream create_change_stream = 56; AlterChangeStream alter_change_stream = 67; DropChangeStream drop_change_stream = 60; // Function statements. CreateFunction create_function = 34; DropFunction drop_function = 36; // Sequence statements. CreateSequence create_sequence = 30; AlterSequence alter_sequence = 31; DropSequence drop_sequence = 32; // Model statements. CreateModel create_model = 71; AlterModel alter_model = 72; DropModel drop_model = 73; // Property Graph statements. CreatePropertyGraph create_property_graph = 85; DropPropertyGraph drop_property_graph = 86; // Schema statements. CreateSchema create_schema = 68; AlterSchema alter_schema = 69; DropSchema drop_schema = 70; // Locality Group statements. CreateLocalityGroup create_locality_group = 17; AlterLocalityGroup alter_locality_group = 18; DropLocalityGroup drop_locality_group = 19; // Statistics statements. Analyze analyze = 75; // FGAC statements CreateRole create_role = 61; DropRole drop_role = 62; GrantPrivilege grant_privilege = 63; RevokePrivilege revoke_privilege = 64; GrantMembership grant_membership = 65; RevokeMembership revoke_membership = 66; // Placement statements. CreatePlacement create_placement = 80; AlterPlacement alter_placement = 81; DropPlacement drop_placement = 82; } } // Used for all SET OPTIONS statements. message SetOption { optional string option_name = 1; // Field name within *OptionsProto. // Option value: exactly one of the following fields must be present. optional bool null_value = 2; // Clears the field regardless of type. optional bool bool_value = 3; optional int64 int64_value = 4; optional double double_value = 8; optional string string_value = 5; repeated string string_list_value = 6; } // Used for CREATE PROTO BUNDLE and ALTER PROTO BUNDLE message ProtoType { optional string source_name = 1; } // If database dialect, e.g., PostgreSQL compatible dialect, is different from // GoogleSQL, this message contains information about original SQL expression // or original SQL select statement used to generate target GoogleSQL expression // or statement. It is used by CheckConstraint, GeneratedColumnDefinition, // ColumnDefaultDefinition and CreateFunction. message SQLExpressionOrigin { // Expression in original dialect. optional string original_expression = 1; optional string serialized_parse_tree = 2; } message TypeDefinition { enum Type { NONE = 0; DOUBLE = 1; FLOAT = 2; INT64 = 3; BOOL = 7; STRING = 8; BYTES = 10; TIMESTAMP = 18; DATE = 15; ARRAY = 16; NUMERIC = 19; JSON = 20; PG_NUMERIC = 21; TOKENLIST = 22; BIGBYTES = 23; PG_JSONB = 24; STRUCT = 26; INTERVAL = 27; } optional Type type = 1; optional string proto_type_name = 2; optional bool proto_is_validated = 3; optional int64 length = 4; optional TypeDefinition array_subtype = 5; message StructDescriptor { message Field { optional string name = 1; optional TypeDefinition type = 2; } repeated Field field = 1; } optional StructDescriptor struct_descriptor = 6; // Set iff type == STRUCT. } message ColumnDefinition { // Required in all cases, except when stored as part of array_subtype. optional string column_name = 1; enum Type { NONE = 0; DOUBLE = 1; FLOAT = 2; INT64 = 3; BOOL = 7; STRING = 8; BYTES = 10; TIMESTAMP = 18; DATE = 15; ARRAY = 16; NUMERIC = 19; JSON = 20; PG_NUMERIC = 21; TOKENLIST = 22; PG_JSONB = 24; STRUCT = 26; INTERVAL = 27; } optional Type type = 2; // Fully-qualified proto type name. optional string proto_type_name = 3; optional bool not_null = 6; // User-specified max length for this column (STRING, BYTES // PROTO // only). // This field will only be set if the user defines a specific column length; // if 'MAX' is specified, it will be unset. optional int64 length = 7; // Enforce the size of a search vector. Currently it can only apply on ARRAY // column "Embeddings ARRAY<FLOAT32>(vector_length=>128)". optional int32 vector_length = 16; repeated SetOption set_options = 8; optional ColumnDefinition array_subtype = 9; // Set iff type == ARRAY. // True if the column is marked as hidden. Hidden columns won't show up in // SELECT * queries. optional bool hidden = 10; optional bool placement_key = 15; // Stores information about generated columns. message GeneratedColumnDefinition { // Googlesql string for the generated column. optional string expression = 1; // True if the generated column is defined as STORED. optional bool stored = 2; // Information about SQL expression in its original dialect, e.g., // PostgreSQL (before translation into GoogleSQL). GoogleSQL is not using // this field. optional SQLExpressionOrigin expression_origin = 4; } optional GeneratedColumnDefinition generated_column = 11; message ColumnDefaultDefinition { // Googlesql string for the default expression. optional string expression = 1; // Information about SQL expression in its original dialect, e.g., // PostgreSQL (before translation into GoogleSQL). GoogleSQL is not using // this field. optional SQLExpressionOrigin expression_origin = 2; } optional ColumnDefaultDefinition column_default = 12; optional TypeDefinition type_definition = 13; // The fields should match the definition of CreateSequence (the field numbers // can be different). message IdentityColumnDefinition { enum Type { BIT_REVERSED_POSITIVE = 1; } optional Type type = 1; optional int64 start_with_counter = 2; optional int64 skip_range_min = 3; optional int64 skip_range_max = 4; } optional IdentityColumnDefinition identity_column = 17; } // Used for CREATE TABLE and CREATE INDEX message KeyPartClause { // Name of key component. May refer to a column name (e.g. dob), or // to a protocol-buffer stored scalar (e.g. data_proto.name). optional string key_name = 1; enum Order { ASC = 0; // Ascending direction with NULLs sorted first. DESC = 1; // Descending direction with NULLs sorted last. ASC_NULLS_LAST = 2; // Ascending direction with NULLs sorted last. DESC_NULLS_FIRST = 3; // Descending direction with NULLs sorted first. } // The ordering specified by this key component (if at all) optional Order order = 2 [default = ASC]; } message ForeignKey { // Optional name of the foreign key constraint. optional string constraint_name = 1; // One or more names of columns of the table defining the foreign key. repeated string constrained_column_name = 2; // Dotted path name of the referenced table. optional string referenced_table_name = 3; // The names of columns of the referenced table. The number of referenced // column names is equal to the number of constrained column names. repeated string referenced_column_name = 4; // Whether or not the constraint is enforced. optional bool enforced = 5; // Referential actions. enum Action { reserved 2, 4, 5; ACTION_UNSPECIFIED = 0; // Unspecified action enum. NO_ACTION = 1; // Referential checks only; default if unspecified. CASCADE = 3; // Constrained rows updated or deleted. } optional Action on_update = 6; optional Action on_delete = 7; } message Function { enum Kind { INVALID_KIND = 0; VIEW = 1; FUNCTION = 2; } enum Language { UNSPECIFIED_LANGUAGE = 0; SQL = 1; } enum SqlSecurity { UNSPECIFIED_SQL_SECURITY = 0; INVOKER = 2; } message Parameter { optional string name = 1; optional string param_typename = 2; optional string default_value = 3; } enum Determinism { UNSPECIFIED_DETERMINISM = 0; // Always returns the same result when passed the same arguments. DETERMINISTIC = 1; // Within one run of a query statement, the function will consistently // return the same result for the same argument values. However, the result // could change across two runs. NOT_DETERMINISTIC_STABLE = 2; // The function does not always return the same result when passed the same // arguments, even within the same run of a query statement. NOT_DETERMINISTIC_VOLATILE = 3; } } message CreateFunction { optional string function_name = 1; optional Function.Kind function_kind = 3; optional bool is_or_replace = 4; optional Function.SqlSecurity sql_security = 5; repeated Function.Parameter param = 6; optional string return_typename = 7; optional string sql_body = 9; optional SQLExpressionOrigin sql_body_origin = 12; optional Function.Language language = 10; optional Function.Determinism determinism = 11; } message DropFunction { optional string function_name = 1; optional Function.Kind function_kind = 2; optional ExistenceModifier existence_modifier = 3; } message CheckConstraint { // Name of the check constraint. optional string name = 1; // Required // GoogleSQL string for the constraint. optional string expression = 2; // True if the constraint is enforced. optional bool enforced = 3; // Information about SQL expression in its original dialect, e.g., PostgreSQL // (before translation into GoogleSQL). GoogleSQL not using this field. optional SQLExpressionOrigin expression_origin = 4; } // Used for CREATE TABLE. message InterleaveClause { // The name of the table in which this table/queue is interleaved. It is // referred to as the interleaving table. optional string table_name = 1; enum Type { // Interleave this table in the parent table, requiring that the parent row // exists for the child row to exist. IN_PARENT = 0; // Interleave this table in the parent table, without establishing any // parent-child row existence relationship. Only used for supporting // INTERLEAVE IN indexes for the PostgreSQL dialect. IN = 1; } // Type of the relationship between the two tables involved. optional Type type = 4 [default = IN_PARENT]; enum Action { NO_ACTION = 0; CASCADE = 1; } optional Action on_delete = 3; } message CreateDatabase { optional string db_name = 4; } message AlterDatabase { optional string db_name = 14; message SetOptions { repeated SetOption options = 1; } oneof alter_type { SetOptions set_options = 5; } } message CreateProtoBundle { repeated ProtoType insert_type = 1; } // A single ALTER PROTO BUNDLE statement can have multiple operations linked to // it. E.g.: // ALTER PROTO BUNDLE INSERT ( // package1.Proto1, // package1.Enum1, // ) UPDATE ( // package1.Proto2, // package1.Enum2, // ) DELETE ( // package1.Proto3, // package1.Enum3, // ); message AlterProtoBundle { repeated ProtoType insert_type = 1; repeated ProtoType update_type = 2; repeated string delete_type = 3; } message DropProtoBundle { // Nothing! } message RowDeletionPolicy { // The timestamp column based on which we set the policy. optional string column_name = 1; // If now - 'column_name' exceeds 'older_than', the row will be deleted. optional DDLTimeLengthProto older_than = 2; // Scan data and delete rows every 'interval'. // Note that it is possible that we could skip intervals. optional DDLTimeLengthProto interval = 3; } enum ExistenceModifier { // Creation/deletion fails if the thing to be created already exists. NONE = 0; // Creation is skipped without error if the thing to be created exists // already. IF_NOT_EXISTS = 1; // For CREATE OR REPLACE statements, if the thing being created already exists // then it will be deleted first, then recreated. OR_REPLACE = 2; // Deletion is skippped without error if the thing to be dropped doesn't // exist. IF_EXISTS = 3; } message CreateTable { optional string table_name = 1; repeated ColumnDefinition column = 2; repeated KeyPartClause primary_key = 3; repeated ForeignKey foreign_key = 10; repeated CheckConstraint check_constraint = 11; optional InterleaveClause interleave_clause = 7; repeated SetOption set_options = 8; optional RowDeletionPolicy row_deletion_policy = 9; // How to handle creation if the table already exists. optional ExistenceModifier existence_modifier = 13; optional string synonym = 15; } message ChangeStreamForClause { message TrackedTables { message Entry { // Name of the tracked table. optional string table_name = 1; message TrackedColumns { // Name of the tracked columns. Primary key columns are tracked by // default and are not listed here. If `column_name` is empty, only the // primary key columns of the table are tracked. repeated string column_name = 1; } oneof track_type { // If set, value must be true, and all columns of the table are tracked // implicitly. bool all_columns = 2; // If exists, only columns listed in `tracked_columns` and the primary // key columns of the table are tracked. TrackedColumns tracked_columns = 3; } } // Tracked tables and information about which of their columns are tracked. repeated Entry table_entry = 1; } oneof for_clause_type { // If set, the value must be true, signifying all trackable database objects // are tracked implicitly. Non-trackable database objects include: // * internal system tables/queues // * queues // * indexes // * views / materialized views // * stored or non-stored generated columns (except for STORED VOLATILE // columns) bool all = 1; // If exists, only explicitly listed tables are tracked. TrackedTables tracked_tables = 2; } } message CreateChangeStream { optional string change_stream_name = 1; optional ChangeStreamForClause for_clause = 2; repeated SetOption set_options = 3; } message CreateSequence { optional string sequence_name = 1; enum Type { reserved 0, 1; BIT_REVERSED_POSITIVE = 2; } optional Type type = 2; repeated SetOption set_options = 9; optional ExistenceModifier existence_modifier = 10; // A value at which the internal sequence counter starts. optional int64 start_with_counter = 11; // An inclusive range whose values the sequence will skip over. optional int64 skip_range_min = 12; optional int64 skip_range_max = 13; } message CreateSchema { optional string schema_name = 1; enum ExistenceModifier { // Create will fail if schema pre-exists. NONE = 0; // Both will pass the create if the schema pre-exists. IF_NOT_EXISTS will // not replace options, while OR_REPLACE will replace options if this is // the case. IF_NOT_EXISTS = 1; OR_REPLACE = 2; } optional ExistenceModifier existence_modifier = 2; repeated SetOption set_options = 3; } message SetColumnOptions { message ColumnPath { optional string table_name = 1; optional string column_name = 2; } repeated ColumnPath column_path = 1; repeated SetOption options = 3; } message DropTable { optional string table_name = 1; optional ExistenceModifier existence_modifier = 2; } message DropChangeStream { optional string change_stream_name = 1; } message AlterTable { optional string table_name = 1; message RenameTo { optional string name = 1; optional string synonym = 2; } message AddColumn { optional ColumnDefinition column = 1; // How to handle the ADD COLUMN if the column is already part of the table. optional ExistenceModifier existence_modifier = 2; } message AlterColumn { optional ColumnDefinition column = 1; // Column name given here. enum AlterColumnOp { SET_DEFAULT = 1; DROP_DEFAULT = 2; SET_NOT_NULL = 3; DROP_NOT_NULL = 4; ALTER_IDENTITY = 5; } // Defines the specific alter operation on the column. optional AlterColumnOp operation = 2; // True, if start_with_counter is altered. optional bool identity_alter_start_with_counter = 3; // True, if skip_range_min and skip_range_max is altered. optional bool identity_alter_skip_range = 4; } message SetOptions { repeated SetOption options = 1; } message SetInterleaveClause { optional InterleaveClause interleave_clause = 1; } message SetOnDelete { optional InterleaveClause.Action action = 1; } message DropRowDeletionPolicy {} message DropConstraint { optional string name = 1; } message AddForeignKey { optional ForeignKey foreign_key = 1; } message AddCheckConstraint { optional CheckConstraint check_constraint = 1; } message AddSynonym { optional string synonym = 1; } message DropSynonym { optional string synonym = 1; } oneof alter_type { RenameTo rename_to = 2; AddColumn add_column = 3; string drop_column = 4; AlterColumn alter_column = 5; SetOptions set_options = 7; SetInterleaveClause set_interleave_clause = 18; SetOnDelete set_on_delete = 10; RowDeletionPolicy add_row_deletion_policy = 11; RowDeletionPolicy alter_row_deletion_policy = 12; DropRowDeletionPolicy drop_row_deletion_policy = 13; DropConstraint drop_constraint = 15; AddForeignKey add_foreign_key = 16; AddCheckConstraint add_check_constraint = 17; AddSynonym add_synonym = 22; DropSynonym drop_synonym = 23; } } message RenameTable { message RenameOp { optional string from_name = 1; optional string to_name = 2; } repeated RenameOp rename_op = 1; } message AlterChangeStream { optional string change_stream_name = 1; message SetOptions { repeated SetOption options = 1; } oneof alter_type { ChangeStreamForClause set_for_clause = 2; SetOptions set_options = 3; // Only DROP FOR ALL is supported for now. ChangeStreamForClause drop_for_clause = 4; } } message AlterSequence { optional string sequence_name = 1; message SetOptions { repeated SetOption options = 1; } message SetStartWithCounter { optional int64 counter_value = 1 [default = 1]; } message SetSkipRange { optional int64 min_value = 1; optional int64 max_value = 2; } oneof alter_type { SetOptions set_options = 3; SetStartWithCounter set_start_with_counter = 9; SetSkipRange set_skip_range = 10; } optional ExistenceModifier existence_modifier = 8; } message DropSequence { optional string sequence_name = 1; optional ExistenceModifier existence_modifier = 2; } message CreateModel { optional string model_name = 1; optional ExistenceModifier existence_modifier = 2; repeated SetOption set_options = 3; optional bool remote = 4; repeated ColumnDefinition input = 5; repeated ColumnDefinition output = 6; } message AlterModel { optional string model_name = 1; optional bool if_exists = 2; message SetOptions { repeated SetOption options = 1; } oneof alter_type { SetOptions set_options = 3; } } message DropModel { optional string model_name = 1; optional bool if_exists = 2; } message AlterSchema { optional string schema_name = 1; optional bool if_exists = 2; repeated SetOption set_options = 3; } message DropSchema { optional string schema_name = 1; optional bool if_exists = 2; } message CreateLocalityGroup { optional string locality_group_name = 1; repeated SetOption set_options = 2; optional ExistenceModifier existence_modifier = 3; } message AlterLocalityGroup { optional string locality_group_name = 1; message SetOptions { repeated SetOption options = 1; } optional SetOptions set_options = 2; optional ExistenceModifier existence_modifier = 3; } message DropLocalityGroup { optional string locality_group_name = 1; optional ExistenceModifier existence_modifier = 2; } // ANALYZE is a special DDL operation that doesn't trigger a schema change in // emulator. The emulator will accept it but perform no-op. This allows the same // script to run in both emulator and production. message Analyze { // Intentionally empty. } // FGAC protos. For now, these are parsed by the DDL parser but do not // actually impact behavior of the emulator. message CreateRole { optional string role_name = 1; } message DropRole { optional string role_name = 1; } message Grantee { enum Type { ROLE = 0; } optional Type type = 1; optional string name = 2; } message Privilege { enum Type { SELECT = 0; INSERT = 1; UPDATE = 2; DELETE = 3; EXECUTE = 4; USAGE = 5; } optional Type type = 1; // TODO: Add support for column-level FGAC // repeated string column = 2; } message PrivilegeTarget { enum Type { TABLE = 0; CHANGE_STREAM = 1; VIEW = 2; FUNCTION = 3; TABLE_FUNCTION = 4; SEQUENCE = 5; ROUTINE = 6; MODEL = 7; SCHEMA = 8; } optional Type type = 1; repeated string name = 2; // Empty means all objects. } message GrantPrivilege { repeated Privilege privilege = 1; optional PrivilegeTarget target = 2; repeated Grantee grantee = 3; } message RevokePrivilege { repeated Privilege privilege = 1; optional PrivilegeTarget target = 2; repeated Grantee grantee = 3; } message GrantMembership { repeated Grantee role = 1; repeated Grantee grantee = 2; } message RevokeMembership { repeated Grantee role = 1; repeated Grantee grantee = 2; } message StoredColumnDefinition { optional string name = 1; } message CreateIndex { optional string index_name = 1; optional string index_base_name = 2; repeated KeyPartClause key = 3; optional bool null_filtered = 5; // Not present for standard "global" indexes. optional string interleave_in_table = 6; repeated SetOption set_options = 8; optional bool unique = 10; // List of columns stored in the index. repeated StoredColumnDefinition stored_column_definition = 15; // Determines what to do if the index already exists. Defaults to an error. optional ExistenceModifier existence_modifier = 17; // Columns that are being null filtered using the WHERE syntax. repeated string null_filtered_column = 14; } message AlterIndex { optional string index_name = 1; message SetOptions { repeated SetOption options = 1; } message AddStoredColumn { optional string column_name = 1; } oneof alter_type { SetOptions set_options = 2; AddStoredColumn add_stored_column = 3; string drop_stored_column = 4; } } message DropIndex { optional string index_name = 1; optional ExistenceModifier existence_modifier = 2; } message TokenColumnDefinition { // Required. optional KeyPartClause token_column = 1; repeated SetOption set_options = 2; } message CreateSearchIndex { // Required. optional string index_name = 1; // Required. optional string index_base_name = 2; // Each column here corresponds to one token index per secondary index. // They should exist in the base table already. repeated TokenColumnDefinition token_column_definition = 3; repeated KeyPartClause partition_by = 4; // Normally there will be at most a single order_by column listed here, but // if the user provides their own UID column, there could be up to two. repeated KeyPartClause order_by = 5; repeated string null_filtered_column = 6; optional string interleave_in_table = 7; repeated StoredColumnDefinition stored_column_definition = 8; repeated SetOption set_options = 9; optional bool null_filtered = 10; } message DropSearchIndex { // Required. optional string index_name = 1; optional ExistenceModifier existence_modifier = 2; } message CreateVectorIndex { // Required. optional string index_name = 1; // Required. optional string index_base_name = 2; // Required. optional KeyPartClause key = 3; repeated StoredColumnDefinition stored_column_definition = 4; repeated KeyPartClause partition_by = 5; repeated SetOption set_options = 6; // Columns that are being null filtered using the WHERE IS NOT NULL syntax. repeated string null_filtered_column = 7; // Handle creation if the vector index already exists. optional ExistenceModifier existence_modifier = 8; } message VectorIndexOptionsProto { // The user defined spec of the search tree. optional int64 tree_depth = 2 [default = 2]; optional int64 num_leaves = 3; optional int64 num_branches = 4; // Controls how leaf clusters are distributed across the index. // Valid values are [0, 32]. // 0 - leaves are grouped by their branch. // 32 - leaves are randomly distributed. optional int64 leaf_scatter_factor = 6; // The minimum number of branch table splits. optional int64 min_branch_splits = 7; // The minimum number of leaf index splits. optional int64 min_leaf_splits = 8; enum DistanceType { DISTANCE_TYPE_UNSPECIFIED = 0; COSINE = 1; EUCLIDEAN = 2; DOT_PRODUCT = 3; } // The distance metric used to compare vectors. // Valid values correspond to the `DistanceType` enum except for unspecified. optional string distance_type = 5; // The locality group in which the index should be stored. optional string locality_group = 9; } message DropVectorIndex { // Required. optional string index_name = 1; optional ExistenceModifier existence_modifier = 2; } message AlterVectorIndex { // Required. optional string index_name = 1; message AddStoredColumn { // Required. optional StoredColumnDefinition column = 1; } message AlterStoredColumn { // Required. optional StoredColumnDefinition column = 1; } message SetOptions { repeated SetOption options = 1; } oneof alter_type { AddStoredColumn add_stored_column = 2; string drop_stored_column = 3; AlterStoredColumn alter_stored_column = 4; SetOptions set_options = 5; } } message DDLTimeLengthProto { enum Unit { DAYS = 86400; // 24 HOURS } optional int64 count = 1; optional Unit unit = 2; } // Operation definition for "CREATE PROPERTY GRAPH". message CreatePropertyGraph { optional string name = 1; optional ExistenceModifier existence_modifier = 2; optional string ddl_body = 3; } // Operation definition for "DROP PROPERTY GRAPH". message DropPropertyGraph { optional string name = 1; optional ExistenceModifier existence_modifier = 2; } message CreatePlacement { // Required. optional string placement_name = 1; optional ExistenceModifier existence_modifier = 2; repeated SetOption set_options = 3; } message AlterPlacement { // Required. optional string placement_name = 1; optional ExistenceModifier existence_modifier = 2; repeated SetOption set_options = 3; } message DropPlacement { // Required. optional string placement_name = 1; optional ExistenceModifier existence_modifier = 2; }