group_by() function (#163)

- `group_by()` now accepts function as argument.
- Refactored grammar.
- Broad improvements to function implementation.
This commit is contained in:
Neil O'Toole 2023-03-26 20:03:40 -06:00 committed by GitHub
parent 0db02b533d
commit fac3a27d7b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 1742 additions and 1489 deletions

View File

@ -4,7 +4,13 @@ import (
"strings"
"testing"
"github.com/neilotoole/sq/cli/output"
"github.com/neilotoole/sq/drivers/sqlserver"
"github.com/neilotoole/sq/drivers/postgres"
"github.com/neilotoole/sq/drivers/sqlite3"
"golang.org/x/exp/slices"
"github.com/neilotoole/sq/drivers/mysql"
@ -35,9 +41,14 @@ func TestSLQ2SQLNew(t *testing.T) {
// wantSQL is the wanted SQL
wantSQL string
// override allows an alternative "wantSQL" for a specific driver type.
// For example, MySQL uses backtick as the quote char.
override map[source.Type]string
// onlyFor indicates that this test should only run on sources of
// the specified types. When empty, the test is executed on all types.
onlyFor []source.Type
// overrideWantSQL allows an alternative "wantSQL" for a specific driver type.
// For example, MySQL uses backtick as the quote char, so it needs
// a separate wantSQL string.
overrideWantSQL map[source.Type]string
// skip indicates the test should be skipped. Useful for test cases
// that we wantSQL to implement in the future.
@ -52,186 +63,214 @@ func TestSLQ2SQLNew(t *testing.T) {
wantRecs int
}{
{
name: "select/cols",
in: `@sakila | .actor | .first_name, .last_name`,
wantSQL: `SELECT "first_name", "last_name" FROM "actor"`,
override: map[source.Type]string{mysql.Type: "SELECT `first_name`, `last_name` FROM `actor`"},
wantRecs: sakila.TblActorCount,
name: "select/cols",
in: `@sakila | .actor | .first_name, .last_name`,
wantSQL: `SELECT "first_name", "last_name" FROM "actor"`,
overrideWantSQL: map[source.Type]string{mysql.Type: "SELECT `first_name`, `last_name` FROM `actor`"},
wantRecs: sakila.TblActorCount,
},
{
name: "select/cols-whitespace-single-col",
in: `@sakila | .actor | ."first name"`,
wantSQL: `SELECT "first name" FROM "actor"`,
override: map[source.Type]string{mysql.Type: "SELECT `first name` FROM `actor`"},
wantRecs: sakila.TblActorCount,
skipExec: true,
name: "select/cols-whitespace-single-col",
in: `@sakila | .actor | ."first name"`,
wantSQL: `SELECT "first name" FROM "actor"`,
overrideWantSQL: map[source.Type]string{mysql.Type: "SELECT `first name` FROM `actor`"},
wantRecs: sakila.TblActorCount,
skipExec: true,
},
{
name: "select/cols-whitespace-multiple-cols",
in: `@sakila | .actor | .actor_id, ."first name", ."last name"`,
wantSQL: `SELECT "actor_id", "first name", "last name" FROM "actor"`,
override: map[source.Type]string{mysql.Type: "SELECT `actor_id`, `first name`, `last name` FROM `actor`"},
wantRecs: sakila.TblActorCount,
skipExec: true,
name: "select/cols-whitespace-multiple-cols",
in: `@sakila | .actor | .actor_id, ."first name", ."last name"`,
wantSQL: `SELECT "actor_id", "first name", "last name" FROM "actor"`,
overrideWantSQL: map[source.Type]string{mysql.Type: "SELECT `actor_id`, `first name`, `last name` FROM `actor`"},
wantRecs: sakila.TblActorCount,
skipExec: true,
},
{
name: "select/count-whitespace-col",
in: `@sakila | .actor | count(."first name")`,
wantSQL: `SELECT COUNT("first name") FROM "actor"`,
override: map[source.Type]string{mysql.Type: "SELECT COUNT(`first name`) FROM `actor`"},
skipExec: true,
name: "select/count-whitespace-col",
in: `@sakila | .actor | count(."first name")`,
wantSQL: `SELECT count("first name") FROM "actor"`,
overrideWantSQL: map[source.Type]string{mysql.Type: "SELECT count(`first name`) FROM `actor`"},
skipExec: true,
},
{
name: "select/table-whitespace",
in: `@sakila | ."film actor"`,
wantSQL: `SELECT * FROM "film actor"`,
override: map[source.Type]string{mysql.Type: "SELECT * FROM `film actor`"},
skipExec: true,
name: "select/table-whitespace",
in: `@sakila | ."film actor"`,
wantSQL: `SELECT * FROM "film actor"`,
overrideWantSQL: map[source.Type]string{mysql.Type: "SELECT * FROM `film actor`"},
skipExec: true,
},
{
name: "select/cols-aliases",
in: `@sakila | .actor | .first_name:given_name, .last_name:family_name`,
wantSQL: `SELECT "first_name" AS "given_name", "last_name" AS "family_name" FROM "actor"`,
override: map[source.Type]string{mysql.Type: "SELECT `first_name` AS `given_name`, `last_name` AS `family_name` FROM `actor`"},
wantRecs: sakila.TblActorCount,
name: "select/cols-aliases",
in: `@sakila | .actor | .first_name:given_name, .last_name:family_name`,
wantSQL: `SELECT "first_name" AS "given_name", "last_name" AS "family_name" FROM "actor"`,
overrideWantSQL: map[source.Type]string{mysql.Type: "SELECT `first_name` AS `given_name`, `last_name` AS `family_name` FROM `actor`"},
wantRecs: sakila.TblActorCount,
},
{
name: "select/count-star",
in: `@sakila | .actor | count(*)`,
wantSQL: `SELECT COUNT(*) FROM "actor"`,
override: map[source.Type]string{mysql.Type: "SELECT COUNT(*) FROM `actor`"},
wantRecs: 1,
name: "select/count-star",
in: `@sakila | .actor | count(*)`,
wantSQL: `SELECT count(*) FROM "actor"`,
overrideWantSQL: map[source.Type]string{mysql.Type: "SELECT count(*) FROM `actor`"},
wantRecs: 1,
},
{
name: "select/count",
in: `@sakila | .actor | count()`,
wantSQL: `SELECT COUNT(*) FROM "actor"`,
override: map[source.Type]string{mysql.Type: "SELECT COUNT(*) FROM `actor`"},
wantRecs: 1,
name: "select/count",
in: `@sakila | .actor | count()`,
wantSQL: `SELECT count(*) FROM "actor"`,
overrideWantSQL: map[source.Type]string{mysql.Type: "SELECT count(*) FROM `actor`"},
wantRecs: 1,
},
{
name: "select/handle-table/cols",
in: `@sakila.actor | .first_name, .last_name`,
wantSQL: `SELECT "first_name", "last_name" FROM "actor"`,
override: map[source.Type]string{mysql.Type: "SELECT `first_name`, `last_name` FROM `actor`"},
wantRecs: sakila.TblActorCount,
name: "select/handle-table/cols",
in: `@sakila.actor | .first_name, .last_name`,
wantSQL: `SELECT "first_name", "last_name" FROM "actor"`,
overrideWantSQL: map[source.Type]string{mysql.Type: "SELECT `first_name`, `last_name` FROM `actor`"},
wantRecs: sakila.TblActorCount,
},
{
name: "select/handle-table/count-star",
in: `@sakila.actor | count(*)`,
wantSQL: `SELECT COUNT(*) FROM "actor"`,
override: map[source.Type]string{mysql.Type: "SELECT COUNT(*) FROM `actor`"},
wantRecs: 1,
name: "select/handle-table/count-star",
in: `@sakila.actor | count(*)`,
wantSQL: `SELECT count(*) FROM "actor"`,
overrideWantSQL: map[source.Type]string{mysql.Type: "SELECT count(*) FROM `actor`"},
wantRecs: 1,
},
{
name: "select/handle-table/count-col",
in: `@sakila.actor | count(."first name")`,
wantSQL: `SELECT COUNT("first name") FROM "actor"`,
override: map[source.Type]string{mysql.Type: "SELECT COUNT(`first name`) FROM `actor`"},
skipExec: true,
name: "select/handle-table/count-col",
in: `@sakila.actor | count(."first name")`,
wantSQL: `SELECT count("first name") FROM "actor"`,
overrideWantSQL: map[source.Type]string{mysql.Type: "SELECT count(`first name`) FROM `actor`"},
skipExec: true,
},
{
name: "select/count-alias",
in: `@sakila | .actor | count(*):quantity`,
wantSQL: `SELECT COUNT(*) AS "quantity" FROM "actor"`,
override: map[source.Type]string{mysql.Type: "SELECT COUNT(*) AS `quantity` FROM `actor`"},
wantRecs: 1,
name: "select/count-alias",
in: `@sakila | .actor | count(*):quantity`,
wantSQL: `SELECT count(*) AS "quantity" FROM "actor"`,
overrideWantSQL: map[source.Type]string{mysql.Type: "SELECT count(*) AS `quantity` FROM `actor`"},
wantRecs: 1,
},
{
name: "filter/equal",
in: `@sakila | .actor | .actor_id == 1`,
wantSQL: `SELECT * FROM "actor" WHERE "actor_id" = 1`,
override: map[source.Type]string{mysql.Type: "SELECT * FROM `actor` WHERE `actor_id` = 1"},
wantRecs: 1,
name: "filter/equal",
in: `@sakila | .actor | .actor_id == 1`,
wantSQL: `SELECT * FROM "actor" WHERE "actor_id" = 1`,
overrideWantSQL: map[source.Type]string{mysql.Type: "SELECT * FROM `actor` WHERE `actor_id` = 1"},
wantRecs: 1,
},
{
name: "join/single-selector",
in: `@sakila | .actor, .film_actor | join(.actor_id)`,
wantSQL: `SELECT * FROM "actor" INNER JOIN "film_actor" ON "actor"."actor_id" = "film_actor"."actor_id"`,
override: map[source.Type]string{mysql.Type: "SELECT * FROM `actor` INNER JOIN `film_actor` ON `actor`.`actor_id` = `film_actor`.`actor_id`"},
wantRecs: sakila.TblFilmActorCount,
name: "join/single-selector",
in: `@sakila | .actor, .film_actor | join(.actor_id)`,
wantSQL: `SELECT * FROM "actor" INNER JOIN "film_actor" ON "actor"."actor_id" = "film_actor"."actor_id"`,
overrideWantSQL: map[source.Type]string{mysql.Type: "SELECT * FROM `actor` INNER JOIN `film_actor` ON `actor`.`actor_id` = `film_actor`.`actor_id`"},
wantRecs: sakila.TblFilmActorCount,
},
{
name: "join/fq-table-cols-equal",
in: `@sakila | .actor, .film_actor | join(.film_actor.actor_id == .actor.actor_id)`,
wantSQL: `SELECT * FROM "actor" INNER JOIN "film_actor" ON "film_actor"."actor_id" = "actor"."actor_id"`,
override: map[source.Type]string{mysql.Type: "SELECT * FROM `actor` INNER JOIN `film_actor` ON `film_actor`.`actor_id` = `actor`.`actor_id`"},
wantRecs: sakila.TblFilmActorCount,
name: "join/fq-table-cols-equal",
in: `@sakila | .actor, .film_actor | join(.film_actor.actor_id == .actor.actor_id)`,
wantSQL: `SELECT * FROM "actor" INNER JOIN "film_actor" ON "film_actor"."actor_id" = "actor"."actor_id"`,
overrideWantSQL: map[source.Type]string{mysql.Type: "SELECT * FROM `actor` INNER JOIN `film_actor` ON `film_actor`.`actor_id` = `actor`.`actor_id`"},
wantRecs: sakila.TblFilmActorCount,
},
{
name: "join/fq-table-cols-equal-whitespace",
in: `@sakila | .actor, ."film actor" | join(."film actor".actor_id == .actor.actor_id)`,
wantSQL: `SELECT * FROM "actor" INNER JOIN "film actor" ON "film actor"."actor_id" = "actor"."actor_id"`,
override: map[source.Type]string{mysql.Type: "SELECT * FROM `actor` INNER JOIN `film actor` ON `film actor`.`actor_id` = `actor`.`actor_id`"},
skipExec: true,
name: "join/fq-table-cols-equal-whitespace",
in: `@sakila | .actor, ."film actor" | join(."film actor".actor_id == .actor.actor_id)`,
wantSQL: `SELECT * FROM "actor" INNER JOIN "film actor" ON "film actor"."actor_id" = "actor"."actor_id"`,
overrideWantSQL: map[source.Type]string{mysql.Type: "SELECT * FROM `actor` INNER JOIN `film actor` ON `film actor`.`actor_id` = `actor`.`actor_id`"},
skipExec: true,
},
{
name: "orderby/single-element",
in: `@sakila | .actor | orderby(.first_name)`,
wantSQL: `SELECT * FROM "actor" ORDER BY "first_name"`,
override: map[source.Type]string{mysql.Type: "SELECT * FROM `actor` ORDER BY `first_name`"},
wantRecs: sakila.TblActorCount,
name: "order_by/single-element",
in: `@sakila | .actor | order_by(.first_name)`,
wantSQL: `SELECT * FROM "actor" ORDER BY "first_name"`,
overrideWantSQL: map[source.Type]string{mysql.Type: "SELECT * FROM `actor` ORDER BY `first_name`"},
wantRecs: sakila.TblActorCount,
},
{
name: "orderby/single-element-table-selector",
in: `@sakila | .actor | orderby(.actor.first_name)`,
wantSQL: `SELECT * FROM "actor" ORDER BY "actor"."first_name"`,
override: map[source.Type]string{mysql.Type: "SELECT * FROM `actor` ORDER BY `actor`.`first_name`"},
wantRecs: sakila.TblActorCount,
name: "order_by/single-element-table-selector",
in: `@sakila | .actor | order_by(.actor.first_name)`,
wantSQL: `SELECT * FROM "actor" ORDER BY "actor"."first_name"`,
overrideWantSQL: map[source.Type]string{mysql.Type: "SELECT * FROM `actor` ORDER BY `actor`.`first_name`"},
wantRecs: sakila.TblActorCount,
},
{
name: "orderby/single-element-asc",
in: `@sakila | .actor | orderby(.first_name+)`,
wantSQL: `SELECT * FROM "actor" ORDER BY "first_name" ASC`,
override: map[source.Type]string{mysql.Type: "SELECT * FROM `actor` ORDER BY `first_name` ASC"},
wantRecs: sakila.TblActorCount,
name: "order_by/single-element-asc",
in: `@sakila | .actor | order_by(.first_name+)`,
wantSQL: `SELECT * FROM "actor" ORDER BY "first_name" ASC`,
overrideWantSQL: map[source.Type]string{mysql.Type: "SELECT * FROM `actor` ORDER BY `first_name` ASC"},
wantRecs: sakila.TblActorCount,
},
{
name: "orderby/single-element-desc",
in: `@sakila | .actor | orderby(.first_name-)`,
wantSQL: `SELECT * FROM "actor" ORDER BY "first_name" DESC`,
override: map[source.Type]string{mysql.Type: "SELECT * FROM `actor` ORDER BY `first_name` DESC"},
wantRecs: sakila.TblActorCount,
name: "order_by/single-element-desc",
in: `@sakila | .actor | order_by(.first_name-)`,
wantSQL: `SELECT * FROM "actor" ORDER BY "first_name" DESC`,
overrideWantSQL: map[source.Type]string{mysql.Type: "SELECT * FROM `actor` ORDER BY `first_name` DESC"},
wantRecs: sakila.TblActorCount,
},
{
name: "orderby/multiple-elements",
in: `@sakila | .actor | orderby(.first_name+, .last_name-)`,
wantSQL: `SELECT * FROM "actor" ORDER BY "first_name" ASC, "last_name" DESC`,
override: map[source.Type]string{mysql.Type: "SELECT * FROM `actor` ORDER BY `first_name` ASC, `last_name` DESC"},
wantRecs: sakila.TblActorCount,
name: "order_by/multiple-elements",
in: `@sakila | .actor | order_by(.first_name+, .last_name-)`,
wantSQL: `SELECT * FROM "actor" ORDER BY "first_name" ASC, "last_name" DESC`,
overrideWantSQL: map[source.Type]string{mysql.Type: "SELECT * FROM `actor` ORDER BY `first_name` ASC, `last_name` DESC"},
wantRecs: sakila.TblActorCount,
},
{
name: "orderby/synonym-sort-by",
in: `@sakila | .actor | sort_by(.first_name)`,
wantSQL: `SELECT * FROM "actor" ORDER BY "first_name"`,
override: map[source.Type]string{mysql.Type: "SELECT * FROM `actor` ORDER BY `first_name`"},
wantRecs: sakila.TblActorCount,
name: "order_by/synonym-sort-by",
in: `@sakila | .actor | sort_by(.first_name)`,
wantSQL: `SELECT * FROM "actor" ORDER BY "first_name"`,
overrideWantSQL: map[source.Type]string{mysql.Type: "SELECT * FROM `actor` ORDER BY `first_name`"},
wantRecs: sakila.TblActorCount,
},
{
name: "orderby/error-no-selector",
in: `@sakila | .actor | orderby()`,
name: "order_by/error-no-selector",
in: `@sakila | .actor | order_by()`,
wantErr: true,
},
{
name: "groupby/single-term",
in: `@sakila | .payment | .customer_id, sum(.amount) | groupby(.customer_id)`,
wantSQL: `SELECT "customer_id", SUM("amount") FROM "payment" GROUP BY "customer_id"`,
override: map[source.Type]string{mysql.Type: "SELECT `customer_id`, SUM(`amount`) FROM `payment` GROUP BY `customer_id`"},
wantRecs: 599,
name: "group_by/single-term",
in: `@sakila | .payment | .customer_id, sum(.amount) | group_by(.customer_id)`,
wantSQL: `SELECT "customer_id", sum("amount") FROM "payment" GROUP BY "customer_id"`,
overrideWantSQL: map[source.Type]string{mysql.Type: "SELECT `customer_id`, sum(`amount`) FROM `payment` GROUP BY `customer_id`"},
wantRecs: 599,
},
{
name: "groupby/synonym-group_by",
in: `@sakila | .payment | .customer_id, sum(.amount) | group_by(.customer_id)`,
wantSQL: `SELECT "customer_id", SUM("amount") FROM "payment" GROUP BY "customer_id"`,
override: map[source.Type]string{mysql.Type: "SELECT `customer_id`, SUM(`amount`) FROM `payment` GROUP BY `customer_id`"},
wantRecs: 599,
name: "group_by/multiple_terms",
in: `@sakila | .payment | .customer_id, .staff_id, sum(.amount) | group_by(.customer_id, .staff_id)`,
wantSQL: `SELECT "customer_id", "staff_id", sum("amount") FROM "payment" GROUP BY "customer_id", "staff_id"`,
overrideWantSQL: map[source.Type]string{mysql.Type: "SELECT `customer_id`, `staff_id`, sum(`amount`) FROM `payment` GROUP BY `customer_id`, `staff_id`"},
wantRecs: 1198,
},
{
name: "groupby/multiple_terms",
in: `@sakila | .payment | .customer_id, .staff_id, sum(.amount) | groupby(.customer_id, .staff_id)`,
wantSQL: `SELECT "customer_id", "staff_id", SUM("amount") FROM "payment" GROUP BY "customer_id", "staff_id"`,
override: map[source.Type]string{mysql.Type: "SELECT `customer_id`, `staff_id`, SUM(`amount`) FROM `payment` GROUP BY `customer_id`, `staff_id`"},
wantRecs: 1198,
name: "group_by/with_func/sqlite",
in: `@sakila | .payment | date("month", .payment_date):month, count(.payment_id):count | group_by(date("month", .payment_date))`,
wantSQL: `SELECT date('month', "payment_date") AS "month", count("payment_id") AS "count" FROM "payment" GROUP BY date('month', "payment_date")`,
onlyFor: []source.Type{sqlite3.Type},
wantRecs: 1,
},
{
name: "datetime/strftime/sqlite",
in: `@sakila | .payment | strftime("%m", .payment_date)`,
wantSQL: `SELECT strftime('%m', "payment_date") FROM "payment"`,
onlyFor: []source.Type{sqlite3.Type},
wantRecs: sakila.TblPaymentCount,
},
{
name: "datetime/date_trunc/postgres",
in: `@sakila | .payment | date_trunc("month", .payment_date)`,
wantSQL: `SELECT date_trunc('month', "payment_date") FROM "payment"`,
onlyFor: []source.Type{postgres.Type},
wantRecs: sakila.TblPaymentCount,
},
{
name: "datetime/month/sqlserver",
in: `@sakila | .payment | month(.payment_date)`,
wantSQL: `SELECT month("payment_date") FROM "payment"`,
onlyFor: []source.Type{sqlserver.Type},
wantRecs: sakila.TblPaymentCount,
},
{
name: "datetime/date_format/mysql",
in: `@sakila | .payment | date_format(.payment_date, "%m")`,
wantSQL: "SELECT date_format(`payment_date`, '%m') FROM `payment`",
onlyFor: []source.Type{mysql.Type},
wantRecs: sakila.TblPaymentCount,
},
}
@ -243,15 +282,20 @@ func TestSLQ2SQLNew(t *testing.T) {
t.Skip()
}
srcs := testh.New(t).NewSourceSet(sakila.SQLLatest()...)
for _, src := range srcs.Items() {
src := src
t.Run(string(src.Type), func(t *testing.T) {
if len(tc.onlyFor) > 0 {
if !slices.Contains(tc.onlyFor, src.Type) {
t.Skip()
}
}
in := strings.Replace(tc.in, "@sakila", src.Handle, 1)
t.Logf(in)
want := tc.wantSQL
if overrideWant, ok := tc.override[src.Type]; ok {
if overrideWant, ok := tc.overrideWantSQL[src.Type]; ok {
want = overrideWant
}
@ -275,15 +319,8 @@ func TestSLQ2SQLNew(t *testing.T) {
return
}
sink := &testh.RecordSink{}
recw := output.NewRecordWriterAdapter(sink)
gotErr = libsq.ExecuteSLQ(th.Context, th.Log, dbases, dbases, srcs, in, recw)
require.NoError(t, gotErr)
written, err := recw.Wait()
sink, err := th.QuerySLQ(in)
require.NoError(t, err)
require.Equal(t, tc.wantRecs, int(written))
require.Equal(t, tc.wantRecs, len(sink.Recs))
})
}

View File

@ -13,32 +13,32 @@ element:
| handle
| selectorElement
| join
| group
| groupBy
| orderBy
| rowRange
| fnElement
| funcElement
| expr;
// cmpr is a comparison operator.
cmpr: LT_EQ | LT | GT_EQ | GT | EQ | NEQ;
fn: fnName '(' ( expr ( ',' expr)* | '*')? ')';
funcElement: func (alias)?;
func: funcName '(' ( expr ( ',' expr)* | '*')? ')';
funcName: ID;
fnElement: fn (alias)?;
join: ('join' | 'JOIN' | 'j') '(' joinConstraint ')';
join: ('join') '(' joinConstraint ')';
joinConstraint:
selector cmpr selector // .user.uid == .address.userid
| selector ; // .uid
/*
groupby
group_by
-------
The 'groupby' construct implments the SQL "GROUP BY" clause.
The 'group_by' construct implments the SQL "GROUP BY" clause.
.payment | .customer_id, sum(.amount) | groupby(.customer_id)
.payment | .customer_id, sum(.amount) | group_by(.customer_id)
Syonyms:
- 'group_by' for jq interoperability.
@ -46,18 +46,19 @@ Syonyms:
- 'group': for legacy sq compabibility. Should this be deprecated and removed?
*/
GROUP_BY: 'groupby' | 'group_by';
group: GROUP_BY '(' selector (',' selector)* ')';
GROUP_BY: 'group_by';
groupByTerm: selector | func;
groupBy: GROUP_BY '(' groupByTerm (',' groupByTerm)* ')';
/*
orderby
order_by
------
The 'orderby' construct implements the SQL "ORDER BY" clause.
The 'order_by' construct implements the SQL "ORDER BY" clause.
.actor | orderby(.first_name, .last_name)
.actor | orderby(.first_name+)
.actor | orderby(.actor.first_name-)
.actor | order_by(.first_name, .last_name)
.actor | order_by(.first_name+)
.actor | order_by(.actor.first_name-)
The optional plus/minus tokens specify ASC or DESC order.
@ -73,7 +74,7 @@ as a no-op.
ORDER_ASC: '+';
ORDER_DESC: '-';
ORDER_BY: 'orderby' | 'sort_by';
ORDER_BY: 'order_by' | 'sort_by';
orderByTerm: selector (ORDER_ASC | ORDER_DESC)?;
orderBy: ORDER_BY '(' orderByTerm (',' orderByTerm)* ')';
@ -100,6 +101,8 @@ selectorElement: selector (alias)?;
alias: ':' ID;
//FUNC_NAME: [a-z_] [a-z_0-9]*;
@ -126,15 +129,17 @@ rowRange:
| NN // [10]
)? ']';
fnName:
'sum'
| 'SUM'
| 'avg'
| 'AVG'
| 'count'
| 'COUNT'
| 'where'
| 'WHERE';
//fnName:
// 'sum'
// | 'SUM'
// | 'avg'
// | 'AVG'
// | 'count'
// | 'COUNT'
// | 'where'
// | 'WHERE';
expr:
selector
@ -147,7 +152,7 @@ expr:
| expr ( '<' | '<=' | '>' | '>=') expr
| expr ( '==' | '!=' |) expr
| expr '&&' expr
| fn
| func
;
literal: NN | NUMBER | STRING | NULL;
@ -233,7 +238,7 @@ fragment X: [xX];
fragment Y: [yY];
fragment Z: [zZ];
LINECOMMENT: '//' .*? '\n' -> skip;
LINECOMMENT: '#' .*? '\n' -> skip;
//// From https://github.com/antlr/grammars-v4/blob/master/sql/sqlite/SQLiteLexer.g4
//IDENTIFIER:

61
libsq/ast/groupby.go Normal file
View File

@ -0,0 +1,61 @@
package ast
import (
"reflect"
"github.com/neilotoole/sq/libsq/ast/internal/slq"
)
var groupByAllowedChildren = []reflect.Type{typeSelectorNode, typeColSelectorNode, typeTblColSelectorNode, typeFuncNode}
// GroupByNode models GROUP BY. The children of GroupBy node can be
// of type selector or FuncNode.
type GroupByNode struct {
baseNode
}
// AddChild implements Node.
func (n *GroupByNode) AddChild(child Node) error {
if err := nodesAreOnlyOfType([]Node{child}, groupByAllowedChildren...); err != nil {
return err
}
n.addChild(child)
return child.SetParent(n)
}
// SetChildren implements ast.Node.
func (n *GroupByNode) SetChildren(children []Node) error {
if err := nodesAreOnlyOfType(children, groupByAllowedChildren...); err != nil {
return err
}
n.setChildren(children)
return nil
}
// String returns a log/debug-friendly representation.
func (n *GroupByNode) String() string {
text := nodeString(n)
return text
}
// VisitGroupBy implements slq.SLQVisitor.
func (v *parseTreeVisitor) VisitGroupBy(ctx *slq.GroupByContext) any {
node := &GroupByNode{}
node.ctx = ctx
node.text = ctx.GetText()
if err := v.cur.AddChild(node); err != nil {
return err
}
return v.using(node, func() any {
// This will result in VisitOrderByTerm being called on the children.
return v.VisitChildren(ctx)
})
}
// VisitGroupByTerm implements slq.SLQVisitor.
func (v *parseTreeVisitor) VisitGroupByTerm(ctx *slq.GroupByTermContext) interface{} {
return v.VisitChildren(ctx)
}

View File

@ -3,6 +3,8 @@ package ast
import (
"reflect"
"github.com/samber/lo"
"github.com/ryboe/q"
"github.com/neilotoole/lg"
@ -46,10 +48,37 @@ func (in *Inspector) FindNodes(typ reflect.Type) []Node {
return nil
})
_ = w.Walk()
if err := w.Walk(); err != nil {
// Should never happen
panic(err)
}
return nodes
}
// FindHandles returns all handles mentioned in the AST.
func (in *Inspector) FindHandles() []string {
var handles []string
if err := walkWith(in.log, in.ast, typeHandleNode, func(log lg.Log, walker *Walker, node Node) error {
handles = append(handles, node.Text())
return nil
}); err != nil {
panic(err)
}
if err := walkWith(in.log, in.ast, typeTblSelectorNode, func(log lg.Log, walker *Walker, node Node) error {
n, _ := node.(*TblSelectorNode)
if n.handle != "" {
handles = append(handles, n.handle)
}
return nil
}); err != nil {
panic(err)
}
return lo.Uniq(handles)
}
// FindWhereClauses returns all the WHERE clauses in the AST.
func (in *Inspector) FindWhereClauses() ([]*WhereNode, error) {
var nodes []*WhereNode
@ -145,14 +174,14 @@ func (in *Inspector) FindGroupByNode() (*GroupByNode, error) {
}
// FindSelectableSegments returns the segments that have at least one child
// that implements Selectable.
// that implements Tabler.
func (in *Inspector) FindSelectableSegments() []*SegmentNode {
segs := in.ast.Segments()
selSegs := make([]*SegmentNode, 0, 2)
for _, seg := range segs {
for _, child := range seg.Children() {
if _, ok := child.(Selectable); ok {
if _, ok := child.(Tabler); ok {
selSegs = append(selSegs, seg)
break
}
@ -163,7 +192,7 @@ func (in *Inspector) FindSelectableSegments() []*SegmentNode {
}
// FindFinalSelectableSegment returns the final segment that
// has at lest one child that implements Selectable.
// has at lest one child that implements Tabler.
func (in *Inspector) FindFinalSelectableSegment() (*SegmentNode, error) {
selectableSegs := in.FindSelectableSegments()
if len(selectableSegs) == 0 {

File diff suppressed because one or more lines are too long

View File

@ -11,77 +11,58 @@ T__9=10
T__10=11
T__11=12
T__12=13
T__13=14
T__14=15
T__15=16
T__16=17
T__17=18
T__18=19
T__19=20
T__20=21
T__21=22
T__22=23
GROUP_BY=24
ORDER_ASC=25
ORDER_DESC=26
ORDER_BY=27
ID=28
WS=29
LPAR=30
RPAR=31
LBRA=32
RBRA=33
COMMA=34
PIPE=35
COLON=36
NULL=37
NN=38
NUMBER=39
LT_EQ=40
LT=41
GT_EQ=42
GT=43
NEQ=44
EQ=45
NAME=46
HANDLE=47
STRING=48
LINECOMMENT=49
GROUP_BY=14
ORDER_ASC=15
ORDER_DESC=16
ORDER_BY=17
ID=18
WS=19
LPAR=20
RPAR=21
LBRA=22
RBRA=23
COMMA=24
PIPE=25
COLON=26
NULL=27
NN=28
NUMBER=29
LT_EQ=30
LT=31
GT_EQ=32
GT=33
NEQ=34
EQ=35
NAME=36
HANDLE=37
STRING=38
LINECOMMENT=39
';'=1
'*'=2
'join'=3
'JOIN'=4
'j'=5
'.['=6
'sum'=7
'SUM'=8
'avg'=9
'AVG'=10
'count'=11
'COUNT'=12
'where'=13
'WHERE'=14
'||'=15
'/'=16
'%'=17
'<<'=18
'>>'=19
'&'=20
'&&'=21
'~'=22
'!'=23
'+'=25
'-'=26
'('=30
')'=31
'['=32
']'=33
','=34
'|'=35
':'=36
'<='=40
'<'=41
'>='=42
'>'=43
'!='=44
'=='=45
'.['=4
'||'=5
'/'=6
'%'=7
'<<'=8
'>>'=9
'&'=10
'&&'=11
'~'=12
'!'=13
'group_by'=14
'+'=15
'-'=16
'('=20
')'=21
'['=22
']'=23
','=24
'|'=25
':'=26
'<='=30
'<'=31
'>='=32
'>'=33
'!='=34
'=='=35

File diff suppressed because one or more lines are too long

View File

@ -11,77 +11,58 @@ T__9=10
T__10=11
T__11=12
T__12=13
T__13=14
T__14=15
T__15=16
T__16=17
T__17=18
T__18=19
T__19=20
T__20=21
T__21=22
T__22=23
GROUP_BY=24
ORDER_ASC=25
ORDER_DESC=26
ORDER_BY=27
ID=28
WS=29
LPAR=30
RPAR=31
LBRA=32
RBRA=33
COMMA=34
PIPE=35
COLON=36
NULL=37
NN=38
NUMBER=39
LT_EQ=40
LT=41
GT_EQ=42
GT=43
NEQ=44
EQ=45
NAME=46
HANDLE=47
STRING=48
LINECOMMENT=49
GROUP_BY=14
ORDER_ASC=15
ORDER_DESC=16
ORDER_BY=17
ID=18
WS=19
LPAR=20
RPAR=21
LBRA=22
RBRA=23
COMMA=24
PIPE=25
COLON=26
NULL=27
NN=28
NUMBER=29
LT_EQ=30
LT=31
GT_EQ=32
GT=33
NEQ=34
EQ=35
NAME=36
HANDLE=37
STRING=38
LINECOMMENT=39
';'=1
'*'=2
'join'=3
'JOIN'=4
'j'=5
'.['=6
'sum'=7
'SUM'=8
'avg'=9
'AVG'=10
'count'=11
'COUNT'=12
'where'=13
'WHERE'=14
'||'=15
'/'=16
'%'=17
'<<'=18
'>>'=19
'&'=20
'&&'=21
'~'=22
'!'=23
'+'=25
'-'=26
'('=30
')'=31
'['=32
']'=33
','=34
'|'=35
':'=36
'<='=40
'<'=41
'>='=42
'>'=43
'!='=44
'=='=45
'.['=4
'||'=5
'/'=6
'%'=7
'<<'=8
'>>'=9
'&'=10
'&&'=11
'~'=12
'!'=13
'group_by'=14
'+'=15
'-'=16
'('=20
')'=21
'['=22
']'=23
','=24
'|'=25
':'=26
'<='=30
'<'=31
'>='=32
'>'=33
'!='=34
'=='=35

View File

@ -50,17 +50,23 @@ func (s *BaseSLQListener) EnterCmpr(ctx *CmprContext) {}
// ExitCmpr is called when production cmpr is exited.
func (s *BaseSLQListener) ExitCmpr(ctx *CmprContext) {}
// EnterFn is called when production fn is entered.
func (s *BaseSLQListener) EnterFn(ctx *FnContext) {}
// EnterFuncElement is called when production funcElement is entered.
func (s *BaseSLQListener) EnterFuncElement(ctx *FuncElementContext) {}
// ExitFn is called when production fn is exited.
func (s *BaseSLQListener) ExitFn(ctx *FnContext) {}
// ExitFuncElement is called when production funcElement is exited.
func (s *BaseSLQListener) ExitFuncElement(ctx *FuncElementContext) {}
// EnterFnElement is called when production fnElement is entered.
func (s *BaseSLQListener) EnterFnElement(ctx *FnElementContext) {}
// EnterFunc is called when production func is entered.
func (s *BaseSLQListener) EnterFunc(ctx *FuncContext) {}
// ExitFnElement is called when production fnElement is exited.
func (s *BaseSLQListener) ExitFnElement(ctx *FnElementContext) {}
// ExitFunc is called when production func is exited.
func (s *BaseSLQListener) ExitFunc(ctx *FuncContext) {}
// EnterFuncName is called when production funcName is entered.
func (s *BaseSLQListener) EnterFuncName(ctx *FuncNameContext) {}
// ExitFuncName is called when production funcName is exited.
func (s *BaseSLQListener) ExitFuncName(ctx *FuncNameContext) {}
// EnterJoin is called when production join is entered.
func (s *BaseSLQListener) EnterJoin(ctx *JoinContext) {}
@ -74,11 +80,17 @@ func (s *BaseSLQListener) EnterJoinConstraint(ctx *JoinConstraintContext) {}
// ExitJoinConstraint is called when production joinConstraint is exited.
func (s *BaseSLQListener) ExitJoinConstraint(ctx *JoinConstraintContext) {}
// EnterGroup is called when production group is entered.
func (s *BaseSLQListener) EnterGroup(ctx *GroupContext) {}
// EnterGroupByTerm is called when production groupByTerm is entered.
func (s *BaseSLQListener) EnterGroupByTerm(ctx *GroupByTermContext) {}
// ExitGroup is called when production group is exited.
func (s *BaseSLQListener) ExitGroup(ctx *GroupContext) {}
// ExitGroupByTerm is called when production groupByTerm is exited.
func (s *BaseSLQListener) ExitGroupByTerm(ctx *GroupByTermContext) {}
// EnterGroupBy is called when production groupBy is entered.
func (s *BaseSLQListener) EnterGroupBy(ctx *GroupByContext) {}
// ExitGroupBy is called when production groupBy is exited.
func (s *BaseSLQListener) ExitGroupBy(ctx *GroupByContext) {}
// EnterOrderByTerm is called when production orderByTerm is entered.
func (s *BaseSLQListener) EnterOrderByTerm(ctx *OrderByTermContext) {}
@ -128,12 +140,6 @@ func (s *BaseSLQListener) EnterRowRange(ctx *RowRangeContext) {}
// ExitRowRange is called when production rowRange is exited.
func (s *BaseSLQListener) ExitRowRange(ctx *RowRangeContext) {}
// EnterFnName is called when production fnName is entered.
func (s *BaseSLQListener) EnterFnName(ctx *FnNameContext) {}
// ExitFnName is called when production fnName is exited.
func (s *BaseSLQListener) ExitFnName(ctx *FnNameContext) {}
// EnterExpr is called when production expr is entered.
func (s *BaseSLQListener) EnterExpr(ctx *ExprContext) {}

View File

@ -27,11 +27,15 @@ func (v *BaseSLQVisitor) VisitCmpr(ctx *CmprContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseSLQVisitor) VisitFn(ctx *FnContext) interface{} {
func (v *BaseSLQVisitor) VisitFuncElement(ctx *FuncElementContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseSLQVisitor) VisitFnElement(ctx *FnElementContext) interface{} {
func (v *BaseSLQVisitor) VisitFunc(ctx *FuncContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseSLQVisitor) VisitFuncName(ctx *FuncNameContext) interface{} {
return v.VisitChildren(ctx)
}
@ -43,7 +47,11 @@ func (v *BaseSLQVisitor) VisitJoinConstraint(ctx *JoinConstraintContext) interfa
return v.VisitChildren(ctx)
}
func (v *BaseSLQVisitor) VisitGroup(ctx *GroupContext) interface{} {
func (v *BaseSLQVisitor) VisitGroupByTerm(ctx *GroupByTermContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseSLQVisitor) VisitGroupBy(ctx *GroupByContext) interface{} {
return v.VisitChildren(ctx)
}
@ -79,10 +87,6 @@ func (v *BaseSLQVisitor) VisitRowRange(ctx *RowRangeContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseSLQVisitor) VisitFnName(ctx *FnNameContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseSLQVisitor) VisitExpr(ctx *ExprContext) interface{} {
return v.VisitChildren(ctx)
}

View File

@ -44,33 +44,30 @@ func slqlexerLexerInit() {
"DEFAULT_MODE",
}
staticData.literalNames = []string{
"", "';'", "'*'", "'join'", "'JOIN'", "'j'", "'.['", "'sum'", "'SUM'",
"'avg'", "'AVG'", "'count'", "'COUNT'", "'where'", "'WHERE'", "'||'",
"'/'", "'%'", "'<<'", "'>>'", "'&'", "'&&'", "'~'", "'!'", "", "'+'",
"'-'", "", "", "", "'('", "')'", "'['", "']'", "','", "'|'", "':'",
"", "", "", "'<='", "'<'", "'>='", "'>'", "'!='", "'=='",
"", "';'", "'*'", "'join'", "'.['", "'||'", "'/'", "'%'", "'<<'", "'>>'",
"'&'", "'&&'", "'~'", "'!'", "'group_by'", "'+'", "'-'", "", "", "",
"'('", "')'", "'['", "']'", "','", "'|'", "':'", "", "", "", "'<='",
"'<'", "'>='", "'>'", "'!='", "'=='",
}
staticData.symbolicNames = []string{
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "GROUP_BY", "ORDER_ASC", "ORDER_DESC", "ORDER_BY",
"ID", "WS", "LPAR", "RPAR", "LBRA", "RBRA", "COMMA", "PIPE", "COLON",
"NULL", "NN", "NUMBER", "LT_EQ", "LT", "GT_EQ", "GT", "NEQ", "EQ", "NAME",
"HANDLE", "STRING", "LINECOMMENT",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "GROUP_BY",
"ORDER_ASC", "ORDER_DESC", "ORDER_BY", "ID", "WS", "LPAR", "RPAR", "LBRA",
"RBRA", "COMMA", "PIPE", "COLON", "NULL", "NN", "NUMBER", "LT_EQ", "LT",
"GT_EQ", "GT", "NEQ", "EQ", "NAME", "HANDLE", "STRING", "LINECOMMENT",
}
staticData.ruleNames = []string{
"T__0", "T__1", "T__2", "T__3", "T__4", "T__5", "T__6", "T__7", "T__8",
"T__9", "T__10", "T__11", "T__12", "T__13", "T__14", "T__15", "T__16",
"T__17", "T__18", "T__19", "T__20", "T__21", "T__22", "GROUP_BY", "ORDER_ASC",
"ORDER_DESC", "ORDER_BY", "ID", "WS", "LPAR", "RPAR", "LBRA", "RBRA",
"COMMA", "PIPE", "COLON", "NULL", "NN", "NUMBER", "INTF", "EXP", "LT_EQ",
"LT", "GT_EQ", "GT", "NEQ", "EQ", "NAME", "HANDLE", "STRING", "ESC",
"UNICODE", "HEX", "DIGIT", "A", "B", "C", "D", "E", "F", "G", "H", "I",
"J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W",
"X", "Y", "Z", "LINECOMMENT",
"T__9", "T__10", "T__11", "T__12", "GROUP_BY", "ORDER_ASC", "ORDER_DESC",
"ORDER_BY", "ID", "WS", "LPAR", "RPAR", "LBRA", "RBRA", "COMMA", "PIPE",
"COLON", "NULL", "NN", "NUMBER", "INTF", "EXP", "LT_EQ", "LT", "GT_EQ",
"GT", "NEQ", "EQ", "NAME", "HANDLE", "STRING", "ESC", "UNICODE", "HEX",
"DIGIT", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L",
"M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
"LINECOMMENT",
}
staticData.predictionContextCache = antlr.NewPredictionContextCache()
staticData.serializedATN = []int32{
4, 0, 49, 477, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2,
4, 0, 39, 401, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2,
4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2,
10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15,
7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7,
@ -83,205 +80,172 @@ func slqlexerLexerInit() {
52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57,
7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7,
62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 2, 66, 7, 66, 2, 67, 7, 67,
2, 68, 7, 68, 2, 69, 7, 69, 2, 70, 7, 70, 2, 71, 7, 71, 2, 72, 7, 72, 2,
73, 7, 73, 2, 74, 7, 74, 2, 75, 7, 75, 2, 76, 7, 76, 2, 77, 7, 77, 2, 78,
7, 78, 2, 79, 7, 79, 2, 80, 7, 80, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 1, 2,
1, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 1, 5,
1, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8,
1, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10,
1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1,
12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14,
1, 15, 1, 15, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 18, 1, 18, 1, 18, 1,
19, 1, 19, 1, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 22, 1, 22, 1, 23, 1, 23,
1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1,
23, 1, 23, 1, 23, 3, 23, 260, 8, 23, 1, 24, 1, 24, 1, 25, 1, 25, 1, 26,
1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1,
26, 1, 26, 1, 26, 3, 26, 280, 8, 26, 1, 27, 1, 27, 5, 27, 284, 8, 27, 10,
27, 12, 27, 287, 9, 27, 1, 28, 4, 28, 290, 8, 28, 11, 28, 12, 28, 291,
1, 28, 1, 28, 1, 29, 1, 29, 1, 30, 1, 30, 1, 31, 1, 31, 1, 32, 1, 32, 1,
33, 1, 33, 1, 34, 1, 34, 1, 35, 1, 35, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36,
1, 36, 1, 36, 1, 36, 3, 36, 318, 8, 36, 1, 37, 1, 37, 1, 38, 1, 38, 3,
38, 324, 8, 38, 1, 38, 1, 38, 1, 38, 4, 38, 329, 8, 38, 11, 38, 12, 38,
330, 1, 38, 3, 38, 334, 8, 38, 1, 38, 3, 38, 337, 8, 38, 1, 38, 1, 38,
1, 38, 1, 38, 3, 38, 343, 8, 38, 1, 38, 3, 38, 346, 8, 38, 1, 39, 1, 39,
1, 39, 5, 39, 351, 8, 39, 10, 39, 12, 39, 354, 9, 39, 3, 39, 356, 8, 39,
1, 40, 1, 40, 3, 40, 360, 8, 40, 1, 40, 1, 40, 1, 41, 1, 41, 1, 41, 1,
42, 1, 42, 1, 43, 1, 43, 1, 43, 1, 44, 1, 44, 1, 45, 1, 45, 1, 45, 1, 46,
1, 46, 1, 46, 1, 47, 1, 47, 1, 47, 3, 47, 383, 8, 47, 1, 48, 1, 48, 1,
48, 1, 49, 1, 49, 1, 49, 5, 49, 391, 8, 49, 10, 49, 12, 49, 394, 9, 49,
1, 49, 1, 49, 1, 50, 1, 50, 1, 50, 3, 50, 401, 8, 50, 1, 51, 1, 51, 1,
51, 1, 51, 1, 51, 1, 51, 1, 52, 1, 52, 1, 53, 1, 53, 1, 54, 1, 54, 1, 55,
1, 55, 1, 56, 1, 56, 1, 57, 1, 57, 1, 58, 1, 58, 1, 59, 1, 59, 1, 60, 1,
60, 1, 61, 1, 61, 1, 62, 1, 62, 1, 63, 1, 63, 1, 64, 1, 64, 1, 65, 1, 65,
1, 66, 1, 66, 1, 67, 1, 67, 1, 68, 1, 68, 1, 69, 1, 69, 1, 70, 1, 70, 1,
71, 1, 71, 1, 72, 1, 72, 1, 73, 1, 73, 1, 74, 1, 74, 1, 75, 1, 75, 1, 76,
1, 76, 1, 77, 1, 77, 1, 78, 1, 78, 1, 79, 1, 79, 1, 80, 1, 80, 1, 80, 1,
80, 5, 80, 469, 8, 80, 10, 80, 12, 80, 472, 9, 80, 1, 80, 1, 80, 1, 80,
1, 80, 1, 470, 0, 81, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8,
17, 9, 19, 10, 21, 11, 23, 12, 25, 13, 27, 14, 29, 15, 31, 16, 33, 17,
35, 18, 37, 19, 39, 20, 41, 21, 43, 22, 45, 23, 47, 24, 49, 25, 51, 26,
53, 27, 55, 28, 57, 29, 59, 30, 61, 31, 63, 32, 65, 33, 67, 34, 69, 35,
71, 36, 73, 37, 75, 38, 77, 39, 79, 0, 81, 0, 83, 40, 85, 41, 87, 42, 89,
43, 91, 44, 93, 45, 95, 46, 97, 47, 99, 48, 101, 0, 103, 0, 105, 0, 107,
0, 109, 0, 111, 0, 113, 0, 115, 0, 117, 0, 119, 0, 121, 0, 123, 0, 125,
0, 127, 0, 129, 0, 131, 0, 133, 0, 135, 0, 137, 0, 139, 0, 141, 0, 143,
0, 145, 0, 147, 0, 149, 0, 151, 0, 153, 0, 155, 0, 157, 0, 159, 0, 161,
49, 1, 0, 35, 3, 0, 65, 90, 95, 95, 97, 122, 4, 0, 48, 57, 65, 90, 95,
95, 97, 122, 3, 0, 9, 10, 13, 13, 32, 32, 1, 0, 48, 57, 1, 0, 49, 57, 2,
0, 69, 69, 101, 101, 2, 0, 43, 43, 45, 45, 2, 0, 34, 34, 92, 92, 8, 0,
34, 34, 47, 47, 92, 92, 98, 98, 102, 102, 110, 110, 114, 114, 116, 116,
3, 0, 48, 57, 65, 70, 97, 102, 2, 0, 65, 65, 97, 97, 2, 0, 66, 66, 98,
98, 2, 0, 67, 67, 99, 99, 2, 0, 68, 68, 100, 100, 2, 0, 70, 70, 102, 102,
2, 0, 71, 71, 103, 103, 2, 0, 72, 72, 104, 104, 2, 0, 73, 73, 105, 105,
2, 0, 74, 74, 106, 106, 2, 0, 75, 75, 107, 107, 2, 0, 76, 76, 108, 108,
2, 0, 77, 77, 109, 109, 2, 0, 78, 78, 110, 110, 2, 0, 79, 79, 111, 111,
2, 0, 80, 80, 112, 112, 2, 0, 81, 81, 113, 113, 2, 0, 82, 82, 114, 114,
2, 0, 83, 83, 115, 115, 2, 0, 84, 84, 116, 116, 2, 0, 85, 85, 117, 117,
2, 0, 86, 86, 118, 118, 2, 0, 87, 87, 119, 119, 2, 0, 88, 88, 120, 120,
2, 0, 89, 89, 121, 121, 2, 0, 90, 90, 122, 122, 465, 0, 1, 1, 0, 0, 0,
0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0,
0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0,
0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0,
0, 0, 0, 27, 1, 0, 0, 0, 0, 29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, 1,
0, 0, 0, 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 41,
1, 0, 0, 0, 0, 43, 1, 0, 0, 0, 0, 45, 1, 0, 0, 0, 0, 47, 1, 0, 0, 0, 0,
49, 1, 0, 0, 0, 0, 51, 1, 0, 0, 0, 0, 53, 1, 0, 0, 0, 0, 55, 1, 0, 0, 0,
0, 57, 1, 0, 0, 0, 0, 59, 1, 0, 0, 0, 0, 61, 1, 0, 0, 0, 0, 63, 1, 0, 0,
0, 0, 65, 1, 0, 0, 0, 0, 67, 1, 0, 0, 0, 0, 69, 1, 0, 0, 0, 0, 71, 1, 0,
0, 0, 0, 73, 1, 0, 0, 0, 0, 75, 1, 0, 0, 0, 0, 77, 1, 0, 0, 0, 0, 83, 1,
0, 0, 0, 0, 85, 1, 0, 0, 0, 0, 87, 1, 0, 0, 0, 0, 89, 1, 0, 0, 0, 0, 91,
1, 0, 0, 0, 0, 93, 1, 0, 0, 0, 0, 95, 1, 0, 0, 0, 0, 97, 1, 0, 0, 0, 0,
99, 1, 0, 0, 0, 0, 161, 1, 0, 0, 0, 1, 163, 1, 0, 0, 0, 3, 165, 1, 0, 0,
0, 5, 167, 1, 0, 0, 0, 7, 172, 1, 0, 0, 0, 9, 177, 1, 0, 0, 0, 11, 179,
1, 0, 0, 0, 13, 182, 1, 0, 0, 0, 15, 186, 1, 0, 0, 0, 17, 190, 1, 0, 0,
0, 19, 194, 1, 0, 0, 0, 21, 198, 1, 0, 0, 0, 23, 204, 1, 0, 0, 0, 25, 210,
1, 0, 0, 0, 27, 216, 1, 0, 0, 0, 29, 222, 1, 0, 0, 0, 31, 225, 1, 0, 0,
0, 33, 227, 1, 0, 0, 0, 35, 229, 1, 0, 0, 0, 37, 232, 1, 0, 0, 0, 39, 235,
1, 0, 0, 0, 41, 237, 1, 0, 0, 0, 43, 240, 1, 0, 0, 0, 45, 242, 1, 0, 0,
0, 47, 259, 1, 0, 0, 0, 49, 261, 1, 0, 0, 0, 51, 263, 1, 0, 0, 0, 53, 279,
1, 0, 0, 0, 55, 281, 1, 0, 0, 0, 57, 289, 1, 0, 0, 0, 59, 295, 1, 0, 0,
0, 61, 297, 1, 0, 0, 0, 63, 299, 1, 0, 0, 0, 65, 301, 1, 0, 0, 0, 67, 303,
1, 0, 0, 0, 69, 305, 1, 0, 0, 0, 71, 307, 1, 0, 0, 0, 73, 317, 1, 0, 0,
0, 75, 319, 1, 0, 0, 0, 77, 345, 1, 0, 0, 0, 79, 355, 1, 0, 0, 0, 81, 357,
1, 0, 0, 0, 83, 363, 1, 0, 0, 0, 85, 366, 1, 0, 0, 0, 87, 368, 1, 0, 0,
0, 89, 371, 1, 0, 0, 0, 91, 373, 1, 0, 0, 0, 93, 376, 1, 0, 0, 0, 95, 379,
1, 0, 0, 0, 97, 384, 1, 0, 0, 0, 99, 387, 1, 0, 0, 0, 101, 397, 1, 0, 0,
0, 103, 402, 1, 0, 0, 0, 105, 408, 1, 0, 0, 0, 107, 410, 1, 0, 0, 0, 109,
412, 1, 0, 0, 0, 111, 414, 1, 0, 0, 0, 113, 416, 1, 0, 0, 0, 115, 418,
1, 0, 0, 0, 117, 420, 1, 0, 0, 0, 119, 422, 1, 0, 0, 0, 121, 424, 1, 0,
0, 0, 123, 426, 1, 0, 0, 0, 125, 428, 1, 0, 0, 0, 127, 430, 1, 0, 0, 0,
129, 432, 1, 0, 0, 0, 131, 434, 1, 0, 0, 0, 133, 436, 1, 0, 0, 0, 135,
438, 1, 0, 0, 0, 137, 440, 1, 0, 0, 0, 139, 442, 1, 0, 0, 0, 141, 444,
1, 0, 0, 0, 143, 446, 1, 0, 0, 0, 145, 448, 1, 0, 0, 0, 147, 450, 1, 0,
0, 0, 149, 452, 1, 0, 0, 0, 151, 454, 1, 0, 0, 0, 153, 456, 1, 0, 0, 0,
155, 458, 1, 0, 0, 0, 157, 460, 1, 0, 0, 0, 159, 462, 1, 0, 0, 0, 161,
464, 1, 0, 0, 0, 163, 164, 5, 59, 0, 0, 164, 2, 1, 0, 0, 0, 165, 166, 5,
42, 0, 0, 166, 4, 1, 0, 0, 0, 167, 168, 5, 106, 0, 0, 168, 169, 5, 111,
0, 0, 169, 170, 5, 105, 0, 0, 170, 171, 5, 110, 0, 0, 171, 6, 1, 0, 0,
0, 172, 173, 5, 74, 0, 0, 173, 174, 5, 79, 0, 0, 174, 175, 5, 73, 0, 0,
175, 176, 5, 78, 0, 0, 176, 8, 1, 0, 0, 0, 177, 178, 5, 106, 0, 0, 178,
10, 1, 0, 0, 0, 179, 180, 5, 46, 0, 0, 180, 181, 5, 91, 0, 0, 181, 12,
1, 0, 0, 0, 182, 183, 5, 115, 0, 0, 183, 184, 5, 117, 0, 0, 184, 185, 5,
109, 0, 0, 185, 14, 1, 0, 0, 0, 186, 187, 5, 83, 0, 0, 187, 188, 5, 85,
0, 0, 188, 189, 5, 77, 0, 0, 189, 16, 1, 0, 0, 0, 190, 191, 5, 97, 0, 0,
191, 192, 5, 118, 0, 0, 192, 193, 5, 103, 0, 0, 193, 18, 1, 0, 0, 0, 194,
195, 5, 65, 0, 0, 195, 196, 5, 86, 0, 0, 196, 197, 5, 71, 0, 0, 197, 20,
1, 0, 0, 0, 198, 199, 5, 99, 0, 0, 199, 200, 5, 111, 0, 0, 200, 201, 5,
117, 0, 0, 201, 202, 5, 110, 0, 0, 202, 203, 5, 116, 0, 0, 203, 22, 1,
0, 0, 0, 204, 205, 5, 67, 0, 0, 205, 206, 5, 79, 0, 0, 206, 207, 5, 85,
0, 0, 207, 208, 5, 78, 0, 0, 208, 209, 5, 84, 0, 0, 209, 24, 1, 0, 0, 0,
210, 211, 5, 119, 0, 0, 211, 212, 5, 104, 0, 0, 212, 213, 5, 101, 0, 0,
213, 214, 5, 114, 0, 0, 214, 215, 5, 101, 0, 0, 215, 26, 1, 0, 0, 0, 216,
217, 5, 87, 0, 0, 217, 218, 5, 72, 0, 0, 218, 219, 5, 69, 0, 0, 219, 220,
5, 82, 0, 0, 220, 221, 5, 69, 0, 0, 221, 28, 1, 0, 0, 0, 222, 223, 5, 124,
0, 0, 223, 224, 5, 124, 0, 0, 224, 30, 1, 0, 0, 0, 225, 226, 5, 47, 0,
0, 226, 32, 1, 0, 0, 0, 227, 228, 5, 37, 0, 0, 228, 34, 1, 0, 0, 0, 229,
230, 5, 60, 0, 0, 230, 231, 5, 60, 0, 0, 231, 36, 1, 0, 0, 0, 232, 233,
5, 62, 0, 0, 233, 234, 5, 62, 0, 0, 234, 38, 1, 0, 0, 0, 235, 236, 5, 38,
0, 0, 236, 40, 1, 0, 0, 0, 237, 238, 5, 38, 0, 0, 238, 239, 5, 38, 0, 0,
239, 42, 1, 0, 0, 0, 240, 241, 5, 126, 0, 0, 241, 44, 1, 0, 0, 0, 242,
243, 5, 33, 0, 0, 243, 46, 1, 0, 0, 0, 244, 245, 5, 103, 0, 0, 245, 246,
5, 114, 0, 0, 246, 247, 5, 111, 0, 0, 247, 248, 5, 117, 0, 0, 248, 249,
5, 112, 0, 0, 249, 250, 5, 98, 0, 0, 250, 260, 5, 121, 0, 0, 251, 252,
5, 103, 0, 0, 252, 253, 5, 114, 0, 0, 253, 254, 5, 111, 0, 0, 254, 255,
5, 117, 0, 0, 255, 256, 5, 112, 0, 0, 256, 257, 5, 95, 0, 0, 257, 258,
5, 98, 0, 0, 258, 260, 5, 121, 0, 0, 259, 244, 1, 0, 0, 0, 259, 251, 1,
0, 0, 0, 260, 48, 1, 0, 0, 0, 261, 262, 5, 43, 0, 0, 262, 50, 1, 0, 0,
0, 263, 264, 5, 45, 0, 0, 264, 52, 1, 0, 0, 0, 265, 266, 5, 111, 0, 0,
266, 267, 5, 114, 0, 0, 267, 268, 5, 100, 0, 0, 268, 269, 5, 101, 0, 0,
269, 270, 5, 114, 0, 0, 270, 271, 5, 98, 0, 0, 271, 280, 5, 121, 0, 0,
272, 273, 5, 115, 0, 0, 273, 274, 5, 111, 0, 0, 274, 275, 5, 114, 0, 0,
275, 276, 5, 116, 0, 0, 276, 277, 5, 95, 0, 0, 277, 278, 5, 98, 0, 0, 278,
280, 5, 121, 0, 0, 279, 265, 1, 0, 0, 0, 279, 272, 1, 0, 0, 0, 280, 54,
1, 0, 0, 0, 281, 285, 7, 0, 0, 0, 282, 284, 7, 1, 0, 0, 283, 282, 1, 0,
0, 0, 284, 287, 1, 0, 0, 0, 285, 283, 1, 0, 0, 0, 285, 286, 1, 0, 0, 0,
286, 56, 1, 0, 0, 0, 287, 285, 1, 0, 0, 0, 288, 290, 7, 2, 0, 0, 289, 288,
1, 0, 0, 0, 290, 291, 1, 0, 0, 0, 291, 289, 1, 0, 0, 0, 291, 292, 1, 0,
0, 0, 292, 293, 1, 0, 0, 0, 293, 294, 6, 28, 0, 0, 294, 58, 1, 0, 0, 0,
295, 296, 5, 40, 0, 0, 296, 60, 1, 0, 0, 0, 297, 298, 5, 41, 0, 0, 298,
62, 1, 0, 0, 0, 299, 300, 5, 91, 0, 0, 300, 64, 1, 0, 0, 0, 301, 302, 5,
93, 0, 0, 302, 66, 1, 0, 0, 0, 303, 304, 5, 44, 0, 0, 304, 68, 1, 0, 0,
0, 305, 306, 5, 124, 0, 0, 306, 70, 1, 0, 0, 0, 307, 308, 5, 58, 0, 0,
308, 72, 1, 0, 0, 0, 309, 310, 5, 110, 0, 0, 310, 311, 5, 117, 0, 0, 311,
312, 5, 108, 0, 0, 312, 318, 5, 108, 0, 0, 313, 314, 5, 78, 0, 0, 314,
315, 5, 85, 0, 0, 315, 316, 5, 76, 0, 0, 316, 318, 5, 76, 0, 0, 317, 309,
1, 0, 0, 0, 317, 313, 1, 0, 0, 0, 318, 74, 1, 0, 0, 0, 319, 320, 3, 79,
39, 0, 320, 76, 1, 0, 0, 0, 321, 346, 3, 75, 37, 0, 322, 324, 5, 45, 0,
0, 323, 322, 1, 0, 0, 0, 323, 324, 1, 0, 0, 0, 324, 325, 1, 0, 0, 0, 325,
326, 3, 79, 39, 0, 326, 328, 5, 46, 0, 0, 327, 329, 7, 3, 0, 0, 328, 327,
1, 0, 0, 0, 329, 330, 1, 0, 0, 0, 330, 328, 1, 0, 0, 0, 330, 331, 1, 0,
0, 0, 331, 333, 1, 0, 0, 0, 332, 334, 3, 81, 40, 0, 333, 332, 1, 0, 0,
0, 333, 334, 1, 0, 0, 0, 334, 346, 1, 0, 0, 0, 335, 337, 5, 45, 0, 0, 336,
335, 1, 0, 0, 0, 336, 337, 1, 0, 0, 0, 337, 338, 1, 0, 0, 0, 338, 339,
3, 79, 39, 0, 339, 340, 3, 81, 40, 0, 340, 346, 1, 0, 0, 0, 341, 343, 5,
45, 0, 0, 342, 341, 1, 0, 0, 0, 342, 343, 1, 0, 0, 0, 343, 344, 1, 0, 0,
0, 344, 346, 3, 79, 39, 0, 345, 321, 1, 0, 0, 0, 345, 323, 1, 0, 0, 0,
345, 336, 1, 0, 0, 0, 345, 342, 1, 0, 0, 0, 346, 78, 1, 0, 0, 0, 347, 356,
5, 48, 0, 0, 348, 352, 7, 4, 0, 0, 349, 351, 7, 3, 0, 0, 350, 349, 1, 0,
0, 0, 351, 354, 1, 0, 0, 0, 352, 350, 1, 0, 0, 0, 352, 353, 1, 0, 0, 0,
353, 356, 1, 0, 0, 0, 354, 352, 1, 0, 0, 0, 355, 347, 1, 0, 0, 0, 355,
348, 1, 0, 0, 0, 356, 80, 1, 0, 0, 0, 357, 359, 7, 5, 0, 0, 358, 360, 7,
6, 0, 0, 359, 358, 1, 0, 0, 0, 359, 360, 1, 0, 0, 0, 360, 361, 1, 0, 0,
0, 361, 362, 3, 79, 39, 0, 362, 82, 1, 0, 0, 0, 363, 364, 5, 60, 0, 0,
364, 365, 5, 61, 0, 0, 365, 84, 1, 0, 0, 0, 366, 367, 5, 60, 0, 0, 367,
86, 1, 0, 0, 0, 368, 369, 5, 62, 0, 0, 369, 370, 5, 61, 0, 0, 370, 88,
1, 0, 0, 0, 371, 372, 5, 62, 0, 0, 372, 90, 1, 0, 0, 0, 373, 374, 5, 33,
0, 0, 374, 375, 5, 61, 0, 0, 375, 92, 1, 0, 0, 0, 376, 377, 5, 61, 0, 0,
377, 378, 5, 61, 0, 0, 378, 94, 1, 0, 0, 0, 379, 382, 5, 46, 0, 0, 380,
383, 3, 55, 27, 0, 381, 383, 3, 99, 49, 0, 382, 380, 1, 0, 0, 0, 382, 381,
1, 0, 0, 0, 383, 96, 1, 0, 0, 0, 384, 385, 5, 64, 0, 0, 385, 386, 3, 55,
27, 0, 386, 98, 1, 0, 0, 0, 387, 392, 5, 34, 0, 0, 388, 391, 3, 101, 50,
0, 389, 391, 8, 7, 0, 0, 390, 388, 1, 0, 0, 0, 390, 389, 1, 0, 0, 0, 391,
394, 1, 0, 0, 0, 392, 390, 1, 0, 0, 0, 392, 393, 1, 0, 0, 0, 393, 395,
1, 0, 0, 0, 394, 392, 1, 0, 0, 0, 395, 396, 5, 34, 0, 0, 396, 100, 1, 0,
0, 0, 397, 400, 5, 92, 0, 0, 398, 401, 7, 8, 0, 0, 399, 401, 3, 103, 51,
0, 400, 398, 1, 0, 0, 0, 400, 399, 1, 0, 0, 0, 401, 102, 1, 0, 0, 0, 402,
403, 5, 117, 0, 0, 403, 404, 3, 105, 52, 0, 404, 405, 3, 105, 52, 0, 405,
406, 3, 105, 52, 0, 406, 407, 3, 105, 52, 0, 407, 104, 1, 0, 0, 0, 408,
409, 7, 9, 0, 0, 409, 106, 1, 0, 0, 0, 410, 411, 7, 3, 0, 0, 411, 108,
1, 0, 0, 0, 412, 413, 7, 10, 0, 0, 413, 110, 1, 0, 0, 0, 414, 415, 7, 11,
0, 0, 415, 112, 1, 0, 0, 0, 416, 417, 7, 12, 0, 0, 417, 114, 1, 0, 0, 0,
418, 419, 7, 13, 0, 0, 419, 116, 1, 0, 0, 0, 420, 421, 7, 5, 0, 0, 421,
118, 1, 0, 0, 0, 422, 423, 7, 14, 0, 0, 423, 120, 1, 0, 0, 0, 424, 425,
7, 15, 0, 0, 425, 122, 1, 0, 0, 0, 426, 427, 7, 16, 0, 0, 427, 124, 1,
0, 0, 0, 428, 429, 7, 17, 0, 0, 429, 126, 1, 0, 0, 0, 430, 431, 7, 18,
0, 0, 431, 128, 1, 0, 0, 0, 432, 433, 7, 19, 0, 0, 433, 130, 1, 0, 0, 0,
434, 435, 7, 20, 0, 0, 435, 132, 1, 0, 0, 0, 436, 437, 7, 21, 0, 0, 437,
134, 1, 0, 0, 0, 438, 439, 7, 22, 0, 0, 439, 136, 1, 0, 0, 0, 440, 441,
7, 23, 0, 0, 441, 138, 1, 0, 0, 0, 442, 443, 7, 24, 0, 0, 443, 140, 1,
0, 0, 0, 444, 445, 7, 25, 0, 0, 445, 142, 1, 0, 0, 0, 446, 447, 7, 26,
0, 0, 447, 144, 1, 0, 0, 0, 448, 449, 7, 27, 0, 0, 449, 146, 1, 0, 0, 0,
450, 451, 7, 28, 0, 0, 451, 148, 1, 0, 0, 0, 452, 453, 7, 29, 0, 0, 453,
150, 1, 0, 0, 0, 454, 455, 7, 30, 0, 0, 455, 152, 1, 0, 0, 0, 456, 457,
7, 31, 0, 0, 457, 154, 1, 0, 0, 0, 458, 459, 7, 32, 0, 0, 459, 156, 1,
0, 0, 0, 460, 461, 7, 33, 0, 0, 461, 158, 1, 0, 0, 0, 462, 463, 7, 34,
0, 0, 463, 160, 1, 0, 0, 0, 464, 465, 5, 47, 0, 0, 465, 466, 5, 47, 0,
0, 466, 470, 1, 0, 0, 0, 467, 469, 9, 0, 0, 0, 468, 467, 1, 0, 0, 0, 469,
472, 1, 0, 0, 0, 470, 471, 1, 0, 0, 0, 470, 468, 1, 0, 0, 0, 471, 473,
1, 0, 0, 0, 472, 470, 1, 0, 0, 0, 473, 474, 5, 10, 0, 0, 474, 475, 1, 0,
0, 0, 475, 476, 6, 80, 0, 0, 476, 162, 1, 0, 0, 0, 20, 0, 259, 279, 285,
291, 317, 323, 330, 333, 336, 342, 345, 352, 355, 359, 382, 390, 392, 400,
470, 1, 6, 0, 0,
2, 68, 7, 68, 2, 69, 7, 69, 2, 70, 7, 70, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2,
1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5,
1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 10, 1, 10,
1, 10, 1, 11, 1, 11, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1,
13, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16,
1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1,
16, 1, 16, 3, 16, 206, 8, 16, 1, 17, 1, 17, 5, 17, 210, 8, 17, 10, 17,
12, 17, 213, 9, 17, 1, 18, 4, 18, 216, 8, 18, 11, 18, 12, 18, 217, 1, 18,
1, 18, 1, 19, 1, 19, 1, 20, 1, 20, 1, 21, 1, 21, 1, 22, 1, 22, 1, 23, 1,
23, 1, 24, 1, 24, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26,
1, 26, 1, 26, 3, 26, 244, 8, 26, 1, 27, 1, 27, 1, 28, 1, 28, 3, 28, 250,
8, 28, 1, 28, 1, 28, 1, 28, 4, 28, 255, 8, 28, 11, 28, 12, 28, 256, 1,
28, 3, 28, 260, 8, 28, 1, 28, 3, 28, 263, 8, 28, 1, 28, 1, 28, 1, 28, 1,
28, 3, 28, 269, 8, 28, 1, 28, 3, 28, 272, 8, 28, 1, 29, 1, 29, 1, 29, 5,
29, 277, 8, 29, 10, 29, 12, 29, 280, 9, 29, 3, 29, 282, 8, 29, 1, 30, 1,
30, 3, 30, 286, 8, 30, 1, 30, 1, 30, 1, 31, 1, 31, 1, 31, 1, 32, 1, 32,
1, 33, 1, 33, 1, 33, 1, 34, 1, 34, 1, 35, 1, 35, 1, 35, 1, 36, 1, 36, 1,
36, 1, 37, 1, 37, 1, 37, 3, 37, 309, 8, 37, 1, 38, 1, 38, 1, 38, 1, 39,
1, 39, 1, 39, 5, 39, 317, 8, 39, 10, 39, 12, 39, 320, 9, 39, 1, 39, 1,
39, 1, 40, 1, 40, 1, 40, 3, 40, 327, 8, 40, 1, 41, 1, 41, 1, 41, 1, 41,
1, 41, 1, 41, 1, 42, 1, 42, 1, 43, 1, 43, 1, 44, 1, 44, 1, 45, 1, 45, 1,
46, 1, 46, 1, 47, 1, 47, 1, 48, 1, 48, 1, 49, 1, 49, 1, 50, 1, 50, 1, 51,
1, 51, 1, 52, 1, 52, 1, 53, 1, 53, 1, 54, 1, 54, 1, 55, 1, 55, 1, 56, 1,
56, 1, 57, 1, 57, 1, 58, 1, 58, 1, 59, 1, 59, 1, 60, 1, 60, 1, 61, 1, 61,
1, 62, 1, 62, 1, 63, 1, 63, 1, 64, 1, 64, 1, 65, 1, 65, 1, 66, 1, 66, 1,
67, 1, 67, 1, 68, 1, 68, 1, 69, 1, 69, 1, 70, 1, 70, 5, 70, 393, 8, 70,
10, 70, 12, 70, 396, 9, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 394, 0, 71,
1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11,
23, 12, 25, 13, 27, 14, 29, 15, 31, 16, 33, 17, 35, 18, 37, 19, 39, 20,
41, 21, 43, 22, 45, 23, 47, 24, 49, 25, 51, 26, 53, 27, 55, 28, 57, 29,
59, 0, 61, 0, 63, 30, 65, 31, 67, 32, 69, 33, 71, 34, 73, 35, 75, 36, 77,
37, 79, 38, 81, 0, 83, 0, 85, 0, 87, 0, 89, 0, 91, 0, 93, 0, 95, 0, 97,
0, 99, 0, 101, 0, 103, 0, 105, 0, 107, 0, 109, 0, 111, 0, 113, 0, 115,
0, 117, 0, 119, 0, 121, 0, 123, 0, 125, 0, 127, 0, 129, 0, 131, 0, 133,
0, 135, 0, 137, 0, 139, 0, 141, 39, 1, 0, 35, 3, 0, 65, 90, 95, 95, 97,
122, 4, 0, 48, 57, 65, 90, 95, 95, 97, 122, 3, 0, 9, 10, 13, 13, 32, 32,
1, 0, 48, 57, 1, 0, 49, 57, 2, 0, 69, 69, 101, 101, 2, 0, 43, 43, 45, 45,
2, 0, 34, 34, 92, 92, 8, 0, 34, 34, 47, 47, 92, 92, 98, 98, 102, 102, 110,
110, 114, 114, 116, 116, 3, 0, 48, 57, 65, 70, 97, 102, 2, 0, 65, 65, 97,
97, 2, 0, 66, 66, 98, 98, 2, 0, 67, 67, 99, 99, 2, 0, 68, 68, 100, 100,
2, 0, 70, 70, 102, 102, 2, 0, 71, 71, 103, 103, 2, 0, 72, 72, 104, 104,
2, 0, 73, 73, 105, 105, 2, 0, 74, 74, 106, 106, 2, 0, 75, 75, 107, 107,
2, 0, 76, 76, 108, 108, 2, 0, 77, 77, 109, 109, 2, 0, 78, 78, 110, 110,
2, 0, 79, 79, 111, 111, 2, 0, 80, 80, 112, 112, 2, 0, 81, 81, 113, 113,
2, 0, 82, 82, 114, 114, 2, 0, 83, 83, 115, 115, 2, 0, 84, 84, 116, 116,
2, 0, 85, 85, 117, 117, 2, 0, 86, 86, 118, 118, 2, 0, 87, 87, 119, 119,
2, 0, 88, 88, 120, 120, 2, 0, 89, 89, 121, 121, 2, 0, 90, 90, 122, 122,
388, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0,
0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1,
0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23,
1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 27, 1, 0, 0, 0, 0, 29, 1, 0, 0, 0, 0,
31, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0,
0, 39, 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0, 0, 0, 45, 1, 0, 0,
0, 0, 47, 1, 0, 0, 0, 0, 49, 1, 0, 0, 0, 0, 51, 1, 0, 0, 0, 0, 53, 1, 0,
0, 0, 0, 55, 1, 0, 0, 0, 0, 57, 1, 0, 0, 0, 0, 63, 1, 0, 0, 0, 0, 65, 1,
0, 0, 0, 0, 67, 1, 0, 0, 0, 0, 69, 1, 0, 0, 0, 0, 71, 1, 0, 0, 0, 0, 73,
1, 0, 0, 0, 0, 75, 1, 0, 0, 0, 0, 77, 1, 0, 0, 0, 0, 79, 1, 0, 0, 0, 0,
141, 1, 0, 0, 0, 1, 143, 1, 0, 0, 0, 3, 145, 1, 0, 0, 0, 5, 147, 1, 0,
0, 0, 7, 152, 1, 0, 0, 0, 9, 155, 1, 0, 0, 0, 11, 158, 1, 0, 0, 0, 13,
160, 1, 0, 0, 0, 15, 162, 1, 0, 0, 0, 17, 165, 1, 0, 0, 0, 19, 168, 1,
0, 0, 0, 21, 170, 1, 0, 0, 0, 23, 173, 1, 0, 0, 0, 25, 175, 1, 0, 0, 0,
27, 177, 1, 0, 0, 0, 29, 186, 1, 0, 0, 0, 31, 188, 1, 0, 0, 0, 33, 205,
1, 0, 0, 0, 35, 207, 1, 0, 0, 0, 37, 215, 1, 0, 0, 0, 39, 221, 1, 0, 0,
0, 41, 223, 1, 0, 0, 0, 43, 225, 1, 0, 0, 0, 45, 227, 1, 0, 0, 0, 47, 229,
1, 0, 0, 0, 49, 231, 1, 0, 0, 0, 51, 233, 1, 0, 0, 0, 53, 243, 1, 0, 0,
0, 55, 245, 1, 0, 0, 0, 57, 271, 1, 0, 0, 0, 59, 281, 1, 0, 0, 0, 61, 283,
1, 0, 0, 0, 63, 289, 1, 0, 0, 0, 65, 292, 1, 0, 0, 0, 67, 294, 1, 0, 0,
0, 69, 297, 1, 0, 0, 0, 71, 299, 1, 0, 0, 0, 73, 302, 1, 0, 0, 0, 75, 305,
1, 0, 0, 0, 77, 310, 1, 0, 0, 0, 79, 313, 1, 0, 0, 0, 81, 323, 1, 0, 0,
0, 83, 328, 1, 0, 0, 0, 85, 334, 1, 0, 0, 0, 87, 336, 1, 0, 0, 0, 89, 338,
1, 0, 0, 0, 91, 340, 1, 0, 0, 0, 93, 342, 1, 0, 0, 0, 95, 344, 1, 0, 0,
0, 97, 346, 1, 0, 0, 0, 99, 348, 1, 0, 0, 0, 101, 350, 1, 0, 0, 0, 103,
352, 1, 0, 0, 0, 105, 354, 1, 0, 0, 0, 107, 356, 1, 0, 0, 0, 109, 358,
1, 0, 0, 0, 111, 360, 1, 0, 0, 0, 113, 362, 1, 0, 0, 0, 115, 364, 1, 0,
0, 0, 117, 366, 1, 0, 0, 0, 119, 368, 1, 0, 0, 0, 121, 370, 1, 0, 0, 0,
123, 372, 1, 0, 0, 0, 125, 374, 1, 0, 0, 0, 127, 376, 1, 0, 0, 0, 129,
378, 1, 0, 0, 0, 131, 380, 1, 0, 0, 0, 133, 382, 1, 0, 0, 0, 135, 384,
1, 0, 0, 0, 137, 386, 1, 0, 0, 0, 139, 388, 1, 0, 0, 0, 141, 390, 1, 0,
0, 0, 143, 144, 5, 59, 0, 0, 144, 2, 1, 0, 0, 0, 145, 146, 5, 42, 0, 0,
146, 4, 1, 0, 0, 0, 147, 148, 5, 106, 0, 0, 148, 149, 5, 111, 0, 0, 149,
150, 5, 105, 0, 0, 150, 151, 5, 110, 0, 0, 151, 6, 1, 0, 0, 0, 152, 153,
5, 46, 0, 0, 153, 154, 5, 91, 0, 0, 154, 8, 1, 0, 0, 0, 155, 156, 5, 124,
0, 0, 156, 157, 5, 124, 0, 0, 157, 10, 1, 0, 0, 0, 158, 159, 5, 47, 0,
0, 159, 12, 1, 0, 0, 0, 160, 161, 5, 37, 0, 0, 161, 14, 1, 0, 0, 0, 162,
163, 5, 60, 0, 0, 163, 164, 5, 60, 0, 0, 164, 16, 1, 0, 0, 0, 165, 166,
5, 62, 0, 0, 166, 167, 5, 62, 0, 0, 167, 18, 1, 0, 0, 0, 168, 169, 5, 38,
0, 0, 169, 20, 1, 0, 0, 0, 170, 171, 5, 38, 0, 0, 171, 172, 5, 38, 0, 0,
172, 22, 1, 0, 0, 0, 173, 174, 5, 126, 0, 0, 174, 24, 1, 0, 0, 0, 175,
176, 5, 33, 0, 0, 176, 26, 1, 0, 0, 0, 177, 178, 5, 103, 0, 0, 178, 179,
5, 114, 0, 0, 179, 180, 5, 111, 0, 0, 180, 181, 5, 117, 0, 0, 181, 182,
5, 112, 0, 0, 182, 183, 5, 95, 0, 0, 183, 184, 5, 98, 0, 0, 184, 185, 5,
121, 0, 0, 185, 28, 1, 0, 0, 0, 186, 187, 5, 43, 0, 0, 187, 30, 1, 0, 0,
0, 188, 189, 5, 45, 0, 0, 189, 32, 1, 0, 0, 0, 190, 191, 5, 111, 0, 0,
191, 192, 5, 114, 0, 0, 192, 193, 5, 100, 0, 0, 193, 194, 5, 101, 0, 0,
194, 195, 5, 114, 0, 0, 195, 196, 5, 95, 0, 0, 196, 197, 5, 98, 0, 0, 197,
206, 5, 121, 0, 0, 198, 199, 5, 115, 0, 0, 199, 200, 5, 111, 0, 0, 200,
201, 5, 114, 0, 0, 201, 202, 5, 116, 0, 0, 202, 203, 5, 95, 0, 0, 203,
204, 5, 98, 0, 0, 204, 206, 5, 121, 0, 0, 205, 190, 1, 0, 0, 0, 205, 198,
1, 0, 0, 0, 206, 34, 1, 0, 0, 0, 207, 211, 7, 0, 0, 0, 208, 210, 7, 1,
0, 0, 209, 208, 1, 0, 0, 0, 210, 213, 1, 0, 0, 0, 211, 209, 1, 0, 0, 0,
211, 212, 1, 0, 0, 0, 212, 36, 1, 0, 0, 0, 213, 211, 1, 0, 0, 0, 214, 216,
7, 2, 0, 0, 215, 214, 1, 0, 0, 0, 216, 217, 1, 0, 0, 0, 217, 215, 1, 0,
0, 0, 217, 218, 1, 0, 0, 0, 218, 219, 1, 0, 0, 0, 219, 220, 6, 18, 0, 0,
220, 38, 1, 0, 0, 0, 221, 222, 5, 40, 0, 0, 222, 40, 1, 0, 0, 0, 223, 224,
5, 41, 0, 0, 224, 42, 1, 0, 0, 0, 225, 226, 5, 91, 0, 0, 226, 44, 1, 0,
0, 0, 227, 228, 5, 93, 0, 0, 228, 46, 1, 0, 0, 0, 229, 230, 5, 44, 0, 0,
230, 48, 1, 0, 0, 0, 231, 232, 5, 124, 0, 0, 232, 50, 1, 0, 0, 0, 233,
234, 5, 58, 0, 0, 234, 52, 1, 0, 0, 0, 235, 236, 5, 110, 0, 0, 236, 237,
5, 117, 0, 0, 237, 238, 5, 108, 0, 0, 238, 244, 5, 108, 0, 0, 239, 240,
5, 78, 0, 0, 240, 241, 5, 85, 0, 0, 241, 242, 5, 76, 0, 0, 242, 244, 5,
76, 0, 0, 243, 235, 1, 0, 0, 0, 243, 239, 1, 0, 0, 0, 244, 54, 1, 0, 0,
0, 245, 246, 3, 59, 29, 0, 246, 56, 1, 0, 0, 0, 247, 272, 3, 55, 27, 0,
248, 250, 5, 45, 0, 0, 249, 248, 1, 0, 0, 0, 249, 250, 1, 0, 0, 0, 250,
251, 1, 0, 0, 0, 251, 252, 3, 59, 29, 0, 252, 254, 5, 46, 0, 0, 253, 255,
7, 3, 0, 0, 254, 253, 1, 0, 0, 0, 255, 256, 1, 0, 0, 0, 256, 254, 1, 0,
0, 0, 256, 257, 1, 0, 0, 0, 257, 259, 1, 0, 0, 0, 258, 260, 3, 61, 30,
0, 259, 258, 1, 0, 0, 0, 259, 260, 1, 0, 0, 0, 260, 272, 1, 0, 0, 0, 261,
263, 5, 45, 0, 0, 262, 261, 1, 0, 0, 0, 262, 263, 1, 0, 0, 0, 263, 264,
1, 0, 0, 0, 264, 265, 3, 59, 29, 0, 265, 266, 3, 61, 30, 0, 266, 272, 1,
0, 0, 0, 267, 269, 5, 45, 0, 0, 268, 267, 1, 0, 0, 0, 268, 269, 1, 0, 0,
0, 269, 270, 1, 0, 0, 0, 270, 272, 3, 59, 29, 0, 271, 247, 1, 0, 0, 0,
271, 249, 1, 0, 0, 0, 271, 262, 1, 0, 0, 0, 271, 268, 1, 0, 0, 0, 272,
58, 1, 0, 0, 0, 273, 282, 5, 48, 0, 0, 274, 278, 7, 4, 0, 0, 275, 277,
7, 3, 0, 0, 276, 275, 1, 0, 0, 0, 277, 280, 1, 0, 0, 0, 278, 276, 1, 0,
0, 0, 278, 279, 1, 0, 0, 0, 279, 282, 1, 0, 0, 0, 280, 278, 1, 0, 0, 0,
281, 273, 1, 0, 0, 0, 281, 274, 1, 0, 0, 0, 282, 60, 1, 0, 0, 0, 283, 285,
7, 5, 0, 0, 284, 286, 7, 6, 0, 0, 285, 284, 1, 0, 0, 0, 285, 286, 1, 0,
0, 0, 286, 287, 1, 0, 0, 0, 287, 288, 3, 59, 29, 0, 288, 62, 1, 0, 0, 0,
289, 290, 5, 60, 0, 0, 290, 291, 5, 61, 0, 0, 291, 64, 1, 0, 0, 0, 292,
293, 5, 60, 0, 0, 293, 66, 1, 0, 0, 0, 294, 295, 5, 62, 0, 0, 295, 296,
5, 61, 0, 0, 296, 68, 1, 0, 0, 0, 297, 298, 5, 62, 0, 0, 298, 70, 1, 0,
0, 0, 299, 300, 5, 33, 0, 0, 300, 301, 5, 61, 0, 0, 301, 72, 1, 0, 0, 0,
302, 303, 5, 61, 0, 0, 303, 304, 5, 61, 0, 0, 304, 74, 1, 0, 0, 0, 305,
308, 5, 46, 0, 0, 306, 309, 3, 35, 17, 0, 307, 309, 3, 79, 39, 0, 308,
306, 1, 0, 0, 0, 308, 307, 1, 0, 0, 0, 309, 76, 1, 0, 0, 0, 310, 311, 5,
64, 0, 0, 311, 312, 3, 35, 17, 0, 312, 78, 1, 0, 0, 0, 313, 318, 5, 34,
0, 0, 314, 317, 3, 81, 40, 0, 315, 317, 8, 7, 0, 0, 316, 314, 1, 0, 0,
0, 316, 315, 1, 0, 0, 0, 317, 320, 1, 0, 0, 0, 318, 316, 1, 0, 0, 0, 318,
319, 1, 0, 0, 0, 319, 321, 1, 0, 0, 0, 320, 318, 1, 0, 0, 0, 321, 322,
5, 34, 0, 0, 322, 80, 1, 0, 0, 0, 323, 326, 5, 92, 0, 0, 324, 327, 7, 8,
0, 0, 325, 327, 3, 83, 41, 0, 326, 324, 1, 0, 0, 0, 326, 325, 1, 0, 0,
0, 327, 82, 1, 0, 0, 0, 328, 329, 5, 117, 0, 0, 329, 330, 3, 85, 42, 0,
330, 331, 3, 85, 42, 0, 331, 332, 3, 85, 42, 0, 332, 333, 3, 85, 42, 0,
333, 84, 1, 0, 0, 0, 334, 335, 7, 9, 0, 0, 335, 86, 1, 0, 0, 0, 336, 337,
7, 3, 0, 0, 337, 88, 1, 0, 0, 0, 338, 339, 7, 10, 0, 0, 339, 90, 1, 0,
0, 0, 340, 341, 7, 11, 0, 0, 341, 92, 1, 0, 0, 0, 342, 343, 7, 12, 0, 0,
343, 94, 1, 0, 0, 0, 344, 345, 7, 13, 0, 0, 345, 96, 1, 0, 0, 0, 346, 347,
7, 5, 0, 0, 347, 98, 1, 0, 0, 0, 348, 349, 7, 14, 0, 0, 349, 100, 1, 0,
0, 0, 350, 351, 7, 15, 0, 0, 351, 102, 1, 0, 0, 0, 352, 353, 7, 16, 0,
0, 353, 104, 1, 0, 0, 0, 354, 355, 7, 17, 0, 0, 355, 106, 1, 0, 0, 0, 356,
357, 7, 18, 0, 0, 357, 108, 1, 0, 0, 0, 358, 359, 7, 19, 0, 0, 359, 110,
1, 0, 0, 0, 360, 361, 7, 20, 0, 0, 361, 112, 1, 0, 0, 0, 362, 363, 7, 21,
0, 0, 363, 114, 1, 0, 0, 0, 364, 365, 7, 22, 0, 0, 365, 116, 1, 0, 0, 0,
366, 367, 7, 23, 0, 0, 367, 118, 1, 0, 0, 0, 368, 369, 7, 24, 0, 0, 369,
120, 1, 0, 0, 0, 370, 371, 7, 25, 0, 0, 371, 122, 1, 0, 0, 0, 372, 373,
7, 26, 0, 0, 373, 124, 1, 0, 0, 0, 374, 375, 7, 27, 0, 0, 375, 126, 1,
0, 0, 0, 376, 377, 7, 28, 0, 0, 377, 128, 1, 0, 0, 0, 378, 379, 7, 29,
0, 0, 379, 130, 1, 0, 0, 0, 380, 381, 7, 30, 0, 0, 381, 132, 1, 0, 0, 0,
382, 383, 7, 31, 0, 0, 383, 134, 1, 0, 0, 0, 384, 385, 7, 32, 0, 0, 385,
136, 1, 0, 0, 0, 386, 387, 7, 33, 0, 0, 387, 138, 1, 0, 0, 0, 388, 389,
7, 34, 0, 0, 389, 140, 1, 0, 0, 0, 390, 394, 5, 35, 0, 0, 391, 393, 9,
0, 0, 0, 392, 391, 1, 0, 0, 0, 393, 396, 1, 0, 0, 0, 394, 395, 1, 0, 0,
0, 394, 392, 1, 0, 0, 0, 395, 397, 1, 0, 0, 0, 396, 394, 1, 0, 0, 0, 397,
398, 5, 10, 0, 0, 398, 399, 1, 0, 0, 0, 399, 400, 6, 70, 0, 0, 400, 142,
1, 0, 0, 0, 19, 0, 205, 211, 217, 243, 249, 256, 259, 262, 268, 271, 278,
281, 285, 308, 316, 318, 326, 394, 1, 6, 0, 0,
}
deserializer := antlr.NewATNDeserializer(nil)
staticData.atn = deserializer.Deserialize(staticData.serializedATN)
@ -335,40 +299,30 @@ const (
SLQLexerT__10 = 11
SLQLexerT__11 = 12
SLQLexerT__12 = 13
SLQLexerT__13 = 14
SLQLexerT__14 = 15
SLQLexerT__15 = 16
SLQLexerT__16 = 17
SLQLexerT__17 = 18
SLQLexerT__18 = 19
SLQLexerT__19 = 20
SLQLexerT__20 = 21
SLQLexerT__21 = 22
SLQLexerT__22 = 23
SLQLexerGROUP_BY = 24
SLQLexerORDER_ASC = 25
SLQLexerORDER_DESC = 26
SLQLexerORDER_BY = 27
SLQLexerID = 28
SLQLexerWS = 29
SLQLexerLPAR = 30
SLQLexerRPAR = 31
SLQLexerLBRA = 32
SLQLexerRBRA = 33
SLQLexerCOMMA = 34
SLQLexerPIPE = 35
SLQLexerCOLON = 36
SLQLexerNULL = 37
SLQLexerNN = 38
SLQLexerNUMBER = 39
SLQLexerLT_EQ = 40
SLQLexerLT = 41
SLQLexerGT_EQ = 42
SLQLexerGT = 43
SLQLexerNEQ = 44
SLQLexerEQ = 45
SLQLexerNAME = 46
SLQLexerHANDLE = 47
SLQLexerSTRING = 48
SLQLexerLINECOMMENT = 49
SLQLexerGROUP_BY = 14
SLQLexerORDER_ASC = 15
SLQLexerORDER_DESC = 16
SLQLexerORDER_BY = 17
SLQLexerID = 18
SLQLexerWS = 19
SLQLexerLPAR = 20
SLQLexerRPAR = 21
SLQLexerLBRA = 22
SLQLexerRBRA = 23
SLQLexerCOMMA = 24
SLQLexerPIPE = 25
SLQLexerCOLON = 26
SLQLexerNULL = 27
SLQLexerNN = 28
SLQLexerNUMBER = 29
SLQLexerLT_EQ = 30
SLQLexerLT = 31
SLQLexerGT_EQ = 32
SLQLexerGT = 33
SLQLexerNEQ = 34
SLQLexerEQ = 35
SLQLexerNAME = 36
SLQLexerHANDLE = 37
SLQLexerSTRING = 38
SLQLexerLINECOMMENT = 39
)

View File

@ -22,11 +22,14 @@ type SLQListener interface {
// EnterCmpr is called when entering the cmpr production.
EnterCmpr(c *CmprContext)
// EnterFn is called when entering the fn production.
EnterFn(c *FnContext)
// EnterFuncElement is called when entering the funcElement production.
EnterFuncElement(c *FuncElementContext)
// EnterFnElement is called when entering the fnElement production.
EnterFnElement(c *FnElementContext)
// EnterFunc is called when entering the func production.
EnterFunc(c *FuncContext)
// EnterFuncName is called when entering the funcName production.
EnterFuncName(c *FuncNameContext)
// EnterJoin is called when entering the join production.
EnterJoin(c *JoinContext)
@ -34,8 +37,11 @@ type SLQListener interface {
// EnterJoinConstraint is called when entering the joinConstraint production.
EnterJoinConstraint(c *JoinConstraintContext)
// EnterGroup is called when entering the group production.
EnterGroup(c *GroupContext)
// EnterGroupByTerm is called when entering the groupByTerm production.
EnterGroupByTerm(c *GroupByTermContext)
// EnterGroupBy is called when entering the groupBy production.
EnterGroupBy(c *GroupByContext)
// EnterOrderByTerm is called when entering the orderByTerm production.
EnterOrderByTerm(c *OrderByTermContext)
@ -61,9 +67,6 @@ type SLQListener interface {
// EnterRowRange is called when entering the rowRange production.
EnterRowRange(c *RowRangeContext)
// EnterFnName is called when entering the fnName production.
EnterFnName(c *FnNameContext)
// EnterExpr is called when entering the expr production.
EnterExpr(c *ExprContext)
@ -88,11 +91,14 @@ type SLQListener interface {
// ExitCmpr is called when exiting the cmpr production.
ExitCmpr(c *CmprContext)
// ExitFn is called when exiting the fn production.
ExitFn(c *FnContext)
// ExitFuncElement is called when exiting the funcElement production.
ExitFuncElement(c *FuncElementContext)
// ExitFnElement is called when exiting the fnElement production.
ExitFnElement(c *FnElementContext)
// ExitFunc is called when exiting the func production.
ExitFunc(c *FuncContext)
// ExitFuncName is called when exiting the funcName production.
ExitFuncName(c *FuncNameContext)
// ExitJoin is called when exiting the join production.
ExitJoin(c *JoinContext)
@ -100,8 +106,11 @@ type SLQListener interface {
// ExitJoinConstraint is called when exiting the joinConstraint production.
ExitJoinConstraint(c *JoinConstraintContext)
// ExitGroup is called when exiting the group production.
ExitGroup(c *GroupContext)
// ExitGroupByTerm is called when exiting the groupByTerm production.
ExitGroupByTerm(c *GroupByTermContext)
// ExitGroupBy is called when exiting the groupBy production.
ExitGroupBy(c *GroupByContext)
// ExitOrderByTerm is called when exiting the orderByTerm production.
ExitOrderByTerm(c *OrderByTermContext)
@ -127,9 +136,6 @@ type SLQListener interface {
// ExitRowRange is called when exiting the rowRange production.
ExitRowRange(c *RowRangeContext)
// ExitFnName is called when exiting the fnName production.
ExitFnName(c *FnNameContext)
// ExitExpr is called when exiting the expr production.
ExitExpr(c *ExprContext)

File diff suppressed because it is too large Load Diff

View File

@ -22,11 +22,14 @@ type SLQVisitor interface {
// Visit a parse tree produced by SLQParser#cmpr.
VisitCmpr(ctx *CmprContext) interface{}
// Visit a parse tree produced by SLQParser#fn.
VisitFn(ctx *FnContext) interface{}
// Visit a parse tree produced by SLQParser#funcElement.
VisitFuncElement(ctx *FuncElementContext) interface{}
// Visit a parse tree produced by SLQParser#fnElement.
VisitFnElement(ctx *FnElementContext) interface{}
// Visit a parse tree produced by SLQParser#func.
VisitFunc(ctx *FuncContext) interface{}
// Visit a parse tree produced by SLQParser#funcName.
VisitFuncName(ctx *FuncNameContext) interface{}
// Visit a parse tree produced by SLQParser#join.
VisitJoin(ctx *JoinContext) interface{}
@ -34,8 +37,11 @@ type SLQVisitor interface {
// Visit a parse tree produced by SLQParser#joinConstraint.
VisitJoinConstraint(ctx *JoinConstraintContext) interface{}
// Visit a parse tree produced by SLQParser#group.
VisitGroup(ctx *GroupContext) interface{}
// Visit a parse tree produced by SLQParser#groupByTerm.
VisitGroupByTerm(ctx *GroupByTermContext) interface{}
// Visit a parse tree produced by SLQParser#groupBy.
VisitGroupBy(ctx *GroupByContext) interface{}
// Visit a parse tree produced by SLQParser#orderByTerm.
VisitOrderByTerm(ctx *OrderByTermContext) interface{}
@ -61,9 +67,6 @@ type SLQVisitor interface {
// Visit a parse tree produced by SLQParser#rowRange.
VisitRowRange(ctx *RowRangeContext) interface{}
// Visit a parse tree produced by SLQParser#fnName.
VisitFnName(ctx *FnNameContext) interface{}
// Visit a parse tree produced by SLQParser#expr.
VisitExpr(ctx *ExprContext) interface{}

View File

@ -29,8 +29,8 @@ func (jn *JoinNode) RightTbl() *TblSelectorNode {
return jn.rightTbl
}
// Selectable implements the Selectable marker interface.
func (jn *JoinNode) selectable() {
// Tabler implements the Tabler marker interface.
func (jn *JoinNode) tabler() {
// no-op
}

View File

@ -3,6 +3,7 @@ package ast
import (
"fmt"
"reflect"
"strings"
"github.com/antlr/antlr4/runtime/Go/antlr/v4"
)
@ -37,26 +38,27 @@ type Node interface {
Text() string
}
// Selectable is a marker interface to indicate that the node can be
// Tabler is a Node marker interface to indicate that the node can be
// selected from. That is, the node represents a SQL table, view, or
// join table, and can be used like "SELECT * FROM [selectable]".
//
// REVISIT: the name "Selectable" might be confusing. Perhaps "Tabler" or such.
type Selectable interface {
// join table, and can be used like "SELECT * FROM [tabler]".
type Tabler interface {
Node
selectable()
tabler()
}
// selector is a marker interface for selector types.
type selector interface {
// Selector is a Node marker interface for selector node types. A selector node
// models a selector such as ".first_name" or ".actor.last_name".
type Selector interface {
Node
selector()
}
// ResultColumn indicates a column selection expression such as a
// ResultColumn indicates a column selection expression Node such as a
// column name, or context-appropriate function, e.g. "COUNT(*)".
// See: https://www.sqlite.org/syntax/result-column.html
type ResultColumn interface {
Node
// IsColumn returns true if the expression represents
// a column, e.g. ".first_name" or "actor.first_name".
// This method returns false for functions, e.g. "COUNT(*)".
@ -160,6 +162,29 @@ func nodeReplace(old, nu Node) error {
return parent.SetChildren(siblings)
}
// nodesAreOnlyOfType returns an error if the type of any non-nil element
// of nodes is not contained in types.
func nodesAreOnlyOfType(nodes []Node, types ...reflect.Type) error {
m := map[reflect.Type]struct{}{}
typeNames := make([]string, 0, len(types))
for _, typ := range types {
m[typ] = struct{}{}
typeNames = append(typeNames, typ.Name())
}
for i, node := range nodes {
if node == nil {
continue
}
if _, ok := m[reflect.TypeOf(node)]; !ok {
return errorf("node[%d] {%s} is not an allowed type in [%s]", i, node, strings.Join(typeNames, ", "))
}
}
return nil
}
// nodeChildIndex returns the index of child in parent's children, or -1.
func nodeChildIndex(parent, child Node) int {
for i, node := range parent.Children() {
@ -212,40 +237,6 @@ func nodesWithType(nodes []Node, typ reflect.Type) []Node {
return s
}
// GroupByNode models GROUP BY.
type GroupByNode struct {
baseNode
}
// AddChild implements Node.
func (n *GroupByNode) AddChild(child Node) error {
_, ok := child.(selector)
if !ok {
return errorf("GROUP() only accepts children of type %s, but got %T", typeColSelectorNode, child)
}
n.addChild(child)
return child.SetParent(n)
}
// SetChildren implements ast.Node.
func (n *GroupByNode) SetChildren(children []Node) error {
for i := range children {
if _, ok := children[i].(selector); !ok {
return errorf("illegal child [%d] type %T {%s} for %T", i, children[i], children[i], n)
}
}
n.setChildren(children)
return nil
}
// String returns a log/debug-friendly representation.
func (n *GroupByNode) String() string {
text := nodeString(n)
return text
}
// ExprNode models a SLQ expression such as ".uid > 4".
type ExprNode struct {
baseNode
@ -294,6 +285,7 @@ type WhereNode struct {
baseNode
}
// String returns a log/debug-friendly representation.
func (n *WhereNode) String() string {
return nodeString(n)
}
@ -307,6 +299,7 @@ func (n *WhereNode) Expr() *ExprNode {
return n.children[0].(*ExprNode)
}
// AddChild implements Node.
func (n *WhereNode) AddChild(node Node) error {
expr, ok := node.(*ExprNode)
if !ok {
@ -333,15 +326,17 @@ func isOperator(text string) bool {
// Cached results from reflect.TypeOf for node types.
var (
typeAST = reflect.TypeOf((*AST)(nil))
typeHandleNode = reflect.TypeOf((*HandleNode)(nil))
typeSegmentNode = reflect.TypeOf((*SegmentNode)(nil))
typeJoinNode = reflect.TypeOf((*JoinNode)(nil))
typeSelectorNode = reflect.TypeOf((*SelectorNode)(nil))
typeColSelectorNode = reflect.TypeOf((*ColSelectorNode)(nil))
typeTblSelectorNode = reflect.TypeOf((*TblSelectorNode)(nil))
typeRowRangeNode = reflect.TypeOf((*RowRangeNode)(nil))
typeOrderByNode = reflect.TypeOf((*OrderByNode)(nil))
typeGroupByNode = reflect.TypeOf((*GroupByNode)(nil))
typeExprNode = reflect.TypeOf((*ExprNode)(nil))
typeAST = reflect.TypeOf((*AST)(nil))
typeSegmentNode = reflect.TypeOf((*SegmentNode)(nil))
typeHandleNode = reflect.TypeOf((*HandleNode)(nil))
typeSelectorNode = reflect.TypeOf((*SelectorNode)(nil))
typeTblSelectorNode = reflect.TypeOf((*TblSelectorNode)(nil))
typeTblColSelectorNode = reflect.TypeOf((*TblColSelectorNode)(nil))
typeColSelectorNode = reflect.TypeOf((*ColSelectorNode)(nil))
typeJoinNode = reflect.TypeOf((*JoinNode)(nil))
typeRowRangeNode = reflect.TypeOf((*RowRangeNode)(nil))
typeOrderByNode = reflect.TypeOf((*OrderByNode)(nil))
typeGroupByNode = reflect.TypeOf((*GroupByNode)(nil))
typeExprNode = reflect.TypeOf((*ExprNode)(nil))
typeFuncNode = reflect.TypeOf((*FuncNode)(nil))
)

View File

@ -1,5 +1,7 @@
package ast
import "github.com/neilotoole/sq/libsq/ast/internal/slq"
// OrderByNode implements the SQL "ORDER BY" clause.
type OrderByNode struct {
baseNode
@ -90,7 +92,7 @@ func (n *OrderByTermNode) SetChildren(children []Node) error {
case 0:
// fallthrough
case 1:
if _, ok := children[0].(selector); !ok {
if _, ok := children[0].(Selector); !ok {
return errorf("illegal child type %T {%s} for %T", children[0], children[0], n)
}
default:
@ -116,3 +118,45 @@ func (n *OrderByTermNode) Direction() OrderByDirection {
func (n *OrderByTermNode) String() string {
return nodeString(n)
}
// VisitOrderBy implements slq.SLQVisitor.
func (v *parseTreeVisitor) VisitOrderBy(ctx *slq.OrderByContext) interface{} {
node := &OrderByNode{}
node.parent = v.cur
node.ctx = ctx
node.text = ctx.GetText()
if err := v.cur.AddChild(node); err != nil {
return err
}
return v.using(node, func() any {
// This will result in VisitOrderByTerm being called on the children.
return v.VisitChildren(ctx)
})
}
// VisitOrderByTerm implements slq.SLQVisitor.
func (v *parseTreeVisitor) VisitOrderByTerm(ctx *slq.OrderByTermContext) interface{} {
node := &OrderByTermNode{}
node.parent = v.cur
node.ctx = ctx
node.text = ctx.GetText()
selNode, err := newSelectorNode(node, ctx.Selector())
if err != nil {
return nil
}
if ctx.ORDER_ASC() != nil {
node.direction = OrderByDirectionAsc
} else if ctx.ORDER_DESC() != nil {
node.direction = OrderByDirectionDesc
}
if err := node.AddChild(selNode); err != nil {
return err
}
return v.cur.AddChild(node)
}

View File

@ -150,12 +150,12 @@ func (v *parseTreeVisitor) Visit(ctx antlr.ParseTree) any {
return v.VisitHandleTable(ctx)
case *slq.SelectorContext:
return v.VisitSelector(ctx)
case *slq.FnElementContext:
return v.VisitFnElement(ctx)
case *slq.FnContext:
return v.VisitFn(ctx)
case *slq.FnNameContext:
return v.VisitFnName(ctx)
case *slq.FuncElementContext:
return v.VisitFuncElement(ctx)
case *slq.FuncContext:
return v.VisitFunc(ctx)
case *slq.FuncNameContext:
return v.VisitFuncName(ctx)
case *slq.JoinContext:
return v.VisitJoin(ctx)
case *slq.AliasContext:
@ -168,8 +168,10 @@ func (v *parseTreeVisitor) Visit(ctx antlr.ParseTree) any {
return v.VisitRowRange(ctx)
case *slq.ExprContext:
return v.VisitExpr(ctx)
case *slq.GroupContext:
return v.VisitGroup(ctx)
case *slq.GroupByContext:
return v.VisitGroupBy(ctx)
case *slq.GroupByTermContext:
return v.VisitGroupByTerm(ctx)
case *slq.OrderByContext:
return v.VisitOrderBy(ctx)
case *slq.OrderByTermContext:
@ -252,60 +254,18 @@ func (v *parseTreeVisitor) VisitSegment(ctx *slq.SegmentContext) any {
return v.VisitChildren(ctx)
}
// VisitOrderBy implements slq.SLQVisitor.
func (v *parseTreeVisitor) VisitOrderBy(ctx *slq.OrderByContext) interface{} {
node := &OrderByNode{}
node.parent = v.cur
node.ctx = ctx
node.text = ctx.GetText()
if err := v.cur.AddChild(node); err != nil {
return err
}
return v.using(node, func() any {
// This will result in VisitOrderByTerm being called on the children.
return v.VisitChildren(ctx)
})
}
// VisitOrderByTerm implements slq.SLQVisitor.
func (v *parseTreeVisitor) VisitOrderByTerm(ctx *slq.OrderByTermContext) interface{} {
node := &OrderByTermNode{}
node.parent = v.cur
node.ctx = ctx
node.text = ctx.GetText()
selNode, err := newSelectorNode(node, ctx.Selector())
if err != nil {
return nil
}
if ctx.ORDER_ASC() != nil {
node.direction = OrderByDirectionAsc
} else if ctx.ORDER_DESC() != nil {
node.direction = OrderByDirectionDesc
}
if err := node.AddChild(selNode); err != nil {
return err
}
return v.cur.AddChild(node)
}
// VisitSelectorElement implements slq.SLQVisitor.
func (v *parseTreeVisitor) VisitSelectorElement(ctx *slq.SelectorElementContext) any {
selNode, err := newSelectorNode(v.cur, ctx.Selector())
node, err := newSelectorNode(v.cur, ctx.Selector())
if err != nil {
return err
}
if aliasCtx := ctx.Alias(); aliasCtx != nil {
selNode.alias = ctx.Alias().ID().GetText()
node.alias = ctx.Alias().ID().GetText()
}
if err := v.cur.AddChild(selNode); err != nil {
if err := v.cur.AddChild(node); err != nil {
return err
}
@ -316,8 +276,12 @@ func (v *parseTreeVisitor) VisitSelectorElement(ctx *slq.SelectorElementContext)
// VisitSelector implements slq.SLQVisitor.
func (v *parseTreeVisitor) VisitSelector(ctx *slq.SelectorContext) any {
// no-op
return nil
node, err := newSelectorNode(v.cur, ctx)
if err != nil {
return err
}
return v.cur.AddChild(node)
}
// VisitElement implements slq.SLQVisitor.
@ -342,10 +306,8 @@ func (v *parseTreeVisitor) VisitAlias(ctx *slq.AliasContext) any {
return nil
}
// VisitFnElement implements slq.SLQVisitor.
func (v *parseTreeVisitor) VisitFnElement(ctx *slq.FnElementContext) any {
v.log.Debugf("visiting FnElement: %v", ctx.GetText())
// VisitFuncElement implements slq.SLQVisitor.
func (v *parseTreeVisitor) VisitFuncElement(ctx *slq.FuncElementContext) any {
childCount := ctx.GetChildCount()
if childCount == 0 || childCount > 2 {
return errorf("parser: invalid function: expected 1 or 2 children, but got %d: %v",
@ -354,12 +316,12 @@ func (v *parseTreeVisitor) VisitFnElement(ctx *slq.FnElementContext) any {
// e.g. count(*)
child1 := ctx.GetChild(0)
fnCtx, ok := child1.(*slq.FnContext)
fnCtx, ok := child1.(*slq.FuncContext)
if !ok {
return errorf("expected first child to be %T but was %T: %v", fnCtx, child1, ctx.GetText())
}
if err := v.VisitFn(fnCtx); err != nil {
if err := v.VisitFunc(fnCtx); err != nil {
return err
}
@ -386,11 +348,9 @@ func (v *parseTreeVisitor) VisitFnElement(ctx *slq.FnElementContext) any {
return nil
}
// VisitFn implements slq.SLQVisitor.
func (v *parseTreeVisitor) VisitFn(ctx *slq.FnContext) any {
v.log.Debugf("visiting Fn: %v", ctx.GetText())
fn := &FuncNode{fnName: ctx.FnName().GetText()}
// VisitFunc implements slq.SLQVisitor.
func (v *parseTreeVisitor) VisitFunc(ctx *slq.FuncContext) any {
fn := &FuncNode{fnName: ctx.FuncName().GetText()}
fn.ctx = ctx
err := fn.SetParent(v.cur)
if err != nil {
@ -454,13 +414,13 @@ func (v *parseTreeVisitor) VisitStmtList(ctx *slq.StmtListContext) any {
// VisitLiteral implements slq.SLQVisitor.
func (v *parseTreeVisitor) VisitLiteral(ctx *slq.LiteralContext) any {
v.log.Debugf("visiting literal: %q", ctx.GetText())
lit := &LiteralNode{}
lit.ctx = ctx
_ = lit.SetParent(v.cur)
err := v.cur.AddChild(lit)
return err
node := &LiteralNode{}
node.ctx = ctx
node.text = ctx.GetText()
if err := node.SetParent(v.cur); err != nil {
return err
}
return v.cur.AddChild(node)
}
// VisitUnaryOperator implements slq.SLQVisitor.
@ -468,42 +428,8 @@ func (v *parseTreeVisitor) VisitUnaryOperator(ctx *slq.UnaryOperatorContext) any
return nil
}
// VisitFnName implements slq.SLQVisitor.
func (v *parseTreeVisitor) VisitFnName(ctx *slq.FnNameContext) any {
return nil
}
// VisitGroup implements slq.SLQVisitor.
func (v *parseTreeVisitor) VisitGroup(ctx *slq.GroupContext) any {
// parent node must be a segment
seg, ok := v.cur.(*SegmentNode)
if !ok {
return errorf("parent of GROUP() must be %T, but got: %T", seg, v.cur)
}
sels := ctx.AllSelector()
if len(sels) == 0 {
return errorf("GROUP() requires at least one column selector argument")
}
grpNode := &GroupByNode{}
grpNode.ctx = ctx
grpNode.text = ctx.GetText()
if err := v.cur.AddChild(grpNode); err != nil {
return err
}
for _, selCtx := range sels {
colSel, err := newSelectorNode(grpNode, selCtx)
if err != nil {
return err
}
err = grpNode.AddChild(colSel)
if err != nil {
return err
}
}
// VisitFuncName implements slq.SLQVisitor.
func (v *parseTreeVisitor) VisitFuncName(ctx *slq.FuncNameContext) any {
return nil
}

View File

@ -44,7 +44,7 @@ func newSelectorNode(parent Node, ctx slq.ISelectorContext) (*SelectorNode, erro
var (
_ Node = (*SelectorNode)(nil)
_ selector = (*SelectorNode)(nil)
_ Selector = (*SelectorNode)(nil)
)
// SelectorNode is a selector such as ".my_table" or ".my_col". The
@ -85,8 +85,8 @@ func (s *SelectorNode) SelValue() (string, error) {
}
var (
_ Node = (*TblSelectorNode)(nil)
_ Selectable = (*TblSelectorNode)(nil)
_ Node = (*TblSelectorNode)(nil)
_ Tabler = (*TblSelectorNode)(nil)
)
// TblSelectorNode is a selector for a table, such as ".my_table"
@ -118,8 +118,8 @@ func (s *TblSelectorNode) Handle() string {
return s.handle
}
// Selectable implements the Selectable marker interface.
func (s *TblSelectorNode) selectable() {
// Tabler implements the Tabler marker interface.
func (s *TblSelectorNode) tabler() {
// no-op
}
@ -143,7 +143,7 @@ func (s *TblSelectorNode) String() string {
var (
_ Node = (*TblColSelectorNode)(nil)
_ ResultColumn = (*TblColSelectorNode)(nil)
_ selector = (*TblColSelectorNode)(nil)
_ Selector = (*TblColSelectorNode)(nil)
)
// TblColSelectorNode models the TABLE.COLUMN selector, e.g. actor.first_name.
@ -206,7 +206,7 @@ func (n *TblColSelectorNode) Alias() string {
var (
_ Node = (*ColSelectorNode)(nil)
_ ResultColumn = (*ColSelectorNode)(nil)
_ selector = (*ColSelectorNode)(nil)
_ Selector = (*ColSelectorNode)(nil)
)
// ColSelectorNode models a column selector such as ".first_name".

View File

@ -12,6 +12,8 @@ import (
"github.com/neilotoole/sq/libsq/core/errz"
)
const singleQuote = '\''
// baseOps is a map of SLQ operator (e.g. "==" or "!=") to its default SQL rendering.
var baseOps = map[string]string{
`==`: `=`,
@ -44,23 +46,36 @@ func (fb *BaseFragmentBuilder) GroupBy(gb *ast.GroupByNode) (string, error) {
return "", nil
}
clause := "GROUP BY "
children := gb.Children()
for i := 0; i < len(children); i++ {
var (
term string
err error
sb strings.Builder
)
sb.WriteString("GROUP BY ")
for i, child := range gb.Children() {
if i > 0 {
clause += ", "
sb.WriteString(", ")
}
// FIXME: really should check for other types
s, err := renderSelectorNode(fb.Quote, children[i])
if err != nil {
return "", err
switch child := child.(type) {
case *ast.FuncNode:
if term, err = fb.Function(child); err != nil {
return "", err
}
case ast.Selector:
if term, err = renderSelectorNode(fb.Quote, child); err != nil {
return "", err
}
default:
// Should never happen
return "", errz.Errorf("invalid child type: %T: %s", child, child)
}
clause += s
sb.WriteString(term)
}
return clause, nil
return sb.String(), nil
}
// OrderBy implements FragmentBuilder.
@ -167,7 +182,7 @@ func (fb *BaseFragmentBuilder) Function(fn *ast.FuncNode) (string, error) {
// HACK: this stuff basically doesn't work at all...
// but for COUNT(), here's a quick hack to make it work on some DBs
if fn.Context().GetText() == "count()" {
buf.WriteString("COUNT(*)")
buf.WriteString("count(*)")
} else {
buf.WriteString(fn.Context().GetText())
}
@ -175,23 +190,41 @@ func (fb *BaseFragmentBuilder) Function(fn *ast.FuncNode) (string, error) {
return buf.String(), nil
}
buf.WriteString(strings.ToUpper(fn.FuncName()))
buf.WriteString(strings.ToLower(fn.FuncName()))
buf.WriteRune('(')
for i, child := range children {
if i > 0 {
buf.WriteString(", ")
}
switch child := child.(type) {
case *ast.ColSelectorNode:
colName := child.ColName()
buf.WriteString(fb.Quote)
buf.WriteString(colName)
buf.WriteString(fb.Quote)
switch node := child.(type) {
case *ast.ColSelectorNode, *ast.TblColSelectorNode, *ast.TblSelectorNode:
s, err := renderSelectorNode(fb.Quote, node)
if err != nil {
return "", err
}
buf.WriteString(s)
case *ast.OperatorNode:
buf.WriteString(child.Text())
buf.WriteString(node.Text())
case *ast.LiteralNode:
// TODO: This is all a bit of a mess. We probably need to
// move to using bound parameters instead of inlining
// literal values.
val, wasQuoted, err := unquoteLiteral(node.Text())
if err != nil {
return "", err
}
if wasQuoted {
// The literal had quotes, so it's a regular string.
buf.WriteRune(singleQuote)
buf.WriteString(escapeLiteralString(val))
buf.WriteRune(singleQuote)
} else {
buf.WriteString(val)
}
default:
fb.Log.Debugf("unknown AST child node type %T", child)
return "", errz.Errorf("unknown AST child node %T: %s", node, node)
}
}
@ -200,6 +233,46 @@ func (fb *BaseFragmentBuilder) Function(fn *ast.FuncNode) (string, error) {
return sql, nil
}
// escapeLiteralString escapes the single quotes in s.
func escapeLiteralString(s string) string {
if !strings.ContainsRune(s, singleQuote) {
return s
}
sb := strings.Builder{}
for _, r := range s {
if r == singleQuote {
_, _ = sb.WriteRune(singleQuote)
_, _ = sb.WriteRune(singleQuote)
continue
}
_, _ = sb.WriteRune(r)
}
return sb.String()
}
// unquoteLiteral returns true if s is a "quoted" string, and also returns
// the value with the quotes stripped. An error is returned if the string
// is malformed.
func unquoteLiteral(s string) (val string, ok bool, err error) {
hasPrefix := strings.HasPrefix(s, `"`)
hasSuffix := strings.HasSuffix(s, `"`)
if hasPrefix && hasSuffix {
val = strings.TrimPrefix(s, `"`)
val = strings.TrimSuffix(val, `"`)
return val, true, nil
}
if hasPrefix != hasSuffix {
return "", false, errz.Errorf("malformed literal: %s", s)
}
return s, false, nil
}
// FromTable implements FragmentBuilder.
func (fb *BaseFragmentBuilder) FromTable(tblSel *ast.TblSelectorNode) (string, error) {
tblName, _ := tblSel.SelValue()

View File

@ -3,6 +3,8 @@ package sqlbuilder
import (
"testing"
"github.com/neilotoole/sq/testh/tutil"
"github.com/stretchr/testify/require"
)
@ -36,3 +38,26 @@ func TestQuoteTableOrColSelector(t *testing.T) {
})
}
}
func TestEscapeLiteralString(t *testing.T) {
testCases := []struct {
in string
want string
}{
{in: ``, want: ``},
{in: ` `, want: ` `},
{in: `hello`, want: `hello`},
{in: `"hello"`, want: `"hello"`},
{in: `there's`, want: `there''s`},
{in: `double''`, want: `double''''`},
}
for i, tc := range testCases {
tc := tc
t.Run(tutil.Name(i, tc.in), func(t *testing.T) {
got := escapeLiteralString(tc.in)
require.Equal(t, tc.want, got)
})
}
}

View File

@ -72,6 +72,11 @@ func (w *Walker) visitChildren(node Node) error {
return nil
}
// walkWith is a convenience function for using Walker.
func walkWith(log lg.Log, ast *AST, typ reflect.Type, fn nodeVisitorFn) error {
return NewWalker(log, ast).AddVisitor(typ, fn).Walk()
}
// narrowTblSel takes a generic selector, and if appropriate, converts it to a TblSel.
func narrowTblSel(log lg.Log, w *Walker, node Node) error {
// node is guaranteed to be typeSelectorNode

View File

@ -348,7 +348,7 @@ func execCopyTable(ctx context.Context, log lg.Log, fromDB driver.Database, from
// queryModel is a model of a SLQ query built from the AST.
type queryModel struct {
AST *ast.AST
Selectable ast.Selectable
Selectable ast.Tabler
Cols []ast.ResultColumn
Range *ast.RowRangeNode
Where *ast.WhereNode
@ -378,7 +378,7 @@ func buildQueryModel(log lg.Log, a *ast.AST) (*queryModel, error) {
len(selectableSeg.Children()))
}
selectable, ok := selectableSeg.Children()[0].(ast.Selectable)
selectable, ok := selectableSeg.Children()[0].(ast.Tabler)
if !ok {
return nil, errz.Errorf(
"the final selectable segment must have exactly one selectable element, but found element %T(%q)",

View File

@ -11,6 +11,8 @@ import (
"sync"
"testing"
"github.com/neilotoole/sq/libsq/ast"
"github.com/neilotoole/lg"
"github.com/neilotoole/lg/testlg"
"github.com/neilotoole/sq/cli/config"
@ -445,6 +447,34 @@ func (h *Helper) QuerySQL(src *source.Source, query string, args ...any) (*Recor
return sink, nil
}
// QuerySLQ executes the SLQ query.
func (h *Helper) QuerySLQ(query string) (*RecordSink, error) {
// We need to ensure that each of the handles in the query is loaded.
a, err := ast.Parse(h.Log, query)
require.NoError(h.T, err)
for _, handle := range ast.NewInspector(h.Log, a).FindHandles() {
// This triggers handle loading
_ = h.Source(handle)
}
srcs := h.srcs
dbases := h.Databases()
sink := &RecordSink{}
recw := output.NewRecordWriterAdapter(sink)
err = libsq.ExecuteSLQ(h.Context, h.Log, dbases, dbases, srcs, query, recw)
if err != nil {
return nil, err
}
if _, err = recw.Wait(); err != nil {
return nil, err
}
return sink, nil
}
// ExecSQL is a convenience wrapper for sql.DB.Exec that returns the
// rows affected, failing on any error. Note that ExecSQL uses the
// same Database instance as returned by h.Open.