Skip to content
Permalink
Browse files
feat(spanner/spansql): define structures and parse UPDATE DML stateme…
…nts (#3192)

Updates #3162.
  • Loading branch information
dsymonds committed Nov 11, 2020
1 parent 1d397aa commit 23b69042c58489df512703259f54d075ba0c0722
Showing with 113 additions and 2 deletions.
  1. +62 −1 spanner/spansql/parser.go
  2. +17 −0 spanner/spansql/sql.go
  3. +15 −0 spanner/spansql/sql_test.go
  4. +19 −1 spanner/spansql/types.go
@@ -1281,7 +1281,13 @@ func (p *parser) parseDMLStmt() (DMLStmt, *parseError) {
DELETE [FROM] target_name [[AS] alias]
WHERE condition
TODO: Insert, Update.
UPDATE target_name [[AS] alias]
SET update_item [, ...]
WHERE condition
update_item: path_expression = expression | path_expression = DEFAULT
TODO: Insert.
*/

if p.eat("DELETE") {
@@ -1304,9 +1310,64 @@ func (p *parser) parseDMLStmt() (DMLStmt, *parseError) {
}, nil
}

if p.eat("UPDATE") {
tname, err := p.parseTableOrIndexOrColumnName()
if err != nil {
return nil, err
}
u := &Update{
Table: tname,
}
// TODO: parse alias.
if err := p.expect("SET"); err != nil {
return nil, err
}
for {
ui, err := p.parseUpdateItem()
if err != nil {
return nil, err
}
u.Items = append(u.Items, ui)
if p.eat(",") {
continue
}
break
}
if err := p.expect("WHERE"); err != nil {
return nil, err
}
where, err := p.parseBoolExpr()
if err != nil {
return nil, err
}
u.Where = where
return u, nil
}

return nil, p.errorf("unknown DML statement")
}

func (p *parser) parseUpdateItem() (UpdateItem, *parseError) {
col, err := p.parseTableOrIndexOrColumnName()
if err != nil {
return UpdateItem{}, err
}
ui := UpdateItem{
Column: col,
}
if err := p.expect("="); err != nil {
return UpdateItem{}, err
}
if p.eat("DEFAULT") {
return ui, nil
}
ui.Value, err = p.parseExpr()
if err != nil {
return UpdateItem{}, err
}
return ui, nil
}

func (p *parser) parseColumnDef() (ColumnDef, *parseError) {
debugf("parseColumnDef: %v", p)

@@ -158,6 +158,23 @@ func (d *Delete) SQL() string {
return "DELETE FROM " + d.Table.SQL() + " WHERE " + d.Where.SQL()
}

func (u *Update) SQL() string {
str := "UPDATE " + u.Table.SQL() + " SET "
for i, item := range u.Items {
if i > 0 {
str += ", "
}
str += item.Column.SQL() + " = "
if item.Value != nil {
str += item.Value.SQL()
} else {
str += "DEFAULT"
}
}
str += " WHERE " + u.Where.SQL()
return str
}

func (cd ColumnDef) SQL() string {
str := cd.Name.SQL() + " " + cd.Type.SQL()
if cd.NotNull {
@@ -232,6 +232,21 @@ func TestSQL(t *testing.T) {
"DELETE FROM Ta WHERE C > 2",
reparseDML,
},
{
&Update{
Table: "Ta",
Items: []UpdateItem{
{Column: "Cb", Value: IntegerLiteral(4)},
{Column: "Ce", Value: StringLiteral("wow")},
{Column: "Cf", Value: ID("Cg")},
{Column: "Cg", Value: Null},
{Column: "Ch", Value: nil},
},
Where: ID("Ca"),
},
`UPDATE Ta SET Cb = 4, Ce = "wow", Cf = Cg, Cg = NULL, Ch = DEFAULT WHERE Ca`,
reparseDML,
},
{
Query{
Select: Select{
@@ -217,7 +217,25 @@ type Delete struct {
func (d *Delete) String() string { return fmt.Sprintf("%#v", d) }
func (*Delete) isDMLStmt() {}

// TODO: Insert, Update.
// TODO: Insert.

// Update represents an UPDATE statement.
// https://cloud.google.com/spanner/docs/dml-syntax#update-statement
type Update struct {
Table ID
Items []UpdateItem
Where BoolExpr

// TODO: Alias
}

func (u *Update) String() string { return fmt.Sprintf("%#v", u) }
func (*Update) isDMLStmt() {}

type UpdateItem struct {
Column ID
Value Expr // or nil for DEFAULT
}

// ColumnDef represents a column definition as part of a CREATE TABLE
// or ALTER TABLE statement.

0 comments on commit 23b6904

Please sign in to comment.