arr is not null, then this array is used as destination (it should
* be at least selection.nRows long)arr is null, then new array is created by new oid_t[] and returned by this method
* @return if arr is not null, then arr, otherwise array created by this method
*/
oid_t* toArrayOfOid(oid_t* arr);
/**
* Execute query.
* @param query selection criteria
* @param aType cursor type: dbCursorForUpdate, dbCursorViewOnly
* @param paramStruct pointer to structure with parameters. If you want to create reentrant precompiled query, i.e.
* query which can be used concurrently by different threadsm you should avoid to use static variables in
* such query, and instead of it place paramters into some structure, specify in query relative offsets to the parameters,
* fill local structure and pass pointer to it to select method.
* @return number of selected records
*/
int select(dbQuery& query, dbCursorType aType, void* paramStruct = NULL) {
type = aType;
reset();
paramBase = paramStruct;
db->select(this, query);
paramBase = NULL;
if (gotoFirst() && prefetch) {
fetch();
}
return selection.nRows;
}
/**
* Execute query with default cursor type.
* @param query selection criteria
* @param paramStruct pointer to structure with parameters.
* @return number of selected records
*/
int select(dbQuery& query, void* paramStruct = NULL) {
return select(query, defaultType, paramStruct);
}
/**
* Execute query.
* @param condition selection criteria
* @param aType cursor type: dbCursorForUpdate, dbCursorViewOnly
* @param paramStruct pointer to structure with parameters.
* @return number of selected records
*/
int select(char const* condition, dbCursorType aType, void* paramStruct = NULL) {
dbQuery query(condition);
return select(query, aType, paramStruct);
}
/**
* Execute query with default cursor type.
* @param condition selection criteria
* @param paramStruct pointer to structure with parameters.
* @return number of selected records
*/
int select(char const* condition, void* paramStruct = NULL) {
return select(condition, defaultType, paramStruct);
}
/**
* Select all records from the table
* @param aType cursor type: dbCursorForUpdate, dbCursorViewOnly
* @return number of selected records
*/
int select(dbCursorType aType) {
type = aType;
reset();
db->select(this);
if (gotoFirst() && prefetch) {
fetch();
}
return selection.nRows;
}
/**
* Select all records from the table with default cursor type
* @return number of selected records
*/
int select() {
return select(defaultType);
}
/**
* Select all records from the table with specfied value of the key
* @param key name of the key field
* @param value searched value of the key
* @return number of selected records
*/
int selectByKey(char const* key, void const* value);
/**
* Select all records from the table with specfied range of the key values
* @param key name of the key field
* @param minValue inclusive low bound for key values, if NULL then there is no low bound
* @param maxValue inclusive high bound for key values, if NULL then there is no high bound
* @return number of selected records
*/
int selectByKeyRange(char const* key, void const* minValue, void const* maxValue);
/**
* Update current record. You should changed value of current record before and then call
* update method to save changes to the database
*/
void update() {
assert(type == dbCursorForUpdate && currId != 0);
updateInProgress = true;
db->update(currId, table, record);
updateInProgress = false;
}
/**
* Remove all records in the table
*/
void removeAll() {
assert(db != NULL);
db->deleteTable(table);
reset();
}
/**
* Remove all selected records
*/
void removeAllSelected();
/**
* Specify maximal number of records to be selected
*/
void setSelectionLimit(size_t lim) { limit = lim; }
/**
* Remove selection limit
*/
void unsetSelectionLimit() { limit = dbDefaultSelectionLimit; }
/**
* Set prefetch mode. By default, current record is fetch as soon as it is becomes current.
* But sometimesyou need only OIDs of selected records. In this case setting prefetchMode to false can help.
* @param mode if false then current record is not fetched. You should explicitly call fetch
* method if you want to fetch it.
*/
void setPrefetchMode(bool mode) { prefetch = mode; }
/**
* Reset cursor
*/
void reset();
/**
* Check whether current record is the last one in the selection
* @return true if next() method will return NULL
*/
bool isLast();
/**
* Check whether current record is the first one in the selection
* @return true if prev() method will return NULL
*/
bool isFirst();
/**
* Freeze cursor. This method makes it possible to save current state of cursor, close transaction to allow
* other threads to proceed, and then later restore state of the cursor using unfreeze method and continue
* traversal through selected records.
*/
void freeze();
/**
* Unfreeze cursor. This method starts new transaction and restore state of the cursor
*/
void unfreeze();
/**
* Skip specified number of records
* @param n if positive then skip n records forward, if negative then skip -n
* records backward
* @return true if specified number of records was successfully skipped, false if
* there is no next (n > 0) or previous (n < 0) record in the selction.
*/
bool skip(int n);
/**
* Position cursor on the record with the specified OID
* @param oid object identifier of record
* @return poistion of the record in the selection or -1 if record with such OID is not in selection
*/
int seek(oid_t oid);
/**
* Get table for which cursor is opened
*/
dbTableDescriptor* getTable() { return table; }
/**
* Set table for the cursor
* @param aTable table which records will be iterated
*/
void setTable(dbTableDescriptor* aTable) {
table = aTable;
db = aTable->db;
}
/**
* Set destination for selected record
* rec - buffer to which fields of current record will be fetched
*/
void setRecord(void* rec) {
record = (byte*)rec;
}
/**
* Get pointer to the location where fields of the current record are fetched
* @return pointer to the memory location set by cursor constructor or setRecord method
*/
void* getRecord() {
return record;
}
protected:
dbDatabase* db;
dbTableDescriptor* table;
dbCursorType type;
dbCursorType defaultType;
dbSelection selection;
bool allRecords;
oid_t firstId;
oid_t lastId;
oid_t currId;
byte* record;
size_t limit;
int4* bitmap; // bitmap to avoid duplicates
size_t bitmapSize;
bool eliminateDuplicates;
bool prefetch;
bool removed; // current record was removed
bool updateInProgress;
void* paramBase;
void checkForDuplicates();
bool isMarked(oid_t oid) {
return bitmap != NULL && (bitmap[oid >> 5] & (1 << (oid & 31))) != 0;
}
void mark(oid_t oid) {
if (bitmap != NULL) {
bitmap[oid >> 5] |= 1 << (oid & 31);
}
}
bool add(oid_t oid) {
if (selection.nRows < limit) {
if (eliminateDuplicates) {
if (bitmap[oid >> 5] & (1 << (oid & 31))) {
return true;
}
bitmap[oid >> 5] |= 1 << (oid & 31);
}
selection.add(oid);
return selection.nRows < limit;
}
return false;
}
bool gotoNext();
bool gotoPrev();
bool gotoFirst();
bool gotoLast();
void setCurrent(dbAnyReference const& ref);
void fetch() {
assert(!(db->currIndex[currId]
& (dbInternalObjectMarker|dbFreeHandleMarker)));
table->columns->fetchRecordFields(record,
(byte*)db->getRow(currId));
}
void adjustReferences(size_t base, size_t size, long shift) {
if (currId != 0) {
table->columns->adjustReferences(record, base, size, shift);
}
}
dbAnyCursor(dbTableDescriptor& aTable, dbCursorType aType, byte* rec)
: table(&aTable),type(aType),defaultType(aType),
allRecords(false),currId(0),record(rec)
{
db = aTable.db;
limit = dbDefaultSelectionLimit;
updateInProgress = false;
prefetch = true;
removed = false;
bitmap = NULL;
bitmapSize = 0;
eliminateDuplicates = false;
paramBase = NULL;
}
public:
dbAnyCursor()
: table(NULL),type(dbCursorViewOnly),defaultType(dbCursorViewOnly),
allRecords(false),currId(0),record(NULL)
{
limit = dbDefaultSelectionLimit;
updateInProgress = false;
prefetch = false;
removed = false;
bitmap = NULL;
bitmapSize = 0;
eliminateDuplicates = false;
db = NULL;
paramBase = NULL;
}
~dbAnyCursor();
};
/**
* Cursor template parameterized by table class
*/
templateNULL if there is no current record
*/
T* get() {
return currId == 0 ? (T*)NULL : &record;
}
/**
* Get next record
* @return pointer to the next record or NULL if there is no next record
*/
T* next() {
if (gotoNext()) {
fetch();
return &record;
}
return NULL;
}
/**
* Get previous record
* @return pointer to the previous record or NULL if there is no previous record
*/
T* prev() {
if (gotoPrev()) {
fetch();
return &record;
}
return NULL;
}
/**
* Get pointer to the first record
* @return pointer to the first record or NULL if no records were selected
*/
T* first() {
if (gotoFirst()) {
fetch();
return &record;
}
return NULL;
}
/**
* Get pointer to the last record
* @return pointer to the last record or NULL if no records were selected
*/
T* last() {
if (gotoLast()) {
fetch();
return &record;
}
return NULL;
}
/**
* Position cursor on the record with the specified OID
* @param oid object identifier of record
* @return poistion of the record in the selection or -1 if record with such OID is not in selection
*/
int seek(dbReference
* if (cursor.select(q) > 0) {
* do {
* if (x) {
* cursor.remove();
* } else {
* cursor.update();
* }
* } while (cursor.nextAvaiable());
* }
*
* @return pointer to the current record
*/
T* nextAvailable() {
if (!removed) {
return next();
} else {
removed = false;
return get();
}
}
};
class dbParallelQueryContext {
public:
dbDatabase* const db;
dbCompiledQuery* const query;
oid_t firstRow;
dbTable* table;
dbAnyCursor* cursor;
dbSelection selection[dbMaxParallelSearchThreads];
void search(int i);
dbParallelQueryContext(dbDatabase* aDb, dbTable* aTable,
dbCompiledQuery* aQuery, dbAnyCursor* aCursor)
: db(aDb), query(aQuery), firstRow(aTable->firstRow), table(aTable), cursor(aCursor) {}
};
extern char* strupper(char* s);
extern char* strlower(char* s);
#endif