package/LICENSE000644 0000002065 3560116604 010270 0ustar00000000 000000 MIT License Copyright (c) 2010 - 2021 Brian Carlson 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. package/lib/build-result.js000644 0000003641 3560116604 013003 0ustar00000000 000000 'use strict' class Result { constructor(types, arrayMode) { this._types = types this._arrayMode = arrayMode this.command = undefined this.rowCount = undefined this.fields = [] this.rows = [] this._prebuiltEmptyResultObject = null } consumeCommand(pq) { this.command = pq.cmdStatus().split(' ')[0] this.rowCount = parseInt(pq.cmdTuples(), 10) } consumeFields(pq) { const nfields = pq.nfields() this.fields = new Array(nfields) const row = {} for (let x = 0; x < nfields; x++) { const name = pq.fname(x) row[name] = null this.fields[x] = { name: name, dataTypeID: pq.ftype(x), } } this._prebuiltEmptyResultObject = { ...row } } consumeRows(pq) { const tupleCount = pq.ntuples() this.rows = new Array(tupleCount) for (let i = 0; i < tupleCount; i++) { this.rows[i] = this._arrayMode ? this.consumeRowAsArray(pq, i) : this.consumeRowAsObject(pq, i) } } consumeRowAsObject(pq, rowIndex) { const row = { ...this._prebuiltEmptyResultObject } for (let j = 0; j < this.fields.length; j++) { row[this.fields[j].name] = this.readValue(pq, rowIndex, j) } return row } consumeRowAsArray(pq, rowIndex) { const row = new Array(this.fields.length) for (let j = 0; j < this.fields.length; j++) { row[j] = this.readValue(pq, rowIndex, j) } return row } readValue(pq, rowIndex, colIndex) { const rawValue = pq.getvalue(rowIndex, colIndex) if (rawValue === '' && pq.getisnull(rowIndex, colIndex)) { return null } const dataTypeId = this.fields[colIndex].dataTypeID return this._types.getTypeParser(dataTypeId)(rawValue) } } function buildResult(pq, types, arrayMode) { const result = new Result(types, arrayMode) result.consumeCommand(pq) result.consumeFields(pq) result.consumeRows(pq) return result } module.exports = buildResult package/lib/copy-stream.js000644 0000007255 3560116604 012640 0ustar00000000 000000 const Duplex = require('stream').Duplex const Writable = require('stream').Writable const util = require('util') const CopyStream = (module.exports = function (pq, options) { Duplex.call(this, options) this.pq = pq this._reading = false }) util.inherits(CopyStream, Duplex) // writer methods CopyStream.prototype._write = function (chunk, encoding, cb) { const result = this.pq.putCopyData(chunk) // sent successfully if (result === 1) return cb() // error if (result === -1) return cb(new Error(this.pq.errorMessage())) // command would block. wait for writable and call again. const self = this this.pq.writable(function () { self._write(chunk, encoding, cb) }) } CopyStream.prototype.end = function () { const args = Array.prototype.slice.call(arguments, 0) const self = this const callback = args.pop() if (args.length) { this.write(args[0]) } const result = this.pq.putCopyEnd() // sent successfully if (result === 1) { // consume our results and then call 'end' on the // "parent" writable class so we can emit 'finish' and // all that jazz return consumeResults(this.pq, function (err, res) { Writable.prototype.end.call(self) // handle possible passing of callback to end method if (callback) { callback(err) } }) } // error if (result === -1) { const err = new Error(this.pq.errorMessage()) return this.emit('error', err) } // command would block. wait for writable and call end again // don't pass any buffers to end on the second call because // we already sent them to possible this.write the first time // we called end return this.pq.writable(function () { return self.end.apply(self, callback) }) } // reader methods CopyStream.prototype._consumeBuffer = function (cb) { const result = this.pq.getCopyData(true) if (result instanceof Buffer) { return setImmediate(function () { cb(null, result) }) } if (result === -1) { // end of stream return cb(null, null) } if (result === 0) { const self = this this.pq.once('readable', function () { self.pq.stopReader() self.pq.consumeInput() self._consumeBuffer(cb) }) return this.pq.startReader() } cb(new Error('Unrecognized read status: ' + result)) } CopyStream.prototype._read = function (size) { if (this._reading) return this._reading = true // console.log('read begin'); const self = this this._consumeBuffer(function (err, buffer) { self._reading = false if (err) { return self.emit('error', err) } if (buffer === false) { // nothing to read for now, return return } self.push(buffer) }) } const consumeResults = function (pq, cb) { const cleanup = function () { pq.removeListener('readable', onReadable) pq.stopReader() } const readError = function (message) { cleanup() return cb(new Error(message || pq.errorMessage())) } const onReadable = function () { // read waiting data from the socket // e.g. clear the pending 'select' if (!pq.consumeInput()) { return readError() } // check if there is still outstanding data // if so, wait for it all to come in if (pq.isBusy()) { return } // load our result object pq.getResult() // "read until results return null" // or in our case ensure we only have one result if (pq.getResult() && pq.resultStatus() !== 'PGRES_COPY_OUT') { return readError('Only one result at a time is accepted') } if (pq.resultStatus() === 'PGRES_FATAL_ERROR') { return readError() } cleanup() return cb(null) } pq.on('readable', onReadable) pq.startReader() } package/index.js000644 0000020350 3560116604 010725 0ustar00000000 000000 const Libpq = require('libpq') const EventEmitter = require('events').EventEmitter const util = require('util') const assert = require('assert') const types = require('pg-types') const buildResult = require('./lib/build-result') const CopyStream = require('./lib/copy-stream') const Client = (module.exports = function (config) { if (!(this instanceof Client)) { return new Client(config) } config = config || {} EventEmitter.call(this) this.pq = new Libpq() this._reading = false this._read = this._read.bind(this) // allow custom type conversion to be passed in this._types = config.types || types // allow config to specify returning results // as an array of values instead of a hash this.arrayMode = config.arrayMode || false this._resultCount = 0 this._rows = undefined this._results = undefined // lazy start the reader if notifications are listened for // this way if you only run sync queries you wont block // the event loop artificially this.on('newListener', (event) => { if (event !== 'notification') return this._startReading() }) this.on('result', this._onResult.bind(this)) this.on('readyForQuery', this._onReadyForQuery.bind(this)) }) util.inherits(Client, EventEmitter) Client.prototype.connect = function (params, cb) { this.pq.connect(params, cb) } Client.prototype.connectSync = function (params) { this.pq.connectSync(params) } Client.prototype.query = function (text, values, cb) { let queryFn if (typeof values === 'function') { cb = values } if (Array.isArray(values)) { queryFn = () => { return this.pq.sendQueryParams(text, values) } } else { queryFn = () => { return this.pq.sendQuery(text) } } this._dispatchQuery(this.pq, queryFn, (err) => { if (err) return cb(err) this._awaitResult(cb) }) } Client.prototype.prepare = function (statementName, text, nParams, cb) { const self = this const fn = function () { return self.pq.sendPrepare(statementName, text, nParams) } self._dispatchQuery(self.pq, fn, function (err) { if (err) return cb(err) self._awaitResult(cb) }) } Client.prototype.execute = function (statementName, parameters, cb) { const self = this const fn = function () { return self.pq.sendQueryPrepared(statementName, parameters) } self._dispatchQuery(self.pq, fn, function (err, rows) { if (err) return cb(err) self._awaitResult(cb) }) } Client.prototype.getCopyStream = function () { this.pq.setNonBlocking(true) this._stopReading() return new CopyStream(this.pq) } // cancel a currently executing query Client.prototype.cancel = function (cb) { assert(cb, 'Callback is required') // result is either true or a string containing an error const result = this.pq.cancel() return setImmediate(function () { cb(result === true ? undefined : new Error(result)) }) } Client.prototype.querySync = function (text, values) { if (values) { this.pq.execParams(text, values) } else { this.pq.exec(text) } throwIfError(this.pq) const result = buildResult(this.pq, this._types, this.arrayMode) return result.rows } Client.prototype.prepareSync = function (statementName, text, nParams) { this.pq.prepare(statementName, text, nParams) throwIfError(this.pq) } Client.prototype.executeSync = function (statementName, parameters) { this.pq.execPrepared(statementName, parameters) throwIfError(this.pq) return buildResult(this.pq, this._types, this.arrayMode).rows } Client.prototype.escapeLiteral = function (value) { return this.pq.escapeLiteral(value) } Client.prototype.escapeIdentifier = function (value) { return this.pq.escapeIdentifier(value) } // export the version number so we can check it in node-postgres module.exports.version = require('./package.json').version Client.prototype.end = function (cb) { this._stopReading() this.pq.finish() if (cb) setImmediate(cb) } Client.prototype._readError = function (message) { const err = new Error(message || this.pq.errorMessage()) this.emit('error', err) } Client.prototype._stopReading = function () { if (!this._reading) return this._reading = false this.pq.stopReader() this.pq.removeListener('readable', this._read) } Client.prototype._consumeQueryResults = function (pq) { return buildResult(pq, this._types, this.arrayMode) } Client.prototype._emitResult = function (pq) { const status = pq.resultStatus() switch (status) { case 'PGRES_FATAL_ERROR': this._queryError = new Error(this.pq.resultErrorMessage()) break case 'PGRES_TUPLES_OK': case 'PGRES_COMMAND_OK': case 'PGRES_EMPTY_QUERY': { const result = this._consumeQueryResults(this.pq) this.emit('result', result) } break case 'PGRES_COPY_OUT': case 'PGRES_COPY_BOTH': { break } default: this._readError('unrecognized command status: ' + status) break } return status } // called when libpq is readable Client.prototype._read = function () { const pq = this.pq // read waiting data from the socket // e.g. clear the pending 'select' if (!pq.consumeInput()) { // if consumeInput returns false // than a read error has been encountered return this._readError() } // check if there is still outstanding data // if so, wait for it all to come in if (pq.isBusy()) { return } // load our result object while (pq.getResult()) { const resultStatus = this._emitResult(this.pq) // if the command initiated copy mode we need to break out of the read loop // so a substream can begin to read copy data if (resultStatus === 'PGRES_COPY_BOTH' || resultStatus === 'PGRES_COPY_OUT') { break } // if reading multiple results, sometimes the following results might cause // a blocking read. in this scenario yield back off the reader until libpq is readable if (pq.isBusy()) { return } } this.emit('readyForQuery') let notice = this.pq.notifies() while (notice) { this.emit('notification', notice) notice = this.pq.notifies() } } // ensures the client is reading and // everything is set up for async io Client.prototype._startReading = function () { if (this._reading) return this._reading = true this.pq.on('readable', this._read) this.pq.startReader() } const throwIfError = function (pq) { const err = pq.resultErrorMessage() || pq.errorMessage() if (err) { throw new Error(err) } } Client.prototype._awaitResult = function (cb) { this._queryCallback = cb return this._startReading() } // wait for the writable socket to drain Client.prototype._waitForDrain = function (pq, cb) { const res = pq.flush() // res of 0 is success if (res === 0) return cb() // res of -1 is failure if (res === -1) return cb(pq.errorMessage()) // otherwise outgoing message didn't flush to socket // wait for it to flush and try again const self = this // you cannot read & write on a socket at the same time return pq.writable(function () { self._waitForDrain(pq, cb) }) } // send an async query to libpq and wait for it to // finish writing query text to the socket Client.prototype._dispatchQuery = function (pq, fn, cb) { this._stopReading() const success = pq.setNonBlocking(true) if (!success) return cb(new Error('Unable to set non-blocking to true')) const sent = fn() if (!sent) return cb(new Error(pq.errorMessage() || 'Something went wrong dispatching the query')) this._waitForDrain(pq, cb) } Client.prototype._onResult = function (result) { if (this._resultCount === 0) { this._results = result this._rows = result.rows } else if (this._resultCount === 1) { this._results = [this._results, result] this._rows = [this._rows, result.rows] } else { this._results.push(result) this._rows.push(result.rows) } this._resultCount++ } Client.prototype._onReadyForQuery = function () { // remove instance callback const cb = this._queryCallback this._queryCallback = undefined // remove instance query error const err = this._queryError this._queryError = undefined // remove instance rows const rows = this._rows this._rows = undefined // remove instance results const results = this._results this._results = undefined this._resultCount = 0 if (cb) { cb(err, rows || [], results) } } package/package.json000644 0000002300 3560116604 011541 0ustar00000000 000000 { "name": "pg-native", "version": "3.5.2", "description": "A slightly nicer interface to Postgres over node-libpq", "main": "index.js", "exports": { ".": { "import": "./esm/index.mjs", "require": "./index.js", "default": "./index.js" }, "./lib/*": { "import": "./lib/*", "require": "./lib/*", "default": "./lib/*" } }, "scripts": { "test": "mocha" }, "repository": { "type": "git", "url": "https://github.com/brianc/node-postgres.git" }, "keywords": [ "postgres", "pg", "libpq" ], "author": "Brian M. Carlson", "license": "MIT", "bugs": { "url": "https://github.com/brianc/node-postgres/issues" }, "homepage": "https://github.com/brianc/node-postgres/tree/master/packages/pg-native", "dependencies": { "libpq": "^1.8.15", "pg-types": "2.2.0" }, "devDependencies": { "async": "^0.9.0", "concat-stream": "^1.4.6", "generic-pool": "^2.1.1", "lodash": "^4.17.21", "mocha": "10.5.2", "node-gyp": ">=10.x", "okay": "^0.3.0", "semver": "^7.7.2" }, "files": [ "index.js", "lib", "esm" ], "gitHead": "8f8e7315e8f7c1bb01e98fdb41c8c92585510782" } package/README.md000644 0000025063 3560116604 010545 0ustar00000000 000000 # node-pg-native [![Build Status](https://travis-ci.org/brianc/node-pg-native.svg?branch=master)](https://travis-ci.org/brianc/node-pg-native) High performance native bindings between node.js and PostgreSQL via [libpq](https://github.com/brianc/node-libpq) with a simple API. ## install You need PostgreSQL client libraries & tools installed. An easy way to check is to type `pg_config`. If `pg_config` is in your path, you should be good to go. If it's not in your path you'll need to consult operating specific instructions on how to go about getting it there. Some ways I've done it in the past: - On macOS: `brew install libpq` - On Ubuntu/Debian: `apt-get install libpq-dev g++ make` - On RHEL/CentOS: `yum install postgresql-devel` - On Windows: 1. Install Visual Studio C++ (successfully built with Express 2010). Express is free. 2. Install PostgreSQL (`http://www.postgresql.org/download/windows/`) 3. Add your Postgre Installation's `bin` folder to the system path (i.e. `C:\Program Files\PostgreSQL\9.3\bin`). 4. Make sure that both `libpq.dll` and `pg_config.exe` are in that folder. Afterwards `pg_config` should be in your path. Then... ```sh $ npm i pg-native ``` ## use ### async ```js const Client = require('pg-native') const client = new Client(); client.connect(function(err) { if(err) throw err // text queries client.query('SELECT NOW() AS the_date', function(err, rows) { if(err) throw err console.log(rows[0].the_date) // Tue Sep 16 2014 23:42:39 GMT-0400 (EDT) // parameterized statements client.query('SELECT $1::text as twitter_handle', ['@briancarlson'], function(err, rows) { if(err) throw err console.log(rows[0].twitter_handle) //@briancarlson }) // prepared statements client.prepare('get_twitter', 'SELECT $1::text as twitter_handle', 1, function(err) { if(err) throw err // execute the prepared, named statement client.execute('get_twitter', ['@briancarlson'], function(err, rows) { if(err) throw err console.log(rows[0].twitter_handle) //@briancarlson // execute the prepared, named statement again client.execute('get_twitter', ['@realcarrotfacts'], function(err, rows) { if(err) throw err console.log(rows[0].twitter_handle) // @realcarrotfacts client.end(function() { console.log('ended') }) }) }) }) }) }) ``` ### sync Because `pg-native` is bound to [libpq](https://github.com/brianc/node-libpq) it is able to provide _sync_ operations for both connecting and queries. This is a bad idea in _non-blocking systems_ like web servers, but is exteremly convienent in scripts and bootstrapping applications - much the same way `fs.readFileSync` comes in handy. ```js const Client = require('pg-native') const client = new Client() client.connectSync() // text queries const rows = client.querySync('SELECT NOW() AS the_date') console.log(rows[0].the_date) // Tue Sep 16 2014 23:42:39 GMT-0400 (EDT) // parameterized queries const rows = client.querySync('SELECT $1::text as twitter_handle', ['@briancarlson']) console.log(rows[0].twitter_handle) // @briancarlson // prepared statements client.prepareSync('get_twitter', 'SELECT $1::text as twitter_handle', 1) const rows = client.executeSync('get_twitter', ['@briancarlson']) console.log(rows[0].twitter_handle) // @briancarlson const rows = client.executeSync('get_twitter', ['@realcarrotfacts']) console.log(rows[0].twitter_handle) // @realcarrotfacts ``` ## api ### constructor - __`constructor Client()`__ Constructs and returns a new `Client` instance ### async functions - __`client.connect(, callback:function(err:Error))`__ Connect to a PostgreSQL backend server. __params__ is _optional_ and is in any format accepted by [libpq](http://www.postgresql.org/docs/9.3/static/libpq-connect.html#LIBPQ-CONNSTRING). The connection string is passed _as is_ to libpq, so any format supported by libpq will be supported here. Likewise, any format _unsupported_ by libpq will not work. If no parameters are supplied libpq will use [environment variables](http://www.postgresql.org/docs/9.3/static/libpq-envars.html) to connect. Returns an `Error` to the `callback` if the connection was unsuccessful. `callback` is _required_. ##### example ```js const client = new Client() client.connect(function(err) { if(err) throw err console.log('connected!') }) const client2 = new Client() client2.connect('postgresql://user:password@host:5432/database?param=value', function(err) { if(err) throw err console.log('connected with connection string!') }) ``` - __`client.query(queryText:string, , callback:Function(err:Error, rows:Object[]))`__ Execute a query with the text of `queryText` and _optional_ parameters specified in the `values` array. All values are passed to the PostgreSQL backend server and executed as a parameterized statement. The callback is _required_ and is called with an `Error` object in the event of a query error, otherwise it is passed an array of result objects. Each element in this array is a dictionary of results with keys for column names and their values as the values for those columns. ##### example ```js const client = new Client() client.connect(function(err) { if (err) throw err client.query('SELECT NOW()', function(err, rows) { if (err) throw err console.log(rows) // [{ "now": "Tue Sep 16 2014 23:42:39 GMT-0400 (EDT)" }] client.query('SELECT $1::text as name', ['Brian'], function(err, rows) { if (err) throw err console.log(rows) // [{ "name": "Brian" }] client.end() }) }) }) ``` - __`client.prepare(statementName:string, queryText:string, nParams:int, callback:Function(err:Error))`__ Prepares a _named statement_ for later execution. You _must_ supply the name of the statement via `statementName`, the command to prepare via `queryText` and the number of parameters in `queryText` via `nParams`. Calls the callback with an `Error` if there was an error. ##### example ```js const client = new Client() client.connect(function(err) { if(err) throw err client.prepare('prepared_statement', 'SELECT $1::text as name', 1, function(err) { if(err) throw err console.log('statement prepared') client.end() }) }) ``` - __`client.execute(statementName:string, , callback:Function(err:err, rows:Object[]))`__ Executes a previously prepared statement on this client with the name of `statementName`, passing it the optional array of query parameters as a `values` array. The `callback` is mandatory and is called with and `Error` if the execution failed, or with the same array of results as would be passed to the callback of a `client.query` result. ##### example ```js const client = new Client() client.connect(function(err) { if(err) throw err client.prepare('i_like_beans', 'SELECT $1::text as beans', 1, function(err) { if(err) throw err client.execute('i_like_beans', ['Brak'], function(err, rows) { if(err) throw err console.log(rows) // [{ "i_like_beans": "Brak" }] client.end() }) }) }) ``` - __`client.end(`__ Ends the connection. Calls the _optional_ callback when the connection is terminated. ##### example ```js const client = new Client() client.connect(function(err) { if(err) throw err client.end(function() { console.log('client ended') // client ended }) }) ``` - __`client.cancel(callback:function(err))`__ Cancels the active query on the client. Callback receives an error if there was an error _sending_ the cancel request. ##### example ```js const client = new Client() client.connectSync() // sleep for 100 seconds client.query('select pg_sleep(100)', function(err) { console.log(err) // [Error: ERROR: canceling statement due to user request] }) client.cancel(function(err) { console.log('cancel dispatched') }) ``` ### sync functions - __`client.connectSync(params:string)`__ Connect to a PostgreSQL backend server. Params is in any format accepted by [libpq](http://www.postgresql.org/docs/9.3/static/libpq-connect.html#LIBPQ-CONNSTRING). Throws an `Error` if the connection was unsuccessful. - __`client.querySync(queryText:string, ) -> results:Object[]`__ Executes a query with a text of `queryText` and optional parameters as `values`. Uses a parameterized query if `values` are supplied. Throws an `Error` if the query fails, otherwise returns an array of results. - __`client.prepareSync(statementName:string, queryText:string, nParams:int)`__ Prepares a name statement with name of `statementName` and a query text of `queryText`. You must specify the number of params in the query with the `nParams` argument. Throws an `Error` if the statement is un-preparable, otherwise returns an array of results. - __`client.executeSync(statementName:string, ) -> results:Object[]`__ Executes a previously prepared statement on this client with the name of `statementName`, passing it the optional array of query parameters as a `values` array. Throws an `Error` if the execution fails, otherwise returns an array of results. ## testing ```sh $ npm test ``` To run the tests you need a PostgreSQL backend reachable by typing `psql` with no connection parameters in your terminal. The tests use [environment variables](http://www.postgresql.org/docs/9.3/static/libpq-envars.html) to connect to the backend. An example of supplying a specific host the tests: ```sh $ PGHOST=blabla.mydatabasehost.com npm test ``` ## license The MIT License (MIT) Copyright (c) 2014 Brian M. Carlson 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. package/esm/index.mjs000644 0000000203 3560116604 011661 0ustar00000000 000000 // ESM wrapper for pg-native import Client from '../index.js' // Export as default only to match CJS module export default Client