pax_global_header00006660000000000000000000000064121161135420014506gustar00rootroot0000000000000052 comment=b56248664c925af299fd95dd0318ce95ee4aad76 node-postgres-0.13.3/000077500000000000000000000000001211611354200143635ustar00rootroot00000000000000node-postgres-0.13.3/.gitignore000066400000000000000000000000571211611354200163550ustar00rootroot00000000000000node_modules/ *.swp *.log .lock-wscript build/ node-postgres-0.13.3/.jshintrc000066400000000000000000000000461211611354200162100ustar00rootroot00000000000000{ "trailing": true, "indent": 2 } node-postgres-0.13.3/.npmignore000066400000000000000000000000251211611354200163570ustar00rootroot00000000000000.lock-wscript build/ node-postgres-0.13.3/.travis.yml000066400000000000000000000001761211611354200165000ustar00rootroot00000000000000language: node_js node_js: - 0.8 before_script: - node script/create-test-tables.js pg://postgres@127.0.0.1:5432/postgres node-postgres-0.13.3/Makefile000066400000000000000000000030201211611354200160160ustar00rootroot00000000000000SHELL := /bin/bash connectionString=pg:// params := $(connectionString) node-command := xargs -n 1 -I file node file $(params) .PHONY : test test-connection test-integration bench test-native \ build/default/binding.node jshint help: @echo "make prepare-test-db [connectionString=pg://]" @echo "make test-all [connectionString=pg://]" test: test-unit test-all: jshint test-unit test-integration test-native test-binary bench: @find benchmark -name "*-bench.js" | $(node-command) build/default/binding.node: @node-gyp rebuild test-unit: @find test/unit -name "*-tests.js" | $(node-command) test-connection: @echo "***Testing connection***" @node script/test-connection.js $(params) test-connection-binary: @echo "***Testing binary connection***" @node script/test-connection.js $(params) binary test-native: build/default/binding.node @echo "***Testing native bindings***" @find test/native -name "*-tests.js" | $(node-command) @find test/integration -name "*-tests.js" | $(node-command) native test-integration: test-connection @echo "***Testing Pure Javascript***" @find test/integration -name "*-tests.js" | $(node-command) test-binary: test-connection-binary @echo "***Testing Pure Javascript (binary)***" @find test/integration -name "*-tests.js" | $(node-command) binary prepare-test-db: @echo "***Preparing the database for tests***" @find script/create-test-tables.js | $(node-command) jshint: @echo "***Starting jshint***" @./node_modules/.bin/jshint lib node-postgres-0.13.3/README.md000066400000000000000000000135651211611354200156540ustar00rootroot00000000000000#node-postgres [![Build Status](https://secure.travis-ci.org/brianc/node-postgres.png)](http://travis-ci.org/brianc/node-postgres) PostgreSQL client for node.js. Pure JavaScript and native libpq bindings. ## Installation npm install pg ## Examples ### Callbacks ```javascript var pg = require('pg'); //or native libpq bindings //var pg = require('pg').native var conString = "tcp://postgres:1234@localhost/postgres"; //note: error handling omitted var client = new pg.Client(conString); client.connect(function(err) { client.query('SELECT NOW() AS "theTime"', function(err, result) { console.log(result.rows[0].theTime); //output: Tue Jan 15 2013 19:12:47 GMT-600 (CST) }) }); ``` ### Events ```javascript var pg = require('pg'); //native libpq bindings = `var pg = require('pg').native` var conString = "tcp://postgres:1234@localhost/postgres"; var client = new pg.Client(conString); client.connect(); //queries are queued and executed one after another once the connection becomes available client.query("CREATE TEMP TABLE beatles(name varchar(10), height integer, birthday timestamptz)"); client.query("INSERT INTO beatles(name, height, birthday) values($1, $2, $3)", ['John', 68, new Date(1944, 10, 13)]); var query = client.query("SELECT * FROM beatles WHERE name = $1", ['John']); //can stream row results back 1 at a time query.on('row', function(row) { console.log(row); console.log("Beatle name: %s", row.name); //Beatle name: John console.log("Beatle birth year: %d", row.birthday.getYear()); //dates are returned as javascript dates console.log("Beatle height: %d' %d\"", Math.floor(row.height/12), row.height%12); //integers are returned as javascript ints }); //fired after last row is emitted query.on('end', function() { client.end(); }); ``` ### Example notes node-postgres supports both an 'event emitter' style API and a 'callback' style. The callback style is more concise and generally preferred, but the evented API can come in handy when you want to handle row events as they come in. They can be mixed and matched. The only events which do __not__ fire when callbacks are supplied are the `error` events, as they are to be handled within the callback function. All examples will work with the pure javascript bindings or the libpq native (c/c++) bindings To use native libpq bindings replace `require('pg')` with `require('pg').native`. The two share the same interface so __no other code changes should be required__. If you find yourself having to change code other than the require statement when switching from `pg` to `pg.native`, please report an issue. ### Features * pure javascript client and native libpq bindings share _the same api_ * row-by-row result streaming * responsive project maintainer * supported PostgreSQL features * parameterized queries * named statements with query plan caching * async notifications with `LISTEN/NOTIFY` * bulk import & export with `COPY TO/COPY FROM` * extensible js<->postgresql data-type coercion ## Documentation Documentation is a work in progress primarily taking place on the github WIKI ### [Documentation](https://github.com/brianc/node-postgres/wiki) ### __PLEASE__ check out the WIKI If you have a question, post it to the FAQ section of the WIKI so everyone can read the answer ## Production Use * [yammer.com](http://www.yammer.com) * [bayt.com](http://bayt.com) * [bitfloor.com](https://bitfloor.com) * [Vendly](http://www.vend.ly) * [SaferAging](http://www.saferaging.com) _if you use node-postgres in production and would like your site listed here, fork & add it_ ## Help If you need help or run into _any_ issues getting node-postgres to work on your system please report a bug or contact me directly. I am usually available via google-talk at my github account public email address. ## Contributing __I love contributions.__ You are welcome contribute via pull requests. If you need help getting the tests running locally feel free to email me or gchat me. I will __happily__ accept your pull request if it: - _has tests_ - looks reasonable - does not break backwards compatibility - satisfies jshint Information about the testing processes is in the [wiki](https://github.com/brianc/node-postgres/wiki/Testing). If you need help or have questions about constructing a pull request I'll be glad to help out as well. ## Support If at all possible when you open an issue please provide - version of node - version of postgres - smallest possible snippet of code to reproduce the problem Usually I'll pop the code into the repo as a test. Hopefully the test fails. Then I make the test pass. Then everyone's happy! ## Extras node-postgres is by design _low level_ with the bare minimum of abstraction. These might help out: - https://github.com/grncdr/node-any-db - https://github.com/brianc/node-sql ## License Copyright (c) 2010 Brian Carlson (brian.m.carlson@gmail.com) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. node-postgres-0.13.3/binding.gyp000066400000000000000000000011571211611354200165220ustar00rootroot00000000000000{ 'targets': [ { 'target_name': 'binding', 'sources': [ 'src/binding.cc' ], 'conditions' : [ ['OS=="win"', { 'include_dirs': [' 0) { this._drainPaused++; } else { this.emit('drain'); } } } }; Client.prototype._copy = function (text, stream) { var config = {}; config.text = text; config.stream = stream; config.callback = function (error) { if(error) { config.stream.error(error); } else { config.stream.close(); } }; var query = new Query(config); this.queryQueue.push(query); this._pulseQueryQueue(); return config.stream; }; Client.prototype.copyFrom = function (text) { return this._copy(text, new CopyFromStream()); }; Client.prototype.copyTo = function (text) { return this._copy(text, new CopyToStream()); }; Client.prototype.query = function(config, values, callback) { //can take in strings, config object or query object var query = (config instanceof Query) ? config : new Query(config, values, callback); if(this.binary && !query.binary) { query.binary = true; } this.queryQueue.push(query); this._pulseQueryQueue(); return query; }; //prevents client from otherwise emitting 'drain' event until 'resumeDrain' is //called Client.prototype.pauseDrain = function() { this._drainPaused = 1; }; //resume raising 'drain' event Client.prototype.resumeDrain = function() { if(this._drainPaused > 1) { this.emit('drain'); } this._drainPaused = 0; }; Client.prototype.end = function() { this.connection.end(); }; Client.md5 = function(string) { return crypto.createHash('md5').update(string).digest('hex'); }; // expose a Query constructor Client.Query = Query; module.exports = Client; node-postgres-0.13.3/lib/connection-parameters.js000066400000000000000000000037701211611354200217760ustar00rootroot00000000000000var dns = require('dns'); var path = require('path'); var defaults = require(__dirname + '/defaults'); var val = function(key, config) { return config[key] || process.env['PG' + key.toUpperCase()] || defaults[key]; }; var url = require('url'); //parses a connection string var parse = function(str) { //unix socket if(str.charAt(0) === '/') { return { host: str }; } // url parse expects spaces encoded as %20 str = encodeURI(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; }; var ConnectionParameters = function(config) { config = typeof config == 'string' ? parse(config) : (config || {}); this.user = val('user', config); this.database = val('database', config); this.port = parseInt(val('port', config), 10); this.host = val('host', config); this.password = val('password', config); this.binary = val('binary', config); this.ssl = config.ssl || defaults.ssl; //a domain socket begins with '/' this.isDomainSocket = (!(this.host||'').indexOf('/')); }; var add = function(params, config, paramName) { var value = config[paramName]; if(value) { params.push(paramName+"='"+value+"'"); } }; ConnectionParameters.prototype.getLibpqConnectionString = function(cb) { var params = []; add(params, this, 'user'); add(params, this, 'password'); add(params, this, 'port'); if(this.database) { params.push("dbname='" + this.database + "'"); } if(this.isDomainSocket) { params.push("host=" + this.host); return cb(null, params.join(' ')); } params.push("options=--client_encoding='utf-8'"); dns.lookup(this.host, function(err, address) { if(err) return cb(err, null); params.push("hostaddr=" + address); return cb(null, params.join(' ')); }); }; module.exports = ConnectionParameters; node-postgres-0.13.3/lib/connection.js000066400000000000000000000347261211611354200176420ustar00rootroot00000000000000var net = require('net'); var crypto = require('crypto'); var EventEmitter = require('events').EventEmitter; var util = require('util'); var utils = require(__dirname + '/utils'); var Writer = require(__dirname + '/writer'); var Connection = function(config) { EventEmitter.call(this); config = config || {}; this.stream = config.stream || new net.Stream(); this.lastBuffer = false; this.lastOffset = 0; this.buffer = null; this.offset = null; this.encoding = 'utf8'; this.parsedStatements = {}; this.writer = new Writer(); this.ssl = config.ssl || false; }; util.inherits(Connection, EventEmitter); Connection.prototype.connect = function(port, host) { if(this.stream.readyState === 'closed') { this.stream.connect(port, host); } else if(this.stream.readyState == 'open') { this.emit('connect'); } var self = this; this.stream.on('connect', function() { self.emit('connect'); }); this.stream.on('error', function(error) { self.emit('error', error); }); if(this.ssl) { this.stream.once('data', function(buffer) { self.setBuffer(buffer); var msg = self.readSslResponse(); self.emit('message', msg); self.emit(msg.name, msg); }); this.once('sslresponse', function(msg) { if(msg.text == 0x53) { var tls = require('tls'); self.stream.removeAllListeners(); self.stream = tls.connect({ socket: self.stream, servername: host, rejectUnauthorized: self.ssl.rejectUnauthorized, ca: self.ssl.ca, pfx: self.ssl.pfx, key: self.ssl.key, passphrase: self.ssl.passphrase, cert: self.ssl.cert, NPNProtocols: self.ssl.NPNProtocols }); self.attachListeners(self.stream); self.emit('sslconnect'); } else { self.emit( 'error', new Error("The server doesn't support SSL/TLS connections.") ); } }); } else { this.attachListeners(this.stream); } }; Connection.prototype.attachListeners = function(stream) { var self = this; stream.on('data', function(buffer) { self.setBuffer(buffer); var msg = self.parseMessage(); while(msg) { self.emit('message', msg); self.emit(msg.name, msg); msg = self.parseMessage(); } }); }; Connection.prototype.requestSsl = function(config) { this.checkSslResponse = true; var bodyBuffer = this.writer .addInt16(0x04D2) .addInt16(0x162F).flush(); var length = bodyBuffer.length + 4; var buffer = new Writer() .addInt32(length) .add(bodyBuffer) .join(); this.stream.write(buffer); }; Connection.prototype.startup = function(config) { var bodyBuffer = this.writer .addInt16(3) .addInt16(0) .addCString('user') .addCString(config.user) .addCString('database') .addCString(config.database) .addCString('client_encoding') .addCString("'utf-8'") .addCString('').flush(); //this message is sent without a code var length = bodyBuffer.length + 4; var buffer = new Writer() .addInt32(length) .add(bodyBuffer) .join(); this.stream.write(buffer); }; Connection.prototype.cancel = function(processID, secretKey) { var bodyBuffer = this.writer .addInt16(1234) .addInt16(5678) .addInt32(processID) .addInt32(secretKey) .addCString('').flush(); var length = bodyBuffer.length + 4; var buffer = new Writer() .addInt32(length) .add(bodyBuffer) .join(); this.stream.write(buffer); }; Connection.prototype.password = function(password) { //0x70 = 'p' this._send(0x70, this.writer.addCString(password)); }; Connection.prototype._send = function(code, more) { if(!this.stream.writable) { return false; } if(more === true) { this.writer.addHeader(code); } else { return this.stream.write(this.writer.flush(code)); } }; Connection.prototype.query = function(text) { //0x51 = Q this.stream.write(this.writer.addCString(text).flush(0x51)); }; //send parse message //"more" === true to buffer the message until flush() is called Connection.prototype.parse = function(query, more) { //expect something like this: // { name: 'queryName', // text: 'select * from blah', // types: ['int8', 'bool'] } //normalize missing query names to allow for null query.name = query.name || ''; //normalize null type array query.types = query.types || []; var len = query.types.length; var buffer = this.writer .addCString(query.name) //name of query .addCString(query.text) //actual query text .addInt16(len); for(var i = 0; i < len; i++) { buffer.addInt32(query.types[i]); } var code = 0x50; this._send(code, more); }; //send bind message //"more" === true to buffer the message until flush() is called Connection.prototype.bind = function(config, more) { //normalize config config = config || {}; config.portal = config.portal || ''; config.statement = config.statement || ''; config.binary = config.binary || false; var values = config.values || []; var len = values.length; var buffer = this.writer .addCString(config.portal) .addCString(config.statement) .addInt16(0) //always use default text format .addInt16(len); //number of parameters for(var i = 0; i < len; i++) { var val = values[i]; if(val === null || typeof val === "undefined") { buffer.addInt32(-1); } else { buffer.addInt32(Buffer.byteLength(val)); buffer.addString(val); } } if(config.binary) { buffer.addInt16(1); // format codes to use binary buffer.addInt16(1); } else { buffer.addInt16(0); // format codes to use text } //0x42 = 'B' this._send(0x42, more); }; //send execute message //"more" === true to buffer the message until flush() is called Connection.prototype.execute = function(config, more) { config = config || {}; config.portal = config.portal || ''; config.rows = config.rows || ''; var buffer = this.writer .addCString(config.portal) .addInt32(config.rows); //0x45 = 'E' this._send(0x45, more); }; var emptyBuffer = Buffer(0); Connection.prototype.flush = function() { //0x48 = 'H' this.writer.add(emptyBuffer); this._send(0x48); }; Connection.prototype.sync = function() { //clear out any pending data in the writer this.writer.flush(0); this.writer.add(emptyBuffer); this._send(0x53); }; Connection.prototype.end = function() { //0x58 = 'X' this.writer.add(emptyBuffer); this._send(0x58); }; Connection.prototype.describe = function(msg, more) { this.writer.addCString(msg.type + (msg.name || '')); this._send(0x44, more); }; Connection.prototype.sendCopyFromChunk = function (chunk) { this.stream.write(this.writer.add(chunk).flush(0x64)); }; Connection.prototype.endCopyFrom = function () { this.stream.write(this.writer.add(emptyBuffer).flush(0x63)); }; Connection.prototype.sendCopyFail = function (msg) { //this.stream.write(this.writer.add(emptyBuffer).flush(0x66)); this.writer.addCString(msg); this._send(0x66); }; //parsing methods Connection.prototype.setBuffer = function(buffer) { if(this.lastBuffer) { //we have unfinished biznaz //need to combine last two buffers var remaining = this.lastBuffer.length - this.lastOffset; var combinedBuffer = new Buffer(buffer.length + remaining); this.lastBuffer.copy(combinedBuffer, 0, this.lastOffset); buffer.copy(combinedBuffer, remaining, 0); buffer = combinedBuffer; } this.buffer = buffer; this.offset = 0; }; Connection.prototype.readSslResponse = function() { var remaining = this.buffer.length - (this.offset); if(remaining < 1) { this.lastBuffer = this.buffer; this.lastOffset = this.offset; return false; } return { name: 'sslresponse', text: this.buffer[this.offset++] }; }; Connection.prototype.parseMessage = function() { var remaining = this.buffer.length - (this.offset); if(remaining < 5) { //cannot read id + length without at least 5 bytes //just abort the read now this.lastBuffer = this.buffer; this.lastOffset = this.offset; return false; } //read message id code var id = this.buffer[this.offset++]; //read message length var length = this.parseInt32(); if(remaining <= length) { this.lastBuffer = this.buffer; //rewind the last 5 bytes we read this.lastOffset = this.offset-5; return false; } var msg = { length: length }; switch(id) { case 0x52: //R msg.name = 'authenticationOk'; return this.parseR(msg); case 0x53: //S msg.name = 'parameterStatus'; return this.parseS(msg); case 0x4b: //K msg.name = 'backendKeyData'; return this.parseK(msg); case 0x43: //C msg.name = 'commandComplete'; return this.parseC(msg); case 0x5a: //Z msg.name = 'readyForQuery'; return this.parseZ(msg); case 0x54: //T msg.name = 'rowDescription'; return this.parseT(msg); case 0x44: //D msg.name = 'dataRow'; return this.parseD(msg); case 0x45: //E msg.name = 'error'; return this.parseE(msg); case 0x4e: //N msg.name = 'notice'; return this.parseN(msg); case 0x31: //1 msg.name = 'parseComplete'; return msg; case 0x32: //2 msg.name = 'bindComplete'; return msg; case 0x41: //A msg.name = 'notification'; return this.parseA(msg); case 0x6e: //n msg.name = 'noData'; return msg; case 0x49: //I msg.name = 'emptyQuery'; return msg; case 0x73: //s msg.name = 'portalSuspended'; return msg; case 0x47: //G msg.name = 'copyInResponse'; return this.parseGH(msg); case 0x48: //H msg.name = 'copyOutResponse'; return this.parseGH(msg); case 0x63: //c msg.name = 'copyDone'; return msg; case 0x64: //d msg.name = 'copyData'; return this.parsed(msg); default: throw new Error("Unrecognized message code " + id); } }; Connection.prototype.parseR = function(msg) { var code = 0; if(msg.length === 8) { code = this.parseInt32(); if(code === 3) { msg.name = 'authenticationCleartextPassword'; } return msg; } if(msg.length === 12) { code = this.parseInt32(); if(code === 5) { //md5 required msg.name = 'authenticationMD5Password'; msg.salt = new Buffer(4); this.buffer.copy(msg.salt, 0, this.offset, this.offset + 4); this.offset += 4; return msg; } } throw new Error("Unknown authenticatinOk message type" + util.inspect(msg)); }; Connection.prototype.parseS = function(msg) { msg.parameterName = this.parseCString(); msg.parameterValue = this.parseCString(); return msg; }; Connection.prototype.parseK = function(msg) { msg.processID = this.parseInt32(); msg.secretKey = this.parseInt32(); return msg; }; Connection.prototype.parseC = function(msg) { msg.text = this.parseCString(); return msg; }; Connection.prototype.parseZ = function(msg) { msg.status = this.readChar(); return msg; }; Connection.prototype.parseT = function(msg) { msg.fieldCount = this.parseInt16(); var fields = []; for(var i = 0; i < msg.fieldCount; i++){ fields[i] = this.parseField(); } msg.fields = fields; return msg; }; Connection.prototype.parseField = function() { var field = { name: this.parseCString(), tableID: this.parseInt32(), columnID: this.parseInt16(), dataTypeID: this.parseInt32(), dataTypeSize: this.parseInt16(), dataTypeModifier: this.parseInt32(), format: this.parseInt16() === 0 ? 'text' : 'binary' }; return field; }; Connection.prototype.parseD = function(msg) { var fieldCount = this.parseInt16(); var fields = []; for(var i = 0; i < fieldCount; i++) { var length = this.parseInt32(); fields[i] = (length === -1 ? null : this.readBytes(length)); } msg.fieldCount = fieldCount; msg.fields = fields; return msg; }; //parses error Connection.prototype.parseE = function(input) { var fields = {}; var msg, item; var fieldType = this.readString(1); while(fieldType != '\0') { fields[fieldType] = this.parseCString(); fieldType = this.readString(1); } if(input.name === 'error') { // the msg is an Error instance msg = new Error(fields.M); for (item in input) { // copy input properties to the error if(input.hasOwnProperty(item)) { msg[item] = input[item]; } } } else { // the msg is an object literal msg = input; msg.message = fields.M; } msg.severity = fields.S; msg.code = fields.C; msg.detail = fields.D; msg.hint = fields.H; msg.position = fields.P; msg.internalPosition = fields.p; msg.internalQuery = fields.q; msg.where = fields.W; msg.file = fields.F; msg.line = fields.L; msg.routine = fields.R; return msg; }; //same thing, different name Connection.prototype.parseN = Connection.prototype.parseE; Connection.prototype.parseA = function(msg) { msg.processId = this.parseInt32(); msg.channel = this.parseCString(); msg.payload = this.parseCString(); return msg; }; Connection.prototype.parseGH = function (msg) { msg.binary = Boolean(this.parseInt8()); var columnCount = this.parseInt16(); msg.columnTypes = []; for(var i = 0; i 1) { this.emit('drain'); } this._drainPaused = 0; }; Connection.prototype.sendCopyFail = function(msg) { this.endCopyFrom(msg); }; var clientBuilder = function(config) { config = config || {}; var connection = new Connection(); connection._queryQueue = []; connection._namedQueries = {}; connection._activeQuery = null; connection.connectionParameters = new ConnectionParameters(config); //attach properties to normalize interface with pure js client 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); }); //proxy some events to active query connection.on('_row', function(row) { connection._activeQuery.handleRow(row); }); connection.on('_cmdStatus', function(status) { //set this here so we can pass it to the query //when the query completes connection._lastMeta = status; }); //TODO: emit more native error properties (make it match js error) connection.on('_error', function(err) { //create Error object from object literal var error = new Error(err.message || "Unknown native driver error"); for(var key in err) { error[key] = err[key]; } //give up on trying to wait for named query prepare this._namedQuery = false; if(connection._activeQuery) { connection._activeQuery.handleError(error); } else { connection.emit('error', error); } }); connection.on('_readyForQuery', function() { var q = this._activeQuery; //a named query finished being prepared if(this._namedQuery) { this._namedQuery = false; this._sendQueryPrepared(q.name, q.values||[]); } else { connection._activeQuery.handleReadyForQuery(connection._lastMeta); connection._activeQuery = null; connection._pulseQueryQueue(); } }); connection.on('copyInResponse', function () { //connection is ready to accept chunks //start to send data from stream connection._activeQuery.streamData(connection); }); connection.on('copyOutResponse', function(msg) { if (connection._activeQuery.stream === undefined) { connection._activeQuery._canceledDueToError = new Error('No destination stream defined'); (new clientBuilder({port: connection.port, host: connection.host})).cancel(connection, connection._activeQuery); } }); connection.on('copyData', function (chunk) { //recieve chunk from connection //move it to stream connection._activeQuery.handleCopyFromChunk(chunk); }); return connection; }; // expose a Query constructor clientBuilder.Query = NativeQuery; module.exports = clientBuilder; node-postgres-0.13.3/lib/native/query.js000066400000000000000000000050261211611354200201250ustar00rootroot00000000000000var EventEmitter = require('events').EventEmitter; var util = require('util'); var types = require(__dirname + '/../types/'); var utils = require(__dirname + '/../utils'); var Result = require(__dirname + '/../result'); //event emitter proxy var NativeQuery = function(config, values, callback) { // use of "new" optional if (!(this instanceof NativeQuery)) { return new NativeQuery(config, values, callback); } EventEmitter.call(this); var c = utils.normalizeQueryConfig(config, values, callback); this.name = c.name; this.text = c.text; this.values = c.values; this.callback = c.callback; this._result = new Result(); //normalize values if(this.values) { for(var i = 0, len = this.values.length; i < len; i++) { this.values[i] = utils.prepareValue(this.values[i]); } } this._canceledDueToError = false; }; util.inherits(NativeQuery, EventEmitter); //maps from native rowdata into api compatible row object var mapRowData = function(row) { var result = {}; for(var i = 0, len = row.length; i < len; i++) { var item = row[i]; result[item.name] = item.value === null ? null : types.getTypeParser(item.type, 'text')(item.value); } return result; }; NativeQuery.prototype.handleRow = function(rowData) { var row = mapRowData(rowData); if(this.callback) { this._result.addRow(row); } this.emit('row', row, this._result); }; NativeQuery.prototype.handleError = function(error) { if (this._canceledDueToError) { error = this._canceledDueToError; this._canceledDueToError = false; } if(this.callback) { this.callback(error); this.callback = null; } else { this.emit('error', error); } }; NativeQuery.prototype.handleReadyForQuery = function(meta) { if (this._canceledDueToError) { return this.handleError(this._canceledDueToError); } if(meta) { this._result.addCommandComplete(meta); } if(this.callback) { this.callback(null, this._result); } this.emit('end', this._result); }; NativeQuery.prototype.streamData = function (connection) { if(this.stream) { this.stream.startStreamingToConnection(connection); } else { connection.sendCopyFail('No source stream defined'); } }; NativeQuery.prototype.handleCopyFromChunk = function (chunk) { if(this.stream) { this.stream.handleChunk(chunk); } //if there are no stream (for example when copy to query was sent by //query method instead of copyTo) error will be handled //on copyOutResponse event, so silently ignore this error here }; module.exports = NativeQuery; node-postgres-0.13.3/lib/pool.js000066400000000000000000000062401211611354200164420ustar00rootroot00000000000000var EventEmitter = require('events').EventEmitter; var defaults = require(__dirname + '/defaults'); var genericPool = require('generic-pool'); var pools = { //dictionary of all key:pool pairs all: {}, //reference to the client constructor - can override in tests or for require('pg').native Client: require(__dirname + '/client'), getOrCreate: function(clientConfig) { clientConfig = clientConfig || {}; var name = JSON.stringify(clientConfig); var pool = pools.all[name]; if(pool) { return pool; } pool = genericPool.Pool({ name: name, max: defaults.poolSize, idleTimeoutMillis: defaults.poolIdleTimeout, reapIntervalMillis: defaults.reapIntervalMillis, log: defaults.poolLog, create: function(cb) { var client = new pools.Client(clientConfig); client.connect(function(err) { if(err) return cb(err, null); //handle connected client background errors by emitting event //via the pg object and then removing errored client from the pool client.on('error', function(e) { pool.emit('error', e, client); pool.destroy(client); }); return cb(null, client); }); }, destroy: function(client) { client.end(); } }); pools.all[name] = pool; //mixin EventEmitter to pool EventEmitter.call(pool); for(var key in EventEmitter.prototype) { if(EventEmitter.prototype.hasOwnProperty(key)) { pool[key] = EventEmitter.prototype[key]; } } //monkey-patch with connect method pool.connect = function(cb) { pool.acquire(function(err, client) { if(err) return cb(err, null, function() {/*NOOP*/}); //support both 2 (old) and 3 arguments (cb.length > 2 ? newConnect : oldConnect)(pool, client, cb); }); }; return pool; } }; //the old connect method of the pool //would automatically subscribe to the 'drain' //event and automatically return the client to //the pool once 'drain' fired once. This caused //a bunch of problems, but for backwards compatibility //we're leaving it in var alarmDuration = 5000; var errorMessage = [ 'A client has been checked out from the pool for longer than ' + alarmDuration + ' ms.', 'You might have a leak!', 'You should use the following new way to check out clients','pg.connect(function(err, client, done)) {', ' //do something', ' done(); //call done() to signal you are finished with the client', '}' ].join(require('os').EOL); var oldConnect = function(pool, client, cb) { var tid = setTimeout(function() { console.error(errorMessage); }, alarmDuration); var onError = function() { clearTimeout(tid); client.removeListener('drain', release); }; var release = function() { clearTimeout(tid); pool.release(client); client.removeListener('error', onError); }; client.once('drain', release); client.once('error', onError); cb(null, client); }; var newConnect = function(pool, client, cb) { cb(null, client, function(err) { if(err) { pool.destroy(client); } else { pool.release(client); } }); }; module.exports = pools; node-postgres-0.13.3/lib/query.js000066400000000000000000000123461211611354200166420ustar00rootroot00000000000000var EventEmitter = require('events').EventEmitter; var util = require('util'); var Result = require(__dirname + '/result'); var Types = require(__dirname + '/types/'); var utils = require(__dirname + '/utils'); var Query = function(config, values, callback) { // use of "new" optional if(!(this instanceof Query)) { return new Query(config, values, callback); } config = utils.normalizeQueryConfig(config, values, callback); this.text = config.text; this.values = config.values; this.rows = config.rows; this.types = config.types; this.name = config.name; this.binary = config.binary; this.stream = config.stream; //use unique portal name each time this.portal = config.portal || ""; this.callback = config.callback; this._fieldNames = []; this._fieldConverters = []; this._result = new Result(); this.isPreparedStatement = false; this._canceledDueToError = false; EventEmitter.call(this); }; util.inherits(Query, EventEmitter); Query.prototype.requiresPreparation = function() { //named queries must always be prepared if(this.name) { return true; } //always prepare if there are max number of rows expected per //portal execution if(this.rows) { return true; } //don't prepare empty text queries if(!this.text) { return false; } //binary should be prepared to specify results should be in binary //unless there are no parameters if(this.binary && !this.values) { return false; } //prepare if there are values return (this.values || 0).length > 0; }; var noParse = function(val) { return val; }; //associates row metadata from the supplied //message with this query object //metadata used when parsing row results Query.prototype.handleRowDescription = function(msg) { this._fieldNames = []; this._fieldConverters = []; var len = msg.fields.length; for(var i = 0; i < len; i++) { var field = msg.fields[i]; var format = field.format; this._fieldNames[i] = field.name; this._fieldConverters[i] = Types.getTypeParser(field.dataTypeID, format); } }; Query.prototype.handleDataRow = function(msg) { var self = this; var row = {}; for(var i = 0; i < msg.fields.length; i++) { var rawValue = msg.fields[i]; if(rawValue === null) { //leave null values alone row[self._fieldNames[i]] = null; } else { //convert value to javascript row[self._fieldNames[i]] = self._fieldConverters[i](rawValue); } } self.emit('row', row, self._result); //if there is a callback collect rows if(self.callback) { self._result.addRow(row); } }; Query.prototype.handleCommandComplete = function(msg) { this._result.addCommandComplete(msg); }; Query.prototype.handleReadyForQuery = function() { if(this._canceledDueToError) { return this.handleError(this._canceledDueToError); } if(this.callback) { this.callback(null, this._result); } this.emit('end', this._result); }; Query.prototype.handleError = function(err) { if(this._canceledDueToError) { err = this._canceledDueToError; this._canceledDueToError = false; } //if callback supplied do not emit error event as uncaught error //events will bubble up to node process if(this.callback) { this.callback(err); } else { this.emit('error', err); } this.emit('end'); }; Query.prototype.submit = function(connection) { var self = this; if(this.requiresPreparation()) { this.prepare(connection); } else { connection.query(this.text); } }; Query.prototype.hasBeenParsed = function(connection) { return this.name && connection.parsedStatements[this.name]; }; Query.prototype.getRows = function(connection) { connection.execute({ portal: this.portalName, rows: this.rows }, true); connection.flush(); }; Query.prototype.prepare = function(connection) { var self = this; //prepared statements need sync to be called after each command //complete or when an error is encountered this.isPreparedStatement = true; //TODO refactor this poor encapsulation if(!this.hasBeenParsed(connection)) { connection.parse({ text: self.text, name: self.name, types: self.types }, true); if(this.name) { connection.parsedStatements[this.name] = true; } } //TODO is there some better way to prepare values for the database? if(self.values) { for(var i = 0, len = self.values.length; i < len; i++) { self.values[i] = utils.prepareValue(self.values[i]); } } //http://developer.postgresql.org/pgdocs/postgres/protocol-flow.html#PROTOCOL-FLOW-EXT-QUERY connection.bind({ portal: self.portalName, statement: self.name, values: self.values, binary: self.binary }, true); connection.describe({ type: 'P', name: self.portalName || "" }, true); this.getRows(connection); }; Query.prototype.streamData = function (connection) { if(this.stream) this.stream.startStreamingToConnection(connection); else connection.sendCopyFail('No source stream defined'); }; Query.prototype.handleCopyFromChunk = function (chunk) { if(this.stream) { this.stream.handleChunk(chunk); } //if there are no stream (for example when copy to query was sent by //query method instead of copyTo) error will be handled //on copyOutResponse event, so silently ignore this error here }; module.exports = Query; node-postgres-0.13.3/lib/result.js000066400000000000000000000017031211611354200170060ustar00rootroot00000000000000//result object returned from query //in the 'end' event and also //passed as second argument to provided callback var Result = function() { this.command = null; this.rowCount = null; this.oid = null; this.rows = []; }; var matchRegexp = /([A-Za-z]+) (\d+ )?(\d+)?/; //adds a command complete message Result.prototype.addCommandComplete = function(msg) { var match; if(msg.text) { //pure javascript match = matchRegexp.exec(msg.text); } else { //native bindings match = matchRegexp.exec(msg.command); } if(match) { this.command = match[1]; //match 3 will only be existing on insert commands if(match[3]) { //msg.value is from native bindings this.rowCount = parseInt(match[3] || msg.value, 10); this.oid = parseInt(match[2], 10); } else { this.rowCount = parseInt(match[2], 10); } } }; Result.prototype.addRow = function(row) { this.rows.push(row); }; module.exports = Result; node-postgres-0.13.3/lib/types/000077500000000000000000000000001211611354200162755ustar00rootroot00000000000000node-postgres-0.13.3/lib/types/arrayParser.js000066400000000000000000000041401211611354200211250ustar00rootroot00000000000000function ArrayParser(source, converter) { this.source = source; this.converter = converter; this.pos = 0; this.entries = []; this.recorded = []; this.dimension = 0; if (!this.converter) { this.converter = function(entry) { return entry; }; } } ArrayParser.prototype.eof = function() { return this.pos >= this.source.length; }; ArrayParser.prototype.nextChar = function() { var c; if ((c = this.source[this.pos++]) === "\\") { return { char: this.source[this.pos++], escaped: true }; } else { return { char: c, escaped: false }; } }; ArrayParser.prototype.record = function(c) { return this.recorded.push(c); }; ArrayParser.prototype.newEntry = function(includeEmpty) { var entry; if (this.recorded.length > 0 || includeEmpty) { entry = this.recorded.join(""); if (entry === "NULL" && !includeEmpty) { entry = null; } if (entry !== null) { entry = this.converter(entry); } this.entries.push(entry); this.recorded = []; } }; ArrayParser.prototype.parse = function(nested) { var c, p, quote; if (nested === null) { nested = false; } quote = false; while (!this.eof()) { c = this.nextChar(); if (c.char === "{" && !quote) { this.dimension++; if (this.dimension > 1) { p = new ArrayParser(this.source.substr(this.pos - 1), this.converter); this.entries.push(p.parse(true)); this.pos += p.pos - 2; } } else if (c.char === "}" && !quote) { this.dimension--; if (this.dimension === 0) { this.newEntry(); if (nested) { return this.entries; } } } else if (c.char === '"' && !c.escaped) { if (quote) { this.newEntry(true); } quote = !quote; } else if (c.char === ',' && !quote) { this.newEntry(); } else { this.record(c.char); } } if (this.dimension !== 0) { throw "array dimension not balanced"; } return this.entries; }; module.exports = { create: function(source, converter){ return new ArrayParser(source, converter); } }; node-postgres-0.13.3/lib/types/binaryParsers.js000066400000000000000000000136571211611354200214730ustar00rootroot00000000000000var parseBits = function(data, bits, offset, invert, callback) { offset = offset || 0; invert = invert || false; callback = callback || function(lastValue, newValue, bits) { return (lastValue * Math.pow(2, bits)) + newValue; }; var offsetBytes = offset >> 3; var inv = function(value) { if (invert) { return ~value & 0xff; } return value; }; // read first (maybe partial) byte var mask = 0xff; var firstBits = 8 - (offset % 8); if (bits < firstBits) { mask = (0xff << (8 - bits)) & 0xff; firstBits = bits; } if (offset) { mask = mask >> (offset % 8); } var result = 0; if ((offset % 8) + bits >= 8) { result = callback(0, inv(data[offsetBytes]) & mask, firstBits); } // read bytes var bytes = (bits + offset) >> 3; for (var i = offsetBytes + 1; i < bytes; i++) { result = callback(result, inv(data[i]), 8); } // bits to read, that are not a complete byte var lastBits = (bits + offset) % 8; if (lastBits > 0) { result = callback(result, inv(data[bytes]) >> (8 - lastBits), lastBits); } return result; }; var parseFloatFromBits = function(data, precisionBits, exponentBits) { var bias = Math.pow(2, exponentBits - 1) - 1; var sign = parseBits(data, 1); var exponent = parseBits(data, exponentBits, 1); if (exponent === 0) { return 0; } // parse mantissa var precisionBitsCounter = 1; var parsePrecisionBits = function(lastValue, newValue, bits) { if (lastValue === 0) { lastValue = 1; } for (var i = 1; i <= bits; i++) { precisionBitsCounter /= 2; if ((newValue & (0x1 << (bits - i))) > 0) { lastValue += precisionBitsCounter; } } return lastValue; }; var mantissa = parseBits(data, precisionBits, exponentBits + 1, false, parsePrecisionBits); // special cases if (exponent == (Math.pow(2, exponentBits + 1) - 1)) { if (mantissa === 0) { return (sign === 0) ? Infinity : -Infinity; } return NaN; } // normale number return ((sign === 0) ? 1 : -1) * Math.pow(2, exponent - bias) * mantissa; }; var parseBool = function(value) { return (parseBits(value, 8) == 1); }; var parseInt16 = function(value) { if (parseBits(value, 1) == 1) { return -1 * (parseBits(value, 15, 1, true) + 1); } return parseBits(value, 15, 1); }; var parseInt32 = function(value) { if (parseBits(value, 1) == 1) { return -1 * (parseBits(value, 31, 1, true) + 1); } return parseBits(value, 31, 1); }; var parseInt64 = function(value) { if (parseBits(value, 1) == 1) { return -1 * (parseBits(value, 63, 1, true) + 1); } return parseBits(value, 63, 1); }; var parseFloat32 = function(value) { return parseFloatFromBits(value, 23, 8); }; var parseFloat64 = function(value) { return parseFloatFromBits(value, 52, 11); }; var parseNumeric = function(value) { var sign = parseBits(value, 16, 32); if (sign == 0xc000) { return NaN; } var weight = Math.pow(10000, parseBits(value, 16, 16)); var result = 0; var digits = []; var ndigits = parseBits(value, 16); for (var i = 0; i < ndigits; i++) { result += parseBits(value, 16, 64 + (16 * i)) * weight; weight /= 10000; } var scale = Math.pow(10, parseBits(value, 16, 48)); return ((sign === 0) ? 1 : -1) * Math.round(result * scale) / scale; }; var parseDate = function(value) { var sign = parseBits(value, 1); var rawValue = parseBits(value, 63, 1); // discard usecs and shift from 2000 to 1970 var result = new Date((((sign === 0) ? 1 : -1) * rawValue / 1000) + 946684800000); // add microseconds to the date result.usec = rawValue % 1000; result.getMicroSeconds = function() { return this.usec; }; result.setMicroSeconds = function(value) { this.usec = value; }; result.getUTCMicroSeconds = function() { return this.usec; }; return result; }; var parseArray = function(value) { var dim = parseBits(value, 32); var flags = parseBits(value, 32, 32); var elementType = parseBits(value, 32, 64); var offset = 96; var dims = []; for (var i = 0; i < dim; i++) { // parse dimension dims[i] = parseBits(value, 32, offset); offset += 32; // ignore lower bounds offset += 32; } var parseElement = function(elementType) { // parse content length var length = parseBits(value, 32, offset); offset += 32; // parse null values if (length == 0xffffffff) { return null; } var result; if ((elementType == 0x17) || (elementType == 0x14)) { // int/bigint result = parseBits(value, length * 8, offset); offset += length * 8; return result; } else if (elementType == 0x19) { // string result = value.toString(this.encoding, offset >> 3, (offset += (length << 3)) >> 3); return result; } else { console.log("ERROR: ElementType not implemented: " + elementType); } }; var parse = function(dimension, elementType) { var array = []; var i; if (dimension.length > 1) { var count = dimension.shift(); for (i = 0; i < count; i++) { array[i] = parse(dimension, elementType); } dimension.unshift(count); } else { for (i = 0; i < dimension[0]; i++) { array[i] = parseElement(elementType); } } return array; }; return parse(dims, elementType); }; var parseText = function(value) { return value.toString('utf8'); }; var parseBool = function(value) { return (parseBits(value, 8) > 0); }; var init = function(register) { register(20, parseInt64); register(21, parseInt16); register(23, parseInt32); register(26, parseInt32); register(1700, parseNumeric); register(700, parseFloat32); register(701, parseFloat64); register(16, parseBool); register(1114, parseDate); register(1184, parseDate); register(1007, parseArray); register(1016, parseArray); register(1008, parseArray); register(1009, parseArray); register(25, parseText); }; module.exports = { init: init }; node-postgres-0.13.3/lib/types/index.js000066400000000000000000000017341211611354200177470ustar00rootroot00000000000000var textParsers = require(__dirname + '/textParsers'); var binaryParsers = require(__dirname + '/binaryParsers'); var typeParsers = { text: {}, binary: {} }; //the empty parse function var noParse = function(val) { return String(val); }; //returns a function used to convert a specific type (specified by //oid) into a result javascript type var getTypeParser = function(oid, format) { if (!typeParsers[format]) { return noParse; } return typeParsers[format][oid] || noParse; }; var setTypeParser = function(oid, format, parseFn) { if(typeof format == 'function') { parseFn = format; format = 'text'; } typeParsers[format][oid] = parseFn; }; textParsers.init(function(oid, converter) { typeParsers.text[oid] = function(value) { return converter(String(value)); }; }); binaryParsers.init(function(oid, converter) { typeParsers.binary[oid] = converter; }); module.exports = { getTypeParser: getTypeParser, setTypeParser: setTypeParser }; node-postgres-0.13.3/lib/types/textParsers.js000066400000000000000000000122431211611354200211610ustar00rootroot00000000000000var arrayParser = require(__dirname + "/arrayParser.js"); //parses PostgreSQL server formatted date strings into javascript date objects var parseDate = function(isoDate) { //TODO this could do w/ a refactor var dateMatcher = /(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})(\.\d{1,})?/; var match = dateMatcher.exec(isoDate); //could not parse date if(!match) { dateMatcher = /^(\d{4})-(\d{2})-(\d{2})$/; match = dateMatcher.test(isoDate); if(!match) { return null; } else { //it is a date in YYYY-MM-DD format return new Date(isoDate); } } var year = match[1]; var month = parseInt(match[2],10)-1; var day = match[3]; var hour = parseInt(match[4],10); var min = parseInt(match[5],10); var seconds = parseInt(match[6], 10); var miliString = match[7]; var mili = 0; if(miliString) { mili = 1000 * parseFloat(miliString); } var tZone = /([Z|+\-])(\d{2})?(\d{2})?/.exec(isoDate.split(' ')[1]); //minutes to adjust for timezone var tzAdjust = 0; if(tZone) { var type = tZone[1]; switch(type) { case 'Z': break; case '-': tzAdjust = -(((parseInt(tZone[2],10)*60)+(parseInt(tZone[3]||0,10)))); break; case '+': tzAdjust = (((parseInt(tZone[2],10)*60)+(parseInt(tZone[3]||0,10)))); break; default: throw new Error("Unidentifed tZone part " + type); } var utcOffset = Date.UTC(year, month, day, hour, min, seconds, mili); return new Date(utcOffset - (tzAdjust * 60* 1000)); } else { return new Date(year, month, day, hour, min, seconds, mili); } }; var parseBool = function(val) { return val === 't'; }; var parseIntegerArray = function(val) { if(!val) { return null; } var p = arrayParser.create(val, function(entry){ if(entry !== null) { entry = parseInt(entry, 10); } return entry; }); return p.parse(); }; var parseFloatArray = function(val) { if(!val) { return null; } var p = arrayParser.create(val, function(entry){ if(entry !== null) { entry = parseFloat(entry, 10); } return entry; }); return p.parse(); }; var parseStringArray = function(val) { if(!val) { return null; } var p = arrayParser.create(val); return p.parse(); }; var NUM = '([+-]?\\d+)'; var YEAR = NUM + '\\s+years?'; var MON = NUM + '\\s+mons?'; var DAY = NUM + '\\s+days?'; var TIME = '([+-])?(\\d\\d):(\\d\\d):(\\d\\d)'; var INTERVAL = [YEAR,MON,DAY,TIME].map(function(p){ return "("+p+")?"; }).join('\\s*'); var parseInterval = function(val) { if (!val) { return {}; } var m = new RegExp(INTERVAL).exec(val); var i = {}; if (m[2]) { i.years = parseInt(m[2], 10); } if (m[4]) { i.months = parseInt(m[4], 10); } if (m[6]) { i.days = parseInt(m[6], 10); } if (m[9]) { i.hours = parseInt(m[9], 10); } if (m[10]) { i.minutes = parseInt(m[10], 10); } if (m[11]) { i.seconds = parseInt(m[11], 10); } if (m[8] == '-'){ if (i.hours) { i.hours *= -1; } if (i.minutes) { i.minutes *= -1; } if (i.seconds) { i.seconds *= -1; } } for (var field in i){ if (i[field] === 0) { delete i[field]; } } return i; }; var parseByteA = function(val) { if(/^\\x/.test(val)){ // new 'hex' style response (pg >9.0) return new Buffer(val.substr(2), 'hex'); }else{ var out = ""; var i = 0; while(i < val.length){ if(val[i] != "\\"){ out += val[i]; ++i; }else{ if(val.substr(i+1,3).match(/[0-7]{3}/)){ out += String.fromCharCode(parseInt(val.substr(i+1,3),8)); i += 4; }else{ backslashes = 1; while(i+backslashes < val.length && val[i+backslashes] == "\\") backslashes++; for(k=0; k maxLen) { console.warn( 'WARNING: value %s is longer than max supported numeric value in ' + 'javascript. Possible data loss', val); } return parseFloat(val); }); //TODO remove for v1.0 register(700, parseFloat); //TODO remove for v1.0 register(701, parseFloat); register(16, parseBool); register(1082, parseDate); // date register(1114, parseDate); // timestamp without timezone register(1184, parseDate); // timestamp register(1005, parseIntegerArray); // _int2 register(1007, parseIntegerArray); // _int4 register(1016, parseIntegerArray); // _int8 register(1021, parseFloatArray); // _float4 register(1022, parseFloatArray); // _float8 register(1231, parseIntegerArray); // _numeric register(1014, parseStringArray); //char register(1015, parseStringArray); //varchar register(1008, parseStringArray); register(1009, parseStringArray); register(1186, parseInterval); register(17, parseByteA); }; module.exports = { init: init }; node-postgres-0.13.3/lib/utils.js000066400000000000000000000037511211611354200166350ustar00rootroot00000000000000var url = require('url'); var defaults = require(__dirname + "/defaults"); var events = require('events'); //compatibility for old nodes if(typeof events.EventEmitter.prototype.once !== 'function') { events.EventEmitter.prototype.once = function (type, listener) { var self = this; self.on(type, function g () { self.removeListener(type, g); listener.apply(this, arguments); }); }; } // convert a JS array to a postgres array literal // uses comma separator so won't work for types like box that use // a different array separator. function arrayString(val) { var result = '{'; for (var i = 0 ; i < val.length; i++) { if(i > 0) { result = result + ','; } if(val[i] instanceof Date) { result = result + JSON.stringify(val[i]); } else if(typeof val[i] === 'undefined') { result = result + 'NULL'; } else if(Array.isArray(val[i])) { result = result + arrayString(val[i]); } else { result = result + (val[i] === null ? 'NULL' : JSON.stringify(val[i])); } } result = result + '}'; return result; } //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 //for complex types, etc... var prepareValue = function(val) { if(val instanceof Date) { return JSON.stringify(val); } if(typeof val === 'undefined') { return null; } if(Array.isArray(val)) { return arrayString(val); } return val === null ? null : val.toString(); }; function normalizeQueryConfig (config, values, callback) { //can take in strings or config objects config = (typeof(config) == 'string') ? { text: config } : config; if(values) { if(typeof values === 'function') { config.callback = values; } else { config.values = values; } } if(callback) { config.callback = callback; } return config; } module.exports = { prepareValue: prepareValue, normalizeQueryConfig: normalizeQueryConfig }; node-postgres-0.13.3/lib/writer.js000066400000000000000000000065721211611354200170150ustar00rootroot00000000000000//binary data writer tuned for creating //postgres message packets as effeciently as possible by reusing the //same buffer to avoid memcpy and limit memory allocations var Writer = function(size) { this.size = size || 1024; this.buffer = Buffer(this.size + 5); this.offset = 5; this.headerPosition = 0; }; //resizes internal buffer if not enough size left Writer.prototype._ensure = function(size) { var remaining = this.buffer.length - this.offset; if(remaining < size) { var oldBuffer = this.buffer; this.buffer = new Buffer(oldBuffer.length + size); oldBuffer.copy(this.buffer); } }; Writer.prototype.addInt32 = function(num) { this._ensure(4); this.buffer[this.offset++] = (num >>> 24 & 0xFF); this.buffer[this.offset++] = (num >>> 16 & 0xFF); this.buffer[this.offset++] = (num >>> 8 & 0xFF); this.buffer[this.offset++] = (num >>> 0 & 0xFF); return this; }; Writer.prototype.addInt16 = function(num) { this._ensure(2); this.buffer[this.offset++] = (num >>> 8 & 0xFF); this.buffer[this.offset++] = (num >>> 0 & 0xFF); return this; }; //for versions of node requiring 'length' as 3rd argument to buffer.write var writeString = function(buffer, string, offset, len) { buffer.write(string, offset, len); }; //overwrite function for older versions of node if(Buffer.prototype.write.length === 3) { writeString = function(buffer, string, offset, len) { buffer.write(string, offset); }; } Writer.prototype.addCString = function(string) { //just write a 0 for empty or null strings if(!string) { this._ensure(1); } else { var len = Buffer.byteLength(string); this._ensure(len + 1); //+1 for null terminator writeString(this.buffer, string, this.offset, len); this.offset += len; } this.buffer[this.offset++] = 0; // null terminator return this; }; Writer.prototype.addChar = function(c) { this._ensure(1); writeString(this.buffer, c, this.offset, 1); this.offset++; return this; }; Writer.prototype.addString = function(string) { string = string || ""; var len = Buffer.byteLength(string); this._ensure(len); this.buffer.write(string, this.offset); this.offset += len; return this; }; Writer.prototype.getByteLength = function() { return this.offset - 5; }; Writer.prototype.add = function(otherBuffer) { this._ensure(otherBuffer.length); otherBuffer.copy(this.buffer, this.offset); this.offset += otherBuffer.length; return this; }; Writer.prototype.clear = function() { this.offset = 5; this.headerPosition = 0; this.lastEnd = 0; }; //appends a header block to all the written data since the last //subsequent header or to the beginning if there is only one data block Writer.prototype.addHeader = function(code, last) { var origOffset = this.offset; this.offset = this.headerPosition; this.buffer[this.offset++] = code; //length is everything in this packet minus the code this.addInt32(origOffset - (this.headerPosition+1)); //set next header position this.headerPosition = origOffset; //make space for next header this.offset = origOffset; if(!last) { this._ensure(5); this.offset += 5; } }; Writer.prototype.join = function(code) { if(code) { this.addHeader(code, true); } return this.buffer.slice(code ? 0 : 5, this.offset); }; Writer.prototype.flush = function(code) { var result = this.join(code); this.clear(); return result; }; module.exports = Writer; node-postgres-0.13.3/package.json000066400000000000000000000014441211611354200166540ustar00rootroot00000000000000{ "name": "pg", "version": "0.13.3", "description": "PostgreSQL client - pure javascript & libpq with the same API", "keywords" : ["postgres", "pg", "libpq", "postgre", "database", "rdbms"], "homepage": "http://github.com/brianc/node-postgres", "repository" : { "type" : "git", "url" : "git://github.com/brianc/node-postgres.git" }, "author" : "Brian Carlson ", "main" : "./lib", "dependencies" : { "generic-pool" : "2.0.2" }, "devDependencies" : { "jshint" : "git://github.com/jshint/jshint.git" }, "scripts" : { "test" : "make test-all connectionString=pg://postgres@localhost:5432/postgres", "prepublish": "rm -r build || (exit 0)", "install" : "node-gyp rebuild || (exit 0)" }, "engines" : { "node": ">= 0.8.0" } } node-postgres-0.13.3/script/000077500000000000000000000000001211611354200156675ustar00rootroot00000000000000node-postgres-0.13.3/script/create-test-tables.js000066400000000000000000000033021211611354200217130ustar00rootroot00000000000000var args = require(__dirname + '/../test/cli'); var pg = require(__dirname + '/../lib'); var people = [ {name: 'Aaron', age: 10}, {name: 'Brian', age: 20}, {name: 'Chris', age: 30}, {name: 'David', age: 40}, {name: 'Elvis', age: 50}, {name: 'Frank', age: 60}, {name: 'Grace', age: 70}, {name: 'Haley', age: 80}, {name: 'Irma', age: 90}, {name: 'Jenny', age: 100}, {name: 'Kevin', age: 110}, {name: 'Larry', age: 120}, {name: 'Michelle', age: 130}, {name: 'Nancy', age: 140}, {name: 'Olivia', age: 150}, {name: 'Peter', age: 160}, {name: 'Quinn', age: 170}, {name: 'Ronda', age: 180}, {name: 'Shelley', age: 190}, {name: 'Tobias', age: 200}, {name: 'Uma', age: 210}, {name: 'Veena', age: 220}, {name: 'Wanda', age: 230}, {name: 'Xavier', age: 240}, {name: 'Yoyo', age: 250}, {name: 'Zanzabar', age: 260} ] var con = new pg.Client({ host: args.host, port: args.port, user: args.user, password: args.password, database: args.database }); con.connect(); if(args.down) { console.log("Dropping table 'person'") var query = con.query("drop table person"); query.on('end', function() { console.log("Dropped!"); con.end(); }); } else { console.log("Creating table 'person'"); con.query("create table person(id serial, name varchar(10), age integer)").on('end', function(){ console.log("Created!"); console.log("Filling it with people"); });; people.map(function(person) { return con.query("insert into person(name, age) values('"+person.name + "', '" + person.age + "')"); }).pop().on('end', function(){ console.log("Inserted 26 people"); con.end(); }); } node-postgres-0.13.3/script/dump-db-types.js000066400000000000000000000007661211611354200207300ustar00rootroot00000000000000var pg = require(__dirname + '/../lib'); var args = require(__dirname + '/../test/cli'); var queries = [ "select CURRENT_TIMESTAMP", "select interval '1 day' + interval '1 hour'", "select TIMESTAMP 'today'"]; queries.forEach(function(query) { var client = new pg.Client({ user: args.user, database: args.database, password: args.password }); client.connect(); client .query(query) .on('row', function(row) { console.log(row); client.end(); }); }); node-postgres-0.13.3/script/list-db-types.js000066400000000000000000000004351211611354200207270ustar00rootroot00000000000000var helper = require(__dirname + "/../test/integration/test-helper"); var pg = helper.pg; pg.connect(helper.config, assert.success(function(client) { var query = client.query('select oid, typname from pg_type where typtype = \'b\' order by oid'); query.on('row', console.log); })) node-postgres-0.13.3/script/test-connection.js000066400000000000000000000016641211611354200213500ustar00rootroot00000000000000var helper = require(__dirname + '/../test/test-helper'); console.log(); console.log("testing ability to connect to '%j'", helper.config); var pg = require(__dirname + '/../lib'); pg.connect(helper.config, function(err, client) { if(err !== null) { console.error("Recieved connection error when attempting to contact PostgreSQL:"); console.error(err); process.exit(255); } console.log("Checking for existance of required test table 'person'") client.query("SELECT COUNT(name) FROM person", function(err, callback) { if(err != null) { console.error("Recieved error when executing query 'SELECT COUNT(name) FROM person'") console.error("It is possible you have not yet run the table create script under script/create-test-tables") console.error("Consult the postgres-node wiki under the 'Testing' section for more information") console.error(err); process.exit(255); } pg.end(); }) }) node-postgres-0.13.3/src/000077500000000000000000000000001211611354200151525ustar00rootroot00000000000000node-postgres-0.13.3/src/binding.cc000066400000000000000000000535621211611354200171060ustar00rootroot00000000000000#include #include #include #include #include #include #define LOG(msg) printf("%s\n",msg); #define TRACE(msg) //printf("%s\n", msg); #define THROW(msg) return ThrowException(Exception::Error(String::New(msg))); using namespace v8; using namespace node; static Persistent severity_symbol; static Persistent code_symbol; static Persistent detail_symbol; static Persistent hint_symbol; static Persistent position_symbol; static Persistent internalPosition_symbol; static Persistent internalQuery_symbol; static Persistent where_symbol; static Persistent file_symbol; static Persistent line_symbol; static Persistent routine_symbol; static Persistent name_symbol; static Persistent value_symbol; static Persistent type_symbol; static Persistent channel_symbol; static Persistent payload_symbol; static Persistent emit_symbol; static Persistent command_symbol; class Connection : public ObjectWrap { public: //creates the V8 objects & attaches them to the module (target) static void Init (Handle target) { HandleScope scope; Local t = FunctionTemplate::New(New); t->InstanceTemplate()->SetInternalFieldCount(1); t->SetClassName(String::NewSymbol("Connection")); emit_symbol = NODE_PSYMBOL("emit"); severity_symbol = NODE_PSYMBOL("severity"); code_symbol = NODE_PSYMBOL("code"); detail_symbol = NODE_PSYMBOL("detail"); hint_symbol = NODE_PSYMBOL("hint"); position_symbol = NODE_PSYMBOL("position"); internalPosition_symbol = NODE_PSYMBOL("internalPosition"); internalQuery_symbol = NODE_PSYMBOL("internalQuery"); where_symbol = NODE_PSYMBOL("where"); file_symbol = NODE_PSYMBOL("file"); line_symbol = NODE_PSYMBOL("line"); routine_symbol = NODE_PSYMBOL("routine"); name_symbol = NODE_PSYMBOL("name"); value_symbol = NODE_PSYMBOL("value"); type_symbol = NODE_PSYMBOL("type"); channel_symbol = NODE_PSYMBOL("channel"); payload_symbol = NODE_PSYMBOL("payload"); command_symbol = NODE_PSYMBOL("command"); NODE_SET_PROTOTYPE_METHOD(t, "connect", Connect); NODE_SET_PROTOTYPE_METHOD(t, "_sendQuery", SendQuery); NODE_SET_PROTOTYPE_METHOD(t, "_sendQueryWithParams", SendQueryWithParams); NODE_SET_PROTOTYPE_METHOD(t, "_sendPrepare", SendPrepare); NODE_SET_PROTOTYPE_METHOD(t, "_sendQueryPrepared", SendQueryPrepared); NODE_SET_PROTOTYPE_METHOD(t, "cancel", Cancel); NODE_SET_PROTOTYPE_METHOD(t, "end", End); NODE_SET_PROTOTYPE_METHOD(t, "_sendCopyFromChunk", SendCopyFromChunk); NODE_SET_PROTOTYPE_METHOD(t, "_endCopyFrom", EndCopyFrom); target->Set(String::NewSymbol("Connection"), t->GetFunction()); TRACE("created class"); } //static function called by libuv as callback entrypoint static void io_event(uv_poll_t* w, int status, int revents) { TRACE("Received IO event"); if(status == -1) { LOG("Connection error."); return; } Connection *connection = static_cast(w->data); connection->HandleIOEvent(revents); } //v8 entry point into Connection#connect static Handle Connect(const Arguments& args) { HandleScope scope; Connection *self = ObjectWrap::Unwrap(args.This()); if(args.Length() == 0 || !args[0]->IsString()) { THROW("Must include connection string as only argument to connect"); } String::Utf8Value conninfo(args[0]->ToString()); bool success = self->Connect(*conninfo); if(!success) { self -> EmitLastError(); self -> DestroyConnection(); } return Undefined(); } //v8 entry point into Connection#cancel static Handle Cancel(const Arguments& args) { HandleScope scope; Connection *self = ObjectWrap::Unwrap(args.This()); bool success = self->Cancel(); if(!success) { self -> EmitLastError(); self -> DestroyConnection(); } return Undefined(); } //v8 entry point into Connection#_sendQuery static Handle SendQuery(const Arguments& args) { HandleScope scope; Connection *self = ObjectWrap::Unwrap(args.This()); const char *lastErrorMessage; if(!args[0]->IsString()) { THROW("First parameter must be a string query"); } char* queryText = MallocCString(args[0]); int result = self->Send(queryText); free(queryText); if(result == 0) { lastErrorMessage = self->GetLastError(); THROW(lastErrorMessage); } //TODO should we flush before throw? self->Flush(); return Undefined(); } //v8 entry point into Connection#_sendQueryWithParams static Handle SendQueryWithParams(const Arguments& args) { HandleScope scope; //dispatch non-prepared parameterized query return DispatchParameterizedQuery(args, false); } //v8 entry point into Connection#_sendPrepare(string queryName, string queryText, int nParams) static Handle SendPrepare(const Arguments& args) { HandleScope scope; Connection *self = ObjectWrap::Unwrap(args.This()); String::Utf8Value queryName(args[0]); String::Utf8Value queryText(args[1]); int length = args[2]->Int32Value(); self->SendPrepare(*queryName, *queryText, length); return Undefined(); } //v8 entry point into Connection#_sendQueryPrepared(string queryName, string[] paramValues) static Handle SendQueryPrepared(const Arguments& args) { HandleScope scope; //dispatch prepared parameterized query return DispatchParameterizedQuery(args, true); } static Handle DispatchParameterizedQuery(const Arguments& args, bool isPrepared) { HandleScope scope; Connection *self = ObjectWrap::Unwrap(args.This()); String::Utf8Value queryName(args[0]); //TODO this is much copy/pasta code if(!args[0]->IsString()) { THROW("First parameter must be a string"); } if(!args[1]->IsArray()) { THROW("Values must be an array"); } Local jsParams = Local::Cast(args[1]); int len = jsParams->Length(); char** paramValues = ArgToCStringArray(jsParams); if(!paramValues) { THROW("Unable to allocate char **paramValues from Local of v8 params"); } char* queryText = MallocCString(args[0]); int result = 0; if(isPrepared) { result = self->SendPreparedQuery(queryText, len, paramValues); } else { result = self->SendQueryParams(queryText, len, paramValues); } free(queryText); ReleaseCStringArray(paramValues, len); if(result == 1) { return Undefined(); } self->EmitLastError(); THROW("Postgres returned non-1 result from query dispatch."); } //v8 entry point into Connection#end static Handle End(const Arguments& args) { HandleScope scope; Connection *self = ObjectWrap::Unwrap(args.This()); self->End(); return Undefined(); } uv_poll_t read_watcher_; uv_poll_t write_watcher_; PGconn *connection_; bool connecting_; bool ioInitialized_; bool copyOutMode_; bool copyInMode_; Connection () : ObjectWrap () { connection_ = NULL; connecting_ = false; ioInitialized_ = false; copyOutMode_ = false; copyInMode_ = false; TRACE("Initializing ev watchers"); read_watcher_.data = this; write_watcher_.data = this; } ~Connection () { } static Handle SendCopyFromChunk(const Arguments& args) { HandleScope scope; Connection *self = ObjectWrap::Unwrap(args.This()); //TODO handle errors in some way if (args.Length() < 1 && !Buffer::HasInstance(args[0])) { THROW("SendCopyFromChunk requires 1 Buffer argument"); } self->SendCopyFromChunk(args[0]->ToObject()); return Undefined(); } static Handle EndCopyFrom(const Arguments& args) { HandleScope scope; Connection *self = ObjectWrap::Unwrap(args.This()); char * error_msg = NULL; if (args[0]->IsString()) { error_msg = MallocCString(args[0]); } //TODO handle errors in some way self->EndCopyFrom(error_msg); free(error_msg); return Undefined(); } protected: //v8 entry point to constructor static Handle New (const Arguments& args) { HandleScope scope; Connection *connection = new Connection(); connection->Wrap(args.This()); return args.This(); } int Send(const char *queryText) { int rv = PQsendQuery(connection_, queryText); StartWrite(); return rv; } int SendQueryParams(const char *command, const int nParams, const char * const *paramValues) { int rv = PQsendQueryParams(connection_, command, nParams, NULL, paramValues, NULL, NULL, 0); StartWrite(); return rv; } int SendPrepare(const char *name, const char *command, const int nParams) { int rv = PQsendPrepare(connection_, name, command, nParams, NULL); StartWrite(); return rv; } int SendPreparedQuery(const char *name, int nParams, const char * const *paramValues) { int rv = PQsendQueryPrepared(connection_, name, nParams, paramValues, NULL, NULL, 0); StartWrite(); return rv; } bool Cancel() { PGcancel* pgCancel = PQgetCancel(connection_); char errbuf[256]; int result = PQcancel(pgCancel, errbuf, 256); StartWrite(); PQfreeCancel(pgCancel); return result; } //flushes socket void Flush() { if(PQflush(connection_) == 1) { TRACE("Flushing"); uv_poll_start(&write_watcher_, UV_WRITABLE, io_event); } } //safely destroys the connection at most 1 time void DestroyConnection() { if(connection_ != NULL) { PQfinish(connection_); connection_ = NULL; } } //initializes initial async connection to postgres via libpq //and hands off control to libev bool Connect(const char* conninfo) { connection_ = PQconnectStart(conninfo); if (!connection_) { LOG("Connection couldn't be created"); } ConnStatusType status = PQstatus(connection_); if(CONNECTION_BAD == status) { return false; } if (PQsetnonblocking(connection_, 1) == -1) { LOG("Unable to set connection to non-blocking"); return false; } int fd = PQsocket(connection_); if(fd < 0) { LOG("socket fd was negative. error"); return false; } assert(PQisnonblocking(connection_)); PQsetNoticeProcessor(connection_, NoticeReceiver, this); TRACE("Setting watchers to socket"); uv_poll_init(uv_default_loop(), &read_watcher_, fd); uv_poll_init(uv_default_loop(), &write_watcher_, fd); ioInitialized_ = true; connecting_ = true; StartWrite(); Ref(); return true; } static void NoticeReceiver(void *arg, const char *message) { Connection *self = (Connection*)arg; self->HandleNotice(message); } void HandleNotice(const char *message) { HandleScope scope; Handle notice = String::New(message); Emit("notice", ¬ice); } //called to process io_events from libuv void HandleIOEvent(int revents) { if(connecting_) { TRACE("Processing connecting_ io"); HandleConnectionIO(); return; } if(revents & UV_READABLE) { TRACE("revents & UV_READABLE"); if(PQconsumeInput(connection_) == 0) { End(); EmitLastError(); LOG("Something happened, consume input is 0"); return; } //declare handlescope as this method is entered via a libuv callback //and not part of the public v8 interface HandleScope scope; if (this->copyOutMode_) { this->HandleCopyOut(); } if (!this->copyInMode_ && !this->copyOutMode_ && PQisBusy(connection_) == 0) { PGresult *result; bool didHandleResult = false; while ((result = PQgetResult(connection_))) { didHandleResult = HandleResult(result); PQclear(result); if(!didHandleResult) { //this means that we are in copy in or copy out mode //in this situation PQgetResult will return same //result untill all data will be read (copy out) or //until data end notification (copy in) //and because of this, we need to break cycle break; } } //might have fired from notification if(didHandleResult) { Emit("_readyForQuery"); } } PGnotify *notify; while ((notify = PQnotifies(connection_))) { Local result = Object::New(); result->Set(channel_symbol, String::New(notify->relname)); result->Set(payload_symbol, String::New(notify->extra)); Handle res = (Handle)result; Emit("notification", &res); PQfreemem(notify); } } if(revents & UV_WRITABLE) { TRACE("revents & UV_WRITABLE"); if (PQflush(connection_) == 0) { StopWrite(); } } } bool HandleCopyOut () { char * buffer = NULL; int copied; Buffer * chunk; copied = PQgetCopyData(connection_, &buffer, 1); while (copied > 0) { chunk = Buffer::New(buffer, copied); Local node_chunk = Local::New(chunk->handle_); Emit("copyData", &node_chunk); PQfreemem(buffer); copied = PQgetCopyData(connection_, &buffer, 1); } if (copied == 0) { //wait for next read ready //result was not handled completely return false; } else if (copied == -1) { this->copyOutMode_ = false; return true; } else if (copied == -2) { this->copyOutMode_ = false; return true; } return false; } bool HandleResult(PGresult* result) { ExecStatusType status = PQresultStatus(result); switch(status) { case PGRES_TUPLES_OK: { HandleTuplesResult(result); EmitCommandMetaData(result); return true; } break; case PGRES_FATAL_ERROR: { HandleErrorResult(result); return true; } break; case PGRES_COMMAND_OK: case PGRES_EMPTY_QUERY: { EmitCommandMetaData(result); return true; } break; case PGRES_COPY_IN: { this->copyInMode_ = true; Emit("copyInResponse"); return false; } break; case PGRES_COPY_OUT: { this->copyOutMode_ = true; Emit("copyOutResponse"); return this->HandleCopyOut(); } break; default: printf("YOU SHOULD NEVER SEE THIS! PLEASE OPEN AN ISSUE ON GITHUB! Unrecogized query status: %s\n", PQresStatus(status)); break; } return true; } void EmitCommandMetaData(PGresult* result) { HandleScope scope; Local info = Object::New(); info->Set(command_symbol, String::New(PQcmdStatus(result))); info->Set(value_symbol, String::New(PQcmdTuples(result))); Handle e = (Handle)info; Emit("_cmdStatus", &e); } //maps the postgres tuple results to v8 objects //and emits row events //TODO look at emitting fewer events because the back & forth between //javascript & c++ might introduce overhead (requires benchmarking) void HandleTuplesResult(const PGresult* result) { HandleScope scope; int rowCount = PQntuples(result); for(int rowNumber = 0; rowNumber < rowCount; rowNumber++) { //create result object for this row Local row = Array::New(); int fieldCount = PQnfields(result); for(int fieldNumber = 0; fieldNumber < fieldCount; fieldNumber++) { Local field = Object::New(); //name of field char* fieldName = PQfname(result, fieldNumber); field->Set(name_symbol, String::New(fieldName)); //oid of type of field int fieldType = PQftype(result, fieldNumber); field->Set(type_symbol, Integer::New(fieldType)); //value of field if(PQgetisnull(result, rowNumber, fieldNumber)) { field->Set(value_symbol, Null()); } else { char* fieldValue = PQgetvalue(result, rowNumber, fieldNumber); field->Set(value_symbol, String::New(fieldValue)); } row->Set(Integer::New(fieldNumber), field); } Handle e = (Handle)row; Emit("_row", &e); } } void HandleErrorResult(const PGresult* result) { HandleScope scope; //instantiate the return object as an Error with the summary Postgres message Local msg = Local::Cast(Exception::Error(String::New(PQresultErrorField(result, PG_DIAG_MESSAGE_PRIMARY)))); //add the other information returned by Postgres to the error object AttachErrorField(result, msg, severity_symbol, PG_DIAG_SEVERITY); AttachErrorField(result, msg, code_symbol, PG_DIAG_SQLSTATE); AttachErrorField(result, msg, detail_symbol, PG_DIAG_MESSAGE_DETAIL); AttachErrorField(result, msg, hint_symbol, PG_DIAG_MESSAGE_HINT); AttachErrorField(result, msg, position_symbol, PG_DIAG_STATEMENT_POSITION); AttachErrorField(result, msg, internalPosition_symbol, PG_DIAG_INTERNAL_POSITION); AttachErrorField(result, msg, internalQuery_symbol, PG_DIAG_INTERNAL_QUERY); AttachErrorField(result, msg, where_symbol, PG_DIAG_CONTEXT); AttachErrorField(result, msg, file_symbol, PG_DIAG_SOURCE_FILE); AttachErrorField(result, msg, line_symbol, PG_DIAG_SOURCE_LINE); AttachErrorField(result, msg, routine_symbol, PG_DIAG_SOURCE_FUNCTION); Handle m = msg; Emit("_error", &m); } void AttachErrorField(const PGresult *result, const Local msg, const Persistent symbol, int fieldcode) { char *val = PQresultErrorField(result, fieldcode); if(val) { msg->Set(symbol, String::New(val)); } } void End() { StopRead(); StopWrite(); DestroyConnection(); } private: //EventEmitter was removed from c++ in node v0.5.x void Emit(const char* message) { HandleScope scope; Handle args[1] = { String::New(message) }; Emit(1, args); } void Emit(const char* message, Handle* arg) { HandleScope scope; Handle args[2] = { String::New(message), *arg }; Emit(2, args); } void Emit(int length, Handle *args) { HandleScope scope; Local emit_v = this->handle_->Get(emit_symbol); assert(emit_v->IsFunction()); Local emit_f = emit_v.As(); TryCatch tc; emit_f->Call(this->handle_, length, args); if(tc.HasCaught()) { FatalException(tc); } } void HandleConnectionIO() { PostgresPollingStatusType status = PQconnectPoll(connection_); switch(status) { case PGRES_POLLING_READING: TRACE("Polled: PGRES_POLLING_READING"); StopWrite(); StartRead(); break; case PGRES_POLLING_WRITING: TRACE("Polled: PGRES_POLLING_WRITING"); StopRead(); StartWrite(); break; case PGRES_POLLING_FAILED: StopRead(); StopWrite(); TRACE("Polled: PGRES_POLLING_FAILED"); EmitLastError(); break; case PGRES_POLLING_OK: TRACE("Polled: PGRES_POLLING_OK"); connecting_ = false; StartRead(); Emit("connect"); default: //printf("Unknown polling status: %d\n", status); break; } } void EmitError(const char *message) { Local exception = Exception::Error(String::New(message)); Emit("_error", &exception); } void EmitLastError() { EmitError(PQerrorMessage(connection_)); } const char *GetLastError() { return PQerrorMessage(connection_); } void StopWrite() { TRACE("Stoping write watcher"); if(ioInitialized_) { uv_poll_stop(&write_watcher_); } } void StartWrite() { TRACE("Starting write watcher"); uv_poll_start(&write_watcher_, UV_WRITABLE, io_event); } void StopRead() { TRACE("Stoping read watcher"); if(ioInitialized_) { uv_poll_stop(&read_watcher_); } } void StartRead() { TRACE("Starting read watcher"); uv_poll_start(&read_watcher_, UV_READABLE, io_event); } //Converts a v8 array to an array of cstrings //the result char** array must be free() when it is no longer needed //if for any reason the array cannot be created, returns 0 static char** ArgToCStringArray(Local params) { int len = params->Length(); char** paramValues = new char*[len]; for(int i = 0; i < len; i++) { Handle val = params->Get(i); if(val->IsString()) { char* cString = MallocCString(val); //will be 0 if could not malloc if(!cString) { LOG("ArgToCStringArray: OUT OF MEMORY OR SOMETHING BAD!"); ReleaseCStringArray(paramValues, i-1); return 0; } paramValues[i] = cString; } else if(val->IsNull()) { paramValues[i] = NULL; } else { //a paramter was not a string LOG("Parameter not a string"); ReleaseCStringArray(paramValues, i-1); return 0; } } return paramValues; } //helper function to release cString arrays static void ReleaseCStringArray(char **strArray, int len) { for(int i = 0; i < len; i++) { free(strArray[i]); } delete [] strArray; } //helper function to malloc new string from v8string static char* MallocCString(v8::Handle v8String) { String::Utf8Value utf8String(v8String->ToString()); char *cString = (char *) malloc(strlen(*utf8String) + 1); if(!cString) { return cString; } strcpy(cString, *utf8String); return cString; } void SendCopyFromChunk(Handle chunk) { PQputCopyData(connection_, Buffer::Data(chunk), Buffer::Length(chunk)); } void EndCopyFrom(char * error_msg) { PQputCopyEnd(connection_, error_msg); this->copyInMode_ = false; } }; extern "C" void init (Handle target) { HandleScope scope; Connection::Init(target); } NODE_MODULE(binding, init) node-postgres-0.13.3/test/000077500000000000000000000000001211611354200153425ustar00rootroot00000000000000node-postgres-0.13.3/test/buffer-list.js000066400000000000000000000027421211611354200201270ustar00rootroot00000000000000BufferList = function() { this.buffers = []; }; var p = BufferList.prototype; p.add = function(buffer, front) { this.buffers[front ? "unshift" : "push"](buffer); return this; }; p.addInt16 = function(val, front) { return this.add(Buffer([(val >>> 8),(val >>> 0)]),front); }; p.getByteLength = function(initial) { return this.buffers.reduce(function(previous, current){ return previous + current.length; },initial || 0); }; p.addInt32 = function(val, first) { return this.add(Buffer([ (val >>> 24 & 0xFF), (val >>> 16 & 0xFF), (val >>> 8 & 0xFF), (val >>> 0 & 0xFF) ]),first); }; p.addCString = function(val, front) { var len = Buffer.byteLength(val); var buffer = new Buffer(len+1); buffer.write(val); buffer[len] = 0; return this.add(buffer, front); }; p.addChar = function(char, first) { return this.add(Buffer(char,'utf8'), first); }; p.join = function(appendLength, char) { var length = this.getByteLength(); if(appendLength) { this.addInt32(length+4, true); return this.join(false, char); } if(char) { this.addChar(char, true); length++; } var result = Buffer(length); var index = 0; this.buffers.forEach(function(buffer) { buffer.copy(result, index, 0); index += buffer.length; }); return result; }; BufferList.concat = function() { var total = new BufferList(); for(var i = 0; i < arguments.length; i++) { total.add(arguments[i]); } return total.join(); }; module.exports = BufferList; node-postgres-0.13.3/test/cli.js000066400000000000000000000006051211611354200164500ustar00rootroot00000000000000var 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()) { case 'native': config.native = true; break; case 'binary': config.binary = true; break; default: break; } } module.exports = config; node-postgres-0.13.3/test/integration/000077500000000000000000000000001211611354200176655ustar00rootroot00000000000000node-postgres-0.13.3/test/integration/client/000077500000000000000000000000001211611354200211435ustar00rootroot00000000000000node-postgres-0.13.3/test/integration/client/api-tests.js000066400000000000000000000133251211611354200234160ustar00rootroot00000000000000var helper = require(__dirname + '/../test-helper'); var pg = require(__dirname + '/../../../lib'); if(helper.args.native) { pg = require(__dirname + '/../../../lib').native; } var log = function() { //console.log.apply(console, arguments); } var sink = new helper.Sink(5, 10000, function() { log("ending connection pool: %j", helper.config); pg.end(helper.config); }); test('api', function() { log("connecting to %j", helper.config) //test weird callback behavior with node-pool pg.connect(helper.config, function(err) { assert.isNull(err); arguments[1].emit('drain'); }); pg.connect(helper.config, assert.calls(function(err, client) { assert.equal(err, null, "Failed to connect: " + helper.sys.inspect(err)); client.query('CREATE TEMP TABLE band(name varchar(100))'); ['the flaming lips', 'wolf parade', 'radiohead', 'bright eyes', 'the beach boys', 'dead black hearts'].forEach(function(bandName) { var query = client.query("INSERT INTO band (name) VALUES ('"+ bandName +"')") }); test('simple query execution',assert.calls( function() { log("executing simple query") client.query("SELECT * FROM band WHERE name = 'the beach boys'", assert.calls(function(err, result) { assert.lengthIs(result.rows, 1) assert.equal(result.rows.pop().name, 'the beach boys') log("simple query executed") })); })) test('prepared statement execution',assert.calls( function() { log("executing prepared statement 1") client.query('SELECT * FROM band WHERE name = $1', ['dead black hearts'],assert.calls( function(err, result) { log("Prepared statement 1 finished") assert.lengthIs(result.rows, 1); assert.equal(result.rows.pop().name, 'dead black hearts'); })) log("executing prepared statement two") client.query('SELECT * FROM band WHERE name LIKE $1 ORDER BY name', ['the %'], assert.calls(function(err, result) { log("prepared statement two finished") assert.lengthIs(result.rows, 2); assert.equal(result.rows.pop().name, 'the flaming lips'); assert.equal(result.rows.pop().name, 'the beach boys'); sink.add(); })) })) })) }) test('executing nested queries', function() { pg.connect(helper.config, assert.calls(function(err, client) { assert.isNull(err); log("connected for nested queriese") client.query('select now as now from NOW()', assert.calls(function(err, result) { assert.equal(new Date().getYear(), result.rows[0].now.getYear()) client.query('select now as now_again FROM NOW()', assert.calls(function() { client.query('select * FROM NOW()', assert.calls(function() { log('all nested queries recieved') assert.ok('all queries hit') sink.add(); })) })) })) })) }) test('raises error if cannot connect', function() { var connectionString = "pg://sfalsdkf:asdf@localhost/ieieie"; log("trying to connect to invalid place for error") pg.connect(connectionString, assert.calls(function(err, client) { assert.ok(err, 'should have raised an error') log("invalid connection supplied error to callback") sink.add(); })) }) test("query errors are handled and do not bubble if callback is provded", function() { pg.connect(helper.config, assert.calls(function(err, client) { assert.isNull(err) log("checking for query error") client.query("SELECT OISDJF FROM LEIWLISEJLSE", assert.calls(function(err, result) { assert.ok(err); log("query error supplied error to callback") sink.add(); })) })) }) test('callback is fired once and only once', function() { pg.connect(helper.config, assert.calls(function(err, client) { assert.isNull(err); client.query("CREATE TEMP TABLE boom(name varchar(10))"); var callCount = 0; client.query([ "INSERT INTO boom(name) VALUES('hai')", "INSERT INTO boom(name) VALUES('boom')", "INSERT INTO boom(name) VALUES('zoom')", ].join(";"), function(err, callback) { assert.equal(callCount++, 0, "Call count should be 0. More means this callback fired more than once."); sink.add(); }) })) }) test('can provide callback and config object', function() { pg.connect(helper.config, assert.calls(function(err, client) { assert.isNull(err); client.query({ name: 'boom', text: 'select NOW()' }, assert.calls(function(err, result) { assert.isNull(err); assert.equal(result.rows[0].now.getYear(), new Date().getYear()) })) })) }) test('can provide callback and config and parameters', function() { pg.connect(helper.config, assert.calls(function(err, client) { assert.isNull(err); var config = { text: 'select $1::text as val' }; client.query(config, ['hi'], assert.calls(function(err, result) { assert.isNull(err); assert.equal(result.rows.length, 1); assert.equal(result.rows[0].val, 'hi'); })) })) }) test('null and undefined are both inserted as NULL', function() { pg.connect(helper.config, assert.calls(function(err, client) { assert.isNull(err); client.query("CREATE TEMP TABLE my_nulls(a varchar(1), b varchar(1), c integer, d integer, e date, f date)"); client.query("INSERT INTO my_nulls(a,b,c,d,e,f) VALUES ($1,$2,$3,$4,$5,$6)", [ null, undefined, null, undefined, null, undefined ]); client.query("SELECT * FROM my_nulls", assert.calls(function(err, result) { assert.isNull(err); assert.equal(result.rows.length, 1); assert.isNull(result.rows[0].a); assert.isNull(result.rows[0].b); assert.isNull(result.rows[0].c); assert.isNull(result.rows[0].d); assert.isNull(result.rows[0].e); assert.isNull(result.rows[0].f); })) })) }) node-postgres-0.13.3/test/integration/client/array-tests.js000066400000000000000000000112161211611354200237600ustar00rootroot00000000000000var helper = require(__dirname + "/test-helper"); var pg = helper.pg; test('parsing array results', function() { pg.connect(helper.config, assert.calls(function(err, client) { assert.isNull(err); client.query("CREATE TEMP TABLE why(names text[], numbors integer[])"); client.query('INSERT INTO why(names, numbors) VALUES(\'{"aaron", "brian","a b c" }\', \'{1, 2, 3}\')').on('error', console.log); test('numbers', function() { // client.connection.on('message', console.log) client.query('SELECT numbors FROM why', assert.success(function(result) { assert.lengthIs(result.rows[0].numbors, 3); assert.equal(result.rows[0].numbors[0], 1); assert.equal(result.rows[0].numbors[1], 2); assert.equal(result.rows[0].numbors[2], 3); })) }) test('parses string arrays', function() { client.query('SELECT names FROM why', assert.success(function(result) { var names = result.rows[0].names; assert.lengthIs(names, 3); assert.equal(names[0], 'aaron'); assert.equal(names[1], 'brian'); assert.equal(names[2], "a b c"); pg.end(); })) }) test('empty array', function(){ client.query("SELECT '{}'::text[] as names", assert.success(function(result) { var names = result.rows[0].names; assert.lengthIs(names, 0); pg.end(); })) }) test('element containing comma', function(){ client.query("SELECT '{\"joe,bob\",jim}'::text[] as names", assert.success(function(result) { var names = result.rows[0].names; assert.lengthIs(names, 2); assert.equal(names[0], 'joe,bob'); assert.equal(names[1], 'jim'); pg.end(); })) }) test('bracket in quotes', function(){ client.query("SELECT '{\"{\",\"}\"}'::text[] as names", assert.success(function(result) { var names = result.rows[0].names; assert.lengthIs(names, 2); assert.equal(names[0], '{'); assert.equal(names[1], '}'); pg.end(); })) }) test('null value', function(){ client.query("SELECT '{joe,null,bob,\"NULL\"}'::text[] as names", assert.success(function(result) { var names = result.rows[0].names; assert.lengthIs(names, 4); assert.equal(names[0], 'joe'); assert.equal(names[1], null); assert.equal(names[2], 'bob'); assert.equal(names[3], 'NULL'); pg.end(); })) }) test('element containing quote char', function(){ client.query("SELECT ARRAY['joe''', 'jim', 'bob\"'] AS names", assert.success(function(result) { var names = result.rows[0].names; assert.lengthIs(names, 3); assert.equal(names[0], 'joe\''); assert.equal(names[1], 'jim'); assert.equal(names[2], 'bob"'); pg.end(); })) }) test('nested array', function(){ client.query("SELECT '{{1,joe},{2,bob}}'::text[] as names", assert.success(function(result) { var names = result.rows[0].names; assert.lengthIs(names, 2); assert.lengthIs(names[0], 2); assert.equal(names[0][0], '1'); assert.equal(names[0][1], 'joe'); assert.lengthIs(names[1], 2); assert.equal(names[1][0], '2'); assert.equal(names[1][1], 'bob'); pg.end(); })) }) test('integer array', function(){ client.query("SELECT '{1,2,3}'::integer[] as names", assert.success(function(result) { var names = result.rows[0].names; assert.lengthIs(names, 3); assert.equal(names[0], 1); assert.equal(names[1], 2); assert.equal(names[2], 3); pg.end(); })) }) test('integer nested array', function(){ client.query("SELECT '{{1,100},{2,100},{3,100}}'::integer[] as names", assert.success(function(result) { var names = result.rows[0].names; assert.lengthIs(names, 3); assert.equal(names[0][0], 1); assert.equal(names[0][1], 100); assert.equal(names[1][0], 2); assert.equal(names[1][1], 100); assert.equal(names[2][0], 3); assert.equal(names[2][1], 100); pg.end(); })) }) test('JS array parameter', function(){ client.query("SELECT $1::integer[] as names", [[[1,100],[2,100],[3,100]]], assert.success(function(result) { var names = result.rows[0].names; assert.lengthIs(names, 3); assert.equal(names[0][0], 1); assert.equal(names[0][1], 100); assert.equal(names[1][0], 2); assert.equal(names[1][1], 100); assert.equal(names[2][0], 3); assert.equal(names[2][1], 100); pg.end(); })) }) })) }) node-postgres-0.13.3/test/integration/client/big-simple-query-tests.js000066400000000000000000000247451211611354200260500ustar00rootroot00000000000000var helper = require(__dirname+"/test-helper"); /* Test to trigger a bug. I really dont know what's wrong but it seams that its triggered by multable big queryies with supplied values. The test bellow can trigger the bug. */ // Big query with a where clouse from supplied value var big_query_rows_1 = []; var big_query_rows_2 = []; var big_query_rows_3 = []; // Works test('big simple query 1',function() { var client = helper.client(); client.query("select 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' as bla from person where name = '' or 1 = 1") .on('row', function(row) { big_query_rows_1.push(row); }) .on('error', function(error) { console.log("big simple query 1 error"); console.log(error); }); client.on('drain', client.end.bind(client)); }); // Works test('big simple query 2',function() { var client = helper.client(); client.query("select 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' as bla from person where name = $1 or 1 = 1",['']) .on('row', function(row) { big_query_rows_2.push(row); }) .on('error', function(error) { console.log("big simple query 2 error"); console.log(error); }); client.on('drain', client.end.bind(client)); }); // Fails most of the time with 'invalid byte sequence for encoding "UTF8": 0xb9' or 'insufficient data left in message' // If test 1 and 2 are commented out it works test('big simple query 3',function() { var client = helper.client(); client.query("select 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' as bla from person where name = $1 or 1 = 1",['']) .on('row', function(row) { big_query_rows_3.push(row); }) .on('error', function(error) { console.log("big simple query 3 error"); console.log(error); }); client.on('drain', client.end.bind(client)); }); process.on('exit', function() { assert.equal(big_query_rows_1.length, 26,'big simple query 1 should return 26 rows'); assert.equal(big_query_rows_2.length, 26,'big simple query 2 should return 26 rows'); assert.equal(big_query_rows_3.length, 26,'big simple query 3 should return 26 rows'); }); var runBigQuery = function(client) { var rows = []; var q = client.query("select 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' as bla from person where name = $1 or 1 = 1",[''], function(err, result) { if(err != null) { console.log(err); throw Err; } assert.lengthIs(result.rows, 26); }); q.on('row', function(row) { rows.push(row); }) assert.emits(q, 'end', function() { //query ended assert.lengthIs(rows, 26); }) } test('many times', function() { var client = helper.client(); for(var i = 0; i < 20; i++) { runBigQuery(client); } client.on('drain', function() { client.end(); setTimeout(function() { //let client disconnect fully }, 100) }); }) node-postgres-0.13.3/test/integration/client/cancel-query-tests.js000066400000000000000000000021551211611354200252340ustar00rootroot00000000000000var helper = require(__dirname+"/test-helper"); //before running this test make sure you run the script create-test-tables test("cancellation of a query", function() { var client = helper.client(); var qry = "select name from person order by name"; client.on('drain', client.end.bind(client)); var rows1 = 0, rows2 = 0, rows3 = 0, rows4 = 0; var query1 = client.query(qry); query1.on('row', function(row) { rows1++; }); var query2 = client.query(qry); query2.on('row', function(row) { rows2++; }); var query3 = client.query(qry); query3.on('row', function(row) { rows3++; }); var query4 = client.query(qry); query4.on('row', function(row) { rows4++; }); helper.pg.cancel(helper.config, client, query1); helper.pg.cancel(helper.config, client, query2); helper.pg.cancel(helper.config, client, query4); setTimeout(function() { assert.equal(rows1, 0); assert.equal(rows2, 0); assert.equal(rows4, 0); }, 2000); assert.emits(query3, 'end', function() { test("returned right number of rows", function() { assert.equal(rows3, 26); }); }); }); node-postgres-0.13.3/test/integration/client/configuration-tests.js000066400000000000000000000023561211611354200255160ustar00rootroot00000000000000var helper = require(__dirname + '/test-helper'); var pg = helper.pg; //clear process.env var realEnv = {}; for(var key in process.env) { realEnv[key] = process.env[key]; if(!key.indexOf('PG')) delete process.env[key]; } test('default values', function() { assert.same(pg.defaults,{ user: process.env.USER, database: process.env.USER, password: null, port: 5432, rows: 0, poolSize: 10 }) test('are used in new clients', function() { var client = new pg.Client(); assert.same(client,{ user: process.env.USER, database: process.env.USER, password: null, port: 5432 }) }) }) if(!helper.args.native) { test('modified values', function() { pg.defaults.user = 'boom' pg.defaults.password = 'zap' pg.defaults.database = 'pow' pg.defaults.port = 1234 pg.defaults.host = 'blam' pg.defaults.rows = 10 pg.defaults.poolSize = 0 test('are passed into created clients', function() { var client = new Client(); assert.same(client,{ user: 'boom', password: 'zap', database: 'pow', port: 1234, host: 'blam' }) }) }) } //restore process.env for(var key in realEnv) { process.env[key] = realEnv[key]; } node-postgres-0.13.3/test/integration/client/copy-tests.js000066400000000000000000000166061211611354200236240ustar00rootroot00000000000000var helper = require(__dirname + '/../test-helper'); var pg = require(__dirname + '/../../../lib'); 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)', assert.calls(function (err, result) { assert.equal(err, null, "create table query should not fail"); callback(); }) ); }; test('COPY FROM', function () { 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"); stream.on('error', function (error) { assert.ok(false, "COPY FROM stream should not emit errors" + helper.sys.inspect(error)); }); for (var i = 0; i < ROWS_TO_INSERT; i++) { 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", 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, 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"); var buf = new Buffer(0); stream.on('error', function (error) { assert.ok(false, "COPY TO stream should not emit errors" + helper.sys.inspect(error)); }); assert.emits(stream, 'data', function (chunk) { buf = Buffer.concat([buf, chunk]); }, "COPY IN stream should emit data event for each row"); assert.emits(stream, 'end', function () { var lines = buf.toString().split('\n'); assert.equal(lines.length >= 0, true, "copy in should return rows saved by copy from"); assert.equal(lines[0].split(',').length, 3, "each line should consists of 3 fields"); 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 () { var query1Done = false, copyQueryDone = false, query2Done = false; client.query("SELECT count(*) from person", function () { query1Done = true; assert.ok(!copyQueryDone && ! query2Done, "first query has to be executed before others"); }); var stream = client.copyTo("COPY person (id, name, age) TO stdin WITH CSV"); //imitate long query, to make impossible, //that copy query end callback runs after //second query callback 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)); }); assert.emits(stream, 'data', function (chunk) { buf = Buffer.concat([buf, chunk]); }, "COPY IN stream should emit data event for each row"); assert.emits(stream, 'end', function () { copyQueryDone = true; assert.ok(query1Done && ! query2Done, "copy query has to be executed before second query and after first"); var lines = buf.toString().split('\n'); assert.equal(lines.length >= 0, true, "copy in should return rows saved by copy from"); assert.equal(lines[0].split(',').length, 3, "each line should consists of 3 fields"); pg.end(helper.config); }, "COPY IN stream should emit end event after all rows"); }); })); }); 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 //send after copy query ends //so we need to test both situations 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. //this has to report error in standart way, instead of just throwing exception client.query( "COPY (SELECT GENERATE_SERIES(1, 10000000)) TO STDOUT WITH CSV", assert.calls(function (error) { assert.ok(error, "error should be reported when sending copy to query with query method"); client.query("SELECT 1", assert.calls(function (error, result) { assert.isNull(error, "incorrect copy usage should not break connection"); assert.ok(result, "incorrect copy usage should not break connection"); pg.end(helper.config); })); }) ); })); }); 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. //this has to report error in standart way, instead of just throwing exception client.query( "COPY (SELECT GENERATE_SERIES(1, 1)) TO STDOUT WITH CSV", assert.calls(function (error) { assert.ok(error, "error should be reported when sending copy to query with query method"); client.query("SELECT 1", assert.calls(function (error, result) { assert.isNull(error, "incorrect copy usage should not break connection"); assert.ok(result, "incorrect copy usage should not break connection"); pg.end(helper.config); })); }) ); })); }); test("COPY FROM incorrect usage", function () { 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. //this has to report error in standart way, instead of just throwing exception client.query( "COPY copy_test from STDIN WITH CSV", assert.calls(function (error) { assert.ok(error, "error should be reported when sending copy to query with query method"); client.query("SELECT 1", assert.calls(function (error, result) { assert.isNull(error, "incorrect copy usage should not break connection"); assert.ok(result, "incorrect copy usage should not break connection"); pg.end(helper.config); })); }) ); }); }); }); node-postgres-0.13.3/test/integration/client/drain-tests.js000066400000000000000000000031001211611354200237300ustar00rootroot00000000000000var helper = require(__dirname + '/test-helper'); var pg = require(__dirname + '/../../../lib'); if(helper.args.native) { pg = require(__dirname + '/../../../lib').native; } var testDrainOfClientWithPendingQueries = function() { pg.connect(helper.config, assert.success(function(client) { test('when there are pending queries and client is resumed', function() { var drainCount = 0; client.on('drain', function() { drainCount++; }); client.pauseDrain(); client.query('SELECT NOW()', function() { client.query('SELECT NOW()', function() { assert.equal(drainCount, 0); process.nextTick(function() { assert.equal(drainCount, 1); pg.end(); }); }); client.resumeDrain(); assert.equal(drainCount, 0); }); }); })); }; pg.connect(helper.config, assert.success(function(client) { var drainCount = 0; client.on('drain', function() { drainCount++; }); test('pauseDrain and resumeDrain on simple client', function() { client.pauseDrain(); client.resumeDrain(); process.nextTick(assert.calls(function() { assert.equal(drainCount, 0); test('drain is paused', function() { client.pauseDrain(); client.query('SELECT NOW()', assert.success(function() { process.nextTick(function() { assert.equal(drainCount, 0); client.resumeDrain(); assert.equal(drainCount, 1); testDrainOfClientWithPendingQueries(); }); })); }); })); }); })); node-postgres-0.13.3/test/integration/client/empty-query-tests.js000066400000000000000000000006101211611354200251370ustar00rootroot00000000000000var helper = require(__dirname+'/test-helper'); var client = helper.client(); test("empty query message handling", function() { assert.emits(client, 'drain', function() { client.end(); }); client.query({text: ""}); }); test('callback supported', assert.calls(function() { client.query("", function(err, result) { assert.isNull(err); assert.empty(result.rows); }) })) node-postgres-0.13.3/test/integration/client/error-handling-tests.js000066400000000000000000000101051211611354200255510ustar00rootroot00000000000000var helper = require(__dirname + '/test-helper'); var util = require('util'); var createErorrClient = function() { var client = helper.client(); client.on('error', function(err) { assert.ok(false, "client should not throw query error: " + util.inspect(err)); }); client.on('drain', client.end.bind(client)); return client; }; test('error handling', function(){ test('within a simple query', function() { var client = createErorrClient(); var query = client.query("select omfg from yodas_dsflsd where pixistix = 'zoiks!!!'"); assert.emits(query, 'error', function(error) { test('error is a psql error', function() { assert.equal(error.severity, "ERROR"); }); }); }); test('within a prepared statement', function() { var client = createErorrClient(); var q = client.query({text: "CREATE TEMP TABLE boom(age integer); INSERT INTO boom (age) VALUES (28);", binary: false}); test("when query is parsing", function() { //this query wont parse since there ain't no table named bang var ensureFuture = function(testClient) { test("client can issue more queries successfully", function() { var goodQuery = testClient.query("select age from boom"); assert.emits(goodQuery, 'row', function(row) { assert.equal(row.age, 28); }); }); }; var query = client.query({ text: "select * from bang where name = $1", values: ['0'] }); test("query emits the error", function() { assert.emits(query, 'error', function(err) { ensureFuture(client); }); }); test("when a query is binding", function() { var query = client.query({ text: 'select * from boom where age = $1', values: ['asldkfjasdf'] }); test("query emits the error", function() { assert.emits(query, 'error', function(err) { test('error has right severity', function() { assert.equal(err.severity, "ERROR"); }) ensureFuture(client); }); }); //TODO how to test for errors during execution? }); }); }); test('non-query error', function() { var client = new Client({ user:'asldkfjsadlfkj' }); assert.emits(client, 'error'); client.connect(); }); test('non-query error with callback', function() { var client = new Client({ user:'asldkfjsadlfkj' }); client.connect(assert.calls(function(error, client) { assert.ok(error); })); }); }); test('non-error calls supplied callback', function() { var client = new Client({ user: helper.args.user, password: helper.args.password, host: helper.args.host, port: helper.args.port, database: helper.args.database }); client.connect(assert.calls(function(err) { assert.isNull(err); client.end(); })) }); test('when connecting to invalid host', function() { return false; var client = new Client({ user: 'aslkdjfsdf', password: '1234', host: 'asldkfjasdf!!#1308140.com' }); assert.emits(client, 'error'); client.connect(); }); test('when connecting to invalid host with callback', function() { return false; var client = new Client({ user: 'brian', password: '1234', host: 'asldkfjasdf!!#1308140.com' }); client.connect(function(error, client) { assert.ok(error); }); }); test('multiple connection errors (gh#31)', function() { return false; test('with single client', function() { //don't run yet...this test fails...need to think of fix var client = new Client({ user: 'blaksdjf', password: 'omfsadfas', host: helper.args.host, port: helper.args.port, database: helper.args.database }); client.connect(); assert.emits(client, 'error', function(e) { client.connect(); assert.emits(client, 'error'); }); }); test('with callback method', function() { var badConString = "tcp://aslkdfj:oi14081@"+helper.args.host+":"+helper.args.port+"/"+helper.args.database; return false; }); }); node-postgres-0.13.3/test/integration/client/huge-numeric-tests.js000066400000000000000000000012601211611354200252300ustar00rootroot00000000000000var helper = require(__dirname + '/test-helper'); helper.pg.connect(helper.config, assert.success(function(client) { var types = require(__dirname + '/../../../lib/types'); //1231 = numericOID types.setTypeParser(1700, function(){ return 'yes'; }) types.setTypeParser(1700, 'binary', function(){ return 'yes'; }) var bignum = '294733346389144765940638005275322203805'; client.query('CREATE TEMP TABLE bignumz(id numeric)'); client.query('INSERT INTO bignumz(id) VALUES ($1)', [bignum]); client.query('SELECT * FROM bignumz', assert.success(function(result) { assert.equal(result.rows[0].id, 'yes') helper.pg.end(); })) })); //custom type converter node-postgres-0.13.3/test/integration/client/no-data-tests.js000066400000000000000000000014551211611354200241710ustar00rootroot00000000000000var helper = require(__dirname + '/test-helper'); test("noData message handling", function() { var client = helper.client(); var q = client.query({ name: 'boom', text: 'create temp table boom(id serial, size integer)' }); client.query({ name: 'insert', text: 'insert into boom(size) values($1)', values: [100] }, function(err, result) { if(err) { console.log(err); throw err; } }); client.query({ name: 'insert', text: 'insert into boom(size) values($1)', values: [101] }); var query = client.query({ name: 'fetch', text: 'select size from boom where size < $1', values: [101] }); assert.emits(query, 'row', function(row) { assert.strictEqual(row.size,100) }); client.on('drain', client.end.bind(client)); }); node-postgres-0.13.3/test/integration/client/notice-tests.js000066400000000000000000000026731211611354200241320ustar00rootroot00000000000000var helper = require(__dirname + '/test-helper'); test('emits notice message', function() { //TODO this doesn't work on all versions of postgres return false; var client = helper.client(); client.query('create temp table boom(id serial, size integer)'); assert.emits(client, 'notice', function(notice) { assert.ok(notice != null); //TODO ending connection after notice generates weird errors process.nextTick(function() { client.end(); }) }); }) test('emits notify message', function() { var client = helper.client(); client.query('LISTEN boom', assert.calls(function() { var otherClient = helper.client(); otherClient.query('LISTEN boom', assert.calls(function() { assert.emits(client, 'notification', function(msg) { //make sure PQfreemem doesn't invalidate string pointers setTimeout(function() { assert.equal(msg.channel, 'boom'); assert.ok(msg.payload == 'omg!' /*9.x*/ || msg.payload == '' /*8.x*/, "expected blank payload or correct payload but got " + msg.message) client.end() }, 100) }); assert.emits(otherClient, 'notification', function(msg) { assert.equal(msg.channel, 'boom'); otherClient.end(); }); client.query("NOTIFY boom, 'omg!'", function(err, q) { if(err) { //notify not supported with payload on 8.x client.query("NOTIFY boom") } }); })); })); }) node-postgres-0.13.3/test/integration/client/prepared-statement-tests.js000066400000000000000000000105501211611354200264460ustar00rootroot00000000000000var helper = require(__dirname +'/test-helper'); test("simple, unnamed prepared statement", function(){ var client = helper.client(); var query = client.query({ text: 'select age from person where name = $1', values: ['Brian'] }); assert.emits(query, 'row', function(row) { assert.equal(row.age, 20); }); assert.emits(query, 'end', function() { client.end(); }); }); test("named prepared statement", function() { var client = helper.client(); client.on('drain', client.end.bind(client)); var queryName = "user by age and like name"; var parseCount = 0; test("first named prepared statement",function() { var query = client.query({ text: 'select name from person where age <= $1 and name LIKE $2', values: [20, 'Bri%'], name: queryName }); assert.emits(query, 'row', function(row) { assert.equal(row.name, 'Brian'); }); assert.emits(query, 'end', function() { }); }); test("second named prepared statement with same name & text", function() { var cachedQuery = client.query({ text: 'select name from person where age <= $1 and name LIKE $2', name: queryName, values: [10, 'A%'] }); assert.emits(cachedQuery, 'row', function(row) { assert.equal(row.name, 'Aaron'); }); assert.emits(cachedQuery, 'end', function() { }); }); test("with same name, but the query text not even there batman!", function() { var q = client.query({ name: queryName, values: [30, '%n%'] }); test("gets first row", function() { assert.emits(q, 'row', function(row) { assert.equal(row.name, "Aaron"); test("gets second row", function() { assert.emits(q, 'row', function(row) { assert.equal(row.name, "Brian"); }); }); }); }); assert.emits(q, 'end', function() { }); }); }); test("prepared statements on different clients", function() { var statementName = "differ"; var statement1 = "select count(*) as count from person"; var statement2 = "select count(*) as count from person where age < $1"; var client1Finished = false; var client2Finished = false; var client1 = helper.client(); var client2 = helper.client(); test("client 1 execution", function() { var query = client1.query({ name: statementName, text: statement1 }); test('gets right data back', function() { assert.emits(query, 'row', function(row) { assert.equal(row.count, 26); }); }); assert.emits(query, 'end', function() { if(client2Finished) { client1.end(); client2.end(); } else { client1Finished = true; } }); }); test('client 2 execution', function() { var query = client2.query({ name: statementName, text: statement2, values: [11] }); test('gets right data', function() { assert.emits(query, 'row', function(row) { assert.equal(row.count, 1); }); }); assert.emits(query, 'end', function() { if(client1Finished) { client1.end(); client2.end(); } else { client2Finished = true; } }); }); }); test('prepared statement', function() { var client = helper.client(); client.on('drain', client.end.bind(client)); client.query('CREATE TEMP TABLE zoom(name varchar(100));'); client.query("INSERT INTO zoom (name) VALUES ('zed')"); client.query("INSERT INTO zoom (name) VALUES ('postgres')"); client.query("INSERT INTO zoom (name) VALUES ('node postgres')"); var checkForResults = function(q) { test('row callback fires for each result', function() { assert.emits(q, 'row', function(row) { assert.equal(row.name, 'node postgres'); assert.emits(q, 'row', function(row) { assert.equal(row.name, 'postgres'); assert.emits(q, 'row', function(row) { assert.equal(row.name, 'zed'); }) }); }) }) }; test('with small row count', function() { var query = client.query({ name: 'get names', text: "SELECT name FROM zoom ORDER BY name", rows: 1 }); checkForResults(query); }) test('with large row count', function() { var query = client.query({ name: 'get names', text: 'SELECT name FROM zoom ORDER BY name', rows: 1000 }) checkForResults(query); }) }) node-postgres-0.13.3/test/integration/client/result-metadata-tests.js000066400000000000000000000020601211611354200257330ustar00rootroot00000000000000var helper = require(__dirname + "/test-helper"); var pg = helper.pg; test('should return insert metadata', function() { pg.connect(helper.config, assert.calls(function(err, client) { assert.isNull(err); client.query("CREATE TEMP TABLE zugzug(name varchar(10))", assert.calls(function(err, result) { assert.isNull(err); assert.equal(result.oid, null); assert.equal(result.command, 'CREATE'); var q = client.query("INSERT INTO zugzug(name) VALUES('more work?')", assert.calls(function(err, result) { assert.equal(result.command, "INSERT"); assert.equal(result.rowCount, 1); client.query('SELECT * FROM zugzug', assert.calls(function(err, result) { assert.isNull(err); assert.equal(result.rowCount, 1); assert.equal(result.command, 'SELECT'); process.nextTick(pg.end.bind(pg)); })); })); assert.emits(q, 'end', function(result) { assert.equal(result.command, "INSERT"); assert.equal(result.rowCount, 1); }); })); })); }); node-postgres-0.13.3/test/integration/client/simple-query-tests.js000066400000000000000000000043551211611354200253040ustar00rootroot00000000000000var helper = require(__dirname+"/test-helper"); //before running this test make sure you run the script create-test-tables test("simple query interface", function() { var client = helper.client(); var query = client.query("select name from person order by name"); client.on('drain', client.end.bind(client)); var rows = []; query.on('row', function(row, result) { assert.ok(result); rows.push(row['name']); }); query.once('row', function(row) { test('Can iterate through columns', function () { var columnCount = 0; for (column in row) { columnCount++; } if ('length' in row) { assert.lengthIs(row, columnCount, 'Iterating through the columns gives a different length from calling .length.'); } }); }); assert.emits(query, 'end', function() { test("returned right number of rows", function() { assert.lengthIs(rows, 26); }); test("row ordering", function(){ assert.equal(rows[0], "Aaron"); assert.equal(rows[25], "Zanzabar"); }); }); }); test("multiple simple queries", function() { var client = helper.client(); client.query({ text: "create temp table bang(id serial, name varchar(5));insert into bang(name) VALUES('boom');"}) client.query("insert into bang(name) VALUES ('yes');"); var query = client.query("select name from bang"); assert.emits(query, 'row', function(row) { assert.equal(row['name'], 'boom'); assert.emits(query, 'row', function(row) { assert.equal(row['name'],'yes'); }); }); client.on('drain', client.end.bind(client)); }); test("multiple select statements", function() { var client = helper.client(); client.query("create temp table boom(age integer); insert into boom(age) values(1); insert into boom(age) values(2); insert into boom(age) values(3)"); client.query({text: "create temp table bang(name varchar(5)); insert into bang(name) values('zoom');"}); var result = client.query({text: "select age from boom where age < 2; select name from bang"}); assert.emits(result, 'row', function(row) { assert.strictEqual(row['age'], 1); assert.emits(result, 'row', function(row) { assert.strictEqual(row['name'], 'zoom'); }); }); client.on('drain', client.end.bind(client)); }); node-postgres-0.13.3/test/integration/client/ssl-tests.js000066400000000000000000000006001211611354200234360ustar00rootroot00000000000000var pg = require(__dirname + '/../../../lib'); var config = require(__dirname + '/test-helper').config; test('can connect with ssl', function() { return false; config.ssl = { rejectUnauthorized: false }; pg.connect(config, assert.success(function(client) { return false; client.query('SELECT NOW()', assert.success(function() { pg.end(); })); })); }); node-postgres-0.13.3/test/integration/client/test-helper.js000066400000000000000000000001151211611354200237320ustar00rootroot00000000000000var helper = require(__dirname+'/../test-helper'); module.exports = helper; node-postgres-0.13.3/test/integration/client/transaction-tests.js000066400000000000000000000035111211611354200251660ustar00rootroot00000000000000var helper = require(__dirname + '/test-helper'); var sink = new helper.Sink(2, function() { helper.pg.end(); }); test('a single connection transaction', function() { helper.pg.connect(helper.config, assert.calls(function(err, client) { assert.isNull(err); client.query('begin'); var getZed = { text: 'SELECT * FROM person WHERE name = $1', values: ['Zed'] }; test('Zed should not exist in the database', function() { client.query(getZed, assert.calls(function(err, result) { assert.isNull(err); assert.empty(result.rows); })) }) client.query("INSERT INTO person(name, age) VALUES($1, $2)", ['Zed', 270], assert.calls(function(err, result) { assert.isNull(err) })); test('Zed should exist in the database', function() { client.query(getZed, assert.calls(function(err, result) { assert.isNull(err); assert.equal(result.rows[0].name, 'Zed'); })) }) client.query('rollback'); test('Zed should not exist in the database', function() { client.query(getZed, assert.calls(function(err, result) { assert.isNull(err); assert.empty(result.rows); sink.add(); })) }) })) }) test('gh#36', function() { helper.pg.connect(helper.config, function(err, client) { if(err) throw err; client.query("BEGIN"); client.query({ name: 'X', text: "SELECT $1::INTEGER", values: [0] }, assert.calls(function(err, result) { if(err) throw err; assert.equal(result.rows.length, 1); })) client.query({ name: 'X', text: "SELECT $1::INTEGER", values: [0] }, assert.calls(function(err, result) { if(err) throw err; assert.equal(result.rows.length, 1); })) client.query("COMMIT", function() { sink.add(); }) }) }) node-postgres-0.13.3/test/integration/client/type-coercion-tests.js000066400000000000000000000104011211611354200254150ustar00rootroot00000000000000var helper = require(__dirname + '/test-helper'); var sink; var testForTypeCoercion = function(type){ helper.pg.connect(helper.config, function(err, client) { assert.isNull(err); client.query("create temp table test_type(col " + type.name + ")", assert.calls(function(err, result) { assert.isNull(err); test("Coerces " + type.name, function() { type.values.forEach(function(val) { var insertQuery = client.query('insert into test_type(col) VALUES($1)',[val],assert.calls(function(err, result) { assert.isNull(err); })); var query = client.query({ name: 'get type ' + type.name , text: 'select col from test_type' }); query.on('error', function(err) { console.log(err); throw err; }); assert.emits(query, 'row', function(row) { assert.strictEqual(row.col, val, "expected " + type.name + " of " + val + " but got " + row.col); }, "row should have been called for " + type.name + " of " + val); client.query('delete from test_type'); }); client.query('drop table test_type', function() { sink.add(); }); }) })); }) }; var types = [{ name: 'integer', values: [1, -1, null] },{ name: 'smallint', values: [-1, 0, 1, null] },{ name: 'bigint', values: [-10000, 0, 10000, null] },{ name: 'varchar(5)', values: ['yo', '', 'zomg!', null] },{ name: 'oid', values: [0, 204410, null] },{ name: 'bool', values: [true, false, null] },{ //TODO get some actual huge numbers here name: 'numeric', values: [-12.34, 0, 12.34, null] },{ name: 'real', values: [101.1, 0, -101.3, null] },{ name: 'double precision', values: [-1.2, 0, 1.2, null] },{ name: 'timestamptz', values: [null] },{ name: 'timestamp', values: [null] },{ name: 'timetz', values: ['13:11:12.1234-05:30',null] },{ name: 'time', values: ['13:12:12.321', null] }]; // ignore some tests in binary mode if (helper.config.binary) { types = types.filter(function(type) { return !(type.name in {'real':1, 'timetz':1, 'time':1}); }); } var valueCount = 0; types.forEach(function(type) { valueCount += type.values.length; }) sink = new helper.Sink(types.length + 1, function() { helper.pg.end(); }) types.forEach(function(type) { testForTypeCoercion(type) }); test("timestampz round trip", function() { var now = new Date(); var client = helper.client(); client.on('error', function(err) { console.log(err); client.end(); }); client.query("create temp table date_tests(name varchar(10), tstz timestamptz(3))"); client.query({ text: "insert into date_tests(name, tstz)VALUES($1, $2)", name: 'add date', values: ['now', now] }); var result = client.query({ name: 'get date', text: 'select * from date_tests where name = $1', values: ['now'] }); assert.emits(result, 'row', function(row) { var date = row.tstz; assert.equal(date.getYear(),now.getYear()); assert.equal(date.getMonth(), now.getMonth()); assert.equal(date.getDate(), now.getDate()); assert.equal(date.getHours(), now.getHours()); assert.equal(date.getMinutes(), now.getMinutes()); assert.equal(date.getSeconds(), now.getSeconds()); test("milliseconds are equal", function() { assert.equal(date.getMilliseconds(), now.getMilliseconds()); }); }); client.on('drain', client.end.bind(client)); }); helper.pg.connect(helper.config, assert.calls(function(err, client) { assert.isNull(err); client.query('select null as res;', assert.calls(function(err, res) { assert.isNull(err); assert.strictEqual(res.rows[0].res, null) })) client.query('select 7 <> $1 as res;',[null], function(err, res) { assert.isNull(err); assert.strictEqual(res.rows[0].res, null); sink.add(); }) })) if(!helper.config.binary) { test("postgres date type", function() { var client = helper.client(); client.on('error', function(err) { console.log(err); client.end(); }); client.query("SELECT '2010-10-31'::date", assert.calls(function(err, result){ assert.isNull(err); assert.UTCDate(result.rows[0].date, 2010, 9, 31, 0, 0, 0, 0); })); client.on('drain', client.end.bind(client)); }); } node-postgres-0.13.3/test/integration/connection-pool/000077500000000000000000000000001211611354200227735ustar00rootroot00000000000000node-postgres-0.13.3/test/integration/connection-pool/double-connection-tests.js000066400000000000000000000001111211611354200300710ustar00rootroot00000000000000var helper = require(__dirname + "/test-helper") helper.testPoolSize(2); node-postgres-0.13.3/test/integration/connection-pool/ending-pool-tests.js000066400000000000000000000012321211611354200267020ustar00rootroot00000000000000var helper = require(__dirname + '/test-helper') var called = false; test('disconnects', function() { var sink = new helper.Sink(4, function() { called = true; //this should exit the process, killing each connection pool helper.pg.end(); }); [helper.config, helper.config, helper.config, helper.config].forEach(function(config) { helper.pg.connect(config, function(err, client) { assert.isNull(err); client.query("SELECT * FROM NOW()", function(err, result) { process.nextTick(function() { assert.equal(called, false, "Should not have disconnected yet") sink.add(); }) }) }) }) }) node-postgres-0.13.3/test/integration/connection-pool/error-tests.js000066400000000000000000000017351211611354200256300ustar00rootroot00000000000000var helper = require(__dirname + "/../test-helper"); var pg = require(__dirname + "/../../../lib"); helper.pg = pg; //first make pool hold 2 clients helper.pg.defaults.poolSize = 2; var killIdleQuery = 'SELECT procpid, (SELECT pg_terminate_backend(procpid)) AS killed FROM pg_stat_activity WHERE current_query LIKE \'\''; //get first client helper.pg.connect(helper.config, assert.success(function(client) { client.id = 1; helper.pg.connect(helper.config, assert.success(function(client2) { client2.id = 2; //subscribe to the pg error event assert.emits(helper.pg, 'error', function(error, brokenClient) { assert.ok(error); assert.ok(brokenClient); assert.equal(client.id, brokenClient.id); helper.pg.end(); }); //kill the connection from client client2.query(killIdleQuery, assert.success(function(res) { //check to make sure client connection actually was killed assert.lengthIs(res.rows, 1); })); })); })); node-postgres-0.13.3/test/integration/connection-pool/idle-timeout-tests.js000066400000000000000000000005241211611354200270730ustar00rootroot00000000000000var helper = require(__dirname + '/test-helper'); helper.pg.defaults.poolIdleTimeout = 200; test('idle timeout', function() { helper.pg.connect(helper.config, assert.calls(function(err, client) { assert.isNull(err); client.query('SELECT NOW()'); //just let this one time out //test will hang if pool doesn't timeout })); }); node-postgres-0.13.3/test/integration/connection-pool/max-connection-tests.js000066400000000000000000000001431211611354200274110ustar00rootroot00000000000000var helper = require(__dirname + "/test-helper") helper.testPoolSize(10); helper.testPoolSize(11); node-postgres-0.13.3/test/integration/connection-pool/optional-config-tests.js000066400000000000000000000010641211611354200275620ustar00rootroot00000000000000var helper = require(__dirname + '/test-helper'); //setup defaults helper.pg.defaults.user = helper.args.user; helper.pg.defaults.password = helper.args.password; helper.pg.defaults.host = helper.args.host; helper.pg.defaults.port = helper.args.port; helper.pg.defaults.database = helper.args.database; helper.pg.defaults.poolSize = 1; helper.pg.connect(assert.calls(function(err, client) { assert.isNull(err); client.query('SELECT NOW()'); client.once('drain', function() { setTimeout(function() { helper.pg.end(); }, 10); }); })); node-postgres-0.13.3/test/integration/connection-pool/single-connection-tests.js000066400000000000000000000001111211611354200301000ustar00rootroot00000000000000var helper = require(__dirname + "/test-helper") helper.testPoolSize(1); node-postgres-0.13.3/test/integration/connection-pool/test-helper.js000066400000000000000000000016401211611354200255660ustar00rootroot00000000000000var helper = require(__dirname + "/../test-helper"); helper.testPoolSize = function(max) { var sink = new helper.Sink(max, function() { helper.pg.end(); }); test("can pool " + max + " times", function() { for(var i = 0; i < max; i++) { helper.pg.poolSize = 10; test("connection #" + i + " executes", function() { helper.pg.connect(helper.config, function(err, client, done) { assert.isNull(err); client.query("select * from person", function(err, result) { assert.lengthIs(result.rows, 26) }) client.query("select count(*) as c from person", function(err, result) { assert.equal(result.rows[0].c, 26) }) var query = client.query("SELECT * FROM NOW()") query.on('end',function() { sink.add(); done(); }) }) }) } }) } module.exports = helper; node-postgres-0.13.3/test/integration/connection-pool/waiting-connection-tests.js000066400000000000000000000001131211611354200302630ustar00rootroot00000000000000var helper = require(__dirname + "/test-helper") helper.testPoolSize(200); node-postgres-0.13.3/test/integration/connection/000077500000000000000000000000001211611354200220245ustar00rootroot00000000000000node-postgres-0.13.3/test/integration/connection/bound-command-tests.js000066400000000000000000000024771211611354200262570ustar00rootroot00000000000000var helper = require(__dirname + '/test-helper'); http://developer.postgresql.org/pgdocs/postgres/protocol-flow.html#PROTOCOL-FLOW-EXT-QUERY test('flushing once', function() { helper.connect(function(con) { con.parse({ text: 'select * from ids' }); con.bind(); con.execute(); con.flush(); assert.emits(con, 'parseComplete'); assert.emits(con, 'bindComplete'); assert.emits(con, 'dataRow'); assert.emits(con, 'commandComplete', function(){ con.sync(); }); assert.emits(con, 'readyForQuery', function(){ con.end(); }); }); }); test("sending many flushes", function() { helper.connect(function(con) { assert.emits(con, 'parseComplete', function(){ con.bind(); con.flush(); }); assert.emits(con, 'bindComplete', function(){ con.execute(); con.flush(); }); assert.emits(con, 'dataRow', function(msg){ assert.equal(msg.fields[0], 1); assert.emits(con, 'dataRow', function(msg){ assert.equal(msg.fields[0], 2); assert.emits(con, 'commandComplete', function(){ con.sync(); }); assert.emits(con, 'readyForQuery', function(){ con.end(); }); }); }); con.parse({ text: "select * from ids order by id" }); con.flush(); }); }); node-postgres-0.13.3/test/integration/connection/copy-tests.js000066400000000000000000000022611211611354200244750ustar00rootroot00000000000000var helper = require(__dirname+"/test-helper"); var assert = require('assert'); test('COPY FROM events check', function () { helper.connect(function (con) { var stdinStream = con.query('COPY person FROM STDIN'); con.on('copyInResponse', function () { con.endCopyFrom(); }); assert.emits(con, 'copyInResponse', function () { con.endCopyFrom(); }, "backend should emit copyInResponse after COPY FROM query" ); assert.emits(con, 'commandComplete', function () { con.end(); }, "backend should emit commandComplete after COPY FROM stream ends" ) }); }); test('COPY TO events check', function () { helper.connect(function (con) { var stdoutStream = con.query('COPY person TO STDOUT'); assert.emits(con, 'copyOutResponse', function () { }, "backend should emit copyOutResponse after COPY TO query" ); assert.emits(con, 'copyData', function () { }, "backend should emit copyData on every data row" ); assert.emits(con, 'copyDone', function () { con.end(); }, "backend should emit copyDone after all data rows" ); }); }); node-postgres-0.13.3/test/integration/connection/notification-tests.js000066400000000000000000000010131211611354200262030ustar00rootroot00000000000000var helper = require(__dirname + '/test-helper'); //http://www.postgresql.org/docs/8.3/static/libpq-notify.html test('recieves notification from same connection with no payload', function() { helper.connect(function(con) { con.query('LISTEN boom'); assert.emits(con, 'readyForQuery', function() { con.query("NOTIFY boom"); assert.emits(con, 'notification', function(msg) { assert.equal(msg.payload, ""); assert.equal(msg.channel, 'boom') con.end(); }); }); }); }); node-postgres-0.13.3/test/integration/connection/query-tests.js000066400000000000000000000013041211611354200246650ustar00rootroot00000000000000var helper = require(__dirname+"/test-helper"); var assert = require('assert'); var rows = []; //testing the low level 1-1 mapping api of client to postgres messages //it's cumbersome to use the api this way test('simple query', function() { helper.connect(function(con) { con.query('select * from ids'); assert.emits(con, 'dataRow'); con.on('dataRow', function(msg) { rows.push(msg.fields); }); assert.emits(con, 'readyForQuery', function() { con.end(); }); }); }); process.on('exit', function() { assert.equal(rows.length, 2); assert.equal(rows[0].length, 1); assert.strictEqual(String(rows[0] [0]), '1'); assert.strictEqual(String(rows[1] [0]), '2'); }); node-postgres-0.13.3/test/integration/connection/test-helper.js000066400000000000000000000026411211611354200246210ustar00rootroot00000000000000var net = require('net'); var helper = require(__dirname+'/../test-helper'); var Connection = require(__dirname + '/../../../lib/connection'); var connect = function(callback) { var username = helper.args.user; var database = helper.args.database; var con = new Connection({stream: new net.Stream()}); con.on('error', function(error){ console.log(error); throw new Error("Connection error"); }); con.connect(helper.args.port || '5432', helper.args.host || 'localhost'); con.once('connect', function() { con.startup({ user: username, database: database }); con.once('authenticationCleartextPassword', function(){ con.password(helper.args.password); }); con.once('authenticationMD5Password', function(msg){ //need js client even if native client is included var client = require(__dirname +"/../../../lib/client"); var inner = client.md5(helper.args.password+helper.args.user); var outer = client.md5(inner + msg.salt.toString('binary')); con.password("md5"+outer); }); con.once('readyForQuery', function() { con.query('create temp table ids(id integer)'); con.once('readyForQuery', function() { con.query('insert into ids(id) values(1); insert into ids(id) values(2);'); con.once('readyForQuery', function() { callback(con); }); }); }); }); }; module.exports = { connect: connect }; node-postgres-0.13.3/test/integration/gh-issues/000077500000000000000000000000001211611354200215745ustar00rootroot00000000000000node-postgres-0.13.3/test/integration/gh-issues/130.js000066400000000000000000000010371211611354200224360ustar00rootroot00000000000000var helper = require(__dirname + '/../test-helper'); var exec = require('child_process').exec; helper.pg.defaults.poolIdleTimeout = 1000; helper.pg.connect(helper.config, function(err,client) { client.query("SELECT pg_backend_pid()", function(err, result) { var pid = result.rows[0].pg_backend_pid; exec('psql -c "select pg_terminate_backend('+pid+')" template1', assert.calls(function (error, stdout, stderr) { assert.isNull(error); })); }); }); helper.pg.on('error', function(err, client) { //swallow errors }); node-postgres-0.13.3/test/integration/gh-issues/131.js000066400000000000000000000015331211611354200224400ustar00rootroot00000000000000var helper = require(__dirname + "/../test-helper"); var pg = helper.pg; test('parsing array results', function() { pg.connect(helper.config, assert.calls(function(err, client) { assert.isNull(err); client.query("CREATE TEMP TABLE why(names text[], numbors integer[], decimals double precision[])"); client.query('INSERT INTO why(names, numbors, decimals) VALUES(\'{"aaron", "brian","a b c" }\', \'{1, 2, 3}\', \'{.1, 0.05, 3.654}\')').on('error', console.log); test('decimals', function() { client.query('SELECT decimals FROM why', assert.success(function(result) { assert.lengthIs(result.rows[0].decimals, 3); assert.equal(result.rows[0].decimals[0], 0.1); assert.equal(result.rows[0].decimals[1], 0.05); assert.equal(result.rows[0].decimals[2], 3.654); pg.end(); })) }) })) }) node-postgres-0.13.3/test/integration/test-helper.js000066400000000000000000000006201211611354200224550ustar00rootroot00000000000000var helper = require(__dirname + '/../test-helper'); if(helper.args.native) { Client = require(__dirname + '/../../lib/native'); helper.Client = Client; helper.pg = helper.pg.native; } //creates a client from cli parameters helper.client = function() { var client = new Client(helper.config); client.connect(); return client; }; //export parent helper stuffs module.exports = helper; node-postgres-0.13.3/test/native/000077500000000000000000000000001211611354200166305ustar00rootroot00000000000000node-postgres-0.13.3/test/native/callback-api-tests.js000066400000000000000000000010771211611354200226360ustar00rootroot00000000000000var helper = require(__dirname + "/../test-helper"); var Client = require(__dirname + "/../../lib/native"); test('fires callback with results', function() { var client = new Client(helper.config); client.connect(); client.query('SELECT 1 as num', assert.calls(function(err, result) { assert.isNull(err); assert.equal(result.rows[0].num, 1); client.query('SELECT * FROM person WHERE name = $1', ['Brian'], assert.calls(function(err, result) { assert.isNull(err); assert.equal(result.rows[0].name, 'Brian'); client.end(); })) })); }) node-postgres-0.13.3/test/native/connection-tests.js000066400000000000000000000011231211611354200224620ustar00rootroot00000000000000var helper = require(__dirname + "/../test-helper"); var Client = require(__dirname + "/../../lib/native"); test('connecting with wrong parameters', function() { var con = new Client("user=asldfkj hostaddr=127.0.0.1 port=5432 dbname=asldkfj"); assert.emits(con, 'error', function(error) { assert.ok(error != null, "error should not be null"); con.end(); }); con.connect(); }); test('connects', function() { var con = new Client(helper.config); con.connect(); assert.emits(con, 'connect', function() { test('disconnects', function() { con.end(); }) }) }) node-postgres-0.13.3/test/native/copy-events-tests.js000066400000000000000000000022441211611354200226040ustar00rootroot00000000000000var helper = require(__dirname+"/../test-helper"); var Client = require(__dirname + "/../../lib/native"); test('COPY FROM events check', function () { var con = new Client(helper.config), stdinStream = con.copyFrom('COPY person FROM STDIN'); assert.emits(con, 'copyInResponse', function () { stdinStream.end(); }, "backend should emit copyInResponse after COPY FROM query" ); assert.emits(con, '_readyForQuery', function () { con.end(); }, "backend should emit _readyForQuery after data will be coped to stdin stream" ); con.connect(); }); test('COPY TO events check', function () { var con = new Client(helper.config), stdoutStream = con.copyTo('COPY person TO STDOUT'); assert.emits(con, 'copyOutResponse', function () {}, "backend should emit copyOutResponse on copyOutResponse message from server" ); assert.emits(con, 'copyData', function () { }, "backend should emit copyData on every data row" ); assert.emits(con, '_readyForQuery', function () { con.end(); }, "backend should emit _readyForQuery after data will be coped to stdout stream" ); con.connect(); }); node-postgres-0.13.3/test/native/copyto-largedata-tests.js000066400000000000000000000020341211611354200235640ustar00rootroot00000000000000var helper = require(__dirname+"/../test-helper"); var Client = require(__dirname + "/../../lib/native"); test("COPY TO large amount of data from postgres", function () { //there were a bug in native implementation of COPY TO: //if there were too much data (if we face situation //when data is not ready while calling PQgetCopyData); //while loop in Connection::HandleIOEvent becomes infinite //in such way hanging node, consumes 100% cpu, and making connection unusable var con = new Client(helper.config), rowCount = 100000, stdoutStream = con.copyTo('COPY (select generate_series(1, ' + rowCount + ')) TO STDOUT'); stdoutStream.on('data', function () { rowCount--; }); stdoutStream.on('end', function () { assert.equal(rowCount, 0, "copy to should load exactly requested number of rows"); con.query("SELECT 1", assert.calls(function (error, result) { assert.ok(!error && result, "loading large amount of data by copy to should not break connection"); con.end(); })); }); con.connect(); }); node-postgres-0.13.3/test/native/error-tests.js000066400000000000000000000025631211611354200214650ustar00rootroot00000000000000var helper = require(__dirname + "/../test-helper"); var Client = require(__dirname + "/../../lib/native"); test('query with non-text as first parameter throws error', function() { var client = new Client(helper.config); client.connect(); assert.emits(client, 'connect', function() { assert.throws(function() { client.query({text:{fail: true}}); }) client.end(); }) }) test('parameterized query with non-text as first parameter throws error', function() { var client = new Client(helper.config); client.connect(); assert.emits(client, 'connect', function() { assert.throws(function() { client.query({ text: {fail: true}, values: [1, 2] }) }) client.end(); }) }) var connect = function(callback) { var client = new Client(helper.config); client.connect(); assert.emits(client, 'connect', function() { callback(client); }) } test('parameterized query with non-array for second value', function() { test('inline', function() { connect(function(client) { assert.throws(function() { client.query("SELECT *", "LKSDJF") }) client.end(); }) }) test('config', function() { connect(function(client) { assert.throws(function() { client.query({ text: "SELECT *", values: "ALSDKFJ" }) }) client.end(); }) }) }) node-postgres-0.13.3/test/native/evented-api-tests.js000066400000000000000000000066071211611354200225400ustar00rootroot00000000000000var helper = require(__dirname + "/../test-helper"); var Client = require(__dirname + "/../../lib/native"); var setupClient = function() { var client = new Client(helper.config); client.connect(); client.query("CREATE TEMP TABLE boom(name varchar(10), age integer)"); client.query("INSERT INTO boom(name, age) VALUES('Aaron', 26)"); client.query("INSERT INTO boom(name, age) VALUES('Brian', 28)"); return client; } test('connects', function() { var client = new Client(helper.config); client.connect(); test('good query', function() { var query = client.query("SELECT 1 as num, 'HELLO' as str"); assert.emits(query, 'row', function(row) { test('has integer data type', function() { assert.strictEqual(row.num, 1); }) test('has string data type', function() { assert.strictEqual(row.str, "HELLO") }) test('emits end AFTER row event', function() { assert.emits(query, 'end'); test('error query', function() { var query = client.query("LSKDJF"); assert.emits(query, 'error', function(err) { assert.ok(err != null, "Should not have emitted null error"); client.end(); }) }) }) }) }) }) test('multiple results', function() { test('queued queries', function() { var client = setupClient(); var q = client.query("SELECT name FROM BOOM"); assert.emits(q, 'row', function(row) { assert.equal(row.name, 'Aaron'); assert.emits(q, 'row', function(row) { assert.equal(row.name, "Brian"); }) }) assert.emits(q, 'end', function() { test('query with config', function() { var q = client.query({text:'SELECT 1 as num'}); assert.emits(q, 'row', function(row) { assert.strictEqual(row.num, 1); assert.emits(q, 'end', function() { client.end(); }) }) }) }) }) }) test('parameterized queries', function() { test('with a single string param', function() { var client = setupClient(); var q = client.query("SELECT * FROM boom WHERE name = $1", ['Aaron']); assert.emits(q, 'row', function(row) { assert.equal(row.name, 'Aaron'); }) assert.emits(q, 'end', function() { client.end(); }); }) test('with object config for query', function() { var client = setupClient(); var q = client.query({ text: "SELECT name FROM boom WHERE name = $1", values: ['Brian'] }); assert.emits(q, 'row', function(row) { assert.equal(row.name, 'Brian'); }) assert.emits(q, 'end', function() { client.end(); }) }) test('multiple parameters', function() { var client = setupClient(); var q = client.query('SELECT name FROM boom WHERE name = $1 or name = $2 ORDER BY name', ['Aaron', 'Brian']); assert.emits(q, 'row', function(row) { assert.equal(row.name, 'Aaron'); assert.emits(q, 'row', function(row) { assert.equal(row.name, 'Brian'); assert.emits(q, 'end', function() { client.end(); }) }) }) }) test('integer parameters', function() { var client = setupClient(); var q = client.query('SELECT * FROM boom WHERE age > $1', [27]); assert.emits(q, 'row', function(row) { assert.equal(row.name, 'Brian'); assert.equal(row.age, 28); }); assert.emits(q, 'end', function() { client.end(); }) }) }) node-postgres-0.13.3/test/native/stress-tests.js000066400000000000000000000022271211611354200216540ustar00rootroot00000000000000var helper = require(__dirname + "/../test-helper"); var Client = require(__dirname + "/../../lib/native"); test('many rows', function() { var client = new Client(helper.config); client.connect(); var q = client.query("SELECT * FROM person"); var rows = []; q.on('row', function(row) { rows.push(row) }); assert.emits(q, 'end', function() { client.end(); assert.lengthIs(rows, 26); }) }); test('many queries', function() { var client = new Client(helper.config); client.connect(); var count = 0; var expected = 100; for(var i = 0; i < expected; i++) { var q = client.query("SELECT * FROM person"); assert.emits(q, 'end', function() { count++; }) } assert.emits(client, 'drain', function() { client.end(); assert.equal(count, expected); }) }) test('many clients', function() { var clients = []; for(var i = 0; i < 10; i++) { clients.push(new Client(helper.config)); } clients.forEach(function(client) { client.connect(); for(var i = 0; i < 20; i++) { client.query('SELECT * FROM person'); } assert.emits(client, 'drain', function() { client.end(); }) }) }) node-postgres-0.13.3/test/test-buffers.js000066400000000000000000000054401211611354200203140ustar00rootroot00000000000000require(__dirname+'/test-helper'); //http://developer.postgresql.org/pgdocs/postgres/protocol-message-formats.html var buffers = {}; buffers.readyForQuery = function() { return new BufferList() .add(Buffer('I')) .join(true,'Z'); }; buffers.authenticationOk = function() { return new BufferList() .addInt32(0) .join(true, 'R'); }; buffers.authenticationCleartextPassword = function() { return new BufferList() .addInt32(3) .join(true, 'R'); }; buffers.authenticationMD5Password = function() { return new BufferList() .addInt32(5) .add(Buffer([1,2,3,4])) .join(true, 'R'); }; buffers.parameterStatus = function(name, value) { return new BufferList() .addCString(name) .addCString(value) .join(true, 'S'); }; buffers.backendKeyData = function(processID, secretKey) { return new BufferList() .addInt32(processID) .addInt32(secretKey) .join(true, 'K'); }; buffers.commandComplete = function(string) { return new BufferList() .addCString(string) .join(true, 'C'); }; buffers.rowDescription = function(fields) { fields = fields || []; var buf = new BufferList(); buf.addInt16(fields.length); fields.forEach(function(field) { buf.addCString(field.name) .addInt32(field.tableID || 0) .addInt16(field.attributeNumber || 0) .addInt32(field.dataTypeID || 0) .addInt16(field.dataTypeSize || 0) .addInt32(field.typeModifier || 0) .addInt16(field.formatCode || 0) }); return buf.join(true, 'T'); }; buffers.dataRow = function(columns) { columns = columns || []; var buf = new BufferList(); buf.addInt16(columns.length); columns.forEach(function(col) { if(col == null) { buf.addInt32(-1); } else { var strBuf = new Buffer(col, 'utf8'); buf.addInt32(strBuf.length); buf.add(strBuf); } }); return buf.join(true, 'D'); }; buffers.error = function(fields) { return errorOrNotice(fields).join(true, 'E'); }; buffers.notice = function(fields) { return errorOrNotice(fields).join(true, 'N'); }; var errorOrNotice = function(fields) { fields = fields || []; var buf = new BufferList(); fields.forEach(function(field) { buf.addChar(field.type); buf.addCString(field.value); }); return buf.add(Buffer([0]));//terminator } buffers.parseComplete = function() { return new BufferList().join(true, '1'); }; buffers.bindComplete = function() { return new BufferList().join(true, '2'); }; buffers.notification = function(id, channel, payload) { return new BufferList() .addInt32(id) .addCString(channel) .addCString(payload) .join(true, 'A') }; buffers.emptyQuery = function() { return new BufferList().join(true, 'I'); }; buffers.portalSuspended = function() { return new BufferList().join(true, 's'); }; module.exports = buffers; node-postgres-0.13.3/test/test-helper.js000066400000000000000000000127571211611354200201500ustar00rootroot00000000000000//make assert a global... assert = require('assert'); var EventEmitter = require('events').EventEmitter; var sys = require('util'); var BufferList = require(__dirname+'/buffer-list') var Connection = require(__dirname + '/../lib/connection'); Client = require(__dirname + '/../lib').Client; process.on('uncaughtException', function(d) { if ('stack' in d && 'message' in d) { console.log("Message: " + d.message); console.log(d.stack); } else { console.log(d); } }); assert.same = function(actual, expected) { for(var key in expected) { assert.equal(actual[key], expected[key]); } }; assert.emits = function(item, eventName, callback, message) { var called = false; var id = setTimeout(function() { test("Should have called '" + eventName + "' event", function() { assert.ok(called, message || "Expected '" + eventName + "' to be called.") }); },5000); item.once(eventName, function() { if (eventName === 'error') { // belt and braces test to ensure all error events return an error assert.ok(arguments[0] instanceof Error, "Expected error events to throw instances of Error but found: " + sys.inspect(arguments[0])); } called = true; clearTimeout(id); assert.ok(true); if(callback) { callback.apply(item, arguments); } }); }; assert.UTCDate = function(actual, year, month, day, hours, min, sec, milisecond) { var actualYear = actual.getUTCFullYear(); assert.equal(actualYear, year, "expected year " + year + " but got " + actualYear); var actualMonth = actual.getUTCMonth(); assert.equal(actualMonth, month, "expected month " + month + " but got " + actualMonth); var actualDate = actual.getUTCDate(); assert.equal(actualDate, day, "expected day " + day + " but got " + actualDate); var actualHours = actual.getUTCHours(); assert.equal(actualHours, hours, "expected hours " + hours + " but got " + actualHours); var actualMin = actual.getUTCMinutes(); assert.equal(actualMin, min, "expected min " + min + " but got " + actualMin); var actualSec = actual.getUTCSeconds(); assert.equal(actualSec, sec, "expected sec " + sec + " but got " + actualSec); var actualMili = actual.getUTCMilliseconds(); assert.equal(actualMili, milisecond, "expected milisecond " + milisecond + " but got " + actualMili); }; var spit = function(actual, expected) { console.log(""); console.log("actual " + sys.inspect(actual)); console.log("expect " + sys.inspect(expected)); console.log(""); } assert.equalBuffers = function(actual, expected) { if(actual.length != expected.length) { spit(actual, expected) assert.equal(actual.length, expected.length); } for(var i = 0; i < actual.length; i++) { if(actual[i] != expected[i]) { spit(actual, expected) } assert.equal(actual[i],expected[i]); } }; assert.empty = function(actual) { assert.lengthIs(actual, 0); }; assert.success = function(callback) { return assert.calls(function(err, arg) { if(err) { console.log(err); } assert.isNull(err); callback(arg); }) } assert.throws = function(offender) { try { offender(); } catch (e) { assert.ok(e instanceof Error, "Expected " + offender + " to throw instances of Error"); return; } assert.ok(false, "Expected " + offender + " to throw exception"); } assert.lengthIs = function(actual, expectedLength) { assert.equal(actual.length, expectedLength); }; var expect = function(callback, timeout) { var executed = false; var id = setTimeout(function() { assert.ok(executed, "Expected execution of function to be fired"); }, timeout || 5000) return function(err, queryResult) { clearTimeout(id); if (err) { assert.ok(err instanceof Error, "Expected errors to be instances of Error: " + sys.inspect(err)); } callback.apply(this, arguments) } } assert.calls = expect; assert.isNull = function(item, message) { message = message || "expected " + item + " to be null"; assert.ok(item === null, message); }; test = function(name, action) { test.testCount ++; test[name] = action; var result = test[name](); if(result === false) { process.stdout.write('?'); }else{ process.stdout.write('.'); } }; //print out the filename process.stdout.write(require('path').basename(process.argv[1])); var args = require(__dirname + '/cli'); if(args.binary) process.stdout.write(' (binary)'); if(args.native) process.stdout.write(' (native)'); process.on('exit', function() { console.log('') }) process.on('uncaughtException', function(err) { console.error("\n %s", err.stack || err.toString()) //causes xargs to abort right away process.exit(255); }); var count = 0; var Sink = function(expected, timeout, callback) { var defaultTimeout = 5000; if(typeof timeout == 'function') { callback = timeout; timeout = defaultTimeout; } timeout = timeout || defaultTimeout; var internalCount = 0; var kill = function() { assert.ok(false, "Did not reach expected " + expected + " with an idle timeout of " + timeout); } var killTimeout = setTimeout(kill, timeout); return { add: function(count) { count = count || 1; internalCount += count; clearTimeout(killTimeout) if(internalCount < expected) { killTimeout = setTimeout(kill, timeout) } else { assert.equal(internalCount, expected); callback(); } } } } module.exports = { Sink: Sink, pg: require(__dirname + '/../lib/'), args: args, config: args, sys: sys, Client: Client }; node-postgres-0.13.3/test/unit/000077500000000000000000000000001211611354200163215ustar00rootroot00000000000000node-postgres-0.13.3/test/unit/client/000077500000000000000000000000001211611354200175775ustar00rootroot00000000000000node-postgres-0.13.3/test/unit/client/cleartext-password-tests.js000066400000000000000000000007401211611354200251310ustar00rootroot00000000000000require(__dirname+'/test-helper'); test('cleartext password authentication', function(){ var client = createClient(); client.password = "!"; client.connection.stream.packets = []; client.connection.emit('authenticationCleartextPassword'); test('responds with password', function() { var packets = client.connection.stream.packets; assert.lengthIs(packets, 1); var packet = packets[0]; assert.equalBuffers(packet, [0x70, 0, 0, 0, 6, 33, 0]); }); }); node-postgres-0.13.3/test/unit/client/configuration-tests.js000066400000000000000000000044451211611354200241530ustar00rootroot00000000000000require(__dirname+'/test-helper'); test('client settings', function() { test('defaults', function() { var client = new Client(); assert.equal(client.user, process.env['PGUSER'] || process.env.USER); assert.equal(client.database, process.env['PGDATABASE'] || process.env.USER); assert.equal(client.port, 5432); }); test('custom', function() { var user = 'brian'; var database = 'pgjstest'; var password = 'boom'; var client = new Client({ user: user, database: database, port: 321, password: password }); assert.equal(client.user, user); assert.equal(client.database, database); assert.equal(client.port, 321); assert.equal(client.password, password); }); }); test('initializing from a config string', function() { test('uses the correct values from the config string', function() { var client = new Client("pg://brian:pass@host1:333/databasename") assert.equal(client.user, 'brian') assert.equal(client.password, "pass") assert.equal(client.host, "host1") assert.equal(client.port, 333) assert.equal(client.database, "databasename") }) test('uses the correct values from the config string with space in password', function() { var client = new Client("pg://brian:pass word@host1:333/databasename") assert.equal(client.user, 'brian') assert.equal(client.password, "pass word") assert.equal(client.host, "host1") assert.equal(client.port, 333) assert.equal(client.database, "databasename") }) test('when not including all values the defaults are used', function() { var client = new Client("pg://host1") assert.equal(client.user, process.env['PGUSER'] || process.env.USER) assert.equal(client.password, process.env['PGPASSWORD'] || null) assert.equal(client.host, "host1") assert.equal(client.port, process.env['PGPORT'] || 5432) assert.equal(client.database, process.env['PGDATABASE'] || process.env.USER) }) }) test('calls connect correctly on connection', function() { var client = new Client("/tmp"); var usedPort = ""; var usedHost = ""; client.connection.connect = function(port, host) { usedPort = port; usedHost = host; }; client.connect(); assert.equal(usedPort, "/tmp/.s.PGSQL.5432"); assert.strictEqual(usedHost, undefined) }) node-postgres-0.13.3/test/unit/client/connection-string-tests.js000066400000000000000000000011011211611354200247310ustar00rootroot00000000000000require(__dirname + '/test-helper'); test("using connection string in client constructor", function() { var client = new Client("postgres://brian:pw@boom:381/lala"); test("parses user", function() { assert.equal(client.user,'brian'); }) test("parses password", function() { assert.equal(client.password, 'pw'); }) test("parses host", function() { assert.equal(client.host, 'boom'); }) test('parses port', function() { assert.equal(client.port, 381) }) test('parses database', function() { assert.equal(client.database, 'lala') }) }) node-postgres-0.13.3/test/unit/client/md5-password-tests.js000066400000000000000000000013641211611354200236260ustar00rootroot00000000000000require(__dirname + '/test-helper') test('md5 authentication', function() { var client = createClient(); client.password = "!"; var salt = Buffer([1, 2, 3, 4]); client.connection.emit('authenticationMD5Password', {salt: salt}); test('responds', function() { assert.lengthIs(client.connection.stream.packets, 1); test('should have correct encrypted data', function() { var encrypted = Client.md5(client.password + client.user); encrypted = Client.md5(encrypted + salt.toString('binary')); var password = "md5" + encrypted //how do we want to test this? assert.equalBuffers(client.connection.stream.packets[0], new BufferList() .addCString(password).join(true,'p')) }); }); }); node-postgres-0.13.3/test/unit/client/notification-tests.js000066400000000000000000000004151211611354200237630ustar00rootroot00000000000000var helper = require(__dirname + "/test-helper"); test('passes connection notification', function() { var client = helper.client(); assert.emits(client, 'notice', function(msg) { assert.equal(msg, "HAY!!"); }) client.connection.emit('notice', "HAY!!"); }) node-postgres-0.13.3/test/unit/client/prepared-statement-tests.js000066400000000000000000000037701211611354200251100ustar00rootroot00000000000000var helper = require(__dirname + '/test-helper'); var client = helper.client(); var con = client.connection; var parseArg = null; con.parse = function(arg) { parseArg = arg; process.nextTick(function() { con.emit('parseComplete'); }); }; var bindArg = null; con.bind = function(arg) { bindArg = arg; process.nextTick(function(){ con.emit('bindComplete'); }); }; var executeArg = null; con.execute = function(arg) { executeArg = arg; process.nextTick(function() { con.emit('rowData',{ fields: [] }); con.emit('commandComplete', { text: "" }); }); }; var describeArg = null; con.describe = function(arg) { describeArg = arg; process.nextTick(function() { con.emit('rowDescription', { fields: [] }); }); }; var syncCalled = false; con.flush = function() { }; con.sync = function() { syncCalled = true; process.nextTick(function() { con.emit('readyForQuery'); }); }; test('bound command', function() { test('simple, unnamed bound command', function() { assert.ok(client.connection.emit('readyForQuery')); var query = client.query({ text: 'select * where name = $1', values: ['hi'] }); assert.emits(query,'end', function() { test('parse argument', function() { assert.equal(parseArg.name, null); assert.equal(parseArg.text, 'select * where name = $1'); assert.equal(parseArg.types, null); }); test('bind argument', function() { assert.equal(bindArg.statement, null); assert.equal(bindArg.portal, null); assert.lengthIs(bindArg.values, 1); assert.equal(bindArg.values[0], 'hi') }); test('describe argument', function() { assert.equal(describeArg.type, 'P'); assert.equal(describeArg.name, ""); }); test('execute argument', function() { assert.equal(executeArg.portal, null); assert.equal(executeArg.rows, null); }); test('sync called', function() { assert.ok(syncCalled); }); }); }); }); node-postgres-0.13.3/test/unit/client/query-queue-tests.js000066400000000000000000000051631211611354200235710ustar00rootroot00000000000000var helper = require(__dirname + '/test-helper'); var Connection = require(__dirname + '/../../../lib/connection'); test('drain', function() { var con = new Connection({stream: "NO"}); var client = new Client({connection:con}); con.connect = function() { con.emit('connect'); }; con.query = function() { }; client.connect(); var raisedDrain = false; client.on('drain', function() { raisedDrain = true; }); client.query("hello"); client.query("sup"); client.query('boom'); test("with pending queries", function() { test("does not emit drain", function() { assert.equal(raisedDrain, false); }); }); test("after some queries executed", function() { con.emit('readyForQuery'); test("does not emit drain", function() { assert.equal(raisedDrain, false); }); }); test("when all queries are sent", function() { con.emit('readyForQuery'); con.emit('readyForQuery'); test("does not emit drain", function() { assert.equal(raisedDrain, false); }); }); test("after last query finishes", function() { con.emit('readyForQuery'); test("emits drain", function() { process.nextTick(function() { assert.ok(raisedDrain); }) }); }); }); test('with drain paused', function() { //mock out a fake connection var con = new Connection({stream: "NO"}); con.connect = function() { con.emit('connect'); }; con.query = function() { }; var client = new Client({connection:con}); client.connect(); var drainCount = 0; client.on('drain', function() { drainCount++; }); test('normally unpaused', function() { con.emit('readyForQuery'); client.query('boom'); assert.emits(client, 'drain', function() { assert.equal(drainCount, 1); }); con.emit('readyForQuery'); }); test('pausing', function() { test('unpaused with no queries in between', function() { client.pauseDrain(); client.resumeDrain(); assert.equal(drainCount, 1); }); test('paused', function() { test('resumeDrain after empty', function() { client.pauseDrain(); client.query('asdf'); con.emit('readyForQuery'); assert.equal(drainCount, 1); client.resumeDrain(); assert.equal(drainCount, 2); }); test('resumDrain while still pending', function() { client.pauseDrain(); client.query('asdf'); client.query('asdf1'); con.emit('readyForQuery'); client.resumeDrain(); assert.equal(drainCount, 2); con.emit('readyForQuery'); assert.equal(drainCount, 3); }); }); }); }); node-postgres-0.13.3/test/unit/client/query-tests.js000066400000000000000000000046001211611354200224420ustar00rootroot00000000000000var helper = require(__dirname + '/test-helper'); var q = {}; q.dateParser = require(__dirname + "/../../../lib/types").getTypeParser(1114, 'text'); q.stringArrayParser = require(__dirname + "/../../../lib/types").getTypeParser(1009, 'text'); test("testing dateParser", function() { assert.equal(q.dateParser("2010-12-11 09:09:04").toString(),new Date("2010-12-11 09:09:04").toString()); }); var testForMs = function(part, expected) { var dateString = "2010-01-01 01:01:01" + part; test('testing for correcting parsing of ' + dateString, function() { var ms = q.dateParser(dateString).getMilliseconds(); assert.equal(ms, expected) }) } testForMs('.1', 100); testForMs('.01', 10); testForMs('.74', 740); test("testing 2dateParser on dates without timezones", function() { var actual = "2010-12-11 09:09:04.1"; var expected = JSON.stringify(new Date(2010,11,11,9,9,4,100)) assert.equal(JSON.stringify(q.dateParser(actual)),expected); }); test("testing 2dateParser on dates with timezones", function() { var actual = "2011-01-23 22:15:51.28-06"; var expected = "\"2011-01-24T04:15:51.280Z\""; assert.equal(JSON.stringify(q.dateParser(actual)),expected); }); test("testing 2dateParser on dates with huge millisecond value", function() { var actual = "2011-01-23 22:15:51.280843-06"; var expected = "\"2011-01-24T04:15:51.280Z\""; assert.equal(JSON.stringify(q.dateParser(actual)),expected); }); test("testing empty array", function(){ var input = '{}'; var expected = []; assert.deepEqual(q.stringArrayParser(input), expected); }); test("testing empty string array", function(){ var input = '{""}'; var expected = [""]; assert.deepEqual(q.stringArrayParser(input), expected); }); test("testing numeric array", function(){ var input = '{1,2,3,4}'; var expected = [1,2,3,4]; assert.deepEqual(q.stringArrayParser(input), expected); }); test("testing stringy array", function(){ var input = '{a,b,c,d}'; var expected = ['a','b','c','d']; assert.deepEqual(q.stringArrayParser(input), expected); }); test("testing stringy array containing escaped strings", function(){ var input = '{"\\"\\"\\"","\\\\\\\\\\\\"}'; var expected = ['"""','\\\\\\']; assert.deepEqual(q.stringArrayParser(input), expected); }); test("testing NULL array", function(){ var input = '{NULL,NULL}'; var expected = [null,null]; assert.deepEqual(q.stringArrayParser(input), expected); }); node-postgres-0.13.3/test/unit/client/result-metadata-tests.js000066400000000000000000000021311211611354200243660ustar00rootroot00000000000000var helper = require(__dirname + "/test-helper") var testForTag = function(tagText, callback) { test('includes command tag data for tag ' + tagText, function() { var client = helper.client(); client.connection.emit('readyForQuery') var query = client.query("whatever"); assert.lengthIs(client.connection.queries, 1) assert.emits(query, 'end', function(result) { assert.ok(result != null, "should pass something to this event") callback(result) }) client.connection.emit('commandComplete', { text: tagText }); client.connection.emit('readyForQuery'); }) } var check = function(oid, rowCount, command) { return function(result) { if(oid != null) { assert.equal(result.oid, oid); } assert.equal(result.rowCount, rowCount); assert.equal(result.command, command); } } testForTag("INSERT 0 3", check(0, 3, "INSERT")); testForTag("INSERT 841 1", check(841, 1, "INSERT")); testForTag("DELETE 10", check(null, 10, "DELETE")); testForTag("UPDATE 11", check(null, 11, "UPDATE")); testForTag("SELECT 20", check(null, 20, "SELECT")); node-postgres-0.13.3/test/unit/client/simple-query-tests.js000066400000000000000000000074421211611354200237400ustar00rootroot00000000000000var helper = require(__dirname + "/test-helper"); test('executing query', function() { test("queing query", function() { test('when connection is ready', function() { var client = helper.client(); assert.empty(client.connection.queries); client.connection.emit('readyForQuery'); client.query('yes'); assert.lengthIs(client.connection.queries, 1); assert.equal(client.connection.queries, 'yes'); }); test('when connection is not ready', function() { var client = helper.client(); test('query is not sent', function() { client.query('boom'); assert.empty(client.connection.queries); }); test('sends query to connection once ready', function() { assert.ok(client.connection.emit('readyForQuery')); assert.lengthIs(client.connection.queries, 1); assert.equal(client.connection.queries[0], "boom"); }); }); test("multiple in the queue", function() { var client = helper.client(); var connection = client.connection; var queries = connection.queries; client.query('one'); client.query('two'); client.query('three'); assert.empty(queries); test("after one ready for query",function() { connection.emit('readyForQuery'); assert.lengthIs(queries, 1); assert.equal(queries[0], "one"); }); test('after two ready for query', function() { connection.emit('readyForQuery'); assert.lengthIs(queries, 2); }); test("after a bunch more", function() { connection.emit('readyForQuery'); connection.emit('readyForQuery'); connection.emit('readyForQuery'); assert.lengthIs(queries, 3); assert.equal(queries[0], "one"); assert.equal(queries[1], 'two'); assert.equal(queries[2], 'three'); }); }); }); test("query event binding and flow", function() { var client = helper.client(); var con = client.connection; var query = client.query('whatever'); test("has no queries sent before ready", function() { assert.empty(con.queries); }); test('sends query on readyForQuery event', function() { con.emit('readyForQuery'); assert.lengthIs(con.queries, 1); assert.equal(con.queries[0], 'whatever'); }); test('handles rowDescription message', function() { var handled = con.emit('rowDescription',{ fields: [{ name: 'boom' }] }); assert.ok(handled, "should have handlded rowDescription"); }); test('handles dataRow messages', function() { assert.emits(query, 'row', function(row) { assert.equal(row['boom'], "hi"); }); var handled = con.emit('dataRow', { fields: ["hi"] }); assert.ok(handled, "should have handled first data row message"); assert.emits(query, 'row', function(row) { assert.equal(row['boom'], "bye"); }); var handledAgain = con.emit('dataRow', { fields: ["bye"] }); assert.ok(handledAgain, "should have handled seciond data row message"); }); //multiple command complete messages will be sent //when multiple queries are in a simple command test('handles command complete messages', function() { con.emit('commandComplete', { text: 'INSERT 31 1' }); }); test('removes itself after another readyForQuery message', function() { return false; assert.emits(query, "end", function(msg) { //TODO do we want to check the complete messages? }); con.emit("readyForQuery"); //this would never actually happen ['dataRow','rowDescription', 'commandComplete'].forEach(function(msg) { assert.equal(con.emit(msg), false, "Should no longer be picking up '"+ msg +"' messages"); }); }); }); }); node-postgres-0.13.3/test/unit/client/test-helper.js000066400000000000000000000010431211611354200223670ustar00rootroot00000000000000var helper = require(__dirname+'/../test-helper'); var Connection = require(__dirname + '/../../../lib/connection'); var makeClient = function() { var connection = new Connection({stream: "no"}); connection.startup = function() {}; connection.connect = function() {}; connection.query = function(text) { this.queries.push(text); }; connection.queries = []; var client = new Client({connection: connection}); client.connect(); client.connection.emit('connect'); return client; }; module.exports = { client: makeClient }; node-postgres-0.13.3/test/unit/client/typed-query-results-tests.js000066400000000000000000000156731211611354200253000ustar00rootroot00000000000000var helper = require(__dirname + '/test-helper'); //http://www.postgresql.org/docs/8.4/static/datatype.html test('typed results', function() { var client = helper.client(); var con = client.connection; con.emit('readyForQuery'); var query = client.query("the bums lost"); //TODO refactor to this style var tests = [{ name: 'string/varchar', format: 'text', dataTypeID: 1043, actual: 'bang', expected: 'bang' },{ name: 'integer/int4', format: 'text', dataTypeID: 23, actual: '100', expected: 100 },{ name: 'smallint/int2', format: 'text', dataTypeID: 21, actual: '101', expected: 101 },{ name: 'bigint/int8', format: 'text', dataTypeID: 20, actual: '102', expected: 102 },{ name: 'oid', format: 'text', dataTypeID: 26, actual: '103', expected: 103 },{ name: 'numeric', format: 'text', dataTypeID: 1700, actual: '12.34', expected: 12.34 },{ name: 'real/float4', dataTypeID: 700, format: 'text', actual: '123.456', expected: 123.456 },{ name: 'double precision / float8', format: 'text', dataTypeID: 701, actual: '1.2', expected: 1.2 },{ name: 'boolean true', format: 'text', dataTypeID: 16, actual: 't', expected: true },{ name: 'boolean false', format: 'text', dataTypeID: 16, actual: 'f', expected: false },{ name: 'boolean null', format: 'text', dataTypeID: 16, actual: null, expected: null },{ name: 'timestamptz with minutes in timezone', format: 'text', dataTypeID: 1184, actual: '2010-10-31 14:54:13.74-0530', expected: function(val) { assert.UTCDate(val, 2010, 9, 31, 20, 24, 13, 740); } },{ name: 'timestamptz with other milisecond digits dropped', format: 'text', dataTypeID: 1184, actual: '2011-01-23 22:05:00.68-06', expected: function(val) { assert.UTCDate(val, 2011, 0, 24, 4, 5, 00, 680); } }, { name: 'timestampz with huge miliseconds in UTC', format: 'text', dataTypeID: 1184, actual: '2010-10-30 14:11:12.730838Z', expected: function(val) { assert.UTCDate(val, 2010, 9, 30, 14, 11, 12, 730); } },{ name: 'timestampz with no miliseconds', format: 'text', dataTypeID: 1184, actual: '2010-10-30 13:10:01+05', expected: function(val) { assert.UTCDate(val, 2010, 9, 30, 8, 10, 01, 0); } },{ name: 'timestamp', format: 'text', dataTypeID: 1114, actual: '2010-10-31 00:00:00', expected: function(val) { assert.equal(val.toUTCString(), new Date(2010, 9, 31, 0, 0, 0, 0, 0).toUTCString()); assert.equal(val.toString(), new Date(2010, 9, 31, 0, 0, 0, 0, 0, 0).toString()); } },{ name: 'date', format: 'text', dataTypeID: 1082, actual: '2010-10-31', expected: function(val) { assert.UTCDate(val, 2010, 9, 31, 0, 0, 0, 0); } },{ name: 'interval time', format: 'text', dataTypeID: 1186, actual: '01:02:03', expected: function(val) { assert.deepEqual(val, {'hours':1, 'minutes':2, 'seconds':3}) } },{ name: 'interval long', format: 'text', dataTypeID: 1186, actual: '1 year -32 days', expected: function(val) { assert.deepEqual(val, {'years':1, 'days':-32}) } },{ name: 'interval combined negative', format: 'text', dataTypeID: 1186, actual: '1 day -00:00:03', expected: function(val) { assert.deepEqual(val, {'days':1, 'seconds':-3}) } },{ name: 'bytea', format: 'text', dataTypeID: 17, actual: 'foo\\000\\200\\\\\\377', expected: function(val) { assert.deepEqual(val, new Buffer([102, 111, 111, 0, 128, 92, 255])); } },{ name: 'empty bytea', format: 'text', dataTypeID: 17, actual: '', expected: function(val) { assert.deepEqual(val, new Buffer(0)); } }, { name : 'array/char', format : 'text', dataTypeID: 1014, actual: '{asdf,asdf}', expected : function(val){ assert.deepEqual(val, ['asdf','asdf']); } },{ name : 'array/varchar', format : 'text', dataTypeID: 1015, actual: '{asdf,asdf}', expected :function(val){ assert.deepEqual(val, ['asdf','asdf']); } },{ name : 'array/text', format : 'text', dataTypeID: 1008, actual: '{"hello world"}', expected :function(val){ assert.deepEqual(val, ['hello world']); } }, { name: 'binary-string/varchar', format: 'binary', dataTypeID: 1043, actual: 'bang', expected: 'bang' },{ name: 'binary-integer/int4', format: 'binary', dataTypeID: 23, actual: [0, 0, 0, 100], expected: 100 },{ name: 'binary-smallint/int2', format: 'binary', dataTypeID: 21, actual: [0, 101], expected: 101 },{ name: 'binary-bigint/int8', format: 'binary', dataTypeID: 20, actual: [0, 0, 0, 0, 0, 0, 0, 102], expected: 102 },{ name: 'binary-bigint/int8-full', format: 'binary', dataTypeID: 20, actual: [1, 0, 0, 0, 0, 0, 0, 102], expected: 72057594037928030 },{ name: 'binary-oid', format: 'binary', dataTypeID: 26, actual: [0, 0, 0, 103], expected: 103 },{ name: 'binary-numeric', format: 'binary', dataTypeID: 1700, actual: [0,2,0,0,0,0,0,0x64,0,12,0xd,0x48,0,0,0,0], expected: 12.34 },{ name: 'binary-real/float4', dataTypeID: 700, format: 'binary', actual: [0x41, 0x48, 0x00, 0x00], expected: 12.5 },{ name: 'binary-double precision / float8', format: 'binary', dataTypeID: 701, actual: [0x3F,0xF3,0x33,0x33,0x33,0x33,0x33,0x33], expected: 1.2 },{ name: 'binary-boolean true', format: 'binary', dataTypeID: 16, actual: [1], expected: true },{ name: 'binary-boolean false', format: 'binary', dataTypeID: 16, actual: [0], expected: false },{ name: 'binary-boolean null', format: 'binary', dataTypeID: 16, actual: null, expected: null },{ name: 'binary-timestamp', format: 'binary', dataTypeID: 1184, actual: [0x00, 0x01, 0x36, 0xee, 0x3e, 0x66, 0x9f, 0xe0], expected: function(val) { assert.UTCDate(val, 2010, 9, 31, 20, 24, 13, 740); } },{ name: 'binary-string', format: 'binary', dataTypeID: 25, actual: new Buffer([0x73, 0x6c, 0x61, 0x64, 0x64, 0x61]), expected: 'sladda' }]; con.emit('rowDescription', { fieldCount: tests.length, fields: tests }); assert.emits(query, 'row', function(row) { for(var i = 0; i < tests.length; i++) { test('parses ' + tests[i].name, function() { var expected = tests[i].expected; if(typeof expected === 'function') { return expected(row[tests[i].name]); } assert.strictEqual(row[tests[i].name], expected); }); } }); assert.ok(con.emit('dataRow', { fields: tests.map(function(x) { return x.actual; }) })); }); node-postgres-0.13.3/test/unit/connection-parameters/000077500000000000000000000000001211611354200226215ustar00rootroot00000000000000node-postgres-0.13.3/test/unit/connection-parameters/creation-tests.js000066400000000000000000000113131211611354200261220ustar00rootroot00000000000000var helper = require(__dirname + '/../test-helper'); var assert = require('assert'); var ConnectionParameters = require(__dirname + '/../../../lib/connection-parameters'); var defaults = require(__dirname + '/../../../lib').defaults; //clear process.env for(var key in process.env) { delete process.env[key]; } test('ConnectionParameters construction', function() { assert.ok(new ConnectionParameters(), 'with null config'); assert.ok(new ConnectionParameters({user: 'asdf'}), 'with config object'); assert.ok(new ConnectionParameters('pg://localhost/postgres'), 'with connection string'); }); var compare = function(actual, expected, type) { assert.equal(actual.user, expected.user, type + ' user'); assert.equal(actual.database, expected.database, type + ' database'); assert.equal(actual.port, expected.port, type + ' port'); assert.equal(actual.host, expected.host, type + ' host'); assert.equal(actual.password, expected.password, type + ' password'); assert.equal(actual.binary, expected.binary, type + ' binary'); }; test('ConnectionParameters initializing from defaults', function() { var subject = new ConnectionParameters(); compare(subject, defaults, 'defaults'); assert.ok(subject.isDomainSocket === false); }); test('ConnectionParameters initializing from config', function() { var config = { user: 'brian', database: 'home', port: 7777, password: 'pizza', binary: true, encoding: 'utf8', host: 'yo', ssl: { asdf: 'blah' } }; var subject = new ConnectionParameters(config); compare(subject, config, 'config'); assert.ok(subject.isDomainSocket === false); }); test('initializing with unix domain socket', function() { var subject = new ConnectionParameters('/var/run/'); assert.ok(subject.isDomainSocket); assert.equal(subject.host, '/var/run/'); }); 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' } var subject = new ConnectionParameters(config); subject.getLibpqConnectionString(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' }; var subject = new ConnectionParameters(config); subject.getLibpqConnectionString(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' }; var subject = new ConnectionParameters(config); subject.getLibpqConnectionString(assert.calls(function(err, constring) { assert.ok(err); assert.isNull(constring) })); }); test('connecting to unix domain socket', function() { var config = { user: 'brian', password: 'asf', port: 5432, host: '/tmp/' }; var subject = new ConnectionParameters(config); subject.getLibpqConnectionString(assert.calls(function(err, constring) { assert.isNull(err); var parts = constring.split(" "); checkForPart(parts, "user='brian'"); checkForPart(parts, "host=/tmp/"); })); }); 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 subject = new ConnectionParameters(connectionString); assert.equal(subject.password, sourceConfig.password); }); test('password contains weird characters', function() { var strang = 'pg://my first name:is&%awesome!@localhost:9000'; var subject = new ConnectionParameters(strang); assert.equal(subject.user, 'my first name'); assert.equal(subject.password, 'is&%awesome!'); assert.equal(subject.host, 'localhost'); }); }); node-postgres-0.13.3/test/unit/connection-parameters/environment-variable-tests.js000066400000000000000000000040351211611354200304500ustar00rootroot00000000000000var helper = require(__dirname + '/../test-helper'); var assert = require('assert'); var ConnectionParameters = require(__dirname + '/../../../lib/connection-parameters'); var defaults = require(__dirname + '/../../../lib').defaults; //clear process.env var realEnv = {}; for(var key in process.env) { realEnv[key] = process.env[key]; delete process.env[key]; } test('ConnectionParameters initialized from environment variables', function(t) { process.env['PGHOST'] = 'local'; process.env['PGUSER'] = 'bmc2'; process.env['PGPORT'] = 7890; process.env['PGDATABASE'] = 'allyerbase'; process.env['PGPASSWORD'] = 'open'; var subject = new ConnectionParameters(); assert.equal(subject.host, 'local', 'env host'); assert.equal(subject.user, 'bmc2', 'env user'); assert.equal(subject.port, 7890, 'env port'); assert.equal(subject.database, 'allyerbase', 'env database'); assert.equal(subject.password, 'open', 'env password'); }); test('ConnectionParameters initialized from mix', function(t) { delete process.env['PGPASSWORD']; delete process.env['PGDATABASE']; var subject = new ConnectionParameters({ user: 'testing', database: 'zugzug' }); assert.equal(subject.host, 'local', 'env host'); assert.equal(subject.user, 'testing', 'config user'); assert.equal(subject.port, 7890, 'env port'); assert.equal(subject.database, 'zugzug', 'config database'); assert.equal(subject.password, defaults.password, 'defaults password'); }); //clear process.env for(var key in process.env) { delete process.env[key]; } test('connection string parsing', function(t) { var string = 'postgres://brian:pw@boom:381/lala'; var subject = new ConnectionParameters(string); assert.equal(subject.host, 'boom', 'string host'); assert.equal(subject.user, 'brian', 'string user'); assert.equal(subject.password, 'pw', 'string password'); assert.equal(subject.port, 381, 'string port'); assert.equal(subject.database, 'lala', 'string database'); }); //restore process.env for(var key in realEnv) { process.env[key] = realEnv[key]; } node-postgres-0.13.3/test/unit/connection/000077500000000000000000000000001211611354200204605ustar00rootroot00000000000000node-postgres-0.13.3/test/unit/connection/error-tests.js000066400000000000000000000005761211611354200233170ustar00rootroot00000000000000var helper = require(__dirname + '/test-helper'); var Connection = require(__dirname + '/../../../lib/connection'); var con = new Connection({stream: new MemoryStream()}); test("connection emits stream errors", function() { assert.emits(con, 'error', function(err) { assert.equal(err.message, "OMG!"); }); con.connect(); con.stream.emit('error', new Error("OMG!")); }); node-postgres-0.13.3/test/unit/connection/inbound-parser-tests.js000066400000000000000000000302671211611354200251160ustar00rootroot00000000000000require(__dirname+'/test-helper'); var Connection = require(__dirname + '/../../../lib/connection'); var buffers = require(__dirname + '/../../test-buffers'); var PARSE = function(buffer) { return new Parser(buffer).parse(); }; var authOkBuffer = buffers.authenticationOk(); var paramStatusBuffer = buffers.parameterStatus('client_encoding', 'UTF8'); var readyForQueryBuffer = buffers.readyForQuery(); var backendKeyDataBuffer = buffers.backendKeyData(1,2); var commandCompleteBuffer = buffers.commandComplete("SELECT 3"); var parseCompleteBuffer = buffers.parseComplete(); var bindCompleteBuffer = buffers.bindComplete(); var portalSuspendedBuffer = buffers.portalSuspended(); var addRow = function(bufferList, name, offset) { return bufferList.addCString(name) //field name .addInt32(offset++) //table id .addInt16(offset++) //attribute of column number .addInt32(offset++) //objectId of field's data type .addInt16(offset++) //datatype size .addInt32(offset++) //type modifier .addInt16(0) //format code, 0 => text }; var row1 = { name: 'id', tableID: 1, attributeNumber: 2, dataTypeID: 3, dataTypeSize: 4, typeModifier: 5, formatCode: 0 }; var oneRowDescBuff = new buffers.rowDescription([row1]); row1.name = 'bang'; var twoRowBuf = new buffers.rowDescription([row1,{ name: 'whoah', tableID: 10, attributeNumber: 11, dataTypeID: 12, dataTypeSize: 13, typeModifier: 14, formatCode: 0 }]) var emptyRowFieldBuf = new BufferList() .addInt16(0) .join(true, 'D'); var emptyRowFieldBuf = buffers.dataRow(); var oneFieldBuf = new BufferList() .addInt16(1) //number of fields .addInt32(5) //length of bytes of fields .addCString('test') .join(true, 'D'); var oneFieldBuf = buffers.dataRow(['test']); var expectedAuthenticationOkayMessage = { name: 'authenticationOk', length: 8 }; var expectedParameterStatusMessage = { name: 'parameterStatus', parameterName: 'client_encoding', parameterValue: 'UTF8', length: 25 }; var expectedBackendKeyDataMessage = { name: 'backendKeyData', processID: 1, secretKey: 2 }; var expectedReadyForQueryMessage = { name: 'readyForQuery', length: 5, status: 'I' }; var expectedCommandCompleteMessage = { length: 13, text: "SELECT 3" }; var emptyRowDescriptionBuffer = new BufferList() .addInt16(0) //number of fields .join(true,'T'); var expectedEmptyRowDescriptionMessage = { name: 'rowDescription', length: 6, fieldCount: 0 }; var expectedOneRowMessage = { name: 'rowDescription', length: 27, fieldCount: 1 }; var expectedTwoRowMessage = { name: 'rowDescription', length: 53, fieldCount: 2 }; var testForMessage = function(buffer, expectedMessage) { var lastMessage = {}; test('recieves and parses ' + expectedMessage.name, function() { var stream = new MemoryStream(); var client = new Connection({ stream: stream }); client.connect(); client.on('message',function(msg) { lastMessage = msg; }); client.on(expectedMessage.name, function() { client.removeAllListeners(expectedMessage.name); }); stream.emit('data', buffer); assert.same(lastMessage, expectedMessage); }); return lastMessage; }; var plainPasswordBuffer = buffers.authenticationCleartextPassword(); var md5PasswordBuffer = buffers.authenticationMD5Password(); var expectedPlainPasswordMessage = { name: 'authenticationCleartextPassword' }; var expectedMD5PasswordMessage = { name: 'authenticationMD5Password' }; var notificationResponseBuffer = buffers.notification(4, 'hi', 'boom'); var expectedNotificationResponseMessage = { name: 'notification', processId: 4, channel: 'hi', payload: 'boom' }; test('Connection', function() { testForMessage(authOkBuffer, expectedAuthenticationOkayMessage); testForMessage(plainPasswordBuffer, expectedPlainPasswordMessage); var msg = testForMessage(md5PasswordBuffer, expectedMD5PasswordMessage); test('md5 has right salt', function() { assert.equalBuffers(msg.salt, Buffer([1,2,3,4])); }); testForMessage(paramStatusBuffer, expectedParameterStatusMessage); testForMessage(backendKeyDataBuffer, expectedBackendKeyDataMessage); testForMessage(readyForQueryBuffer, expectedReadyForQueryMessage); testForMessage(commandCompleteBuffer,expectedCommandCompleteMessage); testForMessage(notificationResponseBuffer, expectedNotificationResponseMessage); test('empty row message', function() { var message = testForMessage(emptyRowDescriptionBuffer, expectedEmptyRowDescriptionMessage); test('has no fields', function() { assert.equal(message.fields.length, 0); }); }); test("no data message", function() { testForMessage(Buffer([0x6e, 0, 0, 0, 4]), { name: 'noData' }); }); test('one row message', function() { var message = testForMessage(oneRowDescBuff, expectedOneRowMessage); test('has one field', function() { assert.equal(message.fields.length, 1); }); test('has correct field info', function() { assert.same(message.fields[0], { name: 'id', tableID: 1, columnID: 2, dataTypeID: 3, dataTypeSize: 4, dataTypeModifier: 5, format: 'text' }); }); }); test('two row message', function() { var message = testForMessage(twoRowBuf, expectedTwoRowMessage); test('has two fields', function() { assert.equal(message.fields.length, 2); }); test('has correct first field', function() { assert.same(message.fields[0], { name: 'bang', tableID: 1, columnID: 2, dataTypeID: 3, dataTypeSize: 4, dataTypeModifier: 5, format: 'text' }) }); test('has correct second field', function() { assert.same(message.fields[1], { name: 'whoah', tableID: 10, columnID: 11, dataTypeID: 12, dataTypeSize: 13, dataTypeModifier: 14, format: 'text' }); }); }); test('parsing rows', function() { test('parsing empty row', function() { var message = testForMessage(emptyRowFieldBuf, { name: 'dataRow', fieldCount: 0 }); test('has 0 fields', function() { assert.equal(message.fields.length, 0); }); }); test('parsing data row with fields', function() { var message = testForMessage(oneFieldBuf, { name: 'dataRow', fieldCount: 1 }); test('has 1 field', function() { assert.equal(message.fields.length, 1); }); test('field is correct', function() { assert.equal(message.fields[0],'test'); }); }); }); test('notice message', function() { //this uses the same logic as error message var buff = buffers.notice([{type: 'C', value: 'code'}]); testForMessage(buff, { name: 'notice', code: 'code' }); }); test('error messages', function() { test('with no fields', function() { var msg = testForMessage(buffers.error(),{ name: 'error' }); }); test('with all the fields', function() { var buffer = buffers.error([{ type: 'S', value: 'ERROR' },{ type: 'C', value: 'code' },{ type: 'M', value: 'message' },{ type: 'D', value: 'details' },{ type: 'H', value: 'hint' },{ type: 'P', value: '100' },{ type: 'p', value: '101' },{ type: 'q', value: 'query' },{ type: 'W', value: 'where' },{ type: 'F', value: 'file' },{ type: 'L', value: 'line' },{ type: 'R', value: 'routine' },{ type: 'Z', //ignored value: 'alsdkf' }]); testForMessage(buffer,{ name: 'error', severity: 'ERROR', code: 'code', message: 'message', detail: 'details', hint: 'hint', position: '100', internalPosition: '101', internalQuery: 'query', where: 'where', file: 'file', line: 'line', routine: 'routine' }); }); }); test('parses parse complete command', function() { testForMessage(parseCompleteBuffer, { name: 'parseComplete' }); }); test('parses bind complete command', function() { testForMessage(bindCompleteBuffer, { name: 'bindComplete' }); }); test('parses portal suspended message', function() { testForMessage(portalSuspendedBuffer, { name: 'portalSuspended' }); }); }); //since the data message on a stream can randomly divide the incomming //tcp packets anywhere, we need to make sure we can parse every single //split on a tcp message test('split buffer, single message parsing', function() { var fullBuffer = buffers.dataRow([null, "bang", "zug zug", null, "!"]); var stream = new MemoryStream(); stream.readyState = 'open'; var client = new Connection({ stream: stream }); client.connect(); var message = null; client.on('message', function(msg) { message = msg; }); test('parses when full buffer comes in', function() { stream.emit('data', fullBuffer); assert.lengthIs(message.fields, 5); assert.equal(message.fields[0], null); assert.equal(message.fields[1], "bang"); assert.equal(message.fields[2], "zug zug"); assert.equal(message.fields[3], null); assert.equal(message.fields[4], "!"); }); var testMessageRecievedAfterSpiltAt = function(split) { var firstBuffer = new Buffer(fullBuffer.length-split); var secondBuffer = new Buffer(fullBuffer.length-firstBuffer.length); fullBuffer.copy(firstBuffer, 0, 0); fullBuffer.copy(secondBuffer, 0, firstBuffer.length); stream.emit('data', firstBuffer); stream.emit('data', secondBuffer); assert.lengthIs(message.fields, 5); assert.equal(message.fields[0], null); assert.equal(message.fields[1], "bang"); assert.equal(message.fields[2], "zug zug"); assert.equal(message.fields[3], null); assert.equal(message.fields[4], "!"); }; test('parses when split in the middle', function() { testMessageRecievedAfterSpiltAt(6); }); test('parses when split at end', function() { testMessageRecievedAfterSpiltAt(2); }); test('parses when split at beginning', function() { testMessageRecievedAfterSpiltAt(fullBuffer.length - 2); testMessageRecievedAfterSpiltAt(fullBuffer.length - 1); testMessageRecievedAfterSpiltAt(fullBuffer.length - 5); }); }); test('split buffer, multiple message parsing', function() { var dataRowBuffer = buffers.dataRow(['!']); var readyForQueryBuffer = buffers.readyForQuery(); var fullBuffer = new Buffer(dataRowBuffer.length + readyForQueryBuffer.length); dataRowBuffer.copy(fullBuffer, 0, 0); readyForQueryBuffer.copy(fullBuffer, dataRowBuffer.length, 0); var messages = []; var stream = new MemoryStream(); var client = new Connection({ stream: stream }); client.connect(); client.on('message', function(msg) { messages.push(msg); }); var verifyMessages = function() { assert.lengthIs(messages, 2); assert.same(messages[0],{ name: 'dataRow', fieldCount: 1 }); assert.equal(messages[0].fields[0],'!'); assert.same(messages[1],{ name: 'readyForQuery' }); messages = []; }; //sanity check test('recieves both messages when packet is not split', function() { stream.emit('data', fullBuffer); verifyMessages(); }); var splitAndVerifyTwoMessages = function(split) { var firstBuffer = new Buffer(fullBuffer.length-split); var secondBuffer = new Buffer(fullBuffer.length-firstBuffer.length); fullBuffer.copy(firstBuffer, 0, 0); fullBuffer.copy(secondBuffer, 0, firstBuffer.length); stream.emit('data', firstBuffer); stream.emit('data', secondBuffer); }; test('recieves both messages when packet is split', function() { test('in the middle', function() { splitAndVerifyTwoMessages(11); }); test('at the front', function() { splitAndVerifyTwoMessages(fullBuffer.length-1); splitAndVerifyTwoMessages(fullBuffer.length-4); splitAndVerifyTwoMessages(fullBuffer.length-6); }); test('at the end', function() { splitAndVerifyTwoMessages(8); splitAndVerifyTwoMessages(1); }); }); }); node-postgres-0.13.3/test/unit/connection/outbound-sending-tests.js000066400000000000000000000107071211611354200254470ustar00rootroot00000000000000require(__dirname + "/test-helper"); var Connection = require(__dirname + '/../../../lib/connection'); var stream = new MemoryStream(); var con = new Connection({ stream: stream }); assert.received = function(stream, buffer) { assert.lengthIs(stream.packets, 1); var packet = stream.packets.pop(); assert.equalBuffers(packet, buffer); }; test("sends startup message", function() { con.startup({ user: 'brian', database: 'bang' }); assert.received(stream, new BufferList() .addInt16(3) .addInt16(0) .addCString('user') .addCString('brian') .addCString('database') .addCString('bang') .addCString('client_encoding') .addCString("'utf-8'") .addCString('').join(true)) }); test('sends password message', function() { con.password("!"); assert.received(stream, new BufferList().addCString("!").join(true,'p')); }); test('sends query message', function() { var txt = 'select * from boom'; con.query(txt); assert.received(stream, new BufferList().addCString(txt).join(true,'Q')); }); test('sends parse message', function() { con.parse({text: '!'}); var expected = new BufferList() .addCString("") .addCString("!") .addInt16(0).join(true, 'P'); assert.received(stream, expected); }); test('sends parse message with named query', function() { con.parse({ name: 'boom', text: 'select * from boom', types: [] }); var expected = new BufferList() .addCString("boom") .addCString("select * from boom") .addInt16(0).join(true,'P'); assert.received(stream, expected); test('with multiple parameters', function() { con.parse({ name: 'force', text: 'select * from bang where name = $1', types: [1, 2, 3 ,4] }); var expected = new BufferList() .addCString("force") .addCString("select * from bang where name = $1") .addInt16(4) .addInt32(1) .addInt32(2) .addInt32(3) .addInt32(4).join(true,'P'); assert.received(stream, expected); }); }); test('bind messages', function() { test('with no values', function() { con.bind(); var expectedBuffer = new BufferList() .addCString("") .addCString("") .addInt16(0) .addInt16(0) .addInt16(0) .join(true,"B"); assert.received(stream, expectedBuffer); }); test('with named statement, portal, and values', function() { con.bind({ portal: 'bang', statement: 'woo', values: ['1', 'hi', null, 'zing'] }); var expectedBuffer = new BufferList() .addCString('bang') //portal name .addCString('woo') //statement name .addInt16(0) .addInt16(4) .addInt32(1) .add(Buffer("1")) .addInt32(2) .add(Buffer("hi")) .addInt32(-1) .addInt32(4) .add(Buffer('zing')) .addInt16(0) .join(true, 'B'); assert.received(stream, expectedBuffer); }); }); test("sends execute message", function() { test("for unamed portal with no row limit", function() { con.execute(); var expectedBuffer = new BufferList() .addCString('') .addInt32(0) .join(true,'E'); assert.received(stream, expectedBuffer); }); test("for named portal with row limit", function() { con.execute({ portal: 'my favorite portal', rows: 100 }); var expectedBuffer = new BufferList() .addCString("my favorite portal") .addInt32(100) .join(true, 'E'); assert.received(stream, expectedBuffer); }); }); test('sends flush command', function() { con.flush(); var expected = new BufferList().join(true, 'H'); assert.received(stream, expected); }); test('sends sync command', function() { con.sync(); var expected = new BufferList().join(true,'S'); assert.received(stream, expected); }); test('sends end command', function() { con.end(); var expected = new Buffer([0x58, 0, 0, 0, 4]); assert.received(stream, expected); }); test('sends describe command',function() { test('describe statement', function() { con.describe({type: 'S', name: 'bang'}); var expected = new BufferList().addChar('S').addCString('bang').join(true, 'D') assert.received(stream, expected); }); test("describe unnamed portal", function() { con.describe({type: 'P'}); var expected = new BufferList().addChar('P').addCString("").join(true, "D"); assert.received(stream, expected); }); }); node-postgres-0.13.3/test/unit/connection/startup-tests.js000066400000000000000000000027431211611354200236660ustar00rootroot00000000000000require(__dirname+'/test-helper'); var Connection = require(__dirname + '/../../../lib/connection'); test('connection can take existing stream', function() { var stream = new MemoryStream(); var con = new Connection({stream: stream}); assert.equal(con.stream, stream); }); test('using closed stream', function() { var stream = new MemoryStream(); stream.readyState = 'closed'; stream.connect = function(port, host) { this.connectCalled = true; this.port = port; this.host = host; } var con = new Connection({stream: stream}); con.connect(1234, 'bang'); test('makes stream connect', function() { assert.equal(stream.connectCalled, true); }); test('uses configured port', function() { assert.equal(stream.port, 1234); }); test('uses configured host', function() { assert.equal(stream.host, 'bang'); }); test('after stream connects client emits connected event', function() { var hit = false; con.once('connect', function() { hit = true; }); assert.ok(stream.emit('connect')); assert.ok(hit); }); }); test('using opened stream', function() { var stream = new MemoryStream(); stream.readyState = 'open'; stream.connect = function() { assert.ok(false, "Should not call open"); }; var con = new Connection({stream: stream}); test('does not call open', function() { var hit = false; con.once('connect', function() { hit = true; }); con.connect(); assert.ok(hit); }); }); node-postgres-0.13.3/test/unit/connection/test-helper.js000066400000000000000000000000451211611354200232510ustar00rootroot00000000000000require(__dirname+'/../test-helper') node-postgres-0.13.3/test/unit/copystream/000077500000000000000000000000001211611354200205075ustar00rootroot00000000000000node-postgres-0.13.3/test/unit/copystream/copyfrom-tests.js000066400000000000000000000102551211611354200240460ustar00rootroot00000000000000var helper = require(__dirname + '/../test-helper'); var CopyFromStream = require(__dirname + '/../../../lib/copystream').CopyFromStream; var ConnectionImitation = function () { this.send = 0; this.hasToBeSend = 0; this.finished = 0; }; ConnectionImitation.prototype = { endCopyFrom: function () { assert.ok(this.finished++ === 0, "end shoud be called only once"); assert.equal(this.send, this.hasToBeSend, "at the moment of the end all data has to be sent"); }, sendCopyFromChunk: function (chunk) { this.send += chunk.length; return true; }, updateHasToBeSend: function (chunk) { this.hasToBeSend += chunk.length; return chunk; } }; var buf1 = new Buffer("asdfasd"), buf2 = new Buffer("q03r90arf0aospd;"), buf3 = new Buffer(542), buf4 = new Buffer("93jfemialfjkasjlfas"); test('CopyFromStream, start streaming before data, end after data. no drain event', function () { var stream = new CopyFromStream(); var conn = new ConnectionImitation(); stream.on('drain', function () { assert.ok(false, "there has not be drain event"); }); stream.startStreamingToConnection(conn); assert.ok(stream.write(conn.updateHasToBeSend(buf1))); assert.ok(stream.write(conn.updateHasToBeSend(buf2))); assert.ok(stream.write(conn.updateHasToBeSend(buf3))); assert.ok(stream.writable, "stream has to be writable"); stream.end(conn.updateHasToBeSend(buf4)); assert.ok(!stream.writable, "stream has not to be writable"); stream.end(); assert.equal(conn.hasToBeSend, conn.send); }); test('CopyFromStream, start streaming after end, end after data. drain event', function () { var stream = new CopyFromStream(); assert.emits(stream, 'drain', function() {}, 'drain have to be emitted'); var conn = new ConnectionImitation() assert.ok(!stream.write(conn.updateHasToBeSend(buf1))); assert.ok(!stream.write(conn.updateHasToBeSend(buf2))); assert.ok(!stream.write(conn.updateHasToBeSend(buf3))); assert.ok(stream.writable, "stream has to be writable"); stream.end(conn.updateHasToBeSend(buf4)); assert.ok(!stream.writable, "stream has not to be writable"); stream.end(); stream.startStreamingToConnection(conn); assert.equal(conn.hasToBeSend, conn.send); }); test('CopyFromStream, start streaming between data chunks. end after data. drain event', function () { var stream = new CopyFromStream(); var conn = new ConnectionImitation() assert.emits(stream, 'drain', function() {}, 'drain have to be emitted'); stream.write(conn.updateHasToBeSend(buf1)); stream.write(conn.updateHasToBeSend(buf2)); stream.startStreamingToConnection(conn); stream.write(conn.updateHasToBeSend(buf3)); assert.ok(stream.writable, "stream has to be writable"); stream.end(conn.updateHasToBeSend(buf4)); assert.equal(conn.hasToBeSend, conn.send); assert.ok(!stream.writable, "stream has not to be writable"); stream.end(); }); test('CopyFromStream, start sreaming before end. end stream with data. drain event', function () { var stream = new CopyFromStream(); var conn = new ConnectionImitation() assert.emits(stream, 'drain', function() {}, 'drain have to be emitted'); stream.write(conn.updateHasToBeSend(buf1)); stream.write(conn.updateHasToBeSend(buf2)); stream.write(conn.updateHasToBeSend(buf3)); stream.startStreamingToConnection(conn); assert.ok(stream.writable, "stream has to be writable"); stream.end(conn.updateHasToBeSend(buf4)); assert.equal(conn.hasToBeSend, conn.send); assert.ok(!stream.writable, "stream has not to be writable"); stream.end(); }); test('CopyFromStream, start streaming after end. end with data. drain event', function(){ var stream = new CopyFromStream(); var conn = new ConnectionImitation() assert.emits(stream, 'drain', function() {}, 'drain have to be emitted'); stream.write(conn.updateHasToBeSend(buf1)); stream.write(conn.updateHasToBeSend(buf2)); stream.write(conn.updateHasToBeSend(buf3)); stream.startStreamingToConnection(conn); assert.ok(stream.writable, "stream has to be writable"); stream.end(conn.updateHasToBeSend(buf4)); stream.startStreamingToConnection(conn); assert.equal(conn.hasToBeSend, conn.send); assert.ok(!stream.writable, "stream has not to be writable"); stream.end(); }); node-postgres-0.13.3/test/unit/copystream/copyto-tests.js000066400000000000000000000073371211611354200235340ustar00rootroot00000000000000var helper = require(__dirname + '/../test-helper'); var CopyToStream = require(__dirname + '/../../../lib/copystream').CopyToStream; var DataCounter = function () { this.sendBytes = 0; this.recievedBytes = 0; }; DataCounter.prototype = { send: function (buf) { this.sendBytes += buf.length; return buf; }, recieve: function (chunk) { this.recievedBytes += chunk.length; }, assert: function () { assert.equal(this.sendBytes, this.recievedBytes, "data bytes send and recieved has to match"); } }; var buf1 = new Buffer("asdfasd"), buf2 = new Buffer("q03r90arf0aospd;"), buf3 = new Buffer(542), buf4 = new Buffer("93jfemialfjkasjlfas"); test('CopyToStream simple', function () { var stream = new CopyToStream(), dc = new DataCounter(); assert.emits(stream, 'end', function () {}, ''); stream.on('data', dc.recieve.bind(dc)); stream.handleChunk(dc.send(buf1)); stream.handleChunk(dc.send(buf2)); stream.handleChunk(dc.send(buf3)); stream.handleChunk(dc.send(buf4)); dc.assert(); stream.close(); }); test('CopyToStream pause/resume/close', function () { var stream = new CopyToStream(), dc = new DataCounter(); stream.on('data', dc.recieve.bind(dc)); assert.emits(stream, 'end', function () {}, 'stream has to emit end after closing'); stream.pause(); stream.handleChunk(dc.send(buf1)); stream.handleChunk(dc.send(buf2)); stream.handleChunk(dc.send(buf3)); assert.equal(dc.recievedBytes, 0); stream.resume(); dc.assert(); stream.handleChunk(dc.send(buf2)); dc.assert(); stream.handleChunk(dc.send(buf3)); dc.assert(); stream.pause(); stream.handleChunk(dc.send(buf4)); assert(dc.sendBytes - dc.recievedBytes, buf4.length, "stream has not emit, data while it is in paused state"); stream.resume(); dc.assert(); stream.close(); }); test('CopyToStream error', function () { var stream = new CopyToStream(), dc = new DataCounter(); stream.on('data', dc.recieve.bind(dc)); assert.emits(stream, 'error', function () {}, 'stream has to emit error event, when error method called'); stream.handleChunk(dc.send(buf1)); stream.handleChunk(dc.send(buf2)); stream.error(new Error('test error')); }); test('CopyToStream do not emit anything while paused', function () { var stream = new CopyToStream(); stream.on('data', function () { assert.ok(false, "stream has not emit data when paused"); }); stream.on('end', function () { assert.ok(false, "stream has not emit end when paused"); }); stream.on('error', function () { assert.ok(false, "stream has not emit end when paused"); }); stream.pause(); stream.handleChunk(buf2); stream.close(); stream.error(); }); test('CopyToStream emit data and error after resume', function () { var stream = new CopyToStream(), paused; stream.on('data', function () { assert.ok(!paused, "stream has not emit data when paused"); }); stream.on('end', function () { assert.ok(!paused, "stream has not emit end when paused"); }); stream.on('error', function () { assert.ok(!paused, "stream has not emit end when paused"); }); paused = true; stream.pause(); stream.handleChunk(buf2); stream.error(); paused = false; stream.resume(); }); test('CopyToStream emit data and end after resume', function () { var stream = new CopyToStream(), paused; stream.on('data', function () { assert.ok(!paused, "stream has not emit data when paused"); }); stream.on('end', function () { assert.ok(!paused, "stream has not emit end when paused"); }); stream.on('error', function () { assert.ok(!paused, "stream has not emit end when paused"); }); paused = true; stream.pause(); stream.handleChunk(buf2); stream.close(); paused = false; stream.resume(); }); node-postgres-0.13.3/test/unit/pool/000077500000000000000000000000001211611354200172725ustar00rootroot00000000000000node-postgres-0.13.3/test/unit/pool/basic-tests.js000066400000000000000000000123711211611354200220550ustar00rootroot00000000000000var util = require('util'); var EventEmitter = require('events').EventEmitter; var libDir = __dirname + '/../../../lib'; var defaults = require(libDir + '/defaults'); var pools = require(libDir + '/pool'); var poolId = 0; require(__dirname + '/../../test-helper'); var FakeClient = function() { EventEmitter.call(this); } util.inherits(FakeClient, EventEmitter); FakeClient.prototype.connect = function(cb) { process.nextTick(cb); } FakeClient.prototype.end = function() { this.endCalled = true; } //Hangs the event loop until 'end' is called on client var HangingClient = function(config) { EventEmitter.call(this); this.config = config; } util.inherits(HangingClient, EventEmitter); HangingClient.prototype.connect = function(cb) { this.intervalId = setInterval(function() { console.log('hung client...'); }, 1000); process.nextTick(cb); } HangingClient.prototype.end = function() { clearInterval(this.intervalId); } pools.Client = FakeClient; test('no pools exist', function() { assert.empty(Object.keys(pools.all)); }); test('pool creates pool on miss', function() { var p = pools.getOrCreate(); assert.ok(p); assert.equal(Object.keys(pools.all).length, 1); var p2 = pools.getOrCreate(); assert.equal(p, p2); assert.equal(Object.keys(pools.all).length, 1); var p3 = pools.getOrCreate("pg://postgres:password@localhost:5432/postgres"); assert.notEqual(p, p3); assert.equal(Object.keys(pools.all).length, 2); }); test('pool follows defaults', function() { var p = pools.getOrCreate(poolId++); for(var i = 0; i < 100; i++) { p.acquire(function(err, client) { }); } assert.equal(p.getPoolSize(), defaults.poolSize); }); test('pool#connect with 2 parameters (legacy, for backwards compat)', function() { var p = pools.getOrCreate(poolId++); p.connect(assert.success(function(client) { assert.ok(client); assert.equal(p.availableObjectsCount(), 0); assert.equal(p.getPoolSize(), 1); client.emit('drain'); assert.equal(p.availableObjectsCount(), 1); assert.equal(p.getPoolSize(), 1); p.destroyAllNow(); })); }); test('pool#connect with 3 parameters', function() { var p = pools.getOrCreate(poolId++); var tid = setTimeout(function() { throw new Error("Connection callback was never called"); }, 100); p.connect(function(err, client, done) { clearTimeout(tid); assert.equal(err, null); assert.ok(client); assert.equal(p.availableObjectsCount(), 0); assert.equal(p.getPoolSize(), 1); client.emit('drain'); assert.equal(p.availableObjectsCount(), 0); assert.equal(p.getPoolSize(), 1); done(); assert.equal(p.availableObjectsCount(), 1); assert.equal(p.getPoolSize(), 1); p.destroyAllNow(); }); }); test('on client error, client is removed from pool', function() { var p = pools.getOrCreate(poolId++); p.connect(assert.success(function(client) { assert.ok(client); client.emit('drain'); assert.equal(p.availableObjectsCount(), 1); assert.equal(p.getPoolSize(), 1); //error event fires on pool BEFORE pool.destroy is called with client assert.emits(p, 'error', function(err) { assert.equal(err.message, 'test error'); assert.ok(!client.endCalled); assert.equal(p.availableObjectsCount(), 1); assert.equal(p.getPoolSize(), 1); //after we're done in our callback, pool.destroy is called process.nextTick(function() { assert.ok(client.endCalled); assert.equal(p.availableObjectsCount(), 0); assert.equal(p.getPoolSize(), 0); p.destroyAllNow(); }); }); client.emit('error', new Error('test error')); })); }); test('pool with connection error on connection', function() { pools.Client = function() { return { connect: function(cb) { process.nextTick(function() { cb(new Error('Could not connect')); }); } }; } test('two parameters', function() { var p = pools.getOrCreate(poolId++); p.connect(assert.calls(function(err, client) { assert.ok(err); assert.equal(client, null); //client automatically removed assert.equal(p.availableObjectsCount(), 0); assert.equal(p.getPoolSize(), 0); })); }); test('three parameters', function() { var p = pools.getOrCreate(poolId++); var tid = setTimeout(function() { assert.fail('Did not call connect callback'); }, 100); p.connect(function(err, client, done) { clearTimeout(tid); assert.ok(err); assert.equal(client, null); //done does nothing done(new Error('OH NOOOO')); done(); assert.equal(p.availableObjectsCount(), 0); assert.equal(p.getPoolSize(), 0); }); }); }); test('returnning an error to done()', function() { var p = pools.getOrCreate(poolId++); pools.Client = FakeClient; p.connect(function(err, client, done) { assert.equal(err, null); assert(client); done(new Error("BROKEN")); assert.equal(p.availableObjectsCount(), 0); assert.equal(p.getPoolSize(), 0); }); }); test('fetching pool by object', function() { var p = pools.getOrCreate({ user: 'brian', host: 'localhost', password: 'password' }); var p2 = pools.getOrCreate({ user: 'brian', host: 'localhost', password: 'password' }); assert.equal(p, p2); }); node-postgres-0.13.3/test/unit/pool/timeout-tests.js000066400000000000000000000017631211611354200224650ustar00rootroot00000000000000var util = require('util'); var EventEmitter = require('events').EventEmitter; var libDir = __dirname + '/../../../lib'; var defaults = require(libDir + '/defaults'); var pools = require(libDir + '/pool'); var poolId = 0; require(__dirname + '/../../test-helper'); var FakeClient = function() { EventEmitter.call(this); } util.inherits(FakeClient, EventEmitter); FakeClient.prototype.connect = function(cb) { process.nextTick(cb); } FakeClient.prototype.end = function() { this.endCalled = true; } defaults.poolIdleTimeout = 10; defaults.reapIntervalMillis = 10; test('client times out from idle', function() { pools.Client = FakeClient; var p = pools.getOrCreate(poolId++); p.connect(function(err, client, done) { done(); }); process.nextTick(function() { assert.equal(p.availableObjectsCount(), 1); assert.equal(p.getPoolSize(), 1); setTimeout(function() { assert.equal(p.availableObjectsCount(), 0); assert.equal(p.getPoolSize(), 0); }, 50); }); }); node-postgres-0.13.3/test/unit/test-helper.js000066400000000000000000000012151211611354200211120ustar00rootroot00000000000000var helper = require(__dirname+'/../test-helper'); var EventEmitter = require('events').EventEmitter; var Connection = require(__dirname + '/../../lib/connection'); MemoryStream = function() { EventEmitter.call(this); this.packets = []; }; helper.sys.inherits(MemoryStream, EventEmitter); var p = MemoryStream.prototype; p.write = function(packet) { this.packets.push(packet); }; p.writable = true; createClient = function() { var stream = new MemoryStream(); stream.readyState = "open"; var client = new Client({ connection: new Connection({stream: stream}) }); client.connect(); return client; }; module.exports = helper; node-postgres-0.13.3/test/unit/utils-tests.js000066400000000000000000000024761211611354200211700ustar00rootroot00000000000000require(__dirname + '/test-helper'); var utils = require(__dirname + "/../../lib/utils"); var defaults = require(__dirname + "/../../lib").defaults; //this tests the monkey patching //to ensure comptability with older //versions of node test("EventEmitter.once", function(t) { //an event emitter var stream = new MemoryStream(); var callCount = 0; stream.once('single', function() { callCount++; }); stream.emit('single'); stream.emit('single'); assert.equal(callCount, 1); }); test('types are exported', function() { var pg = require(__dirname + '/../../lib/index'); assert.ok(pg.types); }); test('normalizing query configs', function() { var config var callback = function () {} config = utils.normalizeQueryConfig({text: 'TEXT'}) assert.same(config, {text: 'TEXT'}) config = utils.normalizeQueryConfig({text: 'TEXT'}, [10]) assert.deepEqual(config, {text: 'TEXT', values: [10]}) config = utils.normalizeQueryConfig({text: 'TEXT', values: [10]}) assert.deepEqual(config, {text: 'TEXT', values: [10]}) config = utils.normalizeQueryConfig('TEXT', [10], callback) assert.deepEqual(config, {text: 'TEXT', values: [10], callback: callback}) config = utils.normalizeQueryConfig({text: 'TEXT', values: [10]}, callback) assert.deepEqual(config, {text: 'TEXT', values: [10], callback: callback}) }) node-postgres-0.13.3/test/unit/writer-tests.js000066400000000000000000000137211211611354200213370ustar00rootroot00000000000000require(__dirname + "/test-helper"); var Writer = require(__dirname + "/../../lib/writer"); test('adding int32', function() { var testAddingInt32 = function(int, expectedBuffer) { test('writes ' + int, function() { var subject = new Writer(); var result = subject.addInt32(int).join(); assert.equalBuffers(result, expectedBuffer); }) } testAddingInt32(0, [0, 0, 0, 0]); testAddingInt32(1, [0, 0, 0, 1]); testAddingInt32(256, [0, 0, 1, 0]); test('writes largest int32', function() { //todo need to find largest int32 when I have internet access return false; }) test('writing multiple int32s', function() { var subject = new Writer(); var result = subject.addInt32(1).addInt32(10).addInt32(0).join(); assert.equalBuffers(result, [0, 0, 0, 1, 0, 0, 0, 0x0a, 0, 0, 0, 0]); }) test('having to resize the buffer', function() { test('after resize correct result returned', function() { var subject = new Writer(10); subject.addInt32(1).addInt32(1).addInt32(1) assert.equalBuffers(subject.join(), [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1]) }) }) }) test('int16', function() { test('writes 0', function() { var subject = new Writer(); var result = subject.addInt16(0).join(); assert.equalBuffers(result, [0,0]); }) test('writes 400', function() { var subject = new Writer(); var result = subject.addInt16(400).join(); assert.equalBuffers(result, [1, 0x90]) }) test('writes many', function() { var subject = new Writer(); var result = subject.addInt16(0).addInt16(1).addInt16(2).join(); assert.equalBuffers(result, [0, 0, 0, 1, 0, 2]) }) test('resizes if internal buffer fills up', function() { var subject = new Writer(3); var result = subject.addInt16(2).addInt16(3).join(); assert.equalBuffers(result, [0, 2, 0, 3]) }) }) test('cString', function() { test('writes empty cstring', function() { var subject = new Writer(); var result = subject.addCString().join(); assert.equalBuffers(result, [0]) }) test('writes two empty cstrings', function() { var subject = new Writer(); var result = subject.addCString("").addCString("").join(); assert.equalBuffers(result, [0, 0]) }) test('writes non-empty cstring', function() { var subject = new Writer(); var result = subject.addCString("!!!").join(); assert.equalBuffers(result, [33, 33, 33, 0]); }) test('resizes if reached end', function() { var subject = new Writer(3); var result = subject.addCString("!!!").join(); assert.equalBuffers(result, [33, 33, 33, 0]); }) test('writes multiple cstrings', function() { var subject = new Writer(); var result = subject.addCString("!").addCString("!").join(); assert.equalBuffers(result, [33, 0, 33, 0]); }) }) test('writes char', function() { var subject = new Writer(2); var result = subject.addChar('a').addChar('b').addChar('c').join(); assert.equalBuffers(result, [0x61, 0x62, 0x63]) }) test('gets correct byte length', function() { var subject = new Writer(5); assert.equal(subject.getByteLength(), 0) subject.addInt32(0) assert.equal(subject.getByteLength(), 4) subject.addCString("!") assert.equal(subject.getByteLength(), 6) }) test('can add arbitrary buffer to the end', function() { var subject = new Writer(4); subject.addCString("!!!") var result = subject.add(Buffer("@@@")).join(); assert.equalBuffers(result, [33, 33, 33, 0, 0x40, 0x40, 0x40]); }) test('can write normal string', function() { var subject = new Writer(4); var result = subject.addString("!").join(); assert.equalBuffers(result, [33]); test('can write cString too', function() { var result = subject.addCString("!").join(); assert.equalBuffers(result, [33, 33, 0]); test('can resize', function() { var result = subject.addString("!!").join(); assert.equalBuffers(result, [33, 33, 0, 33, 33]); }) }) }) test('clearing', function() { var subject = new Writer(); subject.addCString("@!!#!#"); subject.addInt32(10401); subject.clear(); assert.equalBuffers(subject.join(), []); test('can keep writing', function() { var joinedResult = subject.addCString("!").addInt32(9).addInt16(2).join(); assert.equalBuffers(joinedResult, [33, 0, 0, 0, 0, 9, 0, 2]); test('flush', function() { var flushedResult = subject.flush(); test('returns result', function() { assert.equalBuffers(flushedResult, [33, 0, 0, 0, 0, 9, 0, 2]) }) test('clears the writer', function() { assert.equalBuffers(subject.join(), []) assert.equalBuffers(subject.flush(), []) }) }) }) }) test("resizing to much larger", function() { var subject = new Writer(2); var string = "!!!!!!!!"; var result = subject.addCString(string).flush(); assert.equalBuffers(result, [33, 33, 33, 33, 33, 33, 33, 33, 0]) }) test("flush", function() { test('added as a hex code to a full writer', function() { var subject = new Writer(2); var result = subject.addCString("!").flush(0x50) assert.equalBuffers(result, [0x50, 0, 0, 0, 6, 33, 0]); }) test('added as a hex code to a non-full writer', function() { var subject = new Writer(10).addCString("!"); var joinedResult = subject.join(0x50); var result = subject.flush(0x50); assert.equalBuffers(result, [0x50, 0, 0, 0, 6, 33, 0]); }) test('added as a hex code to a buffer which requires resizing', function() { var result = new Writer(2).addCString("!!!!!!!!").flush(0x50); assert.equalBuffers(result, [0x50, 0, 0, 0, 0x0D, 33, 33, 33, 33, 33, 33, 33, 33, 0]); }) }) test("header", function() { test('adding two packets with headers', function() { var subject = new Writer(10).addCString("!"); subject.addHeader(0x50); subject.addCString("!!"); subject.addHeader(0x40); subject.addCString("!"); var result = subject.flush(0x10); assert.equalBuffers(result, [0x50, 0, 0, 0, 6, 33, 0, 0x40, 0, 0, 0, 7, 33, 33, 0, 0x10, 0, 0, 0, 6, 33, 0 ]); }) }) node-postgres-0.13.3/wscript000066400000000000000000000017361211611354200160100ustar00rootroot00000000000000import Options, Utils from os import unlink, symlink, popen from os.path import exists srcdir = '.' blddir = 'build' VERSION = '0.0.1' def set_options(opt): opt.tool_options('compiler_cxx') def configure(conf): conf.check_tool('compiler_cxx') conf.check_tool('node_addon') pg_config = conf.find_program('pg_config', var='PG_CONFIG', mandatory=True) pg_libdir = popen("%s --libdir" % pg_config).readline().strip() conf.env.append_value("LIBPATH_PG", pg_libdir) conf.env.append_value("LIB_PG", "pq") pg_includedir = popen("%s --includedir" % pg_config).readline().strip() conf.env.append_value("CPPPATH_PG", pg_includedir) def build(bld): obj = bld.new_task_gen('cxx', 'shlib', 'node_addon') obj.cxxflags = ["-g", "-D_LARGEFILE_SOURCE", "-Wall"] obj.target = 'binding' obj.source = "./src/binding.cc" obj.uselib = "PG" def test(test): Utils.exec_command("node test/native/connection-tests.js") Utils.exec_command("node test/native/evented-api-tests.js")