diff --git a/lib/native/index.js b/lib/native/index.js index eb9307d..c0394b4 100644 --- a/lib/native/index.js +++ b/lib/native/index.js @@ -1,5 +1,7 @@ //require the c++ bindings & export to javascript var EventEmitter = require('events').EventEmitter; + +var ConnectionParameters = require(__dirname + '/../connection-parameters'); var utils = require(__dirname + "/../utils"); var CopyFromStream = require(__dirname + '/../copystream').CopyFromStream; var CopyToStream = require(__dirname + '/../copystream').CopyToStream; @@ -28,7 +30,7 @@ var nativeConnect = p.connect; p.connect = function(cb) { var self = this; - utils.buildLibpqConnectionString(this._config, function(err, conString) { + this.connectionParameters.getLibpqConnectionString(function(err, conString) { if(err) { return cb ? cb(err) : self.emit('error', err); } @@ -143,13 +145,13 @@ var clientBuilder = function(config) { connection._queryQueue = []; connection._namedQueries = {}; connection._activeQuery = null; - connection._config = utils.normalizeConnectionInfo(config); + connection.connectionParameters = new ConnectionParameters(config); //attach properties to normalize interface with pure js client - connection.user = connection._config.user; - connection.password = connection._config.password; - connection.database = connection._config.database; - connection.host = connection._config.host; - connection.port = connection._config.port; + connection.user = connection.connectionParameters.user; + connection.password = connection.connectionParameters.password; + connection.database = connection.connectionParameters.database; + connection.host = connection.connectionParameters.host; + connection.port = connection.connectionParameters.port; connection.on('connect', function() { connection._connected = true; connection._pulseQueryQueue(true); diff --git a/lib/utils.js b/lib/utils.js index b644163..366ebe6 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -13,88 +13,6 @@ if(typeof events.EventEmitter.prototype.once !== 'function') { }; } -var parseConnectionString = function(str) { - //unix socket - if(str.charAt(0) === '/') { - return { host: str }; - } - var result = url.parse(str); - var config = {}; - config.host = result.hostname; - config.database = result.pathname ? result.pathname.slice(1) : null - var auth = (result.auth || ':').split(':'); - config.user = auth[0]; - config.password = auth[1]; - config.port = result.port; - return config; -}; - -//allows passing false as property to remove it from config -var norm = function(config, propName) { - config[propName] = (config[propName] || (config[propName] === false ? undefined : defaults[propName])) -}; - -//normalizes connection info -//which can be in the form of an object -//or a connection string -var normalizeConnectionInfo = function(config) { - switch(typeof config) { - case 'object': - norm(config, 'user'); - norm(config, 'password'); - norm(config, 'host'); - norm(config, 'port'); - norm(config, 'database'); - return config; - case 'string': - return normalizeConnectionInfo(parseConnectionString(config)); - default: - throw new Error("Unrecognized connection config parameter: " + config); - } -}; - - -var add = function(params, config, paramName) { - var value = config[paramName]; - if(value) { - params.push(paramName+"='"+value+"'"); - } -} - -//builds libpq specific connection string -//from a supplied config object -//the config object conforms to the interface of the config object -//accepted by the pure javascript client -var getLibpgConString = function(config, callback) { - if(typeof config == 'object') { - var params = [] - add(params, config, 'user'); - add(params, config, 'password'); - add(params, config, 'port'); - if(config.database) { - params.push("dbname='" + config.database + "'"); - } - if(config.host) { - if (!config.host.indexOf("/")) { - params.push("host=" + config.host); - } else { - if(config.host != 'localhost' && config.host != '127.0.0.1') { - //do dns lookup - return require('dns').lookup(config.host, function(err, address) { - if(err) return callback(err, null); - params.push("hostaddr="+address) - callback(null, params.join(" ")) - }) - } - params.push("hostaddr=127.0.0.1 "); - } - } - callback(null, params.join(" ")); - } else { - throw new Error("Unrecognized config type for connection"); - } -} - //converts values from javascript types //to their 'raw' counterparts for use as a postgres parameter //note: you can override this function to provide your own conversion mechanism @@ -126,12 +44,6 @@ function normalizeQueryConfig (config, values, callback) { } module.exports = { - normalizeConnectionInfo: normalizeConnectionInfo, - //only exported here to make testing of this method possible - //since it contains quite a bit of logic and testing for - //each connection scenario in an integration test is impractical - buildLibpqConnectionString: getLibpgConString, - parseConnectionString: parseConnectionString, prepareValue: prepareValue, normalizeQueryConfig: normalizeQueryConfig } diff --git a/test/cli.js b/test/cli.js index 45bb5ae..b6ca963 100644 --- a/test/cli.js +++ b/test/cli.js @@ -1,14 +1,5 @@ -var config = {}; -if(process.argv[2]) { - config = require(__dirname + '/../lib/utils').parseConnectionString(process.argv[2]); -} -//TODO use these environment variables in lib/ code -//http://www.postgresql.org/docs/8.4/static/libpq-envars.html -config.host = config.host || process.env['PGHOST'] || process.env['PGHOSTADDR']; -config.port = config.port || process.env['PGPORT']; -config.database = config.database || process.env['PGDATABASE']; -config.user = config.user || process.env['PGUSER']; -config.password = config.password || process.env['PGPASSWORD']; +var ConnectionParameters = require(__dirname + '/../lib/connection-parameters'); +var config = new ConnectionParameters(process.argv[2]); for(var i = 0; i < process.argv.length; i++) { switch(process.argv[i].toLowerCase()) { diff --git a/test/integration/client/copy-tests.js b/test/integration/client/copy-tests.js index eccf49e..d53360b 100644 --- a/test/integration/client/copy-tests.js +++ b/test/integration/client/copy-tests.js @@ -4,7 +4,6 @@ if(helper.args.native) { pg = require(__dirname + '/../../../lib').native; } var ROWS_TO_INSERT = 1000; - var prepareTable = function (client, callback) { client.query( 'CREATE TEMP TABLE copy_test (id SERIAL, name CHARACTER VARYING(10), age INT)', @@ -14,9 +13,8 @@ var prepareTable = function (client, callback) { }) ); }; - test('COPY FROM', function () { - pg.connect(helper.config, assert.calls(function (error, client) { + pg.connect(helper.config, function (error, client) { assert.equal(error, null, "Failed to connect: " + helper.sys.inspect(error)); prepareTable(client, function () { var stream = client.copyFrom("COPY copy_test (name, age) FROM stdin WITH CSV"); @@ -27,21 +25,20 @@ test('COPY FROM', function () { stream.write( String(Date.now() + Math.random()).slice(0,10) + ',' + i + '\n'); } assert.emits(stream, 'close', function () { - client.query("SELECT count(*), sum(age) from copy_test", assert.calls(function (err, result) { + client.query("SELECT count(*), sum(age) from copy_test", function (err, result) { assert.equal(err, null, "Query should not fail"); assert.lengthIs(result.rows, 1) assert.equal(result.rows[0].sum, ROWS_TO_INSERT * (0 + ROWS_TO_INSERT -1)/2); assert.equal(result.rows[0].count, ROWS_TO_INSERT); pg.end(helper.config); - })); + }); }, "COPY FROM stream should emit close after query end"); stream.end(); }); - })); + }); }); - test('COPY TO', function () { - pg.connect(helper.config, assert.calls(function (error, client) { + pg.connect(helper.config, function (error, client) { assert.equal(error, null, "Failed to connect: " + helper.sys.inspect(error)); prepareTable(client, function () { var stream = client.copyTo("COPY person (id, name, age) TO stdin WITH CSV"); @@ -59,10 +56,11 @@ test('COPY TO', function () { pg.end(helper.config); }, "COPY IN stream should emit end event after all rows"); }); - })); + }); }); test('COPY TO, queue queries', function () { + if(helper.config.native) return false; pg.connect(helper.config, assert.calls(function (error, client) { assert.equal(error, null, "Failed to connect: " + helper.sys.inspect(error)); prepareTable(client, function () { @@ -77,10 +75,10 @@ test('COPY TO, queue queries', function () { //imitate long query, to make impossible, //that copy query end callback runs after //second query callback - client.query("SELECT pg_sleep(1)", assert.calls(function () { + client.query("SELECT pg_sleep(1)", function () { query2Done = true; assert.ok(copyQueryDone && query2Done, "second query has to be executed after others"); - })); + }); var buf = new Buffer(0); stream.on('error', function (error) { assert.ok(false, "COPY TO stream should not emit errors" + helper.sys.inspect(error)); @@ -101,6 +99,7 @@ test('COPY TO, queue queries', function () { }); test("COPY TO incorrect usage with large data", function () { + if(helper.config.native) return false; //when many data is loaded from database (and it takes a lot of time) //there are chance, that query will be canceled before it ends //but if there are not so much data, cancel message may be @@ -125,6 +124,7 @@ test("COPY TO incorrect usage with large data", function () { }); test("COPY TO incorrect usage with small data", function () { + if(helper.config.native) return false; pg.connect(helper.config, assert.calls(function (error, client) { assert.equal(error, null, "Failed to connect: " + helper.sys.inspect(error)); //intentionally incorrect usage of copy. @@ -144,7 +144,7 @@ test("COPY TO incorrect usage with small data", function () { }); test("COPY FROM incorrect usage", function () { - pg.connect(helper.config, assert.calls(function (error, client) { + pg.connect(helper.config, function (error, client) { assert.equal(error, null, "Failed to connect: " + helper.sys.inspect(error)); prepareTable(client, function () { //intentionally incorrect usage of copy. @@ -161,6 +161,6 @@ test("COPY FROM incorrect usage", function () { }) ); }); - })); + }); }); diff --git a/test/unit/utils-tests.js b/test/unit/utils-tests.js index 03a3d38..30ff9d2 100644 --- a/test/unit/utils-tests.js +++ b/test/unit/utils-tests.js @@ -21,144 +21,6 @@ test("EventEmitter.once", function(t) { }); -test('normalizing connection info', function() { - test('with objects', function() { - test('empty object uses defaults', function() { - var input = {}; - var output = utils.normalizeConnectionInfo(input); - assert.equal(output.user, defaults.user); - assert.equal(output.database, defaults.database); - assert.equal(output.port, defaults.port); - assert.equal(output.host, defaults.host); - assert.equal(output.password, defaults.password); - }); - - test('full object ignores defaults', function() { - var input = { - user: 'test1', - database: 'test2', - port: 'test3', - host: 'test4', - password: 'test5' - }; - assert.equal(utils.normalizeConnectionInfo(input), input); - }); - - test('connection string', function() { - test('non-unix socket', function() { - test('uses defaults', function() { - var input = ""; - var output = utils.normalizeConnectionInfo(input); - assert.equal(output.user, defaults.user); - assert.equal(output.database, defaults.database); - assert.equal(output.port, defaults.port); - assert.equal(output.host, defaults.host); - assert.equal(output.password, defaults.password); - }); - test('ignores defaults if string contains them all', function() { - var input = "tcp://user1:pass2@host3:3333/databaseName"; - var output = utils.normalizeConnectionInfo(input); - assert.equal(output.user, 'user1'); - assert.equal(output.database, 'databaseName'); - assert.equal(output.port, 3333); - assert.equal(output.host, 'host3'); - assert.equal(output.password, 'pass2'); - }) - }); - - test('unix socket', function() { - test('uses defaults', function() { - var input = "/var/run/postgresql"; - var output = utils.normalizeConnectionInfo(input); - assert.equal(output.user, process.env.USER); - assert.equal(output.host, '/var/run/postgresql'); - assert.equal(output.database, process.env.USER); - assert.equal(output.port, 5432); - }); - - test('uses overridden defaults', function() { - defaults.host = "/var/run/postgresql"; - defaults.user = "boom"; - defaults.password = "yeah"; - defaults.port = 1234; - var output = utils.normalizeConnectionInfo("asdf"); - assert.equal(output.user, "boom"); - assert.equal(output.password, "yeah"); - assert.equal(output.port, 1234); - assert.equal(output.host, "/var/run/postgresql"); - }) - }) - }) - }) -}) - -test('libpq connection string building', function() { - var checkForPart = function(array, part) { - assert.ok(array.indexOf(part) > -1, array.join(" ") + " did not contain " + part); - } - - test('builds simple string', function() { - var config = { - user: 'brian', - password: 'xyz', - port: 888, - host: 'localhost', - database: 'bam' - } - utils.buildLibpqConnectionString(config, assert.calls(function(err, constring) { - assert.isNull(err) - var parts = constring.split(" "); - checkForPart(parts, "user='brian'") - checkForPart(parts, "password='xyz'") - checkForPart(parts, "port='888'") - checkForPart(parts, "hostaddr=127.0.0.1") - checkForPart(parts, "dbname='bam'") - })) - }) - test('builds dns string', function() { - var config = { - user: 'brian', - password: 'asdf', - port: 5432, - host: 'localhost' - } - utils.buildLibpqConnectionString(config, assert.calls(function(err, constring) { - assert.isNull(err); - var parts = constring.split(" "); - checkForPart(parts, "user='brian'") - checkForPart(parts, "hostaddr=127.0.0.1") - })) - }) - - test('error when dns fails', function() { - var config = { - user: 'brian', - password: 'asf', - port: 5432, - host: 'asdlfkjasldfkksfd#!$!!!!..com' - } - utils.buildLibpqConnectionString(config, assert.calls(function(err, constring) { - assert.ok(err); - assert.isNull(constring) - })) - }) - - test('password contains < and/or > characters', function () { - return false; - var sourceConfig = { - user:'brian', - password: 'helloe', - port: 5432, - host: 'localhost', - database: 'postgres' - } - var connectionString = 'pg://' + sourceConfig.user + ':' + sourceConfig.password + '@' + sourceConfig.host + ':' + sourceConfig.port + '/' + sourceConfig.database; - var config = utils.parseConnectionString(connectionString); - assert.same(config, sourceConfig); - }); - -}) - test('types are exported', function() { var pg = require(__dirname + '/../../lib/index'); assert.ok(pg.types);