Giter Club home page Giter Club logo

pg-mem's Introduction

pg-mem is an experimental in-memory emulation of a postgres database.

โค It works both in Node or in the browser.

โญ this repo if you like this package, it helps to motivate me :)

๐Ÿ‘‰ See it in action with pg-mem playground

๐Ÿ“ Usage

Using Node.js

As always, it starts with an:

npm i pg-mem --save

Then, assuming you're using something like webpack, if you're targeting a browser:

import { newDb } from "pg-mem";

const db = newDb();
db.public.many(/* put some sql here */);

Using Deno

Pretty straightforward :)

import { newDb } from "https://deno.land/x/pg_mem/mod.ts";

const db = newDb();
db.public.many(/* put some sql here */);

Only use the SQL syntax parser

โค Head to the pgsql-ast-parser repo

โš  Disclaimer

The sql syntax parser is home-made. Which means that some features are not implemented, and will be considered as invalid syntaxes.

This lib is quite new, so forgive it if some obvious pg syntax is not supported !

... And open an issue if you feel like a feature should be implemented :)

Moreover, even if I wrote hundreds of tests, keep in mind that this implementation is a best effort to replicate PG. Keep an eye on your query results if you perform complex queries. Please file issues if some results seem incoherent with what should be returned.

Finally, I invite you to read the below section to have an idea of you can or cannot do.

๐Ÿ” Features

Rollback to a previous state

pg-mem uses immutable data structures (here and here), which means that you can have restore points for free!

This is super useful if you intend to use pg-mem to mock your database for unit tests.

You could:

  1. Create your schema only once (which could be a heavy operation for a single unit test)
  2. Insert test data which will be shared by all test
  3. Create a restore point
  4. Run your tests with the same db instance, executing a backup.restore() before each test (which instantly resets db to the state it has after creating the restore point)

Usage:

const db = newDb();
db.public.none(`create table test(id text);
                insert into test values ('value');`);
// create a restore point & mess with data
const backup = db.backup();
db.public.none(`update test set id='new value';`);
// restore it !
backup.restore();
db.public.many(`select * from test`); // => {test: 'value'}

Custom functions

You can declare custom functions like this:

db.public.registerFunction({
  name: "say_hello",
  args: [DataType.text],
  returns: DataType.text,
  implementation: (x) => "hello " + x,
});

And then use them like in SQL select say_hello('world').

Custom functions support overloading and variadic arguments.

โš  However, the value you return is not type checked. It MUST correspond to the datatype you provided as 'returns' (it won't fail if not, but could lead to weird bugs).

Custom types

Not all pg types are implemented in pg-mem. That said, most of the types are often equivalent to other types, with a format validation. pg-mem provides a way to register such types.

For instance, lets say you'd like to register the MACADDR type, which is basically a string, with a format constraint.

You can register it like this:

db.public.registerEquivalentType({
  name: "macaddr",
  // which type is it equivalent to (will be able to cast it from it)
  equivalentTo: DataType.text,
  isValid(val: string) {
    // check that it will be this format
    return isValidMacAddress(val);
  },
});

Doing so, you'll be able to do things such as:

SELECT '08:00:2b:01:02:03:04:05'::macaddr; -- WORKS
SELECT 'invalid'::macaddr; -- will throw a conversion error

If you feel your implementation of a type matches the standard, and would like to include it in pg-mem for others to enjoy it, please consider filing a pull request ! (tip: see the INET type implementation as an example, and the pg_catalog index where supported types are registered)

Extensions

No native extension is implemented (pull requests are welcome), but you can define kind-of extensions like this:

db.registerExtension("my-ext", (schema) => {
  // install your ext in 'schema'
  // ex:  schema.registerFunction(...)
});

Statements like create extension "my-ext" will then be supported.

๐Ÿ“ƒ Libraries adapters

pg-mem provides handy shortcuts to create instances of popular libraries that will be bound to pg-mem instead of a real postgres db.

  • pg-native
  • node-postgres (pg)
  • pg-promise (pgp)
  • slonik
  • typeorm
  • knex
  • kysely
  • mikro-orm

See the wiki for more details

๐Ÿ’ฅ Inspection

Intercept queries

If you would like to hook your database, and return ad-hoc results, you can do so like this:

const db = newDb();

db.public.interceptQueries((sql) => {
  if (sql === "select * from whatever") {
    // intercept this statement, and return something custom:
    return [{ something: 42 }];
  }
  // proceed to actual SQL execution for other requests.
  return null;
});

Inspect a table

You can manually inspect a table content using the find() method:

for (const item of db.public.getTable<TItem>("mytable").find(itemTemplate)) {
  console.log(item);
}

Manually insert items

If you'd like to insert items manually into a table, you can do this like that:

db.public.getTable<TItem>('mytable').insert({ /* item to insert */ }))

Subscribe to events

You can subscribe to some events, like:

const db = newDb();

// called on each successful sql request
db.on("query", (sql) => {});
// called on each failed sql request
db.on("query-failed", (sql) => {});
// called on schema changes
db.on("schema-change", () => {});
// called when a CREATE EXTENSION schema is encountered.
db.on("create-extension", (ext) => {});

Experimental events

pg-mem implements a basic support for indices.

These handlers are called when a request cannot be optimized using one of the created indices.

However, a real postgres instance will be much smarter to optimize its requests... so when pg-mem says "this request does not use an index", dont take my word for it.

// called when a table is iterated entirely (ex: 'select * from data where notIndex=3' triggers it)
db.on('seq-scan', () => {});

// same, but on a specific table
db.getTable('myTable').on('seq-scan', () = {});

// will be called if pg-mem did not find any way to optimize a join
// (which leads to a O(n*m) lookup with the current implementation)
db.on('catastrophic-join-optimization', () => {});

๐Ÿ™‹โ€โ™‚๏ธ FAQ

Detailed answers in the wiki

โš ๏ธ Current limitations

  • Materialized views are implemented as views (meaning that they are always up-to-date, without needing them to refresh)
  • Indices implementations are basic
  • No support for timezones
  • All number-like types are all handled as javascript numbers, meaning that types like numeric(x,y) could not behave as expected.

๐Ÿœ Development

Pull requests are welcome :)

To start hacking this lib, you'll have to:

... once done, tests should appear. HMR is on, which means that changes in your code are instantly propagated to unit tests. This allows for ultra fast development cycles (running tests takes less than 1 sec).

To debug tests: Just hit "run" (F5, or whatever)... VS Code should attach the mocha worker. Then run the test you want to debug.

Alternatively, you could just run npm run test without installing anything, but this is a bit long.

pg-mem's People

Contributors

anfilat avatar anton-nagornyi avatar brahmlower avatar bramkaashoek avatar clemens-smartparking avatar crubier avatar cyrille-arundo avatar dependabot[bot] avatar engin33r avatar gthb avatar igalklebanov avatar imatveev avatar jonathannen avatar karlhorky avatar krumft avatar luixo avatar maxvandongen avatar nirweiner2 avatar oguimbal avatar onkarjanwa avatar rafaelgss avatar ranmocy avatar sangaman avatar secam avatar simoncockx avatar tankist avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pg-mem's Issues

Extension, and col_description errors

So I have 2 different errors.

One is related to CREATE EXTENSION IF when trying to use UUIDs:

Error: Syntax error at line 1 col 18:

        CREATE EXTENSION IF
                         ^
      Unexpected word token: "IF". I did not expect any more input. Here is the state of my parse table:

          kw_index โ†’ %word โ—

          at Parser.feed (/home/bobby/phorm-new/node_modules/nearley/lib/nearley.js:343:27)
          at _parse (/home/bobby/phorm-new/node_modules/pgsql-ast-parser/src/parser.ts:43:12)
          at Object.parse (/home/bobby/phorm-new/node_modules/pgsql-ast-parser/src/parser.ts:24:18)
          at Object.parseSql (/home/bobby/phorm-new/node_modules/pg-mem/src/parse-cache.ts:25:15)
          at Query.parse (/home/bobby/phorm-new/node_modules/pg-mem/src/query.ts:80:16)
          at Query.queries (/home/bobby/phorm-new/node_modules/pg-mem/src/query.ts:88:31)
          at queries.next (<anonymous>)
          at Query.query (/home/bobby/phorm-new/node_modules/pg-mem/src/query.ts:67:20)
          at /home/bobby/phorm-new/node_modules/pg-mem/src/adapters.ts:81:80
          at Timeout.task [as _onTimeout] (/home/bobby/phorm-new/node_modules/jsdom/lib/jsdom/browser/Window.js:391:19)
          at listOnTimeout (internal/timers.js:554:17)
          at processTimers (internal/timers.js:497:7)

The other seems to be the AST parser disliking column descriptions (or at least the function):

