{"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 { Func, PropRef, getHavingExpression, isExpressionLike } from '../ir.js'\nimport {\n  AggregateFunctionNotInSelectError,\n  NonAggregateExpressionNotInGroupByError,\n  UnknownHavingExpressionTypeError,\n  UnsupportedAggregateFunctionError,\n} from '../../errors.js'\nimport { compileExpression, toBooleanPredicate } from './evaluators.js'\nimport type {\n  Aggregate,\n  BasicExpression,\n  GroupBy,\n  Having,\n  Select,\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__`\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)) {\n    if (alias === `$selected`) continue\n    const asRecord = value\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 BasicExpression | Aggregate,\n            aggCounter,\n          )\n          for (const [syntheticAlias, aggExpr] of Object.entries(extracted)) {\n            aggregates[syntheticAlias] = getAggregateFunction(aggExpr)\n          }\n          wrappedAggExprs[alias] = compileExpression(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 BasicExpression | Aggregate,\n          aggCounter,\n        )\n        for (const [syntheticAlias, aggExpr] of Object.entries(extracted)) {\n          aggregates[syntheticAlias] = getAggregateFunction(aggExpr)\n        }\n        wrappedAggExprs[alias] = compileExpression(transformed)\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        )\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): void {\n  for (const key of Object.keys(aggregatedRow)) {\n    if (key.startsWith(`__agg_`)) {\n      finalResults[key] = aggregatedRow[key]\n    }\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_`)) delete finalResults[key]\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 (!isExpressionLike(expr)) {\n    return false\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: BasicExpression | Aggregate,\n  counter: { value: number },\n): {\n  transformed: BasicExpression\n  extracted: Record<string, Aggregate>\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\n    })\n    return {\n      transformed: new Func(expr.name, newArgs),\n      extracted: allExtracted,\n    }\n  }\n\n  // ref / val – pass through unchanged\n  return { transformed: expr as BasicExpression, extracted: {} }\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","compileExpression","keyExtractor","groupBy","map","serializeValue","getHavingExpression","filter","toBooleanPredicate","UnsupportedAggregateFunctionError","PropRef","AggregateFunctionNotInSelectError","Func","UnknownHavingExpressionTypeError","isExpressionLike"],"mappings":"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"}