Skip to content

sql/opt: make TG_ARGV use 0-based indexing for PostgreSQL compatibility#161925

Merged
craig[bot] merged 1 commit intocockroachdb:masterfrom
rafiss:trigger-tg-argv
Feb 3, 2026
Merged

sql/opt: make TG_ARGV use 0-based indexing for PostgreSQL compatibility#161925
craig[bot] merged 1 commit intocockroachdb:masterfrom
rafiss:trigger-tg-argv

Conversation

@rafiss
Copy link
Collaborator

@rafiss rafiss commented Jan 28, 2026

Previously, TG_ARGV used 1-based indexing (standard SQL array behavior), which was incompatible with PostgreSQL's 0-based indexing for TG_ARGV. A session setting allow_create_trigger_function_with_argv_references was required to use TG_ARGV at all, since it was blocked by default due to this incompatibility.

This change makes TG_ARGV use 0-based indexing to match PostgreSQL behavior. To implement this, we add a zeroIndexed field to the DArray struct that controls the FirstIndex() return value. When constructing TG_ARGV for trigger functions, we now call SetZeroIndexed() to mark the array as 0-indexed.

The allow_create_trigger_function_with_argv_references session setting is no longer needed and has been converted to a backwards-compatibility no-op variable.

Fixes: #135311

Release note (backward-incompatible change): The TG_ARGV trigger function parameter now uses 0-based indexing to match PostgreSQL behavior. Previously, TG_ARGV[1] returned the first argument; now TG_ARGV[0] returns the first argument and TG_ARGV[1] returns the second argument. Additionally, usage of TG_ARGV no longer requires setting the allow_create_trigger_function_with_argv_references session variable.

@cockroach-teamcity
Copy link
Member

This change is Reviewable

@rafiss rafiss force-pushed the trigger-tg-argv branch 2 times, most recently from 61d6a09 to cb25567 Compare January 30, 2026 02:52
@rafiss rafiss marked this pull request as ready for review January 30, 2026 15:54
@rafiss rafiss requested a review from a team as a code owner January 30, 2026 15:54
@rafiss rafiss requested review from a team, DrewKimball, ZhouXing19 and mw5h and removed request for a team and ZhouXing19 January 30, 2026 15:54
Copy link
Collaborator

@DrewKimball DrewKimball left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:lgtm: Nice job Rafi!

@DrewKimball reviewed 11 files and all commit messages, and made 3 comments.
Reviewable status: :shipit: complete! 1 of 0 LGTMs obtained (waiting on @mw5h and @rafiss).


pkg/sql/sem/tree/datum.go line 4902 at r1 (raw file):

