@relq/orm 0.1.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/LICENSE +21 -0
- package/README.md +236 -0
- package/dist/cjs/__test-types.cjs +17 -0
- package/dist/cjs/addon/cursor.cjs +1473 -0
- package/dist/cjs/addon/pg.cjs +4969 -0
- package/dist/cjs/cache/index.cjs +9 -0
- package/dist/cjs/cache/query-cache.cjs +311 -0
- package/dist/cjs/condition/array-condition-builder.cjs +527 -0
- package/dist/cjs/condition/array-numeric-condition-builder.cjs +186 -0
- package/dist/cjs/condition/array-specialized-condition-builder.cjs +206 -0
- package/dist/cjs/condition/array-string-condition-builder.cjs +146 -0
- package/dist/cjs/condition/base-condition-builder.cjs +2 -0
- package/dist/cjs/condition/condition-collector.cjs +461 -0
- package/dist/cjs/condition/fulltext-condition-builder.cjs +61 -0
- package/dist/cjs/condition/geometric-condition-builder.cjs +228 -0
- package/dist/cjs/condition/index.cjs +29 -0
- package/dist/cjs/condition/jsonb-condition-builder.cjs +448 -0
- package/dist/cjs/condition/network-condition-builder.cjs +237 -0
- package/dist/cjs/condition/postgis-condition-builder.cjs +188 -0
- package/dist/cjs/condition/range-condition-builder.cjs +98 -0
- package/dist/cjs/core/helpers/ConnectedAggregateBuilder.cjs +132 -0
- package/dist/cjs/core/helpers/ConnectedCTEBuilder.cjs +53 -0
- package/dist/cjs/core/helpers/ConnectedCountBuilder.cjs +73 -0
- package/dist/cjs/core/helpers/ConnectedDeleteBuilder.cjs +65 -0
- package/dist/cjs/core/helpers/ConnectedInsertBuilder.cjs +112 -0
- package/dist/cjs/core/helpers/ConnectedInsertFromSelectBuilder.cjs +66 -0
- package/dist/cjs/core/helpers/ConnectedQueryBuilder.cjs +146 -0
- package/dist/cjs/core/helpers/ConnectedRawQueryBuilder.cjs +46 -0
- package/dist/cjs/core/helpers/ConnectedSelectBuilder.cjs +331 -0
- package/dist/cjs/core/helpers/ConnectedTransactionBuilder.cjs +105 -0
- package/dist/cjs/core/helpers/ConnectedUpdateBuilder.cjs +79 -0
- package/dist/cjs/core/helpers/PaginateBuilder.cjs +178 -0
- package/dist/cjs/core/helpers/ReturningExecutor.cjs +70 -0
- package/dist/cjs/core/helpers/capability-guard.cjs +10 -0
- package/dist/cjs/core/helpers/index.cjs +31 -0
- package/dist/cjs/core/helpers/methods.cjs +10 -0
- package/dist/cjs/core/helpers/query-convenience.cjs +238 -0
- package/dist/cjs/core/helpers/select-joins.cjs +251 -0
- package/dist/cjs/core/helpers/select-pagination.cjs +233 -0
- package/dist/cjs/core/helpers/select-types.cjs +2 -0
- package/dist/cjs/core/pg-family/cockroachdb-client/capabilities.cjs +31 -0
- package/dist/cjs/core/pg-family/cockroachdb-client/index.cjs +7 -0
- package/dist/cjs/core/pg-family/cockroachdb-client/relq-cockroach.cjs +16 -0
- package/dist/cjs/core/pg-family/dsql-client/capabilities.cjs +31 -0
- package/dist/cjs/core/pg-family/dsql-client/index.cjs +7 -0
- package/dist/cjs/core/pg-family/dsql-client/relq-dsql.cjs +16 -0
- package/dist/cjs/core/pg-family/index.cjs +19 -0
- package/dist/cjs/core/pg-family/nile-client/capabilities.cjs +31 -0
- package/dist/cjs/core/pg-family/nile-client/index.cjs +7 -0
- package/dist/cjs/core/pg-family/nile-client/relq-nile.cjs +36 -0
- package/dist/cjs/core/pg-family/nile-client/tenant-context.cjs +44 -0
- package/dist/cjs/core/pg-family/pg-client/capabilities.cjs +31 -0
- package/dist/cjs/core/pg-family/pg-client/index.cjs +7 -0
- package/dist/cjs/core/pg-family/pg-client/relq-postgres.cjs +43 -0
- package/dist/cjs/core/pg-family/shared/pg-base.cjs +385 -0
- package/dist/cjs/core/pg-family/shared/pg-dialect.cjs +67 -0
- package/dist/cjs/core/pg-family/shared/pg-error-parser.cjs +34 -0
- package/dist/cjs/core/pg-family/shared/pg-type-coercion.cjs +14 -0
- package/dist/cjs/core/relq-base.cjs +307 -0
- package/dist/cjs/core/relq-client.cjs +56 -0
- package/dist/cjs/core/shared/cleanup.cjs +36 -0
- package/dist/cjs/core/shared/column-mapping.cjs +97 -0
- package/dist/cjs/core/shared/errors.cjs +17 -0
- package/dist/cjs/core/shared/index.cjs +24 -0
- package/dist/cjs/core/shared/table-accessor.cjs +22 -0
- package/dist/cjs/core/shared/transform.cjs +35 -0
- package/dist/cjs/core/shared/types.cjs +2 -0
- package/dist/cjs/core/shared/validation.cjs +140 -0
- package/dist/cjs/core/types/core.types.cjs +2 -0
- package/dist/cjs/count/count-builder.cjs +88 -0
- package/dist/cjs/count/index.cjs +5 -0
- package/dist/cjs/delete/delete-builder.cjs +176 -0
- package/dist/cjs/delete/index.cjs +5 -0
- package/dist/cjs/explain/explain-builder.cjs +99 -0
- package/dist/cjs/explain/index.cjs +5 -0
- package/dist/cjs/index.cjs +26 -0
- package/dist/cjs/insert/conflict-builder.cjs +213 -0
- package/dist/cjs/insert/index.cjs +5 -0
- package/dist/cjs/insert/insert-builder.cjs +320 -0
- package/dist/cjs/insert/insert-from-select-builder.cjs +86 -0
- package/dist/cjs/pubsub/index.cjs +7 -0
- package/dist/cjs/pubsub/listen-notify-builder.cjs +57 -0
- package/dist/cjs/pubsub/listener-connection.cjs +180 -0
- package/dist/cjs/raw/index.cjs +8 -0
- package/dist/cjs/raw/raw-query-builder.cjs +27 -0
- package/dist/cjs/raw/sql-template.cjs +73 -0
- package/dist/cjs/select/aggregate-builder.cjs +179 -0
- package/dist/cjs/select/index.cjs +16 -0
- package/dist/cjs/select/join-builder.cjs +192 -0
- package/dist/cjs/select/join-condition-builder.cjs +189 -0
- package/dist/cjs/select/join-internals.cjs +5 -0
- package/dist/cjs/select/join-many-condition-builder.cjs +159 -0
- package/dist/cjs/select/scalar-query-builder.cjs +134 -0
- package/dist/cjs/select/scalar-select-builder.cjs +78 -0
- package/dist/cjs/select/select-builder.cjs +426 -0
- package/dist/cjs/select/sql-expression.cjs +38 -0
- package/dist/cjs/select/table-proxy.cjs +99 -0
- package/dist/cjs/shared/aws-dsql.cjs +181 -0
- package/dist/cjs/shared/errors/relq-errors.cjs +361 -0
- package/dist/cjs/shared/pg-format.cjs +383 -0
- package/dist/cjs/shared/types/config-types.cjs +51 -0
- package/dist/cjs/transaction/index.cjs +6 -0
- package/dist/cjs/transaction/transaction-builder.cjs +78 -0
- package/dist/cjs/types/aggregate-types.cjs +2 -0
- package/dist/cjs/types/inference-types.cjs +18 -0
- package/dist/cjs/types/pagination-types.cjs +7 -0
- package/dist/cjs/types/result-types.cjs +2 -0
- package/dist/cjs/types/scalar-types.cjs +2 -0
- package/dist/cjs/types/schema-types.cjs +2 -0
- package/dist/cjs/types/subscription-types.cjs +2 -0
- package/dist/cjs/types.cjs +2 -0
- package/dist/cjs/update/array-update-builder.cjs +232 -0
- package/dist/cjs/update/index.cjs +16 -0
- package/dist/cjs/update/jsonb-update-builder.cjs +219 -0
- package/dist/cjs/update/update-builder.cjs +274 -0
- package/dist/cjs/utils/addon/pg/cursor.cjs +8 -0
- package/dist/cjs/utils/addon/pg/pg.cjs +23 -0
- package/dist/cjs/utils/case-converter.cjs +58 -0
- package/dist/cjs/utils/env-resolver.cjs +226 -0
- package/dist/cjs/utils/environment-detection.cjs +124 -0
- package/dist/cjs/utils/fk-resolver.cjs +186 -0
- package/dist/cjs/utils/index.cjs +25 -0
- package/dist/cjs/utils/pool-defaults.cjs +91 -0
- package/dist/cjs/utils/type-coercion.cjs +120 -0
- package/dist/cjs/window/index.cjs +5 -0
- package/dist/cjs/window/window-builder.cjs +80 -0
- package/dist/esm/__test-types.js +15 -0
- package/dist/esm/addon/cursor.js +1440 -0
- package/dist/esm/addon/pg.js +4931 -0
- package/dist/esm/cache/index.js +1 -0
- package/dist/esm/cache/query-cache.js +303 -0
- package/dist/esm/condition/array-condition-builder.js +519 -0
- package/dist/esm/condition/array-numeric-condition-builder.js +182 -0
- package/dist/esm/condition/array-specialized-condition-builder.js +200 -0
- package/dist/esm/condition/array-string-condition-builder.js +142 -0
- package/dist/esm/condition/base-condition-builder.js +1 -0
- package/dist/esm/condition/condition-collector.js +452 -0
- package/dist/esm/condition/fulltext-condition-builder.js +53 -0
- package/dist/esm/condition/geometric-condition-builder.js +220 -0
- package/dist/esm/condition/index.js +8 -0
- package/dist/esm/condition/jsonb-condition-builder.js +439 -0
- package/dist/esm/condition/network-condition-builder.js +229 -0
- package/dist/esm/condition/postgis-condition-builder.js +180 -0
- package/dist/esm/condition/range-condition-builder.js +90 -0
- package/dist/esm/core/helpers/ConnectedAggregateBuilder.js +128 -0
- package/dist/esm/core/helpers/ConnectedCTEBuilder.js +49 -0
- package/dist/esm/core/helpers/ConnectedCountBuilder.js +69 -0
- package/dist/esm/core/helpers/ConnectedDeleteBuilder.js +61 -0
- package/dist/esm/core/helpers/ConnectedInsertBuilder.js +108 -0
- package/dist/esm/core/helpers/ConnectedInsertFromSelectBuilder.js +62 -0
- package/dist/esm/core/helpers/ConnectedQueryBuilder.js +142 -0
- package/dist/esm/core/helpers/ConnectedRawQueryBuilder.js +42 -0
- package/dist/esm/core/helpers/ConnectedSelectBuilder.js +327 -0
- package/dist/esm/core/helpers/ConnectedTransactionBuilder.js +100 -0
- package/dist/esm/core/helpers/ConnectedUpdateBuilder.js +75 -0
- package/dist/esm/core/helpers/PaginateBuilder.js +174 -0
- package/dist/esm/core/helpers/ReturningExecutor.js +66 -0
- package/dist/esm/core/helpers/capability-guard.js +7 -0
- package/dist/esm/core/helpers/index.js +13 -0
- package/dist/esm/core/helpers/methods.js +6 -0
- package/dist/esm/core/helpers/query-convenience.js +194 -0
- package/dist/esm/core/helpers/select-joins.js +246 -0
- package/dist/esm/core/helpers/select-pagination.js +226 -0
- package/dist/esm/core/helpers/select-types.js +1 -0
- package/dist/esm/core/pg-family/cockroachdb-client/capabilities.js +28 -0
- package/dist/esm/core/pg-family/cockroachdb-client/index.js +2 -0
- package/dist/esm/core/pg-family/cockroachdb-client/relq-cockroach.js +12 -0
- package/dist/esm/core/pg-family/dsql-client/capabilities.js +28 -0
- package/dist/esm/core/pg-family/dsql-client/index.js +2 -0
- package/dist/esm/core/pg-family/dsql-client/relq-dsql.js +12 -0
- package/dist/esm/core/pg-family/index.js +6 -0
- package/dist/esm/core/pg-family/nile-client/capabilities.js +28 -0
- package/dist/esm/core/pg-family/nile-client/index.js +2 -0
- package/dist/esm/core/pg-family/nile-client/relq-nile.js +32 -0
- package/dist/esm/core/pg-family/nile-client/tenant-context.js +40 -0
- package/dist/esm/core/pg-family/pg-client/capabilities.js +28 -0
- package/dist/esm/core/pg-family/pg-client/index.js +2 -0
- package/dist/esm/core/pg-family/pg-client/relq-postgres.js +39 -0
- package/dist/esm/core/pg-family/shared/pg-base.js +347 -0
- package/dist/esm/core/pg-family/shared/pg-dialect.js +63 -0
- package/dist/esm/core/pg-family/shared/pg-error-parser.js +29 -0
- package/dist/esm/core/pg-family/shared/pg-type-coercion.js +6 -0
- package/dist/esm/core/relq-base.js +270 -0
- package/dist/esm/core/relq-client.js +48 -0
- package/dist/esm/core/shared/cleanup.js +27 -0
- package/dist/esm/core/shared/column-mapping.js +90 -0
- package/dist/esm/core/shared/errors.js +13 -0
- package/dist/esm/core/shared/index.js +6 -0
- package/dist/esm/core/shared/table-accessor.js +19 -0
- package/dist/esm/core/shared/transform.js +30 -0
- package/dist/esm/core/shared/types.js +1 -0
- package/dist/esm/core/shared/validation.js +136 -0
- package/dist/esm/core/types/core.types.js +1 -0
- package/dist/esm/count/count-builder.js +81 -0
- package/dist/esm/count/index.js +1 -0
- package/dist/esm/delete/delete-builder.js +169 -0
- package/dist/esm/delete/index.js +1 -0
- package/dist/esm/explain/explain-builder.js +95 -0
- package/dist/esm/explain/index.js +1 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/insert/conflict-builder.js +202 -0
- package/dist/esm/insert/index.js +1 -0
- package/dist/esm/insert/insert-builder.js +313 -0
- package/dist/esm/insert/insert-from-select-builder.js +79 -0
- package/dist/esm/pubsub/index.js +1 -0
- package/dist/esm/pubsub/listen-notify-builder.js +48 -0
- package/dist/esm/pubsub/listener-connection.js +173 -0
- package/dist/esm/raw/index.js +2 -0
- package/dist/esm/raw/raw-query-builder.js +20 -0
- package/dist/esm/raw/sql-template.js +66 -0
- package/dist/esm/select/aggregate-builder.js +172 -0
- package/dist/esm/select/index.js +4 -0
- package/dist/esm/select/join-builder.js +184 -0
- package/dist/esm/select/join-condition-builder.js +181 -0
- package/dist/esm/select/join-internals.js +2 -0
- package/dist/esm/select/join-many-condition-builder.js +151 -0
- package/dist/esm/select/scalar-query-builder.js +126 -0
- package/dist/esm/select/scalar-select-builder.js +70 -0
- package/dist/esm/select/select-builder.js +419 -0
- package/dist/esm/select/sql-expression.js +33 -0
- package/dist/esm/select/table-proxy.js +91 -0
- package/dist/esm/shared/aws-dsql.js +140 -0
- package/dist/esm/shared/errors/relq-errors.js +339 -0
- package/dist/esm/shared/pg-format.js +375 -0
- package/dist/esm/shared/types/config-types.js +46 -0
- package/dist/esm/transaction/index.js +1 -0
- package/dist/esm/transaction/transaction-builder.js +70 -0
- package/dist/esm/types/aggregate-types.js +1 -0
- package/dist/esm/types/inference-types.js +12 -0
- package/dist/esm/types/pagination-types.js +4 -0
- package/dist/esm/types/result-types.js +1 -0
- package/dist/esm/types/scalar-types.js +1 -0
- package/dist/esm/types/schema-types.js +1 -0
- package/dist/esm/types/subscription-types.js +1 -0
- package/dist/esm/types.js +1 -0
- package/dist/esm/update/array-update-builder.js +219 -0
- package/dist/esm/update/index.js +3 -0
- package/dist/esm/update/jsonb-update-builder.js +211 -0
- package/dist/esm/update/update-builder.js +267 -0
- package/dist/esm/utils/addon/pg/cursor.js +1 -0
- package/dist/esm/utils/addon/pg/pg.js +2 -0
- package/dist/esm/utils/case-converter.js +55 -0
- package/dist/esm/utils/env-resolver.js +213 -0
- package/dist/esm/utils/environment-detection.js +114 -0
- package/dist/esm/utils/fk-resolver.js +178 -0
- package/dist/esm/utils/index.js +4 -0
- package/dist/esm/utils/pool-defaults.js +85 -0
- package/dist/esm/utils/type-coercion.js +112 -0
- package/dist/esm/window/index.js +1 -0
- package/dist/esm/window/window-builder.js +73 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +7281 -0
- package/dist/index.js +1 -0
- package/package.json +52 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { InsertBuilder } from "../../insert/insert-builder.js";
|
|
2
|
+
import { UpdateBuilder } from "../../update/update-builder.js";
|
|
3
|
+
import { INTERNAL } from "./methods.js";
|
|
4
|
+
export class ReturningExecutor {
|
|
5
|
+
builder;
|
|
6
|
+
relq;
|
|
7
|
+
tableName;
|
|
8
|
+
schemaKey;
|
|
9
|
+
columnMapping;
|
|
10
|
+
constructor(builder, relq, tableName, schemaKey) {
|
|
11
|
+
this.builder = builder;
|
|
12
|
+
this.relq = relq;
|
|
13
|
+
this.tableName = tableName;
|
|
14
|
+
this.schemaKey = schemaKey;
|
|
15
|
+
this.setupColumnMapping();
|
|
16
|
+
}
|
|
17
|
+
setupColumnMapping() {
|
|
18
|
+
if (!this.tableName && !this.schemaKey) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const internal = this.relq[INTERNAL];
|
|
22
|
+
const tableDef = internal.getTableDef(this.schemaKey || this.tableName);
|
|
23
|
+
if (!tableDef) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const tableColumns = tableDef.$columns || tableDef;
|
|
27
|
+
this.columnMapping = new Map();
|
|
28
|
+
for (const [propName, columnDef] of Object.entries(tableColumns)) {
|
|
29
|
+
if (columnDef && typeof columnDef === 'object') {
|
|
30
|
+
const sqlName = columnDef.$columnName || propName;
|
|
31
|
+
this.columnMapping.set(sqlName, propName);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
transformRow(row) {
|
|
36
|
+
if (!this.columnMapping) {
|
|
37
|
+
return row;
|
|
38
|
+
}
|
|
39
|
+
const transformed = {};
|
|
40
|
+
for (const [sqlKey, value] of Object.entries(row)) {
|
|
41
|
+
const propName = this.columnMapping.get(sqlKey) || sqlKey;
|
|
42
|
+
transformed[propName] = value;
|
|
43
|
+
}
|
|
44
|
+
return transformed;
|
|
45
|
+
}
|
|
46
|
+
toString() {
|
|
47
|
+
return this.builder.toString();
|
|
48
|
+
}
|
|
49
|
+
async run(withMetadata) {
|
|
50
|
+
if (this.builder instanceof InsertBuilder) {
|
|
51
|
+
for (const row of this.builder.insertData) {
|
|
52
|
+
this.relq[INTERNAL].validateData(this.builder.tableName, row, 'insert');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
else if (this.builder instanceof UpdateBuilder) {
|
|
56
|
+
this.relq[INTERNAL].validateData(this.builder.tableName, this.builder.updateData, 'update');
|
|
57
|
+
}
|
|
58
|
+
const sql = this.builder.toString();
|
|
59
|
+
const result = await this.relq[INTERNAL].executeSelect(sql);
|
|
60
|
+
const transformedData = result.data.map(row => this.transformRow(row));
|
|
61
|
+
if (withMetadata) {
|
|
62
|
+
return { ...result, data: transformedData };
|
|
63
|
+
}
|
|
64
|
+
return transformedData;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { RelqDialectError } from "../shared/errors.js";
|
|
2
|
+
export function requireCapability(relq, capability, featureName, suggestion) {
|
|
3
|
+
const value = relq.capabilities[capability];
|
|
4
|
+
if (value === false) {
|
|
5
|
+
throw new RelqDialectError(`${featureName} is not supported`, relq.dialect, suggestion);
|
|
6
|
+
}
|
|
7
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { ConnectedAggregateBuilder } from "./ConnectedAggregateBuilder.js";
|
|
2
|
+
export { ConnectedCTEBuilder } from "./ConnectedCTEBuilder.js";
|
|
3
|
+
export { ConnectedSelectBuilder } from "./ConnectedSelectBuilder.js";
|
|
4
|
+
export { ConnectedUpdateBuilder } from "./ConnectedUpdateBuilder.js";
|
|
5
|
+
export { ConnectedDeleteBuilder } from "./ConnectedDeleteBuilder.js";
|
|
6
|
+
export { ConnectedInsertBuilder } from "./ConnectedInsertBuilder.js";
|
|
7
|
+
export { ConnectedCountBuilder } from "./ConnectedCountBuilder.js";
|
|
8
|
+
export { TransactionClient, executeTransaction } from "./ConnectedTransactionBuilder.js";
|
|
9
|
+
export { ConnectedQueryBuilder } from "./ConnectedQueryBuilder.js";
|
|
10
|
+
export { ConnectedRawQueryBuilder } from "./ConnectedRawQueryBuilder.js";
|
|
11
|
+
export { PaginateBuilder } from "./PaginateBuilder.js";
|
|
12
|
+
export { ReturningExecutor } from "./ReturningExecutor.js";
|
|
13
|
+
export { INTERNAL, debugLog } from "./methods.js";
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { CountBuilder } from "../../count/count-builder.js";
|
|
2
|
+
import { InsertBuilder } from "../../insert/insert-builder.js";
|
|
3
|
+
import { SelectBuilder } from "../../select/select-builder.js";
|
|
4
|
+
import { UpdateBuilder } from "../../update/update-builder.js";
|
|
5
|
+
import { INTERNAL } from "./methods.js";
|
|
6
|
+
export async function executeFindById(ctx, id, getPrimaryKeyColumn) {
|
|
7
|
+
const pkColumn = getPrimaryKeyColumn();
|
|
8
|
+
const dbColumn = ctx.relq[INTERNAL].transformToDbColumns(ctx.tableName, { [pkColumn]: id });
|
|
9
|
+
const dbColName = Object.keys(dbColumn)[0];
|
|
10
|
+
const builder = new SelectBuilder(ctx.tableName, ['*']);
|
|
11
|
+
builder.where(q => q.equal(dbColName, id));
|
|
12
|
+
builder.limit(1);
|
|
13
|
+
const sql = builder.toString();
|
|
14
|
+
const result = await ctx.relq[INTERNAL].executeSelectOne(sql, ctx.tableName);
|
|
15
|
+
return result.data;
|
|
16
|
+
}
|
|
17
|
+
export async function executeFindOne(ctx, filter) {
|
|
18
|
+
const builder = new SelectBuilder(ctx.tableName, ['*']);
|
|
19
|
+
const dbFilter = ctx.relq[INTERNAL].transformToDbColumns(ctx.tableName, filter);
|
|
20
|
+
builder.where(q => {
|
|
21
|
+
for (const [col, val] of Object.entries(dbFilter)) {
|
|
22
|
+
q.equal(col, val);
|
|
23
|
+
}
|
|
24
|
+
return q;
|
|
25
|
+
});
|
|
26
|
+
builder.limit(1);
|
|
27
|
+
const sql = builder.toString();
|
|
28
|
+
const result = await ctx.relq[INTERNAL].executeSelectOne(sql, ctx.tableName);
|
|
29
|
+
return result.data;
|
|
30
|
+
}
|
|
31
|
+
export async function executeFindMany(ctx, filter) {
|
|
32
|
+
const builder = new SelectBuilder(ctx.tableName, ['*']);
|
|
33
|
+
const dbFilter = ctx.relq[INTERNAL].transformToDbColumns(ctx.tableName, filter);
|
|
34
|
+
builder.where(q => {
|
|
35
|
+
for (const [col, val] of Object.entries(dbFilter)) {
|
|
36
|
+
q.equal(col, val);
|
|
37
|
+
}
|
|
38
|
+
return q;
|
|
39
|
+
});
|
|
40
|
+
const sql = builder.toString();
|
|
41
|
+
const result = await ctx.relq[INTERNAL].executeSelect(sql, ctx.tableName);
|
|
42
|
+
return result.data;
|
|
43
|
+
}
|
|
44
|
+
export async function executeExists(ctx, filter) {
|
|
45
|
+
const builder = new CountBuilder(ctx.tableName);
|
|
46
|
+
const dbFilter = ctx.relq[INTERNAL].transformToDbColumns(ctx.tableName, filter);
|
|
47
|
+
builder.where(q => {
|
|
48
|
+
for (const [col, val] of Object.entries(dbFilter)) {
|
|
49
|
+
q.equal(col, val);
|
|
50
|
+
}
|
|
51
|
+
return q;
|
|
52
|
+
});
|
|
53
|
+
const sql = builder.toString();
|
|
54
|
+
const result = await ctx.relq[INTERNAL].executeCount(sql);
|
|
55
|
+
return result.count > 0;
|
|
56
|
+
}
|
|
57
|
+
export async function executeUpsert(ctx, options) {
|
|
58
|
+
const dbCreate = ctx.relq[INTERNAL].transformToDbColumns(ctx.tableName, options.create);
|
|
59
|
+
const dbUpdate = ctx.relq[INTERNAL].transformToDbColumns(ctx.tableName, options.update);
|
|
60
|
+
const dbWhere = ctx.relq[INTERNAL].transformToDbColumns(ctx.tableName, options.where);
|
|
61
|
+
const conflictColumn = Object.keys(dbWhere)[0];
|
|
62
|
+
const builder = new InsertBuilder(ctx.tableName, dbCreate);
|
|
63
|
+
builder._onConflict(conflictColumn, cb => cb.doUpdate(dbUpdate));
|
|
64
|
+
builder.returning(['*']);
|
|
65
|
+
const sql = builder.toString();
|
|
66
|
+
const result = await ctx.relq[INTERNAL].executeQuery(sql);
|
|
67
|
+
const transformed = ctx.relq[INTERNAL].transformResultsFromDb(ctx.tableName, result.result.rows);
|
|
68
|
+
return transformed[0];
|
|
69
|
+
}
|
|
70
|
+
export async function executeSoftDelete(ctx, filter) {
|
|
71
|
+
const dbFilter = ctx.relq[INTERNAL].transformToDbColumns(ctx.tableName, filter);
|
|
72
|
+
const builder = new UpdateBuilder(ctx.tableName, { deleted_at: new Date() });
|
|
73
|
+
builder.where(q => {
|
|
74
|
+
for (const [col, val] of Object.entries(dbFilter)) {
|
|
75
|
+
q.equal(col, val);
|
|
76
|
+
}
|
|
77
|
+
return q;
|
|
78
|
+
});
|
|
79
|
+
builder.returning(['*']);
|
|
80
|
+
const sql = builder.toString();
|
|
81
|
+
const result = await ctx.relq[INTERNAL].executeQuery(sql);
|
|
82
|
+
if (result.result.rows.length === 0)
|
|
83
|
+
return null;
|
|
84
|
+
const transformed = ctx.relq[INTERNAL].transformResultsFromDb(ctx.tableName, result.result.rows);
|
|
85
|
+
return transformed[0];
|
|
86
|
+
}
|
|
87
|
+
export async function executeRestore(ctx, filter) {
|
|
88
|
+
const dbFilter = ctx.relq[INTERNAL].transformToDbColumns(ctx.tableName, filter);
|
|
89
|
+
const builder = new UpdateBuilder(ctx.tableName, { deleted_at: null });
|
|
90
|
+
builder.where(q => {
|
|
91
|
+
for (const [col, val] of Object.entries(dbFilter)) {
|
|
92
|
+
q.equal(col, val);
|
|
93
|
+
}
|
|
94
|
+
return q;
|
|
95
|
+
});
|
|
96
|
+
builder.returning(['*']);
|
|
97
|
+
const sql = builder.toString();
|
|
98
|
+
const result = await ctx.relq[INTERNAL].executeQuery(sql);
|
|
99
|
+
if (result.result.rows.length === 0)
|
|
100
|
+
return null;
|
|
101
|
+
const transformed = ctx.relq[INTERNAL].transformResultsFromDb(ctx.tableName, result.result.rows);
|
|
102
|
+
return transformed[0];
|
|
103
|
+
}
|
|
104
|
+
export async function executeCreateWith(ctx, parentData, related) {
|
|
105
|
+
const internal = ctx.relq[INTERNAL];
|
|
106
|
+
const schema = internal.getSchema();
|
|
107
|
+
const relations = internal.getRelations();
|
|
108
|
+
if (!schema || !relations) {
|
|
109
|
+
throw new Error('Cannot use createWith() without schema and relations config. ' +
|
|
110
|
+
'Use separate insert calls with a transaction instead.');
|
|
111
|
+
}
|
|
112
|
+
const parentSchemaKey = findSchemaKeyByTableName(schema, ctx.tableName) || ctx.tableName;
|
|
113
|
+
try {
|
|
114
|
+
await internal.executeRun('BEGIN');
|
|
115
|
+
const dbParent = internal.transformToDbColumns(ctx.tableName, parentData);
|
|
116
|
+
const parentBuilder = new InsertBuilder(ctx.tableName, dbParent);
|
|
117
|
+
parentBuilder.returning(['*']);
|
|
118
|
+
const parentSQL = parentBuilder.toString();
|
|
119
|
+
const parentResult = await internal.executeQuery(parentSQL);
|
|
120
|
+
if (!parentResult.result.rows || parentResult.result.rows.length === 0) {
|
|
121
|
+
throw new Error('Parent insert returned no rows');
|
|
122
|
+
}
|
|
123
|
+
const parentRow = parentResult.result.rows[0];
|
|
124
|
+
for (const [relationKey, childData] of Object.entries(related)) {
|
|
125
|
+
const childTableDef = schema[relationKey];
|
|
126
|
+
if (!childTableDef) {
|
|
127
|
+
throw new Error(`Unknown table "${relationKey}" in createWith(). Check your schema.`);
|
|
128
|
+
}
|
|
129
|
+
const childTableName = childTableDef.$name || relationKey;
|
|
130
|
+
const { resolveForeignKey } = await import("../../utils/fk-resolver.js");
|
|
131
|
+
const fk = resolveForeignKey(relations, schema, parentSchemaKey, relationKey);
|
|
132
|
+
if (!fk) {
|
|
133
|
+
throw new Error(`No FK relationship between "${parentSchemaKey}" and "${relationKey}". ` +
|
|
134
|
+
'Define the relationship in your relations config.');
|
|
135
|
+
}
|
|
136
|
+
let fkColumn;
|
|
137
|
+
let fkValue;
|
|
138
|
+
if (fk.direction === 'reverse') {
|
|
139
|
+
fkColumn = fk.toColumn;
|
|
140
|
+
fkValue = parentRow[fk.fromColumn];
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
fkColumn = fk.fromColumn;
|
|
144
|
+
fkValue = parentRow[fk.toColumn];
|
|
145
|
+
}
|
|
146
|
+
const rows = Array.isArray(childData) ? childData : [childData];
|
|
147
|
+
if (rows.length === 0)
|
|
148
|
+
continue;
|
|
149
|
+
const firstRow = { ...rows[0], [fkColumn]: fkValue };
|
|
150
|
+
const dbFirst = internal.transformToDbColumns(childTableName, firstRow);
|
|
151
|
+
const childBuilder = new InsertBuilder(childTableName, dbFirst);
|
|
152
|
+
for (let i = 1; i < rows.length; i++) {
|
|
153
|
+
const row = { ...rows[i], [fkColumn]: fkValue };
|
|
154
|
+
const dbRow = internal.transformToDbColumns(childTableName, row);
|
|
155
|
+
childBuilder.addRow(dbRow);
|
|
156
|
+
}
|
|
157
|
+
const childSQL = childBuilder.toString();
|
|
158
|
+
await internal.executeQuery(childSQL);
|
|
159
|
+
}
|
|
160
|
+
await internal.executeRun('COMMIT');
|
|
161
|
+
const transformed = internal.transformResultsFromDb(ctx.tableName, [parentRow]);
|
|
162
|
+
return transformed[0];
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
try {
|
|
166
|
+
await internal.executeRun('ROLLBACK');
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
}
|
|
170
|
+
throw error;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
function findSchemaKeyByTableName(schema, tableName) {
|
|
174
|
+
for (const [key, def] of Object.entries(schema)) {
|
|
175
|
+
if (def?.$name === tableName || key === tableName) {
|
|
176
|
+
return key;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
export function getPrimaryKeyColumn(relq, tableName) {
|
|
182
|
+
const schema = relq.schema;
|
|
183
|
+
if (schema && schema[tableName]) {
|
|
184
|
+
const tableSchema = schema[tableName];
|
|
185
|
+
for (const [colName, colDef] of Object.entries(tableSchema)) {
|
|
186
|
+
if (colDef && typeof colDef === 'object' &&
|
|
187
|
+
('$primaryKey' in colDef ||
|
|
188
|
+
colDef.config?.$primaryKey === true)) {
|
|
189
|
+
return colName;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return 'id';
|
|
194
|
+
}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { createTableProxy } from "../../select/table-proxy.js";
|
|
2
|
+
import { JoinConditionBuilder } from "../../select/join-condition-builder.js";
|
|
3
|
+
import { JOIN_INTERNAL, JOIN_SETUP } from "../../select/join-internals.js";
|
|
4
|
+
import { JoinManyConditionBuilder } from "../../select/join-many-condition-builder.js";
|
|
5
|
+
import { RelqQueryError } from "../../../shared/errors/relq-errors.js";
|
|
6
|
+
import { resolveForeignKey } from "../../utils/fk-resolver.js";
|
|
7
|
+
import { INTERNAL } from "./methods.js";
|
|
8
|
+
export function executeTypeSafeJoin(ctx, joinType, tableOrAlias, callback) {
|
|
9
|
+
const [rightTableKey, rightAlias] = Array.isArray(tableOrAlias)
|
|
10
|
+
? tableOrAlias
|
|
11
|
+
: [tableOrAlias, tableOrAlias];
|
|
12
|
+
const internal = ctx.relq[INTERNAL];
|
|
13
|
+
const schema = internal.getSchema();
|
|
14
|
+
const relations = internal.getRelations();
|
|
15
|
+
const leftTableDef = internal.getTableDef(ctx.schemaKey || ctx.tableName);
|
|
16
|
+
const rightTableDef = internal.getTableDef(rightTableKey);
|
|
17
|
+
const leftTableName = leftTableDef?.$name || ctx.tableName;
|
|
18
|
+
const rightTableName = rightTableDef?.$name || rightTableKey;
|
|
19
|
+
const leftAlias = ctx.builder.getTableIdentifier();
|
|
20
|
+
const leftProxy = createTableProxy(leftTableName, leftAlias, leftTableDef);
|
|
21
|
+
const rightProxy = createTableProxy(rightTableName, rightAlias, rightTableDef);
|
|
22
|
+
const conditionBuilder = new JoinConditionBuilder();
|
|
23
|
+
if (callback) {
|
|
24
|
+
if (joinType === 'RIGHT JOIN') {
|
|
25
|
+
callback(conditionBuilder, leftProxy, rightProxy);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
callback(conditionBuilder, rightProxy, leftProxy);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const conditionInternals = conditionBuilder[JOIN_INTERNAL];
|
|
32
|
+
if (!conditionInternals.hasConditions() && schema && relations) {
|
|
33
|
+
const fkResolution = resolveForeignKey(relations, schema, ctx.schemaKey || ctx.tableName, rightTableKey);
|
|
34
|
+
if (fkResolution) {
|
|
35
|
+
const leftCol = leftProxy[fkResolution.fromColumn];
|
|
36
|
+
const rightCol = rightProxy[fkResolution.toColumn];
|
|
37
|
+
conditionBuilder.equal(leftCol, rightCol);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
throw new RelqQueryError(`Cannot auto-resolve FK relationship between "${ctx.schemaKey || ctx.tableName}" and "${rightTableKey}". ` +
|
|
41
|
+
`Either provide a callback with explicit join conditions, or define the relationship in your relations config.`, { hint: `Use .join('${rightTableKey}', (on, ${rightTableKey}, source) => on.equal(${rightTableKey}.id, source.columnName))` });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const selectedProps = conditionInternals.getSelectedColumns();
|
|
45
|
+
let selectColumns;
|
|
46
|
+
const toSnake = (s) => s.replace(/[A-Z]/g, l => `_${l.toLowerCase()}`);
|
|
47
|
+
if (selectedProps && selectedProps.length > 0) {
|
|
48
|
+
const rightColumns = rightTableDef?.$columns || rightTableDef;
|
|
49
|
+
selectColumns = selectedProps.map(prop => {
|
|
50
|
+
const columnDef = rightColumns?.[prop];
|
|
51
|
+
const sqlName = columnDef?.$columnName || toSnake(prop);
|
|
52
|
+
return { property: prop, sqlName };
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
else if (rightTableDef) {
|
|
56
|
+
const rightColumns = rightTableDef.$columns || rightTableDef;
|
|
57
|
+
selectColumns = Object.entries(rightColumns)
|
|
58
|
+
.filter(([_, colDef]) => colDef && typeof colDef === 'object' && '$type' in colDef)
|
|
59
|
+
.map(([propName, colDef]) => ({
|
|
60
|
+
property: propName,
|
|
61
|
+
sqlName: colDef.$columnName || toSnake(propName),
|
|
62
|
+
}));
|
|
63
|
+
}
|
|
64
|
+
const joinClause = {
|
|
65
|
+
type: joinType,
|
|
66
|
+
table: rightTableName,
|
|
67
|
+
alias: rightAlias,
|
|
68
|
+
schemaKey: rightTableKey,
|
|
69
|
+
onClause: conditionInternals.toSQL() || undefined,
|
|
70
|
+
usingColumns: conditionInternals.getUsingColumns() || undefined,
|
|
71
|
+
selectColumns
|
|
72
|
+
};
|
|
73
|
+
ctx.builder.addStructuredJoin(joinClause);
|
|
74
|
+
}
|
|
75
|
+
export function executeTypeSafeJoinMany(ctx, joinType, tableOrAlias, callback) {
|
|
76
|
+
const [rightTableKey, rightAlias] = Array.isArray(tableOrAlias)
|
|
77
|
+
? tableOrAlias
|
|
78
|
+
: [tableOrAlias, tableOrAlias];
|
|
79
|
+
const internal = ctx.relq[INTERNAL];
|
|
80
|
+
const leftTableDef = internal.getTableDef(ctx.schemaKey || ctx.tableName);
|
|
81
|
+
const rightTableDef = internal.getTableDef(rightTableKey);
|
|
82
|
+
const leftTableName = leftTableDef?.$name || ctx.tableName;
|
|
83
|
+
const rightTableName = rightTableDef?.$name || rightTableKey;
|
|
84
|
+
const leftAlias = ctx.builder.getTableIdentifier();
|
|
85
|
+
const leftProxy = createTableProxy(leftTableName, leftAlias, leftTableDef);
|
|
86
|
+
const rightProxy = createTableProxy(rightTableName, rightAlias, rightTableDef);
|
|
87
|
+
const conditionBuilder = new JoinManyConditionBuilder();
|
|
88
|
+
const proxyCreator = (tableKey, alias) => {
|
|
89
|
+
const tableDef = internal.getTableDef(tableKey);
|
|
90
|
+
const sqlTableName = tableDef?.$name || tableKey;
|
|
91
|
+
return {
|
|
92
|
+
proxy: createTableProxy(sqlTableName, alias, tableDef),
|
|
93
|
+
tableName: sqlTableName
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
conditionBuilder[JOIN_SETUP](proxyCreator, rightProxy);
|
|
97
|
+
callback(conditionBuilder, rightProxy, leftProxy);
|
|
98
|
+
const lateralSQL = buildLateralSubquery(rightTableName, rightAlias, conditionBuilder, rightTableDef);
|
|
99
|
+
const lateralJoinType = joinType === 'LEFT JOIN' ? 'LEFT JOIN LATERAL' : 'JOIN LATERAL';
|
|
100
|
+
const joinClause = {
|
|
101
|
+
type: lateralJoinType,
|
|
102
|
+
table: rightTableName,
|
|
103
|
+
alias: rightAlias,
|
|
104
|
+
schemaKey: rightTableKey,
|
|
105
|
+
lateralSubquery: lateralSQL
|
|
106
|
+
};
|
|
107
|
+
ctx.builder.addStructuredJoin(joinClause);
|
|
108
|
+
}
|
|
109
|
+
function buildLateralSubquery(tableName, alias, builder, tableDef) {
|
|
110
|
+
const parts = [];
|
|
111
|
+
parts.push('(SELECT');
|
|
112
|
+
parts.push(`COALESCE(jsonb_agg(row_to_json(sub.*)), '[]'::jsonb) AS ${alias}`);
|
|
113
|
+
parts.push('FROM (');
|
|
114
|
+
const internals = builder[JOIN_INTERNAL];
|
|
115
|
+
parts.push('SELECT');
|
|
116
|
+
const selectedProps = internals.getSelectedColumns();
|
|
117
|
+
const innerJoins = internals.getInnerJoins();
|
|
118
|
+
const hasInnerJoins = innerJoins.length > 0;
|
|
119
|
+
const toSnake = (s) => s.replace(/[A-Z]/g, l => `_${l.toLowerCase()}`);
|
|
120
|
+
if (selectedProps && selectedProps.length > 0) {
|
|
121
|
+
const tableColumns = tableDef?.$columns || tableDef;
|
|
122
|
+
const selectCols = selectedProps.map(prop => {
|
|
123
|
+
const columnDef = tableColumns?.[prop];
|
|
124
|
+
if (columnDef || !hasInnerJoins) {
|
|
125
|
+
const sqlName = columnDef?.$columnName || toSnake(prop);
|
|
126
|
+
return `"${alias}"."${sqlName}" AS "${prop}"`;
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
const sqlName = toSnake(prop);
|
|
130
|
+
return `"${sqlName}" AS "${prop}"`;
|
|
131
|
+
}
|
|
132
|
+
}).join(', ');
|
|
133
|
+
parts.push(selectCols);
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
parts.push(`"${alias}".*`);
|
|
137
|
+
}
|
|
138
|
+
parts.push(`FROM "${tableName}" AS "${alias}"`);
|
|
139
|
+
for (const join of innerJoins) {
|
|
140
|
+
parts.push(`${join.type} "${join.table}" AS "${join.alias}" ON ${join.onClause}`);
|
|
141
|
+
}
|
|
142
|
+
const whereSQL = internals.toWhereSQL();
|
|
143
|
+
if (whereSQL) {
|
|
144
|
+
parts.push(`WHERE ${whereSQL}`);
|
|
145
|
+
}
|
|
146
|
+
const orderBySQL = internals.toOrderBySQL();
|
|
147
|
+
if (orderBySQL) {
|
|
148
|
+
parts.push(`ORDER BY ${orderBySQL}`);
|
|
149
|
+
}
|
|
150
|
+
const limitSQL = internals.toLimitSQL();
|
|
151
|
+
if (limitSQL) {
|
|
152
|
+
parts.push(limitSQL);
|
|
153
|
+
}
|
|
154
|
+
const offsetSQL = internals.toOffsetSQL();
|
|
155
|
+
if (offsetSQL) {
|
|
156
|
+
parts.push(offsetSQL);
|
|
157
|
+
}
|
|
158
|
+
parts.push(') sub');
|
|
159
|
+
parts.push(`) AS "${alias}_lateral"`);
|
|
160
|
+
return parts.join(' ');
|
|
161
|
+
}
|
|
162
|
+
export function executeTypeSafeJoinManyThrough(ctx, joinType, targetTableOrAlias, junctionTableKey, callback) {
|
|
163
|
+
const [targetKey, targetAlias] = Array.isArray(targetTableOrAlias)
|
|
164
|
+
? targetTableOrAlias
|
|
165
|
+
: [targetTableOrAlias, targetTableOrAlias];
|
|
166
|
+
const internal = ctx.relq[INTERNAL];
|
|
167
|
+
const schema = internal.getSchema();
|
|
168
|
+
const relations = internal.getRelations();
|
|
169
|
+
if (!schema || !relations) {
|
|
170
|
+
throw new RelqQueryError(`Cannot use { through } without schema and relations config.`, { hint: `Define relations in your schema to use the { through } pattern, or use the callback form of joinMany instead.` });
|
|
171
|
+
}
|
|
172
|
+
const leftTableDef = internal.getTableDef(ctx.schemaKey || ctx.tableName);
|
|
173
|
+
const junctionTableDef = internal.getTableDef(junctionTableKey);
|
|
174
|
+
const targetTableDef = internal.getTableDef(targetKey);
|
|
175
|
+
const leftTableName = leftTableDef?.$name || ctx.tableName;
|
|
176
|
+
const junctionTableName = junctionTableDef?.$name || junctionTableKey;
|
|
177
|
+
const targetTableName = targetTableDef?.$name || targetKey;
|
|
178
|
+
const leftAlias = ctx.builder.getTableIdentifier();
|
|
179
|
+
const leftToJunction = resolveForeignKey(relations, schema, ctx.schemaKey || ctx.tableName, junctionTableKey);
|
|
180
|
+
if (!leftToJunction) {
|
|
181
|
+
throw new RelqQueryError(`Cannot resolve FK between "${ctx.schemaKey || ctx.tableName}" and junction table "${junctionTableKey}".`, { hint: `Define a foreign key relationship between these tables in your relations config, or use the callback form of joinMany instead.` });
|
|
182
|
+
}
|
|
183
|
+
const junctionToTarget = resolveForeignKey(relations, schema, junctionTableKey, targetKey);
|
|
184
|
+
if (!junctionToTarget) {
|
|
185
|
+
throw new RelqQueryError(`Cannot resolve FK between junction table "${junctionTableKey}" and target table "${targetKey}".`, { hint: `Define a foreign key relationship between these tables in your relations config, or use the callback form of joinMany instead.` });
|
|
186
|
+
}
|
|
187
|
+
const conditionBuilder = new JoinManyConditionBuilder();
|
|
188
|
+
if (callback) {
|
|
189
|
+
callback(conditionBuilder);
|
|
190
|
+
}
|
|
191
|
+
const lateralSQL = buildThroughLateralSubquery(junctionTableName, junctionTableKey, targetTableName, targetAlias, leftAlias, leftToJunction, junctionToTarget, conditionBuilder, targetTableDef);
|
|
192
|
+
const lateralJoinType = joinType === 'LEFT JOIN' ? 'LEFT JOIN LATERAL' : 'JOIN LATERAL';
|
|
193
|
+
const joinClause = {
|
|
194
|
+
type: lateralJoinType,
|
|
195
|
+
table: targetTableName,
|
|
196
|
+
alias: targetAlias,
|
|
197
|
+
schemaKey: targetKey,
|
|
198
|
+
lateralSubquery: lateralSQL
|
|
199
|
+
};
|
|
200
|
+
ctx.builder.addStructuredJoin(joinClause);
|
|
201
|
+
}
|
|
202
|
+
function buildThroughLateralSubquery(junctionTableName, junctionAlias, targetTableName, targetAlias, leftAlias, leftToJunction, junctionToTarget, builder, targetTableDef) {
|
|
203
|
+
const parts = [];
|
|
204
|
+
const internals = builder[JOIN_INTERNAL];
|
|
205
|
+
const toSnake = (s) => s.replace(/[A-Z]/g, l => `_${l.toLowerCase()}`);
|
|
206
|
+
parts.push('(SELECT');
|
|
207
|
+
parts.push(`COALESCE(jsonb_agg(row_to_json(sub.*)), '[]'::jsonb) AS ${targetAlias}`);
|
|
208
|
+
parts.push('FROM (');
|
|
209
|
+
parts.push('SELECT');
|
|
210
|
+
const selectedProps = internals.getSelectedColumns();
|
|
211
|
+
if (selectedProps && selectedProps.length > 0) {
|
|
212
|
+
const tableColumns = targetTableDef?.$columns || targetTableDef;
|
|
213
|
+
const selectCols = selectedProps.map(prop => {
|
|
214
|
+
const columnDef = tableColumns?.[prop];
|
|
215
|
+
const sqlName = columnDef?.$columnName || toSnake(prop);
|
|
216
|
+
return `"${targetAlias}"."${sqlName}" AS "${prop}"`;
|
|
217
|
+
}).join(', ');
|
|
218
|
+
parts.push(selectCols);
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
parts.push(`"${targetAlias}".*`);
|
|
222
|
+
}
|
|
223
|
+
parts.push(`FROM "${junctionTableName}" AS "${junctionAlias}"`);
|
|
224
|
+
parts.push(`JOIN "${targetTableName}" AS "${targetAlias}" ON "${junctionAlias}"."${junctionToTarget.fromColumn}" = "${targetAlias}"."${junctionToTarget.toColumn}"`);
|
|
225
|
+
let whereSQL = `"${junctionAlias}"."${leftToJunction.toColumn}" = "${leftAlias}"."${leftToJunction.fromColumn}"`;
|
|
226
|
+
const userWhereSQL = internals.toWhereSQL();
|
|
227
|
+
if (userWhereSQL) {
|
|
228
|
+
whereSQL += ` AND ${userWhereSQL}`;
|
|
229
|
+
}
|
|
230
|
+
parts.push(`WHERE ${whereSQL}`);
|
|
231
|
+
const orderBySQL = internals.toOrderBySQL();
|
|
232
|
+
if (orderBySQL) {
|
|
233
|
+
parts.push(`ORDER BY ${orderBySQL}`);
|
|
234
|
+
}
|
|
235
|
+
const limitSQL = internals.toLimitSQL();
|
|
236
|
+
if (limitSQL) {
|
|
237
|
+
parts.push(limitSQL);
|
|
238
|
+
}
|
|
239
|
+
const offsetSQL = internals.toOffsetSQL();
|
|
240
|
+
if (offsetSQL) {
|
|
241
|
+
parts.push(offsetSQL);
|
|
242
|
+
}
|
|
243
|
+
parts.push(') sub');
|
|
244
|
+
parts.push(`) AS "${targetAlias}_lateral"`);
|
|
245
|
+
return parts.join(' ');
|
|
246
|
+
}
|