@warlock.js/cascade 5.20.0 → 5.21.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/CHANGELOG.md +30 -1
- package/cjs/index.cjs +2283 -1037
- package/cjs/index.cjs.map +1 -1
- package/esm/contracts/query-builder.contract.d.mts +30 -3
- package/esm/contracts/query-builder.contract.d.mts.map +1 -1
- package/esm/data-source/data-source-registry.d.mts +33 -1
- package/esm/data-source/data-source-registry.d.mts.map +1 -1
- package/esm/data-source/data-source-registry.mjs +75 -5
- package/esm/data-source/data-source-registry.mjs.map +1 -1
- package/esm/database-dirty-tracker.d.mts +5 -0
- package/esm/database-dirty-tracker.d.mts.map +1 -1
- package/esm/database-dirty-tracker.mjs +16 -0
- package/esm/database-dirty-tracker.mjs.map +1 -1
- package/esm/drivers/mongodb/mongodb-driver.d.mts +12 -0
- package/esm/drivers/mongodb/mongodb-driver.d.mts.map +1 -1
- package/esm/drivers/mongodb/mongodb-driver.mjs +47 -16
- package/esm/drivers/mongodb/mongodb-driver.mjs.map +1 -1
- package/esm/drivers/mongodb/mongodb-migration-driver.d.mts +1 -1
- package/esm/drivers/mongodb/mongodb-migration-driver.d.mts.map +1 -1
- package/esm/drivers/mongodb/mongodb-migration-driver.mjs +15 -18
- package/esm/drivers/mongodb/mongodb-migration-driver.mjs.map +1 -1
- package/esm/drivers/mongodb/mongodb-query-builder.d.mts +13 -3
- package/esm/drivers/mongodb/mongodb-query-builder.d.mts.map +1 -1
- package/esm/drivers/mongodb/mongodb-query-builder.mjs +72 -19
- package/esm/drivers/mongodb/mongodb-query-builder.mjs.map +1 -1
- package/esm/drivers/mongodb/mongodb-query-parser.d.mts +6 -0
- package/esm/drivers/mongodb/mongodb-query-parser.d.mts.map +1 -1
- package/esm/drivers/mongodb/mongodb-query-parser.mjs +128 -30
- package/esm/drivers/mongodb/mongodb-query-parser.mjs.map +1 -1
- package/esm/drivers/postgres/index.d.mts +2 -3
- package/esm/drivers/postgres/index.mjs +1 -2
- package/esm/drivers/postgres/postgres-blueprint.mjs +4 -4
- package/esm/drivers/postgres/postgres-blueprint.mjs.map +1 -1
- package/esm/drivers/postgres/postgres-dialect.d.mts +5 -1
- package/esm/drivers/postgres/postgres-dialect.d.mts.map +1 -1
- package/esm/drivers/postgres/postgres-dialect.mjs +38 -8
- package/esm/drivers/postgres/postgres-dialect.mjs.map +1 -1
- package/esm/drivers/postgres/postgres-driver.d.mts +52 -29
- package/esm/drivers/postgres/postgres-driver.d.mts.map +1 -1
- package/esm/drivers/postgres/postgres-driver.mjs +190 -57
- package/esm/drivers/postgres/postgres-driver.mjs.map +1 -1
- package/esm/drivers/postgres/postgres-migration-driver.d.mts +2 -1
- package/esm/drivers/postgres/postgres-migration-driver.d.mts.map +1 -1
- package/esm/drivers/postgres/postgres-migration-driver.mjs +8 -6
- package/esm/drivers/postgres/postgres-migration-driver.mjs.map +1 -1
- package/esm/drivers/postgres/postgres-query-builder.d.mts +18 -7
- package/esm/drivers/postgres/postgres-query-builder.d.mts.map +1 -1
- package/esm/drivers/postgres/postgres-query-builder.mjs +146 -31
- package/esm/drivers/postgres/postgres-query-builder.mjs.map +1 -1
- package/esm/drivers/postgres/postgres-query-parser.d.mts +23 -1
- package/esm/drivers/postgres/postgres-query-parser.d.mts.map +1 -1
- package/esm/drivers/postgres/postgres-query-parser.mjs +160 -43
- package/esm/drivers/postgres/postgres-query-parser.mjs.map +1 -1
- package/esm/drivers/postgres/postgres-sql-serializer.mjs +3 -2
- package/esm/drivers/postgres/postgres-sql-serializer.mjs.map +1 -1
- package/esm/drivers/postgres/postgres-type-converters.mjs +57 -0
- package/esm/drivers/postgres/postgres-type-converters.mjs.map +1 -0
- package/esm/events/model-events.d.mts +1 -1
- package/esm/events/model-events.d.mts.map +1 -1
- package/esm/events/model-events.mjs.map +1 -1
- package/esm/index.d.mts +5 -6
- package/esm/index.mjs +4 -5
- package/esm/migration/column-builder.d.mts.map +1 -1
- package/esm/migration/column-builder.mjs +2 -2
- package/esm/migration/column-builder.mjs.map +1 -1
- package/esm/migration/index.d.mts +2 -2
- package/esm/migration/index.mjs +1 -1
- package/esm/migration/migration-order.mjs +5 -2
- package/esm/migration/migration-order.mjs.map +1 -1
- package/esm/migration/migration-runner.d.mts +50 -5
- package/esm/migration/migration-runner.d.mts.map +1 -1
- package/esm/migration/migration-runner.mjs +369 -146
- package/esm/migration/migration-runner.mjs.map +1 -1
- package/esm/migration/migration.d.mts +36 -3
- package/esm/migration/migration.d.mts.map +1 -1
- package/esm/migration/migration.mjs +62 -5
- package/esm/migration/migration.mjs.map +1 -1
- package/esm/model/methods/accessor-methods.mjs +1 -3
- package/esm/model/methods/accessor-methods.mjs.map +1 -1
- package/esm/model/methods/delete-methods.mjs +6 -2
- package/esm/model/methods/delete-methods.mjs.map +1 -1
- package/esm/model/methods/hydration-methods.mjs +16 -2
- package/esm/model/methods/hydration-methods.mjs.map +1 -1
- package/esm/model/methods/instance-event-methods.mjs +2 -1
- package/esm/model/methods/instance-event-methods.mjs.map +1 -1
- package/esm/model/methods/meta-methods.mjs +53 -16
- package/esm/model/methods/meta-methods.mjs.map +1 -1
- package/esm/model/methods/query-methods.mjs +86 -15
- package/esm/model/methods/query-methods.mjs.map +1 -1
- package/esm/model/methods/static-event-methods.mjs +16 -1
- package/esm/model/methods/static-event-methods.mjs.map +1 -1
- package/esm/model/methods/write-methods.d.mts.map +1 -1
- package/esm/model/methods/write-methods.mjs +50 -50
- package/esm/model/methods/write-methods.mjs.map +1 -1
- package/esm/model/model.d.mts +16 -1
- package/esm/model/model.d.mts.map +1 -1
- package/esm/model/model.mjs +39 -5
- package/esm/model/model.mjs.map +1 -1
- package/esm/model/register-model.d.mts +6 -1
- package/esm/model/register-model.d.mts.map +1 -1
- package/esm/model/register-model.mjs +16 -1
- package/esm/model/register-model.mjs.map +1 -1
- package/esm/query-builder/query-builder.d.mts +21 -2
- package/esm/query-builder/query-builder.d.mts.map +1 -1
- package/esm/query-builder/query-builder.mjs +97 -20
- package/esm/query-builder/query-builder.mjs.map +1 -1
- package/esm/relations/key-conventions.mjs +13 -3
- package/esm/relations/key-conventions.mjs.map +1 -1
- package/esm/relations/pivot-operations.mjs +1 -1
- package/esm/relations/relation-loader.d.mts +15 -5
- package/esm/relations/relation-loader.d.mts.map +1 -1
- package/esm/relations/relation-loader.mjs +51 -16
- package/esm/relations/relation-loader.mjs.map +1 -1
- package/esm/remover/database-remover.d.mts.map +1 -1
- package/esm/remover/database-remover.mjs +21 -7
- package/esm/remover/database-remover.mjs.map +1 -1
- package/esm/restorer/database-restorer.d.mts +12 -2
- package/esm/restorer/database-restorer.d.mts.map +1 -1
- package/esm/restorer/database-restorer.mjs +43 -13
- package/esm/restorer/database-restorer.mjs.map +1 -1
- package/esm/sync/model-events.mjs +63 -3
- package/esm/sync/model-events.mjs.map +1 -1
- package/esm/sync/model-sync-operation.d.mts.map +1 -1
- package/esm/sync/model-sync-operation.mjs +3 -2
- package/esm/sync/model-sync-operation.mjs.map +1 -1
- package/esm/sync/sync-manager.d.mts +3 -42
- package/esm/sync/sync-manager.d.mts.map +1 -1
- package/esm/sync/sync-manager.mjs +37 -126
- package/esm/sync/sync-manager.mjs.map +1 -1
- package/esm/utils/connect-to-database.d.mts.map +1 -1
- package/esm/utils/connect-to-database.mjs +4 -4
- package/esm/utils/connect-to-database.mjs.map +1 -1
- package/esm/utils/database-writer.utils.d.mts +1 -1
- package/esm/utils/database-writer.utils.d.mts.map +1 -1
- package/esm/utils/database-writer.utils.mjs +1 -0
- package/esm/utils/database-writer.utils.mjs.map +1 -1
- package/esm/utils/define-model.d.mts.map +1 -1
- package/esm/utils/define-model.mjs +2 -10
- package/esm/utils/define-model.mjs.map +1 -1
- package/esm/utils/is-valid-date-value.mjs +3 -2
- package/esm/utils/is-valid-date-value.mjs.map +1 -1
- package/esm/utils/once-connected.d.mts.map +1 -1
- package/esm/utils/once-connected.mjs +12 -2
- package/esm/utils/once-connected.mjs.map +1 -1
- package/esm/validation/mutators/embed-mutator.mjs +19 -11
- package/esm/validation/mutators/embed-mutator.mjs.map +1 -1
- package/esm/validation/plugins/embed-validator-plugin.d.mts +2 -2
- package/esm/validation/plugins/embed-validator-plugin.d.mts.map +1 -1
- package/esm/validation/plugins/embed-validator-plugin.mjs +8 -2
- package/esm/validation/plugins/embed-validator-plugin.mjs.map +1 -1
- package/esm/validation/rules/database-model-rule.mjs +6 -5
- package/esm/validation/rules/database-model-rule.mjs.map +1 -1
- package/esm/validation/rules/exists-rule.mjs +6 -4
- package/esm/validation/rules/exists-rule.mjs.map +1 -1
- package/esm/validation/rules/unique-rule.mjs +5 -2
- package/esm/validation/rules/unique-rule.mjs.map +1 -1
- package/esm/validation/validators/embed-validator.d.mts.map +1 -1
- package/esm/validation/validators/embed-validator.mjs +1 -1
- package/esm/validation/validators/embed-validator.mjs.map +1 -1
- package/esm/writer/database-writer.d.mts +27 -0
- package/esm/writer/database-writer.d.mts.map +1 -1
- package/esm/writer/database-writer.mjs +84 -17
- package/esm/writer/database-writer.mjs.map +1 -1
- package/package.json +4 -4
- package/esm/drivers/postgres/postgres-sync-adapter.d.mts +0 -79
- package/esm/drivers/postgres/postgres-sync-adapter.d.mts.map +0 -1
- package/esm/drivers/postgres/postgres-sync-adapter.mjs +0 -162
- package/esm/drivers/postgres/postgres-sync-adapter.mjs.map +0 -1
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { SyncAdapterContract, SyncInstruction } from "../../contracts/sync-adapter.contract.mjs";
|
|
2
|
-
import { PostgresDriver } from "./postgres-driver.mjs";
|
|
3
|
-
|
|
4
|
-
//#region ../cascade/src/drivers/postgres/postgres-sync-adapter.d.ts
|
|
5
|
-
/**
|
|
6
|
-
* PostgreSQL Sync Adapter.
|
|
7
|
-
*
|
|
8
|
-
* Handles batch updates for embedded/denormalized data stored
|
|
9
|
-
* in JSONB columns. In a normalized SQL world, this is less common
|
|
10
|
-
* than in MongoDB, but still useful for JSONB documents.
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* ```typescript
|
|
14
|
-
* const syncAdapter = driver.syncAdapter();
|
|
15
|
-
*
|
|
16
|
-
* // Update embedded user data in posts
|
|
17
|
-
* await syncAdapter.executeBatch([
|
|
18
|
-
* {
|
|
19
|
-
* targetTable: 'posts',
|
|
20
|
-
* filter: { 'author.id': 123 },
|
|
21
|
-
* update: { 'author.name': 'New Name' },
|
|
22
|
-
* // ... other fields
|
|
23
|
-
* }
|
|
24
|
-
* ]);
|
|
25
|
-
* ```
|
|
26
|
-
*/
|
|
27
|
-
declare class PostgresSyncAdapter implements SyncAdapterContract {
|
|
28
|
-
private readonly driver;
|
|
29
|
-
/**
|
|
30
|
-
* Create a new sync adapter.
|
|
31
|
-
*
|
|
32
|
-
* @param driver - The PostgreSQL driver instance
|
|
33
|
-
*/
|
|
34
|
-
constructor(driver: PostgresDriver);
|
|
35
|
-
/**
|
|
36
|
-
* Execute a batch of sync instructions.
|
|
37
|
-
*
|
|
38
|
-
* @param instructions - Array of sync instructions
|
|
39
|
-
* @returns Total number of affected rows
|
|
40
|
-
*/
|
|
41
|
-
executeBatch(instructions: SyncInstruction[]): Promise<number>;
|
|
42
|
-
/**
|
|
43
|
-
* Execute a single sync instruction.
|
|
44
|
-
*
|
|
45
|
-
* @param instruction - Sync instruction
|
|
46
|
-
* @returns Number of affected rows
|
|
47
|
-
*/
|
|
48
|
-
executeOne(instruction: SyncInstruction): Promise<number>;
|
|
49
|
-
/**
|
|
50
|
-
* Execute an array update instruction with positional operators.
|
|
51
|
-
*
|
|
52
|
-
* @param instruction - Sync instruction with array update info
|
|
53
|
-
* @returns Number of affected rows
|
|
54
|
-
*/
|
|
55
|
-
executeArrayUpdate(instruction: SyncInstruction): Promise<number>;
|
|
56
|
-
/**
|
|
57
|
-
* Execute an update on JSONB fields.
|
|
58
|
-
*
|
|
59
|
-
* @param table - Table name
|
|
60
|
-
* @param filter - Row filter
|
|
61
|
-
* @param update - Fields to update
|
|
62
|
-
* @returns Number of affected rows
|
|
63
|
-
*/
|
|
64
|
-
private executeJsonbUpdate;
|
|
65
|
-
/**
|
|
66
|
-
* Execute an update on elements within a JSONB array.
|
|
67
|
-
*
|
|
68
|
-
* @param table - Table name
|
|
69
|
-
* @param filter - Row filter
|
|
70
|
-
* @param arrayField - JSONB array column
|
|
71
|
-
* @param arrayFilter - Filter to match array elements
|
|
72
|
-
* @param update - Fields to update on matched elements
|
|
73
|
-
* @returns Number of affected rows
|
|
74
|
-
*/
|
|
75
|
-
private executeArrayElementUpdate;
|
|
76
|
-
}
|
|
77
|
-
//#endregion
|
|
78
|
-
export { PostgresSyncAdapter };
|
|
79
|
-
//# sourceMappingURL=postgres-sync-adapter.d.mts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"postgres-sync-adapter.d.mts","names":[],"sources":["../../../../../../../../cascade/src/drivers/postgres/postgres-sync-adapter.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;cAkCa,mBAAA,YAA+B,mBAAA;EAAA,iBAMN,MAAA;EAiEtB;;;AAmFyB;;cApJH,MAAA,EAAQ,cAAA;;;;;;;EAQ/B,YAAA,CAAa,YAAA,EAAc,eAAA,KAAoB,OAAA;;;;;;;EAoB/C,UAAA,CAAW,WAAA,EAAa,eAAA,GAAkB,OAAA;;;;;;;EAW1C,kBAAA,CAAmB,WAAA,EAAa,eAAA,GAAkB,OAAA;;;;;;;;;UA0BjD,kBAAA;;;;;;;;;;;UAmFA,yBAAA;AAAA"}
|
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
//#region ../cascade/src/drivers/postgres/postgres-sync-adapter.ts
|
|
2
|
-
/**
|
|
3
|
-
* PostgreSQL Sync Adapter.
|
|
4
|
-
*
|
|
5
|
-
* Handles batch updates for embedded/denormalized data stored
|
|
6
|
-
* in JSONB columns. In a normalized SQL world, this is less common
|
|
7
|
-
* than in MongoDB, but still useful for JSONB documents.
|
|
8
|
-
*
|
|
9
|
-
* @example
|
|
10
|
-
* ```typescript
|
|
11
|
-
* const syncAdapter = driver.syncAdapter();
|
|
12
|
-
*
|
|
13
|
-
* // Update embedded user data in posts
|
|
14
|
-
* await syncAdapter.executeBatch([
|
|
15
|
-
* {
|
|
16
|
-
* targetTable: 'posts',
|
|
17
|
-
* filter: { 'author.id': 123 },
|
|
18
|
-
* update: { 'author.name': 'New Name' },
|
|
19
|
-
* // ... other fields
|
|
20
|
-
* }
|
|
21
|
-
* ]);
|
|
22
|
-
* ```
|
|
23
|
-
*/
|
|
24
|
-
var PostgresSyncAdapter = class {
|
|
25
|
-
driver;
|
|
26
|
-
/**
|
|
27
|
-
* Create a new sync adapter.
|
|
28
|
-
*
|
|
29
|
-
* @param driver - The PostgreSQL driver instance
|
|
30
|
-
*/
|
|
31
|
-
constructor(driver) {
|
|
32
|
-
this.driver = driver;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Execute a batch of sync instructions.
|
|
36
|
-
*
|
|
37
|
-
* @param instructions - Array of sync instructions
|
|
38
|
-
* @returns Total number of affected rows
|
|
39
|
-
*/
|
|
40
|
-
async executeBatch(instructions) {
|
|
41
|
-
let totalAffected = 0;
|
|
42
|
-
for (const instruction of instructions) if (instruction.isArrayUpdate) totalAffected += await this.executeArrayUpdate(instruction);
|
|
43
|
-
else totalAffected += await this.executeOne(instruction);
|
|
44
|
-
return totalAffected;
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Execute a single sync instruction.
|
|
48
|
-
*
|
|
49
|
-
* @param instruction - Sync instruction
|
|
50
|
-
* @returns Number of affected rows
|
|
51
|
-
*/
|
|
52
|
-
async executeOne(instruction) {
|
|
53
|
-
const { targetTable, filter, update } = instruction;
|
|
54
|
-
return this.executeJsonbUpdate(targetTable, filter, update);
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Execute an array update instruction with positional operators.
|
|
58
|
-
*
|
|
59
|
-
* @param instruction - Sync instruction with array update info
|
|
60
|
-
* @returns Number of affected rows
|
|
61
|
-
*/
|
|
62
|
-
async executeArrayUpdate(instruction) {
|
|
63
|
-
const { targetTable, filter, update, arrayField, identifierField, identifierValue } = instruction;
|
|
64
|
-
if (!arrayField || !identifierField || identifierValue === void 0) return this.executeOne(instruction);
|
|
65
|
-
return this.executeArrayElementUpdate(targetTable, filter, arrayField, { [identifierField]: identifierValue }, update);
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* Execute an update on JSONB fields.
|
|
69
|
-
*
|
|
70
|
-
* @param table - Table name
|
|
71
|
-
* @param filter - Row filter
|
|
72
|
-
* @param update - Fields to update
|
|
73
|
-
* @returns Number of affected rows
|
|
74
|
-
*/
|
|
75
|
-
async executeJsonbUpdate(table, filter, update) {
|
|
76
|
-
const quotedTable = this.driver.dialect.quoteIdentifier(table);
|
|
77
|
-
const setClauses = [];
|
|
78
|
-
const params = [];
|
|
79
|
-
let paramIndex = 1;
|
|
80
|
-
for (const [path, value] of Object.entries(update)) if (path.includes(".")) {
|
|
81
|
-
const [column, ...pathParts] = path.split(".");
|
|
82
|
-
const quotedColumn = this.driver.dialect.quoteIdentifier(column ?? path);
|
|
83
|
-
const jsonPath = `{${pathParts.join(",")}}`;
|
|
84
|
-
const placeholder = this.driver.dialect.placeholder(paramIndex++);
|
|
85
|
-
params.push(JSON.stringify(value));
|
|
86
|
-
setClauses.push(`${quotedColumn} = jsonb_set(COALESCE(${quotedColumn}, '{}'::jsonb), '${jsonPath}', ${placeholder}::jsonb)`);
|
|
87
|
-
} else {
|
|
88
|
-
const quotedColumn = this.driver.dialect.quoteIdentifier(path);
|
|
89
|
-
const placeholder = this.driver.dialect.placeholder(paramIndex++);
|
|
90
|
-
params.push(value);
|
|
91
|
-
setClauses.push(`${quotedColumn} = ${placeholder}`);
|
|
92
|
-
}
|
|
93
|
-
const whereClauses = [];
|
|
94
|
-
for (const [key, value] of Object.entries(filter)) if (key.includes(".")) {
|
|
95
|
-
const [column, ...pathParts] = key.split(".");
|
|
96
|
-
const quotedColumn = this.driver.dialect.quoteIdentifier(column ?? key);
|
|
97
|
-
const intermediateExpr = pathParts.slice(0, -1).map((p) => `->'${p}'`).join("");
|
|
98
|
-
const finalExpr = `->>'${pathParts[pathParts.length - 1]}'`;
|
|
99
|
-
const placeholder = this.driver.dialect.placeholder(paramIndex++);
|
|
100
|
-
params.push(value);
|
|
101
|
-
whereClauses.push(`${quotedColumn}${intermediateExpr}${finalExpr} = ${placeholder}`);
|
|
102
|
-
} else {
|
|
103
|
-
const quotedKey = this.driver.dialect.quoteIdentifier(key);
|
|
104
|
-
const placeholder = this.driver.dialect.placeholder(paramIndex++);
|
|
105
|
-
params.push(value);
|
|
106
|
-
whereClauses.push(`${quotedKey} = ${placeholder}`);
|
|
107
|
-
}
|
|
108
|
-
const sql = `UPDATE ${quotedTable} SET ${setClauses.join(", ")} WHERE ${whereClauses.join(" AND ")}`;
|
|
109
|
-
return (await this.driver.query(sql, params)).rowCount ?? 0;
|
|
110
|
-
}
|
|
111
|
-
/**
|
|
112
|
-
* Execute an update on elements within a JSONB array.
|
|
113
|
-
*
|
|
114
|
-
* @param table - Table name
|
|
115
|
-
* @param filter - Row filter
|
|
116
|
-
* @param arrayField - JSONB array column
|
|
117
|
-
* @param arrayFilter - Filter to match array elements
|
|
118
|
-
* @param update - Fields to update on matched elements
|
|
119
|
-
* @returns Number of affected rows
|
|
120
|
-
*/
|
|
121
|
-
async executeArrayElementUpdate(table, filter, arrayField, arrayFilter, update) {
|
|
122
|
-
const quotedTable = this.driver.dialect.quoteIdentifier(table);
|
|
123
|
-
const quotedArrayField = this.driver.dialect.quoteIdentifier(arrayField);
|
|
124
|
-
const params = [];
|
|
125
|
-
let paramIndex = 1;
|
|
126
|
-
const sql = `
|
|
127
|
-
WITH updated AS (
|
|
128
|
-
SELECT ctid, (
|
|
129
|
-
SELECT jsonb_agg(
|
|
130
|
-
CASE
|
|
131
|
-
WHEN ${Object.entries(arrayFilter).map(([key, value]) => {
|
|
132
|
-
params.push(value);
|
|
133
|
-
return `elem->>'${key}' = ${this.driver.dialect.placeholder(paramIndex++)}`;
|
|
134
|
-
}).join(" AND ")}
|
|
135
|
-
THEN elem || jsonb_build_object(${Object.entries(update).map(([key, value]) => {
|
|
136
|
-
params.push(JSON.stringify(value));
|
|
137
|
-
return `'${key}', ${this.driver.dialect.placeholder(paramIndex++)}::jsonb`;
|
|
138
|
-
}).join(", ")})
|
|
139
|
-
ELSE elem
|
|
140
|
-
END
|
|
141
|
-
)
|
|
142
|
-
FROM jsonb_array_elements(${quotedArrayField}) elem
|
|
143
|
-
) AS new_array
|
|
144
|
-
FROM ${quotedTable}
|
|
145
|
-
WHERE ${Object.entries(filter).map(([key, value]) => {
|
|
146
|
-
const quotedKey = this.driver.dialect.quoteIdentifier(key);
|
|
147
|
-
params.push(value);
|
|
148
|
-
return `${quotedKey} = ${this.driver.dialect.placeholder(paramIndex++)}`;
|
|
149
|
-
}).join(" AND ")}
|
|
150
|
-
)
|
|
151
|
-
UPDATE ${quotedTable} t
|
|
152
|
-
SET ${quotedArrayField} = u.new_array
|
|
153
|
-
FROM updated u
|
|
154
|
-
WHERE t.ctid = u.ctid
|
|
155
|
-
`;
|
|
156
|
-
return (await this.driver.query(sql, params)).rowCount ?? 0;
|
|
157
|
-
}
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
//#endregion
|
|
161
|
-
export { PostgresSyncAdapter };
|
|
162
|
-
//# sourceMappingURL=postgres-sync-adapter.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"postgres-sync-adapter.mjs","names":[],"sources":["../../../../../../../../cascade/src/drivers/postgres/postgres-sync-adapter.ts"],"sourcesContent":["/**\r\n * PostgreSQL Sync Adapter\r\n *\r\n * Implements the SyncAdapterContract for batch update operations\r\n * on embedded/denormalized data in PostgreSQL.\r\n *\r\n * @module cascade/drivers/postgres\r\n */\r\n\r\nimport type { SyncAdapterContract, SyncInstruction } from \"../../contracts/sync-adapter.contract\";\r\nimport type { PostgresDriver } from \"./postgres-driver\";\r\n\r\n/**\r\n * PostgreSQL Sync Adapter.\r\n *\r\n * Handles batch updates for embedded/denormalized data stored\r\n * in JSONB columns. In a normalized SQL world, this is less common\r\n * than in MongoDB, but still useful for JSONB documents.\r\n *\r\n * @example\r\n * ```typescript\r\n * const syncAdapter = driver.syncAdapter();\r\n *\r\n * // Update embedded user data in posts\r\n * await syncAdapter.executeBatch([\r\n * {\r\n * targetTable: 'posts',\r\n * filter: { 'author.id': 123 },\r\n * update: { 'author.name': 'New Name' },\r\n * // ... other fields\r\n * }\r\n * ]);\r\n * ```\r\n */\r\nexport class PostgresSyncAdapter implements SyncAdapterContract {\r\n /**\r\n * Create a new sync adapter.\r\n *\r\n * @param driver - The PostgreSQL driver instance\r\n */\r\n public constructor(private readonly driver: PostgresDriver) {}\r\n\r\n /**\r\n * Execute a batch of sync instructions.\r\n *\r\n * @param instructions - Array of sync instructions\r\n * @returns Total number of affected rows\r\n */\r\n public async executeBatch(instructions: SyncInstruction[]): Promise<number> {\r\n let totalAffected = 0;\r\n\r\n for (const instruction of instructions) {\r\n if (instruction.isArrayUpdate) {\r\n totalAffected += await this.executeArrayUpdate(instruction);\r\n } else {\r\n totalAffected += await this.executeOne(instruction);\r\n }\r\n }\r\n\r\n return totalAffected;\r\n }\r\n\r\n /**\r\n * Execute a single sync instruction.\r\n *\r\n * @param instruction - Sync instruction\r\n * @returns Number of affected rows\r\n */\r\n public async executeOne(instruction: SyncInstruction): Promise<number> {\r\n const { targetTable, filter, update } = instruction;\r\n return this.executeJsonbUpdate(targetTable, filter, update);\r\n }\r\n\r\n /**\r\n * Execute an array update instruction with positional operators.\r\n *\r\n * @param instruction - Sync instruction with array update info\r\n * @returns Number of affected rows\r\n */\r\n public async executeArrayUpdate(instruction: SyncInstruction): Promise<number> {\r\n const { targetTable, filter, update, arrayField, identifierField, identifierValue } =\r\n instruction;\r\n\r\n if (!arrayField || !identifierField || identifierValue === undefined) {\r\n // Fall back to regular update if array info is missing\r\n return this.executeOne(instruction);\r\n }\r\n\r\n return this.executeArrayElementUpdate(\r\n targetTable,\r\n filter,\r\n arrayField,\r\n { [identifierField]: identifierValue },\r\n update,\r\n );\r\n }\r\n\r\n /**\r\n * Execute an update on JSONB fields.\r\n *\r\n * @param table - Table name\r\n * @param filter - Row filter\r\n * @param update - Fields to update\r\n * @returns Number of affected rows\r\n */\r\n private async executeJsonbUpdate(\r\n table: string,\r\n filter: Record<string, unknown>,\r\n update: Record<string, unknown>,\r\n ): Promise<number> {\r\n const quotedTable = this.driver.dialect.quoteIdentifier(table);\r\n const setClauses: string[] = [];\r\n const params: unknown[] = [];\r\n let paramIndex = 1;\r\n\r\n // Build SET clauses for each update field\r\n for (const [path, value] of Object.entries(update)) {\r\n if (path.includes(\".\")) {\r\n // JSONB path update: \"column.nested.field\" -> jsonb_set\r\n const [column, ...pathParts] = path.split(\".\");\r\n\r\n // The `includes(\".\")` check above guarantees a non-empty first segment,\r\n // but the compiler does not carry it here. Falling back to the whole\r\n // path keeps a REAL identifier in the SQL — quoting `undefined` would\r\n // emit a valid query against a column that does not exist.\r\n const quotedColumn = this.driver.dialect.quoteIdentifier(column ?? path);\r\n const jsonPath = `{${pathParts.join(\",\")}}`;\r\n const placeholder = this.driver.dialect.placeholder(paramIndex++);\r\n params.push(JSON.stringify(value));\r\n\r\n setClauses.push(\r\n `${quotedColumn} = jsonb_set(COALESCE(${quotedColumn}, '{}'::jsonb), '${jsonPath}', ${placeholder}::jsonb)`,\r\n );\r\n } else {\r\n // Regular column update\r\n const quotedColumn = this.driver.dialect.quoteIdentifier(path);\r\n const placeholder = this.driver.dialect.placeholder(paramIndex++);\r\n params.push(value);\r\n\r\n setClauses.push(`${quotedColumn} = ${placeholder}`);\r\n }\r\n }\r\n\r\n // Build WHERE clause\r\n const whereClauses: string[] = [];\r\n for (const [key, value] of Object.entries(filter)) {\r\n if (key.includes(\".\")) {\r\n // JSONB path filter: intermediate segments use -> (jsonb), final uses ->> (text).\r\n // e.g. \"column.a\" → \"column\"->>'a', \"column.a.b\" → \"column\"->'a'->>'b'\r\n const [column, ...pathParts] = key.split(\".\");\r\n\r\n // Same reasoning as the jsonb_set path above: fall back to the whole\r\n // key so the emitted SQL always names a real column.\r\n const quotedColumn = this.driver.dialect.quoteIdentifier(column ?? key);\r\n const intermediateExpr = pathParts\r\n .slice(0, -1)\r\n .map((p) => `->'${p}'`)\r\n .join(\"\");\r\n const finalExpr = `->>'${pathParts[pathParts.length - 1]}'`;\r\n const placeholder = this.driver.dialect.placeholder(paramIndex++);\r\n params.push(value);\r\n\r\n whereClauses.push(`${quotedColumn}${intermediateExpr}${finalExpr} = ${placeholder}`);\r\n } else {\r\n const quotedKey = this.driver.dialect.quoteIdentifier(key);\r\n const placeholder = this.driver.dialect.placeholder(paramIndex++);\r\n params.push(value);\r\n\r\n whereClauses.push(`${quotedKey} = ${placeholder}`);\r\n }\r\n }\r\n\r\n const sql = `UPDATE ${quotedTable} SET ${setClauses.join(\", \")} WHERE ${whereClauses.join(\" AND \")}`;\r\n\r\n const result = await this.driver.query(sql, params);\r\n return result.rowCount ?? 0;\r\n }\r\n\r\n /**\r\n * Execute an update on elements within a JSONB array.\r\n *\r\n * @param table - Table name\r\n * @param filter - Row filter\r\n * @param arrayField - JSONB array column\r\n * @param arrayFilter - Filter to match array elements\r\n * @param update - Fields to update on matched elements\r\n * @returns Number of affected rows\r\n */\r\n private async executeArrayElementUpdate(\r\n table: string,\r\n filter: Record<string, unknown>,\r\n arrayField: string,\r\n arrayFilter: Record<string, unknown>,\r\n update: Record<string, unknown>,\r\n ): Promise<number> {\r\n const quotedTable = this.driver.dialect.quoteIdentifier(table);\r\n const quotedArrayField = this.driver.dialect.quoteIdentifier(arrayField);\r\n\r\n const params: unknown[] = [];\r\n let paramIndex = 1;\r\n\r\n // Build array element filter condition\r\n const arrayFilterCondition = Object.entries(arrayFilter)\r\n .map(([key, value]) => {\r\n params.push(value);\r\n return `elem->>'${key}' = ${this.driver.dialect.placeholder(paramIndex++)}`;\r\n })\r\n .join(\" AND \");\r\n\r\n // Build update expression for matched elements\r\n const updateExpr = Object.entries(update)\r\n .map(([key, value]) => {\r\n params.push(JSON.stringify(value));\r\n return `'${key}', ${this.driver.dialect.placeholder(paramIndex++)}::jsonb`;\r\n })\r\n .join(\", \");\r\n\r\n // Build WHERE clause for row filter\r\n const whereClauses = Object.entries(filter).map(([key, value]) => {\r\n const quotedKey = this.driver.dialect.quoteIdentifier(key);\r\n params.push(value);\r\n return `${quotedKey} = ${this.driver.dialect.placeholder(paramIndex++)}`;\r\n });\r\n\r\n // Use a CTE to update array elements.\r\n // ctid is PostgreSQL's built-in physical row identifier, available on every\r\n // table regardless of the primary key column name.\r\n const sql = `\r\n WITH updated AS (\r\n SELECT ctid, (\r\n SELECT jsonb_agg(\r\n CASE\r\n WHEN ${arrayFilterCondition}\r\n THEN elem || jsonb_build_object(${updateExpr})\r\n ELSE elem\r\n END\r\n )\r\n FROM jsonb_array_elements(${quotedArrayField}) elem\r\n ) AS new_array\r\n FROM ${quotedTable}\r\n WHERE ${whereClauses.join(\" AND \")}\r\n )\r\n UPDATE ${quotedTable} t\r\n SET ${quotedArrayField} = u.new_array\r\n FROM updated u\r\n WHERE t.ctid = u.ctid\r\n `;\r\n\r\n const result = await this.driver.query(sql, params);\r\n return result.rowCount ?? 0;\r\n }\r\n}\r\n"],"mappings":"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"}
|