From d13eabaa54a20742045e1674cdc78f469ee5a33b Mon Sep 17 00:00:00 2001 From: adranwit Date: Thu, 2 Nov 2023 19:52:19 -0700 Subject: [PATCH] patched except --- list.go | 4 ++-- query.go | 12 +++++++++++- query_test.go | 19 +++++++++++++++---- stringify.go | 6 ++++++ 4 files changed, 34 insertions(+), 7 deletions(-) diff --git a/list.go b/list.go index dc6f090..62da1b8 100644 --- a/list.go +++ b/list.go @@ -17,7 +17,7 @@ func parseSelectListItem(cursor *parsly.Cursor, list *query.List) error { item.Comments = match.Text(cursor) } list.Append(item) - match := cursor.MatchAfterOptional(whitespaceMatcher, inlineCommentMatcher, commentBlockMatcher, binaryOperatorMatcher, logicalOperatorMatcher, nextMatcher) + match := cursor.MatchAfterOptional(whitespaceMatcher, exceptKeywordMatcher, inlineCommentMatcher, commentBlockMatcher, binaryOperatorMatcher, logicalOperatorMatcher, nextMatcher) switch match.Code { case commentBlock: item.Comments = match.Text(cursor) @@ -167,7 +167,7 @@ func parseGroupByList(cursor *parsly.Cursor, list *query.List) error { return nil } -//ParseList parses list +// ParseList parses list func ParseList(raw string) (query.List, error) { cursor := parsly.NewCursor("", []byte(raw), 0) list := query.List{} diff --git a/query.go b/query.go index 5428847..61a09aa 100644 --- a/query.go +++ b/query.go @@ -228,10 +228,20 @@ func matchPostFrom(cursor *parsly.Cursor, dest *query.Select, match *parsly.Toke } func expectExpectIdentifiers(cursor *parsly.Cursor, expect *[]string) (bool, error) { - match := cursor.MatchAfterOptional(whitespaceMatcher, identifierMatcher) + pos := cursor.Pos + match := cursor.MatchAfterOptional(whitespaceMatcher, parenthesesMatcher, identifierMatcher) switch match.Code { + case parenthesesCode: + block := match.Text(cursor) + for _, item := range strings.Split(block[1:len(block)-1], ",") { + *expect = append(*expect, strings.TrimSpace(item)) + } case identifierCode: item := match.Text(cursor) + if cursor.Pos < len(cursor.Input) && cursor.Input[cursor.Pos] == '(' { + cursor.Pos = pos + return false, nil + } *expect = append(*expect, item) default: return false, nil diff --git a/query_test.go b/query_test.go index b63b822..86aa707 100644 --- a/query_test.go +++ b/query_test.go @@ -22,6 +22,17 @@ func TestParseSelect(t *testing.T) { options []Option }{ + { + description: "except", + SQL: "SELECT main.* EXCEPT(Id), cast(main AS Record), cardinality(main, 'One') AS main FROM ta", + expect: "SELECT main.* EXCEPT Id, cast(main AS Record), cardinality(main, 'One') AS main FROM ta", + }, + { + description: "except group", + SQL: "SELECT main.* EXCEPT (Id,Name), cast(main AS Record), cardinality(main, 'One') AS main FROM ta", + expect: "SELECT main.* EXCEPT (Id, Name), cast(main AS Record), cardinality(main, 'One') AS main FROM ta", + }, + { description: "criteria with expr", SQL: "SELECT Name FROM BAR WHERE ${predicate}", @@ -96,13 +107,13 @@ func TestParseSelect(t *testing.T) { { description: "except select", SQL: "SELECT * EXCEPT c1,c2 FROM x t", - expect: "SELECT * EXCEPT c1, c2 FROM x t", + expect: "SELECT * EXCEPT (c1, c2) FROM x t", }, { description: "except select", SQL: "SELECT t1.* EXCEPT c1,c2, t2.* EXCEPT c3 FROM x t1 JOIN y AS t2 ON t1.ID=t2.ID", - expect: "SELECT t1.* EXCEPT c1, c2, t2.* EXCEPT c3 FROM x t1 JOIN y t2 ON t1.ID = t2.ID", + expect: "SELECT t1.* EXCEPT (c1, c2), t2.* EXCEPT c3 FROM x t1 JOIN y t2 ON t1.ID = t2.ID", }, { @@ -164,13 +175,13 @@ func TestParseSelect(t *testing.T) { { description: "except select", SQL: "SELECT * EXCEPT c1,c2 FROM x t", - expect: "SELECT * EXCEPT c1, c2 FROM x t", + expect: "SELECT * EXCEPT (c1, c2) FROM x t", }, { description: "except select", SQL: "SELECT t1.* EXCEPT c1,c2, t2.* EXCEPT c3 FROM x t1 JOIN y AS t2 ON t1.ID=t2.ID", - expect: "SELECT t1.* EXCEPT c1, c2, t2.* EXCEPT c3 FROM x t1 JOIN y t2 ON t1.ID = t2.ID", + expect: "SELECT t1.* EXCEPT (c1, c2), t2.* EXCEPT c3 FROM x t1 JOIN y t2 ON t1.ID = t2.ID", }, { diff --git a/stringify.go b/stringify.go index f3d37a6..1c276fd 100644 --- a/stringify.go +++ b/stringify.go @@ -107,12 +107,18 @@ func stringify(n node.Node, builder *bytes.Buffer) { stringify(actual.X, builder) if len(actual.Except) > 0 { builder.WriteString(" EXCEPT ") + if len(actual.Except) > 1 { + builder.WriteString("(") + } for i, item := range actual.Except { if i > 0 { builder.WriteString(", ") } builder.WriteString(item) } + if len(actual.Except) > 1 { + builder.WriteString(")") + } } if actual.Comments != "" { builder.WriteString(" ")