Skip to content

Commit 358fec6

Browse files
craig[bot]DrewKimball
andcommitted
Merge #143827
143827: sql: disallow creating triggers which use TG_ARGV r=yuzefovich a=DrewKimball The `TG_ARGV` trigger function argument is 0-indexed in Postgres. Since we currently don't have a way to replicate this behvaior, this commit disallows usage of `TG_ARGV` for now. The session setting `allow_create_trigger_function_with_argv_references` can be set to true to allow usage (with 1-based indexing). Informs #135311 Release note (sql change): Usage of `TG_ARGV` in trigger functions is now disallowed by default. Co-authored-by: Drew Kimball <[email protected]>
2 parents 04aaac4 + 1947241 commit 358fec6

File tree

14 files changed

+130
-13
lines changed

14 files changed

+130
-13
lines changed

pkg/ccl/logictestccl/testdata/logic_test/triggers

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4086,6 +4086,10 @@ DROP FUNCTION update_listing_balance ;
40864086
statement ok
40874087
drop table listings_balance cascade;
40884088

4089+
# ==============================================================================
4090+
# Regression tests.
4091+
# ==============================================================================
4092+
40894093
# Test that CREATE and DROP TRIGGER work when sent in a statement batch.
40904094

40914095
subtest create_drop_in_batch_statement
@@ -4150,3 +4154,53 @@ SELECT id, a, b FROM tab_141810 ORDER BY id
41504154
4 NULL NULL
41514155

41524156
subtest end
4157+
4158+
# Until #135311 can be fixed, disallow TG_ARGV references by default. There is
4159+
# a setting to allow them if necessary.
4160+
subtest regression_135311
4161+
4162+
statement ok
4163+
DROP FUNCTION IF EXISTS g;
4164+
4165+
statement ok
4166+
CREATE FUNCTION g() RETURNS TRIGGER LANGUAGE PLpgSQL AS $$
4167+
BEGIN
4168+
RAISE NOTICE 'g()';
4169+
RAISE NOTICE 'TG_ARGV[1]: %', TG_ARGV[1];
4170+
RETURN NEW;
4171+
END
4172+
$$;
4173+
4174+
statement error pgcode 0A000 pq: unimplemented: referencing the TG_ARGV trigger function parameter is not yet supported
4175+
CREATE TRIGGER foo BEFORE INSERT ON xy FOR EACH ROW EXECUTE FUNCTION g('foo', 'bar');
4176+
4177+
statement ok
4178+
SET allow_create_trigger_function_with_argv_references = true;
4179+
4180+
statement ok
4181+
CREATE TRIGGER foo BEFORE INSERT ON xy FOR EACH ROW EXECUTE FUNCTION g('foo', 'bar');
4182+
4183+
query T noticetrace
4184+
INSERT INTO xy VALUES (1, 2);
4185+
----
4186+
NOTICE: g()
4187+
NOTICE: TG_ARGV[1]: foo
4188+
4189+
statement ok
4190+
RESET allow_create_trigger_function_with_argv_references;
4191+
4192+
# The setting only disallows creating the trigger - not executing it if it was
4193+
# already created.
4194+
query T noticetrace
4195+
INSERT INTO xy VALUES (3, 4);
4196+
----
4197+
NOTICE: g()
4198+
NOTICE: TG_ARGV[1]: foo
4199+
4200+
statement ok
4201+
DROP TRIGGER foo ON xy;
4202+
4203+
statement ok
4204+
DROP FUNCTION g;
4205+
4206+
subtest end

pkg/sql/exec_util.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4119,6 +4119,10 @@ func (m *sessionDataMutator) SetOptimizerUseDeleteRangeFastPath(val bool) {
41194119
m.data.OptimizerUseDeleteRangeFastPath = val
41204120
}
41214121

4122+
func (m *sessionDataMutator) SetAllowCreateTriggerFunctionWithArgvReferences(val bool) {
4123+
m.data.AllowCreateTriggerFunctionWithArgvReferences = val
4124+
}
4125+
41224126
// Utility functions related to scrubbing sensitive information on SQL Stats.
41234127

41244128
// quantizeCounts ensures that the Count field in the

