Skip to content

Commit 8cf7d10

Browse files
sql: populate pg_class.relhastriggers (#170891)
sql: populate pg_class.relhastriggers
2 parents 3e97b8a + 42294b9 commit 8cf7d10

7 files changed

Lines changed: 76 additions & 39 deletions

File tree

pkg/sql/explain_bundle_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -831,7 +831,7 @@ CREATE TABLE users(id UUID DEFAULT gen_random_uuid() PRIMARY KEY, promo_id INT R
831831
"CREATE FUNCTION public.trigger_f()",
832832
"CREATE TABLE public.trigger_t1",
833833
"CREATE TABLE public.trigger_t2",
834-
"CREATE TRIGGER trigger_foo AFTER INSERT ON defaultdb.public.trigger_t1"} {
834+
"CREATE TRIGGER trigger_foo AFTER INSERT ON public.trigger_t1"} {
835835
if !strings.Contains(contents, expected) {
836836
return errors.Errorf("could not find %q in schema.sql:\n%s", expected, contents)
837837
}

pkg/sql/logictest/testdata/logic_test/pg_builtins

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1482,13 +1482,13 @@ skipif config local-legacy-schema-changer
14821482
query T
14831483
SELECT pg_get_triggerdef(oid) FROM pg_trigger WHERE tgname = 'test_trig'
14841484
----
1485-
CREATE TRIGGER test_trig BEFORE INSERT ON test.public.trig_test FOR EACH ROW EXECUTE FUNCTION test.public.trig_fn()
1485+
CREATE TRIGGER test_trig BEFORE INSERT ON public.trig_test FOR EACH ROW EXECUTE FUNCTION public.trig_fn()
14861486

14871487
skipif config local-legacy-schema-changer
14881488
query T
14891489
SELECT pg_get_triggerdef(oid, true) FROM pg_trigger WHERE tgname = 'test_trig'
14901490
----
1491-
CREATE TRIGGER test_trig BEFORE INSERT ON test.public.trig_test FOR EACH ROW EXECUTE FUNCTION test.public.trig_fn()
1491+
CREATE TRIGGER test_trig BEFORE INSERT ON public.trig_test FOR EACH ROW EXECUTE FUNCTION public.trig_fn()
14921492

14931493
# Invalid OID returns NULL.
14941494
query T nosort

pkg/sql/logictest/testdata/logic_test/row_level_security

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1104,6 +1104,12 @@ select relname, relrowsecurity, relforcerowsecurity from pg_class where relname
11041104
----
11051105
roaches true true
11061106

1107+
# pg_tables.rowsecurity should agree with pg_class.relrowsecurity.
1108+
query TB
1109+
select tablename, rowsecurity from pg_tables where tablename = 'roaches';
1110+
----
1111+
roaches true
1112+
11071113
skipif config schema-locked-disabled
11081114
query T
11091115
select create_statement from crdb_internal.create_statements where descriptor_name='roaches'

pkg/sql/logictest/testdata/logic_test/show_create_all_triggers

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ query T colnames,nosort
3939
SHOW CREATE ALL TRIGGERS;
4040
----
4141
create_statement
42-
CREATE TRIGGER t1_trigger AFTER INSERT ON d.public.t1 FOR EACH ROW EXECUTE FUNCTION d.public.trigger_func();
42+
CREATE TRIGGER t1_trigger AFTER INSERT ON public.t1 FOR EACH ROW EXECUTE FUNCTION public.trigger_func();
4343

4444
statement ok
4545
CREATE TRIGGER t2_trigger
@@ -51,8 +51,8 @@ query T colnames,nosort
5151
SHOW CREATE ALL TRIGGERS;
5252
----
5353
create_statement
54-
CREATE TRIGGER t1_trigger AFTER INSERT ON d.public.t1 FOR EACH ROW EXECUTE FUNCTION d.public.trigger_func();
55-
CREATE TRIGGER t2_trigger AFTER INSERT ON d.public.t2 FOR EACH ROW EXECUTE FUNCTION d.public.trigger_func();
54+
CREATE TRIGGER t1_trigger AFTER INSERT ON public.t1 FOR EACH ROW EXECUTE FUNCTION public.trigger_func();
55+
CREATE TRIGGER t2_trigger AFTER INSERT ON public.t2 FOR EACH ROW EXECUTE FUNCTION public.trigger_func();
5656

5757
statement ok
5858
CREATE OR REPLACE FUNCTION trigger_func2()
@@ -75,9 +75,9 @@ query T colnames,nosort
7575
SHOW CREATE ALL TRIGGERS;
7676
----
7777
create_statement
78-
CREATE TRIGGER t1_trigger AFTER INSERT ON d.public.t1 FOR EACH ROW EXECUTE FUNCTION d.public.trigger_func();
79-
CREATE TRIGGER t1_trigger2 AFTER INSERT ON d.public.t1 FOR EACH ROW EXECUTE FUNCTION d.public.trigger_func2();
80-
CREATE TRIGGER t2_trigger AFTER INSERT ON d.public.t2 FOR EACH ROW EXECUTE FUNCTION d.public.trigger_func();
78+
CREATE TRIGGER t1_trigger AFTER INSERT ON public.t1 FOR EACH ROW EXECUTE FUNCTION public.trigger_func();
79+
CREATE TRIGGER t1_trigger2 AFTER INSERT ON public.t1 FOR EACH ROW EXECUTE FUNCTION public.trigger_func2();
80+
CREATE TRIGGER t2_trigger AFTER INSERT ON public.t2 FOR EACH ROW EXECUTE FUNCTION public.trigger_func();
8181

8282
statement ok
8383
DROP TRIGGER t1_trigger2 ON t1;
@@ -86,5 +86,5 @@ query T colnames,nosort
8686
SHOW CREATE ALL TRIGGERS;
8787
----
8888
create_statement
89-
CREATE TRIGGER t1_trigger AFTER INSERT ON d.public.t1 FOR EACH ROW EXECUTE FUNCTION d.public.trigger_func();
90-
CREATE TRIGGER t2_trigger AFTER INSERT ON d.public.t2 FOR EACH ROW EXECUTE FUNCTION d.public.trigger_func();
89+
CREATE TRIGGER t1_trigger AFTER INSERT ON public.t1 FOR EACH ROW EXECUTE FUNCTION public.trigger_func();
90+
CREATE TRIGGER t2_trigger AFTER INSERT ON public.t2 FOR EACH ROW EXECUTE FUNCTION public.trigger_func();

pkg/sql/logictest/testdata/logic_test/triggers

Lines changed: 45 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4164,19 +4164,19 @@ query TT colnames
41644164
SHOW CREATE TRIGGER tr ON t;
41654165
----
41664166
trigger_name create_statement
4167-
tr CREATE TRIGGER tr AFTER INSERT ON test.public.t FOR EACH ROW EXECUTE FUNCTION test.public.g()
4167+
tr CREATE TRIGGER tr AFTER INSERT ON public.t FOR EACH ROW EXECUTE FUNCTION public.g()
41684168

41694169
query TT colnames
41704170
SHOW CREATE TRIGGER tr2 ON t;
41714171
----
41724172
trigger_name create_statement
4173-
tr2 CREATE TRIGGER tr2 BEFORE UPDATE ON test.public.t FOR EACH ROW EXECUTE FUNCTION test.public.g()
4173+
tr2 CREATE TRIGGER tr2 BEFORE UPDATE ON public.t FOR EACH ROW EXECUTE FUNCTION public.g()
41744174

41754175
query TT colnames
41764176
SHOW CREATE TRIGGER tr3 ON t;
41774177
----
41784178
trigger_name create_statement
4179-
tr3 CREATE TRIGGER tr3 AFTER INSERT OR UPDATE ON test.public.t FOR EACH ROW WHEN (new).a < 5:::INT8 EXECUTE FUNCTION test.public.g()
4179+
tr3 CREATE TRIGGER tr3 AFTER INSERT OR UPDATE ON public.t FOR EACH ROW WHEN (new).a < 5:::INT8 EXECUTE FUNCTION public.g()
41804180

41814181
statement ok
41824182
DROP TRIGGER tr ON t;
@@ -4564,7 +4564,7 @@ CREATE OR REPLACE TRIGGER replace_test BEFORE INSERT ON xy FOR EACH ROW EXECUTE
45644564
query TT
45654565
SHOW CREATE TRIGGER replace_test ON xy;
45664566
----
4567-
replace_test CREATE TRIGGER replace_test BEFORE INSERT ON test.public.xy FOR EACH ROW EXECUTE FUNCTION test.public.f()
4567+
replace_test CREATE TRIGGER replace_test BEFORE INSERT ON public.xy FOR EACH ROW EXECUTE FUNCTION public.f()
45684568

45694569
# CREATE OR REPLACE TRIGGER when the trigger already exists replaces it.
45704570
statement ok
@@ -4573,7 +4573,7 @@ CREATE OR REPLACE TRIGGER replace_test AFTER UPDATE ON xy FOR EACH ROW EXECUTE F
45734573
query TT
45744574
SHOW CREATE TRIGGER replace_test ON xy;
45754575
----
4576-
replace_test CREATE TRIGGER replace_test AFTER UPDATE ON test.public.xy FOR EACH ROW EXECUTE FUNCTION test.public.f()
4576+
replace_test CREATE TRIGGER replace_test AFTER UPDATE ON public.xy FOR EACH ROW EXECUTE FUNCTION public.f()
45774577

45784578
# CREATE TRIGGER (without REPLACE) still errors on duplicate.
45794579
statement error pgcode 42710 pq: trigger "replace_test" for relation "xy" already exists
@@ -4978,11 +4978,11 @@ WHERE tgrelid = 'test_triggers'::regclass
49784978
ORDER BY tgname;
49794979
----
49804980
tgname tgtype has_func_oid tgnargs tgenabled tgoldtable tgnewtable
4981-
after_delete_row 9 true 0 A NULL NULL
4982-
after_insert_row 5 true 0 A NULL NULL
4983-
before_update_row 19 true 0 A NULL NULL
4984-
multi_event_trigger 31 true 0 A NULL NULL
4985-
trigger_with_args 5 true 3 A NULL NULL
4981+
after_delete_row 9 true 0 O NULL NULL
4982+
after_insert_row 5 true 0 O NULL NULL
4983+
before_update_row 19 true 0 O NULL NULL
4984+
multi_event_trigger 31 true 0 O NULL NULL
4985+
trigger_with_args 5 true 3 O NULL NULL
49864986

49874987
# Test INSTEAD OF triggers (not yet implemented)
49884988
statement error pgcode 42809 "test_triggers" is a table\nDETAIL: Tables cannot have INSTEAD OF triggers.
@@ -5075,6 +5075,28 @@ WHERE tgrelid = 'other_schema.test_table'::regclass;
50755075
tgname table_name trigger_func_name
50765076
other_schema_trigger test_table trigger_func1
50775077

5078+
# Test that pg_class.relhastriggers reflects the presence of triggers.
5079+
query TB colnames,rowsort
5080+
SELECT relname, relhastriggers
5081+
FROM pg_catalog.pg_class
5082+
WHERE relname IN ('test_triggers', 'audit_log', 'test_table');
5083+
----
5084+
relname relhastriggers
5085+
audit_log false
5086+
test_table true
5087+
test_triggers true
5088+
5089+
# pg_tables.hastriggers should agree with pg_class.relhastriggers.
5090+
query TB colnames,rowsort
5091+
SELECT tablename, hastriggers
5092+
FROM pg_catalog.pg_tables
5093+
WHERE tablename IN ('test_triggers', 'audit_log', 'test_table');
5094+
----
5095+
tablename hastriggers
5096+
audit_log false
5097+
test_table true
5098+
test_triggers true
5099+
50785100
# Clean up
50795101
statement ok
50805102
DROP TABLE test_triggers CASCADE;
@@ -5141,12 +5163,12 @@ CREATE TRIGGER insert_trigger
51415163
FOR EACH ROW
51425164
EXECUTE FUNCTION audit_trigger_func();
51435165

5144-
# Verify trigger is enabled by default (tgenabled = 'A' for always).
5166+
# Verify trigger is enabled by default (tgenabled = 'O' for origin).
51455167
skipif config local-mixed-25.4 local-mixed-26.1
51465168
query T
51475169
SELECT tgenabled FROM pg_trigger WHERE tgname = 'insert_trigger';
51485170
----
5149-
A
5171+
O
51505172

51515173
# SHOW TRIGGERS should also report enabled = true.
51525174
skipif config local-mixed-25.4 local-mixed-26.1
@@ -5205,7 +5227,7 @@ skipif config local-mixed-25.4 local-mixed-26.1
52055227
query T
52065228
SELECT tgenabled FROM pg_trigger WHERE tgname = 'insert_trigger';
52075229
----
5208-
A
5230+
O
52095231

52105232
# Insert another row - trigger should fire again.
52115233
skipif config local-mixed-25.4 local-mixed-26.1
@@ -5247,7 +5269,7 @@ skipif config local-mixed-25.4 local-mixed-26.1
52475269
query T
52485270
SELECT tgenabled FROM pg_trigger WHERE tgname = 'insert_trigger';
52495271
----
5250-
A
5272+
O
52515273

52525274
# Test error when trigger doesn't exist.
52535275
skipif config local-mixed-25.4 local-mixed-26.1
@@ -5272,8 +5294,8 @@ skipif config local-mixed-25.4 local-mixed-26.1
52725294
query TT rowsort
52735295
SELECT tgname, tgenabled FROM pg_trigger WHERE tgrelid = 'enable_disable_t'::regclass ORDER BY tgname;
52745296
----
5275-
insert_trigger A
5276-
update_trigger A
5297+
insert_trigger O
5298+
update_trigger O
52775299

52785300
# Insert and update - both triggers should fire.
52795301
skipif config local-mixed-25.4 local-mixed-26.1
@@ -5340,8 +5362,8 @@ skipif config local-mixed-25.4 local-mixed-26.1
53405362
query TT rowsort
53415363
SELECT tgname, tgenabled FROM pg_trigger WHERE tgrelid = 'enable_disable_t'::regclass ORDER BY tgname;
53425364
----
5343-
insert_trigger A
5344-
update_trigger A
5365+
insert_trigger O
5366+
update_trigger O
53455367

53465368
# Both triggers should fire again.
53475369
skipif config local-mixed-25.4 local-mixed-26.1
@@ -5374,7 +5396,7 @@ query TT rowsort
53745396
SELECT tgname, tgenabled FROM pg_trigger WHERE tgrelid = 'enable_disable_t'::regclass ORDER BY tgname;
53755397
----
53765398
insert_trigger D
5377-
update_trigger A
5399+
update_trigger O
53785400

53795401
# Insert and update - only update trigger should fire.
53805402
skipif config local-mixed-25.4 local-mixed-26.1
@@ -5431,8 +5453,8 @@ skipif config local-mixed-25.4 local-mixed-26.1
54315453
query TT rowsort
54325454
SELECT tgname, tgenabled FROM pg_trigger WHERE tgrelid = 'enable_disable_t'::regclass ORDER BY tgname;
54335455
----
5434-
insert_trigger A
5435-
update_trigger A
5456+
insert_trigger O
5457+
update_trigger O
54365458

54375459
# Test that ALL/USER on a table with no triggers is a no-op.
54385460
skipif config local-mixed-25.4 local-mixed-26.1
@@ -5471,8 +5493,8 @@ skipif config local-mixed-25.4 local-mixed-26.1
54715493
query TT rowsort
54725494
SELECT tgname, tgenabled FROM pg_trigger WHERE tgrelid = 'enable_disable_t'::regclass ORDER BY tgname;
54735495
----
5474-
insert_trigger A
5475-
update_trigger A
5496+
insert_trigger O
5497+
update_trigger O
54765498

54775499
# Test with IF EXISTS on table.
54785500
skipif config local-mixed-25.4 local-mixed-26.1

pkg/sql/pg_catalog.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1024,7 +1024,7 @@ https://www.postgresql.org/docs/9.5/catalog-pg-class.html`,
10241024
tree.DBoolFalse, // relhasoids
10251025
tree.MakeDBool(tree.DBool(table.IsPhysicalTable())), // relhaspkey
10261026
tree.DBoolFalse, // relhasrules
1027-
tree.DBoolFalse, // relhastriggers
1027+
tree.MakeDBool(tree.DBool(len(table.GetTriggers()) > 0)), // relhastriggers
10281028
tree.DBoolFalse, // relhassubclass
10291029
zeroVal, // relfrozenxid
10301030
relacl, // relacl
@@ -4171,8 +4171,8 @@ https://www.postgresql.org/docs/9.5/view-pg-tables.html`,
41714171
tree.DNull, // tablespace
41724172
tree.MakeDBool(tree.DBool(table.IsPhysicalTable())), // hasindexes
41734173
tree.DBoolFalse, // hasrules
4174-
tree.DBoolFalse, // hastriggers
4175-
tree.DBoolFalse, // rowsecurity
4174+
tree.MakeDBool(tree.DBool(len(table.GetTriggers()) > 0)), // hastriggers
4175+
tree.MakeDBool(tree.DBool(table.IsRowLevelSecurityEnabled())), // rowsecurity
41764176
)
41774177
})
41784178
},
@@ -4254,8 +4254,11 @@ https://www.postgresql.org/docs/16/catalog-pg-trigger.html`,
42544254
}
42554255
}
42564256

