Index: library/mongodb/README =================================================================== diff -u -r2b56284a45054d5136ddfb67343a70655aba5666 -r04b17cf850af721f6ad1760dece06ef78b11da83 --- library/mongodb/README (.../README) (revision 2b56284a45054d5136ddfb67343a70655aba5666) +++ library/mongodb/README (.../README) (revision 04b17cf850af721f6ad1760dece06ef78b11da83) @@ -3,14 +3,20 @@ Ingredients: https://github.com/mongodb/mongo - https://github.com/mongodb/mongo-c-driver (requires version v0.3 or better) + https://github.com/mongodb/mongo-c-driver +The current version is tested with +- MongoDB v2.4.4-pre- +- mongodb-c-driver v0.7.1 + Compile or obtain mongodb (the database). Compile or obtain the mongo-c-driver (client interface) +Install the mongo-c-driver -Assume, Tcl is under /usr/local/ns/lib and the mongo-c-driver is under -/usr/local/src/mongo-c-driver/, then configure the nsf interface via the -following command from nsf*/library/mongodb/ +Assume, Tcl is under /usr/local/ns/lib and the source of the +mongo-c-driver is under /usr/local/src/mongo-c-driver/, +then configure the nsf interface via the following +command from nsf*/library/mongodb/ ./configure --with-tcl=/usr/local/ns/lib --with-nsf=../../ \ --with-mongo=/usr/local/src/mongo-c-driver/src/,/usr/local/src/mongo-c-driver @@ -28,23 +34,37 @@ * go to your nsf source directory * make sure, the c-driver libraries are on the library path - export DYLD_LIBRARY_PATH=/usr/local/src/mongo-c-driver/:`pwd` + (assuming the the c-driver was installed in /usr/local/lib) - * run ./nxsh library/mongodb/example-nsf-mongo.tcl + export DYLD_LIBRARY_PATH=/usr/local/lib:`pwd` + + * run + + ./nxsh library/mongodb/example-nsf-mongo.tcl The script is using the low level interface (nsf::mongo) and has a few insert, query and delete statements, some of these are commented out. - * run ./nxsh library/mongodb/example-nx-mongo.tcl + * run + + ./nxsh library/mongodb/example-nx-mongo.tcl This example script is using the higher level object oriented interface for nx (nx::mongo). +* Further sample-scripts: + + ./nxsh library/mongodb/example-nx-bi.tcl + ./nxsh library/mongodb/example-nx-reference-one.tcl + ./nxsh library/mongodb/example-nx-reference-many.tcl + ./nxsh library/mongodb/example-nsf-gridfs.tcl + + After running the scripts, you should see output like the one below. % /usr/local/bin/mongo - MongoDB shell version: 1.9.0-pre- + MongoDB shell version: 2.4.4-pre- connecting to: test > use tutorial switched to db tutorial Index: library/mongodb/example-nsf-mongo.tcl =================================================================== diff -u -r2b56284a45054d5136ddfb67343a70655aba5666 -r04b17cf850af721f6ad1760dece06ef78b11da83 --- library/mongodb/example-nsf-mongo.tcl (.../example-nsf-mongo.tcl) (revision 2b56284a45054d5136ddfb67343a70655aba5666) +++ library/mongodb/example-nsf-mongo.tcl (.../example-nsf-mongo.tcl) (revision 04b17cf850af721f6ad1760dece06ef78b11da83) @@ -1,6 +1,7 @@ package require nx package require nsf::mongo +#nsf::configure debug 2 # # One might query these values from the mongo shell via: # @@ -11,9 +12,18 @@ #set mongoConn [::mongo::connect -server 127.0.0.1:27017] set mongoConn [::mongo::connect] +puts "Connection: $mongoConn" if {1} { ::mongo::remove $mongoConn tutorial.persons {} + # Drop old potenially old collection and + # recreate it as a capped collection + ::mongo::run $mongoConn tutorial {drop string persons} + puts "\nCreate a capped collection: [::mongo::run $mongoConn tutorial { + create string persons + capped bool 1 + size int 100000 + }]" puts "\nInserting a few tuples" if {[catch { @@ -47,6 +57,38 @@ puts stderr "\nCount Age > 30" puts [::mongo::count $mongoConn tutorial.persons {age object {$gt int 30}}] +puts stderr "\nAge > 30 (all atts, via cursor interface)" +set cursor [::mongo::cursor::find $mongoConn tutorial.persons [list \$query object {age object {$gt int 30}}]] +puts "Cursor: $cursor" +puts NEXT=[::mongo::cursor::next $cursor] +puts NEXT=[::mongo::cursor::next $cursor] +puts NEXT=[::mongo::cursor::next $cursor] +::mongo::cursor::close $cursor + +puts stderr "\nAge > 30 (all atts, via cursor interface, tailable)" +set cursor [::mongo::cursor::find $mongoConn tutorial.persons [list \$query object {age object {$gt int 30}}] -tailable] +puts "Cursor: $cursor" +if {$cursor ne ""} { + while {1} { + set result [::mongo::cursor::next $cursor] + if {$result eq ""} break + puts NEXT=$result + } + ::mongo::cursor::close $cursor +} + +puts stderr "\nempty result (via cursor interface)" +set cursor [::mongo::cursor::find $mongoConn tutorial.persons [list \$query object {age object {$gt int 300}}]] +puts "Cursor: $cursor" +if {$cursor ne ""} { + while {1} { + set result [::mongo::cursor::next $cursor] + if {$result eq ""} break + puts NEXT=$result + } + ::mongo::cursor::close $cursor +} + puts stderr "\nArray 'a' contains 'x'" puts [join [::mongo::query $mongoConn tutorial.persons [list \$query object {a string "x"}]] \n] Index: library/mongodb/example-nx-bi.tcl =================================================================== diff -u -r9b6c213c40496c03faa151f4f92028fc2610c396 -r04b17cf850af721f6ad1760dece06ef78b11da83 --- library/mongodb/example-nx-bi.tcl (.../example-nx-bi.tcl) (revision 9b6c213c40496c03faa151f4f92028fc2610c396) +++ library/mongodb/example-nx-bi.tcl (.../example-nx-bi.tcl) (revision 04b17cf850af721f6ad1760dece06ef78b11da83) @@ -28,6 +28,8 @@ package require nx::serializer package require nx::test +#nsf::configure debug 2 + # Establish connection to the database ::nx::mongo::db connect -db "tutorial" @@ -46,16 +48,16 @@ nx::mongo::Class create Comment { :property author:required :property comment:required - :property replies:embedded,incremental,type=::Comment,0..n + :property -incremental replies:embedded,type=::Comment,0..n } nx::mongo::Class create Posting { :index tags :property title:required :property author:required :property ts:required - :property comments:embedded,incremental,type=::Comment,0..n - :property tags:incremental,0..n + :property -incremental comments:embedded,type=::Comment,0..n + :property -incremental tags:0..n } ###################################################################### Index: library/mongodb/example-nx-mongo.tcl =================================================================== diff -u -ra24e1f836c3126d0a0e9467bde3a9fa8da901711 -r04b17cf850af721f6ad1760dece06ef78b11da83 --- library/mongodb/example-nx-mongo.tcl (.../example-nx-mongo.tcl) (revision a24e1f836c3126d0a0e9467bde3a9fa8da901711) +++ library/mongodb/example-nx-mongo.tcl (.../example-nx-mongo.tcl) (revision 04b17cf850af721f6ad1760dece06ef78b11da83) @@ -6,6 +6,7 @@ # Gustaf Neumann fecit, April 2011 # package require nx::mongo +#nsf::configure debug 2 # Establish connection to the database ::nx::mongo::db connect -db "tutorial" Index: library/mongodb/mongoAPI.decls =================================================================== diff -u -ra08065ac227c3b26514f29392a961f11db121369 -r04b17cf850af721f6ad1760dece06ef78b11da83 --- library/mongodb/mongoAPI.decls (.../mongoAPI.decls) (revision a08065ac227c3b26514f29392a961f11db121369) +++ library/mongodb/mongoAPI.decls (.../mongoAPI.decls) (revision 04b17cf850af721f6ad1760dece06ef78b11da83) @@ -11,6 +11,7 @@ mongo 1 gridfs 1 gridfile 1 + mongo_cursor 1 } @@ -23,6 +24,12 @@ {-argName "-timeout" -required 0 -nrargs 1 -type int32} } +cmd run NsfMongoRunCmd { + {-argName "conn" -required 1 -type mongo} + {-argName "db" -required 1} + {-argName "cmd" -required 1 -type tclobj} +} + cmd count NsfMongoCount { {-argName "conn" -required 1 -type mongo} {-argName "namespace" -required 1} @@ -33,6 +40,7 @@ {-argName "conn" -required 1 -type mongo} {-argName "namespace" -required 1} {-argName "attributes" -required 1 -type tclobj} + {-argName "-name" -required 0 -nrargs 1} {-argName "-background" -required 0 -nrargs 0} {-argName "-dropdups" -required 0 -nrargs 0} {-argName "-sparse" -required 0 -nrargs 0} @@ -70,6 +78,26 @@ } # +# Cursor +# +cmd cursor::find NsfMongoCursorFind { + {-argName "conn" -required 1 -type mongo} + {-argName "namespace" -required 1} + {-argName "query" -required 1 -type tclobj} + {-argName "-atts" -required 0 -nrargs 1 -type tclobj} + {-argName "-limit" -required 0 -type int32} + {-argName "-skip" -required 0 -type int32} + {-argName "-tailable" -required 0 -nrargs 0} + {-argName "-awaitdata" -required 0 -nrargs 0} +} +cmd cursor::next NsfMongoCursorNext { + {-argName "cursor" -required 1 -type mongo_cursor} +} +cmd cursor::close NsfMongoCursorClose { + {-argName "cursor" -required 1 -type mongo_cursor -withObj 1} +} + +# # GridFS # cmd gridfs::open NsfMongoGridFSOpen { @@ -122,4 +150,5 @@ cmd "gridfile::seek" NsfMongoGridFileSeek { {-argName "file" -required 1 -type gridfile} {-argName "offset" -required 1 -type int32} -} \ No newline at end of file +} + Index: library/mongodb/mongoAPI.h =================================================================== diff -u -r2b56284a45054d5136ddfb67343a70655aba5666 -r04b17cf850af721f6ad1760dece06ef78b11da83 --- library/mongodb/mongoAPI.h (.../mongoAPI.h) (revision 2b56284a45054d5136ddfb67343a70655aba5666) +++ library/mongodb/mongoAPI.h (.../mongoAPI.h) (revision 04b17cf850af721f6ad1760dece06ef78b11da83) @@ -21,14 +21,17 @@ /* just to define the symbol */ -static Nsf_methodDefinition method_definitions[20]; +static Nsf_methodDefinition method_definitions[24]; static CONST char *method_command_namespace_names[] = { "::mongo" }; static int NsfMongoCloseStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []); static int NsfMongoConnectStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []); static int NsfMongoCountStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []); +static int NsfMongoCursorCloseStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []); +static int NsfMongoCursorFindStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []); +static int NsfMongoCursorNextStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []); static int NsfMongoGridFSCloseStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []); static int NsfMongoGridFSOpenStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []); static int NsfMongoGridFSRemoveFileStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []); @@ -44,11 +47,15 @@ static int NsfMongoInsertStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []); static int NsfMongoQueryStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []); static int NsfMongoRemoveStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []); +static int NsfMongoRunCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []); static int NsfMongoUpdateStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv []); static int NsfMongoClose(Tcl_Interp *interp, mongo *connPtr, Tcl_Obj *connObj); static int NsfMongoConnect(Tcl_Interp *interp, CONST char *withReplica_set, Tcl_Obj *withServer, int withTimeout); static int NsfMongoCount(Tcl_Interp *interp, mongo *connPtr, CONST char *namespace, Tcl_Obj *query); +static int NsfMongoCursorClose(Tcl_Interp *interp, mongo_cursor *cursorPtr, Tcl_Obj *cursorObj); +static int NsfMongoCursorFind(Tcl_Interp *interp, mongo *connPtr, CONST char *namespace, Tcl_Obj *query, Tcl_Obj *withAtts, int withLimit, int withSkip, int withTailable, int withAwaitdata); +static int NsfMongoCursorNext(Tcl_Interp *interp, mongo_cursor *cursorPtr); static int NsfMongoGridFSClose(Tcl_Interp *interp, gridfs *gfsPtr, Tcl_Obj *gfsObj); static int NsfMongoGridFSOpen(Tcl_Interp *interp, mongo *connPtr, CONST char *dbname, CONST char *prefix); static int NsfMongoGridFSRemoveFile(Tcl_Interp *interp, gridfs *gfsPtr, CONST char *filename); @@ -60,16 +67,20 @@ static int NsfMongoGridFileOpen(Tcl_Interp *interp, gridfs *fsPtr, CONST char *filename); static int NsfMongoGridFileRead(Tcl_Interp *interp, gridfile *filePtr, int size); static int NsfMongoGridFileSeek(Tcl_Interp *interp, gridfile *filePtr, int offset); -static int NsfMongoIndex(Tcl_Interp *interp, mongo *connPtr, CONST char *namespace, Tcl_Obj *attributes, int withBackground, int withDropdups, int withSparse, int withUnique); +static int NsfMongoIndex(Tcl_Interp *interp, mongo *connPtr, CONST char *namespace, Tcl_Obj *attributes, CONST char *withName, int withBackground, int withDropdups, int withSparse, int withUnique); static int NsfMongoInsert(Tcl_Interp *interp, mongo *connPtr, CONST char *namespace, Tcl_Obj *values); static int NsfMongoQuery(Tcl_Interp *interp, mongo *connPtr, CONST char *namespace, Tcl_Obj *query, Tcl_Obj *withAtts, int withLimit, int withSkip); static int NsfMongoRemove(Tcl_Interp *interp, mongo *connPtr, CONST char *namespace, Tcl_Obj *condition); +static int NsfMongoRunCmd(Tcl_Interp *interp, mongo *connPtr, CONST char *db, Tcl_Obj *cmd); static int NsfMongoUpdate(Tcl_Interp *interp, mongo *connPtr, CONST char *namespace, Tcl_Obj *cond, Tcl_Obj *values, int withUpsert, int withAll); enum { NsfMongoCloseIdx, NsfMongoConnectIdx, NsfMongoCountIdx, + NsfMongoCursorCloseIdx, + NsfMongoCursorFindIdx, + NsfMongoCursorNextIdx, NsfMongoGridFSCloseIdx, NsfMongoGridFSOpenIdx, NsfMongoGridFSRemoveFileIdx, @@ -85,6 +96,7 @@ NsfMongoInsertIdx, NsfMongoQueryIdx, NsfMongoRemoveIdx, + NsfMongoRunCmdIdx, NsfMongoUpdateIdx } NsfMethods; @@ -96,7 +108,7 @@ if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoCloseIdx].paramDefs, - method_definitions[NsfMongoCloseIdx].nrParameters, 0, 1, + method_definitions[NsfMongoCloseIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { mongo *connPtr = (mongo *)pc.clientData[0]; @@ -115,7 +127,7 @@ if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoConnectIdx].paramDefs, - method_definitions[NsfMongoConnectIdx].nrParameters, 0, 1, + method_definitions[NsfMongoConnectIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { CONST char *withReplica_set = (CONST char *)pc.clientData[0]; Tcl_Obj *withServer = (Tcl_Obj *)pc.clientData[1]; @@ -136,7 +148,7 @@ if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoCountIdx].paramDefs, - method_definitions[NsfMongoCountIdx].nrParameters, 0, 1, + method_definitions[NsfMongoCountIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { mongo *connPtr = (mongo *)pc.clientData[0]; CONST char *namespace = (CONST char *)pc.clientData[1]; @@ -151,13 +163,77 @@ } static int +NsfMongoCursorCloseStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + ParseContext pc; + (void)clientData; + + if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], + method_definitions[NsfMongoCursorCloseIdx].paramDefs, + method_definitions[NsfMongoCursorCloseIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, + &pc) == TCL_OK)) { + mongo_cursor *cursorPtr = (mongo_cursor *)pc.clientData[0]; + + assert(pc.status == 0); + return NsfMongoCursorClose(interp, cursorPtr,pc.objv[0]); + + } else { + return TCL_ERROR; + } +} + +static int +NsfMongoCursorFindStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + ParseContext pc; + (void)clientData; + + if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], + method_definitions[NsfMongoCursorFindIdx].paramDefs, + method_definitions[NsfMongoCursorFindIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, + &pc) == TCL_OK)) { + mongo *connPtr = (mongo *)pc.clientData[0]; + CONST char *namespace = (CONST char *)pc.clientData[1]; + Tcl_Obj *query = (Tcl_Obj *)pc.clientData[2]; + Tcl_Obj *withAtts = (Tcl_Obj *)pc.clientData[3]; + int withLimit = (int )PTR2INT(pc.clientData[4]); + int withSkip = (int )PTR2INT(pc.clientData[5]); + int withTailable = (int )PTR2INT(pc.clientData[6]); + int withAwaitdata = (int )PTR2INT(pc.clientData[7]); + + assert(pc.status == 0); + return NsfMongoCursorFind(interp, connPtr, namespace, query, withAtts, withLimit, withSkip, withTailable, withAwaitdata); + + } else { + return TCL_ERROR; + } +} + +static int +NsfMongoCursorNextStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + ParseContext pc; + (void)clientData; + + if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], + method_definitions[NsfMongoCursorNextIdx].paramDefs, + method_definitions[NsfMongoCursorNextIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, + &pc) == TCL_OK)) { + mongo_cursor *cursorPtr = (mongo_cursor *)pc.clientData[0]; + + assert(pc.status == 0); + return NsfMongoCursorNext(interp, cursorPtr); + + } else { + return TCL_ERROR; + } +} + +static int NsfMongoGridFSCloseStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoGridFSCloseIdx].paramDefs, - method_definitions[NsfMongoGridFSCloseIdx].nrParameters, 0, 1, + method_definitions[NsfMongoGridFSCloseIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { gridfs *gfsPtr = (gridfs *)pc.clientData[0]; @@ -176,7 +252,7 @@ if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoGridFSOpenIdx].paramDefs, - method_definitions[NsfMongoGridFSOpenIdx].nrParameters, 0, 1, + method_definitions[NsfMongoGridFSOpenIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { mongo *connPtr = (mongo *)pc.clientData[0]; CONST char *dbname = (CONST char *)pc.clientData[1]; @@ -197,7 +273,7 @@ if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoGridFSRemoveFileIdx].paramDefs, - method_definitions[NsfMongoGridFSRemoveFileIdx].nrParameters, 0, 1, + method_definitions[NsfMongoGridFSRemoveFileIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { gridfs *gfsPtr = (gridfs *)pc.clientData[0]; CONST char *filename = (CONST char *)pc.clientData[1]; @@ -217,7 +293,7 @@ if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoGridFSStoreFileIdx].paramDefs, - method_definitions[NsfMongoGridFSStoreFileIdx].nrParameters, 0, 1, + method_definitions[NsfMongoGridFSStoreFileIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { gridfs *gfsPtr = (gridfs *)pc.clientData[0]; CONST char *filename = (CONST char *)pc.clientData[1]; @@ -239,7 +315,7 @@ if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoGridFileCloseIdx].paramDefs, - method_definitions[NsfMongoGridFileCloseIdx].nrParameters, 0, 1, + method_definitions[NsfMongoGridFileCloseIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { gridfile *filePtr = (gridfile *)pc.clientData[0]; @@ -258,7 +334,7 @@ if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoGridFileGetContentTypeIdx].paramDefs, - method_definitions[NsfMongoGridFileGetContentTypeIdx].nrParameters, 0, 1, + method_definitions[NsfMongoGridFileGetContentTypeIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { gridfile *filePtr = (gridfile *)pc.clientData[0]; @@ -277,7 +353,7 @@ if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoGridFileGetContentlengthIdx].paramDefs, - method_definitions[NsfMongoGridFileGetContentlengthIdx].nrParameters, 0, 1, + method_definitions[NsfMongoGridFileGetContentlengthIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { gridfile *filePtr = (gridfile *)pc.clientData[0]; @@ -296,7 +372,7 @@ if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoGridFileGetMetaDataIdx].paramDefs, - method_definitions[NsfMongoGridFileGetMetaDataIdx].nrParameters, 0, 1, + method_definitions[NsfMongoGridFileGetMetaDataIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { gridfile *filePtr = (gridfile *)pc.clientData[0]; @@ -315,7 +391,7 @@ if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoGridFileOpenIdx].paramDefs, - method_definitions[NsfMongoGridFileOpenIdx].nrParameters, 0, 1, + method_definitions[NsfMongoGridFileOpenIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { gridfs *fsPtr = (gridfs *)pc.clientData[0]; CONST char *filename = (CONST char *)pc.clientData[1]; @@ -335,7 +411,7 @@ if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoGridFileReadIdx].paramDefs, - method_definitions[NsfMongoGridFileReadIdx].nrParameters, 0, 1, + method_definitions[NsfMongoGridFileReadIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { gridfile *filePtr = (gridfile *)pc.clientData[0]; int size = (int )PTR2INT(pc.clientData[1]); @@ -355,7 +431,7 @@ if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoGridFileSeekIdx].paramDefs, - method_definitions[NsfMongoGridFileSeekIdx].nrParameters, 0, 1, + method_definitions[NsfMongoGridFileSeekIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { gridfile *filePtr = (gridfile *)pc.clientData[0]; int offset = (int )PTR2INT(pc.clientData[1]); @@ -375,18 +451,19 @@ if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoIndexIdx].paramDefs, - method_definitions[NsfMongoIndexIdx].nrParameters, 0, 1, + method_definitions[NsfMongoIndexIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { mongo *connPtr = (mongo *)pc.clientData[0]; CONST char *namespace = (CONST char *)pc.clientData[1]; Tcl_Obj *attributes = (Tcl_Obj *)pc.clientData[2]; - int withBackground = (int )PTR2INT(pc.clientData[3]); - int withDropdups = (int )PTR2INT(pc.clientData[4]); - int withSparse = (int )PTR2INT(pc.clientData[5]); - int withUnique = (int )PTR2INT(pc.clientData[6]); + CONST char *withName = (CONST char *)pc.clientData[3]; + int withBackground = (int )PTR2INT(pc.clientData[4]); + int withDropdups = (int )PTR2INT(pc.clientData[5]); + int withSparse = (int )PTR2INT(pc.clientData[6]); + int withUnique = (int )PTR2INT(pc.clientData[7]); assert(pc.status == 0); - return NsfMongoIndex(interp, connPtr, namespace, attributes, withBackground, withDropdups, withSparse, withUnique); + return NsfMongoIndex(interp, connPtr, namespace, attributes, withName, withBackground, withDropdups, withSparse, withUnique); } else { return TCL_ERROR; @@ -400,7 +477,7 @@ if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoInsertIdx].paramDefs, - method_definitions[NsfMongoInsertIdx].nrParameters, 0, 1, + method_definitions[NsfMongoInsertIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { mongo *connPtr = (mongo *)pc.clientData[0]; CONST char *namespace = (CONST char *)pc.clientData[1]; @@ -421,7 +498,7 @@ if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoQueryIdx].paramDefs, - method_definitions[NsfMongoQueryIdx].nrParameters, 0, 1, + method_definitions[NsfMongoQueryIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { mongo *connPtr = (mongo *)pc.clientData[0]; CONST char *namespace = (CONST char *)pc.clientData[1]; @@ -445,7 +522,7 @@ if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoRemoveIdx].paramDefs, - method_definitions[NsfMongoRemoveIdx].nrParameters, 0, 1, + method_definitions[NsfMongoRemoveIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { mongo *connPtr = (mongo *)pc.clientData[0]; CONST char *namespace = (CONST char *)pc.clientData[1]; @@ -460,13 +537,34 @@ } static int +NsfMongoRunCmdStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + ParseContext pc; + (void)clientData; + + if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], + method_definitions[NsfMongoRunCmdIdx].paramDefs, + method_definitions[NsfMongoRunCmdIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, + &pc) == TCL_OK)) { + mongo *connPtr = (mongo *)pc.clientData[0]; + CONST char *db = (CONST char *)pc.clientData[1]; + Tcl_Obj *cmd = (Tcl_Obj *)pc.clientData[2]; + + assert(pc.status == 0); + return NsfMongoRunCmd(interp, connPtr, db, cmd); + + } else { + return TCL_ERROR; + } +} + +static int NsfMongoUpdateStub(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { ParseContext pc; (void)clientData; if (likely(ArgumentParse(interp, objc, objv, NULL, objv[0], method_definitions[NsfMongoUpdateIdx].paramDefs, - method_definitions[NsfMongoUpdateIdx].nrParameters, 0, 1, + method_definitions[NsfMongoUpdateIdx].nrParameters, 0, NSF_ARGPARSE_BUILTIN, &pc) == TCL_OK)) { mongo *connPtr = (mongo *)pc.clientData[0]; CONST char *namespace = (CONST char *)pc.clientData[1]; @@ -483,7 +581,7 @@ } } -static Nsf_methodDefinition method_definitions[20] = { +static Nsf_methodDefinition method_definitions[24] = { {"::mongo::close", NsfMongoCloseStub, 1, { {"conn", NSF_ARG_REQUIRED, 1, Nsf_ConvertToPointer, NULL,NULL,"mongo",NULL,NULL,NULL,NULL,NULL}} }, @@ -497,6 +595,22 @@ {"namespace", NSF_ARG_REQUIRED, 1, Nsf_ConvertToString, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"query", NSF_ARG_REQUIRED, 1, Nsf_ConvertToTclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, +{"::mongo::cursor::close", NsfMongoCursorCloseStub, 1, { + {"cursor", NSF_ARG_REQUIRED, 1, Nsf_ConvertToPointer, NULL,NULL,"mongo_cursor",NULL,NULL,NULL,NULL,NULL}} +}, +{"::mongo::cursor::find", NsfMongoCursorFindStub, 8, { + {"conn", NSF_ARG_REQUIRED, 1, Nsf_ConvertToPointer, NULL,NULL,"mongo",NULL,NULL,NULL,NULL,NULL}, + {"namespace", NSF_ARG_REQUIRED, 1, Nsf_ConvertToString, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, + {"query", NSF_ARG_REQUIRED, 1, Nsf_ConvertToTclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, + {"-atts", 0, 1, Nsf_ConvertToTclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, + {"-limit", 0, 1, Nsf_ConvertToInt32, NULL,NULL,"int32",NULL,NULL,NULL,NULL,NULL}, + {"-skip", 0, 1, Nsf_ConvertToInt32, NULL,NULL,"int32",NULL,NULL,NULL,NULL,NULL}, + {"-tailable", 0, 0, Nsf_ConvertToString, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, + {"-awaitdata", 0, 0, Nsf_ConvertToString, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} +}, +{"::mongo::cursor::next", NsfMongoCursorNextStub, 1, { + {"cursor", NSF_ARG_REQUIRED, 1, Nsf_ConvertToPointer, NULL,NULL,"mongo_cursor",NULL,NULL,NULL,NULL,NULL}} +}, {"::mongo::gridfs::close", NsfMongoGridFSCloseStub, 1, { {"gfs", NSF_ARG_REQUIRED, 1, Nsf_ConvertToPointer, NULL,NULL,"gridfs",NULL,NULL,NULL,NULL,NULL}} }, @@ -539,10 +653,11 @@ {"file", NSF_ARG_REQUIRED, 1, Nsf_ConvertToPointer, NULL,NULL,"gridfile",NULL,NULL,NULL,NULL,NULL}, {"offset", NSF_ARG_REQUIRED, 1, Nsf_ConvertToInt32, NULL,NULL,"int32",NULL,NULL,NULL,NULL,NULL}} }, -{"::mongo::index", NsfMongoIndexStub, 7, { +{"::mongo::index", NsfMongoIndexStub, 8, { {"conn", NSF_ARG_REQUIRED, 1, Nsf_ConvertToPointer, NULL,NULL,"mongo",NULL,NULL,NULL,NULL,NULL}, {"namespace", NSF_ARG_REQUIRED, 1, Nsf_ConvertToString, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"attributes", NSF_ARG_REQUIRED, 1, Nsf_ConvertToTclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, + {"-name", 0, 1, Nsf_ConvertToString, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"-background", 0, 0, Nsf_ConvertToString, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"-dropdups", 0, 0, Nsf_ConvertToString, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"-sparse", 0, 0, Nsf_ConvertToString, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, @@ -566,6 +681,11 @@ {"namespace", NSF_ARG_REQUIRED, 1, Nsf_ConvertToString, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"condition", NSF_ARG_REQUIRED, 1, Nsf_ConvertToTclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} }, +{"::mongo::run", NsfMongoRunCmdStub, 3, { + {"conn", NSF_ARG_REQUIRED, 1, Nsf_ConvertToPointer, NULL,NULL,"mongo",NULL,NULL,NULL,NULL,NULL}, + {"db", NSF_ARG_REQUIRED, 1, Nsf_ConvertToString, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, + {"cmd", NSF_ARG_REQUIRED, 1, Nsf_ConvertToTclobj, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}} +}, {"::mongo::update", NsfMongoUpdateStub, 6, { {"conn", NSF_ARG_REQUIRED, 1, Nsf_ConvertToPointer, NULL,NULL,"mongo",NULL,NULL,NULL,NULL,NULL}, {"namespace", NSF_ARG_REQUIRED, 1, Nsf_ConvertToString, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, Index: library/mongodb/nsfmongo.c =================================================================== diff -u -r2b56284a45054d5136ddfb67343a70655aba5666 -r04b17cf850af721f6ad1760dece06ef78b11da83 --- library/mongodb/nsfmongo.c (.../nsfmongo.c) (revision 2b56284a45054d5136ddfb67343a70655aba5666) +++ library/mongodb/nsfmongo.c (.../nsfmongo.c) (revision 04b17cf850af721f6ad1760dece06ef78b11da83) @@ -31,6 +31,7 @@ static int gridfsCount = 0; static int gridfileCount = 0; static int mongoCount = 0; +static int cursorCount = 0; /*********************************************************************** * The following definitions should not be here, but they are included @@ -353,6 +354,19 @@ bson_append_finish_object(bbPtr); break; } + + case BSON_BINDATA: + case BSON_DBREF: + case BSON_CODE: + case BSON_SYMBOL: + case BSON_CODEWSCOPE: + return NsfPrintError(interp, "tag %s not handled yet", tag); + break; + + case BSON_UNDEFINED: + case BSON_EOO: + break; + /* no default here, to get the warning to the compilation log for the time being */ } return result; @@ -425,7 +439,7 @@ if (connPtr) { mongo_destroy(connPtr); - Nsf_PointerDelete(ObjStr(connObj), connPtr); + Nsf_PointerDelete(ObjStr(connObj), connPtr, 1); } return TCL_OK; } @@ -460,15 +474,15 @@ * No -server argument or an empty list was provided; use the * mongo default values. */ - status = mongo_connect( connPtr, "127.0.0.1", 27017 ); + status = mongo_client( connPtr, "127.0.0.1", 27017 ); } else if (objc == 1 && replicaSet == NULL) { /* * A single element was provided to -server, we have no replica * set specified. */ mongo_parse_host(ObjStr(objv[0]), &host_port); - status = mongo_connect( connPtr, host_port.host, host_port.port ); + status = mongo_client( connPtr, host_port.host, host_port.port ); if (buffer) {ckfree(buffer);} } else if (replicaSet) { @@ -523,6 +537,38 @@ } /* +cmd run NsfMongoRunCmd { + {-argName "conn" -required 1 -type mongo} + {-argName "db" -required 1} + {-argName "cmd" -required 1 -type tclobj} +} +*/ +static int +NsfMongoRunCmd(Tcl_Interp *interp, mongo *connPtr, CONST char *db, Tcl_Obj *cmdObj) { + int result, objc; + Tcl_Obj **objv; + bson cmd[1], out[1]; + + result = Tcl_ListObjGetElements(interp, cmdObj, &objc, &objv); + if (result != TCL_OK || (objc % 3 != 0)) { + return NsfPrintError(interp, "%s: must contain a multiple of 3 elements", ObjStr(cmdObj)); + } + BsonAppendObjv(interp, cmd, objc, objv); + + mongo_clear_errors( connPtr ); + result = mongo_run_command( connPtr, db, cmd, out ); + bson_destroy( cmd ); + + if (result != MONGO_OK) { + return NsfPrintError(interp, "mongo::run: provided command returned an unknown error"); + } + + Tcl_SetObjResult(interp, Tcl_NewIntObj(1)); + return TCL_OK; +} + + +/* cmd query NsfMongoCount { {-argName "conn" -required 1 -type mongo} {-argName "namespace" -required 1} @@ -572,6 +618,7 @@ {-argName "conn" -required 1 -type mongo} {-argName "namespace" -required 1} {-argName "attributes" -required 1 -type tclobj} + {-argName "-name" -required 0 -nrargs 1} {-argName "-background" -required 0 -nrargs 0} {-argName "-dropdups" -required 0 -nrargs 0} {-argName "-sparse" -required 0 -nrargs 0} @@ -583,6 +630,7 @@ mongo *connPtr, CONST char *namespace, Tcl_Obj *attributesObj, + CONST char *withName, int withBackground, int withDropdups, int withSparse, @@ -602,7 +650,7 @@ if (withSparse) {options |= MONGO_INDEX_SPARSE;} if (withUnique) {options |= MONGO_INDEX_UNIQUE;} - success = mongo_create_index(connPtr, namespace, keys, options, out); + success = mongo_create_index(connPtr, namespace, keys, withName, options, out); bson_destroy(keys); /* TODO: examples in mongo-client do not touch out; do we have to do something about it? */ @@ -785,6 +833,109 @@ } /*********************************************************************** + * Cursor interface + ***********************************************************************/ +/* +cmd cursor::find NsfMongoCursorFind { + {-argName "conn" -required 1 -type mongo} + {-argName "namespace" -required 1} + {-argName "query" -required 1 -type tclobj} + {-argName "-atts" -required 0 -nrargs 1 -type tclobj} + {-argName "-limit" -required 0 -type int32} + {-argName "-skip" -required 0 -type int32} + {-argName "-tailable" -required 0 -nrargs 0} + {-argName "-awaitdata" -required 0 -nrargs 0} +} +*/ +static int +NsfMongoCursorFind(Tcl_Interp *interp, mongo *connPtr, CONST char *namespace, + Tcl_Obj *queryObj, Tcl_Obj *withAttsObj, + int withLimit, int withSkip, + int withTailable, int withAwaitdata) { + int objc1, objc2, result, options = 0; + Tcl_Obj **objv1, **objv2; + char buffer[80]; + mongo_cursor *cursor; + bson query[1]; + bson atts[1]; + + /*fprintf(stderr, "NsfMongoQuery: namespace %s withLimit %d withSkip %d\n", + namespace, withLimit, withSkip);*/ + + result = Tcl_ListObjGetElements(interp, queryObj, &objc1, &objv1); + if (result != TCL_OK || (objc1 % 3 != 0)) { + return NsfPrintError(interp, "%s: must contain a multiple of 3 elements", ObjStr(queryObj)); + } + if (withAttsObj) { + result = Tcl_ListObjGetElements(interp, withAttsObj, &objc2, &objv2); + if (result != TCL_OK || (objc2 % 3 != 0)) { + return NsfPrintError(interp, "%s: must contain a multiple of 3 elements", ObjStr(withAttsObj)); + } + } else { + objc2 = 0; + } + + BsonAppendObjv(interp, query, objc1, objv1); + BsonAppendObjv(interp, atts, objc2, objv2); + + /* + * The last field of mongo_find is options, semantics are described here + * http://www.mongodb.org/display/DOCS/Mongo+Wire+Protocol#MongoWireProtocol-OPQUERY + */ + if (withTailable) { + options |= MONGO_TAILABLE; + } + if (withAwaitdata) { + options |= MONGO_AWAIT_DATA; + } + cursor = mongo_find( connPtr, namespace, query, atts, withLimit, withSkip, options); + + if (cursor) { + Nsf_PointerAdd(interp, buffer, "mongo_cursor", cursor); + Tcl_SetObjResult(interp, Tcl_NewStringObj(buffer, -1)); + } else { + Tcl_ResetResult(interp); + } + + bson_destroy( query ); + bson_destroy( atts ); + + return TCL_OK; +} + +/* +cmd cursor::next NsfMongoCursorNext { + {-argName "cursor" -required 1 -type mongo_cursor} +} +*/ +static int +NsfMongoCursorNext(Tcl_Interp *interp, mongo_cursor *cursor) { + int result; + + result = mongo_cursor_next( cursor ); + if (result == MONGO_OK) { + Tcl_SetObjResult(interp, BsonToList(interp, (&cursor->current)->data, 0)); + } + return TCL_OK; +} + +/* +cmd cursor::close NsfMongoCursorClose { + {-argName "cursor" -required 1 -type mongo_cursor -withObj 1} +} +*/ +static int +NsfMongoCursorClose(Tcl_Interp *interp, mongo_cursor *cursor, Tcl_Obj *cursorObj) { + + mongo_cursor_destroy( cursor ); + Nsf_PointerDelete(ObjStr(cursorObj), cursor, 0); + + return TCL_OK; +} + + + +/*********************************************************************** * GridFS interface ***********************************************************************/ /* @@ -820,10 +971,13 @@ static int NsfMongoGridFSRemoveFile(Tcl_Interp *interp, gridfs *gridfsPtr, CONST char *filename) { + int result; /* the current interfaces does not return a status ! */ - gridfs_remove_filename(gridfsPtr, filename); + result = gridfs_remove_filename(gridfsPtr, filename); + Tcl_SetObjResult(interp, Tcl_NewIntObj(result == MONGO_OK)); + return TCL_OK; } @@ -839,7 +993,8 @@ NsfMongoGridFSStoreFile(Tcl_Interp *interp, gridfs *gridfsPtr, CONST char *filename, CONST char *remotename, CONST char *contenttype) { - int result = gridfs_store_file(gridfsPtr, filename, remotename, contenttype); + int flags = 0; // TODO: add/handle flags + int result = gridfs_store_file(gridfsPtr, filename, remotename, contenttype, flags); /* currently, we do not get the bson structure; Tcl_SetObjResult(interp, BsonToList(interp, b.data, 0));*/ @@ -858,7 +1013,7 @@ NsfMongoGridFSClose(Tcl_Interp *interp, gridfs *gridfsPtr, Tcl_Obj *gridfsObj) { gridfs_destroy(gridfsPtr); - Nsf_PointerDelete(ObjStr(gridfsObj), gridfsPtr); + Nsf_PointerDelete(ObjStr(gridfsObj), gridfsPtr, 1); return TCL_OK; } @@ -879,7 +1034,7 @@ NsfMongoGridFileClose(Tcl_Interp *interp, gridfile* gridFilePtr, Tcl_Obj *gridFileObj) { gridfile_destroy(gridFilePtr); - Nsf_PointerDelete(ObjStr(gridFileObj), gridFilePtr); + Nsf_PointerDelete(ObjStr(gridFileObj), gridFilePtr, 1); return TCL_OK; } @@ -922,8 +1077,9 @@ static int NsfMongoGridFileGetMetaData(Tcl_Interp *interp, gridfile* gridFilePtr) { bson b; + bson_bool_t copyData = 0; // TODO: what does this - gridfile_get_metadata(gridFilePtr, &b); + gridfile_get_metadata(gridFilePtr, &b, copyData); Tcl_SetObjResult(interp, BsonToList(interp, b.data, 0)); return TCL_OK; @@ -943,7 +1099,6 @@ gridFilePtr = (gridfile *)ckalloc(sizeof(gridfile)); result = gridfs_find_filename(gridfsPtr, filename, gridFilePtr); - fprintf(stderr, "NsfMongoGridFileOpen returned result %d\n", result); if (result == MONGO_OK) { Nsf_PointerAdd(interp, buffer, "gridfile", gridFilePtr); @@ -968,7 +1123,7 @@ char *buffer; buffer = ckalloc(size); - readSize = gridfile_read(gridFilePtr, size, buffer); + readSize = gridfile_read_buffer(gridFilePtr, buffer, size); Tcl_SetObjResult(interp, Tcl_NewStringObj(buffer, readSize)); ckfree(buffer); @@ -997,7 +1152,8 @@ void Nsfmongo_Exit(ClientData clientData) { - fprintf(stderr, "Nsfmongo Exit\n"); + Tcl_Interp *interp = (Tcl_Interp *)clientData; + NsfLog(interp,NSF_LOG_NOTICE, "Nsfmongo Exit"); } extern int @@ -1032,9 +1188,10 @@ /* * register the pointer converter */ - Nsf_PointerTypeRegister(interp, "gridfs", &gridfsCount); - Nsf_PointerTypeRegister(interp, "gridfile", &gridfileCount); - Nsf_PointerTypeRegister(interp, "mongo", &mongoCount); + Nsf_PointerTypeRegister(interp, "gridfs", &gridfsCount); + Nsf_PointerTypeRegister(interp, "gridfile", &gridfileCount); + Nsf_PointerTypeRegister(interp, "mongo", &mongoCount); + Nsf_PointerTypeRegister(interp, "mongo_cursor", &cursorCount); /* create all method commands (will use the namespaces above) */ for (i=0; i < nr_elements(method_definitions)-1; i++) { Index: library/mongodb/nx-mongo.tcl =================================================================== diff -u -rc9ef41c49f482a38e89f7cffc54cabf909710425 -r04b17cf850af721f6ad1760dece06ef78b11da83 --- library/mongodb/nx-mongo.tcl (.../nx-mongo.tcl) (revision c9ef41c49f482a38e89f7cffc54cabf909710425) +++ library/mongodb/nx-mongo.tcl (.../nx-mongo.tcl) (revision 04b17cf850af721f6ad1760dece06ef78b11da83) @@ -339,9 +339,14 @@ # Overload method property to provide "::nx::mongo::Attribute" as a # default slot class # - :public method property {spec {-class ::nx::mongo::Attribute} {initblock ""}} { + :public method property { + {-incremental:switch} + spec + {-class ::nx::mongo::Attribute} + {initblock ""} + } { regsub -all {,type=} $spec {,arg=} spec - next [list -class $class $spec $initblock] + next [list -class $class -incremental=$incremental $spec $initblock] } #