Skip to content

Commit 0925baa

Browse files
committed
Build deno and cjs
1 parent 74dd8a0 commit 0925baa

File tree

14 files changed

+247
-89
lines changed

14 files changed

+247
-89
lines changed

cjs/src/connection.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,11 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
268268
socket,
269269
...(ssl === 'require' || ssl === 'allow' || ssl === 'prefer'
270270
? { rejectUnauthorized: false }
271-
: ssl
271+
: ssl === 'verify-full'
272+
? {}
273+
: typeof ssl === 'object'
274+
? ssl
275+
: {}
272276
)
273277
})
274278
socket.on('secureConnect', connected)
@@ -565,7 +569,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
565569
return errored(Errors.generic('UNSAFE_TRANSACTION', 'Only use sql.begin or max: 1'))
566570

567571
if (query.options.simple)
568-
return
572+
return BindComplete()
569573

570574
if (query.cursorFn) {
571575
result.count && query.cursorFn(result)

cjs/src/index.js

Lines changed: 40 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -376,44 +376,62 @@ function parseOptions(a, b) {
376376
const env = process.env // eslint-disable-line
377377
, o = (typeof a === 'string' ? b : a) || {}
378378
, { url, multihost } = parseUrl(a)
379-
, query = url.searchParams
379+
, query = [...url.searchParams].reduce((a, [b, c]) => (a[b] = c, a), {})
380380
, host = o.hostname || o.host || multihost || url.hostname || env.PGHOST || 'localhost'
381381
, port = o.port || url.port || env.PGPORT || 5432
382382
, user = o.user || o.username || url.username || env.PGUSERNAME || env.PGUSER || osUsername()
383383

384-
return Object.assign({
384+
o.no_prepare && (o.prepare = false)
385+
query.sslmode && (query.ssl = query.sslmode, delete query.sslmode)
386+
'timeout' in o && (console.log('The timeout option is deprecated, use idle_timeout instead'), o.idle_timeout = o.timeout) // eslint-disable-line
387+
388+
const defaults = {
389+
max : 10,
390+
ssl : false,
391+
idle_timeout : null,
392+
connect_timeout : 30,
393+
max_lifetime : max_lifetime,
394+
max_pipeline : 100,
395+
backoff : backoff,
396+
keep_alive : 60,
397+
prepare : true,
398+
debug : false,
399+
fetch_types : true,
400+
publications : 'alltables'
401+
}
402+
403+
return {
385404
host : Array.isArray(host) ? host : host.split(',').map(x => x.split(':')[0]),
386405
port : Array.isArray(port) ? port : host.split(',').map(x => parseInt(x.split(':')[1] || port)),
387406
path : o.path || host.indexOf('/') > -1 && host + '/.s.PGSQL.' + port,
388407
database : o.database || o.db || (url.pathname || '').slice(1) || env.PGDATABASE || user,
389408
user : user,
390409
pass : o.pass || o.password || url.password || env.PGPASSWORD || '',
391-
max : o.max || query.get('max') || 10,
410+
...Object.entries(defaults).reduce((acc, [k, d]) =>
411+
(acc[k] = k in o ? o[k] : k in query
412+
? (query[k] === 'disable' || query[k] === 'false' ? false : query[k])
413+
: env['PG' + k.toUpperCase()] || d,
414+
acc
415+
),
416+
{}
417+
),
418+
connection : {
419+
application_name: 'postgres.js',
420+
...o.connection,
421+
...Object.entries(query).reduce((acc, [k, v]) => (k in defaults || (acc[k] = v), acc), {})
422+
},
392423
types : o.types || {},
393-
ssl : o.ssl || parseSSL(query.get('sslmode') || query.get('ssl')) || false,
394-
idle_timeout : o.idle_timeout || query.get('idle_timeout') || env.PGIDLE_TIMEOUT || warn(o.timeout),
395-
connect_timeout : o.connect_timeout || query.get('connect_timeout') || env.PGCONNECT_TIMEOUT || 30,
396-
max_lifetime : o.max_lifetime || url.max_lifetime || max_lifetime,
397-
max_pipeline : o.max_pipeline || url.max_pipeline || 100,
398-
backoff : o.backoff || url.backoff || backoff,
399-
keep_alive : o.keep_alive || url.keep_alive || 60,
400-
prepare : 'prepare' in o ? o.prepare : 'no_prepare' in o ? !o.no_prepare : true,
424+
target_session_attrs: tsa(o, url, env),
401425
onnotice : o.onnotice,
402426
onnotify : o.onnotify,
403427
onclose : o.onclose,
404428
onparameter : o.onparameter,
405-
transform : parseTransform(o.transform || { undefined: undefined }),
406-
connection : Object.assign({ application_name: 'postgres.js' }, o.connection),
407-
target_session_attrs: tsa(o, url, env),
408-
debug : o.debug,
409429
socket : o.socket,
410-
fetch_types : 'fetch_types' in o ? o.fetch_types : true,
430+
transform : parseTransform(o.transform || { undefined: undefined }),
411431
parameters : {},
412432
shared : { retries: 0, typeArrayMap: {} },
413-
publications : o.publications || query.get('publications') || 'alltables'
414-
},
415-
mergeUserTypes(o.types)
416-
)
433+
...mergeUserTypes(o.types)
434+
}
417435
}
418436

419437
function tsa(o, url, env) {
@@ -450,30 +468,20 @@ function parseTransform(x) {
450468
}
451469
}
452470

453-
function parseSSL(x) {
454-
return x !== 'disable' && x !== 'false' && x
455-
}
456-
457471
function parseUrl(url) {
458472
if (typeof url !== 'string')
459473
return { url: { searchParams: new Map() } }
460474

461475
let host = url
462-
host = host.slice(host.indexOf('://') + 3)
463-
host = host.split(/[?/]/)[0]
464-
host = host.slice(host.indexOf('@') + 1)
476+
host = host.slice(host.indexOf('://') + 3).split(/[?/]/)[0]
477+
host = decodeURIComponent(host.slice(host.indexOf('@') + 1))
465478

466479
return {
467480
url: new URL(url.replace(host, host.split(',')[0])),
468481
multihost: host.indexOf(',') > -1 && host
469482
}
470483
}
471484

472-
function warn(x) {
473-
typeof x !== 'undefined' && console.log('The timeout option is deprecated, use idle_timeout instead') // eslint-disable-line
474-
return x
475-
}
476-
477485
function osUsername() {
478486
try {
479487
return os.userInfo().username // eslint-disable-line

cjs/src/subscribe.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ function parse(x, state, parsers, handle) {
212212
old && (i = tuples(x, old, key ? relation.keys : relation.columns, i += 3))
213213

214214
const row = {}
215-
i = tuples(x, row, relation.columns, i + 3)
215+
tuples(x, row, relation.columns, i + 3)
216216

217217
handle(row, {
218218
command: 'update',

cjs/src/types.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ const defaultHandlers = typeHandlers(types)
9999
module.exports.stringify = stringify;function stringify(q, string, value, parameters, types, options) { // eslint-disable-line
100100
for (let i = 1; i < q.strings.length; i++) {
101101
string += (
102+
value && value[0] instanceof Query ? value.reduce((acc, x) => acc + ' ' + fragment(x, parameters, types), '') :
102103
value instanceof Query ? fragment(value, parameters, types) :
103104
value instanceof Identifier ? value.value :
104105
value instanceof Builder ? value.build(string, parameters, types, options) :

cjs/tests/index.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,10 @@ t('unsafe simple', async() => {
612612
return [1, (await sql.unsafe('select 1 as x'))[0].x]
613613
})
614614

615+
t('unsafe simple includes columns', async() => {
616+
return ['x', (await sql.unsafe('select 1 as x').values()).columns[0].name]
617+
})
618+
615619
t('listen and notify', async() => {
616620
const sql = postgres(options)
617621
const channel = 'hello'
@@ -1812,7 +1816,7 @@ t('subscribe reconnects and calls onsubscribe', { timeout: 4 }, async() => {
18121816
await subscribeSql.close()
18131817
await delay(500)
18141818
await sql`delete from test`
1815-
await delay(10)
1819+
await delay(100)
18161820
await unsubscribe()
18171821
return [
18181822
'2insert,Murray,,delete,1,',
@@ -2096,3 +2100,14 @@ t('Supports nested fragments with parameters', async() => {
20962100
await sql`drop table test`
20972101
]
20982102
})
2103+
2104+
t('Supports arrays of fragments', async() => {
2105+
const [{ x }] = await sql`
2106+
${ [sql`select`, sql`1`, sql`as`, sql`x`] }
2107+
`
2108+
2109+
return [
2110+
1,
2111+
x
2112+
]
2113+
})

cjs/tests/test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const tests = {}
1313
const nt = module.exports.nt = () => ignored++
1414
const ot = module.exports.ot = (...rest) => (only = true, test(true, ...rest))
1515
const t = module.exports.t = (...rest) => test(false, ...rest)
16-
t.timeout = 0.5
16+
t.timeout = 1
1717

1818
async function test(o, name, options, fn) {
1919
typeof options !== 'object' && (fn = options, options = {})

deno/README.md

Lines changed: 91 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ async function insertUser({ name, age }) {
6868
* [Listen & notify](#listen--notify)
6969
* [Realtime subscribe](#realtime-subscribe)
7070
* [Numbers, bigint, numeric](#numbers-bigint-numeric)
71+
* [Result Array](#result-array)
7172
* [Connection details](#connection-details)
7273
* [Custom Types](#custom-types)
7374
* [Teardown / Cleanup](#teardown--cleanup)
@@ -336,7 +337,7 @@ await sql`
336337
`.cursor(async([row]) => {
337338
// row = { x: 1 }
338339
await http.request('https://example.com/wat', { row })
339-
}
340+
})
340341
```
341342

342343
##### for await...of
@@ -361,7 +362,7 @@ await sql`
361362
await Promise.all(rows.map(row =>
362363
http.request('https://example.com/wat', { row })
363364
))
364-
}
365+
})
365366
```
366367

367368
If an error is thrown inside the callback function no more rows will be requested and the outer promise will reject with the thrown error.
@@ -421,6 +422,53 @@ Using a file for a query is also supported with optional parameters to use if th
421422
const result = await sql.file('query.sql', ['Murray', 68])
422423
```
423424

425+
### Copy to/from as Streams
426+
427+
Postgres.js supports [`COPY ...`](https://www.postgresql.org/docs/14/sql-copy.html) queries, which are exposed as [Node.js streams](https://nodejs.org/api/stream.html).
428+
429+
#### ```await sql`copy ... from stdin`.writable() -> Writable```
430+
431+
```js
432+
import { pipeline } from 'node:stream/promises'
433+
434+
// Stream of users with the default tab delimitated cells and new-line delimitated rows
435+
const userStream = Readable.from([
436+
'Murray\t68\n',
437+
'Walter\t80\n'
438+
])
439+
440+
const query = await sql`copy users (name, age) from stdin`.writable()
441+
await pipeline(userStream, query);
442+
```
443+
444+
#### ```await sql`copy ... to stdout`.readable() -> Readable```
445+
446+
##### Using Stream Pipeline
447+
```js
448+
import { pipeline } from 'node:stream/promises'
449+
import { createWriteStream } from 'node:fs'
450+
451+
const readableStream = await sql`copy users (name, age) to stdout`.readable()
452+
await pipeline(readableStream, createWriteStream('output.tsv'))
453+
// output.tsv content: `Murray\t68\nWalter\t80\n`
454+
```
455+
456+
##### Using `for await...of`
457+
```js
458+
const readableStream = await sql`
459+
copy (
460+
select name, age
461+
from users
462+
where age = 68
463+
) to stdout
464+
`.readable()
465+
for await (const chunk of readableStream) {
466+
// chunk.toString() === `Murray\t68\n`
467+
}
468+
```
469+
470+
> **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/docs/guides/backpressuring-in-streams/) is handled correctly to avoid memory exhaustion.
471+
424472
### Canceling Queries in Progress
425473

426474
Postgres.js supports, [canceling queries in progress](https://www.postgresql.org/docs/7.1/protocol-protocol.html#AEN39000). It works by opening a new connection with a protocol level startup message to cancel the current query running on a specific connection. That means there is no guarantee that the query will be canceled, and due to the possible race conditions it might even result in canceling another query. This is fine for long running queries, but in the case of high load and fast queries it might be better to simply ignore results instead of canceling.
@@ -657,6 +705,46 @@ const sql = postgres({
657705

658706
There is currently no guaranteed way to handle `numeric` / `decimal` types in native Javascript. **These [and similar] types will be returned as a `string`**. The best way in this case is to use [custom types](#custom-types).
659707

708+
## Result Array
709+
710+
The `Result` Array returned from queries is a custom array allowing for easy destructuring or passing on directly to JSON.stringify or general Array usage. It includes the following properties.
711+
712+
### .count
713+
714+
The `count` property is the number of affected rows returned by the database. This is usefull for insert, update and delete operations to know the number of rows since .length will be 0 in these cases if not using `RETURNING ...`.
715+
716+
### .command
717+
718+
The `command` run by the query - eg. one of `SELECT`, `UPDATE`, `INSERT`, `DELETE`
719+
720+
### .columns
721+
722+
The `columns` returned by the query useful to determine types, or map to the result values when using `.values()`
723+
724+
```js
725+
{
726+
name : String, // Column name,
727+
type : oid, // PostgreSQL oid column type
728+
parser: Function // The function used by Postgres.js for parsing
729+
}
730+
```
731+
732+
### .statement
733+
734+
The `statement` contains information about the statement implicitly created by Postgres.js.
735+
736+
```js
737+
{
738+
name : String, // The auto generated statement name
739+
string : String, // The actual query string executed
740+
types : [oid], // An array of oid expected as input parameters
741+
columns : [Column] // Array of columns - same as Result.columns
742+
}
743+
```
744+
745+
### .state
746+
747+
This is the state `{ pid, secret }` of the connection that executed the query.
660748

661749
## Connection details
662750

@@ -675,7 +763,7 @@ const sql = postgres('postgres://username:password@host:port/database', {
675763
max_lifetime : null, // Max lifetime in seconds (more info below)
676764
idle_timeout : 0, // Idle connection timeout in seconds
677765
connect_timeout : 30, // Connect timeout in seconds
678-
no_prepare : false, // No automatic creation of prepared statements
766+
prepare : true, // Automatic creation of prepared statements
679767
types : [], // Array of custom types, see more below
680768
onnotice : fn, // Defaults to console.log
681769
onparameter : fn, // (key, value) when server param change

deno/src/connection.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,11 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
272272
socket,
273273
...(ssl === 'require' || ssl === 'allow' || ssl === 'prefer'
274274
? { rejectUnauthorized: false }
275-
: ssl
275+
: ssl === 'verify-full'
276+
? {}
277+
: typeof ssl === 'object'
278+
? ssl
279+
: {}
276280
)
277281
})
278282
socket.on('secureConnect', connected)
@@ -569,7 +573,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
569573
return errored(Errors.generic('UNSAFE_TRANSACTION', 'Only use sql.begin or max: 1'))
570574

571575
if (query.options.simple)
572-
return
576+
return BindComplete()
573577

574578
if (query.cursorFn) {
575579
result.count && query.cursorFn(result)

0 commit comments

Comments
 (0)