@steadybrew/postgresjs 4.4.0 → 4.4.1

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 CHANGED
@@ -592,7 +592,7 @@ for await (const chunk of readableStream) {
592
592
 
593
593
  > **NOTE** This is a low-level API which does not provide any type safety. To make this work, you must match your [`copy query` parameters](https://www.postgresql.org/docs/14/sql-copy.html) correctly to your [Node.js stream read or write](https://nodejs.org/api/stream.html) code. Ensure [Node.js stream backpressure](https://nodejs.org/en/learn/modules/backpressuring-in-streams) is handled correctly to avoid memory exhaustion.
594
594
 
595
- A `COPY` holds its connection until it finishes. Postgres.js cannot tell that a simple-protocol query is a `COPY` until the server answers, so it sends nothing behind a simple query whose text contains the word `copy` (`sql.unsafe()` or `sql.file()` without parameters, `.simple()`, `.readable()`, `.writable()`). Queries sent through `sql` meanwhile run on another connection or wait for one. With `max: 1`, or when every connection is running a `COPY`, a query awaited before the `COPY` is ended therefore waits forever. Statements on a `sql.reserve()` or `sql.begin()` handle reject with `COPY_IN_PROGRESS` while its `COPY` runs.
595
+ A `COPY` holds its connection until it finishes. Postgres.js cannot tell that a simple-protocol query is a `COPY` until the server answers, so it sends nothing behind a simple query whose text contains the word `copy` (`sql.unsafe()` or `sql.file()` without parameters, `.simple()`, `.readable()`, `.writable()`). Queries sent through `sql` meanwhile run on another connection or wait for one. When every connection is running a `COPY` that has not been ended, for example with `max: 1`, queries, `sql.reserve()` and `sql.begin()` through `sql` reject with `COPY_IN_PROGRESS` instead, because only that `COPY` finishing could free a connection. Statements on a `sql.reserve()` or `sql.begin()` handle reject with `COPY_IN_PROGRESS` while its `COPY` runs.
596
596
 
597
597
  ### Canceling Queries in Progress
598
598
 
@@ -1468,7 +1468,7 @@ If the only attempt times out, the error is `CONNECT_TIMEOUT`. Once earlier atte
1468
1468
  ##### COPY_IN_PROGRESS
1469
1469
  > You cannot execute queries during copy
1470
1470
 
1471
- This error is thrown for a statement on a `sql.reserve()` or `sql.begin()` handle while its copy operation (writable / readable) runs, including statements sent before the server started it. Queries through `sql` wait instead.
1471
+ This error is thrown for a statement on a `sql.reserve()` or `sql.begin()` handle while its copy operation (writable / readable) runs, including statements sent before the server started it. A query, `sql.reserve()` or `sql.begin()` through `sql` gets it when every connection is running a copy operation whose writable has not been ended; otherwise it waits for a connection.
1472
1472
 
1473
1473
  ##### TRANSACTION_ROLLED_BACK
1474
1474
  > The server rolled the transaction back instead of committing it
@@ -231,6 +231,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
231
231
  get pipelined() { return sent.length },
232
232
  get accepting() { return accepting() },
233
233
  get copying() { return stream !== null },
234
+ get copyHeld() { return stream !== null && !stream.writableEnded },
234
235
  get acquiring() { return acquisition && !acquisition.owner.cancelled ? 1 : 0 },
235
236
  count: 0,
236
237
  id
package/cf/src/index.js CHANGED
@@ -274,7 +274,9 @@ function Postgres(a, b) {
274
274
  const query = { reserve: c => resolve(own(c, l)), reject }
275
275
  closed.length
276
276
  ? connect(closed.shift(), query)
277
- : queries.push(query)
277
+ : allHeldByCopy()
278
+ ? reject(copyInProgress())
279
+ : queries.push(query)
278
280
  })
279
281
  return l
280
282
  }