pkg/sql/logictest/testdata/logic_test/information_schema

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3902,6 +3902,7 @@ WHERE
39023902
ORDER BY variable
39033903
----
39043904
variable value
3905+
allow_create_trigger_function_with_argv_references off
39053906
allow_ordinal_column_references off
39063907
allow_role_memberships_to_change_during_transaction off
39073908
alter_primary_region_super_region_override off

pkg/sql/logictest/testdata/logic_test/pg_catalog

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2911,6 +2911,7 @@ WHERE
29112911
ORDER BY name
29122912
----
29132913
name setting category short_desc extra_desc vartype
2914+
allow_create_trigger_function_with_argv_references off NULL NULL NULL string
29142915
allow_ordinal_column_references off NULL NULL NULL string
29152916
allow_role_memberships_to_change_during_transaction off NULL NULL NULL string
29162917
alter_primary_region_super_region_override off NULL NULL NULL string
@@ -3127,6 +3128,7 @@ WHERE
31273128
ORDER BY name
31283129
----
31293130
name setting unit context enumvals boot_val reset_val
3131+
allow_create_trigger_function_with_argv_references off NULL user NULL off off
31303132
allow_ordinal_column_references off NULL user NULL off off
31313133
allow_role_memberships_to_change_during_transaction off NULL user NULL off off
31323134
alter_primary_region_super_region_override off NULL user NULL off off
@@ -3336,6 +3338,7 @@ query TTTTTT colnames,rowsort
33363338
SELECT name, source, min_val, max_val, sourcefile, sourceline FROM pg_catalog.pg_settings
33373339
----
33383340
name source min_val max_val sourcefile sourceline
3341+
allow_create_trigger_function_with_argv_references NULL NULL NULL NULL NULL
33393342
allow_ordinal_column_references NULL NULL NULL NULL NULL
33403343
allow_role_memberships_to_change_during_transaction NULL NULL NULL NULL NULL
33413344
alter_primary_region_super_region_override NULL NULL NULL NULL NULL

