From fa35c0a7f626903a2b3c0133b53ac230aaf59e0e Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Wed, 6 Jan 2010 17:13:03 -0800 Subject: [PATCH 01/16] Update API to match latest node --- binding.cc | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/binding.cc b/binding.cc index 245d284..c41c254 100644 --- a/binding.cc +++ b/binding.cc @@ -6,7 +6,10 @@ using namespace v8; using namespace node; - +static Persistent ready_symbol; +static Persistent result_symbol; +static Persistent close_symbol; +static Persistent connect_symbol; #define READY_STATE_SYMBOL String::NewSymbol("readyState") class Connection : public EventEmitter { @@ -21,6 +24,11 @@ class Connection : public EventEmitter { t->Inherit(EventEmitter::constructor_template); t->InstanceTemplate()->SetInternalFieldCount(1); + close_symbol = NODE_PSYMBOL("close"); + connect_symbol = NODE_PSYMBOL("connect"); + result_symbol = NODE_PSYMBOL("result"); + ready_symbol = NODE_PSYMBOL("ready"); + NODE_SET_PROTOTYPE_METHOD(t, "connect", Connect); NODE_SET_PROTOTYPE_METHOD(t, "close", Close); NODE_SET_PROTOTYPE_METHOD(t, "reset", Reset); @@ -67,7 +75,7 @@ class Connection : public EventEmitter { */ ev_io_start(EV_DEFAULT_ &write_watcher_); - Attach(); + Ref(); return true; } @@ -112,11 +120,11 @@ class Connection : public EventEmitter { PQfinish(connection_); connection_ = NULL; if (exception.IsEmpty()) { - Emit("close", 0, NULL); + Emit(close_symbol, 0, NULL); } else { - Emit("close", 1, &exception); + Emit(close_symbol, 1, &exception); } - Detach(); + Unref(); } char * ErrorMessage ( ) @@ -297,12 +305,12 @@ class Connection : public EventEmitter { } if (status == PGRES_POLLING_OK) { - Emit("connect", 0, NULL); + Emit(connect_symbol, 0, NULL); connecting_ = resetting_ = false; ev_io_start(EV_DEFAULT_ &read_watcher_); return; } - + CloseConnectionWithError(); } @@ -337,7 +345,7 @@ class Connection : public EventEmitter { default: #ifndef NDEBUG - printf("Unhandled OID: %d\n", t); +// printf("Unhandled OID: %d\n", t); #endif cell = String::New(string); } @@ -428,26 +436,26 @@ class Connection : public EventEmitter { switch (PQresultStatus(result)) { case PGRES_EMPTY_QUERY: case PGRES_COMMAND_OK: - Emit("result", 0, NULL); + Emit(result_symbol, 0, NULL); break; case PGRES_TUPLES_OK: tuples = BuildTuples(result); - Emit("result", 1, &tuples); + Emit(result_symbol, 1, &tuples); break; case PGRES_COPY_OUT: case PGRES_COPY_IN: assert(0 && "Not yet implemented."); exception = Exception::Error(String::New("Not yet implemented")); - Emit("result", 1, &exception); + Emit(result_symbol, 1, &exception); break; case PGRES_BAD_RESPONSE: case PGRES_NONFATAL_ERROR: case PGRES_FATAL_ERROR: exception = BuildResultException(result); - Emit("result", 1, &exception); + Emit(result_symbol, 1, &exception); break; } } @@ -478,7 +486,7 @@ class Connection : public EventEmitter { EmitResult(result); PQclear(result); } - Emit("ready", 0, NULL); + Emit(ready_symbol, 0, NULL); } } From 4e40e1eb2b1de3f5aee3f82061898d556968ca4e Mon Sep 17 00:00:00 2001 From: Devin Torres Date: Mon, 1 Mar 2010 16:49:42 -0600 Subject: [PATCH 02/16] Promises are depricated. Use callbacks instead --- .gitignore | 2 ++ postgres.js | 19 ++++++------------- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/.gitignore b/.gitignore index 4bd6440..f7e326b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ +.lock-wscript binding.o binding.node +build/ diff --git a/postgres.js b/postgres.js index e3ebcb3..2daf01e 100644 --- a/postgres.js +++ b/postgres.js @@ -11,17 +11,14 @@ Connection.prototype.maybeDispatchQuery = function () { if (this.readyState != "OK") return; if (!this.currentQuery && this._queries.length > 0) { this.currentQuery = this._queries.shift(); - this.dispatchQuery(this.currentQuery.sql); + this.dispatchQuery(this.currentQuery[0]); } }; -Connection.prototype.query = function (sql) { - if (!this._queries) this._queries = []; - var promise = new process.Promise; - promise.sql = sql; - this._queries.push(promise); +Connection.prototype.query = function (sql, callback) { + this._queries = this._queries || []; + this._queries.push([sql, callback]); this.maybeDispatchQuery(); - return promise; }; exports.createConnection = function (conninfo) { @@ -33,13 +30,9 @@ exports.createConnection = function (conninfo) { c.addListener("result", function (arg) { process.assert(c.currentQuery); - var promise = c.currentQuery; + var callback = c.currentQuery[1]; c.currentQuery = null; - if (arg instanceof Error) { - promise.emitError([arg]); - } else { - promise.emitSuccess([arg]); - } + if (callback) callback(arg[0]); }); c.addListener("ready", function () { From 1adca05969f043b6ccedd3c0254a5f15077f40e3 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Fri, 5 Mar 2010 13:44:16 -0800 Subject: [PATCH 03/16] Update tests to not use promises --- test.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/test.js b/test.js index 2451f01..b5b312d 100644 --- a/test.js +++ b/test.js @@ -15,22 +15,24 @@ c.addListener("close", function (e) { } }); -c.query("select * from test;").addCallback(function (rows) { +c.query("select * from test;", function (rows) { puts("result1:"); p(rows); }); -c.query("select * from test limit 1;").addCallback(function (rows) { +c.query("select * from test limit 1;", function (rows) { puts("result2:"); p(rows); }); -c.query("select ____ from test limit 1;").addCallback(function (rows) { +c.query("select ____ from test limit 1;", function (rows) { + if (false /* error */ ) { + puts("error! "+ e.message); + puts("full: "+ e.full); + puts("severity: "+ e.severity); + c.close(); + return; + } puts("result3:"); p(rows); -}).addErrback(function (e) { - puts("error! "+ e.message); - puts("full: "+ e.full); - puts("severity: "+ e.severity); - c.close(); }); From f2949a8838b23c303e3ca620041265f448bdda00 Mon Sep 17 00:00:00 2001 From: Scott McWhirter Date: Fri, 5 Mar 2010 13:45:09 -0800 Subject: [PATCH 04/16] Fix the query queuing to take libpq transaction status into account --- binding.cc | 21 ++++++++++++++++++++- test.js | 7 +++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/binding.cc b/binding.cc index c41c254..5d9c854 100644 --- a/binding.cc +++ b/binding.cc @@ -228,7 +228,26 @@ class Connection : public EventEmitter { HandleScope scope; - const char *s; + const char *s = NULL; + + switch(PQtransactionStatus(connection->connection_)) { + case PQTRANS_IDLE: + s = "OK"; + break; + case PQTRANS_ACTIVE: + s = "commandActive"; + break; + case PQTRANS_UNKNOWN: + s = "bad"; + break; + case PQTRANS_INTRANS: + s = "commandActiveButIdle"; + break; + } + + if(s != NULL) { + return scope.Close(String::NewSymbol(s)); + } switch (PQstatus(connection->connection_)) { case CONNECTION_STARTED: diff --git a/test.js b/test.js index b5b312d..5dcd5e8 100644 --- a/test.js +++ b/test.js @@ -36,3 +36,10 @@ c.query("select ____ from test limit 1;", function (rows) { puts("result3:"); p(rows); }); + +c.query("select * from test;", function () { + c.query("select * from test;", function (row) { + puts("result4:"); + p(rows); + }); +}); From fa5fcd94aaa1e5c9321f982d9d2fd536806c8641 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Fri, 5 Mar 2010 14:05:00 -0800 Subject: [PATCH 05/16] Callbacks should give error in first argument And fix cast error --- binding.cc | 9 +++++++-- postgres.js | 6 ++++-- test.js | 30 +++++++++++++++--------------- 3 files changed, 26 insertions(+), 19 deletions(-) diff --git a/binding.cc b/binding.cc index 5d9c854..dc71bfa 100644 --- a/binding.cc +++ b/binding.cc @@ -449,9 +449,13 @@ class Connection : public EventEmitter { void EmitResult (PGresult *result) { + HandleScope scope; + Local tuples; Local exception; + Local args[2]; + switch (PQresultStatus(result)) { case PGRES_EMPTY_QUERY: case PGRES_COMMAND_OK: @@ -459,8 +463,9 @@ class Connection : public EventEmitter { break; case PGRES_TUPLES_OK: - tuples = BuildTuples(result); - Emit(result_symbol, 1, &tuples); + args[0] = Local::New(Null()); + args[1] = BuildTuples(result); + Emit(result_symbol, 2, args); break; case PGRES_COPY_OUT: diff --git a/postgres.js b/postgres.js index 2daf01e..3e52648 100644 --- a/postgres.js +++ b/postgres.js @@ -28,11 +28,13 @@ exports.createConnection = function (conninfo) { c.maybeDispatchQuery(); }); - c.addListener("result", function (arg) { + c.addListener("result", function () { process.assert(c.currentQuery); var callback = c.currentQuery[1]; c.currentQuery = null; - if (callback) callback(arg[0]); + if (callback) { + callback.apply(c, arguments); + } }); c.addListener("ready", function () { diff --git a/test.js b/test.js index 5dcd5e8..d679f45 100644 --- a/test.js +++ b/test.js @@ -1,5 +1,7 @@ -process.mixin(GLOBAL, require("sys")); -var postgres = require("./postgres"); +var postgres = require('./postgres'), + sys = require('sys'), + p = sys.p, + puts = sys.puts; var c = postgres.createConnection("host=localhost dbname=ryan"); @@ -8,28 +10,26 @@ c.addListener("connect", function () { puts(c.readyState); }); -c.addListener("close", function (e) { +c.addListener("close", function (err) { puts("connection closed."); - if (e) { - puts("error: " + e.message); - } + if (err) puts("error: " + err.message); }); -c.query("select * from test;", function (rows) { +c.query("select * from test;", function (err, rows) { puts("result1:"); p(rows); }); -c.query("select * from test limit 1;", function (rows) { +c.query("select * from test limit 1;", function (err, rows) { puts("result2:"); p(rows); }); -c.query("select ____ from test limit 1;", function (rows) { - if (false /* error */ ) { - puts("error! "+ e.message); - puts("full: "+ e.full); - puts("severity: "+ e.severity); +c.query("select ____ from test limit 1;", function (err, rows) { + if (err) { + puts("error! "+ err.message); + puts("full: "+ err.full); + puts("severity: "+ err.severity); c.close(); return; } @@ -37,8 +37,8 @@ c.query("select ____ from test limit 1;", function (rows) { p(rows); }); -c.query("select * from test;", function () { - c.query("select * from test;", function (row) { +c.query("select * from test;", function (err, rows) { + c.query("select * from test;", function (err, rows) { puts("result4:"); p(rows); }); From 1f9482723be93e0dcb5b0c98bdca213a854f6e5e Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Fri, 5 Mar 2010 14:16:37 -0800 Subject: [PATCH 06/16] Add more info to the readme --- README | 29 +++++++++++++++++++++++++++++ test.js | 2 +- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/README b/README index 5717756..d682bef 100644 --- a/README +++ b/README @@ -4,3 +4,32 @@ or better) and do node-waf configure build +h2. Getting Started + + +You'll need postgres to run this. Here's the easy way to do this locally: + + wget http://wwwmaster.postgresql.org/redir/198/h/source/v8.4.2/postgresql-8.4.2.tar.gz + tar -zxf postgresql-8.4.2.tar.gz + cd postgresql-8.4.2 + ./configure --prefix=$HOME/local/postgres-8.4.2 && make && make install + cd $HOME/local/postgres-8.4.2 + bin/initdb ./data + bin/postgres -D ./data + +You'll probably want to add $HOME/local/postgres-8.4.2/bin to your PATH +environment variable. + + export PATH=$HOME/local/postgres-8.4.2/bin:$PATH + +And puts few things into the database + + createdb test + echo "CREATE TABLE test (a int, b int); INSERT INTO test VALUES (1, 2);" | psql -d test + + cd ~/src/node_postgres + node-waf configure + node-waf build + node test.js + + diff --git a/test.js b/test.js index d679f45..4b50347 100644 --- a/test.js +++ b/test.js @@ -3,7 +3,7 @@ var postgres = require('./postgres'), p = sys.p, puts = sys.puts; -var c = postgres.createConnection("host=localhost dbname=ryan"); +var c = postgres.createConnection("host=localhost dbname=test"); c.addListener("connect", function () { puts("connected"); From a03bead90a74b00ecf7e058610afbcee86ce9415 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Fri, 5 Mar 2010 14:21:55 -0800 Subject: [PATCH 07/16] Add error checks to the test --- test.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test.js b/test.js index 4b50347..529acd5 100644 --- a/test.js +++ b/test.js @@ -16,11 +16,13 @@ c.addListener("close", function (err) { }); c.query("select * from test;", function (err, rows) { + if (err) throw err; puts("result1:"); p(rows); }); c.query("select * from test limit 1;", function (err, rows) { + if (err) throw err; puts("result2:"); p(rows); }); From cd3bfce98859093d89c4819baab02398af9a8714 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Lal?= Date: Sat, 6 Mar 2010 08:44:35 -0800 Subject: [PATCH 08/16] Fix for EV_MULTIPLICITY=1 --- binding.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/binding.cc b/binding.cc index dc71bfa..41e32cc 100644 --- a/binding.cc +++ b/binding.cc @@ -108,7 +108,7 @@ class Connection : public EventEmitter { if (r == 0) { return false; } - if (PQflush(connection_) == 1) ev_io_start(&write_watcher_); + if (PQflush(connection_) == 1) ev_io_start(EV_DEFAULT_ &write_watcher_); return true; } @@ -515,7 +515,7 @@ class Connection : public EventEmitter { } if (revents & EV_WRITE) { - if (PQflush(connection_) == 0) ev_io_stop(&write_watcher_); + if (PQflush(connection_) == 0) ev_io_stop(EV_DEFAULT_ &write_watcher_); } } From bd5f8af53d56ebb31a9354c7591e10409fca1963 Mon Sep 17 00:00:00 2001 From: Alexey Churkin Date: Fri, 9 Apr 2010 07:52:36 -0700 Subject: [PATCH 09/16] Escape, mapTupleFields - a native string escaping: conn.escapeString('string') - optional tuple-to-object mapping: conn.mapTupleFields = true causes tuple to look like: { field1: value, field2: value2 } instead of [ value, value2 ] --- binding.cc | 107 +++++++++++++++++++++++++++++++++++++++++++++++++---- test.js | 25 ++++++++++++- 2 files changed, 124 insertions(+), 8 deletions(-) diff --git a/binding.cc b/binding.cc index 41e32cc..40c447d 100644 --- a/binding.cc +++ b/binding.cc @@ -3,6 +3,7 @@ #include #include #include +#include using namespace v8; using namespace node; @@ -11,6 +12,7 @@ static Persistent result_symbol; static Persistent close_symbol; static Persistent connect_symbol; #define READY_STATE_SYMBOL String::NewSymbol("readyState") +#define MAP_TUPLE_ITEMS_SYMBOL String::NewSymbol("mapTupleItems") class Connection : public EventEmitter { public: @@ -33,8 +35,12 @@ class Connection : public EventEmitter { NODE_SET_PROTOTYPE_METHOD(t, "close", Close); NODE_SET_PROTOTYPE_METHOD(t, "reset", Reset); NODE_SET_PROTOTYPE_METHOD(t, "dispatchQuery", DispatchQuery); + NODE_SET_PROTOTYPE_METHOD(t, "escapeString", EscapeString); t->PrototypeTemplate()->SetAccessor(READY_STATE_SYMBOL, ReadyStateGetter); + t->PrototypeTemplate()->SetAccessor(MAP_TUPLE_ITEMS_SYMBOL, + MapTupleItemsGetter, + MapTupleItemsSetter); target->Set(String::NewSymbol("Connection"), t->GetFunction()); } @@ -145,6 +151,46 @@ class Connection : public EventEmitter { return args.This(); } + static Handle + EscapeString (const Arguments& args) + { + Connection *connection = ObjectWrap::Unwrap(args.This()); + + HandleScope scope; + + if (args.Length() == 0 || !args[0]->IsString()) { + return ThrowException(Exception::Error( + String::New("Must give a string to escape"))); + } + + String::Utf8Value unescaped_string(args[0]->ToString()); + + // the string to be escaped is a raw value and + // may contain everything you can imagine + int from_len = unescaped_string.length(); + + char *to; + to = (char *) malloc(from_len * 2 + 1); + + if (connection->connection_) { + int *error; + // TODO handle an error if set + PQescapeStringConn(connection->connection_, + to, + *unescaped_string, + (size_t)from_len, + error); + } else { + PQescapeString(to, *unescaped_string, (size_t)from_len); + } + + Handle result = String::New(to); + + free(to); + + return scope.Close(result); + } + static Handle Connect (const Arguments& args) { @@ -153,7 +199,8 @@ class Connection : public EventEmitter { HandleScope scope; if (args.Length() == 0 || !args[0]->IsString()) { - return ThrowException(String::New("Must give conninfo string as argument")); + return ThrowException(Exception::Error( + String::New("Must give conninfo string as argument"))); } String::Utf8Value conninfo(args[0]->ToString()); @@ -218,6 +265,37 @@ class Connection : public EventEmitter { return Undefined(); } + static Handle + MapTupleItemsGetter (Local property, const AccessorInfo& info) + { + Connection *connection = ObjectWrap::Unwrap(info.This()); + assert(connection); + + assert(property == MAP_TUPLE_ITEMS_SYMBOL); + + HandleScope scope; + + return scope.Close(Boolean::New(connection->mapTupleItems_)); + } + + static void + MapTupleItemsSetter (Local property, + Local value, + const AccessorInfo& info) + { + Connection *connection = ObjectWrap::Unwrap(info.This()); + assert(connection); + + assert(property == MAP_TUPLE_ITEMS_SYMBOL); + + if (!value->IsBoolean()) { + ThrowException(Exception::TypeError( + String::New("mapTupleItems should be of Boolean value"))); + } + + connection->mapTupleItems_ = value->ToBoolean()->Value(); + } + static Handle ReadyStateGetter (Local property, const AccessorInfo& info) { @@ -378,16 +456,30 @@ class Connection : public EventEmitter { int nrows = PQntuples(result); int ncols = PQnfields(result); int row_index, col_index; + char *field_name; Local tuples = Array::New(nrows); - for (row_index = 0; row_index < nrows; row_index++) { - Local row = Array::New(ncols); - tuples->Set(Integer::New(row_index), row); + if (mapTupleItems_) { + for (row_index = 0; row_index < nrows; row_index++) { + Local row = Object::New(); + tuples->Set(Integer::New(row_index), row); - for (col_index = 0; col_index < ncols; col_index++) { - Local cell = BuildCell(result, row_index, col_index); - row->Set(Integer::New(col_index), cell); + for (col_index = 0; col_index < ncols; col_index++) { + field_name = PQfname(result, col_index); + Local cell = BuildCell(result, row_index, col_index); + row->Set(String::New(field_name), cell); + } + } + } else { + for (row_index = 0; row_index < nrows; row_index++) { + Local row = Array::New(nrows); + tuples->Set(Integer::New(row_index), row); + + for (col_index = 0; col_index < ncols; col_index++) { + Local cell = BuildCell(result, row_index, col_index); + row->Set(Integer::New(col_index), cell); + } } } @@ -531,6 +623,7 @@ class Connection : public EventEmitter { PGconn *connection_; bool connecting_; bool resetting_; + bool mapTupleItems_; }; extern "C" void diff --git a/test.js b/test.js index 529acd5..5a56c3c 100644 --- a/test.js +++ b/test.js @@ -3,11 +3,27 @@ var postgres = require('./postgres'), p = sys.p, puts = sys.puts; -var c = postgres.createConnection("host=localhost dbname=test"); +var c = postgres.createConnection("host='' dbname=test"); + +puts(c.escapeString("e's'c'a'p'e me")); + +puts('map tuple items: ' + sys.inspect(c.mapTupleItems)) +c.mapTupleItems = false +puts('map tuple items is turned off and now: ' + sys.inspect(c.mapTupleItems)) + +try { + c.mapTupleItems = "x"; + puts('broken behaviour: mapTupleItems accepted a string, but should reject it') +} +catch (e) { + puts('map tuple items rejects non-boolean values') +} c.addListener("connect", function () { puts("connected"); puts(c.readyState); + + puts(c.escapeString("e's'c'a'p'e UTF8 too: ±—°.")); }); c.addListener("close", function (err) { @@ -21,6 +37,13 @@ c.query("select * from test;", function (err, rows) { p(rows); }); +c.mapTupleItems = true; +c.query("select * from test;", function (err, rows) { + if (err) throw err; + puts("result1.1:"); + p(rows); +}); + c.query("select * from test limit 1;", function (err, rows) { if (err) throw err; puts("result2:"); From fbc40e209d2f53faa720293ab1806fca5c2cc365 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Thu, 15 Apr 2010 11:11:51 -0700 Subject: [PATCH 10/16] Fix segfault --- binding.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/binding.cc b/binding.cc index 40c447d..077fcfe 100644 --- a/binding.cc +++ b/binding.cc @@ -167,7 +167,7 @@ class Connection : public EventEmitter { // the string to be escaped is a raw value and // may contain everything you can imagine - int from_len = unescaped_string.length(); + int from_len = args[0]->ToString()->Utf8Length(); char *to; to = (char *) malloc(from_len * 2 + 1); From 003a83ff676de061c4e73ae25054f02eddcca9e0 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Thu, 13 May 2010 13:00:19 -0700 Subject: [PATCH 11/16] Add package.json Fix package.json --- package.json | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 package.json diff --git a/package.json b/package.json new file mode 100644 index 0000000..3fb443d --- /dev/null +++ b/package.json @@ -0,0 +1,5 @@ +{ "name" : "node_postgres" +, "version" : "0.0.1" +, "description" : "very basic libpg binding to node" +, "author": "Ryan Dahl" +} From e93ec04aaed83c384441364c5d7eb5d718f621ca Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Thu, 13 May 2010 13:01:37 -0700 Subject: [PATCH 12/16] Add license file --- LICENSE-MIT | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 LICENSE-MIT diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000..f30a31d --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,19 @@ +Copyright 2009,2010 Ryan Dahl + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. From f76902489f255ef14a4405570cd1a5daf14f4953 Mon Sep 17 00:00:00 2001 From: Keith Hubbard Date: Fri, 21 May 2010 07:30:18 -0400 Subject: [PATCH 13/16] Add support for async notifications --- binding.cc | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/binding.cc b/binding.cc index 077fcfe..43cfc76 100644 --- a/binding.cc +++ b/binding.cc @@ -11,6 +11,7 @@ static Persistent ready_symbol; static Persistent result_symbol; static Persistent close_symbol; static Persistent connect_symbol; +static Persistent notify_symbol; #define READY_STATE_SYMBOL String::NewSymbol("readyState") #define MAP_TUPLE_ITEMS_SYMBOL String::NewSymbol("mapTupleItems") @@ -30,6 +31,7 @@ class Connection : public EventEmitter { connect_symbol = NODE_PSYMBOL("connect"); result_symbol = NODE_PSYMBOL("result"); ready_symbol = NODE_PSYMBOL("ready"); + notify_symbol = NODE_PSYMBOL("notify"); NODE_SET_PROTOTYPE_METHOD(t, "connect", Connect); NODE_SET_PROTOTYPE_METHOD(t, "close", Close); @@ -576,6 +578,18 @@ class Connection : public EventEmitter { } } + void EmitNotify (PGnotify *notify) + { + HandleScope scope; + + Local args[3]; + + args[0] = Local::New(String::New(notify->relname)); + args[1] = Local::New(Integer::New(notify->be_pid)); + args[2] = Local::New(String::New(notify->extra)); + Emit(notify_symbol, 3, args); + } + void Event (int revents) { if (revents & EV_ERROR) { @@ -603,7 +617,13 @@ class Connection : public EventEmitter { PQclear(result); } Emit(ready_symbol, 0, NULL); - } + } + + PGnotify *notify; + while ((notify = PQnotifies(connection_))) { + EmitNotify(notify); + PQfreemem(notify); + } } if (revents & EV_WRITE) { From cd7e0efe8d024e26ec2617b67a3d533f623cab75 Mon Sep 17 00:00:00 2001 From: Keith Hubbard Date: Mon, 7 Jun 2010 11:33:44 -0400 Subject: [PATCH 14/16] Add test for async notification --- test.js | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/test.js b/test.js index 5a56c3c..7f3d685 100644 --- a/test.js +++ b/test.js @@ -55,8 +55,6 @@ c.query("select ____ from test limit 1;", function (err, rows) { puts("error! "+ err.message); puts("full: "+ err.full); puts("severity: "+ err.severity); - c.close(); - return; } puts("result3:"); p(rows); @@ -68,3 +66,20 @@ c.query("select * from test;", function (err, rows) { p(rows); }); }); + +c.query("listen testnotice;", function () { + var timeout = setTimeout(function () { + puts("timeout waiting for notification"); + c.close(); + }, 2000); + + c.addListener("notify", function (relname, pid, extras) { + if (relname === "testnotice") { + puts("got notification from " + pid + ", extras:" + extras); + clearTimeout(timeout); + c.close(); + } + }); + + c.query("notify testnotice;"); +}); From d0dba3739429d794ff5661f911bbe0927562b366 Mon Sep 17 00:00:00 2001 From: Keith Hubbard Date: Mon, 7 Jun 2010 10:55:13 -0400 Subject: [PATCH 15/16] Fix segfault in EscapeString --- binding.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/binding.cc b/binding.cc index 43cfc76..ea5821d 100644 --- a/binding.cc +++ b/binding.cc @@ -175,13 +175,13 @@ class Connection : public EventEmitter { to = (char *) malloc(from_len * 2 + 1); if (connection->connection_) { - int *error; + int error; // TODO handle an error if set PQescapeStringConn(connection->connection_, to, *unescaped_string, (size_t)from_len, - error); + &error); } else { PQescapeString(to, *unescaped_string, (size_t)from_len); } From 0c5462e1ff9ce2aa35885f6ca3424421fac3c986 Mon Sep 17 00:00:00 2001 From: Keith Hubbard Date: Mon, 7 Jun 2010 11:37:25 -0400 Subject: [PATCH 16/16] Replace use of deprecated sys.p --- test.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test.js b/test.js index 7f3d685..78f0bc4 100644 --- a/test.js +++ b/test.js @@ -1,8 +1,11 @@ var postgres = require('./postgres'), sys = require('sys'), - p = sys.p, puts = sys.puts; +var p = function () { + puts(sys.inspect.apply(this, arguments)); +} + var c = postgres.createConnection("host='' dbname=test"); puts(c.escapeString("e's'c'a'p'e me"));