An insanely long AST error (I'm assuming)
Error: Syntax error at line 4 col 43:

                        pg_catalog.col_description(
                                                  ^
      Unexpected lparen token: "(". Instead, I was expecting to see one of the following:

      A op_cast token based on:
          expr_member โ†’ expr_member$subexpression$3 โ— %op_cast data_type
          expr_array_index โ†’  โ— expr_member
          expr_unary_add$macrocall$4 โ†’  โ— expr_array_index
          expr_unary_add$macrocall$1 โ†’  โ— expr_unary_add$macrocall$4
          expr_unary_add โ†’  โ— expr_unary_add$macrocall$1
          expr_exp$macrocall$4 โ†’  โ— expr_unary_add
          expr_exp$macrocall$1 โ†’  โ— expr_exp$macrocall$4
          expr_exp โ†’  โ— expr_exp$macrocall$1
          expr_mult$macrocall$4 โ†’  โ— expr_exp
          expr_mult$macrocall$1 โ†’  โ— expr_mult$macrocall$4
          expr_mult โ†’  โ— expr_mult$macrocall$1
          expr_add$macrocall$4 โ†’  โ— expr_mult
          expr_add$macrocall$1 โ†’  โ— expr_add$macrocall$4
          expr_add โ†’  โ— expr_add$macrocall$1
          expr_in$macrocall$4 โ†’  โ— expr_add
          expr_in$macrocall$1 โ†’  โ— expr_in$macrocall$4
          expr_in โ†’  โ— expr_in$macrocall$1
          expr_like$macrocall$4 โ†’  โ— expr_in
          expr_like$macrocall$1 โ†’  โ— expr_like$macrocall$4
          expr_like โ†’  โ— expr_like$macrocall$1
          expr_range$macrocall$5 โ†’  โ— expr_like
          expr_range$macrocall$1 โ†’  โ— expr_range$macrocall$5
          expr_range โ†’  โ— expr_range$macrocall$1
          expr_compare$macrocall$4 โ†’  โ— expr_range
          expr_compare$macrocall$1 โ†’  โ— expr_compare$macrocall$4
          expr_compare โ†’  โ— expr_compare$macrocall$1
          expr_is โ†’  โ— expr_compare
          expr_eq$macrocall$4 โ†’  โ— expr_is
          expr_eq$macrocall$1 โ†’  โ— expr_eq$macrocall$4
          expr_eq โ†’  โ— expr_eq$macrocall$1
          expr_not$macrocall$4 โ†’  โ— expr_eq
          expr_not$macrocall$1 โ†’  โ— expr_not$macrocall$4
          expr_not โ†’  โ— expr_not$macrocall$1
          expr_and$macrocall$4 โ†’  โ— expr_not
          expr_and$macrocall$1 โ†’  โ— expr_and$macrocall$4
          expr_and โ†’  โ— expr_and$macrocall$1
          expr_or$macrocall$4 โ†’  โ— expr_and
          expr_or$macrocall$1 โ†’  โ— expr_or$macrocall$4
          expr_or โ†’  โ— expr_or$macrocall$1
          expr_nostar โ†’  โ— expr_or
          expr โ†’  โ— expr_nostar
          select_expr_list_item โ†’  โ— expr select_expr_list_item$ebnf$1
          select_expr_list_aliased$ebnf$1$subexpression$1 โ†’ comma โ— select_expr_list_item
          select_expr_list_aliased$ebnf$1 โ†’ select_expr_list_aliased$ebnf$1 โ— select_expr_list_aliased$ebnf$1$subexpression$1
          select_expr_list_aliased โ†’ select_expr_list_item โ— select_expr_list_aliased$ebnf$1
          select_what โ†’ %kw_select โ— select_expr_list_aliased
          select_statement โ†’  โ— select_what select_statement$ebnf$1 select_statement$ebnf$2 select_statement$ebnf$3 select_statement$ebnf$4 select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A lbracket token based on:
          expr_array_index โ†’ expr_array_index$subexpression$1 โ— %lbracket expr_nostar %rbracket
          expr_unary_add$macrocall$4 โ†’  โ— expr_array_index
          expr_unary_add$macrocall$1 โ†’  โ— expr_unary_add$macrocall$4
          expr_unary_add โ†’  โ— expr_unary_add$macrocall$1
          expr_exp$macrocall$4 โ†’  โ— expr_unary_add
          expr_exp$macrocall$1 โ†’  โ— expr_exp$macrocall$4
          expr_exp โ†’  โ— expr_exp$macrocall$1
          expr_mult$macrocall$4 โ†’  โ— expr_exp
          expr_mult$macrocall$1 โ†’  โ— expr_mult$macrocall$4
          expr_mult โ†’  โ— expr_mult$macrocall$1
          expr_add$macrocall$4 โ†’  โ— expr_mult
          expr_add$macrocall$1 โ†’  โ— expr_add$macrocall$4
          expr_add โ†’  โ— expr_add$macrocall$1
          expr_in$macrocall$4 โ†’  โ— expr_add
          expr_in$macrocall$1 โ†’  โ— expr_in$macrocall$4
          expr_in โ†’  โ— expr_in$macrocall$1
          expr_like$macrocall$4 โ†’  โ— expr_in
          expr_like$macrocall$1 โ†’  โ— expr_like$macrocall$4
          expr_like โ†’  โ— expr_like$macrocall$1
          expr_range$macrocall$5 โ†’  โ— expr_like
          expr_range$macrocall$1 โ†’  โ— expr_range$macrocall$5
          expr_range โ†’  โ— expr_range$macrocall$1
          expr_compare$macrocall$4 โ†’  โ— expr_range
          expr_compare$macrocall$1 โ†’  โ— expr_compare$macrocall$4
          expr_compare โ†’  โ— expr_compare$macrocall$1
          expr_is โ†’  โ— expr_compare
          expr_eq$macrocall$4 โ†’  โ— expr_is
          expr_eq$macrocall$1 โ†’  โ— expr_eq$macrocall$4
          expr_eq โ†’  โ— expr_eq$macrocall$1
          expr_not$macrocall$4 โ†’  โ— expr_eq
          expr_not$macrocall$1 โ†’  โ— expr_not$macrocall$4
          expr_not โ†’  โ— expr_not$macrocall$1
          expr_and$macrocall$4 โ†’  โ— expr_not
          expr_and$macrocall$1 โ†’  โ— expr_and$macrocall$4
          expr_and โ†’  โ— expr_and$macrocall$1
          expr_or$macrocall$4 โ†’  โ— expr_and
          expr_or$macrocall$1 โ†’  โ— expr_or$macrocall$4
          expr_or โ†’  โ— expr_or$macrocall$1
          expr_nostar โ†’  โ— expr_or
          expr โ†’  โ— expr_nostar
          select_expr_list_item โ†’  โ— expr select_expr_list_item$ebnf$1
          select_expr_list_aliased$ebnf$1$subexpression$1 โ†’ comma โ— select_expr_list_item
          select_expr_list_aliased$ebnf$1 โ†’ select_expr_list_aliased$ebnf$1 โ— select_expr_list_aliased$ebnf$1$subexpression$1
          select_expr_list_aliased โ†’ select_expr_list_item โ— select_expr_list_aliased$ebnf$1
          select_what โ†’ %kw_select โ— select_expr_list_aliased
          select_statement โ†’  โ— select_what select_statement$ebnf$1 select_statement$ebnf$2 select_statement$ebnf$3 select_statement$ebnf$4 select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A op_member token based on:
          ops_member$subexpression$1 โ†’  โ— %op_member
          ops_member โ†’  โ— ops_member$subexpression$1
          expr_member โ†’ expr_member$subexpression$1 โ— ops_member expr_member$subexpression$2
          expr_array_index โ†’  โ— expr_member
          expr_unary_add$macrocall$4 โ†’  โ— expr_array_index
          expr_unary_add$macrocall$1 โ†’  โ— expr_unary_add$macrocall$4
          expr_unary_add โ†’  โ— expr_unary_add$macrocall$1
          expr_exp$macrocall$4 โ†’  โ— expr_unary_add
          expr_exp$macrocall$1 โ†’  โ— expr_exp$macrocall$4
          expr_exp โ†’  โ— expr_exp$macrocall$1
          expr_mult$macrocall$4 โ†’  โ— expr_exp
          expr_mult$macrocall$1 โ†’  โ— expr_mult$macrocall$4
          expr_mult โ†’  โ— expr_mult$macrocall$1
          expr_add$macrocall$4 โ†’  โ— expr_mult
          expr_add$macrocall$1 โ†’  โ— expr_add$macrocall$4
          expr_add โ†’  โ— expr_add$macrocall$1
          expr_in$macrocall$4 โ†’  โ— expr_add
          expr_in$macrocall$1 โ†’  โ— expr_in$macrocall$4
          expr_in โ†’  โ— expr_in$macrocall$1
          expr_like$macrocall$4 โ†’  โ— expr_in
          expr_like$macrocall$1 โ†’  โ— expr_like$macrocall$4
          expr_like โ†’  โ— expr_like$macrocall$1
          expr_range$macrocall$5 โ†’  โ— expr_like
          expr_range$macrocall$1 โ†’  โ— expr_range$macrocall$5
          expr_range โ†’  โ— expr_range$macrocall$1
          expr_compare$macrocall$4 โ†’  โ— expr_range
          expr_compare$macrocall$1 โ†’  โ— expr_compare$macrocall$4
          expr_compare โ†’  โ— expr_compare$macrocall$1
          expr_is โ†’  โ— expr_compare
          expr_eq$macrocall$4 โ†’  โ— expr_is
          expr_eq$macrocall$1 โ†’  โ— expr_eq$macrocall$4
          expr_eq โ†’  โ— expr_eq$macrocall$1
          expr_not$macrocall$4 โ†’  โ— expr_eq
          expr_not$macrocall$1 โ†’  โ— expr_not$macrocall$4
          expr_not โ†’  โ— expr_not$macrocall$1
          expr_and$macrocall$4 โ†’  โ— expr_not
          expr_and$macrocall$1 โ†’  โ— expr_and$macrocall$4
          expr_and โ†’  โ— expr_and$macrocall$1
          expr_or$macrocall$4 โ†’  โ— expr_and
          expr_or$macrocall$1 โ†’  โ— expr_or$macrocall$4
          expr_or โ†’  โ— expr_or$macrocall$1
          expr_nostar โ†’  โ— expr_or
          expr โ†’  โ— expr_nostar
          select_expr_list_item โ†’  โ— expr select_expr_list_item$ebnf$1
          select_expr_list_aliased$ebnf$1$subexpression$1 โ†’ comma โ— select_expr_list_item
          select_expr_list_aliased$ebnf$1 โ†’ select_expr_list_aliased$ebnf$1 โ— select_expr_list_aliased$ebnf$1$subexpression$1
          select_expr_list_aliased โ†’ select_expr_list_item โ— select_expr_list_aliased$ebnf$1
          select_what โ†’ %kw_select โ— select_expr_list_aliased
          select_statement โ†’  โ— select_what select_statement$ebnf$1 select_statement$ebnf$2 select_statement$ebnf$3 select_statement$ebnf$4 select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A op_membertext token based on:
          ops_member$subexpression$1 โ†’  โ— %op_membertext
          ops_member โ†’  โ— ops_member$subexpression$1
          expr_member โ†’ expr_member$subexpression$1 โ— ops_member expr_member$subexpression$2
          expr_array_index โ†’  โ— expr_member
          expr_unary_add$macrocall$4 โ†’  โ— expr_array_index
          expr_unary_add$macrocall$1 โ†’  โ— expr_unary_add$macrocall$4
          expr_unary_add โ†’  โ— expr_unary_add$macrocall$1
          expr_exp$macrocall$4 โ†’  โ— expr_unary_add
          expr_exp$macrocall$1 โ†’  โ— expr_exp$macrocall$4
          expr_exp โ†’  โ— expr_exp$macrocall$1
          expr_mult$macrocall$4 โ†’  โ— expr_exp
          expr_mult$macrocall$1 โ†’  โ— expr_mult$macrocall$4
          expr_mult โ†’  โ— expr_mult$macrocall$1
          expr_add$macrocall$4 โ†’  โ— expr_mult
          expr_add$macrocall$1 โ†’  โ— expr_add$macrocall$4
          expr_add โ†’  โ— expr_add$macrocall$1
          expr_in$macrocall$4 โ†’  โ— expr_add
          expr_in$macrocall$1 โ†’  โ— expr_in$macrocall$4
          expr_in โ†’  โ— expr_in$macrocall$1
          expr_like$macrocall$4 โ†’  โ— expr_in
          expr_like$macrocall$1 โ†’  โ— expr_like$macrocall$4
          expr_like โ†’  โ— expr_like$macrocall$1
          expr_range$macrocall$5 โ†’  โ— expr_like
          expr_range$macrocall$1 โ†’  โ— expr_range$macrocall$5
          expr_range โ†’  โ— expr_range$macrocall$1
          expr_compare$macrocall$4 โ†’  โ— expr_range
          expr_compare$macrocall$1 โ†’  โ— expr_compare$macrocall$4
          expr_compare โ†’  โ— expr_compare$macrocall$1
          expr_is โ†’  โ— expr_compare
          expr_eq$macrocall$4 โ†’  โ— expr_is
          expr_eq$macrocall$1 โ†’  โ— expr_eq$macrocall$4
          expr_eq โ†’  โ— expr_eq$macrocall$1
          expr_not$macrocall$4 โ†’  โ— expr_eq
          expr_not$macrocall$1 โ†’  โ— expr_not$macrocall$4
          expr_not โ†’  โ— expr_not$macrocall$1
          expr_and$macrocall$4 โ†’  โ— expr_not
          expr_and$macrocall$1 โ†’  โ— expr_and$macrocall$4
          expr_and โ†’  โ— expr_and$macrocall$1
          expr_or$macrocall$4 โ†’  โ— expr_and
          expr_or$macrocall$1 โ†’  โ— expr_or$macrocall$4
          expr_or โ†’  โ— expr_or$macrocall$1
          expr_nostar โ†’  โ— expr_or
          expr โ†’  โ— expr_nostar
          select_expr_list_item โ†’  โ— expr select_expr_list_item$ebnf$1
          select_expr_list_aliased$ebnf$1$subexpression$1 โ†’ comma โ— select_expr_list_item
          select_expr_list_aliased$ebnf$1 โ†’ select_expr_list_aliased$ebnf$1 โ— select_expr_list_aliased$ebnf$1$subexpression$1
          select_expr_list_aliased โ†’ select_expr_list_item โ— select_expr_list_aliased$ebnf$1
          select_what โ†’ %kw_select โ— select_expr_list_aliased
          select_statement โ†’  โ— select_what select_statement$ebnf$1 select_statement$ebnf$2 select_statement$ebnf$3 select_statement$ebnf$4 select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A op_exp token based on:
          expr_exp$macrocall$2 โ†’  โ— %op_exp
          expr_exp$macrocall$1 โ†’ expr_exp$macrocall$1$subexpression$1 โ— expr_exp$macrocall$2 expr_exp$macrocall$1$subexpression$2
          expr_exp โ†’  โ— expr_exp$macrocall$1
          expr_mult$macrocall$4 โ†’  โ— expr_exp
          expr_mult$macrocall$1 โ†’  โ— expr_mult$macrocall$4
          expr_mult โ†’  โ— expr_mult$macrocall$1
          expr_add$macrocall$4 โ†’  โ— expr_mult
          expr_add$macrocall$1 โ†’  โ— expr_add$macrocall$4
          expr_add โ†’  โ— expr_add$macrocall$1
          expr_in$macrocall$4 โ†’  โ— expr_add
          expr_in$macrocall$1 โ†’  โ— expr_in$macrocall$4
          expr_in โ†’  โ— expr_in$macrocall$1
          expr_like$macrocall$4 โ†’  โ— expr_in
          expr_like$macrocall$1 โ†’  โ— expr_like$macrocall$4
          expr_like โ†’  โ— expr_like$macrocall$1
          expr_range$macrocall$5 โ†’  โ— expr_like
          expr_range$macrocall$1 โ†’  โ— expr_range$macrocall$5
          expr_range โ†’  โ— expr_range$macrocall$1
          expr_compare$macrocall$4 โ†’  โ— expr_range
          expr_compare$macrocall$1 โ†’  โ— expr_compare$macrocall$4
          expr_compare โ†’  โ— expr_compare$macrocall$1
          expr_is โ†’  โ— expr_compare
          expr_eq$macrocall$4 โ†’  โ— expr_is
          expr_eq$macrocall$1 โ†’  โ— expr_eq$macrocall$4
          expr_eq โ†’  โ— expr_eq$macrocall$1
          expr_not$macrocall$4 โ†’  โ— expr_eq
          expr_not$macrocall$1 โ†’  โ— expr_not$macrocall$4
          expr_not โ†’  โ— expr_not$macrocall$1
          expr_and$macrocall$4 โ†’  โ— expr_not
          expr_and$macrocall$1 โ†’  โ— expr_and$macrocall$4
          expr_and โ†’  โ— expr_and$macrocall$1
          expr_or$macrocall$4 โ†’  โ— expr_and
          expr_or$macrocall$1 โ†’  โ— expr_or$macrocall$4
          expr_or โ†’  โ— expr_or$macrocall$1
          expr_nostar โ†’  โ— expr_or
          expr โ†’  โ— expr_nostar
          select_expr_list_item โ†’  โ— expr select_expr_list_item$ebnf$1
          select_expr_list_aliased$ebnf$1$subexpression$1 โ†’ comma โ— select_expr_list_item
          select_expr_list_aliased$ebnf$1 โ†’ select_expr_list_aliased$ebnf$1 โ— select_expr_list_aliased$ebnf$1$subexpression$1
          select_expr_list_aliased โ†’ select_expr_list_item โ— select_expr_list_aliased$ebnf$1
          select_what โ†’ %kw_select โ— select_expr_list_aliased
          select_statement โ†’  โ— select_what select_statement$ebnf$1 select_statement$ebnf$2 select_statement$ebnf$3 select_statement$ebnf$4 select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A star token based on:
          expr_mult$macrocall$2$subexpression$1 โ†’  โ— %star
          expr_mult$macrocall$2 โ†’  โ— expr_mult$macrocall$2$subexpression$1
          expr_mult$macrocall$1 โ†’ expr_mult$macrocall$1$subexpression$1 โ— expr_mult$macrocall$2 expr_mult$macrocall$1$subexpression$2
          expr_mult โ†’  โ— expr_mult$macrocall$1
          expr_add$macrocall$4 โ†’  โ— expr_mult
          expr_add$macrocall$1 โ†’  โ— expr_add$macrocall$4
          expr_add โ†’  โ— expr_add$macrocall$1
          expr_in$macrocall$4 โ†’  โ— expr_add
          expr_in$macrocall$1 โ†’  โ— expr_in$macrocall$4
          expr_in โ†’  โ— expr_in$macrocall$1
          expr_like$macrocall$4 โ†’  โ— expr_in
          expr_like$macrocall$1 โ†’  โ— expr_like$macrocall$4
          expr_like โ†’  โ— expr_like$macrocall$1
          expr_range$macrocall$5 โ†’  โ— expr_like
          expr_range$macrocall$1 โ†’  โ— expr_range$macrocall$5
          expr_range โ†’  โ— expr_range$macrocall$1
          expr_compare$macrocall$4 โ†’  โ— expr_range
          expr_compare$macrocall$1 โ†’  โ— expr_compare$macrocall$4
          expr_compare โ†’  โ— expr_compare$macrocall$1
          expr_is โ†’  โ— expr_compare
          expr_eq$macrocall$4 โ†’  โ— expr_is
          expr_eq$macrocall$1 โ†’  โ— expr_eq$macrocall$4
          expr_eq โ†’  โ— expr_eq$macrocall$1
          expr_not$macrocall$4 โ†’  โ— expr_eq
          expr_not$macrocall$1 โ†’  โ— expr_not$macrocall$4
          expr_not โ†’  โ— expr_not$macrocall$1
          expr_and$macrocall$4 โ†’  โ— expr_not
          expr_and$macrocall$1 โ†’  โ— expr_and$macrocall$4
          expr_and โ†’  โ— expr_and$macrocall$1
          expr_or$macrocall$4 โ†’  โ— expr_and
          expr_or$macrocall$1 โ†’  โ— expr_or$macrocall$4
          expr_or โ†’  โ— expr_or$macrocall$1
          expr_nostar โ†’  โ— expr_or
          expr โ†’  โ— expr_nostar
          select_expr_list_item โ†’  โ— expr select_expr_list_item$ebnf$1
          select_expr_list_aliased$ebnf$1$subexpression$1 โ†’ comma โ— select_expr_list_item
          select_expr_list_aliased$ebnf$1 โ†’ select_expr_list_aliased$ebnf$1 โ— select_expr_list_aliased$ebnf$1$subexpression$1
          select_expr_list_aliased โ†’ select_expr_list_item โ— select_expr_list_aliased$ebnf$1
          select_what โ†’ %kw_select โ— select_expr_list_aliased
          select_statement โ†’  โ— select_what select_statement$ebnf$1 select_statement$ebnf$2 select_statement$ebnf$3 select_statement$ebnf$4 select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A op_div token based on:
          expr_mult$macrocall$2$subexpression$1 โ†’  โ— %op_div
          expr_mult$macrocall$2 โ†’  โ— expr_mult$macrocall$2$subexpression$1
          expr_mult$macrocall$1 โ†’ expr_mult$macrocall$1$subexpression$1 โ— expr_mult$macrocall$2 expr_mult$macrocall$1$subexpression$2
          expr_mult โ†’  โ— expr_mult$macrocall$1
          expr_add$macrocall$4 โ†’  โ— expr_mult
          expr_add$macrocall$1 โ†’  โ— expr_add$macrocall$4
          expr_add โ†’  โ— expr_add$macrocall$1
          expr_in$macrocall$4 โ†’  โ— expr_add
          expr_in$macrocall$1 โ†’  โ— expr_in$macrocall$4
          expr_in โ†’  โ— expr_in$macrocall$1
          expr_like$macrocall$4 โ†’  โ— expr_in
          expr_like$macrocall$1 โ†’  โ— expr_like$macrocall$4
          expr_like โ†’  โ— expr_like$macrocall$1
          expr_range$macrocall$5 โ†’  โ— expr_like
          expr_range$macrocall$1 โ†’  โ— expr_range$macrocall$5
          expr_range โ†’  โ— expr_range$macrocall$1
          expr_compare$macrocall$4 โ†’  โ— expr_range
          expr_compare$macrocall$1 โ†’  โ— expr_compare$macrocall$4
          expr_compare โ†’  โ— expr_compare$macrocall$1
          expr_is โ†’  โ— expr_compare
          expr_eq$macrocall$4 โ†’  โ— expr_is
          expr_eq$macrocall$1 โ†’  โ— expr_eq$macrocall$4
          expr_eq โ†’  โ— expr_eq$macrocall$1
          expr_not$macrocall$4 โ†’  โ— expr_eq
          expr_not$macrocall$1 โ†’  โ— expr_not$macrocall$4
          expr_not โ†’  โ— expr_not$macrocall$1
          expr_and$macrocall$4 โ†’  โ— expr_not
          expr_and$macrocall$1 โ†’  โ— expr_and$macrocall$4
          expr_and โ†’  โ— expr_and$macrocall$1
          expr_or$macrocall$4 โ†’  โ— expr_and
          expr_or$macrocall$1 โ†’  โ— expr_or$macrocall$4
          expr_or โ†’  โ— expr_or$macrocall$1
          expr_nostar โ†’  โ— expr_or
          expr โ†’  โ— expr_nostar
          select_expr_list_item โ†’  โ— expr select_expr_list_item$ebnf$1
          select_expr_list_aliased$ebnf$1$subexpression$1 โ†’ comma โ— select_expr_list_item
          select_expr_list_aliased$ebnf$1 โ†’ select_expr_list_aliased$ebnf$1 โ— select_expr_list_aliased$ebnf$1$subexpression$1
          select_expr_list_aliased โ†’ select_expr_list_item โ— select_expr_list_aliased$ebnf$1
          select_what โ†’ %kw_select โ— select_expr_list_aliased
          select_statement โ†’  โ— select_what select_statement$ebnf$1 select_statement$ebnf$2 select_statement$ebnf$3 select_statement$ebnf$4 select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A op_mod token based on:
          expr_mult$macrocall$2$subexpression$1 โ†’  โ— %op_mod
          expr_mult$macrocall$2 โ†’  โ— expr_mult$macrocall$2$subexpression$1
          expr_mult$macrocall$1 โ†’ expr_mult$macrocall$1$subexpression$1 โ— expr_mult$macrocall$2 expr_mult$macrocall$1$subexpression$2
          expr_mult โ†’  โ— expr_mult$macrocall$1
          expr_add$macrocall$4 โ†’  โ— expr_mult
          expr_add$macrocall$1 โ†’  โ— expr_add$macrocall$4
          expr_add โ†’  โ— expr_add$macrocall$1
          expr_in$macrocall$4 โ†’  โ— expr_add
          expr_in$macrocall$1 โ†’  โ— expr_in$macrocall$4
          expr_in โ†’  โ— expr_in$macrocall$1
          expr_like$macrocall$4 โ†’  โ— expr_in
          expr_like$macrocall$1 โ†’  โ— expr_like$macrocall$4
          expr_like โ†’  โ— expr_like$macrocall$1
          expr_range$macrocall$5 โ†’  โ— expr_like
          expr_range$macrocall$1 โ†’  โ— expr_range$macrocall$5
          expr_range โ†’  โ— expr_range$macrocall$1
          expr_compare$macrocall$4 โ†’  โ— expr_range
          expr_compare$macrocall$1 โ†’  โ— expr_compare$macrocall$4
          expr_compare โ†’  โ— expr_compare$macrocall$1
          expr_is โ†’  โ— expr_compare
          expr_eq$macrocall$4 โ†’  โ— expr_is
          expr_eq$macrocall$1 โ†’  โ— expr_eq$macrocall$4
          expr_eq โ†’  โ— expr_eq$macrocall$1
          expr_not$macrocall$4 โ†’  โ— expr_eq
          expr_not$macrocall$1 โ†’  โ— expr_not$macrocall$4
          expr_not โ†’  โ— expr_not$macrocall$1
          expr_and$macrocall$4 โ†’  โ— expr_not
          expr_and$macrocall$1 โ†’  โ— expr_and$macrocall$4
          expr_and โ†’  โ— expr_and$macrocall$1
          expr_or$macrocall$4 โ†’  โ— expr_and
          expr_or$macrocall$1 โ†’  โ— expr_or$macrocall$4
          expr_or โ†’  โ— expr_or$macrocall$1
          expr_nostar โ†’  โ— expr_or
          expr โ†’  โ— expr_nostar
          select_expr_list_item โ†’  โ— expr select_expr_list_item$ebnf$1
          select_expr_list_aliased$ebnf$1$subexpression$1 โ†’ comma โ— select_expr_list_item
          select_expr_list_aliased$ebnf$1 โ†’ select_expr_list_aliased$ebnf$1 โ— select_expr_list_aliased$ebnf$1$subexpression$1
          select_expr_list_aliased โ†’ select_expr_list_item โ— select_expr_list_aliased$ebnf$1
          select_what โ†’ %kw_select โ— select_expr_list_aliased
          select_statement โ†’  โ— select_what select_statement$ebnf$1 select_statement$ebnf$2 select_statement$ebnf$3 select_statement$ebnf$4 select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A op_plus token based on:
          expr_add$macrocall$2$subexpression$1 โ†’  โ— %op_plus
          expr_add$macrocall$2 โ†’  โ— expr_add$macrocall$2$subexpression$1
          expr_add$macrocall$1 โ†’ expr_add$macrocall$1$subexpression$1 โ— expr_add$macrocall$2 expr_add$macrocall$1$subexpression$2
          expr_add โ†’  โ— expr_add$macrocall$1
          expr_in$macrocall$4 โ†’  โ— expr_add
          expr_in$macrocall$1 โ†’  โ— expr_in$macrocall$4
          expr_in โ†’  โ— expr_in$macrocall$1
          expr_like$macrocall$4 โ†’  โ— expr_in
          expr_like$macrocall$1 โ†’  โ— expr_like$macrocall$4
          expr_like โ†’  โ— expr_like$macrocall$1
          expr_range$macrocall$5 โ†’  โ— expr_like
          expr_range$macrocall$1 โ†’  โ— expr_range$macrocall$5
          expr_range โ†’  โ— expr_range$macrocall$1
          expr_compare$macrocall$4 โ†’  โ— expr_range
          expr_compare$macrocall$1 โ†’  โ— expr_compare$macrocall$4
          expr_compare โ†’  โ— expr_compare$macrocall$1
          expr_is โ†’  โ— expr_compare
          expr_eq$macrocall$4 โ†’  โ— expr_is
          expr_eq$macrocall$1 โ†’  โ— expr_eq$macrocall$4
          expr_eq โ†’  โ— expr_eq$macrocall$1
          expr_not$macrocall$4 โ†’  โ— expr_eq
          expr_not$macrocall$1 โ†’  โ— expr_not$macrocall$4
          expr_not โ†’  โ— expr_not$macrocall$1
          expr_and$macrocall$4 โ†’  โ— expr_not
          expr_and$macrocall$1 โ†’  โ— expr_and$macrocall$4
          expr_and โ†’  โ— expr_and$macrocall$1
          expr_or$macrocall$4 โ†’  โ— expr_and
          expr_or$macrocall$1 โ†’  โ— expr_or$macrocall$4
          expr_or โ†’  โ— expr_or$macrocall$1
          expr_nostar โ†’  โ— expr_or
          expr โ†’  โ— expr_nostar
          select_expr_list_item โ†’  โ— expr select_expr_list_item$ebnf$1
          select_expr_list_aliased$ebnf$1$subexpression$1 โ†’ comma โ— select_expr_list_item
          select_expr_list_aliased$ebnf$1 โ†’ select_expr_list_aliased$ebnf$1 โ— select_expr_list_aliased$ebnf$1$subexpression$1
          select_expr_list_aliased โ†’ select_expr_list_item โ— select_expr_list_aliased$ebnf$1
          select_what โ†’ %kw_select โ— select_expr_list_aliased
          select_statement โ†’  โ— select_what select_statement$ebnf$1 select_statement$ebnf$2 select_statement$ebnf$3 select_statement$ebnf$4 select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A op_minus token based on:
          expr_add$macrocall$2$subexpression$1 โ†’  โ— %op_minus
          expr_add$macrocall$2 โ†’  โ— expr_add$macrocall$2$subexpression$1
          expr_add$macrocall$1 โ†’ expr_add$macrocall$1$subexpression$1 โ— expr_add$macrocall$2 expr_add$macrocall$1$subexpression$2
          expr_add โ†’  โ— expr_add$macrocall$1
          expr_in$macrocall$4 โ†’  โ— expr_add
          expr_in$macrocall$1 โ†’  โ— expr_in$macrocall$4
          expr_in โ†’  โ— expr_in$macrocall$1
          expr_like$macrocall$4 โ†’  โ— expr_in
          expr_like$macrocall$1 โ†’  โ— expr_like$macrocall$4
          expr_like โ†’  โ— expr_like$macrocall$1
          expr_range$macrocall$5 โ†’  โ— expr_like
          expr_range$macrocall$1 โ†’  โ— expr_range$macrocall$5
          expr_range โ†’  โ— expr_range$macrocall$1
          expr_compare$macrocall$4 โ†’  โ— expr_range
          expr_compare$macrocall$1 โ†’  โ— expr_compare$macrocall$4
          expr_compare โ†’  โ— expr_compare$macrocall$1
          expr_is โ†’  โ— expr_compare
          expr_eq$macrocall$4 โ†’  โ— expr_is
          expr_eq$macrocall$1 โ†’  โ— expr_eq$macrocall$4
          expr_eq โ†’  โ— expr_eq$macrocall$1
          expr_not$macrocall$4 โ†’  โ— expr_eq
          expr_not$macrocall$1 โ†’  โ— expr_not$macrocall$4
          expr_not โ†’  โ— expr_not$macrocall$1
          expr_and$macrocall$4 โ†’  โ— expr_not
          expr_and$macrocall$1 โ†’  โ— expr_and$macrocall$4
          expr_and โ†’  โ— expr_and$macrocall$1
          expr_or$macrocall$4 โ†’  โ— expr_and
          expr_or$macrocall$1 โ†’  โ— expr_or$macrocall$4
          expr_or โ†’  โ— expr_or$macrocall$1
          expr_nostar โ†’  โ— expr_or
          expr โ†’  โ— expr_nostar
          select_expr_list_item โ†’  โ— expr select_expr_list_item$ebnf$1
          select_expr_list_aliased$ebnf$1$subexpression$1 โ†’ comma โ— select_expr_list_item
          select_expr_list_aliased$ebnf$1 โ†’ select_expr_list_aliased$ebnf$1 โ— select_expr_list_aliased$ebnf$1$subexpression$1
          select_expr_list_aliased โ†’ select_expr_list_item โ— select_expr_list_aliased$ebnf$1
          select_what โ†’ %kw_select โ— select_expr_list_aliased
          select_statement โ†’  โ— select_what select_statement$ebnf$1 select_statement$ebnf$2 select_statement$ebnf$3 select_statement$ebnf$4 select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A op_additive token based on:
          expr_add$macrocall$2$subexpression$1 โ†’  โ— %op_additive
          expr_add$macrocall$2 โ†’  โ— expr_add$macrocall$2$subexpression$1
          expr_add$macrocall$1 โ†’ expr_add$macrocall$1$subexpression$1 โ— expr_add$macrocall$2 expr_add$macrocall$1$subexpression$2
          expr_add โ†’  โ— expr_add$macrocall$1
          expr_in$macrocall$4 โ†’  โ— expr_add
          expr_in$macrocall$1 โ†’  โ— expr_in$macrocall$4
          expr_in โ†’  โ— expr_in$macrocall$1
          expr_like$macrocall$4 โ†’  โ— expr_in
          expr_like$macrocall$1 โ†’  โ— expr_like$macrocall$4
          expr_like โ†’  โ— expr_like$macrocall$1
          expr_range$macrocall$5 โ†’  โ— expr_like
          expr_range$macrocall$1 โ†’  โ— expr_range$macrocall$5
          expr_range โ†’  โ— expr_range$macrocall$1
          expr_compare$macrocall$4 โ†’  โ— expr_range
          expr_compare$macrocall$1 โ†’  โ— expr_compare$macrocall$4
          expr_compare โ†’  โ— expr_compare$macrocall$1
          expr_is โ†’  โ— expr_compare
          expr_eq$macrocall$4 โ†’  โ— expr_is
          expr_eq$macrocall$1 โ†’  โ— expr_eq$macrocall$4
          expr_eq โ†’  โ— expr_eq$macrocall$1
          expr_not$macrocall$4 โ†’  โ— expr_eq
          expr_not$macrocall$1 โ†’  โ— expr_not$macrocall$4
          expr_not โ†’  โ— expr_not$macrocall$1
          expr_and$macrocall$4 โ†’  โ— expr_not
          expr_and$macrocall$1 โ†’  โ— expr_and$macrocall$4
          expr_and โ†’  โ— expr_and$macrocall$1
          expr_or$macrocall$4 โ†’  โ— expr_and
          expr_or$macrocall$1 โ†’  โ— expr_or$macrocall$4
          expr_or โ†’  โ— expr_or$macrocall$1
          expr_nostar โ†’  โ— expr_or
          expr โ†’  โ— expr_nostar
          select_expr_list_item โ†’  โ— expr select_expr_list_item$ebnf$1
          select_expr_list_aliased$ebnf$1$subexpression$1 โ†’ comma โ— select_expr_list_item
          select_expr_list_aliased$ebnf$1 โ†’ select_expr_list_aliased$ebnf$1 โ— select_expr_list_aliased$ebnf$1$subexpression$1
          select_expr_list_aliased โ†’ select_expr_list_item โ— select_expr_list_aliased$ebnf$1
          select_what โ†’ %kw_select โ— select_expr_list_aliased
          select_statement โ†’  โ— select_what select_statement$ebnf$1 select_statement$ebnf$2 select_statement$ebnf$3 select_statement$ebnf$4 select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A kw_not token based on:
          ops_in$ebnf$1 โ†’  โ— %kw_not
          ops_in โ†’  โ— ops_in$ebnf$1 %kw_in
          expr_in$macrocall$2 โ†’  โ— ops_in
          expr_in$macrocall$1 โ†’ expr_in$macrocall$1$subexpression$1 โ— expr_in$macrocall$2 expr_in$macrocall$1$subexpression$2
          expr_in โ†’  โ— expr_in$macrocall$1
          expr_like$macrocall$4 โ†’  โ— expr_in
          expr_like$macrocall$1 โ†’  โ— expr_like$macrocall$4
          expr_like โ†’  โ— expr_like$macrocall$1
          expr_range$macrocall$5 โ†’  โ— expr_like
          expr_range$macrocall$1 โ†’  โ— expr_range$macrocall$5
          expr_range โ†’  โ— expr_range$macrocall$1
          expr_compare$macrocall$4 โ†’  โ— expr_range
          expr_compare$macrocall$1 โ†’  โ— expr_compare$macrocall$4
          expr_compare โ†’  โ— expr_compare$macrocall$1
          expr_is โ†’  โ— expr_compare
          expr_eq$macrocall$4 โ†’  โ— expr_is
          expr_eq$macrocall$1 โ†’  โ— expr_eq$macrocall$4
          expr_eq โ†’  โ— expr_eq$macrocall$1
          expr_not$macrocall$4 โ†’  โ— expr_eq
          expr_not$macrocall$1 โ†’  โ— expr_not$macrocall$4
          expr_not โ†’  โ— expr_not$macrocall$1
          expr_and$macrocall$4 โ†’  โ— expr_not
          expr_and$macrocall$1 โ†’  โ— expr_and$macrocall$4
          expr_and โ†’  โ— expr_and$macrocall$1
          expr_or$macrocall$4 โ†’  โ— expr_and
          expr_or$macrocall$1 โ†’  โ— expr_or$macrocall$4
          expr_or โ†’  โ— expr_or$macrocall$1
          expr_nostar โ†’  โ— expr_or
          expr โ†’  โ— expr_nostar
          select_expr_list_item โ†’  โ— expr select_expr_list_item$ebnf$1
          select_expr_list_aliased$ebnf$1$subexpression$1 โ†’ comma โ— select_expr_list_item
          select_expr_list_aliased$ebnf$1 โ†’ select_expr_list_aliased$ebnf$1 โ— select_expr_list_aliased$ebnf$1$subexpression$1
          select_expr_list_aliased โ†’ select_expr_list_item โ— select_expr_list_aliased$ebnf$1
          select_what โ†’ %kw_select โ— select_expr_list_aliased
          select_statement โ†’  โ— select_what select_statement$ebnf$1 select_statement$ebnf$2 select_statement$ebnf$3 select_statement$ebnf$4 select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A kw_in token based on:
          ops_in โ†’ ops_in$ebnf$1 โ— %kw_in
          expr_in$macrocall$2 โ†’  โ— ops_in
          expr_in$macrocall$1 โ†’ expr_in$macrocall$1$subexpression$1 โ— expr_in$macrocall$2 expr_in$macrocall$1$subexpression$2
          expr_in โ†’  โ— expr_in$macrocall$1
          expr_like$macrocall$4 โ†’  โ— expr_in
          expr_like$macrocall$1 โ†’  โ— expr_like$macrocall$4
          expr_like โ†’  โ— expr_like$macrocall$1
          expr_range$macrocall$5 โ†’  โ— expr_like
          expr_range$macrocall$1 โ†’  โ— expr_range$macrocall$5
          expr_range โ†’  โ— expr_range$macrocall$1
          expr_compare$macrocall$4 โ†’  โ— expr_range
          expr_compare$macrocall$1 โ†’  โ— expr_compare$macrocall$4
          expr_compare โ†’  โ— expr_compare$macrocall$1
          expr_is โ†’  โ— expr_compare
          expr_eq$macrocall$4 โ†’  โ— expr_is
          expr_eq$macrocall$1 โ†’  โ— expr_eq$macrocall$4
          expr_eq โ†’  โ— expr_eq$macrocall$1
          expr_not$macrocall$4 โ†’  โ— expr_eq
          expr_not$macrocall$1 โ†’  โ— expr_not$macrocall$4
          expr_not โ†’  โ— expr_not$macrocall$1
          expr_and$macrocall$4 โ†’  โ— expr_not
          expr_and$macrocall$1 โ†’  โ— expr_and$macrocall$4
          expr_and โ†’  โ— expr_and$macrocall$1
          expr_or$macrocall$4 โ†’  โ— expr_and
          expr_or$macrocall$1 โ†’  โ— expr_or$macrocall$4
          expr_or โ†’  โ— expr_or$macrocall$1
          expr_nostar โ†’  โ— expr_or
          expr โ†’  โ— expr_nostar
          select_expr_list_item โ†’  โ— expr select_expr_list_item$ebnf$1
          select_expr_list_aliased$ebnf$1$subexpression$1 โ†’ comma โ— select_expr_list_item
          select_expr_list_aliased$ebnf$1 โ†’ select_expr_list_aliased$ebnf$1 โ— select_expr_list_aliased$ebnf$1$subexpression$1
          select_expr_list_aliased โ†’ select_expr_list_item โ— select_expr_list_aliased$ebnf$1
          select_what โ†’ %kw_select โ— select_expr_list_aliased
          select_statement โ†’  โ— select_what select_statement$ebnf$1 select_statement$ebnf$2 select_statement$ebnf$3 select_statement$ebnf$4 select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A kw_not token based on:
          ops_like_keywors$ebnf$1 โ†’  โ— %kw_not
          ops_like_keywors โ†’  โ— ops_like_keywors$ebnf$1 ops_like_keywors$subexpression$1
          ops_like โ†’  โ— ops_like_keywors
          expr_like$macrocall$2 โ†’  โ— ops_like
          expr_like$macrocall$1 โ†’ expr_like$macrocall$1$subexpression$1 โ— expr_like$macrocall$2 expr_like$macrocall$1$subexpression$2
          expr_like โ†’  โ— expr_like$macrocall$1
          expr_range$macrocall$5 โ†’  โ— expr_like
          expr_range$macrocall$1 โ†’  โ— expr_range$macrocall$5
          expr_range โ†’  โ— expr_range$macrocall$1
          expr_compare$macrocall$4 โ†’  โ— expr_range
          expr_compare$macrocall$1 โ†’  โ— expr_compare$macrocall$4
          expr_compare โ†’  โ— expr_compare$macrocall$1
          expr_is โ†’  โ— expr_compare
          expr_eq$macrocall$4 โ†’  โ— expr_is
          expr_eq$macrocall$1 โ†’  โ— expr_eq$macrocall$4
          expr_eq โ†’  โ— expr_eq$macrocall$1
          expr_not$macrocall$4 โ†’  โ— expr_eq
          expr_not$macrocall$1 โ†’  โ— expr_not$macrocall$4
          expr_not โ†’  โ— expr_not$macrocall$1
          expr_and$macrocall$4 โ†’  โ— expr_not
          expr_and$macrocall$1 โ†’  โ— expr_and$macrocall$4
          expr_and โ†’  โ— expr_and$macrocall$1
          expr_or$macrocall$4 โ†’  โ— expr_and
          expr_or$macrocall$1 โ†’  โ— expr_or$macrocall$4
          expr_or โ†’  โ— expr_or$macrocall$1
          expr_nostar โ†’  โ— expr_or
          expr โ†’  โ— expr_nostar
          select_expr_list_item โ†’  โ— expr select_expr_list_item$ebnf$1
          select_expr_list_aliased$ebnf$1$subexpression$1 โ†’ comma โ— select_expr_list_item
          select_expr_list_aliased$ebnf$1 โ†’ select_expr_list_aliased$ebnf$1 โ— select_expr_list_aliased$ebnf$1$subexpression$1
          select_expr_list_aliased โ†’ select_expr_list_item โ— select_expr_list_aliased$ebnf$1
          select_what โ†’ %kw_select โ— select_expr_list_aliased
          select_statement โ†’  โ— select_what select_statement$ebnf$1 select_statement$ebnf$2 select_statement$ebnf$3 select_statement$ebnf$4 select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A op_like token based on:
          ops_like_operators$subexpression$1 โ†’  โ— %op_like
          ops_like_operators โ†’  โ— ops_like_operators$subexpression$1
          ops_like โ†’  โ— ops_like_operators
          expr_like$macrocall$2 โ†’  โ— ops_like
          expr_like$macrocall$1 โ†’ expr_like$macrocall$1$subexpression$1 โ— expr_like$macrocall$2 expr_like$macrocall$1$subexpression$2
          expr_like โ†’  โ— expr_like$macrocall$1
          expr_range$macrocall$5 โ†’  โ— expr_like
          expr_range$macrocall$1 โ†’  โ— expr_range$macrocall$5
          expr_range โ†’  โ— expr_range$macrocall$1
          expr_compare$macrocall$4 โ†’  โ— expr_range
          expr_compare$macrocall$1 โ†’  โ— expr_compare$macrocall$4
          expr_compare โ†’  โ— expr_compare$macrocall$1
          expr_is โ†’  โ— expr_compare
          expr_eq$macrocall$4 โ†’  โ— expr_is
          expr_eq$macrocall$1 โ†’  โ— expr_eq$macrocall$4
          expr_eq โ†’  โ— expr_eq$macrocall$1
          expr_not$macrocall$4 โ†’  โ— expr_eq
          expr_not$macrocall$1 โ†’  โ— expr_not$macrocall$4
          expr_not โ†’  โ— expr_not$macrocall$1
          expr_and$macrocall$4 โ†’  โ— expr_not
          expr_and$macrocall$1 โ†’  โ— expr_and$macrocall$4
          expr_and โ†’  โ— expr_and$macrocall$1
          expr_or$macrocall$4 โ†’  โ— expr_and
          expr_or$macrocall$1 โ†’  โ— expr_or$macrocall$4
          expr_or โ†’  โ— expr_or$macrocall$1
          expr_nostar โ†’  โ— expr_or
          expr โ†’  โ— expr_nostar
          select_expr_list_item โ†’  โ— expr select_expr_list_item$ebnf$1
          select_expr_list_aliased$ebnf$1$subexpression$1 โ†’ comma โ— select_expr_list_item
          select_expr_list_aliased$ebnf$1 โ†’ select_expr_list_aliased$ebnf$1 โ— select_expr_list_aliased$ebnf$1$subexpression$1
          select_expr_list_aliased โ†’ select_expr_list_item โ— select_expr_list_aliased$ebnf$1
          select_what โ†’ %kw_select โ— select_expr_list_aliased
          select_statement โ†’  โ— select_what select_statement$ebnf$1 select_statement$ebnf$2 select_statement$ebnf$3 select_statement$ebnf$4 select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A op_ilike token based on:
          ops_like_operators$subexpression$2 โ†’  โ— %op_ilike
          ops_like_operators โ†’  โ— ops_like_operators$subexpression$2
          ops_like โ†’  โ— ops_like_operators
          expr_like$macrocall$2 โ†’  โ— ops_like
          expr_like$macrocall$1 โ†’ expr_like$macrocall$1$subexpression$1 โ— expr_like$macrocall$2 expr_like$macrocall$1$subexpression$2
          expr_like โ†’  โ— expr_like$macrocall$1
          expr_range$macrocall$5 โ†’  โ— expr_like
          expr_range$macrocall$1 โ†’  โ— expr_range$macrocall$5
          expr_range โ†’  โ— expr_range$macrocall$1
          expr_compare$macrocall$4 โ†’  โ— expr_range
          expr_compare$macrocall$1 โ†’  โ— expr_compare$macrocall$4
          expr_compare โ†’  โ— expr_compare$macrocall$1
          expr_is โ†’  โ— expr_compare
          expr_eq$macrocall$4 โ†’  โ— expr_is
          expr_eq$macrocall$1 โ†’  โ— expr_eq$macrocall$4
          expr_eq โ†’  โ— expr_eq$macrocall$1
          expr_not$macrocall$4 โ†’  โ— expr_eq
          expr_not$macrocall$1 โ†’  โ— expr_not$macrocall$4
          expr_not โ†’  โ— expr_not$macrocall$1
          expr_and$macrocall$4 โ†’  โ— expr_not
          expr_and$macrocall$1 โ†’  โ— expr_and$macrocall$4
          expr_and โ†’  โ— expr_and$macrocall$1
          expr_or$macrocall$4 โ†’  โ— expr_and
          expr_or$macrocall$1 โ†’  โ— expr_or$macrocall$4
          expr_or โ†’  โ— expr_or$macrocall$1
          expr_nostar โ†’  โ— expr_or
          expr โ†’  โ— expr_nostar
          select_expr_list_item โ†’  โ— expr select_expr_list_item$ebnf$1
          select_expr_list_aliased$ebnf$1$subexpression$1 โ†’ comma โ— select_expr_list_item
          select_expr_list_aliased$ebnf$1 โ†’ select_expr_list_aliased$ebnf$1 โ— select_expr_list_aliased$ebnf$1$subexpression$1
          select_expr_list_aliased โ†’ select_expr_list_item โ— select_expr_list_aliased$ebnf$1
          select_what โ†’ %kw_select โ— select_expr_list_aliased
          select_statement โ†’  โ— select_what select_statement$ebnf$1 select_statement$ebnf$2 select_statement$ebnf$3 select_statement$ebnf$4 select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A op_not_like token based on:
          ops_like_operators$subexpression$3 โ†’  โ— %op_not_like
          ops_like_operators โ†’  โ— ops_like_operators$subexpression$3
          ops_like โ†’  โ— ops_like_operators
          expr_like$macrocall$2 โ†’  โ— ops_like
          expr_like$macrocall$1 โ†’ expr_like$macrocall$1$subexpression$1 โ— expr_like$macrocall$2 expr_like$macrocall$1$subexpression$2
          expr_like โ†’  โ— expr_like$macrocall$1
          expr_range$macrocall$5 โ†’  โ— expr_like
          expr_range$macrocall$1 โ†’  โ— expr_range$macrocall$5
          expr_range โ†’  โ— expr_range$macrocall$1
          expr_compare$macrocall$4 โ†’  โ— expr_range
          expr_compare$macrocall$1 โ†’  โ— expr_compare$macrocall$4
          expr_compare โ†’  โ— expr_compare$macrocall$1
          expr_is โ†’  โ— expr_compare
          expr_eq$macrocall$4 โ†’  โ— expr_is
          expr_eq$macrocall$1 โ†’  โ— expr_eq$macrocall$4
          expr_eq โ†’  โ— expr_eq$macrocall$1
          expr_not$macrocall$4 โ†’  โ— expr_eq
          expr_not$macrocall$1 โ†’  โ— expr_not$macrocall$4
          expr_not โ†’  โ— expr_not$macrocall$1
          expr_and$macrocall$4 โ†’  โ— expr_not
          expr_and$macrocall$1 โ†’  โ— expr_and$macrocall$4
          expr_and โ†’  โ— expr_and$macrocall$1
          expr_or$macrocall$4 โ†’  โ— expr_and
          expr_or$macrocall$1 โ†’  โ— expr_or$macrocall$4
          expr_or โ†’  โ— expr_or$macrocall$1
          expr_nostar โ†’  โ— expr_or
          expr โ†’  โ— expr_nostar
          select_expr_list_item โ†’  โ— expr select_expr_list_item$ebnf$1
          select_expr_list_aliased$ebnf$1$subexpression$1 โ†’ comma โ— select_expr_list_item
          select_expr_list_aliased$ebnf$1 โ†’ select_expr_list_aliased$ebnf$1 โ— select_expr_list_aliased$ebnf$1$subexpression$1
          select_expr_list_aliased โ†’ select_expr_list_item โ— select_expr_list_aliased$ebnf$1
          select_what โ†’ %kw_select โ— select_expr_list_aliased
          select_statement โ†’  โ— select_what select_statement$ebnf$1 select_statement$ebnf$2 select_statement$ebnf$3 select_statement$ebnf$4 select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A op_not_ilike token based on:
          ops_like_operators$subexpression$4 โ†’  โ— %op_not_ilike
          ops_like_operators โ†’  โ— ops_like_operators$subexpression$4
          ops_like โ†’  โ— ops_like_operators
          expr_like$macrocall$2 โ†’  โ— ops_like
          expr_like$macrocall$1 โ†’ expr_like$macrocall$1$subexpression$1 โ— expr_like$macrocall$2 expr_like$macrocall$1$subexpression$2
          expr_like โ†’  โ— expr_like$macrocall$1
          expr_range$macrocall$5 โ†’  โ— expr_like
          expr_range$macrocall$1 โ†’  โ— expr_range$macrocall$5
          expr_range โ†’  โ— expr_range$macrocall$1
          expr_compare$macrocall$4 โ†’  โ— expr_range
          expr_compare$macrocall$1 โ†’  โ— expr_compare$macrocall$4
          expr_compare โ†’  โ— expr_compare$macrocall$1
          expr_is โ†’  โ— expr_compare
          expr_eq$macrocall$4 โ†’  โ— expr_is
          expr_eq$macrocall$1 โ†’  โ— expr_eq$macrocall$4
          expr_eq โ†’  โ— expr_eq$macrocall$1
          expr_not$macrocall$4 โ†’  โ— expr_eq
          expr_not$macrocall$1 โ†’  โ— expr_not$macrocall$4
          expr_not โ†’  โ— expr_not$macrocall$1
          expr_and$macrocall$4 โ†’  โ— expr_not
          expr_and$macrocall$1 โ†’  โ— expr_and$macrocall$4
          expr_and โ†’  โ— expr_and$macrocall$1
          expr_or$macrocall$4 โ†’  โ— expr_and
          expr_or$macrocall$1 โ†’  โ— expr_or$macrocall$4
          expr_or โ†’  โ— expr_or$macrocall$1
          expr_nostar โ†’  โ— expr_or
          expr โ†’  โ— expr_nostar
          select_expr_list_item โ†’  โ— expr select_expr_list_item$ebnf$1
          select_expr_list_aliased$ebnf$1$subexpression$1 โ†’ comma โ— select_expr_list_item
          select_expr_list_aliased$ebnf$1 โ†’ select_expr_list_aliased$ebnf$1 โ— select_expr_list_aliased$ebnf$1$subexpression$1
          select_expr_list_aliased โ†’ select_expr_list_item โ— select_expr_list_aliased$ebnf$1
          select_what โ†’ %kw_select โ— select_expr_list_aliased
          select_statement โ†’  โ— select_what select_statement$ebnf$1 select_statement$ebnf$2 select_statement$ebnf$3 select_statement$ebnf$4 select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A kw_like token based on:
          ops_like_keywors$subexpression$1 โ†’  โ— %kw_like
          ops_like_keywors โ†’ ops_like_keywors$ebnf$1 โ— ops_like_keywors$subexpression$1
          ops_like โ†’  โ— ops_like_keywors
          expr_like$macrocall$2 โ†’  โ— ops_like
          expr_like$macrocall$1 โ†’ expr_like$macrocall$1$subexpression$1 โ— expr_like$macrocall$2 expr_like$macrocall$1$subexpression$2
          expr_like โ†’  โ— expr_like$macrocall$1
          expr_range$macrocall$5 โ†’  โ— expr_like
          expr_range$macrocall$1 โ†’  โ— expr_range$macrocall$5
          expr_range โ†’  โ— expr_range$macrocall$1
          expr_compare$macrocall$4 โ†’  โ— expr_range
          expr_compare$macrocall$1 โ†’  โ— expr_compare$macrocall$4
          expr_compare โ†’  โ— expr_compare$macrocall$1
          expr_is โ†’  โ— expr_compare
          expr_eq$macrocall$4 โ†’  โ— expr_is
          expr_eq$macrocall$1 โ†’  โ— expr_eq$macrocall$4
          expr_eq โ†’  โ— expr_eq$macrocall$1
          expr_not$macrocall$4 โ†’  โ— expr_eq
          expr_not$macrocall$1 โ†’  โ— expr_not$macrocall$4
          expr_not โ†’  โ— expr_not$macrocall$1
          expr_and$macrocall$4 โ†’  โ— expr_not
          expr_and$macrocall$1 โ†’  โ— expr_and$macrocall$4
          expr_and โ†’  โ— expr_and$macrocall$1
          expr_or$macrocall$4 โ†’  โ— expr_and
          expr_or$macrocall$1 โ†’  โ— expr_or$macrocall$4
          expr_or โ†’  โ— expr_or$macrocall$1
          expr_nostar โ†’  โ— expr_or
          expr โ†’  โ— expr_nostar
          select_expr_list_item โ†’  โ— expr select_expr_list_item$ebnf$1
          select_expr_list_aliased$ebnf$1$subexpression$1 โ†’ comma โ— select_expr_list_item
          select_expr_list_aliased$ebnf$1 โ†’ select_expr_list_aliased$ebnf$1 โ— select_expr_list_aliased$ebnf$1$subexpression$1
          select_expr_list_aliased โ†’ select_expr_list_item โ— select_expr_list_aliased$ebnf$1
          select_what โ†’ %kw_select โ— select_expr_list_aliased
          select_statement โ†’  โ— select_what select_statement$ebnf$1 select_statement$ebnf$2 select_statement$ebnf$3 select_statement$ebnf$4 select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A kw_ilike token based on:
          ops_like_keywors$subexpression$1 โ†’  โ— %kw_ilike
          ops_like_keywors โ†’ ops_like_keywors$ebnf$1 โ— ops_like_keywors$subexpression$1
          ops_like โ†’  โ— ops_like_keywors
          expr_like$macrocall$2 โ†’  โ— ops_like
          expr_like$macrocall$1 โ†’ expr_like$macrocall$1$subexpression$1 โ— expr_like$macrocall$2 expr_like$macrocall$1$subexpression$2
          expr_like โ†’  โ— expr_like$macrocall$1
          expr_range$macrocall$5 โ†’  โ— expr_like
          expr_range$macrocall$1 โ†’  โ— expr_range$macrocall$5
          expr_range โ†’  โ— expr_range$macrocall$1
          expr_compare$macrocall$4 โ†’  โ— expr_range
          expr_compare$macrocall$1 โ†’  โ— expr_compare$macrocall$4
          expr_compare โ†’  โ— expr_compare$macrocall$1
          expr_is โ†’  โ— expr_compare
          expr_eq$macrocall$4 โ†’  โ— expr_is
          expr_eq$macrocall$1 โ†’  โ— expr_eq$macrocall$4
          expr_eq โ†’  โ— expr_eq$macrocall$1
          expr_not$macrocall$4 โ†’  โ— expr_eq
          expr_not$macrocall$1 โ†’  โ— expr_not$macrocall$4
          expr_not โ†’  โ— expr_not$macrocall$1
          expr_and$macrocall$4 โ†’  โ— expr_not
          expr_and$macrocall$1 โ†’  โ— expr_and$macrocall$4
          expr_and โ†’  โ— expr_and$macrocall$1
          expr_or$macrocall$4 โ†’  โ— expr_and
          expr_or$macrocall$1 โ†’  โ— expr_or$macrocall$4
          expr_or โ†’  โ— expr_or$macrocall$1
          expr_nostar โ†’  โ— expr_or
          expr โ†’  โ— expr_nostar
          select_expr_list_item โ†’  โ— expr select_expr_list_item$ebnf$1
          select_expr_list_aliased$ebnf$1$subexpression$1 โ†’ comma โ— select_expr_list_item
          select_expr_list_aliased$ebnf$1 โ†’ select_expr_list_aliased$ebnf$1 โ— select_expr_list_aliased$ebnf$1$subexpression$1
          select_expr_list_aliased โ†’ select_expr_list_item โ— select_expr_list_aliased$ebnf$1
          select_what โ†’ %kw_select โ— select_expr_list_aliased
          select_statement โ†’  โ— select_what select_statement$ebnf$1 select_statement$ebnf$2 select_statement$ebnf$3 select_statement$ebnf$4 select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A kw_not token based on:
          ops_between$ebnf$1 โ†’  โ— %kw_not
          ops_between โ†’  โ— ops_between$ebnf$1 kw_between
          expr_range$macrocall$2 โ†’  โ— ops_between
          expr_range$macrocall$1 โ†’ expr_range$macrocall$1$subexpression$1 โ— expr_range$macrocall$2 expr_range$macrocall$1$subexpression$2 expr_range$macrocall$3 expr_range$macrocall$1$subexpression$3
          expr_range โ†’  โ— expr_range$macrocall$1
          expr_compare$macrocall$4 โ†’  โ— expr_range
          expr_compare$macrocall$1 โ†’  โ— expr_compare$macrocall$4
          expr_compare โ†’  โ— expr_compare$macrocall$1
          expr_is โ†’  โ— expr_compare
          expr_eq$macrocall$4 โ†’  โ— expr_is
          expr_eq$macrocall$1 โ†’  โ— expr_eq$macrocall$4
          expr_eq โ†’  โ— expr_eq$macrocall$1
          expr_not$macrocall$4 โ†’  โ— expr_eq
          expr_not$macrocall$1 โ†’  โ— expr_not$macrocall$4
          expr_not โ†’  โ— expr_not$macrocall$1
          expr_and$macrocall$4 โ†’  โ— expr_not
          expr_and$macrocall$1 โ†’  โ— expr_and$macrocall$4
          expr_and โ†’  โ— expr_and$macrocall$1
          expr_or$macrocall$4 โ†’  โ— expr_and
          expr_or$macrocall$1 โ†’  โ— expr_or$macrocall$4
          expr_or โ†’  โ— expr_or$macrocall$1
          expr_nostar โ†’  โ— expr_or
          expr โ†’  โ— expr_nostar
          select_expr_list_item โ†’  โ— expr select_expr_list_item$ebnf$1
          select_expr_list_aliased$ebnf$1$subexpression$1 โ†’ comma โ— select_expr_list_item
          select_expr_list_aliased$ebnf$1 โ†’ select_expr_list_aliased$ebnf$1 โ— select_expr_list_aliased$ebnf$1$subexpression$1
          select_expr_list_aliased โ†’ select_expr_list_item โ— select_expr_list_aliased$ebnf$1
          select_what โ†’ %kw_select โ— select_expr_list_aliased
          select_statement โ†’  โ— select_what select_statement$ebnf$1 select_statement$ebnf$2 select_statement$ebnf$3 select_statement$ebnf$4 select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A kw_is token based on:
          expr_is โ†’ expr_is$subexpression$5 โ— %kw_is expr_is$ebnf$1 expr_is$subexpression$6
          expr_eq$macrocall$4 โ†’  โ— expr_is
          expr_eq$macrocall$1 โ†’  โ— expr_eq$macrocall$4
          expr_eq โ†’  โ— expr_eq$macrocall$1
          expr_not$macrocall$4 โ†’  โ— expr_eq
          expr_not$macrocall$1 โ†’  โ— expr_not$macrocall$4
          expr_not โ†’  โ— expr_not$macrocall$1
          expr_and$macrocall$4 โ†’  โ— expr_not
          expr_and$macrocall$1 โ†’  โ— expr_and$macrocall$4
          expr_and โ†’  โ— expr_and$macrocall$1
          expr_or$macrocall$4 โ†’  โ— expr_and
          expr_or$macrocall$1 โ†’  โ— expr_or$macrocall$4
          expr_or โ†’  โ— expr_or$macrocall$1
          expr_nostar โ†’  โ— expr_or
          expr โ†’  โ— expr_nostar
          select_expr_list_item โ†’  โ— expr select_expr_list_item$ebnf$1
          select_expr_list_aliased$ebnf$1$subexpression$1 โ†’ comma โ— select_expr_list_item
          select_expr_list_aliased$ebnf$1 โ†’ select_expr_list_aliased$ebnf$1 โ— select_expr_list_aliased$ebnf$1$subexpression$1
          select_expr_list_aliased โ†’ select_expr_list_item โ— select_expr_list_aliased$ebnf$1
          select_what โ†’ %kw_select โ— select_expr_list_aliased
          select_statement โ†’  โ— select_what select_statement$ebnf$1 select_statement$ebnf$2 select_statement$ebnf$3 select_statement$ebnf$4 select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A op_compare token based on:
          expr_compare$macrocall$2 โ†’  โ— %op_compare
          expr_compare$macrocall$1 โ†’ expr_compare$macrocall$1$subexpression$1 โ— expr_compare$macrocall$2 expr_compare$macrocall$1$subexpression$2
          expr_compare โ†’  โ— expr_compare$macrocall$1
          expr_is โ†’  โ— expr_compare
          expr_eq$macrocall$4 โ†’  โ— expr_is
          expr_eq$macrocall$1 โ†’  โ— expr_eq$macrocall$4
          expr_eq โ†’  โ— expr_eq$macrocall$1
          expr_not$macrocall$4 โ†’  โ— expr_eq
          expr_not$macrocall$1 โ†’  โ— expr_not$macrocall$4
          expr_not โ†’  โ— expr_not$macrocall$1
          expr_and$macrocall$4 โ†’  โ— expr_not
          expr_and$macrocall$1 โ†’  โ— expr_and$macrocall$4
          expr_and โ†’  โ— expr_and$macrocall$1
          expr_or$macrocall$4 โ†’  โ— expr_and
          expr_or$macrocall$1 โ†’  โ— expr_or$macrocall$4
          expr_or โ†’  โ— expr_or$macrocall$1
          expr_nostar โ†’  โ— expr_or
          expr โ†’  โ— expr_nostar
          select_expr_list_item โ†’  โ— expr select_expr_list_item$ebnf$1
          select_expr_list_aliased$ebnf$1$subexpression$1 โ†’ comma โ— select_expr_list_item
          select_expr_list_aliased$ebnf$1 โ†’ select_expr_list_aliased$ebnf$1 โ— select_expr_list_aliased$ebnf$1$subexpression$1
          select_expr_list_aliased โ†’ select_expr_list_item โ— select_expr_list_aliased$ebnf$1
          select_what โ†’ %kw_select โ— select_expr_list_aliased
          select_statement โ†’  โ— select_what select_statement$ebnf$1 select_statement$ebnf$2 select_statement$ebnf$3 select_statement$ebnf$4 select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A kw_notnull token based on:
          expr_is$subexpression$4 โ†’  โ— %kw_notnull
          expr_is โ†’ expr_is$subexpression$3 โ— expr_is$subexpression$4
          expr_eq$macrocall$4 โ†’  โ— expr_is
          expr_eq$macrocall$1 โ†’  โ— expr_eq$macrocall$4
          expr_eq โ†’  โ— expr_eq$macrocall$1
          expr_not$macrocall$4 โ†’  โ— expr_eq
          expr_not$macrocall$1 โ†’  โ— expr_not$macrocall$4
          expr_not โ†’  โ— expr_not$macrocall$1
          expr_and$macrocall$4 โ†’  โ— expr_not
          expr_and$macrocall$1 โ†’  โ— expr_and$macrocall$4
          expr_and โ†’  โ— expr_and$macrocall$1
          expr_or$macrocall$4 โ†’  โ— expr_and
          expr_or$macrocall$1 โ†’  โ— expr_or$macrocall$4
          expr_or โ†’  โ— expr_or$macrocall$1
          expr_nostar โ†’  โ— expr_or
          expr โ†’  โ— expr_nostar
          select_expr_list_item โ†’  โ— expr select_expr_list_item$ebnf$1
          select_expr_list_aliased$ebnf$1$subexpression$1 โ†’ comma โ— select_expr_list_item
          select_expr_list_aliased$ebnf$1 โ†’ select_expr_list_aliased$ebnf$1 โ— select_expr_list_aliased$ebnf$1$subexpression$1
          select_expr_list_aliased โ†’ select_expr_list_item โ— select_expr_list_aliased$ebnf$1
          select_what โ†’ %kw_select โ— select_expr_list_aliased
          select_statement โ†’  โ— select_what select_statement$ebnf$1 select_statement$ebnf$2 select_statement$ebnf$3 select_statement$ebnf$4 select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A kw_is token based on:
          expr_is$subexpression$4 โ†’  โ— %kw_is kw_not_null
          expr_is โ†’ expr_is$subexpression$3 โ— expr_is$subexpression$4
          expr_eq$macrocall$4 โ†’  โ— expr_is
          expr_eq$macrocall$1 โ†’  โ— expr_eq$macrocall$4
          expr_eq โ†’  โ— expr_eq$macrocall$1
          expr_not$macrocall$4 โ†’  โ— expr_eq
          expr_not$macrocall$1 โ†’  โ— expr_not$macrocall$4
          expr_not โ†’  โ— expr_not$macrocall$1
          expr_and$macrocall$4 โ†’  โ— expr_not
          expr_and$macrocall$1 โ†’  โ— expr_and$macrocall$4
          expr_and โ†’  โ— expr_and$macrocall$1
          expr_or$macrocall$4 โ†’  โ— expr_and
          expr_or$macrocall$1 โ†’  โ— expr_or$macrocall$4
          expr_or โ†’  โ— expr_or$macrocall$1
          expr_nostar โ†’  โ— expr_or
          expr โ†’  โ— expr_nostar
          select_expr_list_item โ†’  โ— expr select_expr_list_item$ebnf$1
          select_expr_list_aliased$ebnf$1$subexpression$1 โ†’ comma โ— select_expr_list_item
          select_expr_list_aliased$ebnf$1 โ†’ select_expr_list_aliased$ebnf$1 โ— select_expr_list_aliased$ebnf$1$subexpression$1
          select_expr_list_aliased โ†’ select_expr_list_item โ— select_expr_list_aliased$ebnf$1
          select_what โ†’ %kw_select โ— select_expr_list_aliased
          select_statement โ†’  โ— select_what select_statement$ebnf$1 select_statement$ebnf$2 select_statement$ebnf$3 select_statement$ebnf$4 select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A kw_isnull token based on:
          expr_is$subexpression$2 โ†’  โ— %kw_isnull
          expr_is โ†’ expr_is$subexpression$1 โ— expr_is$subexpression$2
          expr_eq$macrocall$4 โ†’  โ— expr_is
          expr_eq$macrocall$1 โ†’  โ— expr_eq$macrocall$4
          expr_eq โ†’  โ— expr_eq$macrocall$1
          expr_not$macrocall$4 โ†’  โ— expr_eq
          expr_not$macrocall$1 โ†’  โ— expr_not$macrocall$4
          expr_not โ†’  โ— expr_not$macrocall$1
          expr_and$macrocall$4 โ†’  โ— expr_not
          expr_and$macrocall$1 โ†’  โ— expr_and$macrocall$4
          expr_and โ†’  โ— expr_and$macrocall$1
          expr_or$macrocall$4 โ†’  โ— expr_and
          expr_or$macrocall$1 โ†’  โ— expr_or$macrocall$4
          expr_or โ†’  โ— expr_or$macrocall$1
          expr_nostar โ†’  โ— expr_or
          expr โ†’  โ— expr_nostar
          select_expr_list_item โ†’  โ— expr select_expr_list_item$ebnf$1
          select_expr_list_aliased$ebnf$1$subexpression$1 โ†’ comma โ— select_expr_list_item
          select_expr_list_aliased$ebnf$1 โ†’ select_expr_list_aliased$ebnf$1 โ— select_expr_list_aliased$ebnf$1$subexpression$1
          select_expr_list_aliased โ†’ select_expr_list_item โ— select_expr_list_aliased$ebnf$1
          select_what โ†’ %kw_select โ— select_expr_list_aliased
          select_statement โ†’  โ— select_what select_statement$ebnf$1 select_statement$ebnf$2 select_statement$ebnf$3 select_statement$ebnf$4 select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A kw_is token based on:
          expr_is$subexpression$2 โ†’  โ— %kw_is %kw_null
          expr_is โ†’ expr_is$subexpression$1 โ— expr_is$subexpression$2
          expr_eq$macrocall$4 โ†’  โ— expr_is
          expr_eq$macrocall$1 โ†’  โ— expr_eq$macrocall$4
          expr_eq โ†’  โ— expr_eq$macrocall$1
          expr_not$macrocall$4 โ†’  โ— expr_eq
          expr_not$macrocall$1 โ†’  โ— expr_not$macrocall$4
          expr_not โ†’  โ— expr_not$macrocall$1
          expr_and$macrocall$4 โ†’  โ— expr_not
          expr_and$macrocall$1 โ†’  โ— expr_and$macrocall$4
          expr_and โ†’  โ— expr_and$macrocall$1
          expr_or$macrocall$4 โ†’  โ— expr_and
          expr_or$macrocall$1 โ†’  โ— expr_or$macrocall$4
          expr_or โ†’  โ— expr_or$macrocall$1
          expr_nostar โ†’  โ— expr_or
          expr โ†’  โ— expr_nostar
          select_expr_list_item โ†’  โ— expr select_expr_list_item$ebnf$1
          select_expr_list_aliased$ebnf$1$subexpression$1 โ†’ comma โ— select_expr_list_item
          select_expr_list_aliased$ebnf$1 โ†’ select_expr_list_aliased$ebnf$1 โ— select_expr_list_aliased$ebnf$1$subexpression$1
          select_expr_list_aliased โ†’ select_expr_list_item โ— select_expr_list_aliased$ebnf$1
          select_what โ†’ %kw_select โ— select_expr_list_aliased
          select_statement โ†’  โ— select_what select_statement$ebnf$1 select_statement$ebnf$2 select_statement$ebnf$3 select_statement$ebnf$4 select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A word token based on:
          kw_between โ†’  โ— %word
          ops_between โ†’ ops_between$ebnf$1 โ— kw_between
          expr_range$macrocall$2 โ†’  โ— ops_between
          expr_range$macrocall$1 โ†’ expr_range$macrocall$1$subexpression$1 โ— expr_range$macrocall$2 expr_range$macrocall$1$subexpression$2 expr_range$macrocall$3 expr_range$macrocall$1$subexpression$3
          expr_range โ†’  โ— expr_range$macrocall$1
          expr_compare$macrocall$4 โ†’  โ— expr_range
          expr_compare$macrocall$1 โ†’  โ— expr_compare$macrocall$4
          expr_compare โ†’  โ— expr_compare$macrocall$1
          expr_is โ†’  โ— expr_compare
          expr_eq$macrocall$4 โ†’  โ— expr_is
          expr_eq$macrocall$1 โ†’  โ— expr_eq$macrocall$4
          expr_eq โ†’  โ— expr_eq$macrocall$1
          expr_not$macrocall$4 โ†’  โ— expr_eq
          expr_not$macrocall$1 โ†’  โ— expr_not$macrocall$4
          expr_not โ†’  โ— expr_not$macrocall$1
          expr_and$macrocall$4 โ†’  โ— expr_not
          expr_and$macrocall$1 โ†’  โ— expr_and$macrocall$4
          expr_and โ†’  โ— expr_and$macrocall$1
          expr_or$macrocall$4 โ†’  โ— expr_and
          expr_or$macrocall$1 โ†’  โ— expr_or$macrocall$4
          expr_or โ†’  โ— expr_or$macrocall$1
          expr_nostar โ†’  โ— expr_or
          expr โ†’  โ— expr_nostar
          select_expr_list_item โ†’  โ— expr select_expr_list_item$ebnf$1
          select_expr_list_aliased$ebnf$1$subexpression$1 โ†’ comma โ— select_expr_list_item
          select_expr_list_aliased$ebnf$1 โ†’ select_expr_list_aliased$ebnf$1 โ— select_expr_list_aliased$ebnf$1$subexpression$1
          select_expr_list_aliased โ†’ select_expr_list_item โ— select_expr_list_aliased$ebnf$1
          select_what โ†’ %kw_select โ— select_expr_list_aliased
          select_statement โ†’  โ— select_what select_statement$ebnf$1 select_statement$ebnf$2 select_statement$ebnf$3 select_statement$ebnf$4 select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A op_eq token based on:
          expr_eq$macrocall$2$subexpression$1 โ†’  โ— %op_eq
          expr_eq$macrocall$2 โ†’  โ— expr_eq$macrocall$2$subexpression$1
          expr_eq$macrocall$1 โ†’ expr_eq$macrocall$1$subexpression$1 โ— expr_eq$macrocall$2 expr_eq$macrocall$1$subexpression$2
          expr_eq โ†’  โ— expr_eq$macrocall$1
          expr_not$macrocall$4 โ†’  โ— expr_eq
          expr_not$macrocall$1 โ†’  โ— expr_not$macrocall$4
          expr_not โ†’  โ— expr_not$macrocall$1
          expr_and$macrocall$4 โ†’  โ— expr_not
          expr_and$macrocall$1 โ†’  โ— expr_and$macrocall$4
          expr_and โ†’  โ— expr_and$macrocall$1
          expr_or$macrocall$4 โ†’  โ— expr_and
          expr_or$macrocall$1 โ†’  โ— expr_or$macrocall$4
          expr_or โ†’  โ— expr_or$macrocall$1
          expr_nostar โ†’  โ— expr_or
          expr โ†’  โ— expr_nostar
          select_expr_list_item โ†’  โ— expr select_expr_list_item$ebnf$1
          select_expr_list_aliased$ebnf$1$subexpression$1 โ†’ comma โ— select_expr_list_item
          select_expr_list_aliased$ebnf$1 โ†’ select_expr_list_aliased$ebnf$1 โ— select_expr_list_aliased$ebnf$1$subexpression$1
          select_expr_list_aliased โ†’ select_expr_list_item โ— select_expr_list_aliased$ebnf$1
          select_what โ†’ %kw_select โ— select_expr_list_aliased
          select_statement โ†’  โ— select_what select_statement$ebnf$1 select_statement$ebnf$2 select_statement$ebnf$3 select_statement$ebnf$4 select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A op_neq token based on:
          expr_eq$macrocall$2$subexpression$1 โ†’  โ— %op_neq
          expr_eq$macrocall$2 โ†’  โ— expr_eq$macrocall$2$subexpression$1
          expr_eq$macrocall$1 โ†’ expr_eq$macrocall$1$subexpression$1 โ— expr_eq$macrocall$2 expr_eq$macrocall$1$subexpression$2
          expr_eq โ†’  โ— expr_eq$macrocall$1
          expr_not$macrocall$4 โ†’  โ— expr_eq
          expr_not$macrocall$1 โ†’  โ— expr_not$macrocall$4
          expr_not โ†’  โ— expr_not$macrocall$1
          expr_and$macrocall$4 โ†’  โ— expr_not
          expr_and$macrocall$1 โ†’  โ— expr_and$macrocall$4
          expr_and โ†’  โ— expr_and$macrocall$1
          expr_or$macrocall$4 โ†’  โ— expr_and
          expr_or$macrocall$1 โ†’  โ— expr_or$macrocall$4
          expr_or โ†’  โ— expr_or$macrocall$1
          expr_nostar โ†’  โ— expr_or
          expr โ†’  โ— expr_nostar
          select_expr_list_item โ†’  โ— expr select_expr_list_item$ebnf$1
          select_expr_list_aliased$ebnf$1$subexpression$1 โ†’ comma โ— select_expr_list_item
          select_expr_list_aliased$ebnf$1 โ†’ select_expr_list_aliased$ebnf$1 โ— select_expr_list_aliased$ebnf$1$subexpression$1
          select_expr_list_aliased โ†’ select_expr_list_item โ— select_expr_list_aliased$ebnf$1
          select_what โ†’ %kw_select โ— select_expr_list_aliased
          select_statement โ†’  โ— select_what select_statement$ebnf$1 select_statement$ebnf$2 select_statement$ebnf$3 select_statement$ebnf$4 select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A kw_and token based on:
          expr_and$macrocall$2 โ†’  โ— %kw_and
          expr_and$macrocall$1 โ†’ expr_and$macrocall$1$subexpression$1 โ— expr_and$macrocall$2 expr_and$macrocall$1$subexpression$2
          expr_and โ†’  โ— expr_and$macrocall$1
          expr_or$macrocall$4 โ†’  โ— expr_and
          expr_or$macrocall$1 โ†’  โ— expr_or$macrocall$4
          expr_or โ†’  โ— expr_or$macrocall$1
          expr_nostar โ†’  โ— expr_or
          expr โ†’  โ— expr_nostar
          select_expr_list_item โ†’  โ— expr select_expr_list_item$ebnf$1
          select_expr_list_aliased$ebnf$1$subexpression$1 โ†’ comma โ— select_expr_list_item
          select_expr_list_aliased$ebnf$1 โ†’ select_expr_list_aliased$ebnf$1 โ— select_expr_list_aliased$ebnf$1$subexpression$1
          select_expr_list_aliased โ†’ select_expr_list_item โ— select_expr_list_aliased$ebnf$1
          select_what โ†’ %kw_select โ— select_expr_list_aliased
          select_statement โ†’  โ— select_what select_statement$ebnf$1 select_statement$ebnf$2 select_statement$ebnf$3 select_statement$ebnf$4 select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A kw_or token based on:
          expr_or$macrocall$2 โ†’  โ— %kw_or
          expr_or$macrocall$1 โ†’ expr_or$macrocall$1$subexpression$1 โ— expr_or$macrocall$2 expr_or$macrocall$1$subexpression$2
          expr_or โ†’  โ— expr_or$macrocall$1
          expr_nostar โ†’  โ— expr_or
          expr โ†’  โ— expr_nostar
          select_expr_list_item โ†’  โ— expr select_expr_list_item$ebnf$1
          select_expr_list_aliased$ebnf$1$subexpression$1 โ†’ comma โ— select_expr_list_item
          select_expr_list_aliased$ebnf$1 โ†’ select_expr_list_aliased$ebnf$1 โ— select_expr_list_aliased$ebnf$1$subexpression$1
          select_expr_list_aliased โ†’ select_expr_list_item โ— select_expr_list_aliased$ebnf$1
          select_what โ†’ %kw_select โ— select_expr_list_aliased
          select_statement โ†’  โ— select_what select_statement$ebnf$1 select_statement$ebnf$2 select_statement$ebnf$3 select_statement$ebnf$4 select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A kw_as token based on:
          ident_aliased$subexpression$1 โ†’  โ— %kw_as ident
          ident_aliased โ†’  โ— ident_aliased$subexpression$1
          select_expr_list_item$ebnf$1 โ†’  โ— ident_aliased
          select_expr_list_item โ†’ expr โ— select_expr_list_item$ebnf$1
          select_expr_list_aliased$ebnf$1$subexpression$1 โ†’ comma โ— select_expr_list_item
          select_expr_list_aliased$ebnf$1 โ†’ select_expr_list_aliased$ebnf$1 โ— select_expr_list_aliased$ebnf$1$subexpression$1
          select_expr_list_aliased โ†’ select_expr_list_item โ— select_expr_list_aliased$ebnf$1
          select_what โ†’ %kw_select โ— select_expr_list_aliased
          select_statement โ†’  โ— select_what select_statement$ebnf$1 select_statement$ebnf$2 select_statement$ebnf$3 select_statement$ebnf$4 select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A word token based on:
          word โ†’  โ— %word
          ident โ†’  โ— word
          ident_aliased โ†’  โ— ident
          select_expr_list_item$ebnf$1 โ†’  โ— ident_aliased
          select_expr_list_item โ†’ expr โ— select_expr_list_item$ebnf$1
          select_expr_list_aliased$ebnf$1$subexpression$1 โ†’ comma โ— select_expr_list_item
          select_expr_list_aliased$ebnf$1 โ†’ select_expr_list_aliased$ebnf$1 โ— select_expr_list_aliased$ebnf$1$subexpression$1
          select_expr_list_aliased โ†’ select_expr_list_item โ— select_expr_list_aliased$ebnf$1
          select_what โ†’ %kw_select โ— select_expr_list_aliased
          select_statement โ†’  โ— select_what select_statement$ebnf$1 select_statement$ebnf$2 select_statement$ebnf$3 select_statement$ebnf$4 select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A comma token based on:
          comma โ†’  โ— %comma
          select_expr_list_aliased$ebnf$1$subexpression$1 โ†’  โ— comma select_expr_list_item
          select_expr_list_aliased$ebnf$1 โ†’ select_expr_list_aliased$ebnf$1 โ— select_expr_list_aliased$ebnf$1$subexpression$1
          select_expr_list_aliased โ†’ select_expr_list_item โ— select_expr_list_aliased$ebnf$1
          select_what โ†’ %kw_select โ— select_expr_list_aliased
          select_statement โ†’  โ— select_what select_statement$ebnf$1 select_statement$ebnf$2 select_statement$ebnf$3 select_statement$ebnf$4 select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A kw_from token based on:
          select_from โ†’  โ— %kw_from select_subject
          select_statement$ebnf$1 โ†’  โ— select_from
          select_statement โ†’ select_what โ— select_statement$ebnf$1 select_statement$ebnf$2 select_statement$ebnf$3 select_statement$ebnf$4 select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A kw_where token based on:
          select_where โ†’  โ— %kw_where expr
          select_statement$ebnf$2 โ†’  โ— select_where
          select_statement โ†’ select_what select_statement$ebnf$1 โ— select_statement$ebnf$2 select_statement$ebnf$3 select_statement$ebnf$4 select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A kw_group token based on:
          select_groupby โ†’  โ— %kw_group kw_by expr_list_raw
          select_statement$ebnf$3 โ†’  โ— select_groupby
          select_statement โ†’ select_what select_statement$ebnf$1 select_statement$ebnf$2 โ— select_statement$ebnf$3 select_statement$ebnf$4 select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A kw_order token based on:
          select_order_by$subexpression$1 โ†’  โ— %kw_order kw_by
          select_order_by โ†’  โ— select_order_by$subexpression$1 select_order_by_expr select_order_by$ebnf$1
          select_statement$ebnf$4 โ†’  โ— select_order_by
          select_statement โ†’ select_what select_statement$ebnf$1 select_statement$ebnf$2 select_statement$ebnf$3 โ— select_statement$ebnf$4 select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A kw_limit token based on:
          select_limit$ebnf$1$subexpression$1 โ†’  โ— %kw_limit int
          select_limit$ebnf$1 โ†’  โ— select_limit$ebnf$1$subexpression$1
          select_limit โ†’  โ— select_limit$ebnf$1 select_limit$ebnf$2 select_limit$ebnf$3
          select_statement โ†’ select_what select_statement$ebnf$1 select_statement$ebnf$2 select_statement$ebnf$3 select_statement$ebnf$4 โ— select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A kw_offset token based on:
          select_limit$ebnf$2$subexpression$1 โ†’  โ— %kw_offset int select_limit$ebnf$2$subexpression$1$ebnf$1
          select_limit$ebnf$2 โ†’  โ— select_limit$ebnf$2$subexpression$1
          select_limit โ†’ select_limit$ebnf$1 โ— select_limit$ebnf$2 select_limit$ebnf$3
          select_statement โ†’ select_what select_statement$ebnf$1 select_statement$ebnf$2 select_statement$ebnf$3 select_statement$ebnf$4 โ— select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A kw_fetch token based on:
          select_limit$ebnf$3$subexpression$1 โ†’  โ— %kw_fetch select_limit$ebnf$3$subexpression$1$ebnf$1 int select_limit$ebnf$3$subexpression$1$ebnf$2
          select_limit$ebnf$3 โ†’  โ— select_limit$ebnf$3$subexpression$1
          select_limit โ†’ select_limit$ebnf$1 select_limit$ebnf$2 โ— select_limit$ebnf$3
          select_statement โ†’ select_what select_statement$ebnf$1 select_statement$ebnf$2 select_statement$ebnf$3 select_statement$ebnf$4 โ— select_limit
          statement โ†’  โ— select_statement
          main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3
      A semicolon token based on:
          statement_separator โ†’  โ— %semicolon
          main$ebnf$3 โ†’ main$ebnf$3 โ— statement_separator
          main โ†’ main$ebnf$1 statement main$ebnf$2 โ— main$ebnf$3

          at Parser.feed (/home/bobby/phorm-new/node_modules/nearley/lib/nearley.js:343:27)
          at _parse (/home/bobby/phorm-new/node_modules/pgsql-ast-parser/src/parser.ts:43:12)
          at Object.parse (/home/bobby/phorm-new/node_modules/pgsql-ast-parser/src/parser.ts:24:18)
          at Object.parseSql (/home/bobby/phorm-new/node_modules/pg-mem/src/parse-cache.ts:25:15)
          at Query.parse (/home/bobby/phorm-new/node_modules/pg-mem/src/query.ts:80:16)
          at Query.queries (/home/bobby/phorm-new/node_modules/pg-mem/src/query.ts:88:31)
          at queries.next (<anonymous>)
          at Query.query (/home/bobby/phorm-new/node_modules/pg-mem/src/query.ts:67:20)
          at /home/bobby/phorm-new/node_modules/pg-mem/src/adapters.ts:81:80
          at Timeout.task [as _onTimeout] (/home/bobby/phorm-new/node_modules/jsdom/lib/jsdom/browser/Window.js:391:19)
          at listOnTimeout (internal/timers.js:554:17)
          at processTimers (internal/timers.js:497:7)

I know this library does not have advanced function yet, but being able to roll extensions and column descriptions might be helpful; even if it completely ignores the type and uses string, and for descriptions it just ignores them.

Idea: register function in postgres-compatible way with plv8

It occurred to me that as well as the API-way of registering a function:

db.public.registerFunction({
            name: 'say_hello',
            args: [DataType.text],
            returns: DataType.text,
            implementation: x => 'hello ' + x,
})

Might it also be possible to enable "real" functions defined via SQL, as long as they use language plv8?

e.g.

create function say_hello(x text) returns text as
$func_delimiter$

return 'hello ' + x

$func_delimiter$
language plv8;

If pgsql-ast-parser is able to parse the create function query, it could be automatically mapped to the arguments required for registerFunction, where implementation is the eval(...) result of the body. That way, at least for plv8 functions, it'd be totally valid both in pg-mem and real postgres.

I'm interested in this as I have a library which implements row tracking through an in-memory JS git implementation: https://npmjs.com/package/plv8-git - I'm super curious if pg-mem could be used somehow to come full circle and even run the database in memory too.

Edit: just seen you have discussions enabled. I would have created this over there if I'd noticed, since it's not an issue. Feel free to move if you prefer it there too!

๐Ÿšฉ Support "CREATE TRIGGER" & "CREATE AGGREGATE" statements

Supporting CREATE TRIGGER is somehow related to the possibility of supporting CREATE FUNCTION statements, which is tracked by #42

That said, supporting triggers for functions declared as custom functions should also be possible, and simpler.

... it must just be decided how to let function implementation access caller context:

  • new/old record versions
  • the current transaction (all sub-requests must be performed in the same outer transaction)

Maybe via some static functions ?

nb: Beware of reentrant calls... must check how this is handled in pg.

newDb.adapters is undefined

I feel like I must be doing something silly.

Installed is version 1.6

I am doing this

import {newDb} from 'pg-mem';
const {Client} = newDb.adapters.createPg();

And I get this error:

TypeError: Cannot read property 'createPg' of undefined

Are the adapter docs up to date?

Missing support for EXTRACT datetime

Specification: https://www.postgresql.org/docs/current/functions-datetime.html#FUNCTIONS-DATETIME-EXTRACT

๐Ÿ’” Your query failed to parse.
This is most likely due to a SQL syntax error. However, you might also have hit a bug, or an unimplemented feature of pg-mem.
If this is the case, please file an issue at https://github.com/oguimbal/pg-mem along with a query that reproduces this syntax error.

๐Ÿ‘‰ Failed query:

SELECT EXTRACT(EPOCH FROM TIMESTAMP  '2001-02-16 20:38:40.12-08');

;

๐Ÿ’€ Syntax error at line 1 col 22:

SELECT EXTRACT(EPOCH FROM
^
Unexpected kw_from token: "from". Instead, I was expecting to see one of the following:

- A "dot" token
- A "dot" token
- A "lparen" token
- A "op_cast" token
- A "lbracket" token
- A "op_member" token
- A "op_membertext" token
- A "op_exp" token
- A "star" token
- A "op_div" token
- A "op_mod" token
- A "op_plus" token
- A "op_minus" token
- A "op_additive" token
- A "kw_not" token
- A "kw_in" token
- A "kw_not" token
- A "op_like" token
- A "op_ilike" token
- A "op_not_like" token
- A "op_not_ilike" token
- A "kw_like" token
- A "kw_ilike" token
- A "kw_not" token
- A "kw_is" token
- A "op_compare" token
- A "kw_notnull" token
- A "kw_is" token
- A "kw_isnull" token
- A "kw_is" token
- A "word" token
- A "op_eq" token
- A "op_neq" token
- A "kw_and" token
- A "kw_or" token
- A "comma" token
- A "rparen" token
- 

See relevant issue at oguimbal/pgsql-ast-parser#5

Database may not be restoring?

Not sure if I'm missing something here but:

const inMemoryDb = newDb();
inMemoryDb.public.none(fs.readFileSync('src/graphql/DB/pgMem/migrations/001-initial.sql', 'utf8'));
const backup = inMemoryDb.backup();
export { inMemoryDb, backup };

When I run this at the top of every test, it doesn't seem like it's resetting the DB to the initial migration because in 001-initial.sql I do not seed any "Crafters" for the Crafter table. No crafters were created in 001-initial.sql'. My tests create crafters.

But when I run my tests, it's not clearing crafters that were created after each test runs it seems. If I output select * from crafters, it seems to be aggregating them.

beforeEach(() => {
  backup.restore();
});

My tests are mainly mocha. A few are test. Right now I'm just talking about the ones using mocha.

Initial Migration Cached?

I've got this:

const inMemoryDb = newDb();
inMemoryDb.public.none(fs.readFileSync(path.resolve(__dirname, 'migrations/001-initial.sql'), 'utf8'));
const backup = inMemoryDb.backup();
export { inMemoryDb, backup };

My JS service then uses this instance.

inMemoryDb.on('query', (sql) => {
  console.log('query succeeded:', sql);
});

I had added an insert and I don't see that in there either...so it has to be caching an old file or something...I don't get this.

Am I just not understanding how pg-mem works here?

I don't have any other migrations, just that initial .sql there so far. And I'm not even using migrations this time, it's being run in my production code, so that isn't using migrations, only my tests are...and I'm not running my tests right now.

Error: type "timestamp with time zone" does not exist

looks like another type is not supported:

Error: type "timestamp with time zone" does not exist

When my create table script which has this field type below in it, blows up:

crafter_created_date timestamp with time zone NOT NULL,`

this is my core table so it's important that it be supported.

Support [email protected]

QueryFailedError: column "columns.table_name" does not exist

๐Ÿœ This seems to be an execution error, which means that your request syntax seems okay,
but the resulting statement cannot be executed โ†’ Probably not a pg-mem error.

*๏ธโƒฃ Failed SQL statement: 
            SELECT columns.*,
              pg_catalog.col_description(('"' || table_catalog || '"."' || table_schema || '"."' || table_name || '"')::regclass::oid, ordinal_position) AS description,
              ('"' || "udt_schema" || '"."' || "udt_name" || '"')::"regtype" AS "regtype",
              pg_catalog.format_type("col_attr"."atttypid", "col_attr"."atttypmod") AS "format_type"
              FROM "information_schema"."columns"
              LEFT JOIN "pg_catalog"."pg_attribute" AS "col_attr"
              ON "col_attr"."attname" = "columns"."column_name"
              AND "col_attr"."attrelid" = (
                SELECT
                  "cls"."oid" FROM "pg_catalog"."pg_class" AS "cls"
                  LEFT JOIN "pg_catalog"."pg_namespace" AS "ns"
                  ON "ns"."oid" = "cls"."relnamespace"
                WHERE "cls"."relname" = "columns"."table_name"
                AND "ns"."nspname" = "columns"."table_schema"
              )
            WHERE
            ("table_schema" = 'public' AND "table_name" = 'account');

see this repro repo, mentioned in comments here

Support for INHERITS

Hi there, thank you for your work, it is awesome !

Are there any plans to support INHERITS ?

Are you open to contributions for supporting it ?

Cannot parse FROM( SELECT ...) in SELECT queries

Thank you for an amazing library, works like a charm for common cases.

However, I've encountered a parse error, trying to parse the following query:

SELECT * FROM table_name as t,(SELECT COUNT(*) as count FROM table_name) as c

The query itself is valid, as you can see on the screenshot:
Screenshot 2021-01-30 at 21 40 33

Here's the full error and stack:

 Error: ๐Ÿ’” Your query failed to parse.
This is most likely due to a SQL syntax error. However, you might also have hit a bug, or an unimplemented feature of pg-mem.
If this is the case, please file an issue at https://github.com/oguimbal/pg-mem along with a query that reproduces this syntax error.

๐Ÿ‘‰ Failed query:

    SELECT * FROM templates as t,(SELECT COUNT(*) as count FROM templates) as c LIMIT 20;

๐Ÿ’€ Syntax error at line 1 col 26:

  SELECT * FROM styles as t,
                           ^
Unexpected comma token: ",". Instead, I was expecting to see one of the following:

    - A "kw_left" token
    - A "kw_right" token
    - A "kw_full" token
    - A "kw_inner" token
    - A "kw_where" token
    - A "kw_group" token
    - A "kw_join" token
    - A "kw_order" token
    - A "kw_limit" token
    - A "kw_offset" token
    - A "kw_fetch" token
    - A "kw_union" token
    - A "semicolon" token


      at Object.parseSql (node_modules/pg-mem/src/parse-cache.ts:44:15)
      at DbSchema.parse (node_modules/pg-mem/src/schema.ts:80:16)
      at DbSchema.queries (node_modules/pg-mem/src/schema.ts:88:31)
      at queries.next (<anonymous>)
      at DbSchema.query (amp-cms/src/schema.ts:67:20)
      at amp-cms/src/mocks.ts:8:17
      at Generator.next (<anonymous>)
      at amp-cms/src/mocks.ts:8:71
      at new Promise (<anonymous>)
      at __awaiter (src/mocks.ts:4:12)



error Command failed with exit code 1.

Query failed when creating enum type

Hello again;

First, thank you for your work on the regclass type. I've appreciated seeing how responsive you are with this project.

Second, I've come across an issue with CREATE TYPE "baz" AS ENUM('foo', 'bar');

If it helps, I've got a small repo created that replicates this issue: https://github.com/xpportal-bmo/nestjs-pg-mem-test

I'm using NestJS + typeorm + pg

Here is the full output:

    QueryFailedError: ๐Ÿ’” Your query failed to parse.
    This is most likely due to a SQL syntax error. However, you might also have hit a bug, or an unimplemented feature of pg-mem.
    If this is the case, please file an issue at https://github.com/oguimbal/pg-mem along with a query that reproduces this syntax error.

    ๐Ÿ‘‰ Failed query:

        CREATE TYPE "User_role_enum" AS ENUM('user', 'admin');

    ๐Ÿ’€ Syntax error at line 1 col 13:

      CREATE TYPE "User_role_enum"
                  ^
    Unexpected word token: "\"User_role_enum\"". I did not expect any more input. Here is the state of my parse table:

        kw_index โ†’ %word โ— 
        kw_sequence โ†’ %word โ— 
        kw_temporary โ†’ %word โ— 
        kw_temp โ†’ %word โ— 
        kw_extension โ†’ %word โ—

      at new QueryFailedError (src/error/QueryFailedError.ts:9:9)
      at src/driver/postgres/PostgresQueryRunner.ts:220:30
      at Timeout._onTimeout (node_modules/pg-mem/src/adapters.ts:92:42)

Multiple queries to same DB instance, same test?

I'd expect that I still would find a crafter in the DB. I confirmed that it does insert a crafter here. But in the second graphQL call when pg-mem looks for the crafter previously insterted, it's lost, that table is empty again for some reason:

it('fetches a crafter by username', async () => {
  const graphQLReturnFields = 'firstName';
  const craftersStub = [{ ...crafterInputStub }];
  craftersStub[0].firstName = 'Dave';
  craftersStub[0].lastName = 'Schinkel';
  await addCraftersGraphQL(craftersStub, graphQLReturnFields); // verified it successfully adds it to the DB here

  const query = {
    query: `query {
      crafter(username: "${username}") {
        username,
      }
    }`,
  };
  const response = await sendQuery(query); // it doesn't find the added crafter above in the DB when it tries to query again because for some reason the crafter table is empty after a second call to the DB
  const { crafter } = response.body.data;

  expect(crafter.username).to.exist;
});

Support for aggregate function max

Would like to be able to run something like this:

select setval('skills_skill_id_seq',select max(id) from skills);

Error: NotSupported [Error]: ๐Ÿ”จ Not supported ๐Ÿ”จ : aggregation function max

Support for INET fields

This might not be so common, but we really like using INET type.
Any plans to support it or should we consider a PR ?
Best,

CREATE TABLE example (
  public_ip_address               INET,
  default_address_pool            INET NOT NULL DEFAULT '172.25.0.0/16'::INET,
);

Seeding a table

I got a pg_dump of my prod DB into a .sql file. In it, there are seeding scripts generated automatically by pg_dump, for example here's one:

COPY public.country (country_id, name) FROM stdin;
0	Afghanistan
1	Albania

is there a way for this to work or do I have to basically manually create insert statements for this data?

Data type undefined

Not sure if I'm missing something else but my 001-initial.sql is bombing out today completely which is now what happened yesterday. Yesterday it was at least parsing some of the create table scripts. I even tried to replace my create table script using one of your examples from your migrations folder, but same error.

This is after bringing down your latest 1.4.5:

CREATE TABLE public.city (
    city_id integer NOT NULL,
    name character varying NOT NULL,
    state_id integer
);

It looks like maybe it doesn't support character type yet or something? I see text is supported in datatypes.d.ts but not character?

Error: ๐Ÿ”จ Not supported ๐Ÿ”จ : Data type undefined

๐Ÿ‘‰ pg-mem is work-in-progress, and it would seem that you've hit one of its limits.

*๏ธโƒฃ Reconsituted failed SQL statement: CREATE TABLE "public"."city" ("city_id" "integer"   not null , "name" "character varying"   not null , "state_id" "integer"  )

๐Ÿ‘‰ You can file an issue at https://github.com/oguimbal/pg-mem along with a way to reproduce this error (if you can), and  the stacktrace:

Aside

Also BTW, when you ported that migrations code over from node-sqlite, did you also make that code more compatible with postgres so that it can parse postgres data types during migrations instead of relying on sqlite data types?

I am going to also suggest that you go one step further (not urgent but fairly) because it seems like you don't have this setup yet, which is to setup CI/CD for this project and show a pill on the main page so that I, the user knows that your tests for this are passing and that I have more confidence in using this lib. That's not meant to be snarky, but being serious here. Just a suggestion...

In fact, would you be cool with adding me as a collaborator which I think would allow me to setup github actions for you and get CI running for you on this?

Also, I don't really want to use VSCode if I am to try to do any PRs or even try to run your stuff locally just to poke around. I am an JetBrains guy. So I read that your "codebase only works in VsCode"? I noticed after I did open it in WebStorm that schema.ts and datatypes.ts after doing yarn, had some types not resolving in those files. For example in WebStorm I'm getting:

Screen Shot 2020-12-12 at 12 25 23 AM

OVERRIDING SYSTEM VALUE - Unexpected word token: "value"

It's not able to run inserts that have OVERRIDING SYSTEM VALUE in them:

INSERT INTO city OVERRIDING SYSTEM VALUE
                                                                                                                                                                                                                                                                                           ^
Unexpected word token: "value". I did not expect any more input. Here is the state of my parse table:

    kw_values โ†’ %word โ— 

    at Object.parseSql (node_modules/pg-mem/src/parse-cache.ts:44:15)
    at DbSchema.parse (node_modules/pg-mem/src/schema.ts:76:16)
    at DbSchema.queries (node_modules/pg-mem/src/schema.ts:84:31)
    at queries.next (<anonymous>)
    at DbSchema.query (node_modules/pg-mem/src/schema.ts:63:20)
    at DbSchema.none (node_modules/pg-mem/src/schema.ts:48:14)

I mean for now I took it out and it's ok without the OVERRIDING SYSTEM VALUE but doesn't feel good that it trips up on that syntax that's generated by pg_dump which is valid SQL for postgres.

Self-Alias not found (subquery using alias of its outer query)

https://oguimbal.github.io/pg-mem-playground/

CREATE TABLE my_table (id text NOT NULL PRIMARY KEY, name text NOT NULL, parent_id text);
CREATE INDEX my_table_idx_name ON my_table (name);
CREATE INDEX my_table_idx_id_parent_id ON my_table (id,parent_id);

SELECT * FROM my_table as t1 WHERE t1.name = 'test';
SELECT * FROM my_table as t1 WHERE t1.name = 'test' AND NOT EXISTS (SELECT * FROM my_table as t2 WHERE t2.parent_id = t1.id);

Alias 't1' not found

๐Ÿœ This seems to be an execution error, which means that your request syntax seems okay,
but the resulting statement cannot be executed โ†’ Probably not a pg-mem error.

*๏ธโƒฃ Reconsituted failed SQL statement: SELECT *  FROM "my_table" AS "t1"  WHERE (("t1"."name" = ('test')) AND (NOT ("exists"((SELECT *  FROM "my_table" AS "t2"  WHERE ("t2"."parent_id" = "t1"."id") )))))

๐Ÿ‘‰ You can file an issue at https://github.com/oguimbal/pg-mem along with a way to reproduce this error (if you can), and  the stacktrace:

Unexpected kw_distinct token: "distinct"

Hello again; found another feature that I hope you could implement.

This is running NestJS + typeorm + postgres

Example repo can be found here: https://github.com/xpportal-bmo/nestjs-pg-mem-test

Replication steps:

  • npm install
  • npm run test

Full error output:

โ— EntityTest โ€บ should find a user by email

    QueryFailedError: ๐Ÿ’” Your query failed to parse.
    This is most likely due to a SQL syntax error. However, you might also have hit a bug, or an unimplemented feature of pg-mem.
    If this is the case, please file an issue at https://github.com/oguimbal/pg-mem along with a query that reproduces this syntax error.

    ๐Ÿ‘‰ Failed query:

        SELECT DISTINCT "distinctAlias"."User_id" as "ids_User_id" FROM (SELECT "User"."id" AS "User_id", "User"."createdDate" AS "User_createdDate", "User"."updatedDate" AS "User_updatedDate", "User"."deletedDate" AS "User_deletedDate", "User"."email" AS "User_email", "User"."role" AS "User_role", "User"."accountId" AS "User_accountId", "User_account"."id" AS "User_account_id", "User_account"."createdDate" AS "User_account_createdDate", "User_account"."updatedDate" AS "User_account_updatedDate", "User_account"."deletedDate" AS "User_account_deletedDate", "User_account"."name" AS "User_account_name", "User_account"."type" AS "User_account_type" FROM "User" "User" LEFT JOIN "Account" "User_account" ON "User_account"."id"="User"."accountId" WHERE ( "User"."email" = '[email protected]' ) AND ( "User"."deletedDate" IS NULL )) "distinctAlias"  ORDER BY "User_id" ASC LIMIT 1;

    ๐Ÿ’€ Syntax error at line 1 col 8:

      SELECT DISTINCT
             ^
    Unexpected kw_distinct token: "distinct". Instead, I was expecting to see one of the following:

        - A "kw_from" token
        - A "star" token
        - A "lparen" token
        - A "kw_where" token
        - A "kw_group" token
        - A "kw_order" token
        - A "kw_not" token
        - A "kw_limit" token
        - A "kw_offset" token
        - A "kw_fetch" token
        - A "semicolon" token
        - A "op_plus" token
        - A "op_minus" token
        - A "word" token
        - A "kw_true" token
        - A "kw_false" token
        - A "kw_null" token
        - A "kw_case" token
        - A "float" token
        - A "int" token
        - A "string" token
        - A "eString" token
        - A "kw_current_catalog" token
        - A "kw_current_date" token
        - A "kw_current_role" token
        - A "kw_current_schema" token
        - A "kw_current_timestamp" token
        - A "kw_current_time" token
        - A "kw_localtimestamp" token
        - A "kw_localtime" token
        - A "kw_session_user" token
        - A "kw_user" token
        - A "kw_current_user" token
        - A "kw_any" token

      at new QueryFailedError (src/error/QueryFailedError.ts:9:9)
      at src/driver/postgres/PostgresQueryRunner.ts:220:30
      at Timeout._onTimeout (node_modules/pg-mem/src/adapters.ts:92:42)

SQL Migrations Not Working?

So I know you wrote a test to test that ported sqlite migration code, saw that test in one of your commits. But when I try this in my app, it's simply not working:

InMemoryDB.ts

const inMemoryDb = newDb();
inMemoryDb.public.none(fs.readFileSync(path.resolve(__dirname, 'migrations/001-initial.sql'), 'utf8'));
(async () =>await inMemoryDb.public.migrate())();
const skills = inMemoryDb.public.many("select * from skills where name = 'Swift'");
console.log('skills', skills); // prints []

the migrations folder is at the same level as InMemoryDB.ts and in that folder is:

001-initial.sql
002-add-skills.sql

001-initial.sql - definitely adds a skills table seeded with some initial skills

002-add-skills.sql

INSERT INTO skills VALUES ('Swift', 0);
INSERT INTO skills VALUES ('Objective-C', 0);

no idea here, thoughts?

Errors importing production schema

Trying to follow the instructions in the wiki for importing production schema (https://github.com/oguimbal/pg-mem/wiki/FAQ#-how-to-import-my-production-schema-in-pg-mem-). I've run into some errors that I believe may not be specific to my DB.

I've run the SQL dump as shown in the example, and tried the same sample script:

const fs = require('fs');
const { newDb } = require('pg-mem');

const db = newDb();

db.public.none(fs.readFileSync('dump.sql', 'utf8'));

const backup = db.backup();

However, I got the Unexpected word token: "public" error for lines that look like these:

CREATE FUNCTION public.some_name ...

CREATE AGGREGATE public.some_name ...

CREATE VIEW public.some_name AS ...

CREATE OR REPLACE VIEW public.some_name AS ...

And Unexpected word token: "some_name" for lines that look like this:

CREATE TRIGGER some_name AFTER UPDATE ON ...

I'm not sure if this is an issue with this library or something on my end.

Unexpected kw_do token: "do"

Should I be able to run something like this via pg-mem?

do $$
    DECLARE maxId integer;
    begin
        select max(id) from skills into maxId;
        select setval('skills_skill_id_seq',maxId);
end $$;

Error

 '๐Ÿ’€ Syntax error at line 406 col 1:\n' +
      '\n' +
      '  do\n' +
      '  ^\n' +
      'Unexpected kw_do token: "do". Instead, I was expecting to see one of the following:\n' +
      '\n' +
      '    - A "semicolon" token\n' +
      '    - A "kw_create" token\n' +
      '    - A "kw_create" token\n' +
      '    - A "kw_create" token\n' +
      '    - A "kw_create" token\n' +
      '    - A "kw_create" token\n' +
      '    - A "kw_with" token\n' +
      '    - A "word" token\n' +
      '    - A "kw_select" token\n' +
      '    - A "word" token\n' +
      '    - A "word" token\n' +
      '    - A "word" token\n' +
      '    - A "word" token\n' +
      '    - A "word" token\n' +
      '    - A "word" token\n' +
      '    - A "word" token\n' +
      '    - A "word" token\n' +
      '    - A "word" token\n' +
      '    - A "word" token\n' +
      '    - A "word" token\n' +
      '    - A "word" token\n' +
      '    - A "word" token\n' +
      '    - A "lparen" token\n' +
      '\n'

if not, I need to because I need to be able to reset the sequence on my table after I do inserts using OVERRIDING SYSTEM VALUE on a table with constraint ADD GENERATED ALWAYS AS IDENTITY

Support data type POINT

Can you support this data type? I need it or else I can't create this table. I'm just showing the field here which had the problem, removed the rest of the fields because you just need to see that the field type is not supported here

Error: ๐Ÿ”จ Not supported ๐Ÿ”จ : Data type "point"

CREATE TABLE public.community (
    coordinates point
);

Migrations format for compatibility with flyway

I plan on using Flyway to pick up the migrations from pg-mem and deploy to my prod database and automate that in CI.

Since pg-mem is using the code ported from node-sqlite, I don't know if this will work. Is it possible to change the format for migrations for pg-mem to base it on the way flyway works just so that it'll be compatible?

https://dev.to/arojunior/the-best-and-easy-way-to-handle-database-migrations-version-control-pdg

So we'd need to be able to prefix migrations with V, etc.
https://flywaydb.org/documentation/concepts/migrations.html

So I'm proposing to tweak the code you ported over to have it work with that kind of prefix so that it's naturally compatible with flyway.

Getting an odd error in relation to `col_description`

I'm rewriting my project completely, and trying to implement pg-mem. However, I'm getting this error when trying to initialize and sync the database:

QueryFailedError: column "columns.table_name" does not exist

    ๐Ÿœ This seems to be an execution error, which means that your request syntax seems okay,
    but the resulting statement cannot be executed โ†’ Probably not a pg-mem error.

    *๏ธโƒฃ Failed SQL statement:
                SELECT columns.*,
                  pg_catalog.col_description(('"' || table_catalog || '"."' || table_schema || '"."' || table_name || '"')::regclass::oid, ordinal_position) AS description,
                  ('"' || "udt_schema" || '"."' || "udt_name" || '"')::"regtype" AS "regtype",
                  pg_catalog.format_type("col_attr"."atttypid", "col_attr"."atttypmod") AS "format_type"
                  FROM "information_schema"."columns"
                  LEFT JOIN "pg_catalog"."pg_attribute" AS "col_attr"
                  ON "col_attr"."attname" = "columns"."column_name"
                  AND "col_attr"."attrelid" = (
                    SELECT
                      "cls"."oid" FROM "pg_catalog"."pg_class" AS "cls"
                      LEFT JOIN "pg_catalog"."pg_namespace" AS "ns"
                      ON "ns"."oid" = "cls"."relnamespace"
                    WHERE "cls"."relname" = "columns"."table_name"
                    AND "ns"."nspname" = "columns"."table_schema"
                  )
                WHERE
                ("table_schema" = 'public' AND "table_name" = 'submission') OR ("table_schema" = 'public' AND "table_name" = 'form') OR ("table_schema" = 'public' AND "table_name" = 'user');

    ๐Ÿ‘‰ You can file an issue at https://github.com/oguimbal/pg-mem along with a way to reproduce this error (if you can), and  the stacktrace:

      at new QueryFailedError (../../src/error/QueryFailedError.ts:9:9)
      at PostgresQueryRunner.<anonymous> (../../src/driver/postgres/PostgresQueryRunner.ts:228:19)
      at step (../../node_modules/typeorm/node_modules/tslib/tslib.js:141:27)
      at Object.throw (../../node_modules/typeorm/node_modules/tslib/tslib.js:122:57)
      at rejected (../../node_modules/typeorm/node_modules/tslib/tslib.js:113:69)

Error when running Create Table Script

In pgAdmin I generate a Create Table Script off my table then try to use it with your lib:

const pg = newDb().adapters.createPgPromise();
const client = await pg.connect();
const crafterTable = await client.none(`CREATE TABLE public.crafter
(
    crafter_name_first character varying COLLATE pg_catalog."default" NOT NULL,
    crafter_name_last character varying COLLATE pg_catalog."default" NOT NULL,
)

TABLESPACE pg_default;

ALTER TABLE public.crafter
    OWNER to postgres;

GRANT ALL ON TABLE public.crafter TO postgres;

GRANT SELECT ON TABLE public.crafter TO wedotdd_readonly_crafter_id_fistname_lastname;

GRANT SELECT(crafter_name_first) ON public.crafter TO wedotdd_readonly_crafter_id_fistname_lastname;

GRANT SELECT(crafter_name_last) ON public.crafter TO wedotdd_readonly_crafter_id_fistname_lastname;

GRANT SELECT(crafter_id) ON public.crafter TO wedotdd_readonly_crafter_id_fistname_lastname;`);

but, I end up with errors like this, not sure how to resolve this, or if your lib can handle this at all?:

Error: Syntax error at line 4 col 41:

      crafter_name_last character varying COLLATE
                                          ^
Unexpected kw_collate token: "COLLATE". Instead, I was expecting to see one of the following:


A lparen token based on:
    lparen โ†’  โ— %lparen
    data_type$ebnf$1$subexpression$1 โ†’  โ— lparen int rparen
    data_type$ebnf$1 โ†’  โ— data_type$ebnf$1$subexpression$1
    data_type โ†’ data_type_simple โ— data_type$ebnf$1 data_type$ebnf$2
    createtable_column โ†’ word โ— data_type createtable_column$ebnf$1 createtable_column$ebnf$2
    createtable_declaration$subexpression$1 โ†’  โ— createtable_column
    createtable_declaration โ†’  โ— createtable_declaration$subexpression$1
    createtable_declarationlist$ebnf$1$subexpression$1 โ†’ comma โ— createtable_declaration
    createtable_declarationlist$ebnf$1 โ†’ createtable_declarationlist$ebnf$1 โ— createtable_declarationlist$ebnf$1$subexpression$1
    createtable_declarationlist โ†’ createtable_declaration โ— createtable_declarationlist$ebnf$1
    createtable_statement โ†’ %kw_create %kw_table createtable_statement$ebnf$1 word lparen โ— createtable_declarationlist rparen
    statement โ†’  โ— createtable_statement
    main โ†’ main$ebnf$1 โ— statement main$ebnf$2 main$ebnf$3

Support for CREATE SCHEMA

Not a big deal, but we do use some custom schemas.
We might try a PR if you'd be OK with that?

CREATE SCHEMA something;

Types

Might want to throw into documentation that you need to import using import { newDb } from 'pg-mem/src'; if you want types to work properly.

Setup Example - but still some issues

this works, just showing a full example now.

InMemoryDB.ts

import path from 'path';
import { IBackup, IMemoryDb, newDb } from 'pg-mem';

let db: IMemoryDb;
let backup: IBackup;

export async function getDb(): Promise<IMemoryDb> {
  if (backup) {
    backup.restore();
    return db;
  }

  db = newDb();
  logFailedSQLStatements(db);
  await db.public.migrate({
    migrationsPath: path.join(__dirname, 'migrations'),
  });
  backup = db.backup();
  return db;
}

function logFailedSQLStatements(db) {
  db.on('query-failed', (sql) => {
    console.log('sql statement failed: ', sql);
  });
}

DB.ts

import { getDb } from './pgMem/InMemoryDB';

...
async function queryInMemoryDB(queryTypeId, query) {
  let res;
  const db = await getDb();

  if (queryTypeId === queryType.MANY) {
    res = await db.public.many(query);
  }

  if (queryTypeId === queryType.ONE) {
    res = await db.public.one(query);
  }
  return res;
}

export default {
  queryDB,
  queryType,
};

In my tests I'm using it as so:

beforeEach(async () => {
  const db = await getDb();
  db.backup().restore(); // had to call backup() to get to restore() now
  ...
});

Support Sequelize : Must implement cross joins (carthesian products)

Sequelize is generating requests using "comma-join-syntax", which is currently not supported.

๐Ÿ‘‰ Failed query:

    SELECT i.relname AS name, ix.indisprimary AS primary, ix.indisunique AS unique, ix.indkey AS indkey, array_agg(a.attnum) as column_indexes, array_agg(a.attname) AS column_names, pg_get_indexdef(ix.indexrelid) AS definition FROM pg_class t, pg_class i, pg_index ix, pg_attribute a WHERE t.oid = ix.indrelid AND i.oid = ix.indexrelid AND a.attrelid = t.oid AND t.relkind = 'r' and t.relname = 'Users' GROUP BY i.relname, ix.indexrelid, ix.indisprimary, ix.indisunique, ix.indkey ORDER BY i.relname;;

๐Ÿ’€ Syntax error at line 1 col 239:

  SELECT i.relname AS name, ix.indisprimary AS primary, ix.indisunique AS unique, ix.indkey AS indkey, array_agg(a.attnum) as column_indexes, array_agg(a.attname) AS column_names, pg_get_indexdef(ix.indexrelid) AS definition FROM pg_class t,
                                                                                                                                                                                                                                                ^
Unexpected comma token: ",". Instead, I was expecting to see one of the following:

This syntax is not supported as of today because it was kind of harder to implement, since it can lead to cross-joins (carthesian products).

=> This must be done.

Unit test stub for sequelize: src/tests/sequelize-real.spec.ts (currently ignored)

Read .sql file to seed Database

I have a need to seed the database when running my app locally.

My initial migration doesn't add a certain set of records because I do not want my tests to see those. But when I run the app locally, I do want to insert that missing set of record so that when I view the app in a browser, they're there.

So it'd be nice to do something like:

const db = newDb();
inMemoryDb.public.none(fs.readFileSync(path.resolve(__dirname, 'migrations/001-initial.sql'), 'utf8'));

That's great for initializing the DB for tests. But for my App.ts when I run it with webpack, I need to be able to add additional seeds, something like this:

db.public.readSQLFile('seed.crafters.sql');

Do you have a way to do this somehow?

Issue with registerFunction

So I've been adding more tests to a project of mine, and I'm getting this error:

Unique constraint violated while adding a record to index PK_cace4a159ff9f2512dd42373760

This error is thrown when I try to create a second entry (with a uuid) in my test environment.

Here's the registerFunction call I use to implement uuid generation:

    mem.registerExtension('uuid-ossp', (schema) => {
      schema.registerFunction({
        name: 'uuid_generate_v4',
        returns: DataType.uuid,
        implementation: v4,
      });
    });

It appears to me that the uuid is generated on table creation (and used for every item inserted), not on insertion, which can become problematic when more complex relations are tested.

Why not install-less approach?

Since you asked a question about supporting it in pg-promise, I want to ask a question back at you...

Would it not be more sensible to provide automation around install-less testing?

There are ways, you know, to run PostgreSql without installing anything. It would be 100% consistent with a proper server, and not require all that complexity of emulating a PostgreSql server.

Support for RAISE and COMMENT

These might not be used by too many people, but we won't mind adding them if you're OK with that?

RAISE NOTICE 'Some message here';
CREATE TABLE groups (members VARCHAR);
COMMENT ON COLUMN groups.members is 'Nice column you have there';

"insert into select" support (todo: array-mode iteration error)

This is code express-session appears to use. Support for this would certainly be nice ๐Ÿ‘ although it seems a bit complicated. I'm not sure what the point of an inner SELECT query is here since it is not actually looking up values from elsewhere but just providing them directly, but somebody better versed in SQL might know why this is clever.

Error: todo: array-mode iteration

*๏ธโƒฃ Failed SQL statement: INSERT INTO "express_server_sessions" (sess, expire, sid) SELECT '{"cookie":{"originalMaxAge":1296000000,"expires":"2021-01-29T15:49:27.925Z","secure":true,"httpOnly":true,"path":"/"},"passport":{"user":653646}}', to_timestamp(1611935368), 'jCUSxo0EnzcIeCQYUgt6g3FQCoIO6Q3S' ON CONFLICT (sid) DO UPDATE SET sess='{"cookie":{"originalMaxAge":1296000000,"expires":"2021-01-29T15:49:27.925Z","secure":true,"httpOnly":true,"path":"/"},"passport":{"user":653646}}', expire=to_timestamp(1611935368) RETURNING sid;

PS: to run this code in the first place I had to work around the lack of to_timestamp:

const pgMem = require('pg-mem');
const mockDb = pgMem.newDb();


mockDb.public.registerFunction({
  name: 'to_timestamp',
  args: [pgMem.DataType.integer],
  returns: pgMem.DataType.timestampz,
  implementation: input => {
    return new Date(input * 1000).toISOString();
  },
});

How to get connection details for Knex.js

I'm using knex.js to connect to my Postgres DB, but in testing, I would like to connect to the pg-mem DB. For that, I think I need to know the connection details or connection string. This is what knex.js uses

  connection: {
    user: DB_USER,
    password: DB_PASS,
    database: DB_NAME,
    host: DB_HOST,
    port: DB_PORT,
  },

Is there a way to get these details from a pg-mem instance so knex will use that instead?

Missing support for type: timestamp with time zone

Specification: https://www.postgresql.org/docs/current/functions-datetime.html

https://oguimbal.github.io/pg-mem-playground/

CREATE TABLE  test ("timestamp" TIMESTAMP WITH TIME ZONE NOT NULL);

type "timestamp with time zone" does not exist

๐Ÿœ This seems to be an execution error, which means that your request syntax seems okay,
but the resulting statement cannot be executed โ†’ Probably not a pg-mem error.

*๏ธโƒฃ Failed SQL statement: CREATE TABLE  test ("timestamp" TIMESTAMP WITH TIME ZONE NOT NULL);;

Cannot run Insert Statement based on Select

So I can't do Unexpected kw_do token: "do"
And I can't do Support for aggregate function max

And now looking for a way to dumb this down so that pg-mem can actually run some migrations I must be able to run, but I can't run any of these dumbed down statements either:

INSERT INTO community (name,community_id)
select 'Atlanta', location_id from location where name = 'Atlanta, GA';

or

INSERT INTO community (name,community_id) (select 'Atlanta', location_id from location where name = 'Atlanta, GA');

or

INSERT INTO community (name,community_id) ((select 'Atlanta', location_id from location where name = 'Atlanta, GA'));

or

INSERT INTO community (name,community_id)
VALUES ((select 'Alanta' as name), (select 'Atlanta', location_id from location where name = 'Atlanta, GA'));

or

INSERT INTO community (name,community_id) VALUES
(('Atlanta'), (select 'Atlanta', location_id from location where name = 'Atlanta, GA'));

Example error (got this on the first example above) :

Error: todo: array-mode iteration
๐Ÿ’ฅ This is a nasty error, which was unexpected by pg-mem. Also known "a bug" ๐Ÿ˜ Please file an issue !

*๏ธโƒฃ Failed SQL statement: INSERT INTO city (name, state_id)
select 'Atlanta', select 'Atlanta', location_id from location where name = 'Atlanta, GA';

So what do I do now? How can I get this insert working? This isn't an advanced thang.

I've got to be able to run these at the very least if you don't support PL/pgSQL yet, this is the only alternative I can think of to get my stuff hopefully to continue to work in pg-mem in the dumbest way possible.

Unexpected word token: "generated" when parsing sql

Not sure how to fix this during parsing. I took at pg_dump of my prod database, removed the roles, etc. and only left table creation and constraints in the dump, but I get this error

ALTER TABLE public.city ALTER COLUMN city_id ADD GENERATED
                                                                                                                                     ^
Unexpected word token: "generated". I did not expect any more input

for this part of the script:

ALTER TABLE public.city ALTER COLUMN city_id ADD GENERATED ALWAYS AS IDENTITY (
  SEQUENCE NAME public.city_city_id_seq
  START WITH 0
  INCREMENT BY 1
  MINVALUE 0
  NO MAXVALUE
  CACHE 1
);

Also got an error on this

ADD CONSTRAINT city_id_fk FOREIGN KEY (city_id) REFERENCES public.
                                                                       ^
Unexpected dot token: ".". Instead, I was expecting to see one of the following:

- A "lparen" token

for:

ALTER TABLE ONLY public.location
    ADD CONSTRAINT city_id_fk FOREIGN KEY (city_id) REFERENCES public.city(city_id) MATCH FULL;

Type "regclass"

This type is specific to table relations and casting to OIDs for internal relations. I believe this type is used by TypeORM for the relationship decorators.

Is there a possibility of this being implemented in the future?

Error for reference:
Error: Not supported: Type "regclass"
          at Object.fromNative (/home/bobby/phorm-new/node_modules/pg-mem/src/datatypes.ts:858:19)
          at _buildValueReal (/home/bobby/phorm-new/node_modules/pg-mem/src/predicate.ts:82:26)
          at _buildValue (/home/bobby/phorm-new/node_modules/pg-mem/src/predicate.ts:33:11)
          at _buildValueReal (/home/bobby/phorm-new/node_modules/pg-mem/src/predicate.ts:81:20)
          at _buildValue (/home/bobby/phorm-new/node_modules/pg-mem/src/predicate.ts:33:11)
          at /home/bobby/phorm-new/node_modules/pg-mem/src/predicate.ts:77:44
          at Array.map (<anonymous>)
          at _buildValueReal (/home/bobby/phorm-new/node_modules/pg-mem/src/predicate.ts:77:35)
          at _buildValue (/home/bobby/phorm-new/node_modules/pg-mem/src/predicate.ts:33:11)
          at Object.buildValue (/home/bobby/phorm-new/node_modules/pg-mem/src/predicate.ts:16:12)
          at new Selection (/home/bobby/phorm-new/node_modules/pg-mem/src/transforms/selection.ts:132:28)
          at Object.buildSelection (/home/bobby/phorm-new/node_modules/pg-mem/src/transforms/selection.ts:30:12)
          at OrFilter.select (/home/bobby/phorm-new/node_modules/pg-mem/src/transforms/transform-base.ts:46:20)
          at Query.buildSelect (/home/bobby/phorm-new/node_modules/pg-mem/src/query.ts:399:23)
          at Query.queries (/home/bobby/phorm-new/node_modules/pg-mem/src/query.ts:98:47)
          at queries.next (<anonymous>)
          at Query.query (/home/bobby/phorm-new/node_modules/pg-mem/src/query.ts:42:20)
          at /home/bobby/phorm-new/node_modules/pg-mem/src/adapters.ts:81:80
          at Timeout.task [as _onTimeout] (/home/bobby/phorm-new/node_modules/jsdom/lib/jsdom/browser/Window.js:391:19)
          at listOnTimeout (internal/timers.js:554:17)
          at processTimers (internal/timers.js:497:7)

Parsing problem with array values

Hi there,

Thank you for creating this package. It is great, and I'd love to use it in my tests. I have encountered the following issue with array values:

https://github.com/oguimbal/pg-mem/blob/master/src/tests/pg.spec.ts#L43 demonstrates running the following query:
const got = await client.query('select * from data where id = $1', ['str']);

However if I change that query to the following, then I get a syntax error and the test fails:
const got = await client.query('select * from data where id = ANY($1)', [['str']]);

I believe the issue relates to how javascript arrays are being treated in https://github.com/oguimbal/pg-mem/blob/master/src/adapters.ts#L11 and https://github.com/oguimbal/pg-mem/blob/master/src/pg-escape.ts#L3.

If you compare with how javascript array values are handled in node-postgres, you can see a difference:
https://github.com/brianc/node-postgres/blob/master/packages/pg/lib/utils.js#L40
https://github.com/brianc/node-postgres/blob/master/packages/pg/lib/utils.js#L16

How could I best help contribute to addressing this issue?

pg-promise and exporting Schemas Automatically

Context:

  • I am not using nor do I want to use an ORM
  • I need an in-memory DB that pulls schemas exported somehow from my real DB and creates the tables

Is there a way for me to:
1) Export my schemas from my postgres DB in a format your library will take and run with that creates the tables automatically for me?

I looked at your examples but none of them talks about some way to do what I'm saying without using an ORM.

Like does yours do something like sqlite has:

https://sqlite.org/schematab.html

Views support

I encountered the following problem:

๐Ÿ’€ Syntax error at line 14 col 13:

  CREATE VIEW fleets_for_cc_users
              ^
Unexpected word token: "fleets_for_cc_users". I did not expect any more input. Here is the state of my parse table:

Does it support the creation of views?

Insert Statement Fails with no Useful Error

I'm having a hard time troubleshooting this.

While I can log any failed sql statements that pg-mem via inMemoryDb.on('query-failed', it doesn't tell me what the problem is exactly.

This query was successfully migrated to my production database using flyway. This same kind of statement also ran ok in my 001 migration but just not my 003 for this community table insert which is really odd.

Here's a screencast on this: https://youtu.be/yaWBzsUtJbE

(note: I've replaced the real strings with x's for privacy for this post)

INSERT INTO public.community (name,image_url,twitter_url,coordinates,meetup_url,main_site_url,slack_url,facebook_url,github_url,eventbrite_url,linkedin_url) VALUES ('xxxxxxxxxx','xxxxxxxxxxx',NULL,'(33.7676338,-84.6176515)','xxxxxxxxxx',NULL,NULL,NULL,NULL,NULL,NULL);

Also tried

INSERT INTO community VALUES ('Software Crafters - Atlanta','https://storage.googleapis.com/wedotdd-client-assets/assets/communities/assets_communities_software-crafters-atlanta.png',NULL,point(33.7676338,-84.6176515),'https://www.meetup.com/Software-Craftsmanship-Atlanta',NULL,NULL,NULL,NULL,NULL,NULL);

Now the initial migration that creates the schema looks like this for my pg-mem 001 migration for the community table:

CREATE TABLE community (
    community_id integer NOT NULL,
    name character varying NOT NULL,
    image_url character varying,
    twitter_url character varying,
    coordinates point,
    meetup_url character varying,
    main_site_url character varying,
    slack_url character varying,
    facebook_url character varying,
    github_url character varying,
    eventbrite_url character varying,
    linkedin_url character varying
);

ALTER TABLE community ALTER COLUMN community_id ADD GENERATED ALWAYS AS IDENTITY (
    SEQUENCE NAME community_community_id_seq
    START WITH 0
    INCREMENT BY 1
    MINVALUE 0
    NO MAXVALUE
    CACHE 1
);

The only thing to note in addition is that in my 001 migration related to community at the end of that script is:

ALTER TABLE ONLY community_location ADD CONSTRAINT community_country_pkey PRIMARY KEY (community_id, location_id);
ALTER TABLE ONLY community ADD CONSTRAINT community_pkey PRIMARY KEY (community_id);
ALTER TABLE ONLY location ADD CONSTRAINT location_pkey PRIMARY KEY (location_id);
ALTER TABLE ONLY skill_type ADD CONSTRAINT skill_type_pkey PRIMARY KEY (skill_type_id);
ALTER TABLE ONLY skills ADD CONSTRAINT skills_pkey PRIMARY KEY (skill_id);
ALTER TABLE ONLY location ADD CONSTRAINT city_id_fk FOREIGN KEY (city_id) REFERENCES city(city_id) MATCH FULL;
ALTER TABLE ONLY community_location ADD CONSTRAINT community_id_fk FOREIGN KEY (community_id) REFERENCES community(community_id) MATCH FULL;
ALTER TABLE ONLY location ADD CONSTRAINT country_id_fk FOREIGN KEY (country_id) REFERENCES country(country_id) MATCH FULL;
ALTER TABLE ONLY community_location ADD CONSTRAINT location_id_fk FOREIGN KEY (location_id) REFERENCES location(location_id) MATCH FULL;
ALTER TABLE ONLY location ADD CONSTRAINT state_id_fk FOREIGN KEY (state_id) REFERENCES state(state_id) MATCH FULL;

If I disable those ALTER statements at then end then change the script to include the community_id, then this works (noice the ID 31):

INSERT INTO community VALUES (31, 'xxxxx', 'xxxxx', NULL, '(-77.03059944399082,-12.045945732454783)', 'xxxxx', NULL, NULL, NULL, NULL, NULL, NULL);

But..I should not have to disable those alter statements AND I shouldn't have to specify a community_id in my insert because I've turned on ADD GENERATED ALWAYS AS IDENTITY which will create the ID for me automatically.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.