diff --git a/.changeset/fn-select-groupby-error.md b/.changeset/fn-select-groupby-error.md new file mode 100644 index 000000000..cfc87883d --- /dev/null +++ b/.changeset/fn-select-groupby-error.md @@ -0,0 +1,5 @@ +--- +'@tanstack/db': patch +--- + +fix(db): throw error when fn.select() is used with groupBy() diff --git a/packages/db/src/errors.ts b/packages/db/src/errors.ts index dc1c7b900..f62278c56 100644 --- a/packages/db/src/errors.ts +++ b/packages/db/src/errors.ts @@ -433,6 +433,17 @@ export class DistinctRequiresSelectError extends QueryCompilationError { } } +export class FnSelectWithGroupByError extends QueryCompilationError { + constructor() { + super( + `fn.select() cannot be used with groupBy(). ` + + `groupBy requires the compiler to statically analyze aggregate functions (count, sum, max, etc.) in the SELECT clause, ` + + `which is not possible with fn.select() since it is an opaque function. ` + + `Use .select() instead of .fn.select() when combining with groupBy().`, + ) + } +} + export class HavingRequiresGroupByError extends QueryCompilationError { constructor() { super(`HAVING clause requires GROUP BY clause`) diff --git a/packages/db/src/query/compiler/index.ts b/packages/db/src/query/compiler/index.ts index d0d7469e2..885a7eaa6 100644 --- a/packages/db/src/query/compiler/index.ts +++ b/packages/db/src/query/compiler/index.ts @@ -4,6 +4,7 @@ import { CollectionInputNotFoundError, DistinctRequiresSelectError, DuplicateAliasInSubqueryError, + FnSelectWithGroupByError, HavingRequiresGroupByError, LimitOffsetRequireOrderByError, UnsupportedFromTypeError, @@ -218,6 +219,10 @@ export function compileQuery( throw new DistinctRequiresSelectError() } + if (query.fnSelect && query.groupBy && query.groupBy.length > 0) { + throw new FnSelectWithGroupByError() + } + // Process the SELECT clause early - always create $selected // This eliminates duplication and allows for DISTINCT implementation if (query.fnSelect) { diff --git a/packages/db/tests/query/group-by.test.ts b/packages/db/tests/query/group-by.test.ts index 0875ec141..3187736a0 100644 --- a/packages/db/tests/query/group-by.test.ts +++ b/packages/db/tests/query/group-by.test.ts @@ -2167,6 +2167,31 @@ function createGroupByTests(autoIndex: `off` | `eager`): void { expect(result?.totalAmount).toBe(700) }) }) + + describe(`fn.select with groupBy throws error`, () => { + let ordersCollection: ReturnType + + beforeEach(() => { + ordersCollection = createOrdersCollection(autoIndex) + }) + + test(`fn.select with groupBy should throw FnSelectWithGroupByError`, () => { + expect(() => + createLiveQueryCollection({ + startSync: true, + query: (q) => + q + .from({ orders: ordersCollection }) + .groupBy(({ orders }) => orders.customer_id) + .fn.select((row) => ({ + customerId: row.orders.customer_id, + totalAmount: sum(row.orders.amount), + orderCount: count(row.orders.id), + })), + }), + ).toThrow(`fn.select() cannot be used with groupBy()`) + }) + }) }) }