mysql2 3.23.4 → 3.23.5-canary.8ec20f1
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/index.js +5 -0
- package/lib/commands/execute.js +2 -1
- package/lib/constants/types.js +1 -0
- package/lib/packets/encode_parameter.js +28 -2
- package/lib/packets/execute.js +14 -6
- package/lib/packets/packet.js +33 -2
- package/lib/packets/typed_parameter.js +381 -0
- package/package.json +1 -1
- package/promise.js +5 -0
- package/typings/mysql/index.d.ts +1 -0
- package/typings/mysql/lib/TypedParameter.d.ts +84 -0
- package/typings/mysql/lib/protocol/sequences/Query.d.ts +2 -0
package/index.js
CHANGED
|
@@ -60,6 +60,11 @@ exports.__defineGetter__(
|
|
|
60
60
|
|
|
61
61
|
exports.__defineGetter__('Types', () => require('./lib/constants/types.js'));
|
|
62
62
|
|
|
63
|
+
exports.__defineGetter__(
|
|
64
|
+
'TypedParameter',
|
|
65
|
+
() => require('./lib/packets/typed_parameter.js').types
|
|
66
|
+
);
|
|
67
|
+
|
|
63
68
|
exports.__defineGetter__('Charsets', () =>
|
|
64
69
|
require('./lib/constants/charsets.js')
|
|
65
70
|
);
|
package/lib/commands/execute.js
CHANGED
|
@@ -54,7 +54,8 @@ class Execute extends Command {
|
|
|
54
54
|
connection.config.timezone,
|
|
55
55
|
this._executeOptions.attributes,
|
|
56
56
|
clientFlags,
|
|
57
|
-
connection._isMariaDB
|
|
57
|
+
connection._isMariaDB,
|
|
58
|
+
this.statement.parameters
|
|
58
59
|
);
|
|
59
60
|
//For reasons why this try-catch is here, please see
|
|
60
61
|
// https://github.com/sidorares/node-mysql2/pull/689
|
package/lib/constants/types.js
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
const Types = require('../constants/types');
|
|
4
4
|
const Packet = require('../packets/packet');
|
|
5
|
+
const {
|
|
6
|
+
TypedParameter,
|
|
7
|
+
encodeTypedParameter,
|
|
8
|
+
integerHint,
|
|
9
|
+
} = require('./typed_parameter.js');
|
|
10
|
+
const FieldFlags = require('../constants/field_flags.js');
|
|
5
11
|
|
|
6
12
|
function isJSON(value) {
|
|
7
13
|
return (
|
|
@@ -11,7 +17,20 @@ function isJSON(value) {
|
|
|
11
17
|
);
|
|
12
18
|
}
|
|
13
19
|
|
|
14
|
-
function toParameter(value, encoding, timezone, jsonAsString) {
|
|
20
|
+
function toParameter(value, encoding, timezone, jsonAsString, hint) {
|
|
21
|
+
if (value instanceof TypedParameter) {
|
|
22
|
+
return encodeTypedParameter(value, encoding, timezone, jsonAsString);
|
|
23
|
+
}
|
|
24
|
+
if (hint) {
|
|
25
|
+
const hinted = integerHint(
|
|
26
|
+
value,
|
|
27
|
+
hint.columnType,
|
|
28
|
+
Boolean(hint.flags & FieldFlags.UNSIGNED)
|
|
29
|
+
);
|
|
30
|
+
if (hinted) {
|
|
31
|
+
return encodeTypedParameter(hinted, encoding, timezone, jsonAsString);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
15
34
|
let type = Types.VAR_STRING;
|
|
16
35
|
let length;
|
|
17
36
|
let writer = function (value) {
|
|
@@ -71,7 +90,14 @@ function toParameter(value, encoding, timezone, jsonAsString) {
|
|
|
71
90
|
if (!length) {
|
|
72
91
|
length = Packet.lengthCodedStringLength(value, encoding);
|
|
73
92
|
}
|
|
74
|
-
return {
|
|
93
|
+
return {
|
|
94
|
+
value,
|
|
95
|
+
type,
|
|
96
|
+
length,
|
|
97
|
+
writer,
|
|
98
|
+
unsigned: false,
|
|
99
|
+
isNull: type === Types.NULL,
|
|
100
|
+
};
|
|
75
101
|
}
|
|
76
102
|
|
|
77
103
|
module.exports = { toParameter, isJSON };
|
package/lib/packets/execute.js
CHANGED
|
@@ -16,7 +16,8 @@ class Execute {
|
|
|
16
16
|
timezone,
|
|
17
17
|
attributes,
|
|
18
18
|
clientFlags,
|
|
19
|
-
jsonAsString
|
|
19
|
+
jsonAsString,
|
|
20
|
+
parameterDefinitions
|
|
20
21
|
) {
|
|
21
22
|
this.id = id;
|
|
22
23
|
this.parameters = parameters;
|
|
@@ -25,6 +26,7 @@ class Execute {
|
|
|
25
26
|
this.attributes = attributes;
|
|
26
27
|
this.clientFlags = clientFlags || 0;
|
|
27
28
|
this.jsonAsString = jsonAsString || false;
|
|
29
|
+
this.parameterDefinitions = parameterDefinitions || [];
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
static fromPacket(packet, encoding) {
|
|
@@ -124,8 +126,14 @@ class Execute {
|
|
|
124
126
|
if (totalParams > 0) {
|
|
125
127
|
const bindParams =
|
|
126
128
|
numParams > 0
|
|
127
|
-
? this.parameters.map((v) =>
|
|
128
|
-
toParameter(
|
|
129
|
+
? this.parameters.map((v, i) =>
|
|
130
|
+
toParameter(
|
|
131
|
+
v,
|
|
132
|
+
this.encoding,
|
|
133
|
+
this.timezone,
|
|
134
|
+
this.jsonAsString,
|
|
135
|
+
this.parameterDefinitions[i]
|
|
136
|
+
)
|
|
129
137
|
)
|
|
130
138
|
: [];
|
|
131
139
|
const attrParams = attrNames.map((name) =>
|
|
@@ -137,7 +145,7 @@ class Execute {
|
|
|
137
145
|
let bitmap = 0;
|
|
138
146
|
let bitValue = 1;
|
|
139
147
|
allParams.forEach((parameter) => {
|
|
140
|
-
if (parameter.
|
|
148
|
+
if (parameter.isNull) {
|
|
141
149
|
bitmap += bitValue;
|
|
142
150
|
}
|
|
143
151
|
bitValue *= 2;
|
|
@@ -156,7 +164,7 @@ class Execute {
|
|
|
156
164
|
// types (and names for attributes)
|
|
157
165
|
for (let i = 0; i < allParams.length; i++) {
|
|
158
166
|
packet.writeInt8(allParams[i].type);
|
|
159
|
-
packet.writeInt8(0);
|
|
167
|
+
packet.writeInt8(allParams[i].unsigned ? 0x80 : 0);
|
|
160
168
|
if (useQueryAttributes) {
|
|
161
169
|
const name = i < numParams ? '' : attrNames[i - numParams];
|
|
162
170
|
packet.writeLengthCodedString(name, this.encoding);
|
|
@@ -165,7 +173,7 @@ class Execute {
|
|
|
165
173
|
|
|
166
174
|
// values
|
|
167
175
|
allParams.forEach((parameter) => {
|
|
168
|
-
if (parameter.
|
|
176
|
+
if (!parameter.isNull) {
|
|
169
177
|
parameter.writer.call(packet, parameter.value);
|
|
170
178
|
}
|
|
171
179
|
});
|
package/lib/packets/packet.js
CHANGED
|
@@ -11,6 +11,7 @@ const Long = require('long');
|
|
|
11
11
|
const StringParser = require('../parsers/string.js');
|
|
12
12
|
const Types = require('../constants/types.js');
|
|
13
13
|
const INVALID_DATE = new Date(NaN);
|
|
14
|
+
const ZERO_DATE = '0000-00-00';
|
|
14
15
|
|
|
15
16
|
// this is nearly duplicate of previous function so generated code is not slower
|
|
16
17
|
// due to "if (dateStrings)" branching
|
|
@@ -317,7 +318,7 @@ class Packet {
|
|
|
317
318
|
return new Date(y, m - 1, d, H, M, S, ms);
|
|
318
319
|
}
|
|
319
320
|
let str = this.readDateTimeString(6, 'T', null);
|
|
320
|
-
if (
|
|
321
|
+
if (str.startsWith(ZERO_DATE)) {
|
|
321
322
|
return INVALID_DATE;
|
|
322
323
|
}
|
|
323
324
|
if (str.length === 10) {
|
|
@@ -335,7 +336,7 @@ class Packet {
|
|
|
335
336
|
let M = 0;
|
|
336
337
|
let S = 0;
|
|
337
338
|
let ms = 0;
|
|
338
|
-
let str;
|
|
339
|
+
let str = ZERO_DATE;
|
|
339
340
|
if (length > 3) {
|
|
340
341
|
y = this.readInt16();
|
|
341
342
|
m = this.readInt8();
|
|
@@ -863,6 +864,36 @@ class Packet {
|
|
|
863
864
|
this.offset += 8;
|
|
864
865
|
}
|
|
865
866
|
|
|
867
|
+
writeFloat(n) {
|
|
868
|
+
this.buffer.writeFloatLE(n, this.offset);
|
|
869
|
+
this.offset += 4;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
writeUIntLE(n, bytes) {
|
|
873
|
+
if (bytes === 8) {
|
|
874
|
+
this.buffer.writeBigUInt64LE(n, this.offset);
|
|
875
|
+
} else {
|
|
876
|
+
this.buffer.writeUIntLE(Number(n), this.offset, bytes);
|
|
877
|
+
}
|
|
878
|
+
this.offset += bytes;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
writeTime({ negative, days, hours, minutes, seconds, microseconds }) {
|
|
882
|
+
if (!days && !hours && !minutes && !seconds && !microseconds) {
|
|
883
|
+
this.writeInt8(0);
|
|
884
|
+
return;
|
|
885
|
+
}
|
|
886
|
+
this.writeInt8(microseconds ? 12 : 8);
|
|
887
|
+
this.writeInt8(negative ? 1 : 0);
|
|
888
|
+
this.writeInt32(days);
|
|
889
|
+
this.writeInt8(hours);
|
|
890
|
+
this.writeInt8(minutes);
|
|
891
|
+
this.writeInt8(seconds);
|
|
892
|
+
if (microseconds) {
|
|
893
|
+
this.writeInt32(microseconds);
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
|
|
866
897
|
writeBuffer(b) {
|
|
867
898
|
b.copy(this.buffer, this.offset);
|
|
868
899
|
this.offset += b.length;
|
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Types = require('../constants/types.js');
|
|
4
|
+
const Packet = require('./packet.js');
|
|
5
|
+
|
|
6
|
+
const INTEGER_BYTES = {
|
|
7
|
+
[Types.TINY]: 1,
|
|
8
|
+
[Types.SHORT]: 2,
|
|
9
|
+
[Types.YEAR]: 2,
|
|
10
|
+
[Types.INT24]: 4,
|
|
11
|
+
[Types.LONG]: 4,
|
|
12
|
+
[Types.LONGLONG]: 8,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const TEMPORAL = [Types.DATE, Types.DATETIME, Types.TIMESTAMP];
|
|
16
|
+
|
|
17
|
+
// MySQL rejects INT24, YEAR, ENUM, SET, BIT and GEOMETRY as bind types, and
|
|
18
|
+
// MariaDB rejects JSON and VECTOR. Each is transmitted as the narrowest type
|
|
19
|
+
// both servers accept, which carries the same value.
|
|
20
|
+
const WIRE_TYPE = {
|
|
21
|
+
[Types.INT24]: Types.LONG,
|
|
22
|
+
[Types.YEAR]: Types.SHORT,
|
|
23
|
+
[Types.ENUM]: Types.STRING,
|
|
24
|
+
[Types.SET]: Types.STRING,
|
|
25
|
+
[Types.VECTOR]: Types.BLOB,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// Only these may be adopted from a server hint: they are valid bind types on
|
|
29
|
+
// every server, so upgrading can never make a working statement fail.
|
|
30
|
+
const HINT_UPGRADABLE = new Set([
|
|
31
|
+
Types.TINY,
|
|
32
|
+
Types.SHORT,
|
|
33
|
+
Types.LONG,
|
|
34
|
+
Types.LONGLONG,
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
const LENGTH_CODED = [
|
|
38
|
+
Types.DECIMAL,
|
|
39
|
+
Types.NEWDECIMAL,
|
|
40
|
+
Types.VARCHAR,
|
|
41
|
+
Types.VAR_STRING,
|
|
42
|
+
Types.STRING,
|
|
43
|
+
Types.ENUM,
|
|
44
|
+
Types.SET,
|
|
45
|
+
Types.JSON,
|
|
46
|
+
Types.VECTOR,
|
|
47
|
+
Types.TINY_BLOB,
|
|
48
|
+
Types.MEDIUM_BLOB,
|
|
49
|
+
Types.LONG_BLOB,
|
|
50
|
+
Types.BLOB,
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
function writeNothing() {}
|
|
54
|
+
|
|
55
|
+
const TIME_PATTERN = /^(-)?(\d+):([0-5]?\d):([0-5]?\d)(?:\.(\d{1,6}))?$/;
|
|
56
|
+
|
|
57
|
+
class TypedParameter {
|
|
58
|
+
constructor(type, value, unsigned) {
|
|
59
|
+
this.type = type;
|
|
60
|
+
this.value = value;
|
|
61
|
+
this.unsigned = unsigned;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
[Symbol.for('nodejs.util.inspect.custom')]() {
|
|
65
|
+
const name = Types[this.type] || `0x${this.type.toString(16)}`;
|
|
66
|
+
return `${name}${this.unsigned ? ' UNSIGNED' : ''}(${String(this.value)})`;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function toInteger(value, name) {
|
|
71
|
+
switch (typeof value) {
|
|
72
|
+
case 'bigint':
|
|
73
|
+
return value;
|
|
74
|
+
case 'boolean':
|
|
75
|
+
return value ? 1n : 0n;
|
|
76
|
+
case 'number':
|
|
77
|
+
if (!Number.isInteger(value)) {
|
|
78
|
+
throw new TypeError(
|
|
79
|
+
`${name} parameter must be an integer, got ${value}`
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
if (!Number.isSafeInteger(value)) {
|
|
83
|
+
throw new RangeError(
|
|
84
|
+
`${name} parameter ${value} exceeds Number.MAX_SAFE_INTEGER and has already lost precision; pass a string or BigInt instead`
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
return BigInt(value);
|
|
88
|
+
case 'string':
|
|
89
|
+
try {
|
|
90
|
+
return BigInt(value.trim());
|
|
91
|
+
} catch (cause) {
|
|
92
|
+
throw new TypeError(
|
|
93
|
+
`${name} parameter must be an integer, got ${JSON.stringify(value)}`,
|
|
94
|
+
{ cause }
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
default:
|
|
98
|
+
throw new TypeError(
|
|
99
|
+
`${name} parameter must be an integer, got ${typeof value}`
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function checkedInteger(value, type, bytes, unsigned) {
|
|
105
|
+
const name = Types[type];
|
|
106
|
+
const bits = BigInt(bytes * 8);
|
|
107
|
+
const n = toInteger(value, name);
|
|
108
|
+
const min = unsigned ? 0n : -(1n << (bits - 1n));
|
|
109
|
+
const max = unsigned ? (1n << bits) - 1n : (1n << (bits - 1n)) - 1n;
|
|
110
|
+
if (n < min || n > max) {
|
|
111
|
+
throw new RangeError(
|
|
112
|
+
`${name}${unsigned ? ' UNSIGNED' : ''} parameter out of range: ${n} is not within ${min}..${max}`
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
return n;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// The server reports an integer type whenever it knows a placeholder holds one,
|
|
119
|
+
// and MySQL rejects a DOUBLE in some of those positions. Adopting the hint is
|
|
120
|
+
// only safe when the value is already an integer that fits, so every other case
|
|
121
|
+
// returns null and keeps the type inferred from JavaScript.
|
|
122
|
+
function integerHint(value, type, unsigned) {
|
|
123
|
+
const bytes = INTEGER_BYTES[type];
|
|
124
|
+
if (!bytes || !HINT_UPGRADABLE.has(type)) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
let n;
|
|
128
|
+
if (typeof value === 'bigint') {
|
|
129
|
+
n = value;
|
|
130
|
+
} else if (typeof value === 'boolean') {
|
|
131
|
+
n = value ? 1n : 0n;
|
|
132
|
+
} else if (typeof value === 'number' && Number.isSafeInteger(value)) {
|
|
133
|
+
n = BigInt(value);
|
|
134
|
+
} else {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
const bits = BigInt(bytes * 8);
|
|
138
|
+
const min = unsigned ? 0n : -(1n << (bits - 1n));
|
|
139
|
+
const max = unsigned ? (1n << bits) - 1n : (1n << (bits - 1n)) - 1n;
|
|
140
|
+
if (n < min || n > max) {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
return new TypedParameter(type, n, unsigned);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function wireType(type, jsonAsString) {
|
|
147
|
+
if (type === Types.JSON && jsonAsString) {
|
|
148
|
+
return Types.VAR_STRING;
|
|
149
|
+
}
|
|
150
|
+
return WIRE_TYPE[type] || type;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function integerEncoder(type, bytes) {
|
|
154
|
+
return (value, unsigned) => {
|
|
155
|
+
const wire = BigInt.asUintN(
|
|
156
|
+
bytes * 8,
|
|
157
|
+
checkedInteger(value, type, bytes, unsigned)
|
|
158
|
+
);
|
|
159
|
+
return {
|
|
160
|
+
value: bytes === 8 ? wire : Number(wire),
|
|
161
|
+
length: bytes,
|
|
162
|
+
writer(v) {
|
|
163
|
+
this.writeUIntLE(v, bytes);
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function toDate(value, name) {
|
|
170
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
171
|
+
if (Number.isNaN(date.getTime())) {
|
|
172
|
+
throw new TypeError(
|
|
173
|
+
`${name} parameter must be a valid Date, got ${String(value)}`
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
return date;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function temporalEncoder(type, timezone) {
|
|
180
|
+
const name = Types[type];
|
|
181
|
+
return (value) => ({
|
|
182
|
+
value: toDate(value, name),
|
|
183
|
+
length: 12,
|
|
184
|
+
writer(v) {
|
|
185
|
+
this.writeDate(v, timezone);
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function toTimeParts(value) {
|
|
191
|
+
if (typeof value === 'number') {
|
|
192
|
+
const negative = value < 0;
|
|
193
|
+
let rest = Math.abs(value);
|
|
194
|
+
const microseconds = Math.round((rest % 1000) * 1000);
|
|
195
|
+
rest = Math.floor(rest / 1000);
|
|
196
|
+
const seconds = rest % 60;
|
|
197
|
+
const minutes = Math.floor(rest / 60) % 60;
|
|
198
|
+
const totalHours = Math.floor(rest / 3600);
|
|
199
|
+
return {
|
|
200
|
+
negative,
|
|
201
|
+
days: Math.floor(totalHours / 24),
|
|
202
|
+
hours: totalHours % 24,
|
|
203
|
+
minutes,
|
|
204
|
+
seconds,
|
|
205
|
+
microseconds,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
const match = TIME_PATTERN.exec(String(value));
|
|
209
|
+
if (!match) {
|
|
210
|
+
throw new TypeError(
|
|
211
|
+
`TIME parameter must be 'HH:MM:SS[.ffffff]' or milliseconds, got ${JSON.stringify(String(value))}`
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
const hours = Number(match[2]);
|
|
215
|
+
return {
|
|
216
|
+
negative: Boolean(match[1]),
|
|
217
|
+
days: Math.floor(hours / 24),
|
|
218
|
+
hours: hours % 24,
|
|
219
|
+
minutes: Number(match[3]),
|
|
220
|
+
seconds: Number(match[4]),
|
|
221
|
+
microseconds: match[5] ? Number(match[5].padEnd(6, '0')) : 0,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function timeEncoder() {
|
|
226
|
+
return (value) => ({
|
|
227
|
+
value: toTimeParts(value),
|
|
228
|
+
length: 13,
|
|
229
|
+
writer(v) {
|
|
230
|
+
this.writeTime(v);
|
|
231
|
+
},
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function lengthCodedEncoder(encoding) {
|
|
236
|
+
return (value) => {
|
|
237
|
+
if (Buffer.isBuffer(value)) {
|
|
238
|
+
return {
|
|
239
|
+
value,
|
|
240
|
+
length: Packet.lengthCodedNumberLength(value.length) + value.length,
|
|
241
|
+
writer: Packet.prototype.writeLengthCodedBuffer,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
const str = typeof value === 'string' ? value : String(value);
|
|
245
|
+
return {
|
|
246
|
+
value: str,
|
|
247
|
+
length: Packet.lengthCodedStringLength(str, encoding),
|
|
248
|
+
writer(v) {
|
|
249
|
+
Packet.prototype.writeLengthCodedString.call(this, v, encoding);
|
|
250
|
+
},
|
|
251
|
+
};
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function jsonEncoder(encoding) {
|
|
256
|
+
const encodeText = lengthCodedEncoder(encoding);
|
|
257
|
+
return (value) =>
|
|
258
|
+
encodeText(
|
|
259
|
+
typeof value === 'string' || Buffer.isBuffer(value)
|
|
260
|
+
? value
|
|
261
|
+
: JSON.stringify(value)
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function encoderFor(type, encoding, timezone) {
|
|
266
|
+
if (INTEGER_BYTES[type]) {
|
|
267
|
+
return integerEncoder(type, INTEGER_BYTES[type]);
|
|
268
|
+
}
|
|
269
|
+
if (type === Types.DOUBLE) {
|
|
270
|
+
return (value) => ({
|
|
271
|
+
value: Number(value),
|
|
272
|
+
length: 8,
|
|
273
|
+
writer: Packet.prototype.writeDouble,
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
if (type === Types.FLOAT) {
|
|
277
|
+
return (value) => ({
|
|
278
|
+
value: Number(value),
|
|
279
|
+
length: 4,
|
|
280
|
+
writer: Packet.prototype.writeFloat,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
if (TEMPORAL.includes(type)) {
|
|
284
|
+
return temporalEncoder(type, timezone);
|
|
285
|
+
}
|
|
286
|
+
if (type === Types.TIME) {
|
|
287
|
+
return timeEncoder();
|
|
288
|
+
}
|
|
289
|
+
if (type === Types.JSON) {
|
|
290
|
+
return jsonEncoder(encoding);
|
|
291
|
+
}
|
|
292
|
+
if (LENGTH_CODED.includes(type)) {
|
|
293
|
+
return lengthCodedEncoder(encoding);
|
|
294
|
+
}
|
|
295
|
+
throw new TypeError(
|
|
296
|
+
`No parameter encoder for MySQL type 0x${type.toString(16)}`
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function encodeTypedParameter(parameter, encoding, timezone, jsonAsString) {
|
|
301
|
+
if (parameter.value === null || parameter.type === Types.NULL) {
|
|
302
|
+
return {
|
|
303
|
+
value: '',
|
|
304
|
+
type: wireType(parameter.type, jsonAsString),
|
|
305
|
+
length: 0,
|
|
306
|
+
writer: writeNothing,
|
|
307
|
+
unsigned: parameter.unsigned,
|
|
308
|
+
isNull: true,
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
const encoded = encoderFor(
|
|
312
|
+
parameter.type,
|
|
313
|
+
encoding,
|
|
314
|
+
timezone
|
|
315
|
+
)(parameter.value, parameter.unsigned);
|
|
316
|
+
return {
|
|
317
|
+
...encoded,
|
|
318
|
+
type: wireType(parameter.type, jsonAsString),
|
|
319
|
+
unsigned: parameter.unsigned,
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const ALIASES = {
|
|
324
|
+
MEDIUMTEXT: 'MEDIUM_BLOB',
|
|
325
|
+
LONGTEXT: 'LONG_BLOB',
|
|
326
|
+
TINYINT: 'TINY',
|
|
327
|
+
SMALLINT: 'SHORT',
|
|
328
|
+
MEDIUMINT: 'INT24',
|
|
329
|
+
INT: 'LONG',
|
|
330
|
+
INTEGER: 'LONG',
|
|
331
|
+
BIGINT: 'LONGLONG',
|
|
332
|
+
REAL: 'DOUBLE',
|
|
333
|
+
CHAR: 'STRING',
|
|
334
|
+
VARBINARY: 'VAR_STRING',
|
|
335
|
+
BINARY: 'STRING',
|
|
336
|
+
TEXT: 'BLOB',
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
const SUPPORTED = [
|
|
340
|
+
...Object.keys(INTEGER_BYTES),
|
|
341
|
+
Types.FLOAT,
|
|
342
|
+
Types.DOUBLE,
|
|
343
|
+
Types.TIME,
|
|
344
|
+
...TEMPORAL,
|
|
345
|
+
...LENGTH_CODED,
|
|
346
|
+
].map(Number);
|
|
347
|
+
|
|
348
|
+
const types = Object.create(null);
|
|
349
|
+
|
|
350
|
+
for (const type of SUPPORTED) {
|
|
351
|
+
const name = Types[type];
|
|
352
|
+
const bytes = INTEGER_BYTES[type];
|
|
353
|
+
const build = bytes
|
|
354
|
+
? (value, unsigned) =>
|
|
355
|
+
new TypedParameter(
|
|
356
|
+
type,
|
|
357
|
+
value === null ? null : checkedInteger(value, type, bytes, unsigned),
|
|
358
|
+
unsigned
|
|
359
|
+
)
|
|
360
|
+
: (value, unsigned) => new TypedParameter(type, value, unsigned);
|
|
361
|
+
const factory = (value) => build(value, false);
|
|
362
|
+
if (bytes) {
|
|
363
|
+
factory.unsigned = (value) => build(value, true);
|
|
364
|
+
}
|
|
365
|
+
types[name] = factory;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
types.NULL = () => new TypedParameter(Types.NULL, null, false);
|
|
369
|
+
|
|
370
|
+
for (const [alias, target] of Object.entries(ALIASES)) {
|
|
371
|
+
if (types[target] && !types[alias]) {
|
|
372
|
+
types[alias] = types[target];
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
module.exports = {
|
|
377
|
+
TypedParameter,
|
|
378
|
+
encodeTypedParameter,
|
|
379
|
+
integerHint,
|
|
380
|
+
types,
|
|
381
|
+
};
|
package/package.json
CHANGED
package/promise.js
CHANGED
|
@@ -192,6 +192,11 @@ exports.PromisePoolConnection = PromisePoolConnection;
|
|
|
192
192
|
|
|
193
193
|
exports.__defineGetter__('Types', () => require('./lib/constants/types.js'));
|
|
194
194
|
|
|
195
|
+
exports.__defineGetter__(
|
|
196
|
+
'TypedParameter',
|
|
197
|
+
() => require('./lib/packets/typed_parameter.js').types
|
|
198
|
+
);
|
|
199
|
+
|
|
195
200
|
exports.__defineGetter__('Charsets', () =>
|
|
196
201
|
require('./lib/constants/charsets.js')
|
|
197
202
|
);
|
package/typings/mysql/index.d.ts
CHANGED
|
@@ -46,6 +46,7 @@ export * from './lib/protocol/packets/index.js';
|
|
|
46
46
|
export * from './lib/Auth.js';
|
|
47
47
|
export * from './lib/constants/index.js';
|
|
48
48
|
export * from './lib/parsers/index.js';
|
|
49
|
+
export * from './lib/TypedParameter.js';
|
|
49
50
|
export * from './lib/Connection.js';
|
|
50
51
|
export * from './lib/PoolConnection.js';
|
|
51
52
|
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A bind parameter carrying an explicit MySQL type.
|
|
3
|
+
*
|
|
4
|
+
* Produced by the `TypedParameter` factories and accepted anywhere
|
|
5
|
+
* `execute()` takes a value. The declared type is sent verbatim in
|
|
6
|
+
* `COM_STMT_EXECUTE` instead of being inferred from the JavaScript type.
|
|
7
|
+
*/
|
|
8
|
+
export interface TypedParameterValue {
|
|
9
|
+
readonly type: number;
|
|
10
|
+
readonly value: unknown;
|
|
11
|
+
readonly unsigned: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type TypedParameterInput =
|
|
15
|
+
string | number | bigint | boolean | Date | Buffer | Uint8Array | null;
|
|
16
|
+
|
|
17
|
+
export interface TypedParameterFactory {
|
|
18
|
+
(value: TypedParameterInput): TypedParameterValue;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface IntegerTypedParameterFactory extends TypedParameterFactory {
|
|
22
|
+
unsigned(value: TypedParameterInput): TypedParameterValue;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface JsonTypedParameterFactory {
|
|
26
|
+
(value: TypedParameterInput | object): TypedParameterValue;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface TypedParameter {
|
|
30
|
+
TINY: IntegerTypedParameterFactory;
|
|
31
|
+
SHORT: IntegerTypedParameterFactory;
|
|
32
|
+
YEAR: IntegerTypedParameterFactory;
|
|
33
|
+
INT24: IntegerTypedParameterFactory;
|
|
34
|
+
LONG: IntegerTypedParameterFactory;
|
|
35
|
+
LONGLONG: IntegerTypedParameterFactory;
|
|
36
|
+
|
|
37
|
+
TINYINT: IntegerTypedParameterFactory;
|
|
38
|
+
SMALLINT: IntegerTypedParameterFactory;
|
|
39
|
+
MEDIUMINT: IntegerTypedParameterFactory;
|
|
40
|
+
INT: IntegerTypedParameterFactory;
|
|
41
|
+
INTEGER: IntegerTypedParameterFactory;
|
|
42
|
+
BIGINT: IntegerTypedParameterFactory;
|
|
43
|
+
|
|
44
|
+
FLOAT: TypedParameterFactory;
|
|
45
|
+
DOUBLE: TypedParameterFactory;
|
|
46
|
+
REAL: TypedParameterFactory;
|
|
47
|
+
|
|
48
|
+
DATE: TypedParameterFactory;
|
|
49
|
+
DATETIME: TypedParameterFactory;
|
|
50
|
+
TIMESTAMP: TypedParameterFactory;
|
|
51
|
+
TIME: TypedParameterFactory;
|
|
52
|
+
|
|
53
|
+
DECIMAL: TypedParameterFactory;
|
|
54
|
+
NEWDECIMAL: TypedParameterFactory;
|
|
55
|
+
VARCHAR: TypedParameterFactory;
|
|
56
|
+
VAR_STRING: TypedParameterFactory;
|
|
57
|
+
STRING: TypedParameterFactory;
|
|
58
|
+
CHAR: TypedParameterFactory;
|
|
59
|
+
VARBINARY: TypedParameterFactory;
|
|
60
|
+
BINARY: TypedParameterFactory;
|
|
61
|
+
ENUM: TypedParameterFactory;
|
|
62
|
+
SET: TypedParameterFactory;
|
|
63
|
+
BIT: TypedParameterFactory;
|
|
64
|
+
GEOMETRY: TypedParameterFactory;
|
|
65
|
+
VECTOR: TypedParameterFactory;
|
|
66
|
+
TINY_BLOB: TypedParameterFactory;
|
|
67
|
+
MEDIUM_BLOB: TypedParameterFactory;
|
|
68
|
+
LONG_BLOB: TypedParameterFactory;
|
|
69
|
+
BLOB: TypedParameterFactory;
|
|
70
|
+
TEXT: TypedParameterFactory;
|
|
71
|
+
|
|
72
|
+
JSON: JsonTypedParameterFactory;
|
|
73
|
+
|
|
74
|
+
NULL(): TypedParameterValue;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Constant `TypedParameter`.
|
|
79
|
+
*
|
|
80
|
+
* Please note that `TypedParameter` can only be accessed from the `mysql` object and not imported directly.
|
|
81
|
+
*/
|
|
82
|
+
declare const TypedParameter: TypedParameter;
|
|
83
|
+
|
|
84
|
+
export { TypedParameter };
|
|
@@ -3,8 +3,10 @@ import { OkPacket, RowDataPacket, FieldPacket } from '../packets/index.js';
|
|
|
3
3
|
import { Readable } from 'stream';
|
|
4
4
|
import { Raw, Timezone } from 'sql-escaper';
|
|
5
5
|
import { TypeCast } from '../../parsers/typeCast.js';
|
|
6
|
+
import { TypedParameterValue } from '../../TypedParameter.js';
|
|
6
7
|
|
|
7
8
|
export type ExecuteValues =
|
|
9
|
+
| TypedParameterValue
|
|
8
10
|
| string
|
|
9
11
|
| number
|
|
10
12
|
| bigint
|