├── .gitattributes ├── docs ├── .gitignore ├── public │ └── favicon.ico ├── pages │ ├── _meta.json │ ├── _app.js │ ├── apis │ │ ├── types.mdx │ │ ├── _meta.json │ │ └── utilities.mdx │ ├── guides │ │ └── _meta.json │ └── features │ │ ├── _meta.json │ │ ├── esm.mdx │ │ ├── callbacks.mdx │ │ ├── transactions.mdx │ │ └── native.mdx ├── components │ ├── info.tsx │ ├── alert.tsx │ └── logo.tsx ├── next.config.js ├── package.json └── README.md ├── packages ├── pg-pool │ ├── test │ │ ├── timeout.js │ │ ├── setup.js │ │ ├── logging.js │ │ ├── verify.js │ │ ├── submittable.js │ │ ├── idle-timeout-exit.js │ │ ├── connection-strings.js │ │ ├── ending.js │ │ ├── lifetime-timeout.js │ │ └── releasing-clients.js │ ├── esm │ │ └── index.mjs │ ├── LICENSE │ └── package.json ├── pg-connection-string │ ├── test │ │ ├── example.ca │ │ ├── example.key │ │ └── example.cert │ ├── .mocharc.json │ ├── .coveralls.yml │ ├── esm │ │ └── index.mjs │ ├── tsconfig.json │ ├── .gitignore │ ├── index.d.ts │ ├── LICENSE │ └── package.json ├── pg-native │ ├── test │ │ ├── mocha.opts │ │ ├── version.js │ │ ├── empty-query.js │ │ ├── connection-errors.js │ │ ├── connection.js │ │ ├── huge-query.js │ │ ├── custom-types.js │ │ ├── many-errors.js │ │ ├── load.js │ │ ├── multiple-statement-results.js │ │ ├── array-mode.js │ │ ├── cancel.js │ │ ├── index.js │ │ ├── copy-to.js │ │ ├── domains.js │ │ ├── many-connections.js │ │ ├── multiple-queries.js │ │ ├── prepare.js │ │ ├── copy-from.js │ │ └── notify.js │ ├── esm │ │ └── index.mjs │ ├── bench │ │ ├── index.js │ │ └── leaks.js │ └── package.json ├── pg-bundler-test │ ├── src │ │ └── index.mjs │ ├── webpack-empty.config.mjs │ ├── esbuild-empty.config.mjs │ ├── esbuild-cloudflare.config.mjs │ ├── vite-empty.config.mjs │ ├── rollup-empty.config.mjs │ ├── webpack-cloudflare.config.mjs │ ├── rollup-cloudflare.config.mjs │ ├── vite-cloudflare.config.mjs │ └── package.json ├── pg │ ├── test │ │ ├── unit │ │ │ ├── client │ │ │ │ ├── pgpass.file │ │ │ │ ├── notification-tests.js │ │ │ │ ├── early-disconnect-tests.js │ │ │ │ ├── test-helper.js │ │ │ │ ├── set-keepalives-tests.js │ │ │ │ ├── query-queue-tests.js │ │ │ │ ├── cleartext-password-tests.js │ │ │ │ ├── stream-and-query-error-interaction-tests.js │ │ │ │ ├── md5-password-tests.js │ │ │ │ ├── result-metadata-tests.js │ │ │ │ └── throw-in-type-parser-tests.js │ │ │ ├── connection │ │ │ │ └── test-helper.js │ │ │ ├── connection-pool │ │ │ │ └── configuration-tests.js │ │ │ └── test-helper.js │ │ ├── integration │ │ │ ├── client │ │ │ │ ├── test-helper.js │ │ │ │ ├── quick-disconnect-tests.js │ │ │ │ ├── field-name-escape-tests.js │ │ │ │ ├── connection-parameter-tests.js │ │ │ │ ├── ssl-tests.js │ │ │ │ ├── empty-query-tests.js │ │ │ │ ├── query-column-names-tests.js │ │ │ │ ├── huge-numeric-tests.js │ │ │ │ ├── no-row-result-tests.js │ │ │ │ ├── no-data-tests.js │ │ │ │ ├── results-as-array-tests.js │ │ │ │ ├── timezone-tests.js │ │ │ │ ├── promise-api-tests.js │ │ │ │ ├── json-type-parsing-tests.js │ │ │ │ ├── parse-int-8-tests.js │ │ │ │ ├── type-parser-override-tests.js │ │ │ │ ├── row-description-on-results-tests.js │ │ │ │ ├── result-metadata-tests.js │ │ │ │ ├── custom-types-tests.js │ │ │ │ ├── query-as-promise-tests.js │ │ │ │ └── async-stack-trace-tests.js │ │ │ ├── connection-pool │ │ │ │ ├── test-helper.js │ │ │ │ ├── native-instance-tests.js │ │ │ │ ├── idle-timeout-tests.js │ │ │ │ ├── tls-tests.js │ │ │ │ ├── yield-support-tests.js │ │ │ │ └── connection-pool-size-tests.js │ │ │ ├── gh-issues │ │ │ │ ├── 1992-tests.js │ │ │ │ ├── 882-tests.js │ │ │ │ ├── 2108-tests.js │ │ │ │ ├── 787-tests.js │ │ │ │ ├── 2416-tests.js │ │ │ │ ├── 2307-tests.js │ │ │ │ ├── 1542-tests.js │ │ │ │ ├── 1105-tests.js │ │ │ │ ├── 507-tests.js │ │ │ │ ├── 3487-tests.js │ │ │ │ ├── 199-tests.js │ │ │ │ ├── 2056-tests.js │ │ │ │ ├── 675-tests.js │ │ │ │ ├── 699-tests.js │ │ │ │ ├── 2862-tests.js │ │ │ │ ├── 3062-tests.js │ │ │ │ ├── 1382-tests.js │ │ │ │ ├── 130-tests.js │ │ │ │ ├── 981-tests.js │ │ │ │ ├── 1854-tests.js │ │ │ │ ├── 2085-tests.js │ │ │ │ ├── 2064-tests.js │ │ │ │ ├── 131-tests.js │ │ │ │ ├── 2556-tests.js │ │ │ │ ├── 2716-tests.js │ │ │ │ ├── 2627-tests.js │ │ │ │ └── 2079-tests.js │ │ │ ├── test-helper.js │ │ │ └── domain-tests.js │ │ ├── tls │ │ │ ├── test-client.key │ │ │ ├── test-server.key │ │ │ ├── test-client-ca.key │ │ │ ├── test-server-ca.key │ │ │ ├── test-client.crt │ │ │ ├── test-server.crt │ │ │ ├── test-client-ca.crt │ │ │ ├── test-server-ca.crt │ │ │ └── GNUmakefile │ │ ├── vitest.config.mts │ │ ├── cloudflare │ │ │ └── vitest-cf.test.ts │ │ ├── cli.js │ │ ├── native │ │ │ ├── native-vs-js-error-tests.js │ │ │ ├── callback-api-tests.js │ │ │ ├── stress-tests.js │ │ │ └── native-connection-string-tests.js │ │ ├── wrangler.jsonc │ │ └── buffer-list.js │ ├── lib │ │ ├── native │ │ │ └── index.js │ │ ├── crypto │ │ │ ├── utils.js │ │ │ └── utils-legacy.js │ │ ├── type-overrides.js │ │ └── index.js │ ├── script │ │ ├── dump-db-types.js │ │ └── create-test-tables.js │ ├── esm │ │ └── index.mjs │ └── package.json ├── pg-protocol │ ├── src │ │ ├── types │ │ │ └── chunky.d.ts │ │ ├── index.ts │ │ ├── b.ts │ │ └── buffer-reader.ts │ ├── README.md │ ├── esm │ │ └── index.js │ ├── tsconfig.json │ └── package.json ├── pg-cursor │ ├── test │ │ ├── mocha.opts │ │ ├── no-data-handling.js │ │ ├── query-config.js │ │ ├── promises.js │ │ ├── transactions.js │ │ └── close.js │ ├── esm │ │ └── index.mjs │ ├── package.json │ └── README.md ├── pg-cloudflare │ ├── esm │ │ └── index.mjs │ ├── src │ │ ├── empty.ts │ │ └── types.d.ts │ ├── tsconfig.json │ └── package.json ├── pg-query-stream │ ├── esm │ │ └── index.mjs │ ├── test │ │ ├── helper.ts │ │ ├── stream-tester.ts │ │ ├── instant.ts │ │ ├── empty-query.ts │ │ ├── slow-reader.ts │ │ ├── config.ts │ │ ├── client-options.ts │ │ ├── concat.ts │ │ ├── stream-tester-timestamp.ts │ │ ├── pauses.ts │ │ ├── issue-3.ts │ │ ├── fast-reader.ts │ │ └── passing-options.ts │ ├── tsconfig.json │ ├── LICENSE │ └── package.json └── pg-esm-test │ ├── pg-pool.test.js │ ├── pg-native.test.js │ ├── pg-cursor.test.js │ ├── README.md │ ├── pg-cloudflare.test.js │ ├── pg-query-stream.test.js │ ├── pg-connection-string.test.js │ ├── pg-protocol.test.js │ ├── package.json │ ├── common-js-imports.test.cjs │ └── pg.test.js ├── .eslintignore ├── .yarnrc ├── .github ├── CODEOWNERS ├── FUNDING.yml └── dependabot.yaml ├── tea.yaml ├── .gitignore ├── lerna.json ├── tsconfig.json ├── .devcontainer ├── devcontainer.json └── docker-compose.yml ├── .eslintrc ├── LICENSE ├── LOCAL_DEV.md └── package.json /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | .next 2 | out 3 | -------------------------------------------------------------------------------- /packages/pg-pool/test/timeout.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /packages/*/dist/ 2 | -------------------------------------------------------------------------------- /.yarnrc: -------------------------------------------------------------------------------- 1 | --install.ignore-engines true -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | /packages/pg-connection-string @hjr3 2 | -------------------------------------------------------------------------------- /packages/pg-connection-string/test/example.ca: -------------------------------------------------------------------------------- 1 | example ca 2 | -------------------------------------------------------------------------------- /packages/pg-connection-string/test/example.key: -------------------------------------------------------------------------------- 1 | example key 2 | -------------------------------------------------------------------------------- /packages/pg-native/test/mocha.opts: -------------------------------------------------------------------------------- 1 | --bail 2 | --no-exit 3 | -------------------------------------------------------------------------------- /packages/pg-bundler-test/src/index.mjs: -------------------------------------------------------------------------------- 1 | import 'pg-cloudflare' 2 | -------------------------------------------------------------------------------- /packages/pg-connection-string/test/example.cert: -------------------------------------------------------------------------------- 1 | example cert 2 | -------------------------------------------------------------------------------- /packages/pg/test/unit/client/pgpass.file: -------------------------------------------------------------------------------- 1 | foo:5432:bar:baz:quz 2 | -------------------------------------------------------------------------------- /packages/pg-protocol/src/types/chunky.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'chunky' 2 | -------------------------------------------------------------------------------- /packages/pg-cursor/test/mocha.opts: -------------------------------------------------------------------------------- 1 | --reporter spec 2 | --no-exit 3 | --bail 4 | -------------------------------------------------------------------------------- /packages/pg/lib/native/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | module.exports = require('./client') 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [brianc] 4 | -------------------------------------------------------------------------------- /docs/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brianc/node-postgres/HEAD/docs/public/favicon.ico -------------------------------------------------------------------------------- /packages/pg-connection-string/.mocharc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": ["js", "ts"], 3 | "require": "tsx" 4 | } 5 | -------------------------------------------------------------------------------- /packages/pg/test/unit/connection/test-helper.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | module.exports = require('../test-helper') 3 | -------------------------------------------------------------------------------- /docs/pages/_meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "index": "Welcome", 3 | "announcements": "Announcements", 4 | "apis": "API" 5 | } 6 | -------------------------------------------------------------------------------- /packages/pg-connection-string/.coveralls.yml: -------------------------------------------------------------------------------- 1 | service_name: travis-pro 2 | repo_token: 5F6dODinz9L9uFR6HatKmtsYDoV1A5S2N 3 | -------------------------------------------------------------------------------- /packages/pg-cloudflare/esm/index.mjs: -------------------------------------------------------------------------------- 1 | import cf from '../dist/index.js' 2 | 3 | export const CloudflareSocket = cf.CloudflareSocket 4 | -------------------------------------------------------------------------------- /packages/pg/test/integration/client/test-helper.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const helper = require('./../test-helper') 3 | 4 | module.exports = helper 5 | -------------------------------------------------------------------------------- /.github/dependabot.yaml: -------------------------------------------------------------------------------- 1 | 2 | version: 2 3 | updates: 4 | - package-ecosystem: "npm" 5 | directory: "/" 6 | schedule: 7 | interval: "monthly" -------------------------------------------------------------------------------- /packages/pg/test/integration/connection-pool/test-helper.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const helper = require('./../test-helper') 3 | 4 | module.exports = helper 5 | -------------------------------------------------------------------------------- /tea.yaml: -------------------------------------------------------------------------------- 1 | # https://tea.xyz/what-is-this-file 2 | --- 3 | version: 1.0.0 4 | codeOwners: 5 | - '0x7e65e980B1409f21E2eb2FF341B2235A8B615122' 6 | quorum: 1 7 | -------------------------------------------------------------------------------- /docs/pages/_app.js: -------------------------------------------------------------------------------- 1 | import 'nextra-theme-docs/style.css' 2 | 3 | export default function Nextra({ Component, pageProps }) { 4 | return 5 | } 6 | -------------------------------------------------------------------------------- /packages/pg-pool/esm/index.mjs: -------------------------------------------------------------------------------- 1 | // ESM wrapper for pg-pool 2 | import Pool from '../index.js' 3 | 4 | // Export as default only to match CJS module 5 | export default Pool 6 | -------------------------------------------------------------------------------- /docs/components/info.tsx: -------------------------------------------------------------------------------- 1 | import { Callout } from 'nextra-theme-docs' 2 | 3 | export const Info = ({ children }) => { 4 | return {children} 5 | } 6 | -------------------------------------------------------------------------------- /packages/pg-cursor/esm/index.mjs: -------------------------------------------------------------------------------- 1 | // ESM wrapper for pg-cursor 2 | import Cursor from '../index.js' 3 | 4 | // Export as default only to match CJS module 5 | export default Cursor 6 | -------------------------------------------------------------------------------- /packages/pg-native/esm/index.mjs: -------------------------------------------------------------------------------- 1 | // ESM wrapper for pg-native 2 | import Client from '../index.js' 3 | 4 | // Export as default only to match CJS module 5 | export default Client 6 | -------------------------------------------------------------------------------- /packages/pg-cloudflare/src/empty.ts: -------------------------------------------------------------------------------- 1 | // This is an empty module that is served up when outside of a workerd environment 2 | // See the `exports` field in package.json 3 | export default {} 4 | -------------------------------------------------------------------------------- /packages/pg-protocol/README.md: -------------------------------------------------------------------------------- 1 | # pg-protocol 2 | 3 | Low level postgres wire protocol parser and serializer written in Typescript. Used by node-postgres. Needs more documentation. :smile: 4 | -------------------------------------------------------------------------------- /packages/pg-bundler-test/webpack-empty.config.mjs: -------------------------------------------------------------------------------- 1 | export default { 2 | mode: 'production', 3 | entry: './src/index.mjs', 4 | output: { 5 | filename: 'webpack-empty.js', 6 | }, 7 | } 8 | -------------------------------------------------------------------------------- /docs/pages/apis/types.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Types 3 | slug: /apis/types 4 | --- 5 | 6 | These docs are incomplete, for now please reference [pg-types docs](https://github.com/brianc/node-pg-types). 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | build/ 3 | .lock-wscript 4 | *.log 5 | node_modules/ 6 | package-lock.json 7 | *.swp 8 | dist 9 | .DS_Store 10 | /.eslintcache 11 | .vscode/ 12 | manually-test-on-heroku.js 13 | -------------------------------------------------------------------------------- /docs/pages/apis/_meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "client": "pg.Client", 3 | "pool": "pg.Pool", 4 | "result": "pg.Result", 5 | "types": "pg.Types", 6 | "cursor": "Cursor", 7 | "utilities": "Utilities" 8 | } 9 | -------------------------------------------------------------------------------- /docs/pages/guides/_meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "project-structure": "Suggested Code Structure", 3 | "async-express": "Express with Async/Await", 4 | "pool-sizing": "Pool Sizing", 5 | "upgrading": "Upgrading" 6 | } 7 | -------------------------------------------------------------------------------- /packages/pg-query-stream/esm/index.mjs: -------------------------------------------------------------------------------- 1 | // ESM wrapper for pg-query-stream 2 | import QueryStream from '../dist/index.js' 3 | 4 | // Export as default only to match CJS module 5 | export default QueryStream 6 | -------------------------------------------------------------------------------- /packages/pg-bundler-test/esbuild-empty.config.mjs: -------------------------------------------------------------------------------- 1 | import * as esbuild from 'esbuild' 2 | 3 | await esbuild.build({ 4 | entryPoints: ['./src/index.mjs'], 5 | bundle: true, 6 | outfile: './dist/esbuild-empty.js', 7 | }) 8 | -------------------------------------------------------------------------------- /docs/components/alert.tsx: -------------------------------------------------------------------------------- 1 | import { Callout } from 'nextra-theme-docs' 2 | 3 | export const Alert = ({ children }) => { 4 | return ( 5 | 6 | {children} 7 | 8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /packages/pg/test/integration/client/quick-disconnect-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | // test for issue #320 3 | // 4 | const helper = require('./test-helper') 5 | 6 | const client = new helper.pg.Client(helper.config) 7 | client.connect() 8 | client.end() 9 | -------------------------------------------------------------------------------- /packages/pg-pool/test/setup.js: -------------------------------------------------------------------------------- 1 | const crash = (reason) => { 2 | process.on(reason, (err) => { 3 | console.error(reason, err.stack) 4 | process.exit(-1) 5 | }) 6 | } 7 | 8 | crash('unhandledRejection') 9 | crash('uncaughtError') 10 | crash('warning') 11 | -------------------------------------------------------------------------------- /packages/pg-bundler-test/esbuild-cloudflare.config.mjs: -------------------------------------------------------------------------------- 1 | import * as esbuild from 'esbuild' 2 | 3 | await esbuild.build({ 4 | entryPoints: ['./src/index.mjs'], 5 | bundle: true, 6 | outfile: './dist/esbuild-cloudflare.js', 7 | conditions: ['import', 'workerd'], 8 | }) 9 | -------------------------------------------------------------------------------- /docs/next.config.js: -------------------------------------------------------------------------------- 1 | // next.config.js 2 | const withNextra = require('nextra')({ 3 | theme: 'nextra-theme-docs', 4 | themeConfig: './theme.config.js', 5 | // optional: add `unstable_staticImage: true` to enable Nextra's auto image import 6 | }) 7 | 8 | module.exports = withNextra() 9 | -------------------------------------------------------------------------------- /packages/pg-esm-test/pg-pool.test.js: -------------------------------------------------------------------------------- 1 | import assert from 'node:assert' 2 | import { describe, it } from 'node:test' 3 | import Pool from 'pg-pool' 4 | 5 | describe('pg-pool', () => { 6 | it('should export Pool constructor', () => { 7 | assert.ok(new Pool()) 8 | }) 9 | }) 10 | -------------------------------------------------------------------------------- /docs/components/logo.tsx: -------------------------------------------------------------------------------- 1 | type Props = { 2 | src: string 3 | alt?: string 4 | } 5 | 6 | export function Logo(props: Props) { 7 | const alt = props.alt || 'Logo' 8 | return {alt} 9 | } 10 | -------------------------------------------------------------------------------- /packages/pg/test/tls/test-client.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgL9jW07+fXy/74Ub3 3 | 579RXm0Xpo7lnNnQleSzkTEXCrmhRANCAARY4j5AgTLi/O/UTB8l1mX+nD9u3SW9 4 | RwN1mekcqEZqCpOPMsQEQ/HLxaKnoSTD6w/GNqrBnHlbMGPwEdKvV96b 5 | -----END PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /packages/pg/test/tls/test-server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgBoW9xxWBH2tHiPFk 3 | 9ajPALHyw0lHAY1DF8WvHQNodx2hRANCAATgzCLp0d5ZFnZBTX1rnKSrqAl8hUkM 4 | Romo2VZeT/n+VqIbLxJwFXRgCW+T3Gx8DJRgPFxBONCR4LRtvOF0Q+M2 5 | -----END PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": ["packages/*"], 3 | "npmClient": "yarn", 4 | "useWorkspaces": true, 5 | "version": "independent", 6 | "command": { 7 | "version": { 8 | "allowBranch": "master" 9 | } 10 | }, 11 | "ignoreChanges": ["**/*.md", "**/test/**"] 12 | } 13 | -------------------------------------------------------------------------------- /packages/pg/test/tls/test-client-ca.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgKsipfQWM+41FriF7 3 | kRxVaiNi8qY1fzLx6Dp/gUQQPG6hRANCAASI/EfxPq0P54VKPkTUOTwBH1iuYbnL 4 | pd4kAGjb1E334/p9CEBbDREVSqDjYjWswFybxKIFooKXtMpEMJfymJAU 5 | -----END PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /packages/pg/test/tls/test-server-ca.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgyUd4vHDNrEFzfttP 3 | z+AFp3Tbyui+b3i9YDW7VqpMOIKhRANCAAT/jGRhFiZu96o0hfgIkep4PusTwI6P 4 | 1ASFh8LgnUu2bMcIlYakQK0ap2XvCaSl9675+Lu9yNZaSZVA5LpFICXt 5 | -----END PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /docs/pages/features/_meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "connecting": "Connecting", 3 | "queries": "Queries", 4 | "pooling": "Pooling", 5 | "transactions": "Transactions", 6 | "types": "Data Types", 7 | "ssl": "SSL", 8 | "native": "Native", 9 | "esm": "ESM", 10 | "callbacks": "Callbacks" 11 | } 12 | -------------------------------------------------------------------------------- /packages/pg-esm-test/pg-native.test.js: -------------------------------------------------------------------------------- 1 | import assert from 'node:assert' 2 | import { describe, it } from 'node:test' 3 | import Client from 'pg-native' 4 | 5 | describe('pg-native', () => { 6 | it('should export Client constructor', () => { 7 | assert.ok(new Client()) 8 | }) 9 | }) 10 | -------------------------------------------------------------------------------- /packages/pg-esm-test/pg-cursor.test.js: -------------------------------------------------------------------------------- 1 | import assert from 'node:assert' 2 | import { describe, it } from 'node:test' 3 | import Cursor from 'pg-cursor' 4 | 5 | describe('pg-cursor', () => { 6 | it('should export Cursor constructor as default', () => { 7 | assert.ok(new Cursor()) 8 | }) 9 | }) 10 | -------------------------------------------------------------------------------- /packages/pg-bundler-test/vite-empty.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | 3 | export default defineConfig({ 4 | build: { 5 | emptyOutDir: false, 6 | lib: { 7 | entry: './src/index.mjs', 8 | fileName: 'vite-empty', 9 | formats: ['es'], 10 | }, 11 | }, 12 | }) 13 | -------------------------------------------------------------------------------- /packages/pg-esm-test/README.md: -------------------------------------------------------------------------------- 1 | This is an internal package for node-postgres used to test esm & cjs module export compatibility. 2 | 3 | The only thing you really need to do is `yarn && yarn test` from the root of the project & these tests will run as well as all the other tests. So, basically, you can ignore this. 😄 4 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "incremental": true, 5 | "composite": true 6 | }, 7 | "include": [], 8 | "references": [ 9 | {"path": "./packages/pg-cloudflare"}, 10 | {"path": "./packages/pg-query-stream"}, 11 | {"path": "./packages/pg-protocol"} 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /packages/pg-esm-test/pg-cloudflare.test.js: -------------------------------------------------------------------------------- 1 | import assert from 'node:assert' 2 | import { describe, it } from 'node:test' 3 | import { CloudflareSocket } from 'pg-cloudflare' 4 | 5 | describe('pg-cloudflare', () => { 6 | it('should export CloudflareSocket constructor', () => { 7 | assert.ok(new CloudflareSocket()) 8 | }) 9 | }) 10 | -------------------------------------------------------------------------------- /packages/pg-esm-test/pg-query-stream.test.js: -------------------------------------------------------------------------------- 1 | import assert from 'node:assert' 2 | import { describe, it } from 'node:test' 3 | import QueryStream from 'pg-query-stream' 4 | 5 | describe('pg-query-stream', () => { 6 | it('should export QueryStream constructor as default', () => { 7 | assert.ok(new QueryStream()) 8 | }) 9 | }) 10 | -------------------------------------------------------------------------------- /packages/pg/test/vitest.config.mts: -------------------------------------------------------------------------------- 1 | import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config' 2 | 3 | export default defineWorkersConfig({ 4 | test: { 5 | watch: false, 6 | poolOptions: { 7 | workers: { 8 | wrangler: { configPath: './wrangler.jsonc' }, 9 | }, 10 | }, 11 | }, 12 | }) 13 | -------------------------------------------------------------------------------- /packages/pg/test/integration/client/field-name-escape-tests.js: -------------------------------------------------------------------------------- 1 | const pg = require('./test-helper').pg 2 | 3 | const sql = 'SELECT 1 AS "\\\'/*", 2 AS "\\\'*/\n + process.exit(-1)] = null;\n//"' 4 | 5 | const client = new pg.Client() 6 | client.connect() 7 | client.query(sql, function (err, res) { 8 | if (err) throw err 9 | client.end() 10 | }) 11 | -------------------------------------------------------------------------------- /packages/pg/test/integration/gh-issues/1992-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 | suite.test('Native should not be enumerable', () => { 8 | const keys = Object.keys(helper.pg) 9 | assert.strictEqual(keys.indexOf('native'), -1) 10 | }) 11 | -------------------------------------------------------------------------------- /packages/pg/test/integration/gh-issues/882-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | // client should not hang on an empty query 3 | const helper = require('../test-helper') 4 | const client = helper.client() 5 | client.query({ name: 'foo1', text: null }) 6 | client.query({ name: 'foo2', text: ' ' }) 7 | client.query({ name: 'foo3', text: '' }, function (err, res) { 8 | client.end() 9 | }) 10 | -------------------------------------------------------------------------------- /packages/pg-native/test/version.js: -------------------------------------------------------------------------------- 1 | const Client = require('../') 2 | const assert = require('assert') 3 | const semver = require('semver') 4 | 5 | describe('version', function () { 6 | it('is exported', function () { 7 | assert(Client.version) 8 | assert.equal(require('../package.json').version, Client.version) 9 | assert(semver.gt(Client.version, '1.4.0')) 10 | }) 11 | }) 12 | -------------------------------------------------------------------------------- /packages/pg-connection-string/esm/index.mjs: -------------------------------------------------------------------------------- 1 | // ESM wrapper for pg-connection-string 2 | import connectionString from '../index.js' 3 | 4 | // Re-export the parse function 5 | export default connectionString.parse 6 | export const parse = connectionString.parse 7 | export const toClientConfig = connectionString.toClientConfig 8 | export const parseIntoClientConfig = connectionString.parseIntoClientConfig 9 | -------------------------------------------------------------------------------- /packages/pg-protocol/esm/index.js: -------------------------------------------------------------------------------- 1 | // ESM wrapper for pg-protocol 2 | import * as protocol from '../dist/index.js' 3 | 4 | // Re-export all the properties 5 | export const DatabaseError = protocol.DatabaseError 6 | export const SASL = protocol.SASL 7 | export const serialize = protocol.serialize 8 | export const parse = protocol.parse 9 | 10 | // Re-export the default 11 | export default protocol 12 | -------------------------------------------------------------------------------- /packages/pg/lib/crypto/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const useLegacyCrypto = parseInt(process.versions && process.versions.node && process.versions.node.split('.')[0]) < 15 4 | if (useLegacyCrypto) { 5 | // We are on an old version of Node.js that requires legacy crypto utilities. 6 | module.exports = require('./utils-legacy') 7 | } else { 8 | module.exports = require('./utils-webcrypto') 9 | } 10 | -------------------------------------------------------------------------------- /packages/pg/test/cloudflare/vitest-cf.test.ts: -------------------------------------------------------------------------------- 1 | import { Pool } from 'pg' 2 | import { test } from 'vitest' 3 | import assert from 'node:assert' 4 | import args from '../cli' 5 | 6 | test('default', async () => { 7 | const pool = new Pool(args) 8 | const result = await pool.query('SELECT $1::text as name', ['cloudflare']) 9 | assert(result.rows[0].name === 'cloudflare') 10 | pool.end() 11 | }) 12 | -------------------------------------------------------------------------------- /packages/pg-bundler-test/rollup-empty.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'rollup' 2 | import { nodeResolve } from '@rollup/plugin-node-resolve' 3 | import commonjs from '@rollup/plugin-commonjs' 4 | 5 | export default defineConfig({ 6 | input: './src/index.mjs', 7 | output: { 8 | file: 'dist/rollup-empty.js', 9 | format: 'es', 10 | }, 11 | plugins: [nodeResolve(), commonjs()], 12 | }) 13 | -------------------------------------------------------------------------------- /packages/pg-query-stream/test/helper.ts: -------------------------------------------------------------------------------- 1 | import pg from 'pg' 2 | 3 | export default function (name, cb) { 4 | describe(name, function () { 5 | const client = new pg.Client() 6 | 7 | before(function (done) { 8 | client.connect(done) 9 | }) 10 | 11 | cb(client) 12 | 13 | after(function (done) { 14 | client.end() 15 | client.on('end', done) 16 | }) 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /packages/pg/test/unit/client/notification-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const helper = require('./test-helper') 3 | const assert = require('assert') 4 | const suite = new helper.Suite() 5 | 6 | suite.test('passes connection notification', function () { 7 | const client = helper.client() 8 | assert.emits(client, 'notice', function (msg) { 9 | assert.equal(msg, 'HAY!!') 10 | }) 11 | client.connection.emit('notice', 'HAY!!') 12 | }) 13 | -------------------------------------------------------------------------------- /packages/pg/test/integration/gh-issues/2108-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const helper = require('./../test-helper') 3 | const suite = new helper.Suite() 4 | 5 | suite.test('Closing an unconnected client calls callback', (done) => { 6 | const client = new helper.pg.Client() 7 | client.end(done) 8 | }) 9 | 10 | suite.testAsync('Closing an unconnected client resolves promise', () => { 11 | const client = new helper.pg.Client() 12 | return client.end() 13 | }) 14 | -------------------------------------------------------------------------------- /packages/pg-bundler-test/webpack-cloudflare.config.mjs: -------------------------------------------------------------------------------- 1 | import webpack from 'webpack' 2 | 3 | export default { 4 | mode: 'production', 5 | entry: './src/index.mjs', 6 | output: { 7 | filename: 'webpack-cloudflare.js', 8 | }, 9 | resolve: { conditionNames: ['import', 'workerd'] }, 10 | plugins: [ 11 | // ignore cloudflare:sockets imports 12 | new webpack.IgnorePlugin({ 13 | resourceRegExp: /^cloudflare:sockets$/, 14 | }), 15 | ], 16 | } 17 | -------------------------------------------------------------------------------- /packages/pg/test/unit/connection-pool/configuration-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const assert = require('assert') 4 | const helper = require('../test-helper') 5 | const suite = new helper.Suite() 6 | 7 | suite.test('pool with copied settings includes password', () => { 8 | const original = new helper.pg.Pool({ 9 | password: 'original', 10 | }) 11 | 12 | const copy = new helper.pg.Pool(original.options) 13 | 14 | assert.equal(copy.options.password, 'original') 15 | }) 16 | -------------------------------------------------------------------------------- /packages/pg/test/integration/gh-issues/787-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const helper = require('../test-helper') 3 | const pool = new helper.pg.Pool() 4 | 5 | pool.connect(function (err, client) { 6 | const q = { 7 | name: 'This is a super long query name just so I can test that an error message is properly spit out to console.error without throwing an exception or anything', 8 | text: 'SELECT NOW()', 9 | } 10 | client.query(q, function () { 11 | client.end() 12 | }) 13 | }) 14 | -------------------------------------------------------------------------------- /packages/pg-connection-string/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "esModuleInterop": true, 5 | "allowSyntheticDefaultImports": true, 6 | "strict": true, 7 | "target": "es6", 8 | "noImplicitAny": true, 9 | "moduleResolution": "node", 10 | "sourceMap": true, 11 | "outDir": "dist", 12 | "incremental": true, 13 | "baseUrl": ".", 14 | "declaration": true 15 | }, 16 | "include": [ 17 | "test/**/*" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docs", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "next.config.js", 6 | "scripts": { 7 | "start": "next dev", 8 | "build": "next build && next export" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "next": "^12.3.1", 15 | "nextra": "2.0.0-beta.29", 16 | "nextra-theme-docs": "2.0.0-beta.29", 17 | "react": "^17.0.1", 18 | "react-dom": "^17.0.1" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/pg-query-stream/test/stream-tester.ts: -------------------------------------------------------------------------------- 1 | import spec from 'stream-spec' 2 | import helper from './helper' 3 | import QueryStream from '../src' 4 | 5 | helper('stream tester', function (client) { 6 | it('passes stream spec', function (done) { 7 | const stream = new QueryStream('SELECT * FROM generate_series(0, 200) num', []) 8 | const query = client.query(stream) 9 | spec(query).readable().pausable({ strict: true }).validateOnExit() 10 | stream.on('end', done) 11 | }) 12 | }) 13 | -------------------------------------------------------------------------------- /packages/pg/test/integration/connection-pool/native-instance-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const helper = require('./../test-helper') 3 | const pg = helper.pg 4 | const native = helper.args.native 5 | const assert = require('assert') 6 | 7 | const pool = new pg.Pool() 8 | 9 | pool.connect( 10 | assert.calls(function (err, client, done) { 11 | if (native) { 12 | assert(client.native) 13 | } else { 14 | assert(!client.native) 15 | } 16 | done() 17 | pool.end() 18 | }) 19 | ) 20 | -------------------------------------------------------------------------------- /packages/pg-protocol/src/index.ts: -------------------------------------------------------------------------------- 1 | import { DatabaseError } from './messages' 2 | import { serialize } from './serializer' 3 | import { Parser, MessageCallback } from './parser' 4 | 5 | export function parse(stream: NodeJS.ReadableStream, callback: MessageCallback): Promise { 6 | const parser = new Parser() 7 | stream.on('data', (buffer: Buffer) => parser.parse(buffer, callback)) 8 | return new Promise((resolve) => stream.on('end', () => resolve())) 9 | } 10 | 11 | export { serialize, DatabaseError } 12 | -------------------------------------------------------------------------------- /packages/pg/test/tls/test-client.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBITCByAIBATAKBggqhkjOPQQDAjAnMSUwIwYDVQQDDBxub2RlLXBvc3RncmVz 3 | IHRlc3QgY2xpZW50IENBMB4XDTIwMTAzMTE5MjU0N1oXDTMwMTAyOTE5MjU0N1ow 4 | EzERMA8GA1UEAwwIcG9zdGdyZXMwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARY 5 | 4j5AgTLi/O/UTB8l1mX+nD9u3SW9RwN1mekcqEZqCpOPMsQEQ/HLxaKnoSTD6w/G 6 | NqrBnHlbMGPwEdKvV96bMAoGCCqGSM49BAMCA0gAMEUCIQDzfjm+BzmjrsIO4QRu 7 | Et0ShHBK3Kley3oqnzoJHCUSmAIgdF5gELQ5mlJVX3bAI8h1cKiC/L6awwg7eBDU 8 | S1gBTaI= 9 | -----END CERTIFICATE----- 10 | -------------------------------------------------------------------------------- /packages/pg/test/tls/test-server.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBITCByQIBATAKBggqhkjOPQQDAjAnMSUwIwYDVQQDDBxub2RlLXBvc3RncmVz 3 | IHRlc3Qgc2VydmVyIENBMB4XDTIwMTAzMTE5MjU0N1oXDTMwMTAyOTE5MjU0N1ow 4 | FDESMBAGA1UEAwwJbG9jYWxob3N0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE 5 | 4Mwi6dHeWRZ2QU19a5ykq6gJfIVJDEaJqNlWXk/5/laiGy8ScBV0YAlvk9xsfAyU 6 | YDxcQTjQkeC0bbzhdEPjNjAKBggqhkjOPQQDAgNHADBEAiB+DW/8Kg3tuoovAE+8 7 | 1Pv/8OkF3MD4A1ztULkW3KJ4PwIgMn7ea3HrEQJoeSKFe1kKIgNrHftdC5kZQYj5 8 | uNXYpLo= 9 | -----END CERTIFICATE----- 10 | -------------------------------------------------------------------------------- /packages/pg-bundler-test/rollup-cloudflare.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'rollup' 2 | import { nodeResolve } from '@rollup/plugin-node-resolve' 3 | import commonjs from '@rollup/plugin-commonjs' 4 | 5 | export default defineConfig({ 6 | input: './src/index.mjs', 7 | output: { 8 | file: 'dist/rollup-cloudflare.js', 9 | format: 'es', 10 | }, 11 | plugins: [nodeResolve({ exportConditions: ['import', 'workerd'], preferBuiltins: true }), commonjs()], 12 | external: ['cloudflare:sockets'], 13 | }) 14 | -------------------------------------------------------------------------------- /packages/pg-query-stream/test/instant.ts: -------------------------------------------------------------------------------- 1 | import helper from './helper' 2 | import assert from 'assert' 3 | import concat from 'concat-stream' 4 | import QueryStream from '../src' 5 | 6 | helper('instant', function (client) { 7 | it('instant', function (done) { 8 | const query = new QueryStream('SELECT pg_sleep(1)', []) 9 | const stream = client.query(query) 10 | stream.pipe( 11 | concat(function (res) { 12 | assert.equal(res.length, 1) 13 | done() 14 | }) 15 | ) 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /packages/pg/test/integration/connection-pool/idle-timeout-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const helper = require('./test-helper') 3 | const assert = require('assert') 4 | 5 | new helper.Suite().test('idle timeout', function () { 6 | const config = Object.assign({}, helper.config, { idleTimeoutMillis: 50 }) 7 | const pool = new helper.pg.Pool(config) 8 | pool.connect( 9 | assert.calls(function (err, client, done) { 10 | assert(!err) 11 | client.query('SELECT NOW()') 12 | done() 13 | }) 14 | ) 15 | }) 16 | -------------------------------------------------------------------------------- /packages/pg-bundler-test/vite-cloudflare.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import commonjs from '@rollup/plugin-commonjs' 3 | 4 | export default defineConfig({ 5 | build: { 6 | emptyOutDir: false, 7 | lib: { 8 | entry: './src/index.mjs', 9 | fileName: 'vite-cloudflare', 10 | formats: ['es'], 11 | }, 12 | rollupOptions: { 13 | external: ['cloudflare:sockets'], 14 | }, 15 | }, 16 | resolve: { 17 | conditions: ['import', 'workerd'], 18 | }, 19 | plugins: [commonjs()], 20 | }) 21 | -------------------------------------------------------------------------------- /packages/pg-native/test/empty-query.js: -------------------------------------------------------------------------------- 1 | const Client = require('../') 2 | const assert = require('assert') 3 | 4 | describe('empty query', () => { 5 | it('has field metadata in result', (done) => { 6 | const client = new Client() 7 | client.connectSync() 8 | client.query('SELECT NOW() as now LIMIT 0', (err, rows, res) => { 9 | assert(!err) 10 | assert.equal(rows.length, 0) 11 | assert(Array.isArray(res.fields)) 12 | assert.equal(res.fields.length, 1) 13 | client.end(done) 14 | }) 15 | }) 16 | }) 17 | -------------------------------------------------------------------------------- /packages/pg/test/integration/gh-issues/2416-tests.js: -------------------------------------------------------------------------------- 1 | const helper = require('../test-helper') 2 | const assert = require('assert') 3 | 4 | const suite = new helper.Suite() 5 | 6 | suite.testAsync('it sets search_path on connection', async () => { 7 | const client = new helper.pg.Client({ 8 | options: '--search_path=foo', 9 | }) 10 | await client.connect() 11 | const { rows } = await client.query('SHOW search_path') 12 | assert.strictEqual(rows.length, 1) 13 | assert.strictEqual(rows[0].search_path, 'foo') 14 | await client.end() 15 | }) 16 | -------------------------------------------------------------------------------- /packages/pg/script/dump-db-types.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const pg = require('../lib') 3 | const args = require('../test/cli') 4 | 5 | const queries = ['select CURRENT_TIMESTAMP', "select interval '1 day' + interval '1 hour'", "select TIMESTAMP 'today'"] 6 | 7 | queries.forEach(function (query) { 8 | const client = new pg.Client({ 9 | user: args.user, 10 | database: args.database, 11 | password: args.password, 12 | }) 13 | client.connect() 14 | client.query(query).on('row', function (row) { 15 | console.log(row) 16 | client.end() 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // If you want to run as a non-root user in the container, see .devcontainer/docker-compose.yml. 2 | { 3 | "name": "Node.js 20 & Postgres", 4 | "dockerComposeFile": "docker-compose.yml", 5 | "service": "web", 6 | "workspaceFolder": "/workspace", 7 | // Add the IDs of extensions you want installed when the container is created in the array below. 8 | "customizations":{ 9 | "vscode": { 10 | "extensions": ["dbaeumer.vscode-eslint"], 11 | "settings": { 12 | "terminal.integrated.shell.linux": "/bin/bash" 13 | } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/pg-native/test/connection-errors.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Client = require('../') 4 | const assert = require('assert') 5 | 6 | describe('connection errors', function () { 7 | it('raise error events', function (done) { 8 | const client = new Client() 9 | client.connectSync() 10 | client.query('SELECT pg_terminate_backend(pg_backend_pid())', assert.fail) 11 | client.on('error', function (err) { 12 | assert(err) 13 | assert.strictEqual(client.pq.resultErrorFields().sqlState, '57P01') 14 | client.end() 15 | done() 16 | }) 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /packages/pg-protocol/src/b.ts: -------------------------------------------------------------------------------- 1 | // file for microbenchmarking 2 | 3 | import { BufferReader } from './buffer-reader' 4 | 5 | const LOOPS = 1000 6 | let count = 0 7 | const start = performance.now() 8 | 9 | const reader = new BufferReader() 10 | const buffer = Buffer.from([33, 33, 33, 33, 33, 33, 33, 0]) 11 | 12 | const run = () => { 13 | if (count > LOOPS) { 14 | console.log(performance.now() - start) 15 | return 16 | } 17 | count++ 18 | for (let i = 0; i < LOOPS; i++) { 19 | reader.setBuffer(0, buffer) 20 | reader.cstring() 21 | } 22 | setImmediate(run) 23 | } 24 | 25 | run() 26 | -------------------------------------------------------------------------------- /packages/pg-protocol/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "node16", 4 | "esModuleInterop": true, 5 | "allowSyntheticDefaultImports": true, 6 | "strict": true, 7 | "target": "es6", 8 | "noImplicitAny": true, 9 | "moduleResolution": "node16", 10 | "sourceMap": true, 11 | "outDir": "dist", 12 | "incremental": true, 13 | "baseUrl": ".", 14 | "declaration": true, 15 | "paths": { 16 | "*": [ 17 | "node_modules/*", 18 | "src/types/*" 19 | ] 20 | } 21 | }, 22 | "include": [ 23 | "src/**/*" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /packages/pg-cloudflare/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "node16", 4 | "esModuleInterop": true, 5 | "allowSyntheticDefaultImports": true, 6 | "strict": true, 7 | "target": "es2020", 8 | "noImplicitAny": true, 9 | "moduleResolution": "node16", 10 | "sourceMap": true, 11 | "outDir": "dist", 12 | "incremental": true, 13 | "baseUrl": ".", 14 | "declaration": true, 15 | "paths": { 16 | "*": [ 17 | "node_modules/*", 18 | "src/types/*" 19 | ] 20 | } 21 | }, 22 | "include": [ 23 | "src/**/*" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /packages/pg/test/unit/client/early-disconnect-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | require('./test-helper') 3 | const net = require('net') 4 | const pg = require('../../../lib/index.js') 5 | const assert = require('assert') 6 | 7 | /* console.log() messages show up in `make test` output. TODO: fix it. */ 8 | const server = net.createServer(function (c) { 9 | c.destroy() 10 | server.close() 11 | }) 12 | 13 | server.listen(7777, function () { 14 | const client = new pg.Client('postgres://localhost:7777') 15 | client.connect( 16 | assert.calls(function (err) { 17 | assert(err) 18 | }) 19 | ) 20 | }) 21 | -------------------------------------------------------------------------------- /packages/pg-pool/test/logging.js: -------------------------------------------------------------------------------- 1 | const expect = require('expect.js') 2 | 3 | const describe = require('mocha').describe 4 | const it = require('mocha').it 5 | 6 | const Pool = require('../') 7 | 8 | describe('logging', function () { 9 | it('logs to supplied log function if given', function () { 10 | const messages = [] 11 | const log = function (msg) { 12 | messages.push(msg) 13 | } 14 | const pool = new Pool({ log: log }) 15 | return pool.query('SELECT NOW()').then(function () { 16 | expect(messages.length).to.be.greaterThan(0) 17 | return pool.end() 18 | }) 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /packages/pg-query-stream/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "node16", 4 | "esModuleInterop": true, 5 | "allowSyntheticDefaultImports": true, 6 | "strict": false, 7 | "target": "es6", 8 | "noImplicitAny": false, 9 | "moduleResolution": "node16", 10 | "sourceMap": true, 11 | "pretty": true, 12 | "outDir": "dist", 13 | "incremental": true, 14 | "baseUrl": ".", 15 | "declaration": true, 16 | "types": [ 17 | "node", 18 | "pg", 19 | "mocha", 20 | "chai" 21 | ] 22 | }, 23 | "include": [ 24 | "src/**/*" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /packages/pg/test/integration/gh-issues/2307-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const pg = require('../../../lib') 4 | const helper = require('../test-helper') 5 | const assert = require('assert') 6 | 7 | const suite = new helper.Suite() 8 | 9 | suite.test('bad ssl credentials do not cause crash', (done) => { 10 | const config = { 11 | ssl: { 12 | ca: 'invalid_value', 13 | key: 'invalid_value', 14 | cert: 'invalid_value', 15 | }, 16 | } 17 | 18 | const client = new pg.Client(config) 19 | 20 | client.connect((err) => { 21 | assert(err) 22 | client.end() 23 | done() 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /packages/pg-pool/test/verify.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const expect = require('expect.js') 3 | 4 | const describe = require('mocha').describe 5 | const it = require('mocha').it 6 | 7 | const Pool = require('../') 8 | 9 | describe('verify', () => { 10 | it('verifies a client with a callback', (done) => { 11 | const pool = new Pool({ 12 | verify: (client, cb) => { 13 | cb(new Error('nope')) 14 | }, 15 | }) 16 | 17 | pool.connect((err, client) => { 18 | expect(err).to.be.an(Error) 19 | expect(err.message).to.be('nope') 20 | pool.end() 21 | done() 22 | }) 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /packages/pg/test/integration/client/connection-parameter-tests.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert') 2 | const helper = require('../test-helper') 3 | const suite = new helper.Suite() 4 | const { Client } = helper.pg 5 | 6 | suite.test('it sends options', async () => { 7 | const client = new Client({ 8 | options: '--default_transaction_isolation=serializable', 9 | }) 10 | await client.connect() 11 | const { rows } = await client.query('SHOW default_transaction_isolation') 12 | assert.strictEqual(rows.length, 1) 13 | assert.strictEqual(rows[0].default_transaction_isolation, 'serializable') 14 | await client.end() 15 | }) 16 | -------------------------------------------------------------------------------- /packages/pg-pool/test/submittable.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const Cursor = require('pg-cursor') 3 | const expect = require('expect.js') 4 | const describe = require('mocha').describe 5 | const it = require('mocha').it 6 | 7 | const Pool = require('../') 8 | 9 | describe('submittle', () => { 10 | it('is returned from the query method', false, (done) => { 11 | const pool = new Pool() 12 | const cursor = pool.query(new Cursor('SELECT * from generate_series(0, 1000)')) 13 | cursor.read((err, rows) => { 14 | expect(err).to.be(undefined) 15 | expect(!!rows).to.be.ok() 16 | cursor.close(done) 17 | }) 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /packages/pg/test/integration/client/ssl-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const helper = require('./test-helper') 3 | const assert = require('assert') 4 | const suite = new helper.Suite() 5 | 6 | suite.test('can connect with ssl', function () { 7 | const config = { 8 | ...helper.config, 9 | ssl: { 10 | rejectUnauthorized: false, 11 | }, 12 | } 13 | const client = new helper.pg.Client(config) 14 | client.connect( 15 | assert.success(function () { 16 | client.query( 17 | 'SELECT NOW()', 18 | assert.success(function () { 19 | client.end() 20 | }) 21 | ) 22 | }) 23 | ) 24 | }) 25 | -------------------------------------------------------------------------------- /packages/pg/test/cli.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const ConnectionParameters = require('../lib/connection-parameters') 3 | const config = new ConnectionParameters(process.argv[2]) 4 | 5 | for (let i = 0; i < process.argv.length; i++) { 6 | switch (process.argv[i].toLowerCase()) { 7 | case 'native': 8 | config.native = true 9 | break 10 | case 'binary': 11 | config.binary = true 12 | break 13 | case 'down': 14 | config.down = true 15 | break 16 | default: 17 | break 18 | } 19 | } 20 | 21 | if (process.env['PG_TEST_NATIVE']) { 22 | config.native = true 23 | } 24 | 25 | module.exports = config 26 | -------------------------------------------------------------------------------- /packages/pg-esm-test/pg-connection-string.test.js: -------------------------------------------------------------------------------- 1 | import assert from 'node:assert' 2 | import { describe, it } from 'node:test' 3 | import { parse, toClientConfig, parseIntoClientConfig } from 'pg-connection-string' 4 | 5 | describe('pg-connection-string', () => { 6 | it('should export parse function', () => { 7 | assert.strictEqual(typeof parse, 'function') 8 | }) 9 | 10 | it('should export toClientConfig function', () => { 11 | assert.strictEqual(typeof toClientConfig, 'function') 12 | }) 13 | 14 | it('should export parseIntoClientConfig function', () => { 15 | assert.strictEqual(typeof parseIntoClientConfig, 'function') 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /packages/pg/test/integration/client/empty-query-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const helper = require('./test-helper') 3 | const suite = new helper.Suite() 4 | const assert = require('assert') 5 | 6 | suite.test('empty query message handling', function (done) { 7 | const client = helper.client() 8 | assert.emits(client, 'drain', function () { 9 | client.end(done) 10 | }) 11 | client.query({ text: '' }) 12 | }) 13 | 14 | suite.test('callback supported', function (done) { 15 | const client = helper.client() 16 | client.query('', function (err, result) { 17 | assert(!err) 18 | assert.empty(result.rows) 19 | client.end(done) 20 | }) 21 | }) 22 | -------------------------------------------------------------------------------- /packages/pg/test/integration/connection-pool/tls-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fs = require('fs') 4 | 5 | const helper = require('./test-helper') 6 | const pg = helper.pg 7 | 8 | const suite = new helper.Suite() 9 | 10 | if (process.env.PG_CLIENT_CERT_TEST) { 11 | suite.testAsync('client certificate', async () => { 12 | const pool = new pg.Pool({ 13 | ssl: { 14 | ca: fs.readFileSync(process.env.PGSSLROOTCERT), 15 | cert: fs.readFileSync(process.env.PGSSLCERT), 16 | key: fs.readFileSync(process.env.PGSSLKEY), 17 | }, 18 | }) 19 | 20 | await pool.query('SELECT 1') 21 | await pool.end() 22 | }) 23 | } 24 | -------------------------------------------------------------------------------- /packages/pg-esm-test/pg-protocol.test.js: -------------------------------------------------------------------------------- 1 | import protocol, { NoticeMessage, DatabaseError } from 'pg-protocol/dist/messages.js' 2 | import { describe, it } from 'node:test' 3 | import { strict as assert } from 'node:assert' 4 | 5 | describe('pg-protocol', () => { 6 | it('should export database error', () => { 7 | assert.ok(DatabaseError) 8 | }) 9 | it('should export protocol', () => { 10 | assert.ok(protocol) 11 | assert.ok(protocol.noData) 12 | assert.ok(protocol.parseComplete) 13 | assert.ok(protocol.NoticeMessage) 14 | }) 15 | it('should export NoticeMessage from file in dist folder', () => { 16 | assert.ok(NoticeMessage) 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /packages/pg/test/integration/gh-issues/1542-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 | suite.testAsync('BoundPool can be subclassed', async () => { 8 | const Pool = helper.pg.Pool 9 | class SubPool extends Pool {} 10 | const subPool = new SubPool() 11 | const client = await subPool.connect() 12 | client.release() 13 | await subPool.end() 14 | assert(subPool instanceof helper.pg.Pool) 15 | }) 16 | 17 | suite.test('calling pg.Pool without new throws', () => { 18 | const Pool = helper.pg.Pool 19 | assert.throws(() => { 20 | Pool() 21 | }) 22 | }) 23 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # node-postgres docs website 2 | 3 | This is the documentation for node-postgres which is currently hosted at [https://node-postgres.com](https://node-postgres.com). 4 | 5 | ## Development 6 | 7 | To run the documentation locally, you need to have [Node.js](https://nodejs.org) installed. Then, you can clone the repository and install the dependencies: 8 | 9 | ```bash 10 | cd docs 11 | yarn 12 | ``` 13 | 14 | Once you've installed the deps, you can run the development server: 15 | 16 | ```bash 17 | yarn dev 18 | ``` 19 | 20 | This will start a local server at [http://localhost:3000](http://localhost:3000) where you can view the documentation and see your changes. 21 | -------------------------------------------------------------------------------- /packages/pg/esm/index.mjs: -------------------------------------------------------------------------------- 1 | // ESM wrapper for pg 2 | import pg from '../lib/index.js' 3 | 4 | // Re-export all the properties 5 | export const Client = pg.Client 6 | export const Pool = pg.Pool 7 | export const Connection = pg.Connection 8 | export const types = pg.types 9 | export const Query = pg.Query 10 | export const DatabaseError = pg.DatabaseError 11 | export const escapeIdentifier = pg.escapeIdentifier 12 | export const escapeLiteral = pg.escapeLiteral 13 | export const Result = pg.Result 14 | export const TypeOverrides = pg.TypeOverrides 15 | 16 | // Also export the defaults 17 | export const defaults = pg.defaults 18 | 19 | // Re-export the default 20 | export default pg 21 | -------------------------------------------------------------------------------- /packages/pg-query-stream/test/empty-query.ts: -------------------------------------------------------------------------------- 1 | import helper from './helper' 2 | import QueryStream from '../src' 3 | 4 | helper('empty-query', function (client) { 5 | it('handles empty query', function (done) { 6 | const stream = new QueryStream('-- this is a comment', []) 7 | const query = client.query(stream) 8 | query 9 | .on('end', function () { 10 | // nothing should happen for empty query 11 | done() 12 | }) 13 | .on('data', function () { 14 | // noop to kick off reading 15 | }) 16 | }) 17 | 18 | it('continues to function after stream', function (done) { 19 | client.query('SELECT NOW()', done) 20 | }) 21 | }) 22 | -------------------------------------------------------------------------------- /packages/pg/test/native/native-vs-js-error-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const assert = require('assert') 3 | const Client = require('../../lib/client') 4 | const NativeClient = require('../../lib/native') 5 | 6 | const client = new Client() 7 | const nativeClient = new NativeClient() 8 | 9 | client.connect() 10 | nativeClient.connect((err) => { 11 | client.query('SELECT alsdkfj', (err) => { 12 | client.end() 13 | 14 | nativeClient.query('SELECT lkdasjfasd', (nativeErr) => { 15 | for (const key in nativeErr) { 16 | assert.equal(err[key], nativeErr[key], `Expected err.${key} to equal nativeErr.${key}`) 17 | } 18 | nativeClient.end() 19 | }) 20 | }) 21 | }) 22 | -------------------------------------------------------------------------------- /packages/pg/test/tls/test-client-ca.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBozCCAUmgAwIBAgIUNYMF06PrmjsMR6x+C8k5YZn9heAwCgYIKoZIzj0EAwIw 3 | JzElMCMGA1UEAwwcbm9kZS1wb3N0Z3JlcyB0ZXN0IGNsaWVudCBDQTAeFw0yMDEw 4 | MzExOTI1NDdaFw0zMDEwMjkxOTI1NDdaMCcxJTAjBgNVBAMMHG5vZGUtcG9zdGdy 5 | ZXMgdGVzdCBjbGllbnQgQ0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASI/Efx 6 | Pq0P54VKPkTUOTwBH1iuYbnLpd4kAGjb1E334/p9CEBbDREVSqDjYjWswFybxKIF 7 | ooKXtMpEMJfymJAUo1MwUTAdBgNVHQ4EFgQU/b/FRwYZ5/VMjdesIolksiqNYK4w 8 | HwYDVR0jBBgwFoAU/b/FRwYZ5/VMjdesIolksiqNYK4wDwYDVR0TAQH/BAUwAwEB 9 | /zAKBggqhkjOPQQDAgNIADBFAiEApHFCAWGbRGqYkyiBO+gMyX6gF5oFJywUupZP 10 | LfgIRDACIDBZotzPe6+BIl2fU9Xgm7CxV6cCoX8bPEJKveKMnOaN 11 | -----END CERTIFICATE----- 12 | -------------------------------------------------------------------------------- /packages/pg/test/tls/test-server-ca.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBozCCAUmgAwIBAgIUD582G2ou0Lg9q7AJeAMpiQVaiPQwCgYIKoZIzj0EAwIw 3 | JzElMCMGA1UEAwwcbm9kZS1wb3N0Z3JlcyB0ZXN0IHNlcnZlciBDQTAeFw0yMDEw 4 | MzExOTI1NDdaFw0zMDEwMjkxOTI1NDdaMCcxJTAjBgNVBAMMHG5vZGUtcG9zdGdy 5 | ZXMgdGVzdCBzZXJ2ZXIgQ0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAT/jGRh 6 | FiZu96o0hfgIkep4PusTwI6P1ASFh8LgnUu2bMcIlYakQK0ap2XvCaSl9675+Lu9 7 | yNZaSZVA5LpFICXto1MwUTAdBgNVHQ4EFgQUHI1BK+6u7r9r1XhighuP2/eGcQUw 8 | HwYDVR0jBBgwFoAUHI1BK+6u7r9r1XhighuP2/eGcQUwDwYDVR0TAQH/BAUwAwEB 9 | /zAKBggqhkjOPQQDAgNIADBFAiALwBWN9pRpaGQ12G9ERACn8/6RtAoO4lI5RmaR 10 | rsTHtAIhAJxMfzNIgBAgX7vBSjHaqA08CozIctDSVag/rDlAzgy0 11 | -----END CERTIFICATE----- 12 | -------------------------------------------------------------------------------- /packages/pg/test/integration/client/query-column-names-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const helper = require('../test-helper') 3 | const pg = helper.pg 4 | const assert = require('assert') 5 | 6 | new helper.Suite().test('support for complex column names', function () { 7 | const pool = new pg.Pool() 8 | pool.connect( 9 | assert.success(function (client, done) { 10 | client.query('CREATE TEMP TABLE t ( "complex\'\'column" TEXT )') 11 | client.query( 12 | 'SELECT * FROM t', 13 | assert.success(function (res) { 14 | done() 15 | assert.strictEqual(res.fields[0].name, "complex''column") 16 | pool.end() 17 | }) 18 | ) 19 | }) 20 | ) 21 | }) 22 | -------------------------------------------------------------------------------- /packages/pg-pool/test/idle-timeout-exit.js: -------------------------------------------------------------------------------- 1 | // This test is meant to be spawned from idle-timeout.js 2 | if (module === require.main) { 3 | const allowExitOnIdle = process.env.ALLOW_EXIT_ON_IDLE === '1' 4 | const Pool = require('../index') 5 | 6 | const pool = new Pool({ 7 | maxLifetimeSeconds: 2, 8 | idleTimeoutMillis: 200, 9 | ...(allowExitOnIdle ? { allowExitOnIdle: true } : {}), 10 | }) 11 | pool.query('SELECT NOW()', (err, res) => console.log('completed first')) 12 | pool.on('remove', () => { 13 | console.log('removed') 14 | }) 15 | 16 | setTimeout(() => { 17 | pool.query('SELECT * from generate_series(0, 1000)', (err, res) => console.log('completed second')) 18 | }, 50) 19 | } 20 | -------------------------------------------------------------------------------- /packages/pg/test/integration/gh-issues/1105-tests.js: -------------------------------------------------------------------------------- 1 | const helper = require('../test-helper') 2 | const suite = new helper.Suite() 3 | 4 | suite.testAsync('timeout causing query crashes', async () => { 5 | const client = new helper.Client() 6 | await client.connect() 7 | await client.query('CREATE TEMP TABLE foobar( name TEXT NOT NULL, id SERIAL)') 8 | await client.query('BEGIN') 9 | await client.query("SET LOCAL statement_timeout TO '1ms'") 10 | let count = 0 11 | while (count++ < 5000) { 12 | try { 13 | await client.query('INSERT INTO foobar(name) VALUES ($1)', [Math.random() * 1000 + '']) 14 | } catch (e) { 15 | await client.query('ROLLBACK') 16 | } 17 | } 18 | await client.end() 19 | }) 20 | -------------------------------------------------------------------------------- /packages/pg-native/test/connection.js: -------------------------------------------------------------------------------- 1 | const Client = require('../') 2 | const assert = require('assert') 3 | 4 | describe('connection error', function () { 5 | it('doesnt segfault', function (done) { 6 | const client = new Client() 7 | client.connect('asldgsdgasgdasdg', function (err) { 8 | assert(err) 9 | // calling error on a closed client was segfaulting 10 | client.end() 11 | done() 12 | }) 13 | }) 14 | }) 15 | 16 | describe('reading while not connected', function () { 17 | it('does not seg fault but does throw execption', function () { 18 | const client = new Client() 19 | assert.throws(function () { 20 | client.on('notification', function (msg) {}) 21 | }) 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /packages/pg/test/integration/connection-pool/yield-support-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const helper = require('./test-helper') 3 | const co = require('co') 4 | const assert = require('assert') 5 | 6 | const pool = new helper.pg.Pool() 7 | new helper.Suite().test( 8 | 'using coroutines works with promises', 9 | co.wrap(function* () { 10 | const client = yield pool.connect() 11 | const res = yield client.query('SELECT $1::text as name', ['foo']) 12 | assert.equal(res.rows[0].name, 'foo') 13 | 14 | let threw = false 15 | try { 16 | yield client.query('SELECT LKDSJDSLKFJ') 17 | } catch (e) { 18 | threw = true 19 | } 20 | assert(threw) 21 | client.release() 22 | yield pool.end() 23 | }) 24 | ) 25 | -------------------------------------------------------------------------------- /packages/pg/test/unit/client/test-helper.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const helper = require('../test-helper') 3 | const Connection = require('../../../lib/connection') 4 | const { Client } = helper 5 | 6 | const makeClient = function () { 7 | const connection = new Connection({ stream: 'no' }) 8 | connection.startup = function () {} 9 | connection.connect = function () {} 10 | connection.query = function (text) { 11 | this.queries.push(text) 12 | } 13 | connection.queries = [] 14 | const client = new Client({ connection: connection }) 15 | client.connect() 16 | client.connection.emit('connect') 17 | return client 18 | } 19 | 20 | module.exports = Object.assign( 21 | { 22 | client: makeClient, 23 | }, 24 | helper 25 | ) 26 | -------------------------------------------------------------------------------- /packages/pg-cloudflare/src/types.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'cloudflare:sockets' { 2 | export class Socket { 3 | public readonly readable: any 4 | public readonly writable: any 5 | public readonly closed: Promise 6 | public close(): Promise 7 | public startTls(options: TlsOptions): Socket 8 | } 9 | 10 | export type TlsOptions = { 11 | expectedServerHostname?: string 12 | } 13 | 14 | export type SocketAddress = { 15 | hostname: string 16 | port: number 17 | } 18 | 19 | export type SocketOptions = { 20 | secureTransport?: 'off' | 'on' | 'starttls' 21 | allowHalfOpen?: boolean 22 | } 23 | 24 | export function connect(address: string | SocketAddress, options?: SocketOptions): Socket 25 | } 26 | -------------------------------------------------------------------------------- /packages/pg-esm-test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pg-esm-test", 3 | "version": "1.2.3", 4 | "description": "A test module for PostgreSQL with ESM support", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "node --test --conditions=workerd" 9 | }, 10 | "keywords": [ 11 | "postgres", 12 | "postgresql", 13 | "esm", 14 | "test" 15 | ], 16 | "devDependencies": { 17 | "pg": "^8.16.3", 18 | "pg-cloudflare": "^1.2.7", 19 | "pg-cursor": "^2.15.3", 20 | "pg-native": "^3.5.2", 21 | "pg-pool": "^3.10.1", 22 | "pg-protocol": "^1.10.3", 23 | "pg-query-stream": "^4.10.3" 24 | }, 25 | "author": "Brian M. Carlson ", 26 | "license": "MIT" 27 | } 28 | -------------------------------------------------------------------------------- /packages/pg-connection-string/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | .nyc_output 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # Compiled binary addons (http://nodejs.org/api/addons.html) 21 | build/Release 22 | 23 | # Dependency directory 24 | # Deployed apps should consider commenting this line out: 25 | # see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git 26 | node_modules 27 | package-lock.json 28 | 29 | # TypeScript output directory 30 | dist 31 | -------------------------------------------------------------------------------- /packages/pg-native/test/huge-query.js: -------------------------------------------------------------------------------- 1 | const Client = require('../') 2 | const assert = require('assert') 3 | 4 | describe('huge async query', 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 params = [''] 16 | const len = 100000 17 | for (let i = 0; i < len; i++) { 18 | params[0] += 'A' 19 | } 20 | const qText = "SELECT '" + params[0] + "'::text as my_text" 21 | this.client.query(qText, function (err, rows) { 22 | if (err) return done(err) 23 | assert.equal(rows[0].my_text.length, len) 24 | done() 25 | }) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /packages/pg/test/integration/gh-issues/507-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const helper = require('../test-helper') 3 | const pg = helper.pg 4 | const assert = require('assert') 5 | 6 | new helper.Suite().test('parsing array results', function (cb) { 7 | const pool = new pg.Pool() 8 | pool.connect( 9 | assert.success(function (client, done) { 10 | client.query('CREATE TEMP TABLE test_table(bar integer, "baz\'s" integer)') 11 | client.query('INSERT INTO test_table(bar, "baz\'s") VALUES(1, 1), (2, 2)') 12 | client.query('SELECT * FROM test_table', function (err, res) { 13 | assert.equal(res.rows[0]["baz's"], 1) 14 | assert.equal(res.rows[1]["baz's"], 2) 15 | done() 16 | pool.end(cb) 17 | }) 18 | }) 19 | ) 20 | }) 21 | -------------------------------------------------------------------------------- /packages/pg/test/integration/gh-issues/3487-tests.js: -------------------------------------------------------------------------------- 1 | const helper = require('../test-helper') 2 | const assert = require('assert') 3 | 4 | const suite = new helper.Suite() 5 | 6 | suite.testAsync('allows you to switch between format modes for arrays', async () => { 7 | const client = new helper.pg.Client() 8 | await client.connect() 9 | 10 | const r1 = await client.query({ 11 | text: 'SELECT CAST($1 AS INT[]) as a', 12 | values: [[1, 2, 8]], 13 | binary: false, 14 | }) 15 | assert.deepEqual([1, 2, 8], r1.rows[0].a) 16 | 17 | const r2 = await client.query({ 18 | text: 'SELECT CAST($1 AS INT[]) as a', 19 | values: [[4, 5, 6]], 20 | binary: true, 21 | }) 22 | assert.deepEqual([4, 5, 6], r2.rows[0].a) 23 | 24 | await client.end() 25 | }) 26 | -------------------------------------------------------------------------------- /packages/pg/test/integration/gh-issues/199-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const helper = require('../test-helper') 3 | const client = helper.client() 4 | const assert = require('assert') 5 | 6 | client.query('CREATE TEMP TABLE arrtest (n integer, s varchar)') 7 | client.query("INSERT INTO arrtest VALUES (4, 'foo'), (5, 'bar'), (6, 'baz');") 8 | 9 | const qText = 10 | "SELECT \ 11 | ARRAY[1, 2, 3] AS b,\ 12 | ARRAY['xx', 'yy', 'zz'] AS c,\ 13 | ARRAY(SELECT n FROM arrtest) AS d,\ 14 | ARRAY(SELECT s FROM arrtest) AS e;" 15 | 16 | client.query(qText, function (err, result) { 17 | if (err) throw err 18 | const row = result.rows[0] 19 | for (const key in row) { 20 | assert.equal(typeof row[key], 'object') 21 | assert.equal(row[key].length, 3) 22 | } 23 | client.end() 24 | }) 25 | -------------------------------------------------------------------------------- /packages/pg-native/test/custom-types.js: -------------------------------------------------------------------------------- 1 | const Client = require('../') 2 | const ok = require('okay') 3 | const assert = require('assert') 4 | 5 | describe('Custom type parser', function () { 6 | it('is used by client', function (done) { 7 | const client = new Client({ 8 | types: { 9 | getTypeParser: function () { 10 | return function () { 11 | return 'blah' 12 | } 13 | }, 14 | }, 15 | }) 16 | client.connectSync() 17 | const rows = client.querySync('SELECT NOW() AS when') 18 | assert.equal(rows[0].when, 'blah') 19 | client.query( 20 | 'SELECT NOW() as when', 21 | ok(function (rows) { 22 | assert.equal(rows[0].when, 'blah') 23 | client.end(done) 24 | }) 25 | ) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /packages/pg-native/test/many-errors.js: -------------------------------------------------------------------------------- 1 | const Client = require('../') 2 | const async = require('async') 3 | const assert = require('assert') 4 | 5 | describe('many errors', function () { 6 | it('functions properly without segfault', function (done) { 7 | const throwError = function (n, cb) { 8 | const client = new Client() 9 | client.connectSync() 10 | 11 | const doIt = function (n, cb) { 12 | client.query('select asdfiasdf', function (err) { 13 | assert(err, 'bad query should emit an error') 14 | cb(null) 15 | }) 16 | } 17 | 18 | async.timesSeries(10, doIt, function (err) { 19 | if (err) return cb(err) 20 | client.end(cb) 21 | }) 22 | } 23 | 24 | async.times(10, throwError, done) 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /packages/pg/test/integration/gh-issues/2056-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 | suite.test('All queries should return a result array', (done) => { 8 | const client = new helper.pg.Client() 9 | client.connect() 10 | const promises = [] 11 | promises.push(client.query('CREATE TEMP TABLE foo(bar TEXT)')) 12 | promises.push(client.query('INSERT INTO foo(bar) VALUES($1)', ['qux'])) 13 | promises.push(client.query('SELECT * FROM foo WHERE bar = $1', ['foo'])) 14 | Promise.all(promises).then((results) => { 15 | results.forEach((res) => { 16 | assert(Array.isArray(res.fields)) 17 | assert(Array.isArray(res.rows)) 18 | }) 19 | client.end(done) 20 | }) 21 | }) 22 | -------------------------------------------------------------------------------- /packages/pg-native/test/load.js: -------------------------------------------------------------------------------- 1 | const Client = require('../') 2 | const async = require('async') 3 | const ok = require('okay') 4 | 5 | const execute = function (x, done) { 6 | const client = new Client() 7 | client.connectSync() 8 | const query = function (n, cb) { 9 | client.query('SELECT $1::int as num', [n], function (err) { 10 | cb(err) 11 | }) 12 | } 13 | return async.timesSeries( 14 | 5, 15 | query, 16 | ok(done, function () { 17 | client.end() 18 | done() 19 | }) 20 | ) 21 | } 22 | describe('Load tests', function () { 23 | it('single client and many queries', function (done) { 24 | async.times(1, execute, done) 25 | }) 26 | 27 | it('multiple client and many queries', function (done) { 28 | async.times(20, execute, done) 29 | }) 30 | }) 31 | -------------------------------------------------------------------------------- /packages/pg/test/integration/gh-issues/675-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const helper = require('../test-helper') 3 | const assert = require('assert') 4 | 5 | const pool = new helper.pg.Pool() 6 | pool.connect(function (err, client, done) { 7 | if (err) throw err 8 | 9 | let c = 'CREATE TEMP TABLE posts (body TEXT)' 10 | 11 | client.query(c, function (err) { 12 | if (err) throw err 13 | 14 | c = 'INSERT INTO posts (body) VALUES ($1) RETURNING *' 15 | 16 | let body = Buffer.from('foo') 17 | client.query(c, [body], function (err) { 18 | if (err) throw err 19 | 20 | body = Buffer.from([]) 21 | client.query(c, [body], function (err, res) { 22 | done() 23 | 24 | if (err) throw err 25 | assert.equal(res.rows[0].body, '') 26 | pool.end() 27 | }) 28 | }) 29 | }) 30 | }) 31 | -------------------------------------------------------------------------------- /packages/pg-cursor/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pg-cursor", 3 | "version": "2.15.3", 4 | "description": "Query cursor extension for node-postgres", 5 | "main": "index.js", 6 | "exports": { 7 | ".": { 8 | "import": "./esm/index.mjs", 9 | "require": "./index.js", 10 | "default": "./index.js" 11 | } 12 | }, 13 | "directories": { 14 | "test": "test" 15 | }, 16 | "scripts": { 17 | "test": "mocha" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "git://github.com/brianc/node-postgres.git", 22 | "directory": "packages/pg-cursor" 23 | }, 24 | "author": "Brian M. Carlson", 25 | "license": "MIT", 26 | "devDependencies": { 27 | "mocha": "^10.5.2", 28 | "pg": "^8.16.3" 29 | }, 30 | "peerDependencies": { 31 | "pg": "^8" 32 | }, 33 | "files": [ 34 | "index.js", 35 | "esm" 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /packages/pg-query-stream/test/slow-reader.ts: -------------------------------------------------------------------------------- 1 | import helper from './helper' 2 | import QueryStream from '../src' 3 | import concat from 'concat-stream' 4 | 5 | import { Transform } from 'stream' 6 | 7 | const mapper = new Transform({ objectMode: true }) 8 | 9 | mapper._transform = function (obj, enc, cb) { 10 | this.push(obj) 11 | setTimeout(cb, 5) 12 | } 13 | 14 | helper('slow reader', function (client) { 15 | it('works', function (done) { 16 | this.timeout(50000) 17 | const stream = new QueryStream('SELECT * FROM generate_series(0, 201) num', [], { 18 | highWaterMark: 100, 19 | batchSize: 50, 20 | }) 21 | stream.on('end', function () { 22 | // console.log('stream end') 23 | }) 24 | client.query(stream) 25 | stream.pipe(mapper).pipe( 26 | concat(function (res) { 27 | done() 28 | }) 29 | ) 30 | }) 31 | }) 32 | -------------------------------------------------------------------------------- /packages/pg-query-stream/test/config.ts: -------------------------------------------------------------------------------- 1 | import assert from 'assert' 2 | import QueryStream from '../src' 3 | 4 | describe('stream config options', () => { 5 | // this is mostly for backwards compatibility. 6 | it('sets readable.highWaterMark based on batch size', () => { 7 | const stream = new QueryStream('SELECT NOW()', [], { 8 | batchSize: 88, 9 | }) 10 | assert.equal(stream.readableHighWaterMark, 88) 11 | }) 12 | 13 | it('sets readable.highWaterMark based on highWaterMark config', () => { 14 | const stream = new QueryStream('SELECT NOW()', [], { 15 | highWaterMark: 88, 16 | }) 17 | 18 | assert.equal(stream.readableHighWaterMark, 88) 19 | }) 20 | 21 | it('defaults to 100 for highWaterMark', () => { 22 | const stream = new QueryStream('SELECT NOW()', []) 23 | 24 | assert.equal(stream.readableHighWaterMark, 100) 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /packages/pg-native/test/multiple-statement-results.js: -------------------------------------------------------------------------------- 1 | const Client = require('../') 2 | const assert = require('assert') 3 | 4 | describe('multiple statements', () => { 5 | before(() => { 6 | this.client = new Client() 7 | this.client.connectSync() 8 | }) 9 | 10 | after(() => this.client.end()) 11 | 12 | it('works with multiple queries', (done) => { 13 | const text = ` 14 | SELECT generate_series(1, 2) as foo; 15 | SELECT generate_series(10, 11) as bar; 16 | SELECT generate_series(20, 22) as baz; 17 | ` 18 | this.client.query(text, (err, results) => { 19 | if (err) return done(err) 20 | assert(Array.isArray(results)) 21 | assert.equal(results.length, 3) 22 | assert(Array.isArray(results[0])) 23 | assert(Array.isArray(results[1])) 24 | assert(Array.isArray(results[2])) 25 | done() 26 | }) 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /packages/pg/test/integration/gh-issues/699-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const helper = require('../test-helper') 3 | const copyFrom = require('pg-copy-streams').from 4 | 5 | if (helper.args.native) return 6 | 7 | const pool = new helper.pg.Pool() 8 | pool.connect(function (err, client, done) { 9 | if (err) throw err 10 | 11 | const c = 'CREATE TEMP TABLE employee (id integer, fname varchar(400), lname varchar(400))' 12 | 13 | client.query(c, function (err) { 14 | if (err) throw err 15 | 16 | const stream = client.query(copyFrom('COPY employee FROM STDIN')) 17 | stream.on('end', function () { 18 | done() 19 | setTimeout(() => { 20 | pool.end() 21 | }, 50) 22 | }) 23 | 24 | for (let i = 1; i <= 5; i++) { 25 | const line = ['1\ttest', i, '\tuser', i, '\n'] 26 | stream.write(line.join('')) 27 | } 28 | stream.end() 29 | }) 30 | }) 31 | -------------------------------------------------------------------------------- /packages/pg-query-stream/test/client-options.ts: -------------------------------------------------------------------------------- 1 | import pg from 'pg' 2 | import assert from 'assert' 3 | import QueryStream from '../src' 4 | 5 | describe('client options', function () { 6 | it('uses custom types from client config', function (done) { 7 | const types = { 8 | getTypeParser: () => (string) => string, 9 | } 10 | //@ts-expect-error 11 | const client = new pg.Client({ types }) 12 | client.connect() 13 | const stream = new QueryStream('SELECT * FROM generate_series(0, 10) num') 14 | const query = client.query(stream) 15 | const result = [] 16 | query.on('data', (datum) => { 17 | result.push(datum) 18 | }) 19 | query.on('end', () => { 20 | const expected = new Array(11).fill(0).map((_, i) => ({ 21 | num: i.toString(), 22 | })) 23 | assert.deepEqual(result, expected) 24 | client.end() 25 | done() 26 | }) 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /packages/pg/test/unit/client/set-keepalives-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const net = require('net') 3 | const pg = require('../../../lib/index.js') 4 | const helper = require('./test-helper') 5 | const assert = require('assert') 6 | 7 | const suite = new helper.Suite() 8 | 9 | suite.test('setting keep alive', (done) => { 10 | const server = net.createServer((c) => { 11 | c.destroy() 12 | server.close() 13 | }) 14 | 15 | server.listen(7777, () => { 16 | const stream = new net.Socket() 17 | stream.setKeepAlive = (enable, initialDelay) => { 18 | assert(enable === true) 19 | assert(initialDelay === 10000) 20 | done() 21 | } 22 | 23 | const client = new pg.Client({ 24 | host: 'localhost', 25 | port: 7777, 26 | keepAlive: true, 27 | keepAliveInitialDelayMillis: 10000, 28 | stream, 29 | }) 30 | 31 | client.connect().catch(() => {}) 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /packages/pg/test/integration/test-helper.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const helper = require('./../test-helper') 3 | let { Client } = helper 4 | const assert = require('assert') 5 | 6 | if (helper.args.native) { 7 | Client = require('./../../lib/native') 8 | helper.Client = Client 9 | helper.pg = helper.pg.native 10 | } 11 | 12 | // creates a client from cli parameters 13 | helper.client = function (cb) { 14 | const client = new Client() 15 | client.connect(cb) 16 | return client 17 | } 18 | 19 | helper.versionGTE = function (client, testVersion, callback) { 20 | client.query( 21 | 'SHOW server_version_num', 22 | assert.calls(function (err, result) { 23 | if (err) return callback(err) 24 | const version = parseInt(result.rows[0].server_version_num, 10) 25 | return callback(null, version >= testVersion) 26 | }) 27 | ) 28 | } 29 | 30 | // export parent helper stuffs 31 | module.exports = helper 32 | -------------------------------------------------------------------------------- /packages/pg/test/integration/client/huge-numeric-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const helper = require('./test-helper') 3 | const pool = new helper.pg.Pool() 4 | const assert = require('assert') 5 | 6 | pool.connect( 7 | assert.success(function (client, done) { 8 | const types = require('pg-types') 9 | // 1231 = numericOID 10 | types.setTypeParser(1700, function () { 11 | return 'yes' 12 | }) 13 | types.setTypeParser(1700, 'binary', function () { 14 | return 'yes' 15 | }) 16 | const bignum = '294733346389144765940638005275322203805' 17 | client.query('CREATE TEMP TABLE bignumz(id numeric)') 18 | client.query('INSERT INTO bignumz(id) VALUES ($1)', [bignum]) 19 | client.query( 20 | 'SELECT * FROM bignumz', 21 | assert.success(function (result) { 22 | assert.equal(result.rows[0].id, 'yes') 23 | done() 24 | pool.end() 25 | }) 26 | ) 27 | }) 28 | ) 29 | -------------------------------------------------------------------------------- /packages/pg/test/integration/gh-issues/2862-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const helper = require('../test-helper') 4 | const assert = require('assert') 5 | const vm = require('vm') 6 | 7 | const suite = new helper.Suite() 8 | 9 | suite.testAsync('Handle date objects as Date', async () => { 10 | const crossRealmDate = await vm.runInNewContext('new Date()') 11 | assert(!(crossRealmDate instanceof Date)) 12 | const date = new Date(crossRealmDate.getTime()) 13 | const client = new helper.pg.Client() 14 | await client.connect() 15 | 16 | await client.query('CREATE TEMP TABLE foo(bar timestamptz, bar2 timestamptz)') 17 | await client.query('INSERT INTO foo(bar, bar2) VALUES($1, $2)', [date, crossRealmDate]) 18 | const results = await client.query('SELECT * FROM foo') 19 | const row = results.rows[0] 20 | assert.deepStrictEqual(row.bar, date) 21 | assert.deepStrictEqual(row.bar2, date) 22 | await client.end() 23 | }) 24 | -------------------------------------------------------------------------------- /packages/pg/lib/type-overrides.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const types = require('pg-types') 4 | 5 | function TypeOverrides(userTypes) { 6 | this._types = userTypes || types 7 | this.text = {} 8 | this.binary = {} 9 | } 10 | 11 | TypeOverrides.prototype.getOverrides = function (format) { 12 | switch (format) { 13 | case 'text': 14 | return this.text 15 | case 'binary': 16 | return this.binary 17 | default: 18 | return {} 19 | } 20 | } 21 | 22 | TypeOverrides.prototype.setTypeParser = function (oid, format, parseFn) { 23 | if (typeof format === 'function') { 24 | parseFn = format 25 | format = 'text' 26 | } 27 | this.getOverrides(format)[oid] = parseFn 28 | } 29 | 30 | TypeOverrides.prototype.getTypeParser = function (oid, format) { 31 | format = format || 'text' 32 | return this.getOverrides(format)[oid] || this._types.getTypeParser(oid, format) 33 | } 34 | 35 | module.exports = TypeOverrides 36 | -------------------------------------------------------------------------------- /packages/pg-query-stream/test/concat.ts: -------------------------------------------------------------------------------- 1 | import assert from 'assert' 2 | import concat from 'concat-stream' 3 | import { Transform } from 'stream' 4 | import helper from './helper' 5 | import QueryStream from '../src' 6 | 7 | helper('concat', function (client) { 8 | it('concats correctly', function (done) { 9 | const stream = new QueryStream('SELECT * FROM generate_series(0, 200) num', []) 10 | const query = client.query(stream) 11 | query 12 | .pipe( 13 | new Transform({ 14 | transform(chunk, _, callback) { 15 | callback(null, chunk.num) 16 | }, 17 | objectMode: true, 18 | }) 19 | ) 20 | .pipe( 21 | concat(function (result) { 22 | const total = result.reduce(function (prev, cur) { 23 | return prev + cur 24 | }) 25 | assert.equal(total, 20100) 26 | }) 27 | ) 28 | stream.on('end', done) 29 | }) 30 | }) 31 | -------------------------------------------------------------------------------- /packages/pg-query-stream/test/stream-tester-timestamp.ts: -------------------------------------------------------------------------------- 1 | import helper from './helper' 2 | import QueryStream from '../src' 3 | import spec from 'stream-spec' 4 | import assert from 'assert' 5 | 6 | helper('stream tester timestamp', function (client) { 7 | it('should not warn about max listeners', function (done) { 8 | const sql = "SELECT * FROM generate_series('1983-12-30 00:00'::timestamp, '2013-12-30 00:00', '1 years')" 9 | const stream = new QueryStream(sql, []) 10 | let ended = false 11 | const query = client.query(stream) 12 | query.on('end', function () { 13 | ended = true 14 | }) 15 | spec(query).readable().pausable({ strict: true }).validateOnExit() 16 | const checkListeners = function () { 17 | assert(stream.listeners('end').length < 10) 18 | if (!ended) { 19 | setImmediate(checkListeners) 20 | } else { 21 | done() 22 | } 23 | } 24 | checkListeners() 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /packages/pg-pool/test/connection-strings.js: -------------------------------------------------------------------------------- 1 | const expect = require('expect.js') 2 | const describe = require('mocha').describe 3 | const it = require('mocha').it 4 | const Pool = require('../') 5 | 6 | describe('Connection strings', function () { 7 | it('pool delegates connectionString property to client', function (done) { 8 | const connectionString = 'postgres://foo:bar@baz:1234/xur' 9 | 10 | const pool = new Pool({ 11 | // use a fake client so we can check we're passed the connectionString 12 | Client: function (args) { 13 | expect(args.connectionString).to.equal(connectionString) 14 | return { 15 | connect: function (cb) { 16 | cb(new Error('testing')) 17 | }, 18 | on: function () {}, 19 | } 20 | }, 21 | connectionString: connectionString, 22 | }) 23 | 24 | pool.connect(function (err, client) { 25 | expect(err).to.not.be(undefined) 26 | done() 27 | }) 28 | }) 29 | }) 30 | -------------------------------------------------------------------------------- /packages/pg-native/test/array-mode.js: -------------------------------------------------------------------------------- 1 | const Client = require('../') 2 | const assert = require('assert') 3 | 4 | describe('client with arrayMode', function () { 5 | it('returns result as array', function (done) { 6 | const client = new Client({ arrayMode: true }) 7 | client.connectSync() 8 | client.querySync('CREATE TEMP TABLE blah(name TEXT)') 9 | client.querySync('INSERT INTO blah (name) VALUES ($1)', ['brian']) 10 | client.querySync('INSERT INTO blah (name) VALUES ($1)', ['aaron']) 11 | const rows = client.querySync('SELECT * FROM blah') 12 | assert.equal(rows.length, 2) 13 | const row = rows[0] 14 | assert.equal(row.length, 1) 15 | assert.equal(row[0], 'brian') 16 | assert.equal(rows[1][0], 'aaron') 17 | 18 | client.query("SELECT 'brian', null", function (err, res) { 19 | assert.ifError(err) 20 | assert.strictEqual(res[0][0], 'brian') 21 | assert.strictEqual(res[0][1], null) 22 | client.end(done) 23 | }) 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /packages/pg/test/integration/gh-issues/3062-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const helper = require('../test-helper') 3 | const assert = require('assert') 4 | const suite = new helper.Suite() 5 | 6 | // https://github.com/brianc/node-postgres/issues/3062 7 | suite.testAsync('result fields with the same name should pick the last value', async () => { 8 | const client = new helper.pg.Client() 9 | await client.connect() 10 | 11 | const { 12 | rows: [shouldBeNullRow], 13 | } = await client.query('SELECT NULL AS test, 10 AS test, NULL AS test') 14 | assert.equal(shouldBeNullRow.test, null) 15 | 16 | const { 17 | rows: [shouldBeTwelveRow], 18 | } = await client.query('SELECT NULL AS test, 10 AS test, 12 AS test') 19 | assert.equal(shouldBeTwelveRow.test, 12) 20 | 21 | const { 22 | rows: [shouldBeAbcRow], 23 | } = await client.query(`SELECT NULL AS test, 10 AS test, 12 AS test, 'ABC' AS test`) 24 | assert.equal(shouldBeAbcRow.test, 'ABC') 25 | 26 | await client.end() 27 | }) 28 | -------------------------------------------------------------------------------- /packages/pg-native/test/cancel.js: -------------------------------------------------------------------------------- 1 | const Client = require('../') 2 | const assert = require('assert') 3 | 4 | describe('cancel query', function () { 5 | it('works', function (done) { 6 | const client = new Client() 7 | client.connectSync() 8 | client.query('SELECT pg_sleep(1000);', function (err) { 9 | assert(err instanceof Error) 10 | client.end(done) 11 | }) 12 | setTimeout(() => { 13 | client.cancel(function (err) { 14 | assert.ifError(err) 15 | }) 16 | }, 100) 17 | }) 18 | 19 | it('does not raise error if no active query', function (done) { 20 | const client = new Client() 21 | client.connectSync() 22 | client.cancel(function (err) { 23 | assert.ifError(err) 24 | done() 25 | }) 26 | }) 27 | 28 | it('raises error if client is not connected', function (done) { 29 | new Client().cancel(function (err) { 30 | assert(err, 'should raise an error when not connected') 31 | done() 32 | }) 33 | }) 34 | }) 35 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["@typescript-eslint", "prettier"], 3 | "parser": "@typescript-eslint/parser", 4 | "extends": ["eslint:recommended", "plugin:prettier/recommended", "prettier"], 5 | "ignorePatterns": ["node_modules", "coverage", "packages/pg-protocol/dist/**/*", "packages/pg-query-stream/dist/**/*"], 6 | "parserOptions": { 7 | "ecmaVersion": 2017, 8 | "sourceType": "module" 9 | }, 10 | "env": { 11 | "node": true, 12 | "es6": true, 13 | "mocha": true 14 | }, 15 | "rules": { 16 | "@typescript-eslint/no-unused-vars": ["error", { 17 | "args": "none", 18 | "varsIgnorePattern": "^_$" 19 | }], 20 | "no-unused-vars": ["error", { 21 | "args": "none", 22 | "varsIgnorePattern": "^_$" 23 | }], 24 | "no-var": "error", 25 | "prefer-const": "error" 26 | }, 27 | "overrides": [ 28 | { 29 | "files": ["*.ts", "*.mts", "*.cts", "*.tsx"], 30 | "rules": { 31 | "no-undef": "off" 32 | } 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /packages/pg-esm-test/common-js-imports.test.cjs: -------------------------------------------------------------------------------- 1 | const assert = require('node:assert') 2 | const test = require('node:test') 3 | const { describe, it } = test 4 | 5 | const paths = [ 6 | 'pg', 7 | 'pg/lib/index.js', 8 | 'pg/lib/index', 9 | 'pg/lib/connection-parameters', 10 | 'pg/lib/connection-parameters.js', 11 | 'pg/lib/type-overrides', 12 | 'pg-protocol/dist/messages.js', 13 | 'pg-protocol/dist/messages', 14 | 'pg-native/lib/build-result.js', 15 | 'pg-cloudflare/package.json', 16 | ] 17 | for (const path of paths) { 18 | describe(`importing ${path}`, () => { 19 | it('works with require', () => { 20 | const mod = require(path) 21 | assert(mod) 22 | }) 23 | }) 24 | } 25 | 26 | describe('pg-native', () => { 27 | it('should work with commonjs', async () => { 28 | const pg = require('pg') 29 | 30 | const pool = new pg.native.Pool() 31 | const result = await pool.query('SELECT 1') 32 | assert.strictEqual(result.rowCount, 1) 33 | pool.end() 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /packages/pg-native/test/index.js: -------------------------------------------------------------------------------- 1 | const Client = require('../') 2 | const assert = require('assert') 3 | 4 | describe('connection', function () { 5 | it('works', function (done) { 6 | Client().connect(done) 7 | }) 8 | 9 | it('connects with args', function (done) { 10 | Client().connect('host=localhost', done) 11 | }) 12 | 13 | it('errors out with bad connection args', function (done) { 14 | Client().connect('host=asldkfjasdf', function (err) { 15 | assert(err, 'should raise an error for bad host') 16 | done() 17 | }) 18 | }) 19 | }) 20 | 21 | describe('connectSync', function () { 22 | it('works without args', function () { 23 | Client().connectSync() 24 | }) 25 | 26 | it('works with args', function () { 27 | const args = 'host=' + (process.env.PGHOST || 'localhost') 28 | Client().connectSync(args) 29 | }) 30 | 31 | it('throws if bad host', function () { 32 | assert.throws(function () { 33 | Client().connectSync('host=laksdjfdsf') 34 | }) 35 | }) 36 | }) 37 | -------------------------------------------------------------------------------- /packages/pg/test/integration/client/no-row-result-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const helper = require('./test-helper') 3 | const pg = helper.pg 4 | const suite = new helper.Suite() 5 | const pool = new pg.Pool() 6 | const assert = require('assert') 7 | 8 | suite.test('can access results when no rows are returned', function (done) { 9 | const checkResult = function (result) { 10 | assert(result.fields, 'should have fields definition') 11 | assert.equal(result.fields.length, 1) 12 | assert.equal(result.fields[0].name, 'val') 13 | assert.equal(result.fields[0].dataTypeID, 25) 14 | } 15 | 16 | pool.connect( 17 | assert.success(function (client, release) { 18 | const q = new pg.Query('select $1::text as val limit 0', ['hi']) 19 | const query = client.query( 20 | q, 21 | assert.success(function (result) { 22 | checkResult(result) 23 | release() 24 | pool.end(done) 25 | }) 26 | ) 27 | 28 | assert.emits(query, 'end', checkResult) 29 | }) 30 | ) 31 | }) 32 | -------------------------------------------------------------------------------- /packages/pg-query-stream/test/pauses.ts: -------------------------------------------------------------------------------- 1 | import helper from './helper' 2 | import concat from 'concat-stream' 3 | import JSONStream from 'JSONStream' 4 | import QueryStream from '../src' 5 | import { Transform } from 'stream' 6 | 7 | class PauseStream extends Transform { 8 | constructor() { 9 | super({ objectMode: true }) 10 | } 11 | 12 | _transform(chunk, encoding, callback): void { 13 | this.push(chunk, encoding) 14 | setTimeout(callback, 1) 15 | } 16 | } 17 | 18 | helper('pauses', function (client) { 19 | it('pauses', function (done) { 20 | this.timeout(5000) 21 | const stream = new QueryStream('SELECT * FROM generate_series(0, $1) num', [200], { 22 | batchSize: 2, 23 | highWaterMark: 2, 24 | }) 25 | const query = client.query(stream) 26 | const pauser = new PauseStream() 27 | query 28 | .pipe(JSONStream.stringify()) 29 | .pipe(pauser) 30 | .pipe( 31 | concat(function (json) { 32 | JSON.parse(json) 33 | done() 34 | }) 35 | ) 36 | }) 37 | }) 38 | -------------------------------------------------------------------------------- /packages/pg-cursor/test/no-data-handling.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert') 2 | const pg = require('pg') 3 | const Cursor = require('../') 4 | 5 | describe('queries with no data', function () { 6 | beforeEach(function (done) { 7 | const client = (this.client = new pg.Client()) 8 | client.connect(done) 9 | }) 10 | 11 | afterEach(function () { 12 | this.client.end() 13 | }) 14 | 15 | it('handles queries that return no data', function (done) { 16 | const cursor = new Cursor('CREATE TEMPORARY TABLE whatwhat (thing int)') 17 | this.client.query(cursor) 18 | cursor.read(100, function (err, rows) { 19 | assert.ifError(err) 20 | assert.strictEqual(rows.length, 0) 21 | done() 22 | }) 23 | }) 24 | 25 | it('handles empty query', function (done) { 26 | let cursor = new Cursor('-- this is a comment') 27 | cursor = this.client.query(cursor) 28 | cursor.read(100, function (err, rows) { 29 | assert.ifError(err) 30 | assert.strictEqual(rows.length, 0) 31 | done() 32 | }) 33 | }) 34 | }) 35 | -------------------------------------------------------------------------------- /packages/pg/test/integration/gh-issues/1382-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const helper = require('./../test-helper') 3 | 4 | const suite = new helper.Suite() 5 | 6 | suite.test('calling end during active query should return a promise', (done) => { 7 | const client = new helper.pg.Client() 8 | let callCount = 0 9 | // ensure both the query rejects and the end promise resolves 10 | const after = () => { 11 | if (++callCount > 1) { 12 | done() 13 | } 14 | } 15 | client.connect().then(() => { 16 | client.query('SELECT NOW()').catch(after) 17 | client.end().then(after) 18 | }) 19 | }) 20 | 21 | suite.test('calling end during an active query should call end callback', (done) => { 22 | const client = new helper.pg.Client() 23 | let callCount = 0 24 | // ensure both the query rejects and the end callback fires 25 | const after = () => { 26 | if (++callCount > 1) { 27 | done() 28 | } 29 | } 30 | client.connect().then(() => { 31 | client.query('SELECT NOW()').catch(after) 32 | client.end(after) 33 | }) 34 | }) 35 | -------------------------------------------------------------------------------- /packages/pg-cloudflare/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pg-cloudflare", 3 | "version": "1.2.7", 4 | "description": "A socket implementation that can run on Cloudflare Workers using native TCP connections.", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "license": "MIT", 8 | "devDependencies": { 9 | "ts-node": "^8.5.4", 10 | "typescript": "^4.0.3" 11 | }, 12 | "exports": { 13 | ".": { 14 | "workerd": { 15 | "import": "./esm/index.mjs", 16 | "require": "./dist/index.js" 17 | }, 18 | "default": "./dist/empty.js" 19 | }, 20 | "./package.json": "./package.json" 21 | }, 22 | "scripts": { 23 | "build": "tsc", 24 | "build:watch": "tsc --watch", 25 | "prepublish": "yarn build", 26 | "test": "echo e2e test in pg package" 27 | }, 28 | "repository": { 29 | "type": "git", 30 | "url": "git://github.com/brianc/node-postgres.git", 31 | "directory": "packages/pg-cloudflare" 32 | }, 33 | "files": [ 34 | "/dist/*{js,ts,map}", 35 | "/src", 36 | "/esm" 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /packages/pg/test/integration/gh-issues/130-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const helper = require('../test-helper') 3 | const exec = require('child_process').exec 4 | const assert = require('assert') 5 | 6 | helper.pg.defaults.poolIdleTimeout = 1000 7 | 8 | const pool = new helper.pg.Pool() 9 | pool.connect(function (err, client, done) { 10 | assert.ifError(err) 11 | client.once('error', function (err) { 12 | client.on('error', (err) => {}) 13 | done(err) 14 | }) 15 | client.query('SELECT pg_backend_pid()', function (err, result) { 16 | assert.ifError(err) 17 | const pid = result.rows[0].pg_backend_pid 18 | let psql = 'psql' 19 | if (helper.args.host) psql = psql + ' -h ' + helper.args.host 20 | if (helper.args.port) psql = psql + ' -p ' + helper.args.port 21 | if (helper.args.user) psql = psql + ' -U ' + helper.args.user 22 | exec( 23 | psql + ' -c "select pg_terminate_backend(' + pid + ')" template1', 24 | assert.calls(function (error, stdout, stderr) { 25 | assert.ifError(error) 26 | }) 27 | ) 28 | }) 29 | }) 30 | -------------------------------------------------------------------------------- /packages/pg-connection-string/index.d.ts: -------------------------------------------------------------------------------- 1 | import { ClientConfig } from 'pg' 2 | 3 | export function parse(connectionString: string, options?: Options): ConnectionOptions 4 | 5 | export interface Options { 6 | // Use libpq semantics when interpreting the connection string 7 | useLibpqCompat?: boolean 8 | } 9 | 10 | interface SSLConfig { 11 | ca?: string 12 | cert?: string | null 13 | key?: string 14 | rejectUnauthorized?: boolean 15 | } 16 | 17 | export interface ConnectionOptions { 18 | host: string | null 19 | password?: string 20 | user?: string 21 | port?: string | null 22 | database: string | null | undefined 23 | client_encoding?: string 24 | ssl?: boolean | string | SSLConfig 25 | 26 | application_name?: string 27 | fallback_application_name?: string 28 | options?: string 29 | keepalives?: number 30 | 31 | // We allow any other options to be passed through 32 | [key: string]: unknown 33 | } 34 | 35 | export function toClientConfig(config: ConnectionOptions): ClientConfig 36 | export function parseIntoClientConfig(connectionString: string): ClientConfig 37 | -------------------------------------------------------------------------------- /packages/pg-native/test/copy-to.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert') 2 | const Client = require('../') 3 | const concat = require('concat-stream') 4 | const _ = require('lodash') 5 | 6 | describe('COPY TO', function () { 7 | before(function (done) { 8 | this.client = Client() 9 | this.client.connect(done) 10 | }) 11 | 12 | after(function (done) { 13 | this.client.end(done) 14 | }) 15 | 16 | it('works - basic check', function (done) { 17 | const limit = 1000 18 | const qText = 'COPY (SELECT * FROM generate_series(0, ' + (limit - 1) + ')) TO stdout' 19 | const self = this 20 | this.client.query(qText, function (err) { 21 | if (err) return done(err) 22 | const stream = self.client.getCopyStream() 23 | // pump the stream for node v0.11.x 24 | stream.read() 25 | stream.pipe( 26 | concat(function (buff) { 27 | const res = buff.toString('utf8') 28 | const expected = _.range(0, limit).join('\n') + '\n' 29 | assert.equal(res, expected) 30 | done() 31 | }) 32 | ) 33 | }) 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /packages/pg/test/integration/gh-issues/981-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const helper = require('./../test-helper') 3 | 4 | //native bindings are only installed for native tests 5 | if (!helper.args.native) { 6 | return 7 | } 8 | 9 | const assert = require('assert') 10 | const pg = require('../../../lib') 11 | const native = require('../../../lib').native 12 | 13 | const JsClient = require('../../../lib/client') 14 | const NativeClient = require('../../../lib/native') 15 | 16 | assert(pg.Client === JsClient) 17 | assert(native.Client === NativeClient) 18 | 19 | const jsPool = new pg.Pool() 20 | const nativePool = new native.Pool() 21 | 22 | const suite = new helper.Suite() 23 | suite.test('js pool returns js client', (cb) => { 24 | jsPool.connect(function (err, client, done) { 25 | assert(client instanceof JsClient) 26 | done() 27 | jsPool.end(cb) 28 | }) 29 | }) 30 | 31 | suite.test('native pool returns native client', (cb) => { 32 | nativePool.connect(function (err, client, done) { 33 | assert(client instanceof NativeClient) 34 | done() 35 | nativePool.end(cb) 36 | }) 37 | }) 38 | -------------------------------------------------------------------------------- /docs/pages/features/esm.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: ESM 3 | --- 4 | 5 | ## ESM Support 6 | 7 | As of v8.15.x node-postgres supporters the __ECMAScript Module__ (ESM) format. This means you can use `import` statements instead of `require` or `import pg from 'pg'`. 8 | 9 | CommonJS modules are still supported. The ESM format is an opt-in feature and will not affect existing codebases that use CommonJS. 10 | 11 | The docs have been changed to show ESM usage, but in a CommonJS context you can still use the same code, you just need to change the import format. 12 | 13 | If you're using CommonJS, you can use the following code to import the `pg` module: 14 | 15 | ```js 16 | const pg = require('pg') 17 | const { Client } = pg 18 | // etc... 19 | ``` 20 | 21 | ### ESM Usage 22 | 23 | If you're using ESM, you can use the following code to import the `pg` module: 24 | 25 | ```js 26 | import { Client } from 'pg' 27 | // etc... 28 | ``` 29 | 30 | 31 | Previously if you were using ESM you would have to use the following code: 32 | 33 | ```js 34 | import pg from 'pg' 35 | const { Client } = pg 36 | // etc... 37 | ``` 38 | -------------------------------------------------------------------------------- /packages/pg/test/unit/client/query-queue-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const helper = require('./test-helper') 3 | const { Client } = helper 4 | const Connection = require('../../../lib/connection') 5 | const assert = require('assert') 6 | const suite = new helper.Suite() 7 | const test = suite.test.bind(suite) 8 | 9 | test('drain', function () { 10 | const con = new Connection({ stream: 'NO' }) 11 | const client = new Client({ connection: con }) 12 | con.connect = function () { 13 | con.emit('connect') 14 | } 15 | con.query = function () {} 16 | client.connect() 17 | 18 | let raisedDrain = false 19 | client.on('drain', function () { 20 | raisedDrain = true 21 | }) 22 | 23 | client.query('hello') 24 | client.query('sup') 25 | client.query('boom') 26 | assert.equal(raisedDrain, false) 27 | con.emit('readyForQuery') 28 | 29 | assert.equal(raisedDrain, false) 30 | con.emit('readyForQuery') 31 | con.emit('readyForQuery') 32 | assert.equal(raisedDrain, false) 33 | con.emit('readyForQuery') 34 | 35 | process.nextTick(function () { 36 | assert.ok(raisedDrain) 37 | }) 38 | }) 39 | -------------------------------------------------------------------------------- /packages/pg-query-stream/test/issue-3.ts: -------------------------------------------------------------------------------- 1 | import pg from 'pg' 2 | import QueryStream from '../src' 3 | 4 | describe('end semantics race condition', function () { 5 | before(function (done) { 6 | const client = new pg.Client() 7 | client.connect() 8 | client.on('drain', client.end.bind(client)) 9 | client.on('end', done) 10 | client.query('create table IF NOT EXISTS p(id serial primary key)') 11 | client.query('create table IF NOT EXISTS c(id int primary key references p)') 12 | }) 13 | it('works', function (done) { 14 | const client1 = new pg.Client() 15 | client1.connect() 16 | const client2 = new pg.Client() 17 | client2.connect() 18 | 19 | const qr = new QueryStream('INSERT INTO p DEFAULT VALUES RETURNING id') 20 | client1.query(qr) 21 | let id = null 22 | qr.on('data', function (row) { 23 | id = row.id 24 | }) 25 | qr.on('end', function () { 26 | client2.query('INSERT INTO c(id) VALUES ($1)', [id], function (err, rows) { 27 | client1.end() 28 | client2.end() 29 | done(err) 30 | }) 31 | }) 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /packages/pg/test/integration/client/no-data-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const helper = require('./test-helper') 3 | const suite = new helper.Suite() 4 | const assert = require('assert') 5 | 6 | suite.test('noData message handling', function () { 7 | const client = helper.client() 8 | 9 | client.query({ 10 | name: 'boom', 11 | text: 'create temp table boom(id serial, size integer)', 12 | }) 13 | 14 | client.query( 15 | { 16 | name: 'insert', 17 | text: 'insert into boom(size) values($1)', 18 | values: [100], 19 | }, 20 | function (err, result) { 21 | if (err) { 22 | console.log(err) 23 | throw err 24 | } 25 | } 26 | ) 27 | 28 | client.query({ 29 | name: 'insert', 30 | values: [101], 31 | }) 32 | 33 | client.query( 34 | { 35 | name: 'fetch', 36 | text: 'select size from boom where size < $1', 37 | values: [101], 38 | }, 39 | (err, res) => { 40 | const row = res.rows[0] 41 | assert.strictEqual(row.size, 100) 42 | } 43 | ) 44 | 45 | client.on('drain', client.end.bind(client)) 46 | }) 47 | -------------------------------------------------------------------------------- /packages/pg-query-stream/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Brian M. Carlson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /packages/pg/test/integration/gh-issues/1854-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const helper = require('./../test-helper') 3 | 4 | const suite = new helper.Suite() 5 | 6 | suite.test('Parameter serialization errors should not cause query to hang', (done) => { 7 | if (helper.config.native) { 8 | // pg-native cannot handle non-string parameters so skip this entirely 9 | return done() 10 | } 11 | const client = new helper.pg.Client() 12 | const expectedErr = new Error('Serialization error') 13 | client 14 | .connect() 15 | .then(() => { 16 | const obj = { 17 | toPostgres: function () { 18 | throw expectedErr 19 | }, 20 | } 21 | return client.query('SELECT $1::text', [obj]).then(() => { 22 | throw new Error('Expected a serialization error to be thrown but no error was thrown') 23 | }) 24 | }) 25 | .catch((err) => { 26 | client.end(() => {}) 27 | if (err !== expectedErr) { 28 | done(new Error('Expected a serialization error to be thrown but instead caught: ' + err)) 29 | return 30 | } 31 | done() 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /docs/pages/features/callbacks.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Callbacks 3 | --- 4 | 5 | ## Callback Support 6 | 7 | `async` / `await` is the preferred way to write async code these days with node, but callbacks are supported in the `pg` module and the `pg-pool` module. To use them, pass a callback function as the last argument to the following methods & it will be called and a promise will not be returned: 8 | 9 | 10 | ```js 11 | const { Pool, Client } = require('pg') 12 | 13 | // pool 14 | const pool = new Pool() 15 | // run a query on an available client 16 | pool.query('SELECT NOW()', (err, res) => { 17 | console.log(err, res) 18 | }) 19 | 20 | // check out a client to do something more complex like a transaction 21 | pool.connect((err, client, release) => { 22 | client.query('SELECT NOW()', (err, res) => { 23 | release() 24 | console.log(err, res) 25 | pool.end() 26 | }) 27 | 28 | }) 29 | 30 | // single client 31 | const client = new Client() 32 | client.connect((err) => { 33 | if (err) throw err 34 | client.query('SELECT NOW()', (err, res) => { 35 | console.log(err, res) 36 | client.end() 37 | }) 38 | }) 39 | ``` 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2010 - 2021 Brian Carlson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/pg-pool/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Brian M. Carlson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/pg/test/integration/gh-issues/2085-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 | // allow skipping of this test via env var for 8 | // local testing when you don't have SSL set up 9 | if (process.env.PGTESTNOSSL) { 10 | return 11 | } 12 | 13 | suite.testAsync('it should connect over ssl', async () => { 14 | const ssl = helper.args.native 15 | ? 'require' 16 | : { 17 | rejectUnauthorized: false, 18 | } 19 | const client = new helper.pg.Client({ ssl }) 20 | await client.connect() 21 | const { rows } = await client.query('SELECT NOW()') 22 | assert.strictEqual(rows.length, 1) 23 | await client.end() 24 | }) 25 | 26 | suite.testAsync('it should fail with self-signed cert error w/o rejectUnauthorized being passed', async () => { 27 | const ssl = helper.args.native ? 'verify-ca' : {} 28 | const client = new helper.pg.Client({ ssl }) 29 | try { 30 | await client.connect() 31 | } catch (e) { 32 | return 33 | } 34 | throw new Error('this test should have thrown an error due to self-signed cert') 35 | }) 36 | -------------------------------------------------------------------------------- /packages/pg-native/test/domains.js: -------------------------------------------------------------------------------- 1 | const Client = require('../') 2 | const assert = require('assert') 3 | 4 | const checkDomain = function (domain, when) { 5 | assert(process.domain, 'Domain was lost after ' + when) 6 | assert.strictEqual(process.domain, domain, 'Domain switched after ' + when) 7 | } 8 | 9 | describe('domains', function () { 10 | it('remains bound after a query', function (done) { 11 | const domain = require('domain').create() 12 | domain.run(function () { 13 | const client = new Client() 14 | client.connect(function () { 15 | checkDomain(domain, 'connection') 16 | client.query('SELECT NOW()', function () { 17 | checkDomain(domain, 'query') 18 | client.prepare('testing', 'SELECT NOW()', 0, function () { 19 | checkDomain(domain, 'prepare') 20 | client.execute('testing', [], function () { 21 | checkDomain(domain, 'execute') 22 | client.end(function () { 23 | checkDomain(domain, 'end') 24 | done() 25 | }) 26 | }) 27 | }) 28 | }) 29 | }) 30 | }) 31 | }) 32 | }) 33 | -------------------------------------------------------------------------------- /packages/pg/test/unit/client/cleartext-password-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const helper = require('./test-helper') 4 | const createClient = require('./test-helper').createClient 5 | const assert = require('assert') 6 | const suite = new helper.Suite() 7 | const { MemoryStream } = helper 8 | 9 | suite.test('cleartext password auth responds with password', function () { 10 | const client = createClient() 11 | client.password = '!' 12 | client.connection.stream.packets = [] 13 | client.connection.emit('authenticationCleartextPassword') 14 | const packets = client.connection.stream.packets 15 | assert.lengthIs(packets, 1) 16 | const packet = packets[0] 17 | assert.equalBuffers(packet, [0x70, 0, 0, 0, 6, 33, 0]) 18 | }) 19 | 20 | suite.test('cleartext password auth does not crash with null password using pg-pass', function () { 21 | process.env.PGPASSFILE = `${__dirname}/pgpass.file` 22 | const client = new helper.Client({ 23 | host: 'foo', 24 | port: 5432, 25 | database: 'bar', 26 | user: 'baz', 27 | stream: new MemoryStream(), 28 | }) 29 | client.connect() 30 | client.connection.emit('authenticationCleartextPassword') 31 | }) 32 | -------------------------------------------------------------------------------- /packages/pg/test/unit/test-helper.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const EventEmitter = require('events').EventEmitter 3 | 4 | const helper = require('../test-helper') 5 | const Connection = require('../../lib/connection') 6 | const { Client } = helper 7 | 8 | const MemoryStream = function () { 9 | EventEmitter.call(this) 10 | this.packets = [] 11 | } 12 | 13 | helper.sys.inherits(MemoryStream, EventEmitter) 14 | 15 | const p = MemoryStream.prototype 16 | 17 | p.connect = function () { 18 | // NOOP 19 | } 20 | 21 | p.setNoDelay = () => {} 22 | 23 | p.write = function (packet, cb) { 24 | this.packets.push(packet) 25 | if (cb) { 26 | cb() 27 | } 28 | } 29 | 30 | p.end = function () { 31 | p.closed = true 32 | } 33 | 34 | p.setKeepAlive = function () {} 35 | p.closed = false 36 | p.writable = true 37 | 38 | const createClient = function () { 39 | const stream = new MemoryStream() 40 | const client = new Client({ 41 | connection: new Connection({ stream: stream }), 42 | }) 43 | client.connect() 44 | return client 45 | } 46 | 47 | module.exports = Object.assign({}, helper, { 48 | createClient: createClient, 49 | MemoryStream: MemoryStream, 50 | }) 51 | -------------------------------------------------------------------------------- /packages/pg-connection-string/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Iced Development 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /packages/pg/test/unit/client/stream-and-query-error-interaction-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const helper = require('./test-helper') 3 | const Connection = require('../../../lib/connection') 4 | const Client = require('../../../lib/client') 5 | const assert = require('assert') 6 | const suite = new helper.Suite() 7 | 8 | suite.test('emits end when not in query', function () { 9 | const stream = new (require('events').EventEmitter)() 10 | stream.setNoDelay = () => {} 11 | stream.connect = function () { 12 | // NOOP 13 | } 14 | stream.write = function () { 15 | // NOOP 16 | } 17 | 18 | const client = new Client({ connection: new Connection({ stream: stream }) }) 19 | client.connect( 20 | assert.calls(function () { 21 | client.query( 22 | 'SELECT NOW()', 23 | assert.calls(function (err, result) { 24 | assert(err) 25 | }) 26 | ) 27 | }) 28 | ) 29 | assert.emits(client, 'error') 30 | assert.emits(client, 'end') 31 | client.connection.emit('connect') 32 | process.nextTick(function () { 33 | client.connection.emit('readyForQuery') 34 | process.nextTick(function () { 35 | stream.emit('close') 36 | }) 37 | }) 38 | }) 39 | -------------------------------------------------------------------------------- /packages/pg/test/integration/client/results-as-array-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const util = require('util') 3 | const helper = require('./test-helper') 4 | const assert = require('assert') 5 | const suite = new helper.Suite() 6 | 7 | const Client = helper.Client 8 | 9 | const conInfo = helper.config 10 | 11 | suite.test('returns results as array', function () { 12 | const client = new Client(conInfo) 13 | const checkRow = function (row) { 14 | assert(util.isArray(row), 'row should be an array') 15 | assert.equal(row.length, 4) 16 | assert.equal(row[0].getFullYear(), new Date().getFullYear()) 17 | assert.strictEqual(row[1], 1) 18 | assert.strictEqual(row[2], 'hai') 19 | assert.strictEqual(row[3], null) 20 | } 21 | client.connect( 22 | assert.success(function () { 23 | const config = { 24 | text: 'SELECT NOW(), 1::int, $1::text, null', 25 | values: ['hai'], 26 | rowMode: 'array', 27 | } 28 | client.query( 29 | config, 30 | assert.success(function (result) { 31 | assert.equal(result.rows.length, 1) 32 | checkRow(result.rows[0]) 33 | client.end() 34 | }) 35 | ) 36 | }) 37 | ) 38 | }) 39 | -------------------------------------------------------------------------------- /packages/pg/test/integration/connection-pool/connection-pool-size-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 | const testPoolSize = function (max) { 8 | suite.testAsync(`test ${max} queries executed on a pool rapidly`, async () => { 9 | const pool = new helper.pg.Pool({ max: 10 }) 10 | 11 | let count = 0 12 | 13 | return new Promise((resolve) => { 14 | for (let i = 0; i < max; i++) { 15 | pool.connect(function (err, client, release) { 16 | assert(!err) 17 | client.query('SELECT * FROM NOW()') 18 | client.query('select generate_series(0, 25)', function (err, result) { 19 | assert.strictEqual(result.rows.length, 26) 20 | }) 21 | client.query('SELECT * FROM NOW()', (err) => { 22 | assert(!err) 23 | release() 24 | if (++count === max) { 25 | resolve() 26 | pool.end() 27 | } 28 | }) 29 | }) 30 | } 31 | }) 32 | }) 33 | } 34 | 35 | testPoolSize(1) 36 | 37 | testPoolSize(2) 38 | 39 | testPoolSize(40) 40 | 41 | testPoolSize(200) 42 | -------------------------------------------------------------------------------- /LOCAL_DEV.md: -------------------------------------------------------------------------------- 1 | # Local development 2 | 3 | Steps to install and configure Postgres on Mac for developing against locally 4 | 5 | 1. Install homebrew 6 | 2. Install postgres 7 | ```sh 8 | brew install postgresql 9 | ``` 10 | 3. Create a database 11 | ```sh 12 | createdb test 13 | ``` 14 | 4. Create SSL certificates 15 | ```sh 16 | cd /opt/homebrew/var/postgresql@14 17 | openssl genrsa -aes128 2048 > server.key 18 | openssl rsa -in server.key -out server.key 19 | chmod 400 server.key 20 | openssl req -new -key server.key -days 365 -out server.crt -x509 21 | cp server.crt root.crt 22 | ``` 23 | 5. Update config in `/opt/homebrew/var/postgresql@14/postgresql.conf` 24 | 25 | ```conf 26 | listen_addresses = '*' 27 | 28 | password_encryption = md5 29 | 30 | ssl = on 31 | ssl_ca_file = 'root.crt' 32 | ssl_cert_file = 'server.crt' 33 | ssl_crl_file = '' 34 | ssl_crl_dir = '' 35 | ssl_key_file = 'server.key' 36 | ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers 37 | ssl_prefer_server_ciphers = on 38 | ``` 39 | 40 | 6. Start Postgres server 41 | ```sh 42 | /opt/homebrew/opt/postgresql@14/bin/postgres -D /opt/homebrew/var/postgresql@14 43 | ``` 44 | -------------------------------------------------------------------------------- /packages/pg/test/unit/client/md5-password-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const helper = require('./test-helper') 3 | const BufferList = require('../../buffer-list') 4 | const crypto = require('../../../lib/crypto/utils') 5 | const assert = require('assert') 6 | const suite = new helper.Suite() 7 | const test = suite.test.bind(suite) 8 | 9 | test('md5 authentication', async function () { 10 | const client = helper.createClient() 11 | client.password = '!' 12 | const salt = Buffer.from([1, 2, 3, 4]) 13 | await client.connection.emit('authenticationMD5Password', { salt: salt }) 14 | 15 | setTimeout(() => 16 | test('responds', function () { 17 | assert.lengthIs(client.connection.stream.packets, 1) 18 | test('should have correct encrypted data', async function () { 19 | const password = await crypto.postgresMd5PasswordHash(client.user, client.password, salt) 20 | // how do we want to test this? 21 | assert.equalBuffers(client.connection.stream.packets[0], new BufferList().addCString(password).join(true, 'p')) 22 | }) 23 | }) 24 | ) 25 | }) 26 | 27 | test('md5 of utf-8 strings', async function () { 28 | assert.equal(await crypto.md5('😊'), '5deda34cd95f304948d2bc1b4a62c11e') 29 | }) 30 | -------------------------------------------------------------------------------- /packages/pg-query-stream/test/fast-reader.ts: -------------------------------------------------------------------------------- 1 | import assert from 'assert' 2 | import helper from './helper' 3 | import QueryStream from '../src' 4 | 5 | helper('fast reader', function (client) { 6 | it('works', function (done) { 7 | const stream = new QueryStream('SELECT * FROM generate_series(0, 200) num', []) 8 | const query = client.query(stream) 9 | const result = [] 10 | stream.on('readable', function () { 11 | let res = stream.read() 12 | while (res) { 13 | if (result.length !== 201) { 14 | assert(res, 'should not return null on evented reader') 15 | } else { 16 | // a readable stream will emit a null datum when it finishes being readable 17 | // https://nodejs.org/api/stream.html#stream_event_readable 18 | assert.equal(res, null) 19 | } 20 | if (res) { 21 | result.push(res.num) 22 | } 23 | res = stream.read() 24 | } 25 | }) 26 | stream.on('end', function () { 27 | const total = result.reduce(function (prev, cur) { 28 | return prev + cur 29 | }) 30 | assert.equal(total, 20100) 31 | done() 32 | }) 33 | assert.strictEqual(query.read(2), null) 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /packages/pg-bundler-test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pg-bundler-test", 3 | "version": "0.0.2", 4 | "description": "Test bundlers with pg-cloudflare, https://github.com/brianc/node-postgres/issues/3452", 5 | "license": "MIT", 6 | "private": true, 7 | "type": "module", 8 | "devDependencies": { 9 | "@rollup/plugin-commonjs": "^28.0.3", 10 | "@rollup/plugin-node-resolve": "^16.0.1", 11 | "esbuild": "^0.25.5", 12 | "pg-cloudflare": "^1.2.7", 13 | "rollup": "^4.41.1", 14 | "vite": "^6.3.5", 15 | "webpack": "^5.99.9", 16 | "webpack-cli": "^6.0.1" 17 | }, 18 | "scripts": { 19 | "test": "yarn webpack && yarn rollup && yarn vite && yarn esbuild", 20 | "webpack": "webpack --config webpack-empty.config.mjs && webpack --config webpack-cloudflare.config.mjs", 21 | "rollup": "rollup --config rollup-empty.config.mjs --failAfterWarnings && rollup --config rollup-cloudflare.config.mjs --failAfterWarnings", 22 | "vite": "[ $(node --version | sed 's/v//' | cut -d'.' -f1) -ge 18 ] && vite build --config vite-empty.config.mjs && vite build --config vite-cloudflare.config.mjs || echo 'Skip Vite test'", 23 | "esbuild": "node esbuild-empty.config.mjs && node esbuild-cloudflare.config.mjs" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/pg/test/integration/gh-issues/2064-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const helper = require('./../test-helper') 3 | const assert = require('assert') 4 | const util = require('util') 5 | 6 | const suite = new helper.Suite() 7 | 8 | const password = 'FAIL THIS TEST' 9 | 10 | suite.test('Password should not exist in toString() output', () => { 11 | const pool = new helper.pg.Pool({ password }) 12 | const client = new helper.pg.Client({ password }) 13 | assert(pool.toString().indexOf(password) === -1) 14 | assert(client.toString().indexOf(password) === -1) 15 | }) 16 | 17 | suite.test('Password should not exist in util.inspect output', () => { 18 | const pool = new helper.pg.Pool({ password }) 19 | const client = new helper.pg.Client({ password }) 20 | const depth = 20 21 | assert(util.inspect(pool, { depth }).indexOf(password) === -1) 22 | assert(util.inspect(client, { depth }).indexOf(password) === -1) 23 | }) 24 | 25 | suite.test('Password should not exist in json.stringfy output', () => { 26 | const pool = new helper.pg.Pool({ password }) 27 | const client = new helper.pg.Client({ password }) 28 | assert(JSON.stringify(pool).indexOf(password) === -1) 29 | assert(JSON.stringify(client).indexOf(password) === -1) 30 | }) 31 | -------------------------------------------------------------------------------- /packages/pg-cursor/test/query-config.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const assert = require('assert') 3 | const Cursor = require('../') 4 | const pg = require('pg') 5 | 6 | describe('query config passed to result', () => { 7 | it('passes rowMode to result', (done) => { 8 | const client = new pg.Client() 9 | client.connect() 10 | const text = 'SELECT generate_series as num FROM generate_series(0, 5)' 11 | const cursor = client.query(new Cursor(text, null, { rowMode: 'array' })) 12 | cursor.read(10, (err, rows) => { 13 | assert(!err) 14 | assert.deepStrictEqual(rows, [[0], [1], [2], [3], [4], [5]]) 15 | client.end() 16 | done() 17 | }) 18 | }) 19 | 20 | it('passes types to result', (done) => { 21 | const client = new pg.Client() 22 | client.connect() 23 | const text = 'SELECT generate_series as num FROM generate_series(0, 2)' 24 | const types = { 25 | getTypeParser: () => () => 'foo', 26 | } 27 | const cursor = client.query(new Cursor(text, null, { types })) 28 | cursor.read(10, (err, rows) => { 29 | assert(!err) 30 | assert.deepStrictEqual(rows, [{ num: 'foo' }, { num: 'foo' }, { num: 'foo' }]) 31 | client.end() 32 | done() 33 | }) 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /packages/pg/test/integration/gh-issues/131-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const helper = require('../test-helper') 3 | const pg = helper.pg 4 | const assert = require('assert') 5 | 6 | const suite = new helper.Suite() 7 | 8 | suite.test('parsing array decimal results', function (done) { 9 | const pool = new pg.Pool() 10 | pool.connect( 11 | assert.calls(function (err, client, release) { 12 | assert(!err) 13 | client.query('CREATE TEMP TABLE why(names text[], numbors integer[], decimals double precision[])') 14 | client 15 | .query( 16 | new pg.Query( 17 | 'INSERT INTO why(names, numbors, decimals) VALUES(\'{"aaron", "brian","a b c" }\', \'{1, 2, 3}\', \'{.1, 0.05, 3.654}\')' 18 | ) 19 | ) 20 | .on('error', console.log) 21 | client.query( 22 | 'SELECT decimals FROM why', 23 | assert.success(function (result) { 24 | assert.lengthIs(result.rows[0].decimals, 3) 25 | assert.equal(result.rows[0].decimals[0], 0.1) 26 | assert.equal(result.rows[0].decimals[1], 0.05) 27 | assert.equal(result.rows[0].decimals[2], 3.654) 28 | release() 29 | pool.end(done) 30 | }) 31 | ) 32 | }) 33 | ) 34 | }) 35 | -------------------------------------------------------------------------------- /packages/pg-protocol/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pg-protocol", 3 | "version": "1.10.3", 4 | "description": "The postgres client/server binary protocol, implemented in TypeScript", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "exports": { 8 | ".": { 9 | "import": "./esm/index.js", 10 | "require": "./dist/index.js", 11 | "default": "./dist/index.js" 12 | }, 13 | "./dist/*": "./dist/*.js", 14 | "./dist/*.js": "./dist/*.js" 15 | }, 16 | "license": "MIT", 17 | "devDependencies": { 18 | "@types/chai": "^4.2.7", 19 | "@types/mocha": "^10.0.7", 20 | "@types/node": "^12.12.21", 21 | "chai": "^4.2.0", 22 | "chunky": "^0.0.0", 23 | "mocha": "^10.5.2", 24 | "ts-node": "^8.5.4", 25 | "typescript": "^4.0.3" 26 | }, 27 | "scripts": { 28 | "test": "mocha dist/**/*.test.js", 29 | "build": "tsc", 30 | "build:watch": "tsc --watch", 31 | "prepublish": "yarn build", 32 | "pretest": "yarn build" 33 | }, 34 | "repository": { 35 | "type": "git", 36 | "url": "git://github.com/brianc/node-postgres.git", 37 | "directory": "packages/pg-protocol" 38 | }, 39 | "files": [ 40 | "/dist/*{js,ts,map}", 41 | "/src", 42 | "/esm" 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /packages/pg-pool/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pg-pool", 3 | "version": "3.10.1", 4 | "description": "Connection pool for node-postgres", 5 | "main": "index.js", 6 | "exports": { 7 | ".": { 8 | "import": "./esm/index.mjs", 9 | "require": "./index.js", 10 | "default": "./index.js" 11 | } 12 | }, 13 | "directories": { 14 | "test": "test" 15 | }, 16 | "scripts": { 17 | "test": " node_modules/.bin/mocha" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "git://github.com/brianc/node-postgres.git", 22 | "directory": "packages/pg-pool" 23 | }, 24 | "keywords": [ 25 | "pg", 26 | "postgres", 27 | "pool", 28 | "database" 29 | ], 30 | "author": "Brian M. Carlson", 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-pool#readme", 36 | "devDependencies": { 37 | "bluebird": "3.7.2", 38 | "co": "4.6.0", 39 | "expect.js": "0.3.1", 40 | "lodash": "^4.17.11", 41 | "mocha": "^10.5.2" 42 | }, 43 | "peerDependencies": { 44 | "pg": ">=8.0" 45 | }, 46 | "files": [ 47 | "index.js", 48 | "esm" 49 | ] 50 | } 51 | -------------------------------------------------------------------------------- /packages/pg-native/bench/index.js: -------------------------------------------------------------------------------- 1 | const pg = require('pg').native 2 | const Native = require('../') 3 | 4 | const warmup = function (fn, cb) { 5 | let count = 0 6 | const max = 10 7 | const run = function (err) { 8 | if (err) return cb(err) 9 | 10 | if (max >= count++) { 11 | return fn(run) 12 | } 13 | 14 | cb() 15 | } 16 | run() 17 | } 18 | 19 | const native = Native() 20 | native.connectSync() 21 | 22 | const queryText = 'SELECT generate_series(0, 1000) as X, generate_series(0, 1000) as Y, generate_series(0, 1000) as Z' 23 | const client = new pg.Client() 24 | client.connect(function () { 25 | const pure = function (cb) { 26 | client.query(queryText, function (err) { 27 | if (err) throw err 28 | cb(err) 29 | }) 30 | } 31 | const nativeQuery = function (cb) { 32 | native.query(queryText, function (err) { 33 | if (err) throw err 34 | cb(err) 35 | }) 36 | } 37 | 38 | const run = function () { 39 | console.time('pure') 40 | warmup(pure, function () { 41 | console.timeEnd('pure') 42 | console.time('native') 43 | warmup(nativeQuery, function () { 44 | console.timeEnd('native') 45 | }) 46 | }) 47 | } 48 | 49 | setInterval(function () { 50 | run() 51 | }, 500) 52 | }) 53 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-postgres", 3 | "description": "node postgres monorepo", 4 | "main": "index.js", 5 | "private": true, 6 | "repository": "git@github.com:brianc/node-postgres.git", 7 | "author": "Brian M. Carlson ", 8 | "license": "MIT", 9 | "workspaces": [ 10 | "packages/*" 11 | ], 12 | "scripts": { 13 | "test": "yarn lerna exec --concurrency 1 yarn test", 14 | "build": "tsc --build", 15 | "build:watch": "tsc --build --watch", 16 | "docs:build": "cd docs && yarn build", 17 | "docs:start": "cd docs && yarn start", 18 | "pretest": "yarn build", 19 | "prepublish": "yarn build", 20 | "lint": "eslint --cache 'packages/**/*.{js,ts,tsx}'" 21 | }, 22 | "devDependencies": { 23 | "@typescript-eslint/eslint-plugin": "^7.0.0", 24 | "@typescript-eslint/parser": "^6.17.0", 25 | "eslint": "^8.56.0", 26 | "eslint-config-prettier": "^10.1.2", 27 | "eslint-plugin-node": "^11.1.0", 28 | "eslint-plugin-prettier": "^5.1.2", 29 | "lerna": "^3.19.0", 30 | "prettier": "3.0.3", 31 | "typescript": "^4.0.3" 32 | }, 33 | "prettier": { 34 | "semi": false, 35 | "printWidth": 120, 36 | "arrowParens": "always", 37 | "trailingComma": "es5", 38 | "singleQuote": true 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /packages/pg/test/integration/client/timezone-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const helper = require('./../test-helper') 3 | const assert = require('assert') 4 | 5 | const oldTz = process.env.TZ 6 | process.env.TZ = 'Europe/Berlin' 7 | 8 | const date = new Date() 9 | 10 | const pool = new helper.pg.Pool() 11 | const suite = new helper.Suite() 12 | 13 | pool.connect(function (err, client, done) { 14 | assert(!err) 15 | 16 | suite.test('timestamp without time zone', function (cb) { 17 | client.query('SELECT CAST($1 AS TIMESTAMP WITHOUT TIME ZONE) AS "val"', [date], function (err, result) { 18 | assert(!err) 19 | assert.equal(result.rows[0].val.getTime(), date.getTime()) 20 | cb() 21 | }) 22 | }) 23 | 24 | suite.testAsync('date comes out as a date', async function () { 25 | const { rows } = await client.query('SELECT NOW()::DATE AS date') 26 | assert(rows[0].date instanceof Date) 27 | }) 28 | 29 | suite.test('timestamp with time zone', function (cb) { 30 | client.query('SELECT CAST($1 AS TIMESTAMP WITH TIME ZONE) AS "val"', [date], function (err, result) { 31 | assert(!err) 32 | assert.equal(result.rows[0].val.getTime(), date.getTime()) 33 | 34 | done() 35 | pool.end(cb) 36 | process.env.TZ = oldTz 37 | }) 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /packages/pg-query-stream/test/passing-options.ts: -------------------------------------------------------------------------------- 1 | import assert from 'assert' 2 | import helper from './helper' 3 | import QueryStream from '../src' 4 | 5 | helper('passing options', function (client) { 6 | it('passes row mode array', function (done) { 7 | const stream = new QueryStream('SELECT * FROM generate_series(0, 10) num', [], { rowMode: 'array' }) 8 | const query = client.query(stream) 9 | const result = [] 10 | query.on('data', (datum) => { 11 | result.push(datum) 12 | }) 13 | query.on('end', () => { 14 | const expected = new Array(11).fill(0).map((_, i) => [i]) 15 | assert.deepEqual(result, expected) 16 | done() 17 | }) 18 | }) 19 | 20 | it('passes custom types', function (done) { 21 | const types = { 22 | getTypeParser: () => (string) => string, 23 | } 24 | const stream = new QueryStream('SELECT * FROM generate_series(0, 10) num', [], { types }) 25 | const query = client.query(stream) 26 | const result = [] 27 | query.on('data', (datum) => { 28 | result.push(datum) 29 | }) 30 | query.on('end', () => { 31 | const expected = new Array(11).fill(0).map((_, i) => ({ 32 | num: i.toString(), 33 | })) 34 | assert.deepEqual(result, expected) 35 | done() 36 | }) 37 | }) 38 | }) 39 | -------------------------------------------------------------------------------- /packages/pg/test/integration/client/promise-api-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const helper = require('./test-helper') 4 | const pg = helper.pg 5 | const assert = require('assert') 6 | 7 | const suite = new helper.Suite() 8 | 9 | suite.test('valid connection completes promise', () => { 10 | const client = new pg.Client() 11 | return client.connect().then(() => { 12 | return client.end().then(() => {}) 13 | }) 14 | }) 15 | 16 | suite.test('valid connection completes promise', () => { 17 | const client = new pg.Client() 18 | return client.connect().then(() => { 19 | return client.end().then(() => {}) 20 | }) 21 | }) 22 | 23 | suite.test('invalid connection rejects promise', (done) => { 24 | const client = new pg.Client({ host: 'alksdjflaskdfj', port: 1234 }) 25 | return client.connect().catch((e) => { 26 | assert(e instanceof Error) 27 | done() 28 | }) 29 | }) 30 | 31 | suite.test('connected client does not reject promise after connection', (done) => { 32 | const client = new pg.Client() 33 | return client.connect().then(() => { 34 | setTimeout(() => { 35 | client.on('error', (e) => { 36 | assert(e instanceof Error) 37 | client.end() 38 | done() 39 | }) 40 | // manually kill the connection 41 | client.emit('error', new Error('something bad happened...but not really')) 42 | }, 50) 43 | }) 44 | }) 45 | -------------------------------------------------------------------------------- /packages/pg/test/native/callback-api-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const domain = require('domain') 3 | const helper = require('./../test-helper') 4 | const Client = require('./../../lib/native') 5 | const suite = new helper.Suite() 6 | const assert = require('assert') 7 | 8 | suite.test('fires callback with results', function (done) { 9 | const client = new Client(helper.config) 10 | client.connect() 11 | client.query( 12 | 'SELECT 1 as num', 13 | assert.calls(function (err, result) { 14 | assert(!err) 15 | assert.equal(result.rows[0].num, 1) 16 | assert.strictEqual(result.rowCount, 1) 17 | client.query( 18 | 'SELECT * FROM person WHERE name = $1', 19 | ['Brian'], 20 | assert.calls(function (err, result) { 21 | assert(!err) 22 | assert.equal(result.rows[0].name, 'Brian') 23 | client.end(done) 24 | }) 25 | ) 26 | }) 27 | ) 28 | }) 29 | 30 | suite.test('preserves domain', function (done) { 31 | const dom = domain.create() 32 | 33 | dom.run(function () { 34 | const client = new Client(helper.config) 35 | assert.ok(dom === require('domain').active, 'domain is active') 36 | client.connect() 37 | client.query('select 1', function () { 38 | assert.ok(dom === require('domain').active, 'domain is still active') 39 | client.end(done) 40 | }) 41 | }) 42 | }) 43 | -------------------------------------------------------------------------------- /packages/pg-native/test/many-connections.js: -------------------------------------------------------------------------------- 1 | const Client = require('../') 2 | const async = require('async') 3 | const ok = require('okay') 4 | const bytes = require('crypto').pseudoRandomBytes 5 | 6 | describe('many connections', function () { 7 | describe('async', function () { 8 | const test = function (count, times) { 9 | it(`connecting ${count} clients ${times} times`, function (done) { 10 | this.timeout(200000) 11 | 12 | const connectClient = function (n, cb) { 13 | const client = new Client() 14 | client.connect( 15 | ok(cb, function () { 16 | bytes( 17 | 1000, 18 | ok(cb, function (chunk) { 19 | client.query( 20 | 'SELECT $1::text as txt', 21 | [chunk.toString('base64')], 22 | ok(cb, function (rows) { 23 | client.end(cb) 24 | }) 25 | ) 26 | }) 27 | ) 28 | }) 29 | ) 30 | } 31 | 32 | const run = function (n, cb) { 33 | async.times(count, connectClient, cb) 34 | } 35 | 36 | async.timesSeries(times, run, done) 37 | }) 38 | } 39 | 40 | test(1, 1) 41 | test(5, 5) 42 | test(10, 10) 43 | test(20, 20) 44 | test(30, 10) 45 | }) 46 | }) 47 | -------------------------------------------------------------------------------- /packages/pg/test/integration/gh-issues/2556-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const helper = require('./../test-helper') 3 | const assert = require('assert') 4 | 5 | const callbackError = new Error('TEST: Throw in callback') 6 | 7 | const suite = new helper.Suite() 8 | 9 | suite.test('it should cleanup client even if an error is thrown in a callback', (done) => { 10 | // temporarily replace the test framework's uncaughtException handlers 11 | // with a custom one that ignores the callbackError 12 | const original_handlers = process.listeners('uncaughtException') 13 | process.removeAllListeners('uncaughtException') 14 | process.on('uncaughtException', (err) => { 15 | if (err != callbackError) { 16 | original_handlers[0](err) 17 | } 18 | }) 19 | 20 | // throw an error in a callback and verify that a subsequent query works without error 21 | const client = helper.client() 22 | client.query('SELECT NOW()', (err) => { 23 | assert(!err) 24 | setTimeout(reuseClient, 50) 25 | throw callbackError 26 | }) 27 | 28 | function reuseClient() { 29 | client.query('SELECT NOW()', (err) => { 30 | assert(!err) 31 | 32 | // restore the test framework's uncaughtException handlers 33 | for (const handler of original_handlers) { 34 | process.on('uncaughtException', handler) 35 | } 36 | 37 | client.end(done) 38 | }) 39 | } 40 | }) 41 | -------------------------------------------------------------------------------- /packages/pg-esm-test/pg.test.js: -------------------------------------------------------------------------------- 1 | import assert from 'node:assert' 2 | import { describe, it } from 'node:test' 3 | import pg, { 4 | Client, 5 | Pool, 6 | Connection, 7 | defaults, 8 | types, 9 | DatabaseError, 10 | escapeIdentifier, 11 | escapeLiteral, 12 | Result, 13 | TypeOverrides, 14 | } from 'pg' 15 | 16 | describe('pg', () => { 17 | it('should export Client constructor', () => { 18 | assert.ok(new Client()) 19 | }) 20 | 21 | it('should export Pool constructor', () => { 22 | assert.ok(new Pool()) 23 | }) 24 | 25 | it('should still provide default export', () => { 26 | assert.ok(new pg.Pool()) 27 | }) 28 | 29 | it('should export Connection constructor', () => { 30 | assert.ok(new Connection()) 31 | }) 32 | 33 | it('should export defaults', () => { 34 | assert.ok(defaults) 35 | }) 36 | 37 | it('should export types', () => { 38 | assert.ok(types) 39 | }) 40 | 41 | it('should export DatabaseError', () => { 42 | assert.ok(DatabaseError) 43 | }) 44 | 45 | it('should export escapeIdentifier', () => { 46 | assert.ok(escapeIdentifier) 47 | }) 48 | 49 | it('should export escapeLiteral', () => { 50 | assert.ok(escapeLiteral) 51 | }) 52 | 53 | it('should export Result', () => { 54 | assert.ok(Result) 55 | }) 56 | 57 | it('should export TypeOverrides', () => { 58 | assert.ok(TypeOverrides) 59 | }) 60 | }) 61 | -------------------------------------------------------------------------------- /packages/pg-native/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pg-native", 3 | "version": "3.5.2", 4 | "description": "A slightly nicer interface to Postgres over node-libpq", 5 | "main": "index.js", 6 | "exports": { 7 | ".": { 8 | "import": "./esm/index.mjs", 9 | "require": "./index.js", 10 | "default": "./index.js" 11 | }, 12 | "./lib/*": { 13 | "import": "./lib/*", 14 | "require": "./lib/*", 15 | "default": "./lib/*" 16 | } 17 | }, 18 | "scripts": { 19 | "test": "mocha" 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "https://github.com/brianc/node-postgres.git" 24 | }, 25 | "keywords": [ 26 | "postgres", 27 | "pg", 28 | "libpq" 29 | ], 30 | "author": "Brian M. Carlson", 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-native", 36 | "dependencies": { 37 | "libpq": "^1.8.15", 38 | "pg-types": "2.2.0" 39 | }, 40 | "devDependencies": { 41 | "async": "^0.9.0", 42 | "concat-stream": "^1.4.6", 43 | "generic-pool": "^2.1.1", 44 | "lodash": "^4.17.21", 45 | "mocha": "10.5.2", 46 | "node-gyp": ">=10.x", 47 | "okay": "^0.3.0", 48 | "semver": "^7.7.2" 49 | }, 50 | "files": [ 51 | "index.js", 52 | "lib", 53 | "esm" 54 | ] 55 | } 56 | -------------------------------------------------------------------------------- /packages/pg/lib/crypto/utils-legacy.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | // This file contains crypto utility functions for versions of Node.js < 15.0.0, 3 | // which does not support the WebCrypto.subtle API. 4 | 5 | const nodeCrypto = require('crypto') 6 | 7 | function md5(string) { 8 | return nodeCrypto.createHash('md5').update(string, 'utf-8').digest('hex') 9 | } 10 | 11 | // See AuthenticationMD5Password at https://www.postgresql.org/docs/current/static/protocol-flow.html 12 | function postgresMd5PasswordHash(user, password, salt) { 13 | const inner = md5(password + user) 14 | const outer = md5(Buffer.concat([Buffer.from(inner), salt])) 15 | return 'md5' + outer 16 | } 17 | 18 | function sha256(text) { 19 | return nodeCrypto.createHash('sha256').update(text).digest() 20 | } 21 | 22 | function hashByName(hashName, text) { 23 | hashName = hashName.replace(/(\D)-/, '$1') // e.g. SHA-256 -> SHA256 24 | return nodeCrypto.createHash(hashName).update(text).digest() 25 | } 26 | 27 | function hmacSha256(key, msg) { 28 | return nodeCrypto.createHmac('sha256', key).update(msg).digest() 29 | } 30 | 31 | async function deriveKey(password, salt, iterations) { 32 | return nodeCrypto.pbkdf2Sync(password, salt, iterations, 32, 'sha256') 33 | } 34 | 35 | module.exports = { 36 | postgresMd5PasswordHash, 37 | randomBytes: nodeCrypto.randomBytes, 38 | deriveKey, 39 | sha256, 40 | hashByName, 41 | hmacSha256, 42 | md5, 43 | } 44 | -------------------------------------------------------------------------------- /packages/pg-native/test/multiple-queries.js: -------------------------------------------------------------------------------- 1 | const Client = require('../') 2 | const assert = require('assert') 3 | 4 | describe('multiple commands in a single query', function () { 5 | before(function (done) { 6 | this.client = new Client() 7 | this.client.connect(done) 8 | }) 9 | 10 | after(function (done) { 11 | this.client.end(done) 12 | }) 13 | 14 | it('all execute to completion', function (done) { 15 | this.client.query("SELECT '10'::int as num; SELECT 'brian'::text as name", function (err, rows) { 16 | assert.ifError(err) 17 | assert.equal(rows.length, 2, 'should return two sets rows') 18 | assert.equal(rows[0][0].num, '10') 19 | assert.equal(rows[1][0].name, 'brian') 20 | done() 21 | }) 22 | }) 23 | 24 | it('inserts and reads at once', function (done) { 25 | let txt = 'CREATE TEMP TABLE boom(age int);' 26 | txt += 'INSERT INTO boom(age) VALUES(10);' 27 | txt += 'SELECT * FROM boom;' 28 | this.client.query(txt, function (err, rows, results) { 29 | assert.ifError(err) 30 | assert.equal(rows.length, 3) 31 | assert.equal(rows[0].length, 0) 32 | assert.equal(rows[1].length, 0) 33 | assert.equal(rows[2][0].age, 10) 34 | 35 | assert.equal(results[0].command, 'CREATE') 36 | assert.equal(results[1].command, 'INSERT') 37 | assert.equal(results[2].command, 'SELECT') 38 | done() 39 | }) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /packages/pg-pool/test/ending.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('pool ending', () => { 11 | it('ends without being used', (done) => { 12 | const pool = new Pool() 13 | pool.end(done) 14 | }) 15 | 16 | it('ends with a promise', () => { 17 | return new Pool().end() 18 | }) 19 | 20 | it( 21 | 'ends with clients', 22 | co.wrap(function* () { 23 | const pool = new Pool() 24 | const res = yield pool.query('SELECT $1::text as name', ['brianc']) 25 | expect(res.rows[0].name).to.equal('brianc') 26 | return pool.end() 27 | }) 28 | ) 29 | 30 | it( 31 | 'allows client to finish', 32 | co.wrap(function* () { 33 | const pool = new Pool() 34 | const query = pool.query('SELECT $1::text as name', ['brianc']) 35 | yield pool.end() 36 | const res = yield query 37 | expect(res.rows[0].name).to.equal('brianc') 38 | }) 39 | ) 40 | 41 | it('pool.end() - finish pending queries', async () => { 42 | const pool = new Pool({ max: 20 }) 43 | let completed = 0 44 | for (let x = 1; x <= 20; x++) { 45 | pool.query('SELECT $1::text as name', ['brianc']).then(() => completed++) 46 | } 47 | await pool.end() 48 | expect(completed).to.equal(20) 49 | }) 50 | }) 51 | -------------------------------------------------------------------------------- /packages/pg/test/integration/client/json-type-parsing-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const helper = require('./test-helper') 3 | const assert = require('assert') 4 | 5 | const pool = new helper.pg.Pool() 6 | pool.connect( 7 | assert.success(function (client, done) { 8 | helper.versionGTE( 9 | client, 10 | 90200, 11 | assert.success(function (jsonSupported) { 12 | if (!jsonSupported) { 13 | console.log('skip json test on older versions of postgres') 14 | done() 15 | return pool.end() 16 | } 17 | client.query('CREATE TEMP TABLE stuff(id SERIAL PRIMARY KEY, data JSON)') 18 | const value = { name: 'Brian', age: 250, alive: true, now: new Date() } 19 | client.query('INSERT INTO stuff (data) VALUES ($1)', [value]) 20 | client.query( 21 | 'SELECT * FROM stuff', 22 | assert.success(function (result) { 23 | assert.equal(result.rows.length, 1) 24 | assert.equal(typeof result.rows[0].data, 'object') 25 | const row = result.rows[0].data 26 | assert.strictEqual(row.name, value.name) 27 | assert.strictEqual(row.age, value.age) 28 | assert.strictEqual(row.alive, value.alive) 29 | assert.equal(JSON.stringify(row.now), JSON.stringify(value.now)) 30 | done() 31 | pool.end() 32 | }) 33 | ) 34 | }) 35 | ) 36 | }) 37 | ) 38 | -------------------------------------------------------------------------------- /packages/pg-native/bench/leaks.js: -------------------------------------------------------------------------------- 1 | const Client = require('../') 2 | const async = require('async') 3 | 4 | const loop = function () { 5 | const client = new Client() 6 | 7 | const connect = function (cb) { 8 | client.connect(cb) 9 | } 10 | 11 | const simpleQuery = function (cb) { 12 | client.query('SELECT NOW()', cb) 13 | } 14 | 15 | const paramsQuery = function (cb) { 16 | client.query('SELECT $1::text as name', ['Brian'], cb) 17 | } 18 | 19 | const prepared = function (cb) { 20 | client.prepare('test', 'SELECT $1::text as name', 1, function (err) { 21 | if (err) return cb(err) 22 | client.execute('test', ['Brian'], cb) 23 | }) 24 | } 25 | 26 | const sync = function (cb) { 27 | client.querySync('SELECT NOW()') 28 | client.querySync('SELECT $1::text as name', ['Brian']) 29 | client.prepareSync('boom', 'SELECT $1::text as name', 1) 30 | client.executeSync('boom', ['Brian']) 31 | setImmediate(cb) 32 | } 33 | 34 | const end = function (cb) { 35 | client.end(cb) 36 | } 37 | 38 | const ops = [connect, simpleQuery, paramsQuery, prepared, sync, end] 39 | 40 | const start = performance.now() 41 | async.series(ops, function (err) { 42 | if (err) throw err 43 | console.log(performance.now() - start) 44 | setImmediate(loop) 45 | }) 46 | } 47 | 48 | // on my machine this will consume memory up to about 50 megs of ram 49 | // and then stabalize at that point 50 | loop() 51 | -------------------------------------------------------------------------------- /packages/pg-cursor/test/promises.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, 5)' 6 | 7 | describe('cursor using promises', function () { 8 | beforeEach(function (done) { 9 | const client = (this.client = new pg.Client()) 10 | client.connect(done) 11 | 12 | this.pgCursor = function (text, values) { 13 | return client.query(new Cursor(text, values || [])) 14 | } 15 | }) 16 | 17 | afterEach(function () { 18 | this.client.end() 19 | }) 20 | 21 | it('resolve with result', async function () { 22 | const cursor = this.pgCursor(text) 23 | const res = await cursor.read(6) 24 | assert.strictEqual(res.length, 6) 25 | }) 26 | 27 | it('reject with error', function (done) { 28 | const cursor = this.pgCursor('select asdfasdf') 29 | cursor.read(1).catch((err) => { 30 | assert(err) 31 | done() 32 | }) 33 | }) 34 | 35 | it('read multiple times', async function () { 36 | const cursor = this.pgCursor(text) 37 | let res 38 | 39 | res = await cursor.read(2) 40 | assert.strictEqual(res.length, 2) 41 | 42 | res = await cursor.read(3) 43 | assert.strictEqual(res.length, 3) 44 | 45 | res = await cursor.read(1) 46 | assert.strictEqual(res.length, 1) 47 | 48 | res = await cursor.read(1) 49 | assert.strictEqual(res.length, 0) 50 | }) 51 | }) 52 | -------------------------------------------------------------------------------- /packages/pg/test/integration/gh-issues/2716-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const helper = require('../test-helper') 3 | 4 | const suite = new helper.Suite() 5 | 6 | // https://github.com/brianc/node-postgres/issues/2716 7 | suite.testAsync('client.end() should resolve if already ended', async () => { 8 | const client = new helper.pg.Client() 9 | await client.connect() 10 | 11 | // this should resolve only when the underlying socket is fully closed, both 12 | // the readable part ("end" event) & writable part ("close" event). 13 | 14 | // https://nodejs.org/docs/latest-v16.x/api/net.html#event-end 15 | // > Emitted when the other end of the socket signals the end of 16 | // > transmission, thus ending the readable side of the socket. 17 | 18 | // https://nodejs.org/docs/latest-v16.x/api/net.html#event-close_1 19 | // > Emitted once the socket is fully closed. 20 | 21 | // here: stream = socket 22 | 23 | await client.end() 24 | // connection.end() 25 | // stream.end() 26 | // ... 27 | // stream emits "end" 28 | // not listening to this event anymore so the promise doesn't resolve yet 29 | // stream emits "close"; no more events will be emitted from the stream 30 | // connection emits "end" 31 | // promise resolved 32 | 33 | // This should now resolve immediately, rather than wait for connection.on('end') 34 | await client.end() 35 | 36 | // this should resolve immediately, rather than waiting forever 37 | await client.end() 38 | }) 39 | -------------------------------------------------------------------------------- /packages/pg/test/integration/client/parse-int-8-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const helper = require('../test-helper') 4 | const pg = helper.pg 5 | const suite = new helper.Suite() 6 | const assert = require('assert') 7 | 8 | const pool = new pg.Pool(helper.config) 9 | suite.test('ability to turn on and off parser', function () { 10 | if (helper.args.binary) return false 11 | pool.connect( 12 | assert.success(function (client, done) { 13 | pg.defaults.parseInt8 = true 14 | client.query('CREATE TEMP TABLE asdf(id SERIAL PRIMARY KEY)') 15 | client.query( 16 | 'SELECT COUNT(*) as "count", \'{1,2,3}\'::bigint[] as array FROM asdf', 17 | assert.success(function (res) { 18 | assert.strictEqual(0, res.rows[0].count) 19 | assert.strictEqual(1, res.rows[0].array[0]) 20 | assert.strictEqual(2, res.rows[0].array[1]) 21 | assert.strictEqual(3, res.rows[0].array[2]) 22 | pg.defaults.parseInt8 = false 23 | client.query( 24 | 'SELECT COUNT(*) as "count", \'{1,2,3}\'::bigint[] as array FROM asdf', 25 | assert.success(function (res) { 26 | done() 27 | assert.strictEqual('0', res.rows[0].count) 28 | assert.strictEqual('1', res.rows[0].array[0]) 29 | assert.strictEqual('2', res.rows[0].array[1]) 30 | assert.strictEqual('3', res.rows[0].array[2]) 31 | pool.end() 32 | }) 33 | ) 34 | }) 35 | ) 36 | }) 37 | ) 38 | }) 39 | -------------------------------------------------------------------------------- /packages/pg/test/unit/client/result-metadata-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const helper = require('./test-helper') 3 | const assert = require('assert') 4 | const suite = new helper.Suite() 5 | const test = suite.test.bind(suite) 6 | 7 | const testForTag = function (tagText, callback) { 8 | test('includes command tag data for tag ' + tagText, function () { 9 | const client = helper.client() 10 | client.connection.emit('readyForQuery') 11 | 12 | client.query( 13 | 'whatever', 14 | assert.calls((err, result) => { 15 | assert.ok(result != null, 'should pass something to this event') 16 | callback(result) 17 | }) 18 | ) 19 | assert.lengthIs(client.connection.queries, 1) 20 | 21 | client.connection.emit('commandComplete', { 22 | text: tagText, 23 | }) 24 | 25 | client.connection.emit('readyForQuery') 26 | }) 27 | } 28 | 29 | const check = function (oid, rowCount, command) { 30 | return function (result) { 31 | if (oid != null) { 32 | assert.equal(result.oid, oid) 33 | } 34 | assert.equal(result.rowCount, rowCount) 35 | assert.equal(result.command, command) 36 | } 37 | } 38 | 39 | testForTag('INSERT 0 3', check(0, 3, 'INSERT')) 40 | testForTag('INSERT 841 1', check(841, 1, 'INSERT')) 41 | testForTag('DELETE 10', check(null, 10, 'DELETE')) 42 | testForTag('UPDATE 11', check(null, 11, 'UPDATE')) 43 | testForTag('SELECT 20', check(null, 20, 'SELECT')) 44 | testForTag('COPY', check(null, null, 'COPY')) 45 | testForTag('COPY 12345', check(null, 12345, 'COPY')) 46 | -------------------------------------------------------------------------------- /docs/pages/features/transactions.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Transactions 3 | --- 4 | 5 | import { Alert } from '/components/alert.tsx' 6 | 7 | To execute a transaction with node-postgres you simply execute `BEGIN / COMMIT / ROLLBACK` queries yourself through a client. Because node-postgres strives to be low level and un-opinionated, it doesn't provide any higher level abstractions specifically around transactions. 8 | 9 | 10 | You must use the same client instance for all statements within a transaction. PostgreSQL 11 | isolates a transaction to individual clients. This means if you initialize or use transactions with the{' '} 12 | pool.query method you will have problems. Do not use transactions with 13 | the pool.query method. 14 | 15 | 16 | ## Examples 17 | 18 | ```js 19 | import { Pool } from 'pg' 20 | const pool = new Pool() 21 | 22 | const client = await pool.connect() 23 | 24 | try { 25 | await client.query('BEGIN') 26 | const queryText = 'INSERT INTO users(name) VALUES($1) RETURNING id' 27 | const res = await client.query(queryText, ['brianc']) 28 | 29 | const insertPhotoText = 'INSERT INTO photos(user_id, photo_url) VALUES ($1, $2)' 30 | const insertPhotoValues = [res.rows[0].id, 's3.bucket.foo'] 31 | await client.query(insertPhotoText, insertPhotoValues) 32 | await client.query('COMMIT') 33 | } catch (e) { 34 | await client.query('ROLLBACK') 35 | throw e 36 | } finally { 37 | client.release() 38 | } 39 | ``` 40 | -------------------------------------------------------------------------------- /packages/pg-native/test/prepare.js: -------------------------------------------------------------------------------- 1 | const Client = require('../') 2 | const ok = require('okay') 3 | const async = require('async') 4 | 5 | describe('async prepare', function () { 6 | const run = function (n, cb) { 7 | const client = new Client() 8 | client.connectSync() 9 | 10 | const exec = function (x, done) { 11 | client.prepare('get_now' + x, 'SELECT NOW()', 0, done) 12 | } 13 | 14 | async.timesSeries( 15 | 10, 16 | exec, 17 | ok(cb, function () { 18 | client.end(cb) 19 | }) 20 | ) 21 | } 22 | 23 | const t = function (n) { 24 | it('works for ' + n + ' clients', function (done) { 25 | async.times(n, run, function (err) { 26 | done(err) 27 | }) 28 | }) 29 | } 30 | 31 | for (let i = 0; i < 10; i++) { 32 | t(i) 33 | } 34 | }) 35 | 36 | describe('async execute', function () { 37 | const run = function (n, cb) { 38 | const client = new Client() 39 | client.connectSync() 40 | client.prepareSync('get_now', 'SELECT NOW()', 0) 41 | const exec = function (x, cb) { 42 | client.execute('get_now', [], cb) 43 | } 44 | async.timesSeries( 45 | 10, 46 | exec, 47 | ok(cb, function () { 48 | client.end(cb) 49 | }) 50 | ) 51 | } 52 | 53 | const t = function (n) { 54 | it('works for ' + n + ' clients', function (done) { 55 | async.times(n, run, function (err) { 56 | done(err) 57 | }) 58 | }) 59 | } 60 | 61 | for (let i = 0; i < 10; i++) { 62 | t(i) 63 | } 64 | }) 65 | -------------------------------------------------------------------------------- /packages/pg/test/integration/client/type-parser-override-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const helper = require('./test-helper') 3 | const assert = require('assert') 4 | 5 | function testTypeParser(client, expectedResult, done) { 6 | const boolValue = true 7 | client.query('CREATE TEMP TABLE parserOverrideTest(id bool)') 8 | client.query('INSERT INTO parserOverrideTest(id) VALUES ($1)', [boolValue]) 9 | client.query( 10 | 'SELECT * FROM parserOverrideTest', 11 | assert.success(function (result) { 12 | assert.equal(result.rows[0].id, expectedResult) 13 | done() 14 | }) 15 | ) 16 | } 17 | 18 | const pool = new helper.pg.Pool(helper.config) 19 | pool.connect( 20 | assert.success(function (client1, done1) { 21 | pool.connect( 22 | assert.success(function (client2, done2) { 23 | const boolTypeOID = 16 24 | client1.setTypeParser(boolTypeOID, function () { 25 | return 'first client' 26 | }) 27 | client2.setTypeParser(boolTypeOID, function () { 28 | return 'second client' 29 | }) 30 | 31 | client1.setTypeParser(boolTypeOID, 'binary', function () { 32 | return 'first client binary' 33 | }) 34 | client2.setTypeParser(boolTypeOID, 'binary', function () { 35 | return 'second client binary' 36 | }) 37 | 38 | testTypeParser(client1, 'first client', () => { 39 | done1() 40 | testTypeParser(client2, 'second client', () => done2(), pool.end()) 41 | }) 42 | }) 43 | ) 44 | }) 45 | ) 46 | -------------------------------------------------------------------------------- /packages/pg/test/native/stress-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const helper = require('../test-helper') 3 | const Client = require('../../lib/native') 4 | const Query = Client.Query 5 | const assert = require('assert') 6 | const suite = new helper.Suite() 7 | 8 | suite.test('many rows', function () { 9 | const client = new Client(helper.config) 10 | client.connect() 11 | const q = client.query(new Query('SELECT * FROM person')) 12 | const rows = [] 13 | q.on('row', function (row) { 14 | rows.push(row) 15 | }) 16 | assert.emits(q, 'end', function () { 17 | client.end() 18 | assert.lengthIs(rows, 26) 19 | }) 20 | }) 21 | 22 | suite.test('many queries', function () { 23 | const client = new Client(helper.config) 24 | client.connect() 25 | let count = 0 26 | const expected = 100 27 | for (let i = 0; i < expected; i++) { 28 | const q = client.query(new Query('SELECT * FROM person')) 29 | assert.emits(q, 'end', function () { 30 | count++ 31 | }) 32 | } 33 | assert.emits(client, 'drain', function () { 34 | client.end() 35 | assert.equal(count, expected) 36 | }) 37 | }) 38 | 39 | suite.test('many clients', function () { 40 | const clients = [] 41 | for (let i = 0; i < 10; i++) { 42 | clients.push(new Client(helper.config)) 43 | } 44 | clients.forEach(function (client) { 45 | client.connect() 46 | for (let i = 0; i < 20; i++) { 47 | client.query('SELECT * FROM person') 48 | } 49 | assert.emits(client, 'drain', function () { 50 | client.end() 51 | }) 52 | }) 53 | }) 54 | -------------------------------------------------------------------------------- /packages/pg-connection-string/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pg-connection-string", 3 | "version": "2.9.1", 4 | "description": "Functions for dealing with a PostgresSQL connection string", 5 | "main": "./index.js", 6 | "types": "./index.d.ts", 7 | "exports": { 8 | ".": { 9 | "types": "./index.d.ts", 10 | "import": "./esm/index.mjs", 11 | "require": "./index.js", 12 | "default": "./index.js" 13 | } 14 | }, 15 | "scripts": { 16 | "test": "nyc --reporter=lcov mocha && npm run check-coverage", 17 | "check-coverage": "nyc check-coverage --statements 100 --branches 100 --lines 100 --functions 100" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "git://github.com/brianc/node-postgres.git", 22 | "directory": "packages/pg-connection-string" 23 | }, 24 | "keywords": [ 25 | "pg", 26 | "connection", 27 | "string", 28 | "parse" 29 | ], 30 | "author": "Blaine Bublitz (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 | --------------------------------------------------------------------------------