mirror of
https://github.com/neilotoole/sq.git
synced 2024-12-29 03:01:59 +03:00
group_by() function (#163)
- `group_by()` now accepts function as argument. - Refactored grammar. - Broad improvements to function implementation.
This commit is contained in:
parent
0db02b533d
commit
fac3a27d7b
@ -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))
|
||||
})
|
||||
}
|
||||
|
@ -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
61
libsq/ast/groupby.go
Normal 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)
|
||||
}
|
@ -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
@ -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
@ -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
|
||||
|
@ -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) {}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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
@ -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{}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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))
|
||||
)
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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".
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)",
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user