From 95940baf75d0eda9092e6ded6e2a9e384b61cacc Mon Sep 17 00:00:00 2001 From: sryoya Date: Fri, 25 Dec 2020 18:55:19 +0900 Subject: [PATCH 1/4] feat(spanner/spannertest): support Not Null constraint --- spanner/spannertest/db.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/spanner/spannertest/db.go b/spanner/spannertest/db.go index d1a9ef20df0..9bec44b47ad 100644 --- a/spanner/spannertest/db.go +++ b/spanner/spannertest/db.go @@ -68,6 +68,7 @@ type table struct { type colInfo struct { Name spansql.ID Type spansql.Type + NotNull bool AggIndex int // Index+1 of SELECT list for which this is an aggregate value. Alias spansql.PathExp // an alternate name for this column (result sets only) } @@ -400,10 +401,12 @@ func (d *database) writeValues(tx *transaction, tbl spansql.ID, cols []spansql.I if x == commitTimestampSentinel { x = tx.commitTimestamp } + if x == nil && t.cols[i].NotNull { + return status.Errorf(codes.FailedPrecondition, "%s must not be NULL in table %s", t.cols[i].Name, tbl) + } r[i] = x } - // TODO: enforce NOT NULL? // TODO: enforce that provided timestamp for commit_timestamp=true columns // are not ahead of the transaction's commit timestamp. @@ -632,8 +635,9 @@ func (t *table) addColumn(cd spansql.ColumnDef, newTable bool) *status.Status { } t.cols = append(t.cols, colInfo{ - Name: cd.Name, - Type: cd.Type, + Name: cd.Name, + Type: cd.Type, + NotNull: cd.NotNull, }) t.colIndex[cd.Name] = len(t.cols) - 1 if !newTable { @@ -856,7 +860,6 @@ func rowEqual(a, b []interface{}) bool { // valForType converts a value from its RPC form into its internal representation. func valForType(v *structpb.Value, t spansql.Type) (interface{}, error) { if _, ok := v.Kind.(*structpb.Value_NullValue); ok { - // TODO: enforce NOT NULL constraints? return nil, nil } From 931c0baa70312dd195320deb77114ae3ce7532db Mon Sep 17 00:00:00 2001 From: sryoya Date: Mon, 4 Jan 2021 23:43:37 +0900 Subject: [PATCH 2/4] add missing handling and comments related to Not Null --- spanner/spannertest/db.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/spanner/spannertest/db.go b/spanner/spannertest/db.go index 9bec44b47ad..c5b9b060605 100644 --- a/spanner/spannertest/db.go +++ b/spanner/spannertest/db.go @@ -68,7 +68,7 @@ type table struct { type colInfo struct { Name spansql.ID Type spansql.Type - NotNull bool + NotNull bool // only set for table columns AggIndex int // Index+1 of SELECT list for which this is an aggregate value. Alias spansql.PathExp // an alternate name for this column (result sets only) } @@ -217,9 +217,10 @@ func (d *database) GetDDL() []spansql.DDLStmt { t.mu.Lock() for i, col := range t.cols { ct.Columns = append(ct.Columns, spansql.ColumnDef{ - Name: col.Name, - Type: col.Type, - // TODO: NotNull, AllowCommitTimestamp + Name: col.Name, + Type: col.Type, + NotNull: col.NotNull, + // TODO: AllowCommitTimestamp }) if i < t.pkCols { ct.PrimaryKey = append(ct.PrimaryKey, spansql.KeyPart{ From 840de7cdb3e5954dd3d1d4f3845014281de1e9f0 Mon Sep 17 00:00:00 2001 From: sryoya Date: Tue, 5 Jan 2021 00:44:49 +0900 Subject: [PATCH 3/4] enable to update Not Null flag by ALTER COLUMN --- spanner/spannertest/db.go | 46 +++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/spanner/spannertest/db.go b/spanner/spannertest/db.go index c5b9b060605..47434ce0f79 100644 --- a/spanner/spannertest/db.go +++ b/spanner/spannertest/db.go @@ -710,38 +710,42 @@ func (t *table) alterColumn(alt spansql.AlterColumn) *status.Status { return status.Newf(codes.InvalidArgument, "unknown column %q", alt.Name) } + // TODO: check if the column isn't a primary key or array types. + t.cols[ci].NotNull = sct.NotNull + // Check and make type transformations. oldT, newT := t.cols[ci].Type, sct.Type stringOrBytes := func(bt spansql.TypeBase) bool { return bt == spansql.String || bt == spansql.Bytes } - // If the only change is adding NOT NULL, this is okay except for primary key columns and array types. - // We don't track whether commit timestamps are permitted on a per-column basis, so that's ignored. - // TODO: Do this when we track NOT NULL-ness. - - // Change between STRING and BYTES is fine, as is increasing/decreasing the length limit. - // TODO: This should permit array conversions too. - if stringOrBytes(oldT.Base) && stringOrBytes(newT.Base) && !oldT.Array && !newT.Array { - // TODO: Validate data; length limit changes should be rejected if they'd lead to data loss, for instance. - var conv func(x interface{}) interface{} - if oldT.Base == spansql.Bytes && newT.Base == spansql.String { - conv = func(x interface{}) interface{} { return string(x.([]byte)) } - } else if oldT.Base == spansql.String && newT.Base == spansql.Bytes { - conv = func(x interface{}) interface{} { return []byte(x.(string)) } - } - if conv != nil { - for _, row := range t.rows { - if row[ci] != nil { // NULL stays as NULL. - row[ci] = conv(row[ci]) + // TODO: We don't track whether commit timestamps are permitted on a per-column basis, so that's ignored. + + if oldT != newT { + // Change between STRING and BYTES is fine, as is increasing/decreasing the length limit. + // TODO: This should permit array conversions too. + if stringOrBytes(oldT.Base) && stringOrBytes(newT.Base) && !oldT.Array && !newT.Array { + // TODO: Validate data; length limit changes should be rejected if they'd lead to data loss, for instance. + var conv func(x interface{}) interface{} + if oldT.Base == spansql.Bytes && newT.Base == spansql.String { + conv = func(x interface{}) interface{} { return string(x.([]byte)) } + } else if oldT.Base == spansql.String && newT.Base == spansql.Bytes { + conv = func(x interface{}) interface{} { return []byte(x.(string)) } + } + if conv != nil { + for _, row := range t.rows { + if row[ci] != nil { // NULL stays as NULL. + row[ci] = conv(row[ci]) + } } } + t.cols[ci].Type = newT + } else { + return status.Newf(codes.InvalidArgument, "unsupported ALTER COLUMN %s", alt.SQL()) } - t.cols[ci].Type = newT - return nil } // TODO: Support other alterations. - return status.Newf(codes.InvalidArgument, "unsupported ALTER COLUMN %s", alt.SQL()) + return nil } func (t *table) insertRow(rowNum int, r row) { From 4df4f5db3958312d002897301242112326bb8239 Mon Sep 17 00:00:00 2001 From: sryoya Date: Tue, 5 Jan 2021 16:19:30 +0900 Subject: [PATCH 4/4] invert changes on alterColumn func --- spanner/spannertest/db.go | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/spanner/spannertest/db.go b/spanner/spannertest/db.go index 47434ce0f79..f02b65a9566 100644 --- a/spanner/spannertest/db.go +++ b/spanner/spannertest/db.go @@ -719,33 +719,30 @@ func (t *table) alterColumn(alt spansql.AlterColumn) *status.Status { // TODO: We don't track whether commit timestamps are permitted on a per-column basis, so that's ignored. - if oldT != newT { - // Change between STRING and BYTES is fine, as is increasing/decreasing the length limit. - // TODO: This should permit array conversions too. - if stringOrBytes(oldT.Base) && stringOrBytes(newT.Base) && !oldT.Array && !newT.Array { - // TODO: Validate data; length limit changes should be rejected if they'd lead to data loss, for instance. - var conv func(x interface{}) interface{} - if oldT.Base == spansql.Bytes && newT.Base == spansql.String { - conv = func(x interface{}) interface{} { return string(x.([]byte)) } - } else if oldT.Base == spansql.String && newT.Base == spansql.Bytes { - conv = func(x interface{}) interface{} { return []byte(x.(string)) } - } - if conv != nil { - for _, row := range t.rows { - if row[ci] != nil { // NULL stays as NULL. - row[ci] = conv(row[ci]) - } + // Change between STRING and BYTES is fine, as is increasing/decreasing the length limit. + // TODO: This should permit array conversions too. + if stringOrBytes(oldT.Base) && stringOrBytes(newT.Base) && !oldT.Array && !newT.Array { + // TODO: Validate data; length limit changes should be rejected if they'd lead to data loss, for instance. + var conv func(x interface{}) interface{} + if oldT.Base == spansql.Bytes && newT.Base == spansql.String { + conv = func(x interface{}) interface{} { return string(x.([]byte)) } + } else if oldT.Base == spansql.String && newT.Base == spansql.Bytes { + conv = func(x interface{}) interface{} { return []byte(x.(string)) } + } + if conv != nil { + for _, row := range t.rows { + if row[ci] != nil { // NULL stays as NULL. + row[ci] = conv(row[ci]) } } - t.cols[ci].Type = newT - } else { - return status.Newf(codes.InvalidArgument, "unsupported ALTER COLUMN %s", alt.SQL()) } + t.cols[ci].Type = newT + return nil } // TODO: Support other alterations. - return nil + return status.Newf(codes.InvalidArgument, "unsupported ALTER COLUMN %s", alt.SQL()) } func (t *table) insertRow(rowNum int, r row) {