// filtermachine.cpp
#include "StdAfx.h"
#include "filtermachine.h"
#include "vtab_info.h"
#include "index_selector.h"
#include "utex.h"
#include "longstrreader.h"
static CNumeric staticNumValue;
inline void writeNum(unsigned char*& ptr, WORD val)
{
*(WORD*)ptr = val;
ptr += 2;
}
inline DWORD readNum(const unsigned char*& ptr)
{
WORD val = *(WORD*)ptr;
ptr += 2;
return val;
}
struct text_compare
{
static int compare(LPCSTR str, LPCSTR pFieldBuf, DWORD dwFieldLen)
{
return u8text::compareLen(str, pFieldBuf, dwFieldLen);
}
};
struct text_nocase_compare
{
static int compare(LPCSTR str, LPCSTR pFieldBuf, DWORD dwFieldLen)
{
return u8text::compareNoCaseLen(str, pFieldBuf, dwFieldLen);
}
};
struct numeric_compare
{
static int compare(LPCSTR pNum, LPCSTR pFieldBuf, DWORD dwFieldLen)
{
BOOL bothNegate = FALSE;
while(dwFieldLen--)
{
int numSymb = static_cast<DWORD>(static_cast<BYTE>(*pNum));
int valSymb = static_cast<DWORD>(static_cast<BYTE>(*pFieldBuf));
int dif = numSymb - valSymb;
if(dif)
{
if('-' == numSymb) // ,
return -1;
else if('-' == valSymb) // ,
return 1;
else
return bothNegate ? -dif : dif;
}
else if('-' == numSymb)
bothNegate = TRUE;
pNum++, pFieldBuf++;
}
return 0;
}
};
template<TestOp Op, typename T>
struct tcTestField : public testConstraint
{
tcTestField(testConstraint*& p, const CString& t, const field_info& fi)
: testConstraint(p), m_value(t), m_fOffset(fi.offsetInRecord()),
m_fLen(fi.length()){}
CString m_value;
int m_fOffset, m_fLen;
virtual BOOL test(const cursor_data& cursor)
{
int res = T::compare(m_value, cursor.tableRecordBuf + m_fOffset, m_fLen);
if(Op == toEqual)
{
if(0 == res)
return TRUE;
}
else if(Op == toLess)
{
if(res > 0)
return TRUE;
}
else if(Op == toLessEq)
{
if(res >= 0)
return TRUE;
}
else if(Op == toGrat)
{
if(res < 0)
return TRUE;
}
else if(Op == toGratEq)
{
if(res <= 0)
return TRUE;
}
return FALSE;
}
};
template<TestOp op>
struct tcFullKey : public testConstraint
{
tcFullKey(testConstraint*& p, CString& key, const index_info* pIndexInfo)
: testConstraint(p), recNum(0), m_pIndexInfo(pIndexInfo)
{
int keyLen = key.GetLength(), keySize = m_pIndexInfo->keySize();
if(keyLen > keySize)
recNum = atol((LPCSTR)key + keySize);
else if(keyLen < keySize)
memset(key.GetBufferSetLength(keySize) + keyLen, ' ', keySize - keyLen);
DWORD countFields = m_pIndexInfo->fieldsCount();
keys.SetSize(countFields);
LPCSTR pRead = key;
const idx_field_info* pFields = m_pIndexInfo->fields();
CString* pString = keys.GetData();
for(DWORD i = countFields; i-- ; )
{
const field_info& info = pFields->info();
memcpy(pString->GetBufferSetLength(info.length()), pRead, info.length());
pRead += info.length();
pString++;
pFields++;
}
}
CStringArray keys;
int recNum;
const index_info* m_pIndexInfo;
virtual BOOL test(const cursor_data& cursor)
{
int size = m_pIndexInfo->fieldsCount();
const idx_field_info* pFields = m_pIndexInfo->fields();
CString* pString = keys.GetData();
while(size--)
{
const field_info& field = pFields->info();
int res;
res = u8text::compareNoCaseLen(*pString, cursor.tableRecordBuf + field.offsetInRecord(), field.length());
switch(op)
{
case toEqual:
if(0 != res)
return FALSE;
break;
case toLess:
if(res > 0)
return TRUE;
else if(res < 0)
return FALSE;
else if(size == 0 && recNum == 0) // ,
return FALSE;
break;
case toLessEq:
if(res > 0)
return TRUE;
else if(res < 0)
return FALSE;
break;
case toGrat:
if(res < 0)
return TRUE;
else if(res > 0)
return FALSE;
else if(size == 0 && recNum == 0) // ,
return FALSE;
break;
case toGratEq:
if(res < 0)
return TRUE;
else if(res > 0)
return FALSE;
break;
}
pFields++;
pString++;
}
// , .
// , .
if(0 == recNum)
return TRUE;
switch(op)
{
case toEqual:
return recNum == cursor.store.m_pos;
case toLess:
return recNum > cursor.store.m_pos;
case toLessEq:
return recNum >= cursor.store.m_pos;
case toGrat:
return recNum < cursor.store.m_pos;
case toGratEq:
return recNum <= cursor.store.m_pos;
}
return FALSE;
}
};
template<TestOp Op>
struct tcLongStr : public testConstraint
{
tcLongStr(testConstraint*& p, const CString& value, DWORD mdID)
: testConstraint(p), m_value(value), m_mdID(mdID){}
DWORD m_mdID;
CString m_value;
virtual BOOL test(const cursor_data& cursor)
{
static CString value;
LongStrReader::get().ReadStr(cursor.tableRecordBuf, m_mdID, value);
int res = u8text::compareRtrimNoCase(m_value, value);
if(Op == toEqual)
{
if(0 == res)
return TRUE;
}
else if(Op == toLess)
{
if(res > 0)
return TRUE;
}
else if(Op == toLessEq)
{
if(res >= 0)
return TRUE;
}
else if(Op == toGrat)
{
if(res < 0)
return TRUE;
}
else if(Op == toGratEq)
{
if(res <= 0)
return TRUE;
}
return FALSE;
}
};
template<TestOp Op>
struct tcRecNo : public testConstraint
{
tcRecNo(testConstraint*& p, long value)
: testConstraint(p), m_value(value){}
long m_value;
virtual BOOL test(const cursor_data& cursor)
{
long res = m_value - cursor.store.m_pos;
if(Op == toEqual)
{
if(0 == res)
return TRUE;
}
else if(Op == toLess)
{
if(res > 0)
return TRUE;
}
else if(Op == toLessEq)
{
if(res >= 0)
return TRUE;
}
else if(Op == toGrat)
{
if(res < 0)
return TRUE;
}
else if(Op == toGratEq)
{
if(res <= 0)
return TRUE;
}
return FALSE;
}
};
inline int nextGratStringUpper(CString& text, DWORD fLen)
{
extern BYTE nextGratUSymbols[256];
unsigned char *pStart = (unsigned char*)text.GetBufferSetLength(fLen), *pWrite = pStart + fLen - 1;
while(pWrite >= pStart)
{
BYTE ngs = nextGratUSymbols[static_cast<DWORD>(*pWrite)];
if(ngs)
{
*pWrite = ngs;
return 1;
}
else
*pWrite-- = ' ';
}
return 2; // ("");
}
// >0 -
// . , ,
// , .
int compareFk_Pk(const CString& fullKey, const CString& partKey, DWORD fillSymbol)
{
LPCSTR pFull = fullKey, pPart = partKey;
int sFull, sPart;
for(;;)
{
sFull = static_cast<DWORD>(static_cast<BYTE>(*pFull));
sPart = static_cast<DWORD>(static_cast<BYTE>(*pPart));
if(!sPart)
return fillSymbol == 0xFF ? -1 : 1;
int dif = sFull - sPart;
if(dif)
return dif;
pFull++;
pPart++;
}
return 0;
}
template<int BoundType> // 0 - equal, 1 - min, 2 - max
struct normalizeString
{
// 0 - success, 1 - trimmed, 2 - error
inline static int set(CString& str, DWORD keyLength)
{
DWORD strLength = str.GetLength();
if(strLength > keyLength) // . .
{
str.TrimRight();
strLength = str.GetLength();
}
if(strLength > keyLength)
{
if(0 == BoundType)
return 2; // . .
else if(1 == BoundType)
{
// . 1 >'ad' >='ad'
// , >='b'.
// , >='' .
// , , ,
// hard .
str.GetBufferSetLength(keyLength);
return nextGratStringUpper(str, keyLength);
}
else if(2 == BoundType)
{
// . 1 <'ad' <='ad'
// , <='a'.
// , hard .
str.GetBufferSetLength(keyLength);
return 1;
}
}
if(strLength < keyLength)
memset(str.GetBufferSetLength(keyLength) + strLength, ' ', keyLength - strLength);
return 0;
}
};
template<int BoundType, BOOL bAllowNegate, BOOL bForIndex>
struct normalizeNumeric
{
// 0 - , 1 - , 2 - , 3 -
inline static int set(double dblVal, const field_info& info, CString& strValue)
{
// AllowNegate bForIndex
char testParam[bAllowNegate && bForIndex && BoundType != 0 ? -1 : 1] = {0};
DWORD lenOfField = info.length(), precOfField = info.precession();
BOOL bNegate = dblVal < 0;
if(!bAllowNegate && bNegate)
{
if(1 == BoundType)
{
// "", > ,
// .
if(bForIndex)
{
memset(strValue.GetBufferSetLength(lenOfField), ' ', lenOfField);
return 0;
}
return 3;
}
else
return 2;
}
staticNumValue = dblVal;
staticNumValue.Round(precOfField);
char buffer[60];
LPSTR pStart = buffer + 30;
char* p = staticNumValue.Convert(pStart, lenOfField + 2, precOfField);
int lenOfStr = strlen(pStart);
if(!bAllowNegate)
{
// . 1
// -1 .
if(lenOfStr >= lenOfField) // .
{
if(0 == BoundType)
return 2; //
else if(1 == BoundType)
return 2; //
else if(2 == BoundType) //
{
// <todo>
if(bForIndex)
{
LPSTR ptr = strValue.GetBufferSetLength(lenOfField);
memset(ptr, '9', lenOfField);
*ptr = ' ';
ptr[lenOfField - precOfField - 1] = '.';
return 1;
}
return 3;
}
}
}
else
{
// .
if(lenOfStr > lenOfField) //
{
if(0 == BoundType)
return 2;
if(bNegate) // .
{
if(1 == BoundType)
return 3; // numeric(4.1) -9.9 > -99.1
else if(2 == BoundType)
return 2;
}
else //
{
if(1 == BoundType)
return 2;
else if(2 == BoundType)
return 3;
}
}
else if(lenOfStr == lenOfField) // ,
{
if(!bNegate) // ,
{
if(0 == BoundType) // ,
return 2;
else if(1 == BoundType) //
return 2;
else if(2 == BoundType) //
return 3;
}
}
}
while(lenOfStr < lenOfField)
{
*--pStart = ' ';
lenOfStr++;
}
memcpy(strValue.GetBufferSetLength(lenOfField), pStart, lenOfField);
return 0;
}
};
template<int BoundType, BOOL bAllowNegate, BOOL bForIndex>
struct normalizeLong
{
// 0 - , 1 - , 2 - , 3 -
inline static int set(__int64& numValue, const field_info& info, CString& strValue)
{
// AllowNegate bForIndex
char testParam[bAllowNegate && bForIndex && BoundType != 0 ? -1 : 1] = {0};
DWORD lenOfField = info.length();
if(!bAllowNegate && numValue < 0)
{
if(1 == BoundType)
{
if(bForIndex)
{
memset(strValue.GetBufferSetLength(lenOfField), ' ', lenOfField);
return 0;
}
return 3;
}
return 2;
}
if(lenOfField == 1)
{
if(numValue >=0 && numValue <= 10)
{
strValue.GetBufferSetLength(1)[0] = numValue + '0';
return 0;
}
if(0 == BoundType)
return 2;
else if(1 == BoundType)
return numValue < 0 ? 3 : 2;
else if(2 == BoundType)
return numValue > 0 ? 3 : 2;
}
int lenOfStr = 0;
char buffer[60];
LPSTR pStart = buffer + 60;
__int64 val = numValue < 0 ? -numValue : numValue;
do
{
*--pStart = (val % 10) + '0';
lenOfStr++;
val /= 10;
}while(val && lenOfStr < lenOfField);
if(!bAllowNegate)
{
// . 1
// -1 , 1 (isfolder )
if(lenOfStr == lenOfField) // .
{
if(0 == BoundType)
return 2; //
else if(1 == BoundType)
return 2; //
else if(2 == BoundType) //
{
// <todo>
if(bForIndex)
{
LPSTR ptr = strValue.GetBufferSetLength(lenOfField);
memset(ptr, '9', lenOfField);
*ptr = ' ';
return 1;
}
return 3;
}
}
}
else
{
// .
if(lenOfStr == lenOfField)
{
if(0 == BoundType)
return 2;
else if(1 == BoundType)
{
if(numValue < 0)
return 3;
else
return 2;
}
else if(2 == BoundType)
{
if(numValue < 0)
return 2;
else
return 3;
}
}
}
if(numValue < 0)
{
*--pStart = '-';
lenOfStr++;
}
lenOfStr = lenOfField - lenOfStr;
while(lenOfStr)
{
*--pStart = ' ';
lenOfStr--;
}
memcpy(strValue.GetBufferSetLength(lenOfField), pStart, lenOfField);
return 0;
}
};
template<int BoundType>
struct addTestText
{
inline static BOOL add(const unsigned char*& pByteCode, CString& value, const CVtabInfo& table, DWORD bHard, testConstraint*& pC)
{
DWORD dwField = readNum(pByteCode);
const one_field& field = table.field(dwField);
if(field.isField())
{
const field_info& fi = table.phisInfo().field(field.pos());
DWORD len = fi.length();
int res = normalizeString<BoundType>::set(value, len);
if(res == 2)
return FALSE;
else if(res == 1)
bHard = FALSE;
if(0 == BoundType)
new tcTestField<toEqual, text_nocase_compare>(pC, value, fi);
else if(1 == BoundType)
{
if(bHard)
new tcTestField<toGrat, text_nocase_compare>(pC, value, fi);
else
new tcTestField<toGratEq, text_nocase_compare>(pC, value, fi);
}
else if(2 == BoundType)
{
if(bHard)
new tcTestField<toLess, text_nocase_compare>(pC, value, fi);
else
new tcTestField<toLessEq, text_nocase_compare>(pC, value, fi);
}
}
else if(field.isIndex())
{
const index_info* pIndexInfo = table.phisInfo().index(field.pos());
DWORD len = pIndexInfo->keySize() + 10;
int res = normalizeString<BoundType>::set(value, len);
if(res == 2)
return FALSE;
else if(res == 1)
bHard = 0;
if(0 == BoundType)
{
new tcFullKey<toEqual>(pC, value, pIndexInfo);
}
else if(1 == BoundType)
{
if(bHard)
new tcFullKey<toGrat>(pC, value, pIndexInfo);
else
new tcFullKey<toGratEq>(pC, value, pIndexInfo);
}
else if(2 == BoundType)
{
if(bHard)
new tcFullKey<toLess>(pC, value, pIndexInfo);
else
new tcFullKey<toLessEq>(pC, value, pIndexInfo);
}
}
else if(field.isLnStr())
{
DWORD mdID = field.pos();
if(0 == BoundType)
{
new tcLongStr<toEqual>(pC, value, mdID);
}
else if(1 == BoundType)
{
if(bHard)
new tcLongStr<toGrat>(pC, value, mdID);
else
new tcLongStr<toGratEq>(pC, value, mdID);
}
else if(2 == BoundType)
{
if(bHard)
new tcLongStr<toLess>(pC, value, mdID);
else
new tcLongStr<toLessEq>(pC, value, mdID);
}
}
return TRUE;
}
};
template<int BoundType>
struct addTestNumeric
{
inline static BOOL add(const unsigned char*& pByteCode, double value, const CVtabInfo& table, DWORD bHard, testConstraint*& pC)
{
static CString strValue;
DWORD dwField = readNum(pByteCode);
const field_info& field = table.phisInfo().field(dwField);
int res = normalizeNumeric<BoundType, TRUE, FALSE>::set(value, field, strValue);
if(res == 3)
return TRUE; //
else if(res == 2) //
return FALSE;
else if(res == 1)
bHard = 0;
if(0 == BoundType)
{
new tcTestField<toEqual, numeric_compare>(pC, strValue, field);
}
else if(1 == BoundType)
{
if(bHard)
new tcTestField<toGrat, numeric_compare>(pC, strValue, field);
else
new tcTestField<toGratEq, numeric_compare>(pC, strValue, field);
}
else if(2 == BoundType)
{
if(bHard)
new tcTestField<toLess, numeric_compare>(pC, strValue, field);
else
new tcTestField<toLessEq, numeric_compare>(pC, strValue, field);
}
return TRUE;
}
};
template<int BoundType>
struct addTestLong
{
inline static BOOL add(const unsigned char*& pByteCode, __int64 value, const CVtabInfo& table, DWORD bHard, testConstraint*& pC)
{
static CString strValue;
DWORD dwField = readNum(pByteCode);
const field_info& field = table.phisInfo().field(dwField);
int res = normalizeLong<BoundType, TRUE, FALSE>::set(value, field, strValue);
if(res == 3)
return TRUE; //
else if(res == 2) //
return FALSE;
else if(res == 1)
bHard = 0;
if(0 == BoundType)
{
new tcTestField<toEqual, numeric_compare>(pC, strValue, field);
}
else if(1 == BoundType)
{
if(bHard)
new tcTestField<toGrat, numeric_compare>(pC, strValue, field);
else
new tcTestField<toGratEq, numeric_compare>(pC, strValue, field);
}
else if(2 == BoundType)
{
if(bHard)
new tcTestField<toLess, numeric_compare>(pC, strValue, field);
else
new tcTestField<toLessEq, numeric_compare>(pC, strValue, field);
}
return TRUE;
}
};
select_iterator* FilterMachine::process(const unsigned char* pByteCode, sqlite3_value** args)
{
static CString argString;
static double argNumeric;
static __int64 argLong;
for(;;)
{
int res;
switch(static_cast<DWORD>(*pByteCode++))
{
case selectIdx:
m_dwCurIdx = readNum(pByteCode);
m_pIndexInfo = m_table.phisInfo().index(m_dwCurIdx);
m_pFields = m_pIndexInfo->fields();
m_cursor.store.SetOrderIndex(m_table.table()->index(m_dwCurIdx));
break;
case getArgText:
if(SQLITE_TEXT != sqlite3_value_type(*args))
return NULL;
u8text::fromUtf8((LPCSTR)sqlite3_value_text(*args++), argString);
u8text::dbUpper(argString);
break;
case getArgNumeric:
if(SQLITE_NULL == sqlite3_value_type(*args))
return NULL;
argNumeric = sqlite3_value_double(*args++);
break;
case getArgLong:
if(SQLITE_NULL == sqlite3_value_type(*args))
return NULL;
argLong = sqlite3_value_int64(*args++);
break;
case setEqualString:
m_crntEqualText = argString;
break;
case setEqualNumeric:
m_crntEqualNum = argNumeric;
break;
case setEqualLong:
m_crntEqualLong = argLong;
break;
case cmpStringEqual_Equal:
if(0 != u8text::compareRtrim(m_crntEqualText, argString))
return NULL;
break;
case cmpNumericEqual_Equal:
if(m_crntEqualNum != argNumeric)
return NULL;
break;
case cmpLongEqual_Equal:
if(m_crntEqualLong != argLong)
return NULL;
break;
// >
case cmpString_Min_Equal:
if(u8text::compareRtrim(m_crntEqualText, argString) <= 0)
return NULL;
break;
case cmpNumeric_Min_Equal:
if(m_crntEqualNum <= argNumeric)
return NULL;
break;
case cmpLong_Min_Equal:
if(m_crntEqualLong <= argLong)
return NULL;
break;
case setMinString:
m_crntMinimumText = argString;
m_hardCurMin = 1;
break;
case setMinNumeric:
m_crntMinimumNum = argNumeric;
m_hardCurMin = 1;
break;
case setMinLong:
m_crntMinimumLong = argLong;
m_hardCurMin = 1;
break;
case selMinString:
if(u8text::compareRtrim(m_crntMinimumText, argString) <= 0)
{
m_crntMinimumText = argString;
m_hardCurMin = 1;
}
break;
case selMinNumeric:
if(m_crntMinimumNum <= argNumeric)
{
m_crntMinimumNum = argNumeric;
m_hardCurMin = 1;
}
break;
case selMinLong:
if(m_crntMinimumLong <= argLong)
{
m_crntMinimumLong = argLong;
m_hardCurMin = 1;
}
break;
// >=
case cmpString_MinEq_Equal:
if(u8text::compareRtrim(m_crntEqualText, argString) < 0)
return NULL;
break;
case cmpNumeric_MinEq_Equal:
if(m_crntEqualNum < argNumeric)
return NULL;
break;
case cmpLong_MinEq_Equal:
if(m_crntEqualLong < argLong)
return NULL;
break;
case setMinEqString:
m_crntMinimumText = argString;
m_hardCurMin = 0;
break;
case setMinEqNumeric:
m_crntMinimumNum = argNumeric;
m_hardCurMin = 0;
break;
case setMinEqLong:
m_crntMinimumLong = argLong;
m_hardCurMin = 0;
break;
case selMinEqString:
if(u8text::compareRtrim(m_crntMinimumText, argString) < 0)
{
m_crntMinimumText = argString;
m_hardCurMin = 0;
}
break;
case selMinEqNumeric:
if(m_crntMinimumNum < argNumeric)
{
m_crntMinimumNum = argNumeric;
m_hardCurMin = 0;
}
break;
case selMinEqLong:
if(m_crntMinimumLong < argLong)
{
m_crntMinimumLong = argLong;
m_hardCurMin = 0;
}
break;
// <
case cmpString_Max_Equal:
if(u8text::compareRtrim(m_crntEqualText, argString) >= 0)
return NULL;
break;
case cmpNumeric_Max_Equal:
if(m_crntEqualNum >= argNumeric)
return NULL;
break;
case cmpLong_Max_Equal:
if(m_crntEqualLong >= argLong)
return NULL;
break;
case cmpString_Max_Minimum:
if(u8text::compareRtrim(m_crntMinimumText, argString) >= 0)
return NULL;
break;
case cmpNumeric_Max_Minimum:
if(m_crntEqualNum >= argNumeric)
return NULL;
break;
case cmpLong_Max_Minimum:
if(m_crntMinimumLong >= argLong)
return NULL;
break;
case setMaxString:
m_crntMaximumText = argString;
m_hardCurMax = 1;
break;
case setMaxNumeric:
m_crntMaximumNum = argNumeric;
m_hardCurMax = 1;
break;
case setMaxLong:
m_crntMaximumLong = argLong;
m_hardCurMax = 1;
break;
case selMaxString:
if(u8text::compareRtrim(m_crntMaximumText, argString) >= 0)
{
m_crntMaximumText = argString;
m_hardCurMax = 1;
}
break;
case selMaxNumeric:
if(m_crntMaximumNum >= argNumeric)
{
m_crntMaximumNum = argNumeric;
m_hardCurMax = 1;
}
break;
case selMaxLong:
if(m_crntMaximumLong >= argLong)
{
m_crntMaximumLong = argLong;
m_hardCurMax = 1;
}
break;
// <=
case cmpString_MaxEq_Equal:
if(u8text::compareRtrim(m_crntEqualText, argString) > 0)
return NULL;
break;
case cmpNumeric_MaxEq_Equal:
if(m_crntEqualNum > argNumeric)
return NULL;
break;
case cmpLong_MaxEq_Equal:
if(m_crntEqualLong > argLong)
return NULL;
break;
case cmpString_MaxEq_Minimum:
res = u8text::compareRtrim(m_crntMinimumText, argString);
if(res > 0 || (res == 0 && m_hardCurMin != 0))
return NULL;
break;
case cmpNumeric_MaxEq_Minimum:
if(m_crntMinimumNum > argNumeric || (m_crntMinimumNum == argNumeric && m_hardCurMin != 0))
return NULL;
break;
case cmpLong_MaxEq_Minimum:
if(m_crntMinimumLong > argLong || (m_crntMinimumLong == argLong && m_hardCurMin != 0))
return NULL;
break;
case setMaxEqString:
m_crntMaximumText = argString;
m_hardCurMax = 0;
break;
case setMaxEqNumeric:
m_crntMaximumNum = argNumeric;
m_hardCurMax = 0;
break;
case setMaxEqLong:
m_crntMaximumLong = argLong;
m_hardCurMax = 0;
break;
case selMaxEqString:
if(u8text::compareRtrim(m_crntMaximumText, argString) > 0)
{
m_crntMaximumText = argString;
m_hardCurMax = 0;
}
break;
case selMaxEqNumeric:
if(m_crntMaximumNum > argNumeric)
{
m_crntMaximumNum = argNumeric;
m_hardCurMax = 0;
}
break;
case selMaxEqLong:
if(m_crntMaximumLong > argLong)
{
m_crntMaximumLong = argLong;
m_hardCurMax = 0;
}
break;
// .
case setFullKeyEqual:
if(2 == normalizeString<0>::set(m_crntEqualText, m_pIndexInfo->keySize() + 10))
return NULL;
m_fullKeyEq = m_crntEqualText;
break;
case setFullKeyMinimum:
res = normalizeString<1>::set(m_crntMinimumText, m_pIndexInfo->keySize() + 10);
if(2 == res)
return NULL;
m_fullKeyMin = m_crntMinimumText;
m_hardFKMin = 0 == res ? m_hardCurMin : 0;
break;
case setFullKeyMaximum:
res = normalizeString<2>::set(m_crntMaximumText, m_pIndexInfo->keySize() + 10);
m_fullKeyMax = m_crntMaximumText;
m_hardFKMax = 0 == res ? m_hardCurMax : 0;
break;
case setPartKeyEqualText:
res = normalizeString<0>::set(m_crntEqualText, m_pFields->info().length());
if(2 == res)
return NULL;
new tcTestField<toEqual, text_nocase_compare>(m_pPKEqConstr, m_crntEqualText, m_pFields->info());
m_partKeyEq += m_crntEqualText;
m_pFields++;
break;
case setPartKeyEqualNumeric:
res = normalizeNumeric<0, TRUE, TRUE>::set(m_crntEqualNum, m_pFields->info(), argString);
if(2 == res)
return NULL;
new tcTestField<toEqual, text_compare>(m_pPKEqConstr, argString, m_pFields->info());
m_partKeyEq += argString;
m_pFields++;
break;
case setPartKeyEqualLong:
res = normalizeLong<0, TRUE, TRUE>::set(m_crntEqualLong, m_pFields->info(), argString);
if(2 == res)
return NULL;
new tcTestField<toEqual, text_compare>(m_pPKEqConstr, argString, m_pFields->info());
m_partKeyEq += argString;
m_pFields++;
break;
case setPartKeyMinimumText:
res = normalizeString<1>::set(m_crntMinimumText, m_pFields->info().length());
if(2 == res)
return NULL;
m_partKeyMin = m_partKeyEq + m_crntMinimumText;
m_hardPKMin = 0 == res ? m_hardCurMin : 0;
if(m_hardPKMin)
new tcTestField<toGrat, text_nocase_compare>(m_pPKMinConstr, m_crntMinimumText, m_pFields->info());
else
new tcTestField<toGratEq, text_nocase_compare>(m_pPKMinConstr, m_crntMinimumText, m_pFields->info());
break;
case setPartKeyMinimumNumeric:
res = normalizeNumeric<1, FALSE, TRUE>::set(m_crntMinimumNum, m_pFields->info(), argString);
if(2 == res)
return NULL;
m_hardPKMin = 0 == res ? m_hardCurMin : 0;
if(m_hardPKMin)
new tcTestField<toGrat, text_compare>(m_pPKMinConstr, argString, m_pFields->info());
else
new tcTestField<toGratEq, text_compare>(m_pPKMinConstr, argString, m_pFields->info());
m_partKeyMin = m_partKeyEq + argString;
break;
case setPartKeyMinimumLong:
res = normalizeLong<1, FALSE, TRUE>::set(m_crntMinimumLong, m_pFields->info(), argString);
if(2 == res)
return NULL;
m_hardPKMin = 0 == res ? m_hardCurMin : 0;
if(m_hardPKMin)
new tcTestField<toGrat, text_compare>(m_pPKMinConstr, argString, m_pFields->info());
else
new tcTestField<toGratEq, text_compare>(m_pPKMinConstr, argString, m_pFields->info());
m_partKeyMin = m_partKeyEq + argString;
break;
case setPartKeyMaximumText:
res = normalizeString<2>::set(m_crntMaximumText, m_pFields->info().length());
m_partKeyMax = m_partKeyEq + m_crntMaximumText;
m_hardPKMax = 0 == res ? m_hardCurMax : 0;
if(m_hardPKMax)
new tcTestField<toLess, text_nocase_compare>(m_pPKMaxConstr, m_crntMaximumText, m_pFields->info());
else
new tcTestField<toLessEq, text_nocase_compare>(m_pPKMaxConstr, m_crntMaximumText, m_pFields->info());
break;
case setPartKeyMaximumNumeric:
res = normalizeNumeric<2, FALSE, TRUE>::set(m_crntMaximumNum, m_pFields->info(), argString);
if(2 == res)
return NULL;
m_hardPKMax = 0 == res ? m_hardCurMax : 0;
if(m_hardPKMax)
new tcTestField<toLess, text_compare>(m_pPKMaxConstr, argString, m_pFields->info());
else
new tcTestField<toLessEq, text_compare>(m_pPKMaxConstr, argString, m_pFields->info());
m_partKeyMax = m_partKeyEq + argString;
break;
case setPartKeyMaximumLong:
res = normalizeLong<2, FALSE, TRUE>::set(m_crntMaximumLong, m_pFields->info(), argString);
if(2 == res)
return NULL;
m_hardPKMax = 0 == res ? m_hardCurMax : 0;
if(m_hardPKMax)
new tcTestField<toLess, text_compare>(m_pPKMaxConstr, argString, m_pFields->info());
else
new tcTestField<toLessEq, text_compare>(m_pPKMaxConstr, argString, m_pFields->info());
m_partKeyMax = m_partKeyEq + argString;
break;
case storeRecNo:
m_recNo = m_crntEqualLong;
break;
case addTestEqualText:
if(!addTestText<0>::add(pByteCode, m_crntEqualText, m_table, 0, m_pAdditionalTests))
return NULL;
break;
case addTestEqualNumeric:
if(!addTestNumeric<0>::add(pByteCode, m_crntEqualNum, m_table, 0, m_pAdditionalTests))
return NULL;
break;
case addTestEqualLong:
if(!addTestLong<0>::add(pByteCode, m_crntEqualLong, m_table, 0, m_pAdditionalTests))
return NULL;
break;
case addTestEqualRecNo:
if(m_crntEqualLong <= 0 || m_crntEqualLong > 0x7FFFFFFF)
return NULL;
new tcRecNo<toEqual>(m_pAdditionalTests, m_crntEqualLong);
break;
case addTestMinimumText:
if(!addTestText<1>::add(pByteCode, m_crntMinimumText, m_table, m_hardCurMin, m_pAdditionalTests))
return NULL;
break;
case addTestMinimumNumeric:
if(!addTestNumeric<1>::add(pByteCode, m_crntMinimumNum, m_table, m_hardCurMin, m_pAdditionalTests))
return NULL;
break;
case addTestMinimumLong:
if(!addTestLong<1>::add(pByteCode, m_crntMinimumLong, m_table, m_hardCurMin, m_pAdditionalTests))
return NULL;
break;
case addTestMinimumRecNo:
if(m_crntMinimumLong > 0x7FFFFFFF)
return NULL;
if(m_crntMinimumLong > 0)
{
if(m_hardCurMin)
new tcRecNo<toGrat>(m_pAdditionalTests, m_crntMinimumLong);
else
new tcRecNo<toGratEq>(m_pAdditionalTests, m_crntMinimumLong);
}
break;
case addTestMaximumText:
if(!addTestText<2>::add(pByteCode, m_crntMaximumText, m_table, m_hardCurMax, m_pAdditionalTests))
return NULL;
break;
case addTestMaximumNumeric:
if(!addTestNumeric<2>::add(pByteCode, m_crntMaximumNum, m_table, m_hardCurMax, m_pAdditionalTests))
return NULL;
break;
case addTestMaximumLong:
if(!addTestNumeric<2>::add(pByteCode, m_crntMaximumLong, m_table, m_hardCurMax, m_pAdditionalTests))
return NULL;
break;
case addTestMaximumRecNo:
if(m_crntMaximumLong <= 0)
return NULL;
if(m_crntMaximumLong < 0x7FFFFFFF)
{
if(m_hardCurMax)
new tcRecNo<toLess>(m_pAdditionalTests, m_crntMaximumLong);
else
new tcRecNo<toLessEq>(m_pAdditionalTests, m_crntMaximumLong);
}
break;
////
//
case setMinKey_FKMin:
m_minKType = keyFKnEq;
break;
//
case setMinKey_FKEqual:
m_minKType = keyFKEq;
break;
//
case setMinKey_PKMin:
m_minKType = keyPKnEq;
break;
//
case setMinKey_PKEqual:
m_minKType = keyPKEq;
break;
//
case selMinKey_FKMin_PKMin:
res = compareFk_Pk(m_fullKeyMin, m_partKeyMin, m_hardPKMin ? 0xFF : 0x1);
m_minKType = res > 0 ? keyFKnEq : keyPKnEq;
break;
//
case selMinKey_FKMin_PKEqual:
res = compareFk_Pk(m_fullKeyMin, m_partKeyEq, 0x1);
m_minKType = res > 0 ? keyFKnEq : keyPKEq;
break;
//
case selMinKey_FKEqual_PKMin:
res = compareFk_Pk(m_fullKeyEq, m_partKeyMin, m_hardPKMin ? 0xFF : 0x1);
m_minKType = res > 0 ? keyFKEq : keyPKnEq;
break;
//
case selMinKey_FKEqual_PKEqual:
res = compareFk_Pk(m_fullKeyEq, m_partKeyEq, 0x1);
m_minKType = res > 0 ? keyFKEq : keyPKEq;
break;
//
case setMaxKey_FKMax:
m_maxKType = keyFKnEq;
break;
//
case setMaxKey_FKEqual:
m_maxKType = keyFKEq;
break;
//
case setMaxKey_PKMax:
m_maxKType = keyPKnEq;
break;
//
case setMaxKey_PKEqual:
m_maxKType = keyPKEq;
break;
//
case selMaxKey_FKMax_PKMax:
res = compareFk_Pk(m_fullKeyMax, m_partKeyMax, m_hardPKMax ? 0x1 : 0xFF);
m_maxKType = res > 0 ? keyPKnEq : keyFKnEq;
break;
//
case selMaxKey_FKMax_PKEqual:
res = compareFk_Pk(m_fullKeyMax, m_partKeyEq, 0xFF);
m_maxKType = res > 0 ? keyPKEq : keyFKnEq;
break;
//
case selMaxKey_FKEqual_PKMax:
res = compareFk_Pk(m_fullKeyEq, m_partKeyMax, m_hardPKMax ? 0x1 : 0xFF);
m_maxKType = res > 0 ? keyPKnEq : keyFKEq;
break;
//
case selMaxKey_FKEqual_PKEqual:
res = compareFk_Pk(m_fullKeyEq, m_partKeyEq, 0xFF);
m_maxKType = res > 0 ? keyPKEq : keyFKEq;
break;
// .
case cmpMinMaxKeys:
{
switch(m_minKType | (m_maxKType << 2))
{
// , .
case keyFKnEq | (keyPKEq << 2):
res = compareFk_Pk(m_fullKeyMin, m_partKeyEq, 0xFF);
if(res > 0 || (res == 0 && m_hardFKMin))
return NULL;
break;
// ,
case keyFKnEq | (keyPKnEq << 2):
res = compareFk_Pk(m_fullKeyMin, m_partKeyMax, m_hardPKMax ? 0x1 : 0xFF);
if(res > 0 || (res == 0 && (m_hardFKMin || m_hardPKMax)))
return NULL;
break;
// ,
case keyFKEq | (keyPKEq << 2):
res = compareFk_Pk(m_fullKeyEq, m_partKeyEq, 0xFF);
if(res > 0)
return NULL;
break;
// ,
case keyFKEq | (keyPKnEq << 2):
res = compareFk_Pk(m_fullKeyEq, m_partKeyMax, m_hardPKMax ? 0x1 : 0xFF);
if(res > 0 || (res == 0 && m_hardPKMax))
return NULL;
break;
// ,
case keyPKnEq | (keyFKEq << 2):
res = compareFk_Pk(m_fullKeyEq, m_partKeyMin, m_hardPKMin ? 0xFF : 0x1);
if(res < 0 || (res == 0 && m_hardPKMin))
return NULL;
break;
// ,
case keyPKnEq | (keyFKnEq << 2):
res = compareFk_Pk(m_fullKeyMax, m_partKeyMin, m_hardPKMin ? 0xFF : 0x1);
if(res < 0 || (res == 0 && (m_hardPKMin || m_hardFKMax)))
return NULL;
break;
// ,
case keyPKEq | (keyFKEq << 2):
res = compareFk_Pk(m_fullKeyEq, m_partKeyEq, 0x1);
if(res < 0)
return NULL;
break;
// ,
case keyPKEq | (keyFKnEq << 2):
res = compareFk_Pk(m_fullKeyMax, m_partKeyEq, 0x1);
if(res < 0 || (res == 0 && m_hardFKMax))
return NULL;
break;
}
}
break;
//
case gotoFirstToLast:
return GotoFirstToLast();
//
case gotoFirstToMax:
return GotoFirstToMax();
//
case gotoMinToLast:
return GotoMinToLast();
//
case gotoMinToMax:
return GotoMinToMax();
//
case gotoLastToFirst:
return GotoLastToFirst();
//
case gotoMaxToFirst:
return GotoMaxToFirst();
//
case gotoLastToMin:
return GotoLastToMin();
//
case gotoMaxToMin:
return GotoMaxToMin();
case gotoToRow:
return GotoToRow();
default:
return NULL;
}
}
}
void byteCodeToStr(BYTE* pStart, BYTE* pEnd, CString& res)
{
int len = pEnd - pStart;
int newLen = (len + 6) / 7 * 8 + 7;
LPSTR ptr = res.GetBufferSetLength(newLen);
wsprintf(ptr, "%6i ", len);
ptr += 7;
while(pStart < pEnd)
{
*ptr++ = ' ' + ( pStart[0] & 0x7F);
*ptr++ = ' ' + ((pStart[0]>>7) | ((pStart[1] & 0x3F)<<1));
*ptr++ = ' ' + ((pStart[1]>>6) | ((pStart[2] & 0x1F)<<2));
*ptr++ = ' ' + ((pStart[2]>>5) | ((pStart[3] & 0xF) <<3));
*ptr++ = ' ' + ((pStart[3]>>4) | ((pStart[4] & 0x7) <<4));
*ptr++ = ' ' + ((pStart[4]>>3) | ((pStart[5] & 0x3) <<5));
*ptr++ = ' ' + ((pStart[5]>>2) | ((pStart[6] & 0x1) <<6));
*ptr++ = ' ' + ( pStart[6]>>1);
pStart += 7;
}
res.GetBufferSetLength(newLen);
}
BYTE* strToByteCode(LPCSTR pStr)
{
int lenOfByteCode = atol(pStr), i = lenOfByteCode;
BYTE* pB = (BYTE*)pStr + 7;
BYTE *pBuf = new BYTE[(lenOfByteCode + 6) / 7 * 7], *ptr = pBuf;
while(i > 0)
{
*ptr++ = (pB[0] - 32) | ((pB[1] - 32) << 7);
*ptr++ = ((pB[1] - 32) >> 1) | ((pB[2] - 32) << 6);
*ptr++ = ((pB[2] - 32) >> 2) | ((pB[3] - 32) << 5);
*ptr++ = ((pB[3] - 32) >> 3) | ((pB[4] - 32) << 4);
*ptr++ = ((pB[4] - 32) >> 4) | ((pB[5] - 32) << 3);
*ptr++ = ((pB[5] - 32) >> 5) | ((pB[6] - 32) << 2);
*ptr++ = ((pB[6] - 32) >> 6) | ((pB[7] - 32) << 1);
pB += 8;
i-=7;
}
return pBuf;
}
template<OpCodes op1, OpCodes op2, OpCodes op3>
struct typed_op
{
static void set(int t, unsigned char*& pByteCode)
{
switch(t)
{
case field_info::ftText:
*pByteCode++ = op1;
break;
case field_info::ftNumeric:
*pByteCode++ = op2;
break;
case field_info::ftNumeric + 1:
*pByteCode++ = op3;
break;
}
}
};
inline static void writeFieldOp(unsigned char*& pByteCode, int& argNum, usage_ptr pUsg, int t, TestOp op, BOOL& bHasEq, BOOL& bHasMin, BOOL& bHasMax)
{
pUsg->argvIndex = argNum++;
pUsg->omit = TRUE;
typed_op<getArgText, getArgNumeric, getArgLong>::set(t, pByteCode);
switch(op)
{
case toEqual:
if(bHasEq)
typed_op<cmpStringEqual_Equal, cmpNumericEqual_Equal, cmpLongEqual_Equal>::set(t, pByteCode);
else
{
typed_op<setEqualString, setEqualNumeric, setEqualLong>::set(t, pByteCode);
bHasEq = TRUE;
}
break;
case toLess:
if(bHasEq)
typed_op<cmpString_Max_Equal, cmpNumeric_Max_Equal, cmpLong_Max_Equal>::set(t, pByteCode);
else
{
if(bHasMin)
typed_op<cmpString_Max_Minimum, cmpNumeric_Max_Minimum, cmpLong_Max_Minimum>::set(t, pByteCode);
if(bHasMax)
typed_op<selMaxString, selMaxNumeric, selMaxLong>::set(t, pByteCode);
else
{
typed_op<setMaxString, setMaxNumeric, setMaxLong>::set(t, pByteCode);
bHasMax = TRUE;
}
}
break;
case toLessEq:
if(bHasEq)
typed_op<cmpString_MaxEq_Equal, cmpNumeric_MaxEq_Equal, cmpLong_MaxEq_Equal>::set(t, pByteCode);
else
{
if(bHasMin)
typed_op<cmpString_MaxEq_Minimum, cmpNumeric_MaxEq_Minimum, cmpLong_MaxEq_Minimum>::set(t, pByteCode);
if(bHasMax)
typed_op<selMaxEqString, selMaxEqNumeric, selMaxEqLong>::set(t, pByteCode);
else
{
typed_op<setMaxEqString, setMaxEqNumeric, setMaxEqLong>::set(t, pByteCode);
bHasMax = TRUE;
}
}
break;
case toGrat:
if(bHasEq)
typed_op<cmpString_Min_Equal, cmpNumeric_Min_Equal, cmpLong_Min_Equal>::set(t, pByteCode);
else
{
if(bHasMin)
typed_op<selMinString, selMinNumeric, selMinLong>::set(t, pByteCode);
else
{
typed_op<setMinString, setMinNumeric, setMinLong>::set(t, pByteCode);
bHasMin = TRUE;
}
}
break;
case toGratEq:
if(bHasEq)
typed_op<cmpString_MinEq_Equal, cmpNumeric_MinEq_Equal, cmpLong_MinEq_Equal>::set(t, pByteCode);
else
{
if(bHasMin)
typed_op<selMinEqString, selMinEqNumeric, selMinEqLong>::set(t, pByteCode);
else
{
typed_op<setMinEqString, setMinEqNumeric, setMinEqLong>::set(t, pByteCode);
bHasMin = TRUE;
}
}
break;
}
}
void buildAdditionalTests(unsigned char*& pByteCode, sqlite3_index_info* pIdx, int argNum, const CVtabInfo& table)
{
struct node
{
int nField;
TestOp op;
usage_ptr pUsage;
node* next;
static void insert(node*& first, node* pNode)
{
node** ppNode = &first;
while(*ppNode)
{
if((*ppNode)->nField > pNode->nField)
break;
else if((*ppNode)->nField == pNode->nField && (*ppNode)->op >= pNode->op)
break;
ppNode = &(*ppNode)->next;
}
pNode->next = *ppNode;
*ppNode = pNode;
}
};
// .
node* nodes = NULL;
constraint_ptr pCtr = pIdx->aConstraint;
usage_ptr pUsg = pIdx->aConstraintUsage;
for(DWORD i = pIdx->nConstraint ; i--; pCtr++, pUsg++)
{
if(isConstraint(pCtr) && !pUsg->argvIndex)
{
node* pNode = new node;
pNode->nField = pCtr->iColumn;
pNode->pUsage = pUsg;
pNode->op = index_selector::sqlite2TestOp(pCtr->op);
node::insert(nodes, pNode);
}
}
node* pNode = nodes;
BOOL bHasMin = FALSE, bHasMax = FALSE, bHasEqual = FALSE;
while(pNode)
{
//
int t;
const one_field& field = table.field(pNode->nField);
if(field.isField())
{
t = table.phisInfo().field(field.pos()).type();
if(t == field_info::ftNumeric && table.phisInfo().field(field.pos()).precession() == 0)
t = field_info::ftNumeric + 1;
}
else if(field.isIndex() || field.isLnStr())
t = field_info::ftText;
else if(field.isRecNo())
t = field_info::ftNumeric + 1;
writeFieldOp(pByteCode, argNum, pNode->pUsage, t, pNode->op, bHasEqual, bHasMin, bHasMax);
if(!pNode->next || pNode->nField != pNode->next->nField)
{
if(bHasEqual)
{
if(field.isRecNo())
*pByteCode++ = addTestEqualRecNo;
else
{
typed_op<addTestEqualText, addTestEqualNumeric, addTestEqualLong>::set(t, pByteCode);
writeNum(pByteCode, pNode->nField);
}
bHasEqual = FALSE;
}
else
{
if(bHasMin)
{
if(field.isRecNo())
*pByteCode++ = addTestMinimumRecNo;
else
{
typed_op<addTestMinimumText, addTestMinimumNumeric, addTestMinimumLong>::set(t, pByteCode);
writeNum(pByteCode, pNode->nField);
}
bHasMin = FALSE;
}
if(bHasMax)
{
if(field.isRecNo())
*pByteCode++ = addTestMaximumRecNo;
else
{
typed_op<addTestMaximumText, addTestMaximumNumeric, addTestMaximumLong>::set(t, pByteCode);
writeNum(pByteCode, pNode->nField);
}
bHasMax = FALSE;
}
}
}
pNode = pNode->next;
}
while(nodes)
{
node* pDel = nodes;
nodes = nodes->next;
delete pDel;
}
}
static int buildIndexWork(unsigned char*& pByteCode, const CVtabInfo& table,
sqlite3_index_info* pIdx, const idx_node* pBest, BOOL& bHasMinKey, BOOL& bHasMaxKey)
{
int argNum = 1;
BOOL bHasFullEqual = FALSE, bHasFullMin = FALSE, bHasFullMax = FALSE;
BOOL bHasPartEqual = FALSE, bHasPartMin = FALSE, bHasPartMax = FALSE;
if(pBest->indexNum == -1) // recNo
{
for(op_node* pOp = pBest->fields->compares; pOp ; pOp = pOp->next)
writeFieldOp(pByteCode, argNum, pOp->pUsage, field_info::ftNumeric + 1, pOp->CompareOp, bHasFullEqual, bHasFullMin, bHasFullMax);
*pByteCode++ = storeRecNo;
return argNum;
}
DWORD posInIdx = 0;
const index_info* pIndexInfo = table.phisInfo().index(pBest->indexNum);
const idx_field_info* pFieldInfo = pIndexInfo->fields();
//
*pByteCode++ = selectIdx;
writeNum(pByteCode, pBest->indexNum);
for(idx_field_node* pField = pBest->fields; pField; pField = pField->next)
{
if(pField->posInIdx == -1) //
{
for(op_node* pOp = pField->compares ; pOp ; pOp = pOp->next)
writeFieldOp(pByteCode, argNum, pOp->pUsage, field_info::ftText, pOp->CompareOp, bHasFullEqual, bHasFullMin, bHasFullMax);
if(bHasFullEqual)
*pByteCode++ = setFullKeyEqual;
else
{
if(bHasFullMin)
*pByteCode++ = setFullKeyMinimum;
if(bHasFullMax)
*pByteCode++ = setFullKeyMaximum;
}
}
else
{
if(pField->posInIdx > posInIdx)
break;
int t = pFieldInfo->info().type();
if(t == field_info::ftNumeric && pFieldInfo->info().precession() == 0)
t = field_info::ftNumeric + 1;
bHasPartEqual = bHasPartMin = bHasPartMax = FALSE;
for(op_node* pOp = pField->compares; pOp; pOp = pOp->next)
writeFieldOp(pByteCode, argNum, pOp->pUsage, t, pOp->CompareOp, bHasPartEqual, bHasPartMin, bHasPartMax);
// .
if(bHasPartEqual)
{
typed_op<setPartKeyEqualText, setPartKeyEqualNumeric, setPartKeyEqualLong>::set(t, pByteCode);
// =, .
++pFieldInfo;
posInIdx++;
}
else
{
if(bHasPartMin)
typed_op<setPartKeyMinimumText, setPartKeyMinimumNumeric, setPartKeyMinimumLong>::set(t, pByteCode);
if(bHasPartMax)
typed_op<setPartKeyMaximumText, setPartKeyMaximumNumeric, setPartKeyMaximumLong>::set(t, pByteCode);
break;
}
}
}
// , / , .
BOOL bPossiblyChangeTypeOfBound = FALSE;
DWORD isPartEqual = bHasPartEqual || (!bHasPartMin && posInIdx > 0);
//
// 1 2 4 8
DWORD typeOfBound = bHasFullEqual | (bHasFullMin << 1) | (isPartEqual<< 2) | (bHasPartMin << 3);
bHasMinKey = TRUE;
switch(typeOfBound)
{
case 0 | 0: //
bHasMinKey = FALSE;
break;
case 0 | 4: // =
*pByteCode++ = setMinKey_PKEqual;
break;
case 0 | 8: // >
*pByteCode++ = setMinKey_PKMin;
break;
case 1 | 0: // =
*pByteCode++ = setMinKey_FKEqual;
break;
case 1 | 4: // = and =
*pByteCode++ = selMinKey_FKEqual_PKEqual;
bPossiblyChangeTypeOfBound = TRUE;
break;
case 1 | 8: // = and >
*pByteCode++ = selMinKey_FKEqual_PKMin;
bPossiblyChangeTypeOfBound = TRUE;
break;
case 2 | 0: // >
*pByteCode++ = setMinKey_FKMin;
break;
case 2 | 4: // > and =
*pByteCode++ = selMinKey_FKMin_PKEqual;
bPossiblyChangeTypeOfBound = TRUE;
break;
case 2 | 8: // > and >
*pByteCode++ = selMinKey_FKMin_PKMin;
bPossiblyChangeTypeOfBound = TRUE;
break;
}
//
isPartEqual = bHasPartEqual || (!bHasPartMax && posInIdx > 0);
typeOfBound = bHasFullEqual | (bHasFullMax << 1) | (isPartEqual << 2) | (bHasPartMax << 3);
bHasMaxKey = TRUE;
switch(typeOfBound)
{
case 0 | 0: //
bHasMaxKey = FALSE;
break;
case 0 | 4: // =
*pByteCode++ = setMaxKey_PKEqual;
break;
case 0 | 8: // <
*pByteCode++ = setMaxKey_PKMax;
break;
case 1 | 0: // =
*pByteCode++ = setMaxKey_FKEqual;
break;
case 1 | 4: // = and =
*pByteCode++ = selMaxKey_FKEqual_PKEqual;
bPossiblyChangeTypeOfBound = TRUE;
break;
case 1 | 8: // = and <
*pByteCode++ = selMaxKey_FKEqual_PKMax;
bPossiblyChangeTypeOfBound = TRUE;
break;
case 2 | 0: // <
*pByteCode++ = setMaxKey_FKMax;
break;
case 2 | 4: // < and =
*pByteCode++ = selMaxKey_FKMax_PKEqual;
bPossiblyChangeTypeOfBound = TRUE;
break;
case 2 | 8: // < and <
*pByteCode++ = selMaxKey_FKMax_PKMax;
bPossiblyChangeTypeOfBound = TRUE;
break;
}
if(bHasMinKey && bHasMaxKey && bPossiblyChangeTypeOfBound)
{
// ,
*pByteCode++ = cmpMinMaxKeys;
}
return argNum;
}
void FilterMachine::build(const CVtabInfo& table, sqlite3_index_info* pIdx, const idx_node* pBest)
{
// , 1
char testOpCodeSize[lastOpCode > 0xFF ? -1 : 1] = {0};
// , 64
// -. , .
BYTE *pStartByteCode = new BYTE[(pIdx->nConstraint + 10) * 64], *pByteCode = pStartByteCode;
BOOL bHasMinKey, bHasMaxKey;
int argNum;
if(pBest)
{
pIdx->idxNum = pBest->indexNum;
if(pBest->order != nooNa)
pIdx->orderByConsumed = TRUE;
argNum = buildIndexWork(pByteCode, table, pIdx, pBest, bHasMinKey, bHasMaxKey);
}
else
{
pIdx->idxNum = -1;
pIdx->orderByConsumed = FALSE;
argNum = 1;
}
// .,
buildAdditionalTests(pByteCode, pIdx, argNum, table);
if(pBest)
{
if(pBest->indexNum == -1)
*pByteCode++ = gotoToRow;
else
{
// .
BOOL bReverse = FALSE;
if(pBest->order == nooDesc) // , .
bReverse = TRUE;
else if(pBest->order == nooNa) // .
{
if(bHasMaxKey && !bHasMinKey) // , ,
bReverse = TRUE;
}
if(bReverse)
*pByteCode++ = bHasMaxKey ? (bHasMinKey ? gotoMaxToMin : gotoMaxToFirst) : (bHasMinKey ? gotoLastToMin : gotoLastToFirst);
else
*pByteCode++ = bHasMinKey ? (bHasMaxKey ? gotoMinToMax : gotoMinToLast) : (bHasMaxKey ? gotoFirstToMax : gotoFirstToLast);
}
}
else
{
// . .
*pByteCode++ = gotoFirstToLast;
}
*pByteCode++ = stop;
CString idxStr, byteCode;
byteCodeToStr(pStartByteCode, pByteCode, byteCode);
idxStr.Format("%s;%s", pBest ? (pBest->indexNum == -1 ? "recNo" : table.table()->index(pBest->indexNum)->szName) : "noIdx", byteCode);
int idxStrLen = idxStr.GetLength() + 1;
pIdx->idxStr = (char*)sqlite3_malloc(idxStrLen);
pIdx->needToFreeIdxStr = TRUE;
memcpy(pIdx->idxStr, (LPCSTR)idxStr, idxStrLen);
delete [] pStartByteCode;
}
select_iterator* FilterMachine::parseFilter(cursor_data& cursor, const CVtabInfo& table, const char* idxStr, sqlite3_value** args)
{
FilterMachine machine(table, cursor);
BYTE* pByteCode = strToByteCode(strchr(idxStr, ';') + 1);
select_iterator* it = machine.process(pByteCode, args);
delete [] pByteCode;
return it;
}
///////////////////////////////////////////////////////////////////////////////////////
//
// - ..
template<NavCtrl direction>
struct iterNoStopKeyNoTest : select_iterator
{
virtual BOOL doNext(cursor_data& cursor, const CVtabInfo& table)
{
return cursor.store.Goto(direction, 0);
}
};
// - ..
template<NavCtrl direction>
struct iterStopKeysNoTests : select_iterator
{
testConstraint* pUntil;
iterStopKeysNoTests(testConstraint*& pS) : pUntil(pS)
{
pS = NULL;
}
~iterStopKeysNoTests()
{
delete pUntil;
}
virtual BOOL doNext(cursor_data& cursor, const CVtabInfo& table)
{
if(!cursor.store.Goto(direction, 0))
return FALSE;
if(!testConstraint::doTest(pUntil, cursor))
return FALSE;
return TRUE;
}
};
// -, ..
template<NavCtrl direction>
struct iterNoStopKeysTests : select_iterator
{
testConstraint* pTests;
iterNoStopKeysTests(testConstraint*& pT) : pTests(pT)
{
pT = NULL;
}
~iterNoStopKeysTests()
{
delete pTests;
}
virtual BOOL doNext(cursor_data& cursor, const CVtabInfo& table)
{
for(;;)
{
if(!cursor.store.Goto(direction, 0))
return FALSE;
if(!testConstraint::doTest(pTests, cursor))
continue;
return TRUE;
}
}
};
// - .
template<NavCtrl direction>
struct iterStopKeysTests : select_iterator
{
testConstraint *pUntil, *pTest;
iterStopKeysTests(testConstraint*& pS, testConstraint*& pT) : pUntil(pS), pTest(pT)
{
pS = NULL;
pT = NULL;
}
~iterStopKeysTests()
{
delete pUntil;
delete pTest;
}
virtual BOOL doNext(cursor_data& cursor, const CVtabInfo& table)
{
for(;;)
{
if(!cursor.store.Goto(direction, 0))
return FALSE;
if(!testConstraint::doTest(pUntil, cursor))
return FALSE;
if(!testConstraint::doTest(pTest, cursor))
continue;
return TRUE;
}
}
};
//
class CUniKeyObj : public CKeyObj
{
public:
CUniKeyObj(CIndex* pI, const CString& key, const index_info* pIndexInfo)
: CKeyObj(pI, 0, 0), m_strKey(key), m_pIndexInfo(pIndexInfo)
{
}
virtual void PrepareKey()
{
LPCSTR ptr = m_strKey;
const idx_field_info* pIdxFieldInfo = m_pIndexInfo->fields();
for(DWORD i = m_pIndexInfo->fieldsCount(); i-- ; pIdxFieldInfo++)
{
int len = pIdxFieldInfo->info().length();
m_pStoreObj->FX_String(pIdxFieldInfo->numInTable(), (char*)ptr, len, 1);
ptr += len;
}
}
LPCSTR m_strKey;
const index_info* m_pIndexInfo;
};
//
template <TestOp op, BOOL bFullKey>
struct Key
{
static int Move(CStoreObj& store, const CString& keyOrig, const index_info* pIndexInfo)
{
// toEqual.
char test_buf[op == toEqual ? 0 : 1]={0};
int CountByteInIdx = pIndexInfo->keySize();
CompCtl direction;
CString key(keyOrig);
if(toLess == op)
direction = ccL;
else if(toLessEq == op)
direction = ccLE;
else if(toGrat == op)
direction = ccG;
else if(toGratEq == op)
direction = ccGE;
int len = key.GetLength();
if(bFullKey)
{
if(len > CountByteInIdx)
{
int row = atol((LPCSTR)key + CountByteInIdx);
key.GetBufferSetLength(CountByteInIdx);
if(row)
{
// .
// .
// , >"aaaaa|10", >"aaaaa",
// >="aaaaa", ,
// (=="aaaaa" <=10)
// <.
if(toGrat == op)
direction = ccGE;
else if(toLess == op)
direction = ccLE;
}
}
else if(len < CountByteInIdx)
memset(key.GetBufferSetLength(CountByteInIdx), ' ', CountByteInIdx - len);
}
else //
{
if(len < CountByteInIdx)
{
// .
// > <=, 0xFF
memset(key.GetBufferSetLength(CountByteInIdx) + len, toGrat == op || toLessEq == op ? 0xFF : ' ', CountByteInIdx - len);
}
}
// 1
//
// 1. 'a'
// 2. 'a'
// 3. 'd'
// <='a', 1C 1.
//
// ,
// 2. <='a' <'b'.
if(ccLE == direction)
{
if(2 == nextGratStringUpper(key, CountByteInIdx))
{
// , <=''
// ,
return store.Goto(navLast, 0);
}
else
direction = ccL;
}
CUniKeyObj idxKey(store.pIndex, key, pIndexInfo);
return store.Goto(&idxKey, direction, 0);
}
};
//
//
select_iterator* FilterMachine::GotoFirstToLast()
{
//
if(!m_cursor.store.Goto(navFirst, 0))
return NULL;
// . ,
if(!m_pAdditionalTests)
return new iterNoStopKeyNoTest<navNext>;
// , .
for(;;)
{
if(testConstraint::doTest(m_pAdditionalTests, m_cursor))
return new iterNoStopKeysTests<navNext>(m_pAdditionalTests);
if(!m_cursor.store.Goto(navNext, 0))
return NULL;
}
}
//
select_iterator* FilterMachine::GotoFirstToMax()
{
//
if(!m_cursor.store.Goto(navFirst, 0))
return NULL;
// , .
testConstraint* pUntil = NULL;
switch(m_maxKType)
{
//case keyPKEq: ,
// "",
// pUntil = m_pPartEqConstr;
// m_pPartEqConstr = NULL; //
// break;
case keyPKnEq:
testConstraint::append(pUntil, m_pPKMaxConstr, m_pPKEqConstr);
break;
//case keyFKEq:
// new tcFullKey(pUntil, toEqual, m_fullKeyEq, m_pIndexInfo);
case keyFKnEq:
if(m_hardFKMax)
new tcFullKey<toLess>(pUntil, m_fullKeyMax, m_pIndexInfo);
else
new tcFullKey<toLessEq>(pUntil, m_fullKeyMax, m_pIndexInfo);
break;
}
for(;;)
{
// , ?
if(!testConstraint::doTest(pUntil, m_cursor))
break;
// . ( , TRUE)
if(testConstraint::doTest(m_pAdditionalTests, m_cursor))
{
if(m_pAdditionalTests)
return new iterStopKeysTests<navNext>(pUntil, m_pAdditionalTests);
else
return new iterStopKeysNoTests<navNext>(pUntil);
}
// ..
if(!m_cursor.store.Goto(navNext, 0))
break;
}
delete pUntil;
return NULL;
}
//
select_iterator* FilterMachine::GotoMinToLast()
{
//
BOOL bMoved;
// , . , ,
// ""
testConstraint* pStartFullKey = NULL;
switch(m_minKType)
{
// ,
// , .
//case keyPKEq:
//case keyFKEq:
case keyPKnEq:
if(m_hardPKMin)
bMoved = Key<toGrat, FALSE>::Move(m_cursor.store, m_partKeyMin, m_pIndexInfo);
else
bMoved = Key<toGratEq, FALSE>::Move(m_cursor.store, m_partKeyMin, m_pIndexInfo);
break;
case keyFKnEq:
// , , .
// .
if(m_hardFKMin)
{
new tcFullKey<toGrat>(pStartFullKey, m_fullKeyMin, m_pIndexInfo);
bMoved = Key<toGrat, TRUE>::Move(m_cursor.store, m_fullKeyMin, m_pIndexInfo);
}
else
{
new tcFullKey<toGratEq>(pStartFullKey, m_fullKeyMin, m_pIndexInfo);
bMoved = Key<toGratEq, TRUE>::Move(m_cursor.store, m_fullKeyMin, m_pIndexInfo);
}
break;
//default:
// bMoved = FALSE;
}
if(pStartFullKey)
{
if(bMoved)
{
// ,
for(;;)
{
if(testConstraint::doTest(pStartFullKey, m_cursor))
break;
if(!m_cursor.store.Goto(navNext, 0))
{
bMoved = FALSE;
break;
}
}
}
delete pStartFullKey;
}
if(!bMoved)
return NULL;
if(!m_pAdditionalTests)
return new iterNoStopKeyNoTest<navNext>;
for(;;)
{
if(testConstraint::doTest(m_pAdditionalTests, m_cursor))
return new iterNoStopKeysTests<navNext>(m_pAdditionalTests);
if(!m_cursor.store.Goto(navNext, 0))
return NULL;
}
}
select_iterator* FilterMachine::GotoMinToMax()
{
BOOL bMoved;
testConstraint* pStartFullKey = NULL;
switch(m_minKType)
{
case keyPKEq:
bMoved = Key<toGratEq, FALSE>::Move(m_cursor.store, m_partKeyEq, m_pIndexInfo);
break;
case keyPKnEq:
if(m_hardPKMin)
bMoved = Key<toGrat, FALSE>::Move(m_cursor.store, m_partKeyMin, m_pIndexInfo);
else
bMoved = Key<toGratEq, FALSE>::Move(m_cursor.store, m_partKeyMin, m_pIndexInfo);
break;
case keyFKEq:
// , , .
// .
new tcFullKey<toGratEq>(pStartFullKey, m_fullKeyEq, m_pIndexInfo);
bMoved = Key<toGratEq, TRUE>::Move(m_cursor.store, m_fullKeyEq, m_pIndexInfo);
break;
case keyFKnEq:
// , , .
// .
if(m_hardFKMin)
{
new tcFullKey<toGrat>(pStartFullKey, m_fullKeyMin, m_pIndexInfo);
bMoved = Key<toGrat, TRUE>::Move(m_cursor.store, m_fullKeyMin, m_pIndexInfo);
}
else
{
new tcFullKey<toGratEq>(pStartFullKey, m_fullKeyMin, m_pIndexInfo);
bMoved = Key<toGratEq, TRUE>::Move(m_cursor.store, m_fullKeyMin, m_pIndexInfo);
}
break;
}
if(pStartFullKey)
{
if(bMoved)
{
// ,
for(;;)
{
if(testConstraint::doTest(pStartFullKey, m_cursor))
break;
if(!m_cursor.store.Goto(navNext, 0))
{
bMoved = FALSE;
break;
}
}
}
delete pStartFullKey;
}
if(!bMoved)
return NULL;
//
testConstraint* pUntil = NULL;
switch(m_maxKType)
{
case keyPKEq:
pUntil = m_pPKEqConstr;
m_pPKEqConstr = NULL; //
break;
case keyPKnEq:
testConstraint::append(pUntil, m_pPKMaxConstr, m_pPKEqConstr);
break;
case keyFKEq:
new tcFullKey<toEqual>(pUntil, m_fullKeyEq, m_pIndexInfo);
break;
case keyFKnEq:
if(m_hardFKMax)
new tcFullKey<toLess>(pUntil, m_fullKeyMax, m_pIndexInfo);
else
new tcFullKey<toLessEq>(pUntil, m_fullKeyMax, m_pIndexInfo);
break;
}
for(;;)
{
if(!testConstraint::doTest(pUntil, m_cursor))
break;
if(testConstraint::doTest(m_pAdditionalTests, m_cursor))
{
if(m_pAdditionalTests)
return new iterStopKeysTests<navNext>(pUntil, m_pAdditionalTests);
else
return new iterStopKeysNoTests<navNext>(pUntil);
}
if(!m_cursor.store.Goto(navNext, 0))
break;
}
delete pUntil;
return NULL;
}
select_iterator* FilterMachine::GotoLastToFirst()
{
if(!m_cursor.store.Goto(navLast, 0))
return NULL;
if(!m_pAdditionalTests)
return new iterNoStopKeyNoTest<navPrev>;
for(;;)
{
if(testConstraint::doTest(m_pAdditionalTests, m_cursor))
return new iterNoStopKeysTests<navPrev>(m_pAdditionalTests);
if(!m_cursor.store.Goto(navPrev, 0))
return NULL;
}
}
select_iterator* FilterMachine::GotoMaxToFirst()
{
BOOL bMoved;
testConstraint* pStartFullKey = NULL;
switch(m_maxKType)
{
// ,
// , .
//case keyPKEq:
//case keyFKEq:
case keyPKnEq:
if(m_hardPKMax)
bMoved = Key<toLess, FALSE>::Move(m_cursor.store, m_partKeyMax, m_pIndexInfo);
else
bMoved = Key<toLessEq, FALSE>::Move(m_cursor.store, m_partKeyMax, m_pIndexInfo);
break;
case keyFKnEq:
// , , .
// .
if(m_hardFKMax)
{
new tcFullKey<toLess>(pStartFullKey, m_fullKeyMax, m_pIndexInfo);
bMoved = Key<toLess, TRUE>::Move(m_cursor.store, m_fullKeyMax, m_pIndexInfo);
}
else
{
new tcFullKey<toLessEq>(pStartFullKey, m_fullKeyMax, m_pIndexInfo);
bMoved = Key<toLessEq, TRUE>::Move(m_cursor.store, m_fullKeyMax, m_pIndexInfo);
}
break;
}
if(pStartFullKey)
{
if(bMoved)
{
// ,
for(;;)
{
if(testConstraint::doTest(pStartFullKey, m_cursor))
break;
if(!m_cursor.store.Goto(navPrev, 0))
{
bMoved = FALSE;
break;;
}
}
}
delete pStartFullKey;
}
if(!bMoved)
return NULL;
if(!m_pAdditionalTests)
return new iterNoStopKeyNoTest<navPrev>;
for(;;)
{
if(testConstraint::doTest(m_pAdditionalTests, m_cursor))
return new iterNoStopKeysTests<navPrev>(m_pAdditionalTests);
if(!m_cursor.store.Goto(navPrev, 0))
return NULL;
}
}
select_iterator* FilterMachine::GotoLastToMin()
{
if(!m_cursor.store.Goto(navLast, 0))
return NULL;
testConstraint* pUntil = NULL;
switch(m_minKType)
{
case keyPKEq:
pUntil = m_pPKEqConstr;
m_pPKEqConstr = NULL; //
break;
case keyPKnEq:
testConstraint::append(pUntil, m_pPKMinConstr, m_pPKEqConstr);
break;
case keyFKEq:
new tcFullKey<toEqual>(pUntil, m_fullKeyEq, m_pIndexInfo);
break;
case keyFKnEq:
if(m_hardFKMin)
new tcFullKey<toGrat>(pUntil, m_fullKeyMin, m_pIndexInfo);
else
new tcFullKey<toGratEq>(pUntil, m_fullKeyMin, m_pIndexInfo);
break;
}
for(;;)
{
if(!testConstraint::doTest(pUntil, m_cursor))
break;
if(testConstraint::doTest(m_pAdditionalTests, m_cursor))
{
if(m_pAdditionalTests)
return new iterStopKeysTests<navPrev>(pUntil, m_pAdditionalTests);
else
return new iterStopKeysNoTests<navPrev>(pUntil);
}
if(!m_cursor.store.Goto(navPrev, 0))
break;
}
delete pUntil;
return NULL;
}
select_iterator* FilterMachine::GotoMaxToMin()
{
BOOL bMoved;
testConstraint* pStartFullKey = NULL;
switch(m_maxKType)
{
case keyPKEq:
bMoved = Key<toLessEq, FALSE>::Move(m_cursor.store, m_partKeyEq, m_pIndexInfo);
break;
case keyPKnEq:
if(m_hardPKMax)
bMoved = Key<toLess, FALSE>::Move(m_cursor.store, m_partKeyMax, m_pIndexInfo);
else
bMoved = Key<toLessEq, FALSE>::Move(m_cursor.store, m_partKeyMax, m_pIndexInfo);
break;
case keyFKEq:
// , , .
// .
new tcFullKey<toLessEq>(pStartFullKey, m_fullKeyEq, m_pIndexInfo);
bMoved = Key<toLessEq, TRUE>::Move(m_cursor.store, m_fullKeyEq, m_pIndexInfo);
break;
case keyFKnEq:
// , , .
// .
if(m_hardFKMax)
{
new tcFullKey<toLess>(pStartFullKey, m_fullKeyMax, m_pIndexInfo);
bMoved = Key<toLess, TRUE>::Move(m_cursor.store, m_fullKeyMax, m_pIndexInfo);
}
else
{
new tcFullKey<toLessEq>(pStartFullKey, m_fullKeyMax, m_pIndexInfo);
bMoved = Key<toLessEq, TRUE>::Move(m_cursor.store, m_fullKeyMax, m_pIndexInfo);
}
break;
}
if(pStartFullKey)
{
if(bMoved)
{
// ,
for(;;)
{
if(testConstraint::doTest(pStartFullKey, m_cursor))
break;
if(!m_cursor.store.Goto(navPrev, 0))
{
bMoved = FALSE;
break;
}
}
}
delete pStartFullKey;
}
if(!bMoved)
return NULL;
//
testConstraint* pUntil = NULL;
switch(m_minKType)
{
case keyPKEq:
pUntil = m_pPKEqConstr;
m_pPKEqConstr = NULL; //
break;
case keyPKnEq:
testConstraint::append(pUntil, m_pPKMinConstr, m_pPKEqConstr);
break;
case keyFKEq:
new tcFullKey<toEqual>(pUntil, m_fullKeyEq, m_pIndexInfo);
break;
case keyFKnEq:
if(m_hardFKMin)
new tcFullKey<toGrat>(pUntil, m_fullKeyMin, m_pIndexInfo);
else
new tcFullKey<toGratEq>(pUntil, m_fullKeyMin, m_pIndexInfo);
break;
}
for(;;)
{
if(!testConstraint::doTest(pUntil, m_cursor))
break;
if(testConstraint::doTest(m_pAdditionalTests, m_cursor))
{
if(m_pAdditionalTests)
return new iterStopKeysTests<navPrev>(pUntil, m_pAdditionalTests);
else
return new iterStopKeysNoTests<navPrev>(pUntil);
}
if(!m_cursor.store.Goto(navPrev, 0))
break;
}
delete pUntil;
return NULL;
}
select_iterator* FilterMachine::GotoToRow()
{
char recno[sizeof(CRecAddr)];
CRecAddr* pAddr = (CRecAddr*)recno;
pAddr->m_pos = m_recNo;
pAddr->data = 0;
if(!m_cursor.store.Goto(pAddr, 0))
return NULL;
if(!testConstraint::doTest(m_pAdditionalTests, m_cursor))
return NULL;
struct iterRow : select_iterator
{
virtual BOOL doNext(cursor_data&, const CVtabInfo&)
{
return FALSE;
}
};
return new iterRow;
}