mirror of
https://github.com/dolthub/dolt.git
synced 2026-02-11 18:49:14 -06:00
Vinai/fk errors (#1595)
Adds INSERT IGNORE INTO error handling changes as well as additional bats tests
This commit is contained in:
@@ -18,7 +18,7 @@ require (
|
||||
github.com/denisbrodbeck/machineid v1.0.1
|
||||
github.com/dolthub/dolt/go/gen/proto/dolt/services/eventsapi v0.0.0-20201005193433-3ee972b1d078
|
||||
github.com/dolthub/fslock v0.0.2
|
||||
github.com/dolthub/go-mysql-server v0.9.1-0.20210429131117-98b28e801d5e
|
||||
github.com/dolthub/go-mysql-server v0.9.1-0.20210429162929-c9e737f45c0c
|
||||
github.com/dolthub/ishell v0.0.0-20210205014355-16a4ce758446
|
||||
github.com/dolthub/mmap-go v1.0.4-0.20201107010347-f9f2a9588a66
|
||||
github.com/dolthub/sqllogictest/go v0.0.0-20201105013724-5123fc66e12c
|
||||
|
||||
18
go/go.sum
18
go/go.sum
@@ -141,28 +141,14 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/dolthub/fslock v0.0.2 h1:8vUh47iKovgrtXNrXVIzsIoWLlspoXg+3nslhUzgKSw=
|
||||
github.com/dolthub/fslock v0.0.2/go.mod h1:0i7bsNkK+XHwFL3dIsSWeXSV7sykVzzVr6+jq8oeEo0=
|
||||
github.com/dolthub/go-mysql-server v0.9.1-0.20210427170653-4c27cebe7aa4 h1:G7IZ72sHvqp+TVGXOgBIqbUt9/JK2KYTLJYJwhV7W3o=
|
||||
github.com/dolthub/go-mysql-server v0.9.1-0.20210427170653-4c27cebe7aa4/go.mod h1:lKviZJ3QgD8TB9NrGWR080mHRQya+w+YHlme2JF4y5U=
|
||||
github.com/dolthub/go-mysql-server v0.9.1-0.20210427205316-55a901baef57 h1:WScama5rl/VsxTsrE5N3JHFA+RCYVrLKXhyMRGGOUD8=
|
||||
github.com/dolthub/go-mysql-server v0.9.1-0.20210427205316-55a901baef57/go.mod h1:pc7zTsq8iYnNs2TMh6WybMT5SLyBlQkqTggEylUC9d4=
|
||||
github.com/dolthub/go-mysql-server v0.9.1-0.20210428113713-7a761467da9d h1:k1wyWcw4wG+JqmWZO6mBzLcvsLaKMWf9KSbR4YVcVgQ=
|
||||
github.com/dolthub/go-mysql-server v0.9.1-0.20210428113713-7a761467da9d/go.mod h1:QvgaFqG0Mbg3BXS9vXhBltNEG2lsBvkMI+gCzOsetxo=
|
||||
github.com/dolthub/go-mysql-server v0.9.1-0.20210428172146-87a95a1dc3bf h1:+LCmTo9xZvBltnYBIxdCATO/0gf0/9463NJ0h9WWpQw=
|
||||
github.com/dolthub/go-mysql-server v0.9.1-0.20210428172146-87a95a1dc3bf/go.mod h1:raUDli6MJt8QRP7LrXXJqnPrPA0Cjnt4Y/R/HVMGHGw=
|
||||
github.com/dolthub/go-mysql-server v0.9.1-0.20210429131117-98b28e801d5e h1:cxOaU28lUDI3IeI2UUsnT1icXll1LIfqcI2jLGb7hiY=
|
||||
github.com/dolthub/go-mysql-server v0.9.1-0.20210429131117-98b28e801d5e/go.mod h1:raUDli6MJt8QRP7LrXXJqnPrPA0Cjnt4Y/R/HVMGHGw=
|
||||
github.com/dolthub/go-mysql-server v0.9.1-0.20210429162929-c9e737f45c0c h1:f7PePb8AkQogPBzgPzlS2bzwGbIZnr4ScgSoCsJpRgg=
|
||||
github.com/dolthub/go-mysql-server v0.9.1-0.20210429162929-c9e737f45c0c/go.mod h1:raUDli6MJt8QRP7LrXXJqnPrPA0Cjnt4Y/R/HVMGHGw=
|
||||
github.com/dolthub/ishell v0.0.0-20210205014355-16a4ce758446 h1:0ol5pj+QlKUKAtqs1LiPM3ZJKs+rHPgLSsMXmhTrCAM=
|
||||
github.com/dolthub/ishell v0.0.0-20210205014355-16a4ce758446/go.mod h1:dhGBqcCEfK5kuFmeO5+WOx3hqc1k3M29c1oS/R7N4ms=
|
||||
github.com/dolthub/mmap-go v1.0.4-0.20201107010347-f9f2a9588a66 h1:WRPDbpJWEnPxPmiuOTndT+lUWUeGjx6eoNOK9O4tQQQ=
|
||||
github.com/dolthub/mmap-go v1.0.4-0.20201107010347-f9f2a9588a66/go.mod h1:N5ZIbMGuDUpTpOFQ7HcsN6WSIpTGQjHP+Mz27AfmAgk=
|
||||
github.com/dolthub/sqllogictest/go v0.0.0-20201105013724-5123fc66e12c h1:ZIo6IOXU3/rJK4lp83QRq1zGhQrjQQtlmE2b7H1Vv/k=
|
||||
github.com/dolthub/sqllogictest/go v0.0.0-20201105013724-5123fc66e12c/go.mod h1:siLfyv2c92W1eN/R4QqG/+RjjX5W2+gCTRjZxBjI3TY=
|
||||
github.com/dolthub/vitess v0.0.0-20210423125910-e55ee465c2e8 h1:DEFVTB71AdE6vbX1FbtVWRJr+bui8RMfw+U0NnQoGnc=
|
||||
github.com/dolthub/vitess v0.0.0-20210423125910-e55ee465c2e8/go.mod h1:hUE8oSk2H5JZnvtlLBhJPYC8WZCA5AoSntdLTcBvdBM=
|
||||
github.com/dolthub/vitess v0.0.0-20210427161911-b5436daa695a h1:W/pWfKFW8vnLf3LYwMt0rmvTC9fK2P9YBJuYD2XxLT4=
|
||||
github.com/dolthub/vitess v0.0.0-20210427161911-b5436daa695a/go.mod h1:hUE8oSk2H5JZnvtlLBhJPYC8WZCA5AoSntdLTcBvdBM=
|
||||
github.com/dolthub/vitess v0.0.0-20210428104209-a5f7932c1bc0 h1:o7g8HXUa3A+weA86zj5evLNTyG8WFmXXyE7Vv5OWv7k=
|
||||
github.com/dolthub/vitess v0.0.0-20210428104209-a5f7932c1bc0/go.mod h1:hUE8oSk2H5JZnvtlLBhJPYC8WZCA5AoSntdLTcBvdBM=
|
||||
github.com/dolthub/vitess v0.0.0-20210428165934-5801b1103b04 h1:yvfUh1EqwPu10H9VpaUxBEWzqKUmKxsW689ufJWNLsg=
|
||||
github.com/dolthub/vitess v0.0.0-20210428165934-5801b1103b04/go.mod h1:hUE8oSk2H5JZnvtlLBhJPYC8WZCA5AoSntdLTcBvdBM=
|
||||
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
|
||||
@@ -101,7 +101,7 @@ func (chk check) ColsIntersectChanges(changes map[uint64]bool) bool {
|
||||
}
|
||||
|
||||
func (chk check) NewErrForKey(key types.Tuple) error {
|
||||
return &ForeignKeyError{
|
||||
return &GenericForeignKeyError{
|
||||
tableName: chk.fk.TableName,
|
||||
referencedTableName: chk.fk.ReferencedTableName,
|
||||
fkName: chk.fk.Name,
|
||||
|
||||
@@ -19,19 +19,20 @@ import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var ErrForeignKeyConstraintViolation = errors.New("foreign key constraint violation")
|
||||
var ErrGenericForeignKeyConstraintViolation = errors.New("foreign key constraint violation")
|
||||
|
||||
type ForeignKeyError struct {
|
||||
// This represents a commit time violation that is different from the FK errors in go-mysql-server (sql/errors.go)
|
||||
type GenericForeignKeyError struct {
|
||||
tableName string
|
||||
referencedTableName string
|
||||
fkName string
|
||||
keyStr string
|
||||
}
|
||||
|
||||
func (err *ForeignKeyError) Error() string {
|
||||
func (err *GenericForeignKeyError) Error() string {
|
||||
return fmt.Sprintf("Foreign key violation on fk: `%s`, table: `%s`, referenced table: `%s`, key: `%s`", err.fkName, err.tableName, err.referencedTableName, err.keyStr)
|
||||
}
|
||||
|
||||
func (err *ForeignKeyError) Unwrap() error {
|
||||
return ErrForeignKeyConstraintViolation
|
||||
func (err *GenericForeignKeyError) Unwrap() error {
|
||||
return ErrGenericForeignKeyConstraintViolation
|
||||
}
|
||||
|
||||
@@ -145,6 +145,10 @@ func TestInsertInto(t *testing.T) {
|
||||
enginetest.TestInsertInto(t, newDoltHarness(t))
|
||||
}
|
||||
|
||||
func TestInsertIgnoreInto(t *testing.T) {
|
||||
enginetest.TestInsertIgnoreInto(t, newDoltHarness(t))
|
||||
}
|
||||
|
||||
func TestInsertIntoErrors(t *testing.T) {
|
||||
enginetest.TestInsertIntoErrors(t, newDoltHarness(t))
|
||||
}
|
||||
|
||||
@@ -1519,19 +1519,19 @@ INSERT INTO child_non_unq VALUES ('1', 1), ('2', NULL), ('3', 3), ('4', 3), ('5'
|
||||
// insert tests against foreign key
|
||||
_, err = ExecuteSql(dEnv, root, "INSERT INTO child VALUES ('9', 9)")
|
||||
if assert.Error(t, err) {
|
||||
assert.Contains(t, err.Error(), "foreign key violation")
|
||||
assert.Contains(t, err.Error(), "Foreign key violation")
|
||||
}
|
||||
_, err = ExecuteSql(dEnv, root, "INSERT INTO child_idx VALUES ('9', 9)")
|
||||
if assert.Error(t, err) {
|
||||
assert.Contains(t, err.Error(), "foreign key violation")
|
||||
assert.Contains(t, err.Error(), "Foreign key violation")
|
||||
}
|
||||
_, err = ExecuteSql(dEnv, root, "INSERT INTO child_unq VALUES ('9', 9)")
|
||||
if assert.Error(t, err) {
|
||||
assert.Contains(t, err.Error(), "foreign key violation")
|
||||
assert.Contains(t, err.Error(), "Foreign key violation")
|
||||
}
|
||||
_, err = ExecuteSql(dEnv, root, "INSERT INTO child_non_unq VALUES ('9', 9)")
|
||||
if assert.Error(t, err) {
|
||||
assert.Contains(t, err.Error(), "foreign key violation")
|
||||
assert.Contains(t, err.Error(), "Foreign key violation")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,11 +18,12 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/dolthub/dolt/go/store/hash"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/row"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
|
||||
"github.com/dolthub/dolt/go/store/hash"
|
||||
"github.com/dolthub/dolt/go/store/types"
|
||||
)
|
||||
|
||||
@@ -198,8 +199,7 @@ func (ste *sessionedTableEditor) handleReferencingRowsOnDelete(ctx context.Conte
|
||||
}
|
||||
case doltdb.ForeignKeyReferenceOption_DefaultAction, doltdb.ForeignKeyReferenceOption_NoAction, doltdb.ForeignKeyReferenceOption_Restrict:
|
||||
indexKeyStr, _ := types.EncodedValue(ctx, indexKey)
|
||||
return fmt.Errorf("foreign key constraint violation on `%s`.`%s`: cannot delete rows with value `%s`",
|
||||
foreignKey.TableName, foreignKey.Name, indexKeyStr)
|
||||
return sql.ErrForeignKeyChildViolation.New(foreignKey.Name, foreignKey.TableName, foreignKey.ReferencedTableName, indexKeyStr)
|
||||
default:
|
||||
return fmt.Errorf("unknown ON DELETE reference option on `%s`: `%s`", foreignKey.Name, foreignKey.OnDelete.String())
|
||||
}
|
||||
@@ -305,8 +305,7 @@ func (ste *sessionedTableEditor) handleReferencingRowsOnUpdate(ctx context.Conte
|
||||
}
|
||||
case doltdb.ForeignKeyReferenceOption_DefaultAction, doltdb.ForeignKeyReferenceOption_NoAction, doltdb.ForeignKeyReferenceOption_Restrict:
|
||||
indexKeyStr, _ := types.EncodedValue(ctx, indexKey)
|
||||
return fmt.Errorf("foreign key constraint violation on `%s`.`%s`: cannot update rows with value `%s`",
|
||||
foreignKey.TableName, foreignKey.Name, indexKeyStr)
|
||||
return sql.ErrForeignKeyParentViolation.New(foreignKey.Name, foreignKey.TableName, foreignKey.ReferencedTableName, indexKeyStr)
|
||||
default:
|
||||
return fmt.Errorf("unknown ON UPDATE reference option on `%s`: `%s`", foreignKey.Name, foreignKey.OnUpdate.String())
|
||||
}
|
||||
@@ -439,7 +438,7 @@ func (ste *sessionedTableEditor) validateForInsert(ctx context.Context, taggedVa
|
||||
}
|
||||
}
|
||||
indexKeyStr, _ := types.EncodedValue(ctx, indexKey)
|
||||
return fmt.Errorf("foreign key violation on `%s`.`%s`: `%s`", foreignKey.TableName, foreignKey.Name, indexKeyStr)
|
||||
return sql.ErrForeignKeyChildViolation.New(foreignKey.Name, foreignKey.TableName, foreignKey.ReferencedTableName, indexKeyStr)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -1708,3 +1708,74 @@ SQL
|
||||
[ "$status" -eq "0" ]
|
||||
[[ "$output" =~ 'child2_fk' ]] || false
|
||||
}
|
||||
|
||||
@test "foreign-keys: child violation correctly detected" {
|
||||
dolt sql <<SQL
|
||||
CREATE TABLE colors (
|
||||
id INT NOT NULL,
|
||||
color VARCHAR(32) NOT NULL,
|
||||
|
||||
PRIMARY KEY (id),
|
||||
INDEX color_index(color)
|
||||
);
|
||||
CREATE TABLE objects (
|
||||
id INT NOT NULL,
|
||||
name VARCHAR(64) NOT NULL,
|
||||
color VARCHAR(32),
|
||||
|
||||
PRIMARY KEY(id),
|
||||
CONSTRAINT color_fk FOREIGN KEY (color) REFERENCES colors(color)
|
||||
);
|
||||
INSERT INTO colors (id,color) VALUES (1,'red'),(2,'green'),(3,'blue'),(4,'purple');
|
||||
INSERT INTO objects (id,name,color) VALUES (1,'truck','red'),(2,'ball','green'),(3,'shoe','blue');
|
||||
SQL
|
||||
|
||||
# Run a query and assert that no changes were made
|
||||
run dolt sql -q "DELETE FROM colors where color='green'"
|
||||
[ "$status" -eq "1" ]
|
||||
[[ "$output" =~ 'cannot add or update a child row - Foreign key violation on fk: `color_fk`, table: `objects`, referenced table: `colors`, key: `(2162,"green")`' ]] || false
|
||||
}
|
||||
|
||||
@test "foreign-keys: insert ignore into works correctly w/ FK violations" {
|
||||
dolt sql <<SQL
|
||||
CREATE TABLE colors (
|
||||
id INT NOT NULL,
|
||||
color VARCHAR(32) NOT NULL,
|
||||
|
||||
PRIMARY KEY (id),
|
||||
INDEX color_index(color)
|
||||
);
|
||||
CREATE TABLE objects (
|
||||
id INT NOT NULL,
|
||||
name VARCHAR(64) NOT NULL,
|
||||
color VARCHAR(32),
|
||||
|
||||
PRIMARY KEY(id),
|
||||
CONSTRAINT color_fk FOREIGN KEY (color) REFERENCES colors(color)
|
||||
);
|
||||
INSERT INTO colors (id,color) VALUES (1,'red'),(2,'green'),(3,'blue'),(4,'purple');
|
||||
INSERT INTO objects (id,name,color) VALUES (1,'truck','red'),(2,'ball','green'),(3,'shoe','blue');
|
||||
SQL
|
||||
|
||||
# Run a query and assert that no changes were made
|
||||
run dolt sql -q "INSERT IGNORE INTO objects (id,name,color) VALUES (5, 'hi', 'yellow');"
|
||||
[ "$status" -eq "0" ]
|
||||
[[ "$output" =~ 'Query OK, 0 rows affected' ]] || false
|
||||
|
||||
# Validate the data is correct
|
||||
run dolt sql -q "SELECT * FROM objects ORDER BY id" -r csv
|
||||
[ "$status" -eq "0" ]
|
||||
[[ $output =~ 'id,name,color' ]] || false
|
||||
[[ "$output" =~ '1,truck,red' ]] || false
|
||||
[[ "$output" =~ '2,ball,green' ]] || false
|
||||
[[ "$output" =~ '3,shoe,blue' ]] || false
|
||||
|
||||
# Run the query again and this time assert warnings
|
||||
run dolt sql <<SQL
|
||||
INSERT IGNORE INTO objects (id,name,color) VALUES (5, 'hi', 'yellow');
|
||||
SHOW WARNINGS;
|
||||
SQL
|
||||
[ "$status" -eq "0" ]
|
||||
[[ "$output" =~ '1452' ]] || false # first ensure the proper code
|
||||
[[ "$output" =~ 'cannot add or update a child row - Foreign key violation on fk: `color_fk`, table: `objects`, referenced table: `colors`, key: `(4011,"yellow")`' ]] || false
|
||||
}
|
||||
@@ -2449,14 +2449,45 @@ SQL
|
||||
# INSERT against foreign key
|
||||
run dolt sql -q "INSERT INTO child VALUES ('9', 9)"
|
||||
[ "$status" -eq "1" ]
|
||||
[[ "$output" =~ "foreign key violation" ]] || false
|
||||
[[ "$output" =~ "Foreign key violation" ]] || false
|
||||
run dolt sql -q "INSERT INTO child_idx VALUES ('9', 9)"
|
||||
[ "$status" -eq "1" ]
|
||||
[[ "$output" =~ "foreign key violation" ]] || false
|
||||
[[ "$output" =~ "Foreign key violation" ]] || false
|
||||
run dolt sql -q "INSERT INTO child_unq VALUES ('9', 9)"
|
||||
[ "$status" -eq "1" ]
|
||||
[[ "$output" =~ "foreign key violation" ]] || false
|
||||
[[ "$output" =~ "Foreign key violation" ]] || false
|
||||
run dolt sql -q "INSERT INTO child_non_unq VALUES ('9', 9)"
|
||||
[ "$status" -eq "1" ]
|
||||
[[ "$output" =~ "foreign key violation" ]] || false
|
||||
[[ "$output" =~ "Foreign key violation" ]] || false
|
||||
}
|
||||
|
||||
@test "index: INSERT IGNORE INTO with unique key violations ignores correctly" {
|
||||
dolt sql -q "CREATE TABLE mytable(pk int PRIMARY KEY, name varchar(20) UNIQUE)"
|
||||
|
||||
dolt sql -q "INSERT INTO mytable values (1,'jon')"
|
||||
|
||||
# Try the repeat and assert an error
|
||||
run dolt sql -q "INSERT INTO mytable values (2,'jon')"
|
||||
[ "$status" -eq "1" ]
|
||||
[[ "$output" =~ "duplicate unique key given: [1]" ]] || false
|
||||
|
||||
# try with ignore
|
||||
run dolt sql << SQL
|
||||
INSERT IGNORE INTO mytable values (2,'jon');
|
||||
SHOW WARNINGS;
|
||||
SQL
|
||||
[ "$status" -eq "0" ]
|
||||
[[ "$output" =~ '1062' ]] || false # First Validate the correct code
|
||||
|
||||
# Now try again to assert the 0 rows affected
|
||||
run dolt sql -q "INSERT IGNORE INTO mytable values (2,'jon');"
|
||||
[ "$status" -eq "0" ]
|
||||
[[ "$output" =~ 'Query OK, 0 rows affected' ]] || false
|
||||
|
||||
run dolt sql -r csv -q "SELECT COUNT(*) FROM mytable"
|
||||
[ "$status" -eq "0" ]
|
||||
[[ "$output" =~ "1" ]] || false
|
||||
|
||||
run dolt sql -r csv -q "SELECT * FROM mytable"
|
||||
[[ "$output" =~ "1,jon" ]] || false
|
||||
}
|
||||
Reference in New Issue
Block a user