зеркало из https://github.com/mozilla/gecko-dev.git
TripleDB has been completely rewritten. Now requires Sleepycat Berkeley DB.
This commit is contained in:
Родитель
ac9b617117
Коммит
49892e9607
|
@ -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.
|
||||
|
|
|
@ -1,554 +0,0 @@
|
|||
/* -*- 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) 1999 Geocast Network Systems. All
|
||||
* Rights Reserved.
|
||||
*
|
||||
* Contributor(s): Terry Weissman <terry@mozilla.org>
|
||||
*/
|
||||
|
||||
/* Routines that add or remove things to the database. */
|
||||
|
||||
#include "tdbtypes.h"
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
#include "tdbdebug.h"
|
||||
#include "prprf.h"
|
||||
static PRBool makedots = PR_FALSE; /* If true, print out dot-graphs of
|
||||
every step of balancing, to help my
|
||||
poor mind debug. */
|
||||
static int dotcount = 0;
|
||||
#endif
|
||||
|
||||
PRStatus TDBAdd(TDB* db, TDBNodePtr triple[3])
|
||||
{
|
||||
PRStatus status = PR_FAILURE;
|
||||
TDBRecord* record;
|
||||
TDBPtr position;
|
||||
PRInt32 tree;
|
||||
PRInt32 i;
|
||||
PRInt64 cmp;
|
||||
PR_ASSERT(db != NULL);
|
||||
if (db == NULL) return PR_FAILURE;
|
||||
PR_Lock(db->mutex);
|
||||
|
||||
/* First, see if we already have this triple around... */
|
||||
tree = 0; /* Hard-coded knowledge that tree zero does
|
||||
things in [0], [1], [2] order. ### */
|
||||
|
||||
position = db->roots[tree];
|
||||
while (position) {
|
||||
record = tdbLoadRecord(db, position);
|
||||
PR_ASSERT(record);
|
||||
if (!record) {
|
||||
goto DONE;
|
||||
}
|
||||
PR_ASSERT(record->position == position);
|
||||
if (record->position != position) {
|
||||
goto DONE;
|
||||
}
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
cmp = tdbCompareNodes(triple[i], record->data[i]);
|
||||
if (cmp < 0) {
|
||||
position = record->entry[tree].child[0];
|
||||
break;
|
||||
} else if (cmp > 0) {
|
||||
position = record->entry[tree].child[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (position == record->position) {
|
||||
/* This means that our new entry exactly matches this one. So,
|
||||
we're done. */
|
||||
status = PR_SUCCESS;
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
|
||||
tdbThrowOutCursorCaches(db);
|
||||
record = tdbAllocateRecord(db, triple);
|
||||
if (record == NULL) {
|
||||
goto DONE;
|
||||
}
|
||||
for (tree=0 ; tree<NUMTREES ; tree++) {
|
||||
status = tdbAddToTree(db, record, tree);
|
||||
if (status != PR_SUCCESS) goto DONE;
|
||||
}
|
||||
status = tdbQueueMatchingCallbacks(db, record, TDBACTION_ADDED);
|
||||
|
||||
|
||||
DONE:
|
||||
if (status == PR_SUCCESS) {
|
||||
tdbFlush(db);
|
||||
} else {
|
||||
tdbThrowAwayUnflushedChanges(db);
|
||||
}
|
||||
PR_Unlock(db->mutex);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
PRStatus TDBReplace(TDB* db, TDBNodePtr triple[3])
|
||||
{
|
||||
/* Write me correctly!!! This works, but is inefficient. ### */
|
||||
|
||||
PRStatus status;
|
||||
TDBNodeRange range[3];
|
||||
range[0].min = triple[0];
|
||||
range[0].max = triple[0];
|
||||
range[1].min = triple[1];
|
||||
range[1].max = triple[1];
|
||||
range[2].min = NULL;
|
||||
range[2].max = NULL;
|
||||
status = TDBRemove(db, range);
|
||||
if (status == PR_SUCCESS) {
|
||||
status = TDBAdd(db, triple);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
PRStatus TDBRemove(TDB* db, TDBNodeRange range[3])
|
||||
{
|
||||
/* This could definitely be faster. We're querying the database for
|
||||
a matching item, then we go search for it again when we delete it.
|
||||
The two operations probably ought to be merged. ### */
|
||||
PRStatus status;
|
||||
TDBCursor* cursor;
|
||||
TDBTriple* triple;
|
||||
TDBPtr position;
|
||||
TDBRecord* record;
|
||||
PRInt32 tree;
|
||||
PR_ASSERT(db != NULL);
|
||||
if (db == NULL) return PR_FAILURE;
|
||||
PR_Lock(db->mutex);
|
||||
tdbThrowOutCursorCaches(db);
|
||||
for (;;) {
|
||||
cursor = tdbQueryNolock(db, range, NULL);
|
||||
if (!cursor) goto FAIL;
|
||||
triple = tdbGetResultNolock(cursor);
|
||||
if (triple) {
|
||||
/* Probably ought to play refcnt games with this to prevent it
|
||||
from being removed from the cache. ### */
|
||||
position = cursor->lasthit->position;
|
||||
}
|
||||
tdbFreeCursorNolock(cursor);
|
||||
cursor = NULL;
|
||||
if (!triple) {
|
||||
/* No more hits; all done. */
|
||||
break;
|
||||
}
|
||||
record = tdbLoadRecord(db, position);
|
||||
if (!record) goto FAIL;
|
||||
|
||||
for (tree=0 ; tree<NUMTREES ; tree++) {
|
||||
status = tdbRemoveFromTree(db, record, tree);
|
||||
if (status != PR_SUCCESS) goto FAIL;
|
||||
}
|
||||
status = tdbAddToTree(db, record, -1);
|
||||
if (status != PR_SUCCESS) goto FAIL;
|
||||
|
||||
status = tdbQueueMatchingCallbacks(db, record, TDBACTION_REMOVED);
|
||||
if (status != PR_SUCCESS) goto FAIL;
|
||||
|
||||
tdbFlush(db);
|
||||
}
|
||||
PR_Unlock(db->mutex);
|
||||
return PR_SUCCESS;
|
||||
FAIL:
|
||||
tdbThrowAwayUnflushedChanges(db);
|
||||
PR_Unlock(db->mutex);
|
||||
return PR_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef MIN
|
||||
#undef MIN
|
||||
#endif
|
||||
#ifdef MAX
|
||||
#undef MAX
|
||||
#endif
|
||||
|
||||
#define MIN(a,b) ((a) < (b) ? (a) : (b))
|
||||
#define MAX(a,b) ((a) > (b) ? (a) : (b))
|
||||
|
||||
static PRBool
|
||||
rotateOnce(TDB* db, TDBPtr* rootptr, TDBRecord* oldroot, PRInt32 tree,
|
||||
PRInt32 dir)
|
||||
{
|
||||
PRInt32 otherdir = 1 - dir;
|
||||
PRBool heightChanged;
|
||||
TDBPtr otherptr;
|
||||
TDBRecord* other;
|
||||
PRInt8 oldrootbal;
|
||||
PRInt8 otherbal;
|
||||
|
||||
#ifdef DEBUG
|
||||
if (makedots) {
|
||||
char* filename;
|
||||
filename = PR_smprintf("/tmp/balance%d-%d.dot", tree, dotcount++);
|
||||
TDBMakeDotGraph(db, filename, tree);
|
||||
PR_smprintf_free(filename);
|
||||
}
|
||||
#endif
|
||||
|
||||
PR_ASSERT(dir == 0 || dir == 1);
|
||||
if (dir != 0 && dir != 1) return PR_FALSE;
|
||||
|
||||
otherptr = oldroot->entry[tree].child[otherdir];
|
||||
if (otherptr == 0) {
|
||||
tdbMarkCorrupted(db);
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
other = tdbLoadRecord(db, otherptr);
|
||||
heightChanged = (other->entry[tree].balance != 0);
|
||||
|
||||
*rootptr = otherptr;
|
||||
|
||||
oldroot->entry[tree].child[otherdir] = other->entry[tree].child[dir];
|
||||
other->entry[tree].child[dir] = oldroot->position;
|
||||
|
||||
/* update balances */
|
||||
oldrootbal = oldroot->entry[tree].balance;
|
||||
otherbal = other->entry[tree].balance;
|
||||
if (dir == 0) {
|
||||
oldrootbal -= 1 + MAX(otherbal, 0);
|
||||
otherbal -= 1 - MIN(oldrootbal, 0);
|
||||
} else {
|
||||
oldrootbal += 1 - MIN(otherbal, 0);
|
||||
otherbal += 1 + MAX(oldrootbal, 0);
|
||||
}
|
||||
oldroot->entry[tree].balance = oldrootbal;
|
||||
other->entry[tree].balance = otherbal;
|
||||
|
||||
oldroot->dirty = PR_TRUE;
|
||||
other->dirty = PR_TRUE;
|
||||
|
||||
return heightChanged;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
rotateTwice(TDB* db, TDBPtr* rootptr, TDBRecord* oldroot, PRInt32 tree,
|
||||
PRInt32 dir)
|
||||
{
|
||||
PRInt32 otherdir = 1 - dir;
|
||||
TDBRecord* child;
|
||||
|
||||
PR_ASSERT(dir == 0 || dir == 1);
|
||||
if (dir != 0 && dir != 1) return;
|
||||
|
||||
child = tdbLoadRecord(db, oldroot->entry[tree].child[otherdir]);
|
||||
PR_ASSERT(child);
|
||||
if (child == NULL) return;
|
||||
|
||||
rotateOnce(db, &(oldroot->entry[tree].child[otherdir]), child, tree,
|
||||
otherdir);
|
||||
oldroot->dirty = PR_TRUE;
|
||||
rotateOnce(db, rootptr, oldroot, tree, dir);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static PRStatus
|
||||
balance(TDB* db, TDBPtr* rootptr, TDBRecord* oldroot, PRInt32 tree,
|
||||
PRBool* heightchange)
|
||||
{
|
||||
PRInt8 oldbalance;
|
||||
TDBRecord* child;
|
||||
*heightchange = PR_FALSE;
|
||||
|
||||
oldbalance = oldroot->entry[tree].balance;
|
||||
|
||||
if (oldbalance < -1) { /* need a right rotation */
|
||||
child = tdbLoadRecord(db, oldroot->entry[tree].child[0]);
|
||||
PR_ASSERT(child);
|
||||
if (child == NULL) return PR_FAILURE;
|
||||
if (child->entry[tree].balance == 1) {
|
||||
rotateTwice(db, rootptr, oldroot, tree, 1); /* double RL rotation
|
||||
needed */
|
||||
*heightchange = PR_TRUE;
|
||||
} else { /* single RR rotation needed */
|
||||
*heightchange = rotateOnce(db, rootptr, oldroot, tree, 1);
|
||||
}
|
||||
} else if (oldbalance > 1) { /* need a left rotation */
|
||||
child = tdbLoadRecord(db, oldroot->entry[tree].child[1]);
|
||||
PR_ASSERT(child);
|
||||
if (child == NULL) return PR_FAILURE;
|
||||
if (child->entry[tree].balance == -1) {
|
||||
rotateTwice(db, rootptr, oldroot, tree, 0); /* double LR rotation
|
||||
needed */
|
||||
*heightchange = PR_TRUE;
|
||||
} else { /* single LL rotation needed */
|
||||
*heightchange = rotateOnce(db, rootptr, oldroot, tree, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return PR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static PRStatus doAdd(TDB* db, TDBRecord* record, PRInt32 tree,
|
||||
PRInt32 comparerule, TDBPtr* rootptr,
|
||||
PRBool* heightchange)
|
||||
{
|
||||
PRBool increase = PR_FALSE;
|
||||
PRInt64 cmp;
|
||||
PRInt32 kid;
|
||||
PRStatus status;
|
||||
TDBRecord* cur;
|
||||
TDBPtr origptr;
|
||||
if (*rootptr == 0) {
|
||||
*rootptr = record->position;
|
||||
*heightchange = PR_TRUE;
|
||||
return PR_SUCCESS;
|
||||
}
|
||||
cur = tdbLoadRecord(db, *rootptr);
|
||||
if (!cur) return PR_FAILURE;
|
||||
cmp = tdbCompareRecords(record, cur, comparerule);
|
||||
PR_ASSERT(cmp != 0); /* We carefully should never insert a
|
||||
record that we already have. */
|
||||
if (cmp == 0) return PR_FAILURE;
|
||||
kid = (cmp < 0) ? 0 : 1;
|
||||
origptr = cur->entry[tree].child[kid];
|
||||
status = doAdd(db, record, tree, comparerule,
|
||||
&(cur->entry[tree].child[kid]), &increase);
|
||||
if (origptr != cur->entry[tree].child[kid]) {
|
||||
cur->dirty = PR_TRUE;
|
||||
}
|
||||
if (increase) {
|
||||
cur->entry[tree].balance += (kid == 0 ? -1 : 1);
|
||||
cur->dirty = PR_TRUE;
|
||||
}
|
||||
if (status != PR_SUCCESS) return status;
|
||||
if (increase && cur->entry[tree].balance != 0) {
|
||||
status = balance(db, rootptr, cur, tree, &increase);
|
||||
*heightchange = ! increase; /* If we did a rotate that absorbed
|
||||
the height change, then we don't want
|
||||
to propagate it on up. */
|
||||
}
|
||||
return PR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
PRStatus tdbAddToTree(TDB* db, TDBRecord* record, PRInt32 tree)
|
||||
{
|
||||
TDBTreeEntry* entry;
|
||||
PRBool checklinks;
|
||||
PRBool ignore;
|
||||
PRStatus status = PR_SUCCESS;
|
||||
TDBPtr* rootptr;
|
||||
TDBPtr origroot;
|
||||
PRInt32 comparerule = tree;
|
||||
|
||||
PR_ASSERT(record != NULL);
|
||||
if (record == NULL) return PR_FAILURE;
|
||||
|
||||
if (tree == -1) {
|
||||
tree = 0;
|
||||
rootptr = &(db->freeroot);
|
||||
} else {
|
||||
PR_ASSERT(tree >= 0 && tree < NUMTREES);
|
||||
if (tree < 0 || tree >= NUMTREES) {
|
||||
return PR_FAILURE;
|
||||
}
|
||||
rootptr = &(db->roots[tree]);
|
||||
}
|
||||
|
||||
/* Check that this record does not seem to be already in this tree. */
|
||||
entry = record->entry + tree;
|
||||
checklinks = (entry->child[0] == 0 &&
|
||||
entry->child[1] == 0 &&
|
||||
entry->balance == 0);
|
||||
PR_ASSERT(checklinks);
|
||||
if (!checklinks) return PR_FAILURE;
|
||||
|
||||
origroot = *rootptr;
|
||||
if (origroot == 0) {
|
||||
*rootptr = record->position;
|
||||
} else {
|
||||
status = doAdd(db, record, tree, comparerule, rootptr, &ignore);
|
||||
}
|
||||
if (origroot != *rootptr) {
|
||||
db->rootdirty = PR_TRUE;
|
||||
}
|
||||
db->dirty = PR_TRUE;
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static PRStatus doRemove(TDB* db, TDBRecord* record, PRInt32 tree,
|
||||
PRInt32 comparerule, TDBPtr* rootptr,
|
||||
TDBPtr** foundleafptr, TDBRecord** foundleaf,
|
||||
PRBool* heightchange)
|
||||
{
|
||||
PRStatus status;
|
||||
TDBRecord* cur;
|
||||
TDBPtr* leafptr;
|
||||
TDBRecord* leaf;
|
||||
PRInt32 kid;
|
||||
TDBPtr origptr;
|
||||
TDBPtr kid0;
|
||||
TDBPtr kid1;
|
||||
PRInt64 cmp;
|
||||
PRBool decrease = PR_FALSE;
|
||||
PRInt32 tmp;
|
||||
PRInt8 tmpbal;
|
||||
PRInt32 i;
|
||||
PR_ASSERT(*rootptr != 0);
|
||||
if (*rootptr == 0) return PR_FAILURE;
|
||||
cur = tdbLoadRecord(db, *rootptr);
|
||||
if (!cur) return PR_FAILURE;
|
||||
if (record == NULL) {
|
||||
cur->dirty = PR_TRUE; /* Oh, what a bad, bad hack. Need a better
|
||||
way to make sure not to miss a parent
|
||||
pointer that we've changed. ### */
|
||||
}
|
||||
kid0 = cur->entry[tree].child[0];
|
||||
kid1 = cur->entry[tree].child[1];
|
||||
if (record == NULL) {
|
||||
/* We're looking for the smallest possible leaf node. */
|
||||
PR_ASSERT(foundleafptr != NULL && foundleaf != NULL);
|
||||
if (kid0) cmp = -1;
|
||||
else {
|
||||
cmp = 0;
|
||||
*foundleafptr = rootptr;
|
||||
*foundleaf = cur;
|
||||
}
|
||||
} else {
|
||||
PR_ASSERT(foundleafptr == NULL && foundleaf == NULL);
|
||||
cmp = tdbCompareRecords(record, cur, comparerule);
|
||||
}
|
||||
if (cmp == 0) {
|
||||
PR_ASSERT(record == cur || record == NULL);
|
||||
if (record != cur && record != NULL) return PR_FAILURE;
|
||||
if (kid0 == 0 && kid1 == 0) {
|
||||
/* We're a leaf node. */
|
||||
*rootptr = 0;
|
||||
*heightchange = PR_TRUE;
|
||||
return PR_SUCCESS;
|
||||
} else if (kid0 == 0 || kid1 == 0) {
|
||||
/* Replace us with our single child. */
|
||||
*rootptr = kid0 != 0 ? kid0 : kid1;
|
||||
cur->entry[tree].child[0] = 0;
|
||||
cur->entry[tree].child[1] = 0;
|
||||
cur->entry[tree].balance = 0;
|
||||
cur->dirty = PR_TRUE;
|
||||
*heightchange = PR_TRUE;
|
||||
return PR_SUCCESS;
|
||||
} else {
|
||||
/* Ick. We're a node in the middle of the tree. Find who to
|
||||
replace us with. */
|
||||
kid = 1; /* Informs balancing code later that we are
|
||||
taking things from the right subtree. */
|
||||
status = doRemove(db, NULL, tree, comparerule,
|
||||
&(cur->entry[tree].child[1]),
|
||||
&leafptr, &leaf, &decrease);
|
||||
/* Swap us in the tree with leaf. Don't use the kid0/kid1
|
||||
variables any more, as they may no longer be valid. */
|
||||
|
||||
if (record != NULL) {
|
||||
leaf->entry[tree].child[0] = cur->entry[tree].child[0];
|
||||
leaf->entry[tree].child[1] = cur->entry[tree].child[1];
|
||||
leaf->entry[tree].balance = cur->entry[tree].balance;
|
||||
cur->entry[tree].child[0] = 0;
|
||||
cur->entry[tree].child[1] = 0;
|
||||
cur->entry[tree].balance = 0;
|
||||
cur->dirty = PR_TRUE;
|
||||
*rootptr = leaf->position;
|
||||
cur = leaf;
|
||||
cur->dirty = PR_TRUE;
|
||||
} else {
|
||||
*foundleaf = cur;
|
||||
*foundleafptr = leafptr;
|
||||
PR_ASSERT(**foundleafptr == leaf->position);
|
||||
*leafptr = cur->position;
|
||||
*rootptr = leaf->position;
|
||||
for (i=0 ; i<2 ; i++) {
|
||||
tmp = leaf->entry[tree].child[i];
|
||||
leaf->entry[tree].child[i] = cur->entry[tree].child[i];
|
||||
cur->entry[tree].child[i] = tmp;
|
||||
}
|
||||
tmpbal = leaf->entry[tree].balance;
|
||||
leaf->entry[tree].balance = cur->entry[tree].balance;
|
||||
cur->entry[tree].balance = tmpbal;
|
||||
cur->dirty = PR_TRUE;
|
||||
leaf->dirty = PR_TRUE;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
kid = (cmp < 0) ? 0 : 1;
|
||||
origptr = cur->entry[tree].child[kid];
|
||||
status = doRemove(db, record, tree, comparerule,
|
||||
&(cur->entry[tree].child[kid]), foundleafptr,
|
||||
foundleaf, &decrease);
|
||||
if (origptr != cur->entry[tree].child[kid]) {
|
||||
cur->dirty = PR_TRUE;
|
||||
}
|
||||
}
|
||||
if (decrease) {
|
||||
cur->entry[tree].balance += (kid == 0 ? 1 : -1);
|
||||
cur->dirty = PR_TRUE;
|
||||
}
|
||||
if (status != PR_SUCCESS) return status;
|
||||
if (decrease) {
|
||||
if (cur->entry[tree].balance != 0) {
|
||||
status = balance(db, rootptr, cur, tree, &decrease);
|
||||
*heightchange = decrease;
|
||||
} else {
|
||||
*heightchange = PR_TRUE;
|
||||
}
|
||||
}
|
||||
return PR_SUCCESS;
|
||||
}
|
||||
|
||||
PRStatus tdbRemoveFromTree(TDB* db, TDBRecord* record, PRInt32 tree)
|
||||
{
|
||||
PRStatus status;
|
||||
PRInt32 comparerule = tree;
|
||||
TDBPtr* rootptr;
|
||||
TDBPtr origroot;
|
||||
PRBool ignore;
|
||||
|
||||
PR_ASSERT(record != NULL);
|
||||
if (record == NULL) return PR_FAILURE;
|
||||
if (tree == -1) {
|
||||
tree = 0;
|
||||
rootptr = &(db->freeroot);
|
||||
} else {
|
||||
PR_ASSERT(tree >= 0 && tree < NUMTREES);
|
||||
if (tree < 0 || tree >= NUMTREES) {
|
||||
return PR_FAILURE;
|
||||
}
|
||||
rootptr = &(db->roots[tree]);
|
||||
}
|
||||
origroot = *rootptr;
|
||||
PR_ASSERT(origroot != 0);
|
||||
if (origroot == 0) return PR_FAILURE;
|
||||
status = doRemove(db, record, tree, comparerule, rootptr, NULL, NULL,
|
||||
&ignore);
|
||||
|
||||
if (origroot != *rootptr) {
|
||||
db->rootdirty = PR_TRUE;
|
||||
}
|
||||
db->dirty = PR_TRUE;
|
||||
record->dirty = PR_TRUE;
|
||||
return status;
|
||||
}
|
|
@ -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 <terry@mozilla.org>
|
||||
*/
|
||||
|
||||
#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 */
|
|
@ -1,213 +0,0 @@
|
|||
/* -*- 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) 1999 Geocast Network Systems. All
|
||||
* Rights Reserved.
|
||||
*
|
||||
* Contributor(s): Terry Weissman <terry@mozilla.org>
|
||||
*/
|
||||
|
||||
/* Routines that query things from the database. */
|
||||
|
||||
#include "tdbtypes.h"
|
||||
|
||||
static void
|
||||
tdbFreeCallbackInfo(TDBCallbackInfo* info)
|
||||
{
|
||||
PRInt32 i;
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
if (info->range[i].min != NULL) TDBFreeNode(info->range[i].min);
|
||||
if (info->range[i].max != NULL) TDBFreeNode(info->range[i].max);
|
||||
}
|
||||
PR_Free(info);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
tdbFreePendingCall(TDBPendingCall* call)
|
||||
{
|
||||
PRInt32 i;
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
if (call->triple.data[i] != NULL) {
|
||||
TDBFreeNode(call->triple.data[i]);
|
||||
}
|
||||
}
|
||||
PR_Free(call);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
tdbCallbackThread(void* closure)
|
||||
{
|
||||
PRStatus status;
|
||||
TDB* db = (TDB*) closure;
|
||||
|
||||
PRLock* mutex = db->mutex;
|
||||
PRCondVar* cvar = db->callbackcvargo;
|
||||
|
||||
TDBPendingCall* call;
|
||||
TDBPendingCall* tmp;
|
||||
PR_Lock(mutex);
|
||||
while (1) {
|
||||
while (db->firstpendingcall == NULL) {
|
||||
db->callbackidle = PR_TRUE;
|
||||
PR_NotifyAllCondVar(db->callbackcvaridle); /* Inform anyone who
|
||||
cares that we are
|
||||
idle. */
|
||||
if (db->killcallbackthread) {
|
||||
/* This db is being closed; go away now. */
|
||||
PR_Unlock(db->mutex);
|
||||
return;
|
||||
}
|
||||
status = PR_WaitCondVar(cvar, PR_INTERVAL_NO_TIMEOUT);
|
||||
db->callbackidle = PR_FALSE;
|
||||
}
|
||||
call = db->firstpendingcall;
|
||||
db->firstpendingcall = NULL;
|
||||
db->lastpendingcall = NULL;
|
||||
PR_Unlock(db->mutex);
|
||||
while (call) {
|
||||
(*call->func)(db, call->closure, &(call->triple), call->action);
|
||||
tmp = call;
|
||||
call = call->next;
|
||||
tdbFreePendingCall(tmp);
|
||||
}
|
||||
PR_Lock(db->mutex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PRStatus tdbQueueMatchingCallbacks(TDB* db, TDBRecord* record,
|
||||
PRInt32 action)
|
||||
{
|
||||
TDBCallbackInfo* info;
|
||||
TDBPendingCall* call;
|
||||
PRInt32 i;
|
||||
for (info = db->firstcallback ; info ; info = info->nextcallback) {
|
||||
if (tdbMatchesRange(record, info->range)) {
|
||||
call = PR_NEWZAP(TDBPendingCall);
|
||||
if (!call) return PR_FAILURE;
|
||||
call->func = info->func;
|
||||
call->closure = info->closure;
|
||||
call->action = action;
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
call->triple.data[i] = tdbNodeDup(record->data[i]);
|
||||
if (call->triple.data[i] == NULL) {
|
||||
tdbFreePendingCall(call);
|
||||
return PR_FAILURE;
|
||||
}
|
||||
}
|
||||
if (db->lastpendingcall) {
|
||||
db->lastpendingcall->next = call;
|
||||
}
|
||||
db->lastpendingcall = call;
|
||||
if (db->firstpendingcall == NULL) {
|
||||
db->firstpendingcall = call;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (db->firstpendingcall != NULL) {
|
||||
/* Kick the background thread. */
|
||||
PR_NotifyAllCondVar(db->callbackcvargo);
|
||||
}
|
||||
return PR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
PRStatus TDBAddCallback(TDB* db, TDBNodeRange range[3],
|
||||
TDBCallbackFunction func, void* closure)
|
||||
{
|
||||
TDBCallbackInfo* info;
|
||||
PRInt32 i;
|
||||
PR_ASSERT(db != NULL);
|
||||
if (db == NULL) return PR_FAILURE;
|
||||
info = PR_NEWZAP(TDBCallbackInfo);
|
||||
if (!info) return PR_FAILURE;
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
if (range[i].min) {
|
||||
info->range[i].min = tdbNodeDup(range[i].min);
|
||||
if (info->range[i].min == NULL) goto FAIL;
|
||||
}
|
||||
if (range[i].max) {
|
||||
info->range[i].max = tdbNodeDup(range[i].max);
|
||||
if (info->range[i].max == NULL) goto FAIL;
|
||||
}
|
||||
}
|
||||
info->func = func;
|
||||
info->closure = closure;
|
||||
info->nextcallback = db->firstcallback;
|
||||
PR_Lock(db->mutex);
|
||||
db->firstcallback = info;
|
||||
PR_Unlock(db->mutex);
|
||||
return PR_SUCCESS;
|
||||
|
||||
FAIL:
|
||||
tdbFreeCallbackInfo(info);
|
||||
return PR_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
PRStatus TDBRemoveCallback(TDB* db, TDBNodeRange range[3],
|
||||
TDBCallbackFunction func, void* closure)
|
||||
{
|
||||
PRStatus status = PR_FAILURE;
|
||||
TDBCallbackInfo** ptr;
|
||||
TDBCallbackInfo* info;
|
||||
PRInt32 i;
|
||||
PRBool match;
|
||||
PR_ASSERT(db != NULL);
|
||||
if (db == NULL) return PR_FAILURE;
|
||||
PR_Lock(db->mutex);
|
||||
for (ptr = &(db->firstcallback) ; *ptr ; ptr = &((*ptr)->nextcallback)) {
|
||||
info = *ptr;
|
||||
if (info->func == func && info->closure == closure) {
|
||||
match = PR_TRUE;
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
if (range[i].min != info->range[i].min &&
|
||||
(range[i].min == NULL || info->range[i].min == NULL ||
|
||||
tdbCompareNodes(range[i].min, info->range[i].min) != 0)) {
|
||||
match = PR_FALSE;
|
||||
break;
|
||||
}
|
||||
if (range[i].max != info->range[i].max &&
|
||||
(range[i].max == NULL || info->range[i].max == NULL ||
|
||||
tdbCompareNodes(range[i].max, info->range[i].max) != 0)) {
|
||||
match = PR_FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
*ptr = info->nextcallback;
|
||||
tdbFreeCallbackInfo(info);
|
||||
status = PR_SUCCESS;
|
||||
|
||||
/* We now make sure that we have no outstanding callbacks
|
||||
queued up to the callback we just removed. It would be
|
||||
real bad to call that callback after we return. So, we
|
||||
make sure to call it now. */
|
||||
while (db->firstpendingcall) {
|
||||
PR_NotifyAllCondVar(db->callbackcvargo);
|
||||
PR_WaitCondVar(db->callbackcvaridle,
|
||||
PR_INTERVAL_NO_TIMEOUT);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
PR_Unlock(db->mutex);
|
||||
return status;
|
||||
}
|
|
@ -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 <terry@mozilla.org>
|
||||
*/
|
||||
|
||||
#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 ; i<cursor->numfields ; 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 ; i<cursor->numfields ; 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 ; i<cursor->numfields ; 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 ; i<cursor->numfields ; 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 ; i<numlayers ; i++) {
|
||||
l = tdbGetLayer(cursor->tdb, 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;
|
||||
}
|
|
@ -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 <terry@mozilla.org>
|
||||
*/
|
||||
|
||||
#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_ */
|
|
@ -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 <terry@mozilla.org>
|
||||
*/
|
||||
|
||||
/* #include "db/db_int.h" */
|
||||
#define MEGABYTE 1048576
|
||||
|
||||
#include "tdbtypes.h"
|
||||
|
||||
#ifdef TDB_USE_NSPR
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#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<count ; i++) {
|
||||
PR_Free(names[i]);
|
||||
}
|
||||
PR_Free(names);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
func_dirlist(const char* dirname, char*** names, int* cnt)
|
||||
{
|
||||
int max = 0;
|
||||
int count = 0;
|
||||
char** result = NULL;
|
||||
PRDir* dir;
|
||||
PRDirEntry* entry;
|
||||
dir = PR_OpenDir(dirname);
|
||||
if (dir == NULL) return PR_GetError();
|
||||
while (NULL != (entry = PR_ReadDir(dir, PR_SKIP_NONE))) {
|
||||
if (count >= 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 */
|
|
@ -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 <terry@mozilla.org>
|
||||
*/
|
||||
|
||||
#ifndef _dbnspr_h_
|
||||
#define _dbnspr_h_ 1
|
||||
|
||||
extern TDBStatus tdbMakeDBCompatableWithNSPR(DB_ENV* env);
|
||||
|
||||
#endif /* _dbnspr_h_ */
|
|
@ -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 <terry@mozilla.org>
|
||||
*/
|
||||
|
||||
/* 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 ; i<indent ; i++) {
|
||||
PR_fprintf(fid, " ");
|
||||
}
|
||||
PR_fprintf(fid, "[%s] pos=%d, len=%d, bal=%d ", kidname, record->position,
|
||||
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 ; m<numindices ; m++) {
|
||||
if (fid) tdb_fprintf(fid, "Checking index %d ...", m);
|
||||
rtype = tdbGetIndex(base, m);
|
||||
vector = tdbVectorNewFromNodes(base, nodes, 0, 0, 0);
|
||||
cursor = tdbRTypeGetCursor(rtype, vector);
|
||||
tdbVectorFree(vector);
|
||||
if (!cursor) continue;
|
||||
count = 0;
|
||||
flag = DB_CURRENT;
|
||||
while (NULL != (vector = tdbRTypeGetCursorValue(rtype,
|
||||
cursor, flag))) {
|
||||
flag = DB_NEXT;
|
||||
count++;
|
||||
for (i=0 ; i<numindices ; i++) {
|
||||
tdb_ASSERT(i == m || tdbRTypeProbe(tdbGetIndex(base, i),
|
||||
vector));
|
||||
}
|
||||
tdbVectorFree(vector);
|
||||
}
|
||||
for (tmp = info.set.next; tmp != &(info.set) ; tmp = tmp->next) {
|
||||
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...) */
|
||||
}
|
||||
|
|
|
@ -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 <terry@mozilla.org>
|
||||
*/
|
||||
|
||||
#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;
|
||||
}
|
|
@ -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 <terry@mozilla.org>
|
||||
*/
|
||||
|
||||
#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_ */
|
|
@ -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 <terry@mozilla.org>
|
||||
*/
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 <terry@mozilla.org>
|
||||
*/
|
||||
|
||||
#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_ */
|
|
@ -1,504 +0,0 @@
|
|||
/* -*- 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) 1999 Geocast Network Systems. All
|
||||
* Rights Reserved.
|
||||
*
|
||||
* Contributor(s): Terry Weissman <terry@mozilla.org>
|
||||
*/
|
||||
|
||||
/* Routines that query things from the database. */
|
||||
|
||||
#include "tdbtypes.h"
|
||||
|
||||
/* Argh, this same static array appears in record.c. Don't do that. ### */
|
||||
|
||||
static int key[NUMTREES][3] = {
|
||||
{0, 1, 2},
|
||||
{1, 0, 2},
|
||||
{2, 1, 0},
|
||||
{1, 2, 0}
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
freeParentChain(TDBCursor* cursor)
|
||||
{
|
||||
TDBParentChain* tmp;
|
||||
while (cursor->parent) {
|
||||
tmp = cursor->parent->next;
|
||||
cursor->parent->record->refcnt--;
|
||||
PR_ASSERT(cursor->parent->record->refcnt >= 0);
|
||||
PR_Free(cursor->parent);
|
||||
cursor->parent = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static PRStatus
|
||||
moveCursorForward(TDBCursor* cursor)
|
||||
{
|
||||
TDBRecord* cur;
|
||||
PRBool found;
|
||||
PRInt32 fwd;
|
||||
PRInt32 rev;
|
||||
TDBPtr ptr;
|
||||
PRInt32 tree = cursor->tree;
|
||||
TDBParentChain* parent;
|
||||
TDBParentChain* tmp;
|
||||
|
||||
cur = cursor->cur;
|
||||
|
||||
PR_ASSERT(cur != NULL);
|
||||
if (cur == NULL) {
|
||||
return PR_FAILURE;
|
||||
}
|
||||
cur->refcnt--;
|
||||
PR_ASSERT(cur->refcnt >= 0);
|
||||
cursor->cur = NULL;
|
||||
fwd = cursor->reverse ? 0 : 1;
|
||||
rev = cursor->reverse ? 1 : 0;
|
||||
ptr = cur->entry[tree].child[fwd];
|
||||
if (ptr != 0) {
|
||||
do {
|
||||
cur->refcnt++;
|
||||
parent = PR_NEWZAP(TDBParentChain);
|
||||
parent->record = cur;
|
||||
parent->next = cursor->parent;
|
||||
cursor->parent = parent;
|
||||
cur = tdbLoadRecord(cursor->db, ptr);
|
||||
PR_ASSERT(cur);
|
||||
if (!cur) {
|
||||
return PR_FAILURE;
|
||||
}
|
||||
ptr = cur->entry[tree].child[rev];
|
||||
} while (ptr != 0);
|
||||
} else {
|
||||
found = PR_FALSE;
|
||||
while (cursor->parent) {
|
||||
ptr = cur->position;
|
||||
cur = cursor->parent->record;
|
||||
cur->refcnt--;
|
||||
PR_ASSERT(cur->refcnt >= 0);
|
||||
tmp = cursor->parent->next;
|
||||
PR_Free(cursor->parent);
|
||||
cursor->parent = tmp;
|
||||
if (cur->entry[tree].child[rev] == ptr) {
|
||||
found = PR_TRUE;
|
||||
break;
|
||||
}
|
||||
if (cur->entry[tree].child[fwd] != ptr) {
|
||||
tdbMarkCorrupted(cursor->db);
|
||||
return PR_FAILURE;
|
||||
}
|
||||
}
|
||||
if (!found) cur = NULL;
|
||||
}
|
||||
if (cur) cur->refcnt++;
|
||||
cursor->cur = cur;
|
||||
return PR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static PRStatus findFirstNode(TDBCursor* cursor, TDBNodeRange range[3])
|
||||
{
|
||||
PRInt32 fwd;
|
||||
PRInt32 rev;
|
||||
PRBool reverse;
|
||||
PRInt32 tree;
|
||||
TDBParentChain* parent;
|
||||
TDBPtr curptr;
|
||||
TDBRecord* cur;
|
||||
PRInt64 cmp;
|
||||
|
||||
reverse = cursor->reverse;
|
||||
fwd = reverse ? 0 : 1;
|
||||
rev = reverse ? 1 : 0;
|
||||
tree = cursor->tree;
|
||||
|
||||
freeParentChain(cursor);
|
||||
|
||||
curptr = cursor->db->roots[tree];
|
||||
cur = NULL;
|
||||
while (curptr) {
|
||||
if (cur) {
|
||||
cur->refcnt++;
|
||||
parent = PR_NEWZAP(TDBParentChain);
|
||||
parent->record = cur;
|
||||
parent->next = cursor->parent;
|
||||
cursor->parent = parent;
|
||||
}
|
||||
cur = tdbLoadRecord(cursor->db, curptr);
|
||||
PR_ASSERT(cur);
|
||||
if (!cur) return PR_FAILURE;
|
||||
cmp = tdbCompareToRange(cur, range, tree);
|
||||
if (reverse) cmp = -cmp;
|
||||
if (cmp >= 0) {
|
||||
curptr = cur->entry[tree].child[rev];
|
||||
} else {
|
||||
curptr = cur->entry[tree].child[fwd];
|
||||
}
|
||||
}
|
||||
if (cursor->cur) {
|
||||
cursor->cur->refcnt--;
|
||||
PR_ASSERT(cursor->cur->refcnt >= 0);
|
||||
}
|
||||
cursor->cur = cur;
|
||||
if (cursor->cur) {
|
||||
cursor->cur->refcnt++;
|
||||
}
|
||||
while (cursor->cur != NULL &&
|
||||
tdbCompareToRange(cursor->cur, range, tree) < 0) {
|
||||
moveCursorForward(cursor);
|
||||
}
|
||||
return PR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
TDBCursor* tdbQueryNolock(TDB* db, TDBNodeRange range[3],
|
||||
TDBSortSpecification* sortspec)
|
||||
{
|
||||
PRInt32 rangescore[3];
|
||||
PRInt32 tree;
|
||||
PRInt32 curscore;
|
||||
PRInt32 bestscore = -1;
|
||||
PRInt32 i;
|
||||
TDBCursor* result;
|
||||
PRBool reverse;
|
||||
PRStatus status;
|
||||
|
||||
result = PR_NEWZAP(TDBCursor);
|
||||
if (!result) return NULL;
|
||||
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
if (range[i].min) {
|
||||
result->range[i].min = tdbNodeDup(range[i].min);
|
||||
if (result->range[i].min == NULL) goto FAIL;
|
||||
}
|
||||
if (range[i].max) {
|
||||
result->range[i].max = tdbNodeDup(range[i].max);
|
||||
if (result->range[i].max == NULL) goto FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
for (tree = 0 ; tree < NUMTREES ; tree++) {
|
||||
if (key[tree][0] == sortspec->keyorder[0] &&
|
||||
key[tree][1] == sortspec->keyorder[1] &&
|
||||
key[tree][2] == sortspec->keyorder[2]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (tree >= NUMTREES) {
|
||||
/* The passed in keyorder was not valid (which, in fact, is the usual
|
||||
case). Go find the best tree to use. */
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
rangescore[i] = 0;
|
||||
if (range[i].min != NULL || range[i].max != NULL) {
|
||||
/* Hey, some limitations were specified, we like this key
|
||||
some. */
|
||||
rangescore[i]++;
|
||||
if (range[i].min != NULL && range[i].max != NULL) {
|
||||
/* Ooh, we were given both minimum and maximum, that's
|
||||
better than only getting one.*/
|
||||
rangescore[i]++;
|
||||
if (tdbCompareNodes(range[i].min, range[i].max) == 0) {
|
||||
/* Say! This key was exactly specified. We like it
|
||||
best. */
|
||||
rangescore[i]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i=0 ; i<NUMTREES ; i++) {
|
||||
curscore = rangescore[key[i][0]] * 100 +
|
||||
rangescore[key[i][1]] * 10 +
|
||||
rangescore[key[i][2]];
|
||||
if (bestscore < curscore) {
|
||||
bestscore = curscore;
|
||||
tree = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reverse = sortspec != NULL && sortspec->reverse;
|
||||
|
||||
result->reverse = reverse;
|
||||
result->db = db;
|
||||
result->tree = tree;
|
||||
status = findFirstNode(result, range);
|
||||
if (status != PR_SUCCESS) goto FAIL;
|
||||
|
||||
result->nextcursor = db->firstcursor;
|
||||
if (result->nextcursor) {
|
||||
result->nextcursor->prevcursor = result;
|
||||
}
|
||||
db->firstcursor = result;
|
||||
|
||||
tdbFlush(db);
|
||||
return result;
|
||||
|
||||
|
||||
FAIL:
|
||||
tdbFreeCursorNolock(result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TDBCursor* TDBQuery(TDB* db, TDBNodeRange range[3],
|
||||
TDBSortSpecification* sortspec)
|
||||
{
|
||||
TDBCursor* result;
|
||||
PR_Lock(db->mutex);
|
||||
result = tdbQueryNolock(db, range, sortspec);
|
||||
PR_Unlock(db->mutex);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
PRStatus tdbFreeCursorNolock(TDBCursor* cursor)
|
||||
{
|
||||
PRInt32 i;
|
||||
TDB* db;
|
||||
PR_ASSERT(cursor);
|
||||
if (!cursor) return PR_FAILURE;
|
||||
db = cursor->db;
|
||||
if (cursor->cur) {
|
||||
cursor->cur->refcnt--;
|
||||
PR_ASSERT(cursor->cur->refcnt >= 0);
|
||||
}
|
||||
if (cursor->lasthit) {
|
||||
cursor->lasthit->refcnt--;
|
||||
PR_ASSERT(cursor->lasthit->refcnt >= 0);
|
||||
}
|
||||
freeParentChain(cursor);
|
||||
if (db) tdbFlush(db);
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
PR_FREEIF(cursor->range[i].min);
|
||||
PR_FREEIF(cursor->range[i].max);
|
||||
}
|
||||
if (cursor->prevcursor) {
|
||||
cursor->prevcursor->nextcursor = cursor->nextcursor;
|
||||
} else {
|
||||
if (db) db->firstcursor = cursor->nextcursor;
|
||||
}
|
||||
if (cursor->nextcursor) {
|
||||
cursor->nextcursor->prevcursor = cursor->prevcursor;
|
||||
}
|
||||
PR_Free(cursor);
|
||||
return PR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
PRStatus TDBFreeCursor(TDBCursor* cursor)
|
||||
{
|
||||
PRStatus status;
|
||||
TDB* db;
|
||||
PR_ASSERT(cursor);
|
||||
if (!cursor) return PR_FAILURE;
|
||||
db = cursor->db;
|
||||
PR_Lock(db->mutex);
|
||||
status = tdbFreeCursorNolock(cursor);
|
||||
#ifdef DEBUG
|
||||
if (db->firstcursor == 0) {
|
||||
/* There are no more cursors. No other thread can be in the middle of
|
||||
writing stuff, because we have the mutex. And so, there shouldn't
|
||||
be anything left in our cache of records; they should all be
|
||||
flushed out. So... */
|
||||
PR_ASSERT(db->firstrecord == NULL);
|
||||
}
|
||||
#endif
|
||||
PR_Unlock(db->mutex);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
TDBTriple* tdbGetResultNolock(TDBCursor* cursor)
|
||||
{
|
||||
PRStatus status;
|
||||
PRInt32 i;
|
||||
PRInt64 cmp;
|
||||
TDBNodeRange range[3];
|
||||
PR_ASSERT(cursor);
|
||||
if (!cursor) return NULL;
|
||||
if (cursor->cur == NULL && cursor->lasthit == NULL &&
|
||||
cursor->triple.data[0] != NULL) {
|
||||
/* Looks like someone did a write to the database since we last were
|
||||
here, and therefore threw away all our cached information about
|
||||
where we were. Go find our place again. */
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
range[i].min = cursor->triple.data[i];
|
||||
range[i].max = NULL;
|
||||
if (cursor->reverse) {
|
||||
range[i].max = range[i].min;
|
||||
range[i].min = NULL;
|
||||
}
|
||||
}
|
||||
status = findFirstNode(cursor, range);
|
||||
if (status != PR_SUCCESS) return NULL;
|
||||
if (cursor->cur) {
|
||||
PRBool match = PR_TRUE;
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
if (tdbCompareNodes(cursor->cur->data[i],
|
||||
cursor->triple.data[i]) != 0) {
|
||||
match = PR_FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
/* OK, this node we found was exactly the one we were at
|
||||
last time. Bump it up one. */
|
||||
moveCursorForward(cursor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
PR_FREEIF(cursor->triple.data[i]);
|
||||
}
|
||||
if (cursor->lasthit) {
|
||||
cursor->lasthit->refcnt--;
|
||||
cursor->lasthit = NULL;
|
||||
}
|
||||
if (cursor->cur == NULL) return NULL;
|
||||
while (cursor->cur != NULL) {
|
||||
if (tdbMatchesRange(cursor->cur, cursor->range)) {
|
||||
break;
|
||||
}
|
||||
cmp = tdbCompareToRange(cursor->cur, cursor->range, cursor->tree);
|
||||
if (cursor->reverse ? (cmp < 0) : (cmp > 0)) {
|
||||
/* We're off the end of the range, all done. */
|
||||
cursor->cur->refcnt--;
|
||||
PR_ASSERT(cursor->cur->refcnt >= 0);
|
||||
cursor->cur = NULL;
|
||||
break;
|
||||
}
|
||||
cursor->misses++;
|
||||
moveCursorForward(cursor);
|
||||
}
|
||||
if (cursor->cur == NULL) {
|
||||
tdbFlush(cursor->db);
|
||||
return NULL;
|
||||
}
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
cursor->triple.data[i] = tdbNodeDup(cursor->cur->data[i]);
|
||||
}
|
||||
cursor->lasthit = cursor->cur;
|
||||
cursor->lasthit->refcnt++;
|
||||
moveCursorForward(cursor);
|
||||
cursor->hits++;
|
||||
#ifdef DEBUG
|
||||
{
|
||||
TDBParentChain* tmp;
|
||||
PR_ASSERT(cursor->cur == NULL || cursor->cur->refcnt > 0);
|
||||
for (tmp = cursor->parent ; tmp ; tmp = tmp->next) {
|
||||
PR_ASSERT(tmp->record->refcnt > 0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
tdbFlush(cursor->db);
|
||||
return &(cursor->triple);
|
||||
}
|
||||
|
||||
|
||||
TDBTriple* TDBGetResult(TDBCursor* cursor)
|
||||
{
|
||||
TDBTriple* result;
|
||||
PR_ASSERT(cursor && cursor->db);
|
||||
if (!cursor || !cursor->db) return NULL;
|
||||
PR_Lock(cursor->db->mutex);
|
||||
result = tdbGetResultNolock(cursor);
|
||||
PR_Unlock(cursor->db->mutex);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Determines where this item falls within the range of items defined.
|
||||
Negative means this item is too early, and positive means too late.
|
||||
Zero means that it seems to fall within the range, but you need to do
|
||||
a call to tdbMatchesRange() to really make sure. The idea here is that
|
||||
as you slowly walk along the appropriate tree in the DB, this routine
|
||||
will always return a nondecreasing result. */
|
||||
|
||||
PRInt64 tdbCompareToRange(TDBRecord* record, TDBNodeRange* range,
|
||||
PRInt32 comparerule)
|
||||
{
|
||||
int i;
|
||||
int k;
|
||||
PR_ASSERT(record != NULL && range != NULL);
|
||||
if (record == NULL || range == NULL) return PR_FALSE;
|
||||
PR_ASSERT(comparerule >= 0 && comparerule < NUMTREES);
|
||||
if (comparerule < 0 || comparerule >= NUMTREES) {
|
||||
return 0;
|
||||
}
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
k = key[comparerule][i];
|
||||
if (range[k].min != NULL &&
|
||||
tdbCompareNodes(record->data[k], range[k].min) < 0) {
|
||||
return -1;
|
||||
}
|
||||
if (range[k].max != NULL &&
|
||||
tdbCompareNodes(record->data[k], range[k].max) > 0) {
|
||||
return 1;
|
||||
}
|
||||
if (range[k].min == NULL || range[k].max == NULL ||
|
||||
tdbCompareNodes(range[k].min, range[k].max) != 0) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
PRBool tdbMatchesRange(TDBRecord* record, TDBNodeRange* range)
|
||||
{
|
||||
PRInt32 i;
|
||||
PR_ASSERT(record != NULL && range != NULL);
|
||||
if (record == NULL || range == NULL) return PR_FALSE;
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
if (range[i].min != NULL &&
|
||||
tdbCompareNodes(record->data[i], range[i].min) < 0) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
if (range[i].max != NULL &&
|
||||
tdbCompareNodes(record->data[i], range[i].max) > 0) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
}
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
|
||||
void tdbThrowOutCursorCaches(TDB* db)
|
||||
{
|
||||
TDBCursor* cursor;
|
||||
for (cursor = db->firstcursor ; cursor ; cursor = cursor->nextcursor) {
|
||||
freeParentChain(cursor);
|
||||
if (cursor->cur) {
|
||||
cursor->cur->refcnt--;
|
||||
PR_ASSERT(cursor->cur->refcnt >= 0);
|
||||
cursor->cur = NULL;
|
||||
}
|
||||
if (cursor->lasthit) {
|
||||
cursor->lasthit->refcnt--;
|
||||
PR_ASSERT(cursor->lasthit->refcnt >= 0);
|
||||
cursor->lasthit = NULL;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,242 +0,0 @@
|
|||
/* -*- 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) 1999 Geocast Network Systems. All
|
||||
* Rights Reserved.
|
||||
*
|
||||
* Contributor(s): Terry Weissman <terry@mozilla.org>
|
||||
*/
|
||||
|
||||
/* Operations that do things on file record. */
|
||||
|
||||
#include "tdbtypes.h"
|
||||
|
||||
|
||||
|
||||
|
||||
TDBRecord* tdbLoadRecord(TDB* db, TDBPtr position)
|
||||
{
|
||||
TDBRecord* result;
|
||||
char buf[sizeof(PRUint32)];
|
||||
char* ptr;
|
||||
PRInt32 i;
|
||||
PRInt32 num;
|
||||
PRInt32 numtoread;
|
||||
|
||||
for (result = db->firstrecord ; result ; result = result->next) {
|
||||
if (result->position == position) {
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
|
||||
if (PR_Seek(db->fid, position, PR_SEEK_SET) < 0) {
|
||||
goto DONE;
|
||||
}
|
||||
num = PR_Read(db->fid, buf, sizeof(buf));
|
||||
if (num != sizeof(buf)) {
|
||||
goto DONE;
|
||||
}
|
||||
result = PR_NEWZAP(TDBRecord);
|
||||
if (result == NULL) goto DONE;
|
||||
result->position = position;
|
||||
ptr = buf;
|
||||
result->length = tdbGetInt32(&ptr);
|
||||
if (result->length < MINRECORDSIZE || result->length > MAXRECORDSIZE) {
|
||||
tdbMarkCorrupted(db);
|
||||
PR_Free(result);
|
||||
result = NULL;
|
||||
goto DONE;
|
||||
}
|
||||
if (tdbGrowIobuf(db, result->length) != PR_SUCCESS) {
|
||||
PR_Free(result);
|
||||
result = NULL;
|
||||
goto DONE;
|
||||
}
|
||||
numtoread = result->length - sizeof(PRInt32);
|
||||
num = PR_Read(db->fid, db->iobuf, numtoread);
|
||||
if (num < numtoread) {
|
||||
PR_Free(result);
|
||||
result = NULL;
|
||||
goto DONE;
|
||||
}
|
||||
ptr = db->iobuf;
|
||||
for (i=0 ; i<NUMTREES ; i++) {
|
||||
result->entry[i].child[0] = tdbGetInt32(&ptr);
|
||||
result->entry[i].child[1] = tdbGetInt32(&ptr);
|
||||
result->entry[i].balance = tdbGetInt8(&ptr);
|
||||
}
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
result->data[i] = tdbGetNode(db, &ptr);
|
||||
if (result->data[i] == NULL) {
|
||||
while (--i >= 0) {
|
||||
PR_Free(result->data[i]);
|
||||
}
|
||||
PR_Free(result);
|
||||
result = NULL;
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
|
||||
if (ptr - db->iobuf != numtoread) {
|
||||
tdbMarkCorrupted(db);
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
PR_Free(result->data[i]);
|
||||
}
|
||||
PR_Free(result);
|
||||
result = NULL;
|
||||
goto DONE;
|
||||
}
|
||||
|
||||
PR_ASSERT(db->firstrecord != result);
|
||||
result->next = db->firstrecord;
|
||||
db->firstrecord = result;
|
||||
|
||||
DONE:
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
PRStatus tdbSaveRecord(TDB* db, TDBRecord* record)
|
||||
{
|
||||
PRStatus status;
|
||||
PRInt32 i;
|
||||
char* ptr;
|
||||
if (PR_Seek(db->fid, record->position, PR_SEEK_SET) < 0) {
|
||||
return PR_FAILURE;
|
||||
}
|
||||
status = tdbGrowIobuf(db, record->length);
|
||||
if (status != PR_SUCCESS) return status;
|
||||
ptr = db->iobuf;
|
||||
tdbPutInt32(&ptr, record->length);
|
||||
for (i=0 ; i<NUMTREES ; i++) {
|
||||
tdbPutInt32(&ptr, record->entry[i].child[0]);
|
||||
tdbPutInt32(&ptr, record->entry[i].child[1]);
|
||||
tdbPutInt8(&ptr, record->entry[i].balance);
|
||||
}
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
tdbPutNode(db, &ptr, record->data[i]);
|
||||
}
|
||||
PR_ASSERT(ptr - db->iobuf == record->length);
|
||||
if (PR_Write(db->fid, db->iobuf, record->length) != record->length) {
|
||||
return PR_FAILURE;
|
||||
}
|
||||
return PR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
PRStatus tdbFreeRecord(TDBRecord* record)
|
||||
{
|
||||
PRInt32 i;
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
PR_Free(record->data[i]);
|
||||
}
|
||||
PR_Free(record);
|
||||
return PR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
TDBRecord* tdbAllocateRecord(TDB* db, TDBNodePtr triple[3])
|
||||
{
|
||||
PRInt32 i;
|
||||
PRInt32 size;
|
||||
TDBRecord* result = NULL;
|
||||
TDBRecord* tmp;
|
||||
TDBPtr position;
|
||||
|
||||
size = sizeof(PRInt32) +
|
||||
NUMTREES * (sizeof(PRInt32) + sizeof(PRInt32) + sizeof(PRInt8)) +
|
||||
tdbNodeSize(triple[0]) +
|
||||
tdbNodeSize(triple[1]) +
|
||||
tdbNodeSize(triple[2]);
|
||||
|
||||
position = db->freeroot;
|
||||
if (position > 0) {
|
||||
do {
|
||||
result = tdbLoadRecord(db, position);
|
||||
if (result->length > size) {
|
||||
position = result->entry[0].child[0];
|
||||
} else if (result->length < size) {
|
||||
position = result->entry[0].child[1];
|
||||
} else {
|
||||
/* Hey, we found one! */
|
||||
if (tdbRemoveFromTree(db, result, -1) != PR_SUCCESS) {
|
||||
tdbMarkCorrupted(db);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} while (position != 0);
|
||||
}
|
||||
if (position == 0) {
|
||||
result = PR_NEWZAP(TDBRecord);
|
||||
if (!result) return NULL;
|
||||
position = db->filelength;
|
||||
db->filelength += size;
|
||||
PR_ASSERT(db->firstrecord != result);
|
||||
result->next = db->firstrecord;
|
||||
db->firstrecord = result;
|
||||
} else {
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
PR_Free(result->data[i]);
|
||||
}
|
||||
tmp = result->next;
|
||||
memset(result, 0, sizeof(TDBRecord));
|
||||
result->next = tmp;
|
||||
}
|
||||
result->position = position;
|
||||
result->length = size;
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
result->data[i] = tdbNodeDup(triple[i]);
|
||||
if (result->data[i] == NULL) {
|
||||
while (--i >= 0) {
|
||||
PR_Free(result->data[i]);
|
||||
}
|
||||
PR_Free(result);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
result->dirty = PR_TRUE;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/* Argh, this same static array appears in query.c. Don't do that. ### */
|
||||
|
||||
static int key[4][3] = {
|
||||
{0, 1, 2},
|
||||
{1, 0, 2},
|
||||
{2, 1, 0},
|
||||
{1, 2, 0}
|
||||
};
|
||||
|
||||
PRInt64 tdbCompareRecords(TDBRecord* r1, TDBRecord* r2, PRInt32 comparerule)
|
||||
{
|
||||
int i, k;
|
||||
PRInt64 cmp;
|
||||
if (comparerule == -1) {
|
||||
cmp = r1->length - r2->length;
|
||||
if (cmp != 0) return cmp;
|
||||
return r1->position - r2->position;
|
||||
}
|
||||
PR_ASSERT(comparerule >= 0 && comparerule < NUMTREES);
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
k = key[comparerule][i];
|
||||
cmp = tdbCompareNodes(r1->data[k], r2->data[k]);
|
||||
if (cmp != 0) return cmp;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -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 <terry@mozilla.org>
|
||||
*/
|
||||
|
||||
#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 ; i<numfields ; i++) {
|
||||
result->fieldnum[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 ; i<rtype->numfields ; 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 ; i<rtype->numfields ; 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 ; i<rtype->numfields ; 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;
|
||||
}
|
|
@ -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 <terry@mozilla.org>
|
||||
*/
|
||||
|
||||
#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_ */
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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 <terry@mozilla.org>
|
||||
*/
|
||||
|
||||
#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_ */
|
|
@ -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 <terry@mozilla.org>
|
||||
|
@ -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 <stdio.h>
|
||||
|
||||
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_ */
|
||||
|
|
|
@ -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 <terry@mozilla.org>
|
||||
*/
|
||||
|
||||
#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_ */
|
|
@ -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 <terry@mozilla.org>
|
||||
|
@ -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);
|
||||
|
|
|
@ -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 <terry@mozilla.org>
|
||||
*/
|
||||
|
||||
#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_ */
|
|
@ -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 <terry@mozilla.org>
|
||||
*/
|
||||
|
||||
#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_ */
|
|
@ -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 <terry@mozilla.org>
|
||||
|
@ -26,6 +26,11 @@
|
|||
/* These are private, internal type definitions, */
|
||||
|
||||
#include <string.h>
|
||||
#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 <stdio.h>
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
|
||||
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_ */
|
||||
|
|
|
@ -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 <terry@mozilla.org>
|
||||
*/
|
||||
|
||||
#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 <sys/types.h>
|
||||
#include <netinet/in.h>
|
||||
#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;
|
||||
}
|
|
@ -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 <terry@mozilla.org>
|
||||
*/
|
||||
|
||||
#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_ */
|
|
@ -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 <terry@mozilla.org>
|
||||
*/
|
||||
|
||||
#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 ; i<numfields ; i++) {
|
||||
if (!nodes[i]) {
|
||||
tdb_ASSERT(0);
|
||||
tdbVectorFree(vector);
|
||||
return NULL;
|
||||
}
|
||||
vector->nodes[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<numfields ; i++) {
|
||||
nodes[i] = tdbGetNode(&ptr, endptr);
|
||||
if (nodes[i] == NULL) {
|
||||
tdbMarkCorrupted(base);
|
||||
for (i-- ; 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 ; i<numfields ; i++) TDBFreeNode(nodes[i]);
|
||||
if (vector == NULL) return NULL;
|
||||
vector->owner = 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 ; i<vector->numfields ; 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 ; i<vector->numfields ; 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 ; i<vector->numfields ; i++) {
|
||||
if (vector->nodes[i]) TDBFreeNode(vector->nodes[i]);
|
||||
}
|
||||
tdb_Free(vector->nodes);
|
||||
}
|
||||
if (vector->interned) {
|
||||
for (i=0 ; i<vector->numfields ; 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 ; i<v1->numfields ; 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;
|
||||
}
|
|
@ -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 <terry@mozilla.org>
|
||||
*/
|
||||
|
||||
#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_ */
|
|
@ -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 <terry@mozilla.org>
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#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<cur ; i++) {
|
||||
tmp[i] = winfo[i];
|
||||
}
|
||||
if (winfo != winfobuf) tdb_Free(winfo);
|
||||
winfo = tmp;
|
||||
}
|
||||
length = ptr2 - ptr1;
|
||||
if (length > 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 ; i<cursor->numwinfo ; 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 <terry@mozilla.org>
|
||||
*/
|
||||
|
||||
#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_ */
|
Загрузка…
Ссылка в новой задаче