squirreling 0.4.7 → 0.5.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 +1 -0
- package/package.json +3 -3
- package/src/execute/aggregates.js +12 -5
- package/src/execute/date.js +57 -0
- package/src/execute/execute.js +20 -7
- package/src/execute/expression.js +268 -40
- package/src/execute/having.js +7 -1
- package/src/execute/join.js +9 -4
- package/src/execute/math.js +165 -0
- package/src/execute/utils.js +3 -0
- package/src/executionErrors.js +62 -0
- package/src/index.js +1 -0
- package/src/parse/comparison.js +41 -7
- package/src/parse/expression.js +121 -10
- package/src/parse/parse.js +6 -1
- package/src/parse/state.js +16 -4
- package/src/parse/tokenize.js +113 -48
- package/src/parseErrors.js +117 -0
- package/src/types.d.ts +58 -14
- package/src/validation.js +23 -1
- package/src/validationErrors.js +127 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// PARSE ERRORS - Issues during SQL tokenization and parsing
|
|
3
|
+
// ============================================================================
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Structured parse error with position range.
|
|
7
|
+
*/
|
|
8
|
+
export class ParseError extends Error {
|
|
9
|
+
/**
|
|
10
|
+
* @param {string} message - Human-readable error message
|
|
11
|
+
* @param {number} positionStart - Start position (0-based character offset)
|
|
12
|
+
* @param {number} positionEnd - End position (exclusive, 0-based character offset)
|
|
13
|
+
*/
|
|
14
|
+
constructor(message, positionStart, positionEnd) {
|
|
15
|
+
super(message)
|
|
16
|
+
this.name = 'ParseError'
|
|
17
|
+
this.positionStart = positionStart
|
|
18
|
+
this.positionEnd = positionEnd
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* General syntax error for unexpected tokens.
|
|
24
|
+
*
|
|
25
|
+
* @param {Object} options
|
|
26
|
+
* @param {string} options.expected - Description of what was expected
|
|
27
|
+
* @param {string} options.received - What was actually found
|
|
28
|
+
* @param {number} options.positionStart - Start character position in query
|
|
29
|
+
* @param {number} options.positionEnd - End character position in query
|
|
30
|
+
* @param {string} [options.after] - What token came before (for context)
|
|
31
|
+
* @returns {ParseError}
|
|
32
|
+
*/
|
|
33
|
+
export function syntaxError({ expected, received, positionStart, positionEnd, after }) {
|
|
34
|
+
const afterClause = after ? ` after "${after}"` : ''
|
|
35
|
+
return new ParseError(`Expected ${expected}${afterClause} but found ${received} at position ${positionStart}`, positionStart, positionEnd)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Error for unterminated literals (strings, identifiers).
|
|
40
|
+
*
|
|
41
|
+
* @param {'string' | 'identifier'} type - Type of unterminated literal
|
|
42
|
+
* @param {number} positionStart - Starting position
|
|
43
|
+
* @param {number} positionEnd - End position
|
|
44
|
+
* @returns {ParseError}
|
|
45
|
+
*/
|
|
46
|
+
export function unterminatedError(type, positionStart, positionEnd) {
|
|
47
|
+
const name = type === 'string' ? 'string literal' : 'identifier'
|
|
48
|
+
return new ParseError(`Unterminated ${name} starting at position ${positionStart}`, positionStart, positionEnd)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Error for invalid literals (numbers, intervals, etc).
|
|
53
|
+
*
|
|
54
|
+
* @param {Object} options
|
|
55
|
+
* @param {string} options.type - Type of invalid literal (e.g., 'number', 'interval value', 'interval unit')
|
|
56
|
+
* @param {string} options.value - The invalid value
|
|
57
|
+
* @param {number} options.positionStart - Start position in query
|
|
58
|
+
* @param {number} options.positionEnd - End position in query
|
|
59
|
+
* @param {string} [options.validValues] - List of valid values (for enums like interval units)
|
|
60
|
+
* @returns {ParseError}
|
|
61
|
+
*/
|
|
62
|
+
export function invalidLiteralError({ type, value, positionStart, positionEnd, validValues }) {
|
|
63
|
+
const suffix = validValues ? `. Valid values: ${validValues}` : ''
|
|
64
|
+
return new ParseError(`Invalid ${type} ${value} at position ${positionStart}${suffix}`, positionStart, positionEnd)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Error for unexpected characters during tokenization.
|
|
69
|
+
*
|
|
70
|
+
* @param {Object} options
|
|
71
|
+
* @param {string} options.char - The unexpected character
|
|
72
|
+
* @param {number} options.positionStart - Position in query
|
|
73
|
+
* @param {boolean} [options.expectsSelect=false] - Whether SELECT was expected (first token)
|
|
74
|
+
* @returns {ParseError}
|
|
75
|
+
*/
|
|
76
|
+
export function unexpectedCharError({ char, positionStart, expectsSelect = false }) {
|
|
77
|
+
const positionEnd = positionStart + 1
|
|
78
|
+
if (expectsSelect) {
|
|
79
|
+
return new ParseError(`Expected SELECT but found "${char}" at position ${positionStart}. Queries must start with SELECT.`, positionStart, positionEnd)
|
|
80
|
+
}
|
|
81
|
+
return new ParseError(`Unexpected character "${char}" at position ${positionStart}`, positionStart, positionEnd)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Error for unknown/unsupported functions.
|
|
86
|
+
*
|
|
87
|
+
* @param {Object} options
|
|
88
|
+
* @param {string} options.funcName - The unknown function name
|
|
89
|
+
* @param {number} options.positionStart - Start position in query
|
|
90
|
+
* @param {number} options.positionEnd - End position in query
|
|
91
|
+
* @param {string} [options.validFunctions] - List of valid functions
|
|
92
|
+
* @returns {ParseError}
|
|
93
|
+
*/
|
|
94
|
+
export function unknownFunctionError({ funcName, positionStart, positionEnd, validFunctions }) {
|
|
95
|
+
const supported = validFunctions ||
|
|
96
|
+
'COUNT, SUM, AVG, MIN, MAX, UPPER, LOWER, CONCAT, LENGTH, SUBSTRING, TRIM, REPLACE, FLOOR, CEIL, ABS, MOD, EXP, LN, LOG10, POWER, SQRT, JSON_OBJECT, JSON_VALUE, JSON_QUERY, JSON_ARRAYAGG'
|
|
97
|
+
|
|
98
|
+
return new ParseError(
|
|
99
|
+
`Unknown function "${funcName}" at position ${positionStart}. Supported: ${supported}`,
|
|
100
|
+
positionStart,
|
|
101
|
+
positionEnd
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Error for missing required clause or structure.
|
|
107
|
+
*
|
|
108
|
+
* @param {Object} options
|
|
109
|
+
* @param {string} options.missing - What is missing (e.g., 'WHEN clause', 'FROM clause', 'ON condition')
|
|
110
|
+
* @param {string} options.context - Where it's missing from (e.g., 'CASE expression', 'SELECT statement', 'JOIN')
|
|
111
|
+
* @param {number} [options.positionStart] - Start position in query
|
|
112
|
+
* @param {number} [options.positionEnd] - End position in query
|
|
113
|
+
* @returns {ParseError}
|
|
114
|
+
*/
|
|
115
|
+
export function missingClauseError({ missing, context, positionStart, positionEnd }) {
|
|
116
|
+
return new ParseError(`${context} requires ${missing}`, positionStart ?? 0, positionEnd ?? 0)
|
|
117
|
+
}
|
package/src/types.d.ts
CHANGED
|
@@ -6,6 +6,11 @@
|
|
|
6
6
|
export interface QueryHints {
|
|
7
7
|
columns?: string[] // columns needed
|
|
8
8
|
where?: ExprNode // where clause
|
|
9
|
+
// important: only apply limit/offset if where is fully applied by the data source
|
|
10
|
+
// otherwise, the data source must return at least enough rows to ensure the engine
|
|
11
|
+
// can apply limit/offset correctly after filtering
|
|
12
|
+
// even with offset, the datasource must return rows starting from offset 0
|
|
13
|
+
// but doesn't need to resolve async rows before the offset
|
|
9
14
|
limit?: number
|
|
10
15
|
offset?: number
|
|
11
16
|
}
|
|
@@ -27,7 +32,15 @@ export interface ExecuteSqlOptions {
|
|
|
27
32
|
query: string
|
|
28
33
|
}
|
|
29
34
|
|
|
30
|
-
export type SqlPrimitive =
|
|
35
|
+
export type SqlPrimitive =
|
|
36
|
+
| string
|
|
37
|
+
| number
|
|
38
|
+
| bigint
|
|
39
|
+
| boolean
|
|
40
|
+
| Date
|
|
41
|
+
| null
|
|
42
|
+
| SqlPrimitive[]
|
|
43
|
+
| Record<string, any>
|
|
31
44
|
|
|
32
45
|
export interface SelectStatement {
|
|
33
46
|
distinct: boolean
|
|
@@ -60,54 +73,59 @@ export type BinaryOp = 'AND' | 'OR' | 'LIKE' | ComparisonOp | ArithmeticOp
|
|
|
60
73
|
|
|
61
74
|
export type ComparisonOp = '=' | '!=' | '<>' | '<' | '>' | '<=' | '>='
|
|
62
75
|
|
|
63
|
-
export interface
|
|
76
|
+
export interface ExprNodeBase {
|
|
77
|
+
positionStart: number
|
|
78
|
+
positionEnd: number
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface LiteralNode extends ExprNodeBase {
|
|
64
82
|
type: 'literal'
|
|
65
83
|
value: SqlPrimitive
|
|
66
84
|
}
|
|
67
85
|
|
|
68
|
-
export interface IdentifierNode {
|
|
86
|
+
export interface IdentifierNode extends ExprNodeBase {
|
|
69
87
|
type: 'identifier'
|
|
70
88
|
name: string
|
|
71
89
|
}
|
|
72
90
|
|
|
73
|
-
export interface UnaryNode {
|
|
91
|
+
export interface UnaryNode extends ExprNodeBase {
|
|
74
92
|
type: 'unary'
|
|
75
93
|
op: 'NOT' | 'IS NULL' | 'IS NOT NULL' | '-'
|
|
76
94
|
argument: ExprNode
|
|
77
95
|
}
|
|
78
96
|
|
|
79
|
-
export interface BinaryNode {
|
|
97
|
+
export interface BinaryNode extends ExprNodeBase {
|
|
80
98
|
type: 'binary'
|
|
81
99
|
op: BinaryOp
|
|
82
100
|
left: ExprNode
|
|
83
101
|
right: ExprNode
|
|
84
102
|
}
|
|
85
103
|
|
|
86
|
-
export interface FunctionNode {
|
|
104
|
+
export interface FunctionNode extends ExprNodeBase {
|
|
87
105
|
type: 'function'
|
|
88
106
|
name: string
|
|
89
107
|
args: ExprNode[]
|
|
90
108
|
}
|
|
91
109
|
|
|
92
|
-
export interface CastNode {
|
|
110
|
+
export interface CastNode extends ExprNodeBase {
|
|
93
111
|
type: 'cast'
|
|
94
112
|
expr: ExprNode
|
|
95
113
|
toType: string
|
|
96
114
|
}
|
|
97
115
|
|
|
98
|
-
export interface InSubqueryNode {
|
|
116
|
+
export interface InSubqueryNode extends ExprNodeBase {
|
|
99
117
|
type: 'in'
|
|
100
118
|
expr: ExprNode
|
|
101
119
|
subquery: SelectStatement
|
|
102
120
|
}
|
|
103
121
|
|
|
104
|
-
export interface InValuesNode {
|
|
122
|
+
export interface InValuesNode extends ExprNodeBase {
|
|
105
123
|
type: 'in valuelist'
|
|
106
124
|
expr: ExprNode
|
|
107
125
|
values: ExprNode[]
|
|
108
126
|
}
|
|
109
127
|
|
|
110
|
-
export interface ExistsNode {
|
|
128
|
+
export interface ExistsNode extends ExprNodeBase {
|
|
111
129
|
type: 'exists' | 'not exists'
|
|
112
130
|
subquery: SelectStatement
|
|
113
131
|
}
|
|
@@ -117,18 +135,26 @@ export interface WhenClause {
|
|
|
117
135
|
result: ExprNode
|
|
118
136
|
}
|
|
119
137
|
|
|
120
|
-
export interface CaseNode {
|
|
138
|
+
export interface CaseNode extends ExprNodeBase {
|
|
121
139
|
type: 'case'
|
|
122
140
|
caseExpr?: ExprNode
|
|
123
141
|
whenClauses: WhenClause[]
|
|
124
142
|
elseResult?: ExprNode
|
|
125
143
|
}
|
|
126
144
|
|
|
127
|
-
export interface SubqueryNode {
|
|
145
|
+
export interface SubqueryNode extends ExprNodeBase {
|
|
128
146
|
type: 'subquery'
|
|
129
147
|
subquery: SelectStatement
|
|
130
148
|
}
|
|
131
149
|
|
|
150
|
+
export type IntervalUnit = 'DAY' | 'MONTH' | 'YEAR' | 'HOUR' | 'MINUTE' | 'SECOND'
|
|
151
|
+
|
|
152
|
+
export interface IntervalNode extends ExprNodeBase {
|
|
153
|
+
type: 'interval'
|
|
154
|
+
value: number
|
|
155
|
+
unit: IntervalUnit
|
|
156
|
+
}
|
|
157
|
+
|
|
132
158
|
export type ExprNode =
|
|
133
159
|
| LiteralNode
|
|
134
160
|
| IdentifierNode
|
|
@@ -141,6 +167,7 @@ export type ExprNode =
|
|
|
141
167
|
| ExistsNode
|
|
142
168
|
| CaseNode
|
|
143
169
|
| SubqueryNode
|
|
170
|
+
| IntervalNode
|
|
144
171
|
|
|
145
172
|
export interface StarColumn {
|
|
146
173
|
kind: 'star'
|
|
@@ -150,6 +177,18 @@ export interface StarColumn {
|
|
|
150
177
|
|
|
151
178
|
export type AggregateFunc = 'COUNT' | 'SUM' | 'AVG' | 'MIN' | 'MAX' | 'JSON_ARRAYAGG'
|
|
152
179
|
|
|
180
|
+
export type MathFunc =
|
|
181
|
+
| 'FLOOR'
|
|
182
|
+
| 'CEIL'
|
|
183
|
+
| 'CEILING'
|
|
184
|
+
| 'ABS'
|
|
185
|
+
| 'MOD'
|
|
186
|
+
| 'EXP'
|
|
187
|
+
| 'LN'
|
|
188
|
+
| 'LOG10'
|
|
189
|
+
| 'POWER'
|
|
190
|
+
| 'SQRT'
|
|
191
|
+
|
|
153
192
|
export type StringFunc =
|
|
154
193
|
| 'UPPER'
|
|
155
194
|
| 'LOWER'
|
|
@@ -162,6 +201,9 @@ export type StringFunc =
|
|
|
162
201
|
| 'JSON_VALUE'
|
|
163
202
|
| 'JSON_QUERY'
|
|
164
203
|
| 'JSON_OBJECT'
|
|
204
|
+
| 'CURRENT_DATE'
|
|
205
|
+
| 'CURRENT_TIME'
|
|
206
|
+
| 'CURRENT_TIMESTAMP'
|
|
165
207
|
|
|
166
208
|
export interface AggregateArgStar {
|
|
167
209
|
kind: 'star'
|
|
@@ -208,6 +250,7 @@ export interface JoinClause {
|
|
|
208
250
|
export interface ParserState {
|
|
209
251
|
tokens: Token[]
|
|
210
252
|
pos: number
|
|
253
|
+
lastPos?: number
|
|
211
254
|
}
|
|
212
255
|
|
|
213
256
|
// Tokenizer types
|
|
@@ -226,7 +269,8 @@ export type TokenType =
|
|
|
226
269
|
export interface Token {
|
|
227
270
|
type: TokenType
|
|
228
271
|
value: string
|
|
229
|
-
|
|
230
|
-
|
|
272
|
+
positionStart: number
|
|
273
|
+
positionEnd: number
|
|
274
|
+
numericValue?: number | bigint
|
|
231
275
|
originalValue?: string
|
|
232
276
|
}
|
package/src/validation.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
/**
|
|
3
|
-
* @import {AggregateFunc, BinaryOp, ComparisonOp, StringFunc} from './types.js'
|
|
3
|
+
* @import {AggregateFunc, BinaryOp, ComparisonOp, IntervalUnit, MathFunc, StringFunc} from './types.js'
|
|
4
4
|
* @param {string} name
|
|
5
5
|
* @returns {name is AggregateFunc}
|
|
6
6
|
*/
|
|
@@ -8,6 +8,25 @@ export function isAggregateFunc(name) {
|
|
|
8
8
|
return ['COUNT', 'SUM', 'AVG', 'MIN', 'MAX', 'JSON_ARRAYAGG'].includes(name)
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* @param {string} name
|
|
13
|
+
* @returns {name is MathFunc}
|
|
14
|
+
*/
|
|
15
|
+
export function isMathFunc(name) {
|
|
16
|
+
return [
|
|
17
|
+
'FLOOR', 'CEIL', 'CEILING', 'ABS', 'MOD',
|
|
18
|
+
'EXP', 'LN', 'LOG10', 'POWER', 'SQRT',
|
|
19
|
+
].includes(name)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param {string} name
|
|
24
|
+
* @returns {name is IntervalUnit}
|
|
25
|
+
*/
|
|
26
|
+
export function isIntervalUnit(name) {
|
|
27
|
+
return ['DAY', 'MONTH', 'YEAR', 'HOUR', 'MINUTE', 'SECOND'].includes(name)
|
|
28
|
+
}
|
|
29
|
+
|
|
11
30
|
/**
|
|
12
31
|
* @param {string} name
|
|
13
32
|
* @returns {name is StringFunc}
|
|
@@ -27,6 +46,9 @@ export function isStringFunc(name) {
|
|
|
27
46
|
'JSON_VALUE',
|
|
28
47
|
'JSON_QUERY',
|
|
29
48
|
'JSON_OBJECT',
|
|
49
|
+
'CURRENT_DATE',
|
|
50
|
+
'CURRENT_TIME',
|
|
51
|
+
'CURRENT_TIMESTAMP',
|
|
30
52
|
].includes(name)
|
|
31
53
|
}
|
|
32
54
|
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { ExecutionError } from './executionErrors.js'
|
|
2
|
+
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// VALIDATION ERRORS - Function argument and type validation
|
|
5
|
+
// ============================================================================
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Function signatures for helpful error messages.
|
|
9
|
+
* Maps function name to its parameter signature.
|
|
10
|
+
* @type {Record<string, string>}
|
|
11
|
+
*/
|
|
12
|
+
const FUNCTION_SIGNATURES = {
|
|
13
|
+
// String functions
|
|
14
|
+
UPPER: 'string',
|
|
15
|
+
LOWER: 'string',
|
|
16
|
+
LENGTH: 'string',
|
|
17
|
+
TRIM: 'string',
|
|
18
|
+
REPLACE: 'string, search, replacement',
|
|
19
|
+
SUBSTRING: 'string, start[, length]',
|
|
20
|
+
SUBSTR: 'string, start[, length]',
|
|
21
|
+
CONCAT: 'value1, value2[, ...]',
|
|
22
|
+
|
|
23
|
+
// Date/time functions
|
|
24
|
+
RANDOM: '',
|
|
25
|
+
RAND: '',
|
|
26
|
+
CURRENT_DATE: '',
|
|
27
|
+
CURRENT_TIME: '',
|
|
28
|
+
CURRENT_TIMESTAMP: '',
|
|
29
|
+
|
|
30
|
+
// Math functions
|
|
31
|
+
FLOOR: 'number',
|
|
32
|
+
CEIL: 'number',
|
|
33
|
+
CEILING: 'number',
|
|
34
|
+
ABS: 'number',
|
|
35
|
+
MOD: 'dividend, divisor',
|
|
36
|
+
EXP: 'number',
|
|
37
|
+
LN: 'number',
|
|
38
|
+
LOG10: 'number',
|
|
39
|
+
POWER: 'base, exponent',
|
|
40
|
+
SQRT: 'number',
|
|
41
|
+
|
|
42
|
+
// JSON functions
|
|
43
|
+
JSON_VALUE: 'expression, path',
|
|
44
|
+
JSON_QUERY: 'expression, path',
|
|
45
|
+
JSON_OBJECT: 'key1, value1[, ...]',
|
|
46
|
+
JSON_ARRAYAGG: 'expression',
|
|
47
|
+
|
|
48
|
+
// Aggregate functions
|
|
49
|
+
COUNT: 'expression',
|
|
50
|
+
SUM: 'expression',
|
|
51
|
+
AVG: 'expression',
|
|
52
|
+
MIN: 'expression',
|
|
53
|
+
MAX: 'expression',
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Error for wrong number of function arguments.
|
|
58
|
+
*
|
|
59
|
+
* @param {Object} options
|
|
60
|
+
* @param {string} options.funcName - The function name
|
|
61
|
+
* @param {number | string} options.expected - Expected count (number or range like "2 or 3")
|
|
62
|
+
* @param {number} options.received - Actual argument count
|
|
63
|
+
* @param {number} options.positionStart - Start position in query
|
|
64
|
+
* @param {number} options.positionEnd - End position in query
|
|
65
|
+
* @param {number} [options.rowNumber] - 1-based row number where error occurred
|
|
66
|
+
* @returns {ExecutionError}
|
|
67
|
+
*/
|
|
68
|
+
export function argCountError({ funcName, expected, received, positionStart, positionEnd, rowNumber }) {
|
|
69
|
+
const signature = FUNCTION_SIGNATURES[funcName] ?? ''
|
|
70
|
+
let expectedStr = `${expected} arguments`
|
|
71
|
+
if (expected === 0) expectedStr = 'no arguments'
|
|
72
|
+
if (expected === 1) expectedStr = '1 argument'
|
|
73
|
+
if (typeof expected === 'string' && expected.endsWith(' 1')) {
|
|
74
|
+
expectedStr = `${expected} argument`
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return new ExecutionError(`${funcName}(${signature}) function requires ${expectedStr}, got ${received}`, positionStart, positionEnd, rowNumber)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Error for invalid argument type or value.
|
|
82
|
+
*
|
|
83
|
+
* @param {Object} options
|
|
84
|
+
* @param {string} options.funcName - The function name
|
|
85
|
+
* @param {string} options.message - Specific error message
|
|
86
|
+
* @param {number} options.positionStart - Start position in query
|
|
87
|
+
* @param {number} options.positionEnd - End position in query
|
|
88
|
+
* @param {string} [options.hint] - Recovery hint
|
|
89
|
+
* @param {number} [options.rowNumber] - 1-based row number where error occurred
|
|
90
|
+
* @returns {ExecutionError}
|
|
91
|
+
*/
|
|
92
|
+
export function argValueError({ funcName, message, positionStart, positionEnd, hint, rowNumber }) {
|
|
93
|
+
const signature = FUNCTION_SIGNATURES[funcName] ?? ''
|
|
94
|
+
const suffix = hint ? `. ${hint}` : ''
|
|
95
|
+
return new ExecutionError(`${funcName}(${signature}): ${message}${suffix}`, positionStart, positionEnd, rowNumber)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Error for aggregate function misuse (e.g., SUM(*)).
|
|
100
|
+
*
|
|
101
|
+
* @param {Object} options
|
|
102
|
+
* @param {string} options.funcName - The aggregate function
|
|
103
|
+
* @param {string} options.issue - What's wrong (e.g., "(*) is not supported")
|
|
104
|
+
* @returns {Error}
|
|
105
|
+
*/
|
|
106
|
+
export function aggregateError({ funcName, issue }) {
|
|
107
|
+
return new Error(`${funcName}${issue}. Only COUNT supports *. Use a column name for ${funcName}.`)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Error for unsupported CAST type.
|
|
112
|
+
*
|
|
113
|
+
* @param {Object} options
|
|
114
|
+
* @param {string} options.toType - The unsupported target type
|
|
115
|
+
* @param {number} options.positionStart - Start position in query
|
|
116
|
+
* @param {number} options.positionEnd - End position in query
|
|
117
|
+
* @param {string} [options.fromType] - The source type (optional)
|
|
118
|
+
* @param {number} [options.rowNumber] - 1-based row number where error occurred
|
|
119
|
+
* @returns {ExecutionError}
|
|
120
|
+
*/
|
|
121
|
+
export function castError({ toType, positionStart, positionEnd, fromType, rowNumber }) {
|
|
122
|
+
const message = fromType
|
|
123
|
+
? `Cannot CAST ${fromType} to ${toType}`
|
|
124
|
+
: `Unsupported CAST to type ${toType}`
|
|
125
|
+
|
|
126
|
+
return new ExecutionError(`${message}. Supported types: TEXT, VARCHAR, INTEGER, INT, BIGINT, FLOAT, REAL, DOUBLE, BOOLEAN`, positionStart, positionEnd, rowNumber)
|
|
127
|
+
}
|