diff --git a/src/main.cc b/src/main.cc index ffded85..2ee2961 100644 --- a/src/main.cc +++ b/src/main.cc @@ -9,6 +9,7 @@ using namespace v8; void init(Handle target){ WgDB::Init(target); Record::Init(); + Cursor::Init(); } NODE_MODULE(wgdb, init); diff --git a/src/wgdb.cc b/src/wgdb.cc index 5245f44..a12a9dc 100644 --- a/src/wgdb.cc +++ b/src/wgdb.cc @@ -1,6 +1,7 @@ #include "wgdb.h" Persistent Record::constructor; +Persistent Cursor::constructor; /* * @@ -39,6 +40,12 @@ struct FindData { void* data; }; +struct CursorData { + int arglen; + wg_query_arg* arglist; + wg_query* query; +}; + /* * @@ -248,6 +255,53 @@ void do_find_record(uv_work_t* req){ } +void do_cursor_next(uv_work_t* req){ + Baton* baton = static_cast(req->data); + + Cursor* cursor = static_cast(baton->data); + + wg_int lock = wg_start_read(baton->wgdb->db_ptr); + if(!lock){ + char buffer[1024]; + sprintf(buffer, "wgdb database %s could not acquire read lock", baton->wgdb->db_name); + baton->error = buffer; + return; + } + + RecordData* new_data = new RecordData(); + new_data->record = wg_fetch(cursor->wgdb->db_ptr, cursor->query); + baton->data = new_data; + + if(!wg_end_read(baton->wgdb->db_ptr, lock)){ + char buffer[1024]; + sprintf(buffer, "wgdb database %s could not releae read lock", baton->wgdb->db_name); + baton->error = buffer; + } +} + +void do_query(uv_work_t* req){ + Baton* baton = static_cast(req->data); + + CursorData* data = static_cast(baton->data); + + wg_int lock = wg_start_read(baton->wgdb->db_ptr); + if(!lock){ + char buffer[1024]; + sprintf(buffer, "wgdb database %s could not acquire read lock", baton->wgdb->db_name); + baton->error = buffer; + return; + } + + data->query = wg_make_query(baton->wgdb->db_ptr, NULL, 0, data->arglist, data->arglen); + baton->data = data; + + if(!wg_end_read(baton->wgdb->db_ptr, lock)){ + char buffer[1024]; + sprintf(buffer, "wgdb database %s could not releae read lock", baton->wgdb->db_name); + baton->error = buffer; + } +} + void do_record_next(uv_work_t* req){ Baton* baton = static_cast(req->data); @@ -449,6 +503,8 @@ void WgDB::Init(Handle target){ FunctionTemplate::New(WgDB::Import)->GetFunction()); tpl->PrototypeTemplate()->Set(String::NewSymbol("findRecord"), FunctionTemplate::New(WgDB::FindRecord)->GetFunction()); + tpl->PrototypeTemplate()->Set(String::NewSymbol("query"), + FunctionTemplate::New(WgDB::Query)->GetFunction()); tpl->Set(String::NewSymbol("EQUAL"), Int32::New(int(WG_COND_EQUAL))); tpl->Set(String::NewSymbol("NOT_EQUAL"), Int32::New(int(WG_COND_NOT_EQUAL))); @@ -712,6 +768,68 @@ Handle WgDB::FindRecord(const Arguments& args){ return scope.Close(Undefined()); } +Handle WgDB::Query(const Arguments& args){ + HandleScope scope; + int argc = args.Length(); + + if(argc < 1){ + return ThrowException(Exception::Error(String::New("query requires 1 parameter"))); + } + + if(!args[0]->IsArray()){ + return ThrowException(Exception::TypeError(String::New("query argument 1 must be an array"))); + } + + Baton* baton = new Baton(); + if(args[argc - 1]->IsFunction()){ + baton->has_cb = true; + baton->callback = Persistent::New(Local::Cast(args[argc - 1])); + } + + WgDB* db = ObjectWrap::Unwrap(args.This()); + baton->wgdb = db; + db->Ref(); + + CursorData* data = new CursorData(); + + Local arg_array = Local::Cast(args[0]->ToObject()); + data->arglen = arg_array->Length(); + wg_query_arg arglist[data->arglen]; + data->arglist = arglist; + int i; + for(i = 0; i < data->arglen; ++i){ + Local next_val = arg_array->Get(i); + if(!next_val->IsArray()){ + return ThrowException(Exception::TypeError(String::New("query argument 1 must be an array of arrays: [ [, , ], ... ]"))); + } + Local next = Local::Cast(next_val->ToObject()); + if(next->Length() != 3){ + return ThrowException(Exception::TypeError(String::New("query argument 1 must be an array of arrays: [ [, , ], ... ]"))); + } + + Local field = next->Get(0); + if(!field->IsInt32()){ + return ThrowException(Exception::TypeError(String::New("query argument 1 must be an array of arrays: [ [, , ], ... ]"))); + } + + Local cond = next->Get(1); + if(!cond->IsInt32()){ + return ThrowException(Exception::TypeError(String::New("query argument 1 must be an array of arrays: [ [, , ], ... ]"))); + } + + data->arglist[i].column = field->Int32Value(); + data->arglist[i].cond = cond->Int32Value(); + data->arglist[i].value = v8_to_encoded_param(baton->wgdb->db_ptr, next->Get(2)); + } + + baton->data = data; + uv_work_t *req = new uv_work_t; + req->data = baton; + uv_queue_work(uv_default_loop(), req, do_query, Cursor::do_after_create_cursor); + + return scope.Close(Undefined()); +} + /* * * Record Class Definitions @@ -869,3 +987,80 @@ Handle Record::GetField(const Arguments& args){ return scope.Close(Undefined()); } + +/* + * + * Cursor Class Definitions + * +*/ +Cursor::~Cursor(){ + if(this->wgdb && this->wgdb->db_ptr){ + wg_free_query(this->wgdb->db_ptr, this->query); + int i; + for(i = 0; i < this->arglen; ++i){ + wg_free_query_param(this->wgdb->db_ptr, this->arglist[i].value); + } + } +} + +void Cursor::Init(){ + HandleScope scope; + Local tpl = FunctionTemplate::New(New); + tpl->SetClassName(String::NewSymbol("cursor")); + tpl->InstanceTemplate()->SetInternalFieldCount(2); + tpl->PrototypeTemplate()->Set(String::NewSymbol("next"), + FunctionTemplate::New(Cursor::Next)->GetFunction()); + constructor = Persistent::New(tpl->GetFunction()); +} + +Handle Cursor::New(const Arguments& args){ + HandleScope scope; + + Cursor* cursor = new Cursor(); + cursor->Wrap(args.This()); + + return scope.Close(args.This()); +} + +Handle Cursor::Next(const Arguments& args){ + HandleScope scope; + int argc = args.Length(); + + Baton* baton = new Baton(); + if(argc > 0 && args[argc - 1]->IsFunction()){ + baton->has_cb = true; + baton->callback = Persistent::New(Local::Cast(args[argc - 1])); + } + + Cursor* cursor = ObjectWrap::Unwrap(args.This()); + baton->wgdb = cursor->wgdb; + baton->data = cursor; + cursor->Ref(); + + uv_work_t *req = new uv_work_t; + req->data = baton; + uv_queue_work(uv_default_loop(), req, do_cursor_next, Record::do_after_create_record); + + return scope.Close(Undefined()); +} + +void Cursor::do_after_create_cursor(uv_work_t* req, int status){ + Baton* baton = static_cast(req->data); + CursorData* data = static_cast(baton->data); + Local result; + if(!baton->error){ + Local cursor_obj = Cursor::constructor->NewInstance(0, NULL); + Cursor* cursor = ObjectWrap::Unwrap(cursor_obj); + cursor->arglen = data->arglen; + cursor->arglist = data->arglist; + cursor->query = data->query; + cursor->wgdb = baton->wgdb; + result = Local::New(cursor_obj); + } else{ + result = Local::New(Undefined()); + } + end_call(baton->has_cb, baton->callback, baton->error, result); + baton->callback.Dispose(); + delete baton; + delete req; +} diff --git a/src/wgdb.h b/src/wgdb.h index 8387ee5..f29abeb 100644 --- a/src/wgdb.h +++ b/src/wgdb.h @@ -25,6 +25,7 @@ class WgDB : ObjectWrap{ static Handle FirstRecord(const Arguments& args); static Handle Dump(const Arguments& args); static Handle Import(const Arguments& args); + static Handle Query(const Arguments& args); static Handle FindRecord(const Arguments& args); }; @@ -47,4 +48,20 @@ class Record : ObjectWrap{ static void do_after_create_record(uv_work_t* req, int status); }; +class Cursor : ObjectWrap{ + public: + WgDB* wgdb; + wg_query* query; + wg_query_arg* arglist; + int arglen; + + ~Cursor(); + static Persistent constructor; + static void Init(); + static Handle New(const Arguments& args); + static Handle Next(const Arguments& args); + + static void do_after_create_cursor(uv_work_t* req, int status); +}; + #endif