Skip to content
This repository was archived by the owner on Sep 8, 2023. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 27 additions & 4 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,16 @@ exports.string = function(val){
* Dollar-Quoted String Constants
*/

var randomTags = [ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'g', 'j', 'k',
'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't'];
var randomTags = [
// Upper case alpha sans vowels
'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M',
'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'X', 'Z',
// Lower case alpha sans vowels
'b', 'c', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm',
'n', 'p', 'q', 'r', 's', 't', 'v', 'w', 'x', 'z',
// Numeric two through nine
'2', '3', '4', '5', '6', '7', '8', '9',
];

/**
* produces a random number from a given range
Expand All @@ -84,15 +92,30 @@ function random(start, end) {
* Format as dollar quoted string.
* see: http://www.postgresql.org/docs/8.3/interactive/sql-syntax-lexical.html#SQL-SYNTAX-DOLLAR-QUOTING
*
* Initially tries to use a tagless $$ as the quote wrapper. If it appears in the string to be quoted, then
* successively longer random tags are chosen until one is found that is not contained within the string.
*
* @param {Mixed} val
* @return {String}
* @api public
*/

exports.dollarQuotedString = function(val) {
if (val === undefined || val === null || val === '') return '';
var randomTag = '$'+ randomTags[ random(0, randomTags.length) ] +'$';
return randomTag + val + randomTag;
// Ensure val is coerced to a string
val = '' + val;
// Start with an empty tag: $$
var tag = '';
while (true) {
var dollarQuote = '$'+ tag + '$';
// Check if val contains our selected dollar quote tag
if (val.indexOf(dollarQuote) < 0) {
// Not contained so dollarQuote is safe to use
return dollarQuote + val + dollarQuote;
}
// Tag was contained within val so add random character to it
tag += randomTags[ random(0, randomTags.length) ];
}
}

/**
Expand Down
25 changes: 21 additions & 4 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ describe('escape(fmt, ...)', function(){
describe('%Q', function(){
it('should format as a dollar quoted string', function(){
escape('%Q', "Tobi's")
.should.match(/\$[a-z]{1}\$Tobi's\$[a-z]\$/);
.should.match("$$Tobi's$$");
})
})
})
Expand All @@ -59,12 +59,29 @@ describe('escape.string(val)', function(){
describe('escape.dollarQuotedString(val)', function() {
it('should coerce to a dollar quoted string', function(){
escape.dollarQuotedString().should.equal('');
escape.dollarQuotedString(0).should.match(/\$[a-z]{1}\$0\$[a-z]\$/);
escape.dollarQuotedString(15).should.match(/\$[a-z]{1}\$15\$[a-z]\$/);
escape.dollarQuotedString('something').should.match(/\$[a-z]{1}\$something\$[a-z]\$/);
escape.dollarQuotedString(0).should.equal('$$0$$');
escape.dollarQuotedString(15).should.equal('$$15$$');
escape.dollarQuotedString('something').should.equal('$$something$$');
})
})

describe('escape.dollarQuotedString(val)', function() {
it('should handle quoting strings with dollar quotes in them', function(){
escape.dollarQuotedString('$$').should.match(/\$[a-zA-Z0-9]+\$\$\$\$[a-zA-Z0-9]+\$/);
})

it('should handle dollar quotes in the string being escaped without resorting to luck', function(){
var sql = 'SELECT $a$test$a$';
// The only occurrences of $a$ in the string should be from the string itself.
// The repeated test from the for loop is to catch $a$ randomly being chosen as the tag.
for(var i=0;i<1000;i++) {
var escapedSql = escape.dollarQuotedString(sql);
var count = escapedSql.match(/\$a\$/g).length;
assert(count === 2);
}
});
})

describe('escape.ident(val)', function(){
it('should quote when necessary', function(){
escape.ident('foo').should.equal('foo');
Expand Down