Update dependency postgres to v4 #169
Reference in New Issue
Block a user
Delete Branch "renovate/postgres-4.x"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
This PR contains the following updates:
3.4.9→4.0.0Release Notes
porsager/postgres (postgres)
v4.0.0Compare Source
v4 rewrites everything below the public API: the pool, the connection, the protocol, startup and the socket layer. Queries, helpers, transactions, cursors, COPY, listen, subscribe, large objects and the options work as in 3.4.9, apart from the changes below.
Security
database,userorconnectionvalue such as'db\0user\0postgres'was sent as further startup parameters, so an app that put untrusted input there, as with a database per tenant, could be made to log in as another user where that user needs no password, or withreplicationoroptionsset. A NUL in any protocol string, also in query text and identifiers, now rejects withNUL_CHARACTER.__proto__is an own property (also in v3). It became the prototype of the row, so a JSON value in it, for instance from a column alias built from user input, made the row inherit its properties (row.isAdmin), in query results and insql.subscriberows alike.Upgrading from v3
The README has an upgrade guide, with a query that shows whether the change in parameter types concerns you.
infer: falseby default) (#392). v3 typed aDateastimestamptz, a boolean, aBuffer, abigintand arrays of these from JavaScript; v4 sends them without a type and writes each value for the type the server asks for, as libpq does.timestamptzcolumns, values written to a column of their own type, strings, numbers and anything cast work as before. Outside UTC, aDatewritten to or compared with atimestampordatecolumn is taken as UTC instead of the session's local time, and atimestampis read as UTC, so it comes back as the instant that was written whatever the time zone. ADatewhere the query doesn't decide its type, such as indate_trunc('day', ${ date }), needs a cast (${ date }::timestamptz).infer: truekeeps v3's behaviour.requirecan load on Node.js 20.19 and 22.12 and later (require('postgres').default).sql.array()is removed. Pass the array as it is, with a cast where the query doesn't decide its type:${ ids }::int[]. Callingsql.array()throwsARRAY_REMOVED.sql.beginrejects withTRANSACTION_ROLLED_BACK; v3 took it as a commit.sqlafterrelease(), or on a transaction'ssqlafter it has committed or rolled back, rejects; v3 ran it on whatever connection it then had. Releasing a reserved connection while its ownBEGINis still open closes it instead of returning an open transaction to the pool.beginoutsidesql.begin, a reserved connection ormax: 1rejects withUNSAFE_TRANSACTIONand is rolled back. A query that looks like it may open a transaction runs alone on its connection, so nothing else lands in it, and any query that leaves its connection in a transaction is rolled back and rejected. v3 rejectedBEGINbut left the transaction open. A script that opens and closes its own transaction in one query, such as a migration file withBEGINandCOMMIT, runs; v3 ran it and still rejected it.sql.begin(#1236, #1238) or one opened by hand on a reserved connection, just as a server error would: the queries after it fail with25P02and a laterCOMMITanswersROLLBACK. v3 skipped the query and let the rest commit.int[]comes back as an array of numbers; v3 returned the text.53300) is retried withbackoffuntilconnect_timeouthas passed, and the waiting queries reject with that error only then. Such a refusal is often momentary: connections opened at the same time against a connection limit can all be refused while the server counts them. v3 rejected the queries of the refused connection at once.target_session_attrsrejects withCONNECTION_DESTROYEDinstead of retrying forever.AUTH_TYPE_NOT_IMPLEMENTEDinstead of waiting for the connect timeout.onerroris logged. When thesql.subscribestream fails and none of its subscriptions has anonerror, the error goes toconsole.errorbefore the stream reconnects; v3 dropped it. Passonerrorto handle or silence it.infinitytimestamps areInfinityand-Infinity, both ways, instead of an invalidDate.import postgres, { type Sql } from 'postgres'). A reserved connection is no longer typed as havingbeginandend, which it never had.New
scheduler: falsepipelines on the busy connection with the fewest queries in flight instead; v3 went round-robin over the busy connections. Withmax: 1it is off, as there is only one connection.queue_timeout: seconds a query, transaction or reserve may wait for a connection before it rejects withQUEUE_TIMEOUT, unsent (#1036, #824)..signal(abortSignal)on a query: aborting rejects it if it is still waiting, or cancels it if it is running..cancel()or an aborted.signal()while the cursor's callback runs, or while an async iterator over it is between batches, fetches no more rows, closes the portal and rejects with57014. v3 sent a cancel request that the idle server ignored, so the cursor ran on to the end (or, arriving late, could cancel the next query).sql` and `.join(items)joins fragments and values with a fragment as separator, leaving out falsy items and adding spaces where missing (#807, #813).sql.begininside a transaction runs in a savepoint (#985).sql.rollback()rolls back a transaction, or the savepoint it is called in, without throwing;sql.beginresolves with the callback's result (#500).Promise.allbetween awaited ones are sent together and run in the order they were made (#951).toandfromcan be type names, looked up when the pool starts, with array types, for extension types such as pgvector (#1115, #719).usingandawait using:sqlends the pool and a reserved connection is released when it goes out of scope (#886, #1094).unref_sockets: truelets the process exit while connections are idle, likeallowExitOnIdlein node-postgres (#869, #918).min: connections kept open once the pool is used;idle_timeoutcloses only those above it (#672).useras a function, called each time a connection opens, likepassword, for credentials that rotate (#881).PGPASSFILEor~/.pgpassis read as libpq reads it (#964).name => valuearguments,make_interval(${ sql({ days: 1 }) })(#1040).copy ... from stdinwritable hascountandcommandonce it has finished, with the number of rows the server copied (#465).slot: 'name'keeps the slot ofsql.subscribeacross restarts and streams from where it was last confirmed. A transaction is confirmed only once its handlers have finished, a handler that fails gets its transaction delivered again, and handlers may be async: they run one after the other in commit order while the stream waits.sql.subscribe:truncateevents, and thelsn,timeandxidof the transaction on every change. Reconnecting retries with backoff.tls-server-end-point) when the server offers it, binding the login to the server certificate.channel_bindingis'prefer'by default,'require'or'disable'as in libpq, also from the url orPGCHANNELBINDING;'require'rejects an unbound connection withCHANNEL_BINDING_REQUIRED(#1008).sslnegotiation: 'direct'(PostgreSQL 17+) is opened as TLS from the first byte and is the fastest encrypted connection.workerdexport condition, oncloudflare:sockets, with TLS bystartTls.query.textandquery.parametersshow the text and values a query will be sent with, before it is sent.sql.stats: connections, opening, idle, leased and waiting, right now.postgres:query,postgres:connectandpostgres:transactionas diagnostics tracing channels, andpostgres:poolandpostgres:noticeas plain channels, for OpenTelemetry and other tracers without an option or a wrapper. Nothing is published, and nothing costs, without a subscriber. See Tracing.max: 1only for a query that is alone on the connection, so queries keep their order. v3 kept failing until the connection was replaced. Each connection reads rows and writes values only by what its own server described for the statement. When one connection sees a statement describe differently, the others prepare it again before their next use instead of trusting what they learned before.sql.close()closes the connections once their queries are done and leaves the pool usable.options.socketis also called with the host and port, and a socket can say it has done TLS itself (negotiated: true).onread: falsereads through the socket stream instead of a shared buffer, should a runtime ever misbehave with it.Fixed
The open issues and pull requests were triaged into 33 correctness bugs, each with a regression test in the v4 suite. Among them:
data_type_name; v3 set it as'data type_name', with a space, and typed it astype_name.JSON.stringifyof aPostgresErrorincludes its message (#767, #1132).readable({ start, end })stops atend; v3 ignored it and read to the end of the object.file://urls.TLS_HANDSHAKE_FAILEDinstead ofCONNECTION_CLOSEDorCONNECT_TIMEOUT(#1213), andperf_hooksis no longer imported (#990). Writes are split into 16 KiB pieces, as Workers were reported to drop a plain connection on a larger write (#1268).42P05(#960).ISqlis exported for functions that take the pool, a transaction or a reserved connection (#1163, #1150);PostgresErroris exported by name as a value as well as a type (#862, #703); nested arrays withnulland other values in helpers (#743); a typed query with a helper inside (#958); column lists that arestring[], while unknown literal columns are still caught (#916); class instances andobjectas JSON (#664, #625);nullandfalsefor the timer options (#1178).update t as x set ${ sql(o) }(#974),insert into t as x ${ sql(o) }(#517, #774),grant update on ${ sql(tables) }(#1101), rows inin (${ sql(rows) })(#820), a column list forinsert into t ${ sql(columns) } select ...(#769), objects in avalueshelper with picked columns, also inside a fragment (#1073, #1042), and fragments among the columns ofselect ${ sql([...]) }(#777). An insert or update helper without columns throwsNO_COLUMNSinstead of sending invalid SQL (#644). Several names outside a clause,order by ${ sql('a', 'b') }, become a list of identifiers; v3 threw aTypeError.valueslist cast afterwards, is written as an array literal or as JSON instead of1,2or[object Object](#741, #872, #765).?host=and?port=as libpq reads them (#984), and passwords with characters that are not percent-encoded, which are used as written (#735, #890).sql.beginand not awaited finishes before the transaction commits.max: 1, a query made beforesql.beginruns before the transaction.connect_timeoutdestroys a TCP connection that is still being opened, so the process can exit.AUTHENTICATION_INCOMPLETE, so SCRAM cannot be skipped.onnoticethat throws during startup no longer fails the connection.sql.subscribeparses custom array types and domains like queries do.Bufferrejects its query, also in a cursor, instead of hanging the connection.DEALLOCATE ALL, a statement prepared again is not forgotten because of an older query's error.search_pathper tenant, say) gets the rows of its own schema on every connection.debuggets the values as they are sent and the parameter types, also on the first use of a statement.sql.listen()({ state, unlisten }) andsql.prepare()(returns the name) match what they return.notifyinside a transaction uses the transaction (#966).nullin arrays (#1258),Infinity(#728), years below 100 and offsets in seconds (#1185).PostgresErrorfrom a plain message (#703) and writable stacks (#1227).end()stops connection attempts under way and settles every query; a reserved connection is never stranded.Performance
int2,int4,oidandboolcolumns are decoded straight from the bytes when they use the built-in parsers; a custom parser still gets the text.onread) instead of a buffer per chunk, on Node, Bun and Deno.Testing
node tests/unit).node tests/simulate.js).sh tests/cf/run.sh) and a TypeScript test for the types (cd types && npm test).npm run coverage(node tests/coverage.js,--linesfor the lines not run) runs the tests with Node's own V8 coverage and reports it per file insrc, with no dependency; CI fails belowCOVERAGE_MIN.Configuration
📅 Schedule: (UTC)
🚦 Automerge: Enabled.
♻ Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR has been generated by Mend Renovate CLI.
View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.