Permalink
Browse files

Cache lastInsertRowID and affectedRows.

Add some options to preparing a statement. If the lastInsertedID and/or
affectedRows options are specified, those values will be looked up after every
step.
  • Loading branch information...
1 parent 858b70c commit a4c1a7567beeb1fb13df1be6c96360f07ff82663 @orlandov orlandov committed Jul 31, 2010
Showing with 335 additions and 66 deletions.
  1. +61 −21 src/database.cc
  2. +39 −30 src/statement.cc
  3. +16 −15 src/statement.h
  4. +101 −0 tests/test-affected-rows.js
  5. +118 −0 tests/test-last-inserted-id.js
View
@@ -257,39 +257,39 @@ int Database::EIO_AfterPrepareAndStep(eio_req *req) {
// if the prepare failed
if (req->result != SQLITE_OK) {
argv[0] = Exception::Error(
- String::New(sqlite3_errmsg(prep_req->dbo->db_)));
+ String::New(sqlite3_errmsg(prep_req->dbo->db_)));
argc = 1;
}
else {
if (req->int1 == SQLITE_DONE) {
- if (prep_req->mode != EXEC_EMPTY) {
- argv[0] = Local<Value>::New(Undefined()); // no error
+ if (prep_req->mode != EXEC_EMPTY) {
+ argv[0] = Local<Value>::New(Undefined()); // no error
- Local<Object> info = Object::New();
+ Local<Object> info = Object::New();
- if (prep_req->mode & EXEC_LAST_INSERT_ID) {
- info->Set(String::NewSymbol("last_inserted_id"),
- Integer::NewFromUnsigned (prep_req->lastInsertId));
- }
- if (prep_req->mode & EXEC_AFFECTED_ROWS) {
- info->Set(String::NewSymbol("affected_rows"),
- Integer::New (prep_req->affectedRows));
- }
- argv[1] = info;
- argc = 2;
+ if (prep_req->mode & EXEC_LAST_INSERT_ID) {
+ info->Set(String::NewSymbol("last_inserted_id"),
+ Integer::NewFromUnsigned (prep_req->lastInsertId));
+ }
+ if (prep_req->mode & EXEC_AFFECTED_ROWS) {
+ info->Set(String::NewSymbol("affected_rows"),
+ Integer::New (prep_req->affectedRows));
+ }
+ argv[1] = info;
+ argc = 2;
} else {
- argc = 0;
+ argc = 0;
}
}
else {
argv[0] = External::New(prep_req->stmt);
argv[1] = Integer::New(req->int1);
Persistent<Object> statement(
- Statement::constructor_template->GetFunction()->NewInstance(2, argv));
+ Statement::constructor_template->GetFunction()->NewInstance(2, argv));
if (prep_req->tail) {
statement->Set(String::New("tail"), String::New(prep_req->tail));
@@ -359,6 +359,7 @@ int Database::EIO_PrepareAndStep(eio_req *req) {
Handle<Value> Database::PrepareAndStep(const Arguments& args) {
HandleScope scope;
+
REQ_STR_ARG(0, sql);
REQ_FUN_ARG(1, cb);
OPT_INT_ARG(2, mode, EXEC_EMPTY);
@@ -392,7 +393,7 @@ int Database::EIO_AfterPrepare(eio_req *req) {
struct prepare_request *prep_req = (struct prepare_request *)(req->data);
HandleScope scope;
- Local<Value> argv[2];
+ Local<Value> argv[3];
int argc = 0;
// if the prepare failed
@@ -404,16 +405,17 @@ int Database::EIO_AfterPrepare(eio_req *req) {
else {
argv[0] = External::New(prep_req->stmt);
argv[1] = Integer::New(-1);
+ argv[2] = Integer::New(prep_req->mode);
Persistent<Object> statement(
- Statement::constructor_template->GetFunction()->NewInstance(2, argv));
+ Statement::constructor_template->GetFunction()->NewInstance(3, argv));
if (prep_req->tail) {
statement->Set(String::New("tail"), String::New(prep_req->tail));
}
+ argc = 2;
argv[0] = Local<Value>::New(Undefined());
argv[1] = Local<Value>::New(statement);
- argc = 2;
}
TryCatch try_catch;
@@ -454,11 +456,49 @@ int Database::EIO_Prepare(eio_req *req) {
return 0;
}
+// Statement#prepare(sql, [ options ,] callback);
Handle<Value> Database::Prepare(const Arguments& args) {
HandleScope scope;
+ Local<Object> options;
+ Local<Function> cb;
+ int mode;
+
REQ_STR_ARG(0, sql);
- REQ_FUN_ARG(1, cb);
- OPT_INT_ARG(2, mode, EXEC_EMPTY);
+
+ // middle argument could be options or
+ switch (args.Length()) {
+ case 2:
+ if (!args[1]->IsFunction()) {
+ return ThrowException(Exception::TypeError(
+ String::New("Argument 1 must be a function")));
+ }
+ cb = Local<Function>::Cast(args[1]);
+ options = Object::New();
+ break;
+
+ case 3:
+ if (!args[1]->IsObject()) {
+ return ThrowException(Exception::TypeError(
+ String::New("Argument 1 must be an object")));
+ }
+ options = Local<Function>::Cast(args[1]);
+
+ if (!args[2]->IsFunction()) {
+ return ThrowException(Exception::TypeError(
+ String::New("Argument 2 must be a function")));
+ }
+ cb = Local<Function>::Cast(args[2]);
+ break;
+ }
+
+ mode = EXEC_EMPTY;
+
+ if (options->Get(String::New("lastInsertRowID"))->IsTrue()) {
+ mode |= EXEC_LAST_INSERT_ID;
+ }
+ if (options->Get(String::New("affectedRows"))->IsTrue()) {
+ mode |= EXEC_AFFECTED_ROWS;
+ }
Database* dbo = ObjectWrap::Unwrap<Database>(args.This());
View
@@ -16,6 +16,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include <string.h>
+#include "database.h"
#include "statement.h"
#include "sqlite3_bindings.h"
@@ -48,8 +49,9 @@ Handle<Value> Statement::New(const Arguments& args) {
HandleScope scope;
REQ_EXT_ARG(0, stmt);
int first_rc = args[1]->IntegerValue();
+ int mode = args[2]->IntegerValue();
- Statement *sto = new Statement((sqlite3_stmt*)stmt->Value(), first_rc);
+ Statement *sto = new Statement((sqlite3_stmt*)stmt->Value(), first_rc, mode);
sto->Wrap(args.This());
sto->Ref();
@@ -107,12 +109,12 @@ int Statement::EIO_BindArray(eio_req *req) {
case KEY_STRING:
index = sqlite3_bind_parameter_index(sto->stmt_,
- (char*)(pair->key));
+ (char*)(pair->key));
break;
- default: {
- // this SHOULD be unreachable
- }
+ default: {
+ // this SHOULD be unreachable
+ }
}
if (!index) {
@@ -130,15 +132,13 @@ int Statement::EIO_BindArray(eio_req *req) {
break;
case VALUE_STRING:
rc = sqlite3_bind_text(sto->stmt_, index, (char*)(pair->value),
- pair->value_size, SQLITE_TRANSIENT);
+ pair->value_size, SQLITE_TRANSIENT);
break;
case VALUE_NULL:
rc = sqlite3_bind_null(sto->stmt_, index);
break;
- default: {
- // should be unreachable
- }
+ // should be unreachable
}
}
@@ -159,16 +159,16 @@ Handle<Value> Statement::BindObject(const Arguments& args) {
if (! args[0]->IsObject())
return ThrowException(Exception::TypeError(
- String::New("First argument must be an Array.")));
+ String::New("First argument must be an Array.")));
Local<Object> obj = args[0]->ToObject();
Local<Array> properties = obj->GetPropertyNames();
struct bind_request *bind_req = (struct bind_request *)
- calloc(1, sizeof(struct bind_request));
+ calloc(1, sizeof(struct bind_request));
int len = bind_req->len = properties->Length();
bind_req->pairs = (struct bind_pair *)
- calloc(len, sizeof(struct bind_pair));
+ calloc(len, sizeof(struct bind_pair));
struct bind_pair *pairs = bind_req->pairs;
@@ -177,7 +177,6 @@ Handle<Value> Statement::BindObject(const Arguments& args) {
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;
@@ -213,7 +212,7 @@ Handle<Value> Statement::BindObject(const Arguments& args) {
else {
free(pairs->key);
return ThrowException(Exception::TypeError(
- String::New("Unable to bind value of this type")));
+ String::New("Unable to bind value of this type")));
}
}
@@ -235,15 +234,15 @@ Handle<Value> Statement::BindArray(const Arguments& args) {
REQ_FUN_ARG(1, cb);
if (! args[0]->IsArray())
return ThrowException(Exception::TypeError(
- String::New("First argument must be an Array.")));
+ String::New("First argument must be an Array.")));
struct bind_request *bind_req = (struct bind_request *)
- calloc(1, sizeof(struct bind_request));
+ calloc(1, sizeof(struct bind_request));
Local<Array> array = Local<Array>::Cast(args[0]);
int len = bind_req->len = array->Length();
bind_req->pairs = (struct bind_pair *)
- calloc(len, sizeof(struct bind_pair));
+ calloc(len, sizeof(struct bind_pair));
struct bind_pair *pairs = bind_req->pairs;
@@ -261,7 +260,6 @@ Handle<Value> Statement::BindArray(const Arguments& args) {
// setup value
if (val->IsInt32()) {
- printf("Binding int\n");
pairs->value_type = VALUE_INT;
int *value = (int *) malloc(sizeof(int));
*value = val->Int32Value();
@@ -288,7 +286,7 @@ Handle<Value> Statement::BindArray(const Arguments& args) {
else {
free(pairs->key);
return ThrowException(Exception::TypeError(
- String::New("Unable to bind value of this type")));
+ String::New("Unable to bind value of this type")));
}
}
@@ -328,24 +326,24 @@ Handle<Value> Statement::Bind(const Arguments& args) {
|| args[0]->IsArray()
|| args[0]->IsObject()))
return ThrowException(Exception::TypeError(
- String::New("First argument must be a string, number, array or object.")));
+ String::New("First argument must be a string, number, array or object.")));
struct bind_request *bind_req = (struct bind_request *)
- calloc(1, sizeof(struct bind_request));
+ calloc(1, sizeof(struct bind_request));
bind_req->len = 1;
struct bind_pair *pair = bind_req->pairs = (struct bind_pair *)
- calloc(1, sizeof(struct bind_pair));
+ calloc(1, sizeof(struct bind_pair));
// setup key
if (args[0]->IsString()) {
- String::Utf8Value keyValue(args[0]);
- pair->key_type = KEY_STRING;
+ String::Utf8Value keyValue(args[0]);
+ pair->key_type = KEY_STRING;
- char *key = (char *) calloc(1, keyValue.length() + 1);
- strcpy(key, *keyValue);
+ char *key = (char *) calloc(1, keyValue.length() + 1);
+ strcpy(key, *keyValue);
- pair->key = key;
+ pair->key = key;
}
else if (args[0]->IsInt32()) {
pair->key_type = KEY_INT;
@@ -384,7 +382,7 @@ Handle<Value> Statement::Bind(const Arguments& args) {
else {
free(pair->key);
return ThrowException(Exception::TypeError(
- String::New("Unable to bind value of this type")));
+ String::New("Unable to bind value of this type")));
}
bind_req->cb = Persistent<Function>::New(cb);
@@ -434,7 +432,7 @@ Handle<Value> Statement::Finalize(const Arguments& args) {
Statement* sto = ObjectWrap::Unwrap<Statement>(args.This());
if (sto->HasCallback()) {
- return ThrowException(Exception::Error(String::New("Already stepping")));
+ return ThrowException(Exception::Error(String::New("Already stepping")));
}
REQ_FUN_ARG(0, cb);
@@ -470,11 +468,12 @@ int Statement::EIO_AfterStep(eio_req *req) {
Statement *sto = (class Statement *)(req->data);
+ sqlite3* db = sqlite3_db_handle(sto->stmt_);
Local<Value> argv[2];
if (sto->error_) {
argv[0] = Exception::Error(
- String::New(sqlite3_errmsg(sqlite3_db_handle(sto->stmt_))));
+ String::New(sqlite3_errmsg(db)));
}
else {
argv[0] = Local<Value>::New(Undefined());
@@ -523,6 +522,16 @@ int Statement::EIO_AfterStep(eio_req *req) {
argv[1] = row;
}
+ if (sto->mode_ & EXEC_LAST_INSERT_ID) {
+ sto->handle_->Set(String::New("lastInsertRowID"),
+ Integer::New(sqlite3_last_insert_rowid(db)));
+ }
+
+ if (sto->mode_ & EXEC_AFFECTED_ROWS) {
+ sto->handle_->Set(String::New("affectedRows"),
+ Integer::New(sqlite3_changes(db)));
+ }
+
TryCatch try_catch;
Local<Function> cb = sto->GetCallback();
Oops, something went wrong.

0 comments on commit a4c1a75

Please sign in to comment.