Merge branch 'master' into normal-encoding-parameter

This commit is contained in:
Charmander 2020-05-13 23:59:39 -07:00
commit d8422552d1
40 changed files with 1167 additions and 1366 deletions

View File

@ -1,16 +1,8 @@
{
"plugins": [
"prettier"
],
"plugins": ["prettier"],
"parser": "@typescript-eslint/parser",
"extends": [
"plugin:prettier/recommended",
"prettier/@typescript-eslint"
],
"ignorePatterns": [
"node_modules",
"packages/pg-protocol/dist/**/*"
],
"extends": ["plugin:prettier/recommended", "prettier/@typescript-eslint"],
"ignorePatterns": ["node_modules", "coverage", "packages/pg-protocol/dist/**/*"],
"parserOptions": {
"ecmaVersion": 2017,
"sourceType": "module"

View File

@ -7,27 +7,25 @@ before_script: |
env:
- CC=clang CXX=clang++ npm_config_clang=1 PGUSER=postgres PGDATABASE=postgres
# test w/ new faster parsing code
- CC=clang CXX=clang++ npm_config_clang=1 PGUSER=postgres PGDATABASE=postgres PG_FAST_CONNECTION=true
node_js:
- lts/dubnium
- lts/erbium
# node 13.7 seems to have changed behavior of async iterators exiting early on streams
# if 13.8 still has this problem when it comes down I'll talk to the node team about the change
# in the mean time...peg to 13.6
# in the mean time...peg to 13.6
- 13.6
- 14
addons:
postgresql: "10"
postgresql: '10'
matrix:
include:
# Run tests/paths that require password authentication
- node_js: lts/erbium
env:
- CC=clang CXX=clang++ npm_config_clang=1 PGUSER=postgres PGDATABASE=postgres PGPASSWORD=test-password
- CC=clang CXX=clang++ npm_config_clang=1 PGUSER=postgres PGDATABASE=postgres PGPASSWORD=test-password SCRAM_TEST_PGUSER=scram_test SCRAM_TEST_PGPASSWORD=test4scram
before_script: |
sudo -u postgres sed -i \
-e '/^local/ s/trust$/peer/' \
@ -36,28 +34,31 @@ matrix:
sudo -u postgres psql -c "ALTER ROLE postgres PASSWORD 'test-password'; SELECT pg_reload_conf()"
yarn build
node packages/pg/script/create-test-tables.js postgresql:///
sudo -u postgres -- psql \
-c "SET password_encryption = 'scram-sha-256'" \
-c "CREATE ROLE scram_test login password 'test4scram'"
- node_js: lts/carbon
addons:
postgresql: "9.5"
postgresql: '9.5'
dist: precise
# different PostgreSQL versions on Node LTS
- node_js: lts/erbium
addons:
postgresql: "9.3"
postgresql: '9.3'
- node_js: lts/erbium
addons:
postgresql: "9.4"
postgresql: '9.4'
- node_js: lts/erbium
addons:
postgresql: "9.5"
postgresql: '9.5'
- node_js: lts/erbium
addons:
postgresql: "9.6"
postgresql: '9.6'
# PostgreSQL 9.2 only works on precise
- node_js: lts/carbon
addons:
postgresql: "9.2"
postgresql: '9.2'
dist: precise

View File

@ -4,6 +4,27 @@ For richer information consult the commit log on github with referenced pull req
We do not include break-fix version release in this file.
### pg@8.2.0
- Switch internal protocol parser & serializer to [pg-protocol](https://github.com/brianc/node-postgres/tree/master/packages/pg-protocol). The change is backwards compatible but results in a significant performance improvement across the board, with some queries as much as 50% faster. This is the first work to land in an on-going performance improvment initiative I'm working on. Stay tuned as things are set to get much faster still! :rocket:
### pg-cursor@2.2.0
- Switch internal protocol parser & serializer to [pg-protocol](https://github.com/brianc/node-postgres/tree/master/packages/pg-protocol). The change is backwards compatible but results in a significant performance improvement across the board, with some queries as much as 50% faster.
### pg-query-stream@3.1.0
- Switch internal protocol parser & serializer to [pg-protocol](https://github.com/brianc/node-postgres/tree/master/packages/pg-protocol). The change is backwards compatible but results in a significant performance improvement across the board, with some queries as much as 50% faster.
### pg@8.1.0
- Switch to using [monorepo](https://github.com/brianc/node-postgres/tree/master/packages/pg-connection-string) version of `pg-connection-string`. This includes better support for SSL argument parsing from connection strings and ensures continuity of support.
- Add `&ssl=no-verify` option to connection string and `PGSSLMODE=no-verify` environment variable support for the pure JS driver. This is equivalent of passing `{ ssl: { rejectUnauthorize: false } }` to the client/pool constructor. The advantage of having support in connection strings and environment variables is it can be "externally" configured via environment variables and CLI arguments much more easily, and should remove the need to directly edit any application code for [the SSL default changes in 8.0](https://node-postgres.com/announcements#2020-02-25). This should make using `pg@8.x` significantly less difficult on environments like Heroku for example.
### pg-pool@3.2.0
- Same changes to `pg` impact `pg-pool` as they both use the same connection parameter and connection string parsing code for configuring SSL.
### pg-pool@3.1.0
- Add [maxUses](https://github.com/brianc/node-postgres/pull/2157) config option.
@ -12,8 +33,8 @@ We do not include break-fix version release in this file.
#### note: for detailed release notes please [check here](https://node-postgres.com/announcements#2020-02-25)
- Remove versions of node older than `6 lts` from the test matrix. `pg>=8.0` may still work on older versions but it is no longer officially supported.
- Change default behavior when not specifying `rejectUnauthorized` with the SSL connection parameters. Previously we defaulted to `rejectUnauthorized: false` when it was not specifically included. We now default to `rejectUnauthorized: true.` Manually specify `{ ssl: { rejectUnauthorized: false } }` for old behavior.
- Remove versions of node older than `6 lts` from the test matrix. `pg>=8.0` may still work on older versions but it is no longer officially supported.
- Change default behavior when not specifying `rejectUnauthorized` with the SSL connection parameters. Previously we defaulted to `rejectUnauthorized: false` when it was not specifically included. We now default to `rejectUnauthorized: true.` Manually specify `{ ssl: { rejectUnauthorized: false } }` for old behavior.
- Change [default database](https://github.com/brianc/node-postgres/pull/1679) when not specified to use the `user` config option if available. Previously `process.env.USER` was used.
- Change `pg.Pool` and `pg.Query` to [be](https://github.com/brianc/node-postgres/pull/2126) an [es6 class](https://github.com/brianc/node-postgres/pull/2063).
- Make `pg.native` non enumerable.
@ -27,16 +48,17 @@ We do not include break-fix version release in this file.
### pg-query-stream@3.0.0
- [Rewrote stream internals](https://github.com/brianc/node-postgres/pull/2051) to better conform to node stream semantics. This should make pg-query-stream much better at respecting [highWaterMark](https://nodejs.org/api/stream.html#stream_new_stream_readable_options) and getting rid of some edge case bugs when using pg-query-stream as an async iterator. Due to the size and nature of this change (effectively a full re-write) it's safest to bump the semver major here, though almost all tests remain untouched and still passing, which brings us to a breaking change to the API....
- Changed `stream.close` to `stream.destroy` which is the [official](https://nodejs.org/api/stream.html#stream_readable_destroy_error) way to terminate a readable stream. This is a __breaking change__ if you rely on the `stream.close` method on pg-query-stream...though should be just a find/replace type operation to upgrade as the semantics remain very similar (not exactly the same, since internals are rewritten, but more in line with how streams are "supposed" to behave).
- Unified the `config.batchSize` and `config.highWaterMark` to both do the same thing: control how many rows are buffered in memory. The `ReadableStream` will manage exactly how many rows are requested from the cursor at a time. This should give better out of the box performance and help with efficient async iteration.
- [Rewrote stream internals](https://github.com/brianc/node-postgres/pull/2051) to better conform to node stream semantics. This should make pg-query-stream much better at respecting [highWaterMark](https://nodejs.org/api/stream.html#stream_new_stream_readable_options) and getting rid of some edge case bugs when using pg-query-stream as an async iterator. Due to the size and nature of this change (effectively a full re-write) it's safest to bump the semver major here, though almost all tests remain untouched and still passing, which brings us to a breaking change to the API....
- Changed `stream.close` to `stream.destroy` which is the [official](https://nodejs.org/api/stream.html#stream_readable_destroy_error) way to terminate a readable stream. This is a **breaking change** if you rely on the `stream.close` method on pg-query-stream...though should be just a find/replace type operation to upgrade as the semantics remain very similar (not exactly the same, since internals are rewritten, but more in line with how streams are "supposed" to behave).
- Unified the `config.batchSize` and `config.highWaterMark` to both do the same thing: control how many rows are buffered in memory. The `ReadableStream` will manage exactly how many rows are requested from the cursor at a time. This should give better out of the box performance and help with efficient async iteration.
### pg@7.17.0
- Add support for `idle_in_transaction_session_timeout` [option](https://github.com/brianc/node-postgres/pull/2049).
### 7.16.0
- Add optional, opt-in behavior to test new, [faster query pipeline](https://github.com/brianc/node-postgres/pull/2044). This is experimental, and not documented yet. The pipeline changes will grow significantly after the 8.0 release.
- Add optional, opt-in behavior to test new, [faster query pipeline](https://github.com/brianc/node-postgres/pull/2044). This is experimental, and not documented yet. The pipeline changes will grow significantly after the 8.0 release.
### 7.15.0
@ -161,7 +183,7 @@ Promise support & other goodness lives now in [pg-pool](https://github.com/brian
- Included support for tcp keep alive. Enable it as follows:
```js
var client = new Client({ keepAlive: true });
var client = new Client({ keepAlive: true })
```
This should help with backends incorrectly considering idle clients to be dead and prematurely disconnecting them.
@ -173,12 +195,12 @@ This should help with backends incorrectly considering idle clients to be dead a
Example:
```js
var client = new Client();
client.connect();
client.query("SELECT $1::text as name", ["brianc"]).then(function(res) {
console.log("hello from", res.rows[0]);
client.end();
});
var client = new Client()
client.connect()
client.query('SELECT $1::text as name', ['brianc']).then(function (res) {
console.log('hello from', res.rows[0])
client.end()
})
```
### v5.0.0

View File

@ -15,6 +15,7 @@ This repo is a monorepo which contains the core [pg](https://github.com/brianc/n
- [pg-pool](https://github.com/brianc/node-postgres/tree/master/packages/pg-pool)
- [pg-cursor](https://github.com/brianc/node-postgres/tree/master/packages/pg-cursor)
- [pg-query-stream](https://github.com/brianc/node-postgres/tree/master/packages/pg-query-stream)
- [pg-connection-string](https://github.com/brianc/node-postgres/tree/master/packages/pg-connection-string)
## Documenation

View File

@ -7,6 +7,7 @@ node-postgres is made possible by the helpful contributors from the community as
- [Timescale](https://timescale.com)
- [Nafundi](https://nafundi.com)
- [CrateDB](https://crate.io/)
- [BitMEX](https://www.bitmex.com/app/trade/XBTUSD)
# Supporters

View File

@ -13,7 +13,7 @@
"test": "yarn lint && yarn lerna exec yarn test",
"build": "yarn lerna exec --scope pg-protocol yarn build",
"pretest": "yarn build",
"lint": "!([[ -e node_modules/.bin/prettier ]]) || eslint '*/**/*.{js,ts,tsx}'"
"lint": "if [ -x ./node_modules/.bin/prettier ]; then eslint '*/**/*.{js,ts,tsx}'; fi;"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^2.27.0",

View File

@ -0,0 +1,2 @@
service_name: travis-pro
repo_token: 5F6dODinz9L9uFR6HatKmtsYDoV1A5S2N

View File

@ -0,0 +1,26 @@
# Logs
logs
*.log
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directory
# Deployed apps should consider commenting this line out:
# see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git
node_modules
package-lock.json

View File

@ -0,0 +1,6 @@
language: node_js
node_js:
- '0.10'
- '6.9'
- '8'
after_success: 'npm run coveralls'

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014 Iced Development
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:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
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.

View File

@ -0,0 +1,72 @@
pg-connection-string
====================
[![NPM](https://nodei.co/npm/pg-connection-string.png?compact=true)](https://nodei.co/npm/pg-connection-string/)
[![Build Status](https://travis-ci.org/iceddev/pg-connection-string.svg?branch=master)](https://travis-ci.org/iceddev/pg-connection-string)
[![Coverage Status](https://coveralls.io/repos/github/iceddev/pg-connection-string/badge.svg?branch=master)](https://coveralls.io/github/iceddev/pg-connection-string?branch=master)
Functions for dealing with a PostgresSQL connection string
`parse` method taken from [node-postgres](https://github.com/brianc/node-postgres.git)
Copyright (c) 2010-2014 Brian Carlson (brian.m.carlson@gmail.com)
MIT License
## Usage
```js
var parse = require('pg-connection-string').parse;
var config = parse('postgres://someuser:somepassword@somehost:381/somedatabase')
```
The resulting config contains a subset of the following properties:
* `host` - Postgres server hostname or, for UNIX doamain sockets, the socket filename
* `port` - port on which to connect
* `user` - User with which to authenticate to the server
* `password` - Corresponding password
* `database` - Database name within the server
* `client_encoding` - string encoding the client will use
* `ssl`, either a boolean or an object with properties
* `cert`
* `key`
* `ca`
* any other query parameters (for example, `application_name`) are preserved intact.
## Connection Strings
The short summary of acceptable URLs is:
* `socket:<path>?<query>` - UNIX domain socket
* `postgres://<user>:<password>@<host>:<port>/<database>?<query>` - TCP connection
But see below for more details.
### UNIX Domain Sockets
When user and password are not given, the socket path follows `socket:`, as in `socket:/var/run/pgsql`.
This form can be shortened to just a path: `/var/run/pgsql`.
When user and password are given, they are included in the typical URL positions, with an empty `host`, as in `socket://user:pass@/var/run/pgsql`.
Query parameters follow a `?` character, including the following special query parameters:
* `db=<database>` - sets the database name (urlencoded)
* `encoding=<encoding>` - sets the `client_encoding` property
### TCP Connections
TCP connections to the Postgres server are indicated with `pg:` or `postgres:` schemes (in fact, any scheme but `socket:` is accepted).
If username and password are included, they should be urlencoded.
The database name, however, should *not* be urlencoded.
Query parameters follow a `?` character, including the following special query parameters:
* `host=<host>` - sets `host` property, overriding the URL's host
* `encoding=<encoding>` - sets the `client_encoding` property
* `ssl=1`, `ssl=true`, `ssl=0`, `ssl=false` - sets `ssl` to true or false, accordingly
* `sslcert=<filename>` - reads data from the given file and includes the result as `ssl.cert`
* `sslkey=<filename>` - reads data from the given file and includes the result as `ssl.key`
* `sslrootcert=<filename>` - reads data from the given file and includes the result as `ssl.ca`
A bare relative URL, such as `salesdata`, will indicate a database name while leaving other properties empty.

View File

@ -0,0 +1,14 @@
export function parse(connectionString: string): ConnectionOptions
export interface ConnectionOptions {
host: string | null
password?: string
user?: string
port?: string | null
database: string | null | undefined
client_encoding?: string
ssl?: boolean | string
application_name?: string
fallback_application_name?: string
}

View File

@ -0,0 +1,89 @@
'use strict'
var url = require('url')
var fs = require('fs')
//Parse method copied from https://github.com/brianc/node-postgres
//Copyright (c) 2010-2014 Brian Carlson (brian.m.carlson@gmail.com)
//MIT License
//parses a connection string
function parse(str) {
//unix socket
if (str.charAt(0) === '/') {
var config = str.split(' ')
return { host: config[0], database: config[1] }
}
// url parse expects spaces encoded as %20
var result = url.parse(
/ |%[^a-f0-9]|%[a-f0-9][^a-f0-9]/i.test(str) ? encodeURI(str).replace(/\%25(\d\d)/g, '%$1') : str,
true
)
var config = result.query
for (var k in config) {
if (Array.isArray(config[k])) {
config[k] = config[k][config[k].length - 1]
}
}
var auth = (result.auth || ':').split(':')
config.user = auth[0]
config.password = auth.splice(1).join(':')
config.port = result.port
if (result.protocol == 'socket:') {
config.host = decodeURI(result.pathname)
config.database = result.query.db
config.client_encoding = result.query.encoding
return config
}
if (!config.host) {
// Only set the host if there is no equivalent query param.
config.host = result.hostname
}
// If the host is missing it might be a URL-encoded path to a socket.
var pathname = result.pathname
if (!config.host && pathname && /^%2f/i.test(pathname)) {
var pathnameSplit = pathname.split('/')
config.host = decodeURIComponent(pathnameSplit[0])
pathname = pathnameSplit.splice(1).join('/')
}
// result.pathname is not always guaranteed to have a '/' prefix (e.g. relative urls)
// only strip the slash if it is present.
if (pathname && pathname.charAt(0) === '/') {
pathname = pathname.slice(1) || null
}
config.database = pathname && decodeURI(pathname)
if (config.ssl === 'true' || config.ssl === '1') {
config.ssl = true
}
if (config.ssl === '0') {
config.ssl = false
}
if (config.sslcert || config.sslkey || config.sslrootcert) {
config.ssl = {}
}
if (config.sslcert) {
config.ssl.cert = fs.readFileSync(config.sslcert).toString()
}
if (config.sslkey) {
config.ssl.key = fs.readFileSync(config.sslkey).toString()
}
if (config.sslrootcert) {
config.ssl.ca = fs.readFileSync(config.sslrootcert).toString()
}
return config
}
module.exports = parse
parse.parse = parse

View File

@ -0,0 +1,38 @@
{
"name": "pg-connection-string",
"version": "2.2.2",
"description": "Functions for dealing with a PostgresSQL connection string",
"main": "./index.js",
"types": "./index.d.ts",
"scripts": {
"test": "istanbul cover _mocha && npm run check-coverage",
"check-coverage": "istanbul check-coverage --statements 100 --branches 100 --lines 100 --functions 100",
"coveralls": "cat ./coverage/lcov.info | ./node_modules/.bin/coveralls"
},
"repository": {
"type": "git",
"url": "git://github.com/brianc/node-postgres.git"
},
"keywords": [
"pg",
"connection",
"string",
"parse"
],
"author": "Blaine Bublitz <blaine@iceddev.com> (http://iceddev.com/)",
"license": "MIT",
"bugs": {
"url": "https://github.com/iceddev/pg-connection-string/issues"
},
"homepage": "https://github.com/iceddev/pg-connection-string",
"devDependencies": {
"chai": "^4.1.1",
"coveralls": "^3.0.4",
"istanbul": "^0.4.5",
"mocha": "^7.1.2"
},
"files": [
"index.js",
"index.d.ts"
]
}

View File

@ -0,0 +1 @@
example ca

View File

@ -0,0 +1 @@
example cert

View File

@ -0,0 +1 @@
example key

View File

@ -0,0 +1,274 @@
'use strict'
var chai = require('chai')
var expect = chai.expect
chai.should()
var parse = require('../').parse
describe('parse', function () {
it('using connection string in client constructor', function () {
var subject = parse('postgres://brian:pw@boom:381/lala')
subject.user.should.equal('brian')
subject.password.should.equal('pw')
subject.host.should.equal('boom')
subject.port.should.equal('381')
subject.database.should.equal('lala')
})
it('escape spaces if present', function () {
var subject = parse('postgres://localhost/post gres')
subject.database.should.equal('post gres')
})
it('do not double escape spaces', function () {
var subject = parse('postgres://localhost/post%20gres')
subject.database.should.equal('post gres')
})
it('initializing with unix domain socket', function () {
var subject = parse('/var/run/')
subject.host.should.equal('/var/run/')
})
it('initializing with unix domain socket and a specific database, the simple way', function () {
var subject = parse('/var/run/ mydb')
subject.host.should.equal('/var/run/')
subject.database.should.equal('mydb')
})
it('initializing with unix domain socket, the health way', function () {
var subject = parse('socket:/some path/?db=my[db]&encoding=utf8')
subject.host.should.equal('/some path/')
subject.database.should.equal('my[db]', 'must to be escaped and unescaped trough "my%5Bdb%5D"')
subject.client_encoding.should.equal('utf8')
})
it('initializing with unix domain socket, the escaped health way', function () {
var subject = parse('socket:/some%20path/?db=my%2Bdb&encoding=utf8')
subject.host.should.equal('/some path/')
subject.database.should.equal('my+db')
subject.client_encoding.should.equal('utf8')
})
it('initializing with unix domain socket, username and password', function () {
var subject = parse('socket://brian:pw@/var/run/?db=mydb')
subject.user.should.equal('brian')
subject.password.should.equal('pw')
subject.host.should.equal('/var/run/')
subject.database.should.equal('mydb')
})
it('password contains < and/or > characters', function () {
var sourceConfig = {
user: 'brian',
password: 'hello<ther>e',
port: 5432,
host: 'localhost',
database: 'postgres',
}
var connectionString =
'postgres://' +
sourceConfig.user +
':' +
sourceConfig.password +
'@' +
sourceConfig.host +
':' +
sourceConfig.port +
'/' +
sourceConfig.database
var subject = parse(connectionString)
subject.password.should.equal(sourceConfig.password)
})
it('password contains colons', function () {
var sourceConfig = {
user: 'brian',
password: 'hello:pass:world',
port: 5432,
host: 'localhost',
database: 'postgres',
}
var connectionString =
'postgres://' +
sourceConfig.user +
':' +
sourceConfig.password +
'@' +
sourceConfig.host +
':' +
sourceConfig.port +
'/' +
sourceConfig.database
var subject = parse(connectionString)
subject.password.should.equal(sourceConfig.password)
})
it('username or password contains weird characters', function () {
var strang = 'pg://my f%irst name:is&%awesome!@localhost:9000'
var subject = parse(strang)
subject.user.should.equal('my f%irst name')
subject.password.should.equal('is&%awesome!')
subject.host.should.equal('localhost')
})
it('url is properly encoded', function () {
var encoded = 'pg://bi%25na%25%25ry%20:s%40f%23@localhost/%20u%2520rl'
var subject = parse(encoded)
subject.user.should.equal('bi%na%%ry ')
subject.password.should.equal('s@f#')
subject.host.should.equal('localhost')
subject.database.should.equal(' u%20rl')
})
it('relative url sets database', function () {
var relative = 'different_db_on_default_host'
var subject = parse(relative)
subject.database.should.equal('different_db_on_default_host')
})
it('no pathname returns null database', function () {
var subject = parse('pg://myhost')
;(subject.database === null).should.equal(true)
})
it('pathname of "/" returns null database', function () {
var subject = parse('pg://myhost/')
subject.host.should.equal('myhost')
;(subject.database === null).should.equal(true)
})
it('configuration parameter host', function () {
var subject = parse('pg://user:pass@/dbname?host=/unix/socket')
subject.user.should.equal('user')
subject.password.should.equal('pass')
subject.host.should.equal('/unix/socket')
subject.database.should.equal('dbname')
})
it('configuration parameter host overrides url host', function () {
var subject = parse('pg://user:pass@localhost/dbname?host=/unix/socket')
subject.host.should.equal('/unix/socket')
})
it('url with encoded socket', function () {
var subject = parse('pg://user:pass@%2Funix%2Fsocket/dbname')
subject.user.should.equal('user')
subject.password.should.equal('pass')
subject.host.should.equal('/unix/socket')
subject.database.should.equal('dbname')
})
it('url with real host and an encoded db name', function () {
var subject = parse('pg://user:pass@localhost/%2Fdbname')
subject.user.should.equal('user')
subject.password.should.equal('pass')
subject.host.should.equal('localhost')
subject.database.should.equal('%2Fdbname')
})
it('configuration parameter host treats encoded socket as part of the db name', function () {
var subject = parse('pg://user:pass@%2Funix%2Fsocket/dbname?host=localhost')
subject.user.should.equal('user')
subject.password.should.equal('pass')
subject.host.should.equal('localhost')
subject.database.should.equal('%2Funix%2Fsocket/dbname')
})
it('configuration parameter application_name', function () {
var connectionString = 'pg:///?application_name=TheApp'
var subject = parse(connectionString)
subject.application_name.should.equal('TheApp')
})
it('configuration parameter fallback_application_name', function () {
var connectionString = 'pg:///?fallback_application_name=TheAppFallback'
var subject = parse(connectionString)
subject.fallback_application_name.should.equal('TheAppFallback')
})
it('configuration parameter fallback_application_name', function () {
var connectionString = 'pg:///?fallback_application_name=TheAppFallback'
var subject = parse(connectionString)
subject.fallback_application_name.should.equal('TheAppFallback')
})
it('configuration parameter ssl=true', function () {
var connectionString = 'pg:///?ssl=true'
var subject = parse(connectionString)
subject.ssl.should.equal(true)
})
it('configuration parameter ssl=1', function () {
var connectionString = 'pg:///?ssl=1'
var subject = parse(connectionString)
subject.ssl.should.equal(true)
})
it('configuration parameter ssl=0', function () {
var connectionString = 'pg:///?ssl=0'
var subject = parse(connectionString)
subject.ssl.should.equal(false)
})
it('set ssl', function () {
var subject = parse('pg://myhost/db?ssl=1')
subject.ssl.should.equal(true)
})
it('configuration parameter sslcert=/path/to/cert', function () {
var connectionString = 'pg:///?sslcert=' + __dirname + '/example.cert'
var subject = parse(connectionString)
subject.ssl.should.eql({
cert: 'example cert\n',
})
})
it('configuration parameter sslkey=/path/to/key', function () {
var connectionString = 'pg:///?sslkey=' + __dirname + '/example.key'
var subject = parse(connectionString)
subject.ssl.should.eql({
key: 'example key\n',
})
})
it('configuration parameter sslrootcert=/path/to/ca', function () {
var connectionString = 'pg:///?sslrootcert=' + __dirname + '/example.ca'
var subject = parse(connectionString)
subject.ssl.should.eql({
ca: 'example ca\n',
})
})
it('allow other params like max, ...', function () {
var subject = parse('pg://myhost/db?max=18&min=4')
subject.max.should.equal('18')
subject.min.should.equal('4')
})
it('configuration parameter keepalives', function () {
var connectionString = 'pg:///?keepalives=1'
var subject = parse(connectionString)
subject.keepalives.should.equal('1')
})
it('unknown configuration parameter is passed into client', function () {
var connectionString = 'pg:///?ThereIsNoSuchPostgresParameter=1234'
var subject = parse(connectionString)
subject.ThereIsNoSuchPostgresParameter.should.equal('1234')
})
it('do not override a config field with value from query string', function () {
var subject = parse('socket:/some path/?db=my[db]&encoding=utf8&client_encoding=bogus')
subject.host.should.equal('/some path/')
subject.database.should.equal('my[db]', 'must to be escaped and unescaped through "my%5Bdb%5D"')
subject.client_encoding.should.equal('utf8')
})
it('return last value of repeated parameter', function () {
var connectionString = 'pg:///?keepalives=1&keepalives=0'
var subject = parse(connectionString)
subject.keepalives.should.equal('0')
})
})

View File

@ -1,6 +1,6 @@
{
"name": "pg-cursor",
"version": "2.1.9",
"version": "2.2.0",
"description": "Query cursor extension for node-postgres",
"main": "index.js",
"directories": {
@ -11,12 +11,12 @@
},
"repository": {
"type": "git",
"url": "git://github.com/brianc/node-pg-cursor.git"
"url": "git://github.com/brianc/node-postgres.git"
},
"author": "Brian M. Carlson",
"license": "MIT",
"devDependencies": {
"mocha": "^6.2.2",
"pg": "^8.0.2"
"mocha": "^7.1.2",
"pg": "^8.2.0"
}
}

View File

@ -1,6 +1,6 @@
{
"name": "pg-pool",
"version": "3.1.0",
"version": "3.2.0",
"description": "Connection pool for node-postgres",
"main": "index.js",
"directories": {
@ -11,7 +11,7 @@
},
"repository": {
"type": "git",
"url": "git://github.com/brianc/node-pg-pool.git"
"url": "git://github.com/brianc/node-postgres.git"
},
"keywords": [
"pg",
@ -30,7 +30,7 @@
"co": "4.6.0",
"expect.js": "0.3.1",
"lodash": "^4.17.11",
"mocha": "^5.2.0",
"mocha": "^7.1.2",
"pg-cursor": "^1.3.0"
},
"peerDependencies": {

View File

@ -1,3 +0,0 @@
--require test/setup.js
--bail
--timeout 10000

View File

@ -1,6 +1,6 @@
{
"name": "pg-protocol",
"version": "1.2.1",
"version": "1.2.3",
"description": "The postgres client/server binary protocol, implemented in TypeScript",
"main": "dist/index.js",
"types": "dist/index.d.ts",
@ -11,7 +11,7 @@
"@types/node": "^12.12.21",
"chai": "^4.2.0",
"chunky": "^0.0.0",
"mocha": "^6.2.2",
"mocha": "^7.1.2",
"ts-node": "^8.5.4",
"typescript": "^3.7.3"
},

View File

@ -210,8 +210,21 @@ describe('PgPacketStream', function () {
testForMessage(md5PasswordBuffer, expectedMD5PasswordMessage)
testForMessage(SASLBuffer, expectedSASLMessage)
testForMessage(SASLContinueBuffer, expectedSASLContinueMessage)
// this exercises a found bug in the parser:
// https://github.com/brianc/node-postgres/pull/2210#issuecomment-627626084
// and adds a test which is deterministic, rather than relying on network packet chunking
const extendedSASLContinueBuffer = Buffer.concat([SASLContinueBuffer, Buffer.from([1, 2, 3, 4])])
testForMessage(extendedSASLContinueBuffer, expectedSASLContinueMessage)
testForMessage(SASLFinalBuffer, expectedSASLFinalMessage)
// this exercises a found bug in the parser:
// https://github.com/brianc/node-postgres/pull/2210#issuecomment-627626084
// and adds a test which is deterministic, rather than relying on network packet chunking
const extendedSASLFinalBuffer = Buffer.concat([SASLFinalBuffer, Buffer.from([1, 2, 4, 5])])
testForMessage(extendedSASLFinalBuffer, expectedSASLFinalMessage)
testForMessage(paramStatusBuffer, expectedParameterStatusMessage)
testForMessage(backendKeyDataBuffer, expectedBackendKeyDataMessage)
testForMessage(readyForQueryBuffer, expectedReadyForQueryMessage)

View File

@ -18,7 +18,7 @@ describe('serializer', () => {
.addCString('database')
.addCString('bang')
.addCString('client_encoding')
.addCString("'utf-8'")
.addCString('UTF8')
.addCString('')
.join(true)
)

View File

@ -296,11 +296,11 @@ export class Parser {
break
case 11: // AuthenticationSASLContinue
message.name = MessageName.authenticationSASLContinue
message.data = this.reader.string(length - 4)
message.data = this.reader.string(length - 8)
break
case 12: // AuthenticationSASLFinal
message.name = MessageName.authenticationSASLFinal
message.data = this.reader.string(length - 4)
message.data = this.reader.string(length - 8)
break
default:
throw new Error('Unknown authenticationOk message type ' + code)

View File

@ -1,6 +1,6 @@
{
"name": "pg-query-stream",
"version": "3.0.6",
"version": "3.1.0",
"description": "Postgres query result returned as readable stream",
"main": "index.js",
"scripts": {
@ -25,13 +25,13 @@
"JSONStream": "~0.7.1",
"concat-stream": "~1.0.1",
"eslint-plugin-promise": "^3.5.0",
"mocha": "^6.2.2",
"pg": "^8.0.2",
"mocha": "^7.1.2",
"pg": "^8.2.0",
"stream-spec": "~0.3.5",
"stream-tester": "0.0.5",
"through": "~2.3.4"
},
"dependencies": {
"pg-cursor": "^2.1.9"
"pg-cursor": "^2.2.0"
}
}

View File

@ -1 +0,0 @@
--bail

View File

@ -5,7 +5,7 @@
<span class="badge-npmversion"><a href="https://npmjs.org/package/pg" title="View this project on NPM"><img src="https://img.shields.io/npm/v/pg.svg" alt="NPM version" /></a></span>
<span class="badge-npmdownloads"><a href="https://npmjs.org/package/pg" title="View this project on NPM"><img src="https://img.shields.io/npm/dm/pg.svg" alt="NPM downloads" /></a></span>
Non-blocking PostgreSQL client for Node.js. Pure JavaScript and optional native libpq bindings.
Non-blocking PostgreSQL client for Node.js. Pure JavaScript and optional native libpq bindings.
## Install
@ -14,30 +14,31 @@ $ npm install pg
```
---
## :star: [Documentation](https://node-postgres.com) :star:
## :star: [Documentation](https://node-postgres.com) :star:
### Features
* Pure JavaScript client and native libpq bindings share _the same API_
* Connection pooling
* Extensible JS ↔ PostgreSQL data-type coercion
* Supported PostgreSQL features
* Parameterized queries
* Named statements with query plan caching
* Async notifications with `LISTEN/NOTIFY`
* Bulk import & export with `COPY TO/COPY FROM`
- Pure JavaScript client and native libpq bindings share _the same API_
- Connection pooling
- Extensible JS ↔ PostgreSQL data-type coercion
- Supported PostgreSQL features
- Parameterized queries
- Named statements with query plan caching
- Async notifications with `LISTEN/NOTIFY`
- Bulk import & export with `COPY TO/COPY FROM`
### Extras
node-postgres is by design pretty light on abstractions. These are some handy modules we've been using over the years to complete the picture.
node-postgres is by design pretty light on abstractions. These are some handy modules we've been using over the years to complete the picture.
The entire list can be found on our [wiki](https://github.com/brianc/node-postgres/wiki/Extras).
## Support
node-postgres is free software. If you encounter a bug with the library please open an issue on the [GitHub repo](https://github.com/brianc/node-postgres). If you have questions unanswered by the documentation please open an issue pointing out how the documentation was unclear & I will do my best to make it better!
node-postgres is free software. If you encounter a bug with the library please open an issue on the [GitHub repo](https://github.com/brianc/node-postgres). If you have questions unanswered by the documentation please open an issue pointing out how the documentation was unclear & I will do my best to make it better!
When you open an issue please provide:
- version of Node
- version of Postgres
- smallest possible snippet of code to reproduce the problem
@ -49,10 +50,6 @@ You can also follow me [@briancarlson](https://twitter.com/briancarlson) if that
node-postgres's continued development has been made possible in part by generous finanical support from [the community](https://github.com/brianc/node-postgres/blob/master/SPONSORS.md) and these featured sponsors:
<div align="center">
<a href="https://www.timescale.com" target="_blank">
<img height="80" src="https://node-postgres.com/timescale.svg" />
</a>
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAABCAQAAAB0m0auAAAADElEQVR42mNkIBIAAABSAAI2VLqiAAAAAElFTkSuQmCC" />
<a href="https://crate.io" target="_blank">
<img height="80" src="https://node-postgres.com/crate-io.png" />
</a>
@ -60,19 +57,18 @@ node-postgres's continued development has been made possible in part by generous
If you or your company are benefiting from node-postgres and would like to help keep the project financially sustainable [please consider supporting](https://github.com/sponsors/brianc) its development.
## Contributing
__:heart: contributions!__
**:heart: contributions!**
I will __happily__ accept your pull request if it:
- __has tests__
I will **happily** accept your pull request if it:
- **has tests**
- looks reasonable
- does not break backwards compatibility
If your change involves breaking backwards compatibility please please point that out in the pull request & we can discuss & plan when and how to release it and what type of documentation or communicate it will require.
## Troubleshooting and FAQ
The causes and solutions to common errors can be found among the [Frequently Asked Questions (FAQ)](https://github.com/brianc/node-postgres/wiki/FAQ)
@ -81,21 +77,20 @@ The causes and solutions to common errors can be found among the [Frequently Ask
Copyright (c) 2010-2020 Brian Carlson (brian.m.carlson@gmail.com)
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:
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:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
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.
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
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.

View File

@ -18,9 +18,6 @@ var ConnectionParameters = require('./connection-parameters')
var Query = require('./query')
var defaults = require('./defaults')
var Connection = require('./connection')
if (process.env.PG_FAST_CONNECTION) {
Connection = require('./connection-fast')
}
var Client = function (config) {
EventEmitter.call(this)

View File

@ -1,216 +0,0 @@
'use strict'
/**
* Copyright (c) 2010-2017 Brian Carlson (brian.m.carlson@gmail.com)
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* README.md file in the root directory of this source tree.
*/
var net = require('net')
var EventEmitter = require('events').EventEmitter
var util = require('util')
// eslint-disable-next-line
const { parse, serialize } = require('../../pg-protocol/dist')
// TODO(bmc) support binary mode here
// var BINARY_MODE = 1
console.log('***using faster connection***')
var Connection = function (config) {
EventEmitter.call(this)
config = config || {}
this.stream = config.stream || new net.Socket()
this.stream.setNoDelay(true)
this._keepAlive = config.keepAlive
this._keepAliveInitialDelayMillis = config.keepAliveInitialDelayMillis
this.lastBuffer = false
this.parsedStatements = {}
this.ssl = config.ssl || false
this._ending = false
this._emitMessage = false
var self = this
this.on('newListener', function (eventName) {
if (eventName === 'message') {
self._emitMessage = true
}
})
}
util.inherits(Connection, EventEmitter)
Connection.prototype.connect = function (port, host) {
var self = this
this._connecting = true
this.stream.connect(port, host)
this.stream.once('connect', function () {
if (self._keepAlive) {
self.stream.setKeepAlive(true, self._keepAliveInitialDelayMillis)
}
self.emit('connect')
})
const reportStreamError = function (error) {
// errors about disconnections should be ignored during disconnect
if (self._ending && (error.code === 'ECONNRESET' || error.code === 'EPIPE')) {
return
}
self.emit('error', error)
}
this.stream.on('error', reportStreamError)
this.stream.on('close', function () {
self.emit('end')
})
if (!this.ssl) {
return this.attachListeners(this.stream)
}
this.stream.once('data', function (buffer) {
var responseCode = buffer.toString('utf8')
switch (responseCode) {
case 'S': // Server supports SSL connections, continue with a secure connection
break
case 'N': // Server does not support SSL connections
self.stream.end()
return self.emit('error', new Error('The server does not support SSL connections'))
default:
// Any other response byte, including 'E' (ErrorResponse) indicating a server error
self.stream.end()
return self.emit('error', new Error('There was an error establishing an SSL connection'))
}
var tls = require('tls')
const options = Object.assign(
{
socket: self.stream,
},
self.ssl
)
if (net.isIP(host) === 0) {
options.servername = host
}
self.stream = tls.connect(options)
self.attachListeners(self.stream)
self.stream.on('error', reportStreamError)
self.emit('sslconnect')
})
}
Connection.prototype.attachListeners = function (stream) {
stream.on('end', () => {
this.emit('end')
})
parse(stream, (msg) => {
var eventName = msg.name === 'error' ? 'errorMessage' : msg.name
if (this._emitMessage) {
this.emit('message', msg)
}
this.emit(eventName, msg)
})
}
Connection.prototype.requestSsl = function () {
this.stream.write(serialize.requestSsl())
}
Connection.prototype.startup = function (config) {
this.stream.write(serialize.startup(config))
}
Connection.prototype.cancel = function (processID, secretKey) {
this._send(serialize.cancel(processID, secretKey))
}
Connection.prototype.password = function (password) {
this._send(serialize.password(password))
}
Connection.prototype.sendSASLInitialResponseMessage = function (mechanism, initialResponse) {
this._send(serialize.sendSASLInitialResponseMessage(mechanism, initialResponse))
}
Connection.prototype.sendSCRAMClientFinalMessage = function (additionalData) {
this._send(serialize.sendSCRAMClientFinalMessage(additionalData))
}
Connection.prototype._send = function (buffer) {
if (!this.stream.writable) {
return false
}
return this.stream.write(buffer)
}
Connection.prototype.query = function (text) {
this._send(serialize.query(text))
}
// send parse message
Connection.prototype.parse = function (query) {
this._send(serialize.parse(query))
}
// send bind message
// "more" === true to buffer the message until flush() is called
Connection.prototype.bind = function (config) {
this._send(serialize.bind(config))
}
// send execute message
// "more" === true to buffer the message until flush() is called
Connection.prototype.execute = function (config) {
this._send(serialize.execute(config))
}
const flushBuffer = serialize.flush()
Connection.prototype.flush = function () {
if (this.stream.writable) {
this.stream.write(flushBuffer)
}
}
const syncBuffer = serialize.sync()
Connection.prototype.sync = function () {
this._ending = true
this._send(syncBuffer)
this._send(flushBuffer)
}
const endBuffer = serialize.end()
Connection.prototype.end = function () {
// 0x58 = 'X'
this._ending = true
if (!this._connecting || !this.stream.writable) {
this.stream.end()
return
}
return this.stream.write(endBuffer, () => {
this.stream.end()
})
}
Connection.prototype.close = function (msg) {
this._send(serialize.close(msg))
}
Connection.prototype.describe = function (msg) {
this._send(serialize.describe(msg))
}
Connection.prototype.sendCopyFromChunk = function (chunk) {
this._send(serialize.copyData(chunk))
}
Connection.prototype.endCopyFrom = function () {
this._send(serialize.copyDone())
}
Connection.prototype.sendCopyFail = function (msg) {
this._send(serialize.copyFail(msg))
}
module.exports = Connection

View File

@ -25,7 +25,7 @@ var val = function (key, config, envVar) {
return config[key] || envVar || defaults[key]
}
var useSsl = function () {
var readSSLConfigFromEnvironment = function () {
switch (process.env.PGSSLMODE) {
case 'disable':
return false
@ -34,6 +34,8 @@ var useSsl = function () {
case 'verify-ca':
case 'verify-full':
return true
case 'no-verify':
return { rejectUnauthorized: false }
}
return defaults.ssl
}
@ -68,7 +70,14 @@ var ConnectionParameters = function (config) {
})
this.binary = val('binary', config)
this.ssl = typeof config.ssl === 'undefined' ? useSsl() : config.ssl
this.ssl = typeof config.ssl === 'undefined' ? readSSLConfigFromEnvironment() : config.ssl
// support passing in ssl=no-verify via connection string
if (this.ssl === 'no-verify') {
this.ssl = { rejectUnauthorized: false }
}
this.client_encoding = val('client_encoding', config)
this.replication = val('replication', config)
// a domain socket begins with '/'

View File

@ -11,11 +11,9 @@ var net = require('net')
var EventEmitter = require('events').EventEmitter
var util = require('util')
var Writer = require('buffer-writer')
var Reader = require('packet-reader')
const { parse, serialize } = require('pg-protocol')
var TEXT_MODE = 0
var BINARY_MODE = 1
// TODO(bmc) support binary mode at some point
var Connection = function (config) {
EventEmitter.call(this)
config = config || {}
@ -23,20 +21,10 @@ var Connection = function (config) {
this._keepAlive = config.keepAlive
this._keepAliveInitialDelayMillis = config.keepAliveInitialDelayMillis
this.lastBuffer = false
this.lastOffset = 0
this.buffer = null
this.offset = null
this.encoding = config.encoding || 'utf8'
this.parsedStatements = {}
this.writer = new Writer()
this.ssl = config.ssl || false
this._ending = false
this._mode = TEXT_MODE
this._emitMessage = false
this._reader = new Reader({
headerSize: 1,
lengthPadding: -4,
})
var self = this
this.on('newListener', function (eventName) {
if (eventName === 'message') {
@ -51,6 +39,7 @@ Connection.prototype.connect = function (port, host) {
var self = this
this._connecting = true
this.stream.setNoDelay(true)
this.stream.connect(port, host)
this.stream.once('connect', function () {
@ -101,576 +90,124 @@ Connection.prototype.connect = function (port, host) {
options.servername = host
}
self.stream = tls.connect(options)
self.stream.on('error', reportStreamError)
self.attachListeners(self.stream)
self.stream.on('error', reportStreamError)
self.emit('sslconnect')
})
}
Connection.prototype.attachListeners = function (stream) {
var self = this
stream.on('data', function (buff) {
self._reader.addChunk(buff)
var packet = self._reader.read()
while (packet) {
var msg = self.parseMessage(packet)
var eventName = msg.name === 'error' ? 'errorMessage' : msg.name
if (self._emitMessage) {
self.emit('message', msg)
}
self.emit(eventName, msg)
packet = self._reader.read()
}
stream.on('end', () => {
this.emit('end')
})
stream.on('end', function () {
self.emit('end')
parse(stream, (msg) => {
var eventName = msg.name === 'error' ? 'errorMessage' : msg.name
if (this._emitMessage) {
this.emit('message', msg)
}
this.emit(eventName, msg)
})
}
Connection.prototype.requestSsl = function () {
var bodyBuffer = this.writer.addInt16(0x04d2).addInt16(0x162f).flush()
var length = bodyBuffer.length + 4
var buffer = new Writer().addInt32(length).add(bodyBuffer).join()
this.stream.write(buffer)
this.stream.write(serialize.requestSsl())
}
Connection.prototype.startup = function (config) {
var writer = this.writer.addInt16(3).addInt16(0)
Object.keys(config).forEach(function (key) {
var val = config[key]
writer.addCString(key).addCString(val)
})
writer.addCString('client_encoding').addCString('UTF8')
var bodyBuffer = writer.addCString('').flush()
// this message is sent without a code
var length = bodyBuffer.length + 4
var buffer = new Writer().addInt32(length).add(bodyBuffer).join()
this.stream.write(buffer)
this.stream.write(serialize.startup(config))
}
Connection.prototype.cancel = function (processID, secretKey) {
var bodyBuffer = this.writer.addInt16(1234).addInt16(5678).addInt32(processID).addInt32(secretKey).flush()
var length = bodyBuffer.length + 4
var buffer = new Writer().addInt32(length).add(bodyBuffer).join()
this.stream.write(buffer)
this._send(serialize.cancel(processID, secretKey))
}
Connection.prototype.password = function (password) {
// 0x70 = 'p'
this._send(0x70, this.writer.addCString(password))
this._send(serialize.password(password))
}
Connection.prototype.sendSASLInitialResponseMessage = function (mechanism, initialResponse) {
// 0x70 = 'p'
this.writer.addCString(mechanism).addInt32(Buffer.byteLength(initialResponse)).addString(initialResponse)
this._send(0x70)
this._send(serialize.sendSASLInitialResponseMessage(mechanism, initialResponse))
}
Connection.prototype.sendSCRAMClientFinalMessage = function (additionalData) {
// 0x70 = 'p'
this.writer.addString(additionalData)
this._send(0x70)
this._send(serialize.sendSCRAMClientFinalMessage(additionalData))
}
Connection.prototype._send = function (code, more) {
Connection.prototype._send = function (buffer) {
if (!this.stream.writable) {
return false
}
if (more === true) {
this.writer.addHeader(code)
} else {
return this.stream.write(this.writer.flush(code))
}
return this.stream.write(buffer)
}
Connection.prototype.query = function (text) {
// 0x51 = Q
this.stream.write(this.writer.addCString(text).flush(0x51))
this._send(serialize.query(text))
}
// send parse message
// "more" === true to buffer the message until flush() is called
Connection.prototype.parse = function (query, more) {
// expect something like this:
// { name: 'queryName',
// text: 'select * from blah',
// types: ['int8', 'bool'] }
// normalize missing query names to allow for null
query.name = query.name || ''
if (query.name.length > 63) {
/* eslint-disable no-console */
console.error('Warning! Postgres only supports 63 characters for query names.')
console.error('You supplied %s (%s)', query.name, query.name.length)
console.error('This can cause conflicts and silent errors executing queries')
/* eslint-enable no-console */
}
// normalize null type array
query.types = query.types || []
var len = query.types.length
var buffer = this.writer
.addCString(query.name) // name of query
.addCString(query.text) // actual query text
.addInt16(len)
for (var i = 0; i < len; i++) {
buffer.addInt32(query.types[i])
}
var code = 0x50
this._send(code, more)
Connection.prototype.parse = function (query) {
this._send(serialize.parse(query))
}
// send bind message
// "more" === true to buffer the message until flush() is called
Connection.prototype.bind = function (config, more) {
// normalize config
config = config || {}
config.portal = config.portal || ''
config.statement = config.statement || ''
config.binary = config.binary || false
var values = config.values || []
var len = values.length
var useBinary = false
for (var j = 0; j < len; j++) {
useBinary |= values[j] instanceof Buffer
}
var buffer = this.writer.addCString(config.portal).addCString(config.statement)
if (!useBinary) {
buffer.addInt16(0)
} else {
buffer.addInt16(len)
for (j = 0; j < len; j++) {
buffer.addInt16(values[j] instanceof Buffer)
}
}
buffer.addInt16(len)
for (var i = 0; i < len; i++) {
var val = values[i]
if (val === null || typeof val === 'undefined') {
buffer.addInt32(-1)
} else if (val instanceof Buffer) {
buffer.addInt32(val.length)
buffer.add(val)
} else {
buffer.addInt32(Buffer.byteLength(val))
buffer.addString(val)
}
}
if (config.binary) {
buffer.addInt16(1) // format codes to use binary
buffer.addInt16(1)
} else {
buffer.addInt16(0) // format codes to use text
}
// 0x42 = 'B'
this._send(0x42, more)
Connection.prototype.bind = function (config) {
this._send(serialize.bind(config))
}
// send execute message
// "more" === true to buffer the message until flush() is called
Connection.prototype.execute = function (config, more) {
config = config || {}
config.portal = config.portal || ''
config.rows = config.rows || ''
this.writer.addCString(config.portal).addInt32(config.rows)
// 0x45 = 'E'
this._send(0x45, more)
Connection.prototype.execute = function (config) {
this._send(serialize.execute(config))
}
var emptyBuffer = Buffer.alloc(0)
const flushBuffer = serialize.flush()
Connection.prototype.flush = function () {
// 0x48 = 'H'
this.writer.add(emptyBuffer)
this._send(0x48)
if (this.stream.writable) {
this.stream.write(flushBuffer)
}
}
const syncBuffer = serialize.sync()
Connection.prototype.sync = function () {
// clear out any pending data in the writer
this.writer.flush(0)
this.writer.add(emptyBuffer)
this._ending = true
this._send(0x53)
this._send(syncBuffer)
this._send(flushBuffer)
}
const END_BUFFER = Buffer.from([0x58, 0x00, 0x00, 0x00, 0x04])
const endBuffer = serialize.end()
Connection.prototype.end = function () {
// 0x58 = 'X'
this.writer.add(emptyBuffer)
this._ending = true
if (!this._connecting || !this.stream.writable) {
this.stream.end()
return
}
return this.stream.write(END_BUFFER, () => {
return this.stream.write(endBuffer, () => {
this.stream.end()
})
}
Connection.prototype.close = function (msg, more) {
this.writer.addCString(msg.type + (msg.name || ''))
this._send(0x43, more)
Connection.prototype.close = function (msg) {
this._send(serialize.close(msg))
}
Connection.prototype.describe = function (msg, more) {
this.writer.addCString(msg.type + (msg.name || ''))
this._send(0x44, more)
Connection.prototype.describe = function (msg) {
this._send(serialize.describe(msg))
}
Connection.prototype.sendCopyFromChunk = function (chunk) {
this.stream.write(this.writer.add(chunk).flush(0x64))
this._send(serialize.copyData(chunk))
}
Connection.prototype.endCopyFrom = function () {
this.stream.write(this.writer.add(emptyBuffer).flush(0x63))
this._send(serialize.copyDone())
}
Connection.prototype.sendCopyFail = function (msg) {
// this.stream.write(this.writer.add(emptyBuffer).flush(0x66));
this.writer.addCString(msg)
this._send(0x66)
this._send(serialize.copyFail(msg))
}
var Message = function (name, length) {
this.name = name
this.length = length
}
Connection.prototype.parseMessage = function (buffer) {
this.offset = 0
var length = buffer.length + 4
switch (this._reader.header) {
case 0x52: // R
return this.parseR(buffer, length)
case 0x53: // S
return this.parseS(buffer, length)
case 0x4b: // K
return this.parseK(buffer, length)
case 0x43: // C
return this.parseC(buffer, length)
case 0x5a: // Z
return this.parseZ(buffer, length)
case 0x54: // T
return this.parseT(buffer, length)
case 0x44: // D
return this.parseD(buffer, length)
case 0x45: // E
return this.parseE(buffer, length)
case 0x4e: // N
return this.parseN(buffer, length)
case 0x31: // 1
return new Message('parseComplete', length)
case 0x32: // 2
return new Message('bindComplete', length)
case 0x33: // 3
return new Message('closeComplete', length)
case 0x41: // A
return this.parseA(buffer, length)
case 0x6e: // n
return new Message('noData', length)
case 0x49: // I
return new Message('emptyQuery', length)
case 0x73: // s
return new Message('portalSuspended', length)
case 0x47: // G
return this.parseG(buffer, length)
case 0x48: // H
return this.parseH(buffer, length)
case 0x57: // W
return new Message('replicationStart', length)
case 0x63: // c
return new Message('copyDone', length)
case 0x64: // d
return this.parsed(buffer, length)
}
}
Connection.prototype.parseR = function (buffer, length) {
var code = this.parseInt32(buffer)
var msg = new Message('authenticationOk', length)
switch (code) {
case 0: // AuthenticationOk
return msg
case 3: // AuthenticationCleartextPassword
if (msg.length === 8) {
msg.name = 'authenticationCleartextPassword'
return msg
}
break
case 5: // AuthenticationMD5Password
if (msg.length === 12) {
msg.name = 'authenticationMD5Password'
msg.salt = Buffer.alloc(4)
buffer.copy(msg.salt, 0, this.offset, this.offset + 4)
this.offset += 4
return msg
}
break
case 10: // AuthenticationSASL
msg.name = 'authenticationSASL'
msg.mechanisms = []
do {
var mechanism = this.parseCString(buffer)
if (mechanism) {
msg.mechanisms.push(mechanism)
}
} while (mechanism)
return msg
case 11: // AuthenticationSASLContinue
msg.name = 'authenticationSASLContinue'
msg.data = this.readString(buffer, length - 4)
return msg
case 12: // AuthenticationSASLFinal
msg.name = 'authenticationSASLFinal'
msg.data = this.readString(buffer, length - 4)
return msg
}
throw new Error('Unknown authenticationOk message type' + util.inspect(msg))
}
Connection.prototype.parseS = function (buffer, length) {
var msg = new Message('parameterStatus', length)
msg.parameterName = this.parseCString(buffer)
msg.parameterValue = this.parseCString(buffer)
return msg
}
Connection.prototype.parseK = function (buffer, length) {
var msg = new Message('backendKeyData', length)
msg.processID = this.parseInt32(buffer)
msg.secretKey = this.parseInt32(buffer)
return msg
}
Connection.prototype.parseC = function (buffer, length) {
var msg = new Message('commandComplete', length)
msg.text = this.parseCString(buffer)
return msg
}
Connection.prototype.parseZ = function (buffer, length) {
var msg = new Message('readyForQuery', length)
msg.name = 'readyForQuery'
msg.status = this.readString(buffer, 1)
return msg
}
var ROW_DESCRIPTION = 'rowDescription'
Connection.prototype.parseT = function (buffer, length) {
var msg = new Message(ROW_DESCRIPTION, length)
msg.fieldCount = this.parseInt16(buffer)
var fields = []
for (var i = 0; i < msg.fieldCount; i++) {
fields.push(this.parseField(buffer))
}
msg.fields = fields
return msg
}
var Field = function () {
this.name = null
this.tableID = null
this.columnID = null
this.dataTypeID = null
this.dataTypeSize = null
this.dataTypeModifier = null
this.format = null
}
var FORMAT_TEXT = 'text'
var FORMAT_BINARY = 'binary'
Connection.prototype.parseField = function (buffer) {
var field = new Field()
field.name = this.parseCString(buffer)
field.tableID = this.parseInt32(buffer)
field.columnID = this.parseInt16(buffer)
field.dataTypeID = this.parseInt32(buffer)
field.dataTypeSize = this.parseInt16(buffer)
field.dataTypeModifier = this.parseInt32(buffer)
if (this.parseInt16(buffer) === TEXT_MODE) {
this._mode = TEXT_MODE
field.format = FORMAT_TEXT
} else {
this._mode = BINARY_MODE
field.format = FORMAT_BINARY
}
return field
}
var DATA_ROW = 'dataRow'
var DataRowMessage = function (length, fieldCount) {
this.name = DATA_ROW
this.length = length
this.fieldCount = fieldCount
this.fields = []
}
// extremely hot-path code
Connection.prototype.parseD = function (buffer, length) {
var fieldCount = this.parseInt16(buffer)
var msg = new DataRowMessage(length, fieldCount)
for (var i = 0; i < fieldCount; i++) {
msg.fields.push(this._readValue(buffer))
}
return msg
}
// extremely hot-path code
Connection.prototype._readValue = function (buffer) {
var length = this.parseInt32(buffer)
if (length === -1) return null
if (this._mode === TEXT_MODE) {
return this.readString(buffer, length)
}
return this.readBytes(buffer, length)
}
// parses error
Connection.prototype.parseE = function (buffer, length, isNotice) {
var fields = {}
var fieldType = this.readString(buffer, 1)
while (fieldType !== '\0') {
fields[fieldType] = this.parseCString(buffer)
fieldType = this.readString(buffer, 1)
}
// the msg is an Error instance
var msg = isNotice ? { message: fields.M } : new Error(fields.M)
// for compatibility with Message
msg.name = isNotice ? 'notice' : 'error'
msg.length = length
msg.severity = fields.S
msg.code = fields.C
msg.detail = fields.D
msg.hint = fields.H
msg.position = fields.P
msg.internalPosition = fields.p
msg.internalQuery = fields.q
msg.where = fields.W
msg.schema = fields.s
msg.table = fields.t
msg.column = fields.c
msg.dataType = fields.d
msg.constraint = fields.n
msg.file = fields.F
msg.line = fields.L
msg.routine = fields.R
return msg
}
// same thing, different name
Connection.prototype.parseN = function (buffer, length) {
var msg = this.parseE(buffer, length, true)
msg.name = 'notice'
return msg
}
Connection.prototype.parseA = function (buffer, length) {
var msg = new Message('notification', length)
msg.processId = this.parseInt32(buffer)
msg.channel = this.parseCString(buffer)
msg.payload = this.parseCString(buffer)
return msg
}
Connection.prototype.parseG = function (buffer, length) {
var msg = new Message('copyInResponse', length)
return this.parseGH(buffer, msg)
}
Connection.prototype.parseH = function (buffer, length) {
var msg = new Message('copyOutResponse', length)
return this.parseGH(buffer, msg)
}
Connection.prototype.parseGH = function (buffer, msg) {
var isBinary = buffer[this.offset] !== 0
this.offset++
msg.binary = isBinary
var columnCount = this.parseInt16(buffer)
msg.columnTypes = []
for (var i = 0; i < columnCount; i++) {
msg.columnTypes.push(this.parseInt16(buffer))
}
return msg
}
Connection.prototype.parsed = function (buffer, length) {
var msg = new Message('copyData', length)
msg.chunk = this.readBytes(buffer, msg.length - 4)
return msg
}
Connection.prototype.parseInt32 = function (buffer) {
var value = buffer.readInt32BE(this.offset)
this.offset += 4
return value
}
Connection.prototype.parseInt16 = function (buffer) {
var value = buffer.readInt16BE(this.offset)
this.offset += 2
return value
}
Connection.prototype.readString = function (buffer, length) {
return buffer.toString(this.encoding, this.offset, (this.offset += length))
}
Connection.prototype.readBytes = function (buffer, length) {
return buffer.slice(this.offset, (this.offset += length))
}
Connection.prototype.parseCString = function (buffer) {
var start = this.offset
var end = buffer.indexOf(0, start)
this.offset = end + 1
return buffer.toString(this.encoding, start, end)
}
// end parsing methods
module.exports = Connection

View File

@ -1,6 +1,6 @@
{
"name": "pg",
"version": "8.0.2",
"version": "8.2.0",
"description": "PostgreSQL client - pure javascript & libpq with the same API",
"keywords": [
"database",
@ -21,9 +21,9 @@
"dependencies": {
"buffer-writer": "2.0.0",
"packet-reader": "1.0.0",
"pg-connection-string": "0.1.3",
"pg-pool": "^3.1.0",
"pg-protocol": "^1.2.1",
"pg-connection-string": "^2.2.2",
"pg-pool": "^3.2.0",
"pg-protocol": "^1.2.3",
"pg-types": "^2.1.0",
"pgpass": "1.x",
"semver": "4.3.2"

View File

@ -1,41 +1,75 @@
'use strict'
var helper = require(__dirname + '/../test-helper')
var pg = helper.pg
const helper = require('./../test-helper')
const pg = helper.pg
const suite = new helper.Suite()
const { native } = helper.args
var suite = new helper.Suite()
/**
* This test only executes if the env variables SCRAM_TEST_PGUSER and
* SCRAM_TEST_PGPASSWORD are defined. You can override additional values
* for the host, port and database with other SCRAM_TEST_ prefixed vars.
* If the variables are not defined the test will be skipped.
*
* SQL to create test role:
*
* SET password_encryption = 'scram-sha-256';
* CREATE ROLE scram_test login password 'test4scram';
*
* Add the following entries to pg_hba.conf:
*
* host all scram_test ::1/128 scram-sha-256
* host all scram_test 0.0.0.0/0 scram-sha-256
*
* Then run this file with after exporting:
*
* SCRAM_TEST_PGUSER=scram_test
* SCRAM_TEST_PGPASSWORD=test4scram
*/
/*
SQL to create test role:
// Base config for SCRAM tests
const config = {
user: process.env.SCRAM_TEST_PGUSER,
password: process.env.SCRAM_TEST_PGPASSWORD,
host: process.env.SCRAM_TEST_PGHOST, // optional
port: process.env.SCRAM_TEST_PGPORT, // optional
database: process.env.SCRAM_TEST_PGDATABASE, // optional
}
set password_encryption = 'scram-sha-256';
create role npgtest login password 'test';
if (native) {
suite.testAsync('skipping SCRAM tests (on native)', () => {})
return
}
if (!config.user || !config.password) {
suite.testAsync('skipping SCRAM tests (missing env)', () => {})
return
}
pg_hba:
host all npgtest ::1/128 scram-sha-256
host all npgtest 0.0.0.0/0 scram-sha-256
*/
/*
suite.test('can connect using sasl/scram', function () {
var connectionString = 'pg://npgtest:test@localhost/postgres'
const pool = new pg.Pool({ connectionString: connectionString })
pool.connect(
assert.calls(function (err, client, done) {
assert.ifError(err, 'should have connected')
done()
})
)
suite.testAsync('can connect using sasl/scram', async () => {
const client = new pg.Client(config)
let usingSasl = false
client.connection.once('authenticationSASL', () => {
usingSasl = true
})
await client.connect()
assert.ok(usingSasl, 'Should be using SASL for authentication')
await client.end()
})
suite.test('sasl/scram fails when password is wrong', function () {
var connectionString = 'pg://npgtest:bad@localhost/postgres'
const pool = new pg.Pool({ connectionString: connectionString })
pool.connect(
assert.calls(function (err, client, done) {
assert.ok(err, 'should have a connection error')
done()
})
)
suite.testAsync('sasl/scram fails when password is wrong', async () => {
const client = new pg.Client({
...config,
password: config.password + 'append-something-to-make-it-bad',
})
let usingSasl = false
client.connection.once('authenticationSASL', () => {
usingSasl = true
})
await assert.rejects(
() => client.connect(),
{
code: '28P01',
},
'Error code should be for a password error'
)
assert.ok(usingSasl, 'Should be using SASL for authentication')
})
*/

View File

@ -1,5 +1,6 @@
'use strict'
require(__dirname + '/test-helper')
var assert = require('assert')
var pguser = process.env['PGUSER'] || process.env.USER
var pgdatabase = process.env['PGDATABASE'] || process.env.USER

View File

@ -5,6 +5,7 @@ var Client = require(__dirname + '/../../../lib/client')
test('emits end when not in query', function () {
var stream = new (require('events').EventEmitter)()
stream.setNoDelay = () => {}
stream.connect = function () {
// NOOP
}

View File

@ -1,5 +1,7 @@
'use strict'
var helper = require(__dirname + '/../test-helper')
const Suite = require('../../suite')
var assert = require('assert')
var ConnectionParameters = require(__dirname + '/../../../lib/connection-parameters')
var defaults = require(__dirname + '/../../../lib').defaults
@ -11,7 +13,17 @@ for (var key in process.env) {
delete process.env[key]
}
test('ConnectionParameters initialized from environment variables', function (t) {
const suite = new Suite('ConnectionParameters')
const clearEnv = () => {
// clear process.env
for (var key in process.env) {
delete process.env[key]
}
}
suite.test('ConnectionParameters initialized from environment variables', function () {
clearEnv()
process.env['PGHOST'] = 'local'
process.env['PGUSER'] = 'bmc2'
process.env['PGPORT'] = 7890
@ -26,7 +38,13 @@ test('ConnectionParameters initialized from environment variables', function (t)
assert.equal(subject.password, 'open', 'env password')
})
test('ConnectionParameters initialized from mix', function (t) {
suite.test('ConnectionParameters initialized from mix', function () {
clearEnv()
process.env['PGHOST'] = 'local'
process.env['PGUSER'] = 'bmc2'
process.env['PGPORT'] = 7890
process.env['PGDATABASE'] = 'allyerbase'
process.env['PGPASSWORD'] = 'open'
delete process.env['PGPASSWORD']
delete process.env['PGDATABASE']
var subject = new ConnectionParameters({
@ -40,12 +58,8 @@ test('ConnectionParameters initialized from mix', function (t) {
assert.equal(subject.password, defaults.password, 'defaults password')
})
// clear process.env
for (var key in process.env) {
delete process.env[key]
}
test('connection string parsing', function (t) {
suite.test('connection string parsing', function () {
clearEnv()
var string = 'postgres://brian:pw@boom:381/lala'
var subject = new ConnectionParameters(string)
assert.equal(subject.host, 'boom', 'string host')
@ -55,7 +69,10 @@ test('connection string parsing', function (t) {
assert.equal(subject.database, 'lala', 'string database')
})
test('connection string parsing - ssl', function (t) {
suite.test('connection string parsing - ssl', function () {
// clear process.env
clearEnv()
var string = 'postgres://brian:pw@boom:381/lala?ssl=true'
var subject = new ConnectionParameters(string)
assert.equal(subject.ssl, true, 'ssl')
@ -75,27 +92,24 @@ test('connection string parsing - ssl', function (t) {
string = 'postgres://brian:pw@boom:381/lala'
subject = new ConnectionParameters(string)
assert.equal(!!subject.ssl, false, 'ssl')
string = 'postgres://brian:pw@boom:381/lala?ssl=no-verify'
subject = new ConnectionParameters(string)
assert.deepStrictEqual(subject.ssl, { rejectUnauthorized: false }, 'ssl')
})
// clear process.env
for (var key in process.env) {
delete process.env[key]
}
test('ssl is false by default', function () {
suite.test('ssl is false by default', function () {
clearEnv()
var subject = new ConnectionParameters()
assert.equal(subject.ssl, false)
})
var testVal = function (mode, expected) {
// clear process.env
for (var key in process.env) {
delete process.env[key]
}
process.env.PGSSLMODE = mode
test('ssl is ' + expected + ' when $PGSSLMODE=' + mode, function () {
suite.test('ssl is ' + expected + ' when $PGSSLMODE=' + mode, function () {
clearEnv()
process.env.PGSSLMODE = mode
var subject = new ConnectionParameters()
assert.equal(subject.ssl, expected)
assert.deepStrictEqual(subject.ssl, expected)
})
}
@ -106,6 +120,7 @@ testVal('prefer', true)
testVal('require', true)
testVal('verify-ca', true)
testVal('verify-full', true)
testVal('no-verify', { rejectUnauthorized: false })
// restore process.env
for (var key in realEnv) {

View File

@ -1,205 +0,0 @@
'use strict'
require(__dirname + '/test-helper')
var Connection = require(__dirname + '/../../../lib/connection')
var stream = new MemoryStream()
var con = new Connection({
stream: stream,
})
con._connecting = true
assert.received = function (stream, buffer) {
assert.lengthIs(stream.packets, 1)
var packet = stream.packets.pop()
assert.equalBuffers(packet, buffer)
}
test('sends startup message', function () {
con.startup({
user: 'brian',
database: 'bang',
})
assert.received(
stream,
new BufferList()
.addInt16(3)
.addInt16(0)
.addCString('user')
.addCString('brian')
.addCString('database')
.addCString('bang')
.addCString('client_encoding')
.addCString('UTF8')
.addCString('')
.join(true)
)
})
test('sends password message', function () {
con.password('!')
assert.received(stream, new BufferList().addCString('!').join(true, 'p'))
})
test('sends SASLInitialResponseMessage message', function () {
con.sendSASLInitialResponseMessage('mech', 'data')
assert.received(stream, new BufferList().addCString('mech').addInt32(4).addString('data').join(true, 'p'))
})
test('sends SCRAMClientFinalMessage message', function () {
con.sendSCRAMClientFinalMessage('data')
assert.received(stream, new BufferList().addString('data').join(true, 'p'))
})
test('sends query message', function () {
var txt = 'select * from boom'
con.query(txt)
assert.received(stream, new BufferList().addCString(txt).join(true, 'Q'))
})
test('sends parse message', function () {
con.parse({ text: '!' })
var expected = new BufferList().addCString('').addCString('!').addInt16(0).join(true, 'P')
assert.received(stream, expected)
})
test('sends parse message with named query', function () {
con.parse({
name: 'boom',
text: 'select * from boom',
types: [],
})
var expected = new BufferList().addCString('boom').addCString('select * from boom').addInt16(0).join(true, 'P')
assert.received(stream, expected)
test('with multiple parameters', function () {
con.parse({
name: 'force',
text: 'select * from bang where name = $1',
types: [1, 2, 3, 4],
})
var expected = new BufferList()
.addCString('force')
.addCString('select * from bang where name = $1')
.addInt16(4)
.addInt32(1)
.addInt32(2)
.addInt32(3)
.addInt32(4)
.join(true, 'P')
assert.received(stream, expected)
})
})
test('bind messages', function () {
test('with no values', function () {
con.bind()
var expectedBuffer = new BufferList()
.addCString('')
.addCString('')
.addInt16(0)
.addInt16(0)
.addInt16(0)
.join(true, 'B')
assert.received(stream, expectedBuffer)
})
test('with named statement, portal, and values', function () {
con.bind({
portal: 'bang',
statement: 'woo',
values: ['1', 'hi', null, 'zing'],
})
var expectedBuffer = new BufferList()
.addCString('bang') // portal name
.addCString('woo') // statement name
.addInt16(0)
.addInt16(4)
.addInt32(1)
.add(Buffer.from('1'))
.addInt32(2)
.add(Buffer.from('hi'))
.addInt32(-1)
.addInt32(4)
.add(Buffer.from('zing'))
.addInt16(0)
.join(true, 'B')
assert.received(stream, expectedBuffer)
})
})
test('with named statement, portal, and buffer value', function () {
con.bind({
portal: 'bang',
statement: 'woo',
values: ['1', 'hi', null, Buffer.from('zing', 'utf8')],
})
var expectedBuffer = new BufferList()
.addCString('bang') // portal name
.addCString('woo') // statement name
.addInt16(4) // value count
.addInt16(0) // string
.addInt16(0) // string
.addInt16(0) // string
.addInt16(1) // binary
.addInt16(4)
.addInt32(1)
.add(Buffer.from('1'))
.addInt32(2)
.add(Buffer.from('hi'))
.addInt32(-1)
.addInt32(4)
.add(Buffer.from('zing', 'UTF-8'))
.addInt16(0)
.join(true, 'B')
assert.received(stream, expectedBuffer)
})
test('sends execute message', function () {
test('for unamed portal with no row limit', function () {
con.execute()
var expectedBuffer = new BufferList().addCString('').addInt32(0).join(true, 'E')
assert.received(stream, expectedBuffer)
})
test('for named portal with row limit', function () {
con.execute({
portal: 'my favorite portal',
rows: 100,
})
var expectedBuffer = new BufferList().addCString('my favorite portal').addInt32(100).join(true, 'E')
assert.received(stream, expectedBuffer)
})
})
test('sends flush command', function () {
con.flush()
var expected = new BufferList().join(true, 'H')
assert.received(stream, expected)
})
test('sends sync command', function () {
con.sync()
var expected = new BufferList().join(true, 'S')
assert.received(stream, expected)
})
test('sends end command', function () {
con.end()
var expected = Buffer.from([0x58, 0, 0, 0, 4])
assert.received(stream, expected)
assert.equal(stream.closed, true)
})
test('sends describe command', function () {
test('describe statement', function () {
con.describe({ type: 'S', name: 'bang' })
var expected = new BufferList().addChar('S').addCString('bang').join(true, 'D')
assert.received(stream, expected)
})
test('describe unnamed portal', function () {
con.describe({ type: 'P' })
var expected = new BufferList().addChar('P').addCString('').join(true, 'D')
assert.received(stream, expected)
})
})

View File

@ -17,6 +17,8 @@ p.connect = function () {
// NOOP
}
p.setNoDelay = () => {}
p.write = function (packet, cb) {
this.packets.push(packet)
if (cb) {

622
yarn.lock

File diff suppressed because it is too large Load Diff