You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1509 lines
50 KiB
1509 lines
50 KiB
//--------------------------------------------------------------------------
|
|
// Query.cpp
|
|
//--------------------------------------------------------------------------
|
|
#include "pch.hxx"
|
|
#include "database.h"
|
|
#include "query.h"
|
|
#include "shlwapi.h"
|
|
#include "strconst.h"
|
|
|
|
//--------------------------------------------------------------------------
|
|
// OPERATORTYPE
|
|
//--------------------------------------------------------------------------
|
|
typedef enum tagOPERATORTYPE {
|
|
OPERATOR_LEFTPAREN,
|
|
OPERATOR_RIGHTPAREN,
|
|
OPERATOR_EQUAL,
|
|
OPERATOR_NOTEQUAL,
|
|
OPERATOR_LESSTHANEQUAL,
|
|
OPERATOR_LESSTHAN,
|
|
OPERATOR_GREATERTHANEQUAL,
|
|
OPERATOR_GREATERTHAN,
|
|
OPERATOR_AND,
|
|
OPERATOR_BITWISEAND,
|
|
OPERATOR_OR,
|
|
OPERATOR_BITWISEOR,
|
|
OPERATOR_STRSTRI,
|
|
OPERATOR_STRSTR,
|
|
OPERATOR_LSTRCMPI,
|
|
OPERATOR_LSTRCMP,
|
|
OPERATOR_ADD,
|
|
OPERATOR_SUBTRACT,
|
|
OPERATOR_MULTIPLY,
|
|
OPERATOR_DIVIDE,
|
|
OPERATOR_MOD,
|
|
OPERATOR_LAST
|
|
} OPERATORTYPE;
|
|
|
|
//--------------------------------------------------------------------------
|
|
// TOKENTYPE
|
|
//--------------------------------------------------------------------------
|
|
typedef enum tagTOKENTYPE {
|
|
TOKEN_INVALID,
|
|
TOKEN_OPERATOR,
|
|
TOKEN_OPERAND
|
|
} TOKENTYPE;
|
|
|
|
//--------------------------------------------------------------------------
|
|
// OPERANDTYPE
|
|
//--------------------------------------------------------------------------
|
|
typedef enum tagOPERANDTYPE {
|
|
OPERAND_INVALID,
|
|
OPERAND_COLUMN,
|
|
OPERAND_STRING,
|
|
OPERAND_DWORD,
|
|
OPERAND_METHOD,
|
|
OPERAND_LAST
|
|
} OPERANDTYPE;
|
|
|
|
//--------------------------------------------------------------------------
|
|
// OPERANDINFO
|
|
//--------------------------------------------------------------------------
|
|
typedef struct tagOPERANDINFO {
|
|
OPERANDTYPE tyOperand;
|
|
DWORD iSymbol;
|
|
LPVOID pRelease;
|
|
union {
|
|
COLUMNORDINAL iColumn; // OPERAND_COLUMN
|
|
LPSTR pszString; // OPERAND_STRING
|
|
DWORD dwValue; // OPERAND_DWORD
|
|
METHODID idMethod; // OPERAND_METHOD
|
|
};
|
|
DWORD dwReserved;
|
|
} OPERANDINFO, *LPOPERANDINFO;
|
|
|
|
//--------------------------------------------------------------------------
|
|
// QUERYTOKEN
|
|
//--------------------------------------------------------------------------
|
|
typedef struct tagQUERYTOKEN *LPQUERYTOKEN;
|
|
typedef struct tagQUERYTOKEN {
|
|
TOKENTYPE tyToken;
|
|
DWORD cRefs;
|
|
union {
|
|
OPERATORTYPE tyOperator; // TOKEN_OPERATOR
|
|
OPERANDINFO Operand; // TOKEN_OPERAND
|
|
};
|
|
LPQUERYTOKEN pNext;
|
|
LPQUERYTOKEN pPrevious;
|
|
} QUERYTOKEN;
|
|
|
|
//--------------------------------------------------------------------------
|
|
// PFNCOMPAREOPERAND - Compares Two Operands of the same type
|
|
//--------------------------------------------------------------------------
|
|
typedef INT (APIENTRY *PFNCOMPAREOPERAND)(LPVOID pDataLeft, LPVOID pDataRight);
|
|
#define PCOMPARE(_pfn) ((PFNCOMPAREOPERAND)_pfn)
|
|
|
|
//--------------------------------------------------------------------------
|
|
// CompareOperandString
|
|
//--------------------------------------------------------------------------
|
|
INT CompareOperandString(LPVOID pDataLeft, LPVOID pDataRight) {
|
|
return(lstrcmpi((LPSTR)pDataLeft, (LPSTR)pDataRight));
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// CompareOperandDword
|
|
//--------------------------------------------------------------------------
|
|
INT CompareOperandDword(LPVOID pDataLeft, LPVOID pDataRight) {
|
|
return (INT)(*((DWORD *)pDataLeft) - *((DWORD *)pDataRight));
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// g_rgpfnCompareOperand
|
|
//--------------------------------------------------------------------------
|
|
const static PFNCOMPAREOPERAND g_rgpfnCompareOperand[OPERAND_LAST] = {
|
|
NULL, // OPERAND_INVALID
|
|
NULL, // OPERAND_COLUMN
|
|
PCOMPARE(CompareOperandString), // OPERAND_STRING
|
|
PCOMPARE(CompareOperandDword) // OPERAND_DWORD
|
|
};
|
|
|
|
//--------------------------------------------------------------------------
|
|
// CompareOperands
|
|
//--------------------------------------------------------------------------
|
|
#define CompareOperands(_tyOperand, _pDataLeft, _pDataRight) \
|
|
(*(g_rgpfnCompareOperand[_tyOperand]))(_pDataLeft, _pDataRight)
|
|
|
|
//--------------------------------------------------------------------------
|
|
// PFNEVALUATEOPERATOR - Compares data in two flat records.
|
|
//--------------------------------------------------------------------------
|
|
typedef DWORD (APIENTRY *PFNEVALUATEOPERATOR)(OPERANDTYPE tyOperand,
|
|
LPVOID pDataLeft, LPVOID pDataRight);
|
|
#define PEVAL(_pfn) ((PFNEVALUATEOPERATOR)_pfn)
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Evaluate Operator Prototypes
|
|
//--------------------------------------------------------------------------
|
|
DWORD EvaluateEqual(OPERANDTYPE tyOperand, LPVOID pDataLeft, LPVOID pDataRight);
|
|
DWORD EvaluateNotEqual(OPERANDTYPE tyOperand, LPVOID pDataLeft, LPVOID pDataRight);
|
|
DWORD EvaluateLessThanEqual(OPERANDTYPE tyOperand, LPVOID pDataLeft, LPVOID pDataRight);
|
|
DWORD EvaluateLessThan(OPERANDTYPE tyOperand, LPVOID pDataLeft, LPVOID pDataRight);
|
|
DWORD EvaluateGreaterThanEqual(OPERANDTYPE tyOperand, LPVOID pDataLeft, LPVOID pDataRight);
|
|
DWORD EvaluateGreaterThan(OPERANDTYPE tyOperand, LPVOID pDataLeft, LPVOID pDataRight);
|
|
DWORD EvaluateAnd(OPERANDTYPE tyOperand, LPVOID pDataLeft, LPVOID pDataRight);
|
|
DWORD EvaluateBitwiseAnd(OPERANDTYPE tyOperand, LPVOID pDataLeft, LPVOID pDataRight);
|
|
DWORD EvaluateOr(OPERANDTYPE tyOperand, LPVOID pDataLeft, LPVOID pDataRight);
|
|
DWORD EvaluateBitwiseOr(OPERANDTYPE tyOperand, LPVOID pDataLeft, LPVOID pDataRight);
|
|
DWORD EvaluateStrStrI(OPERANDTYPE tyOperand, LPVOID pDataLeft, LPVOID pDataRight);
|
|
DWORD EvaluateStrStr(OPERANDTYPE tyOperand, LPVOID pDataLeft, LPVOID pDataRight);
|
|
DWORD EvaluateStrcmpi(OPERANDTYPE tyOperand, LPVOID pDataLeft, LPVOID pDataRight);
|
|
DWORD EvaluateStrcmp(OPERANDTYPE tyOperand, LPVOID pDataLeft, LPVOID pDataRight);
|
|
DWORD EvaluateAdd(OPERANDTYPE tyOperand, LPVOID pDataLeft, LPVOID pDataRight);
|
|
DWORD EvaluateSubtract(OPERANDTYPE tyOperand, LPVOID pDataLeft, LPVOID pDataRight);
|
|
DWORD EvaluateMultiply(OPERANDTYPE tyOperand, LPVOID pDataLeft, LPVOID pDataRight);
|
|
DWORD EvaluateDivide(OPERANDTYPE tyOperand, LPVOID pDataLeft, LPVOID pDataRight);
|
|
DWORD EvaluateModula(OPERANDTYPE tyOperand, LPVOID pDataLeft, LPVOID pDataRight);
|
|
|
|
//--------------------------------------------------------------------------
|
|
// OPERATORINFO
|
|
//--------------------------------------------------------------------------
|
|
typedef struct tagOPERATORINFO {
|
|
LPCSTR pszName;
|
|
BYTE bISP;
|
|
BYTE bICP;
|
|
PFNEVALUATEOPERATOR pfnEvaluate;
|
|
} OPERATORINFO, *LPOPERATORINFO;
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Operator Precedence Table
|
|
//--------------------------------------------------------------------------
|
|
static const OPERATORINFO g_rgOperator[OPERATOR_LAST] = {
|
|
// Name ISP ICP Function
|
|
{ "(", 6, 0, NULL }, // OPERATOR_LEFTPAREN
|
|
{ ")", 1, 1, NULL }, // OPERATOR_RIGHTPAREN
|
|
{ "==", 5, 5, PEVAL(EvaluateEqual) }, // OPERATOR_EQUAL
|
|
{ "!=", 5, 5, PEVAL(EvaluateNotEqual) }, // OPERATOR_NOTEQUAL
|
|
{ "<=", 5, 5, PEVAL(EvaluateLessThanEqual) }, // OPERATOR_LESSTHANEQUAL
|
|
{ "<", 5, 5, PEVAL(EvaluateLessThan) }, // OPERATOR_LESSTHAN
|
|
{ ">=", 5, 5, PEVAL(EvaluateGreaterThanEqual) }, // OPERATOR_GREATERTHANEQUAL
|
|
{ ">", 5, 5, PEVAL(EvaluateGreaterThan) }, // OPERATOR_GREATERTHAN
|
|
{ "&&", 4, 4, PEVAL(EvaluateAnd) }, // OPERATOR_AND
|
|
{ "&", 3, 3, PEVAL(EvaluateBitwiseAnd) }, // OPERATOR_BITWISEAND
|
|
{ "||", 4, 4, PEVAL(EvaluateOr) }, // OPERATOR_OR
|
|
{ "|", 3, 3, PEVAL(EvaluateBitwiseOr) }, // OPERATOR_BITWISEOR
|
|
{ "containsi", 5, 5, PEVAL(EvaluateStrStrI) }, // OPERATOR_STRSTRI
|
|
{ "contains", 5, 5, PEVAL(EvaluateStrStr) }, // OPERATOR_STRSTR
|
|
{ "comparei", 5, 5, PEVAL(EvaluateStrcmpi) }, // OPERATOR_LSTRCMPI
|
|
{ "compare", 5, 5, PEVAL(EvaluateStrcmp) }, // OPERATOR_LSTRCMP
|
|
{ "+", 4, 4, PEVAL(EvaluateAdd) }, // OPERATOR_ADD,
|
|
{ "-", 4, 4, PEVAL(EvaluateSubtract) }, // OPERATOR_SUBTRACT,
|
|
{ "*", 3, 3, PEVAL(EvaluateMultiply) }, // OPERATOR_MULTIPLY,
|
|
{ "/", 3, 3, PEVAL(EvaluateDivide) }, // OPERATOR_DIVIDE,
|
|
{ "%", 3, 3, PEVAL(EvaluateModula) }, // OPERATOR_MOD,
|
|
};
|
|
|
|
//--------------------------------------------------------------------------
|
|
// EvaluateOperator
|
|
//--------------------------------------------------------------------------
|
|
#define EvaluateOperator(_tyOperator, _tyOperand, _pDataLeft, _pDataRight) \
|
|
(*(g_rgOperator[_tyOperator].pfnEvaluate))(_tyOperand, _pDataLeft, _pDataRight)
|
|
|
|
//--------------------------------------------------------------------------
|
|
// MAPCOLUMNTYPE
|
|
//--------------------------------------------------------------------------
|
|
typedef void (APIENTRY *PFNMAPCOLUMNTYPE)(LPOPERANDINFO pOperand,
|
|
LPCTABLECOLUMN pColumn, LPVOID pBinding, LPVOID *ppValue);
|
|
#define PMAP(_pfn) ((PFNMAPCOLUMNTYPE)_pfn)
|
|
|
|
//--------------------------------------------------------------------------
|
|
// MapColumnString
|
|
//--------------------------------------------------------------------------
|
|
void MapColumnString(LPOPERANDINFO pOperand, LPCTABLECOLUMN pColumn,
|
|
LPVOID pBinding, LPVOID *ppValue) {
|
|
(*ppValue) = *((LPSTR *)((LPBYTE)pBinding + pColumn->ofBinding));
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// MapColumnByte
|
|
//--------------------------------------------------------------------------
|
|
void MapColumnByte(LPOPERANDINFO pOperand, LPCTABLECOLUMN pColumn,
|
|
LPVOID pBinding, LPVOID *ppValue) {
|
|
pOperand->dwReserved = *((BYTE *)((LPBYTE)pBinding + pColumn->ofBinding));
|
|
(*ppValue) = (LPVOID)&pOperand->dwReserved;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// MapColumnDword
|
|
//--------------------------------------------------------------------------
|
|
void MapColumnDword(LPOPERANDINFO pOperand, LPCTABLECOLUMN pColumn,
|
|
LPVOID pBinding, LPVOID *ppValue) {
|
|
pOperand->dwReserved = *((DWORD *)((LPBYTE)pBinding + pColumn->ofBinding));
|
|
(*ppValue) = (LPVOID)&pOperand->dwReserved;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// MapColumnWord
|
|
//--------------------------------------------------------------------------
|
|
void MapColumnWord(LPOPERANDINFO pOperand, LPCTABLECOLUMN pColumn,
|
|
LPVOID pBinding, LPVOID *ppValue) {
|
|
pOperand->dwReserved = *((WORD *)((LPBYTE)pBinding + pColumn->ofBinding));
|
|
(*ppValue) = (LPVOID)&pOperand->dwReserved;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// COLUMNTYPEINFO
|
|
//--------------------------------------------------------------------------
|
|
typedef struct tagCOLUMNTYPEINFO {
|
|
OPERANDTYPE tyOperand;
|
|
PFNMAPCOLUMNTYPE pfnMapColumnType;
|
|
} COLUMNTYPEINFO, *LPCOLUMNTYPEINFO;
|
|
|
|
//--------------------------------------------------------------------------
|
|
// g_rgColumnTypeInfo
|
|
//--------------------------------------------------------------------------
|
|
static const COLUMNTYPEINFO g_rgColumnTypeInfo[CDT_LASTTYPE] = {
|
|
{ OPERAND_INVALID, NULL }, // CDT_FILETIME,
|
|
{ OPERAND_STRING, PMAP(MapColumnString) }, // CDT_FIXSTRA,
|
|
{ OPERAND_STRING, PMAP(MapColumnString) }, // CDT_VARSTRA,
|
|
{ OPERAND_DWORD, PMAP(MapColumnByte) }, // CDT_BYTE,
|
|
{ OPERAND_DWORD, PMAP(MapColumnDword) }, // CDT_DWORD,
|
|
{ OPERAND_DWORD, PMAP(MapColumnWord) }, // CDT_WORD,
|
|
{ OPERAND_DWORD, PMAP(MapColumnDword) }, // CDT_STREAM,
|
|
{ OPERAND_INVALID, NULL }, // CDT_VARBLOB,
|
|
{ OPERAND_INVALID, NULL }, // CDT_FIXBLOB,
|
|
{ OPERAND_DWORD, PMAP(MapColumnDword) }, // CDT_FLAGS,
|
|
{ OPERAND_DWORD, PMAP(MapColumnDword) }, // CDT_UNIQUE
|
|
};
|
|
|
|
//--------------------------------------------------------------------------
|
|
// MapColumnType
|
|
//--------------------------------------------------------------------------
|
|
#define MapColumnType(_tyColumn, _pOperand, _pColumn, _pBinding, _ppValue) \
|
|
(*(g_rgColumnTypeInfo[_tyColumn].pfnMapColumnType))(_pOperand, _pColumn, _pBinding, _ppValue)
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Prototypes
|
|
//--------------------------------------------------------------------------
|
|
HRESULT GetNextQueryToken(LPSTR *ppszT, LPCTABLESCHEMA pSchema, LPQUERYTOKEN *ppToken, CDatabase *pDB);
|
|
HRESULT LinkToken(LPQUERYTOKEN pToken, LPQUERYTOKEN *ppHead, LPQUERYTOKEN *ppTail);
|
|
HRESULT ReleaseTokenList(BOOL fReverse, LPQUERYTOKEN *ppHead, CDatabase *pDB);
|
|
HRESULT ReleaseToken(LPQUERYTOKEN *ppToken, CDatabase *pDB);
|
|
HRESULT ParseStringLiteral(LPCSTR pszStart, LPOPERANDINFO pOperand, LPSTR *ppszEnd, CDatabase *pDB);
|
|
HRESULT ParseNumeric(LPCSTR pszT, LPOPERANDINFO pOperand, LPSTR *ppszEnd);
|
|
HRESULT ParseSymbol(LPCSTR pszT, LPCTABLESCHEMA pSchema, LPOPERANDINFO pOperand, LPSTR *ppszEnd, CDatabase *pDB);
|
|
HRESULT PushStackToken(LPQUERYTOKEN pToken, LPQUERYTOKEN *ppStackTop);
|
|
HRESULT PopStackToken(LPQUERYTOKEN *ppToken, LPQUERYTOKEN *ppStackTop);
|
|
HRESULT EvaluateClause(OPERATORTYPE tyOperator, LPVOID pBinding, LPCTABLESCHEMA pSchema, LPQUERYTOKEN *ppStackTop, CDatabase *pDB, IDatabaseExtension *pExtension);
|
|
IF_DEBUG(HRESULT DebugDumpExpression(LPCSTR pszQuery, LPCTABLESCHEMA pSchema, LPQUERYTOKEN pPostfixHead));
|
|
|
|
//--------------------------------------------------------------------------
|
|
// ISP inline
|
|
//--------------------------------------------------------------------------
|
|
inline BYTE ISP(LPQUERYTOKEN pToken)
|
|
{
|
|
// Validate
|
|
Assert(TOKEN_OPERATOR == pToken->tyToken && pToken->tyOperator < OPERATOR_LAST);
|
|
|
|
// Return ISP
|
|
return (g_rgOperator[pToken->tyOperator].bISP);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// ICP inline
|
|
//--------------------------------------------------------------------------
|
|
inline BYTE ICP(LPQUERYTOKEN pToken)
|
|
{
|
|
// Validate
|
|
Assert(TOKEN_OPERATOR == pToken->tyToken && pToken->tyOperator < OPERATOR_LAST);
|
|
|
|
// Return ISP
|
|
return (g_rgOperator[pToken->tyOperator].bICP);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// DBIsDigit
|
|
// --------------------------------------------------------------------------
|
|
int DBIsDigit(LPSTR psz)
|
|
{
|
|
WORD wType;
|
|
if (IsDBCSLeadByte(*psz))
|
|
SideAssert(GetStringTypeEx(LOCALE_USER_DEFAULT, CT_CTYPE1, psz, 2, &wType));
|
|
else
|
|
SideAssert(GetStringTypeEx(LOCALE_USER_DEFAULT, CT_CTYPE1, psz, 1, &wType));
|
|
return(wType & C1_DIGIT);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// EvaluateQuery
|
|
//--------------------------------------------------------------------------
|
|
HRESULT EvaluateQuery(HQUERY hQuery, LPVOID pBinding, LPCTABLESCHEMA pSchema,
|
|
CDatabase *pDB, IDatabaseExtension *pExtension)
|
|
{
|
|
// Locals
|
|
HRESULT hr=S_OK;
|
|
LPQUERYTOKEN pToken;
|
|
LPQUERYTOKEN pResult=NULL;
|
|
LPQUERYTOKEN pStackTop=NULL;
|
|
|
|
// Trace
|
|
TraceCall("EvaluateQuery");
|
|
|
|
// Assert
|
|
Assert(hQuery && pBinding && pSchema);
|
|
|
|
// Walk through the tokens
|
|
for (pToken=(LPQUERYTOKEN)hQuery; pToken!=NULL; pToken=pToken->pNext)
|
|
{
|
|
// If this is an operand, append to the stack
|
|
if (TOKEN_OPERAND == pToken->tyToken)
|
|
{
|
|
// LinkStackToken
|
|
PushStackToken(pToken, &pStackTop);
|
|
}
|
|
|
|
// Otherwise, must be an operator
|
|
else
|
|
{
|
|
// Operator ?
|
|
Assert(TOKEN_OPERATOR == pToken->tyToken && g_rgOperator[pToken->tyOperator].pfnEvaluate != NULL);
|
|
|
|
// EvaluateOperator
|
|
IF_FAILEXIT(hr = EvaluateClause(pToken->tyOperator, pBinding, pSchema, &pStackTop, pDB, pExtension));
|
|
}
|
|
}
|
|
|
|
// Pop the stack
|
|
PopStackToken(&pResult, &pStackTop);
|
|
|
|
// No Token and Stack should now be empty..
|
|
Assert(pResult && NULL == pStackTop && pResult->tyToken == TOKEN_OPERAND && pResult->Operand.tyOperand == OPERAND_DWORD);
|
|
|
|
// 0 or not zero
|
|
hr = (pResult->Operand.dwValue == 0) ? S_FALSE : S_OK;
|
|
|
|
exit:
|
|
// Cleanup
|
|
ReleaseToken(&pResult, pDB);
|
|
ReleaseTokenList(TRUE, &pStackTop, pDB);
|
|
|
|
// Done
|
|
return(SUCCEEDED(hr) ? hr : S_FALSE);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// ParseQuery
|
|
//--------------------------------------------------------------------------
|
|
HRESULT ParseQuery(LPCSTR pszQuery, LPCTABLESCHEMA pSchema, LPHQUERY phQuery,
|
|
CDatabase *pDB)
|
|
{
|
|
// Locals
|
|
HRESULT hr=S_OK;
|
|
LPSTR pszT=(LPSTR)pszQuery;
|
|
LPQUERYTOKEN pCurrent;
|
|
LPQUERYTOKEN pPrevious;
|
|
LPQUERYTOKEN pToken=NULL;
|
|
LPQUERYTOKEN pPostfixHead=NULL;
|
|
LPQUERYTOKEN pPostfixTail=NULL;
|
|
LPQUERYTOKEN pStackTop=NULL;
|
|
|
|
// Trace
|
|
TraceCall("ParseQuery");
|
|
|
|
// Invalid Args
|
|
if (NULL == pszQuery || NULL == pSchema || NULL == phQuery)
|
|
return TraceResult(E_INVALIDARG);
|
|
|
|
// Initialize
|
|
(*phQuery) = NULL;
|
|
|
|
// Start the Parsing Loop
|
|
while(1)
|
|
{
|
|
// Parse next Token
|
|
IF_FAILEXIT(hr = GetNextQueryToken(&pszT, pSchema, &pToken, pDB));
|
|
|
|
// Done
|
|
if (S_FALSE == hr)
|
|
break;
|
|
|
|
// If this was an operand, append to postfix expression
|
|
if (TOKEN_OPERAND == pToken->tyToken)
|
|
{
|
|
// LinkToken
|
|
LinkToken(pToken, &pPostfixHead, &pPostfixTail);
|
|
|
|
// Don't pToken
|
|
ReleaseToken(&pToken, pDB);
|
|
}
|
|
|
|
// Otherwise, must be an operator
|
|
else
|
|
{
|
|
// Must be an operator
|
|
Assert(TOKEN_OPERATOR == pToken->tyToken);
|
|
|
|
// If Right Paren
|
|
if (OPERATOR_RIGHTPAREN == pToken->tyOperator)
|
|
{
|
|
// Pop all the items from the stack and link into the postfix expression
|
|
while (pStackTop && OPERATOR_LEFTPAREN != pStackTop->tyOperator)
|
|
{
|
|
// Save pPrevious
|
|
pPrevious = pStackTop->pPrevious;
|
|
|
|
// Otherwise
|
|
LinkToken(pStackTop, &pPostfixHead, &pPostfixTail);
|
|
|
|
// Releae
|
|
ReleaseToken(&pStackTop, pDB);
|
|
|
|
// Goto Previuos
|
|
pStackTop = pPrevious;
|
|
}
|
|
|
|
// If not a left parent was found, then we failed
|
|
if (OPERATOR_LEFTPAREN != pStackTop->tyOperator)
|
|
{
|
|
hr = TraceResult(DB_E_UNMATCHINGPARENS);
|
|
goto exit;
|
|
}
|
|
|
|
// Save pPrevious
|
|
pPrevious = pStackTop->pPrevious;
|
|
|
|
// Free pStackTop
|
|
ReleaseToken(&pStackTop, pDB);
|
|
|
|
// Reset pStackTop
|
|
pStackTop = pPrevious;
|
|
|
|
// Free pToken
|
|
ReleaseToken(&pToken, pDB);
|
|
}
|
|
|
|
// Otherwise
|
|
else
|
|
{
|
|
// Pop all the items into the postfix expression according to a cool little priority rule
|
|
while (pStackTop && ISP(pStackTop) <= ICP(pToken))
|
|
{
|
|
// Save pPrevious
|
|
pPrevious = pStackTop->pPrevious;
|
|
|
|
// Otherwise
|
|
LinkToken(pStackTop, &pPostfixHead, &pPostfixTail);
|
|
|
|
// Releae
|
|
ReleaseToken(&pStackTop, pDB);
|
|
|
|
// Goto Previuos
|
|
pStackTop = pPrevious;
|
|
}
|
|
|
|
// Append pToken to the Stack
|
|
LinkToken(pToken, NULL, &pStackTop);
|
|
|
|
// Don't pToken
|
|
ReleaseToken(&pToken, pDB);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Pop all the items from the stack and link into the postfix expression
|
|
while (pStackTop)
|
|
{
|
|
// Save pPrevious
|
|
pPrevious = pStackTop->pPrevious;
|
|
|
|
// Append to Postfix Expression
|
|
LinkToken(pStackTop, &pPostfixHead, &pPostfixTail);
|
|
|
|
// Releae
|
|
ReleaseToken(&pStackTop, pDB);
|
|
|
|
// Goto Previuos
|
|
pStackTop = pPrevious;
|
|
}
|
|
|
|
// lets write the postfix notation...
|
|
//IF_DEBUG(DebugDumpExpression(pszQuery, pSchema, pPostfixHead));
|
|
|
|
// Success
|
|
(*phQuery) = (HQUERY)pPostfixHead;
|
|
|
|
exit:
|
|
// Cleanup On Failure
|
|
if (FAILED(hr))
|
|
{
|
|
// Free pToken
|
|
ReleaseToken(&pToken, pDB);
|
|
|
|
// Free the Stack
|
|
ReleaseTokenList(TRUE, &pStackTop, pDB);
|
|
|
|
// Free the Postfix Expression
|
|
ReleaseTokenList(FALSE, &pPostfixHead, pDB);
|
|
}
|
|
|
|
// Done
|
|
return(hr);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// DebugDumpExpression
|
|
//--------------------------------------------------------------------------
|
|
#ifdef DEBUG
|
|
HRESULT DebugDumpExpression(LPCSTR pszQuery, LPCTABLESCHEMA pSchema,
|
|
LPQUERYTOKEN pPostfixHead)
|
|
{
|
|
// Locals
|
|
LPQUERYTOKEN pToken;
|
|
|
|
// Trace
|
|
TraceCall("DebugDumpExpression");
|
|
|
|
// Write Infix
|
|
DebugTrace("ParseQuery (Infix) : %s\n", pszQuery);
|
|
|
|
// Write Postfix header
|
|
DebugTrace("ParseQuery (Postfix) : ");
|
|
|
|
// Loop through the tokens
|
|
for (pToken=pPostfixHead; pToken!=NULL; pToken=pToken->pNext)
|
|
{
|
|
// Operator
|
|
if (TOKEN_OPERATOR == pToken->tyToken)
|
|
{
|
|
// Write the Operator
|
|
DebugTrace("%s", g_rgOperator[pToken->tyOperator].pszName);
|
|
}
|
|
|
|
// Operand
|
|
else if (TOKEN_OPERAND == pToken->tyToken)
|
|
{
|
|
// Column Operand
|
|
if (OPERAND_COLUMN == pToken->Operand.tyOperand)
|
|
{
|
|
// Must have an iSymbol
|
|
Assert(0xffffffff != pToken->Operand.iSymbol);
|
|
|
|
// Write the Symbol
|
|
DebugTrace("Column: %d (%s)", pToken->Operand.dwValue, pSchema->pSymbols->rgSymbol[pToken->Operand.iSymbol].pszName);
|
|
}
|
|
|
|
// String Operand
|
|
else if (OPERAND_STRING == pToken->Operand.tyOperand)
|
|
{
|
|
// Write the Symbol
|
|
DebugTrace("<%s>", pToken->Operand.pszString);
|
|
}
|
|
|
|
// Dword Operand
|
|
else if (OPERAND_DWORD == pToken->Operand.tyOperand)
|
|
{
|
|
// Has a iSymbol
|
|
if (0xffffffff != pToken->Operand.iSymbol)
|
|
{
|
|
// Write the Symbol
|
|
DebugTrace("%d (%s)", pToken->Operand.dwValue, pSchema->pSymbols->rgSymbol[pToken->Operand.iSymbol].pszName);
|
|
}
|
|
|
|
// Otherwise, just write the value
|
|
else
|
|
{
|
|
// Write the Symbol
|
|
DebugTrace("%d", pToken->Operand.dwValue);
|
|
}
|
|
}
|
|
|
|
// Method
|
|
else if (OPERAND_METHOD == pToken->Operand.tyOperand)
|
|
{
|
|
// Validate Symbol Type
|
|
Assert(SYMBOL_METHOD == pSchema->pSymbols->rgSymbol[pToken->Operand.iSymbol].tySymbol);
|
|
|
|
// Write the Method
|
|
DebugTrace("Method: %d (%s)", pToken->Operand.idMethod, pSchema->pSymbols->rgSymbol[pToken->Operand.iSymbol].pszName);
|
|
}
|
|
}
|
|
|
|
// Bad
|
|
else
|
|
Assert(FALSE);
|
|
|
|
// Write Delimiter
|
|
DebugTrace(", ");
|
|
}
|
|
|
|
// Wrap the line
|
|
DebugTrace("\n");
|
|
|
|
// Done
|
|
return(S_OK);
|
|
}
|
|
#endif // DEBUG
|
|
|
|
//--------------------------------------------------------------------------
|
|
// CompareSymbol
|
|
//--------------------------------------------------------------------------
|
|
HRESULT CompareSymbol(LPSTR pszT, LPCSTR pszName, LPSTR *ppszEnd)
|
|
{
|
|
// Locals
|
|
LPSTR pszName1;
|
|
LPSTR pszName2;
|
|
|
|
// Trace
|
|
TraceCall("CompareSymbol");
|
|
|
|
// Set pszName
|
|
pszName1 = (LPSTR)pszName;
|
|
|
|
// Set pszName2
|
|
pszName2 = pszT;
|
|
|
|
// Compare pszTo to Operator pszName...
|
|
while ('\0' != *pszName2 && *pszName1 == *pszName2)
|
|
{
|
|
// Increment
|
|
pszName1++;
|
|
pszName2++;
|
|
|
|
// Reached the End of pszName1, must be a match
|
|
if ('\0' == *pszName1)
|
|
{
|
|
// Set ppszEnd
|
|
*ppszEnd = pszName2;
|
|
|
|
// Done
|
|
return(S_OK);
|
|
}
|
|
}
|
|
|
|
// Done
|
|
return(S_FALSE);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// GetNextQueryToken
|
|
//--------------------------------------------------------------------------
|
|
HRESULT GetNextQueryToken(LPSTR *ppszT, LPCTABLESCHEMA pSchema,
|
|
LPQUERYTOKEN *ppToken, CDatabase *pDB)
|
|
{
|
|
// Locals
|
|
HRESULT hr=S_FALSE;
|
|
LPSTR pszT=(*ppszT);
|
|
LPSTR pszEnd;
|
|
DWORD i;
|
|
LPQUERYTOKEN pToken=NULL;
|
|
|
|
// Trace
|
|
TraceCall("GetNextQueryToken");
|
|
|
|
// Allocate a Token
|
|
IF_NULLEXIT(pToken = (LPQUERYTOKEN)pDB->PHeapAllocate(HEAP_ZERO_MEMORY, sizeof(QUERYTOKEN)));
|
|
|
|
// Set Reference Count
|
|
pToken->cRefs = 1;
|
|
|
|
// No Token foundyet
|
|
pToken->tyToken = TOKEN_INVALID;
|
|
|
|
// Invalid Symbol Index
|
|
pToken->Operand.iSymbol = 0xffffffff;
|
|
|
|
// Skip White Space...
|
|
while(*pszT && (*pszT == ' ' || *pszT == '\t'))
|
|
pszT++;
|
|
|
|
// Done
|
|
if ('\0' == *pszT)
|
|
goto exit;
|
|
|
|
// Check for the Start of an Operator...
|
|
for (i=0; i<OPERATOR_LAST; i++)
|
|
{
|
|
// Does pszT point to the start of an operator ?
|
|
if (S_OK == CompareSymbol(pszT, g_rgOperator[i].pszName, &pszEnd))
|
|
{
|
|
// Update pszT
|
|
pszT = pszEnd;
|
|
|
|
// We found an operator
|
|
pToken->tyToken = TOKEN_OPERATOR;
|
|
|
|
// Set the operator type
|
|
pToken->tyOperator = (OPERATORTYPE)i;
|
|
|
|
// Done
|
|
break;
|
|
}
|
|
}
|
|
|
|
// No Token Yet ?
|
|
if (TOKEN_INVALID == pToken->tyToken)
|
|
{
|
|
// Start of a String Literal ?
|
|
if ('"' == *pszT)
|
|
{
|
|
// ParseStringLiteral
|
|
IF_FAILEXIT(hr = ParseStringLiteral(pszT, &pToken->Operand, &pszEnd, pDB));
|
|
}
|
|
|
|
// Otherwise, start of a number
|
|
else if (DBIsDigit(pszT))
|
|
{
|
|
// ParseNumeric
|
|
IF_FAILEXIT(hr = ParseNumeric(pszT, &pToken->Operand, &pszEnd));
|
|
}
|
|
|
|
// Start of a Symbol
|
|
else
|
|
{
|
|
// ParseSymbol
|
|
IF_FAILEXIT(hr = ParseSymbol(pszT, pSchema, &pToken->Operand, &pszEnd, pDB));
|
|
}
|
|
|
|
// Must have been an operand
|
|
pToken->tyToken = TOKEN_OPERAND;
|
|
|
|
// Set pszT
|
|
pszT = pszEnd;
|
|
}
|
|
|
|
// Set ppszT
|
|
*ppszT = pszT;
|
|
|
|
// Success
|
|
hr = S_OK;
|
|
|
|
// Return the Token
|
|
*ppToken = pToken;
|
|
|
|
// Don't Free the Token
|
|
pToken = NULL;
|
|
|
|
exit:
|
|
// Cleanup
|
|
ReleaseToken(&pToken, pDB);
|
|
|
|
// Done
|
|
return(hr);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// ParseStringLiteral
|
|
//--------------------------------------------------------------------------
|
|
HRESULT ParseStringLiteral(LPCSTR pszStart, LPOPERANDINFO pOperand,
|
|
LPSTR *ppszEnd, CDatabase *pDB)
|
|
{
|
|
// Locals
|
|
HRESULT hr=S_OK;
|
|
LPSTR pszValue;
|
|
DWORD cchString;
|
|
LPSTR pszT=(LPSTR)pszStart;
|
|
|
|
// Trace
|
|
TraceCall("ParseStringLiteral");
|
|
|
|
// Validate Args
|
|
Assert(*pszT == '"' && pOperand && ppszEnd);
|
|
|
|
// Increment over "
|
|
pszT++;
|
|
|
|
// Find the End of the Quoted String
|
|
while(*pszT)
|
|
{
|
|
// DBCS Lead Byte
|
|
if (IsDBCSLeadByte(*pszT) || '\\' == *pszT)
|
|
{
|
|
pszT+=2;
|
|
continue;
|
|
}
|
|
|
|
// If Escaped Quote..
|
|
if ('"' == *pszT)
|
|
{
|
|
// Set ppszEnd
|
|
*ppszEnd = pszT + 1;
|
|
|
|
// Done
|
|
break;
|
|
}
|
|
|
|
// Increment pszT
|
|
pszT++;
|
|
}
|
|
|
|
// Not Found
|
|
if ('\0' == *pszT)
|
|
{
|
|
hr = TraceResult(DB_E_UNMATCHINGQUOTES);
|
|
goto exit;
|
|
}
|
|
|
|
// Get Size
|
|
cchString = (DWORD)(pszT - (pszStart + 1));
|
|
|
|
// Duplicate the String
|
|
IF_NULLEXIT(pszValue = (LPSTR)pDB->PHeapAllocate(NOFLAGS, cchString + 1));
|
|
|
|
// Copy the String
|
|
CopyMemory(pszValue, pszStart + 1, cchString);
|
|
|
|
// Set the Null
|
|
pszValue[cchString] = '\0';
|
|
|
|
// Set Operand Type
|
|
pOperand->tyOperand = OPERAND_STRING;
|
|
|
|
// Release
|
|
pOperand->pRelease = (LPVOID)pszValue;
|
|
|
|
// Set Value
|
|
pOperand->pszString = pszValue;
|
|
|
|
exit:
|
|
// Done
|
|
return(hr);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// ParseNumeric
|
|
//--------------------------------------------------------------------------
|
|
HRESULT ParseNumeric(LPCSTR pszStart, LPOPERANDINFO pOperand, LPSTR *ppszEnd)
|
|
{
|
|
// Locals
|
|
HRESULT hr=S_OK;
|
|
DWORD dwValue;
|
|
CHAR szNumber[255];
|
|
DWORD dwIncrement=0;
|
|
LPSTR pszT=(LPSTR)pszStart;
|
|
DWORD cchNumber;
|
|
|
|
// Trace
|
|
TraceCall("ParseNumeric");
|
|
|
|
// Validate Args
|
|
Assert(DBIsDigit(pszT) && pOperand && ppszEnd);
|
|
|
|
// Is Hex: 0x
|
|
if ('0' == *pszT && '\0' != *(pszT + 1) && 'X' == TOUPPERA(*(pszT + 1)))
|
|
{
|
|
// IsHex
|
|
dwIncrement = 2;
|
|
|
|
// Set pszT
|
|
pszT += 2;
|
|
}
|
|
|
|
// Find the End of the Number
|
|
while (*pszT && DBIsDigit(pszT))
|
|
{
|
|
// Increment
|
|
pszT++;
|
|
}
|
|
|
|
// Get Length
|
|
cchNumber = (DWORD)(pszT - (pszStart + dwIncrement));
|
|
|
|
// Too Frickin Big
|
|
if (cchNumber >= ARRAYSIZE(szNumber))
|
|
{
|
|
hr = TraceResult(DB_E_NUMBERTOOBIG);
|
|
goto exit;
|
|
}
|
|
|
|
// Copy into szNumber
|
|
CopyMemory(szNumber, pszStart + dwIncrement, cchNumber);
|
|
|
|
// Set Null
|
|
szNumber[cchNumber] = '\0';
|
|
|
|
// If Is Hex, convert to integer
|
|
if (FALSE == StrToIntEx(szNumber, dwIncrement ? STIF_SUPPORT_HEX : STIF_DEFAULT, (INT *)&dwValue))
|
|
{
|
|
hr = TraceResult(DB_E_BADNUMBER);
|
|
goto exit;
|
|
}
|
|
|
|
// Set Operand Type
|
|
pOperand->tyOperand = OPERAND_DWORD;
|
|
|
|
// Set Value
|
|
pOperand->dwValue = dwValue;
|
|
|
|
// Return ppszEnd
|
|
*ppszEnd = pszT;
|
|
|
|
exit:
|
|
// Done
|
|
return(hr);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// ParseSymbol
|
|
//--------------------------------------------------------------------------
|
|
HRESULT ParseSymbol(LPCSTR pszT, LPCTABLESCHEMA pSchema, LPOPERANDINFO pOperand,
|
|
LPSTR *ppszEnd, CDatabase *pDB)
|
|
{
|
|
// Locals
|
|
HRESULT hr=S_OK;
|
|
DWORD i;
|
|
LPSYMBOLINFO pSymbol;
|
|
LPSTR pszEnd;
|
|
|
|
// Trace
|
|
TraceCall("ParseSymbol");
|
|
|
|
// No Symbols
|
|
if (NULL == pSchema->pSymbols)
|
|
{
|
|
hr = TraceResult(DB_E_NOSYMBOLS);
|
|
goto exit;
|
|
}
|
|
|
|
// Check for the Start of an Operator...
|
|
for (i=0; i<pSchema->pSymbols->cSymbols; i++)
|
|
{
|
|
// Readability
|
|
pSymbol = (LPSYMBOLINFO)&pSchema->pSymbols->rgSymbol[i];
|
|
|
|
// Does pszT point to the start of an operator ?
|
|
if (S_OK == CompareSymbol((LPSTR)pszT, pSymbol->pszName, &pszEnd))
|
|
{
|
|
// Update pszT
|
|
*ppszEnd = pszEnd;
|
|
|
|
// Save iSymbol
|
|
pOperand->iSymbol = i;
|
|
|
|
// Is Column Symbol
|
|
if (SYMBOL_COLUMN == pSymbol->tySymbol)
|
|
{
|
|
// Validate the Ordinal
|
|
if (pSymbol->dwValue > pSchema->cColumns)
|
|
{
|
|
hr = TraceResult(DB_E_INVALIDCOLUMN);
|
|
goto exit;
|
|
}
|
|
|
|
// Convert to OPERANDTYPE
|
|
pOperand->tyOperand = OPERAND_COLUMN;
|
|
|
|
// Save the Column
|
|
pOperand->iColumn = (COLUMNORDINAL)pSymbol->dwValue;
|
|
}
|
|
|
|
// Otherwise, is a method ?
|
|
else if (SYMBOL_METHOD == pSymbol->tySymbol)
|
|
{
|
|
// Convert to OPERANDTYPE
|
|
pOperand->tyOperand = OPERAND_METHOD;
|
|
|
|
// Save the Column
|
|
pOperand->idMethod = pSymbol->dwValue;
|
|
}
|
|
|
|
// Otherwise, just a dword value
|
|
else
|
|
{
|
|
// Dword
|
|
pOperand->tyOperand = OPERAND_DWORD;
|
|
|
|
// Set the operator type
|
|
pOperand->dwValue = pSymbol->dwValue;
|
|
}
|
|
|
|
// Done
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
// Not Found
|
|
hr = TraceResult(DB_E_INVALIDSYMBOL);
|
|
|
|
exit:
|
|
// Done
|
|
return(hr);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// CloseQuery
|
|
//--------------------------------------------------------------------------
|
|
HRESULT CloseQuery(LPHQUERY phQuery, CDatabase *pDB)
|
|
{
|
|
// Trace
|
|
TraceCall("CloseQuery");
|
|
|
|
// ReleaseTokenList
|
|
ReleaseTokenList(FALSE, (LPQUERYTOKEN *)phQuery, pDB);
|
|
|
|
// Done
|
|
return(S_OK);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// PushStackToken
|
|
//--------------------------------------------------------------------------
|
|
HRESULT PushStackToken(LPQUERYTOKEN pToken, LPQUERYTOKEN *ppStackTop)
|
|
{
|
|
// Trace
|
|
TraceCall("PushStackToken");
|
|
|
|
// Set pStackPrevious
|
|
pToken->pPrevious = (*ppStackTop);
|
|
|
|
// Update Stack Top
|
|
(*ppStackTop) = pToken;
|
|
|
|
// AddRef the Token
|
|
pToken->cRefs++;
|
|
|
|
// Done
|
|
return(S_OK);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// PopStackToken
|
|
//--------------------------------------------------------------------------
|
|
HRESULT PopStackToken(LPQUERYTOKEN *ppToken, LPQUERYTOKEN *ppStackTop)
|
|
{
|
|
// Trace
|
|
TraceCall("PopStackToken");
|
|
|
|
// Validate
|
|
Assert(ppToken && ppStackTop);
|
|
|
|
// No more tokens...
|
|
if (NULL == *ppStackTop)
|
|
return TraceResult(DB_E_BADEXPRESSION);
|
|
|
|
// Set Token
|
|
*ppToken = (*ppStackTop);
|
|
|
|
// Goto Previous
|
|
(*ppStackTop) = (*ppToken)->pPrevious;
|
|
|
|
// Release the Token
|
|
//(*ppToken)->cRefs--;
|
|
|
|
// Done
|
|
return(S_OK);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// LinkToken
|
|
//--------------------------------------------------------------------------
|
|
HRESULT LinkToken(LPQUERYTOKEN pToken, LPQUERYTOKEN *ppHead, LPQUERYTOKEN *ppTail)
|
|
{
|
|
// Trace
|
|
TraceCall("LinkToken");
|
|
|
|
// Invalid Args
|
|
Assert(pToken && ppTail);
|
|
|
|
// No Next and No Previous
|
|
pToken->pNext = pToken->pPrevious = NULL;
|
|
|
|
// No Head yet ?
|
|
if (ppHead && NULL == *ppHead)
|
|
{
|
|
// Set the Head and Tail
|
|
*ppHead = pToken;
|
|
}
|
|
|
|
// Otherwise, append to the end
|
|
else if (*ppTail)
|
|
{
|
|
// Set ppTail->pNext
|
|
(*ppTail)->pNext = pToken;
|
|
|
|
// Set Previous
|
|
pToken->pPrevious = (*ppTail);
|
|
}
|
|
|
|
// Update the Tail
|
|
*ppTail = pToken;
|
|
|
|
// AddRef the Token
|
|
pToken->cRefs++;
|
|
|
|
// Done
|
|
return(S_OK);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// ReleaseToken
|
|
//--------------------------------------------------------------------------
|
|
HRESULT ReleaseToken(LPQUERYTOKEN *ppToken, CDatabase *pDB)
|
|
{
|
|
// Trace
|
|
TraceCall("ReleaseToken");
|
|
|
|
// Token
|
|
if (*ppToken)
|
|
{
|
|
// Validate Reference Count
|
|
Assert((*ppToken)->cRefs);
|
|
|
|
// Decrement Reference Count
|
|
(*ppToken)->cRefs--;
|
|
|
|
// No more refs...
|
|
if (0 == (*ppToken)->cRefs)
|
|
{
|
|
// Free pData
|
|
pDB->HeapFree((*ppToken)->Operand.pRelease);
|
|
|
|
// Free pElement
|
|
pDB->HeapFree((*ppToken));
|
|
}
|
|
|
|
// Don't Release Again
|
|
*ppToken = NULL;
|
|
}
|
|
|
|
// Done
|
|
return(S_OK);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// ReleaseTokenList
|
|
//--------------------------------------------------------------------------
|
|
HRESULT ReleaseTokenList(BOOL fReverse, LPQUERYTOKEN *ppHead, CDatabase *pDB)
|
|
{
|
|
// Locals
|
|
LPQUERYTOKEN pNext;
|
|
LPQUERYTOKEN pToken=(*ppHead);
|
|
|
|
// Trace
|
|
TraceCall("ReleaseTokenList");
|
|
|
|
// Walk the Linked List
|
|
while (pToken)
|
|
{
|
|
// Save Next
|
|
pNext = (fReverse ? pToken->pPrevious : pToken->pNext);
|
|
|
|
// Free this token
|
|
ReleaseToken(&pToken, pDB);
|
|
|
|
// Goto Next
|
|
pToken = pNext;
|
|
}
|
|
|
|
// Don't Free Again
|
|
*ppHead = NULL;
|
|
|
|
// Done
|
|
return(S_OK);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// PGetOperandData
|
|
//--------------------------------------------------------------------------
|
|
LPVOID PGetOperandData(OPERANDTYPE tyOperand, LPOPERANDINFO pOperand,
|
|
LPVOID pBinding, LPCTABLESCHEMA pSchema, CDatabase *pDB,
|
|
IDatabaseExtension *pExtension)
|
|
{
|
|
// Locals
|
|
LPVOID pValue=NULL;
|
|
|
|
// Trace
|
|
TraceCall("PGetOperandData");
|
|
|
|
// OPERAND_COLUMN
|
|
if (OPERAND_COLUMN == pOperand->tyOperand)
|
|
{
|
|
// Get the Tag
|
|
LPCTABLECOLUMN pColumn = &pSchema->prgColumn[pOperand->iColumn];
|
|
|
|
// MapColumnType
|
|
MapColumnType(pColumn->type, pOperand, pColumn, pBinding, &pValue);
|
|
}
|
|
|
|
// OPERAND_STRING
|
|
else if (OPERAND_STRING == pOperand->tyOperand)
|
|
{
|
|
// Better want a string out
|
|
Assert(OPERAND_STRING == tyOperand);
|
|
|
|
// Return Data Pointer
|
|
pValue = pOperand->pszString;
|
|
}
|
|
|
|
// OPERAND_DWORD
|
|
else if (OPERAND_DWORD == pOperand->tyOperand)
|
|
{
|
|
// Better want a dword out
|
|
Assert(OPERAND_DWORD == tyOperand);
|
|
|
|
// Return Data Pointer
|
|
pValue = (LPVOID)&pOperand->dwValue;
|
|
}
|
|
|
|
// OPERAND_METHOD
|
|
else if (OPERAND_METHOD == pOperand->tyOperand && pExtension)
|
|
{
|
|
// Better want a dword out
|
|
Assert(OPERAND_DWORD == tyOperand);
|
|
|
|
// Call the Method on the Extension
|
|
pExtension->OnExecuteMethod(pOperand->idMethod, pBinding, &pOperand->dwReserved);
|
|
|
|
// Return Data Pointer
|
|
pValue = (LPVOID)&pOperand->dwReserved;
|
|
}
|
|
|
|
// No Data ?
|
|
if (NULL == pValue)
|
|
{
|
|
// What type of operand was wanted
|
|
switch(tyOperand)
|
|
{
|
|
case OPERAND_STRING:
|
|
pValue = (LPVOID)c_szEmpty;
|
|
break;
|
|
|
|
case OPERAND_DWORD:
|
|
pOperand->dwReserved = 0;
|
|
pValue = (LPVOID)&pOperand->dwReserved;
|
|
break;
|
|
|
|
default:
|
|
AssertSz(FALSE, "While go ahead and Jimmy my buffet..");
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Done
|
|
return(pValue);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// GetCommonOperandType
|
|
//--------------------------------------------------------------------------
|
|
OPERANDTYPE GetCommonOperandType(LPOPERANDINFO pLeft, LPOPERANDINFO pRight,
|
|
LPCTABLESCHEMA pSchema)
|
|
{
|
|
// Locals
|
|
OPERANDTYPE tyLeft = (OPERAND_STRING == pLeft->tyOperand ? OPERAND_STRING : OPERAND_DWORD);
|
|
OPERANDTYPE tyRight = (OPERAND_STRING == pRight->tyOperand ? OPERAND_STRING : OPERAND_DWORD);
|
|
|
|
// Trace
|
|
TraceCall("GetCommonOperandType");
|
|
|
|
// Left is a column
|
|
if (OPERAND_COLUMN == pLeft->tyOperand)
|
|
{
|
|
// Maps to a string
|
|
if (OPERAND_STRING == g_rgColumnTypeInfo[pSchema->prgColumn[pLeft->iColumn].type].tyOperand)
|
|
tyLeft = OPERAND_STRING;
|
|
}
|
|
|
|
// Right is a string
|
|
if (OPERAND_COLUMN == pRight->tyOperand)
|
|
{
|
|
// Maps to a String ?
|
|
if (OPERAND_STRING == g_rgColumnTypeInfo[pSchema->prgColumn[pRight->iColumn].type].tyOperand)
|
|
tyRight = OPERAND_STRING;
|
|
}
|
|
|
|
// Better be the Same
|
|
Assert(tyLeft == tyRight);
|
|
|
|
// Return tyLeft since they are the same
|
|
return(tyLeft);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// EvaluateClause
|
|
//--------------------------------------------------------------------------
|
|
HRESULT EvaluateClause(OPERATORTYPE tyOperator, LPVOID pBinding,
|
|
LPCTABLESCHEMA pSchema, LPQUERYTOKEN *ppStackTop, CDatabase *pDB,
|
|
IDatabaseExtension *pExtension)
|
|
{
|
|
// Locals
|
|
HRESULT hr=S_OK;
|
|
LPVOID pDataLeft=NULL;
|
|
LPVOID pDataRight=NULL;
|
|
LPQUERYTOKEN pTokenResult=NULL;
|
|
LPQUERYTOKEN pTokenRight=NULL;
|
|
LPQUERYTOKEN pTokenLeft=NULL;
|
|
OPERANDTYPE tyOperand;
|
|
INT nCompare;
|
|
|
|
// Trace
|
|
TraceCall("EvaluateClause");
|
|
|
|
// Pop the right token
|
|
IF_FAILEXIT(hr = PopStackToken(&pTokenRight, ppStackTop));
|
|
|
|
// Pop the left token
|
|
IF_FAILEXIT(hr = PopStackToken(&pTokenLeft, ppStackTop));
|
|
|
|
// Better have Data
|
|
Assert(TOKEN_OPERAND == pTokenLeft->tyToken && TOKEN_OPERAND == pTokenRight->tyToken);
|
|
|
|
// Compute Operand type
|
|
tyOperand = GetCommonOperandType(&pTokenLeft->Operand, &pTokenRight->Operand, pSchema);
|
|
|
|
// Get Left Data
|
|
pDataLeft = PGetOperandData(tyOperand, &pTokenLeft->Operand, pBinding, pSchema, pDB, pExtension);
|
|
|
|
// Get Right Data
|
|
pDataRight = PGetOperandData(tyOperand, &pTokenRight->Operand, pBinding, pSchema, pDB, pExtension);
|
|
|
|
// Create new Token to push back onto the stack
|
|
IF_NULLEXIT(pTokenResult = (LPQUERYTOKEN)pDB->PHeapAllocate(HEAP_ZERO_MEMORY, sizeof(QUERYTOKEN)));
|
|
|
|
// Set Reference Count
|
|
pTokenResult->cRefs = 1;
|
|
|
|
// No Token foundyet
|
|
pTokenResult->tyToken = TOKEN_OPERAND;
|
|
|
|
// Invalid Symbol Index
|
|
pTokenResult->Operand.iSymbol = 0xffffffff;
|
|
|
|
// Set Result
|
|
pTokenResult->Operand.tyOperand = OPERAND_DWORD;
|
|
|
|
// EvaluateData
|
|
pTokenResult->Operand.dwValue = EvaluateOperator(tyOperator, tyOperand, pDataLeft, pDataRight);
|
|
|
|
// Push the result operand
|
|
PushStackToken(pTokenResult, ppStackTop);
|
|
|
|
exit:
|
|
// Cleanup
|
|
ReleaseToken(&pTokenLeft, pDB);
|
|
ReleaseToken(&pTokenRight, pDB);
|
|
ReleaseToken(&pTokenResult, pDB);
|
|
|
|
// Done
|
|
return(hr);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// EvaluateEqual
|
|
//--------------------------------------------------------------------------
|
|
DWORD EvaluateEqual(OPERANDTYPE tyOperand, LPVOID pDataLeft, LPVOID pDataRight) {
|
|
return (0 == CompareOperands(tyOperand, pDataLeft, pDataRight) ? TRUE : FALSE);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// EvaluateNotEqual
|
|
//--------------------------------------------------------------------------
|
|
DWORD EvaluateNotEqual(OPERANDTYPE tyOperand, LPVOID pDataLeft, LPVOID pDataRight) {
|
|
return (0 != CompareOperands(tyOperand, pDataLeft, pDataRight) ? TRUE : FALSE);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// EvaluateLessThanEqual
|
|
//--------------------------------------------------------------------------
|
|
DWORD EvaluateLessThanEqual(OPERANDTYPE tyOperand, LPVOID pDataLeft, LPVOID pDataRight) {
|
|
return (CompareOperands(tyOperand, pDataLeft, pDataRight) <= 0 ? TRUE : FALSE);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// EvaluateLessThan
|
|
//--------------------------------------------------------------------------
|
|
DWORD EvaluateLessThan(OPERANDTYPE tyOperand, LPVOID pDataLeft, LPVOID pDataRight) {
|
|
return (CompareOperands(tyOperand, pDataLeft, pDataRight) < 0 ? TRUE : FALSE);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// EvaluateGreaterThanEqual
|
|
//--------------------------------------------------------------------------
|
|
DWORD EvaluateGreaterThanEqual(OPERANDTYPE tyOperand, LPVOID pDataLeft, LPVOID pDataRight) {
|
|
return (CompareOperands(tyOperand, pDataLeft, pDataRight) >= 0 ? TRUE : FALSE);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// EvaluateGreaterThan
|
|
//--------------------------------------------------------------------------
|
|
DWORD EvaluateGreaterThan(OPERANDTYPE tyOperand, LPVOID pDataLeft, LPVOID pDataRight) {
|
|
return (CompareOperands(tyOperand, pDataLeft, pDataRight) > 0 ? TRUE : FALSE);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// EvaluateAnd
|
|
//--------------------------------------------------------------------------
|
|
DWORD EvaluateAnd(OPERANDTYPE tyOperand, LPVOID pDataLeft, LPVOID pDataRight) {
|
|
return (INT)(*((DWORD *)pDataLeft) && *((DWORD *)pDataRight));
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// EvaluateBitwiseAnd
|
|
//--------------------------------------------------------------------------
|
|
DWORD EvaluateBitwiseAnd(OPERANDTYPE tyOperand, LPVOID pDataLeft, LPVOID pDataRight) {
|
|
return (INT)(*((DWORD *)pDataLeft) & *((DWORD *)pDataRight));
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// EvaluateOr
|
|
//--------------------------------------------------------------------------
|
|
DWORD EvaluateOr(OPERANDTYPE tyOperand, LPVOID pDataLeft, LPVOID pDataRight) {
|
|
return (INT)(*((DWORD *)pDataLeft) || *((DWORD *)pDataRight));
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// EvaluateBitwiseOr
|
|
//--------------------------------------------------------------------------
|
|
DWORD EvaluateBitwiseOr(OPERANDTYPE tyOperand, LPVOID pDataLeft, LPVOID pDataRight) {
|
|
return (INT)(*((DWORD *)pDataLeft) | *((DWORD *)pDataRight));
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// EvaluateStrStrI
|
|
//--------------------------------------------------------------------------
|
|
DWORD EvaluateStrStrI(OPERANDTYPE tyOperand, LPVOID pDataLeft, LPVOID pDataRight) {
|
|
return (NULL == StrStrIA((LPCSTR)pDataLeft, (LPCSTR)pDataRight) ? FALSE : TRUE);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// EvaluateStrStr
|
|
//--------------------------------------------------------------------------
|
|
DWORD EvaluateStrStr(OPERANDTYPE tyOperand, LPVOID pDataLeft, LPVOID pDataRight) {
|
|
return (NULL == StrStrA((LPCSTR)pDataLeft, (LPCSTR)pDataRight) ? FALSE : TRUE);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// EvaluateStrcmpi
|
|
//--------------------------------------------------------------------------
|
|
DWORD EvaluateStrcmpi(OPERANDTYPE tyOperand, LPVOID pDataLeft, LPVOID pDataRight) {
|
|
return (lstrcmpi((LPCSTR)pDataLeft, (LPCSTR)pDataRight) == 0 ? TRUE : FALSE);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// EvaluateStrcmp
|
|
//--------------------------------------------------------------------------
|
|
DWORD EvaluateStrcmp(OPERANDTYPE tyOperand, LPVOID pDataLeft, LPVOID pDataRight) {
|
|
return (lstrcmp((LPCSTR)pDataLeft, (LPCSTR)pDataRight) == 0 ? TRUE : FALSE);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// EvaluateAdd
|
|
//--------------------------------------------------------------------------
|
|
DWORD EvaluateAdd(OPERANDTYPE tyOperand, LPVOID pDataLeft, LPVOID pDataRight) {
|
|
return (INT)(*((DWORD *)pDataLeft) + *((DWORD *)pDataRight));
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// EvaluateSubtract
|
|
//--------------------------------------------------------------------------
|
|
DWORD EvaluateSubtract(OPERANDTYPE tyOperand, LPVOID pDataLeft, LPVOID pDataRight) {
|
|
return (INT)(*((DWORD *)pDataLeft) - *((DWORD *)pDataRight));
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// EvaluateMultiply
|
|
//--------------------------------------------------------------------------
|
|
DWORD EvaluateMultiply(OPERANDTYPE tyOperand, LPVOID pDataLeft, LPVOID pDataRight) {
|
|
return (INT)(*((DWORD *)pDataLeft) * *((DWORD *)pDataRight));
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// EvaluateDivide
|
|
//--------------------------------------------------------------------------
|
|
DWORD EvaluateDivide(OPERANDTYPE tyOperand, LPVOID pDataLeft, LPVOID pDataRight) {
|
|
return (INT)(*((DWORD *)pDataLeft) / *((DWORD *)pDataRight));
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// EvaluateModula
|
|
//--------------------------------------------------------------------------
|
|
DWORD EvaluateModula(OPERANDTYPE tyOperand, LPVOID pDataLeft, LPVOID pDataRight) {
|
|
return (INT)(*((DWORD *)pDataLeft) % *((DWORD *)pDataRight));
|
|
}
|