mirror of
https://github.com/brianc/node-postgres.git
synced 2025-12-08 20:16:25 +00:00
Add pg-native to monorepo
This commit is contained in:
parent
e25428c8dc
commit
23e33fdea5
@ -17,7 +17,7 @@
|
||||
"docs:start": "cd docs && yarn start",
|
||||
"pretest": "yarn build",
|
||||
"prepublish": "yarn build",
|
||||
"lint": "eslint --cache '*/**/*.{js,ts,tsx}'"
|
||||
"lint": "eslint --cache 'packages/**/*.{js,ts,tsx}'"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^7.0.0",
|
||||
|
||||
14
packages/pg-native/Makefile
Normal file
14
packages/pg-native/Makefile
Normal file
@ -0,0 +1,14 @@
|
||||
.PHONY: publish-patch test
|
||||
|
||||
test:
|
||||
npm test
|
||||
|
||||
patch: test
|
||||
npm version patch -m "Bump version"
|
||||
git push origin master --tags
|
||||
npm publish
|
||||
|
||||
minor: test
|
||||
npm version minor -m "Bump version"
|
||||
git push origin master --tags
|
||||
npm publish
|
||||
306
packages/pg-native/README.md
Normal file
306
packages/pg-native/README.md
Normal file
@ -0,0 +1,306 @@
|
||||
# node-pg-native
|
||||
|
||||
[](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
|
||||
var Client = require('pg-native')
|
||||
|
||||
var 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
|
||||
var Client = require('pg-native')
|
||||
|
||||
var client = new Client()
|
||||
client.connectSync()
|
||||
|
||||
//text queries
|
||||
var 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
|
||||
var 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)
|
||||
|
||||
var rows = client.executeSync('get_twitter', ['@briancarlson'])
|
||||
console.log(rows[0].twitter_handle) //@briancarlson
|
||||
|
||||
var 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(<params:string>, 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
|
||||
var client = new Client()
|
||||
client.connect(function(err) {
|
||||
if(err) throw err
|
||||
|
||||
console.log('connected!')
|
||||
})
|
||||
|
||||
var 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, <values: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
|
||||
var 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
|
||||
var 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, <values: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
|
||||
var 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(<callback:Function()>`__
|
||||
|
||||
Ends the connection. Calls the _optional_ callback when the connection is terminated.
|
||||
|
||||
##### example
|
||||
|
||||
```js
|
||||
var 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
|
||||
var 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, <values: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, <values:string[]>) -> results:Object[]`__
|
||||
|
||||
Executes a previously prepared statement on this client with the name of `statementName`, passing it the optional array of query paramters as a `values` array. Throws an `Error` if the execution fails, otherwas 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.
|
||||
52
packages/pg-native/bench/index.js
Normal file
52
packages/pg-native/bench/index.js
Normal file
@ -0,0 +1,52 @@
|
||||
var pg = require("pg").native;
|
||||
var Native = require("../");
|
||||
|
||||
var warmup = function (fn, cb) {
|
||||
var count = 0;
|
||||
var max = 10;
|
||||
var run = function (err) {
|
||||
if (err) return cb(err);
|
||||
|
||||
if (max >= count++) {
|
||||
return fn(run);
|
||||
}
|
||||
|
||||
cb();
|
||||
};
|
||||
run();
|
||||
};
|
||||
|
||||
var native = Native();
|
||||
native.connectSync();
|
||||
|
||||
var queryText = "SELECT generate_series(0, 1000)";
|
||||
var client = new pg.Client();
|
||||
client.connect(function () {
|
||||
var pure = function (cb) {
|
||||
client.query(queryText, function (err) {
|
||||
if (err) throw err;
|
||||
cb(err);
|
||||
});
|
||||
};
|
||||
var nativeQuery = function (cb) {
|
||||
native.query(queryText, function (err) {
|
||||
if (err) throw err;
|
||||
cb(err);
|
||||
});
|
||||
};
|
||||
|
||||
var run = function () {
|
||||
var start = Date.now();
|
||||
warmup(pure, function () {
|
||||
console.log("pure done", Date.now() - start);
|
||||
start = Date.now();
|
||||
warmup(nativeQuery, function () {
|
||||
console.log("native done", Date.now() - start);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
setInterval(function () {
|
||||
run();
|
||||
}, 500);
|
||||
});
|
||||
50
packages/pg-native/bench/leaks.js
Normal file
50
packages/pg-native/bench/leaks.js
Normal file
@ -0,0 +1,50 @@
|
||||
var Client = require("../");
|
||||
var async = require("async");
|
||||
|
||||
var loop = function () {
|
||||
var client = new Client();
|
||||
|
||||
var connect = function (cb) {
|
||||
client.connect(cb);
|
||||
};
|
||||
|
||||
var simpleQuery = function (cb) {
|
||||
client.query("SELECT NOW()", cb);
|
||||
};
|
||||
|
||||
var paramsQuery = function (cb) {
|
||||
client.query("SELECT $1::text as name", ["Brian"], cb);
|
||||
};
|
||||
|
||||
var prepared = function (cb) {
|
||||
client.prepare("test", "SELECT $1::text as name", 1, function (err) {
|
||||
if (err) return cb(err);
|
||||
client.execute("test", ["Brian"], cb);
|
||||
});
|
||||
};
|
||||
|
||||
var sync = function (cb) {
|
||||
client.querySync("SELECT NOW()");
|
||||
client.querySync("SELECT $1::text as name", ["Brian"]);
|
||||
client.prepareSync("boom", "SELECT $1::text as name", 1);
|
||||
client.executeSync("boom", ["Brian"]);
|
||||
setImmediate(cb);
|
||||
};
|
||||
|
||||
var end = function (cb) {
|
||||
client.end(cb);
|
||||
};
|
||||
|
||||
var ops = [connect, simpleQuery, paramsQuery, prepared, sync, end];
|
||||
|
||||
var start = Date.now();
|
||||
async.series(ops, function (err) {
|
||||
if (err) throw err;
|
||||
console.log(Date.now() - start);
|
||||
setImmediate(loop);
|
||||
});
|
||||
};
|
||||
|
||||
// on my machine this will consume memory up to about 50 megs of ram
|
||||
// and then stabalize at that point
|
||||
loop();
|
||||
332
packages/pg-native/index.js
Normal file
332
packages/pg-native/index.js
Normal file
@ -0,0 +1,332 @@
|
||||
var Libpq = require("libpq");
|
||||
var EventEmitter = require("events").EventEmitter;
|
||||
var util = require("util");
|
||||
var assert = require("assert");
|
||||
var types = require("pg-types");
|
||||
var buildResult = require("./lib/build-result");
|
||||
var CopyStream = require("./lib/copy-stream");
|
||||
|
||||
var 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 converstion 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) {
|
||||
var queryFn;
|
||||
|
||||
if (typeof values === "function") {
|
||||
cb = values;
|
||||
}
|
||||
|
||||
if (Array.isArray(values) && values.length > 0) {
|
||||
queryFn = function () {
|
||||
return self.pq.sendQueryParams(text, values);
|
||||
};
|
||||
} else {
|
||||
queryFn = function () {
|
||||
return self.pq.sendQuery(text);
|
||||
};
|
||||
}
|
||||
|
||||
var self = this;
|
||||
|
||||
self._dispatchQuery(self.pq, queryFn, function (err) {
|
||||
if (err) return cb(err);
|
||||
|
||||
self._awaitResult(cb);
|
||||
});
|
||||
};
|
||||
|
||||
Client.prototype.prepare = function (statementName, text, nParams, cb) {
|
||||
var self = this;
|
||||
var 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) {
|
||||
var self = this;
|
||||
|
||||
var 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
|
||||
var 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) {
|
||||
var 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) {
|
||||
var 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 () {
|
||||
var 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");
|
||||
|
||||
var 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();
|
||||
};
|
||||
|
||||
var throwIfError = function (pq) {
|
||||
var 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) {
|
||||
var 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
|
||||
var 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();
|
||||
var success = pq.setNonBlocking(true);
|
||||
if (!success) return cb(new Error("Unable to set non-blocking to true"));
|
||||
var 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);
|
||||
}
|
||||
};
|
||||
76
packages/pg-native/lib/build-result.js
Normal file
76
packages/pg-native/lib/build-result.js
Normal file
@ -0,0 +1,76 @@
|
||||
"use strict";
|
||||
|
||||
class Result {
|
||||
constructor(types, arrayMode) {
|
||||
this._types = types;
|
||||
this._arrayMode = arrayMode;
|
||||
|
||||
this.command = undefined;
|
||||
this.rowCount = undefined;
|
||||
this.fields = [];
|
||||
this.rows = [];
|
||||
}
|
||||
|
||||
consumeCommand(pq) {
|
||||
this.command = pq.cmdStatus().split(" ")[0];
|
||||
this.rowCount = parseInt(pq.cmdTuples(), 10);
|
||||
}
|
||||
|
||||
consumeFields(pq) {
|
||||
const nfields = pq.nfields();
|
||||
for (var x = 0; x < nfields; x++) {
|
||||
this.fields.push({
|
||||
name: pq.fname(x),
|
||||
dataTypeID: pq.ftype(x),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
consumeRows(pq) {
|
||||
const tupleCount = pq.ntuples();
|
||||
for (var i = 0; i < tupleCount; i++) {
|
||||
const row = this._arrayMode ? this.consumeRowAsArray(pq, i) : this.consumeRowAsObject(pq, i);
|
||||
this.rows.push(row);
|
||||
}
|
||||
}
|
||||
|
||||
consumeRowAsObject(pq, rowIndex) {
|
||||
const row = {};
|
||||
for (var j = 0; j < this.fields.length; j++) {
|
||||
const value = this.readValue(pq, rowIndex, j);
|
||||
row[this.fields[j].name] = value;
|
||||
}
|
||||
return row;
|
||||
}
|
||||
|
||||
consumeRowAsArray(pq, rowIndex) {
|
||||
const row = [];
|
||||
for (var j = 0; j < this.fields.length; j++) {
|
||||
const value = this.readValue(pq, rowIndex, j);
|
||||
row.push(value);
|
||||
}
|
||||
return row;
|
||||
}
|
||||
|
||||
readValue(pq, rowIndex, colIndex) {
|
||||
var rawValue = pq.getvalue(rowIndex, colIndex);
|
||||
if (rawValue === "") {
|
||||
if (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;
|
||||
155
packages/pg-native/lib/copy-stream.js
Normal file
155
packages/pg-native/lib/copy-stream.js
Normal file
@ -0,0 +1,155 @@
|
||||
var Duplex = require("stream").Duplex;
|
||||
var Writable = require("stream").Writable;
|
||||
var util = require("util");
|
||||
|
||||
var 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) {
|
||||
var 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.
|
||||
var self = this;
|
||||
this.pq.writable(function () {
|
||||
self._write(chunk, encoding, cb);
|
||||
});
|
||||
};
|
||||
|
||||
CopyStream.prototype.end = function () {
|
||||
var args = Array.prototype.slice.call(arguments, 0);
|
||||
var self = this;
|
||||
|
||||
var callback = args.pop();
|
||||
|
||||
if (args.length) {
|
||||
this.write(args[0]);
|
||||
}
|
||||
var 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) {
|
||||
var 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) {
|
||||
var 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) {
|
||||
var 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');
|
||||
var 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);
|
||||
});
|
||||
};
|
||||
|
||||
var consumeResults = function (pq, cb) {
|
||||
var cleanup = function () {
|
||||
pq.removeListener("readable", onReadable);
|
||||
pq.stopReader();
|
||||
};
|
||||
|
||||
var readError = function (message) {
|
||||
cleanup();
|
||||
return cb(new Error(message || pq.errorMessage()));
|
||||
};
|
||||
|
||||
var 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();
|
||||
};
|
||||
49
packages/pg-native/package.json
Normal file
49
packages/pg-native/package.json
Normal file
@ -0,0 +1,49 @@
|
||||
{
|
||||
"name": "pg-native",
|
||||
"version": "3.0.1",
|
||||
"description": "A slightly nicer interface to Postgres over node-libpq",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "mocha"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/brianc/node-pg-native.git"
|
||||
},
|
||||
"keywords": [
|
||||
"postgres",
|
||||
"pg",
|
||||
"libpq"
|
||||
],
|
||||
"author": "Brian M. Carlson",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/brianc/node-pg-native/issues"
|
||||
},
|
||||
"homepage": "https://github.com/brianc/node-pg-native",
|
||||
"dependencies": {
|
||||
"libpq": "1.8.13",
|
||||
"pg-types": "^1.12.1",
|
||||
"readable-stream": "1.0.31"
|
||||
},
|
||||
"devDependencies": {
|
||||
"async": "^0.9.0",
|
||||
"concat-stream": "^1.4.6",
|
||||
"eslint": "4.2.0",
|
||||
"eslint-config-standard": "10.2.1",
|
||||
"eslint-plugin-import": "2.7.0",
|
||||
"eslint-plugin-node": "5.1.0",
|
||||
"eslint-plugin-promise": "3.5.0",
|
||||
"eslint-plugin-standard": "3.0.1",
|
||||
"generic-pool": "^2.1.1",
|
||||
"lodash": "^2.4.1",
|
||||
"mocha": "3.4.2",
|
||||
"node-gyp": "^10.1.0",
|
||||
"okay": "^0.3.0",
|
||||
"pg": "*",
|
||||
"semver": "^4.1.0"
|
||||
},
|
||||
"prettier": {
|
||||
"printWidth": 200
|
||||
}
|
||||
}
|
||||
25
packages/pg-native/test/array-mode.js
Normal file
25
packages/pg-native/test/array-mode.js
Normal file
@ -0,0 +1,25 @@
|
||||
var Client = require("../");
|
||||
var assert = require("assert");
|
||||
|
||||
describe("client with arrayMode", function () {
|
||||
it("returns result as array", function (done) {
|
||||
var client = new Client({ arrayMode: true });
|
||||
client.connectSync();
|
||||
client.querySync("CREATE TEMP TABLE blah(name TEXT)");
|
||||
client.querySync("INSERT INTO blah (name) VALUES ($1)", ["brian"]);
|
||||
client.querySync("INSERT INTO blah (name) VALUES ($1)", ["aaron"]);
|
||||
var rows = client.querySync("SELECT * FROM blah");
|
||||
assert.equal(rows.length, 2);
|
||||
var row = rows[0];
|
||||
assert.equal(row.length, 1);
|
||||
assert.equal(row[0], "brian");
|
||||
assert.equal(rows[1][0], "aaron");
|
||||
|
||||
client.query("SELECT 'brian', null", function (err, res) {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(res[0][0], "brian");
|
||||
assert.strictEqual(res[0][1], null);
|
||||
client.end(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
80
packages/pg-native/test/async-workflow.js
Normal file
80
packages/pg-native/test/async-workflow.js
Normal file
@ -0,0 +1,80 @@
|
||||
var Client = require("../");
|
||||
var ok = require("okay");
|
||||
var assert = require("assert");
|
||||
var concat = require("concat-stream");
|
||||
|
||||
describe("async workflow", function () {
|
||||
before(function (done) {
|
||||
this.client = new Client();
|
||||
this.client.connect(done);
|
||||
});
|
||||
|
||||
var echoParams = function (params, cb) {
|
||||
this.client.query(
|
||||
"SELECT $1::text as first, $2::text as second",
|
||||
params,
|
||||
ok(cb, function (rows) {
|
||||
checkParams(params, rows);
|
||||
cb(null, rows);
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
var checkParams = function (params, rows) {
|
||||
assert.equal(rows.length, 1);
|
||||
assert.equal(rows[0].first, params[0]);
|
||||
assert.equal(rows[0].second, params[1]);
|
||||
};
|
||||
|
||||
it("sends async query", function (done) {
|
||||
var params = ["one", "two"];
|
||||
echoParams.call(this, params, done);
|
||||
});
|
||||
|
||||
it("sends multiple async queries", function (done) {
|
||||
var self = this;
|
||||
var params = ["bang", "boom"];
|
||||
echoParams.call(
|
||||
this,
|
||||
params,
|
||||
ok(done, function (rows) {
|
||||
echoParams.call(self, params, done);
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("sends an async query, copies in, copies out, and sends another query", function (done) {
|
||||
var self = this;
|
||||
this.client.querySync("CREATE TEMP TABLE test(name text, age int)");
|
||||
this.client.query(
|
||||
"INSERT INTO test(name, age) VALUES('brian', 32)",
|
||||
ok(done, function () {
|
||||
self.client.querySync("COPY test FROM stdin");
|
||||
var input = self.client.getCopyStream();
|
||||
input.write(Buffer.from("Aaron\t30\n", "utf8"));
|
||||
input.end(function () {
|
||||
self.client.query(
|
||||
"SELECT COUNT(*) FROM test",
|
||||
ok(done, function (rows) {
|
||||
assert.equal(rows.length, 1);
|
||||
self.client.query(
|
||||
"COPY test TO stdout",
|
||||
ok(done, function () {
|
||||
var output = self.client.getCopyStream();
|
||||
|
||||
// pump the stream
|
||||
output.read();
|
||||
output.pipe(
|
||||
concat(function (res) {
|
||||
done();
|
||||
}),
|
||||
);
|
||||
}),
|
||||
);
|
||||
}),
|
||||
);
|
||||
});
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
32
packages/pg-native/test/cancel.js
Normal file
32
packages/pg-native/test/cancel.js
Normal file
@ -0,0 +1,32 @@
|
||||
var Client = require("../");
|
||||
var assert = require("assert");
|
||||
|
||||
describe("cancel query", function () {
|
||||
it("works", function (done) {
|
||||
var client = new Client();
|
||||
client.connectSync();
|
||||
client.query("SELECT pg_sleep(100);", function (err) {
|
||||
assert(err instanceof Error);
|
||||
client.end(done);
|
||||
});
|
||||
client.cancel(function (err) {
|
||||
assert.ifError(err);
|
||||
});
|
||||
});
|
||||
|
||||
it("does not raise error if no active query", function (done) {
|
||||
var client = new Client();
|
||||
client.connectSync();
|
||||
client.cancel(function (err) {
|
||||
assert.ifError(err);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("raises error if client is not connected", function (done) {
|
||||
new Client().cancel(function (err) {
|
||||
assert(err, "should raise an error when not connected");
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
18
packages/pg-native/test/connection-errors.js
Normal file
18
packages/pg-native/test/connection-errors.js
Normal file
@ -0,0 +1,18 @@
|
||||
"use strict";
|
||||
|
||||
var Client = require("../");
|
||||
var assert = require("assert");
|
||||
|
||||
describe("connection errors", function () {
|
||||
it("raise error events", function (done) {
|
||||
var client = new Client();
|
||||
client.connectSync();
|
||||
client.query("SELECT pg_terminate_backend(pg_backend_pid())", assert.fail);
|
||||
client.on("error", function (err) {
|
||||
assert(err);
|
||||
assert.strictEqual(client.pq.resultErrorFields().sqlState, "57P01");
|
||||
client.end();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
23
packages/pg-native/test/connection.js
Normal file
23
packages/pg-native/test/connection.js
Normal file
@ -0,0 +1,23 @@
|
||||
var Client = require("../");
|
||||
var assert = require("assert");
|
||||
|
||||
describe("connection error", function () {
|
||||
it("doesnt segfault", function (done) {
|
||||
var client = new Client();
|
||||
client.connect("asldgsdgasgdasdg", function (err) {
|
||||
assert(err);
|
||||
// calling error on a closed client was segfaulting
|
||||
client.end();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("reading while not connected", function () {
|
||||
it("does not seg fault but does throw execption", function () {
|
||||
var client = new Client();
|
||||
assert.throws(function () {
|
||||
client.on("notification", function (msg) {});
|
||||
});
|
||||
});
|
||||
});
|
||||
47
packages/pg-native/test/copy-from.js
Normal file
47
packages/pg-native/test/copy-from.js
Normal file
@ -0,0 +1,47 @@
|
||||
var assert = require("assert");
|
||||
var Client = require("../");
|
||||
|
||||
describe("COPY FROM", function () {
|
||||
before(function (done) {
|
||||
this.client = Client();
|
||||
this.client.connect(done);
|
||||
});
|
||||
|
||||
after(function (done) {
|
||||
this.client.end(done);
|
||||
});
|
||||
|
||||
it("works", function (done) {
|
||||
var client = this.client;
|
||||
this.client.querySync("CREATE TEMP TABLE blah(name text, age int)");
|
||||
this.client.querySync("COPY blah FROM stdin");
|
||||
var stream = this.client.getCopyStream();
|
||||
stream.write(Buffer.from("Brian\t32\n", "utf8"));
|
||||
stream.write(Buffer.from("Aaron\t30\n", "utf8"));
|
||||
stream.write(Buffer.from("Shelley\t28\n", "utf8"));
|
||||
stream.end();
|
||||
|
||||
stream.once("finish", function () {
|
||||
var rows = client.querySync("SELECT COUNT(*) FROM blah");
|
||||
assert.equal(rows.length, 1);
|
||||
assert.equal(rows[0].count, 3);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("works with a callback passed to end", function (done) {
|
||||
var client = this.client;
|
||||
this.client.querySync("CREATE TEMP TABLE boom(name text, age int)");
|
||||
this.client.querySync("COPY boom FROM stdin");
|
||||
var stream = this.client.getCopyStream();
|
||||
stream.write(Buffer.from("Brian\t32\n", "utf8"));
|
||||
stream.write(Buffer.from("Aaron\t30\n", "utf8"), function () {
|
||||
stream.end(Buffer.from("Shelley\t28\n", "utf8"), function () {
|
||||
var rows = client.querySync("SELECT COUNT(*) FROM boom");
|
||||
assert.equal(rows.length, 1);
|
||||
assert.equal(rows[0].count, 3);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
35
packages/pg-native/test/copy-to.js
Normal file
35
packages/pg-native/test/copy-to.js
Normal file
@ -0,0 +1,35 @@
|
||||
var assert = require("assert");
|
||||
var Client = require("../");
|
||||
var concat = require("concat-stream");
|
||||
var _ = require("lodash");
|
||||
|
||||
describe("COPY TO", function () {
|
||||
before(function (done) {
|
||||
this.client = Client();
|
||||
this.client.connect(done);
|
||||
});
|
||||
|
||||
after(function (done) {
|
||||
this.client.end(done);
|
||||
});
|
||||
|
||||
it("works - basic check", function (done) {
|
||||
var limit = 1000;
|
||||
var qText = "COPY (SELECT * FROM generate_series(0, " + (limit - 1) + ")) TO stdout";
|
||||
var self = this;
|
||||
this.client.query(qText, function (err) {
|
||||
if (err) return done(err);
|
||||
var stream = self.client.getCopyStream();
|
||||
// pump the stream for node v0.11.x
|
||||
stream.read();
|
||||
stream.pipe(
|
||||
concat(function (buff) {
|
||||
var res = buff.toString("utf8");
|
||||
var expected = _.range(0, limit).join("\n") + "\n";
|
||||
assert.equal(res, expected);
|
||||
done();
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
27
packages/pg-native/test/custom-types.js
Normal file
27
packages/pg-native/test/custom-types.js
Normal file
@ -0,0 +1,27 @@
|
||||
var Client = require("../");
|
||||
var ok = require("okay");
|
||||
var assert = require("assert");
|
||||
|
||||
describe("Custom type parser", function () {
|
||||
it("is used by client", function (done) {
|
||||
var client = new Client({
|
||||
types: {
|
||||
getTypeParser: function () {
|
||||
return function () {
|
||||
return "blah";
|
||||
};
|
||||
},
|
||||
},
|
||||
});
|
||||
client.connectSync();
|
||||
var rows = client.querySync("SELECT NOW() AS when");
|
||||
assert.equal(rows[0].when, "blah");
|
||||
client.query(
|
||||
"SELECT NOW() as when",
|
||||
ok(function (rows) {
|
||||
assert.equal(rows[0].when, "blah");
|
||||
client.end(done);
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
32
packages/pg-native/test/domains.js
Normal file
32
packages/pg-native/test/domains.js
Normal file
@ -0,0 +1,32 @@
|
||||
var Client = require("../");
|
||||
var assert = require("assert");
|
||||
|
||||
var checkDomain = function (domain, when) {
|
||||
assert(process.domain, "Domain was lost after " + when);
|
||||
assert.strictEqual(process.domain, domain, "Domain switched after " + when);
|
||||
};
|
||||
|
||||
describe("domains", function (done) {
|
||||
it("remains bound after a query", function (done) {
|
||||
var domain = require('domain').create() // eslint-disable-line
|
||||
domain.run(function () {
|
||||
var client = new Client();
|
||||
client.connect(function () {
|
||||
checkDomain(domain, "connection");
|
||||
client.query("SELECT NOW()", function () {
|
||||
checkDomain(domain, "query");
|
||||
client.prepare("testing", "SELECT NOW()", 0, function () {
|
||||
checkDomain(domain, "prepare");
|
||||
client.execute("testing", [], function () {
|
||||
checkDomain(domain, "execute");
|
||||
client.end(function () {
|
||||
checkDomain(domain, "end");
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
16
packages/pg-native/test/empty-query.js
Normal file
16
packages/pg-native/test/empty-query.js
Normal file
@ -0,0 +1,16 @@
|
||||
var Client = require("../");
|
||||
var assert = require("assert");
|
||||
|
||||
describe("empty query", () => {
|
||||
it("has field metadata in result", (done) => {
|
||||
const client = new Client();
|
||||
client.connectSync();
|
||||
client.query("SELECT NOW() as now LIMIT 0", (err, rows, res) => {
|
||||
assert(!err);
|
||||
assert.equal(rows.length, 0);
|
||||
assert(Array.isArray(res.fields));
|
||||
assert.equal(res.fields.length, 1);
|
||||
client.end(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
27
packages/pg-native/test/huge-query.js
Normal file
27
packages/pg-native/test/huge-query.js
Normal file
@ -0,0 +1,27 @@
|
||||
var Client = require("../");
|
||||
var assert = require("assert");
|
||||
|
||||
describe("huge async query", function () {
|
||||
before(function (done) {
|
||||
this.client = Client();
|
||||
this.client.connect(done);
|
||||
});
|
||||
|
||||
after(function (done) {
|
||||
this.client.end(done);
|
||||
});
|
||||
|
||||
it("works", function (done) {
|
||||
var params = [""];
|
||||
var len = 100000;
|
||||
for (var i = 0; i < len; i++) {
|
||||
params[0] += "A";
|
||||
}
|
||||
var qText = "SELECT '" + params[0] + "'::text as my_text";
|
||||
this.client.query(qText, function (err, rows) {
|
||||
if (err) return done(err);
|
||||
assert.equal(rows[0].my_text.length, len);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
36
packages/pg-native/test/index.js
Normal file
36
packages/pg-native/test/index.js
Normal file
@ -0,0 +1,36 @@
|
||||
var Client = require("../");
|
||||
var assert = require("assert");
|
||||
|
||||
describe("connection", function () {
|
||||
it("works", function (done) {
|
||||
Client().connect(done);
|
||||
});
|
||||
|
||||
it("connects with args", function (done) {
|
||||
Client().connect("host=localhost", done);
|
||||
});
|
||||
|
||||
it("errors out with bad connection args", function (done) {
|
||||
Client().connect("host=asldkfjasdf", function (err) {
|
||||
assert(err, "should raise an error for bad host");
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("connectSync", function () {
|
||||
it("works without args", function () {
|
||||
Client().connectSync();
|
||||
});
|
||||
|
||||
it("works with args", function () {
|
||||
var args = "host=" + (process.env.PGHOST || "localhost");
|
||||
Client().connectSync(args);
|
||||
});
|
||||
|
||||
it("throws if bad host", function () {
|
||||
assert.throws(function () {
|
||||
Client().connectSync("host=laksdjfdsf");
|
||||
});
|
||||
});
|
||||
});
|
||||
30
packages/pg-native/test/load.js
Normal file
30
packages/pg-native/test/load.js
Normal file
@ -0,0 +1,30 @@
|
||||
var Client = require("../");
|
||||
var async = require("async");
|
||||
var ok = require("okay");
|
||||
|
||||
var execute = function (x, done) {
|
||||
var client = new Client();
|
||||
client.connectSync();
|
||||
var query = function (n, cb) {
|
||||
client.query("SELECT $1::int as num", [n], function (err) {
|
||||
cb(err);
|
||||
});
|
||||
};
|
||||
return async.timesSeries(
|
||||
5,
|
||||
query,
|
||||
ok(done, function () {
|
||||
client.end();
|
||||
done();
|
||||
}),
|
||||
);
|
||||
};
|
||||
describe("Load tests", function () {
|
||||
it("single client and many queries", function (done) {
|
||||
async.times(1, execute, done);
|
||||
});
|
||||
|
||||
it("multiple client and many queries", function (done) {
|
||||
async.times(20, execute, done);
|
||||
});
|
||||
});
|
||||
57
packages/pg-native/test/many-connections.js
Normal file
57
packages/pg-native/test/many-connections.js
Normal file
@ -0,0 +1,57 @@
|
||||
var Client = require("../");
|
||||
var async = require("async");
|
||||
var ok = require("okay");
|
||||
var bytes = require("crypto").pseudoRandomBytes;
|
||||
|
||||
describe("many connections", function () {
|
||||
describe("async", function () {
|
||||
var test = function (count, times) {
|
||||
it("connecting " + count + " clients " + times, function (done) {
|
||||
this.timeout(200000);
|
||||
|
||||
var connectClient = function (n, cb) {
|
||||
var client = new Client();
|
||||
client.connect(
|
||||
ok(cb, function () {
|
||||
bytes(
|
||||
1000,
|
||||
ok(cb, function (chunk) {
|
||||
client.query(
|
||||
"SELECT $1::text as txt",
|
||||
[chunk.toString("base64")],
|
||||
ok(cb, function (rows) {
|
||||
client.end(cb);
|
||||
}),
|
||||
);
|
||||
}),
|
||||
);
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
var run = function (n, cb) {
|
||||
async.times(count, connectClient, cb);
|
||||
};
|
||||
|
||||
async.timesSeries(times, run, done);
|
||||
});
|
||||
};
|
||||
|
||||
test(1, 1);
|
||||
test(1, 1);
|
||||
test(1, 1);
|
||||
test(5, 5);
|
||||
test(5, 5);
|
||||
test(5, 5);
|
||||
test(5, 5);
|
||||
test(10, 10);
|
||||
test(10, 10);
|
||||
test(10, 10);
|
||||
test(20, 20);
|
||||
test(20, 20);
|
||||
test(20, 20);
|
||||
test(30, 10);
|
||||
test(30, 10);
|
||||
test(30, 10);
|
||||
});
|
||||
});
|
||||
26
packages/pg-native/test/many-errors.js
Normal file
26
packages/pg-native/test/many-errors.js
Normal file
@ -0,0 +1,26 @@
|
||||
var Client = require("../");
|
||||
var async = require("async");
|
||||
var assert = require("assert");
|
||||
|
||||
describe("many errors", function () {
|
||||
it("functions properly without segfault", function (done) {
|
||||
var throwError = function (n, cb) {
|
||||
var client = new Client();
|
||||
client.connectSync();
|
||||
|
||||
var doIt = function (n, cb) {
|
||||
client.query("select asdfiasdf", function (err) {
|
||||
assert(err, "bad query should emit an error");
|
||||
cb(null);
|
||||
});
|
||||
};
|
||||
|
||||
async.timesSeries(10, doIt, function (err) {
|
||||
if (err) return cb(err);
|
||||
client.end(cb);
|
||||
});
|
||||
};
|
||||
|
||||
async.times(10, throwError, done);
|
||||
});
|
||||
});
|
||||
2
packages/pg-native/test/mocha.opts
Normal file
2
packages/pg-native/test/mocha.opts
Normal file
@ -0,0 +1,2 @@
|
||||
--bail
|
||||
--no-exit
|
||||
41
packages/pg-native/test/multiple-queries.js
Normal file
41
packages/pg-native/test/multiple-queries.js
Normal file
@ -0,0 +1,41 @@
|
||||
var Client = require("../");
|
||||
var assert = require("assert");
|
||||
|
||||
describe("multiple commands in a single query", function () {
|
||||
before(function (done) {
|
||||
this.client = new Client();
|
||||
this.client.connect(done);
|
||||
});
|
||||
|
||||
after(function (done) {
|
||||
this.client.end(done);
|
||||
});
|
||||
|
||||
it("all execute to completion", function (done) {
|
||||
this.client.query("SELECT '10'::int as num; SELECT 'brian'::text as name", function (err, rows) {
|
||||
assert.ifError(err);
|
||||
assert.equal(rows.length, 2, "should return two sets rows");
|
||||
assert.equal(rows[0][0].num, "10");
|
||||
assert.equal(rows[1][0].name, "brian");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("inserts and reads at once", function (done) {
|
||||
var txt = "CREATE TEMP TABLE boom(age int);";
|
||||
txt += "INSERT INTO boom(age) VALUES(10);";
|
||||
txt += "SELECT * FROM boom;";
|
||||
this.client.query(txt, function (err, rows, results) {
|
||||
assert.ifError(err);
|
||||
assert.equal(rows.length, 3);
|
||||
assert.equal(rows[0].length, 0);
|
||||
assert.equal(rows[1].length, 0);
|
||||
assert.equal(rows[2][0].age, 10);
|
||||
|
||||
assert.equal(results[0].command, "CREATE");
|
||||
assert.equal(results[1].command, "INSERT");
|
||||
assert.equal(results[2].command, "SELECT");
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
28
packages/pg-native/test/multiple-statement-results.js
Normal file
28
packages/pg-native/test/multiple-statement-results.js
Normal file
@ -0,0 +1,28 @@
|
||||
var Client = require("../");
|
||||
var assert = require("assert");
|
||||
|
||||
describe("multiple statements", () => {
|
||||
before(() => {
|
||||
this.client = new Client();
|
||||
this.client.connectSync();
|
||||
});
|
||||
|
||||
after(() => this.client.end());
|
||||
|
||||
it("works with multiple queries", (done) => {
|
||||
const text = `
|
||||
SELECT generate_series(1, 2) as foo;
|
||||
SELECT generate_series(10, 11) as bar;
|
||||
SELECT generate_series(20, 22) as baz;
|
||||
`;
|
||||
this.client.query(text, (err, results) => {
|
||||
if (err) return done(err);
|
||||
assert(Array.isArray(results));
|
||||
assert.equal(results.length, 3);
|
||||
assert(Array.isArray(results[0]));
|
||||
assert(Array.isArray(results[1]));
|
||||
assert(Array.isArray(results[2]));
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
64
packages/pg-native/test/notify.js
Normal file
64
packages/pg-native/test/notify.js
Normal file
@ -0,0 +1,64 @@
|
||||
var Client = require("../");
|
||||
var ok = require("okay");
|
||||
|
||||
var notify = function (channel, payload) {
|
||||
var client = new Client();
|
||||
client.connectSync();
|
||||
client.querySync("NOTIFY " + channel + ", '" + payload + "'");
|
||||
client.end();
|
||||
};
|
||||
|
||||
describe("simple LISTEN/NOTIFY", function () {
|
||||
before(function (done) {
|
||||
var client = (this.client = new Client());
|
||||
client.connect(done);
|
||||
});
|
||||
|
||||
it("works", function (done) {
|
||||
var client = this.client;
|
||||
client.querySync("LISTEN boom");
|
||||
client.on("notification", function (msg) {
|
||||
done();
|
||||
});
|
||||
notify("boom", "sup");
|
||||
});
|
||||
|
||||
after(function (done) {
|
||||
this.client.end(done);
|
||||
});
|
||||
});
|
||||
|
||||
if (!process.env.TRAVIS_CI) {
|
||||
describe("async LISTEN/NOTIFY", function () {
|
||||
before(function (done) {
|
||||
var client = (this.client = new Client());
|
||||
client.connect(done);
|
||||
});
|
||||
|
||||
it("works", function (done) {
|
||||
var client = this.client;
|
||||
var count = 0;
|
||||
var check = function () {
|
||||
count++;
|
||||
if (count >= 2) return done();
|
||||
};
|
||||
client.on("notification", check);
|
||||
client.query(
|
||||
"LISTEN test",
|
||||
ok(done, function () {
|
||||
notify("test", "bot");
|
||||
client.query(
|
||||
"SELECT pg_sleep(.05)",
|
||||
ok(done, function () {
|
||||
notify("test", "bot");
|
||||
}),
|
||||
);
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
after(function (done) {
|
||||
this.client.end(done);
|
||||
});
|
||||
});
|
||||
}
|
||||
64
packages/pg-native/test/prepare.js
Normal file
64
packages/pg-native/test/prepare.js
Normal file
@ -0,0 +1,64 @@
|
||||
var Client = require("../");
|
||||
var ok = require("okay");
|
||||
var async = require("async");
|
||||
|
||||
describe("async prepare", function () {
|
||||
var run = function (n, cb) {
|
||||
var client = new Client();
|
||||
client.connectSync();
|
||||
|
||||
var exec = function (x, done) {
|
||||
client.prepare("get_now" + x, "SELECT NOW()", 0, done);
|
||||
};
|
||||
|
||||
async.timesSeries(
|
||||
10,
|
||||
exec,
|
||||
ok(cb, function () {
|
||||
client.end(cb);
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
var t = function (n) {
|
||||
it("works for " + n + " clients", function (done) {
|
||||
async.times(n, run, function (err) {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
for (var i = 0; i < 10; i++) {
|
||||
t(i);
|
||||
}
|
||||
});
|
||||
|
||||
describe("async execute", function () {
|
||||
var run = function (n, cb) {
|
||||
var client = new Client();
|
||||
client.connectSync();
|
||||
client.prepareSync("get_now", "SELECT NOW()", 0);
|
||||
var exec = function (x, cb) {
|
||||
client.execute("get_now", [], cb);
|
||||
};
|
||||
async.timesSeries(
|
||||
10,
|
||||
exec,
|
||||
ok(cb, function () {
|
||||
client.end(cb);
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
var t = function (n) {
|
||||
it("works for " + n + " clients", function (done) {
|
||||
async.times(n, run, function (err) {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
for (var i = 0; i < 10; i++) {
|
||||
t(i);
|
||||
}
|
||||
});
|
||||
115
packages/pg-native/test/query-async.js
Normal file
115
packages/pg-native/test/query-async.js
Normal file
@ -0,0 +1,115 @@
|
||||
var Client = require("../");
|
||||
var assert = require("assert");
|
||||
var async = require("async");
|
||||
var ok = require("okay");
|
||||
|
||||
describe("async query", function () {
|
||||
before(function (done) {
|
||||
this.client = Client();
|
||||
this.client.connect(done);
|
||||
});
|
||||
|
||||
after(function (done) {
|
||||
this.client.end(done);
|
||||
});
|
||||
|
||||
it("can execute many prepared statements on a client", function (done) {
|
||||
async.timesSeries(
|
||||
20,
|
||||
(i, cb) => {
|
||||
this.client.query("SELECT $1::text as name", ["brianc"], cb);
|
||||
},
|
||||
done,
|
||||
);
|
||||
});
|
||||
|
||||
it("simple query works", function (done) {
|
||||
var runQuery = function (n, done) {
|
||||
this.client.query("SELECT NOW() AS the_time", function (err, rows) {
|
||||
if (err) return done(err);
|
||||
assert.equal(rows[0].the_time.getFullYear(), new Date().getFullYear());
|
||||
return done();
|
||||
});
|
||||
}.bind(this);
|
||||
async.timesSeries(3, runQuery, done);
|
||||
});
|
||||
|
||||
it("parameters work", function (done) {
|
||||
var runQuery = function (n, done) {
|
||||
this.client.query("SELECT $1::text AS name", ["Brian"], done);
|
||||
}.bind(this);
|
||||
async.timesSeries(3, runQuery, done);
|
||||
});
|
||||
|
||||
it("prepared, named statements work", function (done) {
|
||||
var client = this.client;
|
||||
client.prepare("test", "SELECT $1::text as name", 1, function (err) {
|
||||
if (err) return done(err);
|
||||
client.execute(
|
||||
"test",
|
||||
["Brian"],
|
||||
ok(done, function (rows) {
|
||||
assert.equal(rows.length, 1);
|
||||
assert.equal(rows[0].name, "Brian");
|
||||
client.execute(
|
||||
"test",
|
||||
["Aaron"],
|
||||
ok(done, function (rows) {
|
||||
assert.equal(rows.length, 1);
|
||||
assert.equal(rows[0].name, "Aaron");
|
||||
done();
|
||||
}),
|
||||
);
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it("returns error if prepare fails", function (done) {
|
||||
this.client.prepare("test", "SELECT AWWW YEAH", 0, function (err) {
|
||||
assert(err, "Should have returned an error");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("returns an error if execute fails", function (done) {
|
||||
this.client.execute("test", [], function (err) {
|
||||
assert(err, "Should have returned an error");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("returns an error if there was a query error", function (done) {
|
||||
var runErrorQuery = function (n, done) {
|
||||
this.client.query("SELECT ALKJSFDSLFKJ", function (err) {
|
||||
assert(err instanceof Error, "Should return an error instance");
|
||||
done();
|
||||
});
|
||||
}.bind(this);
|
||||
async.timesSeries(3, runErrorQuery, done);
|
||||
});
|
||||
|
||||
it("is still usable after an error", function (done) {
|
||||
const runErrorQuery = (_, cb) => {
|
||||
this.client.query("SELECT LKJSDJFLSDKFJ", (err) => {
|
||||
assert(err instanceof Error, "Should return an error instance");
|
||||
cb(null, err);
|
||||
});
|
||||
};
|
||||
async.timesSeries(3, runErrorQuery, (err, res) => {
|
||||
assert(!err);
|
||||
assert.equal(res.length, 3);
|
||||
this.client.query("SELECT NOW()", done);
|
||||
});
|
||||
});
|
||||
|
||||
it("supports empty query", function (done) {
|
||||
this.client.query("", function (err, rows) {
|
||||
assert.ifError(err);
|
||||
assert(Array.isArray(rows));
|
||||
console.log("rows", rows);
|
||||
assert(rows.length === 0);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
83
packages/pg-native/test/query-sync.js
Normal file
83
packages/pg-native/test/query-sync.js
Normal file
@ -0,0 +1,83 @@
|
||||
var Client = require("../");
|
||||
var assert = require("assert");
|
||||
|
||||
describe("query sync", function (done) {
|
||||
before(function () {
|
||||
this.client = Client();
|
||||
this.client.connectSync();
|
||||
});
|
||||
|
||||
after(function (done) {
|
||||
this.client.end(done);
|
||||
});
|
||||
|
||||
it("simple query works", function () {
|
||||
var rows = this.client.querySync("SELECT NOW() AS the_time");
|
||||
assert.equal(rows.length, 1);
|
||||
assert.equal(rows[0].the_time.getFullYear(), new Date().getFullYear());
|
||||
});
|
||||
|
||||
it("parameterized query works", function () {
|
||||
var rows = this.client.querySync("SELECT $1::text AS name", ["Brian"]);
|
||||
assert.equal(rows.length, 1);
|
||||
assert.equal(rows[0].name, "Brian");
|
||||
});
|
||||
|
||||
it("throws when second argument is not an array", function () {
|
||||
assert.throws(() => {
|
||||
this.client.querySync("SELECT $1::text AS name", "Brian");
|
||||
});
|
||||
assert.throws(() => {
|
||||
this.client.prepareSync("test-failure", "SELECT $1::text as name", 1);
|
||||
|
||||
this.client.executeSync("test-failure", "Brian");
|
||||
});
|
||||
});
|
||||
|
||||
it("prepared statement works", function () {
|
||||
this.client.prepareSync("test", "SELECT $1::text as name", 1);
|
||||
|
||||
var rows = this.client.executeSync("test", ["Brian"]);
|
||||
assert.equal(rows.length, 1);
|
||||
assert.equal(rows[0].name, "Brian");
|
||||
|
||||
var rows2 = this.client.executeSync("test", ["Aaron"]);
|
||||
assert.equal(rows2.length, 1);
|
||||
assert.equal(rows2[0].name, "Aaron");
|
||||
});
|
||||
|
||||
it("prepare throws exception on error", function () {
|
||||
assert.throws(
|
||||
function () {
|
||||
this.client.prepareSync("blah", "I LIKE TO PARTY!!!", 0);
|
||||
}.bind(this),
|
||||
);
|
||||
});
|
||||
|
||||
it("throws exception on executing improperly", function () {
|
||||
assert.throws(function () {
|
||||
// wrong number of parameters
|
||||
this.client.executeSync("test", []);
|
||||
});
|
||||
});
|
||||
|
||||
it("throws exception on error", function () {
|
||||
assert.throws(
|
||||
function () {
|
||||
this.client.querySync("SELECT ASLKJASLKJF");
|
||||
}.bind(this),
|
||||
);
|
||||
});
|
||||
|
||||
it("is still usable after an error", function () {
|
||||
var rows = this.client.querySync("SELECT NOW()");
|
||||
assert(rows, "should have returned rows");
|
||||
assert.equal(rows.length, 1);
|
||||
});
|
||||
|
||||
it("supports empty query", function () {
|
||||
var rows = this.client.querySync("");
|
||||
assert(rows, "should return rows");
|
||||
assert.equal(rows.length, 0, "should return no rows");
|
||||
});
|
||||
});
|
||||
11
packages/pg-native/test/version.js
Normal file
11
packages/pg-native/test/version.js
Normal file
@ -0,0 +1,11 @@
|
||||
var Client = require("../");
|
||||
var assert = require("assert");
|
||||
var semver = require("semver");
|
||||
|
||||
describe("version", function () {
|
||||
it("is exported", function () {
|
||||
assert(Client.version);
|
||||
assert.equal(require("../package.json").version, Client.version);
|
||||
assert(semver.gt(Client.version, "1.4.0"));
|
||||
});
|
||||
});
|
||||
@ -7,7 +7,7 @@ params := $(connectionString)
|
||||
node-command := xargs -n 1 -I file node file $(params)
|
||||
|
||||
.PHONY : test test-connection test-integration bench test-native \
|
||||
publish test-missing-native update-npm
|
||||
publish update-npm
|
||||
|
||||
all:
|
||||
npm install
|
||||
@ -17,7 +17,7 @@ help:
|
||||
|
||||
test: test-unit
|
||||
|
||||
test-all: test-missing-native test-unit test-integration test-native test-worker
|
||||
test-all: test-unit test-integration test-native test-worker
|
||||
|
||||
|
||||
update-npm:
|
||||
@ -33,19 +33,13 @@ test-connection:
|
||||
@echo "***Testing connection***"
|
||||
@node script/create-test-tables.js $(params)
|
||||
|
||||
test-missing-native:
|
||||
@echo "***Testing optional native install***"
|
||||
@node test/native/missing-native.js
|
||||
|
||||
test-native: test-connection
|
||||
@echo "***Testing native bindings***"
|
||||
ifeq ($(TEST_SKIP_NATIVE), true)
|
||||
@echo "***Skipping tests***"
|
||||
else
|
||||
@npm i --no-save pg-native
|
||||
@find test/native -name "*-tests.js" | $(node-command)
|
||||
@find test/integration -name "*-tests.js" | $(node-command) native
|
||||
@npm uninstall pg-native
|
||||
endif
|
||||
|
||||
test-integration: test-connection
|
||||
|
||||
@ -1,7 +0,0 @@
|
||||
'use strict'
|
||||
// this test assumes it has been run from the Makefile
|
||||
// and that node_modules/pg-native has been deleted
|
||||
|
||||
var assert = require('assert')
|
||||
|
||||
assert.equal(require('../../lib').native, null)
|
||||
Loading…
x
Reference in New Issue
Block a user