Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Schema change plan for CREATE TRIGGER tr BEFORE INSERT OR UPDATE OR DELETE ON
│ ├── SetTriggerEnabled {"Enabled":true,"TableID":104,"TriggerID":1}
│ ├── SetTriggerTiming {"Timing":{"ActionTime":1,"ForEachRow":true,"TableID":104,"TriggerID":1}}
│ ├── SetTriggerEvents {"Events":{"TableID":104,"TriggerID":1}}
│ ├── SetTriggerFunctionCall {"FunctionCall":{"FuncBody":"BEGIN\nRAISE NOTI...","FuncID":105,"TableID":104,"TriggerID":1}}
│ ├── SetTriggerFunctionCall {"FunctionCall":{"CanMutate":2,"FuncBody":"BEGIN\nRAISE NOTI...","FuncID":105,"TableID":104,"TriggerID":1}}
│ ├── SetTriggerForwardReferences {"Deps":{"TableID":104,"TriggerID":1}}
│ ├── UpdateTriggerBackReferencesInRelations {"TableID":104,"TriggerID":1}
│ └── AddTriggerBackReferencesInRoutines {"BackReferencedTableID":104,"BackReferencedTriggerID":1}
Expand Down Expand Up @@ -66,7 +66,7 @@ Schema change plan for CREATE TRIGGER tr BEFORE INSERT OR UPDATE OR DELETE ON
│ ├── SetTriggerEnabled {"Enabled":true,"TableID":104,"TriggerID":1}
│ ├── SetTriggerTiming {"Timing":{"ActionTime":1,"ForEachRow":true,"TableID":104,"TriggerID":1}}
│ ├── SetTriggerEvents {"Events":{"TableID":104,"TriggerID":1}}
│ ├── SetTriggerFunctionCall {"FunctionCall":{"FuncBody":"BEGIN\nRAISE NOTI...","FuncID":105,"TableID":104,"TriggerID":1}}
│ ├── SetTriggerFunctionCall {"FunctionCall":{"CanMutate":2,"FuncBody":"BEGIN\nRAISE NOTI...","FuncID":105,"TableID":104,"TriggerID":1}}
│ ├── SetTriggerForwardReferences {"Deps":{"TableID":104,"TriggerID":1}}
│ ├── UpdateTriggerBackReferencesInRelations {"TableID":104,"TriggerID":1}
│ ├── AddTriggerBackReferencesInRoutines {"BackReferencedTableID":104,"BackReferencedTriggerID":1}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ upsert descriptor #104
- schemaLocked: true
+ triggers:
+ - actionTime: BEFORE
+ canMutate: CANNOT_MUTATE
+ dependsOn:
+ - 104
+ dependsOnRoutines:
Expand Down Expand Up @@ -141,6 +142,7 @@ upsert descriptor #104
- schemaLocked: true
+ triggers:
+ - actionTime: BEFORE
+ canMutate: CANNOT_MUTATE
+ dependsOn:
+ - 104
+ dependsOnRoutines:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ upsert descriptor #104
- schemaLocked: true
- triggers:
- - actionTime: BEFORE
- canMutate: CANNOT_MUTATE
- dependsOn:
- - 104
- dependsOnRoutines:
Expand Down Expand Up @@ -112,6 +113,7 @@ upsert descriptor #104
- schemaLocked: true
- triggers:
- - actionTime: BEFORE
- canMutate: CANNOT_MUTATE
- dependsOn:
- - 104
- dependsOnRoutines:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ upsert descriptor #104
schemaLocked: true
- triggers:
- - actionTime: BEFORE
- canMutate: CANNOT_MUTATE
- dependsOn:
- - 104
- dependsOnRoutines:
Expand Down Expand Up @@ -90,6 +91,7 @@ upsert descriptor #104
schemaLocked: true
- triggers:
- - actionTime: BEFORE
- canMutate: CANNOT_MUTATE
- dependsOn:
- - 104
- dependsOnRoutines:
Expand Down
6 changes: 6 additions & 0 deletions pkg/sql/catalog/catpb/function.proto
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ message Function {
INVOKER = 0;
DEFINER = 1;
}

enum CanMutate {
UNKNOWN_CAN_MUTATE = 0;
CAN_MUTATE = 1;
CANNOT_MUTATE = 2;
}
}

// These wrappers are for the convenience of referencing the enum types from a
Expand Down
35 changes: 34 additions & 1 deletion pkg/sql/catalog/descpb/structured.proto
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,24 @@ message TriggerDescriptor {
// DependsOnRoutines are the IDs of the user-defined routines that this
// trigger depends on.
repeated uint32 depends_on_routines = 15 [(gogoproto.casttype) = "ID"];

// CanMutate indicates whether the trigger function body can perform
// mutations. This includes direct DML statements (INSERT, UPDATE, DELETE,
// UPSERT), mutations inside CTEs or subqueries, and calls to other routines
// that themselves can mutate. It is computed at CREATE TRIGGER time by
// inspecting the CanMutate logical property of the built trigger function
// body, and persisted so it is available without rebuilding the body.
//
// Unlike a trigger function's own descriptor, the trigger descriptor has the
// table binding required to build the body, so the value is meaningful here.
// See the FunctionDescriptor.can_mutate field for the analogous field on
// function descriptors.
//
// UNKNOWN_CAN_MUTATE (the zero value) indicates that the mutation status has
// not been determined. This is the case for trigger descriptors created
// before this field was introduced. When unknown, consumers fall back to
// deriving the value from the built trigger function body.
optional cockroach.sql.catalog.catpb.Function.CanMutate can_mutate = 16 [(gogoproto.nullable) = false];
}

