diff --git a/lib/utils.js b/lib/utils.js index aadb3e13..3522c101 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -41,7 +41,7 @@ function arrayString(val) { //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) { +var prepareValue = function(val, seen) { if (val instanceof Buffer) { return val; } @@ -55,11 +55,24 @@ var prepareValue = function(val) { return null; } if(typeof val === 'object') { - return JSON.stringify(val); + return prepareObject(val, seen); } return val.toString(); }; +function prepareObject(val, seen) { + if(val.toPostgres && typeof val.toPostgres === 'function') { + seen = seen || []; + if (seen.indexOf(val) !== -1) { + throw new Error('circular reference detected while preparing "' + val + '" for query'); + } + seen.push(val); + + return prepareValue(val.toPostgres(), seen); + } + return JSON.stringify(val); +} + function dateToString(date) { function pad(number, digits) { number = ""+number; diff --git a/test/unit/utils-tests.js b/test/unit/utils-tests.js index 9cb2a3e4..ecf34475 100644 --- a/test/unit/utils-tests.js +++ b/test/unit/utils-tests.js @@ -119,3 +119,43 @@ test('prepareValue: arbitrary objects prepared properly', function() { var out = utils.prepareValue({ x: 42 }); assert.strictEqual(out, '{"x":42}'); }); + +test('prepareValue: objects with simple toPostgres prepared properly', function() { + var customType = { + toPostgres: function() { + return "zomgcustom!"; + } + }; + var out = utils.prepareValue(customType); + assert.strictEqual(out, "zomgcustom!"); +}); + +test('prepareValue: objects with complex toPostgres prepared properly', function() { + var buf = new Buffer("zomgcustom!"); + var customType = { + toPostgres: function() { + return [1, 2]; + } + }; + var out = utils.prepareValue(customType); + assert.strictEqual(out, '{"1","2"}'); +}); + +test('prepareValue: objects with circular toPostgres rejected', function() { + var buf = new Buffer("zomgcustom!"); + var customType = { + toPostgres: function() { + return { toPostgres: function () { return customType; } }; + } + }; + + //can't use `assert.throws` since we need to distinguish circular reference + //errors from call stack exceeded errors + try { + utils.prepareValue(customType); + } catch (e) { + assert.ok(e.message.match(/circular/), "Expected circular reference error but got " + e); + return; + } + throw new Error("Expected prepareValue to throw exception"); +});