Skip to content

Commit

Permalink
feat(spanner/spansql): support JSON literals (#5968)
Browse files Browse the repository at this point in the history
Co-authored-by: rahul2393 <rahulyadavsep92@gmail.com>
  • Loading branch information
neglect-yp and rahul2393 committed May 9, 2022
1 parent dbfdf76 commit b500120
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 0 deletions.
18 changes: 18 additions & 0 deletions spanner/spansql/parser.go
Expand Up @@ -2975,6 +2975,11 @@ func (p *parser) parseLit() (Expr, *parseError) {
p.back()
return p.parseTimestampLit()
}
case tok.caseEqual("JSON"):
if p.sniffTokenType(stringToken) {
p.back()
return p.parseJSONLit()
}
}

// TODO: struct literals
Expand Down Expand Up @@ -3140,6 +3145,19 @@ func (p *parser) parseTimestampLit() (TimestampLiteral, *parseError) {
return TimestampLiteral{}, p.errorf("invalid timestamp literal %q", s)
}

func (p *parser) parseJSONLit() (JSONLiteral, *parseError) {
if err := p.expect("JSON"); err != nil {
return JSONLiteral{}, err
}
s, err := p.parseStringLit()
if err != nil {
return JSONLiteral{}, err
}
// It is not guaranteed that the returned JSONLiteral is a valid JSON document
// to avoid error due to parsing SQL generated with an invalid JSONLiteral like JSONLiteral("")
return JSONLiteral(s), nil
}

func (p *parser) parseStringLit() (StringLiteral, *parseError) {
tok := p.next()
if tok.err != nil {
Expand Down
3 changes: 3 additions & 0 deletions spanner/spansql/parser_test.go
Expand Up @@ -427,6 +427,9 @@ func TestParseExpr(t *testing.T) {
{`[1, 2, 3]`, Array{IntegerLiteral(1), IntegerLiteral(2), IntegerLiteral(3)}},
{`['x', 'y', 'xy']`, Array{StringLiteral("x"), StringLiteral("y"), StringLiteral("xy")}},
{`ARRAY[1, 2, 3]`, Array{IntegerLiteral(1), IntegerLiteral(2), IntegerLiteral(3)}},
// JSON literals:
// https://cloud.google.com/spanner/docs/reference/standard-sql/lexical#json_literals
{`JSON '{"a": 1}'`, JSONLiteral(`{"a": 1}`)},

// OR is lower precedence than AND.
{`A AND B OR C`, LogicalOp{LHS: LogicalOp{LHS: ID("A"), Op: And, RHS: ID("B")}, Op: Or, RHS: ID("C")}},
Expand Down
5 changes: 5 additions & 0 deletions spanner/spansql/sql.go
Expand Up @@ -738,3 +738,8 @@ func (tl TimestampLiteral) SQL() string { return buildSQL(tl) }
func (tl TimestampLiteral) addSQL(sb *strings.Builder) {
fmt.Fprintf(sb, "TIMESTAMP '%s'", time.Time(tl).Format("2006-01-02 15:04:05.000000 -07:00"))
}

func (jl JSONLiteral) SQL() string { return buildSQL(jl) }
func (jl JSONLiteral) addSQL(sb *strings.Builder) {
fmt.Fprintf(sb, "JSON '%s'", jl)
}
5 changes: 5 additions & 0 deletions spanner/spansql/sql_test.go
Expand Up @@ -559,6 +559,11 @@ func TestSQL(t *testing.T) {
`TIMESTAMP '2014-09-27 12:34:56.123456 -07:00'`,
reparseExpr,
},
{
JSONLiteral(`{"a": 1}`),
`JSON '{"a": 1}'`,
reparseExpr,
},
{
Query{
Select: Select{
Expand Down
6 changes: 6 additions & 0 deletions spanner/spansql/types.go
Expand Up @@ -771,6 +771,12 @@ type TimestampLiteral time.Time

func (TimestampLiteral) isExpr() {}

// JSONLiteral represents a JSON literal
// https://cloud.google.com/spanner/docs/reference/standard-sql/lexical#json_literals
type JSONLiteral []byte

func (JSONLiteral) isExpr() {}

type StarExpr int

// Star represents a "*" in an expression.
Expand Down

0 comments on commit b500120

Please sign in to comment.