diff --git a/enginetest/queries/script_queries.go b/enginetest/queries/script_queries.go index 29a56d8499..7908239c06 100644 --- a/enginetest/queries/script_queries.go +++ b/enginetest/queries/script_queries.go @@ -2810,7 +2810,7 @@ CREATE TABLE tab3 ( }, { Query: "INSERT INTO test (pk) VALUES (3);", - ExpectedErr: sql.ErrInsertIntoNonNullableDefaultNullColumn, + ExpectedErr: sql.ErrInsertIntoNonNullableProvidedNull, }, { Query: "ALTER TABLE test ALTER v2 DROP DEFAULT;", @@ -2826,7 +2826,7 @@ CREATE TABLE tab3 ( }, { Query: "INSERT INTO test (pk) VALUES (2);", - ExpectedErr: sql.ErrInsertIntoNonNullableDefaultNullColumn, + ExpectedErr: sql.ErrInsertIntoNonNullableProvidedNull, }, { Query: "ALTER TABLE test ALTER v1 SET DEFAULT 100, alter v1 SET DEFAULT 200", diff --git a/enginetest/queries/trigger_queries.go b/enginetest/queries/trigger_queries.go index ff66cff884..a7e64baf19 100644 --- a/enginetest/queries/trigger_queries.go +++ b/enginetest/queries/trigger_queries.go @@ -506,6 +506,92 @@ var TriggerTests = []ScriptTest{ }, }, }, + { + Name: "insert trigger with missing column default value", + SetUpScript: []string{ + "CREATE TABLE t (i INT PRIMARY KEY, j INT NOT NULL);", + ` +CREATE TRIGGER trig BEFORE INSERT ON t +FOR EACH ROW +BEGIN + SET new.j = 10; +END;`, + }, + Assertions: []ScriptTestAssertion{ + { + Query: "INSERT INTO t (i) VALUES (1);", + Expected: []sql.Row{ + {types.OkResult{RowsAffected: 1}}, + }, + }, + { + Query: "INSERT INTO t (i, j) VALUES (2, null);", + Expected: []sql.Row{ + {types.OkResult{RowsAffected: 1}}, + }, + }, + { + Query: "SELECT * FROM t;", + Expected: []sql.Row{ + {1, 10}, + {2, 10}, + }, + }, + }, + }, + { + Name: "not null column with trigger that sets null should error", + SetUpScript: []string{ + "CREATE TABLE t (i INT PRIMARY KEY, j INT NOT NULL);", + ` +CREATE TRIGGER trig BEFORE INSERT ON t +FOR EACH ROW +BEGIN + SET new.j = null; +END;`, + }, + Assertions: []ScriptTestAssertion{ + { + Query: "INSERT INTO t (i) VALUES (1);", + ExpectedErr: sql.ErrInsertIntoNonNullableProvidedNull, + }, + { + Query: "INSERT INTO t (i, j) VALUES (1, 2);", + ExpectedErr: sql.ErrInsertIntoNonNullableProvidedNull, + }, + }, + }, + { + Name: "not null column with before insert trigger should error", + SetUpScript: []string{ + "CREATE TABLE t (i INT PRIMARY KEY, j INT NOT NULL);", + ` +CREATE TRIGGER trig BEFORE INSERT ON t +FOR EACH ROW +BEGIN + SET new.i = 10 * new.i; +END;`, + }, + Assertions: []ScriptTestAssertion{ + { + Query: "INSERT INTO t (i) VALUES (1);", + // TODO: should be sql.ErrInsertIntoNonNullableDefaultNullColumn + ExpectedErr: sql.ErrInsertIntoNonNullableProvidedNull, + }, + { + Query: "INSERT INTO t (i, j) VALUES (1, 2);", + Expected: []sql.Row{ + {types.NewOkResult(1)}, + }, + }, + { + Query: "SELECT * FROM t;", + Expected: []sql.Row{ + {10, 2}, + }, + }, + }, + }, // UPDATE triggers { diff --git a/sql/analyzer/inserts.go b/sql/analyzer/inserts.go index 3c108bcd57..c9221b2c7b 100644 --- a/sql/analyzer/inserts.go +++ b/sql/analyzer/inserts.go @@ -123,15 +123,11 @@ func wrapRowSource(ctx *sql.Context, insertSource sql.Node, destTbl sql.Table, s for i, col := range schema { colIdx := findColIdx(col.Name, columnNames) - // if column was not explicitly specified, try to substitute with default or generated value if colIdx == -1 { defaultExpr := col.Default if defaultExpr == nil { defaultExpr = col.Generated } - if !col.Nullable && defaultExpr == nil && !col.AutoIncrement { - return nil, -1, sql.ErrInsertIntoNonNullableDefaultNullColumn.New(col.Name) - } var err error colNameToIdx := make(map[string]int) diff --git a/sql/rowexec/insert.go b/sql/rowexec/insert.go index 573c5fc7f7..a71dedb5f8 100644 --- a/sql/rowexec/insert.go +++ b/sql/rowexec/insert.go @@ -393,14 +393,19 @@ func (i *insertIter) evaluateChecks(ctx *sql.Context, row sql.Row) error { func (i *insertIter) validateNullability(ctx *sql.Context, dstSchema sql.Schema, row sql.Row) error { for count, col := range dstSchema { - if !col.Nullable && row[count] == nil { + if row[count] != nil { + continue + } + if !col.Nullable { // In the case of an IGNORE we set the nil value to a default and add a warning - if i.ignore { - row[count] = col.Type.Zero() - _ = warnOnIgnorableError(ctx, row, sql.ErrInsertIntoNonNullableProvidedNull.New(col.Name)) // will always return nil - } else { + if !i.ignore { + if col.Default != nil || col.Generated != nil { + return sql.ErrInsertIntoNonNullableDefaultNullColumn.New(col.Name) + } return sql.ErrInsertIntoNonNullableProvidedNull.New(col.Name) } + row[count] = col.Type.Zero() + _ = warnOnIgnorableError(ctx, row, sql.ErrInsertIntoNonNullableProvidedNull.New(col.Name)) // will always return nil } } return nil