Skip to content
Permalink
Browse files
feat(spanner/spannertest): support JSON_VALUE function (#5173)
* feat: support JSON_VALUE function

Adds support for the JSON_VALUE function. The function always returns an empty string,
as there is no XPath query engine available in the Spanner client library. This does
however enable the usage of computed columns that use the function.

This change also fixes the TODO that generated columns cannot be added to non-empty
tables.

Fixes #5167

* fix: linting
  • Loading branch information
olavloite committed Nov 29, 2021
1 parent c103ff6 commit ac98735cb1adc9384c5b2caeb9aac938db275bf7
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 9 deletions.
@@ -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)
}
}
}

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

}
@@ -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) {
@@ -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(
@@ -231,4 +231,7 @@ var allFuncs = []string{
"UNIX_MILLIS",
"UNIX_MICROS",
"PENDING_COMMIT_TIMESTAMP",

// JSON functions.
"JSON_VALUE",
}
@@ -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)

0 comments on commit ac98735

Please sign in to comment.