diff --git a/lib/native.js b/lib/native.js index ca88701a..00657d33 100644 --- a/lib/native.js +++ b/lib/native.js @@ -119,9 +119,18 @@ var NativeQuery = function(text, values, callback) { if(this.values) { for(var i = 0, len = this.values.length; i < len; i++) { var item = this.values[i]; - if(item instanceof Date) { - this.values[i] = JSON.stringify(item); - } else { + switch(typeof item) { + case 'undefined': + this.values[i] = null; + break; + case 'object': + this.values[i] = item === null ? null : JSON.stringify(item); + break; + case 'string': + //value already string + break; + default: + //numbers this.values[i] = item.toString(); } } @@ -137,9 +146,8 @@ var p = NativeQuery.prototype; var mapRowData = function(row) { var result = {}; for(var i = 0, len = row.length; i < len; i++) { - var item = row[i]; - var parser = types.getStringTypeParser(item.type); - result[item.name] = parser(item.value); + var item = row[i]; + result[item.name] = item.value == null ? null : types.getStringTypeParser(item.type)(item.value); } return result; } @@ -174,5 +182,5 @@ module.exports = { Client: ctor, connect: pool.connect, end: pool.end, - defaults: require(__dirname + '/defaults') + defaults: require(__dirname + '/defaults') }; diff --git a/lib/types.js b/lib/types.js index 7357cef9..35e2715d 100644 --- a/lib/types.js +++ b/lib/types.js @@ -25,6 +25,10 @@ var parseDate = function(isoDate) { 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) { + return null; + } var year = match[1]; var month = parseInt(match[2],10)-1; var day = match[3]; diff --git a/src/binding.cc b/src/binding.cc index 724c39b3..0123021c 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -211,6 +211,7 @@ public: if(result == 1) { return Undefined(); } + self->EmitLastError(); THROW("Postgres returned non-1 result from query dispatch."); } @@ -444,12 +445,22 @@ protected: int fieldCount = PQnfields(result); for(int fieldNumber = 0; fieldNumber < fieldCount; fieldNumber++) { Local field = Object::New(); + //name of field char* fieldName = PQfname(result, fieldNumber); - int fieldType = PQftype(result, fieldNumber); - char* fieldValue = PQgetvalue(result, rowNumber, fieldNumber); field->Set(name_symbol, String::New(fieldName)); - field->Set(value_symbol, String::New(fieldValue)); + + //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); } @@ -578,6 +589,8 @@ private: return 0; } paramValues[i] = cString; + } else if(val->IsNull()) { + paramValues[i] = NULL; } else { //a paramter was not a string LOG("Parameter not a string"); diff --git a/test/integration/client/notice-tests.js b/test/integration/client/notice-tests.js index 44d7abad..db7b4822 100644 --- a/test/integration/client/notice-tests.js +++ b/test/integration/client/notice-tests.js @@ -22,7 +22,7 @@ test('emits notify message', 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() - }, 500) + }, 100) }); assert.emits(otherClient, 'notification', function(msg) { diff --git a/test/integration/client/type-coercion-tests.js b/test/integration/client/type-coercion-tests.js index 6d99de64..01790eeb 100644 --- a/test/integration/client/type-coercion-tests.js +++ b/test/integration/client/type-coercion-tests.js @@ -4,35 +4,36 @@ var connectionString = helper.connectionString(); var testForTypeCoercion = function(type){ helper.pg.connect(connectionString, function(err, client) { assert.isNull(err) - client.query("create temp table test_type(col " + type.name + ")"); + 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) { - 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 insertQuery = client.query({ - name: 'insert type test ' + type.name, - text: 'insert into test_type(col) VALUES($1)', - values: [val] + 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[0]); + }, "row should have been called for " + type.name + " of " + val); + + client.query('delete from test_type'); }); - var query = client.query({ - name: 'get type ' + type.name , - text: 'select col from test_type' + client.query('drop table test_type', function() { + sink.add(); }); - - assert.emits(query, 'row', function(row) { - assert.strictEqual(row.col, val, "expected " + type.name + " of " + val + " but got " + row[0]); - }); - - client.query({ - name: 'delete values', - text: 'delete from test_type' - }); - sink.add(); - }); - - client.query('drop table test_type'); - }); + }) + })); }) }; @@ -82,14 +83,15 @@ var valueCount = 0; types.forEach(function(type) { valueCount += type.values.length; }) -sink = new helper.Sink(valueCount, function() { +sink = new helper.Sink(types.length, function() { helper.pg.end(); }) -types.forEach(testForTypeCoercion); +types.forEach(function(type) { + testForTypeCoercion(type) +}); test("timestampz round trip", function() { - var now = new Date(); var client = helper.client(); client.on('error', function(err) {