Skip to content

Commit bfcf53b

Browse files
author
James Cor
committed
allow before insert trigger to specify missing column
1 parent f9c30b3 commit bfcf53b

File tree

5 files changed

+109
-25
lines changed

5 files changed

+109
-25
lines changed

enginetest/queries/trigger_queries.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,32 @@ var TriggerTests = []ScriptTest{
506506
},
507507
},
508508
},
509+
{
510+
Name: "insert trigger with missing column default value",
511+
SetUpScript: []string{
512+
"CREATE TABLE t (i INT PRIMARY KEY, j INT NOT NULL);",
513+
`
514+
CREATE TRIGGER trig BEFORE INSERT ON t
515+
FOR EACH ROW
516+
BEGIN
517+
SET new.j = 10;
518+
END;`,
519+
},
520+
Assertions: []ScriptTestAssertion{
521+
{
522+
Query: "INSERT INTO t (i) VALUES (1);",
523+
Expected: []sql.Row{
524+
{types.OkResult{RowsAffected: 1}},
525+
},
526+
},
527+
{
528+
Query: "SELECT * FROM t;",
529+
Expected: []sql.Row{
530+
{1, 10},
531+
},
532+
},
533+
},
534+
},
509535

510536
// UPDATE triggers
511537
{

sql/analyzer/inserts.go

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,65 @@ func resolveInsertRows(ctx *sql.Context, a *Analyzer, n sql.Node, scope *plan.Sc
9090
})
9191
}
9292

93+
func validateInsertRows(ctx *sql.Context, a *Analyzer, n sql.Node, scope *plan.Scope, sel RuleSelector, qFlags *sql.QueryFlags) (sql.Node, transform.TreeIdentity, error) {
94+
if _, ok := n.(*plan.TriggerExecutor); ok {
95+
return n, transform.SameTree, nil
96+
} else if _, ok := n.(*plan.CreateProcedure); ok {
97+
return n, transform.SameTree, nil
98+
}
99+
// We capture all INSERTs along the tree, such as those inside of block statements.
100+
var err error
101+
transform.Inspect(n, func(n sql.Node) bool {
102+
insert, ok := n.(*plan.InsertInto)
103+
if !ok {
104+
return true
105+
}
106+
107+
var insertable sql.InsertableTable
108+
table := getResolvedTable(insert.Destination)
109+
insertable, err = plan.GetInsertable(table)
110+
if err != nil {
111+
return false
112+
}
113+
114+
source := insert.Source
115+
dstSchema := insertable.Schema()
116+
117+
// normalize the column name
118+
columnNames := make([]string, len(insert.ColumnNames))
119+
for i, name := range insert.ColumnNames {
120+
columnNames[i] = strings.ToLower(name)
121+
}
122+
123+
// If no columns are given and value tuples are not all empty, use the full schema
124+
if len(columnNames) == 0 && existsNonZeroValueCount(source) {
125+
columnNames = make([]string, len(dstSchema))
126+
for i, f := range dstSchema {
127+
columnNames[i] = f.Name
128+
}
129+
}
130+
131+
for _, col := range dstSchema {
132+
colIdx := findColIdx(col.Name, columnNames)
133+
if colIdx != -1 {
134+
continue
135+
}
136+
137+
if _, isTrigExec := source.(*plan.TriggerExecutor); !isTrigExec && !col.AutoIncrement && !col.Nullable && col.Default == nil && col.Generated == nil {
138+
err = sql.ErrInsertIntoNonNullableDefaultNullColumn.New(col.Name)
139+
return false
140+
}
141+
}
142+
143+
return true
144+
})
145+
146+
if err != nil {
147+
return nil, transform.SameTree, err
148+
}
149+
return n, transform.SameTree, nil
150+
}
151+
93152
// Ensures that the number of elements in each Value tuple is empty
94153
func existsNonZeroValueCount(values sql.Node) bool {
95154
switch node := values.(type) {
@@ -123,15 +182,11 @@ func wrapRowSource(ctx *sql.Context, insertSource sql.Node, destTbl sql.Table, s
123182

124183
for i, col := range schema {
125184
colIdx := findColIdx(col.Name, columnNames)
126-
// if column was not explicitly specified, try to substitute with default or generated value
127185
if colIdx == -1 {
128186
defaultExpr := col.Default
129187
if defaultExpr == nil {
130188
defaultExpr = col.Generated
131189
}
132-
if !col.Nullable && defaultExpr == nil && !col.AutoIncrement {
133-
return nil, -1, sql.ErrInsertIntoNonNullableDefaultNullColumn.New(col.Name)
134-
}
135190

136191
var err error
137192
colNameToIdx := make(map[string]int)

sql/analyzer/rule_ids.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ const (
6161
applyHashInId // applyHashIn
6262
resolveInsertRowsId // resolveInsertRows
6363
applyTriggersId // applyTriggers
64+
validateInsertRowsId // validateInsertRows
6465
applyProceduresId // applyProcedures
6566
assignRoutinesId // assignRoutines
6667
modifyUpdateExprsForJoinId // modifyUpdateExprsForJoin

sql/analyzer/ruleid_string.go

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

sql/analyzer/rules.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ func init() {
2020
// resolveInsertRows inserts a projection wrapping values that cannot be seen by fixup
2121
{resolveInsertRowsId, resolveInsertRows},
2222
{applyTriggersId, applyTriggers},
23+
{validateInsertRowsId, validateInsertRows},
2324
{applyProceduresId, applyProcedures},
2425
{inlineSubqueryAliasRefsId, inlineSubqueryAliasRefs},
2526
{cacheSubqueryAliasesInJoinsId, cacheSubqueryAliasesInJoins},

0 commit comments

Comments
 (0)