diff --git a/ast/ast.go b/ast/ast.go index 0d941103..54e003cb 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -539,6 +539,15 @@ type InsertInput interface { func (ValuesInput) isInsertInput() {} func (SubQueryInput) isInsertInput() {} +// UpdateItem represents SET clause items in UPDATE. +type UpdateItem interface { + Node + isUpdateItem() +} + +func (UpdateItemSetValue) isUpdateItem() {} +func (UpdateItemDML) isUpdateItem() {} + // ChangeStreamFor represents FOR clause in CREATE/ALTER CHANGE STREAM statement. type ChangeStreamFor interface { Node @@ -3953,7 +3962,7 @@ type Insert struct { Hint *Hint // optional TableName *Path - Columns []*Ident + Columns []*Ident // optional when nested Input InsertInput ThenReturn *ThenReturn // optional } @@ -4037,16 +4046,27 @@ type Update struct { Hint *Hint // optional 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. +// UpdateItemDML is nested update UpdateItem node in UPDATE statement. +// +// ({{.DML | sql}}) +type UpdateItemDML struct { + // pos = Lparen + // end = Rparen + 1 + + Lparen, Rparen token.Pos // position of "(", ")" + DML DML +} + +// UpdateItemSetValue is assignment style UpdateItem node in UPDATE statement . // // {{.Path | sqlJoin "."}} = {{.DefaultExpr | sql}} -type UpdateItem struct { +type UpdateItemSetValue struct { // pos = Path[0].pos // end = DefaultExpr.end diff --git a/ast/pos.go b/ast/pos.go index b6ff62b2..55e68d86 100644 --- a/ast/pos.go +++ b/ast/pos.go @@ -2046,11 +2046,19 @@ func (u *Update) End() token.Pos { return nodeEnd(nodeChoice(wrapNode(u.ThenReturn), wrapNode(u.Where))) } -func (u *UpdateItem) Pos() token.Pos { +func (u *UpdateItemDML) Pos() token.Pos { + return u.Lparen +} + +func (u *UpdateItemDML) End() token.Pos { + return posAdd(u.Rparen, 1) +} + +func (u *UpdateItemSetValue) Pos() token.Pos { return nodePos(nodeSliceIndex(u.Path, 0)) } -func (u *UpdateItem) 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 530d84ae..e339b985 100644 --- a/ast/sql.go +++ b/ast/sql.go @@ -1354,10 +1354,14 @@ func (u *Update) SQL() string { sqlOpt(" ", u.ThenReturn, "") } -func (u *UpdateItem) SQL() string { +func (u *UpdateItemSetValue) SQL() string { return sqlJoin(u.Path, ".") + " = " + u.DefaultExpr.SQL() } +func (u *UpdateItemDML) SQL() string { + return "(" + u.DML.SQL() + ")" +} + // ================================================================================ // // Procedural language diff --git a/ast/walk_internal.go b/ast/walk_internal.go index 5a351eb8..ddf6b3f2 100644 --- a/ast/walk_internal.go +++ b/ast/walk_internal.go @@ -937,7 +937,10 @@ func walkInternal(node Node, v Visitor, stack []*stackItem) []*stackItem { stack = append(stack, &stackItem{node: wrapNode(n.TableName), visitor: v.Field("TableName")}) stack = append(stack, &stackItem{node: wrapNode(n.Hint), visitor: v.Field("Hint")}) - case *UpdateItem: + case *UpdateItemDML: + stack = append(stack, &stackItem{node: wrapNode(n.DML), visitor: v.Field("DML")}) + + case *UpdateItemSetValue: stack = append(stack, &stackItem{node: wrapNode(n.DefaultExpr), visitor: v.Field("DefaultExpr")}) stack = append(stack, &stackItem{nodes: wrapNodes(n.Path), visitor: v.Field("Path")}) diff --git a/parser.go b/parser.go index cb5f8eb9..181196bf 100644 --- a/parser.go +++ b/parser.go @@ -178,7 +178,7 @@ func (p *Parser) parseStatementInternal(hint *ast.Hint) (stmt ast.Statement) { case p.Token.Kind == "SELECT" || p.Token.Kind == "WITH" || p.Token.Kind == "(" || p.Token.Kind == "FROM": return p.parseQueryStatementInternal(hint) case p.Token.IsKeywordLike("INSERT") || p.Token.IsKeywordLike("DELETE") || p.Token.IsKeywordLike("UPDATE"): - return p.parseDMLInternal(hint) + return p.parseDMLInternal(hint, false) case hint != nil: panic(p.errorfAtPosition(hint.Pos(), p.Token.End, "statement hint is only permitted before query or DML, but got: %s", p.Token.Raw)) case p.Token.Kind == "CREATE" || p.Token.IsKeywordLike("ALTER") || p.Token.IsKeywordLike("DROP") || @@ -5014,6 +5014,8 @@ func (p *Parser) parseIfExists() bool { // // ================================================================================ +// parseDML parses non-nested DML with optional hints. +// This function is parseStatements friendly. func (p *Parser) parseDML() (dml ast.DML) { l := p.Lexer.Clone() defer func() { @@ -5024,10 +5026,13 @@ func (p *Parser) parseDML() (dml ast.DML) { }() hint := p.tryParseHint() - return p.parseDMLInternal(hint) + return p.parseDMLInternal(hint, false) } -func (p *Parser) parseDMLInternal(hint *ast.Hint) (dml ast.DML) { +// parseDMLInternal can parse nested and non-nested DML with parsed hints. +// The behavior is controlled by nested flag. +// 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() { if r := recover(); r != nil { @@ -5042,7 +5047,7 @@ func (p *Parser) parseDMLInternal(hint *ast.Hint) (dml ast.DML) { pos := id.Pos switch { case id.IsKeywordLike("INSERT"): - return p.parseInsert(pos, hint) + return p.parseInsert(pos, hint, nested) case id.IsKeywordLike("DELETE"): return p.parseDelete(pos, hint) case id.IsKeywordLike("UPDATE"): @@ -5085,7 +5090,7 @@ func (p *Parser) tryParseThenReturn() *ast.ThenReturn { } } -func (p *Parser) parseInsert(pos token.Pos, hint *ast.Hint) *ast.Insert { +func (p *Parser) parseInsert(pos token.Pos, hint *ast.Hint, nested bool) *ast.Insert { var insertOrType ast.InsertOrType if p.Token.Kind == "OR" { p.nextToken() @@ -5106,18 +5111,22 @@ func (p *Parser) parseInsert(pos token.Pos, hint *ast.Hint) *ast.Insert { name := p.parsePath() - p.expect("(") + // 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 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.lookaheadSubQuery()) { + 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") { @@ -5237,12 +5246,23 @@ func (p *Parser) parseUpdate(pos token.Pos, hint *ast.Hint) *ast.Update { } } -func (p *Parser) parseUpdateItem() *ast.UpdateItem { +func (p *Parser) parseUpdateItem() ast.UpdateItem { + if p.Token.Kind == "(" { + lparen := p.expect("(").Pos + dml := p.parseDMLInternal(nil, true) + rparen := p.expect(")").Pos + return &ast.UpdateItemDML{ + Lparen: lparen, + Rparen: rparen, + DML: dml, + } + } + path := p.parseIdentOrPath() p.expect("=") defaultExpr := p.parseDefaultExpr() - return &ast.UpdateItem{ + return &ast.UpdateItemSetValue{ Path: path, DefaultExpr: defaultExpr, } 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_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/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..b6e2020b --- /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.UpdateItemDML{ + 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.UpdateItemDML{ + 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.UpdateItemSetValue{ + 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.UpdateItemDML{ + 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..aa53a31b --- /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.UpdateItemDML{ + 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_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/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..ce5328ec --- /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.UpdateItemDML{ + 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.UpdateItemSetValue{ + 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..9b841d22 --- /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.UpdateItemDML{ + 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..73826642 --- /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.UpdateItemDML{ + 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.UpdateItemDML{ + 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..e1a6ca73 --- /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.UpdateItemDML{ + 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.UpdateItemDML{ + 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.UpdateItemSetValue{ + 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..b9c4c895 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.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.UpdateItem{ + &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.UpdateItem{ + &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 b7aad4d7..c3f10738 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.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 9f33753b..976bf8d8 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.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.UpdateItem{ + &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.UpdateItem{ + &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 7990f304..8f51ac1a 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.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.UpdateItem{ + &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.UpdateItem{ + &ast.UpdateItemSetValue{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 37, diff --git a/testdata/result/dml/update_with_hint.sql.txt b/testdata/result/dml/update_with_hint.sql.txt index a8829eb0..81cf0e5f 100644 --- a/testdata/result/dml/update_with_hint.sql.txt +++ b/testdata/result/dml/update_with_hint.sql.txt @@ -35,8 +35,8 @@ update foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 }, }, }, - Updates: []*ast.UpdateItem{ - &ast.UpdateItem{ + Updates: []ast.UpdateItem{ + &ast.UpdateItemSetValue{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 41, @@ -53,7 +53,7 @@ update foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 }, }, }, - &ast.UpdateItem{ + &ast.UpdateItemSetValue{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 52, @@ -70,7 +70,7 @@ update foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 }, }, }, - &ast.UpdateItem{ + &ast.UpdateItemSetValue{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 63, 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..3e01ee53 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.UpdateItemSetValue{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 100, @@ -163,7 +163,7 @@ WHERE products.desc_embed IS NULL }, }, }, - &ast.UpdateItem{ + &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 new file mode 100644 index 00000000..b6e2020b --- /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.UpdateItemDML{ + 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.UpdateItemDML{ + 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.UpdateItemSetValue{ + 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.UpdateItemDML{ + 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..aa53a31b --- /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.UpdateItemDML{ + 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_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 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..ce5328ec --- /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.UpdateItemDML{ + 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.UpdateItemSetValue{ + 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..9b841d22 --- /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.UpdateItemDML{ + 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..73826642 --- /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.UpdateItemDML{ + 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.UpdateItemDML{ + 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..e1a6ca73 --- /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.UpdateItemDML{ + 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.UpdateItemDML{ + 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.UpdateItemSetValue{ + 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..b9c4c895 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.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.UpdateItem{ + &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.UpdateItem{ + &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 b7aad4d7..c3f10738 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.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 9f33753b..976bf8d8 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.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.UpdateItem{ + &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.UpdateItem{ + &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 7990f304..8f51ac1a 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.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.UpdateItem{ + &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.UpdateItem{ + &ast.UpdateItemSetValue{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 37, diff --git a/testdata/result/statement/update_with_hint.sql.txt b/testdata/result/statement/update_with_hint.sql.txt index a8829eb0..81cf0e5f 100644 --- a/testdata/result/statement/update_with_hint.sql.txt +++ b/testdata/result/statement/update_with_hint.sql.txt @@ -35,8 +35,8 @@ update foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 }, }, }, - Updates: []*ast.UpdateItem{ - &ast.UpdateItem{ + Updates: []ast.UpdateItem{ + &ast.UpdateItemSetValue{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 41, @@ -53,7 +53,7 @@ update foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 }, }, }, - &ast.UpdateItem{ + &ast.UpdateItemSetValue{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 52, @@ -70,7 +70,7 @@ update foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 }, }, }, - &ast.UpdateItem{ + &ast.UpdateItemSetValue{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 63, 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..3e01ee53 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.UpdateItemSetValue{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 100, @@ -163,7 +163,7 @@ WHERE products.desc_embed IS NULL }, }, }, - &ast.UpdateItem{ + &ast.UpdateItemSetValue{ Path: []*ast.Ident{ &ast.Ident{ NamePos: 340,