@quereus/quereus 1.3.1 → 2.0.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/dist/src/core/database.js +2 -2
- package/dist/src/core/database.js.map +1 -1
- package/dist/src/emit/ast-stringify.d.ts.map +1 -1
- package/dist/src/emit/ast-stringify.js +36 -2
- package/dist/src/emit/ast-stringify.js.map +1 -1
- package/dist/src/func/builtins/index.d.ts.map +1 -1
- package/dist/src/func/builtins/index.js +2 -1
- package/dist/src/func/builtins/index.js.map +1 -1
- package/dist/src/func/builtins/schema.d.ts +1 -0
- package/dist/src/func/builtins/schema.d.ts.map +1 -1
- package/dist/src/func/builtins/schema.js +83 -9
- package/dist/src/func/builtins/schema.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/parser/ast.d.ts +7 -1
- package/dist/src/parser/ast.d.ts.map +1 -1
- package/dist/src/parser/parser.d.ts +7 -0
- package/dist/src/parser/parser.d.ts.map +1 -1
- package/dist/src/parser/parser.js +196 -52
- package/dist/src/parser/parser.js.map +1 -1
- package/dist/src/planner/building/create-view.d.ts.map +1 -1
- package/dist/src/planner/building/create-view.js +1 -1
- package/dist/src/planner/building/create-view.js.map +1 -1
- package/dist/src/planner/building/foreign-key-builder.d.ts.map +1 -1
- package/dist/src/planner/building/foreign-key-builder.js +7 -3
- package/dist/src/planner/building/foreign-key-builder.js.map +1 -1
- package/dist/src/planner/framework/pass.d.ts.map +1 -1
- package/dist/src/planner/framework/pass.js +2 -0
- package/dist/src/planner/framework/pass.js.map +1 -1
- package/dist/src/planner/framework/registry.d.ts.map +1 -1
- package/dist/src/planner/framework/registry.js +3 -0
- package/dist/src/planner/framework/registry.js.map +1 -1
- package/dist/src/planner/nodes/create-view-node.d.ts +3 -1
- package/dist/src/planner/nodes/create-view-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/create-view-node.js +3 -1
- package/dist/src/planner/nodes/create-view-node.js.map +1 -1
- package/dist/src/planner/optimizer-tuning.d.ts +2 -0
- package/dist/src/planner/optimizer-tuning.d.ts.map +1 -1
- package/dist/src/planner/optimizer-tuning.js.map +1 -1
- package/dist/src/runtime/cache/shared-cache.d.ts.map +1 -1
- package/dist/src/runtime/cache/shared-cache.js +4 -2
- package/dist/src/runtime/cache/shared-cache.js.map +1 -1
- package/dist/src/runtime/emit/create-view.d.ts.map +1 -1
- package/dist/src/runtime/emit/create-view.js +2 -1
- package/dist/src/runtime/emit/create-view.js.map +1 -1
- package/dist/src/runtime/foreign-key-actions.js +1 -1
- package/dist/src/runtime/foreign-key-actions.js.map +1 -1
- package/dist/src/schema/catalog.d.ts.map +1 -1
- package/dist/src/schema/catalog.js +24 -1
- package/dist/src/schema/catalog.js.map +1 -1
- package/dist/src/schema/column.d.ts +3 -0
- package/dist/src/schema/column.d.ts.map +1 -1
- package/dist/src/schema/column.js.map +1 -1
- package/dist/src/schema/manager.d.ts +16 -0
- package/dist/src/schema/manager.d.ts.map +1 -1
- package/dist/src/schema/manager.js +48 -6
- package/dist/src/schema/manager.js.map +1 -1
- package/dist/src/schema/schema-hasher.d.ts.map +1 -1
- package/dist/src/schema/schema-hasher.js +46 -2
- package/dist/src/schema/schema-hasher.js.map +1 -1
- package/dist/src/schema/table.d.ts +12 -2
- package/dist/src/schema/table.d.ts.map +1 -1
- package/dist/src/schema/table.js +4 -0
- package/dist/src/schema/table.js.map +1 -1
- package/dist/src/schema/view.d.ts +3 -0
- package/dist/src/schema/view.d.ts.map +1 -1
- package/dist/src/util/comparison.d.ts.map +1 -1
- package/dist/src/util/comparison.js +14 -21
- package/dist/src/util/comparison.js.map +1 -1
- package/package.json +5 -3
|
@@ -29,6 +29,13 @@ function _createLoc(startToken, endToken) {
|
|
|
29
29
|
},
|
|
30
30
|
};
|
|
31
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* IMPORTANT: Any changes to parsed syntax must also be reflected in the corresponding emitters:
|
|
34
|
+
* - packages/quereus/src/emit/ast-stringify.ts (AST-to-SQL string conversion)
|
|
35
|
+
* - packages/quereus/src/schema/catalog.ts (DDL generation for catalog/hashing)
|
|
36
|
+
* - packages/quereus-store/src/common/ddl-generator.ts (DDL generation for persistence)
|
|
37
|
+
* If only the parser is updated, SQL round-trips and persisted schemas will silently lose the new syntax.
|
|
38
|
+
*/
|
|
32
39
|
export class Parser {
|
|
33
40
|
tokens = [];
|
|
34
41
|
current = 0;
|
|
@@ -2051,15 +2058,26 @@ export class Parser {
|
|
|
2051
2058
|
this.consume(TokenType.RPAREN, "Expected ')' after module arguments.");
|
|
2052
2059
|
}
|
|
2053
2060
|
}
|
|
2054
|
-
// Parse
|
|
2061
|
+
// Parse trailing WITH clauses (CONTEXT, TAGS) in any order
|
|
2055
2062
|
let contextDefinitions;
|
|
2056
|
-
|
|
2063
|
+
let tags;
|
|
2064
|
+
while (this.matchKeyword('WITH')) {
|
|
2057
2065
|
if (this.matchKeyword('CONTEXT')) {
|
|
2066
|
+
if (contextDefinitions) {
|
|
2067
|
+
throw this.error(this.previous(), "Duplicate WITH CONTEXT clause");
|
|
2068
|
+
}
|
|
2058
2069
|
contextDefinitions = this.parseMutationContextDefinitions();
|
|
2059
2070
|
}
|
|
2071
|
+
else if (this.matchKeyword('TAGS')) {
|
|
2072
|
+
if (tags) {
|
|
2073
|
+
throw this.error(this.previous(), "Duplicate WITH TAGS clause");
|
|
2074
|
+
}
|
|
2075
|
+
tags = this.parseTags();
|
|
2076
|
+
}
|
|
2060
2077
|
else {
|
|
2061
|
-
// Not a WITH
|
|
2078
|
+
// Not a recognized WITH clause, backtrack
|
|
2062
2079
|
this.current--;
|
|
2080
|
+
break;
|
|
2063
2081
|
}
|
|
2064
2082
|
}
|
|
2065
2083
|
return {
|
|
@@ -2072,6 +2090,7 @@ export class Parser {
|
|
|
2072
2090
|
moduleName,
|
|
2073
2091
|
moduleArgs,
|
|
2074
2092
|
contextDefinitions,
|
|
2093
|
+
tags,
|
|
2075
2094
|
loc: _createLoc(startToken, this.previous()),
|
|
2076
2095
|
};
|
|
2077
2096
|
}
|
|
@@ -2101,6 +2120,16 @@ export class Parser {
|
|
|
2101
2120
|
if (this.matchKeyword('WHERE')) {
|
|
2102
2121
|
where = this.expression();
|
|
2103
2122
|
}
|
|
2123
|
+
// Parse optional WITH TAGS
|
|
2124
|
+
let tags;
|
|
2125
|
+
if (this.matchKeyword('WITH')) {
|
|
2126
|
+
if (this.matchKeyword('TAGS')) {
|
|
2127
|
+
tags = this.parseTags();
|
|
2128
|
+
}
|
|
2129
|
+
else {
|
|
2130
|
+
this.current--;
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2104
2133
|
return {
|
|
2105
2134
|
type: 'createIndex',
|
|
2106
2135
|
index,
|
|
@@ -2109,6 +2138,7 @@ export class Parser {
|
|
|
2109
2138
|
columns,
|
|
2110
2139
|
where,
|
|
2111
2140
|
isUnique,
|
|
2141
|
+
tags,
|
|
2112
2142
|
loc: _createLoc(startToken, this.previous()),
|
|
2113
2143
|
};
|
|
2114
2144
|
}
|
|
@@ -2144,6 +2174,16 @@ export class Parser {
|
|
|
2144
2174
|
this.consumeKeyword('AS', "Expected 'AS' before SELECT statement for CREATE VIEW.");
|
|
2145
2175
|
const selectStartToken = this.consume(TokenType.SELECT, "Expected 'SELECT' after 'AS' in CREATE VIEW.");
|
|
2146
2176
|
const select = this.selectStatement(selectStartToken, withClause);
|
|
2177
|
+
// Parse optional WITH TAGS
|
|
2178
|
+
let tags;
|
|
2179
|
+
if (this.matchKeyword('WITH')) {
|
|
2180
|
+
if (this.matchKeyword('TAGS')) {
|
|
2181
|
+
tags = this.parseTags();
|
|
2182
|
+
}
|
|
2183
|
+
else {
|
|
2184
|
+
this.current--;
|
|
2185
|
+
}
|
|
2186
|
+
}
|
|
2147
2187
|
return {
|
|
2148
2188
|
type: 'createView',
|
|
2149
2189
|
view,
|
|
@@ -2151,6 +2191,7 @@ export class Parser {
|
|
|
2151
2191
|
columns,
|
|
2152
2192
|
select,
|
|
2153
2193
|
isTemporary,
|
|
2194
|
+
tags,
|
|
2154
2195
|
loc: _createLoc(startToken, this.previous()),
|
|
2155
2196
|
};
|
|
2156
2197
|
}
|
|
@@ -2478,15 +2519,25 @@ export class Parser {
|
|
|
2478
2519
|
else {
|
|
2479
2520
|
this.consume(TokenType.RPAREN, "Expected ')' after table definition.");
|
|
2480
2521
|
}
|
|
2481
|
-
// Parse
|
|
2522
|
+
// Parse trailing WITH clauses (CONTEXT, TAGS) in any order
|
|
2482
2523
|
let contextDefinitions;
|
|
2483
|
-
|
|
2524
|
+
let tags;
|
|
2525
|
+
while (this.matchKeyword('WITH')) {
|
|
2484
2526
|
if (this.matchKeyword('CONTEXT')) {
|
|
2527
|
+
if (contextDefinitions) {
|
|
2528
|
+
throw this.error(this.previous(), "Duplicate WITH CONTEXT clause");
|
|
2529
|
+
}
|
|
2485
2530
|
contextDefinitions = this.parseMutationContextDefinitions();
|
|
2486
2531
|
}
|
|
2532
|
+
else if (this.matchKeyword('TAGS')) {
|
|
2533
|
+
if (tags) {
|
|
2534
|
+
throw this.error(this.previous(), "Duplicate WITH TAGS clause");
|
|
2535
|
+
}
|
|
2536
|
+
tags = this.parseTags();
|
|
2537
|
+
}
|
|
2487
2538
|
else {
|
|
2488
|
-
// Not a WITH CONTEXT clause, backtrack
|
|
2489
2539
|
this.current--;
|
|
2540
|
+
break;
|
|
2490
2541
|
}
|
|
2491
2542
|
}
|
|
2492
2543
|
// Build the CREATE TABLE AST node for this declared table
|
|
@@ -2499,7 +2550,8 @@ export class Parser {
|
|
|
2499
2550
|
isTemporary: false,
|
|
2500
2551
|
moduleName,
|
|
2501
2552
|
moduleArgs,
|
|
2502
|
-
contextDefinitions
|
|
2553
|
+
contextDefinitions,
|
|
2554
|
+
tags
|
|
2503
2555
|
};
|
|
2504
2556
|
return { type: 'declaredTable', tableStmt };
|
|
2505
2557
|
}
|
|
@@ -2510,13 +2562,24 @@ export class Parser {
|
|
|
2510
2562
|
this.consume(TokenType.LPAREN, "Expected '(' before index columns.");
|
|
2511
2563
|
const columns = this.indexedColumnList();
|
|
2512
2564
|
this.consume(TokenType.RPAREN, "Expected ')' after index columns.");
|
|
2565
|
+
// Parse optional WITH TAGS
|
|
2566
|
+
let tags;
|
|
2567
|
+
if (this.matchKeyword('WITH')) {
|
|
2568
|
+
if (this.matchKeyword('TAGS')) {
|
|
2569
|
+
tags = this.parseTags();
|
|
2570
|
+
}
|
|
2571
|
+
else {
|
|
2572
|
+
this.current--;
|
|
2573
|
+
}
|
|
2574
|
+
}
|
|
2513
2575
|
const indexStmt = {
|
|
2514
2576
|
type: 'createIndex',
|
|
2515
2577
|
index: { type: 'identifier', name: indexName },
|
|
2516
2578
|
table: { type: 'identifier', name: tableName },
|
|
2517
2579
|
ifNotExists: false,
|
|
2518
2580
|
columns,
|
|
2519
|
-
isUnique: false
|
|
2581
|
+
isUnique: false,
|
|
2582
|
+
tags
|
|
2520
2583
|
};
|
|
2521
2584
|
return { type: 'declaredIndex', indexStmt };
|
|
2522
2585
|
}
|
|
@@ -2530,13 +2593,24 @@ export class Parser {
|
|
|
2530
2593
|
this.consumeKeyword('AS', "Expected AS before SELECT in view declaration.");
|
|
2531
2594
|
const selTok = this.consume(TokenType.SELECT, "Expected SELECT after AS in view declaration.");
|
|
2532
2595
|
const select = this.selectStatement(selTok);
|
|
2596
|
+
// Parse optional WITH TAGS
|
|
2597
|
+
let tags;
|
|
2598
|
+
if (this.matchKeyword('WITH')) {
|
|
2599
|
+
if (this.matchKeyword('TAGS')) {
|
|
2600
|
+
tags = this.parseTags();
|
|
2601
|
+
}
|
|
2602
|
+
else {
|
|
2603
|
+
this.current--;
|
|
2604
|
+
}
|
|
2605
|
+
}
|
|
2533
2606
|
const viewStmt = {
|
|
2534
2607
|
type: 'createView',
|
|
2535
2608
|
view: { type: 'identifier', name: viewName },
|
|
2536
2609
|
ifNotExists: false,
|
|
2537
2610
|
columns,
|
|
2538
2611
|
select,
|
|
2539
|
-
isTemporary: false
|
|
2612
|
+
isTemporary: false,
|
|
2613
|
+
tags
|
|
2540
2614
|
};
|
|
2541
2615
|
return { type: 'declaredView', viewStmt };
|
|
2542
2616
|
}
|
|
@@ -2842,7 +2916,17 @@ export class Parser {
|
|
|
2842
2916
|
}
|
|
2843
2917
|
}
|
|
2844
2918
|
const constraints = this.columnConstraintList();
|
|
2845
|
-
|
|
2919
|
+
// Parse optional column-level WITH TAGS
|
|
2920
|
+
let tags;
|
|
2921
|
+
if (this.matchKeyword('WITH')) {
|
|
2922
|
+
if (this.matchKeyword('TAGS')) {
|
|
2923
|
+
tags = this.parseTags();
|
|
2924
|
+
}
|
|
2925
|
+
else {
|
|
2926
|
+
this.current--;
|
|
2927
|
+
}
|
|
2928
|
+
}
|
|
2929
|
+
return { name, dataType, constraints, tags };
|
|
2846
2930
|
}
|
|
2847
2931
|
/** @internal Parses mutation context variable definitions: WITH CONTEXT (var type [NULL], ...) */
|
|
2848
2932
|
parseMutationContextDefinitions() {
|
|
@@ -2914,6 +2998,7 @@ export class Parser {
|
|
|
2914
2998
|
parseTrailingWithClauses() {
|
|
2915
2999
|
let contextValues;
|
|
2916
3000
|
let schemaPath;
|
|
3001
|
+
let tags;
|
|
2917
3002
|
// Keep trying to parse WITH clauses until we don't find any more
|
|
2918
3003
|
while (this.matchKeyword('WITH')) {
|
|
2919
3004
|
if (this.matchKeyword('CONTEXT')) {
|
|
@@ -2933,13 +3018,64 @@ export class Parser {
|
|
|
2933
3018
|
} while (this.match(TokenType.COMMA));
|
|
2934
3019
|
schemaPath = schemas;
|
|
2935
3020
|
}
|
|
3021
|
+
else if (this.matchKeyword('TAGS')) {
|
|
3022
|
+
if (tags) {
|
|
3023
|
+
throw this.error(this.previous(), "Duplicate WITH TAGS clause");
|
|
3024
|
+
}
|
|
3025
|
+
tags = this.parseTags();
|
|
3026
|
+
}
|
|
2936
3027
|
else {
|
|
2937
|
-
// Not a
|
|
3028
|
+
// Not a recognized WITH clause, backtrack
|
|
2938
3029
|
this.current--;
|
|
2939
3030
|
break;
|
|
2940
3031
|
}
|
|
2941
3032
|
}
|
|
2942
|
-
return { contextValues, schemaPath };
|
|
3033
|
+
return { contextValues, schemaPath, tags };
|
|
3034
|
+
}
|
|
3035
|
+
/**
|
|
3036
|
+
* @internal Parses a tags list: (key = value, ...)
|
|
3037
|
+
* Called after the TAGS keyword has been consumed.
|
|
3038
|
+
* Keys are identifiers, values are literals (string, number, boolean via TRUE/FALSE, NULL).
|
|
3039
|
+
*/
|
|
3040
|
+
parseTags() {
|
|
3041
|
+
this.consume(TokenType.LPAREN, "Expected '(' after TAGS.");
|
|
3042
|
+
const tags = {};
|
|
3043
|
+
if (!this.check(TokenType.RPAREN)) {
|
|
3044
|
+
do {
|
|
3045
|
+
const key = this.consumeIdentifier("Expected tag key identifier.");
|
|
3046
|
+
this.consume(TokenType.EQUAL, `Expected '=' after tag key '${key}'.`);
|
|
3047
|
+
const value = this.parseTagValue();
|
|
3048
|
+
tags[key] = value;
|
|
3049
|
+
} while (this.match(TokenType.COMMA));
|
|
3050
|
+
}
|
|
3051
|
+
this.consume(TokenType.RPAREN, "Expected ')' after tag list.");
|
|
3052
|
+
return tags;
|
|
3053
|
+
}
|
|
3054
|
+
/** @internal Parses a tag value: string, number, TRUE, FALSE, or NULL */
|
|
3055
|
+
parseTagValue() {
|
|
3056
|
+
if (this.match(TokenType.STRING)) {
|
|
3057
|
+
return this.previous().literal;
|
|
3058
|
+
}
|
|
3059
|
+
if (this.match(TokenType.INTEGER) || this.match(TokenType.FLOAT)) {
|
|
3060
|
+
return this.previous().literal;
|
|
3061
|
+
}
|
|
3062
|
+
if (this.match(TokenType.TRUE)) {
|
|
3063
|
+
return true;
|
|
3064
|
+
}
|
|
3065
|
+
if (this.match(TokenType.FALSE)) {
|
|
3066
|
+
return false;
|
|
3067
|
+
}
|
|
3068
|
+
if (this.match(TokenType.NULL)) {
|
|
3069
|
+
return null;
|
|
3070
|
+
}
|
|
3071
|
+
// Allow negative numbers
|
|
3072
|
+
if (this.match(TokenType.MINUS)) {
|
|
3073
|
+
if (this.match(TokenType.INTEGER) || this.match(TokenType.FLOAT)) {
|
|
3074
|
+
return -this.previous().literal;
|
|
3075
|
+
}
|
|
3076
|
+
throw this.error(this.peek(), "Expected number after '-' in tag value.");
|
|
3077
|
+
}
|
|
3078
|
+
throw this.error(this.peek(), "Expected tag value (string, number, true, false, or null).");
|
|
2943
3079
|
}
|
|
2944
3080
|
/** @internal Parses column constraints */
|
|
2945
3081
|
columnConstraintList() {
|
|
@@ -2971,6 +3107,7 @@ export class Parser {
|
|
|
2971
3107
|
name = this.consumeIdentifier("Expected constraint name after CONSTRAINT.");
|
|
2972
3108
|
endToken = this.previous();
|
|
2973
3109
|
}
|
|
3110
|
+
let result;
|
|
2974
3111
|
if (this.match(TokenType.PRIMARY)) {
|
|
2975
3112
|
this.consume(TokenType.KEY, "Expected KEY after PRIMARY.");
|
|
2976
3113
|
const direction = this.match(TokenType.ASC) ? 'asc' : this.match(TokenType.DESC) ? 'desc' : undefined;
|
|
@@ -2978,57 +3115,48 @@ export class Parser {
|
|
|
2978
3115
|
endToken = this.previous();
|
|
2979
3116
|
const onConflict = this.parseConflictClause();
|
|
2980
3117
|
if (onConflict)
|
|
2981
|
-
endToken = this.previous();
|
|
3118
|
+
endToken = this.previous();
|
|
2982
3119
|
if (this.check(TokenType.AUTOINCREMENT)) {
|
|
2983
3120
|
throw this.error(this.peek(), 'AUTOINCREMENT is not supported. Quereus uses key-based addressing without implicit side-effects.');
|
|
2984
3121
|
}
|
|
2985
|
-
|
|
3122
|
+
result = { type: 'primaryKey', name, onConflict, direction, loc: _createLoc(startToken, endToken) };
|
|
2986
3123
|
}
|
|
2987
3124
|
else if (this.match(TokenType.NOT)) {
|
|
2988
3125
|
this.consume(TokenType.NULL, "Expected NULL after NOT.");
|
|
2989
3126
|
endToken = this.previous();
|
|
2990
3127
|
const onConflict = this.parseConflictClause();
|
|
2991
3128
|
if (onConflict)
|
|
2992
|
-
endToken = this.previous();
|
|
2993
|
-
|
|
3129
|
+
endToken = this.previous();
|
|
3130
|
+
result = { type: 'notNull', name, onConflict, loc: _createLoc(startToken, endToken) };
|
|
2994
3131
|
}
|
|
2995
3132
|
else if (this.match(TokenType.NULL)) {
|
|
2996
3133
|
endToken = this.previous();
|
|
2997
3134
|
const onConflict = this.parseConflictClause();
|
|
2998
3135
|
if (onConflict)
|
|
2999
|
-
endToken = this.previous();
|
|
3000
|
-
|
|
3136
|
+
endToken = this.previous();
|
|
3137
|
+
result = { type: 'null', name, onConflict, loc: _createLoc(startToken, endToken) };
|
|
3001
3138
|
}
|
|
3002
3139
|
else if (this.match(TokenType.UNIQUE)) {
|
|
3003
3140
|
endToken = this.previous();
|
|
3004
3141
|
const onConflict = this.parseConflictClause();
|
|
3005
3142
|
if (onConflict)
|
|
3006
|
-
endToken = this.previous();
|
|
3007
|
-
|
|
3143
|
+
endToken = this.previous();
|
|
3144
|
+
result = { type: 'unique', name, onConflict, loc: _createLoc(startToken, endToken) };
|
|
3008
3145
|
}
|
|
3009
3146
|
else if (this.match(TokenType.CHECK)) {
|
|
3010
|
-
// --- Parse optional ON clause before parentheses --- //
|
|
3011
3147
|
let operations;
|
|
3012
3148
|
if (this.matchKeyword('ON')) {
|
|
3013
3149
|
operations = this.parseRowOpList();
|
|
3014
3150
|
}
|
|
3015
|
-
// --- End Parse ON clause --- //
|
|
3016
3151
|
this.consume(TokenType.LPAREN, "Expected '(' after CHECK.");
|
|
3017
3152
|
const expr = this.expression();
|
|
3018
3153
|
endToken = this.consume(TokenType.RPAREN, "Expected ')' after CHECK expression.");
|
|
3019
|
-
|
|
3020
|
-
return {
|
|
3021
|
-
type: 'check',
|
|
3022
|
-
name,
|
|
3023
|
-
expr,
|
|
3024
|
-
operations,
|
|
3025
|
-
loc: _createLoc(startToken, endToken)
|
|
3026
|
-
};
|
|
3154
|
+
result = { type: 'check', name, expr, operations, loc: _createLoc(startToken, endToken) };
|
|
3027
3155
|
}
|
|
3028
3156
|
else if (this.match(TokenType.DEFAULT)) {
|
|
3029
3157
|
const expr = this.expression();
|
|
3030
3158
|
endToken = this.previous();
|
|
3031
|
-
|
|
3159
|
+
result = { type: 'default', name, expr, loc: _createLoc(startToken, endToken) };
|
|
3032
3160
|
}
|
|
3033
3161
|
else if (this.match(TokenType.COLLATE)) {
|
|
3034
3162
|
if (!this.check(TokenType.IDENTIFIER)) {
|
|
@@ -3036,12 +3164,12 @@ export class Parser {
|
|
|
3036
3164
|
}
|
|
3037
3165
|
const collation = this.getIdentifierValue(this.advance());
|
|
3038
3166
|
endToken = this.previous();
|
|
3039
|
-
|
|
3167
|
+
result = { type: 'collate', name, collation, loc: _createLoc(startToken, endToken) };
|
|
3040
3168
|
}
|
|
3041
3169
|
else if (this.match(TokenType.REFERENCES)) {
|
|
3042
3170
|
const fkClause = this.foreignKeyClause();
|
|
3043
|
-
endToken = this.previous();
|
|
3044
|
-
|
|
3171
|
+
endToken = this.previous();
|
|
3172
|
+
result = { type: 'foreignKey', name, foreignKey: fkClause, loc: _createLoc(startToken, endToken) };
|
|
3045
3173
|
}
|
|
3046
3174
|
else if (this.match(TokenType.GENERATED)) {
|
|
3047
3175
|
this.consume(TokenType.ALWAYS, "Expected ALWAYS after GENERATED.");
|
|
@@ -3058,9 +3186,21 @@ export class Parser {
|
|
|
3058
3186
|
else if (this.match(TokenType.VIRTUAL)) {
|
|
3059
3187
|
endToken = this.previous();
|
|
3060
3188
|
}
|
|
3061
|
-
|
|
3189
|
+
result = { type: 'generated', name, generated: { expr, stored }, loc: _createLoc(startToken, endToken) };
|
|
3190
|
+
}
|
|
3191
|
+
else {
|
|
3192
|
+
throw this.error(this.peek(), "Expected column constraint type (PRIMARY KEY, NOT NULL, UNIQUE, CHECK, DEFAULT, COLLATE, REFERENCES, GENERATED).");
|
|
3193
|
+
}
|
|
3194
|
+
// Parse optional trailing WITH TAGS for the constraint
|
|
3195
|
+
if (this.matchKeyword('WITH')) {
|
|
3196
|
+
if (this.matchKeyword('TAGS')) {
|
|
3197
|
+
result.tags = this.parseTags();
|
|
3198
|
+
}
|
|
3199
|
+
else {
|
|
3200
|
+
this.current--;
|
|
3201
|
+
}
|
|
3062
3202
|
}
|
|
3063
|
-
|
|
3203
|
+
return result;
|
|
3064
3204
|
}
|
|
3065
3205
|
/** @internal Parses a table constraint */
|
|
3066
3206
|
tableConstraint() {
|
|
@@ -3071,6 +3211,7 @@ export class Parser {
|
|
|
3071
3211
|
name = this.consumeIdentifier("Expected constraint name after CONSTRAINT.");
|
|
3072
3212
|
endToken = this.previous();
|
|
3073
3213
|
}
|
|
3214
|
+
let result;
|
|
3074
3215
|
if (this.match(TokenType.PRIMARY)) {
|
|
3075
3216
|
this.consume(TokenType.KEY, "Expected KEY after PRIMARY.");
|
|
3076
3217
|
this.consume(TokenType.LPAREN, "Expected '(' before PRIMARY KEY columns.");
|
|
@@ -3083,7 +3224,7 @@ export class Parser {
|
|
|
3083
3224
|
const onConflict = this.parseConflictClause();
|
|
3084
3225
|
if (onConflict)
|
|
3085
3226
|
endToken = this.previous();
|
|
3086
|
-
|
|
3227
|
+
result = { type: 'primaryKey', name, columns, onConflict, loc: _createLoc(startToken, endToken) };
|
|
3087
3228
|
}
|
|
3088
3229
|
else if (this.match(TokenType.UNIQUE)) {
|
|
3089
3230
|
this.consume(TokenType.LPAREN, "Expected '(' before UNIQUE columns.");
|
|
@@ -3093,26 +3234,17 @@ export class Parser {
|
|
|
3093
3234
|
const onConflict = this.parseConflictClause();
|
|
3094
3235
|
if (onConflict)
|
|
3095
3236
|
endToken = this.previous();
|
|
3096
|
-
|
|
3237
|
+
result = { type: 'unique', name, columns, onConflict, loc: _createLoc(startToken, endToken) };
|
|
3097
3238
|
}
|
|
3098
3239
|
else if (this.match(TokenType.CHECK)) {
|
|
3099
|
-
// --- Parse optional ON clause before parentheses --- //
|
|
3100
3240
|
let operations;
|
|
3101
3241
|
if (this.matchKeyword('ON')) {
|
|
3102
3242
|
operations = this.parseRowOpList();
|
|
3103
3243
|
}
|
|
3104
|
-
// --- End Parse ON clause --- //
|
|
3105
3244
|
this.consume(TokenType.LPAREN, "Expected '(' after CHECK.");
|
|
3106
3245
|
const expr = this.expression();
|
|
3107
3246
|
endToken = this.consume(TokenType.RPAREN, "Expected ')' after CHECK expression.");
|
|
3108
|
-
|
|
3109
|
-
return {
|
|
3110
|
-
type: 'check',
|
|
3111
|
-
name,
|
|
3112
|
-
expr,
|
|
3113
|
-
operations,
|
|
3114
|
-
loc: _createLoc(startToken, endToken)
|
|
3115
|
-
};
|
|
3247
|
+
result = { type: 'check', name, expr, operations, loc: _createLoc(startToken, endToken) };
|
|
3116
3248
|
}
|
|
3117
3249
|
else if (this.match(TokenType.FOREIGN)) {
|
|
3118
3250
|
this.consume(TokenType.KEY, "Expected KEY after FOREIGN.");
|
|
@@ -3120,10 +3252,22 @@ export class Parser {
|
|
|
3120
3252
|
const columns = this.identifierList().map(name => ({ name }));
|
|
3121
3253
|
this.consume(TokenType.RPAREN, "Expected ')' after FOREIGN KEY columns.");
|
|
3122
3254
|
const fkClause = this.foreignKeyClause();
|
|
3123
|
-
endToken = this.previous();
|
|
3124
|
-
|
|
3255
|
+
endToken = this.previous();
|
|
3256
|
+
result = { type: 'foreignKey', name, columns, foreignKey: fkClause, loc: _createLoc(startToken, endToken) };
|
|
3257
|
+
}
|
|
3258
|
+
else {
|
|
3259
|
+
throw this.error(this.peek(), "Expected table constraint type (PRIMARY KEY, UNIQUE, CHECK, FOREIGN KEY).");
|
|
3260
|
+
}
|
|
3261
|
+
// Parse optional trailing WITH TAGS for the constraint
|
|
3262
|
+
if (this.matchKeyword('WITH')) {
|
|
3263
|
+
if (this.matchKeyword('TAGS')) {
|
|
3264
|
+
result.tags = this.parseTags();
|
|
3265
|
+
}
|
|
3266
|
+
else {
|
|
3267
|
+
this.current--;
|
|
3268
|
+
}
|
|
3125
3269
|
}
|
|
3126
|
-
|
|
3270
|
+
return result;
|
|
3127
3271
|
}
|
|
3128
3272
|
/** @internal Parses a foreign key clause (REFERENCES may already be consumed by caller) */
|
|
3129
3273
|
foreignKeyClause() {
|
|
@@ -3223,7 +3367,7 @@ export class Parser {
|
|
|
3223
3367
|
}
|
|
3224
3368
|
else if (this.match(TokenType.NO)) {
|
|
3225
3369
|
this.consume(TokenType.ACTION, "Expected ACTION after NO.");
|
|
3226
|
-
return '
|
|
3370
|
+
return 'ignore';
|
|
3227
3371
|
}
|
|
3228
3372
|
throw this.error(this.peek(), "Expected foreign key action (SET NULL, SET DEFAULT, CASCADE, RESTRICT, NO ACTION).");
|
|
3229
3373
|
}
|