Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add escapeLiteral and escapeIdentifier functions #396

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
46 changes: 46 additions & 0 deletions lib/client.js
Expand Up @@ -213,6 +213,52 @@ Client.prototype.cancel = function(client, query) {
}
};

// Ported from PostgreSQL 9.2.4 source code in src/interfaces/libpq/fe-exec.c
Client.prototype.escapeIdentifier = function(str) {

var escaped = '"';

for(var i = 0; i < str.length; i++) {
var c = str[i];
if(c === '"') {
escaped += c + c;
} else {
escaped += c;
}
}

escaped += '"';

return escaped;
};

// Ported from PostgreSQL 9.2.4 source code in src/interfaces/libpq/fe-exec.c
Client.prototype.escapeLiteral = function(str) {

var hasBackslash = false;
var escaped = '\'';

for(var i = 0; i < str.length; i++) {
var c = str[i];
if(c === '\'') {
escaped += c + c;
} else if (c === '\\') {
escaped += c + c;
hasBackslash = true;
} else {
escaped += c;
}
}

escaped += '\'';

if(hasBackslash === true) {
escaped = ' E' + escaped;
}

return escaped;
};

Client.prototype._pulseQueryQueue = function() {
if(this.readyForQuery===true) {
this.activeQuery = this.queryQueue.shift();
Expand Down
56 changes: 56 additions & 0 deletions src/binding.cc
Expand Up @@ -67,6 +67,8 @@ class Connection : public ObjectWrap {
command_symbol = NODE_PSYMBOL("command");

NODE_SET_PROTOTYPE_METHOD(t, "connect", Connect);
NODE_SET_PROTOTYPE_METHOD(t, "escapeIdentifier", EscapeIdentifier);
NODE_SET_PROTOTYPE_METHOD(t, "escapeLiteral", EscapeLiteral);
NODE_SET_PROTOTYPE_METHOD(t, "_sendQuery", SendQuery);
NODE_SET_PROTOTYPE_METHOD(t, "_sendQueryWithParams", SendQueryWithParams);
NODE_SET_PROTOTYPE_METHOD(t, "_sendPrepare", SendPrepare);
Expand Down Expand Up @@ -130,6 +132,48 @@ class Connection : public ObjectWrap {
return Undefined();
}

//v8 entry point into Connection#escapeIdentifier
static Handle<Value>
EscapeIdentifier(const Arguments& args)
{
HandleScope scope;
Connection *self = ObjectWrap::Unwrap<Connection>(args.This());

char* inputStr = MallocCString(args[0]);
char* escapedStr = self->EscapeIdentifier(inputStr);
free(inputStr);

if(escapedStr == NULL) {
THROW(self->GetLastError());
}

Local<Value> jsStr = String::New(escapedStr, strlen(escapedStr));
PQfreemem(escapedStr);

return scope.Close(jsStr);
}

//v8 entry point into Connection#escapeLiteral
static Handle<Value>
EscapeLiteral(const Arguments& args)
{
HandleScope scope;
Connection *self = ObjectWrap::Unwrap<Connection>(args.This());

char* inputStr = MallocCString(args[0]);
char* escapedStr = self->EscapeLiteral(inputStr);
free(inputStr);

if(escapedStr == NULL) {
THROW(self->GetLastError());
}

Local<Value> jsStr = String::New(escapedStr, strlen(escapedStr));
PQfreemem(escapedStr);

return scope.Close(jsStr);
}

//v8 entry point into Connection#_sendQuery
static Handle<Value>
SendQuery(const Arguments& args)
Expand Down Expand Up @@ -307,6 +351,18 @@ class Connection : public ObjectWrap {
return args.This();
}

char * EscapeIdentifier(const char *str)
{
TRACE("js::EscapeIdentifier")
return PQescapeIdentifier(connection_, str, strlen(str));
}

char * EscapeLiteral(const char *str)
{
TRACE("js::EscapeLiteral")
return PQescapeLiteral(connection_, str, strlen(str));
}

int Send(const char *queryText)
{
TRACE("js::Send")
Expand Down
153 changes: 153 additions & 0 deletions test/integration/client/escape-tests.js
@@ -0,0 +1,153 @@
var helper = require(__dirname + '/test-helper');

function createClient(callback) {
var client = new Client(helper.config);
client.connect(function(err) {
return callback(client);
});
}

test('escapeLiteral: no special characters', function() {
createClient(function(client) {
var expected = "'hello world'";
var actual = client.escapeLiteral('hello world');
assert.equal(expected, actual);
client.end();
});
});

test('escapeLiteral: contains double quotes only', function() {
createClient(function(client) {
var expected = "'hello \" world'";
var actual = client.escapeLiteral('hello " world');
assert.equal(expected, actual);
client.end();
});
});

test('escapeLiteral: contains single quotes only', function() {
createClient(function(client) {
var expected = "'hello \'\' world'";
var actual = client.escapeLiteral('hello \' world');
assert.equal(expected, actual);
client.end();
});
});

test('escapeLiteral: contains backslashes only', function() {
createClient(function(client) {
var expected = " E'hello \\\\ world'";
var actual = client.escapeLiteral('hello \\ world');
assert.equal(expected, actual);
client.end();
});
});

test('escapeLiteral: contains single quotes and double quotes', function() {
createClient(function(client) {
var expected = "'hello '' \" world'";
var actual = client.escapeLiteral('hello \' " world');
assert.equal(expected, actual);
client.end();
});
});

test('escapeLiteral: contains double quotes and backslashes', function() {
createClient(function(client) {
var expected = " E'hello \\\\ \" world'";
var actual = client.escapeLiteral('hello \\ " world');
assert.equal(expected, actual);
client.end();
});
});

test('escapeLiteral: contains single quotes and backslashes', function() {
createClient(function(client) {
var expected = " E'hello \\\\ '' world'";
var actual = client.escapeLiteral('hello \\ \' world');
assert.equal(expected, actual);
client.end();
});
});

test('escapeLiteral: contains single quotes, double quotes, and backslashes', function() {
createClient(function(client) {
var expected = " E'hello \\\\ '' \" world'";
var actual = client.escapeLiteral('hello \\ \' " world');
assert.equal(expected, actual);
client.end();
});
});

test('escapeIdentifier: no special characters', function() {
createClient(function(client) {
var expected = '"hello world"';
var actual = client.escapeIdentifier('hello world');
assert.equal(expected, actual);
client.end();
});
});

test('escapeIdentifier: contains double quotes only', function() {
createClient(function(client) {
var expected = '"hello "" world"';
var actual = client.escapeIdentifier('hello " world');
assert.equal(expected, actual);
client.end();
});
});

test('escapeIdentifier: contains single quotes only', function() {
createClient(function(client) {
var expected = '"hello \' world"';
var actual = client.escapeIdentifier('hello \' world');
assert.equal(expected, actual);
client.end();
});
});

test('escapeIdentifier: contains backslashes only', function() {
createClient(function(client) {
var expected = '"hello \\ world"';
var actual = client.escapeIdentifier('hello \\ world');
assert.equal(expected, actual);
client.end();
});
});

test('escapeIdentifier: contains single quotes and double quotes', function() {
createClient(function(client) {
var expected = '"hello \' "" world"';
var actual = client.escapeIdentifier('hello \' " world');
assert.equal(expected, actual);
client.end();
});
});

test('escapeIdentifier: contains double quotes and backslashes', function() {
return createClient(function(client) {
var expected = '"hello \\ "" world"';
var actual = client.escapeIdentifier('hello \\ " world');
assert.equal(expected, actual);
client.end();
return;
});
});

test('escapeIdentifier: contains single quotes and backslashes', function() {
createClient(function(client) {
var expected = '"hello \\ \' world"';
var actual = client.escapeIdentifier('hello \\ \' world');
assert.equal(expected, actual);
client.end();
});
});

test('escapeIdentifier: contains single quotes, double quotes, and backslashes', function() {
createClient(function(client) {
var expected = '"hello \\ \' "" world"';
var actual = client.escapeIdentifier('hello \\ \' " world');
assert.equal(expected, actual);
client.end();
});
});