#1
authoradam <anton@adamansky.com>
Wed, 31 Oct 2012 06:57:00 +0000 (13:57 +0700)
committeradam <anton@adamansky.com>
Wed, 31 Oct 2012 06:57:00 +0000 (13:57 +0700)
node/ejdb_native.cc
node/nbproject/configurations.xml
tcejdb/bson.c
tcejdb/bson.h
tcejdb/ejdb.c
tcejdb/ejdb.h

index cc9f05b..1b9bdb5 100644 (file)
@@ -16,6 +16,7 @@
 using namespace node;
 using namespace v8;
 
+static const int CMD_RET_ERROR = 1;
 
 #define DEFINE_INT64_CONSTANT(target, constant)                       \
   (target)->Set(v8::String::NewSymbol(#constant),                         \
@@ -23,17 +24,21 @@ using namespace v8;
                 static_cast<v8::PropertyAttribute>(                       \
                     v8::ReadOnly|v8::DontDelete))
 
-static Persistent<String> sym_close;
-static Persistent<String> sym_save;
-static Persistent<String> sym_load;
-static Persistent<String> sym_query;
-
 namespace ejdb {
 
-    /** Convert V8 object into binary json instance. After usage, it must be freed by bson_del() */
-    static void toBSON(Handle<Object> obj, bson *bs) {
+    typedef struct {
+        Handle<Object> traversed;
+    } TBSONCTX;
+
+    static void toBSON0(Handle<Object> obj, bson *bs, TBSONCTX *ctx) {
         HandleScope scope;
-        assert(obj->IsObject());
+        assert(ctx && obj->IsObject());
+        if (ctx->traversed->Get(obj)->IsObject()) {
+            bs->err = BSON_ERROR_ANY;
+            bs->errstr = strdup("Circular object reference");
+            return;
+        }
+        ctx->traversed->Set(obj, obj);
         Local<Array> pnames = obj->GetOwnPropertyNames();
         for (uint32_t i = 0; i < pnames->Length(); ++i) {
             Local<Value> pn = pnames->Get(i);
@@ -77,7 +82,7 @@ namespace ejdb {
                 } else {
                     bson_append_start_object(bs, *spn);
                 }
-                toBSON(Handle<Object>::Cast(pv), bs);
+                toBSON0(Handle<Object>::Cast(pv), bs, ctx);
                 if (pv->IsArray()) {
                     bson_append_finish_array(bs);
                 } else {
@@ -87,6 +92,14 @@ namespace ejdb {
         }
     }
 
+    /** Convert V8 object into binary json instance. After usage, it must be freed by bson_del() */
+    static void toBSON(Handle<Object> obj, bson *bs) {
+        HandleScope scope;
+        TBSONCTX ctx;
+        ctx.traversed = Object::New();
+        toBSON0(obj, bs, &ctx);
+    }
+
     ///////////////////////////////////////////////////////////////////////////
     //                          Main NodeEJDB                                //
     ///////////////////////////////////////////////////////////////////////////
@@ -94,16 +107,25 @@ namespace ejdb {
     class NodeEJDB : public ObjectWrap {
 
         enum { //Commands
-            cmdSave = 1 //Save JSON object
+            cmdSave = 1, //Save JSON object
+            cmdLoad = 2, //Load BSON by oid
+            cmdQuery = 3 //Query collection
         };
 
-        struct BSONCmdData {
-            std::vector<bson*> bsons;
+        //Any bson cmd data
 
-            BSONCmdData() {
+        struct BSONCmdData {
+            std::string cname; //Name of collection
+            std::vector<bson*> bsons; //bsons to save
+            std::vector<bson_oid_t> ids; //saved updated oids
+            bson_oid_t ref; //Bson ref
+
+            BSONCmdData(const char* cname) {
+                this->cname = cname;
+                memset(&ref, 0, sizeof (ref));
             }
 
-            ~BSONCmdData() {
+            virtual ~BSONCmdData() {
                 std::vector<bson*>::iterator it;
                 for (it = bsons.begin(); it < bsons.end(); it++) {
                     bson *bs = *(it);
@@ -112,8 +134,24 @@ namespace ejdb {
             }
         };
 
+        //Query cmd data
+
+        struct BSONQCmdData : public BSONCmdData {
+            TCLIST *res;
+
+            BSONQCmdData(const char* cname) : BSONCmdData::BSONCmdData(cname), res(NULL) {
+            }
+
+            virtual ~BSONQCmdData() {
+                if (res) {
+                    tclistdel(res);
+                }
+            }
+        };
+
         typedef EIOCmdTask<NodeEJDB> EJBTask;
-        typedef EIOCmdTask<NodeEJDB, BSONCmdData> EJBSONTask;
+        typedef EIOCmdTask<NodeEJDB, BSONCmdData> BSONCmdTask;
+        typedef EIOCmdTask<NodeEJDB, BSONQCmdData> QueryCmdTask;
 
         static Persistent<FunctionTemplate> constructor_template;
 
@@ -126,7 +164,7 @@ namespace ejdb {
             NodeEJDB *njb = new NodeEJDB();
             if (!njb->open(*dbPath, mode)) {
                 std::ostringstream os;
-                os << "Unable to open database: " << (*dbPath) << " error: " << njb->jb_error_msg();
+                os << "Unable to open database: " << (*dbPath) << " error: " << njb->_jb_error_msg();
                 EJ_LOG_ERROR("%s", os.str().c_str());
                 delete njb;
                 return scope.Close(ThrowException(Exception::Error(String::New(os.str().c_str()))));
@@ -136,17 +174,17 @@ namespace ejdb {
         }
 
         static void s_exec_cmd_eio(uv_work_t *req) {
-            EJBTask *task = static_cast<EJBTask*>(req->data);
+            EJBTask *task = static_cast<EJBTask*> (req->data);
             NodeEJDB *njb = task->wrapped;
             assert(njb);
-            //TODO njb->exec_cmd(task);
+            njb->exec_cmd(task);
         }
 
         static void s_exec_cmd_eio_after(uv_work_t *req) {
-            EJBTask *task = static_cast<EJBTask*>(req->data);
+            EJBTask *task = static_cast<EJBTask*> (req->data);
             NodeEJDB *njb = task->wrapped;
             assert(njb);
-            //TODO njb->exec_cmd_after(task);
+            njb->exec_cmd_after(task);
             delete task;
         }
 
@@ -155,18 +193,40 @@ namespace ejdb {
             NodeEJDB *njb = ObjectWrap::Unwrap< NodeEJDB > (args.This());
             assert(njb);
             if (!njb->close()) {
-                return scope.Close(ThrowException(Exception::Error(String::New(njb->jb_error_msg()))));
+                return scope.Close(ThrowException(Exception::Error(String::New(njb->_jb_error_msg()))));
             }
             return scope.Close(args.This());
         }
 
+        static Handle<Value> s_load(const Arguments& args) {
+            HandleScope scope;
+            REQ_ARGS(3);
+            REQ_STR_ARG(0, cname); //Collection name
+            REQ_STR_ARG(1, soid); //String OID
+            REQ_FUN_ARG(2, cb); //Callback
+            if (soid.length() != 24) {
+                return scope.Close(ThrowException(Exception::Error(String::New("Argument 2: Invalid OID string"))));
+            }
+            bson_oid_t oid;
+            bson_oid_from_string(&oid, *soid);
+            BSONCmdData *cmdata = new BSONCmdData(*cname);
+            cmdata->ref = oid;
+
+            NodeEJDB *njb = ObjectWrap::Unwrap< NodeEJDB > (args.This());
+            assert(njb);
+            BSONCmdTask *task = new BSONCmdTask(cb, njb, cmdLoad, cmdata, BSONCmdTask::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());
+        }
+
         static Handle<Value> s_save(const Arguments& args) {
             HandleScope scope;
-            REQ_ARGS(2);
-            REQ_ARR_ARG(0, oarr);
-            REQ_FUN_ARG(1, cb);
+            REQ_ARGS(3);
+            REQ_STR_ARG(0, cname); //Collection name
+            REQ_ARR_ARG(1, oarr); //Array of JAVA objects
+            REQ_FUN_ARG(2, cb); //Callback
 
-            BSONCmdData *cmdata = new BSONCmdData();
+            BSONCmdData *cmdata = new BSONCmdData(*cname);
             for (uint32_t i = 0; i < oarr->Length(); ++i) {
                 Local<Value> v = oarr->Get(i);
                 if (!v->IsObject()) continue;
@@ -184,18 +244,100 @@ namespace ejdb {
             }
             NodeEJDB *njb = ObjectWrap::Unwrap< NodeEJDB > (args.This());
             assert(njb);
-            EJBSONTask *task = new EJBSONTask(cb, njb, cmdSave, cmdata, EJBSONTask::delete_val);
+            BSONCmdTask *task = new BSONCmdTask(cb, njb, cmdSave, cmdata, BSONCmdTask::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());
         }
 
-        NodeEJDB() : m_jb(NULL) {
+        ///////////////////////////////////////////////////////////////////////////
+        //                            Instance methods                           //
+        ///////////////////////////////////////////////////////////////////////////
+
+        void exec_cmd(EJBTask *task) {
+            int cmd = task->cmd;
+            switch (cmd) {
+                case cmdLoad:
+                    load((BSONCmdTask*) task);
+                    break;
+                case cmdSave:
+                    save((BSONCmdTask*) task);
+                    break;
+            }
         }
 
-        virtual ~NodeEJDB() {
-            if (m_jb) {
-                ejdbdel(m_jb);
+        void exec_cmd_after(EJBTask *task) {
+            int cmd = task->cmd;
+            switch (cmd) {
+                case cmdLoad:
+                    load_after((BSONCmdTask*) task);
+                    break;
+                case cmdSave:
+                    save_after((BSONCmdTask*) task);
+                    break;
             }
+            delete task;
+        }
+
+        void save(BSONCmdTask *task) {
+            if (!_check_state((EJBTask*) task)) {
+                return;
+            }
+            BSONCmdData *cmdata = task->cmd_data;
+            assert(cmdata);
+            EJCOLL *coll = ejdbcreatecoll(m_jb, cmdata->cname.c_str(), NULL);
+            if (!coll) {
+                task->cmd_ret_msg = _jb_error_msg();
+            }
+
+            std::vector<bson*>::iterator it;
+            for (it = cmdata->bsons.begin(); it < cmdata->bsons.end(); it++) {
+                bson *bs = *(it);
+                assert(bs);
+                bson_oid_t oid;
+                if (!ejdbsavebson(coll, bs, &oid)) {
+                    task->cmd_ret = CMD_RET_ERROR;
+                    task->cmd_ret_msg = _jb_error_msg();
+                    break;
+                }
+                cmdata->ids.push_back(oid);
+            }
+        }
+
+        void save_after(BSONCmdTask *task) {
+            HandleScope scope;
+            Local<Value> argv[2];
+            if (task->cmd_ret != 0) {
+                argv[0] = Exception::Error(String::New(task->cmd_ret_msg.c_str()));
+            } else {
+                argv[0] = Local<Primitive>::New(Null());
+            }
+            Local<Array> oids = Array::New();
+            std::vector<bson_oid_t>::iterator it;
+            int32_t c = 0;
+            for (it = task->cmd_data->ids.begin(); it < task->cmd_data->ids.end(); it++) {
+                char oidhex[25];
+                bson_oid_t& oid = *it;
+                bson_oid_to_string(&oid, oidhex);
+                oids->Set(Integer::New(c++), String::New(oidhex));
+            }
+            argv[1] = oids;
+            TryCatch try_catch;
+            task->cb->Call(Context::GetCurrent()->Global(), 2, argv);
+            if (try_catch.HasCaught()) {
+                FatalException(try_catch);
+            }
+        }
+
+        void load(BSONCmdTask *task) {
+            if (!_check_state((EJBTask*) task)) {
+                return;
+            }
+
+        }
+
+        void load_after(BSONCmdTask *task) {
+            HandleScope scope;
+
         }
 
         bool open(const char* dbpath, int mode) {
@@ -210,8 +352,26 @@ namespace ejdb {
             return m_jb ? ejdbclose(m_jb) : false;
         }
 
-        const char* jb_error_msg() {
-            return m_jb ? ejdberrmsg(ejdbecode(m_jb)) : "unknown error";
+        const char* _jb_error_msg() {
+            return m_jb ? ejdberrmsg(ejdbecode(m_jb)) : "Unknown error";
+        }
+
+        bool _check_state(EJBTask *task) {
+            if (!ejdbisopen(m_jb)) {
+                task->cmd_ret = CMD_RET_ERROR;
+                task->cmd_ret_msg = "Database is not opened";
+                return false;
+            }
+            return true;
+        }
+
+        NodeEJDB() : m_jb(NULL) {
+        }
+
+        virtual ~NodeEJDB() {
+            if (m_jb) {
+                ejdbdel(m_jb);
+            }
         }
 
     public:
@@ -245,15 +405,11 @@ namespace ejdb {
             NODE_DEFINE_CONSTANT(target, JBQRYCOUNT);
 
             //Symbols
-            sym_close = NODE_PSYMBOL("close");
-            sym_save = NODE_PSYMBOL("save");
-            sym_load = NODE_PSYMBOL("load");
-            sym_query = NODE_PSYMBOL("query");
-
             target->Set(v8::String::NewSymbol("NodeEJDB"), constructor_template->GetFunction());
 
             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);
         }
 
         void Ref() {
index 58f454e..c158788 100644 (file)
@@ -9,10 +9,6 @@
             </df>
           </df>
         </df>
-        <in>Makefile</in>
-        <in>binding.Makefile</in>
-        <in>config.gypi</in>
-        <in>ejdb_native.target.mk</in>
       </df>
       <df name="nodejs">
         <df name="benchmark">
           </df>
         </df>
       </df>
+      <df name="tests">
+      </df>
       <df name="win32">
         <in>pthread_mutex.h</in>
       </df>
index 78d725c..1a5db45 100644 (file)
@@ -757,6 +757,10 @@ EJDB_EXPORT void bson_destroy(bson *b) {
         b->data = 0;
         b->cur = 0;
         b->finished = 1;
+        if (b->errstr) {
+            bson_free_func(b->errstr);
+            b->errstr = NULL;
+        }
     }
 }
 
index 8a75c01..49d28cc 100644 (file)
@@ -54,7 +54,8 @@ enum bson_validity_t {
     BSON_NOT_UTF8 = (1 << 1), /**< A key or a string is not valid UTF-8. */
     BSON_FIELD_HAS_DOT = (1 << 2), /**< Warning: key contains '.' character. */
     BSON_FIELD_INIT_DOLLAR = (1 << 3), /**< Warning: key starts with '$' character. */
-    BSON_ALREADY_FINISHED = (1 << 4) /**< Trying to modify a finished BSON object. */
+    BSON_ALREADY_FINISHED = (1 << 4), /**< Trying to modify a finished BSON object. */
+    BSON_ERROR_ANY = (1 << 5) /**< Unspecified error */
 };
 
 enum bson_binary_subtype_t {
index 7a07d5d..f0e85de 100644 (file)
@@ -150,6 +150,10 @@ EJDB_EXPORT bool ejdbclose(EJDB *jb) {
     return rv;
 }
 
+EJDB_EXPORT bool ejdbisopen(EJDB *jb) {
+    return JBISOPEN(jb);
+}
+
 EJDB_EXPORT bool ejdbopen(EJDB *jb, const char *path, int mode) {
     assert(jb && path);
     assert(jb->metadb);
index cdb1771..78b1fbf 100644 (file)
@@ -121,6 +121,14 @@ EJDB_EXPORT bool ejdbclose(EJDB *jb);
  */
 EJDB_EXPORT bool ejdbopen(EJDB *jb, const char *path, int mode);
 
+
+/**
+ * Return true if database is in open state
+ * @param jb EJDB database hadle
+ * @return True if database is in open state otherwise false
+ */
+EJDB_EXPORT bool ejdbisopen(EJDB *jb);
+
 /**
  * Retrieve collection handle for collection specified `collname`.
  * If collection with specified name does't exists it will return NULL.