// ConstraintToUpdate represents a constraint to be added to the table and
Expand Down Expand Up @@ -2039,7 +2057,22 @@ message FunctionDescriptor {
optional uint32 replicated_pcr_version = 24 [(gogoproto.nullable) = false,
(gogoproto.customname) = "ReplicatedPCRVersion", (gogoproto.casttype) = "DescriptorVersion"];

// Next field id is 25
// CanMutate indicates whether the routine body can perform mutations.
// This includes direct DML statements (INSERT, UPDATE, DELETE, UPSERT),
// mutations inside CTEs or subqueries, and calls to other routines that
// themselves can mutate. It is computed at CREATE FUNCTION time by
// inspecting the CanMutate logical property of the built body
// statements, and persisted so that it is available at execution time
// without needing to build the routine body.
//
// UNKNOWN_CAN_MUTATE (the zero value) indicates that the mutation status
// has not been determined. This is the case for function descriptors
// created before this field was introduced. When unknown, consumers must
// fall back to inspecting the body RelExprs to determine whether the
// routine can mutate.
optional cockroach.sql.catalog.catpb.Function.CanMutate can_mutate = 25 [(gogoproto.nullable) = false];

// Next field id is 26
}

// Descriptor is a union type for descriptors for tables, schemas, databases,
Expand Down
3 changes: 3 additions & 0 deletions pkg/sql/catalog/descriptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -1177,6 +1177,9 @@ type FunctionDescriptor interface {

// GetSecurity returns the security specification of this function.
GetSecurity() catpb.Function_Security

// GetCanMutate returns the CanMutate field of this function descriptor.
GetCanMutate() catpb.Function_CanMutate
}

// FilterDroppedDescriptor returns an error if the descriptor state is DROP.
Expand Down
36 changes: 36 additions & 0 deletions pkg/sql/catalog/funcdesc/func_desc.go
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,11 @@ func (desc *Mutable) SetSecurity(v catpb.Function_Security) {
desc.Security = v
}

// SetCanMutate sets the CanMutate field on the function descriptor.
func (desc *Mutable) SetCanMutate(v catpb.Function_CanMutate) {
desc.CanMutate = v
}

// SetName sets the function name.
func (desc *Mutable) SetName(n string) {
desc.Name = n
Expand Down Expand Up @@ -1027,6 +1032,11 @@ func (desc *immutable) GetSecurity() catpb.Function_Security {
return desc.Security
}

// GetCanMutate implements the FunctionDescriptor interface.
func (desc *immutable) GetCanMutate() catpb.Function_CanMutate {
return desc.CanMutate
}

func (desc *immutable) ToOverload() (ret *tree.Overload, err error) {
routineType := tree.UDFRoutine
if desc.IsProcedure() {
Expand Down Expand Up @@ -1075,10 +1085,36 @@ func (desc *immutable) ToOverload() (ret *tree.Overload, err error) {
ret.Class = tree.GeneratorClass
}
ret.SecurityMode = desc.getCreateExprSecurity()
ret.CanMutate = canMutateToOverload(desc.FunctionDescriptor.CanMutate)

return ret, nil
}

// canMutateToOverload converts the proto CanMutate enum to the tree type.
func canMutateToOverload(cm catpb.Function_CanMutate) tree.RoutineCanMutate {
switch cm {
case catpb.Function_CAN_MUTATE:
return tree.RoutineMutates
case catpb.Function_CANNOT_MUTATE:
return tree.RoutineDoesNotMutate
default:
return tree.RoutineCanMutateUnknown
}
}

// CanMutateToProto converts a RoutineCanMutate enum to the proto enum for
// persistence on the function descriptor.
func CanMutateToProto(cm tree.RoutineCanMutate) catpb.Function_CanMutate {
switch cm {
case tree.RoutineMutates:
return catpb.Function_CAN_MUTATE
case tree.RoutineDoesNotMutate:
return catpb.Function_CANNOT_MUTATE
default:
return catpb.Function_UNKNOWN_CAN_MUTATE
}
}

func (desc *immutable) getOverloadVolatility() (volatility.V, error) {
var ret volatility.V
switch desc.Volatility {
Expand Down
2 changes: 2 additions & 0 deletions pkg/sql/catalog/tabledesc/validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@ var validationMap = []struct {
"IsProcedure": {status: thisFieldReferencesNoObjects},
"Security": {status: thisFieldReferencesNoObjects},
"ReplicatedPCRVersion": {status: thisFieldReferencesNoObjects},
"CanMutate": {status: thisFieldReferencesNoObjects},
},
},
{
Expand All @@ -370,6 +371,7 @@ var validationMap = []struct {
"DependsOn": {status: iSolemnlySwearThisFieldIsValidated},
"DependsOnTypes": {status: iSolemnlySwearThisFieldIsValidated},
"DependsOnRoutines": {status: iSolemnlySwearThisFieldIsValidated},
"CanMutate": {status: thisFieldReferencesNoObjects},
},
},
}
Expand Down
Loading
Loading