@steadybrew/postgresjs 4.4.0 → 4.5.0-rc1
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 +234 -18
- package/cf/src/connection.js +42 -14
- package/cf/src/index.js +45 -9
- package/cf/src/query.js +3 -10
- package/cf/src/subscribe.js +5 -0
- package/cf/src/types.js +5 -0
- package/cjs/src/connection.js +42 -14
- package/cjs/src/index.js +45 -9
- package/cjs/src/query.js +3 -10
- package/cjs/src/subscribe.js +5 -0
- package/cjs/src/types.js +5 -0
- package/package.json +3 -2
- package/src/connection.js +42 -14
- package/src/index.js +45 -9
- package/src/query.js +3 -10
- package/src/subscribe.js +5 -0
- package/src/types.js +5 -0
- package/types/index.d.ts +30 -0
- package/types/package.json +1 -1
package/cf/src/subscribe.js
CHANGED
|
@@ -4,6 +4,7 @@ import { defaultBackoff, backoffSeconds } from './connection.js'
|
|
|
4
4
|
|
|
5
5
|
const noop = () => { /* noop */ }
|
|
6
6
|
, commands = { 68: 'delete', 73: 'insert', 85: 'update' }
|
|
7
|
+
, statusInterval = 1000
|
|
7
8
|
|
|
8
9
|
export default function Subscribe(postgres, options) {
|
|
9
10
|
const subscribers = new Map()
|
|
@@ -152,6 +153,10 @@ export default function Subscribe(postgres, options) {
|
|
|
152
153
|
stream.on('error', reportConnection)
|
|
153
154
|
stream.on('close', sql.close)
|
|
154
155
|
|
|
156
|
+
// A walsender can miss the wakeup for a commit and sleep until its timeout; a status update wakes it.
|
|
157
|
+
const status = setInterval(() => stream.writable && pong(), statusInterval)
|
|
158
|
+
stream.on('close', () => clearInterval(status))
|
|
159
|
+
|
|
155
160
|
return { stream, state: xs.state }
|
|
156
161
|
|
|
157
162
|
function data(x) {
|
package/cf/src/types.js
CHANGED
|
@@ -249,6 +249,11 @@ export const inferType = function inferType(x) {
|
|
|
249
249
|
)
|
|
250
250
|
}
|
|
251
251
|
|
|
252
|
+
export function clientSerialize(x) {
|
|
253
|
+
const proto = typeof x === 'object' && Object.getPrototypeOf(x)
|
|
254
|
+
return proto === Object.prototype || proto === null ? JSON.stringify(x) : '' + x
|
|
255
|
+
}
|
|
256
|
+
|
|
252
257
|
const escapeBackslash = /\\/g
|
|
253
258
|
const escapeQuote = /"/g
|
|
254
259
|
|
package/cjs/src/connection.js
CHANGED
|
@@ -4,7 +4,7 @@ const crypto = require('crypto')
|
|
|
4
4
|
const Stream = require('stream')
|
|
5
5
|
const { performance } = require('perf_hooks')
|
|
6
6
|
|
|
7
|
-
const { stringify, handleValue, addArrayType } = require('./types.js')
|
|
7
|
+
const { stringify, handleValue, addArrayType, clientSerialize } = require('./types.js')
|
|
8
8
|
const { Errors, callback } = require('./errors.js')
|
|
9
9
|
const Result = require('./result.js')
|
|
10
10
|
const Queue = require('./queue.js')
|
|
@@ -214,7 +214,10 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
|
|
|
214
214
|
const connection = {
|
|
215
215
|
queue: queues.closed,
|
|
216
216
|
idleTimer,
|
|
217
|
-
connect
|
|
217
|
+
// Sentry's hook wraps connect and reads the connection from this, so it must stay a method
|
|
218
|
+
connect(request) {
|
|
219
|
+
dispatch('acquire', request)
|
|
220
|
+
},
|
|
218
221
|
terminate: () => dispatch('terminate'),
|
|
219
222
|
execute,
|
|
220
223
|
release: () => dispatch('release'),
|
|
@@ -229,6 +232,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
|
|
|
229
232
|
get pipelined() { return sent.length },
|
|
230
233
|
get accepting() { return accepting() },
|
|
231
234
|
get copying() { return stream !== null },
|
|
235
|
+
get copyHeld() { return stream !== null && !stream.writableEnded },
|
|
232
236
|
get acquiring() { return acquisition && !acquisition.owner.cancelled ? 1 : 0 },
|
|
233
237
|
count: 0,
|
|
234
238
|
id
|
|
@@ -537,7 +541,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
|
|
|
537
541
|
|
|
538
542
|
build(q)
|
|
539
543
|
const written = write(toBuffer(q))
|
|
540
|
-
if (q.describeFirst || q.cursorFn || mayCopy(q))
|
|
544
|
+
if (q.describeFirst || q.cursorFn || mayCopy(q) || (q.prepare && !q.prepared && !q.options.simple && q.parameters.length))
|
|
541
545
|
barrier = q
|
|
542
546
|
return written && accepting()
|
|
543
547
|
} catch (error) {
|
|
@@ -575,7 +579,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
|
|
|
575
579
|
|
|
576
580
|
function prepared(q) {
|
|
577
581
|
return Buffer.concat([
|
|
578
|
-
Bind(q
|
|
582
|
+
Bind(q),
|
|
579
583
|
q.cursorFn
|
|
580
584
|
? Execute('', q.cursorRows)
|
|
581
585
|
: ExecuteUnnamed
|
|
@@ -603,8 +607,11 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
|
|
|
603
607
|
q.signature = q.prepare && types + string
|
|
604
608
|
q.onlyDescribe && (delete statements[q.signature])
|
|
605
609
|
q.parameters = q.parameters || parameters
|
|
610
|
+
q.inferred = options.parameter_types === 'server' ? null : types.slice()
|
|
606
611
|
q.prepared = q.prepare && q.signature in statements
|
|
607
|
-
q.describeFirst = q.onlyDescribe || (
|
|
612
|
+
q.describeFirst = q.onlyDescribe || (!q.prepared && types.some((x, i) =>
|
|
613
|
+
!x && (options.parameter_types !== 'client' || Array.isArray(q.parameters[i]))
|
|
614
|
+
))
|
|
608
615
|
q.statement = q.prepared
|
|
609
616
|
? statements[q.signature]
|
|
610
617
|
: { string, types, name: q.prepare ? statementId + statementCount++ : '' }
|
|
@@ -688,7 +695,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
|
|
|
688
695
|
err = new Error(err)
|
|
689
696
|
|
|
690
697
|
'query' in err || 'parameters' in err || Object.defineProperties(err, {
|
|
691
|
-
stack: { value: err.stack + query.origin.replace(/.*\n/, '\n'), enumerable: options.debug },
|
|
698
|
+
stack: { value: err.stack + query.origin.replace(/.*\n/, '\n'), enumerable: options.debug, writable: true, configurable: true },
|
|
692
699
|
query: { value: query.string, enumerable: options.debug },
|
|
693
700
|
parameters: { value: query.parameters, enumerable: options.debug },
|
|
694
701
|
args: { value: query.args, enumerable: options.debug },
|
|
@@ -1212,20 +1219,21 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
|
|
|
1212
1219
|
}
|
|
1213
1220
|
|
|
1214
1221
|
/* Messages */
|
|
1215
|
-
function Bind(
|
|
1222
|
+
function Bind(q) {
|
|
1223
|
+
const differences = []
|
|
1216
1224
|
let prev
|
|
1217
1225
|
, type
|
|
1218
1226
|
|
|
1219
|
-
b().B().str(
|
|
1227
|
+
b().B().str((q.cursorName || '') + b.N).str(q.statement.name + b.N).i16(0).i16(q.parameters.length)
|
|
1220
1228
|
|
|
1221
|
-
parameters.forEach((x, i) => {
|
|
1229
|
+
q.parameters.forEach((x, i) => {
|
|
1222
1230
|
if (x === null)
|
|
1223
1231
|
return b.i32(0xFFFFFFFF)
|
|
1224
1232
|
|
|
1225
|
-
type = types[i]
|
|
1226
|
-
x =
|
|
1227
|
-
?
|
|
1228
|
-
: ''
|
|
1233
|
+
type = q.statement.types[i]
|
|
1234
|
+
x = !q.inferred || q.inferred[i] || Array.isArray(x)
|
|
1235
|
+
? serialize(x, type)
|
|
1236
|
+
: options.parameter_types === 'client' ? clientSerialize(x) : compare(x, type, i, differences)
|
|
1229
1237
|
|
|
1230
1238
|
prev = b.i
|
|
1231
1239
|
b.inc(4).str(x).i32(b.i - prev - 4, prev)
|
|
@@ -1233,7 +1241,27 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
|
|
|
1233
1241
|
|
|
1234
1242
|
b.i16(0)
|
|
1235
1243
|
|
|
1236
|
-
|
|
1244
|
+
const message = b.end()
|
|
1245
|
+
differences.length && callback(options.ontypemismatch || console.warn, { query: q.string, parameters: differences }) // eslint-disable-line
|
|
1246
|
+
return message
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
function serialize(x, type) {
|
|
1250
|
+
return type in options.serializers
|
|
1251
|
+
? options.serializers[type](x)
|
|
1252
|
+
: '' + x
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
function compare(x, type, i, differences) {
|
|
1256
|
+
const serverText = serialize(x, type)
|
|
1257
|
+
let clientText
|
|
1258
|
+
try {
|
|
1259
|
+
clientText = clientSerialize(x)
|
|
1260
|
+
} catch (error) {
|
|
1261
|
+
clientText = error
|
|
1262
|
+
}
|
|
1263
|
+
serverText === clientText || differences.push({ position: i + 1, oid: type, typeof: typeof x, ...options.debug && { serverText, clientText } })
|
|
1264
|
+
return serverText
|
|
1237
1265
|
}
|
|
1238
1266
|
|
|
1239
1267
|
function Parse(str, parameters, types, name = '') {
|
package/cjs/src/index.js
CHANGED
|
@@ -86,7 +86,8 @@ function Postgres(a, b) {
|
|
|
86
86
|
listen,
|
|
87
87
|
begin,
|
|
88
88
|
close,
|
|
89
|
-
end
|
|
89
|
+
end,
|
|
90
|
+
...Symbol.asyncDispose && { [Symbol.asyncDispose]: () => end() }
|
|
90
91
|
})
|
|
91
92
|
|
|
92
93
|
return sql
|
|
@@ -273,7 +274,9 @@ function Postgres(a, b) {
|
|
|
273
274
|
const query = { reserve: c => resolve(own(c, l)), reject }
|
|
274
275
|
closed.length
|
|
275
276
|
? connect(closed.shift(), query)
|
|
276
|
-
:
|
|
277
|
+
: allHeldByCopy()
|
|
278
|
+
? reject(copyInProgress())
|
|
279
|
+
: queries.push(query)
|
|
277
280
|
})
|
|
278
281
|
return l
|
|
279
282
|
}
|
|
@@ -325,10 +328,17 @@ function Postgres(a, b) {
|
|
|
325
328
|
|
|
326
329
|
async function reserve() {
|
|
327
330
|
const l = await lease(false)
|
|
328
|
-
const sql = Sql(
|
|
331
|
+
const sql = Sql(handler)
|
|
329
332
|
sql.release = () => settle(l, Lease.Released)
|
|
333
|
+
Symbol.dispose && (sql[Symbol.dispose] = () => sql.release())
|
|
334
|
+
Symbol.asyncDispose && (sql[Symbol.asyncDispose] = async() => sql.release())
|
|
330
335
|
sql.stats = stats
|
|
331
336
|
return sql
|
|
337
|
+
|
|
338
|
+
// dd-trace starts query spans only inside function declarations named handler
|
|
339
|
+
function handler(q) {
|
|
340
|
+
send(l, q)
|
|
341
|
+
}
|
|
332
342
|
}
|
|
333
343
|
|
|
334
344
|
async function begin(options, fn) {
|
|
@@ -345,7 +355,7 @@ function Postgres(a, b) {
|
|
|
345
355
|
}
|
|
346
356
|
|
|
347
357
|
async function transaction() {
|
|
348
|
-
await Sql(
|
|
358
|
+
await Sql(handler).unsafe('begin ' + options.replace(/[^a-z ]/ig, ''), []).execute().catch(e => {
|
|
349
359
|
e instanceof PostgresError && settle(l, Lease.Released)
|
|
350
360
|
throw e
|
|
351
361
|
})
|
|
@@ -356,7 +366,7 @@ function Postgres(a, b) {
|
|
|
356
366
|
let result
|
|
357
367
|
l.state = Lease.Settling
|
|
358
368
|
try {
|
|
359
|
-
result = await statement(Sql(q =>
|
|
369
|
+
result = await statement(Sql(q => handler(q, Lease.Settling)))
|
|
360
370
|
} catch (e) {
|
|
361
371
|
settle(l, Lease.Discarded)
|
|
362
372
|
throw e
|
|
@@ -366,7 +376,7 @@ function Postgres(a, b) {
|
|
|
366
376
|
}
|
|
367
377
|
|
|
368
378
|
async function scope(fn, name) {
|
|
369
|
-
const sql = Sql(
|
|
379
|
+
const sql = Sql(track)
|
|
370
380
|
sql.savepoint = savepoint
|
|
371
381
|
sql.prepare = x => {
|
|
372
382
|
if (typeof x !== 'string' || !x || x.includes('\0'))
|
|
@@ -420,11 +430,16 @@ function Postgres(a, b) {
|
|
|
420
430
|
return scope(fn, 's' + savepoints++ + (name ? '_' + name : ''))
|
|
421
431
|
}
|
|
422
432
|
|
|
423
|
-
function
|
|
433
|
+
function track(q) {
|
|
424
434
|
q.catch(e => uncaughtError || (uncaughtError = e))
|
|
425
|
-
|
|
435
|
+
handler(q)
|
|
426
436
|
}
|
|
427
437
|
}
|
|
438
|
+
|
|
439
|
+
// dd-trace starts query spans only inside function declarations named handler
|
|
440
|
+
function handler(q, expected) {
|
|
441
|
+
send(l, q, expected)
|
|
442
|
+
}
|
|
428
443
|
}
|
|
429
444
|
|
|
430
445
|
function move(c, queue) {
|
|
@@ -462,7 +477,17 @@ function Postgres(a, b) {
|
|
|
462
477
|
const c = takeBusy()
|
|
463
478
|
c
|
|
464
479
|
? go(c, query)
|
|
465
|
-
:
|
|
480
|
+
: allHeldByCopy()
|
|
481
|
+
? query.reject(copyInProgress())
|
|
482
|
+
: queries.push(query)
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
function allHeldByCopy() {
|
|
486
|
+
return connections.every(c => c.copyHeld)
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
function copyInProgress() {
|
|
490
|
+
return Errors.generic('COPY_IN_PROGRESS', 'You cannot execute queries during copy')
|
|
466
491
|
}
|
|
467
492
|
|
|
468
493
|
function takeBusy() {
|
|
@@ -662,6 +687,7 @@ function parseOptions(a, b) {
|
|
|
662
687
|
prepare : true,
|
|
663
688
|
debug : false,
|
|
664
689
|
fetch_types : true,
|
|
690
|
+
parameter_types : 'server',
|
|
665
691
|
publications : 'alltables',
|
|
666
692
|
target_session_attrs: null
|
|
667
693
|
}
|
|
@@ -692,11 +718,13 @@ function parseOptions(a, b) {
|
|
|
692
718
|
},
|
|
693
719
|
types : o.types || {},
|
|
694
720
|
target_session_attrs: tsa(o, url, env),
|
|
721
|
+
parameter_types : parameterTypes(o, url, env),
|
|
695
722
|
onnotice : o.onnotice,
|
|
696
723
|
onnotify : o.onnotify,
|
|
697
724
|
onclose : o.onclose,
|
|
698
725
|
onidle : o.onidle,
|
|
699
726
|
onparameter : o.onparameter,
|
|
727
|
+
ontypemismatch : o.ontypemismatch,
|
|
700
728
|
socket : o.socket,
|
|
701
729
|
transform : parseTransform(o.transform || { undefined: undefined }),
|
|
702
730
|
parameters : {},
|
|
@@ -716,6 +744,14 @@ function tsa(o, url, env) {
|
|
|
716
744
|
throw new Error('target_session_attrs ' + x + ' is not supported')
|
|
717
745
|
}
|
|
718
746
|
|
|
747
|
+
function parameterTypes(o, url, env) {
|
|
748
|
+
const x = o.parameter_types || url.searchParams.get('parameter_types') || env.PGPARAMETER_TYPES || 'server'
|
|
749
|
+
if (['server', 'client', 'warn'].includes(x))
|
|
750
|
+
return x
|
|
751
|
+
|
|
752
|
+
throw new Error('parameter_types ' + x + ' is not supported')
|
|
753
|
+
}
|
|
754
|
+
|
|
719
755
|
function max_lifetime() {
|
|
720
756
|
return 60 * (30 + Math.random() * 30)
|
|
721
757
|
}
|
package/cjs/src/query.js
CHANGED
|
@@ -23,16 +23,9 @@ const Query = module.exports.Query = class Query extends Promise {
|
|
|
23
23
|
this.state = null
|
|
24
24
|
this.statement = null
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
resolve(x)
|
|
30
|
-
}
|
|
31
|
-
this.reject = x => {
|
|
32
|
-
this.active = false
|
|
33
|
-
this.settled = true
|
|
34
|
-
reject(x)
|
|
35
|
-
}
|
|
26
|
+
// dd-trace's load-time rewrite only accepts single-expression arrows here
|
|
27
|
+
this.resolve = x => (this.active = false, this.settled = true, resolve(x))
|
|
28
|
+
this.reject = x => (this.active = false, this.settled = true, reject(x))
|
|
36
29
|
|
|
37
30
|
this.active = false
|
|
38
31
|
this.settled = false
|
package/cjs/src/subscribe.js
CHANGED
|
@@ -3,6 +3,7 @@ const { defaultBackoff, backoffSeconds } = require('./connection.js')
|
|
|
3
3
|
|
|
4
4
|
const noop = () => { /* noop */ }
|
|
5
5
|
, commands = { 68: 'delete', 73: 'insert', 85: 'update' }
|
|
6
|
+
, statusInterval = 1000
|
|
6
7
|
|
|
7
8
|
module.exports = Subscribe;function Subscribe(postgres, options) {
|
|
8
9
|
const subscribers = new Map()
|
|
@@ -151,6 +152,10 @@ module.exports = Subscribe;function Subscribe(postgres, options) {
|
|
|
151
152
|
stream.on('error', reportConnection)
|
|
152
153
|
stream.on('close', sql.close)
|
|
153
154
|
|
|
155
|
+
// A walsender can miss the wakeup for a commit and sleep until its timeout; a status update wakes it.
|
|
156
|
+
const status = setInterval(() => stream.writable && pong(), statusInterval)
|
|
157
|
+
stream.on('close', () => clearInterval(status))
|
|
158
|
+
|
|
154
159
|
return { stream, state: xs.state }
|
|
155
160
|
|
|
156
161
|
function data(x) {
|
package/cjs/src/types.js
CHANGED
|
@@ -248,6 +248,11 @@ const inferType = module.exports.inferType = function inferType(x) {
|
|
|
248
248
|
)
|
|
249
249
|
}
|
|
250
250
|
|
|
251
|
+
module.exports.clientSerialize = clientSerialize;function clientSerialize(x) {
|
|
252
|
+
const proto = typeof x === 'object' && Object.getPrototypeOf(x)
|
|
253
|
+
return proto === Object.prototype || proto === null ? JSON.stringify(x) : '' + x
|
|
254
|
+
}
|
|
255
|
+
|
|
251
256
|
const escapeBackslash = /\\/g
|
|
252
257
|
const escapeQuote = /"/g
|
|
253
258
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@steadybrew/postgresjs",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.5.0-rc1",
|
|
4
4
|
"description": "PostgreSQL client for Node.js and Deno, independently maintained by Steady Brew",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "src/index.js",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"types": "types/index.d.ts",
|
|
25
25
|
"typings": "types/index.d.ts",
|
|
26
26
|
"engines": {
|
|
27
|
-
"node": ">=
|
|
27
|
+
"node": ">=22"
|
|
28
28
|
},
|
|
29
29
|
"scripts": {
|
|
30
30
|
"build": "npm run build:cjs && npm run build:deno && npm run build:cf",
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"test": "npm run test:startup && npm run test:esm && npm run test:cjs && npm run test:deno",
|
|
35
35
|
"test:esm": "node tests-startup/run.js --integration && node tests/index.js",
|
|
36
36
|
"test:startup": "node tests-startup/run.js",
|
|
37
|
+
"test:tracing": "node tests-tracing/run.js",
|
|
37
38
|
"test:cjs": "npm run build:cjs && node tests-startup/run.js --integration --cjs && cd cjs/tests && node index.js && cd ../../",
|
|
38
39
|
"test:deno": "npm run test:deno-socket && npm run build:deno && cd deno/tests && deno run --no-lock --allow-all --unsafely-ignore-certificate-errors index.js && cd ../../",
|
|
39
40
|
"lint": "eslint src && eslint tests && eslint tests-startup --ext .js,.mjs",
|
package/src/connection.js
CHANGED
|
@@ -4,7 +4,7 @@ import crypto from 'crypto'
|
|
|
4
4
|
import Stream from 'stream'
|
|
5
5
|
import { performance } from 'perf_hooks'
|
|
6
6
|
|
|
7
|
-
import { stringify, handleValue, addArrayType } from './types.js'
|
|
7
|
+
import { stringify, handleValue, addArrayType, clientSerialize } from './types.js'
|
|
8
8
|
import { Errors, callback } from './errors.js'
|
|
9
9
|
import Result from './result.js'
|
|
10
10
|
import Queue from './queue.js'
|
|
@@ -214,7 +214,10 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
|
|
|
214
214
|
const connection = {
|
|
215
215
|
queue: queues.closed,
|
|
216
216
|
idleTimer,
|
|
217
|
-
connect
|
|
217
|
+
// Sentry's hook wraps connect and reads the connection from this, so it must stay a method
|
|
218
|
+
connect(request) {
|
|
219
|
+
dispatch('acquire', request)
|
|
220
|
+
},
|
|
218
221
|
terminate: () => dispatch('terminate'),
|
|
219
222
|
execute,
|
|
220
223
|
release: () => dispatch('release'),
|
|
@@ -229,6 +232,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
|
|
|
229
232
|
get pipelined() { return sent.length },
|
|
230
233
|
get accepting() { return accepting() },
|
|
231
234
|
get copying() { return stream !== null },
|
|
235
|
+
get copyHeld() { return stream !== null && !stream.writableEnded },
|
|
232
236
|
get acquiring() { return acquisition && !acquisition.owner.cancelled ? 1 : 0 },
|
|
233
237
|
count: 0,
|
|
234
238
|
id
|
|
@@ -537,7 +541,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
|
|
|
537
541
|
|
|
538
542
|
build(q)
|
|
539
543
|
const written = write(toBuffer(q))
|
|
540
|
-
if (q.describeFirst || q.cursorFn || mayCopy(q))
|
|
544
|
+
if (q.describeFirst || q.cursorFn || mayCopy(q) || (q.prepare && !q.prepared && !q.options.simple && q.parameters.length))
|
|
541
545
|
barrier = q
|
|
542
546
|
return written && accepting()
|
|
543
547
|
} catch (error) {
|
|
@@ -575,7 +579,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
|
|
|
575
579
|
|
|
576
580
|
function prepared(q) {
|
|
577
581
|
return Buffer.concat([
|
|
578
|
-
Bind(q
|
|
582
|
+
Bind(q),
|
|
579
583
|
q.cursorFn
|
|
580
584
|
? Execute('', q.cursorRows)
|
|
581
585
|
: ExecuteUnnamed
|
|
@@ -603,8 +607,11 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
|
|
|
603
607
|
q.signature = q.prepare && types + string
|
|
604
608
|
q.onlyDescribe && (delete statements[q.signature])
|
|
605
609
|
q.parameters = q.parameters || parameters
|
|
610
|
+
q.inferred = options.parameter_types === 'server' ? null : types.slice()
|
|
606
611
|
q.prepared = q.prepare && q.signature in statements
|
|
607
|
-
q.describeFirst = q.onlyDescribe || (
|
|
612
|
+
q.describeFirst = q.onlyDescribe || (!q.prepared && types.some((x, i) =>
|
|
613
|
+
!x && (options.parameter_types !== 'client' || Array.isArray(q.parameters[i]))
|
|
614
|
+
))
|
|
608
615
|
q.statement = q.prepared
|
|
609
616
|
? statements[q.signature]
|
|
610
617
|
: { string, types, name: q.prepare ? statementId + statementCount++ : '' }
|
|
@@ -688,7 +695,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
|
|
|
688
695
|
err = new Error(err)
|
|
689
696
|
|
|
690
697
|
'query' in err || 'parameters' in err || Object.defineProperties(err, {
|
|
691
|
-
stack: { value: err.stack + query.origin.replace(/.*\n/, '\n'), enumerable: options.debug },
|
|
698
|
+
stack: { value: err.stack + query.origin.replace(/.*\n/, '\n'), enumerable: options.debug, writable: true, configurable: true },
|
|
692
699
|
query: { value: query.string, enumerable: options.debug },
|
|
693
700
|
parameters: { value: query.parameters, enumerable: options.debug },
|
|
694
701
|
args: { value: query.args, enumerable: options.debug },
|
|
@@ -1212,20 +1219,21 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
|
|
|
1212
1219
|
}
|
|
1213
1220
|
|
|
1214
1221
|
/* Messages */
|
|
1215
|
-
function Bind(
|
|
1222
|
+
function Bind(q) {
|
|
1223
|
+
const differences = []
|
|
1216
1224
|
let prev
|
|
1217
1225
|
, type
|
|
1218
1226
|
|
|
1219
|
-
b().B().str(
|
|
1227
|
+
b().B().str((q.cursorName || '') + b.N).str(q.statement.name + b.N).i16(0).i16(q.parameters.length)
|
|
1220
1228
|
|
|
1221
|
-
parameters.forEach((x, i) => {
|
|
1229
|
+
q.parameters.forEach((x, i) => {
|
|
1222
1230
|
if (x === null)
|
|
1223
1231
|
return b.i32(0xFFFFFFFF)
|
|
1224
1232
|
|
|
1225
|
-
type = types[i]
|
|
1226
|
-
x =
|
|
1227
|
-
?
|
|
1228
|
-
: ''
|
|
1233
|
+
type = q.statement.types[i]
|
|
1234
|
+
x = !q.inferred || q.inferred[i] || Array.isArray(x)
|
|
1235
|
+
? serialize(x, type)
|
|
1236
|
+
: options.parameter_types === 'client' ? clientSerialize(x) : compare(x, type, i, differences)
|
|
1229
1237
|
|
|
1230
1238
|
prev = b.i
|
|
1231
1239
|
b.inc(4).str(x).i32(b.i - prev - 4, prev)
|
|
@@ -1233,7 +1241,27 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
|
|
|
1233
1241
|
|
|
1234
1242
|
b.i16(0)
|
|
1235
1243
|
|
|
1236
|
-
|
|
1244
|
+
const message = b.end()
|
|
1245
|
+
differences.length && callback(options.ontypemismatch || console.warn, { query: q.string, parameters: differences }) // eslint-disable-line
|
|
1246
|
+
return message
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
function serialize(x, type) {
|
|
1250
|
+
return type in options.serializers
|
|
1251
|
+
? options.serializers[type](x)
|
|
1252
|
+
: '' + x
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
function compare(x, type, i, differences) {
|
|
1256
|
+
const serverText = serialize(x, type)
|
|
1257
|
+
let clientText
|
|
1258
|
+
try {
|
|
1259
|
+
clientText = clientSerialize(x)
|
|
1260
|
+
} catch (error) {
|
|
1261
|
+
clientText = error
|
|
1262
|
+
}
|
|
1263
|
+
serverText === clientText || differences.push({ position: i + 1, oid: type, typeof: typeof x, ...options.debug && { serverText, clientText } })
|
|
1264
|
+
return serverText
|
|
1237
1265
|
}
|
|
1238
1266
|
|
|
1239
1267
|
function Parse(str, parameters, types, name = '') {
|
package/src/index.js
CHANGED
|
@@ -86,7 +86,8 @@ function Postgres(a, b) {
|
|
|
86
86
|
listen,
|
|
87
87
|
begin,
|
|
88
88
|
close,
|
|
89
|
-
end
|
|
89
|
+
end,
|
|
90
|
+
...Symbol.asyncDispose && { [Symbol.asyncDispose]: () => end() }
|
|
90
91
|
})
|
|
91
92
|
|
|
92
93
|
return sql
|
|
@@ -273,7 +274,9 @@ function Postgres(a, b) {
|
|
|
273
274
|
const query = { reserve: c => resolve(own(c, l)), reject }
|
|
274
275
|
closed.length
|
|
275
276
|
? connect(closed.shift(), query)
|
|
276
|
-
:
|
|
277
|
+
: allHeldByCopy()
|
|
278
|
+
? reject(copyInProgress())
|
|
279
|
+
: queries.push(query)
|
|
277
280
|
})
|
|
278
281
|
return l
|
|
279
282
|
}
|
|
@@ -325,10 +328,17 @@ function Postgres(a, b) {
|
|
|
325
328
|
|
|
326
329
|
async function reserve() {
|
|
327
330
|
const l = await lease(false)
|
|
328
|
-
const sql = Sql(
|
|
331
|
+
const sql = Sql(handler)
|
|
329
332
|
sql.release = () => settle(l, Lease.Released)
|
|
333
|
+
Symbol.dispose && (sql[Symbol.dispose] = () => sql.release())
|
|
334
|
+
Symbol.asyncDispose && (sql[Symbol.asyncDispose] = async() => sql.release())
|
|
330
335
|
sql.stats = stats
|
|
331
336
|
return sql
|
|
337
|
+
|
|
338
|
+
// dd-trace starts query spans only inside function declarations named handler
|
|
339
|
+
function handler(q) {
|
|
340
|
+
send(l, q)
|
|
341
|
+
}
|
|
332
342
|
}
|
|
333
343
|
|
|
334
344
|
async function begin(options, fn) {
|
|
@@ -345,7 +355,7 @@ function Postgres(a, b) {
|
|
|
345
355
|
}
|
|
346
356
|
|
|
347
357
|
async function transaction() {
|
|
348
|
-
await Sql(
|
|
358
|
+
await Sql(handler).unsafe('begin ' + options.replace(/[^a-z ]/ig, ''), []).execute().catch(e => {
|
|
349
359
|
e instanceof PostgresError && settle(l, Lease.Released)
|
|
350
360
|
throw e
|
|
351
361
|
})
|
|
@@ -356,7 +366,7 @@ function Postgres(a, b) {
|
|
|
356
366
|
let result
|
|
357
367
|
l.state = Lease.Settling
|
|
358
368
|
try {
|
|
359
|
-
result = await statement(Sql(q =>
|
|
369
|
+
result = await statement(Sql(q => handler(q, Lease.Settling)))
|
|
360
370
|
} catch (e) {
|
|
361
371
|
settle(l, Lease.Discarded)
|
|
362
372
|
throw e
|
|
@@ -366,7 +376,7 @@ function Postgres(a, b) {
|
|
|
366
376
|
}
|
|
367
377
|
|
|
368
378
|
async function scope(fn, name) {
|
|
369
|
-
const sql = Sql(
|
|
379
|
+
const sql = Sql(track)
|
|
370
380
|
sql.savepoint = savepoint
|
|
371
381
|
sql.prepare = x => {
|
|
372
382
|
if (typeof x !== 'string' || !x || x.includes('\0'))
|
|
@@ -420,11 +430,16 @@ function Postgres(a, b) {
|
|
|
420
430
|
return scope(fn, 's' + savepoints++ + (name ? '_' + name : ''))
|
|
421
431
|
}
|
|
422
432
|
|
|
423
|
-
function
|
|
433
|
+
function track(q) {
|
|
424
434
|
q.catch(e => uncaughtError || (uncaughtError = e))
|
|
425
|
-
|
|
435
|
+
handler(q)
|
|
426
436
|
}
|
|
427
437
|
}
|
|
438
|
+
|
|
439
|
+
// dd-trace starts query spans only inside function declarations named handler
|
|
440
|
+
function handler(q, expected) {
|
|
441
|
+
send(l, q, expected)
|
|
442
|
+
}
|
|
428
443
|
}
|
|
429
444
|
|
|
430
445
|
function move(c, queue) {
|
|
@@ -462,7 +477,17 @@ function Postgres(a, b) {
|
|
|
462
477
|
const c = takeBusy()
|
|
463
478
|
c
|
|
464
479
|
? go(c, query)
|
|
465
|
-
:
|
|
480
|
+
: allHeldByCopy()
|
|
481
|
+
? query.reject(copyInProgress())
|
|
482
|
+
: queries.push(query)
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
function allHeldByCopy() {
|
|
486
|
+
return connections.every(c => c.copyHeld)
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
function copyInProgress() {
|
|
490
|
+
return Errors.generic('COPY_IN_PROGRESS', 'You cannot execute queries during copy')
|
|
466
491
|
}
|
|
467
492
|
|
|
468
493
|
function takeBusy() {
|
|
@@ -662,6 +687,7 @@ function parseOptions(a, b) {
|
|
|
662
687
|
prepare : true,
|
|
663
688
|
debug : false,
|
|
664
689
|
fetch_types : true,
|
|
690
|
+
parameter_types : 'server',
|
|
665
691
|
publications : 'alltables',
|
|
666
692
|
target_session_attrs: null
|
|
667
693
|
}
|
|
@@ -692,11 +718,13 @@ function parseOptions(a, b) {
|
|
|
692
718
|
},
|
|
693
719
|
types : o.types || {},
|
|
694
720
|
target_session_attrs: tsa(o, url, env),
|
|
721
|
+
parameter_types : parameterTypes(o, url, env),
|
|
695
722
|
onnotice : o.onnotice,
|
|
696
723
|
onnotify : o.onnotify,
|
|
697
724
|
onclose : o.onclose,
|
|
698
725
|
onidle : o.onidle,
|
|
699
726
|
onparameter : o.onparameter,
|
|
727
|
+
ontypemismatch : o.ontypemismatch,
|
|
700
728
|
socket : o.socket,
|
|
701
729
|
transform : parseTransform(o.transform || { undefined: undefined }),
|
|
702
730
|
parameters : {},
|
|
@@ -716,6 +744,14 @@ function tsa(o, url, env) {
|
|
|
716
744
|
throw new Error('target_session_attrs ' + x + ' is not supported')
|
|
717
745
|
}
|
|
718
746
|
|
|
747
|
+
function parameterTypes(o, url, env) {
|
|
748
|
+
const x = o.parameter_types || url.searchParams.get('parameter_types') || env.PGPARAMETER_TYPES || 'server'
|
|
749
|
+
if (['server', 'client', 'warn'].includes(x))
|
|
750
|
+
return x
|
|
751
|
+
|
|
752
|
+
throw new Error('parameter_types ' + x + ' is not supported')
|
|
753
|
+
}
|
|
754
|
+
|
|
719
755
|
function max_lifetime() {
|
|
720
756
|
return 60 * (30 + Math.random() * 30)
|
|
721
757
|
}
|
package/src/query.js
CHANGED
|
@@ -23,16 +23,9 @@ export class Query extends Promise {
|
|
|
23
23
|
this.state = null
|
|
24
24
|
this.statement = null
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
resolve(x)
|
|
30
|
-
}
|
|
31
|
-
this.reject = x => {
|
|
32
|
-
this.active = false
|
|
33
|
-
this.settled = true
|
|
34
|
-
reject(x)
|
|
35
|
-
}
|
|
26
|
+
// dd-trace's load-time rewrite only accepts single-expression arrows here
|
|
27
|
+
this.resolve = x => (this.active = false, this.settled = true, resolve(x))
|
|
28
|
+
this.reject = x => (this.active = false, this.settled = true, reject(x))
|
|
36
29
|
|
|
37
30
|
this.active = false
|
|
38
31
|
this.settled = false
|
package/src/subscribe.js
CHANGED
|
@@ -3,6 +3,7 @@ import { defaultBackoff, backoffSeconds } from './connection.js'
|
|
|
3
3
|
|
|
4
4
|
const noop = () => { /* noop */ }
|
|
5
5
|
, commands = { 68: 'delete', 73: 'insert', 85: 'update' }
|
|
6
|
+
, statusInterval = 1000
|
|
6
7
|
|
|
7
8
|
export default function Subscribe(postgres, options) {
|
|
8
9
|
const subscribers = new Map()
|
|
@@ -151,6 +152,10 @@ export default function Subscribe(postgres, options) {
|
|
|
151
152
|
stream.on('error', reportConnection)
|
|
152
153
|
stream.on('close', sql.close)
|
|
153
154
|
|
|
155
|
+
// A walsender can miss the wakeup for a commit and sleep until its timeout; a status update wakes it.
|
|
156
|
+
const status = setInterval(() => stream.writable && pong(), statusInterval)
|
|
157
|
+
stream.on('close', () => clearInterval(status))
|
|
158
|
+
|
|
154
159
|
return { stream, state: xs.state }
|
|
155
160
|
|
|
156
161
|
function data(x) {
|