@quereus/quereus 0.4.11 → 0.5.1
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 +40 -16
- package/dist/src/core/database.d.ts +18 -0
- package/dist/src/core/database.d.ts.map +1 -1
- package/dist/src/core/database.js +22 -0
- package/dist/src/core/database.js.map +1 -1
- package/dist/src/func/builtins/conversion.d.ts +51 -0
- package/dist/src/func/builtins/conversion.d.ts.map +1 -0
- package/dist/src/func/builtins/conversion.js +152 -0
- package/dist/src/func/builtins/conversion.js.map +1 -0
- package/dist/src/func/builtins/index.d.ts.map +1 -1
- package/dist/src/func/builtins/index.js +20 -1
- package/dist/src/func/builtins/index.js.map +1 -1
- package/dist/src/func/builtins/json.d.ts +1 -0
- package/dist/src/func/builtins/json.d.ts.map +1 -1
- package/dist/src/func/builtins/json.js +100 -0
- package/dist/src/func/builtins/json.js.map +1 -1
- package/dist/src/func/builtins/schema.js +1 -1
- package/dist/src/func/builtins/schema.js.map +1 -1
- package/dist/src/index.d.ts +8 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +6 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/runtime/emit/binary.d.ts.map +1 -1
- package/dist/src/runtime/emit/binary.js +6 -2
- package/dist/src/runtime/emit/binary.js.map +1 -1
- package/dist/src/runtime/emit/constraint-check.d.ts.map +1 -1
- package/dist/src/runtime/emit/constraint-check.js +8 -1
- package/dist/src/runtime/emit/constraint-check.js.map +1 -1
- package/dist/src/runtime/emit/insert.d.ts.map +1 -1
- package/dist/src/runtime/emit/insert.js +4 -24
- package/dist/src/runtime/emit/insert.js.map +1 -1
- package/dist/src/runtime/emit/scalar-function.d.ts +9 -0
- package/dist/src/runtime/emit/scalar-function.d.ts.map +1 -1
- package/dist/src/runtime/emit/scalar-function.js +23 -1
- package/dist/src/runtime/emit/scalar-function.js.map +1 -1
- package/dist/src/runtime/emit/unary.d.ts.map +1 -1
- package/dist/src/runtime/emit/unary.js +3 -1
- package/dist/src/runtime/emit/unary.js.map +1 -1
- package/dist/src/schema/column.d.ts +4 -1
- package/dist/src/schema/column.d.ts.map +1 -1
- package/dist/src/schema/column.js +3 -0
- package/dist/src/schema/column.js.map +1 -1
- package/dist/src/schema/function.d.ts +15 -0
- package/dist/src/schema/function.d.ts.map +1 -1
- package/dist/src/schema/function.js.map +1 -1
- package/dist/src/schema/table.d.ts.map +1 -1
- package/dist/src/schema/table.js +10 -1
- package/dist/src/schema/table.js.map +1 -1
- package/dist/src/types/builtin-types.d.ts +37 -0
- package/dist/src/types/builtin-types.d.ts.map +1 -0
- package/dist/src/types/builtin-types.js +359 -0
- package/dist/src/types/builtin-types.js.map +1 -0
- package/dist/src/types/index.d.ts +7 -0
- package/dist/src/types/index.d.ts.map +1 -0
- package/dist/src/types/index.js +13 -0
- package/dist/src/types/index.js.map +1 -0
- package/dist/src/types/json-type.d.ts +8 -0
- package/dist/src/types/json-type.d.ts.map +1 -0
- package/dist/src/types/json-type.js +143 -0
- package/dist/src/types/json-type.js.map +1 -0
- package/dist/src/types/logical-type.d.ts +52 -0
- package/dist/src/types/logical-type.d.ts.map +1 -0
- package/dist/src/types/logical-type.js +34 -0
- package/dist/src/types/logical-type.js.map +1 -0
- package/dist/src/types/plugin-interface.d.ts +9 -0
- package/dist/src/types/plugin-interface.d.ts.map +1 -0
- package/dist/src/types/plugin-interface.js +2 -0
- package/dist/src/types/plugin-interface.js.map +1 -0
- package/dist/src/types/registry.d.ts +72 -0
- package/dist/src/types/registry.d.ts.map +1 -0
- package/dist/src/types/registry.js +168 -0
- package/dist/src/types/registry.js.map +1 -0
- package/dist/src/types/temporal-types.d.ts +17 -0
- package/dist/src/types/temporal-types.d.ts.map +1 -0
- package/dist/src/types/temporal-types.js +178 -0
- package/dist/src/types/temporal-types.js.map +1 -0
- package/dist/src/types/validation.d.ts +52 -0
- package/dist/src/types/validation.d.ts.map +1 -0
- package/dist/src/types/validation.js +96 -0
- package/dist/src/types/validation.js.map +1 -0
- package/dist/src/util/comparison.d.ts +24 -5
- package/dist/src/util/comparison.d.ts.map +1 -1
- package/dist/src/util/comparison.js +71 -9
- package/dist/src/util/comparison.js.map +1 -1
- package/dist/src/util/plugin-loader.d.ts.map +1 -1
- package/dist/src/util/plugin-loader.js +17 -0
- package/dist/src/util/plugin-loader.js.map +1 -1
- package/dist/src/vtab/manifest.d.ts +4 -0
- package/dist/src/vtab/manifest.d.ts.map +1 -1
- package/dist/src/vtab/memory/index.d.ts +1 -0
- package/dist/src/vtab/memory/index.d.ts.map +1 -1
- package/dist/src/vtab/memory/index.js +15 -4
- package/dist/src/vtab/memory/index.js.map +1 -1
- package/dist/src/vtab/memory/layer/manager.d.ts.map +1 -1
- package/dist/src/vtab/memory/layer/manager.js +20 -2
- package/dist/src/vtab/memory/layer/manager.js.map +1 -1
- package/dist/src/vtab/memory/utils/primary-key.d.ts.map +1 -1
- package/dist/src/vtab/memory/utils/primary-key.js +17 -12
- package/dist/src/vtab/memory/utils/primary-key.js.map +1 -1
- package/package.json +5 -4
- package/src/core/database.ts +24 -0
- package/src/func/builtins/conversion.ts +201 -0
- package/src/func/builtins/index.ts +20 -1
- package/src/func/builtins/json.ts +121 -0
- package/src/func/builtins/schema.ts +1 -1
- package/src/index.ts +35 -0
- package/src/runtime/emit/binary.ts +8 -2
- package/src/runtime/emit/constraint-check.ts +9 -1
- package/src/runtime/emit/insert.ts +4 -16
- package/src/runtime/emit/scalar-function.ts +27 -1
- package/src/runtime/emit/unary.ts +4 -1
- package/src/schema/column.ts +8 -1
- package/src/schema/function.ts +18 -0
- package/src/schema/table.ts +411 -398
- package/src/types/builtin-types.ts +350 -0
- package/src/types/index.ts +17 -0
- package/src/types/json-type.ts +152 -0
- package/src/types/logical-type.ts +75 -0
- package/src/types/plugin-interface.ts +10 -0
- package/src/types/registry.ts +200 -0
- package/src/types/temporal-types.ts +167 -0
- package/src/types/validation.ts +120 -0
- package/src/util/comparison.ts +87 -14
- package/src/util/plugin-loader.ts +19 -0
- package/src/vtab/manifest.ts +7 -1
- package/src/vtab/memory/index.ts +191 -178
- package/src/vtab/memory/layer/manager.ts +28 -2
- package/src/vtab/memory/utils/primary-key.ts +19 -14
|
@@ -2,10 +2,20 @@ import fastJsonPatch from 'fast-json-patch';
|
|
|
2
2
|
import type { Operation } from 'fast-json-patch';
|
|
3
3
|
const { applyPatch } = fastJsonPatch;
|
|
4
4
|
|
|
5
|
+
// moat-maker: Runtime validation library with TypeScript-like syntax
|
|
6
|
+
// Used for json_schema() function to validate JSON against structural schemas
|
|
7
|
+
import { validator } from 'moat-maker';
|
|
8
|
+
|
|
5
9
|
import { createLogger } from '../../common/logger.js';
|
|
6
10
|
import type { SqlValue, JSONValue } from '../../common/types.js';
|
|
7
11
|
import { createScalarFunction, createAggregateFunction } from '../registration.js';
|
|
8
12
|
import { safeJsonParse, resolveJsonPathForModify, prepareJsonValue, deepCopyJson, getJsonType } from './json-helpers.js';
|
|
13
|
+
import type { ScalarFunctionCallNode } from '../../planner/nodes/function.js';
|
|
14
|
+
import type { EmissionContext } from '../../runtime/emission-context.js';
|
|
15
|
+
import type { Instruction, RuntimeContext } from '../../runtime/types.js';
|
|
16
|
+
import { PlanNodeType } from '../../planner/nodes/plan-node-type.js';
|
|
17
|
+
import { LiteralNode } from '../../planner/nodes/scalar.js';
|
|
18
|
+
import { emitPlanNode } from '../../runtime/emitters.js';
|
|
9
19
|
|
|
10
20
|
const log = createLogger('func:builtins:json');
|
|
11
21
|
const errorLog = log.extend('error');
|
|
@@ -20,6 +30,117 @@ export const jsonValidFunc = createScalarFunction(
|
|
|
20
30
|
}
|
|
21
31
|
);
|
|
22
32
|
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Custom emitter for json_schema that caches compiled validators in the EmissionContext.
|
|
37
|
+
* This provides significant performance improvements for CHECK constraints and repeated validations.
|
|
38
|
+
*/
|
|
39
|
+
function emitJsonSchema(
|
|
40
|
+
plan: ScalarFunctionCallNode,
|
|
41
|
+
ctx: EmissionContext,
|
|
42
|
+
defaultEmit: (plan: ScalarFunctionCallNode, ctx: EmissionContext) => Instruction
|
|
43
|
+
): Instruction {
|
|
44
|
+
// Check if the second argument (schema definition) is a constant
|
|
45
|
+
const schemaDefArg = plan.operands[1];
|
|
46
|
+
|
|
47
|
+
if (schemaDefArg?.nodeType === PlanNodeType.Literal) {
|
|
48
|
+
const literalNode = schemaDefArg as LiteralNode;
|
|
49
|
+
const schemaDef = literalNode.getValue();
|
|
50
|
+
|
|
51
|
+
if (typeof schemaDef === 'string') {
|
|
52
|
+
try {
|
|
53
|
+
// Compile the validator once at emission time using moat-maker
|
|
54
|
+
// moat-maker uses template literals, so we need to create a validator dynamically
|
|
55
|
+
// Template literals have a special structure: an array with a 'raw' property
|
|
56
|
+
// We create this structure manually to simulate a template literal
|
|
57
|
+
const parts: any = [schemaDef];
|
|
58
|
+
parts.raw = [schemaDef];
|
|
59
|
+
const compiledValidator = validator(parts, ...[]);
|
|
60
|
+
|
|
61
|
+
// Emit only the JSON argument (first operand)
|
|
62
|
+
const jsonArgInstruction = emitPlanNode(plan.operands[0], ctx);
|
|
63
|
+
|
|
64
|
+
// Create optimized runtime function that uses the cached validator
|
|
65
|
+
// The validator is captured in the closure, so it lives with the plan
|
|
66
|
+
function run(_rctx: RuntimeContext, ...args: any[]): SqlValue {
|
|
67
|
+
const json = args[0];
|
|
68
|
+
if (typeof json !== 'string') return 0;
|
|
69
|
+
|
|
70
|
+
let data: JSONValue | null;
|
|
71
|
+
try {
|
|
72
|
+
data = JSON.parse(json) as JSONValue;
|
|
73
|
+
} catch {
|
|
74
|
+
return 0; // Invalid JSON
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Use the cached compiled validator
|
|
78
|
+
try {
|
|
79
|
+
// moat-maker's .matches() returns true if valid, false otherwise
|
|
80
|
+
const isValid = compiledValidator.matches(data);
|
|
81
|
+
return isValid ? 1 : 0;
|
|
82
|
+
} catch (e) {
|
|
83
|
+
errorLog('json_schema validation failed: %O', e);
|
|
84
|
+
return 0;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
params: [jsonArgInstruction],
|
|
90
|
+
run,
|
|
91
|
+
note: `json_schema(cached:${schemaDef.substring(0, 20)}...)`
|
|
92
|
+
};
|
|
93
|
+
} catch (e) {
|
|
94
|
+
errorLog('Failed to compile schema at emission time: %O', e);
|
|
95
|
+
// Fall through to default emission
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// If schema is not a constant or compilation failed, use default emission
|
|
101
|
+
return defaultEmit(plan, ctx);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// json_schema(X, schema_def)
|
|
105
|
+
// Note: This function uses a custom emitter that caches compiled validators
|
|
106
|
+
// during plan emission for better performance.
|
|
107
|
+
export const jsonSchemaFunc = createScalarFunction(
|
|
108
|
+
{ name: 'json_schema', numArgs: 2, deterministic: true },
|
|
109
|
+
(json: SqlValue, schemaDef: SqlValue): SqlValue => {
|
|
110
|
+
// This is the fallback implementation for when no cache is available
|
|
111
|
+
// (e.g., during direct function calls outside of query execution)
|
|
112
|
+
|
|
113
|
+
// Schema definition must be a string
|
|
114
|
+
if (typeof schemaDef !== 'string') return 0;
|
|
115
|
+
|
|
116
|
+
// Parse the JSON value - need to check if it's valid JSON first
|
|
117
|
+
if (typeof json !== 'string') return 0;
|
|
118
|
+
|
|
119
|
+
let data: JSONValue | null;
|
|
120
|
+
try {
|
|
121
|
+
data = JSON.parse(json) as JSONValue;
|
|
122
|
+
} catch {
|
|
123
|
+
return 0; // Invalid JSON
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Compile and validate using moat-maker (no caching in fallback path)
|
|
127
|
+
try {
|
|
128
|
+
const parts: any = [schemaDef];
|
|
129
|
+
parts.raw = [schemaDef];
|
|
130
|
+
const compiledValidator = validator(parts, ...[]);
|
|
131
|
+
// moat-maker's .matches() returns true if valid, false otherwise
|
|
132
|
+
const isValid = compiledValidator.matches(data);
|
|
133
|
+
return isValid ? 1 : 0;
|
|
134
|
+
} catch (e) {
|
|
135
|
+
errorLog('json_schema validation failed: %O', e);
|
|
136
|
+
return 0;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
// Attach the custom emitter to the function schema
|
|
142
|
+
jsonSchemaFunc.customEmitter = emitJsonSchema;
|
|
143
|
+
|
|
23
144
|
// json_type(X, P?)
|
|
24
145
|
export const jsonTypeFunc = createScalarFunction(
|
|
25
146
|
{ name: 'json_type', numArgs: -1, deterministic: true },
|
|
@@ -49,7 +49,7 @@ export const schemaFunc = createIntegratedTableValuedFunction(
|
|
|
49
49
|
for (const tableSchema of schemaInstance.getAllTables()) {
|
|
50
50
|
let createSql: string | null = null;
|
|
51
51
|
try {
|
|
52
|
-
const columnsStr = tableSchema.columns.map((c: ColumnSchema) => `"${c.name}" ${c.
|
|
52
|
+
const columnsStr = tableSchema.columns.map((c: ColumnSchema) => `"${c.name}" ${c.logicalType.name}`).join(', ');
|
|
53
53
|
const argsStr = Object.entries(tableSchema.vtabArgs ?? {}).map(([key, value]) => `${key}=${value}`).join(', ');
|
|
54
54
|
createSql = `create table "${tableSchema.name}" (${columnsStr}) using ${tableSchema.vtabModuleName}(${argsStr})`;
|
|
55
55
|
} catch {
|
package/src/index.ts
CHANGED
|
@@ -47,6 +47,40 @@ export {
|
|
|
47
47
|
resolveCollation
|
|
48
48
|
} from './util/comparison.js';
|
|
49
49
|
|
|
50
|
+
// Type system
|
|
51
|
+
export type { LogicalType, CollationFunction as TypeCollationFunction } from './types/logical-type.js';
|
|
52
|
+
export { PhysicalType } from './types/logical-type.js';
|
|
53
|
+
export {
|
|
54
|
+
NULL_TYPE,
|
|
55
|
+
INTEGER_TYPE,
|
|
56
|
+
REAL_TYPE,
|
|
57
|
+
TEXT_TYPE,
|
|
58
|
+
BLOB_TYPE,
|
|
59
|
+
BOOLEAN_TYPE,
|
|
60
|
+
NUMERIC_TYPE,
|
|
61
|
+
ANY_TYPE
|
|
62
|
+
} from './types/builtin-types.js';
|
|
63
|
+
export {
|
|
64
|
+
DATE_TYPE,
|
|
65
|
+
TIME_TYPE,
|
|
66
|
+
DATETIME_TYPE
|
|
67
|
+
} from './types/temporal-types.js';
|
|
68
|
+
export { JSON_TYPE } from './types/json-type.js';
|
|
69
|
+
export {
|
|
70
|
+
typeRegistry,
|
|
71
|
+
registerType,
|
|
72
|
+
getType,
|
|
73
|
+
getTypeOrDefault,
|
|
74
|
+
inferType
|
|
75
|
+
} from './types/registry.js';
|
|
76
|
+
export {
|
|
77
|
+
validateValue,
|
|
78
|
+
parseValue,
|
|
79
|
+
validateAndParse,
|
|
80
|
+
isValidForType,
|
|
81
|
+
tryParse
|
|
82
|
+
} from './types/validation.js';
|
|
83
|
+
|
|
50
84
|
// SQL Parser and Compiler
|
|
51
85
|
export { Parser } from './parser/parser.js';
|
|
52
86
|
export { Lexer, TokenType, KEYWORDS } from './parser/lexer.js';
|
|
@@ -90,6 +124,7 @@ export type {
|
|
|
90
124
|
VTablePluginInfo,
|
|
91
125
|
FunctionPluginInfo,
|
|
92
126
|
CollationPluginInfo,
|
|
127
|
+
TypePluginInfo,
|
|
93
128
|
PluginRegistrations
|
|
94
129
|
} from './vtab/manifest.js';
|
|
95
130
|
|
|
@@ -10,7 +10,10 @@ import { simpleLike } from "../../util/patterns.js";
|
|
|
10
10
|
import type { EmissionContext } from "../emission-context.js";
|
|
11
11
|
|
|
12
12
|
export function emitBinaryOp(plan: BinaryOpNode, ctx: EmissionContext): Instruction {
|
|
13
|
-
|
|
13
|
+
// Normalize operator to uppercase for case-insensitive matching of keywords
|
|
14
|
+
const operator = plan.expression.operator.toUpperCase();
|
|
15
|
+
|
|
16
|
+
switch (operator) {
|
|
14
17
|
case '+':
|
|
15
18
|
case '-':
|
|
16
19
|
case '*':
|
|
@@ -227,9 +230,12 @@ export function emitConcatOp(plan: BinaryOpNode, ctx: EmissionContext): Instruct
|
|
|
227
230
|
}
|
|
228
231
|
|
|
229
232
|
export function emitLogicalOp(plan: BinaryOpNode, ctx: EmissionContext): Instruction {
|
|
233
|
+
// Normalize operator to uppercase for case-insensitive matching
|
|
234
|
+
const operator = plan.expression.operator.toUpperCase();
|
|
235
|
+
|
|
230
236
|
function run(ctx: RuntimeContext, v1: SqlValue, v2: SqlValue): SqlValue {
|
|
231
237
|
// SQL three-valued logic
|
|
232
|
-
switch (
|
|
238
|
+
switch (operator) {
|
|
233
239
|
case 'AND': {
|
|
234
240
|
// NULL AND x -> NULL if x is true or NULL, otherwise 0
|
|
235
241
|
// 0 AND x -> 0
|
|
@@ -9,12 +9,14 @@ import type { RowConstraintSchema, TableSchema } from '../../schema/table.js';
|
|
|
9
9
|
import type { RowDescriptor } from '../../planner/nodes/plan-node.js';
|
|
10
10
|
import { RowOpFlag } from '../../schema/table.js';
|
|
11
11
|
import { withAsyncRowContext, createRowSlot } from '../context-helpers.js';
|
|
12
|
+
import { expressionToString } from '../../util/ast-stringify.js';
|
|
12
13
|
|
|
13
14
|
interface ConstraintMetadataEntry {
|
|
14
15
|
schema: RowConstraintSchema;
|
|
15
16
|
flatRowDescriptor: RowDescriptor;
|
|
16
17
|
evaluator: (ctx: RuntimeContext) => OutputValue;
|
|
17
18
|
constraintName: string;
|
|
19
|
+
constraintExpr: string; // Stringified constraint expression
|
|
18
20
|
shouldDefer: boolean;
|
|
19
21
|
baseTable: string;
|
|
20
22
|
contextRow?: Row; // Mutation context row if present
|
|
@@ -52,11 +54,13 @@ export function emitConstraintCheck(plan: ConstraintCheckNode, ctx: EmissionCont
|
|
|
52
54
|
const constraintMetadata: ConstraintMetadataEntry[] = plan.constraintChecks.map((check, idx) => {
|
|
53
55
|
const evaluatorInstruction = checkEvaluators[idx];
|
|
54
56
|
const constraintName = check.constraint.name ?? generateDefaultConstraintName(tableSchema, check.constraint);
|
|
57
|
+
const constraintExpr = expressionToString(check.constraint.expr);
|
|
55
58
|
return {
|
|
56
59
|
schema: check.constraint,
|
|
57
60
|
flatRowDescriptor: plan.flatRowDescriptor,
|
|
58
61
|
evaluator: evaluatorInstruction.run,
|
|
59
62
|
constraintName,
|
|
63
|
+
constraintExpr,
|
|
60
64
|
shouldDefer: Boolean(check.deferrable || check.initiallyDeferred || check.containsSubquery),
|
|
61
65
|
baseTable: `${tableSchema.schemaName}.${tableSchema.name}`,
|
|
62
66
|
contextRow: undefined,
|
|
@@ -261,8 +265,12 @@ async function checkCheckConstraints(
|
|
|
261
265
|
// CHECK constraint passes if result is truthy or NULL
|
|
262
266
|
// It fails only if result is false or 0 (SQLite-style numeric boolean)
|
|
263
267
|
if (result === false || result === 0) {
|
|
268
|
+
// Include constraint expression in error message for better debugging
|
|
269
|
+
const exprHint = metadata.constraintExpr.length <= 60
|
|
270
|
+
? ` (${metadata.constraintExpr})`
|
|
271
|
+
: '';
|
|
264
272
|
throw new QuereusError(
|
|
265
|
-
`CHECK constraint failed: ${metadata.constraintName}`,
|
|
273
|
+
`CHECK constraint failed: ${metadata.constraintName}${exprHint}`,
|
|
266
274
|
StatusCode.CONSTRAINT
|
|
267
275
|
);
|
|
268
276
|
}
|
|
@@ -3,12 +3,11 @@ import type { Instruction, RuntimeContext, InstructionRun } from '../types.js';
|
|
|
3
3
|
import { emitPlanNode } from '../emitters.js';
|
|
4
4
|
import type { Row } from '../../common/types.js';
|
|
5
5
|
import type { EmissionContext } from '../emission-context.js';
|
|
6
|
-
import { SqlDataType, type SqlValue } from '../../common/types.js';
|
|
7
|
-
import { applyIntegerAffinity, applyRealAffinity, applyNumericAffinity, applyTextAffinity, applyBlobAffinity } from '../../util/affinity.js';
|
|
8
6
|
|
|
9
7
|
export function emitInsert(plan: InsertNode, ctx: EmissionContext): Instruction {
|
|
10
8
|
// INSERT node now only handles data transformations and passes flat rows through.
|
|
11
9
|
// The actual database insert operations are handled by DmlExecutorNode.
|
|
10
|
+
// Type conversion is handled by the table manager's validateAndParse in performInsert.
|
|
12
11
|
async function* run(_ctx: RuntimeContext, sourceValue: AsyncIterable<Row>): AsyncIterable<Row> {
|
|
13
12
|
const tableSchema = plan.table.tableSchema;
|
|
14
13
|
const colCount = tableSchema.columns.length;
|
|
@@ -23,21 +22,10 @@ export function emitInsert(plan: InsertNode, ctx: EmissionContext): Instruction
|
|
|
23
22
|
flatRow[i] = null;
|
|
24
23
|
}
|
|
25
24
|
|
|
26
|
-
// Fill NEW section with source values
|
|
25
|
+
// Fill NEW section with source values (indices n..2n-1)
|
|
26
|
+
// No affinity conversion here - let the type system handle it
|
|
27
27
|
for (let colIdx = 0; colIdx < colCount; colIdx++) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
let converted: SqlValue;
|
|
31
|
-
switch (tableSchema.columns[colIdx].affinity) {
|
|
32
|
-
case SqlDataType.INTEGER: converted = applyIntegerAffinity(sourceValue); break;
|
|
33
|
-
case SqlDataType.REAL: converted = applyRealAffinity(sourceValue); break;
|
|
34
|
-
case SqlDataType.NUMERIC: converted = applyNumericAffinity(sourceValue); break;
|
|
35
|
-
case SqlDataType.TEXT: converted = applyTextAffinity(sourceValue); break;
|
|
36
|
-
case SqlDataType.BLOB: converted = applyBlobAffinity(sourceValue); break;
|
|
37
|
-
default: converted = sourceValue;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
flatRow[colCount + colIdx] = converted;
|
|
28
|
+
flatRow[colCount + colIdx] = sourceRow[colIdx];
|
|
41
29
|
}
|
|
42
30
|
|
|
43
31
|
yield flatRow;
|
|
@@ -7,7 +7,11 @@ import type { EmissionContext } from '../emission-context.js';
|
|
|
7
7
|
import type { ScalarFunctionSchema } from '../../schema/function.js';
|
|
8
8
|
import { isScalarFunctionSchema } from '../../schema/function.js';
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Default emission logic for scalar function calls.
|
|
12
|
+
* This is exported so custom emitters can call it if needed.
|
|
13
|
+
*/
|
|
14
|
+
export function emitScalarFunctionCallDefault(plan: ScalarFunctionCallNode, ctx: EmissionContext): Instruction {
|
|
11
15
|
const functionName = plan.expression.name.toLowerCase();
|
|
12
16
|
const functionSchema = plan.functionSchema;
|
|
13
17
|
|
|
@@ -41,3 +45,25 @@ export function emitScalarFunctionCall(plan: ScalarFunctionCallNode, ctx: Emissi
|
|
|
41
45
|
`${plan.expression.name}(${plan.operands.length})`
|
|
42
46
|
);
|
|
43
47
|
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Main emitter for scalar function calls.
|
|
51
|
+
* Checks if the function has a custom emitter and uses it, otherwise uses default logic.
|
|
52
|
+
*/
|
|
53
|
+
export function emitScalarFunctionCall(plan: ScalarFunctionCallNode, ctx: EmissionContext): Instruction {
|
|
54
|
+
const functionSchema = plan.functionSchema;
|
|
55
|
+
|
|
56
|
+
// Validate that it's a scalar function
|
|
57
|
+
if (!isScalarFunctionSchema(functionSchema)) {
|
|
58
|
+
const functionName = plan.expression.name.toLowerCase();
|
|
59
|
+
throw new QuereusError(`Function ${functionName} is not a scalar function`, StatusCode.ERROR);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Check if function has a custom emitter
|
|
63
|
+
if (functionSchema.customEmitter) {
|
|
64
|
+
return functionSchema.customEmitter(plan, ctx, emitScalarFunctionCallDefault);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Use default emission logic
|
|
68
|
+
return emitScalarFunctionCallDefault(plan, ctx);
|
|
69
|
+
}
|
|
@@ -12,7 +12,10 @@ export function emitUnaryOp(plan: UnaryOpNode, ctx: EmissionContext): Instructio
|
|
|
12
12
|
let run: (ctx: RuntimeContext, operand: SqlValue) => SqlValue;
|
|
13
13
|
let note: string;
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
// Normalize operator to uppercase for case-insensitive matching
|
|
16
|
+
const operator = plan.expression.operator.toUpperCase();
|
|
17
|
+
|
|
18
|
+
switch (operator) {
|
|
16
19
|
case 'NOT':
|
|
17
20
|
run = (ctx: RuntimeContext, operand: SqlValue) => {
|
|
18
21
|
// SQL NOT: NULL -> NULL, 0 -> 1, anything else -> 0
|
package/src/schema/column.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SqlDataType } from '../common/types.js';
|
|
2
2
|
import type { Expression } from '../parser/ast.js';
|
|
3
|
+
import type { LogicalType } from '../types/logical-type.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Represents the schema definition of a single column in a table.
|
|
@@ -7,7 +8,9 @@ import type { Expression } from '../parser/ast.js';
|
|
|
7
8
|
export interface ColumnSchema {
|
|
8
9
|
/** Column name */
|
|
9
10
|
name: string;
|
|
10
|
-
/**
|
|
11
|
+
/** Logical type definition */
|
|
12
|
+
logicalType: LogicalType;
|
|
13
|
+
/** Data type affinity (TEXT, INTEGER, REAL, BLOB, NUMERIC) - kept for backward compatibility during transition */
|
|
11
14
|
affinity: SqlDataType;
|
|
12
15
|
/** Whether the column has a NOT NULL constraint */
|
|
13
16
|
notNull: boolean;
|
|
@@ -34,8 +37,12 @@ export interface ColumnSchema {
|
|
|
34
37
|
* @returns A new column schema with default values
|
|
35
38
|
*/
|
|
36
39
|
export function createDefaultColumnSchema(name: string, defaultNotNull: boolean = true): ColumnSchema {
|
|
40
|
+
// Import TEXT_TYPE lazily to avoid circular dependencies
|
|
41
|
+
const { TEXT_TYPE } = require('../types/builtin-types.js');
|
|
42
|
+
|
|
37
43
|
return {
|
|
38
44
|
name: name,
|
|
45
|
+
logicalType: TEXT_TYPE,
|
|
39
46
|
affinity: SqlDataType.TEXT,
|
|
40
47
|
notNull: defaultNotNull, // Third Manifesto: default to NOT NULL
|
|
41
48
|
primaryKey: false,
|
package/src/schema/function.ts
CHANGED
|
@@ -33,6 +33,17 @@ export type AggregateReducer<T = any> = (accumulator: T, ...args: SqlValue[]) =>
|
|
|
33
33
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
34
34
|
export type AggregateFinalizer<T = any> = (accumulator: T) => SqlValue;
|
|
35
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Custom emitter hook for functions that need special emission logic.
|
|
38
|
+
* This allows functions to cache compiled state in the EmissionContext,
|
|
39
|
+
* optimize constant arguments, or perform other emission-time optimizations.
|
|
40
|
+
*/
|
|
41
|
+
export type CustomEmitterHook = (
|
|
42
|
+
plan: any, // ScalarFunctionCallNode, but we avoid circular dependency
|
|
43
|
+
ctx: any, // EmissionContext, but we avoid circular dependency
|
|
44
|
+
defaultEmit: (plan: any, ctx: any) => any // Instruction
|
|
45
|
+
) => any; // Instruction
|
|
46
|
+
|
|
36
47
|
/**
|
|
37
48
|
* Base interface for all function schemas with common properties.
|
|
38
49
|
*/
|
|
@@ -47,6 +58,13 @@ interface BaseFunctionSchema {
|
|
|
47
58
|
userData?: unknown;
|
|
48
59
|
/** Return type information */
|
|
49
60
|
returnType: BaseType;
|
|
61
|
+
/**
|
|
62
|
+
* Optional custom emitter hook for emission-time optimizations.
|
|
63
|
+
* If provided, this function will be called during plan emission instead of
|
|
64
|
+
* the default scalar function emitter. The hook receives the plan node,
|
|
65
|
+
* emission context, and a reference to the default emitter.
|
|
66
|
+
*/
|
|
67
|
+
customEmitter?: CustomEmitterHook;
|
|
50
68
|
}
|
|
51
69
|
|
|
52
70
|
/**
|