Skip to content

Commit

Permalink
Add support of 'OR UPDATE|IGNORE' syntax for INSERT DML (#79)
Browse files Browse the repository at this point in the history
* Add support of 'OR UPDATE|INGORE' syntax for INSERT DML

See: https://cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#insert-ignore

* Update test cases

* Update ast/ast.go

Co-authored-by: Hiroya Fujinami <[email protected]>

---------

Co-authored-by: Hiroya Fujinami <[email protected]>
  • Loading branch information
git-hulk and makenowjust authored May 29, 2024
1 parent 0a1141e commit f02fa7b
Show file tree
Hide file tree
Showing 20 changed files with 311 additions and 33 deletions.
10 changes: 6 additions & 4 deletions ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -1633,7 +1633,7 @@ type AlterTable struct {

Alter token.Pos // position of "ALTER" keyword

Name *Ident
Name *Ident
TableAlteration TableAlteration
}

Expand All @@ -1646,7 +1646,7 @@ type AlterIndex struct {

Alter token.Pos // position of "ALTER" keyword

Name *Ident
Name *Ident
IndexAlteration IndexAlteration
}

Expand All @@ -1659,7 +1659,7 @@ type AlterChangeStream struct {

Alter token.Pos // position of "ALTER" keyword

Name *Ident
Name *Ident
ChangeStreamAlteration ChangeStreamAlteration
}

Expand Down Expand Up @@ -2213,13 +2213,15 @@ type ArraySchemaType struct {

// Insert is INSERT statement node.
//
// INSERT INTO {{.TableName | sql}} ({{.Columns | sqlJoin ","}}) {{.Input | sql}}
// INSERT {{if .InsertOrType}}OR .InsertOrType{{end}}INTO {{.TableName | sql}} ({{.Columns | sqlJoin ","}}) {{.Input | sql}}
type Insert struct {
// pos = Insert
// end = Input.end

Insert token.Pos // position of "INSERT" keyword

InsertOrType InsertOrType

TableName *Ident
Columns []*Ident
Input InsertInput
Expand Down
7 changes: 7 additions & 0 deletions ast/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,10 @@ const (
SecurityTypeInvoker SecurityType = "INVOKER"
SecurityTypeDefiner SecurityType = "DEFINER"
)

type InsertOrType string

const (
InsertOrTypeUpdate InsertOrType = "UPDATE"
InsertOrTypeIgnore InsertOrType = "IGNORE"
)
6 changes: 5 additions & 1 deletion ast/sql.go
Original file line number Diff line number Diff line change
Expand Up @@ -1223,7 +1223,11 @@ func (a *ArraySchemaType) SQL() string {
// ================================================================================

func (i *Insert) SQL() string {
sql := "INSERT INTO " + i.TableName.SQL() + " ("
sql := "INSERT "
if i.InsertOrType != "" {
sql += "OR " + string(i.InsertOrType) + " "
}
sql += "INTO " + i.TableName.SQL() + " ("
for i, c := range i.Columns {
if i != 0 {
sql += ", "
Expand Down
31 changes: 23 additions & 8 deletions parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -2704,8 +2704,8 @@ func (p *Parser) parseAlterTable(pos token.Pos) *ast.AlterTable {
}

return &ast.AlterTable{
Alter: pos,
Name: name,
Alter: pos,
Name: name,
TableAlteration: alteration,
}
}
Expand Down Expand Up @@ -2869,8 +2869,8 @@ func (p *Parser) parseAlterIndex(pos token.Pos) *ast.AlterIndex {
}

return &ast.AlterIndex{
Alter: pos,
Name: name,
Alter: pos,
Name: name,
IndexAlteration: alteration,
}
}
Expand Down Expand Up @@ -3235,6 +3235,20 @@ func (p *Parser) parseDML() ast.DML {
}

func (p *Parser) parseInsert(pos token.Pos) *ast.Insert {
var insertOrType ast.InsertOrType
if p.Token.Kind == "OR" {
p.nextToken()
switch {
case p.Token.IsKeywordLike("UPDATE"):
insertOrType = ast.InsertOrTypeUpdate
case p.Token.Kind == "IGNORE":
insertOrType = ast.InsertOrTypeIgnore
default:
p.panicfAtToken(&p.Token, "expected pseudo keyword: UPDATE, IGNORE, but: %s", p.Token.AsString)
}
p.nextToken()
}

if p.Token.Kind == "INTO" {
p.nextToken()
}
Expand Down Expand Up @@ -3262,10 +3276,11 @@ func (p *Parser) parseInsert(pos token.Pos) *ast.Insert {
}

return &ast.Insert{
Insert: pos,
TableName: name,
Columns: columns,
Input: input,
Insert: pos,
InsertOrType: insertOrType,
TableName: name,
Columns: columns,
Input: input,
}
}

Expand Down
2 changes: 2 additions & 0 deletions testdata/input/dml/insert_or_ignore.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
INSERT OR IGNORE INTO foo
(foo, bar) VALUES (1, 2)
2 changes: 2 additions & 0 deletions testdata/input/dml/insert_or_update.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
INSERT OR UPDATE INTO foo
(foo, bar) VALUES (1, 2)
5 changes: 3 additions & 2 deletions testdata/result/dml/insert_into_values.sql.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ values (1, 2, 3),
(4, 5, 6)
--- AST
&ast.Insert{
Insert: 0,
TableName: &ast.Ident{
Insert: 0,
InsertOrType: "",
TableName: &ast.Ident{
NamePos: 12,
NameEnd: 15,
Name: "foo",
Expand Down
59 changes: 59 additions & 0 deletions testdata/result/dml/insert_or_ignore.sql.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
--- insert_or_ignore.sql
INSERT OR IGNORE INTO foo
(foo, bar) VALUES (1, 2)
--- AST
&ast.Insert{
Insert: 0,
InsertOrType: "IGNORE",
TableName: &ast.Ident{
NamePos: 22,
NameEnd: 25,
Name: "foo",
},
Columns: []*ast.Ident{
&ast.Ident{
NamePos: 27,
NameEnd: 30,
Name: "foo",
},
&ast.Ident{
NamePos: 32,
NameEnd: 35,
Name: "bar",
},
},
Input: &ast.ValuesInput{
Values: 37,
Rows: []*ast.ValuesRow{
&ast.ValuesRow{
Lparen: 44,
Rparen: 49,
Exprs: []*ast.DefaultExpr{
&ast.DefaultExpr{
DefaultPos: -1,
Default: false,
Expr: &ast.IntLiteral{
ValuePos: 45,
ValueEnd: 46,
Base: 10,
Value: "1",
},
},
&ast.DefaultExpr{
DefaultPos: -1,
Default: false,
Expr: &ast.IntLiteral{
ValuePos: 48,
ValueEnd: 49,
Base: 10,
Value: "2",
},
},
},
},
},
},
}

--- SQL
INSERT OR IGNORE INTO foo (foo, bar) VALUES (1, 2)
59 changes: 59 additions & 0 deletions testdata/result/dml/insert_or_update.sql.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
--- insert_or_update.sql
INSERT OR UPDATE INTO foo
(foo, bar) VALUES (1, 2)
--- AST
&ast.Insert{
Insert: 0,
InsertOrType: "UPDATE",
TableName: &ast.Ident{
NamePos: 22,
NameEnd: 25,
Name: "foo",
},
Columns: []*ast.Ident{
&ast.Ident{
NamePos: 27,
NameEnd: 30,
Name: "foo",
},
&ast.Ident{
NamePos: 32,
NameEnd: 35,
Name: "bar",
},
},
Input: &ast.ValuesInput{
Values: 37,
Rows: []*ast.ValuesRow{
&ast.ValuesRow{
Lparen: 44,
Rparen: 49,
Exprs: []*ast.DefaultExpr{
&ast.DefaultExpr{
DefaultPos: -1,
Default: false,
Expr: &ast.IntLiteral{
ValuePos: 45,
ValueEnd: 46,
Base: 10,
Value: "1",
},
},
&ast.DefaultExpr{
DefaultPos: -1,
Default: false,
Expr: &ast.IntLiteral{
ValuePos: 48,
ValueEnd: 49,
Base: 10,
Value: "2",
},
},
},
},
},
},
}

--- SQL
INSERT OR UPDATE INTO foo (foo, bar) VALUES (1, 2)
5 changes: 3 additions & 2 deletions testdata/result/dml/insert_select.sql.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ insert foo (foo, bar)
select * from unnest([(1, 2), (3, 4)])
--- AST
&ast.Insert{
Insert: 0,
TableName: &ast.Ident{
Insert: 0,
InsertOrType: "",
TableName: &ast.Ident{
NamePos: 7,
NameEnd: 10,
Name: "foo",
Expand Down
5 changes: 3 additions & 2 deletions testdata/result/dml/insert_values.sql.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ values (1, 2, 3),
(4, 5, 6)
--- AST
&ast.Insert{
Insert: 0,
TableName: &ast.Ident{
Insert: 0,
InsertOrType: "",
TableName: &ast.Ident{
NamePos: 7,
NameEnd: 10,
Name: "foo",
Expand Down
5 changes: 3 additions & 2 deletions testdata/result/dml/insert_values_default.sql.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ insert foo (foo, bar)
values (1, default)
--- AST
&ast.Insert{
Insert: 0,
TableName: &ast.Ident{
Insert: 0,
InsertOrType: "",
TableName: &ast.Ident{
NamePos: 7,
NameEnd: 10,
Name: "foo",
Expand Down
5 changes: 3 additions & 2 deletions testdata/result/dml/insert_with_sequence_function.sql.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ INSERT INTO foo(bar) VALUES (GET_NEXT_SEQUENCE_VALUE(SEQUENCE my_sequence))

--- AST
&ast.Insert{
Insert: 0,
TableName: &ast.Ident{
Insert: 0,
InsertOrType: "",
TableName: &ast.Ident{
NamePos: 12,
NameEnd: 15,
Name: "foo",
Expand Down
5 changes: 3 additions & 2 deletions testdata/result/statement/insert_into_values.sql.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ values (1, 2, 3),
(4, 5, 6)
--- AST
&ast.Insert{
Insert: 0,
TableName: &ast.Ident{
Insert: 0,
InsertOrType: "",
TableName: &ast.Ident{
NamePos: 12,
NameEnd: 15,
Name: "foo",
Expand Down
59 changes: 59 additions & 0 deletions testdata/result/statement/insert_or_ignore.sql.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
--- insert_or_ignore.sql
INSERT OR IGNORE INTO foo
(foo, bar) VALUES (1, 2)
--- AST
&ast.Insert{
Insert: 0,
InsertOrType: "IGNORE",
TableName: &ast.Ident{
NamePos: 22,
NameEnd: 25,
Name: "foo",
},
Columns: []*ast.Ident{
&ast.Ident{
NamePos: 27,
NameEnd: 30,
Name: "foo",
},
&ast.Ident{
NamePos: 32,
NameEnd: 35,
Name: "bar",
},
},
Input: &ast.ValuesInput{
Values: 37,
Rows: []*ast.ValuesRow{
&ast.ValuesRow{
Lparen: 44,
Rparen: 49,
Exprs: []*ast.DefaultExpr{
&ast.DefaultExpr{
DefaultPos: -1,
Default: false,
Expr: &ast.IntLiteral{
ValuePos: 45,
ValueEnd: 46,
Base: 10,
Value: "1",
},
},
&ast.DefaultExpr{
DefaultPos: -1,
Default: false,
Expr: &ast.IntLiteral{
ValuePos: 48,
ValueEnd: 49,
Base: 10,
Value: "2",
},
},
},
},
},
},
}

--- SQL
INSERT OR IGNORE INTO foo (foo, bar) VALUES (1, 2)
Loading

0 comments on commit f02fa7b

Please sign in to comment.