allow type-coercion overrides for custom objects

Attempt to call a `toPostgres` method on objects passed as query values
before converting them to JSON. This allows custom types to convert
themselves to the appropriate PostgreSQL literal.

This strategy is fully backwards-compatible and uses the same pattern as
the `toJSON` override.
This commit is contained in:
Nikhil Benesch 2014-03-30 16:47:20 -04:00
parent c41eedc3e0
commit 6ced524390
2 changed files with 55 additions and 2 deletions

View File

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

View File

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