From b6c72bffe0518725abb8a7d12c0a66606dfd8a51 Mon Sep 17 00:00:00 2001 From: "terry%mozilla.org" Date: Thu, 22 Jun 2000 23:11:37 +0000 Subject: [PATCH] TripleDB has been completely rewritten. Now requires Sleepycat Berkeley DB. --- db/tripledb/README | 6 +- db/tripledb/src/add.c | 0 db/tripledb/src/bg.c | 263 +++++ db/tripledb/src/callback.c | 0 db/tripledb/src/cursor.c | 417 +++++++ db/tripledb/src/cursor.h | 63 + db/tripledb/src/dbnspr.c | 246 ++++ db/tripledb/src/dbnspr.h | 28 + db/tripledb/src/debug.c | 337 ++---- db/tripledb/src/intern.c | 237 ++++ db/tripledb/src/intern.h | 48 + db/tripledb/src/node.c | 223 ++-- db/tripledb/src/node.h | 39 + db/tripledb/src/query.c | 0 db/tripledb/src/record.c | 0 db/tripledb/src/rtype.c | 431 +++++++ db/tripledb/src/rtype.h | 111 ++ db/tripledb/src/tdb.c | 1777 +++++++++++++++++++++++------ db/tripledb/src/tdb.h | 117 ++ db/tripledb/src/tdbapi.h | 372 ++++-- db/tripledb/src/tdbbg.h | 72 ++ db/tripledb/src/tdbdebug.h | 14 +- db/tripledb/src/tdbexperimental.h | 61 + db/tripledb/src/tdbimpl.h | 94 ++ db/tripledb/src/tdbtypes.h | 275 ++--- db/tripledb/src/util.c | 237 ++++ db/tripledb/src/util.h | 50 + db/tripledb/src/vector.c | 360 ++++++ db/tripledb/src/vector.h | 61 + db/tripledb/src/windex.c | 402 +++++++ db/tripledb/src/windex.h | 44 + 31 files changed, 5426 insertions(+), 959 deletions(-) delete mode 100644 db/tripledb/src/add.c create mode 100644 db/tripledb/src/bg.c delete mode 100644 db/tripledb/src/callback.c create mode 100644 db/tripledb/src/cursor.c create mode 100644 db/tripledb/src/cursor.h create mode 100644 db/tripledb/src/dbnspr.c create mode 100644 db/tripledb/src/dbnspr.h create mode 100644 db/tripledb/src/intern.c create mode 100644 db/tripledb/src/intern.h create mode 100644 db/tripledb/src/node.h delete mode 100644 db/tripledb/src/query.c delete mode 100644 db/tripledb/src/record.c create mode 100644 db/tripledb/src/rtype.c create mode 100644 db/tripledb/src/rtype.h create mode 100644 db/tripledb/src/tdb.h create mode 100644 db/tripledb/src/tdbbg.h create mode 100644 db/tripledb/src/tdbexperimental.h create mode 100644 db/tripledb/src/tdbimpl.h create mode 100644 db/tripledb/src/util.c create mode 100644 db/tripledb/src/util.h create mode 100644 db/tripledb/src/vector.c create mode 100644 db/tripledb/src/vector.h create mode 100644 db/tripledb/src/windex.c create mode 100644 db/tripledb/src/windex.h diff --git a/db/tripledb/README b/db/tripledb/README index bfdd153c5d1..50e146556b5 100644 --- a/db/tripledb/README +++ b/db/tripledb/README @@ -18,6 +18,10 @@ database. Each member of a triple can be a string, integer, or date. For more details, read the API, in tdbapi.h. +Note that TripleDB now depends on the Sleepycat Berkeley DB, version +3.1.11 or greater. For more information on that, see +http://www.sleepycat.com. + FAQ: @@ -26,7 +30,7 @@ A: Yeah, yeah, I know. This is developed as part of a project at work, and our Makefiles are all messed up. If you have one that fits in well with other stuff at mozilla.org, please feel free to contribute it. -Q: Um, so else here uses this? +Q: Um, so nobody else here uses this? A: That's right. But this maps in pretty well to RDF, I believe. I wouldn't be surprised if someone decides to use it to store a biggish RDF database, and adds it to mozilla for that purpose. diff --git a/db/tripledb/src/add.c b/db/tripledb/src/add.c deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/db/tripledb/src/bg.c b/db/tripledb/src/bg.c new file mode 100644 index 00000000000..9afe5274170 --- /dev/null +++ b/db/tripledb/src/bg.c @@ -0,0 +1,263 @@ +/* -*- Mode: C; indent-tabs-mode: nil; -*- + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the TripleDB database library. + * + * The Initial Developer of the Original Code is Geocast Network Systems. + * Portions created by Geocast are + * Copyright (C) 2000 Geocast Network Systems. All + * Rights Reserved. + * + * Contributor(s): Terry Weissman + */ + +#include "tdbapi.h" +#include "tdbbg.h" + +#ifdef TDB_USE_NSPR + +#include "tdbtypes.h" + +#include "prtypes.h" + +#include "prcvar.h" +#include "prlock.h" +#include "prmem.h" +#include "prinrval.h" +#include "prthread.h" + +#include "plstr.h" + + + +typedef struct _TDBBGFunc { + struct _TDBBGFunc* next; + char* name; + TDBBG_Function func; + void* closure; + TDBInt32 priority; + PRIntervalTime when; +} TDBBGFunc; + +struct _TDBBG { + PRThread* bgthread; /* Background, low-priority thread. */ + PRLock* mutex; + PRCondVar* cvar; /* Condition variable to wake up the + background thread. */ + TDBBool killbgthread; /* If TRUE, then we are waiting for the + background thread to die. */ + TDBBool bgthreadidle; + TDBBGFunc* firstfunc; +}; + + +static void +backgroundThread(void* closure) +{ + TDBBG* bg = (TDBBG*) closure; + TDBBGFunc* func; + TDBBGFunc** ptr; + TDBBGFunc** which; + PRIntervalTime now; + PRIntervalTime delay; + PR_Lock(bg->mutex); + while (1) { + if (bg->killbgthread) break; + if (bg->firstfunc) { + now = PR_IntervalNow(); + delay = (bg->firstfunc->when > now) ? + (bg->firstfunc->when - now) : 0; + } else { + delay = PR_INTERVAL_NO_TIMEOUT; + } + if (delay > 0) { + bg->bgthreadidle = TDB_TRUE; + PR_NotifyAllCondVar(bg->cvar); + PR_WaitCondVar(bg->cvar, delay); + bg->bgthreadidle = TDB_FALSE; + } + if (bg->killbgthread) break; + now = PR_IntervalNow(); + if (bg->firstfunc == NULL || bg->firstfunc->when > now) continue; + /* Search through anything whose time has come, and find the one with + the highest priority. */ + which = &(bg->firstfunc); + for (ptr = which ; *ptr ; ptr = &((*ptr)->next)) { + if ((*ptr)->when > now) break; + if ((*ptr)->priority > (*which)->priority) { + which = ptr; + } + } + func = *which; + *which = func->next; + PR_Unlock(bg->mutex); + (*func->func)(func->closure); + if (func->name) PR_Free(func->name); + PR_Free(func); + PR_Lock(bg->mutex); + } + PR_Unlock(bg->mutex); +} + + +TDBBG* +TDBBG_Open() +{ + TDBBG* bg; + bg = PR_NEWZAP(TDBBG); + if (!bg) return NULL; + + bg->mutex = PR_NewLock(); + if (!bg->mutex) goto FAIL; + + bg->cvar = PR_NewCondVar(bg->mutex); + if (!bg->cvar) goto FAIL; + + bg->bgthread = + PR_CreateThread(PR_SYSTEM_THREAD, backgroundThread, bg, + PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, + PR_JOINABLE_THREAD, 0); + if (bg->bgthread == NULL) goto FAIL; + PR_Lock(bg->mutex); + while (!bg->bgthreadidle) { + PR_WaitCondVar(bg->cvar, PR_INTERVAL_NO_TIMEOUT); + } + PR_Unlock(bg->mutex); + + return bg; + FAIL: + TDBBG_Close(bg); + return NULL; +} + + +TDBStatus +TDBBG_Close(TDBBG* bg) +{ + TDBBGFunc* func; + tdb_ASSERT(bg != NULL); + if (!bg) return TDB_FAILURE; + if (bg->bgthread) { + PR_Lock(bg->mutex); + bg->killbgthread = TDB_TRUE; + PR_NotifyAllCondVar(bg->cvar); + PR_Unlock(bg->mutex); + PR_JoinThread(bg->bgthread); + } + if (bg->cvar) PR_DestroyCondVar(bg->cvar); + if (bg->mutex) PR_DestroyLock(bg->mutex); + while (bg->firstfunc) { + func = bg->firstfunc; + bg->firstfunc = func->next; + PR_Free(func->name); + PR_Free(func); + } + PR_Free(bg); + return TDB_SUCCESS; +} + + +TDBStatus +TDBBG_AddFunction(TDBBG* bg, const char* name, + TDBInt32 secondsToWait, TDBInt32 priority, + TDBBG_Function f, void* closure) +{ + TDBBGFunc** ptr; + TDBBGFunc* func; + if (bg == NULL|| name == NULL || f == NULL) { + tdb_ASSERT(0); + return PR_FAILURE; + } + func = PR_NEWZAP(TDBBGFunc); + if (!func) return PR_FAILURE; + func->name = PL_strdup(name); + if (!func->name) { + PR_Free(func->name); + PR_Free(func); + return PR_FAILURE; + } + func->func = f; + func->closure = closure; + func->priority = priority; + func->when = PR_IntervalNow() + PR_SecondsToInterval(secondsToWait); + PR_Lock(bg->mutex); + for (ptr = &(bg->firstfunc) ; *ptr ; ptr = &((*ptr)->next)) { + if (func->when <= (*ptr)->when) break; + } + func->next = *ptr; + *ptr = func; + if (bg->firstfunc == func) { + /* We have a new head function, which probably means that the + background thread has to sleep less long than it was before, + so wake it up. */ + PR_NotifyAllCondVar(bg->cvar); + } + PR_Unlock(bg->mutex); + return PR_SUCCESS; +} + + +PRStatus +TDBBG_RescheduleFunction(TDBBG* bg, const char* name, + PRInt32 secondsToWait, + PRInt32 priority, + TDBBG_Function func, void* closure) +{ + TDBBG_RemoveFunction(bg, name, func, closure); + return TDBBG_AddFunction(bg, name, secondsToWait, priority, func, closure); +} + + +PRStatus +TDBBG_RemoveFunction(TDBBG* bg, const char* name, TDBBG_Function f, void* closure) +{ + PRStatus status = PR_FAILURE; + TDBBGFunc** ptr; + TDBBGFunc* func; + if (bg == NULL|| name == NULL || f == NULL) { + tdb_ASSERT(0); + return PR_FAILURE; + } + PR_Lock(bg->mutex); + ptr = &(bg->firstfunc); + while (*ptr) { + if ((*ptr)->func == f && (*ptr)->closure == closure && + strcmp((*ptr)->name, name) == 0) { + func = *ptr; + *ptr = func->next; + PR_Free(func->name); + PR_Free(func); + status = PR_SUCCESS; + } else { + ptr = &((*ptr)->next); + } + } + PR_Unlock(bg->mutex); + return status; +} + + +PRStatus +TDBBG_WaitUntilIdle(TDBBG* bg) +{ + if (bg == NULL) { + tdb_ASSERT(0); + return PR_FAILURE; + } + PR_Lock(bg->mutex); + while (!bg->bgthreadidle) { + PR_WaitCondVar(bg->cvar, PR_INTERVAL_NO_TIMEOUT); + } + PR_Unlock(bg->mutex); + return PR_SUCCESS; +} +#endif /* TDB_USE_NSPR */ diff --git a/db/tripledb/src/callback.c b/db/tripledb/src/callback.c deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/db/tripledb/src/cursor.c b/db/tripledb/src/cursor.c new file mode 100644 index 00000000000..10ec60b5320 --- /dev/null +++ b/db/tripledb/src/cursor.c @@ -0,0 +1,417 @@ +/* -*- Mode: C; indent-tabs-mode: nil; -*- + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the TripleDB database library. + * + * The Initial Developer of the Original Code is Geocast Network Systems. + * Portions created by Geocast are + * Copyright (C) 2000 Geocast Network Systems. All + * Rights Reserved. + * + * Contributor(s): Terry Weissman + */ + +#include "tdbtypes.h" + +#include "cursor.h" +#include "intern.h" +#include "node.h" +#include "rtype.h" +#include "tdb.h" +#include "vector.h" +#include "windex.h" + +struct _TDBCursor { + TDB* tdb; + TDBBase* base; + void* implcursor; + TDBCursor* next; + TDBNodePtr* range; + TDBVector* vector; /* The last vector returned from this cursor */ + TDBVector* curvector; /* The current position being considered by + the cursor. */ + TDBVector* candidate; /* The next candidate to be returned as a + match. */ + TDBWindexCursor* wcursor; + DBC* dbcurs; + TDBInt32 curentry; + TDBRType* indextype; + TDBInt32 numfields; /* Cached from indextype. */ + TDBBool reverse; + TDBBool includefalse; + TDBBool alldone; + TDBTriple triple; + TDBInt32 hits; + TDBInt32 misses; +}; + + +TDBCursor* +tdbCursorNew(TDB* tdb, TDBRType* indextype, TDBNodePtr* range, + TDBBool includefalse, TDBBool reverse) +{ + TDBInt32 i; + TDBCursor* cursor; + if (indextype == NULL || range == NULL) { + tdb_ASSERT(0); + return NULL; + } + cursor = tdb_NEWZAP(TDBCursor); + if (!cursor) return NULL; + cursor->tdb = tdb; + cursor->base = tdbGetBase(tdb); + cursor->numfields = tdbRTypeGetNumFields(indextype); + cursor->range = tdb_Calloc(cursor->numfields, sizeof(TDBNodePtr)); + if (!cursor->range) { + tdb_Free(cursor); + return NULL; + } + for (i=0 ; inumfields ; i++) { + if (range[i]) { + if (i == 1 && range[i]->type != TDBTYPE_INTERNED) { + cursor->range[i] = tdbIntern(cursor->base, range[i]); + } else { + cursor->range[i] = TDBNodeDup(range[i]); + } + if (!cursor->range[i]) { + tdbCursorFree(cursor); + return NULL; + } + } + } + cursor->indextype = indextype; + cursor->includefalse = includefalse; + cursor->reverse = reverse; + cursor->next = tdbGetFirstCursor(cursor->tdb); + tdbSetFirstCursor(cursor->tdb, cursor); + return cursor; +} + + +TDBCursor* +tdbCursorNewWordSubstring(TDB* tdb, const char* string) +{ + TDBWindex* windex; + TDBCursor* cursor; + TDBBase* base; + base = tdbGetBase(tdb); + windex = tdbGetWindex(base); + cursor = tdb_NEWZAP(TDBCursor); + if (!cursor) return NULL; + cursor->base = base; + cursor->tdb = tdb; + cursor->wcursor = tdbWindexGetCursor(windex, string); + if (!cursor->wcursor) { + tdb_Free(cursor); + return NULL; + } + return cursor; +} + + +TDBCursor* +tdbCursorNewImpl(TDB* tdb, void* implcursor) +{ + TDBCursor* cursor; + if (tdb == NULL || implcursor == NULL) { + tdb_ASSERT(0); + return NULL; + } + cursor = tdb_NEWZAP(TDBCursor); + if (!cursor) return NULL; + cursor->tdb = tdb; + cursor->base = tdbGetBase(tdb); + cursor->implcursor = implcursor; + return cursor; +} + +static void +freeCurVector(TDBCursor* cursor) +{ + if (cursor->curvector) { + if (cursor->curvector != cursor->candidate && + cursor->curvector != cursor->vector) { + tdbVectorFree(cursor->curvector); + } + cursor->curvector = NULL; + } +} + + +void +tdbCursorFree(TDBCursor* cursor) +{ + TDBInt32 i; + TDBCursor* first; + if (cursor == NULL) { + tdb_ASSERT(0); + return; + } + if (cursor->wcursor) tdbWindexCursorFree(cursor->wcursor); + freeCurVector(cursor); + if (cursor->vector) tdbVectorFree(cursor->vector); + if (cursor->candidate) tdbVectorFree(cursor->candidate); + if (cursor->range) { + for (i=0 ; inumfields ; i++) { + if (cursor->range[i]) TDBFreeNode(cursor->range[i]); + } + tdb_Free(cursor->range); + } + if (cursor->dbcurs) cursor->dbcurs->c_close(cursor->dbcurs); + first = tdbGetFirstCursor(cursor->tdb); + if (first == cursor) { + tdbSetFirstCursor(cursor->tdb, first->next); + } else { + for (; first ; first = first->next) { + if (first->next == cursor) { + first->next = cursor->next; + break; + } + } + } + tdb_Free(cursor); +} + + +TDB* +tdbCursorGetTDB(TDBCursor* cursor) +{ + if (cursor == NULL) { + tdb_ASSERT(0); + return NULL; + } + return cursor->tdb; +} + + +TDBBase* +tdbCursorGetBase(TDBCursor* cursor) +{ + if (cursor == NULL) { + tdb_ASSERT(0); + return NULL; + } + return cursor->base; +} + + + +void* +tdbCursorGetImplcursor(TDBCursor* cursor) +{ + if (cursor == NULL) { + tdb_ASSERT(0); + return NULL; + } + return cursor->implcursor; +} + + +static TDBVector* +getNextCandidate(TDBCursor* cursor) +{ + TDBInt32 i; + TDBNode minmaxnode; + TDBNodePtr nodes[3]; + TDBBool bumpcursor = TDB_FALSE; + TDBVector* vector; + TDBBool inrange; + + if (cursor->curvector == NULL) { + /* First time. */ + for (i=0 ; inumfields ; i++) { + if (cursor->range[i]) { + nodes[i] = cursor->range[i]; + } else { + minmaxnode.type = + cursor->reverse ? TDBTYPE_MAXNODE : TDBTYPE_MINNODE; + nodes[i] = &minmaxnode; + + /* The below should be restored if we ever restore the rule + that the first member of a triple must be an ID. */ +/* if (i == 0) { */ +/* minmaxid.type = TDBTYPE_ID; */ +/* minmaxid.d.i = cursor->reverse ? 0xffffffffffffffff : 0; */ +/* nodes[i] = &minmaxid; */ +/* } */ + + } + } + cursor->curvector = tdbVectorNewFromNodes(cursor->base, nodes, 0, 0, 0); + if (!cursor->curvector) return NULL; + } + while (1) { + vector = NULL; + if (cursor->dbcurs == NULL) { + cursor->dbcurs = tdbRTypeGetCursor(cursor->indextype, + cursor->curvector); + if (!cursor->dbcurs) return NULL; + } else { + bumpcursor = TDB_TRUE; + } + vector = tdbRTypeGetCursorValue(cursor->indextype, cursor->dbcurs, + bumpcursor ? + (cursor->reverse ? DB_PREV : + DB_NEXT) : DB_CURRENT); + freeCurVector(cursor); + cursor->curvector = vector; + if (vector == NULL) { + break; + } + inrange = TDB_TRUE; + for (i=0 ; inumfields ; i++) { + if (cursor->range[i] && + TDBCompareNodes(cursor->range[i], + tdbVectorGetInternedNode(vector, i)) != 0) { + inrange = TDB_FALSE; + break; + } + } + if (inrange) { + if (cursor->includefalse || + (tdbVectorGetFlags(cursor->curvector) & TDBFLAG_ISASSERT)) { + return cursor->curvector; + } + } + + if (!inrange) break; + + /* So, this one missed, but there might be more. */ + cursor->misses++; + } + freeCurVector(cursor); + if (cursor->dbcurs) { + cursor->dbcurs->c_close(cursor->dbcurs); + cursor->dbcurs = NULL; + } + return NULL; +} + + + +TDBVector* +tdbCursorGetNext(TDBCursor* cursor) +{ + TDBInt32 i; + TDBVector* next; + TDBInt32 l; + TDBInt32 l1; + TDBInt32 l2; + TDBInt32 numlayers; + if (cursor == NULL) { + tdb_ASSERT(0); + return NULL; + } + if (cursor->wcursor) { + while (1) { + if (cursor->vector) tdbVectorFree(cursor->vector); + cursor->vector = tdbWindexGetCursorValue(cursor->wcursor); + if (!cursor->vector) return NULL; + if (tdbVectorLayerMatches(cursor->vector, cursor->tdb)) { + cursor->hits++; + return cursor->vector; + } + cursor->misses++; + } + } + if (cursor->alldone) return NULL; + + while (1) { + next = getNextCandidate(cursor); + if (next == NULL) { + cursor->alldone = TDB_TRUE; + if (cursor->vector) tdbVectorFree(cursor->vector); + cursor->vector = cursor->candidate; + cursor->candidate = NULL; + if (cursor->vector) cursor->hits++; + return cursor->vector; + } + if (!tdbVectorLayerMatches(next, cursor->tdb)) { + cursor->misses++; + continue; + } + if (cursor->candidate == NULL) { + cursor->candidate = next; + continue; + } + if (!tdbVectorEqual(cursor->candidate, next)) { + if (cursor->vector) tdbVectorFree(cursor->vector); + cursor->vector = cursor->candidate; + cursor->candidate = next; + cursor->hits++; + return cursor->vector; + } + cursor->misses++; + l1 = tdbVectorGetLayer(cursor->candidate); + l2 = tdbVectorGetLayer(next); + if (l1 != l2) { + numlayers = tdbGetNumLayers(cursor->tdb); + for (i=0 ; itdb, i); + if (l == l1) { + break; /* Our existing candidate wins. */ + } + if (l == l2) { + tdbVectorFree(cursor->candidate); + cursor->candidate = next; + next = NULL; + } + } + } + } +} + + +TDBTriple* +tdbCursorGetLastResultAsTriple(TDBCursor* cursor) +{ + TDBInt32 i; + if (cursor == NULL) { + tdb_ASSERT(0); + return NULL; + } + if (cursor->vector == NULL) return NULL; + for (i=0 ; i<3 ; i++) { + cursor->triple.data[i] = + tdbVectorGetNonInternedNode(cursor->vector, i); + } + cursor->triple.asserted = + ((tdbVectorGetFlags(cursor->vector) & TDBFLAG_ISASSERT) != 0); + return &(cursor->triple); +} + +extern void +TDBGetCursorStats(TDBCursor* cursor, + TDBInt32* hits, + TDBInt32* misses) +{ + if (cursor == NULL || hits == NULL || misses == NULL) { + tdb_ASSERT(0); + return; + } + *hits = cursor->hits; + *misses = cursor->misses; +} + + +TDBStatus +tdbCursorInvalidateCache(TDB* tdb) +{ + TDBCursor* cursor; + for (cursor = tdbGetFirstCursor(tdb) ; cursor ; cursor = cursor->next) { + if (cursor->dbcurs) { + cursor->dbcurs->c_close(cursor->dbcurs); + cursor->dbcurs = NULL; + } + } + return TDB_SUCCESS; +} diff --git a/db/tripledb/src/cursor.h b/db/tripledb/src/cursor.h new file mode 100644 index 00000000000..e3f2b5a2e80 --- /dev/null +++ b/db/tripledb/src/cursor.h @@ -0,0 +1,63 @@ +/* -*- Mode: C; indent-tabs-mode: nil; -*- + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the TripleDB database library. + * + * The Initial Developer of the Original Code is Geocast Network Systems. + * Portions created by Geocast are + * Copyright (C) 2000 Geocast Network Systems. All + * Rights Reserved. + * + * Contributor(s): Terry Weissman + */ + +#ifndef _cursor_h_ +#define _cursor_h_ 1 + +extern TDBCursor* tdbCursorNew(TDB* tdb, TDBRType* indextype, + TDBNodePtr* triple, + TDBBool includefalse, TDBBool reverse); + + +extern TDBCursor* tdbCursorNewWordSubstring(TDB* tdb, const char* string); + +extern TDBCursor* tdbCursorNewImpl(TDB* tdb, void* implcursor); + +extern void tdbCursorFree(TDBCursor* cursor); + +extern TDB* tdbCursorGetTDB(TDBCursor* cursor); + +extern TDBBase* tdbCursorGetBase(TDBCursor* cursor); + +extern void* tdbCursorGetImplcursor(TDBCursor* cursor); + +/* tdbCursorGetNext() returns the next matching item for this cursor. The + returned vector is owned by the cursor object, and must not be freed or + modified. */ + +extern TDBVector* tdbCursorGetNext(TDBCursor* cursor); + +/* tdbCursorGetLastResultAsTriple() is a hack that returns the + last result as a TDBTriple*, to help us implement our published API + (which has drifted a bit from our internal implementation.) */ + +extern TDBTriple* tdbCursorGetLastResultAsTriple(TDBCursor* cursor); + + +/* tdbCursorInvalidateCache() causes all cursors to invalidate any + information about the tree that they may have cached. This is called when + a change is going to be made to the tree structure. */ + +extern TDBStatus tdbCursorInvalidateCache(TDB* tdb); + + +#endif /* _cursor_h_ */ diff --git a/db/tripledb/src/dbnspr.c b/db/tripledb/src/dbnspr.c new file mode 100644 index 00000000000..55ae8189d73 --- /dev/null +++ b/db/tripledb/src/dbnspr.c @@ -0,0 +1,246 @@ +/* -*- Mode: C; indent-tabs-mode: nil; -*- + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the TripleDB database library. + * + * The Initial Developer of the Original Code is Geocast Network Systems. + * Portions created by Geocast are + * Copyright (C) 2000 Geocast Network Systems. All + * Rights Reserved. + * + * Contributor(s): Terry Weissman + */ + +/* #include "db/db_int.h" */ +#define MEGABYTE 1048576 + +#include "tdbtypes.h" + +#ifdef TDB_USE_NSPR + +#include +#include +#include +#include + +#include "db/db.h" + +#include "plstr.h" + +#include "prerror.h" +#include "prinrval.h" +#include "prio.h" +#include "prmem.h" +#include "prthread.h" + + +static int +func_close(int fd) +{ + if (fd == 0) return -1; + if (PR_Close((PRFileDesc*) fd) == PR_SUCCESS) return 0; + return PR_GetError(); +} + + +static void +func_dirfree(char** names, int count) +{ + int i; + for (i=0 ; i= max) { + max += 100; + result = result ? PR_Realloc(result, max * sizeof(char*)) : + PR_Malloc(max * sizeof(char*)); + if (!result) return PR_GetError(); + } + result[count] = PL_strdup(entry->name); + if (result[count] == NULL) { + func_dirfree(result, count); + return PR_GetError(); + } + count++; + } + *names = result; + *cnt = count; + return 0; +} + + +static int +func_exists(const char *path, int *isdirp) +{ + PRFileInfo info; + if (PR_GetFileInfo(path, &info) != PR_SUCCESS) { + return PR_GetError(); + } + if (isdirp) { + *isdirp = (info.type == PR_FILE_DIRECTORY); + } + return 0; +} + + +static int +func_fsync(int fd) +{ + return PR_Sync((PRFileDesc*) fd) == PR_SUCCESS ? 0 : PR_GetError(); +} + + +static int +func_ioinfo(const char *path, int fd, u_int32_t *mbytesp, u_int32_t *bytesp, + u_int32_t *iosizep) +{ + PRFileInfo64 info; + if (PR_GetFileInfo64(path, &info) != PR_SUCCESS) { + return PR_GetError(); + } + if (mbytesp) *mbytesp = info.size / MEGABYTE; + if (bytesp) *bytesp = info.size % MEGABYTE; + if (iosizep) *iosizep = (8 * 1024); /* ### Ack! Fix! *** */ + return 0; +} + + +static int +func_open(const char* path, int flags, int mode) +{ + PRIntn f = 0; + PRFileDesc* fid; + if (flags & O_RDONLY) { + f |= PR_RDONLY; + } + if (flags & O_WRONLY) { + f |= PR_WRONLY; + } + if (flags & O_RDWR) { + f |= PR_RDWR; + } + if (flags & O_CREAT) { + f |= PR_CREATE_FILE; + } + if (flags & O_TRUNC) { + f |= PR_TRUNCATE; + } + if (flags & O_SYNC) { + f |= PR_SYNC; + } + fid = PR_Open(path, f, mode); + if (fid == NULL) return -1; + return (int) fid; +} + + +static ssize_t +func_read(int fd, void* buf, size_t nbytes) +{ + return PR_Read((PRFileDesc*) fd, buf, nbytes); +} + + +static int +func_seek(int fd, size_t pgsize, + db_pgno_t pageno, u_int32_t relative, int rewind, int db_whence) +{ + PRSeekWhence whence; + PRInt64 offset; + switch (db_whence) { + case SEEK_CUR: + whence = PR_SEEK_CUR; + break; + case SEEK_END: + whence = PR_SEEK_END; + break; + case SEEK_SET: + whence = PR_SEEK_SET; + break; + default: + return -1; + } + offset = ((PRInt64) pgsize) * ((PRInt64) pageno) + relative; + if (rewind) offset = -offset; + if (PR_Seek64((PRFileDesc*) fd, offset, whence) < 0) return PR_GetError(); + return 0; +} + + +static int +func_sleep(u_long seconds, u_long microseconds) +{ + if (PR_Sleep(PR_SecondsToInterval(seconds) + + PR_MicrosecondsToInterval(microseconds)) == PR_SUCCESS) { + return 0; + } + return PR_GetError(); +} + + +static int +func_unlink(const char* path) +{ + return PR_Delete(path) == PR_SUCCESS ? 0 : PR_GetError(); +} + + +static ssize_t +func_write(int fd, const void* buffer, size_t nbytes) +{ + return PR_Write((PRFileDesc*) fd, buffer, nbytes); +} + + +TDBStatus +tdbMakeDBCompatableWithNSPR(DB_ENV* env) +{ + if (env->set_func_close(env, func_close) != DB_OK) return TDB_FAILURE; + if (env->set_func_dirfree(env, func_dirfree) != DB_OK) return TDB_FAILURE; + if (env->set_func_dirlist(env, func_dirlist) != DB_OK) return TDB_FAILURE; + if (env->set_func_exists(env, func_exists) != DB_OK) return TDB_FAILURE; + if (env->set_func_free(env, PR_Free) != DB_OK) return TDB_FAILURE; + if (env->set_func_fsync(env, func_fsync) != DB_OK) return TDB_FAILURE; + if (env->set_func_ioinfo(env, func_ioinfo) != DB_OK) return TDB_FAILURE; + if (env->set_func_malloc(env, PR_Malloc) != DB_OK) return TDB_FAILURE; + /* if (env->set_func_map(env, func_map) != DB_OK) return TDB_FAILURE; */ + if (env->set_func_open(env, (int (*)(const char *, int, ...)) func_open) + != DB_OK) return TDB_FAILURE; + if (env->set_func_read(env, func_read) != DB_OK) return TDB_FAILURE; + if (env->set_func_realloc(env, PR_Realloc) != DB_OK) return TDB_FAILURE; + if (env->set_func_seek(env, func_seek) != DB_OK) return TDB_FAILURE; + if (env->set_func_sleep(env, func_sleep) != DB_OK) return TDB_FAILURE; + if (env->set_func_unlink(env, func_unlink) != DB_OK) return TDB_FAILURE; + /* if (env->set_func_unmap(env, func_unmap) != DB_OK) return TDB_FAILURE;*/ + if (env->set_func_write(env, func_write) != DB_OK) return TDB_FAILURE; + /* if (env->set_func_yield(env, func_yield) != DB_OK) return TDB_FAILURE;*/ + + return TDB_SUCCESS; +} + + +#endif /* TDB_USE_NSPR */ diff --git a/db/tripledb/src/dbnspr.h b/db/tripledb/src/dbnspr.h new file mode 100644 index 00000000000..1fde8366d09 --- /dev/null +++ b/db/tripledb/src/dbnspr.h @@ -0,0 +1,28 @@ +/* -*- Mode: C; indent-tabs-mode: nil; -*- + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the TripleDB database library. + * + * The Initial Developer of the Original Code is Geocast Network Systems. + * Portions created by Geocast are + * Copyright (C) 2000 Geocast Network Systems. All + * Rights Reserved. + * + * Contributor(s): Terry Weissman + */ + +#ifndef _dbnspr_h_ +#define _dbnspr_h_ 1 + +extern TDBStatus tdbMakeDBCompatableWithNSPR(DB_ENV* env); + +#endif /* _dbnspr_h_ */ diff --git a/db/tripledb/src/debug.c b/db/tripledb/src/debug.c index b0daa9fa86f..def9eea591d 100644 --- a/db/tripledb/src/debug.c +++ b/db/tripledb/src/debug.c @@ -14,288 +14,113 @@ * * The Initial Developer of the Original Code is Geocast Network Systems. * Portions created by Geocast are - * Copyright (C) 1999 Geocast Network Systems. All + * Copyright (C) 2000 Geocast Network Systems. All * Rights Reserved. * * Contributor(s): Terry Weissman */ /* Various debugging routines. */ - #include "tdbtypes.h" #include "tdbdebug.h" -#include "prprf.h" -void TDBDumpNode(PRFileDesc* fid, TDBNode* node) +#include "tdb.h" +#include "rtype.h" +#include "vector.h" + +#ifdef TDB_USE_NSPR +#include "prprf.h" +#define tdb_fprintf PR_fprintf +#else +#define tdb_fprintf fprintf +#endif + +void TDBDumpNode(TDBFileDesc* fid, TDBNode* node) { +#ifdef TDB_USE_NSPR + PRExplodedTime expl; +#endif switch(node->type) { case TDBTYPE_STRING: - PR_fprintf(fid, "%s", node->d.str.string); + tdb_fprintf(fid, "%s", node->d.str.string); break; +#ifdef TDB_USE_NSPR + case TDBTYPE_TIME: + PR_ExplodeTime(node->d.time, PR_LocalTimeParameters, &expl); + tdb_fprintf(fid, "%02d/%02d/%04d %02d:%02d:%02d", + expl.tm_month+1, expl.tm_mday, expl.tm_year, + expl.tm_hour, expl.tm_min, expl.tm_sec); + break; +#endif default: - PR_fprintf(fid, "%ld", node->d.i); + tdb_fprintf(fid, "%lld", node->d.i); } } -void TDBDumpRecord(PRFileDesc* fid, TDB* db, TDBPtr ptr, PRInt32 tree, int indent, const char* kidname) +void +TDBDumpTree(TDBFileDesc* fid, TDB* db, TDBInt32 tree) { - TDBRecord* record; + /* Write me! (or not...) */ +} + + + + + + +TDBStatus TDBSanityCheck(TDBBase* base, TDBFileDesc* fid) +{ + + TDBNode minnode; + DBC* cursor; + TDBVector* vector; + int m; int i; - TDBPtr kid0; - TDBPtr kid1; - if (ptr == 0) return; - record = tdbLoadRecord(db, ptr); - for (i=0 ; iposition, - record->length, record->entry[tree].balance); - TDBDumpNode(fid, record->data[0]); - PR_fprintf(fid, ","); - TDBDumpNode(fid, record->data[1]); - PR_fprintf(fid, ","); - TDBDumpNode(fid, record->data[2]); - PR_fprintf(fid, "\n"); - kid0 = record->entry[tree].child[0]; - kid1 = record->entry[tree].child[1]; - tdbFlush(db); - TDBDumpRecord(fid, db, kid0, tree, indent + 1, "0"); - TDBDumpRecord(fid, db, kid1, tree, indent + 1, "1"); - -} - - -void TDBDumpTree(PRFileDesc* fid, TDB* db, PRInt32 tree) -{ - TDBPtr root; - if (tree < 0) { - root = db->freeroot; - tree = 0; - } else { - root = db->roots[tree]; - } - TDBDumpRecord(fid, db, root, tree, 0, "root"); -} - - - -typedef struct _TDBRangeSet { - PRInt32 position; - PRInt32 length; - struct _TDBRangeSet* next; - struct _TDBRangeSet* prev; -} TDBRangeSet; - - -static PRStatus checkMerges(TDBRangeSet* set, TDBRangeSet* tmp) -{ - while (tmp->next != set && - tmp->position + tmp->length == tmp->next->position) { - TDBRangeSet* t = tmp->next; - tmp->next = t->next; - tmp->next->prev = tmp; - tmp->length += t->length; - PR_Free(t); - } - while (tmp->prev != set && - tmp->position == tmp->prev->position + tmp->prev->length) { - TDBRangeSet* t = tmp->prev; - tmp->prev = t->prev; - tmp->prev->next = tmp; - tmp->position = t->position; - t->length += t->length; - PR_Free(t); - } - return PR_SUCCESS; -} - - -static PRStatus addRange(TDBRangeSet* set, PRInt32 position, PRInt32 length) -{ - TDBRangeSet* tmp; - TDBRangeSet* this; - for (tmp = set->next ; tmp != set ; tmp = tmp->next) { - if ((position >= tmp->position && - position < tmp->position + tmp->length) || - (tmp->position >= position && - tmp->position < position + length)) { - PR_ASSERT(0); /* Collision! */ - return PR_FAILURE; - } - if (position + length == tmp->position) { - tmp->position = position; - tmp->length += length; - return checkMerges(set, tmp); - } else if (tmp->position + tmp->length == position) { - tmp->length += length; - return checkMerges(set, tmp); - } - if (tmp->position > position + length) { - break; - } - } - this = PR_NEWZAP(TDBRangeSet); - if (!this) return PR_FAILURE; - this->next = tmp; - this->prev = tmp->prev; - this->next->prev = this; - this->prev->next = this; - this->position = position; - this->length = length; - return PR_SUCCESS; -} - - -typedef struct _TDBTreeInfo { - TDB* db; - PRInt32 tree; /* Which tree we're analyzing */ - PRInt32 comparerule; - PRInt32 count; /* Number of nodes in tree. */ - TDBRangeSet set; /* Range of bytes used by tree. */ - PRInt32 maxdepth; /* Maximum depth of tree. */ -} TDBTreeInfo; - - -static PRStatus checkTree(TDBTreeInfo* info, TDBPtr ptr, int depth, - int* maxdepth, TDBRecord* parent, int kid) -{ - TDBRecord* record; - PRStatus status; - TDBPtr kid0; - TDBPtr kid1; - int d0 = depth; - int d1 = depth; - PRInt8 bal; - TDBRecord keep; - PRInt64 cmp; - int i; - if (ptr == 0) return PR_SUCCESS; - info->count++; - if (*maxdepth < depth) { - *maxdepth = depth; - } - record = tdbLoadRecord(info->db, ptr); - if (parent) { - cmp = tdbCompareRecords(record, parent, info->comparerule); - PR_ASSERT((kid == 0 && cmp < 0) || (kid == 1 && cmp > 0)); - if (! ((kid == 0 && cmp < 0) || (kid == 1 && cmp > 0))) { - return PR_FAILURE; - } - } - status = addRange(&(info->set), record->position, record->length); - if (status != PR_SUCCESS) return status; - bal = record->entry[info->tree].balance; - if (bal < -1 || bal > 1) { - PR_ASSERT(0); - return PR_FAILURE; - } - kid0 = record->entry[info->tree].child[0]; - kid1 = record->entry[info->tree].child[1]; - memcpy(&keep, record, sizeof(TDBRecord)); + int flag; + int count; + int numindices; + TDBNodePtr nodes[3]; + TDBRType* rtype; + tdbBeginExclusiveOp(base); + minnode.type = TDBTYPE_MINNODE; for (i=0 ; i<3 ; i++) { - keep.data[i] = tdbNodeDup(keep.data[i]); + nodes[i] = &minnode; } - tdbFlush(info->db); - status = checkTree(info, kid0, depth + 1, &d0, &keep, 0); - if (status != PR_SUCCESS) return status; - status = checkTree(info, kid1, depth + 1, &d1, &keep, 1); - if (status != PR_SUCCESS) return status; - for (i=0 ; i<3 ; i++) { - TDBFreeNode(keep.data[i]); - } - if (d1 - d0 != bal) { - PR_ASSERT(0); - return PR_FAILURE; - } - if (*maxdepth < d0) { - *maxdepth = d0; - } - if (*maxdepth < d1) { - *maxdepth = d1; - } - - return PR_SUCCESS; -} - - -PRStatus TDBSanityCheck(TDB* db, PRFileDesc* fid) -{ - TDBTreeInfo info; - TDBRangeSet* tmp; - int i; - for (i=-1 ; i<4 ; i++) { - info.set.next = &(info.set); - info.set.prev = &(info.set); - info.db = db; - info.tree = (i < 0 ? 0 : i); - info.comparerule = i; - info.maxdepth = 0; - info.count = 0; - PR_fprintf(fid, "Checking tree %d...\n", i); - if (checkTree(&info, i < 0 ? db->freeroot : db->roots[i], 0, - &(info.maxdepth), NULL, 0) != PR_SUCCESS) { - PR_fprintf(fid, "Problem found!\n"); - return PR_FAILURE; + numindices = tdbGetNumIndices(base); + for (m = 0 ; mnext) { - PR_fprintf(fid, " %d - %d (%d)\n", - tmp->position, tmp->position + tmp->length, - tmp->length); - } - PR_fprintf(fid, " maxdepth: %d count %d\n", info.maxdepth, - info.count); + cursor->c_close(cursor); + if (fid) tdb_fprintf(fid, " done (%d triples found)\n", count); } - return PR_SUCCESS; + tdbEndExclusiveOp(base); + + + return TDB_SUCCESS; } -extern void TDBGetCursorStats(TDBCursor* cursor, - PRInt32* hits, - PRInt32* misses) + + + +void TDBMakeDotGraph(TDB* db, const char* filename, TDBInt32 tree) { - *hits = cursor->hits; - *misses = cursor->misses; -} - - - -void makeDotEntry(TDB* db, PRFileDesc* fid, TDBPtr ptr, PRInt32 tree) -{ - TDBRecord* record; - TDBPtr kid[2]; - int i; - record = tdbLoadRecord(db, ptr); - PR_fprintf(fid, "%d [label=\"%d\\n", record->position, record->position); - for (i=0 ; i<3 ; i++) { - TDBDumpNode(fid, record->data[i]); - if (i<2) PR_fprintf(fid, ","); - } - PR_fprintf(fid, "\\nbal=%d\"]\n", record->entry[tree].balance); - kid[0] = record->entry[tree].child[0]; - kid[1] = record->entry[tree].child[1]; - /* tdbFlush(db); */ - for (i=0 ; i<2 ; i++) { - if (kid[i] != 0) { - makeDotEntry(db, fid, kid[i], tree); - PR_fprintf(fid, "%d -> %d [label=\"%d\"]\n", ptr, kid[i], i); - } - } -} - -void TDBMakeDotGraph(TDB* db, const char* filename, PRInt32 tree) -{ - TDBPtr root; - PRFileDesc* fid; - fid = PR_Open(filename, PR_WRONLY | PR_CREATE_FILE, 0666); - if (tree == -1) { - root = db->freeroot; - tree = 0; - } else { - root = db->roots[tree]; - } - PR_fprintf(fid, "digraph G {\n"); - makeDotEntry(db, fid, root, tree); - PR_fprintf(fid, "}\n"); - PR_Close(fid); + /* Write me! (or not...) */ } diff --git a/db/tripledb/src/intern.c b/db/tripledb/src/intern.c new file mode 100644 index 00000000000..df6253c454c --- /dev/null +++ b/db/tripledb/src/intern.c @@ -0,0 +1,237 @@ +/* -*- Mode: C; indent-tabs-mode: nil; -*- + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the TripleDB database library. + * + * The Initial Developer of the Original Code is Geocast Network Systems. + * Portions created by Geocast are + * Copyright (C) 2000 Geocast Network Systems. All + * Rights Reserved. + * + * Contributor(s): Terry Weissman + */ + +#include "tdbtypes.h" + +#include "intern.h" +#include "node.h" +#include "tdb.h" +#include "util.h" + +struct _TDBIntern { + DB* nodetoatomdb; + DB* atomtonodedb; + char* tmpbuf; + TDBInt32 tmpbufsize; +}; + + + +TDBIntern* +tdbInternInit(TDBBase* base) +{ + TDBIntern* intern; + if (base == NULL) { + tdb_ASSERT(0); + return NULL; + } + intern = tdb_NEWZAP(TDBIntern); + if (!intern) return NULL; + if (db_create(&(intern->nodetoatomdb), tdbGetDBEnv(base), 0) != DB_OK) { + goto FAIL; + } + if (intern->nodetoatomdb->open(intern->nodetoatomdb, "nodetoatomdb", + NULL, DB_HASH, + DB_CREATE, 0666) != DB_OK) goto FAIL; + if (db_create(&(intern->atomtonodedb), tdbGetDBEnv(base), 0) != DB_OK) { + goto FAIL; + } + if (intern->atomtonodedb->open(intern->atomtonodedb, "atomtonodedb", + NULL, DB_RECNO, + DB_CREATE, 0666) != DB_OK) goto FAIL; + intern->tmpbufsize = 512; + intern->tmpbuf = tdb_Malloc(intern->tmpbufsize); + if (!intern->tmpbuf) goto FAIL; + return intern; + + FAIL: + tdbInternFree(intern); + return NULL; +} + + +void +tdbInternFree(TDBIntern* intern) +{ + if (intern == NULL) { + tdb_ASSERT(0); + return; + } + if (intern->nodetoatomdb) { + intern->nodetoatomdb->close(intern->nodetoatomdb, 0); + } + if (intern->atomtonodedb) { + intern->atomtonodedb->close(intern->atomtonodedb, 0); + } + if (intern->tmpbuf) tdb_Free(intern->tmpbuf); + tdb_Free(intern); +} + + +TDBNodePtr +tdbIntern(TDBBase* base, TDBNodePtr node) +{ + TDBIntern* intern; + char* ptr; + char* endptr; + DBT key; + DBT data; + int dbstatus; + TDBUint32 atom; + TDBUint32 atom2; + if (base == NULL || node == NULL) { + tdb_ASSERT(0); + return NULL; + } + intern = tdbGetIntern(base); + + if (intern == NULL || intern->tmpbuf == NULL) { + tdb_ASSERT(0); + return NULL; + } + + if (node->type == TDBTYPE_MINNODE) { + return TDBCreateIntNode(0, TDBTYPE_INTERNED); + } + if (node->type == TDBTYPE_MAXNODE) { + return TDBCreateIntNode(0xffffffff, TDBTYPE_INTERNED); + } + if (node->type == TDBTYPE_INTERNED) { + tdb_ASSERT(0); + return NULL; + } + + if (intern->tmpbuf) { + ptr = intern->tmpbuf; + endptr = ptr + intern->tmpbufsize; + tdbPutNode(&ptr, node, endptr); + } else { + ptr = NULL; + } + if (ptr == NULL || ptr >= endptr) { + intern->tmpbufsize = tdbNodeSize(node) + 10; + intern->tmpbuf = tdb_Realloc(intern->tmpbuf, intern->tmpbufsize); + if (!intern->tmpbuf) return NULL; + ptr = intern->tmpbuf; + endptr = ptr + intern->tmpbufsize; + tdbPutNode(&ptr, node, endptr); + if (ptr >= endptr) { + tdb_ASSERT(0); + return NULL; + } + } + + + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + key.data = intern->tmpbuf; + key.size = ptr - intern->tmpbuf; + dbstatus = intern->nodetoatomdb->get(intern->nodetoatomdb, + tdbGetTransaction(base), + &key, &data, 0); + if (dbstatus == DB_NOTFOUND) { + do { + data.data = &atom; + data.ulen = sizeof(atom); + data.flags = DB_DBT_USERMEM; + dbstatus = intern->atomtonodedb->put(intern->atomtonodedb, + tdbGetTransaction(base), + &data, &key, DB_APPEND); + if (dbstatus != DB_OK) return NULL; + tdb_ASSERT(data.size == sizeof(TDBUint32)); + } while (atom == 0); /* First time, we may get an atom of zero, + which I don't want, since I reserve atom + of zero to mean something special. */ + data.data = &atom2; + ptr = data.data; + endptr = ptr + data.size; + tdbPutUInt32(&ptr, atom, endptr); + dbstatus = intern->nodetoatomdb->put(intern->nodetoatomdb, + tdbGetTransaction(base), + &key, &data, 0); + if (dbstatus != DB_OK) return NULL; + } else { + if (dbstatus != DB_OK) return NULL; + if (data.size != sizeof(TDBUint32)) { + tdb_ASSERT(0); + return NULL; + } + ptr = data.data; + endptr = ptr + data.size; + atom = tdbGetUInt32(&ptr, endptr); + } + return TDBCreateIntNode(atom, TDBTYPE_INTERNED); +} + + +TDBNodePtr +tdbUnintern(TDBBase* base, TDBNodePtr node) +{ + char* ptr; + char* endptr; + DBT key; + DBT data; + TDBIntern* intern; + TDBUint32 atom; + int dbstatus; + if (base == NULL || node == NULL || node->type != TDBTYPE_INTERNED || + node->d.i == 0 || node->d.i == 0xffffffff) { + tdb_ASSERT(0); + return NULL; + } + intern = tdbGetIntern(base); + if (intern == NULL) { + tdb_ASSERT(0); + return NULL; + } + atom = node->d.i; + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + key.data = &atom; + key.size = sizeof(atom); + dbstatus = intern->atomtonodedb->get(intern->atomtonodedb, + tdbGetTransaction(base), + &key, &data, 0); + if (dbstatus != DB_OK) return NULL; + ptr = (char*) data.data; + endptr = ptr + data.size; + return tdbGetNode(&ptr, endptr); +} + + +TDBStatus +tdbInternSync(TDBBase* base) +{ + TDBIntern* intern; + if (base == NULL) { + tdb_ASSERT(0); + return TDB_FAILURE; + } + intern = tdbGetIntern(base); + if (intern->atomtonodedb->sync(intern->atomtonodedb, 0) != DB_OK) { + return TDB_FAILURE; + } + if (intern->nodetoatomdb->sync(intern->nodetoatomdb, 0) != DB_OK) { + return TDB_FAILURE; + } + return TDB_SUCCESS; +} diff --git a/db/tripledb/src/intern.h b/db/tripledb/src/intern.h new file mode 100644 index 00000000000..8b0c08c1901 --- /dev/null +++ b/db/tripledb/src/intern.h @@ -0,0 +1,48 @@ +/* -*- Mode: C; indent-tabs-mode: nil; -*- + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the TripleDB database library. + * + * The Initial Developer of the Original Code is Geocast Network Systems. + * Portions created by Geocast are + * Copyright (C) 2000 Geocast Network Systems. All + * Rights Reserved. + * + * Contributor(s): Terry Weissman + */ + +#ifndef _intern_h_ +#define _intern_h_ 1 + +extern TDBIntern* tdbInternInit(TDBBase* base); + +extern void tdbInternFree(TDBIntern* intern); + +/* tdbIntern() takes a non-interned node and returns an atom representing it. + The returned node should be free'd with TDBFreeNode(). */ + +extern TDBNodePtr tdbIntern(TDBBase* base, TDBNodePtr node); + + +/* tdbUnintern() takes an atom and allocates a new node representing it. The + returned node should be free'd with TDBFreeNode(). */ + +extern TDBNodePtr tdbUnintern(TDBBase* base, TDBNodePtr node); + +/* tdbRTypeSync() causes any changes made to intern tables to be written out + to disk. */ + +extern TDBStatus tdbInternSync(TDBBase* base); + + + +#endif /* _intern_h_ */ diff --git a/db/tripledb/src/node.c b/db/tripledb/src/node.c index 0a1a25feee6..3d59bf535e6 100644 --- a/db/tripledb/src/node.c +++ b/db/tripledb/src/node.c @@ -14,24 +14,29 @@ * * The Initial Developer of the Original Code is Geocast Network Systems. * Portions created by Geocast are - * Copyright (C) 1999 Geocast Network Systems. All + * Copyright (C) 2000 Geocast Network Systems. All * Rights Reserved. * * Contributor(s): Terry Weissman */ -/* Operations that do things on nodes. */ - #include "tdbtypes.h" +#include "node.h" +#include "util.h" + TDBNodePtr TDBCreateStringNode(const char* string) { TDBNode* result; - PRInt32 length; - PR_ASSERT(string != NULL); + TDBInt32 length; + tdb_ASSERT(string != NULL); if (string == NULL) return NULL; length = strlen(string); - result = (TDBNode*) PR_Malloc(sizeof(TDBNode) + length + 1); + if (length > 65535) { + tdb_ASSERT(0); /* Don't call us with huge strings! */ + return NULL; + } + result = (TDBNode*) tdb_Malloc(sizeof(TDBNode) + length + 1); if (result == NULL) return NULL; result->type = TDBTYPE_STRING; @@ -41,34 +46,46 @@ TDBNodePtr TDBCreateStringNode(const char* string) } -TDBNodePtr TDBCreateIntNode(PRInt64 value, PRInt8 type) +TDBNodePtr TDBCreateIntNode(TDBInt64 value, TDBInt8 type) { TDBNode* result; - PR_ASSERT(type >= TDBTYPE_INT8 && type <= TDBTYPE_TIME); - if (type < TDBTYPE_INT8 || type > TDBTYPE_TIME) { + if ((type < TDBTYPE_INT32 || type >= TDBTYPE_STRING) && type != + TDBTYPE_INTERNED) { + tdb_ASSERT(0); return NULL; } - result = PR_NEW(TDBNode); + result = tdb_NEWZAP(TDBNode); if (result == NULL) return NULL; result->type = type; result->d.i = value; return result; } -void TDBFreeNode(TDBNode* node) + +TDBNodePtr tdbCreateSpecialNode(TDBInt8 type) { - PR_Free(node); + TDBNodePtr result; + tdb_ASSERT(type == TDBTYPE_MINNODE || type == TDBTYPE_MAXNODE); + result = tdb_NEWZAP(TDBNode); + if (result == NULL) return NULL; + result->type = type; + return result; } -TDBNode* tdbNodeDup(TDBNode* node) +void TDBFreeNode(TDBNode* node) +{ + tdb_Free(node); +} + +TDBNode* TDBNodeDup(TDBNode* node) { TDBNode* result; - PRInt32 length; - PR_ASSERT(node != NULL); + TDBInt32 length; + tdb_ASSERT(node != NULL); if (node == NULL) return NULL; if (node->type == TDBTYPE_STRING) { length = node->d.str.length; - result = PR_Malloc(sizeof(TDBNode) + length + 1); + result = tdb_Malloc(sizeof(TDBNode) + length + 1); if (result == NULL) return NULL; result->type = TDBTYPE_STRING; result->d.str.length = length; @@ -76,54 +93,106 @@ TDBNode* tdbNodeDup(TDBNode* node) result->d.str.string[length] = '\0'; return result; } + if (node->type == TDBTYPE_MINNODE || node->type == TDBTYPE_MAXNODE) { + return tdbCreateSpecialNode(node->type); + } return TDBCreateIntNode(node->d.i, node->type); } -PRInt32 tdbNodeSize(TDBNode* node) +TDBInt32 tdbNodeSize(TDBNode* node) { - PR_ASSERT(node != NULL); + tdb_ASSERT(node != NULL); if (node == NULL) return 0; switch (node->type) { - case TDBTYPE_INT8: - return 1 + sizeof(PRInt8); - case TDBTYPE_INT16: - return 1 + sizeof(PRInt16); case TDBTYPE_INT32: - return 1 + sizeof(PRInt32); + return 1 + sizeof(TDBInt32); case TDBTYPE_INT64: - return 1 + sizeof(PRInt64); + return 1 + sizeof(TDBInt64); + case TDBTYPE_ID: + return 1 + sizeof(TDBUint64); case TDBTYPE_TIME: - return 1 + sizeof(PRTime); + return 1 + sizeof(TDBTime); case TDBTYPE_STRING: - return 1 + sizeof(PRUint16) + node->d.str.length; + return 1 + node->d.str.length + 1; + case TDBTYPE_BLOB: + return 1 + sizeof(TDBUint16) + node->d.str.length; + case TDBTYPE_INTERNED: + return 1 + sizeof(TDBUint32); default: - PR_ASSERT(0); + tdb_ASSERT(0); return 0; } } -PRInt64 tdbCompareNodes(TDBNode* n1, TDBNode* n2) +TDBStatus tdbPutNode(char** ptr, TDBNode* node, char* endptr) { - if (n1->type == TDBTYPE_STRING && n2->type == TDBTYPE_STRING) { - return strcmp(n1->d.str.string, n2->d.str.string); - } else if (n1->type != TDBTYPE_STRING && n2->type != TDBTYPE_STRING) { - return n1->d.i - n2->d.i; - } else { - return n1->type - n2->type; + if (tdbPutInt8(ptr, node->type, endptr) != TDB_SUCCESS) { + return TDB_FAILURE; + } + switch (node->type) { + case TDBTYPE_STRING: + if (endptr - *ptr <= node->d.str.length) return TDB_FAILURE; + memcpy(*ptr, node->d.str.string, node->d.str.length); + *ptr += node->d.str.length; + *(*ptr)++ = '\0'; + return TDB_SUCCESS; + case TDBTYPE_BLOB: + tdbPutUInt16(ptr, node->d.str.length, endptr); + if (endptr - *ptr < node->d.str.length) { + return TDB_FAILURE; + } + memcpy(*ptr, node->d.str.string, node->d.str.length); + *ptr += node->d.str.length; + return TDB_SUCCESS; + case TDBTYPE_INT32: + return tdbPutInt32(ptr, (TDBInt32) node->d.i, endptr); + case TDBTYPE_INT64: + case TDBTYPE_TIME: + return tdbPutInt64(ptr, node->d.i, endptr); + case TDBTYPE_ID: + return tdbPutUInt64(ptr, node->d.i, endptr); + case TDBTYPE_INTERNED: + return tdbPutUInt32(ptr, node->d.i, endptr); + case TDBTYPE_MINNODE: + case TDBTYPE_MAXNODE: + return TDB_SUCCESS; + default: + tdb_ASSERT(0); + return TDB_FAILURE; } } -TDBNode* tdbGetNode(TDB* db, char** ptr) +TDBNode* tdbGetNode(char** ptr, char* endptr) { TDBNode* result; - PRInt8 type = tdbGetInt8(ptr); - PRInt64 i; - if (type == TDBTYPE_STRING) { - PRUint16 length = tdbGetUInt16(ptr); - result = (TDBNode*) PR_Malloc(sizeof(TDBNode) + length + 1); + TDBInt8 type; + TDBInt64 i; + TDBUint16 length; + char* p; + type = tdbGetInt8(ptr, endptr); + if (*ptr >= endptr) return NULL; + switch (type) { + case TDBTYPE_STRING: + for (p = *ptr ; p <= endptr && *p ; p++) {} + if (p > endptr) return NULL; + length = p - *ptr; + result = (TDBNode*) tdb_Malloc(sizeof(TDBNode) + length + 1); + if (result == NULL) { + return NULL; + } + result->type = type; + result->d.str.length = length; + memcpy(result->d.str.string, *ptr, length + 1); + *ptr = p + 1; + return result; + case TDBTYPE_BLOB: + length = tdbGetUInt16(ptr, endptr); + if (*ptr > endptr) return NULL; + if (*ptr + length > endptr) return NULL; + result = (TDBNode*) tdb_Malloc(sizeof(TDBNode) + length + 1); if (result == NULL) { return NULL; } @@ -132,55 +201,39 @@ TDBNode* tdbGetNode(TDB* db, char** ptr) memcpy(result->d.str.string, *ptr, length); result->d.str.string[length] = '\0'; (*ptr) += length; - } else { - switch(type) { - case TDBTYPE_INT8: - i = tdbGetInt8(ptr); - break; - case TDBTYPE_INT16: - i = tdbGetInt16(ptr); - break; - case TDBTYPE_INT32: - i = tdbGetInt32(ptr); - break; - case TDBTYPE_INT64: - case TDBTYPE_TIME: - i = tdbGetInt64(ptr); - break; - default: - tdbMarkCorrupted(db); - return NULL; - } - result = TDBCreateIntNode(i, type); + return result; + case TDBTYPE_INT32: + i = tdbGetInt32(ptr, endptr); + break; + case TDBTYPE_INT64: + case TDBTYPE_TIME: + i = tdbGetInt64(ptr, endptr); + break; + case TDBTYPE_ID: + i = tdbGetUInt64(ptr, endptr); + break; + case TDBTYPE_INTERNED: + i = tdbGetUInt32(ptr, endptr); + break; + default: + return NULL; } - return result; + return TDBCreateIntNode(i, type); } -void tdbPutNode(TDB* db, char** ptr, TDBNode* node) +TDBInt64 TDBCompareNodes(TDBNode* n1, TDBNode* n2) { - tdbPutInt8(ptr, node->type); - switch (node->type) { - case TDBTYPE_STRING: - tdbPutUInt16(ptr, node->d.str.length); - memcpy(*ptr, node->d.str.string, node->d.str.length); - *ptr += node->d.str.length; - break; - case TDBTYPE_INT8: - tdbPutInt8(ptr, (PRInt8) node->d.i); - break; - case TDBTYPE_INT16: - tdbPutInt16(ptr, (PRInt16) node->d.i); - break; - case TDBTYPE_INT32: - tdbPutInt32(ptr, (PRInt32) node->d.i); - break; - case TDBTYPE_INT64: - case TDBTYPE_TIME: - tdbPutInt64(ptr, node->d.i); - break; - default: - tdbMarkCorrupted(db); - break; + TDBInt64 result; + if (n1->type != n2->type) { + return n1->type - n2->type; + } else if (n1->type == TDBTYPE_STRING || n1->type == TDBTYPE_BLOB) { + result = memcmp(n1->d.str.string, n2->d.str.string, + n1->d.str.length < n2->d.str.length ? + n1->d.str.length : n2->d.str.length); + if (result != 0) return result; + return ((TDBInt64) n1->d.str.length) - ((TDBInt64) n2->d.str.length); + } else { + return n1->d.i - n2->d.i; } } diff --git a/db/tripledb/src/node.h b/db/tripledb/src/node.h new file mode 100644 index 00000000000..d1a9fdabfa3 --- /dev/null +++ b/db/tripledb/src/node.h @@ -0,0 +1,39 @@ +/* -*- Mode: C; indent-tabs-mode: nil; -*- + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the TripleDB database library. + * + * The Initial Developer of the Original Code is Geocast Network Systems. + * Portions created by Geocast are + * Copyright (C) 2000 Geocast Network Systems. All + * Rights Reserved. + * + * Contributor(s): Terry Weissman + */ + +#ifndef _node_h_ +#define _node_h_ 1 + + + + +extern TDBNode* tdbNodeDup(TDBNode* node); +extern TDBInt32 tdbNodeSize(TDBNode* node); +extern TDBNode* tdbGetNode(char** ptr, char* endptr); +extern TDBStatus tdbPutNode(char** ptr, TDBNode* node, char* endptr); + + +extern TDBNode* tdbGetNodeFromKey(char** ptr, char* endptr); +extern TDBStatus tdbPutNodeToKey(char** ptr, TDBNode* node, char* endptr, + TDBUint8 *flags); + +#endif /* _node_h_ */ diff --git a/db/tripledb/src/query.c b/db/tripledb/src/query.c deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/db/tripledb/src/record.c b/db/tripledb/src/record.c deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/db/tripledb/src/rtype.c b/db/tripledb/src/rtype.c new file mode 100644 index 00000000000..b157c012026 --- /dev/null +++ b/db/tripledb/src/rtype.c @@ -0,0 +1,431 @@ +/* -*- Mode: C; indent-tabs-mode: nil; -*- + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the TripleDB database library. + * + * The Initial Developer of the Original Code is Geocast Network Systems. + * Portions created by Geocast are + * Copyright (C) 2000 Geocast Network Systems. All + * Rights Reserved. + * + * Contributor(s): Terry Weissman + */ + +#include "tdbtypes.h" + +#include "node.h" +#include "rtype.h" +#include "tdb.h" +#include "util.h" +#include "vector.h" + +struct _TDBRType { + TDBBase* base; + TDBRType* next; + char* name; + TDBUint8 code; + + TDBInt32 numfields; + TDBInt32 fieldnum[TDB_MAXTYPES]; + + DB* db; +}; + + + + +TDBRType* +tdbRTypeNew(TDBBase* base, const char* name, TDBUint8 code, + TDBInt32 numfields, TDBInt32* fieldnum) +{ + TDBInt32 i; + TDBRType* result; + TDBRType* first; + int dbstatus; + first = tdbGetFirstRType(base); + if (numfields <= 0 || numfields > TDB_MAXTYPES || fieldnum == NULL) { + tdb_ASSERT(0); + return NULL; + } + + for (result = first ; result ; result = result->next) { + if (result->code == code) { + tdb_ASSERT(0); + return NULL; + } + } + + result = tdb_NEWZAP(TDBRType); + if (!result) return NULL; + result->base = base; + result->name = tdb_strdup(name); + if (!result->name) { + tdb_Free(result); + return NULL; + } + result->code = code; + result->numfields = numfields; + result->next = first; + tdbSetFirstRType(base, result); + + for (i=0 ; ifieldnum[i] = fieldnum[i]; + } + + dbstatus = db_create(&(result->db), tdbGetDBEnv(base), 0); + if (dbstatus != DB_OK) goto FAIL; + dbstatus = result->db->open(result->db, name, NULL, DB_BTREE, + DB_CREATE, 0666); + if (dbstatus != DB_OK) goto FAIL; + + + return result; + + FAIL: + tdbRTypeFree(result); + return NULL; +} + + +void tdbRTypeFree(TDBRType* rtype) +{ + TDBRType* first; + if (!rtype) { + tdb_ASSERT(0); + return; + } + first = tdbGetFirstRType(rtype->base); + if (first == rtype) { + tdbSetFirstRType(rtype->base, first->next); + } else { + for (; first ; first = first->next) { + if (first->next == rtype) { + first->next = rtype->next; + break; + } + } + } + if (rtype->name) tdb_Free(rtype->name); + if (rtype->db) { + rtype->db->close(rtype->db, 0); + } + tdb_Free(rtype); +} + + +TDBBase* +tdbRTypeGetBase(TDBRType* rtype) +{ + if (!rtype) { + tdb_ASSERT(0); + return NULL; + } + return rtype->base; +} + + +TDBUint8 +tdbRTypeGetCode(TDBRType* rtype) +{ + if (!rtype) { + tdb_ASSERT(0); + return 0; + } + return rtype->code; +} + + +const char* +tdbRTypeGetName(TDBRType* rtype) +{ + if (!rtype) { + tdb_ASSERT(0); + return 0; + } + return rtype->name; +} + + +TDBInt32 +tdbRTypeGetNumFields(TDBRType* rtype) +{ + if (!rtype) { + tdb_ASSERT(0); + return 0; + } + return rtype->numfields; +} + + +TDBStatus +tdbRTypeIndexGetField(TDBRType* rtype, TDBInt32 which, TDBInt32* fieldnum) +{ + if (!rtype || which < 0 || which >= rtype->numfields) { + tdb_ASSERT(0); + return TDB_FAILURE; + } + if (fieldnum) *fieldnum = rtype->fieldnum[which]; + return TDB_SUCCESS; +} + + +TDBRType* +tdbRTypeFromCode(TDBBase* base, TDBUint8 code) +{ + TDBRType* result; + tdb_ASSERT((code & TDB_FREERECORD) == 0); + for (result = tdbGetFirstRType(base); result ; result = result->next) { + if (result->code == code) break; + } + return result; +} + + +TDBRType* tdbRTypeGetNextRType(TDBRType* rtype) +{ + if (rtype == NULL) { + tdb_ASSERT(0); + return NULL; + } + return rtype->next; +} + + +#define FIELDTYPE(i) ((i)==0 ? TDBTYPE_INT64 : ((i)==1 ? TDBTYPE_INTERNED : 0)) + +static TDBStatus +loadKeyData(TDBRType* rtype, DBT* key, TDBVector* vector) +{ + char* keydata; + TDBNodePtr node; + TDBInt32 i; + TDBInt32 w; + char* ptr; + char* endptr; + if (rtype == NULL || vector == NULL || + tdbVectorGetNumFields(vector) != rtype->numfields) { + tdb_ASSERT(0); + return TDB_FAILURE; + } + keydata = tdbGrowTmpBuf(rtype->base, 512); + if (keydata == NULL) { + tdb_ASSERT(0); + return TDB_FAILURE; + } + ptr = keydata; + endptr = ptr + tdbGetTmpBufSize(rtype->base); + for (i=0 ; inumfields ; i++) { + w = rtype->fieldnum[i]; + node = tdbVectorGetInternedNode(vector, w); + switch (w) { + case 1: + if (node->type != TDBTYPE_INTERNED) { + tdb_ASSERT(0); + return TDB_FAILURE; + } + tdbPutUInt32(&ptr, node->d.i, endptr); + break; + default: + tdb_ASSERT(w == 0 || w == 2); + if (tdbPutNode(&ptr, node, endptr) != TDB_SUCCESS) { + ptr = endptr; + } + break; + } + } + tdbPutUInt8(&ptr, tdbVectorGetLayer(vector), endptr); + tdbPutUInt8(&ptr, tdbVectorGetFlags(vector), endptr); + if (ptr >= endptr) { + if (tdbGrowTmpBuf(rtype->base, sizeof(TDBUint64) + sizeof(TDBUint32) + + tdbNodeSize(tdbVectorGetInternedNode(vector, 2)) + + 10) != TDB_SUCCESS) return TDB_FAILURE; + return loadKeyData(rtype, key, vector); + } + + key->data = keydata; + key->size = ptr - keydata; + + return TDB_SUCCESS; +} + + +TDBStatus +tdbRTypeAdd(TDBRType* rtype, TDBVector* vector) +{ + DBT key; + DBT data; + int dbstatus; + TDBUint32 recordnum; + char buf[10]; + char* ptr; + char* endptr; + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + if (loadKeyData(rtype, &key, vector) != TDB_SUCCESS) { + return TDB_FAILURE; + } + recordnum = tdbVectorGetRecordNumber(vector); + tdb_ASSERT(recordnum > 0); + ptr = buf; + endptr = buf + sizeof(buf); + tdbPutUInt32(&ptr, recordnum, endptr); + data.data = buf; + data.size = ptr - buf; + /* tdb_ASSERT(rtype->tdb->transaction != NULL); */ + dbstatus = rtype->db->put(rtype->db, tdbGetTransaction(rtype->base), + &key, &data, 0); + if (dbstatus != DB_OK) return TDB_FAILURE; + return TDB_SUCCESS; +} + + +TDBStatus +tdbRTypeRemove(TDBRType* rtype, TDBVector* vector) +{ + DBT key; + int dbstatus; + memset(&key, 0, sizeof(key)); + if (loadKeyData(rtype, &key, vector) != TDB_SUCCESS) { + return TDB_FAILURE; + } + dbstatus = rtype->db->del(rtype->db, tdbGetTransaction(rtype->base), + &key, 0); + if (dbstatus != DB_OK) return TDB_FAILURE; + return TDB_SUCCESS; +} + + +DBC* +tdbRTypeGetCursor(TDBRType* rtype, TDBVector* vector) +{ + DBT key; + DBT data; + int dbstatus; + DBC* cursor; + if (rtype == NULL || vector == NULL) { + tdb_ASSERT(0); + return NULL; + } + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + if (loadKeyData(rtype, &key, vector) != TDB_SUCCESS) { + return NULL; + } + dbstatus = rtype->db->cursor(rtype->db, NULL, + &cursor, 0); + if (dbstatus != DB_OK) return NULL; + dbstatus = cursor->c_get(cursor, &key, &data, DB_SET_RANGE); + if (dbstatus != DB_OK) { + cursor->c_close(cursor); + return NULL; + } + + return cursor; +} + + + + +TDBVector* +tdbRTypeGetCursorValue(TDBRType* rtype, DBC* cursor, int dbflags) +{ + DBT key; + DBT data; + TDBNodePtr nodes[20]; + int dbstatus; + TDBInt32 i; + TDBInt32 w; + char* ptr; + char* endptr; + TDBUint8 layer; + TDBUint8 flags; + TDBUint32 recordnum; + TDBVector* result; + if (rtype == NULL || cursor == NULL || + rtype->numfields > sizeof(nodes) / sizeof(nodes[0])) { + tdb_ASSERT(0); + return NULL; + } + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + dbstatus = cursor->c_get(cursor, &key, &data, dbflags); + if (dbstatus == DB_NOTFOUND) return NULL; + if (dbstatus != DB_OK) return NULL; + ptr = key.data; + endptr = ptr + key.size; + for (i=0 ; inumfields ; i++) { + w = rtype->fieldnum[i]; + switch (w) { + case 1: + nodes[w] = TDBCreateIntNode(tdbGetUInt32(&ptr, endptr), + TDBTYPE_INTERNED); + break; + default: + nodes[w] = tdbGetNode(&ptr, endptr); + break; + } + if (nodes[w] == NULL) { + for (i-- ; i>=0 ; i--) { + w = rtype->fieldnum[i]; + TDBFreeNode(nodes[w]); + } + return NULL; + } + } + layer = tdbGetUInt8(&ptr, endptr); + flags = tdbGetUInt8(&ptr, endptr); + tdb_ASSERT(ptr == endptr); + ptr = data.data; + endptr = ptr + data.size; + recordnum = tdbGetUInt32(&ptr, endptr); + result = tdbVectorNewFromNodes(rtype->base, nodes, layer, flags, + recordnum); + for (i=0 ; inumfields ; i++) { + w = rtype->fieldnum[i]; + TDBFreeNode(nodes[w]); + } + return result; +} + + +TDBStatus +tdbRTypeSync(TDBRType* rtype) +{ + if (rtype == NULL) { + tdb_ASSERT(0); + return TDB_FAILURE; + } + if (rtype->db->sync(rtype->db, 0) != DB_OK) return TDB_FAILURE; + return TDB_SUCCESS; +} + + +extern TDBBool +tdbRTypeProbe(TDBRType* rtype, TDBVector* vector) +{ + TDBBool result = TDB_FALSE; + DBC* cursor; + TDBVector* v; + cursor = tdbRTypeGetCursor(rtype, vector); + if (!cursor) { + return TDB_FALSE; + } + v = tdbRTypeGetCursorValue(rtype, cursor, DB_CURRENT); + cursor->c_close(cursor); + if (v) { + if (tdbVectorEqual(vector, v)) { + result = TDB_TRUE; + } + tdbVectorFree(v); + } + return result; +} diff --git a/db/tripledb/src/rtype.h b/db/tripledb/src/rtype.h new file mode 100644 index 00000000000..e9f95f5eefc --- /dev/null +++ b/db/tripledb/src/rtype.h @@ -0,0 +1,111 @@ +/* -*- Mode: C; indent-tabs-mode: nil; -*- + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the TripleDB database library. + * + * The Initial Developer of the Original Code is Geocast Network Systems. + * Portions created by Geocast are + * Copyright (C) 2000 Geocast Network Systems. All + * Rights Reserved. + * + * Contributor(s): Terry Weissman + */ + +#ifndef _rtype_h_ +#define _rtype_h_ 1 + + +extern TDBRType* tdbRTypeNew(TDBBase* base, const char* name, TDBUint8 code, + TDBInt32 numfields, TDBInt32* fieldnums); + + + +/* tdbRTypeFree() frees the given rtype. */ + +extern void tdbRTypeFree(TDBRType* rtype); + + + +/* tdbRTypeGetBase() gets the TDB associated with this rtype. */ + +extern TDBBase* tdbRTypeGetBase(TDBRType* rtype); + +/* tdbRTypeGetCode() returns the code associated with the given rtype. */ + +extern TDBUint8 tdbRTypeGetCode(TDBRType* rtype); + +/* tdbRTypeGetName() returns the name associated with the given rtype. */ + +extern const char* tdbRTypeGetName(TDBRType* rtype); + + +/* tdbRTypeGetNumFields() returns how many fields this type has. If it is + an index, this is the number of fields it indexes; if it is a base type, + this is the number of different values we are storing in this type; if it + is an extension type, this is zero. */ + +extern TDBInt32 tdbRTypeGetNumFields(TDBRType* rtype); + + + +/* tdbRTypeIndexGetField() returns information about one field that this type + indexes. */ + +extern TDBStatus tdbRTypeIndexGetField(TDBRType* rtype, TDBInt32 which, + TDBInt32* fieldnum); + + + +/* tdbRTypeFromCode() returns the rtype entry given the code number. */ + +extern TDBRType* tdbRTypeFromCode(TDBBase* base, TDBUint8 code); + + + +/* tdbRTypeGetNextRType() gets the next rtype from the master list of all + rtypes. */ + +extern TDBRType* tdbRTypeGetNextRType(TDBRType* rtype); + + +extern TDBStatus tdbRTypeAdd(TDBRType* rtype, TDBVector* vector); + + +extern TDBStatus tdbRTypeRemove(TDBRType* rtype, TDBVector* vector); + + +/* tdbRTypeGetCursor() gets a DB cursor into this index, and starts it pointing + at the first node greater than or equal to the given vector. */ + +extern DBC* tdbRTypeGetCursor(TDBRType* rtype, TDBVector* vector); + + +/* tdbRTypeGetCursorValue() passes the given flags to the cursor, and retrieves + the value of the resulting position of the cursor, translating it to + a TDBVector. */ + +extern TDBVector* tdbRTypeGetCursorValue(TDBRType* rtype, DBC* cursor, + int flags); + + +/* tdbRTypeSync() causes any changes made to this object to be written out + to disk. */ + +extern TDBStatus tdbRTypeSync(TDBRType* rtype); + + +/* tdbRTypeProbe() determines whether the given vector seems to be included + as part of this index. */ +extern TDBBool tdbRTypeProbe(TDBRType* rtype, TDBVector* vector); + + +#endif /* _rtype_h_ */ diff --git a/db/tripledb/src/tdb.c b/db/tripledb/src/tdb.c index e71a6c115da..c0f237cc5c4 100644 --- a/db/tripledb/src/tdb.c +++ b/db/tripledb/src/tdb.c @@ -14,428 +14,1515 @@ * * The Initial Developer of the Original Code is Geocast Network Systems. * Portions created by Geocast are - * Copyright (C) 1999 Geocast Network Systems. All + * Copyright (C) 2000 Geocast Network Systems. All * Rights Reserved. * * Contributor(s): Terry Weissman */ -/* tdb.c -- Routines operating on the database as a whole. */ +#define USE_TRANSACTIONS +#include /* For getpid() */ + #include "tdbtypes.h" -#include "prprf.h" +#include "tdbimpl.h" -TDB* TDBOpen(const char* filename) +#include "cursor.h" +#include "dbnspr.h" +#include "intern.h" +#include "rtype.h" +#include "tdb.h" +#include "util.h" +#include "vector.h" +#include "windex.h" + +#ifdef TDB_USE_THREADS +#include "tdbbg.h" +#endif + +struct _TDBBase { + char* filename; + + TDBLock* mutex; /* Used to prevent more than one thread + from doing anything in DB code at the + same time. */ + TDBCallbackInfo* firstcallback; + TDBPendingCall* firstpendingcall; + TDBPendingCall* lastpendingcall; + + TDBRType* firstrtype; + + TDBBool corrupted; + TDBBool toobustedtorecover; + + DB_ENV* env; + DB* miscdb; /* Database to store miscellaneous facts + (e.g., tdb version number). */ + DB* recorddb; /* Database that stores full records about + each triple. */ + + DB_TXN* transaction; + + TDBIntern* intern; + + int numindices; + TDBRType** index; + + TDBWindex* windex; + + char* tmpbuf; + TDBInt32 tmpbufsize; + + TDBBool dirty; /* Whether there are changes in the db that + we have not sync'd back to disk. */ + + TDB* firsttdb; + +#ifdef TDB_USE_THREADS + TDBBG* bgthread; +#endif +}; + + +struct _TDB { + TDBBase* base; + TDBCursor* firstcursor; + TDBImplement* impl; + void* impldata; + TDB* nexttdb; + TDBInt32 numlayers; + TDBInt32* layers; + TDBInt32 outlayer; +}; + + +void +tdbMarkCorrupted(TDBBase* base) { - PRStatus status; - TDB* db; - - PR_ASSERT(filename != NULL); - if (filename == NULL) return NULL; - db = PR_NEWZAP(TDB); - if (db == NULL) return NULL; - db->filename = PR_Malloc(strlen(filename) + 1); - if (db->filename == NULL) { - PR_Free(db); + if (base == NULL) { + tdb_ASSERT(0); + return; + } + base->corrupted = TDB_TRUE; +} + + +void +tdbMarkTooBustedToRecover(TDBBase* base) +{ + if (base == NULL) { + tdb_ASSERT(0); + return; + } + base->toobustedtorecover = TDB_TRUE; + base->corrupted = TDB_TRUE; +} + + +TDBRType* +tdbGetFirstRType(TDBBase* base) +{ + if (base == NULL) { + tdb_ASSERT(0); return NULL; } - strcpy(db->filename, filename); - db->fid = PR_Open(filename, PR_RDWR | PR_CREATE_FILE, 0666); - if (db->fid == NULL) { - /* Can't open file. */ + return base->firstrtype; +} + + +TDBStatus +tdbSetFirstRType(TDBBase* base, TDBRType* rtype) +{ + if (base == NULL) { + tdb_ASSERT(0); + return TDB_FAILURE; + } + base->firstrtype = rtype; + return TDB_SUCCESS; +} + + +TDBCursor* +tdbGetFirstCursor(TDB* tdb) +{ + if (tdb == NULL) { + tdb_ASSERT(0); + return NULL; + } + return tdb->firstcursor; +} + + +TDBStatus +tdbSetFirstCursor(TDB* tdb, TDBCursor* cursor) +{ + if (tdb == NULL) { + tdb_ASSERT(0); + return TDB_FAILURE; + } + tdb->firstcursor = cursor; + return TDB_SUCCESS; +} + + + +static int Order[3][3] = { + {0, 1, 2}, + {1, 2, 0}, + {2, 1, 0} +}; + +static void +panicFunc(DB_ENV* env, int errval) +{ + tdb_ASSERT(0); /* XXX Write me!!! ### */ + /* tdbMarkTooBustedToRecover(WHO?); */ +} + + +#ifdef TDB_USE_NSPR +#include "prprf.h" +#define tdb_fprintf PR_fprintf +#define tdb_stderr PR_STDERR +#else +#define tdb_fprintf fprintf +#define tdb_stderr stderr +#endif + +static void +errorFunc(const char* prefix, char* msg) +{ + tdb_fprintf(tdb_stderr, "DB ERROR: %s %s\n", prefix, msg); + tdb_ASSERT(0); +} + +static TDBStatus realSync(TDBBase* base); + +#ifdef TDB_USE_THREADS +static void +dosync(void* closure) +{ + TDBBase* base = (TDBBase*) closure; + realSync(base); +} +#endif + + +void +tdbMarkDirty(TDBBase* base) +{ + if (!base->dirty) { + base->dirty = TDB_TRUE; +#ifdef TDB_USE_THREADS + TDBBG_AddFunction(base->bgthread, "sync", 10, TDBBG_PRIORITY_LOW, + dosync, base); +#endif + } +} + + +#ifdef TDB_USE_NSPR +static TDBStatus +tdb_GetFileType(const char* path, TDBBool* isdir) +{ + PRFileInfo64 info; + if (PR_GetFileInfo64(path, &info) != PR_SUCCESS) return TDB_FAILURE; + if (isdir) { + *isdir = (info.type == PR_FILE_DIRECTORY); + } + return TDB_SUCCESS; +} +#else +static TDBStatus +tdb_GetFileType(const char* path, TDBBool* isdir) +{ + struct stat sbuf; + if (stat(path, &sbuf)) return TDB_FAILURE; + if (isdir) { + *isdir = S_ISDIR(sbuf.st_mode); + } + return TDB_SUCCESS; +} +#endif + +static TDBStatus +openGuts(TDBBase* base, const char* filename) +{ + TDBInt32 i; + TDBInt32 j; + DBT key; + DBT data; + char buf[32]; + char* ptr; + char* endptr; + TDBUint16 major; + TDBUint16 minor; + int dbstatus; + TDBBool isdir; + TDBBool needdir; + + if (base == NULL || filename == NULL) { + tdb_ASSERT(0); + return TDB_FAILURE; + } + base->filename = tdb_strdup(filename); + if (base->filename == NULL) { + return TDB_FAILURE; + } + + needdir = TDB_FALSE; + if (tdb_GetFileType(filename, &isdir) != TDB_SUCCESS) { + needdir = TDB_TRUE; + } else { + if (!isdir) { + /* Ooh, looky that, a file. This is either something really + old-fashioned, or something very busted and wrong happened. + Either way, we cope by simply nuking it. */ + if (tdb_Delete(filename) != TDB_SUCCESS) { + return TDB_FAILURE; + } + needdir = TDB_TRUE; + } + } + if (needdir) { + if (tdb_MkDir(filename, 0777) != TDB_SUCCESS) return TDB_FAILURE; + } + + if (db_env_create(&(base->env), 0) != DB_OK) goto FAIL; + + +#ifdef TDB_USE_NSPR + if (tdbMakeDBCompatableWithNSPR(base->env) != TDB_SUCCESS) goto FAIL; +#endif + + + if (base->env->set_cachesize(base->env, 0, 5 * 1024 * 1024, 0) != DB_OK) { goto FAIL; } + base->env->set_paniccall(base->env, panicFunc); + base->env->set_errcall(base->env, errorFunc); /* Hack. */ - db->mutex = PR_NewLock(); - if (db->mutex == NULL) goto FAIL; +/* (void)base->env->set_verbose(base->env, DB_VERB_RECOVERY, 1); */ +/* (void)base->env->set_verbose(base->env, DB_VERB_CHKPOINT, 1); */ - db->callbackcvargo = PR_NewCondVar(db->mutex); - if (db->callbackcvargo == NULL) goto FAIL; + dbstatus = + base->env->open(base->env, filename, + DB_INIT_MPOOL | +#ifdef USE_TRANSACTIONS + DB_INIT_TXN | DB_INIT_LOG | DB_TXN_NOSYNC | +#endif + DB_RECOVER | DB_RECOVER_FATAL | DB_CREATE | + DB_PRIVATE /* | DB_THREAD */, + 0666); + if (dbstatus != DB_OK) goto FAIL; - db->callbackcvaridle = PR_NewCondVar(db->mutex); - if (db->callbackcvaridle == NULL) goto FAIL; - - db->callbackthread = - PR_CreateThread(PR_SYSTEM_THREAD, tdbCallbackThread, db, - PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, - PR_JOINABLE_THREAD, 0); - if (db->callbackthread == NULL) goto FAIL; - - PR_Lock(db->mutex); - while (!db->callbackidle) { - PR_WaitCondVar(db->callbackcvaridle, PR_INTERVAL_NO_TIMEOUT); + if (db_create(&(base->miscdb), base->env, 0) != DB_OK) goto FAIL; + dbstatus = base->miscdb->open(base->miscdb, "misc", NULL, DB_HASH, + DB_CREATE, 0666); + if (dbstatus != DB_OK) { + if (dbstatus != DB_OLD_VERSION) { + goto FAIL; + } + tdbMarkTooBustedToRecover(base); + return TDB_SUCCESS; + } + if (db_create(&(base->recorddb), base->env, 0) != DB_OK) goto FAIL; + if (base->recorddb->open(base->recorddb, "record", NULL, DB_RECNO, + DB_CREATE, 0666) != DB_OK) goto FAIL; + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + key.data = "version"; + key.size = 7; + dbstatus = base->miscdb->get(base->miscdb, NULL, &key, &data, 0); + if (dbstatus == DB_NOTFOUND && needdir) { + ptr = buf; + endptr = buf + sizeof(buf); + tdbPutUInt16(&ptr, TDB_MAJOR_VERSION, endptr); + tdbPutUInt16(&ptr, TDB_MINOR_VERSION, endptr); + tdb_ASSERT(ptr < endptr); + data.data = buf; + data.size = ptr - buf; + dbstatus = base->miscdb->put(base->miscdb, NULL, &key, &data, 0); + base->miscdb->sync(base->miscdb, 0); } - status = tdbLoadRoots(db); - PR_Unlock(db->mutex); - if (status == PR_FAILURE) goto FAIL; - return db; + if (dbstatus != DB_OK) { + tdbMarkTooBustedToRecover(base); + } else { + ptr = data.data; + endptr = ptr + data.size; + major = tdbGetUInt16(&ptr, endptr); + minor = tdbGetUInt16(&ptr, endptr); + if (major != TDB_MAJOR_VERSION || ptr != endptr) { + tdbMarkTooBustedToRecover(base); + } else { + if (minor != TDB_MINOR_VERSION) { + tdbMarkCorrupted(base); + } + } + } + + base->numindices = sizeof(Order) / sizeof(Order[0]); + base->index = tdb_Calloc(base->numindices, sizeof(TDBRType*)); + if (base->index == NULL) goto FAIL; + for (i=0 ; inumindices ; i++) { + char buf[20]; + memset(buf, 0, sizeof(buf)); + strcpy(buf, "index"); + for (j=0 ; j<3 ; j++) { + buf[j+5] = Order[i][j] + '0'; + } + base->index[i] = tdbRTypeNew(base, buf, i + 1, 3, Order[i]); + if (base->index[i] == NULL) goto FAIL; + } + + base->intern = tdbInternInit(base); + if (!base->intern) goto FAIL; + base->windex = tdbWindexNew(base); + if (!base->windex) goto FAIL; + + return TDB_SUCCESS; FAIL: - TDBClose(db); + return TDB_FAILURE; +} + + +void +removeOldLogFiles(void* closure) +{ +#ifdef USE_TRANSACTIONS + TDBBase* base = (TDBBase*) closure; + char** list; + char** ptr; + tdbBeginExclusiveOp(base); + if (log_archive(base->env, &list, DB_ARCH_ABS, tdb_Malloc) == DB_OK) { + if (list) { + for (ptr = list ; *ptr ; ptr++) { + tdb_Delete(*ptr); + } + tdb_Free(list); + } + } +#ifdef TDB_USE_THREADS + TDBBG_AddFunction(base->bgthread, "removeOldLogFiles", 5 * 60, + TDBBG_PRIORITY_LOW, removeOldLogFiles, base); +#endif + tdbEndExclusiveOp(base); +#endif +} + + +TDBBase* +TDBOpenBase(const char* filename) +{ + TDBBase* base; + + /* The below double-checks that whatever definition we're using for all our + integer types really do work out to (more-or-less) the right thing. */ + + tdb_ASSERT(sizeof(TDBInt8) == 1); + tdb_ASSERT(sizeof(TDBInt16) == 2); + tdb_ASSERT(sizeof(TDBInt32) == 4); + tdb_ASSERT(sizeof(TDBInt64) == 8); + tdb_ASSERT(sizeof(TDBUint8) == 1); + tdb_ASSERT(sizeof(TDBUint16) == 2); + + tdb_ASSERT(filename != NULL); + if (filename == NULL) return NULL; + base = tdb_NEWZAP(TDBBase); + if (base == NULL) return NULL; + + if (openGuts(base, filename) != TDB_SUCCESS) { + TDBCloseBase(base); + return NULL; + } + +#ifdef TDB_USE_THREADS + base->mutex = PR_NewLock(); + base->bgthread = TDBBG_Open(); + if (base->mutex == NULL || base->bgthread == NULL) { + TDBCloseBase(base); + return NULL; + } +#endif + + if (base->corrupted) { + if (tdbRecover(base) != TDB_SUCCESS) { + TDBCloseBase(base); + return NULL; + } + } + + removeOldLogFiles(base); + + return base; +} + + +TDBStatus +TDBBlowAwayDatabaseFiles(const char* path) +{ +#ifdef TDB_USE_NSPR + PRDir* dir; + PRDirEntry* entry; + char* ptr; + dir = PR_OpenDir(path); + if (dir) { + while (NULL != (entry = PR_ReadDir(dir, PR_SKIP_BOTH))) { + ptr = PR_smprintf("%s/%s", path, entry->name); + PR_Delete(ptr); + PR_Free(ptr); + } + PR_CloseDir(dir); + } + return PR_RmDir(path); +#else + tdb_ASSERT(0); /* Need to write the non-NSPR version + of this code! XXX*/ + return TDB_FAILURE; +#endif /* TDB_USE_NSPR */ +} + + +static TDBStatus +closeGuts(TDBBase* base) +{ + TDB* tdb; + if (base == NULL) { + tdb_ASSERT(0); + return TDB_FAILURE; + } + if (base->transaction) { + if (txn_commit(base->transaction, 0) != DB_OK) { + tdb_ASSERT(0); + } + base->transaction = NULL; + } + for (tdb = base->firsttdb ; tdb ; tdb = tdb->nexttdb) { + tdbCursorInvalidateCache(tdb); + } + /* ### Do I need to free up firstcallback and firstpendingcall stuff here? + XXX */ + if (base->intern) { + tdbInternFree(base->intern); + base->intern = NULL; + } + if (base->windex) { + tdbWindexFree(base->windex); + base->windex = NULL; + } + while (base->firstrtype) { + tdbRTypeFree(base->firstrtype); + } + if (base->filename) { + tdb_Free(base->filename); + base->filename = NULL; + } + if (base->index) { + tdb_Free(base->index); + base->index = NULL; + } + if (base->tmpbuf) { + tdb_Free(base->tmpbuf); + base->tmpbuf = NULL; + } + base->tmpbufsize = 0; + if (base->miscdb) { + base->miscdb->close(base->miscdb, 0); + base->miscdb = NULL; + } + if (base->recorddb) { + base->recorddb->close(base->recorddb, 0); + base->recorddb = NULL; + } + if (base->env) { + base->env->close(base->env, 0); + base->env = NULL; + } + base->corrupted = base->toobustedtorecover = TDB_FALSE; + base->dirty = TDB_FALSE; + + return TDB_SUCCESS; +} + + +TDBStatus +TDBCloseBase(TDBBase* base) +{ + TDBStatus status; + if (base == NULL) { + tdb_ASSERT(0); + return TDB_FAILURE; + } +#ifdef USE_TRANSACTIONS + if (base->env) { + int dbstatus; + while ((dbstatus = txn_checkpoint(base->env, + 0, 0, 0)) == DB_INCOMPLETE) {} + tdb_ASSERT(dbstatus == DB_OK); + } +#endif + status = closeGuts(base); +#ifdef TDB_USE_THREADS + if (base->bgthread) { + TDBBG_Close(base->bgthread); + base->bgthread = NULL; + } + if (base->mutex) PR_DestroyLock(base->mutex); +#endif + tdb_Free(base); + return status; +} + + + +static TDBStatus +realSync(TDBBase* base) +{ + TDBStatus status = TDB_FAILURE; + int i; + if (base == NULL) { + tdb_ASSERT(0); + return TDB_FAILURE; + } + tdbBeginExclusiveOp(base); +#ifdef USE_TRANSACTIONS + { + int dbstatus; + while ((dbstatus = txn_checkpoint(base->env, 0, 0, 0)) == DB_INCOMPLETE) {} + if (dbstatus != DB_OK) { + tdb_ASSERT(0); + goto FAIL; + } + } +#endif + if (base->miscdb->sync(base->miscdb, 0) != DB_OK) goto FAIL; + if (base->recorddb->sync(base->recorddb, 0) != DB_OK) goto FAIL; + for (i=0 ; inumindices ; i++) { + if (tdbRTypeSync(base->index[i]) != TDB_SUCCESS) goto FAIL; + } + if (tdbInternSync(base) != TDB_SUCCESS) goto FAIL; + if (tdbWindexSync(base->windex) != TDB_SUCCESS) goto FAIL; + status = TDB_SUCCESS; + base->dirty = TDB_FALSE; + + FAIL: + tdbEndExclusiveOp(base); + return status; +} + + +TDBStatus +TDBSync(TDB* tdb) +{ + if (tdb == NULL) { + tdb_ASSERT(0); + return TDB_FAILURE; + } + return realSync(tdb->base); +} + +#ifdef TDB_USE_THREADS + +TDBBG* +TDBGetBG(TDB* tdb) +{ + if (tdb == NULL) { + tdb_ASSERT(0); + return NULL; + } + return tdb->base->bgthread; +} + + +#endif /* TDB_USE_THREADS */ + + +const char* +TDBGetFilename(TDB* tdb) +{ + if (tdb == NULL || tdb->base == NULL) { + tdb_ASSERT(0); + return NULL; + } + return tdb->base->filename; +} + + +void +tdbBeginExclusiveOp(TDBBase* base) +{ + if (base == NULL) { + tdb_ASSERT(0); + return; + } + tdb_Lock(base->mutex); + tdb_ASSERT(base->transaction == NULL); +#ifdef USE_TRANSACTIONS + if (txn_begin(base->env, NULL, &(base->transaction), 0) != DB_OK) { + tdb_ASSERT(0); + base->transaction = NULL; + } else { + tdb_ASSERT(base->transaction != NULL); + } +#endif +} + +void +tdbEndExclusiveOp(TDBBase* base) +{ + if (base == NULL) { + tdb_ASSERT(0); + return; + } + if (base->transaction) { + if (txn_commit(base->transaction, 0) != DB_OK) { + tdb_ASSERT(0); + } + base->transaction = NULL; + } + tdb_Unlock(base->mutex); +} + + +static TDBStatus +doAdd(TDB* tdb, TDBNodePtr triple[3], TDBUint64 owner, TDBBool isassert) +{ + TDBVector* vector; + TDBInt32 i; + TDBBase* base = tdb->base; + + tdbCursorInvalidateCache(tdb); + + vector = tdbVectorNewFromNodes(base, triple, tdb->outlayer, + isassert ? TDBFLAG_ISASSERT : 0, 0); + if (!vector) return TDB_FAILURE; + if (tdbVectorPutInNewRecord(vector, owner) != TDB_SUCCESS) { + tdbVectorFree(vector); + return TDB_FAILURE; + } + for (i=0 ; inumindices ; i++) { + if (tdbRTypeAdd(base->index[i], vector) != TDB_SUCCESS) { + tdbVectorFree(vector); + return TDB_FAILURE; + } + } + if (tdbWindexAdd(base->windex, vector) != TDB_SUCCESS) { + tdbVectorFree(vector); + return TDB_FAILURE; + } + tdbVectorFree(vector); + tdbMarkDirty(base); + return TDB_SUCCESS; +} + + + + + + +static TDBStatus +realAdd(TDB* tdb, TDBNodePtr triple[3], TDBUint64 owner, TDBBool assert) +{ + TDBStatus status; + TDBBase* base; + + if (tdb == NULL || triple == NULL || triple[0] == NULL || + triple[1] == NULL || triple[2] == NULL) { + tdb_ASSERT(0); + return TDB_FAILURE; + } + + if (tdb && tdb->impl) { + if (assert) { + return (*(tdb->impl->add))(tdb, triple, owner); + } else { + return (*(tdb->impl->addfalse))(tdb, triple, owner); + } + } + + base = tdb->base; + + tdbBeginExclusiveOp(base); + status = doAdd(tdb, triple, owner, assert); + if (base->corrupted) { + tdbRecover(base); + status = doAdd(tdb, triple, owner, assert); + } + tdbEndExclusiveOp(base); + + return status; +} + + +TDBStatus +TDBAdd(TDB* tdb, TDBNodePtr triple[3], TDBUint64 owner) +{ + return realAdd(tdb, triple, owner, TDB_TRUE); +} + + +TDBStatus +TDBAddFalse(TDB* tdb, TDBNodePtr triple[3], TDBUint64 owner) +{ + return realAdd(tdb, triple, owner, TDB_FALSE); +} + + +static void +fillRangeScore(TDBInt32* rangescore, TDBNodePtr* triple, TDBInt32 numfields) +{ + TDBInt32 i; + for (i=0 ; ibase; + + if (sortspec) { + for (tree=0 ; treenumindices ; tree++) { + numfields = tdbRTypeGetNumFields(base->index[tree]); + tdb_ASSERT(numfields == 3); + if (numfields != 3) continue; + match = TDB_TRUE; + for (i=0 ; iindex[tree], i, &j); + if (sortspec->keyorder[i] != j) { + match = TDB_FALSE; + break; + } + } + if (match) return base->index[tree]; + } + } + + /* The passed in keyorder was not valid (which, in fact, is the usual + case). Go find the best tree to use. */ + + numfields = 3; + + fillRangeScore(rangescore, triple, numfields); + + indextype = NULL; + bestscore = -1; + for (tree=0 ; treenumindices ; tree++) { + tdb_ASSERT(tdbRTypeGetNumFields(base->index[tree]) == numfields); + curscore = 0; + for (i=0 ; iindex[tree], i, &j); + curscore = curscore * 10 + rangescore[j]; + } + if (bestscore < curscore) { + bestscore = curscore; + indextype = base->index[tree]; + } + } + tdb_ASSERT(indextype != NULL); + return indextype; +} + + +static TDBStatus +doRemove(TDB* tdb, TDBNodePtr* triple) +{ + TDBStatus status = TDB_FAILURE; + TDBVector* vector; + TDBCursor* cursor; + TDBInt32 i; + TDBUint32 recordnum; + DBT key; + int dbstatus; + TDBBase* base = tdb->base; + TDBInt32 realnumlayers; + TDBInt32* reallayers; + + realnumlayers = tdb->numlayers; + reallayers = tdb->layers; + tdb->numlayers = 1; + tdb->layers = &(tdb->outlayer); + + cursor = tdbCursorNew(tdb, pickIndexType(tdb, triple, NULL), triple, + TDB_TRUE, TDB_FALSE); + if (!cursor) goto FAIL; + while (NULL != (vector = tdbCursorGetNext(cursor))) { + if (tdbVectorGetLayer(vector) != tdb->outlayer) continue; + memset(&key, 0, sizeof(key)); + recordnum = tdbVectorGetRecordNumber(vector); + key.data = &recordnum; + key.size = sizeof(recordnum); + dbstatus = base->recorddb->del(base->recorddb, base->transaction, + &key, 0); + if (dbstatus != DB_OK) { + tdbMarkCorrupted(base); + } + for (i=0 ; inumindices ; i++) { + if (tdbRTypeRemove(base->index[i], vector) != TDB_SUCCESS) { + tdbCursorFree(cursor); + tdbMarkCorrupted(base); + goto FAIL; + } + } + if (tdbWindexRemove(base->windex, vector) != TDB_SUCCESS) { + tdbCursorFree(cursor); + goto FAIL; + } + tdbCursorInvalidateCache(tdb); + } + tdbCursorFree(cursor); + tdbMarkDirty(base); + status = TDB_SUCCESS; + FAIL: + tdb->numlayers = realnumlayers; + tdb->layers = reallayers; + return status; +} + + + +TDBStatus +TDBRemove(TDB* tdb, TDBNodePtr triple[3]) +{ + TDBStatus status; + + if (tdb == NULL || triple == NULL) { + tdb_ASSERT(0); + return TDB_FAILURE; + } + + if (tdb->impl) { + return (*(tdb->impl->remove))(tdb, triple); + } + + tdbBeginExclusiveOp(tdb->base); + status = doRemove(tdb, triple); + if (tdb->base->corrupted) { + tdbRecover(tdb->base); + status = doRemove(tdb, triple); + } + tdbEndExclusiveOp(tdb->base); + + return status; +} + + + + +TDBStatus +TDBReplace(TDB* tdb, TDBNodePtr triple[3], TDBUint64 owner) +{ + /* Write me correctly!!! This works, but is inefficient. ### */ + + TDBStatus status; + TDBNodePtr tmp; + TDBBase* base; + if (tdb == NULL || + triple[0] == NULL || triple[1] == NULL || triple[2] == NULL) { + tdb_ASSERT(0); + return TDB_FAILURE; + } + if (tdb->impl) { + return (*(tdb->impl->replace))(tdb, triple, owner); + } + base = tdb->base; + tdbBeginExclusiveOp(base); + tmp = triple[2]; + triple[2] = NULL; + status = doRemove(tdb, triple); + triple[2] = tmp; + if (status == TDB_SUCCESS) { + status = doAdd(tdb, triple, owner, TDB_TRUE); + } + if (base->corrupted) { + tdbRecover(base); + triple[2] = NULL; + status = doRemove(tdb, triple); + if (status == TDB_SUCCESS) { + triple[2] = tmp; + status = doAdd(tdb, triple, owner, TDB_TRUE); + } + } + tdbEndExclusiveOp(base); + return status; +} + + + + +TDBCursor* TDBQuery(TDB* tdb, TDBNodePtr triple[3], + TDBSortSpecification* sortspec) +{ + TDBCursor* result; + void* implcursor; + if (tdb == NULL || triple == NULL) { + tdb_ASSERT(0); + return NULL; + } + if (tdb->impl) { + implcursor = (*(tdb->impl->query))(tdb, triple, sortspec); + if (!implcursor) return NULL; + return tdbCursorNewImpl(tdb, implcursor); + } + tdbBeginExclusiveOp(tdb->base); + result = tdbCursorNew(tdb, pickIndexType(tdb, triple, sortspec), + triple, + sortspec ? sortspec->includefalse : TDB_FALSE, + sortspec ? sortspec->reverse : TDB_FALSE); + tdbEndExclusiveOp(tdb->base); + return result; +} + + +TDBCursor* TDBQueryWordSubstring(TDB* tdb, const char* string) +{ + TDBCursor* result; + if (tdb == NULL || string == NULL) { + tdb_ASSERT(0); + return NULL; + } + tdbBeginExclusiveOp(tdb->base); + result = tdbCursorNewWordSubstring(tdb, string); + tdbEndExclusiveOp(tdb->base); + return result; +} + + +TDBTriple* TDBGetResult(TDBCursor* cursor) +{ + TDBBase* base; + TDB* tdb; + TDBTriple* result = NULL; + tdb_ASSERT(cursor); + if (!cursor) return NULL; + base = tdbCursorGetBase(cursor); + tdb = tdbCursorGetTDB(cursor); + if (tdb && tdb->impl) { + return (*(tdb->impl->getresult))(tdbCursorGetImplcursor(cursor)); + } + if (!base || !tdb) { + tdb_ASSERT(0); + return NULL; + } + tdbBeginExclusiveOp(base); + if (tdbCursorGetNext(cursor)) { + result = tdbCursorGetLastResultAsTriple(cursor); + } + tdbEndExclusiveOp(base); + return result; +} + + +TDBStatus +TDBFreeCursor(TDBCursor* cursor) +{ + TDBStatus status; + TDBBase* base; + void* implcursor; + if (cursor == NULL) { + tdb_ASSERT(0); + return TDB_FAILURE; + } + implcursor = tdbCursorGetImplcursor(cursor); + if (implcursor) { + TDB* tdb; + tdb = tdbCursorGetTDB(cursor); + if (!tdb->impl) { + tdb_ASSERT(0); + return TDB_FAILURE; + } + status = (*tdb->impl->freecursor)(implcursor); + if (status == TDB_SUCCESS) tdbCursorFree(cursor); + return status; + } + base = tdbCursorGetBase(cursor); + if (base == NULL) { + tdb_ASSERT(0); + return TDB_FAILURE; + } + tdbBeginExclusiveOp(base); + tdbCursorFree(cursor); + tdbEndExclusiveOp(base); + return TDB_SUCCESS; +} + + +TDB* TDBOpenImplementation(TDBImplement* impl) +{ + TDB* db; + tdb_ASSERT(impl != NULL); + if (impl == NULL) return NULL; + db = tdb_NEWZAP(TDB); + if (db == NULL) return NULL; + db->impl = impl; + return db; +} + +TDBStatus TDBSetImplData(TDB* db, void* impldata) +{ + if (db == NULL) { + tdb_ASSERT(0); + return TDB_FAILURE; + } + db->impldata = impldata; + return TDB_SUCCESS; +} + +void* TDBGetImplData(TDB* db) +{ + tdb_ASSERT(db != NULL); + return db ? db->impldata : NULL; +} + + +void +TDBValidateSortOrder(TDB* tdb, TDBSortSpecification* sortspec, + TDBNodePtr triple[3]) +{ + TDBRType* rtype; + TDBInt32 i; + TDBInt32 j; + TDBInt32 k; + TDBInt32 rangescore[3]; + TDBInt32 max; + TDBBool seen[3]; + if (triple == NULL || sortspec == NULL) { + tdb_ASSERT(0); + return; + } + if (tdb == NULL) { + /* Oh, well, ick. If anything legal-looking was specified, just use + that; otherwise, just pick the ordering with the highest score. */ + for (i=0 ; i<3 ; i++) { + seen[i] = TDB_FALSE; + } + for (i=0 ; i<3 ; i++) { + j = sortspec->keyorder[i]; + if (j >= 0 && j < 3) seen[j] = TDB_TRUE; + } + for (i=0 ; i<3 ; i++) { + if (!seen[i]) break; + } + if (i == 3) return; /* A legal sortspec was picked already. */ + fillRangeScore(rangescore, triple, 3); + for (i=0 ; i<3 ; i++) { + max = -9; + for (j=0 ; j<3 ; j++) { + if (max < rangescore[j]) { + max = rangescore[j]; + k = j; + } + } + sortspec->keyorder[i] = k; + rangescore[k] = -99; + } + return; + } + rtype = pickIndexType(tdb, triple, sortspec); + for (i=0 ; i<3 ; i++) { + tdbRTypeIndexGetField(rtype, i, &j); + sortspec->keyorder[i] = j; + } +} + + +TDBStatus +TDBGetTripleID(TDB* database, TDBNodePtr triple[3], TDBInt64* id) +{ + tdb_ASSERT(0); /* Write me! (or nuke me from the API) */ + return TDB_FAILURE; +} + + +TDBTriple* +TDBFindTripleFromID(TDB* database, TDBInt64 id) +{ + tdb_ASSERT(0); /* Write me! (or nuke me from the API) */ return NULL; } -PRStatus tdbLoadRoots(TDB* db) +void TDBFreeTriple(TDBTriple* triple) { - char buf[TDB_FILEHEADER_SIZE]; - char* ptr; - PRInt32 i, length; - PRFileInfo info; - - if (PR_Seek(db->fid, 0, PR_SEEK_SET) < 0) { - return PR_FAILURE; + int i; + if (!triple) { + tdb_ASSERT(0); + return; } - - i = 4 + sizeof(PRInt32) + (NUMTREES + 1) * sizeof(TDBPtr); - length = PR_Read(db->fid, buf, i); - if (length > 0 && length < i) { - /* Ick. This appears to be a short file. Punt. */ - return PR_FAILURE; + for (i=0 ; i<3 ; i++) { + if (triple->data[i]) TDBFreeNode(triple->data[i]); } - if (length > 0) { - if (memcmp(TDB_MAGIC, buf, 4) != 0) { - /* Bad magic number. */ - return PR_FAILURE; - } - ptr = buf + 4; - i = tdbGetInt32(&ptr); - if (i != TDB_VERSION) { - /* Bad version number. */ - return PR_FAILURE; - } - db->freeroot = tdbGetInt32(&ptr); - for (i=0 ; iroots[i] = tdbGetInt32(&ptr); - } - } else { - db->dirty = db->rootdirty = PR_TRUE; - tdbFlush(db); /* Write out the roots the first time, so - that we can accurately read the file - size and stuff. */ - } - PR_GetOpenFileInfo(db->fid, &info); - if (info.size < TDB_FILEHEADER_SIZE) { - int length = TDB_FILEHEADER_SIZE - info.size; - if (PR_Seek(db->fid, info.size, PR_SEEK_SET) < 0) { - return PR_FAILURE; - } - memset(buf, 0, length); - if (length != PR_Write(db->fid, buf, length)) { - return PR_FAILURE; - } - PR_GetOpenFileInfo(db->fid, &info); - PR_ASSERT(info.size == TDB_FILEHEADER_SIZE); - } - db->filelength = info.size; - return PR_SUCCESS; + tdb_Free(triple); } -PRStatus TDBClose(TDB* db) +char* +tdbGrowTmpBuf(TDBBase* base, TDBInt32 newsize) { - PRStatus result = PR_SUCCESS; - PR_ASSERT(db != NULL); - if (db == NULL) return PR_SUCCESS; - if (db->mutex) PR_Lock(db->mutex); - PR_ASSERT(db->firstcursor == NULL); - if (db->firstcursor != NULL) { - if (db->mutex) PR_Unlock(db->mutex); - return PR_FAILURE; + if (base == NULL || newsize <= 0) { + tdb_ASSERT(0); + return NULL; } - - if (db->dirty) { - tdbFlush(db); + if (newsize <= base->tmpbufsize) return base->tmpbuf; + base->tmpbuf = base->tmpbuf ? tdb_Realloc(base->tmpbuf, newsize) : + tdb_Malloc(newsize); + if (base->tmpbuf) { + base->tmpbufsize = newsize; + return base->tmpbuf; } + base->tmpbufsize = 0; + return NULL; +} - if (db->callbackthread) { - db->killcallbackthread = PR_TRUE; - PR_NotifyAllCondVar(db->callbackcvargo); - PR_Unlock(db->mutex); - PR_JoinThread(db->callbackthread); - PR_Lock(db->mutex); + +TDBInt32 +tdbGetTmpBufSize(TDBBase* base) +{ + if (base == NULL) { + tdb_ASSERT(0); + return -1; } - if (db->callbackcvargo) { - PR_DestroyCondVar(db->callbackcvargo); + return base->tmpbufsize; +} + + +DB* +tdbGetRecordDB(TDBBase* base) +{ + if (base == NULL) { + tdb_ASSERT(0); + return NULL; } - if (db->callbackcvaridle) { - PR_DestroyCondVar(db->callbackcvaridle); + return base->recorddb; +} + + +TDBStatus +tdbRecover(TDBBase* base) +{ + TDBStatus status; + char* filename; + char* newfile; + DBC* cursor; + TDBInt32 i; + TDBVector* vector; + TDBBase* outbase; + TDB* outtdb[256]; + int dbstatus; + DBT key; + DBT data; + TDBNodePtr nodes[3]; + TDBUint32 recordnum; + TDBUint64 owner; + TDBInt32 layer; + if (base == NULL || base->filename == NULL) { + tdb_ASSERT(0); + return TDB_FAILURE; } - if (db->mutex) { - PR_Unlock(db->mutex); - PR_DestroyLock(db->mutex); + memset(outtdb, 0, sizeof(outtdb)); + filename = tdb_strdup(base->filename); + if (!filename) return TDB_FAILURE; + if (base->toobustedtorecover) { + closeGuts(base); + TDBBlowAwayDatabaseFiles(filename); + } else { + newfile = tdb_Malloc(strlen(filename) + 50); + if (!newfile) return TDB_FAILURE; + strcpy(newfile, filename); + strcat(newfile, "-recover-"); + sprintf(newfile + strlen(newfile), "%d", getpid()); + outbase = TDBOpenBase(newfile); + if (!outbase) return TDB_FAILURE; + dbstatus = base->recorddb->cursor(base->recorddb, NULL, &cursor, 0); + if (dbstatus != DB_OK) return TDB_FAILURE; + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + while (DB_OK == cursor->c_get(cursor, &key, &data, DB_NEXT)) { + memcpy(&recordnum, key.data, sizeof(recordnum)); + vector = tdbVectorNewFromRecord(base, recordnum); + if (vector) { + for (i=0 ; i<3 ; i++) { + nodes[i] = tdbVectorGetNonInternedNode(vector, i); + } + owner = tdbVectorGetOwner(vector); + layer = tdbVectorGetLayer(vector); + if (outtdb[layer] == NULL) { + outtdb[layer] = TDBOpenLayers(outbase, 1, &layer); + } + if (tdbVectorGetFlags(vector) & TDBFLAG_ISASSERT) { + TDBAdd(outtdb[layer], nodes, owner); + } else { + TDBAddFalse(outtdb[layer], nodes, owner); + } + tdbVectorFree(vector); + } + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + } + cursor->c_close(cursor); + for (layer = 0 ; layer < 256 ; layer++) { + if (outtdb[layer]) { + TDBClose(outtdb[layer]); + outtdb[layer] = NULL; + } + } + TDBCloseBase(outbase); + closeGuts(base); + TDBBlowAwayDatabaseFiles(filename); + tdb_Rename(newfile, filename); + tdb_Free(newfile); } - if (db->fid) { - result = PR_Close(db->fid); + status = openGuts(base, filename); + tdb_Free(filename); + if (base->corrupted) { + /* Looks like recovery didn't work after all. Yuck! */ + status = TDB_FAILURE; } - PR_Free(db->filename); - PR_Free(db); - return result; + return status; } - -PRStatus TDBRebuildDatabase(TDB* db) +TDBWindex* +tdbGetWindex(TDBBase* base) { - PRStatus status; - char* tmpfile; - PRFileInfo info; - PRInt32 count = 0; - TDB* tmpdb; + if (base == NULL) { + tdb_ASSERT(0); + return NULL; + } + return base->windex; +} - PR_ASSERT(db != NULL); - if (db == NULL) return PR_SUCCESS; - PR_Lock(db->mutex); - do { - tmpfile = PR_smprintf("%s-tmp-%d", db->filename, ++count); - status = PR_GetFileInfo(tmpfile, &info); - } while (status == PR_SUCCESS); - tmpdb = TDBOpen(tmpfile); - if (!tmpdb) return PR_FAILURE; - /* ### Finish writing me! */ + +TDBCallbackInfo* +tdbGetFirstCallback(TDBBase* base) +{ + if (base == NULL) { + tdb_ASSERT(0); + return NULL; + } + return base->firstcallback; +} + +void +tdbSetFirstCallback(TDBBase* base, TDBCallbackInfo* call) +{ + if (base == NULL) { + tdb_ASSERT(0); + return; + } + base->firstcallback = call; - return PR_FAILURE; +} + +TDBPendingCall* +tdbGetFirstPendingCall(TDBBase* base) +{ + if (base == NULL) { + tdb_ASSERT(0); + return NULL; + } + return base->firstpendingcall; +} + +void +tdbSetFirstPendingCall(TDBBase* base, TDBPendingCall* call) +{ + if (base == NULL) { + tdb_ASSERT(0); + return; + } + base->firstpendingcall = call; +} + +TDBPendingCall* +tdbGetLastPendingCall(TDBBase* base) +{ + if (base == NULL) { + tdb_ASSERT(0); + return NULL; + } + return base->lastpendingcall; +} + +void +tdbSetLastPendingCall(TDBBase* base, TDBPendingCall* call) +{ + if (base == NULL) { + tdb_ASSERT(0); + return; + } + base->lastpendingcall = call; +} + +DB_ENV* +tdbGetDBEnv(TDBBase* base) +{ + if (base == NULL) { + tdb_ASSERT(0); + return NULL; + } + return base->env; +} + +TDBIntern* +tdbGetIntern(TDBBase* base) +{ + if (base == NULL) { + tdb_ASSERT(0); + return NULL; + } + return base->intern; +} + +DB_TXN* +tdbGetTransaction(TDBBase* base) +{ + if (base == NULL) { + tdb_ASSERT(0); + return NULL; + } + return base->transaction; } -PRStatus tdbFlush(TDB* db) + +TDB* +TDBOpenLayers(TDBBase* base, TDBInt32 numlayers, TDBInt32* layers) { - PRInt32 length; - PRInt32 i; - PRStatus status; - TDBRecord* record; - TDBRecord** recptr; - char* ptr; - PR_ASSERT(db != NULL); - if (db == NULL) return PR_FAILURE; - if (db->rootdirty) { - PR_ASSERT(db->dirty); - if (PR_Seek(db->fid, 0, PR_SEEK_SET) < 0) { - return PR_FAILURE; - } - length = 4 + sizeof(PRInt32) + (NUMTREES + 1) * sizeof(TDBPtr); - status = tdbGrowIobuf(db, length); - if (status != PR_SUCCESS) return status; - ptr = db->iobuf; - memcpy(ptr, TDB_MAGIC, 4); - ptr += 4; - tdbPutInt32(&ptr, TDB_VERSION); - tdbPutInt32(&ptr, db->freeroot); - for (i=0 ; iroots[i]); - } - PR_ASSERT(ptr - db->iobuf <= TDB_FILEHEADER_SIZE); - if (PR_Write(db->fid, db->iobuf, ptr - db->iobuf) != ptr - db->iobuf) { - return PR_FAILURE; - } - db->rootdirty = PR_FALSE; + TDB* tdb; + TDBInt32 i; + if (base == NULL || layers == NULL || numlayers <= 0) { + tdb_ASSERT(0); + return NULL; } - recptr = &(db->firstrecord); - while (*recptr) { - record = *recptr; - if (record->dirty) { - PR_ASSERT(db->dirty); - PR_ASSERT(record->refcnt == 0); - status = tdbSaveRecord(db, record); - if (status != PR_SUCCESS) return status; - } - if (record->refcnt == 0) { - *recptr = record->next; - status = tdbFreeRecord(record); - if (status != PR_SUCCESS) return status; - } else { - PR_ASSERT(record->refcnt > 0); - recptr = (&record->next); - } + tdb = tdb_NEWZAP(TDB); + if (!tdb) return NULL; + tdb->numlayers = numlayers; + tdb->layers = tdb_Malloc(sizeof(TDBInt32) * numlayers); + if (tdb->layers == NULL) { + tdb_Free(tdb); + return NULL; } - db->dirty = PR_FALSE; - return PR_SUCCESS; + for (i=0 ; ilayers[i] = layers[i]; + } + tdb->outlayer = layers[0]; + tdb->base = base; + tdbBeginExclusiveOp(base); + tdb->nexttdb = base->firsttdb; + base->firsttdb = tdb; + tdbEndExclusiveOp(base); + return tdb; } - -PRStatus tdbThrowAwayUnflushedChanges(TDB* db) +TDBStatus +TDBClose(TDB* tdb) { - TDBRecord* record; - db->dirty = PR_FALSE; - db->rootdirty = PR_FALSE; - for (record = db->firstrecord ; record ; record = record->next) { - record->dirty = PR_FALSE; + TDBStatus status; + TDB** ptr; + if (tdb == NULL) { + tdb_ASSERT(0); + return TDB_FAILURE; } - tdbFlush(db); - return tdbLoadRoots(db); -} - - -PRStatus tdbGrowIobuf(TDB* db, PRInt32 length) -{ - if (length > db->iobuflength) { - db->iobuflength = length; - if (db->iobuf == NULL) { - db->iobuf = PR_Malloc(db->iobuflength); - } else { - db->iobuf = PR_Realloc(db->iobuf, db->iobuflength); - } - if (db->iobuf == NULL) { - return PR_FAILURE; + if (tdb->impl) { + status = tdb->impl->close(tdb); + tdb_Free(tdb); + return status; + } + tdb_ASSERT(tdb->firstcursor == NULL); + tdbBeginExclusiveOp(tdb->base); + for (ptr = &(tdb->base->firsttdb) ; *ptr ; ptr = &((*ptr)->nexttdb)) { + if (*ptr == tdb) { + *ptr = tdb->nexttdb; + break; } } - return PR_SUCCESS; + tdbEndExclusiveOp(tdb->base); + tdb_Free(tdb->layers); + tdb_Free(tdb); + return TDB_SUCCESS; } - -void tdbMarkCorrupted(TDB* db) +TDBBase* +tdbGetBase(TDB* tdb) { - PR_ASSERT(0); - /* ### Write me!!! ### */ + if (tdb == NULL) { + tdb_ASSERT(0); + return NULL; + } + return tdb->base; } - -PRInt32 tdbGetInt32(char** ptr) +TDBInt32 +tdbGetNumIndices(TDBBase* base) { - PRInt32 result; - memcpy(&result, *ptr, sizeof(PRInt32)); - *ptr += sizeof(PRInt32); - return result; + if (base == NULL) { + tdb_ASSERT(0); + return -1; + } + return base->numindices; } -PRInt32 tdbGetInt16(char** ptr) +TDBRType* +tdbGetIndex(TDBBase* base, TDBInt32 which) { - PRInt16 result; - memcpy(&result, *ptr, sizeof(PRInt16)); - *ptr += sizeof(PRInt16); - return result; + if (base == NULL || which < 0 || which >= base->numindices) { + tdb_ASSERT(0); + return NULL; + } + return base->index[which]; } -PRInt8 tdbGetInt8(char** ptr) +TDBInt32 +tdbGetNumLayers(TDB* tdb) { - PRInt8 result; - memcpy(&result, *ptr, sizeof(PRInt8)); - *ptr += sizeof(PRInt8); - return result; + if (tdb == NULL) { + tdb_ASSERT(0); + return 0; + } + return tdb->numlayers; } -PRInt64 tdbGetInt64(char** ptr) +TDBInt32 +tdbGetLayer(TDB* tdb, TDBInt32 which) { - PRInt64 result; - memcpy(&result, *ptr, sizeof(PRInt64)); - *ptr += sizeof(PRInt64); - return result; + if (tdb == NULL || which < 0 || which >= tdb->numlayers) { + tdb_ASSERT(0); + return -1; + } + return tdb->layers[which]; } -PRUint16 tdbGetUInt16(char** ptr) + +TDBInt32 +tdbGetOutLayer(TDB* tdb) { - PRUint16 result; - memcpy(&result, *ptr, sizeof(PRUint16)); - *ptr += sizeof(PRUint16); - return result; + if (tdb == NULL) { + tdb_ASSERT(0); + return -1; + } + return tdb->outlayer; } - -void tdbPutInt32(char** ptr, PRInt32 value) -{ - memcpy(*ptr, &value, sizeof(PRInt32)); - *ptr += sizeof(PRInt32); -} - -void tdbPutInt16(char** ptr, PRInt16 value) -{ - memcpy(*ptr, &value, sizeof(PRInt16)); - *ptr += sizeof(PRInt16); -} - -void tdbPutUInt16(char** ptr, PRUint16 value) -{ - memcpy(*ptr, &value, sizeof(PRUint16)); - *ptr += sizeof(PRUint16); -} - -void tdbPutInt8(char** ptr, PRInt8 value) -{ - memcpy(*ptr, &value, sizeof(PRInt8)); - *ptr += sizeof(PRInt8); -} - -void tdbPutInt64(char** ptr, PRInt64 value) -{ - memcpy(*ptr, &value, sizeof(PRInt64)); - *ptr += sizeof(PRInt64); -} - - -/* - * The below was an attempt to write all these routines in a nice way, so - * that the database would be platform independent. But I keep choking - * sign bits and things. I give up for now. These probably should be - * resurrected and fixed up. - * - * PRInt32 tdbGetInt32(char** ptr) - * { - * PRInt32 result = ((*ptr)[0] << 24) | ((*ptr)[1] << 16) | - * ((*ptr)[2] << 8) | (*ptr)[3]; - * (*ptr) += 4; - * return result; - * } - * - * PRInt32 tdbGetInt16(char** ptr) - * { - * PRInt16 result = ((*ptr)[0] << 8) | (*ptr)[1]; - * (*ptr) += 2; - * return result; - * } - * - * PRUint16 tdbGetUInt16(char** ptr) - * { - * PRUint16 result = ((*ptr)[0] << 8) | (*ptr)[1]; - * (*ptr) += 2; - * return result; - * } - * - * PRInt8 tdbGetInt8(char** ptr) - * { - * return (PRInt8) (*((*ptr)++)); - * } - * - * - * PRInt64 tdbGetInt64(char** ptr) - * { - * PRInt64 a = tdbGetInt32(ptr); - * return (a << 32) | tdbGetInt32(ptr); - * } - * - * - * void tdbPutInt32(char** ptr, PRInt32 value) - * { - * *((*ptr)++) = (value >> 24) & 0xff; - * *((*ptr)++) = (value >> 16) & 0xff; - * *((*ptr)++) = (value >> 8) & 0xff; - * *((*ptr)++) = (value) & 0xff; - * } - * - * - * void tdbPutInt16(char** ptr, PRInt16 value) - * { - * *((*ptr)++) = (value >> 8) & 0xff; - * *((*ptr)++) = (value) & 0xff; - * } - * - * void tdbPutUInt16(char** ptr, PRUint16 value) - * { - * *((*ptr)++) = (value >> 8) & 0xff; - * *((*ptr)++) = (value) & 0xff; - * } - * - * void tdbPutInt8(char** ptr, PRInt32 value) - * { - * *((*ptr)++) = value; - * } - * - * void tdbPutInt64(char** ptr, PRInt64 value) - * { - * tdbPutInt32(ptr, ((PRInt32) (value >> 32))); - * tdbPutInt32(ptr, ((PRInt32) (value & 0xffffffff))); - * } - */ diff --git a/db/tripledb/src/tdb.h b/db/tripledb/src/tdb.h new file mode 100644 index 00000000000..bb1f14ceec0 --- /dev/null +++ b/db/tripledb/src/tdb.h @@ -0,0 +1,117 @@ +/* -*- Mode: C; indent-tabs-mode: nil; -*- + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the TripleDB database library. + * + * The Initial Developer of the Original Code is Geocast Network Systems. + * Portions created by Geocast are + * Copyright (C) 2000 Geocast Network Systems. All + * Rights Reserved. + * + * Contributor(s): Terry Weissman + */ + +#ifndef _tdb_h_ +#define _tdb_h_ 1 + + +/* tdbGetFID() gets the file descripter for the database file. */ + +extern TDBFileDesc* tdbGetFID(TDBBase* base); + + +/* tdbMarkCorrupted() marks the tdb as being corrupted. */ + +extern void tdbMarkCorrupted(TDBBase* base); + + +/* tdbMarkTooBustedToRecover() marks the tdb as being so corrupted that we + shouldn't try to recover from it. */ + +extern void tdbMarkTooBustedToRecover(TDBBase* base); + + +/* tdbGetFileLength() gets the current length of the file. */ + +extern TDBPtr tdbGetFileLength(TDBBase* base); + + +/* tdbSetFileLength() sets in memory a new idea as to the current + length of the file. Should only be used by the page.c module, when + it adds a new page to the file. */ + +extern TDBStatus tdbSetFileLength(TDBBase* base, TDBPtr length); + + + +/* tdbGetFirstRType() returns the first rtype. This should only be used by + the rtype.c module. */ + +extern TDBRType* tdbGetFirstRType(TDBBase* base); + + +/* tdbSetFirstRType() sets the first rtype. This should only be used by + the rtype.c module. */ + +extern TDBStatus tdbSetFirstRType(TDBBase* base, TDBRType* rtype); + + +/* tdbGetFirstCursor() returns the first cursor. This should only be used by + the cursor.c module. */ + +extern TDBCursor* tdbGetFirstCursor(TDB* tdb); + + +/* tdbSetFirstCursor() sets the first cursor. This should only be used by + the cursor.c module. */ + +extern TDBStatus tdbSetFirstCursor(TDB* tdb, TDBCursor* cursor); + + +extern void tdbBeginExclusiveOp(TDBBase* base); + +extern void tdbEndExclusiveOp(TDBBase* base); + +extern char* tdbGrowTmpBuf(TDBBase* base, TDBInt32 newsize); + +extern TDBInt32 tdbGetTmpBufSize(TDBBase* base); + +extern DB* tdbGetRecordDB(TDBBase* base); + +extern TDBBase* tdbGetBase(TDB* base); + +extern TDBStatus tdbRecover(TDBBase* base); + +extern TDBWindex* tdbGetWindex(TDBBase* base); + +extern TDBPendingCall* tdbGetFirstPendingCall(TDBBase* base); + +extern void tdbSetFirstPendingCall(TDBBase* base, TDBPendingCall* call); + +extern TDBPendingCall* tdbGetLastPendingCall(TDBBase* base); + +extern void tdbSetLastPendingCall(TDBBase* base, TDBPendingCall* call); + +extern DB_ENV* tdbGetDBEnv(TDBBase* base); + +extern TDBIntern* tdbGetIntern(TDBBase* base); + +extern DB_TXN* tdbGetTransaction(TDBBase* base); + +extern TDBInt32 tdbGetNumIndices(TDBBase* base); +extern TDBRType* tdbGetIndex(TDBBase* base, TDBInt32 which); + +extern TDBInt32 tdbGetNumLayers(TDB* tdb); +extern TDBInt32 tdbGetLayer(TDB* tdb, TDBInt32 which); +extern TDBInt32 tdbGetOutLayer(TDB* tdb); + +#endif /* _tdb_h_ */ diff --git a/db/tripledb/src/tdbapi.h b/db/tripledb/src/tdbapi.h index cf7dedccf58..69cce232a98 100644 --- a/db/tripledb/src/tdbapi.h +++ b/db/tripledb/src/tdbapi.h @@ -14,7 +14,7 @@ * * The Initial Developer of the Original Code is Geocast Network Systems. * Portions created by Geocast are - * Copyright (C) 1999 Geocast Network Systems. All + * Copyright (C) 2000 Geocast Network Systems. All * Rights Reserved. * * Contributor(s): Terry Weissman @@ -24,7 +24,83 @@ #define _TDBapi_h_ 1 /* All things are prefixed with TDB, which stands for "Triples - DataBase". Suggestions for better names are always welcome. */ + DataBase". */ + + +/* TDB can be built with or without NSPR. If built with NSPR, then it can be + built with thread support. If you want to built it with some non-NSPR + threading package, you'll have to do some hacking. */ + +#define TDB_USE_NSPR 1 +#define TDB_USE_THREADS 1 + + + +#ifdef TDB_USE_NSPR +#include "prtypes.h" +#include "prtime.h" +#include "prmem.h" + +typedef PRInt16 TDBInt16; +typedef PRInt32 TDBInt32; +typedef PRInt64 TDBInt64; +typedef PRInt8 TDBInt8; +typedef PRTime TDBTime; +typedef PRUint8 TDBUint8; +typedef PRUint16 TDBUint16; +typedef PRUint32 TDBUint32; +typedef PRUint64 TDBUint64; + +typedef PRBool TDBBool; +#define TDB_TRUE PR_TRUE +#define TDB_FALSE PR_FALSE + +#define TDB_BEGIN_EXTERN_C PR_BEGIN_EXTERN_C +#define TDB_END_EXTERN_C PR_END_EXTERN_C +#define TDB_EXTERN(t) PR_EXTERN(t) + +typedef struct PRFileDesc TDBFileDesc; + +#define tdb_NEWZAP(x) PR_NEWZAP(x) +#define tdb_Malloc PR_Malloc +#define tdb_Calloc(x,y) PR_Calloc(x,y) +#define tdb_Realloc(x,y) PR_Realloc(x,y) +#define tdb_Free(x) PR_Free(x) + +#else + +#include + +typedef short TDBInt16; +typedef int TDBInt32; +typedef long long TDBInt64; +typedef signed char TDBInt8; +typedef TDBInt64 TDBTime; +typedef unsigned char TDBUint8; +typedef unsigned short TDBUint16; +typedef unsigned long TDBUint32; +typedef unsigned long long TDBUint64; + +typedef int TDBBool; +#define TDB_TRUE 1 +#define TDB_FALSE 0 + +#define TDB_BEGIN_EXTERN_C +#define TDB_END_EXTERN_C +#define TDB_EXTERN(t) extern t + +#define TDBFileDesc FILE + +#define tdb_NEWZAP(x) ((x*)calloc(1, sizeof(x))) +#define tdb_Malloc malloc +#define tdb_Calloc(x,y) calloc(x,y) +#define tdb_Realloc(x,y) realloc(x,y) +#define tdb_Free(x) free(x) + +#endif /* TDB_USE_NSPR */ + + +typedef enum { TDB_FAILURE = -1, TDB_SUCCESS = 0 } TDBStatus; /* A TDBNode contains one of the three items in a triple. This is a structure that defines a very basic type, strings or ints or dates. @@ -32,29 +108,35 @@ then this is where we muck things. It is important that all nodes be strictly ordered. All the integer values - sort together in the obvious way. PRTimes get sorted with them by treating - them as if they were PRInt64's. All strings are considered to be greater + sort together in the obvious way. TDBTimes get sorted with them by treating + them as if they were TDBInt64's. All strings are considered to be greater than all integers. */ -#define TDBTYPE_INT8 1 /* 8-bit signed integer */ -#define TDBTYPE_INT16 2 /* 16-bit signed integer */ -#define TDBTYPE_INT32 3 /* 32-bit signed integer */ -#define TDBTYPE_INT64 4 /* 64-bit signed integer */ -#define TDBTYPE_TIME 5 /* NSPR date&time stamp (PRTime) */ -#define TDBTYPE_STRING 6 /* A string (up to 65535 chars long) */ +#define TDBTYPE_INT32 1 /* 32-bit signed integer */ +#define TDBTYPE_INT64 2 /* 64-bit signed integer */ +#define TDBTYPE_ID 3 /* A 64-bit unsigned identifier, generally used + to represent an RDF resource. */ +#define TDBTYPE_TIME 4 /* NSPR-style date&time stamp -- number of + microseconds since midnight, + 1/1/1970, GMT. */ +#define TDBTYPE_STRING 5 /* A string (up to 65535 chars long) */ +#define TDBTYPE_BLOB 6 /* A blob, which is just like a string, except + it is not considered to have searchable text + in it.) */ typedef struct { - PRInt8 type; + TDBInt8 type; union { - PRInt64 i; /* All the int types are stored here, as an - Int64. The type just indicates how it is to be - stored in the database. */ + TDBInt64 i; /* All the int types are stored here, as an + Int64. The type just indicates how it is to be + stored in the database. */ + TDBUint64 id; /* Unsigned 64-bit identifier. */ struct { - PRUint16 length; + TDBUint16 length; char string[1]; - } str; - PRTime time; + } str; /* Used for both blobs and strings. */ + TDBTime time; } d; } TDBNode, *TDBNodePtr; @@ -65,36 +147,30 @@ typedef struct { typedef struct { TDBNodePtr data[3]; + TDBBool asserted; /* If TRUE, then this is a normal triple. + If FALSE, then this is a triple that we + have explicitely turned off using + TDBAddFalse(). The only way to get such + triples from a query is to turn on the + includefalse member of the + TDBSortSpecification passed to + TDBQuery(). */ } TDBTriple; -/* A TDBNodeRange specifies a range of values for a node. If min is - not NULL, then this matches only nodes that are greater than or - equal to min. If max is not NULL, then this matches only nodes - that are less than or equal to max. Therefore, if both min and - max are NULL, then this specifies the range of all possible nodes. - If min and max are not NULL, and are the same value, then it specifies - exactly that value. */ - -typedef struct { - TDBNodePtr min; - TDBNodePtr max; -} TDBNodeRange; - - /* A TDBSortSpecification specifies what order results from a request should come in. I suspect that there will someday be much more to it than this. */ typedef struct { - PRBool reverse; /* If true, then return biggest results + TDBBool reverse; /* If true, then return biggest results first. Otherwise, the smallest stuff comes first. */ - PRInt16 keyorder[3]; /* Specify which keys to sort in. If you use + TDBInt16 keyorder[3]; /* Specify which keys to sort in. If you use this, then each of the three entries must be a unique number between 0 and 2. For example, if: - keyorder[0] == 1 + keyorder[0] == 1 keyorder[1] == 2 keyorder[2] == 0 then results will be returned sorted @@ -115,19 +191,22 @@ typedef struct { pick the order it can do most efficiently.) - Practically speaking, the only reason to - ever set the keyorder is if your request - only gives values for one of the ranges, - and you want to specify which order the - other fields should be sorted in. And - keyorder[0] had better specify which - entry is the non-NULL one, or things will - be very slow. + Practically speaking, there is currently + no reason to ever set this stuff. */ + TDBBool includefalse; /* Whether this query should include entries + that were added using TDBAddFalse(). If + so, such entries will have the asserted + field the TDBTriple structure turned off. */ } TDBSortSpecification; -/* A TDB* is an opaque pointer that represents an entire database. */ +/* A TDBBase* is an opaque pointer that represents an entire database. */ + +typedef struct _TDBBase TDBBase; + + +/* A TDB* is an opaque pointer that represents a view on a database. */ typedef struct _TDB TDB; @@ -139,109 +218,162 @@ typedef struct _TDB TDB; typedef struct _TDBCursor TDBCursor; -/* TDBCallbackFunction is for callbacks notifying of certain database - changes. */ - -typedef void (*TDBCallbackFunction)(TDB* database, void* closure, - TDBTriple* triple, - PRInt32 action); /* One of the below - TDBACTION_* values. */ - -#define TDBACTION_ADDED 1 /* The given triple has been added to the - database. */ -#define TDBACTION_REMOVED 2 /* The given triple has been removed from the - database. */ -PR_BEGIN_EXTERN_C +TDB_BEGIN_EXTERN_C -/* Open a database (creating it if non-existant). Returns NULL on failure. */ +/* TDBOpenBase() opens a database from a file (creating it if non-existant). + Returns NULL on failure. */ -PR_EXTERN(TDB*) TDBOpen(const char* filename); +TDB_EXTERN(TDBBase*) TDBOpenBase(const char* filename); -/* Close an opened database. Frees the storage for TDB; you may not use - that pointer after that call. Will flush out any changes that have - been made. This call will fail if you have not freed up all of the - cursors that were created with TDBQuery. */ +/* TDBOpenLayers() opens a database, looking at the given layers. The layers + are given in priority order (earlier listed layers override later ones). + The first layer is the one that is to be changed by any calls that + add or remove triples. -PR_EXTERN(PRStatus) TDBClose(TDB* database); + ### Need to add here or somewhere a lecture on what 'layers' are all + about. */ + +TDB_EXTERN(TDB*) TDBOpenLayers(TDBBase* base, TDBInt32 numlayers, + TDBInt32* layers); + + + +/* TDBBlowAwayDatabaseFiles() blows away all database files associated with + the given pathname. This very much is throwing away real data; use this + call with care! */ + +TDB_EXTERN(TDBStatus) TDBBlowAwayDatabaseFiles(const char* filename); + + + +/* TDBGetFilename() returns the filename associated with the database. The + returned string should not be modified in any way. */ + +const char* TDBGetFilename(TDB* database); + + + +/* TDBClose() closes an opened database. Frees the storage for TDB; you may + not use that pointer after that call. Will flush out any changes that have + been made. This call will fail if you have not freed up all of the cursors + that were created with TDBQuery. */ + +TDB_EXTERN(TDBStatus) TDBClose(TDB* database); + + + +/* TDBCloseBase() closes the base database file. This call will fail if you + have not freed up all of the database views that were created with + TDBOpenLayers(). */ + +TDB_EXTERN(TDBStatus) TDBCloseBase(TDBBase* base); + + +/* TDBSync() makes sure that any changes made to the database have been written + out to disk. It effectively gets called by TDBClose(), and may also be + implicitely called from time to time. */ + +TDB_EXTERN(TDBStatus) TDBSync(TDB* database); -/* TDBQuery() returns a cursor that you can use with TDBNextResult() to get + +#ifdef TDB_USE_THREADS + +/* TDBGetBG() returns the TDBBG* object (see tdbbg.h) that represents the + thread tripledb uses for its background operations. If you would like + that thread to do some other background operations, you can queue them + up. That can interfere with the performance of tripledb, so you may + want to create your own TDBBG* object instead. */ + +TDB_EXTERN(struct _TDBBG*) TDBGetBG(TDB* database); + +#endif + + +/* TDBQuery() returns a cursor that you can use with TDBGetResult() to get the results of a query. It will return NULL on failure. If the query is legal, but there are no matching results, this will *not* return NULL; it will return a cursor that will have no results. - If a single value is specified for both range[0] and range[1], then the - results are guaranteed to be sorted by range[2]. If a single value is - specified for both range[1] and range[2], then the results are guaranteed - to be sorted by range[0]. No other sorting rules are promised (at least, - not yet.) + If a member of a the given triple is not NULL, then only triples with the + identical value in that position will be returned. If it is NULL, then + all possible triples are returned. - A NULL TDBSortSpecification can be provided, which will sort in the - default manner. */ - -PR_EXTERN(TDBCursor*) TDBQuery(TDB* database, TDBNodeRange range[3], - TDBSortSpecification* sortspec); + If the only variation in triples are values that are of type + TDBTYPE_INT24, then the triples will be sorted by those values. + If a non-NULL sortspec is passed in, and it has the "reverse" field set, + then these int24's will be sorted in descending order; otherwise, they + will be ascending. -/* TDBGetResult returns the next result that matches the cursor, and + A NULL TDBSortSpecification can be provided, which will make the query + behave in the default manner. */ + +TDB_EXTERN(TDBCursor*) TDBQuery(TDB* database, TDBNodePtr triple[3], + TDBSortSpecification* sortspec); + + +/* TDBQueryWordSubstring() returns a cursor of all triples whose last element + is a string and matches the given string. It will only match by full words; + that is, if you provide a partial word, it will not match strings that + contain that word inside another one. It will only return strings which + contain the given string as a substring, ignoring case. */ + +TDB_EXTERN(TDBCursor*) TDBQueryWordSubstring(TDB* database, + const char* string); + + +/* TDBGetResult() returns the next result that matches the cursor, and advances the cursor to the next matching entry. It will return NULL when there are no more matching entries. The returned triple must not be modified in any way by the caller, and is valid only until the next call to TDBGetResult(), or until the cursor is freed. */ -PR_EXTERN(TDBTriple*) TDBGetResult(TDBCursor* cursor); +TDB_EXTERN(TDBTriple*) TDBGetResult(TDBCursor* cursor); + + +/* TDBCursorGetTDB() returns the base TDB* object that the given cursor is + working on. */ + +TDB_EXTERN(TDB*) TDBCursorGetTDB(TDBCursor* cursor); /* TDBFreeCursor frees the cursor. */ -PR_EXTERN(PRStatus) TDBFreeCursor(TDBCursor* cursor); - - -/* TDBAddCallback calls the given function whenever a change to the database - is made that matches the given range. Note that no guarantees are made - about which thread this call will be done in. It is also possible (though - not very likely) that by the time the callback gets called, further changes - may have been made to the database, and the action being notified here has - already been undone. (If that happens, though, you will get another - callback soon.) - - It is legal for the receiver of the callback to make any queries or - modifications it wishes to the database. It is probably not a good idea - to undo the action being notified about; this kind of policy leads to - infinite loops. - - The receiver should not take unduly long before returning from the callback; - until the callback returns, no other callbacks will occur. - */ - -PR_EXTERN(PRStatus) TDBAddCallback(TDB* database, TDBNodeRange range[3], - TDBCallbackFunction func, void* closure); - -/* TDBRemoveCallback will remove a callback that was earlier registered with a - call to TDBAddCallback. */ - -PR_EXTERN(PRStatus) TDBRemoveCallback(TDB* database, TDBNodeRange range[3], - TDBCallbackFunction func, void* closure); +TDB_EXTERN(TDBStatus) TDBFreeCursor(TDBCursor* cursor); /* TDBRemove() removes all entries matching the given parameters from the database. */ -PR_EXTERN(PRStatus) TDBRemove(TDB* database, TDBNodeRange range[3]); +TDB_EXTERN(TDBStatus) TDBRemove(TDB* database, TDBNodePtr triple[3]); -/* TDBAdd() adds a triple into the database. */ +/* TDBAdd() adds a triple into the database. The "owner" of each triple + is recorded, so that we can later remove all things owned by a given + owner. */ -PR_EXTERN(PRStatus) TDBAdd(TDB* database, TDBNodePtr triple[3]); +TDB_EXTERN(TDBStatus) TDBAdd(TDB* database, TDBNodePtr triple[3], + TDBUint64 owner); + + +/* TDBAddFalse() adds a triple into the database that explicitely asserts that + this triple is *not* to be considered part of the database. It is useful + when this database gets merged into a bigger database (using + TDBOpenMergedDatabase()). In that case, this triple will not be returned + by queries, even if it appears in an earlier database on the merge list. */ + +TDB_EXTERN(TDBStatus) TDBAddFalse(TDB* database, TDBNodePtr triple[3], + TDBUint64 owner); /* TDBReplace() looks for an existing entry that matches triple[0] and @@ -251,7 +383,8 @@ PR_EXTERN(PRStatus) TDBAdd(TDB* database, TDBNodePtr triple[3]); sense if you know up-front that there is no more than one existing triple with the given "subject" and "verb". */ -PR_EXTERN(PRStatus) TDBReplace(TDB* database, TDBNodePtr triple[3]); +TDB_EXTERN(TDBStatus) TDBReplace(TDB* database, TDBNodePtr triple[3], + TDBUint64 owner); @@ -259,7 +392,7 @@ PR_EXTERN(PRStatus) TDBReplace(TDB* database, TDBNodePtr triple[3]); allocates a new TDBNode that represents the given string. The TDBNode can be free'd using TDBFreeNode(). */ -PR_EXTERN(TDBNodePtr) TDBCreateStringNode(const char* string); +TDB_EXTERN(TDBNodePtr) TDBCreateStringNode(const char* string); /* TDBCreateIntNode() is just a utility routine that correctly @@ -267,13 +400,28 @@ PR_EXTERN(TDBNodePtr) TDBCreateStringNode(const char* string); TDBNode can be free'd using TDBFreeNode(). You must specify the correct TDBTYPE_* value for it. */ -PR_EXTERN(TDBNodePtr) TDBCreateIntNode(PRInt64 value, PRInt8 type); +TDB_EXTERN(TDBNodePtr) TDBCreateIntNode(TDBInt64 value, TDBInt8 type); /* Free up a node created with TDBCreateStringNode or TDBCreateIntNode. */ -PR_EXTERN(void) TDBFreeNode(TDBNodePtr node); +TDB_EXTERN(void) TDBFreeNode(TDBNodePtr node); -PR_END_EXTERN_C +/* TDBCompareNodes() compares two nodes. Returns negative if the first is less + than the second, zero if they are equal, positive if the first is greater. + Note That this returns a 64-bit int; be careful! */ + +TDB_EXTERN(TDBInt64) TDBCompareNodes(TDBNode* n1, TDBNode* n2); + + +/* TDBNodeDup allocates a new node object, and initializes it to have the + same value as the given object. Returns NULL on failure. */ + +TDB_EXTERN(TDBNodePtr) TDBNodeDup(TDBNodePtr node); + + + + +TDB_END_EXTERN_C #endif /* _TDBapi_h_ */ diff --git a/db/tripledb/src/tdbbg.h b/db/tripledb/src/tdbbg.h new file mode 100644 index 00000000000..1be142a7d35 --- /dev/null +++ b/db/tripledb/src/tdbbg.h @@ -0,0 +1,72 @@ +/* -*- Mode: C; indent-tabs-mode: nil; -*- + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the TripleDB database library. + * + * The Initial Developer of the Original Code is Geocast Network Systems. + * Portions created by Geocast are + * Copyright (C) 2000 Geocast Network Systems. All + * Rights Reserved. + * + * Contributor(s): Terry Weissman + */ + +#ifndef _tdbbg_h_ +#define _tdbbg_h_ 1 + +typedef struct _TDBBG TDBBG; + + +typedef void (*TDBBG_Function)(void* closure); + + +TDB_BEGIN_EXTERN_C + +#define TDBBG_PRIORITY_HIGH 100 +#define TDBBG_PRIORITY_NORMAL 50 +#define TDBBG_PRIORITY_LOW 0 + + +TDB_EXTERN(TDBBG*) TDBBG_Open(); + + +TDB_EXTERN(TDBStatus) TDBBG_Close(TDBBG* tdbbg); + + +TDB_EXTERN(TDBStatus) TDBBG_AddFunction(TDBBG* tdbbg, const char* name, + TDBInt32 secondsToWait, + TDBInt32 priority, + TDBBG_Function func, void* closure); + +TDB_EXTERN(TDBStatus) TDBBG_RescheduleFunction(TDBBG* tdbbg, const char* name, + TDBInt32 secondsToWait, + TDBInt32 priority, + TDBBG_Function func, + void* closure); + +TDB_EXTERN(TDBStatus) TDBBG_RemoveFunction(TDBBG* tdbbg, const char* name, + TDBBG_Function func, void* closure); + + +/* TDBBG_WaitUntilIdle() waits until the background thread is idle doing + nothing. This is occasionally useful, when you want to make sure any + immediate background tasks you may have had are not doing anything. Note + that this will *not* wait for any things that are scheduled off into the + future; it only looks for immediately scheduled tasks. */ + +TDB_EXTERN(TDBStatus) TDBBG_WaitUntilIdle(TDBBG* tdbbg); + + +TDB_END_EXTERN_C + + +#endif /* _tdbbg_h_ */ diff --git a/db/tripledb/src/tdbdebug.h b/db/tripledb/src/tdbdebug.h index 74b87c514cd..ad369d6ae72 100644 --- a/db/tripledb/src/tdbdebug.h +++ b/db/tripledb/src/tdbdebug.h @@ -14,7 +14,7 @@ * * The Initial Developer of the Original Code is Geocast Network Systems. * Portions created by Geocast are - * Copyright (C) 1999 Geocast Network Systems. All + * Copyright (C) 2000 Geocast Network Systems. All * Rights Reserved. * * Contributor(s): Terry Weissman @@ -23,18 +23,18 @@ /* These are debugging routines for the TDB. Don't use any of these calls in production code! */ -extern void TDBDumpNode(PRFileDesc* fid, TDBNode* node); -extern void TDBDumpTree(PRFileDesc* fid, TDB* db, PRInt32 tree); +extern void TDBDumpNode(TDBFileDesc* fid, TDBNode* node); +extern void TDBDumpTree(TDBFileDesc* fid, TDB* db, TDBInt32 tree); -extern PRStatus TDBSanityCheck(TDB* db, PRFileDesc* fid); +extern TDBStatus TDBSanityCheck(TDBBase* base, TDBFileDesc* fid); extern void TDBGetCursorStats(TDBCursor* cursor, - PRInt32* hits, - PRInt32* misses); + TDBInt32* hits, + TDBInt32* misses); /* Create a "dot" graph file. To view these, and learn more about them, see http://www.research.att.com/~north/cgi-bin/webdot.cgi */ -extern void TDBMakeDotGraph(TDB* db, const char* filename, PRInt32 tree); +extern void TDBMakeDotGraph(TDB* db, const char* filename, TDBInt32 tree); diff --git a/db/tripledb/src/tdbexperimental.h b/db/tripledb/src/tdbexperimental.h new file mode 100644 index 00000000000..a904e2a970d --- /dev/null +++ b/db/tripledb/src/tdbexperimental.h @@ -0,0 +1,61 @@ +/* -*- Mode: C; indent-tabs-mode: nil; -*- + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the TripleDB database library. + * + * The Initial Developer of the Original Code is Geocast Network Systems. + * Portions created by Geocast are + * Copyright (C) 2000 Geocast Network Systems. All + * Rights Reserved. + * + * Contributor(s): Terry Weissman + */ + +#ifndef _TDBexperimental_h_ +#define _TDBexperimental_h_ 1 + +/* These are experimental APIs. You should not use them in real code; you + should only use them if you fully understand the fragility of the APIs, + etc., etc.*/ + + + +/* TDBGetTripleID() gets a unique identifier that represents a triple in the + database. You can later call TDBFindTripleFromID() to get back the + same triple. This will return TDB_FAILURE if the given triple is not + actually stored in the database. + + The top four bits of the returned ID is guaranteed to be zero. */ + +TDB_EXTERN(TDBStatus) TDBGetTripleID(TDB* database, TDBNodePtr triple[3], + TDBInt64* id); + + + +/* TDBFindTripleFromID() returns the triple that corresponds to the given ID. + It is potentially fatal to call this with an illegal ID (though we try real + hard to detect that case and return NULL instead). The returned triple + must be free'd with TDBFreeTriple(). + */ + +TDB_EXTERN(TDBTriple*) TDBFindTripleFromID(TDB* database, TDBInt64 id); + + +/* TDBFreeTriple() is used to free a triple returned from + TDBFindTripleFromID(). It must *not* be used on a triple returned from + TDBGetResult(); the TDB code is responsible for freeing those. */ + +TDB_EXTERN(void) TDBFreeTriple(TDBTriple* triple); + + + +#endif /* _TDBexperimental_h_ */ diff --git a/db/tripledb/src/tdbimpl.h b/db/tripledb/src/tdbimpl.h new file mode 100644 index 00000000000..aec1ec8748d --- /dev/null +++ b/db/tripledb/src/tdbimpl.h @@ -0,0 +1,94 @@ +/* -*- Mode: C; indent-tabs-mode: nil; -*- + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the TripleDB database library. + * + * The Initial Developer of the Original Code is Geocast Network Systems. + * Portions created by Geocast are + * Copyright (C) 2000 Geocast Network Systems. All + * Rights Reserved. + * + * Contributor(s): Terry Weissman + */ + +#ifndef _TDBimpl_h_ +#define _TDBimpl_h_ 1 + + +typedef struct { + TDBStatus (*close)(TDB* database); + void* (*query)(TDB* database, TDBNodePtr triple[3], + TDBSortSpecification* sortspec); + TDBTriple* (*getresult)(void* cursor); + TDBStatus (*freecursor)(void* cursor); + TDBStatus (*remove)(TDB* database, TDBNodePtr triple[3]); + TDBStatus (*add)(TDB* database, TDBNodePtr triple[3], TDBUint64 owner); + TDBStatus (*addfalse)(TDB* database, TDBNodePtr triple[3], + TDBUint64 owner); + TDBStatus (*replace)(TDB* database, TDBNodePtr triple[3], TDBUint64 owner); + TDBStatus (*gettripleid)(TDB* db, TDBNodePtr triple[3], TDBInt64* id); + TDBTriple* (*findtriplefromid)(TDB* db, TDBInt64 id); +} TDBImplement; + + +TDB_BEGIN_EXTERN_C + + +/* TDBOpenImplementation() opens a database object which supports the calls + listed in tdbapi.h, but whose implentation is provided by some other module. + The implementation is provided as a bunch of function pointers. Each of + the TDB* routines that correspond to the pointers know to implement + themselves entirely by calling the appropriate routine. + + NONE OF THESE FUNCTION POINTERS MAY BE NULL! We don't allow inheritence + from the base routines. Sorry! + + The void* that are passed to and from the query(), getresult() and + freecursor() routines are implementation-specific instances of the + cursor object. It is up to the implementation how they are done, + although they will almost certainly need to include a pointer back to the + database object, or to the impldata. + + The query() routine returns a pointer to a TDBImplCursor object. + The db field in that object should be a pointer back to the + database itself (the same pointer that was passed to query), and should + never be modified. The data field is implementation-specific, and is where + the implementation should hang all state relating to the cursor. + */ + + +TDB_EXTERN(TDB*) TDBOpenImplementation(TDBImplement* impl); + + +/* TDBSetImplData() sets implementation-specific data associated with the + database. */ + +TDB_EXTERN(TDBStatus) TDBSetImplData(TDB* database, void* impldata); + +/* TDBGetImplData() gets any implementation-specific data associated with the + database. */ + +TDB_EXTERN(void*) TDBGetImplData(TDB* database); + + +/* TDBValidateSortOrder takes a sortspec and range, and makes sure the + keyorder field in the sortspec is correctly filled out. + */ + +TDB_EXTERN(void) TDBValidateSortOrder(TDB* tdb, TDBSortSpecification* sortspec, + TDBNodePtr triple[3]); + + +TDB_END_EXTERN_C + + +#endif /* _TDBimpl_h_ */ diff --git a/db/tripledb/src/tdbtypes.h b/db/tripledb/src/tdbtypes.h index 48110fa4bc8..84483d80ded 100644 --- a/db/tripledb/src/tdbtypes.h +++ b/db/tripledb/src/tdbtypes.h @@ -14,7 +14,7 @@ * * The Initial Developer of the Original Code is Geocast Network Systems. * Portions created by Geocast are - * Copyright (C) 1999 Geocast Network Systems. All + * Copyright (C) 2000 Geocast Network Systems. All * Rights Reserved. * * Contributor(s): Terry Weissman @@ -26,6 +26,11 @@ /* These are private, internal type definitions, */ #include +#include "tdbapi.h" + +#include "db/db.h" + +#ifdef TDB_USE_NSPR #include "pratom.h" #include "prcvar.h" #include "prio.h" @@ -33,28 +38,100 @@ #include "prlog.h" #include "prmem.h" #include "prthread.h" -#include "prtime.h" -#include "prtypes.h" +#include "plstr.h" + +#include "geoassert/geoassert.h" /* This line can be safely removed if you + are building this in a non-Geocast + environment. */ + +typedef PRLock TDBLock; +typedef PRThread TDBThread; +typedef PRCondVar TDBCondVar; + +#define tdb_Lock(l) PR_Lock(l) +#define tdb_Unlock(l) PR_Unlock(l) +#ifdef GeoAssert +#define tdb_ASSERT(x) GeoAssert(x) +#else +#define tdb_ASSERT(x) PR_ASSERT(x) +#endif + +#define tdb_OpenFileRW(filename) PR_Open(filename, PR_RDWR | PR_CREATE_FILE, 0666) +#define tdb_OpenFileReadOnly(filename) PR_Open(filename, PR_RDONLY, 0666) +#define tdb_Close PR_Close +#define tdb_Seek(fid, l) (PR_Seek(fid, l, PR_SEEK_SET) == l ? TDB_SUCCESS : TDB_FAILURE) +#define tdb_Read PR_Read +#define tdb_Write PR_Write +#define tdb_Delete PR_Delete +#define tdb_Rename PR_Rename +#define tdb_MkDir PR_MkDir + +#define tdb_strdup PL_strdup +#define tdb_strcasestr PL_strcasestr + +#else + +#ifdef TDB_USE_THREADS +#undef TDB_USE_THREADS +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +typedef void* TDBLock; +typedef void* TDBThread; +typedef void* TDBCondVar; + +#define tdb_Lock(l) ((void) 0) +#define tdb_Unlock(l) ((void) 0) + +#define tdb_ASSERT assert + +#define tdb_OpenFileRW(filename) fopen(filename, "r+") +#define tdb_OpenFileReadOnly(filename) fopen(filename, "r") +#define tdb_Close fclose +#define tdb_Seek(fid, l) (fseek(fid, l, SEEK_SET) == l ? TDB_SUCCESS : TDB_FAILURE) +#define tdb_Read(fid, buf, length) fread(buf, 1, length, fid) +#define tdb_Write(fid, buf, length) fwrite(buf, 1, length, fid) +#define tdb_Delete unlink +#define tdb_Rename rename +#define tdb_MkDir mkdir + +#define tdb_strdup strdup +#define tdb_strcasestr strstr /* WRONG! Should be a case-independent one! + XXX ### */ + +#endif /* TDB_USE_NSPR */ + + + +/* Special node types that are used internally only. */ + +#define TDBTYPE_MINNODE 0 /* A node that is less than all other nodes. */ +#define TDBTYPE_INTERNED 126 /* An interned string. */ +#define TDBTYPE_MAXNODE 127 /* A node that is bigger than all others. */ -#include "tdbapi.h" /* Magic number that appears as first four bytes of db files. */ #define TDB_MAGIC "TDB\n" -/* Total number of bytes we reserve at the beginning of the db file for - special stuff. */ -#define TDB_FILEHEADER_SIZE 1024 - - - /* Current version number for db files. */ -#define TDB_VERSION 1 +#define TDB_MAJOR_VERSION 11 +#define TDB_MINOR_VERSION 0 -#define NUMTREES 4 /* How many binary trees we're keeping, - indexing the data. */ + +/* Maximum number of different record types we are prepared to handle. */ + +#define TDB_MAXTYPES 20 /* The trees index things in the following orders: 0: 0, 1, 2 @@ -65,164 +142,56 @@ /* MINRECORDSIZE and MAXRECORDSIZE are the minimum and maximum possible record size. They're useful for sanity checking. */ -#define BASERECORDSIZE (sizeof(PRInt32) + NUMTREES * (2*sizeof(TDBPtr)+sizeof(PRInt8))) +#define BASERECORDSIZE (sizeof(TDBInt32) + NUMTREES * (2*sizeof(TDBPtr)+sizeof(TDBInt8))) #define MINRECORDSIZE (BASERECORDSIZE + 3 * 2) #define MAXRECORDSIZE (BASERECORDSIZE + 3 * 65540) -typedef PRInt32 TDBPtr; +/* The meaning of the various flag bits in a record entry. */ -typedef struct { - TDBPtr child[2]; - PRInt8 balance; -} TDBTreeEntry; +#define TDBFLAG_ISASSERT 0x1 /* On if this entry is an assertion, off + if this entry is a false assertion. */ +#define TDBFLAG_ISEXTENDED 0x2 /* On if this entry is incomplete, and the + last four bytes of it is actually a + pointer to an extension record.. */ +#define TDBFLAG_KEYINSUFFICIENT 0x4 /* Even though the data in the key may + look like it has enough to regenerate + the entire record content, in fact + it doesn't. This gets set in key + records when string data contains + embedded nulls, since the technique + for getting strings to and from + keys relies on using NULL characters + to mark end-of-string. */ -typedef struct _TDBRecord { - TDBPtr position; - PRInt32 length; - PRInt32 refcnt; - PRBool dirty; - struct _TDBRecord* next; - PRUint8 flags; - TDBTreeEntry entry[NUMTREES]; - TDBNode* data[3]; -} TDBRecord; +/* The meaning of various flag bits in a record-type byte. */ + +#define TDB_FREERECORD 0x80 /* If set, then this record is in the + free list. */ -typedef struct _TDBParentChain { - TDBRecord* record; - struct _TDBParentChain* next; -} TDBParentChain; +/* The meaning of various flag bits in a index record. */ + +#define TDB_LEAFFLAG 0x1 -struct _TDBCursor { - TDB* db; - TDBNodeRange range[3]; - TDBRecord* cur; - TDBRecord* lasthit; - TDBParentChain* parent; - PRInt32 tree; - PRBool reverse; - TDBTriple triple; - PRInt32 hits; - PRInt32 misses; - struct _TDBCursor* nextcursor; - struct _TDBCursor* prevcursor; -}; +#define DB_OK 0 - -typedef struct _TDBCallbackInfo { - struct _TDBCallbackInfo* nextcallback; - TDBNodeRange range[3]; - TDBCallbackFunction func; - void* closure; -} TDBCallbackInfo; - -typedef struct _TDBPendingCall { - struct _TDBPendingCall* next; - TDBCallbackFunction func; - void* closure; - TDBTriple triple; - PRInt32 action; -} TDBPendingCall; - - - - -struct _TDB { - char* filename; - PRInt32 filelength; - PRFileDesc* fid; - TDBPtr roots[NUMTREES]; - TDBPtr freeroot; - PRBool dirty; - PRBool rootdirty; - PRLock* mutex; /* Used to prevent more than one thread - from doing anything in DB code at the - same time. */ - PRThread* callbackthread; /* Thread ID of the background - callback-calling thread. */ - PRCondVar* callbackcvargo; /* Used to wake up the callback-calling - thread. */ - PRCondVar* callbackcvaridle; /* Used by the callback-calling to indicate - that it is now idle. */ - PRBool killcallbackthread; - PRBool callbackidle; - char* iobuf; - PRInt32 iobuflength; - TDBRecord* firstrecord; - TDBCursor* firstcursor; - TDBCallbackInfo* firstcallback; - TDBPendingCall* firstpendingcall; - TDBPendingCall* lastpendingcall; -}; - - -/* ----------------------------- From add.c: ----------------------------- */ - -extern PRStatus tdbAddToTree(TDB* db, TDBRecord* record, PRInt32 tree); -extern PRStatus tdbRemoveFromTree(TDB* db, TDBRecord* record, PRInt32 tree); - - -/* ----------------------------- From callback.c: ------------------------- */ -extern void tdbCallbackThread(void* closure); -extern PRStatus tdbQueueMatchingCallbacks(TDB* db, TDBRecord* record, - PRInt32 action); - - -/* ----------------------------- From node.c: ----------------------------- */ - - -extern TDBNode* tdbNodeDup(TDBNode* node); -extern PRInt32 tdbNodeSize(TDBNode* node); -extern PRInt64 tdbCompareNodes(TDBNode* n1, TDBNode* n2); -extern TDBNode* tdbGetNode(TDB* db, char** ptr); -extern void tdbPutNode(TDB* db, char** ptr, TDBNode* node); - - -/* ----------------------------- From query.c: ----------------------------- */ - -extern TDBCursor* tdbQueryNolock(TDB* db, TDBNodeRange range[3], - TDBSortSpecification* sortspec); -extern PRStatus tdbFreeCursorNolock(TDBCursor* cursor); -extern TDBTriple* tdbGetResultNolock(TDBCursor* cursor); -extern PRInt64 tdbCompareToRange(TDBRecord* record, TDBNodeRange* range, - PRInt32 comparerule); -extern PRBool tdbMatchesRange(TDBRecord* record, TDBNodeRange* range); -extern void tdbThrowOutCursorCaches(TDB* db); +typedef TDBInt32 TDBPtr; -/* ----------------------------- From record.c: ---------------------------- */ -extern TDBRecord* tdbLoadRecord(TDB* db, TDBPtr position); -extern PRStatus tdbSaveRecord(TDB* db, TDBRecord* record); -extern PRStatus tdbFreeRecord(TDBRecord* record); -extern TDBRecord* tdbAllocateRecord(TDB* db, TDBNodePtr triple[3]); -extern PRInt64 tdbCompareRecords(TDBRecord* r1, TDBRecord* r2, - PRInt32 comparerule); +typedef struct _TDBCallbackInfo TDBCallbackInfo; +typedef struct _TDBIntern TDBIntern; +typedef struct _TDBPendingCall TDBPendingCall; +typedef struct _TDBRType TDBRType; +typedef struct _TDBVector TDBVector; +typedef struct _TDBWindex TDBWindex; +typedef struct _TDBWindexCursor TDBWindexCursor; -/* ----------------------------- From tdb.c: ----------------------------- */ - -extern PRStatus tdbFlush(TDB* db); -extern PRStatus tdbThrowAwayUnflushedChanges(TDB* db); -extern PRStatus tdbGrowIobuf(TDB* db, PRInt32 length); -extern PRStatus tdbLoadRoots(TDB* db); -extern void tdbMarkCorrupted(TDB* db); - -extern PRInt32 tdbGetInt32(char** ptr); -extern PRInt32 tdbGetInt16(char** ptr); -extern PRInt8 tdbGetInt8(char** ptr); -extern PRInt64 tdbGetInt64(char** ptr); -extern PRUint16 tdbGetUInt16(char** ptr); -extern void tdbPutInt32(char** ptr, PRInt32 value); -extern void tdbPutInt16(char** ptr, PRInt16 value) ; -extern void tdbPutUInt16(char** ptr, PRUint16 value) ; -extern void tdbPutInt8(char** ptr, PRInt8 value); -extern void tdbPutInt64(char** ptr, PRInt64 value); - #endif /* _TDBtypes_h_ */ diff --git a/db/tripledb/src/util.c b/db/tripledb/src/util.c new file mode 100644 index 00000000000..0bf58b74055 --- /dev/null +++ b/db/tripledb/src/util.c @@ -0,0 +1,237 @@ +/* -*- Mode: C; indent-tabs-mode: nil; -*- + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the TripleDB database library. + * + * The Initial Developer of the Original Code is Geocast Network Systems. + * Portions created by Geocast are + * Copyright (C) 2000 Geocast Network Systems. All + * Rights Reserved. + * + * Contributor(s): Terry Weissman + */ + +#include "tdbtypes.h" + +#include "util.h" + + +#ifdef TDB_USE_NSPR +/* Don't attempt to use NSPR's "ll" routines; they don't do what we want. */ +#include "prnetdb.h" +#define tdb_ntohs PR_ntohs +#define tdb_ntohl PR_ntohl +#define tdb_htons PR_htons +#define tdb_htonl PR_htonl +#else +#include +#include +#define tdb_ntohs ntohs +#define tdb_ntohl ntohl +#define tdb_htons htons +#define tdb_htonl htonl +#endif + + +TDBPtr +tdbGetPtr(char** ptr, char* endptr) +{ + TDBPtr result; + if (*ptr + sizeof(result) > endptr) return -1; + memcpy(&result, *ptr, sizeof(result)); + *ptr += sizeof(result); + result = tdb_ntohl(result); + return result; +} + +TDBInt32 +tdbGetInt32(char** ptr, char* endptr) +{ + TDBInt32 result; + if (*ptr + sizeof(TDBInt32) > endptr) return 0; + memcpy(&result, *ptr, sizeof(TDBInt32)); + *ptr += sizeof(TDBInt32); + result = tdb_ntohl(result); + return result; +} + +TDBInt32 +tdbGetInt16(char** ptr, char* endptr) +{ + TDBInt16 result; + if (*ptr + sizeof(TDBInt16) > endptr) return 0; + memcpy(&result, *ptr, sizeof(TDBInt16)); + *ptr += sizeof(TDBInt16); + result = tdb_ntohs(result); + return result; +} + +TDBInt8 +tdbGetInt8(char** ptr, char* endptr) +{ + TDBInt8 result; + if (*ptr + sizeof(TDBInt8) > endptr) return 0; + memcpy(&result, *ptr, sizeof(TDBInt8)); + *ptr += sizeof(TDBInt8); + return result; +} + +TDBUint8 +tdbGetUInt8(char** ptr, char* endptr) +{ + TDBUint8 result; + if (*ptr + sizeof(TDBUint8) > endptr) return 0; + memcpy(&result, *ptr, sizeof(TDBUint8)); + *ptr += sizeof(TDBUint8); + return result; +} + +TDBInt64 +tdbGetInt64(char** ptr, char* endptr) +{ + return (TDBInt64) tdbGetUInt64(ptr, endptr); +} + +TDBUint16 +tdbGetUInt16(char** ptr, char* endptr) +{ + TDBUint16 result; + if (*ptr + sizeof(TDBUint16) > endptr) return 0; + memcpy(&result, *ptr, sizeof(TDBUint16)); + *ptr += sizeof(TDBUint16); + result = tdb_ntohs(result); + return result; +} + + +TDBUint64 +tdbGetUInt64(char** ptr, char* endptr) +{ + TDBUint32 hi, low; + TDBUint64 result; + if (*ptr + sizeof(TDBUint64) > endptr) return 0; + hi = tdbGetUInt32(ptr, endptr); + low = tdbGetUInt32(ptr, endptr); + result = ((TDBUint64) hi) << 32 | low; + return result; +} + + +TDBUint32 +tdbGetUInt32(char** ptr, char* endptr) +{ + TDBUint32 result; + if (*ptr + sizeof(TDBUint32) > endptr) return 0; + memcpy(&result, *ptr, sizeof(TDBUint32)); + *ptr += sizeof(TDBUint32); + result = tdb_ntohl(result); + return result; +} + + +TDBStatus +tdbPutPtr(char** ptr, TDBPtr value, char* endptr) +{ + if (*ptr + sizeof(value) > endptr) return TDB_FAILURE; + value = tdb_htonl(value); + memcpy(*ptr, &value, sizeof(TDBPtr)); + *ptr += sizeof(TDBPtr); + return TDB_SUCCESS; +} + +TDBStatus +tdbPutInt32(char** ptr, TDBInt32 value, char* endptr) +{ + if (*ptr + sizeof(value) > endptr) return TDB_FAILURE; + value = tdb_htonl(value); + memcpy(*ptr, &value, sizeof(TDBInt32)); + *ptr += sizeof(TDBInt32); + return TDB_SUCCESS; +} + +TDBStatus +tdbPutInt16(char** ptr, TDBInt16 value, char* endptr) +{ + if (*ptr + sizeof(value) > endptr) return TDB_FAILURE; + value = tdb_htons(value); + memcpy(*ptr, &value, sizeof(TDBInt16)); + *ptr += sizeof(TDBInt16); + return TDB_SUCCESS; +} + +TDBStatus +tdbPutUInt16(char** ptr, TDBUint16 value, char* endptr) +{ + if (*ptr + sizeof(value) > endptr) return TDB_FAILURE; + value = tdb_htons(value); + memcpy(*ptr, &value, sizeof(TDBUint16)); + *ptr += sizeof(TDBUint16); + return TDB_SUCCESS; +} + +TDBStatus +tdbPutInt8(char** ptr, TDBInt8 value, char* endptr) +{ + if (*ptr + sizeof(value) > endptr) return TDB_FAILURE; + memcpy(*ptr, &value, sizeof(TDBInt8)); + *ptr += sizeof(TDBInt8); + return TDB_SUCCESS; +} + +TDBStatus +tdbPutUInt8(char** ptr, TDBUint8 value, char* endptr) +{ + if (*ptr + sizeof(value) > endptr) return TDB_FAILURE; + memcpy(*ptr, &value, sizeof(TDBUint8)); + *ptr += sizeof(TDBUint8); + return TDB_SUCCESS; +} + +TDBStatus +tdbPutInt64(char** ptr, TDBInt64 value, char* endptr) +{ + return tdbPutUInt64(ptr, (TDBUint64) value, endptr); +} + +TDBStatus +tdbPutUInt64(char** ptr, TDBUint64 value, char* endptr) +{ + TDBUint32 hi, low; + if (*ptr + sizeof(value) > endptr) return TDB_FAILURE; + hi = (TDBUint32) (value >> 32); + low = (TDBUint32) value; + if (tdbPutUInt32(ptr, hi, endptr) != TDB_SUCCESS) return TDB_FAILURE; + return tdbPutUInt32(ptr, low, endptr); +} + +TDBStatus +tdbPutUInt32(char** ptr, TDBUint32 value, char* endptr) +{ + if (*ptr + sizeof(value) > endptr) return TDB_FAILURE; + value = tdb_htonl(value); + memcpy(*ptr, &value, sizeof(TDBInt32)); + *ptr += sizeof(TDBUint32); + return TDB_SUCCESS; +} + + +TDBStatus +tdbPutString(char** ptr, const char* str, char* endptr) +{ + while (*str) { + if (*ptr >= endptr) return TDB_FAILURE; + *(*ptr)++ = *str++; + } + if (*ptr >= endptr) return TDB_FAILURE; + *(*ptr)++ = '\0'; + return TDB_SUCCESS; +} diff --git a/db/tripledb/src/util.h b/db/tripledb/src/util.h new file mode 100644 index 00000000000..5204ee5c9ba --- /dev/null +++ b/db/tripledb/src/util.h @@ -0,0 +1,50 @@ +/* -*- Mode: C; indent-tabs-mode: nil; -*- + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the TripleDB database library. + * + * The Initial Developer of the Original Code is Geocast Network Systems. + * Portions created by Geocast are + * Copyright (C) 2000 Geocast Network Systems. All + * Rights Reserved. + * + * Contributor(s): Terry Weissman + */ + +#ifndef _util_h_ +#define _util_h_ 1 + +extern TDBInt32 tdbGetInt16(char** ptr, char* endptr); +extern TDBInt32 tdbGetInt32(char** ptr, char* endptr); +extern TDBInt64 tdbGetInt64(char** ptr, char* endptr); +extern TDBInt8 tdbGetInt8(char** ptr, char* endptr); +extern TDBPtr tdbGetPtr(char** ptr, char* endptr); +extern TDBUint16 tdbGetUInt16(char** ptr, char* endptr); +extern TDBUint32 tdbGetUInt32(char** ptr, char* endptr); +extern TDBUint64 tdbGetUInt64(char** ptr, char* endptr); +extern TDBUint8 tdbGetUInt8(char** ptr, char* endptr); + +extern TDBStatus tdbPutInt16(char** ptr, TDBInt16 value, char* endptr); +extern TDBStatus tdbPutInt32(char** ptr, TDBInt32 value, char* endptr); +extern TDBStatus tdbPutInt64(char** ptr, TDBInt64 value, char* endptr); +extern TDBStatus tdbPutInt8(char** ptr, TDBInt8 value, char* endptr); +extern TDBStatus tdbPutPtr(char** ptr, TDBPtr value, char* endptr); +extern TDBStatus tdbPutUInt16(char** ptr, TDBUint16 value, char* endptr); +extern TDBStatus tdbPutUInt32(char** ptr, TDBUint32 value, char* endptr); +extern TDBStatus tdbPutUInt64(char** ptr, TDBUint64 value, char* endptr); +extern TDBStatus tdbPutUInt8(char** ptr, TDBUint8 value, char* endptr); + +/* tdbPutString() puts the string and its trailing NULL into the given + buffer. */ +extern TDBStatus tdbPutString(char** ptr, const char* str, char* endptr); + +#endif /* _util_h_ */ diff --git a/db/tripledb/src/vector.c b/db/tripledb/src/vector.c new file mode 100644 index 00000000000..e97c7f68387 --- /dev/null +++ b/db/tripledb/src/vector.c @@ -0,0 +1,360 @@ +/* -*- Mode: C; indent-tabs-mode: nil; -*- + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the TripleDB database library. + * + * The Initial Developer of the Original Code is Geocast Network Systems. + * Portions created by Geocast are + * Copyright (C) 2000 Geocast Network Systems. All + * Rights Reserved. + * + * Contributor(s): Terry Weissman + */ + +#include "tdbtypes.h" + +#include "intern.h" +#include "node.h" +#include "tdb.h" +#include "util.h" +#include "vector.h" + + +struct _TDBVector { + TDBBase* base; + TDBInt32 numfields; + TDBNodePtr* nodes; + TDBNodePtr* interned; + TDBUint8 layer; + TDBUint8 flags; + TDBUint32 recordnum; + TDBUint64 owner; + +}; + + +TDBVector* +tdbVectorNewFromNodes(TDBBase* base, TDBNodePtr* nodes, TDBUint8 layer, + TDBUint8 flags, TDBUint32 recordnum) +{ + TDBVector* vector; + TDBInt32 numfields = 3; /* Sigh... */ + TDBInt32 i; + if (base == NULL || nodes == NULL) { + tdb_ASSERT(0); + return NULL; + } + vector = tdb_NEWZAP(TDBVector); + if (!vector) return NULL; + vector->base = base; + vector->numfields = numfields; + vector->nodes = tdb_Calloc(numfields, sizeof(TDBNodePtr)); + vector->interned = tdb_Calloc(numfields, sizeof(TDBNodePtr)); + if (!vector->nodes || !vector->interned) { + tdb_Free(vector); + return NULL; + } + for (i=0 ; inodes[i] = TDBNodeDup(nodes[i]); + if (!vector->nodes[i]) { + tdbVectorFree(vector); + return NULL; + } + if (vector->nodes[i]->type == TDBTYPE_INTERNED) { + vector->interned[i] = vector->nodes[i]; + vector->nodes[i] = NULL; + } + } + vector->layer = layer; + vector->flags = flags; + vector->recordnum = recordnum; + return vector; +} + + +extern +TDBVector* tdbVectorNewFromRecord(TDBBase* base, TDBUint32 recordnum) +{ + TDBVector* vector; + TDBInt32 numfields = 3; /* Sigh... */ + TDBNodePtr nodes[3]; + DBT key; + DBT data; + DB* db; + int dbstatus; + char* ptr; + char* endptr; + TDBInt32 i; + TDBUint8 layer; + TDBUint8 flags; + TDBUint64 owner; + if (base == NULL || recordnum == 0) { + tdb_ASSERT(0); + return NULL; + } + db = tdbGetRecordDB(base); + if (db == NULL) { + tdb_ASSERT(0); + return NULL; + } + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + key.data = &recordnum; + key.size = sizeof(recordnum); + dbstatus = db->get(db, tdbGetTransaction(base), &key, &data, 0); + if (dbstatus != DB_OK) { + tdbMarkCorrupted(base); + return NULL; + } + ptr = data.data; + endptr = ptr + data.size; + for (i=0 ; i=0 ; i--) TDBFreeNode(nodes[i]); + return NULL; + } + } + layer = tdbGetUInt8(&ptr, endptr); + flags = tdbGetUInt8(&ptr, endptr); + owner = tdbGetUInt64(&ptr, endptr); + vector = tdbVectorNewFromNodes(base, nodes, layer, flags, recordnum); + for (i=0 ; iowner = owner; + return vector; +} + + +extern TDBStatus +tdbVectorPutInNewRecord(TDBVector* vector, TDBUint64 owner) +{ + DBT key; + DBT data; + DB* db; + TDBInt32 length; + TDBInt32 i; + char* tmpbuf; + char* ptr; + char* endptr; + int dbstatus; + if (vector == NULL || vector->recordnum != 0) { + tdb_ASSERT(0); + return TDB_FAILURE; + } + db = tdbGetRecordDB(vector->base); + if (db == NULL) { + tdb_ASSERT(0); + return TDB_FAILURE; + } + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + length = sizeof(TDBUint64) + sizeof(TDBUint8) + sizeof(TDBUint8); + for (i=0 ; inumfields ; i++) { + length += tdbNodeSize(tdbVectorGetInternedNode(vector, i)); + } + length += 20; /* Just for general slop. */ + tmpbuf = tdbGrowTmpBuf(vector->base, length); + if (tmpbuf == NULL) return TDB_FAILURE; + data.data = tmpbuf; + ptr = data.data; + endptr = ptr + length; + for (i=0 ; inumfields ; i++) { + if (tdbPutNode(&ptr, tdbVectorGetInternedNode(vector, i), + endptr) != TDB_SUCCESS) return TDB_FAILURE; + } + tdbPutUInt8(&ptr, vector->layer, endptr); + tdbPutUInt8(&ptr, vector->flags, endptr); + tdbPutUInt64(&ptr, vector->owner, endptr); + data.size = ptr - tmpbuf; + if (data.size != length - 20) { + tdb_ASSERT(0); + return TDB_FAILURE; + } + key.data = &(vector->recordnum); + key.ulen = sizeof(vector->recordnum); + key.flags = DB_DBT_USERMEM; + dbstatus = db->put(db, tdbGetTransaction(vector->base), + &key, &data, DB_APPEND); + if (dbstatus != DB_OK) return TDB_FAILURE; + tdb_ASSERT(vector->recordnum > 0); + return TDB_SUCCESS; +} + +TDBUint32 +tdbVectorGetRecordNumber(TDBVector* vector) +{ + if (vector == NULL) { + tdb_ASSERT(0); + return 0; + } + return vector->recordnum; +} + + +TDBUint64 +tdbVectorGetOwner(TDBVector* vector) +{ + if (vector == NULL) { + tdb_ASSERT(0); + return 0; + } + return vector->owner; +} + +void +tdbVectorFree(TDBVector* vector) +{ + TDBInt32 i; + if (vector == NULL) { + tdb_ASSERT(0); + return; + } + if (vector->nodes) { + for (i=0 ; inumfields ; i++) { + if (vector->nodes[i]) TDBFreeNode(vector->nodes[i]); + } + tdb_Free(vector->nodes); + } + if (vector->interned) { + for (i=0 ; inumfields ; i++) { + if (vector->interned[i]) TDBFreeNode(vector->interned[i]); + } + tdb_Free(vector->interned); + } + tdb_Free(vector); +} + + +TDBNodePtr +tdbVectorGetNonInternedNode(TDBVector* vector, TDBInt32 i) +{ + if (vector == NULL || i < 0 || i >= vector->numfields) { + tdb_ASSERT(0); + return NULL; + } + if (i != 1) { + tdb_ASSERT(vector->nodes[i] != NULL && vector->interned[i] == NULL); + return vector->nodes[i]; + } + if (vector->nodes[i] == NULL) { + if (vector->interned[i] == NULL) { + tdb_ASSERT(0); + return NULL; + } + vector->nodes[i] = tdbUnintern(vector->base, vector->interned[i]); + } + return vector->nodes[i]; +} + + +TDBNodePtr +tdbVectorGetInternedNode(TDBVector* vector, TDBInt32 i) +{ + if (vector == NULL || i < 0 || i >= vector->numfields) { + tdb_ASSERT(0); + return NULL; + } + if (i != 1) { + tdb_ASSERT(vector->nodes[i] != NULL && vector->interned[i] == NULL); + return vector->nodes[i]; + } + if (vector->interned[i] == NULL) { + if (vector->nodes[i] == NULL) { + tdb_ASSERT(0); + return NULL; + } + vector->interned[i] = tdbIntern(vector->base, vector->nodes[i]); + } + return vector->interned[i]; +} + + +TDBUint8 +tdbVectorGetLayer(TDBVector* vector) +{ + if (vector == NULL) { + tdb_ASSERT(0); + return 0; + } + return vector->layer; +} + + + +TDBUint8 +tdbVectorGetFlags(TDBVector* vector) +{ + if (vector == NULL) { + tdb_ASSERT(0); + return 0; + } + return vector->flags; +} + + + + +TDBInt32 +tdbVectorGetNumFields(TDBVector* vector) +{ + if (vector == NULL) { + tdb_ASSERT(0); + return -1; + } + return vector->numfields; +} + + +TDBBool +tdbVectorEqual(TDBVector* v1, TDBVector* v2) +{ + TDBInt32 i; + TDBInt64 cmp; + if (v1 == NULL || v2 == NULL || v1->numfields != v2->numfields) { + tdb_ASSERT(0); + return TDB_FALSE; + } + for (i=0 ; inumfields ; i++) { + if (v1->interned[i] != NULL && v2->interned[i] != NULL) { + cmp = TDBCompareNodes(v1->interned[i], v2->interned[i]); + } else { + cmp = TDBCompareNodes(tdbVectorGetNonInternedNode(v1, i), + tdbVectorGetNonInternedNode(v2, i)); + } + if (cmp != 0) return TDB_FALSE; + } + return TDB_TRUE; +} + + +TDBBool +tdbVectorLayerMatches(TDBVector* vector, TDB* tdb) +{ + TDBInt32 i; + if (vector == NULL || tdb == NULL) { + tdb_ASSERT(0); + return TDB_FALSE; + } + for (i=tdbGetNumLayers(tdb) - 1 ; i >= 0 ; i--) { + if (vector->layer == tdbGetLayer(tdb, i)) { + return TDB_TRUE; + } + } + return TDB_FALSE; +} diff --git a/db/tripledb/src/vector.h b/db/tripledb/src/vector.h new file mode 100644 index 00000000000..58669c98431 --- /dev/null +++ b/db/tripledb/src/vector.h @@ -0,0 +1,61 @@ +/* -*- Mode: C; indent-tabs-mode: nil; -*- + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the TripleDB database library. + * + * The Initial Developer of the Original Code is Geocast Network Systems. + * Portions created by Geocast are + * Copyright (C) 2000 Geocast Network Systems. All + * Rights Reserved. + * + * Contributor(s): Terry Weissman + */ + +#ifndef _vector_h_ +#define _vector_h_ 1 + +extern TDBVector* tdbVectorNewFromNodes(TDBBase* base, TDBNodePtr* nodes, + TDBUint8 layer, TDBUint8 flags, + TDBUint32 recordnum); + +extern TDBVector* tdbVectorNewFromRecord(TDBBase* base, TDBUint32 recordnum); + +extern TDBStatus tdbVectorPutInNewRecord(TDBVector* vector, TDBUint64 owner); + +extern TDBUint32 tdbVectorGetRecordNumber(TDBVector* vector); + +extern TDBUint64 tdbVectorGetOwner(TDBVector* vector); + +extern void tdbVectorFree(TDBVector* vector); + +extern TDBNodePtr tdbVectorGetNonInternedNode(TDBVector* vector, + TDBInt32 which); + +extern TDBNodePtr tdbVectorGetInternedNode(TDBVector* vector, TDBInt32 which); + +extern TDBUint8 tdbVectorGetFlags(TDBVector* vector); + +extern TDBUint8 tdbVectorGetLayer(TDBVector* vector); + +extern TDBInt32 tdbVectorGetNumFields(TDBVector* vector); + +extern TDBBool tdbVectorEqual(TDBVector* v1, TDBVector* v2); + +/* tdbVectorLayerMatches() returns TRUE if the given vector is in one of the + layers specified by the given TDB. */ + +extern TDBBool tdbVectorLayerMatches(TDBVector* vector, TDB* tdb); + + + + +#endif /* _vector_h_ */ diff --git a/db/tripledb/src/windex.c b/db/tripledb/src/windex.c new file mode 100644 index 00000000000..9faf02e1f22 --- /dev/null +++ b/db/tripledb/src/windex.c @@ -0,0 +1,402 @@ +/* -*- Mode: C; indent-tabs-mode: nil; -*- + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the TripleDB database library. + * + * The Initial Developer of the Original Code is Geocast Network Systems. + * Portions created by Geocast are + * Copyright (C) 2000 Geocast Network Systems. All + * Rights Reserved. + * + * Contributor(s): Terry Weissman + */ + +#include + +#include "tdbtypes.h" + +#include "tdb.h" +#include "util.h" +#include "vector.h" +#include "windex.h" + +#define MAXWORDLEN 10 + +typedef struct { + char word[MAXWORDLEN + 1]; + TDBInt32 length; + TDBUint16 position; +} WordInfo; + +struct _TDBWindex { + TDBBase* base; + DB* db; +}; + + +struct _TDBWindexCursor { + TDBWindex* windex; + char* string; + DBC* cursor; + WordInfo* winfo; + TDBInt32 numwinfo; + TDBInt32 main; + int cmdflag; +}; + + +TDBWindex* +tdbWindexNew(TDBBase* base) +{ + TDBWindex* windex; + int dbstatus; + windex = tdb_NEWZAP(TDBWindex); + if (!windex) return NULL; + windex->base = base; + dbstatus = db_create(&(windex->db), tdbGetDBEnv(base), 0); + if (dbstatus != DB_OK) goto FAIL; + dbstatus = windex->db->open(windex->db, "windex", NULL, DB_BTREE, + DB_CREATE, 0666); + if (dbstatus != DB_OK) goto FAIL; + return windex; + + FAIL: + tdbWindexFree(windex); + return NULL; +} + +void +tdbWindexFree(TDBWindex* windex) +{ + if (windex == NULL) { + tdb_ASSERT(0); + return; + } + if (windex->db) { + windex->db->close(windex->db, 0); + } + tdb_Free(windex); +} + + +TDBStatus +tdbWindexSync(TDBWindex* windex) +{ + if (windex == NULL) { + tdb_ASSERT(0); + return TDB_FAILURE; + } + if (windex->db->sync(windex->db, 0) != DB_OK) return TDB_FAILURE; + return TDB_SUCCESS; +} + + +static WordInfo* +splitIntoWords(char* str, WordInfo* winfobuf, TDBInt32 winfobuflen) +{ + WordInfo* winfo; + WordInfo* tmp; + TDBInt32 cur; + TDBInt32 max; + char* ptr; + char* ptr1; + char* ptr2; + TDBInt32 i; + TDBInt32 length; + + + winfo = winfobuf; + max = winfobuflen - 1; + cur = 0; + for (ptr1 = str ; *ptr1 ; ptr1++) { + if (!isalnum(*ptr1)) continue; + for (ptr2 = ptr1 + 1 ; *ptr2 && isalnum(*ptr2) ; ptr2++) { } + if (ptr2 - ptr1 > 2) { + /* Should check for stopwords here. XXX */ + if (cur >= max) { + max = cur + 20; + tmp = tdb_Calloc(max + 1, sizeof(WordInfo)); + for (i=0 ; i MAXWORDLEN) length = MAXWORDLEN; + winfo[cur].length = length; + winfo[cur].position = ptr1 - str; + for (ptr = winfo[cur].word ; length > 0 ; length--) { + *ptr++ = tolower(*ptr1++); + } + *ptr = '\0'; + cur++; + } + ptr1 = ptr2; + if (!*ptr1) break; + } + if (winfo) winfo[cur].length = 0; + return winfo; +} + + +TDBStatus +tdbWindexAdd(TDBWindex* windex, TDBVector* vector) +{ + TDBNodePtr node; + WordInfo winfobuf[20]; + WordInfo* winfo; + WordInfo* wptr; + char buf[100]; + DBT key; + DBT data; + char* ptr; + char* endptr; + int dbstatus; + if (windex == NULL || vector == NULL) { + tdb_ASSERT(0); + return TDB_FAILURE; + } + node = tdbVectorGetNonInternedNode(vector, 2 /* XXX Stupid hardcoding */); + if (node == NULL) return TDB_FAILURE; + if (node->type != TDBTYPE_STRING) { + return TDB_SUCCESS; /* Quietly do nothing if it's not a string. */ + } + winfo = splitIntoWords(node->d.str.string, winfobuf, + sizeof(winfobuf) / sizeof(winfobuf[0])); + if (!winfo) return TDB_FAILURE; + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + for (wptr = winfo ; wptr->length > 0 ; wptr++) { + ptr = buf; + endptr = buf + sizeof(buf); + tdbPutString(&ptr, wptr->word, endptr); + tdbPutPtr(&ptr, tdbVectorGetRecordNumber(vector), endptr); + tdbPutUInt16(&ptr, (TDBUint16) wptr->position, endptr); + if (ptr >= endptr) { + tdb_ASSERT(0); + } else { + key.size = ptr - buf; + key.data = buf; + dbstatus = windex->db->put(windex->db, + tdbGetTransaction(windex->base), + &key, &data, 0); + if (dbstatus != DB_OK) return TDB_FAILURE; + } + } + if (winfo != winfobuf) { + tdb_Free(winfo); + } + return TDB_SUCCESS; +} + + +TDBStatus +tdbWindexRemove(TDBWindex* windex, TDBVector* vector) +{ + TDBNodePtr node; + WordInfo winfobuf[20]; + WordInfo* winfo; + WordInfo* wptr; + DBT key; + DBT data; + char* ptr; + char* endptr; + char buf[100]; + int dbstatus; + if (windex == NULL || vector == NULL) { + tdb_ASSERT(0); + return TDB_FAILURE; + } + node = tdbVectorGetNonInternedNode(vector, 2 /* XXX Stupid hardcoding */); + if (node == NULL) return TDB_FAILURE; + if (node->type != TDBTYPE_STRING) { + return TDB_SUCCESS; /* Quietly do nothing if it's not a string. */ + } + winfo = splitIntoWords(node->d.str.string, winfobuf, + sizeof(winfobuf) / sizeof(winfobuf[0])); + if (!winfo) return TDB_FAILURE; + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + for (wptr = winfo ; wptr->length > 0 ; wptr++) { + ptr = buf; + endptr = buf + sizeof(buf); + tdbPutString(&ptr, wptr->word, endptr); + tdbPutPtr(&ptr, tdbVectorGetRecordNumber(vector), endptr); + tdbPutUInt16(&ptr, (TDBUint16) wptr->position, endptr); + if (ptr >= endptr) { + tdb_ASSERT(0); + } else { + key.size = ptr - buf; + key.data = buf; + dbstatus = windex->db->del(windex->db, + tdbGetTransaction(windex->base), + &key, 0); + if (dbstatus != DB_OK) return TDB_FAILURE; + } + } + if (winfo != winfobuf) { + tdb_Free(winfo); + } + return TDB_SUCCESS; + +} + + +TDBWindexCursor* +tdbWindexGetCursor(TDBWindex* windex, const char* string) +{ + DBT key; + DBT data; + int dbstatus; + TDBWindexCursor* cursor; + TDBInt32 i; + TDBInt32 l; + if (windex == NULL || string == NULL) { + tdb_ASSERT(0); + return NULL; + } + cursor = tdb_NEWZAP(TDBWindexCursor); + if (!cursor) return NULL; + cursor->windex = windex; + cursor->string = tdb_strdup(string); + if (!cursor->string) goto FAIL; + cursor->winfo = splitIntoWords(cursor->string, NULL, 0); + if (cursor->winfo == NULL) goto FAIL; + + cursor->main = 0; + cursor->cmdflag = DB_CURRENT; + + for (i=0 ; cursor->winfo[i].length > 0 ; i++) { + l = cursor->winfo[i].length; + if (l > cursor->winfo[cursor->main].length) { + cursor->main = i; + } + cursor->numwinfo = i + 1; + } + + if (cursor->numwinfo == 0) goto FAIL; + + + dbstatus = windex->db->cursor(windex->db, NULL, + &(cursor->cursor), 0); + if (dbstatus != DB_OK) goto FAIL; + + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + + key.data = cursor->winfo[cursor->main].word; + key.size = cursor->winfo[cursor->main].length + 1; + + dbstatus = cursor->cursor->c_get(cursor->cursor, &key, &data, + DB_SET_RANGE); + if (dbstatus != DB_OK && dbstatus != DB_NOTFOUND) goto FAIL; + + return cursor; + FAIL: + tdbWindexCursorFree(cursor); + return NULL; +} + + +TDBStatus +tdbWindexCursorFree(TDBWindexCursor* cursor) +{ + if (!cursor) { + tdb_ASSERT(0); + return TDB_FAILURE; + } + if (cursor->string) tdb_Free(cursor->string); + if (cursor->cursor) cursor->cursor->c_close(cursor->cursor); + if (cursor->winfo) tdb_Free(cursor->winfo); + tdb_Free(cursor); + return TDB_SUCCESS; +} + + +TDBVector* +tdbWindexGetCursorValue(TDBWindexCursor* cursor) +{ + TDBInt32 l; + DBT key; + DBT data; + TDBBool match; + TDBPtr recordnum; + TDBInt32 position; + TDBInt32 p; + char* ptr; + char* endptr; + TDBInt32 i; + char buf[MAXWORDLEN + 20]; + int dbstatus; + TDBWindex* windex; + TDBVector* vector; + TDBNodePtr node; + if (cursor == NULL) { + tdb_ASSERT(0); + return NULL; + } + windex = cursor->windex; + while (1) { + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + dbstatus = cursor->cursor->c_get(cursor->cursor, &key, &data, + cursor->cmdflag); + cursor->cmdflag = DB_NEXT; + if (dbstatus != DB_OK) return NULL; + l = cursor->winfo[cursor->main].length; + if (key.size <= l || + memcmp(key.data, cursor->winfo[cursor->main].word, l + 1) != 0) { + /* We're past the end of matching things. */ + return NULL; + } + ptr = key.data + l + 1; + endptr = key.data + key.size; + recordnum = tdbGetPtr(&ptr, endptr); + position = tdbGetUInt16(&ptr, endptr); + position -= cursor->winfo[cursor->main].position; + match = TDB_TRUE; + for (i=0 ; inumwinfo ; i++) { + if (i == cursor->main) continue; + p = position + cursor->winfo[i].position; + if (p < 0) { + match = TDB_FALSE; + break; + } + ptr = buf; + endptr = buf + sizeof(buf); + tdbPutString(&ptr, cursor->winfo[i].word, endptr); + tdbPutPtr(&ptr, recordnum, endptr); + tdbPutUInt16(&ptr, p, endptr); + key.data = buf; + key.size = ptr - buf; + memset(&data, 0, sizeof(data)); + dbstatus = windex->db->get(windex->db, + tdbGetTransaction(windex->base), + &key, &data, 0); + if (dbstatus != DB_OK) { + match = TDB_FALSE; + break; + } + } + if (match) { + vector = tdbVectorNewFromRecord(windex->base, recordnum); + node = tdbVectorGetNonInternedNode(vector, 2); + if (node && node->type == TDBTYPE_STRING && + tdb_strcasestr(node->d.str.string, cursor->string) != NULL) { + return vector; + } + tdbVectorFree(vector); + } + } +} + diff --git a/db/tripledb/src/windex.h b/db/tripledb/src/windex.h new file mode 100644 index 00000000000..dc179d9a64b --- /dev/null +++ b/db/tripledb/src/windex.h @@ -0,0 +1,44 @@ +/* -*- Mode: C; indent-tabs-mode: nil; -*- + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the TripleDB database library. + * + * The Initial Developer of the Original Code is Geocast Network Systems. + * Portions created by Geocast are + * Copyright (C) 2000 Geocast Network Systems. All + * Rights Reserved. + * + * Contributor(s): Terry Weissman + */ + +#ifndef _windex_h_ +#define _windex_h_ 1 + +extern TDBWindex* tdbWindexNew(TDBBase* base); + +extern void tdbWindexFree(TDBWindex* windex); + +extern TDBStatus tdbWindexSync(TDBWindex* windex); + +extern TDBStatus tdbWindexAdd(TDBWindex* windex, TDBVector* vector); + +extern TDBStatus tdbWindexRemove(TDBWindex* windex, TDBVector* vector); + +extern TDBWindexCursor* tdbWindexGetCursor(TDBWindex* windex, + const char* string); + +extern TDBStatus tdbWindexCursorFree(TDBWindexCursor* cursor); + +extern TDBVector* tdbWindexGetCursorValue(TDBWindexCursor* cursor); + + +#endif /* _windex_h_ */