// SetZeroIndexed marks this array as 0-indexed. This is used for TG_ARGV
// to match PostgreSQL's 0-indexed behavior.
func (d *DArray) SetZeroIndexed() {

Wow, nice work! I didn't realize this would require so few changes.


pkg/ccl/logictestccl/testdata/logic_test/triggers line 4805 at r1 (raw file):


statement ok
CREATE FUNCTION g() RETURNS TRIGGER LANGUAGE PLpgSQL AS $$

It would be a good idea to add some more tests:

  • DECLARE a TEXT[] variable, assign TG_ARGV to it, then try the zero-indexed read.
  • The above, but assign into the variable using SELECT ... INTO .
  • DECLARE a function that takes a TEXT[] argument and tries a zero-indexed read on it.

Previously, TG_ARGV used 1-based indexing (standard SQL array behavior),
which was incompatible with PostgreSQL's 0-based indexing for TG_ARGV.
A session setting `allow_create_trigger_function_with_argv_references`
was required to use TG_ARGV at all, since it was blocked by default due
to this incompatibility.

This change makes TG_ARGV use 0-based indexing to match PostgreSQL
behavior. To implement this, we add a `zeroIndexed` field to the DArray
struct that controls the FirstIndex() return value. When constructing
TG_ARGV for trigger functions, we now call SetZeroIndexed() to mark the
array as 0-indexed.

The `allow_create_trigger_function_with_argv_references` session setting
is no longer needed and has been converted to a backwards-compatibility
no-op variable.

Fixes: cockroachdb#135311

Release note (backward-incompatible change): The TG_ARGV trigger
function parameter now uses 0-based indexing to match PostgreSQL
behavior. Previously, TG_ARGV[1] returned the first argument; now
TG_ARGV[0] returns the first argument and TG_ARGV[1] returns the second
argument. Additionally, usage of TG_ARGV no longer requires setting the
`allow_create_trigger_function_with_argv_references` session variable.
Copy link
Collaborator Author

@rafiss rafiss left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rafiss made 1 comment.
Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (and 1 stale) (waiting on @DrewKimball and @mw5h).


pkg/ccl/logictestccl/testdata/logic_test/triggers line 4805 at r1 (raw file):

Previously, DrewKimball (Drew Kimball) wrote…

It would be a good idea to add some more tests:

  • DECLARE a TEXT[] variable, assign TG_ARGV to it, then try the zero-indexed read.
  • The above, but assign into the variable using SELECT ... INTO .
  • DECLARE a function that takes a TEXT[] argument and tries a zero-indexed read on it.

i added the first and third test cases and verified that it matches postgres behavior. are those what you had in mind?

for the second test case you asked for, there is a separate problem that seems to prevent SELECT ... INTO from working on TG_* variables. i got this error even when trying with TG_NAME:

statement ok
CREATE TABLE xy (x INT, y INT);

statement ok
CREATE FUNCTION g() RETURNS TRIGGER LANGUAGE PLpgSQL AS $$
  DECLARE
    args TEXT;
  BEGIN
    SELECT TG_NAME INTO args;
    RAISE NOTICE 'args: %', args;
    RETURN NEW;
  END
$$;

statement ok
CREATE TRIGGER foo BEFORE INSERT ON xy FOR EACH ROW EXECUTE FUNCTION g('into_one', 'into_two');

statement ok
INSERT INTO xy VALUES (101, 201);
[16:25:59] INSERT INTO xy VALUES (101, 201);;
[16:25:59] 	-- OK;
[16:25:59] --- done: /Users/rafiss/.cache/bazel/_bazel_rafiss/bases/cf10ff/1/sandbox/darwin-sandbox/2452/execroot/com_github_cockroachdb_cockroach/bazel-out/darwin_arm64-fastbuild/bin/pkg/ccl/logictestccl/tests/local/local_test_/local_test.runfiles/com_github_cockroachdb_cockroach/pkg/ccl/logictestccl/testdata/logic_test/_tmp with config local: 3 tests, 0 failures

    logic.go:4653:
        /Users/rafiss/.cache/bazel/_bazel_rafiss/bases/cf10ff/1/sandbox/darwin-sandbox/2452/execroot/com_github_cockroachdb_cockroach/bazel-out/darwin_arm64-fastbuild/bin/pkg/ccl/logictestccl/tests/local/local_test_/local_test.runfiles/com_github_cockroachdb_cockroach/pkg/ccl/logictestccl/testdata/logic_test/_tmp:21: error while processing
    logic.go:4653:
        /Users/rafiss/.cache/bazel/_bazel_rafiss/bases/cf10ff/1/sandbox/darwin-sandbox/2452/execroot/com_github_cockroachdb_cockroach/bazel-out/darwin_arm64-fastbuild/bin/pkg/ccl/logictestccl/tests/local/local_test_/local_test.runfiles/com_github_cockroachdb_cockroach/pkg/ccl/logictestccl/testdata/logic_test/_tmp:21:
        expected success, but found
        (XX000) internal error: top-level relational expression cannot have outer columns: (38)
        optimizer.go:277: in Optimize()
        DETAIL: stack trace:
        pkg/sql/opt/xform/optimizer.go:277: Optimize()
        pkg/sql/opt/exec/execbuilder/scalar.go:1288: func2()
        pkg/sql/routine.go:329: startInternal()
        pkg/sql/routine.go:279: Start()
        pkg/sql/routine.go:179: EvalRoutineExpr()
        pkg/sql/sem/eval/expr.go:648: EvalRoutineExpr()
        bazel-out/darwin_arm64-fastbuild/bin/pkg/sql/sem/tree/eval_expr_generated.go:361: Eval()
        pkg/sql/sem/eval/expr.go:22: Expr()
        pkg/sql/execinfra/execexpr/expr.go:310: eval()
        pkg/sql/execinfra/execexpr/expr.go:163: EvalExpr()
        pkg/sql/execinfra/processorsbase.go:294: ProcessRow()
        pkg/sql/execinfra/processorsbase.go:744: ProcessRowHelper()
        pkg/sql/rowexec/noop.go:96: Next()
        pkg/sql/colexec/columnarizer.go:244: Next()
        pkg/sql/colexec/invariants_checker.go:99: Next()
        pkg/sql/colexec/materializer.go:248: next()
        pkg/sql/colexec/materializer.go:276: nextAdapter()
        pkg/sql/colexecerror/error.go:162: CatchVectorizedRuntimeError()
        pkg/sql/colexec/materializer.go:282: Next()
        pkg/sql/colflow/flow_coordinator.go:119: next()
        pkg/sql/colflow/flow_coordinator.go:136: nextAdapter()
        pkg/sql/colexecerror/error.go:162: CatchVectorizedRuntimeError()
        pkg/sql/colflow/flow_coordinator.go:141: Next()
        pkg/sql/execinfra/base.go:193: Run()
        pkg/sql/execinfra/processorsbase.go:766: Run()
        pkg/sql/flowinfra/flow.go:574: Run()
        pkg/sql/colflow/vectorized_flow.go:302: Run()
        pkg/sql/distsql_running.go:1087: Run()
        pkg/sql/distsql_running.go:2353: PlanAndRun()
        pkg/sql/apply_join.go:378: func2()
        pkg/sql/apply_join.go:381: runPlanInsidePlan()
        pkg/sql/routine.go:413: func1()

@DrewKimball is that already tracked in a different issue?

Copy link
Contributor

@mw5h mw5h left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:lgtm:

@mw5h reviewed 11 files and all commit messages, and made 1 comment.
Reviewable status: :shipit: complete! 1 of 0 LGTMs obtained (and 1 stale) (waiting on @DrewKimball and @rafiss).

Copy link
Collaborator

@DrewKimball DrewKimball left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:lgtm:

@DrewKimball reviewed 1 file and all commit messages, and made 2 comments.
Reviewable status: :shipit: complete! 2 of 0 LGTMs obtained (waiting on @rafiss).


pkg/ccl/logictestccl/testdata/logic_test/triggers line 4805 at r1 (raw file):

Previously, rafiss (Rafi Shamim) wrote…

i added the first and third test cases and verified that it matches postgres behavior. are those what you had in mind?

for the second test case you asked for, there is a separate problem that seems to prevent SELECT ... INTO from working on TG_* variables. i got this error even when trying with TG_NAME:

statement ok
CREATE TABLE xy (x INT, y INT);

# Test assigning TG_ARGV to a TEXT[] variable using SELECT ... INTO.
# TODO(#???): This should work like PostgreSQL, but currently the INSERT causes an
# internal error.
statement ok
CREATE FUNCTION g() RETURNS TRIGGER LANGUAGE PLpgSQL AS $$
  DECLARE
    args TEXT;
  BEGIN
    SELECT TG_NAME INTO args;
    RAISE NOTICE 'args: %', args;
    RETURN NEW;
  END
$$;

statement ok
CREATE TRIGGER foo BEFORE INSERT ON xy FOR EACH ROW EXECUTE FUNCTION g('into_one', 'into_two');

statement ok
INSERT INTO xy VALUES (101, 201);
[16:25:59] INSERT INTO xy VALUES (101, 201);;
[16:25:59] 	-- OK;
[16:25:59] --- done: /Users/rafiss/.cache/bazel/_bazel_rafiss/bases/cf10ff/1/sandbox/darwin-sandbox/2452/execroot/com_github_cockroachdb_cockroach/bazel-out/darwin_arm64-fastbuild/bin/pkg/ccl/logictestccl/tests/local/local_test_/local_test.runfiles/com_github_cockroachdb_cockroach/pkg/ccl/logictestccl/testdata/logic_test/_tmp with config local: 3 tests, 0 failures

    logic.go:4653:
        /Users/rafiss/.cache/bazel/_bazel_rafiss/bases/cf10ff/1/sandbox/darwin-sandbox/2452/execroot/com_github_cockroachdb_cockroach/bazel-out/darwin_arm64-fastbuild/bin/pkg/ccl/logictestccl/tests/local/local_test_/local_test.runfiles/com_github_cockroachdb_cockroach/pkg/ccl/logictestccl/testdata/logic_test/_tmp:21: error while processing
    logic.go:4653:
        /Users/rafiss/.cache/bazel/_bazel_rafiss/bases/cf10ff/1/sandbox/darwin-sandbox/2452/execroot/com_github_cockroachdb_cockroach/bazel-out/darwin_arm64-fastbuild/bin/pkg/ccl/logictestccl/tests/local/local_test_/local_test.runfiles/com_github_cockroachdb_cockroach/pkg/ccl/logictestccl/testdata/logic_test/_tmp:21:
        expected success, but found
        (XX000) internal error: top-level relational expression cannot have outer columns: (38)
        optimizer.go:277: in Optimize()
        DETAIL: stack trace:
        pkg/sql/opt/xform/optimizer.go:277: Optimize()
        pkg/sql/opt/exec/execbuilder/scalar.go:1288: func2()
        pkg/sql/routine.go:329: startInternal()
        pkg/sql/routine.go:279: Start()
        pkg/sql/routine.go:179: EvalRoutineExpr()
        pkg/sql/sem/eval/expr.go:648: EvalRoutineExpr()
        bazel-out/darwin_arm64-fastbuild/bin/pkg/sql/sem/tree/eval_expr_generated.go:361: Eval()
        pkg/sql/sem/eval/expr.go:22: Expr()
        pkg/sql/execinfra/execexpr/expr.go:310: eval()
        pkg/sql/execinfra/execexpr/expr.go:163: EvalExpr()
        pkg/sql/execinfra/processorsbase.go:294: ProcessRow()
        pkg/sql/execinfra/processorsbase.go:744: ProcessRowHelper()
        pkg/sql/rowexec/noop.go:96: Next()
        pkg/sql/colexec/columnarizer.go:244: Next()
        pkg/sql/colexec/invariants_checker.go:99: Next()
        pkg/sql/colexec/materializer.go:248: next()
        pkg/sql/colexec/materializer.go:276: nextAdapter()
        pkg/sql/colexecerror/error.go:162: CatchVectorizedRuntimeError()
        pkg/sql/colexec/materializer.go:282: Next()
        pkg/sql/colflow/flow_coordinator.go:119: next()
        pkg/sql/colflow/flow_coordinator.go:136: nextAdapter()
        pkg/sql/colexecerror/error.go:162: CatchVectorizedRuntimeError()
        pkg/sql/colflow/flow_coordinator.go:141: Next()
        pkg/sql/execinfra/base.go:193: Run()
        pkg/sql/execinfra/processorsbase.go:766: Run()
        pkg/sql/flowinfra/flow.go:574: Run()
        pkg/sql/colflow/vectorized_flow.go:302: Run()
        pkg/sql/distsql_running.go:1087: Run()
        pkg/sql/distsql_running.go:2353: PlanAndRun()
        pkg/sql/apply_join.go:378: func2()
        pkg/sql/apply_join.go:381: runPlanInsidePlan()
        pkg/sql/routine.go:413: func1()

@DrewKimball is that already tracked in a different issue?

Thanks for adding the tests. This internal error is new to me, weird...

Copy link
Collaborator Author

@rafiss rafiss left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TFTR!

bors r+

@rafiss made 2 comments.
Reviewable status: :shipit: complete! 2 of 0 LGTMs obtained (waiting on @DrewKimball).


pkg/ccl/logictestccl/testdata/logic_test/triggers line 4805 at r1 (raw file):

Previously, DrewKimball (Drew Kimball) wrote…

Thanks for adding the tests. This internal error is new to me, weird...

i filed #162289 to track the internal error.

@craig
Copy link
Contributor

craig bot commented Feb 3, 2026

@craig craig bot merged commit 7232f50 into cockroachdb:master Feb 3, 2026
26 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SQL Triggers are mis-reading the TG_ARGV array

4 participants