@tanstack/db
Version:
A reactive client store for building super fast apps on sync
1 lines • 46.5 kB
Source Map (JSON)
{"version":3,"file":"group-by.cjs","sources":["../../../../src/query/compiler/group-by.ts"],"sourcesContent":["import {\n filter,\n groupBy,\n groupByOperators,\n map,\n serializeValue,\n} from '@tanstack/db-ivm'\nimport {\n ConditionalSelect,\n Func,\n PropRef,\n getHavingExpression,\n isExpressionLike,\n} from '../ir.js'\nimport {\n AggregateFunctionNotInSelectError,\n NonAggregateExpressionNotInGroupByError,\n UnknownHavingExpressionTypeError,\n UnsupportedAggregateFunctionError,\n} from '../../errors.js'\nimport {\n compileExpression,\n isCaseWhenConditionTrue,\n toBooleanPredicate,\n} from './evaluators.js'\nimport type {\n Aggregate,\n BasicExpression,\n GroupBy,\n Having,\n Select,\n SelectValueExpression,\n} from '../ir.js'\nimport type { NamespacedAndKeyedStream, NamespacedRow } from '../../types.js'\nimport type { VirtualOrigin } from '../../virtual-props.js'\n\nconst VIRTUAL_SYNCED_KEY = `__virtual_synced__`\nconst VIRTUAL_HAS_LOCAL_KEY = `__virtual_has_local__`\nconst GROUP_KEY_REF_PREFIX = `__group_key_`\n\ntype RowVirtualMetadata = {\n synced: boolean\n hasLocal: boolean\n}\n\nfunction getRowVirtualMetadata(row: NamespacedRow): RowVirtualMetadata {\n let found = false\n let allSynced = true\n let hasLocal = false\n\n for (const [alias, value] of Object.entries(row as Record<string, unknown>)) {\n if (alias === `$selected`) continue\n if (value === null || typeof value !== `object`) continue\n const asRecord = value as Record<string, unknown>\n const hasSyncedProp = `$synced` in asRecord\n const hasOriginProp = `$origin` in asRecord\n if (!hasSyncedProp && !hasOriginProp) {\n continue\n }\n found = true\n if (asRecord.$synced === false) {\n allSynced = false\n }\n if (asRecord.$origin === `local`) {\n hasLocal = true\n }\n }\n\n return {\n synced: found ? allSynced : true,\n hasLocal,\n }\n}\n\nconst { sum, count, avg, min, max } = groupByOperators\n\n/**\n * Interface for caching the mapping between GROUP BY expressions and SELECT expressions\n */\ninterface GroupBySelectMapping {\n selectToGroupByIndex: Map<string, number> // Maps SELECT alias to GROUP BY expression index\n groupByExpressions: Array<any> // The GROUP BY expressions for reference\n}\n\n/**\n * Validates that all non-aggregate expressions in SELECT are present in GROUP BY\n * and creates a cached mapping for efficient lookup during processing\n */\nfunction validateAndCreateMapping(\n groupByClause: GroupBy,\n selectClause?: Select,\n): GroupBySelectMapping {\n const selectToGroupByIndex = new Map<string, number>()\n const groupByExpressions = [...groupByClause]\n\n if (!selectClause) {\n return { selectToGroupByIndex, groupByExpressions }\n }\n\n // Validate each SELECT expression\n for (const [alias, expr] of Object.entries(selectClause)) {\n if (expr.type === `agg` || containsAggregate(expr)) {\n // Aggregate expressions (plain or wrapped) are allowed and don't need to be in GROUP BY\n continue\n }\n\n // Non-aggregate expression must be in GROUP BY\n const groupIndex = groupByExpressions.findIndex((groupExpr) =>\n expressionsEqual(expr, groupExpr),\n )\n\n if (groupIndex === -1) {\n throw new NonAggregateExpressionNotInGroupByError(alias)\n }\n\n // Cache the mapping\n selectToGroupByIndex.set(alias, groupIndex)\n }\n\n return { selectToGroupByIndex, groupByExpressions }\n}\n\n/**\n * Processes the GROUP BY clause with optional HAVING and SELECT\n * Works with the new $selected structure from early SELECT processing\n */\nexport function processGroupBy(\n pipeline: NamespacedAndKeyedStream,\n groupByClause: GroupBy,\n havingClauses?: Array<Having>,\n selectClause?: Select,\n fnHavingClauses?: Array<(row: any) => any>,\n aggregateCollectionId?: string,\n mainSource?: string,\n): NamespacedAndKeyedStream {\n const virtualAggregates: Record<string, any> = {\n [VIRTUAL_SYNCED_KEY]: {\n preMap: ([, row]: [string, NamespacedRow]) =>\n getRowVirtualMetadata(row).synced,\n reduce: (values: Array<[boolean, number]>) => {\n for (const [isSynced, multiplicity] of values) {\n if (!isSynced && multiplicity > 0) {\n return false\n }\n }\n return true\n },\n },\n [VIRTUAL_HAS_LOCAL_KEY]: {\n preMap: ([, row]: [string, NamespacedRow]) =>\n getRowVirtualMetadata(row).hasLocal,\n reduce: (values: Array<[boolean, number]>) => {\n for (const [isLocal, multiplicity] of values) {\n if (isLocal && multiplicity > 0) {\n return true\n }\n }\n return false\n },\n },\n }\n\n // Handle empty GROUP BY (single-group aggregation)\n if (groupByClause.length === 0) {\n // For single-group aggregation, create a single group with all data\n const aggregates: Record<string, any> = virtualAggregates\n\n // Expressions that wrap aggregates (e.g. coalesce(count(...), 0)).\n // Keys are the original SELECT aliases; values are pre-compiled evaluators\n // over the transformed (aggregate-free) expression.\n const wrappedAggExprs: Record<string, (data: any) => any> = {}\n const aggCounter = { value: 0 }\n\n if (selectClause) {\n // Scan the SELECT clause for aggregate functions\n for (const [alias, expr] of Object.entries(selectClause)) {\n if (expr.type === `agg`) {\n aggregates[alias] = getAggregateFunction(expr)\n } else if (containsAggregate(expr)) {\n const { transformed, extracted } = extractAndReplaceAggregates(\n expr as SelectValueExpression,\n aggCounter,\n )\n for (const [syntheticAlias, aggExpr] of Object.entries(extracted)) {\n aggregates[syntheticAlias] = getAggregateFunction(aggExpr)\n }\n wrappedAggExprs[alias] = compileGroupedSelectValue(transformed)\n }\n }\n }\n\n // Use a constant key for single group.\n // When mainSource is set (includes mode), include __correlationKey so that\n // rows from different parents aggregate separately.\n const keyExtractor = mainSource\n ? ([, row]: [string, NamespacedRow]) => ({\n __singleGroup: true,\n __correlationKey: (row as any)?.[mainSource]?.__correlationKey,\n })\n : () => ({ __singleGroup: true })\n\n // Apply the groupBy operator with single group\n pipeline = pipeline.pipe(\n groupBy(keyExtractor, aggregates),\n ) as NamespacedAndKeyedStream\n\n // Update $selected to include aggregate values\n pipeline = pipeline.pipe(\n map(([, aggregatedRow]) => {\n // Start with the existing $selected from early SELECT processing\n const selectResults = (aggregatedRow as any).$selected || {}\n const finalResults: Record<string, any> = { ...selectResults }\n\n if (selectClause) {\n // First pass: populate plain aggregate results and synthetic aliases\n for (const [alias, expr] of Object.entries(selectClause)) {\n if (expr.type === `agg`) {\n finalResults[alias] = aggregatedRow[alias]\n }\n }\n evaluateWrappedAggregates(\n finalResults,\n aggregatedRow as Record<string, any>,\n wrappedAggExprs,\n )\n }\n\n // Use a single key for the result and update $selected.\n // When in includes mode, restore the namespaced source structure with\n // __correlationKey so output extraction can route results per-parent.\n const correlationKey = mainSource\n ? (aggregatedRow as any).__correlationKey\n : undefined\n const resultKey =\n correlationKey !== undefined\n ? `single_group_${serializeValue(correlationKey)}`\n : `single_group`\n const resultRow: Record<string, any> = {\n ...(aggregatedRow as Record<string, any>),\n $selected: finalResults,\n }\n const groupSynced = (aggregatedRow as Record<string, any>)[\n VIRTUAL_SYNCED_KEY\n ]\n const groupHasLocal = (aggregatedRow as Record<string, any>)[\n VIRTUAL_HAS_LOCAL_KEY\n ]\n resultRow.$synced = groupSynced ?? true\n resultRow.$origin = (\n groupHasLocal ? `local` : `remote`\n ) satisfies VirtualOrigin\n resultRow.$key = resultKey\n resultRow.$collectionId =\n aggregateCollectionId ?? resultRow.$collectionId\n if (mainSource && correlationKey !== undefined) {\n resultRow[mainSource] = { __correlationKey: correlationKey }\n }\n return [resultKey, resultRow] as [unknown, Record<string, any>]\n }),\n )\n\n // Apply HAVING clauses if present\n if (havingClauses && havingClauses.length > 0) {\n for (const havingClause of havingClauses) {\n const havingExpression = getHavingExpression(havingClause)\n const transformedHavingClause = replaceAggregatesByRefs(\n havingExpression,\n selectClause || {},\n `$selected`,\n )\n const compiledHaving = compileExpression(transformedHavingClause)\n\n pipeline = pipeline.pipe(\n filter(([, row]) => {\n // Create a namespaced row structure for HAVING evaluation\n const namespacedRow = { $selected: (row as any).$selected }\n return toBooleanPredicate(compiledHaving(namespacedRow))\n }),\n )\n }\n }\n\n // Apply functional HAVING clauses if present\n if (fnHavingClauses && fnHavingClauses.length > 0) {\n for (const fnHaving of fnHavingClauses) {\n pipeline = pipeline.pipe(\n filter(([, row]) => {\n // Create a namespaced row structure for functional HAVING evaluation\n const namespacedRow = { $selected: (row as any).$selected }\n return toBooleanPredicate(fnHaving(namespacedRow))\n }),\n )\n }\n }\n\n return pipeline\n }\n\n // Multi-group aggregation logic...\n // Validate and create mapping for non-aggregate expressions in SELECT\n const mapping = validateAndCreateMapping(groupByClause, selectClause)\n\n // Pre-compile groupBy expressions\n const compiledGroupByExpressions = groupByClause.map((e) =>\n compileExpression(e),\n )\n\n // Create a key extractor function using simple __key_X format.\n // When mainSource is set (includes mode), include __correlationKey so that\n // rows from different parents with the same group key aggregate separately.\n const keyExtractor = ([, row]: [\n string,\n NamespacedRow & { $selected?: any },\n ]) => {\n // Use the original namespaced row for GROUP BY expressions, not $selected\n const namespacedRow = { ...row }\n delete (namespacedRow as any).$selected\n\n const key: Record<string, unknown> = {}\n\n // Use simple __key_X format for each groupBy expression\n for (let i = 0; i < groupByClause.length; i++) {\n const compiledExpr = compiledGroupByExpressions[i]!\n const value = compiledExpr(namespacedRow)\n key[`__key_${i}`] = value\n }\n\n if (mainSource) {\n key.__correlationKey = (row as any)?.[mainSource]?.__correlationKey\n }\n\n return key\n }\n\n // Create aggregate functions for any aggregated columns in the SELECT clause\n const aggregates: Record<string, any> = virtualAggregates\n const wrappedAggExprs: Record<string, (data: any) => any> = {}\n const aggCounter = { value: 0 }\n\n if (selectClause) {\n // Scan the SELECT clause for aggregate functions\n for (const [alias, expr] of Object.entries(selectClause)) {\n if (expr.type === `agg`) {\n aggregates[alias] = getAggregateFunction(expr)\n } else if (containsAggregate(expr)) {\n const { transformed, extracted } = extractAndReplaceAggregates(\n expr as SelectValueExpression,\n aggCounter,\n )\n for (const [syntheticAlias, aggExpr] of Object.entries(extracted)) {\n aggregates[syntheticAlias] = getAggregateFunction(aggExpr)\n }\n wrappedAggExprs[alias] = compileGroupedSelectValue(\n replaceGroupByRefsInSelectValue(transformed, groupByClause),\n )\n }\n }\n }\n\n // Apply the groupBy operator\n pipeline = pipeline.pipe(groupBy(keyExtractor, aggregates))\n\n // Update $selected to handle GROUP BY results\n pipeline = pipeline.pipe(\n map(([, aggregatedRow]) => {\n // Start with the existing $selected from early SELECT processing\n const selectResults = (aggregatedRow as any).$selected || {}\n const finalResults: Record<string, any> = {}\n\n if (selectClause) {\n // First pass: populate group keys, plain aggregates, and synthetic aliases\n for (const [alias, expr] of Object.entries(selectClause)) {\n if (expr.type === `agg`) {\n finalResults[alias] = aggregatedRow[alias]\n } else if (!wrappedAggExprs[alias]) {\n // Use cached mapping to get the corresponding __key_X for non-aggregates\n const groupIndex = mapping.selectToGroupByIndex.get(alias)\n if (groupIndex !== undefined) {\n finalResults[alias] = aggregatedRow[`__key_${groupIndex}`]\n } else {\n // Fallback to original SELECT results\n finalResults[alias] = selectResults[alias]\n }\n }\n }\n evaluateWrappedAggregates(\n finalResults,\n aggregatedRow as Record<string, any>,\n wrappedAggExprs,\n groupByClause.length,\n )\n } else {\n // No SELECT clause - just use the group keys\n for (let i = 0; i < groupByClause.length; i++) {\n finalResults[`__key_${i}`] = aggregatedRow[`__key_${i}`]\n }\n }\n\n // Generate a simple key for the live collection using group values.\n // When in includes mode, include the correlation key so that groups\n // from different parents don't collide.\n const correlationKey = mainSource\n ? (aggregatedRow as any).__correlationKey\n : undefined\n const keyParts: Array<unknown> = []\n for (let i = 0; i < groupByClause.length; i++) {\n keyParts.push(aggregatedRow[`__key_${i}`])\n }\n if (correlationKey !== undefined) {\n keyParts.push(correlationKey)\n }\n const finalKey =\n keyParts.length === 1 ? keyParts[0] : serializeValue(keyParts)\n\n // When in includes mode, restore the namespaced source structure with\n // __correlationKey so output extraction can route results per-parent.\n const resultRow: Record<string, any> = {\n ...(aggregatedRow as Record<string, any>),\n $selected: finalResults,\n }\n const groupSynced = (aggregatedRow as Record<string, any>)[\n VIRTUAL_SYNCED_KEY\n ]\n const groupHasLocal = (aggregatedRow as Record<string, any>)[\n VIRTUAL_HAS_LOCAL_KEY\n ]\n resultRow.$synced = groupSynced ?? true\n resultRow.$origin = (\n groupHasLocal ? `local` : `remote`\n ) satisfies VirtualOrigin\n resultRow.$key = finalKey\n resultRow.$collectionId = aggregateCollectionId ?? resultRow.$collectionId\n if (mainSource && correlationKey !== undefined) {\n resultRow[mainSource] = { __correlationKey: correlationKey }\n }\n return [finalKey, resultRow] as [unknown, Record<string, any>]\n }),\n )\n\n // Apply HAVING clauses if present\n if (havingClauses && havingClauses.length > 0) {\n for (const havingClause of havingClauses) {\n const havingExpression = getHavingExpression(havingClause)\n const transformedHavingClause = replaceAggregatesByRefs(\n havingExpression,\n selectClause || {},\n )\n const compiledHaving = compileExpression(transformedHavingClause)\n\n pipeline = pipeline.pipe(\n filter(([, row]) => {\n // Create a namespaced row structure for HAVING evaluation\n const namespacedRow = { $selected: (row as any).$selected }\n return compiledHaving(namespacedRow)\n }),\n )\n }\n }\n\n // Apply functional HAVING clauses if present\n if (fnHavingClauses && fnHavingClauses.length > 0) {\n for (const fnHaving of fnHavingClauses) {\n pipeline = pipeline.pipe(\n filter(([, row]) => {\n // Create a namespaced row structure for functional HAVING evaluation\n const namespacedRow = { $selected: (row as any).$selected }\n return toBooleanPredicate(fnHaving(namespacedRow))\n }),\n )\n }\n }\n\n return pipeline\n}\n\n/**\n * Helper function to check if two expressions are equal\n */\nfunction expressionsEqual(expr1: any, expr2: any): boolean {\n if (!expr1 || !expr2) return false\n if (expr1.type !== expr2.type) return false\n\n switch (expr1.type) {\n case `ref`:\n // Compare paths as arrays\n if (!expr1.path || !expr2.path) return false\n if (expr1.path.length !== expr2.path.length) return false\n return expr1.path.every(\n (segment: string, i: number) => segment === expr2.path[i],\n )\n case `val`:\n return expr1.value === expr2.value\n case `func`:\n return (\n expr1.name === expr2.name &&\n expr1.args?.length === expr2.args?.length &&\n (expr1.args || []).every((arg: any, i: number) =>\n expressionsEqual(arg, expr2.args[i]),\n )\n )\n case `agg`:\n return (\n expr1.name === expr2.name &&\n expr1.args?.length === expr2.args?.length &&\n (expr1.args || []).every((arg: any, i: number) =>\n expressionsEqual(arg, expr2.args[i]),\n )\n )\n default:\n return false\n }\n}\n\n/**\n * Helper function to get an aggregate function based on the Agg expression\n */\nfunction getAggregateFunction(aggExpr: Aggregate) {\n // Pre-compile the value extractor expression\n const compiledExpr = compileExpression(aggExpr.args[0]!)\n\n // Create a value extractor function for the expression to aggregate\n const valueExtractor = ([, namespacedRow]: [string, NamespacedRow]) => {\n const value = compiledExpr(namespacedRow)\n // Ensure we return a number for numeric aggregate functions\n if (typeof value === `number`) {\n return value\n }\n return value != null ? Number(value) : 0\n }\n\n // Create a value extractor function for min/max that preserves comparable types\n const valueExtractorForMinMax = ([, namespacedRow]: [\n string,\n NamespacedRow,\n ]) => {\n const value = compiledExpr(namespacedRow)\n // Preserve strings, numbers, Dates, and bigints for comparison\n if (\n typeof value === `number` ||\n typeof value === `string` ||\n typeof value === `bigint` ||\n value instanceof Date\n ) {\n return value\n }\n return value != null ? Number(value) : 0\n }\n\n // Create a raw value extractor function for the expression to aggregate\n const rawValueExtractor = ([, namespacedRow]: [string, NamespacedRow]) => {\n return compiledExpr(namespacedRow)\n }\n\n // Return the appropriate aggregate function\n switch (aggExpr.name.toLowerCase()) {\n case `sum`:\n return sum(valueExtractor)\n case `count`:\n return count(rawValueExtractor)\n case `avg`:\n return avg(valueExtractor)\n case `min`:\n return min(valueExtractorForMinMax)\n case `max`:\n return max(valueExtractorForMinMax)\n default:\n throw new UnsupportedAggregateFunctionError(aggExpr.name)\n }\n}\n\n/**\n * Transforms expressions to replace aggregate functions with references to computed values.\n *\n * For aggregate expressions, finds matching aggregates in the SELECT clause and replaces them\n * with PropRef([resultAlias, alias]) to reference the computed aggregate value.\n *\n * Ref expressions (table columns and $selected fields) and value expressions are passed through unchanged.\n * Function expressions are recursively transformed.\n *\n * @param havingExpr - The expression to transform (can be aggregate, ref, func, or val)\n * @param selectClause - The SELECT clause containing aliases and aggregate definitions\n * @param resultAlias - The namespace alias for SELECT results (default: '$selected')\n * @returns A transformed BasicExpression that references computed values instead of raw expressions\n */\nexport function replaceAggregatesByRefs(\n havingExpr: BasicExpression | Aggregate,\n selectClause: Select,\n resultAlias: string = `$selected`,\n): BasicExpression {\n switch (havingExpr.type) {\n case `agg`: {\n const aggExpr = havingExpr\n // Find matching aggregate in SELECT clause\n for (const [alias, selectExpr] of Object.entries(selectClause)) {\n if (selectExpr.type === `agg` && aggregatesEqual(aggExpr, selectExpr)) {\n // Replace with a reference to the computed aggregate\n return new PropRef([resultAlias, alias])\n }\n }\n // If no matching aggregate found in SELECT, throw error\n throw new AggregateFunctionNotInSelectError(aggExpr.name)\n }\n\n case `func`: {\n const funcExpr = havingExpr\n // Transform function arguments recursively\n const transformedArgs = funcExpr.args.map(\n (arg: BasicExpression | Aggregate) =>\n replaceAggregatesByRefs(arg, selectClause),\n )\n return new Func(funcExpr.name, transformedArgs)\n }\n\n case `ref`:\n // Ref expressions are passed through unchanged - they reference either:\n // - $selected fields (which are already in the correct namespace)\n // - Table column references (which remain valid)\n return havingExpr as BasicExpression\n\n case `val`:\n // Return as-is\n return havingExpr as BasicExpression\n\n default:\n throw new UnknownHavingExpressionTypeError((havingExpr as any).type)\n }\n}\n\n/**\n * Evaluates wrapped-aggregate expressions against the aggregated row.\n * Copies synthetic __agg_N values into finalResults so the compiled wrapper\n * expressions can reference them, evaluates each wrapper, then removes the\n * synthetic keys so they don't leak onto user-visible result rows.\n */\nfunction evaluateWrappedAggregates(\n finalResults: Record<string, any>,\n aggregatedRow: Record<string, any>,\n wrappedAggExprs: Record<string, (data: any) => any>,\n groupKeyCount: number = 0,\n): void {\n for (const key of Object.keys(aggregatedRow)) {\n if (key.startsWith(`__agg_`)) {\n finalResults[key] = aggregatedRow[key]\n }\n }\n for (let i = 0; i < groupKeyCount; i++) {\n finalResults[`${GROUP_KEY_REF_PREFIX}${i}`] = aggregatedRow[`__key_${i}`]\n }\n for (const [alias, evaluator] of Object.entries(wrappedAggExprs)) {\n finalResults[alias] = evaluator({ $selected: finalResults })\n }\n for (const key of Object.keys(finalResults)) {\n if (key.startsWith(`__agg_`) || key.startsWith(GROUP_KEY_REF_PREFIX)) {\n delete finalResults[key]\n }\n }\n}\n\n/**\n * Checks whether an expression contains an aggregate anywhere in its tree.\n * Returns true for a top-level Aggregate, or a Func whose args (recursively)\n * contain an Aggregate. Safely returns false for nested Select objects.\n */\nexport function containsAggregate(\n expr: BasicExpression | Aggregate | Select | { type: string },\n): boolean {\n if (isConditionalSelect(expr)) {\n const branchHasAggregate = expr.branches.some(\n (branch) =>\n containsAggregate(branch.condition) || containsAggregate(branch.value),\n )\n\n return (\n branchHasAggregate ||\n (expr.defaultValue !== undefined && containsAggregate(expr.defaultValue))\n )\n }\n\n if (isNestedSelectObject(expr)) {\n return Object.values(expr).some((value) =>\n containsAggregate(value as BasicExpression | Aggregate | Select),\n )\n }\n\n if (!isExpressionLike(expr)) {\n return false\n }\n\n if (expr.type === `agg`) {\n return true\n }\n if (expr.type === `func` && `args` in expr) {\n return (expr.args as Array<BasicExpression | Aggregate>).some(\n (arg: BasicExpression | Aggregate) => containsAggregate(arg),\n )\n }\n return false\n}\n\n/**\n * Walks an expression tree containing nested aggregates.\n * Each Aggregate node is extracted, assigned a synthetic alias (__agg_N),\n * and replaced with PropRef([\"$selected\", \"__agg_N\"]) so the wrapper\n * expression can be compiled as a pure BasicExpression after groupBy\n * populates the synthetic values.\n */\nfunction extractAndReplaceAggregates(\n expr: SelectValueExpression,\n counter: { value: number },\n): {\n transformed: SelectValueExpression\n extracted: Record<string, Aggregate>\n} {\n if (expr.type === `includesSubquery`) {\n return { transformed: expr, extracted: {} }\n }\n\n if (expr.type === `agg`) {\n const alias = `__agg_${counter.value++}`\n return {\n transformed: new PropRef([`$selected`, alias]),\n extracted: { [alias]: expr },\n }\n }\n\n if (expr.type === `func`) {\n const allExtracted: Record<string, Aggregate> = {}\n const newArgs = expr.args.map((arg: BasicExpression | Aggregate) => {\n const result = extractAndReplaceAggregates(arg, counter)\n Object.assign(allExtracted, result.extracted)\n return result.transformed as BasicExpression\n })\n return {\n transformed: new Func(expr.name, newArgs),\n extracted: allExtracted,\n }\n }\n\n if (isConditionalSelect(expr)) {\n const allExtracted: Record<string, Aggregate> = {}\n const branches = expr.branches.map((branch) => {\n const condition = extractAndReplaceAggregates(branch.condition, counter)\n const value = extractAndReplaceAggregates(branch.value, counter)\n Object.assign(allExtracted, condition.extracted, value.extracted)\n return {\n condition: condition.transformed as BasicExpression,\n value: value.transformed,\n }\n })\n const defaultValue =\n expr.defaultValue === undefined\n ? undefined\n : extractAndReplaceAggregates(expr.defaultValue, counter)\n\n if (defaultValue) {\n Object.assign(allExtracted, defaultValue.extracted)\n }\n\n return {\n transformed: new ConditionalSelect(branches, defaultValue?.transformed),\n extracted: allExtracted,\n }\n }\n\n if (isNestedSelectObject(expr)) {\n const allExtracted: Record<string, Aggregate> = {}\n const transformed: Select = {}\n\n for (const [key, value] of Object.entries(expr)) {\n const result = extractAndReplaceAggregates(\n value as SelectValueExpression,\n counter,\n )\n Object.assign(allExtracted, result.extracted)\n transformed[key] = result.transformed\n }\n\n return { transformed, extracted: allExtracted }\n }\n\n // ref / val – pass through unchanged\n return { transformed: expr, extracted: {} }\n}\n\nfunction replaceGroupByRefsInSelectValue(\n value: SelectValueExpression,\n groupByClause: GroupBy,\n): SelectValueExpression {\n if (isConditionalSelect(value)) {\n return new ConditionalSelect(\n value.branches.map((branch) => ({\n condition: replaceGroupByRefsInExpression(\n branch.condition,\n groupByClause,\n ),\n value: replaceGroupByRefsInSelectValue(branch.value, groupByClause),\n })),\n value.defaultValue === undefined\n ? undefined\n : replaceGroupByRefsInSelectValue(value.defaultValue, groupByClause),\n )\n }\n\n if (isNestedSelectObject(value)) {\n const transformed: Select = {}\n for (const [key, entry] of Object.entries(value)) {\n transformed[key] = replaceGroupByRefsInSelectValue(\n entry as SelectValueExpression,\n groupByClause,\n )\n }\n return transformed\n }\n\n if (!isExpressionLike(value)) {\n return value\n }\n\n if (value.type === `includesSubquery` || value.type === `agg`) {\n return value\n }\n\n return replaceGroupByRefsInExpression(value, groupByClause)\n}\n\nfunction replaceGroupByRefsInExpression(\n expr: BasicExpression,\n groupByClause: GroupBy,\n): BasicExpression {\n if (expr.type === `ref`) {\n const groupIndex = groupByClause.findIndex((groupExpr) =>\n expressionsEqual(expr, groupExpr),\n )\n return groupIndex === -1\n ? expr\n : new PropRef([`$selected`, `${GROUP_KEY_REF_PREFIX}${groupIndex}`])\n }\n\n if (expr.type === `func`) {\n return new Func(\n expr.name,\n expr.args.map((arg) =>\n replaceGroupByRefsInExpression(arg, groupByClause),\n ),\n )\n }\n\n return expr\n}\n\nfunction compileGroupedSelectValue(\n value: SelectValueExpression,\n): (row: NamespacedRow) => any {\n if (isConditionalSelect(value)) {\n return compileGroupedConditionalSelect(value)\n }\n\n if (value.type === `includesSubquery`) {\n return () => null\n }\n\n if (isNestedSelectObject(value)) {\n return compileGroupedSelectObject(value)\n }\n\n if (!isExpressionLike(value)) {\n return () => value\n }\n\n return compileExpression(value as BasicExpression)\n}\n\nfunction compileGroupedSelectObject(\n obj: Select,\n): (row: NamespacedRow) => Record<string, any> {\n const entries = Object.entries(obj).map(([key, value]) => {\n if (key.startsWith(`__SPREAD_SENTINEL__`)) {\n const rest = key.slice(`__SPREAD_SENTINEL__`.length)\n const splitIndex = rest.lastIndexOf(`__`)\n const pathStr = splitIndex >= 0 ? rest.slice(0, splitIndex) : rest\n const isRefExpr =\n typeof value === `object` && `type` in value && value.type === `ref`\n const expression = isRefExpr\n ? (value as BasicExpression)\n : (new PropRef(pathStr.split(`.`)) as BasicExpression)\n\n return {\n key,\n spread: true,\n value: compileExpression(expression),\n }\n }\n\n return {\n key,\n spread: false,\n value: compileGroupedSelectValue(value as SelectValueExpression),\n }\n })\n\n return (row) => {\n const result: Record<string, any> = {}\n for (const entry of entries) {\n const value = entry.value(row)\n if (entry.spread) {\n if (value && typeof value === `object`) {\n Object.assign(result, value)\n }\n } else {\n result[entry.key] = value\n }\n }\n return result\n }\n}\n\nfunction compileGroupedConditionalSelect(\n conditional: ConditionalSelect,\n): (row: NamespacedRow) => any {\n const branches = conditional.branches.map((branch) => ({\n condition: compileExpression(branch.condition),\n value: compileGroupedSelectValue(branch.value),\n }))\n const defaultValue =\n conditional.defaultValue === undefined\n ? undefined\n : compileGroupedSelectValue(conditional.defaultValue)\n\n return (row) => {\n for (const branch of branches) {\n if (isCaseWhenConditionTrue(branch.condition(row))) {\n return branch.value(row)\n }\n }\n\n return defaultValue !== undefined ? defaultValue(row) : null\n }\n}\n\nfunction isNestedSelectObject(value: unknown): value is Select {\n return (\n value != null &&\n typeof value === `object` &&\n !Array.isArray(value) &&\n !(value as any).__refProxy &&\n !isExpressionLike(value)\n )\n}\n\nfunction isConditionalSelect(value: unknown): value is ConditionalSelect {\n return (\n value instanceof ConditionalSelect ||\n (value != null &&\n typeof value === `object` &&\n (value as { type?: string }).type === `conditionalSelect`)\n )\n}\n\n/**\n * Checks if two aggregate expressions are equal\n */\nfunction aggregatesEqual(agg1: Aggregate, agg2: Aggregate): boolean {\n return (\n agg1.name === agg2.name &&\n agg1.args.length === agg2.args.length &&\n agg1.args.every((arg, i) => expressionsEqual(arg, agg2.args[i]))\n )\n}\n"],"names":["groupByOperators","NonAggregateExpressionNotInGroupByError","aggregates","wrappedAggExprs","aggCounter","keyExtractor","groupBy","map","serializeValue","getHavingExpression","compileExpression","filter","toBooleanPredicate","UnsupportedAggregateFunctionError","PropRef","AggregateFunctionNotInSelectError","Func","UnknownHavingExpressionTypeError","isExpressionLike","ConditionalSelect","isCaseWhenConditionTrue"],"mappings":"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"}