Foreign key remnants

This commit is contained in:
Daylon Wilkins
2020-06-29 11:45:36 -07:00
committed by Daylon Wilkins
parent e6965be63d
commit eb65425e87
18 changed files with 400 additions and 44 deletions

View File

@@ -634,6 +634,55 @@ SQL
[[ "$output" =~ "does not have column" ]] || false
}
@test "foreign-keys: CREATE TABLE SET NULL on non-nullable column" {
dolt sql <<SQL
CREATE TABLE parent (
id BIGINT PRIMARY KEY,
extra BIGINT
);
SQL
run dolt sql <<SQL
CREATE TABLE child (
id BIGINT PRIMARY KEY,
parent_extra BIGINT NOT NULL,
CONSTRAINT fk_name FOREIGN KEY (parent_extra)
REFERENCES parent(extra)
ON DELETE SET NULL
);
SQL
[ "$status" -eq "1" ]
[[ "$output" =~ "SET NULL" ]] || false
[[ "$output" =~ "parent_extra" ]] || false
run dolt sql <<SQL
CREATE TABLE child (
id BIGINT PRIMARY KEY,
parent_extra BIGINT NOT NULL,
CONSTRAINT fk_name FOREIGN KEY (parent_extra)
REFERENCES parent(extra)
ON UPDATE SET NULL
);
SQL
[ "$status" -eq "1" ]
[[ "$output" =~ "SET NULL" ]] || false
[[ "$output" =~ "parent_extra" ]] || false
run dolt sql <<SQL
CREATE TABLE child (
id BIGINT PRIMARY KEY,
parent_extra BIGINT NOT NULL,
CONSTRAINT fk_name FOREIGN KEY (parent_extra)
REFERENCES parent(extra)
ON DELETE SET NULL
ON UPDATE SET NULL
);
SQL
[ "$status" -eq "1" ]
[[ "$output" =~ "SET NULL" ]] || false
[[ "$output" =~ "parent_extra" ]] || false
}
@test "foreign-keys: ALTER TABLE Single Unnamed FOREIGN KEY" {
dolt sql <<SQL
CREATE TABLE parent (
@@ -1149,6 +1198,54 @@ SQL
[ "$status" -eq "1" ]
}
@test "foreign-keys: ALTER TABLE SET NULL on non-nullable column" {
dolt sql <<SQL
CREATE TABLE parent (
id BIGINT PRIMARY KEY,
extra BIGINT
);
CREATE TABLE child (
id BIGINT PRIMARY KEY,
parent_extra BIGINT NOT NULL
);
SQL
run dolt sql -q "ALTER TABLE child ADD CONSTRAINT fk_name FOREIGN KEY (parent_extra) REFERENCES parent(extra) ON DELETE SET NULL"
[ "$status" -eq "1" ]
[[ "$output" =~ "SET NULL" ]] || false
[[ "$output" =~ "parent_extra" ]] || false
run dolt sql -q "ALTER TABLE child ADD CONSTRAINT fk_name FOREIGN KEY (parent_extra) REFERENCES parent(extra) ON UPDATE SET NULL"
[ "$status" -eq "1" ]
[[ "$output" =~ "SET NULL" ]] || false
[[ "$output" =~ "parent_extra" ]] || false
run dolt sql -q "ALTER TABLE child ADD CONSTRAINT fk_name FOREIGN KEY (parent_extra) REFERENCES parent(extra) ON DELETE SET NULL ON UPDATE SET NULL"
[ "$status" -eq "1" ]
[[ "$output" =~ "SET NULL" ]] || false
[[ "$output" =~ "parent_extra" ]] || false
}
@test "foreign-keys: ADD FOREIGN KEY fails on existing table when data would cause violation" {
dolt sql <<SQL
CREATE TABLE parent (
pk BIGINT PRIMARY KEY,
v1 BIGINT
);
CREATE TABLE child (
pk BIGINT PRIMARY KEY,
v1 BIGINT
);
INSERT INTO parent VALUES (1, 1), (2, 2);
INSERT INTO child VALUES (1, 1), (2, 3);
SQL
run dolt sql -q "ALTER TABLE child ADD CONSTRAINT fk_name FOREIGN KEY (v1) REFERENCES parent(v1)"
[ "$status" -eq "1" ]
[[ "$output" =~ "violation" ]] || false
[[ "$output" =~ "fk_name" ]] || false
}
@test "foreign-keys: RENAME TABLE" {
dolt sql <<SQL
CREATE TABLE parent (
@@ -1258,6 +1355,36 @@ SQL
dolt table rm parent
}
@test "foreign-keys: dolt table cp" {
dolt sql <<SQL
CREATE TABLE one (
id BIGINT PRIMARY KEY,
extra BIGINT
);
CREATE TABLE two (
id BIGINT PRIMARY KEY,
one_extra BIGINT,
FOREIGN KEY (one_extra)
REFERENCES one(extra)
);
SQL
dolt table cp two two_new
run dolt index ls two_new
[ "$status" -eq "0" ]
[[ "$output" =~ "No indexes" ]] || false
run dolt schema show two_new
[ "$status" -eq "0" ]
! [[ "$output" =~ "FOREIGN KEY" ]] || false
run dolt index ls two
[ "$status" -eq "0" ]
! [[ "$output" =~ "No indexes" ]] || false
run dolt schema show two
[ "$status" -eq "0" ]
[[ "$output" =~ "FOREIGN KEY" ]] || false
}
@test "foreign-keys: ALTER TABLE RENAME COLUMN" {
dolt sql <<SQL
CREATE TABLE parent (
@@ -1278,6 +1405,63 @@ SQL
[[ `echo "$output" | tr -d "\n" | tr -s " "` =~ 'CONSTRAINT `fk_child_parent_1` FOREIGN KEY (`parent_id_new`) REFERENCES `parent` (`id_new`)' ]] || false
}
@test "foreign-keys: DROP COLUMN" {
dolt sql <<SQL
CREATE TABLE parent (
id BIGINT PRIMARY KEY,
extra BIGINT
);
CREATE TABLE child (
id BIGINT PRIMARY KEY,
parent_extra BIGINT,
CONSTRAINT fk_name FOREIGN KEY (parent_extra)
REFERENCES parent(extra)
);
SQL
dolt add -A
dolt commit -m "initial commit"
run dolt sql -q "ALTER TABLE parent DROP COLUMN extra"
[ "$status" -eq "1" ]
[[ "$output" =~ "extra" ]] || false
dolt sql -q "ALTER TABLE child DROP FOREIGN KEY fk_name"
dolt sql -q "ALTER TABLE parent DROP COLUMN extra"
dolt reset --hard
run dolt sql -q "ALTER TABLE child DROP COLUMN parent_extra"
[ "$status" -eq "1" ]
[[ "$output" =~ "parent_extra" ]] || false
dolt sql -q "ALTER TABLE child DROP FOREIGN KEY fk_name"
dolt sql -q "ALTER TABLE child DROP COLUMN parent_extra"
}
@test "foreign-keys: Disallow change column type when SET NULL" {
dolt sql <<SQL
CREATE TABLE parent (
id BIGINT PRIMARY KEY,
extra BIGINT
);
CREATE TABLE child (
id BIGINT PRIMARY KEY,
parent_extra BIGINT,
CONSTRAINT fk_name FOREIGN KEY (parent_extra)
REFERENCES parent(extra)
ON DELETE SET NULL
ON UPDATE SET NULL
);
SQL
run dolt sql -q "ALTER TABLE child CHANGE COLUMN parent_extra parent_extra BIGINT"
[ "$status" -eq "1" ]
[[ "$output" =~ "SET NULL" ]] || false
[[ "$output" =~ "parent_extra" ]] || false
run dolt sql -q "ALTER TABLE child CHANGE COLUMN parent_extra parent_extra BIGINT NULL"
[ "$status" -eq "1" ]
[[ "$output" =~ "SET NULL" ]] || false
[[ "$output" =~ "parent_extra" ]] || false
}
@test "foreign-keys: SQL CASCADE" {
dolt sql <<SQL
CREATE TABLE one (
@@ -1449,6 +1633,39 @@ SQL
[[ "$output" =~ "violation" ]] || false
}
@test "foreign-keys: SQL INSERT multiple keys violates only one" {
dolt sql <<SQL
CREATE TABLE one (
pk BIGINT PRIMARY KEY,
v1 BIGINT,
v2 BIGINT
);
CREATE TABLE two (
pk BIGINT PRIMARY KEY,
v1 BIGINT,
v2 BIGINT,
CONSTRAINT fk_name_1 FOREIGN KEY (v1)
REFERENCES one(v1),
CONSTRAINT fk_name_2 FOREIGN KEY (v2)
REFERENCES one(v2)
);
INSERT INTO one VALUES (1, 1, 1), (2, 2, 2), (3, 3, 3);
INSERT INTO two VALUES (1, NULL, 1);
SQL
run dolt sql -q "INSERT INTO two VALUES (2, NULL, 4)"
[ "$status" -eq "1" ]
[[ "$output" =~ "violation" ]] || false
[[ "$output" =~ "fk_name_2" ]] || false
run dolt sql -q "INSERT INTO two VALUES (3, 4, NULL)"
[ "$status" -eq "1" ]
[[ "$output" =~ "violation" ]] || false
[[ "$output" =~ "fk_name_1" ]] || false
dolt sql -q "INSERT INTO two VALUES (4, NULL, NULL)" # sanity check
}
@test "foreign-keys: dolt table import" {
dolt sql <<SQL
CREATE TABLE parent (
@@ -1923,7 +2140,7 @@ SQL
dolt checkout other
dolt sql <<SQL
ALTER TABLE child DROP FOREIGN KEY fk_name;
SET FOREIGN_KEY_CHECKS=0;
UPDATE parent SET v1 = v1 - 1;
SQL
dolt add -A
@@ -1976,7 +2193,7 @@ SQL
dolt checkout other
dolt sql <<SQL
ALTER TABLE child DROP FOREIGN KEY fk_name;
SET FOREIGN_KEY_CHECKS=0;
UPDATE parent SET v1 = v1 - 1;
SQL
dolt add -A
@@ -2016,7 +2233,7 @@ SQL
dolt checkout other
dolt sql <<SQL
ALTER TABLE child DROP FOREIGN KEY fk_name;
SET FOREIGN_KEY_CHECKS=0;
UPDATE child SET v1 = v1 + 1;
SQL
dolt add -A
@@ -2069,7 +2286,7 @@ SQL
dolt checkout other
dolt sql <<SQL
ALTER TABLE child DROP FOREIGN KEY fk_name;
SET FOREIGN_KEY_CHECKS=0;
UPDATE child SET v1 = v1 - 1;
SQL
dolt add -A
@@ -2109,7 +2326,7 @@ SQL
dolt checkout other
dolt sql <<SQL
ALTER TABLE child DROP FOREIGN KEY fk_name;
SET FOREIGN_KEY_CHECKS=0;
UPDATE parent SET v1 = v1 - 1;
UPDATE child SET v1 = v1 + 1;
SQL
@@ -2164,7 +2381,7 @@ SQL
dolt checkout other
dolt sql <<SQL
ALTER TABLE child DROP FOREIGN KEY fk_name;
SET FOREIGN_KEY_CHECKS=0;
UPDATE parent SET v1 = v1 - 1;
UPDATE child SET v1 = v1 + 1;
SQL

View File

@@ -578,7 +578,7 @@ func createEnvWithSeedData(t *testing.T) *env.DoltEnv {
}
wrSch := wr.GetSchema()
wrSch.Indexes().Merge(sch.Indexes().AllIndexes()...)
wrSch.Indexes().Merge(true, sch.Indexes().AllIndexes()...)
err = dEnv.PutTableToWorking(context.Background(), *wr.GetMap(), wrSch, tableName)
if err != nil {

View File

@@ -231,7 +231,7 @@ func newTableCopyDataMover(ctx context.Context, root *doltdb.RootValue, fs files
}
newTblSch := schema.SchemaFromCols(cc)
newTblSch.Indexes().Merge(oldTblSch.Indexes().AllIndexes()...)
newTblSch.Indexes().Merge(false, oldTblSch.Indexes().AllIndexes()...)
transforms, err := mvdata.NameMapTransform(oldTblSch, newTblSch, make(rowconv.NameMapper))

View File

@@ -23,13 +23,14 @@
package eventsapi
import (
reflect "reflect"
sync "sync"
proto "github.com/golang/protobuf/proto"
duration "github.com/golang/protobuf/ptypes/duration"
timestamp "github.com/golang/protobuf/ptypes/timestamp"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (

View File

@@ -4,6 +4,7 @@ package eventsapi
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"

View File

@@ -23,11 +23,12 @@
package eventsapi
import (
reflect "reflect"
sync "sync"
proto "github.com/golang/protobuf/proto"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (

View File

@@ -21,11 +21,12 @@
package remotesapi
import (
reflect "reflect"
sync "sync"
proto "github.com/golang/protobuf/proto"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (

View File

@@ -4,6 +4,7 @@ package remotesapi
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"

View File

@@ -21,11 +21,12 @@
package remotesapi
import (
reflect "reflect"
sync "sync"
proto "github.com/golang/protobuf/proto"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (

View File

@@ -4,6 +4,7 @@ package remotesapi
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"

View File

@@ -17,10 +17,12 @@ package doltdb
import (
"context"
"fmt"
"io"
"sort"
"github.com/liquidata-inc/dolt/go/libraries/doltcore/row"
"github.com/liquidata-inc/dolt/go/libraries/doltcore/schema"
"github.com/liquidata-inc/dolt/go/libraries/doltcore/table/typed/noms"
"github.com/liquidata-inc/dolt/go/store/marshal"
"github.com/liquidata-inc/dolt/go/store/types"
)
@@ -298,6 +300,73 @@ func (fkc *ForeignKeyCollection) Stage(ctx context.Context, fksToAdd []*ForeignK
}
}
// ValidateData ensures that the foreign key is valid by comparing the index data from the given table against the index
// data from the referenced table.
func (fk *ForeignKey) ValidateData(ctx context.Context, tableIndexData types.Map, refTableIndex schema.Index, refTableIndexData types.Map) error {
if fk.ReferencedTableIndex != refTableIndex.Name() {
return fmt.Errorf("cannot validate data as wrong referenced index was given: expected `%s` but received `%s`",
fk.ReferencedTableIndex, refTableIndex.Name())
}
refTableIndexSch := refTableIndex.Schema()
err := tableIndexData.Iter(ctx, func(key, value types.Value) (stop bool, err error) {
indexTaggedValues, err := row.ParseTaggedValues(key.(types.Tuple))
if err != nil {
return true, err
}
refIndexKeyVals := make([]types.Value, len(fk.TableColumns)*2)
for i, colTag := range fk.TableColumns {
val, ok := indexTaggedValues[colTag]
if !ok {
return true, fmt.Errorf("cannot find value for tag `%d` on table `%s`", colTag, fk.TableName)
}
newTag := fk.ReferencedTableColumns[i]
refIndexKeyVals[2*i] = types.Uint(newTag)
refIndexKeyVals[2*i+1] = val
}
refIndexKey, err := types.NewTuple(refTableIndexData.Format(), refIndexKeyVals...)
if err != nil {
return true, err
}
indexIter := noms.NewNomsRangeReader(refTableIndexSch, refTableIndexData,
[]*noms.ReadRange{{Start: refIndexKey, Inclusive: true, Reverse: false, Check: func(tuple types.Tuple) (bool, error) {
return tuple.StartsWith(refIndexKey), nil
}}},
)
_, err = indexIter.ReadRow(ctx)
if err == nil { // row exists
return false, nil
} else if err != io.EOF {
return true, err
} else {
indexKeyStr, _ := types.EncodedValue(ctx, refIndexKey)
return true, fmt.Errorf("foreign key violation on `%s`.`%s`: `%s`", fk.Name, fk.TableName, indexKeyStr)
}
})
return err
}
// Equals returns whether the given foreign key is equivalent to another. As tags are unique, we can compare using those
// and ignore the table names, ensuring equality even through table and column renames.
func (fk *ForeignKey) Equals(other *ForeignKey) bool {
if len(fk.TableColumns) != len(other.TableColumns) || len(fk.ReferencedTableColumns) != len(other.ReferencedTableColumns) {
return false
}
for i := range fk.TableColumns {
if fk.TableColumns[i] != other.TableColumns[i] {
return false
}
}
for i := range fk.ReferencedTableColumns {
if fk.ReferencedTableColumns[i] != other.ReferencedTableColumns[i] {
return false
}
}
return fk.Name == other.Name &&
fk.OnUpdate == other.OnUpdate &&
fk.OnDelete == other.OnDelete
}
// ValidateReferencedTableSchema verifies that the given schema matches the expectation of the referenced table.
func (fk *ForeignKey) ValidateReferencedTableSchema(sch schema.Schema) error {
allSchCols := sch.GetAllCols()

View File

@@ -315,24 +315,12 @@ func (ste *SessionedTableEditor) validateForInsert(ctx context.Context, dRow row
return nil
}
for _, foreignKey := range ste.referencedTables {
// first check if it's all nulls, as they are always valid to INSERT
allNulls := true
for _, colTag := range foreignKey.TableColumns {
val, ok := dRow.GetColVal(colTag)
if ok && !types.IsNull(val) {
allNulls = false
}
}
if allNulls {
return nil
}
indexKey, hasNulls, err := ste.reduceRowAndConvert(ste.tableEditor.nbf, foreignKey.TableColumns, foreignKey.ReferencedTableColumns, dRow)
if err != nil {
return err
}
if hasNulls {
return nil
continue
}
referencingSte, ok := ste.tableEditSession.tables[foreignKey.ReferencedTableName]
if !ok {

View File

@@ -114,7 +114,11 @@ func (merger *Merger) MergeTable(ctx context.Context, tblName string, tableEditS
if h != mh {
ms, err = calcTableMergeStats(ctx, tbl, mergeTbl)
}
// force load the table editor since this counts as a change
_, err := tableEditSession.GetTableEditor(ctx, tblName, nil)
if err != nil {
return nil, nil, err
}
return mergeTbl, &ms, nil
} else if mh == anch {
return tbl, &MergeStats{Operation: TableUnmodified}, nil

View File

@@ -17,6 +17,7 @@ package alterschema
import (
"context"
"errors"
"fmt"
"github.com/liquidata-inc/dolt/go/libraries/doltcore/doltdb"
"github.com/liquidata-inc/dolt/go/libraries/doltcore/row"
@@ -26,7 +27,7 @@ import (
)
// DropColumn drops a column from a table, and removes its associated cell values
func DropColumn(ctx context.Context, tbl *doltdb.Table, colName string) (*doltdb.Table, error) {
func DropColumn(ctx context.Context, tbl *doltdb.Table, colName string, foreignKeys []*doltdb.ForeignKey) (*doltdb.Table, error) {
if tbl == nil {
panic("invalid parameters")
}
@@ -48,6 +49,19 @@ func DropColumn(ctx context.Context, tbl *doltdb.Table, colName string) (*doltdb
dropTag = col.Tag
}
for _, foreignKey := range foreignKeys {
for _, fkTag := range foreignKey.TableColumns {
if dropTag == fkTag {
return nil, fmt.Errorf("cannot drop column `%s` as it is used in foreign key `%d`", colName, dropTag)
}
}
for _, fkTag := range foreignKey.ReferencedTableColumns {
if dropTag == fkTag {
return nil, fmt.Errorf("cannot drop column `%s` as it is used in foreign key `%d`", colName, dropTag)
}
}
}
for _, index := range tblSch.Indexes().IndexesWithColumn(colName) {
_, err = tblSch.Indexes().RemoveIndex(index.Name())
if err != nil {

View File

@@ -112,12 +112,11 @@ func TestDropColumn(t *testing.T) {
ctx := context.Background()
root, err := dEnv.WorkingRoot(ctx)
assert.NoError(t, err)
require.NoError(t, err)
tbl, _, err := root.GetTable(ctx, tableName)
require.NoError(t, err)
updatedTable, err := DropColumn(ctx, tbl, tt.colName)
updatedTable, err := DropColumn(ctx, tbl, tt.colName, nil)
if len(tt.expectedErr) > 0 {
require.Error(t, err)
assert.Contains(t, err.Error(), tt.expectedErr)
@@ -187,12 +186,11 @@ func TestDropColumnUsedByIndex(t *testing.T) {
ctx := context.Background()
root, err := dEnv.WorkingRoot(ctx)
assert.NoError(t, err)
require.NoError(t, err)
tbl, _, err := root.GetTable(ctx, tableName)
require.NoError(t, err)
updatedTable, err := DropColumn(ctx, tbl, tt.colName)
updatedTable, err := DropColumn(ctx, tbl, tt.colName, nil)
require.NoError(t, err)
sch, err := updatedTable.GetSchema(ctx)

View File

@@ -53,7 +53,7 @@ type IndexCollection interface {
// rather than by tag number, which allows an index from a different table to be added as long as they have matching
// column names. If an index with the same name or column structure already exists, or the index contains different
// columns, then it is skipped.
Merge(indexes ...Index)
Merge(includeHidden bool, indexes ...Index)
// RemoveIndex removes an index from the table metadata.
RemoveIndex(indexName string) (Index, error)
// RenameIndex renames an index in the table metadata.
@@ -237,13 +237,17 @@ func (ixc *indexCollectionImpl) IndexesWithTag(tag uint64) []Index {
return indexes
}
func (ixc *indexCollectionImpl) Merge(indexes ...Index) {
func (ixc *indexCollectionImpl) Merge(includeHidden bool, indexes ...Index) {
for _, index := range indexes {
if !includeHidden && index.IsHidden() {
continue
}
if tags, ok := ixc.columnNamesToTags(index.ColumnNames()); ok && !ixc.Contains(index.Name()) {
newIndex := &indexImpl{
name: index.Name(),
tags: tags,
indexColl: ixc,
isHidden: index.IsHidden(),
isUnique: index.IsUnique(),
comment: index.Comment(),
}

View File

@@ -16,6 +16,7 @@ package sqle
import (
"context"
"fmt"
"github.com/liquidata-inc/go-mysql-server/sql"
@@ -223,6 +224,25 @@ func (sess *DoltSession) Set(ctx context.Context, key string, typ sql.Type, valu
return nil
}
if key == "foreign_key_checks" {
convertedVal, err := sql.Int64.Convert(value)
if err != nil {
return err
}
intVal := convertedVal.(int64)
if intVal == 0 {
for _, tableEditSession := range sess.dbEditors {
tableEditSession.Props.ForeignKeyChecksDisabled = true
}
} else if intVal == 1 {
for _, tableEditSession := range sess.dbEditors {
tableEditSession.Props.ForeignKeyChecksDisabled = false
}
} else {
return fmt.Errorf("variable 'foreign_key_checks' can't be set to the value of '%d'", intVal)
}
}
return sess.Session.Set(ctx, key, typ, value)
}

View File

@@ -371,7 +371,13 @@ func (t *AlterableDoltTable) DropColumn(ctx *sql.Context, columnName string) err
return err
}
updatedTable, err = alterschema.DropColumn(ctx, updatedTable, columnName)
fkCollection, err := root.GetForeignKeyCollection(ctx)
if err != nil {
return err
}
declaresFk, referencesFk := fkCollection.KeysForTable(t.name)
updatedTable, err = alterschema.DropColumn(ctx, updatedTable, columnName, append(declaresFk, referencesFk...))
if err != nil {
return err
}
@@ -425,6 +431,18 @@ func (t *AlterableDoltTable) ModifyColumn(ctx *sql.Context, columnName string, c
}
}
fkCollection, err := root.GetForeignKeyCollection(ctx)
if err != nil {
return err
}
declaresFk, _ := fkCollection.KeysForTable(t.name)
for _, foreignKey := range declaresFk {
if (foreignKey.OnUpdate == doltdb.ForeignKeyReferenceOption_SetNull || foreignKey.OnDelete == doltdb.ForeignKeyReferenceOption_SetNull) &&
col.IsNullable() {
return fmt.Errorf("foreign key `%s` has SET NULL thus column `%s` cannot be altered to accept null values", foreignKey.Name, col.Name)
}
}
updatedTable, err := alterschema.ModifyColumn(ctx, table, existingCol, col, defVal, orderToOrder(order))
if err != nil {
return err
@@ -547,6 +565,10 @@ func (t *AlterableDoltTable) CreateForeignKey(ctx *sql.Context, fkName string, c
//TODO: fix go-mysql-server equivalent check, needs two vals
return fmt.Errorf("table `%s` does not have column `%s`", t.name, col)
}
if (onUpdate == sql.ForeignKeyReferenceOption_SetNull || onDelete == sql.ForeignKeyReferenceOption_SetNull) &&
!tableCol.IsNullable() {
return fmt.Errorf("cannot use SET NULL as column `%s` is non-nullable", tableCol.Name)
}
tblCols[i] = tableCol
colTags[i] = tableCol.Tag
sqlColNames[i] = sql.IndexColumn{
@@ -568,7 +590,6 @@ func (t *AlterableDoltTable) CreateForeignKey(ctx *sql.Context, fkName string, c
Length: 0,
}
if !tblCols[i].TypeInfo.Equals(tableCol.TypeInfo) {
//TODO: make this more descriptive about our lack of support of different types for fks, figure it out later
return fmt.Errorf("column type mismatch on `%s` and `%s`", columns[i], tableCol.Name)
}
sqlparserType := tableCol.TypeInfo.ToSqlType().Type()
@@ -615,7 +636,7 @@ func (t *AlterableDoltTable) CreateForeignKey(ctx *sql.Context, fkName string, c
if err != nil {
return err
}
err = foreignKeyCollection.AddKey(&doltdb.ForeignKey{
foreignKey := &doltdb.ForeignKey{
Name: fkName,
TableName: t.name,
TableIndex: tableIndex.Name(),
@@ -625,7 +646,8 @@ func (t *AlterableDoltTable) CreateForeignKey(ctx *sql.Context, fkName string, c
ReferencedTableColumns: refColTags,
OnUpdate: onUpdateRefOp,
OnDelete: onDeleteRefOp,
})
}
err = foreignKeyCollection.AddKey(foreignKey)
if err != nil {
return err
}
@@ -634,6 +656,19 @@ func (t *AlterableDoltTable) CreateForeignKey(ctx *sql.Context, fkName string, c
return err
}
tableIndexData, err := newTable.GetIndexRowData(ctx, tableIndex.Name())
if err != nil {
return err
}
refTableIndexData, err := newRefTable.GetIndexRowData(ctx, refTableIndex.Name())
if err != nil {
return err
}
err = foreignKey.ValidateData(ctx, tableIndexData, refTableIndex, refTableIndexData)
if err != nil {
return err
}
err = t.db.SetRoot(ctx, newRoot)
if err != nil {
return err