squirreling 0.2.3 → 0.2.4
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/package.json +1 -1
- package/src/execute/expression.js +18 -0
- package/src/parse/expression.js +81 -14
- package/src/types.d.ts +9 -2
package/package.json
CHANGED
|
@@ -190,6 +190,24 @@ export function evaluateExpr(node, row) {
|
|
|
190
190
|
throw new Error('Unsupported CAST to type ' + node.toType)
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
+
// IN and NOT IN with value lists
|
|
194
|
+
if (node.type === 'in valuelist') {
|
|
195
|
+
const exprVal = evaluateExpr(node.expr, row)
|
|
196
|
+
for (const valueNode of node.values) {
|
|
197
|
+
const val = evaluateExpr(valueNode, row)
|
|
198
|
+
if (exprVal === val) return true
|
|
199
|
+
}
|
|
200
|
+
return false
|
|
201
|
+
}
|
|
202
|
+
if (node.type === 'not in valuelist') {
|
|
203
|
+
const exprVal = evaluateExpr(node.expr, row)
|
|
204
|
+
for (const valueNode of node.values) {
|
|
205
|
+
const val = evaluateExpr(valueNode, row)
|
|
206
|
+
if (exprVal === val) return false
|
|
207
|
+
}
|
|
208
|
+
return true
|
|
209
|
+
}
|
|
210
|
+
|
|
193
211
|
// IN and NOT IN with subqueries
|
|
194
212
|
if (node.type === 'in') {
|
|
195
213
|
throw new Error('WHERE IN with subqueries is not yet supported.')
|
package/src/parse/expression.js
CHANGED
|
@@ -30,6 +30,21 @@ export function parsePrimary(c) {
|
|
|
30
30
|
if (tok.type === 'identifier') {
|
|
31
31
|
const next = c.peek(1)
|
|
32
32
|
|
|
33
|
+
// CAST expression
|
|
34
|
+
if (tok.value === 'CAST' && next.type === 'paren' && next.value === '(') {
|
|
35
|
+
c.consume() // CAST
|
|
36
|
+
c.consume() // '('
|
|
37
|
+
const expr = parseExpression(c)
|
|
38
|
+
c.expect('keyword', 'AS')
|
|
39
|
+
const typeTok = c.expectIdentifier()
|
|
40
|
+
c.expect('paren', ')')
|
|
41
|
+
return {
|
|
42
|
+
type: 'cast',
|
|
43
|
+
expr,
|
|
44
|
+
toType: typeTok.value,
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
33
48
|
// function call
|
|
34
49
|
if (next.type === 'paren' && next.value === '(') {
|
|
35
50
|
const funcName = tok.value
|
|
@@ -281,28 +296,80 @@ function parseComparison(c) {
|
|
|
281
296
|
if (nextTok.type === 'keyword' && nextTok.value === 'IN') {
|
|
282
297
|
c.consume() // NOT
|
|
283
298
|
c.consume() // IN
|
|
284
|
-
|
|
285
|
-
|
|
299
|
+
|
|
300
|
+
// Check if it's a subquery or a list of values by peeking ahead
|
|
301
|
+
// parseSubquery expects to consume the opening paren itself
|
|
302
|
+
const parenTok = c.current()
|
|
303
|
+
if (parenTok.type !== 'paren' || parenTok.value !== '(') {
|
|
304
|
+
throw new Error('Expected ( after IN')
|
|
286
305
|
}
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
306
|
+
const peekTok = c.peek(1)
|
|
307
|
+
if (peekTok.type === 'keyword' && peekTok.value === 'SELECT') {
|
|
308
|
+
// Subquery - let parseSubquery handle the parens
|
|
309
|
+
if (!c.parseSubquery) {
|
|
310
|
+
throw new Error('Subquery parsing not available in this context')
|
|
311
|
+
}
|
|
312
|
+
const subquery = c.parseSubquery()
|
|
313
|
+
return {
|
|
314
|
+
type: 'not in',
|
|
315
|
+
expr: left,
|
|
316
|
+
subquery,
|
|
317
|
+
}
|
|
318
|
+
} else {
|
|
319
|
+
// Parse list of values - we handle the parens
|
|
320
|
+
c.consume() // '('
|
|
321
|
+
/** @type {ExprNode[]} */
|
|
322
|
+
const values = []
|
|
323
|
+
while (true) {
|
|
324
|
+
values.push(parseExpression(c))
|
|
325
|
+
if (!c.match('comma')) break
|
|
326
|
+
}
|
|
327
|
+
c.expect('paren', ')')
|
|
328
|
+
return {
|
|
329
|
+
type: 'not in valuelist',
|
|
330
|
+
expr: left,
|
|
331
|
+
values,
|
|
332
|
+
}
|
|
292
333
|
}
|
|
293
334
|
}
|
|
294
335
|
}
|
|
295
336
|
|
|
296
337
|
if (tok.type === 'keyword' && tok.value === 'IN') {
|
|
297
338
|
c.consume() // IN
|
|
298
|
-
|
|
299
|
-
|
|
339
|
+
|
|
340
|
+
// Check if it's a subquery or a list of values by peeking ahead
|
|
341
|
+
// parseSubquery expects to consume the opening paren itself
|
|
342
|
+
const parenTok = c.current()
|
|
343
|
+
if (parenTok.type !== 'paren' || parenTok.value !== '(') {
|
|
344
|
+
throw new Error('Expected ( after IN')
|
|
300
345
|
}
|
|
301
|
-
const
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
346
|
+
const peekTok = c.peek(1)
|
|
347
|
+
if (peekTok.type === 'keyword' && peekTok.value === 'SELECT') {
|
|
348
|
+
// Subquery - let parseSubquery handle the parens
|
|
349
|
+
if (!c.parseSubquery) {
|
|
350
|
+
throw new Error('Subquery parsing not available in this context')
|
|
351
|
+
}
|
|
352
|
+
const subquery = c.parseSubquery()
|
|
353
|
+
return {
|
|
354
|
+
type: 'in',
|
|
355
|
+
expr: left,
|
|
356
|
+
subquery,
|
|
357
|
+
}
|
|
358
|
+
} else {
|
|
359
|
+
// Parse list of values - we handle the parens
|
|
360
|
+
c.consume() // '('
|
|
361
|
+
/** @type {ExprNode[]} */
|
|
362
|
+
const values = []
|
|
363
|
+
while (true) {
|
|
364
|
+
values.push(parseExpression(c))
|
|
365
|
+
if (!c.match('comma')) break
|
|
366
|
+
}
|
|
367
|
+
c.expect('paren', ')')
|
|
368
|
+
return {
|
|
369
|
+
type: 'in valuelist',
|
|
370
|
+
expr: left,
|
|
371
|
+
values,
|
|
372
|
+
}
|
|
306
373
|
}
|
|
307
374
|
}
|
|
308
375
|
|
package/src/types.d.ts
CHANGED
|
@@ -108,12 +108,18 @@ export interface BetweenNode {
|
|
|
108
108
|
upper: ExprNode
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
export interface
|
|
111
|
+
export interface InSubqueryNode {
|
|
112
112
|
type: 'in' | 'not in'
|
|
113
113
|
expr: ExprNode
|
|
114
114
|
subquery: SelectStatement
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
+
export interface InValuesNode {
|
|
118
|
+
type: 'in valuelist' | 'not in valuelist'
|
|
119
|
+
expr: ExprNode
|
|
120
|
+
values: ExprNode[]
|
|
121
|
+
}
|
|
122
|
+
|
|
117
123
|
export interface ExistsNode {
|
|
118
124
|
type: 'exists' | 'not exists'
|
|
119
125
|
subquery: SelectStatement
|
|
@@ -127,7 +133,8 @@ export type ExprNode =
|
|
|
127
133
|
| FunctionNode
|
|
128
134
|
| CastNode
|
|
129
135
|
| BetweenNode
|
|
130
|
-
|
|
|
136
|
+
| InSubqueryNode
|
|
137
|
+
| InValuesNode
|
|
131
138
|
| ExistsNode
|
|
132
139
|
|
|
133
140
|
export interface StarColumn {
|