From d4105bae43c452d201cedd599d0a8bc400a370f6 Mon Sep 17 00:00:00 2001
From: daniel-le97 <107774403+daniel-le97@users.noreply.github.com>
Date: Thu, 7 Nov 2024 20:26:49 -0700
Subject: [PATCH 01/13] v orm implementation with standard query
---
Makefile | 3 +
examples/orm.v | 62 +++++++
vsql/orm.v | 448 ++++++++++++++++++++++++++++++++++++++++++++++++
vsql/orm_test.v | 319 ++++++++++++++++++++++++++++++++++
4 files changed, 832 insertions(+)
create mode 100644 examples/orm.v
create mode 100644 vsql/orm.v
create mode 100644 vsql/orm_test.v
diff --git a/Makefile b/Makefile
index a194e11..b499469 100644
--- a/Makefile
+++ b/Makefile
@@ -87,6 +87,9 @@ btree-test: oldv
sql-test: oldv
$(V) -stats $(BUILD_OPTIONS) vsql/sql_test.v
+orm-test: oldv
+ $(V) -stats $(BUILD_OPTIONS) vsql/orm_test.v
+
# CLI Tests
cli-test: bin/vsql
diff --git a/examples/orm.v b/examples/orm.v
new file mode 100644
index 0000000..55b90a9
--- /dev/null
+++ b/examples/orm.v
@@ -0,0 +1,62 @@
+import os
+import vsql
+import time
+
+fn main() {
+ os.rm('test.vsql') or {}
+
+ example() or { panic(err) }
+}
+
+// NOTE for some reason if we declare a @[primary] on a struct field, we can not do delete queries on the tables...
+// so id is not a primary key in this example
+struct Product {
+ id int //@[primary]
+ product_name string @[sql_type: 'varchar(100)']
+ price f64
+}
+
+fn example() ! {
+ timer := time.new_stopwatch()
+ mut db := vsql.open('test.vsql')!
+
+ sql db {
+ create table Product
+ }!
+
+ products := [
+ Product{1, 'Ice Cream', 5.99},
+ Product{2, 'Ham Sandwhich', 3.47},
+ Product{3, 'Bagel', 1.25},
+ ]
+
+ // product := Product{1, 'Ice Cream', 5.99}
+ for product in products {
+ sql db {
+ insert product into Product
+ } or { panic(err) }
+ }
+ sql db {
+ update Product set product_name = 'Cereal' where id == 1
+ } or { panic(err) }
+
+ prod_one := sql db {
+ select from Product where id == 1
+ }!
+
+ assert prod_one.len == 1
+
+ sql db {
+ delete from Product where product_name == 'Cereal'
+ } or { panic(err) }
+
+ all := sql db {
+ select from Product
+ }!
+
+ assert all.len == 2
+
+ println(timer.elapsed())
+ // println(typeof[?int]().idx)
+ // println(typeof[int]().idx)
+}
diff --git a/vsql/orm.v b/vsql/orm.v
new file mode 100644
index 0000000..77a8029
--- /dev/null
+++ b/vsql/orm.v
@@ -0,0 +1,448 @@
+module vsql
+
+import orm
+import time
+
+pub const varchar_default_len = 255
+
+// DDL (table creation/destroying etc)
+fn vsql_type_from_v(typ int) !string {
+ return if typ == orm.type_idx['i8'] || typ == orm.type_idx['i16'] || typ == orm.type_idx['u8'] {
+ 'SMALLINT'
+ } else if typ == orm.type_idx['bool'] {
+ 'BOOLEAN'
+ } else if typ == orm.type_idx['int'] || typ == orm.type_idx['u16'] || typ == 8 {
+ 'INT'
+ } else if typ == orm.type_idx['i64'] || typ == orm.type_idx['u32'] {
+ 'BIGINT'
+ } else if typ == orm.type_idx['f32'] {
+ 'REAL'
+ } else if typ == orm.type_idx['u64'] {
+ // 'NUMERIC(20)'
+ 'BIGINT'
+ } else if typ == orm.type_idx['f64'] {
+ 'DOUBLE PRECISION'
+ } else if typ == orm.type_idx['string'] {
+ 'VARCHAR(${varchar_default_len})'
+ } else if typ == typeof[time.Time]().idx || typ == -2 {
+ // time.Time will be converted to a string
+ // will be parsed as a time.Time when read
+ 'TIMESTAMP(6) WITHOUT TIME ZONE'
+ } else if typ == -1 {
+ // SERIAL
+ 'INTEGER'
+ } else {
+ error('Unknown type ${typ}')
+ }
+}
+
+// fn get_prepared_args(query_data []orm.QueryData) map[string]Value {
+// mut mp := map[string]Value{}
+// for i , q in query_data {
+// // for f in q.fields {
+// // type_idx :=
+// // }
+// // for j, v in f.data {
+// // mp[':${i}_${j}'] = v
+// // }
+// }
+// return mp
+// }
+
+// `query_converter_lite` converts a statement like `INSERT INTO Product (id, product_name, price) VALUES (:1, :2, :3)` to `INSERT INTO Product (id, product_name, price) VALUES (:id, :product_name, :price)`
+fn query_converter_lite(query string, query_data []orm.QueryData) !string {
+ mut counter := 1
+ mut new_query := query
+ for data in query_data {
+ for field in data.fields {
+ new_query = new_query.replace(':${counter}', ':${field}')
+ counter++
+ }
+ }
+ return new_query
+}
+
+fn query_converter(query string, query_data []orm.QueryData) !string {
+ mut counter := 1
+ mut new_query := query
+
+ for data in query_data {
+ vals := primitive_array_to_string_array(data.data)
+ for val in vals {
+ new_query = new_query.replace(':${counter}', val)
+ counter++
+ }
+ }
+
+ return new_query
+}
+
+fn primitive_array_to_string_array(prims []orm.Primitive) []string {
+ mut values := prims.map(fn (p orm.Primitive) string {
+ match p {
+ orm.InfixType {
+ // TODO(elliotchance): Not sure what this is?
+ return '${p}'
+ }
+ time.Time {
+ //
+ return 'TIMESTAMP \'${p}\''
+ // return '${p}'
+ }
+ orm.Null {
+ return 'NULL'
+ }
+ bool {
+ if p {
+ return 'TRUE'
+ }
+
+ return 'FALSE'
+ }
+ string {
+ return '\'${p}\''
+ }
+ f32 {
+ return '${p}'
+ }
+ f64 {
+ return '${p}'
+ }
+ i16 {
+ return '${p}'
+ }
+ i64 {
+ return '${p}'
+ }
+ i8 {
+ return '${p}'
+ }
+ int {
+ return '${p}'
+ }
+ u16 {
+ return '${p}'
+ }
+ u32 {
+ return '${p}'
+ }
+ u64 {
+ return '${p}'
+ }
+ u8 {
+ return '${p}'
+ }
+ }
+ })
+ return values
+}
+
+// select is used internally by V's ORM for processing `SELECT` queries
+pub fn (mut db Connection) select(config orm.SelectConfig, data orm.QueryData, where orm.QueryData) ![][]orm.Primitive {
+ // 1. Create query and bind necessary data
+ // println(orm.type_idx)
+ mut query := orm.orm_select_gen(config, '', true, ':', 1, where)
+ query = query_converter(query, [data, where])!
+ rows := db.query(query)!
+ mut ret := [][]orm.Primitive{}
+ for row in rows {
+ mut row_primitives := []orm.Primitive{}
+ keys := row.data.keys()
+ for idx, key in keys {
+ prim := row.get(key)!
+ type_idx := config.types[idx]
+
+ // check orm.type_idx for what these numbers are
+ if type_idx == 5 {
+ row_primitives << i8(prim.int_value())
+ } else if type_idx == 2 {
+ row_primitives << time.parse(prim.string_value())!
+ } else if type_idx == 6 {
+ row_primitives << i16(prim.int_value())
+ } else if type_idx == 8 {
+ row_primitives << int(prim.int_value())
+ } else if type_idx == 9 {
+ row_primitives << prim.int_value()
+ } else if type_idx == 11 {
+ row_primitives << u8(prim.int_value())
+ } else if type_idx == 12 {
+ row_primitives << u16(prim.int_value())
+ } else if type_idx == 13 {
+ row_primitives << u32(prim.int_value())
+ } else if type_idx == 14 {
+ row_primitives << u64(prim.int_value())
+ } else if type_idx == 16 {
+ row_primitives << f32(prim.f64_value())
+ } else if type_idx == 17 {
+ row_primitives << prim.f64_value()
+ } else if type_idx == 19 {
+ row_primitives << prim.bool_value() == .is_true
+ } else {
+ row_primitives << prim.string_value()
+ }
+ }
+ ret << row_primitives
+ }
+ // println(ret)
+ return ret
+}
+
+fn serial_name(table string) string {
+ return '${table}_SERIAL'
+}
+
+fn get_table_columns(mut db Connection, table string, data []orm.QueryData) !map[string]Value {
+ mut mp := map[string]Value{}
+ mut tbl := Table{}
+ mut cat := db.catalog()
+ tables := cat.schema_tables('PUBLIC') or { [] }
+ for t in tables {
+ if t.name.entity_name == table.to_upper() {
+ tbl = t
+ break
+ }
+ }
+ if tbl.name.entity_name != table.to_upper() {
+ return error('Table ${table} not found')
+ }
+ for d in data {
+ for i, f in d.fields {
+ for c in tbl.columns {
+ if c.name.sub_entity_name == f.to_upper() {
+ mp[f] = primitive_to_value(c.typ, d.data[i])!
+ }
+ }
+ }
+ }
+
+ return mp
+}
+
+// insert is used internally by V's ORM for processing `INSERT` queries
+pub fn (mut db Connection) insert(table string, data orm.QueryData) ! {
+ // println(data)
+ // mut tbl := get_table_columns(mut db, table, [data]) or { return err }
+ // println(tbl)
+
+ mut values := primitive_array_to_string_array(data.data)
+ if data.auto_fields.len > 1 {
+ return error('multiple AUTO fields are not supported')
+ } else if data.auto_fields.len == 1 {
+ // println(data)
+ values[data.auto_fields[0]] = 'NEXT VALUE FOR "${serial_name(table)}"'
+ }
+ mut nums := []string{}
+ for i, _ in data.fields {
+ nums << '${i}'
+ }
+ insert_sql := 'INSERT INTO ${table} (${data.fields.join(', ')}) VALUES (${values.join(', ')})'
+ println(insert_sql)
+ // println(tbl)
+ $if trace_vsql_orm ? {
+ eprintln('> vsql insert: ${query}')
+ }
+ db.query(insert_sql) or { return err }
+ // mut stmt := db.prepare(insert_sql) or { return err }
+ // stmt.query(tbl) or { return err }
+}
+
+// update is used internally by V's ORM for processing `UPDATE` queries
+pub fn (mut db Connection) update(table string, data orm.QueryData, where orm.QueryData) ! {
+ mut query, _ := orm.orm_stmt_gen(.sqlite, table, '', .update, true, ':', 1, data,
+ where)
+
+ // values := get_table_columns(mut db, table, [data, where]) or { return err }
+ query = query_converter(query, [data, where])!
+ println(query)
+
+ $if trace_vsql_orm ? {
+ eprintln('> vsql update: ${query}')
+ }
+ db.query(query) or { return err }
+ // mut stmt := db.prepare(query) or { return err }
+ // stmt.query(values) or { return err }
+}
+
+// delete is used internally by V's ORM for processing `DELETE ` queries
+pub fn (mut db Connection) delete(table string, where orm.QueryData) ! {
+ mut query, _ := orm.orm_stmt_gen(.sqlite, table, '', .delete, true, ':', 1, orm.QueryData{},
+ where)
+
+ query = query_converter(query, [where])!
+ // values := get_table_columns(mut db, table, [where]) or { return err }
+
+ $if trace_vsql_orm ? {
+ eprintln('> vsql delete: ${query}')
+ }
+ db.query(query) or { return err }
+ // mut stmt := db.prepare(query) or { return err }
+ // stmt.query(values) or { return err }
+}
+
+// `last_id` is used internally by V's ORM for post-processing `INSERT` queries
+// TODO i dont think vsql supports this
+pub fn (mut db Connection) last_id() int {
+ return 0
+}
+
+// create is used internally by V's ORM for processing table creation queries (DDL)
+pub fn (mut db Connection) create(table string, fields []orm.TableField) ! {
+ check_for_not_supported(mut db, table, fields) or { return err }
+ mut new_table := table
+ if is_reserved_word(table) {
+ new_table = '"${table}"'
+ }
+ mut query := orm.orm_table_gen(new_table, '', true, 0, fields, vsql_type_from_v, false) or {
+ return err
+ }
+ // 'IF NOT EXISTS' is not supported in vsql, so we remove it
+ query = query.replace(' IF NOT EXISTS ', ' ')
+ // `TEXT` is not supported in vsql, so we replace it with `VARCHAR(255) if its somehow used`
+ query = query.replace('TEXT', 'VARCHAR(${varchar_default_len})')
+
+ $if trace_vsql_orm ? {
+ eprintln('> vsql create: ${query}')
+ }
+ // println(query)
+ // println(table)
+ // println(fields)
+ db.query(query) or { return err }
+}
+
+// drop is used internally by V's ORM for processing table destroying queries (DDL)
+pub fn (mut db Connection) drop(table string) ! {
+ query := 'DROP TABLE ${table};'
+ $if trace_vsql_orm ? {
+ eprintln('> vsql drop: ${query}')
+ }
+
+ db.query(query) or { return err }
+
+ // // check to see if there is a SEQUENCE for the table (for the @[sql: 'serial'] attribute)
+ db.query('EXPLAIN DROP SEQUENCE "${serial_name(table)}"') or { return }
+ // if we have not returned then we can drop the sequence
+ db.query('DROP SEQUENCE "${serial_name(table)}"') or { return err }
+}
+
+fn check_for_not_supported(mut db Connection, table string, fields []orm.TableField) ! {
+ for field in fields {
+ if field.typ == orm.enum_ {
+ return error('enum is not supported in vsql')
+ }
+ if is_reserved_word(field.name) {
+ return error('reserved word ${field.name} cannot be used as a field name at ${table}.${field.name}')
+ }
+ for attr in field.attrs {
+ if attr.name == 'sql' {
+ if attr.arg == 'serial' {
+ db.query('CREATE SEQUENCE "${serial_name(table)}"')!
+ }
+ if is_reserved_word(attr.arg) {
+ return error('${attr.arg} is a reserved word in vsql')
+ }
+ }
+ if attr.name == 'default' {
+ return error('default is not supported in vsql')
+ }
+ if attr.name == 'unique' {
+ return error('unique is not supported in vsql')
+ }
+ if attr.name == 'primary' {
+ eprintln('primary is supported, but currently will break delete queries')
+ // return error('primary is supported, but currently will break delete queries')
+ }
+ }
+ }
+}
+
+// primitive_to_value returns the Value of a Primitive based on the intended
+// destination type. Primitives are used by the ORM.
+//
+// It's important to note that while types may be compatible, they can still be
+// out of range, such as assigning an overflowing integer value to SMALLINT.
+fn primitive_to_value(typ Type, p orm.Primitive) !Value {
+ // The match should be exhaustive for typ and p so that we can make sure we
+ // cover all combinations now and in the future.
+ match p {
+ orm.Null {
+ // In standard SQL, NULL's must be typed.
+ return new_null_value(typ.typ)
+ }
+ bool {
+ match typ.typ {
+ .is_boolean {
+ return new_boolean_value(p)
+ }
+ else {}
+ }
+ }
+ f32, f64 {
+ match typ.typ {
+ .is_real {
+ return new_real_value(f32(p))
+ }
+ .is_double_precision {
+ return new_double_precision_value(f64(p))
+ }
+ else {}
+ }
+ }
+ i16, i8, u8 {
+ match typ.typ {
+ .is_smallint {
+ return new_smallint_value(i16(p))
+ }
+ else {}
+ }
+ }
+ int, u16 {
+ match typ.typ {
+ .is_smallint {
+ return new_smallint_value(i16(p))
+ }
+ .is_integer {
+ return new_integer_value(int(p))
+ }
+ else {}
+ }
+ }
+ u32, i64 {
+ match typ.typ {
+ .is_bigint {
+ return new_bigint_value(i64(p))
+ }
+ else {}
+ }
+ }
+ u64 {
+ match typ.typ {
+ .is_smallint {
+ return new_smallint_value(i16(p))
+ }
+ else {}
+ }
+ }
+ string {
+ match typ.typ {
+ .is_varchar {
+ return new_varchar_value(p)
+ }
+ .is_numeric {
+ return new_numeric_value(p)
+ }
+ else {}
+ }
+ }
+ time.Time {
+ match typ.typ {
+ .is_timestamp_with_time_zone, .is_timestamp_without_time_zone {
+ return new_timestamp_value(p.str())!
+ }
+ else {}
+ }
+ }
+ orm.InfixType {}
+ }
+
+ return error('cannot assign ${p} to ${typ}')
+}
diff --git a/vsql/orm_test.v b/vsql/orm_test.v
new file mode 100644
index 0000000..03a19d1
--- /dev/null
+++ b/vsql/orm_test.v
@@ -0,0 +1,319 @@
+module vsql
+
+// Structs intentionally have less than 6 fields, any more then inserts queries get exponentially slower.
+import time
+
+// struct TestDateTypes {
+// id int @[primary; sql: serial]
+// custom1 string @[sql_type: 'TIME WITH TIME ZONE']
+// custom2 string @[sql_type: 'TIMESTAMP(3) WITH TIME ZONE']
+// custom3 string @[sql_type: 'INT']
+// custom4 string @[sql_type: 'DATE']
+// custom5 string @[sql_type: 'TIMESTAMP(3) WITHOUT TIME ZONE']
+// custom6 string @[sql_type: 'TIME WITHOUT TIME ZONE']
+// }
+
+@[table: 'testcustomtable']
+struct TestCustomTableAndSerial {
+ id int @[sql: serial]
+ an_bool bool
+}
+
+struct TestPrimaryBroken {
+ id int @[primary; sql: serial]
+}
+
+struct TestDefaultAttribute {
+ id int @[sql: serial]
+ name string
+ created_at string @[default: 'CURRENT_TIMESTAMP(6)'; sql_type: 'TIMESTAMP(3) WITHOUT TIME ZONE']
+}
+
+struct TestUniqueAttribute {
+ attribute string @[unique]
+}
+
+struct TestReservedWordField {
+ where string
+}
+
+struct TestReservedWordSqlAttribute {
+ ok string @[sql: 'ORDER']
+}
+
+struct TestOrmValuesOne {
+ an_f32 f32 // REAL
+ an_f64 f64 // DOUBLE PRECISION
+ an_i16 i16 // SMALLINT
+ an_i64 i64 // BIGINT
+}
+
+struct TestOrmValuesTwo {
+ an_i8 i8 // SMALLINT
+ an_int int // INTEGER
+ a_string string // CHARACTER VARYING(255)
+}
+
+struct TestOrmValuesThree {
+ an_u16 u16 // INTEGER
+ an_u32 u32 // BIGINT
+ an_u64 u64 // BIGINT
+ an_u8 u8 // SMALLINT
+}
+
+struct TestOrmValuesFour {
+ a_time time.Time // TIMESTAMP(6) WITH TIME ZONE
+ a_bool bool // BOOLEAN
+ int_or_null ?int // optional int
+}
+
+// ORMTableEnum is not supported.
+struct ORMTableEnum {
+ an_enum Colors
+}
+
+enum Colors {
+ red
+ green
+ blue
+}
+
+// @[table: 'GROUP']
+struct ORMTable2 {
+ dummy int
+}
+
+fn test_orm_create_success2() {
+ mut db := open(':memory:')!
+ sql db {
+ create table ORMTable2
+ }!
+ dumm := ORMTable2{dummy: 1}
+ sql db {
+ insert dumm into ORMTable2
+ }!
+
+ sql db {
+ drop table ORMTable2
+ }!
+}
+
+// We cannot test this because it will make all tests fail, because the database itself will run into an error
+// so it is put behind a -d flag if you really want to try it
+fn test_primary_key_broken() {
+ $if test_primary_key ? {
+ mut db := open(':memory:')!
+ mut error := ''
+ sql db {
+ create table TestPrimaryBroken
+ } or { error = err.str() }
+ assert error == ''
+
+ test_primary_broken := TestPrimaryBroken{}
+
+ sql db {
+ insert test_primary_broken into TestPrimaryBroken
+ } or { error = err.str() }
+ assert error == ''
+
+ mut all := sql db {
+ select from TestPrimaryBroken
+ }!
+
+ sql db {
+ delete from TestPrimaryBroken where id == all[0].id
+ } or { error = err.str() }
+ }
+}
+
+fn test_custom_table_name_and_serial_crud() {
+ mut db := open(':memory:')!
+ mut error := ''
+ sql db {
+ create table TestCustomTableAndSerial
+ } or { error = err.str() }
+ assert error == ''
+
+ test_custom_sql := TestCustomTableAndSerial{
+ an_bool: true
+ }
+ test_custom_sql2 := TestCustomTableAndSerial{
+ an_bool: false
+ }
+
+ sql db {
+ insert test_custom_sql into TestCustomTableAndSerial
+ insert test_custom_sql2 into TestCustomTableAndSerial
+ } or { error = err.str() }
+
+ assert error == ''
+ mut all := sql db {
+ select from TestCustomTableAndSerial
+ }!
+ assert all[0].id != 0
+
+ sql db {
+ update TestCustomTableAndSerial set an_bool = false where id == all[0].id
+ } or { error = err.str() }
+ assert error == ''
+
+ sql db {
+ delete from TestCustomTableAndSerial where id == all[1].id
+ } or { error = err.str() }
+ assert error == ''
+
+ all = sql db {
+ select from TestCustomTableAndSerial
+ }!
+ assert all.len == 1
+ assert all[0].an_bool == false
+
+ sql db {
+ drop table TestCustomTableAndSerial
+ } or { error = err.str() }
+
+ assert error == ''
+}
+
+fn test_reserved_words() {
+ mut db := open(':memory:')!
+ mut error := ''
+ sql db {
+ create table TestReservedWordField
+ } or { error = err.str() }
+ assert error == 'reserved word where cannot be used as a field name at TestReservedWordField.where'
+ sql db {
+ create table TestReservedWordSqlAttribute
+ } or { error = err.str() }
+ assert error == 'ORDER is a reserved word in vsql'
+}
+
+fn test_unsupported_attributes() {
+ mut db := open(':memory:')!
+ mut error := ''
+ sql db {
+ create table TestDefaultAttribute
+ } or { error = err.str() }
+ assert error == 'default is not supported in vsql'
+ sql db {
+ create table TestUniqueAttribute
+ } or { error = err.str() }
+ assert error == 'unique is not supported in vsql'
+}
+
+fn test_default_orm_values() {
+ mut db := open(':memory:')!
+ mut error := ''
+ sql db {
+ create table TestOrmValuesOne
+ create table TestOrmValuesTwo
+ create table TestOrmValuesThree
+ create table TestOrmValuesFour
+ } or { error = err.str() }
+ assert error == ''
+
+ values_one := TestOrmValuesOne{
+ an_f32: 3.14
+ an_f64: 2.718281828459
+ an_i16: 12345
+ an_i64: 123456789012345
+ }
+ values_two := TestOrmValuesTwo{
+ an_i8: 12
+ an_int: 123456
+ a_string: 'Hello, World!'
+ }
+ values_three := TestOrmValuesThree{
+ an_u16: 54321
+ an_u32: 1234567890
+ an_u64: 1234
+ an_u8: 255
+ }
+
+ values_four := TestOrmValuesFour{
+ a_time: time.now()
+ a_bool: true
+ // int_or_null: 123
+ }
+ values_four_b := TestOrmValuesFour{
+ a_time: time.now()
+ a_bool: false
+ int_or_null: 123
+ }
+
+ sql db {
+ insert values_one into TestOrmValuesOne
+ insert values_two into TestOrmValuesTwo
+ insert values_three into TestOrmValuesThree
+ insert values_four into TestOrmValuesFour
+ insert values_four_b into TestOrmValuesFour
+ } or { error = err.str() }
+ assert error == ''
+
+ result_values_one := sql db {
+ select from TestOrmValuesOne
+ }!
+ one := result_values_one[0]
+
+ assert typeof(one.an_f32).idx == typeof[f32]().idx
+ assert one.an_f32 == 3.14
+ assert typeof(one.an_f64).idx == typeof[f64]().idx
+ assert one.an_f64 == 2.718281828459
+ assert typeof(one.an_i16).idx == typeof[i16]().idx
+ assert one.an_i16 == 12345
+ assert typeof(one.an_i64).idx == typeof[i64]().idx
+ assert one.an_i64 == 123456789012345
+
+ result_values_two := sql db {
+ select from TestOrmValuesTwo
+ }!
+
+ two := result_values_two[0]
+
+ assert typeof(two.an_i8).idx == typeof[i8]().idx
+ assert two.an_i8 == 12
+ assert typeof(two.an_int).idx == typeof[int]().idx
+ assert two.an_int == 123456
+ assert typeof(two.a_string).idx == typeof[string]().idx
+ assert two.a_string == 'Hello, World!'
+
+ result_values_three := sql db {
+ select from TestOrmValuesThree
+ }!
+
+ three := result_values_three[0]
+
+ assert typeof(three.an_u16).idx == typeof[u16]().idx
+ assert three.an_u16 == 54321
+ assert typeof(three.an_u32).idx == typeof[u32]().idx
+ assert three.an_u32 == 1234567890
+ // println(three.an_u64)
+ assert typeof(three.an_u64).idx == typeof[u64]().idx
+ assert three.an_u64 == 1234
+ assert typeof(three.an_u8).idx == typeof[u8]().idx
+ assert three.an_u8 == 255
+
+ result_values_four := sql db {
+ select from TestOrmValuesFour
+ }!
+
+ four := result_values_four[0]
+
+ assert typeof(four.a_time).idx == typeof[time.Time]().idx
+ assert typeof(four.a_bool).idx == typeof[bool]().idx
+ assert four.a_bool == true
+ assert typeof(four.int_or_null).idx == typeof[?int]().idx
+ unwrapped_option_one := four.int_or_null or { 0 }
+ assert unwrapped_option_one == 0
+ unwrapped_option_two := result_values_four[1].int_or_null or { 0 }
+ assert unwrapped_option_two == 123
+}
+
+fn test_orm_create_enum_is_not_supported() {
+ mut db := open(':memory:')!
+ mut error := ''
+ sql db {
+ create table ORMTableEnum
+ } or { error = err.str() }
+ assert error == 'enum is not supported in vsql'
+}
From 5851f4592df3c8d78a8a49b3af7d3fd7438bfc5a Mon Sep 17 00:00:00 2001
From: daniel-le97 <107774403+daniel-le97@users.noreply.github.com>
Date: Sat, 9 Nov 2024 12:38:03 -0700
Subject: [PATCH 02/13] orm: prefer db.prepare over db.query where possible,
test now fully pass
---
examples/orm.v | 8 +-
vsql/orm.v | 533 ++++++++++++++++++++++--------------------------
vsql/orm_test.v | 191 +++++++++++++----
3 files changed, 405 insertions(+), 327 deletions(-)
diff --git a/examples/orm.v b/examples/orm.v
index 55b90a9..8748210 100644
--- a/examples/orm.v
+++ b/examples/orm.v
@@ -2,14 +2,16 @@ import os
import vsql
import time
+// CHECK ./vsql/orm_test.v for more advanced usage
+
fn main() {
os.rm('test.vsql') or {}
-
+
example() or { panic(err) }
}
// NOTE for some reason if we declare a @[primary] on a struct field, we can not do delete queries on the tables...
-// so id is not a primary key in this example
+// so for now we are not supporting primary keys
struct Product {
id int //@[primary]
product_name string @[sql_type: 'varchar(100)']
@@ -57,6 +59,4 @@ fn example() ! {
assert all.len == 2
println(timer.elapsed())
- // println(typeof[?int]().idx)
- // println(typeof[int]().idx)
}
diff --git a/vsql/orm.v b/vsql/orm.v
index 77a8029..afdb9c3 100644
--- a/vsql/orm.v
+++ b/vsql/orm.v
@@ -24,7 +24,7 @@ fn vsql_type_from_v(typ int) !string {
'DOUBLE PRECISION'
} else if typ == orm.type_idx['string'] {
'VARCHAR(${varchar_default_len})'
- } else if typ == typeof[time.Time]().idx || typ == -2 {
+ } else if typ == orm.time_ {
// time.Time will be converted to a string
// will be parsed as a time.Time when read
'TIMESTAMP(6) WITHOUT TIME ZONE'
@@ -36,167 +36,113 @@ fn vsql_type_from_v(typ int) !string {
}
}
-// fn get_prepared_args(query_data []orm.QueryData) map[string]Value {
-// mut mp := map[string]Value{}
-// for i , q in query_data {
-// // for f in q.fields {
-// // type_idx :=
-// // }
-// // for j, v in f.data {
-// // mp[':${i}_${j}'] = v
-// // }
-// }
-// return mp
-// }
-
-// `query_converter_lite` converts a statement like `INSERT INTO Product (id, product_name, price) VALUES (:1, :2, :3)` to `INSERT INTO Product (id, product_name, price) VALUES (:id, :product_name, :price)`
-fn query_converter_lite(query string, query_data []orm.QueryData) !string {
- mut counter := 1
- mut new_query := query
- for data in query_data {
- for field in data.fields {
- new_query = new_query.replace(':${counter}', ':${field}')
- counter++
- }
- }
- return new_query
-}
-
-fn query_converter(query string, query_data []orm.QueryData) !string {
- mut counter := 1
- mut new_query := query
-
- for data in query_data {
- vals := primitive_array_to_string_array(data.data)
- for val in vals {
- new_query = new_query.replace(':${counter}', val)
- counter++
+// primitive_to_value returns the Value of a Primitive based on the intended
+// destination type. Primitives are used by the ORM.
+//
+// It's important to note that while types may be compatible, they can still be
+// out of range, such as assigning an overflowing integer value to SMALLINT.
+fn primitive_to_value(typ Type, p orm.Primitive) !Value {
+ // The match should be exhaustive for typ and p so that we can make sure we
+ // cover all combinations now and in the future.
+ match p {
+ orm.Null {
+ // In standard SQL, NULL's must be typed.
+ return new_null_value(typ.typ)
}
- }
-
- return new_query
-}
-
-fn primitive_array_to_string_array(prims []orm.Primitive) []string {
- mut values := prims.map(fn (p orm.Primitive) string {
- match p {
- orm.InfixType {
- // TODO(elliotchance): Not sure what this is?
- return '${p}'
- }
- time.Time {
- //
- return 'TIMESTAMP \'${p}\''
- // return '${p}'
- }
- orm.Null {
- return 'NULL'
- }
- bool {
- if p {
- return 'TRUE'
+ bool {
+ match typ.typ {
+ .is_boolean {
+ return new_boolean_value(p)
}
-
- return 'FALSE'
- }
- string {
- return '\'${p}\''
- }
- f32 {
- return '${p}'
- }
- f64 {
- return '${p}'
- }
- i16 {
- return '${p}'
- }
- i64 {
- return '${p}'
+ else {}
}
- i8 {
- return '${p}'
+ }
+ f32, f64 {
+ match typ.typ {
+ .is_real {
+ return new_real_value(f32(p))
+ }
+ .is_double_precision {
+ return new_double_precision_value(f64(p))
+ }
+ else {}
}
- int {
- return '${p}'
+ }
+ i16, i8, u8 {
+ match typ.typ {
+ .is_smallint {
+ return new_smallint_value(i16(p))
+ }
+ else {}
}
- u16 {
- return '${p}'
+ }
+ int, u16 {
+ match typ.typ {
+ .is_smallint {
+ return new_smallint_value(i16(p))
+ }
+ .is_integer {
+ return new_integer_value(int(p))
+ }
+ else {}
}
- u32 {
- return '${p}'
+ }
+ u32, i64 {
+ match typ.typ {
+ .is_bigint {
+ return new_bigint_value(i64(p))
+ }
+ else {}
}
- u64 {
- return '${p}'
+ }
+ u64 {
+ match typ.typ {
+ .is_smallint {
+ return new_smallint_value(i16(p))
+ }
+ .is_bigint {
+ return new_bigint_value(i64(p))
+ }
+ else {}
}
- u8 {
- return '${p}'
+ }
+ string {
+ match typ.typ {
+ .is_varchar {
+ return new_varchar_value(p)
+ }
+ .is_numeric {
+ return new_numeric_value(p)
+ }
+ else {}
}
}
- })
- return values
-}
-
-// select is used internally by V's ORM for processing `SELECT` queries
-pub fn (mut db Connection) select(config orm.SelectConfig, data orm.QueryData, where orm.QueryData) ![][]orm.Primitive {
- // 1. Create query and bind necessary data
- // println(orm.type_idx)
- mut query := orm.orm_select_gen(config, '', true, ':', 1, where)
- query = query_converter(query, [data, where])!
- rows := db.query(query)!
- mut ret := [][]orm.Primitive{}
- for row in rows {
- mut row_primitives := []orm.Primitive{}
- keys := row.data.keys()
- for idx, key in keys {
- prim := row.get(key)!
- type_idx := config.types[idx]
-
- // check orm.type_idx for what these numbers are
- if type_idx == 5 {
- row_primitives << i8(prim.int_value())
- } else if type_idx == 2 {
- row_primitives << time.parse(prim.string_value())!
- } else if type_idx == 6 {
- row_primitives << i16(prim.int_value())
- } else if type_idx == 8 {
- row_primitives << int(prim.int_value())
- } else if type_idx == 9 {
- row_primitives << prim.int_value()
- } else if type_idx == 11 {
- row_primitives << u8(prim.int_value())
- } else if type_idx == 12 {
- row_primitives << u16(prim.int_value())
- } else if type_idx == 13 {
- row_primitives << u32(prim.int_value())
- } else if type_idx == 14 {
- row_primitives << u64(prim.int_value())
- } else if type_idx == 16 {
- row_primitives << f32(prim.f64_value())
- } else if type_idx == 17 {
- row_primitives << prim.f64_value()
- } else if type_idx == 19 {
- row_primitives << prim.bool_value() == .is_true
- } else {
- row_primitives << prim.string_value()
+ time.Time {
+ match typ.typ {
+ .is_timestamp_with_time_zone, .is_timestamp_without_time_zone {
+ return new_timestamp_value(p.str())!
+ }
+ else {}
}
}
- ret << row_primitives
+ orm.InfixType {}
}
- // println(ret)
- return ret
+
+ return error('cannot assign ${p} to ${typ}')
}
fn serial_name(table string) string {
return '${table}_SERIAL'
}
-fn get_table_columns(mut db Connection, table string, data []orm.QueryData) !map[string]Value {
+fn get_table_values_map(mut db Connection, table string, data []orm.QueryData) !map[string]Value {
mut mp := map[string]Value{}
mut tbl := Table{}
mut cat := db.catalog()
tables := cat.schema_tables('PUBLIC') or { [] }
+ // println(tables)
for t in tables {
+ // println(t.name.entity_name)
if t.name.entity_name == table.to_upper() {
tbl = t
break
@@ -205,11 +151,18 @@ fn get_table_columns(mut db Connection, table string, data []orm.QueryData) !map
if tbl.name.entity_name != table.to_upper() {
return error('Table ${table} not found')
}
+ mut field_counter := 1
for d in data {
for i, f in d.fields {
for c in tbl.columns {
if c.name.sub_entity_name == f.to_upper() {
- mp[f] = primitive_to_value(c.typ, d.data[i])!
+ if mp.keys().contains(f) {
+ field_key := '${f}_${field_counter.str()}'
+ mp[field_key] = primitive_to_value(c.typ, d.data[i])!
+ field_counter++
+ } else {
+ mp[f] = primitive_to_value(c.typ, d.data[i])!
+ }
}
}
}
@@ -218,80 +171,170 @@ fn get_table_columns(mut db Connection, table string, data []orm.QueryData) !map
return mp
}
+// `query_reformatter` converts a statement like `INSERT INTO Product (id, product_name, price) VALUES (:1, :2, :3)` to `INSERT INTO Product (id, product_name, price) VALUES (:id, :product_name, :price)`
+fn query_reformatter(query string, query_data []orm.QueryData) string {
+ mut counter := 1
+ mut field_counter := 1
+ mut new_query := query
+ for data in query_data {
+ for field in data.fields {
+ // this if check is for if there are multiple of the same field being checked for
+ // products = sql db {
+ // select from Product where price > '3' && price < '3.50'
+ // }!
+ if new_query.contains(':${field}') {
+ new_query = new_query.replace(':${counter}', ':${field}_${field_counter.str()}')
+ field_counter++
+ counter++
+ } else {
+ new_query = new_query.replace(':${counter}', ':${field}')
+ counter++
+ }
+ }
+ }
+ return new_query
+}
+
+fn check_for_not_supported(mut db Connection, table string, fields []orm.TableField) ! {
+ for field in fields {
+ if field.typ == orm.enum_ {
+ return error('enum is not supported in vsql')
+ }
+ if is_reserved_word(field.name) {
+ return error('reserved word ${field.name} cannot be used as a field name at ${table}.${field.name}')
+ }
+ for attr in field.attrs {
+ if attr.name == 'sql' {
+ if attr.arg == 'serial' {
+ db.query('CREATE SEQUENCE "${serial_name(table)}"')!
+ }
+ if is_reserved_word(attr.arg) {
+ return error('${attr.arg} is a reserved word in vsql')
+ }
+ }
+ if attr.name == 'default' {
+ return error('default is not supported in vsql')
+ }
+ if attr.name == 'unique' {
+ return error('unique is not supported in vsql')
+ }
+ if attr.name == 'primary' {
+ // TODO (daniel-le97) figure out how we should be handling primary keys as it will break delete queries
+ return error('primary key is supported, but currently will break delete queries')
+ }
+ }
+ }
+}
+
+// select is used internally by V's ORM for processing `SELECT` queries
+pub fn (mut db Connection) select(config orm.SelectConfig, data orm.QueryData, where orm.QueryData) ![][]orm.Primitive {
+ conf := orm.SelectConfig{
+ ...config
+ table: reformat_table_name(config.table)
+ }
+ mut query := orm.orm_select_gen(conf, '', true, ':', 1, where)
+ query = query_reformatter(query, [data, where])
+ // println('query: ${query}')
+
+ mut rows := Result{}
+ if data.data.len == 0 && where.data.len == 0 {
+ rows = db.query(query) or { return err }
+ } else {
+ values := get_table_values_map(mut db, config.table, [data, where]) or { return err }
+ mut stmt := db.prepare(query) or { return err }
+ rows = stmt.query(values) or { return err }
+ }
+
+ mut ret := [][]orm.Primitive{}
+ for row in rows {
+ mut row_primitives := []orm.Primitive{}
+ keys := row.data.keys()
+ for _, key in keys {
+ prim := row.get(key)!
+ row_primitives << prim.primitive() or { return err }
+ }
+ ret << row_primitives
+ }
+
+ return ret
+}
+
// insert is used internally by V's ORM for processing `INSERT` queries
pub fn (mut db Connection) insert(table string, data orm.QueryData) ! {
- // println(data)
- // mut tbl := get_table_columns(mut db, table, [data]) or { return err }
- // println(tbl)
+ new_table := reformat_table_name(table)
+ mut tbl := get_table_values_map(mut db, table, [data]) or { return err }
+
+ mut query := 'INSERT INTO ${new_table} (${data.fields.join(', ')}) VALUES (:${data.fields.join(', :')})'
- mut values := primitive_array_to_string_array(data.data)
+ // if a table has a serial field, we need to remove it from the insert statement
+ // only allows one serial field per table, as do most RDBMS
if data.auto_fields.len > 1 {
return error('multiple AUTO fields are not supported')
} else if data.auto_fields.len == 1 {
- // println(data)
- values[data.auto_fields[0]] = 'NEXT VALUE FOR "${serial_name(table)}"'
+ autofield_idx := data.auto_fields[0]
+ autofield := data.fields[autofield_idx]
+ tbl.delete(autofield)
+ query = query.replace(':${autofield}', 'NEXT VALUE FOR "${serial_name(table)}"')
}
- mut nums := []string{}
- for i, _ in data.fields {
- nums << '${i}'
- }
- insert_sql := 'INSERT INTO ${table} (${data.fields.join(', ')}) VALUES (${values.join(', ')})'
- println(insert_sql)
- // println(tbl)
$if trace_vsql_orm ? {
eprintln('> vsql insert: ${query}')
}
- db.query(insert_sql) or { return err }
- // mut stmt := db.prepare(insert_sql) or { return err }
- // stmt.query(tbl) or { return err }
+ mut stmt := db.prepare(query) or { return err }
+ stmt.query(tbl) or { return err }
}
// update is used internally by V's ORM for processing `UPDATE` queries
pub fn (mut db Connection) update(table string, data orm.QueryData, where orm.QueryData) ! {
- mut query, _ := orm.orm_stmt_gen(.sqlite, table, '', .update, true, ':', 1, data,
+ new_table := reformat_table_name(table)
+ mut query, _ := orm.orm_stmt_gen(.sqlite, new_table, '', .update, true, ':', 1, data,
where)
- // values := get_table_columns(mut db, table, [data, where]) or { return err }
- query = query_converter(query, [data, where])!
- println(query)
+ values := get_table_values_map(mut db, table, [data, where]) or { return err }
+ query = query_reformatter(query, [data, where])
+ // println(query)
$if trace_vsql_orm ? {
eprintln('> vsql update: ${query}')
}
- db.query(query) or { return err }
- // mut stmt := db.prepare(query) or { return err }
- // stmt.query(values) or { return err }
+ mut stmt := db.prepare(query) or { return err }
+ stmt.query(values) or { return err }
}
// delete is used internally by V's ORM for processing `DELETE ` queries
pub fn (mut db Connection) delete(table string, where orm.QueryData) ! {
- mut query, _ := orm.orm_stmt_gen(.sqlite, table, '', .delete, true, ':', 1, orm.QueryData{},
+ new_table := reformat_table_name(table)
+ mut query, _ := orm.orm_stmt_gen(.sqlite, new_table, '', .delete, true, ':', 1, orm.QueryData{},
where)
- query = query_converter(query, [where])!
- // values := get_table_columns(mut db, table, [where]) or { return err }
+ query = query_reformatter(query, [where])
+ values := get_table_values_map(mut db, table, [where]) or { return err }
$if trace_vsql_orm ? {
eprintln('> vsql delete: ${query}')
}
- db.query(query) or { return err }
- // mut stmt := db.prepare(query) or { return err }
- // stmt.query(values) or { return err }
+ // db.query(query) or { return err }
+ mut stmt := db.prepare(query) or { return err }
+ stmt.query(values) or { return err }
}
// `last_id` is used internally by V's ORM for post-processing `INSERT` queries
-// TODO i dont think vsql supports this
+//
TODO i dont think vsql supports this
pub fn (mut db Connection) last_id() int {
return 0
}
-// create is used internally by V's ORM for processing table creation queries (DDL)
-pub fn (mut db Connection) create(table string, fields []orm.TableField) ! {
- check_for_not_supported(mut db, table, fields) or { return err }
+fn reformat_table_name(table string) string {
mut new_table := table
if is_reserved_word(table) {
new_table = '"${table}"'
}
+ return new_table
+}
+
+// create is used internally by V's ORM for processing table creation queries (DDL)
+pub fn (mut db Connection) create(table string, fields []orm.TableField) ! {
+ check_for_not_supported(mut db, table, fields) or { return err }
+ new_table := reformat_table_name(table)
mut query := orm.orm_table_gen(new_table, '', true, 0, fields, vsql_type_from_v, false) or {
return err
}
@@ -303,15 +346,13 @@ pub fn (mut db Connection) create(table string, fields []orm.TableField) ! {
$if trace_vsql_orm ? {
eprintln('> vsql create: ${query}')
}
- // println(query)
- // println(table)
- // println(fields)
db.query(query) or { return err }
}
// drop is used internally by V's ORM for processing table destroying queries (DDL)
pub fn (mut db Connection) drop(table string) ! {
- query := 'DROP TABLE ${table};'
+ new_table := reformat_table_name(table)
+ mut query := 'DROP TABLE ${new_table};'
$if trace_vsql_orm ? {
eprintln('> vsql drop: ${query}')
}
@@ -320,129 +361,49 @@ pub fn (mut db Connection) drop(table string) ! {
// // check to see if there is a SEQUENCE for the table (for the @[sql: 'serial'] attribute)
db.query('EXPLAIN DROP SEQUENCE "${serial_name(table)}"') or { return }
+
// if we have not returned then we can drop the sequence
- db.query('DROP SEQUENCE "${serial_name(table)}"') or { return err }
+ query = 'DROP SEQUENCE "${serial_name(table)}"'
+ $if trace_vsql_orm ? {
+ eprintln('> vsql drop: ${query}')
+ }
+ db.query(query) or { return err }
}
-fn check_for_not_supported(mut db Connection, table string, fields []orm.TableField) ! {
- for field in fields {
- if field.typ == orm.enum_ {
- return error('enum is not supported in vsql')
- }
- if is_reserved_word(field.name) {
- return error('reserved word ${field.name} cannot be used as a field name at ${table}.${field.name}')
- }
- for attr in field.attrs {
- if attr.name == 'sql' {
- if attr.arg == 'serial' {
- db.query('CREATE SEQUENCE "${serial_name(table)}"')!
- }
- if is_reserved_word(attr.arg) {
- return error('${attr.arg} is a reserved word in vsql')
- }
- }
- if attr.name == 'default' {
- return error('default is not supported in vsql')
- }
- if attr.name == 'unique' {
- return error('unique is not supported in vsql')
- }
- if attr.name == 'primary' {
- eprintln('primary is supported, but currently will break delete queries')
- // return error('primary is supported, but currently will break delete queries')
- }
- }
+// convert a vsql row value to orm.Primitive
+fn (v Value) primitive() !orm.Primitive {
+ if v.is_null {
+ return orm.Null{}
}
-}
-// primitive_to_value returns the Value of a Primitive based on the intended
-// destination type. Primitives are used by the ORM.
-//
-// It's important to note that while types may be compatible, they can still be
-// out of range, such as assigning an overflowing integer value to SMALLINT.
-fn primitive_to_value(typ Type, p orm.Primitive) !Value {
- // The match should be exhaustive for typ and p so that we can make sure we
- // cover all combinations now and in the future.
- match p {
- orm.Null {
- // In standard SQL, NULL's must be typed.
- return new_null_value(typ.typ)
+ return match v.typ.typ {
+ .is_boolean {
+ orm.Primitive(v.bool_value() == .is_true)
}
- bool {
- match typ.typ {
- .is_boolean {
- return new_boolean_value(p)
- }
- else {}
- }
+ .is_smallint {
+ orm.Primitive(i16(v.int_value()))
}
- f32, f64 {
- match typ.typ {
- .is_real {
- return new_real_value(f32(p))
- }
- .is_double_precision {
- return new_double_precision_value(f64(p))
- }
- else {}
- }
+ .is_integer {
+ orm.Primitive(int(v.int_value()))
}
- i16, i8, u8 {
- match typ.typ {
- .is_smallint {
- return new_smallint_value(i16(p))
- }
- else {}
- }
+ .is_bigint {
+ orm.Primitive(i64(v.int_value()))
}
- int, u16 {
- match typ.typ {
- .is_smallint {
- return new_smallint_value(i16(p))
- }
- .is_integer {
- return new_integer_value(int(p))
- }
- else {}
- }
+ .is_varchar, .is_character {
+ orm.Primitive(v.string_value())
}
- u32, i64 {
- match typ.typ {
- .is_bigint {
- return new_bigint_value(i64(p))
- }
- else {}
- }
+ .is_real {
+ orm.Primitive(f32(v.f64_value()))
}
- u64 {
- match typ.typ {
- .is_smallint {
- return new_smallint_value(i16(p))
- }
- else {}
- }
+ .is_double_precision {
+ orm.Primitive(v.f64_value())
}
- string {
- match typ.typ {
- .is_varchar {
- return new_varchar_value(p)
- }
- .is_numeric {
- return new_numeric_value(p)
- }
- else {}
- }
+ .is_decimal, .is_numeric {
+ orm.Primitive(v.str())
}
- time.Time {
- match typ.typ {
- .is_timestamp_with_time_zone, .is_timestamp_without_time_zone {
- return new_timestamp_value(p.str())!
- }
- else {}
- }
+ .is_date, .is_time_with_time_zone, .is_timestamp_without_time_zone,
+ .is_timestamp_with_time_zone, .is_time_without_time_zone {
+ orm.Primitive(v.str())
}
- orm.InfixType {}
}
-
- return error('cannot assign ${p} to ${typ}')
}
diff --git a/vsql/orm_test.v b/vsql/orm_test.v
index 03a19d1..94a7cf6 100644
--- a/vsql/orm_test.v
+++ b/vsql/orm_test.v
@@ -3,16 +3,6 @@ module vsql
// Structs intentionally have less than 6 fields, any more then inserts queries get exponentially slower.
import time
-// struct TestDateTypes {
-// id int @[primary; sql: serial]
-// custom1 string @[sql_type: 'TIME WITH TIME ZONE']
-// custom2 string @[sql_type: 'TIMESTAMP(3) WITH TIME ZONE']
-// custom3 string @[sql_type: 'INT']
-// custom4 string @[sql_type: 'DATE']
-// custom5 string @[sql_type: 'TIMESTAMP(3) WITHOUT TIME ZONE']
-// custom6 string @[sql_type: 'TIME WITHOUT TIME ZONE']
-// }
-
@[table: 'testcustomtable']
struct TestCustomTableAndSerial {
id int @[sql: serial]
@@ -78,52 +68,79 @@ enum Colors {
blue
}
-// @[table: 'GROUP']
+@[table: 'GROUP']
struct ORMTable2 {
dummy int
}
-fn test_orm_create_success2() {
+struct Product {
+ id int // @[primary] FIXME
+ product_name string
+ price string @[sql_type: 'NUMERIC(5,2)']
+ quantity ?i16
+}
+
+fn test_orm_create_table_with_reserved_word() {
mut db := open(':memory:')!
+ mut error := ''
sql db {
create table ORMTable2
- }!
- dumm := ORMTable2{dummy: 1}
+ } or { error = err.str() }
+ assert error == ''
+ dumm := ORMTable2{
+ dummy: 1
+ }
sql db {
insert dumm into ORMTable2
+ } or { error = err.str() }
+ assert error == ''
+
+ sql db {
+ update ORMTable2 set dummy = 2 where dummy == 1
}!
+ all := sql db {
+ select from ORMTable2
+ }!
+ assert all[0].dummy == 2
+
+ sql db {
+ delete from ORMTable2 where dummy == 2
+ } or { error = err.str() }
+ assert error == ''
sql db {
drop table ORMTable2
- }!
+ } or { error = err.str() }
+
+ assert error == ''
}
-// We cannot test this because it will make all tests fail, because the database itself will run into an error
-// so it is put behind a -d flag if you really want to try it
-fn test_primary_key_broken() {
- $if test_primary_key ? {
- mut db := open(':memory:')!
- mut error := ''
- sql db {
- create table TestPrimaryBroken
- } or { error = err.str() }
- assert error == ''
+// we are not supporting primary key for now as it breaks delete queries
+fn test_primary_key_not_supported() {
+ // $if test_primary_key ? {
+ mut db := open(':memory:')!
+ mut error := ''
+ sql db {
+ create table TestPrimaryBroken
+ } or { error = err.str() }
+ assert error == 'primary key is supported, but currently will break delete queries'
- test_primary_broken := TestPrimaryBroken{}
+ // test_primary_broken := TestPrimaryBroken{}
- sql db {
- insert test_primary_broken into TestPrimaryBroken
- } or { error = err.str() }
- assert error == ''
+ // sql db {
+ // insert test_primary_broken into TestPrimaryBroken
+ // } or { error = err.str() }
+ // assert error == ''
- mut all := sql db {
- select from TestPrimaryBroken
- }!
+ // mut all := sql db {
+ // select from TestPrimaryBroken
+ // }!
- sql db {
- delete from TestPrimaryBroken where id == all[0].id
- } or { error = err.str() }
- }
+ // sql db {
+ // delete from TestPrimaryBroken where id == all[0].id
+ // } or { error = err.str() }
+ // assert error != ''
+ // }
}
fn test_custom_table_name_and_serial_crud() {
@@ -317,3 +334,103 @@ fn test_orm_create_enum_is_not_supported() {
} or { error = err.str() }
assert error == 'enum is not supported in vsql'
}
+
+fn test_orm_select_where() {
+ mut db := open(':memory:')!
+ mut error := ''
+
+ sql db {
+ create table Product
+ } or { panic(err) }
+
+ prods := [
+ Product{1, 'Ice Cream', '5.99', 17},
+ Product{2, 'Ham Sandwhich', '3.47', none},
+ Product{3, 'Bagel', '1.25', 45},
+ ]
+ for product in prods {
+ sql db {
+ insert product into Product
+ } or { panic(err) }
+ }
+ mut products := sql db {
+ select from Product where id == 2
+ }!
+
+ assert products == [Product{2, 'Ham Sandwhich', '3.47', none}]
+
+ products = sql db {
+ select from Product where id == 5
+ }!
+
+ assert products == []
+
+ products = sql db {
+ select from Product where id != 3
+ }!
+
+ assert products == [Product{1, 'Ice Cream', '5.99', 17},
+ Product{2, 'Ham Sandwhich', '3.47', none}]
+
+ products = sql db {
+ select from Product where price > '3.47'
+ }!
+
+ assert products == [Product{1, 'Ice Cream', '5.99', 17}]
+
+ products = sql db {
+ select from Product where price >= '3'
+ }!
+
+ assert products == [Product{1, 'Ice Cream', '5.99', 17},
+ Product{2, 'Ham Sandwhich', '3.47', none}]
+
+ products = sql db {
+ select from Product where price < '3.47'
+ }!
+
+ assert products == [Product{3, 'Bagel', '1.25', 45}]
+
+ products = sql db {
+ select from Product where price <= '5'
+ }!
+
+ assert products == [Product{2, 'Ham Sandwhich', '3.47', none},
+ Product{3, 'Bagel', '1.25', 45}]
+
+ // TODO (daniel-le97): The ORM does not support a "not like" constraint right now.
+
+ products = sql db {
+ select from Product where product_name like 'Ham%'
+ }!
+
+ assert products == [Product{2, 'Ham Sandwhich', '3.47', none}]
+
+ products = sql db {
+ select from Product where quantity is none
+ }!
+
+ // println(products)
+
+ assert products == [Product{2, 'Ham Sandwhich', '3.47', none}]
+
+ products = sql db {
+ select from Product where quantity !is none
+ }!
+
+ // println(products)
+ assert products == [Product{1, 'Ice Cream', '5.99', 17}, Product{3, 'Bagel', '1.25', 45}]
+
+ products = sql db {
+ select from Product where price > '3' && price < '3.50'
+ }!
+ // println(products)
+
+ assert products == [Product{2, 'Ham Sandwhich', '3.47', none}]
+
+ products = sql db {
+ select from Product where price < '2.000' || price >= '5'
+ }!
+
+ assert products == [Product{1, 'Ice Cream', '5.99', 17}, Product{3, 'Bagel', '1.25', 45}]
+}
From a0823363f5b771640fdb20af33c8a64e0e58a4e1 Mon Sep 17 00:00:00 2001
From: daniel-le97 <107774403+daniel-le97@users.noreply.github.com>
Date: Sun, 10 Nov 2024 08:46:49 -0700
Subject: [PATCH 03/13] run make fmt
---
cmd/vsql/bench.v | 10 +--
cmd/vsql/cli.v | 8 +--
cmd/vsql/in.v | 18 ++---
cmd/vsql/main.v | 4 +-
cmd/vsql/out.v | 12 ++--
cmd/vsql/server.v | 22 +++---
cmd/vsql/version.v | 4 +-
vsql/bench.v | 8 +--
vsql/btree.v | 6 +-
vsql/btree_test.v | 2 +-
vsql/compiler.v | 4 +-
vsql/connection.v | 31 +++++----
vsql/connection_test.v | 4 +-
vsql/earley.v | 16 ++---
vsql/header.v | 10 +--
vsql/lexer.v | 36 +++++-----
vsql/numeric.v | 26 +++----
vsql/page.v | 14 ++--
vsql/pager.v | 2 +-
vsql/planner.v | 22 +++---
vsql/result.v | 6 +-
vsql/server.v | 8 +--
vsql/sql_test.v | 12 ++--
vsql/std_aggregate_function.v | 6 +-
vsql/std_alter_sequence_generator_statement.v | 4 +-
vsql/std_between_predicate.v | 18 ++---
vsql/std_boolean_value_expression.v | 16 ++---
vsql/std_case_expression.v | 8 +--
vsql/std_cast_specification.v | 4 +-
vsql/std_comparison_predicate.v | 4 +-
...d_contextually_typed_value_specification.v | 4 +-
vsql/std_datetime_value_function.v | 20 +++---
vsql/std_group_by_clause.v | 6 +-
vsql/std_insert_statement.v | 6 +-
vsql/std_like_predicate.v | 4 +-
vsql/std_names_and_identifiers.v | 60 +++++++++-------
vsql/std_next_value_expression.v | 4 +-
vsql/std_null_predicate.v | 4 +-
vsql/std_numeric_value_expression.v | 10 +--
vsql/std_query_expression.v | 30 ++++----
vsql/std_query_specification.v | 2 +-
vsql/std_routine_invocation.v | 6 +-
vsql/std_row_value_constructor.v | 2 +-
vsql/std_search_condition.v | 2 +-
vsql/std_sequence_generator_definition.v | 14 ++--
vsql/std_set_catalog_statement.v | 2 +-
vsql/std_set_schema_statement.v | 2 +-
vsql/std_similar_predicate.v | 4 +-
vsql/std_sqlstate.v | 68 +++++++++----------
vsql/std_store_assignment.v | 6 +-
vsql/std_string_value_expression.v | 4 +-
vsql/std_string_value_function.v | 16 ++---
vsql/std_table_definition.v | 6 +-
vsql/std_table_reference.v | 4 +-
vsql/std_table_value_constructor.v | 12 ++--
vsql/std_update_statement_searched.v | 6 +-
...e_specification_and_target_specification.v | 8 +--
vsql/table.v | 6 +-
vsql/time.v | 48 ++++++-------
vsql/type.v | 28 ++++----
vsql/value.v | 38 +++++------
vsql/virtual_table.v | 4 +-
vsql/walk.v | 2 +-
63 files changed, 396 insertions(+), 387 deletions(-)
diff --git a/cmd/vsql/bench.v b/cmd/vsql/bench.v
index 7ae2889..ece3686 100644
--- a/cmd/vsql/bench.v
+++ b/cmd/vsql/bench.v
@@ -5,14 +5,14 @@ import vsql
fn register_bench_command(mut cmd cli.Command) {
mut bench_cmd := cli.Command{
- name: 'bench'
+ name: 'bench'
description: 'Run benchmark'
- execute: bench_command
+ execute: bench_command
}
bench_cmd.add_flag(cli.Flag{
- flag: .string
- name: 'file'
- abbrev: 'f'
+ flag: .string
+ name: 'file'
+ abbrev: 'f'
description: 'File path that will be deleted and created for the test. You can use :memory: as well (default bench.vsql)'
})
cmd.add_command(bench_cmd)
diff --git a/cmd/vsql/cli.v b/cmd/vsql/cli.v
index 09d4fec..0ac3217 100644
--- a/cmd/vsql/cli.v
+++ b/cmd/vsql/cli.v
@@ -7,11 +7,11 @@ import vsql
fn register_cli_command(mut cmd cli.Command) {
mut cli_cmd := cli.Command{
- name: 'cli'
- usage: ''
+ name: 'cli'
+ usage: ''
required_args: 1
- description: 'Run SQL in a vsql file'
- execute: cli_command
+ description: 'Run SQL in a vsql file'
+ execute: cli_command
}
cmd.add_command(cli_cmd)
}
diff --git a/cmd/vsql/in.v b/cmd/vsql/in.v
index 2fce7e9..65edf80 100644
--- a/cmd/vsql/in.v
+++ b/cmd/vsql/in.v
@@ -6,23 +6,23 @@ import vsql
fn register_in_command(mut cmd cli.Command) {
mut in_cmd := cli.Command{
- name: 'in'
- usage: ''
+ name: 'in'
+ usage: ''
required_args: 1
- description: 'Import schema and data'
- execute: in_command
+ description: 'Import schema and data'
+ execute: in_command
}
in_cmd.add_flag(cli.Flag{
- flag: .bool
- name: 'continue-on-error'
+ flag: .bool
+ name: 'continue-on-error'
description: 'Continue when errors occur'
})
in_cmd.add_flag(cli.Flag{
- flag: .bool
- name: 'verbose'
- abbrev: 'v'
+ flag: .bool
+ name: 'verbose'
+ abbrev: 'v'
description: 'Show result of each command'
})
diff --git a/cmd/vsql/main.v b/cmd/vsql/main.v
index 1d5c087..23bd8db 100644
--- a/cmd/vsql/main.v
+++ b/cmd/vsql/main.v
@@ -5,9 +5,9 @@ import os
fn main() {
mut cmd := cli.Command{
- name: 'vsql'
+ name: 'vsql'
description: 'vsql is a single-file or PostgeSQL-compatible SQL database written in V.\nhttps://github.com/elliotchance/vsql'
- execute: unknown_command
+ execute: unknown_command
}
register_bench_command(mut cmd)
diff --git a/cmd/vsql/out.v b/cmd/vsql/out.v
index 738493a..53f1d9b 100644
--- a/cmd/vsql/out.v
+++ b/cmd/vsql/out.v
@@ -5,16 +5,16 @@ import vsql
fn register_out_command(mut cmd cli.Command) {
mut out_cmd := cli.Command{
- name: 'out'
- usage: ''
+ name: 'out'
+ usage: ''
required_args: 1
- description: 'Export schema and data'
- execute: out_command
+ description: 'Export schema and data'
+ execute: out_command
}
out_cmd.add_flag(cli.Flag{
- flag: .bool
- name: 'create-public-schema'
+ flag: .bool
+ name: 'create-public-schema'
description: 'Include "CREATE SCHEMA PUBLIC"'
})
diff --git a/cmd/vsql/server.v b/cmd/vsql/server.v
index 232d59a..04ec58e 100644
--- a/cmd/vsql/server.v
+++ b/cmd/vsql/server.v
@@ -5,22 +5,22 @@ import vsql
fn register_server_command(mut cmd cli.Command) {
mut server_cmd := cli.Command{
- name: 'server'
- description: 'Run as a server for PostgreSQL clients'
- usage: ''
+ name: 'server'
+ description: 'Run as a server for PostgreSQL clients'
+ usage: ''
required_args: 1
- execute: server_command
+ execute: server_command
}
server_cmd.add_flag(cli.Flag{
- flag: .bool
- name: 'verbose'
- abbrev: 'v'
+ flag: .bool
+ name: 'verbose'
+ abbrev: 'v'
description: 'Verbose (show all messages in and out of the server)'
})
server_cmd.add_flag(cli.Flag{
- flag: .int
- name: 'port'
- abbrev: 'p'
+ flag: .int
+ name: 'port'
+ abbrev: 'p'
description: 'Port number (default 3210)'
})
cmd.add_command(server_cmd)
@@ -36,7 +36,7 @@ fn server_command(cmd cli.Command) ! {
mut server := vsql.new_server(vsql.ServerOptions{
db_file: cmd.args[0]
- port: port
+ port: port
verbose: cmd.flags.get_bool('verbose') or { false }
})
diff --git a/cmd/vsql/version.v b/cmd/vsql/version.v
index 6b739b7..daf055d 100644
--- a/cmd/vsql/version.v
+++ b/cmd/vsql/version.v
@@ -4,9 +4,9 @@ import cli
fn register_version_command(mut cmd cli.Command) {
mut version_cmd := cli.Command{
- name: 'version'
+ name: 'version'
description: 'Show version'
- execute: version_command
+ execute: version_command
}
cmd.add_command(version_cmd)
}
diff --git a/vsql/bench.v b/vsql/bench.v
index 68b9bfb..599e318 100644
--- a/vsql/bench.v
+++ b/vsql/bench.v
@@ -25,10 +25,10 @@ pub mut:
pub fn new_benchmark(conn &Connection) Benchmark {
return Benchmark{
account_rows: 100000
- teller_rows: 10
- branch_rows: 1
- run_for: time.minute
- conn: conn
+ teller_rows: 10
+ branch_rows: 1
+ run_for: time.minute
+ conn: conn
}
}
diff --git a/vsql/btree.v b/vsql/btree.v
index d0675d8..16ff54f 100644
--- a/vsql/btree.v
+++ b/vsql/btree.v
@@ -30,7 +30,7 @@ mut:
fn new_btree(pager Pager, page_size int) &Btree {
return &Btree{
- pager: pager
+ pager: pager
page_size: page_size
}
}
@@ -383,8 +383,8 @@ fn (mut p Btree) split_page(path []int, page Page, obj PageObject, kind u8) ! {
fn (p Btree) new_range_iterator(min []u8, max []u8) PageIterator {
return PageIterator{
btree: p
- min: min
- max: max
+ min: min
+ max: max
}
}
diff --git a/vsql/btree_test.v b/vsql/btree_test.v
index 3cbf1dd..858ee6a 100644
--- a/vsql/btree_test.v
+++ b/vsql/btree_test.v
@@ -29,7 +29,7 @@ fn test_btree_test() ! {
page_size := 256
file_name := 'btree.vsql'
- for tt in 0 .. vsql.times {
+ for tt in 0 .. times {
for size := 1; size <= 1000; size *= 10 {
for blob_size in blob_sizes {
if os.exists(file_name) {
diff --git a/vsql/compiler.v b/vsql/compiler.v
index 7e59110..8bf60bd 100644
--- a/vsql/compiler.v
+++ b/vsql/compiler.v
@@ -24,8 +24,8 @@ struct CompileResult {
fn (c CompileResult) with_agg(contains_agg bool) CompileResult {
return CompileResult{
- run: c.run
- typ: c.typ
+ run: c.run
+ typ: c.typ
contains_agg: contains_agg
}
}
diff --git a/vsql/connection.v b/vsql/connection.v
index 858e3aa..b85b6c7 100644
--- a/vsql/connection.v
+++ b/vsql/connection.v
@@ -113,10 +113,10 @@ fn open_connection(path string, options ConnectionOptions) !&Connection {
catalog_name := catalog_name_from_path(path)
mut conn := &Connection{
- query_cache: options.query_cache
+ query_cache: options.query_cache
current_catalog: catalog_name
- current_schema: vsql.default_schema_name
- now: default_now
+ current_schema: default_schema_name
+ now: default_now
}
register_builtin_funcs(mut conn)!
@@ -140,9 +140,9 @@ pub fn (mut conn Connection) add_catalog(catalog_name string, path string, optio
catalog := &CatalogConnection{
catalog_name: catalog_name
- path: path
- storage: new_storage(btree)
- options: options
+ path: path
+ storage: new_storage(btree)
+ options: options
}
conn.catalogs[catalog_name] = catalog
@@ -350,9 +350,9 @@ pub fn (mut conn Connection) register_virtual_table(create_table string, data Vi
mut table_name := conn.resolve_schema_identifier(stmt.table_name)!
conn.catalogs[conn.current_catalog].virtual_tables[table_name.storage_id()] = VirtualTable{
- create_table_sql: create_table
+ create_table_sql: create_table
create_table_stmt: stmt
- data: data
+ data: data
}
return
@@ -416,19 +416,20 @@ pub fn (mut conn CatalogConnection) schema_tables(schema string) ![]Table {
// canonical (fully qualified) form.
fn (conn Connection) resolve_identifier(identifier Identifier) Identifier {
return Identifier{
- custom_id: identifier.custom_id
- custom_typ: identifier.custom_typ
- catalog_name: if identifier.catalog_name == '' && !identifier.entity_name.starts_with('$') {
+ custom_id: identifier.custom_id
+ custom_typ: identifier.custom_typ
+ catalog_name: if identifier.catalog_name == ''
+ && !identifier.entity_name.starts_with('$') {
conn.current_catalog
} else {
identifier.catalog_name
}
- schema_name: if identifier.schema_name == '' && !identifier.entity_name.starts_with('$') {
+ schema_name: if identifier.schema_name == '' && !identifier.entity_name.starts_with('$') {
conn.current_schema
} else {
identifier.schema_name
}
- entity_name: identifier.entity_name
+ entity_name: identifier.entity_name
sub_entity_name: identifier.sub_entity_name
}
}
@@ -518,8 +519,8 @@ pub mut:
pub fn default_connection_options() ConnectionOptions {
return ConnectionOptions{
query_cache: new_query_cache()
- page_size: 4096
- mutex: sync.new_rwmutex()
+ page_size: 4096
+ mutex: sync.new_rwmutex()
}
}
diff --git a/vsql/connection_test.v b/vsql/connection_test.v
index ec6110f..0690ecd 100644
--- a/vsql/connection_test.v
+++ b/vsql/connection_test.v
@@ -24,11 +24,11 @@ fn test_concurrent_writes() ! {
waits << spawn fn (file_name string, i int, options ConnectionOptions) {
mut db := open_database(file_name, options) or { panic(err) }
for j in 0 .. 100 {
- if vsql.verbose {
+ if verbose {
println('${i}.${j}: INSERT start')
}
db.query('INSERT INTO foo (x) VALUES (1)') or { panic(err) }
- if vsql.verbose {
+ if verbose {
println('${i}.${j}: INSERT done')
}
}
diff --git a/vsql/earley.v b/vsql/earley.v
index b97a0d5..20455e0 100644
--- a/vsql/earley.v
+++ b/vsql/earley.v
@@ -63,12 +63,12 @@ fn new_earley_state(name string, production &EarleyProduction, dot_index int, st
}
return &EarleyState{
- name: name
- production: production
+ name: name
+ production: production
start_column: start_column
- dot_index: dot_index
- rules: rules
- end_column: unsafe { 0 }
+ dot_index: dot_index
+ rules: rules
+ end_column: unsafe { 0 }
}
}
@@ -136,9 +136,9 @@ mut:
fn new_earley_column(index int, token string, value string) &EarleyColumn {
return &EarleyColumn{
- index: index
- token: token
- value: value
+ index: index
+ token: token
+ value: value
unique: &Set{}
}
}
diff --git a/vsql/header.v b/vsql/header.v
index fddc851..c164921 100644
--- a/vsql/header.v
+++ b/vsql/header.v
@@ -49,10 +49,10 @@ mut:
fn new_header(page_size int) Header {
return Header{
// Set all default here - even if they are zero - to be explicit.
- version: vsql.current_version
+ version: current_version
schema_version: 0
- page_size: page_size
- root_page: 0
+ page_size: page_size
+ root_page: 0
// 2 is a reserved number for freezing rows, but the transaction_is is
// always incremented before returning the next value.
transaction_id: 2
@@ -73,8 +73,8 @@ fn read_header(mut file os.File) !Header {
header := file.read_raw[Header]() or { return error('unable to read raw header: ${err}') }
// Check file version compatibility.
- if header.version != vsql.current_version {
- return error('need version ${vsql.current_version} but database is ${header.version}')
+ if header.version != current_version {
+ return error('need version ${current_version} but database is ${header.version}')
}
return header
diff --git a/vsql/lexer.v b/vsql/lexer.v
index fc15be0..97510ca 100644
--- a/vsql/lexer.v
+++ b/vsql/lexer.v
@@ -6,27 +6,27 @@ module vsql
// Except for the eof and the keywords, the other tokens use the names described
// in the SQL standard.
enum TokenKind {
- asterisk // ::= *
- colon // ::= :
- comma // ::= ,
- concatenation_operator // ::= ||
- equals_operator // ::= =
- greater_than_operator // ::= >
+ asterisk // ::= *
+ colon // ::= :
+ comma // ::= ,
+ concatenation_operator // ::= ||
+ equals_operator // ::= =
+ greater_than_operator // ::= >
greater_than_or_equals_operator // ::= >=
keyword
- left_paren // ::= (
- less_than_operator // ::= <
+ left_paren // ::= (
+ less_than_operator // ::= <
less_than_or_equals_operator // ::= <=
- literal_identifier // foo or "foo" (delimited)
- literal_number // 123
- literal_string // 'hello'
- minus_sign // ::= -
- not_equals_operator // ::= <>
- period // ::= .
- plus_sign // ::= +
- right_paren // ::= )
- semicolon // ::= ;
- solidus // ::= /
+ literal_identifier // foo or "foo" (delimited)
+ literal_number // 123
+ literal_string // 'hello'
+ minus_sign // ::= -
+ not_equals_operator // ::= <>
+ period // ::= .
+ plus_sign // ::= +
+ right_paren // ::= )
+ semicolon // ::= ;
+ solidus // ::= /
}
struct Token {
diff --git a/vsql/numeric.v b/vsql/numeric.v
index 16ed1c3..dcf8606 100644
--- a/vsql/numeric.v
+++ b/vsql/numeric.v
@@ -24,15 +24,15 @@ struct Numeric {
fn new_numeric(typ Type, numerator big.Integer, denominator big.Integer) Numeric {
return Numeric{
- typ: typ
- numerator: numerator
+ typ: typ
+ numerator: numerator
denominator: denominator
}
}
fn new_null_numeric(typ Type) Numeric {
return Numeric{
- typ: typ
+ typ: typ
is_null: true
}
}
@@ -110,18 +110,18 @@ fn (n Numeric) bytes() []u8 {
mut buf := new_bytes([]u8{})
mut flags := u8(0)
if n.is_null {
- flags |= vsql.numeric_is_null
+ flags |= numeric_is_null
}
if n.is_zero() {
- flags |= vsql.numeric_is_zero
+ flags |= numeric_is_zero
}
if n.is_negative() {
- flags |= vsql.numeric_is_negative
+ flags |= numeric_is_negative
}
buf.write_u8(flags)
// If the value is NULL or 0 we don't need to encode anything further.
- if flags & vsql.numeric_is_null != 0 || flags & vsql.numeric_is_zero != 0 {
+ if flags & numeric_is_null != 0 || flags & numeric_is_zero != 0 {
return buf.bytes()
}
@@ -140,11 +140,11 @@ fn new_numeric_from_bytes(typ Type, data []u8) Numeric {
mut buf := new_bytes(data)
flags := buf.read_u8()
- if flags & vsql.numeric_is_null != 0 {
+ if flags & numeric_is_null != 0 {
return new_null_numeric(typ)
}
- if flags & vsql.numeric_is_zero != 0 {
+ if flags & numeric_is_zero != 0 {
return new_numeric(typ, big.zero_int, big.zero_int)
}
@@ -154,7 +154,7 @@ fn new_numeric_from_bytes(typ Type, data []u8) Numeric {
denominator_len := buf.read_i16()
denominator := big.integer_from_bytes(buf.read_u8s(denominator_len), big.IntegerConfig{})
- if flags & vsql.numeric_is_negative != 0 {
+ if flags & numeric_is_negative != 0 {
numerator = numerator.neg()
}
@@ -214,9 +214,9 @@ fn (n Numeric) round(scale i16) Numeric {
denominator := n.denominator / big.integer_from_int(10).pow(u32(-(scale - n.denominator.str().len) - 1))
return new_numeric(Type{
- typ: n.typ.typ
- size: n.typ.size
- scale: scale
+ typ: n.typ.typ
+ size: n.typ.size
+ scale: scale
not_null: n.typ.not_null
}, numerator, denominator)
}
diff --git a/vsql/page.v b/vsql/page.v
index 4de7bfd..e10da57 100644
--- a/vsql/page.v
+++ b/vsql/page.v
@@ -80,7 +80,7 @@ fn new_reference_object(key []u8, tid int, xid int, blob_peices int, has_fragmen
}
fn (o PageObject) length() int {
- return vsql.page_object_prefix_length + o.key.len + o.value.len
+ return page_object_prefix_length + o.key.len + o.value.len
}
// blob_info only applies to blob objects.
@@ -113,7 +113,7 @@ fn parse_page_object(data []u8) (int, PageObject) {
key_len := buf.read_i16()
key := buf.read_u8s(key_len)
is_blob_ref := buf.read_bool()
- value := buf.read_u8s(total_len - vsql.page_object_prefix_length - key_len)
+ value := buf.read_u8s(total_len - page_object_prefix_length - key_len)
return total_len, PageObject{key, value, is_blob_ref, tid, xid}
}
@@ -132,17 +132,17 @@ mut:
fn new_page(kind u8, page_size int) &Page {
return &Page{
kind: kind
- used: vsql.page_header_size // includes kind and self
- data: []u8{len: page_size - vsql.page_header_size}
+ used: page_header_size // includes kind and self
+ data: []u8{len: page_size - page_header_size}
}
}
fn (p Page) is_empty() bool {
- return p.used == vsql.page_header_size
+ return p.used == page_header_size
}
fn (p Page) page_size() int {
- return p.data.len + vsql.page_header_size
+ return p.data.len + page_header_size
}
// TODO(elliotchance): This really isn't the most efficient way to do this. Make
@@ -307,7 +307,7 @@ fn (p Page) objects() []PageObject {
mut objects := []PageObject{}
mut n := 0
- for n < p.used - vsql.page_header_size {
+ for n < p.used - page_header_size {
// Be careful to clone the size as the underlying data might get moved
// around.
m, object := parse_page_object(p.data[n..].clone())
diff --git a/vsql/pager.v b/vsql/pager.v
index 3f875ba..1637eda 100644
--- a/vsql/pager.v
+++ b/vsql/pager.v
@@ -85,7 +85,7 @@ fn new_file_pager(mut file os.File, page_size int, root_page int) !&FilePager {
file_len := file.tell() or { return error('unable to get file length: ${err}') }
return &FilePager{
- file: file
+ file: file
page_size: page_size
root_page: root_page
// The first page is reserved for header information. We do not include
diff --git a/vsql/planner.v b/vsql/planner.v
index e11b125..0fc23ac 100644
--- a/vsql/planner.v
+++ b/vsql/planner.v
@@ -162,7 +162,7 @@ fn create_select_plan_without_join(body QuerySpecification, from_clause TablePri
mut subplan_columns := []Column{}
for col in subplan.columns() {
subplan_columns << Column{Identifier{
- entity_name: table_name.id()
+ entity_name: table_name.id()
sub_entity_name: col.name.sub_entity_name
}, col.typ, col.not_null}
}
@@ -170,7 +170,7 @@ fn create_select_plan_without_join(body QuerySpecification, from_clause TablePri
// NOTE: This has to be assigned to a variable otherwise the value
// is lost. This must be a bug in V.
table = Table{
- name: table_name
+ name: table_name
columns: subplan_columns
}
@@ -209,7 +209,7 @@ fn add_group_by_plan(mut plan Plan, group_clause []Identifier, select_exprs []De
// expressions contain an aggregate function we need to have an implicit
// GROUP BY for the whole set.
mut c := Compiler{
- conn: conn
+ conn: conn
params: params
tables: tables
}
@@ -228,7 +228,7 @@ fn add_group_by_plan(mut plan Plan, group_clause []Identifier, select_exprs []De
mut order := []SortSpecification{}
for col in group_clause {
order << SortSpecification{
- expr: ValueExpression(BooleanValueExpression{
+ expr: ValueExpression(BooleanValueExpression{
term: BooleanTerm{
factor: BooleanTest{
expr: BooleanPrimary(BooleanPredicand(NonparenthesizedValueExpressionPrimary(col)))
@@ -250,9 +250,9 @@ fn add_group_by_plan(mut plan Plan, group_clause []Identifier, select_exprs []De
fn create_delete_plan(stmt DeleteStatementSearched, params map[string]Value, mut conn Connection) !Plan {
select_stmt := QuerySpecification{
- exprs: AsteriskExpr(true)
+ exprs: AsteriskExpr(true)
table_expression: TableExpression{
- from_clause: TablePrimary{
+ from_clause: TablePrimary{
body: stmt.table_name
}
where_clause: stmt.where
@@ -266,9 +266,9 @@ fn create_delete_plan(stmt DeleteStatementSearched, params map[string]Value, mut
fn create_update_plan(stmt UpdateStatementSearched, params map[string]Value, mut conn Connection) !Plan {
select_stmt := QuerySpecification{
- exprs: AsteriskExpr(true)
+ exprs: AsteriskExpr(true)
table_expression: TableExpression{
- from_clause: TablePrimary{
+ from_clause: TablePrimary{
body: stmt.table_name
}
where_clause: stmt.where
@@ -288,7 +288,7 @@ fn create_query_expression_plan(stmt QueryExpression, params map[string]Value, m
mut order := []SortSpecification{}
for spec in stmt.order {
order << SortSpecification{
- expr: spec.expr
+ expr: spec.expr
is_asc: spec.is_asc
}
}
@@ -427,7 +427,7 @@ fn new_expr_operation(mut conn Connection, params map[string]Value, select_list
sub_entity_name: column_name
}
mut c := Compiler{
- conn: conn
+ conn: conn
params: params
tables: tables
}
@@ -452,7 +452,7 @@ fn (o ExprOperation) columns() Columns {
fn (mut o ExprOperation) execute(rows []Row) ![]Row {
mut c := Compiler{
- conn: o.conn
+ conn: o.conn
params: o.params
tables: o.tables
}
diff --git a/vsql/result.v b/vsql/result.v
index 323db15..460ad08 100644
--- a/vsql/result.v
+++ b/vsql/result.v
@@ -25,10 +25,10 @@ mut:
pub fn new_result(columns Columns, rows []Row, elapsed_parse time.Duration, elapsed_exec time.Duration) Result {
return Result{
- columns: columns
- rows: rows
+ columns: columns
+ rows: rows
elapsed_parse: elapsed_parse
- elapsed_exec: elapsed_exec
+ elapsed_exec: elapsed_exec
}
}
diff --git a/vsql/server.v b/vsql/server.v
index c1b75a9..691733a 100644
--- a/vsql/server.v
+++ b/vsql/server.v
@@ -29,14 +29,14 @@ pub fn new_server(options ServerOptions) Server {
catalog := &CatalogConnection{
catalog_name: catalog_name
- storage: new_storage(btree)
- options: default_connection_options()
+ storage: new_storage(btree)
+ options: default_connection_options()
}
return Server{options, &Connection{
query_cache: new_query_cache()
- now: default_now
- catalogs: {
+ now: default_now
+ catalogs: {
catalog_name: catalog
}
}}
diff --git a/vsql/sql_test.v b/vsql/sql_test.v
index 415ca1e..aa4d539 100644
--- a/vsql/sql_test.v
+++ b/vsql/sql_test.v
@@ -177,12 +177,12 @@ fn run_single_test(test SQLTest, query_cache &QueryCache, verbose bool, filter_l
mut db := open_database(':memory:', options)!
db.now = fn () (time.Time, i16) {
return time.new(time.Time{
- year: 2022
- month: 7
- day: 4
- hour: 14
- minute: 5
- second: 3
+ year: 2022
+ month: 7
+ day: 4
+ hour: 14
+ minute: 5
+ second: 3
nanosecond: 120056000
}), 300
}
diff --git a/vsql/std_aggregate_function.v b/vsql/std_aggregate_function.v
index 9dcff17..9a3a531 100644
--- a/vsql/std_aggregate_function.v
+++ b/vsql/std_aggregate_function.v
@@ -39,15 +39,15 @@ fn (e AggregateFunction) compile(mut c Compiler) !CompileResult {
match e {
AggregateFunctionCount {
compiled := Identifier{
- custom_id: 'COUNT(*)'
+ custom_id: 'COUNT(*)'
custom_typ: new_type('INTEGER', 0, 0)
}.compile(mut c)!
return CompileResult{
- run: fn [compiled] (mut conn Connection, data Row, params map[string]Value) !Value {
+ run: fn [compiled] (mut conn Connection, data Row, params map[string]Value) !Value {
return compiled.run(mut conn, data, params)!
}
- typ: new_type('INTEGER', 0, 0)
+ typ: new_type('INTEGER', 0, 0)
contains_agg: true
}
}
diff --git a/vsql/std_alter_sequence_generator_statement.v b/vsql/std_alter_sequence_generator_statement.v
index a63316d..2c577b4 100644
--- a/vsql/std_alter_sequence_generator_statement.v
+++ b/vsql/std_alter_sequence_generator_statement.v
@@ -39,7 +39,7 @@ struct AlterSequenceGeneratorStatement {
fn parse_alter_sequence_generator_statement(generator_name Identifier, options []SequenceGeneratorOption) !AlterSequenceGeneratorStatement {
return AlterSequenceGeneratorStatement{
- name: generator_name
+ name: generator_name
options: options
}
}
@@ -79,7 +79,7 @@ fn (stmt AlterSequenceGeneratorStatement) execute(mut conn Connection, params ma
old_sequence := catalog.storage.sequence(name)!
mut sequence := old_sequence.copy()
mut c := Compiler{
- conn: conn
+ conn: conn
params: params
}
diff --git a/vsql/std_between_predicate.v b/vsql/std_between_predicate.v
index 26c9e87..bee8900 100644
--- a/vsql/std_between_predicate.v
+++ b/vsql/std_between_predicate.v
@@ -53,7 +53,7 @@ fn (e BetweenPredicate) compile(mut c Compiler) !CompileResult {
compiled_right := e.right.compile(mut c)!
return CompileResult{
- run: fn [e, compiled_expr, compiled_left, compiled_right] (mut conn Connection, data Row, params map[string]Value) !Value {
+ run: fn [e, compiled_expr, compiled_left, compiled_right] (mut conn Connection, data Row, params map[string]Value) !Value {
expr := compiled_expr.run(mut conn, data, params)!
mut left := compiled_left.run(mut conn, data, params)!
mut right := compiled_right.run(mut conn, data, params)!
@@ -80,7 +80,7 @@ fn (e BetweenPredicate) compile(mut c Compiler) !CompileResult {
return new_boolean_value(result)
}
- typ: new_type('BOOLEAN', 0, 0)
+ typ: new_type('BOOLEAN', 0, 0)
contains_agg: compiled_expr.contains_agg || compiled_left.contains_agg
|| compiled_left.contains_agg
}
@@ -88,11 +88,11 @@ fn (e BetweenPredicate) compile(mut c Compiler) !CompileResult {
fn parse_between(expr RowValueConstructorPredicand, between BetweenPredicate) !BetweenPredicate {
return BetweenPredicate{
- not: between.not
+ not: between.not
symmetric: between.symmetric
- expr: expr
- left: between.left
- right: between.right
+ expr: expr
+ left: between.left
+ right: between.right
}
}
@@ -103,9 +103,9 @@ fn parse_between_1(is_true bool, left RowValueConstructorPredicand, right RowVal
fn parse_between_2(is_true bool, symmetric bool, left RowValueConstructorPredicand, right RowValueConstructorPredicand) !BetweenPredicate {
return BetweenPredicate{
- not: !is_true
+ not: !is_true
symmetric: symmetric
- left: left
- right: right
+ left: left
+ right: right
}
}
diff --git a/vsql/std_boolean_value_expression.v b/vsql/std_boolean_value_expression.v
index b36ffd1..d93f650 100644
--- a/vsql/std_boolean_value_expression.v
+++ b/vsql/std_boolean_value_expression.v
@@ -96,7 +96,7 @@ fn (e BooleanValueExpression) compile(mut c Compiler) !CompileResult {
compiled_b := e.term.compile(mut c)!
return CompileResult{
- run: fn [compiled_a, compiled_b] (mut conn Connection, data Row, params map[string]Value) !Value {
+ run: fn [compiled_a, compiled_b] (mut conn Connection, data Row, params map[string]Value) !Value {
a := compiled_a.run(mut conn, data, params)!
b := compiled_b.run(mut conn, data, params)!
@@ -117,7 +117,7 @@ fn (e BooleanValueExpression) compile(mut c Compiler) !CompileResult {
return b
}
- typ: new_type('BOOLEAN', 0, 0)
+ typ: new_type('BOOLEAN', 0, 0)
contains_agg: compiled_a.contains_agg || compiled_b.contains_agg
}
}
@@ -144,7 +144,7 @@ fn (e BooleanTerm) compile(mut c Compiler) !CompileResult {
compiled_b := e.factor.compile(mut c)!
return CompileResult{
- run: fn [compiled_a, compiled_b] (mut conn Connection, data Row, params map[string]Value) !Value {
+ run: fn [compiled_a, compiled_b] (mut conn Connection, data Row, params map[string]Value) !Value {
a := compiled_a.run(mut conn, data, params)!
b := compiled_b.run(mut conn, data, params)!
@@ -165,7 +165,7 @@ fn (e BooleanTerm) compile(mut c Compiler) !CompileResult {
return new_boolean_value(false)
}
- typ: new_type('BOOLEAN', 0, 0)
+ typ: new_type('BOOLEAN', 0, 0)
contains_agg: compiled_a.contains_agg || compiled_b.contains_agg
}
}
@@ -216,7 +216,7 @@ fn (e BooleanTest) compile(mut c Compiler) !CompileResult {
if v := e.value {
return CompileResult{
- run: fn [e, v, compiled] (mut conn Connection, data Row, params map[string]Value) !Value {
+ run: fn [e, v, compiled] (mut conn Connection, data Row, params map[string]Value) !Value {
// See ISO/IEC 9075-2:2016(E), 6.39, ,
// "Table 15 — Truth table for the IS boolean operator"
@@ -240,16 +240,16 @@ fn (e BooleanTest) compile(mut c Compiler) !CompileResult {
return e.unary_not(result)!
}
- typ: new_type('BOOLEAN', 0, 0)
+ typ: new_type('BOOLEAN', 0, 0)
contains_agg: compiled.contains_agg
}
}
return CompileResult{
- run: fn [e, compiled] (mut conn Connection, data Row, params map[string]Value) !Value {
+ run: fn [e, compiled] (mut conn Connection, data Row, params map[string]Value) !Value {
return e.unary_not(compiled.run(mut conn, data, params)!)!
}
- typ: compiled.typ
+ typ: compiled.typ
contains_agg: compiled.contains_agg
}
}
diff --git a/vsql/std_case_expression.v b/vsql/std_case_expression.v
index 6ad7f86..577384e 100644
--- a/vsql/std_case_expression.v
+++ b/vsql/std_case_expression.v
@@ -42,7 +42,7 @@ fn (e CaseExpression) compile(mut c Compiler) !CompileResult {
compiled_b := e.b.compile(mut c)!
return CompileResult{
- run: fn [compiled_a, compiled_b] (mut conn Connection, data Row, params map[string]Value) !Value {
+ run: fn [compiled_a, compiled_b] (mut conn Connection, data Row, params map[string]Value) !Value {
a := compiled_a.run(mut conn, data, params)!
b := compiled_b.run(mut conn, data, params)!
@@ -57,7 +57,7 @@ fn (e CaseExpression) compile(mut c Compiler) !CompileResult {
return a
}
- typ: compiled_a.typ
+ typ: compiled_a.typ
contains_agg: compiled_a.contains_agg || compiled_b.contains_agg
}
}
@@ -70,7 +70,7 @@ fn (e CaseExpression) compile(mut c Compiler) !CompileResult {
}
return CompileResult{
- run: fn [compiled_exprs] (mut conn Connection, data Row, params map[string]Value) !Value {
+ run: fn [compiled_exprs] (mut conn Connection, data Row, params map[string]Value) !Value {
mut typ := SQLType.is_varchar
mut first := true
for i, compiled_expr in compiled_exprs {
@@ -96,7 +96,7 @@ fn (e CaseExpression) compile(mut c Compiler) !CompileResult {
return new_null_value(value.typ.typ)
}
- typ: compiled_exprs[0].typ
+ typ: compiled_exprs[0].typ
contains_agg: contains_agg
}
}
diff --git a/vsql/std_cast_specification.v b/vsql/std_cast_specification.v
index e4f9443..3e18239 100644
--- a/vsql/std_cast_specification.v
+++ b/vsql/std_cast_specification.v
@@ -49,12 +49,12 @@ fn (e CastSpecification) compile(mut c Compiler) !CompileResult {
compiled_expr := e.expr.compile(mut c)!
return CompileResult{
- run: fn [e, compiled_expr] (mut conn Connection, data Row, params map[string]Value) !Value {
+ run: fn [e, compiled_expr] (mut conn Connection, data Row, params map[string]Value) !Value {
value := compiled_expr.run(mut conn, data, params)!
return cast(mut conn, 'for CAST', value, e.target)!
}
- typ: e.target
+ typ: e.target
contains_agg: compiled_expr.contains_agg
}
}
diff --git a/vsql/std_comparison_predicate.v b/vsql/std_comparison_predicate.v
index 4143719..a199073 100644
--- a/vsql/std_comparison_predicate.v
+++ b/vsql/std_comparison_predicate.v
@@ -39,7 +39,7 @@ fn (e ComparisonPredicate) compile(mut c Compiler) !CompileResult {
compiled_right := e.right.compile(mut c)!
return CompileResult{
- run: fn [e, compiled_left, compiled_right] (mut conn Connection, data Row, params map[string]Value) !Value {
+ run: fn [e, compiled_left, compiled_right] (mut conn Connection, data Row, params map[string]Value) !Value {
mut left := compiled_left.run(mut conn, data, params)!
mut right := compiled_right.run(mut conn, data, params)!
@@ -73,7 +73,7 @@ fn (e ComparisonPredicate) compile(mut c Compiler) !CompileResult {
}
})
}
- typ: new_type('BOOLEAN', 0, 0)
+ typ: new_type('BOOLEAN', 0, 0)
contains_agg: compiled_left.contains_agg || compiled_right.contains_agg
}
}
diff --git a/vsql/std_contextually_typed_value_specification.v b/vsql/std_contextually_typed_value_specification.v
index dbd96aa..dbce09e 100644
--- a/vsql/std_contextually_typed_value_specification.v
+++ b/vsql/std_contextually_typed_value_specification.v
@@ -31,10 +31,10 @@ fn (e NullSpecification) pstr(params map[string]Value) string {
fn (e NullSpecification) compile(mut c Compiler) !CompileResult {
if null_type := c.null_type {
return CompileResult{
- run: fn [null_type] (mut conn Connection, data Row, params map[string]Value) !Value {
+ run: fn [null_type] (mut conn Connection, data Row, params map[string]Value) !Value {
return new_null_value(null_type.typ)
}
- typ: null_type
+ typ: null_type
contains_agg: false
}
}
diff --git a/vsql/std_datetime_value_function.v b/vsql/std_datetime_value_function.v
index e3c656a..23b8278 100644
--- a/vsql/std_datetime_value_function.v
+++ b/vsql/std_datetime_value_function.v
@@ -58,10 +58,10 @@ fn (e DatetimeValueFunction) compile(mut c Compiler) !CompileResult {
now, _ := c.conn.now()
return CompileResult{
- run: fn [now] (mut conn Connection, data Row, params map[string]Value) !Value {
+ run: fn [now] (mut conn Connection, data Row, params map[string]Value) !Value {
return new_date_value(now.strftime('%Y-%m-%d'))!
}
- typ: new_type('DATE', 0, 0)
+ typ: new_type('DATE', 0, 0)
contains_agg: false
}
}
@@ -75,10 +75,10 @@ fn (e DatetimeValueFunction) compile(mut c Compiler) !CompileResult {
e2 := e
return CompileResult{
- run: fn [e2] (mut conn Connection, data Row, params map[string]Value) !Value {
+ run: fn [e2] (mut conn Connection, data Row, params map[string]Value) !Value {
return new_time_value(time_value(conn, e2.prec, true))!
}
- typ: new_type('TIME WITH TIME ZONE', 0, 0)
+ typ: new_type('TIME WITH TIME ZONE', 0, 0)
contains_agg: false
}
}
@@ -94,11 +94,11 @@ fn (e DatetimeValueFunction) compile(mut c Compiler) !CompileResult {
e2 := e
return CompileResult{
- run: fn [e2, now] (mut conn Connection, data Row, params map[string]Value) !Value {
+ run: fn [e2, now] (mut conn Connection, data Row, params map[string]Value) !Value {
return new_timestamp_value(now.strftime('%Y-%m-%d ') +
time_value(conn, e2.prec, true))!
}
- typ: new_type('TIMESTAMP WITH TIME ZONE', 0, 0)
+ typ: new_type('TIMESTAMP WITH TIME ZONE', 0, 0)
contains_agg: false
}
}
@@ -112,10 +112,10 @@ fn (e DatetimeValueFunction) compile(mut c Compiler) !CompileResult {
e2 := e
return CompileResult{
- run: fn [e2] (mut conn Connection, data Row, params map[string]Value) !Value {
+ run: fn [e2] (mut conn Connection, data Row, params map[string]Value) !Value {
return new_time_value(time_value(conn, e2.prec, false))!
}
- typ: new_type('TIME WITHOUT TIME ZONE', 0, 0)
+ typ: new_type('TIME WITHOUT TIME ZONE', 0, 0)
contains_agg: false
}
}
@@ -131,11 +131,11 @@ fn (e DatetimeValueFunction) compile(mut c Compiler) !CompileResult {
e2 := e
return CompileResult{
- run: fn [e2, now] (mut conn Connection, data Row, params map[string]Value) !Value {
+ run: fn [e2, now] (mut conn Connection, data Row, params map[string]Value) !Value {
return new_timestamp_value(now.strftime('%Y-%m-%d ') +
time_value(conn, e2.prec, false))!
}
- typ: new_type('TIMESTAMP WITHOUT TIME ZONE', 0, 0)
+ typ: new_type('TIMESTAMP WITHOUT TIME ZONE', 0, 0)
contains_agg: false
}
}
diff --git a/vsql/std_group_by_clause.v b/vsql/std_group_by_clause.v
index 9164022..8ab1b6c 100644
--- a/vsql/std_group_by_clause.v
+++ b/vsql/std_group_by_clause.v
@@ -72,14 +72,14 @@ fn new_group_operation(select_exprs []DerivedColumn, group_exprs []Identifier, p
}
mut c := Compiler{
- conn: conn
+ conn: conn
params: params
}
for expr in select_exprs {
compiled_expr := expr.expr.compile(mut c)!
if compiled_expr.contains_agg {
columns << Column{Identifier{
- custom_id: expr.expr.pstr(params)
+ custom_id: expr.expr.pstr(params)
custom_typ: compiled_expr.typ
}, compiled_expr.typ, false}
@@ -115,7 +115,7 @@ fn (o &GroupOperation) columns() Columns {
fn (mut o GroupOperation) execute(rows []Row) ![]Row {
mut c := Compiler{
- conn: o.conn
+ conn: o.conn
params: o.params
}
diff --git a/vsql/std_insert_statement.v b/vsql/std_insert_statement.v
index a91163a..a238cc0 100644
--- a/vsql/std_insert_statement.v
+++ b/vsql/std_insert_statement.v
@@ -41,7 +41,7 @@ fn parse_insert_statement(insertion_target Identifier, stmt InsertStatement) !St
fn parse_from_constructor(columns []Identifier, values []ContextuallyTypedRowValueConstructor) !InsertStatement {
return InsertStatement{
columns: columns
- values: values
+ values: values
}
}
@@ -96,8 +96,8 @@ fn (stmt InsertStatement) execute(mut conn Connection, params map[string]Value,
table_column := table.column(column_name)!
mut c := Compiler{
- conn: conn
- params: params
+ conn: conn
+ params: params
null_type: table_column.typ
}
raw_value := values[i].compile(mut c)!.run(mut conn, Row{}, params)!
diff --git a/vsql/std_like_predicate.v b/vsql/std_like_predicate.v
index d22b97c..33c8ee1 100644
--- a/vsql/std_like_predicate.v
+++ b/vsql/std_like_predicate.v
@@ -49,7 +49,7 @@ fn (e CharacterLikePredicate) compile(mut c Compiler) !CompileResult {
compiled_l := l.compile(mut c)!
return CompileResult{
- run: fn [e, compiled_l, compiled_right] (mut conn Connection, data Row, params map[string]Value) !Value {
+ run: fn [e, compiled_l, compiled_right] (mut conn Connection, data Row, params map[string]Value) !Value {
left := compiled_l.run(mut conn, data, params)!
right := compiled_right.run(mut conn, data, params)!
@@ -70,7 +70,7 @@ fn (e CharacterLikePredicate) compile(mut c Compiler) !CompileResult {
return new_boolean_value(result)
}
- typ: new_type('BOOLEAN', 0, 0)
+ typ: new_type('BOOLEAN', 0, 0)
contains_agg: compiled_right.contains_agg || compiled_l.contains_agg
}
}
diff --git a/vsql/std_names_and_identifiers.v b/vsql/std_names_and_identifiers.v
index 9ec7f3b..10174c9 100644
--- a/vsql/std_names_and_identifiers.v
+++ b/vsql/std_names_and_identifiers.v
@@ -138,7 +138,7 @@ fn new_identifier1(s string) !Identifier {
2 {
return Identifier{
catalog_name: parts[0]
- schema_name: parts[1]
+ schema_name: parts[1]
}
}
else {
@@ -165,8 +165,8 @@ fn new_identifier2(s string) !Identifier {
3 {
return Identifier{
catalog_name: parts[0]
- schema_name: parts[1]
- entity_name: parts[2]
+ schema_name: parts[1]
+ entity_name: parts[2]
}
}
else {
@@ -209,22 +209,22 @@ fn new_identifier3(s string) !Identifier {
}
2 {
return Identifier{
- entity_name: parts[0]
+ entity_name: parts[0]
sub_entity_name: parts[1]
}
}
3 {
return Identifier{
- schema_name: parts[0]
- entity_name: parts[1]
+ schema_name: parts[0]
+ entity_name: parts[1]
sub_entity_name: parts[2]
}
}
4 {
return Identifier{
- catalog_name: parts[0]
- schema_name: parts[1]
- entity_name: parts[2]
+ catalog_name: parts[0]
+ schema_name: parts[1]
+ entity_name: parts[2]
sub_entity_name: parts[3]
}
}
@@ -289,10 +289,10 @@ fn (e Identifier) compile(mut c Compiler) !CompileResult {
// removed in the future.
if e.custom_id != '' {
return CompileResult{
- run: fn [e] (mut conn Connection, data Row, params map[string]Value) !Value {
+ run: fn [e] (mut conn Connection, data Row, params map[string]Value) !Value {
return data.data[e.id()] or { return sqlstate_42601('unknown column: ${e}') }
}
- typ: e.custom_typ
+ typ: e.custom_typ
contains_agg: false
}
}
@@ -302,10 +302,10 @@ fn (e Identifier) compile(mut c Compiler) !CompileResult {
column := table.column(e.sub_entity_name) or { Column{} }
if column.name.sub_entity_name == e.sub_entity_name {
return CompileResult{
- run: fn [e] (mut conn Connection, data Row, params map[string]Value) !Value {
+ run: fn [e] (mut conn Connection, data Row, params map[string]Value) !Value {
return data.data[e.id()] or { return sqlstate_42601('unknown column: ${e}') }
}
- typ: column.typ
+ typ: column.typ
contains_agg: false
}
}
@@ -313,13 +313,21 @@ fn (e Identifier) compile(mut c Compiler) !CompileResult {
// 3. Try to use the context.
mut ident := c.conn.resolve_identifier(Identifier{
- catalog_name: if c.context.catalog_name != '' {
+ catalog_name: if c.context.catalog_name != '' {
c.context.catalog_name
} else {
e.catalog_name
}
- schema_name: if c.context.schema_name != '' { c.context.schema_name } else { e.schema_name }
- entity_name: if c.context.entity_name != '' { c.context.entity_name } else { e.entity_name }
+ schema_name: if c.context.schema_name != '' {
+ c.context.schema_name
+ } else {
+ e.schema_name
+ }
+ entity_name: if c.context.entity_name != '' {
+ c.context.entity_name
+ } else {
+ e.entity_name
+ }
sub_entity_name: e.sub_entity_name
})
mut catalog := c.conn.catalogs[ident.catalog_name] or {
@@ -331,12 +339,12 @@ fn (e Identifier) compile(mut c Compiler) !CompileResult {
column := table.column(ident.sub_entity_name) or { Column{} }
if column.name.sub_entity_name == ident.sub_entity_name {
return CompileResult{
- run: fn [ident] (mut conn Connection, data Row, params map[string]Value) !Value {
+ run: fn [ident] (mut conn Connection, data Row, params map[string]Value) !Value {
return data.data[ident.id()] or {
return sqlstate_42601('unknown column: ${ident}')
}
}
- typ: column.typ
+ typ: column.typ
contains_agg: false
}
}
@@ -351,12 +359,12 @@ fn (e Identifier) compile(mut c Compiler) !CompileResult {
ident = column.name
return CompileResult{
- run: fn [ident] (mut conn Connection, data Row, params map[string]Value) !Value {
+ run: fn [ident] (mut conn Connection, data Row, params map[string]Value) !Value {
return data.data[ident.id()] or {
return sqlstate_42601('unknown column: ${ident}')
}
}
- typ: column.typ
+ typ: column.typ
contains_agg: false
}
}
@@ -370,12 +378,12 @@ fn (e Identifier) compile(mut c Compiler) !CompileResult {
column := table.column(ident.sub_entity_name) or { Column{} }
if column.name.sub_entity_name == ident.sub_entity_name {
return CompileResult{
- run: fn [ident] (mut conn Connection, data Row, params map[string]Value) !Value {
+ run: fn [ident] (mut conn Connection, data Row, params map[string]Value) !Value {
return data.data[ident.id()] or {
return sqlstate_42601('unknown column: ${ident}')
}
}
- typ: column.typ
+ typ: column.typ
contains_agg: false
}
}
@@ -393,12 +401,12 @@ fn (e Identifier) compile(mut c Compiler) !CompileResult {
ident = column.name
return CompileResult{
- run: fn [ident] (mut conn Connection, data Row, params map[string]Value) !Value {
+ run: fn [ident] (mut conn Connection, data Row, params map[string]Value) !Value {
return data.data[ident.id()] or {
return sqlstate_42601('unknown column: ${ident}')
}
}
- typ: column.typ
+ typ: column.typ
contains_agg: false
}
}
@@ -513,10 +521,10 @@ fn (e HostParameterName) compile(mut c Compiler) !CompileResult {
p := c.params[e.name] or { return sqlstate_42p02(e.name) }
return CompileResult{
- run: fn [p] (mut conn Connection, data Row, params map[string]Value) !Value {
+ run: fn [p] (mut conn Connection, data Row, params map[string]Value) !Value {
return p
}
- typ: p.typ
+ typ: p.typ
contains_agg: false
}
}
diff --git a/vsql/std_next_value_expression.v b/vsql/std_next_value_expression.v
index 9c8384e..50171f5 100644
--- a/vsql/std_next_value_expression.v
+++ b/vsql/std_next_value_expression.v
@@ -25,12 +25,12 @@ fn (e NextValueExpression) compile(mut c Compiler) !CompileResult {
name := c.conn.resolve_identifier(e.name)
return CompileResult{
- run: fn [name, mut catalog] (mut conn Connection, data Row, params map[string]Value) !Value {
+ run: fn [name, mut catalog] (mut conn Connection, data Row, params map[string]Value) !Value {
next := catalog.storage.sequence_next_value(name)!
return new_bigint_value(next)
}
- typ: new_type('INTEGER', 0, 0)
+ typ: new_type('INTEGER', 0, 0)
contains_agg: false
}
}
diff --git a/vsql/std_null_predicate.v b/vsql/std_null_predicate.v
index e80cba1..b645545 100644
--- a/vsql/std_null_predicate.v
+++ b/vsql/std_null_predicate.v
@@ -33,7 +33,7 @@ fn (e NullPredicate) compile(mut c Compiler) !CompileResult {
compiled := e.expr.compile(mut c)!
return CompileResult{
- run: fn [e, compiled] (mut conn Connection, data Row, params map[string]Value) !Value {
+ run: fn [e, compiled] (mut conn Connection, data Row, params map[string]Value) !Value {
value := compiled.run(mut conn, data, params)!
if e.not {
@@ -42,7 +42,7 @@ fn (e NullPredicate) compile(mut c Compiler) !CompileResult {
return new_boolean_value(value.is_null)
}
- typ: new_type('BOOLEAN', 0, 0)
+ typ: new_type('BOOLEAN', 0, 0)
contains_agg: compiled.contains_agg
}
}
diff --git a/vsql/std_numeric_value_expression.v b/vsql/std_numeric_value_expression.v
index 80cd83a..05d8fdb 100644
--- a/vsql/std_numeric_value_expression.v
+++ b/vsql/std_numeric_value_expression.v
@@ -56,7 +56,7 @@ fn (e NumericValueExpression) compile(mut c Compiler) !CompileResult {
// TODO(elliotchance): This is not correct, we would have to return
// the highest resolution type (need to check the SQL standard about
// this behavior).
- typ: compiled_n.typ
+ typ: compiled_n.typ
contains_agg: compiled_term.contains_agg || compiled_n.contains_agg
}
}
@@ -85,13 +85,13 @@ fn (e Term) compile(mut c Compiler) !CompileResult {
compiled_term := term.compile(mut c)!
return CompileResult{
- run: fn [e, compiled_term, compiled_factor] (mut conn Connection, data Row, params map[string]Value) !Value {
+ run: fn [e, compiled_term, compiled_factor] (mut conn Connection, data Row, params map[string]Value) !Value {
mut left := compiled_term.run(mut conn, data, params)!
mut right := compiled_factor.run(mut conn, data, params)!
return eval_binary(mut conn, data, left, e.op, right, params)!
}
- typ: compiled_term.typ
+ typ: compiled_term.typ
contains_agg: compiled_factor.contains_agg || compiled_term.contains_agg
}
}
@@ -112,7 +112,7 @@ fn (e SignedValueExpressionPrimary) compile(mut c Compiler) !CompileResult {
compiled := e.e.compile(mut c)!
return CompileResult{
- run: fn [e, compiled] (mut conn Connection, data Row, params map[string]Value) !Value {
+ run: fn [e, compiled] (mut conn Connection, data Row, params map[string]Value) !Value {
value := compiled.run(mut conn, data, params)!
key := '${e.sign} ${value.typ.typ}'
@@ -123,7 +123,7 @@ fn (e SignedValueExpressionPrimary) compile(mut c Compiler) !CompileResult {
return sqlstate_42883('operator does not exist: ${key}')
}
- typ: compiled.typ
+ typ: compiled.typ
contains_agg: compiled.contains_agg
}
}
diff --git a/vsql/std_query_expression.v b/vsql/std_query_expression.v
index 4034d1e..cef2b84 100644
--- a/vsql/std_query_expression.v
+++ b/vsql/std_query_expression.v
@@ -87,36 +87,36 @@ fn parse_query_expression(body SimpleTable) !QueryExpression {
fn parse_query_expression_order(body SimpleTable, order []SortSpecification) !QueryExpression {
return QueryExpression{
- body: body
+ body: body
order: order
}
}
fn parse_query_expression_offset(body SimpleTable, offset ValueSpecification) !QueryExpression {
return QueryExpression{
- body: body
+ body: body
offset: offset
}
}
fn parse_query_expression_order_offset(body SimpleTable, order []SortSpecification, offset ValueSpecification) !QueryExpression {
return QueryExpression{
- body: body
+ body: body
offset: offset
- order: order
+ order: order
}
}
fn parse_query_expression_fetch(body SimpleTable, fetch ValueSpecification) !QueryExpression {
return QueryExpression{
- body: body
+ body: body
fetch: fetch
}
}
fn parse_query_expression_order_fetch(body SimpleTable, order []SortSpecification, fetch ValueSpecification) !QueryExpression {
return QueryExpression{
- body: body
+ body: body
fetch: fetch
order: order
}
@@ -124,18 +124,18 @@ fn parse_query_expression_order_fetch(body SimpleTable, order []SortSpecificatio
fn parse_query_expression_offset_fetch(body SimpleTable, offset ValueSpecification, fetch ValueSpecification) !QueryExpression {
return QueryExpression{
- body: body
+ body: body
offset: offset
- fetch: fetch
+ fetch: fetch
}
}
fn parse_query_expression_order_offset_fetch(body SimpleTable, order []SortSpecification, offset ValueSpecification, fetch ValueSpecification) !QueryExpression {
return QueryExpression{
- body: body
+ body: body
offset: offset
- fetch: fetch
- order: order
+ fetch: fetch
+ order: order
}
}
@@ -231,7 +231,7 @@ fn (mut o OrderOperation) execute(rows []Row) ![]Row {
head_cmp := row_cmp(mut o.conn, o.params, row, head.row, o.order)!
if head_cmp < 0 {
head = &RowLink{
- row: row
+ row: row
next: head
}
continue
@@ -244,7 +244,7 @@ fn (mut o OrderOperation) execute(rows []Row) ![]Row {
cmp := row_cmp(mut o.conn, o.params, row, cursor.next.row, o.order)!
if cmp < 0 {
cursor.next = &RowLink{
- row: row
+ row: row
next: cursor.next
}
inserted = true
@@ -291,7 +291,7 @@ fn (l &RowLink) rows() []Row {
fn row_cmp(mut conn Connection, params map[string]Value, r1 Row, r2 Row, specs []SortSpecification) !int {
mut c := Compiler{
- conn: conn
+ conn: conn
params: params
}
@@ -436,7 +436,7 @@ fn (o &LimitOperation) columns() Columns {
fn (mut o LimitOperation) execute(rows []Row) ![]Row {
mut c := Compiler{
- conn: o.conn
+ conn: o.conn
params: o.params
}
mut offset := i64(0)
diff --git a/vsql/std_query_specification.v b/vsql/std_query_specification.v
index 1ab8a38..2264f02 100644
--- a/vsql/std_query_specification.v
+++ b/vsql/std_query_specification.v
@@ -65,7 +65,7 @@ struct QuerySpecification {
fn parse_query_specification(select_list SelectList, table_expression TableExpression) !SimpleTable {
return QuerySpecification{
- exprs: select_list
+ exprs: select_list
table_expression: table_expression
}
}
diff --git a/vsql/std_routine_invocation.v b/vsql/std_routine_invocation.v
index 45fa376..12834a8 100644
--- a/vsql/std_routine_invocation.v
+++ b/vsql/std_routine_invocation.v
@@ -45,7 +45,7 @@ fn (e RoutineInvocation) compile(mut c Compiler) !CompileResult {
if found_func.is_agg {
return Identifier{
- custom_id: e.pstr(c.params)
+ custom_id: e.pstr(c.params)
custom_typ: found_func.return_type
}.compile(mut c)!.with_agg(true)
}
@@ -62,7 +62,7 @@ fn (e RoutineInvocation) compile(mut c Compiler) !CompileResult {
}
return CompileResult{
- run: fn [found_func, func_name, arg_types, compiled_args] (mut conn Connection, data Row, params map[string]Value) !Value {
+ run: fn [found_func, func_name, arg_types, compiled_args] (mut conn Connection, data Row, params map[string]Value) !Value {
mut args := []Value{}
mut i := 0
for typ in arg_types {
@@ -73,7 +73,7 @@ fn (e RoutineInvocation) compile(mut c Compiler) !CompileResult {
return found_func.func(args)!
}
- typ: found_func.return_type
+ typ: found_func.return_type
contains_agg: found_func.is_agg
}
}
diff --git a/vsql/std_row_value_constructor.v b/vsql/std_row_value_constructor.v
index be93d06..67c17ce 100644
--- a/vsql/std_row_value_constructor.v
+++ b/vsql/std_row_value_constructor.v
@@ -158,7 +158,7 @@ fn (r RowValueConstructor) eval_row(mut conn Connection, data Row, params map[st
mut col_number := 1
mut row := map[string]Value{}
mut c := Compiler{
- conn: conn
+ conn: conn
params: params
}
match r {
diff --git a/vsql/std_search_condition.v b/vsql/std_search_condition.v
index d993e92..987e9fe 100644
--- a/vsql/std_search_condition.v
+++ b/vsql/std_search_condition.v
@@ -52,7 +52,7 @@ fn (mut o WhereOperation) execute(rows []Row) ![]Row {
fn eval_as_bool(mut conn Connection, data Row, e BooleanValueExpression, params map[string]Value, tables map[string]Table) !bool {
mut c := Compiler{
- conn: conn
+ conn: conn
params: params
tables: tables
}
diff --git a/vsql/std_sequence_generator_definition.v b/vsql/std_sequence_generator_definition.v
index bb2bf08..3815834 100644
--- a/vsql/std_sequence_generator_definition.v
+++ b/vsql/std_sequence_generator_definition.v
@@ -116,7 +116,7 @@ fn parse_sequence_generator_definition_1(generator_name Identifier) !Stmt {
fn parse_sequence_generator_definition_2(generator_name Identifier, options []SequenceGeneratorOption) !Stmt {
return SequenceGeneratorDefinition{
- name: generator_name
+ name: generator_name
options: options
}
}
@@ -174,7 +174,7 @@ fn (stmt SequenceGeneratorDefinition) execute(mut conn Connection, params map[st
}
mut c := Compiler{
- conn: conn
+ conn: conn
params: params
}
mut catalog := conn.catalog()
@@ -236,14 +236,14 @@ fn (stmt SequenceGeneratorDefinition) execute(mut conn Connection, params map[st
}
sequence := Sequence{
- name: sequence_name
+ name: sequence_name
current_value: current_value
- increment_by: increment_by
- cycle: cycle
+ increment_by: increment_by
+ cycle: cycle
has_min_value: has_min_value
- min_value: min_value
+ min_value: min_value
has_max_value: has_max_value
- max_value: max_value
+ max_value: max_value
}
catalog.storage.create_sequence(sequence)!
diff --git a/vsql/std_set_catalog_statement.v b/vsql/std_set_catalog_statement.v
index 71eb7d3..715e0c2 100644
--- a/vsql/std_set_catalog_statement.v
+++ b/vsql/std_set_catalog_statement.v
@@ -41,7 +41,7 @@ fn (stmt SetCatalogStatement) execute(mut conn Connection, params map[string]Val
// This does not need to hold a write connection with the file.
mut c := Compiler{
- conn: conn
+ conn: conn
params: params
}
new_catalog := stmt.catalog_name.compile(mut c)!.run(mut conn, Row{}, map[string]Value{})!.str()
diff --git a/vsql/std_set_schema_statement.v b/vsql/std_set_schema_statement.v
index ed6d75a..4e00813 100644
--- a/vsql/std_set_schema_statement.v
+++ b/vsql/std_set_schema_statement.v
@@ -41,7 +41,7 @@ fn (stmt SetSchemaStatement) execute(mut conn Connection, params map[string]Valu
// This does not need to hold a write connection with the file.
mut c := Compiler{
- conn: conn
+ conn: conn
params: params
}
new_schema := stmt.schema_name.compile(mut c)!.run(mut conn, Row{}, map[string]Value{})!.str()
diff --git a/vsql/std_similar_predicate.v b/vsql/std_similar_predicate.v
index af7373f..9814de2 100644
--- a/vsql/std_similar_predicate.v
+++ b/vsql/std_similar_predicate.v
@@ -45,7 +45,7 @@ fn (e SimilarPredicate) compile(mut c Compiler) !CompileResult {
compiled_right := e.right.compile(mut c)!
return CompileResult{
- run: fn [e, compiled_l, compiled_right] (mut conn Connection, data Row, params map[string]Value) !Value {
+ run: fn [e, compiled_l, compiled_right] (mut conn Connection, data Row, params map[string]Value) !Value {
left := compiled_l.run(mut conn, data, params)!
right := compiled_right.run(mut conn, data, params)!
@@ -62,7 +62,7 @@ fn (e SimilarPredicate) compile(mut c Compiler) !CompileResult {
return new_boolean_value(result)
}
- typ: new_type('BOOLEAN', 0, 0)
+ typ: new_type('BOOLEAN', 0, 0)
contains_agg: compiled_l.contains_agg || compiled_right.contains_agg
}
}
diff --git a/vsql/std_sqlstate.v b/vsql/std_sqlstate.v
index b53ed65..9e4fafd 100644
--- a/vsql/std_sqlstate.v
+++ b/vsql/std_sqlstate.v
@@ -79,8 +79,8 @@ struct SQLState22001 {
fn sqlstate_22001(to Type) IError {
return SQLState22001{
code: sqlstate_to_int('22001')
- msg: 'string data right truncation for ${to}'
- to: to
+ msg: 'string data right truncation for ${to}'
+ to: to
}
}
@@ -92,7 +92,7 @@ struct SQLState22003 {
fn sqlstate_22003() IError {
return SQLState22003{
code: sqlstate_to_int('22003')
- msg: 'numeric value out of range'
+ msg: 'numeric value out of range'
}
}
@@ -105,7 +105,7 @@ struct SQLState2200H {
fn sqlstate_2200h(generator_name string) IError {
return SQLState2200H{
code: sqlstate_to_int('2200H')
- msg: 'sequence generator limit exceeded: ${generator_name}'
+ msg: 'sequence generator limit exceeded: ${generator_name}'
}
}
@@ -117,7 +117,7 @@ struct SQLState22012 {
fn sqlstate_22012() IError {
return SQLState22012{
code: sqlstate_to_int('22012')
- msg: 'division by zero'
+ msg: 'division by zero'
}
}
@@ -129,7 +129,7 @@ struct SQLState23502 {
fn sqlstate_23502(msg string) IError {
return SQLState23502{
code: sqlstate_to_int('23502')
- msg: 'violates non-null constraint: ${msg}'
+ msg: 'violates non-null constraint: ${msg}'
}
}
@@ -142,8 +142,8 @@ pub:
fn sqlstate_2bp01(object_name string) IError {
return SQLState2BP01{
- code: sqlstate_to_int('2BP01')
- msg: 'dependent objects still exist on ${object_name}'
+ code: sqlstate_to_int('2BP01')
+ msg: 'dependent objects still exist on ${object_name}'
object_name: object_name
}
}
@@ -157,8 +157,8 @@ pub:
fn sqlstate_3d000(catalog_name string) IError {
return SQLState3D000{
- code: sqlstate_to_int('3D000')
- msg: 'invalid catalog name: ${catalog_name}'
+ code: sqlstate_to_int('3D000')
+ msg: 'invalid catalog name: ${catalog_name}'
catalog_name: catalog_name
}
}
@@ -172,8 +172,8 @@ pub:
fn sqlstate_3f000(schema_name string) IError {
return SQLState3F000{
- code: sqlstate_to_int('3F000')
- msg: 'invalid schema name: ${schema_name}'
+ code: sqlstate_to_int('3F000')
+ msg: 'invalid schema name: ${schema_name}'
schema_name: schema_name
}
}
@@ -186,7 +186,7 @@ struct SQLState42601 {
fn sqlstate_42601(message string) IError {
return SQLState42601{
code: sqlstate_to_int('42601')
- msg: 'syntax error: ${message}'
+ msg: 'syntax error: ${message}'
}
}
@@ -199,8 +199,8 @@ pub:
fn sqlstate_42703(column_name string) IError {
return SQLState42703{
- code: sqlstate_to_int('42703')
- msg: 'no such column: ${column_name}'
+ code: sqlstate_to_int('42703')
+ msg: 'no such column: ${column_name}'
column_name: column_name
}
}
@@ -214,10 +214,10 @@ struct SQLState42804 {
fn sqlstate_42804(msg string, expected string, actual string) IError {
return SQLState42804{
- code: sqlstate_to_int('42804')
- msg: 'data type mismatch ${msg}: expected ${expected} but got ${actual}'
+ code: sqlstate_to_int('42804')
+ msg: 'data type mismatch ${msg}: expected ${expected} but got ${actual}'
expected: expected
- actual: actual
+ actual: actual
}
}
@@ -231,9 +231,9 @@ struct SQLState42846 {
fn sqlstate_42846(from Type, to Type) IError {
return SQLState42846{
code: sqlstate_to_int('42846')
- msg: 'cannot coerce ${from} to ${to}'
+ msg: 'cannot coerce ${from} to ${to}'
from: from
- to: to
+ to: to
}
}
@@ -249,8 +249,8 @@ pub:
fn sqlstate_42p01(entity_type string, entity_name string) IError {
return SQLState42P01{
- code: sqlstate_to_int('42P01')
- msg: 'no such ${entity_type}: ${entity_name}'
+ code: sqlstate_to_int('42P01')
+ msg: 'no such ${entity_type}: ${entity_name}'
entity_type: entity_type
entity_name: entity_name
}
@@ -265,8 +265,8 @@ pub:
fn sqlstate_42p06(schema_name string) IError {
return SQLState42P06{
- code: sqlstate_to_int('42P06')
- msg: 'duplicate schema: ${schema_name}'
+ code: sqlstate_to_int('42P06')
+ msg: 'duplicate schema: ${schema_name}'
schema_name: schema_name
}
}
@@ -280,8 +280,8 @@ pub:
fn sqlstate_42p07(table_name string) IError {
return SQLState42P07{
- code: sqlstate_to_int('42P07')
- msg: 'duplicate table: ${table_name}'
+ code: sqlstate_to_int('42P07')
+ msg: 'duplicate table: ${table_name}'
table_name: table_name
}
}
@@ -294,7 +294,7 @@ struct SQLState42883 {
fn sqlstate_42883(msg string) IError {
return SQLState42883{
code: sqlstate_to_int('42883')
- msg: msg
+ msg: msg
}
}
@@ -307,8 +307,8 @@ pub:
fn sqlstate_42p02(parameter_name string) IError {
return SQLState42P02{
- code: sqlstate_to_int('42P02')
- msg: 'parameter does not exist: ${parameter_name}'
+ code: sqlstate_to_int('42P02')
+ msg: 'parameter does not exist: ${parameter_name}'
parameter_name: parameter_name
}
}
@@ -321,7 +321,7 @@ struct SQLState25001 {
fn sqlstate_25001() IError {
return SQLState25001{
code: sqlstate_to_int('25001')
- msg: 'invalid transaction state: active sql transaction'
+ msg: 'invalid transaction state: active sql transaction'
}
}
@@ -333,7 +333,7 @@ struct SQLState2D000 {
fn sqlstate_2d000() IError {
return SQLState2D000{
code: sqlstate_to_int('2D000')
- msg: 'invalid transaction termination'
+ msg: 'invalid transaction termination'
}
}
@@ -345,7 +345,7 @@ struct SQLState0B000 {
fn sqlstate_0b000(msg string) IError {
return SQLState0B000{
code: sqlstate_to_int('0B000')
- msg: 'invalid transaction initiation: ${msg}'
+ msg: 'invalid transaction initiation: ${msg}'
}
}
@@ -357,7 +357,7 @@ struct SQLState40001 {
fn sqlstate_40001(message string) IError {
return SQLState40001{
code: sqlstate_to_int('40001')
- msg: 'serialization failure: ${message}'
+ msg: 'serialization failure: ${message}'
}
}
@@ -369,6 +369,6 @@ struct SQLState25P02 {
fn sqlstate_25p02() IError {
return SQLState25P02{
code: sqlstate_to_int('25P02')
- msg: 'transaction is aborted, commands ignored until end of transaction block'
+ msg: 'transaction is aborted, commands ignored until end of transaction block'
}
}
diff --git a/vsql/std_store_assignment.v b/vsql/std_store_assignment.v
index 096bdc2..8336f8e 100644
--- a/vsql/std_store_assignment.v
+++ b/vsql/std_store_assignment.v
@@ -557,17 +557,17 @@ fn cast_timestamp_without_to_timestamp_without(conn &Connection, v Value, to Typ
fn check_numeric_range(x Numeric, typ SQLType) ! {
match typ {
.is_smallint {
- if x.less_than(vsql.min_smallint) || x.greater_than(vsql.max_smallint) {
+ if x.less_than(min_smallint) || x.greater_than(max_smallint) {
return sqlstate_22003()
}
}
.is_integer {
- if x.less_than(vsql.min_integer) || x.greater_than(vsql.max_integer) {
+ if x.less_than(min_integer) || x.greater_than(max_integer) {
return sqlstate_22003()
}
}
.is_bigint {
- if x.less_than(vsql.min_bigint) || x.greater_than(vsql.max_bigint) {
+ if x.less_than(min_bigint) || x.greater_than(max_bigint) {
return sqlstate_22003()
}
}
diff --git a/vsql/std_string_value_expression.v b/vsql/std_string_value_expression.v
index 5e23839..8fad73b 100644
--- a/vsql/std_string_value_expression.v
+++ b/vsql/std_string_value_expression.v
@@ -77,7 +77,7 @@ fn (e Concatenation) compile(mut c Compiler) !CompileResult {
compiled_right := e.right.compile(mut c)!
return CompileResult{
- run: fn [compiled_left, compiled_right] (mut conn Connection, data Row, params map[string]Value) !Value {
+ run: fn [compiled_left, compiled_right] (mut conn Connection, data Row, params map[string]Value) !Value {
mut left := compiled_left.run(mut conn, data, params)!
mut right := compiled_right.run(mut conn, data, params)!
@@ -88,7 +88,7 @@ fn (e Concatenation) compile(mut c Compiler) !CompileResult {
return sqlstate_42883('operator does not exist: ${left.typ.typ} || ${right.typ.typ}')
}
- typ: new_type('CHARACTER VARYING', 0, 0)
+ typ: new_type('CHARACTER VARYING', 0, 0)
contains_agg: compiled_left.contains_agg || compiled_right.contains_agg
}
}
diff --git a/vsql/std_string_value_function.v b/vsql/std_string_value_function.v
index dddb84d..ccc07bb 100644
--- a/vsql/std_string_value_function.v
+++ b/vsql/std_string_value_function.v
@@ -108,8 +108,8 @@ fn (e CharacterSubstringFunction) compile(mut c Compiler) !CompileResult {
f.compile(mut c)!
} else {
CompileResult{
- run: unsafe { nil }
- typ: Type{}
+ run: unsafe { nil }
+ typ: Type{}
contains_agg: false
}
}
@@ -117,14 +117,14 @@ fn (e CharacterSubstringFunction) compile(mut c Compiler) !CompileResult {
f.compile(mut c)!
} else {
CompileResult{
- run: unsafe { nil }
- typ: Type{}
+ run: unsafe { nil }
+ typ: Type{}
contains_agg: false
}
}
return CompileResult{
- run: fn [e, compiled_value, compiled_from, compiled_for] (mut conn Connection, data Row, params map[string]Value) !Value {
+ run: fn [e, compiled_value, compiled_from, compiled_for] (mut conn Connection, data Row, params map[string]Value) !Value {
value := compiled_value.run(mut conn, data, params)!
mut from := 0
@@ -158,7 +158,7 @@ fn (e CharacterSubstringFunction) compile(mut c Compiler) !CompileResult {
return new_varchar_value(value.string_value().substr(from, from + @for))
}
- typ: new_type('CHARACTER VARYING', 0, 0)
+ typ: new_type('CHARACTER VARYING', 0, 0)
contains_agg: compiled_value.contains_agg
}
}
@@ -180,7 +180,7 @@ fn (e TrimFunction) compile(mut c Compiler) !CompileResult {
compiled_character := e.character.compile(mut c)!
return CompileResult{
- run: fn [e, compiled_source, compiled_character] (mut conn Connection, data Row, params map[string]Value) !Value {
+ run: fn [e, compiled_source, compiled_character] (mut conn Connection, data Row, params map[string]Value) !Value {
source := compiled_source.run(mut conn, data, params)!
character := compiled_character.run(mut conn, data, params)!
@@ -194,7 +194,7 @@ fn (e TrimFunction) compile(mut c Compiler) !CompileResult {
return new_varchar_value(source.string_value().trim(character.string_value()))
}
- typ: new_type('CHARACTER VARYING', 0, 0)
+ typ: new_type('CHARACTER VARYING', 0, 0)
contains_agg: compiled_source.contains_agg || compiled_character.contains_agg
}
}
diff --git a/vsql/std_table_definition.v b/vsql/std_table_definition.v
index ee0e6cd..8e8de11 100644
--- a/vsql/std_table_definition.v
+++ b/vsql/std_table_definition.v
@@ -95,9 +95,9 @@ fn (stmt TableDefinition) execute(mut conn Connection, params map[string]Value,
}
columns << Column{Identifier{
- catalog_name: table_name.catalog_name
- schema_name: table_name.schema_name
- entity_name: table_name.entity_name
+ catalog_name: table_name.catalog_name
+ schema_name: table_name.schema_name
+ entity_name: table_name.entity_name
sub_entity_name: table_element.name.sub_entity_name
}, table_element.typ, table_element.not_null}
}
diff --git a/vsql/std_table_reference.v b/vsql/std_table_reference.v
index facadcd..7afb706 100644
--- a/vsql/std_table_reference.v
+++ b/vsql/std_table_reference.v
@@ -105,7 +105,7 @@ fn parse_table_primary_identifier(name Identifier) !TablePrimary {
fn parse_table_primary_derived_2(body TablePrimary, correlation Correlation) !TablePrimary {
return TablePrimary{
- body: body.body
+ body: body.body
correlation: correlation
}
}
@@ -128,7 +128,7 @@ fn parse_correlation_1(name Identifier) !Correlation {
fn parse_correlation_2(name Identifier, columns []Identifier) !Correlation {
return Correlation{
- name: name
+ name: name
columns: columns
}
}
diff --git a/vsql/std_table_value_constructor.v b/vsql/std_table_value_constructor.v
index 31a1c2b..68a75f8 100644
--- a/vsql/std_table_value_constructor.v
+++ b/vsql/std_table_value_constructor.v
@@ -124,7 +124,7 @@ fn (o &ValuesOperation) str() string {
fn (o &ValuesOperation) columns() Columns {
mut c := Compiler{
- conn: o.conn
+ conn: o.conn
params: o.params
}
e := o.rows[0]
@@ -138,7 +138,7 @@ fn (o &ValuesOperation) columns() Columns {
typ := (e.exprs[i].compile(mut c) or { panic(err) }).typ
columns << Column{
name: column
- typ: typ
+ typ: typ
}
}
QueryExpression {
@@ -149,7 +149,7 @@ fn (o &ValuesOperation) columns() Columns {
CommonValueExpression, BooleanValueExpression {
columns << Column{
name: column
- typ: (e.compile(mut c) or { panic(err) }).typ
+ typ: (e.compile(mut c) or { panic(err) }).typ
}
}
}
@@ -172,7 +172,7 @@ fn (o &ValuesOperation) columns() Columns {
name: Identifier{
sub_entity_name: 'COL${i}'
}
- typ: typ
+ typ: typ
}
}
}
@@ -186,7 +186,7 @@ fn (o &ValuesOperation) columns() Columns {
name: Identifier{
sub_entity_name: 'COL1'
}
- typ: (e.compile(mut c) or { panic(err) }).typ
+ typ: (e.compile(mut c) or { panic(err) }).typ
}
}
}
@@ -196,7 +196,7 @@ fn (o &ValuesOperation) columns() Columns {
fn (mut o ValuesOperation) execute(_ []Row) ![]Row {
mut c := Compiler{
- conn: o.conn
+ conn: o.conn
params: o.params
}
offset := int((o.offset.compile(mut c)!.run(mut o.conn, Row{}, o.params)!).f64_value())
diff --git a/vsql/std_update_statement_searched.v b/vsql/std_update_statement_searched.v
index e8a0434..94f0139 100644
--- a/vsql/std_update_statement_searched.v
+++ b/vsql/std_update_statement_searched.v
@@ -52,7 +52,7 @@ fn (stmt UpdateStatementSearched) execute(mut conn Connection, params map[string
}
mut c := Compiler{
- conn: conn
+ conn: conn
params: params
}
mut catalog := conn.catalog()
@@ -77,8 +77,8 @@ fn (stmt UpdateStatementSearched) execute(mut conn Connection, params map[string
table_column := table.column(column_name)!
c.context = Identifier{
catalog_name: table_column.name.catalog_name
- schema_name: table_column.name.schema_name
- entity_name: table.name.entity_name
+ schema_name: table_column.name.schema_name
+ entity_name: table.name.entity_name
}
raw_value := match v {
diff --git a/vsql/std_value_specification_and_target_specification.v b/vsql/std_value_specification_and_target_specification.v
index dc7409c..4390ffb 100644
--- a/vsql/std_value_specification_and_target_specification.v
+++ b/vsql/std_value_specification_and_target_specification.v
@@ -74,19 +74,19 @@ fn (e GeneralValueSpecification) compile(mut c Compiler) !CompileResult {
}
CurrentCatalog {
return CompileResult{
- run: fn (mut conn Connection, data Row, params map[string]Value) !Value {
+ run: fn (mut conn Connection, data Row, params map[string]Value) !Value {
return new_varchar_value(conn.current_catalog)
}
- typ: new_type('CHARACTER VARYING', 0, 0)
+ typ: new_type('CHARACTER VARYING', 0, 0)
contains_agg: false
}
}
CurrentSchema {
return CompileResult{
- run: fn (mut conn Connection, data Row, params map[string]Value) !Value {
+ run: fn (mut conn Connection, data Row, params map[string]Value) !Value {
return new_varchar_value(conn.current_schema)
}
- typ: new_type('CHARACTER VARYING', 0, 0)
+ typ: new_type('CHARACTER VARYING', 0, 0)
contains_agg: false
}
}
diff --git a/vsql/table.v b/vsql/table.v
index 6fda268..8308948 100644
--- a/vsql/table.v
+++ b/vsql/table.v
@@ -117,9 +117,9 @@ fn new_table_from_bytes(data []u8, tid int, catalog_name string) Table {
scale := b.read_i16()
columns << Column{Identifier{
- catalog_name: catalog_name
- schema_name: table_name.schema_name
- entity_name: table_name.entity_name
+ catalog_name: catalog_name
+ schema_name: table_name.schema_name
+ entity_name: table_name.entity_name
sub_entity_name: column_name
}, type_from_number(column_type, size, scale), is_not_null}
}
diff --git a/vsql/time.v b/vsql/time.v
index de9874d..571a831 100644
--- a/vsql/time.v
+++ b/vsql/time.v
@@ -65,8 +65,8 @@ fn new_timestamp_from_string(s string) !Time {
expects_time_zone := s.len > 6 && (s[s.len - 6] == `+` || s[s.len - 6] == `-`)
mut re := regex.regex_opt(match expects_time_zone {
- true { vsql.unquoted_timestamp_with_time_zone_string }
- false { vsql.unquoted_timestamp_without_time_zone_string }
+ true { unquoted_timestamp_with_time_zone_string }
+ false { unquoted_timestamp_without_time_zone_string }
}) or { return error('cannot compile regex for timestamp: ${err}') }
if !re.matches_string(s) {
return sqlstate_42601('TIMESTAMP \'${s}\' is not valid')
@@ -93,7 +93,7 @@ fn new_timestamp_from_string(s string) !Time {
}
mut typ := Type{
- typ: if expects_time_zone {
+ typ: if expects_time_zone {
.is_timestamp_with_time_zone
} else {
.is_timestamp_without_time_zone
@@ -142,12 +142,12 @@ fn new_date_from_string(s string) !Time {
fn new_time_from_components(typ Type, year int, month int, day int, hour int, minute int, second int, microsecond int, time_zone i16) Time {
return Time{typ, time_zone, time.new(time.Time{
- year: year
- month: month
- day: day
- hour: hour
- minute: minute
- second: second
+ year: year
+ month: month
+ day: day
+ hour: hour
+ minute: minute
+ second: second
nanosecond: microsecond * 1000
})}
}
@@ -156,23 +156,23 @@ fn new_time_from_bytes(typ Type, bytes []u8) Time {
mut buf := new_bytes(bytes)
mut ts_i64 := buf.read_i64()
- year := int(ts_i64 / vsql.year_period)
- ts_i64 -= year * vsql.year_period
+ year := int(ts_i64 / year_period)
+ ts_i64 -= year * year_period
- month := int(ts_i64 / vsql.month_period)
- ts_i64 -= month * vsql.month_period
+ month := int(ts_i64 / month_period)
+ ts_i64 -= month * month_period
- day := int(ts_i64 / vsql.day_period)
- ts_i64 -= day * vsql.day_period
+ day := int(ts_i64 / day_period)
+ ts_i64 -= day * day_period
- hour := int(ts_i64 / vsql.hour_period)
- ts_i64 -= hour * vsql.hour_period
+ hour := int(ts_i64 / hour_period)
+ ts_i64 -= hour * hour_period
- minute := int(ts_i64 / vsql.minute_period)
- ts_i64 -= minute * vsql.minute_period
+ minute := int(ts_i64 / minute_period)
+ ts_i64 -= minute * minute_period
- second := int(ts_i64 / vsql.second_period)
- ts_i64 -= second * vsql.second_period
+ second := int(ts_i64 / second_period)
+ ts_i64 -= second * second_period
mut time_zone := i16(0)
if typ.typ == .is_time_with_time_zone || typ.typ == .is_timestamp_with_time_zone {
@@ -230,13 +230,13 @@ fn (t Time) i64() i64 {
// See i64() for details.
fn (t Time) time_i64() i64 {
- return t.t.hour * vsql.hour_period + t.t.minute * vsql.minute_period +
- t.t.second * vsql.second_period + int(t.t.nanosecond / 1000)
+ return t.t.hour * hour_period + t.t.minute * minute_period + t.t.second * second_period +
+ int(t.t.nanosecond / 1000)
}
// See i64() for details.
fn (t Time) date_i64() i64 {
- return t.t.year * vsql.year_period + t.t.month * vsql.month_period + t.t.day * vsql.day_period
+ return t.t.year * year_period + t.t.month * month_period + t.t.day * day_period
}
// Returns the Time formatted based on its type.
diff --git a/vsql/type.v b/vsql/type.v
index b75ccb1..f6f6af9 100644
--- a/vsql/type.v
+++ b/vsql/type.v
@@ -17,21 +17,21 @@ mut:
// Represents the fundamental SQL type.
enum SQLType {
- is_bigint // BIGINT
- is_boolean // BOOLEAN
- is_character // CHARACTER(n), CHAR(n), CHARACTER and CHAR
- is_double_precision // DOUBLE PRECISION, FLOAT and FLOAT(n)
- is_integer // INTEGER and INT
- is_real // REAL
- is_smallint // SMALLINT
- is_varchar // CHARACTER VARYING, CHAR VARYING and VARCHAR
- is_date // DATE
- is_time_without_time_zone // TIME, TIME WITHOUT TIME ZONE
- is_time_with_time_zone // TIME WITH TIME ZONE
+ is_bigint // BIGINT
+ is_boolean // BOOLEAN
+ is_character // CHARACTER(n), CHAR(n), CHARACTER and CHAR
+ is_double_precision // DOUBLE PRECISION, FLOAT and FLOAT(n)
+ is_integer // INTEGER and INT
+ is_real // REAL
+ is_smallint // SMALLINT
+ is_varchar // CHARACTER VARYING, CHAR VARYING and VARCHAR
+ is_date // DATE
+ is_time_without_time_zone // TIME, TIME WITHOUT TIME ZONE
+ is_time_with_time_zone // TIME WITH TIME ZONE
is_timestamp_without_time_zone // TIMESTAMP, TIMESTAMP WITHOUT TIME ZONE
- is_timestamp_with_time_zone // TIMESTAMP WITH TIME ZONE
- is_decimal // DECIMAL
- is_numeric // NUMERIC
+ is_timestamp_with_time_zone // TIMESTAMP WITH TIME ZONE
+ is_decimal // DECIMAL
+ is_numeric // NUMERIC
}
// The SQL representation, such as ``TIME WITHOUT TIME ZONE``.
diff --git a/vsql/value.v b/vsql/value.v
index 0295d9f..1acfe24 100644
--- a/vsql/value.v
+++ b/vsql/value.v
@@ -38,10 +38,10 @@ pub mut:
fn (e Value) compile(mut c Compiler) !CompileResult {
return CompileResult{
- run: fn [e] (mut conn Connection, data Row, params map[string]Value) !Value {
+ run: fn [e] (mut conn Connection, data Row, params map[string]Value) !Value {
return e
}
- typ: e.typ
+ typ: e.typ
contains_agg: false
}
}
@@ -70,7 +70,7 @@ mut:
// values need to have a type.
pub fn new_null_value(typ SQLType) Value {
return Value{
- typ: Type{typ, 0, 0, false}
+ typ: Type{typ, 0, 0, false}
is_null: true
}
}
@@ -80,7 +80,7 @@ pub fn new_null_value(typ SQLType) Value {
pub fn new_boolean_value(b bool) Value {
return Value{
typ: Type{.is_boolean, 0, 0, false}
- v: InternalValue{
+ v: InternalValue{
bool_value: if b { .is_true } else { .is_false }
}
}
@@ -90,7 +90,7 @@ pub fn new_boolean_value(b bool) Value {
// representation of ``BOOLEAN``.
pub fn new_unknown_value() Value {
return Value{
- typ: Type{.is_boolean, 0, 0, false}
+ typ: Type{.is_boolean, 0, 0, false}
is_null: true
}
}
@@ -99,7 +99,7 @@ pub fn new_unknown_value() Value {
pub fn new_double_precision_value(x f64) Value {
return Value{
typ: Type{.is_double_precision, 0, 0, false}
- v: InternalValue{
+ v: InternalValue{
f64_value: x
}
}
@@ -109,7 +109,7 @@ pub fn new_double_precision_value(x f64) Value {
pub fn new_integer_value(x int) Value {
return Value{
typ: Type{.is_integer, 0, 0, false}
- v: InternalValue{
+ v: InternalValue{
int_value: x
}
}
@@ -119,7 +119,7 @@ pub fn new_integer_value(x int) Value {
pub fn new_bigint_value(x i64) Value {
return Value{
typ: Type{.is_bigint, 0, 0, false}
- v: InternalValue{
+ v: InternalValue{
int_value: x
}
}
@@ -129,7 +129,7 @@ pub fn new_bigint_value(x i64) Value {
pub fn new_real_value(x f32) Value {
return Value{
typ: Type{.is_real, 0, 0, false}
- v: InternalValue{
+ v: InternalValue{
f64_value: x
}
}
@@ -139,7 +139,7 @@ pub fn new_real_value(x f32) Value {
pub fn new_smallint_value(x i16) Value {
return Value{
typ: Type{.is_smallint, 0, 0, false}
- v: InternalValue{
+ v: InternalValue{
int_value: x
}
}
@@ -149,7 +149,7 @@ pub fn new_smallint_value(x i16) Value {
pub fn new_varchar_value(x string) Value {
return Value{
typ: Type{.is_varchar, x.len, 0, false}
- v: InternalValue{
+ v: InternalValue{
string_value: x
}
}
@@ -160,7 +160,7 @@ pub fn new_varchar_value(x string) Value {
pub fn new_character_value(x string) Value {
return Value{
typ: Type{.is_character, x.len, 0, false}
- v: InternalValue{
+ v: InternalValue{
string_value: x
}
}
@@ -180,7 +180,7 @@ pub fn new_numeric_value(x string) Value {
return Value{
typ: n.typ
- v: InternalValue{
+ v: InternalValue{
numeric_value: n.normalize_denominator(n.typ)
}
}
@@ -201,7 +201,7 @@ pub fn new_decimal_value(x string) Value {
return Value{
typ: typ
- v: InternalValue{
+ v: InternalValue{
numeric_value: n.normalize_denominator(typ)
}
}
@@ -210,7 +210,7 @@ pub fn new_decimal_value(x string) Value {
fn new_numeric_value_from_numeric(n Numeric) Value {
return Value{
typ: n.typ
- v: InternalValue{
+ v: InternalValue{
numeric_value: n.normalize_denominator(n.typ)
}
}
@@ -221,7 +221,7 @@ fn new_decimal_value_from_numeric(n Numeric) Value {
return Value{
typ: typ
- v: InternalValue{
+ v: InternalValue{
numeric_value: n.normalize_denominator(n.typ)
}
}
@@ -233,7 +233,7 @@ pub fn new_timestamp_value(ts string) !Value {
return Value{
typ: t.typ
- v: InternalValue{
+ v: InternalValue{
time_value: t
}
}
@@ -245,7 +245,7 @@ pub fn new_time_value(ts string) !Value {
return Value{
typ: t.typ
- v: InternalValue{
+ v: InternalValue{
time_value: t
}
}
@@ -257,7 +257,7 @@ pub fn new_date_value(ts string) !Value {
return Value{
typ: t.typ
- v: InternalValue{
+ v: InternalValue{
time_value: t
}
}
diff --git a/vsql/virtual_table.v b/vsql/virtual_table.v
index 0bdd9d2..1dc30d2 100644
--- a/vsql/virtual_table.v
+++ b/vsql/virtual_table.v
@@ -35,8 +35,8 @@ pub fn (mut v VirtualTable) done() {
pub fn (v VirtualTable) table() Table {
return Table{
- name: v.create_table_stmt.table_name
- columns: v.create_table_stmt.columns()
+ name: v.create_table_stmt.table_name
+ columns: v.create_table_stmt.columns()
is_virtual: true
}
}
diff --git a/vsql/walk.v b/vsql/walk.v
index 34998b5..b1f8995 100644
--- a/vsql/walk.v
+++ b/vsql/walk.v
@@ -127,7 +127,7 @@ fn (o &PrimaryKeyOperation) columns() Columns {
fn (mut o PrimaryKeyOperation) execute(_ []Row) ![]Row {
mut c := Compiler{
- conn: o.conn
+ conn: o.conn
params: o.params
}
mut lower := o.lower.compile(mut c)!.run(mut o.conn, Row{}, o.params)!
From 7a9b776b208a222187defa5b0657150c650fd508 Mon Sep 17 00:00:00 2001
From: daniel-le97 <107774403+daniel-le97@users.noreply.github.com>
Date: Tue, 12 Nov 2024 13:13:24 -0700
Subject: [PATCH 04/13] chore: remove unnecessary comments, add more
descriptive comments
---
examples/orm.v | 3 +-
vsql/orm.v | 80 +++-----
vsql/orm_test.v | 486 ++++++++++++++++++++++--------------------------
3 files changed, 249 insertions(+), 320 deletions(-)
diff --git a/examples/orm.v b/examples/orm.v
index 8748210..91d4f66 100644
--- a/examples/orm.v
+++ b/examples/orm.v
@@ -11,7 +11,7 @@ fn main() {
}
// NOTE for some reason if we declare a @[primary] on a struct field, we can not do delete queries on the tables...
-// so for now we are not supporting primary keys
+// https://github.com/elliotchance/vsql/issues/200
struct Product {
id int //@[primary]
product_name string @[sql_type: 'varchar(100)']
@@ -32,7 +32,6 @@ fn example() ! {
Product{3, 'Bagel', 1.25},
]
- // product := Product{1, 'Ice Cream', 5.99}
for product in products {
sql db {
insert product into Product
diff --git a/vsql/orm.v b/vsql/orm.v
index afdb9c3..9a76e54 100644
--- a/vsql/orm.v
+++ b/vsql/orm.v
@@ -5,31 +5,28 @@ import time
pub const varchar_default_len = 255
-// DDL (table creation/destroying etc)
+// Returns a string of the vsql type based on the v type index
fn vsql_type_from_v(typ int) !string {
return if typ == orm.type_idx['i8'] || typ == orm.type_idx['i16'] || typ == orm.type_idx['u8'] {
'SMALLINT'
} else if typ == orm.type_idx['bool'] {
'BOOLEAN'
} else if typ == orm.type_idx['int'] || typ == orm.type_idx['u16'] || typ == 8 {
- 'INT'
+ 'INTEGER'
} else if typ == orm.type_idx['i64'] || typ == orm.type_idx['u32'] {
'BIGINT'
} else if typ == orm.type_idx['f32'] {
'REAL'
} else if typ == orm.type_idx['u64'] {
- // 'NUMERIC(20)'
'BIGINT'
} else if typ == orm.type_idx['f64'] {
'DOUBLE PRECISION'
} else if typ == orm.type_idx['string'] {
'VARCHAR(${varchar_default_len})'
} else if typ == orm.time_ {
- // time.Time will be converted to a string
- // will be parsed as a time.Time when read
'TIMESTAMP(6) WITHOUT TIME ZONE'
} else if typ == -1 {
- // SERIAL
+ // -1 is a field with @[sql: serial]
'INTEGER'
} else {
error('Unknown type ${typ}')
@@ -135,14 +132,21 @@ fn serial_name(table string) string {
return '${table}_SERIAL'
}
+fn reformat_table_name(table string) string {
+ mut new_table := table
+ if is_reserved_word(table) {
+ new_table = '"${table}"'
+ }
+ return new_table
+}
+
fn get_table_values_map(mut db Connection, table string, data []orm.QueryData) !map[string]Value {
mut mp := map[string]Value{}
mut tbl := Table{}
mut cat := db.catalog()
tables := cat.schema_tables('PUBLIC') or { [] }
- // println(tables)
+
for t in tables {
- // println(t.name.entity_name)
if t.name.entity_name == table.to_upper() {
tbl = t
break
@@ -179,9 +183,6 @@ fn query_reformatter(query string, query_data []orm.QueryData) string {
for data in query_data {
for field in data.fields {
// this if check is for if there are multiple of the same field being checked for
- // products = sql db {
- // select from Product where price > '3' && price < '3.50'
- // }!
if new_query.contains(':${field}') {
new_query = new_query.replace(':${counter}', ':${field}_${field_counter.str()}')
field_counter++
@@ -218,10 +219,6 @@ fn check_for_not_supported(mut db Connection, table string, fields []orm.TableFi
if attr.name == 'unique' {
return error('unique is not supported in vsql')
}
- if attr.name == 'primary' {
- // TODO (daniel-le97) figure out how we should be handling primary keys as it will break delete queries
- return error('primary key is supported, but currently will break delete queries')
- }
}
}
}
@@ -234,8 +231,6 @@ pub fn (mut db Connection) select(config orm.SelectConfig, data orm.QueryData, w
}
mut query := orm.orm_select_gen(conf, '', true, ':', 1, where)
query = query_reformatter(query, [data, where])
- // println('query: ${query}')
-
mut rows := Result{}
if data.data.len == 0 && where.data.len == 0 {
rows = db.query(query) or { return err }
@@ -263,9 +258,7 @@ pub fn (mut db Connection) select(config orm.SelectConfig, data orm.QueryData, w
pub fn (mut db Connection) insert(table string, data orm.QueryData) ! {
new_table := reformat_table_name(table)
mut tbl := get_table_values_map(mut db, table, [data]) or { return err }
-
mut query := 'INSERT INTO ${new_table} (${data.fields.join(', ')}) VALUES (:${data.fields.join(', :')})'
-
// if a table has a serial field, we need to remove it from the insert statement
// only allows one serial field per table, as do most RDBMS
if data.auto_fields.len > 1 {
@@ -276,9 +269,6 @@ pub fn (mut db Connection) insert(table string, data orm.QueryData) ! {
tbl.delete(autofield)
query = query.replace(':${autofield}', 'NEXT VALUE FOR "${serial_name(table)}"')
}
- $if trace_vsql_orm ? {
- eprintln('> vsql insert: ${query}')
- }
mut stmt := db.prepare(query) or { return err }
stmt.query(tbl) or { return err }
}
@@ -288,14 +278,8 @@ pub fn (mut db Connection) update(table string, data orm.QueryData, where orm.Qu
new_table := reformat_table_name(table)
mut query, _ := orm.orm_stmt_gen(.sqlite, new_table, '', .update, true, ':', 1, data,
where)
-
values := get_table_values_map(mut db, table, [data, where]) or { return err }
query = query_reformatter(query, [data, where])
- // println(query)
-
- $if trace_vsql_orm ? {
- eprintln('> vsql update: ${query}')
- }
mut stmt := db.prepare(query) or { return err }
stmt.query(values) or { return err }
}
@@ -305,14 +289,8 @@ pub fn (mut db Connection) delete(table string, where orm.QueryData) ! {
new_table := reformat_table_name(table)
mut query, _ := orm.orm_stmt_gen(.sqlite, new_table, '', .delete, true, ':', 1, orm.QueryData{},
where)
-
query = query_reformatter(query, [where])
values := get_table_values_map(mut db, table, [where]) or { return err }
-
- $if trace_vsql_orm ? {
- eprintln('> vsql delete: ${query}')
- }
- // db.query(query) or { return err }
mut stmt := db.prepare(query) or { return err }
stmt.query(values) or { return err }
}
@@ -323,14 +301,6 @@ pub fn (mut db Connection) last_id() int {
return 0
}
-fn reformat_table_name(table string) string {
- mut new_table := table
- if is_reserved_word(table) {
- new_table = '"${table}"'
- }
- return new_table
-}
-
// create is used internally by V's ORM for processing table creation queries (DDL)
pub fn (mut db Connection) create(table string, fields []orm.TableField) ! {
check_for_not_supported(mut db, table, fields) or { return err }
@@ -342,10 +312,6 @@ pub fn (mut db Connection) create(table string, fields []orm.TableField) ! {
query = query.replace(' IF NOT EXISTS ', ' ')
// `TEXT` is not supported in vsql, so we replace it with `VARCHAR(255) if its somehow used`
query = query.replace('TEXT', 'VARCHAR(${varchar_default_len})')
-
- $if trace_vsql_orm ? {
- eprintln('> vsql create: ${query}')
- }
db.query(query) or { return err }
}
@@ -353,21 +319,18 @@ pub fn (mut db Connection) create(table string, fields []orm.TableField) ! {
pub fn (mut db Connection) drop(table string) ! {
new_table := reformat_table_name(table)
mut query := 'DROP TABLE ${new_table};'
- $if trace_vsql_orm ? {
- eprintln('> vsql drop: ${query}')
- }
-
db.query(query) or { return err }
- // // check to see if there is a SEQUENCE for the table (for the @[sql: 'serial'] attribute)
- db.query('EXPLAIN DROP SEQUENCE "${serial_name(table)}"') or { return }
-
- // if we have not returned then we can drop the sequence
- query = 'DROP SEQUENCE "${serial_name(table)}"'
- $if trace_vsql_orm ? {
- eprintln('> vsql drop: ${query}')
+ // check to see if there is a SEQUENCE for the table (for the @[sql: 'serial'] attribute)
+ // if there is, drop it
+ mut cat := db.catalog()
+ seqs := cat.sequences('PUBLIC') or { [] }
+ for sequence in seqs {
+ if sequence.name.entity_name == serial_name(table).to_upper() {
+ query = 'DROP SEQUENCE "${serial_name(table)}"'
+ db.query(query) or { return err }
+ }
}
- db.query(query) or { return err }
}
// convert a vsql row value to orm.Primitive
@@ -375,7 +338,6 @@ fn (v Value) primitive() !orm.Primitive {
if v.is_null {
return orm.Null{}
}
-
return match v.typ.typ {
.is_boolean {
orm.Primitive(v.bool_value() == .is_true)
diff --git a/vsql/orm_test.v b/vsql/orm_test.v
index 94a7cf6..def53d3 100644
--- a/vsql/orm_test.v
+++ b/vsql/orm_test.v
@@ -1,6 +1,7 @@
module vsql
// Structs intentionally have less than 6 fields, any more then inserts queries get exponentially slower.
+// https://github.com/elliotchance/vsql/issues/199
import time
@[table: 'testcustomtable']
@@ -41,7 +42,7 @@ struct TestOrmValuesOne {
struct TestOrmValuesTwo {
an_i8 i8 // SMALLINT
an_int int // INTEGER
- a_string string // CHARACTER VARYING(255)
+ a_string string // CHARACTER VARYING(255) / VARCHAR(255)
}
struct TestOrmValuesThree {
@@ -54,7 +55,7 @@ struct TestOrmValuesThree {
struct TestOrmValuesFour {
a_time time.Time // TIMESTAMP(6) WITH TIME ZONE
a_bool bool // BOOLEAN
- int_or_null ?int // optional int
+ int_or_null ?int // optional INTEGER
}
// ORMTableEnum is not supported.
@@ -74,7 +75,7 @@ struct ORMTable2 {
}
struct Product {
- id int // @[primary] FIXME
+ id int
product_name string
price string @[sql_type: 'NUMERIC(5,2)']
quantity ?i16
@@ -115,34 +116,6 @@ fn test_orm_create_table_with_reserved_word() {
assert error == ''
}
-// we are not supporting primary key for now as it breaks delete queries
-fn test_primary_key_not_supported() {
- // $if test_primary_key ? {
- mut db := open(':memory:')!
- mut error := ''
- sql db {
- create table TestPrimaryBroken
- } or { error = err.str() }
- assert error == 'primary key is supported, but currently will break delete queries'
-
- // test_primary_broken := TestPrimaryBroken{}
-
- // sql db {
- // insert test_primary_broken into TestPrimaryBroken
- // } or { error = err.str() }
- // assert error == ''
-
- // mut all := sql db {
- // select from TestPrimaryBroken
- // }!
-
- // sql db {
- // delete from TestPrimaryBroken where id == all[0].id
- // } or { error = err.str() }
- // assert error != ''
- // }
-}
-
fn test_custom_table_name_and_serial_crud() {
mut db := open(':memory:')!
mut error := ''
@@ -192,245 +165,240 @@ fn test_custom_table_name_and_serial_crud() {
assert error == ''
}
-fn test_reserved_words() {
- mut db := open(':memory:')!
- mut error := ''
- sql db {
- create table TestReservedWordField
- } or { error = err.str() }
- assert error == 'reserved word where cannot be used as a field name at TestReservedWordField.where'
- sql db {
- create table TestReservedWordSqlAttribute
- } or { error = err.str() }
- assert error == 'ORDER is a reserved word in vsql'
-}
-
-fn test_unsupported_attributes() {
- mut db := open(':memory:')!
- mut error := ''
- sql db {
- create table TestDefaultAttribute
- } or { error = err.str() }
- assert error == 'default is not supported in vsql'
- sql db {
- create table TestUniqueAttribute
- } or { error = err.str() }
- assert error == 'unique is not supported in vsql'
-}
-
-fn test_default_orm_values() {
- mut db := open(':memory:')!
- mut error := ''
- sql db {
- create table TestOrmValuesOne
- create table TestOrmValuesTwo
- create table TestOrmValuesThree
- create table TestOrmValuesFour
- } or { error = err.str() }
- assert error == ''
-
- values_one := TestOrmValuesOne{
- an_f32: 3.14
- an_f64: 2.718281828459
- an_i16: 12345
- an_i64: 123456789012345
- }
- values_two := TestOrmValuesTwo{
- an_i8: 12
- an_int: 123456
- a_string: 'Hello, World!'
- }
- values_three := TestOrmValuesThree{
- an_u16: 54321
- an_u32: 1234567890
- an_u64: 1234
- an_u8: 255
- }
-
- values_four := TestOrmValuesFour{
- a_time: time.now()
- a_bool: true
- // int_or_null: 123
- }
- values_four_b := TestOrmValuesFour{
- a_time: time.now()
- a_bool: false
- int_or_null: 123
- }
-
- sql db {
- insert values_one into TestOrmValuesOne
- insert values_two into TestOrmValuesTwo
- insert values_three into TestOrmValuesThree
- insert values_four into TestOrmValuesFour
- insert values_four_b into TestOrmValuesFour
- } or { error = err.str() }
- assert error == ''
-
- result_values_one := sql db {
- select from TestOrmValuesOne
- }!
- one := result_values_one[0]
-
- assert typeof(one.an_f32).idx == typeof[f32]().idx
- assert one.an_f32 == 3.14
- assert typeof(one.an_f64).idx == typeof[f64]().idx
- assert one.an_f64 == 2.718281828459
- assert typeof(one.an_i16).idx == typeof[i16]().idx
- assert one.an_i16 == 12345
- assert typeof(one.an_i64).idx == typeof[i64]().idx
- assert one.an_i64 == 123456789012345
-
- result_values_two := sql db {
- select from TestOrmValuesTwo
- }!
-
- two := result_values_two[0]
-
- assert typeof(two.an_i8).idx == typeof[i8]().idx
- assert two.an_i8 == 12
- assert typeof(two.an_int).idx == typeof[int]().idx
- assert two.an_int == 123456
- assert typeof(two.a_string).idx == typeof[string]().idx
- assert two.a_string == 'Hello, World!'
-
- result_values_three := sql db {
- select from TestOrmValuesThree
- }!
-
- three := result_values_three[0]
-
- assert typeof(three.an_u16).idx == typeof[u16]().idx
- assert three.an_u16 == 54321
- assert typeof(three.an_u32).idx == typeof[u32]().idx
- assert three.an_u32 == 1234567890
- // println(three.an_u64)
- assert typeof(three.an_u64).idx == typeof[u64]().idx
- assert three.an_u64 == 1234
- assert typeof(three.an_u8).idx == typeof[u8]().idx
- assert three.an_u8 == 255
-
- result_values_four := sql db {
- select from TestOrmValuesFour
- }!
-
- four := result_values_four[0]
-
- assert typeof(four.a_time).idx == typeof[time.Time]().idx
- assert typeof(four.a_bool).idx == typeof[bool]().idx
- assert four.a_bool == true
- assert typeof(four.int_or_null).idx == typeof[?int]().idx
- unwrapped_option_one := four.int_or_null or { 0 }
- assert unwrapped_option_one == 0
- unwrapped_option_two := result_values_four[1].int_or_null or { 0 }
- assert unwrapped_option_two == 123
-}
-
-fn test_orm_create_enum_is_not_supported() {
- mut db := open(':memory:')!
- mut error := ''
- sql db {
- create table ORMTableEnum
- } or { error = err.str() }
- assert error == 'enum is not supported in vsql'
-}
-
-fn test_orm_select_where() {
- mut db := open(':memory:')!
- mut error := ''
-
- sql db {
- create table Product
- } or { panic(err) }
-
- prods := [
- Product{1, 'Ice Cream', '5.99', 17},
- Product{2, 'Ham Sandwhich', '3.47', none},
- Product{3, 'Bagel', '1.25', 45},
- ]
- for product in prods {
- sql db {
- insert product into Product
- } or { panic(err) }
- }
- mut products := sql db {
- select from Product where id == 2
- }!
-
- assert products == [Product{2, 'Ham Sandwhich', '3.47', none}]
-
- products = sql db {
- select from Product where id == 5
- }!
-
- assert products == []
-
- products = sql db {
- select from Product where id != 3
- }!
+// fn test_reserved_words() {
+// mut db := open(':memory:')!
+// mut error := ''
+// sql db {
+// create table TestReservedWordField
+// } or { error = err.str() }
+// assert error == 'reserved word where cannot be used as a field name at TestReservedWordField.where'
+// sql db {
+// create table TestReservedWordSqlAttribute
+// } or { error = err.str() }
+// assert error == 'ORDER is a reserved word in vsql'
+// }
+
+// fn test_unsupported_attributes() {
+// mut db := open(':memory:')!
+// mut error := ''
+// sql db {
+// create table TestDefaultAttribute
+// } or { error = err.str() }
+// assert error == 'default is not supported in vsql'
+// sql db {
+// create table TestUniqueAttribute
+// } or { error = err.str() }
+// assert error == 'unique is not supported in vsql'
+// }
+
+// fn test_default_orm_values() {
+// mut db := open(':memory:')!
+// mut error := ''
+// sql db {
+// create table TestOrmValuesOne
+// create table TestOrmValuesTwo
+// create table TestOrmValuesThree
+// create table TestOrmValuesFour
+// } or { error = err.str() }
+// assert error == ''
+
+// values_one := TestOrmValuesOne{
+// an_f32: 3.14
+// an_f64: 2.718281828459
+// an_i16: 12345
+// an_i64: 123456789012345
+// }
+// values_two := TestOrmValuesTwo{
+// an_i8: 12
+// an_int: 123456
+// a_string: 'Hello, World!'
+// }
+// values_three := TestOrmValuesThree{
+// an_u16: 54321
+// an_u32: 1234567890
+// an_u64: 1234
+// an_u8: 255
+// }
+
+// values_four := TestOrmValuesFour{
+// a_time: time.now()
+// a_bool: true
+// // int_or_null: 123
+// }
+// values_four_b := TestOrmValuesFour{
+// a_time: time.now()
+// a_bool: false
+// int_or_null: 123
+// }
+
+// sql db {
+// insert values_one into TestOrmValuesOne
+// insert values_two into TestOrmValuesTwo
+// insert values_three into TestOrmValuesThree
+// insert values_four into TestOrmValuesFour
+// insert values_four_b into TestOrmValuesFour
+// } or { error = err.str() }
+// assert error == ''
+
+// result_values_one := sql db {
+// select from TestOrmValuesOne
+// }!
+// one := result_values_one[0]
+
+// assert typeof(one.an_f32).idx == typeof[f32]().idx
+// assert one.an_f32 == 3.14
+// assert typeof(one.an_f64).idx == typeof[f64]().idx
+// assert one.an_f64 == 2.718281828459
+// assert typeof(one.an_i16).idx == typeof[i16]().idx
+// assert one.an_i16 == 12345
+// assert typeof(one.an_i64).idx == typeof[i64]().idx
+// assert one.an_i64 == 123456789012345
+
+// result_values_two := sql db {
+// select from TestOrmValuesTwo
+// }!
+
+// two := result_values_two[0]
+
+// assert typeof(two.an_i8).idx == typeof[i8]().idx
+// assert two.an_i8 == 12
+// assert typeof(two.an_int).idx == typeof[int]().idx
+// assert two.an_int == 123456
+// assert typeof(two.a_string).idx == typeof[string]().idx
+// assert two.a_string == 'Hello, World!'
+
+// result_values_three := sql db {
+// select from TestOrmValuesThree
+// }!
+
+// three := result_values_three[0]
+
+// assert typeof(three.an_u16).idx == typeof[u16]().idx
+// assert three.an_u16 == 54321
+// assert typeof(three.an_u32).idx == typeof[u32]().idx
+// assert three.an_u32 == 1234567890
+// assert typeof(three.an_u64).idx == typeof[u64]().idx
+// assert three.an_u64 == 1234
+// assert typeof(three.an_u8).idx == typeof[u8]().idx
+// assert three.an_u8 == 255
+
+// result_values_four := sql db {
+// select from TestOrmValuesFour
+// }!
+
+// four := result_values_four[0]
+
+// assert typeof(four.a_time).idx == typeof[time.Time]().idx
+// assert typeof(four.a_bool).idx == typeof[bool]().idx
+// assert four.a_bool == true
+// assert typeof(four.int_or_null).idx == typeof[?int]().idx
+// unwrapped_option_one := four.int_or_null or { 0 }
+// assert unwrapped_option_one == 0
+// unwrapped_option_two := result_values_four[1].int_or_null or { 0 }
+// assert unwrapped_option_two == 123
+// }
+
+// fn test_orm_create_enum_is_not_supported() {
+// mut db := open(':memory:')!
+// mut error := ''
+// sql db {
+// create table ORMTableEnum
+// } or { error = err.str() }
+// assert error == 'enum is not supported in vsql'
+// }
+
+// fn test_orm_select_where() {
+// mut db := open(':memory:')!
+// mut error := ''
+
+// sql db {
+// create table Product
+// } or { panic(err) }
+
+// prods := [
+// Product{1, 'Ice Cream', '5.99', 17},
+// Product{2, 'Ham Sandwhich', '3.47', none},
+// Product{3, 'Bagel', '1.25', 45},
+// ]
+// for product in prods {
+// sql db {
+// insert product into Product
+// } or { panic(err) }
+// }
+// mut products := sql db {
+// select from Product where id == 2
+// }!
+
+// assert products == [Product{2, 'Ham Sandwhich', '3.47', none}]
+
+// products = sql db {
+// select from Product where id == 5
+// }!
+
+// assert products == []
+
+// products = sql db {
+// select from Product where id != 3
+// }!
+
+// assert products == [Product{1, 'Ice Cream', '5.99', 17},
+// Product{2, 'Ham Sandwhich', '3.47', none}]
+
+// products = sql db {
+// select from Product where price > '3.47'
+// }!
+
+// assert products == [Product{1, 'Ice Cream', '5.99', 17}]
+
+// products = sql db {
+// select from Product where price >= '3'
+// }!
- assert products == [Product{1, 'Ice Cream', '5.99', 17},
- Product{2, 'Ham Sandwhich', '3.47', none}]
+// assert products == [Product{1, 'Ice Cream', '5.99', 17},
+// Product{2, 'Ham Sandwhich', '3.47', none}]
- products = sql db {
- select from Product where price > '3.47'
- }!
+// products = sql db {
+// select from Product where price < '3.47'
+// }!
+
+// assert products == [Product{3, 'Bagel', '1.25', 45}]
+
+// products = sql db {
+// select from Product where price <= '5'
+// }!
- assert products == [Product{1, 'Ice Cream', '5.99', 17}]
+// assert products == [Product{2, 'Ham Sandwhich', '3.47', none},
+// Product{3, 'Bagel', '1.25', 45}]
- products = sql db {
- select from Product where price >= '3'
- }!
+// // TODO (daniel-le97): The ORM does not support a "not like" constraint right now.
- assert products == [Product{1, 'Ice Cream', '5.99', 17},
- Product{2, 'Ham Sandwhich', '3.47', none}]
+// products = sql db {
+// select from Product where product_name like 'Ham%'
+// }!
- products = sql db {
- select from Product where price < '3.47'
- }!
+// assert products == [Product{2, 'Ham Sandwhich', '3.47', none}]
- assert products == [Product{3, 'Bagel', '1.25', 45}]
+// products = sql db {
+// select from Product where quantity is none
+// }!
- products = sql db {
- select from Product where price <= '5'
- }!
+// assert products == [Product{2, 'Ham Sandwhich', '3.47', none}]
- assert products == [Product{2, 'Ham Sandwhich', '3.47', none},
- Product{3, 'Bagel', '1.25', 45}]
+// products = sql db {
+// select from Product where quantity !is none
+// }!
- // TODO (daniel-le97): The ORM does not support a "not like" constraint right now.
+// assert products == [Product{1, 'Ice Cream', '5.99', 17}, Product{3, 'Bagel', '1.25', 45}]
- products = sql db {
- select from Product where product_name like 'Ham%'
- }!
+// products = sql db {
+// select from Product where price > '3' && price < '3.50'
+// }!
- assert products == [Product{2, 'Ham Sandwhich', '3.47', none}]
+// assert products == [Product{2, 'Ham Sandwhich', '3.47', none}]
- products = sql db {
- select from Product where quantity is none
- }!
+// products = sql db {
+// select from Product where price < '2.000' || price >= '5'
+// }!
- // println(products)
-
- assert products == [Product{2, 'Ham Sandwhich', '3.47', none}]
-
- products = sql db {
- select from Product where quantity !is none
- }!
-
- // println(products)
- assert products == [Product{1, 'Ice Cream', '5.99', 17}, Product{3, 'Bagel', '1.25', 45}]
-
- products = sql db {
- select from Product where price > '3' && price < '3.50'
- }!
- // println(products)
-
- assert products == [Product{2, 'Ham Sandwhich', '3.47', none}]
-
- products = sql db {
- select from Product where price < '2.000' || price >= '5'
- }!
-
- assert products == [Product{1, 'Ice Cream', '5.99', 17}, Product{3, 'Bagel', '1.25', 45}]
-}
+// assert products == [Product{1, 'Ice Cream', '5.99', 17}, Product{3, 'Bagel', '1.25', 45}]
+// }
From e988688d1aaeebd49095dae65953d2bd471f8682 Mon Sep 17 00:00:00 2001
From: daniel-le97 <107774403+daniel-le97@users.noreply.github.com>
Date: Sat, 16 Nov 2024 00:24:56 -0700
Subject: [PATCH 05/13] fix: uncomment orm_test.v
---
vsql/orm_test.v | 446 ++++++++++++++++++++++++------------------------
1 file changed, 223 insertions(+), 223 deletions(-)
diff --git a/vsql/orm_test.v b/vsql/orm_test.v
index def53d3..aa9e4c9 100644
--- a/vsql/orm_test.v
+++ b/vsql/orm_test.v
@@ -165,240 +165,240 @@ fn test_custom_table_name_and_serial_crud() {
assert error == ''
}
-// fn test_reserved_words() {
-// mut db := open(':memory:')!
-// mut error := ''
-// sql db {
-// create table TestReservedWordField
-// } or { error = err.str() }
-// assert error == 'reserved word where cannot be used as a field name at TestReservedWordField.where'
-// sql db {
-// create table TestReservedWordSqlAttribute
-// } or { error = err.str() }
-// assert error == 'ORDER is a reserved word in vsql'
-// }
-
-// fn test_unsupported_attributes() {
-// mut db := open(':memory:')!
-// mut error := ''
-// sql db {
-// create table TestDefaultAttribute
-// } or { error = err.str() }
-// assert error == 'default is not supported in vsql'
-// sql db {
-// create table TestUniqueAttribute
-// } or { error = err.str() }
-// assert error == 'unique is not supported in vsql'
-// }
-
-// fn test_default_orm_values() {
-// mut db := open(':memory:')!
-// mut error := ''
-// sql db {
-// create table TestOrmValuesOne
-// create table TestOrmValuesTwo
-// create table TestOrmValuesThree
-// create table TestOrmValuesFour
-// } or { error = err.str() }
-// assert error == ''
-
-// values_one := TestOrmValuesOne{
-// an_f32: 3.14
-// an_f64: 2.718281828459
-// an_i16: 12345
-// an_i64: 123456789012345
-// }
-// values_two := TestOrmValuesTwo{
-// an_i8: 12
-// an_int: 123456
-// a_string: 'Hello, World!'
-// }
-// values_three := TestOrmValuesThree{
-// an_u16: 54321
-// an_u32: 1234567890
-// an_u64: 1234
-// an_u8: 255
-// }
-
-// values_four := TestOrmValuesFour{
-// a_time: time.now()
-// a_bool: true
-// // int_or_null: 123
-// }
-// values_four_b := TestOrmValuesFour{
-// a_time: time.now()
-// a_bool: false
-// int_or_null: 123
-// }
-
-// sql db {
-// insert values_one into TestOrmValuesOne
-// insert values_two into TestOrmValuesTwo
-// insert values_three into TestOrmValuesThree
-// insert values_four into TestOrmValuesFour
-// insert values_four_b into TestOrmValuesFour
-// } or { error = err.str() }
-// assert error == ''
-
-// result_values_one := sql db {
-// select from TestOrmValuesOne
-// }!
-// one := result_values_one[0]
-
-// assert typeof(one.an_f32).idx == typeof[f32]().idx
-// assert one.an_f32 == 3.14
-// assert typeof(one.an_f64).idx == typeof[f64]().idx
-// assert one.an_f64 == 2.718281828459
-// assert typeof(one.an_i16).idx == typeof[i16]().idx
-// assert one.an_i16 == 12345
-// assert typeof(one.an_i64).idx == typeof[i64]().idx
-// assert one.an_i64 == 123456789012345
-
-// result_values_two := sql db {
-// select from TestOrmValuesTwo
-// }!
-
-// two := result_values_two[0]
-
-// assert typeof(two.an_i8).idx == typeof[i8]().idx
-// assert two.an_i8 == 12
-// assert typeof(two.an_int).idx == typeof[int]().idx
-// assert two.an_int == 123456
-// assert typeof(two.a_string).idx == typeof[string]().idx
-// assert two.a_string == 'Hello, World!'
-
-// result_values_three := sql db {
-// select from TestOrmValuesThree
-// }!
-
-// three := result_values_three[0]
-
-// assert typeof(three.an_u16).idx == typeof[u16]().idx
-// assert three.an_u16 == 54321
-// assert typeof(three.an_u32).idx == typeof[u32]().idx
-// assert three.an_u32 == 1234567890
-// assert typeof(three.an_u64).idx == typeof[u64]().idx
-// assert three.an_u64 == 1234
-// assert typeof(three.an_u8).idx == typeof[u8]().idx
-// assert three.an_u8 == 255
-
-// result_values_four := sql db {
-// select from TestOrmValuesFour
-// }!
-
-// four := result_values_four[0]
-
-// assert typeof(four.a_time).idx == typeof[time.Time]().idx
-// assert typeof(four.a_bool).idx == typeof[bool]().idx
-// assert four.a_bool == true
-// assert typeof(four.int_or_null).idx == typeof[?int]().idx
-// unwrapped_option_one := four.int_or_null or { 0 }
-// assert unwrapped_option_one == 0
-// unwrapped_option_two := result_values_four[1].int_or_null or { 0 }
-// assert unwrapped_option_two == 123
-// }
-
-// fn test_orm_create_enum_is_not_supported() {
-// mut db := open(':memory:')!
-// mut error := ''
-// sql db {
-// create table ORMTableEnum
-// } or { error = err.str() }
-// assert error == 'enum is not supported in vsql'
-// }
-
-// fn test_orm_select_where() {
-// mut db := open(':memory:')!
-// mut error := ''
-
-// sql db {
-// create table Product
-// } or { panic(err) }
-
-// prods := [
-// Product{1, 'Ice Cream', '5.99', 17},
-// Product{2, 'Ham Sandwhich', '3.47', none},
-// Product{3, 'Bagel', '1.25', 45},
-// ]
-// for product in prods {
-// sql db {
-// insert product into Product
-// } or { panic(err) }
-// }
-// mut products := sql db {
-// select from Product where id == 2
-// }!
-
-// assert products == [Product{2, 'Ham Sandwhich', '3.47', none}]
-
-// products = sql db {
-// select from Product where id == 5
-// }!
-
-// assert products == []
-
-// products = sql db {
-// select from Product where id != 3
-// }!
-
-// assert products == [Product{1, 'Ice Cream', '5.99', 17},
-// Product{2, 'Ham Sandwhich', '3.47', none}]
-
-// products = sql db {
-// select from Product where price > '3.47'
-// }!
-
-// assert products == [Product{1, 'Ice Cream', '5.99', 17}]
-
-// products = sql db {
-// select from Product where price >= '3'
-// }!
+fn test_reserved_words() {
+ mut db := open(':memory:')!
+ mut error := ''
+ sql db {
+ create table TestReservedWordField
+ } or { error = err.str() }
+ assert error == 'reserved word where cannot be used as a field name at TestReservedWordField.where'
+ sql db {
+ create table TestReservedWordSqlAttribute
+ } or { error = err.str() }
+ assert error == 'ORDER is a reserved word in vsql'
+}
-// assert products == [Product{1, 'Ice Cream', '5.99', 17},
-// Product{2, 'Ham Sandwhich', '3.47', none}]
+fn test_unsupported_attributes() {
+ mut db := open(':memory:')!
+ mut error := ''
+ sql db {
+ create table TestDefaultAttribute
+ } or { error = err.str() }
+ assert error == 'default is not supported in vsql'
+ sql db {
+ create table TestUniqueAttribute
+ } or { error = err.str() }
+ assert error == 'unique is not supported in vsql'
+}
-// products = sql db {
-// select from Product where price < '3.47'
-// }!
-
-// assert products == [Product{3, 'Bagel', '1.25', 45}]
-
-// products = sql db {
-// select from Product where price <= '5'
-// }!
+fn test_default_orm_values() {
+ mut db := open(':memory:')!
+ mut error := ''
+ sql db {
+ create table TestOrmValuesOne
+ create table TestOrmValuesTwo
+ create table TestOrmValuesThree
+ create table TestOrmValuesFour
+ } or { error = err.str() }
+ assert error == ''
-// assert products == [Product{2, 'Ham Sandwhich', '3.47', none},
-// Product{3, 'Bagel', '1.25', 45}]
+ values_one := TestOrmValuesOne{
+ an_f32: 3.14
+ an_f64: 2.718281828459
+ an_i16: 12345
+ an_i64: 123456789012345
+ }
+ values_two := TestOrmValuesTwo{
+ an_i8: 12
+ an_int: 123456
+ a_string: 'Hello, World!'
+ }
+ values_three := TestOrmValuesThree{
+ an_u16: 54321
+ an_u32: 1234567890
+ an_u64: 1234
+ an_u8: 255
+ }
+
+ values_four := TestOrmValuesFour{
+ a_time: time.now()
+ a_bool: true
+ // int_or_null: 123
+ }
+ values_four_b := TestOrmValuesFour{
+ a_time: time.now()
+ a_bool: false
+ int_or_null: 123
+ }
+
+ sql db {
+ insert values_one into TestOrmValuesOne
+ insert values_two into TestOrmValuesTwo
+ insert values_three into TestOrmValuesThree
+ insert values_four into TestOrmValuesFour
+ insert values_four_b into TestOrmValuesFour
+ } or { error = err.str() }
+ assert error == ''
-// // TODO (daniel-le97): The ORM does not support a "not like" constraint right now.
+ result_values_one := sql db {
+ select from TestOrmValuesOne
+ }!
+ one := result_values_one[0]
+
+ assert typeof(one.an_f32).idx == typeof[f32]().idx
+ assert one.an_f32 == 3.14
+ assert typeof(one.an_f64).idx == typeof[f64]().idx
+ assert one.an_f64 == 2.718281828459
+ assert typeof(one.an_i16).idx == typeof[i16]().idx
+ assert one.an_i16 == 12345
+ assert typeof(one.an_i64).idx == typeof[i64]().idx
+ assert one.an_i64 == 123456789012345
+
+ result_values_two := sql db {
+ select from TestOrmValuesTwo
+ }!
-// products = sql db {
-// select from Product where product_name like 'Ham%'
-// }!
+ two := result_values_two[0]
-// assert products == [Product{2, 'Ham Sandwhich', '3.47', none}]
+ assert typeof(two.an_i8).idx == typeof[i8]().idx
+ assert two.an_i8 == 12
+ assert typeof(two.an_int).idx == typeof[int]().idx
+ assert two.an_int == 123456
+ assert typeof(two.a_string).idx == typeof[string]().idx
+ assert two.a_string == 'Hello, World!'
-// products = sql db {
-// select from Product where quantity is none
-// }!
+ result_values_three := sql db {
+ select from TestOrmValuesThree
+ }!
-// assert products == [Product{2, 'Ham Sandwhich', '3.47', none}]
+ three := result_values_three[0]
-// products = sql db {
-// select from Product where quantity !is none
-// }!
+ assert typeof(three.an_u16).idx == typeof[u16]().idx
+ assert three.an_u16 == 54321
+ assert typeof(three.an_u32).idx == typeof[u32]().idx
+ assert three.an_u32 == 1234567890
+ assert typeof(three.an_u64).idx == typeof[u64]().idx
+ assert three.an_u64 == 1234
+ assert typeof(three.an_u8).idx == typeof[u8]().idx
+ assert three.an_u8 == 255
-// assert products == [Product{1, 'Ice Cream', '5.99', 17}, Product{3, 'Bagel', '1.25', 45}]
+ result_values_four := sql db {
+ select from TestOrmValuesFour
+ }!
-// products = sql db {
-// select from Product where price > '3' && price < '3.50'
-// }!
+ four := result_values_four[0]
-// assert products == [Product{2, 'Ham Sandwhich', '3.47', none}]
+ assert typeof(four.a_time).idx == typeof[time.Time]().idx
+ assert typeof(four.a_bool).idx == typeof[bool]().idx
+ assert four.a_bool == true
+ assert typeof(four.int_or_null).idx == typeof[?int]().idx
+ unwrapped_option_one := four.int_or_null or { 0 }
+ assert unwrapped_option_one == 0
+ unwrapped_option_two := result_values_four[1].int_or_null or { 0 }
+ assert unwrapped_option_two == 123
+}
-// products = sql db {
-// select from Product where price < '2.000' || price >= '5'
-// }!
+fn test_orm_create_enum_is_not_supported() {
+ mut db := open(':memory:')!
+ mut error := ''
+ sql db {
+ create table ORMTableEnum
+ } or { error = err.str() }
+ assert error == 'enum is not supported in vsql'
+}
-// assert products == [Product{1, 'Ice Cream', '5.99', 17}, Product{3, 'Bagel', '1.25', 45}]
-// }
+fn test_orm_select_where() {
+ mut db := open(':memory:')!
+ mut error := ''
+
+ sql db {
+ create table Product
+ } or { panic(err) }
+
+ prods := [
+ Product{1, 'Ice Cream', '5.99', 17},
+ Product{2, 'Ham Sandwhich', '3.47', none},
+ Product{3, 'Bagel', '1.25', 45},
+ ]
+ for product in prods {
+ sql db {
+ insert product into Product
+ } or { panic(err) }
+ }
+ mut products := sql db {
+ select from Product where id == 2
+ }!
+
+ assert products == [Product{2, 'Ham Sandwhich', '3.47', none}]
+
+ products = sql db {
+ select from Product where id == 5
+ }!
+
+ assert products == []
+
+ products = sql db {
+ select from Product where id != 3
+ }!
+
+ assert products == [Product{1, 'Ice Cream', '5.99', 17},
+ Product{2, 'Ham Sandwhich', '3.47', none}]
+
+ products = sql db {
+ select from Product where price > '3.47'
+ }!
+
+ assert products == [Product{1, 'Ice Cream', '5.99', 17}]
+
+ products = sql db {
+ select from Product where price >= '3'
+ }!
+
+ assert products == [Product{1, 'Ice Cream', '5.99', 17},
+ Product{2, 'Ham Sandwhich', '3.47', none}]
+
+ products = sql db {
+ select from Product where price < '3.47'
+ }!
+
+ assert products == [Product{3, 'Bagel', '1.25', 45}]
+
+ products = sql db {
+ select from Product where price <= '5'
+ }!
+
+ assert products == [Product{2, 'Ham Sandwhich', '3.47', none},
+ Product{3, 'Bagel', '1.25', 45}]
+
+ // TODO (daniel-le97): The ORM does not support a "not like" constraint right now.
+
+ products = sql db {
+ select from Product where product_name like 'Ham%'
+ }!
+
+ assert products == [Product{2, 'Ham Sandwhich', '3.47', none}]
+
+ products = sql db {
+ select from Product where quantity is none
+ }!
+
+ assert products == [Product{2, 'Ham Sandwhich', '3.47', none}]
+
+ products = sql db {
+ select from Product where quantity !is none
+ }!
+
+ assert products == [Product{1, 'Ice Cream', '5.99', 17}, Product{3, 'Bagel', '1.25', 45}]
+
+ products = sql db {
+ select from Product where price > '3' && price < '3.50'
+ }!
+
+ assert products == [Product{2, 'Ham Sandwhich', '3.47', none}]
+
+ products = sql db {
+ select from Product where price < '2.000' || price >= '5'
+ }!
+
+ assert products == [Product{1, 'Ice Cream', '5.99', 17}, Product{3, 'Bagel', '1.25', 45}]
+}
From 4a34bc6ad5cbecca38dea8f79f3c1b533440a987 Mon Sep 17 00:00:00 2001
From: Elliot Chance
Date: Mon, 18 Nov 2024 21:19:04 -0500
Subject: [PATCH 06/13] docs: Fixing CI (#201)
Fixing the "Verify docs" job and running `v fmt` for the new
version.
---
.github/workflows/ci.yml | 1 +
docs/requirements.txt | 4 ++--
tests/double-precision.sql | 10 +++++-----
tests/real.sql | 4 ++--
4 files changed, 10 insertions(+), 9 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 3740556..a6e5c13 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -32,6 +32,7 @@ jobs:
- name: Verify docs
run: |
+ export PIP_BREAK_SYSTEM_PACKAGES=1
pip install sphinx sphinx_rtd_theme
cd docs && python3 -m pip install -r requirements.txt && cd -
make clean-docs docs
diff --git a/docs/requirements.txt b/docs/requirements.txt
index 483eb39..b471fc1 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -1,3 +1,3 @@
-sphinx==5.0.0
-sphinx_rtd_theme==1.0.0
+sphinx==8.0.0
+sphinx_rtd_theme==3.0.2
readthedocs-sphinx-search==0.1.1
diff --git a/tests/double-precision.sql b/tests/double-precision.sql
index 25f1820..84530e9 100644
--- a/tests/double-precision.sql
+++ b/tests/double-precision.sql
@@ -77,11 +77,11 @@ VALUES 53.7e0 - CAST(1.23 AS DOUBLE PRECISION);
/* types */
VALUES CAST(-2000000000.1 AS DOUBLE PRECISION) - 500000000.7e0;
--- COL1: -0.25000000008e+10 (DOUBLE PRECISION)
+-- COL1: -2.5000000008e+09 (DOUBLE PRECISION)
/* types */
VALUES -500000000.7e0 - CAST(2000000000.1 AS DOUBLE PRECISION);
--- COL1: -0.25000000008e+10 (DOUBLE PRECISION)
+-- COL1: -2.5000000008e+09 (DOUBLE PRECISION)
/* types */
VALUES CAST(12.3 AS DOUBLE PRECISION) * 53.7e0;
@@ -93,11 +93,11 @@ VALUES -53.7e0 * CAST(12.3 AS DOUBLE PRECISION);
/* types */
VALUES CAST(-300000.1 AS DOUBLE PRECISION) * 200000.7e0;
--- COL1: -6.000023000007e+10 (DOUBLE PRECISION)
+-- COL1: -6.00002300001e+10 (DOUBLE PRECISION)
/* types */
VALUES -300000.7e0 * CAST(200000.1 AS DOUBLE PRECISION);
--- COL1: -6.000017000007e+10 (DOUBLE PRECISION)
+-- COL1: -6.00001700001e+10 (DOUBLE PRECISION)
/* types */
VALUES CAST(1.23 AS DOUBLE PRECISION) / 53.7e0;
@@ -128,5 +128,5 @@ SELECT * FROM foo;
SELECT CAST(x AS BIGINT) FROM foo;
-- msg: CREATE TABLE 1
-- msg: INSERT 1
--- X: 0.1234567891235e+27
+-- X: 1.23456789123e+26
-- error 22003: numeric value out of range
diff --git a/tests/real.sql b/tests/real.sql
index b3cbfaa..066c52d 100644
--- a/tests/real.sql
+++ b/tests/real.sql
@@ -93,11 +93,11 @@ VALUES CAST(-53.7 AS REAL) * CAST(12.3 AS REAL);
/* types */
VALUES CAST(-300000.1 AS REAL) * CAST(200000.7 AS REAL);
--- COL1: -6.000023e+10 (REAL)
+-- COL1: -6.00002e+10 (REAL)
/* types */
VALUES CAST(-300000.7 AS REAL) * CAST(200000.1 AS REAL);
--- COL1: -0.6000017e+11 (REAL)
+-- COL1: -6.00002e+10 (REAL)
/* types */
VALUES CAST(1.23 AS REAL) / CAST(53.7 AS REAL);
From 62fe6926263d6288064c8e1d1b62cd8669c1bda6 Mon Sep 17 00:00:00 2001
From: Elliot Chance
Date: Fri, 27 Dec 2024 12:37:40 -0500
Subject: [PATCH 07/13] parser: Complete rewrite to LALR (#202)
This replaces the existing Earley parser (which is O(n^3)) with a LALR parser
using Yacc which is an ideal O(n). Even with very short SQL statements, the
existing parser was _really_ slow, so I had to build a query cache as bandaid,
but that has also been removed now.
This refactoring was made possible by adapting yacc from a Go implementation
here: https://github.com/elliotchance/vyac. However, in keeping with the promise
of this repo being completely written in V, the source has been copied to this
repo.
Other notable and breaking changes:
1. Not sure how this worked before, but the query may not specify a catalog in
identity chains (for example, `catalog.schema.table`). The catalog must be set
using `SET CATALOG`.
2. Syntax error messages will be slightly different, but should be a little more
helpful.
3. There are some ambiguities with the SQL grammar, such as trying to decode
what `x IS NOT TRUE` means or differentiating between `COUNT(expr)` vs
`COUNT(*)` due to lookahead limitations. Some special tokens for combinations of
operators and keywords have had to be added for known edge cases, but there are
many remaining conflicts. I suspect these conflicts don't matter as ambiguous
paths should still yield valid results, so these warnings have to be ignored for
now.
4. Fixes a very minor bug where string literals in VALUES might be treated as
`VARCHAR` instead of `CHARACTER` in some cases.
5. Renamed "std_" files with their position number in the standard. This helps
for grouping similar sections and makes lookups easier.
---
.gitignore | 3 +
Makefile | 23 +-
cmd/tests/catalogs.sh | 4 +-
cmd/vsql/cli.v | 55 +-
docs/contributing.rst | 24 +-
docs/v-client-library-docs.rst | 38 +-
generate-grammar.py | 531 ---
scripts/generate_grammar.py | 26 +
scripts/vyacc.v | 3725 +++++++++++++++++
tests/catalogs.sql | 40 +-
tests/errors.sql | 4 +-
tests/fold.sql | 4 +-
tests/math.sql | 4 +-
tests/reserved-words.sql | 738 ++--
tests/syntax_tokens.sql | 14 +
tests/values.sql | 24 +-
tests/whitespace.sql | 2 +-
vsql/connection.v | 48 +-
vsql/earley.v | 364 --
vsql/grammar.y | 695 +++
vsql/lexer.v | 332 +-
vsql/planner.v | 14 +-
vsql/query_cache.v | 171 -
vsql/server.v | 5 +-
vsql/sql_test.v | 20 +-
vsql/std_10_10_sort_specification_list.v | 18 +
vsql/std_10_10_sort_specification_list.y | 22 +
...cation.v => std_10_4_routine_invocation.v} | 42 -
vsql/std_10_4_routine_invocation.y | 23 +
...nction.v => std_10_9_aggregate_function.v} | 30 -
vsql/std_10_9_aggregate_function.y | 26 +
...inition.v => std_11_1_schema_definition.v} | 14 -
vsql/std_11_1_schema_definition.y | 11 +
...ent.v => std_11_2_drop_schema_statement.v} | 15 -
vsql/std_11_2_drop_schema_statement.y | 12 +
...ent.v => std_11_31_drop_table_statement.v} | 11 -
vsql/std_11_31_drop_table_statement.y | 6 +
...finition.v => std_11_3_table_definition.v} | 43 -
vsql/std_11_3_table_definition.y | 24 +
vsql/std_11_4_column_definition.y | 24 +
vsql/std_11_6_table_constraint_definition.y | 15 +
.../std_11_72_sequence_generator_definition.v | 133 +
.../std_11_72_sequence_generator_definition.y | 98 +
...1_73_alter_sequence_generator_statement.v} | 55 -
...11_73_alter_sequence_generator_statement.y | 35 +
...11_74_drop_sequence_generator_statement.v} | 12 -
..._11_74_drop_sequence_generator_statement.y | 8 +
vsql/std_11_7_unique_constraint_definition.v | 9 +
vsql/std_11_7_unique_constraint_definition.y | 14 +
vsql/std_13_4_sql_procedure_statement.y | 31 +
...atement.v => std_14_11_insert_statement.v} | 33 -
vsql/std_14_11_insert_statement.y | 27 +
... => std_14_14_update_statement_searched.v} | 19 -
vsql/std_14_14_update_statement_searched.y | 13 +
vsql/std_14_15_set_clause_list.v | 23 +
vsql/std_14_15_set_clause_list.y | 32 +
vsql/std_14_3_cursor_specification.y | 10 +
vsql/std_14_8_delete_statement_positioned.y | 10 +
...v => std_14_9_delete_statement_searched.v} | 17 -
vsql/std_14_9_delete_statement_searched.y | 14 +
...=> std_17_1_start_transaction_statement.v} | 11 -
vsql/std_17_1_start_transaction_statement.y | 6 +
...tatement.v => std_17_7_commit_statement.v} | 12 -
vsql/std_17_7_commit_statement.y | 7 +
...tement.v => std_17_8_rollback_statement.v} | 12 -
vsql/std_17_8_rollback_statement.y | 7 +
...ent.v => std_19_5_set_catalog_statement.v} | 18 -
vsql/std_19_5_set_catalog_statement.y | 11 +
...ment.v => std_19_6_set_schema_statement.v} | 18 -
vsql/std_19_6_set_schema_statement.y | 11 +
vsql/std_20_7_prepare_statement.y | 31 +
vsql/{std_sqlstate.v => std_24_1_sqlstate.v} | 0
...d_sql_schemas.v => std_4_26_sql_schemas.v} | 0
...ators.v => std_4_27_sequence_generators.v} | 0
vsql/std_5_1_sql_terminal_character.y | 31 +
vsql/std_5_2_token_and_separator.y | 304 ++
vsql/std_5_3_literal.v | 38 +
vsql/std_5_3_literal.y | 96 +
...iers.v => std_5_4_names_and_identifiers.v} | 82 -
vsql/std_5_4_names_and_identifiers.y | 71 +
...xpression.v => std_6_12_case_expression.v} | 37 -
vsql/std_6_12_case_expression.y | 25 +
...cation.v => std_6_13_cast_specification.v} | 18 -
vsql/std_6_13_cast_specification.y | 17 +
...ion.v => std_6_14_next_value_expression.v} | 13 -
vsql/std_6_14_next_value_expression.y | 10 +
vsql/std_6_1_data_type.v | 21 +
vsql/std_6_1_data_type.y | 121 +
...pression.v => std_6_28_value_expression.v} | 13 -
vsql/std_6_28_value_expression.y | 22 +
....v => std_6_29_numeric_value_expression.v} | 38 -
vsql/std_6_29_numeric_value_expression.y | 37 +
vsql/std_6_30_numeric_value_function.v | 172 +
vsql/std_6_30_numeric_value_function.y | 160 +
...n.v => std_6_31_string_value_expression.v} | 27 -
vsql/std_6_31_string_value_expression.y | 29 +
vsql/std_6_32_string_value_function.v | 144 +
vsql/std_6_32_string_value_function.y | 87 +
...v => std_6_35_datetime_value_expression.v} | 17 -
vsql/std_6_35_datetime_value_expression.y | 14 +
...n.v => std_6_36_datetime_value_function.v} | 68 -
vsql/std_6_36_datetime_value_function.y | 47 +
....v => std_6_39_boolean_value_expression.v} | 97 +-
vsql/std_6_39_boolean_value_expression.y | 90 +
...y.v => std_6_3_value_expression_primary.v} | 24 -
vsql/std_6_3_value_expression_primary.y | 39 +
..._specification_and_target_specification.v} | 32 -
...e_specification_and_target_specification.y | 29 +
..._contextually_typed_value_specification.v} | 17 -
...5_contextually_typed_value_specification.y | 12 +
vsql/std_6_6_identifier_chain.v | 15 +
vsql/std_6_6_identifier_chain.y | 14 +
vsql/std_6_7_column_reference.y | 12 +
...v => std_6_9_set_function_specification.y} | 14 +-
...joined_table.v => std_7_10_joined_table.v} | 26 -
vsql/std_7_10_joined_table.y | 21 +
vsql/std_7_12_where_clause.y | 11 +
...by_clause.v => std_7_13_group_by_clause.v} | 73 +-
vsql/std_7_13_group_by_clause.y | 21 +
vsql/std_7_16_query_specification.v | 29 +
vsql/std_7_16_query_specification.y | 50 +
...pression.v => std_7_17_query_expression.v} | 134 -
vsql/std_7_17_query_expression.y | 89 +
vsql/std_7_19_subquery.v | 7 +
vsql/std_7_19_subquery.y | 16 +
vsql/std_7_1_row_value_constructor.v | 158 +
vsql/std_7_1_row_value_constructor.y | 75 +
vsql/std_7_2_row_value_expression.y | 20 +
...or.v => std_7_3_table_value_constructor.v} | 50 -
vsql/std_7_3_table_value_constructor.y | 30 +
vsql/std_7_4_table_expression.v | 11 +
vsql/std_7_4_table_expression.y | 25 +
vsql/std_7_5_from_clause.y | 13 +
vsql/std_7_6_table_reference.v | 43 +
vsql/std_7_6_table_reference.y | 84 +
vsql/{std_predicate.v => std_8_1_predicate.v} | 11 -
vsql/std_8_1_predicate.y | 10 +
...ondition.v => std_8_21_search_condition.v} | 7 -
vsql/std_8_21_search_condition.y | 6 +
...icate.v => std_8_2_comparison_predicate.v} | 27 -
vsql/std_8_2_comparison_predicate.y | 29 +
...redicate.v => std_8_3_between_predicate.v} | 47 -
vsql/std_8_3_between_predicate.y | 42 +
...e_predicate.v => std_8_5_like_predicate.v} | 29 -
vsql/std_8_5_like_predicate.y | 27 +
...redicate.v => std_8_6_similar_predicate.v} | 26 -
vsql/std_8_6_similar_predicate.y | 20 +
...l_predicate.v => std_8_8_null_predicate.v} | 15 -
vsql/std_8_8_null_predicate.y | 12 +
...ssignment.v => std_9_2_store_assignment.v} | 2 -
vsql/std_column_definition.v | 31 -
vsql/std_column_reference.v | 16 -
vsql/std_cursor_specification.v | 12 -
vsql/std_data_type.v | 218 -
vsql/std_delete_statement_positioned.v | 12 -
vsql/std_from_clause.v | 19 -
vsql/std_identifier_chain.v | 30 -
vsql/std_literal.v | 176 -
vsql/std_numeric_value_function.v | 338 --
vsql/std_prepare_statement.v | 33 -
vsql/std_query_specification.v | 102 -
vsql/std_row_value_constructor.v | 245 --
vsql/std_row_value_expression.v | 18 -
vsql/std_sequence_generator_definition.v | 256 --
vsql/std_set_clause_list.v | 64 -
vsql/std_sort_specification_list.v | 56 -
vsql/std_sql_procedure_statement.v | 33 -
vsql/std_sql_terminal_character.v | 38 -
vsql/std_string_value_function.v | 246 --
vsql/std_subquery.v | 26 -
vsql/std_table_constraint_definition.v | 15 -
vsql/std_table_expression.v | 37 -
vsql/std_table_reference.v | 138 -
vsql/std_token_and_separator.v | 323 --
vsql/std_unique_constraint_definition.v | 31 -
vsql/std_where_clause.v | 17 -
vsql/utils.v | 25 +
177 files changed, 8519 insertions(+), 5467 deletions(-)
delete mode 100644 generate-grammar.py
create mode 100644 scripts/generate_grammar.py
create mode 100644 scripts/vyacc.v
create mode 100644 tests/syntax_tokens.sql
delete mode 100644 vsql/earley.v
create mode 100644 vsql/grammar.y
delete mode 100644 vsql/query_cache.v
create mode 100644 vsql/std_10_10_sort_specification_list.v
create mode 100644 vsql/std_10_10_sort_specification_list.y
rename vsql/{std_routine_invocation.v => std_10_4_routine_invocation.v} (57%)
create mode 100644 vsql/std_10_4_routine_invocation.y
rename vsql/{std_aggregate_function.v => std_10_9_aggregate_function.v} (78%)
create mode 100644 vsql/std_10_9_aggregate_function.y
rename vsql/{std_schema_definition.v => std_11_1_schema_definition.v} (73%)
create mode 100644 vsql/std_11_1_schema_definition.y
rename vsql/{std_drop_schema_statement.v => std_11_2_drop_schema_statement.v} (79%)
create mode 100644 vsql/std_11_2_drop_schema_statement.y
rename vsql/{std_drop_table_statement.v => std_11_31_drop_table_statement.v} (79%)
create mode 100644 vsql/std_11_31_drop_table_statement.y
rename vsql/{std_table_definition.v => std_11_3_table_definition.v} (68%)
create mode 100644 vsql/std_11_3_table_definition.y
create mode 100644 vsql/std_11_4_column_definition.y
create mode 100644 vsql/std_11_6_table_constraint_definition.y
create mode 100644 vsql/std_11_72_sequence_generator_definition.v
create mode 100644 vsql/std_11_72_sequence_generator_definition.y
rename vsql/{std_alter_sequence_generator_statement.v => std_11_73_alter_sequence_generator_statement.v} (55%)
create mode 100644 vsql/std_11_73_alter_sequence_generator_statement.y
rename vsql/{std_drop_sequence_generator_statement.v => std_11_74_drop_sequence_generator_statement.v} (73%)
create mode 100644 vsql/std_11_74_drop_sequence_generator_statement.y
create mode 100644 vsql/std_11_7_unique_constraint_definition.v
create mode 100644 vsql/std_11_7_unique_constraint_definition.y
create mode 100644 vsql/std_13_4_sql_procedure_statement.y
rename vsql/{std_insert_statement.v => std_14_11_insert_statement.v} (72%)
create mode 100644 vsql/std_14_11_insert_statement.y
rename vsql/{std_update_statement_searched.v => std_14_14_update_statement_searched.v} (87%)
create mode 100644 vsql/std_14_14_update_statement_searched.y
create mode 100644 vsql/std_14_15_set_clause_list.v
create mode 100644 vsql/std_14_15_set_clause_list.y
create mode 100644 vsql/std_14_3_cursor_specification.y
create mode 100644 vsql/std_14_8_delete_statement_positioned.y
rename vsql/{std_delete_statement_searched.v => std_14_9_delete_statement_searched.v} (68%)
create mode 100644 vsql/std_14_9_delete_statement_searched.y
rename vsql/{std_start_transaction_statement.v => std_17_1_start_transaction_statement.v} (87%)
create mode 100644 vsql/std_17_1_start_transaction_statement.y
rename vsql/{std_commit_statement.v => std_17_7_commit_statement.v} (88%)
create mode 100644 vsql/std_17_7_commit_statement.y
rename vsql/{std_rollback_statement.v => std_17_8_rollback_statement.v} (88%)
create mode 100644 vsql/std_17_8_rollback_statement.y
rename vsql/{std_set_catalog_statement.v => std_19_5_set_catalog_statement.v} (72%)
create mode 100644 vsql/std_19_5_set_catalog_statement.y
rename vsql/{std_set_schema_statement.v => std_19_6_set_schema_statement.v} (73%)
create mode 100644 vsql/std_19_6_set_schema_statement.y
create mode 100644 vsql/std_20_7_prepare_statement.y
rename vsql/{std_sqlstate.v => std_24_1_sqlstate.v} (100%)
rename vsql/{std_sql_schemas.v => std_4_26_sql_schemas.v} (100%)
rename vsql/{std_sequence_generators.v => std_4_27_sequence_generators.v} (100%)
create mode 100644 vsql/std_5_1_sql_terminal_character.y
create mode 100644 vsql/std_5_2_token_and_separator.y
create mode 100644 vsql/std_5_3_literal.v
create mode 100644 vsql/std_5_3_literal.y
rename vsql/{std_names_and_identifiers.v => std_5_4_names_and_identifiers.v} (81%)
create mode 100644 vsql/std_5_4_names_and_identifiers.y
rename vsql/{std_case_expression.v => std_6_12_case_expression.v} (66%)
create mode 100644 vsql/std_6_12_case_expression.y
rename vsql/{std_cast_specification.v => std_6_13_cast_specification.v} (67%)
create mode 100644 vsql/std_6_13_cast_specification.y
rename vsql/{std_next_value_expression.v => std_6_14_next_value_expression.v} (76%)
create mode 100644 vsql/std_6_14_next_value_expression.y
create mode 100644 vsql/std_6_1_data_type.v
create mode 100644 vsql/std_6_1_data_type.y
rename vsql/{std_value_expression.v => std_6_28_value_expression.v} (67%)
create mode 100644 vsql/std_6_28_value_expression.y
rename vsql/{std_numeric_value_expression.v => std_6_29_numeric_value_expression.v} (81%)
create mode 100644 vsql/std_6_29_numeric_value_expression.y
create mode 100644 vsql/std_6_30_numeric_value_function.v
create mode 100644 vsql/std_6_30_numeric_value_function.y
rename vsql/{std_string_value_expression.v => std_6_31_string_value_expression.v} (69%)
create mode 100644 vsql/std_6_31_string_value_expression.y
create mode 100644 vsql/std_6_32_string_value_function.v
create mode 100644 vsql/std_6_32_string_value_function.y
rename vsql/{std_datetime_value_expression.v => std_6_35_datetime_value_expression.v} (53%)
create mode 100644 vsql/std_6_35_datetime_value_expression.y
rename vsql/{std_datetime_value_function.v => std_6_36_datetime_value_function.v} (63%)
create mode 100644 vsql/std_6_36_datetime_value_function.y
rename vsql/{std_boolean_value_expression.v => std_6_39_boolean_value_expression.v} (63%)
create mode 100644 vsql/std_6_39_boolean_value_expression.y
rename vsql/{std_value_expression_primary.v => std_6_3_value_expression_primary.v} (56%)
create mode 100644 vsql/std_6_3_value_expression_primary.y
rename vsql/{std_value_specification_and_target_specification.v => std_6_4_value_specification_and_target_specification.v} (61%)
create mode 100644 vsql/std_6_4_value_specification_and_target_specification.y
rename vsql/{std_contextually_typed_value_specification.v => std_6_5_contextually_typed_value_specification.v} (72%)
create mode 100644 vsql/std_6_5_contextually_typed_value_specification.y
create mode 100644 vsql/std_6_6_identifier_chain.v
create mode 100644 vsql/std_6_6_identifier_chain.y
create mode 100644 vsql/std_6_7_column_reference.y
rename vsql/{std_set_function_specification.v => std_6_9_set_function_specification.y} (50%)
rename vsql/{std_joined_table.v => std_7_10_joined_table.v} (85%)
create mode 100644 vsql/std_7_10_joined_table.y
create mode 100644 vsql/std_7_12_where_clause.y
rename vsql/{std_group_by_clause.v => std_7_13_group_by_clause.v} (67%)
create mode 100644 vsql/std_7_13_group_by_clause.y
create mode 100644 vsql/std_7_16_query_specification.v
create mode 100644 vsql/std_7_16_query_specification.y
rename vsql/{std_query_expression.v => std_7_17_query_expression.v} (69%)
create mode 100644 vsql/std_7_17_query_expression.y
create mode 100644 vsql/std_7_19_subquery.v
create mode 100644 vsql/std_7_19_subquery.y
create mode 100644 vsql/std_7_1_row_value_constructor.v
create mode 100644 vsql/std_7_1_row_value_constructor.y
create mode 100644 vsql/std_7_2_row_value_expression.y
rename vsql/{std_table_value_constructor.v => std_7_3_table_value_constructor.v} (68%)
create mode 100644 vsql/std_7_3_table_value_constructor.y
create mode 100644 vsql/std_7_4_table_expression.v
create mode 100644 vsql/std_7_4_table_expression.y
create mode 100644 vsql/std_7_5_from_clause.y
create mode 100644 vsql/std_7_6_table_reference.v
create mode 100644 vsql/std_7_6_table_reference.y
rename vsql/{std_predicate.v => std_8_1_predicate.v} (68%)
create mode 100644 vsql/std_8_1_predicate.y
rename vsql/{std_search_condition.v => std_8_21_search_condition.v} (92%)
create mode 100644 vsql/std_8_21_search_condition.y
rename vsql/{std_comparison_predicate.v => std_8_2_comparison_predicate.v} (95%)
create mode 100644 vsql/std_8_2_comparison_predicate.y
rename vsql/{std_between_predicate.v => std_8_3_between_predicate.v} (54%)
create mode 100644 vsql/std_8_3_between_predicate.y
rename vsql/{std_like_predicate.v => std_8_5_like_predicate.v} (64%)
create mode 100644 vsql/std_8_5_like_predicate.y
rename vsql/{std_similar_predicate.v => std_8_6_similar_predicate.v} (65%)
create mode 100644 vsql/std_8_6_similar_predicate.y
rename vsql/{std_null_predicate.v => std_8_8_null_predicate.v} (68%)
create mode 100644 vsql/std_8_8_null_predicate.y
rename vsql/{std_store_assignment.v => std_9_2_store_assignment.v} (99%)
delete mode 100644 vsql/std_column_definition.v
delete mode 100644 vsql/std_column_reference.v
delete mode 100644 vsql/std_cursor_specification.v
delete mode 100644 vsql/std_data_type.v
delete mode 100644 vsql/std_delete_statement_positioned.v
delete mode 100644 vsql/std_from_clause.v
delete mode 100644 vsql/std_identifier_chain.v
delete mode 100644 vsql/std_literal.v
delete mode 100644 vsql/std_numeric_value_function.v
delete mode 100644 vsql/std_prepare_statement.v
delete mode 100644 vsql/std_query_specification.v
delete mode 100644 vsql/std_row_value_constructor.v
delete mode 100644 vsql/std_row_value_expression.v
delete mode 100644 vsql/std_sequence_generator_definition.v
delete mode 100644 vsql/std_set_clause_list.v
delete mode 100644 vsql/std_sort_specification_list.v
delete mode 100644 vsql/std_sql_procedure_statement.v
delete mode 100644 vsql/std_sql_terminal_character.v
delete mode 100644 vsql/std_string_value_function.v
delete mode 100644 vsql/std_subquery.v
delete mode 100644 vsql/std_table_constraint_definition.v
delete mode 100644 vsql/std_table_expression.v
delete mode 100644 vsql/std_table_reference.v
delete mode 100644 vsql/std_token_and_separator.v
delete mode 100644 vsql/std_unique_constraint_definition.v
delete mode 100644 vsql/std_where_clause.v
diff --git a/.gitignore b/.gitignore
index b839895..d0cf327 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,6 @@ vsql-server
grammar.bnf
vsql/grammar.v
scripts/generate-v-client-library-docs
+y.output
+vsql/y.v
+vsql/y.y
diff --git a/Makefile b/Makefile
index b499469..5ad50e1 100644
--- a/Makefile
+++ b/Makefile
@@ -23,16 +23,16 @@ ready: grammar fmt snippets
# Binaries
-bin/vsql: vsql/grammar.v
+bin/vsql: vsql/y.v
mkdir -p bin
v $(BUILD_OPTIONS) $(PROD) cmd/vsql -o bin/vsql
-bin/vsql.exe: vsql/grammar.v
+bin/vsql.exe: vsql/y.v
mkdir -p bin
v -os windows $(BUILD_OPTIONS) $(PROD) cmd/vsql
mv cmd/vsql/vsql.exe bin/vsql.exe
-oldv: vsql/grammar.v
+oldv: vsql/y.v
ifdef OLDV
@mkdir -p /tmp/oldv/
@# VJOBS and VFLAGS needs to be provided for macOS. I'm not sure if they also
@@ -54,19 +54,18 @@ docs: snippets
clean-docs:
cd docs && make clean
-# Grammar (BNF)
+# Grammar
-grammar.bnf:
- grep "//~" -r vsql | cut -d~ -f2 > grammar.bnf
+vsql/y.y:
+ python3 scripts/generate_grammar.py
-vsql/grammar.v: grammar.bnf
- python3 generate-grammar.py
- v fmt -w vsql/grammar.v
+vsql/y.v: vsql/y.y
+ v run scripts/vyacc.v -o vsql/y.v vsql/y.y
clean-grammar:
- rm -f grammar.bnf vsql/grammar.v
+ rm -f vsql/y.v vsql/y.y
-grammar: clean-grammar vsql/grammar.v
+grammar: clean-grammar vsql/y.v
# Formatting
@@ -107,7 +106,7 @@ examples:
echo $$f; v run $$f || exit 1; \
done
-examples/%: vsql/grammar.v
+examples/%: vsql/y.v
v run examples/$*.v
# Benchmarking
diff --git a/cmd/tests/catalogs.sh b/cmd/tests/catalogs.sh
index 30acd48..5f894e4 100755
--- a/cmd/tests/catalogs.sh
+++ b/cmd/tests/catalogs.sh
@@ -12,8 +12,8 @@ echo 'INSERT INTO foo (bar) VALUES (123);' | $VSQL cli $VSQL1_FILE
echo 'CREATE TABLE foo (baz INT);' | $VSQL cli $VSQL2_FILE
echo 'INSERT INTO foo (baz) VALUES (456);' | $VSQL cli $VSQL2_FILE
-echo 'SELECT * FROM "file1".public.foo;' | $VSQL cli $VSQL1_FILE $VSQL2_FILE > $TXT_FILE
-echo 'SELECT * FROM "file2".public.foo;' | $VSQL cli $VSQL1_FILE $VSQL2_FILE >> $TXT_FILE
+echo "SET CATALOG 'file1'; SELECT * FROM public.foo;" | $VSQL cli $VSQL1_FILE $VSQL2_FILE > $TXT_FILE
+echo "SET CATALOG 'file2'; SELECT * FROM public.foo;" | $VSQL cli $VSQL1_FILE $VSQL2_FILE >> $TXT_FILE
grep -R "BAR: 123" $TXT_FILE
grep -R "BAZ: 456" $TXT_FILE
diff --git a/cmd/vsql/cli.v b/cmd/vsql/cli.v
index 0ac3217..ece2fdd 100644
--- a/cmd/vsql/cli.v
+++ b/cmd/vsql/cli.v
@@ -32,39 +32,50 @@ fn cli_command(cmd cli.Command) ! {
print('vsql> ')
os.flush()
- query := os.get_line()
+ raw_query := os.get_line()
// When running on Docker, ctrl+C doesn't always get passed through. Also,
// this provides another text based way to break out of the shell.
- if query.trim_space() == 'exit' {
+ if raw_query.trim_space() == 'exit' {
break
}
- if query != '' {
- start := time.ticks()
- db.clear_warnings()
- result := db.query(query) or {
- print_error('Error', err)
- continue
- }
+ if raw_query != '' {
+ // TODO: This is a very poor way to handle multiple queries.
+ for i, query in raw_query.split(';') {
+ if query.trim_space() == '' {
+ continue
+ }
- for warning in db.warnings {
- print_error('Warning', warning)
- }
+ start := time.ticks()
+ db.clear_warnings()
+ result := db.query(query) or {
+ print_error('Error', err)
+ continue
+ }
- mut total_rows := 0
- for row in result {
- for column in result.columns {
- print('${column.name.sub_entity_name}: ${row.get_string(column.name.sub_entity_name)!} ')
+ for warning in db.warnings {
+ print_error('Warning', warning)
}
- total_rows++
- }
- if total_rows > 0 {
- println('')
- }
+ mut total_rows := 0
+ for row in result {
+ for column in result.columns {
+ print('${column.name.sub_entity_name}: ${row.get_string(column.name.sub_entity_name)!} ')
+ }
+ total_rows++
+ }
+
+ if total_rows > 0 {
+ println('')
+ }
- println('${total_rows} ${vsql.pluralize(total_rows, 'row')} (${time.ticks() - start} ms)')
+ println('${total_rows} ${vsql.pluralize(total_rows, 'row')} (${time.ticks() - start} ms)')
+
+ if i > 0 {
+ println('')
+ }
+ }
} else {
// This means there is no more input and should only occur when the
// commands are being few through a pipe like:
diff --git a/docs/contributing.rst b/docs/contributing.rst
index 047a266..2789d76 100644
--- a/docs/contributing.rst
+++ b/docs/contributing.rst
@@ -52,34 +52,16 @@ rebuild the entire docs you can use:
Parser & SQL Grammar
--------------------
-To make changes to the SQL grammar you will need to modify the ``grammar.bnf``
-file. These rules are partial or complete BNF rules from the
+To make changes to the SQL grammar you will need to modify the ``*.y`` files.
+These rules are partial or complete BNF rules from the
`2016 SQL standard `_.
-Within ``grammar.bnf`` you will see that some of the rules have a parser
-function which is a name after ``->``. The actual parser function will have
-``parse_`` prefix added. You can find all the existing parse functions in the
-``parse.v`` file.
-
-If a rule does not have a parse function (no ``->``) then the value will be
-passed up the chain which is the desired behavior in most cases. However, be
-careful if there are multiple terms, you will need to provide a parse function
-to return the correct term.
-
-Each of the rules can have an optional type described in ``/* */`` before
-``::=``. Rules that do not have a type will be ignored as parameters for parse
-functions. Otherwise, these types are used in the generated code to make sure
-the correct types are passed into the parse functions.
-
-After making changes to ``grammar.bnf`` you will need to run:
+After making changes to grammar file(s) you will need to run:
.. code-block:: sh
make grammar
-Now, when running `v test .` you may receive errors for missing ``parse_``
-functions, you should implement those now.
-
Testing
-------
diff --git a/docs/v-client-library-docs.rst b/docs/v-client-library-docs.rst
index 1a95ebe..05e73a0 100644
--- a/docs/v-client-library-docs.rst
+++ b/docs/v-client-library-docs.rst
@@ -141,16 +141,6 @@ new_numeric_value expects a value to be valid and the size and scale are determi
-fn new_query_cache
-------------------
-
-
-.. code-block:: v
-
- pub fn new_query_cache() &QueryCache
-
-Create a new query cache.
-
fn new_real_value
-----------------
@@ -393,17 +383,15 @@ struct Connection
catalogs map[string]&CatalogConnection
// funcs only needs to be initialized once on open()
funcs []Func
- // query_cache is maintained over file reopens.
- query_cache &QueryCache
// cast_rules are use for CAST() (see cast.v)
cast_rules map[string]CastFunc
// unary_operators and binary_operators are for operators (see operators.v)
unary_operators map[string]UnaryOperatorFunc
binary_operators map[string]BinaryOperatorFunc
- // current_schema is where to search for unquailified table names. It will
+ // current_schema is where to search for unqualified table names. It will
// have an initial value of 'PUBLIC'.
current_schema string
- // current_catalog (also known as the database). It will have an inital value
+ // current_catalog (also known as the database). It will have an initial value
// derived from the first database file loaded.
current_catalog string
pub mut:
@@ -431,14 +419,6 @@ struct ConnectionOptions
pub struct ConnectionOptions {
pub mut:
- // query_cache contains the precompiled prepared statements that can be
- // reused. This makes execution much faster as parsing the SQL is extremely
- // expensive.
- //
- // By default each connection will be given its own query cache. However,
- // you can safely share a single cache over multiple connections and you are
- // encouraged to do so.
- query_cache &QueryCache = unsafe { nil }
// Warning: This only works for :memory: databases. Configuring it for
// file-based databases will either be ignored or causes crashes.
page_size int
@@ -548,20 +528,6 @@ struct PreparedStmt
A prepared statement is compiled and validated, but not executed. It can then be executed with a set of host parameters to be substituted into the statement. Each invocation requires all host parameters to be passed in.
-struct QueryCache
------------------
-
-
-.. code-block:: v
-
- @[heap]
- pub struct QueryCache {
- mut:
- stmts map[string]Stmt
- }
-
-A QueryCache improves the performance of parsing by caching previously cached statements. By default, a new QueryCache is created for each Connection. However, you can share a single QueryCache safely amung multiple connections for even better performance. See ConnectionOptions.
-
struct Result
-------------
diff --git a/generate-grammar.py b/generate-grammar.py
deleted file mode 100644
index e2b89a7..0000000
--- a/generate-grammar.py
+++ /dev/null
@@ -1,531 +0,0 @@
-import re
-import sys
-
-# type RuleOrString = EarleyRule | string
-
-class EarleyProduction(object):
- # terms []RuleOrString
-
- def __init__(self, terms):
- assert isinstance(terms, list)
- if len(terms) > 0:
- assert isinstance(terms[0], str) or isinstance(terms[0], EarleyRule)
-
- self.terms = terms
-
- def index(self):
- return ','.join([t.__str__() for t in self.terms])
-
-class EarleyRule(object):
- # name string
- # productions []EarleyProduction
-
- def __init__(self, name, productions):
- assert isinstance(name, str)
- assert isinstance(productions, list)
- if len(productions) > 0:
- assert isinstance(productions[0], EarleyProduction)
-
- self.name = name
- self.productions = productions
-
- def __str__(self):
- return self.name
-
- # def __repr__(self):
- # return "%s ::= %s" % (self.name, " | ".join(repr(p) for p in self.productions))
-
-class EarleyState(object):
- # name string
- # production EarleyProduction
- # dot_index int
- # start_column EarleyColumn
- # rules []EarleyRule
-
- def __init__(self, name, production, dot_index, start_column):
- assert isinstance(name, str)
- assert isinstance(production, EarleyProduction)
- assert isinstance(dot_index, int)
- assert isinstance(start_column, EarleyColumn)
-
- self.name = name
- self.production = production
- self.start_column = start_column
- self.dot_index = dot_index
-
- rules = []
- for t in production.terms:
- if isinstance(t, EarleyRule):
- rules.append(t)
-
- self.rules = rules
-
- def __repr__(self):
- terms = []
- for p in self.production.terms:
- terms.append(p.__str__())
-
- terms.insert(self.dot_index, "$")
-
- return f'{self.name} -> {" ".join(terms)} [{self.start_column}-{self.end_column}]'
-
- def eq(self, other):
- return self.name == other.name and self.production == other.production \
- and self.dot_index == other.dot_index and self.start_column == other.start_column
-
- def index(self):
- return f'{self.name} {self.production.index()} {self.dot_index} {self.start_column}'
-
- def completed(self):
- return self.dot_index >= len(self.production.terms)
-
- def next_term(self):
- if self.completed():
- return ''
-
- return self.production.terms[self.dot_index]
-
-class Set(object):
- def __init__(self):
- self.elems = {}
-
- def exists(self, v):
- return v.index() in self.elems
-
- def add(self, v):
- if self.exists(v):
- return
-
- self.elems[v.index()] = v
-
-class EarleyColumn(object):
- # index int
- # token string
- # value string
- # states []EarleyState
- # unique Set
-
- def __init__(self, index, token, value):
- assert isinstance(index, int)
- assert isinstance(token, str)
- assert isinstance(value, str)
-
- self.index = index
- self.token = token
- self.value = value
- self.states = []
- self.unique = Set()
-
- def __str__(self):
- return f'{self.index}'
-
- def __repr__(self):
- return f'{self.token}:{self.value}'
-
- def add(self, state):
- if not self.unique.exists(state):
- self.unique.add(state)
- state.end_column = self
- self.states.append(state)
- return True
-
- return False
-
- def print(self, completedOnly = False):
- print(f'[{self.index}] {self.token.__repr__()}')
- print("=" * 35)
- for s in self.states:
- if completedOnly and not s.completed():
- continue
- print(repr(s))
- print()
-
-class EarleyNode(object):
- # value EarleyState
- # children []EarleyNode
-
- def __init__(self, value, children):
- assert isinstance(value, EarleyState)
- assert isinstance(children, list)
- if len(children) > 0:
- assert isinstance(children[0], EarleyNode)
-
- self.value = value
- self.children = children
-
- def print(self, level):
- print(" " * level + str(self.value))
- for child in self.children:
- child.print(level + 1)
-
- def max(self):
- max = 0
- for child in self.children:
- child_max = child.max()
- if child_max > max:
- max = child_max
-
- if self.value.end_column.index > max:
- max = self.value.end_column.index
-
- return max
-
-def predict(col, rule):
- assert isinstance(rule, EarleyRule)
-
- for prod in rule.productions:
- col.add(EarleyState(rule.name, prod, 0, col))
-
-def scan(col, state, token):
- assert isinstance(state, EarleyState)
- assert isinstance(token, str)
-
- if token != col.token:
- return
-
- col.add(EarleyState(state.name, state.production, state.dot_index + 1, state.start_column))
-
-def complete(col, state):
- assert isinstance(state, EarleyState)
-
- if not state.completed():
- return
-
- for st in state.start_column.states:
- term = st.next_term()
- if not isinstance(term, EarleyRule):
- continue
- if term.name == state.name:
- col.add(EarleyState(st.name, st.production, st.dot_index + 1, st.start_column))
-
-def parse3(rule, table):
- assert isinstance(rule, EarleyRule)
- assert isinstance(table, list)
- if len(table) > 0:
- assert isinstance(table[0], EarleyColumn)
-
- table[0].add(EarleyState("start", EarleyProduction([rule]), 0, table[0]))
-
- for i, col in enumerate(table):
- for j, state in enumerate(col.states):
- if state.completed():
- complete(col, state)
- else:
- term = state.next_term()
- if isinstance(term, EarleyRule):
- predict(col, term)
- elif i + 1 < len(table):
- scan(table[i+1], state, term)
-
- # find gamma rule in last table column (otherwise fail)
- for st in table[len(table)-1].states:
- if st.name == "start" and st.completed():
- return st
-
- max = 0
- for col in table:
- for st in col.states:
- t = build_trees(st)
- if len(t) > 0:
- m = t[0].max()
- if m > max:
- max = m
-
- if max+1 >= len(table):
- max = len(table)-2
-
- raise ValueError("syntax error at " + table[max+1].value)
-
-def build_trees(state):
- assert isinstance(state, EarleyState)
-
- return build_trees_helper([], state, len(state.rules) - 1, state.end_column)
-
-def build_trees_helper(children, state, rule_index, end_column):
- assert isinstance(children, list)
- if len(children) > 0:
- assert isinstance(children[0], EarleyNode)
- assert isinstance(state, EarleyState)
- assert isinstance(rule_index, int)
- assert isinstance(end_column, EarleyColumn)
-
- start_column = EarleyColumn(-1, '', '')
- if rule_index < 0:
- return [EarleyNode(state, children)]
- elif rule_index == 0:
- start_column = state.start_column
-
- rule = state.rules[rule_index]
- outputs = []
- for st in end_column.states:
- if st is state:
- break
- if st is state or not st.completed() or st.name != rule.name:
- continue
- if start_column.index >= 0 and st.start_column != start_column:
- continue
- for sub_tree in build_trees(st):
- new_children = [sub_tree]
- for child in children:
- new_children.append(child)
-
- for node in build_trees_helper(new_children, state, rule_index - 1, st.start_column):
- outputs.append(node)
-
- return outputs
-
-def parse_grammar(grammar):
- grammar_rules = {
- '^identifier': EarleyRule("^identifier", [EarleyProduction(["^identifier"])]),
- '^integer': EarleyRule("^integer", [EarleyProduction(["^integer"])]),
- '^string': EarleyRule("^string", [EarleyProduction(["^string"])]),
- }
- lines = []
- parse_functions = {}
- grammar_types = {}
-
- full_line = ''
- for line in grammar.split("\n"):
- if line.startswith('#'):
- continue
- if '/*' in line and '::=' in line:
- parts = line.split('/*')
- full_line += parts[0] + "::= "
- grammar_types[parts[0].strip()] = parts[1].split('*/')[0].strip()
- elif line.strip() == '':
- if full_line != '':
- lines.append(full_line.strip())
- full_line = ''
- else:
- full_line += ' ' + line
-
- # First parse to collect empty rules by name
- for line_number, line in enumerate(lines):
- name, raw_rules = line.split(' ::=')
-
- if '"' in raw_rules:
- grammar_rules[name] = EarleyRule(name, [EarleyProduction(re.findall('"(.+)"', raw_rules))])
- continue
-
- grammar_rule = EarleyRule(name, [])
- grammar_rules[name] = grammar_rule
- rules = raw_rules.split(' | ')
- for rule_number, rule in enumerate(rules):
- # If there is a parsing function we replace the current rule with a
- # subrule:
- #
- # /* Term */ ::=
- #
- # | -> term_1
- # | -> term_2
- #
- # Becomes:
- #
- # ::=
- #
- # |
- # |
- #
- # ::=
- #
- # ::=
- #
- # The subrule is important becuase it creates a single node that we
- # can capture all the children and invoke the parsing function.
- if '->' in rule:
- rule, parse_function = rule.split(' -> ')
- new_rule = name[:-1] + ': #' + str(rule_number + 1) + '>'
- lines.append(new_rule + ' ::= ' + rule)
- rules[rule_number] = new_rule
- parse_functions[new_rule] = (parse_function.strip(), re.findall('(<.*?>|[^\\s]+)', rule))
-
- for token in re.findall('(<.*?>|[^\\s]+)', rule):
- if token.isupper():
- grammar_rules[token] = EarleyRule(token, [EarleyProduction([token])])
- grammar_types[token] = ''
-
- lines[line_number] = name + ' ::= ' + ' | '.join(rules)
-
- grammar_rules[name] = grammar_rule
-
- # Second parse to add the productions for rules
- for line in lines:
- name, raw_rules = line.split(' ::= ')
-
- if '"' in raw_rules:
- continue
-
- rules = raw_rules.split(' | ')
- for rule in rules:
- actual_rule = []
- for token in re.findall('(<.*?>|[^\\s]+)', rule):
- actual_rule.append(grammar_rules[token])
- grammar_rules[name].productions.append(EarleyProduction(actual_rule))
-
- return grammar_rules, parse_functions, grammar_types
-
-grammar_file = open('grammar.bnf', mode='r')
-grammar, parse_functions, grammar_types = parse_grammar(grammar_file.read())
-grammar_file.close()
-
-def rule_name(name):
- if isinstance(name, EarleyRule):
- return name.name
-
- return name
-
-def var_name(name):
- return "rule_" + rule_name(name).lower().replace('<', '').replace('#', '_').replace('-', '_').replace(':', '').replace('>', '_').replace(' ', '_').replace('^', '_')
-
-# Generate grammar file
-grammar_file = open('vsql/grammar.v', mode='w')
-
-grammar_file.write('// grammar.v is generated. DO NOT EDIT.\n')
-grammar_file.write('// It can be regenerated from the grammar.bnf with:\n')
-grammar_file.write('// python generate-grammar.py\n\n')
-grammar_file.write('module vsql\n\n')
-
-grammar_file.write('type EarleyValue = ')
-grammar_file.write(' | '.join(sorted(set([g for g in grammar_types.values() if g]))))
-grammar_file.write('\n\n')
-
-grammar_file.write('fn get_grammar() map[string]EarleyRule {\n')
-grammar_file.write('\tmut rules := map[string]EarleyRule{}\n\n')
-
-for gr in sorted(grammar.keys(), key=lambda s: s.lower()):
- grammar_file.write('\tmut ' + var_name(gr) + ' := &EarleyRule{name: "' + gr + '"}\n')
-
-grammar_file.write('\n')
-
-for gr in sorted(grammar.keys(), key=lambda s: s.lower()):
- for production in grammar[gr].productions:
- grammar_file.write('\t' + var_name(gr) + '.productions << &EarleyProduction{[\n')
- for term in production.terms:
- if isinstance(term, str):
- grammar_file.write("\t\t&EarleyRuleOrString{str: '" + rule_name(term) + "', rule: unsafe { 0 }},\n")
- else:
- grammar_file.write("\t\t&EarleyRuleOrString{rule: " + var_name(term) + "},\n")
- grammar_file.write('\t]}\n')
- grammar_file.write('\n')
-
-for gr in sorted(grammar.keys(), key=lambda s: s.lower()):
- grammar_file.write('\trules[\'' + rule_name(gr) + '\'] = ' + var_name(gr) + '\n')
-
-grammar_file.write('\n\treturn rules\n')
-grammar_file.write('}\n\n')
-
-grammar_file.write("""fn parse_ast(node &EarleyNode) ![]EarleyValue {
- if node.children.len == 0 {
- match node.value.name {
- '^integer' {
- return [EarleyValue(node.value.end_column.value)]
- }
- '^identifier' {
- return [EarleyValue(IdentifierChain{node.value.end_column.value})]
- }
- '^string' {
- // See ISO/IEC 9075-2:2016(E), "5.3 ", #17
- return [EarleyValue(new_character_value(node.value.end_column.value))]
- }
- else {
- if node.value.name[0] == `<` {
- return [EarleyValue(node.value.end_column.value)]
- }
-
- if node.value.name.is_upper() {
- return [EarleyValue(node.value.name)]
- }
-
- panic(node.value.name)
- return []EarleyValue{}
- }
- }
- }
-
- mut children := []EarleyValue{}
- for child in node.children {
- for result in parse_ast(child) ! {
- children << result
- }
- }
-
- return parse_ast_name(children, node.value.name)
-}
-
-fn parse_ast_name(children []EarleyValue, name string) ![]EarleyValue {
- match name {
-""")
-
-for rule in sorted(parse_functions.keys(), key=lambda s: s.lower()):
- grammar_file.write("\t\t'" + rule + "' {\n")
- function_name, terms = parse_functions[rule]
- if function_name == function_name.lower():
- grammar_file.write("\t\t\treturn [EarleyValue(parse_" + function_name + "(")
- else:
- grammar_file.write("\t\t\treturn [EarleyValue(" + function_name + "(")
- grammar_file.write(', '.join([
- 'children[' + str(i) + '] as ' + grammar_types[t]
- for i, t in enumerate(terms)
- if t in grammar_types and grammar_types[t] != '']))
- if function_name == function_name.lower():
- grammar_file.write(") !)]\n")
- else:
- grammar_file.write("))]\n")
-
- grammar_file.write("\t\t}\n")
-
-grammar_file.write("""\t\telse {
- return children
- }
- }
-}
-""")
-
-grammar_file.close()
-
-def parse_tree(text):
- tokens = [''] + text.split(' ')
- table = []
- for i, token in enumerate(tokens):
- if token == '(' or token == ')' or token == ',' or token == '.' or \
- token == '+' or token == '-' or token == '||' or token == ':':
- table.append(EarleyColumn(i, token, token))
- elif token == '' or token.isupper():
- table.append(EarleyColumn(i, token, token))
- elif token[0] == "'":
- table.append(EarleyColumn(i, '^string', token[1:-1]))
- elif token.isdigit():
- table.append(EarleyColumn(i, '^integer', token))
- else:
- table.append(EarleyColumn(i, '^identifier', token))
-
- q0 = parse3(grammar[''], table)
- build_trees(q0)[0].print(0)
-
-# Here are some examples you can enable for testing and debugging.
-
-# parse_tree("DROP TABLE foo")
-# parse_tree("SELECT 1 + 2 FROM t")
-# parse_tree("INSERT INTO foo ( a , b ) VALUES ( 123 , 'bar' )")
-# parse_tree("CREATE TABLE foo ( x INT NOT NULL )")
-# parse_tree("INSERT INTO t ( x ) VALUES ( 0 )")
-# parse_tree("SELECT ABS ( 1 . 2 ) , ABS ( - 1 . 23 ) FROM t")
-# parse_tree("SELECT FLOOR ( 3 . 7 ) , FLOOR ( 3 . 3 ) , FLOOR ( - 3 . 7 ) , FLOOR ( - 3 . 3 ) FROM t")
-# parse_tree("CREATE TABLE ABS (x INT)")
-# parse_tree("SELECT 'foo' || 'bar' AS Str FROM t")
-# parse_tree("SELECT TRUE AND TRUE FROM t")
-# parse_tree("SELECT * FROM t OFFSET 0 ROWS")
-# parse_tree("SELECT * FROM t FETCH FIRST 1 ROW ONLY")
-# parse_tree("SELECT product_name , no_pennies ( price ) AS total FROM products")
-# parse_tree("SELECT * FROM ( VALUES 1 )")
-# parse_tree("SELECT * FROM ( VALUES ROW ( 123 ) , ROW ( 456 ) )")
-# parse_tree("SELECT x FROM ( SELECT y FROM t2 )")
-# parse_tree("SELECT x , y FROM ( SELECT x , y FROM t1 )")
-# parse_tree("VALUES TIMESTAMP '2022-06-30'")
-# parse_tree("INSERT INTO foo ( f1 ) VALUES ( TIMESTAMP '2022-06-30' )")
-# parse_tree("VALUES TRIM ( 'helloworld' )")
-# parse_tree("CREATE SCHEMA public")
-
-for arg in sys.argv[1:]:
- print(arg)
- parse_tree(arg)
diff --git a/scripts/generate_grammar.py b/scripts/generate_grammar.py
new file mode 100644
index 0000000..b4a8fab
--- /dev/null
+++ b/scripts/generate_grammar.py
@@ -0,0 +1,26 @@
+from os import listdir
+from os.path import isfile, join
+import sys
+
+path = 'vsql'
+files = [path + '/' + f for f in listdir(path) if isfile(join(path, f)) and f.endswith('.y')]
+
+top = ""
+middle = ""
+bottom = ""
+
+for file_path in sorted(files):
+ with open(file_path) as f:
+ parts = f.read().split('%%')
+ if len(parts) != 3:
+ sys.exit(path + ': wrong number of parts (' + str(len(parts)) + ')')
+ top += parts[0]
+ middle += parts[1]
+ bottom += parts[2]
+
+with open(path + '/' + 'y.y', 'w') as f:
+ f.write(top.strip())
+ f.write('\n%%\n')
+ f.write(middle.strip())
+ f.write('\n%%\n')
+ f.write(bottom.strip())
diff --git a/scripts/vyacc.v b/scripts/vyacc.v
new file mode 100644
index 0000000..a9a72ae
--- /dev/null
+++ b/scripts/vyacc.v
@@ -0,0 +1,3725 @@
+/*
+Derived from Go's yacc tool
+https://pkg.go.dev/golang.org/x/tools/cmd/goyacc
+
+Derived from Inferno's utils/iyacc/yacc.c
+http://code.google.com/p/inferno-os/source/browse/utils/iyacc/yacc.c
+
+This copyright NOTICE applies to all files in this directory and
+subdirectories, unless another copyright notice appears in a given
+file or subdirectory. If you take substantial code from this software to use in
+other programs, you must somehow include with it an appropriate
+copyright notice that includes the copyright notice and the other
+notices below. It is fine (and often tidier) to do that in a separate
+file such as NOTICE, LICENCE or COPYING.
+
+ Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+ Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+ Portions Copyright © 1997-1999 Vita Nuova Limited
+ Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+ Portions Copyright © 2004,2006 Bruce Ellis
+ Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+ Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+ Portions Copyright © 2009 The Go Authors. All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+module main
+
+// yacc
+// major difference is lack of stem ("y" variable)
+//
+import flag
+import os
+import strings
+import encoding.utf8
+
+// the following are adjustable
+// according to memory size
+const actsize = 240000
+const nstates = 16000
+const tempsize = 16000
+
+const syminc = 50 // increase for non-term or term
+const ruleinc = 50 // increase for max rule length y.prodptr[i]
+const prodinc = 100 // increase for productions y.prodptr
+const wsetinc = 50 // increase for working sets y.wsets
+const stateinc = 200 // increase for states y.statemem
+
+const private = 0xE000 // unicode private use
+
+// relationships which must hold:
+// tempsize >= NTERMS + NNONTERM + 1;
+// tempsize >= nstates;
+//
+
+const ntbase = 0o10000
+const errcode = 8190
+const acceptcode = 8191
+const yylexunk = 3
+const tokstart = 4 // index of first defined token
+
+// no, left, right, binary assoc.
+const noasc = 0
+const lasc = 1
+const rasc = 2
+const basc = 3
+
+// flags for state generation
+const done = 0
+const mustdo = 1
+const mustlookahead = 2
+
+// flags for a rule having an action, and being reduced
+const actflag = 4
+const redflag = 8
+
+// output parser flags
+const yy_flag = -1000
+
+// parse tokens
+const identifier = private + 0
+const mark = private + 1
+const term = private + 2
+const left = private + 3
+const right = private + 4
+const binary = private + 5
+const prec = private + 6
+const lcurly = private + 7
+const identcolon = private + 8
+const number = private + 9
+const start = private + 10
+const typedef = private + 11
+const typename = private + 12
+const union = private + 13
+const error = private + 14
+
+const max_int8 = 127
+const min_int8 = -128
+const max_int16 = 32767
+const min_int16 = -32768
+const max_int32 = 2147483647
+const min_int32 = -2147483648
+const max_uint8 = 255
+const max_uint16 = 65535
+
+const endfile = 0
+const empty = 1
+const whoknows = 0
+const ok = 1
+const nomore = -1000
+
+// macros for getting associativity and precedence levels
+fn assoc(i int) int {
+ return i & 3
+}
+
+fn plevel(i int) int {
+ return (i >> 4) & 0o77
+}
+
+fn typ(i int) int {
+ return (i >> 10) & 0o77
+}
+
+// macros for setting associativity and precedence levels
+fn setasc(i int, j int) int {
+ return i | j
+}
+
+fn setplev(i int, j int) int {
+ unsafe {
+ return i | (j << 4)
+ }
+}
+
+fn settype(i int, j int) int {
+ unsafe {
+ return i | (j << 10)
+ }
+}
+
+struct Vyacc {
+mut:
+ // I/O descriptors
+ finput os.File // Reader: input file
+ stderr os.File // Writer
+ ftable os.File // Writer: y.v file
+ fcode strings.Builder = strings.new_builder(0) // saved code
+ foutput ?os.File // Writer: y.output file
+
+ fmt_imported bool // output file has recorded an import of "fmt"
+
+ oflag string // -o [y.v] - y.v file
+ vflag string // -v [y.output] - y.output file
+ lflag bool // -l - disable line directives
+ prefix string // name prefix for identifiers, default yy
+ restflag []string
+
+ // communication variables between various I/O routines
+ infile string // input file name
+ numbval int // value of an input number
+ tokname string // input token name, slop for runes and 0
+ tokflag bool
+
+ // storage of types
+ ntypes int // number of types defined
+ typeset map[int]string = map[int]string{} // pointers to type tags
+
+ // token information
+ ntokens int // number of tokens
+ tokset []Symb
+ toklev []int // vector with the precedence of the terminals
+
+ // nonterminal information
+ nnonter int = -1 // the number of nonterminals
+ nontrst []Symb
+ start int // start symbol
+
+ // state information
+ nstate int // number of states
+ pstate []int = []int{len: nstates + 2} // index into y.statemem to the descriptions of the states
+ statemem []Item
+ tystate []int = []int{len: nstates} // contains type information about the states
+ tstates []int // states generated by terminal gotos
+ ntstates []int // states generated by nonterminal gotos
+ mstates []int = []int{len: nstates} // chain of overflows of term/nonterm generation lists
+ lastred int // number of last reduction of a state
+ defact []int = []int{len: nstates} // default actions of states
+
+ // lookahead set information
+ nolook int // flag to turn off lookahead computations
+ tbitset int // size of lookahead sets
+ clset Lkset // temporary storage for lookahead computations
+
+ // working set information
+ wsets []Wset
+ cwp int
+
+ // storage for action table
+ amem []int // action table storage
+ memp int // next free action table position
+ indgo []int = []int{len: nstates} // index to the stored goto table
+
+ // temporary vector, indexable by states, terms, or ntokens
+ temp1 []int = []int{len: tempsize} // temporary storage, indexed by terms + ntokens or states
+ lineno int = 1 // current input line number
+ fatfl int = 1 // if on, error is fatal
+ nerrors int // number of y.errors
+
+ // assigned token type values
+ extval int
+
+ // grammar rule information
+ nprod int = 1 // number of productions
+ prdptr [][]int // pointers to descriptions of productions
+ levprd []int // precedence levels for the productions
+ rlines []int // line number for this rule
+
+ // statistics collection variables
+ zzgoent int
+ zzgobest int
+ zzacent int
+ zzexcp int
+ zzclose int
+ zzrrconf int
+ zzsrconf int
+ zzstate int
+
+ // optimizer arrays
+ yypgo [][]int
+ optst [][]int
+ ggreed []int
+ pgo []int
+
+ maxspr int // maximum spread of any entry
+ maxoff int // maximum offset into a array
+ maxa int
+
+ // storage for information about the nonterminals
+ pres [][][]int // vector of pointers to productions yielding each nonterminal
+ pfirst []Lkset
+ pempty []int // vector of nonterminals nontrivially deriving e
+
+ // random stuff picked out from between functions
+ indebug int // debugging flag for cpfir
+ pidebug int // debugging flag for putitem
+ gsdebug int // debugging flag for stagen
+ cldebug int // debugging flag for closure
+ pkdebug int // debugging flag for apack
+ g2debug int // debugging for go2gen
+ adb int = 1 // debugging for callopt
+
+ errors []ParseError
+
+ state_table []Row
+
+ zznewstate int
+
+ yaccpar string // will be processed version of yaccpartext: s/$\$/prefix/g
+
+ peekline int
+
+ resrv []Resrv = [
+ Resrv{'binary', binary},
+ Resrv{'left', left},
+ Resrv{'nonassoc', binary},
+ Resrv{'prec', prec},
+ Resrv{'right', right},
+ Resrv{'start', start},
+ Resrv{'term', term},
+ Resrv{'token', term},
+ Resrv{'type', typedef},
+ Resrv{'union', @union},
+ Resrv{'struct', @union},
+ Resrv{'error', error},
+]
+
+ // utility routines
+ peekrune rune
+}
+
+fn (mut y Vyacc) init() {
+ mut parser := flag.new_flag_parser(os.args)
+ y.oflag = parser.string('', `o`, 'y.v', 'parser output', flag.FlagConfig{})
+ y.prefix = parser.string('', `p`, 'yy', 'name prefix to use in generated code', flag.FlagConfig{})
+ y.vflag = parser.string('', `v`, 'y.output', 'create parsing tables', flag.FlagConfig{})
+ y.lflag = parser.bool('', `l`, false, 'disable line directives', flag.FlagConfig{})
+ y.restflag = (parser.finalize() or { panic(err) })[1..]
+}
+
+const initialstacksize = 16
+
+// structure declarations
+type Lkset = []int
+
+struct Pitem {
+mut:
+ prod []int
+ off int // offset within the production
+ first int // first term or non-term in item
+ prodno int // production number for sorting
+}
+
+struct Item {
+mut:
+ pitem Pitem
+ look Lkset
+}
+
+struct Symb {
+mut:
+ name string
+ noconst bool
+ value int
+}
+
+struct Wset {
+mut:
+ pitem Pitem
+ flag int
+ ws Lkset
+}
+
+struct Resrv {
+ name string
+ value int
+}
+
+struct ParseError {
+ lineno int
+ tokens []string
+ msg string
+}
+
+struct Row {
+ actions []int
+ default_action int
+}
+
+const eof = -1
+
+fn main() {
+ mut y := Vyacc{}
+
+ y.init()
+ y.setup()! // initialize and read productions
+
+ y.tbitset = (y.ntokens + 32) / 32
+ y.cpres() // make table of which productions yield a given nonterminal
+ y.cempty() // make a table of which nonterminals can match the empty string
+ y.cpfir()! // make a table of firsts of nonterminals
+
+ y.stagen()! // generate the states
+
+ y.yypgo = [][]int{len: y.nnonter + 1}
+ y.optst = [][]int{len: y.nstate}
+ y.output()! // write the states and the tables
+ y.go2out()!
+
+ y.hideprod()!
+ y.summary()
+
+ y.callopt()!
+
+ y.others()!
+
+ exit_(0)
+}
+
+fn (mut y Vyacc) setup() ! {
+ mut j := 0
+ mut ty := 0
+
+ y.stderr = os.stderr()
+ y.foutput = none
+
+ if y.restflag.len != 1 {
+ y.usage()
+ }
+ if initialstacksize < 1 {
+ // never set so cannot happen
+ y.stderr.write_string('yacc: stack size too small\n')!
+ y.usage()
+ }
+ y.yaccpar = yaccpartext.replace('$\$', y.prefix)
+ y.openup()!
+
+ y.ftable.write_string('// Code generated by vyacc ${os.args[1..].join(' ')}. DO NOT EDIT.\n')!
+
+ y.defin(0, '\$end')
+ y.extval = private // tokens start in unicode 'private use'
+ y.defin(0, 'error')
+ y.defin(1, '\$accept')
+ y.defin(0, '\$unk')
+ mut i := 0
+
+ mut t := y.gettok()
+
+ outer: for {
+ match t {
+ mark, endfile {
+ break outer
+ }
+ int(`;`) {
+ // Do nothing.
+ }
+ start {
+ t = y.gettok()
+ if t != identifier {
+ y.errorf('bad %start construction')
+ }
+ y.start = y.chfind(1, y.tokname)
+ }
+ error {
+ lno := y.lineno
+ mut tokens := []string{}
+ for {
+ t2 := y.gettok()
+ if t2 == `:` {
+ break
+ }
+ if t2 != identifier && t2 != identcolon {
+ y.errorf('bad syntax in %error')
+ }
+ tokens << y.tokname
+ if t2 == identcolon {
+ break
+ }
+ }
+ if y.gettok() != identifier {
+ y.errorf('bad syntax in %error')
+ }
+ y.errors << ParseError{lno, tokens, y.tokname}
+ }
+ typedef {
+ t = y.gettok()
+ if t != typename {
+ y.errorf('bad syntax in %type')
+ }
+ ty = y.numbval
+ for {
+ t = y.gettok()
+ match t {
+ identifier {
+ t = y.chfind(1, y.tokname)
+ if t < ntbase {
+ j = typ(y.toklev[t])
+ if j != 0 && j != ty {
+ y.errorf('type redeclaration of token ${y.tokset[t].name}')
+ } else {
+ y.toklev[t] = settype(y.toklev[t], ty)
+ }
+ } else {
+ j = y.nontrst[t - ntbase].value
+ if j != 0 && j != ty {
+ y.errorf('type redeclaration of nonterminal ${y.nontrst[t - ntbase].name}')
+ } else {
+ y.nontrst[t - ntbase].value = ty
+ }
+ }
+ continue
+ }
+ int(`,`) {
+ continue
+ }
+ else {}
+ }
+ break
+ }
+ continue
+ }
+ @union {
+ y.cpyunion()!
+ }
+ left, binary, right, term {
+ // nonzero means new prec. and assoc.
+ lev := t - term
+ if lev != 0 {
+ i++
+ }
+ ty = 0
+
+ // get identifiers so defined
+ t = y.gettok()
+
+ // there is a type defined
+ if t == typename {
+ ty = y.numbval
+ t = y.gettok()
+ }
+ for {
+ match t {
+ int(`,`) {
+ t = y.gettok()
+ continue
+ }
+ int(`;`) {
+ // Do nothing.
+ }
+ identifier {
+ j = y.chfind(0, y.tokname)
+ if j >= ntbase {
+ y.errorf('${y.tokname} defined earlier as nonterminal')
+ }
+ if lev != 0 {
+ if assoc(y.toklev[j]) != 0 {
+ y.errorf('redeclaration of precedence of ${y.tokname}')
+ }
+ y.toklev[j] = setasc(y.toklev[j], lev)
+ y.toklev[j] = setplev(y.toklev[j], i)
+ }
+ if ty != 0 {
+ if typ(y.toklev[j]) != 0 {
+ y.errorf('redeclaration of type of ${y.tokname}')
+ }
+ y.toklev[j] = settype(y.toklev[j], ty)
+ }
+ t = y.gettok()
+ if t == number {
+ y.tokset[j].value = y.numbval
+ t = y.gettok()
+ }
+
+ continue
+ }
+ else {}
+ }
+ break
+ }
+ continue
+ }
+ lcurly {
+ y.cpycode()!
+ }
+ else {
+ y.errorf('syntax error tok=${t - private}')
+ }
+ }
+ t = y.gettok()
+ }
+
+ if t == endfile {
+ y.errorf('unexpected EOF before %')
+ }
+
+ y.fcode.write_string('match ${y.prefix}nt {\n')
+
+ y.moreprod()
+ y.prdptr[0] = [int(ntbase), y.start, 1, 0]
+
+ y.nprod = 1
+ mut curprod := []int{len: ruleinc}
+ t = y.gettok()
+ if t != identcolon {
+ y.errorf('bad syntax on first rule')
+ }
+
+ if y.start == 0 {
+ y.prdptr[0][1] = y.chfind(1, y.tokname)
+ }
+
+ // read rules
+ // put into y.prdptr array in the format
+ // target
+ // followed by id's of terminals and non-terminals
+ // followed by -y.nprod
+
+ for t != mark && t != endfile {
+ mut mem := 0
+
+ // process a rule
+ y.rlines[y.nprod] = y.lineno
+ ruleline := y.lineno
+ if t == `|` {
+ curprod[mem] = y.prdptr[y.nprod - 1][0]
+ mem++
+ } else if t == identcolon {
+ curprod[mem] = y.chfind(1, y.tokname)
+ if curprod[mem] < ntbase {
+ y.lerrorf(ruleline, 'token illegal on LHS of grammar rule')
+ }
+ mem++
+ } else {
+ y.lerrorf(ruleline, 'illegal rule: missing semicolon or | ?')
+ }
+
+ // read rule body
+ t = y.gettok()
+ for {
+ for t == identifier {
+ curprod[mem] = y.chfind(1, y.tokname)
+ if curprod[mem] < ntbase {
+ y.levprd[y.nprod] = y.toklev[curprod[mem]]
+ }
+ mem++
+ if mem >= curprod.len {
+ mut ncurprod := []int{len: mem + ruleinc}
+ gocopy(mut ncurprod, curprod)
+ curprod = ncurprod.clone()
+ }
+ t = y.gettok()
+ }
+ if t == prec {
+ if y.gettok() != identifier {
+ y.lerrorf(ruleline, 'illegal %prec syntax')
+ }
+ j = y.chfind(2, y.tokname)
+ if j >= ntbase {
+ y.lerrorf(ruleline, 'nonterminal ${y.nontrst[j - ntbase].name} illegal after %prec')
+ }
+ y.levprd[y.nprod] = y.toklev[j]
+ t = y.gettok()
+ }
+ if t != `=` {
+ break
+ }
+ y.levprd[y.nprod] |= actflag
+ y.fcode.write_string('\n\t${y.nprod} {')
+ y.fcode.write_string('\n\t\t${y.prefix}_dollar = ${y.prefix}_s[${y.prefix}pt-${mem - 1}..${y.prefix}pt+1].clone()')
+ y.cpyact(curprod, mem)!
+ y.fcode.write_string('\n\t}')
+
+ // action within rule...
+ t = y.gettok()
+ if t == identifier {
+ // make it a nonterminal
+ j = y.chfind(1, '$\$${y.nprod}')
+
+ //
+ // the current rule will become rule number y.nprod+1
+ // enter null production for action
+ //
+ y.prdptr[y.nprod] = []int{len: 2}
+ y.prdptr[y.nprod][0] = j
+ y.prdptr[y.nprod][1] = -y.nprod
+
+ // update the production information
+ y.nprod++
+ y.moreprod()
+ y.levprd[y.nprod] = y.levprd[y.nprod - 1] & ~actflag
+ y.levprd[y.nprod - 1] = actflag
+ y.rlines[y.nprod] = y.lineno
+
+ // make the action appear in the original rule
+ curprod[mem] = j
+ mem++
+ if mem >= curprod.len {
+ mut ncurprod := []int{len: mem + ruleinc}
+ gocopy(mut ncurprod, curprod)
+ curprod = ncurprod.clone()
+ }
+ }
+ }
+
+ for t == `;` {
+ t = y.gettok()
+ }
+ curprod[mem] = -y.nprod
+ mem++
+
+ // check that default action is reasonable
+ if y.ntypes != 0 && (y.levprd[y.nprod] & actflag) == 0
+ && y.nontrst[curprod[0] - ntbase].value != 0 {
+ // no explicit action, LHS has value
+ mut tempty := curprod[1]
+ if tempty < 0 {
+ y.lerrorf(ruleline, 'must return a value, since LHS has a type')
+ }
+ if tempty >= ntbase {
+ tempty = y.nontrst[tempty - ntbase].value
+ } else {
+ tempty = typ(y.toklev[tempty])
+ }
+ if tempty != y.nontrst[curprod[0] - ntbase].value {
+ y.lerrorf(ruleline, 'default action causes potential type clash')
+ }
+ }
+ y.moreprod()
+ y.prdptr[y.nprod] = []int{len: mem}
+ gocopy(mut y.prdptr[y.nprod], curprod)
+ y.nprod++
+ y.moreprod()
+ y.levprd[y.nprod] = 0
+ }
+ y.fcode.write_string('\n\telse {}')
+
+ if tempsize < y.ntokens + y.nnonter + 1 {
+ y.errorf('too many tokens (${y.ntokens}) or non-terminals (${y.nnonter})')
+ }
+
+ //
+ // end of all rules
+ // dump out the prefix code
+ //
+
+ y.fcode.write_string('\n\t}')
+
+ // put out non-literal terminals
+ for i2 := tokstart; i2 <= y.ntokens; i2++ {
+ // non-literals
+ if !y.tokset[i2].noconst {
+ y.ftable.write_string('const token_${y.tokset[i2].name.to_lower()} = ${y.tokset[i2].value}\n')!
+ }
+ }
+
+ // put out names of tokens
+ y.ftable.write_string('\n')!
+ y.ftable.write_string('const ${y.prefix}_toknames = [\n')!
+ for i2 := 1; i2 <= y.ntokens; i2++ {
+ y.ftable.write_string("\t\"${y.tokset[i2].name.replace('$', '\\$')}\",\n")!
+ }
+ y.ftable.write_string(']\n')!
+
+ // put out names of states.
+ // commented out to avoid a huge table just for debugging.
+ // re-enable to have the names in the binary.
+ y.ftable.write_string('\n')!
+ y.ftable.write_string('const ${y.prefix}_statenames = [\n')!
+ for j = tokstart; j <= y.ntokens; j++ {
+ y.ftable.write_string("\t\"${y.tokset[j].name}\",\n")!
+ }
+ y.ftable.write_string(']\n')!
+
+ y.ftable.write_string('\n')!
+ y.ftable.write_string('const ${y.prefix}_eof_code = 1\n')!
+ y.ftable.write_string('const ${y.prefix}_err_code = 2\n')!
+ y.ftable.write_string('const ${y.prefix}_initial_stack_size = ${initialstacksize}\n')!
+
+ //
+ // copy any postfix code
+ //
+ if t == mark {
+ if !y.lflag {
+ y.ftable.write_string('\n//line ${y.infile}:${y.lineno}\n')!
+ }
+ for {
+ c := y.getrune(mut y.finput)
+ if c == eof {
+ break
+ }
+ y.ftable.write_string(c.str())!
+ }
+ }
+}
+
+// allocate enough room to hold another production
+fn (mut y Vyacc) moreprod() {
+ n := y.prdptr.len
+ if y.nprod >= n {
+ nn := n + prodinc
+ mut aprod := [][]int{len: nn}
+ mut alevprd := []int{len: nn}
+ mut arlines := []int{len: nn}
+
+ gocopy(mut aprod, y.prdptr)
+ gocopy(mut alevprd, y.levprd)
+ gocopy(mut arlines, y.rlines)
+
+ y.prdptr = aprod
+ y.levprd = alevprd
+ y.rlines = arlines
+ }
+}
+
+// define s to be a terminal if nt==0
+// or a nonterminal if nt==1
+fn (mut y Vyacc) defin(nt int, s string) int {
+ mut val := 0
+ if nt != 0 {
+ y.nnonter++
+ if y.nnonter >= y.nontrst.len {
+ mut anontrst := []Symb{len: y.nnonter + syminc}
+ gocopy(mut anontrst, y.nontrst)
+ y.nontrst = anontrst
+ }
+ y.nontrst[y.nnonter] = Symb{
+ name: s
+ }
+ return ntbase + y.nnonter
+ }
+
+ // must be a token
+ y.ntokens++
+ if y.ntokens >= y.tokset.len {
+ nn := y.ntokens + syminc
+ mut atokset := []Symb{len: nn}
+ mut atoklev := []int{len: nn}
+
+ gocopy(mut atoklev, y.toklev)
+ gocopy(mut atokset, y.tokset)
+
+ y.tokset = atokset
+ y.toklev = atoklev
+ }
+ y.tokset[y.ntokens].name = s
+ y.toklev[y.ntokens] = 0
+
+ // establish value for token
+ // single character literal
+ if s[0] == `'` || s[0] == `"` {
+ q := unquote(s) or {
+ y.errorf('invalid token: ${err}')
+ ''
+ }
+ rq := q.runes()
+ if rq.len != 1 {
+ y.errorf('character token too long: ${s}')
+ }
+ val = int(rq[0])
+ if val == 0 {
+ y.errorf('token value 0 is illegal')
+ }
+ y.tokset[y.ntokens].noconst = true
+ } else {
+ val = y.extval
+ y.extval++
+ if s[0] == `$` {
+ y.tokset[y.ntokens].noconst = true
+ }
+ }
+
+ y.tokset[y.ntokens].value = val
+ return y.ntokens
+}
+
+fn gocopy[T](mut dst []T, src []T) int {
+ mut min := dst.len
+ if src.len < min {
+ min = src.len
+ }
+ for i := 0; i < min; i++ {
+ dst[i] = src[i]
+ }
+ return src.len
+}
+
+fn (mut y Vyacc) gettok() int {
+ mut i := 0
+ mut mtch := rune(0)
+ mut c := rune(0)
+
+ y.tokname = ''
+ for {
+ y.lineno += y.peekline
+ y.peekline = 0
+ c = y.getrune(mut y.finput)
+ for c == ` ` || c == `\n` || c == `\t` || c == `\v` || c == `\r` {
+ if c == `\n` {
+ y.lineno++
+ }
+ c = y.getrune(mut y.finput)
+ }
+
+ // skip comment -- fix
+ if c != `/` {
+ break
+ }
+ y.lineno += y.skipcom()
+ }
+
+ match c {
+ eof {
+ if y.tokflag {
+ print('>>> endfile ${y.lineno}\n')
+ }
+ return endfile
+ }
+ `{` {
+ y.ungetrune(y.finput, c)
+ if y.tokflag {
+ print('>>> ={ ${y.lineno}\n')
+ }
+ return int(`=`)
+ }
+ `<` {
+ // get, and look up, a type name (union member name)
+ c = y.getrune(mut y.finput)
+ for c != `>` && c != eof && c != `\n` {
+ y.tokname += c.str()
+ c = y.getrune(mut y.finput)
+ }
+
+ if c != `>` {
+ y.errorf('unterminated < ... > clause')
+ }
+
+ for i = 1; i <= y.ntypes; i++ {
+ if y.typeset[i] == y.tokname {
+ y.numbval = i
+ if y.tokflag {
+ print('>>> typename old <${y.tokname}> ${y.lineno}\n')
+ }
+ return typename
+ }
+ }
+ y.ntypes++
+ y.numbval = y.ntypes
+ y.typeset[y.numbval] = y.tokname
+ if y.tokflag {
+ print('>>> typename new <${y.tokname}> ${y.lineno}\n')
+ }
+ return typename
+ }
+ `"`, `'` {
+ mtch = c
+ y.tokname = c.str()
+ for {
+ c = y.getrune(mut y.finput)
+ if c == `\n` || c == eof {
+ y.errorf('illegal or missing \' or "')
+ }
+ if c == `\\` {
+ y.tokname += `\\`.str()
+ c = y.getrune(mut y.finput)
+ } else if c == mtch {
+ if y.tokflag {
+ print(">>> identifier \"${y.tokname}\" ${y.lineno}\n")
+ }
+ y.tokname += c.str()
+ return identifier
+ }
+ y.tokname += c.str()
+ }
+ }
+ `%` {
+ c = y.getrune(mut y.finput)
+ match c {
+ `%` {
+ if y.tokflag {
+ print('>>> mark % ${y.lineno}\n')
+ }
+ return mark
+ }
+ `=` {
+ if y.tokflag {
+ print('>>> prec %= ${y.lineno}\n')
+ }
+ return prec
+ }
+ `{` {
+ if y.tokflag {
+ print('>>> lcurly %{ ${y.lineno}\n')
+ }
+ return lcurly
+ }
+ else {}
+ }
+
+ y.getword(c)
+ // find a reserved word
+ for i2, _ in y.resrv {
+ if y.tokname == y.resrv[i2].name {
+ if y.tokflag {
+ print('>>> %${y.tokname} ${y.resrv[i2].value - private} ${y.lineno}\n')
+ }
+ return y.resrv[i2].value
+ }
+ }
+ y.errorf('invalid escape, or illegal reserved word: ${y.tokname}')
+ }
+ `0`, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9` {
+ y.numbval = int(c - `0`)
+ for {
+ c = y.getrune(mut y.finput)
+ if !isdigit(c) {
+ break
+ }
+ y.numbval = y.numbval * 10 + int(c - `0`)
+ }
+ y.ungetrune(y.finput, c)
+ if y.tokflag {
+ print('>>> number ${y.numbval} ${y.lineno}\n')
+ }
+ return number
+ }
+ else {
+ if isword(c) || c == `.` || c == `$` {
+ y.getword(c)
+ } else {
+ if y.tokflag {
+ print('>>> OPERATOR ${c.str()} ${y.lineno}\n')
+ }
+ return int(c)
+ }
+ }
+ }
+
+ // look ahead to distinguish identifier from identcolon
+ c = y.getrune(mut y.finput)
+ for c == ` ` || c == `\t` || c == `\n` || c == `\v` || c == `\r` || c == `/` {
+ if c == `\n` {
+ y.peekline++
+ }
+ // look for comments
+ if c == `/` {
+ y.peekline += y.skipcom()
+ }
+ c = y.getrune(mut y.finput)
+ }
+ if c == `:` {
+ if y.tokflag {
+ print('>>> identcolon ${y.tokname}: ${y.lineno}\n')
+ }
+ return identcolon
+ }
+
+ y.ungetrune(y.finput, c)
+ if y.tokflag {
+ print('>>> identifier ${y.tokname} ${y.lineno}\n')
+ }
+ return identifier
+}
+
+fn (mut y Vyacc) getword(c rune) {
+ mut c2 := c
+ y.tokname = ''
+ for isword(c2) || isdigit(c2) || c2 == `.` || c2 == `$` {
+ y.tokname += c2.str()
+ c2 = y.getrune(mut y.finput)
+ }
+ y.ungetrune(y.finput, c2)
+}
+
+// determine the type of a symbol
+fn (mut y Vyacc) fdtype(t int) int {
+ mut v := 0
+ mut s := ''
+
+ if t >= ntbase {
+ v = y.nontrst[t - ntbase].value
+ s = y.nontrst[t - ntbase].name
+ } else {
+ v = typ(y.toklev[t])
+ s = y.tokset[t].name
+ }
+ if v <= 0 {
+ y.errorf('must specify type for ${s}')
+ }
+ return v
+}
+
+fn (mut y Vyacc) chfind(t int, s string) int {
+ mut t2 := t
+
+ if s[0] == `"` || s[0] == `'` {
+ t2 = 0
+ }
+ for i := 0; i <= y.ntokens; i++ {
+ if s == y.tokset[i].name {
+ return i
+ }
+ }
+ for i := 0; i <= y.nnonter; i++ {
+ if s == y.nontrst[i].name {
+ return ntbase + i
+ }
+ }
+
+ // cannot find name
+ if t2 > 1 {
+ y.errorf('${s} should have been defined earlier')
+ }
+ return y.defin(t2, s)
+}
+
+// copy the union declaration to the output, and the define file if present
+fn (mut y Vyacc) cpyunion() ! {
+ if !y.lflag {
+ y.ftable.write_string('\n//line ${y.infile}:${y.lineno}\n')!
+ }
+ y.ftable.write_string('type ${y.prefix}SymType struct')!
+
+ mut level := 0
+
+ out: for {
+ c := y.getrune(mut y.finput)
+ if c == eof {
+ y.errorf('EOF encountered while processing %union')
+ }
+ y.ftable.write(c.bytes())!
+ match c {
+ `\n` {
+ y.lineno++
+ }
+ `{` {
+ if level == 0 {
+ y.ftable.write_string('\n\tyys int')!
+ }
+ level++
+ }
+ `}` {
+ level--
+ if level == 0 {
+ break out
+ }
+ }
+ else {}
+ }
+ }
+ y.ftable.write_string('\n\n')!
+}
+
+// saves code between %{ and %}
+// adds an import for __fmt__ the first time
+fn (mut y Vyacc) cpycode() ! {
+ lno := y.lineno
+
+ mut c := y.getrune(mut y.finput)
+ if c == `\n` {
+ c = y.getrune(mut y.finput)
+ y.lineno++
+ }
+ if !y.lflag {
+ y.ftable.write_string('\n//line ${y.infile}:${y.lineno}\n')!
+ }
+ // accumulate until %}
+ mut code := []rune{cap: 1024}
+ for c != eof {
+ if c == `%` {
+ c = y.getrune(mut y.finput)
+ if c == `}` {
+ y.emitcode(code, lno + 1)!
+ return
+ }
+ code << `%`
+ }
+ code << c
+ if c == `\n` {
+ y.lineno++
+ }
+ c = y.getrune(mut y.finput)
+ }
+ y.lineno = lno
+ y.errorf('eof before %}')
+}
+
+// emits code saved up from between %{ and %}
+// called by cpycode
+// adds an import for __yyfmt__ after the package clause
+fn (mut y Vyacc) emitcode(code []rune, lineno int) ! {
+ for i, line in lines(code) {
+ y.writecode(line)!
+ if !y.fmt_imported && is_package_clause(line) {
+ y.ftable.write_string('import __yyfmt__ "fmt"\n')!
+ if !y.lflag {
+ y.ftable.write_string('//line ${y.infile}:${lineno + i}\n\t\t')!
+ }
+ y.fmt_imported = true
+ }
+ }
+}
+
+// does this line look like a package clause? not perfect: might be confused by early comments.
+fn is_package_clause(line_ []rune) bool {
+ mut line := line_.clone()
+ line = skipspace(line)
+
+ // must be big enough.
+ if line.len < 'package X\n'.len {
+ return false
+ }
+
+ // must start with "package"
+ for i, r in 'package'.runes() {
+ if line[i] != r {
+ return false
+ }
+ }
+ line = skipspace(line['package'.len..])
+
+ // must have another identifier.
+ if line.len == 0 || (!utf8.is_letter(line[0]) && line[0] != `_`) {
+ return false
+ }
+ for line.len > 0 {
+ if !utf8.is_letter(line[0]) && !utf8.is_number(line[0]) && line[0] != `_` {
+ break
+ }
+ line = line.clone()[1..]
+ }
+ line = skipspace(line)
+
+ // eol, newline, or comment must follow
+ if line.len == 0 {
+ return true
+ }
+ if line[0] == `\r` || line[0] == `\n` {
+ return true
+ }
+ if line.len >= 2 {
+ return line[0] == `/` && (line[1] == `/` || line[1] == `*`)
+ }
+ return false
+}
+
+// skip initial spaces
+fn skipspace(line_ []rune) []rune {
+ mut line := line_.clone()
+ for line.len > 0 {
+ if line[0] != ` ` && line[0] != `\t` {
+ break
+ }
+ line = line.clone()[1..]
+ }
+ return line
+}
+
+// break code into lines
+fn lines(code_ []rune) [][]rune {
+ mut code := code_.clone()
+ mut l := [][]rune{cap: 100}
+ for code.len > 0 {
+ // one line per loop
+ mut i := 0
+ for idx, _ in code {
+ if code[idx] == `\n` {
+ i = idx
+ break
+ }
+ }
+ l << code[..i + 1]
+ code = code.clone()[i + 1..]
+ }
+ return l
+}
+
+// writes code to y.ftable
+fn (mut y Vyacc) writecode(code []rune) ! {
+ for _, r in code {
+ y.ftable.write_string(r.str())!
+ }
+}
+
+// skip over comments
+// skipcom is called after reading a `/`
+fn (mut y Vyacc) skipcom() int {
+ mut c := y.getrune(mut y.finput)
+ if c == `/` {
+ for c != eof {
+ if c == `\n` {
+ return 1
+ }
+ c = y.getrune(mut y.finput)
+ }
+ y.errorf('EOF inside comment')
+ return 0
+ }
+ if c != `*` {
+ y.errorf('illegal comment')
+ }
+
+ mut nl := 0 // lines skipped
+ c = y.getrune(mut y.finput)
+
+ l1:
+ match c {
+ `*` {
+ c = y.getrune(mut y.finput)
+ if c != `/` {
+ unsafe {
+ goto l1
+ }
+ }
+ }
+ `\n` {
+ nl++
+ c = y.getrune(mut y.finput)
+ unsafe {
+ goto l1
+ }
+ }
+ else {
+ c = y.getrune(mut y.finput)
+ unsafe {
+ goto l1
+ }
+ }
+ }
+ return nl
+}
+
+// copy action to the next ; or closing }
+fn (mut y Vyacc) cpyact(curprod []int, max int) ! {
+ if !y.lflag {
+ y.fcode.write_string('\n//line ${y.infile}:${y.lineno}')
+ }
+ y.fcode.write_string('\n\t\t')
+
+ lno := y.lineno
+ mut brac := 0
+
+ loop: for {
+ mut c := y.getrune(mut y.finput)
+
+ match c {
+ `;` {
+ if brac == 0 {
+ y.fcode.write_string(c.str())
+ return
+ }
+ }
+ `{` {
+ brac++
+ }
+ `$` {
+ mut s := 1
+ mut tok := -1
+ c = y.getrune(mut y.finput)
+
+ // type description
+ if c == `<` {
+ y.ungetrune(y.finput, c)
+ if y.gettok() != typename {
+ y.errorf('bad syntax on $ clause')
+ }
+ tok = y.numbval
+ c = y.getrune(mut y.finput)
+ }
+ if c == `$` {
+ y.fcode.write_string('${y.prefix}_val')
+
+ // put out the proper tag...
+ if y.ntypes != 0 {
+ if tok < 0 {
+ tok = y.fdtype(curprod[0])
+ }
+ y.fcode.write_string('.${y.typeset[tok]}')
+ }
+ continue loop
+ }
+ if c == `-` {
+ s = -s
+ c = y.getrune(mut y.finput)
+ }
+ mut j := 0
+ if isdigit(c) {
+ for isdigit(c) {
+ j = j * 10 + int(c - `0`)
+ c = y.getrune(mut y.finput)
+ }
+ y.ungetrune(y.finput, c)
+ j = j * s
+ if j >= max {
+ y.errorf('Illegal use of $${j}')
+ }
+ } else if isword(c) || c == `.` {
+ // look for $name
+ y.ungetrune(y.finput, c)
+ if y.gettok() != identifier {
+ y.errorf('$ must be followed by an identifier')
+ }
+ tokn := y.chfind(2, y.tokname)
+ mut fnd := -1
+ c = y.getrune(mut y.finput)
+ if c != `@` {
+ y.ungetrune(y.finput, c)
+ } else if y.gettok() != number {
+ y.errorf('@ must be followed by number')
+ } else {
+ fnd = y.numbval
+ }
+ for k := 1; k < max; k++ {
+ if tokn == curprod[k] {
+ fnd--
+ if fnd <= 0 {
+ break
+ }
+ }
+ }
+ if j >= max {
+ y.errorf('\$name or \$name@number not found')
+ }
+ } else {
+ y.fcode.write_string('$')
+ if s < 0 {
+ y.fcode.write_string('-')
+ }
+ y.ungetrune(y.finput, c)
+ continue loop
+ }
+ y.fcode.write_string('${y.prefix}_dollar[${j}]')
+
+ // put out the proper tag
+ if y.ntypes != 0 {
+ if j <= 0 && tok < 0 {
+ y.errorf('must specify type of $${j}')
+ }
+ if tok < 0 {
+ tok = y.fdtype(curprod[j])
+ }
+ y.fcode.write_string('.${y.typeset[tok]}')
+ }
+ continue loop
+ }
+ `}` {
+ brac--
+ if brac == 0 {
+ y.fcode.write_string(c.str())
+ return
+ }
+ }
+ `/` {
+ nc := y.getrune(mut y.finput)
+ if nc != `/` && nc != `*` {
+ y.ungetrune(y.finput, nc)
+ } else {
+ // a comment
+ y.fcode.write_string(c.str())
+ y.fcode.write_string(nc.str())
+ c = y.getrune(mut y.finput)
+ for c != eof {
+ match true {
+ c == `\n` {
+ y.lineno++
+ if nc == `/` { // end of // comment
+ unsafe {
+ goto swt
+ }
+ }
+ }
+ c == `*` && nc == `*` { // end of /* comment?
+ nnc := y.getrune(mut y.finput)
+ if nnc == `/` {
+ y.fcode.write_string('*')
+ y.fcode.write_string('/')
+ continue loop
+ }
+ y.ungetrune(y.finput, nnc)
+ }
+ else {}
+ }
+ y.fcode.write_string(c.str())
+ c = y.getrune(mut y.finput)
+ }
+ y.errorf('EOF inside comment')
+ }
+ }
+ `'`, `"` {
+ // character string or constant
+ mtch := c
+ y.fcode.write_string(c.str())
+ c = y.getrune(mut y.finput)
+ for c != eof {
+ if c == `\\` {
+ y.fcode.write_string(c.str())
+ c = y.getrune(mut y.finput)
+ if c == `\n` {
+ y.lineno++
+ }
+ } else if c == mtch {
+ unsafe {
+ goto swt
+ }
+ }
+ if c == `\n` {
+ y.errorf('newline in string or char const')
+ }
+ y.fcode.write_string(c.str())
+ c = y.getrune(mut y.finput)
+ }
+ y.errorf('EOF in string or character constant')
+ }
+ eof {
+ y.lineno = lno
+ y.errorf('action does not terminate')
+ }
+ `\n` {
+ y.fcode.write_string('\n\t')
+ y.lineno++
+ continue loop
+ }
+ else {}
+ }
+ swt:
+ y.fcode.write_string(c.str())
+ }
+}
+
+fn (mut y Vyacc) openup() ! {
+ y.infile = y.restflag[0]
+ y.finput = y.open(y.infile)!
+
+ y.foutput = none
+ if y.vflag != '' {
+ y.foutput = y.create(y.vflag)!
+ }
+
+ if y.oflag == '' {
+ y.oflag = 'y.v'
+ }
+ y.ftable = y.create(y.oflag)!
+}
+
+// return a pointer to the name of symbol i
+fn (y Vyacc) symnam(i int) string {
+ mut s := ''
+
+ if i >= ntbase {
+ s = y.nontrst[i - ntbase].name
+ } else {
+ s = y.tokset[i].name
+ }
+ return s
+}
+
+// set elements 0 through n-1 to c
+fn aryfil(mut v []int, n int, c int) {
+ for i := 0; i < n; i++ {
+ v[i] = c
+ }
+}
+
+// compute an array with the beginnings of productions yielding given nonterminals
+// The array y.pres points to these lists
+// the array pyield has the lists: the total size is only NPROD+1
+fn (mut y Vyacc) cpres() {
+ y.pres = [][][]int{len: y.nnonter + 1}
+ mut curres := [][]int{len: y.nprod}
+
+ if false {
+ for j := 0; j <= y.nnonter; j++ {
+ print('y.nnonter[${j}] = ${y.nontrst[j].name}\n')
+ }
+ for j := 0; j < y.nprod; j++ {
+ print('y.prdptr[${j}][0] = ${y.prdptr[j][0] - ntbase}+ntbase\n')
+ }
+ }
+
+ y.fatfl = 0 // make undefined symbols nonfatal
+ for i := 0; i <= y.nnonter; i++ {
+ mut n := 0
+ c := i + ntbase
+ for j := 0; j < y.nprod; j++ {
+ if y.prdptr[j][0] == c {
+ curres[n] = y.prdptr[j][1..]
+ n++
+ }
+ }
+ if n == 0 {
+ y.errorf('nonterminal ${y.nontrst[i].name} not defined')
+ continue
+ }
+ y.pres[i] = [][]int{len: n}
+ gocopy(mut y.pres[i], curres)
+ }
+ y.fatfl = 1
+ if y.nerrors != 0 {
+ y.summary()
+ exit_(1)
+ }
+}
+
+// mark nonterminals which derive the empty string
+// also, look for nonterminals which don't derive any token strings
+fn (mut y Vyacc) cempty() {
+ mut i := 0
+ mut p := 0
+ mut np := 0
+ mut prd := []int{}
+
+ y.pempty = []int{len: y.nnonter + 1}
+
+ // first, use the array y.pempty to detect productions that can never be reduced
+ // set y.pempty to WHONOWS
+ aryfil(mut y.pempty, y.nnonter + 1, whoknows)
+
+ // now, look at productions, marking nonterminals which derive something
+
+ more: for {
+ for i = 0; i < y.nprod; i++ {
+ prd = y.prdptr[i]
+ if y.pempty[prd[0] - ntbase] != 0 {
+ continue
+ }
+ np = prd.len - 1
+ for p = 1; p < np; p++ {
+ if prd[p] >= ntbase && y.pempty[prd[p] - ntbase] == whoknows {
+ break
+ }
+ }
+ // production can be derived
+ if p == np {
+ y.pempty[prd[0] - ntbase] = ok
+ continue more
+ }
+ }
+ break
+ }
+
+ // now, look at the nonterminals, to see if they are all ok
+ for i = 0; i <= y.nnonter; i++ {
+ // the added production rises or falls as the start symbol ...
+ if i == 0 {
+ continue
+ }
+ if y.pempty[i] != ok {
+ y.fatfl = 0
+ y.errorf('nonterminal ${y.nontrst[i].name} never derives any token string')
+ }
+ }
+
+ if y.nerrors != 0 {
+ y.summary()
+ exit_(1)
+ }
+
+ // now, compute the y.pempty array, to see which nonterminals derive the empty string
+ // set y.pempty to whoknows
+ aryfil(mut y.pempty, y.nnonter + 1, whoknows)
+
+ // loop as long as we keep finding empty nonterminals
+
+ again: for {
+ next: for i = 1; i < y.nprod; i++ {
+ // not known to be empty
+ prd = y.prdptr[i]
+ if y.pempty[prd[0] - ntbase] != whoknows {
+ continue
+ }
+ np = prd.len - 1
+ for p = 1; p < np; p++ {
+ if prd[p] < ntbase || y.pempty[prd[p] - ntbase] != empty {
+ continue next
+ }
+ }
+
+ // we have a nontrivially empty nonterminal
+ y.pempty[prd[0] - ntbase] = empty
+
+ // got one ... try for another
+ continue again
+ }
+ return
+ }
+}
+
+// compute an array with the first of nonterminals
+fn (mut y Vyacc) cpfir() ! {
+ mut s := 0
+ mut n := 0
+ mut p := 0
+ mut np := 0
+ mut ch := 0
+ mut i := 0
+ mut curres := [][]int{}
+ mut prd := []int{}
+
+ y.wsets = []Wset{len: y.nnonter + wsetinc}
+ y.pfirst = []Lkset{len: y.nnonter + 1}
+ for i = 0; i <= y.nnonter; i++ {
+ y.wsets[i].ws = y.mkset()
+ y.pfirst[i] = y.mkset()
+ curres = y.pres[i]
+ n = curres.len
+
+ // initially fill the sets
+ for s = 0; s < n; s++ {
+ prd = curres[s]
+ np = prd.len - 1
+ for p = 0; p < np; p++ {
+ ch = prd[p]
+ if ch < ntbase {
+ setbit(mut y.pfirst[i], ch)
+ break
+ }
+ if y.pempty[ch - ntbase] == 0 {
+ break
+ }
+ }
+ }
+ }
+
+ // now, reflect transitivity
+ mut changes := 1
+ for changes != 0 {
+ changes = 0
+ for i = 0; i <= y.nnonter; i++ {
+ curres = y.pres[i]
+ n = curres.len
+ for s = 0; s < n; s++ {
+ prd = curres[s]
+ np = prd.len - 1
+ for p = 0; p < np; p++ {
+ ch = prd[p] - ntbase
+ if ch < 0 {
+ break
+ }
+ changes |= y.setunion(mut y.pfirst[i], y.pfirst[ch])
+ if y.pempty[ch] == 0 {
+ break
+ }
+ }
+ }
+ }
+ }
+
+ if y.indebug == 0 {
+ return
+ }
+ if y.foutput != none {
+ for i = 0; i <= y.nnonter; i++ {
+ y.foutput.write_string('\n${y.nontrst[i].name}: ${y.pfirst[i]} ${y.pempty[i]}\n')!
+ }
+ }
+}
+
+// generate the states
+fn (mut y Vyacc) stagen() ! {
+ // initialize
+ y.nstate = 0
+ y.tstates = []int{len: y.ntokens + 1} // states generated by terminal gotos
+ y.ntstates = []int{len: y.nnonter + 1} // states generated by nonterminal gotos
+ y.amem = []int{len: actsize}
+ y.memp = 0
+
+ y.clset = y.mkset()
+ y.pstate[0] = 0
+ y.pstate[1] = 0
+ aryfil(mut y.clset, y.tbitset, 0)
+ mut item := Pitem{y.prdptr[0], 0, 0, 0}
+ y.putitem(item, y.clset)!
+ y.tystate[0] = mustdo
+ y.nstate = 1
+ y.pstate[2] = y.pstate[1]
+
+ //
+ // now, the main state generation loop
+ // first pass generates all of the states
+ // later passes fix up lookahead
+ // could be sped up a lot by remembering
+ // results of the first pass rather than recomputing
+ //
+ mut first := 1
+ for more := 1; more != 0; first = 0 {
+ more = 0
+ for i := 0; i < y.nstate; i++ {
+ if y.tystate[i] != mustdo {
+ continue
+ }
+
+ y.tystate[i] = done
+ aryfil(mut y.temp1, y.nnonter + 1, 0)
+
+ // take state i, close it, and do gotos
+ y.closure(i)!
+
+ // generate goto's
+ for p := 0; p < y.cwp; p++ {
+ pi := y.wsets[p]
+ if pi.flag != 0 {
+ continue
+ }
+ y.wsets[p].flag = 1
+ c := pi.pitem.first
+ if c <= 1 {
+ if y.pstate[i + 1] - y.pstate[i] <= p {
+ y.tystate[i] = mustlookahead
+ }
+ continue
+ }
+
+ // do a goto on c
+ y.putitem(y.wsets[p].pitem, y.wsets[p].ws)!
+ for q := p + 1; q < y.cwp; q++ {
+ // this item contributes to the goto
+ if c == y.wsets[q].pitem.first {
+ y.putitem(y.wsets[q].pitem, y.wsets[q].ws)!
+ y.wsets[q].flag = 1
+ }
+ }
+
+ if c < ntbase {
+ y.state(c) // register new state
+ } else {
+ y.temp1[c - ntbase] = y.state(c)
+ }
+ }
+
+ if y.gsdebug != 0 && y.foutput != none {
+ y.foutput.write_string('${i}: ')!
+ for j := 0; j <= y.nnonter; j++ {
+ if y.temp1[j] != 0 {
+ y.foutput.write_string('${y.nontrst[j].name} ${y.temp1[j]},')!
+ }
+ }
+ y.foutput.write_string('\n')!
+ }
+
+ if first != 0 {
+ y.indgo[i] = y.apack(mut y.temp1[1..], y.nnonter - 1)! - 1
+ }
+
+ more++
+ }
+ }
+}
+
+// generate the closure of state i
+fn (mut y Vyacc) closure(i int) ! {
+ y.zzclose++
+
+ // first, copy kernel of state i to y.wsets
+ y.cwp = 0
+ q := y.pstate[i + 1]
+ for p := y.pstate[i]; p < q; p++ {
+ y.wsets[y.cwp].pitem = y.statemem[p].pitem
+ y.wsets[y.cwp].flag = 1 // this item must get closed
+ gocopy(mut y.wsets[y.cwp].ws, y.statemem[p].look)
+ y.cwp++
+ }
+
+ // now, go through the loop, closing each item
+ mut work := 1
+ for work != 0 {
+ work = 0
+ for u := 0; u < y.cwp; u++ {
+ if y.wsets[u].flag == 0 {
+ continue
+ }
+
+ // dot is before c
+ c := y.wsets[u].pitem.first
+ if c < ntbase {
+ y.wsets[u].flag = 0
+ // only interesting case is where . is before nonterminal
+ continue
+ }
+
+ // compute the lookahead
+ aryfil(mut y.clset, y.tbitset, 0)
+
+ // find items involving c
+ for v := u; v < y.cwp; v++ {
+ if y.wsets[v].flag != 1 || y.wsets[v].pitem.first != c {
+ continue
+ }
+ pi := y.wsets[v].pitem.prod
+ mut ipi := y.wsets[v].pitem.off + 1
+
+ y.wsets[v].flag = 0
+ if y.nolook != 0 {
+ continue
+ }
+
+ mut ch := pi[ipi]
+ ipi++
+ for ch > 0 {
+ // terminal symbol
+ if ch < ntbase {
+ setbit(mut y.clset, ch)
+ break
+ }
+
+ // nonterminal symbol
+ y.setunion(mut y.clset, y.pfirst[ch - ntbase])
+ if y.pempty[ch - ntbase] == 0 {
+ break
+ }
+ ch = pi[ipi]
+ ipi++
+ }
+ if ch <= 0 {
+ y.setunion(mut y.clset, y.wsets[v].ws)
+ }
+ }
+
+ //
+ // now loop over productions derived from c
+ //
+ curres := y.pres[c - ntbase]
+ n := curres.len
+
+ nexts:
+ // initially fill the sets
+ for s := 0; s < n; s++ {
+ prd := curres[s]
+
+ //
+ // put these items into the closure
+ // is the item there
+ //
+ for v := 0; v < y.cwp; v++ {
+ // yes, it is there
+ if y.wsets[v].pitem.off == 0 && aryeq(y.wsets[v].pitem.prod, prd) != 0 {
+ if y.nolook == 0 && y.setunion(mut y.wsets[v].ws, y.clset) != 0 {
+ y.wsets[v].flag = 1
+ work = 1
+ }
+ continue nexts
+ }
+ }
+
+ // not there; make a new entry
+ if y.cwp >= y.wsets.len {
+ mut awsets := []Wset{len: y.cwp + wsetinc}
+ gocopy(mut awsets, y.wsets)
+ y.wsets = awsets
+ }
+ y.wsets[y.cwp].pitem = Pitem{prd, 0, prd[0], -prd[prd.len - 1]}
+ y.wsets[y.cwp].flag = 1
+ y.wsets[y.cwp].ws = y.mkset()
+ if y.nolook == 0 {
+ work = 1
+ gocopy(mut y.wsets[y.cwp].ws, y.clset)
+ }
+ y.cwp++
+ }
+ }
+ }
+
+ // have computed closure; flags are reset; return
+ if y.cldebug != 0 && y.foutput != none {
+ y.foutput.write_string('\nState ${i}, nolook = ${y.nolook}\n')!
+ for u := 0; u < y.cwp; u++ {
+ if y.wsets[u].flag != 0 {
+ y.foutput.write_string('flag set\n')!
+ }
+ y.wsets[u].flag = 0
+ y.foutput.write_string('\t${y.writem(y.wsets[u].pitem)}')!
+ y.prlook(y.wsets[u].ws)!
+ y.foutput.write_string('\n')!
+ }
+ }
+}
+
+// sorts last state,and sees if it equals earlier ones. returns state number
+fn (mut y Vyacc) state(c int) int {
+ y.zzstate++
+ p1 := y.pstate[y.nstate]
+ p2 := y.pstate[y.nstate + 1]
+ if p1 == p2 {
+ return 0 // null state
+ }
+
+ // sort the items
+ mut k := 0
+ mut l := 0
+ for k = p1 + 1; k < p2; k++ { // make k the biggest
+ for l = k; l > p1; l-- {
+ if y.statemem[l].pitem.prodno < y.statemem[l - 1].pitem.prodno
+ || (y.statemem[l].pitem.prodno == y.statemem[l - 1].pitem.prodno
+ && y.statemem[l].pitem.off < y.statemem[l - 1].pitem.off) {
+ s := y.statemem[l]
+ y.statemem[l] = y.statemem[l - 1]
+ y.statemem[l - 1] = s
+ } else {
+ break
+ }
+ }
+ }
+
+ size1 := p2 - p1 // size of state
+
+ mut i := 0
+ if c >= ntbase {
+ i = y.ntstates[c - ntbase]
+ } else {
+ i = y.tstates[c]
+ }
+
+ look: for ; i != 0; i = y.mstates[i] {
+ // get ith state
+ q1 := y.pstate[i]
+ q2 := y.pstate[i + 1]
+ size2 := q2 - q1
+ if size1 != size2 {
+ continue
+ }
+ k = p1
+ for l = q1; l < q2; l++ {
+ if aryeq(y.statemem[l].pitem.prod, y.statemem[k].pitem.prod) == 0
+ || y.statemem[l].pitem.off != y.statemem[k].pitem.off {
+ continue look
+ }
+ k++
+ }
+
+ // found it
+ y.pstate[y.nstate + 1] = y.pstate[y.nstate] // delete last state
+
+ // fix up lookaheads
+ if y.nolook != 0 {
+ return i
+ }
+ k = p1
+ for l = q1; l < q2; l++ {
+ if y.setunion(mut y.statemem[l].look, y.statemem[k].look) != 0 {
+ y.tystate[i] = mustdo
+ }
+ k++
+ }
+ return i
+ }
+
+ // state is new
+ y.zznewstate++
+ if y.nolook != 0 {
+ y.errorf('yacc state/y.nolook error')
+ }
+ y.pstate[y.nstate + 2] = p2
+ if y.nstate + 1 >= nstates {
+ y.errorf('too many states')
+ }
+ if c >= ntbase {
+ y.mstates[y.nstate] = y.ntstates[c - ntbase]
+ y.ntstates[c - ntbase] = y.nstate
+ } else {
+ y.mstates[y.nstate] = y.tstates[c]
+ y.tstates[c] = y.nstate
+ }
+ y.tystate[y.nstate] = mustdo
+ y.nstate++
+ return y.nstate - 1
+}
+
+fn (mut y Vyacc) putitem(p_ Pitem, set Lkset) ! {
+ mut p := p_
+ p.off++
+ p.first = p.prod[p.off]
+
+ if y.pidebug != 0 && y.foutput != none {
+ y.foutput.write_string('putitem(${y.writem(p)}), state ${y.nstate}\n')!
+ }
+ mut j := y.pstate[y.nstate + 1]
+ if j >= y.statemem.len {
+ mut asmm := []Item{len: j + stateinc}
+ gocopy(mut asmm, y.statemem)
+ y.statemem = asmm
+ }
+ y.statemem[j].pitem = p
+ if y.nolook == 0 {
+ mut s := y.mkset()
+ gocopy(mut s, set)
+ y.statemem[j].look = s
+ }
+ j++
+ y.pstate[y.nstate + 1] = j
+}
+
+// creates output string for item pointed to by pp
+fn (mut y Vyacc) writem(pp Pitem) string {
+ mut i := 0
+
+ mut p := pp.prod.clone()
+ mut q := chcopy(y.nontrst[y.prdptr[pp.prodno][0] - ntbase].name) + ': '
+ mut npi := pp.off
+
+ mut pi := aryeq(p, y.prdptr[pp.prodno])
+
+ for {
+ mut c := ` `
+ if pi == npi {
+ c = `.`
+ }
+ q += c.str()
+
+ i = p[pi]
+ pi++
+ if i <= 0 {
+ break
+ }
+ q += chcopy(y.symnam(i))
+ }
+
+ // an item calling for a reduction
+ i = p[npi]
+ if i < 0 {
+ q += ' (${-i})'
+ }
+
+ return q
+}
+
+// pack state i from y.temp1 into y.amem
+fn (mut y Vyacc) apack(mut p []int, n_ int) !int {
+ mut n := n_
+ //
+ // we don't need to worry about checking because
+ // we will only look at entries known to be there...
+ // eliminate leading and trailing 0's
+ //
+ mut off := 0
+ mut pp := 0
+ for ; pp <= n && p[pp] == 0; pp++ {
+ off--
+ }
+
+ // no actions
+ if pp > n {
+ return 0
+ }
+ for ; n > pp && p[n] == 0; n-- {
+ }
+ p = p.clone()[pp..n + 1]
+
+ // now, find a place for the elements from p to q, inclusive
+ r := y.amem.len - p.len
+
+ nextk: for rr := 0; rr <= r; rr++ {
+ mut qq := rr
+ for pp = 0; pp < p.len; pp++ {
+ if p[pp] != 0 {
+ if p[pp] != y.amem[qq] && y.amem[qq] != 0 {
+ continue nextk
+ }
+ }
+ qq++
+ }
+
+ // we have found an acceptable k
+ if y.pkdebug != 0 && y.foutput != none {
+ y.foutput.write_string('off = ${off + rr}, k = ${rr}\n')!
+ }
+ qq = rr
+ for pp = 0; pp < p.len; pp++ {
+ if p[pp] != 0 {
+ if qq > y.memp {
+ y.memp = qq
+ }
+ y.amem[qq] = p[pp]
+ }
+ qq++
+ }
+ if y.pkdebug != 0 && y.foutput != none {
+ for pp = 0; pp <= y.memp; pp += 10 {
+ y.foutput.write_string('\n')!
+ for qq = pp; qq <= pp + 9; qq++ {
+ y.foutput.write_string('${y.amem[qq]} ')!
+ }
+ y.foutput.write_string('\n')!
+ }
+ }
+ return off + rr
+ }
+ y.errorf('no space in action table')
+ return 0
+}
+
+// print the output for the states
+fn (mut y Vyacc) output() ! {
+ mut c := 0
+ mut u := 0
+ mut v := 0
+
+ if !y.lflag {
+ y.ftable.write_string('\n//line yacctab:1')!
+ }
+ mut actions := []int{}
+
+ if y.errors.len > 0 {
+ y.state_table = []Row{len: y.nstate}
+ }
+
+ noset := y.mkset()
+
+ // output the stuff for state i
+ for i := 0; i < y.nstate; i++ {
+ y.nolook = 0
+ if y.tystate[i] != mustlookahead {
+ y.nolook = 1
+ }
+ y.closure(i)!
+
+ // output actions
+ y.nolook = 1
+ aryfil(mut y.temp1, y.ntokens + y.nnonter + 1, 0)
+ for u = 0; u < y.cwp; u++ {
+ c = y.wsets[u].pitem.first
+ if c > 1 && c < ntbase && y.temp1[c] == 0 {
+ for v = u; v < y.cwp; v++ {
+ if c == y.wsets[v].pitem.first {
+ y.putitem(y.wsets[v].pitem, noset)!
+ }
+ }
+ y.temp1[c] = y.state(c)
+ } else if c > ntbase {
+ c -= ntbase
+ if y.temp1[c + y.ntokens] == 0 {
+ y.temp1[c + y.ntokens] = y.amem[y.indgo[i] + c]
+ }
+ }
+ }
+ if i == 1 {
+ y.temp1[1] = acceptcode
+ }
+
+ // now, we have the shifts; look at the reductions
+ y.lastred = 0
+ for u = 0; u < y.cwp; u++ {
+ c = y.wsets[u].pitem.first
+
+ // reduction
+ if c > 0 {
+ continue
+ }
+ y.lastred = -c
+ us := y.wsets[u].ws
+ for k := 0; k <= y.ntokens; k++ {
+ if bitset(us, k) == 0 {
+ continue
+ }
+ if y.temp1[k] == 0 {
+ y.temp1[k] = c
+ } else if y.temp1[k] < 0 { // reduce/reduce conflict
+ if y.foutput != none {
+ y.foutput.write_string("\n ${i}: reduce/reduce conflict (red'ns " +
+ '${-y.temp1[k]} and ${y.lastred}) on ${y.symnam(k)}')!
+ }
+ if -y.temp1[k] > y.lastred {
+ y.temp1[k] = -y.lastred
+ }
+ y.zzrrconf++
+ } else {
+ // potential shift/reduce conflict
+ y.precftn(y.lastred, k, i)!
+ }
+ }
+ }
+ actions = y.add_actions(mut actions, i)!
+ }
+
+ y.array_out_columns('_exca', actions, 2, false)!
+ y.ftable.write_string('\n')!
+ y.ftable.write_string('\n')!
+ y.ftable.write_string('const ${y.prefix}_private = ${private}\n')!
+}
+
+// decide a shift/reduce conflict by precedence.
+// r is a rule number, t a token number
+// the conflict is in state s
+// y.temp1[t] is changed to reflect the action
+fn (mut y Vyacc) precftn(r int, t int, s int) ! {
+ mut action := noasc
+
+ mut lp := y.levprd[r]
+ mut lt := y.toklev[t]
+ if plevel(lt) == 0 || plevel(lp) == 0 {
+ // conflict
+ if y.foutput != none {
+ y.foutput.write_string("\n${s}: shift/reduce conflict (shift ${y.temp1[t]}(${plevel(lt)}), red'n ${r}(${plevel(lp)})) on ${y.symnam(t)}")!
+ }
+ y.zzsrconf++
+ return
+ }
+ if plevel(lt) == plevel(lp) {
+ action = assoc(lt)
+ } else if plevel(lt) > plevel(lp) {
+ action = rasc // shift
+ } else {
+ action = lasc
+ } // reduce
+ match action {
+ basc { // error action
+ y.temp1[t] = errcode
+ }
+ lasc { // reduce
+ y.temp1[t] = -r
+ }
+ else {}
+ }
+}
+
+// output state i
+// y.temp1 has the actions, y.lastred the default
+fn (mut y Vyacc) add_actions(mut act []int, i int) ![]int {
+ mut p := 0
+ mut p1 := 0
+
+ // find the best choice for y.lastred
+ y.lastred = 0
+ mut ntimes := 0
+ for j := 0; j <= y.ntokens; j++ {
+ if y.temp1[j] >= 0 {
+ continue
+ }
+ if y.temp1[j] + y.lastred == 0 {
+ continue
+ }
+ // count the number of appearances of y.temp1[j]
+ mut count := 0
+ tred := -y.temp1[j]
+ y.levprd[tred] |= redflag
+ for p = 0; p <= y.ntokens; p++ {
+ if y.temp1[p] + tred == 0 {
+ count++
+ }
+ }
+ if count > ntimes {
+ y.lastred = tred
+ ntimes = count
+ }
+ }
+
+ //
+ // for error recovery, arrange that, if there is a shift on the
+ // error recovery token, `error', that the default be the error action
+ //
+ if y.temp1[2] > 0 {
+ y.lastred = 0
+ }
+
+ // clear out entries in y.temp1 which equal y.lastred
+ // count entries in y.optst table
+ mut n := 0
+ for p = 0; p <= y.ntokens; p++ {
+ p1 = y.temp1[p]
+ if p1 + y.lastred == 0 {
+ y.temp1[p] = 0
+ p1 = 0
+ }
+ if p1 > 0 && p1 != acceptcode && p1 != errcode {
+ n++
+ }
+ }
+
+ y.wrstate(i)!
+ y.defact[i] = y.lastred
+ mut f := 0
+ mut os_ := []int{len: n * 2}
+ n = 0
+ for p = 0; p <= y.ntokens; p++ {
+ p1 = y.temp1[p]
+ if p1 != 0 {
+ if p1 < 0 {
+ p1 = -p1
+ } else if p1 == acceptcode {
+ p1 = -1
+ } else if p1 == errcode {
+ p1 = 0
+ } else {
+ os_[n] = p
+ n++
+ os_[n] = p1
+ n++
+ y.zzacent++
+ continue
+ }
+ if f == 0 {
+ act << -1
+ act << i
+ }
+ f++
+ act << p
+ act << p1
+ y.zzexcp++
+ }
+ }
+
+ if f != 0 {
+ y.defact[i] = -2
+ act << -2
+ act << y.lastred
+ }
+ y.optst[i] = os_
+
+ return act
+}
+
+// writes state i
+fn (mut y Vyacc) wrstate(i int) ! {
+ mut j0 := 0
+ mut j1 := 0
+ mut u := 0
+ mut pp := 0
+ mut qq := 0
+
+ if y.errors.len > 0 {
+ actions := y.temp1.clone()
+ mut default_action := errcode
+ if y.lastred != 0 {
+ default_action = -y.lastred
+ }
+ y.state_table[i] = Row{actions, default_action}
+ }
+
+ if y.foutput != none {
+ y.foutput.write_string('\nstate ${i}\n')!
+ qq = y.pstate[i + 1]
+ for pp = y.pstate[i]; pp < qq; pp++ {
+ y.foutput.write_string('\t${y.writem(y.statemem[pp].pitem)}\n')!
+ }
+ if y.tystate[i] == mustlookahead {
+ // print out empty productions in closure
+ for u = y.pstate[i + 1] - y.pstate[i]; u < y.cwp; u++ {
+ if y.wsets[u].pitem.first < 0 {
+ y.foutput.write_string('\t${y.writem(y.wsets[u].pitem)}\n')!
+ }
+ }
+ }
+
+ // check for state equal to another
+ for j0 = 0; j0 <= y.ntokens; j0++ {
+ j1 = y.temp1[j0]
+ if j1 != 0 {
+ y.foutput.write_string('\n\t${y.symnam(j0)} ')!
+
+ // shift, error, or accept
+ if j1 > 0 {
+ if j1 == acceptcode {
+ y.foutput.write_string('accept')!
+ } else if j1 == errcode {
+ y.foutput.write_string('error')!
+ } else {
+ y.foutput.write_string('shift ${j1}')!
+ }
+ } else {
+ y.foutput.write_string('reduce ${-j1} (src line ${y.rlines[-j1]})')!
+ }
+ }
+ }
+
+ // output the final production
+ if y.lastred != 0 {
+ y.foutput.write_string('\n\t. reduce ${y.lastred} (src line ${y.rlines[y.lastred]})\n\n')!
+ } else {
+ y.foutput.write_string('\n\t. error\n\n')!
+ }
+
+ // now, output nonterminal actions
+ j1 = y.ntokens
+ for j0 = 1; j0 <= y.nnonter; j0++ {
+ j1++
+ if y.temp1[j1] != 0 {
+ y.foutput.write_string('\t${y.symnam(j0 + ntbase)} goto ${y.temp1[j1]}\n')!
+ }
+ }
+ }
+}
+
+// output the gotos for the nontermninals
+fn (mut y Vyacc) go2out() ! {
+ for i := 1; i <= y.nnonter; i++ {
+ y.go2gen(i)!
+
+ // find the best one to make default
+ mut best := -1
+ mut times := 0
+
+ // is j the most frequent
+ for j := 0; j < y.nstate; j++ {
+ if y.tystate[j] == 0 {
+ continue
+ }
+ if y.tystate[j] == best {
+ continue
+ }
+
+ // is y.tystate[j] the most frequent
+ mut count := 0
+ cbest := y.tystate[j]
+ for k := j; k < y.nstate; k++ {
+ if y.tystate[k] == cbest {
+ count++
+ }
+ }
+ if count > times {
+ best = cbest
+ times = count
+ }
+ }
+
+ // best is now the default entry
+ y.zzgobest += times - 1
+ mut n := 0
+ for j := 0; j < y.nstate; j++ {
+ if y.tystate[j] != 0 && y.tystate[j] != best {
+ n++
+ }
+ }
+ mut goent := []int{len: 2 * n + 1}
+ n = 0
+ for j := 0; j < y.nstate; j++ {
+ if y.tystate[j] != 0 && y.tystate[j] != best {
+ goent[n] = j
+ n++
+ goent[n] = y.tystate[j]
+ n++
+ y.zzgoent++
+ }
+ }
+
+ // now, the default
+ if best == -1 {
+ best = 0
+ }
+
+ y.zzgoent++
+ goent[n] = best
+ y.yypgo[i] = goent
+ }
+}
+
+// output the gotos for nonterminal c
+fn (mut y Vyacc) go2gen(c int) ! {
+ mut i := 0
+ mut cc := 0
+ mut p := 0
+ mut q := 0
+
+ // first, find nonterminals with gotos on c
+ aryfil(mut y.temp1, y.nnonter + 1, 0)
+ y.temp1[c] = 1
+ mut work := 1
+ for work != 0 {
+ work = 0
+ for i = 0; i < y.nprod; i++ {
+ // cc is a nonterminal with a goto on c
+ cc = y.prdptr[i][1] - ntbase
+ if cc >= 0 && y.temp1[cc] != 0 {
+ // thus, the left side of production i does too
+ cc = y.prdptr[i][0] - ntbase
+ if y.temp1[cc] == 0 {
+ work = 1
+ y.temp1[cc] = 1
+ }
+ }
+ }
+ }
+
+ // now, we have y.temp1[c] = 1 if a goto on c in closure of cc
+ if y.g2debug != 0 && y.foutput != none {
+ y.foutput.write_string('${y.nontrst[c].name}: gotos on ')!
+ for i = 0; i <= y.nnonter; i++ {
+ if y.temp1[i] != 0 {
+ y.foutput.write_string('${y.nontrst[i].name} ')!
+ }
+ }
+ y.foutput.write_string('\n')!
+ }
+
+ // now, go through and put gotos into y.tystate
+ aryfil(mut y.tystate, y.nstate, 0)
+ for i = 0; i < y.nstate; i++ {
+ q = y.pstate[i + 1]
+ for p = y.pstate[i]; p < q; p++ {
+ cc = y.statemem[p].pitem.first
+ if cc >= ntbase {
+ // goto on c is possible
+ if y.temp1[cc - ntbase] != 0 {
+ y.tystate[i] = y.amem[y.indgo[i] + c]
+ break
+ }
+ }
+ }
+ }
+}
+
+// in order to free up the mem and y.amem arrays for the optimizer,
+// and still be able to output yyr1, etc., after the sizes of
+// the action array is known, we hide the nonterminals
+// derived by productions in y.levprd.
+fn (mut y Vyacc) hideprod() ! {
+ mut nred := 0
+ y.levprd[0] = 0
+ for i := 1; i < y.nprod; i++ {
+ if (y.levprd[i] & redflag) == 0 {
+ if y.foutput != none {
+ y.foutput.write_string('Rule not reduced: ${y.writem(Pitem{y.prdptr[i], 0, 0, i})}\n')!
+ }
+ print('rule ${y.writem(Pitem{y.prdptr[i], 0, 0, i})} never reduced\n')
+ nred++
+ }
+ y.levprd[i] = y.prdptr[i][0] - ntbase
+ }
+ if nred != 0 {
+ print('${nred} rules never reduced\n')
+ }
+}
+
+fn (mut y Vyacc) callopt() ! {
+ mut j := 0
+ mut k := 0
+ mut p := 0
+ mut q := 0
+ mut i := 0
+ mut v := []int{}
+
+ y.pgo = []int{len: y.nnonter + 1}
+ y.pgo[0] = 0
+ y.maxoff = 0
+ y.maxspr = 0
+ for i = 0; i < y.nstate; i++ {
+ k = 32000
+ j = 0
+ v = y.optst[i]
+ q = v.len
+ for p = 0; p < q; p += 2 {
+ if v[p] > j {
+ j = v[p]
+ }
+ if v[p] < k {
+ k = v[p]
+ }
+ }
+
+ // nontrivial situation
+ if k <= j {
+ // j is now the range
+ // j -= k; // call scj
+ if k > y.maxoff {
+ y.maxoff = k
+ }
+ }
+ y.tystate[i] = q + 2 * j
+ if j > y.maxspr {
+ y.maxspr = j
+ }
+ }
+
+ // initialize y.ggreed table
+ y.ggreed = []int{len: y.nnonter + 1}
+ for i = 1; i <= y.nnonter; i++ {
+ y.ggreed[i] = 1
+ j = 0
+
+ // minimum entry index is always 0
+ v = y.yypgo[i]
+ q = v.len - 1
+ for p = 0; p < q; p += 2 {
+ y.ggreed[i] += 2
+ if v[p] > j {
+ j = v[p]
+ }
+ }
+ y.ggreed[i] = y.ggreed[i] + 2 * j
+ if j > y.maxoff {
+ y.maxoff = j
+ }
+ }
+
+ // now, prepare to put the shift actions into the y.amem array
+ for i = 0; i < actsize; i++ {
+ y.amem[i] = 0
+ }
+ y.maxa = 0
+ for i = 0; i < y.nstate; i++ {
+ if y.tystate[i] == 0 && y.adb > 1 {
+ y.ftable.write_string('State ${i}: null\n')!
+ }
+ y.indgo[i] = yy_flag
+ }
+
+ i = y.nxti()
+ for i != nomore {
+ if i >= 0 {
+ y.stin(i)!
+ } else {
+ y.gin(-i)!
+ }
+ i = y.nxti()
+ }
+
+ // print y.amem array
+ if y.adb > 2 {
+ for p = 0; p <= y.maxa; p += 10 {
+ y.ftable.write_string('${p} ')!
+ for i = 0; i < 10; i++ {
+ y.ftable.write_string('${y.amem[p + i]} ')!
+ }
+ y.ftable.write_string('\n')!
+ }
+ }
+
+ y.aoutput()!
+ y.osummary()!
+}
+
+// finds the next i
+fn (mut y Vyacc) nxti() int {
+ mut max := 0
+ mut maxi := 0
+ for i := 1; i <= y.nnonter; i++ {
+ if y.ggreed[i] >= max {
+ max = y.ggreed[i]
+ maxi = -i
+ }
+ }
+ for i := 0; i < y.nstate; i++ {
+ if y.tystate[i] >= max {
+ max = y.tystate[i]
+ maxi = i
+ }
+ }
+ if max == 0 {
+ return nomore
+ }
+ return maxi
+}
+
+fn (mut y Vyacc) gin(i int) ! {
+ mut s := 0
+
+ // enter gotos on nonterminal i into array y.amem
+ y.ggreed[i] = 0
+
+ q := y.yypgo[i]
+ nq := q.len - 1
+
+ // now, find y.amem place for it
+
+ nextgp: for p := 0; p < actsize; p++ {
+ if y.amem[p] != 0 {
+ continue
+ }
+ for r := 0; r < nq; r += 2 {
+ s = p + q[r] + 1
+ if s > y.maxa {
+ y.maxa = s
+ if y.maxa >= actsize {
+ y.errorf('a array overflow')
+ }
+ }
+ if y.amem[s] != 0 {
+ continue nextgp
+ }
+ }
+
+ // we have found y.amem spot
+ y.amem[p] = q[nq]
+ if p > y.maxa {
+ y.maxa = p
+ }
+ for r := 0; r < nq; r += 2 {
+ s = p + q[r] + 1
+ y.amem[s] = q[r + 1]
+ }
+ y.pgo[i] = p
+ if y.adb > 1 {
+ y.ftable.write_string('Nonterminal ${i}, entry at ${y.pgo[i]}\n')!
+ }
+ return
+ }
+ y.errorf('cannot place goto ${i}\n')
+}
+
+fn (mut y Vyacc) stin(i int) ! {
+ mut s := 0
+
+ y.tystate[i] = 0
+
+ // enter state i into the y.amem array
+ q := y.optst[i]
+ nq := q.len
+
+ nextn:
+ // find an acceptable place
+ for n := -y.maxoff; n < actsize; n++ {
+ mut f := 0
+ for r := 0; r < nq; r += 2 {
+ s = q[r] + n
+ if s < 0 || s > actsize {
+ continue nextn
+ }
+ if y.amem[s] == 0 {
+ f++
+ } else if y.amem[s] != q[r + 1] {
+ continue nextn
+ }
+ }
+
+ // check the position equals another only if the states are identical
+ for j := 0; j < y.nstate; j++ {
+ if y.indgo[j] == n {
+ // we have some disagreement
+ if f != 0 {
+ continue nextn
+ }
+ if nq == y.optst[j].len {
+ // states are equal
+ y.indgo[i] = n
+ if y.adb > 1 {
+ y.ftable.write_string('State ${i}: entry at' + '${n} equals state ${j}\n')!
+ }
+ return
+ }
+
+ // we have some disagreement
+ continue nextn
+ }
+ }
+
+ for r := 0; r < nq; r += 2 {
+ s = q[r] + n
+ if s > y.maxa {
+ y.maxa = s
+ }
+ if y.amem[s] != 0 && y.amem[s] != q[r + 1] {
+ y.errorf("clobber of a array, pos'n ${s}, by ${q[r + 1]}")
+ }
+ y.amem[s] = q[r + 1]
+ }
+ y.indgo[i] = n
+ if y.adb > 1 {
+ y.ftable.write_string('State ${i}: entry at ${y.indgo[i]}\n')!
+ }
+ return
+ }
+ y.errorf('Error; failure to place state ${i}')
+}
+
+// this version is for limbo
+// write out the optimized parser
+fn (mut y Vyacc) aoutput() ! {
+ y.ftable.write_string('\n')!
+ y.ftable.write_string('const ${y.prefix}_last = ${y.maxa + 1}\n')!
+ y.arout('_act', y.amem, y.maxa + 1)!
+ y.arout('_pact', y.indgo, y.nstate)!
+ y.arout('_pgo', y.pgo, y.nnonter + 1)!
+}
+
+// put out other arrays, copy the parsers
+fn (mut y Vyacc) others() ! {
+ mut i := 0
+ mut j := 0
+
+ y.arout('_r1', y.levprd, y.nprod)!
+ aryfil(mut y.temp1, y.nprod, 0)
+
+ //
+ // yyr2 is the number of rules for each production
+ //
+ for i = 1; i < y.nprod; i++ {
+ y.temp1[i] = y.prdptr[i].len - 2
+ }
+ y.arout('_r2', y.temp1, y.nprod)!
+
+ aryfil(mut y.temp1, y.nstate, -1000)
+ for i = 0; i <= y.ntokens; i++ {
+ for j2 := y.tstates[i]; j2 != 0; j2 = y.mstates[j2] {
+ y.temp1[j2] = i
+ }
+ }
+ for i = 0; i <= y.nnonter; i++ {
+ for j = y.ntstates[i]; j != 0; j = y.mstates[j] {
+ y.temp1[j] = -i
+ }
+ }
+ y.arout('_chk', y.temp1, y.nstate)!
+ y.array_out_columns('_def', y.defact[..y.nstate], 10, false)!
+
+ // put out token translation tables
+ // table 1 has 0-256
+ aryfil(mut y.temp1, 256, 0)
+ mut c := 0
+ for i2 := 1; i2 <= y.ntokens; i2++ {
+ j = y.tokset[i2].value
+ if j >= 0 && j < 256 {
+ if y.temp1[j] != 0 {
+ print('yacc bug -- cannot have 2 different Ts with same value\n')
+ print(' ${y.tokset[i2].name} and ${y.tokset[y.temp1[j]].name}\n')
+ y.nerrors++
+ }
+ y.temp1[j] = i2
+ if j > c {
+ c = j
+ }
+ }
+ }
+ for i = 0; i <= c; i++ {
+ if y.temp1[i] == 0 {
+ y.temp1[i] = yylexunk
+ }
+ }
+ y.arout('_tok1', y.temp1, c + 1)!
+
+ // table 2 has private-private+256
+ aryfil(mut y.temp1, 256, 0)
+ c = 0
+ for i = 1; i <= y.ntokens; i++ {
+ j = y.tokset[i].value - private
+ if j >= 0 && j < 256 {
+ if y.temp1[j] != 0 {
+ print('yacc bug -- cannot have 2 different Ts with same value\n')
+ print(' ${y.tokset[i].name} and ${y.tokset[y.temp1[j]].name}\n')
+ y.nerrors++
+ }
+ y.temp1[j] = i
+ if j > c {
+ c = j
+ }
+ }
+ }
+ y.arout('_tok2', y.temp1, c + 1)!
+
+ // table 3 has everything else
+ y.ftable.write_string('\n')!
+ mut v := []int{}
+ for i = 1; i <= y.ntokens; i++ {
+ j = y.tokset[i].value
+ if j >= 0 && j < 256 {
+ continue
+ }
+ if j >= private && j < 256 + private {
+ continue
+ }
+
+ v << j
+ v << i
+ }
+ v << 0
+ y.arout('_tok3', v, v.len)!
+ y.ftable.write_string('\n')!
+
+ // Custom error messages.
+ y.ftable.write_string('\n')!
+ y.ftable.write_string('struct ErrorMessage {\n')!
+ y.ftable.write_string('\tstate int\n')!
+ y.ftable.write_string('\ttoken int\n')!
+ y.ftable.write_string('\tmsg string\n')!
+ y.ftable.write_string('}\n\n')!
+ if y.errors.len == 0 {
+ y.ftable.write_string('const ${y.prefix}_error_messages = []ErrorMessage{}\n')!
+ } else {
+ y.ftable.write_string('const ${y.prefix}_error_messages = [\n')!
+ for _, err in y.errors {
+ y.lineno = err.lineno
+ state, token := y.run_machine(err.tokens)
+ y.ftable.write_string('\tErrorMessage{${state}, ${token}, ${err.msg}},\n')!
+ }
+ y.ftable.write_string(']\n')!
+ }
+
+ // copy parser text
+ mut ch := y.getrune(mut y.finput)
+ for ch != eof {
+ y.ftable.write_string(ch.str())!
+ ch = y.getrune(mut y.finput)
+ }
+
+ // copy yaccpar
+ if !y.lflag {
+ y.ftable.write_string('\n//line yaccpar:1\n')!
+ }
+
+ parts := y.yaccpar.split_n(y.prefix + 'run()', 2)
+ y.ftable.write_string('${parts[0]}')!
+ y.ftable.write_string(y.fcode.str())!
+ y.ftable.write_string('${parts[1]}')!
+}
+
+fn (mut y Vyacc) run_machine(tokens []string) (int, int) {
+ mut state := 0
+ mut token := 0
+ mut stack := []int{}
+ mut i := 0
+ token = -1
+
+ Loop:
+ if token < 0 {
+ token = y.chfind(2, tokens[i])
+ i++
+ }
+
+ row := y.state_table[state]
+
+ mut c := token
+ if token >= ntbase {
+ c = token - ntbase + y.ntokens
+ }
+ mut action := row.actions[c]
+ if action == 0 {
+ action = row.default_action
+ }
+
+ match true {
+ action == acceptcode {
+ y.errorf('tokens are accepted')
+ return state, token
+ }
+ action == errcode {
+ if token >= ntbase {
+ y.errorf('error at non-terminal token ${y.symnam(token)}')
+ }
+ return state, token
+ }
+ action > 0 {
+ // Shift to state action.
+ stack << state
+ state = action
+ token = -1
+ unsafe {
+ goto Loop
+ }
+ }
+ else {
+ // Reduce by production -action.
+ prod := y.prdptr[-action]
+ rhs_len := prod.len - 2
+ if rhs_len > 0 {
+ n := stack.len - rhs_len
+ state = stack[n]
+ stack = stack.clone()[..n]
+ }
+ if token >= 0 {
+ i--
+ }
+ token = prod[0]
+ unsafe {
+ goto Loop
+ }
+ }
+ }
+
+ return state, token
+}
+
+fn min_max(v []int) (int, int) {
+ mut min := 0
+ mut max := 0
+ if v.len == 0 {
+ return min, max
+ }
+ min = v[0]
+ max = v[0]
+ for _, i in v {
+ if i < min {
+ min = i
+ }
+ if i > max {
+ max = i
+ }
+ }
+ return min, max
+}
+
+// return the smaller integral base type to store the values in v
+fn min_type(v []int, allow_unsigned bool) string {
+ mut typ := 'int'
+ mut type_len := 8
+ min, max := min_max(v)
+
+ if min >= min_int32 && max <= max_int32 && type_len > 4 {
+ typ = 'int32'
+ type_len = 4
+ }
+ if min >= min_int16 && max <= max_int16 && type_len > 2 {
+ typ = 'int16'
+ type_len = 2
+ }
+ if min >= min_int8 && max <= max_int8 && type_len > 1 {
+ typ = 'int8'
+ type_len = 1
+ }
+
+ if allow_unsigned {
+ // Do not check for uint32, not worth and won't compile on 32 bit systems
+
+ if min >= 0 && max <= max_uint16 && type_len > 2 {
+ typ = 'uint16'
+ type_len = 2
+ }
+ if min >= 0 && max <= max_uint8 && type_len > 1 {
+ typ = 'uint8'
+ type_len = 1
+ }
+ }
+ return typ
+}
+
+fn (mut y Vyacc) array_out_columns(s_ string, v []int, columns int, allow_unsigned bool) ! {
+ mut s := y.prefix + s_
+ y.ftable.write_string('\n')!
+ // min_typ := min_type(v, allow_unsigned)
+ y.ftable.write_string('const ${s} = [')!
+ for i, val in v {
+ if i % columns == 0 {
+ y.ftable.write_string('\n\t')!
+ } else {
+ y.ftable.write_string(' ')!
+ }
+ y.ftable.write_string('${val},')!
+ }
+ y.ftable.write_string('\n]\n')!
+}
+
+fn (mut y Vyacc) arout(s string, v []int, n int) ! {
+ y.array_out_columns(s, v[..n], 10, true)!
+}
+
+// output the summary on y.output
+fn (mut y Vyacc) summary() {
+ if y.foutput != none {
+ y.foutput.write_string('\n${y.ntokens} terminals, ${y.nnonter + 1} nonterminals\n') or {
+ panic(err)
+ }
+ y.foutput.write_string('${y.nprod} grammar rules, ${y.nstate}/${nstates} states\n') or {
+ panic(err)
+ }
+ y.foutput.write_string('${y.zzsrconf} shift/reduce, ${y.zzrrconf} reduce/reduce conflicts reported\n') or {
+ panic(err)
+ }
+ y.foutput.write_string('${y.wsets.len} working sets used\n') or { panic(err) }
+ y.foutput.write_string('memory: parser ${y.memp}/${actsize}\n') or { panic(err) }
+ y.foutput.write_string('${y.zzclose - 2 * y.nstate} extra closures\n') or { panic(err) }
+ y.foutput.write_string('${y.zzacent} shift entries, ${y.zzexcp} exceptions\n') or {
+ panic(err)
+ }
+ y.foutput.write_string('${y.zzgoent} goto entries\n') or { panic(err) }
+ y.foutput.write_string('${y.zzgobest} entries saved by goto default\n') or { panic(err) }
+ }
+ if y.zzsrconf != 0 || y.zzrrconf != 0 {
+ print('\nconflicts: ')
+ if y.zzsrconf != 0 {
+ print('${y.zzsrconf} shift/reduce')
+ }
+ if y.zzsrconf != 0 && y.zzrrconf != 0 {
+ print(', ')
+ }
+ if y.zzrrconf != 0 {
+ print('${y.zzrrconf} reduce/reduce')
+ }
+ print('\n')
+ }
+}
+
+// write optimizer summary
+fn (mut y Vyacc) osummary() ! {
+ if y.foutput != none {
+ mut i := 0
+ for p := y.maxa; p >= 0; p-- {
+ if y.amem[p] == 0 {
+ i++
+ }
+ }
+
+ y.foutput.write_string('Optimizer space used: output ${y.maxa + 1}/${actsize}\n')!
+ y.foutput.write_string('${y.maxa + 1} table entries, ${i} zero\n')!
+ y.foutput.write_string('maximum spread: ${y.maxspr}, maximum offset: ${y.maxoff}\n')!
+ }
+}
+
+// copies and protects "'s in q
+fn chcopy(q string) string {
+ mut s := ''
+ mut i := 0
+ mut j := 0
+ for i = 0; i < q.len; i++ {
+ if q[i] == `"` {
+ s += q[j..i] + '\\'
+ j = i
+ }
+ }
+ return s + q[j..i]
+}
+
+fn (mut y Vyacc) usage() {
+ y.stderr.write_string('usage: yacc [-o output] [-v parsetable] input\n') or { panic(err) }
+ exit_(1)
+}
+
+fn bitset(set Lkset, bit int) int {
+ return set[bit >> 5] & (1 << u32(bit & 31))
+}
+
+fn setbit(mut set Lkset, bit int) {
+ set[bit >> 5] |= (1 << u32(bit & 31))
+}
+
+fn (y Vyacc) mkset() Lkset {
+ return []int{len: y.tbitset}
+}
+
+// set a to the union of a and b
+// return 1 if b is not a subset of a, 0 otherwise
+fn (mut y Vyacc) setunion(mut a []int, b []int) int {
+ mut sub := 0
+ for i := 0; i < y.tbitset; i++ {
+ x := a[i]
+ y_ := x | b[i]
+ a[i] = y_
+ if y_ != x {
+ sub = 1
+ }
+ }
+ return sub
+}
+
+fn (mut y Vyacc) prlook(p Lkset) ! {
+ if y.foutput != none {
+ // if p == none {
+ // y.foutput.write_string("\tNULL")!
+ // return
+ // }
+ y.foutput.write_string(' { ')!
+ for j := 0; j <= y.ntokens; j++ {
+ if bitset(p, j) != 0 {
+ y.foutput.write_string('${y.symnam(j)} ')!
+ }
+ }
+ y.foutput.write_string('}')!
+ }
+}
+
+fn isdigit(c rune) bool {
+ return c >= `0` && c <= `9`
+}
+
+fn isword(c rune) bool {
+ return c >= 0xa0 || c == `_` || (c >= `a` && c <= `z`) || (c >= `A` && c <= `Z`)
+}
+
+// return 1 if 2 arrays are equal
+// return 0 if not equal
+fn aryeq(a []int, b []int) int {
+ n := a.len
+ if b.len != n {
+ return 0
+ }
+ for ll := 0; ll < n; ll++ {
+ if a[ll] != b[ll] {
+ return 0
+ }
+ }
+ return 1
+}
+
+fn (mut y Vyacc) getrune(mut f os.File) rune {
+ mut r := rune(0)
+
+ if y.peekrune != 0 {
+ if y.peekrune == eof {
+ return eof
+ }
+ r = y.peekrune
+ y.peekrune = 0
+ return r
+ }
+
+ if f.eof() {
+ return eof
+ }
+
+ mut buf := []u8{len: 1}
+
+ f.read(mut buf) or { return eof }
+ return buf[0]
+}
+
+fn (mut y Vyacc) ungetrune(f os.File, c rune) {
+ if f != y.finput {
+ panic('ungetc - not y.finput')
+ }
+ if y.peekrune != 0 {
+ panic('ungetc - 2nd unget')
+ }
+ y.peekrune = c
+}
+
+fn (mut y Vyacc) open(s string) !os.File {
+ return os.open(s)
+}
+
+fn (mut y Vyacc) create(s string) !os.File {
+ return os.create(s)
+}
+
+// write out error comment
+fn (mut y Vyacc) lerrorf(lineno int, s string) {
+ y.nerrors++
+ y.stderr.write_string(s) or { panic(err) }
+ y.stderr.write_string(': ${y.infile}:${lineno}\n') or { panic(err) }
+ if y.fatfl != 0 {
+ y.summary()
+ exit_(1)
+ }
+}
+
+fn (mut y Vyacc) errorf(s string) {
+ y.lerrorf(y.lineno, s)
+}
+
+fn exit_(status int) {
+ // if y.ftable != none {
+ // y.ftable.flush()
+ // }
+ // if y.foutput != none {
+ // y.foutput.flush()
+ // }
+ // if y.stderr != none {
+ // y.stderr.flush()
+ // }
+ exit(status)
+}
+
+fn unquote(s string) !string {
+ return s[1..s.len - 2]
+}
+
+const yaccpartext = '
+// parser for yacc output
+
+const $\$_debug = 0
+const $\$_error_verbose = true
+
+interface YYLexer {
+mut:
+ lex(mut lval YYSymType) int
+ error(s string)!
+}
+
+interface YYParser {
+mut:
+ parse(mut YYLexer) !int
+ lookahead() int
+}
+
+struct YYParserImpl {
+mut:
+ lval YYSymType
+ stack [$\$_initial_stack_size]YYSymType
+ char int
+}
+
+fn (mut p YYParserImpl) lookahead() int {
+ return p.char
+}
+
+fn $\$_new_parser() YYParser {
+ return YYParserImpl{}
+}
+
+const $\$_flag = -1000
+
+fn $\$_tokname(c int) string {
+ if c >= 1 && c-1 < $\$_toknames.len {
+ if $\$_toknames[c-1] != "" {
+ return $\$_toknames[c-1]
+ }
+ }
+ return "tok-\$c"
+}
+
+fn $\$_statname(s int) string {
+ if s >= 0 && s < $\$_statenames.len {
+ if $\$_statenames[s] != "" {
+ return $\$_statenames[s]
+ }
+ }
+ return "state-\$s"
+}
+
+const tokstart = 4
+
+fn $\$_error_message(state int, look_ahead int) string {
+ if !$\$_error_verbose {
+ return "syntax error"
+ }
+
+ for e in $\$_error_messages {
+ if e.state == state && e.token == look_ahead {
+ return "syntax error: " + e.msg
+ }
+ }
+
+ mut res := "syntax error: unexpected " + $\$_tokname(look_ahead)
+
+ // To match Bison, suggest at most four expected tokens.
+ mut expected := []int{cap: 4}
+
+ // Look for shiftable tokens.
+ base := int($\$_pact[state])
+ for tok := tokstart; tok-1 < $\$_toknames.len; tok++ {
+ n := base + tok
+ if n >= 0 && n < $\$_last && int($\$_chk[int($\$_act[n])]) == tok {
+ if expected.len == expected.cap {
+ return res
+ }
+ expected << tok
+ }
+ }
+
+ if $\$_def[state] == -2 {
+ mut i := 0
+ for $\$_exca[i] != -1 || int($\$_exca[i+1]) != state {
+ i += 2
+ }
+
+ // Look for tokens that we accept or reduce.
+ for i += 2; $\$_exca[i] >= 0; i += 2 {
+ tok := int($\$_exca[i])
+ if tok < tokstart || $\$_exca[i+1] == 0 {
+ continue
+ }
+ if expected.len == expected.cap {
+ return res
+ }
+ expected << tok
+ }
+
+ // If the default action is to accept or reduce, give up.
+ if $\$_exca[i+1] != 0 {
+ return res
+ }
+ }
+
+ for i, tok in expected {
+ if i == 0 {
+ res += ", expecting "
+ } else {
+ res += " or "
+ }
+ res += $\$_tokname(tok)
+ }
+ return res
+}
+
+fn $\$lex1(mut lex YYLexer, mut lval YYSymType) (int, int) {
+ mut token := 0
+ mut ch := lex.lex(mut lval)
+ if ch <= 0 {
+ token = int($\$_tok1[0])
+ unsafe { goto out }
+ }
+ if ch < $\$_tok1.len {
+ token = int($\$_tok1[ch])
+ unsafe { goto out }
+ }
+ if ch >= $\$_private {
+ if ch < $\$_private+$\$_tok2.len {
+ token = int($\$_tok2[ch-$\$_private])
+ unsafe { goto out }
+ }
+ }
+ for i := 0; i < $\$_tok3.len; i += 2 {
+ token = int($\$_tok3[i+0])
+ if token == ch {
+ token = int($\$_tok3[i+1])
+ unsafe { goto out }
+ }
+ }
+
+out:
+ if token == 0 {
+ token = int($\$_tok2[1]) // unknown char
+ }
+ if $\$_debug >= 3 {
+ println("lex \${$\$_tokname(token)}(\${u8(ch)})")
+ }
+ return ch, token
+}
+
+fn $\$_parse(mut $\$lex YYLexer) !int {
+ mut parser := $\$_new_parser()
+ return parser.parse(mut $\$lex)
+}
+
+fn (mut $\$rcvr YYParserImpl) parse(mut $\$lex YYLexer) !int {
+ mut $\$n := 0
+ mut $\$_val := YYSymType{}
+ mut $\$_dollar := []YYSymType{}
+ _ = $\$_dollar // silence set and not used
+ mut $\$_s := $\$rcvr.stack[..]
+
+ mut n_errs := 0 // number of errors
+ mut err_flag := 0 // error recovery flag
+ mut $\$state := 0
+ $\$rcvr.char = -1
+ mut $\$token := -1 // $\$rcvr.char translated into internal numbering
+ defer {
+ // Make sure we report no lookahead when not parsing.
+ $\$state = -1
+ $\$rcvr.char = -1
+ $\$token = -1
+ }
+ mut $\$p := -1
+ unsafe { goto $\$stack }
+
+ret0:
+ return 0
+
+ret1:
+ return 1
+
+$\$stack:
+ // put a state and value onto the stack
+ if $\$_debug >= 4 {
+ println("char \${$\$_tokname($\$token)} in \${$\$_statname($\$state)}")
+ }
+
+ $\$p++
+ if $\$p >= $\$_s.len {
+ mut nyys := []YYSymType{len: $\$_s.len*2}
+ gocopy(mut nyys, $\$_s)
+ $\$_s = nyys.clone()
+ }
+ $\$_s[$\$p] = $\$_val
+ $\$_s[$\$p].yys = $\$state
+
+$\$newstate:
+ $\$n = int($\$_pact[$\$state])
+ if $\$n <= $\$_flag {
+ unsafe {
+ goto $\$default // simple state
+ }
+ }
+ if $\$rcvr.char < 0 {
+ $\$rcvr.char, $\$token = $\$lex1(mut $\$lex, mut $\$rcvr.lval)
+ }
+ $\$n += $\$token
+ if $\$n < 0 || $\$n >= $\$_last {
+ unsafe { goto $\$default }
+ }
+ $\$n = int($\$_act[$\$n])
+ if int($\$_chk[$\$n]) == $\$token {
+ // valid shift
+ $\$rcvr.char = -1
+ $\$token = -1
+ $\$_val = $\$rcvr.lval
+ $\$state = $\$n
+ if err_flag > 0 {
+ err_flag--
+ }
+ unsafe { goto $\$stack }
+ }
+
+$\$default:
+ // default state action
+ $\$n = int($\$_def[$\$state])
+ if $\$n == -2 {
+ if $\$rcvr.char < 0 {
+ $\$rcvr.char, $\$token = $\$lex1(mut $\$lex, mut $\$rcvr.lval)
+ }
+
+ // look through exception table
+ mut xi := 0
+ for {
+ if $\$_exca[xi+0] == -1 && int($\$_exca[xi+1]) == $\$state {
+ break
+ }
+ xi += 2
+ }
+ for xi += 2; ; xi += 2 {
+ $\$n = int($\$_exca[xi+0])
+ if $\$n < 0 || $\$n == $\$token {
+ break
+ }
+ }
+ $\$n = int($\$_exca[xi+1])
+ if $\$n < 0 {
+ unsafe { goto ret0 }
+ }
+ }
+ if $\$n == 0 {
+ // error ... attempt to resume parsing
+ match err_flag {
+ 0 {
+ // brand new error
+ $\$lex.error($\$_error_message($\$state, $\$token))!
+ n_errs++
+ if $\$_debug >= 1 {
+ print($\$_statname($\$state))
+ println(" saw \${$\$_tokname($\$token)}")
+ }
+
+ // Note: fallthrough copies the next case:
+ err_flag = 3
+
+ // find a state where "error" is a legal shift action
+ for $\$p >= 0 {
+ $\$n = int($\$_pact[$\$_s[$\$p].yys]) + $\$_err_code
+ if $\$n >= 0 && $\$n < $\$_last {
+ $\$state = int($\$_act[$\$n]) // simulate a shift of "error"
+ if int($\$_chk[$\$state]) == $\$_err_code {
+ unsafe { goto $\$stack }
+ }
+ }
+
+ // the current p has no shift on "error", pop stack
+ if $\$_debug >= 2 {
+ println("error recovery pops state \${$\$_s[$\$p].yys}")
+ }
+ $\$p--
+ }
+ // there is no state on the stack with an error shift ... abort
+ unsafe { goto ret1 }
+ }
+
+ 1, 2 {
+ // incompletely recovered error ... try again
+ err_flag = 3
+
+ // find a state where "error" is a legal shift action
+ for $\$p >= 0 {
+ $\$n = int($\$_pact[$\$_s[$\$p].yys]) + $\$_err_code
+ if $\$n >= 0 && $\$n < $\$_last {
+ $\$state = int($\$_act[$\$n]) // simulate a shift of "error"
+ if int($\$_chk[$\$state]) == $\$_err_code {
+ unsafe { goto $\$stack }
+ }
+ }
+
+ // the current p has no shift on "error", pop stack
+ if $\$_debug >= 2 {
+ println("error recovery pops state \${$\$_s[$\$p].yys}")
+ }
+ $\$p--
+ }
+ // there is no state on the stack with an error shift ... abort
+ unsafe { goto ret1 }
+ }
+
+ 3 {
+ // no shift yet; clobber input char
+ if $\$_debug >= 2 {
+ println("error recovery discards \${$\$_tokname($\$token)}")
+ }
+ if $\$token == $\$_eof_code {
+ unsafe { goto ret1 }
+ }
+ $\$rcvr.char = -1
+ $\$token = -1
+ unsafe {
+ goto $\$newstate
+ // try again in the same state
+ }
+ }
+
+ else {}
+ }
+ }
+
+ // reduction by production $\$n
+ if $\$_debug >= 2 {
+ println("reduce \${$\$n} in:\\n\\t\${$\$_statname($\$state)}")
+ }
+
+ $\$nt := $\$n
+ $\$pt := $\$p
+ _ = $\$pt // guard against "declared and not used"
+
+ $\$p -= int($\$_r2[$\$n])
+ // $\$p is now the index of \$0. Perform the default action. Iff the
+ // reduced production is ε, \$1 is possibly out of range.
+ if $\$p+1 >= $\$_s.len {
+ mut nyys := []YYSymType{len: $\$_s.len*2}
+ gocopy(mut nyys, $\$_s)
+ $\$_s = nyys.clone()
+ }
+ $\$_val = $\$_s[$\$p+1]
+
+ // consult goto table to find next state
+ $\$n = int($\$_r1[$\$n])
+ $\$g := int($\$_pgo[$\$n])
+ $\$j := $\$g + $\$_s[$\$p].yys + 1
+
+ if $\$j >= $\$_last {
+ $\$state = int($\$_act[$\$g])
+ } else {
+ $\$state = int($\$_act[$\$j])
+ if int($\$_chk[$\$state]) != -$\$n {
+ $\$state = int($\$_act[$\$g])
+ }
+ }
+ // dummy call; replaced with literal code
+ $\$run()
+ unsafe {
+ goto $\$stack
+ // stack new state and value
+ }
+}
+
+fn gocopy[T](mut dst []T, src []T) int {
+ mut min := dst.len
+ if src.len < min {
+ min = src.len
+ }
+ for i := 0; i < min; i++ {
+ dst[i] = src[i]
+ }
+ return src.len
+}
+'
+
+fn slice_str[T](ss []T) string {
+ mut s := '['
+ for i, a in ss {
+ if i > 0 {
+ s += ' '
+ }
+ s += '${a}'
+ }
+ s += ']'
+ return s
+}
+
+fn (y Vyacc) dump(msg string) {
+ println('--- ${msg}')
+ println('nstate = ${y.nstate}')
+ println('tystate = ${slice_str(y.tystate[..y.nstate])}')
+ println('wsets (${y.ntokens})')
+ for i, w in y.wsets[..y.ntokens] {
+ println(' ${i}: ${pitem_str(w.pitem)} ${w.flag} ${w.ws}')
+ }
+ println('temp1 = ${slice_str(y.temp1[..50])}')
+}
+
+fn pitem_str(p Pitem) string {
+ if p.prod.len == 0 {
+ return '{[] ${p.off} ${p.first} ${p.prodno}}'
+ }
+ if p.prod.len == 2 {
+ return '{[${p.prod[0]} ${p.prod[1]}] ${p.off} ${p.first} ${p.prodno}}'
+ }
+ if p.prod.len == 4 {
+ return '{[${p.prod[0]} ${p.prod[1]} ${p.prod[2]} ${p.prod[3]}] ${p.off} ${p.first} ${p.prodno}}'
+ }
+ return 'BAD ${p.prod.len}'
+}
diff --git a/tests/catalogs.sql b/tests/catalogs.sql
index de10bda..b4faf56 100644
--- a/tests/catalogs.sql
+++ b/tests/catalogs.sql
@@ -1,11 +1,11 @@
-CREATE TABLE ":memory:".PUBLIC.foo (baz INTEGER);
-INSERT INTO ":memory:".PUBLIC.foo (baz) VALUES (123);
-INSERT INTO ":memory:".PUBLIC.foo (baz) VALUES (456);
-SELECT * FROM ":memory:".PUBLIC.foo;
-UPDATE ":memory:".PUBLIC.foo SET baz = 789 WHERE baz = 123;
-SELECT * FROM ":memory:".PUBLIC.foo;
-DELETE FROM ":memory:".PUBLIC.foo WHERE baz > 700;
-SELECT * FROM ":memory:".PUBLIC.foo;
+CREATE TABLE PUBLIC.foo (baz INTEGER);
+INSERT INTO PUBLIC.foo (baz) VALUES (123);
+INSERT INTO PUBLIC.foo (baz) VALUES (456);
+SELECT * FROM PUBLIC.foo;
+UPDATE PUBLIC.foo SET baz = 789 WHERE baz = 123;
+SELECT * FROM PUBLIC.foo;
+DELETE FROM PUBLIC.foo WHERE baz > 700;
+SELECT * FROM PUBLIC.foo;
-- msg: CREATE TABLE 1
-- msg: INSERT 1
-- msg: INSERT 1
@@ -18,17 +18,21 @@ SELECT * FROM ":memory:".PUBLIC.foo;
-- BAZ: 456
/* create_catalog FOO */
-CREATE TABLE foo.public.bar (baz INTEGER);
-EXPLAIN SELECT * FROM foo.public.bar;
+SET CATALOG 'FOO';
+CREATE TABLE public.bar (baz INTEGER);
+EXPLAIN SELECT * FROM public.bar;
+-- msg: SET CATALOG 1
-- msg: CREATE TABLE 1
-- EXPLAIN: TABLE FOO.PUBLIC.BAR (BAZ INTEGER)
-- EXPLAIN: EXPR (FOO.PUBLIC.BAR.BAZ INTEGER)
/* create_catalog FOO */
-CREATE TABLE foo.public.bar (baz INTEGER);
-INSERT INTO foo.public.bar (baz) VALUES (123);
-EXPLAIN SELECT * FROM foo.public.bar;
+SET CATALOG 'FOO';
+CREATE TABLE public.bar (baz INTEGER);
+INSERT INTO public.bar (baz) VALUES (123);
+EXPLAIN SELECT * FROM public.bar;
SET CATALOG ':memory:';
+-- msg: SET CATALOG 1
-- msg: CREATE TABLE 1
-- msg: INSERT 1
-- EXPLAIN: TABLE FOO.PUBLIC.BAR (BAZ INTEGER)
@@ -51,10 +55,10 @@ SELECT * FROM baz;
CREATE TABLE baz (num2 INTEGER);
INSERT INTO baz (num2) VALUES (456);
SELECT * FROM baz;
-SET CATALOG ':memory:';
-SELECT * FROM foo.public.baz;
-SELECT * FROM bar.public.baz;
-SELECT * FROM foo.public.baz JOIN bar.public.baz ON TRUE;
+SET CATALOG 'FOO';
+SELECT * FROM public.baz;
+SET CATALOG 'BAR';
+SELECT * FROM public.baz;
-- COL1: :memory:
-- COL1: BAR
-- msg: SET CATALOG 1
@@ -71,5 +75,5 @@ SELECT * FROM foo.public.baz JOIN bar.public.baz ON TRUE;
-- NUM2: 456
-- msg: SET CATALOG 1
-- NUM2: 456
+-- msg: SET CATALOG 1
-- NUM1: 123
--- NUM2: 456 NUM1: 123
diff --git a/tests/errors.sql b/tests/errors.sql
index 29b6d6c..bfb699b 100644
--- a/tests/errors.sql
+++ b/tests/errors.sql
@@ -1,5 +1,5 @@
TABLE;
--- error 42601: syntax error: near "TABLE"
+-- error 42601: syntax error: unexpected TABLE
SELECT 1; DELETE;
--- error 42601: syntax error: near "DELETE"
+-- error 42601: syntax error: unexpected ";", expecting FROM or ","
diff --git a/tests/fold.sql b/tests/fold.sql
index 2b6ab60..13b3879 100644
--- a/tests/fold.sql
+++ b/tests/fold.sql
@@ -13,7 +13,7 @@ VALUES LOWER(TRUE);
-- error 42883: function does not exist: LOWER(BOOLEAN)
VALUES UPPER();
--- error 42601: syntax error: near ")"
+-- error 42601: syntax error: unexpected ")"
VALUES LOWER('abc', 123);
--- error 42601: syntax error: near ","
+-- error 42601: syntax error: unexpected ",", expecting ")" or "||"
diff --git a/tests/math.sql b/tests/math.sql
index 567ce4d..5cc6f6b 100644
--- a/tests/math.sql
+++ b/tests/math.sql
@@ -10,10 +10,10 @@ VALUES ABS('hello');
-- error 42883: function does not exist: ABS(CHARACTER(5))
VALUES ABS();
--- error 42601: syntax error: near ")"
+-- error 42601: syntax error: unexpected ")"
VALUES ABS(1, 2);
--- error 42601: syntax error: near ","
+-- error 42601: syntax error: unexpected ",", expecting ")" or "+" or "-"
/* types */
VALUES MOD(232.0, 3.0);
diff --git a/tests/reserved-words.sql b/tests/reserved-words.sql
index 33b8bfd..187f23f 100644
--- a/tests/reserved-words.sql
+++ b/tests/reserved-words.sql
@@ -1,1106 +1,1106 @@
CREATE TABLE ABS (x INT);
--- error 42601: syntax error: near "ABS"
+-- error 42601: syntax error: unexpected ABS
CREATE TABLE ACOS (x INT);
--- error 42601: syntax error: near "ACOS"
+-- error 42601: syntax error: unexpected ACOS
CREATE TABLE ALL (x INT);
--- error 42601: syntax error: near "ALL"
+-- error 42601: syntax error: unexpected ALL
CREATE TABLE ALLOCATE (x INT);
--- error 42601: syntax error: near "ALLOCATE"
+-- error 42601: syntax error: unexpected ALLOCATE
CREATE TABLE ALTER (x INT);
--- error 42601: syntax error: near "ALTER"
+-- error 42601: syntax error: unexpected ALTER
CREATE TABLE AND (x INT);
--- error 42601: syntax error: near "AND"
+-- error 42601: syntax error: unexpected AND
CREATE TABLE ANY (x INT);
--- error 42601: syntax error: near "ANY"
+-- error 42601: syntax error: unexpected ANY
CREATE TABLE ARE (x INT);
--- error 42601: syntax error: near "ARE"
+-- error 42601: syntax error: unexpected ARE
CREATE TABLE ARRAY (x INT);
--- error 42601: syntax error: near "ARRAY"
+-- error 42601: syntax error: unexpected ARRAY
CREATE TABLE ARRAY_AGG (x INT);
--- error 42601: syntax error: near "ARRAY_AGG"
+-- error 42601: syntax error: unexpected ARRAY_AGG
CREATE TABLE ARRAY_MAX_CARDINALITY (x INT);
--- error 42601: syntax error: near "ARRAY_MAX_CARDINALITY"
+-- error 42601: syntax error: unexpected ARRAY_MAX_CARDINALITY
CREATE TABLE AS (x INT);
--- error 42601: syntax error: near "AS"
+-- error 42601: syntax error: unexpected AS
CREATE TABLE ASENSITIVE (x INT);
--- error 42601: syntax error: near "ASENSITIVE"
+-- error 42601: syntax error: unexpected ASENSITIVE
CREATE TABLE ASIN (x INT);
--- error 42601: syntax error: near "ASIN"
+-- error 42601: syntax error: unexpected ASIN
CREATE TABLE ASYMMETRIC (x INT);
--- error 42601: syntax error: near "ASYMMETRIC"
+-- error 42601: syntax error: unexpected ASYMMETRIC
CREATE TABLE AT (x INT);
--- error 42601: syntax error: near "AT"
+-- error 42601: syntax error: unexpected AT
CREATE TABLE ATAN (x INT);
--- error 42601: syntax error: near "ATAN"
+-- error 42601: syntax error: unexpected ATAN
CREATE TABLE ATOMIC (x INT);
--- error 42601: syntax error: near "ATOMIC"
+-- error 42601: syntax error: unexpected ATOMIC
CREATE TABLE AUTHORIZATION (x INT);
--- error 42601: syntax error: near "AUTHORIZATION"
+-- error 42601: syntax error: unexpected AUTHORIZATION
CREATE TABLE AVG (x INT);
--- error 42601: syntax error: near "AVG"
+-- error 42601: syntax error: unexpected AVG
CREATE TABLE BEGIN (x INT);
--- error 42601: syntax error: near "BEGIN"
+-- error 42601: syntax error: unexpected BEGIN
CREATE TABLE BEGIN_FRAME (x INT);
--- error 42601: syntax error: near "BEGIN_FRAME"
+-- error 42601: syntax error: unexpected BEGIN_FRAME
CREATE TABLE BEGIN_PARTITION (x INT);
--- error 42601: syntax error: near "BEGIN_PARTITION"
+-- error 42601: syntax error: unexpected BEGIN_PARTITION
CREATE TABLE BETWEEN (x INT);
--- error 42601: syntax error: near "BETWEEN"
+-- error 42601: syntax error: unexpected BETWEEN
CREATE TABLE BIGINT (x INT);
--- error 42601: syntax error: near "BIGINT"
+-- error 42601: syntax error: unexpected BIGINT
CREATE TABLE BINARY (x INT);
--- error 42601: syntax error: near "BINARY"
+-- error 42601: syntax error: unexpected BINARY
CREATE TABLE BLOB (x INT);
--- error 42601: syntax error: near "BLOB"
+-- error 42601: syntax error: unexpected BLOB
CREATE TABLE BOOLEAN (x INT);
--- error 42601: syntax error: near "BOOLEAN"
+-- error 42601: syntax error: unexpected BOOLEAN
CREATE TABLE BOTH (x INT);
--- error 42601: syntax error: near "BOTH"
+-- error 42601: syntax error: unexpected BOTH
CREATE TABLE BY (x INT);
--- error 42601: syntax error: near "BY"
+-- error 42601: syntax error: unexpected BY
CREATE TABLE CALL (x INT);
--- error 42601: syntax error: near "CALL"
+-- error 42601: syntax error: unexpected CALL
CREATE TABLE CALLED (x INT);
--- error 42601: syntax error: near "CALLED"
+-- error 42601: syntax error: unexpected CALLED
CREATE TABLE CARDINALITY (x INT);
--- error 42601: syntax error: near "CARDINALITY"
+-- error 42601: syntax error: unexpected CARDINALITY
CREATE TABLE CASCADED (x INT);
--- error 42601: syntax error: near "CASCADED"
+-- error 42601: syntax error: unexpected CASCADED
CREATE TABLE CASE (x INT);
--- error 42601: syntax error: near "CASE"
+-- error 42601: syntax error: unexpected CASE
CREATE TABLE CAST (x INT);
--- error 42601: syntax error: near "CAST"
+-- error 42601: syntax error: unexpected CAST
CREATE TABLE CEIL (x INT);
--- error 42601: syntax error: near "CEIL"
+-- error 42601: syntax error: unexpected CEIL
CREATE TABLE CEILING (x INT);
--- error 42601: syntax error: near "CEILING"
+-- error 42601: syntax error: unexpected CEILING
CREATE TABLE CHAR (x INT);
--- error 42601: syntax error: near "CHAR"
+-- error 42601: syntax error: unexpected CHAR
CREATE TABLE CHAR_LENGTH (x INT);
--- error 42601: syntax error: near "CHAR_LENGTH"
+-- error 42601: syntax error: unexpected CHAR_LENGTH
CREATE TABLE CHARACTER (x INT);
--- error 42601: syntax error: near "CHARACTER"
+-- error 42601: syntax error: unexpected CHARACTER
CREATE TABLE CHARACTER_LENGTH (x INT);
--- error 42601: syntax error: near "CHARACTER_LENGTH"
+-- error 42601: syntax error: unexpected CHARACTER_LENGTH
CREATE TABLE CHECK (x INT);
--- error 42601: syntax error: near "CHECK"
+-- error 42601: syntax error: unexpected CHECK
CREATE TABLE CLASSIFIER (x INT);
--- error 42601: syntax error: near "CLASSIFIER"
+-- error 42601: syntax error: unexpected CLASSIFIER
CREATE TABLE CLOB (x INT);
--- error 42601: syntax error: near "CLOB"
+-- error 42601: syntax error: unexpected CLOB
CREATE TABLE CLOSE (x INT);
--- error 42601: syntax error: near "CLOSE"
+-- error 42601: syntax error: unexpected CLOSE
CREATE TABLE COALESCE (x INT);
--- error 42601: syntax error: near "COALESCE"
+-- error 42601: syntax error: unexpected COALESCE
CREATE TABLE COLLATE (x INT);
--- error 42601: syntax error: near "COLLATE"
+-- error 42601: syntax error: unexpected COLLATE
CREATE TABLE COLLECT (x INT);
--- error 42601: syntax error: near "COLLECT"
+-- error 42601: syntax error: unexpected COLLECT
CREATE TABLE COLUMN (x INT);
--- error 42601: syntax error: near "COLUMN"
+-- error 42601: syntax error: unexpected COLUMN
CREATE TABLE COMMIT (x INT);
--- error 42601: syntax error: near "COMMIT"
+-- error 42601: syntax error: unexpected COMMIT
CREATE TABLE CONDITION (x INT);
--- error 42601: syntax error: near "CONDITION"
+-- error 42601: syntax error: unexpected CONDITION
CREATE TABLE CONNECT (x INT);
--- error 42601: syntax error: near "CONNECT"
+-- error 42601: syntax error: unexpected CONNECT
CREATE TABLE CONSTRAINT (x INT);
--- error 42601: syntax error: near "CONSTRAINT"
+-- error 42601: syntax error: unexpected CONSTRAINT
CREATE TABLE CONTAINS (x INT);
--- error 42601: syntax error: near "CONTAINS"
+-- error 42601: syntax error: unexpected CONTAINS
CREATE TABLE CONVERT (x INT);
--- error 42601: syntax error: near "CONVERT"
+-- error 42601: syntax error: unexpected CONVERT
CREATE TABLE COPY (x INT);
--- error 42601: syntax error: near "COPY"
+-- error 42601: syntax error: unexpected COPY
CREATE TABLE CORR (x INT);
--- error 42601: syntax error: near "CORR"
+-- error 42601: syntax error: unexpected CORR
CREATE TABLE CORRESPONDING (x INT);
--- error 42601: syntax error: near "CORRESPONDING"
+-- error 42601: syntax error: unexpected CORRESPONDING
CREATE TABLE COS (x INT);
--- error 42601: syntax error: near "COS"
+-- error 42601: syntax error: unexpected COS
CREATE TABLE COSH (x INT);
--- error 42601: syntax error: near "COSH"
+-- error 42601: syntax error: unexpected COSH
CREATE TABLE COUNT (x INT);
--- error 42601: syntax error: near "COUNT"
+-- error 42601: syntax error: unexpected COUNT
CREATE TABLE COVAR_POP (x INT);
--- error 42601: syntax error: near "COVAR_POP"
+-- error 42601: syntax error: unexpected COVAR_POP
CREATE TABLE COVAR_SAMP (x INT);
--- error 42601: syntax error: near "COVAR_SAMP"
+-- error 42601: syntax error: unexpected COVAR_SAMP
CREATE TABLE CREATE (x INT);
--- error 42601: syntax error: near "CREATE"
+-- error 42601: syntax error: unexpected CREATE
CREATE TABLE CROSS (x INT);
--- error 42601: syntax error: near "CROSS"
+-- error 42601: syntax error: unexpected CROSS
CREATE TABLE CUBE (x INT);
--- error 42601: syntax error: near "CUBE"
+-- error 42601: syntax error: unexpected CUBE
CREATE TABLE CUME_DIST (x INT);
--- error 42601: syntax error: near "CUME_DIST"
+-- error 42601: syntax error: unexpected CUME_DIST
CREATE TABLE CURRENT (x INT);
--- error 42601: syntax error: near "CURRENT"
+-- error 42601: syntax error: unexpected CURRENT
CREATE TABLE CURRENT_CATALOG (x INT);
--- error 42601: syntax error: near "CURRENT_CATALOG"
+-- error 42601: syntax error: unexpected CURRENT_CATALOG
CREATE TABLE CURRENT_DATE (x INT);
--- error 42601: syntax error: near "CURRENT_DATE"
+-- error 42601: syntax error: unexpected CURRENT_DATE
CREATE TABLE CURRENT_DEFAULT_TRANSFORM_GROUP (x INT);
--- error 42601: syntax error: near "CURRENT_DEFAULT_TRANSFORM_GROUP"
+-- error 42601: syntax error: unexpected CURRENT_DEFAULT_TRANSFORM_GROUP
CREATE TABLE CURRENT_PATH (x INT);
--- error 42601: syntax error: near "CURRENT_PATH"
+-- error 42601: syntax error: unexpected CURRENT_PATH
CREATE TABLE CURRENT_ROLE (x INT);
--- error 42601: syntax error: near "CURRENT_ROLE"
+-- error 42601: syntax error: unexpected CURRENT_ROLE
CREATE TABLE CURRENT_ROW (x INT);
--- error 42601: syntax error: near "CURRENT_ROW"
+-- error 42601: syntax error: unexpected CURRENT_ROW
CREATE TABLE CURRENT_SCHEMA (x INT);
--- error 42601: syntax error: near "CURRENT_SCHEMA"
+-- error 42601: syntax error: unexpected CURRENT_SCHEMA
CREATE TABLE CURRENT_TIME (x INT);
--- error 42601: syntax error: near "CURRENT_TIME"
+-- error 42601: syntax error: unexpected CURRENT_TIME
CREATE TABLE CURRENT_TIMESTAMP (x INT);
--- error 42601: syntax error: near "CURRENT_TIMESTAMP"
+-- error 42601: syntax error: unexpected CURRENT_TIMESTAMP
CREATE TABLE CURRENT_PATH (x INT);
--- error 42601: syntax error: near "CURRENT_PATH"
+-- error 42601: syntax error: unexpected CURRENT_PATH
CREATE TABLE CURRENT_ROLE (x INT);
--- error 42601: syntax error: near "CURRENT_ROLE"
+-- error 42601: syntax error: unexpected CURRENT_ROLE
CREATE TABLE CURRENT_TRANSFORM_GROUP_FOR_TYPE (x INT);
--- error 42601: syntax error: near "CURRENT_TRANSFORM_GROUP_FOR_TYPE"
+-- error 42601: syntax error: unexpected CURRENT_TRANSFORM_GROUP_FOR_TYPE
CREATE TABLE CURRENT_USER (x INT);
--- error 42601: syntax error: near "CURRENT_USER"
+-- error 42601: syntax error: unexpected CURRENT_USER
CREATE TABLE CURSOR (x INT);
--- error 42601: syntax error: near "CURSOR"
+-- error 42601: syntax error: unexpected CURSOR
CREATE TABLE CYCLE (x INT);
--- error 42601: syntax error: near "CYCLE"
+-- error 42601: syntax error: unexpected CYCLE
CREATE TABLE DATE (x INT);
--- error 42601: syntax error: near "DATE"
+-- error 42601: syntax error: unexpected DATE
CREATE TABLE DAY (x INT);
--- error 42601: syntax error: near "DAY"
+-- error 42601: syntax error: unexpected DAY
CREATE TABLE DEALLOCATE (x INT);
--- error 42601: syntax error: near "DEALLOCATE"
+-- error 42601: syntax error: unexpected DEALLOCATE
CREATE TABLE DEC (x INT);
--- error 42601: syntax error: near "DEC"
+-- error 42601: syntax error: unexpected DEC
CREATE TABLE DECIMAL (x INT);
--- error 42601: syntax error: near "DECIMAL"
+-- error 42601: syntax error: unexpected DECIMAL
CREATE TABLE DECFLOAT (x INT);
--- error 42601: syntax error: near "DECFLOAT"
+-- error 42601: syntax error: unexpected DECFLOAT
CREATE TABLE DECLARE (x INT);
--- error 42601: syntax error: near "DECLARE"
+-- error 42601: syntax error: unexpected DECLARE
CREATE TABLE DEFAULT (x INT);
--- error 42601: syntax error: near "DEFAULT"
+-- error 42601: syntax error: unexpected DEFAULT
CREATE TABLE DEFINE (x INT);
--- error 42601: syntax error: near "DEFINE"
+-- error 42601: syntax error: unexpected DEFINE
CREATE TABLE DELETE (x INT);
--- error 42601: syntax error: near "DELETE"
+-- error 42601: syntax error: unexpected DELETE
CREATE TABLE DENSE_RANK (x INT);
--- error 42601: syntax error: near "DENSE_RANK"
+-- error 42601: syntax error: unexpected DENSE_RANK
CREATE TABLE DEREF (x INT);
--- error 42601: syntax error: near "DEREF"
+-- error 42601: syntax error: unexpected DEREF
CREATE TABLE DESCRIBE (x INT);
--- error 42601: syntax error: near "DESCRIBE"
+-- error 42601: syntax error: unexpected DESCRIBE
CREATE TABLE DETERMINISTIC (x INT);
--- error 42601: syntax error: near "DETERMINISTIC"
+-- error 42601: syntax error: unexpected DETERMINISTIC
CREATE TABLE DISCONNECT (x INT);
--- error 42601: syntax error: near "DISCONNECT"
+-- error 42601: syntax error: unexpected DISCONNECT
CREATE TABLE DISTINCT (x INT);
--- error 42601: syntax error: near "DISTINCT"
+-- error 42601: syntax error: unexpected DISTINCT
CREATE TABLE DOUBLE (x INT);
--- error 42601: syntax error: near "DOUBLE"
+-- error 42601: syntax error: unexpected DOUBLE
CREATE TABLE DROP (x INT);
--- error 42601: syntax error: near "DROP"
+-- error 42601: syntax error: unexpected DROP
CREATE TABLE DYNAMIC (x INT);
--- error 42601: syntax error: near "DYNAMIC"
+-- error 42601: syntax error: unexpected DYNAMIC
CREATE TABLE EACH (x INT);
--- error 42601: syntax error: near "EACH"
+-- error 42601: syntax error: unexpected EACH
CREATE TABLE ELEMENT (x INT);
--- error 42601: syntax error: near "ELEMENT"
+-- error 42601: syntax error: unexpected ELEMENT
CREATE TABLE ELSE (x INT);
--- error 42601: syntax error: near "ELSE"
+-- error 42601: syntax error: unexpected ELSE
CREATE TABLE EMPTY (x INT);
--- error 42601: syntax error: near "EMPTY"
+-- error 42601: syntax error: unexpected EMPTY
CREATE TABLE END (x INT);
--- error 42601: syntax error: near "END"
+-- error 42601: syntax error: unexpected END
CREATE TABLE END_FRAME (x INT);
--- error 42601: syntax error: near "END_FRAME"
+-- error 42601: syntax error: unexpected END_FRAME
CREATE TABLE END_PARTITION (x INT);
--- error 42601: syntax error: near "END_PARTITION"
+-- error 42601: syntax error: unexpected END_PARTITION
CREATE TABLE END-EXEC (x INT);
--- error 42601: syntax error: near "END"
+-- error 42601: syntax error: unexpected END
CREATE TABLE EQUALS (x INT);
--- error 42601: syntax error: near "EQUALS"
+-- error 42601: syntax error: unexpected EQUALS
CREATE TABLE ESCAPE (x INT);
--- error 42601: syntax error: near "ESCAPE"
+-- error 42601: syntax error: unexpected ESCAPE
CREATE TABLE EVERY (x INT);
--- error 42601: syntax error: near "EVERY"
+-- error 42601: syntax error: unexpected EVERY
CREATE TABLE EXCEPT (x INT);
--- error 42601: syntax error: near "EXCEPT"
+-- error 42601: syntax error: unexpected EXCEPT
CREATE TABLE EXEC (x INT);
--- error 42601: syntax error: near "EXEC"
+-- error 42601: syntax error: unexpected EXEC
CREATE TABLE EXECUTE (x INT);
--- error 42601: syntax error: near "EXECUTE"
+-- error 42601: syntax error: unexpected EXECUTE
CREATE TABLE EXISTS (x INT);
--- error 42601: syntax error: near "EXISTS"
+-- error 42601: syntax error: unexpected EXISTS
CREATE TABLE EXP (x INT);
--- error 42601: syntax error: near "EXP"
+-- error 42601: syntax error: unexpected EXP
CREATE TABLE EXTERNAL (x INT);
--- error 42601: syntax error: near "EXTERNAL"
+-- error 42601: syntax error: unexpected EXTERNAL
CREATE TABLE EXTRACT (x INT);
--- error 42601: syntax error: near "EXTRACT"
+-- error 42601: syntax error: unexpected EXTRACT
CREATE TABLE FALSE (x INT);
--- error 42601: syntax error: near "FALSE"
+-- error 42601: syntax error: unexpected FALSE
CREATE TABLE FETCH (x INT);
--- error 42601: syntax error: near "FETCH"
+-- error 42601: syntax error: unexpected FETCH
CREATE TABLE FILTER (x INT);
--- error 42601: syntax error: near "FILTER"
+-- error 42601: syntax error: unexpected FILTER
CREATE TABLE FIRST_VALUE (x INT);
--- error 42601: syntax error: near "FIRST_VALUE"
+-- error 42601: syntax error: unexpected FIRST_VALUE
CREATE TABLE FLOAT (x INT);
--- error 42601: syntax error: near "FLOAT"
+-- error 42601: syntax error: unexpected FLOAT
CREATE TABLE FLOOR (x INT);
--- error 42601: syntax error: near "FLOOR"
+-- error 42601: syntax error: unexpected FLOOR
CREATE TABLE FOR (x INT);
--- error 42601: syntax error: near "FOR"
+-- error 42601: syntax error: unexpected FOR
CREATE TABLE FOREIGN (x INT);
--- error 42601: syntax error: near "FOREIGN"
+-- error 42601: syntax error: unexpected FOREIGN
CREATE TABLE FRAME_ROW (x INT);
--- error 42601: syntax error: near "FRAME_ROW"
+-- error 42601: syntax error: unexpected FRAME_ROW
CREATE TABLE FREE (x INT);
--- error 42601: syntax error: near "FREE"
+-- error 42601: syntax error: unexpected FREE
CREATE TABLE FROM (x INT);
--- error 42601: syntax error: near "FROM"
+-- error 42601: syntax error: unexpected FROM
CREATE TABLE FULL (x INT);
--- error 42601: syntax error: near "FULL"
+-- error 42601: syntax error: unexpected FULL
CREATE TABLE FUNCTION (x INT);
--- error 42601: syntax error: near "FUNCTION"
+-- error 42601: syntax error: unexpected FUNCTION
CREATE TABLE FUSION (x INT);
--- error 42601: syntax error: near "FUSION"
+-- error 42601: syntax error: unexpected FUSION
CREATE TABLE GET (x INT);
--- error 42601: syntax error: near "GET"
+-- error 42601: syntax error: unexpected GET
CREATE TABLE GLOBAL (x INT);
--- error 42601: syntax error: near "GLOBAL"
+-- error 42601: syntax error: unexpected GLOBAL
CREATE TABLE GRANT (x INT);
--- error 42601: syntax error: near "GRANT"
+-- error 42601: syntax error: unexpected GRANT
CREATE TABLE GROUP (x INT);
--- error 42601: syntax error: near "GROUP"
+-- error 42601: syntax error: unexpected GROUP
CREATE TABLE GROUPING (x INT);
--- error 42601: syntax error: near "GROUPING"
+-- error 42601: syntax error: unexpected GROUPING
CREATE TABLE GROUPS (x INT);
--- error 42601: syntax error: near "GROUPS"
+-- error 42601: syntax error: unexpected GROUPS
CREATE TABLE HAVING (x INT);
--- error 42601: syntax error: near "HAVING"
+-- error 42601: syntax error: unexpected HAVING
CREATE TABLE HOLD (x INT);
--- error 42601: syntax error: near "HOLD"
+-- error 42601: syntax error: unexpected HOLD
CREATE TABLE HOUR (x INT);
--- error 42601: syntax error: near "HOUR"
+-- error 42601: syntax error: unexpected HOUR
CREATE TABLE IDENTITY (x INT);
--- error 42601: syntax error: near "IDENTITY"
+-- error 42601: syntax error: unexpected IDENTITY
CREATE TABLE IN (x INT);
--- error 42601: syntax error: near "IN"
+-- error 42601: syntax error: unexpected IN
CREATE TABLE INDICATOR (x INT);
--- error 42601: syntax error: near "INDICATOR"
+-- error 42601: syntax error: unexpected INDICATOR
CREATE TABLE INITIAL (x INT);
--- error 42601: syntax error: near "INITIAL"
+-- error 42601: syntax error: unexpected INITIAL
CREATE TABLE INNER (x INT);
--- error 42601: syntax error: near "INNER"
+-- error 42601: syntax error: unexpected INNER
CREATE TABLE INOUT (x INT);
--- error 42601: syntax error: near "INOUT"
+-- error 42601: syntax error: unexpected INOUT
CREATE TABLE INSENSITIVE (x INT);
--- error 42601: syntax error: near "INSENSITIVE"
+-- error 42601: syntax error: unexpected INSENSITIVE
CREATE TABLE INSERT (x INT);
--- error 42601: syntax error: near "INSERT"
+-- error 42601: syntax error: unexpected INSERT
CREATE TABLE INT (x INT);
--- error 42601: syntax error: near "INT"
+-- error 42601: syntax error: unexpected INT
CREATE TABLE INTEGER (x INT);
--- error 42601: syntax error: near "INTEGER"
+-- error 42601: syntax error: unexpected INTEGER
CREATE TABLE INTERSECT (x INT);
--- error 42601: syntax error: near "INTERSECT"
+-- error 42601: syntax error: unexpected INTERSECT
CREATE TABLE INTERSECTION (x INT);
--- error 42601: syntax error: near "INTERSECTION"
+-- error 42601: syntax error: unexpected INTERSECTION
CREATE TABLE INTERVAL (x INT);
--- error 42601: syntax error: near "INTERVAL"
+-- error 42601: syntax error: unexpected INTERVAL
CREATE TABLE INTO (x INT);
--- error 42601: syntax error: near "INTO"
+-- error 42601: syntax error: unexpected INTO
CREATE TABLE IS (x INT);
--- error 42601: syntax error: near "IS"
+-- error 42601: syntax error: unexpected IS
CREATE TABLE JOIN (x INT);
--- error 42601: syntax error: near "JOIN"
+-- error 42601: syntax error: unexpected JOIN
CREATE TABLE JSON_ARRAY (x INT);
--- error 42601: syntax error: near "JSON_ARRAY"
+-- error 42601: syntax error: unexpected JSON_ARRAY
CREATE TABLE JSON_ARRAYAGG (x INT);
--- error 42601: syntax error: near "JSON_ARRAYAGG"
+-- error 42601: syntax error: unexpected JSON_ARRAYAGG
CREATE TABLE JSON_EXISTS (x INT);
--- error 42601: syntax error: near "JSON_EXISTS"
+-- error 42601: syntax error: unexpected JSON_EXISTS
CREATE TABLE JSON_OBJECT (x INT);
--- error 42601: syntax error: near "JSON_OBJECT"
+-- error 42601: syntax error: unexpected JSON_OBJECT
CREATE TABLE JSON_OBJECTAGG (x INT);
--- error 42601: syntax error: near "JSON_OBJECTAGG"
+-- error 42601: syntax error: unexpected JSON_OBJECTAGG
CREATE TABLE JSON_QUERY (x INT);
--- error 42601: syntax error: near "JSON_QUERY"
+-- error 42601: syntax error: unexpected JSON_QUERY
CREATE TABLE JSON_TABLE (x INT);
--- error 42601: syntax error: near "JSON_TABLE"
+-- error 42601: syntax error: unexpected JSON_TABLE
CREATE TABLE JSON_TABLE_PRIMITIVE (x INT);
--- error 42601: syntax error: near "JSON_TABLE_PRIMITIVE"
+-- error 42601: syntax error: unexpected JSON_TABLE_PRIMITIVE
CREATE TABLE JSON_VALUE (x INT);
--- error 42601: syntax error: near "JSON_VALUE"
+-- error 42601: syntax error: unexpected JSON_VALUE
CREATE TABLE LAG (x INT);
--- error 42601: syntax error: near "LAG"
+-- error 42601: syntax error: unexpected LAG
CREATE TABLE LANGUAGE (x INT);
--- error 42601: syntax error: near "LANGUAGE"
+-- error 42601: syntax error: unexpected LANGUAGE
CREATE TABLE LARGE (x INT);
--- error 42601: syntax error: near "LARGE"
+-- error 42601: syntax error: unexpected LARGE
CREATE TABLE LAST_VALUE (x INT);
--- error 42601: syntax error: near "LAST_VALUE"
+-- error 42601: syntax error: unexpected LAST_VALUE
CREATE TABLE LATERAL (x INT);
--- error 42601: syntax error: near "LATERAL"
+-- error 42601: syntax error: unexpected LATERAL
CREATE TABLE LEAD (x INT);
--- error 42601: syntax error: near "LEAD"
+-- error 42601: syntax error: unexpected LEAD
CREATE TABLE LEADING (x INT);
--- error 42601: syntax error: near "LEADING"
+-- error 42601: syntax error: unexpected LEADING
CREATE TABLE LEFT (x INT);
--- error 42601: syntax error: near "LEFT"
+-- error 42601: syntax error: unexpected LEFT
CREATE TABLE LIKE (x INT);
--- error 42601: syntax error: near "LIKE"
+-- error 42601: syntax error: unexpected LIKE
CREATE TABLE LIKE_REGEX (x INT);
--- error 42601: syntax error: near "LIKE_REGEX"
+-- error 42601: syntax error: unexpected LIKE_REGEX
CREATE TABLE LISTAGG (x INT);
--- error 42601: syntax error: near "LISTAGG"
+-- error 42601: syntax error: unexpected LISTAGG
CREATE TABLE LN (x INT);
--- error 42601: syntax error: near "LN"
+-- error 42601: syntax error: unexpected LN
CREATE TABLE LOCAL (x INT);
--- error 42601: syntax error: near "LOCAL"
+-- error 42601: syntax error: unexpected LOCAL
CREATE TABLE LOCALTIME (x INT);
--- error 42601: syntax error: near "LOCALTIME"
+-- error 42601: syntax error: unexpected LOCALTIME
CREATE TABLE LOCALTIMESTAMP (x INT);
--- error 42601: syntax error: near "LOCALTIMESTAMP"
+-- error 42601: syntax error: unexpected LOCALTIMESTAMP
CREATE TABLE LOG (x INT);
--- error 42601: syntax error: near "LOG"
+-- error 42601: syntax error: unexpected LOG
CREATE TABLE LOG10 (x INT);
--- error 42601: syntax error: near "LOG10"
+-- error 42601: syntax error: unexpected LOG10
CREATE TABLE LOWER (x INT);
--- error 42601: syntax error: near "LOWER"
+-- error 42601: syntax error: unexpected LOWER
CREATE TABLE MATCH (x INT);
--- error 42601: syntax error: near "MATCH"
+-- error 42601: syntax error: unexpected MATCH
CREATE TABLE MATCH_NUMBER (x INT);
--- error 42601: syntax error: near "MATCH_NUMBER"
+-- error 42601: syntax error: unexpected MATCH_NUMBER
CREATE TABLE MATCH_RECOGNIZE (x INT);
--- error 42601: syntax error: near "MATCH_RECOGNIZE"
+-- error 42601: syntax error: unexpected MATCH_RECOGNIZE
CREATE TABLE MATCHES (x INT);
--- error 42601: syntax error: near "MATCHES"
+-- error 42601: syntax error: unexpected MATCHES
CREATE TABLE MAX (x INT);
--- error 42601: syntax error: near "MAX"
+-- error 42601: syntax error: unexpected MAX
CREATE TABLE MEMBER (x INT);
--- error 42601: syntax error: near "MEMBER"
+-- error 42601: syntax error: unexpected MEMBER
CREATE TABLE MERGE (x INT);
--- error 42601: syntax error: near "MERGE"
+-- error 42601: syntax error: unexpected MERGE
CREATE TABLE METHOD (x INT);
--- error 42601: syntax error: near "METHOD"
+-- error 42601: syntax error: unexpected METHOD
CREATE TABLE MIN (x INT);
--- error 42601: syntax error: near "MIN"
+-- error 42601: syntax error: unexpected MIN
CREATE TABLE MINUTE (x INT);
--- error 42601: syntax error: near "MINUTE"
+-- error 42601: syntax error: unexpected MINUTE
CREATE TABLE MOD (x INT);
--- error 42601: syntax error: near "MOD"
+-- error 42601: syntax error: unexpected MOD
CREATE TABLE MODIFIES (x INT);
--- error 42601: syntax error: near "MODIFIES"
+-- error 42601: syntax error: unexpected MODIFIES
CREATE TABLE MODULE (x INT);
--- error 42601: syntax error: near "MODULE"
+-- error 42601: syntax error: unexpected MODULE
CREATE TABLE MONTH (x INT);
--- error 42601: syntax error: near "MONTH"
+-- error 42601: syntax error: unexpected MONTH
CREATE TABLE MULTISET (x INT);
--- error 42601: syntax error: near "MULTISET"
+-- error 42601: syntax error: unexpected MULTISET
CREATE TABLE NATIONAL (x INT);
--- error 42601: syntax error: near "NATIONAL"
+-- error 42601: syntax error: unexpected NATIONAL
CREATE TABLE NATURAL (x INT);
--- error 42601: syntax error: near "NATURAL"
+-- error 42601: syntax error: unexpected NATURAL
CREATE TABLE NCHAR (x INT);
--- error 42601: syntax error: near "NCHAR"
+-- error 42601: syntax error: unexpected NCHAR
CREATE TABLE NCLOB (x INT);
--- error 42601: syntax error: near "NCLOB"
+-- error 42601: syntax error: unexpected NCLOB
CREATE TABLE NEW (x INT);
--- error 42601: syntax error: near "NEW"
+-- error 42601: syntax error: unexpected NEW
CREATE TABLE NO (x INT);
--- error 42601: syntax error: near "NO"
+-- error 42601: syntax error: unexpected NO
CREATE TABLE NONE (x INT);
--- error 42601: syntax error: near "NONE"
+-- error 42601: syntax error: unexpected NONE
CREATE TABLE NORMALIZE (x INT);
--- error 42601: syntax error: near "NORMALIZE"
+-- error 42601: syntax error: unexpected NORMALIZE
CREATE TABLE NOT (x INT);
--- error 42601: syntax error: near "NOT"
+-- error 42601: syntax error: unexpected NOT
CREATE TABLE NTH_VALUE (x INT);
--- error 42601: syntax error: near "NTH_VALUE"
+-- error 42601: syntax error: unexpected NTH_VALUE
CREATE TABLE NTILE (x INT);
--- error 42601: syntax error: near "NTILE"
+-- error 42601: syntax error: unexpected NTILE
CREATE TABLE NULL (x INT);
--- error 42601: syntax error: near "NULL"
+-- error 42601: syntax error: unexpected NULL
CREATE TABLE NULLIF (x INT);
--- error 42601: syntax error: near "NULLIF"
+-- error 42601: syntax error: unexpected NULLIF
CREATE TABLE NUMERIC (x INT);
--- error 42601: syntax error: near "NUMERIC"
+-- error 42601: syntax error: unexpected NUMERIC
CREATE TABLE OCTET_LENGTH (x INT);
--- error 42601: syntax error: near "OCTET_LENGTH"
+-- error 42601: syntax error: unexpected OCTET_LENGTH
CREATE TABLE OCCURRENCES_REGEX (x INT);
--- error 42601: syntax error: near "OCCURRENCES_REGEX"
+-- error 42601: syntax error: unexpected OCCURRENCES_REGEX
CREATE TABLE OF (x INT);
--- error 42601: syntax error: near "OF"
+-- error 42601: syntax error: unexpected OF
CREATE TABLE OFFSET (x INT);
--- error 42601: syntax error: near "OFFSET"
+-- error 42601: syntax error: unexpected OFFSET
CREATE TABLE OLD (x INT);
--- error 42601: syntax error: near "OLD"
+-- error 42601: syntax error: unexpected OLD
CREATE TABLE OMIT (x INT);
--- error 42601: syntax error: near "OMIT"
+-- error 42601: syntax error: unexpected OMIT
CREATE TABLE ON (x INT);
--- error 42601: syntax error: near "ON"
+-- error 42601: syntax error: unexpected ON
CREATE TABLE ONE (x INT);
--- error 42601: syntax error: near "ONE"
+-- error 42601: syntax error: unexpected ONE
CREATE TABLE ONLY (x INT);
--- error 42601: syntax error: near "ONLY"
+-- error 42601: syntax error: unexpected ONLY
CREATE TABLE OPEN (x INT);
--- error 42601: syntax error: near "OPEN"
+-- error 42601: syntax error: unexpected OPEN
CREATE TABLE OR (x INT);
--- error 42601: syntax error: near "OR"
+-- error 42601: syntax error: unexpected OR
CREATE TABLE ORDER (x INT);
--- error 42601: syntax error: near "ORDER"
+-- error 42601: syntax error: unexpected ORDER
CREATE TABLE OUT (x INT);
--- error 42601: syntax error: near "OUT"
+-- error 42601: syntax error: unexpected OUT
CREATE TABLE OUTER (x INT);
--- error 42601: syntax error: near "OUTER"
+-- error 42601: syntax error: unexpected OUTER
CREATE TABLE OVER (x INT);
--- error 42601: syntax error: near "OVER"
+-- error 42601: syntax error: unexpected OVER
CREATE TABLE OVERLAPS (x INT);
--- error 42601: syntax error: near "OVERLAPS"
+-- error 42601: syntax error: unexpected OVERLAPS
CREATE TABLE OVERLAY (x INT);
--- error 42601: syntax error: near "OVERLAY"
+-- error 42601: syntax error: unexpected OVERLAY
CREATE TABLE PARAMETER (x INT);
--- error 42601: syntax error: near "PARAMETER"
+-- error 42601: syntax error: unexpected PARAMETER
CREATE TABLE PARTITION (x INT);
--- error 42601: syntax error: near "PARTITION"
+-- error 42601: syntax error: unexpected PARTITION
CREATE TABLE PATTERN (x INT);
--- error 42601: syntax error: near "PATTERN"
+-- error 42601: syntax error: unexpected PATTERN
CREATE TABLE PER (x INT);
--- error 42601: syntax error: near "PER"
+-- error 42601: syntax error: unexpected PER
CREATE TABLE PERCENT (x INT);
--- error 42601: syntax error: near "PERCENT"
+-- error 42601: syntax error: unexpected PERCENT
CREATE TABLE PERCENT_RANK (x INT);
--- error 42601: syntax error: near "PERCENT_RANK"
+-- error 42601: syntax error: unexpected PERCENT_RANK
CREATE TABLE PERCENTILE_CONT (x INT);
--- error 42601: syntax error: near "PERCENTILE_CONT"
+-- error 42601: syntax error: unexpected PERCENTILE_CONT
CREATE TABLE PERCENTILE_DISC (x INT);
--- error 42601: syntax error: near "PERCENTILE_DISC"
+-- error 42601: syntax error: unexpected PERCENTILE_DISC
CREATE TABLE PERIOD (x INT);
--- error 42601: syntax error: near "PERIOD"
+-- error 42601: syntax error: unexpected PERIOD
CREATE TABLE PORTION (x INT);
--- error 42601: syntax error: near "PORTION"
+-- error 42601: syntax error: unexpected PORTION
CREATE TABLE POSITION (x INT);
--- error 42601: syntax error: near "POSITION"
+-- error 42601: syntax error: unexpected POSITION
CREATE TABLE POSITION_REGEX (x INT);
--- error 42601: syntax error: near "POSITION_REGEX"
+-- error 42601: syntax error: unexpected POSITION_REGEX
CREATE TABLE POWER (x INT);
--- error 42601: syntax error: near "POWER"
+-- error 42601: syntax error: unexpected POWER
CREATE TABLE PRECEDES (x INT);
--- error 42601: syntax error: near "PRECEDES"
+-- error 42601: syntax error: unexpected PRECEDES
CREATE TABLE PRECISION (x INT);
--- error 42601: syntax error: near "PRECISION"
+-- error 42601: syntax error: unexpected PRECISION
CREATE TABLE PREPARE (x INT);
--- error 42601: syntax error: near "PREPARE"
+-- error 42601: syntax error: unexpected PREPARE
CREATE TABLE PRIMARY (x INT);
--- error 42601: syntax error: near "PRIMARY"
+-- error 42601: syntax error: unexpected PRIMARY
CREATE TABLE PROCEDURE (x INT);
--- error 42601: syntax error: near "PROCEDURE"
+-- error 42601: syntax error: unexpected PROCEDURE
CREATE TABLE PTF (x INT);
--- error 42601: syntax error: near "PTF"
+-- error 42601: syntax error: unexpected PTF
CREATE TABLE RANGE (x INT);
--- error 42601: syntax error: near "RANGE"
+-- error 42601: syntax error: unexpected RANGE
CREATE TABLE RANK (x INT);
--- error 42601: syntax error: near "RANK"
+-- error 42601: syntax error: unexpected RANK
CREATE TABLE READS (x INT);
--- error 42601: syntax error: near "READS"
+-- error 42601: syntax error: unexpected READS
CREATE TABLE REAL (x INT);
--- error 42601: syntax error: near "REAL"
+-- error 42601: syntax error: unexpected REAL
CREATE TABLE RECURSIVE (x INT);
--- error 42601: syntax error: near "RECURSIVE"
+-- error 42601: syntax error: unexpected RECURSIVE
CREATE TABLE REF (x INT);
--- error 42601: syntax error: near "REF"
+-- error 42601: syntax error: unexpected REF
CREATE TABLE REFERENCES (x INT);
--- error 42601: syntax error: near "REFERENCES"
+-- error 42601: syntax error: unexpected REFERENCES
CREATE TABLE REFERENCING (x INT);
--- error 42601: syntax error: near "REFERENCING"
+-- error 42601: syntax error: unexpected REFERENCING
CREATE TABLE REGR_AVGX (x INT);
--- error 42601: syntax error: near "REGR_AVGX"
+-- error 42601: syntax error: unexpected REGR_AVGX
CREATE TABLE REGR_AVGY (x INT);
--- error 42601: syntax error: near "REGR_AVGY"
+-- error 42601: syntax error: unexpected REGR_AVGY
CREATE TABLE REGR_COUNT (x INT);
--- error 42601: syntax error: near "REGR_COUNT"
+-- error 42601: syntax error: unexpected REGR_COUNT
CREATE TABLE REGR_INTERCEPT (x INT);
--- error 42601: syntax error: near "REGR_INTERCEPT"
+-- error 42601: syntax error: unexpected REGR_INTERCEPT
CREATE TABLE REGR_R2 (x INT);
--- error 42601: syntax error: near "REGR_R2"
+-- error 42601: syntax error: unexpected REGR_R2
CREATE TABLE REGR_SLOPE (x INT);
--- error 42601: syntax error: near "REGR_SLOPE"
+-- error 42601: syntax error: unexpected REGR_SLOPE
CREATE TABLE REGR_SXX (x INT);
--- error 42601: syntax error: near "REGR_SXX"
+-- error 42601: syntax error: unexpected REGR_SXX
CREATE TABLE REGR_SXY (x INT);
--- error 42601: syntax error: near "REGR_SXY"
+-- error 42601: syntax error: unexpected REGR_SXY
CREATE TABLE REGR_SYY (x INT);
--- error 42601: syntax error: near "REGR_SYY"
+-- error 42601: syntax error: unexpected REGR_SYY
CREATE TABLE RELEASE (x INT);
--- error 42601: syntax error: near "RELEASE"
+-- error 42601: syntax error: unexpected RELEASE
CREATE TABLE RESULT (x INT);
--- error 42601: syntax error: near "RESULT"
+-- error 42601: syntax error: unexpected RESULT
CREATE TABLE RETURN (x INT);
--- error 42601: syntax error: near "RETURN"
+-- error 42601: syntax error: unexpected RETURN
CREATE TABLE RETURNS (x INT);
--- error 42601: syntax error: near "RETURNS"
+-- error 42601: syntax error: unexpected RETURNS
CREATE TABLE REVOKE (x INT);
--- error 42601: syntax error: near "REVOKE"
+-- error 42601: syntax error: unexpected REVOKE
CREATE TABLE RIGHT (x INT);
--- error 42601: syntax error: near "RIGHT"
+-- error 42601: syntax error: unexpected RIGHT
CREATE TABLE ROLLBACK (x INT);
--- error 42601: syntax error: near "ROLLBACK"
+-- error 42601: syntax error: unexpected ROLLBACK
CREATE TABLE ROLLUP (x INT);
--- error 42601: syntax error: near "ROLLUP"
+-- error 42601: syntax error: unexpected ROLLUP
CREATE TABLE ROW (x INT);
--- error 42601: syntax error: near "ROW"
+-- error 42601: syntax error: unexpected ROW
CREATE TABLE ROW_NUMBER (x INT);
--- error 42601: syntax error: near "ROW_NUMBER"
+-- error 42601: syntax error: unexpected ROW_NUMBER
CREATE TABLE ROWS (x INT);
--- error 42601: syntax error: near "ROWS"
+-- error 42601: syntax error: unexpected ROWS
CREATE TABLE RUNNING (x INT);
--- error 42601: syntax error: near "RUNNING"
+-- error 42601: syntax error: unexpected RUNNING
CREATE TABLE SAVEPOINT (x INT);
--- error 42601: syntax error: near "SAVEPOINT"
+-- error 42601: syntax error: unexpected SAVEPOINT
CREATE TABLE SCOPE (x INT);
--- error 42601: syntax error: near "SCOPE"
+-- error 42601: syntax error: unexpected SCOPE
CREATE TABLE SCROLL (x INT);
--- error 42601: syntax error: near "SCROLL"
+-- error 42601: syntax error: unexpected SCROLL
CREATE TABLE SEARCH (x INT);
--- error 42601: syntax error: near "SEARCH"
+-- error 42601: syntax error: unexpected SEARCH
CREATE TABLE SECOND (x INT);
--- error 42601: syntax error: near "SECOND"
+-- error 42601: syntax error: unexpected SECOND
CREATE TABLE SEEK (x INT);
--- error 42601: syntax error: near "SEEK"
+-- error 42601: syntax error: unexpected SEEK
CREATE TABLE SELECT (x INT);
--- error 42601: syntax error: near "SELECT"
+-- error 42601: syntax error: unexpected SELECT
CREATE TABLE SENSITIVE (x INT);
--- error 42601: syntax error: near "SENSITIVE"
+-- error 42601: syntax error: unexpected SENSITIVE
CREATE TABLE SESSION_USER (x INT);
--- error 42601: syntax error: near "SESSION_USER"
+-- error 42601: syntax error: unexpected SESSION_USER
CREATE TABLE SET (x INT);
--- error 42601: syntax error: near "SET"
+-- error 42601: syntax error: unexpected SET
CREATE TABLE SHOW (x INT);
--- error 42601: syntax error: near "SHOW"
+-- error 42601: syntax error: unexpected SHOW
CREATE TABLE SIMILAR (x INT);
--- error 42601: syntax error: near "SIMILAR"
+-- error 42601: syntax error: unexpected SIMILAR
CREATE TABLE SIN (x INT);
--- error 42601: syntax error: near "SIN"
+-- error 42601: syntax error: unexpected SIN
CREATE TABLE SINH (x INT);
--- error 42601: syntax error: near "SINH"
+-- error 42601: syntax error: unexpected SINH
CREATE TABLE SKIP (x INT);
--- error 42601: syntax error: near "SKIP"
+-- error 42601: syntax error: unexpected SKIP
CREATE TABLE SMALLINT (x INT);
--- error 42601: syntax error: near "SMALLINT"
+-- error 42601: syntax error: unexpected SMALLINT
CREATE TABLE SOME (x INT);
--- error 42601: syntax error: near "SOME"
+-- error 42601: syntax error: unexpected SOME
CREATE TABLE SPECIFIC (x INT);
--- error 42601: syntax error: near "SPECIFIC"
+-- error 42601: syntax error: unexpected SPECIFIC
CREATE TABLE SPECIFICTYPE (x INT);
--- error 42601: syntax error: near "SPECIFICTYPE"
+-- error 42601: syntax error: unexpected SPECIFICTYPE
CREATE TABLE SQL (x INT);
--- error 42601: syntax error: near "SQL"
+-- error 42601: syntax error: unexpected SQL
CREATE TABLE SQLEXCEPTION (x INT);
--- error 42601: syntax error: near "SQLEXCEPTION"
+-- error 42601: syntax error: unexpected SQLEXCEPTION
CREATE TABLE SQLSTATE (x INT);
--- error 42601: syntax error: near "SQLSTATE"
+-- error 42601: syntax error: unexpected SQLSTATE
CREATE TABLE SQLWARNING (x INT);
--- error 42601: syntax error: near "SQLWARNING"
+-- error 42601: syntax error: unexpected SQLWARNING
CREATE TABLE SQRT (x INT);
--- error 42601: syntax error: near "SQRT"
+-- error 42601: syntax error: unexpected SQRT
CREATE TABLE START (x INT);
--- error 42601: syntax error: near "START"
+-- error 42601: syntax error: unexpected START
CREATE TABLE STATIC (x INT);
--- error 42601: syntax error: near "STATIC"
+-- error 42601: syntax error: unexpected STATIC
CREATE TABLE STDDEV_POP (x INT);
--- error 42601: syntax error: near "STDDEV_POP"
+-- error 42601: syntax error: unexpected STDDEV_POP
CREATE TABLE STDDEV_SAMP (x INT);
--- error 42601: syntax error: near "STDDEV_SAMP"
+-- error 42601: syntax error: unexpected STDDEV_SAMP
CREATE TABLE SUBMULTISET (x INT);
--- error 42601: syntax error: near "SUBMULTISET"
+-- error 42601: syntax error: unexpected SUBMULTISET
CREATE TABLE SUBSET (x INT);
--- error 42601: syntax error: near "SUBSET"
+-- error 42601: syntax error: unexpected SUBSET
CREATE TABLE SUBSTRING (x INT);
--- error 42601: syntax error: near "SUBSTRING"
+-- error 42601: syntax error: unexpected SUBSTRING
CREATE TABLE SUBSTRING_REGEX (x INT);
--- error 42601: syntax error: near "SUBSTRING_REGEX"
+-- error 42601: syntax error: unexpected SUBSTRING_REGEX
CREATE TABLE SUCCEEDS (x INT);
--- error 42601: syntax error: near "SUCCEEDS"
+-- error 42601: syntax error: unexpected SUCCEEDS
CREATE TABLE SUM (x INT);
--- error 42601: syntax error: near "SUM"
+-- error 42601: syntax error: unexpected SUM
CREATE TABLE SYMMETRIC (x INT);
--- error 42601: syntax error: near "SYMMETRIC"
+-- error 42601: syntax error: unexpected SYMMETRIC
CREATE TABLE SYSTEM (x INT);
--- error 42601: syntax error: near "SYSTEM"
+-- error 42601: syntax error: unexpected SYSTEM
CREATE TABLE SYSTEM_TIME (x INT);
--- error 42601: syntax error: near "SYSTEM_TIME"
+-- error 42601: syntax error: unexpected SYSTEM_TIME
CREATE TABLE SYSTEM_USER (x INT);
--- error 42601: syntax error: near "SYSTEM_USER"
+-- error 42601: syntax error: unexpected SYSTEM_USER
CREATE TABLE TABLE (x INT);
--- error 42601: syntax error: near "TABLE"
+-- error 42601: syntax error: unexpected TABLE
CREATE TABLE TABLESAMPLE (x INT);
--- error 42601: syntax error: near "TABLESAMPLE"
+-- error 42601: syntax error: unexpected TABLESAMPLE
CREATE TABLE TAN (x INT);
--- error 42601: syntax error: near "TAN"
+-- error 42601: syntax error: unexpected TAN
CREATE TABLE TANH (x INT);
--- error 42601: syntax error: near "TANH"
+-- error 42601: syntax error: unexpected TANH
CREATE TABLE THEN (x INT);
--- error 42601: syntax error: near "THEN"
+-- error 42601: syntax error: unexpected THEN
CREATE TABLE TIME (x INT);
--- error 42601: syntax error: near "TIME"
+-- error 42601: syntax error: unexpected TIME
CREATE TABLE TIMESTAMP (x INT);
--- error 42601: syntax error: near "TIMESTAMP"
+-- error 42601: syntax error: unexpected TIMESTAMP
CREATE TABLE TIMEZONE_HOUR (x INT);
--- error 42601: syntax error: near "TIMEZONE_HOUR"
+-- error 42601: syntax error: unexpected TIMEZONE_HOUR
CREATE TABLE TIMEZONE_MINUTE (x INT);
--- error 42601: syntax error: near "TIMEZONE_MINUTE"
+-- error 42601: syntax error: unexpected TIMEZONE_MINUTE
CREATE TABLE TO (x INT);
--- error 42601: syntax error: near "TO"
+-- error 42601: syntax error: unexpected TO
CREATE TABLE TRAILING (x INT);
--- error 42601: syntax error: near "TRAILING"
+-- error 42601: syntax error: unexpected TRAILING
CREATE TABLE TRANSLATE (x INT);
--- error 42601: syntax error: near "TRANSLATE"
+-- error 42601: syntax error: unexpected TRANSLATE
CREATE TABLE TRANSLATE_REGEX (x INT);
--- error 42601: syntax error: near "TRANSLATE_REGEX"
+-- error 42601: syntax error: unexpected TRANSLATE_REGEX
CREATE TABLE TRANSLATION (x INT);
--- error 42601: syntax error: near "TRANSLATION"
+-- error 42601: syntax error: unexpected TRANSLATION
CREATE TABLE TREAT (x INT);
--- error 42601: syntax error: near "TREAT"
+-- error 42601: syntax error: unexpected TREAT
CREATE TABLE TRIGGER (x INT);
--- error 42601: syntax error: near "TRIGGER"
+-- error 42601: syntax error: unexpected TRIGGER
CREATE TABLE TRIM (x INT);
--- error 42601: syntax error: near "TRIM"
+-- error 42601: syntax error: unexpected TRIM
CREATE TABLE TRIM_ARRAY (x INT);
--- error 42601: syntax error: near "TRIM_ARRAY"
+-- error 42601: syntax error: unexpected TRIM_ARRAY
CREATE TABLE TRUE (x INT);
--- error 42601: syntax error: near "TRUE"
+-- error 42601: syntax error: unexpected TRUE
CREATE TABLE TRUNCATE (x INT);
--- error 42601: syntax error: near "TRUNCATE"
+-- error 42601: syntax error: unexpected TRUNCATE
CREATE TABLE UESCAPE (x INT);
--- error 42601: syntax error: near "UESCAPE"
+-- error 42601: syntax error: unexpected UESCAPE
CREATE TABLE UNION (x INT);
--- error 42601: syntax error: near "UNION"
+-- error 42601: syntax error: unexpected UNION
CREATE TABLE UNIQUE (x INT);
--- error 42601: syntax error: near "UNIQUE"
+-- error 42601: syntax error: unexpected UNIQUE
CREATE TABLE UNKNOWN (x INT);
--- error 42601: syntax error: near "UNKNOWN"
+-- error 42601: syntax error: unexpected UNKNOWN
CREATE TABLE UNNEST (x INT);
--- error 42601: syntax error: near "UNNEST"
+-- error 42601: syntax error: unexpected UNNEST
CREATE TABLE UPDATE (x INT);
--- error 42601: syntax error: near "UPDATE"
+-- error 42601: syntax error: unexpected UPDATE
CREATE TABLE UPPER (x INT);
--- error 42601: syntax error: near "UPPER"
+-- error 42601: syntax error: unexpected UPPER
CREATE TABLE USER (x INT);
--- error 42601: syntax error: near "USER"
+-- error 42601: syntax error: unexpected USER
CREATE TABLE USING (x INT);
--- error 42601: syntax error: near "USING"
+-- error 42601: syntax error: unexpected USING
CREATE TABLE VALUE (x INT);
--- error 42601: syntax error: near "VALUE"
+-- error 42601: syntax error: unexpected VALUE
CREATE TABLE VALUES (x INT);
--- error 42601: syntax error: near "VALUES"
+-- error 42601: syntax error: unexpected VALUES
CREATE TABLE VALUE_OF (x INT);
--- error 42601: syntax error: near "VALUE_OF"
+-- error 42601: syntax error: unexpected VALUE_OF
CREATE TABLE VAR_POP (x INT);
--- error 42601: syntax error: near "VAR_POP"
+-- error 42601: syntax error: unexpected VAR_POP
CREATE TABLE VAR_SAMP (x INT);
--- error 42601: syntax error: near "VAR_SAMP"
+-- error 42601: syntax error: unexpected VAR_SAMP
CREATE TABLE VARBINARY (x INT);
--- error 42601: syntax error: near "VARBINARY"
+-- error 42601: syntax error: unexpected VARBINARY
CREATE TABLE VARCHAR (x INT);
--- error 42601: syntax error: near "VARCHAR"
+-- error 42601: syntax error: unexpected VARCHAR
CREATE TABLE VARYING (x INT);
--- error 42601: syntax error: near "VARYING"
+-- error 42601: syntax error: unexpected VARYING
CREATE TABLE VERSIONING (x INT);
--- error 42601: syntax error: near "VERSIONING"
+-- error 42601: syntax error: unexpected VERSIONING
CREATE TABLE WHEN (x INT);
--- error 42601: syntax error: near "WHEN"
+-- error 42601: syntax error: unexpected WHEN
CREATE TABLE WHENEVER (x INT);
--- error 42601: syntax error: near "WHENEVER"
+-- error 42601: syntax error: unexpected WHENEVER
CREATE TABLE WHERE (x INT);
--- error 42601: syntax error: near "WHERE"
+-- error 42601: syntax error: unexpected WHERE
CREATE TABLE WIDTH_BUCKET (x INT);
--- error 42601: syntax error: near "WIDTH_BUCKET"
+-- error 42601: syntax error: unexpected WIDTH_BUCKET
CREATE TABLE WINDOW (x INT);
--- error 42601: syntax error: near "WINDOW"
+-- error 42601: syntax error: unexpected WINDOW
CREATE TABLE WITH (x INT);
--- error 42601: syntax error: near "WITH"
+-- error 42601: syntax error: unexpected WITH
CREATE TABLE WITHIN (x INT);
--- error 42601: syntax error: near "WITHIN"
+-- error 42601: syntax error: unexpected WITHIN
CREATE TABLE WITHOUT (x INT);
--- error 42601: syntax error: near "WITHOUT"
+-- error 42601: syntax error: unexpected WITHOUT
CREATE TABLE YEAR (x INT);
--- error 42601: syntax error: near "YEAR"
+-- error 42601: syntax error: unexpected YEAR
CREATE TABLE abs (x INT);
--- error 42601: syntax error: near "ABS"
+-- error 42601: syntax error: unexpected ABS
CREATE TABLE foo (ABS INT);
--- error 42601: syntax error: near "ABS"
+-- error 42601: syntax error: unexpected ABS
CREATE TABLE foo (abs int);
--- error 42601: syntax error: near "ABS"
+-- error 42601: syntax error: unexpected ABS
diff --git a/tests/syntax_tokens.sql b/tests/syntax_tokens.sql
new file mode 100644
index 0000000..147aeac
--- /dev/null
+++ b/tests/syntax_tokens.sql
@@ -0,0 +1,14 @@
+-- # There are some special tokens that are required to reduce the ambiguity for
+-- # the parser. These tests ensure different whitespace combinations don't
+-- # interfere with that.
+
+-- # Reducing: "." "*". We don't care about the errors, only ensure there is no
+-- # syntax errors.
+SELECT x, count(*) FROM foo;
+SELECT x, count( * ) FROM foo;
+SELECT x, count (*) FROM foo;
+SELECT x, count ( *) FROM foo;
+-- error 42P01: no such table: ":memory:".PUBLIC.FOO
+-- error 42P01: no such table: ":memory:".PUBLIC.FOO
+-- error 42P01: no such table: ":memory:".PUBLIC.FOO
+-- error 42P01: no such table: ":memory:".PUBLIC.FOO
diff --git a/tests/values.sql b/tests/values.sql
index 6de92c7..193433b 100644
--- a/tests/values.sql
+++ b/tests/values.sql
@@ -23,9 +23,9 @@ SELECT * FROM (VALUES 1, 'foo', TRUE);
EXPLAIN SELECT * FROM (VALUES ROW(1, 'foo', TRUE));
-- EXPLAIN: $1:
--- EXPLAIN: VALUES (COL1 SMALLINT, COL2 CHARACTER VARYING(3), COL3 BOOLEAN) = ROW(1, 'foo', TRUE)
--- EXPLAIN: TABLE $1 (COL1 SMALLINT, COL2 CHARACTER VARYING(3), COL3 BOOLEAN)
--- EXPLAIN: EXPR ($1.COL1 SMALLINT, $1.COL2 CHARACTER VARYING(3), $1.COL3 BOOLEAN)
+-- EXPLAIN: VALUES (COL1 SMALLINT, COL2 CHARACTER(3), COL3 BOOLEAN) = ROW(1, 'foo', TRUE)
+-- EXPLAIN: TABLE $1 (COL1 SMALLINT, COL2 CHARACTER(3), COL3 BOOLEAN)
+-- EXPLAIN: EXPR ($1.COL1 SMALLINT, $1.COL2 CHARACTER(3), $1.COL3 BOOLEAN)
EXPLAIN SELECT * FROM (VALUES 1, 'foo', TRUE) AS t1 (abc, col2, "f");
-- EXPLAIN: T1:
@@ -35,9 +35,9 @@ EXPLAIN SELECT * FROM (VALUES 1, 'foo', TRUE) AS t1 (abc, col2, "f");
EXPLAIN SELECT * FROM (VALUES ROW(1, 'foo', TRUE)) AS t1 (abc, col2, "f");
-- EXPLAIN: T1:
--- EXPLAIN: VALUES (ABC SMALLINT, COL2 CHARACTER VARYING(3), "f" BOOLEAN) = ROW(1, 'foo', TRUE)
--- EXPLAIN: TABLE T1 (ABC SMALLINT, COL2 CHARACTER VARYING(3), "f" BOOLEAN)
--- EXPLAIN: EXPR (T1.ABC SMALLINT, T1.COL2 CHARACTER VARYING(3), T1."f" BOOLEAN)
+-- EXPLAIN: VALUES (ABC SMALLINT, COL2 CHARACTER(3), "f" BOOLEAN) = ROW(1, 'foo', TRUE)
+-- EXPLAIN: TABLE T1 (ABC SMALLINT, COL2 CHARACTER(3), "f" BOOLEAN)
+-- EXPLAIN: EXPR (T1.ABC SMALLINT, T1.COL2 CHARACTER(3), T1."f" BOOLEAN)
SELECT * FROM (VALUES 1, 'foo', TRUE) AS t1 (abc, col2, "f");
-- error 42601: syntax error: unknown column: T1.COL2
@@ -91,9 +91,9 @@ SELECT * FROM (VALUES ROW(123, 'hi'), ROW(456, 'there'));
EXPLAIN SELECT * FROM (VALUES ROW(123, 'hi'), ROW(456, 'there'));
-- EXPLAIN: $1:
--- EXPLAIN: VALUES (COL1 SMALLINT, COL2 CHARACTER VARYING(2)) = ROW(123, 'hi'), ROW(456, 'there')
--- EXPLAIN: TABLE $1 (COL1 SMALLINT, COL2 CHARACTER VARYING(2))
--- EXPLAIN: EXPR ($1.COL1 SMALLINT, $1.COL2 CHARACTER VARYING(2))
+-- EXPLAIN: VALUES (COL1 SMALLINT, COL2 CHARACTER(2)) = ROW(123, 'hi'), ROW(456, 'there')
+-- EXPLAIN: TABLE $1 (COL1 SMALLINT, COL2 CHARACTER(2))
+-- EXPLAIN: EXPR ($1.COL1 SMALLINT, $1.COL2 CHARACTER(2))
SELECT * FROM (VALUES ROW(123, 'hi'), ROW(456, 'there')) AS foo (bar, baz);
-- BAR: 123 BAZ: hi
@@ -102,9 +102,9 @@ SELECT * FROM (VALUES ROW(123, 'hi'), ROW(456, 'there')) AS foo (bar, baz);
EXPLAIN SELECT *
FROM (VALUES ROW(123, 'hi'), ROW(456, 'there')) AS foo (bar, baz);
-- EXPLAIN: FOO:
--- EXPLAIN: VALUES (BAR SMALLINT, BAZ CHARACTER VARYING(2)) = ROW(123, 'hi'), ROW(456, 'there')
--- EXPLAIN: TABLE FOO (BAR SMALLINT, BAZ CHARACTER VARYING(2))
--- EXPLAIN: EXPR (FOO.BAR SMALLINT, FOO.BAZ CHARACTER VARYING(2))
+-- EXPLAIN: VALUES (BAR SMALLINT, BAZ CHARACTER(2)) = ROW(123, 'hi'), ROW(456, 'there')
+-- EXPLAIN: TABLE FOO (BAR SMALLINT, BAZ CHARACTER(2))
+-- EXPLAIN: EXPR (FOO.BAR SMALLINT, FOO.BAZ CHARACTER(2))
SELECT * FROM (VALUES 1, 2) AS t1 (foo);
-- FOO: 1
diff --git a/tests/whitespace.sql b/tests/whitespace.sql
index 84985f0..70bed6e 100644
--- a/tests/whitespace.sql
+++ b/tests/whitespace.sql
@@ -119,4 +119,4 @@ VALUES123;
-- COL1: 123
VALUES123;
--- error 42601: syntax error: near "VALUESa123"
+-- error 42601: syntax error: unexpected identifier
diff --git a/vsql/connection.v b/vsql/connection.v
index b85b6c7..04734c2 100644
--- a/vsql/connection.v
+++ b/vsql/connection.v
@@ -34,17 +34,15 @@ mut:
catalogs map[string]&CatalogConnection
// funcs only needs to be initialized once on open()
funcs []Func
- // query_cache is maintained over file reopens.
- query_cache &QueryCache
// cast_rules are use for CAST() (see cast.v)
cast_rules map[string]CastFunc
// unary_operators and binary_operators are for operators (see operators.v)
unary_operators map[string]UnaryOperatorFunc
binary_operators map[string]BinaryOperatorFunc
- // current_schema is where to search for unquailified table names. It will
+ // current_schema is where to search for unqualified table names. It will
// have an initial value of 'PUBLIC'.
current_schema string
- // current_catalog (also known as the database). It will have an inital value
+ // current_catalog (also known as the database). It will have an initial value
// derived from the first database file loaded.
current_catalog string
pub mut:
@@ -113,7 +111,6 @@ fn open_connection(path string, options ConnectionOptions) !&Connection {
catalog_name := catalog_name_from_path(path)
mut conn := &Connection{
- query_cache: options.query_cache
current_catalog: catalog_name
current_schema: default_schema_name
now: default_now
@@ -244,14 +241,27 @@ fn (mut conn CatalogConnection) release_read_connection() {
// with different provided parameters.
pub fn (mut conn Connection) prepare(sql_stmt string) !PreparedStmt {
t := start_timer()
- stmt, params, explain := conn.query_cache.parse(sql_stmt) or {
- mut catalog := conn.catalog()
- catalog.storage.transaction_aborted()
- return err
+
+ mut tokens := tokenize(sql_stmt)
+
+ // EXPLAIN is super helpful, but not part of the SQL standard so we only
+ // treat it as a prefix that is trimmed off before parsing.
+ mut explain := false
+ if tokens[0].sym.v is IdentifierChain {
+ if tokens[0].sym.v.identifier.to_upper() == 'EXPLAIN' {
+ explain = true
+ tokens = tokens[1..].clone()
+ }
}
+
+ mut lexer := Lexer{tokens, 0}
+ mut parser := yy_new_parser()
+ parser.parse(mut lexer)!
+
+ stmt := (parser as YYParserImpl).lval.v as Stmt
elapsed_parse := t.elapsed()
- return PreparedStmt{stmt, params, explain, &conn, elapsed_parse}
+ return PreparedStmt{stmt, map[string]Value{}, explain, &conn, elapsed_parse}
}
// query executes a statement. If there is a result set it will be returned.
@@ -344,7 +354,10 @@ pub fn (mut conn Connection) register_function(prototype string, func fn ([]Valu
pub fn (mut conn Connection) register_virtual_table(create_table string, data VirtualTableProviderFn) ! {
// Registering virtual tables does not need use query cache.
mut tokens := tokenize(create_table)
- stmt := parse(tokens)!
+ mut lexer := Lexer{tokens, 0}
+ mut parser := yy_new_parser()
+ parser.parse(mut lexer)!
+ stmt := (parser as YYParserImpl).lval.v as Stmt
if stmt is TableDefinition {
mut table_name := conn.resolve_schema_identifier(stmt.table_name)!
@@ -481,14 +494,6 @@ fn (mut conn Connection) resolve_table_identifier(identifier Identifier, allow_v
// default_connection_options() as a starting point and modify the attributes.
pub struct ConnectionOptions {
pub mut:
- // query_cache contains the precompiled prepared statements that can be
- // reused. This makes execution much faster as parsing the SQL is extremely
- // expensive.
- //
- // By default each connection will be given its own query cache. However,
- // you can safely share a single cache over multiple connections and you are
- // encouraged to do so.
- query_cache &QueryCache = unsafe { nil }
// Warning: This only works for :memory: databases. Configuring it for
// file-based databases will either be ignored or causes crashes.
page_size int
@@ -518,9 +523,8 @@ pub mut:
// the correct base to provide your own option overrides. See ConnectionOptions.
pub fn default_connection_options() ConnectionOptions {
return ConnectionOptions{
- query_cache: new_query_cache()
- page_size: 4096
- mutex: sync.new_rwmutex()
+ page_size: 4096
+ mutex: sync.new_rwmutex()
}
}
diff --git a/vsql/earley.v b/vsql/earley.v
deleted file mode 100644
index 20455e0..0000000
--- a/vsql/earley.v
+++ /dev/null
@@ -1,364 +0,0 @@
-// earley.v is an implementation of an Earley parser that was heavily adapted
-// from: https://github.com/tomerfiliba/tau/blob/master/earley3.py.
-
-module vsql
-
-import strings
-
-@[heap]
-struct EarleyRuleOrString {
- rule &EarleyRule
- str string
-}
-
-fn (o &EarleyRuleOrString) str() string {
- if unsafe { o.rule != 0 } {
- return (*o.rule).str()
- }
-
- return o.str
-}
-
-@[heap]
-struct EarleyProduction {
- terms []&EarleyRuleOrString
-}
-
-fn (prod &EarleyProduction) index() string {
- mut elems := []string{}
- for t in prod.terms {
- elems << t.str()
- }
- return elems.join(',')
-}
-
-@[heap]
-struct EarleyRule {
- name string
-mut:
- productions []&EarleyProduction
-}
-
-fn (rule &EarleyRule) str() string {
- return rule.name
-}
-
-@[heap]
-struct EarleyState {
- name string
- production &EarleyProduction
- dot_index int
- start_column &EarleyColumn
-mut:
- end_column &EarleyColumn
- rules []&EarleyRule
-}
-
-fn new_earley_state(name string, production &EarleyProduction, dot_index int, start_column &EarleyColumn) &EarleyState {
- mut rules := []&EarleyRule{}
- for t in production.terms {
- if unsafe { t.rule != 0 } {
- rules << t.rule
- }
- }
-
- return &EarleyState{
- name: name
- production: production
- start_column: start_column
- dot_index: dot_index
- rules: rules
- end_column: unsafe { 0 }
- }
-}
-
-fn (state &EarleyState) index() string {
- return '${state.name} ${state.production.index()} ${state.dot_index} ${state.start_column}'
-}
-
-fn (state &EarleyState) str() string {
- mut terms := []string{}
- for p in state.production.terms {
- terms << p.str()
- }
-
- terms.insert(state.dot_index, '$')
-
- return '${state.name} -> ${terms.join(' ')} [${state.start_column}-${state.end_column}]'
-}
-
-fn (state &EarleyState) eq(other &EarleyState) bool {
- return state.name == other.name && state.production == other.production
- && state.dot_index == other.dot_index && state.start_column == other.start_column
-}
-
-fn (state &EarleyState) completed() bool {
- return state.dot_index >= state.production.terms.len
-}
-
-fn (state &EarleyState) next_term() &EarleyRuleOrString {
- if state.completed() {
- return &EarleyRuleOrString{
- rule: unsafe { 0 }
- }
- }
-
- return state.production.terms[state.dot_index]
-}
-
-@[heap]
-struct Set {
-mut:
- elems map[string]bool
-}
-
-fn (s &Set) exists(v string) bool {
- return v in s.elems
-}
-
-fn (mut s Set) add(v string) {
- if s.exists(v) {
- return
- }
-
- s.elems[v] = true
-}
-
-@[heap]
-struct EarleyColumn {
- index int
- token string
- value string
-mut:
- states []&EarleyState
- unique &Set
-}
-
-fn new_earley_column(index int, token string, value string) &EarleyColumn {
- return &EarleyColumn{
- index: index
- token: token
- value: value
- unique: &Set{}
- }
-}
-
-fn (col &EarleyColumn) str() string {
- return '${col.index}'
-}
-
-fn (col &EarleyColumn) repr() string {
- return '${col.token}:${col.value}'
-}
-
-fn (mut col EarleyColumn) add(mut state EarleyState) bool {
- if !col.unique.exists(state.index()) {
- col.unique.add(state.index())
- state.end_column = col
- col.states << state
-
- return true
- }
-
- return false
-}
-
-fn (col &EarleyColumn) print() {
- println('[${col.index}] ${col.token}')
- println(strings.repeat_string('=', 35))
- for s in col.states {
- println(s.str())
- }
- println('')
-}
-
-@[heap]
-struct EarleyNode {
- value &EarleyState
- children []&EarleyNode
-}
-
-fn (node &EarleyNode) print(level int) {
- println(strings.repeat_string(' ', level) + node.value.str())
- for child in node.children {
- child.print(level + 1)
- }
-}
-
-fn (node &EarleyNode) max() int {
- mut max := 0
- for child in node.children {
- child_max := child.max()
- if child_max > max {
- max = child_max
- }
- }
-
- if node.value.end_column.index > max {
- max = node.value.end_column.index
- }
-
- return max
-}
-
-fn predict(mut col EarleyColumn, rule &EarleyRule) {
- for prod in rule.productions {
- col.add(mut new_earley_state(rule.name, prod, 0, col))
- }
-}
-
-fn scan(mut col EarleyColumn, state &EarleyState, token string) {
- if token != col.token {
- return
- }
-
- col.add(mut new_earley_state(state.name, state.production, state.dot_index + 1, state.start_column))
-}
-
-fn complete(mut col EarleyColumn, state &EarleyState) {
- if !state.completed() {
- return
- }
-
- for st in state.start_column.states {
- term := st.next_term()
-
- if unsafe { term.rule == 0 } {
- continue
- }
-
- if term.rule.name == state.name {
- col.add(mut new_earley_state(st.name, st.production, st.dot_index + 1, st.start_column))
- }
- }
-}
-
-fn parse(tokens []Token) !Stmt {
- mut columns := tokenize_earley_columns(tokens)
- mut grammar := get_grammar()
-
- q0 := parse_earley(unsafe { grammar[''] }, mut columns)!
-
- trees := build_trees(q0)
- if trees.len == 0 {
- panic(q0.end_column)
- }
-
- // This is helpful for debugging. Enable it to see the tree (or at least the
- // first resolved tree since there could be multiple solutions in an
- // ambiguous grammar)
- //
- // trees[0].print(0)
-
- return (parse_ast(trees[0])!)[0] as Stmt
-}
-
-fn parse_earley(rule &EarleyRule, mut table []&EarleyColumn) !&EarleyState {
- table[0].add(mut new_earley_state('start', &EarleyProduction{[
- &EarleyRuleOrString{ rule: rule },
- ]}, 0, table[0]))
-
- for i, mut col in table {
- for state in col.states {
- if state.completed() {
- complete(mut *col, state)
- } else {
- term := state.next_term()
- if unsafe { term.rule != 0 } {
- predict(mut *col, term.rule)
- } else if i + 1 < table.len {
- scan(mut table[i + 1], state, term.str)
- }
- }
- }
- }
-
- // find gamma rule in last table column (otherwise fail)
- for st in table[table.len - 1].states {
- if st.name == 'start' && st.completed() {
- return st
- }
- }
-
- // Find the deepest resolved path which is our best guess of where the
- // syntax error is located.
- mut max := 0
- for col in table {
- for st in col.states {
- t := build_trees(st)
- if t.len > 0 {
- m := t[0].max()
- if m > max {
- max = m
- }
- }
- }
- }
-
- if max + 1 >= table.len {
- max = table.len - 2
- }
-
- // +1 here because we add an empty token at the start of the stream.
- return sqlstate_42601('near "${table[max + 1].value}"')
-}
-
-fn build_trees(state &EarleyState) []&EarleyNode {
- return build_trees_helper([]&EarleyNode{}, state, state.rules.len - 1, state.end_column)
-}
-
-fn build_trees_helper(children []&EarleyNode, state &EarleyState, rule_index int, end_column &EarleyColumn) []&EarleyNode {
- mut start_column := new_earley_column(0, '', '')
- if rule_index < 0 {
- return [&EarleyNode{state, children}]
- } else if rule_index == 0 {
- start_column = state.start_column
- }
-
- rule := state.rules[rule_index]
- mut outputs := []&EarleyNode{}
- for st in end_column.states {
- if st.eq(state) || !st.completed() || st.name != rule.name {
- continue
- }
- if start_column.index != 0 && st.start_column != start_column {
- continue
- }
- for sub_tree in build_trees(st) {
- mut new_children := [sub_tree]
- for child in children {
- new_children << child
- }
-
- for node in build_trees_helper(new_children, state, rule_index - 1, st.start_column) {
- outputs << node
- }
- }
- }
-
- return outputs
-}
-
-fn tokenize_earley_columns(tokens []Token) []&EarleyColumn {
- mut table := [new_earley_column(0, '', '')]
-
- for i, token in tokens {
- v := token.value
-
- if tokens[i].kind == .literal_string {
- table << new_earley_column(i + 1, '^string', v)
- } else if tokens[i].kind == .keyword {
- table << new_earley_column(i + 1, v, v)
- } else if v[0].is_digit() {
- table << new_earley_column(i + 1, '^integer', v)
- } else if v == '(' || v == ')' || v == ',' || v == '=' || v == '<>' || v == '.' || v == '+'
- || v == '-' || v == '*' || v == '/' || v == '>' || v == '<' || v == '>=' || v == '<='
- || v == '||' || v == ':' {
- table << new_earley_column(i + 1, v, v)
- } else {
- table << new_earley_column(i + 1, '^identifier', v)
- }
- }
-
- return table
-}
diff --git a/vsql/grammar.y b/vsql/grammar.y
new file mode 100644
index 0000000..f6a4ba5
--- /dev/null
+++ b/vsql/grammar.y
@@ -0,0 +1,695 @@
+%{
+
+module vsql
+
+import math
+
+%}
+
+// non-reserved words
+%token A
+%token ABSOLUTE
+%token ACTION
+%token ADA
+%token ADD
+%token ADMIN
+%token AFTER
+%token ALWAYS
+%token ASC
+%token ASSERTION
+%token ASSIGNMENT
+%token ATTRIBUTE
+%token ATTRIBUTES
+%token BEFORE
+%token BERNOULLI
+%token BREADTH
+%token C
+%token CASCADE
+%token CATALOG
+%token CATALOG_NAME
+%token CHAIN
+%token CHAINING
+%token CHARACTER_SET_CATALOG
+%token CHARACTER_SET_NAME
+%token CHARACTER_SET_SCHEMA
+%token CHARACTERISTICS
+%token CHARACTERS
+%token CLASS_ORIGIN
+%token COBOL
+%token COLLATION
+%token COLLATION_CATALOG
+%token COLLATION_NAME
+%token COLLATION_SCHEMA
+%token COLUMNS
+%token COLUMN_NAME
+%token COMMAND_FUNCTION
+%token COMMAND_FUNCTION_CODE
+%token COMMITTED
+%token CONDITIONAL
+%token CONDITION_NUMBER
+%token CONNECTION
+%token CONNECTION_NAME
+%token CONSTRAINT_CATALOG
+%token CONSTRAINT_NAME
+%token CONSTRAINT_SCHEMA
+%token CONSTRAINTS
+%token CONSTRUCTOR
+%token CONTINUE
+%token CURSOR_NAME
+%token DATA
+%token DATETIME_INTERVAL_CODE
+%token DATETIME_INTERVAL_PRECISION
+%token DEFAULTS
+%token DEFERRABLE
+%token DEFERRED
+%token DEFINED
+%token DEFINER
+%token DEGREE
+%token DEPTH
+%token DERIVED
+%token DESC
+%token DESCRIBE_CATALOG
+%token DESCRIBE_NAME
+%token DESCRIBE_PROCEDURE_SPECIFIC_CATALOG
+%token DESCRIBE_PROCEDURE_SPECIFIC_NAME
+%token DESCRIBE_PROCEDURE_SPECIFIC_SCHEMA
+%token DESCRIBE_SCHEMA
+%token DESCRIPTOR
+%token DIAGNOSTICS
+%token DISPATCH
+%token DOMAIN
+%token DYNAMIC_FUNCTION
+%token DYNAMIC_FUNCTION_CODE
+%token ENCODING
+%token ENFORCED
+%token ERROR
+%token EXCLUDE
+%token EXCLUDING
+%token EXPRESSION
+%token FINAL
+%token FINISH
+%token FINISH_CATALOG
+%token FINISH_NAME
+%token FINISH_PROCEDURE_SPECIFIC_CATALOG
+%token FINISH_PROCEDURE_SPECIFIC_NAME
+%token FINISH_PROCEDURE_SPECIFIC_SCHEMA
+%token FINISH_SCHEMA
+%token FIRST
+%token FLAG
+%token FOLLOWING
+%token FORMAT
+%token FORTRAN
+%token FOUND
+%token FULFILL
+%token FULFILL_CATALOG
+%token FULFILL_NAME
+%token FULFILL_PROCEDURE_SPECIFIC_CATALOG
+%token FULFILL_PROCEDURE_SPECIFIC_NAME
+%token FULFILL_PROCEDURE_SPECIFIC_SCHEMA
+%token FULFILL_SCHEMA
+%token G
+%token GENERAL
+%token GENERATED
+%token GO
+%token GOTO
+%token GRANTED
+%token HAS_PASS_THROUGH_COLUMNS
+%token HAS_PASS_THRU_COLS
+%token HIERARCHY
+%token IGNORE
+%token IMMEDIATE
+%token IMMEDIATELY
+%token IMPLEMENTATION
+%token INCLUDING
+%token INCREMENT
+%token INITIALLY
+%token INPUT
+%token INSTANCE
+%token INSTANTIABLE
+%token INSTEAD
+%token INVOKER
+%token ISOLATION
+%token IS_PRUNABLE
+%token JSON
+%token K
+%token KEEP
+%token KEY
+%token KEYS
+%token KEY_MEMBER
+%token KEY_TYPE
+%token LAST
+%token LENGTH
+%token LEVEL
+%token LOCATOR
+%token M
+%token MAP
+%token MATCHED
+%token MAXVALUE
+%token MESSAGE_LENGTH
+%token MESSAGE_OCTET_LENGTH
+%token MESSAGE_TEXT
+%token MINVALUE
+%token MORE
+%token MUMPS
+%token NAME
+%token NAMES
+%token NESTED
+%token NESTING
+%token NEXT
+%token NFC
+%token NFD
+%token NFKC
+%token NFKD
+%token NORMALIZED
+%token NULLABLE
+%token NULLS
+%token NUMBER
+%token OBJECT
+%token OCTETS
+%token OPTION
+%token OPTIONS
+%token ORDERING
+%token ORDINALITY
+%token OTHERS
+%token OUTPUT
+%token OVERFLOW
+%token OVERRIDING
+%token P
+%token PAD
+%token PARAMETER_MODE
+%token PARAMETER_NAME
+%token PARAMETER_ORDINAL_POSITION
+%token PARAMETER_SPECIFIC_CATALOG
+%token PARAMETER_SPECIFIC_NAME
+%token PARAMETER_SPECIFIC_SCHEMA
+%token PARTIAL
+%token PASCAL
+%token PASS
+%token PASSING
+%token PAST
+%token PATH
+%token PLACING
+%token PLAN
+%token PLI
+%token PRECEDING
+%token PRESERVE
+%token PRIOR
+%token PRIVATE
+%token PRIVATE_PARAMETERS
+%token PRIVATE_PARAMS_S
+%token PRIVILEGES
+%token PRUNE
+%token PUBLIC
+%token QUOTES
+%token READ
+%token RELATIVE
+%token REPEATABLE
+%token RESPECT
+%token RESTART
+%token RESTRICT
+%token RETURNED_CARDINALITY
+%token RETURNED_LENGTH
+%token RETURNED_OCTET_LENGTH
+%token RETURNED_SQLSTATE
+%token RETURNING
+%token RETURNS_ONLY_PASS_THROUGH
+%token RET_ONLY_PASS_THRU
+%token ROLE
+%token ROUTINE
+%token ROUTINE_CATALOG
+%token ROUTINE_NAME
+%token ROUTINE_SCHEMA
+%token ROW_COUNT
+%token SCALAR
+%token SCALE
+%token SCHEMA
+%token SCHEMA_NAME
+%token SCOPE_CATALOG
+%token SCOPE_NAME
+%token SCOPE_SCHEMA
+%token SECTION
+%token SECURITY
+%token SELF
+%token SEQUENCE
+%token SERIALIZABLE
+%token SERVER_NAME
+%token SESSION
+%token SETS
+%token SIMPLE
+%token SIZE
+%token SOURCE
+%token SPACE
+%token SPECIFIC_NAME
+%token START_CATALOG
+%token START_NAME
+%token START_PROCEDURE_SPECIFIC_CATALOG
+%token START_PROCEDURE_SPECIFIC_NAME
+%token START_PROCEDURE_SPECIFIC_SCHEMA
+%token START_SCHEMA
+%token STATE
+%token STATEMENT
+%token STRING
+%token STRUCTURE
+%token STYLE
+%token SUBCLASS_ORIGIN
+%token T
+%token TABLE_NAME
+%token TABLE_SEMANTICS
+%token TEMPORARY
+%token THROUGH
+%token TIES
+%token TOP_LEVEL_COUNT
+%token TRANSACTION
+%token TRANSACTION_ACTIVE
+%token TRANSACTIONS_COMMITTED
+%token TRANSACTIONS_ROLLED_BACK
+%token TRANSFORM
+%token TRANSFORMS
+%token TRIGGER_CATALOG
+%token TRIGGER_NAME
+%token TRIGGER_SCHEMA
+%token TYPE
+%token UNBOUNDED
+%token UNCOMMITTED
+%token UNCONDITIONAL
+%token UNDER
+%token UNNAMED
+%token USAGE
+%token USER_DEFINED_TYPE_CATALOG
+%token USER_DEFINED_TYPE_CODE
+%token USER_DEFINED_TYPE_NAME
+%token USER_DEFINED_TYPE_SCHEMA
+%token UTF16
+%token UTF32
+%token UTF8
+%token VIEW
+%token WORK
+%token WRAPPER
+%token WRITE
+%token ZONE
+
+// reserved words
+%token ABS
+%token ACOS
+%token ALL
+%token ALLOCATE
+%token ALTER
+%token AND
+%token ANY
+%token ARE
+%token ARRAY
+%token ARRAY_AGG
+%token ARRAY_MAX_CARDINALITY
+%token AS
+%token ASENSITIVE
+%token ASIN
+%token ASYMMETRIC
+%token AT
+%token ATAN
+%token ATOMIC
+%token AUTHORIZATION
+%token AVG
+%token BEGIN
+%token BEGIN_FRAME
+%token BEGIN_PARTITION
+%token BETWEEN
+%token BIGINT
+%token BINARY
+%token BLOB
+%token BOOLEAN
+%token BOTH
+%token BY
+%token CALL
+%token CALLED
+%token CARDINALITY
+%token CASCADED
+%token CASE
+%token CAST
+%token CEIL
+%token CEILING
+%token CHAR
+%token CHAR_LENGTH
+%token CHARACTER
+%token CHARACTER_LENGTH
+%token CHECK
+%token CLASSIFIER
+%token CLOB
+%token CLOSE
+%token COALESCE
+%token COLLATE
+%token COLLECT
+%token COLUMN
+%token COMMIT
+%token CONDITION
+%token CONNECT
+%token CONSTRAINT
+%token CONTAINS
+%token CONVERT
+%token COPY
+%token CORR
+%token CORRESPONDING
+%token COS
+%token COSH
+%token COUNT
+%token COVAR_POP
+%token COVAR_SAMP
+%token CREATE
+%token CROSS
+%token CUBE
+%token CUME_DIST
+%token CURRENT
+%token CURRENT_CATALOG
+%token CURRENT_DATE
+%token CURRENT_DEFAULT_TRANSFORM_GROUP
+%token CURRENT_PATH
+%token CURRENT_ROLE
+%token CURRENT_ROW
+%token CURRENT_SCHEMA
+%token CURRENT_TIME
+%token CURRENT_TIMESTAMP
+%token CURRENT_PATH
+%token CURRENT_ROLE
+%token CURRENT_TRANSFORM_GROUP_FOR_TYPE
+%token CURRENT_USER
+%token CURSOR
+%token CYCLE
+%token DATE
+%token DAY
+%token DEALLOCATE
+%token DEC
+%token DECIMAL
+%token DECFLOAT
+%token DECLARE
+%token DEFAULT
+%token DEFINE
+%token DELETE
+%token DENSE_RANK
+%token DEREF
+%token DESCRIBE
+%token DETERMINISTIC
+%token DISCONNECT
+%token DISTINCT
+%token DOUBLE
+%token DROP
+%token DYNAMIC
+%token EACH
+%token ELEMENT
+%token ELSE
+%token EMPTY
+%token END
+%token END_FRAME
+%token END_PARTITION
+//%token END-EXEC
+%token EQUALS
+%token ESCAPE
+%token EVERY
+%token EXCEPT
+%token EXEC
+%token EXECUTE
+%token EXISTS
+%token EXP
+%token EXTERNAL
+%token EXTRACT
+%token FALSE
+%token FETCH
+%token FILTER
+%token FIRST_VALUE
+%token FLOAT
+%token FLOOR
+%token FOR
+%token FOREIGN
+%token FRAME_ROW
+%token FREE
+%token FROM
+%token FULL
+%token FUNCTION
+%token FUSION
+%token GET
+%token GLOBAL
+%token GRANT
+%token GROUP
+%token GROUPING
+%token GROUPS
+%token HAVING
+%token HOLD
+%token HOUR
+%token IDENTITY
+%token IN
+%token INDICATOR
+%token INITIAL
+%token INNER
+%token INOUT
+%token INSENSITIVE
+%token INSERT
+%token INT
+%token INTEGER
+%token INTERSECT
+%token INTERSECTION
+%token INTERVAL
+%token INTO
+%token IS
+%token JOIN
+%token JSON_ARRAY
+%token JSON_ARRAYAGG
+%token JSON_EXISTS
+%token JSON_OBJECT
+%token JSON_OBJECTAGG
+%token JSON_QUERY
+%token JSON_TABLE
+%token JSON_TABLE_PRIMITIVE
+%token JSON_VALUE
+%token LAG
+%token LANGUAGE
+%token LARGE
+%token LAST_VALUE
+%token LATERAL
+%token LEAD
+%token LEADING
+%token LEFT
+%token LIKE
+%token LIKE_REGEX
+%token LISTAGG
+%token LN
+%token LOCAL
+%token LOCALTIME
+%token LOCALTIMESTAMP
+%token LOG
+%token LOG10
+%token LOWER
+%token MATCH
+%token MATCH_NUMBER
+%token MATCH_RECOGNIZE
+%token MATCHES
+%token MAX
+%token MEMBER
+%token MERGE
+%token METHOD
+%token MIN
+%token MINUTE
+%token MOD
+%token MODIFIES
+%token MODULE
+%token MONTH
+%token MULTISET
+%token NATIONAL
+%token NATURAL
+%token NCHAR
+%token NCLOB
+%token NEW
+%token NO
+%token NONE
+%token NORMALIZE
+%token NOT
+%token NTH_VALUE
+%token NTILE
+%token NULL
+%token NULLIF
+%token NUMERIC
+%token OCTET_LENGTH
+%token OCCURRENCES_REGEX
+%token OF
+%token OFFSET
+%token OLD
+%token OMIT
+%token ON
+%token ONE
+%token ONLY
+%token OPEN
+%token OR
+%token ORDER
+%token OUT
+%token OUTER
+%token OVER
+%token OVERLAPS
+%token OVERLAY
+%token PARAMETER
+%token PARTITION
+%token PATTERN
+%token PER
+%token PERCENT
+%token PERCENT_RANK
+%token PERCENTILE_CONT
+%token PERCENTILE_DISC
+%token PERIOD
+%token PORTION
+%token POSITION
+%token POSITION_REGEX
+%token POWER
+%token PRECEDES
+%token PRECISION
+%token PREPARE
+%token PRIMARY
+%token PROCEDURE
+%token PTF
+%token RANGE
+%token RANK
+%token READS
+%token REAL
+%token RECURSIVE
+%token REF
+%token REFERENCES
+%token REFERENCING
+%token REGR_AVGX
+%token REGR_AVGY
+%token REGR_COUNT
+%token REGR_INTERCEPT
+%token REGR_R2
+%token REGR_SLOPE
+%token REGR_SXX
+%token REGR_SXY
+%token REGR_SYY
+%token RELEASE
+%token RESULT
+%token RETURN
+%token RETURNS
+%token REVOKE
+%token RIGHT
+%token ROLLBACK
+%token ROLLUP
+%token ROW
+%token ROW_NUMBER
+%token ROWS
+%token RUNNING
+%token SAVEPOINT
+%token SCOPE
+%token SCROLL
+%token SEARCH
+%token SECOND
+%token SEEK
+%token SELECT
+%token SENSITIVE
+%token SESSION_USER
+%token SET
+%token SHOW
+%token SIMILAR
+%token SIN
+%token SINH
+%token SKIP
+%token SMALLINT
+%token SOME
+%token SPECIFIC
+%token SPECIFICTYPE
+%token SQL
+%token SQLEXCEPTION
+%token SQLSTATE
+%token SQLWARNING
+%token SQRT
+%token START
+%token STATIC
+%token STDDEV_POP
+%token STDDEV_SAMP
+%token SUBMULTISET
+%token SUBSET
+%token SUBSTRING
+%token SUBSTRING_REGEX
+%token SUCCEEDS
+%token SUM
+%token SYMMETRIC
+%token SYSTEM
+%token SYSTEM_TIME
+%token SYSTEM_USER
+%token TABLE
+%token TABLESAMPLE
+%token TAN
+%token TANH
+%token THEN
+%token TIME
+%token TIMESTAMP
+%token TIMEZONE_HOUR
+%token TIMEZONE_MINUTE
+%token TO
+%token TRAILING
+%token TRANSLATE
+%token TRANSLATE_REGEX
+%token TRANSLATION
+%token TREAT
+%token TRIGGER
+%token TRIM
+%token TRIM_ARRAY
+%token TRUE
+%token TRUNCATE
+%token UESCAPE
+%token UNION
+%token UNIQUE
+%token UNKNOWN
+%token UNNEST
+%token UPDATE
+%token UPPER
+%token USER
+%token USING
+%token VALUE
+%token VALUES
+%token VALUE_OF
+%token VAR_POP
+%token VAR_SAMP
+%token VARBINARY
+%token VARCHAR
+%token VARYING
+%token VERSIONING
+%token WHEN
+%token WHENEVER
+%token WHERE
+%token WIDTH_BUCKET
+%token WINDOW
+%token WITH
+%token WITHIN
+%token WITHOUT
+%token YEAR
+
+// operators
+%token OPERATOR_EQUALS OPERATOR_LEFT_PAREN OPERATOR_RIGHT_PAREN;
+%token OPERATOR_ASTERISK OPERATOR_COMMA OPERATOR_PLUS OPERATOR_MINUS;
+%token OPERATOR_PERIOD OPERATOR_SOLIDUS OPERATOR_COLON OPERATOR_LESS_THAN;
+%token OPERATOR_GREATER_THAN OPERATOR_DOUBLE_PIPE OPERATOR_NOT_EQUALS;
+%token OPERATOR_GREATER_EQUALS OPERATOR_LESS_EQUALS OPERATOR_SEMICOLON;
+
+%token OPERATOR_PERIOD_ASTERISK OPERATOR_LEFT_PAREN_ASTERISK;
+
+// literals
+%token LITERAL_IDENTIFIER LITERAL_STRING LITERAL_NUMBER;
+%token E;
+
+// pseudo keywords
+%token IS_TRUE IS_FALSE IS_UNKNOWN IS_NOT_TRUE IS_NOT_FALSE IS_NOT_UNKNOWN;
+
+%start start;
+
+%%
+
+// This is a special case that uses `yyrcvr.lval` to make sure the parser
+// captures the final result.
+start:
+ preparable_statement { yyrcvr.lval.v = $1.v as Stmt }
+
+// This is from 6.35. I have no idea why this needs to be placed higher than
+// other rules, but without doing this it will report that "datetime_primary"
+// cannot be reduced.
+datetime_primary:
+ value_expression_primary {
+ $$.v = DatetimePrimary($1.v as ValueExpressionPrimary)
+ }
+| datetime_value_function {
+ $$.v = DatetimePrimary($1.v as DatetimeValueFunction)
+ }
+
+%%
diff --git a/vsql/lexer.v b/vsql/lexer.v
index 97510ca..313da3e 100644
--- a/vsql/lexer.v
+++ b/vsql/lexer.v
@@ -3,36 +3,166 @@
module vsql
-// Except for the eof and the keywords, the other tokens use the names described
-// in the SQL standard.
-enum TokenKind {
- asterisk // ::= *
- colon // ::= :
- comma // ::= ,
- concatenation_operator // ::= ||
- equals_operator // ::= =
- greater_than_operator // ::= >
- greater_than_or_equals_operator // ::= >=
- keyword
- left_paren // ::= (
- less_than_operator // ::= <
- less_than_or_equals_operator // ::= <=
- literal_identifier // foo or "foo" (delimited)
- literal_number // 123
- literal_string // 'hello'
- minus_sign // ::= -
- not_equals_operator // ::= <>
- period // ::= .
- plus_sign // ::= +
- right_paren // ::= )
- semicolon // ::= ;
- solidus // ::= /
+// YYSym allows for every possible type that needs to pass through $$ rules in
+// the grammar. If V ever supports some kind of "any" type this would be a
+// little easier.
+type YYSym = AggregateFunction
+ | AggregateFunctionCount
+ | AlterSequenceGeneratorStatement
+ | BetweenPredicate
+ | BooleanPredicand
+ | BooleanPrimary
+ | BooleanTerm
+ | BooleanTest
+ | BooleanValueExpression
+ | CaseExpression
+ | CastOperand
+ | CastSpecification
+ | CharacterLikePredicate
+ | CharacterPrimary
+ | CharacterSubstringFunction
+ | CharacterValueExpression
+ | CharacterValueFunction
+ | Column
+ | CommonValueExpression
+ | ComparisonPredicate
+ | ComparisonPredicatePart2
+ | Concatenation
+ | ContextuallyTypedRowValueConstructor
+ | ContextuallyTypedRowValueConstructorElement
+ | Correlation
+ | CurrentDate
+ | CurrentTime
+ | CurrentTimestamp
+ | DatetimePrimary
+ | DatetimeValueFunction
+ | DerivedColumn
+ | ExplicitRowValueConstructor
+ | GeneralValueSpecification
+ | Identifier
+ | IdentifierChain
+ | InsertStatement
+ | LocalTime
+ | LocalTimestamp
+ | NextValueExpression
+ | NonparenthesizedValueExpressionPrimary
+ | NullPredicate
+ | NullSpecification
+ | NumericPrimary
+ | NumericValueExpression
+ | ParenthesizedValueExpression
+ | Predicate
+ | QualifiedAsteriskExpr
+ | QualifiedJoin
+ | QueryExpression
+ | QuerySpecification
+ | RoutineInvocation
+ | RowValueConstructor
+ | RowValueConstructorPredicand
+ | SelectList
+ | SequenceGeneratorDefinition
+ | SequenceGeneratorIncrementByOption
+ | SequenceGeneratorMaxvalueOption
+ | SequenceGeneratorMinvalueOption
+ | SequenceGeneratorOption
+ | SequenceGeneratorRestartOption
+ | SequenceGeneratorStartWithOption
+ | SetSchemaStatement
+ | SimilarPredicate
+ | SimpleTable
+ | SortSpecification
+ | Stmt
+ | Table
+ | TableElement
+ | TableExpression
+ | TablePrimary
+ | TableReference
+ | Term
+ | TrimFunction
+ | Type
+ | UniqueConstraintDefinition
+ | UpdateSource
+ | Value
+ | ValueExpression
+ | ValueExpressionPrimary
+ | ValueSpecification
+ | []ContextuallyTypedRowValueConstructor
+ | []ContextuallyTypedRowValueConstructorElement
+ | []DerivedColumn
+ | []Identifier
+ | []RowValueConstructor
+ | []SequenceGeneratorOption
+ | []SortSpecification
+ | []TableElement
+ | []ValueExpression
+ | bool
+ | map[string]UpdateSource
+ | string
+
+// YYSymType is the yacc internal type for the stack that contains the symbols
+// to reduce (pass to the code). The only requirement here is that `yys` is
+// included as it contains the position of the token.
+struct YYSymType {
+mut:
+ v YYSym
+ yys int
+}
+
+struct Lexer {
+mut:
+ tokens []Token
+ pos int
+}
+
+fn (mut l Lexer) lex(mut lval YYSymType) int {
+ if l.pos >= l.tokens.len {
+ return 0
+ }
+
+ l.pos++
+ unsafe {
+ *lval = l.tokens[l.pos - 1].sym
+ }
+ return l.tokens[l.pos - 1].token
+}
+
+fn (mut l Lexer) error(s string) ! {
+ return sqlstate_42601(cleanup_yacc_error(s))
+}
+
+fn cleanup_yacc_error(s string) string {
+ mut msg := s
+
+ msg = msg.replace('OPERATOR_COMMA', '","')
+ msg = msg.replace('OPERATOR_RIGHT_PAREN', '")"')
+ msg = msg.replace('OPERATOR_DOUBLE_PIPE', '"||"')
+ msg = msg.replace('OPERATOR_PLUS', '"+"')
+ msg = msg.replace('OPERATOR_MINUS', '"-"')
+ msg = msg.replace('OPERATOR_SEMICOLON', '";"')
+ msg = msg.replace('OPERATOR_EQUALS', '"="')
+ msg = msg.replace('OPERATOR_LEFT_PAREN', '"("')
+ msg = msg.replace('OPERATOR_ASTERISK', '"*"')
+ msg = msg.replace('OPERATOR_PERIOD', '"."')
+ msg = msg.replace('OPERATOR_SOLIDUS', '"/"')
+ msg = msg.replace('OPERATOR_COLON', '":"')
+ msg = msg.replace('OPERATOR_LESS_THAN', '"<"')
+ msg = msg.replace('OPERATOR_GREATER_THAN', '">"')
+ msg = msg.replace('OPERATOR_NOT_EQUALS', '"<>"')
+ msg = msg.replace('OPERATOR_GREATER_EQUALS', '">="')
+ msg = msg.replace('OPERATOR_LESS_EQUALS', '"<="')
+ msg = msg.replace('OPERATOR_PERIOD_ASTERISK', '"." "*"')
+ msg = msg.replace('OPERATOR_LEFT_PAREN_ASTERISK', '"(" "*"')
+
+ msg = msg.replace('LITERAL_IDENTIFIER', 'identifier')
+ msg = msg.replace('LITERAL_STRING', 'string')
+ msg = msg.replace('LITERAL_NUMBER', 'number')
+
+ return msg['syntax error: '.len..]
}
struct Token {
-pub:
- kind TokenKind
- value string
+ token int
+ sym YYSymType
}
fn tokenize(sql_stmt string) []Token {
@@ -40,7 +170,7 @@ fn tokenize(sql_stmt string) []Token {
cs := sql_stmt.trim(';').runes()
mut i := 0
- next: for i < cs.len {
+ for i < cs.len {
// Numbers
if cs[i] >= `0` && cs[i] <= `9` {
mut word := ''
@@ -48,18 +178,22 @@ fn tokenize(sql_stmt string) []Token {
word += '${cs[i]}'
i++
}
- tokens << Token{.literal_number, word}
+ tokens << Token{token_literal_number, YYSymType{
+ v: word
+ }}
// There is a special case for approximate numbers where 'E' is considered
// a separate token in the SQL BNF. However, "e2" should not be treated as
// two tokens, but rather we need to catch this case only when with a
// number token.
if i < cs.len && (cs[i] == `e` || cs[i] == `E`) {
- tokens << Token{.keyword, 'E'}
+ tokens << Token{token_e, YYSymType{}}
i++
}
- continue
+ unsafe {
+ goto next
+ }
}
// Strings
@@ -71,8 +205,12 @@ fn tokenize(sql_stmt string) []Token {
i++
}
i++
- tokens << Token{.literal_string, word}
- continue
+ tokens << Token{token_literal_string, YYSymType{
+ v: new_character_value(word)
+ }}
+ unsafe {
+ goto next
+ }
}
// Delimited identifiers
@@ -84,45 +222,59 @@ fn tokenize(sql_stmt string) []Token {
i++
}
i++
- tokens << Token{.literal_identifier, '"${word}"'}
- continue
+ tokens << Token{token_literal_identifier, YYSymType{
+ v: IdentifierChain{
+ identifier: '"${word}"'
+ }
+ }}
+ unsafe {
+ goto next
+ }
}
// Operators
multi := {
- '<>': TokenKind.not_equals_operator
- '>=': TokenKind.greater_than_or_equals_operator
- '<=': TokenKind.less_than_or_equals_operator
- '||': TokenKind.concatenation_operator
+ '<>': token_operator_not_equals
+ '>=': token_operator_greater_equals
+ '<=': token_operator_less_equals
+ '||': token_operator_double_pipe
}
for op, tk in multi {
- if cs[i] == op[0] && cs[i + 1] == op[1] {
- tokens << Token{tk, op}
+ if i < cs.len - 1 && cs[i] == op[0] && cs[i + 1] == op[1] {
+ tokens << Token{tk, YYSymType{
+ v: op
+ }}
i += 2
- continue next
+ unsafe {
+ goto next
+ }
}
}
single := {
- `(`: TokenKind.left_paren
- `)`: TokenKind.right_paren
- `*`: TokenKind.asterisk
- `+`: TokenKind.plus_sign
- `,`: TokenKind.comma
- `-`: TokenKind.minus_sign
- `/`: TokenKind.solidus
- `;`: TokenKind.semicolon
- `<`: TokenKind.less_than_operator
- `=`: TokenKind.equals_operator
- `>`: TokenKind.greater_than_operator
- `.`: TokenKind.period
- `:`: TokenKind.colon
+ `(`: token_operator_left_paren
+ `)`: token_operator_right_paren
+ `*`: token_operator_asterisk
+ `+`: token_operator_plus
+ `,`: token_operator_comma
+ `-`: token_operator_minus
+ `/`: token_operator_solidus
+ `;`: token_operator_semicolon
+ `<`: token_operator_less_than
+ `=`: token_operator_equals
+ `>`: token_operator_greater_than
+ `.`: token_operator_period
+ `:`: token_operator_colon
}
for op, tk in single {
if cs[i] == op {
- tokens << Token{tk, op.str()}
+ tokens << Token{tk, YYSymType{
+ v: op.str()
+ }}
i++
- continue next
+ unsafe {
+ goto next
+ }
}
}
@@ -140,10 +292,32 @@ fn tokenize(sql_stmt string) []Token {
continue
}
- tokens << if is_key_word(word) {
- Token{TokenKind.keyword, word.to_upper()}
- } else {
- Token{TokenKind.literal_identifier, word}
+ upper_word := word.to_upper()
+ mut found := false
+ for tok_pos, tok_name in yy_toknames {
+ if tok_name == upper_word {
+ tok_number := tok_pos + 57343
+ tokens << Token{tok_number, YYSymType{
+ v: upper_word
+ }}
+ found = true
+ break
+ }
+ }
+
+ if !found {
+ tokens << Token{token_literal_identifier, YYSymType{
+ v: IdentifierChain{
+ identifier: word
+ }
+ }}
+ }
+
+ next:
+ length, new_token := tail_substitution(tokens)
+ if length > 0 {
+ tokens = tokens[..tokens.len - length].clone()
+ tokens << Token{new_token, YYSymType{}}
}
}
@@ -160,3 +334,39 @@ fn is_identifier_char(c rune, is_not_first bool) bool {
return yes
}
+
+fn tail_substitution(tokens []Token) (int, int) {
+ len := tokens.len
+
+ if len > 2 && tokens[len - 2].token == token_operator_period
+ && tokens[len - 1].token == token_operator_asterisk {
+ return 2, token_operator_period_asterisk
+ }
+ if len > 2 && tokens[len - 2].token == token_operator_left_paren
+ && tokens[len - 1].token == token_operator_asterisk {
+ return 2, token_operator_left_paren_asterisk
+ }
+ if len > 2 && tokens[len - 2].token == token_is && tokens[len - 1].token == token_true {
+ return 2, token_is_true
+ }
+ if len > 2 && tokens[len - 2].token == token_is && tokens[len - 1].token == token_false {
+ return 2, token_is_false
+ }
+ if len > 2 && tokens[len - 2].token == token_is && tokens[len - 1].token == token_unknown {
+ return 2, token_is_unknown
+ }
+ if len > 3 && tokens[len - 3].token == token_is && tokens[len - 2].token == token_not
+ && tokens[len - 1].token == token_true {
+ return 3, token_is_not_true
+ }
+ if len > 3 && tokens[len - 3].token == token_is && tokens[len - 2].token == token_not
+ && tokens[len - 1].token == token_false {
+ return 3, token_is_not_false
+ }
+ if len > 3 && tokens[len - 3].token == token_is && tokens[len - 2].token == token_not
+ && tokens[len - 1].token == token_unknown {
+ return 3, token_is_not_unknown
+ }
+
+ return 0, 0
+}
diff --git a/vsql/planner.v b/vsql/planner.v
index 0fc23ac..ffc02f9 100644
--- a/vsql/planner.v
+++ b/vsql/planner.v
@@ -409,14 +409,12 @@ fn new_expr_operation(mut conn Connection, params map[string]Value, select_list
mut column_name := 'COL${i + 1}'
if column.as_clause.sub_entity_name != '' {
column_name = column.as_clause.sub_entity_name
- } else if column.expr is CommonValueExpression {
- if column.expr is DatetimePrimary {
- if column.expr is ValueExpressionPrimary {
- if column.expr is NonparenthesizedValueExpressionPrimary {
- if column.expr is Identifier {
- e := column.expr
- column_name = e.sub_entity_name
- }
+ } else if column.expr is BooleanValueExpression {
+ e := column.expr.term.factor.expr
+ if e is BooleanPredicand {
+ if e is NonparenthesizedValueExpressionPrimary {
+ if e is Identifier {
+ column_name = e.sub_entity_name
}
}
}
diff --git a/vsql/query_cache.v b/vsql/query_cache.v
deleted file mode 100644
index ba77bc3..0000000
--- a/vsql/query_cache.v
+++ /dev/null
@@ -1,171 +0,0 @@
-// query_cache.v provides tooling to cache previously parsed prepared
-// statements. This is becuase parsing a statement is extremely expensive with
-// the current Earley implementation and many queries (excluding values) are
-// used more than once.
-//
-// The query cache is made more useful by the fact it can turn any existing
-// query into a prepared statement so that cache works in all cases.
-
-module vsql
-
-// A QueryCache improves the performance of parsing by caching previously cached
-// statements. By default, a new QueryCache is created for each Connection.
-// However, you can share a single QueryCache safely amung multiple connections
-// for even better performance. See ConnectionOptions.
-@[heap]
-pub struct QueryCache {
-mut:
- stmts map[string]Stmt
-}
-
-// Create a new query cache.
-pub fn new_query_cache() &QueryCache {
- return &QueryCache{}
-}
-
-fn (q QueryCache) prepare(tokens []Token) (string, map[string]Value, []Token) {
- // It's only worth caching specific types of queries.
- match tokens[0].value {
- 'SELECT', 'INSERT', 'UPDATE', 'DELETE' { return q.prepare_stmt(tokens) }
- else { return '', map[string]Value{}, tokens }
- }
-}
-
-fn (q QueryCache) prepare_stmt(tokens []Token) (string, map[string]Value, []Token) {
- mut key := ''
- mut i := 0
- mut params := map[string]Value{}
-
- // TODO(elliotchance): It's not efficient to expand the number of tokens
- // like this. Perhaps the parser should just understand a new type of
- // placeholder so it can be replaced in place?
- mut new_tokens := []Token{cap: tokens.len}
-
- mut j := 0
- for j < tokens.len {
- token := tokens[j]
-
- // Special handling for named literals.
- if j < tokens.len - 1 && token.kind == .keyword
- && (token.value == 'TIMESTAMP' || token.value == 'TIME' || token.value == 'DATE')
- && tokens[j + 1].kind == .literal_string {
- v := match token.value {
- 'DATE' {
- new_date_value(tokens[j + 1].value) or { panic(err) }
- }
- 'TIME' {
- new_time_value(tokens[j + 1].value) or { panic(err) }
- }
- 'TIMESTAMP' {
- new_timestamp_value(tokens[j + 1].value) or { panic(err) }
- }
- else {
- panic(token.value)
- }
- }
- params['P${i}'] = v
-
- key += ':P${i} '
- new_tokens << Token{.colon, ':'}
- new_tokens << Token{.literal_identifier, 'P${i}'}
- i++
-
- j += 2
- continue
- }
-
- // Do not replace numbers that appear in types. Such as 'NUMERIC(10, 2)'.
- if j < tokens.len - 6 && token.kind == .keyword
- && (token.value == 'DECIMAL' || token.value == 'NUMERIC') && tokens[j + 1].value == '('
- && tokens[j + 3].value == ',' {
- key += tokens[j].value.to_upper() + ' '
- key += tokens[j + 1].value.to_upper() + ' '
- key += tokens[j + 2].value.to_upper() + ' '
- key += tokens[j + 3].value.to_upper() + ' '
- key += tokens[j + 4].value.to_upper() + ' '
- key += tokens[j + 5].value.to_upper() + ' '
- new_tokens << tokens[j..j + 6]
- j += 6
- continue
- }
-
- // Do not replace numbers that appear in types. Such as 'VARCHAR(10)'.
- if j < tokens.len - 4 && token.kind == .keyword && (token.value == 'VARCHAR'
- || token.value == 'CHAR' || token.value == 'VARYING'
- || token.value == 'DECIMAL' || token.value == 'NUMERIC'
- || token.value == 'TIMESTAMP' || token.value == 'TIME') && tokens[j + 1].value == '(' {
- key += tokens[j].value.to_upper() + ' '
- key += tokens[j + 1].value.to_upper() + ' '
- key += tokens[j + 2].value.to_upper() + ' '
- key += tokens[j + 3].value.to_upper() + ' '
- new_tokens << tokens[j..j + 4]
- j += 4
- continue
- }
-
- match token.kind {
- .literal_number {
- mut numeric_tokens := ''
- // Numeric values with a decimal and approximate literals (1e2) are
- // actually multiple tokens like [number, '.' number] or
- // [number, 'E', number] so we need to be careful to consume all.
- for j < tokens.len && (tokens[j].kind == .literal_number
- || tokens[j].kind == .period || tokens[j].value == 'E') {
- numeric_tokens += tokens[j].value
- j++
- }
-
- // This should never fail as the value is already well formed, but we
- // have to satisfy the compiler with an "or".
- v := numeric_literal(numeric_tokens) or { panic(numeric_tokens) }
- params['P${i}'] = v
-
- key += ':P${i} '
- new_tokens << Token{.colon, ':'}
- new_tokens << Token{.literal_identifier, 'P${i}'}
- i++
- continue
- }
- .literal_string {
- key += ':P${i} '
- params['P${i}'] = new_varchar_value(token.value)
- new_tokens << Token{.colon, ':'}
- new_tokens << Token{.literal_identifier, 'P${i}'}
- i++
- j++
- continue
- }
- else {}
- }
-
- key += token.value.to_upper() + ' '
- new_tokens << token
- j++
- }
-
- return key, params, new_tokens
-}
-
-fn (mut q QueryCache) parse(query string) !(Stmt, map[string]Value, bool) {
- mut tokens := tokenize(query)
-
- // EXPLAIN is super helpful, but not part of the SQL standard so we only
- // treat it as a prefix that is trimmed off before parsing.
- mut explain := false
- if tokens[0].value.to_upper() == 'EXPLAIN' {
- explain = true
- tokens = tokens[1..].clone()
- }
-
- key, params, new_tokens := q.prepare(tokens)
- if key == '' {
- stmt := parse(new_tokens)!
- return stmt, map[string]Value{}, explain
- }
-
- if key !in q.stmts {
- q.stmts[key] = parse(new_tokens)!
- }
-
- return q.stmts[key] or { panic('impossible') }, params, explain
-}
diff --git a/vsql/server.v b/vsql/server.v
index 691733a..61b7fad 100644
--- a/vsql/server.v
+++ b/vsql/server.v
@@ -34,9 +34,8 @@ pub fn new_server(options ServerOptions) Server {
}
return Server{options, &Connection{
- query_cache: new_query_cache()
- now: default_now
- catalogs: {
+ now: default_now
+ catalogs: {
catalog_name: catalog
}
}}
diff --git a/vsql/sql_test.v b/vsql/sql_test.v
index aa4d539..5ddc85e 100644
--- a/vsql/sql_test.v
+++ b/vsql/sql_test.v
@@ -148,14 +148,13 @@ fn replace_unicode(s string) string {
fn test_all() ! {
filter_test, filter_line := get_test_filter()
verbose := $env('VERBOSE')
- query_cache := new_query_cache()
for test in get_tests()! {
- run_single_test(test, query_cache, verbose != '', filter_line)!
+ run_single_test(test, verbose != '', filter_line)!
}
}
@[assert_continues]
-fn run_single_test(test SQLTest, query_cache &QueryCache, verbose bool, filter_line int) ! {
+fn run_single_test(test SQLTest, verbose bool, filter_line int) ! {
if filter_line != 0 && test.line_number != filter_line {
if verbose {
println('SKIP ${test.file_name}:${test.line_number}\n')
@@ -172,7 +171,6 @@ fn run_single_test(test SQLTest, query_cache &QueryCache, verbose bool, filter_l
}
mut options := default_connection_options()
- options.query_cache = query_cache
mut db := open_database(':memory:', options)!
db.now = fn () (time.Time, i16) {
@@ -276,3 +274,17 @@ fn run_single_test(test SQLTest, query_cache &QueryCache, verbose bool, filter_l
assert expected == actual_trim
}
+
+// Make sure any non-keywords (operators, literals, etc) are replaced in error
+// messages.
+fn test_cleanup_yacc_error() {
+ mut msg := ''
+ for tok_name in yy_toknames {
+ if tok_name.starts_with('OPERATOR_') || tok_name.starts_with('LITERAL_') {
+ msg += ' ' + tok_name
+ }
+ }
+ result := cleanup_yacc_error(msg)
+ assert !result.contains('OPERATOR_')
+ assert !result.contains('LITERAL_')
+}
diff --git a/vsql/std_10_10_sort_specification_list.v b/vsql/std_10_10_sort_specification_list.v
new file mode 100644
index 0000000..3c04867
--- /dev/null
+++ b/vsql/std_10_10_sort_specification_list.v
@@ -0,0 +1,18 @@
+module vsql
+
+// ISO/IEC 9075-2:2016(E), 10.10,
+//
+// Specify a sort order.
+
+struct SortSpecification {
+ expr ValueExpression
+ is_asc bool
+}
+
+fn (e SortSpecification) pstr(params map[string]Value) string {
+ if e.is_asc {
+ return '${e.expr.pstr(params)} ASC'
+ }
+
+ return '${e.expr.pstr(params)} DESC'
+}
diff --git a/vsql/std_10_10_sort_specification_list.y b/vsql/std_10_10_sort_specification_list.y
new file mode 100644
index 0000000..c6cd0f3
--- /dev/null
+++ b/vsql/std_10_10_sort_specification_list.y
@@ -0,0 +1,22 @@
+%%
+
+sort_specification_list:
+ sort_specification { $$.v = [$1.v as SortSpecification] }
+| sort_specification_list comma sort_specification {
+ $$.v = append_list($1.v as []SortSpecification, $3.v as SortSpecification)
+ }
+
+sort_specification:
+ sort_key { $$.v = SortSpecification{$1.v as ValueExpression, true} }
+| sort_key ordering_specification {
+ $$.v = SortSpecification{$1.v as ValueExpression, $2.v as bool}
+ }
+
+sort_key:
+ value_expression { $$.v = $1.v as ValueExpression }
+
+ordering_specification:
+ ASC { $$.v = true }
+| DESC { $$.v = false }
+
+%%
diff --git a/vsql/std_routine_invocation.v b/vsql/std_10_4_routine_invocation.v
similarity index 57%
rename from vsql/std_routine_invocation.v
rename to vsql/std_10_4_routine_invocation.v
index 12834a8..777595a 100644
--- a/vsql/std_routine_invocation.v
+++ b/vsql/std_10_4_routine_invocation.v
@@ -2,26 +2,7 @@ module vsql
// ISO/IEC 9075-2:2016(E), 10.4,
//
-// # Function
-//
// Invoke an SQL-invoked routine.
-//
-// # Format
-//~
-//~ /* RoutineInvocation */ ::=
-//~ -> routine_invocation
-//~
-//~ /* Identifier */ ::=
-//~ -> routine_name
-//~
-//~ /* []ValueExpression */ ::=
-//~ -> sql_argument_list_1
-//~ | -> sql_argument_list_2
-//~ |
-//~ -> sql_argument_list_3
-//~
-//~ /* ValueExpression */ ::=
-//~
struct RoutineInvocation {
function_name string
@@ -81,26 +62,3 @@ fn (e RoutineInvocation) compile(mut c Compiler) !CompileResult {
fn (e RoutineInvocation) resolve_identifiers(conn &Connection, tables map[string]Table) !RoutineInvocation {
return RoutineInvocation{e.function_name, e.args}
}
-
-fn parse_routine_invocation(name Identifier, args []ValueExpression) !RoutineInvocation {
- return RoutineInvocation{name.entity_name, args}
-}
-
-fn parse_routine_name(identifier IdentifierChain) !Identifier {
- return new_function_identifier(identifier.identifier)
-}
-
-fn parse_sql_argument_list_1() ![]ValueExpression {
- return []ValueExpression{}
-}
-
-fn parse_sql_argument_list_2(expr ValueExpression) ![]ValueExpression {
- return [expr]
-}
-
-fn parse_sql_argument_list_3(element_list []ValueExpression, element ValueExpression) ![]ValueExpression {
- mut new_list := element_list.clone()
- new_list << element
-
- return new_list
-}
diff --git a/vsql/std_10_4_routine_invocation.y b/vsql/std_10_4_routine_invocation.y
new file mode 100644
index 0000000..039ab8f
--- /dev/null
+++ b/vsql/std_10_4_routine_invocation.y
@@ -0,0 +1,23 @@
+%%
+
+routine_invocation:
+ routine_name sql_argument_list {
+ $$.v = RoutineInvocation{($1.v as Identifier).entity_name, $2.v as []ValueExpression}
+ }
+
+routine_name:
+ qualified_identifier {
+ $$.v = new_function_identifier(($1.v as IdentifierChain).identifier)!
+ }
+
+sql_argument_list:
+ left_paren right_paren { $$.v = []ValueExpression{} }
+| left_paren sql_argument right_paren { $$.v = [$2.v as ValueExpression] }
+| left_paren sql_argument_list comma sql_argument right_paren {
+ $$.v = append_list($2.v as []ValueExpression, $4.v as ValueExpression)
+ }
+
+sql_argument:
+ value_expression { $$.v = $1.v as ValueExpression }
+
+%%
diff --git a/vsql/std_aggregate_function.v b/vsql/std_10_9_aggregate_function.v
similarity index 78%
rename from vsql/std_aggregate_function.v
rename to vsql/std_10_9_aggregate_function.v
index 9a3a531..1d55028 100644
--- a/vsql/std_aggregate_function.v
+++ b/vsql/std_10_9_aggregate_function.v
@@ -2,29 +2,7 @@ module vsql
// ISO/IEC 9075-2:2016(E), 10.9,
//
-// # Function
-//
// Specify a value computed from a collection of rows.
-//
-// # Format
-//~
-//~ /* AggregateFunction */ ::=
-//~ COUNT -> count_all
-//~ |
-//~
-//~ /* AggregateFunction */ ::=
-//~
-//~ -> general_set_function
-//~
-//~ /* string */ ::=
-//~
-//~
-//~ /* string */ ::=
-//~ AVG
-//~ | MAX
-//~ | MIN
-//~ | SUM
-//~ | COUNT
type AggregateFunction = AggregateFunctionCount | RoutineInvocation
@@ -59,14 +37,6 @@ fn (e AggregateFunction) compile(mut c Compiler) !CompileResult {
struct AggregateFunctionCount {}
-fn parse_count_all(asterisk string) !AggregateFunction {
- return AggregateFunctionCount{}
-}
-
-fn parse_general_set_function(name string, expr ValueExpression) !AggregateFunction {
- return RoutineInvocation{name, [expr]}
-}
-
// COUNT(ANY) INTEGER
fn func_count(values []Value) !Value {
mut count := 0
diff --git a/vsql/std_10_9_aggregate_function.y b/vsql/std_10_9_aggregate_function.y
new file mode 100644
index 0000000..f920c42
--- /dev/null
+++ b/vsql/std_10_9_aggregate_function.y
@@ -0,0 +1,26 @@
+%%
+
+// was: COUNT left_paren asterisk right_paren
+aggregate_function:
+ COUNT OPERATOR_LEFT_PAREN_ASTERISK right_paren {
+ $$.v = AggregateFunction(AggregateFunctionCount{})
+ }
+| general_set_function { $$.v = $1.v as AggregateFunction }
+
+general_set_function:
+ set_function_type left_paren value_expression right_paren {
+ $$.v = AggregateFunction(RoutineInvocation{
+ $1.v as string, [$3.v as ValueExpression]})
+ }
+
+set_function_type:
+ computational_operation { $$.v = $1.v as string }
+
+computational_operation:
+ AVG { $$.v = $1.v as string }
+| MAX { $$.v = $1.v as string }
+| MIN { $$.v = $1.v as string }
+| SUM { $$.v = $1.v as string }
+| COUNT { $$.v = $1.v as string }
+
+%%
diff --git a/vsql/std_schema_definition.v b/vsql/std_11_1_schema_definition.v
similarity index 73%
rename from vsql/std_schema_definition.v
rename to vsql/std_11_1_schema_definition.v
index c7e6f39..0c9c13a 100644
--- a/vsql/std_schema_definition.v
+++ b/vsql/std_11_1_schema_definition.v
@@ -4,26 +4,12 @@ import time
// ISO/IEC 9075-2:2016(E), 11.1,
//
-// # Function
-//
// Define a schema.
-//
-// # Format
-//~
-//~ /* Stmt */ ::=
-//~ CREATE SCHEMA -> schema_definition
-//~
-//~ /* Identifier */ ::=
-//~
struct SchemaDefinition {
schema_name Identifier
}
-fn parse_schema_definition(schema_name Identifier) !Stmt {
- return SchemaDefinition{schema_name}
-}
-
fn (stmt SchemaDefinition) execute(mut conn Connection, params map[string]Value, elapsed_parse time.Duration) !Result {
t := start_timer()
diff --git a/vsql/std_11_1_schema_definition.y b/vsql/std_11_1_schema_definition.y
new file mode 100644
index 0000000..5889c42
--- /dev/null
+++ b/vsql/std_11_1_schema_definition.y
@@ -0,0 +1,11 @@
+%%
+
+schema_definition:
+ CREATE SCHEMA schema_name_clause {
+ $$.v = Stmt(SchemaDefinition{$3.v as Identifier})
+ }
+
+schema_name_clause:
+ schema_name { $$.v = $1.v as Identifier }
+
+%%
diff --git a/vsql/std_drop_schema_statement.v b/vsql/std_11_2_drop_schema_statement.v
similarity index 79%
rename from vsql/std_drop_schema_statement.v
rename to vsql/std_11_2_drop_schema_statement.v
index edb8691..e49460e 100644
--- a/vsql/std_drop_schema_statement.v
+++ b/vsql/std_11_2_drop_schema_statement.v
@@ -4,28 +4,13 @@ import time
// ISO/IEC 9075-2:2016(E), 11.2,
//
-// # Function
-//
// Destroy a schema.
-//
-// # Format
-//~
-//~ /* Stmt */ ::=
-//~ DROP SCHEMA -> drop_schema_statement
-//~
-//~ /* string */ ::=
-//~ CASCADE
-//~ | RESTRICT
struct DropSchemaStatement {
schema_name Identifier
behavior string // CASCADE or RESTRICT
}
-fn parse_drop_schema_statement(schema_name Identifier, behavior string) !Stmt {
- return DropSchemaStatement{schema_name, behavior}
-}
-
fn (stmt DropSchemaStatement) execute(mut conn Connection, params map[string]Value, elapsed_parse time.Duration) !Result {
t := start_timer()
diff --git a/vsql/std_11_2_drop_schema_statement.y b/vsql/std_11_2_drop_schema_statement.y
new file mode 100644
index 0000000..b1a609b
--- /dev/null
+++ b/vsql/std_11_2_drop_schema_statement.y
@@ -0,0 +1,12 @@
+%%
+
+drop_schema_statement:
+ DROP SCHEMA schema_name drop_behavior {
+ $$.v = Stmt(DropSchemaStatement{$3.v as Identifier, $4.v as string})
+ }
+
+drop_behavior:
+ CASCADE { $$.v = $1.v as string }
+| RESTRICT { $$.v = $1.v as string }
+
+%%
diff --git a/vsql/std_drop_table_statement.v b/vsql/std_11_31_drop_table_statement.v
similarity index 79%
rename from vsql/std_drop_table_statement.v
rename to vsql/std_11_31_drop_table_statement.v
index 57501b5..1795e7a 100644
--- a/vsql/std_drop_table_statement.v
+++ b/vsql/std_11_31_drop_table_statement.v
@@ -4,23 +4,12 @@ import time
// ISO/IEC 9075-2:2016(E), 11.31,
//
-// # Function
-//
// Destroy a table.
-//
-// # Format
-//~
-//~ /* Stmt */ ::=
-//~ DROP TABLE -> drop_table_statement
struct DropTableStatement {
table_name Identifier
}
-fn parse_drop_table_statement(table_name Identifier) !Stmt {
- return DropTableStatement{table_name}
-}
-
fn (stmt DropTableStatement) execute(mut conn Connection, params map[string]Value, elapsed_parse time.Duration) !Result {
t := start_timer()
diff --git a/vsql/std_11_31_drop_table_statement.y b/vsql/std_11_31_drop_table_statement.y
new file mode 100644
index 0000000..968768e
--- /dev/null
+++ b/vsql/std_11_31_drop_table_statement.y
@@ -0,0 +1,6 @@
+%%
+
+drop_table_statement:
+ DROP TABLE table_name { $$.v = Stmt(DropTableStatement{$3.v as Identifier}) }
+
+%%
diff --git a/vsql/std_table_definition.v b/vsql/std_11_3_table_definition.v
similarity index 68%
rename from vsql/std_table_definition.v
rename to vsql/std_11_3_table_definition.v
index 8e8de11..bafb12d 100644
--- a/vsql/std_table_definition.v
+++ b/vsql/std_11_3_table_definition.v
@@ -4,33 +4,8 @@ import time
// ISO/IEC 9075-2:2016(E), 11.3,
//
-// # Function
-//
// Define a persistent base table, a created local temporary table, or a global
// temporary table.
-//
-// # Format
-//~
-//~ /* TableDefinition */ ::=
-//~ CREATE TABLE -> table_definition
-//~
-//~ /* []TableElement */ ::=
-//~
-//~
-//~ /* []TableElement */ ::=
-//~
-//~
-//~ -> table_element_list
-//~
-//~ /* TableElement */ ::=
-//~
-//~ |
-//
-// These are non-standard, just to simplify standard rules:
-//~
-//~ /* []TableElement */ ::=
-//~ -> table_elements_1
-//~ | -> table_elements_2
type TableElement = Column | UniqueConstraintDefinition
@@ -50,24 +25,6 @@ fn (s TableDefinition) columns() Columns {
return columns
}
-fn parse_table_definition(table_name Identifier, table_contents_source []TableElement) !Stmt {
- return TableDefinition{table_name, table_contents_source}
-}
-
-fn parse_table_element_list(table_elements []TableElement) ![]TableElement {
- return table_elements
-}
-
-fn parse_table_elements_1(table_element TableElement) ![]TableElement {
- return [table_element]
-}
-
-fn parse_table_elements_2(table_elements []TableElement, table_element TableElement) ![]TableElement {
- mut new_table_elements := table_elements.clone()
- new_table_elements << table_element
- return new_table_elements
-}
-
// TODO(elliotchance): A table is allowed to have zero columns.
fn (stmt TableDefinition) execute(mut conn Connection, params map[string]Value, elapsed_parse time.Duration) !Result {
diff --git a/vsql/std_11_3_table_definition.y b/vsql/std_11_3_table_definition.y
new file mode 100644
index 0000000..d6b3d23
--- /dev/null
+++ b/vsql/std_11_3_table_definition.y
@@ -0,0 +1,24 @@
+%%
+
+table_definition:
+ CREATE TABLE table_name table_contents_source {
+ $$.v = Stmt(TableDefinition{$3.v as Identifier, $4.v as []TableElement})
+ }
+
+table_contents_source:
+ table_element_list { $$.v = $1.v as []TableElement }
+
+table_element_list:
+ left_paren table_elements right_paren { $$.v = $2.v as []TableElement }
+
+table_element:
+ column_definition { $$.v = $1.v as TableElement }
+| table_constraint_definition { $$.v = $1.v as TableElement }
+
+table_elements:
+ table_element { $$.v = [$1.v as TableElement] }
+| table_elements comma table_element {
+ $$.v = append_list($1.v as []TableElement, $3.v as TableElement)
+ }
+
+%%
diff --git a/vsql/std_11_4_column_definition.y b/vsql/std_11_4_column_definition.y
new file mode 100644
index 0000000..9749aba
--- /dev/null
+++ b/vsql/std_11_4_column_definition.y
@@ -0,0 +1,24 @@
+%%
+
+// ISO/IEC 9075-2:2016(E), 11.4,
+//
+// Define a column of a base table.
+
+column_definition:
+ column_name data_type_or_domain_name {
+ $$.v = TableElement(Column{$1.v as Identifier, $2.v as Type, false})
+ }
+| column_name data_type_or_domain_name column_constraint_definition {
+ $$.v = TableElement(Column{$1.v as Identifier, $2.v as Type, $3.v as bool})
+ }
+
+data_type_or_domain_name:
+ data_type { $$.v = $1.v as Type }
+
+column_constraint_definition:
+ column_constraint { $$.v = $1.v as bool }
+
+column_constraint:
+ NOT NULL { $$.v = true }
+
+%%
diff --git a/vsql/std_11_6_table_constraint_definition.y b/vsql/std_11_6_table_constraint_definition.y
new file mode 100644
index 0000000..1148dd1
--- /dev/null
+++ b/vsql/std_11_6_table_constraint_definition.y
@@ -0,0 +1,15 @@
+%%
+
+// ISO/IEC 9075-2:2016(E), 11.6,
+//
+// Specify an integrity constraint.
+
+table_constraint_definition:
+ table_constraint { $$.v = $1.v as TableElement }
+
+table_constraint:
+ unique_constraint_definition {
+ $$.v = TableElement($1.v as UniqueConstraintDefinition)
+ }
+
+%%
diff --git a/vsql/std_11_72_sequence_generator_definition.v b/vsql/std_11_72_sequence_generator_definition.v
new file mode 100644
index 0000000..1e5aa90
--- /dev/null
+++ b/vsql/std_11_72_sequence_generator_definition.v
@@ -0,0 +1,133 @@
+module vsql
+
+import time
+
+// ISO/IEC 9075-2:2016(E), 11.72,
+//
+// Define an external sequence generator.
+
+type SequenceGeneratorOption = SequenceGeneratorCycleOption
+ | SequenceGeneratorIncrementByOption
+ | SequenceGeneratorMaxvalueOption
+ | SequenceGeneratorMinvalueOption
+ | SequenceGeneratorRestartOption
+ | SequenceGeneratorStartWithOption
+
+struct SequenceGeneratorStartWithOption {
+ start_value Value
+}
+
+struct SequenceGeneratorRestartOption {
+ restart_value ?Value
+}
+
+struct SequenceGeneratorIncrementByOption {
+ increment_by Value
+}
+
+struct SequenceGeneratorMinvalueOption {
+ min_value ?Value // not set = NO MINVALUE
+}
+
+struct SequenceGeneratorMaxvalueOption {
+ max_value ?Value // not set = NO MAXVALUE
+}
+
+struct SequenceGeneratorCycleOption {
+ cycle bool
+}
+
+struct SequenceGeneratorDefinition {
+ name Identifier
+ options []SequenceGeneratorOption
+}
+
+fn (stmt SequenceGeneratorDefinition) execute(mut conn Connection, params map[string]Value, elapsed_parse time.Duration) !Result {
+ t := start_timer()
+
+ conn.open_write_connection()!
+ defer {
+ conn.release_write_connection()
+ }
+
+ mut c := Compiler{
+ conn: conn
+ params: params
+ }
+ mut catalog := conn.catalog()
+ mut sequence_name := conn.resolve_schema_identifier(stmt.name)!
+ mut increment_by := i64(1)
+ mut has_start_value := false
+ mut start_value := i64(1)
+ mut has_min_value := false
+ mut min_value := i64(0)
+ mut has_max_value := false
+ mut max_value := i64(0)
+ mut cycle := false
+ for option in stmt.options {
+ match option {
+ SequenceGeneratorStartWithOption {
+ start_value = (option.start_value.compile(mut c)!.run(mut conn, Row{},
+ map[string]Value{})!).as_int()
+ has_start_value = true
+ }
+ SequenceGeneratorRestartOption {
+ // Not possible.
+ }
+ SequenceGeneratorIncrementByOption {
+ increment_by = (option.increment_by.compile(mut c)!.run(mut conn, Row{},
+ map[string]Value{})!).as_int()
+ }
+ SequenceGeneratorMinvalueOption {
+ if v := option.min_value {
+ min_value = (v.compile(mut c)!.run(mut conn, Row{}, map[string]Value{})!).as_int()
+ has_min_value = true
+ }
+ }
+ SequenceGeneratorMaxvalueOption {
+ if v := option.max_value {
+ max_value = (v.compile(mut c)!.run(mut conn, Row{}, map[string]Value{})!).as_int()
+ has_max_value = true
+ }
+ }
+ SequenceGeneratorCycleOption {
+ cycle = option.cycle
+ }
+ }
+ }
+
+ is_ascending := increment_by >= 0
+ current_value := match true {
+ has_start_value {
+ start_value - increment_by
+ }
+ is_ascending && has_min_value {
+ min_value - increment_by
+ }
+ !is_ascending && has_max_value {
+ max_value - increment_by
+ }
+ else {
+ 1 - increment_by
+ }
+ }
+
+ sequence := Sequence{
+ name: sequence_name
+ current_value: current_value
+ increment_by: increment_by
+ cycle: cycle
+ has_min_value: has_min_value
+ min_value: min_value
+ has_max_value: has_max_value
+ max_value: max_value
+ }
+
+ catalog.storage.create_sequence(sequence)!
+
+ return new_result_msg('CREATE SEQUENCE 1', elapsed_parse, t.elapsed())
+}
+
+fn (stmt SequenceGeneratorDefinition) explain(mut conn Connection, params map[string]Value, elapsed_parse time.Duration) !Result {
+ return sqlstate_42601('Cannot EXPLAIN CREATE SEQUENCE')
+}
diff --git a/vsql/std_11_72_sequence_generator_definition.y b/vsql/std_11_72_sequence_generator_definition.y
new file mode 100644
index 0000000..e95d87d
--- /dev/null
+++ b/vsql/std_11_72_sequence_generator_definition.y
@@ -0,0 +1,98 @@
+%%
+
+sequence_generator_definition:
+ CREATE SEQUENCE sequence_generator_name {
+ $$.v = SequenceGeneratorDefinition{
+ name: $3.v as Identifier
+ }
+ }
+| CREATE SEQUENCE sequence_generator_name sequence_generator_options {
+ $$.v = SequenceGeneratorDefinition{
+ name: $3.v as Identifier
+ options: $4.v as []SequenceGeneratorOption
+ }
+ }
+
+sequence_generator_options:
+ sequence_generator_option { $$.v = $1.v as []SequenceGeneratorOption }
+| sequence_generator_options sequence_generator_option {
+ $$.v = $1.v as []SequenceGeneratorOption
+ }
+
+sequence_generator_option:
+ common_sequence_generator_options { $$.v = $1.v as []SequenceGeneratorOption }
+
+common_sequence_generator_options:
+ common_sequence_generator_option { $$.v = [$1.v as SequenceGeneratorOption] }
+| common_sequence_generator_options common_sequence_generator_option {
+ $$.v = append_list($1.v as []SequenceGeneratorOption,
+ $2.v as SequenceGeneratorOption)
+ }
+
+common_sequence_generator_option:
+ sequence_generator_start_with_option {
+ $$.v = SequenceGeneratorOption($1.v as SequenceGeneratorStartWithOption)
+ }
+| basic_sequence_generator_option { $$.v = $1.v as SequenceGeneratorOption }
+
+basic_sequence_generator_option:
+ sequence_generator_increment_by_option {
+ $$.v = SequenceGeneratorOption($1.v as SequenceGeneratorIncrementByOption)
+ }
+| sequence_generator_maxvalue_option {
+ $$.v = SequenceGeneratorOption($1.v as SequenceGeneratorMaxvalueOption)
+ }
+| sequence_generator_minvalue_option {
+ $$.v = SequenceGeneratorOption($1.v as SequenceGeneratorMinvalueOption)
+ }
+| sequence_generator_cycle_option {
+ $$.v = SequenceGeneratorOption(SequenceGeneratorCycleOption{$1.v as bool})
+ }
+
+sequence_generator_start_with_option:
+ START WITH sequence_generator_start_value {
+ $$.v = SequenceGeneratorStartWithOption{
+ start_value: $3.v as Value
+ }
+ }
+
+sequence_generator_start_value:
+ signed_numeric_literal { $$.v = $1.v as Value }
+
+sequence_generator_increment_by_option:
+ INCREMENT BY sequence_generator_increment {
+ $$.v = SequenceGeneratorIncrementByOption{
+ increment_by: $3.v as Value
+ }
+ }
+
+sequence_generator_increment:
+ signed_numeric_literal { $$.v = $1.v as Value }
+
+sequence_generator_maxvalue_option:
+ MAXVALUE sequence_generator_max_value {
+ $$.v = SequenceGeneratorMaxvalueOption{
+ max_value: $2.v as Value
+ }
+ }
+| NO MAXVALUE { $$.v = SequenceGeneratorMaxvalueOption{} }
+
+sequence_generator_max_value:
+ signed_numeric_literal { $$.v = $1.v as Value }
+
+sequence_generator_minvalue_option:
+ MINVALUE sequence_generator_min_value {
+ $$.v = SequenceGeneratorMinvalueOption{
+ min_value: $2.v as Value
+ }
+ }
+| NO MINVALUE { $$.v = SequenceGeneratorMinvalueOption{} }
+
+sequence_generator_min_value:
+ signed_numeric_literal { $$.v = $1.v as Value }
+
+sequence_generator_cycle_option:
+ CYCLE { $$.v = true }
+| NO CYCLE { $$.v = false }
+
+%%
diff --git a/vsql/std_alter_sequence_generator_statement.v b/vsql/std_11_73_alter_sequence_generator_statement.v
similarity index 55%
rename from vsql/std_alter_sequence_generator_statement.v
rename to vsql/std_11_73_alter_sequence_generator_statement.v
index 2c577b4..22367bc 100644
--- a/vsql/std_alter_sequence_generator_statement.v
+++ b/vsql/std_11_73_alter_sequence_generator_statement.v
@@ -4,68 +4,13 @@ import time
// ISO/IEC 9075-2:2016(E), 11.73,
//
-// # Function
-//
// Change the definition of an external sequence generator.
-//
-// # Format
-//~
-//~ /* AlterSequenceGeneratorStatement */ ::=
-//~ ALTER SEQUENCE
-//~
-//~ -> alter_sequence_generator_statement
-//~
-//~ /* []SequenceGeneratorOption */ ::=
-//~ -> sequence_generator_options_1
-//~ |
-//~ -> sequence_generator_options_2
-//~
-//~ /* SequenceGeneratorOption */ ::=
-//~ -> SequenceGeneratorOption
-//~ |
-//~
-//~ /* SequenceGeneratorRestartOption */ ::=
-//~ RESTART -> sequence_generator_restart_option_1
-//~ | RESTART WITH
-//~ -> sequence_generator_restart_option_2
-//~
-//~ /* Value */ ::=
-//~
struct AlterSequenceGeneratorStatement {
name Identifier
options []SequenceGeneratorOption
}
-fn parse_alter_sequence_generator_statement(generator_name Identifier, options []SequenceGeneratorOption) !AlterSequenceGeneratorStatement {
- return AlterSequenceGeneratorStatement{
- name: generator_name
- options: options
- }
-}
-
-fn parse_sequence_generator_options_1(option SequenceGeneratorOption) ![]SequenceGeneratorOption {
- return [option]
-}
-
-fn parse_sequence_generator_options_2(options []SequenceGeneratorOption, option SequenceGeneratorOption) ![]SequenceGeneratorOption {
- mut new_options := options.clone()
- new_options << option
- return new_options
-}
-
-fn parse_sequence_generator_restart_option_1() !SequenceGeneratorRestartOption {
- return SequenceGeneratorRestartOption{
- restart_value: none
- }
-}
-
-fn parse_sequence_generator_restart_option_2(restart_value Value) !SequenceGeneratorRestartOption {
- return SequenceGeneratorRestartOption{
- restart_value: restart_value
- }
-}
-
fn (stmt AlterSequenceGeneratorStatement) execute(mut conn Connection, params map[string]Value, elapsed_parse time.Duration) !Result {
t := start_timer()
diff --git a/vsql/std_11_73_alter_sequence_generator_statement.y b/vsql/std_11_73_alter_sequence_generator_statement.y
new file mode 100644
index 0000000..59c4834
--- /dev/null
+++ b/vsql/std_11_73_alter_sequence_generator_statement.y
@@ -0,0 +1,35 @@
+%%
+
+alter_sequence_generator_statement:
+ ALTER SEQUENCE sequence_generator_name alter_sequence_generator_options {
+ $$.v = AlterSequenceGeneratorStatement{
+ name: $3.v as Identifier
+ options: $4.v as []SequenceGeneratorOption
+ }
+ }
+
+alter_sequence_generator_options:
+ alter_sequence_generator_option { $$.v = [$1.v as SequenceGeneratorOption] }
+| alter_sequence_generator_options alter_sequence_generator_option {
+ $$.v = append_list($1.v as []SequenceGeneratorOption,
+ $2.v as SequenceGeneratorOption)
+ }
+
+alter_sequence_generator_option:
+ alter_sequence_generator_restart_option {
+ $$.v = SequenceGeneratorOption($1.v as SequenceGeneratorRestartOption)
+ }
+| basic_sequence_generator_option { $$.v = $1.v as SequenceGeneratorOption }
+
+alter_sequence_generator_restart_option:
+ RESTART { $$.v = SequenceGeneratorRestartOption{} }
+| RESTART WITH sequence_generator_restart_value {
+ $$.v = SequenceGeneratorRestartOption{
+ restart_value: $3.v as Value
+ }
+ }
+
+sequence_generator_restart_value:
+ signed_numeric_literal { $$.v = $1.v as Value }
+
+%%
diff --git a/vsql/std_drop_sequence_generator_statement.v b/vsql/std_11_74_drop_sequence_generator_statement.v
similarity index 73%
rename from vsql/std_drop_sequence_generator_statement.v
rename to vsql/std_11_74_drop_sequence_generator_statement.v
index 5f21868..447a963 100644
--- a/vsql/std_drop_sequence_generator_statement.v
+++ b/vsql/std_11_74_drop_sequence_generator_statement.v
@@ -4,24 +4,12 @@ import time
// ISO/IEC 9075-2:2016(E), 11.74,
//
-// # Function
-//
// Destroy an external sequence generator.
-//
-// # Format
-//~
-//~ /* Stmt */ ::=
-//~ DROP SEQUENCE
-//~ -> drop_sequence_generator_statement
struct DropSequenceGeneratorStatement {
sequence_name Identifier
}
-fn parse_drop_sequence_generator_statement(sequence_name Identifier) !Stmt {
- return DropSequenceGeneratorStatement{sequence_name}
-}
-
fn (stmt DropSequenceGeneratorStatement) execute(mut conn Connection, params map[string]Value, elapsed_parse time.Duration) !Result {
t := start_timer()
diff --git a/vsql/std_11_74_drop_sequence_generator_statement.y b/vsql/std_11_74_drop_sequence_generator_statement.y
new file mode 100644
index 0000000..c91d27a
--- /dev/null
+++ b/vsql/std_11_74_drop_sequence_generator_statement.y
@@ -0,0 +1,8 @@
+%%
+
+drop_sequence_generator_statement:
+ DROP SEQUENCE sequence_generator_name {
+ $$.v = Stmt(DropSequenceGeneratorStatement{$3.v as Identifier})
+ }
+
+%%
diff --git a/vsql/std_11_7_unique_constraint_definition.v b/vsql/std_11_7_unique_constraint_definition.v
new file mode 100644
index 0000000..ec82574
--- /dev/null
+++ b/vsql/std_11_7_unique_constraint_definition.v
@@ -0,0 +1,9 @@
+module vsql
+
+// ISO/IEC 9075-2:2016(E), 11.7,
+//
+// Specify a uniqueness constraint for a table.
+
+struct UniqueConstraintDefinition {
+ columns []Identifier
+}
diff --git a/vsql/std_11_7_unique_constraint_definition.y b/vsql/std_11_7_unique_constraint_definition.y
new file mode 100644
index 0000000..378114b
--- /dev/null
+++ b/vsql/std_11_7_unique_constraint_definition.y
@@ -0,0 +1,14 @@
+%%
+
+unique_constraint_definition:
+ unique_specification left_paren unique_column_list right_paren {
+ $$.v = UniqueConstraintDefinition{$3.v as []Identifier}
+ }
+
+unique_specification:
+ PRIMARY KEY
+
+unique_column_list:
+ column_name_list { $$.v = $1.v as []Identifier }
+
+%%
diff --git a/vsql/std_13_4_sql_procedure_statement.y b/vsql/std_13_4_sql_procedure_statement.y
new file mode 100644
index 0000000..703c63d
--- /dev/null
+++ b/vsql/std_13_4_sql_procedure_statement.y
@@ -0,0 +1,31 @@
+%%
+
+// ISO/IEC 9075-2:2016(E), 13.4,
+//
+// Define all of the SQL-statements that are s.
+
+sql_schema_statement:
+ sql_schema_definition_statement { $$.v = $1.v as Stmt }
+| sql_schema_manipulation_statement { $$.v = $1.v as Stmt }
+
+sql_schema_definition_statement:
+ schema_definition { $$.v = $1.v as Stmt }
+| table_definition { $$.v = $1.v as Stmt }
+| sequence_generator_definition { $$.v = Stmt($1.v as SequenceGeneratorDefinition) }
+
+sql_schema_manipulation_statement:
+ drop_schema_statement { $$.v = $1.v as Stmt }
+| drop_table_statement { $$.v = $1.v as Stmt }
+| alter_sequence_generator_statement { $$.v = Stmt($1.v as AlterSequenceGeneratorStatement) }
+| drop_sequence_generator_statement { $$.v = $1.v as Stmt }
+
+sql_transaction_statement:
+ start_transaction_statement { $$.v = $1.v as Stmt }
+| commit_statement { $$.v = $1.v as Stmt }
+| rollback_statement { $$.v = $1.v as Stmt }
+
+sql_session_statement:
+ set_schema_statement { $$.v = Stmt($1.v as SetSchemaStatement) }
+| set_catalog_statement { $$.v = $1.v as Stmt }
+
+%%
diff --git a/vsql/std_insert_statement.v b/vsql/std_14_11_insert_statement.v
similarity index 72%
rename from vsql/std_insert_statement.v
rename to vsql/std_14_11_insert_statement.v
index a238cc0..4a8f825 100644
--- a/vsql/std_insert_statement.v
+++ b/vsql/std_14_11_insert_statement.v
@@ -4,29 +4,7 @@ import time
// ISO/IEC 9075-2:2016(E), 14.11,
//
-// # Function
-//
// Create new rows in a table.
-//
-// # Format
-//~
-//~ /* Stmt */ ::=
-//~ INSERT INTO
-//~
-//~ -> insert_statement
-//~
-//~ /* Identifier */ ::=
-//~
-//~
-//~ /* InsertStatement */ ::=
-//~
-//~
-//~ /* InsertStatement */ ::=
-//~
-//~ -> from_constructor
-//~
-//~ /* []Identifier */ ::=
-//~
struct InsertStatement {
table_name Identifier
@@ -34,17 +12,6 @@ struct InsertStatement {
values []ContextuallyTypedRowValueConstructor
}
-fn parse_insert_statement(insertion_target Identifier, stmt InsertStatement) !Stmt {
- return InsertStatement{insertion_target, stmt.columns, stmt.values}
-}
-
-fn parse_from_constructor(columns []Identifier, values []ContextuallyTypedRowValueConstructor) !InsertStatement {
- return InsertStatement{
- columns: columns
- values: values
- }
-}
-
fn (stmt InsertStatement) execute(mut conn Connection, params map[string]Value, elapsed_parse time.Duration) !Result {
t := start_timer()
diff --git a/vsql/std_14_11_insert_statement.y b/vsql/std_14_11_insert_statement.y
new file mode 100644
index 0000000..39ada83
--- /dev/null
+++ b/vsql/std_14_11_insert_statement.y
@@ -0,0 +1,27 @@
+%%
+
+insert_statement:
+ INSERT INTO insertion_target insert_columns_and_source {
+ stmt := $4.v as InsertStatement
+ $$.v = Stmt(InsertStatement{$3.v as Identifier, stmt.columns, stmt.values})
+ }
+
+insertion_target:
+ table_name { $$.v = $1.v as Identifier }
+
+insert_columns_and_source:
+ from_constructor { $$.v = $1.v as InsertStatement }
+
+from_constructor:
+ left_paren insert_column_list right_paren
+ contextually_typed_table_value_constructor {
+ $$.v = InsertStatement{
+ columns: $2.v as []Identifier
+ values: $4.v as []ContextuallyTypedRowValueConstructor
+ }
+ }
+
+insert_column_list:
+ column_name_list { $$.v = $1.v as []Identifier }
+
+%%
diff --git a/vsql/std_update_statement_searched.v b/vsql/std_14_14_update_statement_searched.v
similarity index 87%
rename from vsql/std_update_statement_searched.v
rename to vsql/std_14_14_update_statement_searched.v
index 94f0139..64900ca 100644
--- a/vsql/std_update_statement_searched.v
+++ b/vsql/std_14_14_update_statement_searched.v
@@ -4,18 +4,7 @@ import time
// ISO/IEC 9075-2:2016(E), 14.14,
//
-// # Function
-//
// Update rows of a table.
-//
-// # Format
-//~
-//~ /* Stmt */ ::=
-//~ UPDATE
-//~ SET -> update_statement_searched_1
-//~ | UPDATE
-//~ SET
-//~ WHERE -> update_statement_searched_2
struct UpdateStatementSearched {
table_name Identifier
@@ -23,14 +12,6 @@ struct UpdateStatementSearched {
where ?BooleanValueExpression
}
-fn parse_update_statement_searched_1(target_table Identifier, set_clause_list map[string]UpdateSource) !Stmt {
- return UpdateStatementSearched{target_table, set_clause_list, none}
-}
-
-fn parse_update_statement_searched_2(target_table Identifier, set_clause_list map[string]UpdateSource, where BooleanValueExpression) !Stmt {
- return UpdateStatementSearched{target_table, set_clause_list, where}
-}
-
// UPDATE under MVCC works by actually executing a DELETE and an INSERT on the
// record to be updated. There are two important caveats for this:
//
diff --git a/vsql/std_14_14_update_statement_searched.y b/vsql/std_14_14_update_statement_searched.y
new file mode 100644
index 0000000..fe75dc0
--- /dev/null
+++ b/vsql/std_14_14_update_statement_searched.y
@@ -0,0 +1,13 @@
+%%
+
+update_statement_searched:
+ UPDATE target_table SET set_clause_list {
+ $$.v = Stmt(UpdateStatementSearched{$2.v as Identifier,
+ $4.v as map[string]UpdateSource, none})
+ }
+| UPDATE target_table SET set_clause_list WHERE search_condition {
+ $$.v = Stmt(UpdateStatementSearched{$2.v as Identifier,
+ $4.v as map[string]UpdateSource, $6.v as BooleanValueExpression})
+ }
+
+%%
diff --git a/vsql/std_14_15_set_clause_list.v b/vsql/std_14_15_set_clause_list.v
new file mode 100644
index 0000000..8f6c799
--- /dev/null
+++ b/vsql/std_14_15_set_clause_list.v
@@ -0,0 +1,23 @@
+module vsql
+
+// ISO/IEC 9075-2:2016(E), 14.15,
+//
+// Specify a list of updates.
+
+type UpdateSource = NullSpecification | ValueExpression
+
+fn (e UpdateSource) pstr(params map[string]Value) string {
+ return match e {
+ ValueExpression, NullSpecification {
+ e.pstr(params)
+ }
+ }
+}
+
+fn (e UpdateSource) compile(mut c Compiler) !CompileResult {
+ match e {
+ ValueExpression, NullSpecification {
+ return e.compile(mut c)!
+ }
+ }
+}
diff --git a/vsql/std_14_15_set_clause_list.y b/vsql/std_14_15_set_clause_list.y
new file mode 100644
index 0000000..d707413
--- /dev/null
+++ b/vsql/std_14_15_set_clause_list.y
@@ -0,0 +1,32 @@
+%%
+
+set_clause_list:
+ set_clause { $$.v = $1.v as map[string]UpdateSource }
+| set_clause_list comma set_clause {
+ $$.v = merge_maps($1.v as map[string]UpdateSource,
+ $3.v as map[string]UpdateSource)
+ }
+
+set_clause:
+ set_target equals_operator update_source {
+ $$.v = {
+ ($1.v as Identifier).str(): $3.v as UpdateSource
+ }
+ }
+
+set_target:
+ update_target { $$.v = $1.v as Identifier }
+
+update_target:
+ object_column { $$.v = $1.v as Identifier }
+
+update_source:
+ value_expression { $$.v = UpdateSource($1.v as ValueExpression) }
+| contextually_typed_value_specification {
+ $$.v = UpdateSource($1.v as NullSpecification)
+ }
+
+object_column:
+ column_name { $$.v = $1.v as Identifier }
+
+%%
diff --git a/vsql/std_14_3_cursor_specification.y b/vsql/std_14_3_cursor_specification.y
new file mode 100644
index 0000000..7b805d7
--- /dev/null
+++ b/vsql/std_14_3_cursor_specification.y
@@ -0,0 +1,10 @@
+%%
+
+// ISO/IEC 9075-2:2016(E), 14.3,
+//
+// Define a result set.
+
+cursor_specification:
+ query_expression { $$.v = Stmt($1.v as QueryExpression) }
+
+%%
diff --git a/vsql/std_14_8_delete_statement_positioned.y b/vsql/std_14_8_delete_statement_positioned.y
new file mode 100644
index 0000000..78b61d4
--- /dev/null
+++ b/vsql/std_14_8_delete_statement_positioned.y
@@ -0,0 +1,10 @@
+%%
+
+// ISO/IEC 9075-2:2016(E), 14.8,
+//
+// Delete a row of a table.
+
+target_table:
+ table_name { $$.v = $1.v as Identifier }
+
+%%
diff --git a/vsql/std_delete_statement_searched.v b/vsql/std_14_9_delete_statement_searched.v
similarity index 68%
rename from vsql/std_delete_statement_searched.v
rename to vsql/std_14_9_delete_statement_searched.v
index 71a07f2..ce0bb1c 100644
--- a/vsql/std_delete_statement_searched.v
+++ b/vsql/std_14_9_delete_statement_searched.v
@@ -4,30 +4,13 @@ import time
// ISO/IEC 9075-2:2016(E), 14.9,
//
-// # Function
-//
// Delete rows of a table.
-//
-// # Format
-//~
-//~ /* Stmt */ ::=
-//~ DELETE FROM -> delete_statement
-//~ | DELETE FROM
-//~ WHERE -> delete_statement_where
struct DeleteStatementSearched {
table_name Identifier
where ?BooleanValueExpression
}
-fn parse_delete_statement(table_name Identifier) !Stmt {
- return DeleteStatementSearched{table_name, none}
-}
-
-fn parse_delete_statement_where(table_name Identifier, where BooleanValueExpression) !Stmt {
- return DeleteStatementSearched{table_name, where}
-}
-
fn (stmt DeleteStatementSearched) explain(mut conn Connection, params map[string]Value, elapsed_parse time.Duration) !Result {
conn.open_write_connection()!
defer {
diff --git a/vsql/std_14_9_delete_statement_searched.y b/vsql/std_14_9_delete_statement_searched.y
new file mode 100644
index 0000000..e5b6db0
--- /dev/null
+++ b/vsql/std_14_9_delete_statement_searched.y
@@ -0,0 +1,14 @@
+%%
+
+delete_statement_searched:
+ DELETE FROM target_table {
+ $$.v = Stmt(DeleteStatementSearched{$3.v as Identifier, none})
+ }
+| DELETE FROM target_table WHERE search_condition {
+ $$.v = Stmt(DeleteStatementSearched{
+ $3.v as Identifier
+ $5.v as BooleanValueExpression
+ })
+ }
+
+%%
diff --git a/vsql/std_start_transaction_statement.v b/vsql/std_17_1_start_transaction_statement.v
similarity index 87%
rename from vsql/std_start_transaction_statement.v
rename to vsql/std_17_1_start_transaction_statement.v
index a8a5d9f..d4cf482 100644
--- a/vsql/std_start_transaction_statement.v
+++ b/vsql/std_17_1_start_transaction_statement.v
@@ -4,22 +4,11 @@ import time
// ISO/IEC 9075-2:2016(E), 17.1,
//
-// # Function
-//
// Start an SQL-transaction and set its characteristics.
-//
-// # Format
-//~
-//~ /* Stmt */ ::=
-//~ START TRANSACTION -> start_transaction_statement
struct StartTransactionStatement {
}
-fn parse_start_transaction_statement() !Stmt {
- return StartTransactionStatement{}
-}
-
fn (stmt StartTransactionStatement) execute(mut conn Connection, params map[string]Value, elapsed_parse time.Duration) !Result {
t := start_timer()
mut catalog := conn.catalog()
diff --git a/vsql/std_17_1_start_transaction_statement.y b/vsql/std_17_1_start_transaction_statement.y
new file mode 100644
index 0000000..fce0a31
--- /dev/null
+++ b/vsql/std_17_1_start_transaction_statement.y
@@ -0,0 +1,6 @@
+%%
+
+start_transaction_statement:
+ START TRANSACTION { $$.v = Stmt(StartTransactionStatement{}) }
+
+%%
diff --git a/vsql/std_commit_statement.v b/vsql/std_17_7_commit_statement.v
similarity index 88%
rename from vsql/std_commit_statement.v
rename to vsql/std_17_7_commit_statement.v
index a6144d3..65ff8bd 100644
--- a/vsql/std_commit_statement.v
+++ b/vsql/std_17_7_commit_statement.v
@@ -4,23 +4,11 @@ import time
// ISO/IEC 9075-2:2016(E), 17.7,
//
-// # Function
-//
// Terminate the current SQL-transaction with commit.
-//
-// # Format
-//~
-//~ /* Stmt */ ::=
-//~ COMMIT -> commit
-//~ | COMMIT WORK -> commit
struct CommitStatement {
}
-fn parse_commit() !Stmt {
- return CommitStatement{}
-}
-
fn (stmt CommitStatement) execute(mut conn Connection, params map[string]Value, elapsed_parse time.Duration) !Result {
t := start_timer()
mut catalog := conn.catalog()
diff --git a/vsql/std_17_7_commit_statement.y b/vsql/std_17_7_commit_statement.y
new file mode 100644
index 0000000..29aa910
--- /dev/null
+++ b/vsql/std_17_7_commit_statement.y
@@ -0,0 +1,7 @@
+%%
+
+commit_statement:
+ COMMIT { $$.v = Stmt(CommitStatement{}) }
+| COMMIT WORK { $$.v = Stmt(CommitStatement{}) }
+
+%%
diff --git a/vsql/std_rollback_statement.v b/vsql/std_17_8_rollback_statement.v
similarity index 88%
rename from vsql/std_rollback_statement.v
rename to vsql/std_17_8_rollback_statement.v
index 2ae8138..89b8382 100644
--- a/vsql/std_rollback_statement.v
+++ b/vsql/std_17_8_rollback_statement.v
@@ -4,24 +4,12 @@ import time
// ISO/IEC 9075-2:2016(E), 17.8,
//
-// # Function
-//
// Terminate the current SQL-transaction with rollback, or rollback all actions
// affecting SQL-data and/or schemas since the establishment of a savepoint.
-//
-// # Format
-//~
-//~ /* Stmt */ ::=
-//~ ROLLBACK -> rollback
-//~ | ROLLBACK WORK -> rollback
struct RollbackStatement {
}
-fn parse_rollback() !Stmt {
- return RollbackStatement{}
-}
-
fn (stmt RollbackStatement) execute(mut conn Connection, params map[string]Value, elapsed_parse time.Duration) !Result {
t := start_timer()
mut catalog := conn.catalog()
diff --git a/vsql/std_17_8_rollback_statement.y b/vsql/std_17_8_rollback_statement.y
new file mode 100644
index 0000000..098686b
--- /dev/null
+++ b/vsql/std_17_8_rollback_statement.y
@@ -0,0 +1,7 @@
+%%
+
+rollback_statement:
+ ROLLBACK { $$.v = Stmt(RollbackStatement{}) }
+| ROLLBACK WORK { $$.v = Stmt(RollbackStatement{}) }
+
+%%
diff --git a/vsql/std_set_catalog_statement.v b/vsql/std_19_5_set_catalog_statement.v
similarity index 72%
rename from vsql/std_set_catalog_statement.v
rename to vsql/std_19_5_set_catalog_statement.v
index 715e0c2..73ac6c1 100644
--- a/vsql/std_set_catalog_statement.v
+++ b/vsql/std_19_5_set_catalog_statement.v
@@ -4,20 +4,10 @@ import time
// ISO/IEC 9075-2:2016(E), 19.5,
//
-// # Function
-//
// Set the default catalog name for unqualified s in
// s that are prepared in the current SQL-session by an
// or a and in
// s that are invoked directly.
-//
-// # Format
-//~
-//~ /* Stmt */ ::=
-//~ SET -> set_catalog_stmt
-//~
-//~ /* ValueSpecification */ ::=
-//~ CATALOG -> catalog_name_characteristic
struct SetCatalogStatement {
catalog_name ValueSpecification
@@ -27,14 +17,6 @@ fn (e SetCatalogStatement) pstr(params map[string]Value) string {
return 'SET CATALOG ${e.catalog_name.pstr(params)}'
}
-fn parse_set_catalog_stmt(catalog_name ValueSpecification) !Stmt {
- return SetCatalogStatement{catalog_name}
-}
-
-fn parse_catalog_name_characteristic(v ValueSpecification) !ValueSpecification {
- return v
-}
-
fn (stmt SetCatalogStatement) execute(mut conn Connection, params map[string]Value, elapsed_parse time.Duration) !Result {
t := start_timer()
diff --git a/vsql/std_19_5_set_catalog_statement.y b/vsql/std_19_5_set_catalog_statement.y
new file mode 100644
index 0000000..573c81f
--- /dev/null
+++ b/vsql/std_19_5_set_catalog_statement.y
@@ -0,0 +1,11 @@
+%%
+
+set_catalog_statement:
+ SET catalog_name_characteristic {
+ $$.v = Stmt(SetCatalogStatement{$2.v as ValueSpecification})
+ }
+
+catalog_name_characteristic:
+ CATALOG value_specification { $$.v = $2.v as ValueSpecification }
+
+%%
diff --git a/vsql/std_set_schema_statement.v b/vsql/std_19_6_set_schema_statement.v
similarity index 73%
rename from vsql/std_set_schema_statement.v
rename to vsql/std_19_6_set_schema_statement.v
index 4e00813..103ed0c 100644
--- a/vsql/std_set_schema_statement.v
+++ b/vsql/std_19_6_set_schema_statement.v
@@ -4,20 +4,10 @@ import time
// ISO/IEC 9075-2:2016(E), 19.6,
//
-// # Function
-//
// Set the default schema name for unqualified s in
// s that are prepared in the current SQL-session by an
// or a and in
// s that are invoked directly.
-//
-// # Format
-//~
-//~ /* Stmt */ ::=
-//~ SET -> set_schema_stmt
-//~
-//~ /* ValueSpecification */ ::=
-//~ SCHEMA -> schema_name_characteristic
struct SetSchemaStatement {
schema_name ValueSpecification
@@ -27,14 +17,6 @@ fn (e SetSchemaStatement) pstr(params map[string]Value) string {
return 'SET SCHEMA ${e.schema_name.pstr(params)}'
}
-fn parse_schema_name_characteristic(v ValueSpecification) !ValueSpecification {
- return v
-}
-
-fn parse_set_schema_stmt(schema_name ValueSpecification) !Stmt {
- return SetSchemaStatement{schema_name}
-}
-
fn (stmt SetSchemaStatement) execute(mut conn Connection, params map[string]Value, elapsed_parse time.Duration) !Result {
t := start_timer()
diff --git a/vsql/std_19_6_set_schema_statement.y b/vsql/std_19_6_set_schema_statement.y
new file mode 100644
index 0000000..6286b99
--- /dev/null
+++ b/vsql/std_19_6_set_schema_statement.y
@@ -0,0 +1,11 @@
+%%
+
+set_schema_statement:
+ SET schema_name_characteristic {
+ $$.v = SetSchemaStatement{$2.v as ValueSpecification}
+ }
+
+schema_name_characteristic:
+ SCHEMA value_specification { $$.v = $2.v }
+
+%%
diff --git a/vsql/std_20_7_prepare_statement.y b/vsql/std_20_7_prepare_statement.y
new file mode 100644
index 0000000..581e65d
--- /dev/null
+++ b/vsql/std_20_7_prepare_statement.y
@@ -0,0 +1,31 @@
+%%
+
+// ISO/IEC 9075-2:2016(E), 20.7,
+//
+// Prepare a statement for execution.
+
+preparable_statement:
+ preparable_sql_data_statement { $$.v = $1.v as Stmt }
+| preparable_sql_schema_statement { $$.v = $1.v as Stmt }
+| preparable_sql_transaction_statement { $$.v = $1.v as Stmt }
+| preparable_sql_session_statement { $$.v = $1.v as Stmt }
+
+preparable_sql_data_statement:
+ delete_statement_searched { $$.v = $1.v as Stmt }
+| insert_statement { $$.v = $1.v as Stmt }
+| dynamic_select_statement { $$.v = $1.v as Stmt }
+| update_statement_searched { $$.v = $1.v as Stmt }
+
+preparable_sql_schema_statement:
+ sql_schema_statement { $$.v = $1.v as Stmt }
+
+preparable_sql_transaction_statement:
+ sql_transaction_statement { $$.v = $1.v as Stmt }
+
+preparable_sql_session_statement:
+ sql_session_statement { $$.v = $1.v as Stmt }
+
+dynamic_select_statement:
+ cursor_specification { $$.v = $1.v as Stmt }
+
+%%
diff --git a/vsql/std_sqlstate.v b/vsql/std_24_1_sqlstate.v
similarity index 100%
rename from vsql/std_sqlstate.v
rename to vsql/std_24_1_sqlstate.v
diff --git a/vsql/std_sql_schemas.v b/vsql/std_4_26_sql_schemas.v
similarity index 100%
rename from vsql/std_sql_schemas.v
rename to vsql/std_4_26_sql_schemas.v
diff --git a/vsql/std_sequence_generators.v b/vsql/std_4_27_sequence_generators.v
similarity index 100%
rename from vsql/std_sequence_generators.v
rename to vsql/std_4_27_sequence_generators.v
diff --git a/vsql/std_5_1_sql_terminal_character.y b/vsql/std_5_1_sql_terminal_character.y
new file mode 100644
index 0000000..e83d1cc
--- /dev/null
+++ b/vsql/std_5_1_sql_terminal_character.y
@@ -0,0 +1,31 @@
+%%
+
+// ISO/IEC 9075-2:2016(E), 5.1,
+//
+// Define the terminal symbols of the SQL language and the elements of strings.
+
+left_paren: OPERATOR_LEFT_PAREN
+
+right_paren: OPERATOR_RIGHT_PAREN
+
+asterisk: OPERATOR_ASTERISK { $$.v = $1.v as string }
+
+plus_sign: OPERATOR_PLUS { $$.v = $1.v as string }
+
+comma: OPERATOR_COMMA
+
+minus_sign: OPERATOR_MINUS { $$.v = $1.v as string }
+
+period: OPERATOR_PERIOD
+
+solidus: OPERATOR_SOLIDUS { $$.v = $1.v as string }
+
+colon: OPERATOR_COLON
+
+less_than_operator: OPERATOR_LESS_THAN
+
+equals_operator: OPERATOR_EQUALS
+
+greater_than_operator: OPERATOR_GREATER_THAN
+
+%%
diff --git a/vsql/std_5_2_token_and_separator.y b/vsql/std_5_2_token_and_separator.y
new file mode 100644
index 0000000..51b26ce
--- /dev/null
+++ b/vsql/std_5_2_token_and_separator.y
@@ -0,0 +1,304 @@
+%%
+
+concatenation_operator:
+ OPERATOR_DOUBLE_PIPE
+
+regular_identifier:
+ identifier_body { $$.v = $1.v as IdentifierChain }
+| non_reserved_word { $$.v = IdentifierChain{$1.v as string} }
+
+identifier_body:
+ identifier_start { $$.v = $1.v as IdentifierChain }
+
+identifier_start:
+ LITERAL_IDENTIFIER { $$.v = $1.v as IdentifierChain }
+
+not_equals_operator: OPERATOR_NOT_EQUALS
+
+greater_than_or_equals_operator: OPERATOR_GREATER_EQUALS
+
+less_than_or_equals_operator: OPERATOR_LESS_EQUALS
+
+non_reserved_word:
+ A
+| ABSOLUTE
+| ACTION
+| ADA
+| ADD
+| ADMIN
+| AFTER
+| ALWAYS
+| ASC
+| ASSERTION
+| ASSIGNMENT
+| ATTRIBUTE
+| ATTRIBUTES
+| BEFORE
+| BERNOULLI
+| BREADTH
+| C
+| CASCADE
+| CATALOG
+| CATALOG_NAME
+| CHAIN
+| CHAINING
+| CHARACTER_SET_CATALOG
+| CHARACTER_SET_NAME
+| CHARACTER_SET_SCHEMA
+| CHARACTERISTICS
+| CHARACTERS
+| CLASS_ORIGIN
+| COBOL
+| COLLATION
+| COLLATION_CATALOG
+| COLLATION_NAME
+| COLLATION_SCHEMA
+| COLUMNS
+| COLUMN_NAME
+| COMMAND_FUNCTION
+| COMMAND_FUNCTION_CODE
+| COMMITTED
+| CONDITIONAL
+| CONDITION_NUMBER
+| CONNECTION
+| CONNECTION_NAME
+| CONSTRAINT_CATALOG
+| CONSTRAINT_NAME
+| CONSTRAINT_SCHEMA
+| CONSTRAINTS
+| CONSTRUCTOR
+| CONTINUE
+| CURSOR_NAME
+| DATA
+| DATETIME_INTERVAL_CODE
+| DATETIME_INTERVAL_PRECISION
+| DEFAULTS
+| DEFERRABLE
+| DEFERRED
+| DEFINED
+| DEFINER
+| DEGREE
+| DEPTH
+| DERIVED
+| DESC
+| DESCRIBE_CATALOG
+| DESCRIBE_NAME
+| DESCRIBE_PROCEDURE_SPECIFIC_CATALOG
+| DESCRIBE_PROCEDURE_SPECIFIC_NAME
+| DESCRIBE_PROCEDURE_SPECIFIC_SCHEMA
+| DESCRIBE_SCHEMA
+| DESCRIPTOR
+| DIAGNOSTICS
+| DISPATCH
+| DOMAIN
+| DYNAMIC_FUNCTION
+| DYNAMIC_FUNCTION_CODE
+| ENCODING
+| ENFORCED
+| ERROR
+| EXCLUDE
+| EXCLUDING
+| EXPRESSION
+| FINAL
+| FINISH
+| FINISH_CATALOG
+| FINISH_NAME
+| FINISH_PROCEDURE_SPECIFIC_CATALOG
+| FINISH_PROCEDURE_SPECIFIC_NAME
+| FINISH_PROCEDURE_SPECIFIC_SCHEMA
+| FINISH_SCHEMA
+| FIRST
+| FLAG
+| FOLLOWING
+| FORMAT
+| FORTRAN
+| FOUND
+| FULFILL
+| FULFILL_CATALOG
+| FULFILL_NAME
+| FULFILL_PROCEDURE_SPECIFIC_CATALOG
+| FULFILL_PROCEDURE_SPECIFIC_NAME
+| FULFILL_PROCEDURE_SPECIFIC_SCHEMA
+| FULFILL_SCHEMA
+| G
+| GENERAL
+| GENERATED
+| GO
+| GOTO
+| GRANTED
+| HAS_PASS_THROUGH_COLUMNS
+| HAS_PASS_THRU_COLS
+| HIERARCHY
+| IGNORE
+| IMMEDIATE
+| IMMEDIATELY
+| IMPLEMENTATION
+| INCLUDING
+| INCREMENT
+| INITIALLY
+| INPUT
+| INSTANCE
+| INSTANTIABLE
+| INSTEAD
+| INVOKER
+| ISOLATION
+| IS_PRUNABLE
+| JSON
+| K
+| KEEP
+| KEY
+| KEYS
+| KEY_MEMBER
+| KEY_TYPE
+| LAST
+| LENGTH
+| LEVEL
+| LOCATOR
+| M
+| MAP
+| MATCHED
+| MAXVALUE
+| MESSAGE_LENGTH
+| MESSAGE_OCTET_LENGTH
+| MESSAGE_TEXT
+| MINVALUE
+| MORE
+| MUMPS
+| NAME
+| NAMES
+| NESTED
+| NESTING
+| NEXT
+| NFC
+| NFD
+| NFKC
+| NFKD
+| NORMALIZED
+| NULLABLE
+| NULLS
+| NUMBER
+| OBJECT
+| OCTETS
+| OPTION
+| OPTIONS
+| ORDERING
+| ORDINALITY
+| OTHERS
+| OUTPUT
+| OVERFLOW
+| OVERRIDING
+| P
+| PAD
+| PARAMETER_MODE
+| PARAMETER_NAME
+| PARAMETER_ORDINAL_POSITION
+| PARAMETER_SPECIFIC_CATALOG
+| PARAMETER_SPECIFIC_NAME
+| PARAMETER_SPECIFIC_SCHEMA
+| PARTIAL
+| PASCAL
+| PASS
+| PASSING
+| PAST
+| PATH
+| PLACING
+| PLAN
+| PLI
+| PRECEDING
+| PRESERVE
+| PRIOR
+| PRIVATE
+| PRIVATE_PARAMETERS
+| PRIVATE_PARAMS_S
+| PRIVILEGES
+| PRUNE
+| PUBLIC
+| QUOTES
+| READ
+| RELATIVE
+| REPEATABLE
+| RESPECT
+| RESTART
+| RESTRICT
+| RETURNED_CARDINALITY
+| RETURNED_LENGTH
+| RETURNED_OCTET_LENGTH
+| RETURNED_SQLSTATE
+| RETURNING
+| RETURNS_ONLY_PASS_THROUGH
+| RET_ONLY_PASS_THRU
+| ROLE
+| ROUTINE
+| ROUTINE_CATALOG
+| ROUTINE_NAME
+| ROUTINE_SCHEMA
+| ROW_COUNT
+| SCALAR
+| SCALE
+| SCHEMA
+| SCHEMA_NAME
+| SCOPE_CATALOG
+| SCOPE_NAME
+| SCOPE_SCHEMA
+| SECTION
+| SECURITY
+| SELF
+| SEQUENCE
+| SERIALIZABLE
+| SERVER_NAME
+| SESSION
+| SETS
+| SIMPLE
+| SIZE
+| SOURCE
+| SPACE
+| SPECIFIC_NAME
+| START_CATALOG
+| START_NAME
+| START_PROCEDURE_SPECIFIC_CATALOG
+| START_PROCEDURE_SPECIFIC_NAME
+| START_PROCEDURE_SPECIFIC_SCHEMA
+| START_SCHEMA
+| STATE
+| STATEMENT
+| STRING
+| STRUCTURE
+| STYLE
+| SUBCLASS_ORIGIN
+| T
+| TABLE_NAME
+| TABLE_SEMANTICS
+| TEMPORARY
+| THROUGH
+| TIES
+| TOP_LEVEL_COUNT
+| TRANSACTION
+| TRANSACTION_ACTIVE
+| TRANSACTIONS_COMMITTED
+| TRANSACTIONS_ROLLED_BACK
+| TRANSFORM
+| TRANSFORMS
+| TRIGGER_CATALOG
+| TRIGGER_NAME
+| TRIGGER_SCHEMA
+| TYPE
+| UNBOUNDED
+| UNCOMMITTED
+| UNCONDITIONAL
+| UNDER
+| UNNAMED
+| USAGE
+| USER_DEFINED_TYPE_CATALOG
+| USER_DEFINED_TYPE_CODE
+| USER_DEFINED_TYPE_NAME
+| USER_DEFINED_TYPE_SCHEMA
+| UTF16
+| UTF32
+| UTF8
+| VIEW
+| WORK
+| WRAPPER
+| WRITE
+| ZONE
+
+%%
diff --git a/vsql/std_5_3_literal.v b/vsql/std_5_3_literal.v
new file mode 100644
index 0000000..db3fb29
--- /dev/null
+++ b/vsql/std_5_3_literal.v
@@ -0,0 +1,38 @@
+module vsql
+
+import math.big
+
+// ISO/IEC 9075-2:2016(E), 5.3,
+//
+// Specify a non-null value.
+
+fn numeric_literal(x string) !Value {
+ // Any number that contains a decimal (even if its a whole number) must be
+ // treated as a NUMERIC.
+ if x.contains('.') {
+ // The trim handles cases of "123." which should be treated as "123".
+ return new_numeric_value(x.trim_right('.'))
+ }
+
+ // Otherwise, we know this is an int but we have to choose the smallest type.
+ //
+ // Note: There is an edge case where the negative sign may be consumed as part
+ // of rather than . See parse_factor_2() for
+ // those edge cases.
+ n := big.integer_from_string(x)!
+
+ if n >= big.integer_from_i64(-32768) && n <= big.integer_from_i64(32767) {
+ return new_smallint_value(i16(x.i64()))
+ }
+
+ if n >= big.integer_from_i64(-2147483648) && n <= big.integer_from_i64(2147483647) {
+ return new_integer_value(int(x.i64()))
+ }
+
+ if n >= big.integer_from_i64(-9223372036854775808)
+ && n <= big.integer_from_i64(9223372036854775807) {
+ return new_bigint_value(x.i64())
+ }
+
+ return new_numeric_value(x)
+}
diff --git a/vsql/std_5_3_literal.y b/vsql/std_5_3_literal.y
new file mode 100644
index 0000000..4ac634e
--- /dev/null
+++ b/vsql/std_5_3_literal.y
@@ -0,0 +1,96 @@
+%%
+
+literal:
+ signed_numeric_literal { $$.v = $1.v as Value }
+| general_literal { $$.v = $1.v as Value }
+
+unsigned_literal:
+ unsigned_numeric_literal { $$.v = $1.v as Value }
+| general_literal { $$.v = $1.v as Value }
+
+general_literal:
+ character_string_literal { $$.v = $1.v as Value }
+| datetime_literal { $$.v = $1.v as Value }
+| boolean_literal { $$.v = $1.v as Value }
+
+character_string_literal:
+ LITERAL_STRING { $$.v = $1.v as Value }
+
+signed_numeric_literal:
+ unsigned_numeric_literal { $$.v = $1.v as Value }
+| sign unsigned_numeric_literal {
+ $$.v = numeric_literal($1.v as string + ($2.v as Value).str())!
+ }
+
+unsigned_numeric_literal:
+ exact_numeric_literal { $$.v = $1.v as Value }
+| approximate_numeric_literal { $$.v = $1.v as Value }
+
+exact_numeric_literal:
+ unsigned_integer { $$.v = numeric_literal($1.v as string)! }
+| unsigned_integer period { $$.v = numeric_literal(($1.v as string) + '.')! }
+| unsigned_integer period unsigned_integer {
+ $$.v = numeric_literal(($1.v as string) + '.' + ($3.v as string))!
+ }
+| period unsigned_integer { $$.v = numeric_literal('0.' + ($2.v as string))! }
+
+sign:
+ plus_sign { $$.v = $1.v as string }
+| minus_sign { $$.v = $1.v as string }
+
+approximate_numeric_literal:
+ mantissa E exponent {
+ $$.v = new_double_precision_value(
+ ($1.v as Value).as_f64()! * math.pow(10, ($3.v as Value).as_f64()!))
+ }
+
+mantissa:
+ exact_numeric_literal { $$.v = $1.v as Value }
+
+exponent:
+ signed_integer { $$.v = $1.v as Value }
+
+signed_integer:
+ unsigned_integer { $$.v = new_numeric_value($1.v as string) }
+| sign unsigned_integer {
+ $$.v = if $1.v as string == '-' {
+ new_numeric_value('-' + ($2.v as string))
+ } else {
+ new_numeric_value($2.v as string)
+ }
+ }
+
+unsigned_integer:
+ LITERAL_NUMBER { $$.v = $1.v as string }
+
+datetime_literal:
+ date_literal { $$.v = $1.v as Value }
+| time_literal { $$.v = $1.v as Value }
+| timestamp_literal { $$.v = $1.v as Value }
+
+date_literal:
+ DATE date_string { $$.v = new_date_value(($2.v as Value).string_value())! }
+
+time_literal:
+ TIME time_string { $$.v = new_time_value(($2.v as Value).string_value())! }
+
+timestamp_literal:
+ TIMESTAMP timestamp_string {
+ $$.v = new_timestamp_value(($2.v as Value).string_value())!
+ }
+
+date_string:
+ LITERAL_STRING { $$.v = $1.v as Value }
+
+time_string:
+ LITERAL_STRING { $$.v = $1.v as Value }
+
+timestamp_string:
+ LITERAL_STRING { $$.v = $1.v as Value }
+
+boolean_literal:
+ TRUE { $$.v = new_boolean_value(true) }
+| FALSE { $$.v = new_boolean_value(false) }
+| UNKNOWN { $$.v = new_unknown_value() }
+
+%%
diff --git a/vsql/std_names_and_identifiers.v b/vsql/std_5_4_names_and_identifiers.v
similarity index 81%
rename from vsql/std_names_and_identifiers.v
rename to vsql/std_5_4_names_and_identifiers.v
index 10174c9..df809b5 100644
--- a/vsql/std_names_and_identifiers.v
+++ b/vsql/std_5_4_names_and_identifiers.v
@@ -2,57 +2,7 @@ module vsql
// ISO/IEC 9075-2:2016(E), 5.4, Names and identifiers
//
-// # Function
-//
// Specify names.
-//
-// # Format
-//~
-//~ /* IdentifierChain */ ::=
-//~
-//~
-//~ /* IdentifierChain */ ::=
-//~
-//~
-//~ /* Identifier */ ::=
-//~ -> table_name
-//~
-//~ /* Identifier */ ::=
-//~ -> schema_name_1
-//~ |
-//~
-//~ /* Identifier */ ::=
-//~