Commit fa981cc9 by Konstantin Käfer

return values from queries

parent 3971b4b5
var sqlite3 = require('./lib/sqlite3');
var db = new sqlite3.Database(':memory:');
db.prepare("CREATE TABLE foo (text bar)")
.run(function(err) {
console.log('x');
})
.finalize()
.close();
db.prepare("sdf", function() {});
db.prepare("CREATE TABLE foo (text bar)").run().finalize(function() {
db.prepare("INSERT INTO foo VALUES('bar baz boo')").run().finalize(function() {
db.prepare("SELECT * FROM foo").run(function(err, row) {
console.log(row);
}).run(function(err, row) {
console.log(row);
});
});
});
......@@ -203,7 +203,7 @@ Handle<Value> Statement::Run(const Arguments& args) {
OPTIONAL_ARGUMENT_FUNCTION(0, callback);
Baton* baton = new Baton(stmt, callback);
RowBaton* baton = new RowBaton(stmt, callback);
stmt->Schedule(EIO_BeginRun, baton);
return args.This();
......@@ -217,13 +217,42 @@ void Statement::EIO_BeginRun(Baton* baton) {
eio_custom(EIO_Run, EIO_PRI_DEFAULT, EIO_AfterRun, baton);
}
void Statement::GetRow(Result::Row* row, sqlite3_stmt* stmt) {
int rows = sqlite3_column_count(stmt);
for (int i = 0; i < rows; i++) {
int type = sqlite3_column_type(stmt, i);
switch (type) {
case SQLITE_INTEGER: {
row->push_back(new Result::Integer(sqlite3_column_int(stmt, i)));
} break;
case SQLITE_FLOAT: {
row->push_back(new Result::Float(sqlite3_column_double(stmt, i)));
} break;
case SQLITE_TEXT: {
const char* text = (const char*)sqlite3_column_text(stmt, i);
int length = sqlite3_column_bytes(stmt, i);
row->push_back(new Result::Text(length, text));
} break;
case SQLITE_BLOB: {
const void* blob = sqlite3_column_blob(stmt, i);
int length = sqlite3_column_bytes(stmt, i);
row->push_back(new Result::Blob(length, blob));
} break;
case SQLITE_NULL: {
row->push_back(new Result::Null());
} break;
default:
assert(false);
}
}
}
int Statement::EIO_Run(eio_req *req) {
Baton* baton = static_cast<Baton*>(req->data);
RowBaton* baton = static_cast<RowBaton*>(req->data);
Statement* stmt = baton->stmt;
Database* db = stmt->db;
fprintf(stderr, "calling run\n");
// In case preparing fails, we use a mutex to make sure we get the associated
// error message.
sqlite3_mutex* mtx = sqlite3_db_mutex(db->handle);
......@@ -238,27 +267,44 @@ int Statement::EIO_Run(eio_req *req) {
sqlite3_mutex_leave(mtx);
if (baton->status == SQLITE_ROW) {
Locker lock;
HandleScope scope;
Local<Value> argv[] = { Local<Value>::New(Null()) };
for (int i = 0; i < 10; i++) {
if (!baton->callback.IsEmpty() && baton->callback->IsFunction()) {
TryCatch try_catch;
baton->callback->Call(baton->stmt->handle_, 1, argv);
if (try_catch.HasCaught()) {
FatalException(try_catch);
}
// Acquire one result row before returning.
GetRow(&baton->row, stmt->handle);
}
return 0;
}
Local<Array> Statement::RowToJS(Result::Row* row) {
Local<Array> result(Array::New(row->size()));
Result::Row::iterator it = row->begin();
for (int i = 0; it < row->end(); it++, i++) {
Result::Field* field = *it;
switch (field->type) {
case SQLITE_INTEGER: {
result->Set(i, Local<Integer>(Integer::New(((Result::Integer*)field)->value)));
} break;
case SQLITE_FLOAT: {
result->Set(i, Local<Number>(Number::New(((Result::Float*)field)->value)));
} break;
case SQLITE_TEXT: {
result->Set(i, Local<String>(String::New(((Result::Text*)field)->value.c_str(), ((Result::Text*)field)->value.size())));
} break;
// case SQLITE_BLOB: {
// result->Set(i, Local<String>(String::New(((Result::Text*)field)->value.c_str())));
// } break;
case SQLITE_NULL: {
result->Set(i, Local<Value>::New(Null()));
} break;
}
}
return 0;
return result;
}
int Statement::EIO_AfterRun(eio_req *req) {
HandleScope scope;
Baton* baton = static_cast<Baton*>(req->data);
RowBaton* baton = static_cast<RowBaton*>(req->data);
Statement* stmt = baton->stmt;
if (baton->status != SQLITE_ROW && baton->status != SQLITE_DONE) {
......@@ -267,10 +313,17 @@ int Statement::EIO_AfterRun(eio_req *req) {
else {
// Fire callbacks.
if (!baton->callback.IsEmpty() && baton->callback->IsFunction()) {
if (baton->status == SQLITE_ROW) {
// Create the result array from the data we acquired.
Local<Value> argv[] = { Local<Value>::New(Null()), RowToJS(&baton->row) };
TRY_CATCH_CALL(stmt->handle_, baton->callback, 2, argv);
}
else {
Local<Value> argv[] = { Local<Value>::New(Null()) };
TRY_CATCH_CALL(stmt->handle_, baton->callback, 1, argv);
}
}
}
stmt->locked = false;
stmt->Process();
......
......@@ -21,14 +21,57 @@
#include "database.h"
#include <typeinfo>
#include <string>
#include <queue>
#include <vector>
#include <sqlite3.h>
using namespace v8;
using namespace node;
namespace Result {
struct Field {
Field(unsigned short _type = SQLITE_NULL) : type(_type) {}
unsigned short type;
};
struct Integer : Field {
Integer(int val) : Field(SQLITE_INTEGER), value(val) {}
int value;
};
struct Float : Field {
Float(double val) : Field(SQLITE_FLOAT), value(val) {}
double value;
};
struct Text : Field {
Text(size_t len, const char* val) : Field(SQLITE_TEXT), value(val, len) {}
std::string value;
};
struct Blob : Field {
Blob(size_t len, const void* val) : Field(SQLITE_BLOB), length(len) {
value = malloc(len);
memcpy(value, val, len);
}
~Blob() {
free(value);
}
int length;
void* value;
};
typedef Field Null;
typedef std::vector<Field*> Row;
}
class Statement : public EventEmitter {
public:
static Persistent<FunctionTemplate> constructor_template;
......@@ -54,6 +97,12 @@ public:
}
};
static struct RowBaton : Baton {
RowBaton(Statement* stmt_, Handle<Function> cb_) :
Baton(stmt_, cb_) {}
Result::Row row;
};
static struct PrepareBaton : Database::Baton {
Statement* stmt;
std::string sql;
......@@ -94,6 +143,9 @@ protected:
static int EIO_Run(eio_req *req);
static int EIO_AfterRun(eio_req *req);
static void GetRow(Result::Row* row, sqlite3_stmt* stmt);
static Local<Array> RowToJS(Result::Row* row);
static void EIO_BeginPrepare(Database::Baton* baton);
static int EIO_Prepare(eio_req *req);
static int EIO_AfterPrepare(eio_req *req);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment