tempest-db-js 0.1.0 → 0.3.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/README.md +70 -2
- package/dist/bin.cjs +1191 -0
- package/dist/bin.cjs.map +1 -0
- package/dist/bin.d.cts +27 -0
- package/dist/bin.d.ts +27 -0
- package/dist/bin.js +111 -0
- package/dist/bin.js.map +1 -0
- package/dist/chunk-OP7FRDI5.js +1225 -0
- package/dist/chunk-OP7FRDI5.js.map +1 -0
- package/dist/{chunk-F36ZSQAN.js → chunk-Q32CBI2A.js} +584 -53
- package/dist/chunk-Q32CBI2A.js.map +1 -0
- package/dist/index.cjs +591 -50
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +320 -15
- package/dist/index.d.ts +320 -15
- package/dist/index.js +1 -1
- package/dist/migrations/index.cjs +338 -53
- package/dist/migrations/index.cjs.map +1 -1
- package/dist/migrations/index.d.cts +116 -5
- package/dist/migrations/index.d.ts +116 -5
- package/dist/migrations/index.js +2 -923
- package/dist/migrations/index.js.map +1 -1
- package/package.json +13 -5
- package/dist/chunk-F36ZSQAN.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -45,6 +45,29 @@ var OPERATORS = [
|
|
|
45
45
|
"between",
|
|
46
46
|
"isNull"
|
|
47
47
|
];
|
|
48
|
+
var Agg = class {
|
|
49
|
+
constructor(fn, column2) {
|
|
50
|
+
this.fn = fn;
|
|
51
|
+
this.column = column2;
|
|
52
|
+
}
|
|
53
|
+
fn;
|
|
54
|
+
column;
|
|
55
|
+
};
|
|
56
|
+
function count() {
|
|
57
|
+
return new Agg("count", "*");
|
|
58
|
+
}
|
|
59
|
+
function sum(column2) {
|
|
60
|
+
return new Agg("sum", column2);
|
|
61
|
+
}
|
|
62
|
+
function avg(column2) {
|
|
63
|
+
return new Agg("avg", column2);
|
|
64
|
+
}
|
|
65
|
+
function min(column2) {
|
|
66
|
+
return new Agg("min", column2);
|
|
67
|
+
}
|
|
68
|
+
function max(column2) {
|
|
69
|
+
return new Agg("max", column2);
|
|
70
|
+
}
|
|
48
71
|
var SelectBuilder = class _SelectBuilder {
|
|
49
72
|
constructor(node, source) {
|
|
50
73
|
this.node = node;
|
|
@@ -59,6 +82,37 @@ var SelectBuilder = class _SelectBuilder {
|
|
|
59
82
|
where(input) {
|
|
60
83
|
return this.with({ where: toCondNode(input) });
|
|
61
84
|
}
|
|
85
|
+
/** Emit `SELECT DISTINCT` — drop duplicate rows. */
|
|
86
|
+
distinct() {
|
|
87
|
+
return this.with({ distinct: true });
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Group by columns and compute aggregates. The result row is the grouped
|
|
91
|
+
* columns (typed from the model) plus one field per aggregate alias.
|
|
92
|
+
*
|
|
93
|
+
* @param groupBy The columns to group by (checked against the model). Pass `[]`
|
|
94
|
+
* for a whole-table aggregate.
|
|
95
|
+
* @param spec A map of result alias → aggregate expression ({@link count},
|
|
96
|
+
* {@link sum}, {@link avg}, {@link min}, {@link max}).
|
|
97
|
+
* @returns A builder whose row is `Pick<Full, K> & { [alias]: aggResult }`.
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```ts
|
|
101
|
+
* select(Order).aggregate(["status"], { n: count(), total: sum("amount") });
|
|
102
|
+
* // rows: { status: string; n: number; total: number | null }[]
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
105
|
+
aggregate(groupBy, spec) {
|
|
106
|
+
const aggregates = Object.entries(spec).map(([alias, agg]) => ({
|
|
107
|
+
fn: agg.fn,
|
|
108
|
+
column: agg.column,
|
|
109
|
+
alias
|
|
110
|
+
}));
|
|
111
|
+
return new _SelectBuilder(
|
|
112
|
+
{ ...this.node, aggregates, groupBy },
|
|
113
|
+
this.source
|
|
114
|
+
);
|
|
115
|
+
}
|
|
62
116
|
/** Order by a column of `Full`. */
|
|
63
117
|
orderBy(column2, direction = "asc") {
|
|
64
118
|
return this.with({
|
|
@@ -80,6 +134,9 @@ function select(model, columns) {
|
|
|
80
134
|
kind: "select",
|
|
81
135
|
table: model.tablename,
|
|
82
136
|
columns: columns ?? "*",
|
|
137
|
+
distinct: false,
|
|
138
|
+
aggregates: [],
|
|
139
|
+
groupBy: [],
|
|
83
140
|
where: void 0,
|
|
84
141
|
orderBy: [],
|
|
85
142
|
limit: void 0,
|
|
@@ -105,6 +162,25 @@ var InsertBuilder = class _InsertBuilder {
|
|
|
105
162
|
const list = Array.isArray(rows) ? rows : [rows];
|
|
106
163
|
return this.with({ values: list });
|
|
107
164
|
}
|
|
165
|
+
/**
|
|
166
|
+
* On a unique/PK conflict on `target`, do nothing (skip the row).
|
|
167
|
+
*
|
|
168
|
+
* @param target The conflicting column(s) — a unique or primary key.
|
|
169
|
+
*/
|
|
170
|
+
onConflictDoNothing(target) {
|
|
171
|
+
return this.with({ onConflict: { target, update: "nothing" } });
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* On a unique/PK conflict on `target`, overwrite the given columns (upsert).
|
|
175
|
+
*
|
|
176
|
+
* @param target The conflicting column(s) — a unique or primary key.
|
|
177
|
+
* @param set The columns to update with new values.
|
|
178
|
+
*/
|
|
179
|
+
onConflictDoUpdate(target, set) {
|
|
180
|
+
return this.with({
|
|
181
|
+
onConflict: { target, update: set }
|
|
182
|
+
});
|
|
183
|
+
}
|
|
108
184
|
returning(columns) {
|
|
109
185
|
return this.with({ returning: columns ?? "*" });
|
|
110
186
|
}
|
|
@@ -212,7 +288,9 @@ var DIALECT_ALIASES = {
|
|
|
212
288
|
sqlite3: "sqlite",
|
|
213
289
|
postgresql: "postgresql",
|
|
214
290
|
postgres: "postgresql",
|
|
215
|
-
pg: "postgresql"
|
|
291
|
+
pg: "postgresql",
|
|
292
|
+
mysql: "mysql",
|
|
293
|
+
mariadb: "mysql"
|
|
216
294
|
};
|
|
217
295
|
function splitScheme(scheme) {
|
|
218
296
|
const plus = scheme.indexOf("+");
|
|
@@ -252,10 +330,10 @@ function parseSqlite(raw, driver, rest) {
|
|
|
252
330
|
raw
|
|
253
331
|
};
|
|
254
332
|
}
|
|
255
|
-
function
|
|
333
|
+
function parseNetworkUrl(raw, driver, rest, dialect) {
|
|
256
334
|
let parsed;
|
|
257
335
|
try {
|
|
258
|
-
parsed = new URL(
|
|
336
|
+
parsed = new URL(`${dialect}:${rest}`);
|
|
259
337
|
} catch {
|
|
260
338
|
throw new InvalidDatabaseUrl(raw, "could not parse host/credentials");
|
|
261
339
|
}
|
|
@@ -263,7 +341,7 @@ function parsePostgres(raw, driver, rest) {
|
|
|
263
341
|
const options = {};
|
|
264
342
|
for (const [key, value] of parsed.searchParams) options[key] = value;
|
|
265
343
|
return {
|
|
266
|
-
dialect
|
|
344
|
+
dialect,
|
|
267
345
|
driver,
|
|
268
346
|
host: parsed.hostname || null,
|
|
269
347
|
port: parsed.port ? Number(parsed.port) : null,
|
|
@@ -288,7 +366,8 @@ function parseDatabaseUrl(url) {
|
|
|
288
366
|
throw new InvalidDatabaseUrl(url, `unknown dialect ${JSON.stringify(base)}`);
|
|
289
367
|
}
|
|
290
368
|
const rest = url.slice(schemeEnd + 1);
|
|
291
|
-
|
|
369
|
+
if (dialect === "sqlite") return parseSqlite(url, driver, rest);
|
|
370
|
+
return parseNetworkUrl(url, driver, rest, dialect);
|
|
292
371
|
}
|
|
293
372
|
function detectDialect(url) {
|
|
294
373
|
return parseDatabaseUrl(url).dialect;
|
|
@@ -408,12 +487,49 @@ function fromDict(model, data) {
|
|
|
408
487
|
function parse(model, json) {
|
|
409
488
|
return fromDict(model, JSON.parse(json));
|
|
410
489
|
}
|
|
490
|
+
function decoderForKind(kind) {
|
|
491
|
+
switch (kind) {
|
|
492
|
+
case "bigint":
|
|
493
|
+
return (v) => v == null ? null : typeof v === "bigint" ? v : BigInt(v);
|
|
494
|
+
case "date":
|
|
495
|
+
case "datetime":
|
|
496
|
+
case "timestamp":
|
|
497
|
+
return (v) => v == null ? null : v instanceof Date ? v : new Date(v);
|
|
498
|
+
case "blob":
|
|
499
|
+
return (v) => v == null ? null : v instanceof Uint8Array ? v : fromBase64(v);
|
|
500
|
+
case "json":
|
|
501
|
+
return (v) => v == null ? null : typeof v === "string" ? JSON.parse(v) : v;
|
|
502
|
+
case "numeric":
|
|
503
|
+
return (v) => v == null ? null : typeof v === "string" ? v : String(v);
|
|
504
|
+
case "boolean":
|
|
505
|
+
return (v) => v == null ? null : typeof v === "boolean" ? v : v === 1 || v === "true";
|
|
506
|
+
case "smallint":
|
|
507
|
+
case "integer":
|
|
508
|
+
case "real":
|
|
509
|
+
case "double":
|
|
510
|
+
return (v) => v == null ? null : typeof v === "number" ? v : Number(v);
|
|
511
|
+
default:
|
|
512
|
+
return null;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
var decoderCache = /* @__PURE__ */ new WeakMap();
|
|
516
|
+
function decodersFor(model) {
|
|
517
|
+
const cached = decoderCache.get(model);
|
|
518
|
+
if (cached) return cached;
|
|
519
|
+
const map = /* @__PURE__ */ new Map();
|
|
520
|
+
for (const [name, col] of Object.entries(columnsOf(model))) {
|
|
521
|
+
const decoder = decoderForKind(col.type.kind);
|
|
522
|
+
if (decoder) map.set(name, decoder);
|
|
523
|
+
}
|
|
524
|
+
decoderCache.set(model, map);
|
|
525
|
+
return map;
|
|
526
|
+
}
|
|
411
527
|
function coerceRow(model, raw) {
|
|
412
|
-
const
|
|
528
|
+
const decoders = decodersFor(model);
|
|
413
529
|
const out = {};
|
|
414
|
-
for (const
|
|
415
|
-
const
|
|
416
|
-
out[name] =
|
|
530
|
+
for (const name of Object.keys(raw)) {
|
|
531
|
+
const decode2 = decoders.get(name);
|
|
532
|
+
out[name] = decode2 ? decode2(raw[name]) : raw[name];
|
|
417
533
|
}
|
|
418
534
|
return out;
|
|
419
535
|
}
|
|
@@ -438,10 +554,29 @@ var Params = class {
|
|
|
438
554
|
return this.placeholder(this.values.length);
|
|
439
555
|
}
|
|
440
556
|
};
|
|
441
|
-
var BaseDialect = class {
|
|
442
|
-
/**
|
|
557
|
+
var BaseDialect = class _BaseDialect {
|
|
558
|
+
/**
|
|
559
|
+
* INSERT SQL templates keyed by structure (dialect|table|columns|rowCount|
|
|
560
|
+
* returning). Shared across dialect instances — the key namespaces by dialect
|
|
561
|
+
* name, and the placeholder text is dialect-specific but structure-determined.
|
|
562
|
+
*/
|
|
563
|
+
static insertTemplates = /* @__PURE__ */ new Map();
|
|
564
|
+
/** Quoted-identifier cache (see {@link quoteId}). Shared across dialects. */
|
|
565
|
+
static quotedIds = /* @__PURE__ */ new Map();
|
|
566
|
+
/**
|
|
567
|
+
* Quote an identifier (column/table) for the active dialect.
|
|
568
|
+
*
|
|
569
|
+
* Memoized: identifiers form a small, stable set (column/table names), but this
|
|
570
|
+
* runs for every identifier on every compile. Caching the quoted form removes a
|
|
571
|
+
* regex-replace + string allocation from the hot path. The standard double-quote
|
|
572
|
+
* form is identical across both dialects, so one shared cache is correct.
|
|
573
|
+
*/
|
|
443
574
|
quoteId(name) {
|
|
444
|
-
|
|
575
|
+
const cached = _BaseDialect.quotedIds.get(name);
|
|
576
|
+
if (cached !== void 0) return cached;
|
|
577
|
+
const quoted = `"${name.replace(/"/g, '""')}"`;
|
|
578
|
+
_BaseDialect.quotedIds.set(name, quoted);
|
|
579
|
+
return quoted;
|
|
445
580
|
}
|
|
446
581
|
/** Compile any node to `{ sql, params }`. */
|
|
447
582
|
compile(node) {
|
|
@@ -474,10 +609,23 @@ var BaseDialect = class {
|
|
|
474
609
|
}
|
|
475
610
|
// ---- statements -------------------------------------------------------
|
|
476
611
|
compileSelect(node, params) {
|
|
477
|
-
|
|
478
|
-
|
|
612
|
+
let cols;
|
|
613
|
+
if (node.aggregates.length > 0) {
|
|
614
|
+
const groupSel = node.groupBy.map((c) => this.quoteId(c));
|
|
615
|
+
const aggSel = node.aggregates.map((a) => {
|
|
616
|
+
const inner = a.column === "*" ? "*" : this.quoteId(a.column);
|
|
617
|
+
return `${a.fn.toUpperCase()}(${inner}) AS ${this.quoteId(a.alias)}`;
|
|
618
|
+
});
|
|
619
|
+
cols = [...groupSel, ...aggSel].join(", ");
|
|
620
|
+
} else {
|
|
621
|
+
cols = node.columns === "*" ? "*" : node.columns.map((c) => this.quoteId(c)).join(", ");
|
|
622
|
+
}
|
|
623
|
+
let sql2 = `SELECT ${node.distinct ? "DISTINCT " : ""}${cols} FROM ${this.quoteId(node.table)}`;
|
|
479
624
|
const where = this.compileCondition(node.where, params, (k) => this.quoteId(k));
|
|
480
625
|
if (where) sql2 += ` WHERE ${where}`;
|
|
626
|
+
if (node.groupBy.length > 0) {
|
|
627
|
+
sql2 += ` GROUP BY ${node.groupBy.map((c) => this.quoteId(c)).join(", ")}`;
|
|
628
|
+
}
|
|
481
629
|
if (node.orderBy.length > 0) {
|
|
482
630
|
const terms = node.orderBy.map(
|
|
483
631
|
(t) => `${this.quoteId(t.column)} ${t.direction === "desc" ? "DESC" : "ASC"}`
|
|
@@ -490,14 +638,58 @@ var BaseDialect = class {
|
|
|
490
638
|
}
|
|
491
639
|
compileInsert(node, params) {
|
|
492
640
|
const columns = node.values.length > 0 ? Object.keys(node.values[0]) : [];
|
|
641
|
+
for (const row of node.values) {
|
|
642
|
+
for (const c of columns) params.bind(row[c] ?? null);
|
|
643
|
+
}
|
|
644
|
+
const conflictCols = node.onConflict && node.onConflict.update !== "nothing" ? Object.keys(node.onConflict.update) : [];
|
|
645
|
+
for (const c of conflictCols) {
|
|
646
|
+
params.bind((node.onConflict?.update)[c]);
|
|
647
|
+
}
|
|
648
|
+
return this.insertTemplate(node, columns, conflictCols);
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* The INSERT SQL template for a given structure, cached across calls.
|
|
652
|
+
*
|
|
653
|
+
* The text depends only on (dialect, table, columns, row count, returning,
|
|
654
|
+
* conflict shape) — never on the bound values — and placeholder positions are
|
|
655
|
+
* deterministic from the counts (a fresh statement always starts binding at 1).
|
|
656
|
+
* So a per-row insert loop compiles the string once and reuses it every row.
|
|
657
|
+
*/
|
|
658
|
+
insertTemplate(node, columns, conflictCols) {
|
|
659
|
+
const returningKey = node.returning === null ? "" : node.returning === "*" ? "*" : node.returning.join(",");
|
|
660
|
+
const conflictKey = node.onConflict ? `${node.onConflict.target.join(",")}>${node.onConflict.update === "nothing" ? "nothing" : conflictCols.join(",")}` : "";
|
|
661
|
+
const key = `${this.name}|${node.table}|${columns.join(",")}|${node.values.length}|${returningKey}|${conflictKey}`;
|
|
662
|
+
const cached = _BaseDialect.insertTemplates.get(key);
|
|
663
|
+
if (cached !== void 0) return cached;
|
|
493
664
|
const colSql = columns.map((c) => this.quoteId(c)).join(", ");
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
).join(", ");
|
|
665
|
+
let position = 0;
|
|
666
|
+
const rowsSql = node.values.map(() => `(${columns.map(() => this.placeholder(++position)).join(", ")})`).join(", ");
|
|
497
667
|
let sql2 = `INSERT INTO ${this.quoteId(node.table)} (${colSql}) VALUES ${rowsSql}`;
|
|
668
|
+
if (node.onConflict) {
|
|
669
|
+
sql2 += this.renderConflict(
|
|
670
|
+
node.onConflict,
|
|
671
|
+
conflictCols,
|
|
672
|
+
() => this.placeholder(++position)
|
|
673
|
+
);
|
|
674
|
+
}
|
|
498
675
|
sql2 += this.compileReturning(node.returning);
|
|
676
|
+
_BaseDialect.insertTemplates.set(key, sql2);
|
|
499
677
|
return sql2;
|
|
500
678
|
}
|
|
679
|
+
/**
|
|
680
|
+
* Render the conflict-handling clause. Standard SQL (SQLite/PostgreSQL) uses
|
|
681
|
+
* `ON CONFLICT (...) DO NOTHING | DO UPDATE SET ...`; MySQL overrides this.
|
|
682
|
+
*
|
|
683
|
+
* @param onConflict The conflict clause from the node.
|
|
684
|
+
* @param conflictCols The columns to overwrite on `DO UPDATE` (empty for nothing).
|
|
685
|
+
* @param nextPlaceholder Yields the next positional placeholder (advances the count).
|
|
686
|
+
*/
|
|
687
|
+
renderConflict(onConflict, conflictCols, nextPlaceholder) {
|
|
688
|
+
const target = onConflict.target.map((c) => this.quoteId(c)).join(", ");
|
|
689
|
+
if (onConflict.update === "nothing") return ` ON CONFLICT (${target}) DO NOTHING`;
|
|
690
|
+
const assignments = conflictCols.map((c) => `${this.quoteId(c)} = ${nextPlaceholder()}`).join(", ");
|
|
691
|
+
return ` ON CONFLICT (${target}) DO UPDATE SET ${assignments}`;
|
|
692
|
+
}
|
|
501
693
|
compileUpdate(node, params) {
|
|
502
694
|
const sets = Object.entries(node.set).map(([col, value]) => `${this.quoteId(col)} = ${params.bind(value)}`).join(", ");
|
|
503
695
|
let sql2 = `UPDATE ${this.quoteId(node.table)} SET ${sets}`;
|
|
@@ -635,8 +827,46 @@ var PostgresDialect = class extends BaseDialect {
|
|
|
635
827
|
return `${column2} ILIKE ${param}`;
|
|
636
828
|
}
|
|
637
829
|
};
|
|
830
|
+
var mysqlQuotedIds = /* @__PURE__ */ new Map();
|
|
831
|
+
var MysqlDialect = class extends BaseDialect {
|
|
832
|
+
name = "mysql";
|
|
833
|
+
placeholder() {
|
|
834
|
+
return "?";
|
|
835
|
+
}
|
|
836
|
+
ilike(column2, param) {
|
|
837
|
+
return `${column2} LIKE ${param}`;
|
|
838
|
+
}
|
|
839
|
+
quoteId(name) {
|
|
840
|
+
const cached = mysqlQuotedIds.get(name);
|
|
841
|
+
if (cached !== void 0) return cached;
|
|
842
|
+
const quoted = `\`${name.replace(/`/g, "``")}\``;
|
|
843
|
+
mysqlQuotedIds.set(name, quoted);
|
|
844
|
+
return quoted;
|
|
845
|
+
}
|
|
846
|
+
renderConflict(onConflict, conflictCols, nextPlaceholder) {
|
|
847
|
+
if (onConflict.update === "nothing") {
|
|
848
|
+
const col = this.quoteId(onConflict.target[0] ?? "id");
|
|
849
|
+
return ` ON DUPLICATE KEY UPDATE ${col} = ${col}`;
|
|
850
|
+
}
|
|
851
|
+
const assignments = conflictCols.map((c) => `${this.quoteId(c)} = ${nextPlaceholder()}`).join(", ");
|
|
852
|
+
return ` ON DUPLICATE KEY UPDATE ${assignments}`;
|
|
853
|
+
}
|
|
854
|
+
compileReturning(returning) {
|
|
855
|
+
if (returning === null) return "";
|
|
856
|
+
throw new Error(
|
|
857
|
+
"RETURNING is not supported on MySQL \u2014 insert, then SELECT by key (e.g. LAST_INSERT_ID())."
|
|
858
|
+
);
|
|
859
|
+
}
|
|
860
|
+
};
|
|
638
861
|
function getDialect(name) {
|
|
639
|
-
|
|
862
|
+
switch (name) {
|
|
863
|
+
case "sqlite":
|
|
864
|
+
return new SqliteDialect();
|
|
865
|
+
case "postgresql":
|
|
866
|
+
return new PostgresDialect();
|
|
867
|
+
case "mysql":
|
|
868
|
+
return new MysqlDialect();
|
|
869
|
+
}
|
|
640
870
|
}
|
|
641
871
|
|
|
642
872
|
// src/join.ts
|
|
@@ -817,6 +1047,101 @@ var BaseRepository = class {
|
|
|
817
1047
|
}
|
|
818
1048
|
};
|
|
819
1049
|
|
|
1050
|
+
// src/active-record.ts
|
|
1051
|
+
function primaryKeyOf2(model) {
|
|
1052
|
+
for (const [name, col] of Object.entries(columnsOf(model))) {
|
|
1053
|
+
if (col.flags.primaryKey) return name;
|
|
1054
|
+
}
|
|
1055
|
+
throw new Error(`${model.tablename} has no primary key`);
|
|
1056
|
+
}
|
|
1057
|
+
var ActiveRecord = class {
|
|
1058
|
+
constructor(model, session, data) {
|
|
1059
|
+
this.model = model;
|
|
1060
|
+
this.session = session;
|
|
1061
|
+
this.data = data;
|
|
1062
|
+
this.pk = primaryKeyOf2(model);
|
|
1063
|
+
}
|
|
1064
|
+
model;
|
|
1065
|
+
session;
|
|
1066
|
+
data;
|
|
1067
|
+
pk;
|
|
1068
|
+
/** The primary-key value of the wrapped row. */
|
|
1069
|
+
pkValue() {
|
|
1070
|
+
return this.data[this.pk];
|
|
1071
|
+
}
|
|
1072
|
+
pkFilter() {
|
|
1073
|
+
return { [this.pk]: this.pkValue() };
|
|
1074
|
+
}
|
|
1075
|
+
/**
|
|
1076
|
+
* Persist the current `data` — insert if new, otherwise overwrite the existing
|
|
1077
|
+
* row (upsert on the primary key). Refreshes `data` from the returned row.
|
|
1078
|
+
*
|
|
1079
|
+
* @returns This wrapper, for chaining.
|
|
1080
|
+
*/
|
|
1081
|
+
async save() {
|
|
1082
|
+
const cols = columnsOf(this.model);
|
|
1083
|
+
const rowData = this.data;
|
|
1084
|
+
const setPatch = {};
|
|
1085
|
+
for (const c of Object.keys(rowData)) {
|
|
1086
|
+
if (c !== this.pk && c in cols) setPatch[c] = rowData[c];
|
|
1087
|
+
}
|
|
1088
|
+
const saved = await this.session.execute(
|
|
1089
|
+
insert(this.model).values(this.data).onConflictDoUpdate(
|
|
1090
|
+
[this.pk],
|
|
1091
|
+
setPatch
|
|
1092
|
+
).returning()
|
|
1093
|
+
).one();
|
|
1094
|
+
this.data = saved;
|
|
1095
|
+
return this;
|
|
1096
|
+
}
|
|
1097
|
+
/**
|
|
1098
|
+
* Update the given columns for this row and merge them into `data`.
|
|
1099
|
+
*
|
|
1100
|
+
* @param patch The columns to change.
|
|
1101
|
+
* @returns This wrapper, for chaining.
|
|
1102
|
+
*/
|
|
1103
|
+
async update(patch) {
|
|
1104
|
+
await this.session.execute(update(this.model).set(patch).where(this.pkFilter()));
|
|
1105
|
+
this.data = { ...this.data, ...patch };
|
|
1106
|
+
return this;
|
|
1107
|
+
}
|
|
1108
|
+
/**
|
|
1109
|
+
* Delete this row.
|
|
1110
|
+
*
|
|
1111
|
+
* @returns The number of rows affected (0 or 1).
|
|
1112
|
+
*/
|
|
1113
|
+
async delete() {
|
|
1114
|
+
return this.session.execute(del(this.model).where(this.pkFilter())).rowsAffected();
|
|
1115
|
+
}
|
|
1116
|
+
/**
|
|
1117
|
+
* Re-fetch this row by primary key and refresh `data`.
|
|
1118
|
+
*
|
|
1119
|
+
* @returns This wrapper, for chaining.
|
|
1120
|
+
* @throws When the row no longer exists.
|
|
1121
|
+
*/
|
|
1122
|
+
async reload() {
|
|
1123
|
+
const fresh = await this.session.execute(select(this.model).where(this.pkFilter())).first();
|
|
1124
|
+
if (fresh === null) {
|
|
1125
|
+
throw new Error(
|
|
1126
|
+
`${this.model.tablename} row ${JSON.stringify(this.pkValue())} not found on reload`
|
|
1127
|
+
);
|
|
1128
|
+
}
|
|
1129
|
+
this.data = fresh;
|
|
1130
|
+
return this;
|
|
1131
|
+
}
|
|
1132
|
+
};
|
|
1133
|
+
function activeRecord(model, session) {
|
|
1134
|
+
const pk = primaryKeyOf2(model);
|
|
1135
|
+
return {
|
|
1136
|
+
wrap: (row) => new ActiveRecord(model, session, row),
|
|
1137
|
+
create: (data) => new ActiveRecord(model, session, data),
|
|
1138
|
+
async get(id) {
|
|
1139
|
+
const row = await session.execute(select(model).where({ [pk]: id })).first();
|
|
1140
|
+
return row === null ? null : new ActiveRecord(model, session, row);
|
|
1141
|
+
}
|
|
1142
|
+
};
|
|
1143
|
+
}
|
|
1144
|
+
|
|
820
1145
|
// src/relations.ts
|
|
821
1146
|
function hasMany(target, keys) {
|
|
822
1147
|
return {
|
|
@@ -879,6 +1204,14 @@ function encodeSqliteParam(value) {
|
|
|
879
1204
|
var NodeSqliteDriver = class _NodeSqliteDriver {
|
|
880
1205
|
// biome-ignore lint/suspicious/noExplicitAny: node:sqlite DatabaseSync has no shipped types here.
|
|
881
1206
|
db;
|
|
1207
|
+
/**
|
|
1208
|
+
* Prepared-statement cache keyed by SQL text. tempest-db-js always
|
|
1209
|
+
* parameterizes, so a query shape maps to one stable SQL string — reusing the
|
|
1210
|
+
* compiled statement avoids re-`prepare()` on every call (the dominant cost of
|
|
1211
|
+
* per-row inserts and point lookups).
|
|
1212
|
+
*/
|
|
1213
|
+
// biome-ignore lint/suspicious/noExplicitAny: node:sqlite StatementSync has no shipped types here.
|
|
1214
|
+
statements = /* @__PURE__ */ new Map();
|
|
882
1215
|
// biome-ignore lint/suspicious/noExplicitAny: accept an already-open DatabaseSync handle.
|
|
883
1216
|
constructor(database) {
|
|
884
1217
|
this.db = database;
|
|
@@ -888,8 +1221,17 @@ var NodeSqliteDriver = class _NodeSqliteDriver {
|
|
|
888
1221
|
const { DatabaseSync } = nodeRequire("node:sqlite");
|
|
889
1222
|
return new _NodeSqliteDriver(new DatabaseSync(path));
|
|
890
1223
|
}
|
|
891
|
-
|
|
1224
|
+
/** Return the cached prepared statement for `sql`, preparing it on first use. */
|
|
1225
|
+
// biome-ignore lint/suspicious/noExplicitAny: statement type is unavailable here.
|
|
1226
|
+
prepare(sql2) {
|
|
1227
|
+
const cached = this.statements.get(sql2);
|
|
1228
|
+
if (cached) return cached;
|
|
892
1229
|
const stmt = this.db.prepare(sql2);
|
|
1230
|
+
this.statements.set(sql2, stmt);
|
|
1231
|
+
return stmt;
|
|
1232
|
+
}
|
|
1233
|
+
execute(sql2, params) {
|
|
1234
|
+
const stmt = this.prepare(sql2);
|
|
893
1235
|
const bound = params.map(encodeSqliteParam);
|
|
894
1236
|
if (returnsRows(sql2)) {
|
|
895
1237
|
return { rows: stmt.all(...bound), changes: 0 };
|
|
@@ -898,11 +1240,12 @@ var NodeSqliteDriver = class _NodeSqliteDriver {
|
|
|
898
1240
|
return { rows: [], changes: Number(info.changes ?? 0) };
|
|
899
1241
|
}
|
|
900
1242
|
*iterate(sql2, params) {
|
|
901
|
-
const stmt = this.
|
|
1243
|
+
const stmt = this.prepare(sql2);
|
|
902
1244
|
const bound = params.map(encodeSqliteParam);
|
|
903
1245
|
yield* stmt.iterate(...bound);
|
|
904
1246
|
}
|
|
905
1247
|
close() {
|
|
1248
|
+
this.statements.clear();
|
|
906
1249
|
this.db.close();
|
|
907
1250
|
}
|
|
908
1251
|
};
|
|
@@ -944,6 +1287,36 @@ var NoResultError = class extends Error {
|
|
|
944
1287
|
this.name = "NoResultError";
|
|
945
1288
|
}
|
|
946
1289
|
};
|
|
1290
|
+
function previewParam(value) {
|
|
1291
|
+
if (value === null || value === void 0) return "null";
|
|
1292
|
+
if (value instanceof Uint8Array) return `<${value.length} bytes>`;
|
|
1293
|
+
const text = typeof value === "string" ? value : String(value);
|
|
1294
|
+
return text.length > 64 ? `${text.slice(0, 61)}...` : text;
|
|
1295
|
+
}
|
|
1296
|
+
var QueryExecutionError = class extends Error {
|
|
1297
|
+
constructor(cause, sql2, params) {
|
|
1298
|
+
const reason = cause instanceof Error ? cause.message : String(cause);
|
|
1299
|
+
super(
|
|
1300
|
+
`Query failed: ${reason}
|
|
1301
|
+
SQL: ${sql2}
|
|
1302
|
+
params: [${params.map(previewParam).join(", ")}]`
|
|
1303
|
+
);
|
|
1304
|
+
this.cause = cause;
|
|
1305
|
+
this.sql = sql2;
|
|
1306
|
+
this.params = params;
|
|
1307
|
+
this.name = "QueryExecutionError";
|
|
1308
|
+
}
|
|
1309
|
+
cause;
|
|
1310
|
+
sql;
|
|
1311
|
+
params;
|
|
1312
|
+
};
|
|
1313
|
+
function emitLog(logger, sql2, params) {
|
|
1314
|
+
if (!logger) return;
|
|
1315
|
+
try {
|
|
1316
|
+
logger({ sql: sql2, params });
|
|
1317
|
+
} catch {
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
947
1320
|
function firstScalar(row) {
|
|
948
1321
|
if (!row) return null;
|
|
949
1322
|
const keys = Object.keys(row);
|
|
@@ -1013,29 +1386,40 @@ var AsyncResult = class {
|
|
|
1013
1386
|
};
|
|
1014
1387
|
var savepointCounter = 0;
|
|
1015
1388
|
var SyncSession = class {
|
|
1016
|
-
constructor(driver, dialect) {
|
|
1389
|
+
constructor(driver, dialect, logger) {
|
|
1017
1390
|
this.driver = driver;
|
|
1018
1391
|
this.dialect = dialect;
|
|
1392
|
+
this.logger = logger;
|
|
1019
1393
|
}
|
|
1020
1394
|
driver;
|
|
1021
1395
|
dialect;
|
|
1396
|
+
logger;
|
|
1397
|
+
/** Log, run, and error-wrap one raw statement. */
|
|
1398
|
+
exec(sql2, params) {
|
|
1399
|
+
emitLog(this.logger, sql2, params);
|
|
1400
|
+
try {
|
|
1401
|
+
return this.driver.execute(sql2, params);
|
|
1402
|
+
} catch (error) {
|
|
1403
|
+
throw new QueryExecutionError(error, sql2, params);
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1022
1406
|
/** Compile, run, and coerce a builder into a result. */
|
|
1023
1407
|
execute(builder) {
|
|
1024
1408
|
const node = builder.node;
|
|
1025
1409
|
const { sql: sql2, params } = this.dialect.compile(node);
|
|
1026
|
-
const result = this.
|
|
1410
|
+
const result = this.exec(sql2, params);
|
|
1027
1411
|
const rows = mapRows(builder, result.rows);
|
|
1028
1412
|
return new SyncResult(rows, result.changes);
|
|
1029
1413
|
}
|
|
1030
1414
|
/** Run `fn` inside a transaction: commit on success, rollback on throw. */
|
|
1031
1415
|
transaction(fn) {
|
|
1032
|
-
this.
|
|
1416
|
+
this.exec("BEGIN", []);
|
|
1033
1417
|
try {
|
|
1034
1418
|
const out = fn(this);
|
|
1035
|
-
this.
|
|
1419
|
+
this.exec("COMMIT", []);
|
|
1036
1420
|
return out;
|
|
1037
1421
|
} catch (error) {
|
|
1038
|
-
this.
|
|
1422
|
+
this.exec("ROLLBACK", []);
|
|
1039
1423
|
throw error;
|
|
1040
1424
|
}
|
|
1041
1425
|
}
|
|
@@ -1043,13 +1427,13 @@ var SyncSession = class {
|
|
|
1043
1427
|
beginNested(fn) {
|
|
1044
1428
|
savepointCounter += 1;
|
|
1045
1429
|
const name = `qsp_${savepointCounter}`;
|
|
1046
|
-
this.
|
|
1430
|
+
this.exec(`SAVEPOINT ${name}`, []);
|
|
1047
1431
|
try {
|
|
1048
1432
|
const out = fn(this);
|
|
1049
|
-
this.
|
|
1433
|
+
this.exec(`RELEASE ${name}`, []);
|
|
1050
1434
|
return out;
|
|
1051
1435
|
} catch (error) {
|
|
1052
|
-
this.
|
|
1436
|
+
this.exec(`ROLLBACK TO ${name}`, []);
|
|
1053
1437
|
throw error;
|
|
1054
1438
|
}
|
|
1055
1439
|
}
|
|
@@ -1060,31 +1444,51 @@ var SyncSession = class {
|
|
|
1060
1444
|
*stream(builder) {
|
|
1061
1445
|
const node = builder.node;
|
|
1062
1446
|
const { sql: sql2, params } = this.dialect.compile(node);
|
|
1447
|
+
emitLog(this.logger, sql2, params);
|
|
1063
1448
|
if (this.driver.iterate) {
|
|
1064
|
-
|
|
1065
|
-
|
|
1449
|
+
try {
|
|
1450
|
+
for (const raw of this.driver.iterate(sql2, params)) {
|
|
1451
|
+
yield coerceOne(builder, raw);
|
|
1452
|
+
}
|
|
1453
|
+
} catch (error) {
|
|
1454
|
+
throw new QueryExecutionError(error, sql2, params);
|
|
1066
1455
|
}
|
|
1067
1456
|
return;
|
|
1068
1457
|
}
|
|
1069
|
-
for (const raw of this.
|
|
1458
|
+
for (const raw of this.exec(sql2, params).rows) {
|
|
1070
1459
|
yield coerceOne(builder, raw);
|
|
1071
1460
|
}
|
|
1072
1461
|
}
|
|
1073
1462
|
close() {
|
|
1074
1463
|
this.driver.close();
|
|
1075
1464
|
}
|
|
1465
|
+
/** `using session = ...` closes the driver when the scope exits. */
|
|
1466
|
+
[Symbol.dispose]() {
|
|
1467
|
+
this.close();
|
|
1468
|
+
}
|
|
1076
1469
|
};
|
|
1077
|
-
var AsyncSession = class {
|
|
1078
|
-
constructor(driver, dialect) {
|
|
1470
|
+
var AsyncSession = class _AsyncSession {
|
|
1471
|
+
constructor(driver, dialect, logger) {
|
|
1079
1472
|
this.driver = driver;
|
|
1080
1473
|
this.dialect = dialect;
|
|
1474
|
+
this.logger = logger;
|
|
1081
1475
|
}
|
|
1082
1476
|
driver;
|
|
1083
1477
|
dialect;
|
|
1478
|
+
logger;
|
|
1479
|
+
/** Log, run, and error-wrap one raw statement. */
|
|
1480
|
+
async exec(sql2, params) {
|
|
1481
|
+
emitLog(this.logger, sql2, params);
|
|
1482
|
+
try {
|
|
1483
|
+
return await this.driver.execute(sql2, params);
|
|
1484
|
+
} catch (error) {
|
|
1485
|
+
throw new QueryExecutionError(error, sql2, params);
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1084
1488
|
execute(builder) {
|
|
1085
1489
|
const node = builder.node;
|
|
1086
1490
|
const { sql: sql2, params } = this.dialect.compile(node);
|
|
1087
|
-
const inner = this.
|
|
1491
|
+
const inner = this.exec(sql2, params).then((result) => {
|
|
1088
1492
|
const rows = mapRows(builder, result.rows);
|
|
1089
1493
|
return new SyncResult(rows, result.changes);
|
|
1090
1494
|
});
|
|
@@ -1094,40 +1498,66 @@ var AsyncSession = class {
|
|
|
1094
1498
|
async *stream(builder) {
|
|
1095
1499
|
const node = builder.node;
|
|
1096
1500
|
const { sql: sql2, params } = this.dialect.compile(node);
|
|
1501
|
+
emitLog(this.logger, sql2, params);
|
|
1097
1502
|
if (this.driver.iterate) {
|
|
1098
|
-
|
|
1099
|
-
|
|
1503
|
+
try {
|
|
1504
|
+
for await (const raw of this.driver.iterate(sql2, params)) {
|
|
1505
|
+
yield coerceOne(builder, raw);
|
|
1506
|
+
}
|
|
1507
|
+
} catch (error) {
|
|
1508
|
+
throw new QueryExecutionError(error, sql2, params);
|
|
1100
1509
|
}
|
|
1101
1510
|
return;
|
|
1102
1511
|
}
|
|
1103
|
-
const result = await this.
|
|
1512
|
+
const result = await this.exec(sql2, params);
|
|
1104
1513
|
for (const raw of result.rows) {
|
|
1105
1514
|
yield coerceOne(builder, raw);
|
|
1106
1515
|
}
|
|
1107
1516
|
}
|
|
1108
1517
|
async transaction(fn) {
|
|
1109
|
-
|
|
1518
|
+
if (this.driver.reserve) {
|
|
1519
|
+
const reserved = await this.driver.reserve();
|
|
1520
|
+
const scoped = new _AsyncSession(reserved, this.dialect, this.logger);
|
|
1521
|
+
try {
|
|
1522
|
+
await scoped.exec("BEGIN", []);
|
|
1523
|
+
const out = await fn(scoped);
|
|
1524
|
+
await scoped.exec("COMMIT", []);
|
|
1525
|
+
return out;
|
|
1526
|
+
} catch (error) {
|
|
1527
|
+
await scoped.exec("ROLLBACK", []);
|
|
1528
|
+
throw error;
|
|
1529
|
+
} finally {
|
|
1530
|
+
await reserved.release();
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
await this.exec("BEGIN", []);
|
|
1110
1534
|
try {
|
|
1111
1535
|
const out = await fn(this);
|
|
1112
|
-
await this.
|
|
1536
|
+
await this.exec("COMMIT", []);
|
|
1113
1537
|
return out;
|
|
1114
1538
|
} catch (error) {
|
|
1115
|
-
await this.
|
|
1539
|
+
await this.exec("ROLLBACK", []);
|
|
1116
1540
|
throw error;
|
|
1117
1541
|
}
|
|
1118
1542
|
}
|
|
1119
1543
|
async close() {
|
|
1120
1544
|
await this.driver.close();
|
|
1121
1545
|
}
|
|
1546
|
+
/** `await using session = ...` closes the driver when the scope exits. */
|
|
1547
|
+
async [Symbol.asyncDispose]() {
|
|
1548
|
+
await this.close();
|
|
1549
|
+
}
|
|
1122
1550
|
};
|
|
1123
1551
|
var SyncEngine = class {
|
|
1124
|
-
constructor(driver) {
|
|
1552
|
+
constructor(driver, logger) {
|
|
1125
1553
|
this.driver = driver;
|
|
1554
|
+
this.logger = logger;
|
|
1126
1555
|
}
|
|
1127
1556
|
driver;
|
|
1557
|
+
logger;
|
|
1128
1558
|
dialect = "sqlite";
|
|
1129
1559
|
session() {
|
|
1130
|
-
return new SyncSession(this.driver, getDialect("sqlite"));
|
|
1560
|
+
return new SyncSession(this.driver, getDialect("sqlite"), this.logger);
|
|
1131
1561
|
}
|
|
1132
1562
|
transaction(fn) {
|
|
1133
1563
|
return this.session().transaction(fn);
|
|
@@ -1135,16 +1565,22 @@ var SyncEngine = class {
|
|
|
1135
1565
|
close() {
|
|
1136
1566
|
this.driver.close();
|
|
1137
1567
|
}
|
|
1568
|
+
/** `using engine = createSyncEngine(...)` closes the pool when the scope exits. */
|
|
1569
|
+
[Symbol.dispose]() {
|
|
1570
|
+
this.close();
|
|
1571
|
+
}
|
|
1138
1572
|
};
|
|
1139
1573
|
var AsyncEngine = class {
|
|
1140
|
-
constructor(driver, dialect) {
|
|
1574
|
+
constructor(driver, dialect, logger) {
|
|
1141
1575
|
this.driver = driver;
|
|
1142
1576
|
this.dialect = dialect;
|
|
1577
|
+
this.logger = logger;
|
|
1143
1578
|
}
|
|
1144
1579
|
driver;
|
|
1145
1580
|
dialect;
|
|
1581
|
+
logger;
|
|
1146
1582
|
session() {
|
|
1147
|
-
return new AsyncSession(this.driver, getDialect(this.dialect));
|
|
1583
|
+
return new AsyncSession(this.driver, getDialect(this.dialect), this.logger);
|
|
1148
1584
|
}
|
|
1149
1585
|
transaction(fn) {
|
|
1150
1586
|
return this.session().transaction(fn);
|
|
@@ -1152,6 +1588,10 @@ var AsyncEngine = class {
|
|
|
1152
1588
|
async close() {
|
|
1153
1589
|
await this.driver.close();
|
|
1154
1590
|
}
|
|
1591
|
+
/** `await using engine = createEngine(...)` closes the pool when the scope exits. */
|
|
1592
|
+
async [Symbol.asyncDispose]() {
|
|
1593
|
+
await this.close();
|
|
1594
|
+
}
|
|
1155
1595
|
};
|
|
1156
1596
|
function asAsync(driver) {
|
|
1157
1597
|
const syncIterate = driver.iterate?.bind(driver);
|
|
@@ -1175,17 +1615,93 @@ function createSyncEngine(url, options) {
|
|
|
1175
1615
|
`createSyncEngine supports only SQLite; ${parsed.dialect} is async-only \u2014 use createEngine.`
|
|
1176
1616
|
);
|
|
1177
1617
|
}
|
|
1178
|
-
return new SyncEngine(
|
|
1618
|
+
return new SyncEngine(
|
|
1619
|
+
openSqliteDriver(parsed.database ?? ":memory:"),
|
|
1620
|
+
options?.onQuery
|
|
1621
|
+
);
|
|
1179
1622
|
}
|
|
1180
1623
|
function createEngine(url, options) {
|
|
1181
1624
|
const parsed = parseDatabaseUrl(url);
|
|
1182
1625
|
if (parsed.dialect === "sqlite") {
|
|
1183
1626
|
return new AsyncEngine(
|
|
1184
1627
|
asAsync(openSqliteDriver(parsed.database ?? ":memory:")),
|
|
1185
|
-
"sqlite"
|
|
1628
|
+
"sqlite",
|
|
1629
|
+
options?.onQuery
|
|
1630
|
+
);
|
|
1631
|
+
}
|
|
1632
|
+
if (parsed.dialect === "mysql") {
|
|
1633
|
+
return new AsyncEngine(
|
|
1634
|
+
createMysqlDriver(parsed.raw, options?.pool),
|
|
1635
|
+
"mysql",
|
|
1636
|
+
options?.onQuery
|
|
1186
1637
|
);
|
|
1187
1638
|
}
|
|
1188
|
-
return new AsyncEngine(
|
|
1639
|
+
return new AsyncEngine(
|
|
1640
|
+
createPostgresDriver(parsed.raw, options?.pool),
|
|
1641
|
+
"postgresql",
|
|
1642
|
+
options?.onQuery
|
|
1643
|
+
);
|
|
1644
|
+
}
|
|
1645
|
+
function encodeMysqlParam(value) {
|
|
1646
|
+
if (value === void 0 || value === null) return null;
|
|
1647
|
+
if (typeof value === "boolean") return value ? 1 : 0;
|
|
1648
|
+
if (value instanceof Uint8Array) return value;
|
|
1649
|
+
if (value instanceof Date) return value;
|
|
1650
|
+
if (typeof value === "object") return JSON.stringify(value);
|
|
1651
|
+
return value;
|
|
1652
|
+
}
|
|
1653
|
+
function toMysqlResult(rows) {
|
|
1654
|
+
if (Array.isArray(rows)) {
|
|
1655
|
+
return { rows, changes: 0 };
|
|
1656
|
+
}
|
|
1657
|
+
const header = rows;
|
|
1658
|
+
return { rows: [], changes: header.affectedRows ?? 0 };
|
|
1659
|
+
}
|
|
1660
|
+
function createMysqlDriver(url, pool) {
|
|
1661
|
+
let poolHandle;
|
|
1662
|
+
const ensure = async () => {
|
|
1663
|
+
if (poolHandle) return;
|
|
1664
|
+
const moduleName = "mysql2/promise";
|
|
1665
|
+
const mod = await import(
|
|
1666
|
+
/* @vite-ignore */
|
|
1667
|
+
moduleName
|
|
1668
|
+
);
|
|
1669
|
+
const opts = { uri: url };
|
|
1670
|
+
if (pool?.size !== void 0) opts.connectionLimit = pool.size;
|
|
1671
|
+
if (pool?.idleTimeoutMs !== void 0) opts.idleTimeout = pool.idleTimeoutMs;
|
|
1672
|
+
if (pool?.connectTimeoutMs !== void 0) opts.connectTimeout = pool.connectTimeoutMs;
|
|
1673
|
+
poolHandle = mod.createPool(opts);
|
|
1674
|
+
};
|
|
1675
|
+
const runOn = async (queryable, sql2, params) => {
|
|
1676
|
+
const [rows] = await queryable.query(sql2, params.map(encodeMysqlParam));
|
|
1677
|
+
return toMysqlResult(rows);
|
|
1678
|
+
};
|
|
1679
|
+
return {
|
|
1680
|
+
async execute(sql2, params) {
|
|
1681
|
+
await ensure();
|
|
1682
|
+
return runOn(poolHandle, sql2, params);
|
|
1683
|
+
},
|
|
1684
|
+
async reserve() {
|
|
1685
|
+
await ensure();
|
|
1686
|
+
const conn = await poolHandle.getConnection();
|
|
1687
|
+
return {
|
|
1688
|
+
execute: (sql2, params) => runOn(conn, sql2, params),
|
|
1689
|
+
async release() {
|
|
1690
|
+
conn.release();
|
|
1691
|
+
},
|
|
1692
|
+
async close() {
|
|
1693
|
+
conn.release();
|
|
1694
|
+
}
|
|
1695
|
+
};
|
|
1696
|
+
},
|
|
1697
|
+
async close() {
|
|
1698
|
+
if (poolHandle) await poolHandle.end();
|
|
1699
|
+
}
|
|
1700
|
+
};
|
|
1701
|
+
}
|
|
1702
|
+
function toPostgresResult(rows) {
|
|
1703
|
+
const arr = rows;
|
|
1704
|
+
return { rows: Array.from(arr), changes: arr.count ?? arr.length };
|
|
1189
1705
|
}
|
|
1190
1706
|
function createPostgresDriver(url, pool) {
|
|
1191
1707
|
let client;
|
|
@@ -1208,10 +1724,21 @@ function createPostgresDriver(url, pool) {
|
|
|
1208
1724
|
return {
|
|
1209
1725
|
async execute(sql2, params) {
|
|
1210
1726
|
await ensure();
|
|
1211
|
-
|
|
1727
|
+
return toPostgresResult(await client.unsafe(sql2, params));
|
|
1728
|
+
},
|
|
1729
|
+
async reserve() {
|
|
1730
|
+
await ensure();
|
|
1731
|
+
const conn = await client.reserve();
|
|
1212
1732
|
return {
|
|
1213
|
-
|
|
1214
|
-
|
|
1733
|
+
async execute(sql2, params) {
|
|
1734
|
+
return toPostgresResult(await conn.unsafe(sql2, params));
|
|
1735
|
+
},
|
|
1736
|
+
async release() {
|
|
1737
|
+
conn.release();
|
|
1738
|
+
},
|
|
1739
|
+
async close() {
|
|
1740
|
+
conn.release();
|
|
1741
|
+
}
|
|
1215
1742
|
};
|
|
1216
1743
|
},
|
|
1217
1744
|
async close() {
|
|
@@ -1347,7 +1874,10 @@ var column = {
|
|
|
1347
1874
|
var Model = class {
|
|
1348
1875
|
static tablename;
|
|
1349
1876
|
};
|
|
1877
|
+
var columnsCache = /* @__PURE__ */ new WeakMap();
|
|
1350
1878
|
function columnsOf(model) {
|
|
1879
|
+
const cached = columnsCache.get(model);
|
|
1880
|
+
if (cached) return cached;
|
|
1351
1881
|
const instance = new model();
|
|
1352
1882
|
const out = {};
|
|
1353
1883
|
for (const [key, value] of Object.entries(instance)) {
|
|
@@ -1355,9 +1885,12 @@ function columnsOf(model) {
|
|
|
1355
1885
|
out[key] = value;
|
|
1356
1886
|
}
|
|
1357
1887
|
}
|
|
1888
|
+
columnsCache.set(model, out);
|
|
1358
1889
|
return out;
|
|
1359
1890
|
}
|
|
1360
1891
|
|
|
1892
|
+
exports.ActiveRecord = ActiveRecord;
|
|
1893
|
+
exports.Agg = Agg;
|
|
1361
1894
|
exports.AsyncEngine = AsyncEngine;
|
|
1362
1895
|
exports.AsyncResult = AsyncResult;
|
|
1363
1896
|
exports.AsyncSession = AsyncSession;
|
|
@@ -1369,10 +1902,12 @@ exports.InsertBuilder = InsertBuilder;
|
|
|
1369
1902
|
exports.InvalidDatabaseUrl = InvalidDatabaseUrl;
|
|
1370
1903
|
exports.JoinBuilder = JoinBuilder;
|
|
1371
1904
|
exports.Model = Model;
|
|
1905
|
+
exports.MysqlDialect = MysqlDialect;
|
|
1372
1906
|
exports.NoResultError = NoResultError;
|
|
1373
1907
|
exports.NodeSqliteDriver = NodeSqliteDriver;
|
|
1374
1908
|
exports.OPERATORS = OPERATORS;
|
|
1375
1909
|
exports.PostgresDialect = PostgresDialect;
|
|
1910
|
+
exports.QueryExecutionError = QueryExecutionError;
|
|
1376
1911
|
exports.RecordNotFound = RecordNotFound;
|
|
1377
1912
|
exports.SelectBuilder = SelectBuilder;
|
|
1378
1913
|
exports.SqliteDialect = SqliteDialect;
|
|
@@ -1381,10 +1916,13 @@ exports.SyncResult = SyncResult;
|
|
|
1381
1916
|
exports.SyncSession = SyncSession;
|
|
1382
1917
|
exports.UpdateBuilder = UpdateBuilder;
|
|
1383
1918
|
exports.ValidationError = ValidationError;
|
|
1919
|
+
exports.activeRecord = activeRecord;
|
|
1384
1920
|
exports.and = and;
|
|
1921
|
+
exports.avg = avg;
|
|
1385
1922
|
exports.belongsTo = belongsTo;
|
|
1386
1923
|
exports.column = column;
|
|
1387
1924
|
exports.columnsOf = columnsOf;
|
|
1925
|
+
exports.count = count;
|
|
1388
1926
|
exports.createEngine = createEngine;
|
|
1389
1927
|
exports.createSyncEngine = createSyncEngine;
|
|
1390
1928
|
exports.del = del;
|
|
@@ -1396,6 +1934,8 @@ exports.insert = insert;
|
|
|
1396
1934
|
exports.isCondition = isCondition;
|
|
1397
1935
|
exports.join = join;
|
|
1398
1936
|
exports.loadRelations = loadRelations;
|
|
1937
|
+
exports.max = max;
|
|
1938
|
+
exports.min = min;
|
|
1399
1939
|
exports.not = not;
|
|
1400
1940
|
exports.or = or;
|
|
1401
1941
|
exports.parse = parse;
|
|
@@ -1403,6 +1943,7 @@ exports.parseDatabaseUrl = parseDatabaseUrl;
|
|
|
1403
1943
|
exports.select = select;
|
|
1404
1944
|
exports.sql = sql;
|
|
1405
1945
|
exports.stringify = stringify;
|
|
1946
|
+
exports.sum = sum;
|
|
1406
1947
|
exports.toCondNode = toCondNode;
|
|
1407
1948
|
exports.toDict = toDict;
|
|
1408
1949
|
exports.toJSON = toJSON;
|