Skip to content

Commit 6ced524

Browse files
committed
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.
1 parent c41eedc commit 6ced524

File tree

2 files changed

+55
-2
lines changed

2 files changed

+55
-2
lines changed

lib/utils.js

+15-2
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ function arrayString(val) {
4141
//to their 'raw' counterparts for use as a postgres parameter
4242
//note: you can override this function to provide your own conversion mechanism
4343
//for complex types, etc...
44-
var prepareValue = function(val) {
44+
var prepareValue = function(val, seen) {
4545
if (val instanceof Buffer) {
4646
return val;
4747
}
@@ -55,11 +55,24 @@ var prepareValue = function(val) {
5555
return null;
5656
}
5757
if(typeof val === 'object') {
58-
return JSON.stringify(val);
58+
return prepareObject(val, seen);
5959
}
6060
return val.toString();
6161
};
6262

63+
function prepareObject(val, seen) {
64+
if(val.toPostgres && typeof val.toPostgres === 'function') {
65+
seen = seen || [];
66+
if (seen.indexOf(val) !== -1) {
67+
throw new Error('circular reference detected while preparing "' + val + '" for query');
68+
}
69+
seen.push(val);
70+
71+
return prepareValue(val.toPostgres(), seen);
72+
}
73+
return JSON.stringify(val);
74+
}
75+
6376
function dateToString(date) {
6477
function pad(number, digits) {
6578
number = ""+number;

test/unit/utils-tests.js

+40
Original file line numberDiff line numberDiff line change
@@ -119,3 +119,43 @@ test('prepareValue: arbitrary objects prepared properly', function() {
119119
var out = utils.prepareValue({ x: 42 });
120120
assert.strictEqual(out, '{"x":42}');
121121
});
122+
123+
test('prepareValue: objects with simple toPostgres prepared properly', function() {
124+
var customType = {
125+
toPostgres: function() {
126+
return "zomgcustom!";
127+
}
128+
};
129+
var out = utils.prepareValue(customType);
130+
assert.strictEqual(out, "zomgcustom!");
131+
});
132+
133+
test('prepareValue: objects with complex toPostgres prepared properly', function() {
134+
var buf = new Buffer("zomgcustom!");
135+
var customType = {
136+
toPostgres: function() {
137+
return [1, 2];
138+
}
139+
};
140+
var out = utils.prepareValue(customType);
141+
assert.strictEqual(out, '{"1","2"}');
142+
});
143+
144+
test('prepareValue: objects with circular toPostgres rejected', function() {
145+
var buf = new Buffer("zomgcustom!");
146+
var customType = {
147+
toPostgres: function() {
148+
return { toPostgres: function () { return customType; } };
149+
}
150+
};
151+
152+
//can't use `assert.throws` since we need to distinguish circular reference
153+
//errors from call stack exceeded errors
154+
try {
155+
utils.prepareValue(customType);
156+
} catch (e) {
157+
assert.ok(e.message.match(/circular/), "Expected circular reference error but got " + e);
158+
return;
159+
}
160+
throw new Error("Expected prepareValue to throw exception");
161+
});

0 commit comments

Comments
 (0)