@prisma/orm-target-mysql 8.0.0-rc.22-dev.10 → 8.0.0-rc.22-dev.12
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 +3 -1
- package/dist/{adapter-_WBomuTw.d.mts → adapter-Dl4Wwz1I.d.mts} +4 -2
- package/dist/adapter-Dl4Wwz1I.d.mts.map +1 -0
- package/dist/adapter.d.mts +5 -5
- package/dist/adapter.mjs +2 -2
- package/dist/adapter__adapter.d.mts +1 -1
- package/dist/adapter__adapter.mjs +1 -1
- package/dist/adapter__codec-types.d.mts +2 -2
- package/dist/adapter__control.d.mts +1 -1
- package/dist/adapter__control.mjs +1 -1
- package/dist/adapter__runtime.d.mts +1 -1
- package/dist/adapter__runtime.mjs +89 -14
- package/dist/adapter__runtime.mjs.map +1 -1
- package/dist/{codec-descriptor-Du6DSz-3-ytBCu_mq.mjs → codec-descriptor-3xp1XvVH-yVLnNzZ8.mjs} +5 -3
- package/dist/codec-descriptor-3xp1XvVH-yVLnNzZ8.mjs.map +1 -0
- package/dist/codec-ids-CrR9Xi7r-CarpqbNM.mjs +20 -0
- package/dist/codec-ids-CrR9Xi7r-CarpqbNM.mjs.map +1 -0
- package/dist/codec-ids-Dgh5IpML-76oBMuwK.d.mts +20 -0
- package/dist/codec-ids-Dgh5IpML-76oBMuwK.d.mts.map +1 -0
- package/dist/codec-types-BH5moCD9.d.mts +1 -0
- package/dist/codec-types-Bai_CFkk-Blx2GxCH.d.mts +31 -0
- package/dist/codec-types-Bai_CFkk-Blx2GxCH.d.mts.map +1 -0
- package/dist/codec-types-zzVcrnbk.d.mts +1 -0
- package/dist/codecs-BMidk26M-DsKq6wt5.d.mts +282 -0
- package/dist/codecs-BMidk26M-DsKq6wt5.d.mts.map +1 -0
- package/dist/codecs-BfiB79-5.mjs +750 -0
- package/dist/codecs-BfiB79-5.mjs.map +1 -0
- package/dist/codecs-C_twedMx.d.mts +36 -0
- package/dist/codecs-C_twedMx.d.mts.map +1 -0
- package/dist/{contract-contributions-B9njRJbt-ClEtrK2L.mjs → contract-contributions-ChiX6W0F-CAng98bZ.mjs} +5 -4
- package/dist/contract-contributions-ChiX6W0F-CAng98bZ.mjs.map +1 -0
- package/dist/{control-6wRZXIJ7.mjs → control-5eVF1TXc.mjs} +2 -2
- package/dist/{control-6wRZXIJ7.mjs.map → control-5eVF1TXc.mjs.map} +1 -1
- package/dist/{control-WTY7Jpxk.mjs → control-BBhWaGnH.mjs} +24 -13
- package/dist/control-BBhWaGnH.mjs.map +1 -0
- package/dist/control-BvSM7mGR.d.mts +396 -0
- package/dist/control-BvSM7mGR.d.mts.map +1 -0
- package/dist/{control-Ez_PtlFI.d.mts → control-MoPFgSaQ.d.mts} +2 -2
- package/dist/{control-Ez_PtlFI.d.mts.map → control-MoPFgSaQ.d.mts.map} +1 -1
- package/dist/data-type-entries-C7VH2IHc-D1d5I8zF.mjs +59 -0
- package/dist/data-type-entries-C7VH2IHc-D1d5I8zF.mjs.map +1 -0
- package/dist/{data-types-SXrh0Lou.d.mts → data-types-C-rfxpgB.d.mts} +2 -2
- package/dist/data-types-C-rfxpgB.d.mts.map +1 -0
- package/dist/data-types-D2HAT-Qf-CRI506c0.d.mts +88 -0
- package/dist/data-types-D2HAT-Qf-CRI506c0.d.mts.map +1 -0
- package/dist/data-types-DOaaPS93-DBkMJyh1.mjs +289 -0
- package/dist/data-types-DOaaPS93-DBkMJyh1.mjs.map +1 -0
- package/dist/date-time-now-generators-DY-8Bodc-DcQ3h4Fg.mjs +72 -0
- package/dist/date-time-now-generators-DY-8Bodc-DcQ3h4Fg.mjs.map +1 -0
- package/dist/{default-normalizer-BDUgHXUn-Xk7HQJuI.mjs → default-normalizer-C8pc7SbL-CKgOEnGl.mjs} +22 -12
- package/dist/default-normalizer-C8pc7SbL-CKgOEnGl.mjs.map +1 -0
- package/dist/{default-normalizer-BBhGpw7r.d.mts → default-normalizer-CGIwQiKB.d.mts} +4 -4
- package/dist/{default-normalizer-BBhGpw7r.d.mts.map → default-normalizer-CGIwQiKB.d.mts.map} +1 -1
- package/dist/descriptor-meta-CsMPRsxp-DhK87vlc.mjs +352 -0
- package/dist/descriptor-meta-CsMPRsxp-DhK87vlc.mjs.map +1 -0
- package/dist/driver.mjs +1 -1
- package/dist/driver__control.mjs +1 -1
- package/dist/driver__runtime.mjs +275 -126
- package/dist/driver__runtime.mjs.map +1 -1
- package/dist/errors-Cng94tYg-D02Eud2d.mjs +87 -0
- package/dist/errors-Cng94tYg-D02Eud2d.mjs.map +1 -0
- package/dist/{errors-BYhjQT7y.d.mts → errors-DfrC_yfG.d.mts} +2 -2
- package/dist/{errors-BYhjQT7y.d.mts.map → errors-DfrC_yfG.d.mts.map} +1 -1
- package/dist/{marker-lock-CmGvN4hw-Com29AtQ.mjs → marker-lock-Bwew7cAs-CZ20Y_tA.mjs} +3 -3
- package/dist/{marker-lock-CmGvN4hw-Com29AtQ.mjs.map → marker-lock-Bwew7cAs-CZ20Y_tA.mjs.map} +1 -1
- package/dist/{marker-read-aEaHihYj-Cyg16yn1.mjs → marker-read-Cf80LSbG-CUp3v1e5.mjs} +5 -5
- package/dist/{marker-read-aEaHihYj-Cyg16yn1.mjs.map → marker-read-Cf80LSbG-CUp3v1e5.mjs.map} +1 -1
- package/dist/mysql-contract-serializer-BnTKRSsN-B2O3EreV.mjs +35 -0
- package/dist/mysql-contract-serializer-BnTKRSsN-B2O3EreV.mjs.map +1 -0
- package/dist/{mysql-contract-serializer-DLv0uW_f-Cg4A43Ry.mjs → mysql-database-CHl9-oSL-CWZLDXt6.mjs} +39 -40
- package/dist/mysql-database-CHl9-oSL-CWZLDXt6.mjs.map +1 -0
- package/dist/now-expression-DVD18CUc-00buJUto.mjs +38 -0
- package/dist/now-expression-DVD18CUc-00buJUto.mjs.map +1 -0
- package/dist/{pack-YVG-iBMQ.d.mts → pack-BXvLVBWP.d.mts} +395 -14
- package/dist/{pack-YVG-iBMQ.d.mts.map → pack-BXvLVBWP.d.mts.map} +1 -1
- package/dist/require-temporal-DeDQhbTn-Bxc65WbA.mjs +16 -0
- package/dist/require-temporal-DeDQhbTn-Bxc65WbA.mjs.map +1 -0
- package/dist/{runner-BaBQoXiU-C7ln-4b2.mjs → runner-C39AEe6--BVG3xPYw.mjs} +7 -7
- package/dist/{runner-BaBQoXiU-C7ln-4b2.mjs.map → runner-C39AEe6--BVG3xPYw.mjs.map} +1 -1
- package/dist/{runtime-CeuRr9nE.d.mts → runtime-ClRDfPiY.d.mts} +2 -2
- package/dist/{runtime-CeuRr9nE.d.mts.map → runtime-ClRDfPiY.d.mts.map} +1 -1
- package/dist/runtime-CrB5DXx4.d.mts +54 -0
- package/dist/runtime-CrB5DXx4.d.mts.map +1 -0
- package/dist/{sql-utils-Db0IQO_B-PaULnfrE.mjs → sql-utils-Cmv_TVa8-BetrIUcj.mjs} +3 -3
- package/dist/{sql-utils-Db0IQO_B-PaULnfrE.mjs.map → sql-utils-Cmv_TVa8-BetrIUcj.mjs.map} +1 -1
- package/dist/{sql-utils-RW0vJvDl.mjs → sql-utils-bD4SfgX0.mjs} +2 -2
- package/dist/{sql-utils-RW0vJvDl.mjs.map → sql-utils-bD4SfgX0.mjs.map} +1 -1
- package/dist/{statement-kfMRMKIt-D6cAOKys.mjs → statement-DC62oiSx-Buzptbpk.mjs} +21 -15
- package/dist/statement-DC62oiSx-Buzptbpk.mjs.map +1 -0
- package/dist/target.d.mts +14 -13
- package/dist/target.mjs +17 -13
- package/dist/target__codec-descriptor.mjs +1 -1
- package/dist/target__codec-ids.d.mts +2 -2
- package/dist/target__codec-ids.mjs +2 -2
- package/dist/target__codec-types.d.mts +2 -2
- package/dist/target__codecs.d.mts +3 -3
- package/dist/target__codecs.mjs +3 -2
- package/dist/target__control.d.mts +2 -2
- package/dist/target__control.mjs +38 -32
- package/dist/target__control.mjs.map +1 -1
- package/dist/target__data-types.d.mts +3 -3
- package/dist/target__data-types.mjs +3 -3
- package/dist/target__default-normalizer.d.mts +1 -1
- package/dist/target__default-normalizer.mjs +1 -1
- package/dist/target__errors.d.mts +1 -1
- package/dist/target__errors.mjs +1 -1
- package/dist/target__marker-lock.mjs +1 -1
- package/dist/target__migration.mjs +1 -1
- package/dist/target__pack.d.mts +1 -1
- package/dist/target__pack.mjs +1 -1
- package/dist/target__runtime.d.mts +2 -2
- package/dist/target__runtime.mjs +3 -3
- package/dist/target__runtime.mjs.map +1 -1
- package/dist/target__sql-utils.mjs +2 -2
- package/dist/target__types.d.mts +2 -0
- package/dist/target__types.mjs +2 -0
- package/dist/{control-_4epnrIW.d.mts → types-CgrFHUdk.d.mts} +3 -8
- package/dist/types-CgrFHUdk.d.mts.map +1 -0
- package/package.json +18 -12
- package/dist/adapter-_WBomuTw.d.mts.map +0 -1
- package/dist/codec-descriptor-Du6DSz-3-ytBCu_mq.mjs.map +0 -1
- package/dist/codec-ids-cjxtOcLM-vFeQhgPm.mjs +0 -10
- package/dist/codec-ids-cjxtOcLM-vFeQhgPm.mjs.map +0 -1
- package/dist/codec-ids-sMOU8F-B-BQbCq2dr.d.mts +0 -10
- package/dist/codec-ids-sMOU8F-B-BQbCq2dr.d.mts.map +0 -1
- package/dist/codec-types-Bd7DL1Mr-CZWNfnp9.d.mts +0 -21
- package/dist/codec-types-Bd7DL1Mr-CZWNfnp9.d.mts.map +0 -1
- package/dist/codec-types-Bo3Jfilx.d.mts +0 -1
- package/dist/codec-types-Ja2kLzLS.d.mts +0 -1
- package/dist/codecs-C00kjHgh.mjs +0 -282
- package/dist/codecs-C00kjHgh.mjs.map +0 -1
- package/dist/codecs-CO_VNEYU-CwAnKzRs.d.mts +0 -102
- package/dist/codecs-CO_VNEYU-CwAnKzRs.d.mts.map +0 -1
- package/dist/codecs-DM61agRr.d.mts +0 -21
- package/dist/codecs-DM61agRr.d.mts.map +0 -1
- package/dist/contract-contributions-B9njRJbt-ClEtrK2L.mjs.map +0 -1
- package/dist/control-WTY7Jpxk.mjs.map +0 -1
- package/dist/control-_4epnrIW.d.mts.map +0 -1
- package/dist/data-type-entries-B0AoBozh-f9AEUIUQ.mjs +0 -42
- package/dist/data-type-entries-B0AoBozh-f9AEUIUQ.mjs.map +0 -1
- package/dist/data-types-BpihGKoK-BMW-m4EN.d.mts +0 -52
- package/dist/data-types-BpihGKoK-BMW-m4EN.d.mts.map +0 -1
- package/dist/data-types-Dv9gwVRV-BZPzCWZk.mjs +0 -181
- package/dist/data-types-Dv9gwVRV-BZPzCWZk.mjs.map +0 -1
- package/dist/data-types-SXrh0Lou.d.mts.map +0 -1
- package/dist/default-normalizer-BDUgHXUn-Xk7HQJuI.mjs.map +0 -1
- package/dist/descriptor-meta-BurjcSiU-Cbk2L1jf.mjs +0 -82
- package/dist/descriptor-meta-BurjcSiU-Cbk2L1jf.mjs.map +0 -1
- package/dist/descriptor-meta-runtime-Bk_00UrX-BLN4Ahpj.mjs +0 -17
- package/dist/descriptor-meta-runtime-Bk_00UrX-BLN4Ahpj.mjs.map +0 -1
- package/dist/errors-rvH_F2Yj-BQrO_--u.mjs +0 -20
- package/dist/errors-rvH_F2Yj-BQrO_--u.mjs.map +0 -1
- package/dist/mysql-contract-serializer-DLv0uW_f-Cg4A43Ry.mjs.map +0 -1
- package/dist/runtime-Cl0BbTZq.d.mts +0 -22
- package/dist/runtime-Cl0BbTZq.d.mts.map +0 -1
- package/dist/statement-kfMRMKIt-D6cAOKys.mjs.map +0 -1
package/dist/driver__runtime.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { c as mysqlConnectionOptions, d as mysqlPoolOptions, f as normalizeMysqlError, i as connectingCannotSucceed, l as mysqlDriverDescriptorMeta, m as statementFor, n as acquireClientQueryLock, s as isFatal, t as SessionSettingsGuard, u as mysqlPoolIdleTimeoutMillis } from "./statement-DC62oiSx-Buzptbpk.mjs";
|
|
2
2
|
import { blindCast } from "@prisma/orm-framework/utils/casts";
|
|
3
3
|
import { ifDefined } from "@prisma/orm-framework/utils/defined";
|
|
4
4
|
import { errorRuntime } from "@prisma/orm-framework/errors/execution";
|
|
@@ -6,6 +6,100 @@ import { createConnection, createPool } from "mariadb";
|
|
|
6
6
|
import { EventEmitter } from "node:events";
|
|
7
7
|
import { finished } from "node:stream/promises";
|
|
8
8
|
//#region ../../../3-targets/mysql/3-driver/dist/runtime.mjs
|
|
9
|
+
/** How long the canceller waits to open its connection, and for MySQL to answer a `KILL QUERY`. */
|
|
10
|
+
const CANCEL_TIMEOUT_MILLIS = 2e3;
|
|
11
|
+
function timeout(millis) {
|
|
12
|
+
let timer;
|
|
13
|
+
return {
|
|
14
|
+
expired: new Promise((resolve) => {
|
|
15
|
+
timer = setTimeout(() => resolve("expired"), millis);
|
|
16
|
+
}),
|
|
17
|
+
clear: () => clearTimeout(timer)
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Cancels statements with `KILL QUERY`, which MySQL takes only from another connection. It keeps one connection of its own, outside the pool, so a cancel neither waits for a pooled connection nor normally opens one; it opens it on first need, and ends it once it has been idle for the pool's idle timeout, as the pool ends its own; with a timeout of 0 it keeps it open, as the pool keeps its own. Opening it and each `KILL QUERY` are bounded by two seconds. The connection is never destroyed: the package's `destroy()` sends a `KILL` of its own whose failure it does not handle (`mariadb` 3.5.4). A connection that stops answering is set aside and ended with `end()` once its statement settles.
|
|
22
|
+
*/
|
|
23
|
+
var Canceller = class {
|
|
24
|
+
#url;
|
|
25
|
+
#idleTimeoutMillis;
|
|
26
|
+
#connection;
|
|
27
|
+
#pending = /* @__PURE__ */ new WeakMap();
|
|
28
|
+
#closed = false;
|
|
29
|
+
#idleTimer;
|
|
30
|
+
constructor(url, idleTimeoutMillis) {
|
|
31
|
+
this.#url = url;
|
|
32
|
+
this.#idleTimeoutMillis = idleTimeoutMillis;
|
|
33
|
+
}
|
|
34
|
+
/** Sends `KILL QUERY` for `threadId`. Never rejects. A kept connection that is no longer valid is replaced once. */
|
|
35
|
+
async cancel(threadId, replaced = false) {
|
|
36
|
+
if (this.#closed || threadId === null) return "unsent";
|
|
37
|
+
clearTimeout(this.#idleTimer);
|
|
38
|
+
const opening = this.#connection ?? this.#open();
|
|
39
|
+
this.#connection = opening;
|
|
40
|
+
const connection = await opening;
|
|
41
|
+
if (connection === void 0) {
|
|
42
|
+
this.#setAside(opening);
|
|
43
|
+
return "unsent";
|
|
44
|
+
}
|
|
45
|
+
if (!connection.isValid()) {
|
|
46
|
+
this.#setAside(opening);
|
|
47
|
+
return replaced ? "unsent" : this.cancel(threadId, true);
|
|
48
|
+
}
|
|
49
|
+
this.#pending.set(connection, this.#pendingOn(connection) + 1);
|
|
50
|
+
const killing = connection.query("KILL QUERY ?", [threadId]).then(() => "sent", (error) => isFatal(error) ? "unconfirmed" : "unsent");
|
|
51
|
+
killing.then(() => {
|
|
52
|
+
this.#pending.set(connection, this.#pendingOn(connection) - 1);
|
|
53
|
+
this.#idleLater(opening, connection);
|
|
54
|
+
});
|
|
55
|
+
const limit = timeout(CANCEL_TIMEOUT_MILLIS);
|
|
56
|
+
const result = await Promise.race([killing, limit.expired]);
|
|
57
|
+
limit.clear();
|
|
58
|
+
if (result === "sent" || result === "unsent") return result;
|
|
59
|
+
this.#setAside(opening);
|
|
60
|
+
connection.end().catch(() => void 0);
|
|
61
|
+
return "unconfirmed";
|
|
62
|
+
}
|
|
63
|
+
/** Ends the connection: `end()` sends `QUIT` once any `KILL QUERY` on it settles. Waits for that only when no `KILL QUERY` is pending on it, so a connection that stopped answering cannot hold `close()` up. */
|
|
64
|
+
async close() {
|
|
65
|
+
this.#closed = true;
|
|
66
|
+
clearTimeout(this.#idleTimer);
|
|
67
|
+
const connection = await this.#connection;
|
|
68
|
+
this.#connection = void 0;
|
|
69
|
+
if (connection === void 0) return;
|
|
70
|
+
const ending = connection.end().catch(() => void 0);
|
|
71
|
+
if (this.#pendingOn(connection) === 0) await ending;
|
|
72
|
+
}
|
|
73
|
+
#idleLater(opening, connection) {
|
|
74
|
+
if (this.#idleTimeoutMillis === 0 || this.#pendingOn(connection) > 0 || this.#connection !== opening) return;
|
|
75
|
+
clearTimeout(this.#idleTimer);
|
|
76
|
+
this.#idleTimer = setTimeout(() => {
|
|
77
|
+
if (this.#pendingOn(connection) > 0 || this.#connection !== opening) return;
|
|
78
|
+
this.#connection = void 0;
|
|
79
|
+
connection.end().catch(() => void 0);
|
|
80
|
+
}, this.#idleTimeoutMillis);
|
|
81
|
+
this.#idleTimer.unref();
|
|
82
|
+
}
|
|
83
|
+
#pendingOn(connection) {
|
|
84
|
+
return this.#pending.get(connection) ?? 0;
|
|
85
|
+
}
|
|
86
|
+
#setAside(opening) {
|
|
87
|
+
if (this.#connection === opening) this.#connection = void 0;
|
|
88
|
+
}
|
|
89
|
+
async #open() {
|
|
90
|
+
try {
|
|
91
|
+
const connection = await createConnection(mysqlConnectionOptions(this.#url, { connectTimeoutMs: CANCEL_TIMEOUT_MILLIS }));
|
|
92
|
+
connection.on("error", () => void 0);
|
|
93
|
+
if (this.#closed) {
|
|
94
|
+
await connection.end().catch(() => void 0);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
return connection;
|
|
98
|
+
} catch {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
};
|
|
9
103
|
function notConnected(message) {
|
|
10
104
|
return errorRuntime("DRIVER.NOT_CONNECTED", message, { fix: "Call connect(binding) before running queries, and do not use the driver after close()." });
|
|
11
105
|
}
|
|
@@ -23,8 +117,15 @@ function startStream(client, request, outcome) {
|
|
|
23
117
|
throw settle(error, outcome);
|
|
24
118
|
}
|
|
25
119
|
}
|
|
26
|
-
|
|
27
|
-
|
|
120
|
+
/** What a statement's result reports: the rows it changed, and, when it changed one row, the value MySQL numbered for it. MySQL reports 0 when it numbered none. */
|
|
121
|
+
function statementStats(result) {
|
|
122
|
+
if (typeof result !== "object" || result === null) return { affectedRows: 0 };
|
|
123
|
+
const affectedRows = "affectedRows" in result && typeof result.affectedRows === "number" ? result.affectedRows : 0;
|
|
124
|
+
const insertId = "insertId" in result ? result.insertId : void 0;
|
|
125
|
+
return affectedRows === 1 && (typeof insertId === "bigint" || typeof insertId === "number") && insertId > 0 ? {
|
|
126
|
+
affectedRows,
|
|
127
|
+
insertId: String(insertId)
|
|
128
|
+
} : { affectedRows };
|
|
28
129
|
}
|
|
29
130
|
/** The error to report for `error`, after noting in `outcome` whether it left the client unusable. */
|
|
30
131
|
function settle(error, outcome) {
|
|
@@ -38,56 +139,58 @@ function resultReceived(rows) {
|
|
|
38
139
|
const state = Reflect.get(rows, "_readableState");
|
|
39
140
|
return typeof state === "object" && state !== null && "ended" in state && state.ended === true;
|
|
40
141
|
}
|
|
41
|
-
/**
|
|
142
|
+
/**
|
|
143
|
+
* Reads and drops what is left of a stream's result. The package's `close()` stops it keeping new rows; the rows already buffered are read here with `read()`, which works whether or not the caller's iterator is still attached to the stream, so an iterator the caller abandoned cannot hold the drain up.
|
|
144
|
+
*/
|
|
42
145
|
async function dropRest(rows) {
|
|
43
146
|
rows.close?.();
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
/** The package can report an error on a stream after the caller stopped reading it, such as the one for a connection destroyed mid-result; the generator has already settled by then. */
|
|
49
|
-
function ignoreAfterStop() {}
|
|
50
|
-
/** Waiting for a statement to end needs only that it settled; its own caller handles how. */
|
|
51
|
-
function settledEither() {}
|
|
52
|
-
/** Waits for `work` to settle, but no longer than `ms`. */
|
|
53
|
-
async function settledWithin(work, ms) {
|
|
54
|
-
let timer;
|
|
55
|
-
const elapsed = new Promise((resolve) => {
|
|
56
|
-
timer = setTimeout(resolve, ms);
|
|
57
|
-
});
|
|
147
|
+
const drain = () => {
|
|
148
|
+
while (rows.read() !== null);
|
|
149
|
+
};
|
|
150
|
+
rows.on("readable", drain);
|
|
58
151
|
try {
|
|
59
|
-
|
|
152
|
+
drain();
|
|
153
|
+
await finished(rows);
|
|
60
154
|
} finally {
|
|
61
|
-
|
|
155
|
+
rows.off("readable", drain);
|
|
62
156
|
}
|
|
63
157
|
}
|
|
64
|
-
function
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
await connection.query("KILL QUERY ?", [threadId]).catch(killReplyLost);
|
|
79
|
-
connection.destroy();
|
|
80
|
-
return true;
|
|
158
|
+
function cancelNotConfirmed() {
|
|
159
|
+
return errorRuntime("DRIVER.NOT_CONNECTED", "A cancel on this MySQL connection could not be confirmed, so the connection can no longer be used", {
|
|
160
|
+
why: "The driver cancelled a stream the caller stopped early, and MySQL did not answer the KILL QUERY in time. It may still arrive and would interrupt whatever statement the connection runs then.",
|
|
161
|
+
fix: "Release the connection, which closes it; MySQL rolls back an open transaction on it. Acquire another connection and run the work again."
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
function connectionReleased() {
|
|
165
|
+
return errorRuntime("DRIVER.NOT_CONNECTED", "MySQL connection was released", { fix: "Send statements on a connection only until you release it, and await each one before release()." });
|
|
166
|
+
}
|
|
167
|
+
/** The package can report an error on a stream after the caller stopped reading it, such as MySQL's for a statement the driver cancelled; the generator has already settled by then. */
|
|
168
|
+
function ignoreAfterStop() {}
|
|
169
|
+
/** MySQL's error for a statement `KILL QUERY` cancelled (`ER_QUERY_INTERRUPTED`). The package marks it fatal when it arrives in the middle of a result, but the connection stays usable. */
|
|
170
|
+
function isQueryInterrupted(error) {
|
|
171
|
+
return error instanceof Error && "errno" in error && error.errno === 1317;
|
|
81
172
|
}
|
|
82
173
|
var MysqlQueryable = class {
|
|
83
174
|
streams;
|
|
84
|
-
/** Called with each stream when it starts and with `undefined` when it ends, so a held connection can end a stream its caller abandoned. */
|
|
85
|
-
streamChanged(_rows) {}
|
|
86
|
-
/** Called with each buffered statement as it starts, so a held connection can wait for it to end. */
|
|
87
|
-
statementRunning(_running) {}
|
|
88
175
|
constructor(streams) {
|
|
89
176
|
this.streams = streams;
|
|
90
177
|
}
|
|
178
|
+
/** Records a stream that is open, and returns the call that records its end. */
|
|
179
|
+
streamOpened(_rows) {
|
|
180
|
+
return () => {};
|
|
181
|
+
}
|
|
182
|
+
/** Takes the client's lock, which keeps statements on it one at a time in the order sent. */
|
|
183
|
+
lockClient(client) {
|
|
184
|
+
return acquireClientQueryLock(client);
|
|
185
|
+
}
|
|
186
|
+
/** Runs one statement that is not a stream, under the client's lock. */
|
|
187
|
+
runStatement(work) {
|
|
188
|
+
return work();
|
|
189
|
+
}
|
|
190
|
+
/** Whether the client went back to the pool under this queryable, so it must send nothing more on it. */
|
|
191
|
+
get handedBack() {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
91
194
|
async *query(request) {
|
|
92
195
|
if (this.streams) {
|
|
93
196
|
yield* this.#streamRows(request);
|
|
@@ -97,17 +200,15 @@ var MysqlQueryable = class {
|
|
|
97
200
|
for (const row of Array.isArray(result) ? result : []) yield blindCast(row);
|
|
98
201
|
}
|
|
99
202
|
async execute(request) {
|
|
100
|
-
return
|
|
203
|
+
return statementStats(await this.#runReleasing(request));
|
|
101
204
|
}
|
|
102
205
|
async #runReleasing(request) {
|
|
103
206
|
const client = await this.acquireClient();
|
|
104
207
|
const outcome = { destroy: false };
|
|
105
208
|
try {
|
|
106
|
-
const releaseLock = await
|
|
209
|
+
const releaseLock = await this.lockClient(client);
|
|
107
210
|
try {
|
|
108
|
-
|
|
109
|
-
this.statementRunning(running);
|
|
110
|
-
return await running;
|
|
211
|
+
return await this.runStatement(() => run(client, request));
|
|
111
212
|
} finally {
|
|
112
213
|
releaseLock();
|
|
113
214
|
}
|
|
@@ -118,17 +219,22 @@ var MysqlQueryable = class {
|
|
|
118
219
|
}
|
|
119
220
|
}
|
|
120
221
|
/**
|
|
121
|
-
* Streams the rows of `request` as they arrive; the package pauses the socket while the caller is busy. A caller that stops reading early is handled by {@link MysqlQueryable.#stopEarly}
|
|
222
|
+
* Streams the rows of `request` as they arrive; the package pauses the socket while the caller is busy. A caller that stops reading early is handled by {@link MysqlQueryable.#stopEarly}. A fatal error, such as a lost connection, destroys the client.
|
|
122
223
|
*/
|
|
123
224
|
async *#streamRows(request) {
|
|
124
225
|
const client = await this.acquireClient();
|
|
125
226
|
const outcome = { destroy: false };
|
|
126
|
-
|
|
127
|
-
|
|
227
|
+
let releaseLock;
|
|
228
|
+
try {
|
|
229
|
+
releaseLock = await this.lockClient(client);
|
|
230
|
+
} catch (error) {
|
|
231
|
+
await this.releaseClient(client, outcome);
|
|
232
|
+
throw error;
|
|
233
|
+
}
|
|
128
234
|
try {
|
|
129
235
|
const rows = startStream(client, request, outcome);
|
|
130
236
|
rows.on("error", ignoreAfterStop);
|
|
131
|
-
this.
|
|
237
|
+
const streamClosed = this.streamOpened(rows);
|
|
132
238
|
let settled = false;
|
|
133
239
|
try {
|
|
134
240
|
for await (const row of rows.iterator({ destroyOnReturn: false })) yield blindCast(row);
|
|
@@ -137,94 +243,126 @@ var MysqlQueryable = class {
|
|
|
137
243
|
settled = true;
|
|
138
244
|
throw settle(error, outcome);
|
|
139
245
|
} finally {
|
|
140
|
-
if (!settled && !this.
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
} else if (!settled) await this.#stopEarly(rows, outcome);
|
|
144
|
-
if (outcome.destroy && ending === void 0) rows.destroy();
|
|
145
|
-
this.streamChanged(void 0);
|
|
246
|
+
if (!settled && !this.handedBack) await this.#stopEarly(client, rows, outcome);
|
|
247
|
+
if (outcome.destroy) rows.destroy();
|
|
248
|
+
streamClosed();
|
|
146
249
|
}
|
|
147
250
|
} finally {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
await this.releaseClient(client, outcome);
|
|
151
|
-
} else ending.then(() => {
|
|
152
|
-
releaseLock();
|
|
153
|
-
return this.releaseClient(client, outcome);
|
|
154
|
-
}).catch(ignoreAfterStop);
|
|
251
|
+
releaseLock();
|
|
252
|
+
await this.releaseClient(client, outcome);
|
|
155
253
|
}
|
|
156
254
|
}
|
|
157
|
-
/**
|
|
158
|
-
|
|
255
|
+
/**
|
|
256
|
+
* Ends a stream the caller stopped reading early, and keeps the client usable. While its result is still arriving, the statement is first cancelled with `KILL QUERY`, so a large result is not downloaded only to be thrown away; MySQL does not say how many rows remain. On a held connection or in a transaction, as on a pooled one: `KILL QUERY` ends only the statement, the session survives, and in a transaction MySQL rolls back only that statement. The rest of the result is then read and dropped, MySQL's error for the cancelled statement among it. The client is never destroyed while its statement runs: the package's `destroy()` would send `KILL` itself, on a connection whose failure it does not handle.
|
|
257
|
+
*/
|
|
258
|
+
async #stopEarly(client, rows, outcome) {
|
|
259
|
+
const cancel = resultReceived(rows) ? "unsent" : await this.cancelStatement(client.threadId);
|
|
159
260
|
await dropRest(rows).catch((error) => {
|
|
160
|
-
if (isFatal(error)) outcome.destroy = true;
|
|
261
|
+
if (isFatal(error) && !(cancel !== "unsent" && isQueryInterrupted(error))) outcome.destroy = true;
|
|
161
262
|
});
|
|
263
|
+
if (cancel === "unconfirmed") this.cancelUnconfirmed(outcome);
|
|
162
264
|
}
|
|
163
265
|
/**
|
|
164
|
-
*
|
|
266
|
+
* A cancel that went out without an answer may still reach MySQL and interrupt the next statement on the client, so the client must not serve another caller. Once its rest has been read, nothing is receiving on it, so closing it sends no `KILL` of the package's own; a late `KILL QUERY` then finds no thread.
|
|
165
267
|
*/
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
rows.destroy();
|
|
268
|
+
cancelUnconfirmed(outcome) {
|
|
269
|
+
outcome.destroy = true;
|
|
169
270
|
}
|
|
170
271
|
};
|
|
272
|
+
function handedBackState() {
|
|
273
|
+
let refuse = () => {};
|
|
274
|
+
const signal = new Promise((_, reject) => {
|
|
275
|
+
refuse = () => reject(connectionReleased());
|
|
276
|
+
});
|
|
277
|
+
signal.catch(() => void 0);
|
|
278
|
+
const state = {
|
|
279
|
+
done: false,
|
|
280
|
+
signal,
|
|
281
|
+
refuse: () => {
|
|
282
|
+
state.done = true;
|
|
283
|
+
refuse();
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
return state;
|
|
287
|
+
}
|
|
171
288
|
/**
|
|
172
289
|
* A queryable on a connection the caller holds, whose statements all run on the same session, in the order they are sent, under the client's lock. A statement sent while a stream on the connection is open waits for that stream to end; one awaited inside the stream's own loop never finishes, as on Postgres with a cursor.
|
|
173
290
|
*/
|
|
174
291
|
var MysqlHeldQueryable = class extends MysqlQueryable {
|
|
175
292
|
client;
|
|
176
293
|
held;
|
|
177
|
-
end;
|
|
178
294
|
holdsClient = true;
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
this.
|
|
295
|
+
get cancelStatement() {
|
|
296
|
+
return this.held.cancel;
|
|
297
|
+
}
|
|
298
|
+
streamOpened(rows) {
|
|
299
|
+
this.held.streams.add(rows);
|
|
300
|
+
return () => this.held.streams.delete(rows);
|
|
301
|
+
}
|
|
302
|
+
get handedBack() {
|
|
303
|
+
return this.held.handedBack.done;
|
|
304
|
+
}
|
|
305
|
+
cancelUnconfirmed() {
|
|
306
|
+
this.held.cancelUnconfirmed = true;
|
|
184
307
|
}
|
|
185
|
-
|
|
186
|
-
|
|
308
|
+
/** Takes the client's lock, or fails once the connection is handed back, whether the statement was sent after that or was still waiting for the lock. A lock taken after that is given straight back. */
|
|
309
|
+
async lockClient(client) {
|
|
310
|
+
this.#refuseIfUnusable();
|
|
311
|
+
const locking = acquireClientQueryLock(client);
|
|
312
|
+
try {
|
|
313
|
+
const releaseLock = await Promise.race([locking, this.held.handedBack.signal]);
|
|
314
|
+
if (!this.held.handedBack.done && !this.held.cancelUnconfirmed) return releaseLock;
|
|
315
|
+
releaseLock();
|
|
316
|
+
} catch {
|
|
317
|
+
locking.then((releaseLock) => releaseLock());
|
|
318
|
+
}
|
|
319
|
+
this.#refuseIfUnusable();
|
|
320
|
+
throw connectionReleased();
|
|
187
321
|
}
|
|
188
|
-
|
|
189
|
-
|
|
322
|
+
/** Refuses a statement on a connection that was handed back, or whose cancel went out without an answer: a late `KILL QUERY` would interrupt it. */
|
|
323
|
+
#refuseIfUnusable() {
|
|
324
|
+
if (this.held.handedBack.done) throw connectionReleased();
|
|
325
|
+
if (this.held.cancelUnconfirmed) throw cancelNotConfirmed();
|
|
190
326
|
}
|
|
191
|
-
|
|
192
|
-
const
|
|
193
|
-
this.held.
|
|
194
|
-
|
|
327
|
+
runStatement(work) {
|
|
328
|
+
const running = work();
|
|
329
|
+
this.held.running.add(running);
|
|
330
|
+
running.then(() => this.held.running.delete(running), () => this.held.running.delete(running));
|
|
331
|
+
return running;
|
|
332
|
+
}
|
|
333
|
+
constructor(client, held, streams) {
|
|
334
|
+
super(streams);
|
|
335
|
+
this.client = client;
|
|
336
|
+
this.held = held;
|
|
195
337
|
}
|
|
196
338
|
async acquireClient() {
|
|
197
|
-
this
|
|
339
|
+
this.#refuseIfUnusable();
|
|
198
340
|
return this.client;
|
|
199
341
|
}
|
|
200
|
-
async releaseClient() {
|
|
201
|
-
this.held.running -= 1;
|
|
202
|
-
}
|
|
342
|
+
async releaseClient() {}
|
|
203
343
|
/** Runs one of the package's transaction calls under the client's lock, taken as a statement takes it, so it keeps its place among the statements sent. */
|
|
204
344
|
async underLock(call) {
|
|
205
345
|
const client = await this.acquireClient();
|
|
346
|
+
const releaseLock = await this.lockClient(client);
|
|
206
347
|
try {
|
|
207
|
-
|
|
208
|
-
try {
|
|
209
|
-
await call();
|
|
210
|
-
} finally {
|
|
211
|
-
releaseLock();
|
|
212
|
-
}
|
|
348
|
+
await this.runStatement(call);
|
|
213
349
|
} catch (error) {
|
|
214
350
|
throw normalizeMysqlError(error);
|
|
215
351
|
} finally {
|
|
216
|
-
|
|
352
|
+
releaseLock();
|
|
217
353
|
}
|
|
218
354
|
}
|
|
219
355
|
};
|
|
220
356
|
var MysqlConnectionImpl = class extends MysqlHeldQueryable {
|
|
221
357
|
#onRelease;
|
|
222
|
-
constructor(client, streams,
|
|
358
|
+
constructor(client, streams, cancel, onRelease) {
|
|
223
359
|
super(client, {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
360
|
+
streams: /* @__PURE__ */ new Set(),
|
|
361
|
+
cancelUnconfirmed: false,
|
|
362
|
+
running: /* @__PURE__ */ new Set(),
|
|
363
|
+
cancel,
|
|
364
|
+
handedBack: handedBackState()
|
|
365
|
+
}, streams);
|
|
228
366
|
this.#onRelease = onRelease;
|
|
229
367
|
}
|
|
230
368
|
#released() {
|
|
@@ -234,28 +372,46 @@ var MysqlConnectionImpl = class extends MysqlHeldQueryable {
|
|
|
234
372
|
}
|
|
235
373
|
async beginTransaction() {
|
|
236
374
|
await this.underLock(() => this.client.beginTransaction());
|
|
237
|
-
return new MysqlTransactionImpl(this.client, this.held, this.streams
|
|
375
|
+
return new MysqlTransactionImpl(this.client, this.held, this.streams);
|
|
238
376
|
}
|
|
239
377
|
/**
|
|
240
|
-
* Returns the connection to the pool, as the Postgres driver's `release()` does.
|
|
378
|
+
* Returns the connection to the pool, as the Postgres driver's `release()` does. From then on the connection refuses every statement, those still waiting for its lock included, so none runs later in another caller's session. A statement that is not a stream and is already running finishes, as on Postgres. A stream still open, such as one the caller dropped, is cancelled and read to its end by the driver: the package's release sends `ROLLBACK` behind that statement and waits for it, which would otherwise never finish, where `pg`'s release does not wait on the server.
|
|
241
379
|
*/
|
|
242
380
|
async release() {
|
|
243
|
-
if (this.held.
|
|
381
|
+
if (this.held.handedBack.done) return;
|
|
382
|
+
this.held.handedBack.refuse();
|
|
244
383
|
try {
|
|
245
|
-
|
|
384
|
+
if (this.held.streams.size > 0) await this.#cancelRunning();
|
|
385
|
+
await this.#runningSettled();
|
|
386
|
+
if (this.held.cancelUnconfirmed) await this.#close();
|
|
387
|
+
else await this.client.release();
|
|
246
388
|
} finally {
|
|
247
389
|
this.#released();
|
|
248
390
|
}
|
|
249
391
|
}
|
|
250
|
-
/**
|
|
392
|
+
/** Closes the connection instead of returning it. It refuses every statement from then on, cancels whatever still runs and waits for it to end, so the package's `destroy()` never sends a `KILL` of its own. */
|
|
251
393
|
async destroy(_reason) {
|
|
252
|
-
if (this.held.
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
394
|
+
if (this.held.handedBack.done) return;
|
|
395
|
+
this.held.handedBack.refuse();
|
|
396
|
+
try {
|
|
397
|
+
if (this.held.streams.size > 0 || this.held.running.size > 0) await this.#cancelRunning();
|
|
398
|
+
await this.#runningSettled();
|
|
399
|
+
await this.#close();
|
|
400
|
+
} finally {
|
|
401
|
+
this.#released();
|
|
256
402
|
}
|
|
403
|
+
}
|
|
404
|
+
/** Closes the client once the `ping` queued behind anything left on it has answered, so nothing is receiving and the package's `destroy()` sends no `KILL` of its own. */
|
|
405
|
+
async #close() {
|
|
406
|
+
await this.client.ping().catch(() => void 0);
|
|
257
407
|
this.client.destroy();
|
|
258
|
-
|
|
408
|
+
}
|
|
409
|
+
async #cancelRunning() {
|
|
410
|
+
if (await this.held.cancel(this.client.threadId) === "unconfirmed") this.held.cancelUnconfirmed = true;
|
|
411
|
+
await Promise.all([...this.held.streams].map((rows) => dropRest(rows).catch(() => void 0)));
|
|
412
|
+
}
|
|
413
|
+
async #runningSettled() {
|
|
414
|
+
await Promise.allSettled([...this.held.running]);
|
|
259
415
|
}
|
|
260
416
|
};
|
|
261
417
|
var MysqlTransactionImpl = class extends MysqlHeldQueryable {
|
|
@@ -319,9 +475,9 @@ var MysqlDriver = class extends MysqlQueryable {
|
|
|
319
475
|
targetId = "mysql";
|
|
320
476
|
holdsClient = false;
|
|
321
477
|
#state = { kind: "unbound" };
|
|
322
|
-
/** The options of the short-lived connection that interrupts a statement, and how long to wait for an interrupted statement to end: as long as a connection may take to open, or the default when `connectionTimeoutMillis` is 0, which the package reads as no limit. Set by `connect()` and kept through `close()`, which waits for statements still ending. */
|
|
323
|
-
#interrupt;
|
|
324
478
|
#inUse = new ClientsInUse();
|
|
479
|
+
#canceller;
|
|
480
|
+
cancelStatement = (threadId) => this.#canceller?.cancel(threadId) ?? Promise.resolve("unsent");
|
|
325
481
|
#options;
|
|
326
482
|
#sessionSettings;
|
|
327
483
|
constructor(options = {}) {
|
|
@@ -340,33 +496,23 @@ var MysqlDriver = class extends MysqlQueryable {
|
|
|
340
496
|
logger: { error: (error) => waiting.reported(error) }
|
|
341
497
|
});
|
|
342
498
|
if (pool instanceof EventEmitter) pool.on("error", ignoreConnectionFailure);
|
|
343
|
-
const connectionTimeout = this.#options.poolOptions?.connectionTimeoutMillis;
|
|
344
|
-
const interruptTimeoutMs = connectionTimeout !== void 0 && connectionTimeout > 0 ? connectionTimeout : DEFAULT_CONNECT_TIMEOUT_MILLIS;
|
|
345
|
-
this.#interrupt = {
|
|
346
|
-
options: mysqlConnectionOptions(binding.url, { connectTimeoutMs: interruptTimeoutMs }),
|
|
347
|
-
waitMs: interruptTimeoutMs
|
|
348
|
-
};
|
|
349
499
|
this.#state = {
|
|
350
500
|
kind: "connected",
|
|
351
501
|
pool,
|
|
352
502
|
waiting
|
|
353
503
|
};
|
|
354
|
-
|
|
355
|
-
/** See {@link EndStatement}. When the `KILL` cannot be sent at all, there is no wait: the client is destroyed at once, and the package interrupts the statement itself. */
|
|
356
|
-
async endStatement(client, ended) {
|
|
357
|
-
const settled = ended.then(settledEither, settledEither);
|
|
358
|
-
const interrupt = this.#interrupt;
|
|
359
|
-
if (interrupt === void 0 || client.threadId === null) return;
|
|
360
|
-
if (await interruptStatement(interrupt.options, client.threadId)) await settledWithin(settled, interrupt.waitMs);
|
|
504
|
+
this.#canceller = new Canceller(binding.url, mysqlPoolIdleTimeoutMillis(this.#options.poolOptions));
|
|
361
505
|
}
|
|
362
506
|
async acquireConnection() {
|
|
363
|
-
return new MysqlConnectionImpl(await this.acquireClient(), this.streams,
|
|
507
|
+
return new MysqlConnectionImpl(await this.acquireClient(), this.streams, this.cancelStatement, () => this.#inUse.released());
|
|
364
508
|
}
|
|
365
509
|
async close() {
|
|
366
510
|
if (this.#state.kind !== "connected") return;
|
|
367
511
|
const { pool } = this.#state;
|
|
368
512
|
this.#state = { kind: "closed" };
|
|
369
513
|
await this.#inUse.none();
|
|
514
|
+
await this.#canceller?.close();
|
|
515
|
+
this.#canceller = void 0;
|
|
370
516
|
await pool.end();
|
|
371
517
|
}
|
|
372
518
|
async acquireClient() {
|
|
@@ -391,6 +537,9 @@ var MysqlDriver = class extends MysqlQueryable {
|
|
|
391
537
|
}
|
|
392
538
|
return client;
|
|
393
539
|
}
|
|
540
|
+
/**
|
|
541
|
+
* Destroys a client only after an error the package marks fatal, or after a cancel that went out without an answer once the rest of its result was read (`cancelUnconfirmed`). The package marks an error fatal when the connection's socket is gone or can no longer be trusted, and it has then already set the connection closed and failed every command on it (`fatalError` in its `connection.js`), so no command is receiving and its `destroy()` sends no `KILL`. A statement the caller stopped early is cancelled and read to its end instead (`#stopEarly`).
|
|
542
|
+
*/
|
|
394
543
|
async releaseClient(client, { destroy }) {
|
|
395
544
|
try {
|
|
396
545
|
if (destroy) client.destroy();
|