linkgress-orm 0.4.56 → 0.4.58
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 +176 -4
- package/dist/entity/db-context.d.ts.map +1 -1
- package/dist/entity/db-context.js +393 -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/cte-builder.d.ts +23 -0
- package/dist/query/cte-builder.d.ts.map +1 -1
- package/dist/query/cte-builder.js +26 -0
- package/dist/query/cte-builder.js.map +1 -1
- package/dist/query/mutation-batch.d.ts +132 -2
- package/dist/query/mutation-batch.d.ts.map +1 -1
- package/dist/query/mutation-batch.js +215 -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 +113 -8
- package/dist/query/query-builder.js.map +1 -1
- package/package.json +2 -3
|
@@ -2491,7 +2491,7 @@ class SelectQueryBuilder {
|
|
|
2491
2491
|
// Qualify columns with table name when using USING clause to avoid ambiguity
|
|
2492
2492
|
const hasJoins = whereJoins.length > 0;
|
|
2493
2493
|
const returningClause = returning !== 'count'
|
|
2494
|
-
? queryBuilder.buildUpdateDeleteReturningClause(returning, hasJoins)
|
|
2494
|
+
? queryBuilder.buildUpdateDeleteReturningClause(returning, hasJoins, { paramCounter: whereParams.length + 1, params: whereParams })
|
|
2495
2495
|
: undefined;
|
|
2496
2496
|
let sql = `DELETE FROM ${qualifiedTableName}`;
|
|
2497
2497
|
if (usingClause) {
|
|
@@ -2511,7 +2511,7 @@ class SelectQueryBuilder {
|
|
|
2511
2511
|
if (!returningClause) {
|
|
2512
2512
|
return undefined;
|
|
2513
2513
|
}
|
|
2514
|
-
return queryBuilder.mapDeleteReturningResults(result.rows, returning);
|
|
2514
|
+
return queryBuilder.mapDeleteReturningResults(result.rows, returning, returningClause.fragmentMappers);
|
|
2515
2515
|
};
|
|
2516
2516
|
return {
|
|
2517
2517
|
then(onfulfilled, onrejected) {
|
|
@@ -2650,7 +2650,7 @@ class SelectQueryBuilder {
|
|
|
2650
2650
|
// Qualify columns with table name when using FROM clause to avoid ambiguity
|
|
2651
2651
|
const hasJoins = whereJoins.length > 0;
|
|
2652
2652
|
const returningClause = returning !== 'count'
|
|
2653
|
-
? queryBuilder.buildUpdateDeleteReturningClause(returning, hasJoins)
|
|
2653
|
+
? queryBuilder.buildUpdateDeleteReturningClause(returning, hasJoins, { paramCounter: values.length + 1, params: values })
|
|
2654
2654
|
: undefined;
|
|
2655
2655
|
let sql = `UPDATE ${qualifiedTableName} SET ${setClauses.join(', ')}`;
|
|
2656
2656
|
if (fromClause) {
|
|
@@ -2670,7 +2670,77 @@ class SelectQueryBuilder {
|
|
|
2670
2670
|
if (!returningClause) {
|
|
2671
2671
|
return undefined;
|
|
2672
2672
|
}
|
|
2673
|
-
return queryBuilder.mapDeleteReturningResults(result.rows, returning);
|
|
2673
|
+
return queryBuilder.mapDeleteReturningResults(result.rows, returning, returningClause.fragmentMappers);
|
|
2674
|
+
};
|
|
2675
|
+
/**
|
|
2676
|
+
* Compile the UPDATE into `{ sql, params }` WITHOUT executing — the
|
|
2677
|
+
* standard-path assembly (same SET/WHERE/FROM semantics as execution,
|
|
2678
|
+
* SqlFragment values and fragment-capable RETURNING included; navigation
|
|
2679
|
+
* RETURNING is not supported here). Used to ride the statement as a
|
|
2680
|
+
* data-modifying CTE (`DbCteBuilder.withMutation`).
|
|
2681
|
+
*/
|
|
2682
|
+
const compileUpdate = (returning) => {
|
|
2683
|
+
if (!queryBuilder.whereCond) {
|
|
2684
|
+
throw new Error('Update requires a WHERE condition. Use where() before update().');
|
|
2685
|
+
}
|
|
2686
|
+
const whereJoins = [];
|
|
2687
|
+
queryBuilder.detectAndAddJoinsFromCondition(queryBuilder.whereCond, whereJoins);
|
|
2688
|
+
const resolvedData = typeof data === 'function'
|
|
2689
|
+
? data(queryBuilder._createMockRow())
|
|
2690
|
+
: data;
|
|
2691
|
+
const setClauses = [];
|
|
2692
|
+
const values = [];
|
|
2693
|
+
let paramIndex = 1;
|
|
2694
|
+
for (const [key, value] of Object.entries(resolvedData)) {
|
|
2695
|
+
const column = queryBuilder.schema.columns[key];
|
|
2696
|
+
if (column) {
|
|
2697
|
+
const config = column.build();
|
|
2698
|
+
if (value instanceof conditions_1.SqlFragment) {
|
|
2699
|
+
const sqlBuildContext = {
|
|
2700
|
+
paramCounter: paramIndex,
|
|
2701
|
+
params: values,
|
|
2702
|
+
};
|
|
2703
|
+
const fragmentSql = value.buildSql(sqlBuildContext);
|
|
2704
|
+
paramIndex = sqlBuildContext.paramCounter;
|
|
2705
|
+
setClauses.push(`"${config.name}" = ${fragmentSql}`);
|
|
2706
|
+
continue;
|
|
2707
|
+
}
|
|
2708
|
+
setClauses.push(`"${config.name}" = $${paramIndex++}`);
|
|
2709
|
+
values.push(config.mapper ? config.mapper.toDriver(value) : value);
|
|
2710
|
+
}
|
|
2711
|
+
}
|
|
2712
|
+
if (setClauses.length === 0) {
|
|
2713
|
+
throw new Error('No valid columns to update');
|
|
2714
|
+
}
|
|
2715
|
+
const condBuilder = new conditions_1.ConditionBuilder();
|
|
2716
|
+
const { sql: whereSql, params: whereParams } = condBuilder.build(queryBuilder.whereCond, paramIndex);
|
|
2717
|
+
values.push(...whereParams);
|
|
2718
|
+
const qualifiedTableName = queryBuilder.getQualifiedTableName(queryBuilder.schema.name, queryBuilder.schema.schema);
|
|
2719
|
+
let fromClause = '';
|
|
2720
|
+
const joinConditions = [];
|
|
2721
|
+
for (const join of whereJoins) {
|
|
2722
|
+
const sourceTable = join.sourceAlias || queryBuilder.schema.name;
|
|
2723
|
+
const joinTableName = queryBuilder.getQualifiedTableName(join.targetTable, join.targetSchema);
|
|
2724
|
+
fromClause = fromClause ? `${fromClause}, ${joinTableName} AS "${join.alias}"` : `FROM ${joinTableName} AS "${join.alias}"`;
|
|
2725
|
+
for (let i = 0; i < join.foreignKeys.length; i++) {
|
|
2726
|
+
joinConditions.push(`${(0, join_utils_1.formatJoinValue)(sourceTable, join.foreignKeys[i])} = ${(0, join_utils_1.formatJoinValue)(join.alias, join.matches[i])}`);
|
|
2727
|
+
}
|
|
2728
|
+
}
|
|
2729
|
+
const fullWhereClause = joinConditions.length > 0
|
|
2730
|
+
? `${joinConditions.join(' AND ')} AND ${whereSql}`
|
|
2731
|
+
: whereSql;
|
|
2732
|
+
const returningClause = returning != null
|
|
2733
|
+
? queryBuilder.buildUpdateDeleteReturningClause(returning, whereJoins.length > 0, { paramCounter: values.length + 1, params: values })
|
|
2734
|
+
: null;
|
|
2735
|
+
let sql = `UPDATE ${qualifiedTableName} SET ${setClauses.join(', ')}`;
|
|
2736
|
+
if (fromClause) {
|
|
2737
|
+
sql += ` ${fromClause}`;
|
|
2738
|
+
}
|
|
2739
|
+
sql += ` WHERE ${fullWhereClause}`;
|
|
2740
|
+
if (returningClause) {
|
|
2741
|
+
sql += ` RETURNING ${returningClause.sql}`;
|
|
2742
|
+
}
|
|
2743
|
+
return { sql, params: values };
|
|
2674
2744
|
};
|
|
2675
2745
|
return {
|
|
2676
2746
|
then(onfulfilled, onrejected) {
|
|
@@ -2683,6 +2753,9 @@ class SelectQueryBuilder {
|
|
|
2683
2753
|
}
|
|
2684
2754
|
};
|
|
2685
2755
|
},
|
|
2756
|
+
toStatement(selector) {
|
|
2757
|
+
return compileUpdate(selector);
|
|
2758
|
+
},
|
|
2686
2759
|
returning(selector) {
|
|
2687
2760
|
const returningConfig = selector ?? true;
|
|
2688
2761
|
return {
|
|
@@ -2697,9 +2770,12 @@ class SelectQueryBuilder {
|
|
|
2697
2770
|
* Build RETURNING clause for delete/update operations
|
|
2698
2771
|
* @param returning - The returning configuration
|
|
2699
2772
|
* @param qualifyWithTable - If true, qualify column names with the main table name (needed for DELETE with USING)
|
|
2773
|
+
* @param paramContext - Statement parameter state (params array + next $n). Required when the
|
|
2774
|
+
* selector contains SqlFragments — their parameters append here, which is
|
|
2775
|
+
* positionally correct because RETURNING is last in the statement text.
|
|
2700
2776
|
* @internal
|
|
2701
2777
|
*/
|
|
2702
|
-
buildUpdateDeleteReturningClause(returning, qualifyWithTable = false) {
|
|
2778
|
+
buildUpdateDeleteReturningClause(returning, qualifyWithTable = false, paramContext) {
|
|
2703
2779
|
if (returning === undefined) {
|
|
2704
2780
|
return null;
|
|
2705
2781
|
}
|
|
@@ -2717,22 +2793,39 @@ class SelectQueryBuilder {
|
|
|
2717
2793
|
if (typeof selection === 'object' && selection !== null) {
|
|
2718
2794
|
const columns = [];
|
|
2719
2795
|
const sqlParts = [];
|
|
2796
|
+
let fragmentMappers;
|
|
2720
2797
|
for (const [alias, field] of Object.entries(selection)) {
|
|
2721
|
-
if (field
|
|
2798
|
+
if (field instanceof conditions_1.SqlFragment) {
|
|
2799
|
+
// Raw fragment under its selector key (the key is the alias — a
|
|
2800
|
+
// fragment-side .as() is ignored here). Params append to the
|
|
2801
|
+
// statement's array; headline use case: PG18 `old."col"` capture.
|
|
2802
|
+
if (!paramContext) {
|
|
2803
|
+
throw new Error(`Returning selector field "${alias}" is a SqlFragment, which this mutation path does not support`);
|
|
2804
|
+
}
|
|
2805
|
+
const fragmentSql = field.buildSql(paramContext);
|
|
2806
|
+
columns.push(alias);
|
|
2807
|
+
sqlParts.push(`${fragmentSql} AS "${alias}"`);
|
|
2808
|
+
fragmentMappers = fragmentMappers ?? new Map();
|
|
2809
|
+
fragmentMappers.set(alias, field.getMapper());
|
|
2810
|
+
}
|
|
2811
|
+
else if (field && typeof field === 'object' && '__dbColumnName' in field) {
|
|
2722
2812
|
const dbName = field.__dbColumnName;
|
|
2723
2813
|
columns.push(alias);
|
|
2724
2814
|
sqlParts.push(`${tablePrefix}"${dbName}" AS "${alias}"`);
|
|
2725
2815
|
}
|
|
2726
2816
|
}
|
|
2727
|
-
return { sql: sqlParts.join(', '), columns };
|
|
2817
|
+
return { sql: sqlParts.join(', '), columns, fragmentMappers };
|
|
2728
2818
|
}
|
|
2729
2819
|
return null;
|
|
2730
2820
|
}
|
|
2731
2821
|
/**
|
|
2732
2822
|
* Map row results for delete/update RETURNING clause
|
|
2823
|
+
* @param fragmentMappers - Per-alias mapWith mappers for SqlFragment selector fields; a
|
|
2824
|
+
* fragment alias bypasses the schema-column mapper scan entirely
|
|
2825
|
+
* (its value is the fragment's, not any column's).
|
|
2733
2826
|
* @internal
|
|
2734
2827
|
*/
|
|
2735
|
-
mapDeleteReturningResults(rows, returning) {
|
|
2828
|
+
mapDeleteReturningResults(rows, returning, fragmentMappers) {
|
|
2736
2829
|
if (returning === true) {
|
|
2737
2830
|
// Full entity mapping - apply fromDriver mappers
|
|
2738
2831
|
return rows.map(row => {
|
|
@@ -2750,6 +2843,11 @@ class SelectQueryBuilder {
|
|
|
2750
2843
|
return rows.map(row => {
|
|
2751
2844
|
const mapped = {};
|
|
2752
2845
|
for (const [key, value] of Object.entries(row)) {
|
|
2846
|
+
if (fragmentMappers?.has(key)) {
|
|
2847
|
+
const fragmentMapper = fragmentMappers.get(key);
|
|
2848
|
+
mapped[key] = fragmentMapper?.fromDriver ? fragmentMapper.fromDriver(value) : value;
|
|
2849
|
+
continue;
|
|
2850
|
+
}
|
|
2753
2851
|
// Try to find column by alias or name
|
|
2754
2852
|
const colEntry = Object.entries(this.schema.columns).find(([propName, col]) => {
|
|
2755
2853
|
const config = col.build();
|
|
@@ -2833,6 +2931,13 @@ class SelectQueryBuilder {
|
|
|
2833
2931
|
allTableAliases.add(collectionBuilder.sourceTable);
|
|
2834
2932
|
}
|
|
2835
2933
|
}
|
|
2934
|
+
else if (field instanceof conditions_1.SqlFragment) {
|
|
2935
|
+
// Raw SQL fragment — rendered verbatim by the plain returning-clause
|
|
2936
|
+
// builder; it never implies navigation. Without this skip it would
|
|
2937
|
+
// fall into the nested-object branch and derail the whole selector
|
|
2938
|
+
// onto the CTE navigation path. (v1: FieldRefs inside returning
|
|
2939
|
+
// fragments are unsupported — reference columns as raw quoted SQL.)
|
|
2940
|
+
}
|
|
2836
2941
|
else if (!Array.isArray(field)) {
|
|
2837
2942
|
// Nested plain object - recurse into it
|
|
2838
2943
|
nestedObjects.set(fieldPath, field);
|