4257-
// tgenabled: O = origin and local, D = disabled, R = replica, A = always
4258-
tgenabled := tree.NewDString("A")
4257+
// tgenabled: O = origin and local, D = disabled, R = replica, A = always.
4258+
// CockroachDB has no session_replication_role equivalent, so a trigger
4259+
// is either enabled (firing on origin and local, the PostgreSQL default)
4260+
// or disabled.
4261+
tgenabled := tree.NewDString("O")
42594262
if !trigger.Enabled {
42604263
tgenabled = tree.NewDString("D")
42614264
}

pkg/sql/show_trigger.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,15 +78,21 @@ func renderCreateTriggerStatement(
7878
}
7979

8080
// Resolve the fully-qualified names of the trigger function and table.
81+
// PostgreSQL's pg_get_triggerdef emits a schema-qualified table name but
82+
// no catalog (database) prefix; suppress the catalog here so SHOW CREATE
83+
// TRIGGER and pg_get_triggerdef produce schema.table, matching PostgreSQL
84+
// and keeping the output reusable across databases.
8185
funcName, err := p.GetQualifiedFunctionNameByID(ctx, int64(trigger.FuncID))
8286
if err != nil {
8387
return "", err
8488
}
89+
funcName.ExplicitCatalog = false
8590

8691
tableName, err := p.getQualifiedTableName(ctx, tableDesc)
8792
if err != nil {
8893
return "", err
8994
}
95+
tableName.ExplicitCatalog = false
9096

9197
// Use the trigger descriptor to decompile a CreateTrigger statement that
9298
// will be used to produce the SHOW CREATE TRIGGER output.

0 commit comments

Comments
 (0)