@nodefony/drizzle 10.0.0-alpha.9 → 10.0.0-beta.2
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 +28 -26
- package/dist/_virtual/{_@oxc-project_runtime@0.151.0 → _@oxc-project_runtime@0.152.0}/helpers/esm/decorate.js +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.151.0 → _@oxc-project_runtime@0.152.0}/helpers/esm/decorateMetadata.js +1 -1
- package/dist/index.js +5 -5
- package/dist/nodefony/command/orm-generate.js +3 -3
- package/dist/nodefony/config/defineModuleConfig.js +2 -2
- package/dist/nodefony/entity/userTable.js +1 -1
- package/dist/nodefony/registerStores.js +2 -2
- package/dist/nodefony/service/DrizzleService.js +26 -12
- package/dist/nodefony/src/DrizzleAuditStore.js +1 -1
- package/dist/nodefony/src/connectorTarget.js +1 -1
- package/dist/nodefony/src/migrator/DrizzleMigrator.js +3 -3
- package/dist/nodefony/src/migrator/adopt.js +1 -1
- package/dist/nodefony/src/migrator/destructive.js +2 -3
- package/dist/nodefony/src/migrator/drivers/mysqlDriver.js +1 -1
- package/dist/nodefony/src/migrator/drivers/postgresDriver.js +1 -1
- package/dist/nodefony/src/migrator/drivers/sqliteDriver.js +6 -3
- package/dist/nodefony/src/migrator/explain.js +7 -10
- package/dist/nodefony/src/migrator/history.js +25 -2
- package/dist/nodefony/src/migrator/kit.js +23 -9
- package/dist/nodefony/src/migrator/rebuildCopy.js +6 -6
- package/dist/nodefony/src/migrator/resolve.js +4 -4
- package/dist/nodefony/src/migrator/sources.js +13 -8
- package/dist/nodefony/src/orm-core/DrizzleOrm.js +55 -32
- package/dist/nodefony/src/orm-core/DrizzleRepository.js +142 -40
- package/dist/nodefony/src/queryKit.js +5 -5
- package/dist/nodefony/src/sqliteJournal.js +54 -0
- package/dist/types/nodefony/src/DrizzleUserRepository.d.ts +1 -1
- package/dist/types/nodefony/src/connectorTarget.d.ts +1 -1
- package/dist/types/nodefony/src/migrator/DrizzleMigrator.d.ts +4 -4
- package/dist/types/nodefony/src/migrator/destructive.d.ts +1 -1
- package/dist/types/nodefony/src/migrator/drivers/index.d.ts +2 -2
- package/dist/types/nodefony/src/migrator/explain.d.ts +9 -9
- package/dist/types/nodefony/src/migrator/name.d.ts +1 -1
- package/dist/types/nodefony/src/migrator/types.d.ts +2 -2
- package/dist/types/nodefony/src/orm-core/DrizzleOrm.d.ts +3 -3
- package/dist/types/nodefony/src/orm-core/DrizzleRepository.d.ts +1 -1
- package/dist/types/nodefony/src/orm-core/ddlPlan.d.ts +2 -2
- package/dist/types/nodefony/src/queryKit.d.ts +9 -9
- package/dist/types/nodefony/src/safeTarget.d.ts +2 -2
- package/dist/types/nodefony/src/sqliteJournal.d.ts +53 -0
- package/docs/index.md +50 -39
- package/docs/migrations.md +1 -1
- package/package.json +16 -16
|
@@ -71,7 +71,8 @@ async function readJournal(dir, source) {
|
|
|
71
71
|
}
|
|
72
72
|
let journal;
|
|
73
73
|
try {
|
|
74
|
-
|
|
74
|
+
const parsed = JSON.parse(raw);
|
|
75
|
+
journal = typeof parsed === "object" && parsed !== null ? parsed : {};
|
|
75
76
|
} catch (e) {
|
|
76
77
|
throw new MigrationVerdictError({
|
|
77
78
|
code: "NF_MIGRATE_UNKNOWN_FORMAT",
|
|
@@ -91,21 +92,25 @@ async function readJournal(dir, source) {
|
|
|
91
92
|
facts: { file },
|
|
92
93
|
nextActions: []
|
|
93
94
|
}, `Le journal des migrations « ${file} » ne porte pas de liste « entries » : ce n'est pas un journal que ce format sait lire.`);
|
|
94
|
-
|
|
95
|
+
const version = String(journal.version);
|
|
96
|
+
if (!SUPPORTED_JOURNAL_VERSIONS.includes(version)) throw new MigrationVerdictError({
|
|
95
97
|
code: "NF_MIGRATE_UNKNOWN_FORMAT",
|
|
96
98
|
connector: "",
|
|
97
99
|
source,
|
|
98
100
|
facts: {
|
|
99
101
|
file,
|
|
100
|
-
journalVersion:
|
|
102
|
+
journalVersion: version,
|
|
101
103
|
supported: SUPPORTED_JOURNAL_VERSIONS
|
|
102
104
|
},
|
|
103
105
|
nextActions: [{
|
|
104
106
|
command: "npm update @nodefony/drizzle",
|
|
105
107
|
args: ["update", "@nodefony/drizzle"]
|
|
106
108
|
}]
|
|
107
|
-
}, `Le journal de migrations de la source « ${source} » est en version ${
|
|
108
|
-
return
|
|
109
|
+
}, `Le journal de migrations de la source « ${source} » est en version ${version}, que cette version du framework ne sait pas lire (reconnue : ${SUPPORTED_JOURNAL_VERSIONS.join(", ")}).`);
|
|
110
|
+
return {
|
|
111
|
+
version,
|
|
112
|
+
entries: journal.entries
|
|
113
|
+
};
|
|
109
114
|
}
|
|
110
115
|
/**
|
|
111
116
|
* Lit un fichier de migration, valide son format et découpe ses statements.
|
|
@@ -370,8 +375,8 @@ function scanLine(line, needle, grammar, curseur) {
|
|
|
370
375
|
*/
|
|
371
376
|
function delimiteurDollar(line, debut) {
|
|
372
377
|
let i = debut + 1;
|
|
373
|
-
while (i < line.length && /[A-Za-z0-9_]/.test(line
|
|
374
|
-
if (i === debut + 1 && /[0-9]/.test(line
|
|
378
|
+
while (i < line.length && /[A-Za-z0-9_]/.test(line.charAt(i))) {
|
|
379
|
+
if (i === debut + 1 && /[0-9]/.test(line.charAt(i))) return null;
|
|
375
380
|
i += 1;
|
|
376
381
|
}
|
|
377
382
|
return line[i] === "$" ? line.slice(debut, i + 1) : null;
|
|
@@ -390,7 +395,7 @@ function delimiteurDollar(line, debut) {
|
|
|
390
395
|
function createdTables(files) {
|
|
391
396
|
const found = /* @__PURE__ */ new Set();
|
|
392
397
|
const pattern = /CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?[`"']?([A-Za-z0-9_$]+)[`"']?/gi;
|
|
393
|
-
for (const file of files) for (const statement of file.statements) for (const
|
|
398
|
+
for (const file of files) for (const statement of file.statements) for (const [, table] of statement.matchAll(pattern)) if (table !== void 0) found.add(table);
|
|
394
399
|
return [...found];
|
|
395
400
|
}
|
|
396
401
|
/**
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { schemaReader, toDollarParams } from "../migrator/catalog.js";
|
|
2
2
|
import "../memoryDatabase.js";
|
|
3
|
+
import { enableWriteAheadLog } from "../sqliteJournal.js";
|
|
3
4
|
import { notConfigured } from "../migrator/refusals.js";
|
|
4
5
|
import { additiveSql, compareSchema, hasGap } from "../migrator/schemaDiff.js";
|
|
5
6
|
import { describeTargetSafely } from "../safeTarget.js";
|
|
@@ -431,17 +432,18 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
431
432
|
}
|
|
432
433
|
async onConnect() {
|
|
433
434
|
await this.#releasePrevious();
|
|
434
|
-
|
|
435
|
+
const tables = Object.create(null);
|
|
436
|
+
this.#tables = tables;
|
|
435
437
|
this.#relations = Object.create(null);
|
|
436
438
|
const entities = this.#ownEntities();
|
|
437
439
|
switch (this.#dialect) {
|
|
438
440
|
case "sqlite":
|
|
439
|
-
await this.#connectSqlite(entities);
|
|
441
|
+
await this.#connectSqlite(entities, tables);
|
|
440
442
|
break;
|
|
441
443
|
case "postgres":
|
|
442
|
-
await this.#connectPostgres(entities);
|
|
444
|
+
await this.#connectPostgres(entities, tables);
|
|
443
445
|
break;
|
|
444
|
-
case "mysql": await this.#connectMysql(entities);
|
|
446
|
+
case "mysql": await this.#connectMysql(entities, tables);
|
|
445
447
|
}
|
|
446
448
|
if (this.#deriveSchema) {
|
|
447
449
|
await this.#reconcileSchema();
|
|
@@ -461,7 +463,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
461
463
|
* La méthode devient asynchrone pour cette seule raison ; le pilote, lui,
|
|
462
464
|
* reste synchrone une fois chargé (`client.exec`, `client.pragma`).
|
|
463
465
|
*/
|
|
464
|
-
async #connectSqlite(entities) {
|
|
466
|
+
async #connectSqlite(entities, tables) {
|
|
465
467
|
let Sqlite;
|
|
466
468
|
let sqliteDrizzle;
|
|
467
469
|
try {
|
|
@@ -471,12 +473,14 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
471
473
|
Sqlite = resolved;
|
|
472
474
|
sqliteDrizzle = (await import("drizzle-orm/better-sqlite3")).drizzle;
|
|
473
475
|
} catch (e) {
|
|
474
|
-
throw new Error(`DrizzleOrm "${this.name}": the sqlite dialect needs the optional driver \`better-sqlite3\` (run \`npm i better-sqlite3\`).
|
|
476
|
+
throw new Error(`DrizzleOrm "${this.name}": the sqlite dialect needs the optional driver \`better-sqlite3\` (run \`npm i better-sqlite3\`). ` + e.message, { cause: e });
|
|
475
477
|
}
|
|
476
478
|
const client = new Sqlite(this.#filename);
|
|
477
|
-
if (this.#filename !== ":memory:") {
|
|
478
|
-
client
|
|
479
|
-
|
|
479
|
+
if (this.#filename !== ":memory:") try {
|
|
480
|
+
await enableWriteAheadLog(client);
|
|
481
|
+
} catch (e) {
|
|
482
|
+
client.close();
|
|
483
|
+
throw e;
|
|
480
484
|
}
|
|
481
485
|
client.pragma("foreign_keys = ON");
|
|
482
486
|
this.#client = client;
|
|
@@ -506,7 +510,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
506
510
|
for (const entity of entities) {
|
|
507
511
|
this.#assertDialectTable(entity, SQLiteTable, "sqlite");
|
|
508
512
|
const table = entity.schema;
|
|
509
|
-
|
|
513
|
+
tables[entity.name] = table;
|
|
510
514
|
entity.model = table;
|
|
511
515
|
}
|
|
512
516
|
if (!this.#deriveSchema) return;
|
|
@@ -537,12 +541,12 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
537
541
|
#rawQuery = async (sql, params = []) => {
|
|
538
542
|
switch (this.#dialect) {
|
|
539
543
|
case "sqlite": {
|
|
540
|
-
const statement = this.#
|
|
544
|
+
const statement = this.#sqliteClient().prepare(sql);
|
|
541
545
|
return statement.reader ? statement.all(...params) : [];
|
|
542
546
|
}
|
|
543
|
-
case "postgres": return (await this.#
|
|
547
|
+
case "postgres": return (await this.#pgClient().query(toDollarParams(sql), params)).rows;
|
|
544
548
|
case "mysql": {
|
|
545
|
-
const [rows] = await this.#
|
|
549
|
+
const [rows] = await this.#mysqlClient().query(sql, params);
|
|
546
550
|
return Array.isArray(rows) ? rows : [];
|
|
547
551
|
}
|
|
548
552
|
}
|
|
@@ -555,13 +559,13 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
555
559
|
async #rawExec(sql) {
|
|
556
560
|
switch (this.#dialect) {
|
|
557
561
|
case "sqlite":
|
|
558
|
-
this.#
|
|
562
|
+
this.#sqliteClient().exec(sql);
|
|
559
563
|
return;
|
|
560
564
|
case "postgres":
|
|
561
|
-
await this.#
|
|
565
|
+
await this.#pgClient().query(sql);
|
|
562
566
|
return;
|
|
563
567
|
case "mysql":
|
|
564
|
-
await this.#
|
|
568
|
+
await this.#mysqlClient().query(sql);
|
|
565
569
|
return;
|
|
566
570
|
}
|
|
567
571
|
}
|
|
@@ -640,20 +644,20 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
640
644
|
case "sqlite": {
|
|
641
645
|
const t = table;
|
|
642
646
|
if (empechees.has(getTableConfig(t).name)) continue;
|
|
643
|
-
for (const statement of this.#createIndexesSQL(t)) this.#
|
|
647
|
+
for (const statement of this.#createIndexesSQL(t)) this.#sqliteClient().exec(statement);
|
|
644
648
|
break;
|
|
645
649
|
}
|
|
646
650
|
case "postgres": {
|
|
647
651
|
const t = table;
|
|
648
652
|
if (empechees.has(getTableConfig$1(t).name)) continue;
|
|
649
|
-
for (const statement of this.#createIndexesPgSQL(t)) await this.#
|
|
653
|
+
for (const statement of this.#createIndexesPgSQL(t)) await this.#pgClient().query(statement);
|
|
650
654
|
break;
|
|
651
655
|
}
|
|
652
656
|
case "mysql": {
|
|
653
657
|
const t = table;
|
|
654
658
|
if (empechees.has(getTableConfig$2(t).name)) continue;
|
|
655
659
|
for (const statement of this.#createIndexesMysqlSQL(t)) try {
|
|
656
|
-
await this.#
|
|
660
|
+
await this.#mysqlClient().query(statement);
|
|
657
661
|
} catch (error) {
|
|
658
662
|
if (error.code !== "ER_DUP_KEYNAME") throw error;
|
|
659
663
|
}
|
|
@@ -677,7 +681,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
677
681
|
* déploiement SQLite ne les tire jamais. Pool partagé ; DDL dérivé pour dev/test
|
|
678
682
|
* (prod = drizzle-kit). Échec d'import → message actionnable (`npm i pg`).
|
|
679
683
|
*/
|
|
680
|
-
async #connectPostgres(entities) {
|
|
684
|
+
async #connectPostgres(entities, tables) {
|
|
681
685
|
if (!this.#url) throw new Error(`DrizzleOrm "${this.name}": dialect "postgres" requires a connection \`url\`.`);
|
|
682
686
|
let PoolCtor;
|
|
683
687
|
let pgDrizzle;
|
|
@@ -708,7 +712,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
708
712
|
this.#pgPool = pool;
|
|
709
713
|
this.#db = pgDrizzle(pool);
|
|
710
714
|
try {
|
|
711
|
-
await this.#finishPostgres(entities, pool, pgDrizzle);
|
|
715
|
+
await this.#finishPostgres(entities, tables, pool, pgDrizzle);
|
|
712
716
|
} catch (e) {
|
|
713
717
|
this.#unwireAll();
|
|
714
718
|
await pool.end().catch(() => void 0);
|
|
@@ -719,11 +723,11 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
719
723
|
}
|
|
720
724
|
}
|
|
721
725
|
/** Suite de la connexion postgres — isolée pour rendre `connect()` atomique. */
|
|
722
|
-
async #finishPostgres(entities, pool, pgDrizzle) {
|
|
726
|
+
async #finishPostgres(entities, tables, pool, pgDrizzle) {
|
|
723
727
|
this.#beginTx = async () => {
|
|
724
728
|
const cx = await pool.connect();
|
|
725
729
|
const puitsTx = (err) => {
|
|
726
|
-
this.connectionLost(`pg (transaction) : ${err
|
|
730
|
+
this.connectionLost(`pg (transaction) : ${err instanceof Error ? err.message : String(err)}`);
|
|
727
731
|
};
|
|
728
732
|
cx.on("error", puitsTx);
|
|
729
733
|
/** Rend la connexion, en retirant d'abord NOTRE puits. */
|
|
@@ -748,7 +752,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
748
752
|
for (const entity of entities) {
|
|
749
753
|
this.#assertDialectTable(entity, PgTable, "postgres");
|
|
750
754
|
const table = entity.schema;
|
|
751
|
-
|
|
755
|
+
tables[entity.name] = table;
|
|
752
756
|
entity.model = table;
|
|
753
757
|
}
|
|
754
758
|
if (!this.#deriveSchema) return;
|
|
@@ -809,7 +813,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
809
813
|
*/
|
|
810
814
|
#wirePgLifecycle(pool) {
|
|
811
815
|
const onError = (err) => {
|
|
812
|
-
this.connectionLost(`pg: ${err
|
|
816
|
+
this.connectionLost(`pg: ${err instanceof Error ? err.message : String(err)}`);
|
|
813
817
|
};
|
|
814
818
|
const onConnect = () => {
|
|
815
819
|
this.connectionRestored();
|
|
@@ -853,7 +857,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
853
857
|
this.connectionRestored();
|
|
854
858
|
cx.on("error", (err) => {
|
|
855
859
|
if (this.#mysqlPool !== pool) return;
|
|
856
|
-
this.connectionLost(`mysql: ${err
|
|
860
|
+
this.connectionLost(`mysql: ${err instanceof Error ? err.message : String(err)}`);
|
|
857
861
|
});
|
|
858
862
|
cx.stream?.once("close", () => {
|
|
859
863
|
if (this.#mysqlPool !== pool) return;
|
|
@@ -873,7 +877,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
873
877
|
* `datetime(3)` (kind `dateMs` du colKit) sont écrites/relues en UTC — mêmes
|
|
874
878
|
* instants que `timestamptz` PG, sans dépendre de la timezone du serveur.
|
|
875
879
|
*/
|
|
876
|
-
async #connectMysql(entities) {
|
|
880
|
+
async #connectMysql(entities, tables) {
|
|
877
881
|
if (!this.#url) throw new Error(`DrizzleOrm "${this.name}": dialect "mysql" requires a connection \`url\`.`);
|
|
878
882
|
let pool;
|
|
879
883
|
let mysqlDrizzle;
|
|
@@ -903,7 +907,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
903
907
|
this.#mysqlPool = pool;
|
|
904
908
|
this.#db = mysqlDrizzle(pool);
|
|
905
909
|
try {
|
|
906
|
-
await this.#finishMysql(entities, pool, mysqlDrizzle);
|
|
910
|
+
await this.#finishMysql(entities, tables, pool, mysqlDrizzle);
|
|
907
911
|
} catch (e) {
|
|
908
912
|
this.#unwireAll();
|
|
909
913
|
await pool.end().catch(() => void 0);
|
|
@@ -914,7 +918,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
914
918
|
}
|
|
915
919
|
}
|
|
916
920
|
/** Suite de la connexion mysql — isolée pour rendre `connect()` atomique. */
|
|
917
|
-
async #finishMysql(entities, pool, mysqlDrizzle) {
|
|
921
|
+
async #finishMysql(entities, tables, pool, mysqlDrizzle) {
|
|
918
922
|
this.#beginTx = async () => {
|
|
919
923
|
const cx = await pool.getConnection();
|
|
920
924
|
try {
|
|
@@ -937,7 +941,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
937
941
|
for (const entity of entities) {
|
|
938
942
|
this.#assertDialectTable(entity, MySqlTable, "mysql");
|
|
939
943
|
const table = entity.schema;
|
|
940
|
-
|
|
944
|
+
tables[entity.name] = table;
|
|
941
945
|
entity.model = table;
|
|
942
946
|
}
|
|
943
947
|
if (!this.#deriveSchema) return;
|
|
@@ -977,7 +981,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
977
981
|
const table = this.#tables?.[name];
|
|
978
982
|
if (!this.#db) throw new Error(`DrizzleOrm "${this.name}": non connecté — impossible de rendre un dépôt pour "${name}". Les tables ne sont résolues qu'à la connexion. Si ce run est une commande en ligne, elle n'a pas déclaré \`externalServices\` (CONSOLE_DATA_RUN_PROFILE).`);
|
|
979
983
|
if (!table) throw new Error(`DrizzleOrm "${this.name}": no entity table registered under "${name}".`);
|
|
980
|
-
|
|
984
|
+
this.#repositories ??= Object.create(null);
|
|
981
985
|
let repository = this.#repositories[name];
|
|
982
986
|
if (repository === void 0) {
|
|
983
987
|
repository = new DrizzleRepository(this.#db, table, this.#relations?.[name] ?? {}, this.name, this.#dialect);
|
|
@@ -1010,6 +1014,25 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
1010
1014
|
throw error;
|
|
1011
1015
|
}
|
|
1012
1016
|
}
|
|
1017
|
+
/**
|
|
1018
|
+
* Connexion SQLite courante — lève si le connecteur n'est pas ouvert. Les
|
|
1019
|
+
* appelants ne passent ici qu'une fois `onConnect` engagé : la levée ne sert
|
|
1020
|
+
* qu'à typer, et à dire la cause si l'ordre était un jour rompu.
|
|
1021
|
+
*/
|
|
1022
|
+
#sqliteClient() {
|
|
1023
|
+
if (this.#client === null) throw new Error(`DrizzleOrm "${this.name}": not connected.`);
|
|
1024
|
+
return this.#client;
|
|
1025
|
+
}
|
|
1026
|
+
/** Pool PostgreSQL courant — même contrat que {@link #sqliteClient}. */
|
|
1027
|
+
#pgClient() {
|
|
1028
|
+
if (this.#pgPool === null) throw new Error(`DrizzleOrm "${this.name}": not connected.`);
|
|
1029
|
+
return this.#pgPool;
|
|
1030
|
+
}
|
|
1031
|
+
/** Pool MySQL courant — même contrat que {@link #sqliteClient}. */
|
|
1032
|
+
#mysqlClient() {
|
|
1033
|
+
if (this.#mysqlPool === null) throw new Error(`DrizzleOrm "${this.name}": not connected.`);
|
|
1034
|
+
return this.#mysqlPool;
|
|
1035
|
+
}
|
|
1013
1036
|
getNativeConnection() {
|
|
1014
1037
|
if (!this.#db) throw new Error(`DrizzleOrm "${this.name}": not connected.`);
|
|
1015
1038
|
return this.#db;
|
|
@@ -1123,7 +1146,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
1123
1146
|
* @returns sonde `pool` (taille max, idle, empruntées, en attente).
|
|
1124
1147
|
*/
|
|
1125
1148
|
#probePgPool(pool) {
|
|
1126
|
-
const max = pool.options
|
|
1149
|
+
const max = pool.options.max;
|
|
1127
1150
|
return { pool: {
|
|
1128
1151
|
size: typeof max === "number" ? max : 10,
|
|
1129
1152
|
available: pool.idleCount,
|
|
@@ -24,6 +24,25 @@ const PREPARED_CACHE_MAX = 128;
|
|
|
24
24
|
/** Séquence des noms de prepared statements (PG les exige uniques par process). */
|
|
25
25
|
let preparedNameSeq = 0;
|
|
26
26
|
/**
|
|
27
|
+
* Les comparaisons scalaires, dans l'ordre où elles entrent dans le `WHERE`.
|
|
28
|
+
*
|
|
29
|
+
* UNE table pour le chemin direct (`#pushOperators`) ET le chemin préparé
|
|
30
|
+
* (`#buildPreparedSelect`) : deux listes recopiées finiraient par traduire le
|
|
31
|
+
* même critère en deux SQL différents, selon que la forme est en cache ou non.
|
|
32
|
+
* Leur texte SQL ne dépend que de l'opérateur, jamais de la valeur — c'est ce
|
|
33
|
+
* qui les rend préparables (≠ `$in`/`$nin`, dont le SQL suit la cardinalité).
|
|
34
|
+
*/
|
|
35
|
+
const COMPARISONS = [
|
|
36
|
+
["$eq", eq],
|
|
37
|
+
["$ne", ne],
|
|
38
|
+
["$gt", gt],
|
|
39
|
+
["$gte", gte],
|
|
40
|
+
["$lt", lt],
|
|
41
|
+
["$lte", lte]
|
|
42
|
+
];
|
|
43
|
+
/** Opérateurs qu'une forme préparée sait rendre : les comparaisons et `$null`. */
|
|
44
|
+
const PREPARABLE_OPERATORS = /* @__PURE__ */ new Set([...COMPARISONS.map(([op]) => op), "$null"]);
|
|
45
|
+
/**
|
|
27
46
|
* Repository portable (contrat {@link IRepository}) au-dessus d'une table Drizzle
|
|
28
47
|
* + driver `better-sqlite3`.
|
|
29
48
|
*
|
|
@@ -83,9 +102,35 @@ var DrizzleRepository = class DrizzleRepository {
|
|
|
83
102
|
this.#dialect = dialect;
|
|
84
103
|
this.#transactional = transactional;
|
|
85
104
|
}
|
|
86
|
-
/**
|
|
105
|
+
/**
|
|
106
|
+
* Colonne Drizzle d'une table par nom logique ; `undefined` si elle n'existe pas.
|
|
107
|
+
*
|
|
108
|
+
* 🔴 `Object.hasOwn` sur l'objet des COLONNES, jamais `table[name]` : le nom
|
|
109
|
+
* vient souvent de la requête (`?sort=`), et une clé héritée du prototype
|
|
110
|
+
* (`constructor`, `__proto__`, `toString`) ne vaut pas `undefined` — elle
|
|
111
|
+
* franchissait la liste blanche et cassait plus loin dans le pilote.
|
|
112
|
+
* `getTableColumns` rend l'objet tel quel, sans copie.
|
|
113
|
+
*/
|
|
87
114
|
#col(table, name) {
|
|
88
|
-
|
|
115
|
+
const columns = getTableColumns(table);
|
|
116
|
+
return Object.hasOwn(columns, name) ? columns[name] : void 0;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Colonne EXIGÉE : un nom inconnu lève au lieu de partir dans le SQL.
|
|
120
|
+
*
|
|
121
|
+
* Strict (B2) : champ inconnu = erreur, pas un skip silencieux (qui ferait
|
|
122
|
+
* disparaître la condition → fuite « renvoie tout »), ni un `undefined` glissé
|
|
123
|
+
* dans un `ORDER BY` (rendu `no such column: asc`). Même contrat que Mongoose.
|
|
124
|
+
* Requêtes natives → `getNativeConnection()`.
|
|
125
|
+
*
|
|
126
|
+
* @param name - nom logique du champ (critère, tri, clé étrangère).
|
|
127
|
+
* @param table - table qui doit le porter (défaut : celle du repository).
|
|
128
|
+
* @throws UnknownCriteriaField si la table ne porte pas ce champ.
|
|
129
|
+
*/
|
|
130
|
+
#requireCol(name, table = this.#table) {
|
|
131
|
+
const col = this.#col(table, name);
|
|
132
|
+
if (col === void 0) throw new UnknownCriteriaField(name, getTableName(table), Object.keys(getTableColumns(table)));
|
|
133
|
+
return col;
|
|
89
134
|
}
|
|
90
135
|
/**
|
|
91
136
|
* Colonnes de la PK de la table (mémoïsées) : colonnes inline `.primaryKey()`
|
|
@@ -121,18 +166,49 @@ var DrizzleRepository = class DrizzleRepository {
|
|
|
121
166
|
*
|
|
122
167
|
* Fallback sans PK déclarée : `rowid` (SQLite-only — toutes les entités
|
|
123
168
|
* framework ont une PK ; une table d'app sans PK est un cas sqlite assumé).
|
|
169
|
+
*
|
|
170
|
+
* Court-circuit : un critère qui fixe TOUTES les colonnes de la PK par une
|
|
171
|
+
* égalité désigne déjà au plus une ligne — le `WHERE` nu suffit, et la
|
|
172
|
+
* sous-requête n'est plus payée. Elle coûtait ~60 µs par UPDATE en SQLite
|
|
173
|
+
* (recompilation d'un SQL plus long + table dérivée matérialisée, mesuré par
|
|
174
|
+
* `scripts/micro/micro-update-pick.mjs` du skill `nodefony-load-test`), soit
|
|
175
|
+
* l'essentiel de l'écart du banc ORM face à NestJS (#510).
|
|
176
|
+
*
|
|
177
|
+
* @param criteria - critère portable de la mutation.
|
|
178
|
+
* @returns le prédicat qui borne la mutation à au plus une ligne.
|
|
124
179
|
*/
|
|
125
|
-
#pickOne(
|
|
180
|
+
#pickOne(criteria) {
|
|
181
|
+
const where = this.#where(criteria);
|
|
126
182
|
const pk = this.#pkColumns();
|
|
183
|
+
if (where && pk && this.#pinsPrimaryKey(pk, criteria)) return where;
|
|
127
184
|
if (!pk) return where ? sql`rowid in (select rowid from ${this.#table} where ${where} limit 1)` : sql`rowid in (select rowid from ${this.#table} limit 1)`;
|
|
128
185
|
const qualified = sql.join(pk, sql.raw(", "));
|
|
129
186
|
const inner = where ? sql`select ${qualified} from ${this.#table} where ${where} limit 1` : sql`select ${qualified} from ${this.#table} limit 1`;
|
|
130
187
|
const output = sql.join(pk.map((col) => sql.identifier(col.name)), sql.raw(", "));
|
|
131
|
-
const first = pk
|
|
188
|
+
const first = pk.at(0);
|
|
132
189
|
const target = pk.length === 1 && first ? sql`${first}` : sql`(${qualified})`;
|
|
133
190
|
return sql`${target} in (select ${output} from (${inner}) as picked)`;
|
|
134
191
|
}
|
|
135
192
|
/**
|
|
193
|
+
* Vrai si le critère fixe chaque colonne de la PK par une égalité à une
|
|
194
|
+
* valeur (ni `null`, ni opérateur, ni branche de `$or`) — l'unicité de la
|
|
195
|
+
* PK garantit alors « au plus une ligne » sans sous-requête.
|
|
196
|
+
*
|
|
197
|
+
* @param pk - colonnes de la PK (au plus 31 : masque de bits).
|
|
198
|
+
* @param criteria - critère portable de la mutation.
|
|
199
|
+
*/
|
|
200
|
+
#pinsPrimaryKey(pk, criteria) {
|
|
201
|
+
if (criteria === void 0 || pk.length > 31) return false;
|
|
202
|
+
let pinned = 0;
|
|
203
|
+
for (const [field, value] of Object.entries(criteria)) {
|
|
204
|
+
if (field === "$or" || value === null || value === void 0 || isFieldOperators(value)) continue;
|
|
205
|
+
const col = this.#col(this.#table, field);
|
|
206
|
+
const i = col === void 0 ? -1 : pk.indexOf(col);
|
|
207
|
+
if (i !== -1) pinned |= 1 << i;
|
|
208
|
+
}
|
|
209
|
+
return pinned === (1 << pk.length) - 1;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
136
212
|
* Lignes affectées, normalisé par driver : better-sqlite3 `{changes}` /
|
|
137
213
|
* pg `{rowCount}` / mysql2 tuple `[ResultSetHeader{affectedRows}, fields]`.
|
|
138
214
|
*/
|
|
@@ -211,12 +287,10 @@ var DrizzleRepository = class DrizzleRepository {
|
|
|
211
287
|
}
|
|
212
288
|
/** Empile les conditions d'un objet d'opérateurs riches sur une colonne. */
|
|
213
289
|
#pushOperators(conds, col, ops) {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
if (ops.$lt !== void 0) conds.push(lt(col, ops.$lt));
|
|
219
|
-
if (ops.$lte !== void 0) conds.push(lte(col, ops.$lte));
|
|
290
|
+
for (const [op, compare] of COMPARISONS) {
|
|
291
|
+
const value = ops[op];
|
|
292
|
+
if (value !== void 0) conds.push(compare(col, value));
|
|
293
|
+
}
|
|
220
294
|
if (ops.$in !== void 0) conds.push(inArray(col, [...ops.$in]));
|
|
221
295
|
if (ops.$nin !== void 0) conds.push(notInArray(col, [...ops.$nin]));
|
|
222
296
|
if (ops.$like !== void 0) conds.push(likeCond(this.#dialect, sql`${col}`, ops.$like));
|
|
@@ -229,12 +303,17 @@ var DrizzleRepository = class DrizzleRepository {
|
|
|
229
303
|
for (const [field, value] of Object.entries(criteria)) {
|
|
230
304
|
if (field === "$or") {
|
|
231
305
|
if (!Array.isArray(value)) throw new Error(`DrizzleRepository(${getTableName(this.#table)}): $or attend un tableau de critères.`);
|
|
232
|
-
|
|
233
|
-
|
|
306
|
+
if (value.length === 0) {
|
|
307
|
+
conds.push(sql`1 = 0`);
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
const branches = value.map((branch) => this.#where(branch));
|
|
311
|
+
if (branches.some((branch) => branch === void 0)) continue;
|
|
312
|
+
const [first, ...rest] = branches;
|
|
313
|
+
if (first !== void 0) conds.push(rest.length === 0 ? first : or(first, ...rest));
|
|
234
314
|
continue;
|
|
235
315
|
}
|
|
236
|
-
const col = this.#
|
|
237
|
-
if (!col) throw new UnknownCriteriaField(field, getTableName(this.#table), Object.keys(getTableColumns(this.#table)));
|
|
316
|
+
const col = this.#requireCol(field);
|
|
238
317
|
if (isFieldOperators(value)) this.#pushOperators(conds, col, value);
|
|
239
318
|
else if (value === null) conds.push(isNull(col));
|
|
240
319
|
else conds.push(eq(col, value));
|
|
@@ -251,8 +330,14 @@ var DrizzleRepository = class DrizzleRepository {
|
|
|
251
330
|
* présence de limit/offset. Les VALEURS n'en font jamais partie : elles
|
|
252
331
|
* deviennent des placeholders (`p<i>`, `lim`, `off`) bindés à l'exécution.
|
|
253
332
|
*
|
|
254
|
-
*
|
|
255
|
-
*
|
|
333
|
+
* Les comparaisons scalaires ({@link COMPARISONS}) et `$null` entrent dans la
|
|
334
|
+
* forme : la COMBINAISON d'opérateurs d'un champ fait partie de la clé, leurs
|
|
335
|
+
* valeurs deviennent des placeholders `p<i>$<op>`. C'est le chemin des
|
|
336
|
+
* lectures bornées dans le temps (`expiresAt: { $gt: now }` — la révocation
|
|
337
|
+
* des jetons, lue à chaque requête authentifiée).
|
|
338
|
+
*
|
|
339
|
+
* Repli (`null`) par construction : `$or`, `$in`/`$nin` (le SQL suit la
|
|
340
|
+
* cardinalité), `$like` (clause d'échappement propre au dialecte), valeur
|
|
256
341
|
* `undefined` (le chemin actuel la rejette, même contrat), transaction
|
|
257
342
|
* (cf ctor), cache plein ({@link PREPARED_CACHE_MAX}).
|
|
258
343
|
*/
|
|
@@ -262,7 +347,22 @@ var DrizzleRepository = class DrizzleRepository {
|
|
|
262
347
|
if (criteria) {
|
|
263
348
|
let i = 0;
|
|
264
349
|
for (const [field, value] of Object.entries(criteria)) {
|
|
265
|
-
if (field === "$or" || value === void 0
|
|
350
|
+
if (field === "$or" || value === void 0) return null;
|
|
351
|
+
if (isFieldOperators(value)) {
|
|
352
|
+
let ops = "";
|
|
353
|
+
for (const op in value) if (!PREPARABLE_OPERATORS.has(op)) return null;
|
|
354
|
+
for (const [op] of COMPARISONS) {
|
|
355
|
+
const operand = value[op];
|
|
356
|
+
if (operand !== void 0) {
|
|
357
|
+
ops += op;
|
|
358
|
+
params[`p${i}${op}`] = operand;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
if (value.$null !== void 0) ops += value.$null ? "$null1" : "$null0";
|
|
362
|
+
key += `${field}\x04${ops}\x00`;
|
|
363
|
+
i++;
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
266
366
|
if (value === null) key += `${field}\x01\x00`;
|
|
267
367
|
else {
|
|
268
368
|
key += `${field}\x00`;
|
|
@@ -305,14 +405,19 @@ var DrizzleRepository = class DrizzleRepository {
|
|
|
305
405
|
const conds = [];
|
|
306
406
|
let i = 0;
|
|
307
407
|
for (const [field, value] of Object.entries(criteria)) {
|
|
308
|
-
const col = this.#
|
|
309
|
-
if (
|
|
408
|
+
const col = this.#requireCol(field);
|
|
409
|
+
if (isFieldOperators(value)) {
|
|
410
|
+
for (const [op, compare] of COMPARISONS) if (value[op] !== void 0) conds.push(compare(col, sql.param(sql.placeholder(`p${i}${op}`), col)));
|
|
411
|
+
if (value.$null !== void 0) conds.push(value.$null ? isNull(col) : isNotNull(col));
|
|
412
|
+
i++;
|
|
413
|
+
continue;
|
|
414
|
+
}
|
|
310
415
|
conds.push(value === null ? isNull(col) : eq(col, sql.param(sql.placeholder(`p${i}`), col)));
|
|
311
416
|
i++;
|
|
312
417
|
}
|
|
313
418
|
if (conds.length > 0) query = query.where(conds.length === 1 ? conds[0] : and(...conds));
|
|
314
419
|
}
|
|
315
|
-
if (options?.order?.length) query = query.orderBy(...options.order.map(([field, dir]) => dir === "DESC" ? desc(this.#
|
|
420
|
+
if (options?.order?.length) query = query.orderBy(...options.order.map(([field, dir]) => dir === "DESC" ? desc(this.#requireCol(field)) : asc(this.#requireCol(field))));
|
|
316
421
|
if (options?.limit !== void 0) query = query.limit(sql.placeholder("lim"));
|
|
317
422
|
else if (options?.offset !== void 0 && this.#dialect === "sqlite") query = query.limit(sql`-1`);
|
|
318
423
|
else if (options?.offset !== void 0 && this.#dialect === "mysql") query = query.limit(Number.MAX_SAFE_INTEGER);
|
|
@@ -335,7 +440,7 @@ var DrizzleRepository = class DrizzleRepository {
|
|
|
335
440
|
const shape = this.#selectShape(criteria, options);
|
|
336
441
|
if (shape === null) return null;
|
|
337
442
|
let cache = this.#preparedSelects;
|
|
338
|
-
|
|
443
|
+
cache ??= this.#preparedSelects = /* @__PURE__ */ new Map();
|
|
339
444
|
let entry = cache.get(shape.key);
|
|
340
445
|
if (entry === void 0) {
|
|
341
446
|
if (cache.size >= PREPARED_CACHE_MAX) return null;
|
|
@@ -364,12 +469,12 @@ var DrizzleRepository = class DrizzleRepository {
|
|
|
364
469
|
let query = this.#db.select().from(execTable(this.#table)).$dynamic();
|
|
365
470
|
const where = this.#where(criteria);
|
|
366
471
|
if (where) query = query.where(where);
|
|
367
|
-
if (options?.order?.length) query = query.orderBy(...options.order.map(([field, dir]) => dir === "DESC" ? desc(this.#
|
|
472
|
+
if (options?.order?.length) query = query.orderBy(...options.order.map(([field, dir]) => dir === "DESC" ? desc(this.#requireCol(field)) : asc(this.#requireCol(field))));
|
|
368
473
|
if (options?.limit !== void 0) query = query.limit(options.limit);
|
|
369
474
|
else if (options?.offset !== void 0 && this.#dialect === "sqlite") query = query.limit(sql`-1`);
|
|
370
475
|
else if (options?.offset !== void 0 && this.#dialect === "mysql") query = query.limit(Number.MAX_SAFE_INTEGER);
|
|
371
476
|
if (options?.offset !== void 0) query = query.offset(options.offset);
|
|
372
|
-
return
|
|
477
|
+
return this.#prof(query);
|
|
373
478
|
}
|
|
374
479
|
/** Eager-load manuel des relations déclarées (1 requête `IN (...)` par relation). */
|
|
375
480
|
async #populate(rows, relations) {
|
|
@@ -379,7 +484,7 @@ var DrizzleRepository = class DrizzleRepository {
|
|
|
379
484
|
if (!rel) throw new Error(`DrizzleRepository(${name}): relation "${name}" non déclarée.`);
|
|
380
485
|
if (rel.type === "one-to-many") {
|
|
381
486
|
const parentIds = rows.map((row) => row[rel.localKey]);
|
|
382
|
-
const fkCol = this.#
|
|
487
|
+
const fkCol = this.#requireCol(rel.foreignKey, rel.targetTable);
|
|
383
488
|
const children = await this.#prof(this.#db.select().from(execTable(rel.targetTable)).where(inArray(fkCol, parentIds)));
|
|
384
489
|
const byParent = /* @__PURE__ */ new Map();
|
|
385
490
|
for (const child of children) {
|
|
@@ -391,7 +496,7 @@ var DrizzleRepository = class DrizzleRepository {
|
|
|
391
496
|
for (const row of rows) row[name] = byParent.get(row[rel.localKey]) ?? [];
|
|
392
497
|
} else {
|
|
393
498
|
const fkValues = rows.map((row) => row[rel.foreignKey]).filter((value) => value !== null && value !== void 0);
|
|
394
|
-
const idCol = this.#
|
|
499
|
+
const idCol = this.#requireCol(rel.targetKey, rel.targetTable);
|
|
395
500
|
const parents = fkValues.length > 0 ? await this.#prof(this.#db.select().from(execTable(rel.targetTable)).where(inArray(idCol, fkValues))) : [];
|
|
396
501
|
const byId = new Map(parents.map((p) => [p[rel.targetKey], p]));
|
|
397
502
|
for (const row of rows) row[name] = byId.get(row[rel.foreignKey]) ?? null;
|
|
@@ -420,7 +525,7 @@ var DrizzleRepository = class DrizzleRepository {
|
|
|
420
525
|
}
|
|
421
526
|
async updateOne(criteria, data) {
|
|
422
527
|
if (this.#dialect === "mysql") return this.#mysqlUpdateOneReturning(data, criteria);
|
|
423
|
-
const pick = this.#pickOne(
|
|
528
|
+
const pick = this.#pickOne(criteria);
|
|
424
529
|
return (await this.#prof(this.#db.update(execTable(this.#table)).set(data).where(pick).returning()))[0] ?? null;
|
|
425
530
|
}
|
|
426
531
|
/**
|
|
@@ -441,8 +546,7 @@ var DrizzleRepository = class DrizzleRepository {
|
|
|
441
546
|
values[field] = value;
|
|
442
547
|
continue;
|
|
443
548
|
}
|
|
444
|
-
const col = this.#
|
|
445
|
-
if (!col) throw new UnknownCriteriaField(field, getTableName(this.#table), Object.keys(getTableColumns(this.#table)));
|
|
549
|
+
const col = this.#requireCol(field);
|
|
446
550
|
const apply = (fn, v) => {
|
|
447
551
|
set[field] = sql`${sql.raw(fn)}(${col}, ${v})`;
|
|
448
552
|
values[field] = v;
|
|
@@ -457,9 +561,7 @@ var DrizzleRepository = class DrizzleRepository {
|
|
|
457
561
|
}
|
|
458
562
|
async upsert(criteria, update, insertOnly) {
|
|
459
563
|
const target = Object.keys(criteria).map((field) => {
|
|
460
|
-
|
|
461
|
-
if (!col) throw new UnknownCriteriaField(field, getTableName(this.#table), Object.keys(getTableColumns(this.#table)));
|
|
462
|
-
return col;
|
|
564
|
+
return this.#requireCol(field);
|
|
463
565
|
});
|
|
464
566
|
const write = this.#writeSet(update);
|
|
465
567
|
const values = {
|
|
@@ -486,12 +588,11 @@ var DrizzleRepository = class DrizzleRepository {
|
|
|
486
588
|
async increment(criteria, changes) {
|
|
487
589
|
const setObj = {};
|
|
488
590
|
for (const [field, delta] of Object.entries(changes)) {
|
|
489
|
-
const col = this.#
|
|
490
|
-
if (!col) throw new UnknownCriteriaField(field, getTableName(this.#table), Object.keys(getTableColumns(this.#table)));
|
|
591
|
+
const col = this.#requireCol(field);
|
|
491
592
|
setObj[field] = sql`${col} + ${delta}`;
|
|
492
593
|
}
|
|
493
594
|
if (this.#dialect === "mysql") return this.#mysqlUpdateOneReturning(setObj, criteria);
|
|
494
|
-
const pick = this.#pickOne(
|
|
595
|
+
const pick = this.#pickOne(criteria);
|
|
495
596
|
return (await this.#prof(this.#db.update(execTable(this.#table)).set(setObj).where(pick).returning()))[0] ?? null;
|
|
496
597
|
}
|
|
497
598
|
async delete(criteria) {
|
|
@@ -510,8 +611,8 @@ var DrizzleRepository = class DrizzleRepository {
|
|
|
510
611
|
}
|
|
511
612
|
/** DELETE atomique d'AU PLUS une ligne (PK via `#pickOne`), RETURNING la ligne. */
|
|
512
613
|
async #deleteOneReturning(criteria) {
|
|
513
|
-
const pick = this.#pickOne(
|
|
514
|
-
return
|
|
614
|
+
const pick = this.#pickOne(criteria);
|
|
615
|
+
return this.#prof(this.#db.delete(execTable(this.#table)).where(pick).returning());
|
|
515
616
|
}
|
|
516
617
|
/** PK obligatoire en mysql (les verbes re-SELECTent par PK). Fail-loud sinon. */
|
|
517
618
|
#requirePk(verb) {
|
|
@@ -522,7 +623,8 @@ var DrizzleRepository = class DrizzleRepository {
|
|
|
522
623
|
/** WHERE d'égalité sur la PK (valeurs plates lues par nom de colonne). */
|
|
523
624
|
#pkWhere(pk, values) {
|
|
524
625
|
const conds = pk.map((col) => eq(col, values[col.name]));
|
|
525
|
-
|
|
626
|
+
const [first, ...rest] = conds;
|
|
627
|
+
return first !== void 0 && rest.length === 0 ? first : and(...conds);
|
|
526
628
|
}
|
|
527
629
|
/** SELECT d'UNE ligne par valeurs de PK (relecture post-mutation mysql). */
|
|
528
630
|
async #selectByPk(pk, values) {
|
|
@@ -544,7 +646,7 @@ var DrizzleRepository = class DrizzleRepository {
|
|
|
544
646
|
async #mysqlUpdateOneReturning(set, criteria) {
|
|
545
647
|
const pk = this.#requirePk("updateOne");
|
|
546
648
|
const where = this.#where(criteria);
|
|
547
|
-
const target = (await this.#runSelect(criteria, { limit: 1 }))
|
|
649
|
+
const target = (await this.#runSelect(criteria, { limit: 1 })).at(0);
|
|
548
650
|
if (!target) return null;
|
|
549
651
|
const pkCond = this.#pkWhere(pk, target);
|
|
550
652
|
const result = await this.#prof(this.#db.update(execTable(this.#table)).set(set).where(where ? and(pkCond, where) : pkCond));
|
|
@@ -556,7 +658,7 @@ var DrizzleRepository = class DrizzleRepository {
|
|
|
556
658
|
async #mysqlDeleteOneReturning(criteria) {
|
|
557
659
|
const pk = this.#requirePk("deleteOne");
|
|
558
660
|
const where = this.#where(criteria);
|
|
559
|
-
const target = (await this.#runSelect(criteria, { limit: 1 }))
|
|
661
|
+
const target = (await this.#runSelect(criteria, { limit: 1 })).at(0);
|
|
560
662
|
if (!target) return null;
|
|
561
663
|
const pkCond = this.#pkWhere(pk, target);
|
|
562
664
|
const result = await this.#prof(this.#db.delete(execTable(this.#table)).where(where ? and(pkCond, where) : pkCond));
|
|
@@ -591,7 +693,7 @@ var DrizzleRepository = class DrizzleRepository {
|
|
|
591
693
|
*/
|
|
592
694
|
async countDistinct(field, criteria) {
|
|
593
695
|
const where = this.#where(criteria);
|
|
594
|
-
const builder = this.#db.select({ value: countDistinct(this.#
|
|
696
|
+
const builder = this.#db.select({ value: countDistinct(this.#requireCol(field)) }).from(execTable(this.#table)).$dynamic();
|
|
595
697
|
const rows = await this.#prof(where ? builder.where(where) : builder);
|
|
596
698
|
return Number(rows[0]?.value ?? 0);
|
|
597
699
|
}
|