Skip to content

Commit 2341388

Browse files
committed
sql: add SHOW CREATE ALL TRIGGERS
and implement it with optimized query. Implemented `SHOW CREATE ALL TRIGGERS` command. This consists on parser changes (sql.y), generator functions, addition of the command to delegator, and also creation of `crdb_internal.create_trigger_statements` as a virtual table. The generators query the table, which is indexed on `table_id` and the query result contains the create statements of all triggers. This is a useful change for the task of a `SHOW CREATE` statement that returns create statements for all objects of the database. Release note (sql): Add `SHOW CREATE ALL TRIGGERS` as a command to CockroachDB Epic: CRDB-49582
1 parent f645ff3 commit 2341388

File tree

40 files changed

+817
-186
lines changed

40 files changed

+817
-186
lines changed

docs/generated/sql/bnf/show_create_stmt.bnf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@ show_create_stmt ::=
22
'SHOW' 'CREATE' object_name opt_show_create_format_options
33
| 'SHOW' 'CREATE' 'ALL' 'SCHEMAS'
44
| 'SHOW' 'CREATE' 'ALL' 'TABLES'
5+
| 'SHOW' 'CREATE' 'ALL' 'TRIGGERS'
56
| 'SHOW' 'CREATE' 'ALL' 'TYPES'
67
| 'SHOW' 'CREATE' 'ALL' 'ROUTINES'

docs/generated/sql/bnf/stmt_block.bnf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -854,6 +854,7 @@ show_create_stmt ::=
854854
'SHOW' 'CREATE' table_name opt_show_create_format_options
855855
| 'SHOW' 'CREATE' 'ALL' 'SCHEMAS'
856856
| 'SHOW' 'CREATE' 'ALL' 'TABLES'
857+
| 'SHOW' 'CREATE' 'ALL' 'TRIGGERS'
857858
| 'SHOW' 'CREATE' 'ALL' 'TYPES'
858859
| 'SHOW' 'CREATE' 'ALL' 'ROUTINES'
859860

pkg/ccl/logictestccl/tests/3node-tenant/generated_test.go

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/ccl/logictestccl/tests/local-read-committed/generated_test.go

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/ccl/logictestccl/tests/local-repeatable-read/generated_test.go

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/cli/testdata/zip/file-filters/testzip_file_filters

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ debug/crdb_internal.create_function_statements.txt
1414
debug/crdb_internal.create_procedure_statements.txt
1515
debug/crdb_internal.create_schema_statements.txt
1616
debug/crdb_internal.create_statements.txt
17+
debug/crdb_internal.create_trigger_statements.txt
1718
debug/crdb_internal.create_type_statements.txt
1819
debug/crdb_internal.default_privileges.txt
1920
debug/crdb_internal.index_usage_statistics.txt
@@ -156,6 +157,7 @@ debug/crdb_internal.create_function_statements.txt
156157
debug/crdb_internal.create_procedure_statements.txt
157158
debug/crdb_internal.create_schema_statements.txt
158159
debug/crdb_internal.create_statements.txt
160+
debug/crdb_internal.create_trigger_statements.txt
159161
debug/crdb_internal.create_type_statements.txt
160162
debug/crdb_internal.default_privileges.txt
161163
debug/crdb_internal.index_usage_statistics.txt
@@ -305,6 +307,7 @@ debug/crdb_internal.create_function_statements.txt
305307
debug/crdb_internal.create_procedure_statements.txt
306308
debug/crdb_internal.create_schema_statements.txt
307309
debug/crdb_internal.create_statements.txt
310+
debug/crdb_internal.create_trigger_statements.txt
308311
debug/crdb_internal.create_type_statements.txt
309312
debug/crdb_internal.default_privileges.txt
310313
debug/crdb_internal.index_usage_statistics.txt
@@ -448,6 +451,7 @@ debug/crdb_internal.create_function_statements.txt
448451
debug/crdb_internal.create_procedure_statements.txt
449452
debug/crdb_internal.create_schema_statements.txt
450453
debug/crdb_internal.create_statements.txt
454+
debug/crdb_internal.create_trigger_statements.txt
451455
debug/crdb_internal.create_type_statements.txt
452456
debug/crdb_internal.default_privileges.txt
453457
debug/crdb_internal.index_usage_statistics.txt
@@ -559,6 +563,7 @@ debug/crdb_internal.create_function_statements.txt
559563
debug/crdb_internal.create_procedure_statements.txt
560564
debug/crdb_internal.create_schema_statements.txt
561565
debug/crdb_internal.create_statements.txt
566+
debug/crdb_internal.create_trigger_statements.txt
562567
debug/crdb_internal.create_type_statements.txt
563568
debug/crdb_internal.default_privileges.txt
564569
debug/crdb_internal.index_usage_statistics.txt

