UNPKG

@tanstack/db

Version:

A reactive client store for building super fast apps on sync

1 lines 6.81 kB
{"version":3,"file":"select.cjs","sources":["../../../../src/query/compiler/select.ts"],"sourcesContent":["import { map } from \"@tanstack/db-ivm\"\nimport { compileExpression } from \"./evaluators.js\"\nimport type { Aggregate, BasicExpression, Select } from \"../ir.js\"\nimport type {\n KeyedStream,\n NamespacedAndKeyedStream,\n NamespacedRow,\n} from \"../../types.js\"\n\n/**\n * Processes the SELECT clause and places results in __select_results\n * while preserving the original namespaced row for ORDER BY access\n */\nexport function processSelectToResults(\n pipeline: NamespacedAndKeyedStream,\n select: Select,\n _allInputs: Record<string, KeyedStream>\n): NamespacedAndKeyedStream {\n // Pre-compile all select expressions\n const compiledSelect: Array<{\n alias: string\n compiledExpression: (row: NamespacedRow) => any\n }> = []\n const spreadAliases: Array<string> = []\n\n for (const [alias, expression] of Object.entries(select)) {\n if (alias.startsWith(`__SPREAD_SENTINEL__`)) {\n // Extract the table alias from the sentinel key\n const tableAlias = alias.replace(`__SPREAD_SENTINEL__`, ``)\n spreadAliases.push(tableAlias)\n } else {\n if (isAggregateExpression(expression)) {\n // For aggregates, we'll store the expression info for GROUP BY processing\n // but still compile a placeholder that will be replaced later\n compiledSelect.push({\n alias,\n compiledExpression: () => null, // Placeholder - will be handled by GROUP BY\n })\n } else {\n compiledSelect.push({\n alias,\n compiledExpression: compileExpression(expression as BasicExpression),\n })\n }\n }\n }\n\n return pipeline.pipe(\n map(([key, namespacedRow]) => {\n const selectResults: Record<string, any> = {}\n\n // First pass: spread table data for any spread sentinels\n for (const tableAlias of spreadAliases) {\n const tableData = namespacedRow[tableAlias]\n if (tableData && typeof tableData === `object`) {\n // Spread the table data into the result, but don't overwrite explicit fields\n for (const [fieldName, fieldValue] of Object.entries(tableData)) {\n if (!(fieldName in selectResults)) {\n selectResults[fieldName] = fieldValue\n }\n }\n }\n }\n\n // Second pass: evaluate all compiled select expressions (non-aggregates)\n for (const { alias, compiledExpression } of compiledSelect) {\n selectResults[alias] = compiledExpression(namespacedRow)\n }\n\n // Return the namespaced row with __select_results added\n return [\n key,\n {\n ...namespacedRow,\n __select_results: selectResults,\n },\n ] as [\n string,\n typeof namespacedRow & { __select_results: typeof selectResults },\n ]\n })\n )\n}\n\n/**\n * Processes the SELECT clause (legacy function - kept for compatibility)\n */\nexport function processSelect(\n pipeline: NamespacedAndKeyedStream,\n select: Select,\n _allInputs: Record<string, KeyedStream>\n): KeyedStream {\n // Pre-compile all select expressions\n const compiledSelect: Array<{\n alias: string\n compiledExpression: (row: NamespacedRow) => any\n }> = []\n const spreadAliases: Array<string> = []\n\n for (const [alias, expression] of Object.entries(select)) {\n if (alias.startsWith(`__SPREAD_SENTINEL__`)) {\n // Extract the table alias from the sentinel key\n const tableAlias = alias.replace(`__SPREAD_SENTINEL__`, ``)\n spreadAliases.push(tableAlias)\n } else {\n if (isAggregateExpression(expression)) {\n // Aggregates should be handled by GROUP BY processing, not here\n throw new Error(\n `Aggregate expressions in SELECT clause should be handled by GROUP BY processing`\n )\n }\n compiledSelect.push({\n alias,\n compiledExpression: compileExpression(expression as BasicExpression),\n })\n }\n }\n\n return pipeline.pipe(\n map(([key, namespacedRow]) => {\n const result: Record<string, any> = {}\n\n // First pass: spread table data for any spread sentinels\n for (const tableAlias of spreadAliases) {\n const tableData = namespacedRow[tableAlias]\n if (tableData && typeof tableData === `object`) {\n // Spread the table data into the result, but don't overwrite explicit fields\n for (const [fieldName, fieldValue] of Object.entries(tableData)) {\n if (!(fieldName in result)) {\n result[fieldName] = fieldValue\n }\n }\n }\n }\n\n // Second pass: evaluate all compiled select expressions\n for (const { alias, compiledExpression } of compiledSelect) {\n result[alias] = compiledExpression(namespacedRow)\n }\n\n return [key, result] as [string, typeof result]\n })\n )\n}\n\n/**\n * Helper function to check if an expression is an aggregate\n */\nfunction isAggregateExpression(\n expr: BasicExpression | Aggregate\n): expr is Aggregate {\n return expr.type === `agg`\n}\n\n/**\n * Processes a single argument in a function context\n */\nexport function processArgument(\n arg: BasicExpression | Aggregate,\n namespacedRow: NamespacedRow\n): any {\n if (isAggregateExpression(arg)) {\n throw new Error(\n `Aggregate expressions are not supported in this context. Use GROUP BY clause for aggregates.`\n )\n }\n\n // Pre-compile the expression and evaluate immediately\n const compiledExpression = compileExpression(arg)\n const value = compiledExpression(namespacedRow)\n\n return value\n}\n"],"names":["compileExpression","map"],"mappings":";;;;AAaO,SAAS,uBACd,UACA,QACA,YAC0B;AAE1B,QAAM,iBAGD,CAAA;AACL,QAAM,gBAA+B,CAAA;AAErC,aAAW,CAAC,OAAO,UAAU,KAAK,OAAO,QAAQ,MAAM,GAAG;AACxD,QAAI,MAAM,WAAW,qBAAqB,GAAG;AAE3C,YAAM,aAAa,MAAM,QAAQ,uBAAuB,EAAE;AAC1D,oBAAc,KAAK,UAAU;AAAA,IAC/B,OAAO;AACL,UAAI,sBAAsB,UAAU,GAAG;AAGrC,uBAAe,KAAK;AAAA,UAClB;AAAA,UACA,oBAAoB,MAAM;AAAA;AAAA,QAAA,CAC3B;AAAA,MACH,OAAO;AACL,uBAAe,KAAK;AAAA,UAClB;AAAA,UACA,oBAAoBA,WAAAA,kBAAkB,UAA6B;AAAA,QAAA,CACpE;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,SAAO,SAAS;AAAA,IACdC,MAAAA,IAAI,CAAC,CAAC,KAAK,aAAa,MAAM;AAC5B,YAAM,gBAAqC,CAAA;AAG3C,iBAAW,cAAc,eAAe;AACtC,cAAM,YAAY,cAAc,UAAU;AAC1C,YAAI,aAAa,OAAO,cAAc,UAAU;AAE9C,qBAAW,CAAC,WAAW,UAAU,KAAK,OAAO,QAAQ,SAAS,GAAG;AAC/D,gBAAI,EAAE,aAAa,gBAAgB;AACjC,4BAAc,SAAS,IAAI;AAAA,YAC7B;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAGA,iBAAW,EAAE,OAAO,mBAAA,KAAwB,gBAAgB;AAC1D,sBAAc,KAAK,IAAI,mBAAmB,aAAa;AAAA,MACzD;AAGA,aAAO;AAAA,QACL;AAAA,QACA;AAAA,UACE,GAAG;AAAA,UACH,kBAAkB;AAAA,QAAA;AAAA,MACpB;AAAA,IAKJ,CAAC;AAAA,EAAA;AAEL;AAkEA,SAAS,sBACP,MACmB;AACnB,SAAO,KAAK,SAAS;AACvB;;"}