@tanstack/db 0.1.11 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/errors.cjs +18 -6
- package/dist/cjs/errors.cjs.map +1 -1
- package/dist/cjs/errors.d.cts +9 -3
- package/dist/cjs/index.cjs +3 -1
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/query/builder/functions.cjs +4 -1
- package/dist/cjs/query/builder/functions.cjs.map +1 -1
- package/dist/cjs/query/builder/functions.d.cts +38 -21
- package/dist/cjs/query/builder/index.cjs +25 -16
- package/dist/cjs/query/builder/index.cjs.map +1 -1
- package/dist/cjs/query/builder/index.d.cts +8 -8
- package/dist/cjs/query/builder/ref-proxy.cjs +12 -8
- package/dist/cjs/query/builder/ref-proxy.cjs.map +1 -1
- package/dist/cjs/query/builder/ref-proxy.d.cts +2 -1
- package/dist/cjs/query/builder/types.d.cts +493 -28
- package/dist/cjs/query/compiler/evaluators.cjs +29 -0
- package/dist/cjs/query/compiler/evaluators.cjs.map +1 -1
- package/dist/cjs/query/compiler/group-by.cjs +4 -2
- package/dist/cjs/query/compiler/group-by.cjs.map +1 -1
- package/dist/cjs/query/compiler/index.cjs +13 -4
- package/dist/cjs/query/compiler/index.cjs.map +1 -1
- package/dist/cjs/query/compiler/joins.cjs +70 -61
- package/dist/cjs/query/compiler/joins.cjs.map +1 -1
- package/dist/cjs/query/compiler/select.cjs +131 -42
- package/dist/cjs/query/compiler/select.cjs.map +1 -1
- package/dist/cjs/query/compiler/select.d.cts +1 -5
- package/dist/cjs/query/ir.cjs +4 -0
- package/dist/cjs/query/ir.cjs.map +1 -1
- package/dist/cjs/query/ir.d.cts +6 -1
- package/dist/cjs/query/optimizer.cjs +61 -20
- package/dist/cjs/query/optimizer.cjs.map +1 -1
- package/dist/esm/errors.d.ts +9 -3
- package/dist/esm/errors.js +18 -6
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/index.js +4 -2
- package/dist/esm/query/builder/functions.d.ts +38 -21
- package/dist/esm/query/builder/functions.js +4 -1
- package/dist/esm/query/builder/functions.js.map +1 -1
- package/dist/esm/query/builder/index.d.ts +8 -8
- package/dist/esm/query/builder/index.js +27 -18
- package/dist/esm/query/builder/index.js.map +1 -1
- package/dist/esm/query/builder/ref-proxy.d.ts +2 -1
- package/dist/esm/query/builder/ref-proxy.js +12 -8
- package/dist/esm/query/builder/ref-proxy.js.map +1 -1
- package/dist/esm/query/builder/types.d.ts +493 -28
- package/dist/esm/query/compiler/evaluators.js +29 -0
- package/dist/esm/query/compiler/evaluators.js.map +1 -1
- package/dist/esm/query/compiler/group-by.js +4 -2
- package/dist/esm/query/compiler/group-by.js.map +1 -1
- package/dist/esm/query/compiler/index.js +15 -6
- package/dist/esm/query/compiler/index.js.map +1 -1
- package/dist/esm/query/compiler/joins.js +71 -62
- package/dist/esm/query/compiler/joins.js.map +1 -1
- package/dist/esm/query/compiler/select.d.ts +1 -5
- package/dist/esm/query/compiler/select.js +131 -42
- package/dist/esm/query/compiler/select.js.map +1 -1
- package/dist/esm/query/ir.d.ts +6 -1
- package/dist/esm/query/ir.js +4 -0
- package/dist/esm/query/ir.js.map +1 -1
- package/dist/esm/query/optimizer.js +62 -21
- package/dist/esm/query/optimizer.js.map +1 -1
- package/package.json +2 -2
- package/src/errors.ts +17 -10
- package/src/query/builder/functions.ts +176 -108
- package/src/query/builder/index.ts +68 -48
- package/src/query/builder/ref-proxy.ts +14 -20
- package/src/query/builder/types.ts +622 -110
- package/src/query/compiler/evaluators.ts +30 -0
- package/src/query/compiler/group-by.ts +6 -1
- package/src/query/compiler/index.ts +23 -6
- package/src/query/compiler/joins.ts +132 -104
- package/src/query/compiler/select.ts +206 -113
- package/src/query/ir.ts +14 -1
- package/src/query/optimizer.ts +131 -59
|
@@ -337,6 +337,36 @@ function compileFunction(func: Func, isSingleRow: boolean): (data: any) => any {
|
|
|
337
337
|
}
|
|
338
338
|
}
|
|
339
339
|
|
|
340
|
+
// Null/undefined checking functions
|
|
341
|
+
case `isUndefined`: {
|
|
342
|
+
const arg = compiledArgs[0]!
|
|
343
|
+
return (data) => {
|
|
344
|
+
const value = arg(data)
|
|
345
|
+
return value === undefined
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
case `isNotUndefined`: {
|
|
349
|
+
const arg = compiledArgs[0]!
|
|
350
|
+
return (data) => {
|
|
351
|
+
const value = arg(data)
|
|
352
|
+
return value !== undefined
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
case `isNull`: {
|
|
356
|
+
const arg = compiledArgs[0]!
|
|
357
|
+
return (data) => {
|
|
358
|
+
const value = arg(data)
|
|
359
|
+
return value === null
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
case `isNotNull`: {
|
|
363
|
+
const arg = compiledArgs[0]!
|
|
364
|
+
return (data) => {
|
|
365
|
+
const value = arg(data)
|
|
366
|
+
return value !== null
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
340
370
|
default:
|
|
341
371
|
throw new UnknownFunctionError(func.name)
|
|
342
372
|
}
|
|
@@ -349,12 +349,17 @@ function getAggregateFunction(aggExpr: Aggregate) {
|
|
|
349
349
|
return typeof value === `number` ? value : value != null ? Number(value) : 0
|
|
350
350
|
}
|
|
351
351
|
|
|
352
|
+
// Create a raw value extractor function for the expression to aggregate
|
|
353
|
+
const rawValueExtractor = ([, namespacedRow]: [string, NamespacedRow]) => {
|
|
354
|
+
return compiledExpr(namespacedRow)
|
|
355
|
+
}
|
|
356
|
+
|
|
352
357
|
// Return the appropriate aggregate function
|
|
353
358
|
switch (aggExpr.name.toLowerCase()) {
|
|
354
359
|
case `sum`:
|
|
355
360
|
return sum(valueExtractor)
|
|
356
361
|
case `count`:
|
|
357
|
-
return count()
|
|
362
|
+
return count(rawValueExtractor)
|
|
358
363
|
case `avg`:
|
|
359
364
|
return avg(valueExtractor)
|
|
360
365
|
case `min`:
|
|
@@ -7,12 +7,12 @@ import {
|
|
|
7
7
|
LimitOffsetRequireOrderByError,
|
|
8
8
|
UnsupportedFromTypeError,
|
|
9
9
|
} from "../../errors.js"
|
|
10
|
-
import { PropRef, getWhereExpression } from "../ir.js"
|
|
10
|
+
import { PropRef, Value as ValClass, getWhereExpression } from "../ir.js"
|
|
11
11
|
import { compileExpression } from "./evaluators.js"
|
|
12
12
|
import { processJoins } from "./joins.js"
|
|
13
13
|
import { processGroupBy } from "./group-by.js"
|
|
14
14
|
import { processOrderBy } from "./order-by.js"
|
|
15
|
-
import {
|
|
15
|
+
import { processSelect } from "./select.js"
|
|
16
16
|
import type { OrderByOptimizationInfo } from "./order-by.js"
|
|
17
17
|
import type {
|
|
18
18
|
BasicExpression,
|
|
@@ -173,7 +173,7 @@ export function compileQuery(
|
|
|
173
173
|
})
|
|
174
174
|
)
|
|
175
175
|
} else if (query.select) {
|
|
176
|
-
pipeline =
|
|
176
|
+
pipeline = processSelect(pipeline, query.select, allInputs)
|
|
177
177
|
} else {
|
|
178
178
|
// If no SELECT clause, create __select_results with the main table data
|
|
179
179
|
pipeline = pipeline.pipe(
|
|
@@ -270,7 +270,8 @@ export function compileQuery(
|
|
|
270
270
|
const resultPipeline = orderedPipeline.pipe(
|
|
271
271
|
map(([key, [row, orderByIndex]]) => {
|
|
272
272
|
// Extract the final results from __select_results and include orderBy index
|
|
273
|
-
const
|
|
273
|
+
const raw = (row as any).__select_results
|
|
274
|
+
const finalResults = unwrapValue(raw)
|
|
274
275
|
return [key, [finalResults, orderByIndex]] as [unknown, [any, string]]
|
|
275
276
|
})
|
|
276
277
|
)
|
|
@@ -294,7 +295,8 @@ export function compileQuery(
|
|
|
294
295
|
const resultPipeline: ResultStream = pipeline.pipe(
|
|
295
296
|
map(([key, row]) => {
|
|
296
297
|
// Extract the final results from __select_results and return [key, [results, undefined]]
|
|
297
|
-
const
|
|
298
|
+
const raw = (row as any).__select_results
|
|
299
|
+
const finalResults = unwrapValue(raw)
|
|
298
300
|
return [key, [finalResults, undefined]] as [
|
|
299
301
|
unknown,
|
|
300
302
|
[any, string | undefined],
|
|
@@ -359,7 +361,9 @@ function processFrom(
|
|
|
359
361
|
const extractedInput = subQueryInput.pipe(
|
|
360
362
|
map((data: any) => {
|
|
361
363
|
const [key, [value, _orderByIndex]] = data
|
|
362
|
-
|
|
364
|
+
// Unwrap Value expressions that might have leaked through as the entire row
|
|
365
|
+
const unwrapped = unwrapValue(value)
|
|
366
|
+
return [key, unwrapped] as [unknown, any]
|
|
363
367
|
})
|
|
364
368
|
)
|
|
365
369
|
|
|
@@ -374,6 +378,19 @@ function processFrom(
|
|
|
374
378
|
}
|
|
375
379
|
}
|
|
376
380
|
|
|
381
|
+
// Helper to check if a value is a Value expression
|
|
382
|
+
function isValue(raw: any): boolean {
|
|
383
|
+
return (
|
|
384
|
+
raw instanceof ValClass ||
|
|
385
|
+
(raw && typeof raw === `object` && `type` in raw && raw.type === `val`)
|
|
386
|
+
)
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Helper to unwrap a Value expression or return the value itself
|
|
390
|
+
function unwrapValue(value: any): any {
|
|
391
|
+
return isValue(value) ? value.value : value
|
|
392
|
+
}
|
|
393
|
+
|
|
377
394
|
/**
|
|
378
395
|
* Recursively maps optimized subqueries to their original queries for proper caching.
|
|
379
396
|
* This ensures that when we encounter the same QueryRef object in different contexts,
|
|
@@ -7,9 +7,11 @@ import {
|
|
|
7
7
|
} from "@tanstack/db-ivm"
|
|
8
8
|
import {
|
|
9
9
|
CollectionInputNotFoundError,
|
|
10
|
+
InvalidJoinCondition,
|
|
11
|
+
InvalidJoinConditionLeftTableError,
|
|
12
|
+
InvalidJoinConditionRightTableError,
|
|
10
13
|
InvalidJoinConditionSameTableError,
|
|
11
14
|
InvalidJoinConditionTableMismatchError,
|
|
12
|
-
InvalidJoinConditionWrongTablesError,
|
|
13
15
|
JoinCollectionNotFoundError,
|
|
14
16
|
UnsupportedJoinSourceTypeError,
|
|
15
17
|
UnsupportedJoinTypeError,
|
|
@@ -139,10 +141,11 @@ function processJoin(
|
|
|
139
141
|
)
|
|
140
142
|
|
|
141
143
|
// Analyze which table each expression refers to and swap if necessary
|
|
144
|
+
const availableTableAliases = Object.keys(tables)
|
|
142
145
|
const { mainExpr, joinedExpr } = analyzeJoinExpressions(
|
|
143
146
|
joinClause.left,
|
|
144
147
|
joinClause.right,
|
|
145
|
-
|
|
148
|
+
availableTableAliases,
|
|
146
149
|
joinedTableAlias
|
|
147
150
|
)
|
|
148
151
|
|
|
@@ -187,93 +190,106 @@ function processJoin(
|
|
|
187
190
|
}
|
|
188
191
|
|
|
189
192
|
if (activeCollection) {
|
|
190
|
-
//
|
|
191
|
-
//
|
|
192
|
-
//
|
|
193
|
-
//
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
// Find the index for the path we join on
|
|
237
|
-
// we need to find the index inside the map operator
|
|
238
|
-
// because the indexes are only available after the initial sync
|
|
239
|
-
// so we can't fetch it during compilation
|
|
240
|
-
index ??= findIndexForField(
|
|
241
|
-
followRefCollection.indexes,
|
|
242
|
-
followRefResult.path
|
|
193
|
+
// If the lazy collection comes from a subquery that has a limit and/or an offset clause
|
|
194
|
+
// then we need to deoptimize the join because we don't know which rows are in the result set
|
|
195
|
+
// since we simply lookup matching keys in the index but the index contains all rows
|
|
196
|
+
// (not just the ones that pass the limit and offset clauses)
|
|
197
|
+
const lazyFrom =
|
|
198
|
+
activeCollection === `main` ? joinClause.from : rawQuery.from
|
|
199
|
+
const limitedSubquery =
|
|
200
|
+
lazyFrom.type === `queryRef` &&
|
|
201
|
+
(lazyFrom.query.limit || lazyFrom.query.offset)
|
|
202
|
+
|
|
203
|
+
if (!limitedSubquery) {
|
|
204
|
+
// This join can be optimized by having the active collection
|
|
205
|
+
// dynamically load keys into the lazy collection
|
|
206
|
+
// based on the value of the joinKey and by looking up
|
|
207
|
+
// matching rows in the index of the lazy collection
|
|
208
|
+
|
|
209
|
+
// Mark the lazy collection as lazy
|
|
210
|
+
// this Set is passed by the liveQueryCollection to the compiler
|
|
211
|
+
// such that the liveQueryCollection can check it after compilation
|
|
212
|
+
// to know which collections are lazy collections
|
|
213
|
+
lazyCollections.add(lazyCollection.id)
|
|
214
|
+
|
|
215
|
+
const activePipeline =
|
|
216
|
+
activeCollection === `main` ? mainPipeline : joinedPipeline
|
|
217
|
+
|
|
218
|
+
let index: BaseIndex<string | number> | undefined
|
|
219
|
+
|
|
220
|
+
const lazyCollectionJoinExpr =
|
|
221
|
+
activeCollection === `main`
|
|
222
|
+
? (joinedExpr as PropRef)
|
|
223
|
+
: (mainExpr as PropRef)
|
|
224
|
+
|
|
225
|
+
const followRefResult = followRef(
|
|
226
|
+
rawQuery,
|
|
227
|
+
lazyCollectionJoinExpr,
|
|
228
|
+
lazyCollection
|
|
229
|
+
)!
|
|
230
|
+
const followRefCollection = followRefResult.collection
|
|
231
|
+
|
|
232
|
+
const fieldName = followRefResult.path[0]
|
|
233
|
+
if (fieldName) {
|
|
234
|
+
ensureIndexForField(
|
|
235
|
+
fieldName,
|
|
236
|
+
followRefResult.path,
|
|
237
|
+
followRefCollection
|
|
243
238
|
)
|
|
239
|
+
}
|
|
244
240
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
241
|
+
let deoptimized = false
|
|
242
|
+
|
|
243
|
+
const activePipelineWithLoading: IStreamBuilder<
|
|
244
|
+
[key: unknown, [originalKey: string, namespacedRow: NamespacedRow]]
|
|
245
|
+
> = activePipeline.pipe(
|
|
246
|
+
tap(([joinKey, _]) => {
|
|
247
|
+
if (deoptimized) {
|
|
248
|
+
return
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Find the index for the path we join on
|
|
252
|
+
// we need to find the index inside the map operator
|
|
253
|
+
// because the indexes are only available after the initial sync
|
|
254
|
+
// so we can't fetch it during compilation
|
|
255
|
+
index ??= findIndexForField(
|
|
256
|
+
followRefCollection.indexes,
|
|
257
|
+
followRefResult.path
|
|
253
258
|
)
|
|
254
|
-
}
|
|
255
259
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
//
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
260
|
+
// The `callbacks` object is passed by the liveQueryCollection to the compiler.
|
|
261
|
+
// It contains a function to lazy load keys for each lazy collection
|
|
262
|
+
// as well as a function to switch back to a regular collection
|
|
263
|
+
// (useful when there's no index for available for lazily loading the collection)
|
|
264
|
+
const collectionCallbacks = callbacks[lazyCollection.id]
|
|
265
|
+
if (!collectionCallbacks) {
|
|
266
|
+
throw new Error(
|
|
267
|
+
`Internal error: callbacks for collection are missing in join pipeline. Make sure the live query collection sets them before running the pipeline.`
|
|
268
|
+
)
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const { loadKeys, loadInitialState } = collectionCallbacks
|
|
272
|
+
|
|
273
|
+
if (index && index.supports(`eq`)) {
|
|
274
|
+
// Use the index to fetch the PKs of the rows in the lazy collection
|
|
275
|
+
// that match this row from the active collection based on the value of the joinKey
|
|
276
|
+
const matchingKeys = index.lookup(`eq`, joinKey)
|
|
277
|
+
// Inform the lazy collection that those keys need to be loaded
|
|
278
|
+
loadKeys(matchingKeys)
|
|
279
|
+
} else {
|
|
280
|
+
// We can't optimize the join because there is no index for the join key
|
|
281
|
+
// on the lazy collection, so we load the initial state
|
|
282
|
+
deoptimized = true
|
|
283
|
+
loadInitialState()
|
|
284
|
+
}
|
|
285
|
+
})
|
|
286
|
+
)
|
|
272
287
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
288
|
+
if (activeCollection === `main`) {
|
|
289
|
+
mainPipeline = activePipelineWithLoading
|
|
290
|
+
} else {
|
|
291
|
+
joinedPipeline = activePipelineWithLoading
|
|
292
|
+
}
|
|
277
293
|
}
|
|
278
294
|
}
|
|
279
295
|
|
|
@@ -286,53 +302,65 @@ function processJoin(
|
|
|
286
302
|
|
|
287
303
|
/**
|
|
288
304
|
* Analyzes join expressions to determine which refers to which table
|
|
289
|
-
* and returns them in the correct order (
|
|
305
|
+
* and returns them in the correct order (available table expression first, joined table expression second)
|
|
290
306
|
*/
|
|
291
307
|
function analyzeJoinExpressions(
|
|
292
308
|
left: BasicExpression,
|
|
293
309
|
right: BasicExpression,
|
|
294
|
-
|
|
310
|
+
allAvailableTableAliases: Array<string>,
|
|
295
311
|
joinedTableAlias: string
|
|
296
312
|
): { mainExpr: BasicExpression; joinedExpr: BasicExpression } {
|
|
313
|
+
// Filter out the joined table alias from the available table aliases
|
|
314
|
+
const availableTableAliases = allAvailableTableAliases.filter(
|
|
315
|
+
(alias) => alias !== joinedTableAlias
|
|
316
|
+
)
|
|
317
|
+
|
|
297
318
|
const leftTableAlias = getTableAliasFromExpression(left)
|
|
298
319
|
const rightTableAlias = getTableAliasFromExpression(right)
|
|
299
320
|
|
|
300
|
-
// If left expression refers to
|
|
321
|
+
// If left expression refers to an available table and right refers to joined table, keep as is
|
|
301
322
|
if (
|
|
302
|
-
leftTableAlias
|
|
323
|
+
leftTableAlias &&
|
|
324
|
+
availableTableAliases.includes(leftTableAlias) &&
|
|
303
325
|
rightTableAlias === joinedTableAlias
|
|
304
326
|
) {
|
|
305
327
|
return { mainExpr: left, joinedExpr: right }
|
|
306
328
|
}
|
|
307
329
|
|
|
308
|
-
// If left expression refers to joined table and right refers to
|
|
330
|
+
// If left expression refers to joined table and right refers to an available table, swap them
|
|
309
331
|
if (
|
|
310
332
|
leftTableAlias === joinedTableAlias &&
|
|
311
|
-
rightTableAlias
|
|
333
|
+
rightTableAlias &&
|
|
334
|
+
availableTableAliases.includes(rightTableAlias)
|
|
312
335
|
) {
|
|
313
336
|
return { mainExpr: right, joinedExpr: left }
|
|
314
337
|
}
|
|
315
338
|
|
|
339
|
+
// If one expression doesn't refer to any table, this is an invalid join
|
|
340
|
+
if (!leftTableAlias || !rightTableAlias) {
|
|
341
|
+
// For backward compatibility, use the first available table alias in error message
|
|
342
|
+
throw new InvalidJoinConditionTableMismatchError()
|
|
343
|
+
}
|
|
344
|
+
|
|
316
345
|
// If both expressions refer to the same alias, this is an invalid join
|
|
317
346
|
if (leftTableAlias === rightTableAlias) {
|
|
318
|
-
throw new InvalidJoinConditionSameTableError(leftTableAlias
|
|
347
|
+
throw new InvalidJoinConditionSameTableError(leftTableAlias)
|
|
319
348
|
}
|
|
320
349
|
|
|
321
|
-
//
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
)
|
|
350
|
+
// Left side must refer to an available table
|
|
351
|
+
// This cannot happen with the query builder as there is no way to build a ref
|
|
352
|
+
// to an unavailable table, but just in case, but could happen with the IR
|
|
353
|
+
if (!availableTableAliases.includes(leftTableAlias)) {
|
|
354
|
+
throw new InvalidJoinConditionLeftTableError(leftTableAlias)
|
|
327
355
|
}
|
|
328
356
|
|
|
329
|
-
//
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
)
|
|
357
|
+
// Right side must refer to the joined table
|
|
358
|
+
if (rightTableAlias !== joinedTableAlias) {
|
|
359
|
+
throw new InvalidJoinConditionRightTableError(joinedTableAlias)
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// This should not be reachable given the logic above, but just in case
|
|
363
|
+
throw new InvalidJoinCondition()
|
|
336
364
|
}
|
|
337
365
|
|
|
338
366
|
/**
|