feat(spanner/spansql): define structures and parse UPDATE DML statements (#3192)

Updates #3162.
diff --git a/spanner/spansql/parser.go b/spanner/spansql/parser.go
index 3ca5fbf..e7c2793 100644
--- a/spanner/spansql/parser.go
+++ b/spanner/spansql/parser.go
@@ -1281,7 +1281,13 @@
 		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 @@
 		}, 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)
 
diff --git a/spanner/spansql/sql.go b/spanner/spansql/sql.go
index 7968232..0ac2b90 100644
--- a/spanner/spansql/sql.go
+++ b/spanner/spansql/sql.go
@@ -158,6 +158,23 @@
 	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 {
diff --git a/spanner/spansql/sql_test.go b/spanner/spansql/sql_test.go
index b72302c..beb92ec 100644
--- a/spanner/spansql/sql_test.go
+++ b/spanner/spansql/sql_test.go
@@ -233,6 +233,21 @@
 			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{
 					List: []Expr{ID("A"), ID("B")},
diff --git a/spanner/spansql/types.go b/spanner/spansql/types.go
index d39400a..ece38b0 100644
--- a/spanner/spansql/types.go
+++ b/spanner/spansql/types.go
@@ -217,7 +217,25 @@
 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.