diff options
author | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
---|---|---|
committer | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
commit | 20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch) | |
tree | 12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/nde/android/Table.cpp | |
parent | 537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff) | |
download | winamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz |
Initial community commit
Diffstat (limited to 'Src/nde/android/Table.cpp')
-rw-r--r-- | Src/nde/android/Table.cpp | 846 |
1 files changed, 846 insertions, 0 deletions
diff --git a/Src/nde/android/Table.cpp b/Src/nde/android/Table.cpp new file mode 100644 index 00000000..315d62cf --- /dev/null +++ b/Src/nde/android/Table.cpp @@ -0,0 +1,846 @@ +/* --------------------------------------------------------------------------- +Nullsoft Database Engine +-------------------- +codename: Near Death Experience +--------------------------------------------------------------------------- */ + +/* --------------------------------------------------------------------------- + +Table Class + +Android (linux) implementation +--------------------------------------------------------------------------- */ +#include "Table.h" +#include "../nde.h" +#include <stdio.h> +#include <string.h> +#include "../CRC.H" +#include "../NDEString.h" +#include "IndexField.h" +#include "ColumnField.h" +#include "../DBUtils.h" +const char *tSign="NDETABLE"; + +//--------------------------------------------------------------------------- +Table::Table(const char *TableName, const char *Idx, int Create, Database *_db, int _Cached) +: Scanner(this), use_row_cache(false), columns_cached(false) +{ + Handle = 0; + memset(column_ids, FIELD_UNKNOWN, 255); + Cached = _Cached; + db = _db; + AutoCreate = Create; + Name = ndestring_wcsdup(TableName); + IdxName = ndestring_wcsdup(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; + + ndestring_release(Name); + ndestring_release(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() +{ + 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", 1); + 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", 1); + 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, "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, const char *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(const char *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(const char *name, const char *desc) +{ + ColumnField *header = GetColumnByName(name); + if (header) + { + unsigned char Idx = header->ID; + AddIndexById(Idx, desc); + } +} + +//--------------------------------------------------------------------------- +void Table::AddIndexById(unsigned char Id, const char *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->WriteFields(this); +} + +//--------------------------------------------------------------------------- +void Table::DropIndexByName(const char *desc) +{ + IndexField *indx = GetIndexByName(desc); + if (!strcasecmp(desc, "None")) 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() +{ + 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; +} + +struct ColumnWalkContext +{ + Table *ctable; +}; + +bool Table::Compact_ColumnWalk(Record *record, Field *entry, void *context_in) +{ + ColumnField *field = static_cast<ColumnField *>(entry); + ColumnWalkContext *context = (ColumnWalkContext *)context_in; + Table *ctable = context->ctable; + + ctable->NewColumn(field->GetFieldId(), field->GetFieldName(), field->GetDataType(), FALSE); + return true; +} + +struct ColumnWalk2Context +{ + Table *ctable; + Table *thisTable; + uint8_t *data; + size_t data_size; + int gotstuff; +}; + +bool Table::Compact_ColumnWalk2(Record *record, Field *entry, void *context_in) +{ + ColumnField *colfield = (ColumnField *)entry; + ColumnWalk2Context *context = (ColumnWalk2Context *)context_in; + + unsigned char fieldid = colfield->GetFieldId(); + //char *fieldname = colfield->GetFieldName(); + Field *mfield = context->thisTable->GetFieldById(fieldid); + //Field *mfield = GetFieldByName(fieldname); + if (mfield != NULL) { + if (!context->gotstuff) { + context->ctable->New(); + context->gotstuff = 1; + } + Field *cfield = context->ctable->NewFieldById(fieldid, 0); + //Field *cfield = ctable->NewFieldByName(fieldname, mfield->GetPerm()); + size_t len = mfield->GetDataSize(); + if (len > context->data_size) + { + context->data_size = len; + context->data = (uint8_t *)realloc(context->data, context->data_size); + } + mfield->WriteTypedData(context->data, len); + cfield->ReadTypedData(context->data, len); + } + + return true; +} + +bool Table::Compact_IndexWalk(Table *table, IndexField *field, void *context) +{ + Table *ctable = (Table *)context; + + if (strcasecmp(field->GetIndexName(), "None")) + ctable->AddIndexById(field->GetFieldId(), field->GetIndexName()); + return true; +} +#if 0 // TODO +//--------------------------------------------------------------------------- +void Table::Compact(int *progress) { + // ok so we're gonna be cheating a bit, instead of figuring out how to safely modify all those + // nifty indexes that cross reference themselves and blablabla, we're just gonna duplicate the + // whole table from scratch, overwrite ourselves, and reopen the table. duh. + + if (!Vflock(Handle)) + { + if (progress != NULL) *progress = 100; + return; + } + // create a temporary table in windows temp dir + wchar_t temp_table[MAX_PATH+12]; + wchar_t temp_index[MAX_PATH+12]; + wchar_t old_table[MAX_PATH+12]; + wchar_t old_index[MAX_PATH+12]; + DWORD pid=GetCurrentProcessId(); + + StringCbPrintfW(temp_table, sizeof(temp_table), L"%s.new%08X", Name,pid); + StringCbPrintfW(temp_index, sizeof(temp_index),L"%s.new%08X", IdxName,pid); + StringCbPrintfW(old_table, sizeof(old_table),L"%s.old%08X", Name,pid); + StringCbPrintfW(old_index, sizeof(old_index),L"%s.old%08X", IdxName,pid); + + // delete them, in case we crashed while packing + + DeleteFileW(temp_table); + DeleteFileW(temp_index); + DeleteFileW(old_table); + DeleteFileW(old_index); + + // create a brand new db and a brand new table + Table *ctable = db->OpenTable(temp_table, temp_index, NDE_OPEN_ALWAYS, Cached); + + // duplicate the columns + Record *record = GetColumns(); + if (record != NULL) + { + ColumnWalkContext context; + context.ctable = ctable; + record->WalkFields(Compact_ColumnWalk, &context); + } + ctable->PostColumns(); + + // duplicate the indexes + WalkIndices(Compact_IndexWalk, (void *)ctable); + + // duplicate the data + int reccount = GetRecordsCount(); + + int count = 0; + First(); + ColumnWalk2Context context; + context.data_size = 65536; + context.data = (uint8_t *)malloc(65536); + context.ctable = ctable; + context.thisTable = this; + + while (1) { + int lasterr = NumErrors(); + GetDefaultScanner()->GetRecordById(count, FALSE); + count++; + + if (Eof() || count > reccount) break; + + if (NumErrors() > lasterr) + continue; + + Index *idx = GetDefaultScanner()->GetIndex(); + int pos = idx->Get(GetDefaultScanner()->GetRecordId()); + + if (pos == 0) continue; + + int pr = (int)((float)GetRecordId()/(float)reccount*100.0f); + if (progress != NULL) *progress = pr; + context.gotstuff = 0; + + if (record != NULL) + record->WalkFields(Compact_ColumnWalk2, &context); + + if (context.gotstuff) + ctable->Post(); + } + free(context.data); + + // done creating temp table + db->CloseTable(ctable); + + // reset the data structures and close underlying file handles + Reset(); + + if (MoveFileW(Name,old_table)) + { + if (MoveFileW(IdxName,old_index)) + { + if (!MoveFileW(temp_table,Name) || !MoveFileW(temp_index,IdxName)) + { + // failed, try to copy back + DeleteFileW(Name); + DeleteFileW(IdxName); + MoveFileW(old_table,Name); // restore old file + MoveFileW(old_index,IdxName); // restore old file + } + } + else + { + MoveFileW(old_table,Name); // restore old file + } + } + + // clean up our temp files + DeleteFileW(temp_table); + DeleteFileW(temp_index); + DeleteFileW(old_table); + DeleteFileW(old_index); + + if (progress != NULL) *progress = 100; + + // reopen our table + Init(); + Open(); +} +#endif +ColumnField *Table::GetColumnById(unsigned char Idx) +{ + if (!FieldsRecord) + return NULL; + return (ColumnField *)FieldsRecord->GetField(Idx); +} + +ColumnField *Table::GetColumnByName(const char *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; +} |