summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--db/db.cpp10
-rw-r--r--db/db.sln6
-rw-r--r--db/db.vcproj24
-rw-r--r--db/jsobj.cpp458
-rw-r--r--db/makefile10
-rw-r--r--db/matcher.cpp484
-rw-r--r--db/query.cpp10
-rw-r--r--stdafx.cpp5
-rw-r--r--util/mmap.cpp2
-rw-r--r--util/unittest.h40
-rw-r--r--util/util.cpp7
11 files changed, 575 insertions, 481 deletions
diff --git a/db/db.cpp b/db/db.cpp
index f4a0d069e6e..7c3a8e44522 100644
--- a/db/db.cpp
+++ b/db/db.cpp
@@ -29,6 +29,7 @@
#include "query.h"
#include "introspect.h"
#include "repl.h"
+#include "../util/unittest.h"
bool slave = false;
bool master = false; // true means keep an op log
@@ -740,8 +741,7 @@ void segvhandler(int x) {
void mysighandler(int x) {
signal(x, SIG_IGN);
- cout << "got kill or ctrl c signal " << x << ", will terminate after current cmd ends" << endl;
- problem() << "got kill or ctrl c signal " << x << ", will terminate after current cmd ends" << endl;
+ log() << "got kill or ctrl c signal " << x << ", will terminate after current cmd ends" << endl;
{
dblock lk;
problem() << " now exiting" << endl;
@@ -824,6 +824,8 @@ int main(int argc, char* argv[], char *envp[] )
#endif
srand(curTimeMillis());
+ UnitTest::runTests();
+
if( argc >= 2 ) {
if( strcmp(argv[1], "quicktest") == 0 ) {
quicktest();
@@ -948,13 +950,13 @@ int main(int argc, char* argv[], char *envp[] )
#undef exit
void dbexit(int rc, const char *why) {
- cout << " dbexit: " << why << "; flushing op log and files" << endl;
+ log() << " dbexit: " << why << "; flushing op log and files" << endl;
flushOpLog();
/* must do this before unmapping mem or you may get a seg fault */
closeAllSockets();
MemoryMappedFile::closeAllFiles();
- cout << " dbexit: really exiting now" << endl;
+ log() << " dbexit: really exiting now" << endl;
exit(rc);
}
diff --git a/db/db.sln b/db/db.sln
index 9ebc444204b..43585735798 100644
--- a/db/db.sln
+++ b/db/db.sln
@@ -3,6 +3,8 @@ Microsoft Visual Studio Solution File, Format Version 10.00
# Visual Studio 2008
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "db", "db.vcproj", "{215B2D68-0A70-4D10-8E75-B31010C62A91}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dbgrid", "..\dbgrid\dbgrid.vcproj", "{E03717ED-69B4-4D21-BC55-DF6690B585C6}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
@@ -13,6 +15,10 @@ Global
{215B2D68-0A70-4D10-8E75-B31010C62A91}.Debug|Win32.Build.0 = Debug|Win32
{215B2D68-0A70-4D10-8E75-B31010C62A91}.Release|Win32.ActiveCfg = Release|Win32
{215B2D68-0A70-4D10-8E75-B31010C62A91}.Release|Win32.Build.0 = Release|Win32
+ {E03717ED-69B4-4D21-BC55-DF6690B585C6}.Debug|Win32.ActiveCfg = Debug|Win32
+ {E03717ED-69B4-4D21-BC55-DF6690B585C6}.Debug|Win32.Build.0 = Debug|Win32
+ {E03717ED-69B4-4D21-BC55-DF6690B585C6}.Release|Win32.ActiveCfg = Release|Win32
+ {E03717ED-69B4-4D21-BC55-DF6690B585C6}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/db/db.vcproj b/db/db.vcproj
index 6ab824bf3c7..4fdae169214 100644
--- a/db/db.vcproj
+++ b/db/db.vcproj
@@ -191,6 +191,10 @@
>
</File>
<File
+ RelativePath=".\commands.cpp"
+ >
+ </File>
+ <File
RelativePath=".\db.cpp"
>
</File>
@@ -215,6 +219,10 @@
>
</File>
<File
+ RelativePath=".\matcher.cpp"
+ >
+ </File>
+ <File
RelativePath="..\grid\message.cpp"
>
</File>
@@ -384,6 +392,10 @@
RelativePath=".\storage.h"
>
</File>
+ <File
+ RelativePath="..\util\unittest.h"
+ >
+ </File>
</Filter>
<Filter
Name="Resource Files"
@@ -403,10 +415,6 @@
Name="misc"
>
<File
- RelativePath=".\how_to_build.txt"
- >
- </File>
- <File
RelativePath=".\makefile"
>
</File>
@@ -426,14 +434,6 @@
RelativePath="..\pcre-7.4\pcrecpp.h"
>
</File>
- <File
- RelativePath=".\testdb.js"
- >
- </File>
- <File
- RelativePath=".\todo.txt"
- >
- </File>
</Filter>
<Filter
Name="libs_etc"
diff --git a/db/jsobj.cpp b/db/jsobj.cpp
index 51c8df5f1a3..8687321f3e4 100644
--- a/db/jsobj.cpp
+++ b/db/jsobj.cpp
@@ -19,101 +19,6 @@
#include "stdafx.h"
#include "jsobj.h"
#include "../util/goodies.h"
-#include "javajs.h"
-
-#if defined(_WIN32)
-
-#include <hash_map>
-using namespace stdext;
-
-typedef const char * MyStr;
-struct less_str {
- bool operator()(const MyStr & x, const MyStr & y) const {
- if ( strcmp(x, y) > 0)
- return true;
-
- return false;
- }
-};
-
-typedef hash_map<const char*, int, hash_compare<const char *, less_str> > strhashmap;
-
-#else
-
-#include <ext/hash_map>
-using namespace __gnu_cxx;
-
-typedef const char * MyStr;
-struct eq_str {
- bool operator()(const MyStr & x, const MyStr & y) const {
- if ( strcmp(x, y) == 0)
- return true;
-
- return false;
- }
-};
-
-typedef hash_map<const char*, int, hash<const char *>, eq_str > strhashmap;
-
-#endif
-
-#include "minilex.h"
-
-MiniLex minilex;
-
-class Where {
-public:
- Where() { codeCopy = 0; }
- ~Where() {
- JavaJS->scopeFree(scope);
- delete codeCopy;
- scope = 0; func = 0; codeCopy = 0;
- }
- jlong scope, func;
- strhashmap fields;
-// map<string,int> fields;
- bool fullObject;
- int nFields;
- char *codeCopy;
-
- void setFunc(const char *code) {
- codeCopy = new char[strlen(code)+1];
- strcpy(codeCopy,code);
- func = JavaJS->functionCreate( code );
- minilex.grabVariables(codeCopy, fields);
- // if user references db, eg db.foo.save(obj),
- // we make sure we have the whole thing.
- fullObject = fields.count("fullObject") +
- fields.count("db") > 0;
- nFields = fields.size();
- }
-
- void buildSubset(JSObj& src, JSObjBuilder& dst) {
- JSElemIter it(src);
- int n = 0;
- if( !it.more() ) return;
- while( 1 ) {
- Element e = it.next();
- if( e.eoo() )
- break;
- if( //n == 0 &&
- fields.find(e.fieldName()) != fields.end()
- //fields.count(e.fieldName())
- ) {
- dst.append(e);
- if( ++n >= nFields )
- break;
- }
- }
- }
-};
-
-JSMatcher::~JSMatcher() {
- for( int i = 0; i < nBuilders; i++ )
- delete builders[i];
- delete in;
- delete where;
-}
Element nullElement;
@@ -334,304 +239,6 @@ int getGtLtOp(Element& e) {
return op;
}
-#include "pdfile.h"
-
-JSMatcher::JSMatcher(JSObj &_jsobj) :
- in(0), where(0), jsobj(_jsobj), nRegex(0)
-{
- nBuilders = 0;
-
- JSElemIter i(jsobj);
- n = 0;
- while( i.more() ) {
- Element e = i.next();
- if( e.eoo() )
- break;
-
- if( e.type() == Code && strcmp(e.fieldName(), "$where")==0 ) {
- // $where: function()...
- assert( where == 0 );
- where = new Where();
- const char *code = e.valuestr();
- massert( "$where query, but jni is disabled", JavaJS );
- where->scope = JavaJS->scopeCreate();
- JavaJS->scopeSetString(where->scope, "$client", client->name.c_str());
- where->setFunc(code);
- continue;
- }
-
- if( e.type() == RegEx ) {
- if( nRegex >= 4 ) {
- cout << "ERROR: too many regexes in query" << endl;
- }
- else {
- pcrecpp::RE_Options options;
- options.set_utf8(true);
- const char *flags = e.regexFlags();
- while( flags && *flags ) {
- if( *flags == 'i' )
- options.set_caseless(true);
- else if( *flags == 'm' )
- options.set_multiline(true);
- else if( *flags == 'x' )
- options.set_extended(true);
- flags++;
- }
- regexs[nRegex].re = new pcrecpp::RE(e.regex(), options);
- regexs[nRegex].fieldName = e.fieldName();
- nRegex++;
- }
- continue;
- }
-
- // greater than / less than...
- // e.g., e == { a : { $gt : 3 } }
- // or
- // { a : { $in : [1,2,3] } }
- if( e.type() == Object ) {
- // e.g., fe == { $gt : 3 }
- JSElemIter j(e.embeddedObject());
- bool ok = false;
- while( j.more() ) {
- Element fe = j.next();
- if( fe.eoo() )
- break;
- // Element fe = e.embeddedObject().firstElement();
- const char *fn = fe.fieldName();
- if( fn[0] == '$' && fn[1] ) {
- if( fn[2] == 't' ) {
- int op = Equality;
- if( fn[1] == 'g' ) {
- if( fn[3] == 0 ) op = GT;
- else if( fn[3] == 'e' && fn[4] == 0 ) op = GTE;
- }
- else if( fn[1] == 'l' ) {
- if( fn[3] == 0 ) op = LT;
- else if( fn[3] == 'e' && fn[4] == 0 ) op = LTE;
- }
- if( op && nBuilders < 8) {
- JSObjBuilder *b = new JSObjBuilder();
- builders[nBuilders++] = b;
- b->appendAs(fe, e.fieldName());
- toMatch.push_back( b->done().firstElement() );
- compareOp.push_back(op);
- n++;
- ok = true;
- }
- }
- else if( fn[1] == 'i' && fn[2] == 'n' && fn[3] == 0 && fe.type() == Array ) {
- // $in
- assert( in == 0 ); // only one per query supported so far. finish...
- in = new set<Element,element_lt>();
- JSElemIter i(fe.embeddedObject());
- if( i.more() ) {
- while( 1 ) {
- Element ie = i.next();
- if( ie.eoo() )
- break;
- in->insert(ie);
- }
- }
- toMatch.push_back(e); // not actually used at the moment
- compareOp.push_back(opIN);
- n++;
- ok = true;
- }
- }
- else {
- ok = false;
- break;
- }
- }
- if( ok )
- continue;
- }
-
- {
- // normal, simple case e.g. { a : "foo" }
- toMatch.push_back(e);
- compareOp.push_back(Equality);
- n++;
- }
- }
-}
-
-inline int JSMatcher::valuesMatch(Element& l, Element& r, int op) {
- if( op == 0 )
- return l.valuesEqual(r);
-
- if( op == opIN ) {
- // { $in : [1,2,3] }
- int c = in->count(l);
- return c;
- }
-
- /* check LT, GTE, ... */
- if( l.type() != r.type() )
- return false;
- int c = compareElementValues(l, r);
- int z = 1 << (c+1);
- return (op & z);
-}
-
-/* Check if a particular field matches.
-
- fieldName - field to match "a.b" if we are reaching into an embedded object.
- toMatch - element we want to match.
- obj - database object to check against
- compareOp - Equality, LT, GT, etc.
- deep - out param. set to true/false if we scanned an array
- isArr -
-
- Special forms:
-
- { "a.b" : 3 } means obj.a.b == 3
- { a : { $lt : 3 } } means obj.a < 3
- { a : { $in : [1,2] } } means [1,2].contains(obj.a)
-
- return value
- -1 mismatch
- 0 missing element
- 1 match
-*/
-int JSMatcher::matchesDotted(const char *fieldName, Element& toMatch, JSObj& obj, int compareOp, bool *deep, bool isArr) {
- {
- const char *p = strchr(fieldName, '.');
- if( p ) {
- string left(fieldName, p-fieldName);
-
- Element e = obj.getField(left.c_str());
- if( e.eoo() )
- return 0;
- if( e.type() != Object && e.type() != Array )
- return -1;
-
- JSObj eo = e.embeddedObject();
- return matchesDotted(p+1, toMatch, eo, compareOp, deep, e.type() == Array);
- }
- }
-
- Element e = obj.getField(fieldName);
-
- if( valuesMatch(e, toMatch, compareOp) ) {
- return 1;
- }
- else if( e.type() == Array ) {
- JSElemIter ai(e.embeddedObject());
- while( ai.more() ) {
- Element z = ai.next();
- if( valuesMatch( z, toMatch, compareOp) ) {
- if( deep )
- *deep = true;
- return 1;
- }
- }
- }
- else if( isArr ) {
- JSElemIter ai(obj);
- while( ai.more() ) {
- Element z = ai.next();
- if( z.type() == Object ) {
- JSObj eo = z.embeddedObject();
- int cmp = matchesDotted(fieldName, toMatch, eo, compareOp, deep);
- if( cmp > 0 ) {
- if( deep ) *deep = true;
- return 1;
- }
- }
- }
- }
- else if( e.eoo() ) {
- return 0;
- }
- return -1;
-}
-
-extern int dump;
-
-inline bool _regexMatches(RegexMatcher& rm, Element& e) {
- char buf[64];
- const char *p = buf;
- if( e.type() == String || e.type() == Symbol )
- p = e.valuestr();
- else if( e.type() == Number ) {
- sprintf(buf, "%f", e.number());
- }
- else if( e.type() == Date ) {
- unsigned long long d = e.date();
- time_t t = (d/1000);
- time_t_to_String(t, buf);
- }
- else
- return false;
- return rm.re->PartialMatch(p);
-}
-/* todo: internal dotted notation scans -- not done yet here. */
-inline bool regexMatches(RegexMatcher& rm, Element& e, bool *deep) {
- if( e.type() != Array )
- return _regexMatches(rm, e);
-
- JSElemIter ai(e.embeddedObject());
- while( ai.more() ) {
- Element z = ai.next();
- if( _regexMatches(rm, z) ) {
- if( deep )
- *deep = true;
- return true;
- }
- }
- return false;
-}
-
-/* See if an object matches the query.
- deep - return true when meanswe looked into arrays for a match
-*/
-bool JSMatcher::matches(JSObj& jsobj, bool *deep) {
- if( deep )
- *deep = false;
-
- /* assuming there is usually only one thing to match. if more this
- could be slow sometimes. */
-
- // check normal non-regex cases:
- for( int i = 0; i < n; i++ ) {
- Element& m = toMatch[i];
- int cmp = matchesDotted(toMatch[i].fieldName(), toMatch[i], jsobj, compareOp[i], deep);
-
- /* missing is ok iff we were looking for null */
- if( cmp < 0 )
- return false;
- if( cmp == 0 && (m.type() != jstNULL && m.type() != Undefined ) )
- return false;
- }
-
- for( int r = 0; r < nRegex; r++ ) {
- RegexMatcher& rm = regexs[r];
- Element e = jsobj.getFieldDotted(rm.fieldName);
- if( e.eoo() )
- return false;
- if( !regexMatches(rm, e, deep) )
- return false;
- }
-
- if( where ) {
- if( where->func == 0 )
- return false; // didn't compile
- if( jsobj.objsize() < 200 || where->fullObject ) {
- JavaJS->scopeSetObject(where->scope, "obj", &jsobj);
- } else {
- JSObjBuilder b;
- where->buildSubset(jsobj, b);
- JSObj temp = b.done();
- JavaJS->scopeSetObject(where->scope, "obj", &temp);
- }
- if( JavaJS->invoke(where->scope, where->func) )
- return false;
- return JavaJS->scopeGetBoolean(where->scope, "return") != 0;
- }
-
- return true;
-}
/* JSObj ------------------------------------------------------------*/
@@ -829,7 +436,6 @@ int JSObj::addFields(JSObj& from, set<string>& fields) {
/*-- test things ----------------------------------------------------*/
#pragma pack(push,1)
-
struct MaxKeyData {
MaxKeyData() { totsize=7; maxkey=MaxKey; name=0; eoo=EOO; }
int totsize;
@@ -844,6 +450,7 @@ struct JSObj0 {
int totsize;
char eoo;
} js0;
+#pragma pack(pop)
Element::Element() {
data = &js0.eoo;
@@ -851,60 +458,13 @@ Element::Element() {
totalSize = -1;
}
-struct JSObj1 js1;
-
-struct JSObj2 {
- JSObj2() {
- totsize=sizeof(JSObj2);
- s = String; strcpy_s(sname, 7, "abcdef"); slen = 10;
- strcpy_s(sval, 10, "123456789"); eoo = EOO;
- }
- unsigned totsize;
- char s;
- char sname[7];
- unsigned slen;
- char sval[10];
- char eoo;
-} js2;
-
-struct JSUnitTest {
- JSUnitTest() {
- JSObj j1((const char *) &js1);
- JSObj j2((const char *) &js2);
- JSMatcher m(j2);
- assert( m.matches(j1) );
- js2.sval[0] = 'z';
- assert( !m.matches(j1) );
- JSMatcher n(j1);
- assert( n.matches(j1) );
- assert( !n.matches(j2) );
-
- JSObj j0((const char *) &js0);
- JSMatcher p(j0);
- assert( p.matches(j1) );
- assert( p.matches(j2) );
- }
-} jsunittest;
-
+#pragma pack(push,1)
+struct EmptyObject {
+ EmptyObject() { len = 5; jstype = EOO; }
+ int len;
+ char jstype;
+} emptyObject;
#pragma pack(pop)
-struct RXTest {
- RXTest() {
- /*
- static const boost::regex e("(\\d{4}[- ]){3}\\d{4}");
- static const boost::regex b(".....");
- cout << "regex result: " << regex_match("hello", e) << endl;
- cout << "regex result: " << regex_match("abcoo", b) << endl;
- */
- pcrecpp::RE re1(")({a}h.*o");
- pcrecpp::RE re("h.llo");
- assert( re.FullMatch("hello") );
- assert( !re1.FullMatch("hello") );
-
-
- pcrecpp::RE_Options options;
- options.set_utf8(true);
- pcrecpp::RE part("dwi", options);
- assert( part.PartialMatch("dwight") );
- }
-} rxtest;
+JSObj emptyObj((char *) &emptyObject);
+
diff --git a/db/makefile b/db/makefile
index b301dd3bd13..89c6a590197 100644
--- a/db/makefile
+++ b/db/makefile
@@ -5,11 +5,14 @@ FLAGS= ${CFLAGS} -fPIC -ggdb -pthread -O3 -I .. -Isrc/p -I/src/p/db -L/usr/loca
LIB_DEPS = -lpcrecpp -lpcre
LIB_BOOST = -lboost_thread -lboost_filesystem
-LIBS= $(LIB_DEPS) $(LIB_BOOST) -lstdc++
+LIBS= $(LIB_DEPS) $(LIB_BOOST) -lstdc++
+DBGRID_LIBS = $(LIB_BOOST) -lstdc++
JVM_LIBS = -L/opt/java/lib/
-OBJS=../stdafx.o ../util/sock.o ../grid/message.o ../util/mmap.o pdfile.o query.o jsobj.o introspect.o btree.o clientcursor.o ../util/util.o javajs.o tests.o json.o repl.o dbclient.o btreecursor.o cloner.o namespace.o commands.o
+OBJS=../stdafx.o ../util/sock.o ../grid/message.o ../util/mmap.o pdfile.o query.o jsobj.o introspect.o btree.o clientcursor.o ../util/util.o javajs.o tests.o json.o repl.o dbclient.o btreecursor.o cloner.o namespace.o commands.o matcher.o
+
+DBGRID_OBJS=../stdafx.o ../util/sock.o ../grid/message.o ../util/util.o jsobj.o dbclient.o ../dbgrid/dbgrid.o
GPP = g++
@@ -39,6 +42,9 @@ _REQ_GPP_VER = 4.2
db: $(OBJS) db.o
$(GPP) $(FLAGS) -o $@ $(OBJS) db.o $(LIBS) $(JVM_LIBS)
# -./db quicktest
+
+dbgrid: $(DBGRID_OBJS) ../dbgrid/dbgrid.o
+ $(GPP) $(FLAGS) -o $@ $(DBGRID_OBJS) $(DBGRID_LIBS)
javatest: javatest.o $(OBJS)
$(GPP) $(FLAGS) -o $@ javatest.o $(OBJS) $(LIBS) $(JVM_LIBS)
diff --git a/db/matcher.cpp b/db/matcher.cpp
new file mode 100644
index 00000000000..24c5fdf8ab8
--- /dev/null
+++ b/db/matcher.cpp
@@ -0,0 +1,484 @@
+// matcher.cpp
+
+/* JSMatcher is our boolean expression evaluator for "where" clauses */
+
+/**
+* Copyright (C) 2008 10gen Inc.
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License, version 3,
+* as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "stdafx.h"
+#include "jsobj.h"
+#include "../util/goodies.h"
+#include "javajs.h"
+#include "../util/unittest.h"
+
+#if defined(_WIN32)
+
+#include <hash_map>
+using namespace stdext;
+
+typedef const char * MyStr;
+struct less_str {
+ bool operator()(const MyStr & x, const MyStr & y) const {
+ if ( strcmp(x, y) > 0)
+ return true;
+
+ return false;
+ }
+};
+
+typedef hash_map<const char*, int, hash_compare<const char *, less_str> > strhashmap;
+
+#else
+
+#include <ext/hash_map>
+using namespace __gnu_cxx;
+
+typedef const char * MyStr;
+struct eq_str {
+ bool operator()(const MyStr & x, const MyStr & y) const {
+ if ( strcmp(x, y) == 0)
+ return true;
+
+ return false;
+ }
+};
+
+typedef hash_map<const char*, int, hash<const char *>, eq_str > strhashmap;
+
+#endif
+
+#include "minilex.h"
+
+MiniLex minilex;
+
+class Where {
+public:
+ Where() { codeCopy = 0; }
+ ~Where() {
+ JavaJS->scopeFree(scope);
+ delete codeCopy;
+ scope = 0; func = 0; codeCopy = 0;
+ }
+ jlong scope, func;
+ strhashmap fields;
+// map<string,int> fields;
+ bool fullObject;
+ int nFields;
+ char *codeCopy;
+
+ void setFunc(const char *code) {
+ codeCopy = new char[strlen(code)+1];
+ strcpy(codeCopy,code);
+ func = JavaJS->functionCreate( code );
+ minilex.grabVariables(codeCopy, fields);
+ // if user references db, eg db.foo.save(obj),
+ // we make sure we have the whole thing.
+ fullObject = fields.count("fullObject") +
+ fields.count("db") > 0;
+ nFields = fields.size();
+ }
+
+ void buildSubset(JSObj& src, JSObjBuilder& dst) {
+ JSElemIter it(src);
+ int n = 0;
+ if( !it.more() ) return;
+ while( 1 ) {
+ Element e = it.next();
+ if( e.eoo() )
+ break;
+ if( //n == 0 &&
+ fields.find(e.fieldName()) != fields.end()
+ //fields.count(e.fieldName())
+ ) {
+ dst.append(e);
+ if( ++n >= nFields )
+ break;
+ }
+ }
+ }
+};
+
+JSMatcher::~JSMatcher() {
+ for( int i = 0; i < nBuilders; i++ )
+ delete builders[i];
+ delete in;
+ delete where;
+}
+
+#include "pdfile.h"
+
+JSMatcher::JSMatcher(JSObj &_jsobj) :
+ in(0), where(0), jsobj(_jsobj), nRegex(0)
+{
+ nBuilders = 0;
+
+ JSElemIter i(jsobj);
+ n = 0;
+ while( i.more() ) {
+ Element e = i.next();
+ if( e.eoo() )
+ break;
+
+ if( e.type() == Code && strcmp(e.fieldName(), "$where")==0 ) {
+ // $where: function()...
+ assert( where == 0 );
+ where = new Where();
+ const char *code = e.valuestr();
+ massert( "$where query, but jni is disabled", JavaJS );
+ where->scope = JavaJS->scopeCreate();
+ JavaJS->scopeSetString(where->scope, "$client", client->name.c_str());
+ where->setFunc(code);
+ continue;
+ }
+
+ if( e.type() == RegEx ) {
+ if( nRegex >= 4 ) {
+ cout << "ERROR: too many regexes in query" << endl;
+ }
+ else {
+ pcrecpp::RE_Options options;
+ options.set_utf8(true);
+ const char *flags = e.regexFlags();
+ while( flags && *flags ) {
+ if( *flags == 'i' )
+ options.set_caseless(true);
+ else if( *flags == 'm' )
+ options.set_multiline(true);
+ else if( *flags == 'x' )
+ options.set_extended(true);
+ flags++;
+ }
+ regexs[nRegex].re = new pcrecpp::RE(e.regex(), options);
+ regexs[nRegex].fieldName = e.fieldName();
+ nRegex++;
+ }
+ continue;
+ }
+
+ // greater than / less than...
+ // e.g., e == { a : { $gt : 3 } }
+ // or
+ // { a : { $in : [1,2,3] } }
+ if( e.type() == Object ) {
+ // e.g., fe == { $gt : 3 }
+ JSElemIter j(e.embeddedObject());
+ bool ok = false;
+ while( j.more() ) {
+ Element fe = j.next();
+ if( fe.eoo() )
+ break;
+ // Element fe = e.embeddedObject().firstElement();
+ const char *fn = fe.fieldName();
+ if( fn[0] == '$' && fn[1] ) {
+ if( fn[2] == 't' ) {
+ int op = Equality;
+ if( fn[1] == 'g' ) {
+ if( fn[3] == 0 ) op = GT;
+ else if( fn[3] == 'e' && fn[4] == 0 ) op = GTE;
+ }
+ else if( fn[1] == 'l' ) {
+ if( fn[3] == 0 ) op = LT;
+ else if( fn[3] == 'e' && fn[4] == 0 ) op = LTE;
+ }
+ if( op && nBuilders < 8) {
+ JSObjBuilder *b = new JSObjBuilder();
+ builders[nBuilders++] = b;
+ b->appendAs(fe, e.fieldName());
+ toMatch.push_back( b->done().firstElement() );
+ compareOp.push_back(op);
+ n++;
+ ok = true;
+ }
+ }
+ else if( fn[1] == 'i' && fn[2] == 'n' && fn[3] == 0 && fe.type() == Array ) {
+ // $in
+ assert( in == 0 ); // only one per query supported so far. finish...
+ in = new set<Element,element_lt>();
+ JSElemIter i(fe.embeddedObject());
+ if( i.more() ) {
+ while( 1 ) {
+ Element ie = i.next();
+ if( ie.eoo() )
+ break;
+ in->insert(ie);
+ }
+ }
+ toMatch.push_back(e); // not actually used at the moment
+ compareOp.push_back(opIN);
+ n++;
+ ok = true;
+ }
+ }
+ else {
+ ok = false;
+ break;
+ }
+ }
+ if( ok )
+ continue;
+ }
+
+ {
+ // normal, simple case e.g. { a : "foo" }
+ toMatch.push_back(e);
+ compareOp.push_back(Equality);
+ n++;
+ }
+ }
+}
+
+inline int JSMatcher::valuesMatch(Element& l, Element& r, int op) {
+ if( op == 0 )
+ return l.valuesEqual(r);
+
+ if( op == opIN ) {
+ // { $in : [1,2,3] }
+ int c = in->count(l);
+ return c;
+ }
+
+ /* check LT, GTE, ... */
+ if( l.type() != r.type() )
+ return false;
+ int c = compareElementValues(l, r);
+ int z = 1 << (c+1);
+ return (op & z);
+}
+
+/* Check if a particular field matches.
+
+ fieldName - field to match "a.b" if we are reaching into an embedded object.
+ toMatch - element we want to match.
+ obj - database object to check against
+ compareOp - Equality, LT, GT, etc.
+ deep - out param. set to true/false if we scanned an array
+ isArr -
+
+ Special forms:
+
+ { "a.b" : 3 } means obj.a.b == 3
+ { a : { $lt : 3 } } means obj.a < 3
+ { a : { $in : [1,2] } } means [1,2].contains(obj.a)
+
+ return value
+ -1 mismatch
+ 0 missing element
+ 1 match
+*/
+int JSMatcher::matchesDotted(const char *fieldName, Element& toMatch, JSObj& obj, int compareOp, bool *deep, bool isArr) {
+ {
+ const char *p = strchr(fieldName, '.');
+ if( p ) {
+ string left(fieldName, p-fieldName);
+
+ Element e = obj.getField(left.c_str());
+ if( e.eoo() )
+ return 0;
+ if( e.type() != Object && e.type() != Array )
+ return -1;
+
+ JSObj eo = e.embeddedObject();
+ return matchesDotted(p+1, toMatch, eo, compareOp, deep, e.type() == Array);
+ }
+ }
+
+ Element e = obj.getField(fieldName);
+
+ if( valuesMatch(e, toMatch, compareOp) ) {
+ return 1;
+ }
+ else if( e.type() == Array ) {
+ JSElemIter ai(e.embeddedObject());
+ while( ai.more() ) {
+ Element z = ai.next();
+ if( valuesMatch( z, toMatch, compareOp) ) {
+ if( deep )
+ *deep = true;
+ return 1;
+ }
+ }
+ }
+ else if( isArr ) {
+ JSElemIter ai(obj);
+ while( ai.more() ) {
+ Element z = ai.next();
+ if( z.type() == Object ) {
+ JSObj eo = z.embeddedObject();
+ int cmp = matchesDotted(fieldName, toMatch, eo, compareOp, deep);
+ if( cmp > 0 ) {
+ if( deep ) *deep = true;
+ return 1;
+ }
+ }
+ }
+ }
+ else if( e.eoo() ) {
+ return 0;
+ }
+ return -1;
+}
+
+extern int dump;
+
+inline bool _regexMatches(RegexMatcher& rm, Element& e) {
+ char buf[64];
+ const char *p = buf;
+ if( e.type() == String || e.type() == Symbol )
+ p = e.valuestr();
+ else if( e.type() == Number ) {
+ sprintf(buf, "%f", e.number());
+ }
+ else if( e.type() == Date ) {
+ unsigned long long d = e.date();
+ time_t t = (d/1000);
+ time_t_to_String(t, buf);
+ }
+ else
+ return false;
+ return rm.re->PartialMatch(p);
+}
+/* todo: internal dotted notation scans -- not done yet here. */
+inline bool regexMatches(RegexMatcher& rm, Element& e, bool *deep) {
+ if( e.type() != Array )
+ return _regexMatches(rm, e);
+
+ JSElemIter ai(e.embeddedObject());
+ while( ai.more() ) {
+ Element z = ai.next();
+ if( _regexMatches(rm, z) ) {
+ if( deep )
+ *deep = true;
+ return true;
+ }
+ }
+ return false;
+}
+
+/* See if an object matches the query.
+ deep - return true when meanswe looked into arrays for a match
+*/
+bool JSMatcher::matches(JSObj& jsobj, bool *deep) {
+ if( deep )
+ *deep = false;
+
+ /* assuming there is usually only one thing to match. if more this
+ could be slow sometimes. */
+
+ // check normal non-regex cases:
+ for( int i = 0; i < n; i++ ) {
+ Element& m = toMatch[i];
+ int cmp = matchesDotted(toMatch[i].fieldName(), toMatch[i], jsobj, compareOp[i], deep);
+
+ /* missing is ok iff we were looking for null */
+ if( cmp < 0 )
+ return false;
+ if( cmp == 0 && (m.type() != jstNULL && m.type() != Undefined ) )
+ return false;
+ }
+
+ for( int r = 0; r < nRegex; r++ ) {
+ RegexMatcher& rm = regexs[r];
+ Element e = jsobj.getFieldDotted(rm.fieldName);
+ if( e.eoo() )
+ return false;
+ if( !regexMatches(rm, e, deep) )
+ return false;
+ }
+
+ if( where ) {
+ if( where->func == 0 )
+ return false; // didn't compile
+ if( jsobj.objsize() < 200 || where->fullObject ) {
+ JavaJS->scopeSetObject(where->scope, "obj", &jsobj);
+ } else {
+ JSObjBuilder b;
+ where->buildSubset(jsobj, b);
+ JSObj temp = b.done();
+ JavaJS->scopeSetObject(where->scope, "obj", &temp);
+ }
+ if( JavaJS->invoke(where->scope, where->func) )
+ return false;
+ return JavaJS->scopeGetBoolean(where->scope, "return") != 0;
+ }
+
+ return true;
+}
+
+struct JSObj1 js1;
+
+#pragma pack(push,1)
+struct JSObj2 {
+ JSObj2() {
+ totsize=sizeof(JSObj2);
+ s = String; strcpy_s(sname, 7, "abcdef"); slen = 10;
+ strcpy_s(sval, 10, "123456789"); eoo = EOO;
+ }
+ unsigned totsize;
+ char s;
+ char sname[7];
+ unsigned slen;
+ char sval[10];
+ char eoo;
+} js2;
+
+struct JSUnitTest : public UnitTest {
+ void run() {
+
+ JSObj j1((const char *) &js1);
+ JSObj j2((const char *) &js2);
+ cout << "j1:" << j1.toString() << endl;
+ cout << "j2:" << j2.toString() << endl;
+ JSMatcher m(j2);
+ assert( m.matches(j1) );
+ js2.sval[0] = 'z';
+ assert( !m.matches(j1) );
+ JSMatcher n(j1);
+ assert( n.matches(j1) );
+ cout << "j2:" << j2.toString() << endl;
+ assert( !n.matches(j2) );
+
+cout << "temp1" << endl;
+ JSObj j0 = emptyObj;
+// JSObj j0((const char *) &js0);
+ JSMatcher p(j0);
+ assert( p.matches(j1) );
+ assert( p.matches(j2) );
+ }
+} jsunittest;
+
+#pragma pack(pop)
+
+struct RXTest : public UnitTest {
+ void run() {
+ /*
+ static const boost::regex e("(\\d{4}[- ]){3}\\d{4}");
+ static const boost::regex b(".....");
+ cout << "regex result: " << regex_match("hello", e) << endl;
+ cout << "regex result: " << regex_match("abcoo", b) << endl;
+ */
+ pcrecpp::RE re1(")({a}h.*o");
+ pcrecpp::RE re("h.llo");
+ assert( re.FullMatch("hello") );
+ assert( !re1.FullMatch("hello") );
+
+
+ pcrecpp::RE_Options options;
+ options.set_utf8(true);
+ pcrecpp::RE part("dwi", options);
+ assert( part.PartialMatch("dwight") );
+ }
+} rxtest;
+
diff --git a/db/query.cpp b/db/query.cpp
index 286142e9ded..7f5a575dc3c 100644
--- a/db/query.cpp
+++ b/db/query.cpp
@@ -41,16 +41,6 @@ LRUishMap<JSObj,DiskLoc,5> lrutest(123);
int nextCursorId = 1;
extern bool useCursors;
-#pragma pack(push,1)
-struct EmptyObject {
- EmptyObject() { len = 5; jstype = EOO; }
- int len;
- char jstype;
-} emptyObject;
-#pragma pack(pop)
-
-JSObj emptyObj((char *) &emptyObject);
-
int getGtLtOp(Element& e);
void appendElementHandlingGtLt(JSObjBuilder& b, Element& e);
diff --git a/stdafx.cpp b/stdafx.cpp
index 56ea439d52e..b050390966f 100644
--- a/stdafx.cpp
+++ b/stdafx.cpp
@@ -23,18 +23,20 @@
// TODO: reference any additional headers you need in STDAFX.H
// and not in this file
+/*
struct MyAsserts {
MyAsserts() {
}
} myassertsstdafx;
+*/
#undef assert
#undef yassert
#include "assert.h"
-void sayDbContext(const char *p = 0);
+void sayDbContext(const char *errmsg = 0);
void wasserted(const char *msg, const char *file, unsigned line) {
problem() << "Assertion failure " << msg << ' ' << file << ' ' << line << endl;
@@ -55,3 +57,4 @@ void msgasserted(const char *msg) {
cout << "Assertion: " << msg << '\n';
throw AssertionException();
}
+
diff --git a/util/mmap.cpp b/util/mmap.cpp
index a3296cd2412..e67fe4560b0 100644
--- a/util/mmap.cpp
+++ b/util/mmap.cpp
@@ -36,7 +36,7 @@ void MemoryMappedFile::closeAllFiles() {
++closingAllFiles;
for( set<MemoryMappedFile*>::iterator i = mmfiles.begin(); i != mmfiles.end(); i++ )
(*i)->close();
- cout << " closeAllFiles() finished" << endl;
+ log() << " closeAllFiles() finished" << endl;
--closingAllFiles;
}
diff --git a/util/unittest.h b/util/unittest.h
new file mode 100644
index 00000000000..db80517c463
--- /dev/null
+++ b/util/unittest.h
@@ -0,0 +1,40 @@
+// unittest.h
+
+/**
+* Copyright (C) 2008 10gen Inc.
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License, version 3,
+* as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+/* the idea here is to let all initialization of global variables (classes inheriting from UnitTest)
+ complete before we run the tests -- otherwise order of initilization being arbitrary may mess
+ us up. The app's main() function should call runTests.
+*/
+struct UnitTest {
+ UnitTest() { registerTest(this); }
+
+ // assert if fails
+ virtual void run() = 0;
+
+ static vector<UnitTest*> tests;
+
+ static void registerTest(UnitTest *t) { tests.push_back(t); }
+
+ static void runTests() {
+ for( vector<UnitTest*>::iterator i = tests.begin(); i != tests.end(); i++ ) {
+ }
+ }
+};
+
diff --git a/util/util.cpp b/util/util.cpp
index c78767f3a69..0879a27b50b 100644
--- a/util/util.cpp
+++ b/util/util.cpp
@@ -16,6 +16,9 @@
#include "stdafx.h"
#include "goodies.h"
+#include "unittest.h"
+
+vector<UnitTest*> UnitTest::tests;
Nullstream nullstream;
Logstream logstream;
@@ -43,8 +46,8 @@ int nextPrime(int n) {
return n;
}
-struct UtilTest {
- UtilTest() {
+struct UtilTest : public UnitTest {
+ void run() {
assert( WrappingInt(0) <= WrappingInt(0) );
assert( WrappingInt(0) <= WrappingInt(1) );
assert( !(WrappingInt(1) <= WrappingInt(0)) );