@warlock.js/cascade 5.22.0 → 5.23.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 +28 -11
- package/cjs/index.cjs +119 -40
- package/cjs/index.cjs.map +1 -1
- package/esm/drivers/postgres/index.d.mts +2 -2
- package/esm/drivers/postgres/index.mjs +1 -1
- package/esm/drivers/postgres/naming.mjs +18 -0
- package/esm/drivers/postgres/naming.mjs.map +1 -0
- package/esm/drivers/postgres/postgres-dialect.d.mts +9 -1
- package/esm/drivers/postgres/postgres-dialect.d.mts.map +1 -1
- package/esm/drivers/postgres/postgres-dialect.mjs +10 -0
- package/esm/drivers/postgres/postgres-dialect.mjs.map +1 -1
- package/esm/drivers/postgres/postgres-driver.d.mts +7 -1
- package/esm/drivers/postgres/postgres-driver.d.mts.map +1 -1
- package/esm/drivers/postgres/postgres-driver.mjs +46 -16
- package/esm/drivers/postgres/postgres-driver.mjs.map +1 -1
- package/esm/drivers/postgres/postgres-query-builder.d.mts.map +1 -1
- package/esm/drivers/postgres/postgres-query-builder.mjs +22 -15
- package/esm/drivers/postgres/postgres-query-builder.mjs.map +1 -1
- package/esm/drivers/postgres/postgres-query-parser.d.mts +1 -0
- package/esm/drivers/postgres/postgres-query-parser.d.mts.map +1 -1
- package/esm/drivers/postgres/postgres-query-parser.mjs +16 -10
- package/esm/drivers/postgres/postgres-query-parser.mjs.map +1 -1
- package/esm/drivers/postgres/types.d.mts +2 -1
- package/esm/drivers/postgres/types.d.mts.map +1 -1
- package/esm/index.d.mts +3 -3
- package/esm/index.mjs +2 -2
- package/esm/model/model.d.mts +5 -1
- package/esm/model/model.d.mts.map +1 -1
- package/esm/model/model.mjs +6 -0
- package/esm/model/model.mjs.map +1 -1
- package/esm/utils/connect-to-database.d.mts.map +1 -1
- package/esm/utils/connect-to-database.mjs +6 -0
- package/esm/utils/connect-to-database.mjs.map +1 -1
- package/esm/validation/plugins/database-rules-plugin.d.mts +12 -4
- package/esm/validation/plugins/database-rules-plugin.d.mts.map +1 -1
- package/esm/validation/plugins/database-rules-plugin.mjs.map +1 -1
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -4,16 +4,34 @@ All notable changes to `@warlock.js/cascade` are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). `@warlock.js/*` packages are released in lockstep — every package shares the same version number, so a version below may list only the changes that affected this package.
|
|
6
6
|
|
|
7
|
-
## 5.
|
|
8
|
-
|
|
9
|
-
### Added
|
|
10
|
-
|
|
11
|
-
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
7
|
+
## 5.23.0 - 2026-09-25
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Postgres `naming: "snake_case"` (opt-in, default `"preserve"`): camelCase model keys map to snake_case columns in inserts, updates, `where`, `orderBy`, `select`, `groupBy` and joins, and rows come back camelCase. Table names and raw SQL are left as written.
|
|
12
|
+
- Static `Model.whereRaw(expression, bindings?)`, matching the other static query entry points (`Model.query().whereRaw(...)` still works).
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- Postgres `bigint`/`int8` columns (and int8 arrays) come back as numbers when they fit in a safe integer, so a bigint id passes `v.number()`; larger values stay strings. The parser is installed on Cascade's own pool, not on the global `pg.types`.
|
|
17
|
+
- `unique()` / `exists()` on `StringValidator` and `NumberValidator` now return `this`, so `Infer<>` keeps the concrete output type (was `unknown`).
|
|
18
|
+
|
|
19
|
+
## 5.22.1 - 2026-09-25
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- Lockstep patch release; package APIs are unchanged.
|
|
24
|
+
|
|
25
|
+
## 5.22.0 - 2026-09-25
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
|
|
29
|
+
- PostgreSQL TTL indexes register purge jobs and persist retention rules in index comments so jobs restore after reconnect.
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- Post-commit sync fan-out retries transient failures without failing committed writes.
|
|
34
|
+
|
|
17
35
|
## 5.21.0 - 2026-09-25
|
|
18
36
|
|
|
19
37
|
### Added
|
|
@@ -39,7 +57,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
39
57
|
- Migrations: `.index()`/`.unique()`/`.vectorIndex()` inside `Migration.alter` `modify` now create the index, and `randomIncrement` takes effect.
|
|
40
58
|
- Mongo: replica-set detection runs `hello` once per driver.
|
|
41
59
|
|
|
42
|
-
|
|
43
60
|
### Changed
|
|
44
61
|
|
|
45
62
|
- Lockstep release maintenance and dependency refresh.
|
package/cjs/index.cjs
CHANGED
|
@@ -7189,6 +7189,12 @@ var Model = class Model {
|
|
|
7189
7189
|
return this.query().where(...args);
|
|
7190
7190
|
}
|
|
7191
7191
|
/**
|
|
7192
|
+
* Use a raw where clause directly.
|
|
7193
|
+
*/
|
|
7194
|
+
static whereRaw(expression, bindings) {
|
|
7195
|
+
return this.query().whereRaw(expression, bindings);
|
|
7196
|
+
}
|
|
7197
|
+
/**
|
|
7192
7198
|
* Count the number of records in the table
|
|
7193
7199
|
* @param filter - The filter to apply to the query
|
|
7194
7200
|
*/
|
|
@@ -15164,6 +15170,22 @@ var PostgresBlueprint = class {
|
|
|
15164
15170
|
}
|
|
15165
15171
|
};
|
|
15166
15172
|
|
|
15173
|
+
//#endregion
|
|
15174
|
+
//#region ../cascade/src/drivers/postgres/naming.ts
|
|
15175
|
+
/** Convert a JavaScript camelCase key to a PostgreSQL snake_case column name. */
|
|
15176
|
+
function camelToSnake(value) {
|
|
15177
|
+
if (value === "*" || value.includes("_")) return value;
|
|
15178
|
+
return (value.match(/[A-Z]+(?=[A-Z][a-z]|\d|$)|[A-Z]?[a-z]+|\d+/g) ?? [value]).map((part) => part.toLowerCase()).join("_");
|
|
15179
|
+
}
|
|
15180
|
+
/** Convert a PostgreSQL snake_case column name to a JavaScript camelCase key. */
|
|
15181
|
+
function snakeToCamel(value) {
|
|
15182
|
+
return value.replace(/_([a-z0-9])/g, (_match, character) => character.toUpperCase());
|
|
15183
|
+
}
|
|
15184
|
+
/** Convert top-level database row keys without rewriting nested JSON values. */
|
|
15185
|
+
function snakeKeysToCamel(row) {
|
|
15186
|
+
return Object.fromEntries(Object.entries(row).map(([key, value]) => [snakeToCamel(key), value]));
|
|
15187
|
+
}
|
|
15188
|
+
|
|
15167
15189
|
//#endregion
|
|
15168
15190
|
//#region ../cascade/src/drivers/postgres/postgres-dialect.ts
|
|
15169
15191
|
/**
|
|
@@ -15186,6 +15208,10 @@ var PostgresBlueprint = class {
|
|
|
15186
15208
|
* ```
|
|
15187
15209
|
*/
|
|
15188
15210
|
var PostgresDialect = class {
|
|
15211
|
+
naming;
|
|
15212
|
+
constructor(options = {}) {
|
|
15213
|
+
this.naming = options.naming ?? "preserve";
|
|
15214
|
+
}
|
|
15189
15215
|
/**
|
|
15190
15216
|
* Dialect name identifier.
|
|
15191
15217
|
*/
|
|
@@ -15219,6 +15245,10 @@ var PostgresDialect = class {
|
|
|
15219
15245
|
* @returns The quoted identifier (e.g., '"user"')
|
|
15220
15246
|
*/
|
|
15221
15247
|
quoteIdentifier(identifier) {
|
|
15248
|
+
return identifier.split(".").map((part) => `"${(this.naming === "snake_case" ? camelToSnake(part) : part).replace(/"/g, "\"\"")}"`).join(".");
|
|
15249
|
+
}
|
|
15250
|
+
/** Quote a table identifier without applying the column naming policy. */
|
|
15251
|
+
quoteTableIdentifier(identifier) {
|
|
15222
15252
|
return identifier.split(".").map((part) => `"${part.replace(/"/g, "\"\"")}"`).join(".");
|
|
15223
15253
|
}
|
|
15224
15254
|
/**
|
|
@@ -16741,7 +16771,7 @@ var PostgresQueryParser = class PostgresQueryParser {
|
|
|
16741
16771
|
buildSql() {
|
|
16742
16772
|
const parts = [];
|
|
16743
16773
|
parts.push(this.buildSelectClause());
|
|
16744
|
-
const quotedTable = this.
|
|
16774
|
+
const quotedTable = this.quoteTable(this.table);
|
|
16745
16775
|
const fromClause = this.alias ? `FROM ${quotedTable} AS ${this.dialect.quoteIdentifier(this.alias)}` : `FROM ${quotedTable}`;
|
|
16746
16776
|
parts.push(fromClause);
|
|
16747
16777
|
if (this.joinClauses.length > 0) parts.push(this.joinClauses.join(" "));
|
|
@@ -16764,7 +16794,7 @@ var PostgresQueryParser = class PostgresQueryParser {
|
|
|
16764
16794
|
*/
|
|
16765
16795
|
buildSelectClause() {
|
|
16766
16796
|
const distinct = this.isDistinct ? "DISTINCT " : "";
|
|
16767
|
-
if (this.selectColumns.length === 0 && this.selectRaw.length === 0) return this.hasJoins ? `SELECT ${distinct}${this.
|
|
16797
|
+
if (this.selectColumns.length === 0 && this.selectRaw.length === 0) return this.hasJoins ? `SELECT ${distinct}${this.quoteTable(this.table)}.*` : `SELECT ${distinct}*`;
|
|
16768
16798
|
const columns = [];
|
|
16769
16799
|
for (const col of this.selectColumns) if (!this.deselectColumns.includes(col)) columns.push(this.parseColumnIdentifier(col, this.table, this.alias));
|
|
16770
16800
|
columns.push(...this.selectRaw);
|
|
@@ -17057,16 +17087,16 @@ var PostgresQueryParser = class PostgresQueryParser {
|
|
|
17057
17087
|
const relationType = data.type;
|
|
17058
17088
|
const constraintOps = data.constraintOps;
|
|
17059
17089
|
const quotedAlias = this.dialect.quoteIdentifier(alias);
|
|
17060
|
-
const quotedTable = this.
|
|
17090
|
+
const quotedTable = this.quoteTable(this.table);
|
|
17061
17091
|
if (!(this.selectColumns.length > 0) && !this.selectRaw.includes(`${quotedTable}.*`)) this.selectRaw.unshift(`${quotedTable}.*`);
|
|
17062
17092
|
if (relationType === "hasMany") {
|
|
17063
17093
|
const relatedTable = data.table;
|
|
17064
17094
|
const foreignKey = data.foreignKey;
|
|
17065
17095
|
const localKey = data.localKey;
|
|
17066
|
-
const quotedRelatedTable = this.
|
|
17096
|
+
const quotedRelatedTable = this.quoteTable(relatedTable);
|
|
17067
17097
|
const quotedForeignKey = this.dialect.quoteIdentifier(foreignKey);
|
|
17068
17098
|
const quotedLocalKey = this.dialect.quoteIdentifier(localKey);
|
|
17069
|
-
const quotedMainTable = this.
|
|
17099
|
+
const quotedMainTable = this.quoteTable(this.table);
|
|
17070
17100
|
let innerSelect;
|
|
17071
17101
|
if (select && select.length > 0) innerSelect = `json_agg(json_build_object(${select.map((col) => `'${escapeSqlLiteral(col)}', a.${this.dialect.quoteIdentifier(col)}`).join(", ")}))`;
|
|
17072
17102
|
else innerSelect = `json_agg(row_to_json(a.*))`;
|
|
@@ -17078,6 +17108,7 @@ var PostgresQueryParser = class PostgresQueryParser {
|
|
|
17078
17108
|
const subParser = new PostgresQueryParser({
|
|
17079
17109
|
table: relatedTable,
|
|
17080
17110
|
alias: "a",
|
|
17111
|
+
dialect: this.dialect,
|
|
17081
17112
|
operations: constraintOps
|
|
17082
17113
|
});
|
|
17083
17114
|
subParser.parse();
|
|
@@ -17095,6 +17126,7 @@ var PostgresQueryParser = class PostgresQueryParser {
|
|
|
17095
17126
|
const subParser = new PostgresQueryParser({
|
|
17096
17127
|
table: data.table ?? alias,
|
|
17097
17128
|
alias,
|
|
17129
|
+
dialect: this.dialect,
|
|
17098
17130
|
operations: constraintOps
|
|
17099
17131
|
});
|
|
17100
17132
|
subParser.parse();
|
|
@@ -17122,7 +17154,7 @@ var PostgresQueryParser = class PostgresQueryParser {
|
|
|
17122
17154
|
const foreignField = "foreignField" in options ? options.foreignField : "";
|
|
17123
17155
|
const alias = "alias" in options ? options.alias : void 0;
|
|
17124
17156
|
const constraintOps = data.constraintOps ?? [];
|
|
17125
|
-
const quotedTable = this.
|
|
17157
|
+
const quotedTable = this.quoteTable(joinTable);
|
|
17126
17158
|
const tableRef = alias ? `${quotedTable} AS ${this.dialect.quoteIdentifier(alias)}` : quotedTable;
|
|
17127
17159
|
const tableAlias = alias ?? joinTable;
|
|
17128
17160
|
this.joinedTables.add(joinTable);
|
|
@@ -17134,6 +17166,7 @@ var PostgresQueryParser = class PostgresQueryParser {
|
|
|
17134
17166
|
const subParser = new PostgresQueryParser({
|
|
17135
17167
|
table: joinTable,
|
|
17136
17168
|
alias: tableAlias,
|
|
17169
|
+
dialect: this.dialect,
|
|
17137
17170
|
operations: whereOps
|
|
17138
17171
|
});
|
|
17139
17172
|
subParser.parse();
|
|
@@ -17175,11 +17208,11 @@ var PostgresQueryParser = class PostgresQueryParser {
|
|
|
17175
17208
|
const effectiveTable = tableAlias ?? defaultTable;
|
|
17176
17209
|
const parts = field.split(".");
|
|
17177
17210
|
let expression;
|
|
17178
|
-
if (parts.length === 1) expression = `${this.
|
|
17211
|
+
if (parts.length === 1) expression = `${this.quoteTable(effectiveTable)}.${this.dialect.quoteIdentifier(field)}`;
|
|
17179
17212
|
else if (parts.length === 2) {
|
|
17180
17213
|
const [first, second] = parts;
|
|
17181
17214
|
if (first === void 0 || second === void 0) throw new Error(`Cannot parse field path "${field}": expected two non-empty segments.`);
|
|
17182
|
-
if (this.isTableReference(first)) expression = `${this.
|
|
17215
|
+
if (this.isTableReference(first)) expression = `${this.quoteTable(first)}.${this.dialect.quoteIdentifier(second)}`;
|
|
17183
17216
|
else expression = this.buildJsonbPath(effectiveTable, first, [second]);
|
|
17184
17217
|
} else {
|
|
17185
17218
|
const [first, second, ...rest] = parts;
|
|
@@ -17201,6 +17234,9 @@ var PostgresQueryParser = class PostgresQueryParser {
|
|
|
17201
17234
|
if (this.joinedTables.has(name)) return true;
|
|
17202
17235
|
return false;
|
|
17203
17236
|
}
|
|
17237
|
+
quoteTable(table) {
|
|
17238
|
+
return this.dialect instanceof PostgresDialect ? this.dialect.quoteTableIdentifier(table) : this.dialect.quoteIdentifier(table);
|
|
17239
|
+
}
|
|
17204
17240
|
/**
|
|
17205
17241
|
* Build a JSONB path expression.
|
|
17206
17242
|
*
|
|
@@ -17221,7 +17257,7 @@ var PostgresQueryParser = class PostgresQueryParser {
|
|
|
17221
17257
|
* // Returns: "posts"."createdBy"->'address'->>'city'
|
|
17222
17258
|
*/
|
|
17223
17259
|
buildJsonbPath(table, column, path) {
|
|
17224
|
-
const quotedTable = this.
|
|
17260
|
+
const quotedTable = this.quoteTable(table);
|
|
17225
17261
|
const quotedColumn = this.dialect.quoteIdentifier(column);
|
|
17226
17262
|
if (path.length === 0) return `${quotedTable}.${quotedColumn}`;
|
|
17227
17263
|
let expression = `${quotedTable}.${quotedColumn}`;
|
|
@@ -17236,7 +17272,7 @@ var PostgresQueryParser = class PostgresQueryParser {
|
|
|
17236
17272
|
*/
|
|
17237
17273
|
processCrossJoin(data) {
|
|
17238
17274
|
const table = data.table;
|
|
17239
|
-
const quotedTable = this.
|
|
17275
|
+
const quotedTable = this.quoteTable(table);
|
|
17240
17276
|
this.joinClauses.push(`CROSS JOIN ${quotedTable}`);
|
|
17241
17277
|
}
|
|
17242
17278
|
/**
|
|
@@ -17585,7 +17621,7 @@ var PostgresQueryBuilder = class PostgresQueryBuilder extends QueryBuilder {
|
|
|
17585
17621
|
similarTo(column, embedding, alias = "score") {
|
|
17586
17622
|
const literal = `[${embedding.join(",")}]`;
|
|
17587
17623
|
const quotedCol = this.driver.dialect.quoteIdentifier(column);
|
|
17588
|
-
const quotedTable = this.driver.dialect.
|
|
17624
|
+
const quotedTable = this.driver.dialect.quoteTableIdentifier(this.table);
|
|
17589
17625
|
this.addOperation("selectRaw", {
|
|
17590
17626
|
expression: `${quotedTable}.*`,
|
|
17591
17627
|
bindings: []
|
|
@@ -17679,7 +17715,7 @@ var PostgresQueryBuilder = class PostgresQueryBuilder extends QueryBuilder {
|
|
|
17679
17715
|
}
|
|
17680
17716
|
/** Full-text search across the whole row, plus optional equality filters. */
|
|
17681
17717
|
textSearch(query, filters) {
|
|
17682
|
-
const table = this.driver.dialect.
|
|
17718
|
+
const table = this.driver.dialect.quoteTableIdentifier(this.table);
|
|
17683
17719
|
this.addOperation("whereRaw", {
|
|
17684
17720
|
expression: `to_tsvector('english', ${table}::text) @@ plainto_tsquery('english', ?)`,
|
|
17685
17721
|
bindings: [query]
|
|
@@ -17834,6 +17870,7 @@ var PostgresQueryBuilder = class PostgresQueryBuilder extends QueryBuilder {
|
|
|
17834
17870
|
if (this.fetchingCallback) await this.fetchingCallback(this);
|
|
17835
17871
|
const { query = "", bindings = [] } = new PostgresQueryParser({
|
|
17836
17872
|
table: this.table,
|
|
17873
|
+
dialect: this.driver.dialect,
|
|
17837
17874
|
operations: toParserOps(this.operations)
|
|
17838
17875
|
}).parse();
|
|
17839
17876
|
try {
|
|
@@ -17923,6 +17960,7 @@ var PostgresQueryBuilder = class PostgresQueryBuilder extends QueryBuilder {
|
|
|
17923
17960
|
}]);
|
|
17924
17961
|
const inner = new PostgresQueryParser({
|
|
17925
17962
|
table: this.table,
|
|
17963
|
+
dialect: this.driver.dialect,
|
|
17926
17964
|
operations: innerOps
|
|
17927
17965
|
}).parse();
|
|
17928
17966
|
query = `SELECT COUNT(*) AS "count" FROM (${inner.query}) AS "__count"`;
|
|
@@ -17934,6 +17972,7 @@ var PostgresQueryBuilder = class PostgresQueryBuilder extends QueryBuilder {
|
|
|
17934
17972
|
}]);
|
|
17935
17973
|
const parsed = new PostgresQueryParser({
|
|
17936
17974
|
table: this.table,
|
|
17975
|
+
dialect: this.driver.dialect,
|
|
17937
17976
|
operations: countOps
|
|
17938
17977
|
}).parse();
|
|
17939
17978
|
query = parsed.query ?? "";
|
|
@@ -18001,6 +18040,7 @@ var PostgresQueryBuilder = class PostgresQueryBuilder extends QueryBuilder {
|
|
|
18001
18040
|
]);
|
|
18002
18041
|
const { query = "", bindings = [] } = new PostgresQueryParser({
|
|
18003
18042
|
table: this.table,
|
|
18043
|
+
dialect: this.driver.dialect,
|
|
18004
18044
|
operations: probeOps
|
|
18005
18045
|
}).parse();
|
|
18006
18046
|
return (await this.driver.query(query, bindings)).rows.length > 0;
|
|
@@ -18024,7 +18064,7 @@ var PostgresQueryBuilder = class PostgresQueryBuilder extends QueryBuilder {
|
|
|
18024
18064
|
this.applyPendingScopes();
|
|
18025
18065
|
const { sql: filterSql, params: filterParams } = this.buildFilter();
|
|
18026
18066
|
const amountPlaceholder = `$${filterParams.length + 1}`;
|
|
18027
|
-
const updateSql = `UPDATE ${this.driver.dialect.
|
|
18067
|
+
const updateSql = `UPDATE ${this.driver.dialect.quoteTableIdentifier(this.table)} SET ${this.driver.dialect.quoteIdentifier(field)} = COALESCE(${this.driver.dialect.quoteIdentifier(field)}, 0) + ${amountPlaceholder} WHERE ctid IN (SELECT ctid FROM ${this.driver.dialect.quoteTableIdentifier(this.table)} ${filterSql} LIMIT 1 FOR UPDATE SKIP LOCKED) RETURNING ${this.driver.dialect.quoteIdentifier(field)}`;
|
|
18028
18068
|
return (await this.driver.query(updateSql, [...filterParams, amount])).rows[0]?.[field] ?? 0;
|
|
18029
18069
|
}
|
|
18030
18070
|
/** Decrement a numeric field. Returns new value. */
|
|
@@ -18036,7 +18076,7 @@ var PostgresQueryBuilder = class PostgresQueryBuilder extends QueryBuilder {
|
|
|
18036
18076
|
this.applyPendingScopes();
|
|
18037
18077
|
const { sql: filterSql, params: filterParams } = this.buildFilter();
|
|
18038
18078
|
const amountPlaceholder = `$${filterParams.length + 1}`;
|
|
18039
|
-
const updateSql = `UPDATE ${this.driver.dialect.
|
|
18079
|
+
const updateSql = `UPDATE ${this.driver.dialect.quoteTableIdentifier(this.table)} SET ${this.driver.dialect.quoteIdentifier(field)} = COALESCE(${this.driver.dialect.quoteIdentifier(field)}, 0) + ${amountPlaceholder}` + (filterSql ? ` WHERE ${filterSql.replace("WHERE ", "")}` : "");
|
|
18040
18080
|
return (await this.driver.query(updateSql, [...filterParams, amount])).rowCount ?? 0;
|
|
18041
18081
|
}
|
|
18042
18082
|
/** Decrement a field for all matching rows. Returns affected row count. */
|
|
@@ -18133,7 +18173,7 @@ var PostgresQueryBuilder = class PostgresQueryBuilder extends QueryBuilder {
|
|
|
18133
18173
|
async delete() {
|
|
18134
18174
|
this.applyPendingScopes();
|
|
18135
18175
|
const { sql, params } = this.buildFilter();
|
|
18136
|
-
const deleteSql = `DELETE FROM ${this.driver.dialect.
|
|
18176
|
+
const deleteSql = `DELETE FROM ${this.driver.dialect.quoteTableIdentifier(this.table)} ${sql}`;
|
|
18137
18177
|
return (await this.driver.query(deleteSql, params)).rowCount ?? 0;
|
|
18138
18178
|
}
|
|
18139
18179
|
/**
|
|
@@ -18145,7 +18185,7 @@ var PostgresQueryBuilder = class PostgresQueryBuilder extends QueryBuilder {
|
|
|
18145
18185
|
async deleteOne() {
|
|
18146
18186
|
this.applyPendingScopes();
|
|
18147
18187
|
const { sql: filterSql, params } = this.buildFilter();
|
|
18148
|
-
const quotedTable = this.driver.dialect.
|
|
18188
|
+
const quotedTable = this.driver.dialect.quoteTableIdentifier(this.table);
|
|
18149
18189
|
const deleteSql = `DELETE FROM ${quotedTable} WHERE ctid IN (SELECT ctid FROM ${quotedTable} ${filterSql} LIMIT 1 FOR UPDATE SKIP LOCKED)`;
|
|
18150
18190
|
return (await this.driver.query(deleteSql, params)).rowCount ?? 0;
|
|
18151
18191
|
}
|
|
@@ -18165,7 +18205,7 @@ var PostgresQueryBuilder = class PostgresQueryBuilder extends QueryBuilder {
|
|
|
18165
18205
|
setParams.push(value);
|
|
18166
18206
|
}
|
|
18167
18207
|
if (setClauses.length === 0) throw new Error("No update operations specified");
|
|
18168
|
-
const updateSql = `UPDATE ${this.driver.dialect.
|
|
18208
|
+
const updateSql = `UPDATE ${this.driver.dialect.quoteTableIdentifier(this.table)} SET ${setClauses.join(", ")}` + (filterSql ? ` ${filterSql}` : "");
|
|
18169
18209
|
return (await this.driver.query(updateSql, [...filterParams, ...setParams])).rowCount ?? 0;
|
|
18170
18210
|
}
|
|
18171
18211
|
/**
|
|
@@ -18177,7 +18217,7 @@ var PostgresQueryBuilder = class PostgresQueryBuilder extends QueryBuilder {
|
|
|
18177
18217
|
if (fields.length === 0) throw new Error("No update operations specified");
|
|
18178
18218
|
const { sql: filterSql, params: filterParams } = this.buildFilter();
|
|
18179
18219
|
const setClauses = fields.map((field) => `${this.driver.dialect.quoteIdentifier(field)} = NULL`);
|
|
18180
|
-
const updateSql = `UPDATE ${this.driver.dialect.
|
|
18220
|
+
const updateSql = `UPDATE ${this.driver.dialect.quoteTableIdentifier(this.table)} SET ${setClauses.join(", ")}` + (filterSql ? ` ${filterSql}` : "");
|
|
18181
18221
|
return (await this.driver.query(updateSql, filterParams)).rowCount ?? 0;
|
|
18182
18222
|
}
|
|
18183
18223
|
/**
|
|
@@ -18197,6 +18237,7 @@ var PostgresQueryBuilder = class PostgresQueryBuilder extends QueryBuilder {
|
|
|
18197
18237
|
this.applyGroupByAggregates();
|
|
18198
18238
|
return new PostgresQueryParser({
|
|
18199
18239
|
table: this.table,
|
|
18240
|
+
dialect: this.driver.dialect,
|
|
18200
18241
|
operations: toParserOps(this.operations)
|
|
18201
18242
|
}).parse();
|
|
18202
18243
|
}
|
|
@@ -18348,8 +18389,8 @@ var PostgresQueryBuilder = class PostgresQueryBuilder extends QueryBuilder {
|
|
|
18348
18389
|
*/
|
|
18349
18390
|
buildHasSubquery(opType, relationName, definition, RelatedModel, constraintOps, operator, count) {
|
|
18350
18391
|
const dialect = this.driver.dialect;
|
|
18351
|
-
const quotedSelfTable = dialect.
|
|
18352
|
-
const quotedRelatedTable = dialect.
|
|
18392
|
+
const quotedSelfTable = dialect.quoteTableIdentifier(this.table);
|
|
18393
|
+
const quotedRelatedTable = dialect.quoteTableIdentifier(RelatedModel.table);
|
|
18353
18394
|
const relationType = definition.type;
|
|
18354
18395
|
const selfModel = this.modelClass;
|
|
18355
18396
|
const conventions = this.dataSource?.relationDefaults;
|
|
@@ -18368,7 +18409,7 @@ var PostgresQueryBuilder = class PostgresQueryBuilder extends QueryBuilder {
|
|
|
18368
18409
|
joinCondition = `${quotedRelatedTable}.${dialect.quoteIdentifier(ownerKey)} = ${quotedSelfTable}.${dialect.quoteIdentifier(foreignKey)}`;
|
|
18369
18410
|
} else if (relationType === "belongsToMany") {
|
|
18370
18411
|
const pivotTableName = definition.pivot ?? inferPivotTable(selfModel?.name ?? "Model", RelatedModel.name, conventions);
|
|
18371
|
-
const quotedPivot = dialect.
|
|
18412
|
+
const quotedPivot = dialect.quoteTableIdentifier(pivotTableName);
|
|
18372
18413
|
const pivotLocalCol = definition.localKey ?? inferPivotKey(selfModel?.name ?? "Model", conventions);
|
|
18373
18414
|
const pivotForeignCol = definition.foreignKey ?? inferPivotKey(RelatedModel.name, conventions);
|
|
18374
18415
|
const selfPk = definition.pivotLocalKey ?? selfModel?.primaryKey ?? "id";
|
|
@@ -18426,7 +18467,7 @@ var PostgresQueryBuilder = class PostgresQueryBuilder extends QueryBuilder {
|
|
|
18426
18467
|
*/
|
|
18427
18468
|
ensureMainColumnsForCount() {
|
|
18428
18469
|
if (this.operations.some((op) => op.type === "select" || op.type === "selectRaw" || op.type === "selectRelatedColumns")) return;
|
|
18429
|
-
const quotedTable = this.driver.dialect.
|
|
18470
|
+
const quotedTable = this.driver.dialect.quoteTableIdentifier(this.table);
|
|
18430
18471
|
this.addOperation("selectRaw", {
|
|
18431
18472
|
expression: `${quotedTable}.*`,
|
|
18432
18473
|
bindings: []
|
|
@@ -18441,8 +18482,8 @@ var PostgresQueryBuilder = class PostgresQueryBuilder extends QueryBuilder {
|
|
|
18441
18482
|
buildCountSubquery(alias, relationName, definition, RelatedModel, constraintOps) {
|
|
18442
18483
|
const dialect = this.driver.dialect;
|
|
18443
18484
|
const quotedAlias = dialect.quoteIdentifier(alias);
|
|
18444
|
-
const quotedSelfTable = dialect.
|
|
18445
|
-
const quotedRelatedTable = dialect.
|
|
18485
|
+
const quotedSelfTable = dialect.quoteTableIdentifier(this.table);
|
|
18486
|
+
const quotedRelatedTable = dialect.quoteTableIdentifier(RelatedModel.table);
|
|
18446
18487
|
const relationType = definition.type;
|
|
18447
18488
|
const selfModel = this.modelClass;
|
|
18448
18489
|
const relatedMeta = RelatedModel;
|
|
@@ -18469,7 +18510,7 @@ var PostgresQueryBuilder = class PostgresQueryBuilder extends QueryBuilder {
|
|
|
18469
18510
|
}
|
|
18470
18511
|
if (relationType === "belongsToMany") {
|
|
18471
18512
|
const pivotTableName = definition.pivot ?? inferPivotTable(selfModel?.name ?? "Model", relatedMeta.name, conventions);
|
|
18472
|
-
const quotedPivot = dialect.
|
|
18513
|
+
const quotedPivot = dialect.quoteTableIdentifier(pivotTableName);
|
|
18473
18514
|
const pivotLocalCol = definition.localKey ?? inferPivotKey(selfModel?.name ?? "Model", conventions);
|
|
18474
18515
|
const pivotForeignCol = definition.foreignKey ?? inferPivotKey(relatedMeta.name, conventions);
|
|
18475
18516
|
const selfPk = definition.pivotLocalKey ?? selfModel?.primaryKey ?? "id";
|
|
@@ -18508,6 +18549,7 @@ var PostgresQueryBuilder = class PostgresQueryBuilder extends QueryBuilder {
|
|
|
18508
18549
|
};
|
|
18509
18550
|
const { query = "", bindings = [] } = new PostgresQueryParser({
|
|
18510
18551
|
table: relatedTable,
|
|
18552
|
+
dialect: this.driver.dialect,
|
|
18511
18553
|
operations: toParserOps(whereOps)
|
|
18512
18554
|
}).parse();
|
|
18513
18555
|
const match = query.match(/WHERE\s+(.+)$/);
|
|
@@ -18650,6 +18692,7 @@ var PostgresQueryBuilder = class PostgresQueryBuilder extends QueryBuilder {
|
|
|
18650
18692
|
};
|
|
18651
18693
|
const { query = "", bindings = [] } = new PostgresQueryParser({
|
|
18652
18694
|
table: this.table,
|
|
18695
|
+
dialect: this.driver.dialect,
|
|
18653
18696
|
operations: toParserOps(whereOps)
|
|
18654
18697
|
}).parse();
|
|
18655
18698
|
const whereMatch = query.match(/WHERE .+$/);
|
|
@@ -18997,6 +19040,34 @@ function assertPostgresUpdate(update, options) {
|
|
|
18997
19040
|
*
|
|
18998
19041
|
* @module cascade/drivers/postgres
|
|
18999
19042
|
*/
|
|
19043
|
+
const POSTGRES_INT8_OID = 20;
|
|
19044
|
+
const POSTGRES_INT8_ARRAY_OID = 1016;
|
|
19045
|
+
/**
|
|
19046
|
+
* Convert a PostgreSQL int8 result to a number only when it can be represented
|
|
19047
|
+
* exactly. PostgreSQL sends int8 values as strings by default to avoid silently
|
|
19048
|
+
* losing precision above JavaScript's safe-integer limit.
|
|
19049
|
+
*/
|
|
19050
|
+
function parsePostgresInt8(value) {
|
|
19051
|
+
if (value === null) return null;
|
|
19052
|
+
const numberValue = Number(value);
|
|
19053
|
+
return Number.isSafeInteger(numberValue) ? numberValue : value;
|
|
19054
|
+
}
|
|
19055
|
+
/**
|
|
19056
|
+
* Build pool-local type overrides without changing pg's process-wide registry.
|
|
19057
|
+
*/
|
|
19058
|
+
function createPostgresTypes(types) {
|
|
19059
|
+
return { getTypeParser(oid, format) {
|
|
19060
|
+
if (oid === POSTGRES_INT8_OID) return parsePostgresInt8;
|
|
19061
|
+
if (oid === POSTGRES_INT8_ARRAY_OID) {
|
|
19062
|
+
const parseArray = types.getTypeParser(oid, format);
|
|
19063
|
+
return (value) => {
|
|
19064
|
+
const array = parseArray(value);
|
|
19065
|
+
return Array.isArray(array) ? array.map(parsePostgresInt8) : array;
|
|
19066
|
+
};
|
|
19067
|
+
}
|
|
19068
|
+
return types.getTypeParser(oid, format);
|
|
19069
|
+
} };
|
|
19070
|
+
}
|
|
19000
19071
|
/**
|
|
19001
19072
|
* Build pg's `PoolConfig` from cascade's `PostgresPoolConfig`, coercing the
|
|
19002
19073
|
* string-typed connection fields.
|
|
@@ -19097,7 +19168,7 @@ var PostgresDriver = class {
|
|
|
19097
19168
|
/**
|
|
19098
19169
|
* SQL dialect for PostgreSQL-specific syntax.
|
|
19099
19170
|
*/
|
|
19100
|
-
dialect
|
|
19171
|
+
dialect;
|
|
19101
19172
|
/**
|
|
19102
19173
|
* PostgreSQL driver model defaults.
|
|
19103
19174
|
*
|
|
@@ -19168,6 +19239,7 @@ var PostgresDriver = class {
|
|
|
19168
19239
|
*/
|
|
19169
19240
|
constructor(config) {
|
|
19170
19241
|
this.config = config;
|
|
19242
|
+
this.dialect = new PostgresDialect({ naming: config.naming });
|
|
19171
19243
|
this._nativeArrayColumns = new Set(config.nativeArrayColumns ?? []);
|
|
19172
19244
|
}
|
|
19173
19245
|
/**
|
|
@@ -19209,6 +19281,7 @@ var PostgresDriver = class {
|
|
|
19209
19281
|
const pg = await loadPg();
|
|
19210
19282
|
try {
|
|
19211
19283
|
const poolConfig = buildPostgresPoolConfig(this.config);
|
|
19284
|
+
poolConfig.types = createPostgresTypes(pg.types);
|
|
19212
19285
|
_warlock_js_logger.log.info("database.postgres", "connection", `Connecting to database ${_mongez_copper.colors.bold(_mongez_copper.colors.yellowBright(poolConfig.database ?? ""))}`);
|
|
19213
19286
|
this._pool = new pg.Pool(poolConfig);
|
|
19214
19287
|
this._pool.on("error", (error) => {
|
|
@@ -19390,12 +19463,11 @@ var PostgresDriver = class {
|
|
|
19390
19463
|
*/
|
|
19391
19464
|
deserialize(data) {
|
|
19392
19465
|
const columnTypes = this.rowColumnTypes.get(data);
|
|
19393
|
-
if (
|
|
19394
|
-
for (const [key, value] of Object.entries(data)) {
|
|
19466
|
+
if (columnTypes) for (const [key, value] of Object.entries(data)) {
|
|
19395
19467
|
if (typeof value !== "string") continue;
|
|
19396
19468
|
data[key] = convertPostgresValue(value, columnTypes.get(key));
|
|
19397
19469
|
}
|
|
19398
|
-
return data;
|
|
19470
|
+
return this.config.naming === "snake_case" ? snakeKeysToCamel(data) : data;
|
|
19399
19471
|
}
|
|
19400
19472
|
/**
|
|
19401
19473
|
* Insert a single row into a table.
|
|
@@ -19418,7 +19490,7 @@ var PostgresDriver = class {
|
|
|
19418
19490
|
if (columns.length === 0) throw new Error("Cannot insert empty document");
|
|
19419
19491
|
const quotedColumns = columns.map((c) => this.dialect.quoteIdentifier(c)).join(", ");
|
|
19420
19492
|
const placeholders = columns.map((_, i) => this.dialect.placeholder(i + 1)).join(", ");
|
|
19421
|
-
const sql = `INSERT INTO ${this.dialect.
|
|
19493
|
+
const sql = `INSERT INTO ${this.dialect.quoteTableIdentifier(table)} (${quotedColumns}) VALUES (${placeholders}) RETURNING *`;
|
|
19422
19494
|
return { document: (await this.query(sql, values)).rows[0] };
|
|
19423
19495
|
}
|
|
19424
19496
|
/**
|
|
@@ -19440,7 +19512,7 @@ var PostgresDriver = class {
|
|
|
19440
19512
|
}
|
|
19441
19513
|
const columns = Array.from(allColumns);
|
|
19442
19514
|
const quotedColumns = columns.map((c) => this.dialect.quoteIdentifier(c)).join(", ");
|
|
19443
|
-
const quotedTable = this.dialect.
|
|
19515
|
+
const quotedTable = this.dialect.quoteTableIdentifier(table);
|
|
19444
19516
|
const valueSets = [];
|
|
19445
19517
|
const params = [];
|
|
19446
19518
|
let paramIndex = 1;
|
|
@@ -19512,7 +19584,7 @@ var PostgresDriver = class {
|
|
|
19512
19584
|
const { sql, params } = this.buildUpdateQuery(table, filter, operations, 1);
|
|
19513
19585
|
return (await this.query(`${sql} RETURNING *`, params)).rows[0] ?? null;
|
|
19514
19586
|
}
|
|
19515
|
-
const quotedTable = this.dialect.
|
|
19587
|
+
const quotedTable = this.dialect.quoteTableIdentifier(table);
|
|
19516
19588
|
const keyList = (qualifier) => primaryKey.map((column) => (qualifier ? `${qualifier}.` : "") + this.dialect.quoteIdentifier(column)).join(", ");
|
|
19517
19589
|
const set = this.buildSetClauses(table, operations, 1, returnBefore ? quotedTable : void 0);
|
|
19518
19590
|
if (set.clauses.length === 0) throw new Error("No update operations specified");
|
|
@@ -19558,7 +19630,7 @@ var PostgresDriver = class {
|
|
|
19558
19630
|
for (const key of Object.keys(operations.$unset ?? {})) delete insertRow[key];
|
|
19559
19631
|
const columns = Object.keys(insertRow).filter((key) => insertRow[key] !== void 0);
|
|
19560
19632
|
const params = columns.map((key) => this.serializeValue(key, insertRow[key], table));
|
|
19561
|
-
const quotedTable = this.dialect.
|
|
19633
|
+
const quotedTable = this.dialect.quoteTableIdentifier(table);
|
|
19562
19634
|
const quotedColumns = columns.map((column) => this.dialect.quoteIdentifier(column)).join(", ");
|
|
19563
19635
|
const placeholders = columns.map((_, index) => this.dialect.placeholder(index + 1)).join(", ");
|
|
19564
19636
|
const set = this.buildSetClauses(table, operations, columns.length + 1, quotedTable);
|
|
@@ -19626,7 +19698,7 @@ var PostgresDriver = class {
|
|
|
19626
19698
|
const serialized = this.serialize(document, table);
|
|
19627
19699
|
const columns = Object.keys(serialized);
|
|
19628
19700
|
const values = Object.values(serialized);
|
|
19629
|
-
const quotedTable = this.dialect.
|
|
19701
|
+
const quotedTable = this.dialect.quoteTableIdentifier(table);
|
|
19630
19702
|
const setClauses = columns.map((col, i) => `${this.dialect.quoteIdentifier(col)} = ${this.dialect.placeholder(i + 1)}`).join(", ");
|
|
19631
19703
|
const { whereClause, whereParams } = this.buildWhereClause(filter, columns.length + 1);
|
|
19632
19704
|
const sql = `UPDATE ${quotedTable} SET ${setClauses} ${whereClause} RETURNING *`;
|
|
@@ -19649,7 +19721,7 @@ var PostgresDriver = class {
|
|
|
19649
19721
|
const columns = Object.keys(serialized);
|
|
19650
19722
|
const values = Object.values(serialized);
|
|
19651
19723
|
if (columns.length === 0) throw new Error("Cannot upsert empty document");
|
|
19652
|
-
const quotedTable = this.dialect.
|
|
19724
|
+
const quotedTable = this.dialect.quoteTableIdentifier(table);
|
|
19653
19725
|
const quotedColumns = columns.map((c) => this.dialect.quoteIdentifier(c)).join(", ");
|
|
19654
19726
|
const placeholders = columns.map((_, i) => this.dialect.placeholder(i + 1)).join(", ");
|
|
19655
19727
|
const conflictColumns = options?.conflictColumns ?? Object.keys(filter);
|
|
@@ -19672,7 +19744,7 @@ var PostgresDriver = class {
|
|
|
19672
19744
|
* @returns The deleted row or null
|
|
19673
19745
|
*/
|
|
19674
19746
|
async findOneAndDelete(table, filter, _options) {
|
|
19675
|
-
const quotedTable = this.dialect.
|
|
19747
|
+
const quotedTable = this.dialect.quoteTableIdentifier(table);
|
|
19676
19748
|
const { whereClause, whereParams } = this.buildWhereClause(filter, 1);
|
|
19677
19749
|
const sql = `${this.singleRowTarget("DELETE FROM " + quotedTable, quotedTable, filter, whereClause)} RETURNING *`;
|
|
19678
19750
|
const result = await this.query(sql, whereParams);
|
|
@@ -19687,7 +19759,7 @@ var PostgresDriver = class {
|
|
|
19687
19759
|
* @returns Number of deleted rows (0 or 1)
|
|
19688
19760
|
*/
|
|
19689
19761
|
async delete(table, filter, _options) {
|
|
19690
|
-
const quotedTable = this.dialect.
|
|
19762
|
+
const quotedTable = this.dialect.quoteTableIdentifier(table);
|
|
19691
19763
|
const { whereClause, whereParams } = this.buildWhereClause(filter ?? {}, 1);
|
|
19692
19764
|
const sql = this.singleRowTarget("DELETE FROM " + quotedTable, quotedTable, filter ?? {}, whereClause);
|
|
19693
19765
|
return (await this.query(sql, whereParams)).rowCount ?? 0;
|
|
@@ -19701,7 +19773,7 @@ var PostgresDriver = class {
|
|
|
19701
19773
|
* @returns Number of deleted rows
|
|
19702
19774
|
*/
|
|
19703
19775
|
async deleteMany(table, filter, _options) {
|
|
19704
|
-
const quotedTable = this.dialect.
|
|
19776
|
+
const quotedTable = this.dialect.quoteTableIdentifier(table);
|
|
19705
19777
|
const { whereClause, whereParams } = this.buildWhereClause(filter ?? {}, 1);
|
|
19706
19778
|
const sql = `DELETE FROM ${quotedTable} ${whereClause}`;
|
|
19707
19779
|
return (await this.query(sql, whereParams)).rowCount ?? 0;
|
|
@@ -19717,7 +19789,7 @@ var PostgresDriver = class {
|
|
|
19717
19789
|
* @returns Number of deleted rows (always 0 for TRUNCATE)
|
|
19718
19790
|
*/
|
|
19719
19791
|
async truncateTable(table, options) {
|
|
19720
|
-
const quotedTable = this.dialect.
|
|
19792
|
+
const quotedTable = this.dialect.quoteTableIdentifier(table);
|
|
19721
19793
|
const cascadeClause = options?.cascade ? " CASCADE" : "";
|
|
19722
19794
|
await this.query(`TRUNCATE TABLE ${quotedTable} RESTART IDENTITY${cascadeClause}`);
|
|
19723
19795
|
return 0;
|
|
@@ -20174,7 +20246,7 @@ var PostgresDriver = class {
|
|
|
20174
20246
|
const set = this.buildSetClauses(table, assertPostgresUpdate(update), 1);
|
|
20175
20247
|
if (set.clauses.length === 0) throw new Error("No update operations specified");
|
|
20176
20248
|
const params = [...set.params];
|
|
20177
|
-
const quotedTable = this.dialect.
|
|
20249
|
+
const quotedTable = this.dialect.quoteTableIdentifier(table);
|
|
20178
20250
|
const { whereClause, whereParams } = this.buildWhereClause(filter, set.nextIndex);
|
|
20179
20251
|
params.push(...whereParams);
|
|
20180
20252
|
const head = `UPDATE ${quotedTable} SET ${set.clauses.join(", ")}`;
|
|
@@ -20380,6 +20452,7 @@ async function connectToDatabase(options) {
|
|
|
20380
20452
|
user: options.username,
|
|
20381
20453
|
password: options.password,
|
|
20382
20454
|
logging: options.logging,
|
|
20455
|
+
...pickPostgresDriverOptions(options.driverOptions),
|
|
20383
20456
|
...options.clientOptions
|
|
20384
20457
|
});
|
|
20385
20458
|
break;
|
|
@@ -20427,6 +20500,11 @@ function getDatabaseDriver() {
|
|
|
20427
20500
|
async function transaction(fn, options) {
|
|
20428
20501
|
return getDatabaseDriver().transaction(fn, options);
|
|
20429
20502
|
}
|
|
20503
|
+
/** The Cascade-side Postgres driver options `driverOptions` may carry. */
|
|
20504
|
+
function pickPostgresDriverOptions(driverOptions) {
|
|
20505
|
+
const naming = driverOptions?.naming;
|
|
20506
|
+
return naming === void 0 ? {} : { naming };
|
|
20507
|
+
}
|
|
20430
20508
|
|
|
20431
20509
|
//#endregion
|
|
20432
20510
|
//#region ../cascade/src/utils/database-writer.utils.ts
|
|
@@ -25301,6 +25379,7 @@ exports.modelSync = modelSync;
|
|
|
25301
25379
|
exports.objectCol = objectCol;
|
|
25302
25380
|
exports.onceConnected = onceConnected;
|
|
25303
25381
|
exports.onceDisconnected = onceDisconnected;
|
|
25382
|
+
exports.parsePostgresInt8 = parsePostgresInt8;
|
|
25304
25383
|
exports.point = point;
|
|
25305
25384
|
exports.polygon = polygon;
|
|
25306
25385
|
exports.registerModelInRegistry = registerModelInRegistry;
|