diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..ccc915a7
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,36 @@
+
+
+### Bug or feature request
+
+
+
+- [ ] Bug
+- [ ] Feature request
+
+### Description of feature (or steps to reproduce if bug)
+
+
+
+### Link to sample repo to reproduce issue (if bug)
+
+
+
+### Expected result
+
+
+
+### Actual result (if bug)
+
+
+
+### Additional information (Node.js version, LoopBack version, etc)
+
+
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 00000000..d2b240f5
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,24 @@
+### Description
+
+
+#### Related issues
+
+
+
+- None
+
+### Checklist
+
+
+
+- [ ] New tests added or existing tests modified to cover all changes
+- [ ] Code conforms with the [style
+ guide](http://loopback.io/doc/en/contrib/style-guide.html)
diff --git a/CHANGES.md b/CHANGES.md
index 0000c628..ba918808 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,3 +1,45 @@
+2018-01-12, Version 2.9.0
+=========================
+
+ * Use pg@7 (Robin Biondi)
+
+ * drop node 0.10 and 0.12 support (Diana Lau)
+
+ * fix 2.x CI (Diana Lau)
+
+
+2017-02-24, Version 2.8.0
+=========================
+
+ * Add test for bulk transactions (Zak Barbuto)
+
+ * Use pg callback over connection.release (#109) (Zak Barbuto)
+
+ * Use pool.pool.release over pool.release (#109) (Zak Barbuto)
+
+ * Add test env information to README (Zak Barbuto)
+
+ * update README for local postgres setup (Diana Lau)
+
+ * Update postgresql.js (tmclouisluk)
+
+ * Fix bug when using postgresql 8.x (tmclouisluk)
+
+ * Use unique param for affectedRows (Loay)
+
+ * Move info from docs into README (#199) (Rand McKinney)
+
+ * Update paid support URL (Siddhi Pai)
+
+ * Revert loopback 2.x (siddhipai)
+
+ * Revert dev-dependency on loopback to 2.x (Siddhi Pai)
+
+ * Set publish tag to "lts" (Siddhi Pai)
+
+ * Update README with correct doc links, etc (Amir Jafarian)
+
+
2016-10-14, Version 2.7.0
=========================
diff --git a/README.md b/README.md
index ee2a927e..33815beb 100644
--- a/README.md
+++ b/README.md
@@ -1,182 +1,394 @@
# loopback-connector-postgresql
-The official PostgreSQL connector for the LoopBack framework.
+[PostgreSQL](https://www.postgresql.org/), is a popular open-source object-relational database.
+The `loopback-connector-postgresql` module is the PostgreSQL connector for the LoopBack framework.
-Please see the [official documentation](http://loopback.io/doc/en/lb2/PostgreSQL-connector.html).
+
For more information, see the
documentation.
+
+NOTE: The PostgreSQL connector requires PostgreSQL 8.x or 9.x.
+
+## Installation
-## Connector settings
+In your application root directory, enter this command to install the connector:
-The connector can be configured using the following settings from the data source.
-* url: The URL to the database, such as 'postgres://test:mypassword@localhost:5432/dev'
-* host or hostname (default to 'localhost'): The host name or ip address of the PostgreSQL DB server
-* port (default to 5432): The port number of the PostgreSQL DB server
-* username or user: The user name to connect to the PostgreSQL DB
-* password: The password
-* database: The PostgreSQL database name
-* debug (default to false)
+```shell
+$ npm install loopback-connector-postgresql --save
+```
-**NOTE**: By default, the 'public' schema is used for all tables.
+This installs the module from npm and adds it as a dependency to the application's `package.json` file.
-The PostgreSQL connector uses [node-postgres](https://github.com/brianc/node-postgres) as the driver. See more
-information about configuration parameters, check [https://github.com/brianc/node-postgres/wiki/Client#constructors](https://github.com/brianc/node-postgres/wiki/Client#constructors).
+If you create a PostgreSQL data source using the data source generator as described below, you don't have to do this, since the generator will run `npm install` for you.
-## Discovering Models
+## Creating a data source
-PostgreSQL data sources allow you to discover model definition information from existing postgresql databases. See the following APIs:
+Use the [Data source generator](http://loopback.io/doc/en/lb3/Data-source-generator.html) to add a PostgreSQL data source to your application.
+The generator will prompt for the database server hostname, port, and other settings
+required to connect to a PostgreSQL database. It will also run the `npm install` command above for you.
- - [dataSource.discoverModelDefinitions([username], fn)](https://github.com/strongloop/loopback#datasourcediscovermodeldefinitionsusername-fn)
- - [dataSource.discoverSchema([owner], name, fn)](https://github.com/strongloop/loopback#datasourcediscoverschemaowner-name-fn)
+The entry in the application's `/server/datasources.json` will look like this:
+{% include code-caption.html content="/server/datasources.json" %}
+```javascript
+"mydb": {
+ "name": "mydb",
+ "connector": "postgresql"
-## Model definition for PostgreSQL
+ "host": "mydbhost",
+ "port": 5432,
+ "url": "postgres://admin:admin@myhost/db",
+ "database": "db1",
+ "password": "admin",
+ "user": "admin"
+}
+```
-The model definition consists of the following properties:
+Edit `datasources.json` to add other properties that enable you to connect the data source to a PostgreSQL database.
+
+### Properties
+
+
+
+
+ | Property |
+ Type |
+ Description |
+
+
+
+
+ | connector |
+ String |
+
+ Connector name, either "loopback-connector-postgresql" or "postgresql"
+ |
+
+
+ | database |
+ String |
+ Database name |
+
+
+ | debug |
+ Boolean |
+ If true, turn on verbose mode to debug database queries and lifecycle. |
+
+
+ | host |
+ String |
+ Database host name |
+
+
+ | password |
+ String |
+ Password to connect to database |
+
+
+ | port |
+ Number |
+ Database TCP port |
+
+
+ | url |
+ String |
+ Use instead of thehost,port,user,password,
+ anddatabaseproperties. For example:'postgres://test:mypassword@localhost:5432/dev'.
+ |
+
+
+ | username |
+ String |
+ Username to connect to database |
+
+
+
-* name: Name of the model, by default, it's the camel case of the table
-* options: Model level operations and mapping to PostgreSQL schema/table
-* properties: Property definitions, including mapping to PostgreSQL column
+**NOTE**: By default, the 'public' schema is used for all tables.
-```json
+The PostgreSQL connector uses [node-postgres](https://github.com/brianc/node-postgres) as the driver. For more
+information about configuration parameters, see [node-postgres documentation](https://github.com/brianc/node-postgres/wiki/Client#constructors).
+
+### Connecting to UNIX domain socket
+
+A common PostgreSQL configuration is to connect to the UNIX domain socket `/var/run/postgresql/.s.PGSQL.5432` instead of using the TCP/IP port. For example:
+
+```javascript
+{
+ "postgres": {
+ "host": "/var/run/postgresql/",
+ "port": "5432",
+ "database": "dbname",
+ "username": "dbuser",
+ "password": "dbpassword",
+ "name": "postgres",
+ "debug": true,
+ "connector": "postgresql"
+ }
+}
+```
- {"name": "Inventory", "options": {
- "idInjection": false,
+## Defining models
+
+The model definition consists of the following properties.
+
+
+
+
+ | Property |
+ Default |
+ Description |
+
+
+
+
+ | name |
+ Camel-case of the database table name |
+ Name of the model. |
+
+
+ | options |
+ N/A |
+ Model level operations and mapping to PostgreSQL schema/table |
+
+
+ | properties |
+ N/A |
+ Property definitions, including mapping to PostgreSQL column |
+
+
+
+
+For example:
+
+{% include code-caption.html content="/common/models/model.json" %}
+```javascript
+{
+ "name": "Inventory",
+ "options": {
+ "idInjection": false,
+ "postgresql": {
+ "schema": "strongloop",
+ "table": "inventory"
+ }
+ },
+ "properties": {
+ "id": {
+ "type": "String",
+ "required": false,
+ "length": 64,
+ "precision": null,
+ "scale": null,
"postgresql": {
- "schema": "strongloop",
- "table": "inventory"
+ "columnName": "id",
+ "dataType": "character varying",
+ "dataLength": 64,
+ "dataPrecision": null,
+ "dataScale": null,
+ "nullable": "NO"
}
- }, "properties": {
- "id": {
- "type": "String",
- "required": false,
- "length": 64,
- "precision": null,
- "scale": null,
- "postgresql": {
- "columnName": "id",
- "dataType": "character varying",
- "dataLength": 64,
- "dataPrecision": null,
- "dataScale": null,
- "nullable": "NO"
- }
- },
- "productId": {
- "type": "String",
- "required": false,
- "length": 20,
- "precision": null,
- "scale": null,
- "id": 1,
- "postgresql": {
- "columnName": "product_id",
- "dataType": "character varying",
- "dataLength": 20,
- "dataPrecision": null,
- "dataScale": null,
- "nullable": "YES"
- }
- },
- "locationId": {
- "type": "String",
- "required": false,
- "length": 20,
- "precision": null,
- "scale": null,
- "id": 1,
- "postgresql": {
- "columnName": "location_id",
- "dataType": "character varying",
- "dataLength": 20,
- "dataPrecision": null,
- "dataScale": null,
- "nullable": "YES"
- }
- },
- "available": {
- "type": "Number",
- "required": false,
- "length": null,
- "precision": 32,
- "scale": 0,
- "postgresql": {
- "columnName": "available",
- "dataType": "integer",
- "dataLength": null,
- "dataPrecision": 32,
- "dataScale": 0,
- "nullable": "YES"
- }
- },
- "total": {
- "type": "Number",
- "required": false,
- "length": null,
- "precision": 32,
- "scale": 0,
- "postgresql": {
- "columnName": "total",
- "dataType": "integer",
- "dataLength": null,
- "dataPrecision": 32,
- "dataScale": 0,
- "nullable": "YES"
- }
+ },
+ "productId": {
+ "type": "String",
+ "required": false,
+ "length": 20,
+ "precision": null,
+ "scale": null,
+ "id": 1,
+ "postgresql": {
+ "columnName": "product_id",
+ "dataType": "character varying",
+ "dataLength": 20,
+ "dataPrecision": null,
+ "dataScale": null,
+ "nullable": "YES"
}
- }}
-
+ },
+ "locationId": {
+ "type": "String",
+ "required": false,
+ "length": 20,
+ "precision": null,
+ "scale": null,
+ "id": 1,
+ "postgresql": {
+ "columnName": "location_id",
+ "dataType": "character varying",
+ "dataLength": 20,
+ "dataPrecision": null,
+ "dataScale": null,
+ "nullable": "YES"
+ }
+ },
+ "available": {
+ "type": "Number",
+ "required": false,
+ "length": null,
+ "precision": 32,
+ "scale": 0,
+ "postgresql": {
+ "columnName": "available",
+ "dataType": "integer",
+ "dataLength": null,
+ "dataPrecision": 32,
+ "dataScale": 0,
+ "nullable": "YES"
+ }
+ },
+ "total": {
+ "type": "Number",
+ "required": false,
+ "length": null,
+ "precision": 32,
+ "scale": 0,
+ "postgresql": {
+ "columnName": "total",
+ "dataType": "integer",
+ "dataLength": null,
+ "dataPrecision": 32,
+ "dataScale": 0,
+ "nullable": "YES"
+ }
+ }
+ }
+}
```
-## Type Mapping
+## Type mapping
+
+See [LoopBack types](http://loopback.io/doc/en/lb3/LoopBack-types.html) for details on LoopBack's data types.
+
+### LoopBack to PostgreSQL types
+
+
+
+
+ | LoopBack Type |
+ PostgreSQL Type |
+
+
+ String JSON Text Default |
+
+ VARCHAR2
+ Default length is 1024
+ |
+
+
+ | Number |
+ INTEGER |
+
+
+ | Date |
+ TIMESTAMP WITH TIME ZONE |
+
+
+ | Boolean |
+ BOOLEAN |
+
+
+
+
+### PostgreSQL types to LoopBack
+
+
+
+
+ | PostgreSQL Type |
+ LoopBack Type |
+
+
+ | BOOLEAN |
+ Boolean |
+
+
+
+ VARCHAR CHARACTER VARYING CHARACTER CHAR TEXT
+ |
+ String |
+
+
+ | BYTEA |
+ Node.js Buffer object |
+
+
+ SMALLINT INTEGER BIGINT DECIMAL NUMERIC REAL DOUBLE SERIAL BIGSERIAL |
+ Number |
+
+
+ DATE TIMESTAMP TIME |
+ Date |
+
+
+ | POINT |
+ GeoPoint |
+
+
+
+
+## Discovery and auto-migration
+
+### Model discovery
+
+The PostgreSQL connector supports _model discovery_ that enables you to create LoopBack models
+based on an existing database schema using the unified [database discovery API](http://apidocs.strongloop.com/loopback-datasource-juggler/#datasource-prototype-discoverandbuildmodels). For more information on discovery, see [Discovering models from relational databases](https://loopback.io/doc/en/lb3/Discovering-models-from-relational-databases.html).
+
+### Auto-migratiion
+
+The PostgreSQL connector also supports _auto-migration_ that enables you to create a database schema
+from LoopBack models using the [LoopBack automigrate method](http://apidocs.strongloop.com/loopback-datasource-juggler/#datasource-prototype-automigrate).
+
+For more information on auto-migration, see [Creating a database schema from models](https://loopback.io/doc/en/lb3/Creating-a-database-schema-from-models.html) for more information.
+
+LoopBack PostgreSQL connector creates the following schema objects for a given
+model: a table, for example, PRODUCT under the 'public' schema within the database.
- - Number
- - Boolean
- - String
- - Object
- - Date
- - Array
- - Buffer
+The auto-migrate method:
-### JSON to PostgreSQL Types
+* Defines a primary key for the properties whose `id` property is true (or a positive number).
+* Creates a column with 'SERIAL' type if the `generated` property of the `id` property is true.
-* String|JSON|Text|default: VARCHAR, default length is 1024
-* Number: INTEGER
-* Date: TIMESTAMP WITH TIME ZONE
-* Timestamp: TIMESTAMP WITH TIME ZONE
-* Boolean: BOOLEAN
+Destroying models may result in errors due to foreign key integrity. First delete any related models by calling delete on models with relationships.
-### PostgreSQL Types to JSON
+## Running tests
-* BOOLEAN: Boolean
-* VARCHAR|CHARACTER VARYING|CHARACTER|CHAR|TEXT: String
-* BYTEA: Binary;
-* SMALLINT|INTEGER|BIGINT|DECIMAL|NUMERIC|REAL|DOUBLE|SERIAL|BIGSERIAL: Number
-* DATE|TIMESTAMP|TIME: Date
-* POINT: GeoPoint
+The tests in this repository are mainly integration tests, meaning you will need to run them using our preconfigured test server.
-## Destroying Models
+1. Ask a core developer for instructions on how to set up test server
+ credentials on your machine
+2. `npm test`
-Destroying models may result in errors due to foreign key integrity. Make sure
-to delete any related models first before calling delete on model's with
-relationships.
+If you wish to run the tests using your own test database instance,
-## Auto Migrate / Auto Update
+__Set up the database__
-After making changes to your model properties you must call `Model.automigrate()`
-or `Model.autoupdate()`. Only call `Model.automigrate()` on new models
-as it will drop existing tables.
+1. Go to pgAdmin.
+By default, the local database is one of the servers under Server Groups > Servers.
+2. Under Login Roles, add a user called ```strongloop```.
-LoopBack PostgreSQL connector creates the following schema objects for a given
-model:
+__Change configuration for database connection__
+
+In ```test\init.js```, change the value of ```config``` to be pointing to the local database. For example,
+```
+ var config = {
+ host: 'localhost',
+ port: '5432',
+ database:'strongloop',
+ username: 'postgres',
+ password: 'postgres',
+ };
+```
-* A table, for example, PRODUCT under the 'public' schema within the database
+2. (`Linux Only`) `CI=true PGHOST=localhost PGPORT= PGDATABASE= PGUSER= PGPASSWORD= npm test`
+__Troubleshooting__
-## Running tests
+When running `npm test`, it runs the ```pretest.js``` which eventually runs ```schema.sql``` to set up the database and tables.
+If there is problem, you can run the ```schema.sql``` manually. To do this:
-The tests in this repository are mainly integration tests, meaning you will need
-to run them using our preconfigured test server.
+1. Go to SQL Shell (psql)
+2. Run:
+```
+\i <>
-1. Ask a core developer for instructions on how to set up test server
- credentials on your machine
-2. `npm test`
+For example on Windows,
+\i c:\somepath\test\schema.sql
+```
\ No newline at end of file
diff --git a/lib/postgresql.js b/lib/postgresql.js
index 329a4ef6..13d78f0a 100644
--- a/lib/postgresql.js
+++ b/lib/postgresql.js
@@ -153,7 +153,7 @@ PostgreSQL.prototype.executeSQL = function(sql, params, options, callback) {
switch (data.command) {
case 'DELETE':
case 'UPDATE':
- result = {count: data.rowCount};
+ result = {affectedRows: data.rowCount, count: data.rowCount};
break;
default:
result = data.rows;
@@ -387,10 +387,10 @@ PostgreSQL.prototype.buildExpression = function(columnName, operator,
operatorValue, propertyDefinition) {
switch (operator) {
case 'like':
- return new ParameterizedSQL(columnName + " LIKE ? ESCAPE '\\'",
+ return new ParameterizedSQL(columnName + " LIKE ? ESCAPE E'\\\\'",
[operatorValue]);
case 'nlike':
- return new ParameterizedSQL(columnName + " NOT LIKE ? ESCAPE '\\'",
+ return new ParameterizedSQL(columnName + " NOT LIKE ? ESCAPE E'\\\\'",
[operatorValue]);
case 'regexp':
if (operatorValue.global)
@@ -520,7 +520,7 @@ PostgreSQL.prototype.getPlaceholderForValue = function(key) {
};
PostgreSQL.prototype.getCountForAffectedRows = function(model, info) {
- return info && info.count;
+ return info && info.affectedRows;
};
require('./discovery')(PostgreSQL);
diff --git a/lib/transaction.js b/lib/transaction.js
index 953dee3b..58aa8f66 100644
--- a/lib/transaction.js
+++ b/lib/transaction.js
@@ -21,6 +21,7 @@ function mixinTransaction(PostgreSQL) {
debug('Begin a transaction with isolation level: %s', isolationLevel);
this.pg.connect(function(err, connection, done) {
if (err) return cb(err);
+ connection.autorelease = done;
connection.query('BEGIN TRANSACTION ISOLATION LEVEL ' + isolationLevel,
function(err) {
if (err) return cb(err);
@@ -63,16 +64,11 @@ function mixinTransaction(PostgreSQL) {
};
PostgreSQL.prototype.releaseConnection = function(connection, err) {
- if (typeof connection.release === 'function') {
- connection.release(err);
- connection.release = null;
+ if (typeof connection.autorelease === 'function') {
+ connection.autorelease(err);
+ connection.autorelease = null;
} else {
- var pool = this.pg;
- if (err) {
- pool.destroy(connection);
- } else {
- pool.release(connection);
- }
+ connection.release();
}
};
}
diff --git a/package.json b/package.json
index 1b00ba1a..6127c56b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,9 @@
{
"name": "loopback-connector-postgresql",
- "version": "2.7.0",
+ "version": "2.9.0",
+ "publishConfig": {
+ "tag": "lts"
+ },
"description": "Loopback PostgreSQL Connector",
"keywords": [
"StrongLoop",
@@ -9,6 +12,9 @@
"DataSource",
"Connector"
],
+ "engines": {
+ "node": ">=4"
+ },
"main": "index.js",
"scripts": {
"pretest": "node pretest.js",
@@ -21,13 +27,13 @@
"bluebird": "^3.4.6",
"debug": "^2.1.1",
"loopback-connector": "^2.1.0",
- "pg": "^6.0.0",
+ "pg": "^7.0.0",
"strong-globalize": "^2.6.2"
},
"devDependencies": {
"eslint": "^2.13.1",
"eslint-config-loopback": "^4.0.0",
- "loopback-datasource-juggler": "^3.0.0",
+ "loopback-datasource-juggler": "^2.53.0",
"mocha": "^2.1.0",
"rc": "^1.0.0",
"should": "^8.0.2",
diff --git a/test/init.js b/test/init.js
index e67b256b..9f4fe0ad 100644
--- a/test/init.js
+++ b/test/init.js
@@ -43,9 +43,14 @@ global.getDBConfig = function(useUrl) {
};
return settings;
};
+
+var db;
global.getDataSource = global.getSchema = function(useUrl) {
+ // Return cached data source if possible to avoid too many client error
+ // due to multiple instances of connection pools
+ if (!useUrl && db) return db;
var settings = getDBConfig(useUrl);
- var db = new DataSource(require('../'), settings);
+ db = new DataSource(require('../'), settings);
db.log = function(a) {
// console.log(a);
};
diff --git a/test/postgresql.dbdefaults.test.js b/test/postgresql.dbdefaults.test.js
index e3755563..633302cc 100644
--- a/test/postgresql.dbdefaults.test.js
+++ b/test/postgresql.dbdefaults.test.js
@@ -34,7 +34,7 @@ describe('database default field values', function() {
created: {
type: 'Date',
postgresql: {
- dbDefault: "'5'",
+ dbDefault: '\'5\'',
},
},
});
@@ -49,6 +49,9 @@ describe('database default field values', function() {
it('should report inconsistent default values used', function(done) {
db.automigrate('PostWithInvalidDbDefaultValue', function(err) {
should.exists(err);
+ // The InvalidDefaults test is polluting the default date
+ // types of the other tests!
+ delete db.connector._models.PostWithInvalidDbDefaultValue;
done();
});
});
diff --git a/test/postgresql.initialization.test.js b/test/postgresql.initialization.test.js
new file mode 100644
index 00000000..ab83eaa9
--- /dev/null
+++ b/test/postgresql.initialization.test.js
@@ -0,0 +1,70 @@
+'use strict';
+require('./init');
+var Promise = require('bluebird');
+var connector = require('..');
+var DataSource = require('loopback-datasource-juggler').DataSource;
+var should = require('should');
+
+// simple wrapper that uses JSON.parse(JSON.stringify()) as cheap clone
+function newConfig(withURL) {
+ return JSON.parse(JSON.stringify(getDBConfig(withURL)));
+}
+
+describe('initialization', function() {
+ it('honours user-defined pg-pool settings', function() {
+ var dataSource = new DataSource(connector, newConfig());
+ var pool = dataSource.connector.pg;
+ pool.options.max.should.not.equal(999);
+
+ var settings = newConfig();
+ settings.max = 999; // non-default value
+ var dataSource = new DataSource(connector, settings);
+ var pool = dataSource.connector.pg;
+ pool.options.max.should.equal(999);
+ });
+
+ it('honours user-defined url settings', function() {
+ var settings = newConfig();
+
+ var dataSource = new DataSource(connector, settings);
+ var clientConfig = dataSource.connector.clientConfig;
+ should.not.exist(clientConfig.connectionString);
+
+ settings = newConfig(true);
+ var dataSource = new DataSource(connector, settings);
+ var clientConfig = dataSource.connector.clientConfig;
+ clientConfig.connectionString.should.equal(settings.url);
+ });
+});
+
+describe('postgresql connector errors', function() {
+ it('Should complete these 4 queries without dying', function(done) {
+ var dataSource = getDataSource();
+ var db = dataSource.connector;
+ var pool = db.pg;
+ pool.options.max = 5;
+ var errors = 0;
+ var shouldGet = 0;
+ function runErrorQuery() {
+ shouldGet++;
+ return new Promise(function(resolve, reject) {
+ db.executeSQL("SELECT 'asd'+1 ", [], {}, function(err, res) {
+ if (err) {
+ errors++;
+ resolve(err);
+ } else {
+ reject(res); // this should always error
+ }
+ });
+ });
+ };
+ var ps = [];
+ for (var i = 0; i < 12; i++) {
+ ps.push(runErrorQuery());
+ }
+ Promise.all(ps).then(function() {
+ shouldGet.should.equal(errors);
+ done();
+ });
+ });
+});
diff --git a/test/postgresql.test.js b/test/postgresql.test.js
index f8893b7b..ecf05800 100644
--- a/test/postgresql.test.js
+++ b/test/postgresql.test.js
@@ -90,6 +90,21 @@ describe('postgresql connector', function() {
});
});
+ it('should preserve property `count` after query execution', function(done) {
+ Post.create(
+ {title: 'T10', content: 'C10'},
+ function(err, p) {
+ if (err) return done(err);
+ post = p;
+ var query = "UPDATE PostWithBoolean SET title ='T20' WHERE id=" + post.id;
+ db.connector.execute(query, function(err, results) {
+ results.should.have.property('count', 1);
+ results.should.have.property('affectedRows', 1);
+ done(err);
+ });
+ });
+ });
+
it('should support updating boolean types with false value', function(done) {
Post.update({id: post.id}, {approved: false}, function(err) {
should.not.exists(err);
diff --git a/test/postgresql.transaction.test.js b/test/postgresql.transaction.test.js
index ce4783aa..a9926498 100644
--- a/test/postgresql.transaction.test.js
+++ b/test/postgresql.transaction.test.js
@@ -58,6 +58,41 @@ describe('transactions', function() {
};
}
+ describe('bulk', function() {
+ it('should work with bulk transactions', function(done) {
+ var completed = 0;
+ var concurrent = 20;
+ for (var i = 0; i <= concurrent; i++) {
+ var post = {title: 'tb' + i, content: 'cb' + i};
+ var create = createPostInTx(post);
+ Transaction.begin(db.connector, Transaction.SERIALIZABLE,
+ function(err, tx) {
+ if (err) return done(err);
+ Post.create(post, {transaction: tx},
+ function(err, p) {
+ if (err) {
+ done(err);
+ } else {
+ tx.commit(function(err) {
+ if (err) {
+ done(err);
+ }
+ completed++;
+ checkResults();
+ });
+ }
+ });
+ });
+ }
+
+ function checkResults() {
+ if (completed === concurrent) {
+ done();
+ }
+ }
+ });
+ });
+
describe('commit', function() {
var post = {title: 't1', content: 'c1'};
before(createPostInTx(post));