@@ -463,7 +465,17 @@ function Postgres(a, b) {
463
465
  const c = takeBusy()
464
466
  c
465
467
  ? go(c, query)
466
- : queries.push(query)
468
+ : allHeldByCopy()
469
+ ? query.reject(copyInProgress())
470
+ : queries.push(query)
471
+ }
472
+
473
+ function allHeldByCopy() {
474
+ return connections.every(c => c.copyHeld)
475
+ }
476
+
477
+ function copyInProgress() {
478
+ return Errors.generic('COPY_IN_PROGRESS', 'You cannot execute queries during copy')
467
479
  }
468
480
 
469
481
  function takeBusy() {
@@ -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) {
@@ -229,6 +229,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
229
229
  get pipelined() { return sent.length },
230
230
  get accepting() { return accepting() },
231
231
  get copying() { return stream !== null },
232
+ get copyHeld() { return stream !== null && !stream.writableEnded },
232
233
  get acquiring() { return acquisition && !acquisition.owner.cancelled ? 1 : 0 },
233
234
  count: 0,
234
235
  id
package/cjs/src/index.js CHANGED
@@ -273,7 +273,9 @@ function Postgres(a, b) {
273
273
  const query = { reserve: c => resolve(own(c, l)), reject }
274
274
  closed.length
275
275
  ? connect(closed.shift(), query)
276
- : queries.push(query)
276
+ : allHeldByCopy()
277
+ ? reject(copyInProgress())
278
+ : queries.push(query)
277
279
  })
278
280
  return l
279
281
  }
@@ -462,7 +464,17 @@ function Postgres(a, b) {
462
464
  const c = takeBusy()
463
465
  c
464
466
  ? go(c, query)
465
- : queries.push(query)
467
+ : allHeldByCopy()
468
+ ? query.reject(copyInProgress())
469
+ : queries.push(query)
470
+ }
471
+
472
+ function allHeldByCopy() {
473
+ return connections.every(c => c.copyHeld)
474
+ }
475
+
476
+ function copyInProgress() {
477
+ return Errors.generic('COPY_IN_PROGRESS', 'You cannot execute queries during copy')
466
478
  }
467
479
 
468
480
  function takeBusy() {
@@ -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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@steadybrew/postgresjs",
3
- "version": "4.4.0",
3
+ "version": "4.4.1",
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",
package/src/connection.js CHANGED
@@ -229,6 +229,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
229
229
  get pipelined() { return sent.length },
230
230
  get accepting() { return accepting() },
231
231
  get copying() { return stream !== null },
232
+ get copyHeld() { return stream !== null && !stream.writableEnded },
232
233
  get acquiring() { return acquisition && !acquisition.owner.cancelled ? 1 : 0 },
233
234
  count: 0,
234
235
  id
package/src/index.js CHANGED
@@ -273,7 +273,9 @@ function Postgres(a, b) {
273
273
  const query = { reserve: c => resolve(own(c, l)), reject }
274
274
  closed.length
275
275
  ? connect(closed.shift(), query)
276
- : queries.push(query)
276
+ : allHeldByCopy()
277
+ ? reject(copyInProgress())
278
+ : queries.push(query)
277
279
  })
278
280
  return l
279
281
  }
@@ -462,7 +464,17 @@ function Postgres(a, b) {
462
464
  const c = takeBusy()
463
465
  c
464
466
  ? go(c, query)
465
- : queries.push(query)
467
+ : allHeldByCopy()
468
+ ? query.reject(copyInProgress())
469
+ : queries.push(query)
470
+ }
471
+
472
+ function allHeldByCopy() {
473
+ return connections.every(c => c.copyHeld)
474
+ }
475
+
476
+ function copyInProgress() {
477
+ return Errors.generic('COPY_IN_PROGRESS', 'You cannot execute queries during copy')
466
478
  }
467
479
 
468
480
  function takeBusy() {
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) {