Comparing version 3.0.5 to 3.0.6
@@ -78,2 +78,3 @@ const net = require('net') | ||
let socket = null | ||
, cancelMessage | ||
, result = new Result() | ||
@@ -143,12 +144,10 @@ , incoming = Buffer.alloc(0) | ||
async function cancel({ pid, secret }, resolve, reject) { | ||
socket || (socket = await createSocket()) | ||
if (!socket) | ||
return | ||
socket.removeAllListeners() | ||
socket = net.Socket() | ||
socket.on('connect', () => socket.write(b().i32(16).i32(80877102).i32(pid).i32(secret).end(16))) | ||
socket.once('error', reject) | ||
socket.once('close', resolve) | ||
connect() | ||
try { | ||
cancelMessage = b().i32(16).i32(80877102).i32(pid).i32(secret).end(16) | ||
await connect() | ||
socket.once('error', reject) | ||
socket.once('close', resolve) | ||
} catch (error) { | ||
reject(error) | ||
} | ||
} | ||
@@ -364,3 +363,3 @@ | ||
function reconnect() { | ||
setTimeout(connect, closedDate ? closedDate + delay - Date.now() : 0) | ||
setTimeout(connect, closedDate ? closedDate + delay - Number(process.hrtime.bigint() / 1000000n) : 0) | ||
} | ||
@@ -427,3 +426,3 @@ | ||
socket.removeListener('connect', connected) | ||
socket.readyState !== 'closed' && socket.end(b().X().end()) | ||
socket.readyState === 'open' && socket.end(b().X().end()) | ||
} | ||
@@ -453,3 +452,3 @@ ended && (ended(), ending = ended = null) | ||
!hadError && (query || sent.length) && error(Errors.connection('CONNECTION_CLOSED', options, socket)) | ||
closedDate = Date.now() | ||
closedDate = Number(process.hrtime.bigint() / 1000000n) | ||
hadError && options.shared.retries++ | ||
@@ -963,3 +962,3 @@ delay = (typeof backoff === 'function' ? backoff(options.shared.retries) : backoff) * 1000 | ||
function StartupMessage() { | ||
return b().inc(4).i16(3).z(2).str( | ||
return cancelMessage || b().inc(4).i16(3).z(2).str( | ||
Object.entries(Object.assign({ | ||
@@ -1021,3 +1020,3 @@ user, | ||
timer && clearTimeout(timer) | ||
timer = setTimeout(done, seconds * 1000, arguments).unref() | ||
timer = setTimeout(done, seconds * 1000, arguments) | ||
} | ||
@@ -1024,0 +1023,0 @@ } |
@@ -121,2 +121,3 @@ const originCache = new Map() | ||
this.forEachFn = fn | ||
this.handle() | ||
return this | ||
@@ -123,0 +124,0 @@ } |
@@ -113,5 +113,24 @@ const { Query } = require('./query.js') | ||
function select(first, rest, parameters, types, transform) { | ||
typeof first === 'string' && (first = [first].concat(rest)) | ||
if (Array.isArray(first)) | ||
return first.map(x => escapeIdentifier(transform.column.to ? transform.column.to(x) : x)).join(',') | ||
let value | ||
const columns = rest.length ? rest.flat() : Object.keys(first) | ||
return columns.map(x => { | ||
value = first[x] | ||
return ( | ||
value instanceof Query ? value.strings[0] : | ||
value instanceof Identifier ? value.value : | ||
handleValue(value, parameters, types) | ||
) + ' as ' + escapeIdentifier(transform.column.to ? transform.column.to(x) : x) | ||
}).join(',') | ||
} | ||
const builders = Object.entries({ | ||
values, | ||
in: values, | ||
select, | ||
returning: select, | ||
@@ -125,19 +144,2 @@ update(first, rest, parameters, types, transform) { | ||
select(first, rest, parameters, types, transform) { | ||
typeof first === 'string' && (first = [first].concat(rest)) | ||
if (Array.isArray(first)) | ||
return first.map(x => escapeIdentifier(transform.column.to ? transform.column.to(x) : x)).join(',') | ||
let value | ||
const columns = rest.length ? rest.flat() : Object.keys(first) | ||
return columns.map(x => { | ||
value = first[x] | ||
return ( | ||
value instanceof Query ? value.strings[0] : | ||
value instanceof Identifier ? value.value : | ||
handleValue(value, parameters, types) | ||
) + ' as ' + escapeIdentifier(transform.column.to ? transform.column.to(x) : x) | ||
}).join(',') | ||
}, | ||
insert(first, rest, parameters, types, transform) { | ||
@@ -144,0 +146,0 @@ const columns = rest.length ? rest.flat() : Object.keys(Array.isArray(first) ? first[0] : first) |
{ | ||
"name": "postgres", | ||
"version": "3.0.5", | ||
"version": "3.0.6", | ||
"description": "Fastest full featured PostgreSQL client for Node.js", | ||
@@ -5,0 +5,0 @@ "type": "module", |
@@ -71,2 +71,3 @@ <img align="left" width="440" height="180" alt="Fastest full PostgreSQL nodejs client" src="https://raw.githubusercontent.com/porsager/postgres/master/postgresjs.svg?sanitize=true"> | ||
* [Transactions](#transactions) | ||
* [Data Transformation](#data-transformation) | ||
* [Listen & notify](#listen--notify) | ||
@@ -133,3 +134,3 @@ * [Realtime subscribe](#realtime-subscribe) | ||
Parameters are automatically extracted and handled by the database so that SQL injection isn't possible. No special handling is necessary, simply use tagged template literals as usual. **Dynamic queries and query building can be seen in the [next section]()**. // todo | ||
Parameters are automatically extracted and handled by the database so that SQL injection isn't possible. No special handling is necessary, simply use tagged template literals as usual. | ||
@@ -522,2 +523,34 @@ ```js | ||
## Data Transformation | ||
`postgres.js` comes with a number of built-in data transformation functions that can be used to transform the data returned from a query or when inserting data. They are available under `transformation` option in the `postgres()` function connection options. | ||
Like - `postgres('connectionURL', { transformation: {...} })` | ||
### Parameters | ||
* `to`: The function to transform the outgoing query column name to, i.e `SELECT ${ sql('aName') }` to `SELECT a_name` when using `postgres.toCamel`. | ||
* `from`: The function to transform the incoming query result column name to, see example below. | ||
> Both parameters are optional, if not provided, the default transformation function will be used. | ||
Built in transformation functions are: | ||
* For camelCase - `postgres.toCamel` and `postgres.fromCamel` | ||
* For PascalCase - `postgres.toPascal` and `postgres.fromPascal` | ||
* For Kebab-Case - `postgres.toKebab` and `postgres.fromKebab` | ||
These functions can be passed in as options when calling `postgres()`. For example - | ||
```js | ||
// this will tranform the column names to camel case back and forth | ||
(async function () { | ||
const sql = postgres('connectionURL', { transform: { column: { to: postgres.fromCamel, from: postgres.toCamel } }}); | ||
await sql`CREATE TABLE IF NOT EXISTS camel_case (a_test INTEGER, b_test TEXT)`; | ||
await sql`INSERT INTO camel_case ${ sql([{ aTest: 1, bTest: 1 }]) }` | ||
const data = await sql`SELECT ${ sql('aTest', 'bTest') } FROM camel_case`; | ||
console.log(data) // [ { aTest: 1, bTest: '1' } ] | ||
process.exit(1) | ||
})(); | ||
``` | ||
> Note that if a column name is originally registered as snake_case in the database then to tranform it from camelCase to snake_case when querying or inserting, the column camelCase name must be put in `sql('columnName')` as it's done in the above example. | ||
## Listen & notify | ||
@@ -692,2 +725,19 @@ | ||
For example: | ||
```js | ||
const sql = postgres() // no connections are opened | ||
await sql`...` // one connection is now opened | ||
await sql`...` // previous opened connection is reused | ||
// two connections are opened now | ||
await Promise.all([ | ||
sql`...`, | ||
sql`...` | ||
]) | ||
``` | ||
> When there are high amount of concurrent queries, `postgres` will open as many connections as needed up until `max` number of connections is reached. By default `max` is 10. This can be changed by setting `max` in the `postgres()` call. Example - `postgres('connectionURL', { max: 20 })`. | ||
This means that we get a much simpler story for error handling and reconnections. Queries will be sent over the wire immediately on the next available connection in the pool. Connections are automatically taken out of the pool if you start a transaction using `sql.begin()`, and automatically returned to the pool once your transaction is done. | ||
@@ -694,0 +744,0 @@ |
@@ -78,2 +78,3 @@ import net from 'net' | ||
let socket = null | ||
, cancelMessage | ||
, result = new Result() | ||
@@ -143,12 +144,10 @@ , incoming = Buffer.alloc(0) | ||
async function cancel({ pid, secret }, resolve, reject) { | ||
socket || (socket = await createSocket()) | ||
if (!socket) | ||
return | ||
socket.removeAllListeners() | ||
socket = net.Socket() | ||
socket.on('connect', () => socket.write(b().i32(16).i32(80877102).i32(pid).i32(secret).end(16))) | ||
socket.once('error', reject) | ||
socket.once('close', resolve) | ||
connect() | ||
try { | ||
cancelMessage = b().i32(16).i32(80877102).i32(pid).i32(secret).end(16) | ||
await connect() | ||
socket.once('error', reject) | ||
socket.once('close', resolve) | ||
} catch (error) { | ||
reject(error) | ||
} | ||
} | ||
@@ -364,3 +363,3 @@ | ||
function reconnect() { | ||
setTimeout(connect, closedDate ? closedDate + delay - Date.now() : 0) | ||
setTimeout(connect, closedDate ? closedDate + delay - Number(process.hrtime.bigint() / 1000000n) : 0) | ||
} | ||
@@ -427,3 +426,3 @@ | ||
socket.removeListener('connect', connected) | ||
socket.readyState !== 'closed' && socket.end(b().X().end()) | ||
socket.readyState === 'open' && socket.end(b().X().end()) | ||
} | ||
@@ -453,3 +452,3 @@ ended && (ended(), ending = ended = null) | ||
!hadError && (query || sent.length) && error(Errors.connection('CONNECTION_CLOSED', options, socket)) | ||
closedDate = Date.now() | ||
closedDate = Number(process.hrtime.bigint() / 1000000n) | ||
hadError && options.shared.retries++ | ||
@@ -963,3 +962,3 @@ delay = (typeof backoff === 'function' ? backoff(options.shared.retries) : backoff) * 1000 | ||
function StartupMessage() { | ||
return b().inc(4).i16(3).z(2).str( | ||
return cancelMessage || b().inc(4).i16(3).z(2).str( | ||
Object.entries(Object.assign({ | ||
@@ -1021,3 +1020,3 @@ user, | ||
timer && clearTimeout(timer) | ||
timer = setTimeout(done, seconds * 1000, arguments).unref() | ||
timer = setTimeout(done, seconds * 1000, arguments) | ||
} | ||
@@ -1024,0 +1023,0 @@ } |
@@ -121,2 +121,3 @@ const originCache = new Map() | ||
this.forEachFn = fn | ||
this.handle() | ||
return this | ||
@@ -123,0 +124,0 @@ } |
@@ -113,5 +113,24 @@ import { Query } from './query.js' | ||
function select(first, rest, parameters, types, transform) { | ||
typeof first === 'string' && (first = [first].concat(rest)) | ||
if (Array.isArray(first)) | ||
return first.map(x => escapeIdentifier(transform.column.to ? transform.column.to(x) : x)).join(',') | ||
let value | ||
const columns = rest.length ? rest.flat() : Object.keys(first) | ||
return columns.map(x => { | ||
value = first[x] | ||
return ( | ||
value instanceof Query ? value.strings[0] : | ||
value instanceof Identifier ? value.value : | ||
handleValue(value, parameters, types) | ||
) + ' as ' + escapeIdentifier(transform.column.to ? transform.column.to(x) : x) | ||
}).join(',') | ||
} | ||
const builders = Object.entries({ | ||
values, | ||
in: values, | ||
select, | ||
returning: select, | ||
@@ -125,19 +144,2 @@ update(first, rest, parameters, types, transform) { | ||
select(first, rest, parameters, types, transform) { | ||
typeof first === 'string' && (first = [first].concat(rest)) | ||
if (Array.isArray(first)) | ||
return first.map(x => escapeIdentifier(transform.column.to ? transform.column.to(x) : x)).join(',') | ||
let value | ||
const columns = rest.length ? rest.flat() : Object.keys(first) | ||
return columns.map(x => { | ||
value = first[x] | ||
return ( | ||
value instanceof Query ? value.strings[0] : | ||
value instanceof Identifier ? value.value : | ||
handleValue(value, parameters, types) | ||
) + ' as ' + escapeIdentifier(transform.column.to ? transform.column.to(x) : x) | ||
}).join(',') | ||
}, | ||
insert(first, rest, parameters, types, transform) { | ||
@@ -144,0 +146,0 @@ const columns = rest.length ? rest.flat() : Object.keys(Array.isArray(first) ? first[0] : first) |
4725
996
187292
26