Skip to content
Browse files

Add statement#bindObject to bind placeholders using an object.

  • Loading branch information...
1 parent 8df79ce commit 9fae3abdbe7140f6fe2f33e7f44efcec9dc1cbb2 @orlandov orlandov committed
Showing with 141 additions and 124 deletions.
  1. +6 −5 README.md
  2. +99 −114 src/statement.cc
  3. +3 −5 src/statement.h
  4. +33 −0 tests/test_bind.js
View
11 README.md
@@ -60,7 +60,7 @@ from the API have been made to improve performance.
db.open("lilponies.db", function () {
var colour = 'pink';
- var sql = 'SELECT name FROM ponies WHERE hair_colour = ? and gemstones = ?';
+ var sql = 'SELECT name FROM ponies WHERE hair_colour = $hair_colour and gemstones = ?';
var ponies = [];
@@ -74,13 +74,14 @@ from the API have been made to improve performance.
db.prepare(sql, function (error, statement) {
if (error) throw error;
- // fill in the placeholders
+ // Fill in the placeholders
// Could also have used:
- // statement.bind($index, $value, function () { ... });
- statement.bindArray('pink', 4], function () {
+ // statement.bind(position, value, function () { ... });
+ // statement.bindObject({ $hair_colour: 'pink' }, function () {});
+ statement.bindArray(['pink', 4], function () {
// call step once per row result
- statement.step(function (row) {
+ statement.step(function (error, row) {
if (!row) {
// end of rows
}
View
213 src/statement.cc
@@ -33,7 +33,9 @@ void Statement::Init(v8::Handle<Object> target) {
constructor_template->SetClassName(String::NewSymbol("Statement"));
NODE_SET_PROTOTYPE_METHOD(t, "bind", Bind);
+ NODE_SET_PROTOTYPE_METHOD(t, "bindObject", BindObject);
NODE_SET_PROTOTYPE_METHOD(t, "bindArray", BindArray);
+
NODE_SET_PROTOTYPE_METHOD(t, "finalize", Finalize);
NODE_SET_PROTOTYPE_METHOD(t, "reset", Reset);
NODE_SET_PROTOTYPE_METHOD(t, "clearBindings", ClearBindings);
@@ -86,42 +88,6 @@ int Statement::EIO_AfterBindArray(eio_req *req) {
return 0;
}
-int Statement::EIO_AfterBind(eio_req *req) {
- ev_unref(EV_DEFAULT_UC);
-
- HandleScope scope;
- struct bind_request *bind_req = (struct bind_request *)(req->data);
-
- Local<Value> argv[1];
- bool err = false;
- if (req->result) {
- err = true;
- argv[0] = Exception::Error(String::New("Error binding parameter"));
- }
-
- TryCatch try_catch;
-
- bind_req->cb->Call(Context::GetCurrent()->Global(), err ? 1 : 0, argv);
-
- if (try_catch.HasCaught()) {
- FatalException(try_catch);
- }
-
- bind_req->cb.Dispose();
-
- struct bind_pair *pair = bind_req->pairs;
-
- for (size_t i = 0; i < bind_req->len; i++, pair++) {
- free(pair->key);
- free(pair->value);
- }
-
- free(bind_req->pairs);
- free(bind_req);
-
- return 0;
-}
-
int Statement::EIO_BindArray(eio_req *req) {
struct bind_request *bind_req = (struct bind_request *)(req->data);
Statement *sto = bind_req->sto;
@@ -181,75 +147,82 @@ int Statement::EIO_BindArray(eio_req *req) {
return 0;
}
-int Statement::EIO_Bind(eio_req *req) {
- struct bind_request *bind_req = (struct bind_request *)(req->data);
- Statement *sto = bind_req->sto;
+Handle<Value> Statement::BindObject(const Arguments& args) {
+ HandleScope scope;
+ Statement* sto = ObjectWrap::Unwrap<Statement>(args.This());
- int index(0);
- switch(bind_req->pairs->key_type) {
- case KEY_INT:
- index = *(int*)(bind_req->pairs->key);
- break;
+ REQ_ARGS(2);
+ REQ_FUN_ARG(1, cb);
- case KEY_STRING:
- index = sqlite3_bind_parameter_index(sto->stmt_,
- (char*)(bind_req->pairs->key));
- break;
+ if (! args[0]->IsObject())
+ return ThrowException(Exception::TypeError(
+ String::New("First argument must be an Array.")));
+ Local<Object> obj = args[0]->ToObject();
+ Local<Array> properties = obj->GetPropertyNames();
- default: {
- // this SHOULD be unreachable
- }
- }
+ struct bind_request *bind_req = (struct bind_request *)
+ calloc(1, sizeof(struct bind_request));
- if (!index) {
- req->result = SQLITE_MISMATCH;
- return 0;
- }
+ int len = bind_req->len = properties->Length();
+ bind_req->pairs = (struct bind_pair *)
+ calloc(len, sizeof(struct bind_pair));
- int rc = 0;
- switch(bind_req->pairs->value_type) {
- case VALUE_INT:
- rc = sqlite3_bind_int(sto->stmt_, index, *(int*)(bind_req->pairs->value));
- break;
- case VALUE_DOUBLE:
- rc = sqlite3_bind_double(sto->stmt_, index, *(double*)(bind_req->pairs->value));
- break;
- case VALUE_STRING:
- rc = sqlite3_bind_text(sto->stmt_, index, (char*)(bind_req->pairs->value),
- bind_req->pairs->value_size, SQLITE_TRANSIENT);
- break;
- case VALUE_NULL:
- rc = sqlite3_bind_null(sto->stmt_, index);
- break;
-
- default: {
- // should be unreachable
- }
- }
+ struct bind_pair *pairs = bind_req->pairs;
- if (rc) {
- req->result = rc;
- return 0;
+ for (uint32_t i = 0; i < properties->Length(); i++, pairs++) {
+ Local<Value> name = properties->Get(Integer::New(i));
+ Local<Value> val = obj->Get(name->ToString());
+
+ String::Utf8Value keyValue(name);
+ printf("prop %d is %s\n", i, *keyValue);
+
+ // setting key type
+ pairs->key_type = KEY_STRING;
+ char *key = (char *) calloc(1, keyValue.length() + 1);
+ strcpy(key, *keyValue);
+ pairs->key = key;
+
+ // setup value
+ if (val->IsInt32()) {
+ pairs->value_type = VALUE_INT;
+ int *value = (int *) malloc(sizeof(int));
+ *value = val->Int32Value();
+ pairs->value = value;
+ }
+ else if (val->IsNumber()) {
+ pairs->value_type = VALUE_DOUBLE;
+ double *value = (double *) malloc(sizeof(double));
+ *value = val->NumberValue();
+ pairs->value = value;
+ }
+ else if (val->IsString()) {
+ pairs->value_type = VALUE_STRING;
+ String::Utf8Value text(val);
+ char *value = (char *) calloc(text.length()+1, sizeof(char*));
+ strcpy(value, *text);
+ pairs->value = value;
+ pairs->value_size = text.length()+1;
+ }
+ else if (val->IsNull() || val->IsUndefined()) {
+ pairs->value_type = VALUE_NULL;
+ pairs->value = NULL;
+ }
+ else {
+ free(pairs->key);
+ return ThrowException(Exception::TypeError(
+ String::New("Unable to bind value of this type")));
+ }
}
- return 0;
-}
+ bind_req->cb = Persistent<Function>::New(cb);
+ bind_req->sto = sto;
+ eio_custom(EIO_BindArray, EIO_PRI_DEFAULT, EIO_AfterBindArray, bind_req);
-// db.prepare "SELECT $x, ?" -> statement
-//
-// Bind multiple placeholders by array
-// statement.bind([ value0, value1 ], callback);
-//
-// Bind multiple placeholdders by name
-// statement.bind({ $x: value }, callback);
-//
-// Bind single placeholder by name:
-// statement.bind('$x', value, callback);
-//
-// Bind placeholder by index:
-// statement.bind(1, value, callback);
+ ev_ref(EV_DEFAULT_UC);
+ return Undefined();
+};
Handle<Value> Statement::BindArray(const Arguments& args) {
HandleScope scope;
@@ -275,12 +248,11 @@ Handle<Value> Statement::BindArray(const Arguments& args) {
for (int i = 0; i < len; i++, pairs++) {
Local<Value> val = array->Get(i);
-
// setting key type
pairs->key_type = KEY_INT;
int *index = (int *) malloc(sizeof(int));
*index = i+1;
- pairs->value_size = sizeof(int);
+// pairs->value_size = sizeof(int);
// don't forget to `free` this
pairs->key = index;
@@ -327,6 +299,20 @@ Handle<Value> Statement::BindArray(const Arguments& args) {
return Undefined();
}
+// db.prepare "SELECT $x, ?" -> statement
+//
+// Bind multiple placeholders by array
+// statement.bind([ value0, value1 ], callback);
+//
+// Bind multiple placeholdders by name
+// statement.bind({ $x: value }, callback);
+//
+// Bind single placeholder by name:
+// statement.bind('$x', value, callback);
+//
+// Bind placeholder by index:
+// statement.bind(1, value, callback);
+
Handle<Value> Statement::Bind(const Arguments& args) {
HandleScope scope;
Statement* sto = ObjectWrap::Unwrap<Statement>(args.This());
@@ -344,57 +330,56 @@ Handle<Value> Statement::Bind(const Arguments& args) {
struct bind_request *bind_req = (struct bind_request *)
calloc(1, sizeof(struct bind_request));
- bind_req->pairs = (struct bind_pair *)
+ bind_req->len = 1;
+ struct bind_pair *pair = bind_req->pairs = (struct bind_pair *)
calloc(1, sizeof(struct bind_pair));
// setup key
if (args[0]->IsString()) {
String::Utf8Value keyValue(args[0]);
- bind_req->pairs->key_type = KEY_STRING;
+ pair->key_type = KEY_STRING;
char *key = (char *) calloc(1, keyValue.length() + 1);
- bind_req->pairs->value_size = keyValue.length() + 1;
strcpy(key, *keyValue);
- bind_req->pairs->key = key;
+ pair->key = key;
}
else if (args[0]->IsInt32()) {
- bind_req->pairs->key_type = KEY_INT;
+ pair->key_type = KEY_INT;
int *index = (int *) malloc(sizeof(int));
*index = args[0]->Int32Value();
- bind_req->pairs->value_size = sizeof(int);
// don't forget to `free` this
- bind_req->pairs->key = index;
+ pair->key = index;
}
// setup value
if (args[1]->IsInt32()) {
- bind_req->pairs->value_type = VALUE_INT;
+ pair->value_type = VALUE_INT;
int *value = (int *) malloc(sizeof(int));
*value = args[1]->Int32Value();
- bind_req->pairs->value = value;
+ pair->value = value;
}
else if (args[1]->IsNumber()) {
- bind_req->pairs->value_type = VALUE_DOUBLE;
+ pair->value_type = VALUE_DOUBLE;
double *value = (double *) malloc(sizeof(double));
*value = args[1]->NumberValue();
- bind_req->pairs->value = value;
+ pair->value = value;
}
else if (args[1]->IsString()) {
- bind_req->pairs->value_type = VALUE_STRING;
+ pair->value_type = VALUE_STRING;
String::Utf8Value text(args[1]);
char *value = (char *) calloc(text.length()+1, sizeof(char*));
strcpy(value, *text);
- bind_req->pairs->value = value;
- bind_req->pairs->value_size = text.length()+1;
+ pair->value = value;
+ pair->value_size = text.length()+1;
}
else if (args[1]->IsNull() || args[1]->IsUndefined()) {
- bind_req->pairs->value_type = VALUE_NULL;
- bind_req->pairs->value = NULL;
+ pair->value_type = VALUE_NULL;
+ pair->value = NULL;
}
else {
- free(bind_req->pairs->key);
+ free(pair->key);
return ThrowException(Exception::TypeError(
String::New("Unable to bind value of this type")));
}
@@ -402,7 +387,7 @@ Handle<Value> Statement::Bind(const Arguments& args) {
bind_req->cb = Persistent<Function>::New(cb);
bind_req->sto = sto;
- eio_custom(EIO_Bind, EIO_PRI_DEFAULT, EIO_AfterBind, bind_req);
+ eio_custom(EIO_BindArray, EIO_PRI_DEFAULT, EIO_AfterBindArray, bind_req);
ev_ref(EV_DEFAULT_UC);
View
8 src/statement.h
@@ -50,13 +50,11 @@ class Statement : public EventEmitter {
if (column_data_) FreeColumnData();
}
- static int EIO_AfterBind(eio_req *req);
- static int EIO_Bind(eio_req *req);
static Handle<Value> Bind(const Arguments& args);
-
- static int EIO_AfterBindArray(eio_req *req);
- static int EIO_BindArray(eio_req *req);
+ static Handle<Value> BindObject(const Arguments& args);
static Handle<Value> BindArray(const Arguments& args);
+ static int EIO_BindArray(eio_req *req);
+ static int EIO_AfterBindArray(eio_req *req);
static int EIO_AfterFinalize(eio_req *req);
static int EIO_Finalize(eio_req *req);
View
33 tests/test_bind.js
@@ -25,6 +25,7 @@ function test_by_index(callback) {
statement.step(function (error, row) {
if (error) throw error;
assert.deepEqual(row, { foo: 'hi', bar: 'there', baz: 'world' });
+ puts("Test ok");
});
});
});
@@ -45,6 +46,7 @@ function test_num_array(callback) {
statement.step(function (error, row) {
if (error) throw error;
assert.deepEqual(row, { foo: 1, bar: 2, baz: 3 });
+ puts("Test ok");
});
});
});
@@ -63,12 +65,42 @@ function test_by_array(callback) {
if (error) throw error;
puts("bound ok");
statement.step(function (error, row) {
+ if (error) throw error;
assert.deepEqual(row, { foo: 'hi', bar: 'there', baz: 'world' });
statement.reset();
statement.bindArray([1, 2, null], function (error) {
statement.step(function (error, row) {
if (error) throw error;
assert.deepEqual(row, { foo: 1, bar: 2, baz: null });
+ puts("Test ok");
+ });
+ });
+ });
+ });
+ });
+ });
+}
+
+function test_by_object(callback) {
+ db = new sqlite.Database();
+ db.open(":memory:", function () {
+ puts("Opened ok");
+ db.prepare("SELECT $x AS foo, $y AS bar, $z AS baz"
+ , function (error, statement) {
+ if (error) throw error;
+ puts("Prepared ok");
+ statement.bindObject({ $x: 'hi', $y: null, $z: 'world' }, function (error) {
+ if (error) throw error;
+ puts("bound ok");
+ statement.step(function (error, row) {
+ if (error) throw error;
+ assert.deepEqual(row, { foo: 'hi', bar: null, baz: 'world' });
+ statement.reset();
+ statement.bindArray([1, 2, null], function (error) {
+ statement.step(function (error, row) {
+ if (error) throw error;
+ assert.deepEqual(row, { foo: 1, bar: 2, baz: null });
+ puts("Test ok");
});
});
});
@@ -80,3 +112,4 @@ function test_by_array(callback) {
test_num_array();
test_by_index();
test_by_array();
+test_by_object();

0 comments on commit 9fae3ab

Please sign in to comment.
Something went wrong with that request. Please try again.