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