merge with upstream
This commit is contained in:
commit
9ad0159037
3
NEWS.md
3
NEWS.md
@ -4,6 +4,9 @@ For richer information consult the commit log on github with referenced pull req
|
||||
|
||||
We do not include break-fix version release in this file.
|
||||
|
||||
### v2.9.0
|
||||
- Add better support for [unix domain socket](https://github.com/brianc/node-postgres/pull/487) connections
|
||||
|
||||
### v2.8.0
|
||||
- Add support for parsing JSON[] and UUID[] result types
|
||||
|
||||
|
@ -2,13 +2,16 @@
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'binding',
|
||||
'sources': ['src/binding.cc'],
|
||||
'include_dirs': [
|
||||
'<!@(pg_config --includedir)',
|
||||
'<!(node -e "require(\'nan\')")'
|
||||
],
|
||||
'conditions' : [
|
||||
['OS=="win"', {
|
||||
'conditions' : [
|
||||
['"<!@(cmd /C where /Q pg_config || echo n)"!="n"',
|
||||
{
|
||||
'sources': ['src/binding.cc'],
|
||||
'include_dirs': ['<!@(pg_config --includedir)'],
|
||||
'libraries' : ['libpq.lib'],
|
||||
'msvs_settings': {
|
||||
'VCLinkerTool' : {
|
||||
@ -24,8 +27,6 @@
|
||||
'conditions' : [
|
||||
['"y"!="n"', # ToDo: add pg_config existance condition that works on linux
|
||||
{
|
||||
'sources': ['src/binding.cc'],
|
||||
'include_dirs': ['<!@(pg_config --includedir)'],
|
||||
'libraries' : ['-lpq -L<!@(pg_config --libdir)']
|
||||
}
|
||||
]
|
||||
|
@ -12,16 +12,24 @@ var val = function(key, config) {
|
||||
var url = require('url');
|
||||
//parses a connection string
|
||||
var parse = function(str) {
|
||||
var config;
|
||||
//unix socket
|
||||
if(str.charAt(0) === '/') {
|
||||
return { host: str };
|
||||
config = str.split(' ');
|
||||
return { host: config[0], database: config[1] };
|
||||
}
|
||||
// url parse expects spaces encoded as %20
|
||||
str = encodeURI(str);
|
||||
if(/ |%[^a-f0-9]|%[a-f0-9][^a-f0-9]/i.test(str)) str = encodeURI(str);
|
||||
var result = url.parse(str, true);
|
||||
var config = {};
|
||||
config = {};
|
||||
if(result.protocol == 'socket:') {
|
||||
config.host = decodeURI(result.pathname);
|
||||
config.database = result.query.db;
|
||||
config.client_encoding = result.query.encoding;
|
||||
return config;
|
||||
}
|
||||
config.host = result.hostname;
|
||||
config.database = result.pathname ? result.pathname.slice(1) : null;
|
||||
config.database = result.pathname ? decodeURI(result.pathname.slice(1)) : null;
|
||||
var auth = (result.auth || ':').split(':');
|
||||
config.user = auth[0];
|
||||
config.password = auth[1];
|
||||
|
@ -3,12 +3,12 @@ var arrayParser = require(__dirname + "/arrayParser.js");
|
||||
//parses PostgreSQL server formatted date strings into javascript date objects
|
||||
var parseDate = function(isoDate) {
|
||||
//TODO this could do w/ a refactor
|
||||
var dateMatcher = /(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})(\.\d{1,})?/;
|
||||
var dateMatcher = /(\d{1,})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})(\.\d{1,})?/;
|
||||
|
||||
var match = dateMatcher.exec(isoDate);
|
||||
//could not parse date
|
||||
if(!match) {
|
||||
dateMatcher = /^(\d{4})-(\d{2})-(\d{2})$/;
|
||||
dateMatcher = /^(\d{1,})-(\d{2})-(\d{2})$/;
|
||||
match = dateMatcher.test(isoDate);
|
||||
if(!match) {
|
||||
return null;
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "pg",
|
||||
"version": "2.8.3",
|
||||
"version": "2.9.0",
|
||||
"description": "PostgreSQL client - pure javascript & libpq with the same API",
|
||||
"keywords": [
|
||||
"postgres",
|
||||
@ -20,7 +20,8 @@
|
||||
"dependencies": {
|
||||
"generic-pool": "2.0.3",
|
||||
"buffer-writer": "1.0.0",
|
||||
"pgpass": "0.0.1"
|
||||
"pgpass": "0.0.1",
|
||||
"nan": "~0.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"jshint": "1.1.0",
|
||||
|
217
src/binding.cc
217
src/binding.cc
@ -1,7 +1,6 @@
|
||||
#include <pg_config.h>
|
||||
#include <libpq-fe.h>
|
||||
#include <node.h>
|
||||
#include <node_buffer.h>
|
||||
#include <nan.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
@ -17,30 +16,11 @@
|
||||
#define SINGLE_ROW_SUPPORTED
|
||||
#endif
|
||||
|
||||
#define THROW(msg) return ThrowException(Exception::Error(String::New(msg)));
|
||||
#define THROW(msg) NanThrowError(msg); NanReturnUndefined();
|
||||
|
||||
using namespace v8;
|
||||
using namespace node;
|
||||
|
||||
static Persistent<String> severity_symbol;
|
||||
static Persistent<String> code_symbol;
|
||||
static Persistent<String> detail_symbol;
|
||||
static Persistent<String> hint_symbol;
|
||||
static Persistent<String> position_symbol;
|
||||
static Persistent<String> internalPosition_symbol;
|
||||
static Persistent<String> internalQuery_symbol;
|
||||
static Persistent<String> where_symbol;
|
||||
static Persistent<String> file_symbol;
|
||||
static Persistent<String> line_symbol;
|
||||
static Persistent<String> routine_symbol;
|
||||
static Persistent<String> name_symbol;
|
||||
static Persistent<String> value_symbol;
|
||||
static Persistent<String> type_symbol;
|
||||
static Persistent<String> channel_symbol;
|
||||
static Persistent<String> payload_symbol;
|
||||
static Persistent<String> emit_symbol;
|
||||
static Persistent<String> command_symbol;
|
||||
|
||||
class Connection : public ObjectWrap {
|
||||
|
||||
public:
|
||||
@ -49,30 +29,11 @@ public:
|
||||
static void
|
||||
Init (Handle<Object> target)
|
||||
{
|
||||
HandleScope scope;
|
||||
NanScope();
|
||||
Local<FunctionTemplate> t = FunctionTemplate::New(New);
|
||||
|
||||
t->InstanceTemplate()->SetInternalFieldCount(1);
|
||||
t->SetClassName(String::NewSymbol("Connection"));
|
||||
|
||||
emit_symbol = NODE_PSYMBOL("emit");
|
||||
severity_symbol = NODE_PSYMBOL("severity");
|
||||
code_symbol = NODE_PSYMBOL("code");
|
||||
detail_symbol = NODE_PSYMBOL("detail");
|
||||
hint_symbol = NODE_PSYMBOL("hint");
|
||||
position_symbol = NODE_PSYMBOL("position");
|
||||
internalPosition_symbol = NODE_PSYMBOL("internalPosition");
|
||||
internalQuery_symbol = NODE_PSYMBOL("internalQuery");
|
||||
where_symbol = NODE_PSYMBOL("where");
|
||||
file_symbol = NODE_PSYMBOL("file");
|
||||
line_symbol = NODE_PSYMBOL("line");
|
||||
routine_symbol = NODE_PSYMBOL("routine");
|
||||
name_symbol = NODE_PSYMBOL("name");
|
||||
value_symbol = NODE_PSYMBOL("value");
|
||||
type_symbol = NODE_PSYMBOL("dataTypeID");
|
||||
channel_symbol = NODE_PSYMBOL("channel");
|
||||
payload_symbol = NODE_PSYMBOL("payload");
|
||||
command_symbol = NODE_PSYMBOL("command");
|
||||
t->SetClassName(NanSymbol("Connection"));
|
||||
|
||||
NODE_SET_PROTOTYPE_METHOD(t, "connect", Connect);
|
||||
#ifdef ESCAPE_SUPPORTED
|
||||
@ -107,10 +68,9 @@ public:
|
||||
}
|
||||
|
||||
//v8 entry point into Connection#connect
|
||||
static Handle<Value>
|
||||
Connect(const Arguments& args)
|
||||
static NAN_METHOD(Connect)
|
||||
{
|
||||
HandleScope scope;
|
||||
NanScope();
|
||||
Connection *self = ObjectWrap::Unwrap<Connection>(args.This());
|
||||
if(args.Length() == 0 || !args[0]->IsString()) {
|
||||
THROW("Must include connection string as only argument to connect");
|
||||
@ -123,14 +83,13 @@ public:
|
||||
self -> DestroyConnection();
|
||||
}
|
||||
|
||||
return Undefined();
|
||||
NanReturnUndefined();
|
||||
}
|
||||
|
||||
//v8 entry point into Connection#cancel
|
||||
static Handle<Value>
|
||||
Cancel(const Arguments& args)
|
||||
static NAN_METHOD(Cancel)
|
||||
{
|
||||
HandleScope scope;
|
||||
NanScope();
|
||||
Connection *self = ObjectWrap::Unwrap<Connection>(args.This());
|
||||
|
||||
bool success = self->Cancel();
|
||||
@ -139,15 +98,14 @@ public:
|
||||
self -> DestroyConnection();
|
||||
}
|
||||
|
||||
return Undefined();
|
||||
NanReturnUndefined();
|
||||
}
|
||||
|
||||
#ifdef ESCAPE_SUPPORTED
|
||||
//v8 entry point into Connection#escapeIdentifier
|
||||
static Handle<Value>
|
||||
EscapeIdentifier(const Arguments& args)
|
||||
static NAN_METHOD(EscapeIdentifier)
|
||||
{
|
||||
HandleScope scope;
|
||||
NanScope();
|
||||
Connection *self = ObjectWrap::Unwrap<Connection>(args.This());
|
||||
|
||||
char* inputStr = MallocCString(args[0]);
|
||||
@ -166,14 +124,13 @@ public:
|
||||
Local<Value> jsStr = String::New(escapedStr, strlen(escapedStr));
|
||||
PQfreemem(escapedStr);
|
||||
|
||||
return scope.Close(jsStr);
|
||||
NanReturnValue(jsStr);
|
||||
}
|
||||
|
||||
//v8 entry point into Connection#escapeLiteral
|
||||
static Handle<Value>
|
||||
EscapeLiteral(const Arguments& args)
|
||||
static NAN_METHOD(EscapeLiteral)
|
||||
{
|
||||
HandleScope scope;
|
||||
NanScope();
|
||||
Connection *self = ObjectWrap::Unwrap<Connection>(args.This());
|
||||
|
||||
char* inputStr = MallocCString(args[0]);
|
||||
@ -192,15 +149,14 @@ public:
|
||||
Local<Value> jsStr = String::New(escapedStr, strlen(escapedStr));
|
||||
PQfreemem(escapedStr);
|
||||
|
||||
return scope.Close(jsStr);
|
||||
NanReturnValue(jsStr);
|
||||
}
|
||||
#endif
|
||||
|
||||
//v8 entry point into Connection#_sendQuery
|
||||
static Handle<Value>
|
||||
SendQuery(const Arguments& args)
|
||||
static NAN_METHOD(SendQuery)
|
||||
{
|
||||
HandleScope scope;
|
||||
NanScope();
|
||||
Connection *self = ObjectWrap::Unwrap<Connection>(args.This());
|
||||
const char *lastErrorMessage;
|
||||
if(!args[0]->IsString()) {
|
||||
@ -218,23 +174,22 @@ public:
|
||||
}
|
||||
//TODO should we flush before throw?
|
||||
self->Flush();
|
||||
return Undefined();
|
||||
NanReturnUndefined();
|
||||
}
|
||||
|
||||
//v8 entry point into Connection#_sendQueryWithParams
|
||||
static Handle<Value>
|
||||
SendQueryWithParams(const Arguments& args)
|
||||
static NAN_METHOD(SendQueryWithParams)
|
||||
{
|
||||
HandleScope scope;
|
||||
NanScope();
|
||||
//dispatch non-prepared parameterized query
|
||||
return DispatchParameterizedQuery(args, false);
|
||||
DispatchParameterizedQuery(args, false);
|
||||
NanReturnUndefined();
|
||||
}
|
||||
|
||||
//v8 entry point into Connection#_sendPrepare(string queryName, string queryText, int nParams)
|
||||
static Handle<Value>
|
||||
SendPrepare(const Arguments& args)
|
||||
static NAN_METHOD(SendPrepare)
|
||||
{
|
||||
HandleScope scope;
|
||||
NanScope();
|
||||
|
||||
Connection *self = ObjectWrap::Unwrap<Connection>(args.This());
|
||||
String::Utf8Value queryName(args[0]);
|
||||
@ -243,32 +198,33 @@ public:
|
||||
bool singleRowMode = (bool)args[3]->Int32Value();
|
||||
self->SendPrepare(*queryName, *queryText, length, singleRowMode);
|
||||
|
||||
return Undefined();
|
||||
NanReturnUndefined();
|
||||
}
|
||||
|
||||
//v8 entry point into Connection#_sendQueryPrepared(string queryName, string[] paramValues)
|
||||
static Handle<Value>
|
||||
SendQueryPrepared(const Arguments& args)
|
||||
static NAN_METHOD(SendQueryPrepared)
|
||||
{
|
||||
HandleScope scope;
|
||||
NanScope();
|
||||
//dispatch prepared parameterized query
|
||||
return DispatchParameterizedQuery(args, true);
|
||||
DispatchParameterizedQuery(args, true);
|
||||
NanReturnUndefined();
|
||||
}
|
||||
|
||||
static Handle<Value>
|
||||
DispatchParameterizedQuery(const Arguments& args, bool isPrepared)
|
||||
static void DispatchParameterizedQuery(_NAN_METHOD_ARGS, bool isPrepared)
|
||||
{
|
||||
HandleScope scope;
|
||||
NanScope();
|
||||
Connection *self = ObjectWrap::Unwrap<Connection>(args.This());
|
||||
|
||||
String::Utf8Value queryName(args[0]);
|
||||
//TODO this is much copy/pasta code
|
||||
if(!args[0]->IsString()) {
|
||||
THROW("First parameter must be a string");
|
||||
NanThrowError("First parameter must be a string");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!args[1]->IsArray()) {
|
||||
THROW("Values must be an array");
|
||||
NanThrowError("Values must be an array");
|
||||
return;
|
||||
}
|
||||
|
||||
Local<Array> jsParams = Local<Array>::Cast(args[1]);
|
||||
@ -277,7 +233,8 @@ public:
|
||||
|
||||
char** paramValues = ArgToCStringArray(jsParams);
|
||||
if(!paramValues) {
|
||||
THROW("Unable to allocate char **paramValues from Local<Array> of v8 params");
|
||||
NanThrowError("Unable to allocate char **paramValues from Local<Array> of v8 params");
|
||||
return;
|
||||
}
|
||||
|
||||
char* queryText = MallocCString(args[0]);
|
||||
@ -293,22 +250,21 @@ public:
|
||||
free(queryText);
|
||||
ReleaseCStringArray(paramValues, len);
|
||||
if(result == 1) {
|
||||
return Undefined();
|
||||
return;
|
||||
}
|
||||
self->EmitLastError();
|
||||
THROW("Postgres returned non-1 result from query dispatch.");
|
||||
NanThrowError("Postgres returned non-1 result from query dispatch.");
|
||||
}
|
||||
|
||||
//v8 entry point into Connection#end
|
||||
static Handle<Value>
|
||||
End(const Arguments& args)
|
||||
static NAN_METHOD(End)
|
||||
{
|
||||
HandleScope scope;
|
||||
NanScope();
|
||||
|
||||
Connection *self = ObjectWrap::Unwrap<Connection>(args.This());
|
||||
|
||||
self->End();
|
||||
return Undefined();
|
||||
NanReturnUndefined();
|
||||
}
|
||||
|
||||
uv_poll_t read_watcher_;
|
||||
@ -340,20 +296,18 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
static Handle<Value>
|
||||
SendCopyFromChunk(const Arguments& args) {
|
||||
HandleScope scope;
|
||||
static NAN_METHOD(SendCopyFromChunk) {
|
||||
NanScope();
|
||||
Connection *self = ObjectWrap::Unwrap<Connection>(args.This());
|
||||
//TODO handle errors in some way
|
||||
if (args.Length() < 1 && !Buffer::HasInstance(args[0])) {
|
||||
THROW("SendCopyFromChunk requires 1 Buffer argument");
|
||||
}
|
||||
self->SendCopyFromChunk(args[0]->ToObject());
|
||||
return Undefined();
|
||||
NanReturnUndefined();
|
||||
}
|
||||
static Handle<Value>
|
||||
EndCopyFrom(const Arguments& args) {
|
||||
HandleScope scope;
|
||||
static NAN_METHOD(EndCopyFrom) {
|
||||
NanScope();
|
||||
Connection *self = ObjectWrap::Unwrap<Connection>(args.This());
|
||||
char * error_msg = NULL;
|
||||
if (args[0]->IsString()) {
|
||||
@ -362,19 +316,18 @@ public:
|
||||
//TODO handle errors in some way
|
||||
self->EndCopyFrom(error_msg);
|
||||
free(error_msg);
|
||||
return Undefined();
|
||||
NanReturnUndefined();
|
||||
}
|
||||
|
||||
protected:
|
||||
//v8 entry point to constructor
|
||||
static Handle<Value>
|
||||
New (const Arguments& args)
|
||||
static NAN_METHOD(New)
|
||||
{
|
||||
HandleScope scope;
|
||||
NanScope();
|
||||
Connection *connection = new Connection();
|
||||
connection->Wrap(args.This());
|
||||
|
||||
return args.This();
|
||||
NanReturnValue(args.This());
|
||||
}
|
||||
|
||||
#ifdef ESCAPE_SUPPORTED
|
||||
@ -523,7 +476,7 @@ protected:
|
||||
|
||||
void HandleNotice(const char *message)
|
||||
{
|
||||
HandleScope scope;
|
||||
NanScope();
|
||||
Handle<Value> notice = String::New(message);
|
||||
Emit("notice", ¬ice);
|
||||
}
|
||||
@ -552,7 +505,7 @@ protected:
|
||||
|
||||
//declare handlescope as this method is entered via a libuv callback
|
||||
//and not part of the public v8 interface
|
||||
HandleScope scope;
|
||||
NanScope();
|
||||
if (this->copyOutMode_) {
|
||||
this->HandleCopyOut();
|
||||
}
|
||||
@ -584,8 +537,8 @@ protected:
|
||||
TRACE("PQnotifies");
|
||||
while ((notify = PQnotifies(connection_))) {
|
||||
Local<Object> result = Object::New();
|
||||
result->Set(channel_symbol, String::New(notify->relname));
|
||||
result->Set(payload_symbol, String::New(notify->extra));
|
||||
result->Set(NanSymbol("channel"), String::New(notify->relname));
|
||||
result->Set(NanSymbol("payload"), String::New(notify->extra));
|
||||
Handle<Value> res = (Handle<Value>)result;
|
||||
Emit("notification", &res);
|
||||
PQfreemem(notify);
|
||||
@ -604,11 +557,9 @@ protected:
|
||||
bool HandleCopyOut () {
|
||||
char * buffer = NULL;
|
||||
int copied;
|
||||
Buffer * chunk;
|
||||
copied = PQgetCopyData(connection_, &buffer, 1);
|
||||
while (copied > 0) {
|
||||
chunk = Buffer::New(buffer, copied);
|
||||
Local<Value> node_chunk = Local<Value>::New(chunk->handle_);
|
||||
Local<Value> node_chunk = NanNewBufferHandle(buffer, copied);
|
||||
Emit("copyData", &node_chunk);
|
||||
PQfreemem(buffer);
|
||||
copied = PQgetCopyData(connection_, &buffer, 1);
|
||||
@ -633,18 +584,18 @@ protected:
|
||||
//javascript & c++ might introduce overhead (requires benchmarking)
|
||||
void EmitRowDescription(const PGresult* result)
|
||||
{
|
||||
HandleScope scope;
|
||||
NanScope();
|
||||
Local<Array> row = Array::New();
|
||||
int fieldCount = PQnfields(result);
|
||||
for(int fieldNumber = 0; fieldNumber < fieldCount; fieldNumber++) {
|
||||
Local<Object> field = Object::New();
|
||||
//name of field
|
||||
char* fieldName = PQfname(result, fieldNumber);
|
||||
field->Set(name_symbol, String::New(fieldName));
|
||||
field->Set(NanSymbol("name"), String::New(fieldName));
|
||||
|
||||
//oid of type of field
|
||||
int fieldType = PQftype(result, fieldNumber);
|
||||
field->Set(type_symbol, Integer::New(fieldType));
|
||||
field->Set(NanSymbol("dataTypeID"), Integer::New(fieldType));
|
||||
|
||||
row->Set(Integer::New(fieldNumber), field);
|
||||
}
|
||||
@ -706,10 +657,10 @@ protected:
|
||||
|
||||
void EmitCommandMetaData(PGresult* result)
|
||||
{
|
||||
HandleScope scope;
|
||||
NanScope();
|
||||
Local<Object> info = Object::New();
|
||||
info->Set(command_symbol, String::New(PQcmdStatus(result)));
|
||||
info->Set(value_symbol, String::New(PQcmdTuples(result)));
|
||||
info->Set(NanSymbol("command"), String::New(PQcmdStatus(result)));
|
||||
info->Set(NanSymbol("value"), String::New(PQcmdTuples(result)));
|
||||
Handle<Value> e = (Handle<Value>)info;
|
||||
Emit("_cmdStatus", &e);
|
||||
}
|
||||
@ -720,7 +671,7 @@ protected:
|
||||
//javascript & c++ might introduce overhead (requires benchmarking)
|
||||
void HandleTuplesResult(const PGresult* result)
|
||||
{
|
||||
HandleScope scope;
|
||||
NanScope();
|
||||
int rowCount = PQntuples(result);
|
||||
for(int rowNumber = 0; rowNumber < rowCount; rowNumber++) {
|
||||
//create result object for this row
|
||||
@ -744,7 +695,7 @@ protected:
|
||||
|
||||
void HandleErrorResult(const PGresult* result)
|
||||
{
|
||||
HandleScope scope;
|
||||
NanScope();
|
||||
//instantiate the return object as an Error with the summary Postgres message
|
||||
TRACE("ReadResultField");
|
||||
const char* errorMessage = PQresultErrorField(result, PG_DIAG_MESSAGE_PRIMARY);
|
||||
@ -756,24 +707,25 @@ protected:
|
||||
Local<Object> msg = Local<Object>::Cast(Exception::Error(String::New(errorMessage)));
|
||||
TRACE("AttachErrorFields");
|
||||
//add the other information returned by Postgres to the error object
|
||||
AttachErrorField(result, msg, severity_symbol, PG_DIAG_SEVERITY);
|
||||
AttachErrorField(result, msg, code_symbol, PG_DIAG_SQLSTATE);
|
||||
AttachErrorField(result, msg, detail_symbol, PG_DIAG_MESSAGE_DETAIL);
|
||||
AttachErrorField(result, msg, hint_symbol, PG_DIAG_MESSAGE_HINT);
|
||||
AttachErrorField(result, msg, position_symbol, PG_DIAG_STATEMENT_POSITION);
|
||||
AttachErrorField(result, msg, internalPosition_symbol, PG_DIAG_INTERNAL_POSITION);
|
||||
AttachErrorField(result, msg, internalQuery_symbol, PG_DIAG_INTERNAL_QUERY);
|
||||
AttachErrorField(result, msg, where_symbol, PG_DIAG_CONTEXT);
|
||||
AttachErrorField(result, msg, file_symbol, PG_DIAG_SOURCE_FILE);
|
||||
AttachErrorField(result, msg, line_symbol, PG_DIAG_SOURCE_LINE);
|
||||
AttachErrorField(result, msg, routine_symbol, PG_DIAG_SOURCE_FUNCTION);
|
||||
AttachErrorField(result, msg, NanSymbol("severity"), PG_DIAG_SEVERITY);
|
||||
AttachErrorField(result, msg, NanSymbol("code"), PG_DIAG_SQLSTATE);
|
||||
AttachErrorField(result, msg, NanSymbol("detail"), PG_DIAG_MESSAGE_DETAIL);
|
||||
AttachErrorField(result, msg, NanSymbol("hint"), PG_DIAG_MESSAGE_HINT);
|
||||
AttachErrorField(result, msg, NanSymbol("position"), PG_DIAG_STATEMENT_POSITION);
|
||||
AttachErrorField(result, msg, NanSymbol("internalPosition"), PG_DIAG_INTERNAL_POSITION);
|
||||
AttachErrorField(result, msg, NanSymbol("internalQuery"), PG_DIAG_INTERNAL_QUERY);
|
||||
AttachErrorField(result, msg, NanSymbol("where"), PG_DIAG_CONTEXT);
|
||||
AttachErrorField(result, msg, NanSymbol("file"), PG_DIAG_SOURCE_FILE);
|
||||
AttachErrorField(result, msg, NanSymbol("line"), PG_DIAG_SOURCE_LINE);
|
||||
AttachErrorField(result, msg, NanSymbol("routine"), PG_DIAG_SOURCE_FUNCTION);
|
||||
Handle<Value> m = msg;
|
||||
TRACE("EmitError");
|
||||
Emit("_error", &m);
|
||||
}
|
||||
|
||||
void AttachErrorField(const PGresult *result, const Local<Object> msg, const Persistent<String> symbol, int fieldcode)
|
||||
void AttachErrorField(const PGresult *result, const Local<Object> msg, const Local<String> symbol, int fieldcode)
|
||||
{
|
||||
NanScope();
|
||||
char *val = PQresultErrorField(result, fieldcode);
|
||||
if(val) {
|
||||
msg->Set(symbol, String::New(val));
|
||||
@ -793,26 +745,26 @@ protected:
|
||||
private:
|
||||
//EventEmitter was removed from c++ in node v0.5.x
|
||||
void Emit(const char* message) {
|
||||
HandleScope scope;
|
||||
NanScope();
|
||||
Handle<Value> args[1] = { String::New(message) };
|
||||
Emit(1, args);
|
||||
}
|
||||
|
||||
void Emit(const char* message, Handle<Value>* arg) {
|
||||
HandleScope scope;
|
||||
NanScope();
|
||||
Handle<Value> args[2] = { String::New(message), *arg };
|
||||
Emit(2, args);
|
||||
}
|
||||
|
||||
void Emit(int length, Handle<Value> *args) {
|
||||
HandleScope scope;
|
||||
NanScope();
|
||||
|
||||
Local<Value> emit_v = this->handle_->Get(emit_symbol);
|
||||
Local<Value> emit_v = NanObjectWrapHandle(this)->Get(NanSymbol("emit"));
|
||||
assert(emit_v->IsFunction());
|
||||
Local<Function> emit_f = emit_v.As<Function>();
|
||||
|
||||
TryCatch tc;
|
||||
emit_f->Call(this->handle_, length, args);
|
||||
emit_f->Call(NanObjectWrapHandle(this), length, args);
|
||||
if(tc.HasCaught()) {
|
||||
FatalException(tc);
|
||||
}
|
||||
@ -849,6 +801,7 @@ private:
|
||||
|
||||
void EmitError(const char *message)
|
||||
{
|
||||
NanScope();
|
||||
Local<Value> exception = Exception::Error(String::New(message));
|
||||
Emit("_error", &exception);
|
||||
}
|
||||
@ -994,7 +947,7 @@ private:
|
||||
|
||||
extern "C" void init (Handle<Object> target)
|
||||
{
|
||||
HandleScope scope;
|
||||
NanScope();
|
||||
Connection::Init(target);
|
||||
}
|
||||
NODE_MODULE(binding, init)
|
||||
|
@ -151,19 +151,23 @@ test("timestampz round trip", function() {
|
||||
});
|
||||
|
||||
if(!helper.config.binary) {
|
||||
test('early AD & BC date', function() {
|
||||
test('date range extremes', function() {
|
||||
var client = helper.client();
|
||||
client.on('error', function(err) {
|
||||
console.log(err);
|
||||
client.end();
|
||||
});
|
||||
|
||||
client.query('SELECT $1::TIMESTAMPTZ as when', ["0062-03-08 14:32:00"], assert.success(function(res) {
|
||||
assert.equal(res.rows[0].when.getFullYear(), 62);
|
||||
// PostgreSQL supports date range of 4713 BCE to 294276 CE
|
||||
// http://www.postgresql.org/docs/9.2/static/datatype-datetime.html
|
||||
// ECMAScript supports date range of Apr 20 271821 BCE to Sep 13 275760 CE
|
||||
// http://ecma-international.org/ecma-262/5.1/#sec-15.9.1.1
|
||||
client.query('SELECT $1::TIMESTAMPTZ as when', ["275760-09-13 00:00:00 GMT"], assert.success(function(res) {
|
||||
assert.equal(res.rows[0].when.getFullYear(), 275760);
|
||||
}))
|
||||
|
||||
client.query('SELECT $1::TIMESTAMPTZ as when', ["0062-03-08 14:32:00 BC"], assert.success(function(res) {
|
||||
assert.equal(res.rows[0].when.getFullYear(), -62);
|
||||
client.query('SELECT $1::TIMESTAMPTZ as when', ["4713-12-31 12:31:59 BC GMT"], assert.success(function(res) {
|
||||
assert.equal(res.rows[0].when.getFullYear(), -4713);
|
||||
}))
|
||||
|
||||
client.on('drain', client.end.bind(client));
|
||||
|
@ -11,11 +11,11 @@ test('disconnects', function() {
|
||||
helper.pg.connect(config, function(err, client, done) {
|
||||
assert.isNull(err);
|
||||
client.query("SELECT * FROM NOW()", function(err, result) {
|
||||
process.nextTick(function() {
|
||||
setTimeout(function() {
|
||||
assert.equal(called, false, "Should not have disconnected yet")
|
||||
sink.add();
|
||||
done();
|
||||
})
|
||||
}, 0)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -47,10 +47,44 @@ test('ConnectionParameters initializing from config', function() {
|
||||
assert.ok(subject.isDomainSocket === false);
|
||||
});
|
||||
|
||||
test('escape spaces if present', function() {
|
||||
subject = new ConnectionParameters('postgres://localhost/post gres');
|
||||
assert.equal(subject.database, 'post gres');
|
||||
});
|
||||
|
||||
test('do not double escape spaces', function() {
|
||||
subject = new ConnectionParameters('postgres://localhost/post%20gres');
|
||||
assert.equal(subject.database, 'post gres');
|
||||
});
|
||||
|
||||
test('initializing with unix domain socket', function() {
|
||||
var subject = new ConnectionParameters('/var/run/');
|
||||
assert.ok(subject.isDomainSocket);
|
||||
assert.equal(subject.host, '/var/run/');
|
||||
assert.equal(subject.database, defaults.user);
|
||||
});
|
||||
|
||||
test('initializing with unix domain socket and a specific database, the simple way', function() {
|
||||
var subject = new ConnectionParameters('/var/run/ mydb');
|
||||
assert.ok(subject.isDomainSocket);
|
||||
assert.equal(subject.host, '/var/run/');
|
||||
assert.equal(subject.database, 'mydb');
|
||||
});
|
||||
|
||||
test('initializing with unix domain socket, the health way', function() {
|
||||
var subject = new ConnectionParameters('socket:/some path/?db=my[db]&encoding=utf8');
|
||||
assert.ok(subject.isDomainSocket);
|
||||
assert.equal(subject.host, '/some path/');
|
||||
assert.equal(subject.database, 'my[db]', 'must to be escaped and unescaped trough "my%5Bdb%5D"');
|
||||
assert.equal(subject.client_encoding, 'utf8');
|
||||
});
|
||||
|
||||
test('initializing with unix domain socket, the escaped health way', function() {
|
||||
var subject = new ConnectionParameters('socket:/some%20path/?db=my%2Bdb&encoding=utf8');
|
||||
assert.ok(subject.isDomainSocket);
|
||||
assert.equal(subject.host, '/some path/');
|
||||
assert.equal(subject.database, 'my+db');
|
||||
assert.equal(subject.client_encoding, 'utf8');
|
||||
});
|
||||
|
||||
test('libpq connection string building', function() {
|
||||
|
Loading…
Reference in New Issue
Block a user