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/Scanner.cpp | |
parent | 537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff) | |
download | winamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz |
Initial community commit
Diffstat (limited to 'Src/nde/android/Scanner.cpp')
-rw-r--r-- | Src/nde/android/Scanner.cpp | 1196 |
1 files changed, 1196 insertions, 0 deletions
diff --git a/Src/nde/android/Scanner.cpp b/Src/nde/android/Scanner.cpp new file mode 100644 index 00000000..001cdc8f --- /dev/null +++ b/Src/nde/android/Scanner.cpp @@ -0,0 +1,1196 @@ +/* --------------------------------------------------------------------------- +Nullsoft Database Engine +-------------------- +codename: Near Death Experience +--------------------------------------------------------------------------- */ + +/* --------------------------------------------------------------------------- + +Scanner Class + +--------------------------------------------------------------------------- */ + +#include "../nde.h" +#include "BinaryField.h" +#include "Binary32Field.h" +#include <assert.h> +//--------------------------------------------------------------------------- +Scanner::Scanner(Table *parentTable) +{ + disable_date_resolution=0; + index = NULL; + pTable = parentTable; + Edition=false; + + lastLocateIndex = NULL; + lastLocateId = -1; + lastLocateIdx = -1; + lastLocateFrom = -128; + lastLocateFieldClone = NULL; + CurrentRecord=NULL; + CurrentRecordIdx=0; + iModified = false; + FiltersOK = false; + + search_any = false; + ResultPtr = 0; + + /*inMatchJoins = 0; + lastJoinCache = 0; + invalidJoinCache = 1;*/ + last_query = NULL; + last_query_failed = false; + token = NULL; + in_query_parser = 0; +} + +//--------------------------------------------------------------------------- +Scanner::~Scanner() +{ + if (CurrentRecord) CurrentRecord->Release(); + + if (lastLocateFieldClone) + { + delete lastLocateFieldClone; + } + + Query_CleanUp(); + + if (token) ndestring_release(token); + ndestring_release(last_query); +} + +//--------------------------------------------------------------------------- +Record *Scanner::GetRecord(int Idx) +{ + int Ptr; + Ptr = index->Get(Idx); + + Record *r = pTable->RowCache_Get(Ptr); + if (r) + { + return r; + } + + r = new Record(Ptr, Idx, pTable->Handle, pTable); + pTable->RowCache_Add(r, Ptr); + return r; +} + +//--------------------------------------------------------------------------- +void Scanner::GetCurrentRecord(void) +{ + /* + if (Eof() || Bof()) + { + if (CurrentRecord) CurrentRecord->Release(); + CurrentRecord = NULL; + return; + } + + Record *old_record = CurrentRecord; + CurrentRecord = NULL; + + Record *new_record = GetRecord(CurrentRecordIdx); + if (old_record) old_record->Release(); + CurrentRecord = new_record; + */ + if (CurrentRecord) CurrentRecord->Release(); + CurrentRecord = NULL; + + //invalidJoinCache = 1; + if (Eof() || Bof()) return; + CurrentRecord = GetRecord(CurrentRecordIdx); + +} + +//--------------------------------------------------------------------------- +void Scanner::GetRecordById(int Id, bool checkFilters) +{ + CurrentRecordIdx=max(min(index->NEntries, Id+NUM_SPECIAL_RECORDS), 0); + GetCurrentRecord(); + if (!checkFilters || MatchFilters()) + return; + Next(); +} + +//--------------------------------------------------------------------------- +void Scanner::First(int *killswitch) +{ + if (last_query_failed) return; + GetRecordById(0); + if (!MatchFilters() && !Eof()) + Next(killswitch); +} + +//--------------------------------------------------------------------------- +int Scanner::Next(int *killswitch) +{ + if (last_query_failed) return 0; + + while (!Eof() && !Bof()) + { + CurrentRecordIdx++; + GetCurrentRecord(); + if (MatchFilters()) + break; + else + { + if ((killswitch && *killswitch)) + return 0; + } + } + return 1; +} + +//--------------------------------------------------------------------------- +int Scanner::Previous(int *killswitch) +{ + if (last_query_failed) return 0; + + while (CurrentRecordIdx >= NUM_SPECIAL_RECORDS) + { + CurrentRecordIdx--; + GetCurrentRecord(); + if (MatchFilters()) + break; + else + { + if ((killswitch && *killswitch)) + return 0; + } + } + return 1; +} + +//--------------------------------------------------------------------------- +void Scanner::Last(int *killswitch) +{ + if (last_query_failed) return; + GetRecordById(index->NEntries-NUM_SPECIAL_RECORDS-1); // -3 here because 1)GetRecordById is public, so -NUM_SPECIAL_RECORDS, and 2)last entry is nentries-1, so -1 + if (!MatchFilters() && !Bof()) + Previous(killswitch); + if (CurrentRecordIdx < NUM_SPECIAL_RECORDS) + { + CurrentRecordIdx = index->NEntries; + GetCurrentRecord(); // will only delete current record if it exists + } +} + +//--------------------------------------------------------------------------- +bool Scanner::Eof(void) +{ + if (last_query_failed) return true; + return CurrentRecordIdx >= index->NEntries; +} + +//--------------------------------------------------------------------------- +bool Scanner::Bof(void) +{ + if (last_query_failed) return true; + return CurrentRecordIdx < NUM_SPECIAL_RECORDS; +} + +//--------------------------------------------------------------------------- +Field *Scanner::GetFieldByName(const char *FieldName) +{ + ColumnField *header = pTable->GetColumnByName(FieldName); + if (header) + { + unsigned char Idx = header->ID; + return GetFieldById(Idx); + } + return NULL; +} + +//--------------------------------------------------------------------------- +Field *Scanner::GetFieldById(unsigned char Id) +{ + if (!CurrentRecord) + return NULL; + Field *field = CurrentRecord->GetField(Id); + if (field) + { + int column_type = pTable->GetColumnType(Id); + if (!CompatibleFields(field->GetType(), column_type)) + { + return NULL; + } + } + return field; +} + +//--------------------------------------------------------------------------- +Field *Scanner::NewFieldByName(const char *fieldName, unsigned char Perm) +{ + ColumnField *header = pTable->GetColumnByName(fieldName); + if (header) + { + unsigned char Id = header->ID; + Field *field = NewFieldById(Id, Perm); + return field; + } + return NULL; +} + +//--------------------------------------------------------------------------- +Field *Scanner::NewFieldById(unsigned char Id, unsigned char Perm) +{ + ColumnField *field = GetColumnById(Id); + if (!field) + return NULL; + Field *O=GetFieldById(Id); + if (O) return O; + switch (field->GetDataType()) + { + case FIELD_STRING: + O = new StringField(); + break; + case FIELD_INTEGER: + O = new IntegerField(); + break; + case FIELD_INT64: + O = new Int64Field(); + break; + case FIELD_INT128: + O = new Int128Field(); + break; + case FIELD_DATETIME: + if (disable_date_resolution) + O= new StringField(); + else + O = new DateTimeField(); + break; + case FIELD_LENGTH: + O = new LengthField(); + break; + case FIELD_FILENAME: + O = new FilenameField(); + break; + case FIELD_BINARY: + O = new BinaryField(); + break; + case FIELD_BINARY32: + O = new Binary32Field(); + break; + default: + //MessageBox(NULL, "unknown field type for id", "debug", 0); + O = new Field(); + break; + } + O->Type = field->GetDataType(); + O->ID = Id; + CurrentRecord->AddField(O); + return O; +} + +//--------------------------------------------------------------------------- +void Scanner::Post(void) +{ + if (!CurrentRecord) return; + /*if (CurrentRecord->RecordIndex == NEW_RECORD) + NEntries++;*/ + if (pTable->use_row_cache && CurrentRecordIdx != NEW_RECORD) + { + int Ptr; + Ptr = index->Get(CurrentRecordIdx); + pTable->RowCache_Remove(Ptr); + } + CurrentRecordIdx = CurrentRecord->WriteFields(pTable, CurrentRecordIdx); + Edition=false; + if (pTable->use_row_cache) + { + int Ptr; + Ptr = index->Get(CurrentRecordIdx); + pTable->RowCache_Add(CurrentRecord, Ptr); + } +} + +//--------------------------------------------------------------------------- +void Scanner::New(void) +{ + if (CurrentRecord) CurrentRecord->Release(); + CurrentRecord = NULL; + + CurrentRecord = new Record(0, index->NEntries, pTable->Handle, pTable); + CurrentRecordIdx = NEW_RECORD; + Edition = true; +} + +//--------------------------------------------------------------------------- +void Scanner::Insert(void) +{ + if (CurrentRecord) CurrentRecord->Release(); + CurrentRecord = NULL; + + CurrentRecord = new Record(0, CurrentRecordIdx, pTable->Handle, pTable); + CurrentRecordIdx = NEW_RECORD; + Edition=true; +} + +//--------------------------------------------------------------------------- +void Scanner::Delete(void) +{ + if (CurrentRecord) + { + if (pTable->use_row_cache && CurrentRecordIdx != NEW_RECORD) + { + int Ptr; + Ptr = index->Get(CurrentRecordIdx); + pTable->RowCache_Delete(Ptr); + } + CurrentRecord->Delete(pTable, CurrentRecordIdx); + } + if (Eof()) + Previous(); + GetRecordById(CurrentRecordIdx-NUM_SPECIAL_RECORDS); +} + +//--------------------------------------------------------------------------- +int Scanner::GetRecordId(void) +{ + return CurrentRecordIdx != NEW_RECORD ? CurrentRecordIdx-NUM_SPECIAL_RECORDS : CurrentRecordIdx; +} + +//--------------------------------------------------------------------------- +void Scanner::Edit(void) +{ + if (Edition) return; + if (!CurrentRecord) + return; + /*Field *f = (Field *)CurrentRecord->Fields->GetHead(); + while (f) + { + f->SubtableRecord = INVALID_RECORD; + f = (Field *)f->GetNext(); + }*/ + + if (CurrentRecord->InCache()) // if it's in the cache + { + // benski> make copy of CurrentRecord, outside the cache + int Ptr; + Ptr = index->Get(CurrentRecordIdx); + pTable->RowCache_Remove(Ptr); + Record *r = new Record(Ptr, CurrentRecordIdx, pTable->Handle, pTable); + CurrentRecord->Release(); + CurrentRecord = r; + } + + Edition = true; +} + + +//--------------------------------------------------------------------------- +void Scanner::Cancel(void) +{ + Edition = false; + GetCurrentRecord(); +} + +//--------------------------------------------------------------------------- +bool Scanner::LocateByName(const char *col, int From, Field *field, int *nskip) +{ + ColumnField *f = pTable->GetColumnByName(col); + if (!f) + return NULL; + return LocateById(f->GetFieldId(), From, field, nskip); +} + +//--------------------------------------------------------------------------- +void Scanner::CacheLastLocate(int Id, int From, Field *field, Index *i, int j) +{ + lastLocateId = Id; + lastLocateFrom = From; + if (lastLocateFieldClone) + { + delete lastLocateFieldClone; + lastLocateFieldClone = NULL; + } + lastLocateFieldClone = field->Clone(pTable); + lastLocateIndex = i; + i->locateUpToDate = true; + pTable->SetGlobalLocateUpToDate(true); + lastLocateIdx = j; +} + + +//--------------------------------------------------------------------------- +bool Scanner::LocateById(int Id, int From, Field *field, int *nskip) +{ + return LocateByIdEx(Id, From, field, nskip, COMPARE_MODE_EXACT); +} +//--------------------------------------------------------------------------- +bool Scanner::LocateByIdEx(int Id, int From, Field *field, int *nskip, int comp_mode) +{ + IndexField *i = pTable->GetIndexById(Id); + Field *compField; + int j; + int n; + Field *cfV; + + if (index->NEntries == NUM_SPECIAL_RECORDS) + return false; + + int success; + + if (nskip) *nskip=0; + // I know this is stupid but.... May be do something later + switch (comp_mode) + { + case COMPARE_MODE_CONTAINS: + while (1) + { + success = false; + if (!i) + { + // No index for this column. Using slow locate, enumerates the database, still faster than what the user + // can do since we have access to QuickFindField which only read field headers + // in order to locate the field we have to compare. user could only read the entire record. + if (From == FIRST_RECORD) + From = NUM_SPECIAL_RECORDS; + else + From+=(NUM_SPECIAL_RECORDS+1); + if (From == lastLocateFrom && Id == lastLocateId && field->Contains(lastLocateFieldClone)==0 && index == lastLocateIndex && (index->locateUpToDate || pTable->GLocateUpToDate)) + { + if (CurrentRecordIdx != lastLocateIdx) GetRecordById(lastLocateIdx-NUM_SPECIAL_RECORDS, false); + success = true; + goto nextiter_1; + } + for (j=From;j<index->NEntries;j++) + { + compField = index->QuickFindField(Id, index->Get(j)); + cfV = /*(compField && compField->Type == FIELD_PRIVATE) ? ((PrivateField *)compField)->myField :*/ compField; + if (!field->Contains(cfV)) + { + if (compField) + { + delete compField; + } + if (CurrentRecordIdx != j) GetRecordById(j-NUM_SPECIAL_RECORDS, false); + CacheLastLocate(Id, From, field, index, j); + success = true; + goto nextiter_1; + } + delete compField; + } + success = false; + goto nextiter_1; + } + else + { + // Index available. Using fast locate. nfetched=log2(nrecords) for first locate, 1 more fetch per locate on same criteria + int p; + if (From == FIRST_RECORD) From = NUM_SPECIAL_RECORDS; + else From = index->TranslateIndex(From+NUM_SPECIAL_RECORDS, i->index)+1; + if (From == lastLocateFrom && Id == lastLocateId && field->Contains(lastLocateFieldClone)==0 && index == lastLocateIndex && (i->index->locateUpToDate || pTable->GLocateUpToDate)) + { + if (CurrentRecordIdx != lastLocateIdx) GetRecordById(lastLocateIdx-NUM_SPECIAL_RECORDS, false); + success = true; + goto nextiter_1; + } + if (From >= index->NEntries) + { + return false; + } + compField = i->index->QuickFindField(Id, i->index->Get(From)); + cfV = /*(compField && compField->Type == FIELD_PRIVATE) ? ((PrivateField *)compField)->myField :*/ compField; + if (field->Contains(cfV) == 0) + { + delete compField; + n = i->index->TranslateIndex(From, index); + if (CurrentRecordIdx != n) GetRecordById(n-NUM_SPECIAL_RECORDS, false); + CacheLastLocate(Id, From, field, i->index, n); + success = true; + goto nextiter_1; + } + delete compField; + p = i->index->QuickFindEx(Id, field, From, comp_mode); + if (p != FIELD_NOT_FOUND) + { + n = (index->GetId() == Id) ? p : i->index->TranslateIndex(p, index); + if (CurrentRecordIdx != n) GetRecordById(n-NUM_SPECIAL_RECORDS, false); + CacheLastLocate(Id, From, field, i->index, n); + success = true; + goto nextiter_1; + } + } +nextiter_1: // eek + if (success) + { + if (!MatchFilters() && !Eof()) + { + From = GetRecordId(); + if (nskip) (*nskip)++; + } + else break; + } + else break; + } + + break; + case COMPARE_MODE_EXACT: + while (1) + { + success = false; + if (!i) + { + // No index for this column. Using slow locate, enumerates the database, still faster than what the user + // can do since we have access to QuickFindField which only read field headers + // in order to locate the field we have to compare. user could only read the entire record. + if (From == FIRST_RECORD) + From = NUM_SPECIAL_RECORDS; + else + From+=(NUM_SPECIAL_RECORDS+1); + if (From == lastLocateFrom && Id == lastLocateId && field->Compare(lastLocateFieldClone)==0 && index == lastLocateIndex && (index->locateUpToDate || pTable->GLocateUpToDate)) + { + if (CurrentRecordIdx != lastLocateIdx) GetRecordById(lastLocateIdx-NUM_SPECIAL_RECORDS, false); + success = true; + goto nextiter_2; + } + for (j=From;j<index->NEntries;j++) + { + compField = index->QuickFindField(Id, index->Get(j)); + cfV = /*(compField && compField->Type == FIELD_PRIVATE) ? ((PrivateField *)compField)->myField :*/ compField; + if (!field->Compare(cfV)) + { + delete compField; + if (CurrentRecordIdx != j) GetRecordById(j-NUM_SPECIAL_RECORDS, false); + CacheLastLocate(Id, From, field, index, j); + success = true; + goto nextiter_2; + } + delete compField; + } + success = false; + goto nextiter_2; + } + else + { + // Index available. Using fast locate. nfetched=log2(nrecords) for first locate, 1 more fetch per locate on same criteria + int p; + if (From == FIRST_RECORD) From = NUM_SPECIAL_RECORDS; + else From = index->TranslateIndex(From+NUM_SPECIAL_RECORDS, i->index)+1; + if (From == lastLocateFrom && Id == lastLocateId && field->Compare(lastLocateFieldClone)==0 && index == lastLocateIndex && (i->index->locateUpToDate || pTable->GLocateUpToDate)) + { + if (CurrentRecordIdx != lastLocateIdx) GetRecordById(lastLocateIdx-NUM_SPECIAL_RECORDS, false); + success = true; + goto nextiter_2; + } + if (From >= index->NEntries) + { + return false; + } + compField = i->index->QuickFindField(Id, i->index->Get(From)); + cfV = /*(compField && compField->Type == FIELD_PRIVATE) ? ((PrivateField *)compField)->myField :*/ compField; + if (field->Compare(cfV) == 0) + { + if (compField) + { + delete compField; + } + n = i->index->TranslateIndex(From, index); + if (CurrentRecordIdx != n) GetRecordById(n-NUM_SPECIAL_RECORDS, false); + CacheLastLocate(Id, From, field, i->index, n); + success = true; + goto nextiter_2; + } + delete compField; + p = i->index->QuickFindEx(Id, field, From, comp_mode); + if (p != FIELD_NOT_FOUND) + { + n = (index->GetId() == Id) ? p : i->index->TranslateIndex(p, index); + if (CurrentRecordIdx != n) GetRecordById(n-NUM_SPECIAL_RECORDS, false); + CacheLastLocate(Id, From, field, i->index, n); + success = true; + goto nextiter_2; + } + } +nextiter_2: // eek + if (success) + { + if (!MatchFilters() && !Eof()) + { + From = GetRecordId(); + if (nskip) (*nskip)++; + } + else break; + } + else break; + } + + break; + case COMPARE_MODE_STARTS: + while (1) + { + success = false; + if (!i) + { + // No index for this column. Using slow locate, enumerates the database, still faster than what the user + // can do since we have access to QuickFindField which only read field headers + // in order to locate the field we have to compare. user could only read the entire record. + if (From == FIRST_RECORD) + From = NUM_SPECIAL_RECORDS; + else + From+=(NUM_SPECIAL_RECORDS+1); + if (From == lastLocateFrom && Id == lastLocateId && field->Starts(lastLocateFieldClone)==0 && index == lastLocateIndex && (index->locateUpToDate || pTable->GLocateUpToDate)) + { + if (CurrentRecordIdx != lastLocateIdx) GetRecordById(lastLocateIdx-NUM_SPECIAL_RECORDS, false); + success = true; + goto nextiter_3; + } + for (j=From;j<index->NEntries;j++) + { + compField = index->QuickFindField(Id, index->Get(j)); + cfV = /*(compField && compField->Type == FIELD_PRIVATE) ? ((PrivateField *)compField)->myField :*/ compField; + if (!field->Starts(cfV)) + { + if (compField) + { + delete compField; + } + if (CurrentRecordIdx != j) GetRecordById(j-NUM_SPECIAL_RECORDS, false); + CacheLastLocate(Id, From, field, index, j); + success = true; + goto nextiter_3; + } + if (compField) + { + delete compField; + } + } + success = false; + goto nextiter_3; + } + else + { + // Index available. Using fast locate. nfetched=log2(nrecords) for first locate, 1 more fetch per locate on same criteria + int p; + if (From == FIRST_RECORD) From = NUM_SPECIAL_RECORDS; + else From = index->TranslateIndex(From+NUM_SPECIAL_RECORDS, i->index)+1; + if (From == lastLocateFrom && Id == lastLocateId && field->Starts(lastLocateFieldClone)==0 && index == lastLocateIndex && (i->index->locateUpToDate || pTable->GLocateUpToDate)) + { + if (CurrentRecordIdx != lastLocateIdx) GetRecordById(lastLocateIdx-NUM_SPECIAL_RECORDS, false); + success = true; + goto nextiter_3; + } + if (From >= index->NEntries) + { + return false; + } + compField = i->index->QuickFindField(Id, i->index->Get(From)); + cfV = /*(compField && compField->Type == FIELD_PRIVATE) ? ((PrivateField *)compField)->myField :*/ compField; + if (field->Starts(cfV) == 0) + { + delete compField; + n = i->index->TranslateIndex(From, index); + if (CurrentRecordIdx != n) GetRecordById(n-NUM_SPECIAL_RECORDS, false); + CacheLastLocate(Id, From, field, i->index, n); + success = true; + goto nextiter_3; + } + delete compField; + p = i->index->QuickFindEx(Id, field, From, comp_mode); + if (p != FIELD_NOT_FOUND) + { + n = (index->GetId() == Id) ? p : i->index->TranslateIndex(p, index); + if (CurrentRecordIdx != n) GetRecordById(n-NUM_SPECIAL_RECORDS, false); + CacheLastLocate(Id, From, field, i->index, n); + success = true; + goto nextiter_3; + } + } +nextiter_3: // eek + if (success) + { + if (!MatchFilters() && !Eof()) + { + From = GetRecordId(); + if (nskip) (*nskip)++; + } + else break; + } + else break; + } + + break; + } + + + return success; +} + + +//--------------------------------------------------------------------------- +void Scanner::DeleteFieldByName(const char *name) +{ + ColumnField *header = pTable->GetColumnByName(name); + if (header) + { + unsigned char Idx = header->ID; + DeleteFieldById(Idx); + } + return; +} + + +//--------------------------------------------------------------------------- +void Scanner::DeleteFieldById(unsigned char Id) +{ + Field *field = CurrentRecord->GetField(Id); + if (!field) return; + CurrentRecord->RemoveField(field); +} + +//--------------------------------------------------------------------------- +void Scanner::DeleteField(Field *field) +{ + if (!field) return; + CurrentRecord->RemoveField(field); +} + + +static bool TotalSizeCalculator(Record *record, Field *f, void *context) +{ + size_t *totalSize = (size_t *)context; + *totalSize += f->GetTotalSize(); + return true; +} + +//--------------------------------------------------------------------------- +float Scanner::FragmentationLevel(void) +{ + int oldP = GetRecordId(); + int i; + size_t totalSize=0; + + if (CurrentRecord) + { + if (CurrentRecord) CurrentRecord->Release(); + CurrentRecord = NULL; + CurrentRecordIdx = 0; + } + + for (i=0;i<index->NEntries;i++) + { + Record *r = GetRecord(i); + if (r) + { + r->WalkFields(TotalSizeCalculator, &totalSize); + r->Release(); + } + } + GetRecordById(oldP); + Vfseek(pTable->Handle, 0, SEEK_END); + return (((float)(Vftell(pTable->Handle)-strlen(__TABLE_SIGNATURE__)) / (float)totalSize) - 1) * 100; +} + +//--------------------------------------------------------------------------- +int Scanner::GetRecordsCount(void) +{ + if (index) + return index->NEntries-NUM_SPECIAL_RECORDS; + else + return 0; +} + +//--------------------------------------------------------------------------- +bool Scanner::SetWorkingIndexById(unsigned char Id) +{ + IndexField *indx = pTable->GetIndexById(Id); + int v = CurrentRecordIdx; + if (indx) + { + if (!Eof() && !Bof()) + { + IndexField *f = index->SecIndex; + v = index->GetCooperative(CurrentRecordIdx); + while (f != indx) + { + v = f->index->GetCooperative(v); + f = f->index->SecIndex; + } + } + index = indx->index; + CurrentRecordIdx = v; + GetCurrentRecord(); + } + return (indx != NULL); +} + +//--------------------------------------------------------------------------- +bool Scanner::SetWorkingIndexByName(const char *desc) +{ + IndexField *indx = pTable->GetIndexByName(desc); + if (indx) + return SetWorkingIndexById(indx->ID); + else + return SetWorkingIndexById(-1); +} + +//--------------------------------------------------------------------------- +void Scanner::IndexModified(void) +{ + iModified = true; +} + +//--------------------------------------------------------------------------- +void Scanner::ClearDirtyBit(void) +{ + iModified = false; +} + +//--------------------------------------------------------------------------- +Table *Scanner::GetTable(void) +{ + return pTable; +} + +//--------------------------------------------------------------------------- +ColumnField *Scanner::GetColumnByName(const char *FieldName) +{ + return pTable->GetColumnByName(FieldName); +} +//--------------------------------------------------------------------------- +ColumnField *Scanner::GetColumnById(unsigned char Idx) +{ + return pTable->GetColumnById(Idx); +} + +//--------------------------------------------------------------------------- +int Scanner::AddFilterByName(const char *name, Field *Data, unsigned char Op) +{ + ColumnField *f = pTable->GetColumnByName(name); + if (f) + return AddFilterById(f->GetFieldId(), Data, Op); + return ADDFILTER_FAILED; +} + +//--------------------------------------------------------------------------- +int Scanner::AddFilterById(unsigned char Id, Field *Data, unsigned char Op) +{ + ColumnField *f = pTable->GetColumnById(Id); + if (f) + { + Filter *filter = new Filter(Data, f->GetFieldId(), Op); + FilterList.AddEntry(filter, true); + } + else + return ADDFILTER_FAILED; + + if (in_query_parser) return 1; + return CheckFilters(); +} + +//--------------------------------------------------------------------------- +int Scanner::AddFilterOp(unsigned char Op) +{ + Filter *filter = new Filter(Op); + FilterList.AddEntry(filter, true); + if (in_query_parser) return 1; + return CheckFilters(); +} + +//--------------------------------------------------------------------------- +Filter *Scanner::GetLastFilter(void) +{ + if (FilterList.GetNElements() == 0) return NULL; + return (Filter *)FilterList.GetFoot(); +} + +//--------------------------------------------------------------------------- +void Scanner::RemoveFilters(void) +{ + last_query_failed = false; + while (FilterList.GetNElements() > 0) + FilterList.RemoveEntry(FilterList.GetHead()); + FiltersOK = false; +} + +//--------------------------------------------------------------------------- +bool Scanner::CheckFilters(void) +{ + int f=0; + FiltersOK = false; + if (FilterList.GetNElements() == 0) // Should never happen + return FILTERS_INVALID; + Filter *filter = (Filter *)FilterList.GetHead(); + while (filter) + { + if (f == 256) return FILTERS_INVALID; + int op = filter->GetOp(); + if (filter->Data() || op == FILTER_ISEMPTY || op == FILTER_ISNOTEMPTY) + f++; + else + { + if (op != FILTER_NOT) + f--; + } + if (f == 0) return FILTERS_INVALID; + filter = (Filter *)filter->GetNext(); + } + + if (f == 1) + { + FiltersOK = true; + return FILTERS_COMPLETE; + } + return FILTERS_INCOMPLETE; +} + +//--------------------------------------------------------------------------- +static bool EmptyMeansTrue(int op) +{ + return op == FILTER_ISEMPTY + || op == FILTER_NOTEQUALS + || op == FILTER_NOTCONTAINS; +} + +//--------------------------------------------------------------------------- +bool Scanner::MatchFilter(Filter *filter) +{ + Field *field = GetFieldById(filter->GetId()); + int op = filter->GetOp(); + Field * f = filter->Data(); + /* old behaviour + if (!field) + return EmptyMeansTrue(op); + else if (op == FILTER_ISEMPTY) + return false; + else if (op == FILTER_ISNOTEMPTY) + return true; + */ + + // new behaviour + if (!field) + { + // if field is empty and we're doing an equals op, match if f is also empty + if (op == FILTER_EQUALS && f) return f->ApplyFilter(f,FILTER_ISEMPTY); + if (op == FILTER_NOTEQUALS && f) return f->ApplyFilter(f,FILTER_ISNOTEMPTY); + return EmptyMeansTrue(op); + } + // no need to check for op == FILTER_ISEMPTY, the fields now handle that + + return field->ApplyFilter(f, op); +} + +struct Results +{ + void operator=(bool _val) + { + calculated=true; + value=_val; + } + + bool Calc(Scanner *scanner) + { + if (!calculated) + { +#if 0 /* if we want to do field-to-field comparisons */ + Field *compare_column = filter->Data(); + if (compare_column && compare_column->Type == FIELD_COLUMN) + { + Field *field = scanner->GetFieldById(filter->GetId()); + Field *compare_field = scanner->GetFieldById(compare_column->ID); + int op = filter->GetOp(); + value = field->ApplyFilter(compare_field, op); + calculated=true; + return value; + } +#endif + + value = scanner->MatchFilter(filter); + calculated=true; + } + return value; + } + + void SetFilter(Filter *_filter) + { + calculated=false; + filter=_filter; + } +private: + bool value; + bool calculated; + Filter *filter; +}; + +bool Scanner::MatchSearch(const SearchFields &fields, StringField *search_field) +{ + for (SearchFields::const_iterator itr = fields.begin(); itr != fields.end();itr++) + { + Field *f = GetFieldById(*itr); + if (f && f->Contains(search_field)) + { + return true; + } + } + return false; // if none of the fields matched the search strings, then bail out +} + +bool Scanner::MatchSearches() +{ + // no search means always match + if (search_strings.empty()) + return true; + + Scanner *s = pTable->GetDefaultScanner(); // kind of a hack but gets around private member variables + const SearchFields &fields = s->search_fields; + if (search_any) + { + for (SearchStrings::const_iterator field_itr=search_strings.begin();field_itr!=search_strings.end();field_itr++) + { + StringField *search_field = *field_itr; + if (MatchSearch(fields, search_field) == true) + return true; + } + return false; // we'll only get here if no search strings matched + } + else + { // normal search (subsequent terms do further filtering + for (SearchStrings::const_iterator field_itr=search_strings.begin();field_itr!=search_strings.end();field_itr++) + { + StringField *search_field = *field_itr; + if (MatchSearch(fields, search_field) == false) + return false; + } + return true; // we'll only get here if all search strings have a match + } +} + +//--------------------------------------------------------------------------- +bool Scanner::MatchFilters(void) +{ + if (!FiltersOK || FilterList.GetNElements() == 0) + { + // return MatchJoins(); + return true; + } + + //if (!MatchSearches()) + //return false; + + ResultPtr = 0; + + Results resultTable[256]; + + Filter *filter = (Filter *)FilterList.GetHead(); + while (filter) + { + if (ResultPtr == 256) + { + FiltersOK = false; // Should never happen, case already discarded by CheckFilters + return true; + } + int op = filter->GetOp(); + if (filter->Data() || op == FILTER_ISEMPTY || op == FILTER_ISNOTEMPTY) + resultTable[ResultPtr++].SetFilter(filter); + else + switch (op) + { + case FILTER_AND: + if (ResultPtr > 1) + resultTable[ResultPtr-2] = resultTable[ResultPtr-2].Calc(this) && resultTable[ResultPtr-1].Calc(this); + ResultPtr--; + break; + case FILTER_OR: + if (ResultPtr > 1) + resultTable[ResultPtr-2] = resultTable[ResultPtr-2].Calc(this) || resultTable[ResultPtr-1].Calc(this); + ResultPtr--; + break; + case FILTER_NOT: + if (ResultPtr > 0) + resultTable[ResultPtr-1] = !resultTable[ResultPtr-1].Calc(this); + break; + } + filter = (Filter *)filter->GetNext(); + } + + if (ResultPtr != 1) // Should never happen, case already discarded by CheckFilters + { + FiltersOK = false; + return true; + } + + if (!resultTable[0].Calc(this)) return 0; + // return MatchJoins(); + //return false; + return MatchSearches(); +} + +void Scanner::WalkFilters(FilterWalker callback, void *context) +{ + if (callback) + { + LinkedListEntry *entry = FilterList.GetHead(); + while (entry) + { + if (!callback(this, (Filter *)entry, context)) + break; + entry = entry->Next; + } + } +} + +void Scanner::Search(const char *search_string) +{ + // first, clear existing search terms + for (SearchStrings::iterator itr=search_strings.begin();itr!=search_strings.end();itr++) + { + delete *itr; + } + search_strings.clear(); + + if (*search_string == '*' && search_string[1] == ' ') + { + search_any=true; + search_string += 2; + } + else + search_any=false; + + if (search_string) + { + + while (*search_string) + { + while (*search_string && (*search_string == ' ' || *search_string == '\t')) + search_string++; + + const char *end=search_string; + + char c = *search_string; + if (c == '\"') // a quoted string + { + end++; + search_string++; + while (*end && *end != '\"') + end++; + + if (*search_string) // make sure it's not just a quote by itself + { + if (*end == 0) // no terminating quotes + { + char *search_term = ndestring_wcsndup(search_string, end-search_string); + search_strings.push_back(new StringField(search_term, STRING_IS_NDESTRING)); + } + else if (end > (search_string+1)) // at least one character in the quotes + { + char *search_term = ndestring_wcsndup(search_string, end-search_string-1); + search_strings.push_back(new StringField(search_term, STRING_IS_NDESTRING)); + end++; + } + } + search_string=end; + } + else if (c) + { + while (*end && *end != ' ' && *end != '\t') + end++; + + char *search_term = ndestring_wcsndup(search_string, end-search_string-1); + search_strings.push_back(new StringField(search_term, STRING_IS_NDESTRING)); + } + } + } +}
\ No newline at end of file |