From d8d2dafd4cdd06a4cee2d6f80aa87034a27844be Mon Sep 17 00:00:00 2001 From: Stephen Haberman Date: Mon, 24 Mar 2025 19:25:46 -0500 Subject: [PATCH] feat: Add onquery callback. --- cf/src/connection.js | 1 + cf/src/index.js | 4 +++- cf/src/query.js | 27 +++++++++++++++++++++++---- cjs/src/connection.js | 1 + cjs/src/index.js | 4 +++- cjs/src/query.js | 27 +++++++++++++++++++++++---- deno/README.md | 27 ++++++++++++++++----------- deno/src/connection.js | 1 + deno/src/index.js | 4 +++- deno/src/query.js | 27 +++++++++++++++++++++++---- src/connection.js | 1 + src/index.js | 2 ++ src/query.js | 27 +++++++++++++++++++++++---- 13 files changed, 123 insertions(+), 30 deletions(-) diff --git a/cf/src/connection.js b/cf/src/connection.js index ee8b1e69..7d7a7158 100644 --- a/cf/src/connection.js +++ b/cf/src/connection.js @@ -167,6 +167,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose : (query = q, query.active = true) build(q) + q.onquery && (q.onquery = q.onquery(q)) return write(toBuffer(q)) && !q.describeFirst && !q.cursorFn diff --git a/cf/src/index.js b/cf/src/index.js index d24e9f9c..eb94550a 100644 --- a/cf/src/index.js +++ b/cf/src/index.js @@ -86,6 +86,7 @@ function Postgres(a, b) { function Sql(handler) { handler.debug = options.debug + handler.onquery = options.onquery Object.entries(options.types).reduce((acc, [name, type]) => { acc[name] = (x) => new Parameter(x, type.to) @@ -481,7 +482,7 @@ function parseOptions(a, b) { {} ), connection : { - application_name: 'postgres.js', + application_name: env.PGAPPNAME || 'postgres.js', ...o.connection, ...Object.entries(query).reduce((acc, [k, v]) => (k in defaults || (acc[k] = v), acc), {}) }, @@ -491,6 +492,7 @@ function parseOptions(a, b) { onnotify : o.onnotify, onclose : o.onclose, onparameter : o.onparameter, + onquery : o.onquery, socket : o.socket, transform : parseTransform(o.transform || { undefined: undefined }), parameters : {}, diff --git a/cf/src/query.js b/cf/src/query.js index 0d44a15c..877927c7 100644 --- a/cf/src/query.js +++ b/cf/src/query.js @@ -13,6 +13,9 @@ export class Query extends Promise { reject = b }) + this.resolver = resolve + this.rejecter = reject + this.tagged = Array.isArray(strings.raw) this.strings = strings this.args = args @@ -23,19 +26,29 @@ export class Query extends Promise { this.state = null this.statement = null - this.resolve = x => (this.active = false, resolve(x)) - this.reject = x => (this.active = false, reject(x)) - this.active = false this.cancelled = null this.executed = false this.signature = '' + this.onquery = this.handler.onquery this[originError] = this.handler.debug ? new Error() : this.tagged && cachedError(this.strings) } + resolve(x) { + this.active = false + this.onquery && (this.onquery = this.onquery(x)) + this.resolver(x) + } + + reject(x) { + this.active = false + this.onquery && (this.onquery = this.onquery(x)) + this.rejecter(x) + } + get origin() { return (this.handler.debug ? this[originError].stack @@ -137,7 +150,13 @@ export class Query extends Promise { } async handle() { - !this.executed && (this.executed = true) && await 1 && this.handler(this) + if (this.executed) + return + + this.executed = true + await 1 + this.onquery && (this.onquery = this.onquery(this)) + this.handler(this) } execute() { diff --git a/cjs/src/connection.js b/cjs/src/connection.js index f7f58d14..85e5e319 100644 --- a/cjs/src/connection.js +++ b/cjs/src/connection.js @@ -165,6 +165,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose : (query = q, query.active = true) build(q) + q.onquery && (q.onquery = q.onquery(q)) return write(toBuffer(q)) && !q.describeFirst && !q.cursorFn diff --git a/cjs/src/index.js b/cjs/src/index.js index 40ac2c18..3708db87 100644 --- a/cjs/src/index.js +++ b/cjs/src/index.js @@ -85,6 +85,7 @@ function Postgres(a, b) { function Sql(handler) { handler.debug = options.debug + handler.onquery = options.onquery Object.entries(options.types).reduce((acc, [name, type]) => { acc[name] = (x) => new Parameter(x, type.to) @@ -480,7 +481,7 @@ function parseOptions(a, b) { {} ), connection : { - application_name: 'postgres.js', + application_name: env.PGAPPNAME || 'postgres.js', ...o.connection, ...Object.entries(query).reduce((acc, [k, v]) => (k in defaults || (acc[k] = v), acc), {}) }, @@ -490,6 +491,7 @@ function parseOptions(a, b) { onnotify : o.onnotify, onclose : o.onclose, onparameter : o.onparameter, + onquery : o.onquery, socket : o.socket, transform : parseTransform(o.transform || { undefined: undefined }), parameters : {}, diff --git a/cjs/src/query.js b/cjs/src/query.js index 45327f2f..f6c0fc22 100644 --- a/cjs/src/query.js +++ b/cjs/src/query.js @@ -13,6 +13,9 @@ const Query = module.exports.Query = class Query extends Promise { reject = b }) + this.resolver = resolve + this.rejecter = reject + this.tagged = Array.isArray(strings.raw) this.strings = strings this.args = args @@ -23,19 +26,29 @@ const Query = module.exports.Query = class Query extends Promise { this.state = null this.statement = null - this.resolve = x => (this.active = false, resolve(x)) - this.reject = x => (this.active = false, reject(x)) - this.active = false this.cancelled = null this.executed = false this.signature = '' + this.onquery = this.handler.onquery this[originError] = this.handler.debug ? new Error() : this.tagged && cachedError(this.strings) } + resolve(x) { + this.active = false + this.onquery && (this.onquery = this.onquery(x)) + this.resolver(x) + } + + reject(x) { + this.active = false + this.onquery && (this.onquery = this.onquery(x)) + this.rejecter(x) + } + get origin() { return (this.handler.debug ? this[originError].stack @@ -137,7 +150,13 @@ const Query = module.exports.Query = class Query extends Promise { } async handle() { - !this.executed && (this.executed = true) && await 1 && this.handler(this) + if (this.executed) + return + + this.executed = true + await 1 + this.onquery && (this.onquery = this.onquery(this)) + this.handler(this) } execute() { diff --git a/deno/README.md b/deno/README.md index 6f8085cf..354fee26 100644 --- a/deno/README.md +++ b/deno/README.md @@ -1121,20 +1121,25 @@ It is also possible to connect to the database without a connection string or an const sql = postgres() ``` -| Option | Environment Variables | -| ----------------- | ------------------------ | -| `host` | `PGHOST` | -| `port` | `PGPORT` | -| `database` | `PGDATABASE` | -| `username` | `PGUSERNAME` or `PGUSER` | -| `password` | `PGPASSWORD` | -| `idle_timeout` | `PGIDLE_TIMEOUT` | -| `connect_timeout` | `PGCONNECT_TIMEOUT` | +| Option | Environment Variables | +| ------------------ | ------------------------ | +| `host` | `PGHOST` | +| `port` | `PGPORT` | +| `database` | `PGDATABASE` | +| `username` | `PGUSERNAME` or `PGUSER` | +| `password` | `PGPASSWORD` | +| `application_name` | `PGAPPNAME` | +| `idle_timeout` | `PGIDLE_TIMEOUT` | +| `connect_timeout` | `PGCONNECT_TIMEOUT` | ### Prepared statements Prepared statements will automatically be created for any queries where it can be inferred that the query is static. This can be disabled by using the `prepare: false` option. For instance — this is useful when [using PGBouncer in `transaction mode`](https://github.com/porsager/postgres/issues/93#issuecomment-656290493). +**update**: [since 1.21.0](https://www.pgbouncer.org/2023/10/pgbouncer-1-21-0) +PGBouncer supports protocol-level named prepared statements when [configured +properly](https://www.pgbouncer.org/config.html#max_prepared_statements) + ## Custom Types You can add ergonomic support for custom types, or simply use `sql.typed(value, type)` inline, where type is the PostgreSQL `oid` for the type and the correctly serialized string. _(`oid` values for types can be found in the `pg_catalog.pg_type` table.)_ @@ -1294,8 +1299,8 @@ This error is thrown if the user has called [`sql.end()`](#teardown--cleanup) an This error is thrown for any queries that were pending when the timeout to [`sql.end({ timeout: X })`](#teardown--cleanup) was reached. -##### CONNECTION_CONNECT_TIMEOUT -> write CONNECTION_CONNECT_TIMEOUT host:port +##### CONNECT_TIMEOUT +> write CONNECT_TIMEOUT host:port This error is thrown if the startup phase of the connection (tcp, protocol negotiation, and auth) took more than the default 30 seconds or what was specified using `connect_timeout` or `PGCONNECT_TIMEOUT`. diff --git a/deno/src/connection.js b/deno/src/connection.js index 1726a9aa..bdfc9639 100644 --- a/deno/src/connection.js +++ b/deno/src/connection.js @@ -168,6 +168,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose : (query = q, query.active = true) build(q) + q.onquery && (q.onquery = q.onquery(q)) return write(toBuffer(q)) && !q.describeFirst && !q.cursorFn diff --git a/deno/src/index.js b/deno/src/index.js index 3bbdf2ba..82bd5a7f 100644 --- a/deno/src/index.js +++ b/deno/src/index.js @@ -86,6 +86,7 @@ function Postgres(a, b) { function Sql(handler) { handler.debug = options.debug + handler.onquery = options.onquery Object.entries(options.types).reduce((acc, [name, type]) => { acc[name] = (x) => new Parameter(x, type.to) @@ -481,7 +482,7 @@ function parseOptions(a, b) { {} ), connection : { - application_name: 'postgres.js', + application_name: env.PGAPPNAME || 'postgres.js', ...o.connection, ...Object.entries(query).reduce((acc, [k, v]) => (k in defaults || (acc[k] = v), acc), {}) }, @@ -491,6 +492,7 @@ function parseOptions(a, b) { onnotify : o.onnotify, onclose : o.onclose, onparameter : o.onparameter, + onquery : o.onquery, socket : o.socket, transform : parseTransform(o.transform || { undefined: undefined }), parameters : {}, diff --git a/deno/src/query.js b/deno/src/query.js index 0d44a15c..877927c7 100644 --- a/deno/src/query.js +++ b/deno/src/query.js @@ -13,6 +13,9 @@ export class Query extends Promise { reject = b }) + this.resolver = resolve + this.rejecter = reject + this.tagged = Array.isArray(strings.raw) this.strings = strings this.args = args @@ -23,19 +26,29 @@ export class Query extends Promise { this.state = null this.statement = null - this.resolve = x => (this.active = false, resolve(x)) - this.reject = x => (this.active = false, reject(x)) - this.active = false this.cancelled = null this.executed = false this.signature = '' + this.onquery = this.handler.onquery this[originError] = this.handler.debug ? new Error() : this.tagged && cachedError(this.strings) } + resolve(x) { + this.active = false + this.onquery && (this.onquery = this.onquery(x)) + this.resolver(x) + } + + reject(x) { + this.active = false + this.onquery && (this.onquery = this.onquery(x)) + this.rejecter(x) + } + get origin() { return (this.handler.debug ? this[originError].stack @@ -137,7 +150,13 @@ export class Query extends Promise { } async handle() { - !this.executed && (this.executed = true) && await 1 && this.handler(this) + if (this.executed) + return + + this.executed = true + await 1 + this.onquery && (this.onquery = this.onquery(this)) + this.handler(this) } execute() { diff --git a/src/connection.js b/src/connection.js index 97cc97e1..60c18b6f 100644 --- a/src/connection.js +++ b/src/connection.js @@ -165,6 +165,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose : (query = q, query.active = true) build(q) + q.onquery && (q.onquery = q.onquery(q)) return write(toBuffer(q)) && !q.describeFirst && !q.cursorFn diff --git a/src/index.js b/src/index.js index 2dfd24e8..9a70f63a 100644 --- a/src/index.js +++ b/src/index.js @@ -85,6 +85,7 @@ function Postgres(a, b) { function Sql(handler) { handler.debug = options.debug + handler.onquery = options.onquery Object.entries(options.types).reduce((acc, [name, type]) => { acc[name] = (x) => new Parameter(x, type.to) @@ -490,6 +491,7 @@ function parseOptions(a, b) { onnotify : o.onnotify, onclose : o.onclose, onparameter : o.onparameter, + onquery : o.onquery, socket : o.socket, transform : parseTransform(o.transform || { undefined: undefined }), parameters : {}, diff --git a/src/query.js b/src/query.js index 0d44a15c..877927c7 100644 --- a/src/query.js +++ b/src/query.js @@ -13,6 +13,9 @@ export class Query extends Promise { reject = b }) + this.resolver = resolve + this.rejecter = reject + this.tagged = Array.isArray(strings.raw) this.strings = strings this.args = args @@ -23,19 +26,29 @@ export class Query extends Promise { this.state = null this.statement = null - this.resolve = x => (this.active = false, resolve(x)) - this.reject = x => (this.active = false, reject(x)) - this.active = false this.cancelled = null this.executed = false this.signature = '' + this.onquery = this.handler.onquery this[originError] = this.handler.debug ? new Error() : this.tagged && cachedError(this.strings) } + resolve(x) { + this.active = false + this.onquery && (this.onquery = this.onquery(x)) + this.resolver(x) + } + + reject(x) { + this.active = false + this.onquery && (this.onquery = this.onquery(x)) + this.rejecter(x) + } + get origin() { return (this.handler.debug ? this[originError].stack @@ -137,7 +150,13 @@ export class Query extends Promise { } async handle() { - !this.executed && (this.executed = true) && await 1 && this.handler(this) + if (this.executed) + return + + this.executed = true + await 1 + this.onquery && (this.onquery = this.onquery(this)) + this.handler(this) } execute() {