} TBSONCTX;
static void toBSON0(Handle<Object> obj, bson *bs, TBSONCTX *ctx) {
- HandleScope scope
+ HandleScope scope;
assert(ctx && obj->IsObject());
if (ctx->traversed->Get(obj)->IsObject()) {
bs->err = BSON_ERROR_ANY;
struct BSONCmdData {
std::string cname; //Name of collection
- std::vector<bson*> bsons; //bsons to save
+ std::vector<bson*> bsons; //bsons to save|query
std::vector<bson_oid_t> ids; //saved updated oids
bson_oid_t ref; //Bson ref
struct BSONQCmdData : public BSONCmdData {
TCLIST *res;
+ int qflags;
+ uint32_t count;
- BSONQCmdData(const char* cname) : BSONCmdData::BSONCmdData(cname), res(NULL) {
+ BSONQCmdData(const char* _cname, int _qflags) :
+ BSONCmdData::BSONCmdData(_cname), res(NULL), qflags(_qflags), count(0) {
}
virtual ~BSONQCmdData() {
typedef EIOCmdTask<NodeEJDB> EJBTask;
typedef EIOCmdTask<NodeEJDB, BSONCmdData> BSONCmdTask;
- typedef EIOCmdTask<NodeEJDB, BSONQCmdData> QueryCmdTask;
+ typedef EIOCmdTask<NodeEJDB, BSONQCmdData> BSONQCmdTask;
static Persistent<FunctionTemplate> constructor_template;
assert(bs);
toBSON(Handle<Object>::Cast(v), bs);
if (bs->err) {
- Local<String> msg = String::New(bs->errstr ? bs->errstr : "bson creation failed");
+ Local<String> msg = String::New(bs->errstr ? bs->errstr : "BSON creation failed");
bson_del(bs);
delete cmdata;
return scope.Close(ThrowException(Exception::Error(msg)));
return scope.Close(args.This());
}
+ static Handle<Value> s_query(const Arguments& args) {
+ HandleScope scope;
+ REQ_ARGS(4);
+ REQ_STR_ARG(0, cname)
+ REQ_ARR_ARG(1, qarr);
+ REQ_INT32_ARG(2, qflags);
+ REQ_FUN_ARG(3, cb);
+
+ if (qarr->Length() == 0) {
+ return scope.Close(ThrowException(Exception::Error(String::New("Query array must have at least one element"))));
+ }
+ BSONQCmdData *cmdata = new BSONQCmdData(*cname, qflags);
+ uint32_t len = qarr->Length();
+ for (uint32_t i = 0; i < len; ++i) {
+ Local<Value> qv = qarr->Get(i);
+ if (i > 0 && i == len - 1 && (qv->IsNull() || qv->IsUndefined())) { //Last hints element can be NULL
+ cmdata->bsons.push_back(NULL);
+ continue;
+ } else if (!qv->IsObject()) {
+ delete cmdata;
+ return scope.Close(ThrowException(
+ Exception::Error(
+ String::New("Each element of query array must be an object (except last hints element)"))
+ ));
+ }
+ bson *bs = bson_create();
+ bson_init(bs);
+ toBSON(Local<Object>::Cast(qv), bs);
+ bson_finish(bs);
+ if (bs->err) {
+ Local<String> msg = String::New(bs->errstr ? bs->errstr : "BSON error");
+ bson_del(bs);
+ delete cmdata;
+ return scope.Close(ThrowException(Exception::Error(msg)));
+ }
+ cmdata->bsons.push_back(bs);
+ }
+ NodeEJDB *njb = ObjectWrap::Unwrap< NodeEJDB > (args.This());
+ assert(njb);
+ BSONQCmdTask *task = new BSONQCmdTask(cb, njb, cmdQuery, cmdata, BSONQCmdTask::delete_val);
+ uv_queue_work(uv_default_loop(), &task->uv_work, s_exec_cmd_eio, s_exec_cmd_eio_after);
+ return scope.Close(args.This());
+ }
+
///////////////////////////////////////////////////////////////////////////
// Instance methods //
///////////////////////////////////////////////////////////////////////////
void exec_cmd(EJBTask *task) {
int cmd = task->cmd;
switch (cmd) {
+ case cmdQuery:
+ query((BSONQCmdTask*) task);
+ break;
case cmdLoad:
load((BSONCmdTask*) task);
break;
void exec_cmd_after(EJBTask *task) {
int cmd = task->cmd;
switch (cmd) {
+ case cmdQuery:
+ query_after((BSONQCmdTask*) task);
+ break;
case cmdLoad:
load_after((BSONCmdTask*) task);
break;
assert(cmdata);
EJCOLL *coll = ejdbcreatecoll(m_jb, cmdata->cname.c_str(), NULL);
if (!coll) {
+ task->cmd_ret = CMD_RET_ERROR;
task->cmd_ret_msg = _jb_error_msg();
return;
}
assert(cmdata);
EJCOLL *coll = ejdbcreatecoll(m_jb, cmdata->cname.c_str(), NULL);
if (!coll) {
+ task->cmd_ret = CMD_RET_ERROR;
task->cmd_ret_msg = _jb_error_msg();
return;
}
}
}
+ void query(BSONQCmdTask *task) {
+ if (!_check_state((EJBTask*) task)) {
+ return;
+ }
+ bson oqarrstack[8]; //max 8 $or bsons on stack
+ BSONQCmdData *cmdata = task->cmd_data;
+ EJCOLL *coll = ejdbcreatecoll(m_jb, cmdata->cname.c_str(), NULL);
+ if (!coll) {
+ task->cmd_ret = CMD_RET_ERROR;
+ task->cmd_ret_msg = _jb_error_msg();
+ return;
+ }
+ std::vector<bson*> &bsons = cmdata->bsons;
+ int orsz = (int) bsons.size() - 2; //Minus main qry at begining and hints object at the end
+ if (orsz < 0) orsz = 0;
+ bson *oqarr = ((orsz <= 8) ? oqarrstack : (bson*) malloc(orsz * sizeof (bson)));
+ for (int i = 1; i < (int) bsons.size() - 1; ++i) {
+ oqarr[i - 1] = *(bsons.at(i));
+ break;
+ }
+ TCLIST *res = NULL;
+ EJQ *q = ejdbcreatequery(
+ m_jb,
+ bsons.front(),
+ (orsz > 0 ? oqarr : NULL), orsz,
+ ((bsons.size() > 1) ? bsons.back() : NULL));
+ if (!q) {
+ task->cmd_ret = CMD_RET_ERROR;
+ task->cmd_ret_msg = _jb_error_msg();
+ goto finish;
+ }
+ res = ejdbqrysearch(coll, q, &cmdata->count, cmdata->qflags, NULL);
+
+
+finish:
+ if (q) {
+ ejdbquerydel(q);
+ }
+ if (oqarr && oqarr != oqarrstack) {
+ free(oqarr);
+ }
+ }
+
+ void query_after(BSONQCmdTask *task) {
+ HandleScope scope;
+
+ }
+
bool open(const char* dbpath, int mode) {
m_jb = ejdbnew();
if (!m_jb) {
NODE_SET_PROTOTYPE_METHOD(constructor_template, "close", s_close);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "save", s_save);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "load", s_load);
+ NODE_SET_PROTOTYPE_METHOD(constructor_template, "query", s_query);
}
void Ref() {
};
///////////////////////////////////////////////////////////////////////////
- // Result set cursor //
+ // ResultSet cursor //
///////////////////////////////////////////////////////////////////////////
class NodeEJDBCursor : public ObjectWrap {
}
NodeEJDBCursor(TCLIST *rs) : m_rs(rs), m_pos(0) {
-
}
virtual ~NodeEJDBCursor() {
void Unref() {
ObjectWrap::Unref();
}
-
};
-
Persistent<FunctionTemplate> NodeEJDB::constructor_template;
Persistent<FunctionTemplate> NodeEJDBCursor::constructor_template;
assert(hdb);
if(hdb->fd >= 0) tchdbclose(hdb);
if(hdb->mmtx){
- pthread_key_delete(*(pthread_key_t *)hdb->eckey);
pthread_mutex_destroy(hdb->wmtx);
pthread_mutex_destroy(hdb->dmtx);
for(int i = UINT8_MAX; i >= 0; i--){
pthread_rwlock_destroy((pthread_rwlock_t *)hdb->rmtxs + i);
}
pthread_rwlock_destroy(hdb->mmtx);
- TCFREE(hdb->eckey);
TCFREE(hdb->wmtx);
TCFREE(hdb->dmtx);
TCFREE(hdb->rmtxs);
TCFREE(hdb->mmtx);
}
+ if (hdb->eckey) {
+ pthread_key_delete(*(pthread_key_t *)hdb->eckey);
+ TCFREE(hdb->eckey);
+ }
TCFREE(hdb);
}
/* Get the last happened error code of a hash database object. */
int tchdbecode(TCHDB *hdb){
assert(hdb);
- return hdb->mmtx ?
- (int)(intptr_t)pthread_getspecific(*(pthread_key_t *)hdb->eckey) : hdb->ecode;
+ return (hdb->eckey) ? (int)(intptr_t)pthread_getspecific(*(pthread_key_t *)hdb->eckey) : hdb->ecode;
}
TCMALLOC(hdb->rmtxs, (UINT8_MAX + 1) * sizeof(pthread_rwlock_t));
TCMALLOC(hdb->dmtx, sizeof(pthread_mutex_t));
TCMALLOC(hdb->wmtx, sizeof(pthread_mutex_t));
- TCMALLOC(hdb->eckey, sizeof(pthread_key_t));
bool err = false;
if(pthread_mutexattr_settype(&rma, PTHREAD_MUTEX_RECURSIVE) != 0) err = true;
if(pthread_rwlock_init(hdb->mmtx, NULL) != 0) err = true;
}
if(pthread_mutex_init(hdb->dmtx, &rma) != 0) err = true;
if(pthread_mutex_init(hdb->wmtx, NULL) != 0) err = true;
- if(pthread_key_create(hdb->eckey, NULL) != 0) err = true;
if(err){
tchdbsetecode(hdb, TCETHREAD, __FILE__, __LINE__, __func__);
pthread_mutexattr_destroy(&rma);
- TCFREE(hdb->eckey);
TCFREE(hdb->wmtx);
TCFREE(hdb->dmtx);
TCFREE(hdb->rmtxs);
TCFREE(hdb->mmtx);
- hdb->eckey = NULL;
hdb->wmtx = NULL;
hdb->dmtx = NULL;
hdb->rmtxs = NULL;
bool tchdbopen(TCHDB *hdb, const char *path, int omode){
assert(hdb && path);
if(!HDBLOCKMETHOD(hdb, true)) return false;
+ if (!hdb->eckey) {
+ TCMALLOC(hdb->eckey, sizeof(pthread_key_t));
+ if(pthread_key_create(hdb->eckey, NULL)) {
+ TCFREE(hdb->eckey);
+ hdb->eckey = NULL;
+ HDBUNLOCKMETHOD(hdb);
+ return false;
+ }
+ }
if(hdb->fd >= 0){
tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
HDBUNLOCKMETHOD(hdb);
assert(hdb && filename && line >= 1 && func);
int myerrno = errno;
if(!hdb->fatal){
- if(hdb->mmtx){
- pthread_setspecific(*(pthread_key_t *)hdb->eckey, (void *)(intptr_t)ecode);
- } else {
- hdb->ecode = ecode;
- }
+ if (hdb->eckey) {
+ pthread_setspecific(*(pthread_key_t *)hdb->eckey, (void *)(intptr_t)ecode);
+ } else {
+ hdb->ecode = ecode;
+ }
+ }
+ if (ecode == TCESUCCESS) {
+ return;
}
- if(ecode != TCESUCCESS && ecode != TCEINVALID && ecode != TCEKEEP && ecode != TCENOREC){
+ if(ecode != TCEINVALID && ecode != TCEKEEP && ecode != TCENOREC){
hdb->fatal = true;
if(hdb->fd >= 0 && (hdb->omode & HDBOWRITER)) tchdbsetflag(hdb, HDBFFATAL, true);
}