Skip to content

Commit

Permalink
feat(postgresql): easier SSL config and options param support (#13673)
Browse files Browse the repository at this point in the history
This commit uses the pg_connection_string package to parse the
connection string if the dialect is postgresql. This is helpful because
it automatically handles reading SSL certs that are specified in the
connection string.

As part of this, support was added for the `options` URL parameter,
which allows arbitrary session variables to be configured in the
connection string.

Co-authored-by: Sascha Depold <sdepold@users.noreply.github.com>
  • Loading branch information
rafiss and sdepold committed Nov 18, 2021
1 parent 0312f8e commit 9591573
Show file tree
Hide file tree
Showing 5 changed files with 27 additions and 2 deletions.
5 changes: 4 additions & 1 deletion lib/dialects/postgres/connection-manager.js
Expand Up @@ -118,7 +118,10 @@ class ConnectionManager extends AbstractConnectionManager {
// Times out queries after a set time in milliseconds in client end, query would be still running in database end.
'query_timeout',
// Terminate any session with an open transaction that has been idle for longer than the specified duration in milliseconds. Added in pg v7.17.0 only supported in postgres >= 10
'idle_in_transaction_session_timeout'
'idle_in_transaction_session_timeout',
// Postgres allows additional session variables to be configured in the connection string in the `options` param.
// see [https://www.postgresql.org/docs/14/libpq-connect.html#LIBPQ-CONNECT-OPTIONS]
'options'
]));
}

Expand Down
7 changes: 7 additions & 0 deletions lib/sequelize.js
Expand Up @@ -2,6 +2,7 @@

const url = require('url');
const path = require('path');
const pgConnectionString = require('pg-connection-string');
const retry = require('retry-as-promised');
const _ = require('lodash');

Expand Down Expand Up @@ -232,6 +233,12 @@ class Sequelize {
}
}
}

// For postgres, we can use this helper to load certs directly from the
// connection string.
if (options.dialect === 'postgres' || options.dialect === 'postgresql') {
Object.assign(options.dialectOptions, pgConnectionString.parse(arguments[0]));
}
} else {
// new Sequelize(database, username, password, { ... options })
options = options || {};
Expand Down
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -32,6 +32,7 @@
"lodash": "^4.17.20",
"moment": "^2.26.0",
"moment-timezone": "^0.5.31",
"pg-connection-string": "^2.5.0",
"retry-as-promised": "^3.2.0",
"semver": "^7.3.2",
"sequelize-pool": "^6.0.0",
Expand Down
7 changes: 7 additions & 0 deletions test/integration/dialects/postgres/connection-manager.test.js
Expand Up @@ -47,6 +47,13 @@ if (dialect.match(/^postgres/)) {
const error = await sequelize.query('select pg_sleep(2)').catch(e => e);
expect(error.message).to.equal('Query read timeout');
});

it('should allow overriding session variables through the `options` param', async () => {
const sequelize = Support.createSequelizeInstance({ dialectOptions: { options: '-csearch_path=abc' } });
const result = await sequelize.query('SHOW search_path');
expect(result[0].search_path).to.equal('abc');
});

});

describe('Dynamic OIDs', () => {
Expand Down
9 changes: 8 additions & 1 deletion test/integration/sequelize.test.js
Expand Up @@ -59,7 +59,7 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
}

if (dialect === 'postgres') {
const getConnectionUri = o => `${o.protocol}://${o.username}:${o.password}@${o.host}${o.port ? `:${o.port}` : ''}/${o.database}`;
const getConnectionUri = o => `${o.protocol}://${o.username}:${o.password}@${o.host}${o.port ? `:${o.port}` : ''}/${o.database}${o.options ? `?options=${o.options}` : ''}`;
it('should work with connection strings (postgres protocol)', () => {
const connectionUri = getConnectionUri({ ...config[dialect], protocol: 'postgres' });
// postgres://...
Expand All @@ -70,6 +70,13 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
// postgresql://...
new Sequelize(connectionUri);
});
it('should work with options in the connection string (postgresql protocol)', async () => {
const connectionUri = getConnectionUri({ ...config[dialect], protocol: 'postgresql', options: '-c%20search_path%3dtest_schema' });
const sequelize = new Sequelize(connectionUri);
const result = await sequelize.query('SHOW search_path');
expect(result[0].search_path).to.equal('test_schema');
});

}
});

Expand Down

0 comments on commit 9591573

Please sign in to comment.