Skip to content

Commit 0f7798b

Browse files
committed
rename stuff and tidy
1 parent 4ee665a commit 0f7798b

File tree

5 files changed

+99
-74
lines changed

5 files changed

+99
-74
lines changed

packages/db/src/errors.ts

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -409,32 +409,32 @@ export class UnsupportedJoinTypeError extends JoinError {
409409
}
410410
}
411411

412-
export class InvalidJoinConditionSameTableError extends JoinError {
413-
constructor(tableAlias: string) {
412+
export class InvalidJoinConditionSameSourceError extends JoinError {
413+
constructor(sourceAlias: string) {
414414
super(
415-
`Invalid join condition: both expressions refer to the same table "${tableAlias}"`
415+
`Invalid join condition: both expressions refer to the same source "${sourceAlias}"`
416416
)
417417
}
418418
}
419419

420-
export class InvalidJoinConditionTableMismatchError extends JoinError {
420+
export class InvalidJoinConditionSourceMismatchError extends JoinError {
421421
constructor() {
422-
super(`Invalid join condition: expressions must reference table aliases`)
422+
super(`Invalid join condition: expressions must reference source aliases`)
423423
}
424424
}
425425

426-
export class InvalidJoinConditionLeftTableError extends JoinError {
427-
constructor(tableAlias: string) {
426+
export class InvalidJoinConditionLeftSourceError extends JoinError {
427+
constructor(sourceAlias: string) {
428428
super(
429-
`Invalid join condition: left expression refers to an unavailable table "${tableAlias}"`
429+
`Invalid join condition: left expression refers to an unavailable source "${sourceAlias}"`
430430
)
431431
}
432432
}
433433

434-
export class InvalidJoinConditionRightTableError extends JoinError {
435-
constructor(tableAlias: string) {
434+
export class InvalidJoinConditionRightSourceError extends JoinError {
435+
constructor(sourceAlias: string) {
436436
super(
437-
`Invalid join condition: right expression does not refer to the joined table "${tableAlias}"`
437+
`Invalid join condition: right expression does not refer to the joined source "${sourceAlias}"`
438438
)
439439
}
440440
}
@@ -581,3 +581,24 @@ export class WhereClauseConversionError extends QueryOptimizerError {
581581
)
582582
}
583583
}
584+
585+
export class SubscriptionNotFoundError extends QueryCompilationError {
586+
constructor(
587+
resolvedAlias: string,
588+
originalAlias: string,
589+
collectionId: string,
590+
availableAliases: Array<string>
591+
) {
592+
super(
593+
`Internal error: subscription for alias '${resolvedAlias}' (remapped from '${originalAlias}', collection '${collectionId}') is missing in join pipeline. Available aliases: ${availableAliases.join(`, `)}. This indicates a bug in alias tracking.`
594+
)
595+
}
596+
}
597+
598+
export class AggregateNotSupportedError extends QueryCompilationError {
599+
constructor() {
600+
super(
601+
`Aggregate expressions are not supported in this context. Use GROUP BY clause for aggregates.`
602+
)
603+
}
604+
}

packages/db/src/query/compiler/joins.ts

Lines changed: 52 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@ import {
88
import {
99
CollectionInputNotFoundError,
1010
InvalidJoinCondition,
11-
InvalidJoinConditionLeftTableError,
12-
InvalidJoinConditionRightTableError,
13-
InvalidJoinConditionSameTableError,
14-
InvalidJoinConditionTableMismatchError,
11+
InvalidJoinConditionLeftSourceError,
12+
InvalidJoinConditionRightSourceError,
13+
InvalidJoinConditionSameSourceError,
14+
InvalidJoinConditionSourceMismatchError,
1515
JoinCollectionNotFoundError,
16+
SubscriptionNotFoundError,
1617
UnsupportedJoinSourceTypeError,
1718
UnsupportedJoinTypeError,
1819
} from "../../errors.js"
@@ -179,7 +180,7 @@ function processJoin(
179180
// Prepare the main pipeline for joining
180181
let mainPipeline = pipeline.pipe(
181182
map(([currentKey, namespacedRow]) => {
182-
// Extract the join key from the main table expression
183+
// Extract the join key from the main source expression
183184
const mainKey = compiledMainExpr(namespacedRow)
184185

185186
// Return [joinKey, [originalKey, namespacedRow]]
@@ -196,7 +197,7 @@ function processJoin(
196197
// Wrap the row in a namespaced structure
197198
const namespacedRow: NamespacedRow = { [joinedSource]: row }
198199

199-
// Extract the join key from the joined table expression
200+
// Extract the join key from the joined source expression
200201
const joinedKey = compiledJoinedExpr(namespacedRow)
201202

202203
// Return [joinKey, [originalKey, namespacedRow]]
@@ -269,8 +270,8 @@ function processJoin(
269270
> = activePipeline.pipe(
270271
tap((data) => {
271272
// For outer joins (LEFT/RIGHT), the driving side determines which alias's
272-
// subscription we consult for lazy loading. The main table drives LEFT joins,
273-
// joined table drives RIGHT joins.
273+
// subscription we consult for lazy loading. The main source drives LEFT joins,
274+
// joined source drives RIGHT joins.
274275
const lazyAliasCandidate =
275276
activeSource === `main` ? joinedSource : mainSource
276277

@@ -284,8 +285,11 @@ function processJoin(
284285
const lazySourceSubscription = subscriptions[resolvedAlias]
285286

286287
if (!lazySourceSubscription) {
287-
throw new Error(
288-
`Internal error: subscription for alias '${resolvedAlias}' (remapped from '${lazyAliasCandidate}', collection '${lazySource.id}') is missing in join pipeline. Available aliases: ${Object.keys(subscriptions).join(`, `)}. This indicates a bug in alias tracking.`
288+
throw new SubscriptionNotFoundError(
289+
resolvedAlias,
290+
lazyAliasCandidate,
291+
lazySource.id,
292+
Object.keys(subscriptions)
289293
)
290294
}
291295

@@ -324,90 +328,89 @@ function processJoin(
324328
}
325329

326330
/**
327-
* Analyzes join expressions to determine which refers to which table
328-
* and returns them in the correct order (available table expression first, joined table expression second)
331+
* Analyzes join expressions to determine which refers to which source
332+
* and returns them in the correct order (available source expression first, joined source expression second)
329333
*/
330334
function analyzeJoinExpressions(
331335
left: BasicExpression,
332336
right: BasicExpression,
333-
allAvailableTableAliases: Array<string>,
337+
allAvailableSourceAliases: Array<string>,
334338
joinedSource: string
335339
): { mainExpr: BasicExpression; joinedExpr: BasicExpression } {
336-
// Filter out the joined table alias from the available table aliases
337-
const availableSources = allAvailableTableAliases.filter(
340+
// Filter out the joined source alias from the available source aliases
341+
const availableSources = allAvailableSourceAliases.filter(
338342
(alias) => alias !== joinedSource
339343
)
340344

341-
const leftTableAlias = getTableAliasFromExpression(left)
342-
const rightTableAlias = getTableAliasFromExpression(right)
345+
const leftSourceAlias = getSourceAliasFromExpression(left)
346+
const rightSourceAlias = getSourceAliasFromExpression(right)
343347

344-
// If left expression refers to an available table and right refers to joined table, keep as is
348+
// If left expression refers to an available source and right refers to joined source, keep as is
345349
if (
346-
leftTableAlias &&
347-
availableSources.includes(leftTableAlias) &&
348-
rightTableAlias === joinedSource
350+
leftSourceAlias &&
351+
availableSources.includes(leftSourceAlias) &&
352+
rightSourceAlias === joinedSource
349353
) {
350354
return { mainExpr: left, joinedExpr: right }
351355
}
352356

353-
// If left expression refers to joined table and right refers to an available table, swap them
357+
// If left expression refers to joined source and right refers to an available source, swap them
354358
if (
355-
leftTableAlias === joinedSource &&
356-
rightTableAlias &&
357-
availableSources.includes(rightTableAlias)
359+
leftSourceAlias === joinedSource &&
360+
rightSourceAlias &&
361+
availableSources.includes(rightSourceAlias)
358362
) {
359363
return { mainExpr: right, joinedExpr: left }
360364
}
361365

362-
// If one expression doesn't refer to any table, this is an invalid join
363-
if (!leftTableAlias || !rightTableAlias) {
364-
// For backward compatibility, use the first available table alias in error message
365-
throw new InvalidJoinConditionTableMismatchError()
366+
// If one expression doesn't refer to any source, this is an invalid join
367+
if (!leftSourceAlias || !rightSourceAlias) {
368+
throw new InvalidJoinConditionSourceMismatchError()
366369
}
367370

368371
// If both expressions refer to the same alias, this is an invalid join
369-
if (leftTableAlias === rightTableAlias) {
370-
throw new InvalidJoinConditionSameTableError(leftTableAlias)
372+
if (leftSourceAlias === rightSourceAlias) {
373+
throw new InvalidJoinConditionSameSourceError(leftSourceAlias)
371374
}
372375

373-
// Left side must refer to an available table
376+
// Left side must refer to an available source
374377
// This cannot happen with the query builder as there is no way to build a ref
375-
// to an unavailable table, but just in case, but could happen with the IR
376-
if (!availableSources.includes(leftTableAlias)) {
377-
throw new InvalidJoinConditionLeftTableError(leftTableAlias)
378+
// to an unavailable source, but just in case, but could happen with the IR
379+
if (!availableSources.includes(leftSourceAlias)) {
380+
throw new InvalidJoinConditionLeftSourceError(leftSourceAlias)
378381
}
379382

380-
// Right side must refer to the joined table
381-
if (rightTableAlias !== joinedSource) {
382-
throw new InvalidJoinConditionRightTableError(joinedSource)
383+
// Right side must refer to the joined source
384+
if (rightSourceAlias !== joinedSource) {
385+
throw new InvalidJoinConditionRightSourceError(joinedSource)
383386
}
384387

385388
// This should not be reachable given the logic above, but just in case
386389
throw new InvalidJoinCondition()
387390
}
388391

389392
/**
390-
* Extracts the table alias from a join expression
393+
* Extracts the source alias from a join expression
391394
*/
392-
function getTableAliasFromExpression(expr: BasicExpression): string | null {
395+
function getSourceAliasFromExpression(expr: BasicExpression): string | null {
393396
switch (expr.type) {
394397
case `ref`:
395-
// PropRef path has the table alias as the first element
398+
// PropRef path has the source alias as the first element
396399
return expr.path[0] || null
397400
case `func`: {
398-
// For function expressions, we need to check if all arguments refer to the same table
399-
const tableAliases = new Set<string>()
401+
// For function expressions, we need to check if all arguments refer to the same source
402+
const sourceAliases = new Set<string>()
400403
for (const arg of expr.args) {
401-
const alias = getTableAliasFromExpression(arg)
404+
const alias = getSourceAliasFromExpression(arg)
402405
if (alias) {
403-
tableAliases.add(alias)
406+
sourceAliases.add(alias)
404407
}
405408
}
406-
// If all arguments refer to the same table, return that table alias
407-
return tableAliases.size === 1 ? Array.from(tableAliases)[0]! : null
409+
// If all arguments refer to the same source, return that source alias
410+
return sourceAliases.size === 1 ? Array.from(sourceAliases)[0]! : null
408411
}
409412
default:
410-
// Values (type='val') don't reference any table
413+
// Values (type='val') don't reference any source
411414
return null
412415
}
413416
}
@@ -567,7 +570,7 @@ function processJoinResults(joinType: string) {
567570
/**
568571
* Returns the active and lazy collections for a join clause.
569572
* The active collection is the one that we need to fully iterate over
570-
* and it can be the main table (i.e. left collection) or the joined table (i.e. right collection).
573+
* and it can be the main source (i.e. left collection) or the joined source (i.e. right collection).
571574
* The lazy collection is the one that we should join-in lazily based on matches in the active collection.
572575
* @param joinClause - The join clause to analyze
573576
* @param leftCollection - The left collection

packages/db/src/query/compiler/select.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { map } from "@tanstack/db-ivm"
22
import { PropRef, Value as ValClass, isExpressionLike } from "../ir.js"
3+
import { AggregateNotSupportedError } from "../../errors.js"
34
import { compileExpression } from "./evaluators.js"
45
import type { Aggregate, BasicExpression, Select } from "../ir.js"
56
import type {
@@ -157,9 +158,7 @@ export function processArgument(
157158
namespacedRow: NamespacedRow
158159
): any {
159160
if (isAggregateExpression(arg)) {
160-
throw new Error(
161-
`Aggregate expressions are not supported in this context. Use GROUP BY clause for aggregates.`
162-
)
161+
throw new AggregateNotSupportedError()
163162
}
164163

165164
// Pre-compile the expression and evaluate immediately

packages/db/tests/query/join.test.ts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1327,7 +1327,7 @@ function createJoinTests(autoIndex: `off` | `eager`): void {
13271327
})
13281328
})
13291329

1330-
test(`should throw error when both expressions refer to the same table`, () => {
1330+
test(`should throw error when both expressions refer to the same source`, () => {
13311331
const usersCollection = createCollection(
13321332
mockSyncCollectionOptions<User>({
13331333
id: `test-users-same-table`,
@@ -1349,11 +1349,11 @@ function createJoinTests(autoIndex: `off` | `eager`): void {
13491349
),
13501350
})
13511351
}).toThrow(
1352-
`Invalid join condition: both expressions refer to the same table "user"`
1352+
`Invalid join condition: both expressions refer to the same source "user"`
13531353
)
13541354
})
13551355

1356-
test(`should throw error when expressions don't reference table aliases`, () => {
1356+
test(`should throw error when expressions don't reference source aliases`, () => {
13571357
const usersCollection = createCollection(
13581358
mockSyncCollectionOptions<User>({
13591359
id: `test-users-no-refs`,
@@ -1375,11 +1375,11 @@ function createJoinTests(autoIndex: `off` | `eager`): void {
13751375
),
13761376
})
13771377
}).toThrow(
1378-
`Invalid join condition: expressions must reference table aliases`
1378+
`Invalid join condition: expressions must reference source aliases`
13791379
)
13801380
})
13811381

1382-
test(`should throw error when right side doesn't match joined table`, () => {
1382+
test(`should throw error when right side doesn't match joined source`, () => {
13831383
const usersCollection = createCollection(
13841384
mockSyncCollectionOptions<User>({
13851385
id: `test-users-no-refs`,
@@ -1410,11 +1410,11 @@ function createJoinTests(autoIndex: `off` | `eager`): void {
14101410
),
14111411
})
14121412
}).toThrow(
1413-
`Invalid join condition: right expression does not refer to the joined table "dept2"`
1413+
`Invalid join condition: right expression does not refer to the joined source "dept2"`
14141414
)
14151415
})
14161416

1417-
test(`should throw error when function expression has mixed table references`, () => {
1417+
test(`should throw error when function expression has mixed source references`, () => {
14181418
const usersCollection = createCollection(
14191419
mockSyncCollectionOptions<User>({
14201420
id: `test-users-mixed-refs`,
@@ -1436,7 +1436,7 @@ function createJoinTests(autoIndex: `off` | `eager`): void {
14361436
),
14371437
})
14381438
}).toThrow(
1439-
`Invalid join condition: both expressions refer to the same table "user"`
1439+
`Invalid join condition: both expressions refer to the same source "user"`
14401440
)
14411441
})
14421442

@@ -1595,7 +1595,9 @@ function createJoinTests(autoIndex: `off` | `eager`): void {
15951595
({ employee, manager }) => eq(employee.manager_id, manager.id),
15961596
`left`
15971597
)
1598-
.where(({ manager }) => or(isNull(manager.id), gt(manager.age, 35)))
1598+
.where(({ manager }) =>
1599+
or(isNull(manager?.id), gt(manager?.age, 35))
1600+
)
15991601
.select(({ employee, manager }) => ({
16001602
employeeId: employee.id,
16011603
employeeName: employee.name,

packages/db/tests/query/order-by.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1491,12 +1491,12 @@ function createOrderByTests(autoIndex: `off` | `eager`): void {
14911491
({ employees, departments }) =>
14921492
eq(employees.department_id, departments.id)
14931493
)
1494-
.orderBy(({ departments }) => departments.name, `asc`)
1494+
.orderBy(({ departments }) => departments?.name, `asc`)
14951495
.limit(5)
14961496
.select(({ employees, departments }) => ({
14971497
employeeId: employees.id,
14981498
employeeName: employees.name,
1499-
departmentName: departments.name,
1499+
departmentName: departments?.name,
15001500
}))
15011501
)
15021502

0 commit comments

Comments
 (0)