Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(spanner/spannertest): support JSON_VALUE function #5173

Merged
merged 2 commits into from Nov 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 13 additions & 5 deletions spanner/spannertest/db.go
Expand Up @@ -700,12 +700,20 @@ func (t *table) addColumn(cd spansql.ColumnDef, newTable bool) *status.Status {
// TODO: what happens in this case?
return status.Newf(codes.Unimplemented, "can't add NOT NULL columns to non-empty tables yet")
}
if cd.Generated != nil {
// TODO: should backfill the data to maintain behaviour with real spanner
return status.Newf(codes.Unimplemented, "can't add generated columns to non-empty tables yet")
}
for i := range t.rows {
t.rows[i] = append(t.rows[i], nil)
if cd.Generated != nil {
ec := evalContext{
cols: t.cols,
row: t.rows[i],
}
val, err := ec.evalExpr(cd.Generated)
if err != nil {
return status.Newf(codes.InvalidArgument, "could not backfill values for generated column: %v", err)
}
t.rows[i] = append(t.rows[i], val)
} else {
t.rows[i] = append(t.rows[i], nil)
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions spanner/spannertest/db_test.go
Expand Up @@ -441,8 +441,8 @@ func TestGeneratedColumn(t *testing.T) {
if err != nil {
t.Fatalf("%s: Bad DDL", err)
}
if st := db.ApplyDDL(ddl.List[0]); st.Code() == codes.OK {
t.Fatalf("Should have failed to add a generated column to non-empty table\n status: %v", st)
if st := db.ApplyDDL(ddl.List[0]); st.Code() != codes.OK {
t.Fatalf("Failed to add a generated column to non-empty table\n status: %v", st)
}

}
Expand Down
19 changes: 19 additions & 0 deletions spanner/spannertest/funcs.go
Expand Up @@ -88,6 +88,25 @@ var functions = map[string]function{
return cast(values, types, true)
},
},
"JSON_VALUE": {
Eval: func(values []interface{}, types []spansql.Type) (interface{}, spansql.Type, error) {
if len(values) != 2 {
return nil, spansql.Type{}, status.Error(codes.InvalidArgument, "No matching signature for function JSON_VALUE for the given argument types")
}
if values[0] == nil || values[1] == nil {
return nil, spansql.Type{Base: spansql.String}, nil
}
_, okArg1 := values[0].(string)
_, okArg2 := values[1].(string)
if !(okArg1 && okArg2) {
return nil, spansql.Type{}, status.Error(codes.InvalidArgument, "No matching signature for function JSON_VALUE for the given argument types")
}
// This function currently has no implementation and always returns
// an empty string, as it would otherwise require an XPath query
// engine.
return "", spansql.Type{Base: spansql.String}, nil
},
},
}

func cast(values []interface{}, types []spansql.Type, safe bool) (interface{}, spansql.Type, error) {
Expand Down
4 changes: 2 additions & 2 deletions spanner/spannertest/integration_test.go
Expand Up @@ -1312,8 +1312,8 @@ func TestIntegration_GeneratedColumns(t *testing.T) {

err = updateDDL(t, adminClient,
`ALTER TABLE `+tableName+` ADD COLUMN TotalSales2 INT64 AS (NumSongs * EstimatedSales) STORED`)
if err == nil {
t.Fatalf("Should have failed to add a generated column to non empty table")
if err != nil {
t.Fatalf("Failed to add a generated column to a non-empty table: %v", err)
}

ri := client.Single().Query(ctx, spanner.NewStatement(
Expand Down
3 changes: 3 additions & 0 deletions spanner/spansql/keywords.go
Expand Up @@ -231,4 +231,7 @@ var allFuncs = []string{
"UNIX_MILLIS",
"UNIX_MICROS",
"PENDING_COMMIT_TIMESTAMP",

// JSON functions.
"JSON_VALUE",
}
15 changes: 15 additions & 0 deletions spanner/spansql/parser_test.go
Expand Up @@ -862,6 +862,21 @@ func TestParseDDL(t *testing.T) {
},
},
}},
{`ALTER TABLE products ADD COLUMN item STRING(MAX) AS (JSON_VALUE(itemDetails, '$.itemDetails')) STORED`, &DDL{Filename: "filename", List: []DDLStmt{
&AlterTable{
Name: "products",
Alteration: AddColumn{Def: ColumnDef{
Name: "item",
Type: Type{Base: String, Len: MaxLen},
Position: line(1),
Generated: Func{
Name: "JSON_VALUE",
Args: []Expr{ID("itemDetails"), StringLiteral("$.itemDetails")},
},
}},
Position: line(1),
},
}}},
}
for _, test := range tests {
got, err := ParseDDL("filename", test.in)
Expand Down