squirreling 0.2.5 → 0.2.6

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "squirreling",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
4
4
  "description": "Squirreling SQL Engine",
5
5
  "author": "Hyperparam",
6
6
  "homepage": "https://hyperparam.app",
@@ -224,5 +224,34 @@ export function evaluateExpr(node, row) {
224
224
  throw new Error('WHERE NOT EXISTS with subqueries is not yet supported.')
225
225
  }
226
226
 
227
+ // CASE expressions
228
+ if (node.type === 'case') {
229
+ // For simple CASE: evaluate the case expression once
230
+ const caseValue = node.caseExpr ? evaluateExpr(node.caseExpr, row) : undefined
231
+
232
+ // Iterate through WHEN clauses
233
+ for (const whenClause of node.whenClauses) {
234
+ let conditionResult
235
+ if (caseValue !== undefined) {
236
+ // Simple CASE: compare caseValue with condition
237
+ const whenValue = evaluateExpr(whenClause.condition, row)
238
+ conditionResult = caseValue === whenValue
239
+ } else {
240
+ // Searched CASE: evaluate condition as boolean
241
+ conditionResult = evaluateExpr(whenClause.condition, row)
242
+ }
243
+
244
+ if (conditionResult) {
245
+ return evaluateExpr(whenClause.result, row)
246
+ }
247
+ }
248
+
249
+ // No WHEN clause matched, return ELSE result or NULL
250
+ if (node.elseResult) {
251
+ return evaluateExpr(node.elseResult, row)
252
+ }
253
+ return null
254
+ }
255
+
227
256
  throw new Error('Unknown expression node type ' + node.type)
228
257
  }
@@ -141,6 +141,48 @@ function parsePrimary(c) {
141
141
  subquery,
142
142
  }
143
143
  }
144
+ if (tok.value === 'CASE') {
145
+ c.consume() // CASE
146
+
147
+ // Check if it's simple CASE (CASE expr WHEN ...) or searched CASE (CASE WHEN ...)
148
+ /** @type {import('../types.js').ExprNode | undefined} */
149
+ let caseExpr
150
+ const nextTok = c.current()
151
+ if (nextTok.type !== 'keyword' || nextTok.value !== 'WHEN') {
152
+ // Simple CASE: parse the case expression
153
+ caseExpr = parseExpression(c)
154
+ }
155
+
156
+ // Parse WHEN clauses
157
+ /** @type {import('../types.js').WhenClause[]} */
158
+ const whenClauses = []
159
+ while (c.match('keyword', 'WHEN')) {
160
+ const condition = parseExpression(c)
161
+ c.expect('keyword', 'THEN')
162
+ const result = parseExpression(c)
163
+ whenClauses.push({ condition, result })
164
+ }
165
+
166
+ if (whenClauses.length === 0) {
167
+ throw new Error('CASE expression must have at least one WHEN clause')
168
+ }
169
+
170
+ // Parse optional ELSE clause
171
+ /** @type {import('../types.js').ExprNode | undefined} */
172
+ let elseResult
173
+ if (c.match('keyword', 'ELSE')) {
174
+ elseResult = parseExpression(c)
175
+ }
176
+
177
+ c.expect('keyword', 'END')
178
+
179
+ return {
180
+ type: 'case',
181
+ caseExpr,
182
+ whenClauses,
183
+ elseResult,
184
+ }
185
+ }
144
186
  }
145
187
 
146
188
  if (tok.type === 'operator' && tok.value === '-') {
@@ -177,7 +177,7 @@ function parseSelectList(state) {
177
177
  function parseSelectItem(state) {
178
178
  const tok = current(state)
179
179
 
180
- if (tok.type !== 'identifier' && tok.type !== 'operator') {
180
+ if (tok.type === 'keyword' && tok.value !== 'CASE' || tok.type === 'eof') {
181
181
  throw parseError(state, 'column name or expression')
182
182
  }
183
183
 
package/src/types.d.ts CHANGED
@@ -105,6 +105,18 @@ export interface ExistsNode {
105
105
  subquery: SelectStatement
106
106
  }
107
107
 
108
+ export interface WhenClause {
109
+ condition: ExprNode
110
+ result: ExprNode
111
+ }
112
+
113
+ export interface CaseNode {
114
+ type: 'case'
115
+ caseExpr?: ExprNode
116
+ whenClauses: WhenClause[]
117
+ elseResult?: ExprNode
118
+ }
119
+
108
120
  export type ExprNode =
109
121
  | LiteralNode
110
122
  | IdentifierNode
@@ -116,6 +128,7 @@ export type ExprNode =
116
128
  | InSubqueryNode
117
129
  | InValuesNode
118
130
  | ExistsNode
131
+ | CaseNode
119
132
 
120
133
  export interface StarColumn {
121
134
  kind: 'star'