Vinai/fk errors (#1595)

Adds INSERT IGNORE INTO error handling changes as well as additional bats tests
This commit is contained in:
Vinai Rachakonda
2021-04-29 15:32:00 -04:00
committed by GitHub
parent 8e2ed97c40
commit 68b2b31e40
9 changed files with 129 additions and 37 deletions

View File

@@ -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

View File

@@ -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=

View File

@@ -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,

View File

@@ -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
}

View File

@@ -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))
}

View File

@@ -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")
}
}

View File

@@ -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

View File

@@ -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
}

View File

@@ -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
}