@m2k-5f/pgtx 2.7.3 → 2.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
- import { DataTypeOids, INT4Length } from "./constants";
1
+ import { INT4Length } from "./constants";
2
2
  import { PostgresError } from "../error";
3
- const columnValueCache = new Map();
3
+ import { handlers } from "./types";
4
4
  const POSTGRES_EPOCH_MS = 946684800000;
5
5
  export class ConnectionResponseBuffer {
6
6
  constructor(buffer) {
@@ -10,6 +10,25 @@ export class ConnectionResponseBuffer {
10
10
  static from(buffer) {
11
11
  return new ConnectionResponseBuffer(buffer);
12
12
  }
13
+ hasMore() {
14
+ return this.caret < this.buffer.length;
15
+ }
16
+ skipBytes(byteCount) {
17
+ this.caret += byteCount;
18
+ }
19
+ hasFullPacket() {
20
+ const availableBytes = this.buffer.length - this.caret;
21
+ if (availableBytes < 5)
22
+ return false;
23
+ const packetLength = this.buffer.readInt32BE(this.caret + 1);
24
+ if (availableBytes < 1 + packetLength)
25
+ return false;
26
+ return true;
27
+ }
28
+ getResidualBuffer() {
29
+ return this.buffer.subarray(this.caret);
30
+ }
31
+ // ------ primitives ------
13
32
  readChar() {
14
33
  const char = String.fromCharCode(this.buffer[this.caret]);
15
34
  this.caret += 1;
@@ -36,109 +55,16 @@ export class ConnectionResponseBuffer {
36
55
  this.caret += length;
37
56
  return text;
38
57
  }
39
- hasMore() {
40
- return this.caret < this.buffer.length;
41
- }
42
- readBinaryDate() {
43
- const daysSince2000 = this.readInt32();
44
- const ms = POSTGRES_EPOCH_MS + (daysSince2000 * 86400000);
45
- return new Date(ms);
46
- }
47
- readByte() {
48
- return this.buffer[this.caret++];
49
- }
50
- readBinaryTimestamp() {
51
- const microSecondsSince2000 = this.readInt64();
52
- const msSince2000 = Number(microSecondsSince2000 / 1000);
53
- return new Date(POSTGRES_EPOCH_MS + msSince2000);
54
- }
55
- readBinaryTime() {
56
- const microSecondsSinceMidnight = this.readInt64();
57
- const totalMs = Number(microSecondsSinceMidnight / 1000);
58
- const seconds = Math.floor(totalMs / 1000);
59
- const ms = totalMs % 1000;
60
- const minutes = Math.floor(seconds / 60);
61
- const hours = Math.floor(minutes / 60);
62
- return `${String(hours).padStart(2, '0')}:${String(minutes % 60).padStart(2, '0')}:${String(seconds % 60).padStart(2, '0')}.${String(ms).padStart(3, '0')}`;
63
- }
64
- skipBytes(byteCount) {
65
- this.caret += byteCount;
66
- }
67
- hasFullPacket() {
68
- const availableBytes = this.buffer.length - this.caret;
69
- if (availableBytes < 5)
70
- return false;
71
- const packetLength = this.buffer.readInt32BE(this.caret + 1);
72
- if (availableBytes < 1 + packetLength)
73
- return false;
74
- return true;
75
- }
76
- getResidualBuffer() {
77
- return this.buffer.subarray(this.caret);
78
- }
79
- readBool() {
80
- const value = this.buffer[this.caret] !== 0;
81
- this.caret++;
58
+ readUInt16() {
59
+ const value = this.buffer.readUInt16BE(this.caret);
60
+ this.caret += 2;
82
61
  return value;
83
62
  }
84
- readBigInt64() {
85
- const value = this.buffer.readBigInt64BE(this.caret);
86
- this.caret += 8;
63
+ readUInt32() {
64
+ const value = this.buffer.readUInt32BE(this.caret);
65
+ this.caret += 4;
87
66
  return value;
88
67
  }
89
- readInt64() {
90
- const hi = this.buffer.readInt32BE(this.caret);
91
- const lo = this.buffer.readUInt32BE(this.caret + 4);
92
- this.caret += 8;
93
- return (hi * 4294967296) + lo;
94
- }
95
- readNumeric() {
96
- const ndigits = this.readInt16();
97
- const weight = this.readInt16();
98
- const sign = this.readInt16();
99
- const dscale = this.readInt16();
100
- if (sign === 0xC000 || sign === -16384)
101
- return 'NaN';
102
- const digits = new Array(ndigits);
103
- for (let i = 0; i < ndigits; i++) {
104
- digits[i] = this.readInt16();
105
- }
106
- const highExp = Math.max(weight, 0);
107
- const lowExp = Math.min(weight - ndigits + 1, -1);
108
- let intPart = '';
109
- for (let exp = highExp; exp >= 0; exp--) {
110
- const idx = weight - exp;
111
- const digit = (idx >= 0 && idx < ndigits) ? digits[idx] : 0;
112
- intPart += exp === highExp ? String(digit) : String(digit).padStart(4, '0');
113
- }
114
- if (intPart === '')
115
- intPart = '0';
116
- let fracPart = '';
117
- for (let exp = -1; exp >= lowExp; exp--) {
118
- const idx = weight - exp;
119
- const digit = (idx >= 0 && idx < ndigits) ? digits[idx] : 0;
120
- fracPart += String(digit).padStart(4, '0');
121
- }
122
- if (dscale > 0) {
123
- fracPart = fracPart.padEnd(dscale, '0').slice(0, dscale);
124
- }
125
- else {
126
- fracPart = '';
127
- }
128
- const negative = sign === 0x4000 || sign === 16384;
129
- const result = fracPart ? `${intPart}.${fracPart}` : intPart;
130
- return negative ? `-${result}` : result;
131
- }
132
- readBinaryTimetz() {
133
- const time = this.readBinaryTime();
134
- const zoneOffsetSeconds = this.readInt32();
135
- const offsetMinutesTotal = -zoneOffsetSeconds / 60;
136
- const sign = offsetMinutesTotal >= 0 ? '+' : '-';
137
- const abs = Math.abs(offsetMinutesTotal);
138
- const offH = Math.floor(abs / 60);
139
- const offM = abs % 60;
140
- return `${time}${sign}${String(offH).padStart(2, '0')}:${String(offM).padStart(2, '0')}`;
141
- }
142
68
  readFloat32() {
143
69
  const value = this.buffer.readFloatBE(this.caret);
144
70
  this.caret += 4;
@@ -149,13 +75,8 @@ export class ConnectionResponseBuffer {
149
75
  this.caret += 8;
150
76
  return value;
151
77
  }
152
- readUuid() {
153
- const buf = this.readBytes(16);
154
- return buf.toString('hex', 0, 4) + '-' +
155
- buf.toString('hex', 4, 6) + '-' +
156
- buf.toString('hex', 6, 8) + '-' +
157
- buf.toString('hex', 8, 10) + '-' +
158
- buf.toString('hex', 10, 16);
78
+ readByte() {
79
+ return this.buffer[this.caret++];
159
80
  }
160
81
  readBytes(length) {
161
82
  const result = Buffer.allocUnsafe(length);
@@ -163,15 +84,23 @@ export class ConnectionResponseBuffer {
163
84
  this.caret += length;
164
85
  return result;
165
86
  }
166
- getBufferHash(length) {
167
- let hash = 2166136261;
168
- const end = this.caret + length;
169
- for (let i = this.caret; i < end; i++) {
170
- hash ^= this.buffer[i];
171
- hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
172
- }
173
- return hash >>> 0;
87
+ readBool() {
88
+ const value = this.buffer[this.caret] !== 0;
89
+ this.caret++;
90
+ return value;
174
91
  }
92
+ readBigInt64() {
93
+ const value = this.buffer.readBigInt64BE(this.caret);
94
+ this.caret += 8;
95
+ return value;
96
+ }
97
+ readInt64() {
98
+ const hi = this.buffer.readInt32BE(this.caret);
99
+ const lo = this.buffer.readUInt32BE(this.caret + 4);
100
+ this.caret += 8;
101
+ return (hi * 4294967296) + lo;
102
+ }
103
+ // ------ commands ------
175
104
  readType() {
176
105
  return { type: this.readByte(), length: this.readInt32() - INT4Length };
177
106
  }
@@ -272,136 +201,6 @@ export class ConnectionResponseBuffer {
272
201
  }
273
202
  return columns;
274
203
  }
275
- readDataRow(descriptions, int8toBigint) {
276
- const fieldsCount = this.readInt16();
277
- const row = {};
278
- for (let i = 0; i < fieldsCount; i++) {
279
- const fieldLength = this.readInt32();
280
- const desc = descriptions[i];
281
- const key = desc.name;
282
- if (fieldLength === -1) {
283
- row[key] = null;
284
- continue;
285
- }
286
- const oid = desc.typeOID;
287
- switch (oid) {
288
- case DataTypeOids.Text:
289
- case DataTypeOids.Varchar:
290
- case DataTypeOids.Char:
291
- case DataTypeOids.Bpchar:
292
- {
293
- if (fieldLength <= 32) {
294
- let cacheForColumn = columnValueCache.get(i);
295
- if (!cacheForColumn) {
296
- cacheForColumn = new Map();
297
- columnValueCache.set(i, cacheForColumn);
298
- }
299
- const byteHash = this.getBufferHash(fieldLength);
300
- let cachedString = cacheForColumn.get(byteHash);
301
- if (cachedString === undefined) {
302
- cachedString = this.readRawString(fieldLength);
303
- if (cacheForColumn.size > 512) {
304
- cacheForColumn.clear();
305
- }
306
- cacheForColumn.set(byteHash, cachedString);
307
- }
308
- else {
309
- this.skipBytes(fieldLength);
310
- }
311
- row[key] = cachedString;
312
- }
313
- else {
314
- row[key] = this.readRawString(fieldLength);
315
- }
316
- }
317
- break;
318
- case DataTypeOids.Int2:
319
- {
320
- row[key] = this.readInt16();
321
- }
322
- break;
323
- case DataTypeOids.Int4:
324
- {
325
- row[key] = this.readInt32();
326
- }
327
- break;
328
- case DataTypeOids.Int8:
329
- {
330
- row[key] = int8toBigint ? this.readBigInt64() : this.readInt64();
331
- }
332
- break;
333
- case DataTypeOids.Float4:
334
- {
335
- row[key] = this.readFloat32();
336
- }
337
- break;
338
- case DataTypeOids.Float8:
339
- {
340
- row[key] = this.readFloat64();
341
- }
342
- break;
343
- case DataTypeOids.Bool:
344
- {
345
- row[key] = this.readBool();
346
- }
347
- break;
348
- case DataTypeOids.Bytea:
349
- {
350
- row[key] = this.readBytes(fieldLength);
351
- }
352
- break;
353
- case DataTypeOids.Jsonb:
354
- {
355
- this.skipBytes(1);
356
- row[key] = JSON.parse(this.readRawString(fieldLength - 1));
357
- }
358
- break;
359
- case DataTypeOids.Json:
360
- {
361
- row[key] = JSON.parse(this.readRawString(fieldLength));
362
- }
363
- break;
364
- case DataTypeOids.Date:
365
- {
366
- row[key] = this.readBinaryDate();
367
- }
368
- break;
369
- case DataTypeOids.Timestamp:
370
- case DataTypeOids.Timestamptz:
371
- {
372
- row[key] = this.readBinaryTimestamp();
373
- }
374
- break;
375
- case DataTypeOids.Uuid:
376
- {
377
- row[key] = this.readUuid();
378
- }
379
- break;
380
- case DataTypeOids.Time:
381
- {
382
- row[key] = this.readBinaryTime();
383
- }
384
- break;
385
- case DataTypeOids.Timetz:
386
- {
387
- row[key] = this.readBinaryTimetz();
388
- }
389
- break;
390
- case DataTypeOids.Numeric:
391
- {
392
- row[key] = this.readNumeric();
393
- }
394
- break;
395
- default:
396
- {
397
- this.skipBytes(fieldLength);
398
- row[key] = null;
399
- }
400
- break;
401
- }
402
- }
403
- return row;
404
- }
405
204
  readNotificationResponse() {
406
205
  this.readInt32();
407
206
  const name = this.readCString();
@@ -419,4 +218,223 @@ export class ConnectionResponseBuffer {
419
218
  }
420
219
  return description;
421
220
  }
221
+ // ------ binary types parsers ------
222
+ readBinaryDate() {
223
+ const daysSince2000 = this.readInt32();
224
+ const ms = POSTGRES_EPOCH_MS + (daysSince2000 * 86400000);
225
+ return new Date(ms);
226
+ }
227
+ readBinaryTimestamp() {
228
+ const microSecondsSince2000 = this.readInt64();
229
+ const msSince2000 = Number(microSecondsSince2000 / 1000);
230
+ return new Date(POSTGRES_EPOCH_MS + msSince2000);
231
+ }
232
+ readBinaryTime() {
233
+ const microSecondsSinceMidnight = this.readInt64();
234
+ const totalMs = Number(microSecondsSinceMidnight / 1000);
235
+ const seconds = Math.floor(totalMs / 1000);
236
+ const ms = totalMs % 1000;
237
+ const minutes = Math.floor(seconds / 60);
238
+ const hours = Math.floor(minutes / 60);
239
+ return `${String(hours).padStart(2, '0')}:${String(minutes % 60).padStart(2, '0')}:${String(seconds % 60).padStart(2, '0')}.${String(ms).padStart(3, '0')}`;
240
+ }
241
+ readBinaryTimetz() {
242
+ const time = this.readBinaryTime();
243
+ const zoneOffsetSeconds = this.readInt32();
244
+ const offsetMinutesTotal = -zoneOffsetSeconds / 60;
245
+ const sign = offsetMinutesTotal >= 0 ? '+' : '-';
246
+ const abs = Math.abs(offsetMinutesTotal);
247
+ const offH = Math.floor(abs / 60);
248
+ const offM = abs % 60;
249
+ return `${time}${sign}${String(offH).padStart(2, '0')}:${String(offM).padStart(2, '0')}`;
250
+ }
251
+ readBinaryNumeric() {
252
+ const ndigits = this.readInt16();
253
+ const weight = this.readInt16();
254
+ const sign = this.readInt16();
255
+ const dscale = this.readInt16();
256
+ if (sign === 0xC000 || sign === -16384)
257
+ return 'NaN';
258
+ const digits = new Array(ndigits);
259
+ for (let i = 0; i < ndigits; i++) {
260
+ digits[i] = this.readInt16();
261
+ }
262
+ const highExp = Math.max(weight, 0);
263
+ const lowExp = Math.min(weight - ndigits + 1, -1);
264
+ let intPart = '';
265
+ for (let exp = highExp; exp >= 0; exp--) {
266
+ const idx = weight - exp;
267
+ const digit = (idx >= 0 && idx < ndigits) ? digits[idx] : 0;
268
+ intPart += exp === highExp ? String(digit) : String(digit).padStart(4, '0');
269
+ }
270
+ if (intPart === '')
271
+ intPart = '0';
272
+ let fracPart = '';
273
+ for (let exp = -1; exp >= lowExp; exp--) {
274
+ const idx = weight - exp;
275
+ const digit = (idx >= 0 && idx < ndigits) ? digits[idx] : 0;
276
+ fracPart += String(digit).padStart(4, '0');
277
+ }
278
+ if (dscale > 0) {
279
+ fracPart = fracPart.padEnd(dscale, '0').slice(0, dscale);
280
+ }
281
+ else {
282
+ fracPart = '';
283
+ }
284
+ const negative = sign === 0x4000 || sign === 16384;
285
+ const result = fracPart ? `${intPart}.${fracPart}` : intPart;
286
+ return negative ? `-${result}` : result;
287
+ }
288
+ readBinaryInterval() {
289
+ const microseconds = this.readBigInt64();
290
+ const days = this.readInt32();
291
+ const months = this.readInt32();
292
+ return { months, days, microseconds };
293
+ }
294
+ readBinaryJson(fieldLength) {
295
+ return JSON.parse(this.readRawString(fieldLength));
296
+ }
297
+ readBinaryJsonb(fieldLength) {
298
+ this.skipBytes(1);
299
+ return JSON.parse(this.readRawString(fieldLength - 1));
300
+ }
301
+ readBinaryMacaddr() {
302
+ const bytes = this.readBytes(6);
303
+ return Array.from(bytes)
304
+ .map(b => b.toString(16).padStart(2, '0'))
305
+ .join(':');
306
+ }
307
+ readBinaryOid() {
308
+ return this.readUInt32();
309
+ }
310
+ readBinaryXid() {
311
+ return this.readUInt32();
312
+ }
313
+ readBinaryCid() {
314
+ return this.readUInt32();
315
+ }
316
+ readBinaryRegproc() {
317
+ return this.readUInt32();
318
+ }
319
+ readBinaryLseg() {
320
+ return {
321
+ a: this.readBinaryPoint(),
322
+ b: this.readBinaryPoint(),
323
+ };
324
+ }
325
+ readBinaryPath() {
326
+ const closed = this.readByte() !== 0;
327
+ const npoints = this.readInt32();
328
+ const points = new Array(npoints);
329
+ for (let i = 0; i < npoints; i++) {
330
+ points[i] = this.readBinaryPoint();
331
+ }
332
+ return { closed, points };
333
+ }
334
+ readBinaryBox() {
335
+ return {
336
+ high: this.readBinaryPoint(),
337
+ low: this.readBinaryPoint(),
338
+ };
339
+ }
340
+ readBinaryPolygon() {
341
+ const npoints = this.readInt32();
342
+ const points = new Array(npoints);
343
+ for (let i = 0; i < npoints; i++) {
344
+ points[i] = this.readBinaryPoint();
345
+ }
346
+ return { points };
347
+ }
348
+ readBinaryLine() {
349
+ return {
350
+ a: this.readFloat64(),
351
+ b: this.readFloat64(),
352
+ c: this.readFloat64(),
353
+ };
354
+ }
355
+ readBinaryUuid() {
356
+ const buf = this.readBytes(16);
357
+ return buf.toString('hex', 0, 4) + '-' +
358
+ buf.toString('hex', 4, 6) + '-' +
359
+ buf.toString('hex', 6, 8) + '-' +
360
+ buf.toString('hex', 8, 10) + '-' +
361
+ buf.toString('hex', 10, 16);
362
+ }
363
+ readBinaryArray(fieldLength, elementParser) {
364
+ if (fieldLength === 0)
365
+ return [];
366
+ const ndims = this.readInt32();
367
+ const hasNull = this.readInt32();
368
+ const elementOid = this.readInt32();
369
+ if (ndims === 0)
370
+ return [];
371
+ if (ndims > 1) {
372
+ for (let d = 1; d < ndims; d++) {
373
+ this.readInt32();
374
+ this.readInt32();
375
+ }
376
+ }
377
+ const dimLength = this.readInt32();
378
+ const lbound = this.readInt32();
379
+ const result = new Array(dimLength);
380
+ for (let i = 0; i < dimLength; i++) {
381
+ const itemLength = this.readInt32();
382
+ if (itemLength === -1) {
383
+ result[i] = null;
384
+ }
385
+ else {
386
+ result[i] = elementParser(itemLength);
387
+ }
388
+ }
389
+ return result;
390
+ }
391
+ readBinaryInet(fieldLength) {
392
+ const family = this.readByte();
393
+ const bits = this.readByte();
394
+ const isCidr = this.readByte();
395
+ const nb = this.readByte();
396
+ if (nb === 4) {
397
+ const b1 = this.readByte();
398
+ const b2 = this.readByte();
399
+ const b3 = this.readByte();
400
+ const b4 = this.readByte();
401
+ return `${b1}.${b2}.${b3}.${b4}`;
402
+ }
403
+ else if (nb === 16) {
404
+ const parts = [];
405
+ for (let i = 0; i < 8; i++) {
406
+ parts.push(this.readUInt16().toString(16));
407
+ }
408
+ return parts.join(':');
409
+ }
410
+ this.skipBytes(fieldLength - 4);
411
+ return '';
412
+ }
413
+ readBinaryPoint() {
414
+ return {
415
+ x: this.readFloat64(),
416
+ y: this.readFloat64()
417
+ };
418
+ }
419
+ readDataRow(descriptions, int8toBigint) {
420
+ const fieldsCount = this.readInt16();
421
+ const row = {};
422
+ for (let i = 0; i < fieldsCount; i++) {
423
+ const fieldLength = this.readInt32();
424
+ const desc = descriptions[i];
425
+ const key = desc.name;
426
+ if (fieldLength === -1) {
427
+ row[key] = null;
428
+ continue;
429
+ }
430
+ const handler = handlers[desc.typeOID];
431
+ if (!handler) {
432
+ this.skipBytes(fieldLength);
433
+ row[key] = undefined;
434
+ continue;
435
+ }
436
+ row[key] = handler.read(this, fieldLength, int8toBigint);
437
+ }
438
+ return row;
439
+ }
422
440
  }
@@ -29,16 +29,16 @@ export const ResponseTypes = {
29
29
  SSLDenied: 78 // "N"
30
30
  };
31
31
  export const DataTypeOids = {
32
- Bool: 16,
32
+ Bool: 16, // parameter
33
33
  Bytea: 17,
34
- Char: 18,
34
+ Char: 18, // parameter
35
35
  Name: 19,
36
- Text: 25,
37
- Varchar: 1043,
38
- Bpchar: 1042,
39
- Int2: 21,
40
- Int4: 23,
41
- Int8: 20,
36
+ Text: 25, // parameter
37
+ Varchar: 1043, // parameter
38
+ Bpchar: 1042, // parameter
39
+ Int2: 21, // parameter
40
+ Int4: 23, // parameter
41
+ Int8: 20, // parameter
42
42
  Float4: 700,
43
43
  Float8: 701,
44
44
  Numeric: 1700,
@@ -0,0 +1,14 @@
1
+ import { ConnectionRequestBuffer } from "./connection-request-writer";
2
+ import { ConnectionResponseBuffer } from "./connection-response-reader";
3
+ import { DataTypeOid } from "./constants";
4
+ type BindValidator = (value: unknown) => boolean;
5
+ type BindWriter = (request: ConnectionRequestBuffer, value: any) => void;
6
+ type FieldReader = (response: ConnectionResponseBuffer, fieldLength: number, int8toBigint: boolean) => unknown;
7
+ interface BindHandler {
8
+ validate: BindValidator;
9
+ write: BindWriter;
10
+ read: FieldReader;
11
+ }
12
+ export declare const handlers: Partial<Record<DataTypeOid, BindHandler>>;
13
+ export {};
14
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/protocol/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAA;AACrE,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAA;AACvE,OAAO,EAAE,WAAW,EAAgB,MAAM,aAAa,CAAA;AAEvD,KAAK,aAAa,GAAG,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAA;AAChD,KAAK,UAAU,GAAG,CAAC,OAAO,EAAE,uBAAuB,EAAE,KAAK,EAAE,GAAG,KAAK,IAAI,CAAA;AACxE,KAAK,WAAW,GAAG,CACf,QAAQ,EAAE,wBAAwB,EAClC,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,OAAO,KACpB,OAAO,CAAA;AAEZ,UAAU,WAAW;IACjB,QAAQ,EAAE,aAAa,CAAA;IACvB,KAAK,EAAE,UAAU,CAAA;IACjB,IAAI,EAAE,WAAW,CAAA;CACpB;AASD,eAAO,MAAM,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,WAAW,CAAC,CAsY9D,CAAA"}