pg-using-bluebird
Advanced tools
Comparing version 3.3.1 to 4.0.0
## 4.0.0 | ||
### Breaking changes | ||
- Changed: updated required node.js version to >=8.0 | ||
- Changed: queryAsync now returns the whole result object instead of just the rows; queryRowsAsync remains unchanged | ||
and should be used when only the result rows are wanted | ||
- Removed: createUpsertCTE, as the ON CONFLICT syntax has been present in PostgreSQL for several versions now and it's | ||
clearer than using a CTE, so there's little incentive to keep the old query builder around | ||
- Removed: custom query object keys as no need for these had come up | ||
### Improvements | ||
- Changed: updated README | ||
## 3.3.1 | ||
- Add withConnection and withTransaction convenience methods | ||
- Expose the pool object through the API | ||
## 3.2.1 | ||
@@ -3,0 +23,0 @@ |
203
index.js
"use strict"; // eslint-disable-line semi | ||
var pg = require('pg'), | ||
const pg = require('pg'), | ||
_ = require('lodash'), | ||
BPromise = require('bluebird'), | ||
stringTemplate = require('string-template'), | ||
using = BPromise.using, | ||
assert = require('assert') | ||
var POOL_DEFAULTS = { | ||
const POOL_DEFAULTS = { | ||
max: 20, // pool size | ||
@@ -15,10 +14,8 @@ ssl: false | ||
var QUERY_DEFAULTS = { | ||
statementTimeout: '0', // the node-postgres default is no timeout | ||
queryValuesKey: 'values', | ||
queryTextKey: 'text' | ||
const QUERY_DEFAULTS = { | ||
statementTimeout: '0' // the node-postgres default is no timeout | ||
} | ||
// Do not try to parse a postgres DATE to a javascript Date. | ||
pg.types.setTypeParser(1082, 'text', _.identity) | ||
pg.types.setTypeParser(1082, 'text', x => x) | ||
@@ -28,3 +25,3 @@ BPromise.promisifyAll(pg) | ||
function getConnection(env, connector) { | ||
var releaseConnection | ||
let releaseConnection | ||
@@ -35,12 +32,11 @@ return connector() | ||
return client.queryAsync("SET statement_timeout TO '" + env.statementTimeout + "'") | ||
.then(function () { return decorateWithQueryRowsAsync(env, client) }) | ||
.then(() => decorateWithQueryRowsAsync(env, client)) | ||
}) | ||
.disposer(function () { | ||
.disposer(() => | ||
releaseConnectionToPool(releaseConnection) | ||
}) | ||
) | ||
} | ||
function getTransaction(env, connector, tablesToLock_) { | ||
var tablesToLock = tablesToLock_ || [] | ||
var releaseConnection | ||
function getTransaction(env, connector, tablesToLock=[]) { | ||
let releaseConnection | ||
@@ -51,10 +47,10 @@ return connector() | ||
return client.queryAsync("SET statement_timeout TO '" + env.statementTimeout + "'") | ||
.then(function () { return client.queryAsync(constructLockingBeginStatement(tablesToLock)) }) | ||
.then(function () { return decorateWithQueryRowsAsync(env, client) }) | ||
.then(() => client.queryAsync(constructLockingBeginStatement(tablesToLock))) | ||
.then(() => decorateWithQueryRowsAsync(env, client)) | ||
}) | ||
.disposer(function (tx, promise) { | ||
if (promise.isFulfilled()) { | ||
return tx.queryAsync('COMMIT').tap(function () { return releaseConnectionToPool(releaseConnection) }) | ||
return tx.queryAsync('COMMIT').tap(() => releaseConnectionToPool(releaseConnection)) | ||
} else { | ||
return tx.queryAsync('ROLLBACK').tap(function () { return releaseConnectionToPool(releaseConnection) }) | ||
return tx.queryAsync('ROLLBACK').tap(() => releaseConnectionToPool(releaseConnection)) | ||
} | ||
@@ -73,121 +69,78 @@ }) | ||
return Object.assign(client, { | ||
queryRowsAsync: (query, args) => queryWithCtxAsync(env, client, query, args).then(getRows) | ||
queryRowsAsync: (query, args) => client.queryAsync(query, args).then(res => res.rows) | ||
}) | ||
} | ||
function executeQueryRowsAsync(env, connector, query, args) { | ||
return using(getConnection(env, connector), function (connection) { | ||
return connection.queryRowsAsync(query, args) | ||
}) | ||
function executeQueryRows(env, connector, query, args) { | ||
return using(getConnection(env, connector), connection => | ||
connection.queryRowsAsync(query, args) | ||
) | ||
} | ||
function getRows(res) { | ||
return res.rows | ||
function executeQuery(env, connector, query, args) { | ||
return using(getConnection(env, connector), connection => | ||
connection.queryAsync(query, args) | ||
) | ||
} | ||
function queryWithCtxAsync(env, client, query, args) { | ||
if (_.isObject(query) && query[env.queryValuesKey] && Array.isArray(args) && args.length > 0) { | ||
throw new Error('Both query.values and args were passed to query. Please use only one of them.') | ||
} | ||
return client.queryAsync(query[env.queryTextKey] || query, query[env.queryValuesKey] || args) | ||
} | ||
function constructLockingBeginStatement(involvedTables) { | ||
var lockSql = 'LOCK TABLE {table} IN SHARE ROW EXCLUSIVE MODE' | ||
var statements = involvedTables.map(function(table) { | ||
return stringTemplate(lockSql, { table: table }) | ||
}) | ||
statements.unshift('BEGIN') | ||
return statements.join(';') | ||
const statements = involvedTables.map(table => | ||
`LOCK TABLE ${table} IN SHARE ROW EXCLUSIVE MODE` | ||
) | ||
return ['BEGIN'].concat(statements).join(';') | ||
} | ||
function createUpsertCTE(table, idField, args) { | ||
var insert = args.insert | ||
var update = args.update | ||
function createMultipleInsertCTE(insert) { | ||
const placeholders = insert.text.match(/\$\d+/g).map(param => parseInt(param.substring(1), 10)) | ||
assert.ok(_.isEqual(placeholders, _.range(1, placeholders.length + 1)), "Refer to the insert statement parameters in ascending order!") | ||
const numberOfParams = placeholders.length | ||
const sqlValuesText = getStringAfterLast(insert.text, 'values') | ||
const valuesTuples = replaceParameters(numberOfParams, sqlValuesText, insert.values) | ||
return { | ||
text: 'with ' + formatQueryText(), | ||
values: getValues() | ||
text: insert.text.replace(sqlValuesText, valuesTuples), | ||
values: insert.values | ||
} | ||
function getValues() { return update.values.concat(insert.values) } | ||
function replaceParameters(parametersInSql, sqlString, values) { | ||
assert.ok(values.length % parametersInSql === 0, | ||
`Check that there are a multiple of parameter count values in the statement, ${values.length} vs ${parametersInSql}`) | ||
const tupleCount = values.length / parametersInSql | ||
const split = sqlString.split(/\$\d+/) | ||
function formatQueryText() { | ||
var insertSql = rewriteInsertSql(insert.text, update.values.length) | ||
return stringTemplate(upsertQuery(), { | ||
table: table, | ||
update: update.text, | ||
uuid: idField, | ||
insert: insertSql | ||
}) | ||
function selectQuery() { | ||
return '(select * from {table}_update) union all (select * from {table}_insert)' | ||
let valuesString = '' | ||
for (let valueTuple = 0; valueTuple < tupleCount; valueTuple++) { | ||
for (let tupleParamIdx = 0; tupleParamIdx < split.length - 1; tupleParamIdx++) { | ||
valuesString += split[tupleParamIdx] + '$' + (valueTuple * (split.length - 1) + tupleParamIdx + 1) | ||
} | ||
valuesString += split[split.length - 1] + ',' | ||
} | ||
function insertQuery() { | ||
return '{insert} where not exists (select * from {table}_update) returning {uuid}' | ||
} | ||
function updateQuery() { | ||
return '{update} returning {uuid}' | ||
} | ||
function upsertQuery() { | ||
return '{table}_update AS (' + | ||
updateQuery() + | ||
'), {table}_insert as (' + | ||
insertQuery() + ')' + | ||
selectQuery() | ||
} | ||
return valuesString.slice(0, -1) | ||
} | ||
function rewriteInsertSql(text, count) { | ||
var i = 0 | ||
return text.split('$').map(function(fragment) { | ||
return fragment.replace(/\d+/, (count + i++)) | ||
}).join('$') | ||
} | ||
function getStringAfterLast(str, searchValue) { | ||
const idx = str.toLowerCase().lastIndexOf(searchValue) | ||
return str.substring(idx + searchValue.length) | ||
} | ||
} | ||
function createMultipleInsertCTE(insert) { | ||
var placeholders = insert.text.match(/\$\d+/g).map(function(param) { return parseInt(param.substring(1), 10)}) | ||
assert.ok(_.isEqual(placeholders, _.range(1, placeholders.length + 1)), "Refer to the insert statement parameters in ascending order!") | ||
var numberOfParams = _.last(placeholders) | ||
var sqlValuesText = _.last(insert.text.split('values')) | ||
var valuesSegmentFragments = replaceParameters(numberOfParams, sqlValuesText, insert.values) | ||
function createPoolConfig(env) { | ||
const poolConfig = Object.assign({}, POOL_DEFAULTS, env) | ||
return { | ||
text: insert.text.replace(sqlValuesText, valuesSegmentFragments.join(',')), | ||
values: insert.values | ||
} | ||
// backwards compatibility | ||
poolConfig.connectionString = env.dbUrl | ||
poolConfig.max = env.poolSize | ||
function replaceParameters(parameterCountInSql, sqlString, values) { | ||
var i = 1 | ||
var valueSegmentCount = values.length / parameterCountInSql | ||
assert.ok(valueSegmentCount % 1 === 0, "Check that there are a multiple of parameter count values in the statement" + values.length + parameterCountInSql ) | ||
return _.times(valueSegmentCount, function () { | ||
var split = sqlString.split(/\$\d+/) | ||
var omit = 0 | ||
return _.map(split, function (fragment) { | ||
var isLastItemInFragment = ++omit % split.length === 0 | ||
return isLastItemInFragment ? fragment : fragment + '$' + i++ | ||
}).join('') | ||
}) | ||
} | ||
return poolConfig | ||
} | ||
module.exports = function (env) { | ||
var poolConfig = _.assign({}, POOL_DEFAULTS, env) | ||
const poolConfig = createPoolConfig(env) | ||
// backwards compatibility | ||
poolConfig.connectionString = env.dbUrl | ||
poolConfig.max = env.poolSize | ||
const pool = new pg.Pool(poolConfig) | ||
var pool = new pg.Pool(poolConfig) | ||
const connectMultiArgAsync = BPromise.promisify(pool.connect, { context: pool, multiArgs: true }) | ||
var connectMultiArgAsync = BPromise.promisify(pool.connect, { context: pool, multiArgs: true}) | ||
const queryConfig = Object.assign({}, QUERY_DEFAULTS, env) | ||
var queryConfig = _.assign({}, QUERY_DEFAULTS, env) | ||
return { | ||
@@ -199,23 +152,23 @@ pool: pool, | ||
withTransaction: withTransaction, | ||
queryAsync: queryRowsWithEnv, | ||
queryAsync: queryWithEnv, | ||
queryRowsAsync: queryRowsWithEnv, | ||
createMultipleInsertCTE: createMultipleInsertCTE, | ||
createUpsertCTE: createUpsertCTE, | ||
on: on, | ||
end: end | ||
createMultipleInsertCTE, | ||
on, | ||
end | ||
} | ||
function getConnectionWithEnv() { | ||
return getConnection(queryConfig, connectMultiArgAsync) | ||
} | ||
function getConnectionWithEnv() { return getConnection(queryConfig, connectMultiArgAsync) } | ||
function getTransactionWithEnv(tablesToLock) { | ||
return getTransaction(queryConfig, connectMultiArgAsync, tablesToLock) | ||
} | ||
function getTransactionWithEnv(tablesToLock) { return getTransaction(queryConfig, connectMultiArgAsync, tablesToLock) } | ||
function queryRowsWithEnv(query, args) { return executeQueryRowsAsync(queryConfig, connectMultiArgAsync, query, args)} | ||
function on(event, fn) { | ||
pool.on(event, fn) | ||
function queryWithEnv(query, args) { | ||
return executeQuery(queryConfig, connectMultiArgAsync, query, args) | ||
} | ||
function end() { | ||
return pool.end() | ||
function queryRowsWithEnv(query, args) { | ||
return executeQueryRows(queryConfig, connectMultiArgAsync, query, args) | ||
} | ||
@@ -230,2 +183,6 @@ | ||
} | ||
function on(event, fn) { return pool.on(event, fn) } | ||
function end() { return pool.end() } | ||
} |
{ | ||
"name": "pg-using-bluebird", | ||
"version": "3.3.1", | ||
"version": "4.0.0", | ||
"description": "Helpers for managing PostgreSQL connections", | ||
"main": "index.js", | ||
"types": "index.d.ts", | ||
"engines": { | ||
"node": ">=4.0.0" | ||
"node": ">=8.0.0" | ||
}, | ||
@@ -13,3 +14,4 @@ "scripts": { | ||
"jenkins-test": "NODE_ENV=test BLUEBIRD_DEBUG=1 JUNIT_REPORT_PATH=test-report.xml mocha --reporter mocha-jenkins-reporter --exit 'test/**/*.js'", | ||
"test": "NODE_ENV=test BLUEBIRD_DEBUG=1 nyc --reporter text mocha --exit 'test/**/*.js'" | ||
"test": "NODE_ENV=test BLUEBIRD_DEBUG=1 nyc --reporter text mocha --exit 'test/**/*.js'", | ||
"tsd": "tsd" | ||
}, | ||
@@ -21,6 +23,7 @@ "repository": { | ||
"dependencies": { | ||
"@types/bluebird": "^3.5.32", | ||
"@types/pg": "^7.14.5", | ||
"bluebird": "^3.7.2", | ||
"lodash": "^4.17.15", | ||
"pg": "^7.14.0", | ||
"string-template": "^1.0.0" | ||
"lodash": "^4.17.20", | ||
"pg": "^8.3.3" | ||
}, | ||
@@ -31,5 +34,7 @@ "devDependencies": { | ||
"eslint": "^5.16.0", | ||
"mocha": "^5.2.0", | ||
"mocha-jenkins-reporter": "^0.4.2", | ||
"nyc": "^14.1.1" | ||
"mocha": "^8.1.3", | ||
"mocha-jenkins-reporter": "^0.4.5", | ||
"nyc": "^15.1.0", | ||
"tsd": "^0.13.1", | ||
"typescript": "^4.0.3" | ||
}, | ||
@@ -36,0 +41,0 @@ "license": "MIT", |
@@ -46,3 +46,3 @@ # pg-using-bluebird | ||
refer to [node-postgres documentation](https://node-postgres.com/api/pool) for | ||
configuration options. | ||
configuration options. See tests for sample usage. | ||
@@ -55,2 +55,4 @@ The initializer returns an object with the following functions: | ||
```queryAsync(query, [args])``` performs a query with the optional argument list inserted into the query. Returns the result object. | ||
```queryRowsAsync(query, [args])``` performs a query with the optional argument list inserted into the query. Returns the resulting rows. | ||
@@ -61,5 +63,2 @@ | ||
```createUpsertCTE(table, idField, args)``` creates an upsert query, returns an object | ||
with ```text``` for the query and ```values``` for the arguments. | ||
```on(event, fn)``` attach and event handler fn to the pool event event, see node-postgres documentation for event types | ||
@@ -77,3 +76,3 @@ | ||
* [pg-promise](https://www.npmjs.com/package/pg-promise), a more generic Promises/A+ promisification of node-postgres with more features and more code. Does not leverage `using()` for resource management. | ||
* [pg-promise](https://www.npmjs.com/package/pg-promise), a more generic Promises/A+ promisification of node-postgres with more features and more code. | ||
* [dbh-pg](https://www.npmjs.com/package/dbh-pg), a node-postgres and bluebird specific library with it's own api for querying. |
"use strict"; // eslint-disable-line semi | ||
var pgrm = require('../index.js') | ||
var configs = {dbUrl: "postgres://localhost/pgrm-tests"} | ||
var BPromise = require('bluebird'), | ||
using = BPromise.using, | ||
pgrmWithDefaults = pgrm(configs), | ||
chai = require('chai'), | ||
chaiAsPromised = require('chai-as-promised'), | ||
_ = require('lodash') | ||
const pgrm = require('../index.js') | ||
const pgConfig = {dbUrl: "postgres://localhost/pgrm-tests"} | ||
const BPromise = require('bluebird') | ||
const using = BPromise.using | ||
const pgrmWithDefaults = pgrm(pgConfig) | ||
const chai = require('chai') | ||
const chaiAsPromised = require('chai-as-promised') | ||
chai.use(chaiAsPromised) | ||
var assert = chai.assert | ||
const assert = chai.assert | ||
var QUERY_CANCELED = '57014' // http://www.postgresql.org/docs/9.4/static/errcodes-appendix.html | ||
const QUERY_CANCELED = '57014' // http://www.postgresql.org/docs/9.4/static/errcodes-appendix.html | ||
describe('connection-test.js', function () { | ||
beforeEach(function () { | ||
return using(pgrmWithDefaults.getConnection(), function (conn) { | ||
return conn.queryAsync("drop table if exists foo").then(function () { | ||
return conn.queryAsync("create table foo(bar integer unique, id serial)") | ||
}) | ||
}) | ||
}) | ||
beforeEach(() => | ||
using(pgrmWithDefaults.getConnection(), conn => | ||
conn.queryAsync("drop table if exists foo").then(() => | ||
conn.queryAsync("create table foo(bar integer unique, id serial)") | ||
))) | ||
describe('configuration', function () { | ||
it('disables parsing of SQL dates to javascript dates', function () { | ||
return assert.eventually.deepEqual(pgrmWithDefaults.queryRowsAsync("select date('2015-03-30') as the_date"), [{the_date: '2015-03-30'}]) | ||
}) | ||
it('disables parsing of SQL dates to javascript dates', () => | ||
assert.eventually.deepEqual( | ||
pgrmWithDefaults.queryRowsAsync("select date('2015-03-30') as the_date"), | ||
[{the_date: '2015-03-30'}]) | ||
) | ||
}) | ||
describe('shortcut queryRowsAsync', function () { | ||
it('returns the rows of the result', function () { | ||
return pgrmWithDefaults.queryRowsAsync("insert into foo(bar) values ($1)", [1]).then(assertOneEventuallyInFoo) | ||
}) | ||
it('is has an alias queryAsync until the next breaking change release', function() { | ||
return pgrmWithDefaults.queryAsync("insert into foo(bar) values ($1)", [1]).then(assertOneEventuallyInFoo) | ||
}) | ||
describe('single queries', function () { | ||
it('queryRowsAsync returns the rows of the result', () => | ||
pgrmWithDefaults.queryRowsAsync("insert into foo(bar) values ($1)", [1]) | ||
.then(() => pgrmWithDefaults.queryRowsAsync("select bar from foo")) | ||
.then(rows => assert.deepEqual(rows, [{ bar: 1 }])) | ||
) | ||
it('queryAsync returns the result object', () => | ||
pgrmWithDefaults.queryAsync("insert into foo(bar) values ($1)", [1]) | ||
.then(() => pgrmWithDefaults.queryAsync("select bar from foo")) | ||
.tap(resultObj => assert.equal(resultObj.rowCount, 1)) | ||
.then(resultObj => assert.deepEqual(resultObj.rows, [{ bar: 1 }])) | ||
) | ||
}) | ||
describe('connections', function () { | ||
it('return the result object', function () { | ||
return using(pgrmWithDefaults.getConnection(), assertResponseObj) | ||
}) | ||
it('commit automatically', function () { | ||
return using(pgrmWithDefaults.getConnection(), insert1IntoFoo) | ||
it('return the result object', () => | ||
using(pgrmWithDefaults.getConnection(), assertResponseObj) | ||
) | ||
it('commit automatically', () => | ||
using(pgrmWithDefaults.getConnection(), insert1IntoFoo) | ||
.then(assertOneEventuallyInFoo) | ||
}) | ||
it('do not rollback errors', function () { | ||
return using(pgrmWithDefaults.getConnection(), function (conn) { | ||
return insert1IntoFoo(conn).then(throwAnError) | ||
}).catch(assertOneEventuallyInFoo) | ||
}) | ||
) | ||
it('do not rollback errors', () => | ||
using(pgrmWithDefaults.getConnection(), conn => | ||
insert1IntoFoo(conn) | ||
.then(throwAnError)) | ||
.catch(assertOneEventuallyInFoo) | ||
) | ||
}) | ||
describe('timeouts', function () { | ||
it('cause rollback', function () { | ||
return using(pgrmWithDefaults.getTransaction(), | ||
function (tx) { | ||
return tx.queryAsync("SET statement_timeout TO '100ms'") | ||
.then(function () { return insert1IntoFoo(tx) }) | ||
.then(function () { return causeAndAssertATimeout(tx) }) | ||
.then(assertFooIsEventuallyEmpty) | ||
}) | ||
}) | ||
describe('can be configured on pgrm level', function () { | ||
var pgrmWithShortTimeout = pgrm(_.assign({}, configs, {statementTimeout: '1ms'})) | ||
it('for transactions', function () { | ||
return using(pgrmWithShortTimeout.getTransaction(), causeAndAssertATimeout) | ||
}) | ||
it('for connections', function () { | ||
return using(pgrmWithShortTimeout.getConnection(), causeAndAssertATimeout) | ||
}) | ||
const pgrmWithShortTimeout = pgrm(Object.assign({}, pgConfig, {statementTimeout: '1ms'})) | ||
it('for transactions', () => | ||
using(pgrmWithShortTimeout.getTransaction(), causeAndAssertATimeout) | ||
) | ||
it('for connections', () => | ||
using(pgrmWithShortTimeout.getConnection(), causeAndAssertATimeout) | ||
) | ||
}) | ||
describe('can be configured per session', function () { | ||
it('for transactions', function () { | ||
return using(pgrmWithDefaults.getTransaction(), function (tx) { | ||
return tx.queryAsync("SET statement_timeout TO '1ms'").then(function () { return causeAndAssertATimeout(tx) }) | ||
}) | ||
}) | ||
it('for connections', function () { | ||
return using(pgrmWithDefaults.getConnection(), function (conn) { | ||
return conn.queryAsync("SET statement_timeout TO '1ms'").then(function () { return causeAndAssertATimeout(conn) }) | ||
}) | ||
}) | ||
it('for transactions', () => | ||
using(pgrmWithDefaults.getTransaction(), tx => | ||
tx.queryAsync("SET statement_timeout TO '1ms'") | ||
.then(() => causeAndAssertATimeout(tx)) | ||
) | ||
) | ||
it('cause rollback in transaction', () => | ||
using(pgrmWithDefaults.getTransaction(), tx => | ||
tx.queryAsync("SET statement_timeout TO '99ms'") | ||
.then(() => insert1IntoFoo(tx)) | ||
.then(() => causeAndAssertATimeout(tx)) | ||
).then(assertFooIsEventuallyEmpty) | ||
) | ||
it('for connections', () => | ||
using(pgrmWithDefaults.getConnection(), conn => | ||
conn.queryAsync("SET statement_timeout TO '1ms'") | ||
.then(() => causeAndAssertATimeout(conn)) | ||
) | ||
) | ||
}) | ||
}) | ||
describe('transactions', function () { | ||
it('return the result object', function () { | ||
return using(pgrmWithDefaults.getTransaction(), assertResponseObj) | ||
}) | ||
it('are committed if there are no exceptions', function () { | ||
return using(pgrmWithDefaults.getTransaction(), insert1IntoFoo) | ||
it('return the result object', () => | ||
using(pgrmWithDefaults.getTransaction(), assertResponseObj) | ||
) | ||
it('are committed if there are no exceptions', () => | ||
using(pgrmWithDefaults.getTransaction(), insert1IntoFoo) | ||
.then(assertOneEventuallyInFoo) | ||
) | ||
it('are rollbacked in case of exceptions within the using-block', () => | ||
using(pgrmWithDefaults.getTransaction(), tx => | ||
insert1IntoFoo(tx).then(throwAnError) | ||
).catch(assertFooIsEventuallyEmpty) | ||
) | ||
it('are rollbacked in case of SQL exceptions', () => { | ||
assert.isRejected( | ||
using(pgrmWithDefaults.getTransaction(), tx => | ||
insert1IntoFoo(tx) | ||
.then(assertOneEventuallyInFoo) | ||
.then(() => | ||
tx.queryAsync("this is not sql") | ||
) | ||
), | ||
Error) | ||
return assertFooIsEventuallyEmpty() | ||
}) | ||
it('are rollbacked in case of exceptions within the using-block', function () { | ||
return using(pgrmWithDefaults.getTransaction(), function (tx) { | ||
return insert1IntoFoo(tx).then(throwAnError) | ||
}).catch(assertFooIsEventuallyEmpty) | ||
}) | ||
it('are rollbacked in case of SQL exceptions', function () { | ||
return using(pgrmWithDefaults.getTransaction(), function (tx) { | ||
return insert1IntoFoo(tx) | ||
.then(assertOneEventuallyInFoo) | ||
.then(function () { | ||
return tx.queryAsync("this is not sql") | ||
}) | ||
}).catch(assertFooIsEventuallyEmpty) | ||
}) | ||
describe('support locking of tables', function () { | ||
it('and do not lock anything by default and are in read committed isolation level', function () { | ||
return using(pgrmWithDefaults.getTransaction(), function (outerTx) { | ||
return using(pgrmWithDefaults.getTransaction(), function (innerTx) { | ||
return insert1IntoFoo(outerTx).then(function () { | ||
return innerTx.queryAsync('insert into foo(bar) values(2)') | ||
}).then(function () { | ||
return assert.eventually.deepEqual(BPromise.all([ | ||
outerTx.queryAsync("select * from foo").then(function (res) {return res.rows}), | ||
innerTx.queryAsync("select * from foo").then(function (res) {return res.rows}), | ||
pgrmWithDefaults.queryAsync("select * from foo")]), | ||
[ | ||
[{bar: 1, id: 1}], | ||
[{bar: 2, id: 2}], | ||
[] | ||
]) | ||
}) | ||
}) | ||
}).then(function () { | ||
return assert.eventually.deepEqual(pgrmWithDefaults.queryRowsAsync("select bar from foo order by bar"), [{bar: 1}, {bar: 2}]) | ||
}) | ||
}) | ||
it('and do not lock anything by default and be in read committed isolation level', () => | ||
using(pgrmWithDefaults.getTransaction(), outerTx => | ||
using(pgrmWithDefaults.getTransaction(), innerTx => | ||
insert1IntoFoo(outerTx) | ||
.then(() => innerTx.queryAsync('insert into foo(bar) values(2)')) | ||
.then(() => | ||
assert.eventually.deepEqual( | ||
BPromise.all([ | ||
outerTx.queryRowsAsync("select * from foo"), | ||
innerTx.queryRowsAsync("select * from foo"), | ||
pgrmWithDefaults.queryRowsAsync("select * from foo")]), | ||
[ | ||
[{ bar: 1, id: 1 }], | ||
[{ bar: 2, id: 2 }], | ||
[] | ||
]) | ||
) | ||
) | ||
).then(() => | ||
assert.eventually.deepEqual( | ||
pgrmWithDefaults.queryRowsAsync("select bar from foo order by bar"), | ||
[{ bar: 1 }, { bar: 2 }]) | ||
) | ||
) | ||
it('and lock the given table', function () { | ||
var selectingTxFn | ||
var selectingTxP = new BPromise(function (resolve) { selectingTxFn = resolve }) | ||
let selectingTxFn | ||
const selectingTxP = new BPromise(resolve => { selectingTxFn = resolve }) | ||
var earlierTxP = using(pgrmWithDefaults.getTransaction(['foo']), function (earlierTx) { | ||
using(pgrmWithDefaults.getTransaction(['foo']), function (laterTx) { | ||
selectingTxFn(laterTx.queryAsync("select bar from foo").then(function (res) { return res.rows })) | ||
const earlierTxP = using(pgrmWithDefaults.getTransaction(['foo']), earlierTx => { | ||
using(pgrmWithDefaults.getTransaction(['foo']), laterTx => { | ||
selectingTxFn(laterTx.queryAsync("select bar from foo").then(res => res.rows)) | ||
}) | ||
return BPromise.delay(100).then(function () { | ||
return insert1IntoFoo(earlierTx).then(function () { return 'inserted'}) | ||
}) | ||
return BPromise.delay(100) | ||
.then(() => insert1IntoFoo(earlierTx)) | ||
.then(() => 'inserted') | ||
}) | ||
return assert.eventually.deepEqual(BPromise.all([earlierTxP, selectingTxP]), ['inserted', [{bar: 1}]]) | ||
return assert.eventually.deepEqual( | ||
BPromise.all([earlierTxP, selectingTxP]), | ||
['inserted', [{bar: 1}]]) | ||
}) | ||
@@ -149,27 +178,27 @@ }) | ||
describe('withConnection', function () { | ||
it('wraps using() and getConnection()', function () { | ||
return pgrmWithDefaults.withConnection(assertResponseObj) | ||
}) | ||
it('wraps using() and getConnection()', () => | ||
pgrmWithDefaults.withConnection(assertResponseObj) | ||
) | ||
it('does not rollback errors', function () { | ||
return pgrmWithDefaults.withConnection(function (conn) { | ||
return insert1IntoFoo(conn).then(throwAnError) | ||
}).catch(assertOneEventuallyInFoo) | ||
}) | ||
it('does not rollback errors', () => | ||
pgrmWithDefaults.withConnection(conn => | ||
insert1IntoFoo(conn).then(throwAnError) | ||
).catch(assertOneEventuallyInFoo) | ||
) | ||
}) | ||
describe('withTransaction', function () { | ||
it('wraps using() and getTransaction()', function () { | ||
return pgrmWithDefaults.withTransaction(assertResponseObj) | ||
}) | ||
it('wraps using() and getTransaction()', () => | ||
pgrmWithDefaults.withTransaction(assertResponseObj) | ||
) | ||
it('rolls back the transaction if the function throws', function () { | ||
return pgrmWithDefaults.withTransaction(function (tx) { | ||
return insert1IntoFoo(tx).then(throwAnError) | ||
}).catch(assertFooIsEventuallyEmpty) | ||
}) | ||
it('rolls back the transaction if the function throws', () => | ||
pgrmWithDefaults.withTransaction(tx => | ||
insert1IntoFoo(tx).then(throwAnError) | ||
).catch(assertFooIsEventuallyEmpty) | ||
) | ||
}) | ||
function insert1IntoFoo(connOrTx) { | ||
return connOrTx.queryAsync("insert into foo(bar) values (1)") | ||
return connOrTx.queryAsync("insert into foo (bar) values (1)") | ||
} | ||
@@ -191,10 +220,11 @@ | ||
function causeAndAssertATimeout(txOrConn) { | ||
return assert.isRejected(txOrConn.queryAsync('SELECT pg_sleep(100)').catch(assertErrCodeIsQueryCanceled), /canceling statement due to statement timeout/) | ||
return assert.isRejected(txOrConn.queryAsync('SELECT pg_sleep(100)') | ||
.catch(assertErrCodeIsQueryCanceled), /canceling statement due to statement timeout/) | ||
} | ||
function assertResponseObj(connOrTx) { | ||
return function () { | ||
return () => { | ||
insert1IntoFoo(connOrTx).then(function assertResponseObject(conn) { | ||
return function () { | ||
return conn.queryAsync('SELECT bar from foo').then(function (res) { | ||
return () => | ||
conn.queryAsync('SELECT bar from foo').then(res => { | ||
assert.equal(res.rowCount, 1) | ||
@@ -204,9 +234,7 @@ assert.equal(res.command, 'SELECT') | ||
}) | ||
} | ||
}).then(function assertRowsObject(conn) { | ||
return function () { | ||
return conn.queryRowsAsync('SELECT bar from foo').then(function (rows) { | ||
return () => | ||
conn.queryRowsAsync('SELECT bar from foo').then(rows => { | ||
assert.deepEqual(rows, [{bar: 1}]) | ||
}) | ||
} | ||
}) | ||
@@ -216,3 +244,2 @@ } | ||
function throwAnError() { | ||
@@ -219,0 +246,0 @@ throw new Error('an error after the insertion has happened') |
"use strict"; // eslint-disable-line semi | ||
var pgrm = require('../index.js') | ||
var configs = {dbUrl: "postgres://localhost/pgrm-tests"} | ||
var BPromise = require('bluebird') | ||
var using = BPromise.using | ||
var pgrmWithDefaults = pgrm(configs) | ||
var chai = require('chai') | ||
var chaiAsPromised = require('chai-as-promised') | ||
const pgrm = require('../index.js') | ||
const pgConfig = {dbUrl: "postgres://localhost/pgrm-tests"} | ||
const BPromise = require('bluebird') | ||
const using = BPromise.using | ||
const pgrmWithDefaults = pgrm(pgConfig) | ||
const chai = require('chai') | ||
const chaiAsPromised = require('chai-as-promised') | ||
chai.use(chaiAsPromised) | ||
var assert = chai.assert | ||
const assert = chai.assert | ||
@@ -17,3 +17,3 @@ describe('pool-event-test.js', function () { | ||
it('receives connect event', () => { | ||
var receivedEvent = false | ||
let receivedEvent = false | ||
pgrmWithDefaults.on('connect', () => { receivedEvent = true }) | ||
@@ -20,0 +20,0 @@ return using(pgrmWithDefaults.getConnection(), () => assert.equal(receivedEvent, true)) |
"use strict"; // eslint-disable-line semi | ||
var pgrm = require('../index.js') | ||
var configs = {dbUrl: "postgres://localhost/pgrm-tests"} | ||
var pgrmWithDefaults = pgrm(configs) | ||
var pgrmWithCustomKeys = pgrm(Object.assign(configs, {queryTextKey: 'customQueryTextKey', queryValuesKey: 'customQueryValuesKey'})) | ||
var using = require('bluebird').using | ||
var chai = require('chai') | ||
var chaiAsPromised = require('chai-as-promised') | ||
var assert = chai.assert | ||
const pgrm = require('../index.js') | ||
const pgConfig = { dbUrl: "postgres://localhost/pgrm-tests" } | ||
const pgrmWithDefaults = pgrm(pgConfig) | ||
const using = require('bluebird').using | ||
const chai = require('chai') | ||
const chaiAsPromised = require('chai-as-promised') | ||
chai.use(chaiAsPromised) | ||
const assert = chai.assert | ||
describe('queryAsync with a query object', function () { | ||
beforeEach(function () { | ||
return using(pgrmWithDefaults.getConnection(), function (conn) { | ||
return conn.queryAsync("drop table if exists foo").then(function () { | ||
return conn.queryAsync("create table foo(bar integer unique, id serial)") | ||
}) | ||
}) | ||
}) | ||
describe('query-object-test.js', function () { | ||
beforeEach(() => | ||
using(pgrmWithDefaults.getConnection(), conn => | ||
conn.queryAsync("drop table if exists foo").then(() => | ||
conn.queryAsync("create table foo(bar integer unique, id serial)") | ||
)) | ||
) | ||
it('behaves correctly when using query object', function () { | ||
return pgrmWithDefaults.queryRowsAsync({text: "insert into foo(bar) values ($1)", values: [1]}) | ||
.then(assertOneEventuallyInFoo) | ||
}) | ||
describe('implicit connection queries', () => { | ||
it('queryAsync behaves correctly called with query object', () => | ||
pgrmWithDefaults.queryAsync({ text: "insert into foo(bar) values ($1)", values: [1] }) | ||
.then(assertOneEventuallyInFoo) | ||
) | ||
it('throws an error if args is passed when using query object', function () { | ||
return pgrmWithDefaults.queryRowsAsync({text: "insert into foo(bar) values ($1)", values: [1]}, [1]) | ||
.catch(function (err) { | ||
assert.equal(err, 'Error: Both query.values and args were passed to query. Please use only one of them.') | ||
return assertFooIsEventuallyEmpty() | ||
}) | ||
it('queryAsync behaves correctly when called with text parameter', () => | ||
pgrmWithDefaults.queryAsync("insert into foo(bar) values ($1)", [1]) | ||
.then(assertOneEventuallyInFoo) | ||
) | ||
it('queryRowsAsync behaves correctly when using query object', () => | ||
pgrmWithDefaults.queryRowsAsync({ text: "insert into foo(bar) values ($1)", values: [1] }) | ||
.then(assertOneEventuallyInFoo) | ||
) | ||
it('queryRowsAsync behaves correctly when using query object', () => | ||
pgrmWithDefaults.queryRowsAsync("insert into foo(bar) values ($1)", [1]) | ||
.then(assertOneEventuallyInFoo) | ||
) | ||
}) | ||
it('works with custom query text and values keys', function () { | ||
return pgrmWithCustomKeys.queryRowsAsync({ | ||
customQueryTextKey: "insert into foo(bar) values ($1)", | ||
customQueryValuesKey: [1] | ||
}).then(assertOneEventuallyInFoo) | ||
}) | ||
}) | ||
function assertOneEventuallyInFoo() { | ||
return assert.eventually.deepEqual(pgrmWithDefaults.queryRowsAsync("select bar from foo"), [{bar: 1}]) | ||
return assert.eventually.deepEqual(pgrmWithDefaults.queryRowsAsync("select bar from foo"), [{ bar: 1 }]) | ||
} | ||
function assertFooIsEventuallyEmpty() { | ||
return assert.eventually.deepEqual(pgrmWithDefaults.queryRowsAsync("select bar from foo"), []) | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
37658
15
645
5
8
75
2
+ Added@types/bluebird@^3.5.32
+ Added@types/pg@^7.14.5
+ Added@types/bluebird@3.5.42(transitive)
+ Added@types/node@22.9.0(transitive)
+ Added@types/pg@7.14.11(transitive)
+ Addedpg@8.13.1(transitive)
+ Addedpg-cloudflare@1.1.1(transitive)
+ Addedpg-connection-string@2.7.0(transitive)
+ Addedpg-pool@3.7.0(transitive)
+ Addedpg-protocol@1.7.0(transitive)
+ Addedundici-types@6.19.8(transitive)
- Removedstring-template@^1.0.0
- Removedbuffer-writer@2.0.0(transitive)
- Removedpacket-reader@1.0.0(transitive)
- Removedpg@7.18.2(transitive)
- Removedpg-connection-string@0.1.3(transitive)
- Removedpg-packet-stream@1.1.0(transitive)
- Removedpg-pool@2.0.10(transitive)
- Removedsemver@4.3.2(transitive)
- Removedstring-template@1.0.0(transitive)
Updatedlodash@^4.17.20
Updatedpg@^8.3.3