diff options
Diffstat (limited to 'Src/nde/osx/Table.cpp')
-rw-r--r-- | Src/nde/osx/Table.cpp | 659 |
1 files changed, 659 insertions, 0 deletions
diff --git a/Src/nde/osx/Table.cpp b/Src/nde/osx/Table.cpp new file mode 100644 index 00000000..31087b1f --- /dev/null +++ b/Src/nde/osx/Table.cpp @@ -0,0 +1,659 @@ +/* --------------------------------------------------------------------------- +Nullsoft Database Engine +-------------------- +codename: Near Death Experience +--------------------------------------------------------------------------- */ + +/* --------------------------------------------------------------------------- + +Table Class +Apple Mac OS X implementation +--------------------------------------------------------------------------- */ +#include "../nde.h" +#include <stdio.h> +#include <string.h> +#include "../CRC.H" + +const char *tSign="NDETABLE"; + +//--------------------------------------------------------------------------- +Table::Table(const char *TableName, const char *Idx, BOOL Create, Database *_db, BOOL _Cached) +: columns_cached(false), use_row_cache(false), Scanner(this) +{ + Handle = 0; + memset(column_ids, FIELD_UNKNOWN, 255); + Cached = _Cached; + db = _db; + AutoCreate = Create; + Name = strdup(TableName); + IdxName = strdup(Idx); + Init(); +} + +//--------------------------------------------------------------------------- +void Table::Init() +{ + numErrors = 0; + Scanners = new LinkedList(); + // benski> cut: Handle=NULL; + IdxHandle=NULL; + FieldsRecord=NULL; + IndexList=NULL; + GLocateUpToDate = FALSE; +} + +//--------------------------------------------------------------------------- +Table::~Table() +{ + Reset(); + if (Handle) // Reset doesn't completely destroy Handle + Vfdestroy(Handle); + Handle = 0; + + free(Name); + free(IdxName); +} + +//--------------------------------------------------------------------------- +void Table::Reset() +{ + if (IndexList) IndexList->Release(); + IndexList=0; + if (FieldsRecord) FieldsRecord->Release(); + FieldsRecord=0; + delete Scanners; + Scanners=0; + if (Handle) + Vfclose(Handle); // close (but don't destroy) to keep mutex open. + if (IdxHandle) + Vfdestroy(IdxHandle); + IdxHandle = 0; + + for (RowCache::iterator itr=rowCache.begin();itr!=rowCache.end();itr++) + { + if (itr->second) + itr->second->Release(); + } + rowCache.clear(); + + memset(column_ids, FIELD_UNKNOWN, 255); + columns_cached=false; +} + + +struct IndexNewWalkerContext +{ + IndexNewWalkerContext(Table *_table) + { + N = -1; + table = _table; + } + int N; + Table *table; +}; + +bool Table::IndexNewWalker(IndexRecord *record, Field *entry, void *context_in) +{ + IndexNewWalkerContext *context = (IndexNewWalkerContext *)context_in; + IndexField *p = (IndexField *)entry; + p->index = new Index(context->table->IdxHandle, p->ID, context->N++, p->Type, FALSE, 0, context->table); + return true; +} + +//--------------------------------------------------------------------------- +BOOL Table::Open(void) +{ + BOOL Valid; + int justcreated = 0; + + if (!Handle) + Handle = Vfnew(Name, "r+b", Cached); + if (!Handle) return FALSE; + if (!Vflock(Handle)) + { + Vfdestroy(Handle); + Handle = 0; + return FALSE; + } + + Handle = Vfopen(Handle, Name, "r+b", Cached); + IdxHandle = Vfopen(0, IdxName, "r+b", TRUE); + Valid = (Handle && IdxHandle); + + // unlock + if (Valid || !AutoCreate) + { + //if (Handle) + //Vfunlock(Handle); + } + else + { + if (Handle) + { + Vfclose(Handle); + if (IdxHandle) + Vfdestroy(IdxHandle); + IdxHandle = 0; + } + else + { + if (IdxHandle) + Vfdestroy(IdxHandle); + IdxHandle = 0; + Handle = Vfnew(Name, "w+b", Cached); + if (!Vflock(Handle)) + { + Vfdestroy(Handle); + return FALSE; + } + } + + Handle = Vfopen(Handle, Name, "w+b", Cached); + IdxHandle = Vfopen(0, IdxName, "w+b", TRUE); + Valid = (Handle && IdxHandle); + + if (Valid) + { + Vfwrite(__TABLE_SIGNATURE__, strlen(__TABLE_SIGNATURE__), Handle); + Vfwrite(__INDEX_SIGNATURE__, strlen(__TABLE_SIGNATURE__), IdxHandle); + // TODO bensk> change if NUM_SPECIAL_RECORDS ever increases + int v=NUM_SPECIAL_RECORDS;//strlen(__TABLE_SIGNATURE__); + Vfwrite(&v, sizeof(v), IdxHandle); + // v = 0; fwrite(&v, sizeof(v), 1, IdxHandle); + v = -1; Vfwrite(&v, sizeof(v), IdxHandle); // write ID + v = 0; + for (int i=0;i<NUM_SPECIAL_RECORDS;i++) + { + Vfwrite(&v, sizeof(v), IdxHandle); + Vfwrite(&v, sizeof(v), IdxHandle); + } + Sync(); + justcreated = 1; + } + } + + + if (!Valid) + { + if (Handle) Vfdestroy(Handle); + if (IdxHandle) Vfdestroy(IdxHandle); + Handle = NULL; + IdxHandle = NULL; + } + else + { + int Ptr; + + char test1[9]={0,}; + char test2[9]={0,}; + + Vfseek(Handle, 0, SEEK_SET); + Vfread(test1, strlen(__TABLE_SIGNATURE__), Handle); + Vfseek(IdxHandle, 0, SEEK_SET); + Vfread(test2, strlen(__INDEX_SIGNATURE__), IdxHandle); + test1[8]=0; + test2[8]=0; + if (strcmp(test1, __TABLE_SIGNATURE__) || strcmp(test2, __INDEX_SIGNATURE__)) + { + if (Handle) Vfdestroy(Handle); + Handle = 0; + if (IdxHandle) Vfdestroy(IdxHandle); + IdxHandle = 0; + return FALSE; + } + + // Load default index + IndexField *field; + + field = new IndexField(PRIMARY_INDEX, -1, -1, CFSTR("None")); + field->index = new Index(IdxHandle, PRIMARY_INDEX, -1, -1, FALSE, 0, this); + + // Get indexes + Ptr = field->index->Get(INDEX_RECORD_NUM); + IndexList = new IndexRecord(Ptr, INDEX_RECORD_NUM, Handle, this); + if (!IndexList) + { + delete field; + if (Handle) Vfdestroy(Handle); + Handle = 0; + if (IdxHandle) Vfdestroy(IdxHandle); + IdxHandle = 0; + return FALSE; + } + + // Init them + IndexNewWalkerContext newContext(this); + IndexList->WalkFields(IndexNewWalker, &newContext); + + // Add default in case its not there (if it is it won't be added by addfield) + IndexList->AddField(field); + + // Get the default index (whether loaded or preloaded) + Scanner::index = ((IndexField*)IndexList->GetField(PRIMARY_INDEX))->index; + + // If it's different from preloaded, delete preloaded + if (field->index != Scanner::index) + { + delete field; + field=0; + } + + // Set up colaboration + IndexList->BuildCollaboration(); + + // Get columns + Ptr = Scanner::index->Get(FIELDS_RECORD_NUM); + FieldsRecord = new Record(Ptr, FIELDS_RECORD_NUM, Handle, this); + if (!FieldsRecord) + { + IndexList->Release(); + IndexList=0; + if (Handle) Vfdestroy(Handle); + Handle = 0; + if (IdxHandle) Vfdestroy(IdxHandle); + IdxHandle = 0; + return FALSE; + } + + // update the column cache + FieldsRecord->WalkFields(BuildColumnCache, this); + columns_cached=true; + } + +#if 0 // TODO + if (Valid && !justcreated) + { + if (IndexList->NeedFix()) + Compact(); + } +#endif + if (Valid) First(); + if (Handle) + Vfunlock(Handle); + return Valid; +} + +//--------------------------------------------------------------------------- +void Table::Close(void) +{ + int v=0; + + if (Handle && IndexList && Vflock(Handle, 0)) + { + IndexList->WalkFields(IndexWriteWalker, 0); + } + + delete Scanners; + Scanners = NULL; + + Vsync(Handle); + if (IdxHandle) + { + Vfdestroy(IdxHandle); + IdxHandle = NULL; + v |= 2; + } + if (Handle) + { + Vfdestroy(Handle); + Handle = NULL; + v |= 1; + } + + if (v != 3) + return; + +} + +bool Table::IndexWriteWalker(IndexRecord *record, Field *entry, void *context) +{ + IndexField *field = (IndexField *)entry; + field->index->WriteIndex(); + return true; +} + +//--------------------------------------------------------------------------- +void Table::Sync(void) +{ + if (!Vflock(Handle)) + return; + + if (IndexList) + IndexList->WalkFields(IndexWriteWalker, 0); + + int err=0; + if (!err && Handle) err|=Vsync(Handle); + if (!err && IdxHandle) err|=Vsync(IdxHandle); + + Vfunlock(Handle); +} + +//--------------------------------------------------------------------------- +ColumnField *Table::NewColumn(unsigned char FieldID, CFStringRef FieldName, unsigned char FieldType, BOOL indexUnique) +{ + columns_cached=false; // if they start writing new columns, kill the columns cache until they PostColumns() + ColumnField *f = GetColumnById(FieldID); + if (f) { + int t = f->GetDataType(); + if (t != FieldType) { + if (CompatibleFields(t, FieldType)) + { + f->SetDataType(FieldType); + goto aok; + } + } + return NULL; + } +aok: + if (GetColumnByName(FieldName)) + return NULL; + ColumnField *field = new ColumnField(FieldID, FieldName, FieldType, this); + column_ids[FieldID]=FieldType; + FieldsRecord->AddField(field); + return field; +} + +void Table::SetFieldSearchableById(unsigned char field_id, bool searchable) +{ + ColumnField *column = GetColumnById(field_id); + if (column) + column->SetSearchable(searchable); + if (searchable) + { + search_fields.insert(field_id); + } + else + { + search_fields.erase(field_id); + } +} + +//--------------------------------------------------------------------------- +bool Table::BuildColumnCache(Record *record, Field *entry, void *context) +{ + Table *table = (Table *)context; + ColumnField *column = (ColumnField *)entry; + unsigned char field_id=column->GetFieldId(); + table->column_ids[field_id] = column->GetDataType(); + + if (column->IsSearchableField()) + { + table->search_fields.insert(field_id); + } + else + { + table->search_fields.erase(field_id); + } + return true; +} + +//--------------------------------------------------------------------------- +void Table::PostColumns(void) +{ + FieldsRecord->WriteFields(this, FIELDS_RECORD_NUM); + memset(column_ids, FIELD_UNKNOWN, 255); + FieldsRecord->WalkFields(BuildColumnCache, this); + columns_cached=true; +} + +unsigned char Table::GetColumnType(unsigned char Id) +{ + if (columns_cached) + { + return column_ids[Id]; + } + else + { + return GetColumnById(Id)->GetDataType(); + } +} + +//--------------------------------------------------------------------------- +IndexField *Table::GetIndexByName(CFStringRef name) +{ + if (!IndexList) + return NULL; + return IndexList->GetIndexByName(name); +} + +//--------------------------------------------------------------------------- +IndexField *Table::GetIndexById(unsigned char Id) +{ + if (!IndexList) + return NULL; + return (IndexField *)IndexList->GetField(Id); +} + +//--------------------------------------------------------------------------- +void Table::AddIndexByName(CFStringRef name, CFStringRef desc) +{ + ColumnField *header = GetColumnByName(name); + if (header) + { + unsigned char Idx = header->ID; + AddIndexById(Idx, desc); + } +} + +//--------------------------------------------------------------------------- +void Table::AddIndexById(unsigned char Id, CFStringRef desc) +{ + if (GetIndexById(Id)) return; + ColumnField *col = GetColumnById(Id); + if (!col) + return; + IndexField *newindex = new IndexField(Id, IndexList->GetColumnCount(), col->GetDataType(), desc); + newindex->index = new Index(IdxHandle, Id, IndexList->GetColumnCount(), col->GetDataType(), TRUE, Scanner::index->NEntries, this); + IndexList->AddField(newindex); + + IndexField *previous = (IndexField *)newindex->prev; + previous->index->Colaborate(newindex); + IndexField *primary_index = (IndexField *)IndexList->GetField(PRIMARY_INDEX); + newindex->index->Colaborate(primary_index); + + previous->index->Propagate(); + + IndexList->WriteFields(this); +} + +//--------------------------------------------------------------------------- +BOOL Table::CheckIndexing(void) +{ + if (IndexList->GetColumnCount() == 0) return TRUE; + + for (int i=0;i<Scanner::index->NEntries;i++) + { + if (!IndexList->CheckIndexing(i)) + return FALSE; + } + return TRUE; +} + +struct IndexWalkerThunkContext +{ + void *context; + Table *_this; + Table::IndexWalker callback; +}; + +bool Table::IndexWalkerThunk(IndexRecord *record, Field *entry, void *context_in) +{ + IndexWalkerThunkContext *context = (IndexWalkerThunkContext *)context_in; + return context->callback(context->_this, (IndexField *)entry, context->context); +} + +//--------------------------------------------------------------------------- +void Table::WalkIndices(IndexWalker callback, void *context) +{ + if (IndexList && callback) + { + IndexWalkerThunkContext walkerContext = { context, this, callback }; + IndexList->WalkFields(IndexWalkerThunk, &walkerContext); + } +} + +//--------------------------------------------------------------------------- +void Table::DropIndex(IndexField *Ptr) +{ + if (!Ptr || Ptr->Type != FIELD_INDEX) return; + if (Scanner::index == Ptr->index) + { + Scanner::index = ((IndexField*)IndexList->GetField(PRIMARY_INDEX))->index; + + IndexList->BuildCollaboration(); + } + IndexList->RemoveField(Ptr); + if (Scanner::index->SecIndex == Ptr) + Scanner::index->SecIndex = 0; + IndexList->SetModified(TRUE); + IndexList->WriteFields(this); +} + +//--------------------------------------------------------------------------- +void Table::DropIndexByName(CFStringRef desc) +{ + IndexField *indx = GetIndexByName(desc); + if (CFStringCompare(desc, CFSTR("None"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) + return; + + if (indx) + DropIndex(indx); +} + +//--------------------------------------------------------------------------- +void Table::DropIndexById(unsigned char Id) +{ + if (!IndexList) + return; + if (Id == (unsigned char)PRIMARY_INDEX) return; + IndexField *indx=(IndexField *)IndexList->GetField(Id); + if (indx) + DropIndex(indx); +} + + + +//--------------------------------------------------------------------------- +BOOL Table::LocateByIdEx(int Id, int From, Field *field, int comp_mode) +{ + return Scanner::LocateByIdEx(Id, From, field, NULL, comp_mode); +} + +//--------------------------------------------------------------------------- +Record *Table::GetColumns(void) +{ + if (!FieldsRecord) + return NULL; + return FieldsRecord; +} + +//--------------------------------------------------------------------------- +Scanner *Table::NewScanner() +{ + Scanner *s = new Scanner(this); + /*if (Scanners->GetNElements() > 0)*/ + s->index = Scanner::index; + Scanners->AddEntry(s, TRUE); + return s; +} + +//--------------------------------------------------------------------------- +Scanner *Table::GetDefaultScanner(void) +{ + return this; +} + +//--------------------------------------------------------------------------- +void Table::DeleteScanner(Scanner *scan) +{ + if (!scan) return; + Scanners->RemoveEntry(scan); +} + +//--------------------------------------------------------------------------- +void Table::IndexModified(void) +{ + Scanner *s = (Scanner *)Scanners->GetHead(); + while (s) + { + s->IndexModified(); + s = (Scanner *)s->GetNext(); + } +} + +//--------------------------------------------------------------------------- +void Table::SetGlobalLocateUpToDate(BOOL is) { + GLocateUpToDate = is; +} + +ColumnField *Table::GetColumnById(unsigned char Idx) +{ + if (!FieldsRecord) + return NULL; + return (ColumnField *)FieldsRecord->GetField(Idx); +} + +ColumnField *Table::GetColumnByName(CFStringRef FieldName) +{ + return FieldsRecord->GetColumnByName(FieldName); +} + +void Table::RowCache_Delete(int position) +{ + if (use_row_cache) + { + RowCache::iterator found = rowCache.find(position); + if (found != rowCache.end()) + { + if (found->second) + found->second->Release(); + rowCache.erase(found); + } + } +} + +void Table::RowCache_Remove(int position) +{ + if (use_row_cache) + { + + Record *&row = rowCache[position]; + if (row) + { + row->Release(); + } + + row = 0; + } +} + +void Table::RowCache_Add(Record *record, int position) +{ + if (use_row_cache) + { + record->Retain(); + + Record *&row = rowCache[position]; + if (row) + { + row->Release(); + } + + row = record; + } +} + +Record *Table::RowCache_Get(int position) +{ + if (!use_row_cache) + return 0; + Record *row = rowCache[position]; + if (row) + row->Retain(); + return row; +} + +void Table::EnableRowCache() +{ + use_row_cache=true; +} |