@quereus/quereus 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +228 -0
- package/dist/src/common/constants.d.ts +49 -0
- package/dist/src/common/constants.d.ts.map +1 -0
- package/dist/src/common/constants.js +62 -0
- package/dist/src/common/constants.js.map +1 -0
- package/dist/src/common/datatype.d.ts +72 -0
- package/dist/src/common/datatype.d.ts.map +1 -0
- package/dist/src/common/datatype.js +10 -0
- package/dist/src/common/datatype.js.map +1 -0
- package/dist/src/common/errors.d.ts +90 -0
- package/dist/src/common/errors.d.ts.map +1 -0
- package/dist/src/common/errors.js +142 -0
- package/dist/src/common/errors.js.map +1 -0
- package/dist/src/common/logger.d.ts +18 -0
- package/dist/src/common/logger.d.ts.map +1 -0
- package/dist/src/common/logger.js +22 -0
- package/dist/src/common/logger.js.map +1 -0
- package/dist/src/common/type-inference.d.ts +11 -0
- package/dist/src/common/type-inference.d.ts.map +1 -0
- package/dist/src/common/type-inference.js +44 -0
- package/dist/src/common/type-inference.js.map +1 -0
- package/dist/src/common/types.d.ts +131 -0
- package/dist/src/common/types.d.ts.map +1 -0
- package/dist/src/common/types.js +53 -0
- package/dist/src/common/types.js.map +1 -0
- package/dist/src/core/database-options.d.ts +67 -0
- package/dist/src/core/database-options.d.ts.map +1 -0
- package/dist/src/core/database-options.js +211 -0
- package/dist/src/core/database-options.js.map +1 -0
- package/dist/src/core/database.d.ts +214 -0
- package/dist/src/core/database.d.ts.map +1 -0
- package/dist/src/core/database.js +750 -0
- package/dist/src/core/database.js.map +1 -0
- package/dist/src/core/param.d.ts +4 -0
- package/dist/src/core/param.d.ts.map +1 -0
- package/dist/src/core/param.js +46 -0
- package/dist/src/core/param.js.map +1 -0
- package/dist/src/core/statement.d.ts +94 -0
- package/dist/src/core/statement.d.ts.map +1 -0
- package/dist/src/core/statement.js +482 -0
- package/dist/src/core/statement.js.map +1 -0
- package/dist/src/func/builtins/aggregate.d.ts +13 -0
- package/dist/src/func/builtins/aggregate.d.ts.map +1 -0
- package/dist/src/func/builtins/aggregate.js +190 -0
- package/dist/src/func/builtins/aggregate.js.map +1 -0
- package/dist/src/func/builtins/builtin-window-functions.d.ts +2 -0
- package/dist/src/func/builtins/builtin-window-functions.d.ts.map +1 -0
- package/dist/src/func/builtins/builtin-window-functions.js +161 -0
- package/dist/src/func/builtins/builtin-window-functions.js.map +1 -0
- package/dist/src/func/builtins/datetime.d.ts +6 -0
- package/dist/src/func/builtins/datetime.d.ts.map +1 -0
- package/dist/src/func/builtins/datetime.js +417 -0
- package/dist/src/func/builtins/datetime.js.map +1 -0
- package/dist/src/func/builtins/explain.d.ts +7 -0
- package/dist/src/func/builtins/explain.d.ts.map +1 -0
- package/dist/src/func/builtins/explain.js +570 -0
- package/dist/src/func/builtins/explain.js.map +1 -0
- package/dist/src/func/builtins/generation.d.ts +2 -0
- package/dist/src/func/builtins/generation.d.ts.map +1 -0
- package/dist/src/func/builtins/generation.js +36 -0
- package/dist/src/func/builtins/generation.js.map +1 -0
- package/dist/src/func/builtins/index.d.ts +4 -0
- package/dist/src/func/builtins/index.d.ts.map +1 -0
- package/dist/src/func/builtins/index.js +106 -0
- package/dist/src/func/builtins/index.js.map +1 -0
- package/dist/src/func/builtins/json-helpers.d.ts +64 -0
- package/dist/src/func/builtins/json-helpers.d.ts.map +1 -0
- package/dist/src/func/builtins/json-helpers.js +237 -0
- package/dist/src/func/builtins/json-helpers.js.map +1 -0
- package/dist/src/func/builtins/json-tvf.d.ts +3 -0
- package/dist/src/func/builtins/json-tvf.d.ts.map +1 -0
- package/dist/src/func/builtins/json-tvf.js +199 -0
- package/dist/src/func/builtins/json-tvf.js.map +1 -0
- package/dist/src/func/builtins/json.d.ts +15 -0
- package/dist/src/func/builtins/json.d.ts.map +1 -0
- package/dist/src/func/builtins/json.js +417 -0
- package/dist/src/func/builtins/json.js.map +1 -0
- package/dist/src/func/builtins/scalar.d.ts +19 -0
- package/dist/src/func/builtins/scalar.d.ts.map +1 -0
- package/dist/src/func/builtins/scalar.js +176 -0
- package/dist/src/func/builtins/scalar.js.map +1 -0
- package/dist/src/func/builtins/schema.d.ts +4 -0
- package/dist/src/func/builtins/schema.d.ts.map +1 -0
- package/dist/src/func/builtins/schema.js +167 -0
- package/dist/src/func/builtins/schema.js.map +1 -0
- package/dist/src/func/builtins/string.d.ts +18 -0
- package/dist/src/func/builtins/string.d.ts.map +1 -0
- package/dist/src/func/builtins/string.js +233 -0
- package/dist/src/func/builtins/string.js.map +1 -0
- package/dist/src/func/context.d.ts +102 -0
- package/dist/src/func/context.d.ts.map +1 -0
- package/dist/src/func/context.js +130 -0
- package/dist/src/func/context.js.map +1 -0
- package/dist/src/func/registration.d.ts +89 -0
- package/dist/src/func/registration.d.ts.map +1 -0
- package/dist/src/func/registration.js +103 -0
- package/dist/src/func/registration.js.map +1 -0
- package/dist/src/index.d.ts +34 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +35 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/parser/ast.d.ts +376 -0
- package/dist/src/parser/ast.d.ts.map +1 -0
- package/dist/src/parser/ast.js +2 -0
- package/dist/src/parser/ast.js.map +1 -0
- package/dist/src/parser/index.d.ts +29 -0
- package/dist/src/parser/index.d.ts.map +1 -0
- package/dist/src/parser/index.js +50 -0
- package/dist/src/parser/index.js.map +1 -0
- package/dist/src/parser/lexer.d.ts +197 -0
- package/dist/src/parser/lexer.d.ts.map +1 -0
- package/dist/src/parser/lexer.js +762 -0
- package/dist/src/parser/lexer.js.map +1 -0
- package/dist/src/parser/parser.d.ts +216 -0
- package/dist/src/parser/parser.d.ts.map +1 -0
- package/dist/src/parser/parser.js +2619 -0
- package/dist/src/parser/parser.js.map +1 -0
- package/dist/src/parser/utils.d.ts +4 -0
- package/dist/src/parser/utils.d.ts.map +1 -0
- package/dist/src/parser/utils.js +8 -0
- package/dist/src/parser/utils.js.map +1 -0
- package/dist/src/parser/visitor.d.ts +38 -0
- package/dist/src/parser/visitor.d.ts.map +1 -0
- package/dist/src/parser/visitor.js +153 -0
- package/dist/src/parser/visitor.js.map +1 -0
- package/dist/src/planner/analysis/const-evaluator.d.ts +14 -0
- package/dist/src/planner/analysis/const-evaluator.d.ts.map +1 -0
- package/dist/src/planner/analysis/const-evaluator.js +50 -0
- package/dist/src/planner/analysis/const-evaluator.js.map +1 -0
- package/dist/src/planner/analysis/const-pass.d.ts +51 -0
- package/dist/src/planner/analysis/const-pass.d.ts.map +1 -0
- package/dist/src/planner/analysis/const-pass.js +200 -0
- package/dist/src/planner/analysis/const-pass.js.map +1 -0
- package/dist/src/planner/analysis/constraint-extractor.d.ts +57 -0
- package/dist/src/planner/analysis/constraint-extractor.d.ts.map +1 -0
- package/dist/src/planner/analysis/constraint-extractor.js +193 -0
- package/dist/src/planner/analysis/constraint-extractor.js.map +1 -0
- package/dist/src/planner/building/alter-table.d.ts +5 -0
- package/dist/src/planner/building/alter-table.d.ts.map +1 -0
- package/dist/src/planner/building/alter-table.js +26 -0
- package/dist/src/planner/building/alter-table.js.map +1 -0
- package/dist/src/planner/building/block.d.ts +5 -0
- package/dist/src/planner/building/block.d.ts.map +1 -0
- package/dist/src/planner/building/block.js +68 -0
- package/dist/src/planner/building/block.js.map +1 -0
- package/dist/src/planner/building/create-view.d.ts +8 -0
- package/dist/src/planner/building/create-view.d.ts.map +1 -0
- package/dist/src/planner/building/create-view.js +37 -0
- package/dist/src/planner/building/create-view.js.map +1 -0
- package/dist/src/planner/building/ddl.d.ts +7 -0
- package/dist/src/planner/building/ddl.d.ts.map +1 -0
- package/dist/src/planner/building/ddl.js +9 -0
- package/dist/src/planner/building/ddl.js.map +1 -0
- package/dist/src/planner/building/delete.d.ts +5 -0
- package/dist/src/planner/building/delete.d.ts.map +1 -0
- package/dist/src/planner/building/delete.js +105 -0
- package/dist/src/planner/building/delete.js.map +1 -0
- package/dist/src/planner/building/drop-table.d.ts +5 -0
- package/dist/src/planner/building/drop-table.d.ts.map +1 -0
- package/dist/src/planner/building/drop-table.js +5 -0
- package/dist/src/planner/building/drop-table.js.map +1 -0
- package/dist/src/planner/building/drop-view.d.ts +8 -0
- package/dist/src/planner/building/drop-view.d.ts.map +1 -0
- package/dist/src/planner/building/drop-view.js +11 -0
- package/dist/src/planner/building/drop-view.js.map +1 -0
- package/dist/src/planner/building/expression.d.ts +5 -0
- package/dist/src/planner/building/expression.d.ts.map +1 -0
- package/dist/src/planner/building/expression.js +147 -0
- package/dist/src/planner/building/expression.js.map +1 -0
- package/dist/src/planner/building/function-call.d.ts +5 -0
- package/dist/src/planner/building/function-call.d.ts.map +1 -0
- package/dist/src/planner/building/function-call.js +70 -0
- package/dist/src/planner/building/function-call.js.map +1 -0
- package/dist/src/planner/building/insert.d.ts +5 -0
- package/dist/src/planner/building/insert.d.ts.map +1 -0
- package/dist/src/planner/building/insert.js +261 -0
- package/dist/src/planner/building/insert.js.map +1 -0
- package/dist/src/planner/building/pragma.d.ts +5 -0
- package/dist/src/planner/building/pragma.d.ts.map +1 -0
- package/dist/src/planner/building/pragma.js +28 -0
- package/dist/src/planner/building/pragma.js.map +1 -0
- package/dist/src/planner/building/schema-resolution.d.ts +25 -0
- package/dist/src/planner/building/schema-resolution.d.ts.map +1 -0
- package/dist/src/planner/building/schema-resolution.js +119 -0
- package/dist/src/planner/building/schema-resolution.js.map +1 -0
- package/dist/src/planner/building/select-aggregates.d.ts +22 -0
- package/dist/src/planner/building/select-aggregates.d.ts.map +1 -0
- package/dist/src/planner/building/select-aggregates.js +164 -0
- package/dist/src/planner/building/select-aggregates.js.map +1 -0
- package/dist/src/planner/building/select-compound.d.ts +9 -0
- package/dist/src/planner/building/select-compound.d.ts.map +1 -0
- package/dist/src/planner/building/select-compound.js +78 -0
- package/dist/src/planner/building/select-compound.js.map +1 -0
- package/dist/src/planner/building/select-context.d.ts +18 -0
- package/dist/src/planner/building/select-context.d.ts.map +1 -0
- package/dist/src/planner/building/select-context.js +54 -0
- package/dist/src/planner/building/select-context.js.map +1 -0
- package/dist/src/planner/building/select-modifiers.d.ts +25 -0
- package/dist/src/planner/building/select-modifiers.d.ts.map +1 -0
- package/dist/src/planner/building/select-modifiers.js +120 -0
- package/dist/src/planner/building/select-modifiers.js.map +1 -0
- package/dist/src/planner/building/select-projections.d.ts +37 -0
- package/dist/src/planner/building/select-projections.d.ts.map +1 -0
- package/dist/src/planner/building/select-projections.js +132 -0
- package/dist/src/planner/building/select-projections.js.map +1 -0
- package/dist/src/planner/building/select-window.d.ts +12 -0
- package/dist/src/planner/building/select-window.d.ts.map +1 -0
- package/dist/src/planner/building/select-window.js +159 -0
- package/dist/src/planner/building/select-window.js.map +1 -0
- package/dist/src/planner/building/select.d.ts +28 -0
- package/dist/src/planner/building/select.d.ts.map +1 -0
- package/dist/src/planner/building/select.js +307 -0
- package/dist/src/planner/building/select.js.map +1 -0
- package/dist/src/planner/building/table-function.d.ts +5 -0
- package/dist/src/planner/building/table-function.d.ts.map +1 -0
- package/dist/src/planner/building/table-function.js +21 -0
- package/dist/src/planner/building/table-function.js.map +1 -0
- package/dist/src/planner/building/table.d.ts +23 -0
- package/dist/src/planner/building/table.d.ts.map +1 -0
- package/dist/src/planner/building/table.js +57 -0
- package/dist/src/planner/building/table.js.map +1 -0
- package/dist/src/planner/building/transaction.d.ts +9 -0
- package/dist/src/planner/building/transaction.d.ts.map +1 -0
- package/dist/src/planner/building/transaction.js +17 -0
- package/dist/src/planner/building/transaction.js.map +1 -0
- package/dist/src/planner/building/update.d.ts +5 -0
- package/dist/src/planner/building/update.d.ts.map +1 -0
- package/dist/src/planner/building/update.js +152 -0
- package/dist/src/planner/building/update.js.map +1 -0
- package/dist/src/planner/building/with.d.ts +13 -0
- package/dist/src/planner/building/with.d.ts.map +1 -0
- package/dist/src/planner/building/with.js +112 -0
- package/dist/src/planner/building/with.js.map +1 -0
- package/dist/src/planner/cache/materialization-advisory.d.ts +66 -0
- package/dist/src/planner/cache/materialization-advisory.d.ts.map +1 -0
- package/dist/src/planner/cache/materialization-advisory.js +187 -0
- package/dist/src/planner/cache/materialization-advisory.js.map +1 -0
- package/dist/src/planner/cache/reference-graph.d.ts +53 -0
- package/dist/src/planner/cache/reference-graph.d.ts.map +1 -0
- package/dist/src/planner/cache/reference-graph.js +139 -0
- package/dist/src/planner/cache/reference-graph.js.map +1 -0
- package/dist/src/planner/cost/index.d.ts +106 -0
- package/dist/src/planner/cost/index.d.ts.map +1 -0
- package/dist/src/planner/cost/index.js +143 -0
- package/dist/src/planner/cost/index.js.map +1 -0
- package/dist/src/planner/debug/logger-utils.d.ts +44 -0
- package/dist/src/planner/debug/logger-utils.d.ts.map +1 -0
- package/dist/src/planner/debug/logger-utils.js +58 -0
- package/dist/src/planner/debug/logger-utils.js.map +1 -0
- package/dist/src/planner/debug.d.ts +53 -0
- package/dist/src/planner/debug.d.ts.map +1 -0
- package/dist/src/planner/debug.js +228 -0
- package/dist/src/planner/debug.js.map +1 -0
- package/dist/src/planner/framework/context.d.ts +86 -0
- package/dist/src/planner/framework/context.d.ts.map +1 -0
- package/dist/src/planner/framework/context.js +117 -0
- package/dist/src/planner/framework/context.js.map +1 -0
- package/dist/src/planner/framework/physical-utils.d.ts +62 -0
- package/dist/src/planner/framework/physical-utils.d.ts.map +1 -0
- package/dist/src/planner/framework/physical-utils.js +149 -0
- package/dist/src/planner/framework/physical-utils.js.map +1 -0
- package/dist/src/planner/framework/registry.d.ts +75 -0
- package/dist/src/planner/framework/registry.d.ts.map +1 -0
- package/dist/src/planner/framework/registry.js +203 -0
- package/dist/src/planner/framework/registry.js.map +1 -0
- package/dist/src/planner/framework/trace.d.ts +84 -0
- package/dist/src/planner/framework/trace.d.ts.map +1 -0
- package/dist/src/planner/framework/trace.js +198 -0
- package/dist/src/planner/framework/trace.js.map +1 -0
- package/dist/src/planner/nodes/add-constraint-node.d.ts +23 -0
- package/dist/src/planner/nodes/add-constraint-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/add-constraint-node.js +47 -0
- package/dist/src/planner/nodes/add-constraint-node.js.map +1 -0
- package/dist/src/planner/nodes/aggregate-function.d.ts +34 -0
- package/dist/src/planner/nodes/aggregate-function.d.ts.map +1 -0
- package/dist/src/planner/nodes/aggregate-function.js +119 -0
- package/dist/src/planner/nodes/aggregate-function.js.map +1 -0
- package/dist/src/planner/nodes/aggregate-node.d.ts +35 -0
- package/dist/src/planner/nodes/aggregate-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/aggregate-node.js +176 -0
- package/dist/src/planner/nodes/aggregate-node.js.map +1 -0
- package/dist/src/planner/nodes/array-index-node.d.ts +22 -0
- package/dist/src/planner/nodes/array-index-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/array-index-node.js +41 -0
- package/dist/src/planner/nodes/array-index-node.js.map +1 -0
- package/dist/src/planner/nodes/block.d.ts +26 -0
- package/dist/src/planner/nodes/block.d.ts.map +1 -0
- package/dist/src/planner/nodes/block.js +58 -0
- package/dist/src/planner/nodes/block.js.map +1 -0
- package/dist/src/planner/nodes/cache-node.d.ts +29 -0
- package/dist/src/planner/nodes/cache-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/cache-node.js +73 -0
- package/dist/src/planner/nodes/cache-node.js.map +1 -0
- package/dist/src/planner/nodes/constraint-check-node.d.ts +28 -0
- package/dist/src/planner/nodes/constraint-check-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/constraint-check-node.js +73 -0
- package/dist/src/planner/nodes/constraint-check-node.js.map +1 -0
- package/dist/src/planner/nodes/create-index-node.d.ts +15 -0
- package/dist/src/planner/nodes/create-index-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/create-index-node.js +31 -0
- package/dist/src/planner/nodes/create-index-node.js.map +1 -0
- package/dist/src/planner/nodes/create-table-node.d.ts +15 -0
- package/dist/src/planner/nodes/create-table-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/create-table-node.js +25 -0
- package/dist/src/planner/nodes/create-table-node.js.map +1 -0
- package/dist/src/planner/nodes/create-view-node.d.ts +21 -0
- package/dist/src/planner/nodes/create-view-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/create-view-node.js +39 -0
- package/dist/src/planner/nodes/create-view-node.js.map +1 -0
- package/dist/src/planner/nodes/cte-node.d.ts +40 -0
- package/dist/src/planner/nodes/cte-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/cte-node.js +88 -0
- package/dist/src/planner/nodes/cte-node.js.map +1 -0
- package/dist/src/planner/nodes/cte-reference-node.d.ts +27 -0
- package/dist/src/planner/nodes/cte-reference-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/cte-reference-node.js +75 -0
- package/dist/src/planner/nodes/cte-reference-node.js.map +1 -0
- package/dist/src/planner/nodes/delete-node.d.ts +27 -0
- package/dist/src/planner/nodes/delete-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/delete-node.js +62 -0
- package/dist/src/planner/nodes/delete-node.js.map +1 -0
- package/dist/src/planner/nodes/distinct-node.d.ts +22 -0
- package/dist/src/planner/nodes/distinct-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/distinct-node.js +74 -0
- package/dist/src/planner/nodes/distinct-node.js.map +1 -0
- package/dist/src/planner/nodes/dml-executor-node.d.ts +30 -0
- package/dist/src/planner/nodes/dml-executor-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/dml-executor-node.js +73 -0
- package/dist/src/planner/nodes/dml-executor-node.js.map +1 -0
- package/dist/src/planner/nodes/drop-table-node.d.ts +15 -0
- package/dist/src/planner/nodes/drop-table-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/drop-table-node.js +25 -0
- package/dist/src/planner/nodes/drop-table-node.js.map +1 -0
- package/dist/src/planner/nodes/drop-view-node.d.ts +17 -0
- package/dist/src/planner/nodes/drop-view-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/drop-view-node.js +30 -0
- package/dist/src/planner/nodes/drop-view-node.js.map +1 -0
- package/dist/src/planner/nodes/filter.d.ts +24 -0
- package/dist/src/planner/nodes/filter.d.ts.map +1 -0
- package/dist/src/planner/nodes/filter.js +72 -0
- package/dist/src/planner/nodes/filter.js.map +1 -0
- package/dist/src/planner/nodes/function.d.ts +21 -0
- package/dist/src/planner/nodes/function.d.ts.map +1 -0
- package/dist/src/planner/nodes/function.js +70 -0
- package/dist/src/planner/nodes/function.js.map +1 -0
- package/dist/src/planner/nodes/insert-node.d.ts +26 -0
- package/dist/src/planner/nodes/insert-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/insert-node.js +82 -0
- package/dist/src/planner/nodes/insert-node.js.map +1 -0
- package/dist/src/planner/nodes/join-node.d.ts +30 -0
- package/dist/src/planner/nodes/join-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/join-node.js +177 -0
- package/dist/src/planner/nodes/join-node.js.map +1 -0
- package/dist/src/planner/nodes/limit-offset.d.ts +24 -0
- package/dist/src/planner/nodes/limit-offset.d.ts.map +1 -0
- package/dist/src/planner/nodes/limit-offset.js +103 -0
- package/dist/src/planner/nodes/limit-offset.js.map +1 -0
- package/dist/src/planner/nodes/physical-access-nodes.d.ts +83 -0
- package/dist/src/planner/nodes/physical-access-nodes.d.ts.map +1 -0
- package/dist/src/planner/nodes/physical-access-nodes.js +226 -0
- package/dist/src/planner/nodes/physical-access-nodes.js.map +1 -0
- package/dist/src/planner/nodes/plan-node-type.d.ts +71 -0
- package/dist/src/planner/nodes/plan-node-type.d.ts.map +1 -0
- package/dist/src/planner/nodes/plan-node-type.js +81 -0
- package/dist/src/planner/nodes/plan-node-type.js.map +1 -0
- package/dist/src/planner/nodes/plan-node.d.ts +297 -0
- package/dist/src/planner/nodes/plan-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/plan-node.js +198 -0
- package/dist/src/planner/nodes/plan-node.js.map +1 -0
- package/dist/src/planner/nodes/pragma.d.ts +21 -0
- package/dist/src/planner/nodes/pragma.d.ts.map +1 -0
- package/dist/src/planner/nodes/pragma.js +80 -0
- package/dist/src/planner/nodes/pragma.js.map +1 -0
- package/dist/src/planner/nodes/project-node.d.ts +34 -0
- package/dist/src/planner/nodes/project-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/project-node.js +181 -0
- package/dist/src/planner/nodes/project-node.js.map +1 -0
- package/dist/src/planner/nodes/recursive-cte-node.d.ts +41 -0
- package/dist/src/planner/nodes/recursive-cte-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/recursive-cte-node.js +114 -0
- package/dist/src/planner/nodes/recursive-cte-node.js.map +1 -0
- package/dist/src/planner/nodes/reference.d.ts +92 -0
- package/dist/src/planner/nodes/reference.d.ts.map +1 -0
- package/dist/src/planner/nodes/reference.js +258 -0
- package/dist/src/planner/nodes/reference.js.map +1 -0
- package/dist/src/planner/nodes/returning-node.d.ts +37 -0
- package/dist/src/planner/nodes/returning-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/returning-node.js +183 -0
- package/dist/src/planner/nodes/returning-node.js.map +1 -0
- package/dist/src/planner/nodes/scalar.d.ts +106 -0
- package/dist/src/planner/nodes/scalar.d.ts.map +1 -0
- package/dist/src/planner/nodes/scalar.js +618 -0
- package/dist/src/planner/nodes/scalar.js.map +1 -0
- package/dist/src/planner/nodes/scan.d.ts +27 -0
- package/dist/src/planner/nodes/scan.d.ts.map +1 -0
- package/dist/src/planner/nodes/scan.js +78 -0
- package/dist/src/planner/nodes/scan.js.map +1 -0
- package/dist/src/planner/nodes/sequencing-node.d.ts +25 -0
- package/dist/src/planner/nodes/sequencing-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/sequencing-node.js +88 -0
- package/dist/src/planner/nodes/sequencing-node.js.map +1 -0
- package/dist/src/planner/nodes/set-operation-node.d.ts +21 -0
- package/dist/src/planner/nodes/set-operation-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/set-operation-node.js +68 -0
- package/dist/src/planner/nodes/set-operation-node.js.map +1 -0
- package/dist/src/planner/nodes/single-row.d.ts +24 -0
- package/dist/src/planner/nodes/single-row.d.ts.map +1 -0
- package/dist/src/planner/nodes/single-row.js +65 -0
- package/dist/src/planner/nodes/single-row.js.map +1 -0
- package/dist/src/planner/nodes/sink-node.d.ts +25 -0
- package/dist/src/planner/nodes/sink-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/sink-node.js +52 -0
- package/dist/src/planner/nodes/sink-node.js.map +1 -0
- package/dist/src/planner/nodes/sort.d.ts +37 -0
- package/dist/src/planner/nodes/sort.d.ts.map +1 -0
- package/dist/src/planner/nodes/sort.js +97 -0
- package/dist/src/planner/nodes/sort.js.map +1 -0
- package/dist/src/planner/nodes/stream-aggregate.d.ts +35 -0
- package/dist/src/planner/nodes/stream-aggregate.d.ts.map +1 -0
- package/dist/src/planner/nodes/stream-aggregate.js +186 -0
- package/dist/src/planner/nodes/stream-aggregate.js.map +1 -0
- package/dist/src/planner/nodes/subquery.d.ts +54 -0
- package/dist/src/planner/nodes/subquery.d.ts.map +1 -0
- package/dist/src/planner/nodes/subquery.js +181 -0
- package/dist/src/planner/nodes/subquery.js.map +1 -0
- package/dist/src/planner/nodes/table-function-call.d.ts +27 -0
- package/dist/src/planner/nodes/table-function-call.d.ts.map +1 -0
- package/dist/src/planner/nodes/table-function-call.js +101 -0
- package/dist/src/planner/nodes/table-function-call.js.map +1 -0
- package/dist/src/planner/nodes/transaction-node.d.ts +22 -0
- package/dist/src/planner/nodes/transaction-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/transaction-node.js +47 -0
- package/dist/src/planner/nodes/transaction-node.js.map +1 -0
- package/dist/src/planner/nodes/update-executor-node.d.ts +24 -0
- package/dist/src/planner/nodes/update-executor-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/update-executor-node.js +57 -0
- package/dist/src/planner/nodes/update-executor-node.js.map +1 -0
- package/dist/src/planner/nodes/update-node.d.ts +38 -0
- package/dist/src/planner/nodes/update-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/update-node.js +88 -0
- package/dist/src/planner/nodes/update-node.js.map +1 -0
- package/dist/src/planner/nodes/values-node.d.ts +49 -0
- package/dist/src/planner/nodes/values-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/values-node.js +204 -0
- package/dist/src/planner/nodes/values-node.js.map +1 -0
- package/dist/src/planner/nodes/view-reference-node.d.ts +27 -0
- package/dist/src/planner/nodes/view-reference-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/view-reference-node.js +83 -0
- package/dist/src/planner/nodes/view-reference-node.js.map +1 -0
- package/dist/src/planner/nodes/window-function.d.ts +25 -0
- package/dist/src/planner/nodes/window-function.d.ts.map +1 -0
- package/dist/src/planner/nodes/window-function.js +62 -0
- package/dist/src/planner/nodes/window-function.js.map +1 -0
- package/dist/src/planner/nodes/window-node.d.ts +40 -0
- package/dist/src/planner/nodes/window-node.d.ts.map +1 -0
- package/dist/src/planner/nodes/window-node.js +156 -0
- package/dist/src/planner/nodes/window-node.js.map +1 -0
- package/dist/src/planner/optimizer-tuning.d.ts +55 -0
- package/dist/src/planner/optimizer-tuning.d.ts.map +1 -0
- package/dist/src/planner/optimizer-tuning.js +31 -0
- package/dist/src/planner/optimizer-tuning.js.map +1 -0
- package/dist/src/planner/optimizer.d.ts +34 -0
- package/dist/src/planner/optimizer.d.ts.map +1 -0
- package/dist/src/planner/optimizer.js +194 -0
- package/dist/src/planner/optimizer.js.map +1 -0
- package/dist/src/planner/physical-utils.d.ts +36 -0
- package/dist/src/planner/physical-utils.d.ts.map +1 -0
- package/dist/src/planner/physical-utils.js +122 -0
- package/dist/src/planner/physical-utils.js.map +1 -0
- package/dist/src/planner/planning-context.d.ts +111 -0
- package/dist/src/planner/planning-context.d.ts.map +1 -0
- package/dist/src/planner/planning-context.js +75 -0
- package/dist/src/planner/planning-context.js.map +1 -0
- package/dist/src/planner/resolve.d.ts +8 -0
- package/dist/src/planner/resolve.d.ts.map +1 -0
- package/dist/src/planner/resolve.js +91 -0
- package/dist/src/planner/resolve.js.map +1 -0
- package/dist/src/planner/rules/access/rule-select-access-path.d.ts +11 -0
- package/dist/src/planner/rules/access/rule-select-access-path.d.ts.map +1 -0
- package/dist/src/planner/rules/access/rule-select-access-path.js +133 -0
- package/dist/src/planner/rules/access/rule-select-access-path.js.map +1 -0
- package/dist/src/planner/rules/aggregate/rule-aggregate-streaming.d.ts +11 -0
- package/dist/src/planner/rules/aggregate/rule-aggregate-streaming.d.ts.map +1 -0
- package/dist/src/planner/rules/aggregate/rule-aggregate-streaming.js +53 -0
- package/dist/src/planner/rules/aggregate/rule-aggregate-streaming.js.map +1 -0
- package/dist/src/planner/rules/cache/rule-cte-optimization.d.ts +11 -0
- package/dist/src/planner/rules/cache/rule-cte-optimization.d.ts.map +1 -0
- package/dist/src/planner/rules/cache/rule-cte-optimization.js +38 -0
- package/dist/src/planner/rules/cache/rule-cte-optimization.js.map +1 -0
- package/dist/src/planner/rules/cache/rule-materialization-advisory.d.ts +11 -0
- package/dist/src/planner/rules/cache/rule-materialization-advisory.d.ts.map +1 -0
- package/dist/src/planner/rules/cache/rule-materialization-advisory.js +51 -0
- package/dist/src/planner/rules/cache/rule-materialization-advisory.js.map +1 -0
- package/dist/src/planner/rules/cache/rule-mutating-subquery-cache.d.ts +11 -0
- package/dist/src/planner/rules/cache/rule-mutating-subquery-cache.d.ts.map +1 -0
- package/dist/src/planner/rules/cache/rule-mutating-subquery-cache.js +79 -0
- package/dist/src/planner/rules/cache/rule-mutating-subquery-cache.js.map +1 -0
- package/dist/src/planner/rules/physical/rule-filter-optimization.d.ts +11 -0
- package/dist/src/planner/rules/physical/rule-filter-optimization.d.ts.map +1 -0
- package/dist/src/planner/rules/physical/rule-filter-optimization.js +49 -0
- package/dist/src/planner/rules/physical/rule-filter-optimization.js.map +1 -0
- package/dist/src/planner/rules/physical/rule-mark-physical.d.ts +11 -0
- package/dist/src/planner/rules/physical/rule-mark-physical.d.ts.map +1 -0
- package/dist/src/planner/rules/physical/rule-mark-physical.js +29 -0
- package/dist/src/planner/rules/physical/rule-mark-physical.js.map +1 -0
- package/dist/src/planner/rules/physical/rule-project-optimization.d.ts +11 -0
- package/dist/src/planner/rules/physical/rule-project-optimization.d.ts.map +1 -0
- package/dist/src/planner/rules/physical/rule-project-optimization.js +44 -0
- package/dist/src/planner/rules/physical/rule-project-optimization.js.map +1 -0
- package/dist/src/planner/rules/physical/rule-sort-optimization.d.ts +11 -0
- package/dist/src/planner/rules/physical/rule-sort-optimization.d.ts.map +1 -0
- package/dist/src/planner/rules/physical/rule-sort-optimization.js +53 -0
- package/dist/src/planner/rules/physical/rule-sort-optimization.js.map +1 -0
- package/dist/src/planner/rules/rewrite/rule-constant-folding.d.ts +11 -0
- package/dist/src/planner/rules/rewrite/rule-constant-folding.d.ts.map +1 -0
- package/dist/src/planner/rules/rewrite/rule-constant-folding.js +59 -0
- package/dist/src/planner/rules/rewrite/rule-constant-folding.js.map +1 -0
- package/dist/src/planner/scopes/aliased.d.ts +18 -0
- package/dist/src/planner/scopes/aliased.d.ts.map +1 -0
- package/dist/src/planner/scopes/aliased.js +41 -0
- package/dist/src/planner/scopes/aliased.js.map +1 -0
- package/dist/src/planner/scopes/base.d.ts +20 -0
- package/dist/src/planner/scopes/base.d.ts.map +1 -0
- package/dist/src/planner/scopes/base.js +20 -0
- package/dist/src/planner/scopes/base.js.map +1 -0
- package/dist/src/planner/scopes/empty.d.ts +11 -0
- package/dist/src/planner/scopes/empty.d.ts.map +1 -0
- package/dist/src/planner/scopes/empty.js +16 -0
- package/dist/src/planner/scopes/empty.js.map +1 -0
- package/dist/src/planner/scopes/global.d.ts +12 -0
- package/dist/src/planner/scopes/global.d.ts.map +1 -0
- package/dist/src/planner/scopes/global.js +65 -0
- package/dist/src/planner/scopes/global.js.map +1 -0
- package/dist/src/planner/scopes/multi.d.ts +17 -0
- package/dist/src/planner/scopes/multi.d.ts.map +1 -0
- package/dist/src/planner/scopes/multi.js +35 -0
- package/dist/src/planner/scopes/multi.js.map +1 -0
- package/dist/src/planner/scopes/param.d.ts +23 -0
- package/dist/src/planner/scopes/param.d.ts.map +1 -0
- package/dist/src/planner/scopes/param.js +78 -0
- package/dist/src/planner/scopes/param.js.map +1 -0
- package/dist/src/planner/scopes/registered.d.ts +47 -0
- package/dist/src/planner/scopes/registered.d.ts.map +1 -0
- package/dist/src/planner/scopes/registered.js +70 -0
- package/dist/src/planner/scopes/registered.js.map +1 -0
- package/dist/src/planner/scopes/scope.d.ts +21 -0
- package/dist/src/planner/scopes/scope.d.ts.map +1 -0
- package/dist/src/planner/scopes/scope.js +3 -0
- package/dist/src/planner/scopes/scope.js.map +1 -0
- package/dist/src/planner/stats/basic-estimates.d.ts +47 -0
- package/dist/src/planner/stats/basic-estimates.d.ts.map +1 -0
- package/dist/src/planner/stats/basic-estimates.js +99 -0
- package/dist/src/planner/stats/basic-estimates.js.map +1 -0
- package/dist/src/planner/stats/index.d.ts +88 -0
- package/dist/src/planner/stats/index.d.ts.map +1 -0
- package/dist/src/planner/stats/index.js +152 -0
- package/dist/src/planner/stats/index.js.map +1 -0
- package/dist/src/planner/type-utils.d.ts +30 -0
- package/dist/src/planner/type-utils.d.ts.map +1 -0
- package/dist/src/planner/type-utils.js +91 -0
- package/dist/src/planner/type-utils.js.map +1 -0
- package/dist/src/planner/validation/plan-validator.d.ts +29 -0
- package/dist/src/planner/validation/plan-validator.d.ts.map +1 -0
- package/dist/src/planner/validation/plan-validator.js +238 -0
- package/dist/src/planner/validation/plan-validator.js.map +1 -0
- package/dist/src/runtime/async-util.d.ts +53 -0
- package/dist/src/runtime/async-util.d.ts.map +1 -0
- package/dist/src/runtime/async-util.js +238 -0
- package/dist/src/runtime/async-util.js.map +1 -0
- package/dist/src/runtime/cache/shared-cache.d.ts +68 -0
- package/dist/src/runtime/cache/shared-cache.d.ts.map +1 -0
- package/dist/src/runtime/cache/shared-cache.js +107 -0
- package/dist/src/runtime/cache/shared-cache.js.map +1 -0
- package/dist/src/runtime/emission-context.d.ts +121 -0
- package/dist/src/runtime/emission-context.d.ts.map +1 -0
- package/dist/src/runtime/emission-context.js +258 -0
- package/dist/src/runtime/emission-context.js.map +1 -0
- package/dist/src/runtime/emit/add-constraint.d.ts +5 -0
- package/dist/src/runtime/emit/add-constraint.d.ts.map +1 -0
- package/dist/src/runtime/emit/add-constraint.js +35 -0
- package/dist/src/runtime/emit/add-constraint.js.map +1 -0
- package/dist/src/runtime/emit/aggregate.d.ts +6 -0
- package/dist/src/runtime/emit/aggregate.d.ts.map +1 -0
- package/dist/src/runtime/emit/aggregate.js +465 -0
- package/dist/src/runtime/emit/aggregate.js.map +1 -0
- package/dist/src/runtime/emit/array-index.d.ts +5 -0
- package/dist/src/runtime/emit/array-index.d.ts.map +1 -0
- package/dist/src/runtime/emit/array-index.js +20 -0
- package/dist/src/runtime/emit/array-index.js.map +1 -0
- package/dist/src/runtime/emit/binary.d.ts +11 -0
- package/dist/src/runtime/emit/binary.d.ts.map +1 -0
- package/dist/src/runtime/emit/binary.js +310 -0
- package/dist/src/runtime/emit/binary.js.map +1 -0
- package/dist/src/runtime/emit/block.d.ts +5 -0
- package/dist/src/runtime/emit/block.d.ts.map +1 -0
- package/dist/src/runtime/emit/block.js +16 -0
- package/dist/src/runtime/emit/block.js.map +1 -0
- package/dist/src/runtime/emit/cache.d.ts +25 -0
- package/dist/src/runtime/emit/cache.d.ts.map +1 -0
- package/dist/src/runtime/emit/cache.js +52 -0
- package/dist/src/runtime/emit/cache.js.map +1 -0
- package/dist/src/runtime/emit/case.d.ts +5 -0
- package/dist/src/runtime/emit/case.d.ts.map +1 -0
- package/dist/src/runtime/emit/case.js +65 -0
- package/dist/src/runtime/emit/case.js.map +1 -0
- package/dist/src/runtime/emit/cast.d.ts +5 -0
- package/dist/src/runtime/emit/cast.d.ts.map +1 -0
- package/dist/src/runtime/emit/cast.js +132 -0
- package/dist/src/runtime/emit/cast.js.map +1 -0
- package/dist/src/runtime/emit/collate.d.ts +5 -0
- package/dist/src/runtime/emit/collate.d.ts.map +1 -0
- package/dist/src/runtime/emit/collate.js +6 -0
- package/dist/src/runtime/emit/collate.js.map +1 -0
- package/dist/src/runtime/emit/column-reference.d.ts +5 -0
- package/dist/src/runtime/emit/column-reference.d.ts.map +1 -0
- package/dist/src/runtime/emit/column-reference.js +36 -0
- package/dist/src/runtime/emit/column-reference.js.map +1 -0
- package/dist/src/runtime/emit/constraint-check.d.ts +5 -0
- package/dist/src/runtime/emit/constraint-check.d.ts.map +1 -0
- package/dist/src/runtime/emit/constraint-check.js +211 -0
- package/dist/src/runtime/emit/constraint-check.js.map +1 -0
- package/dist/src/runtime/emit/create-index.d.ts +5 -0
- package/dist/src/runtime/emit/create-index.d.ts.map +1 -0
- package/dist/src/runtime/emit/create-index.js +9 -0
- package/dist/src/runtime/emit/create-index.js.map +1 -0
- package/dist/src/runtime/emit/create-table.d.ts +5 -0
- package/dist/src/runtime/emit/create-table.d.ts.map +1 -0
- package/dist/src/runtime/emit/create-table.js +9 -0
- package/dist/src/runtime/emit/create-table.js.map +1 -0
- package/dist/src/runtime/emit/create-view.d.ts +5 -0
- package/dist/src/runtime/emit/create-view.d.ts.map +1 -0
- package/dist/src/runtime/emit/create-view.js +36 -0
- package/dist/src/runtime/emit/create-view.js.map +1 -0
- package/dist/src/runtime/emit/cte-reference.d.ts +5 -0
- package/dist/src/runtime/emit/cte-reference.d.ts.map +1 -0
- package/dist/src/runtime/emit/cte-reference.js +56 -0
- package/dist/src/runtime/emit/cte-reference.js.map +1 -0
- package/dist/src/runtime/emit/cte.d.ts +5 -0
- package/dist/src/runtime/emit/cte.d.ts.map +1 -0
- package/dist/src/runtime/emit/cte.js +34 -0
- package/dist/src/runtime/emit/cte.js.map +1 -0
- package/dist/src/runtime/emit/delete.d.ts +5 -0
- package/dist/src/runtime/emit/delete.d.ts.map +1 -0
- package/dist/src/runtime/emit/delete.js +18 -0
- package/dist/src/runtime/emit/delete.js.map +1 -0
- package/dist/src/runtime/emit/distinct.d.ts +5 -0
- package/dist/src/runtime/emit/distinct.d.ts.map +1 -0
- package/dist/src/runtime/emit/distinct.js +35 -0
- package/dist/src/runtime/emit/distinct.js.map +1 -0
- package/dist/src/runtime/emit/dml-executor.d.ts +5 -0
- package/dist/src/runtime/emit/dml-executor.d.ts.map +1 -0
- package/dist/src/runtime/emit/dml-executor.js +90 -0
- package/dist/src/runtime/emit/dml-executor.js.map +1 -0
- package/dist/src/runtime/emit/drop-table.d.ts +5 -0
- package/dist/src/runtime/emit/drop-table.d.ts.map +1 -0
- package/dist/src/runtime/emit/drop-table.js +19 -0
- package/dist/src/runtime/emit/drop-table.js.map +1 -0
- package/dist/src/runtime/emit/drop-view.d.ts +5 -0
- package/dist/src/runtime/emit/drop-view.d.ts.map +1 -0
- package/dist/src/runtime/emit/drop-view.js +31 -0
- package/dist/src/runtime/emit/drop-view.js.map +1 -0
- package/dist/src/runtime/emit/filter.d.ts +5 -0
- package/dist/src/runtime/emit/filter.d.ts.map +1 -0
- package/dist/src/runtime/emit/filter.js +30 -0
- package/dist/src/runtime/emit/filter.js.map +1 -0
- package/dist/src/runtime/emit/insert.d.ts +5 -0
- package/dist/src/runtime/emit/insert.d.ts.map +1 -0
- package/dist/src/runtime/emit/insert.js +52 -0
- package/dist/src/runtime/emit/insert.js.map +1 -0
- package/dist/src/runtime/emit/join.d.ts +9 -0
- package/dist/src/runtime/emit/join.d.ts.map +1 -0
- package/dist/src/runtime/emit/join.js +116 -0
- package/dist/src/runtime/emit/join.js.map +1 -0
- package/dist/src/runtime/emit/limit-offset.d.ts +5 -0
- package/dist/src/runtime/emit/limit-offset.d.ts.map +1 -0
- package/dist/src/runtime/emit/limit-offset.js +56 -0
- package/dist/src/runtime/emit/limit-offset.js.map +1 -0
- package/dist/src/runtime/emit/literal.d.ts +5 -0
- package/dist/src/runtime/emit/literal.d.ts.map +1 -0
- package/dist/src/runtime/emit/literal.js +12 -0
- package/dist/src/runtime/emit/literal.js.map +1 -0
- package/dist/src/runtime/emit/parameter.d.ts +5 -0
- package/dist/src/runtime/emit/parameter.d.ts.map +1 -0
- package/dist/src/runtime/emit/parameter.js +57 -0
- package/dist/src/runtime/emit/parameter.js.map +1 -0
- package/dist/src/runtime/emit/pragma.d.ts +5 -0
- package/dist/src/runtime/emit/pragma.d.ts.map +1 -0
- package/dist/src/runtime/emit/pragma.js +44 -0
- package/dist/src/runtime/emit/pragma.js.map +1 -0
- package/dist/src/runtime/emit/project.d.ts +5 -0
- package/dist/src/runtime/emit/project.d.ts.map +1 -0
- package/dist/src/runtime/emit/project.js +32 -0
- package/dist/src/runtime/emit/project.js.map +1 -0
- package/dist/src/runtime/emit/recursive-cte.d.ts +5 -0
- package/dist/src/runtime/emit/recursive-cte.d.ts.map +1 -0
- package/dist/src/runtime/emit/recursive-cte.js +92 -0
- package/dist/src/runtime/emit/recursive-cte.js.map +1 -0
- package/dist/src/runtime/emit/returning.d.ts +5 -0
- package/dist/src/runtime/emit/returning.d.ts.map +1 -0
- package/dist/src/runtime/emit/returning.js +34 -0
- package/dist/src/runtime/emit/returning.js.map +1 -0
- package/dist/src/runtime/emit/scalar-function.d.ts +5 -0
- package/dist/src/runtime/emit/scalar-function.d.ts.map +1 -0
- package/dist/src/runtime/emit/scalar-function.js +29 -0
- package/dist/src/runtime/emit/scalar-function.js.map +1 -0
- package/dist/src/runtime/emit/scan.d.ts +5 -0
- package/dist/src/runtime/emit/scan.d.ts.map +1 -0
- package/dist/src/runtime/emit/scan.js +63 -0
- package/dist/src/runtime/emit/scan.js.map +1 -0
- package/dist/src/runtime/emit/sequencing.d.ts +5 -0
- package/dist/src/runtime/emit/sequencing.d.ts.map +1 -0
- package/dist/src/runtime/emit/sequencing.js +17 -0
- package/dist/src/runtime/emit/sequencing.js.map +1 -0
- package/dist/src/runtime/emit/set-operation.d.ts +5 -0
- package/dist/src/runtime/emit/set-operation.d.ts.map +1 -0
- package/dist/src/runtime/emit/set-operation.js +99 -0
- package/dist/src/runtime/emit/set-operation.js.map +1 -0
- package/dist/src/runtime/emit/sink.d.ts +5 -0
- package/dist/src/runtime/emit/sink.d.ts.map +1 -0
- package/dist/src/runtime/emit/sink.js +19 -0
- package/dist/src/runtime/emit/sink.js.map +1 -0
- package/dist/src/runtime/emit/sort.d.ts +5 -0
- package/dist/src/runtime/emit/sort.d.ts.map +1 -0
- package/dist/src/runtime/emit/sort.js +59 -0
- package/dist/src/runtime/emit/sort.js.map +1 -0
- package/dist/src/runtime/emit/subquery.d.ts +7 -0
- package/dist/src/runtime/emit/subquery.d.ts.map +1 -0
- package/dist/src/runtime/emit/subquery.js +114 -0
- package/dist/src/runtime/emit/subquery.js.map +1 -0
- package/dist/src/runtime/emit/table-reference.d.ts +5 -0
- package/dist/src/runtime/emit/table-reference.d.ts.map +1 -0
- package/dist/src/runtime/emit/table-reference.js +67 -0
- package/dist/src/runtime/emit/table-reference.js.map +1 -0
- package/dist/src/runtime/emit/table-valued-function.d.ts +5 -0
- package/dist/src/runtime/emit/table-valued-function.d.ts.map +1 -0
- package/dist/src/runtime/emit/table-valued-function.js +98 -0
- package/dist/src/runtime/emit/table-valued-function.js.map +1 -0
- package/dist/src/runtime/emit/transaction.d.ts +5 -0
- package/dist/src/runtime/emit/transaction.d.ts.map +1 -0
- package/dist/src/runtime/emit/transaction.js +153 -0
- package/dist/src/runtime/emit/transaction.js.map +1 -0
- package/dist/src/runtime/emit/unary.d.ts +5 -0
- package/dist/src/runtime/emit/unary.d.ts.map +1 -0
- package/dist/src/runtime/emit/unary.js +81 -0
- package/dist/src/runtime/emit/unary.js.map +1 -0
- package/dist/src/runtime/emit/update-executor.d.ts +5 -0
- package/dist/src/runtime/emit/update-executor.d.ts.map +1 -0
- package/dist/src/runtime/emit/update-executor.js +54 -0
- package/dist/src/runtime/emit/update-executor.js.map +1 -0
- package/dist/src/runtime/emit/update.d.ts +5 -0
- package/dist/src/runtime/emit/update.d.ts.map +1 -0
- package/dist/src/runtime/emit/update.js +58 -0
- package/dist/src/runtime/emit/update.js.map +1 -0
- package/dist/src/runtime/emit/values.d.ts +9 -0
- package/dist/src/runtime/emit/values.d.ts.map +1 -0
- package/dist/src/runtime/emit/values.js +51 -0
- package/dist/src/runtime/emit/values.js.map +1 -0
- package/dist/src/runtime/emit/window-function.d.ts +5 -0
- package/dist/src/runtime/emit/window-function.d.ts.map +1 -0
- package/dist/src/runtime/emit/window-function.js +31 -0
- package/dist/src/runtime/emit/window-function.js.map +1 -0
- package/dist/src/runtime/emit/window.d.ts +5 -0
- package/dist/src/runtime/emit/window.d.ts.map +1 -0
- package/dist/src/runtime/emit/window.js +328 -0
- package/dist/src/runtime/emit/window.js.map +1 -0
- package/dist/src/runtime/emitters.d.ts +48 -0
- package/dist/src/runtime/emitters.d.ts.map +1 -0
- package/dist/src/runtime/emitters.js +73 -0
- package/dist/src/runtime/emitters.js.map +1 -0
- package/dist/src/runtime/register.d.ts +2 -0
- package/dist/src/runtime/register.d.ts.map +1 -0
- package/dist/src/runtime/register.js +122 -0
- package/dist/src/runtime/register.js.map +1 -0
- package/dist/src/runtime/scheduler.d.ts +27 -0
- package/dist/src/runtime/scheduler.d.ts.map +1 -0
- package/dist/src/runtime/scheduler.js +385 -0
- package/dist/src/runtime/scheduler.js.map +1 -0
- package/dist/src/runtime/types.d.ts +109 -0
- package/dist/src/runtime/types.d.ts.map +1 -0
- package/dist/src/runtime/types.js +90 -0
- package/dist/src/runtime/types.js.map +1 -0
- package/dist/src/runtime/utils.d.ts +32 -0
- package/dist/src/runtime/utils.d.ts.map +1 -0
- package/dist/src/runtime/utils.js +117 -0
- package/dist/src/runtime/utils.js.map +1 -0
- package/dist/src/schema/change-events.d.ts +42 -0
- package/dist/src/schema/change-events.d.ts.map +1 -0
- package/dist/src/schema/change-events.js +55 -0
- package/dist/src/schema/change-events.js.map +1 -0
- package/dist/src/schema/column.d.ts +33 -0
- package/dist/src/schema/column.d.ts.map +1 -0
- package/dist/src/schema/column.js +22 -0
- package/dist/src/schema/column.js.map +1 -0
- package/dist/src/schema/function.d.ts +109 -0
- package/dist/src/schema/function.d.ts.map +1 -0
- package/dist/src/schema/function.js +26 -0
- package/dist/src/schema/function.js.map +1 -0
- package/dist/src/schema/manager.d.ts +222 -0
- package/dist/src/schema/manager.d.ts.map +1 -0
- package/dist/src/schema/manager.js +608 -0
- package/dist/src/schema/manager.js.map +1 -0
- package/dist/src/schema/schema.d.ts +110 -0
- package/dist/src/schema/schema.d.ts.map +1 -0
- package/dist/src/schema/schema.js +179 -0
- package/dist/src/schema/schema.js.map +1 -0
- package/dist/src/schema/table.d.ts +143 -0
- package/dist/src/schema/table.d.ts.map +1 -0
- package/dist/src/schema/table.js +245 -0
- package/dist/src/schema/table.js.map +1 -0
- package/dist/src/schema/view.d.ts +18 -0
- package/dist/src/schema/view.d.ts.map +1 -0
- package/dist/src/schema/view.js +2 -0
- package/dist/src/schema/view.js.map +1 -0
- package/dist/src/schema/window-function.d.ts +26 -0
- package/dist/src/schema/window-function.d.ts.map +1 -0
- package/dist/src/schema/window-function.js +34 -0
- package/dist/src/schema/window-function.js.map +1 -0
- package/dist/src/util/affinity.d.ts +34 -0
- package/dist/src/util/affinity.d.ts.map +1 -0
- package/dist/src/util/affinity.js +153 -0
- package/dist/src/util/affinity.js.map +1 -0
- package/dist/src/util/ast-stringify.d.ts +23 -0
- package/dist/src/util/ast-stringify.d.ts.map +1 -0
- package/dist/src/util/ast-stringify.js +683 -0
- package/dist/src/util/ast-stringify.js.map +1 -0
- package/dist/src/util/cached.d.ts +11 -0
- package/dist/src/util/cached.d.ts.map +1 -0
- package/dist/src/util/cached.js +24 -0
- package/dist/src/util/cached.js.map +1 -0
- package/dist/src/util/coercion.d.ts +34 -0
- package/dist/src/util/coercion.d.ts.map +1 -0
- package/dist/src/util/coercion.js +106 -0
- package/dist/src/util/coercion.js.map +1 -0
- package/dist/src/util/comparison.d.ts +153 -0
- package/dist/src/util/comparison.d.ts.map +1 -0
- package/dist/src/util/comparison.js +397 -0
- package/dist/src/util/comparison.js.map +1 -0
- package/dist/src/util/environment.d.ts +28 -0
- package/dist/src/util/environment.d.ts.map +1 -0
- package/dist/src/util/environment.js +47 -0
- package/dist/src/util/environment.js.map +1 -0
- package/dist/src/util/latches.d.ts +16 -0
- package/dist/src/util/latches.d.ts.map +1 -0
- package/dist/src/util/latches.js +41 -0
- package/dist/src/util/latches.js.map +1 -0
- package/dist/src/util/patterns.d.ts +23 -0
- package/dist/src/util/patterns.d.ts.map +1 -0
- package/dist/src/util/patterns.js +54 -0
- package/dist/src/util/patterns.js.map +1 -0
- package/dist/src/util/plan-formatter.d.ts +23 -0
- package/dist/src/util/plan-formatter.d.ts.map +1 -0
- package/dist/src/util/plan-formatter.js +41 -0
- package/dist/src/util/plan-formatter.js.map +1 -0
- package/dist/src/util/plugin-loader.d.ts +20 -0
- package/dist/src/util/plugin-loader.d.ts.map +1 -0
- package/dist/src/util/plugin-loader.js +57 -0
- package/dist/src/util/plugin-loader.js.map +1 -0
- package/dist/src/util/row-descriptor.d.ts +35 -0
- package/dist/src/util/row-descriptor.d.ts.map +1 -0
- package/dist/src/util/row-descriptor.js +85 -0
- package/dist/src/util/row-descriptor.js.map +1 -0
- package/dist/src/util/serialization.d.ts +11 -0
- package/dist/src/util/serialization.d.ts.map +1 -0
- package/dist/src/util/serialization.js +41 -0
- package/dist/src/util/serialization.js.map +1 -0
- package/dist/src/util/working-table-iterable.d.ts +17 -0
- package/dist/src/util/working-table-iterable.d.ts.map +1 -0
- package/dist/src/util/working-table-iterable.js +30 -0
- package/dist/src/util/working-table-iterable.js.map +1 -0
- package/dist/src/vtab/best-access-plan.d.ts +144 -0
- package/dist/src/vtab/best-access-plan.d.ts.map +1 -0
- package/dist/src/vtab/best-access-plan.js +156 -0
- package/dist/src/vtab/best-access-plan.js.map +1 -0
- package/dist/src/vtab/connection.d.ts +27 -0
- package/dist/src/vtab/connection.d.ts.map +1 -0
- package/dist/src/vtab/connection.js +2 -0
- package/dist/src/vtab/connection.js.map +1 -0
- package/dist/src/vtab/filter-info.d.ts +26 -0
- package/dist/src/vtab/filter-info.d.ts.map +1 -0
- package/dist/src/vtab/filter-info.js +2 -0
- package/dist/src/vtab/filter-info.js.map +1 -0
- package/dist/src/vtab/index-info.d.ts +69 -0
- package/dist/src/vtab/index-info.d.ts.map +1 -0
- package/dist/src/vtab/index-info.js +7 -0
- package/dist/src/vtab/index-info.js.map +1 -0
- package/dist/src/vtab/manifest.d.ts +35 -0
- package/dist/src/vtab/manifest.d.ts.map +1 -0
- package/dist/src/vtab/manifest.js +2 -0
- package/dist/src/vtab/manifest.js.map +1 -0
- package/dist/src/vtab/memory/connection.d.ts +29 -0
- package/dist/src/vtab/memory/connection.d.ts.map +1 -0
- package/dist/src/vtab/memory/connection.js +61 -0
- package/dist/src/vtab/memory/connection.js.map +1 -0
- package/dist/src/vtab/memory/index.d.ts +38 -0
- package/dist/src/vtab/memory/index.d.ts.map +1 -0
- package/dist/src/vtab/memory/index.js +132 -0
- package/dist/src/vtab/memory/index.js.map +1 -0
- package/dist/src/vtab/memory/layer/base-cursor.d.ts +5 -0
- package/dist/src/vtab/memory/layer/base-cursor.d.ts.map +1 -0
- package/dist/src/vtab/memory/layer/base-cursor.js +111 -0
- package/dist/src/vtab/memory/layer/base-cursor.js.map +1 -0
- package/dist/src/vtab/memory/layer/base.d.ts +51 -0
- package/dist/src/vtab/memory/layer/base.d.ts.map +1 -0
- package/dist/src/vtab/memory/layer/base.js +199 -0
- package/dist/src/vtab/memory/layer/base.js.map +1 -0
- package/dist/src/vtab/memory/layer/connection.d.ts +38 -0
- package/dist/src/vtab/memory/layer/connection.d.ts.map +1 -0
- package/dist/src/vtab/memory/layer/connection.js +170 -0
- package/dist/src/vtab/memory/layer/connection.js.map +1 -0
- package/dist/src/vtab/memory/layer/interface.d.ts +41 -0
- package/dist/src/vtab/memory/layer/interface.d.ts.map +1 -0
- package/dist/src/vtab/memory/layer/interface.js +2 -0
- package/dist/src/vtab/memory/layer/interface.js.map +1 -0
- package/dist/src/vtab/memory/layer/manager.d.ts +68 -0
- package/dist/src/vtab/memory/layer/manager.d.ts.map +1 -0
- package/dist/src/vtab/memory/layer/manager.js +752 -0
- package/dist/src/vtab/memory/layer/manager.js.map +1 -0
- package/dist/src/vtab/memory/layer/safe-iterate.d.ts +9 -0
- package/dist/src/vtab/memory/layer/safe-iterate.d.ts.map +1 -0
- package/dist/src/vtab/memory/layer/safe-iterate.js +47 -0
- package/dist/src/vtab/memory/layer/safe-iterate.js.map +1 -0
- package/dist/src/vtab/memory/layer/scan-plan.d.ts +37 -0
- package/dist/src/vtab/memory/layer/scan-plan.d.ts.map +1 -0
- package/dist/src/vtab/memory/layer/scan-plan.js +87 -0
- package/dist/src/vtab/memory/layer/scan-plan.js.map +1 -0
- package/dist/src/vtab/memory/layer/transaction-cursor.d.ts +5 -0
- package/dist/src/vtab/memory/layer/transaction-cursor.d.ts.map +1 -0
- package/dist/src/vtab/memory/layer/transaction-cursor.js +149 -0
- package/dist/src/vtab/memory/layer/transaction-cursor.js.map +1 -0
- package/dist/src/vtab/memory/layer/transaction.d.ts +44 -0
- package/dist/src/vtab/memory/layer/transaction.d.ts.map +1 -0
- package/dist/src/vtab/memory/layer/transaction.js +183 -0
- package/dist/src/vtab/memory/layer/transaction.js.map +1 -0
- package/dist/src/vtab/memory/module.d.ts +89 -0
- package/dist/src/vtab/memory/module.d.ts.map +1 -0
- package/dist/src/vtab/memory/module.js +533 -0
- package/dist/src/vtab/memory/module.js.map +1 -0
- package/dist/src/vtab/memory/table.d.ts +49 -0
- package/dist/src/vtab/memory/table.d.ts.map +1 -0
- package/dist/src/vtab/memory/table.js +209 -0
- package/dist/src/vtab/memory/table.js.map +1 -0
- package/dist/src/vtab/memory/types.d.ts +19 -0
- package/dist/src/vtab/memory/types.d.ts.map +1 -0
- package/dist/src/vtab/memory/types.js +2 -0
- package/dist/src/vtab/memory/types.js.map +1 -0
- package/dist/src/vtab/memory/utils/logging.d.ts +13 -0
- package/dist/src/vtab/memory/utils/logging.d.ts.map +1 -0
- package/dist/src/vtab/memory/utils/logging.js +28 -0
- package/dist/src/vtab/memory/utils/logging.js.map +1 -0
- package/dist/src/vtab/memory/utils/primary-key.d.ts +21 -0
- package/dist/src/vtab/memory/utils/primary-key.d.ts.map +1 -0
- package/dist/src/vtab/memory/utils/primary-key.js +107 -0
- package/dist/src/vtab/memory/utils/primary-key.js.map +1 -0
- package/dist/src/vtab/module.d.ts +111 -0
- package/dist/src/vtab/module.d.ts.map +1 -0
- package/dist/src/vtab/module.js +2 -0
- package/dist/src/vtab/module.js.map +1 -0
- package/dist/src/vtab/table.d.ts +114 -0
- package/dist/src/vtab/table.d.ts.map +1 -0
- package/dist/src/vtab/table.js +26 -0
- package/dist/src/vtab/table.js.map +1 -0
- package/package.json +61 -0
- package/src/common/constants.ts +60 -0
- package/src/common/datatype.ts +86 -0
- package/src/common/errors.ts +189 -0
- package/src/common/logger.ts +23 -0
- package/src/common/type-inference.ts +40 -0
- package/src/common/types.ts +148 -0
- package/src/core/database-options.ts +258 -0
- package/src/core/database.ts +875 -0
- package/src/core/param.ts +41 -0
- package/src/core/statement.ts +490 -0
- package/src/func/builtins/aggregate.ts +247 -0
- package/src/func/builtins/builtin-window-functions.ts +165 -0
- package/src/func/builtins/datetime.ts +453 -0
- package/src/func/builtins/explain.ts +648 -0
- package/src/func/builtins/generation.ts +43 -0
- package/src/func/builtins/index.ts +126 -0
- package/src/func/builtins/json-helpers.ts +237 -0
- package/src/func/builtins/json-tvf.ts +225 -0
- package/src/func/builtins/json.ts +466 -0
- package/src/func/builtins/scalar.ts +232 -0
- package/src/func/builtins/schema.ts +193 -0
- package/src/func/builtins/string.ts +293 -0
- package/src/func/context.ts +251 -0
- package/src/func/registration.ts +167 -0
- package/src/index.ts +67 -0
- package/src/parser/ast.ts +475 -0
- package/src/parser/index.ts +65 -0
- package/src/parser/lexer.ts +767 -0
- package/src/parser/parser.ts +2783 -0
- package/src/parser/utils.ts +10 -0
- package/src/parser/visitor.ts +187 -0
- package/src/planner/analysis/README.md +93 -0
- package/src/planner/analysis/const-evaluator.ts +63 -0
- package/src/planner/analysis/const-pass.ts +270 -0
- package/src/planner/analysis/constraint-extractor.ts +277 -0
- package/src/planner/building/alter-table.ts +47 -0
- package/src/planner/building/block.ts +78 -0
- package/src/planner/building/create-view.ts +56 -0
- package/src/planner/building/ddl.ts +24 -0
- package/src/planner/building/delete.ts +158 -0
- package/src/planner/building/drop-table.ts +13 -0
- package/src/planner/building/drop-view.ts +19 -0
- package/src/planner/building/expression.ts +176 -0
- package/src/planner/building/function-call.ts +87 -0
- package/src/planner/building/insert.ts +336 -0
- package/src/planner/building/pragma.ts +33 -0
- package/src/planner/building/schema-resolution.ts +176 -0
- package/src/planner/building/select-aggregates.ts +245 -0
- package/src/planner/building/select-compound.ts +110 -0
- package/src/planner/building/select-context.ts +75 -0
- package/src/planner/building/select-modifiers.ts +170 -0
- package/src/planner/building/select-projections.ts +177 -0
- package/src/planner/building/select-window.ts +248 -0
- package/src/planner/building/select.ts +388 -0
- package/src/planner/building/table-function.ts +48 -0
- package/src/planner/building/table.ts +73 -0
- package/src/planner/building/transaction.ts +23 -0
- package/src/planner/building/update.ts +255 -0
- package/src/planner/building/with.ts +181 -0
- package/src/planner/cache/materialization-advisory.ts +244 -0
- package/src/planner/cache/reference-graph.ts +172 -0
- package/src/planner/cost/index.ts +169 -0
- package/src/planner/debug/logger-utils.ts +68 -0
- package/src/planner/debug.ts +316 -0
- package/src/planner/framework/README.md +132 -0
- package/src/planner/framework/context.ts +188 -0
- package/src/planner/framework/physical-utils.ts +210 -0
- package/src/planner/framework/registry.ts +274 -0
- package/src/planner/framework/trace.ts +259 -0
- package/src/planner/nodes/add-constraint-node.ts +58 -0
- package/src/planner/nodes/aggregate-function.ts +145 -0
- package/src/planner/nodes/aggregate-node.ts +220 -0
- package/src/planner/nodes/array-index-node.ts +50 -0
- package/src/planner/nodes/block.ts +80 -0
- package/src/planner/nodes/cache-node.ts +94 -0
- package/src/planner/nodes/constraint-check-node.ts +95 -0
- package/src/planner/nodes/create-index-node.ts +37 -0
- package/src/planner/nodes/create-table-node.ts +31 -0
- package/src/planner/nodes/create-view-node.ts +40 -0
- package/src/planner/nodes/cte-node.ts +111 -0
- package/src/planner/nodes/cte-reference-node.ts +89 -0
- package/src/planner/nodes/delete-node.ts +72 -0
- package/src/planner/nodes/distinct-node.ts +93 -0
- package/src/planner/nodes/dml-executor-node.ts +98 -0
- package/src/planner/nodes/drop-table-node.ts +31 -0
- package/src/planner/nodes/drop-view-node.ts +33 -0
- package/src/planner/nodes/filter.ts +91 -0
- package/src/planner/nodes/function.ts +93 -0
- package/src/planner/nodes/insert-node.ts +97 -0
- package/src/planner/nodes/join-node.ts +213 -0
- package/src/planner/nodes/limit-offset.ts +125 -0
- package/src/planner/nodes/physical-access-nodes.ts +298 -0
- package/src/planner/nodes/plan-node-type.ts +87 -0
- package/src/planner/nodes/plan-node.ts +466 -0
- package/src/planner/nodes/pragma.ts +94 -0
- package/src/planner/nodes/project-node.ts +223 -0
- package/src/planner/nodes/recursive-cte-node.ts +130 -0
- package/src/planner/nodes/reference.ts +310 -0
- package/src/planner/nodes/returning-node.ts +215 -0
- package/src/planner/nodes/scalar.ts +736 -0
- package/src/planner/nodes/scan.ts +103 -0
- package/src/planner/nodes/sequencing-node.ts +113 -0
- package/src/planner/nodes/set-operation-node.ts +87 -0
- package/src/planner/nodes/single-row.ts +80 -0
- package/src/planner/nodes/sink-node.ts +61 -0
- package/src/planner/nodes/sort.ts +137 -0
- package/src/planner/nodes/stream-aggregate.ts +225 -0
- package/src/planner/nodes/subquery.ts +207 -0
- package/src/planner/nodes/table-function-call.ts +127 -0
- package/src/planner/nodes/transaction-node.ts +61 -0
- package/src/planner/nodes/update-node.ts +114 -0
- package/src/planner/nodes/values-node.ts +244 -0
- package/src/planner/nodes/view-reference-node.ts +97 -0
- package/src/planner/nodes/window-function.ts +73 -0
- package/src/planner/nodes/window-node.ts +193 -0
- package/src/planner/optimizer-tuning.ts +87 -0
- package/src/planner/optimizer.ts +263 -0
- package/src/planner/planning-context.ts +168 -0
- package/src/planner/resolve.ts +100 -0
- package/src/planner/rules/README.md +96 -0
- package/src/planner/rules/access/rule-select-access-path.ts +180 -0
- package/src/planner/rules/aggregate/rule-aggregate-streaming.ts +76 -0
- package/src/planner/rules/cache/rule-cte-optimization.ts +66 -0
- package/src/planner/rules/cache/rule-materialization-advisory.ts +66 -0
- package/src/planner/rules/cache/rule-mutating-subquery-cache.ts +110 -0
- package/src/planner/rules/physical/rule-mark-physical.ts +37 -0
- package/src/planner/scopes/aliased.ts +50 -0
- package/src/planner/scopes/base.ts +27 -0
- package/src/planner/scopes/empty.ts +22 -0
- package/src/planner/scopes/global.ts +73 -0
- package/src/planner/scopes/multi.ts +41 -0
- package/src/planner/scopes/param.ts +96 -0
- package/src/planner/scopes/registered.ts +78 -0
- package/src/planner/scopes/scope.ts +28 -0
- package/src/planner/stats/basic-estimates.ts +107 -0
- package/src/planner/stats/index.ts +223 -0
- package/src/planner/type-utils.ts +95 -0
- package/src/planner/validation/plan-validator.ts +340 -0
- package/src/runtime/async-util.ts +282 -0
- package/src/runtime/cache/shared-cache.ts +169 -0
- package/src/runtime/emission-context.ts +310 -0
- package/src/runtime/emit/add-constraint.ts +54 -0
- package/src/runtime/emit/aggregate.ts +533 -0
- package/src/runtime/emit/array-index.ts +25 -0
- package/src/runtime/emit/binary.ts +335 -0
- package/src/runtime/emit/block.ts +23 -0
- package/src/runtime/emit/cache.ts +64 -0
- package/src/runtime/emit/case.ts +87 -0
- package/src/runtime/emit/cast.ts +151 -0
- package/src/runtime/emit/collate.ts +9 -0
- package/src/runtime/emit/column-reference.ts +50 -0
- package/src/runtime/emit/constraint-check.ts +285 -0
- package/src/runtime/emit/create-index.ts +15 -0
- package/src/runtime/emit/create-table.ts +15 -0
- package/src/runtime/emit/create-view.ts +52 -0
- package/src/runtime/emit/cte-reference.ts +75 -0
- package/src/runtime/emit/cte.ts +39 -0
- package/src/runtime/emit/delete.ts +24 -0
- package/src/runtime/emit/distinct.ts +45 -0
- package/src/runtime/emit/dml-executor.ts +94 -0
- package/src/runtime/emit/drop-table.ts +27 -0
- package/src/runtime/emit/drop-view.ts +49 -0
- package/src/runtime/emit/filter.ts +37 -0
- package/src/runtime/emit/insert.ts +54 -0
- package/src/runtime/emit/join.ts +145 -0
- package/src/runtime/emit/limit-offset.ts +73 -0
- package/src/runtime/emit/literal.ts +17 -0
- package/src/runtime/emit/parameter.ts +59 -0
- package/src/runtime/emit/pragma.ts +56 -0
- package/src/runtime/emit/project.ts +40 -0
- package/src/runtime/emit/recursive-cte.ts +120 -0
- package/src/runtime/emit/returning.ts +48 -0
- package/src/runtime/emit/scalar-function.ts +43 -0
- package/src/runtime/emit/scan.ts +88 -0
- package/src/runtime/emit/sequencing.ts +24 -0
- package/src/runtime/emit/set-operation.ts +111 -0
- package/src/runtime/emit/sink.ts +27 -0
- package/src/runtime/emit/sort.ts +78 -0
- package/src/runtime/emit/subquery.ts +143 -0
- package/src/runtime/emit/table-reference.ts +92 -0
- package/src/runtime/emit/table-valued-function.ts +119 -0
- package/src/runtime/emit/transaction.ts +167 -0
- package/src/runtime/emit/unary.ts +85 -0
- package/src/runtime/emit/update.ts +73 -0
- package/src/runtime/emit/values.ts +66 -0
- package/src/runtime/emit/window-function.ts +42 -0
- package/src/runtime/emit/window.ts +472 -0
- package/src/runtime/emitters.ts +130 -0
- package/src/runtime/register.ts +134 -0
- package/src/runtime/scheduler.ts +488 -0
- package/src/runtime/types.ts +191 -0
- package/src/runtime/utils.ts +136 -0
- package/src/schema/change-events.ts +80 -0
- package/src/schema/column.ts +45 -0
- package/src/schema/function.ts +139 -0
- package/src/schema/manager.ts +694 -0
- package/src/schema/schema.ts +199 -0
- package/src/schema/table.ts +364 -0
- package/src/schema/view.ts +19 -0
- package/src/schema/window-function.ts +54 -0
- package/src/util/affinity.ts +151 -0
- package/src/util/ast-stringify.ts +764 -0
- package/src/util/cached.ts +25 -0
- package/src/util/coercion.ts +113 -0
- package/src/util/comparison.ts +437 -0
- package/src/util/environment.ts +52 -0
- package/src/util/latches.ts +47 -0
- package/src/util/patterns.ts +56 -0
- package/src/util/plan-formatter.ts +51 -0
- package/src/util/plugin-loader.ts +69 -0
- package/src/util/row-descriptor.ts +105 -0
- package/src/util/serialization.ts +45 -0
- package/src/util/working-table-iterable.ts +30 -0
- package/src/vtab/best-access-plan.ts +270 -0
- package/src/vtab/connection.ts +36 -0
- package/src/vtab/filter-info.ts +23 -0
- package/src/vtab/index-info.ts +84 -0
- package/src/vtab/manifest.ts +37 -0
- package/src/vtab/memory/connection.ts +73 -0
- package/src/vtab/memory/index.ts +178 -0
- package/src/vtab/memory/layer/base-cursor.ts +124 -0
- package/src/vtab/memory/layer/base.ts +273 -0
- package/src/vtab/memory/layer/connection.ts +203 -0
- package/src/vtab/memory/layer/interface.ts +47 -0
- package/src/vtab/memory/layer/manager.ts +861 -0
- package/src/vtab/memory/layer/safe-iterate.ts +49 -0
- package/src/vtab/memory/layer/scan-plan.ts +84 -0
- package/src/vtab/memory/layer/transaction-cursor.ts +162 -0
- package/src/vtab/memory/layer/transaction.ts +229 -0
- package/src/vtab/memory/module.ts +673 -0
- package/src/vtab/memory/table.ts +253 -0
- package/src/vtab/memory/types.ts +23 -0
- package/src/vtab/memory/utils/logging.ts +33 -0
- package/src/vtab/memory/utils/primary-key.ts +158 -0
- package/src/vtab/module.ts +140 -0
- package/src/vtab/table.ts +143 -0
|
@@ -0,0 +1,2619 @@
|
|
|
1
|
+
import { createLogger } from '../common/logger.js'; // Import logger
|
|
2
|
+
import { Lexer, TokenType } from './lexer.js';
|
|
3
|
+
import { ConflictResolution } from '../common/constants.js';
|
|
4
|
+
import { quereusError } from '../common/errors.js';
|
|
5
|
+
import { StatusCode } from '../common/types.js';
|
|
6
|
+
import { getSyncLiteral } from './utils.js';
|
|
7
|
+
const log = createLogger('parser:parser'); // Create logger instance
|
|
8
|
+
const errorLog = log.extend('error');
|
|
9
|
+
export class ParseError extends Error {
|
|
10
|
+
token;
|
|
11
|
+
constructor(token, message) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.token = token;
|
|
14
|
+
this.name = 'ParseError';
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
// Helper function to create the location object
|
|
18
|
+
function _createLoc(startToken, endToken) {
|
|
19
|
+
return {
|
|
20
|
+
start: {
|
|
21
|
+
line: startToken.startLine,
|
|
22
|
+
column: startToken.startColumn,
|
|
23
|
+
offset: startToken.startOffset,
|
|
24
|
+
},
|
|
25
|
+
end: {
|
|
26
|
+
line: endToken.endLine,
|
|
27
|
+
column: endToken.endColumn,
|
|
28
|
+
offset: endToken.endOffset,
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export class Parser {
|
|
33
|
+
tokens = [];
|
|
34
|
+
current = 0;
|
|
35
|
+
// Counter for positional parameters
|
|
36
|
+
parameterPosition = 1;
|
|
37
|
+
/**
|
|
38
|
+
* Initialize the parser with tokens from a SQL string
|
|
39
|
+
* @param sql SQL string to parse
|
|
40
|
+
* @returns this parser instance for chaining
|
|
41
|
+
*/
|
|
42
|
+
initialize(sql) {
|
|
43
|
+
const lexer = new Lexer(sql);
|
|
44
|
+
this.tokens = lexer.scanTokens();
|
|
45
|
+
this.current = 0;
|
|
46
|
+
this.parameterPosition = 1; // Reset parameter counter
|
|
47
|
+
// Check for errors from lexer
|
|
48
|
+
const errorToken = this.tokens.find(t => t.type === TokenType.ERROR);
|
|
49
|
+
if (errorToken) {
|
|
50
|
+
quereusError(`Lexer error: ${errorToken.lexeme}`, StatusCode.ERROR, undefined, {
|
|
51
|
+
loc: {
|
|
52
|
+
start: {
|
|
53
|
+
line: errorToken.startLine,
|
|
54
|
+
column: errorToken.startColumn,
|
|
55
|
+
},
|
|
56
|
+
end: {
|
|
57
|
+
line: errorToken.endLine,
|
|
58
|
+
column: errorToken.endColumn,
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return this;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Parse SQL text into an array of ASTs
|
|
67
|
+
*/
|
|
68
|
+
parseAll(sql) {
|
|
69
|
+
this.initialize(sql);
|
|
70
|
+
const statements = [];
|
|
71
|
+
while (!this.isAtEnd()) {
|
|
72
|
+
try {
|
|
73
|
+
const stmt = this.statement();
|
|
74
|
+
statements.push(stmt); // Cast needed as statement() returns AstNode
|
|
75
|
+
// Consume optional semicolon at the end of the statement
|
|
76
|
+
this.match(TokenType.SEMICOLON);
|
|
77
|
+
}
|
|
78
|
+
catch (e) {
|
|
79
|
+
// error() method now throws QuereusError directly with location info
|
|
80
|
+
if (e instanceof Error && e.name === 'QuereusError') {
|
|
81
|
+
throw e;
|
|
82
|
+
}
|
|
83
|
+
// Handle unexpected non-QuereusError exceptions
|
|
84
|
+
errorLog("Unhandled parser error: %O", e);
|
|
85
|
+
quereusError(`Parser error: ${e instanceof Error ? e.message : e}`, StatusCode.ERROR, e instanceof Error ? e : undefined);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// If we consumed all tokens and didn't parse any statements (e.g., empty input or only comments/whitespace),
|
|
89
|
+
// return an empty array instead of throwing an error.
|
|
90
|
+
return statements;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Parse SQL text into a single AST node.
|
|
94
|
+
* Use parseAll instead for potentially multi-statement strings.
|
|
95
|
+
* Throws error if more than one statement is found after the first.
|
|
96
|
+
*/
|
|
97
|
+
parse(sql) {
|
|
98
|
+
const statements = this.parseAll(sql);
|
|
99
|
+
if (statements.length === 0) {
|
|
100
|
+
// Handle case of empty input or input with only comments/whitespace
|
|
101
|
+
// Depending on desired behavior, could return null, undefined, or throw.
|
|
102
|
+
// Throwing seems reasonable as prepare/eval expect a statement.
|
|
103
|
+
quereusError("No SQL statement found to parse.", StatusCode.ERROR);
|
|
104
|
+
}
|
|
105
|
+
if (statements.length > 1) {
|
|
106
|
+
// Find the token that starts the second statement for better error location
|
|
107
|
+
const secondStatementStartToken = statements[1]?.loc?.start;
|
|
108
|
+
const errToken = this.tokens.find(t => t.startOffset === secondStatementStartToken?.offset) ?? this.peek();
|
|
109
|
+
this.error(errToken, "Provided SQL string contains multiple statements. Use exec() for multi-statement execution.");
|
|
110
|
+
}
|
|
111
|
+
return statements[0];
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Attempts to parse a WITH clause if present.
|
|
115
|
+
* @returns The WithClause AST node or undefined if no WITH clause is found.
|
|
116
|
+
*/
|
|
117
|
+
tryParseWithClause() {
|
|
118
|
+
if (!this.check(TokenType.WITH)) {
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
const startToken = this.advance(); // Consume WITH
|
|
122
|
+
const recursive = this.match(TokenType.RECURSIVE);
|
|
123
|
+
const ctes = [];
|
|
124
|
+
do {
|
|
125
|
+
ctes.push(this.commonTableExpression());
|
|
126
|
+
} while (this.match(TokenType.COMMA));
|
|
127
|
+
// Parse optional OPTION clause
|
|
128
|
+
let options;
|
|
129
|
+
if (this.matchKeyword('OPTION')) {
|
|
130
|
+
this.consume(TokenType.LPAREN, "Expected '(' after OPTION.");
|
|
131
|
+
// Parse MAXRECURSION option
|
|
132
|
+
if (this.matchKeyword('MAXRECURSION')) {
|
|
133
|
+
if (!this.check(TokenType.INTEGER)) {
|
|
134
|
+
this.error(this.peek(), "Expected integer value after MAXRECURSION.");
|
|
135
|
+
}
|
|
136
|
+
const maxRecursionToken = this.advance();
|
|
137
|
+
const maxRecursion = maxRecursionToken.literal;
|
|
138
|
+
if (maxRecursion < 0) {
|
|
139
|
+
this.error(maxRecursionToken, "MAXRECURSION value must be non-negative.");
|
|
140
|
+
}
|
|
141
|
+
options = { maxRecursion };
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
throw this.error(this.peek(), "Expected MAXRECURSION in OPTION clause.");
|
|
145
|
+
}
|
|
146
|
+
this.consume(TokenType.RPAREN, "Expected ')' after OPTION clause.");
|
|
147
|
+
}
|
|
148
|
+
const endToken = this.previous(); // Last token of the WITH clause
|
|
149
|
+
return { type: 'with', recursive, ctes, options, loc: _createLoc(startToken, endToken) };
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Parses a single Common Table Expression (CTE).
|
|
153
|
+
* cte_name [(col1, col2, ...)] AS (query)
|
|
154
|
+
*/
|
|
155
|
+
commonTableExpression() {
|
|
156
|
+
const startToken = this.peek(); // Peek before consuming name
|
|
157
|
+
const name = this.consumeIdentifier(['key', 'action', 'set', 'default', 'check', 'unique', 'like'], "Expected CTE name.");
|
|
158
|
+
let endToken = this.previous(); // End token initially is the name
|
|
159
|
+
let columns;
|
|
160
|
+
if (this.match(TokenType.LPAREN)) {
|
|
161
|
+
columns = [];
|
|
162
|
+
if (!this.check(TokenType.RPAREN)) {
|
|
163
|
+
do {
|
|
164
|
+
columns.push(this.consumeIdentifier(['key', 'action', 'set', 'default', 'check', 'unique', 'like'], "Expected column name in CTE definition."));
|
|
165
|
+
} while (this.match(TokenType.COMMA));
|
|
166
|
+
}
|
|
167
|
+
endToken = this.consume(TokenType.RPAREN, "Expected ')' after CTE column list.");
|
|
168
|
+
}
|
|
169
|
+
this.consume(TokenType.AS, "Expected 'AS' after CTE name.");
|
|
170
|
+
let materializationHint;
|
|
171
|
+
if (this.matchKeyword('MATERIALIZED')) {
|
|
172
|
+
materializationHint = 'materialized';
|
|
173
|
+
}
|
|
174
|
+
else if (this.matchKeyword('NOT')) {
|
|
175
|
+
this.consumeKeyword('MATERIALIZED', "Expected 'MATERIALIZED' after 'NOT'.");
|
|
176
|
+
materializationHint = 'not_materialized';
|
|
177
|
+
}
|
|
178
|
+
this.consume(TokenType.LPAREN, "Expected '(' before CTE query.");
|
|
179
|
+
// Parse the CTE query (can be SELECT, VALUES (via SELECT), INSERT, UPDATE, DELETE)
|
|
180
|
+
const queryStartToken = this.peek();
|
|
181
|
+
let query;
|
|
182
|
+
if (this.check(TokenType.SELECT)) {
|
|
183
|
+
this.advance(); // Consume SELECT token
|
|
184
|
+
query = this.selectStatement(queryStartToken); // Pass start token
|
|
185
|
+
}
|
|
186
|
+
else if (this.check(TokenType.INSERT)) {
|
|
187
|
+
this.advance(); // Consume INSERT token
|
|
188
|
+
query = this.insertStatement(queryStartToken);
|
|
189
|
+
}
|
|
190
|
+
else if (this.check(TokenType.UPDATE)) {
|
|
191
|
+
this.advance(); // Consume UPDATE token
|
|
192
|
+
query = this.updateStatement(queryStartToken);
|
|
193
|
+
}
|
|
194
|
+
else if (this.check(TokenType.DELETE)) {
|
|
195
|
+
this.advance(); // Consume DELETE token
|
|
196
|
+
query = this.deleteStatement(queryStartToken);
|
|
197
|
+
}
|
|
198
|
+
// TODO: Add support for VALUES directly if needed (though VALUES is usually part of SELECT)
|
|
199
|
+
else {
|
|
200
|
+
throw this.error(this.peek(), "Expected SELECT, INSERT, UPDATE, or DELETE statement for CTE query.");
|
|
201
|
+
}
|
|
202
|
+
endToken = this.consume(TokenType.RPAREN, "Expected ')' after CTE query."); // Capture ')' as end token
|
|
203
|
+
return { type: 'commonTableExpr', name, columns, query, materializationHint, loc: _createLoc(startToken, endToken) };
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Parse a single SQL statement
|
|
207
|
+
*/
|
|
208
|
+
statement() {
|
|
209
|
+
// Check for WITH clause first
|
|
210
|
+
let withClause;
|
|
211
|
+
if (this.check(TokenType.WITH)) {
|
|
212
|
+
withClause = this.tryParseWithClause();
|
|
213
|
+
}
|
|
214
|
+
const startToken = this.peek();
|
|
215
|
+
// --- Check for specific keywords first ---
|
|
216
|
+
const currentKeyword = startToken.lexeme.toUpperCase();
|
|
217
|
+
let stmt;
|
|
218
|
+
switch (currentKeyword) {
|
|
219
|
+
case 'SELECT':
|
|
220
|
+
this.advance();
|
|
221
|
+
stmt = this.selectStatement(startToken, withClause);
|
|
222
|
+
break;
|
|
223
|
+
case 'INSERT':
|
|
224
|
+
this.advance();
|
|
225
|
+
stmt = this.insertStatement(startToken, withClause);
|
|
226
|
+
break;
|
|
227
|
+
case 'UPDATE':
|
|
228
|
+
this.advance();
|
|
229
|
+
stmt = this.updateStatement(startToken, withClause);
|
|
230
|
+
break;
|
|
231
|
+
case 'DELETE':
|
|
232
|
+
this.advance();
|
|
233
|
+
stmt = this.deleteStatement(startToken, withClause);
|
|
234
|
+
break;
|
|
235
|
+
case 'VALUES':
|
|
236
|
+
this.advance();
|
|
237
|
+
stmt = this.valuesStatement(startToken);
|
|
238
|
+
break;
|
|
239
|
+
case 'CREATE':
|
|
240
|
+
this.advance();
|
|
241
|
+
stmt = this.createStatement(startToken, withClause);
|
|
242
|
+
break;
|
|
243
|
+
case 'DROP':
|
|
244
|
+
this.advance();
|
|
245
|
+
stmt = this.dropStatement(startToken, withClause);
|
|
246
|
+
break;
|
|
247
|
+
case 'ALTER':
|
|
248
|
+
this.advance();
|
|
249
|
+
stmt = this.alterTableStatement(startToken, withClause);
|
|
250
|
+
break;
|
|
251
|
+
case 'BEGIN':
|
|
252
|
+
this.advance();
|
|
253
|
+
stmt = this.beginStatement(startToken, withClause);
|
|
254
|
+
break;
|
|
255
|
+
case 'COMMIT':
|
|
256
|
+
this.advance();
|
|
257
|
+
stmt = this.commitStatement(startToken, withClause);
|
|
258
|
+
break;
|
|
259
|
+
case 'ROLLBACK':
|
|
260
|
+
this.advance();
|
|
261
|
+
stmt = this.rollbackStatement(startToken, withClause);
|
|
262
|
+
break;
|
|
263
|
+
case 'SAVEPOINT':
|
|
264
|
+
this.advance();
|
|
265
|
+
stmt = this.savepointStatement(startToken, withClause);
|
|
266
|
+
break;
|
|
267
|
+
case 'RELEASE':
|
|
268
|
+
this.advance();
|
|
269
|
+
stmt = this.releaseStatement(startToken, withClause);
|
|
270
|
+
break;
|
|
271
|
+
// TODO: Replace pragmas with build-in functions
|
|
272
|
+
case 'PRAGMA':
|
|
273
|
+
this.advance();
|
|
274
|
+
stmt = this.pragmaStatement(startToken, withClause);
|
|
275
|
+
break;
|
|
276
|
+
// --- Add default case ---
|
|
277
|
+
default:
|
|
278
|
+
// If it wasn't a recognized keyword starting the statement
|
|
279
|
+
throw this.error(startToken, `Expected statement type (SELECT, INSERT, UPDATE, DELETE, VALUES, CREATE, etc.), got '${startToken.lexeme}'.`);
|
|
280
|
+
}
|
|
281
|
+
// Attach WITH clause if present and supported
|
|
282
|
+
if (withClause && this.statementSupportsWithClause(stmt)) {
|
|
283
|
+
stmt.withClause = withClause;
|
|
284
|
+
if (withClause.loc && stmt.loc) {
|
|
285
|
+
stmt.loc.start = withClause.loc.start;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
else if (withClause) {
|
|
289
|
+
throw this.error(this.previous(), `WITH clause cannot be used with ${stmt.type} statement.`);
|
|
290
|
+
}
|
|
291
|
+
return stmt;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Parse an INSERT statement
|
|
295
|
+
* @returns AST for the INSERT statement
|
|
296
|
+
*/
|
|
297
|
+
insertStatement(startToken, withClause) {
|
|
298
|
+
// INTO keyword is optional in SQLite
|
|
299
|
+
this.matchKeyword('INTO'); // Handle missing keyword gracefully
|
|
300
|
+
// Parse the table reference
|
|
301
|
+
const table = this.tableIdentifier();
|
|
302
|
+
const contextualKeywords = ['key', 'action', 'set', 'default', 'check', 'unique', 'references', 'on', 'cascade', 'restrict', 'like'];
|
|
303
|
+
// Parse column list if provided
|
|
304
|
+
let columns;
|
|
305
|
+
if (this.match(TokenType.LPAREN)) {
|
|
306
|
+
columns = [];
|
|
307
|
+
do {
|
|
308
|
+
if (!this.checkIdentifierLike(contextualKeywords)) {
|
|
309
|
+
throw this.error(this.peek(), "Expected column name.");
|
|
310
|
+
}
|
|
311
|
+
columns.push(this.advance().lexeme);
|
|
312
|
+
} while (this.match(TokenType.COMMA));
|
|
313
|
+
this.consume(TokenType.RPAREN, "Expected ')' after column list.");
|
|
314
|
+
}
|
|
315
|
+
// Parse VALUES clause
|
|
316
|
+
let values;
|
|
317
|
+
let select;
|
|
318
|
+
let lastConsumedToken = this.previous(); // After columns or table id
|
|
319
|
+
if (this.match(TokenType.VALUES)) {
|
|
320
|
+
values = [];
|
|
321
|
+
do {
|
|
322
|
+
this.consume(TokenType.LPAREN, "Expected '(' before values.");
|
|
323
|
+
const valueList = [];
|
|
324
|
+
if (!this.check(TokenType.RPAREN)) { // Check for empty value list
|
|
325
|
+
do {
|
|
326
|
+
valueList.push(this.expression());
|
|
327
|
+
} while (this.match(TokenType.COMMA));
|
|
328
|
+
}
|
|
329
|
+
this.consume(TokenType.RPAREN, "Expected ')' after values.");
|
|
330
|
+
values.push(valueList);
|
|
331
|
+
lastConsumedToken = this.previous(); // Update after closing paren of value list
|
|
332
|
+
} while (this.match(TokenType.COMMA));
|
|
333
|
+
}
|
|
334
|
+
else if (this.check(TokenType.SELECT)) { // If current token is SELECT
|
|
335
|
+
// Handle INSERT ... SELECT
|
|
336
|
+
// Consume the SELECT token, as selectStatement expects to start parsing after it.
|
|
337
|
+
// selectKeywordToken will be the actual 'SELECT' token object, used for location.
|
|
338
|
+
const selectKeywordToken = this.advance(); // Consume 'SELECT'
|
|
339
|
+
// Pass the withClause so the embedded SELECT can (via the planner) resolve CTEs defined for the INSERT.
|
|
340
|
+
select = this.selectStatement(selectKeywordToken, withClause);
|
|
341
|
+
lastConsumedToken = this.previous(); // After SELECT statement is parsed
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
throw this.error(this.peek(), "Expected VALUES or SELECT after INSERT.");
|
|
345
|
+
}
|
|
346
|
+
// Parse RETURNING clause if present
|
|
347
|
+
let returning;
|
|
348
|
+
if (this.matchKeyword('RETURNING')) {
|
|
349
|
+
returning = this.columnList();
|
|
350
|
+
lastConsumedToken = this.previous(); // Update after RETURNING clause
|
|
351
|
+
}
|
|
352
|
+
return {
|
|
353
|
+
type: 'insert',
|
|
354
|
+
table,
|
|
355
|
+
columns,
|
|
356
|
+
values,
|
|
357
|
+
select,
|
|
358
|
+
returning,
|
|
359
|
+
loc: _createLoc(startToken, lastConsumedToken),
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Parse a SELECT statement
|
|
364
|
+
* @param startToken The 'SELECT' token or start token of a sub-query
|
|
365
|
+
* @param withClause The WITH clause context for CTE access
|
|
366
|
+
* @param isCompoundSubquery If true, don't parse ORDER BY/LIMIT as they belong to the outer compound
|
|
367
|
+
* @returns AST for the SELECT statement
|
|
368
|
+
*/
|
|
369
|
+
selectStatement(startToken, withClause, isCompoundSubquery = false) {
|
|
370
|
+
const start = startToken ?? this.previous(); // Use provided or the keyword token
|
|
371
|
+
let lastConsumedToken = start; // Initialize lastConsumed
|
|
372
|
+
const distinct = this.matchKeyword('DISTINCT');
|
|
373
|
+
if (distinct)
|
|
374
|
+
lastConsumedToken = this.previous();
|
|
375
|
+
const all = !distinct && this.matchKeyword('ALL');
|
|
376
|
+
if (all)
|
|
377
|
+
lastConsumedToken = this.previous();
|
|
378
|
+
// Parse column list
|
|
379
|
+
const columns = this.columnList();
|
|
380
|
+
if (columns.length > 0)
|
|
381
|
+
lastConsumedToken = this.previous(); // Update after last column element
|
|
382
|
+
// Parse FROM clause if present
|
|
383
|
+
let from;
|
|
384
|
+
if (this.match(TokenType.FROM)) {
|
|
385
|
+
from = this.tableSourceList(withClause);
|
|
386
|
+
if (from.length > 0)
|
|
387
|
+
lastConsumedToken = this.previous(); // After last source/join
|
|
388
|
+
}
|
|
389
|
+
// Parse WHERE clause if present
|
|
390
|
+
let where;
|
|
391
|
+
if (this.match(TokenType.WHERE)) {
|
|
392
|
+
where = this.expression();
|
|
393
|
+
lastConsumedToken = this.previous(); // After where expression
|
|
394
|
+
}
|
|
395
|
+
// Parse GROUP BY clause if present
|
|
396
|
+
let groupBy;
|
|
397
|
+
if (this.match(TokenType.GROUP) && this.consume(TokenType.BY, "Expected 'BY' after 'GROUP'.")) {
|
|
398
|
+
groupBy = [];
|
|
399
|
+
do {
|
|
400
|
+
groupBy.push(this.expression());
|
|
401
|
+
} while (this.match(TokenType.COMMA));
|
|
402
|
+
lastConsumedToken = this.previous(); // After last group by expression
|
|
403
|
+
}
|
|
404
|
+
// Parse HAVING clause if present
|
|
405
|
+
let having;
|
|
406
|
+
if (this.match(TokenType.HAVING)) {
|
|
407
|
+
having = this.expression();
|
|
408
|
+
lastConsumedToken = this.previous(); // After having expression
|
|
409
|
+
}
|
|
410
|
+
// Check for compound set operations (UNION / INTERSECT / EXCEPT) BEFORE ORDER BY/LIMIT
|
|
411
|
+
let compound;
|
|
412
|
+
if (this.match(TokenType.UNION, TokenType.INTERSECT, TokenType.EXCEPT)) {
|
|
413
|
+
const tok = this.previous();
|
|
414
|
+
let op;
|
|
415
|
+
if (tok.type === TokenType.UNION) {
|
|
416
|
+
if (this.match(TokenType.ALL)) {
|
|
417
|
+
op = 'unionAll';
|
|
418
|
+
}
|
|
419
|
+
else {
|
|
420
|
+
op = 'union';
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
else if (tok.type === TokenType.INTERSECT) {
|
|
424
|
+
op = 'intersect';
|
|
425
|
+
}
|
|
426
|
+
else {
|
|
427
|
+
op = 'except';
|
|
428
|
+
}
|
|
429
|
+
let rightSelect;
|
|
430
|
+
// Handle parenthesized subquery after set operation
|
|
431
|
+
if (this.match(TokenType.LPAREN)) {
|
|
432
|
+
const selectToken = this.consume(TokenType.SELECT, "Expected 'SELECT' in parenthesized set operation.");
|
|
433
|
+
rightSelect = this.selectStatement(selectToken, withClause, true); // Pass true to indicate compound subquery
|
|
434
|
+
this.consume(TokenType.RPAREN, "Expected ')' after parenthesized set operation.");
|
|
435
|
+
}
|
|
436
|
+
else {
|
|
437
|
+
// Handle direct SELECT statement
|
|
438
|
+
const selectStartToken = this.peek();
|
|
439
|
+
if (this.match(TokenType.SELECT)) {
|
|
440
|
+
rightSelect = this.selectStatement(selectStartToken, withClause, true); // Pass true to indicate compound subquery
|
|
441
|
+
}
|
|
442
|
+
else {
|
|
443
|
+
throw this.error(this.peek(), "Expected 'SELECT' or '(' after set operation keyword.");
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
lastConsumedToken = this.previous();
|
|
447
|
+
compound = { op, select: rightSelect };
|
|
448
|
+
}
|
|
449
|
+
// Parse ORDER BY clause if present (applies to final result after compound operations)
|
|
450
|
+
// Skip if this is a compound subquery as ORDER BY belongs to the outer compound
|
|
451
|
+
let orderBy;
|
|
452
|
+
if (!isCompoundSubquery && this.match(TokenType.ORDER) && this.consume(TokenType.BY, "Expected 'BY' after 'ORDER'.")) {
|
|
453
|
+
orderBy = [];
|
|
454
|
+
do {
|
|
455
|
+
const expr = this.expression();
|
|
456
|
+
const direction = this.match(TokenType.DESC) ? 'desc' :
|
|
457
|
+
(this.match(TokenType.ASC) ? 'asc' : 'asc'); // Default to ASC
|
|
458
|
+
orderBy.push({ expr, direction });
|
|
459
|
+
} while (this.match(TokenType.COMMA));
|
|
460
|
+
lastConsumedToken = this.previous(); // After last order by clause
|
|
461
|
+
}
|
|
462
|
+
// Parse LIMIT clause if present (applies to final result after compound operations)
|
|
463
|
+
// Skip if this is a compound subquery as LIMIT belongs to the outer compound
|
|
464
|
+
let limit;
|
|
465
|
+
let offset;
|
|
466
|
+
if (!isCompoundSubquery && this.match(TokenType.LIMIT)) {
|
|
467
|
+
limit = this.expression();
|
|
468
|
+
lastConsumedToken = this.previous(); // After limit expression
|
|
469
|
+
// LIMIT x OFFSET y syntax
|
|
470
|
+
if (this.match(TokenType.OFFSET)) {
|
|
471
|
+
offset = this.expression();
|
|
472
|
+
lastConsumedToken = this.previous(); // After offset expression
|
|
473
|
+
}
|
|
474
|
+
// LIMIT x, y syntax (x is offset, y is limit)
|
|
475
|
+
else if (this.match(TokenType.COMMA)) {
|
|
476
|
+
offset = limit;
|
|
477
|
+
limit = this.expression();
|
|
478
|
+
lastConsumedToken = this.previous(); // After second limit expression
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
return {
|
|
482
|
+
type: 'select',
|
|
483
|
+
columns,
|
|
484
|
+
from,
|
|
485
|
+
where,
|
|
486
|
+
groupBy,
|
|
487
|
+
having,
|
|
488
|
+
orderBy,
|
|
489
|
+
limit,
|
|
490
|
+
offset,
|
|
491
|
+
distinct,
|
|
492
|
+
all,
|
|
493
|
+
compound,
|
|
494
|
+
loc: _createLoc(start, lastConsumedToken),
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* Parse a comma-separated list of result columns for SELECT
|
|
499
|
+
*/
|
|
500
|
+
columnList() {
|
|
501
|
+
const columns = [];
|
|
502
|
+
const contextualKeywords = ['key', 'action', 'set', 'default', 'check', 'unique', 'references', 'on', 'cascade', 'restrict', 'like'];
|
|
503
|
+
do {
|
|
504
|
+
log(`columnList: Loop start. Current token: ${this.peek().lexeme} (${this.peek().type})`); // DEBUG
|
|
505
|
+
// Handle wildcard: * or table.*
|
|
506
|
+
if (this.match(TokenType.ASTERISK)) {
|
|
507
|
+
columns.push({ type: 'all' });
|
|
508
|
+
}
|
|
509
|
+
// Handle table.* syntax
|
|
510
|
+
else if (this.checkIdentifierLike(contextualKeywords) && this.checkNext(1, TokenType.DOT) &&
|
|
511
|
+
this.checkNext(2, TokenType.ASTERISK)) {
|
|
512
|
+
const table = this.consumeIdentifier(contextualKeywords, "Expected table name before '.*'.");
|
|
513
|
+
this.advance(); // consume DOT
|
|
514
|
+
this.advance(); // consume ASTERISK
|
|
515
|
+
columns.push({ type: 'all', table });
|
|
516
|
+
}
|
|
517
|
+
// Handle regular column expression
|
|
518
|
+
else {
|
|
519
|
+
log(`columnList: Parsing expression...`); // DEBUG
|
|
520
|
+
const expr = this.expression();
|
|
521
|
+
log(`columnList: Parsed expression. Current token: ${this.peek().lexeme} (${this.peek().type})`); // DEBUG
|
|
522
|
+
let alias;
|
|
523
|
+
// Handle AS alias or just alias
|
|
524
|
+
if (this.match(TokenType.AS)) {
|
|
525
|
+
if (this.checkIdentifierLike(contextualKeywords) || this.check(TokenType.STRING)) {
|
|
526
|
+
const aliasToken = this.advance();
|
|
527
|
+
alias = aliasToken.lexeme;
|
|
528
|
+
if (aliasToken.type === TokenType.STRING) {
|
|
529
|
+
alias = aliasToken.literal;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
else {
|
|
533
|
+
throw this.error(this.peek(), "Expected identifier or string after 'AS'.");
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
// Implicit alias (no AS keyword)
|
|
537
|
+
else if (this.checkIdentifierLike([]) &&
|
|
538
|
+
!this.checkNext(1, TokenType.LPAREN) &&
|
|
539
|
+
!this.checkNext(1, TokenType.DOT) &&
|
|
540
|
+
!this.checkNext(1, TokenType.COMMA) &&
|
|
541
|
+
!this.isEndOfClause()) {
|
|
542
|
+
const aliasToken = this.advance();
|
|
543
|
+
alias = aliasToken.lexeme;
|
|
544
|
+
}
|
|
545
|
+
columns.push({ type: 'column', expr, alias });
|
|
546
|
+
}
|
|
547
|
+
log(`columnList: Checking for comma. Current token: ${this.peek().lexeme} (${this.peek().type})`); // DEBUG
|
|
548
|
+
} while (this.match(TokenType.COMMA));
|
|
549
|
+
log(`columnList: Loop ended. Current token: ${this.peek().lexeme} (${this.peek().type})`); // DEBUG
|
|
550
|
+
return columns;
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* Parse a table identifier (possibly schema-qualified)
|
|
554
|
+
*/
|
|
555
|
+
tableIdentifier() {
|
|
556
|
+
const startToken = this.peek();
|
|
557
|
+
let schema;
|
|
558
|
+
let name;
|
|
559
|
+
let endToken = startToken;
|
|
560
|
+
const contextualKeywords = ['key', 'action', 'set', 'default', 'check', 'unique', 'references', 'on', 'cascade', 'restrict', 'like'];
|
|
561
|
+
// Check for schema.table pattern
|
|
562
|
+
if (this.checkIdentifierLike(contextualKeywords) && this.checkNext(1, TokenType.DOT)) {
|
|
563
|
+
schema = this.consumeIdentifier(contextualKeywords, "Expected schema name.");
|
|
564
|
+
this.advance(); // Consume DOT
|
|
565
|
+
name = this.consumeIdentifier(contextualKeywords, "Expected table name after schema.");
|
|
566
|
+
endToken = this.previous();
|
|
567
|
+
}
|
|
568
|
+
else if (this.checkIdentifierLike(contextualKeywords)) {
|
|
569
|
+
name = this.consumeIdentifier(contextualKeywords, "Expected table name.");
|
|
570
|
+
endToken = this.previous();
|
|
571
|
+
}
|
|
572
|
+
else {
|
|
573
|
+
throw this.error(this.peek(), "Expected table name.");
|
|
574
|
+
}
|
|
575
|
+
return {
|
|
576
|
+
type: 'identifier',
|
|
577
|
+
name,
|
|
578
|
+
schema,
|
|
579
|
+
loc: _createLoc(startToken, endToken),
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* Parse a comma-separated list of table sources (FROM clause)
|
|
584
|
+
*/
|
|
585
|
+
tableSourceList(withClause) {
|
|
586
|
+
const sources = [];
|
|
587
|
+
do {
|
|
588
|
+
// Get the base table source
|
|
589
|
+
let source = this.tableSource(withClause);
|
|
590
|
+
// Look for JOINs
|
|
591
|
+
while (this.isJoinToken()) {
|
|
592
|
+
source = this.joinClause(source, withClause);
|
|
593
|
+
}
|
|
594
|
+
sources.push(source);
|
|
595
|
+
} while (this.match(TokenType.COMMA));
|
|
596
|
+
return sources;
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* Parse a single table source, which can now be a table name, table-valued function call, or subquery
|
|
600
|
+
*/
|
|
601
|
+
tableSource(withClause) {
|
|
602
|
+
const startToken = this.peek();
|
|
603
|
+
const contextualKeywords = ['key', 'action', 'set', 'default', 'check', 'unique', 'references', 'on', 'cascade', 'restrict', 'like'];
|
|
604
|
+
// Check for subquery: ( SELECT ... or ( VALUES ... or ( INSERT/UPDATE/DELETE ...
|
|
605
|
+
if (this.check(TokenType.LPAREN)) {
|
|
606
|
+
// Look ahead to see if this is a subquery
|
|
607
|
+
const lookahead = this.current + 1;
|
|
608
|
+
if (lookahead < this.tokens.length) {
|
|
609
|
+
const nextTokenType = this.tokens[lookahead].type;
|
|
610
|
+
if (nextTokenType === TokenType.SELECT || nextTokenType === TokenType.VALUES) {
|
|
611
|
+
return this.subquerySource(startToken, withClause);
|
|
612
|
+
}
|
|
613
|
+
else if (nextTokenType === TokenType.INSERT || nextTokenType === TokenType.UPDATE || nextTokenType === TokenType.DELETE) {
|
|
614
|
+
return this.mutatingSubquerySource(startToken, withClause);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
// Check for function call syntax: IDENTIFIER (
|
|
619
|
+
if (this.checkIdentifierLike(contextualKeywords) && this.checkNext(1, TokenType.LPAREN)) {
|
|
620
|
+
return this.functionSource(startToken);
|
|
621
|
+
}
|
|
622
|
+
// Otherwise, assume it's a standard table source
|
|
623
|
+
else {
|
|
624
|
+
return this.standardTableSource(startToken);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
/** Parses a subquery source: (SELECT ...) AS alias */
|
|
628
|
+
subquerySource(startToken, withClause) {
|
|
629
|
+
this.consume(TokenType.LPAREN, "Expected '(' before subquery.");
|
|
630
|
+
let subquery;
|
|
631
|
+
if (this.check(TokenType.SELECT)) {
|
|
632
|
+
// Consume the SELECT token and pass it as startToken to selectStatement
|
|
633
|
+
const selectToken = this.advance();
|
|
634
|
+
subquery = this.selectStatement(selectToken, withClause);
|
|
635
|
+
}
|
|
636
|
+
else if (this.check(TokenType.VALUES)) {
|
|
637
|
+
// Handle VALUES subquery
|
|
638
|
+
const valuesToken = this.advance();
|
|
639
|
+
subquery = this.valuesStatement(valuesToken);
|
|
640
|
+
}
|
|
641
|
+
else {
|
|
642
|
+
throw this.error(this.peek(), "Expected 'SELECT' or 'VALUES' in subquery.");
|
|
643
|
+
}
|
|
644
|
+
this.consume(TokenType.RPAREN, "Expected ')' after subquery.");
|
|
645
|
+
// Parse optional alias for subquery
|
|
646
|
+
let alias;
|
|
647
|
+
let columns;
|
|
648
|
+
if (this.match(TokenType.AS)) {
|
|
649
|
+
if (!this.checkIdentifierLike([])) {
|
|
650
|
+
throw this.error(this.peek(), "Expected alias after 'AS'.");
|
|
651
|
+
}
|
|
652
|
+
const aliasToken = this.advance();
|
|
653
|
+
alias = aliasToken.lexeme;
|
|
654
|
+
}
|
|
655
|
+
else if (this.checkIdentifierLike([]) &&
|
|
656
|
+
!this.checkNext(1, TokenType.DOT) &&
|
|
657
|
+
!this.checkNext(1, TokenType.COMMA) &&
|
|
658
|
+
!this.isJoinToken() &&
|
|
659
|
+
!this.isEndOfClause()) {
|
|
660
|
+
const aliasToken = this.advance();
|
|
661
|
+
alias = aliasToken.lexeme;
|
|
662
|
+
}
|
|
663
|
+
else {
|
|
664
|
+
// Generate a default alias if none provided
|
|
665
|
+
alias = `subquery_${startToken.startOffset}`;
|
|
666
|
+
}
|
|
667
|
+
// Parse optional column list after alias: AS alias(col1, col2, ...)
|
|
668
|
+
if (this.match(TokenType.LPAREN)) {
|
|
669
|
+
columns = [];
|
|
670
|
+
const contextualKeywords = ['key', 'action', 'set', 'default', 'check', 'unique', 'references', 'on', 'cascade', 'restrict', 'like'];
|
|
671
|
+
if (!this.check(TokenType.RPAREN)) {
|
|
672
|
+
do {
|
|
673
|
+
columns.push(this.consumeIdentifier(contextualKeywords, "Expected column name in alias column list."));
|
|
674
|
+
} while (this.match(TokenType.COMMA));
|
|
675
|
+
}
|
|
676
|
+
this.consume(TokenType.RPAREN, "Expected ')' after alias column list.");
|
|
677
|
+
}
|
|
678
|
+
const endToken = this.previous();
|
|
679
|
+
return {
|
|
680
|
+
type: 'subquerySource',
|
|
681
|
+
subquery,
|
|
682
|
+
alias,
|
|
683
|
+
columns,
|
|
684
|
+
loc: _createLoc(startToken, endToken),
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
/** Parses a mutating subquery source: (INSERT/UPDATE/DELETE ... RETURNING ...) AS alias */
|
|
688
|
+
mutatingSubquerySource(startToken, withClause) {
|
|
689
|
+
this.consume(TokenType.LPAREN, "Expected '(' before mutating subquery.");
|
|
690
|
+
let stmt;
|
|
691
|
+
if (this.check(TokenType.INSERT)) {
|
|
692
|
+
const insertToken = this.advance();
|
|
693
|
+
stmt = this.insertStatement(insertToken, withClause);
|
|
694
|
+
}
|
|
695
|
+
else if (this.check(TokenType.UPDATE)) {
|
|
696
|
+
const updateToken = this.advance();
|
|
697
|
+
stmt = this.updateStatement(updateToken, withClause);
|
|
698
|
+
}
|
|
699
|
+
else if (this.check(TokenType.DELETE)) {
|
|
700
|
+
const deleteToken = this.advance();
|
|
701
|
+
stmt = this.deleteStatement(deleteToken, withClause);
|
|
702
|
+
}
|
|
703
|
+
else {
|
|
704
|
+
throw this.error(this.peek(), "Expected 'INSERT', 'UPDATE', or 'DELETE' in mutating subquery.");
|
|
705
|
+
}
|
|
706
|
+
// Validate that the statement has a RETURNING clause
|
|
707
|
+
if (!stmt.returning || stmt.returning.length === 0) {
|
|
708
|
+
throw this.error(this.previous(), "Mutating subqueries must have a RETURNING clause to be used as table sources.");
|
|
709
|
+
}
|
|
710
|
+
this.consume(TokenType.RPAREN, "Expected ')' after mutating subquery.");
|
|
711
|
+
// Parse optional alias for mutating subquery
|
|
712
|
+
let alias;
|
|
713
|
+
let columns;
|
|
714
|
+
if (this.match(TokenType.AS)) {
|
|
715
|
+
if (!this.checkIdentifierLike([])) {
|
|
716
|
+
throw this.error(this.peek(), "Expected alias after 'AS'.");
|
|
717
|
+
}
|
|
718
|
+
const aliasToken = this.advance();
|
|
719
|
+
alias = aliasToken.lexeme;
|
|
720
|
+
}
|
|
721
|
+
else if (this.checkIdentifierLike([]) &&
|
|
722
|
+
!this.checkNext(1, TokenType.DOT) &&
|
|
723
|
+
!this.checkNext(1, TokenType.COMMA) &&
|
|
724
|
+
!this.isJoinToken() &&
|
|
725
|
+
!this.isEndOfClause()) {
|
|
726
|
+
const aliasToken = this.advance();
|
|
727
|
+
alias = aliasToken.lexeme;
|
|
728
|
+
}
|
|
729
|
+
else {
|
|
730
|
+
// Generate a default alias if none provided
|
|
731
|
+
alias = `mutating_subquery_${startToken.startOffset}`;
|
|
732
|
+
}
|
|
733
|
+
// Parse optional column list after alias: AS alias(col1, col2, ...)
|
|
734
|
+
if (this.match(TokenType.LPAREN)) {
|
|
735
|
+
columns = [];
|
|
736
|
+
const contextualKeywords = ['key', 'action', 'set', 'default', 'check', 'unique', 'references', 'on', 'cascade', 'restrict', 'like'];
|
|
737
|
+
if (!this.check(TokenType.RPAREN)) {
|
|
738
|
+
do {
|
|
739
|
+
columns.push(this.consumeIdentifier(contextualKeywords, "Expected column name in alias column list."));
|
|
740
|
+
} while (this.match(TokenType.COMMA));
|
|
741
|
+
}
|
|
742
|
+
this.consume(TokenType.RPAREN, "Expected ')' after alias column list.");
|
|
743
|
+
}
|
|
744
|
+
const endToken = this.previous();
|
|
745
|
+
return {
|
|
746
|
+
type: 'mutatingSubquerySource',
|
|
747
|
+
stmt,
|
|
748
|
+
alias,
|
|
749
|
+
columns,
|
|
750
|
+
loc: _createLoc(startToken, endToken),
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
/** Parses a standard table source (schema.table or table) */
|
|
754
|
+
standardTableSource(startToken) {
|
|
755
|
+
const contextualKeywords = ['key', 'action', 'set', 'default', 'check', 'unique', 'references', 'on', 'cascade', 'restrict', 'like'];
|
|
756
|
+
// Parse table name (potentially schema-qualified)
|
|
757
|
+
const table = this.tableIdentifier();
|
|
758
|
+
let endToken = this.previous(); // Initialize endToken after parsing table identifier
|
|
759
|
+
// Parse optional alias
|
|
760
|
+
let alias;
|
|
761
|
+
if (this.match(TokenType.AS)) {
|
|
762
|
+
if (!this.checkIdentifierLike(contextualKeywords)) {
|
|
763
|
+
throw this.error(this.peek(), "Expected alias after 'AS'.");
|
|
764
|
+
}
|
|
765
|
+
const aliasToken = this.advance();
|
|
766
|
+
alias = aliasToken.lexeme;
|
|
767
|
+
endToken = aliasToken;
|
|
768
|
+
}
|
|
769
|
+
else if (this.checkIdentifierLike([]) &&
|
|
770
|
+
!this.checkNext(1, TokenType.DOT) &&
|
|
771
|
+
!this.checkNext(1, TokenType.COMMA) &&
|
|
772
|
+
!this.isJoinToken() &&
|
|
773
|
+
!this.isEndOfClause()) {
|
|
774
|
+
const aliasToken = this.advance();
|
|
775
|
+
alias = aliasToken.lexeme;
|
|
776
|
+
endToken = aliasToken;
|
|
777
|
+
}
|
|
778
|
+
return {
|
|
779
|
+
type: 'table',
|
|
780
|
+
table,
|
|
781
|
+
alias,
|
|
782
|
+
loc: _createLoc(startToken, endToken),
|
|
783
|
+
};
|
|
784
|
+
}
|
|
785
|
+
/** Parses a table-valued function source: name(arg1, ...) [AS alias] */
|
|
786
|
+
functionSource(startToken) {
|
|
787
|
+
const contextualKeywords = ['key', 'action', 'set', 'default', 'check', 'unique', 'references', 'on', 'cascade', 'restrict', 'like'];
|
|
788
|
+
const name = this.tableIdentifier(); // name has its own loc
|
|
789
|
+
let endToken = this.previous(); // Initialize endToken after parsing function identifier
|
|
790
|
+
this.consume(TokenType.LPAREN, "Expected '(' after table function name.");
|
|
791
|
+
const args = [];
|
|
792
|
+
if (!this.check(TokenType.RPAREN)) {
|
|
793
|
+
// Handle DISTINCT inside function calls like COUNT(DISTINCT col)
|
|
794
|
+
const distinct = this.matchKeyword('DISTINCT');
|
|
795
|
+
// Handle * argument AFTER checking for distinct
|
|
796
|
+
if (this.match(TokenType.ASTERISK)) {
|
|
797
|
+
// Do not add '*' as an argument to the list for aggregates like COUNT(*)
|
|
798
|
+
if (args.length > 0 || distinct) {
|
|
799
|
+
// '*' is only valid as the *only* argument, potentially after DISTINCT
|
|
800
|
+
// e.g. COUNT(*), COUNT(DISTINCT *) - though DISTINCT * might not be standard SQL?
|
|
801
|
+
// For now, disallow '*' if other args exist.
|
|
802
|
+
throw this.error(this.previous(), "'*' cannot be used with other arguments in function call.");
|
|
803
|
+
}
|
|
804
|
+
// If we parsed '*', the args list remains empty.
|
|
805
|
+
}
|
|
806
|
+
else {
|
|
807
|
+
// Parse regular arguments if '*' wasn't found
|
|
808
|
+
do {
|
|
809
|
+
args.push(this.expression());
|
|
810
|
+
} while (this.match(TokenType.COMMA));
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
endToken = this.consume(TokenType.RPAREN, "Expected ')' after table function arguments.");
|
|
814
|
+
// Parse optional alias (same logic as for standard tables)
|
|
815
|
+
let alias;
|
|
816
|
+
if (this.match(TokenType.AS)) {
|
|
817
|
+
if (!this.checkIdentifierLike(contextualKeywords)) {
|
|
818
|
+
throw this.error(this.peek(), "Expected alias after 'AS'.");
|
|
819
|
+
}
|
|
820
|
+
const aliasToken = this.advance();
|
|
821
|
+
alias = aliasToken.lexeme;
|
|
822
|
+
endToken = aliasToken;
|
|
823
|
+
}
|
|
824
|
+
else if (this.checkIdentifierLike([]) &&
|
|
825
|
+
!this.checkNext(1, TokenType.DOT) &&
|
|
826
|
+
!this.checkNext(1, TokenType.COMMA) &&
|
|
827
|
+
!this.isJoinToken() &&
|
|
828
|
+
!this.isEndOfClause()) {
|
|
829
|
+
const aliasToken = this.advance();
|
|
830
|
+
alias = aliasToken.lexeme;
|
|
831
|
+
endToken = aliasToken;
|
|
832
|
+
}
|
|
833
|
+
return {
|
|
834
|
+
type: 'functionSource',
|
|
835
|
+
name,
|
|
836
|
+
args,
|
|
837
|
+
alias,
|
|
838
|
+
loc: _createLoc(startToken, endToken),
|
|
839
|
+
};
|
|
840
|
+
}
|
|
841
|
+
/**
|
|
842
|
+
* Parse a JOIN clause
|
|
843
|
+
*/
|
|
844
|
+
joinClause(left, withClause) {
|
|
845
|
+
const joinStartToken = this.peek(); // Capture token before parsing JOIN type
|
|
846
|
+
// Determine join type
|
|
847
|
+
let joinType = 'inner';
|
|
848
|
+
if (this.match(TokenType.LEFT)) {
|
|
849
|
+
this.match(TokenType.OUTER); // optional
|
|
850
|
+
joinType = 'left';
|
|
851
|
+
}
|
|
852
|
+
else if (this.match(TokenType.RIGHT)) {
|
|
853
|
+
this.match(TokenType.OUTER); // optional
|
|
854
|
+
joinType = 'right';
|
|
855
|
+
}
|
|
856
|
+
else if (this.match(TokenType.FULL)) {
|
|
857
|
+
this.match(TokenType.OUTER); // optional
|
|
858
|
+
joinType = 'full';
|
|
859
|
+
}
|
|
860
|
+
else if (this.match(TokenType.CROSS)) {
|
|
861
|
+
joinType = 'cross';
|
|
862
|
+
}
|
|
863
|
+
else if (this.match(TokenType.INNER)) {
|
|
864
|
+
joinType = 'inner';
|
|
865
|
+
}
|
|
866
|
+
// Consume JOIN token
|
|
867
|
+
this.consume(TokenType.JOIN, "Expected 'JOIN'.");
|
|
868
|
+
// Parse right side of join
|
|
869
|
+
const right = this.tableSource(withClause);
|
|
870
|
+
// Parse join condition
|
|
871
|
+
let condition;
|
|
872
|
+
let columns;
|
|
873
|
+
let endToken = this.previous(); // End token is end of right source initially
|
|
874
|
+
if (this.match(TokenType.ON)) {
|
|
875
|
+
condition = this.expression();
|
|
876
|
+
endToken = this.previous(); // End token is end of ON expression
|
|
877
|
+
}
|
|
878
|
+
else if (this.match(TokenType.USING)) {
|
|
879
|
+
this.consume(TokenType.LPAREN, "Expected '(' after 'USING'.");
|
|
880
|
+
columns = [];
|
|
881
|
+
const contextualKeywords = ['key', 'action', 'set', 'default', 'check', 'unique', 'references', 'on', 'cascade', 'restrict', 'like'];
|
|
882
|
+
do {
|
|
883
|
+
columns.push(this.consumeIdentifier(contextualKeywords, "Expected column name."));
|
|
884
|
+
} while (this.match(TokenType.COMMA));
|
|
885
|
+
endToken = this.consume(TokenType.RPAREN, "Expected ')' after columns.");
|
|
886
|
+
}
|
|
887
|
+
else if (joinType !== 'cross') {
|
|
888
|
+
throw this.error(this.peek(), "Expected 'ON' or 'USING' after JOIN.");
|
|
889
|
+
}
|
|
890
|
+
return {
|
|
891
|
+
type: 'join',
|
|
892
|
+
joinType,
|
|
893
|
+
left,
|
|
894
|
+
right,
|
|
895
|
+
condition,
|
|
896
|
+
columns,
|
|
897
|
+
loc: _createLoc(joinStartToken, endToken),
|
|
898
|
+
};
|
|
899
|
+
}
|
|
900
|
+
/**
|
|
901
|
+
* Parse an expression
|
|
902
|
+
*/
|
|
903
|
+
expression() {
|
|
904
|
+
return this.logicalXorOr();
|
|
905
|
+
}
|
|
906
|
+
/**
|
|
907
|
+
* Parse logical OR and XOR expressions (lowest precedence)
|
|
908
|
+
*/
|
|
909
|
+
logicalXorOr() {
|
|
910
|
+
let expr = this.logicalAnd();
|
|
911
|
+
const startToken = expr.loc ? this.tokens.find(t => t.startOffset === expr.loc.start.offset) ?? this.peek() : this.peek(); // Get start token of left expr
|
|
912
|
+
while (this.match(TokenType.OR, TokenType.XOR)) { // Added XOR
|
|
913
|
+
const operator = this.previous().type === TokenType.XOR ? 'XOR' : 'OR'; // Determine operator
|
|
914
|
+
const right = this.logicalAnd();
|
|
915
|
+
const endToken = this.previous(); // End token is end of right expr
|
|
916
|
+
expr = {
|
|
917
|
+
type: 'binary',
|
|
918
|
+
operator,
|
|
919
|
+
left: expr,
|
|
920
|
+
right,
|
|
921
|
+
loc: _createLoc(startToken, endToken),
|
|
922
|
+
};
|
|
923
|
+
}
|
|
924
|
+
return expr;
|
|
925
|
+
}
|
|
926
|
+
/**
|
|
927
|
+
* Parse logical AND expression
|
|
928
|
+
*/
|
|
929
|
+
logicalAnd() {
|
|
930
|
+
let expr = this.isNull();
|
|
931
|
+
const startToken = expr.loc ? this.tokens.find(t => t.startOffset === expr.loc.start.offset) ?? this.peek() : this.peek(); // Get start token of left expr
|
|
932
|
+
while (this.match(TokenType.AND)) {
|
|
933
|
+
const operator = 'AND';
|
|
934
|
+
const right = this.isNull();
|
|
935
|
+
const endToken = this.previous(); // End token is end of right expr
|
|
936
|
+
expr = {
|
|
937
|
+
type: 'binary',
|
|
938
|
+
operator,
|
|
939
|
+
left: expr,
|
|
940
|
+
right,
|
|
941
|
+
loc: _createLoc(startToken, endToken),
|
|
942
|
+
};
|
|
943
|
+
}
|
|
944
|
+
return expr;
|
|
945
|
+
}
|
|
946
|
+
/**
|
|
947
|
+
* Parse IS NULL / IS NOT NULL expressions
|
|
948
|
+
*/
|
|
949
|
+
isNull() {
|
|
950
|
+
const expr = this.equality();
|
|
951
|
+
const startToken = expr.loc ? this.tokens.find(t => t.startOffset === expr.loc.start.offset) ?? this.peek() : this.peek(); // Get start token of left expr
|
|
952
|
+
if (this.match(TokenType.IS)) {
|
|
953
|
+
let isNot = false;
|
|
954
|
+
if (this.match(TokenType.NOT)) {
|
|
955
|
+
isNot = true;
|
|
956
|
+
}
|
|
957
|
+
if (this.match(TokenType.NULL)) {
|
|
958
|
+
const endToken = this.previous(); // End token is NULL
|
|
959
|
+
const operator = isNot ? 'IS NOT NULL' : 'IS NULL';
|
|
960
|
+
// Represent IS NULL / IS NOT NULL as UnaryExpr for simplicity
|
|
961
|
+
return { type: 'unary', operator, expr, loc: _createLoc(startToken, endToken) };
|
|
962
|
+
}
|
|
963
|
+
// If it was IS or IS NOT but not followed by NULL, maybe it's IS TRUE/FALSE/DISTINCT FROM?
|
|
964
|
+
// For now, assume standard comparison if NULL doesn't follow IS [NOT]
|
|
965
|
+
// We need to "unread" the IS and optional NOT token if we didn't match NULL.
|
|
966
|
+
// Backtrack current position.
|
|
967
|
+
if (isNot)
|
|
968
|
+
this.current--; // Backtrack NOT
|
|
969
|
+
this.current--; // Backtrack IS
|
|
970
|
+
}
|
|
971
|
+
return expr;
|
|
972
|
+
}
|
|
973
|
+
/**
|
|
974
|
+
* Parse equality expression
|
|
975
|
+
*/
|
|
976
|
+
equality() {
|
|
977
|
+
let expr = this.comparison();
|
|
978
|
+
const startToken = expr.loc ? this.tokens.find(t => t.startOffset === expr.loc.start.offset) ?? this.peek() : this.peek(); // Get start token of left expr
|
|
979
|
+
while (this.match(TokenType.EQUAL, TokenType.EQUAL_EQUAL, TokenType.NOT_EQUAL)) {
|
|
980
|
+
let operator;
|
|
981
|
+
switch (this.previous().type) {
|
|
982
|
+
case TokenType.NOT_EQUAL:
|
|
983
|
+
operator = '!=';
|
|
984
|
+
break;
|
|
985
|
+
case TokenType.EQUAL_EQUAL:
|
|
986
|
+
operator = '==';
|
|
987
|
+
break;
|
|
988
|
+
default:
|
|
989
|
+
operator = '=';
|
|
990
|
+
break;
|
|
991
|
+
}
|
|
992
|
+
const right = this.comparison();
|
|
993
|
+
const endToken = this.previous(); // End token is end of right expr
|
|
994
|
+
expr = {
|
|
995
|
+
type: 'binary',
|
|
996
|
+
operator,
|
|
997
|
+
left: expr,
|
|
998
|
+
right,
|
|
999
|
+
loc: _createLoc(startToken, endToken),
|
|
1000
|
+
};
|
|
1001
|
+
}
|
|
1002
|
+
return expr;
|
|
1003
|
+
}
|
|
1004
|
+
/**
|
|
1005
|
+
* Parse comparison expression
|
|
1006
|
+
*/
|
|
1007
|
+
comparison() {
|
|
1008
|
+
let expr = this.term();
|
|
1009
|
+
const startToken = expr.loc ? this.tokens.find(t => t.startOffset === expr.loc.start.offset) ?? this.peek() : this.peek(); // Get start token of left expr
|
|
1010
|
+
while (this.match(TokenType.LESS, TokenType.LESS_EQUAL, TokenType.GREATER, TokenType.GREATER_EQUAL, TokenType.BETWEEN, TokenType.IN, TokenType.NOT, TokenType.LIKE)) {
|
|
1011
|
+
const operatorToken = this.previous();
|
|
1012
|
+
// Handle NOT IN, NOT BETWEEN, and NOT LIKE
|
|
1013
|
+
if (operatorToken.type === TokenType.NOT) {
|
|
1014
|
+
const notStartToken = operatorToken;
|
|
1015
|
+
if (this.match(TokenType.IN)) {
|
|
1016
|
+
// NOT IN
|
|
1017
|
+
this.consume(TokenType.LPAREN, "Expected '(' after NOT IN.");
|
|
1018
|
+
if (this.check(TokenType.SELECT)) {
|
|
1019
|
+
// NOT IN subquery: expr NOT IN (SELECT ...)
|
|
1020
|
+
const selectToken = this.advance(); // Consume SELECT
|
|
1021
|
+
const subquery = this.selectStatement(selectToken);
|
|
1022
|
+
const endToken = this.consume(TokenType.RPAREN, "Expected ')' after NOT IN subquery.");
|
|
1023
|
+
// Create an IN expression with subquery, then wrap in NOT
|
|
1024
|
+
const inExpr = {
|
|
1025
|
+
type: 'in',
|
|
1026
|
+
expr,
|
|
1027
|
+
subquery,
|
|
1028
|
+
loc: _createLoc(startToken, endToken),
|
|
1029
|
+
};
|
|
1030
|
+
expr = {
|
|
1031
|
+
type: 'unary',
|
|
1032
|
+
operator: 'NOT',
|
|
1033
|
+
expr: inExpr,
|
|
1034
|
+
loc: _createLoc(notStartToken, endToken),
|
|
1035
|
+
};
|
|
1036
|
+
}
|
|
1037
|
+
else {
|
|
1038
|
+
// NOT IN value list: expr NOT IN (value1, value2, ...)
|
|
1039
|
+
const values = [];
|
|
1040
|
+
if (!this.check(TokenType.RPAREN)) {
|
|
1041
|
+
do {
|
|
1042
|
+
values.push(this.expression());
|
|
1043
|
+
} while (this.match(TokenType.COMMA));
|
|
1044
|
+
}
|
|
1045
|
+
const endToken = this.consume(TokenType.RPAREN, "Expected ')' after NOT IN values.");
|
|
1046
|
+
// Create an IN expression with value list, then wrap in NOT
|
|
1047
|
+
const inExpr = {
|
|
1048
|
+
type: 'in',
|
|
1049
|
+
expr,
|
|
1050
|
+
values,
|
|
1051
|
+
loc: _createLoc(startToken, endToken),
|
|
1052
|
+
};
|
|
1053
|
+
expr = {
|
|
1054
|
+
type: 'unary',
|
|
1055
|
+
operator: 'NOT',
|
|
1056
|
+
expr: inExpr,
|
|
1057
|
+
loc: _createLoc(notStartToken, endToken),
|
|
1058
|
+
};
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
else if (this.match(TokenType.BETWEEN)) {
|
|
1062
|
+
// NOT BETWEEN
|
|
1063
|
+
const low = this.term();
|
|
1064
|
+
this.consume(TokenType.AND, "Expected 'AND' after NOT BETWEEN lower bound.");
|
|
1065
|
+
const high = this.term();
|
|
1066
|
+
const endToken = this.previous(); // End token is end of high expr
|
|
1067
|
+
// Create a binary AND expression for the bounds
|
|
1068
|
+
const boundsExpr = {
|
|
1069
|
+
type: 'binary',
|
|
1070
|
+
operator: 'AND',
|
|
1071
|
+
left: low,
|
|
1072
|
+
right: high,
|
|
1073
|
+
loc: _createLoc(low.loc?.start ? this.tokens.find(t => t.startOffset === low.loc.start.offset) ?? this.peek() : this.peek(), endToken)
|
|
1074
|
+
};
|
|
1075
|
+
// Create the BETWEEN expression as a binary expression, then wrap in NOT
|
|
1076
|
+
const betweenExpr = {
|
|
1077
|
+
type: 'binary',
|
|
1078
|
+
operator: 'BETWEEN',
|
|
1079
|
+
left: expr,
|
|
1080
|
+
right: boundsExpr,
|
|
1081
|
+
loc: _createLoc(startToken, endToken),
|
|
1082
|
+
};
|
|
1083
|
+
expr = {
|
|
1084
|
+
type: 'unary',
|
|
1085
|
+
operator: 'NOT',
|
|
1086
|
+
expr: betweenExpr,
|
|
1087
|
+
loc: _createLoc(notStartToken, endToken),
|
|
1088
|
+
};
|
|
1089
|
+
}
|
|
1090
|
+
else if (this.match(TokenType.LIKE)) {
|
|
1091
|
+
// NOT LIKE
|
|
1092
|
+
const pattern = this.term();
|
|
1093
|
+
const endToken = this.previous(); // End token is end of pattern expr
|
|
1094
|
+
// Create the LIKE expression as a binary expression, then wrap in NOT
|
|
1095
|
+
const likeExpr = {
|
|
1096
|
+
type: 'binary',
|
|
1097
|
+
operator: 'LIKE',
|
|
1098
|
+
left: expr,
|
|
1099
|
+
right: pattern,
|
|
1100
|
+
loc: _createLoc(startToken, endToken),
|
|
1101
|
+
};
|
|
1102
|
+
expr = {
|
|
1103
|
+
type: 'unary',
|
|
1104
|
+
operator: 'NOT',
|
|
1105
|
+
expr: likeExpr,
|
|
1106
|
+
loc: _createLoc(notStartToken, endToken),
|
|
1107
|
+
};
|
|
1108
|
+
}
|
|
1109
|
+
else {
|
|
1110
|
+
// Put back the NOT token and break out of the loop
|
|
1111
|
+
this.current--;
|
|
1112
|
+
break;
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
else if (operatorToken.type === TokenType.LIKE) {
|
|
1116
|
+
// Parse LIKE expression: expr LIKE pattern
|
|
1117
|
+
const pattern = this.term();
|
|
1118
|
+
const endToken = this.previous(); // End token is end of pattern expr
|
|
1119
|
+
// Create the LIKE expression as a binary expression
|
|
1120
|
+
expr = {
|
|
1121
|
+
type: 'binary',
|
|
1122
|
+
operator: 'LIKE',
|
|
1123
|
+
left: expr,
|
|
1124
|
+
right: pattern,
|
|
1125
|
+
loc: _createLoc(startToken, endToken),
|
|
1126
|
+
};
|
|
1127
|
+
}
|
|
1128
|
+
else if (operatorToken.type === TokenType.BETWEEN) {
|
|
1129
|
+
// Parse BETWEEN expression: expr BETWEEN low AND high
|
|
1130
|
+
const low = this.term();
|
|
1131
|
+
this.consume(TokenType.AND, "Expected 'AND' after BETWEEN lower bound.");
|
|
1132
|
+
const high = this.term();
|
|
1133
|
+
const endToken = this.previous(); // End token is end of high expr
|
|
1134
|
+
// Create a binary AND expression for the bounds
|
|
1135
|
+
const boundsExpr = {
|
|
1136
|
+
type: 'binary',
|
|
1137
|
+
operator: 'AND',
|
|
1138
|
+
left: low,
|
|
1139
|
+
right: high,
|
|
1140
|
+
loc: _createLoc(low.loc?.start ? this.tokens.find(t => t.startOffset === low.loc.start.offset) ?? this.peek() : this.peek(), endToken)
|
|
1141
|
+
};
|
|
1142
|
+
// Create the BETWEEN expression as a binary expression
|
|
1143
|
+
expr = {
|
|
1144
|
+
type: 'binary',
|
|
1145
|
+
operator: 'BETWEEN',
|
|
1146
|
+
left: expr,
|
|
1147
|
+
right: boundsExpr,
|
|
1148
|
+
loc: _createLoc(startToken, endToken),
|
|
1149
|
+
};
|
|
1150
|
+
}
|
|
1151
|
+
else if (operatorToken.type === TokenType.IN) {
|
|
1152
|
+
// Parse IN expression: expr IN (value1, value2, ...) or expr IN (subquery)
|
|
1153
|
+
this.consume(TokenType.LPAREN, "Expected '(' after IN.");
|
|
1154
|
+
// Check if this is a subquery or value list
|
|
1155
|
+
if (this.check(TokenType.SELECT)) {
|
|
1156
|
+
// IN subquery: expr IN (SELECT ...)
|
|
1157
|
+
const selectToken = this.advance(); // Consume SELECT
|
|
1158
|
+
const subquery = this.selectStatement(selectToken);
|
|
1159
|
+
const endToken = this.consume(TokenType.RPAREN, "Expected ')' after IN subquery.");
|
|
1160
|
+
// Create an IN expression with subquery
|
|
1161
|
+
expr = {
|
|
1162
|
+
type: 'in',
|
|
1163
|
+
expr,
|
|
1164
|
+
subquery,
|
|
1165
|
+
loc: _createLoc(startToken, endToken),
|
|
1166
|
+
};
|
|
1167
|
+
}
|
|
1168
|
+
else {
|
|
1169
|
+
// IN value list: expr IN (value1, value2, ...)
|
|
1170
|
+
const values = [];
|
|
1171
|
+
if (!this.check(TokenType.RPAREN)) {
|
|
1172
|
+
do {
|
|
1173
|
+
values.push(this.expression());
|
|
1174
|
+
} while (this.match(TokenType.COMMA));
|
|
1175
|
+
}
|
|
1176
|
+
const endToken = this.consume(TokenType.RPAREN, "Expected ')' after IN values.");
|
|
1177
|
+
// Create an IN expression with value list
|
|
1178
|
+
expr = {
|
|
1179
|
+
type: 'in',
|
|
1180
|
+
expr,
|
|
1181
|
+
values,
|
|
1182
|
+
loc: _createLoc(startToken, endToken),
|
|
1183
|
+
};
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
else {
|
|
1187
|
+
// Handle other comparison operators
|
|
1188
|
+
let operator;
|
|
1189
|
+
switch (operatorToken.type) {
|
|
1190
|
+
case TokenType.LESS:
|
|
1191
|
+
operator = '<';
|
|
1192
|
+
break;
|
|
1193
|
+
case TokenType.LESS_EQUAL:
|
|
1194
|
+
operator = '<=';
|
|
1195
|
+
break;
|
|
1196
|
+
case TokenType.GREATER:
|
|
1197
|
+
operator = '>';
|
|
1198
|
+
break;
|
|
1199
|
+
case TokenType.GREATER_EQUAL:
|
|
1200
|
+
operator = '>=';
|
|
1201
|
+
break;
|
|
1202
|
+
default: operator = '?';
|
|
1203
|
+
}
|
|
1204
|
+
const right = this.term();
|
|
1205
|
+
const endToken = this.previous(); // End token is end of right expr
|
|
1206
|
+
expr = {
|
|
1207
|
+
type: 'binary',
|
|
1208
|
+
operator,
|
|
1209
|
+
left: expr,
|
|
1210
|
+
right,
|
|
1211
|
+
loc: _createLoc(startToken, endToken),
|
|
1212
|
+
};
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
return expr;
|
|
1216
|
+
}
|
|
1217
|
+
/**
|
|
1218
|
+
* Parse addition and subtraction
|
|
1219
|
+
*/
|
|
1220
|
+
term() {
|
|
1221
|
+
let expr = this.factor();
|
|
1222
|
+
const startToken = expr.loc ? this.tokens.find(t => t.startOffset === expr.loc.start.offset) ?? this.peek() : this.peek(); // Get start token of left expr
|
|
1223
|
+
while (this.match(TokenType.PLUS, TokenType.MINUS)) {
|
|
1224
|
+
const operator = this.previous().type === TokenType.PLUS ? '+' : '-';
|
|
1225
|
+
const right = this.factor();
|
|
1226
|
+
const endToken = this.previous(); // End token is end of right expr
|
|
1227
|
+
expr = {
|
|
1228
|
+
type: 'binary',
|
|
1229
|
+
operator,
|
|
1230
|
+
left: expr,
|
|
1231
|
+
right,
|
|
1232
|
+
loc: _createLoc(startToken, endToken),
|
|
1233
|
+
};
|
|
1234
|
+
}
|
|
1235
|
+
return expr;
|
|
1236
|
+
}
|
|
1237
|
+
/**
|
|
1238
|
+
* Parse multiplication and division
|
|
1239
|
+
*/
|
|
1240
|
+
factor() {
|
|
1241
|
+
// First, handle unary operators
|
|
1242
|
+
if (this.match(TokenType.MINUS, TokenType.PLUS, TokenType.TILDE, TokenType.NOT)) { // Added NOT
|
|
1243
|
+
const operatorToken = this.previous();
|
|
1244
|
+
const operatorStartToken = operatorToken; // Start token is the operator itself
|
|
1245
|
+
const operator = operatorToken.lexeme;
|
|
1246
|
+
// Unary operator applies to the result of the *next* precedence level (concatenation)
|
|
1247
|
+
const right = this.concatenation(); // Should call concatenation (higher precedence than factor)
|
|
1248
|
+
const endToken = this.previous(); // End token is end of the operand
|
|
1249
|
+
return { type: 'unary', operator, expr: right, loc: _createLoc(operatorStartToken, endToken) };
|
|
1250
|
+
}
|
|
1251
|
+
let expr = this.concatenation(); // Factor operands have higher precedence (concatenation)
|
|
1252
|
+
const startToken = expr.loc ? this.tokens.find(t => t.startOffset === expr.loc.start.offset) ?? this.peek() : this.peek(); // Get start token of left expr
|
|
1253
|
+
while (this.match(TokenType.ASTERISK, TokenType.SLASH, TokenType.PERCENT)) {
|
|
1254
|
+
const operatorToken = this.previous();
|
|
1255
|
+
const operator = operatorToken.lexeme;
|
|
1256
|
+
const right = this.concatenation(); // Factor operands have higher precedence (concatenation)
|
|
1257
|
+
const endToken = this.previous(); // End token is end of right expr
|
|
1258
|
+
expr = { type: 'binary', operator, left: expr, right, loc: _createLoc(startToken, endToken) };
|
|
1259
|
+
}
|
|
1260
|
+
return expr;
|
|
1261
|
+
}
|
|
1262
|
+
/**
|
|
1263
|
+
* Parse concatenation expression (||)
|
|
1264
|
+
*/
|
|
1265
|
+
concatenation() {
|
|
1266
|
+
let expr = this.collateExpression(); // Concatenation operands have higher precedence (collate)
|
|
1267
|
+
const startToken = expr.loc ? this.tokens.find(t => t.startOffset === expr.loc.start.offset) ?? this.peek() : this.peek(); // Get start token of left expr
|
|
1268
|
+
while (this.match(TokenType.PIPE_PIPE)) {
|
|
1269
|
+
const operator = '||';
|
|
1270
|
+
const right = this.collateExpression(); // Concatenation operands have higher precedence (collate)
|
|
1271
|
+
const endToken = this.previous(); // End token is end of right expr
|
|
1272
|
+
expr = {
|
|
1273
|
+
type: 'binary',
|
|
1274
|
+
operator,
|
|
1275
|
+
left: expr,
|
|
1276
|
+
right,
|
|
1277
|
+
loc: _createLoc(startToken, endToken),
|
|
1278
|
+
};
|
|
1279
|
+
}
|
|
1280
|
+
return expr;
|
|
1281
|
+
}
|
|
1282
|
+
/**
|
|
1283
|
+
* Parse COLLATE expression
|
|
1284
|
+
*/
|
|
1285
|
+
collateExpression() {
|
|
1286
|
+
const expr = this.primary(); // Parse primary expression first
|
|
1287
|
+
if (this.matchKeyword('COLLATE')) {
|
|
1288
|
+
const collationToken = this.consume(TokenType.IDENTIFIER, "Expected collation name after COLLATE.");
|
|
1289
|
+
const collation = collationToken.lexeme;
|
|
1290
|
+
// Use the start of the original expression and end of collation name for location
|
|
1291
|
+
const startLocToken = expr.loc?.start ? this.tokens.find(t => t.startOffset === expr.loc.start.offset) ?? this.peek() : this.peek();
|
|
1292
|
+
return { type: 'collate', expr, collation, loc: _createLoc(startLocToken, collationToken) };
|
|
1293
|
+
}
|
|
1294
|
+
return expr;
|
|
1295
|
+
}
|
|
1296
|
+
/**
|
|
1297
|
+
* Parse primary expressions (literals, identifiers, etc.)
|
|
1298
|
+
*/
|
|
1299
|
+
primary() {
|
|
1300
|
+
const startToken = this.peek();
|
|
1301
|
+
// Case expression
|
|
1302
|
+
if (this.matchKeyword('CASE')) {
|
|
1303
|
+
return this.parseCaseExpression(startToken);
|
|
1304
|
+
}
|
|
1305
|
+
// CAST expression: CAST(expr AS type)
|
|
1306
|
+
if (this.peekKeyword('CAST') && this.checkNext(1, TokenType.LPAREN)) {
|
|
1307
|
+
const castToken = this.advance(); // Consume CAST
|
|
1308
|
+
this.consume(TokenType.LPAREN, "Expected '(' after CAST.");
|
|
1309
|
+
const expr = this.expression();
|
|
1310
|
+
this.consumeKeyword('AS', "Expected 'AS' in CAST expression.");
|
|
1311
|
+
// Allow type names that might be keywords (e.g., TEXT, INTEGER, REAL, BLOB)
|
|
1312
|
+
// or multi-word type names if supported (e.g., "VARCHAR(255)") - for now, simple identifier
|
|
1313
|
+
if (!this.check(TokenType.IDENTIFIER) &&
|
|
1314
|
+
!this.isTypeNameKeyword(this.peek().lexeme.toUpperCase())) {
|
|
1315
|
+
throw this.error(this.peek(), "Expected type name after 'AS' in CAST expression.");
|
|
1316
|
+
}
|
|
1317
|
+
const typeToken = this.advance(); // Consume type name
|
|
1318
|
+
const targetType = typeToken.lexeme;
|
|
1319
|
+
const endToken = this.consume(TokenType.RPAREN, "Expected ')' after CAST expression type.");
|
|
1320
|
+
return { type: 'cast', expr, targetType, loc: _createLoc(castToken, endToken) };
|
|
1321
|
+
}
|
|
1322
|
+
// EXISTS expression: EXISTS(SELECT ...)
|
|
1323
|
+
if (this.match(TokenType.EXISTS)) {
|
|
1324
|
+
const existsToken = this.previous();
|
|
1325
|
+
this.consume(TokenType.LPAREN, "Expected '(' after EXISTS.");
|
|
1326
|
+
const selectToken = this.consume(TokenType.SELECT, "Expected 'SELECT' in EXISTS subquery.");
|
|
1327
|
+
const subquery = this.selectStatement(selectToken);
|
|
1328
|
+
const endToken = this.consume(TokenType.RPAREN, "Expected ')' after EXISTS subquery.");
|
|
1329
|
+
return {
|
|
1330
|
+
type: 'exists',
|
|
1331
|
+
subquery,
|
|
1332
|
+
loc: _createLoc(existsToken, endToken)
|
|
1333
|
+
};
|
|
1334
|
+
}
|
|
1335
|
+
// Literals
|
|
1336
|
+
if (this.match(TokenType.INTEGER, TokenType.FLOAT, TokenType.STRING, TokenType.NULL, TokenType.TRUE, TokenType.FALSE, TokenType.BLOB)) {
|
|
1337
|
+
const token = this.previous();
|
|
1338
|
+
let value;
|
|
1339
|
+
let lexeme = undefined;
|
|
1340
|
+
if (token.type === TokenType.NULL) {
|
|
1341
|
+
value = null;
|
|
1342
|
+
lexeme = token.lexeme; // Store original case (NULL vs null)
|
|
1343
|
+
}
|
|
1344
|
+
else if (token.type === TokenType.TRUE) {
|
|
1345
|
+
value = true;
|
|
1346
|
+
lexeme = token.lexeme; // Store original case (TRUE vs true)
|
|
1347
|
+
}
|
|
1348
|
+
else if (token.type === TokenType.FALSE) {
|
|
1349
|
+
value = false;
|
|
1350
|
+
lexeme = token.lexeme; // Store original case (FALSE vs false)
|
|
1351
|
+
}
|
|
1352
|
+
else if (token.type === TokenType.FLOAT) {
|
|
1353
|
+
// For FLOAT, parse the literal (which is the original string)
|
|
1354
|
+
value = parseFloat(token.literal);
|
|
1355
|
+
lexeme = token.literal; // Store original string as lexeme
|
|
1356
|
+
}
|
|
1357
|
+
else if (token.type === TokenType.INTEGER) {
|
|
1358
|
+
value = token.literal; // Already number or BigInt
|
|
1359
|
+
if (token.lexeme !== String(value)) { // Store lexeme only if different
|
|
1360
|
+
lexeme = token.lexeme;
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
else {
|
|
1364
|
+
value = token.literal; // STRING, BLOB
|
|
1365
|
+
}
|
|
1366
|
+
const node = { type: 'literal', value, loc: _createLoc(startToken, token) };
|
|
1367
|
+
if (lexeme !== undefined) {
|
|
1368
|
+
node.lexeme = lexeme;
|
|
1369
|
+
}
|
|
1370
|
+
return node;
|
|
1371
|
+
}
|
|
1372
|
+
// Parameter expressions (?, :name, $name)
|
|
1373
|
+
if (this.match(TokenType.QUESTION)) {
|
|
1374
|
+
const token = this.previous();
|
|
1375
|
+
return { type: 'parameter', index: this.parameterPosition++, loc: _createLoc(startToken, token) };
|
|
1376
|
+
}
|
|
1377
|
+
if (this.match(TokenType.COLON, TokenType.DOLLAR)) {
|
|
1378
|
+
// Named parameter (can be identifier like :name or integer like :1)
|
|
1379
|
+
if (!this.check(TokenType.IDENTIFIER) && !this.check(TokenType.INTEGER)) {
|
|
1380
|
+
throw this.error(this.peek(), "Expected identifier or number after parameter prefix.");
|
|
1381
|
+
}
|
|
1382
|
+
const nameToken = this.advance();
|
|
1383
|
+
return { type: 'parameter', name: nameToken.lexeme, loc: _createLoc(startToken, nameToken) };
|
|
1384
|
+
}
|
|
1385
|
+
// Function call (with optional window function support)
|
|
1386
|
+
if (this.checkIdentifierLike(['key', 'action', 'set', 'default', 'check', 'unique', 'references', 'on', 'cascade', 'restrict', 'like']) && this.checkNext(1, TokenType.LPAREN)) {
|
|
1387
|
+
const name = this.consumeIdentifier(['key', 'action', 'set', 'default', 'check', 'unique', 'references', 'on', 'cascade', 'restrict', 'like'], "Expected function name.");
|
|
1388
|
+
this.consume(TokenType.LPAREN, "Expected '(' after function name.");
|
|
1389
|
+
const args = [];
|
|
1390
|
+
let distinct = false;
|
|
1391
|
+
if (!this.check(TokenType.RPAREN)) {
|
|
1392
|
+
// Handle DISTINCT inside function calls like COUNT(DISTINCT col)
|
|
1393
|
+
distinct = this.matchKeyword('DISTINCT');
|
|
1394
|
+
// Handle * argument AFTER checking for distinct
|
|
1395
|
+
if (this.match(TokenType.ASTERISK)) {
|
|
1396
|
+
// Do not add '*' as an argument to the list for aggregates like COUNT(*)
|
|
1397
|
+
if (args.length > 0 || distinct) {
|
|
1398
|
+
// '*' is only valid as the *only* argument, potentially after DISTINCT
|
|
1399
|
+
// e.g. COUNT(*), COUNT(DISTINCT *) - though DISTINCT * might not be standard SQL?
|
|
1400
|
+
// For now, disallow '*' if other args exist.
|
|
1401
|
+
throw this.error(this.previous(), "'*' cannot be used with other arguments in function call.");
|
|
1402
|
+
}
|
|
1403
|
+
// If we parsed '*', the args list remains empty.
|
|
1404
|
+
}
|
|
1405
|
+
else {
|
|
1406
|
+
// Parse regular arguments if '*' wasn't found
|
|
1407
|
+
do {
|
|
1408
|
+
args.push(this.expression());
|
|
1409
|
+
} while (this.match(TokenType.COMMA));
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
const endToken = this.consume(TokenType.RPAREN, "Expected ')' after function arguments.");
|
|
1413
|
+
const funcExpr = {
|
|
1414
|
+
type: 'function',
|
|
1415
|
+
name,
|
|
1416
|
+
args,
|
|
1417
|
+
loc: _createLoc(startToken, endToken)
|
|
1418
|
+
};
|
|
1419
|
+
// Add distinct field if it was parsed
|
|
1420
|
+
if (distinct) {
|
|
1421
|
+
funcExpr.distinct = true;
|
|
1422
|
+
}
|
|
1423
|
+
// Check for OVER clause (window function)
|
|
1424
|
+
if (this.matchKeyword('OVER')) {
|
|
1425
|
+
const window = this.parseWindowSpecification();
|
|
1426
|
+
const overEndToken = this.previous();
|
|
1427
|
+
return {
|
|
1428
|
+
type: 'windowFunction',
|
|
1429
|
+
function: funcExpr,
|
|
1430
|
+
window,
|
|
1431
|
+
loc: _createLoc(startToken, overEndToken)
|
|
1432
|
+
};
|
|
1433
|
+
}
|
|
1434
|
+
return funcExpr;
|
|
1435
|
+
}
|
|
1436
|
+
// Column/identifier expressions
|
|
1437
|
+
const contextualKeywords = ['key', 'action', 'set', 'default', 'check', 'unique', 'references', 'on', 'cascade', 'restrict', 'like'];
|
|
1438
|
+
if (this.checkIdentifierLike(contextualKeywords)) {
|
|
1439
|
+
// Schema.table.column
|
|
1440
|
+
if (this.checkNext(1, TokenType.DOT) && this.checkIdentifierLikeAt(2, contextualKeywords) &&
|
|
1441
|
+
this.checkNext(3, TokenType.DOT) && this.checkIdentifierLikeAt(4, contextualKeywords)) {
|
|
1442
|
+
const schema = this.consumeIdentifier(contextualKeywords, "Expected schema name.");
|
|
1443
|
+
this.advance(); // Consume DOT
|
|
1444
|
+
const table = this.consumeIdentifier(contextualKeywords, "Expected table name.");
|
|
1445
|
+
this.advance(); // Consume DOT
|
|
1446
|
+
const name = this.consumeIdentifier(contextualKeywords, "Expected column name.");
|
|
1447
|
+
const nameToken = this.previous();
|
|
1448
|
+
return {
|
|
1449
|
+
type: 'column',
|
|
1450
|
+
name,
|
|
1451
|
+
table,
|
|
1452
|
+
schema,
|
|
1453
|
+
loc: _createLoc(startToken, nameToken),
|
|
1454
|
+
};
|
|
1455
|
+
}
|
|
1456
|
+
// table.column
|
|
1457
|
+
else if (this.checkNext(1, TokenType.DOT) && this.checkIdentifierLikeAt(2, contextualKeywords)) {
|
|
1458
|
+
const table = this.consumeIdentifier(contextualKeywords, "Expected table name.");
|
|
1459
|
+
this.advance(); // Consume DOT
|
|
1460
|
+
const name = this.consumeIdentifier(contextualKeywords, "Expected column name.");
|
|
1461
|
+
const nameToken = this.previous();
|
|
1462
|
+
return {
|
|
1463
|
+
type: 'column',
|
|
1464
|
+
name,
|
|
1465
|
+
table,
|
|
1466
|
+
loc: _createLoc(startToken, nameToken),
|
|
1467
|
+
};
|
|
1468
|
+
}
|
|
1469
|
+
// just column
|
|
1470
|
+
else {
|
|
1471
|
+
const name = this.consumeIdentifier(contextualKeywords, "Expected column name.");
|
|
1472
|
+
const nameToken = this.previous();
|
|
1473
|
+
return {
|
|
1474
|
+
type: 'column',
|
|
1475
|
+
name,
|
|
1476
|
+
loc: _createLoc(startToken, nameToken),
|
|
1477
|
+
};
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
// Parenthesized expression or scalar subquery
|
|
1481
|
+
if (this.match(TokenType.LPAREN)) {
|
|
1482
|
+
// Look ahead to see if this is a scalar subquery (SELECT ...)
|
|
1483
|
+
if (this.check(TokenType.SELECT)) {
|
|
1484
|
+
const selectToken = this.consume(TokenType.SELECT, "Expected 'SELECT' in subquery.");
|
|
1485
|
+
const subquery = this.selectStatement(selectToken);
|
|
1486
|
+
this.consume(TokenType.RPAREN, "Expected ')' after subquery.");
|
|
1487
|
+
return {
|
|
1488
|
+
type: 'subquery',
|
|
1489
|
+
query: subquery,
|
|
1490
|
+
loc: _createLoc(startToken, this.previous())
|
|
1491
|
+
};
|
|
1492
|
+
}
|
|
1493
|
+
else {
|
|
1494
|
+
// Regular parenthesized expression
|
|
1495
|
+
const expr = this.expression();
|
|
1496
|
+
this.consume(TokenType.RPAREN, "Expected ')' after expression.");
|
|
1497
|
+
return expr;
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
throw this.error(this.peek(), "Expected expression.");
|
|
1501
|
+
}
|
|
1502
|
+
/**
|
|
1503
|
+
* Parses a window specification: (PARTITION BY ... ORDER BY ... [frame])
|
|
1504
|
+
*/
|
|
1505
|
+
parseWindowSpecification() {
|
|
1506
|
+
if (this.match(TokenType.LPAREN)) {
|
|
1507
|
+
let partitionBy;
|
|
1508
|
+
let orderBy;
|
|
1509
|
+
let frame;
|
|
1510
|
+
if (this.matchKeyword('PARTITION')) {
|
|
1511
|
+
this.consumeKeyword('BY', "Expected 'BY' after 'PARTITION'.");
|
|
1512
|
+
partitionBy = [];
|
|
1513
|
+
do {
|
|
1514
|
+
partitionBy.push(this.expression());
|
|
1515
|
+
} while (this.match(TokenType.COMMA));
|
|
1516
|
+
}
|
|
1517
|
+
if (this.matchKeyword('ORDER')) {
|
|
1518
|
+
this.consumeKeyword('BY', "Expected 'BY' after 'ORDER'.");
|
|
1519
|
+
orderBy = [];
|
|
1520
|
+
do {
|
|
1521
|
+
const expr = this.expression();
|
|
1522
|
+
const direction = this.match(TokenType.DESC) ? 'desc' : (this.match(TokenType.ASC) ? 'asc' : 'asc');
|
|
1523
|
+
// Handle NULLS FIRST/LAST
|
|
1524
|
+
let nullsOrdering;
|
|
1525
|
+
if (this.matchKeyword('NULLS')) {
|
|
1526
|
+
if (this.matchKeyword('FIRST')) {
|
|
1527
|
+
nullsOrdering = 'first';
|
|
1528
|
+
}
|
|
1529
|
+
else if (this.matchKeyword('LAST')) {
|
|
1530
|
+
nullsOrdering = 'last';
|
|
1531
|
+
}
|
|
1532
|
+
else {
|
|
1533
|
+
throw this.error(this.peek(), "Expected 'FIRST' or 'LAST' after 'NULLS'.");
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
const orderClause = { expr, direction };
|
|
1537
|
+
if (nullsOrdering) {
|
|
1538
|
+
orderClause.nullsOrdering = nullsOrdering;
|
|
1539
|
+
}
|
|
1540
|
+
orderBy.push(orderClause);
|
|
1541
|
+
} while (this.match(TokenType.COMMA));
|
|
1542
|
+
}
|
|
1543
|
+
// Frame clause (ROWS|RANGE ...)
|
|
1544
|
+
if (this.matchKeyword('ROWS') || this.matchKeyword('RANGE')) {
|
|
1545
|
+
const frameType = this.previous().lexeme.toLowerCase();
|
|
1546
|
+
// Handle both BETWEEN...AND and single bound syntax
|
|
1547
|
+
if (this.matchKeyword('BETWEEN')) {
|
|
1548
|
+
// ROWS BETWEEN start_bound AND end_bound
|
|
1549
|
+
const start = this.parseWindowFrameBound();
|
|
1550
|
+
this.consumeKeyword('AND', "Expected 'AND' after frame start bound.");
|
|
1551
|
+
const end = this.parseWindowFrameBound();
|
|
1552
|
+
frame = { type: frameType, start, end };
|
|
1553
|
+
}
|
|
1554
|
+
else {
|
|
1555
|
+
// ROWS start_bound (shorthand for ROWS BETWEEN start_bound AND CURRENT ROW)
|
|
1556
|
+
const start = this.parseWindowFrameBound();
|
|
1557
|
+
frame = { type: frameType, start, end: null };
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
this.consume(TokenType.RPAREN, "Expected ')' after window specification.");
|
|
1561
|
+
return { type: 'windowDefinition', partitionBy, orderBy, frame };
|
|
1562
|
+
}
|
|
1563
|
+
else {
|
|
1564
|
+
// Window name (not implemented)
|
|
1565
|
+
throw this.error(this.peek(), 'Window name references are not yet supported. Use explicit window specs.');
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
/**
|
|
1569
|
+
* Parses a window frame bound (UNBOUNDED PRECEDING, CURRENT ROW, n PRECEDING/FOLLOWING)
|
|
1570
|
+
*/
|
|
1571
|
+
parseWindowFrameBound() {
|
|
1572
|
+
if (this.matchKeyword('UNBOUNDED')) {
|
|
1573
|
+
if (this.matchKeyword('PRECEDING')) {
|
|
1574
|
+
return { type: 'unboundedPreceding' };
|
|
1575
|
+
}
|
|
1576
|
+
else if (this.matchKeyword('FOLLOWING')) {
|
|
1577
|
+
return { type: 'unboundedFollowing' };
|
|
1578
|
+
}
|
|
1579
|
+
else {
|
|
1580
|
+
throw this.error(this.peek(), "Expected PRECEDING or FOLLOWING after UNBOUNDED.");
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
else if (this.matchKeyword('CURRENT')) {
|
|
1584
|
+
this.consumeKeyword('ROW', "Expected 'ROW' after 'CURRENT'.");
|
|
1585
|
+
return { type: 'currentRow' };
|
|
1586
|
+
}
|
|
1587
|
+
else {
|
|
1588
|
+
const value = this.expression();
|
|
1589
|
+
if (this.matchKeyword('PRECEDING')) {
|
|
1590
|
+
return { type: 'preceding', value };
|
|
1591
|
+
}
|
|
1592
|
+
else if (this.matchKeyword('FOLLOWING')) {
|
|
1593
|
+
return { type: 'following', value };
|
|
1594
|
+
}
|
|
1595
|
+
else {
|
|
1596
|
+
throw this.error(this.peek(), "Expected PRECEDING or FOLLOWING after frame value.");
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
// Helper methods for token management
|
|
1601
|
+
match(...types) {
|
|
1602
|
+
for (const type of types) {
|
|
1603
|
+
if (this.check(type)) {
|
|
1604
|
+
this.advance();
|
|
1605
|
+
return true;
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
return false;
|
|
1609
|
+
}
|
|
1610
|
+
consume(type, message) {
|
|
1611
|
+
if (this.check(type)) {
|
|
1612
|
+
return this.advance();
|
|
1613
|
+
}
|
|
1614
|
+
this.error(this.peek(), message);
|
|
1615
|
+
}
|
|
1616
|
+
check(type) {
|
|
1617
|
+
if (this.isAtEnd())
|
|
1618
|
+
return false;
|
|
1619
|
+
return this.peek().type === type;
|
|
1620
|
+
}
|
|
1621
|
+
checkNext(n, type) {
|
|
1622
|
+
if (this.current + n >= this.tokens.length)
|
|
1623
|
+
return false;
|
|
1624
|
+
return this.tokens[this.current + n].type === type;
|
|
1625
|
+
}
|
|
1626
|
+
advance() {
|
|
1627
|
+
if (!this.isAtEnd())
|
|
1628
|
+
this.current++;
|
|
1629
|
+
return this.previous();
|
|
1630
|
+
}
|
|
1631
|
+
isAtEnd() {
|
|
1632
|
+
return this.peek().type === TokenType.EOF;
|
|
1633
|
+
}
|
|
1634
|
+
peek() {
|
|
1635
|
+
return this.tokens[this.current];
|
|
1636
|
+
}
|
|
1637
|
+
previous() {
|
|
1638
|
+
return this.tokens[this.current - 1];
|
|
1639
|
+
}
|
|
1640
|
+
error(token, message) {
|
|
1641
|
+
quereusError(message, StatusCode.ERROR, undefined, {
|
|
1642
|
+
loc: {
|
|
1643
|
+
start: {
|
|
1644
|
+
line: token.startLine,
|
|
1645
|
+
column: token.startColumn,
|
|
1646
|
+
},
|
|
1647
|
+
end: {
|
|
1648
|
+
line: token.endLine,
|
|
1649
|
+
column: token.endColumn,
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
});
|
|
1653
|
+
}
|
|
1654
|
+
isJoinToken() {
|
|
1655
|
+
return this.check(TokenType.JOIN) ||
|
|
1656
|
+
this.check(TokenType.INNER) ||
|
|
1657
|
+
this.check(TokenType.LEFT) ||
|
|
1658
|
+
this.check(TokenType.RIGHT) ||
|
|
1659
|
+
this.check(TokenType.FULL) ||
|
|
1660
|
+
this.check(TokenType.CROSS);
|
|
1661
|
+
}
|
|
1662
|
+
isEndOfClause() {
|
|
1663
|
+
const token = this.peek().type;
|
|
1664
|
+
return token === TokenType.FROM ||
|
|
1665
|
+
token === TokenType.WHERE ||
|
|
1666
|
+
token === TokenType.GROUP ||
|
|
1667
|
+
token === TokenType.HAVING ||
|
|
1668
|
+
token === TokenType.ORDER ||
|
|
1669
|
+
token === TokenType.LIMIT ||
|
|
1670
|
+
token === TokenType.UNION ||
|
|
1671
|
+
token === TokenType.SEMICOLON ||
|
|
1672
|
+
token === TokenType.EOF;
|
|
1673
|
+
}
|
|
1674
|
+
// --- Statement Parsing Stubs ---
|
|
1675
|
+
/** @internal */
|
|
1676
|
+
updateStatement(startToken, _withClause) {
|
|
1677
|
+
const table = this.tableIdentifier();
|
|
1678
|
+
this.consume(TokenType.SET, "Expected 'SET' after table name in UPDATE.");
|
|
1679
|
+
const assignments = [];
|
|
1680
|
+
do {
|
|
1681
|
+
const column = this.consumeIdentifier(['key', 'action', 'set', 'default', 'check', 'unique', 'references', 'on', 'cascade', 'restrict', 'like'], "Expected column name in SET clause.");
|
|
1682
|
+
this.consume(TokenType.EQUAL, "Expected '=' after column name in SET clause.");
|
|
1683
|
+
const value = this.expression();
|
|
1684
|
+
assignments.push({ column, value });
|
|
1685
|
+
} while (this.match(TokenType.COMMA));
|
|
1686
|
+
let where;
|
|
1687
|
+
if (this.match(TokenType.WHERE)) {
|
|
1688
|
+
where = this.expression();
|
|
1689
|
+
}
|
|
1690
|
+
// Parse RETURNING clause if present
|
|
1691
|
+
let returning;
|
|
1692
|
+
if (this.matchKeyword('RETURNING')) {
|
|
1693
|
+
returning = this.columnList();
|
|
1694
|
+
}
|
|
1695
|
+
const endToken = this.previous();
|
|
1696
|
+
return { type: 'update', table, assignments, where, returning, loc: _createLoc(startToken, endToken) };
|
|
1697
|
+
}
|
|
1698
|
+
/** @internal */
|
|
1699
|
+
deleteStatement(startToken, _withClause) {
|
|
1700
|
+
this.matchKeyword('FROM');
|
|
1701
|
+
const table = this.tableIdentifier();
|
|
1702
|
+
let where;
|
|
1703
|
+
if (this.match(TokenType.WHERE)) {
|
|
1704
|
+
where = this.expression();
|
|
1705
|
+
}
|
|
1706
|
+
// Parse RETURNING clause if present
|
|
1707
|
+
let returning;
|
|
1708
|
+
if (this.matchKeyword('RETURNING')) {
|
|
1709
|
+
returning = this.columnList();
|
|
1710
|
+
}
|
|
1711
|
+
const endToken = this.previous();
|
|
1712
|
+
return { type: 'delete', table, where, returning, loc: _createLoc(startToken, endToken) };
|
|
1713
|
+
}
|
|
1714
|
+
/** @internal */
|
|
1715
|
+
valuesStatement(startToken) {
|
|
1716
|
+
const values = [];
|
|
1717
|
+
do {
|
|
1718
|
+
this.consume(TokenType.LPAREN, "Expected '(' before values.");
|
|
1719
|
+
const valueList = [];
|
|
1720
|
+
if (!this.check(TokenType.RPAREN)) { // Check for empty value list
|
|
1721
|
+
do {
|
|
1722
|
+
valueList.push(this.expression());
|
|
1723
|
+
} while (this.match(TokenType.COMMA));
|
|
1724
|
+
}
|
|
1725
|
+
this.consume(TokenType.RPAREN, "Expected ')' after values.");
|
|
1726
|
+
values.push(valueList);
|
|
1727
|
+
} while (this.match(TokenType.COMMA));
|
|
1728
|
+
const endToken = this.previous();
|
|
1729
|
+
return { type: 'values', values, loc: _createLoc(startToken, endToken) };
|
|
1730
|
+
}
|
|
1731
|
+
/** @internal */
|
|
1732
|
+
createStatement(startToken, withClause) {
|
|
1733
|
+
if (this.peekKeyword('TABLE')) {
|
|
1734
|
+
this.consumeKeyword('TABLE', "Expected 'TABLE' after CREATE.");
|
|
1735
|
+
return this.createTableStatement(startToken, withClause);
|
|
1736
|
+
}
|
|
1737
|
+
else if (this.peekKeyword('INDEX')) {
|
|
1738
|
+
this.consumeKeyword('INDEX', "Expected 'INDEX' after CREATE.");
|
|
1739
|
+
return this.createIndexStatement(startToken, false, withClause);
|
|
1740
|
+
}
|
|
1741
|
+
else if (this.peekKeyword('VIEW')) {
|
|
1742
|
+
this.consumeKeyword('VIEW', "Expected 'VIEW' after CREATE.");
|
|
1743
|
+
return this.createViewStatement(startToken, withClause);
|
|
1744
|
+
}
|
|
1745
|
+
else if (this.peekKeyword('UNIQUE')) {
|
|
1746
|
+
this.consumeKeyword('UNIQUE', "Expected 'UNIQUE' after CREATE.");
|
|
1747
|
+
this.consumeKeyword('INDEX', "Expected 'INDEX' after CREATE UNIQUE.");
|
|
1748
|
+
return this.createIndexStatement(startToken, true, withClause);
|
|
1749
|
+
}
|
|
1750
|
+
throw this.error(this.peek(), "Expected TABLE, [UNIQUE] INDEX, VIEW, or VIRTUAL after CREATE.");
|
|
1751
|
+
}
|
|
1752
|
+
/**
|
|
1753
|
+
* Parse CREATE TABLE statement
|
|
1754
|
+
* @returns AST for CREATE TABLE
|
|
1755
|
+
*/
|
|
1756
|
+
createTableStatement(startToken, _withClause) {
|
|
1757
|
+
let isTemporary = false;
|
|
1758
|
+
if (this.peekKeyword('TEMP') || this.peekKeyword('TEMPORARY')) {
|
|
1759
|
+
isTemporary = true;
|
|
1760
|
+
this.advance();
|
|
1761
|
+
}
|
|
1762
|
+
let ifNotExists = false;
|
|
1763
|
+
if (this.matchKeyword('IF')) {
|
|
1764
|
+
this.consumeKeyword('NOT', "Expected 'NOT' after 'IF'.");
|
|
1765
|
+
this.consumeKeyword('EXISTS', "Expected 'EXISTS' after 'IF NOT'.");
|
|
1766
|
+
ifNotExists = true;
|
|
1767
|
+
}
|
|
1768
|
+
const table = this.tableIdentifier();
|
|
1769
|
+
const columns = [];
|
|
1770
|
+
const constraints = [];
|
|
1771
|
+
if (this.check(TokenType.LPAREN)) {
|
|
1772
|
+
this.consume(TokenType.LPAREN, "Expected '(' to start table definition.");
|
|
1773
|
+
do {
|
|
1774
|
+
if (this.peekKeyword('PRIMARY') || this.peekKeyword('UNIQUE') || this.peekKeyword('CHECK') || this.peekKeyword('FOREIGN') || this.peekKeyword('CONSTRAINT')) {
|
|
1775
|
+
constraints.push(this.tableConstraint());
|
|
1776
|
+
}
|
|
1777
|
+
else {
|
|
1778
|
+
columns.push(this.columnDefinition());
|
|
1779
|
+
}
|
|
1780
|
+
} while (this.match(TokenType.COMMA));
|
|
1781
|
+
this.consume(TokenType.RPAREN, "Expected ')' after table definition.");
|
|
1782
|
+
}
|
|
1783
|
+
else if (this.matchKeyword('AS')) {
|
|
1784
|
+
const token = this.previous();
|
|
1785
|
+
quereusError('CREATE TABLE AS SELECT is not supported.', StatusCode.UNSUPPORTED, undefined, { loc: { start: { line: token.startLine, column: token.startColumn } } });
|
|
1786
|
+
}
|
|
1787
|
+
else {
|
|
1788
|
+
throw this.error(this.peek(), "Expected '(' or 'AS' after table name.");
|
|
1789
|
+
}
|
|
1790
|
+
let moduleName;
|
|
1791
|
+
const moduleArgs = {};
|
|
1792
|
+
if (this.matchKeyword('USING')) {
|
|
1793
|
+
moduleName = this.consumeIdentifier("Expected module name after 'USING'.");
|
|
1794
|
+
if (this.matchKeyword('(')) {
|
|
1795
|
+
while (!this.match(TokenType.RPAREN)) {
|
|
1796
|
+
const nameValue = this.nameValueItem("module argument");
|
|
1797
|
+
moduleArgs[nameValue.name] = nameValue.value && nameValue.value.type === 'literal'
|
|
1798
|
+
? getSyncLiteral(nameValue.value) : nameValue.name;
|
|
1799
|
+
if (!this.match(TokenType.COMMA) || this.check(TokenType.RPAREN)) {
|
|
1800
|
+
throw this.error(this.peek(), "Expected ',' or ')' after module argument.");
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
return {
|
|
1806
|
+
type: 'createTable',
|
|
1807
|
+
table,
|
|
1808
|
+
ifNotExists,
|
|
1809
|
+
columns,
|
|
1810
|
+
constraints,
|
|
1811
|
+
isTemporary,
|
|
1812
|
+
moduleName,
|
|
1813
|
+
moduleArgs,
|
|
1814
|
+
loc: _createLoc(startToken, this.previous()),
|
|
1815
|
+
};
|
|
1816
|
+
}
|
|
1817
|
+
/**
|
|
1818
|
+
* Parse CREATE INDEX statement
|
|
1819
|
+
* @param isUnique Flag indicating if UNIQUE keyword was already parsed
|
|
1820
|
+
* @returns AST for CREATE INDEX
|
|
1821
|
+
*/
|
|
1822
|
+
createIndexStatement(startToken, isUnique = false, _withClause) {
|
|
1823
|
+
if (!isUnique && this.peekKeyword('UNIQUE')) {
|
|
1824
|
+
isUnique = true;
|
|
1825
|
+
this.advance();
|
|
1826
|
+
}
|
|
1827
|
+
let ifNotExists = false;
|
|
1828
|
+
if (this.matchKeyword('IF')) {
|
|
1829
|
+
this.consumeKeyword('NOT', "Expected 'NOT' after 'IF'.");
|
|
1830
|
+
this.consumeKeyword('EXISTS', "Expected 'EXISTS' after 'IF NOT'.");
|
|
1831
|
+
ifNotExists = true;
|
|
1832
|
+
}
|
|
1833
|
+
const index = this.tableIdentifier();
|
|
1834
|
+
this.consumeKeyword('ON', "Expected 'ON' after index name.");
|
|
1835
|
+
const table = this.tableIdentifier();
|
|
1836
|
+
this.consume(TokenType.LPAREN, "Expected '(' before indexed columns.");
|
|
1837
|
+
const columns = this.indexedColumnList();
|
|
1838
|
+
this.consume(TokenType.RPAREN, "Expected ')' after indexed columns.");
|
|
1839
|
+
let where;
|
|
1840
|
+
if (this.matchKeyword('WHERE')) {
|
|
1841
|
+
where = this.expression();
|
|
1842
|
+
}
|
|
1843
|
+
return {
|
|
1844
|
+
type: 'createIndex',
|
|
1845
|
+
index,
|
|
1846
|
+
table,
|
|
1847
|
+
ifNotExists,
|
|
1848
|
+
columns,
|
|
1849
|
+
where,
|
|
1850
|
+
isUnique,
|
|
1851
|
+
loc: _createLoc(startToken, this.previous()),
|
|
1852
|
+
};
|
|
1853
|
+
}
|
|
1854
|
+
/**
|
|
1855
|
+
* Parse CREATE VIEW statement
|
|
1856
|
+
* @returns AST for CREATE VIEW
|
|
1857
|
+
*/
|
|
1858
|
+
createViewStatement(startToken, withClause) {
|
|
1859
|
+
let isTemporary = false;
|
|
1860
|
+
if (this.peekKeyword('TEMP') || this.peekKeyword('TEMPORARY')) {
|
|
1861
|
+
isTemporary = true;
|
|
1862
|
+
this.advance();
|
|
1863
|
+
}
|
|
1864
|
+
let ifNotExists = false;
|
|
1865
|
+
if (this.matchKeyword('IF')) {
|
|
1866
|
+
this.consumeKeyword('NOT', "Expected 'NOT' after 'IF'.");
|
|
1867
|
+
this.consumeKeyword('EXISTS', "Expected 'EXISTS' after 'IF NOT'.");
|
|
1868
|
+
ifNotExists = true;
|
|
1869
|
+
}
|
|
1870
|
+
const view = this.tableIdentifier();
|
|
1871
|
+
let columns;
|
|
1872
|
+
if (this.check(TokenType.LPAREN)) {
|
|
1873
|
+
this.consume(TokenType.LPAREN, "Expected '(' to start view column list.");
|
|
1874
|
+
columns = [];
|
|
1875
|
+
const contextualKeywords = ['key', 'action', 'set', 'default', 'check', 'unique', 'references', 'on', 'cascade', 'restrict', 'like'];
|
|
1876
|
+
if (!this.check(TokenType.RPAREN)) {
|
|
1877
|
+
do {
|
|
1878
|
+
columns.push(this.consumeIdentifier(contextualKeywords, "Expected column name in view column list."));
|
|
1879
|
+
} while (this.match(TokenType.COMMA));
|
|
1880
|
+
}
|
|
1881
|
+
this.consume(TokenType.RPAREN, "Expected ')' after view column list.");
|
|
1882
|
+
}
|
|
1883
|
+
this.consumeKeyword('AS', "Expected 'AS' before SELECT statement for CREATE VIEW.");
|
|
1884
|
+
const selectStartToken = this.consume(TokenType.SELECT, "Expected 'SELECT' after 'AS' in CREATE VIEW.");
|
|
1885
|
+
const select = this.selectStatement(selectStartToken, withClause);
|
|
1886
|
+
return {
|
|
1887
|
+
type: 'createView',
|
|
1888
|
+
view,
|
|
1889
|
+
ifNotExists,
|
|
1890
|
+
columns,
|
|
1891
|
+
select,
|
|
1892
|
+
isTemporary,
|
|
1893
|
+
loc: _createLoc(startToken, this.previous()),
|
|
1894
|
+
};
|
|
1895
|
+
}
|
|
1896
|
+
/**
|
|
1897
|
+
* Parse DROP statement
|
|
1898
|
+
* @returns AST for DROP statement
|
|
1899
|
+
*/
|
|
1900
|
+
dropStatement(startToken, _withClause) {
|
|
1901
|
+
let objectType;
|
|
1902
|
+
if (this.peekKeyword('TABLE')) {
|
|
1903
|
+
this.consumeKeyword('TABLE', "Expected TABLE after DROP.");
|
|
1904
|
+
objectType = 'table';
|
|
1905
|
+
}
|
|
1906
|
+
else if (this.peekKeyword('VIEW')) {
|
|
1907
|
+
this.consumeKeyword('VIEW', "Expected VIEW after DROP.");
|
|
1908
|
+
objectType = 'view';
|
|
1909
|
+
}
|
|
1910
|
+
else if (this.peekKeyword('INDEX')) {
|
|
1911
|
+
this.consumeKeyword('INDEX', "Expected INDEX after DROP.");
|
|
1912
|
+
objectType = 'index';
|
|
1913
|
+
}
|
|
1914
|
+
else {
|
|
1915
|
+
throw this.error(this.peek(), "Expected TABLE, VIEW, or INDEX after DROP.");
|
|
1916
|
+
}
|
|
1917
|
+
let ifExists = false;
|
|
1918
|
+
if (this.matchKeyword('IF')) {
|
|
1919
|
+
this.consumeKeyword('EXISTS', "Expected 'EXISTS' after 'IF'.");
|
|
1920
|
+
ifExists = true;
|
|
1921
|
+
}
|
|
1922
|
+
const name = this.tableIdentifier();
|
|
1923
|
+
return {
|
|
1924
|
+
type: 'drop',
|
|
1925
|
+
objectType,
|
|
1926
|
+
name,
|
|
1927
|
+
ifExists,
|
|
1928
|
+
loc: _createLoc(startToken, this.previous()),
|
|
1929
|
+
};
|
|
1930
|
+
}
|
|
1931
|
+
/**
|
|
1932
|
+
* Parse ALTER TABLE statement
|
|
1933
|
+
* @returns AST for ALTER TABLE statement
|
|
1934
|
+
*/
|
|
1935
|
+
alterTableStatement(startToken, _withClause) {
|
|
1936
|
+
this.consumeKeyword('TABLE', "Expected 'TABLE' after ALTER.");
|
|
1937
|
+
const table = this.tableIdentifier();
|
|
1938
|
+
let action;
|
|
1939
|
+
if (this.peekKeyword('RENAME')) {
|
|
1940
|
+
this.consumeKeyword('RENAME', "Expected RENAME.");
|
|
1941
|
+
if (this.matchKeyword('COLUMN')) {
|
|
1942
|
+
const oldName = this.consumeIdentifier(['key', 'action', 'set', 'default', 'check', 'unique', 'references', 'on', 'cascade', 'restrict', 'like'], "Expected old column name after RENAME COLUMN.");
|
|
1943
|
+
this.consumeKeyword('TO', "Expected 'TO' after old column name.");
|
|
1944
|
+
const newName = this.consumeIdentifier(['key', 'action', 'set', 'default', 'check', 'unique', 'references', 'on', 'cascade', 'restrict', 'like'], "Expected new column name after TO.");
|
|
1945
|
+
action = { type: 'renameColumn', oldName, newName };
|
|
1946
|
+
}
|
|
1947
|
+
else {
|
|
1948
|
+
this.consumeKeyword('TO', "Expected 'TO' after RENAME.");
|
|
1949
|
+
const newName = this.consumeIdentifier(['key', 'action', 'set', 'default', 'check', 'unique', 'references', 'on', 'cascade', 'restrict', 'like'], "Expected new table name after RENAME TO.");
|
|
1950
|
+
action = { type: 'renameTable', newName };
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1953
|
+
else if (this.peekKeyword('ADD')) {
|
|
1954
|
+
this.consumeKeyword('ADD', "Expected ADD.");
|
|
1955
|
+
if (this.peekKeyword('CONSTRAINT')) {
|
|
1956
|
+
// ADD CONSTRAINT ... - let tableConstraint parse everything including CONSTRAINT keyword
|
|
1957
|
+
const constraint = this.tableConstraint();
|
|
1958
|
+
action = { type: 'addConstraint', constraint };
|
|
1959
|
+
}
|
|
1960
|
+
else {
|
|
1961
|
+
// ADD [COLUMN] column_def
|
|
1962
|
+
this.matchKeyword('COLUMN');
|
|
1963
|
+
const column = this.columnDefinition();
|
|
1964
|
+
action = { type: 'addColumn', column };
|
|
1965
|
+
}
|
|
1966
|
+
}
|
|
1967
|
+
else if (this.peekKeyword('DROP')) {
|
|
1968
|
+
this.consumeKeyword('DROP', "Expected DROP.");
|
|
1969
|
+
this.matchKeyword('COLUMN');
|
|
1970
|
+
const name = this.consumeIdentifier(['key', 'action', 'set', 'default', 'check', 'unique', 'references', 'on', 'cascade', 'restrict', 'like'], "Expected column name after DROP COLUMN.");
|
|
1971
|
+
action = { type: 'dropColumn', name };
|
|
1972
|
+
}
|
|
1973
|
+
else {
|
|
1974
|
+
throw this.error(this.peek(), "Expected RENAME, ADD, or DROP after table name in ALTER TABLE.");
|
|
1975
|
+
}
|
|
1976
|
+
return {
|
|
1977
|
+
type: 'alterTable',
|
|
1978
|
+
table,
|
|
1979
|
+
action,
|
|
1980
|
+
loc: _createLoc(startToken, this.previous()),
|
|
1981
|
+
};
|
|
1982
|
+
}
|
|
1983
|
+
/**
|
|
1984
|
+
* Parse BEGIN statement
|
|
1985
|
+
* @returns AST for BEGIN statement
|
|
1986
|
+
*/
|
|
1987
|
+
beginStatement(startToken, _withClause) {
|
|
1988
|
+
let mode;
|
|
1989
|
+
if (this.peekKeyword('DEFERRED')) {
|
|
1990
|
+
this.advance();
|
|
1991
|
+
mode = 'deferred';
|
|
1992
|
+
}
|
|
1993
|
+
else if (this.peekKeyword('IMMEDIATE')) {
|
|
1994
|
+
this.advance();
|
|
1995
|
+
mode = 'immediate';
|
|
1996
|
+
}
|
|
1997
|
+
else if (this.peekKeyword('EXCLUSIVE')) {
|
|
1998
|
+
this.advance();
|
|
1999
|
+
mode = 'exclusive';
|
|
2000
|
+
}
|
|
2001
|
+
this.matchKeyword('TRANSACTION');
|
|
2002
|
+
return { type: 'begin', mode, loc: _createLoc(startToken, this.previous()) };
|
|
2003
|
+
}
|
|
2004
|
+
/**
|
|
2005
|
+
* Parse COMMIT statement
|
|
2006
|
+
* @returns AST for COMMIT statement
|
|
2007
|
+
*/
|
|
2008
|
+
commitStatement(startToken, _withClause) {
|
|
2009
|
+
this.matchKeyword('TRANSACTION');
|
|
2010
|
+
return { type: 'commit', loc: _createLoc(startToken, this.previous()) };
|
|
2011
|
+
}
|
|
2012
|
+
/**
|
|
2013
|
+
* Parse ROLLBACK statement
|
|
2014
|
+
* @returns AST for ROLLBACK statement
|
|
2015
|
+
*/
|
|
2016
|
+
rollbackStatement(startToken, _withClause) {
|
|
2017
|
+
this.matchKeyword('TRANSACTION');
|
|
2018
|
+
let savepoint;
|
|
2019
|
+
if (this.matchKeyword('TO')) {
|
|
2020
|
+
this.matchKeyword('SAVEPOINT');
|
|
2021
|
+
if (!this.check(TokenType.IDENTIFIER)) {
|
|
2022
|
+
throw this.error(this.peek(), "Expected savepoint name after ROLLBACK TO.");
|
|
2023
|
+
}
|
|
2024
|
+
savepoint = this.advance().lexeme;
|
|
2025
|
+
}
|
|
2026
|
+
return { type: 'rollback', savepoint, loc: _createLoc(startToken, this.previous()) };
|
|
2027
|
+
}
|
|
2028
|
+
/**
|
|
2029
|
+
* Parse SAVEPOINT statement
|
|
2030
|
+
* @returns AST for SAVEPOINT statement
|
|
2031
|
+
*/
|
|
2032
|
+
savepointStatement(startToken, _withClause) {
|
|
2033
|
+
const name = this.consumeIdentifier("Expected savepoint name after SAVEPOINT.");
|
|
2034
|
+
return { type: 'savepoint', name, loc: _createLoc(startToken, this.previous()) };
|
|
2035
|
+
}
|
|
2036
|
+
/**
|
|
2037
|
+
* Parse RELEASE statement
|
|
2038
|
+
* @returns AST for RELEASE statement
|
|
2039
|
+
*/
|
|
2040
|
+
releaseStatement(startToken, _withClause) {
|
|
2041
|
+
this.matchKeyword('SAVEPOINT');
|
|
2042
|
+
const name = this.consumeIdentifier("Expected savepoint name after RELEASE [SAVEPOINT].");
|
|
2043
|
+
return { type: 'release', savepoint: name, loc: _createLoc(startToken, this.previous()) };
|
|
2044
|
+
}
|
|
2045
|
+
/**
|
|
2046
|
+
* Parse PRAGMA statement
|
|
2047
|
+
* @returns AST for PRAGMA statement
|
|
2048
|
+
*/
|
|
2049
|
+
pragmaStatement(startToken, _withClause) {
|
|
2050
|
+
const nameValue = this.nameValueItem("pragma");
|
|
2051
|
+
return { type: 'pragma', ...nameValue, loc: _createLoc(startToken, this.previous()) };
|
|
2052
|
+
}
|
|
2053
|
+
nameValueItem(context) {
|
|
2054
|
+
const name = this.consumeIdentifier(`Expected ${context} name.`);
|
|
2055
|
+
let value;
|
|
2056
|
+
if (this.match(TokenType.EQUAL)) {
|
|
2057
|
+
if (this.check(TokenType.IDENTIFIER)) {
|
|
2058
|
+
value = { type: 'identifier', name: this.advance().lexeme };
|
|
2059
|
+
}
|
|
2060
|
+
else if (this.match(TokenType.STRING, TokenType.INTEGER, TokenType.FLOAT, TokenType.NULL, TokenType.TRUE, TokenType.FALSE)) {
|
|
2061
|
+
const token = this.previous();
|
|
2062
|
+
let literal_value;
|
|
2063
|
+
if (token.type === TokenType.NULL) {
|
|
2064
|
+
literal_value = null;
|
|
2065
|
+
}
|
|
2066
|
+
else if (token.type === TokenType.TRUE) {
|
|
2067
|
+
literal_value = 1;
|
|
2068
|
+
}
|
|
2069
|
+
else if (token.type === TokenType.FALSE) {
|
|
2070
|
+
literal_value = 0;
|
|
2071
|
+
}
|
|
2072
|
+
else {
|
|
2073
|
+
literal_value = token.literal;
|
|
2074
|
+
}
|
|
2075
|
+
value = { type: 'literal', value: literal_value };
|
|
2076
|
+
}
|
|
2077
|
+
else if (this.match(TokenType.MINUS)) {
|
|
2078
|
+
if (this.check(TokenType.INTEGER) || this.check(TokenType.FLOAT)) {
|
|
2079
|
+
const token = this.advance();
|
|
2080
|
+
value = { type: 'literal', value: -token.literal };
|
|
2081
|
+
}
|
|
2082
|
+
else {
|
|
2083
|
+
throw this.error(this.peek(), "Expected number after '-'.");
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
else {
|
|
2087
|
+
throw this.error(this.peek(), `Expected ${context} value (identifier, string, number, or NULL).`);
|
|
2088
|
+
}
|
|
2089
|
+
}
|
|
2090
|
+
// If no '=' is found, value remains undefined (reading mode)
|
|
2091
|
+
return { name: name.toLowerCase(), value };
|
|
2092
|
+
}
|
|
2093
|
+
// --- Supporting Clause / Definition Parsers ---
|
|
2094
|
+
/** @internal Parses a comma-separated list of indexed columns */
|
|
2095
|
+
indexedColumnList() {
|
|
2096
|
+
const columns = [];
|
|
2097
|
+
do {
|
|
2098
|
+
columns.push(this.indexedColumn());
|
|
2099
|
+
} while (this.match(TokenType.COMMA));
|
|
2100
|
+
return columns;
|
|
2101
|
+
}
|
|
2102
|
+
/** @internal Parses a single indexed column definition */
|
|
2103
|
+
indexedColumn() {
|
|
2104
|
+
const expr = this.expression();
|
|
2105
|
+
let name;
|
|
2106
|
+
if (expr.type === 'column' && !expr.table && !expr.schema) {
|
|
2107
|
+
name = expr.name;
|
|
2108
|
+
}
|
|
2109
|
+
let direction;
|
|
2110
|
+
if (this.match(TokenType.ASC)) {
|
|
2111
|
+
direction = 'asc';
|
|
2112
|
+
}
|
|
2113
|
+
else if (this.match(TokenType.DESC)) {
|
|
2114
|
+
direction = 'desc';
|
|
2115
|
+
}
|
|
2116
|
+
if (name) {
|
|
2117
|
+
return { name, direction };
|
|
2118
|
+
}
|
|
2119
|
+
else {
|
|
2120
|
+
return { expr, direction };
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
2123
|
+
consumeIdentifier(errorMessageOrKeywords, errorMessage) {
|
|
2124
|
+
if (typeof errorMessageOrKeywords === 'string') {
|
|
2125
|
+
// Single parameter version - no contextual keywords
|
|
2126
|
+
return this.consumeIdentifierOrContextualKeyword([], errorMessageOrKeywords);
|
|
2127
|
+
}
|
|
2128
|
+
else {
|
|
2129
|
+
// Two parameter version - with contextual keywords
|
|
2130
|
+
return this.consumeIdentifierOrContextualKeyword(errorMessageOrKeywords, errorMessage);
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
/**
|
|
2134
|
+
* @internal Helper to consume an IDENTIFIER token or specified contextual keywords
|
|
2135
|
+
* @param availableKeywords Array of keyword strings that can be used as identifiers in this context
|
|
2136
|
+
* @param errorMessage Error message if no valid token is found
|
|
2137
|
+
* @returns The lexeme of the consumed token
|
|
2138
|
+
*/
|
|
2139
|
+
consumeIdentifierOrContextualKeyword(availableKeywords, errorMessage) {
|
|
2140
|
+
const token = this.peek();
|
|
2141
|
+
// First check for regular identifier
|
|
2142
|
+
if (this.check(TokenType.IDENTIFIER)) {
|
|
2143
|
+
return this.advance().lexeme;
|
|
2144
|
+
}
|
|
2145
|
+
// Then check for available contextual keywords
|
|
2146
|
+
for (const keyword of availableKeywords) {
|
|
2147
|
+
const keywordUpper = keyword.toUpperCase();
|
|
2148
|
+
const expectedTokenType = TokenType[keywordUpper];
|
|
2149
|
+
if (expectedTokenType && token.type === expectedTokenType) {
|
|
2150
|
+
// This keyword token is available as an identifier in this context
|
|
2151
|
+
return this.advance().lexeme;
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
throw this.error(this.peek(), errorMessage);
|
|
2155
|
+
}
|
|
2156
|
+
/**
|
|
2157
|
+
* @internal Helper to check if current token is an identifier or available contextual keyword
|
|
2158
|
+
*/
|
|
2159
|
+
checkIdentifierLike(availableKeywords = []) {
|
|
2160
|
+
if (this.check(TokenType.IDENTIFIER)) {
|
|
2161
|
+
return true;
|
|
2162
|
+
}
|
|
2163
|
+
return this.isContextualKeywordAvailable(availableKeywords);
|
|
2164
|
+
}
|
|
2165
|
+
/**
|
|
2166
|
+
* @internal Helper to check if token at offset is an identifier or available contextual keyword
|
|
2167
|
+
*/
|
|
2168
|
+
checkIdentifierLikeAt(offset, availableKeywords = []) {
|
|
2169
|
+
if (this.checkNext(offset, TokenType.IDENTIFIER)) {
|
|
2170
|
+
return true;
|
|
2171
|
+
}
|
|
2172
|
+
if (this.current + offset >= this.tokens.length)
|
|
2173
|
+
return false;
|
|
2174
|
+
const token = this.tokens[this.current + offset];
|
|
2175
|
+
for (const keyword of availableKeywords) {
|
|
2176
|
+
const keywordUpper = keyword.toUpperCase();
|
|
2177
|
+
const expectedTokenType = TokenType[keywordUpper];
|
|
2178
|
+
if (expectedTokenType && token.type === expectedTokenType) {
|
|
2179
|
+
return true;
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
return false;
|
|
2183
|
+
}
|
|
2184
|
+
/**
|
|
2185
|
+
* @internal Helper to check if any of the specified contextual keywords are available at current position
|
|
2186
|
+
*/
|
|
2187
|
+
isContextualKeywordAvailable(availableKeywords) {
|
|
2188
|
+
const token = this.peek();
|
|
2189
|
+
for (const keyword of availableKeywords) {
|
|
2190
|
+
const keywordUpper = keyword.toUpperCase();
|
|
2191
|
+
const expectedTokenType = TokenType[keywordUpper];
|
|
2192
|
+
if (expectedTokenType && token.type === expectedTokenType) {
|
|
2193
|
+
return true;
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
return false;
|
|
2197
|
+
}
|
|
2198
|
+
// --- Stubs for required helpers (implement fully for CREATE TABLE) ---
|
|
2199
|
+
/** @internal Parses a column definition */
|
|
2200
|
+
columnDefinition() {
|
|
2201
|
+
const name = this.consumeIdentifier(['key', 'action', 'set', 'default', 'check', 'unique', 'references', 'on', 'cascade', 'restrict', 'like'], "Expected column name.");
|
|
2202
|
+
let dataType;
|
|
2203
|
+
if (this.check(TokenType.IDENTIFIER)) {
|
|
2204
|
+
dataType = this.advance().lexeme;
|
|
2205
|
+
if (this.match(TokenType.LPAREN)) {
|
|
2206
|
+
dataType += '(';
|
|
2207
|
+
let parenLevel = 1;
|
|
2208
|
+
while (parenLevel > 0 && !this.isAtEnd()) {
|
|
2209
|
+
const token = this.peek();
|
|
2210
|
+
if (token.type === TokenType.LPAREN)
|
|
2211
|
+
parenLevel++;
|
|
2212
|
+
if (token.type === TokenType.RPAREN)
|
|
2213
|
+
parenLevel--;
|
|
2214
|
+
if (parenLevel > 0) {
|
|
2215
|
+
dataType += this.advance().lexeme;
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
dataType += ')';
|
|
2219
|
+
this.consume(TokenType.RPAREN, "Expected ')' after type parameters.");
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
const constraints = this.columnConstraintList();
|
|
2223
|
+
return { name, dataType, constraints };
|
|
2224
|
+
}
|
|
2225
|
+
/** @internal Parses column constraints */
|
|
2226
|
+
columnConstraintList() {
|
|
2227
|
+
const constraints = [];
|
|
2228
|
+
while (this.isColumnConstraintStart()) {
|
|
2229
|
+
constraints.push(this.columnConstraint());
|
|
2230
|
+
}
|
|
2231
|
+
return constraints;
|
|
2232
|
+
}
|
|
2233
|
+
/** @internal Checks if the current token can start a column constraint */
|
|
2234
|
+
isColumnConstraintStart() {
|
|
2235
|
+
return this.check(TokenType.CONSTRAINT) ||
|
|
2236
|
+
this.check(TokenType.PRIMARY) ||
|
|
2237
|
+
this.check(TokenType.NOT) ||
|
|
2238
|
+
this.check(TokenType.NULL) ||
|
|
2239
|
+
this.check(TokenType.UNIQUE) ||
|
|
2240
|
+
this.check(TokenType.CHECK) ||
|
|
2241
|
+
this.check(TokenType.DEFAULT) ||
|
|
2242
|
+
this.check(TokenType.COLLATE) ||
|
|
2243
|
+
this.check(TokenType.REFERENCES) ||
|
|
2244
|
+
this.check(TokenType.GENERATED);
|
|
2245
|
+
}
|
|
2246
|
+
/** @internal Parses a single column constraint */
|
|
2247
|
+
columnConstraint() {
|
|
2248
|
+
let name;
|
|
2249
|
+
const startToken = this.peek(); // Capture start token
|
|
2250
|
+
let endToken = startToken; // Initialize end token
|
|
2251
|
+
if (this.match(TokenType.CONSTRAINT)) {
|
|
2252
|
+
name = this.consumeIdentifier("Expected constraint name after CONSTRAINT.");
|
|
2253
|
+
endToken = this.previous();
|
|
2254
|
+
}
|
|
2255
|
+
if (this.match(TokenType.PRIMARY)) {
|
|
2256
|
+
this.consume(TokenType.KEY, "Expected KEY after PRIMARY.");
|
|
2257
|
+
const direction = this.match(TokenType.ASC) ? 'asc' : this.match(TokenType.DESC) ? 'desc' : undefined;
|
|
2258
|
+
if (direction)
|
|
2259
|
+
endToken = this.previous();
|
|
2260
|
+
const onConflict = this.parseConflictClause();
|
|
2261
|
+
if (onConflict)
|
|
2262
|
+
endToken = this.previous(); // Update endToken if conflict clause was parsed
|
|
2263
|
+
const autoincrement = this.match(TokenType.AUTOINCREMENT);
|
|
2264
|
+
if (autoincrement)
|
|
2265
|
+
endToken = this.previous();
|
|
2266
|
+
return { type: 'primaryKey', name, onConflict, autoincrement, direction, loc: _createLoc(startToken, endToken) };
|
|
2267
|
+
}
|
|
2268
|
+
else if (this.match(TokenType.NOT)) {
|
|
2269
|
+
this.consume(TokenType.NULL, "Expected NULL after NOT.");
|
|
2270
|
+
endToken = this.previous();
|
|
2271
|
+
const onConflict = this.parseConflictClause();
|
|
2272
|
+
if (onConflict)
|
|
2273
|
+
endToken = this.previous(); // Update endToken if conflict clause was parsed
|
|
2274
|
+
return { type: 'notNull', name, onConflict, loc: _createLoc(startToken, endToken) };
|
|
2275
|
+
}
|
|
2276
|
+
else if (this.match(TokenType.NULL)) {
|
|
2277
|
+
endToken = this.previous();
|
|
2278
|
+
const onConflict = this.parseConflictClause();
|
|
2279
|
+
if (onConflict)
|
|
2280
|
+
endToken = this.previous(); // Update endToken if conflict clause was parsed
|
|
2281
|
+
return { type: 'null', name, onConflict, loc: _createLoc(startToken, endToken) };
|
|
2282
|
+
}
|
|
2283
|
+
else if (this.match(TokenType.UNIQUE)) {
|
|
2284
|
+
endToken = this.previous();
|
|
2285
|
+
const onConflict = this.parseConflictClause();
|
|
2286
|
+
if (onConflict)
|
|
2287
|
+
endToken = this.previous(); // Update endToken if conflict clause was parsed
|
|
2288
|
+
return { type: 'unique', name, onConflict, loc: _createLoc(startToken, endToken) };
|
|
2289
|
+
}
|
|
2290
|
+
else if (this.match(TokenType.CHECK)) {
|
|
2291
|
+
// --- Parse optional ON clause before parentheses --- //
|
|
2292
|
+
let operations;
|
|
2293
|
+
if (this.matchKeyword('ON')) {
|
|
2294
|
+
operations = this.parseRowOpList();
|
|
2295
|
+
}
|
|
2296
|
+
// --- End Parse ON clause --- //
|
|
2297
|
+
this.consume(TokenType.LPAREN, "Expected '(' after CHECK.");
|
|
2298
|
+
const expr = this.expression();
|
|
2299
|
+
endToken = this.consume(TokenType.RPAREN, "Expected ')' after CHECK expression.");
|
|
2300
|
+
return { type: 'check', name, expr, operations, loc: _createLoc(startToken, endToken) };
|
|
2301
|
+
}
|
|
2302
|
+
else if (this.match(TokenType.DEFAULT)) {
|
|
2303
|
+
const expr = this.expression();
|
|
2304
|
+
endToken = this.previous();
|
|
2305
|
+
return { type: 'default', name, expr, loc: _createLoc(startToken, endToken) };
|
|
2306
|
+
}
|
|
2307
|
+
else if (this.match(TokenType.COLLATE)) {
|
|
2308
|
+
if (!this.check(TokenType.IDENTIFIER)) {
|
|
2309
|
+
throw this.error(this.peek(), "Expected collation name after COLLATE.");
|
|
2310
|
+
}
|
|
2311
|
+
const collation = this.advance().lexeme;
|
|
2312
|
+
endToken = this.previous();
|
|
2313
|
+
return { type: 'collate', name, collation, loc: _createLoc(startToken, endToken) };
|
|
2314
|
+
}
|
|
2315
|
+
else if (this.match(TokenType.REFERENCES)) {
|
|
2316
|
+
const fkClause = this.foreignKeyClause();
|
|
2317
|
+
endToken = this.previous(); // End token is end of FK clause
|
|
2318
|
+
return { type: 'foreignKey', name, foreignKey: fkClause, loc: _createLoc(startToken, endToken) };
|
|
2319
|
+
}
|
|
2320
|
+
else if (this.match(TokenType.GENERATED)) {
|
|
2321
|
+
this.consume(TokenType.ALWAYS, "Expected ALWAYS after GENERATED.");
|
|
2322
|
+
this.consume(TokenType.AS, "Expected AS after GENERATED ALWAYS.");
|
|
2323
|
+
this.consume(TokenType.LPAREN, "Expected '(' after AS.");
|
|
2324
|
+
const expr = this.expression();
|
|
2325
|
+
this.consume(TokenType.RPAREN, "Expected ')' after generated expression.");
|
|
2326
|
+
endToken = this.previous();
|
|
2327
|
+
let stored = false;
|
|
2328
|
+
if (this.match(TokenType.STORED)) {
|
|
2329
|
+
stored = true;
|
|
2330
|
+
endToken = this.previous();
|
|
2331
|
+
}
|
|
2332
|
+
else if (this.match(TokenType.VIRTUAL)) {
|
|
2333
|
+
endToken = this.previous();
|
|
2334
|
+
}
|
|
2335
|
+
return { type: 'generated', name, generated: { expr, stored }, loc: _createLoc(startToken, endToken) };
|
|
2336
|
+
}
|
|
2337
|
+
throw this.error(this.peek(), "Expected column constraint type (PRIMARY KEY, NOT NULL, UNIQUE, CHECK, DEFAULT, COLLATE, REFERENCES, GENERATED).");
|
|
2338
|
+
}
|
|
2339
|
+
/** @internal Parses a table constraint */
|
|
2340
|
+
tableConstraint() {
|
|
2341
|
+
let name;
|
|
2342
|
+
const startToken = this.peek(); // Capture start token
|
|
2343
|
+
let endToken = startToken; // Initialize end token
|
|
2344
|
+
if (this.match(TokenType.CONSTRAINT)) {
|
|
2345
|
+
name = this.consumeIdentifier("Expected constraint name after CONSTRAINT.");
|
|
2346
|
+
endToken = this.previous();
|
|
2347
|
+
}
|
|
2348
|
+
if (this.match(TokenType.PRIMARY)) {
|
|
2349
|
+
this.consume(TokenType.KEY, "Expected KEY after PRIMARY.");
|
|
2350
|
+
this.consume(TokenType.LPAREN, "Expected '(' before PRIMARY KEY columns.");
|
|
2351
|
+
// Handle empty PRIMARY KEY () for singleton tables (Third Manifesto feature)
|
|
2352
|
+
let columns = [];
|
|
2353
|
+
if (!this.check(TokenType.RPAREN)) {
|
|
2354
|
+
columns = this.identifierListWithDirection();
|
|
2355
|
+
}
|
|
2356
|
+
endToken = this.consume(TokenType.RPAREN, "Expected ')' after PRIMARY KEY columns.");
|
|
2357
|
+
const onConflict = this.parseConflictClause();
|
|
2358
|
+
if (onConflict)
|
|
2359
|
+
endToken = this.previous();
|
|
2360
|
+
return { type: 'primaryKey', name, columns, onConflict, loc: _createLoc(startToken, endToken) };
|
|
2361
|
+
}
|
|
2362
|
+
else if (this.match(TokenType.UNIQUE)) {
|
|
2363
|
+
this.consume(TokenType.LPAREN, "Expected '(' before UNIQUE columns.");
|
|
2364
|
+
const columnsSimple = this.identifierList();
|
|
2365
|
+
const columns = columnsSimple.map(name => ({ name }));
|
|
2366
|
+
endToken = this.consume(TokenType.RPAREN, "Expected ')' after UNIQUE columns.");
|
|
2367
|
+
const onConflict = this.parseConflictClause();
|
|
2368
|
+
if (onConflict)
|
|
2369
|
+
endToken = this.previous();
|
|
2370
|
+
return { type: 'unique', name, columns, onConflict, loc: _createLoc(startToken, endToken) };
|
|
2371
|
+
}
|
|
2372
|
+
else if (this.match(TokenType.CHECK)) {
|
|
2373
|
+
// --- Parse optional ON clause before parentheses --- //
|
|
2374
|
+
let operations;
|
|
2375
|
+
if (this.matchKeyword('ON')) {
|
|
2376
|
+
operations = this.parseRowOpList();
|
|
2377
|
+
}
|
|
2378
|
+
// --- End Parse ON clause --- //
|
|
2379
|
+
this.consume(TokenType.LPAREN, "Expected '(' after CHECK.");
|
|
2380
|
+
const expr = this.expression();
|
|
2381
|
+
endToken = this.consume(TokenType.RPAREN, "Expected ')' after CHECK expression.");
|
|
2382
|
+
return { type: 'check', name, expr, operations, loc: _createLoc(startToken, endToken) };
|
|
2383
|
+
}
|
|
2384
|
+
else if (this.match(TokenType.FOREIGN)) {
|
|
2385
|
+
this.consume(TokenType.KEY, "Expected KEY after FOREIGN.");
|
|
2386
|
+
this.consume(TokenType.LPAREN, "Expected '(' before FOREIGN KEY columns.");
|
|
2387
|
+
const columns = this.identifierList().map(name => ({ name }));
|
|
2388
|
+
this.consume(TokenType.RPAREN, "Expected ')' after FOREIGN KEY columns.");
|
|
2389
|
+
const fkClause = this.foreignKeyClause();
|
|
2390
|
+
endToken = this.previous(); // End token is end of FK clause
|
|
2391
|
+
return { type: 'foreignKey', name, columns, foreignKey: fkClause, loc: _createLoc(startToken, endToken) };
|
|
2392
|
+
}
|
|
2393
|
+
throw this.error(this.peek(), "Expected table constraint type (PRIMARY KEY, UNIQUE, CHECK, FOREIGN KEY).");
|
|
2394
|
+
}
|
|
2395
|
+
/** @internal Parses a foreign key clause */
|
|
2396
|
+
foreignKeyClause() {
|
|
2397
|
+
this.consume(TokenType.REFERENCES, "Expected REFERENCES for foreign key.");
|
|
2398
|
+
const table = this.consumeIdentifier("Expected foreign table name.");
|
|
2399
|
+
let columns;
|
|
2400
|
+
if (this.match(TokenType.LPAREN)) {
|
|
2401
|
+
columns = this.identifierList();
|
|
2402
|
+
this.consume(TokenType.RPAREN, "Expected ')' after foreign columns.");
|
|
2403
|
+
}
|
|
2404
|
+
let onDelete;
|
|
2405
|
+
let onUpdate;
|
|
2406
|
+
let deferrable;
|
|
2407
|
+
let initiallyDeferred;
|
|
2408
|
+
while (this.check(TokenType.ON) || this.check(TokenType.DEFERRABLE) || this.check(TokenType.NOT)) {
|
|
2409
|
+
if (this.match(TokenType.ON)) {
|
|
2410
|
+
if (this.match(TokenType.DELETE)) {
|
|
2411
|
+
onDelete = this.parseForeignKeyAction();
|
|
2412
|
+
}
|
|
2413
|
+
else if (this.match(TokenType.UPDATE)) {
|
|
2414
|
+
onUpdate = this.parseForeignKeyAction();
|
|
2415
|
+
}
|
|
2416
|
+
else {
|
|
2417
|
+
throw this.error(this.peek(), "Expected DELETE or UPDATE after ON.");
|
|
2418
|
+
}
|
|
2419
|
+
}
|
|
2420
|
+
else if (this.match(TokenType.DEFERRABLE)) {
|
|
2421
|
+
deferrable = true;
|
|
2422
|
+
if (this.match(TokenType.INITIALLY)) {
|
|
2423
|
+
if (this.match(TokenType.DEFERRED)) {
|
|
2424
|
+
initiallyDeferred = true;
|
|
2425
|
+
}
|
|
2426
|
+
else if (this.match(TokenType.IMMEDIATE)) {
|
|
2427
|
+
initiallyDeferred = false;
|
|
2428
|
+
}
|
|
2429
|
+
else {
|
|
2430
|
+
throw this.error(this.peek(), "Expected DEFERRED or IMMEDIATE after INITIALLY.");
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
}
|
|
2434
|
+
else if (this.match(TokenType.NOT)) {
|
|
2435
|
+
this.consume(TokenType.DEFERRABLE, "Expected DEFERRABLE after NOT.");
|
|
2436
|
+
deferrable = false;
|
|
2437
|
+
if (this.match(TokenType.INITIALLY)) {
|
|
2438
|
+
if (this.match(TokenType.DEFERRED)) {
|
|
2439
|
+
initiallyDeferred = true;
|
|
2440
|
+
}
|
|
2441
|
+
else if (this.match(TokenType.IMMEDIATE)) {
|
|
2442
|
+
initiallyDeferred = false;
|
|
2443
|
+
}
|
|
2444
|
+
else {
|
|
2445
|
+
throw this.error(this.peek(), "Expected DEFERRED or IMMEDIATE after INITIALLY.");
|
|
2446
|
+
}
|
|
2447
|
+
}
|
|
2448
|
+
}
|
|
2449
|
+
else {
|
|
2450
|
+
break;
|
|
2451
|
+
}
|
|
2452
|
+
}
|
|
2453
|
+
return { table, columns, onDelete, onUpdate, deferrable, initiallyDeferred };
|
|
2454
|
+
}
|
|
2455
|
+
/** @internal Parses the ON CONFLICT clause */
|
|
2456
|
+
parseConflictClause() {
|
|
2457
|
+
if (this.match(TokenType.ON)) {
|
|
2458
|
+
this.consume(TokenType.CONFLICT, "Expected CONFLICT after ON.");
|
|
2459
|
+
if (this.match(TokenType.ROLLBACK))
|
|
2460
|
+
return ConflictResolution.ROLLBACK;
|
|
2461
|
+
if (this.match(TokenType.ABORT))
|
|
2462
|
+
return ConflictResolution.ABORT;
|
|
2463
|
+
if (this.match(TokenType.FAIL))
|
|
2464
|
+
return ConflictResolution.FAIL;
|
|
2465
|
+
if (this.match(TokenType.IGNORE))
|
|
2466
|
+
return ConflictResolution.IGNORE;
|
|
2467
|
+
if (this.match(TokenType.REPLACE))
|
|
2468
|
+
return ConflictResolution.REPLACE;
|
|
2469
|
+
throw this.error(this.peek(), "Expected conflict resolution algorithm (ROLLBACK, ABORT, FAIL, IGNORE, REPLACE).");
|
|
2470
|
+
}
|
|
2471
|
+
return undefined;
|
|
2472
|
+
}
|
|
2473
|
+
/** @internal Parses the foreign key action */
|
|
2474
|
+
parseForeignKeyAction() {
|
|
2475
|
+
if (this.match(TokenType.SET)) {
|
|
2476
|
+
if (this.match(TokenType.NULL))
|
|
2477
|
+
return 'setNull';
|
|
2478
|
+
if (this.match(TokenType.DEFAULT))
|
|
2479
|
+
return 'setDefault';
|
|
2480
|
+
throw this.error(this.peek(), "Expected NULL or DEFAULT after SET.");
|
|
2481
|
+
}
|
|
2482
|
+
else if (this.match(TokenType.CASCADE)) {
|
|
2483
|
+
return 'cascade';
|
|
2484
|
+
}
|
|
2485
|
+
else if (this.match(TokenType.RESTRICT)) {
|
|
2486
|
+
return 'restrict';
|
|
2487
|
+
}
|
|
2488
|
+
else if (this.match(TokenType.NO)) {
|
|
2489
|
+
this.consume(TokenType.ACTION, "Expected ACTION after NO.");
|
|
2490
|
+
return 'noAction';
|
|
2491
|
+
}
|
|
2492
|
+
throw this.error(this.peek(), "Expected foreign key action (SET NULL, SET DEFAULT, CASCADE, RESTRICT, NO ACTION).");
|
|
2493
|
+
}
|
|
2494
|
+
/** @internal Parses a comma-separated list of identifiers, optionally with ASC/DESC */
|
|
2495
|
+
identifierList() {
|
|
2496
|
+
const identifiers = [];
|
|
2497
|
+
do {
|
|
2498
|
+
identifiers.push(this.consumeIdentifier(['key', 'action', 'set', 'default', 'check', 'unique', 'references', 'on', 'cascade', 'restrict', 'like'], "Expected identifier in list."));
|
|
2499
|
+
} while (this.match(TokenType.COMMA));
|
|
2500
|
+
return identifiers;
|
|
2501
|
+
}
|
|
2502
|
+
/** @internal Parses a comma-separated list of identifiers, optionally with ASC/DESC */
|
|
2503
|
+
identifierListWithDirection() {
|
|
2504
|
+
const identifiers = [];
|
|
2505
|
+
do {
|
|
2506
|
+
const name = this.consumeIdentifier(['key', 'action', 'set', 'default', 'check', 'unique', 'references', 'on', 'cascade', 'restrict', 'like'], "Expected identifier in list.");
|
|
2507
|
+
const direction = this.match(TokenType.ASC) ? 'asc' : this.match(TokenType.DESC) ? 'desc' : undefined;
|
|
2508
|
+
identifiers.push({ name, direction });
|
|
2509
|
+
} while (this.match(TokenType.COMMA));
|
|
2510
|
+
return identifiers;
|
|
2511
|
+
}
|
|
2512
|
+
// --- Helper method to peek keywords case-insensitively ---
|
|
2513
|
+
peekKeyword(keyword) {
|
|
2514
|
+
if (this.isAtEnd())
|
|
2515
|
+
return false;
|
|
2516
|
+
const token = this.peek();
|
|
2517
|
+
// The keyword lookup string should be uppercase to match TokenType enum keys (e.g., TokenType.SELECT)
|
|
2518
|
+
const keywordKey = keyword.toUpperCase();
|
|
2519
|
+
const expectedTokenType = TokenType[keywordKey];
|
|
2520
|
+
// Check if the current token's type is the expected specific keyword TokenType.
|
|
2521
|
+
// This assumes the lexer has already correctly typed true keywords.
|
|
2522
|
+
if (expectedTokenType !== undefined && token.type === expectedTokenType) {
|
|
2523
|
+
return true;
|
|
2524
|
+
}
|
|
2525
|
+
// Fallback: if the token is a generic IDENTIFIER, check if its lexeme matches the keyword.
|
|
2526
|
+
// This handles contextual keywords like FIRST, LAST that aren't reserved keywords.
|
|
2527
|
+
if (token.type === TokenType.IDENTIFIER && token.lexeme.toUpperCase() === keywordKey) {
|
|
2528
|
+
return true;
|
|
2529
|
+
}
|
|
2530
|
+
return false;
|
|
2531
|
+
}
|
|
2532
|
+
// --- Helper method to match keywords case-insensitively ---
|
|
2533
|
+
matchKeyword(keyword) {
|
|
2534
|
+
if (this.isAtEnd())
|
|
2535
|
+
return false;
|
|
2536
|
+
if (this.peekKeyword(keyword)) {
|
|
2537
|
+
this.advance();
|
|
2538
|
+
return true;
|
|
2539
|
+
}
|
|
2540
|
+
return false;
|
|
2541
|
+
}
|
|
2542
|
+
// --- Helper method to consume keywords case-insensitively ---
|
|
2543
|
+
consumeKeyword(keyword, message) {
|
|
2544
|
+
if (this.peekKeyword(keyword)) {
|
|
2545
|
+
return this.advance();
|
|
2546
|
+
}
|
|
2547
|
+
throw this.error(this.peek(), message);
|
|
2548
|
+
}
|
|
2549
|
+
/** Parses the list of operations for CHECK ON */
|
|
2550
|
+
parseRowOpList() {
|
|
2551
|
+
const operations = [];
|
|
2552
|
+
// Parse operations in a comma-separated list
|
|
2553
|
+
do {
|
|
2554
|
+
if (this.match(TokenType.INSERT)) {
|
|
2555
|
+
operations.push('insert');
|
|
2556
|
+
}
|
|
2557
|
+
else if (this.match(TokenType.UPDATE)) {
|
|
2558
|
+
operations.push('update');
|
|
2559
|
+
}
|
|
2560
|
+
else if (this.match(TokenType.DELETE)) {
|
|
2561
|
+
operations.push('delete');
|
|
2562
|
+
}
|
|
2563
|
+
else {
|
|
2564
|
+
throw this.error(this.peek(), "Expected INSERT, UPDATE, or DELETE after ON.");
|
|
2565
|
+
}
|
|
2566
|
+
} while (this.match(TokenType.COMMA));
|
|
2567
|
+
// Optional: Check for duplicates? The design allows them but ignores them.
|
|
2568
|
+
return operations;
|
|
2569
|
+
}
|
|
2570
|
+
/**
|
|
2571
|
+
* Parses a CASE expression
|
|
2572
|
+
* CASE [base_expr] WHEN cond THEN result ... [ELSE else_result] END
|
|
2573
|
+
* CASE WHEN cond THEN result ... [ELSE else_result] END
|
|
2574
|
+
*/
|
|
2575
|
+
parseCaseExpression(startToken) {
|
|
2576
|
+
let baseExpr;
|
|
2577
|
+
const whenThenClauses = [];
|
|
2578
|
+
let elseExpr;
|
|
2579
|
+
let endToken = startToken; // Initialize with CASE token
|
|
2580
|
+
// Check if it's CASE expr WHEN ... or CASE WHEN ...
|
|
2581
|
+
if (!this.peekKeyword('WHEN')) { // Changed from checkKeyword
|
|
2582
|
+
baseExpr = this.expression();
|
|
2583
|
+
}
|
|
2584
|
+
while (this.matchKeyword('WHEN')) {
|
|
2585
|
+
const whenCondition = this.expression();
|
|
2586
|
+
this.consumeKeyword('THEN', "Expected 'THEN' after WHEN condition in CASE expression.");
|
|
2587
|
+
const thenResult = this.expression();
|
|
2588
|
+
whenThenClauses.push({ when: whenCondition, then: thenResult });
|
|
2589
|
+
endToken = this.previous(); // Update endToken to the end of the THEN expression
|
|
2590
|
+
}
|
|
2591
|
+
if (whenThenClauses.length === 0) {
|
|
2592
|
+
throw this.error(this.peek(), "CASE expression must have at least one WHEN clause.");
|
|
2593
|
+
}
|
|
2594
|
+
if (this.matchKeyword('ELSE')) {
|
|
2595
|
+
elseExpr = this.expression();
|
|
2596
|
+
endToken = this.previous(); // Update endToken to the end of the ELSE expression
|
|
2597
|
+
}
|
|
2598
|
+
endToken = this.consumeKeyword('END', "Expected 'END' to terminate CASE expression.");
|
|
2599
|
+
return {
|
|
2600
|
+
type: 'case',
|
|
2601
|
+
baseExpr,
|
|
2602
|
+
whenThenClauses,
|
|
2603
|
+
elseExpr,
|
|
2604
|
+
loc: _createLoc(startToken, endToken),
|
|
2605
|
+
};
|
|
2606
|
+
}
|
|
2607
|
+
// Helper to check if a token lexeme is a common type name keyword for CAST
|
|
2608
|
+
isTypeNameKeyword(lexeme) {
|
|
2609
|
+
const typeKeywords = ['TEXT', 'INTEGER', 'REAL', 'BLOB', 'NUMERIC', 'VARCHAR', 'CHAR', 'DATE', 'DATETIME', 'BOOLEAN', 'INT'];
|
|
2610
|
+
return typeKeywords.includes(lexeme.toUpperCase());
|
|
2611
|
+
}
|
|
2612
|
+
statementSupportsWithClause(statement) {
|
|
2613
|
+
return statement.type === 'select' ||
|
|
2614
|
+
statement.type === 'insert' ||
|
|
2615
|
+
statement.type === 'update' ||
|
|
2616
|
+
statement.type === 'delete';
|
|
2617
|
+
}
|
|
2618
|
+
}
|
|
2619
|
+
//# sourceMappingURL=parser.js.map
|