Skip to content

Commit

Permalink
Merge pull request #95 from gcpug/non-nullable-generated-columns
Browse files Browse the repository at this point in the history
Support non-nullable generated column (fix)
  • Loading branch information
kazegusuri authored Jun 3, 2023
2 parents 8c10084 + 7d02417 commit 9379ddd
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 10 deletions.
2 changes: 1 addition & 1 deletion server/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -762,7 +762,7 @@ func (d *database) write(ctx context.Context, tx *transaction, tbl string, cols

// Check not nullable columns are specified for Insert/Replace
if nonNullCheck {
if exist, nonNullables := table.NonNullableColumnsExist(cols); exist {
if exist, nonNullables := table.NonNullableAndNonGeneratedColumnsExist(cols); exist {
columns := strings.Join(nonNullables, ", ")
return status.Errorf(codes.FailedPrecondition,
"A new row in table %s does not specify a non-null value for these NOT NULL columns: %s",
Expand Down
24 changes: 23 additions & 1 deletion server/database_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import (
)

var (
allSchema = []string{schemaSimple, schemaInterleaved, schemaInterleavedCascade, schemaInterleavedNoAction, schemaCompositePrimaryKeys, schemaFullTypes, schemaArrayTypes, schemaJoinA, schemaJoinB, schemaFromTable, schemaGeneratedValues, schemaDefaultValues}
allSchema = []string{schemaSimple, schemaInterleaved, schemaInterleavedCascade, schemaInterleavedNoAction, schemaCompositePrimaryKeys, schemaFullTypes, schemaArrayTypes, schemaJoinA, schemaJoinB, schemaFromTable, schemaGeneratedValues, schemaGeneratedColumn, schemaDefaultValues}
schemaSimple = `CREATE TABLE Simple (
Id INT64 NOT NULL,
Value STRING(MAX) NOT NULL,
Expand Down Expand Up @@ -139,6 +139,11 @@ CREATE INDEX FullTypesByTimestamp ON FullTypes(FTTimestamp);
"`JOIN` INT64 NOT NULL, " +
") PRIMARY KEY(`ALL`); \n" +
"CREATE INDEX `ALL` ON `From`(`ALL`);"
schemaGeneratedColumn = `CREATE TABLE GeneratedColumn (
Id INT64 NOT NULL,
Value STRING(MAX) NOT NULL AS (CAST(Id AS STRING)) STORED,
) PRIMARY KEY(Id);
`

schemaGeneratedValues = `CREATE TABLE GeneratedValues (
Id INT64 NOT NULL,
Expand Down Expand Up @@ -1698,6 +1703,18 @@ func TestInsertAndReplace(t *testing.T) {
[]interface{}{int64(2), int64(2), int64(2)},
},
},
"GeneratedColumn": {
tbl: "GeneratedColumn",
wcols: []string{"Id"},
values: []*structpb.Value{
makeStringValue("100"),
},
cols: []string{"Id", "Value"},
limit: 100,
expected: [][]interface{}{
[]interface{}{int64(100), "100"},
},
},
}

for _, op := range []string{"INSERT", "REPLACE"} {
Expand Down Expand Up @@ -3789,6 +3806,7 @@ func TestInformationSchema(t *testing.T) {
[]interface{}{"", "", "DefaultValues", nil, nil, "COMMITTED"},
[]interface{}{"", "", "From", nil, nil, "COMMITTED"},
[]interface{}{"", "", "FullTypes", nil, nil, "COMMITTED"},
[]interface{}{"", "", "GeneratedColumn", nil, nil, "COMMITTED"},
[]interface{}{"", "", "GeneratedValues", nil, nil, "COMMITTED"},
[]interface{}{"", "", "Interleaved", "ParentTable", nil, "COMMITTED"},
[]interface{}{"", "", "InterleavedCascade", "ParentTableCascade", "CASCADE", "COMMITTED"},
Expand Down Expand Up @@ -3941,6 +3959,8 @@ func TestInformationSchema(t *testing.T) {
{"", "", "FullTypes", "FTFloatNull", int64(13), nil, nil, "YES", "FLOAT64"},
{"", "", "FullTypes", "FTDate", int64(14), nil, nil, "NO", "DATE"},
{"", "", "FullTypes", "FTDateNull", int64(15), nil, nil, "YES", "DATE"},
{"", "", "GeneratedColumn", "Id", int64(1), nil, nil, "NO", "INT64"},
{"", "", "GeneratedColumn", "Value", int64(2), nil, nil, "NO", "STRING(MAX)"},
{"", "", "GeneratedValues", "Id", int64(1), nil, nil, "NO", "INT64"},
{"", "", "GeneratedValues", "Value", int64(2), nil, nil, "NO", "STRING(32)"},
{"", "", "GeneratedValues", "N", int64(3), nil, nil, "NO", "INT64"},
Expand Down Expand Up @@ -4007,6 +4027,7 @@ func TestInformationSchema(t *testing.T) {
{"", "", "DefaultValues", "PRIMARY_KEY", "PRIMARY_KEY", "", true, false, nil, false},
{"", "", "From", "PRIMARY_KEY", "PRIMARY_KEY", "", true, false, nil, false},
{"", "", "FullTypes", "PRIMARY_KEY", "PRIMARY_KEY", "", true, false, nil, false},
{"", "", "GeneratedColumn", "PRIMARY_KEY", "PRIMARY_KEY", "", true, false, nil, false},
{"", "", "GeneratedValues", "PRIMARY_KEY", "PRIMARY_KEY", "", true, false, nil, false},
{"", "", "Interleaved", "PRIMARY_KEY", "PRIMARY_KEY", "", true, false, nil, false},
{"", "", "InterleavedCascade", "PRIMARY_KEY", "PRIMARY_KEY", "", true, false, nil, false},
Expand Down Expand Up @@ -4109,6 +4130,7 @@ func TestInformationSchema(t *testing.T) {
{"", "", "FullTypes", "FullTypesByIntTimestamp", "INDEX", "FTTimestamp", int64(2), "ASC", "NO", "TIMESTAMP"},
{"", "", "FullTypes", "FullTypesByTimestamp", "INDEX", "FTTimestamp", int64(1), "ASC", "NO", "TIMESTAMP"},
{"", "", "FullTypes", "PRIMARY_KEY", "PRIMARY_KEY", "PKey", int64(1), "ASC", "NO", "STRING(32)"},
{"", "", "GeneratedColumn", "PRIMARY_KEY", "PRIMARY_KEY", "Id", int64(1), "ASC", "NO", "INT64"},
{"", "", "GeneratedValues", "PRIMARY_KEY", "PRIMARY_KEY", "Id", int64(1), "ASC", "NO", "INT64"},
{"", "", "Interleaved", "InterleavedKey", "INDEX", "Id", int64(1), "ASC", "NO", "INT64"},
{"", "", "Interleaved", "InterleavedKey", "INDEX", "Value", int64(2), "ASC", "NO", "STRING(MAX)"},
Expand Down
2 changes: 1 addition & 1 deletion server/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,7 @@ func (b *QueryBuilder) buildInsert(up *ast.Insert) (string, []interface{}, error
}

// Check not nullable columns
if exist, nonNullables := t.NonNullableColumnsExist(columns); exist {
if exist, nonNullables := t.NonNullableAndNonGeneratedColumnsExist(columns); exist {
columns := strings.Join(nonNullables, ", ")
return "", nil, status.Errorf(codes.FailedPrecondition,
"A new row in table %s does not specify a non-null value for these NOT NULL columns: %s",
Expand Down
17 changes: 10 additions & 7 deletions server/table.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,28 +60,31 @@ func (t *Table) TableViewWithAlias(alias string) *TableView {
return createTableViewFromTable(t, alias)
}

// NonNullableColumnsExist checks non nullable columns exist in the spciefied columns.
// It returns true and the columns if non nullable columns exist.
func (t *Table) NonNullableColumnsExist(columns []string) (bool, []string) {
// NonNullableAndNonGeneratedColumnsExist checks non nullable columns exist in the spciefied columns.
// It returns true and the columns if non nullable and non generated columns exist.
func (t *Table) NonNullableAndNonGeneratedColumnsExist(columns []string) (bool, []string) {
usedColumns := make(map[string]struct{}, len(columns))
for _, name := range columns {
usedColumns[name] = struct{}{}
}

var noExsitNonNullableColumns []string
var noExistNonNullableColumns []string
for _, c := range t.columns {
if c.nullable {
continue
}
if c.ast != nil && c.ast.GeneratedExpr != nil {
continue
}

n := c.Name()
if _, ok := usedColumns[n]; !ok {
noExsitNonNullableColumns = append(noExsitNonNullableColumns, n)
noExistNonNullableColumns = append(noExistNonNullableColumns, n)
}
}

if len(noExsitNonNullableColumns) > 0 {
return true, noExsitNonNullableColumns
if len(noExistNonNullableColumns) > 0 {
return true, noExistNonNullableColumns
}

return false, nil
Expand Down

0 comments on commit 9379ddd

Please sign in to comment.