/* --------------------------------------------------------------------------- Nullsoft Database Engine -------------------- codename: Near Death Experience --------------------------------------------------------------------------- */ /* --------------------------------------------------------------------------- Scanner Class --------------------------------------------------------------------------- */ #include "../nde.h" #include "BinaryField.h" #include "Binary32Field.h" #include //--------------------------------------------------------------------------- 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;jNEntries;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;jNEntries;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;jNEntries;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;iNEntries;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)); } } } }