use ConnectionParameters with native bindings and remove unused util functions

This commit is contained in:
bmc 2013-01-22 23:23:47 -06:00
parent 020607c49c
commit 113b6298e2
5 changed files with 24 additions and 257 deletions

View File

@ -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);

View File

@ -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
}

View File

@ -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()) {

View File

@ -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 () {
})
);
});
}));
});
});

View File

@ -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: 'hello<ther>e',
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);