pkg/cli/zip_table_registry.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,19 @@ var zipInternalTablesPerCluster = DebugZipTableRegistry{
272272
"crdb_internal.hide_sql_constants(create_statement) as create_statement",
273273
},
274274
},
275+
`"".crdb_internal.create_trigger_statements`: {
276+
nonSensitiveCols: NonSensitiveColumns{
277+
"database_id",
278+
"database_name",
279+
"schema_id",
280+
"schema_name",
281+
"table_id",
282+
"table_name",
283+
"trigger_id",
284+
"trigger_name",
285+
"crdb_internal.hide_sql_constants(create_statement) as create_statement",
286+
},
287+
},
275288
`"".crdb_internal.create_procedure_statements`: {
276289
nonSensitiveCols: NonSensitiveColumns{
277290
"database_id",

pkg/cli/zip_upload_table_dumps.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ var clusterWideTableDumps = map[string]columnParserMap{
9999
"crdb_internal.create_type_statements.txt": {},
100100
"crdb_internal.create_procedure_statements.txt": {},
101101
"crdb_internal.create_function_statements.txt": {},
102+
"crdb_internal.create_trigger_statements.txt": {},
102103
"crdb_internal.logical_replication_spans.txt": {},
103104
"crdb_internal.cluster_replication_spans.txt": {},
104105
"system.protected_ts_records.txt": {},

pkg/sql/conn_executor_exec.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3458,7 +3458,8 @@ func (ex *connExecutor) makeExecPlan(
34583458
// session var is set to 'true' ('false' is the default).
34593459
catalog = planner.optPlanningCtx.catalog
34603460
}
3461-
_, err := explain.DecodePlanGistToRows(ctx, &planner.extendedEvalCtx.Context, ih.planGist.String(), catalog)
3461+
planStr := ih.planGist.String()
3462+
_, err := explain.DecodePlanGistToRows(ctx, &planner.extendedEvalCtx.Context, planStr, catalog)
34623463
if err != nil {
34633464
return ctx, errors.NewAssertionErrorWithWrappedErrf(err, "failed to decode plan gist: %q", ih.planGist.String())
34643465
}

pkg/sql/crdb_internal.go

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ var crdbInternal = virtualSchema{
143143
catconstants.CrdbInternalClusterSettingsTableID: crdbInternalClusterSettingsTable,
144144
catconstants.CrdbInternalClusterStmtStatsTableID: crdbInternalClusterStmtStatsTable,
145145
catconstants.CrdbInternalCreateFunctionStmtsTableID: crdbInternalCreateFunctionStmtsTable,
146+
catconstants.CrdbInternalCreateTriggerStmtsTableID: crdbInternalCreateTriggerStmtsTable,
146147
catconstants.CrdbInternalCreateProcedureStmtsTableID: crdbInternalCreateProcedureStmtsTable,
147148
catconstants.CrdbInternalCreateSchemaStmtsTableID: crdbInternalCreateSchemaStmtsTable,
148149
catconstants.CrdbInternalCreateStmtsTableID: crdbInternalCreateStmtsTable,
@@ -3943,6 +3944,189 @@ CREATE TABLE crdb_internal.create_procedure_statements (
39433944
populate: createRoutinePopulate(true /* procedure */),
39443945
}
39453946

3947+
func renderCreateTriggerStatement(
3948+
ctx context.Context,
3949+
p *planner,
3950+
trigger *descpb.TriggerDescriptor,
3951+
tableDesc catalog.TableDescriptor,
3952+
) (string, error) {
3953+
tableTyp, err := p.ResolveTypeByOID(ctx, typedesc.TableIDToImplicitTypeOID(tableDesc.GetID()))
3954+
if err != nil {
3955+
return "", err
3956+
}
3957+
3958+
funcName, err := p.GetQualifiedFunctionNameByID(ctx, int64(trigger.FuncID))
3959+
if err != nil {
3960+
return "", err
3961+
}
3962+
3963+
tableName, err := p.getQualifiedTableName(ctx, tableDesc)
3964+
if err != nil {
3965+
return "", err
3966+
}
3967+
3968+
events := make([]*tree.TriggerEvent, len(trigger.Events))
3969+
for j := range events {
3970+
descEvent := trigger.Events[j]
3971+
events[j] = &tree.TriggerEvent{
3972+
EventType: tree.TriggerEventTypeToTree[descEvent.Type],
3973+
Columns: make(tree.NameList, 0, len(descEvent.ColumnNames)),
3974+
}
3975+
for _, colName := range descEvent.ColumnNames {
3976+
events[j].Columns = append(events[j].Columns, tree.Name(colName))
3977+
}
3978+
}
3979+
3980+
var transitions []*tree.TriggerTransition
3981+
if trigger.NewTransitionAlias != "" {
3982+
transitions = append(transitions, &tree.TriggerTransition{
3983+
IsNew: true,
3984+
Name: tree.Name(trigger.NewTransitionAlias),
3985+
})
3986+
}
3987+
if trigger.OldTransitionAlias != "" {
3988+
transitions = append(transitions, &tree.TriggerTransition{
3989+
IsNew: false,
3990+
Name: tree.Name(trigger.OldTransitionAlias),
3991+
})
3992+
}
3993+
3994+
forEach := tree.TriggerForEachStatement
3995+
if trigger.ForEachRow {
3996+
forEach = tree.TriggerForEachRow
3997+
}
3998+
3999+
var whenExpr tree.Expr
4000+
if trigger.WhenExpr != "" {
4001+
whenExpr, err = schemaexpr.ParseTriggerWhenExprForDisplay(
4002+
ctx, tableTyp, trigger.WhenExpr, p.EvalContext(), p.SemaCtx(), tree.FmtParsable,
4003+
)
4004+
if err != nil {
4005+
return "", err
4006+
}
4007+
}
4008+
4009+
createTrigger := &tree.CreateTrigger{
4010+
Replace: false,
4011+
Name: tree.Name(trigger.Name),
4012+
ActionTime: tree.TriggerActionTimeToTree[trigger.ActionTime],
4013+
Events: events,
4014+
TableName: tableName.ToUnresolvedObjectName(),
4015+
Transitions: transitions,
4016+
ForEach: forEach,
4017+
When: whenExpr,
4018+
FuncName: funcName.ToUnresolvedObjectName().ToUnresolvedName(),
4019+
FuncArgs: trigger.FuncArgs,
4020+
}
4021+
4022+
f := tree.NewFmtCtx(
4023+
tree.FmtParsable,
4024+
tree.FmtDataConversionConfig(p.SessionData().DataConversionConfig),
4025+
tree.FmtLocation(p.SessionData().Location),
4026+
)
4027+
f.FormatNode(createTrigger)
4028+
4029+
return f.CloseAndGetString(), nil
4030+
}
4031+
4032+
func createTriggerPopulate(
4033+
ctx context.Context, p *planner, db catalog.DatabaseDescriptor, addRow func(...tree.Datum) error,
4034+
) error {
4035+
// Skip virtual tables by setting virtualOpts to hideVirtual since they do not have triggers.
4036+
options := forEachTableDescOptions{virtualOpts: hideVirtual}
4037+
return forEachTableDesc(ctx, p, db, options, func(ctx context.Context, tblCtx tableDescContext) error {
4038+
tbl := tblCtx.table
4039+
curDB := tblCtx.database
4040+
sc := tblCtx.schema
4041+
4042+
for _, trig := range tbl.GetTriggers() {
4043+
sql, err := renderCreateTriggerStatement(ctx, p, &trig, tbl)
4044+
if err != nil {
4045+
return err
4046+
}
4047+
4048+
err = addRow(
4049+
tree.NewDInt(tree.DInt(curDB.GetID())), // database_id
4050+
tree.NewDString(curDB.GetName()), // database_name
4051+
tree.NewDInt(tree.DInt(sc.GetID())), // schema_id
4052+
tree.NewDString(sc.GetName()), // schema_name
4053+
tree.NewDInt(tree.DInt(tbl.GetID())), // table_id
4054+
tree.NewDString(tbl.GetName()), // table_name
4055+
tree.NewDInt(tree.DInt(trig.ID)), // trigger_id
4056+
tree.NewDString(trig.Name), // trigger_name
4057+
tree.NewDString(sql), // create_statement
4058+
)
4059+
if err != nil {
4060+
return err
4061+
}
4062+
}
4063+
return nil
4064+
},
4065+
)
4066+
}
4067+
4068+
var crdbInternalCreateTriggerStmtsTable = virtualSchemaTable{
4069+
comment: "CREATE statements for all user-defined triggers.",
4070+
schema: `
4071+
CREATE TABLE crdb_internal.create_trigger_statements (
4072+
database_id INT,
4073+
database_name STRING,
4074+
schema_id INT,
4075+
schema_name STRING,
4076+
table_id INT,
4077+
table_name STRING,
4078+
trigger_id INT,
4079+
trigger_name STRING,
4080+
create_statement STRING,
4081+
INDEX (table_id)
4082+
)`,
4083+
populate: createTriggerPopulate,
4084+
indexes: []virtualIndex{
4085+
{
4086+
populate: func(
4087+
ctx context.Context,
4088+
unwrappedConstraint tree.Datum,
4089+
p *planner,
4090+
db catalog.DatabaseDescriptor,
4091+
addRow func(...tree.Datum) error,
4092+
) (matched bool, err error) {
4093+
tableID := descpb.ID(tree.MustBeDInt(unwrappedConstraint))
4094+
tableDesc, err := p.LookupTableByID(ctx, tableID)
4095+
if err != nil || tableDesc == nil {
4096+
return false, err
4097+
}
4098+
triggers := tableDesc.GetTriggers()
4099+
schemaID := tableDesc.GetParentSchemaID()
4100+
sc, err := p.LookupSchemaByID(ctx, schemaID)
4101+
if err != nil {
4102+
return false, err
4103+
}
4104+
for _, trig := range triggers {
4105+
sql, err := renderCreateTriggerStatement(ctx, p, &trig, tableDesc)
4106+
if err != nil {
4107+
return false, err
4108+
}
4109+
err = addRow(
4110+
tree.NewDInt(tree.DInt(db.GetID())),
4111+
tree.NewDString(db.GetName()),
4112+
tree.NewDInt(tree.DInt(sc.GetID())),
4113+
tree.NewDString(sc.GetName()),
4114+
tree.NewDInt(tree.DInt(tableDesc.GetID())),
4115+
tree.NewDString(tableDesc.GetName()),
4116+
tree.NewDInt(tree.DInt(trig.ID)),
4117+
tree.NewDString(trig.Name),
4118+
tree.NewDString(sql),
4119+
)
4120+
if err != nil {
4121+
return false, err
4122+
}
4123+
}
4124+
return true, nil
4125+
},
4126+
},
4127+
},
4128+
}
4129+
39464130
// Prepare the row populate function.
39474131
var typeView = tree.NewDString("view")
39484132
var typeTable = tree.NewDString("table")

0 commit comments

Comments
 (0)