pkg/sql/logictest/testdata/logic_test/show_source

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ WHERE variable NOT IN ('optimizer', 'crdb_version', 'session_id', 'distsql_workm
2727
ORDER BY variable
2828
----
2929
variable value
30+
allow_create_trigger_function_with_argv_references off
3031
allow_ordinal_column_references off
3132
allow_role_memberships_to_change_during_transaction off
3233
alter_primary_region_super_region_override off

pkg/sql/opt/optbuilder/create_function.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,7 @@ func (b *Builder) buildCreateFunction(cf *tree.CreateRoutine, inScope *scope) (o
428428

429429
// Special handling for trigger functions.
430430
buildSQL := true
431+
isTriggerFn := false
431432
if funcReturnType.Identical(types.Trigger) {
432433
// Trigger functions cannot have user-defined parameters. However, they do
433434
// have a set of implicitly defined parameters.
@@ -449,15 +450,16 @@ func (b *Builder) buildCreateFunction(cf *tree.CreateRoutine, inScope *scope) (o
449450
// Analysis of SQL expressions for trigger functions must be deferred
450451
// until the function is bound to a trigger.
451452
buildSQL = false
453+
isTriggerFn = true
452454
}
453455

454456
// We need to disable stable function folding because we want to catch the
455457
// volatility of stable functions. If folded, we only get a scalar and lose
456458
// the volatility.
457459
b.factory.FoldingControl().TemporarilyDisallowStableFolds(func() {
458460
plBuilder := newPLpgSQLBuilder(
459-
b, cf.Name.Object(), stmt.AST.Label, nil /* colRefs */, routineParams,
460-
funcReturnType, cf.IsProcedure, false /* isDoBlock */, buildSQL, nil, /* outScope */
461+
b, cf.Name.Object(), stmt.AST.Label, nil /* colRefs */, routineParams, funcReturnType,
462+
cf.IsProcedure, false /* isDoBlock */, isTriggerFn, buildSQL, nil, /* outScope */
461463
)
462464
stmtScope = plBuilder.buildRootBlock(stmt.AST, bodyScope, routineParams)
463465
})

pkg/sql/opt/optbuilder/create_trigger.go

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -216,15 +216,20 @@ func (b *Builder) buildFunctionForTrigger(
216216
// The trigger function takes a set of implicitly-defined parameters, two of
217217
// which are determined by the table's record type. Add them to the trigger
218218
// function scope.
219-
numStaticParams := len(triggerFuncStaticParams)
220-
triggerFuncParams := make([]routineParam, numStaticParams, numStaticParams+2)
221-
copy(triggerFuncParams, triggerFuncStaticParams)
219+
triggerFuncParams := make([]routineParam, 0, len(triggerFuncStaticParams)+2)
222220
triggerFuncParams = append(triggerFuncParams, routineParam{name: triggerColNew, typ: tableTyp})
223221
triggerFuncParams = append(triggerFuncParams, routineParam{name: triggerColOld, typ: tableTyp})
222+
triggerFuncParams = append(triggerFuncParams, triggerFuncStaticParams...)
224223
for i, param := range triggerFuncParams {
225224
paramColName := funcParamColName(param.name, i)
226225
col := b.synthesizeColumn(funcScope, paramColName, param.typ, nil /* expr */, nil /* scalar */)
227226
col.setParamOrd(i)
227+
if i == triggerArgvColIdx {
228+
// Due to #135311, we disallow references to the TG_ARGV param for now.
229+
if !b.evalCtx.SessionData().AllowCreateTriggerFunctionWithArgvReferences {
230+
col.resolveErr = unimplementedArgvErr
231+
}
232+
}
228233
}
229234

230235
// Now that the transition relations and table type are known, fully build and
@@ -240,7 +245,8 @@ func (b *Builder) buildFunctionForTrigger(
240245
b.factory.FoldingControl().TemporarilyDisallowStableFolds(func() {
241246
plBuilder := newPLpgSQLBuilder(
242247
b, ct.FuncName.String(), stmt.AST.Label, nil /* colRefs */, triggerFuncParams, tableTyp,
243-
false /* isProcedure */, false /* isDoBlock */, true /* buildSQL */, nil, /* outScope */
248+
false /* isProcedure */, false, /* isDoBlock */
249+
true /* isTriggerFn */, true /* buildSQL */, nil, /* outScope */
244250
)
245251
funcScope = plBuilder.buildRootBlock(stmt.AST, funcScope, triggerFuncParams)
246252
})
@@ -330,6 +336,10 @@ var triggerFuncStaticParams = []routineParam{
330336
{name: "tg_argv", typ: types.StringArray, class: tree.RoutineParamIn},
331337
}
332338

339+
// The trigger function parameters consist of OLD and NEW, followed by the
340+
// static parameters. The TG_ARGV parameter is last.
341+
var triggerArgvColIdx = len(triggerFuncStaticParams) + 1
342+
333343
const triggerColNew = "new"
334344
const triggerColOld = "old"
335345

@@ -369,4 +379,6 @@ var (
369379
"column lists are not yet supported for triggers")
370380
unimplementedViewTriggerErr = unimplemented.NewWithIssue(135658,
371381
"triggers on views are not yet supported")
382+
unimplementedArgvErr = unimplemented.NewWithIssue(135311,
383+
"referencing the TG_ARGV trigger function parameter is not yet supported")
372384
)

pkg/sql/opt/optbuilder/plpgsql.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ type plpgsqlBuilder struct {
192192
routineName string
193193
isProcedure bool
194194
isDoBlock bool
195+
isTriggerFn bool
195196
buildSQL bool
196197
identCounter int
197198
}
@@ -209,7 +210,7 @@ func newPLpgSQLBuilder(
209210
colRefs *opt.ColSet,
210211
routineParams []routineParam,
211212
returnType *types.T,
212-
isProcedure, isDoBlock, buildSQL bool,
213+
isProcedure, isDoBlock, isTriggerFn, buildSQL bool,
213214
outScope *scope,
214215
) *plpgsqlBuilder {
215216
const initialBlocksCap = 2
@@ -221,6 +222,7 @@ func newPLpgSQLBuilder(
221222
routineName: routineName,
222223
isProcedure: isProcedure,
223224
isDoBlock: isDoBlock,
225+
isTriggerFn: isTriggerFn,
224226
buildSQL: buildSQL,
225227
outScope: outScope,
226228
}
@@ -2017,7 +2019,14 @@ func (b *plpgsqlBuilder) makeContinuation(conName string) continuation {
20172019
col := b.ob.synthesizeColumn(s, name, typ, nil /* expr */, nil /* scalar */)
20182020
// TODO(mgartner): Lift the 100 parameter restriction for synthesized
20192021
// continuation UDFs.
2022+
paramOrd := len(params)
20202023
col.setParamOrd(len(params))
2024+
if b.ob.insideFuncDef && b.isTriggerFn && paramOrd == triggerArgvColIdx {
2025+
// Due to #135311, we disallow references to the TG_ARGV param for now.
2026+
if !b.ob.evalCtx.SessionData().AllowCreateTriggerFunctionWithArgvReferences {
2027+
col.resolveErr = unimplementedArgvErr
2028+
}
2029+
}
20212030
params = append(params, col.id)
20222031
}
20232032
// Invariant: the variables of a child block always follow those of a parent
@@ -2166,7 +2175,9 @@ func (b *plpgsqlBuilder) makeContinuationArgs(con *continuation, s *scope) memo.
21662175
block := &b.blocks[i]
21672176
for _, name := range block.vars {
21682177
_, source, _, err := s.FindSourceProvidingColumn(b.ob.ctx, name)
2169-
if err != nil {
2178+
if err != nil && !errors.Is(err, unimplementedArgvErr) {
2179+
// Swallow unimplementedArgvErr, since it's ok to reference the TG_ARGV
2180+
// parameter when calling a continuation.
21702181
panic(err)
21712182
}
21722183
args = append(args, b.ob.factory.ConstructVariable(source.(*scopeColumn).id))

pkg/sql/opt/optbuilder/routine.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,7 @@ func (b *Builder) buildRoutine(
447447
var physProps *physical.Required
448448
plBuilder := newPLpgSQLBuilder(
449449
b, def.Name, stmt.AST.Label, colRefs, routineParams, f.ResolvedType(),
450-
isProc, false /* isDoBlock */, true /* buildSQL */, outScope,
450+
isProc, false /* isDoBlock */, false /* isTriggerFn */, true /* buildSQL */, outScope,
451451
)
452452
stmtScope := plBuilder.buildRootBlock(stmt.AST, bodyScope, routineParams)
453453
rTyp := b.finalizeRoutineReturnType(f, stmtScope, inScope, oldInsideDataSource)
@@ -859,7 +859,8 @@ func (b *Builder) buildPLpgSQLDoBody(
859859
// Build an expression for each statement in the function body.
860860
plBuilder := newPLpgSQLBuilder(
861861
b, doBlockRoutineName, do.Block.Label, nil /* colRefs */, nil /* routineParams */, types.Void,
862-
true /* isProc */, true /* isDoBlock */, true /* buildSQL */, nil, /* outScope */
862+
true /* isProc */, true, /* isDoBlock */
863+
false /* isTriggerFn */, true /* buildSQL */, nil, /* outScope */
863864
)
864865
// Allocate a fresh scope, since DO blocks do not take parameters or reference
865866
// variables or columns from the calling context.

pkg/sql/opt/optbuilder/scope.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -872,7 +872,7 @@ func (s *scope) FindSourceProvidingColumn(
872872
if candidate.ambiguous {
873873
return nil, nil, -1, s.newAmbiguousColumnError(colName, candidate.matchClass)
874874
}
875-
return &col.table, col, int(col.id), nil
875+
return &col.table, col, int(col.id), col.resolveErr
876876
}
877877
// No matches in this scope; proceed to the parent scope.
878878
}
@@ -1015,7 +1015,7 @@ func (s *scope) Resolve(
10151015
if col.visibility != inaccessible &&
10161016
col.name.MatchesReferenceName(colName) &&
10171017
sourceNameMatches(*prefix, col.table) {
1018-
return col, nil
1018+
return col, col.resolveErr
10191019
}
10201020
}
10211021

0 commit comments

Comments
 (0)