(http://iceddev.com/)",
31 | "license": "MIT",
32 | "bugs": {
33 | "url": "https://github.com/brianc/node-postgres/issues"
34 | },
35 | "homepage": "https://github.com/brianc/node-postgres/tree/master/packages/pg-connection-string",
36 | "devDependencies": {
37 | "@types/pg": "^8.12.0",
38 | "chai": "^4.1.1",
39 | "coveralls": "^3.0.4",
40 | "istanbul": "^0.4.5",
41 | "mocha": "^10.5.2",
42 | "nyc": "^15",
43 | "tsx": "^4.19.4",
44 | "typescript": "^4.0.3"
45 | },
46 | "files": [
47 | "index.js",
48 | "index.d.ts",
49 | "esm"
50 | ]
51 | }
52 |
--------------------------------------------------------------------------------
/packages/pg-query-stream/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pg-query-stream",
3 | "version": "4.10.3",
4 | "description": "Postgres query result returned as readable stream",
5 | "main": "./dist/index.js",
6 | "types": "./dist/index.d.ts",
7 | "exports": {
8 | ".": {
9 | "import": "./esm/index.mjs",
10 | "require": "./dist/index.js",
11 | "default": "./dist/index.js"
12 | }
13 | },
14 | "scripts": {
15 | "test": "mocha -r ts-node/register test/**/*.ts"
16 | },
17 | "repository": {
18 | "type": "git",
19 | "url": "git://github.com/brianc/node-postgres.git",
20 | "directory": "packages/pg-query-stream"
21 | },
22 | "keywords": [
23 | "postgres",
24 | "query-stream",
25 | "pg",
26 | "query",
27 | "stream"
28 | ],
29 | "files": [
30 | "/dist/*{js,ts,map}",
31 | "/src",
32 | "/esm"
33 | ],
34 | "author": "Brian M. Carlson",
35 | "license": "MIT",
36 | "bugs": {
37 | "url": "https://github.com/brianc/node-postgres/issues"
38 | },
39 | "devDependencies": {
40 | "@types/chai": "^4.2.13",
41 | "@types/mocha": "^10.0.7",
42 | "@types/node": "^14.0.0",
43 | "@types/pg": "^7.14.5",
44 | "JSONStream": "~1.3.5",
45 | "concat-stream": "~1.0.1",
46 | "eslint-plugin-promise": "^7.2.1",
47 | "mocha": "^10.5.2",
48 | "pg": "^8.16.3",
49 | "stream-spec": "~0.3.5",
50 | "ts-node": "^8.5.4",
51 | "typescript": "^4.0.3"
52 | },
53 | "peerDependencies": {
54 | "pg": "^8"
55 | },
56 | "dependencies": {
57 | "pg-cursor": "^2.15.3"
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/packages/pg/test/integration/client/row-description-on-results-tests.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const helper = require('./test-helper')
3 | const assert = require('assert')
4 | const suite = new helper.Suite()
5 |
6 | const Client = helper.Client
7 |
8 | const conInfo = helper.config
9 |
10 | const checkResult = function (result) {
11 | assert(result.fields)
12 | assert.equal(result.fields.length, 3)
13 | const fields = result.fields
14 | assert.equal(fields[0].name, 'now')
15 | assert.equal(fields[1].name, 'num')
16 | assert.equal(fields[2].name, 'texty')
17 | assert.equal(fields[0].dataTypeID, 1184)
18 | assert.equal(fields[1].dataTypeID, 23)
19 | assert.equal(fields[2].dataTypeID, 25)
20 | }
21 |
22 | suite.test('row descriptions on result object', function () {
23 | const client = new Client(conInfo)
24 | client.connect(
25 | assert.success(function () {
26 | client.query(
27 | 'SELECT NOW() as now, 1::int as num, $1::text as texty',
28 | ['hello'],
29 | assert.success(function (result) {
30 | checkResult(result)
31 | client.end()
32 | })
33 | )
34 | })
35 | )
36 | })
37 |
38 | suite.test('row description on no rows', function () {
39 | const client = new Client(conInfo)
40 | client.connect(
41 | assert.success(function () {
42 | client.query(
43 | 'SELECT NOW() as now, 1::int as num, $1::text as texty LIMIT 0',
44 | ['hello'],
45 | assert.success(function (result) {
46 | checkResult(result)
47 | client.end()
48 | })
49 | )
50 | })
51 | )
52 | })
53 |
--------------------------------------------------------------------------------
/docs/pages/apis/utilities.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Utilities
3 | ---
4 | import { Alert } from '/components/alert.tsx'
5 |
6 | ## Utility Functions
7 | ### pg.escapeIdentifier
8 |
9 | Escapes a string as a [SQL identifier](https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS).
10 |
11 | ```js
12 | import { escapeIdentifier } from 'pg';
13 | const escapedIdentifier = escapeIdentifier('FooIdentifier')
14 | console.log(escapedIdentifier) // '"FooIdentifier"'
15 | ```
16 |
17 |
18 | **Note**: When using an identifier that is the result of this function in an operation like `CREATE TABLE ${escapedIdentifier(identifier)}`, the table that is created will be CASE SENSITIVE. If you use any capital letters in the escaped identifier, you must always refer to the created table like `SELECT * from "MyCaseSensitiveTable"`; queries like `SELECT * FROM MyCaseSensitiveTable` will result in a "Non-existent table" error since case information is stripped from the query.
19 |
20 |
21 | ### pg.escapeLiteral
22 |
23 |
24 | **Note**: Instead of manually escaping SQL literals, it is recommended to use parameterized queries. Refer to [parameterized queries](/features/queries#parameterized-query) and the [client.query](/apis/client#clientquery) API for more information.
25 |
26 |
27 | Escapes a string as a [SQL literal](https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-CONSTANTS).
28 |
29 | ```js
30 | import { escapeLiteral } from 'pg';
31 | const escapedLiteral = escapeLiteral("hello 'world'")
32 | console.log(escapedLiteral) // "'hello ''world'''"
33 | ```
34 |
--------------------------------------------------------------------------------
/packages/pg-cursor/README.md:
--------------------------------------------------------------------------------
1 | node-pg-cursor
2 | ==============
3 |
4 | Use a PostgreSQL result cursor from node with an easy to use API.
5 |
6 | ### install
7 |
8 | ```sh
9 | $ npm install pg-cursor
10 | ```
11 | ___note___: this depends on _either_ `npm install pg` or `npm install pg.js`, but you __must__ be using the pure JavaScript client. This will __not work__ with the native bindings.
12 |
13 | ### :star: [Documentation](https://node-postgres.com/apis/cursor) :star:
14 |
15 | ### license
16 |
17 | The MIT License (MIT)
18 |
19 | Copyright (c) 2013 Brian M. Carlson
20 |
21 | Permission is hereby granted, free of charge, to any person obtaining a copy
22 | of this software and associated documentation files (the "Software"), to deal
23 | in the Software without restriction, including without limitation the rights
24 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
25 | copies of the Software, and to permit persons to whom the Software is
26 | furnished to do so, subject to the following conditions:
27 |
28 | The above copyright notice and this permission notice shall be included in
29 | all copies or substantial portions of the Software.
30 |
31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
37 | THE SOFTWARE.
38 |
--------------------------------------------------------------------------------
/packages/pg-native/test/copy-from.js:
--------------------------------------------------------------------------------
1 | const assert = require('assert')
2 | const Client = require('../')
3 |
4 | describe('COPY FROM', function () {
5 | before(function (done) {
6 | this.client = Client()
7 | this.client.connect(done)
8 | })
9 |
10 | after(function (done) {
11 | this.client.end(done)
12 | })
13 |
14 | it('works', function (done) {
15 | const client = this.client
16 | this.client.querySync('CREATE TEMP TABLE blah(name text, age int)')
17 | this.client.querySync('COPY blah FROM stdin')
18 | const stream = this.client.getCopyStream()
19 | stream.write(Buffer.from('Brian\t32\n', 'utf8'))
20 | stream.write(Buffer.from('Aaron\t30\n', 'utf8'))
21 | stream.write(Buffer.from('Shelley\t28\n', 'utf8'))
22 | stream.end()
23 |
24 | stream.once('finish', function () {
25 | const rows = client.querySync('SELECT COUNT(*) FROM blah')
26 | assert.equal(rows.length, 1)
27 | assert.equal(rows[0].count, 3)
28 | done()
29 | })
30 | })
31 |
32 | it('works with a callback passed to end', function (done) {
33 | const client = this.client
34 | this.client.querySync('CREATE TEMP TABLE boom(name text, age int)')
35 | this.client.querySync('COPY boom FROM stdin')
36 | const stream = this.client.getCopyStream()
37 | stream.write(Buffer.from('Brian\t32\n', 'utf8'))
38 | stream.write(Buffer.from('Aaron\t30\n', 'utf8'), function () {
39 | stream.end(Buffer.from('Shelley\t28\n', 'utf8'), function () {
40 | const rows = client.querySync('SELECT COUNT(*) FROM boom')
41 | assert.equal(rows.length, 1)
42 | assert.equal(rows[0].count, 3)
43 | done()
44 | })
45 | })
46 | })
47 | })
48 |
--------------------------------------------------------------------------------
/packages/pg/test/integration/client/result-metadata-tests.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const helper = require('./test-helper')
3 | const pg = helper.pg
4 | const assert = require('assert')
5 |
6 | const pool = new pg.Pool()
7 | new helper.Suite().test('should return insert metadata', function () {
8 | pool.connect(
9 | assert.calls(function (err, client, done) {
10 | assert(!err)
11 |
12 | helper.versionGTE(
13 | client,
14 | 90000,
15 | assert.success(function (hasRowCount) {
16 | client.query(
17 | 'CREATE TEMP TABLE zugzug(name varchar(10))',
18 | assert.calls(function (err, result) {
19 | assert(!err)
20 | assert.equal(result.oid, null)
21 | assert.equal(result.command, 'CREATE')
22 |
23 | client.query(
24 | "INSERT INTO zugzug(name) VALUES('more work?')",
25 | assert.calls(function (err, result) {
26 | assert(!err)
27 | assert.equal(result.command, 'INSERT')
28 | assert.equal(result.rowCount, 1)
29 |
30 | client.query(
31 | 'SELECT * FROM zugzug',
32 | assert.calls(function (err, result) {
33 | assert(!err)
34 | if (hasRowCount) assert.equal(result.rowCount, 1)
35 | assert.equal(result.command, 'SELECT')
36 | done()
37 | process.nextTick(pool.end.bind(pool))
38 | })
39 | )
40 | })
41 | )
42 | })
43 | )
44 | })
45 | )
46 | })
47 | )
48 | })
49 |
--------------------------------------------------------------------------------
/packages/pg/test/tls/GNUmakefile:
--------------------------------------------------------------------------------
1 | DESTDIR ::= /var/lib/postgres/data
2 | POSTGRES_USER ::= postgres
3 | POSTGRES_GROUP ::= postgres
4 | DATABASE_HOST ::= localhost
5 | DATABASE_USER ::= postgres
6 |
7 | all: \
8 | test-server-ca.crt \
9 | test-client-ca.crt \
10 | test-server.key \
11 | test-server.crt \
12 | test-client.key \
13 | test-client.crt
14 |
15 | clean:
16 | rm -f \
17 | test-server-ca.key \
18 | test-client-ca.key \
19 | test-server-ca.crt \
20 | test-client-ca.crt \
21 | test-server.key \
22 | test-server.crt \
23 | test-client.key \
24 | test-client.crt
25 |
26 | install: test-server.crt test-server.key test-client-ca.crt
27 | install \
28 | --owner=$(POSTGRES_USER) \
29 | --group=$(POSTGRES_GROUP) \
30 | --mode=0600 \
31 | -t $(DESTDIR) \
32 | $^
33 |
34 | test-%-ca.crt: test-%-ca.key
35 | openssl req -new -x509 \
36 | -subj '/CN=node-postgres test $* CA' \
37 | -days 3650 \
38 | -key $< \
39 | -out $@
40 |
41 | test-server.csr: test-server.key
42 | openssl req -new \
43 | -subj '/CN=$(DATABASE_HOST)' \
44 | -key $< \
45 | -out $@
46 |
47 | test-client.csr: test-client.key
48 | openssl req -new \
49 | -subj '/CN=$(DATABASE_USER)' \
50 | -key $< \
51 | -out $@
52 |
53 | test-%.crt: test-%.csr test-%-ca.crt test-%-ca.key
54 | openssl x509 -req \
55 | -CA test-$*-ca.crt \
56 | -CAkey test-$*-ca.key \
57 | -set_serial 1 \
58 | -days 3650 \
59 | -in $< \
60 | -out $@
61 |
62 | %.key:
63 | openssl genpkey \
64 | -algorithm EC \
65 | -pkeyopt ec_paramgen_curve:prime256v1 \
66 | -out $@
67 |
68 | .PHONY: all clean install
69 | .SECONDARY: test-server-ca.key test-client-ca.key
70 | .INTERMEDIATE: test-server.csr test-client.csr
71 | .POSIX:
72 |
--------------------------------------------------------------------------------
/packages/pg/test/wrangler.jsonc:
--------------------------------------------------------------------------------
1 | /**
2 | * For more details on how to configure Wrangler, refer to:
3 | * https://developers.cloudflare.com/workers/wrangler/configuration/
4 | */
5 | {
6 | "$schema": "node_modules/wrangler/config-schema.json",
7 | "name": "my-first-worker",
8 | "main": "src/index.ts",
9 | "compatibility_date": "2025-04-07",
10 | "compatibility_flags": ["nodejs_compat"],
11 | "observability": {
12 | "enabled": true
13 | }
14 | /**
15 | * t
16 | * Docs: https://developers.cloudflare.com/workers/configuration/smart-placement/#smart-placement
17 | */
18 | // "placement": { "mode": "smart" },
19 |
20 | /**
21 | * Bindings
22 | * Bindings allow your Worker to interact with resources on the Cloudflare Developer Platform, including
23 | * databases, object storage, AI inference, real-time communication and more.
24 | * https://developers.cloudflare.com/workers/runtime-apis/bindings/
25 | */
26 |
27 | /**
28 | * Environment Variables
29 | * https://developers.cloudflare.com/workers/wrangler/configuration/#environment-variables
30 | */
31 | // "vars": { "MY_VARIABLE": "production_value" },
32 | /**
33 | * Note: Use secrets to store sensitive data.
34 | * https://developers.cloudflare.com/workers/configuration/secrets/
35 | */
36 |
37 | /**
38 | * Static Assets
39 | * https://developers.cloudflare.com/workers/static-assets/binding/
40 | */
41 | // "assets": { "directory": "./public/", "binding": "ASSETS" },
42 |
43 | /**
44 | * Service Bindings (communicate between multiple Workers)
45 | * https://developers.cloudflare.com/workers/wrangler/configuration/#service-bindings
46 | */
47 | // "services": [{ "binding": "MY_SERVICE", "service": "my-service" }]
48 | }
49 |
--------------------------------------------------------------------------------
/packages/pg-native/test/notify.js:
--------------------------------------------------------------------------------
1 | const Client = require('../')
2 | const ok = require('okay')
3 |
4 | const notify = function (channel, payload) {
5 | const client = new Client()
6 | client.connectSync()
7 | client.querySync('NOTIFY ' + channel + ", '" + payload + "'")
8 | client.end()
9 | }
10 |
11 | describe('simple LISTEN/NOTIFY', function () {
12 | before(function (done) {
13 | const client = (this.client = new Client())
14 | client.connect(done)
15 | })
16 |
17 | it('works', function (done) {
18 | const client = this.client
19 | client.querySync('LISTEN boom')
20 | client.on('notification', function (msg) {
21 | done()
22 | })
23 | notify('boom', 'sup')
24 | })
25 |
26 | after(function (done) {
27 | this.client.end(done)
28 | })
29 | })
30 |
31 | if (!process.env.TRAVIS_CI) {
32 | describe('async LISTEN/NOTIFY', function () {
33 | before(function (done) {
34 | const client = (this.client = new Client())
35 | client.connect(done)
36 | })
37 |
38 | it('works', function (done) {
39 | const client = this.client
40 | let count = 0
41 | const check = function () {
42 | count++
43 | if (count >= 2) return done()
44 | }
45 | client.on('notification', check)
46 | client.query(
47 | 'LISTEN test',
48 | ok(done, function () {
49 | notify('test', 'bot')
50 | client.query(
51 | 'SELECT pg_sleep(.05)',
52 | ok(done, function () {
53 | notify('test', 'bot')
54 | })
55 | )
56 | })
57 | )
58 | })
59 |
60 | after(function (done) {
61 | this.client.end(done)
62 | })
63 | })
64 | }
65 |
--------------------------------------------------------------------------------
/packages/pg-protocol/src/buffer-reader.ts:
--------------------------------------------------------------------------------
1 | const emptyBuffer = Buffer.allocUnsafe(0)
2 |
3 | export class BufferReader {
4 | private buffer: Buffer = emptyBuffer
5 |
6 | // TODO(bmc): support non-utf8 encoding?
7 | private encoding: string = 'utf-8'
8 |
9 | constructor(private offset: number = 0) {}
10 |
11 | public setBuffer(offset: number, buffer: Buffer): void {
12 | this.offset = offset
13 | this.buffer = buffer
14 | }
15 |
16 | public int16(): number {
17 | const result = this.buffer.readInt16BE(this.offset)
18 | this.offset += 2
19 | return result
20 | }
21 |
22 | public byte(): number {
23 | const result = this.buffer[this.offset]
24 | this.offset++
25 | return result
26 | }
27 |
28 | public int32(): number {
29 | const result = this.buffer.readInt32BE(this.offset)
30 | this.offset += 4
31 | return result
32 | }
33 |
34 | public uint32(): number {
35 | const result = this.buffer.readUInt32BE(this.offset)
36 | this.offset += 4
37 | return result
38 | }
39 |
40 | public string(length: number): string {
41 | const result = this.buffer.toString(this.encoding, this.offset, this.offset + length)
42 | this.offset += length
43 | return result
44 | }
45 |
46 | public cstring(): string {
47 | const start = this.offset
48 | let end = start
49 | // eslint-disable-next-line no-empty
50 | while (this.buffer[end++] !== 0) {}
51 | this.offset = end
52 | return this.buffer.toString(this.encoding, start, end - 1)
53 | }
54 |
55 | public bytes(length: number): Buffer {
56 | const result = this.buffer.slice(this.offset, this.offset + length)
57 | this.offset += length
58 | return result
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/packages/pg/test/integration/client/custom-types-tests.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const helper = require('./test-helper')
3 | const Client = helper.pg.Client
4 | const suite = new helper.Suite()
5 | const assert = require('assert')
6 |
7 | const customTypes = {
8 | getTypeParser: () => () => 'okay!',
9 | }
10 |
11 | suite.test('custom type parser in client config', (done) => {
12 | const client = new Client({ types: customTypes })
13 |
14 | client.connect().then(() => {
15 | client.query(
16 | 'SELECT NOW() as val',
17 | assert.success(function (res) {
18 | assert.equal(res.rows[0].val, 'okay!')
19 | client.end().then(done)
20 | })
21 | )
22 | })
23 | })
24 |
25 | suite.test('custom type parser in client config with multiple results', (done) => {
26 | const client = new Client({ types: customTypes })
27 |
28 | client.connect().then(() => {
29 | client.query(
30 | `SELECT 'foo'::text as name; SELECT 'bar'::text as baz`,
31 | assert.success(function (res) {
32 | assert.equal(res[0].rows[0].name, 'okay!')
33 | assert.equal(res[1].rows[0].baz, 'okay!')
34 | client.end().then(done)
35 | })
36 | )
37 | })
38 | })
39 |
40 | // Custom type-parsers per query are not supported in native
41 | if (!helper.args.native) {
42 | suite.test('custom type parser in query', (done) => {
43 | const client = new Client()
44 |
45 | client.connect().then(() => {
46 | client.query(
47 | {
48 | text: 'SELECT NOW() as val',
49 | types: customTypes,
50 | },
51 | assert.success(function (res) {
52 | assert.equal(res.rows[0].val, 'okay!')
53 | client.end().then(done)
54 | })
55 | )
56 | })
57 | })
58 | }
59 |
--------------------------------------------------------------------------------
/packages/pg/test/native/native-connection-string-tests.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const helper = require('../test-helper')
3 | const Client = require('../../lib/native')
4 | const suite = new helper.Suite()
5 | const assert = require('assert')
6 |
7 | suite.test('respects nativeConnectionString in config', function (done) {
8 | const realPort = helper.config.port
9 | const nativeConnectionString = `host=${helper.config.host} port=${helper.config.port} dbname=${helper.config.database} user=${helper.config.user} password=${helper.config.password}`
10 |
11 | // setting wrong port to make sure config is take from nativeConnectionString and not env
12 | helper.config.port = '90929'
13 |
14 | const client = new Client({
15 | ...helper.config,
16 | nativeConnectionString,
17 | })
18 |
19 | client.connect(function (err) {
20 | assert(!err)
21 | client.query(
22 | 'SELECT 1 as num',
23 | assert.calls(function (err, result) {
24 | assert(!err)
25 | assert.equal(result.rows[0].num, 1)
26 | assert.strictEqual(result.rowCount, 1)
27 | // restore post in case helper config will be reused
28 | helper.config.port = realPort
29 | client.end(done)
30 | })
31 | )
32 | })
33 | })
34 |
35 | suite.test('respects nativeConnectionString in config even when it is corrupted', function (done) {
36 | const nativeConnectionString = `foobar`
37 |
38 | const client = new Client({
39 | nativeConnectionString,
40 | })
41 |
42 | client.connect(function (err) {
43 | assert(err)
44 | assert.equal(
45 | err.message,
46 | 'missing "=" after "foobar" in connection info string\n',
47 | 'Connection error should have been thrown'
48 | )
49 | client.end(done)
50 | })
51 | })
52 |
--------------------------------------------------------------------------------
/packages/pg/test/integration/client/query-as-promise-tests.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const bluebird = require('bluebird')
3 | const helper = require('../test-helper')
4 | const pg = helper.pg
5 | const assert = require('assert')
6 |
7 | process.on('unhandledRejection', function (e) {
8 | console.error(e, e.stack)
9 | process.exit(1)
10 | })
11 |
12 | const suite = new helper.Suite()
13 |
14 | suite.test('promise API', (cb) => {
15 | const pool = new pg.Pool()
16 | pool.connect().then((client) => {
17 | client
18 | .query('SELECT $1::text as name', ['foo'])
19 | .then(function (result) {
20 | assert.equal(result.rows[0].name, 'foo')
21 | return client
22 | })
23 | .then(function (client) {
24 | client.query('ALKJSDF').catch(function (e) {
25 | assert(e instanceof Error)
26 | client.query('SELECT 1 as num').then(function (result) {
27 | assert.equal(result.rows[0].num, 1)
28 | client.release()
29 | pool.end(cb)
30 | })
31 | })
32 | })
33 | })
34 | })
35 |
36 | suite.test('promise API with configurable promise type', (cb) => {
37 | const client = new pg.Client({ Promise: bluebird })
38 | const connectPromise = client.connect()
39 | assert(connectPromise instanceof bluebird, 'Client connect() returns configured promise')
40 |
41 | connectPromise
42 | .then(() => {
43 | const queryPromise = client.query('SELECT 1')
44 | assert(queryPromise instanceof bluebird, 'Client query() returns configured promise')
45 |
46 | return queryPromise.then(() => {
47 | client.end(cb)
48 | })
49 | })
50 | .catch((error) => {
51 | process.nextTick(() => {
52 | throw error
53 | })
54 | })
55 | })
56 |
--------------------------------------------------------------------------------
/packages/pg-pool/test/lifetime-timeout.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const co = require('co')
3 | const expect = require('expect.js')
4 |
5 | const describe = require('mocha').describe
6 | const it = require('mocha').it
7 |
8 | const Pool = require('../')
9 |
10 | describe('lifetime timeout', () => {
11 | it('connection lifetime should expire and remove the client', (done) => {
12 | const pool = new Pool({ maxLifetimeSeconds: 1 })
13 | pool.query('SELECT NOW()')
14 | pool.on('remove', () => {
15 | console.log('expired while idle - on-remove event')
16 | expect(pool.expiredCount).to.equal(0)
17 | expect(pool.totalCount).to.equal(0)
18 | done()
19 | })
20 | })
21 | it('connection lifetime should expire and remove the client after the client is done working', (done) => {
22 | const pool = new Pool({ maxLifetimeSeconds: 1 })
23 | pool.query('SELECT pg_sleep(1.4)')
24 | pool.on('remove', () => {
25 | console.log('expired while busy - on-remove event')
26 | expect(pool.expiredCount).to.equal(0)
27 | expect(pool.totalCount).to.equal(0)
28 | done()
29 | })
30 | })
31 | it(
32 | 'can remove expired clients and recreate them',
33 | co.wrap(function* () {
34 | const pool = new Pool({ maxLifetimeSeconds: 1 })
35 | const query = pool.query('SELECT pg_sleep(1.4)')
36 | expect(pool.expiredCount).to.equal(0)
37 | expect(pool.totalCount).to.equal(1)
38 | yield query
39 | yield new Promise((resolve) => setTimeout(resolve, 100))
40 | expect(pool.expiredCount).to.equal(0)
41 | expect(pool.totalCount).to.equal(0)
42 | yield pool.query('SELECT NOW()')
43 | expect(pool.expiredCount).to.equal(0)
44 | expect(pool.totalCount).to.equal(1)
45 | })
46 | )
47 | })
48 |
--------------------------------------------------------------------------------
/packages/pg/script/create-test-tables.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const args = require('../test/cli')
3 | const pg = require('../lib')
4 |
5 | const people = [
6 | { name: 'Aaron', age: 10 },
7 | { name: 'Brian', age: 20 },
8 | { name: 'Chris', age: 30 },
9 | { name: 'David', age: 40 },
10 | { name: 'Elvis', age: 50 },
11 | { name: 'Frank', age: 60 },
12 | { name: 'Grace', age: 70 },
13 | { name: 'Haley', age: 80 },
14 | { name: 'Irma', age: 90 },
15 | { name: 'Jenny', age: 100 },
16 | { name: 'Kevin', age: 110 },
17 | { name: 'Larry', age: 120 },
18 | { name: 'Michelle', age: 130 },
19 | { name: 'Nancy', age: 140 },
20 | { name: 'Olivia', age: 150 },
21 | { name: 'Peter', age: 160 },
22 | { name: 'Quinn', age: 170 },
23 | { name: 'Ronda', age: 180 },
24 | { name: 'Shelley', age: 190 },
25 | { name: 'Tobias', age: 200 },
26 | { name: 'Uma', age: 210 },
27 | { name: 'Veena', age: 220 },
28 | { name: 'Wanda', age: 230 },
29 | { name: 'Xavier', age: 240 },
30 | { name: 'Yoyo', age: 250 },
31 | { name: 'Zanzabar', age: 260 },
32 | ]
33 |
34 | async function run() {
35 | const con = new pg.Client({
36 | user: args.user,
37 | password: args.password,
38 | host: args.host,
39 | port: args.port,
40 | database: args.database,
41 | })
42 | console.log('creating test dataset')
43 | await con.connect()
44 | await con.query('DROP TABLE IF EXISTS person')
45 | await con.query('CREATE TABLE person (id serial, name varchar(10), age integer)')
46 | await con.query(
47 | 'INSERT INTO person (name, age) VALUES' + people.map((person) => ` ('${person.name}', ${person.age})`).join(',')
48 | )
49 | await con.end()
50 | console.log('created test dataset')
51 | }
52 |
53 | run().catch((e) => {
54 | console.log('setup failed', e)
55 | process.exit(255)
56 | })
57 |
--------------------------------------------------------------------------------
/packages/pg/test/buffer-list.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const BufferList = function () {
4 | this.buffers = []
5 | }
6 | const p = BufferList.prototype
7 |
8 | p.add = function (buffer, front) {
9 | this.buffers[front ? 'unshift' : 'push'](buffer)
10 | return this
11 | }
12 |
13 | p.addInt16 = function (val, front) {
14 | return this.add(Buffer.from([val >>> 8, val >>> 0]), front)
15 | }
16 |
17 | p.getByteLength = function () {
18 | return this.buffers.reduce(function (previous, current) {
19 | return previous + current.length
20 | }, 0)
21 | }
22 |
23 | p.addInt32 = function (val, first) {
24 | return this.add(
25 | Buffer.from([(val >>> 24) & 0xff, (val >>> 16) & 0xff, (val >>> 8) & 0xff, (val >>> 0) & 0xff]),
26 | first
27 | )
28 | }
29 |
30 | p.addCString = function (val, front) {
31 | const len = Buffer.byteLength(val)
32 | const buffer = Buffer.alloc(len + 1)
33 | buffer.write(val)
34 | buffer[len] = 0
35 | return this.add(buffer, front)
36 | }
37 |
38 | p.addString = function (val, front) {
39 | const len = Buffer.byteLength(val)
40 | const buffer = Buffer.alloc(len)
41 | buffer.write(val)
42 | return this.add(buffer, front)
43 | }
44 |
45 | p.addChar = function (char, first) {
46 | return this.add(Buffer.from(char, 'utf8'), first)
47 | }
48 |
49 | p.join = function (appendLength, char) {
50 | let length = this.getByteLength()
51 | if (appendLength) {
52 | this.addInt32(length + 4, true)
53 | return this.join(false, char)
54 | }
55 | if (char) {
56 | this.addChar(char, true)
57 | length++
58 | }
59 | const result = Buffer.alloc(length)
60 | let index = 0
61 | this.buffers.forEach(function (buffer) {
62 | buffer.copy(result, index, 0)
63 | index += buffer.length
64 | })
65 | return result
66 | }
67 |
68 | module.exports = BufferList
69 |
--------------------------------------------------------------------------------
/packages/pg/test/integration/client/async-stack-trace-tests.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const helper = require('../test-helper')
3 | const pg = helper.pg
4 |
5 | process.on('unhandledRejection', function (e) {
6 | console.error(e, e.stack)
7 | process.exit(1)
8 | })
9 |
10 | const suite = new helper.Suite()
11 |
12 | // these tests will only work for if --async-stack-traces is on, which is the default starting in node 16.
13 | const NODE_MAJOR_VERSION = +process.versions.node.split('.')[0]
14 | if (NODE_MAJOR_VERSION >= 16) {
15 | suite.testAsync('promise API async stack trace in pool', async function outerFunction() {
16 | async function innerFunction() {
17 | const pool = new pg.Pool()
18 | await pool.query('SELECT test from nonexistent')
19 | }
20 | try {
21 | await innerFunction()
22 | throw Error('should have errored')
23 | } catch (e) {
24 | const stack = e.stack
25 | if (!e.stack.includes('innerFunction') || !e.stack.includes('outerFunction')) {
26 | throw Error('async stack trace does not contain wanted values: ' + stack)
27 | }
28 | }
29 | })
30 |
31 | suite.testAsync('promise API async stack trace in client', async function outerFunction() {
32 | async function innerFunction() {
33 | const client = new pg.Client()
34 | await client.connect()
35 | try {
36 | await client.query('SELECT test from nonexistent')
37 | } finally {
38 | client.end()
39 | }
40 | }
41 | try {
42 | await innerFunction()
43 | throw Error('should have errored')
44 | } catch (e) {
45 | const stack = e.stack
46 | if (!e.stack.includes('innerFunction') || !e.stack.includes('outerFunction')) {
47 | throw Error('async stack trace does not contain wanted values: ' + stack)
48 | }
49 | }
50 | })
51 | }
52 |
--------------------------------------------------------------------------------
/packages/pg/test/unit/client/throw-in-type-parser-tests.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const helper = require('./test-helper')
3 | const Query = require('../../../lib/query')
4 | const types = require('pg-types')
5 | const assert = require('assert')
6 |
7 | const suite = new helper.Suite()
8 |
9 | const typeParserError = new Error('TEST: Throw in type parsers')
10 |
11 | types.setTypeParser('special oid that will throw', function () {
12 | throw typeParserError
13 | })
14 |
15 | const emitFakeEvents = (con) => {
16 | setImmediate(() => {
17 | con.emit('readyForQuery')
18 |
19 | con.emit('rowDescription', {
20 | fields: [
21 | {
22 | name: 'boom',
23 | dataTypeID: 'special oid that will throw',
24 | },
25 | ],
26 | })
27 |
28 | con.emit('dataRow', { fields: ['hi'] })
29 | con.emit('dataRow', { fields: ['hi'] })
30 | con.emit('commandComplete', { text: 'INSERT 31 1' })
31 | con.emit('readyForQuery')
32 | })
33 | }
34 |
35 | suite.test('emits error', function (done) {
36 | const client = helper.client()
37 | const con = client.connection
38 | const query = client.query(new Query('whatever'))
39 | emitFakeEvents(con)
40 |
41 | assert.emits(query, 'error', function (err) {
42 | assert.equal(err, typeParserError)
43 | done()
44 | })
45 | })
46 |
47 | suite.test('calls callback with error', function (done) {
48 | const client = helper.client()
49 | const con = client.connection
50 | emitFakeEvents(con)
51 | client.query('whatever', function (err) {
52 | assert.equal(err, typeParserError)
53 | done()
54 | })
55 | })
56 |
57 | suite.test('rejects promise with error', function (done) {
58 | const client = helper.client()
59 | const con = client.connection
60 | emitFakeEvents(con)
61 | client.query('whatever').catch((err) => {
62 | assert.equal(err, typeParserError)
63 | done()
64 | })
65 | })
66 |
--------------------------------------------------------------------------------
/docs/pages/features/native.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Native Bindings
3 | slug: /features/native
4 | metaTitle: bar
5 | ---
6 |
7 | Native bindings between node.js & [libpq](https://www.postgresql.org/docs/9.5/static/libpq.html) are provided by the [node-pg-native](https://github.com/brianc/node-pg-native) package. node-postgres can consume this package & use the native bindings to access the PostgreSQL server while giving you the same interface that is used with the JavaScript version of the library.
8 |
9 | To use the native bindings first you'll need to install them:
10 |
11 | ```sh
12 | $ npm install pg pg-native
13 | ```
14 |
15 | Once `pg-native` is installed instead of requiring a `Client` or `Pool` constructor from `pg` you do the following:
16 |
17 | ```js
18 | import pg from 'pg'
19 | const { native } = pg
20 | const { Client, Pool } = native
21 | ```
22 |
23 | When you access the `.native` property on `'pg'` it will automatically require the `pg-native` package and wrap it in the same API.
24 |
25 |
26 | Care has been taken to normalize between the two, but there might still be edge cases where things behave subtly differently due to the nature of using libpq over handling the binary protocol directly in JavaScript, so it's recommended you chose to either use the JavaScript driver or the native bindings both in development and production. For what its worth: I use the pure JavaScript driver because the JavaScript driver is more portable (doesn't need a compiler), and the pure JavaScript driver is plenty fast.
27 |
28 |
29 | Some of the modules using advanced features of PostgreSQL such as [pg-query-stream](https://github.com/brianc/node-pg-query-stream), [pg-cursor](https://github.com/brianc/node-pg-cursor),and [pg-copy-streams](https://github.com/brianc/node-pg-copy-streams) need to operate directly on the binary stream and therefore are incompatible with the native bindings.
30 |
--------------------------------------------------------------------------------
/packages/pg-cursor/test/transactions.js:
--------------------------------------------------------------------------------
1 | const assert = require('assert')
2 | const Cursor = require('../')
3 | const pg = require('pg')
4 |
5 | describe('transactions', () => {
6 | it('can execute multiple statements in a transaction', async () => {
7 | const client = new pg.Client()
8 | await client.connect()
9 | await client.query('begin')
10 | await client.query('CREATE TEMP TABLE foobar(id SERIAL PRIMARY KEY)')
11 | const cursor = client.query(new Cursor('SELECT * FROM foobar'))
12 | const rows = await new Promise((resolve, reject) => {
13 | cursor.read(10, (err, rows) => (err ? reject(err) : resolve(rows)))
14 | })
15 | assert.strictEqual(rows.length, 0)
16 | await client.query('ALTER TABLE foobar ADD COLUMN name TEXT')
17 | await client.end()
18 | })
19 |
20 | it('can execute multiple statements in a transaction if ending cursor early', async () => {
21 | const client = new pg.Client()
22 | await client.connect()
23 | await client.query('begin')
24 | await client.query('CREATE TEMP TABLE foobar(id SERIAL PRIMARY KEY)')
25 | const cursor = client.query(new Cursor('SELECT * FROM foobar'))
26 | await new Promise((resolve) => cursor.close(resolve))
27 | await client.query('ALTER TABLE foobar ADD COLUMN name TEXT')
28 | await client.end()
29 | })
30 |
31 | it('can execute multiple statements in a transaction if no data', async () => {
32 | const client = new pg.Client()
33 | await client.connect()
34 | await client.query('begin')
35 | // create a cursor that has no data response
36 | const createText = 'CREATE TEMP TABLE foobar(id SERIAL PRIMARY KEY)'
37 | const cursor = client.query(new Cursor(createText))
38 | const err = await new Promise((resolve) => cursor.read(100, resolve))
39 | assert.ifError(err)
40 | await client.query('ALTER TABLE foobar ADD COLUMN name TEXT')
41 | await client.end()
42 | })
43 | })
44 |
--------------------------------------------------------------------------------
/packages/pg-pool/test/releasing-clients.js:
--------------------------------------------------------------------------------
1 | const Pool = require('../')
2 |
3 | const expect = require('expect.js')
4 |
5 | describe('releasing clients', () => {
6 | it('removes a client which cannot be queried', async () => {
7 | // make a pool w/ only 1 client
8 | const pool = new Pool({ max: 1 })
9 | expect(pool.totalCount).to.eql(0)
10 | const client = await pool.connect()
11 | expect(pool.totalCount).to.eql(1)
12 | expect(pool.idleCount).to.eql(0)
13 | // reach into the client and sever its connection
14 | client.connection.end()
15 |
16 | // wait for the client to error out
17 | const err = await new Promise((resolve) => client.once('error', resolve))
18 | expect(err).to.be.ok()
19 | expect(pool.totalCount).to.eql(1)
20 | expect(pool.idleCount).to.eql(0)
21 |
22 | // try to return it to the pool - this removes it because its broken
23 | client.release()
24 | expect(pool.totalCount).to.eql(0)
25 | expect(pool.idleCount).to.eql(0)
26 |
27 | // make sure pool still works
28 | const { rows } = await pool.query('SELECT NOW()')
29 | expect(rows).to.have.length(1)
30 | await pool.end()
31 | })
32 |
33 | it('removes a client which is ending', async () => {
34 | // make a pool w/ only 1 client
35 | const pool = new Pool({ max: 1 })
36 | expect(pool.totalCount).to.eql(0)
37 | const client = await pool.connect()
38 | expect(pool.totalCount).to.eql(1)
39 | expect(pool.idleCount).to.eql(0)
40 | // end the client gracefully (but you shouldn't do this with pooled clients)
41 | client.end()
42 |
43 | // try to return it to the pool
44 | client.release()
45 | expect(pool.totalCount).to.eql(0)
46 | expect(pool.idleCount).to.eql(0)
47 |
48 | // make sure pool still works
49 | const { rows } = await pool.query('SELECT NOW()')
50 | expect(rows).to.have.length(1)
51 | await pool.end()
52 | })
53 | })
54 |
--------------------------------------------------------------------------------
/packages/pg/test/integration/domain-tests.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const helper = require('./test-helper')
4 | const Query = helper.pg.Query
5 | const suite = new helper.Suite()
6 |
7 | const assert = require('assert')
8 | const Pool = helper.pg.Pool
9 |
10 | suite.test('no domain', function (cb) {
11 | assert(!process.domain)
12 | const pool = new Pool()
13 | pool.connect(
14 | assert.success(function (client, done) {
15 | assert(!process.domain)
16 | done()
17 | pool.end(cb)
18 | })
19 | )
20 | })
21 |
22 | suite.test('with domain', function (cb) {
23 | assert(!process.domain)
24 | const pool = new Pool()
25 | const domain = require('domain').create()
26 | domain.run(function () {
27 | const startingDomain = process.domain
28 | assert(startingDomain)
29 | pool.connect(
30 | assert.success(function (client, done) {
31 | assert(process.domain, 'no domain exists in connect callback')
32 | assert.equal(startingDomain, process.domain, 'domain was lost when checking out a client')
33 | client.query(
34 | 'SELECT NOW()',
35 | assert.success(function () {
36 | assert(process.domain, 'no domain exists in query callback')
37 | assert.equal(startingDomain, process.domain, 'domain was lost when checking out a client')
38 | done(true)
39 | process.domain.exit()
40 | pool.end(cb)
41 | })
42 | )
43 | })
44 | )
45 | })
46 | })
47 |
48 | suite.test('error on domain', function (cb) {
49 | const domain = require('domain').create()
50 | const pool = new Pool()
51 | domain.on('error', function () {
52 | pool.end(cb)
53 | })
54 | domain.run(function () {
55 | pool.connect(
56 | assert.success(function (client, done) {
57 | client.query(new Query('SELECT SLDKJFLSKDJF'))
58 | client.on('drain', done)
59 | })
60 | )
61 | })
62 | })
63 |
--------------------------------------------------------------------------------
/packages/pg/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pg",
3 | "version": "8.16.3",
4 | "description": "PostgreSQL client - pure javascript & libpq with the same API",
5 | "keywords": [
6 | "database",
7 | "libpq",
8 | "pg",
9 | "postgre",
10 | "postgres",
11 | "postgresql",
12 | "rdbms"
13 | ],
14 | "homepage": "https://github.com/brianc/node-postgres",
15 | "repository": {
16 | "type": "git",
17 | "url": "git://github.com/brianc/node-postgres.git",
18 | "directory": "packages/pg"
19 | },
20 | "author": "Brian Carlson ",
21 | "main": "./lib",
22 | "exports": {
23 | ".": {
24 | "import": "./esm/index.mjs",
25 | "require": "./lib/index.js",
26 | "default": "./lib/index.js"
27 | },
28 | "./package.json": {
29 | "default": "./package.json"
30 | },
31 | "./lib/*": "./lib/*.js",
32 | "./lib/*.js": "./lib/*.js"
33 | },
34 | "dependencies": {
35 | "pg-connection-string": "^2.9.1",
36 | "pg-pool": "^3.10.1",
37 | "pg-protocol": "^1.10.3",
38 | "pg-types": "2.2.0",
39 | "pgpass": "1.0.5"
40 | },
41 | "devDependencies": {
42 | "@cloudflare/vitest-pool-workers": "0.8.23",
43 | "@cloudflare/workers-types": "^4.20230404.0",
44 | "async": "2.6.4",
45 | "bluebird": "3.7.2",
46 | "co": "4.6.0",
47 | "pg-copy-streams": "0.3.0",
48 | "typescript": "^4.0.3",
49 | "vitest": "~3.0.9",
50 | "wrangler": "^3.x"
51 | },
52 | "optionalDependencies": {
53 | "pg-cloudflare": "^1.2.7"
54 | },
55 | "peerDependencies": {
56 | "pg-native": ">=3.0.1"
57 | },
58 | "peerDependenciesMeta": {
59 | "pg-native": {
60 | "optional": true
61 | }
62 | },
63 | "scripts": {
64 | "test": "make test-all"
65 | },
66 | "files": [
67 | "lib",
68 | "esm",
69 | "SPONSORS.md"
70 | ],
71 | "license": "MIT",
72 | "engines": {
73 | "node": ">= 16.0.0"
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/packages/pg/test/integration/gh-issues/2627-tests.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const net = require('net')
3 | const helper = require('./../test-helper')
4 | const assert = require('assert')
5 |
6 | const suite = new helper.Suite()
7 |
8 | const options = {
9 | host: 'localhost',
10 | port: Math.floor(Math.random() * 2000) + 2000,
11 | connectionTimeoutMillis: 2000,
12 | user: 'not',
13 | database: 'existing',
14 | }
15 |
16 | // This is the content of the packets sent by a MySQL server during the handshake.
17 | // Those were captured with the `mysql:8.0.33` docker image.
18 | const MySqlHandshake = Buffer.from(
19 | 'SgAAAAo4LjAuMjgAHwAAAB4dKyUJZ2p6AP///wIA/98VAAAAAAAAAAAA' +
20 | 'AAo1YiNJajgKKGkpfgBjYWNoaW5nX3NoYTJfcGFzc3dvcmQAIQAAAf+EBC' +
21 | 'MwOFMwMUdvdCBwYWNrZXRzIG91dCBvZiBvcmRlcg==',
22 | 'base64'
23 | )
24 |
25 | const serverWithInvalidResponse = (port, callback) => {
26 | const sockets = new Set()
27 |
28 | const server = net.createServer((socket) => {
29 | socket.write(MySqlHandshake)
30 |
31 | // This server sends an invalid response which should throw in pg-protocol
32 | sockets.add(socket)
33 | })
34 |
35 | let closing = false
36 | const closeServer = (done) => {
37 | if (closing) return
38 | closing = true
39 |
40 | server.close(done)
41 | for (const socket of sockets) {
42 | socket.destroy()
43 | }
44 | }
45 |
46 | server.listen(port, options.host, () => callback(closeServer))
47 | }
48 |
49 | suite.test('client should fail to connect', (done) => {
50 | serverWithInvalidResponse(options.port, (closeServer) => {
51 | const client = new helper.Client(options)
52 |
53 | client
54 | .connect()
55 | .then(() => {
56 | done(new Error('Expected client.connect() to fail'))
57 | })
58 | .catch((err) => {
59 | assert(err)
60 | assert(err.message.includes('invalid response'))
61 | closeServer(done)
62 | })
63 | })
64 | })
65 |
--------------------------------------------------------------------------------
/.devcontainer/docker-compose.yml:
--------------------------------------------------------------------------------
1 | #-------------------------------------------------------------------------------------------------------------
2 | # Copyright (c) Microsoft Corporation. All rights reserved.
3 | # Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information.
4 | #-------------------------------------------------------------------------------------------------------------
5 |
6 | version: '3.9'
7 | services:
8 | web:
9 | # Uncomment the next line to use a non-root user for all processes. You can also
10 | # simply use the "remoteUser" property in devcontainer.json if you just want VS Code
11 | # and its sub-processes (terminals, tasks, debugging) to execute as the user. On Linux,
12 | # you may need to update USER_UID and USER_GID in .devcontainer/Dockerfile to match your
13 | # user if not 1000. See https://aka.ms/vscode-remote/containers/non-root for details.
14 | # user: node
15 |
16 | build:
17 | context: .
18 | dockerfile: Dockerfile
19 |
20 | volumes:
21 | - ..:/workspace:cached
22 |
23 | environment:
24 | PGPASSWORD: pass
25 | PGUSER: user
26 | PGDATABASE: data
27 | PGHOST: db
28 | # set this to true in the development environment until I can get SSL setup on the
29 | # docker postgres instance
30 | PGTESTNOSSL: 'true'
31 |
32 | # Overrides default command so things don't shut down after the process ends.
33 | command: sleep infinity
34 |
35 | depends_on:
36 | - db
37 |
38 | links:
39 | - db:db
40 |
41 | db:
42 | image: postgres:14-alpine
43 | restart: unless-stopped
44 | ports:
45 | - 5432:5432
46 | command: postgres -c password_encryption=md5
47 | environment:
48 | POSTGRES_HOST_AUTH_METHOD: trust
49 | POSTGRES_INITDB_ARGS: "--auth-local=md5"
50 | POSTGRES_PASSWORD: pass
51 | POSTGRES_USER: user
52 | POSTGRES_DB: data
53 |
--------------------------------------------------------------------------------
/packages/pg/test/integration/gh-issues/2079-tests.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const helper = require('./../test-helper')
3 | const assert = require('assert')
4 |
5 | const suite = new helper.Suite()
6 |
7 | // makes a backend server that responds with a non 'S' ssl response buffer
8 | const makeTerminatingBackend = (byte) => {
9 | const { createServer } = require('net')
10 |
11 | const server = createServer((socket) => {
12 | // attach a listener so the socket can drain
13 | // https://www.postgresql.org/docs/9.3/protocol-message-formats.html
14 | socket.on('data', (buff) => {
15 | const code = buff.readInt32BE(4)
16 | // I don't see anything in the docs about 80877104
17 | // but libpq is sending it...
18 | if (code === 80877103 || code === 80877104) {
19 | const packet = Buffer.from(byte, 'utf-8')
20 | socket.write(packet)
21 | }
22 | })
23 | socket.on('close', () => {
24 | server.close()
25 | })
26 | })
27 |
28 | server.listen()
29 | const { port } = server.address()
30 | return port
31 | }
32 |
33 | suite.test('SSL connection error allows event loop to exit', (done) => {
34 | const port = makeTerminatingBackend('N')
35 | const client = new helper.pg.Client({ ssl: 'require', port, host: 'localhost' })
36 | // since there was a connection error the client's socket should be closed
37 | // and the event loop will have no refs and exit cleanly
38 | client.connect((err) => {
39 | assert(err instanceof Error)
40 | done()
41 | })
42 | })
43 |
44 | suite.test('Non "S" response code allows event loop to exit', (done) => {
45 | const port = makeTerminatingBackend('X')
46 | const client = new helper.pg.Client({ ssl: 'require', host: 'localhost', port })
47 | // since there was a connection error the client's socket should be closed
48 | // and the event loop will have no refs and exit cleanly
49 | client.connect((err) => {
50 | assert(err instanceof Error)
51 | done()
52 | })
53 | })
54 |
--------------------------------------------------------------------------------
/packages/pg/lib/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Client = require('./client')
4 | const defaults = require('./defaults')
5 | const Connection = require('./connection')
6 | const Result = require('./result')
7 | const utils = require('./utils')
8 | const Pool = require('pg-pool')
9 | const TypeOverrides = require('./type-overrides')
10 | const { DatabaseError } = require('pg-protocol')
11 | const { escapeIdentifier, escapeLiteral } = require('./utils')
12 |
13 | const poolFactory = (Client) => {
14 | return class BoundPool extends Pool {
15 | constructor(options) {
16 | super(options, Client)
17 | }
18 | }
19 | }
20 |
21 | const PG = function (clientConstructor) {
22 | this.defaults = defaults
23 | this.Client = clientConstructor
24 | this.Query = this.Client.Query
25 | this.Pool = poolFactory(this.Client)
26 | this._pools = []
27 | this.Connection = Connection
28 | this.types = require('pg-types')
29 | this.DatabaseError = DatabaseError
30 | this.TypeOverrides = TypeOverrides
31 | this.escapeIdentifier = escapeIdentifier
32 | this.escapeLiteral = escapeLiteral
33 | this.Result = Result
34 | this.utils = utils
35 | }
36 |
37 | if (typeof process.env.NODE_PG_FORCE_NATIVE !== 'undefined') {
38 | module.exports = new PG(require('./native'))
39 | } else {
40 | module.exports = new PG(Client)
41 |
42 | // lazy require native module...the native module may not have installed
43 | Object.defineProperty(module.exports, 'native', {
44 | configurable: true,
45 | enumerable: false,
46 | get() {
47 | let native = null
48 | try {
49 | native = new PG(require('./native'))
50 | } catch (err) {
51 | if (err.code !== 'MODULE_NOT_FOUND') {
52 | throw err
53 | }
54 | }
55 |
56 | // overwrite module.exports.native so that getter is never called again
57 | Object.defineProperty(module.exports, 'native', {
58 | value: native,
59 | })
60 |
61 | return native
62 | },
63 | })
64 | }
65 |
--------------------------------------------------------------------------------
/packages/pg-cursor/test/close.js:
--------------------------------------------------------------------------------
1 | const assert = require('assert')
2 | const Cursor = require('../')
3 | const pg = require('pg')
4 |
5 | const text = 'SELECT generate_series as num FROM generate_series(0, 50)'
6 | describe('close', function () {
7 | beforeEach(function (done) {
8 | const client = (this.client = new pg.Client())
9 | client.connect(done)
10 | })
11 |
12 | this.afterEach(function (done) {
13 | this.client.end(done)
14 | })
15 |
16 | it('can close a finished cursor without a callback', function (done) {
17 | const cursor = new Cursor(text)
18 | this.client.query(cursor)
19 | this.client.query('SELECT NOW()', done)
20 | cursor.read(100, function (err) {
21 | assert.ifError(err)
22 | cursor.close()
23 | })
24 | })
25 |
26 | it('can close a finished cursor a promise', function (done) {
27 | const cursor = new Cursor(text)
28 | this.client.query(cursor)
29 | cursor.read(100, (err) => {
30 | assert.ifError(err)
31 | cursor.close().then(() => {
32 | this.client.query('SELECT NOW()', done)
33 | })
34 | })
35 | })
36 |
37 | it('closes cursor early', function (done) {
38 | const cursor = new Cursor(text)
39 | this.client.query(cursor)
40 | this.client.query('SELECT NOW()', done)
41 | cursor.read(25, function (err) {
42 | assert.ifError(err)
43 | cursor.close()
44 | })
45 | })
46 |
47 | it('works with callback style', function (done) {
48 | const cursor = new Cursor(text)
49 | const client = this.client
50 | client.query(cursor)
51 | cursor.read(25, function (err, rows) {
52 | assert.ifError(err)
53 | assert.strictEqual(rows.length, 25)
54 | cursor.close(function (err) {
55 | assert.ifError(err)
56 | client.query('SELECT NOW()', done)
57 | })
58 | })
59 | })
60 |
61 | it('is a no-op to "close" the cursor before submitting it', function (done) {
62 | const cursor = new Cursor(text)
63 | cursor.close(done)
64 | })
65 | })
66 |
--------------------------------------------------------------------------------