linkgress-orm 0.4.56 → 0.4.57
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/entity/db-context.d.ts +133 -4
- package/dist/entity/db-context.d.ts.map +1 -1
- package/dist/entity/db-context.js +325 -4
- package/dist/entity/db-context.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/query/conditions.d.ts +41 -0
- package/dist/query/conditions.d.ts.map +1 -1
- package/dist/query/conditions.js +48 -0
- package/dist/query/conditions.js.map +1 -1
- package/dist/query/mutation-batch.d.ts +91 -2
- package/dist/query/mutation-batch.d.ts.map +1 -1
- package/dist/query/mutation-batch.js +166 -4
- package/dist/query/mutation-batch.js.map +1 -1
- package/dist/query/query-builder.d.ts +6 -0
- package/dist/query/query-builder.d.ts.map +1 -1
- package/dist/query/query-builder.js +40 -8
- package/dist/query/query-builder.js.map +1 -1
- package/package.json +1 -1
|
@@ -2208,6 +2208,184 @@ ${extraJoins.join('\n')}${orderBy}`;
|
|
|
2208
2208
|
});
|
|
2209
2209
|
return { parent: parentRow, children: mapChildren(strippedRows) };
|
|
2210
2210
|
}
|
|
2211
|
+
/**
|
|
2212
|
+
* Bulk sibling of {@link insertWithChildren}: N parent rows plus their child rows in
|
|
2213
|
+
* ONE statement (a task-DAG persist — N task rows + one audit row each — used to
|
|
2214
|
+
* cost two). Parent identity mapping rides the SAME serial-ascend guarantee the
|
|
2215
|
+
* single-parent variant documents: the parent leg inserts `ORDER BY` an input
|
|
2216
|
+
* ordinal, so generated serial ids ascend in input-row order, and a
|
|
2217
|
+
* `row_number() OVER (ORDER BY pk)` CTE recovers each parent's input index for the
|
|
2218
|
+
* child join:
|
|
2219
|
+
*
|
|
2220
|
+
* WITH "__ibwc_parent__" AS (
|
|
2221
|
+
* INSERT INTO parent (cols)
|
|
2222
|
+
* SELECT cols FROM (VALUES (0, ...), (1, ...)) v("__ibwc_ord", cols)
|
|
2223
|
+
* ORDER BY v."__ibwc_ord" RETURNING *
|
|
2224
|
+
* ), "__ibwc_pord__" AS (
|
|
2225
|
+
* SELECT p.*, row_number() OVER (ORDER BY p."pk") - 1 AS "__ibwc_ord"
|
|
2226
|
+
* FROM "__ibwc_parent__" p
|
|
2227
|
+
* ), "__mutation__" AS (
|
|
2228
|
+
* INSERT INTO child ("fk", cols)
|
|
2229
|
+
* SELECT p."pk", v.cols
|
|
2230
|
+
* FROM (VALUES (0, pix, ...), ...) v("__ibwc_cord", "__ibwc_pix", cols)
|
|
2231
|
+
* JOIN "__ibwc_pord__" p ON p."__ibwc_ord" = v."__ibwc_pix"
|
|
2232
|
+
* ORDER BY v."__ibwc_cord" RETURNING <child cols>, "childPk"
|
|
2233
|
+
* )
|
|
2234
|
+
* SELECT "__mutation__".*, <parent cols + ordinal via the fk join>
|
|
2235
|
+
* FROM "__mutation__" JOIN "__ibwc_pord__" ON fk = pk ORDER BY child pk
|
|
2236
|
+
*
|
|
2237
|
+
* Single-statement atomicity: a failing leg rolls back both inserts. Parents come
|
|
2238
|
+
* back in input order; children in child-input order.
|
|
2239
|
+
*
|
|
2240
|
+
* v1 restrictions: FLAT returning selectors on BOTH sides (no navigation
|
|
2241
|
+
* projections); single-column auto/serial primary keys on both tables; no
|
|
2242
|
+
* `unlessExists` guard; child rows must NOT carry the foreign-key property; EVERY
|
|
2243
|
+
* parent must be referenced by at least one child (parents are returned through
|
|
2244
|
+
* the child join — a childless parent would insert but vanish from the result, so
|
|
2245
|
+
* it is rejected up front; use plain `insertBulk` for childless rows); the whole
|
|
2246
|
+
* shape must fit one statement (no chunking).
|
|
2247
|
+
*/
|
|
2248
|
+
insertBulkWithChildren(config) {
|
|
2249
|
+
const { rows: childRows, foreignKey } = config.children;
|
|
2250
|
+
if (config.rows.length === 0) {
|
|
2251
|
+
throw new Error('insertBulkWithChildren: rows must be non-empty');
|
|
2252
|
+
}
|
|
2253
|
+
if (childRows.length === 0) {
|
|
2254
|
+
throw new Error('insertBulkWithChildren: children.rows must be non-empty — use insertBulk for childless parents');
|
|
2255
|
+
}
|
|
2256
|
+
const referenced = new Set();
|
|
2257
|
+
for (let i = 0; i < childRows.length; i++) {
|
|
2258
|
+
const { parentIndex, row } = childRows[i];
|
|
2259
|
+
if (!Number.isInteger(parentIndex) || parentIndex < 0 || parentIndex >= config.rows.length) {
|
|
2260
|
+
throw new Error(`insertBulkWithChildren: child row at index ${i} has parentIndex ${parentIndex} outside 0..${config.rows.length - 1}`);
|
|
2261
|
+
}
|
|
2262
|
+
if (row[foreignKey] !== undefined) {
|
|
2263
|
+
throw new Error(`insertBulkWithChildren: child row at index ${i} carries the foreign-key property "${foreignKey}" — it is sourced from its inserted parent`);
|
|
2264
|
+
}
|
|
2265
|
+
referenced.add(parentIndex);
|
|
2266
|
+
}
|
|
2267
|
+
for (let i = 0; i < config.rows.length; i++) {
|
|
2268
|
+
if (!referenced.has(i)) {
|
|
2269
|
+
throw new Error(`insertBulkWithChildren: parent at index ${i} has no child row — parents return through the child join (v1); insert childless rows via insertBulk`);
|
|
2270
|
+
}
|
|
2271
|
+
}
|
|
2272
|
+
const columnCount = Math.max(1, Object.keys(childRows[0].row).length + 2);
|
|
2273
|
+
const singleStatementLimit = Math.floor(Math.floor(65535 / columnCount) * 0.6);
|
|
2274
|
+
if (childRows.length + config.rows.length > singleStatementLimit) {
|
|
2275
|
+
throw new Error(`insertBulkWithChildren: ${config.rows.length} parents + ${childRows.length} children exceed the ~${singleStatementLimit}-row single-statement budget`);
|
|
2276
|
+
}
|
|
2277
|
+
return this.executeInsertBulkWithChildren(config);
|
|
2278
|
+
}
|
|
2279
|
+
/** @internal Async body of {@link insertBulkWithChildren} (validation stays synchronous). */
|
|
2280
|
+
async executeInsertBulkWithChildren(config) {
|
|
2281
|
+
const parentSchema = this._getSchema();
|
|
2282
|
+
const executor = this._getExecutor();
|
|
2283
|
+
const client = this._getClient();
|
|
2284
|
+
const childTable = config.children.table;
|
|
2285
|
+
if (childTable._getClient() !== client || childTable._getExecutor() !== executor) {
|
|
2286
|
+
throw new Error('insertBulkWithChildren: the child table uses a different database client or transaction than the parent — both must share one connection context');
|
|
2287
|
+
}
|
|
2288
|
+
const pkEntries = Object.entries(parentSchema.columns).filter(([, colBuilder]) => colBuilder.build().primaryKey);
|
|
2289
|
+
if (pkEntries.length !== 1) {
|
|
2290
|
+
throw new Error('insertBulkWithChildren requires a single-column parent primary key');
|
|
2291
|
+
}
|
|
2292
|
+
const parentPkDbName = pkEntries[0][1].build().name;
|
|
2293
|
+
// ---- parent leg: ordinal-ordered VALUES so serial ids ascend in input order ----
|
|
2294
|
+
const parentCompiled = this.compileValuesWithCasts(parentSchema, config.rows, null);
|
|
2295
|
+
const parentColNames = parentCompiled.columns.map(c => `"${c.dbName}"`).join(', ');
|
|
2296
|
+
const parentSelectCols = parentCompiled.columns.map(c => `v."${c.dbName}"`).join(', ');
|
|
2297
|
+
const parentValueRows = parentCompiled.valueRows.map((row, ix) => `(${ix}, ${row})`).join(', ');
|
|
2298
|
+
const params = [...parentCompiled.params];
|
|
2299
|
+
const parentMock = this.createMockEntity();
|
|
2300
|
+
const parentSelection = config.returning.parents(parentMock);
|
|
2301
|
+
const parentSelCols = [];
|
|
2302
|
+
for (const [prop, field] of Object.entries(parentSelection)) {
|
|
2303
|
+
const tableAlias = field?.__tableAlias;
|
|
2304
|
+
const dbColumnName = field?.__dbColumnName;
|
|
2305
|
+
if (dbColumnName == null || (tableAlias && tableAlias !== parentSchema.name)) {
|
|
2306
|
+
throw new Error(`insertBulkWithChildren: parents returning supports flat parent columns only — "${prop}" is not one`);
|
|
2307
|
+
}
|
|
2308
|
+
const colEntry = Object.entries(parentSchema.columns).find(([, colBuilder]) => colBuilder.build().name === dbColumnName);
|
|
2309
|
+
parentSelCols.push({ prop, dbName: dbColumnName, mapper: colEntry ? colEntry[1].build().mapper : undefined });
|
|
2310
|
+
}
|
|
2311
|
+
const parentSql = `INSERT INTO ${this._getQualifiedTableName()} (${parentColNames})
|
|
2312
|
+
SELECT ${parentSelectCols} FROM (VALUES ${parentValueRows}) AS v("__ibwc_ord", ${parentColNames})
|
|
2313
|
+
ORDER BY v."__ibwc_ord"
|
|
2314
|
+
RETURNING *`;
|
|
2315
|
+
// ---- child leg: (childOrd, parentIx, cells) VALUES joined to the ordinal CTE ----
|
|
2316
|
+
const childSchema = childTable._getSchema();
|
|
2317
|
+
const fkColBuilder = childSchema.columns[config.children.foreignKey];
|
|
2318
|
+
if (!fkColBuilder) {
|
|
2319
|
+
throw new Error(`insertBulkWithChildren: unknown child foreign-key property "${config.children.foreignKey}"`);
|
|
2320
|
+
}
|
|
2321
|
+
const fkDbName = fkColBuilder.build().name;
|
|
2322
|
+
const childCompiled = childTable.compileValuesWithCasts(childSchema, config.children.rows.map(r => r.row), config.children.foreignKey);
|
|
2323
|
+
const childColNames = childCompiled.columns.map(c => `"${c.dbName}"`).join(', ');
|
|
2324
|
+
const childSelectCols = childCompiled.columns.map(c => `v."${c.dbName}"`).join(', ');
|
|
2325
|
+
const childValueRows = childCompiled.valueRows.map((row, ix) => `(${ix}, ${config.children.rows[ix].parentIndex}, ${row})`).join(', ');
|
|
2326
|
+
const childOffset = params.length;
|
|
2327
|
+
let childSql = `INSERT INTO ${childTable._getQualifiedTableName()} ("${fkDbName}", ${childColNames})
|
|
2328
|
+
SELECT p."${parentPkDbName}", ${childSelectCols}
|
|
2329
|
+
FROM (VALUES ${childValueRows}) AS v("__ibwc_cord", "__ibwc_pix", ${childColNames})
|
|
2330
|
+
JOIN "__ibwc_pord__" p ON p."__ibwc_ord" = v."__ibwc_pix"
|
|
2331
|
+
ORDER BY v."__ibwc_cord"`;
|
|
2332
|
+
childSql = childOffset === 0 ? childSql : (0, sql_utils_1.renumberPlaceholders)(childSql, childOffset);
|
|
2333
|
+
params.push(...childCompiled.params);
|
|
2334
|
+
// ---- assembly: flat child returning + parent cols through the fk join ----
|
|
2335
|
+
if (childTable.detectNavigationInReturning(config.returning.children)) {
|
|
2336
|
+
throw new Error('insertBulkWithChildren: children returning supports flat child columns only (v1)');
|
|
2337
|
+
}
|
|
2338
|
+
const childPkEntries = Object.entries(childSchema.columns).filter(([, colBuilder]) => colBuilder.build().primaryKey);
|
|
2339
|
+
if (childPkEntries.length !== 1) {
|
|
2340
|
+
throw new Error('insertBulkWithChildren requires a single-column child primary key');
|
|
2341
|
+
}
|
|
2342
|
+
const childPkDbName = childPkEntries[0][1].build().name;
|
|
2343
|
+
const returningClause = childTable.buildReturningClause(config.returning.children);
|
|
2344
|
+
const parentJoinSelects = [
|
|
2345
|
+
`"__ibwc_pj__"."__ibwc_ord" AS "__ibwc_parent__.__ord"`,
|
|
2346
|
+
...parentSelCols.map(c => `"__ibwc_pj__"."${c.dbName}" AS "__ibwc_parent__.${c.prop}"`),
|
|
2347
|
+
];
|
|
2348
|
+
const sql = `WITH "__ibwc_parent__" AS (
|
|
2349
|
+
${parentSql}
|
|
2350
|
+
),
|
|
2351
|
+
"__ibwc_pord__" AS (
|
|
2352
|
+
SELECT p.*, row_number() OVER (ORDER BY p."${parentPkDbName}") - 1 AS "__ibwc_ord"
|
|
2353
|
+
FROM "__ibwc_parent__" p
|
|
2354
|
+
),
|
|
2355
|
+
"__mutation__" AS (
|
|
2356
|
+
${childSql}
|
|
2357
|
+
RETURNING ${returningClause.sql}, "${fkDbName}" AS "__ibwc_child_fk__", "${childPkDbName}" AS "__ibwc_child_pk__"
|
|
2358
|
+
)
|
|
2359
|
+
SELECT "__mutation__".*, ${parentJoinSelects.join(', ')}
|
|
2360
|
+
FROM "__mutation__"
|
|
2361
|
+
JOIN "__ibwc_pord__" "__ibwc_pj__" ON "__ibwc_pj__"."${parentPkDbName}" = "__mutation__"."__ibwc_child_fk__"
|
|
2362
|
+
ORDER BY "__mutation__"."__ibwc_child_pk__"`;
|
|
2363
|
+
const result = executor ? await executor.query(sql, params) : await client.query(sql, params);
|
|
2364
|
+
const rawRows = result.rows;
|
|
2365
|
+
const parentsByOrd = new Map();
|
|
2366
|
+
const strippedRows = [];
|
|
2367
|
+
for (const row of rawRows) {
|
|
2368
|
+
const ord = Number(row['__ibwc_parent__.__ord']);
|
|
2369
|
+
if (!parentsByOrd.has(ord)) {
|
|
2370
|
+
const parentRow = {};
|
|
2371
|
+
for (const col of parentSelCols) {
|
|
2372
|
+
const raw = row[`__ibwc_parent__.${col.prop}`];
|
|
2373
|
+
parentRow[col.prop] = col.mapper ? col.mapper.fromDriver(raw) : raw;
|
|
2374
|
+
}
|
|
2375
|
+
parentsByOrd.set(ord, parentRow);
|
|
2376
|
+
}
|
|
2377
|
+
const clean = {};
|
|
2378
|
+
for (const [key, value] of Object.entries(row)) {
|
|
2379
|
+
if (!key.startsWith('__ibwc_parent__.') && key !== '__ibwc_child_fk__' && key !== '__ibwc_child_pk__') {
|
|
2380
|
+
clean[key] = value;
|
|
2381
|
+
}
|
|
2382
|
+
}
|
|
2383
|
+
strippedRows.push(clean);
|
|
2384
|
+
}
|
|
2385
|
+
const parents = [...parentsByOrd.entries()].sort((a, b) => a[0] - b[0]).map(([, parentRow]) => parentRow);
|
|
2386
|
+
const children = childTable.mapReturningResults(strippedRows, returningClause.aliasToProperty);
|
|
2387
|
+
return { parents, children };
|
|
2388
|
+
}
|
|
2211
2389
|
/**
|
|
2212
2390
|
* Compile rows into a cast-annotated VALUES fragment (`$n::type` / `NULL::type`
|
|
2213
2391
|
* per cell — the bulkUpdate technique, so a bare `VALUES` source keeps correct
|
|
@@ -2377,6 +2555,32 @@ ${extraJoins.join('\n')}${orderBy}`;
|
|
|
2377
2555
|
}
|
|
2378
2556
|
return { sql, params };
|
|
2379
2557
|
}
|
|
2558
|
+
/**
|
|
2559
|
+
* Builds a bare `DELETE FROM t WHERE "col" IN ($1, …)` statement (no
|
|
2560
|
+
* RETURNING clause) so `MutationBatch` can compose it as a data-modifying
|
|
2561
|
+
* CTE leg. Each value runs through the column's toDriver mapper — the same
|
|
2562
|
+
* fidelity rule the insert/update legs follow. Returns null for an empty
|
|
2563
|
+
* values array (mirroring the other leg builders' empty-input semantics);
|
|
2564
|
+
* an unknown column property throws at build time.
|
|
2565
|
+
* @internal
|
|
2566
|
+
*/
|
|
2567
|
+
_buildDeleteWhereInStatement(field, values) {
|
|
2568
|
+
if (values.length === 0) {
|
|
2569
|
+
return null;
|
|
2570
|
+
}
|
|
2571
|
+
const schema = this._getSchema();
|
|
2572
|
+
const colBuilder = schema.columns[field];
|
|
2573
|
+
if (!colBuilder) {
|
|
2574
|
+
throw new Error(`deleteWhereIn: unknown column property "${field}" on entity "${schema.name}"`);
|
|
2575
|
+
}
|
|
2576
|
+
const config = colBuilder.build();
|
|
2577
|
+
const params = values.map(value => (config.mapper ? config.mapper.toDriver(value) : value));
|
|
2578
|
+
const placeholders = params.map((_, ix) => `$${ix + 1}`).join(', ');
|
|
2579
|
+
return {
|
|
2580
|
+
sql: `DELETE FROM ${this._getQualifiedTableName()} WHERE "${config.name}" IN (${placeholders})`,
|
|
2581
|
+
params,
|
|
2582
|
+
};
|
|
2583
|
+
}
|
|
2380
2584
|
/**
|
|
2381
2585
|
* Upsert with advanced configuration
|
|
2382
2586
|
* Auto-detects primary keys and supports chunking
|
|
@@ -2494,13 +2698,13 @@ ${extraJoins.join('\n')}${orderBy}`;
|
|
|
2494
2698
|
};
|
|
2495
2699
|
}
|
|
2496
2700
|
/**
|
|
2497
|
-
*
|
|
2701
|
+
* The bare `INSERT .. ON CONFLICT` assembly (no RETURNING) shared by
|
|
2702
|
+
* {@link upsertBulkSingle} and the MutationBatch upsert-leg compile —
|
|
2703
|
+
* one definition so the two can never drift.
|
|
2498
2704
|
* @internal
|
|
2499
2705
|
*/
|
|
2500
|
-
|
|
2706
|
+
buildUpsertStatementCore(values, primaryKeys, updateColumns, updateColumnFilter, overridingSystemValue, targetWhere, setWhere) {
|
|
2501
2707
|
const schema = this._getSchema();
|
|
2502
|
-
const executor = this._getExecutor();
|
|
2503
|
-
const client = this._getClient();
|
|
2504
2708
|
const qualifiedTableName = this._getQualifiedTableName();
|
|
2505
2709
|
// Extract all unique column names from all data objects
|
|
2506
2710
|
const columnConfigs = [];
|
|
@@ -2605,6 +2809,123 @@ ${extraJoins.join('\n')}${orderBy}`;
|
|
|
2605
2809
|
sql += ` WHERE ${setWhere}`;
|
|
2606
2810
|
}
|
|
2607
2811
|
}
|
|
2812
|
+
return { sql, params };
|
|
2813
|
+
}
|
|
2814
|
+
/**
|
|
2815
|
+
* Builds a bare `UPDATE t SET .. WHERE "col" IN ($1, …)` statement (no
|
|
2816
|
+
* RETURNING) so `MutationBatch.addUpdateWhereIn` can compose it as a
|
|
2817
|
+
* data-modifying-CTE leg. SET semantics mirror the fluent `update()`:
|
|
2818
|
+
* plain values run through column mappers, `SqlFragment` values inline
|
|
2819
|
+
* with their params merged, and the lambda form resolves column refs
|
|
2820
|
+
* against this table's mock row. Returns null for an empty values array.
|
|
2821
|
+
* @internal
|
|
2822
|
+
*/
|
|
2823
|
+
_buildUpdateWhereInStatement(field, values, set) {
|
|
2824
|
+
if (values.length === 0) {
|
|
2825
|
+
return null;
|
|
2826
|
+
}
|
|
2827
|
+
const schema = this._getSchema();
|
|
2828
|
+
const whereColBuilder = schema.columns[field];
|
|
2829
|
+
if (!whereColBuilder) {
|
|
2830
|
+
throw new Error(`updateWhereIn: unknown column property "${field}" on entity "${schema.name}"`);
|
|
2831
|
+
}
|
|
2832
|
+
const whereConfig = whereColBuilder.build();
|
|
2833
|
+
const resolvedSet = typeof set === 'function' ? set(this.createMockEntity()) : set;
|
|
2834
|
+
const setClauses = [];
|
|
2835
|
+
const params = [];
|
|
2836
|
+
let paramIndex = 1;
|
|
2837
|
+
for (const [key, value] of Object.entries(resolvedSet)) {
|
|
2838
|
+
const column = schema.columns[key];
|
|
2839
|
+
if (!column) {
|
|
2840
|
+
continue;
|
|
2841
|
+
}
|
|
2842
|
+
const config = column.build();
|
|
2843
|
+
if (value instanceof conditions_1.SqlFragment) {
|
|
2844
|
+
const sqlBuildContext = {
|
|
2845
|
+
paramCounter: paramIndex,
|
|
2846
|
+
params,
|
|
2847
|
+
};
|
|
2848
|
+
const fragmentSql = value.buildSql(sqlBuildContext);
|
|
2849
|
+
paramIndex = sqlBuildContext.paramCounter;
|
|
2850
|
+
setClauses.push(`"${config.name}" = ${fragmentSql}`);
|
|
2851
|
+
continue;
|
|
2852
|
+
}
|
|
2853
|
+
setClauses.push(`"${config.name}" = $${paramIndex++}`);
|
|
2854
|
+
params.push(config.mapper ? config.mapper.toDriver(value) : value);
|
|
2855
|
+
}
|
|
2856
|
+
if (setClauses.length === 0) {
|
|
2857
|
+
throw new Error(`updateWhereIn: no valid columns to update on entity "${schema.name}"`);
|
|
2858
|
+
}
|
|
2859
|
+
const whereParams = values.map(value => (whereConfig.mapper ? whereConfig.mapper.toDriver(value) : value));
|
|
2860
|
+
const placeholders = whereParams.map(() => `$${paramIndex++}`).join(', ');
|
|
2861
|
+
params.push(...whereParams);
|
|
2862
|
+
return {
|
|
2863
|
+
sql: `UPDATE ${this._getQualifiedTableName()} SET ${setClauses.join(', ')} WHERE "${whereConfig.name}" IN (${placeholders})`,
|
|
2864
|
+
params,
|
|
2865
|
+
};
|
|
2866
|
+
}
|
|
2867
|
+
/**
|
|
2868
|
+
* Bare `INSERT .. ON CONFLICT` compile (no RETURNING) for the MutationBatch
|
|
2869
|
+
* upsert leg — the {@link buildUpsertStatementCore} assembly with a narrow
|
|
2870
|
+
* plain config (prop-name primaryKey + updateColumns; no chunking, no
|
|
2871
|
+
* targetWhere/setWhere/system-value overrides in v1).
|
|
2872
|
+
* @internal
|
|
2873
|
+
*/
|
|
2874
|
+
_buildUpsertBulkStatement(values, config) {
|
|
2875
|
+
if (values.length === 0) {
|
|
2876
|
+
return null;
|
|
2877
|
+
}
|
|
2878
|
+
const primaryKeys = Array.isArray(config.primaryKey) ? config.primaryKey : [config.primaryKey];
|
|
2879
|
+
return this.buildUpsertStatementCore(values, primaryKeys, config.updateColumns, undefined, false, undefined, undefined);
|
|
2880
|
+
}
|
|
2881
|
+
/**
|
|
2882
|
+
* Bare `INSERT INTO t (..) SELECT <cells> FROM "__MB_PARENT__" WHERE
|
|
2883
|
+
* "__MB_PARENT__"."<col>" <> $n` compile for the MutationBatch dependent
|
|
2884
|
+
* leg — a single row inserted iff the parent leg's exposed column differs
|
|
2885
|
+
* from the sentinel (the conditional-audit-log shape). Cells reuse the
|
|
2886
|
+
* insertWithChildren `$n::type` cast technique so types survive without a
|
|
2887
|
+
* VALUES-derived table; the `__MB_PARENT__` token is rewritten to the
|
|
2888
|
+
* parent's actual CTE name at batch assembly.
|
|
2889
|
+
* @internal
|
|
2890
|
+
*/
|
|
2891
|
+
_buildDependentInsertSelectStatement(row, whereColumnAlias, whereNotEquals) {
|
|
2892
|
+
const schema = this._getSchema();
|
|
2893
|
+
const compiled = this.compileValuesWithCasts(schema, [row], null);
|
|
2894
|
+
const columnList = compiled.columns.map(c => `"${c.dbName}"`).join(', ');
|
|
2895
|
+
const params = [...compiled.params];
|
|
2896
|
+
const sentinelIndex = params.length + 1;
|
|
2897
|
+
params.push(whereNotEquals);
|
|
2898
|
+
return {
|
|
2899
|
+
sql: `INSERT INTO ${this._getQualifiedTableName()} (${columnList}) SELECT ${compiled.valueRows[0]} FROM "__MB_PARENT__" WHERE "__MB_PARENT__"."${whereColumnAlias}" <> $${sentinelIndex}`,
|
|
2900
|
+
params,
|
|
2901
|
+
};
|
|
2902
|
+
}
|
|
2903
|
+
/**
|
|
2904
|
+
* Resolves entity property names to their DB column names (throws on an
|
|
2905
|
+
* unknown property) — the MutationBatch expose/returning lists come in as
|
|
2906
|
+
* prop names and compile to quoted DB identifiers.
|
|
2907
|
+
* @internal
|
|
2908
|
+
*/
|
|
2909
|
+
_resolveColumnDbNames(props) {
|
|
2910
|
+
const schema = this._getSchema();
|
|
2911
|
+
return props.map((prop) => {
|
|
2912
|
+
const colBuilder = schema.columns[prop];
|
|
2913
|
+
if (!colBuilder) {
|
|
2914
|
+
throw new Error(`Unknown column property "${prop}" on entity "${schema.name}"`);
|
|
2915
|
+
}
|
|
2916
|
+
return { prop, dbName: colBuilder.build().name };
|
|
2917
|
+
});
|
|
2918
|
+
}
|
|
2919
|
+
/**
|
|
2920
|
+
* Execute a single upsert batch
|
|
2921
|
+
* @internal
|
|
2922
|
+
*/
|
|
2923
|
+
async upsertBulkSingle(values, primaryKeys, updateColumns, updateColumnFilter, overridingSystemValue, targetWhere, setWhere, returning) {
|
|
2924
|
+
const executor = this._getExecutor();
|
|
2925
|
+
const client = this._getClient();
|
|
2926
|
+
const built = this.buildUpsertStatementCore(values, primaryKeys, updateColumns, updateColumnFilter, overridingSystemValue, targetWhere, setWhere);
|
|
2927
|
+
let sql = built.sql;
|
|
2928
|
+
const params = built.params;
|
|
2608
2929
|
// Check if RETURNING uses navigation properties
|
|
2609
2930
|
const navigationInfo = returning && returning !== true && typeof returning === 'function'
|
|
2610
2931
|
? this.detectNavigationInReturning(returning)
|