@tanstack/db 0.0.14 → 0.0.16
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/dist/cjs/collection.cjs +117 -104
- package/dist/cjs/collection.cjs.map +1 -1
- package/dist/cjs/collection.d.cts +18 -21
- package/dist/cjs/index.cjs +31 -13
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +0 -1
- package/dist/cjs/query/builder/functions.cjs +107 -0
- package/dist/cjs/query/builder/functions.cjs.map +1 -0
- package/dist/cjs/query/builder/functions.d.cts +38 -0
- package/dist/cjs/query/builder/index.cjs +499 -0
- package/dist/cjs/query/builder/index.cjs.map +1 -0
- package/dist/cjs/query/builder/index.d.cts +324 -0
- package/dist/cjs/query/builder/ref-proxy.cjs +92 -0
- package/dist/cjs/query/builder/ref-proxy.cjs.map +1 -0
- package/dist/cjs/query/builder/ref-proxy.d.cts +28 -0
- package/dist/cjs/query/builder/types.d.cts +81 -0
- package/dist/cjs/query/compiler/evaluators.cjs +261 -0
- package/dist/cjs/query/compiler/evaluators.cjs.map +1 -0
- package/dist/cjs/query/compiler/evaluators.d.cts +11 -0
- package/dist/cjs/query/compiler/group-by.cjs +271 -0
- package/dist/cjs/query/compiler/group-by.cjs.map +1 -0
- package/dist/cjs/query/compiler/group-by.d.cts +7 -0
- package/dist/cjs/query/compiler/index.cjs +181 -0
- package/dist/cjs/query/compiler/index.cjs.map +1 -0
- package/dist/cjs/query/compiler/index.d.cts +15 -0
- package/dist/cjs/query/compiler/joins.cjs +116 -0
- package/dist/cjs/query/compiler/joins.cjs.map +1 -0
- package/dist/cjs/query/compiler/joins.d.cts +11 -0
- package/dist/cjs/query/compiler/order-by.cjs +89 -0
- package/dist/cjs/query/compiler/order-by.cjs.map +1 -0
- package/dist/cjs/query/compiler/order-by.d.cts +9 -0
- package/dist/cjs/query/compiler/select.cjs +57 -0
- package/dist/cjs/query/compiler/select.cjs.map +1 -0
- package/dist/cjs/query/compiler/select.d.cts +15 -0
- package/dist/cjs/query/index.d.cts +5 -5
- package/dist/cjs/query/ir.cjs +57 -0
- package/dist/cjs/query/ir.cjs.map +1 -0
- package/dist/cjs/query/ir.d.cts +81 -0
- package/dist/cjs/query/live-query-collection.cjs +224 -0
- package/dist/cjs/query/live-query-collection.cjs.map +1 -0
- package/dist/cjs/query/live-query-collection.d.cts +124 -0
- package/dist/cjs/transactions.cjs +20 -13
- package/dist/cjs/transactions.cjs.map +1 -1
- package/dist/cjs/transactions.d.cts +10 -1
- package/dist/cjs/types.d.cts +13 -0
- package/dist/esm/collection.d.ts +18 -21
- package/dist/esm/collection.js +118 -105
- package/dist/esm/collection.js.map +1 -1
- package/dist/esm/index.d.ts +0 -1
- package/dist/esm/index.js +30 -12
- package/dist/esm/query/builder/functions.d.ts +38 -0
- package/dist/esm/query/builder/functions.js +107 -0
- package/dist/esm/query/builder/functions.js.map +1 -0
- package/dist/esm/query/builder/index.d.ts +324 -0
- package/dist/esm/query/builder/index.js +499 -0
- package/dist/esm/query/builder/index.js.map +1 -0
- package/dist/esm/query/builder/ref-proxy.d.ts +28 -0
- package/dist/esm/query/builder/ref-proxy.js +92 -0
- package/dist/esm/query/builder/ref-proxy.js.map +1 -0
- package/dist/esm/query/builder/types.d.ts +81 -0
- package/dist/esm/query/compiler/evaluators.d.ts +11 -0
- package/dist/esm/query/compiler/evaluators.js +261 -0
- package/dist/esm/query/compiler/evaluators.js.map +1 -0
- package/dist/esm/query/compiler/group-by.d.ts +7 -0
- package/dist/esm/query/compiler/group-by.js +271 -0
- package/dist/esm/query/compiler/group-by.js.map +1 -0
- package/dist/esm/query/compiler/index.d.ts +15 -0
- package/dist/esm/query/compiler/index.js +181 -0
- package/dist/esm/query/compiler/index.js.map +1 -0
- package/dist/esm/query/compiler/joins.d.ts +11 -0
- package/dist/esm/query/compiler/joins.js +116 -0
- package/dist/esm/query/compiler/joins.js.map +1 -0
- package/dist/esm/query/compiler/order-by.d.ts +9 -0
- package/dist/esm/query/compiler/order-by.js +89 -0
- package/dist/esm/query/compiler/order-by.js.map +1 -0
- package/dist/esm/query/compiler/select.d.ts +15 -0
- package/dist/esm/query/compiler/select.js +57 -0
- package/dist/esm/query/compiler/select.js.map +1 -0
- package/dist/esm/query/index.d.ts +5 -5
- package/dist/esm/query/ir.d.ts +81 -0
- package/dist/esm/query/ir.js +57 -0
- package/dist/esm/query/ir.js.map +1 -0
- package/dist/esm/query/live-query-collection.d.ts +124 -0
- package/dist/esm/query/live-query-collection.js +224 -0
- package/dist/esm/query/live-query-collection.js.map +1 -0
- package/dist/esm/transactions.d.ts +10 -1
- package/dist/esm/transactions.js +20 -13
- package/dist/esm/transactions.js.map +1 -1
- package/dist/esm/types.d.ts +13 -0
- package/package.json +3 -4
- package/src/collection.ts +152 -129
- package/src/index.ts +0 -1
- package/src/query/builder/functions.ts +267 -0
- package/src/query/builder/index.ts +648 -0
- package/src/query/builder/ref-proxy.ts +156 -0
- package/src/query/builder/types.ts +282 -0
- package/src/query/compiler/evaluators.ts +315 -0
- package/src/query/compiler/group-by.ts +428 -0
- package/src/query/compiler/index.ts +276 -0
- package/src/query/compiler/joins.ts +228 -0
- package/src/query/compiler/order-by.ts +139 -0
- package/src/query/compiler/select.ts +173 -0
- package/src/query/index.ts +54 -5
- package/src/query/ir.ts +128 -0
- package/src/query/live-query-collection.ts +512 -0
- package/src/transactions.ts +27 -16
- package/src/types.ts +15 -0
- package/dist/cjs/query/compiled-query.cjs +0 -160
- package/dist/cjs/query/compiled-query.cjs.map +0 -1
- package/dist/cjs/query/compiled-query.d.cts +0 -20
- package/dist/cjs/query/evaluators.cjs +0 -161
- package/dist/cjs/query/evaluators.cjs.map +0 -1
- package/dist/cjs/query/evaluators.d.cts +0 -14
- package/dist/cjs/query/extractors.cjs +0 -122
- package/dist/cjs/query/extractors.cjs.map +0 -1
- package/dist/cjs/query/extractors.d.cts +0 -22
- package/dist/cjs/query/functions.cjs +0 -152
- package/dist/cjs/query/functions.cjs.map +0 -1
- package/dist/cjs/query/functions.d.cts +0 -21
- package/dist/cjs/query/group-by.cjs +0 -88
- package/dist/cjs/query/group-by.cjs.map +0 -1
- package/dist/cjs/query/group-by.d.cts +0 -40
- package/dist/cjs/query/joins.cjs +0 -141
- package/dist/cjs/query/joins.cjs.map +0 -1
- package/dist/cjs/query/joins.d.cts +0 -14
- package/dist/cjs/query/order-by.cjs +0 -185
- package/dist/cjs/query/order-by.cjs.map +0 -1
- package/dist/cjs/query/order-by.d.cts +0 -3
- package/dist/cjs/query/pipeline-compiler.cjs +0 -89
- package/dist/cjs/query/pipeline-compiler.cjs.map +0 -1
- package/dist/cjs/query/pipeline-compiler.d.cts +0 -10
- package/dist/cjs/query/query-builder.cjs +0 -307
- package/dist/cjs/query/query-builder.cjs.map +0 -1
- package/dist/cjs/query/query-builder.d.cts +0 -225
- package/dist/cjs/query/schema.d.cts +0 -100
- package/dist/cjs/query/select.cjs +0 -130
- package/dist/cjs/query/select.cjs.map +0 -1
- package/dist/cjs/query/select.d.cts +0 -3
- package/dist/cjs/query/types.d.cts +0 -189
- package/dist/cjs/query/utils.cjs +0 -154
- package/dist/cjs/query/utils.cjs.map +0 -1
- package/dist/cjs/query/utils.d.cts +0 -37
- package/dist/cjs/utils.cjs +0 -17
- package/dist/cjs/utils.cjs.map +0 -1
- package/dist/cjs/utils.d.cts +0 -3
- package/dist/esm/query/compiled-query.d.ts +0 -20
- package/dist/esm/query/compiled-query.js +0 -160
- package/dist/esm/query/compiled-query.js.map +0 -1
- package/dist/esm/query/evaluators.d.ts +0 -14
- package/dist/esm/query/evaluators.js +0 -161
- package/dist/esm/query/evaluators.js.map +0 -1
- package/dist/esm/query/extractors.d.ts +0 -22
- package/dist/esm/query/extractors.js +0 -122
- package/dist/esm/query/extractors.js.map +0 -1
- package/dist/esm/query/functions.d.ts +0 -21
- package/dist/esm/query/functions.js +0 -152
- package/dist/esm/query/functions.js.map +0 -1
- package/dist/esm/query/group-by.d.ts +0 -40
- package/dist/esm/query/group-by.js +0 -88
- package/dist/esm/query/group-by.js.map +0 -1
- package/dist/esm/query/joins.d.ts +0 -14
- package/dist/esm/query/joins.js +0 -141
- package/dist/esm/query/joins.js.map +0 -1
- package/dist/esm/query/order-by.d.ts +0 -3
- package/dist/esm/query/order-by.js +0 -185
- package/dist/esm/query/order-by.js.map +0 -1
- package/dist/esm/query/pipeline-compiler.d.ts +0 -10
- package/dist/esm/query/pipeline-compiler.js +0 -89
- package/dist/esm/query/pipeline-compiler.js.map +0 -1
- package/dist/esm/query/query-builder.d.ts +0 -225
- package/dist/esm/query/query-builder.js +0 -307
- package/dist/esm/query/query-builder.js.map +0 -1
- package/dist/esm/query/schema.d.ts +0 -100
- package/dist/esm/query/select.d.ts +0 -3
- package/dist/esm/query/select.js +0 -130
- package/dist/esm/query/select.js.map +0 -1
- package/dist/esm/query/types.d.ts +0 -189
- package/dist/esm/query/utils.d.ts +0 -37
- package/dist/esm/query/utils.js +0 -154
- package/dist/esm/query/utils.js.map +0 -1
- package/dist/esm/utils.d.ts +0 -3
- package/dist/esm/utils.js +0 -17
- package/dist/esm/utils.js.map +0 -1
- package/src/query/compiled-query.ts +0 -234
- package/src/query/evaluators.ts +0 -250
- package/src/query/extractors.ts +0 -214
- package/src/query/functions.ts +0 -297
- package/src/query/group-by.ts +0 -139
- package/src/query/joins.ts +0 -260
- package/src/query/order-by.ts +0 -264
- package/src/query/pipeline-compiler.ts +0 -149
- package/src/query/query-builder.ts +0 -902
- package/src/query/schema.ts +0 -268
- package/src/query/select.ts +0 -208
- package/src/query/types.ts +0 -418
- package/src/query/utils.ts +0 -245
- package/src/utils.ts +0 -15
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ref-proxy.js","sources":["../../../../src/query/builder/ref-proxy.ts"],"sourcesContent":["import { PropRef, Value } from \"../ir.js\"\nimport type { BasicExpression } from \"../ir.js\"\n\nexport interface RefProxy<T = any> {\n /** @internal */\n readonly __refProxy: true\n /** @internal */\n readonly __path: Array<string>\n /** @internal */\n readonly __type: T\n}\n\n/**\n * Creates a proxy object that records property access paths\n * Used in callbacks like where, select, etc. to create type-safe references\n */\nexport function createRefProxy<T extends Record<string, any>>(\n aliases: Array<string>\n): RefProxy<T> & T {\n const cache = new Map<string, any>()\n const spreadSentinels = new Set<string>() // Track which aliases have been spread\n\n function createProxy(path: Array<string>): any {\n const pathKey = path.join(`.`)\n if (cache.has(pathKey)) {\n return cache.get(pathKey)\n }\n\n const proxy = new Proxy({} as any, {\n get(target, prop, receiver) {\n if (prop === `__refProxy`) return true\n if (prop === `__path`) return path\n if (prop === `__type`) return undefined // Type is only for TypeScript inference\n if (typeof prop === `symbol`) return Reflect.get(target, prop, receiver)\n\n const newPath = [...path, String(prop)]\n return createProxy(newPath)\n },\n\n has(target, prop) {\n if (prop === `__refProxy` || prop === `__path` || prop === `__type`)\n return true\n return Reflect.has(target, prop)\n },\n\n ownKeys(target) {\n // If this is a table-level proxy (path length 1), mark it as spread\n if (path.length === 1) {\n const aliasName = path[0]!\n spreadSentinels.add(aliasName)\n }\n return Reflect.ownKeys(target)\n },\n\n getOwnPropertyDescriptor(target, prop) {\n if (prop === `__refProxy` || prop === `__path` || prop === `__type`) {\n return { enumerable: false, configurable: true }\n }\n return Reflect.getOwnPropertyDescriptor(target, prop)\n },\n })\n\n cache.set(pathKey, proxy)\n return proxy\n }\n\n // Create the root proxy with all aliases as top-level properties\n const rootProxy = new Proxy({} as any, {\n get(target, prop, receiver) {\n if (prop === `__refProxy`) return true\n if (prop === `__path`) return []\n if (prop === `__type`) return undefined // Type is only for TypeScript inference\n if (prop === `__spreadSentinels`) return spreadSentinels // Expose spread sentinels\n if (typeof prop === `symbol`) return Reflect.get(target, prop, receiver)\n\n const propStr = String(prop)\n if (aliases.includes(propStr)) {\n return createProxy([propStr])\n }\n\n return undefined\n },\n\n has(target, prop) {\n if (\n prop === `__refProxy` ||\n prop === `__path` ||\n prop === `__type` ||\n prop === `__spreadSentinels`\n )\n return true\n if (typeof prop === `string` && aliases.includes(prop)) return true\n return Reflect.has(target, prop)\n },\n\n ownKeys(_target) {\n return [...aliases, `__refProxy`, `__path`, `__type`, `__spreadSentinels`]\n },\n\n getOwnPropertyDescriptor(target, prop) {\n if (\n prop === `__refProxy` ||\n prop === `__path` ||\n prop === `__type` ||\n prop === `__spreadSentinels`\n ) {\n return { enumerable: false, configurable: true }\n }\n if (typeof prop === `string` && aliases.includes(prop)) {\n return { enumerable: true, configurable: true }\n }\n return undefined\n },\n })\n\n return rootProxy\n}\n\n/**\n * Converts a value to an Expression\n * If it's a RefProxy, creates a Ref, otherwise creates a Value\n */\nexport function toExpression<T = any>(value: T): BasicExpression<T>\nexport function toExpression(value: RefProxy<any>): BasicExpression<any>\nexport function toExpression(value: any): BasicExpression<any> {\n if (isRefProxy(value)) {\n return new PropRef(value.__path)\n }\n // If it's already an Expression (Func, Ref, Value) or Agg, return it directly\n if (\n value &&\n typeof value === `object` &&\n `type` in value &&\n (value.type === `func` ||\n value.type === `ref` ||\n value.type === `val` ||\n value.type === `agg`)\n ) {\n return value\n }\n return new Value(value)\n}\n\n/**\n * Type guard to check if a value is a RefProxy\n */\nexport function isRefProxy(value: any): value is RefProxy {\n return value && typeof value === `object` && value.__refProxy === true\n}\n\n/**\n * Helper to create a Value expression from a literal\n */\nexport function val<T>(value: T): BasicExpression<T> {\n return new Value(value)\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { CollectionImpl } from '../../collection.js';
|
|
2
|
+
import { Aggregate, BasicExpression } from '../ir.js';
|
|
3
|
+
import { QueryBuilder } from './index.js';
|
|
4
|
+
export interface Context {
|
|
5
|
+
baseSchema: ContextSchema;
|
|
6
|
+
schema: ContextSchema;
|
|
7
|
+
fromSourceName: string;
|
|
8
|
+
hasJoins?: boolean;
|
|
9
|
+
joinTypes?: Record<string, `inner` | `left` | `right` | `full` | `outer` | `cross`>;
|
|
10
|
+
result?: any;
|
|
11
|
+
}
|
|
12
|
+
export type ContextSchema = Record<string, unknown>;
|
|
13
|
+
export type Source = {
|
|
14
|
+
[alias: string]: CollectionImpl<any, any> | QueryBuilder<Context>;
|
|
15
|
+
};
|
|
16
|
+
export type InferCollectionType<T> = T extends CollectionImpl<infer U> ? U : never;
|
|
17
|
+
export type SchemaFromSource<T extends Source> = Prettify<{
|
|
18
|
+
[K in keyof T]: T[K] extends CollectionImpl<infer U> ? U : T[K] extends QueryBuilder<infer TContext> ? GetResult<TContext> : never;
|
|
19
|
+
}>;
|
|
20
|
+
export type GetAliases<TContext extends Context> = keyof TContext[`schema`];
|
|
21
|
+
export type WhereCallback<TContext extends Context> = (refs: RefProxyForContext<TContext>) => any;
|
|
22
|
+
export type SelectObject<T extends Record<string, BasicExpression | Aggregate | RefProxy | RefProxyFor<any>> = Record<string, BasicExpression | Aggregate | RefProxy | RefProxyFor<any>>> = T;
|
|
23
|
+
export type ResultTypeFromSelect<TSelectObject> = {
|
|
24
|
+
[K in keyof TSelectObject]: TSelectObject[K] extends RefProxy<infer T> ? T : TSelectObject[K] extends BasicExpression<infer T> ? T : TSelectObject[K] extends Aggregate<infer T> ? T : TSelectObject[K] extends RefProxyFor<infer T> ? T : never;
|
|
25
|
+
};
|
|
26
|
+
export type OrderByCallback<TContext extends Context> = (refs: RefProxyForContext<TContext>) => any;
|
|
27
|
+
export type GroupByCallback<TContext extends Context> = (refs: RefProxyForContext<TContext>) => any;
|
|
28
|
+
export type JoinOnCallback<TContext extends Context> = (refs: RefProxyForContext<TContext>) => any;
|
|
29
|
+
export type RefProxyForContext<TContext extends Context> = {
|
|
30
|
+
[K in keyof TContext[`schema`]]: RefProxyFor<TContext[`schema`][K]>;
|
|
31
|
+
};
|
|
32
|
+
type IsExactlyUndefined<T> = [T] extends [undefined] ? true : false;
|
|
33
|
+
type IsOptional<T> = undefined extends T ? true : false;
|
|
34
|
+
type NonUndefined<T> = T extends undefined ? never : T;
|
|
35
|
+
export type RefProxyFor<T> = OmitRefProxy<IsExactlyUndefined<T> extends true ? RefProxy<T> : IsOptional<T> extends true ? NonUndefined<T> extends Record<string, any> ? {
|
|
36
|
+
[K in keyof NonUndefined<T>]: NonUndefined<T>[K] extends Record<string, any> ? RefProxyFor<NonUndefined<T>[K] | undefined> & RefProxy<NonUndefined<T>[K] | undefined> : RefProxy<NonUndefined<T>[K] | undefined>;
|
|
37
|
+
} & RefProxy<T> : RefProxy<T> : T extends Record<string, any> ? {
|
|
38
|
+
[K in keyof T]: T[K] extends Record<string, any> ? RefProxyFor<T[K]> & RefProxy<T[K]> : RefProxy<T[K]>;
|
|
39
|
+
} & RefProxy<T> : RefProxy<T>>;
|
|
40
|
+
export type Ref<T> = RefProxyFor<T>;
|
|
41
|
+
type OmitRefProxy<T> = Omit<T, `__refProxy` | `__path` | `__type`>;
|
|
42
|
+
export interface RefProxy<T = any> {
|
|
43
|
+
/** @internal */
|
|
44
|
+
readonly __refProxy: true;
|
|
45
|
+
/** @internal */
|
|
46
|
+
readonly __path: Array<string>;
|
|
47
|
+
/** @internal */
|
|
48
|
+
readonly __type: T;
|
|
49
|
+
}
|
|
50
|
+
export type MergeContextWithJoinType<TContext extends Context, TNewSchema extends ContextSchema, TJoinType extends `inner` | `left` | `right` | `full` | `outer` | `cross`> = {
|
|
51
|
+
baseSchema: TContext[`baseSchema`];
|
|
52
|
+
schema: ApplyJoinOptionalityToMergedSchema<TContext[`schema`], TNewSchema, TJoinType, TContext[`fromSourceName`]>;
|
|
53
|
+
fromSourceName: TContext[`fromSourceName`];
|
|
54
|
+
hasJoins: true;
|
|
55
|
+
joinTypes: (TContext[`joinTypes`] extends Record<string, any> ? TContext[`joinTypes`] : {}) & {
|
|
56
|
+
[K in keyof TNewSchema & string]: TJoinType;
|
|
57
|
+
};
|
|
58
|
+
result: TContext[`result`];
|
|
59
|
+
};
|
|
60
|
+
export type ApplyJoinOptionalityToMergedSchema<TExistingSchema extends ContextSchema, TNewSchema extends ContextSchema, TJoinType extends `inner` | `left` | `right` | `full` | `outer` | `cross`, TFromSourceName extends string> = {
|
|
61
|
+
[K in keyof TExistingSchema]: K extends TFromSourceName ? TJoinType extends `right` | `full` ? TExistingSchema[K] | undefined : TExistingSchema[K] : TExistingSchema[K];
|
|
62
|
+
} & {
|
|
63
|
+
[K in keyof TNewSchema]: TJoinType extends `left` | `full` ? // New table becomes optional for left and full joins
|
|
64
|
+
TNewSchema[K] | undefined : TNewSchema[K];
|
|
65
|
+
};
|
|
66
|
+
export type GetResult<TContext extends Context> = Prettify<TContext[`result`] extends object ? TContext[`result`] : TContext[`hasJoins`] extends true ? TContext[`schema`] : TContext[`schema`][TContext[`fromSourceName`]]>;
|
|
67
|
+
export type ApplyJoinOptionalityToSchema<TSchema extends ContextSchema, TJoinTypes extends Record<string, string>, TFromSourceName extends string> = {
|
|
68
|
+
[K in keyof TSchema]: K extends TFromSourceName ? HasJoinType<TJoinTypes, `right` | `full`> extends true ? TSchema[K] | undefined : TSchema[K] : K extends keyof TJoinTypes ? TJoinTypes[K] extends `left` | `full` ? TSchema[K] | undefined : IsTableMadeOptionalBySubsequentJoins<K, TJoinTypes, TFromSourceName> extends true ? TSchema[K] | undefined : TSchema[K] : TSchema[K];
|
|
69
|
+
};
|
|
70
|
+
type IsTableMadeOptionalBySubsequentJoins<TTableAlias extends string | number | symbol, TJoinTypes extends Record<string, string>, TFromSourceName extends string> = TTableAlias extends TFromSourceName ? HasJoinType<TJoinTypes, `right` | `full`> : false;
|
|
71
|
+
export type HasJoinType<TJoinTypes extends Record<string, string>, TTargetTypes extends string> = true extends {
|
|
72
|
+
[K in keyof TJoinTypes]: TJoinTypes[K] extends TTargetTypes ? true : false;
|
|
73
|
+
}[keyof TJoinTypes] ? true : false;
|
|
74
|
+
export type MergeContext<TContext extends Context, TNewSchema extends ContextSchema> = MergeContextWithJoinType<TContext, TNewSchema, `left`>;
|
|
75
|
+
export type WithResult<TContext extends Context, TResult> = Prettify<Omit<TContext, `result`> & {
|
|
76
|
+
result: Prettify<TResult>;
|
|
77
|
+
}>;
|
|
78
|
+
export type Prettify<T> = {
|
|
79
|
+
[K in keyof T]: T[K];
|
|
80
|
+
} & {};
|
|
81
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { BasicExpression } from '../ir.js';
|
|
2
|
+
import { NamespacedRow } from '../../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Compiled expression evaluator function type
|
|
5
|
+
*/
|
|
6
|
+
export type CompiledExpression = (namespacedRow: NamespacedRow) => any;
|
|
7
|
+
/**
|
|
8
|
+
* Compiles an expression into an optimized evaluator function.
|
|
9
|
+
* This eliminates branching during evaluation by pre-compiling the expression structure.
|
|
10
|
+
*/
|
|
11
|
+
export declare function compileExpression(expr: BasicExpression): CompiledExpression;
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
function compileExpression(expr) {
|
|
2
|
+
switch (expr.type) {
|
|
3
|
+
case `val`: {
|
|
4
|
+
const value = expr.value;
|
|
5
|
+
return () => value;
|
|
6
|
+
}
|
|
7
|
+
case `ref`: {
|
|
8
|
+
return compileRef(expr);
|
|
9
|
+
}
|
|
10
|
+
case `func`: {
|
|
11
|
+
return compileFunction(expr);
|
|
12
|
+
}
|
|
13
|
+
default:
|
|
14
|
+
throw new Error(`Unknown expression type: ${expr.type}`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function compileRef(ref) {
|
|
18
|
+
const [tableAlias, ...propertyPath] = ref.path;
|
|
19
|
+
if (!tableAlias) {
|
|
20
|
+
throw new Error(`Reference path cannot be empty`);
|
|
21
|
+
}
|
|
22
|
+
if (propertyPath.length === 0) {
|
|
23
|
+
return (namespacedRow) => namespacedRow[tableAlias];
|
|
24
|
+
} else if (propertyPath.length === 1) {
|
|
25
|
+
const prop = propertyPath[0];
|
|
26
|
+
return (namespacedRow) => {
|
|
27
|
+
const tableData = namespacedRow[tableAlias];
|
|
28
|
+
return tableData == null ? void 0 : tableData[prop];
|
|
29
|
+
};
|
|
30
|
+
} else {
|
|
31
|
+
return (namespacedRow) => {
|
|
32
|
+
const tableData = namespacedRow[tableAlias];
|
|
33
|
+
if (tableData === void 0) {
|
|
34
|
+
return void 0;
|
|
35
|
+
}
|
|
36
|
+
let value = tableData;
|
|
37
|
+
for (const prop of propertyPath) {
|
|
38
|
+
if (value == null) {
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
value = value[prop];
|
|
42
|
+
}
|
|
43
|
+
return value;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function compileFunction(func) {
|
|
48
|
+
const compiledArgs = func.args.map(compileExpression);
|
|
49
|
+
switch (func.name) {
|
|
50
|
+
// Comparison operators
|
|
51
|
+
case `eq`: {
|
|
52
|
+
const argA = compiledArgs[0];
|
|
53
|
+
const argB = compiledArgs[1];
|
|
54
|
+
return (namespacedRow) => {
|
|
55
|
+
const a = argA(namespacedRow);
|
|
56
|
+
const b = argB(namespacedRow);
|
|
57
|
+
return a === b;
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
case `gt`: {
|
|
61
|
+
const argA = compiledArgs[0];
|
|
62
|
+
const argB = compiledArgs[1];
|
|
63
|
+
return (namespacedRow) => {
|
|
64
|
+
const a = argA(namespacedRow);
|
|
65
|
+
const b = argB(namespacedRow);
|
|
66
|
+
return a > b;
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
case `gte`: {
|
|
70
|
+
const argA = compiledArgs[0];
|
|
71
|
+
const argB = compiledArgs[1];
|
|
72
|
+
return (namespacedRow) => {
|
|
73
|
+
const a = argA(namespacedRow);
|
|
74
|
+
const b = argB(namespacedRow);
|
|
75
|
+
return a >= b;
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
case `lt`: {
|
|
79
|
+
const argA = compiledArgs[0];
|
|
80
|
+
const argB = compiledArgs[1];
|
|
81
|
+
return (namespacedRow) => {
|
|
82
|
+
const a = argA(namespacedRow);
|
|
83
|
+
const b = argB(namespacedRow);
|
|
84
|
+
return a < b;
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
case `lte`: {
|
|
88
|
+
const argA = compiledArgs[0];
|
|
89
|
+
const argB = compiledArgs[1];
|
|
90
|
+
return (namespacedRow) => {
|
|
91
|
+
const a = argA(namespacedRow);
|
|
92
|
+
const b = argB(namespacedRow);
|
|
93
|
+
return a <= b;
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
// Boolean operators
|
|
97
|
+
case `and`:
|
|
98
|
+
return (namespacedRow) => {
|
|
99
|
+
for (const compiledArg of compiledArgs) {
|
|
100
|
+
if (!compiledArg(namespacedRow)) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return true;
|
|
105
|
+
};
|
|
106
|
+
case `or`:
|
|
107
|
+
return (namespacedRow) => {
|
|
108
|
+
for (const compiledArg of compiledArgs) {
|
|
109
|
+
if (compiledArg(namespacedRow)) {
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return false;
|
|
114
|
+
};
|
|
115
|
+
case `not`: {
|
|
116
|
+
const arg = compiledArgs[0];
|
|
117
|
+
return (namespacedRow) => !arg(namespacedRow);
|
|
118
|
+
}
|
|
119
|
+
// Array operators
|
|
120
|
+
case `in`: {
|
|
121
|
+
const valueEvaluator = compiledArgs[0];
|
|
122
|
+
const arrayEvaluator = compiledArgs[1];
|
|
123
|
+
return (namespacedRow) => {
|
|
124
|
+
const value = valueEvaluator(namespacedRow);
|
|
125
|
+
const array = arrayEvaluator(namespacedRow);
|
|
126
|
+
if (!Array.isArray(array)) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
return array.includes(value);
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
// String operators
|
|
133
|
+
case `like`: {
|
|
134
|
+
const valueEvaluator = compiledArgs[0];
|
|
135
|
+
const patternEvaluator = compiledArgs[1];
|
|
136
|
+
return (namespacedRow) => {
|
|
137
|
+
const value = valueEvaluator(namespacedRow);
|
|
138
|
+
const pattern = patternEvaluator(namespacedRow);
|
|
139
|
+
return evaluateLike(value, pattern, false);
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
case `ilike`: {
|
|
143
|
+
const valueEvaluator = compiledArgs[0];
|
|
144
|
+
const patternEvaluator = compiledArgs[1];
|
|
145
|
+
return (namespacedRow) => {
|
|
146
|
+
const value = valueEvaluator(namespacedRow);
|
|
147
|
+
const pattern = patternEvaluator(namespacedRow);
|
|
148
|
+
return evaluateLike(value, pattern, true);
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
// String functions
|
|
152
|
+
case `upper`: {
|
|
153
|
+
const arg = compiledArgs[0];
|
|
154
|
+
return (namespacedRow) => {
|
|
155
|
+
const value = arg(namespacedRow);
|
|
156
|
+
return typeof value === `string` ? value.toUpperCase() : value;
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
case `lower`: {
|
|
160
|
+
const arg = compiledArgs[0];
|
|
161
|
+
return (namespacedRow) => {
|
|
162
|
+
const value = arg(namespacedRow);
|
|
163
|
+
return typeof value === `string` ? value.toLowerCase() : value;
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
case `length`: {
|
|
167
|
+
const arg = compiledArgs[0];
|
|
168
|
+
return (namespacedRow) => {
|
|
169
|
+
const value = arg(namespacedRow);
|
|
170
|
+
if (typeof value === `string`) {
|
|
171
|
+
return value.length;
|
|
172
|
+
}
|
|
173
|
+
if (Array.isArray(value)) {
|
|
174
|
+
return value.length;
|
|
175
|
+
}
|
|
176
|
+
return 0;
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
case `concat`:
|
|
180
|
+
return (namespacedRow) => {
|
|
181
|
+
return compiledArgs.map((evaluator) => {
|
|
182
|
+
const arg = evaluator(namespacedRow);
|
|
183
|
+
try {
|
|
184
|
+
return String(arg ?? ``);
|
|
185
|
+
} catch {
|
|
186
|
+
try {
|
|
187
|
+
return JSON.stringify(arg) || ``;
|
|
188
|
+
} catch {
|
|
189
|
+
return `[object]`;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}).join(``);
|
|
193
|
+
};
|
|
194
|
+
case `coalesce`:
|
|
195
|
+
return (namespacedRow) => {
|
|
196
|
+
for (const evaluator of compiledArgs) {
|
|
197
|
+
const value = evaluator(namespacedRow);
|
|
198
|
+
if (value !== null && value !== void 0) {
|
|
199
|
+
return value;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return null;
|
|
203
|
+
};
|
|
204
|
+
// Math functions
|
|
205
|
+
case `add`: {
|
|
206
|
+
const argA = compiledArgs[0];
|
|
207
|
+
const argB = compiledArgs[1];
|
|
208
|
+
return (namespacedRow) => {
|
|
209
|
+
const a = argA(namespacedRow);
|
|
210
|
+
const b = argB(namespacedRow);
|
|
211
|
+
return (a ?? 0) + (b ?? 0);
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
case `subtract`: {
|
|
215
|
+
const argA = compiledArgs[0];
|
|
216
|
+
const argB = compiledArgs[1];
|
|
217
|
+
return (namespacedRow) => {
|
|
218
|
+
const a = argA(namespacedRow);
|
|
219
|
+
const b = argB(namespacedRow);
|
|
220
|
+
return (a ?? 0) - (b ?? 0);
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
case `multiply`: {
|
|
224
|
+
const argA = compiledArgs[0];
|
|
225
|
+
const argB = compiledArgs[1];
|
|
226
|
+
return (namespacedRow) => {
|
|
227
|
+
const a = argA(namespacedRow);
|
|
228
|
+
const b = argB(namespacedRow);
|
|
229
|
+
return (a ?? 0) * (b ?? 0);
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
case `divide`: {
|
|
233
|
+
const argA = compiledArgs[0];
|
|
234
|
+
const argB = compiledArgs[1];
|
|
235
|
+
return (namespacedRow) => {
|
|
236
|
+
const a = argA(namespacedRow);
|
|
237
|
+
const b = argB(namespacedRow);
|
|
238
|
+
const divisor = b ?? 0;
|
|
239
|
+
return divisor !== 0 ? (a ?? 0) / divisor : null;
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
default:
|
|
243
|
+
throw new Error(`Unknown function: ${func.name}`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
function evaluateLike(value, pattern, caseInsensitive) {
|
|
247
|
+
if (typeof value !== `string` || typeof pattern !== `string`) {
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
const searchValue = caseInsensitive ? value.toLowerCase() : value;
|
|
251
|
+
const searchPattern = caseInsensitive ? pattern.toLowerCase() : pattern;
|
|
252
|
+
let regexPattern = searchPattern.replace(/[.*+?^${}()|[\]\\]/g, `\\$&`);
|
|
253
|
+
regexPattern = regexPattern.replace(/%/g, `.*`);
|
|
254
|
+
regexPattern = regexPattern.replace(/_/g, `.`);
|
|
255
|
+
const regex = new RegExp(`^${regexPattern}$`);
|
|
256
|
+
return regex.test(searchValue);
|
|
257
|
+
}
|
|
258
|
+
export {
|
|
259
|
+
compileExpression
|
|
260
|
+
};
|
|
261
|
+
//# sourceMappingURL=evaluators.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evaluators.js","sources":["../../../../src/query/compiler/evaluators.ts"],"sourcesContent":["import type { BasicExpression, Func, PropRef } from \"../ir.js\"\nimport type { NamespacedRow } from \"../../types.js\"\n\n/**\n * Compiled expression evaluator function type\n */\nexport type CompiledExpression = (namespacedRow: NamespacedRow) => any\n\n/**\n * Compiles an expression into an optimized evaluator function.\n * This eliminates branching during evaluation by pre-compiling the expression structure.\n */\nexport function compileExpression(expr: BasicExpression): CompiledExpression {\n switch (expr.type) {\n case `val`: {\n // For constant values, return a function that just returns the value\n const value = expr.value\n return () => value\n }\n\n case `ref`: {\n // For references, pre-compile the property path navigation\n return compileRef(expr)\n }\n\n case `func`: {\n // For functions, pre-compile the function and its arguments\n return compileFunction(expr)\n }\n\n default:\n throw new Error(`Unknown expression type: ${(expr as any).type}`)\n }\n}\n\n/**\n * Compiles a reference expression into an optimized evaluator\n */\nfunction compileRef(ref: PropRef): CompiledExpression {\n const [tableAlias, ...propertyPath] = ref.path\n\n if (!tableAlias) {\n throw new Error(`Reference path cannot be empty`)\n }\n\n // Pre-compile the property path navigation\n if (propertyPath.length === 0) {\n // Simple table reference\n return (namespacedRow) => namespacedRow[tableAlias]\n } else if (propertyPath.length === 1) {\n // Single property access - most common case\n const prop = propertyPath[0]!\n return (namespacedRow) => {\n const tableData = namespacedRow[tableAlias]\n return tableData?.[prop]\n }\n } else {\n // Multiple property navigation\n return (namespacedRow) => {\n const tableData = namespacedRow[tableAlias]\n if (tableData === undefined) {\n return undefined\n }\n\n let value: any = tableData\n for (const prop of propertyPath) {\n if (value == null) {\n return value\n }\n value = value[prop]\n }\n return value\n }\n }\n}\n\n/**\n * Compiles a function expression into an optimized evaluator\n */\nfunction compileFunction(func: Func): CompiledExpression {\n // Pre-compile all arguments\n const compiledArgs = func.args.map(compileExpression)\n\n switch (func.name) {\n // Comparison operators\n case `eq`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (namespacedRow) => {\n const a = argA(namespacedRow)\n const b = argB(namespacedRow)\n return a === b\n }\n }\n case `gt`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (namespacedRow) => {\n const a = argA(namespacedRow)\n const b = argB(namespacedRow)\n return a > b\n }\n }\n case `gte`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (namespacedRow) => {\n const a = argA(namespacedRow)\n const b = argB(namespacedRow)\n return a >= b\n }\n }\n case `lt`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (namespacedRow) => {\n const a = argA(namespacedRow)\n const b = argB(namespacedRow)\n return a < b\n }\n }\n case `lte`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (namespacedRow) => {\n const a = argA(namespacedRow)\n const b = argB(namespacedRow)\n return a <= b\n }\n }\n\n // Boolean operators\n case `and`:\n return (namespacedRow) => {\n for (const compiledArg of compiledArgs) {\n if (!compiledArg(namespacedRow)) {\n return false\n }\n }\n return true\n }\n case `or`:\n return (namespacedRow) => {\n for (const compiledArg of compiledArgs) {\n if (compiledArg(namespacedRow)) {\n return true\n }\n }\n return false\n }\n case `not`: {\n const arg = compiledArgs[0]!\n return (namespacedRow) => !arg(namespacedRow)\n }\n\n // Array operators\n case `in`: {\n const valueEvaluator = compiledArgs[0]!\n const arrayEvaluator = compiledArgs[1]!\n return (namespacedRow) => {\n const value = valueEvaluator(namespacedRow)\n const array = arrayEvaluator(namespacedRow)\n if (!Array.isArray(array)) {\n return false\n }\n return array.includes(value)\n }\n }\n\n // String operators\n case `like`: {\n const valueEvaluator = compiledArgs[0]!\n const patternEvaluator = compiledArgs[1]!\n return (namespacedRow) => {\n const value = valueEvaluator(namespacedRow)\n const pattern = patternEvaluator(namespacedRow)\n return evaluateLike(value, pattern, false)\n }\n }\n case `ilike`: {\n const valueEvaluator = compiledArgs[0]!\n const patternEvaluator = compiledArgs[1]!\n return (namespacedRow) => {\n const value = valueEvaluator(namespacedRow)\n const pattern = patternEvaluator(namespacedRow)\n return evaluateLike(value, pattern, true)\n }\n }\n\n // String functions\n case `upper`: {\n const arg = compiledArgs[0]!\n return (namespacedRow) => {\n const value = arg(namespacedRow)\n return typeof value === `string` ? value.toUpperCase() : value\n }\n }\n case `lower`: {\n const arg = compiledArgs[0]!\n return (namespacedRow) => {\n const value = arg(namespacedRow)\n return typeof value === `string` ? value.toLowerCase() : value\n }\n }\n case `length`: {\n const arg = compiledArgs[0]!\n return (namespacedRow) => {\n const value = arg(namespacedRow)\n if (typeof value === `string`) {\n return value.length\n }\n if (Array.isArray(value)) {\n return value.length\n }\n return 0\n }\n }\n case `concat`:\n return (namespacedRow) => {\n return compiledArgs\n .map((evaluator) => {\n const arg = evaluator(namespacedRow)\n try {\n return String(arg ?? ``)\n } catch {\n try {\n return JSON.stringify(arg) || ``\n } catch {\n return `[object]`\n }\n }\n })\n .join(``)\n }\n case `coalesce`:\n return (namespacedRow) => {\n for (const evaluator of compiledArgs) {\n const value = evaluator(namespacedRow)\n if (value !== null && value !== undefined) {\n return value\n }\n }\n return null\n }\n\n // Math functions\n case `add`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (namespacedRow) => {\n const a = argA(namespacedRow)\n const b = argB(namespacedRow)\n return (a ?? 0) + (b ?? 0)\n }\n }\n case `subtract`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (namespacedRow) => {\n const a = argA(namespacedRow)\n const b = argB(namespacedRow)\n return (a ?? 0) - (b ?? 0)\n }\n }\n case `multiply`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (namespacedRow) => {\n const a = argA(namespacedRow)\n const b = argB(namespacedRow)\n return (a ?? 0) * (b ?? 0)\n }\n }\n case `divide`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (namespacedRow) => {\n const a = argA(namespacedRow)\n const b = argB(namespacedRow)\n const divisor = b ?? 0\n return divisor !== 0 ? (a ?? 0) / divisor : null\n }\n }\n\n default:\n throw new Error(`Unknown function: ${func.name}`)\n }\n}\n\n/**\n * Evaluates LIKE/ILIKE patterns\n */\nfunction evaluateLike(\n value: any,\n pattern: any,\n caseInsensitive: boolean\n): boolean {\n if (typeof value !== `string` || typeof pattern !== `string`) {\n return false\n }\n\n const searchValue = caseInsensitive ? value.toLowerCase() : value\n const searchPattern = caseInsensitive ? pattern.toLowerCase() : pattern\n\n // Convert SQL LIKE pattern to regex\n // First escape all regex special chars except % and _\n let regexPattern = searchPattern.replace(/[.*+?^${}()|[\\]\\\\]/g, `\\\\$&`)\n\n // Then convert SQL wildcards to regex\n regexPattern = regexPattern.replace(/%/g, `.*`) // % matches any sequence\n regexPattern = regexPattern.replace(/_/g, `.`) // _ matches any single char\n\n const regex = new RegExp(`^${regexPattern}$`)\n return regex.test(searchValue)\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { GroupBy, Having, Select } from '../ir.js';
|
|
2
|
+
import { NamespacedAndKeyedStream } from '../../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Processes the GROUP BY clause with optional HAVING and SELECT
|
|
5
|
+
* Works with the new __select_results structure from early SELECT processing
|
|
6
|
+
*/
|
|
7
|
+
export declare function processGroupBy(pipeline: NamespacedAndKeyedStream, groupByClause: GroupBy, havingClauses?: Array<Having>, selectClause?: Select, fnHavingClauses?: Array<(row: any) => any>): NamespacedAndKeyedStream;
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import { groupBy, map, filter, groupByOperators } from "@electric-sql/d2mini";
|
|
2
|
+
import { PropRef, Func } from "../ir.js";
|
|
3
|
+
import { compileExpression } from "./evaluators.js";
|
|
4
|
+
const { sum, count, avg, min, max } = groupByOperators;
|
|
5
|
+
function validateAndCreateMapping(groupByClause, selectClause) {
|
|
6
|
+
const selectToGroupByIndex = /* @__PURE__ */ new Map();
|
|
7
|
+
const groupByExpressions = [...groupByClause];
|
|
8
|
+
if (!selectClause) {
|
|
9
|
+
return { selectToGroupByIndex, groupByExpressions };
|
|
10
|
+
}
|
|
11
|
+
for (const [alias, expr] of Object.entries(selectClause)) {
|
|
12
|
+
if (expr.type === `agg`) {
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
const groupIndex = groupByExpressions.findIndex(
|
|
16
|
+
(groupExpr) => expressionsEqual(expr, groupExpr)
|
|
17
|
+
);
|
|
18
|
+
if (groupIndex === -1) {
|
|
19
|
+
throw new Error(
|
|
20
|
+
`Non-aggregate expression '${alias}' in SELECT must also appear in GROUP BY clause`
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
selectToGroupByIndex.set(alias, groupIndex);
|
|
24
|
+
}
|
|
25
|
+
return { selectToGroupByIndex, groupByExpressions };
|
|
26
|
+
}
|
|
27
|
+
function processGroupBy(pipeline, groupByClause, havingClauses, selectClause, fnHavingClauses) {
|
|
28
|
+
if (groupByClause.length === 0) {
|
|
29
|
+
const aggregates2 = {};
|
|
30
|
+
if (selectClause) {
|
|
31
|
+
for (const [alias, expr] of Object.entries(selectClause)) {
|
|
32
|
+
if (expr.type === `agg`) {
|
|
33
|
+
const aggExpr = expr;
|
|
34
|
+
aggregates2[alias] = getAggregateFunction(aggExpr);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const keyExtractor2 = () => ({ __singleGroup: true });
|
|
39
|
+
pipeline = pipeline.pipe(
|
|
40
|
+
groupBy(keyExtractor2, aggregates2)
|
|
41
|
+
);
|
|
42
|
+
pipeline = pipeline.pipe(
|
|
43
|
+
map(([, aggregatedRow]) => {
|
|
44
|
+
const selectResults = aggregatedRow.__select_results || {};
|
|
45
|
+
const finalResults = { ...selectResults };
|
|
46
|
+
if (selectClause) {
|
|
47
|
+
for (const [alias, expr] of Object.entries(selectClause)) {
|
|
48
|
+
if (expr.type === `agg`) {
|
|
49
|
+
finalResults[alias] = aggregatedRow[alias];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return [
|
|
54
|
+
`single_group`,
|
|
55
|
+
{
|
|
56
|
+
...aggregatedRow,
|
|
57
|
+
__select_results: finalResults
|
|
58
|
+
}
|
|
59
|
+
];
|
|
60
|
+
})
|
|
61
|
+
);
|
|
62
|
+
if (havingClauses && havingClauses.length > 0) {
|
|
63
|
+
for (const havingClause of havingClauses) {
|
|
64
|
+
const transformedHavingClause = transformHavingClause(
|
|
65
|
+
havingClause,
|
|
66
|
+
selectClause || {}
|
|
67
|
+
);
|
|
68
|
+
const compiledHaving = compileExpression(transformedHavingClause);
|
|
69
|
+
pipeline = pipeline.pipe(
|
|
70
|
+
filter(([, row]) => {
|
|
71
|
+
const namespacedRow = { result: row.__select_results };
|
|
72
|
+
return compiledHaving(namespacedRow);
|
|
73
|
+
})
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (fnHavingClauses && fnHavingClauses.length > 0) {
|
|
78
|
+
for (const fnHaving of fnHavingClauses) {
|
|
79
|
+
pipeline = pipeline.pipe(
|
|
80
|
+
filter(([, row]) => {
|
|
81
|
+
const namespacedRow = { result: row.__select_results };
|
|
82
|
+
return fnHaving(namespacedRow);
|
|
83
|
+
})
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return pipeline;
|
|
88
|
+
}
|
|
89
|
+
const mapping = validateAndCreateMapping(groupByClause, selectClause);
|
|
90
|
+
const compiledGroupByExpressions = groupByClause.map(compileExpression);
|
|
91
|
+
const keyExtractor = ([, row]) => {
|
|
92
|
+
const namespacedRow = { ...row };
|
|
93
|
+
delete namespacedRow.__select_results;
|
|
94
|
+
const key = {};
|
|
95
|
+
for (let i = 0; i < groupByClause.length; i++) {
|
|
96
|
+
const compiledExpr = compiledGroupByExpressions[i];
|
|
97
|
+
const value = compiledExpr(namespacedRow);
|
|
98
|
+
key[`__key_${i}`] = value;
|
|
99
|
+
}
|
|
100
|
+
return key;
|
|
101
|
+
};
|
|
102
|
+
const aggregates = {};
|
|
103
|
+
if (selectClause) {
|
|
104
|
+
for (const [alias, expr] of Object.entries(selectClause)) {
|
|
105
|
+
if (expr.type === `agg`) {
|
|
106
|
+
const aggExpr = expr;
|
|
107
|
+
aggregates[alias] = getAggregateFunction(aggExpr);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
pipeline = pipeline.pipe(groupBy(keyExtractor, aggregates));
|
|
112
|
+
pipeline = pipeline.pipe(
|
|
113
|
+
map(([, aggregatedRow]) => {
|
|
114
|
+
const selectResults = aggregatedRow.__select_results || {};
|
|
115
|
+
const finalResults = {};
|
|
116
|
+
if (selectClause) {
|
|
117
|
+
for (const [alias, expr] of Object.entries(selectClause)) {
|
|
118
|
+
if (expr.type !== `agg`) {
|
|
119
|
+
const groupIndex = mapping.selectToGroupByIndex.get(alias);
|
|
120
|
+
if (groupIndex !== void 0) {
|
|
121
|
+
finalResults[alias] = aggregatedRow[`__key_${groupIndex}`];
|
|
122
|
+
} else {
|
|
123
|
+
finalResults[alias] = selectResults[alias];
|
|
124
|
+
}
|
|
125
|
+
} else {
|
|
126
|
+
finalResults[alias] = aggregatedRow[alias];
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
} else {
|
|
130
|
+
for (let i = 0; i < groupByClause.length; i++) {
|
|
131
|
+
finalResults[`__key_${i}`] = aggregatedRow[`__key_${i}`];
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
let finalKey;
|
|
135
|
+
if (groupByClause.length === 1) {
|
|
136
|
+
finalKey = aggregatedRow[`__key_0`];
|
|
137
|
+
} else {
|
|
138
|
+
const keyParts = [];
|
|
139
|
+
for (let i = 0; i < groupByClause.length; i++) {
|
|
140
|
+
keyParts.push(aggregatedRow[`__key_${i}`]);
|
|
141
|
+
}
|
|
142
|
+
finalKey = JSON.stringify(keyParts);
|
|
143
|
+
}
|
|
144
|
+
return [
|
|
145
|
+
finalKey,
|
|
146
|
+
{
|
|
147
|
+
...aggregatedRow,
|
|
148
|
+
__select_results: finalResults
|
|
149
|
+
}
|
|
150
|
+
];
|
|
151
|
+
})
|
|
152
|
+
);
|
|
153
|
+
if (havingClauses && havingClauses.length > 0) {
|
|
154
|
+
for (const havingClause of havingClauses) {
|
|
155
|
+
const transformedHavingClause = transformHavingClause(
|
|
156
|
+
havingClause,
|
|
157
|
+
selectClause || {}
|
|
158
|
+
);
|
|
159
|
+
const compiledHaving = compileExpression(transformedHavingClause);
|
|
160
|
+
pipeline = pipeline.pipe(
|
|
161
|
+
filter(([, row]) => {
|
|
162
|
+
const namespacedRow = { result: row.__select_results };
|
|
163
|
+
return compiledHaving(namespacedRow);
|
|
164
|
+
})
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (fnHavingClauses && fnHavingClauses.length > 0) {
|
|
169
|
+
for (const fnHaving of fnHavingClauses) {
|
|
170
|
+
pipeline = pipeline.pipe(
|
|
171
|
+
filter(([, row]) => {
|
|
172
|
+
const namespacedRow = { result: row.__select_results };
|
|
173
|
+
return fnHaving(namespacedRow);
|
|
174
|
+
})
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return pipeline;
|
|
179
|
+
}
|
|
180
|
+
function expressionsEqual(expr1, expr2) {
|
|
181
|
+
var _a, _b, _c, _d;
|
|
182
|
+
if (!expr1 || !expr2) return false;
|
|
183
|
+
if (expr1.type !== expr2.type) return false;
|
|
184
|
+
switch (expr1.type) {
|
|
185
|
+
case `ref`:
|
|
186
|
+
if (!expr1.path || !expr2.path) return false;
|
|
187
|
+
if (expr1.path.length !== expr2.path.length) return false;
|
|
188
|
+
return expr1.path.every(
|
|
189
|
+
(segment, i) => segment === expr2.path[i]
|
|
190
|
+
);
|
|
191
|
+
case `val`:
|
|
192
|
+
return expr1.value === expr2.value;
|
|
193
|
+
case `func`:
|
|
194
|
+
return expr1.name === expr2.name && ((_a = expr1.args) == null ? void 0 : _a.length) === ((_b = expr2.args) == null ? void 0 : _b.length) && (expr1.args || []).every(
|
|
195
|
+
(arg, i) => expressionsEqual(arg, expr2.args[i])
|
|
196
|
+
);
|
|
197
|
+
case `agg`:
|
|
198
|
+
return expr1.name === expr2.name && ((_c = expr1.args) == null ? void 0 : _c.length) === ((_d = expr2.args) == null ? void 0 : _d.length) && (expr1.args || []).every(
|
|
199
|
+
(arg, i) => expressionsEqual(arg, expr2.args[i])
|
|
200
|
+
);
|
|
201
|
+
default:
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
function getAggregateFunction(aggExpr) {
|
|
206
|
+
const compiledExpr = compileExpression(aggExpr.args[0]);
|
|
207
|
+
const valueExtractor = ([, namespacedRow]) => {
|
|
208
|
+
const value = compiledExpr(namespacedRow);
|
|
209
|
+
return typeof value === `number` ? value : value != null ? Number(value) : 0;
|
|
210
|
+
};
|
|
211
|
+
switch (aggExpr.name.toLowerCase()) {
|
|
212
|
+
case `sum`:
|
|
213
|
+
return sum(valueExtractor);
|
|
214
|
+
case `count`:
|
|
215
|
+
return count();
|
|
216
|
+
// count() doesn't need a value extractor
|
|
217
|
+
case `avg`:
|
|
218
|
+
return avg(valueExtractor);
|
|
219
|
+
case `min`:
|
|
220
|
+
return min(valueExtractor);
|
|
221
|
+
case `max`:
|
|
222
|
+
return max(valueExtractor);
|
|
223
|
+
default:
|
|
224
|
+
throw new Error(`Unsupported aggregate function: ${aggExpr.name}`);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
function transformHavingClause(havingExpr, selectClause) {
|
|
228
|
+
switch (havingExpr.type) {
|
|
229
|
+
case `agg`: {
|
|
230
|
+
const aggExpr = havingExpr;
|
|
231
|
+
for (const [alias, selectExpr] of Object.entries(selectClause)) {
|
|
232
|
+
if (selectExpr.type === `agg` && aggregatesEqual(aggExpr, selectExpr)) {
|
|
233
|
+
return new PropRef([`result`, alias]);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
throw new Error(
|
|
237
|
+
`Aggregate function in HAVING clause must also be in SELECT clause: ${aggExpr.name}`
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
case `func`: {
|
|
241
|
+
const funcExpr = havingExpr;
|
|
242
|
+
const transformedArgs = funcExpr.args.map(
|
|
243
|
+
(arg) => transformHavingClause(arg, selectClause)
|
|
244
|
+
);
|
|
245
|
+
return new Func(funcExpr.name, transformedArgs);
|
|
246
|
+
}
|
|
247
|
+
case `ref`: {
|
|
248
|
+
const refExpr = havingExpr;
|
|
249
|
+
if (refExpr.path.length === 1) {
|
|
250
|
+
const alias = refExpr.path[0];
|
|
251
|
+
if (selectClause[alias]) {
|
|
252
|
+
return new PropRef([`result`, alias]);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return havingExpr;
|
|
256
|
+
}
|
|
257
|
+
case `val`:
|
|
258
|
+
return havingExpr;
|
|
259
|
+
default:
|
|
260
|
+
throw new Error(
|
|
261
|
+
`Unknown expression type in HAVING clause: ${havingExpr.type}`
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
function aggregatesEqual(agg1, agg2) {
|
|
266
|
+
return agg1.name === agg2.name && agg1.args.length === agg2.args.length && agg1.args.every((arg, i) => expressionsEqual(arg, agg2.args[i]));
|
|
267
|
+
}
|
|
268
|
+
export {
|
|
269
|
+
processGroupBy
|
|
270
|
+
};
|
|
271
|
+
//# sourceMappingURL=group-by.js.map
|