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/spansql): add a support for parsing INSERT statement #6148

Merged
merged 3 commits into from Jun 15, 2022
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
51 changes: 50 additions & 1 deletion spanner/spansql/parser.go
Expand Up @@ -1442,7 +1442,16 @@ func (p *parser) parseDMLStmt() (DMLStmt, *parseError) {
update_item: path_expression = expression | path_expression = DEFAULT
TODO: Insert.
INSERT [INTO] target_name
(column_name_1 [, ..., column_name_n] )
input
input:
VALUES (row_1_column_1_expr [, ..., row_1_column_n_expr ] )
[, ..., (row_k_column_1_expr [, ..., row_k_column_n_expr ] ) ]
| select_query
expr: value_expression | DEFAULT
*/

if p.eat("DELETE") {
Expand Down Expand Up @@ -1499,6 +1508,46 @@ func (p *parser) parseDMLStmt() (DMLStmt, *parseError) {
return u, nil
}

if p.eat("INSERT") {
p.eat("INTO") // optional
tname, err := p.parseTableOrIndexOrColumnName()
if err != nil {
return nil, err
}

columns, err := p.parseColumnNameList()
if err != nil {
return nil, err
}

var input ValuesOrSelect
if p.eat("VALUES") {
values := make([][]Expr, 0)
for {
exprs, err := p.parseParenExprList()
if err != nil {
return nil, err
}
values = append(values, exprs)
if !p.eat(",") {
break
}
}
input = Values(values)
} else {
input, err = p.parseSelect()
if err != nil {
return nil, err
}
}

return &Insert{
Table: tname,
Columns: columns,
Input: input,
}, nil
}

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

Expand Down
42 changes: 42 additions & 0 deletions spanner/spansql/parser_test.go
Expand Up @@ -300,6 +300,48 @@ func TestParseQuery(t *testing.T) {
}
}

func TestParseDMLStmt(t *testing.T) {
tests := []struct {
in string
want DMLStmt
}{
{"INSERT Singers (SingerId, FirstName, LastName) VALUES (1, 'Marc', 'Richards')",
&Insert{
Table: "Singers",
Columns: []ID{ID("SingerId"), ID("FirstName"), ID("LastName")},
Input: Values{{IntegerLiteral(1), StringLiteral("Marc"), StringLiteral("Richards")}},
},
},
{"INSERT Singers (SingerId, FirstName, LastName) SELECT * FROM UNNEST ([1, 2, 3]) AS data",
&Insert{
Table: "Singers",
Columns: []ID{ID("SingerId"), ID("FirstName"), ID("LastName")},
Input: Select{
List: []Expr{Star},
From: []SelectFrom{SelectFromUnnest{
Expr: Array{
IntegerLiteral(1),
IntegerLiteral(2),
IntegerLiteral(3),
},
Alias: ID("data"),
}},
},
},
},
}
for _, test := range tests {
got, err := ParseDMLStmt(test.in)
if err != nil {
t.Errorf("ParseDMLStmt(%q): %v", test.in, err)
continue
}
if !reflect.DeepEqual(got, test.want) {
t.Errorf("ParseDMLStmt(%q) incorrect.\n got %#v\nwant %#v", test.in, got, test.want)
}
}
}

func TestParseExpr(t *testing.T) {
tests := []struct {
in string
Expand Down
32 changes: 32 additions & 0 deletions spanner/spansql/sql.go
Expand Up @@ -260,6 +260,38 @@ func (u *Update) SQL() string {
return str
}

func (i *Insert) SQL() string {
str := "INSERT " + i.Table.SQL() + " INTO ("
for i, column := range i.Columns {
if i > 0 {
str += ", "
}
str += column.SQL()
}
str += ") "
str += i.Input.SQL()
return str
}

func (v Values) SQL() string {
str := "VALUES "
for j, values := range v {
if j > 0 {
str += ", "
}
str += "("

for k, value := range values {
if k > 0 {
str += ", "
}
str += value.SQL()
}
str += ")"
}
return str
}

func (cd ColumnDef) SQL() string {
str := cd.Name.SQL() + " " + cd.Type.SQL()
if cd.NotNull {
Expand Down
24 changes: 23 additions & 1 deletion spanner/spansql/types.go
Expand Up @@ -302,7 +302,29 @@ type Delete struct {
func (d *Delete) String() string { return fmt.Sprintf("%#v", d) }
func (*Delete) isDMLStmt() {}

// TODO: Insert.
// Insert represents an INSERT statement.
// https://cloud.google.com/spanner/docs/dml-syntax#insert-statement
type Insert struct {
Table ID
Columns []ID
Input ValuesOrSelect
}

// Values represents one or more lists of expressions passed to an `INSERT` statement.
type Values [][]Expr

func (v Values) isValuesOrSelect() {}
func (v Values) String() string { return fmt.Sprintf("%#v", v) }

type ValuesOrSelect interface {
isValuesOrSelect()
SQL() string
}

func (Select) isValuesOrSelect() {}

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

// Update represents an UPDATE statement.
// https://cloud.google.com/spanner/docs/dml-syntax#update-statement
Expand Down