From 696a365551092765ccd7711e4dcfe9d95ade6e7e Mon Sep 17 00:00:00 2001 From: apstndb <803393+apstndb@users.noreply.github.com> Date: Thu, 9 Jan 2025 23:11:29 +0900 Subject: [PATCH 1/8] Implement nested update --- ast/ast.go | 30 +++++++++++++++++++++++++----- ast/pos.go | 12 ++++++++++-- ast/sql.go | 6 +++++- parser.go | 51 +++++++++++++++++++++++++++++++++++---------------- 4 files changed, 75 insertions(+), 24 deletions(-) diff --git a/ast/ast.go b/ast/ast.go index c425d444..19abeb48 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -3968,7 +3968,7 @@ type Insert struct { InsertOrType InsertOrType TableName *Path - Columns []*Ident + Columns []*Ident // optional when nested Input InsertInput ThenReturn *ThenReturn // optional } @@ -4048,16 +4048,36 @@ type Update struct { Update token.Pos // position of "UPDATE" keyword TableName *Path - As *AsAlias // optional - Updates []*UpdateItem // len(Updates) > 0 + As *AsAlias // optional + Updates []UpdateItem // len(Updates) > 0 Where *Where ThenReturn *ThenReturn // optional } -// UpdateItem is SET clause items in UPDATE. +// UpdateItem represents SET clause items in UPDATE. +type UpdateItem interface { + Node + isUpdateItem() +} + +func (UpdateItemAssign) isUpdateItem() {} +func (UpdateItemNested) isUpdateItem() {} + +// UpdateItemNested is nested update node in UPDATE statement. +// +// ({{.DML | sql}}) +type UpdateItemNested struct { + // pos = Lparen + // end = Rparen + 1 + + Lparen, Rparen token.Pos // position of "(", ")" + DML DML +} + +// UpdateItemAssign is assignment node in UPDATE statement . // // {{.Path | sqlJoin "."}} = {{.DefaultExpr | sql}} -type UpdateItem struct { +type UpdateItemAssign struct { // pos = Path[0].pos // end = DefaultExpr.end diff --git a/ast/pos.go b/ast/pos.go index d370cc48..e057afef 100644 --- a/ast/pos.go +++ b/ast/pos.go @@ -2062,11 +2062,19 @@ func (u *Update) End() token.Pos { return nodeEnd(nodeChoice(wrapNode(u.ThenReturn), wrapNode(u.Where))) } -func (u *UpdateItem) Pos() token.Pos { +func (u *UpdateItemNested) Pos() token.Pos { + return u.Lparen +} + +func (u *UpdateItemNested) End() token.Pos { + return posAdd(u.Rparen, 1) +} + +func (u *UpdateItemAssign) Pos() token.Pos { return nodePos(nodeSliceIndex(u.Path, 0)) } -func (u *UpdateItem) End() token.Pos { +func (u *UpdateItemAssign) End() token.Pos { return nodeEnd(wrapNode(u.DefaultExpr)) } diff --git a/ast/sql.go b/ast/sql.go index f110b0cf..6dca38ba 100644 --- a/ast/sql.go +++ b/ast/sql.go @@ -1351,10 +1351,14 @@ func (u *Update) SQL() string { sqlOpt(" ", u.ThenReturn, "") } -func (u *UpdateItem) SQL() string { +func (u *UpdateItemAssign) SQL() string { return sqlJoin(u.Path, ".") + " = " + u.DefaultExpr.SQL() } +func (u *UpdateItemNested) SQL() string { + return "(" + u.DML.SQL() + ")" +} + // ================================================================================ // // Procedural language diff --git a/parser.go b/parser.go index 2d46bdab..5c8f0faa 100644 --- a/parser.go +++ b/parser.go @@ -123,7 +123,7 @@ func (p *Parser) ParseDDLs() ([]ast.DDL, error) { // ParseDML parses a INSERT/DELETE/UPDATE statement. func (p *Parser) ParseDML() (ast.DML, error) { p.nextToken() - dml := p.parseDML() + dml := p.parseDMLTopLevel() if p.Token.Kind != token.TokenEOF { p.errors = append(p.errors, p.errorfAtToken(&p.Token, "expected token: , but: %s", p.Token.Kind)) } @@ -138,7 +138,7 @@ func (p *Parser) ParseDML() (ast.DML, error) { // ParseDMLs parses INSERT/DELETE/UPDATE statements list separated by semi-colon. func (p *Parser) ParseDMLs() ([]ast.DML, error) { p.nextToken() - dmls := parseStatements(p, p.parseDML) + dmls := parseStatements(p, p.parseDMLTopLevel) if p.Token.Kind != token.TokenEOF { p.errors = append(p.errors, p.errorfAtToken(&p.Token, "expected token: , but: %s", p.Token.Kind)) } @@ -166,7 +166,7 @@ func (p *Parser) parseStatement() (stmt ast.Statement) { p.Token.IsKeywordLike("ANALYZE"): return p.parseDDL() case p.Token.IsKeywordLike("INSERT") || p.Token.IsKeywordLike("DELETE") || p.Token.IsKeywordLike("UPDATE"): - return p.parseDML() + return p.parseDMLTopLevel() case p.Token.IsKeywordLike("CALL"): return p.parseOtherStatement() } @@ -4987,7 +4987,12 @@ func (p *Parser) parseIfExists() bool { // // ================================================================================ -func (p *Parser) parseDML() (dml ast.DML) { +// parseDMLTopLevel parses non-nested DML. This function is added for parseStatements friendly. +func (p *Parser) parseDMLTopLevel() (dml ast.DML) { + return p.parseDML(false) +} + +func (p *Parser) parseDML(nested bool) (dml ast.DML) { l := p.Lexer.Clone() defer func() { if r := recover(); r != nil { @@ -4999,7 +5004,7 @@ func (p *Parser) parseDML() (dml ast.DML) { pos := id.Pos switch { case id.IsKeywordLike("INSERT"): - return p.parseInsert(pos) + return p.parseInsert(nested, pos) case id.IsKeywordLike("DELETE"): return p.parseDelete(pos) case id.IsKeywordLike("UPDATE"): @@ -5042,7 +5047,7 @@ func (p *Parser) tryParseThenReturn() *ast.ThenReturn { } } -func (p *Parser) parseInsert(pos token.Pos) *ast.Insert { +func (p *Parser) parseInsert(nested bool, pos token.Pos) *ast.Insert { var insertOrType ast.InsertOrType if p.Token.Kind == "OR" { p.nextToken() @@ -5063,18 +5068,21 @@ func (p *Parser) parseInsert(pos token.Pos) *ast.Insert { name := p.parsePath() - p.expect("(") + // optional when nested var columns []*ast.Ident - if p.Token.Kind != ")" { - for p.Token.Kind != token.TokenEOF { - columns = append(columns, p.parseIdent()) - if p.Token.Kind != "," { - break + if !nested || p.Token.Kind == "(" { + p.expect("(") + if p.Token.Kind != ")" { + for p.Token.Kind != token.TokenEOF { + columns = append(columns, p.parseIdent()) + if p.Token.Kind != "," { + break + } + p.nextToken() } - p.nextToken() } + p.expect(")") } - p.expect(")") var input ast.InsertInput if p.Token.IsKeywordLike("VALUES") { @@ -5191,12 +5199,23 @@ func (p *Parser) parseUpdate(pos token.Pos) *ast.Update { } } -func (p *Parser) parseUpdateItem() *ast.UpdateItem { +func (p *Parser) parseUpdateItem() ast.UpdateItem { + if p.Token.Kind == "(" { + lparen := p.expect("(").Pos + dml := p.parseDML(true) + rparen := p.expect(")").Pos + return &ast.UpdateItemNested{ + Lparen: lparen, + Rparen: rparen, + DML: dml, + } + } + path := p.parseIdentOrPath() p.expect("=") defaultExpr := p.parseDefaultExpr() - return &ast.UpdateItem{ + return &ast.UpdateItemAssign{ Path: path, DefaultExpr: defaultExpr, } From 564abda1eb9155189695b35ad9e380a25bfce513 Mon Sep 17 00:00:00 2001 From: apstndb <803393+apstndb@users.noreply.github.com> Date: Thu, 9 Jan 2025 23:11:49 +0900 Subject: [PATCH 2/8] Update testdata --- .../nested_update_delete_update_insert.sql | 6 + .../input/dml/nested_update_insert_song.sql | 4 + .../dml/nested_update_insert_song_path.sql | 5 + .../input/dml/nested_update_insert_string.sql | 4 + .../nested_update_update_nested_insert.sql | 6 + .../nested_update_update_nested_update.sql | 7 + ...nested_update_delete_update_insert.sql.txt | 266 ++++++++++++++++++ .../dml/nested_update_insert_song.sql.txt | 138 +++++++++ .../nested_update_insert_song_path.sql.txt | 160 +++++++++++ .../dml/nested_update_insert_string.sql.txt | 131 +++++++++ ...nested_update_update_nested_insert.sql.txt | 159 +++++++++++ ...nested_update_update_nested_update.sql.txt | 199 +++++++++++++ testdata/result/dml/update.sql.txt | 8 +- testdata/result/dml/update_as.sql.txt | 4 +- testdata/result/dml/update_fqn.sql.txt | 8 +- .../update_then_return_with_action.sql.txt | 8 +- .../dml/update_with_safe_ml_predict.sql.txt | 6 +- ...nested_update_delete_update_insert.sql.txt | 266 ++++++++++++++++++ .../nested_update_insert_song.sql.txt | 138 +++++++++ .../nested_update_insert_song_path.sql.txt | 160 +++++++++++ .../nested_update_insert_string.sql.txt | 131 +++++++++ ...nested_update_update_nested_insert.sql.txt | 159 +++++++++++ ...nested_update_update_nested_update.sql.txt | 199 +++++++++++++ testdata/result/statement/update.sql.txt | 8 +- testdata/result/statement/update_as.sql.txt | 4 +- testdata/result/statement/update_fqn.sql.txt | 8 +- .../update_then_return_with_action.sql.txt | 8 +- .../update_with_safe_ml_predict.sql.txt | 6 +- 28 files changed, 2172 insertions(+), 34 deletions(-) create mode 100644 testdata/input/dml/nested_update_delete_update_insert.sql create mode 100644 testdata/input/dml/nested_update_insert_song.sql create mode 100644 testdata/input/dml/nested_update_insert_song_path.sql create mode 100644 testdata/input/dml/nested_update_insert_string.sql create mode 100644 testdata/input/dml/nested_update_update_nested_insert.sql create mode 100644 testdata/input/dml/nested_update_update_nested_update.sql create mode 100644 testdata/result/dml/nested_update_delete_update_insert.sql.txt create mode 100644 testdata/result/dml/nested_update_insert_song.sql.txt create mode 100644 testdata/result/dml/nested_update_insert_song_path.sql.txt create mode 100644 testdata/result/dml/nested_update_insert_string.sql.txt create mode 100644 testdata/result/dml/nested_update_update_nested_insert.sql.txt create mode 100644 testdata/result/dml/nested_update_update_nested_update.sql.txt create mode 100644 testdata/result/statement/nested_update_delete_update_insert.sql.txt create mode 100644 testdata/result/statement/nested_update_insert_song.sql.txt create mode 100644 testdata/result/statement/nested_update_insert_song_path.sql.txt create mode 100644 testdata/result/statement/nested_update_insert_string.sql.txt create mode 100644 testdata/result/statement/nested_update_update_nested_insert.sql.txt create mode 100644 testdata/result/statement/nested_update_update_nested_update.sql.txt diff --git a/testdata/input/dml/nested_update_delete_update_insert.sql b/testdata/input/dml/nested_update_delete_update_insert.sql new file mode 100644 index 00000000..6c17f484 --- /dev/null +++ b/testdata/input/dml/nested_update_delete_update_insert.sql @@ -0,0 +1,6 @@ +UPDATE Singers s +SET + (DELETE FROM s.SingerInfo.Residence r WHERE r.City = 'Seattle'), + (UPDATE s.Albums.Song song SET song.songtitle = 'No, This Is Rubbish' WHERE song.songtitle = 'This Is Pretty Good'), + (INSERT s.Albums.Song VALUES ("songtitle: 'The Second Best Song'")) +WHERE SingerId = 3 AND s.Albums.title = 'Go! Go! Go!' \ No newline at end of file diff --git a/testdata/input/dml/nested_update_insert_song.sql b/testdata/input/dml/nested_update_insert_song.sql new file mode 100644 index 00000000..e208516c --- /dev/null +++ b/testdata/input/dml/nested_update_insert_song.sql @@ -0,0 +1,4 @@ +UPDATE Singers s +SET (INSERT s.AlbumInfo.Song(Song) + VALUES ("songtitle: 'Bonus Track', length: 180")) +WHERE s.SingerId = 5 AND s.AlbumInfo.title = "Fire is Hot" \ No newline at end of file diff --git a/testdata/input/dml/nested_update_insert_song_path.sql b/testdata/input/dml/nested_update_insert_song_path.sql new file mode 100644 index 00000000..543c60cd --- /dev/null +++ b/testdata/input/dml/nested_update_insert_song_path.sql @@ -0,0 +1,5 @@ +UPDATE Singers s +SET (INSERT s.AlbumInfo.Song + VALUES ('''songtitle: 'Bonus Track', length:180''')), + s.Albums.tracks = 16 +WHERE s.SingerId = 5 and s.AlbumInfo.title = "Fire is Hot" \ No newline at end of file diff --git a/testdata/input/dml/nested_update_insert_string.sql b/testdata/input/dml/nested_update_insert_string.sql new file mode 100644 index 00000000..fd2a56d1 --- /dev/null +++ b/testdata/input/dml/nested_update_insert_string.sql @@ -0,0 +1,4 @@ +UPDATE Singers s +SET (INSERT s.AlbumInfo.comments + VALUES ("Groovy!")) +WHERE s.SingerId = 5 AND s.AlbumInfo.title = "Fire is Hot" \ No newline at end of file diff --git a/testdata/input/dml/nested_update_update_nested_insert.sql b/testdata/input/dml/nested_update_update_nested_insert.sql new file mode 100644 index 00000000..7d3a0f4f --- /dev/null +++ b/testdata/input/dml/nested_update_update_nested_insert.sql @@ -0,0 +1,6 @@ +UPDATE Singers s +SET (UPDATE s.AlbumInfo.Song so + SET (INSERT INTO so.Chart + VALUES ("chartname: 'Galaxy Top 100', rank: 5")) + WHERE so.songtitle = "Bonus Track") +WHERE s.SingerId = 5 \ No newline at end of file diff --git a/testdata/input/dml/nested_update_update_nested_update.sql b/testdata/input/dml/nested_update_update_nested_update.sql new file mode 100644 index 00000000..eb20a283 --- /dev/null +++ b/testdata/input/dml/nested_update_update_nested_update.sql @@ -0,0 +1,7 @@ +UPDATE Singers s +SET (UPDATE s.AlbumInfo.Song so + SET (UPDATE so.Chart c + SET c.rank = 2 + WHERE c.chartname = "Galaxy Top 100") + WHERE so.songtitle = "Bonus Track") +WHERE s.SingerId = 5 \ No newline at end of file diff --git a/testdata/result/dml/nested_update_delete_update_insert.sql.txt b/testdata/result/dml/nested_update_delete_update_insert.sql.txt new file mode 100644 index 00000000..e4d81a60 --- /dev/null +++ b/testdata/result/dml/nested_update_delete_update_insert.sql.txt @@ -0,0 +1,266 @@ +--- nested_update_delete_update_insert.sql +UPDATE Singers s +SET + (DELETE FROM s.SingerInfo.Residence r WHERE r.City = 'Seattle'), + (UPDATE s.Albums.Song song SET song.songtitle = 'No, This Is Rubbish' WHERE song.songtitle = 'This Is Pretty Good'), + (INSERT s.Albums.Song VALUES ("songtitle: 'The Second Best Song'")) +WHERE SingerId = 3 AND s.Albums.title = 'Go! Go! Go!' +--- AST +&ast.Update{ + TableName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 7, + NameEnd: 14, + Name: "Singers", + }, + }, + }, + As: &ast.AsAlias{ + As: -1, + Alias: &ast.Ident{ + NamePos: 15, + NameEnd: 16, + Name: "s", + }, + }, + Updates: []ast.UpdateItem{ + &ast.UpdateItemNested{ + Lparen: 23, + Rparen: 85, + DML: &ast.Delete{ + Delete: 24, + TableName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 36, + NameEnd: 37, + Name: "s", + }, + &ast.Ident{ + NamePos: 38, + NameEnd: 48, + Name: "SingerInfo", + }, + &ast.Ident{ + NamePos: 49, + NameEnd: 58, + Name: "Residence", + }, + }, + }, + As: &ast.AsAlias{ + As: -1, + Alias: &ast.Ident{ + NamePos: 59, + NameEnd: 60, + Name: "r", + }, + }, + Where: &ast.Where{ + Where: 61, + Expr: &ast.BinaryExpr{ + Op: "=", + Left: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 67, + NameEnd: 68, + Name: "r", + }, + &ast.Ident{ + NamePos: 69, + NameEnd: 73, + Name: "City", + }, + }, + }, + Right: &ast.StringLiteral{ + ValuePos: 76, + ValueEnd: 85, + Value: "Seattle", + }, + }, + }, + }, + }, + &ast.UpdateItemNested{ + Lparen: 90, + Rparen: 204, + DML: &ast.Update{ + Update: 91, + TableName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 98, + NameEnd: 99, + Name: "s", + }, + &ast.Ident{ + NamePos: 100, + NameEnd: 106, + Name: "Albums", + }, + &ast.Ident{ + NamePos: 107, + NameEnd: 111, + Name: "Song", + }, + }, + }, + As: &ast.AsAlias{ + As: -1, + Alias: &ast.Ident{ + NamePos: 112, + NameEnd: 116, + Name: "song", + }, + }, + Updates: []ast.UpdateItem{ + &ast.UpdateItemAssign{ + Path: []*ast.Ident{ + &ast.Ident{ + NamePos: 121, + NameEnd: 125, + Name: "song", + }, + &ast.Ident{ + NamePos: 126, + NameEnd: 135, + Name: "songtitle", + }, + }, + DefaultExpr: &ast.DefaultExpr{ + DefaultPos: -1, + Expr: &ast.StringLiteral{ + ValuePos: 138, + ValueEnd: 159, + Value: "No, This Is Rubbish", + }, + }, + }, + }, + Where: &ast.Where{ + Where: 160, + Expr: &ast.BinaryExpr{ + Op: "=", + Left: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 166, + NameEnd: 170, + Name: "song", + }, + &ast.Ident{ + NamePos: 171, + NameEnd: 180, + Name: "songtitle", + }, + }, + }, + Right: &ast.StringLiteral{ + ValuePos: 183, + ValueEnd: 204, + Value: "This Is Pretty Good", + }, + }, + }, + }, + }, + &ast.UpdateItemNested{ + Lparen: 209, + Rparen: 275, + DML: &ast.Insert{ + Insert: 210, + TableName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 217, + NameEnd: 218, + Name: "s", + }, + &ast.Ident{ + NamePos: 219, + NameEnd: 225, + Name: "Albums", + }, + &ast.Ident{ + NamePos: 226, + NameEnd: 230, + Name: "Song", + }, + }, + }, + Input: &ast.ValuesInput{ + Values: 231, + Rows: []*ast.ValuesRow{ + &ast.ValuesRow{ + Lparen: 238, + Rparen: 274, + Exprs: []*ast.DefaultExpr{ + &ast.DefaultExpr{ + DefaultPos: -1, + Expr: &ast.StringLiteral{ + ValuePos: 239, + ValueEnd: 274, + Value: "songtitle: 'The Second Best Song'", + }, + }, + }, + }, + }, + }, + }, + }, + }, + Where: &ast.Where{ + Where: 277, + Expr: &ast.BinaryExpr{ + Op: "AND", + Left: &ast.BinaryExpr{ + Op: "=", + Left: &ast.Ident{ + NamePos: 283, + NameEnd: 291, + Name: "SingerId", + }, + Right: &ast.IntLiteral{ + ValuePos: 294, + ValueEnd: 295, + Base: 10, + Value: "3", + }, + }, + Right: &ast.BinaryExpr{ + Op: "=", + Left: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 300, + NameEnd: 301, + Name: "s", + }, + &ast.Ident{ + NamePos: 302, + NameEnd: 308, + Name: "Albums", + }, + &ast.Ident{ + NamePos: 309, + NameEnd: 314, + Name: "title", + }, + }, + }, + Right: &ast.StringLiteral{ + ValuePos: 317, + ValueEnd: 330, + Value: "Go! Go! Go!", + }, + }, + }, + }, +} + +--- SQL +UPDATE Singers s SET (DELETE FROM s.SingerInfo.Residence r WHERE r.City = "Seattle"), (UPDATE s.Albums.Song song SET song.songtitle = "No, This Is Rubbish" WHERE song.songtitle = "This Is Pretty Good"), (INSERT INTO s.Albums.Song () VALUES ("songtitle: 'The Second Best Song'")) WHERE SingerId = 3 AND s.Albums.title = "Go! Go! Go!" diff --git a/testdata/result/dml/nested_update_insert_song.sql.txt b/testdata/result/dml/nested_update_insert_song.sql.txt new file mode 100644 index 00000000..0b098568 --- /dev/null +++ b/testdata/result/dml/nested_update_insert_song.sql.txt @@ -0,0 +1,138 @@ +--- nested_update_insert_song.sql +UPDATE Singers s +SET (INSERT s.AlbumInfo.Song(Song) + VALUES ("songtitle: 'Bonus Track', length: 180")) +WHERE s.SingerId = 5 AND s.AlbumInfo.title = "Fire is Hot" +--- AST +&ast.Update{ + TableName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 7, + NameEnd: 14, + Name: "Singers", + }, + }, + }, + As: &ast.AsAlias{ + As: -1, + Alias: &ast.Ident{ + NamePos: 15, + NameEnd: 16, + Name: "s", + }, + }, + Updates: []ast.UpdateItem{ + &ast.UpdateItemNested{ + Lparen: 21, + Rparen: 105, + DML: &ast.Insert{ + Insert: 22, + TableName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 29, + NameEnd: 30, + Name: "s", + }, + &ast.Ident{ + NamePos: 31, + NameEnd: 40, + Name: "AlbumInfo", + }, + &ast.Ident{ + NamePos: 41, + NameEnd: 45, + Name: "Song", + }, + }, + }, + Columns: []*ast.Ident{ + &ast.Ident{ + NamePos: 46, + NameEnd: 50, + Name: "Song", + }, + }, + Input: &ast.ValuesInput{ + Values: 57, + Rows: []*ast.ValuesRow{ + &ast.ValuesRow{ + Lparen: 64, + Rparen: 104, + Exprs: []*ast.DefaultExpr{ + &ast.DefaultExpr{ + DefaultPos: -1, + Expr: &ast.StringLiteral{ + ValuePos: 65, + ValueEnd: 104, + Value: "songtitle: 'Bonus Track', length: 180", + }, + }, + }, + }, + }, + }, + }, + }, + }, + Where: &ast.Where{ + Where: 107, + Expr: &ast.BinaryExpr{ + Op: "AND", + Left: &ast.BinaryExpr{ + Op: "=", + Left: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 113, + NameEnd: 114, + Name: "s", + }, + &ast.Ident{ + NamePos: 115, + NameEnd: 123, + Name: "SingerId", + }, + }, + }, + Right: &ast.IntLiteral{ + ValuePos: 126, + ValueEnd: 127, + Base: 10, + Value: "5", + }, + }, + Right: &ast.BinaryExpr{ + Op: "=", + Left: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 132, + NameEnd: 133, + Name: "s", + }, + &ast.Ident{ + NamePos: 134, + NameEnd: 143, + Name: "AlbumInfo", + }, + &ast.Ident{ + NamePos: 144, + NameEnd: 149, + Name: "title", + }, + }, + }, + Right: &ast.StringLiteral{ + ValuePos: 152, + ValueEnd: 165, + Value: "Fire is Hot", + }, + }, + }, + }, +} + +--- SQL +UPDATE Singers s SET (INSERT INTO s.AlbumInfo.Song (Song) VALUES ("songtitle: 'Bonus Track', length: 180")) WHERE s.SingerId = 5 AND s.AlbumInfo.title = "Fire is Hot" diff --git a/testdata/result/dml/nested_update_insert_song_path.sql.txt b/testdata/result/dml/nested_update_insert_song_path.sql.txt new file mode 100644 index 00000000..1a102009 --- /dev/null +++ b/testdata/result/dml/nested_update_insert_song_path.sql.txt @@ -0,0 +1,160 @@ +--- nested_update_insert_song_path.sql +UPDATE Singers s +SET (INSERT s.AlbumInfo.Song + VALUES ('''songtitle: 'Bonus Track', length:180''')), + s.Albums.tracks = 16 +WHERE s.SingerId = 5 and s.AlbumInfo.title = "Fire is Hot" +--- AST +&ast.Update{ + TableName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 7, + NameEnd: 14, + Name: "Singers", + }, + }, + }, + As: &ast.AsAlias{ + As: -1, + Alias: &ast.Ident{ + NamePos: 15, + NameEnd: 16, + Name: "s", + }, + }, + Updates: []ast.UpdateItem{ + &ast.UpdateItemNested{ + Lparen: 21, + Rparen: 102, + DML: &ast.Insert{ + Insert: 22, + TableName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 29, + NameEnd: 30, + Name: "s", + }, + &ast.Ident{ + NamePos: 31, + NameEnd: 40, + Name: "AlbumInfo", + }, + &ast.Ident{ + NamePos: 41, + NameEnd: 45, + Name: "Song", + }, + }, + }, + Input: &ast.ValuesInput{ + Values: 51, + Rows: []*ast.ValuesRow{ + &ast.ValuesRow{ + Lparen: 58, + Rparen: 101, + Exprs: []*ast.DefaultExpr{ + &ast.DefaultExpr{ + DefaultPos: -1, + Expr: &ast.StringLiteral{ + ValuePos: 59, + ValueEnd: 101, + Value: "songtitle: 'Bonus Track', length:180", + }, + }, + }, + }, + }, + }, + }, + }, + &ast.UpdateItemAssign{ + Path: []*ast.Ident{ + &ast.Ident{ + NamePos: 109, + NameEnd: 110, + Name: "s", + }, + &ast.Ident{ + NamePos: 111, + NameEnd: 117, + Name: "Albums", + }, + &ast.Ident{ + NamePos: 118, + NameEnd: 124, + Name: "tracks", + }, + }, + DefaultExpr: &ast.DefaultExpr{ + DefaultPos: -1, + Expr: &ast.IntLiteral{ + ValuePos: 127, + ValueEnd: 129, + Base: 10, + Value: "16", + }, + }, + }, + }, + Where: &ast.Where{ + Where: 130, + Expr: &ast.BinaryExpr{ + Op: "AND", + Left: &ast.BinaryExpr{ + Op: "=", + Left: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 136, + NameEnd: 137, + Name: "s", + }, + &ast.Ident{ + NamePos: 138, + NameEnd: 146, + Name: "SingerId", + }, + }, + }, + Right: &ast.IntLiteral{ + ValuePos: 149, + ValueEnd: 150, + Base: 10, + Value: "5", + }, + }, + Right: &ast.BinaryExpr{ + Op: "=", + Left: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 155, + NameEnd: 156, + Name: "s", + }, + &ast.Ident{ + NamePos: 157, + NameEnd: 166, + Name: "AlbumInfo", + }, + &ast.Ident{ + NamePos: 167, + NameEnd: 172, + Name: "title", + }, + }, + }, + Right: &ast.StringLiteral{ + ValuePos: 175, + ValueEnd: 188, + Value: "Fire is Hot", + }, + }, + }, + }, +} + +--- SQL +UPDATE Singers s SET (INSERT INTO s.AlbumInfo.Song () VALUES ("songtitle: 'Bonus Track', length:180")), s.Albums.tracks = 16 WHERE s.SingerId = 5 AND s.AlbumInfo.title = "Fire is Hot" diff --git a/testdata/result/dml/nested_update_insert_string.sql.txt b/testdata/result/dml/nested_update_insert_string.sql.txt new file mode 100644 index 00000000..c97a77ef --- /dev/null +++ b/testdata/result/dml/nested_update_insert_string.sql.txt @@ -0,0 +1,131 @@ +--- nested_update_insert_string.sql +UPDATE Singers s +SET (INSERT s.AlbumInfo.comments + VALUES ("Groovy!")) +WHERE s.SingerId = 5 AND s.AlbumInfo.title = "Fire is Hot" +--- AST +&ast.Update{ + TableName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 7, + NameEnd: 14, + Name: "Singers", + }, + }, + }, + As: &ast.AsAlias{ + As: -1, + Alias: &ast.Ident{ + NamePos: 15, + NameEnd: 16, + Name: "s", + }, + }, + Updates: []ast.UpdateItem{ + &ast.UpdateItemNested{ + Lparen: 21, + Rparen: 73, + DML: &ast.Insert{ + Insert: 22, + TableName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 29, + NameEnd: 30, + Name: "s", + }, + &ast.Ident{ + NamePos: 31, + NameEnd: 40, + Name: "AlbumInfo", + }, + &ast.Ident{ + NamePos: 41, + NameEnd: 49, + Name: "comments", + }, + }, + }, + Input: &ast.ValuesInput{ + Values: 55, + Rows: []*ast.ValuesRow{ + &ast.ValuesRow{ + Lparen: 62, + Rparen: 72, + Exprs: []*ast.DefaultExpr{ + &ast.DefaultExpr{ + DefaultPos: -1, + Expr: &ast.StringLiteral{ + ValuePos: 63, + ValueEnd: 72, + Value: "Groovy!", + }, + }, + }, + }, + }, + }, + }, + }, + }, + Where: &ast.Where{ + Where: 75, + Expr: &ast.BinaryExpr{ + Op: "AND", + Left: &ast.BinaryExpr{ + Op: "=", + Left: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 81, + NameEnd: 82, + Name: "s", + }, + &ast.Ident{ + NamePos: 83, + NameEnd: 91, + Name: "SingerId", + }, + }, + }, + Right: &ast.IntLiteral{ + ValuePos: 94, + ValueEnd: 95, + Base: 10, + Value: "5", + }, + }, + Right: &ast.BinaryExpr{ + Op: "=", + Left: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 100, + NameEnd: 101, + Name: "s", + }, + &ast.Ident{ + NamePos: 102, + NameEnd: 111, + Name: "AlbumInfo", + }, + &ast.Ident{ + NamePos: 112, + NameEnd: 117, + Name: "title", + }, + }, + }, + Right: &ast.StringLiteral{ + ValuePos: 120, + ValueEnd: 133, + Value: "Fire is Hot", + }, + }, + }, + }, +} + +--- SQL +UPDATE Singers s SET (INSERT INTO s.AlbumInfo.comments () VALUES ("Groovy!")) WHERE s.SingerId = 5 AND s.AlbumInfo.title = "Fire is Hot" diff --git a/testdata/result/dml/nested_update_update_nested_insert.sql.txt b/testdata/result/dml/nested_update_update_nested_insert.sql.txt new file mode 100644 index 00000000..46237bfa --- /dev/null +++ b/testdata/result/dml/nested_update_update_nested_insert.sql.txt @@ -0,0 +1,159 @@ +--- nested_update_update_nested_insert.sql +UPDATE Singers s +SET (UPDATE s.AlbumInfo.Song so + SET (INSERT INTO so.Chart + VALUES ("chartname: 'Galaxy Top 100', rank: 5")) + WHERE so.songtitle = "Bonus Track") +WHERE s.SingerId = 5 +--- AST +&ast.Update{ + TableName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 7, + NameEnd: 14, + Name: "Singers", + }, + }, + }, + As: &ast.AsAlias{ + As: -1, + Alias: &ast.Ident{ + NamePos: 15, + NameEnd: 16, + Name: "s", + }, + }, + Updates: []ast.UpdateItem{ + &ast.UpdateItemNested{ + Lparen: 21, + Rparen: 175, + DML: &ast.Update{ + Update: 22, + TableName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 29, + NameEnd: 30, + Name: "s", + }, + &ast.Ident{ + NamePos: 31, + NameEnd: 40, + Name: "AlbumInfo", + }, + &ast.Ident{ + NamePos: 41, + NameEnd: 45, + Name: "Song", + }, + }, + }, + As: &ast.AsAlias{ + As: -1, + Alias: &ast.Ident{ + NamePos: 46, + NameEnd: 48, + Name: "so", + }, + }, + Updates: []ast.UpdateItem{ + &ast.UpdateItemNested{ + Lparen: 57, + Rparen: 135, + DML: &ast.Insert{ + Insert: 58, + TableName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 70, + NameEnd: 72, + Name: "so", + }, + &ast.Ident{ + NamePos: 73, + NameEnd: 78, + Name: "Chart", + }, + }, + }, + Input: &ast.ValuesInput{ + Values: 88, + Rows: []*ast.ValuesRow{ + &ast.ValuesRow{ + Lparen: 95, + Rparen: 134, + Exprs: []*ast.DefaultExpr{ + &ast.DefaultExpr{ + DefaultPos: -1, + Expr: &ast.StringLiteral{ + ValuePos: 96, + ValueEnd: 134, + Value: "chartname: 'Galaxy Top 100', rank: 5", + }, + }, + }, + }, + }, + }, + }, + }, + }, + Where: &ast.Where{ + Where: 141, + Expr: &ast.BinaryExpr{ + Op: "=", + Left: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 147, + NameEnd: 149, + Name: "so", + }, + &ast.Ident{ + NamePos: 150, + NameEnd: 159, + Name: "songtitle", + }, + }, + }, + Right: &ast.StringLiteral{ + ValuePos: 162, + ValueEnd: 175, + Value: "Bonus Track", + }, + }, + }, + }, + }, + }, + Where: &ast.Where{ + Where: 177, + Expr: &ast.BinaryExpr{ + Op: "=", + Left: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 183, + NameEnd: 184, + Name: "s", + }, + &ast.Ident{ + NamePos: 185, + NameEnd: 193, + Name: "SingerId", + }, + }, + }, + Right: &ast.IntLiteral{ + ValuePos: 196, + ValueEnd: 197, + Base: 10, + Value: "5", + }, + }, + }, +} + +--- SQL +UPDATE Singers s SET (UPDATE s.AlbumInfo.Song so SET (INSERT INTO so.Chart () VALUES ("chartname: 'Galaxy Top 100', rank: 5")) WHERE so.songtitle = "Bonus Track") WHERE s.SingerId = 5 diff --git a/testdata/result/dml/nested_update_update_nested_update.sql.txt b/testdata/result/dml/nested_update_update_nested_update.sql.txt new file mode 100644 index 00000000..02c31b6a --- /dev/null +++ b/testdata/result/dml/nested_update_update_nested_update.sql.txt @@ -0,0 +1,199 @@ +--- nested_update_update_nested_update.sql +UPDATE Singers s +SET (UPDATE s.AlbumInfo.Song so + SET (UPDATE so.Chart c + SET c.rank = 2 + WHERE c.chartname = "Galaxy Top 100") + WHERE so.songtitle = "Bonus Track") +WHERE s.SingerId = 5 +--- AST +&ast.Update{ + TableName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 7, + NameEnd: 14, + Name: "Singers", + }, + }, + }, + As: &ast.AsAlias{ + As: -1, + Alias: &ast.Ident{ + NamePos: 15, + NameEnd: 16, + Name: "s", + }, + }, + Updates: []ast.UpdateItem{ + &ast.UpdateItemNested{ + Lparen: 21, + Rparen: 189, + DML: &ast.Update{ + Update: 22, + TableName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 29, + NameEnd: 30, + Name: "s", + }, + &ast.Ident{ + NamePos: 31, + NameEnd: 40, + Name: "AlbumInfo", + }, + &ast.Ident{ + NamePos: 41, + NameEnd: 45, + Name: "Song", + }, + }, + }, + As: &ast.AsAlias{ + As: -1, + Alias: &ast.Ident{ + NamePos: 46, + NameEnd: 48, + Name: "so", + }, + }, + Updates: []ast.UpdateItem{ + &ast.UpdateItemNested{ + Lparen: 58, + Rparen: 148, + DML: &ast.Update{ + Update: 59, + TableName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 66, + NameEnd: 68, + Name: "so", + }, + &ast.Ident{ + NamePos: 69, + NameEnd: 74, + Name: "Chart", + }, + }, + }, + As: &ast.AsAlias{ + As: -1, + Alias: &ast.Ident{ + NamePos: 75, + NameEnd: 76, + Name: "c", + }, + }, + Updates: []ast.UpdateItem{ + &ast.UpdateItemAssign{ + Path: []*ast.Ident{ + &ast.Ident{ + NamePos: 91, + NameEnd: 92, + Name: "c", + }, + &ast.Ident{ + NamePos: 93, + NameEnd: 97, + Name: "rank", + }, + }, + DefaultExpr: &ast.DefaultExpr{ + DefaultPos: -1, + Expr: &ast.IntLiteral{ + ValuePos: 100, + ValueEnd: 101, + Base: 10, + Value: "2", + }, + }, + }, + }, + Where: &ast.Where{ + Where: 112, + Expr: &ast.BinaryExpr{ + Op: "=", + Left: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 118, + NameEnd: 119, + Name: "c", + }, + &ast.Ident{ + NamePos: 120, + NameEnd: 129, + Name: "chartname", + }, + }, + }, + Right: &ast.StringLiteral{ + ValuePos: 132, + ValueEnd: 148, + Value: "Galaxy Top 100", + }, + }, + }, + }, + }, + }, + Where: &ast.Where{ + Where: 155, + Expr: &ast.BinaryExpr{ + Op: "=", + Left: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 161, + NameEnd: 163, + Name: "so", + }, + &ast.Ident{ + NamePos: 164, + NameEnd: 173, + Name: "songtitle", + }, + }, + }, + Right: &ast.StringLiteral{ + ValuePos: 176, + ValueEnd: 189, + Value: "Bonus Track", + }, + }, + }, + }, + }, + }, + Where: &ast.Where{ + Where: 191, + Expr: &ast.BinaryExpr{ + Op: "=", + Left: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 197, + NameEnd: 198, + Name: "s", + }, + &ast.Ident{ + NamePos: 199, + NameEnd: 207, + Name: "SingerId", + }, + }, + }, + Right: &ast.IntLiteral{ + ValuePos: 210, + ValueEnd: 211, + Base: 10, + Value: "5", + }, + }, + }, +} + +--- SQL +UPDATE Singers s SET (UPDATE s.AlbumInfo.Song so SET (UPDATE so.Chart c SET c.rank = 2 WHERE c.chartname = "Galaxy Top 100") WHERE so.songtitle = "Bonus Track") WHERE s.SingerId = 5 diff --git a/testdata/result/dml/update.sql.txt b/testdata/result/dml/update.sql.txt index 32a3ca49..ac42f67f 100644 --- a/testdata/result/dml/update.sql.txt +++ b/testdata/result/dml/update.sql.txt @@ -11,8 +11,8 @@ update foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 }, }, }, - Updates: []*ast.UpdateItem{ - &ast.UpdateItem{ + Updates: []ast.UpdateItem{ + &ast.UpdateItemAssign{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 15, @@ -29,7 +29,7 @@ update foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 }, }, }, - &ast.UpdateItem{ + &ast.UpdateItemAssign{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 26, @@ -46,7 +46,7 @@ update foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 }, }, }, - &ast.UpdateItem{ + &ast.UpdateItemAssign{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 37, diff --git a/testdata/result/dml/update_as.sql.txt b/testdata/result/dml/update_as.sql.txt index b7aad4d7..e2b9b1a2 100644 --- a/testdata/result/dml/update_as.sql.txt +++ b/testdata/result/dml/update_as.sql.txt @@ -19,8 +19,8 @@ update foo as F set F.foo = F.bar + 1 where foo = F.bar Name: "F", }, }, - Updates: []*ast.UpdateItem{ - &ast.UpdateItem{ + Updates: []ast.UpdateItem{ + &ast.UpdateItemAssign{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 20, diff --git a/testdata/result/dml/update_fqn.sql.txt b/testdata/result/dml/update_fqn.sql.txt index 9f33753b..d0551e03 100644 --- a/testdata/result/dml/update_fqn.sql.txt +++ b/testdata/result/dml/update_fqn.sql.txt @@ -16,8 +16,8 @@ update sch1.foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 }, }, }, - Updates: []*ast.UpdateItem{ - &ast.UpdateItem{ + Updates: []ast.UpdateItem{ + &ast.UpdateItemAssign{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 20, @@ -34,7 +34,7 @@ update sch1.foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 }, }, }, - &ast.UpdateItem{ + &ast.UpdateItemAssign{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 31, @@ -51,7 +51,7 @@ update sch1.foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 }, }, }, - &ast.UpdateItem{ + &ast.UpdateItemAssign{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 42, diff --git a/testdata/result/dml/update_then_return_with_action.sql.txt b/testdata/result/dml/update_then_return_with_action.sql.txt index 7990f304..0a31310c 100644 --- a/testdata/result/dml/update_then_return_with_action.sql.txt +++ b/testdata/result/dml/update_then_return_with_action.sql.txt @@ -11,8 +11,8 @@ update foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 then return wit }, }, }, - Updates: []*ast.UpdateItem{ - &ast.UpdateItem{ + Updates: []ast.UpdateItem{ + &ast.UpdateItemAssign{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 15, @@ -29,7 +29,7 @@ update foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 then return wit }, }, }, - &ast.UpdateItem{ + &ast.UpdateItemAssign{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 26, @@ -46,7 +46,7 @@ update foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 then return wit }, }, }, - &ast.UpdateItem{ + &ast.UpdateItemAssign{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 37, diff --git a/testdata/result/dml/update_with_safe_ml_predict.sql.txt b/testdata/result/dml/update_with_safe_ml_predict.sql.txt index 0a67f8d7..875eace9 100644 --- a/testdata/result/dml/update_with_safe_ml_predict.sql.txt +++ b/testdata/result/dml/update_with_safe_ml_predict.sql.txt @@ -23,8 +23,8 @@ WHERE products.desc_embed IS NULL }, }, }, - Updates: []*ast.UpdateItem{ - &ast.UpdateItem{ + Updates: []ast.UpdateItem{ + &ast.UpdateItemAssign{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 100, @@ -163,7 +163,7 @@ WHERE products.desc_embed IS NULL }, }, }, - &ast.UpdateItem{ + &ast.UpdateItemAssign{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 340, diff --git a/testdata/result/statement/nested_update_delete_update_insert.sql.txt b/testdata/result/statement/nested_update_delete_update_insert.sql.txt new file mode 100644 index 00000000..e4d81a60 --- /dev/null +++ b/testdata/result/statement/nested_update_delete_update_insert.sql.txt @@ -0,0 +1,266 @@ +--- nested_update_delete_update_insert.sql +UPDATE Singers s +SET + (DELETE FROM s.SingerInfo.Residence r WHERE r.City = 'Seattle'), + (UPDATE s.Albums.Song song SET song.songtitle = 'No, This Is Rubbish' WHERE song.songtitle = 'This Is Pretty Good'), + (INSERT s.Albums.Song VALUES ("songtitle: 'The Second Best Song'")) +WHERE SingerId = 3 AND s.Albums.title = 'Go! Go! Go!' +--- AST +&ast.Update{ + TableName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 7, + NameEnd: 14, + Name: "Singers", + }, + }, + }, + As: &ast.AsAlias{ + As: -1, + Alias: &ast.Ident{ + NamePos: 15, + NameEnd: 16, + Name: "s", + }, + }, + Updates: []ast.UpdateItem{ + &ast.UpdateItemNested{ + Lparen: 23, + Rparen: 85, + DML: &ast.Delete{ + Delete: 24, + TableName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 36, + NameEnd: 37, + Name: "s", + }, + &ast.Ident{ + NamePos: 38, + NameEnd: 48, + Name: "SingerInfo", + }, + &ast.Ident{ + NamePos: 49, + NameEnd: 58, + Name: "Residence", + }, + }, + }, + As: &ast.AsAlias{ + As: -1, + Alias: &ast.Ident{ + NamePos: 59, + NameEnd: 60, + Name: "r", + }, + }, + Where: &ast.Where{ + Where: 61, + Expr: &ast.BinaryExpr{ + Op: "=", + Left: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 67, + NameEnd: 68, + Name: "r", + }, + &ast.Ident{ + NamePos: 69, + NameEnd: 73, + Name: "City", + }, + }, + }, + Right: &ast.StringLiteral{ + ValuePos: 76, + ValueEnd: 85, + Value: "Seattle", + }, + }, + }, + }, + }, + &ast.UpdateItemNested{ + Lparen: 90, + Rparen: 204, + DML: &ast.Update{ + Update: 91, + TableName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 98, + NameEnd: 99, + Name: "s", + }, + &ast.Ident{ + NamePos: 100, + NameEnd: 106, + Name: "Albums", + }, + &ast.Ident{ + NamePos: 107, + NameEnd: 111, + Name: "Song", + }, + }, + }, + As: &ast.AsAlias{ + As: -1, + Alias: &ast.Ident{ + NamePos: 112, + NameEnd: 116, + Name: "song", + }, + }, + Updates: []ast.UpdateItem{ + &ast.UpdateItemAssign{ + Path: []*ast.Ident{ + &ast.Ident{ + NamePos: 121, + NameEnd: 125, + Name: "song", + }, + &ast.Ident{ + NamePos: 126, + NameEnd: 135, + Name: "songtitle", + }, + }, + DefaultExpr: &ast.DefaultExpr{ + DefaultPos: -1, + Expr: &ast.StringLiteral{ + ValuePos: 138, + ValueEnd: 159, + Value: "No, This Is Rubbish", + }, + }, + }, + }, + Where: &ast.Where{ + Where: 160, + Expr: &ast.BinaryExpr{ + Op: "=", + Left: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 166, + NameEnd: 170, + Name: "song", + }, + &ast.Ident{ + NamePos: 171, + NameEnd: 180, + Name: "songtitle", + }, + }, + }, + Right: &ast.StringLiteral{ + ValuePos: 183, + ValueEnd: 204, + Value: "This Is Pretty Good", + }, + }, + }, + }, + }, + &ast.UpdateItemNested{ + Lparen: 209, + Rparen: 275, + DML: &ast.Insert{ + Insert: 210, + TableName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 217, + NameEnd: 218, + Name: "s", + }, + &ast.Ident{ + NamePos: 219, + NameEnd: 225, + Name: "Albums", + }, + &ast.Ident{ + NamePos: 226, + NameEnd: 230, + Name: "Song", + }, + }, + }, + Input: &ast.ValuesInput{ + Values: 231, + Rows: []*ast.ValuesRow{ + &ast.ValuesRow{ + Lparen: 238, + Rparen: 274, + Exprs: []*ast.DefaultExpr{ + &ast.DefaultExpr{ + DefaultPos: -1, + Expr: &ast.StringLiteral{ + ValuePos: 239, + ValueEnd: 274, + Value: "songtitle: 'The Second Best Song'", + }, + }, + }, + }, + }, + }, + }, + }, + }, + Where: &ast.Where{ + Where: 277, + Expr: &ast.BinaryExpr{ + Op: "AND", + Left: &ast.BinaryExpr{ + Op: "=", + Left: &ast.Ident{ + NamePos: 283, + NameEnd: 291, + Name: "SingerId", + }, + Right: &ast.IntLiteral{ + ValuePos: 294, + ValueEnd: 295, + Base: 10, + Value: "3", + }, + }, + Right: &ast.BinaryExpr{ + Op: "=", + Left: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 300, + NameEnd: 301, + Name: "s", + }, + &ast.Ident{ + NamePos: 302, + NameEnd: 308, + Name: "Albums", + }, + &ast.Ident{ + NamePos: 309, + NameEnd: 314, + Name: "title", + }, + }, + }, + Right: &ast.StringLiteral{ + ValuePos: 317, + ValueEnd: 330, + Value: "Go! Go! Go!", + }, + }, + }, + }, +} + +--- SQL +UPDATE Singers s SET (DELETE FROM s.SingerInfo.Residence r WHERE r.City = "Seattle"), (UPDATE s.Albums.Song song SET song.songtitle = "No, This Is Rubbish" WHERE song.songtitle = "This Is Pretty Good"), (INSERT INTO s.Albums.Song () VALUES ("songtitle: 'The Second Best Song'")) WHERE SingerId = 3 AND s.Albums.title = "Go! Go! Go!" diff --git a/testdata/result/statement/nested_update_insert_song.sql.txt b/testdata/result/statement/nested_update_insert_song.sql.txt new file mode 100644 index 00000000..0b098568 --- /dev/null +++ b/testdata/result/statement/nested_update_insert_song.sql.txt @@ -0,0 +1,138 @@ +--- nested_update_insert_song.sql +UPDATE Singers s +SET (INSERT s.AlbumInfo.Song(Song) + VALUES ("songtitle: 'Bonus Track', length: 180")) +WHERE s.SingerId = 5 AND s.AlbumInfo.title = "Fire is Hot" +--- AST +&ast.Update{ + TableName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 7, + NameEnd: 14, + Name: "Singers", + }, + }, + }, + As: &ast.AsAlias{ + As: -1, + Alias: &ast.Ident{ + NamePos: 15, + NameEnd: 16, + Name: "s", + }, + }, + Updates: []ast.UpdateItem{ + &ast.UpdateItemNested{ + Lparen: 21, + Rparen: 105, + DML: &ast.Insert{ + Insert: 22, + TableName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 29, + NameEnd: 30, + Name: "s", + }, + &ast.Ident{ + NamePos: 31, + NameEnd: 40, + Name: "AlbumInfo", + }, + &ast.Ident{ + NamePos: 41, + NameEnd: 45, + Name: "Song", + }, + }, + }, + Columns: []*ast.Ident{ + &ast.Ident{ + NamePos: 46, + NameEnd: 50, + Name: "Song", + }, + }, + Input: &ast.ValuesInput{ + Values: 57, + Rows: []*ast.ValuesRow{ + &ast.ValuesRow{ + Lparen: 64, + Rparen: 104, + Exprs: []*ast.DefaultExpr{ + &ast.DefaultExpr{ + DefaultPos: -1, + Expr: &ast.StringLiteral{ + ValuePos: 65, + ValueEnd: 104, + Value: "songtitle: 'Bonus Track', length: 180", + }, + }, + }, + }, + }, + }, + }, + }, + }, + Where: &ast.Where{ + Where: 107, + Expr: &ast.BinaryExpr{ + Op: "AND", + Left: &ast.BinaryExpr{ + Op: "=", + Left: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 113, + NameEnd: 114, + Name: "s", + }, + &ast.Ident{ + NamePos: 115, + NameEnd: 123, + Name: "SingerId", + }, + }, + }, + Right: &ast.IntLiteral{ + ValuePos: 126, + ValueEnd: 127, + Base: 10, + Value: "5", + }, + }, + Right: &ast.BinaryExpr{ + Op: "=", + Left: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 132, + NameEnd: 133, + Name: "s", + }, + &ast.Ident{ + NamePos: 134, + NameEnd: 143, + Name: "AlbumInfo", + }, + &ast.Ident{ + NamePos: 144, + NameEnd: 149, + Name: "title", + }, + }, + }, + Right: &ast.StringLiteral{ + ValuePos: 152, + ValueEnd: 165, + Value: "Fire is Hot", + }, + }, + }, + }, +} + +--- SQL +UPDATE Singers s SET (INSERT INTO s.AlbumInfo.Song (Song) VALUES ("songtitle: 'Bonus Track', length: 180")) WHERE s.SingerId = 5 AND s.AlbumInfo.title = "Fire is Hot" diff --git a/testdata/result/statement/nested_update_insert_song_path.sql.txt b/testdata/result/statement/nested_update_insert_song_path.sql.txt new file mode 100644 index 00000000..1a102009 --- /dev/null +++ b/testdata/result/statement/nested_update_insert_song_path.sql.txt @@ -0,0 +1,160 @@ +--- nested_update_insert_song_path.sql +UPDATE Singers s +SET (INSERT s.AlbumInfo.Song + VALUES ('''songtitle: 'Bonus Track', length:180''')), + s.Albums.tracks = 16 +WHERE s.SingerId = 5 and s.AlbumInfo.title = "Fire is Hot" +--- AST +&ast.Update{ + TableName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 7, + NameEnd: 14, + Name: "Singers", + }, + }, + }, + As: &ast.AsAlias{ + As: -1, + Alias: &ast.Ident{ + NamePos: 15, + NameEnd: 16, + Name: "s", + }, + }, + Updates: []ast.UpdateItem{ + &ast.UpdateItemNested{ + Lparen: 21, + Rparen: 102, + DML: &ast.Insert{ + Insert: 22, + TableName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 29, + NameEnd: 30, + Name: "s", + }, + &ast.Ident{ + NamePos: 31, + NameEnd: 40, + Name: "AlbumInfo", + }, + &ast.Ident{ + NamePos: 41, + NameEnd: 45, + Name: "Song", + }, + }, + }, + Input: &ast.ValuesInput{ + Values: 51, + Rows: []*ast.ValuesRow{ + &ast.ValuesRow{ + Lparen: 58, + Rparen: 101, + Exprs: []*ast.DefaultExpr{ + &ast.DefaultExpr{ + DefaultPos: -1, + Expr: &ast.StringLiteral{ + ValuePos: 59, + ValueEnd: 101, + Value: "songtitle: 'Bonus Track', length:180", + }, + }, + }, + }, + }, + }, + }, + }, + &ast.UpdateItemAssign{ + Path: []*ast.Ident{ + &ast.Ident{ + NamePos: 109, + NameEnd: 110, + Name: "s", + }, + &ast.Ident{ + NamePos: 111, + NameEnd: 117, + Name: "Albums", + }, + &ast.Ident{ + NamePos: 118, + NameEnd: 124, + Name: "tracks", + }, + }, + DefaultExpr: &ast.DefaultExpr{ + DefaultPos: -1, + Expr: &ast.IntLiteral{ + ValuePos: 127, + ValueEnd: 129, + Base: 10, + Value: "16", + }, + }, + }, + }, + Where: &ast.Where{ + Where: 130, + Expr: &ast.BinaryExpr{ + Op: "AND", + Left: &ast.BinaryExpr{ + Op: "=", + Left: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 136, + NameEnd: 137, + Name: "s", + }, + &ast.Ident{ + NamePos: 138, + NameEnd: 146, + Name: "SingerId", + }, + }, + }, + Right: &ast.IntLiteral{ + ValuePos: 149, + ValueEnd: 150, + Base: 10, + Value: "5", + }, + }, + Right: &ast.BinaryExpr{ + Op: "=", + Left: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 155, + NameEnd: 156, + Name: "s", + }, + &ast.Ident{ + NamePos: 157, + NameEnd: 166, + Name: "AlbumInfo", + }, + &ast.Ident{ + NamePos: 167, + NameEnd: 172, + Name: "title", + }, + }, + }, + Right: &ast.StringLiteral{ + ValuePos: 175, + ValueEnd: 188, + Value: "Fire is Hot", + }, + }, + }, + }, +} + +--- SQL +UPDATE Singers s SET (INSERT INTO s.AlbumInfo.Song () VALUES ("songtitle: 'Bonus Track', length:180")), s.Albums.tracks = 16 WHERE s.SingerId = 5 AND s.AlbumInfo.title = "Fire is Hot" diff --git a/testdata/result/statement/nested_update_insert_string.sql.txt b/testdata/result/statement/nested_update_insert_string.sql.txt new file mode 100644 index 00000000..c97a77ef --- /dev/null +++ b/testdata/result/statement/nested_update_insert_string.sql.txt @@ -0,0 +1,131 @@ +--- nested_update_insert_string.sql +UPDATE Singers s +SET (INSERT s.AlbumInfo.comments + VALUES ("Groovy!")) +WHERE s.SingerId = 5 AND s.AlbumInfo.title = "Fire is Hot" +--- AST +&ast.Update{ + TableName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 7, + NameEnd: 14, + Name: "Singers", + }, + }, + }, + As: &ast.AsAlias{ + As: -1, + Alias: &ast.Ident{ + NamePos: 15, + NameEnd: 16, + Name: "s", + }, + }, + Updates: []ast.UpdateItem{ + &ast.UpdateItemNested{ + Lparen: 21, + Rparen: 73, + DML: &ast.Insert{ + Insert: 22, + TableName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 29, + NameEnd: 30, + Name: "s", + }, + &ast.Ident{ + NamePos: 31, + NameEnd: 40, + Name: "AlbumInfo", + }, + &ast.Ident{ + NamePos: 41, + NameEnd: 49, + Name: "comments", + }, + }, + }, + Input: &ast.ValuesInput{ + Values: 55, + Rows: []*ast.ValuesRow{ + &ast.ValuesRow{ + Lparen: 62, + Rparen: 72, + Exprs: []*ast.DefaultExpr{ + &ast.DefaultExpr{ + DefaultPos: -1, + Expr: &ast.StringLiteral{ + ValuePos: 63, + ValueEnd: 72, + Value: "Groovy!", + }, + }, + }, + }, + }, + }, + }, + }, + }, + Where: &ast.Where{ + Where: 75, + Expr: &ast.BinaryExpr{ + Op: "AND", + Left: &ast.BinaryExpr{ + Op: "=", + Left: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 81, + NameEnd: 82, + Name: "s", + }, + &ast.Ident{ + NamePos: 83, + NameEnd: 91, + Name: "SingerId", + }, + }, + }, + Right: &ast.IntLiteral{ + ValuePos: 94, + ValueEnd: 95, + Base: 10, + Value: "5", + }, + }, + Right: &ast.BinaryExpr{ + Op: "=", + Left: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 100, + NameEnd: 101, + Name: "s", + }, + &ast.Ident{ + NamePos: 102, + NameEnd: 111, + Name: "AlbumInfo", + }, + &ast.Ident{ + NamePos: 112, + NameEnd: 117, + Name: "title", + }, + }, + }, + Right: &ast.StringLiteral{ + ValuePos: 120, + ValueEnd: 133, + Value: "Fire is Hot", + }, + }, + }, + }, +} + +--- SQL +UPDATE Singers s SET (INSERT INTO s.AlbumInfo.comments () VALUES ("Groovy!")) WHERE s.SingerId = 5 AND s.AlbumInfo.title = "Fire is Hot" diff --git a/testdata/result/statement/nested_update_update_nested_insert.sql.txt b/testdata/result/statement/nested_update_update_nested_insert.sql.txt new file mode 100644 index 00000000..46237bfa --- /dev/null +++ b/testdata/result/statement/nested_update_update_nested_insert.sql.txt @@ -0,0 +1,159 @@ +--- nested_update_update_nested_insert.sql +UPDATE Singers s +SET (UPDATE s.AlbumInfo.Song so + SET (INSERT INTO so.Chart + VALUES ("chartname: 'Galaxy Top 100', rank: 5")) + WHERE so.songtitle = "Bonus Track") +WHERE s.SingerId = 5 +--- AST +&ast.Update{ + TableName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 7, + NameEnd: 14, + Name: "Singers", + }, + }, + }, + As: &ast.AsAlias{ + As: -1, + Alias: &ast.Ident{ + NamePos: 15, + NameEnd: 16, + Name: "s", + }, + }, + Updates: []ast.UpdateItem{ + &ast.UpdateItemNested{ + Lparen: 21, + Rparen: 175, + DML: &ast.Update{ + Update: 22, + TableName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 29, + NameEnd: 30, + Name: "s", + }, + &ast.Ident{ + NamePos: 31, + NameEnd: 40, + Name: "AlbumInfo", + }, + &ast.Ident{ + NamePos: 41, + NameEnd: 45, + Name: "Song", + }, + }, + }, + As: &ast.AsAlias{ + As: -1, + Alias: &ast.Ident{ + NamePos: 46, + NameEnd: 48, + Name: "so", + }, + }, + Updates: []ast.UpdateItem{ + &ast.UpdateItemNested{ + Lparen: 57, + Rparen: 135, + DML: &ast.Insert{ + Insert: 58, + TableName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 70, + NameEnd: 72, + Name: "so", + }, + &ast.Ident{ + NamePos: 73, + NameEnd: 78, + Name: "Chart", + }, + }, + }, + Input: &ast.ValuesInput{ + Values: 88, + Rows: []*ast.ValuesRow{ + &ast.ValuesRow{ + Lparen: 95, + Rparen: 134, + Exprs: []*ast.DefaultExpr{ + &ast.DefaultExpr{ + DefaultPos: -1, + Expr: &ast.StringLiteral{ + ValuePos: 96, + ValueEnd: 134, + Value: "chartname: 'Galaxy Top 100', rank: 5", + }, + }, + }, + }, + }, + }, + }, + }, + }, + Where: &ast.Where{ + Where: 141, + Expr: &ast.BinaryExpr{ + Op: "=", + Left: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 147, + NameEnd: 149, + Name: "so", + }, + &ast.Ident{ + NamePos: 150, + NameEnd: 159, + Name: "songtitle", + }, + }, + }, + Right: &ast.StringLiteral{ + ValuePos: 162, + ValueEnd: 175, + Value: "Bonus Track", + }, + }, + }, + }, + }, + }, + Where: &ast.Where{ + Where: 177, + Expr: &ast.BinaryExpr{ + Op: "=", + Left: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 183, + NameEnd: 184, + Name: "s", + }, + &ast.Ident{ + NamePos: 185, + NameEnd: 193, + Name: "SingerId", + }, + }, + }, + Right: &ast.IntLiteral{ + ValuePos: 196, + ValueEnd: 197, + Base: 10, + Value: "5", + }, + }, + }, +} + +--- SQL +UPDATE Singers s SET (UPDATE s.AlbumInfo.Song so SET (INSERT INTO so.Chart () VALUES ("chartname: 'Galaxy Top 100', rank: 5")) WHERE so.songtitle = "Bonus Track") WHERE s.SingerId = 5 diff --git a/testdata/result/statement/nested_update_update_nested_update.sql.txt b/testdata/result/statement/nested_update_update_nested_update.sql.txt new file mode 100644 index 00000000..02c31b6a --- /dev/null +++ b/testdata/result/statement/nested_update_update_nested_update.sql.txt @@ -0,0 +1,199 @@ +--- nested_update_update_nested_update.sql +UPDATE Singers s +SET (UPDATE s.AlbumInfo.Song so + SET (UPDATE so.Chart c + SET c.rank = 2 + WHERE c.chartname = "Galaxy Top 100") + WHERE so.songtitle = "Bonus Track") +WHERE s.SingerId = 5 +--- AST +&ast.Update{ + TableName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 7, + NameEnd: 14, + Name: "Singers", + }, + }, + }, + As: &ast.AsAlias{ + As: -1, + Alias: &ast.Ident{ + NamePos: 15, + NameEnd: 16, + Name: "s", + }, + }, + Updates: []ast.UpdateItem{ + &ast.UpdateItemNested{ + Lparen: 21, + Rparen: 189, + DML: &ast.Update{ + Update: 22, + TableName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 29, + NameEnd: 30, + Name: "s", + }, + &ast.Ident{ + NamePos: 31, + NameEnd: 40, + Name: "AlbumInfo", + }, + &ast.Ident{ + NamePos: 41, + NameEnd: 45, + Name: "Song", + }, + }, + }, + As: &ast.AsAlias{ + As: -1, + Alias: &ast.Ident{ + NamePos: 46, + NameEnd: 48, + Name: "so", + }, + }, + Updates: []ast.UpdateItem{ + &ast.UpdateItemNested{ + Lparen: 58, + Rparen: 148, + DML: &ast.Update{ + Update: 59, + TableName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 66, + NameEnd: 68, + Name: "so", + }, + &ast.Ident{ + NamePos: 69, + NameEnd: 74, + Name: "Chart", + }, + }, + }, + As: &ast.AsAlias{ + As: -1, + Alias: &ast.Ident{ + NamePos: 75, + NameEnd: 76, + Name: "c", + }, + }, + Updates: []ast.UpdateItem{ + &ast.UpdateItemAssign{ + Path: []*ast.Ident{ + &ast.Ident{ + NamePos: 91, + NameEnd: 92, + Name: "c", + }, + &ast.Ident{ + NamePos: 93, + NameEnd: 97, + Name: "rank", + }, + }, + DefaultExpr: &ast.DefaultExpr{ + DefaultPos: -1, + Expr: &ast.IntLiteral{ + ValuePos: 100, + ValueEnd: 101, + Base: 10, + Value: "2", + }, + }, + }, + }, + Where: &ast.Where{ + Where: 112, + Expr: &ast.BinaryExpr{ + Op: "=", + Left: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 118, + NameEnd: 119, + Name: "c", + }, + &ast.Ident{ + NamePos: 120, + NameEnd: 129, + Name: "chartname", + }, + }, + }, + Right: &ast.StringLiteral{ + ValuePos: 132, + ValueEnd: 148, + Value: "Galaxy Top 100", + }, + }, + }, + }, + }, + }, + Where: &ast.Where{ + Where: 155, + Expr: &ast.BinaryExpr{ + Op: "=", + Left: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 161, + NameEnd: 163, + Name: "so", + }, + &ast.Ident{ + NamePos: 164, + NameEnd: 173, + Name: "songtitle", + }, + }, + }, + Right: &ast.StringLiteral{ + ValuePos: 176, + ValueEnd: 189, + Value: "Bonus Track", + }, + }, + }, + }, + }, + }, + Where: &ast.Where{ + Where: 191, + Expr: &ast.BinaryExpr{ + Op: "=", + Left: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 197, + NameEnd: 198, + Name: "s", + }, + &ast.Ident{ + NamePos: 199, + NameEnd: 207, + Name: "SingerId", + }, + }, + }, + Right: &ast.IntLiteral{ + ValuePos: 210, + ValueEnd: 211, + Base: 10, + Value: "5", + }, + }, + }, +} + +--- SQL +UPDATE Singers s SET (UPDATE s.AlbumInfo.Song so SET (UPDATE so.Chart c SET c.rank = 2 WHERE c.chartname = "Galaxy Top 100") WHERE so.songtitle = "Bonus Track") WHERE s.SingerId = 5 diff --git a/testdata/result/statement/update.sql.txt b/testdata/result/statement/update.sql.txt index 32a3ca49..ac42f67f 100644 --- a/testdata/result/statement/update.sql.txt +++ b/testdata/result/statement/update.sql.txt @@ -11,8 +11,8 @@ update foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 }, }, }, - Updates: []*ast.UpdateItem{ - &ast.UpdateItem{ + Updates: []ast.UpdateItem{ + &ast.UpdateItemAssign{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 15, @@ -29,7 +29,7 @@ update foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 }, }, }, - &ast.UpdateItem{ + &ast.UpdateItemAssign{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 26, @@ -46,7 +46,7 @@ update foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 }, }, }, - &ast.UpdateItem{ + &ast.UpdateItemAssign{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 37, diff --git a/testdata/result/statement/update_as.sql.txt b/testdata/result/statement/update_as.sql.txt index b7aad4d7..e2b9b1a2 100644 --- a/testdata/result/statement/update_as.sql.txt +++ b/testdata/result/statement/update_as.sql.txt @@ -19,8 +19,8 @@ update foo as F set F.foo = F.bar + 1 where foo = F.bar Name: "F", }, }, - Updates: []*ast.UpdateItem{ - &ast.UpdateItem{ + Updates: []ast.UpdateItem{ + &ast.UpdateItemAssign{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 20, diff --git a/testdata/result/statement/update_fqn.sql.txt b/testdata/result/statement/update_fqn.sql.txt index 9f33753b..d0551e03 100644 --- a/testdata/result/statement/update_fqn.sql.txt +++ b/testdata/result/statement/update_fqn.sql.txt @@ -16,8 +16,8 @@ update sch1.foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 }, }, }, - Updates: []*ast.UpdateItem{ - &ast.UpdateItem{ + Updates: []ast.UpdateItem{ + &ast.UpdateItemAssign{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 20, @@ -34,7 +34,7 @@ update sch1.foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 }, }, }, - &ast.UpdateItem{ + &ast.UpdateItemAssign{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 31, @@ -51,7 +51,7 @@ update sch1.foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 }, }, }, - &ast.UpdateItem{ + &ast.UpdateItemAssign{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 42, diff --git a/testdata/result/statement/update_then_return_with_action.sql.txt b/testdata/result/statement/update_then_return_with_action.sql.txt index 7990f304..0a31310c 100644 --- a/testdata/result/statement/update_then_return_with_action.sql.txt +++ b/testdata/result/statement/update_then_return_with_action.sql.txt @@ -11,8 +11,8 @@ update foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 then return wit }, }, }, - Updates: []*ast.UpdateItem{ - &ast.UpdateItem{ + Updates: []ast.UpdateItem{ + &ast.UpdateItemAssign{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 15, @@ -29,7 +29,7 @@ update foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 then return wit }, }, }, - &ast.UpdateItem{ + &ast.UpdateItemAssign{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 26, @@ -46,7 +46,7 @@ update foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 then return wit }, }, }, - &ast.UpdateItem{ + &ast.UpdateItemAssign{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 37, diff --git a/testdata/result/statement/update_with_safe_ml_predict.sql.txt b/testdata/result/statement/update_with_safe_ml_predict.sql.txt index 0a67f8d7..875eace9 100644 --- a/testdata/result/statement/update_with_safe_ml_predict.sql.txt +++ b/testdata/result/statement/update_with_safe_ml_predict.sql.txt @@ -23,8 +23,8 @@ WHERE products.desc_embed IS NULL }, }, }, - Updates: []*ast.UpdateItem{ - &ast.UpdateItem{ + Updates: []ast.UpdateItem{ + &ast.UpdateItemAssign{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 100, @@ -163,7 +163,7 @@ WHERE products.desc_embed IS NULL }, }, }, - &ast.UpdateItem{ + &ast.UpdateItemAssign{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 340, From 4c236b1935e6f3722c2964a1be442611d7f70a95 Mon Sep 17 00:00:00 2001 From: apstndb <803393+apstndb@users.noreply.github.com> Date: Fri, 10 Jan 2025 00:20:30 +0900 Subject: [PATCH 3/8] Fix placement of UpdateItem interface --- ast/ast.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ast/ast.go b/ast/ast.go index 19abeb48..6ecc4cb2 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -538,6 +538,15 @@ type InsertInput interface { func (ValuesInput) isInsertInput() {} func (SubQueryInput) isInsertInput() {} +// UpdateItem represents SET clause items in UPDATE. +type UpdateItem interface { + Node + isUpdateItem() +} + +func (UpdateItemAssign) isUpdateItem() {} +func (UpdateItemNested) isUpdateItem() {} + // ChangeStreamFor represents FOR clause in CREATE/ALTER CHANGE STREAM statement. type ChangeStreamFor interface { Node @@ -4054,15 +4063,6 @@ type Update struct { ThenReturn *ThenReturn // optional } -// UpdateItem represents SET clause items in UPDATE. -type UpdateItem interface { - Node - isUpdateItem() -} - -func (UpdateItemAssign) isUpdateItem() {} -func (UpdateItemNested) isUpdateItem() {} - // UpdateItemNested is nested update node in UPDATE statement. // // ({{.DML | sql}}) From 733b9aa67201e74a6f1715debdc47d4c304003e2 Mon Sep 17 00:00:00 2001 From: apstndb <803393+apstndb@users.noreply.github.com> Date: Fri, 10 Jan 2025 00:22:08 +0900 Subject: [PATCH 4/8] Change signature of parseInsert --- parser.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/parser.go b/parser.go index 5c8f0faa..1edea10e 100644 --- a/parser.go +++ b/parser.go @@ -5004,7 +5004,7 @@ func (p *Parser) parseDML(nested bool) (dml ast.DML) { pos := id.Pos switch { case id.IsKeywordLike("INSERT"): - return p.parseInsert(nested, pos) + return p.parseInsert(pos, nested) case id.IsKeywordLike("DELETE"): return p.parseDelete(pos) case id.IsKeywordLike("UPDATE"): @@ -5047,7 +5047,7 @@ func (p *Parser) tryParseThenReturn() *ast.ThenReturn { } } -func (p *Parser) parseInsert(nested bool, pos token.Pos) *ast.Insert { +func (p *Parser) parseInsert(pos token.Pos, nested bool) *ast.Insert { var insertOrType ast.InsertOrType if p.Token.Kind == "OR" { p.nextToken() From 3620b3f94f3103884ad6fc6efae5f300ba34b72a Mon Sep 17 00:00:00 2001 From: apstndb <803393+apstndb@users.noreply.github.com> Date: Fri, 10 Jan 2025 00:31:36 +0900 Subject: [PATCH 5/8] Rename UpdateItem* --- ast/ast.go | 12 ++++++------ ast/pos.go | 8 ++++---- ast/sql.go | 4 ++-- parser.go | 4 ++-- .../dml/nested_update_delete_update_insert.sql.txt | 8 ++++---- .../result/dml/nested_update_insert_song.sql.txt | 2 +- .../dml/nested_update_insert_song_path.sql.txt | 4 ++-- .../result/dml/nested_update_insert_string.sql.txt | 2 +- .../dml/nested_update_update_nested_insert.sql.txt | 4 ++-- .../dml/nested_update_update_nested_update.sql.txt | 6 +++--- testdata/result/dml/update.sql.txt | 6 +++--- testdata/result/dml/update_as.sql.txt | 2 +- testdata/result/dml/update_fqn.sql.txt | 6 +++--- .../dml/update_then_return_with_action.sql.txt | 6 +++--- .../result/dml/update_with_safe_ml_predict.sql.txt | 4 ++-- .../nested_update_delete_update_insert.sql.txt | 8 ++++---- .../statement/nested_update_insert_song.sql.txt | 2 +- .../statement/nested_update_insert_song_path.sql.txt | 4 ++-- .../statement/nested_update_insert_string.sql.txt | 2 +- .../nested_update_update_nested_insert.sql.txt | 4 ++-- .../nested_update_update_nested_update.sql.txt | 6 +++--- testdata/result/statement/update.sql.txt | 6 +++--- testdata/result/statement/update_as.sql.txt | 2 +- testdata/result/statement/update_fqn.sql.txt | 6 +++--- .../statement/update_then_return_with_action.sql.txt | 6 +++--- .../statement/update_with_safe_ml_predict.sql.txt | 4 ++-- 26 files changed, 64 insertions(+), 64 deletions(-) diff --git a/ast/ast.go b/ast/ast.go index 6ecc4cb2..90cdd6f4 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -544,8 +544,8 @@ type UpdateItem interface { isUpdateItem() } -func (UpdateItemAssign) isUpdateItem() {} -func (UpdateItemNested) isUpdateItem() {} +func (UpdateItemSetValue) isUpdateItem() {} +func (UpdateItemDML) isUpdateItem() {} // ChangeStreamFor represents FOR clause in CREATE/ALTER CHANGE STREAM statement. type ChangeStreamFor interface { @@ -4063,10 +4063,10 @@ type Update struct { ThenReturn *ThenReturn // optional } -// UpdateItemNested is nested update node in UPDATE statement. +// UpdateItemDML is nested update UpdateItem node in UPDATE statement. // // ({{.DML | sql}}) -type UpdateItemNested struct { +type UpdateItemDML struct { // pos = Lparen // end = Rparen + 1 @@ -4074,10 +4074,10 @@ type UpdateItemNested struct { DML DML } -// UpdateItemAssign is assignment node in UPDATE statement . +// UpdateItemSetValue is assignment style UpdateItem node in UPDATE statement . // // {{.Path | sqlJoin "."}} = {{.DefaultExpr | sql}} -type UpdateItemAssign struct { +type UpdateItemSetValue struct { // pos = Path[0].pos // end = DefaultExpr.end diff --git a/ast/pos.go b/ast/pos.go index e057afef..6446204c 100644 --- a/ast/pos.go +++ b/ast/pos.go @@ -2062,19 +2062,19 @@ func (u *Update) End() token.Pos { return nodeEnd(nodeChoice(wrapNode(u.ThenReturn), wrapNode(u.Where))) } -func (u *UpdateItemNested) Pos() token.Pos { +func (u *UpdateItemDML) Pos() token.Pos { return u.Lparen } -func (u *UpdateItemNested) End() token.Pos { +func (u *UpdateItemDML) End() token.Pos { return posAdd(u.Rparen, 1) } -func (u *UpdateItemAssign) Pos() token.Pos { +func (u *UpdateItemSetValue) Pos() token.Pos { return nodePos(nodeSliceIndex(u.Path, 0)) } -func (u *UpdateItemAssign) End() token.Pos { +func (u *UpdateItemSetValue) End() token.Pos { return nodeEnd(wrapNode(u.DefaultExpr)) } diff --git a/ast/sql.go b/ast/sql.go index 6dca38ba..944c3662 100644 --- a/ast/sql.go +++ b/ast/sql.go @@ -1351,11 +1351,11 @@ func (u *Update) SQL() string { sqlOpt(" ", u.ThenReturn, "") } -func (u *UpdateItemAssign) SQL() string { +func (u *UpdateItemSetValue) SQL() string { return sqlJoin(u.Path, ".") + " = " + u.DefaultExpr.SQL() } -func (u *UpdateItemNested) SQL() string { +func (u *UpdateItemDML) SQL() string { return "(" + u.DML.SQL() + ")" } diff --git a/parser.go b/parser.go index 1edea10e..40a13efc 100644 --- a/parser.go +++ b/parser.go @@ -5204,7 +5204,7 @@ func (p *Parser) parseUpdateItem() ast.UpdateItem { lparen := p.expect("(").Pos dml := p.parseDML(true) rparen := p.expect(")").Pos - return &ast.UpdateItemNested{ + return &ast.UpdateItemDML{ Lparen: lparen, Rparen: rparen, DML: dml, @@ -5215,7 +5215,7 @@ func (p *Parser) parseUpdateItem() ast.UpdateItem { p.expect("=") defaultExpr := p.parseDefaultExpr() - return &ast.UpdateItemAssign{ + return &ast.UpdateItemSetValue{ Path: path, DefaultExpr: defaultExpr, } diff --git a/testdata/result/dml/nested_update_delete_update_insert.sql.txt b/testdata/result/dml/nested_update_delete_update_insert.sql.txt index e4d81a60..b6e2020b 100644 --- a/testdata/result/dml/nested_update_delete_update_insert.sql.txt +++ b/testdata/result/dml/nested_update_delete_update_insert.sql.txt @@ -25,7 +25,7 @@ WHERE SingerId = 3 AND s.Albums.title = 'Go! Go! Go!' }, }, Updates: []ast.UpdateItem{ - &ast.UpdateItemNested{ + &ast.UpdateItemDML{ Lparen: 23, Rparen: 85, DML: &ast.Delete{ @@ -84,7 +84,7 @@ WHERE SingerId = 3 AND s.Albums.title = 'Go! Go! Go!' }, }, }, - &ast.UpdateItemNested{ + &ast.UpdateItemDML{ Lparen: 90, Rparen: 204, DML: &ast.Update{ @@ -117,7 +117,7 @@ WHERE SingerId = 3 AND s.Albums.title = 'Go! Go! Go!' }, }, Updates: []ast.UpdateItem{ - &ast.UpdateItemAssign{ + &ast.UpdateItemSetValue{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 121, @@ -167,7 +167,7 @@ WHERE SingerId = 3 AND s.Albums.title = 'Go! Go! Go!' }, }, }, - &ast.UpdateItemNested{ + &ast.UpdateItemDML{ Lparen: 209, Rparen: 275, DML: &ast.Insert{ diff --git a/testdata/result/dml/nested_update_insert_song.sql.txt b/testdata/result/dml/nested_update_insert_song.sql.txt index 0b098568..aa53a31b 100644 --- a/testdata/result/dml/nested_update_insert_song.sql.txt +++ b/testdata/result/dml/nested_update_insert_song.sql.txt @@ -23,7 +23,7 @@ WHERE s.SingerId = 5 AND s.AlbumInfo.title = "Fire is Hot" }, }, Updates: []ast.UpdateItem{ - &ast.UpdateItemNested{ + &ast.UpdateItemDML{ Lparen: 21, Rparen: 105, DML: &ast.Insert{ diff --git a/testdata/result/dml/nested_update_insert_song_path.sql.txt b/testdata/result/dml/nested_update_insert_song_path.sql.txt index 1a102009..ce5328ec 100644 --- a/testdata/result/dml/nested_update_insert_song_path.sql.txt +++ b/testdata/result/dml/nested_update_insert_song_path.sql.txt @@ -24,7 +24,7 @@ WHERE s.SingerId = 5 and s.AlbumInfo.title = "Fire is Hot" }, }, Updates: []ast.UpdateItem{ - &ast.UpdateItemNested{ + &ast.UpdateItemDML{ Lparen: 21, Rparen: 102, DML: &ast.Insert{ @@ -69,7 +69,7 @@ WHERE s.SingerId = 5 and s.AlbumInfo.title = "Fire is Hot" }, }, }, - &ast.UpdateItemAssign{ + &ast.UpdateItemSetValue{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 109, diff --git a/testdata/result/dml/nested_update_insert_string.sql.txt b/testdata/result/dml/nested_update_insert_string.sql.txt index c97a77ef..9b841d22 100644 --- a/testdata/result/dml/nested_update_insert_string.sql.txt +++ b/testdata/result/dml/nested_update_insert_string.sql.txt @@ -23,7 +23,7 @@ WHERE s.SingerId = 5 AND s.AlbumInfo.title = "Fire is Hot" }, }, Updates: []ast.UpdateItem{ - &ast.UpdateItemNested{ + &ast.UpdateItemDML{ Lparen: 21, Rparen: 73, DML: &ast.Insert{ diff --git a/testdata/result/dml/nested_update_update_nested_insert.sql.txt b/testdata/result/dml/nested_update_update_nested_insert.sql.txt index 46237bfa..73826642 100644 --- a/testdata/result/dml/nested_update_update_nested_insert.sql.txt +++ b/testdata/result/dml/nested_update_update_nested_insert.sql.txt @@ -25,7 +25,7 @@ WHERE s.SingerId = 5 }, }, Updates: []ast.UpdateItem{ - &ast.UpdateItemNested{ + &ast.UpdateItemDML{ Lparen: 21, Rparen: 175, DML: &ast.Update{ @@ -58,7 +58,7 @@ WHERE s.SingerId = 5 }, }, Updates: []ast.UpdateItem{ - &ast.UpdateItemNested{ + &ast.UpdateItemDML{ Lparen: 57, Rparen: 135, DML: &ast.Insert{ diff --git a/testdata/result/dml/nested_update_update_nested_update.sql.txt b/testdata/result/dml/nested_update_update_nested_update.sql.txt index 02c31b6a..e1a6ca73 100644 --- a/testdata/result/dml/nested_update_update_nested_update.sql.txt +++ b/testdata/result/dml/nested_update_update_nested_update.sql.txt @@ -26,7 +26,7 @@ WHERE s.SingerId = 5 }, }, Updates: []ast.UpdateItem{ - &ast.UpdateItemNested{ + &ast.UpdateItemDML{ Lparen: 21, Rparen: 189, DML: &ast.Update{ @@ -59,7 +59,7 @@ WHERE s.SingerId = 5 }, }, Updates: []ast.UpdateItem{ - &ast.UpdateItemNested{ + &ast.UpdateItemDML{ Lparen: 58, Rparen: 148, DML: &ast.Update{ @@ -87,7 +87,7 @@ WHERE s.SingerId = 5 }, }, Updates: []ast.UpdateItem{ - &ast.UpdateItemAssign{ + &ast.UpdateItemSetValue{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 91, diff --git a/testdata/result/dml/update.sql.txt b/testdata/result/dml/update.sql.txt index ac42f67f..b9c4c895 100644 --- a/testdata/result/dml/update.sql.txt +++ b/testdata/result/dml/update.sql.txt @@ -12,7 +12,7 @@ update foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 }, }, Updates: []ast.UpdateItem{ - &ast.UpdateItemAssign{ + &ast.UpdateItemSetValue{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 15, @@ -29,7 +29,7 @@ update foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 }, }, }, - &ast.UpdateItemAssign{ + &ast.UpdateItemSetValue{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 26, @@ -46,7 +46,7 @@ update foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 }, }, }, - &ast.UpdateItemAssign{ + &ast.UpdateItemSetValue{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 37, diff --git a/testdata/result/dml/update_as.sql.txt b/testdata/result/dml/update_as.sql.txt index e2b9b1a2..c3f10738 100644 --- a/testdata/result/dml/update_as.sql.txt +++ b/testdata/result/dml/update_as.sql.txt @@ -20,7 +20,7 @@ update foo as F set F.foo = F.bar + 1 where foo = F.bar }, }, Updates: []ast.UpdateItem{ - &ast.UpdateItemAssign{ + &ast.UpdateItemSetValue{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 20, diff --git a/testdata/result/dml/update_fqn.sql.txt b/testdata/result/dml/update_fqn.sql.txt index d0551e03..976bf8d8 100644 --- a/testdata/result/dml/update_fqn.sql.txt +++ b/testdata/result/dml/update_fqn.sql.txt @@ -17,7 +17,7 @@ update sch1.foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 }, }, Updates: []ast.UpdateItem{ - &ast.UpdateItemAssign{ + &ast.UpdateItemSetValue{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 20, @@ -34,7 +34,7 @@ update sch1.foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 }, }, }, - &ast.UpdateItemAssign{ + &ast.UpdateItemSetValue{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 31, @@ -51,7 +51,7 @@ update sch1.foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 }, }, }, - &ast.UpdateItemAssign{ + &ast.UpdateItemSetValue{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 42, diff --git a/testdata/result/dml/update_then_return_with_action.sql.txt b/testdata/result/dml/update_then_return_with_action.sql.txt index 0a31310c..8f51ac1a 100644 --- a/testdata/result/dml/update_then_return_with_action.sql.txt +++ b/testdata/result/dml/update_then_return_with_action.sql.txt @@ -12,7 +12,7 @@ update foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 then return wit }, }, Updates: []ast.UpdateItem{ - &ast.UpdateItemAssign{ + &ast.UpdateItemSetValue{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 15, @@ -29,7 +29,7 @@ update foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 then return wit }, }, }, - &ast.UpdateItemAssign{ + &ast.UpdateItemSetValue{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 26, @@ -46,7 +46,7 @@ update foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 then return wit }, }, }, - &ast.UpdateItemAssign{ + &ast.UpdateItemSetValue{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 37, diff --git a/testdata/result/dml/update_with_safe_ml_predict.sql.txt b/testdata/result/dml/update_with_safe_ml_predict.sql.txt index 875eace9..3e01ee53 100644 --- a/testdata/result/dml/update_with_safe_ml_predict.sql.txt +++ b/testdata/result/dml/update_with_safe_ml_predict.sql.txt @@ -24,7 +24,7 @@ WHERE products.desc_embed IS NULL }, }, Updates: []ast.UpdateItem{ - &ast.UpdateItemAssign{ + &ast.UpdateItemSetValue{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 100, @@ -163,7 +163,7 @@ WHERE products.desc_embed IS NULL }, }, }, - &ast.UpdateItemAssign{ + &ast.UpdateItemSetValue{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 340, diff --git a/testdata/result/statement/nested_update_delete_update_insert.sql.txt b/testdata/result/statement/nested_update_delete_update_insert.sql.txt index e4d81a60..b6e2020b 100644 --- a/testdata/result/statement/nested_update_delete_update_insert.sql.txt +++ b/testdata/result/statement/nested_update_delete_update_insert.sql.txt @@ -25,7 +25,7 @@ WHERE SingerId = 3 AND s.Albums.title = 'Go! Go! Go!' }, }, Updates: []ast.UpdateItem{ - &ast.UpdateItemNested{ + &ast.UpdateItemDML{ Lparen: 23, Rparen: 85, DML: &ast.Delete{ @@ -84,7 +84,7 @@ WHERE SingerId = 3 AND s.Albums.title = 'Go! Go! Go!' }, }, }, - &ast.UpdateItemNested{ + &ast.UpdateItemDML{ Lparen: 90, Rparen: 204, DML: &ast.Update{ @@ -117,7 +117,7 @@ WHERE SingerId = 3 AND s.Albums.title = 'Go! Go! Go!' }, }, Updates: []ast.UpdateItem{ - &ast.UpdateItemAssign{ + &ast.UpdateItemSetValue{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 121, @@ -167,7 +167,7 @@ WHERE SingerId = 3 AND s.Albums.title = 'Go! Go! Go!' }, }, }, - &ast.UpdateItemNested{ + &ast.UpdateItemDML{ Lparen: 209, Rparen: 275, DML: &ast.Insert{ diff --git a/testdata/result/statement/nested_update_insert_song.sql.txt b/testdata/result/statement/nested_update_insert_song.sql.txt index 0b098568..aa53a31b 100644 --- a/testdata/result/statement/nested_update_insert_song.sql.txt +++ b/testdata/result/statement/nested_update_insert_song.sql.txt @@ -23,7 +23,7 @@ WHERE s.SingerId = 5 AND s.AlbumInfo.title = "Fire is Hot" }, }, Updates: []ast.UpdateItem{ - &ast.UpdateItemNested{ + &ast.UpdateItemDML{ Lparen: 21, Rparen: 105, DML: &ast.Insert{ diff --git a/testdata/result/statement/nested_update_insert_song_path.sql.txt b/testdata/result/statement/nested_update_insert_song_path.sql.txt index 1a102009..ce5328ec 100644 --- a/testdata/result/statement/nested_update_insert_song_path.sql.txt +++ b/testdata/result/statement/nested_update_insert_song_path.sql.txt @@ -24,7 +24,7 @@ WHERE s.SingerId = 5 and s.AlbumInfo.title = "Fire is Hot" }, }, Updates: []ast.UpdateItem{ - &ast.UpdateItemNested{ + &ast.UpdateItemDML{ Lparen: 21, Rparen: 102, DML: &ast.Insert{ @@ -69,7 +69,7 @@ WHERE s.SingerId = 5 and s.AlbumInfo.title = "Fire is Hot" }, }, }, - &ast.UpdateItemAssign{ + &ast.UpdateItemSetValue{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 109, diff --git a/testdata/result/statement/nested_update_insert_string.sql.txt b/testdata/result/statement/nested_update_insert_string.sql.txt index c97a77ef..9b841d22 100644 --- a/testdata/result/statement/nested_update_insert_string.sql.txt +++ b/testdata/result/statement/nested_update_insert_string.sql.txt @@ -23,7 +23,7 @@ WHERE s.SingerId = 5 AND s.AlbumInfo.title = "Fire is Hot" }, }, Updates: []ast.UpdateItem{ - &ast.UpdateItemNested{ + &ast.UpdateItemDML{ Lparen: 21, Rparen: 73, DML: &ast.Insert{ diff --git a/testdata/result/statement/nested_update_update_nested_insert.sql.txt b/testdata/result/statement/nested_update_update_nested_insert.sql.txt index 46237bfa..73826642 100644 --- a/testdata/result/statement/nested_update_update_nested_insert.sql.txt +++ b/testdata/result/statement/nested_update_update_nested_insert.sql.txt @@ -25,7 +25,7 @@ WHERE s.SingerId = 5 }, }, Updates: []ast.UpdateItem{ - &ast.UpdateItemNested{ + &ast.UpdateItemDML{ Lparen: 21, Rparen: 175, DML: &ast.Update{ @@ -58,7 +58,7 @@ WHERE s.SingerId = 5 }, }, Updates: []ast.UpdateItem{ - &ast.UpdateItemNested{ + &ast.UpdateItemDML{ Lparen: 57, Rparen: 135, DML: &ast.Insert{ diff --git a/testdata/result/statement/nested_update_update_nested_update.sql.txt b/testdata/result/statement/nested_update_update_nested_update.sql.txt index 02c31b6a..e1a6ca73 100644 --- a/testdata/result/statement/nested_update_update_nested_update.sql.txt +++ b/testdata/result/statement/nested_update_update_nested_update.sql.txt @@ -26,7 +26,7 @@ WHERE s.SingerId = 5 }, }, Updates: []ast.UpdateItem{ - &ast.UpdateItemNested{ + &ast.UpdateItemDML{ Lparen: 21, Rparen: 189, DML: &ast.Update{ @@ -59,7 +59,7 @@ WHERE s.SingerId = 5 }, }, Updates: []ast.UpdateItem{ - &ast.UpdateItemNested{ + &ast.UpdateItemDML{ Lparen: 58, Rparen: 148, DML: &ast.Update{ @@ -87,7 +87,7 @@ WHERE s.SingerId = 5 }, }, Updates: []ast.UpdateItem{ - &ast.UpdateItemAssign{ + &ast.UpdateItemSetValue{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 91, diff --git a/testdata/result/statement/update.sql.txt b/testdata/result/statement/update.sql.txt index ac42f67f..b9c4c895 100644 --- a/testdata/result/statement/update.sql.txt +++ b/testdata/result/statement/update.sql.txt @@ -12,7 +12,7 @@ update foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 }, }, Updates: []ast.UpdateItem{ - &ast.UpdateItemAssign{ + &ast.UpdateItemSetValue{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 15, @@ -29,7 +29,7 @@ update foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 }, }, }, - &ast.UpdateItemAssign{ + &ast.UpdateItemSetValue{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 26, @@ -46,7 +46,7 @@ update foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 }, }, }, - &ast.UpdateItemAssign{ + &ast.UpdateItemSetValue{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 37, diff --git a/testdata/result/statement/update_as.sql.txt b/testdata/result/statement/update_as.sql.txt index e2b9b1a2..c3f10738 100644 --- a/testdata/result/statement/update_as.sql.txt +++ b/testdata/result/statement/update_as.sql.txt @@ -20,7 +20,7 @@ update foo as F set F.foo = F.bar + 1 where foo = F.bar }, }, Updates: []ast.UpdateItem{ - &ast.UpdateItemAssign{ + &ast.UpdateItemSetValue{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 20, diff --git a/testdata/result/statement/update_fqn.sql.txt b/testdata/result/statement/update_fqn.sql.txt index d0551e03..976bf8d8 100644 --- a/testdata/result/statement/update_fqn.sql.txt +++ b/testdata/result/statement/update_fqn.sql.txt @@ -17,7 +17,7 @@ update sch1.foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 }, }, Updates: []ast.UpdateItem{ - &ast.UpdateItemAssign{ + &ast.UpdateItemSetValue{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 20, @@ -34,7 +34,7 @@ update sch1.foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 }, }, }, - &ast.UpdateItemAssign{ + &ast.UpdateItemSetValue{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 31, @@ -51,7 +51,7 @@ update sch1.foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 }, }, }, - &ast.UpdateItemAssign{ + &ast.UpdateItemSetValue{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 42, diff --git a/testdata/result/statement/update_then_return_with_action.sql.txt b/testdata/result/statement/update_then_return_with_action.sql.txt index 0a31310c..8f51ac1a 100644 --- a/testdata/result/statement/update_then_return_with_action.sql.txt +++ b/testdata/result/statement/update_then_return_with_action.sql.txt @@ -12,7 +12,7 @@ update foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 then return wit }, }, Updates: []ast.UpdateItem{ - &ast.UpdateItemAssign{ + &ast.UpdateItemSetValue{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 15, @@ -29,7 +29,7 @@ update foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 then return wit }, }, }, - &ast.UpdateItemAssign{ + &ast.UpdateItemSetValue{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 26, @@ -46,7 +46,7 @@ update foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 then return wit }, }, }, - &ast.UpdateItemAssign{ + &ast.UpdateItemSetValue{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 37, diff --git a/testdata/result/statement/update_with_safe_ml_predict.sql.txt b/testdata/result/statement/update_with_safe_ml_predict.sql.txt index 875eace9..3e01ee53 100644 --- a/testdata/result/statement/update_with_safe_ml_predict.sql.txt +++ b/testdata/result/statement/update_with_safe_ml_predict.sql.txt @@ -24,7 +24,7 @@ WHERE products.desc_embed IS NULL }, }, Updates: []ast.UpdateItem{ - &ast.UpdateItemAssign{ + &ast.UpdateItemSetValue{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 100, @@ -163,7 +163,7 @@ WHERE products.desc_embed IS NULL }, }, }, - &ast.UpdateItemAssign{ + &ast.UpdateItemSetValue{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 340, From 3f71a571709797e09d37c24a09f7684d18a0006c Mon Sep 17 00:00:00 2001 From: apstndb <803393+apstndb@users.noreply.github.com> Date: Sun, 12 Jan 2025 13:56:34 +0900 Subject: [PATCH 6/8] Rename to parseDMLInternal --- parser.go | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/parser.go b/parser.go index 40a13efc..48d27f73 100644 --- a/parser.go +++ b/parser.go @@ -123,7 +123,7 @@ func (p *Parser) ParseDDLs() ([]ast.DDL, error) { // ParseDML parses a INSERT/DELETE/UPDATE statement. func (p *Parser) ParseDML() (ast.DML, error) { p.nextToken() - dml := p.parseDMLTopLevel() + dml := p.parseDML() if p.Token.Kind != token.TokenEOF { p.errors = append(p.errors, p.errorfAtToken(&p.Token, "expected token: , but: %s", p.Token.Kind)) } @@ -138,7 +138,7 @@ func (p *Parser) ParseDML() (ast.DML, error) { // ParseDMLs parses INSERT/DELETE/UPDATE statements list separated by semi-colon. func (p *Parser) ParseDMLs() ([]ast.DML, error) { p.nextToken() - dmls := parseStatements(p, p.parseDMLTopLevel) + dmls := parseStatements(p, p.parseDML) if p.Token.Kind != token.TokenEOF { p.errors = append(p.errors, p.errorfAtToken(&p.Token, "expected token: , but: %s", p.Token.Kind)) } @@ -166,7 +166,7 @@ func (p *Parser) parseStatement() (stmt ast.Statement) { p.Token.IsKeywordLike("ANALYZE"): return p.parseDDL() case p.Token.IsKeywordLike("INSERT") || p.Token.IsKeywordLike("DELETE") || p.Token.IsKeywordLike("UPDATE"): - return p.parseDMLTopLevel() + return p.parseDML() case p.Token.IsKeywordLike("CALL"): return p.parseOtherStatement() } @@ -4987,12 +4987,13 @@ func (p *Parser) parseIfExists() bool { // // ================================================================================ -// parseDMLTopLevel parses non-nested DML. This function is added for parseStatements friendly. -func (p *Parser) parseDMLTopLevel() (dml ast.DML) { - return p.parseDML(false) +// parseDML parses non-nested DML. This function is added for parseStatements friendly. +func (p *Parser) parseDML() (dml ast.DML) { + return p.parseDMLInternal(false) } -func (p *Parser) parseDML(nested bool) (dml ast.DML) { +// parseDMLInternal can parse nested and non-nested DML. This behavior is controlled by nested flag. +func (p *Parser) parseDMLInternal(nested bool) (dml ast.DML) { l := p.Lexer.Clone() defer func() { if r := recover(); r != nil { @@ -5202,7 +5203,7 @@ func (p *Parser) parseUpdate(pos token.Pos) *ast.Update { func (p *Parser) parseUpdateItem() ast.UpdateItem { if p.Token.Kind == "(" { lparen := p.expect("(").Pos - dml := p.parseDML(true) + dml := p.parseDMLInternal(true) rparen := p.expect(")").Pos return &ast.UpdateItemDML{ Lparen: lparen, From 1179c567d4e43c703122e1764f0595b535cfb218 Mon Sep 17 00:00:00 2001 From: apstndb <803393+apstndb@users.noreply.github.com> Date: Fri, 17 Jan 2025 20:02:24 +0900 Subject: [PATCH 7/8] Update comment --- parser.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parser.go b/parser.go index cf18b851..f20cb254 100644 --- a/parser.go +++ b/parser.go @@ -5031,7 +5031,7 @@ func (p *Parser) parseDML() (dml ast.DML) { // parseDMLInternal can parse nested and non-nested DML with parsed hints. // The behavior is controlled by nested flag. -// Note: Usually, it is recommended to use parseDML if you want to parse a DML statement. +// Note: It is recommended to use parseDML if you want to parse a complete DML statement. func (p *Parser) parseDMLInternal(hint *ast.Hint, nested bool) (dml ast.DML) { l := p.Lexer.Clone() defer func() { From b14286e0d949d32d3460596c45758baac796dd2d Mon Sep 17 00:00:00 2001 From: apstndb <803393+apstndb@users.noreply.github.com> Date: Sat, 18 Jan 2025 18:42:05 +0900 Subject: [PATCH 8/8] Resolve ambiguity in nested update --- parser.go | 5 +- ...d_update_insert_song_paren_query_input.sql | 5 + ...date_insert_song_paren_query_input.sql.txt | 115 ++++++++++++++++++ ...date_insert_song_paren_query_input.sql.txt | 115 ++++++++++++++++++ 4 files changed, 238 insertions(+), 2 deletions(-) create mode 100644 testdata/input/dml/nested_update_insert_song_paren_query_input.sql create mode 100644 testdata/result/dml/nested_update_insert_song_paren_query_input.sql.txt create mode 100644 testdata/result/statement/nested_update_insert_song_paren_query_input.sql.txt diff --git a/parser.go b/parser.go index f20cb254..181196bf 100644 --- a/parser.go +++ b/parser.go @@ -5111,9 +5111,10 @@ func (p *Parser) parseInsert(pos token.Pos, hint *ast.Hint, nested bool) *ast.In name := p.parsePath() - // optional when nested + // Column list is optional only when nested update(not top-level DML). + // Note: There is a ambiguity between column list and parenthesized query. var columns []*ast.Ident - if !nested || p.Token.Kind == "(" { + if !nested || (p.Token.Kind == "(" && !p.lookaheadSubQuery()) { p.expect("(") if p.Token.Kind != ")" { for p.Token.Kind != token.TokenEOF { diff --git a/testdata/input/dml/nested_update_insert_song_paren_query_input.sql b/testdata/input/dml/nested_update_insert_song_paren_query_input.sql new file mode 100644 index 00000000..7afb195a --- /dev/null +++ b/testdata/input/dml/nested_update_insert_song_paren_query_input.sql @@ -0,0 +1,5 @@ +-- In nested query, column list is optional so there is ambiguity between parenthesized query input and column list. +-- I believe Spanner hasn't yet supported this kind of query, but it can be parsed. +UPDATE Singers s +SET (INSERT s.AlbumInfo.Song (SELECT AS VALUE CAST("songtitle: 'The Second Best Song'" AS googlesql.example.Album.Song))) +WHERE TRUE \ No newline at end of file diff --git a/testdata/result/dml/nested_update_insert_song_paren_query_input.sql.txt b/testdata/result/dml/nested_update_insert_song_paren_query_input.sql.txt new file mode 100644 index 00000000..dec5eece --- /dev/null +++ b/testdata/result/dml/nested_update_insert_song_paren_query_input.sql.txt @@ -0,0 +1,115 @@ +--- nested_update_insert_song_paren_query_input.sql +-- In nested query, column list is optional so there is ambiguity between parenthesized query input and column list. +-- I believe Spanner hasn't yet supported this kind of query, but it can be parsed. +UPDATE Singers s +SET (INSERT s.AlbumInfo.Song (SELECT AS VALUE CAST("songtitle: 'The Second Best Song'" AS googlesql.example.Album.Song))) +WHERE TRUE +--- AST +&ast.Update{ + Update: 201, + TableName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 208, + NameEnd: 215, + Name: "Singers", + }, + }, + }, + As: &ast.AsAlias{ + As: -1, + Alias: &ast.Ident{ + NamePos: 216, + NameEnd: 217, + Name: "s", + }, + }, + Updates: []ast.UpdateItem{ + &ast.UpdateItemDML{ + Lparen: 222, + Rparen: 338, + DML: &ast.Insert{ + Insert: 223, + TableName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 230, + NameEnd: 231, + Name: "s", + }, + &ast.Ident{ + NamePos: 232, + NameEnd: 241, + Name: "AlbumInfo", + }, + &ast.Ident{ + NamePos: 242, + NameEnd: 246, + Name: "Song", + }, + }, + }, + Input: &ast.SubQueryInput{ + Query: &ast.SubQuery{ + Lparen: 247, + Rparen: 337, + Query: &ast.Select{ + Select: 248, + As: &ast.AsValue{ + As: 255, + Value: 258, + }, + Results: []ast.SelectItem{ + &ast.ExprSelectItem{ + Expr: &ast.CastExpr{ + Cast: 264, + Rparen: 336, + Expr: &ast.StringLiteral{ + ValuePos: 269, + ValueEnd: 304, + Value: "songtitle: 'The Second Best Song'", + }, + Type: &ast.NamedType{ + Path: []*ast.Ident{ + &ast.Ident{ + NamePos: 308, + NameEnd: 317, + Name: "googlesql", + }, + &ast.Ident{ + NamePos: 318, + NameEnd: 325, + Name: "example", + }, + &ast.Ident{ + NamePos: 326, + NameEnd: 331, + Name: "Album", + }, + &ast.Ident{ + NamePos: 332, + NameEnd: 336, + Name: "Song", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Where: &ast.Where{ + Where: 340, + Expr: &ast.BoolLiteral{ + ValuePos: 346, + Value: true, + }, + }, +} + +--- SQL +UPDATE Singers s SET (INSERT INTO s.AlbumInfo.Song () (SELECT AS VALUE CAST("songtitle: 'The Second Best Song'" AS googlesql.example.Album.Song))) WHERE TRUE diff --git a/testdata/result/statement/nested_update_insert_song_paren_query_input.sql.txt b/testdata/result/statement/nested_update_insert_song_paren_query_input.sql.txt new file mode 100644 index 00000000..dec5eece --- /dev/null +++ b/testdata/result/statement/nested_update_insert_song_paren_query_input.sql.txt @@ -0,0 +1,115 @@ +--- nested_update_insert_song_paren_query_input.sql +-- In nested query, column list is optional so there is ambiguity between parenthesized query input and column list. +-- I believe Spanner hasn't yet supported this kind of query, but it can be parsed. +UPDATE Singers s +SET (INSERT s.AlbumInfo.Song (SELECT AS VALUE CAST("songtitle: 'The Second Best Song'" AS googlesql.example.Album.Song))) +WHERE TRUE +--- AST +&ast.Update{ + Update: 201, + TableName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 208, + NameEnd: 215, + Name: "Singers", + }, + }, + }, + As: &ast.AsAlias{ + As: -1, + Alias: &ast.Ident{ + NamePos: 216, + NameEnd: 217, + Name: "s", + }, + }, + Updates: []ast.UpdateItem{ + &ast.UpdateItemDML{ + Lparen: 222, + Rparen: 338, + DML: &ast.Insert{ + Insert: 223, + TableName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 230, + NameEnd: 231, + Name: "s", + }, + &ast.Ident{ + NamePos: 232, + NameEnd: 241, + Name: "AlbumInfo", + }, + &ast.Ident{ + NamePos: 242, + NameEnd: 246, + Name: "Song", + }, + }, + }, + Input: &ast.SubQueryInput{ + Query: &ast.SubQuery{ + Lparen: 247, + Rparen: 337, + Query: &ast.Select{ + Select: 248, + As: &ast.AsValue{ + As: 255, + Value: 258, + }, + Results: []ast.SelectItem{ + &ast.ExprSelectItem{ + Expr: &ast.CastExpr{ + Cast: 264, + Rparen: 336, + Expr: &ast.StringLiteral{ + ValuePos: 269, + ValueEnd: 304, + Value: "songtitle: 'The Second Best Song'", + }, + Type: &ast.NamedType{ + Path: []*ast.Ident{ + &ast.Ident{ + NamePos: 308, + NameEnd: 317, + Name: "googlesql", + }, + &ast.Ident{ + NamePos: 318, + NameEnd: 325, + Name: "example", + }, + &ast.Ident{ + NamePos: 326, + NameEnd: 331, + Name: "Album", + }, + &ast.Ident{ + NamePos: 332, + NameEnd: 336, + Name: "Song", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Where: &ast.Where{ + Where: 340, + Expr: &ast.BoolLiteral{ + ValuePos: 346, + Value: true, + }, + }, +} + +--- SQL +UPDATE Singers s SET (INSERT INTO s.AlbumInfo.Song () (SELECT AS VALUE CAST("songtitle: 'The Second Best Song'" AS googlesql.example.Album.Song))) WHERE TRUE