squirreling 0.15.2 → 0.16.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/package.json +2 -2
- package/src/backend/batch.js +236 -0
- package/src/backend/batchAdapters.js +178 -0
- package/src/backend/dataSource.js +17 -1
- package/src/execute/aggregates.js +2 -1
- package/src/execute/batchResults.js +28 -0
- package/src/execute/batches.js +274 -0
- package/src/execute/execute.js +440 -24
- package/src/execute/streamingAggregate.js +151 -1
- package/src/execute/utils.js +37 -0
- package/src/expression/batch.js +659 -0
- package/src/expression/binary.js +17 -4
- package/src/expression/evaluate.js +39 -109
- package/src/expression/scalar.js +105 -0
- package/src/index.d.ts +44 -1
- package/src/index.js +9 -0
- package/src/internalTypes.d.ts +28 -0
- package/src/plan/columns.js +105 -59
- package/src/plan/plan.js +12 -3
- package/src/plan/types.d.ts +3 -0
- package/src/types.d.ts +156 -4
- package/src/validation/tables.js +4 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { executeStatement } from '../execute/execute.js'
|
|
2
|
-
import { isPlainObject, keyify, sqlEquals
|
|
2
|
+
import { isPlainObject, keyify, sqlEquals } from '../execute/utils.js'
|
|
3
3
|
import { yieldToEventLoop } from '../execute/yield.js'
|
|
4
4
|
import { ArgValueError, ExecutionError } from '../validation/executionErrors.js'
|
|
5
5
|
import { isAggregateFunc, isMathFunc, isRegexpFunc, isSpatialFunc, isStringFunc } from '../validation/functions.js'
|
|
@@ -7,9 +7,10 @@ import { UnknownFunctionError } from '../validation/parseErrors.js'
|
|
|
7
7
|
import { ColumnNotFoundError } from '../validation/tables.js'
|
|
8
8
|
import { derivedAlias } from './alias.js'
|
|
9
9
|
import { applyBinaryOp } from './binary.js'
|
|
10
|
-
import { applyIntervalToDate, dateDiff, dateTrunc, extractField
|
|
10
|
+
import { applyIntervalToDate, dateDiff, dateTrunc, extractField } from './date.js'
|
|
11
11
|
import { evaluateMathFunc } from './math.js'
|
|
12
12
|
import { evaluateRegexpFunc } from './regexp.js'
|
|
13
|
+
import { applyCast, evaluateJsonExtract } from './scalar.js'
|
|
13
14
|
import { evaluateSpatialFunc } from '../spatial/spatial.js'
|
|
14
15
|
import { evaluateStringFunc } from './strings.js'
|
|
15
16
|
|
|
@@ -74,6 +75,13 @@ export async function evaluateExpr({ node, row, rowIndex, rows, context }) {
|
|
|
74
75
|
if (qualified in row.cells) {
|
|
75
76
|
return row.cells[qualified]()
|
|
76
77
|
}
|
|
78
|
+
if (context.scope?.includes(node.prefix) && node.name in row.cells) {
|
|
79
|
+
return row.cells[node.name]()
|
|
80
|
+
}
|
|
81
|
+
if (context.outerRow && context.outerAliases?.has(node.prefix)) {
|
|
82
|
+
if (qualified in context.outerRow.cells) return context.outerRow.cells[qualified]()
|
|
83
|
+
if (node.name in context.outerRow.cells) return context.outerRow.cells[node.name]()
|
|
84
|
+
}
|
|
77
85
|
const prefix = node.prefix + '.'
|
|
78
86
|
const prefixedColumns = row.columns.filter(col => col.startsWith(prefix))
|
|
79
87
|
if (prefixedColumns.length === 1) {
|
|
@@ -93,10 +101,6 @@ export async function evaluateExpr({ node, row, rowIndex, rows, context }) {
|
|
|
93
101
|
return value[node.name]
|
|
94
102
|
}
|
|
95
103
|
}
|
|
96
|
-
// Check outer row for correlated subquery references
|
|
97
|
-
if (context.outerRow && context.outerAliases?.has(node.prefix) && node.name in context.outerRow.cells) {
|
|
98
|
-
return context.outerRow.cells[node.name]()
|
|
99
|
-
}
|
|
100
104
|
// Standalone `FROM UNNEST(...) AS alias` row has a single bare column;
|
|
101
105
|
// `alias.field` should struct-access that column's element.
|
|
102
106
|
if (context.scope?.includes(node.prefix) && row.columns.length === 1) {
|
|
@@ -167,7 +171,9 @@ export async function evaluateExpr({ node, row, rowIndex, rows, context }) {
|
|
|
167
171
|
if (val == null) return null
|
|
168
172
|
return -val
|
|
169
173
|
}
|
|
170
|
-
|
|
174
|
+
// NOT UNKNOWN is UNKNOWN: JS ! would flip null to true and return rows
|
|
175
|
+
// SQL excludes, e.g. WHERE NOT (nullable < 300)
|
|
176
|
+
if (node.op === 'NOT') return val == null ? null : !val
|
|
171
177
|
if (node.op === 'IS NULL') return val == null
|
|
172
178
|
if (node.op === 'IS NOT NULL') return val != null
|
|
173
179
|
}
|
|
@@ -186,9 +192,11 @@ export async function evaluateExpr({ node, row, rowIndex, rows, context }) {
|
|
|
186
192
|
|
|
187
193
|
const left = await evaluateExpr({ node: node.left, row, rowIndex, rows, context })
|
|
188
194
|
|
|
189
|
-
// Short-circuit evaluation for AND and OR
|
|
190
|
-
|
|
191
|
-
|
|
195
|
+
// Short-circuit evaluation for AND and OR. A null left cannot
|
|
196
|
+
// short-circuit AND: NULL AND FALSE is false but NULL AND TRUE is null,
|
|
197
|
+
// so the right side decides
|
|
198
|
+
if (node.op === 'AND' && left != null && !left) return false
|
|
199
|
+
if (node.op === 'OR' && left != null && Boolean(left)) return true
|
|
192
200
|
|
|
193
201
|
const right = await evaluateExpr({ node: node.right, row, rowIndex, rows, context })
|
|
194
202
|
return applyBinaryOp(node.op, left, right)
|
|
@@ -665,60 +673,7 @@ export async function evaluateExpr({ node, row, rowIndex, rows, context }) {
|
|
|
665
673
|
}
|
|
666
674
|
|
|
667
675
|
if (funcName === 'JSON_VALUE' || funcName === 'JSON_QUERY' || funcName === 'JSON_EXTRACT' || funcName === 'JSON_EXTRACT_STRING') {
|
|
668
|
-
|
|
669
|
-
const pathArg = args[1]
|
|
670
|
-
if (jsonArg == null || pathArg == null) return null
|
|
671
|
-
|
|
672
|
-
// Parse JSON if string, otherwise use directly
|
|
673
|
-
if (typeof jsonArg === 'string') {
|
|
674
|
-
try {
|
|
675
|
-
jsonArg = JSON.parse(jsonArg)
|
|
676
|
-
} catch {
|
|
677
|
-
throw new ArgValueError({
|
|
678
|
-
...node,
|
|
679
|
-
message: 'invalid JSON string',
|
|
680
|
-
hint: 'First argument must be valid JSON.',
|
|
681
|
-
rowIndex,
|
|
682
|
-
})
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
if (typeof jsonArg !== 'object' || jsonArg instanceof Date) {
|
|
686
|
-
throw new ArgValueError({
|
|
687
|
-
...node,
|
|
688
|
-
message: `first argument must be JSON string or object, got ${typeof jsonArg}`,
|
|
689
|
-
rowIndex,
|
|
690
|
-
})
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
// Parse path ("$.foo.bar[0].baz" or "foo.bar[0]")
|
|
694
|
-
const path = String(pathArg)
|
|
695
|
-
const normalizedPath = path.startsWith('$') ? path.slice(1) : path
|
|
696
|
-
|
|
697
|
-
// Navigate the path
|
|
698
|
-
let current = jsonArg
|
|
699
|
-
const segments = normalizedPath.match(/\.?([^.[]+)|\[(\d+)\]/g) || []
|
|
700
|
-
for (const segment of segments) {
|
|
701
|
-
if (current == null) return null
|
|
702
|
-
if (segment.startsWith('[')) {
|
|
703
|
-
// Array index access
|
|
704
|
-
const index = parseInt(segment.slice(1, -1), 10)
|
|
705
|
-
if (!Array.isArray(current)) return null
|
|
706
|
-
current = current[index]
|
|
707
|
-
} else {
|
|
708
|
-
// Property access
|
|
709
|
-
const key = segment.startsWith('.') ? segment.slice(1) : segment
|
|
710
|
-
if (typeof current !== 'object' || Array.isArray(current)) return null
|
|
711
|
-
current = current[key]
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
if (current == null) return null
|
|
716
|
-
// JSON_EXTRACT_STRING returns text: unquoted scalars, JSON text for objects and arrays
|
|
717
|
-
if (funcName === 'JSON_EXTRACT_STRING') {
|
|
718
|
-
if (typeof current === 'object') return JSON.stringify(current)
|
|
719
|
-
return String(current)
|
|
720
|
-
}
|
|
721
|
-
return current
|
|
676
|
+
return evaluateJsonExtract({ funcName, node, args, rowIndex })
|
|
722
677
|
}
|
|
723
678
|
|
|
724
679
|
// Check user-defined functions (case-insensitive lookup)
|
|
@@ -735,60 +690,31 @@ export async function evaluateExpr({ node, row, rowIndex, rows, context }) {
|
|
|
735
690
|
|
|
736
691
|
if (node.type === 'cast') {
|
|
737
692
|
const val = await evaluateExpr({ node: node.expr, row, rowIndex, rows, context })
|
|
738
|
-
|
|
739
|
-
const { toType } = node
|
|
740
|
-
if (toType === 'TEXT' || toType === 'STRING' || toType === 'VARCHAR') {
|
|
741
|
-
if (typeof val === 'object') return stringify(val)
|
|
742
|
-
return String(val)
|
|
743
|
-
}
|
|
744
|
-
// Can only cast primitives (and Dates) to other primitive types
|
|
745
|
-
if (typeof val === 'object' && !(val instanceof Date)) {
|
|
746
|
-
if (node.tryCast) return null
|
|
747
|
-
throw new ExecutionError({ message: `Cannot CAST object to ${toType}`, rowIndex, ...node })
|
|
748
|
-
}
|
|
749
|
-
if (toType === 'INTEGER' || toType === 'INT') {
|
|
750
|
-
const num = Number(val)
|
|
751
|
-
if (isNaN(num)) return null
|
|
752
|
-
return Math.trunc(num)
|
|
753
|
-
}
|
|
754
|
-
if (toType === 'BIGINT') {
|
|
755
|
-
if (typeof val === 'bigint') return val
|
|
756
|
-
const num = Number(val)
|
|
757
|
-
// NaN and Infinity have no bigint representation
|
|
758
|
-
if (!isFinite(num)) return null
|
|
759
|
-
return BigInt(Math.trunc(num))
|
|
760
|
-
}
|
|
761
|
-
if (toType === 'FLOAT' || toType === 'REAL' || toType === 'DOUBLE') {
|
|
762
|
-
const num = Number(val)
|
|
763
|
-
if (isNaN(num)) return null
|
|
764
|
-
return num
|
|
765
|
-
}
|
|
766
|
-
if (toType === 'BOOLEAN' || toType === 'BOOL') {
|
|
767
|
-
return Boolean(val)
|
|
768
|
-
}
|
|
769
|
-
if (toType === 'TIMESTAMP') {
|
|
770
|
-
if (typeof val === 'number' || typeof val === 'bigint') {
|
|
771
|
-
const date = new Date(Number(val))
|
|
772
|
-
if (isNaN(date.getTime())) return null
|
|
773
|
-
return date
|
|
774
|
-
}
|
|
775
|
-
return toDate(val)
|
|
776
|
-
}
|
|
693
|
+
return applyCast(node, val, rowIndex)
|
|
777
694
|
}
|
|
778
695
|
|
|
779
|
-
// IN and NOT IN with value lists
|
|
696
|
+
// IN and NOT IN with value lists. IN is a chain of OR'd equalities, so it
|
|
697
|
+
// inherits their three-valued logic: a null on either side of any equality
|
|
698
|
+
// makes a non-match UNKNOWN rather than false, and NOT above it must not
|
|
699
|
+
// turn that into true
|
|
780
700
|
if (node.type === 'in valuelist') {
|
|
781
701
|
const exprVal = await evaluateExpr({ node: node.expr, row, rowIndex, rows, context })
|
|
702
|
+
let sawNull = exprVal == null
|
|
782
703
|
for (const valueNode of node.values) {
|
|
783
704
|
const val = await evaluateExpr({ node: valueNode, row, rowIndex, rows, context })
|
|
784
|
-
if (
|
|
705
|
+
if (val == null) {
|
|
706
|
+
sawNull = true
|
|
707
|
+
} else if (exprVal != null && sqlEquals(exprVal, val)) {
|
|
708
|
+
return true
|
|
709
|
+
}
|
|
785
710
|
}
|
|
786
|
-
return false
|
|
711
|
+
return sawNull ? null : false
|
|
787
712
|
}
|
|
788
|
-
// IN with subqueries
|
|
713
|
+
// IN with subqueries, same three-valued logic as the value-list form
|
|
789
714
|
if (node.type === 'in') {
|
|
790
715
|
const exprVal = await evaluateExpr({ node: node.expr, row, rowIndex, rows, context })
|
|
791
716
|
const subResult = executeStatement({ query: node.subquery, context })
|
|
717
|
+
let sawNull = false
|
|
792
718
|
let innerCount = 0
|
|
793
719
|
for await (const resRow of subResult.rows()) {
|
|
794
720
|
if (++innerCount % YIELD_INTERVAL === 0) {
|
|
@@ -796,9 +722,13 @@ export async function evaluateExpr({ node, row, rowIndex, rows, context }) {
|
|
|
796
722
|
context.signal?.throwIfAborted()
|
|
797
723
|
}
|
|
798
724
|
const value = await resRow.cells[resRow.columns[0]]()
|
|
799
|
-
if (
|
|
725
|
+
if (exprVal == null || value == null) {
|
|
726
|
+
sawNull = true
|
|
727
|
+
} else if (exprVal != null && sqlEquals(exprVal, value)) {
|
|
728
|
+
return true
|
|
729
|
+
}
|
|
800
730
|
}
|
|
801
|
-
return false
|
|
731
|
+
return sawNull ? null : false
|
|
802
732
|
}
|
|
803
733
|
|
|
804
734
|
// EXISTS and NOT EXISTS with subqueries
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { stringify } from '../execute/utils.js'
|
|
2
|
+
import { ArgValueError, ExecutionError } from '../validation/executionErrors.js'
|
|
3
|
+
import { toDate } from './date.js'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @import { CastNode, FunctionNode, SqlPrimitive } from '../types.js'
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Applies a scalar CAST without requiring a row evaluator.
|
|
11
|
+
*
|
|
12
|
+
* @param {CastNode} node
|
|
13
|
+
* @param {SqlPrimitive} value
|
|
14
|
+
* @param {number} [rowIndex]
|
|
15
|
+
* @returns {SqlPrimitive}
|
|
16
|
+
*/
|
|
17
|
+
export function applyCast(node, value, rowIndex) {
|
|
18
|
+
if (value == null) return null
|
|
19
|
+
const { toType } = node
|
|
20
|
+
if (toType === 'TEXT' || toType === 'STRING' || toType === 'VARCHAR') {
|
|
21
|
+
return typeof value === 'object' ? stringify(value) : String(value)
|
|
22
|
+
}
|
|
23
|
+
if (typeof value === 'object' && !(value instanceof Date)) {
|
|
24
|
+
if (node.tryCast) return null
|
|
25
|
+
throw new ExecutionError({ message: `Cannot CAST object to ${toType}`, rowIndex, ...node })
|
|
26
|
+
}
|
|
27
|
+
if (toType === 'INTEGER' || toType === 'INT') {
|
|
28
|
+
const number = Number(value)
|
|
29
|
+
return isNaN(number) ? null : Math.trunc(number)
|
|
30
|
+
}
|
|
31
|
+
if (toType === 'BIGINT') {
|
|
32
|
+
if (typeof value === 'bigint') return value
|
|
33
|
+
const number = Number(value)
|
|
34
|
+
return isFinite(number) ? BigInt(Math.trunc(number)) : null
|
|
35
|
+
}
|
|
36
|
+
if (toType === 'FLOAT' || toType === 'REAL' || toType === 'DOUBLE') {
|
|
37
|
+
const number = Number(value)
|
|
38
|
+
return isNaN(number) ? null : number
|
|
39
|
+
}
|
|
40
|
+
if (toType === 'BOOLEAN' || toType === 'BOOL') return Boolean(value)
|
|
41
|
+
if (typeof value === 'number' || typeof value === 'bigint') {
|
|
42
|
+
const date = new Date(Number(value))
|
|
43
|
+
return isNaN(date.getTime()) ? null : date
|
|
44
|
+
}
|
|
45
|
+
return toDate(value)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Applies the JSON extraction functions shared by row and batch evaluation.
|
|
50
|
+
*
|
|
51
|
+
* @param {object} options
|
|
52
|
+
* @param {string} options.funcName
|
|
53
|
+
* @param {FunctionNode} options.node
|
|
54
|
+
* @param {SqlPrimitive[]} options.args
|
|
55
|
+
* @param {number} [options.rowIndex]
|
|
56
|
+
* @returns {SqlPrimitive}
|
|
57
|
+
*/
|
|
58
|
+
export function evaluateJsonExtract({ funcName, node, args, rowIndex }) {
|
|
59
|
+
let json = args[0]
|
|
60
|
+
const pathArg = args[1]
|
|
61
|
+
if (json == null || pathArg == null) return null
|
|
62
|
+
|
|
63
|
+
if (typeof json === 'string') {
|
|
64
|
+
try {
|
|
65
|
+
json = JSON.parse(json)
|
|
66
|
+
} catch {
|
|
67
|
+
throw new ArgValueError({
|
|
68
|
+
...node,
|
|
69
|
+
message: 'invalid JSON string',
|
|
70
|
+
hint: 'First argument must be valid JSON.',
|
|
71
|
+
rowIndex,
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (typeof json !== 'object' || json instanceof Date) {
|
|
76
|
+
throw new ArgValueError({
|
|
77
|
+
...node,
|
|
78
|
+
message: `first argument must be JSON string or object, got ${typeof json}`,
|
|
79
|
+
rowIndex,
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const path = String(pathArg)
|
|
84
|
+
const normalized = path.startsWith('$') ? path.slice(1) : path
|
|
85
|
+
/** @type {any} */
|
|
86
|
+
let current = json
|
|
87
|
+
const segments = normalized.match(/\.?([^.[]+)|\[(\d+)\]/g) || []
|
|
88
|
+
for (const segment of segments) {
|
|
89
|
+
if (current == null) return null
|
|
90
|
+
if (segment.startsWith('[')) {
|
|
91
|
+
if (!Array.isArray(current)) return null
|
|
92
|
+
current = current[parseInt(segment.slice(1, -1), 10)]
|
|
93
|
+
} else {
|
|
94
|
+
if (typeof current !== 'object' || Array.isArray(current)) return null
|
|
95
|
+
const key = segment.startsWith('.') ? segment.slice(1) : segment
|
|
96
|
+
current = current[key]
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (current == null) return null
|
|
101
|
+
if (funcName === 'JSON_EXTRACT_STRING') {
|
|
102
|
+
return typeof current === 'object' ? JSON.stringify(current) : String(current)
|
|
103
|
+
}
|
|
104
|
+
return current
|
|
105
|
+
}
|
package/src/index.d.ts
CHANGED
|
@@ -1,19 +1,38 @@
|
|
|
1
|
-
import type { AsyncDataSource, AsyncRow, ExecuteContext, ExecuteSqlOptions, ExprNode, ParseSqlOptions, PlanSqlOptions, QueryPlan, QueryResults, SqlPrimitive, Statement, Token } from './types.js'
|
|
1
|
+
import type { AsyncBatch, AsyncDataSource, AsyncRow, ColumnResult, ColumnVector, ExecuteContext, ExecuteSqlOptions, ExprNode, ParseSqlOptions, PlanSqlOptions, QueryPlan, QueryResults, ReadBatchColumnOptions, RowsToBatchesOptions, RowSelection, SqlPrimitive, Statement, Token } from './types.js'
|
|
2
2
|
export type {
|
|
3
|
+
AsyncBatch,
|
|
3
4
|
AsyncCells,
|
|
4
5
|
AsyncDataSource,
|
|
5
6
|
AsyncRow,
|
|
7
|
+
BatchColumn,
|
|
8
|
+
ColumnDemand,
|
|
9
|
+
ColumnReadRequest,
|
|
10
|
+
ColumnResult,
|
|
11
|
+
ColumnVector,
|
|
6
12
|
ExecuteContext,
|
|
7
13
|
ExecuteSqlOptions,
|
|
8
14
|
ExprNode,
|
|
15
|
+
Field,
|
|
16
|
+
NumericArray,
|
|
9
17
|
ParseSqlOptions,
|
|
10
18
|
PlanSqlOptions,
|
|
19
|
+
PreparedScan,
|
|
20
|
+
PrepareScan,
|
|
11
21
|
QueryPlan,
|
|
12
22
|
QueryResults,
|
|
23
|
+
ReadBatchColumnOptions,
|
|
24
|
+
ReadColumn,
|
|
25
|
+
RelationSchema,
|
|
26
|
+
RowsToBatchesOptions,
|
|
27
|
+
RowSelection,
|
|
13
28
|
ScanOptions,
|
|
29
|
+
ScanProperties,
|
|
30
|
+
ScanRequest,
|
|
31
|
+
ScanResidual,
|
|
14
32
|
ScanResults,
|
|
15
33
|
SelectStatement,
|
|
16
34
|
SetOperationStatement,
|
|
35
|
+
SqlType,
|
|
17
36
|
SqlPrimitive,
|
|
18
37
|
Statement,
|
|
19
38
|
Token,
|
|
@@ -96,6 +115,30 @@ export function asyncRow(row: Record<string, SqlPrimitive>, columns: string[]):
|
|
|
96
115
|
|
|
97
116
|
export function cachedDataSource(source: AsyncDataSource): AsyncDataSource
|
|
98
117
|
|
|
118
|
+
export function selectedRowCount(selection: RowSelection): number
|
|
119
|
+
|
|
120
|
+
export function composeSelections(outer: RowSelection, inner: RowSelection): RowSelection
|
|
121
|
+
|
|
122
|
+
export function valueAt(vector: ColumnVector, index: number): SqlPrimitive
|
|
123
|
+
|
|
124
|
+
export function selectVector(vector: ColumnVector, selection: RowSelection): ColumnVector
|
|
125
|
+
|
|
126
|
+
export function readBatchColumn(options: ReadBatchColumnOptions): ColumnResult
|
|
127
|
+
|
|
128
|
+
export function selectBatch(batch: AsyncBatch, selection: RowSelection): AsyncBatch
|
|
129
|
+
|
|
130
|
+
export function rowsToBatches(
|
|
131
|
+
rows: AsyncIterable<AsyncRow>,
|
|
132
|
+
columns: string[],
|
|
133
|
+
options?: RowsToBatchesOptions,
|
|
134
|
+
): AsyncIterable<AsyncBatch>
|
|
135
|
+
|
|
136
|
+
export function batchesToRows(
|
|
137
|
+
batches: AsyncIterable<AsyncBatch>,
|
|
138
|
+
columns: string[],
|
|
139
|
+
signal?: AbortSignal,
|
|
140
|
+
): AsyncIterable<AsyncRow>
|
|
141
|
+
|
|
99
142
|
/**
|
|
100
143
|
* Generates a default alias for a derived column expression.
|
|
101
144
|
* Useful for generating column names pre-execution.
|
package/src/index.js
CHANGED
|
@@ -5,4 +5,13 @@ export { planSql } from './plan/plan.js'
|
|
|
5
5
|
export { tokenizeSql } from './parse/tokenize.js'
|
|
6
6
|
export { collect } from './execute/utils.js'
|
|
7
7
|
export { asyncRow, cachedDataSource } from './backend/dataSource.js'
|
|
8
|
+
export {
|
|
9
|
+
composeSelections,
|
|
10
|
+
readBatchColumn,
|
|
11
|
+
selectBatch,
|
|
12
|
+
selectedRowCount,
|
|
13
|
+
selectVector,
|
|
14
|
+
valueAt,
|
|
15
|
+
} from './backend/batch.js'
|
|
16
|
+
export { batchesToRows, rowsToBatches } from './backend/batchAdapters.js'
|
|
8
17
|
export { derivedAlias } from './expression/alias.js'
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ColumnReadRequest, ColumnResult, ColumnVector, SqlPrimitive } from './types.js'
|
|
2
|
+
|
|
3
|
+
export interface CompiledBatchExpression {
|
|
4
|
+
evaluate(context: ColumnReadRequest): ColumnResult
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export type ValueKernel = (
|
|
8
|
+
vectors: ColumnVector[],
|
|
9
|
+
rowIndex: number,
|
|
10
|
+
streamRowIndex: number,
|
|
11
|
+
) => SqlPrimitive
|
|
12
|
+
|
|
13
|
+
export interface CompileState {
|
|
14
|
+
dependencies: number[]
|
|
15
|
+
dependencyPositions: Map<number, number>
|
|
16
|
+
columns: readonly string[]
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface BatchAggregateInputs {
|
|
20
|
+
keys: CompiledBatchExpression[]
|
|
21
|
+
filters: (CompiledBatchExpression | undefined)[]
|
|
22
|
+
args: (CompiledBatchExpression | undefined)[]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type BatchProjection =
|
|
26
|
+
| { type: 'column', columnIndex: number }
|
|
27
|
+
| { type: 'constant', value: SqlPrimitive }
|
|
28
|
+
| { type: 'expression', expression: CompiledBatchExpression }
|