mirror of https://github.com/tongzx/nt5src
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.
7396 lines
221 KiB
7396 lines
221 KiB
//NEW ONE
|
|
/***************************************************************************/
|
|
/* PARSE.C */
|
|
/* Copyright (C) 1995-96 SYWARE Inc., All rights reserved */
|
|
/***************************************************************************/
|
|
// Commenting #define out - causing compiler error - not sure if needed, compiles
|
|
// okay without it.
|
|
//#define WINVER 0x0400
|
|
#include "precomp.h"
|
|
#include "wbemidl.h"
|
|
|
|
#include <comdef.h>
|
|
//smart pointer
|
|
_COM_SMARTPTR_TYPEDEF(IWbemServices, IID_IWbemServices);
|
|
_COM_SMARTPTR_TYPEDEF(IEnumWbemClassObject, IID_IEnumWbemClassObject);
|
|
//_COM_SMARTPTR_TYPEDEF(IWbemContext, IID_IWbemContext );
|
|
_COM_SMARTPTR_TYPEDEF(IWbemLocator, IID_IWbemLocator);
|
|
|
|
#include "drdbdr.h"
|
|
#include <comdef.h> //for _bstr_t
|
|
|
|
/***************************************************************************/
|
|
/* Notes: */
|
|
/* */
|
|
/* PARSE.C contains the code that changes a SQL query (as a character */
|
|
/* string) and converts it to a tree that represents the query. */
|
|
/* */
|
|
/* The parse tree is actually an array of nodes. Initially it contains */
|
|
/* SQLNODE_ALLOC nodes, but it is expanded if additional nodes are needed. */
|
|
/* The root node of the tree is always the ROOT_SQLNODE'th element of this */
|
|
/* array. */
|
|
/* */
|
|
/* String values (such as table names, literal string values, etc.) are */
|
|
/* not stored directly in a node. Instead, they are stored in a separate */
|
|
/* string area and an index to the string is stored in the parse tree */
|
|
/* node. This is done to keep the parse tree nodes from getting too big. */
|
|
/* Initially the string area is STRING_ALLOC bytslong, but it is expanded */
|
|
/* is additional room is needed. */
|
|
/* */
|
|
/* As mentioned above, both the parse tree and string area may be expanded.*/
|
|
/* Because of this, it is not possible for a node to have hard pointers to */
|
|
/* child nodes or strings. To accomodate this problem, SQLNODEIDX and */
|
|
/* STRINGIDX values are stored instead. The ToNode() and ToString() macros*/
|
|
/* convert these indexes to pointers. */
|
|
/* */
|
|
/* The parser is recursive descent parser built entirely in C. To modify */
|
|
/* the grammar, all you need to do is change the code in this module (tools*/
|
|
/* such as YACC and LEX are not used). To best understand the code, start */
|
|
/* by looking at routine Parse() (at the end of this module). */
|
|
/* */
|
|
/* ShowSemantic() in TRACE.C can be used to dump a parse tree to the */
|
|
/* debug monitor. */
|
|
/* */
|
|
/***************************************************************************/
|
|
/* Forward references */
|
|
|
|
RETCODE INTFUNC ParseBoolean(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, LPUSTR lpszToken);
|
|
|
|
RETCODE INTFUNC ParseExpression(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, LPUSTR lpszToken);
|
|
|
|
RETCODE INTFUNC ParseSelect(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, LPUSTR lpszToken);
|
|
|
|
/***************************************************************************/
|
|
/* Keywords */
|
|
|
|
LPUSTR lpszKeywords[] = {
|
|
(LPUSTR) "ALL",
|
|
(LPUSTR) "ALTER", //Dr. DeeBee does not support this
|
|
(LPUSTR) "AND",
|
|
(LPUSTR) "ANY",
|
|
(LPUSTR) "AS",
|
|
(LPUSTR) "ASC",
|
|
(LPUSTR) "AVG",
|
|
(LPUSTR) "BY",
|
|
(LPUSTR) "COUNT",
|
|
(LPUSTR) "CREATE",
|
|
(LPUSTR) "DELETE",
|
|
(LPUSTR) "DESC",
|
|
(LPUSTR) "DISTINCT",
|
|
(LPUSTR) "DROP",
|
|
(LPUSTR) "EXISTS",
|
|
(LPUSTR) "GRANT", //Dr. DeeBee does not support this
|
|
(LPUSTR) "FROM",
|
|
(LPUSTR) "GROUP",
|
|
(LPUSTR) "HAVING",
|
|
(LPUSTR) "IN",
|
|
(LPUSTR) "INDEX",
|
|
(LPUSTR) "INSERT",
|
|
(LPUSTR) "INTO",
|
|
(LPUSTR) "IS",
|
|
(LPUSTR) "JOIN",
|
|
(LPUSTR) "LEFT",
|
|
(LPUSTR) "LIKE",
|
|
(LPUSTR) "MAX",
|
|
(LPUSTR) "MIN",
|
|
(LPUSTR) "NOT",
|
|
(LPUSTR) "NULL",
|
|
(LPUSTR) "ON",
|
|
(LPUSTR) "OR",
|
|
(LPUSTR) "ORDER",
|
|
(LPUSTR) "REVOKE", //Dr. DeeBee does not support this
|
|
(LPUSTR) "OUTER",
|
|
(LPUSTR) "SELECT",
|
|
(LPUSTR) "SET",
|
|
(LPUSTR) "SQL",
|
|
(LPUSTR) "SUM",
|
|
(LPUSTR) "TABLE",
|
|
(LPUSTR) "UNIQUE",
|
|
(LPUSTR) "UPDATE",
|
|
(LPUSTR) "USER",
|
|
(LPUSTR) "VALUES",
|
|
(LPUSTR) "WHERE",
|
|
NULL };
|
|
|
|
/***************************************************************************/
|
|
/* Token types */
|
|
|
|
#define TOKEN_TYPE_NONE 0
|
|
#define TOKEN_TYPE_SYMBOL 1
|
|
#define TOKEN_TYPE_NUMBER 2
|
|
#define TOKEN_TYPE_STRING 3
|
|
#define TOKEN_TYPE_IDENTIFIER 4
|
|
#define TOKEN_TYPE_KEYWORD 5
|
|
#define TOKEN_TYPE_ESCAPE 6
|
|
|
|
/***************************************************************************/
|
|
|
|
PassthroughLookupTable::PassthroughLookupTable()
|
|
{
|
|
tableAlias[0] = 0;
|
|
columnName[0] = 0;
|
|
}
|
|
|
|
BOOL PassthroughLookupTable::SetTableAlias(char* tblAlias)
|
|
{
|
|
tableAlias[0] = 0;
|
|
|
|
if (tblAlias)
|
|
{
|
|
if (strlen(tblAlias) <= MAX_QUALIFIER_NAME_LENGTH)
|
|
{
|
|
strcpy(tableAlias, tblAlias);
|
|
}
|
|
else
|
|
{
|
|
//Truncation
|
|
return FALSE;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL PassthroughLookupTable::SetColumnName(char* columName)
|
|
{
|
|
columnName[0] = 0;
|
|
|
|
if (columName)
|
|
{
|
|
if (strlen(columName) <= MAX_COLUMN_NAME_LENGTH)
|
|
{
|
|
strcpy(columnName, columName);
|
|
}
|
|
else
|
|
{
|
|
//Truncation
|
|
return FALSE;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
VirtualInstanceInfo::VirtualInstanceInfo()
|
|
{
|
|
cElements = 0;
|
|
cCycle = 1;
|
|
}
|
|
|
|
VirtualInstanceInfo::~VirtualInstanceInfo()
|
|
{
|
|
}
|
|
|
|
VirtualInstanceManager::VirtualInstanceManager()
|
|
{
|
|
virtualInfo = new CMapStringToPtr();
|
|
currentInstance = 0;
|
|
cNumberOfInstances = 0;
|
|
}
|
|
|
|
VirtualInstanceManager::~VirtualInstanceManager()
|
|
{
|
|
//Tidy up VirtualInfo
|
|
if (!virtualInfo->IsEmpty())
|
|
{
|
|
for(POSITION pos = virtualInfo->GetStartPosition(); pos != NULL; )
|
|
{
|
|
CString key; //not used
|
|
VirtualInstanceInfo* pa = NULL;
|
|
virtualInfo->GetNextAssoc( pos, key, (void*&)pa );
|
|
|
|
if (pa)
|
|
delete pa;
|
|
}
|
|
}
|
|
delete (virtualInfo);
|
|
virtualInfo = NULL;
|
|
}
|
|
|
|
void VirtualInstanceManager::AddColumnInstance(LPCTSTR TableAlias, LPCTSTR ColumnName, VirtualInstanceInfo* col)
|
|
{
|
|
//First check if new element contains any elements
|
|
long newElements = col->cElements;
|
|
|
|
if (! newElements)
|
|
return;
|
|
|
|
|
|
//Multiply call previous cycles counts by this number
|
|
if (!virtualInfo->IsEmpty())
|
|
{
|
|
for(POSITION pos = virtualInfo->GetStartPosition(); pos != NULL; )
|
|
{
|
|
CString key; //not used
|
|
VirtualInstanceInfo* pa = NULL;
|
|
virtualInfo->GetNextAssoc( pos, key, (void*&)pa );
|
|
|
|
if (pa)
|
|
{
|
|
pa->cCycle = pa->cCycle * newElements;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
//First contruct the key
|
|
char* tempbuff = ConstructKey(TableAlias, ColumnName);
|
|
_bstr_t mapKey(tempbuff);
|
|
delete tempbuff;
|
|
|
|
|
|
//Add element
|
|
virtualInfo->SetAt( (LPCTSTR)mapKey, col);
|
|
|
|
//Update the instance count
|
|
if (cNumberOfInstances)
|
|
{
|
|
cNumberOfInstances = cNumberOfInstances * newElements;
|
|
}
|
|
else
|
|
{
|
|
cNumberOfInstances = newElements;
|
|
}
|
|
}
|
|
|
|
|
|
void VirtualInstanceManager::Load(CMapWordToPtr* passthroughMap, IWbemClassObject* myInstance)
|
|
{
|
|
//Cycle through each column in the passthrough table
|
|
//and fetch its value from the IWbemClassObject instance
|
|
if (passthroughMap && !(passthroughMap->IsEmpty()))
|
|
{
|
|
for(POSITION pos = passthroughMap->GetStartPosition(); pos != NULL; )
|
|
{
|
|
WORD key = 0; //not used
|
|
PassthroughLookupTable* pltable = NULL;
|
|
passthroughMap->GetNextAssoc( pos, key, (void*&)pltable );
|
|
|
|
//Get TableAlias and ColumnName
|
|
CBString myTableAlias;
|
|
CBString myColumnName;
|
|
myTableAlias.AddString(pltable->GetTableAlias(), FALSE);
|
|
myColumnName.AddString(pltable->GetColumnName(), FALSE);
|
|
|
|
//Get the variant value
|
|
VARIANT myVal;
|
|
//SAI ADDED no need to init VariantInit(&myVal);
|
|
BOOL gotVarValue = GetVariantValue(myTableAlias, myColumnName, myInstance, &myVal);
|
|
|
|
{
|
|
//We don't care if this is an array of strings or not
|
|
//as we are not asking for any values, we just want to
|
|
//know the array size
|
|
BSTR_SafeArray myArray(&myVal);
|
|
|
|
//If this is an array type with at least one element
|
|
if ( myArray.IsValid() )
|
|
{
|
|
if ( myArray.Count() )
|
|
{
|
|
VirtualInstanceInfo* myInfo = new VirtualInstanceInfo();
|
|
myInfo->cElements = myArray.Count();
|
|
|
|
//Add to manager
|
|
AddColumnInstance(_bstr_t(myTableAlias.GetString()), _bstr_t(myColumnName.GetString()), myInfo);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (gotVarValue)
|
|
VariantClear(&myVal);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
BOOL VirtualInstanceManager::GetVariantValue(CBString& myTableAlias, CBString& myColumnName, IWbemClassObject* myInstance, VARIANT FAR* myVal)
|
|
{
|
|
// VariantInit(myVal);
|
|
|
|
//First we need to get the correctIWbemClassObject for the column value
|
|
IWbemClassObject* correctWbemObject = myInstance;
|
|
|
|
|
|
//We begin by checking if this is an __Generic class
|
|
//if so we need to fetch the embedded object
|
|
BOOL fIs__Generic = FALSE;
|
|
|
|
VARIANT grVariant;
|
|
//SAI ADDED no need VariantInit(&grVariant);
|
|
CBString classBSTR(WBEMDR32_L_CLASS, FALSE);
|
|
if (FAILED(myInstance->Get(classBSTR.GetString(), 0, &grVariant, NULL, NULL)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//Compare with __Generic
|
|
if (_wcsicmp(grVariant.bstrVal, L"__Generic") == 0)
|
|
{
|
|
fIs__Generic = TRUE;
|
|
//(4)
|
|
correctWbemObject = GetWbemObject(myTableAlias, myInstance);
|
|
}
|
|
|
|
VariantClear(&grVariant);
|
|
|
|
//Get the ColumnValue
|
|
if (FAILED(correctWbemObject->Get(myColumnName.GetString(), 0, myVal, NULL, 0) ))
|
|
{
|
|
//(4)
|
|
if (fIs__Generic)
|
|
correctWbemObject->Release();
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
if (fIs__Generic)
|
|
{
|
|
//(4)
|
|
correctWbemObject->Release();
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
IWbemClassObject* VirtualInstanceManager::GetWbemObject(CBString& myTableAlias, IWbemClassObject* myInstance)
|
|
{
|
|
//Get the property type and value
|
|
CIMTYPE vType;
|
|
VARIANT pVal;
|
|
//SAI ADDED no need to init VariantInit(&pVal);
|
|
|
|
if ( SUCCEEDED(myInstance->Get(myTableAlias.GetString(), 0, &pVal, &vType, 0)) )
|
|
{
|
|
//We are looking for embedded objects
|
|
if (vType == CIM_OBJECT)
|
|
{
|
|
|
|
IWbemClassObject* myEmbeddedObj = NULL;
|
|
|
|
IUnknown* myUnk = pVal.punkVal;
|
|
myUnk->QueryInterface(IID_IWbemClassObject, (void**)&myEmbeddedObj);
|
|
|
|
VariantClear(&pVal);
|
|
|
|
return myEmbeddedObj;
|
|
}
|
|
|
|
VariantClear(&pVal);
|
|
}
|
|
|
|
|
|
//error
|
|
return NULL;
|
|
}
|
|
|
|
char* VirtualInstanceManager::ConstructKey(LPCTSTR TableAlias, LPCTSTR ColumnName)
|
|
{
|
|
_bstr_t mapKey2("");
|
|
|
|
|
|
//Make key in upper case
|
|
CString myUpperCaseTbl(TableAlias);
|
|
|
|
if (! myUpperCaseTbl.IsEmpty())
|
|
myUpperCaseTbl.MakeUpper();
|
|
_bstr_t myTblAlias(myUpperCaseTbl);
|
|
|
|
CString myUpperCaseCol(ColumnName);
|
|
if (! myUpperCaseCol.IsEmpty())
|
|
myUpperCaseCol.MakeUpper();
|
|
_bstr_t myColName(myUpperCaseCol);
|
|
|
|
if ( myTblAlias.length() )
|
|
mapKey2 += (LPCTSTR)myTblAlias;
|
|
|
|
mapKey2+= ".";
|
|
|
|
if ( myColName.length() )
|
|
mapKey2 += (LPCTSTR)myColName;
|
|
|
|
long len = mapKey2.length();
|
|
char* buffer = new char[len + 1];
|
|
buffer[0] = 0;
|
|
wcstombs(buffer, mapKey2, len);
|
|
buffer[len] = 0;
|
|
return buffer;
|
|
// return (LPCTSTR)mapKey;
|
|
}
|
|
|
|
long VirtualInstanceManager::GetArrayIndex(LPCTSTR TableAlias, LPCTSTR ColumnName, long instanceNum)
|
|
{
|
|
long index = -1;
|
|
|
|
//First construct key for lookup map
|
|
char* tempbuff = ConstructKey(TableAlias, ColumnName);
|
|
_bstr_t mapKey(tempbuff);
|
|
delete tempbuff;
|
|
|
|
VirtualInstanceInfo* myInfo = NULL;
|
|
if ( virtualInfo->Lookup((LPCTSTR)mapKey, (void*&)myInfo) )
|
|
{
|
|
//Calculate index for given instance
|
|
//(Note: instance number is zero based)
|
|
long cCycle = myInfo->cCycle;
|
|
|
|
index = ( (instanceNum) / (myInfo->cCycle) );
|
|
|
|
index = index % (myInfo->cElements);
|
|
}
|
|
|
|
return index;
|
|
}
|
|
|
|
/*
|
|
void VirtualInstanceManager::Testy()
|
|
{
|
|
//Testing
|
|
VirtualInstanceInfo* myInfo = NULL;
|
|
|
|
//TableA
|
|
myInfo = new VirtualInstanceInfo();
|
|
myInfo->cElements = 2;
|
|
AddColumnInstance(_bstr_t("t1"), _bstr_t("TableA"), myInfo);
|
|
|
|
//TableB
|
|
myInfo = new VirtualInstanceInfo();
|
|
myInfo->cElements = 3;
|
|
AddColumnInstance(_bstr_t("t2"), _bstr_t("TableB"), myInfo);
|
|
|
|
//TableC
|
|
myInfo = new VirtualInstanceInfo();
|
|
myInfo->cElements = 2;
|
|
AddColumnInstance(_bstr_t("t3"), _bstr_t("TableC"), myInfo);
|
|
|
|
|
|
//Now fetch the index's for instance 0
|
|
long myindexA = 0;
|
|
long myindexB = 0;
|
|
long myindexC = 0;
|
|
|
|
myindexA = GetArrayIndex(_bstr_t("t1"), _bstr_t("TableA"), 0);
|
|
myindexB = GetArrayIndex(_bstr_t("t2"), _bstr_t("TableB"), 0);
|
|
myindexC = GetArrayIndex(_bstr_t("t3"), _bstr_t("TableC"), 0);
|
|
|
|
myindexA = GetArrayIndex(_bstr_t("t1"), _bstr_t("TableA"), 5);
|
|
myindexB = GetArrayIndex(_bstr_t("t2"), _bstr_t("TableB"), 5);
|
|
myindexC = GetArrayIndex(_bstr_t("t3"), _bstr_t("TableC"), 5);
|
|
|
|
|
|
myindexA = GetArrayIndex(_bstr_t("t1"), _bstr_t("TableA"), 6);
|
|
myindexB = GetArrayIndex(_bstr_t("t2"), _bstr_t("TableB"), 6);
|
|
myindexC = GetArrayIndex(_bstr_t("t3"), _bstr_t("TableC"), 6);
|
|
|
|
myindexA = GetArrayIndex(_bstr_t("t1"), _bstr_t("TableA"), 11);
|
|
myindexB = GetArrayIndex(_bstr_t("t2"), _bstr_t("TableB"), 11);
|
|
myindexC = GetArrayIndex(_bstr_t("t3"), _bstr_t("TableC"), 11);
|
|
|
|
}
|
|
*/
|
|
/***************************************************************************/
|
|
|
|
SQLNODEIDX INTFUNC AllocateNode(LPSQLTREE FAR *lplpSql, UWORD sqlNodeType)
|
|
|
|
/* Allocates a new node for the parse tree. */
|
|
/* */
|
|
/* If there is an available node, the index of that node is returned. */
|
|
/* Otherwise the array is expanded and the index of one of the newly */
|
|
/* created nodes is returned. */
|
|
/* */
|
|
/* lplpSql points to the node array. Its value may change. */
|
|
|
|
{
|
|
HGLOBAL hParseArray;
|
|
HGLOBAL hOldParseArray;
|
|
LPSQLTREE lpSql;
|
|
|
|
/* Is there space available? */
|
|
lpSql = *lplpSql;
|
|
if ((lpSql->node.root.iParseArray + 1) >= lpSql->node.root.cParseArray) {
|
|
|
|
/* No. Allocate more space */
|
|
hOldParseArray = lpSql->node.root.hParseArray;
|
|
lpSql->node.root.hParseArray = NULL;
|
|
GlobalUnlock(hOldParseArray);
|
|
hParseArray = GlobalReAlloc(hOldParseArray,
|
|
sizeof(SQLNODE) * (SQLNODE_ALLOC + lpSql->node.root.cParseArray),
|
|
GMEM_MOVEABLE);
|
|
if (hParseArray == NULL) {
|
|
GlobalFree(hOldParseArray);
|
|
return NO_SQLNODE;
|
|
}
|
|
lpSql = (LPSQLTREE) GlobalLock(hParseArray);
|
|
if (lpSql == NULL) {
|
|
GlobalFree(hParseArray);
|
|
return NO_SQLNODE;
|
|
}
|
|
*lplpSql = lpSql;
|
|
|
|
/* Update the root node */
|
|
lpSql->node.root.hParseArray = hParseArray;
|
|
lpSql->node.root.cParseArray += (SQLNODE_ALLOC);
|
|
}
|
|
|
|
/* Return the next node */
|
|
(lpSql->node.root.iParseArray)++;
|
|
|
|
/* Initialize the node */
|
|
lpSql[lpSql->node.root.iParseArray].sqlNodeType = sqlNodeType;
|
|
lpSql[lpSql->node.root.iParseArray].sqlDataType = TYPE_UNKNOWN;
|
|
lpSql[lpSql->node.root.iParseArray].sqlSqlType = SQL_TYPE_NULL;
|
|
lpSql[lpSql->node.root.iParseArray].sqlPrecision = 0;
|
|
lpSql[lpSql->node.root.iParseArray].sqlScale = NO_SCALE;
|
|
lpSql[lpSql->node.root.iParseArray].value.String = NULL;
|
|
lpSql[lpSql->node.root.iParseArray].value.Double = 0.0;
|
|
lpSql[lpSql->node.root.iParseArray].value.Date.year = 0;
|
|
lpSql[lpSql->node.root.iParseArray].value.Date.month = 0;
|
|
lpSql[lpSql->node.root.iParseArray].value.Date.day = 0;
|
|
lpSql[lpSql->node.root.iParseArray].value.Time.hour = 0;
|
|
lpSql[lpSql->node.root.iParseArray].value.Time.minute = 0;
|
|
lpSql[lpSql->node.root.iParseArray].value.Time.second = 0;
|
|
lpSql[lpSql->node.root.iParseArray].value.Timestamp.year = 0;
|
|
lpSql[lpSql->node.root.iParseArray].value.Timestamp.month = 0;
|
|
lpSql[lpSql->node.root.iParseArray].value.Timestamp.day = 0;
|
|
lpSql[lpSql->node.root.iParseArray].value.Timestamp.hour = 0;
|
|
lpSql[lpSql->node.root.iParseArray].value.Timestamp.minute = 0;
|
|
lpSql[lpSql->node.root.iParseArray].value.Timestamp.second = 0;
|
|
lpSql[lpSql->node.root.iParseArray].value.Timestamp.fraction = 0;
|
|
lpSql[lpSql->node.root.iParseArray].value.Binary = NULL;
|
|
lpSql[lpSql->node.root.iParseArray].sqlIsNull = TRUE;
|
|
|
|
return lpSql->node.root.iParseArray;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
STRINGIDX INTFUNC AllocateSpace(LPSQLTREE FAR *lplpSql, SWORD cbSize)
|
|
|
|
/* Allocates space from the string area of parse tree. */
|
|
/* */
|
|
/* If there is room, the index of the string is returned. Otherwise the */
|
|
/* string area is expanded and the index of the newly created string */
|
|
/* is returned. If the string area cannot be expanded, NO_STRING is */
|
|
/* returned. */
|
|
/* */
|
|
/* lpSql points to the node array. Its value may change. cbSize is */
|
|
/* the number of bytes needed. */
|
|
|
|
{
|
|
HGLOBAL hStringArea;
|
|
LPUSTR lpstr;
|
|
LPSQLTREE lpSql;
|
|
|
|
/* First time? */
|
|
lpSql = *lplpSql;
|
|
if (lpSql->node.root.hStringArea == NULL) {
|
|
|
|
/* Yes. Allocate initial space */
|
|
hStringArea = GlobalAlloc(GMEM_MOVEABLE, STRING_ALLOC);
|
|
if (hStringArea == NULL)
|
|
return NO_STRING;
|
|
lpstr = (LPUSTR) GlobalLock(hStringArea);
|
|
if (lpstr == NULL) {
|
|
GlobalFree(hStringArea);
|
|
return NO_STRING;
|
|
}
|
|
|
|
/* Update the root node */
|
|
lpSql->node.root.hStringArea = hStringArea;
|
|
lpSql->node.root.lpStringArea = lpstr;
|
|
lpSql->node.root.cbStringArea = STRING_ALLOC;
|
|
lpSql->node.root.ibStringArea = -1;
|
|
}
|
|
|
|
/* Is there enough space available? */
|
|
while ((lpSql->node.root.ibStringArea + cbSize) >= lpSql->node.root.cbStringArea) {
|
|
|
|
/* No. Allocate more space */
|
|
GlobalUnlock(lpSql->node.root.hStringArea);
|
|
hStringArea = GlobalReAlloc(lpSql->node.root.hStringArea,
|
|
STRING_ALLOC + lpSql->node.root.cbStringArea, GMEM_MOVEABLE);
|
|
if (hStringArea == NULL) {
|
|
GlobalFree(lpSql->node.root.hStringArea);
|
|
lpSql->node.root.hStringArea = NULL;
|
|
return NO_STRING;
|
|
}
|
|
lpstr = (LPUSTR) GlobalLock(hStringArea);
|
|
if (lpstr == NULL) {
|
|
GlobalFree(hStringArea);
|
|
lpSql->node.root.hStringArea = NULL;
|
|
return NO_STRING;
|
|
}
|
|
|
|
/* Update the root node */
|
|
lpSql->node.root.hStringArea = hStringArea;
|
|
lpSql->node.root.lpStringArea = lpstr;
|
|
lpSql->node.root.cbStringArea += (STRING_ALLOC);
|
|
}
|
|
|
|
/* Return the next node */
|
|
lpSql->node.root.ibStringArea += (cbSize);
|
|
return (lpSql->node.root.ibStringArea - cbSize + 1);
|
|
}
|
|
/***************************************************************************/
|
|
|
|
STRINGIDX INTFUNC AllocateString(LPSQLTREE FAR *lplpSql, LPUSTR lpszStr)
|
|
|
|
/* Allocates a string from the string area of parse tree. */
|
|
/* */
|
|
/* If there is room, the index of the string is returned. Otherwise the */
|
|
/* string area is expanded and the index of the newly created string */
|
|
/* is returned. If the string area cannot be expanded, NO_STRING is */
|
|
/* returned. */
|
|
/* */
|
|
/* lpSql points to the node array. Its value may change. */
|
|
|
|
{
|
|
STRINGIDX idx;
|
|
|
|
idx = AllocateSpace(lplpSql, (SWORD) (s_lstrlen(lpszStr) + 1));
|
|
if (idx == NO_STRING)
|
|
return NO_STRING;
|
|
s_lstrcpy(ToString(*lplpSql, idx), lpszStr);
|
|
return idx;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
void INTFUNC FreeTree(LPSQLTREE lpSql)
|
|
/* Deallocates a parse tree */
|
|
{
|
|
HGLOBAL hParseArray;
|
|
|
|
/* If nothing to do, just return */
|
|
if (lpSql == NULL)
|
|
return;
|
|
|
|
/* Free semantic information */
|
|
FreeTreeSemantic(lpSql, ROOT_SQLNODE);
|
|
|
|
/* Deallocate the string area */
|
|
if (lpSql->node.root.hStringArea != NULL) {
|
|
GlobalUnlock(lpSql->node.root.hStringArea);
|
|
GlobalFree(lpSql->node.root.hStringArea);
|
|
}
|
|
|
|
/* Deallocate the parse array */
|
|
hParseArray = lpSql->node.root.hParseArray;
|
|
if (hParseArray != NULL) {
|
|
GlobalUnlock(hParseArray);
|
|
GlobalFree(hParseArray);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************/
|
|
/***************************************************************************/
|
|
|
|
UWORD INTFUNC KeywordOrIdentifier(LPUSTR lpToken)
|
|
|
|
/* Determines if token is a keyword or an identifier */
|
|
|
|
{
|
|
LPUSTR FAR *lpKeyword;
|
|
|
|
lpKeyword = lpszKeywords;
|
|
while (*lpKeyword != NULL) {
|
|
if (!s_lstrcmpi(lpToken, *lpKeyword))
|
|
return TOKEN_TYPE_KEYWORD;
|
|
lpKeyword++;
|
|
}
|
|
return TOKEN_TYPE_IDENTIFIER;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC GetToken(LPSTMT lpstmt, LPUSTR lpFrom, SDWORD cbFrom,
|
|
LPUSTR lpToken, UWORD FAR *pfType,
|
|
LPUSTR FAR *lpRemainder, SDWORD FAR *pcbRemainder)
|
|
|
|
/* Retrives a token from input string, and returns the token and a pointer */
|
|
/* to the remainder of the input string. lpToken is assumed to be at */
|
|
/* least (MAX_TOKEN_SIZE + 1) bytes long. */
|
|
|
|
{
|
|
int len;
|
|
BOOL foundDecimalPoint;
|
|
BOOL foundExponent;
|
|
LPUSTR lpTo;
|
|
|
|
/* Remove leading blanks */
|
|
while ((cbFrom != 0) &&
|
|
((*lpFrom == ' ') ||
|
|
(*lpFrom == '\012') ||
|
|
(*lpFrom == '\015') ||
|
|
(*lpFrom == '\011'))) {
|
|
lpFrom++;
|
|
cbFrom--;
|
|
}
|
|
|
|
/* Leave if no more */
|
|
lpTo = lpToken;
|
|
if (cbFrom == 0) {
|
|
*lpTo = '\0';
|
|
*lpRemainder = lpFrom;
|
|
*pcbRemainder = cbFrom;
|
|
*pfType = TOKEN_TYPE_NONE;
|
|
return ERR_SUCCESS;
|
|
}
|
|
|
|
/* What kind of token? */
|
|
switch (*lpFrom) {
|
|
|
|
/* End of input */
|
|
case '\0':
|
|
*lpTo = '\0';
|
|
*lpRemainder = lpFrom;
|
|
*pcbRemainder = cbFrom;
|
|
*pfType = TOKEN_TYPE_NONE;
|
|
return ERR_SUCCESS;
|
|
|
|
/* Single character tokens */
|
|
case '(':
|
|
case ')':
|
|
case '=':
|
|
case '?':
|
|
case '*':
|
|
case ',':
|
|
case '+':
|
|
case '/':
|
|
case '{':
|
|
case '}':
|
|
|
|
*lpTo = *lpFrom;
|
|
lpTo++;
|
|
lpFrom++;
|
|
cbFrom--;
|
|
*lpTo = '\0';
|
|
*lpRemainder = lpFrom;
|
|
*pcbRemainder = cbFrom;
|
|
*pfType = TOKEN_TYPE_SYMBOL;
|
|
return ERR_SUCCESS;
|
|
|
|
/* - or -- */
|
|
case '-':
|
|
*lpTo = *lpFrom;
|
|
lpTo++;
|
|
lpFrom++;
|
|
cbFrom--;
|
|
if ((cbFrom != 0) && (*lpFrom == '-')) {
|
|
*lpTo = *lpFrom;
|
|
lpTo++;
|
|
lpFrom++;
|
|
cbFrom--;
|
|
}
|
|
*lpTo = '\0';
|
|
*lpRemainder = lpFrom;
|
|
*pcbRemainder = cbFrom;
|
|
*pfType = TOKEN_TYPE_SYMBOL;
|
|
return ERR_SUCCESS;
|
|
|
|
/* < or <= or <> */
|
|
case '<':
|
|
*lpTo = *lpFrom;
|
|
lpTo++;
|
|
lpFrom++;
|
|
cbFrom--;
|
|
if ((cbFrom != 0) && ((*lpFrom == '=') || (*lpFrom == '>'))) {
|
|
*lpTo = *lpFrom;
|
|
lpTo++;
|
|
lpFrom++;
|
|
cbFrom--;
|
|
}
|
|
*lpTo = '\0';
|
|
*lpRemainder = lpFrom;
|
|
*pcbRemainder = cbFrom;
|
|
*pfType = TOKEN_TYPE_SYMBOL;
|
|
return ERR_SUCCESS;
|
|
|
|
/* > or >= */
|
|
case '>':
|
|
*lpTo = *lpFrom;
|
|
lpTo++;
|
|
lpFrom++;
|
|
cbFrom--;
|
|
if ((cbFrom != 0) && (*lpFrom == '=')) {
|
|
*lpTo = *lpFrom;
|
|
lpTo++;
|
|
lpFrom++;
|
|
cbFrom--;
|
|
}
|
|
*lpTo = '\0';
|
|
*lpRemainder = lpFrom;
|
|
*pcbRemainder = cbFrom;
|
|
*pfType = TOKEN_TYPE_SYMBOL;
|
|
return ERR_SUCCESS;
|
|
|
|
/* Numbers */
|
|
case '.':
|
|
if (cbFrom == 1) {
|
|
*lpTo = *lpFrom;
|
|
lpTo++;
|
|
lpFrom++;
|
|
cbFrom--;
|
|
*lpTo = '\0';
|
|
*lpRemainder = lpFrom;
|
|
*pcbRemainder = cbFrom;
|
|
*pfType = TOKEN_TYPE_SYMBOL;
|
|
return ERR_SUCCESS;
|
|
}
|
|
switch (*(lpFrom+1)) {
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
break;
|
|
default:
|
|
*lpTo = *lpFrom;
|
|
lpTo++;
|
|
lpFrom++;
|
|
cbFrom--;
|
|
*lpTo = '\0';
|
|
*lpRemainder = lpFrom;
|
|
*pcbRemainder = cbFrom;
|
|
*pfType = TOKEN_TYPE_SYMBOL;
|
|
return ERR_SUCCESS;
|
|
}
|
|
/* **** DROP DOWN TO NEXT CASE **** */
|
|
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
/* **** CONTROL MAY COME HERE FROM PREVIOUS CASE **** */
|
|
|
|
len = 0;
|
|
foundDecimalPoint = FALSE;
|
|
foundExponent = FALSE;
|
|
while (TRUE) {
|
|
if (cbFrom == 0) {
|
|
*lpTo = '\0';
|
|
*lpRemainder = lpFrom;
|
|
*pcbRemainder = cbFrom;
|
|
*pfType = TOKEN_TYPE_NUMBER;
|
|
return ERR_SUCCESS;
|
|
}
|
|
switch (*lpFrom) {
|
|
case '.':
|
|
if (foundDecimalPoint || foundExponent) {
|
|
*lpTo = '\0';
|
|
s_lstrcpy(lpstmt->szError, lpToken);
|
|
return ERR_MALFORMEDNUMBER;
|
|
}
|
|
foundDecimalPoint = TRUE;
|
|
/* **** DROP DOWN TO NEXT CASE **** */
|
|
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
/* **** CONTROL MAY COME HERE FROM PREVIOUS CASE **** */
|
|
break;
|
|
case 'e':
|
|
case 'E':
|
|
if (foundExponent) {
|
|
*lpTo = '\0';
|
|
s_lstrcpy(lpstmt->szError, lpToken);
|
|
return ERR_MALFORMEDNUMBER;
|
|
}
|
|
foundExponent = TRUE;
|
|
if ((cbFrom > 1) && ((*(lpFrom+1) == '+') ||
|
|
(*(lpFrom+1) == '-'))) {
|
|
if (len >= MAX_TOKEN_SIZE) {
|
|
*lpTo = '\0';
|
|
s_lstrcpy(lpstmt->szError, lpToken);
|
|
return ERR_ELEMENTTOOBIG;
|
|
}
|
|
len++;
|
|
*lpTo = *lpFrom;
|
|
lpTo++;
|
|
lpFrom++;
|
|
cbFrom--;
|
|
}
|
|
if (cbFrom < 2) {
|
|
*lpTo = '\0';
|
|
s_lstrcpy(lpstmt->szError, lpToken);
|
|
return ERR_MALFORMEDNUMBER;
|
|
}
|
|
break;
|
|
default:
|
|
*lpTo = '\0';
|
|
*lpRemainder = lpFrom;
|
|
*pcbRemainder = cbFrom;
|
|
*pfType = TOKEN_TYPE_NUMBER;
|
|
return ERR_SUCCESS;
|
|
}
|
|
if (len >= MAX_TOKEN_SIZE) {
|
|
*lpTo = '\0';
|
|
s_lstrcpy(lpstmt->szError, lpToken);
|
|
return ERR_ELEMENTTOOBIG;
|
|
}
|
|
len++;
|
|
|
|
*lpTo = *lpFrom;
|
|
lpTo++;
|
|
lpFrom++;
|
|
cbFrom--;
|
|
}
|
|
break; /* Control should never get here */
|
|
|
|
/* Character literals */
|
|
case '\'':
|
|
lpFrom++;
|
|
cbFrom--;
|
|
len = 0;
|
|
while (TRUE) {
|
|
|
|
if (cbFrom == 0)
|
|
return ERR_UNEXPECTEDEND;
|
|
|
|
switch (*lpFrom) {
|
|
case '\0':
|
|
return ERR_UNEXPECTEDEND;
|
|
|
|
case '\'':
|
|
lpFrom++;
|
|
cbFrom--;
|
|
if ((cbFrom == 0) || (*lpFrom != '\'')) {
|
|
*lpTo = '\0';
|
|
*lpRemainder = lpFrom;
|
|
*pcbRemainder = cbFrom;
|
|
*pfType = TOKEN_TYPE_STRING;
|
|
return ERR_SUCCESS;
|
|
}
|
|
/* **** DROP DOWN TO NEXT CASE **** */
|
|
|
|
default:
|
|
/* **** CONTROL MAY COME HERE FROM PREVIOUS CASE **** */
|
|
break;
|
|
}
|
|
if (len >= MAX_TOKEN_SIZE) {
|
|
*lpTo = '\0';
|
|
s_lstrcpy(lpstmt->szError, lpToken);
|
|
return ERR_ELEMENTTOOBIG;
|
|
}
|
|
len++;
|
|
|
|
*lpTo = *lpFrom;
|
|
lpTo++;
|
|
lpFrom++;
|
|
cbFrom--;
|
|
}
|
|
break; /* Control should never get here */
|
|
|
|
/* Quoted identifiers */
|
|
case '"':
|
|
len = 0;
|
|
lpFrom++;
|
|
cbFrom--;
|
|
while (TRUE) {
|
|
|
|
if (cbFrom == 0)
|
|
return ERR_UNEXPECTEDEND;
|
|
|
|
switch (*lpFrom) {
|
|
case '\0':
|
|
return ERR_UNEXPECTEDEND;
|
|
|
|
case '"':
|
|
lpFrom++;
|
|
cbFrom--;
|
|
*lpTo = '\0';
|
|
*lpRemainder = lpFrom;
|
|
*pcbRemainder = cbFrom;
|
|
*pfType = TOKEN_TYPE_IDENTIFIER;
|
|
return ERR_SUCCESS;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
if (len >= MAX_TOKEN_SIZE) {
|
|
*lpTo = '\0';
|
|
s_lstrcpy(lpstmt->szError, lpToken);
|
|
return ERR_ELEMENTTOOBIG;
|
|
}
|
|
len++;
|
|
|
|
*lpTo = *lpFrom;
|
|
lpTo++;
|
|
lpFrom++;
|
|
cbFrom--;
|
|
}
|
|
break; /* Control should never get here */
|
|
|
|
/* Other identifiers (and keywords) */
|
|
default:
|
|
len = 0;
|
|
while (TRUE) {
|
|
if (cbFrom == 0) {
|
|
*lpTo = '\0';
|
|
*lpRemainder = lpFrom;
|
|
*pcbRemainder = cbFrom;
|
|
*pfType = KeywordOrIdentifier(lpToken);
|
|
return ERR_SUCCESS;
|
|
}
|
|
|
|
switch (*lpFrom) {
|
|
case ' ':
|
|
case '\012':
|
|
case '\015':
|
|
case '\011':
|
|
case '\0':
|
|
case '(':
|
|
case ')':
|
|
case '=':
|
|
case '?':
|
|
case '*':
|
|
case ',':
|
|
case '.':
|
|
case '<':
|
|
case '>':
|
|
case '-':
|
|
case '+':
|
|
case '/':
|
|
case '{':
|
|
case '}':
|
|
*lpTo = '\0';
|
|
*lpRemainder = lpFrom;
|
|
*pcbRemainder = cbFrom;
|
|
*pfType = KeywordOrIdentifier(lpToken);
|
|
return ERR_SUCCESS;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
if (len >= MAX_TOKEN_SIZE) {
|
|
*lpTo = '\0';
|
|
s_lstrcpy(lpstmt->szError, lpToken);
|
|
return ERR_ELEMENTTOOBIG;
|
|
}
|
|
len++;
|
|
|
|
*lpTo = *lpFrom;
|
|
lpTo++;
|
|
lpFrom++;
|
|
cbFrom--;
|
|
}
|
|
break; /* Control should never get here */
|
|
}
|
|
/* Control never gets here */
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC GetEscape(LPSTMT lpstmt, LPUSTR lpFrom, SDWORD cbFrom,
|
|
LPUSTR lpToken, LPUSTR FAR *lpRemainder,
|
|
SDWORD FAR *pcbRemainder)
|
|
|
|
/* Retrives an escape sequence from input string, and returns it as */
|
|
/* a token and returns a pointer to the remainder of the input string. */
|
|
/* lpToken is assumed to be at least (MAX_TOKEN_SIZE + 1) bytes long. */
|
|
|
|
{
|
|
int len;
|
|
LPUSTR lpTo;
|
|
|
|
/* Remove leading blanks */
|
|
while ((cbFrom != 0) &&
|
|
((*lpFrom == ' ') ||
|
|
(*lpFrom == '\012') ||
|
|
(*lpFrom == '\015') ||
|
|
(*lpFrom == '\011'))) {
|
|
lpFrom++;
|
|
cbFrom--;
|
|
}
|
|
|
|
/* Error if no more */
|
|
lpTo = lpToken;
|
|
if (cbFrom == 0) {
|
|
*lpTo = '\0';
|
|
*lpRemainder = lpFrom;
|
|
*pcbRemainder = cbFrom;
|
|
return ERR_UNEXPECTEDEND;
|
|
}
|
|
|
|
/* What kind of escape? */
|
|
switch (*lpFrom) {
|
|
|
|
/* Shorthand sequences */
|
|
case '{':
|
|
|
|
/* Get sequence */
|
|
len = 0;
|
|
while (TRUE) {
|
|
if (cbFrom == 0)
|
|
return ERR_UNEXPECTEDEND;
|
|
|
|
/* End of seqeuence? */
|
|
if (*lpFrom == '}') {
|
|
|
|
/* Yes. Return it */
|
|
*lpTo = *lpFrom;
|
|
lpTo++;
|
|
lpFrom++;
|
|
cbFrom--;
|
|
|
|
*lpTo = '\0';
|
|
*lpRemainder = lpFrom;
|
|
*pcbRemainder = cbFrom;
|
|
return ERR_SUCCESS;
|
|
}
|
|
|
|
if (len >= MAX_TOKEN_SIZE) {
|
|
*lpTo = '\0';
|
|
s_lstrcpy(lpstmt->szError, lpToken);
|
|
return ERR_ELEMENTTOOBIG;
|
|
}
|
|
len++;
|
|
|
|
*lpTo = *lpFrom;
|
|
lpTo++;
|
|
lpFrom++;
|
|
cbFrom--;
|
|
}
|
|
break;
|
|
|
|
/* Escape sequences */
|
|
case '-':
|
|
if ((cbFrom > 1) && (*(lpFrom+1) == '-')) {
|
|
|
|
/* Get starting hyphen */
|
|
*lpTo = *lpFrom;
|
|
lpTo++;
|
|
lpFrom++;
|
|
cbFrom--;
|
|
|
|
/* Get rest of sequence */
|
|
len = 0;
|
|
while (TRUE) {
|
|
if (cbFrom == 0)
|
|
return ERR_UNEXPECTEDEND;
|
|
|
|
/* End of seqeuence? */
|
|
if ((*lpFrom == '-') && (cbFrom > 1) && (*(lpFrom+1) == '-')) {
|
|
|
|
/* Yes. Return it */
|
|
*lpTo = *lpFrom;
|
|
lpTo++;
|
|
lpFrom++;
|
|
cbFrom--;
|
|
|
|
*lpTo = *lpFrom;
|
|
lpTo++;
|
|
lpFrom++;
|
|
cbFrom--;
|
|
|
|
*lpTo = '\0';
|
|
*lpRemainder = lpFrom;
|
|
*pcbRemainder = cbFrom;
|
|
return ERR_SUCCESS;
|
|
}
|
|
|
|
if (len >= MAX_TOKEN_SIZE-1) {
|
|
*lpTo = '\0';
|
|
s_lstrcpy(lpstmt->szError, lpToken);
|
|
return ERR_ELEMENTTOOBIG;
|
|
}
|
|
len++;
|
|
|
|
*lpTo = *lpFrom;
|
|
lpTo++;
|
|
lpFrom++;
|
|
cbFrom--;
|
|
}
|
|
}
|
|
else
|
|
return ERR_UNEXPECTEDEND;
|
|
|
|
default:
|
|
s_lstrcpy(lpstmt->szError, "<escape-sequence>");
|
|
return ERR_EXPECTEDOTHER;
|
|
}
|
|
/* Control never gets here */
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC GetSymbol(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSTR lpszSymbol,
|
|
LPUSTR lpszToken)
|
|
|
|
/* Retrives a symbol from the input stream. If lpszSymbol is not null, */
|
|
/* ERR_EXPECTEDOTHER is returned if the token found is not lpszSymbol. */
|
|
|
|
{
|
|
UWORD fType;
|
|
RETCODE err;
|
|
|
|
/* Get Identifier */
|
|
err = GetToken(lpstmt, *lplpszSqlStr, *pcbSqlStr, lpszToken, &fType,
|
|
lplpszSqlStr, pcbSqlStr);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
if (fType != TOKEN_TYPE_SYMBOL) {
|
|
if (lpszSymbol != NULL)
|
|
s_lstrcpy(lpstmt->szError, lpszSymbol);
|
|
else
|
|
s_lstrcpy(lpstmt->szError, "<symbol>");
|
|
return ERR_EXPECTEDOTHER;
|
|
}
|
|
|
|
/* Check value */
|
|
if (lpszSymbol != NULL) {
|
|
if (s_lstrcmpi(lpszSymbol, lpszToken)) {
|
|
s_lstrcpy(lpstmt->szError, lpszSymbol);
|
|
return ERR_EXPECTEDOTHER;
|
|
}
|
|
}
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC GetInteger(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, UDWORD FAR *pudValue,
|
|
LPUSTR lpszToken)
|
|
|
|
/* Retrives an integer number from the input stream. The number is */
|
|
/* returned in both pudValue and lpszToken */
|
|
|
|
{
|
|
UWORD fType;
|
|
RETCODE err;
|
|
|
|
/* Get Identifier */
|
|
err = GetToken(lpstmt, *lplpszSqlStr, *pcbSqlStr, lpszToken, &fType,
|
|
lplpszSqlStr, pcbSqlStr);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
if (fType != TOKEN_TYPE_NUMBER) {
|
|
s_lstrcpy(lpstmt->szError, lpszToken);
|
|
return ERR_MALFORMEDNUMBER;
|
|
}
|
|
|
|
*pudValue = 0;
|
|
while (*lpszToken) {
|
|
switch (*lpszToken) {
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
*pudValue = ((*pudValue) * 10) + (*lpszToken - '0');
|
|
break;
|
|
default:
|
|
s_lstrcpy(lpstmt->szError, lpszToken);
|
|
return ERR_MALFORMEDNUMBER;
|
|
}
|
|
lpszToken++;
|
|
}
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC GetNumber(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, double FAR *lpDouble,
|
|
LPUSTR lpszToken, SWORD FAR *lpDatatype,
|
|
SWORD FAR *lpScale)
|
|
|
|
/* Retrives a real number from the input stream. The number is returned */
|
|
/* in both lpDouble and lpszToken */
|
|
|
|
{
|
|
UWORD fType;
|
|
LPUSTR lpChar;
|
|
BOOL neg;
|
|
RETCODE err;
|
|
UCHAR szTempToken[MAX_TOKEN_SIZE + 1];
|
|
BOOL negExponent;
|
|
SWORD exponent;
|
|
SWORD iScale;
|
|
double scaleFactor;
|
|
|
|
/* Get Identifier */
|
|
err = GetToken(lpstmt, *lplpszSqlStr, *pcbSqlStr, lpszToken, &fType,
|
|
lplpszSqlStr, pcbSqlStr);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Figure out if value is negative */
|
|
if ((fType == TOKEN_TYPE_SYMBOL) && (!s_lstrcmpi("-", lpszToken))) {
|
|
neg = TRUE;
|
|
err = GetToken(lpstmt, *lplpszSqlStr, *pcbSqlStr, lpszToken, &fType,
|
|
lplpszSqlStr, pcbSqlStr);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
else if ((fType == TOKEN_TYPE_SYMBOL) && (!s_lstrcmpi("+", lpszToken))) {
|
|
neg = FALSE;
|
|
err = GetToken(lpstmt, *lplpszSqlStr, *pcbSqlStr, lpszToken, &fType,
|
|
lplpszSqlStr, pcbSqlStr);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
else
|
|
neg = FALSE;
|
|
|
|
/* Error if no number */
|
|
if (fType != TOKEN_TYPE_NUMBER) {
|
|
s_lstrcpy(lpstmt->szError, "<number>");
|
|
return ERR_EXPECTEDOTHER;
|
|
}
|
|
|
|
/* Error if number is too big */
|
|
if (s_lstrlen(lpszToken) >= MAX_TOKEN_SIZE) {
|
|
s_lstrcpy(lpstmt->szError, lpszToken);
|
|
return ERR_MALFORMEDNUMBER;
|
|
}
|
|
|
|
/* Convert the number from a text string. At the same time, figure */
|
|
/* out its datatype */
|
|
negExponent = FALSE;
|
|
exponent = 0;
|
|
*lpDouble = 0.0;
|
|
lpChar = lpszToken;
|
|
*lpDatatype = TYPE_INTEGER;
|
|
*lpScale = 0;
|
|
while (*lpChar != '\0') {
|
|
switch (*lpChar) {
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
|
|
/* Add the digit into the result */
|
|
switch (*lpDatatype) {
|
|
case TYPE_DOUBLE:
|
|
exponent = (exponent * 10) + (*lpChar - '0');
|
|
break;
|
|
case TYPE_NUMERIC:
|
|
*lpDouble = (*lpDouble * 10.0) + (*lpChar - '0');
|
|
(*lpScale)++;
|
|
break;
|
|
case TYPE_INTEGER:
|
|
*lpDouble = (*lpDouble * 10.0) + (*lpChar - '0');
|
|
break;
|
|
case TYPE_CHAR:
|
|
case TYPE_BINARY:
|
|
case TYPE_DATE:
|
|
case TYPE_TIME:
|
|
case TYPE_TIMESTAMP:
|
|
default:
|
|
return ERR_INTERNAL;
|
|
}
|
|
break;
|
|
case '.':
|
|
|
|
/* If a decimal point, this is a numeric value */
|
|
if (*lpDatatype != TYPE_INTEGER) {
|
|
s_lstrcpy(lpstmt->szError, lpszToken);
|
|
return ERR_MALFORMEDNUMBER;
|
|
}
|
|
*lpDatatype = TYPE_NUMERIC;
|
|
break;
|
|
|
|
case 'e':
|
|
case 'E':
|
|
|
|
/* If an E, this is a double value */
|
|
*lpDatatype = TYPE_DOUBLE;
|
|
|
|
/* Get sign of exponent */
|
|
if (*(lpChar+1) == '-') {
|
|
negExponent = TRUE;
|
|
lpChar++;
|
|
}
|
|
else if (*(lpChar+1) == '+') {
|
|
lpChar++;
|
|
}
|
|
break;
|
|
default:
|
|
s_lstrcpy(lpstmt->szError, lpszToken);
|
|
return ERR_MALFORMEDNUMBER;
|
|
}
|
|
lpChar++;
|
|
}
|
|
|
|
/* If scale of a numeric value is zero, remove trailing decimal point */
|
|
if ((*lpDatatype == TYPE_NUMERIC) && (*lpScale == 0)) {
|
|
lpszToken[s_lstrlen(lpszToken)-1] = '\0';
|
|
*lpDatatype = TYPE_INTEGER;
|
|
}
|
|
|
|
/* Return the number as a string */
|
|
if (neg) {
|
|
s_lstrcpy(szTempToken, lpszToken);
|
|
s_lstrcpy(lpszToken, "-");
|
|
s_lstrcat(lpszToken, szTempToken);
|
|
}
|
|
|
|
/* Adjust for scale */
|
|
iScale = -(*lpScale);
|
|
if (negExponent)
|
|
iScale -= (exponent);
|
|
else
|
|
iScale += (exponent);
|
|
|
|
scaleFactor = 1.0;
|
|
while (iScale > 0) {
|
|
scaleFactor = scaleFactor * 10.0;
|
|
iScale--;
|
|
}
|
|
*lpDouble = *lpDouble * scaleFactor;
|
|
|
|
scaleFactor = 1.0;
|
|
while (iScale < 0) {
|
|
scaleFactor = scaleFactor * 10.0;
|
|
iScale++;
|
|
}
|
|
*lpDouble = *lpDouble / scaleFactor;
|
|
|
|
/* Adjust for sign */
|
|
if (neg)
|
|
*lpDouble = -(*lpDouble);
|
|
|
|
/* Adjust scale */
|
|
if (*lpDatatype == TYPE_DOUBLE)
|
|
*lpScale = NO_SCALE;
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC GetIdent(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSTR lpszIdent,
|
|
LPUSTR lpszToken)
|
|
|
|
/* Retrives an identifier from the input stream. The value is returned. */
|
|
|
|
{
|
|
UWORD fType;
|
|
LPUSTR lpFrom;
|
|
SDWORD cbFrom;
|
|
RETCODE err;
|
|
|
|
/* Remove leading blanks */
|
|
lpFrom = *lplpszSqlStr;
|
|
cbFrom = *pcbSqlStr;
|
|
while ((cbFrom != 0) &&
|
|
((*lpFrom == ' ') ||
|
|
(*lpFrom == '\012') ||
|
|
(*lpFrom == '\015') ||
|
|
(*lpFrom == '\011'))) {
|
|
lpFrom++;
|
|
cbFrom--;
|
|
}
|
|
if (cbFrom == 0)
|
|
return ERR_UNEXPECTEDEND;
|
|
if (*lpFrom == '"') {
|
|
if (lpszIdent != NULL)
|
|
s_lstrcpy(lpstmt->szError, lpszIdent);
|
|
else
|
|
s_lstrcpy(lpstmt->szError, "<keyword>");
|
|
return ERR_EXPECTEDOTHER;
|
|
}
|
|
|
|
/* Get Identifier */
|
|
err = GetToken(lpstmt, *lplpszSqlStr, *pcbSqlStr, lpszToken, &fType,
|
|
lplpszSqlStr, pcbSqlStr);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
if ((fType != TOKEN_TYPE_IDENTIFIER) && (fType != TOKEN_TYPE_KEYWORD)) {
|
|
if (lpszIdent != NULL)
|
|
s_lstrcpy(lpstmt->szError, lpszIdent);
|
|
else
|
|
s_lstrcpy(lpstmt->szError, "<identifier>");
|
|
return ERR_EXPECTEDOTHER;
|
|
}
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC GetIdentifier(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
STRINGIDX FAR *lpIdx, LPUSTR lpszToken)
|
|
|
|
/* Retrives an identifier from the input stream and puts it into the */
|
|
/* string area. The value is also returned. */
|
|
|
|
{
|
|
UWORD fType;
|
|
RETCODE err;
|
|
|
|
/* Get Identifier */
|
|
err = GetToken(lpstmt, *lplpszSqlStr, *pcbSqlStr, lpszToken, &fType,
|
|
lplpszSqlStr, pcbSqlStr);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
if (fType != TOKEN_TYPE_IDENTIFIER) {
|
|
s_lstrcpy(lpstmt->szError, "<identifier>");
|
|
return ERR_EXPECTEDOTHER;
|
|
}
|
|
|
|
/* Put value into the string area */
|
|
*lpIdx = AllocateString(lplpSql, lpszToken);
|
|
if (*lpIdx == NO_STRING)
|
|
return ERR_MEMALLOCFAIL;
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC GetKeyword(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSTR lpszKeyword,
|
|
LPUSTR lpszToken)
|
|
|
|
/* Retrives a keyword from the input stream. If lpszKeyword is not null, */
|
|
/* ERR_EXPECTEDOTHER is returned if the token found is not lpszKeyword. */
|
|
|
|
{
|
|
UWORD fType;
|
|
LPUSTR lpFrom;
|
|
SDWORD cbFrom;
|
|
RETCODE err;
|
|
|
|
/* Remove leading blanks */
|
|
lpFrom = *lplpszSqlStr;
|
|
cbFrom = *pcbSqlStr;
|
|
while ((cbFrom != 0) &&
|
|
((*lpFrom == ' ') ||
|
|
(*lpFrom == '\012') ||
|
|
(*lpFrom == '\015') ||
|
|
(*lpFrom == '\011'))) {
|
|
lpFrom++;
|
|
cbFrom--;
|
|
}
|
|
if (cbFrom == 0)
|
|
return ERR_UNEXPECTEDEND;
|
|
if (*lpFrom == '"') {
|
|
if (lpszKeyword != NULL)
|
|
s_lstrcpy(lpstmt->szError, lpszKeyword);
|
|
else
|
|
s_lstrcpy(lpstmt->szError, "<keyword>");
|
|
return ERR_EXPECTEDOTHER;
|
|
}
|
|
|
|
/* Get Identifier */
|
|
err = GetToken(lpstmt, *lplpszSqlStr, *pcbSqlStr, lpszToken, &fType,
|
|
lplpszSqlStr, pcbSqlStr);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
if (fType != TOKEN_TYPE_KEYWORD) {
|
|
if (lpszKeyword != NULL)
|
|
s_lstrcpy(lpstmt->szError, lpszKeyword);
|
|
else
|
|
s_lstrcpy(lpstmt->szError, "<keyword>");
|
|
return ERR_EXPECTEDOTHER;
|
|
}
|
|
|
|
/* Check value */
|
|
if (lpszKeyword != NULL) {
|
|
if (s_lstrcmpi(lpszKeyword, lpszToken)) {
|
|
s_lstrcpy(lpstmt->szError, lpszKeyword);
|
|
return ERR_EXPECTEDOTHER;
|
|
}
|
|
}
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC GetString(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
STRINGIDX FAR *lpIdx, LPUSTR lpszToken)
|
|
|
|
/* Retrives a string from the input stream and puts it into the string */
|
|
/* area. The value is also returned. */
|
|
|
|
{
|
|
UWORD fType;
|
|
RETCODE err;
|
|
|
|
/* Get Identifier */
|
|
err = GetToken(lpstmt, *lplpszSqlStr, *pcbSqlStr, lpszToken, &fType,
|
|
lplpszSqlStr, pcbSqlStr);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
if (fType != TOKEN_TYPE_STRING) {
|
|
s_lstrcpy(lpstmt->szError, "<string>");
|
|
return ERR_EXPECTEDOTHER;
|
|
}
|
|
|
|
/* Put value into the string area */
|
|
*lpIdx = AllocateString(lplpSql, lpszToken);
|
|
if (*lpIdx == NO_STRING)
|
|
return ERR_MEMALLOCFAIL;
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParseTable(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, LPUSTR lpszToken)
|
|
|
|
/* Retrives a tablename from the input stream and creates a */
|
|
/* NODE_TYPE_TABLE node */
|
|
/* */
|
|
/* table ::= tablename */
|
|
|
|
{
|
|
RETCODE err;
|
|
LPSQLNODE lpSqlNode;
|
|
STRINGIDX idxName;
|
|
STRINGIDX idxQualifier;
|
|
|
|
/* Get the tablename */
|
|
err = GetIdentifier(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxName, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
LPUSTR old_lpszSqlStr = *lplpszSqlStr;
|
|
SDWORD old_cbSqlStr = *pcbSqlStr;
|
|
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, ".", lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* No. unqualified table name */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
idxQualifier = NO_STRING;
|
|
}
|
|
else {
|
|
|
|
/* Get the real table name */
|
|
idxQualifier = idxName;
|
|
err = GetIdentifier(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxName, lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* This must be a simple table name. */ // seems a bit dgy, see ParseColRef
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
idxName = idxQualifier;
|
|
idxQualifier = NO_STRING;
|
|
}
|
|
}
|
|
|
|
/* Create the TABLE node */
|
|
*lpIdx = AllocateNode(lplpSql, NODE_TYPE_TABLE);
|
|
if (*lpIdx == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
lpSqlNode = ToNode(*lplpSql, *lpIdx);
|
|
lpSqlNode->node.table.Name = idxName;
|
|
lpSqlNode->node.table.Qualifier = idxQualifier;
|
|
lpSqlNode->node.table.Alias = NO_STRING;
|
|
lpSqlNode->node.table.OuterJoinFromTables = NO_SQLNODE;
|
|
lpSqlNode->node.table.OuterJoinPredicate = NO_SQLNODE;
|
|
lpSqlNode->node.table.Handle = NULL;
|
|
lpSqlNode->node.table.cRestrict = 0;
|
|
lpSqlNode->node.table.Restrict = NO_SQLNODE;
|
|
lpSqlNode->node.table.Sortsequence = 0;
|
|
lpSqlNode->node.table.Sortcount = 0;
|
|
lpSqlNode->node.table.Sortcolumns = NO_SQLNODE;
|
|
lpSqlNode->node.table.AllNull = FALSE;
|
|
lpSqlNode->node.table.Rewound = TRUE;
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParseTableref(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, LPUSTR lpszToken)
|
|
|
|
/* Retrives a table reference from the input stream and creates a */
|
|
/* NODE_TYPE_TABLE node */
|
|
/* */
|
|
/* tableref ::= table aliasname | table */
|
|
|
|
{
|
|
RETCODE err;
|
|
LPUSTR old_lpszSqlStr;
|
|
SDWORD old_cbSqlStr;
|
|
LPSQLNODE lpSqlNode;
|
|
STRINGIDX idxAlias;
|
|
|
|
/* Get the table */
|
|
err = ParseTable(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql, lpIdx,
|
|
lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Alias given? */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetIdentifier(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql, &idxAlias,
|
|
lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* No. No alias given */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
idxAlias = NO_STRING;
|
|
}
|
|
else {
|
|
|
|
/* Yes. Save the alias */
|
|
lpSqlNode = ToNode(*lplpSql, *lpIdx);
|
|
lpSqlNode->node.table.Alias = idxAlias;
|
|
}
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParseOJ(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdxLeft, SQLNODEIDX FAR *lpIdxRight,
|
|
LPUSTR lpszToken)
|
|
|
|
/* Retrives an outer join from the input stream and creates two */
|
|
/* NODE_TYPE_TABLES nodes which are linked together. */
|
|
/* */
|
|
/* oj := tableref LEFT OUTER JOIN tableref ON boolean | */
|
|
/* tableref LEFT OUTER JOIN oj ON boolean */
|
|
|
|
{
|
|
RETCODE err;
|
|
SQLNODEIDX idxTableLeft;
|
|
SQLNODEIDX idxTableRight;
|
|
SQLNODEIDX idxTablesRight;
|
|
SQLNODEIDX idxPredicate;
|
|
LPSQLNODE lpSqlNode;
|
|
LPUSTR old_lpszSqlStr;
|
|
SDWORD old_cbSqlStr;
|
|
|
|
/* Get left table */
|
|
err = ParseTableref(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxTableLeft, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Get LEFT OUTER JOIN keywords */
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr, "LEFT", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr, "OUTER", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr, "JOIN", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Get nested outer join */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = ParseOJ(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxTablesRight, lpIdxRight, lpszToken);
|
|
if (err == ERR_SUCCESS) {
|
|
lpSqlNode = ToNode(*lplpSql, idxTablesRight);
|
|
idxTableRight = lpSqlNode->node.tables.Table;
|
|
}
|
|
else {
|
|
|
|
/* Not a nested outer join. Just get right table */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
err = ParseTableref(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxTableRight, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Create the TABLES node for the right table */
|
|
idxTablesRight = AllocateNode(lplpSql, NODE_TYPE_TABLES);
|
|
if (idxTablesRight == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
lpSqlNode = ToNode(*lplpSql, idxTablesRight);
|
|
lpSqlNode->node.tables.Table = idxTableRight;
|
|
lpSqlNode->node.tables.Next = NO_SQLNODE;
|
|
*lpIdxRight = idxTablesRight;
|
|
}
|
|
|
|
/* Get ON keyword */
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr, "ON", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Get condition */
|
|
err = ParseBoolean(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxPredicate, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Create the TABLES node for the left table */
|
|
*lpIdxLeft = AllocateNode(lplpSql, NODE_TYPE_TABLES);
|
|
if (*lpIdxLeft == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
lpSqlNode = ToNode(*lplpSql, *lpIdxLeft);
|
|
lpSqlNode->node.tables.Table = idxTableLeft;
|
|
lpSqlNode->node.tables.Next = idxTablesRight;
|
|
|
|
/* Put in outer join information into the right table node */
|
|
lpSqlNode = ToNode(*lplpSql, idxTableRight);
|
|
lpSqlNode->node.table.OuterJoinFromTables = *lpIdxLeft;
|
|
lpSqlNode->node.table.OuterJoinPredicate = idxPredicate;
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParseOuterJoin(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdxLeft, SQLNODEIDX FAR *lpIdxRight,
|
|
LPUSTR lpszToken)
|
|
|
|
/* Retrives an outer join from the input stream and creates two */
|
|
/* NODE_TYPE_TABLES nodes which are linked together. */
|
|
/* */
|
|
/* ojshorthand := { OJ oj } */
|
|
/* ojescape := --*(VENDOR(MICROSOFT),PRODUCT(ODBC) OJ oj)*-- */
|
|
/* outerjoin ::= ojescape | ojshorthand */
|
|
|
|
{
|
|
RETCODE err;
|
|
BOOL fShorthand;
|
|
|
|
/* Get starting symbol */
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, NULL, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Shorthand? */
|
|
if (s_lstrcmpi(lpszToken, "{")) {
|
|
|
|
/* No. Not a shorthand */
|
|
fShorthand = FALSE;
|
|
|
|
/* If not an escape clause, error */
|
|
if (s_lstrcmpi(lpszToken, "--")) {
|
|
s_lstrcpy(lpstmt->szError, "--");
|
|
return ERR_EXPECTEDOTHER;
|
|
}
|
|
|
|
/* Get the rest of the starting sequence */
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, "(", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, "*", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
err = GetIdent(lpstmt, lplpszSqlStr, pcbSqlStr, "vendor", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, "(", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
err = GetIdent(lpstmt, lplpszSqlStr, pcbSqlStr, "Microsoft", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, ")", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, ",", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
err = GetIdent(lpstmt, lplpszSqlStr, pcbSqlStr, "product", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, "(", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
err = GetIdent(lpstmt, lplpszSqlStr, pcbSqlStr, "ODBC", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, ")", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
else
|
|
fShorthand = TRUE;
|
|
|
|
/* Get OJ keyword */
|
|
err = GetIdent(lpstmt, lplpszSqlStr, pcbSqlStr, "oj", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Get outer join tables and condition */
|
|
err = ParseOJ(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
lpIdxLeft, lpIdxRight, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Get closing bracket */
|
|
if (!fShorthand) {
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, "*", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, ")", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, "--", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
else {
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, "}", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParseTablelist(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, LPUSTR lpszToken)
|
|
|
|
/* Retrives a list of tablenames from the input stream and creates a */
|
|
/* NODE_TYPE_TABLES node */
|
|
/* */
|
|
/* tablelistitem ::= tableref | outerjoin */
|
|
/* tablelist ::= tablelistitem , tablelist | tablelistitem */
|
|
|
|
{
|
|
RETCODE err;
|
|
LPUSTR old_lpszSqlStr;
|
|
SDWORD old_cbSqlStr;
|
|
LPSQLNODE lpSqlNode;
|
|
SQLNODEIDX idxTables;
|
|
SQLNODEIDX idxTable;
|
|
SQLNODEIDX idxRight;
|
|
BOOL fOuterJoin;
|
|
|
|
/* Outerjoin? */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = ParseOuterJoin(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql, lpIdx,
|
|
&idxRight, lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* No. Table reference? */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
err = ParseTableref(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxTable, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
fOuterJoin = FALSE;
|
|
}
|
|
else
|
|
fOuterJoin = TRUE;
|
|
|
|
/* Is there a comma? */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, ",", lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* No. This is the last one */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
idxTables = NO_SQLNODE;
|
|
}
|
|
else {
|
|
|
|
/* Yes. Get the rest of the list */
|
|
err = ParseTablelist(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxTables, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
|
|
/* Outer join? */
|
|
if (!fOuterJoin) {
|
|
|
|
/* No. Create the TABLES node */
|
|
*lpIdx = AllocateNode(lplpSql, NODE_TYPE_TABLES);
|
|
if (*lpIdx == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
lpSqlNode = ToNode(*lplpSql, *lpIdx);
|
|
lpSqlNode->node.tables.Table = idxTable;
|
|
lpSqlNode->node.tables.Next = idxTables;
|
|
}
|
|
else {
|
|
|
|
/* Yes. Link right side to the rest of the list */
|
|
if (idxTables != NO_SQLNODE) {
|
|
lpSqlNode = ToNode(*lplpSql, idxRight);
|
|
lpSqlNode->node.tables.Next = idxTables;
|
|
}
|
|
}
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParseColref(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, LPUSTR lpszToken)
|
|
|
|
/* Retrives a pattern from the input stream and creates a */
|
|
/* NODE_TYPE_COLUMN node */
|
|
/* */
|
|
/* colref ::= aliasname . columnname | columnname */
|
|
|
|
{
|
|
RETCODE err;
|
|
LPUSTR old_lpszSqlStr;
|
|
SDWORD old_cbSqlStr;
|
|
LPSQLNODE lpSqlNode;
|
|
STRINGIDX idxColumn = NO_STRING;
|
|
STRINGIDX idxAlias = NO_STRING;
|
|
STRINGIDX idxQualifier = NO_STRING;
|
|
|
|
/* Get the columnname */
|
|
err = GetIdentifier(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxColumn, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Qualified column name? */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, ".", lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* No. Simple column name */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
idxAlias = NO_STRING;
|
|
idxQualifier = NO_STRING;
|
|
}
|
|
else {
|
|
|
|
/* Get the real columnname */
|
|
idxAlias = idxColumn;
|
|
err = GetIdentifier(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxColumn, lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* This must be a simple column name. */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
idxColumn = idxAlias;
|
|
idxAlias = NO_STRING;
|
|
idxQualifier = NO_STRING;
|
|
}
|
|
else
|
|
{
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetSymbol (lpstmt, lplpszSqlStr, pcbSqlStr, ".", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
{
|
|
/* No. No qualifier. */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
idxQualifier = NO_STRING;
|
|
}
|
|
else
|
|
{
|
|
idxQualifier = idxAlias;
|
|
idxAlias = idxColumn;
|
|
err = GetIdentifier(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxColumn, lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* This must be a simple column name. */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
idxColumn = idxAlias;
|
|
idxAlias = idxQualifier;
|
|
idxQualifier = NO_STRING;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Create the COLUMN node */
|
|
*lpIdx = AllocateNode(lplpSql, NODE_TYPE_COLUMN);
|
|
if (*lpIdx == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
lpSqlNode = ToNode(*lplpSql, *lpIdx);
|
|
lpSqlNode->node.column.Tablealias = idxAlias;
|
|
lpSqlNode->node.column.Column = idxColumn;
|
|
lpSqlNode->node.column.Qualifier = idxQualifier;
|
|
|
|
/* These will be filled in by SemanticCheck() */
|
|
lpSqlNode->node.column.TableIndex = NO_SQLNODE;
|
|
lpSqlNode->node.column.MatchedAlias = FALSE;
|
|
lpSqlNode->node.column.Table = NO_SQLNODE;
|
|
lpSqlNode->node.column.Id = -1;
|
|
lpSqlNode->node.column.Value = NO_STRING;
|
|
lpSqlNode->node.column.InSortRecord = FALSE;
|
|
lpSqlNode->node.column.Offset = 0;
|
|
lpSqlNode->node.column.Length = 0;
|
|
lpSqlNode->node.column.DistinctOffset = 0;
|
|
lpSqlNode->node.column.DistinctLength = 0;
|
|
lpSqlNode->node.column.EnclosingStatement = NO_SQLNODE;
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParseOrderbyterm(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, BOOL FAR *lpfDescending,
|
|
LPUSTR lpszToken)
|
|
|
|
/* Retrives a sort order from the input stream and creates a */
|
|
/* NODE_TYPE_COLUMN or NODE_TYPE_NUMERIC node */
|
|
/* */
|
|
/* orderbyterm ::= colref asc | integer asc */
|
|
/* asc ::= | ASC | DESC */
|
|
|
|
{
|
|
RETCODE err;
|
|
LPUSTR old_lpszSqlStr;
|
|
SDWORD old_cbSqlStr;
|
|
LPSQLNODE lpSqlNode;
|
|
UDWORD udInteger;
|
|
|
|
/* Integer specified? */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetInteger(lpstmt, lplpszSqlStr, pcbSqlStr, &udInteger,
|
|
lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* No. Get column reference instead */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
err = ParseColref(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql, lpIdx,
|
|
lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
else {
|
|
|
|
/* Yes. Return it */
|
|
*lpIdx = AllocateNode(lplpSql, NODE_TYPE_NUMERIC);
|
|
if (*lpIdx == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
lpSqlNode = ToNode(*lplpSql, *lpIdx);
|
|
lpSqlNode->sqlDataType = TYPE_INTEGER;
|
|
lpSqlNode->sqlSqlType = SQL_INTEGER;
|
|
lpSqlNode->sqlPrecision = 10;
|
|
lpSqlNode->sqlScale = 0;
|
|
lpSqlNode->node.numeric.Value = udInteger;
|
|
lpSqlNode->node.numeric.Numeric = AllocateString(lplpSql, lpszToken);
|
|
if (lpSqlNode->node.numeric.Numeric == NO_STRING)
|
|
return ERR_MEMALLOCFAIL;
|
|
}
|
|
|
|
/* Get ASC or DESC */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr, NULL, lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
*lpfDescending = FALSE;
|
|
}
|
|
else if (!s_lstrcmpi(lpszToken, "ASC")) {
|
|
*lpfDescending = FALSE;
|
|
}
|
|
else if (!s_lstrcmpi(lpszToken, "DESC")) {
|
|
*lpfDescending = TRUE;
|
|
}
|
|
else {
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
*lpfDescending = FALSE;
|
|
}
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParseOrderbyterms(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, LPUSTR lpszToken)
|
|
|
|
/* Retrives a sort order from the input stream and creates a */
|
|
/* NODE_TYPE_SORTCOLUMNS node */
|
|
/* */
|
|
/* orderbyterms ::= orderbyterm | orderbyterm , orderbyterms */
|
|
|
|
{
|
|
RETCODE err;
|
|
LPUSTR old_lpszSqlStr;
|
|
SDWORD old_cbSqlStr;
|
|
LPSQLNODE lpSqlNode;
|
|
BOOL fDescending;
|
|
SQLNODEIDX idxColumn;
|
|
SQLNODEIDX idxSortcolumns;
|
|
SQLNODEIDX idxSortcolumnsPrev;
|
|
|
|
/* Get list of nodes */
|
|
idxSortcolumnsPrev = NO_SQLNODE;
|
|
while (TRUE) {
|
|
|
|
/* Get orderby column */
|
|
err = ParseOrderbyterm(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxColumn, &fDescending, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Create the SORTCOLUMNS node */
|
|
idxSortcolumns = AllocateNode(lplpSql, NODE_TYPE_SORTCOLUMNS);
|
|
if (idxSortcolumns == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
lpSqlNode = ToNode(*lplpSql, idxSortcolumns);
|
|
lpSqlNode->node.sortcolumns.Column = idxColumn;
|
|
lpSqlNode->node.sortcolumns.Descending = fDescending;
|
|
lpSqlNode->node.sortcolumns.Next = NO_SQLNODE;
|
|
|
|
/* Put node on list */
|
|
if (idxSortcolumnsPrev != NO_SQLNODE) {
|
|
lpSqlNode = ToNode(*lplpSql, idxSortcolumnsPrev);
|
|
lpSqlNode->node.sortcolumns.Next = idxSortcolumns;
|
|
}
|
|
else
|
|
*lpIdx = idxSortcolumns;
|
|
idxSortcolumnsPrev = idxSortcolumns;
|
|
|
|
/* Is there a comma? */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, ",", lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* No. This is the last orderby column */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParseOrderby(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, LPUSTR lpszToken)
|
|
|
|
/* Retrives a sort order from the input stream and creates a */
|
|
/* NODE_TYPE_SORTCOLUMNS or NULL node */
|
|
/* */
|
|
/* orderby ::= | ORDER BY orderbyterms */
|
|
|
|
{
|
|
RETCODE err;
|
|
LPUSTR old_lpszSqlStr;
|
|
SDWORD old_cbSqlStr;
|
|
|
|
/* Is there an "ORDER BY"? */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr, "ORDER", lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* No. Return null */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
*lpIdx = NO_SQLNODE;
|
|
}
|
|
else {
|
|
|
|
/* Yes. Get keyword */
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr, "BY", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Get orderby column */
|
|
err = ParseOrderbyterms(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
lpIdx, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParseGroupbyterms(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, LPUSTR lpszToken)
|
|
|
|
/* Retrives a sort order from the input stream and creates a */
|
|
/* NODE_TYPE_SORTCOLUMNS node */
|
|
/* */
|
|
/* groupbyterms ::= colref | colref , groupbyterms */
|
|
|
|
{
|
|
RETCODE err;
|
|
LPUSTR old_lpszSqlStr;
|
|
SDWORD old_cbSqlStr;
|
|
LPSQLNODE lpSqlNode;
|
|
SQLNODEIDX idxColumn;
|
|
SQLNODEIDX idxGroupbycolumns;
|
|
SQLNODEIDX idxGroupbycolumnsPrev;
|
|
|
|
/* Get list of nodes */
|
|
idxGroupbycolumnsPrev = NO_SQLNODE;
|
|
while (TRUE) {
|
|
|
|
/* Get column reference */
|
|
err = ParseColref(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxColumn, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Create the GROUPBYCOLUMNS node */
|
|
idxGroupbycolumns = AllocateNode(lplpSql, NODE_TYPE_GROUPBYCOLUMNS);
|
|
if (idxGroupbycolumns == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
lpSqlNode = ToNode(*lplpSql, idxGroupbycolumns);
|
|
lpSqlNode->node.groupbycolumns.Column = idxColumn;
|
|
lpSqlNode->node.groupbycolumns.Next = NO_SQLNODE;
|
|
|
|
/* Put node on list */
|
|
if (idxGroupbycolumnsPrev != NO_SQLNODE) {
|
|
lpSqlNode = ToNode(*lplpSql, idxGroupbycolumnsPrev);
|
|
lpSqlNode->node.groupbycolumns.Next = idxGroupbycolumns;
|
|
}
|
|
else
|
|
*lpIdx = idxGroupbycolumns;
|
|
idxGroupbycolumnsPrev = idxGroupbycolumns;
|
|
|
|
/* Is there a comma? */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, ",", lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* No. This is the last groupbyby column */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParseGroupby(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, LPUSTR lpszToken)
|
|
|
|
/* Retrives a sort order from the input stream and creates a */
|
|
/* NODE_TYPE_GROUPBYCOLUMNS or NULL node */
|
|
/* */
|
|
/* groupby ::= | GROUP BY groupbyterms */
|
|
|
|
{
|
|
RETCODE err;
|
|
LPUSTR old_lpszSqlStr;
|
|
SDWORD old_cbSqlStr;
|
|
|
|
/* Is there an "GROUP BY"? */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr, "GROUP", lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* No. Return null */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
*lpIdx = NO_SQLNODE;
|
|
}
|
|
else {
|
|
|
|
/* Yes. Get keyword */
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr, "BY", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Get grooupby columns */
|
|
err = ParseGroupbyterms(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
lpIdx, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParseSimpleterm(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, LPUSTR lpszToken)
|
|
|
|
/* Retrives a term from the input stream and creates a NODE_TYPE_STRING, */
|
|
/* NODE_TYPE_NUMERIC, NODE_TYPE_PARAMETER, NODE_TYPE_USER, NODE_TYPE_DATE, */
|
|
/* NODE_TYPE_TIME, or NODE_TYPE_TIMESTAMP node. */
|
|
/* */
|
|
/* simpleterm ::= string | realnumber | ? | USER | date | time | timestamp */
|
|
{
|
|
RETCODE err;
|
|
LPUSTR old_lpszSqlStr;
|
|
SDWORD old_cbSqlStr;
|
|
STRINGIDX idxString;
|
|
LPSQLNODE lpSqlNode;
|
|
double dbl;
|
|
SQLNODEIDX idxParameter;
|
|
UWORD parameterId;
|
|
SWORD datatype;
|
|
SWORD scale;
|
|
DATE_STRUCT date;
|
|
TIME_STRUCT time;
|
|
TIMESTAMP_STRUCT timestamp;
|
|
|
|
/* Is it a string? */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetString(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxString, lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* No. Is it a parameter */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, "?", lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* No. Is it USER? */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr, "USER",
|
|
lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* No. Is it a realnumber? */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
err = GetNumber(lpstmt, lplpszSqlStr, pcbSqlStr, &dbl,
|
|
lpszToken, &datatype, &scale);
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* No. Is it an escape sequence? */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
err = GetEscape(lpstmt, *lplpszSqlStr, *pcbSqlStr,
|
|
lpszToken, lplpszSqlStr, pcbSqlStr);
|
|
if (err != ERR_SUCCESS) {
|
|
if (err == ERR_EXPECTEDOTHER)
|
|
s_lstrcpy(lpstmt->szError, "<simpleterm>");
|
|
return err;
|
|
}
|
|
|
|
/* It is an escape sequence. A date? */
|
|
if (!CharToDate(lpszToken, SQL_NTS, &date)) {
|
|
|
|
/* Make date node */
|
|
*lpIdx = AllocateNode(lplpSql, NODE_TYPE_DATE);
|
|
if (*lpIdx == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
lpSqlNode = ToNode(*lplpSql, *lpIdx);
|
|
lpSqlNode->node.date.Value = date;
|
|
}
|
|
|
|
/* A time? */
|
|
else if (!CharToTime(lpszToken, SQL_NTS, &time)) {
|
|
|
|
/* Make time node */
|
|
*lpIdx = AllocateNode(lplpSql, NODE_TYPE_TIME);
|
|
if (*lpIdx == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
lpSqlNode = ToNode(*lplpSql, *lpIdx);
|
|
lpSqlNode->node.time.Value = time;
|
|
}
|
|
|
|
/* A timestamp? */
|
|
else if (!CharToTimestamp(lpszToken, SQL_NTS, ×tamp)) {
|
|
|
|
/* Make timestamp node */
|
|
*lpIdx = AllocateNode(lplpSql, NODE_TYPE_TIMESTAMP);
|
|
if (*lpIdx == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
lpSqlNode = ToNode(*lplpSql, *lpIdx);
|
|
lpSqlNode->node.timestamp.Value = timestamp;
|
|
}
|
|
|
|
else {
|
|
s_lstrcpy(lpstmt->szError, lpszToken);
|
|
return ERR_BADESCAPE;
|
|
}
|
|
}
|
|
else {
|
|
/* Make numeric node */
|
|
*lpIdx = AllocateNode(lplpSql, NODE_TYPE_NUMERIC);
|
|
if (*lpIdx == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
lpSqlNode = ToNode(*lplpSql, *lpIdx);
|
|
if ((s_lstrlen(lpszToken) >= 10) &&
|
|
(datatype == TYPE_INTEGER)) {
|
|
datatype = TYPE_DOUBLE;
|
|
scale = NO_SCALE;
|
|
}
|
|
switch (datatype) {
|
|
case TYPE_DOUBLE:
|
|
lpSqlNode->sqlDataType = TYPE_DOUBLE;
|
|
lpSqlNode->sqlSqlType = SQL_DOUBLE;
|
|
lpSqlNode->sqlPrecision = 15;
|
|
lpSqlNode->sqlScale = NO_SCALE;
|
|
break;
|
|
case TYPE_INTEGER:
|
|
lpSqlNode->sqlDataType = TYPE_INTEGER;
|
|
lpSqlNode->sqlSqlType = SQL_INTEGER;
|
|
lpSqlNode->sqlPrecision = 10;
|
|
lpSqlNode->sqlScale = 0;
|
|
break;
|
|
case TYPE_NUMERIC:
|
|
lpSqlNode->sqlDataType = TYPE_NUMERIC;
|
|
lpSqlNode->sqlSqlType = SQL_DECIMAL;
|
|
if (*lpszToken != '-')
|
|
lpSqlNode->sqlPrecision = s_lstrlen(lpszToken)-1;
|
|
else
|
|
lpSqlNode->sqlPrecision = s_lstrlen(lpszToken)-2;
|
|
lpSqlNode->sqlScale = scale;
|
|
BCDNormalize(lpszToken, s_lstrlen(lpszToken),
|
|
lpszToken, s_lstrlen(lpszToken) + 1,
|
|
lpSqlNode->sqlPrecision,
|
|
lpSqlNode->sqlScale);
|
|
break;
|
|
case TYPE_CHAR:
|
|
case TYPE_BINARY:
|
|
case TYPE_DATE:
|
|
case TYPE_TIME:
|
|
case TYPE_TIMESTAMP:
|
|
default:
|
|
return ERR_INTERNAL;
|
|
}
|
|
lpSqlNode->node.numeric.Numeric = AllocateSpace(lplpSql,
|
|
(SWORD) (s_lstrlen(lpszToken) + 2));
|
|
/* The extra space is to allow negation of */
|
|
/* this value in SEMANTIC.C */
|
|
if (lpSqlNode->node.numeric.Numeric == NO_STRING)
|
|
return ERR_MEMALLOCFAIL;
|
|
s_lstrcpy(ToString(*lplpSql, lpSqlNode->node.numeric.Numeric),
|
|
lpszToken);
|
|
lpSqlNode->node.numeric.Value = dbl;
|
|
}
|
|
}
|
|
else {
|
|
|
|
/* Yes. Make USER node */
|
|
*lpIdx = AllocateNode(lplpSql, NODE_TYPE_USER);
|
|
if (*lpIdx == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
}
|
|
}
|
|
else {
|
|
|
|
/* Yes. Make PARAMETER node */
|
|
*lpIdx = AllocateNode(lplpSql, NODE_TYPE_PARAMETER);
|
|
if (*lpIdx == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
|
|
/* First parameter? */
|
|
idxParameter = (*lplpSql)->node.root.parameters;
|
|
if (idxParameter == NO_SQLNODE) {
|
|
|
|
/* Yes. Initialize list of parameters */
|
|
(*lplpSql)->node.root.parameters = *lpIdx;
|
|
lpSqlNode = ToNode(*lplpSql, *lpIdx);
|
|
lpSqlNode->node.parameter.Id = 1;
|
|
}
|
|
else {
|
|
|
|
/* No. Get last element of parameter list */
|
|
while (TRUE) {
|
|
lpSqlNode = ToNode(*lplpSql, idxParameter);
|
|
idxParameter = lpSqlNode->node.parameter.Next;
|
|
if (idxParameter == NO_SQLNODE)
|
|
break;
|
|
}
|
|
|
|
/* Put node on list */
|
|
lpSqlNode->node.parameter.Next = *lpIdx;
|
|
|
|
/* Determine id of parameter */
|
|
parameterId = lpSqlNode->node.parameter.Id + 1;
|
|
lpSqlNode = ToNode(*lplpSql, *lpIdx);
|
|
lpSqlNode->node.parameter.Id = parameterId;
|
|
}
|
|
lpSqlNode->node.parameter.Next = NO_SQLNODE;
|
|
lpSqlNode->node.parameter.Value = NO_STRING;
|
|
lpSqlNode->node.parameter.AtExec = FALSE;
|
|
}
|
|
}
|
|
else {
|
|
|
|
/* Yes. Make STRING node */
|
|
*lpIdx = AllocateNode(lplpSql, NODE_TYPE_STRING);
|
|
if (*lpIdx == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
lpSqlNode = ToNode(*lplpSql, *lpIdx);
|
|
lpSqlNode->node.string.Value = idxString;
|
|
}
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParseAggterm(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, LPUSTR lpszToken)
|
|
|
|
/* Retrives a term from the input stream and creates a NODE_TYPE_AGGREGATE */
|
|
/* node. */
|
|
/* */
|
|
/* aggterm ::= COUNT ( * ) | AVG ( expression ) | MAX ( expression ) | */
|
|
/* MIN ( expression ) | SUM ( expression ) */
|
|
{
|
|
RETCODE err;
|
|
UWORD op;
|
|
SQLNODEIDX idxExpression;
|
|
LPSQLNODE lpSqlNode;
|
|
|
|
/* Get starting keyword */
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr, NULL, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
//double check for aggregate functions not supported
|
|
if (lpszToken && !s_lstrcmpi(lpszToken, "LEN"))
|
|
{
|
|
return ERR_LEN_NOTSUPP;
|
|
}
|
|
|
|
/* Convert it to an aggregate operator */
|
|
if (!s_lstrcmpi(lpszToken, "COUNT"))
|
|
op = AGG_COUNT;
|
|
else if (!s_lstrcmpi(lpszToken, "AVG"))
|
|
op = AGG_AVG;
|
|
else if (!s_lstrcmpi(lpszToken, "MAX"))
|
|
op = AGG_MAX;
|
|
else if (!s_lstrcmpi(lpszToken, "MIN"))
|
|
op = AGG_MIN;
|
|
else if (!s_lstrcmpi(lpszToken, "SUM"))
|
|
op = AGG_SUM;
|
|
else {
|
|
s_lstrcpy(lpstmt->szError, "COUNT, AVG, MAX, MIN, or SUM");
|
|
return ERR_EXPECTEDOTHER;
|
|
}
|
|
|
|
/* Get opening paren */
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, "(", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Based on type of operator... */
|
|
switch(op) {
|
|
case AGG_COUNT:
|
|
|
|
/* Get * */
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, "*", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return ERR_COUNT_NOTSUPP;
|
|
idxExpression = NO_SQLNODE;
|
|
break;
|
|
|
|
case AGG_AVG:
|
|
case AGG_MAX:
|
|
case AGG_MIN:
|
|
case AGG_SUM:
|
|
|
|
/* Get expression */
|
|
err = ParseExpression(lpstmt, lplpszSqlStr, pcbSqlStr,
|
|
lplpSql, &idxExpression, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
break;
|
|
|
|
default:
|
|
return ERR_INTERNAL;
|
|
}
|
|
|
|
/* Get closing paren */
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, ")", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Make AGGREGATE node */
|
|
*lpIdx = AllocateNode(lplpSql, NODE_TYPE_AGGREGATE);
|
|
if (*lpIdx == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
lpSqlNode = ToNode(*lplpSql, *lpIdx);
|
|
lpSqlNode->node.aggregate.Operator = op;
|
|
lpSqlNode->node.aggregate.Expression = idxExpression;
|
|
lpSqlNode->node.aggregate.Value = NO_STRING;
|
|
lpSqlNode->node.aggregate.Offset = 0;
|
|
lpSqlNode->node.aggregate.Length = 0;
|
|
lpSqlNode->node.aggregate.DistinctOffset = 0;
|
|
lpSqlNode->node.aggregate.DistinctLength = 0;
|
|
lpSqlNode->node.aggregate.EnclosingStatement = NO_SQLNODE;
|
|
lpSqlNode->node.aggregate.Next = NO_SQLNODE;
|
|
lpSqlNode->node.aggregate.Count = 0.0;
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParseValuelist(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, LPUSTR lpszToken)
|
|
|
|
/* Retrives a list of expressions from the input stream and creates a */
|
|
/* NODE_TYPE_VALUES node */
|
|
/* */
|
|
/* valuelist ::= expression , valuelist | expression | NULL , valuelist | */
|
|
/* NULL */
|
|
|
|
{
|
|
RETCODE err;
|
|
LPUSTR old_lpszSqlStr;
|
|
SDWORD old_cbSqlStr;
|
|
LPSQLNODE lpSqlNode;
|
|
SQLNODEIDX idxValues;
|
|
SQLNODEIDX idxValue;
|
|
SQLNODEIDX idxValuesPrev;
|
|
|
|
/* Get list of nodes */
|
|
idxValuesPrev = NO_SQLNODE;
|
|
while (TRUE) {
|
|
|
|
/* NULL specified? */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr, "NULL", lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* No. Get expression */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
err = ParseExpression(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxValue, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
else {
|
|
|
|
/* Yes. Create a NULL node */
|
|
idxValue = AllocateNode(lplpSql, NODE_TYPE_NULL);
|
|
if (idxValue == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
}
|
|
|
|
/* Create the VALUES node */
|
|
idxValues = AllocateNode(lplpSql, NODE_TYPE_VALUES);
|
|
if (idxValues == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
lpSqlNode = ToNode(*lplpSql, idxValues);
|
|
lpSqlNode->node.values.Value = idxValue;
|
|
lpSqlNode->node.values.Alias = NO_STRING;
|
|
lpSqlNode->node.values.Next = NO_SQLNODE;
|
|
|
|
/* Put node on list */
|
|
if (idxValuesPrev != NO_SQLNODE) {
|
|
lpSqlNode = ToNode(*lplpSql, idxValuesPrev);
|
|
lpSqlNode->node.values.Next = idxValues;
|
|
}
|
|
else
|
|
*lpIdx = idxValues;
|
|
idxValuesPrev = idxValues;
|
|
|
|
/* Is there a comma? */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, ",", lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* No. This is the last value */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParseScalar(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, LPUSTR lpszToken)
|
|
|
|
/* Retrives ascalar reference from the input stream and creates a */
|
|
/* NODE_TYPE_SCALE node. */
|
|
/* */
|
|
/* fn := functionname ( valuelist ) | functionname ( ) */
|
|
/* scalarshorthand := { FN fn } */
|
|
/* scalarescape := --*(VENDOR(MICROSOFT),PRODUCT(ODBC) FN fn)*-- */
|
|
/* scalar ::= scalarescape | scalarshorthand */
|
|
{
|
|
RETCODE err;
|
|
BOOL fShorthand;
|
|
STRINGIDX idxFunction;
|
|
SQLNODEIDX idxValues;
|
|
LPSQLNODE lpSqlNode;
|
|
LPUSTR old_lpszSqlStr;
|
|
SDWORD old_cbSqlStr;
|
|
|
|
/* Get starting symbol */
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, NULL, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Shorthand? */
|
|
if (s_lstrcmpi(lpszToken, "{")) {
|
|
|
|
/* No. Not a shorthand */
|
|
fShorthand = FALSE;
|
|
|
|
/* If not an escape clause, error */
|
|
if (s_lstrcmpi(lpszToken, "--")) {
|
|
s_lstrcpy(lpstmt->szError, "--");
|
|
return ERR_EXPECTEDOTHER;
|
|
}
|
|
|
|
/* Get the rest of the starting sequence */
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, "(", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, "*", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
err = GetIdent(lpstmt, lplpszSqlStr, pcbSqlStr, "vendor", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, "(", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
err = GetIdent(lpstmt, lplpszSqlStr, pcbSqlStr, "Microsoft", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, ")", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, ",", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
err = GetIdent(lpstmt, lplpszSqlStr, pcbSqlStr, "product", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, "(", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
err = GetIdent(lpstmt, lplpszSqlStr, pcbSqlStr, "ODBC", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, ")", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
else
|
|
fShorthand = TRUE;
|
|
|
|
/* Get FN keyword */
|
|
err = GetIdent(lpstmt, lplpszSqlStr, pcbSqlStr, "fn", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Get function name */
|
|
err = GetIdent(lpstmt, lplpszSqlStr, pcbSqlStr, NULL, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
idxFunction = AllocateString(lplpSql, lpszToken);
|
|
if (idxFunction == NO_STRING)
|
|
return ERR_MEMALLOCFAIL;
|
|
|
|
/* Get arguments (if any) */
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, "(", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = ParseValuelist(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxValues, lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
idxValues = NO_SQLNODE;
|
|
}
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, ")", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Get closing bracket */
|
|
if (!fShorthand) {
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, "*", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, ")", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, "--", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
else {
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, "}", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
|
|
/* Create the SCALAR node */
|
|
*lpIdx = AllocateNode(lplpSql, NODE_TYPE_SCALAR);
|
|
if (*lpIdx == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
lpSqlNode = ToNode(*lplpSql, *lpIdx);
|
|
lpSqlNode->node.scalar.Function = idxFunction;
|
|
lpSqlNode->node.scalar.Arguments = idxValues;
|
|
lpSqlNode->node.scalar.Id = 0;
|
|
lpSqlNode->node.scalar.Interval = 0;
|
|
lpSqlNode->node.scalar.Value = NO_STRING;
|
|
lpSqlNode->node.scalar.DistinctOffset = 0;
|
|
lpSqlNode->node.scalar.DistinctLength = 0;
|
|
lpSqlNode->node.scalar.EnclosingStatement = NO_SQLNODE;
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParseTerm(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, LPUSTR lpszToken)
|
|
|
|
/* Retrives a term from the input stream and creates a NODE_TYPE_COLUMN, */
|
|
/* NODE_TYPE_ALGEBRAIC, NODE_TYPE_AGGREGATE, NODE_TYPE_STRING, */
|
|
/* NODE_TYPE_NUMERIC, NODE_TYPE_PARAMETER, NODE_TYPE_USER, NODE_TYPE_DATE, */
|
|
/* NODE_TYPE_TIME, or NODE_TYPE_TIMESTAMP node. */
|
|
/* */
|
|
/* term ::= ( expression ) | colref | simpleterm | aggterm | scalar */
|
|
{
|
|
RETCODE err;
|
|
LPUSTR old_lpszSqlStr;
|
|
SDWORD old_cbSqlStr;
|
|
|
|
/* Is it a simpleterm? */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = ParseSimpleterm(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql, lpIdx,
|
|
lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* No. Is it an aggregate function? */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
err = ParseAggterm(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql, lpIdx,
|
|
lpszToken);
|
|
|
|
//Return straight away if error was due to
|
|
//non-support of certain aggregate functions
|
|
if ( (err == ERR_LEN_NOTSUPP) || (err == ERR_COUNT_NOTSUPP) )
|
|
return err;
|
|
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* No. Is it ( expression )? */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, "(", lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* No. Is it a column reference? */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
err = ParseColref(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
lpIdx, lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* No. Is it a scalar? */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
err = ParseScalar(lpstmt, lplpszSqlStr, pcbSqlStr,
|
|
lplpSql, lpIdx, lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
s_lstrcpy(lpstmt->szError, "<identifier>");
|
|
return ERR_EXPECTEDOTHER;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
|
|
/* Yes. Get expression */
|
|
err = ParseExpression(lpstmt, lplpszSqlStr, pcbSqlStr,
|
|
lplpSql, lpIdx, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Get terminating ")" */
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, ")",
|
|
lpszToken);
|
|
|
|
//Do a double check for the unsupported BETWEEN function
|
|
if (lpszToken && !s_lstrcmpi(lpszToken, "BETWEEN"))
|
|
{
|
|
return ERR_BETWEEN_NOTSUPP;
|
|
}
|
|
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParseNeg(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, LPUSTR lpszToken)
|
|
|
|
/* Retrives an algebraic expression from the input stream and creates a */
|
|
/* NODE_TYPE_ALGEBRAIC node */
|
|
/* */
|
|
/* neg ::= term | + term | - term */
|
|
|
|
{
|
|
RETCODE err;
|
|
LPUSTR old_lpszSqlStr;
|
|
SDWORD old_cbSqlStr;
|
|
LPSQLNODE lpSqlNode;
|
|
SQLNODEIDX idxChild;
|
|
UWORD op;
|
|
|
|
/* Get + or - */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, NULL, lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
op = OP_NONE;
|
|
}
|
|
else {
|
|
if (!s_lstrcmpi(lpszToken, "-"))
|
|
op = OP_NEG;
|
|
else if (!s_lstrcmpi(lpszToken, "+"))
|
|
op = OP_NONE;
|
|
else {
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
op = OP_NONE;
|
|
}
|
|
}
|
|
|
|
/* Get term */
|
|
err = ParseTerm(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql, &idxChild,
|
|
lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Return node */
|
|
if (op == OP_NONE) {
|
|
*lpIdx = idxChild;
|
|
}
|
|
else {
|
|
*lpIdx = AllocateNode(lplpSql, NODE_TYPE_ALGEBRAIC);
|
|
if (*lpIdx == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
lpSqlNode = ToNode(*lplpSql, *lpIdx);
|
|
lpSqlNode->node.algebraic.Operator = OP_NEG;
|
|
lpSqlNode->node.algebraic.Left = idxChild;
|
|
lpSqlNode->node.algebraic.Right = NO_SQLNODE;
|
|
lpSqlNode->node.algebraic.Value = NO_STRING;
|
|
lpSqlNode->node.algebraic.WorkBuffer1 = NO_STRING;
|
|
lpSqlNode->node.algebraic.WorkBuffer2 = NO_STRING;
|
|
lpSqlNode->node.algebraic.WorkBuffer3 = NO_STRING;
|
|
lpSqlNode->node.algebraic.DistinctOffset = 0;
|
|
lpSqlNode->node.algebraic.DistinctLength = 0;
|
|
lpSqlNode->node.algebraic.EnclosingStatement = NO_SQLNODE;
|
|
}
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParseTimes(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, LPUSTR lpszToken)
|
|
|
|
/* Retrives an algebraic expression from the input stream and creates a */
|
|
/* NODE_TYPE_ALGEBRAIC node */
|
|
/* */
|
|
/* times ::= times * neg | times / neg | neg */
|
|
|
|
{
|
|
RETCODE err;
|
|
LPUSTR old_lpszSqlStr;
|
|
SDWORD old_cbSqlStr;
|
|
LPSQLNODE lpSqlNode;
|
|
SQLNODEIDX idxLeft;
|
|
SQLNODEIDX idxRight;
|
|
SQLNODEIDX idxNew;
|
|
UWORD op;
|
|
|
|
/* Get start of expression */
|
|
err = ParseNeg(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql, &idxLeft,
|
|
lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Get the rest of the expression */
|
|
while (TRUE) {
|
|
|
|
/* Get * or / */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, NULL, lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
*lpIdx = idxLeft;
|
|
break;
|
|
}
|
|
else {
|
|
if ( (!s_lstrcmpi(lpszToken, "*")) &&
|
|
( (*pcbSqlStr == 0) || (*(*lplpszSqlStr) != ')' ) ) )
|
|
op = OP_TIMES;
|
|
else if (!s_lstrcmpi(lpszToken, "/"))
|
|
op = OP_DIVIDEDBY;
|
|
else {
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
*lpIdx = idxLeft;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Get right side */
|
|
err = ParseNeg(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql, &idxRight,
|
|
lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Make two halves into a node */
|
|
idxNew = AllocateNode(lplpSql, NODE_TYPE_ALGEBRAIC);
|
|
if (idxNew == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
lpSqlNode = ToNode(*lplpSql, idxNew);
|
|
lpSqlNode->node.algebraic.Operator = op;
|
|
lpSqlNode->node.algebraic.Left = idxLeft;
|
|
lpSqlNode->node.algebraic.Right = idxRight;
|
|
lpSqlNode->node.algebraic.Value = NO_STRING;
|
|
lpSqlNode->node.algebraic.WorkBuffer1 = NO_STRING;
|
|
lpSqlNode->node.algebraic.WorkBuffer2 = NO_STRING;
|
|
lpSqlNode->node.algebraic.WorkBuffer3 = NO_STRING;
|
|
lpSqlNode->node.algebraic.DistinctOffset = 0;
|
|
lpSqlNode->node.algebraic.DistinctLength = 0;
|
|
lpSqlNode->node.algebraic.EnclosingStatement = NO_SQLNODE;
|
|
|
|
/* Get ready for next iteration */
|
|
idxLeft = idxNew;
|
|
}
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParseExpression(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, LPUSTR lpszToken)
|
|
|
|
/* Retrives an algebraic expression from the input stream and creates a */
|
|
/* NODE_TYPE_ALGEBRAIC node */
|
|
/* */
|
|
/* expression ::= expression + times | expression - times | times */
|
|
|
|
{
|
|
RETCODE err;
|
|
LPUSTR old_lpszSqlStr;
|
|
SDWORD old_cbSqlStr;
|
|
LPSQLNODE lpSqlNode;
|
|
SQLNODEIDX idxLeft;
|
|
SQLNODEIDX idxRight;
|
|
SQLNODEIDX idxNew;
|
|
UWORD op;
|
|
|
|
/* Get start of expression */
|
|
err = ParseTimes(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql, &idxLeft,
|
|
lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Get the rest of the expression */
|
|
while (TRUE) {
|
|
|
|
/* Get + or - */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, NULL, lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
*lpIdx = idxLeft;
|
|
break;
|
|
}
|
|
else {
|
|
if (!s_lstrcmpi(lpszToken, "-"))
|
|
op = OP_MINUS;
|
|
else if (!s_lstrcmpi(lpszToken, "+"))
|
|
op = OP_PLUS;
|
|
else {
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
*lpIdx = idxLeft;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Get right side */
|
|
err = ParseTimes(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql, &idxRight,
|
|
lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Make two halves into a node */
|
|
idxNew = AllocateNode(lplpSql, NODE_TYPE_ALGEBRAIC);
|
|
if (idxNew == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
lpSqlNode = ToNode(*lplpSql, idxNew);
|
|
lpSqlNode->node.algebraic.Operator = op;
|
|
lpSqlNode->node.algebraic.Left = idxLeft;
|
|
lpSqlNode->node.algebraic.Right = idxRight;
|
|
lpSqlNode->node.algebraic.Value = NO_STRING;
|
|
lpSqlNode->node.algebraic.WorkBuffer1 = NO_STRING;
|
|
lpSqlNode->node.algebraic.WorkBuffer2 = NO_STRING;
|
|
lpSqlNode->node.algebraic.WorkBuffer3 = NO_STRING;
|
|
lpSqlNode->node.algebraic.DistinctOffset = 0;
|
|
lpSqlNode->node.algebraic.DistinctLength = 0;
|
|
lpSqlNode->node.algebraic.EnclosingStatement = NO_SQLNODE;
|
|
|
|
/* Get ready for next iteration */
|
|
idxLeft = idxNew;
|
|
}
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParsePattern(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, LPUSTR lpszToken)
|
|
|
|
/* Retrives a pattern from the input stream and creates a */
|
|
/* NODE_TYPE_STRING, NODE_TYPE_PARAMETER, or NODE_TYPE_USER node */
|
|
/* */
|
|
/* pattern ::= string | ? | USER */
|
|
|
|
{
|
|
RETCODE err;
|
|
LPUSTR old_lpszSqlStr;
|
|
SDWORD old_cbSqlStr;
|
|
STRINGIDX idxString;
|
|
LPSQLNODE lpSqlNode;
|
|
SQLNODEIDX idxParameter;
|
|
UWORD parameterId;
|
|
|
|
/* Is it a string? */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetString(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxString, lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* No. Is it a parameter */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, "?", lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* No. Is it USER? */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr, "USER",
|
|
lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* No. Error */
|
|
return err;
|
|
}
|
|
else {
|
|
|
|
/* Yes. Make USER node */
|
|
*lpIdx = AllocateNode(lplpSql, NODE_TYPE_USER);
|
|
if (*lpIdx == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
}
|
|
}
|
|
else {
|
|
|
|
/* Yes. Make PARAMETER node */
|
|
*lpIdx = AllocateNode(lplpSql, NODE_TYPE_PARAMETER);
|
|
if (*lpIdx == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
|
|
/* First parameter? */
|
|
idxParameter = (*lplpSql)->node.root.parameters;
|
|
if (idxParameter == NO_SQLNODE) {
|
|
|
|
/* Yes. Initialize list of parameters */
|
|
(*lplpSql)->node.root.parameters = *lpIdx;
|
|
lpSqlNode = ToNode(*lplpSql, *lpIdx);
|
|
lpSqlNode->node.parameter.Id = 1;
|
|
}
|
|
else {
|
|
|
|
/* No. Get last element of parameter list */
|
|
while (TRUE) {
|
|
lpSqlNode = ToNode(*lplpSql, idxParameter);
|
|
idxParameter = lpSqlNode->node.parameter.Next;
|
|
if (idxParameter == NO_SQLNODE)
|
|
break;
|
|
}
|
|
|
|
/* Put node on list */
|
|
lpSqlNode->node.parameter.Next = *lpIdx;
|
|
|
|
/* Determine id of parameter */
|
|
parameterId = lpSqlNode->node.parameter.Id + 1;
|
|
lpSqlNode = ToNode(*lplpSql, *lpIdx);
|
|
lpSqlNode->node.parameter.Id = parameterId;
|
|
}
|
|
lpSqlNode->node.parameter.Next = NO_SQLNODE;
|
|
lpSqlNode->node.parameter.Value = NO_STRING;
|
|
lpSqlNode->node.parameter.AtExec = FALSE;
|
|
}
|
|
|
|
}
|
|
else {
|
|
|
|
/* Yes. Make STRING node */
|
|
*lpIdx = AllocateNode(lplpSql, NODE_TYPE_STRING);
|
|
if (*lpIdx == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
lpSqlNode = ToNode(*lplpSql, *lpIdx);
|
|
lpSqlNode->node.string.Value = idxString;
|
|
}
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParseComparison(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, LPUSTR lpszToken)
|
|
|
|
/* Retrives a comparison expression from the input stream and creates a */
|
|
/* NODE_TYPE_COMPARISON node */
|
|
/* */
|
|
/* comparison ::= ( boolean ) | colref IS NULL | colref IS NOT NULL | */
|
|
/* expression LIKE pattern | expression NOT LIKE pattern | */
|
|
/* expression IN ( valuelist ) | expression NOT IN ( valuelist ) | */
|
|
/* expression op expression | EXISTS ( SELECT select ) | */
|
|
/* expression op selectop ( SELECT select ) | */
|
|
/* expression IN ( SELECT select ) | */
|
|
/* expression NOT IN ( SELECT select ) */
|
|
/* op ::= > | >= | < | <= | = | <> */
|
|
/* selectop ::= | ALL | ANY */
|
|
|
|
{
|
|
RETCODE err;
|
|
LPUSTR old_lpszSqlStr;
|
|
SDWORD old_cbSqlStr;
|
|
LPSQLNODE lpSqlNode;
|
|
SQLNODEIDX idxLeft;
|
|
SQLNODEIDX idxRight;
|
|
UWORD op;
|
|
UWORD selectModifier;
|
|
|
|
/* Is there a "("? */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, "(", lpszToken);
|
|
if (err == ERR_SUCCESS) {
|
|
|
|
/* Yes. Return boolean */
|
|
err = ParseBoolean(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql, lpIdx,
|
|
lpszToken);
|
|
|
|
/* Make sure closing ")" is there */
|
|
if (err == ERR_SUCCESS)
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, ")", lpszToken);
|
|
}
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* Look for EXISTS predicate */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr, "EXISTS",
|
|
lpszToken);
|
|
if (err == ERR_SUCCESS) {
|
|
|
|
/* Found it. Get opening "(" */
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, "(", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Found it. Get opening "SELECT" */
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr, "SELECT",
|
|
lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Get the sub-select */
|
|
err = ParseSelect(lpstmt, lplpszSqlStr, pcbSqlStr,
|
|
lplpSql, &idxLeft, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Check for trailing ")" */
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, ")", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Return EXISTS condition */
|
|
op = OP_EXISTS;
|
|
idxRight = NO_SQLNODE;
|
|
selectModifier = SELECT_EXISTS;
|
|
}
|
|
else {
|
|
|
|
/* Get left child */
|
|
selectModifier = SELECT_NOTSELECT;
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
err = ParseExpression(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxLeft, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Is left child a column reference? */
|
|
lpSqlNode = ToNode(*lplpSql, idxLeft);
|
|
if (lpSqlNode->sqlNodeType == NODE_TYPE_COLUMN) {
|
|
|
|
/* Is this an "IS NULL" or "IS NOT NULL"? */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr, "IS",
|
|
lpszToken);
|
|
if (err == ERR_SUCCESS) {
|
|
|
|
/* Yes. See which of the two it is */
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr, NULL,
|
|
lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
if (!s_lstrcmpi(lpszToken, "NOT")) {
|
|
op = OP_NE;
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr,
|
|
NULL, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
else
|
|
op = OP_EQ;
|
|
|
|
/* Error if missing keyword */
|
|
if (s_lstrcmpi(lpszToken, "NULL")) {
|
|
s_lstrcpy(lpstmt->szError, "NULL");
|
|
return ERR_EXPECTEDOTHER;
|
|
}
|
|
|
|
/* Create the value to compare it to */
|
|
idxRight = AllocateNode(lplpSql, NODE_TYPE_NULL);
|
|
if (idxRight == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
}
|
|
else {
|
|
|
|
/* No. Set flag */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
op = OP_NONE;
|
|
}
|
|
}
|
|
else
|
|
op = OP_NONE;
|
|
|
|
/* Have we gotten the operator yet? */
|
|
if (op == OP_NONE) {
|
|
|
|
/* No. Get it now. */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, NULL,
|
|
lpszToken);
|
|
|
|
/* Is the operator >, >=, <, <=, =, or <> ? */
|
|
if (err == ERR_SUCCESS) {
|
|
|
|
/* Yes. Figure out which one */
|
|
if (!s_lstrcmpi(lpszToken, ">"))
|
|
op = OP_GT;
|
|
else if (!s_lstrcmpi(lpszToken, ">="))
|
|
op = OP_GE;
|
|
else if (!s_lstrcmpi(lpszToken, "<"))
|
|
op = OP_LT;
|
|
else if (!s_lstrcmpi(lpszToken, "<="))
|
|
op = OP_LE;
|
|
else if (!s_lstrcmpi(lpszToken, "="))
|
|
op = OP_EQ;
|
|
else if (!s_lstrcmpi(lpszToken, "<>"))
|
|
op = OP_NE;
|
|
else {
|
|
s_lstrcpy(lpstmt->szError, "=, <>, <, <=, >, or >=");
|
|
return ERR_EXPECTEDOTHER;
|
|
}
|
|
|
|
/* ALL or ANY specified? */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr, "ALL",
|
|
lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr,
|
|
"ANY", lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
}
|
|
else {
|
|
selectModifier = SELECT_ANY;
|
|
}
|
|
}
|
|
else {
|
|
selectModifier = SELECT_ALL;
|
|
}
|
|
|
|
/* Nested sub-select? */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, "(",
|
|
lpszToken);
|
|
if (err == ERR_SUCCESS) {
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr,
|
|
"SELECT", lpszToken);
|
|
if (err == ERR_SUCCESS) {
|
|
|
|
/* Yes. Get the sub-select */
|
|
err = ParseSelect(lpstmt, lplpszSqlStr, pcbSqlStr,
|
|
lplpSql, &idxRight, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Check for trailing ")" */
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr,
|
|
")", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Set selectModifier if not already set */
|
|
if (selectModifier == SELECT_NOTSELECT)
|
|
selectModifier = SELECT_ONE;
|
|
}
|
|
else {
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
}
|
|
}
|
|
else {
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
}
|
|
|
|
/* Get right child if not a nested sub-select */
|
|
if (selectModifier == SELECT_NOTSELECT) {
|
|
if (err != ERR_SUCCESS) {
|
|
err = ParseExpression(lpstmt, lplpszSqlStr,
|
|
pcbSqlStr, lplpSql, &idxRight, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
|
|
/* No. Maybe it is IN, NOT IN, LIKE, or NOT LIKE? Get */
|
|
/* the keyword */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr, NULL,
|
|
lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Is the operator "NOT LIKE" or "NOT IN"? */
|
|
if (!s_lstrcmpi(lpszToken, "NOT")) {
|
|
|
|
/* Yes. Figure out which one */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr,
|
|
"LIKE", lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr,
|
|
"IN", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
op = OP_NOTIN;
|
|
}
|
|
else
|
|
op = OP_NOTLIKE;
|
|
}
|
|
|
|
/* Is the operator "LIKE"? */
|
|
else if (!s_lstrcmpi(lpszToken, "LIKE")) {
|
|
|
|
/* Yes. Operator is LIKE */
|
|
op = OP_LIKE;
|
|
}
|
|
|
|
/* Is the operator "IN"? */
|
|
else if (!s_lstrcmpi(lpszToken, "IN")) {
|
|
|
|
/* Yes. Operator is IN */
|
|
op = OP_IN;
|
|
}
|
|
else {
|
|
s_lstrcpy(lpstmt->szError, "IN, NOT IN, LIKE, or NOT LIKE");
|
|
return ERR_EXPECTEDOTHER;
|
|
}
|
|
|
|
/* Get right argument */
|
|
switch (op) {
|
|
case OP_IN:
|
|
case OP_NOTIN:
|
|
|
|
/* Get opening paren */
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, "(",
|
|
lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Nested sub-select? */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr,
|
|
"SELECT", lpszToken);
|
|
if (err == ERR_SUCCESS) {
|
|
|
|
/* Yes. Get the sub-select */
|
|
err = ParseSelect(lpstmt, lplpszSqlStr, pcbSqlStr,
|
|
lplpSql, &idxRight, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
else {
|
|
|
|
/* No. Get valuelist */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
err = ParseValuelist(lpstmt, lplpszSqlStr,
|
|
pcbSqlStr, lplpSql, &idxRight, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
|
|
/* Get closing paren */
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, ")",
|
|
lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
break;
|
|
|
|
case OP_LIKE:
|
|
case OP_NOTLIKE:
|
|
|
|
/* Get pattern */
|
|
err = ParsePattern(lpstmt, lplpszSqlStr, pcbSqlStr,
|
|
lplpSql, &idxRight, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
break;
|
|
|
|
default:
|
|
return ERR_INTERNAL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Create the COMPARISON node */
|
|
*lpIdx = AllocateNode(lplpSql, NODE_TYPE_COMPARISON);
|
|
if (*lpIdx == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
lpSqlNode = ToNode(*lplpSql, *lpIdx);
|
|
lpSqlNode->node.comparison.Operator = op;
|
|
lpSqlNode->node.comparison.SelectModifier = selectModifier;
|
|
lpSqlNode->node.comparison.Left = idxLeft;
|
|
lpSqlNode->node.comparison.Right = idxRight;
|
|
lpSqlNode->node.comparison.fSelectivity = 0;
|
|
lpSqlNode->node.comparison.NextRestrict = NO_SQLNODE;
|
|
}
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParseNot(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, LPUSTR lpszToken)
|
|
|
|
/* Retrives a NOT expression from the input stream and creates a */
|
|
/* NODE_TYPE_BOOLEAN node */
|
|
/* */
|
|
/* not ::= comparison | NOT comparison */
|
|
|
|
{
|
|
RETCODE err;
|
|
LPUSTR old_lpszSqlStr;
|
|
SDWORD old_cbSqlStr;
|
|
LPSQLNODE lpSqlNode;
|
|
SQLNODEIDX idxChild;
|
|
|
|
/* Is there a "NOT"? */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr, "NOT", lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* No. Return left child */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
err = ParseComparison(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql, lpIdx,
|
|
lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
else {
|
|
|
|
/* Yes. Get right child */
|
|
err = ParseComparison(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxChild, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Create the BOOLEAN node */
|
|
*lpIdx = AllocateNode(lplpSql, NODE_TYPE_BOOLEAN);
|
|
if (*lpIdx == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
lpSqlNode = ToNode(*lplpSql, *lpIdx);
|
|
lpSqlNode->node.boolean.Operator = OP_NOT;
|
|
lpSqlNode->node.boolean.Left = idxChild;
|
|
lpSqlNode->node.boolean.Right = NO_SQLNODE;
|
|
}
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParseAnd(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, LPUSTR lpszToken)
|
|
|
|
/* Retrives an AND expression from the input stream and creates a */
|
|
/* NODE_TYPE_BOOLEAN node */
|
|
/* */
|
|
/* and ::= not | not AND and */
|
|
|
|
{
|
|
RETCODE err;
|
|
LPUSTR old_lpszSqlStr;
|
|
SDWORD old_cbSqlStr;
|
|
LPSQLNODE lpSqlNode;
|
|
SQLNODEIDX idxLeft;
|
|
SQLNODEIDX idxRight;
|
|
|
|
/* Get left child */
|
|
err = ParseNot(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql, &idxLeft,
|
|
lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Is there an "AND"? */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr, "AND", lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* No. Return left child */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
*lpIdx = idxLeft;
|
|
}
|
|
else {
|
|
|
|
/* Yes. Get right child */
|
|
err = ParseAnd(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql, &idxRight,
|
|
lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Create the BOOLEAN node */
|
|
*lpIdx = AllocateNode(lplpSql, NODE_TYPE_BOOLEAN);
|
|
if (*lpIdx == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
lpSqlNode = ToNode(*lplpSql, *lpIdx);
|
|
lpSqlNode->node.boolean.Operator = OP_AND;
|
|
lpSqlNode->node.boolean.Left = idxLeft;
|
|
lpSqlNode->node.boolean.Right = idxRight;
|
|
}
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParseBoolean(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, LPUSTR lpszToken)
|
|
|
|
/* Retrives an OR expression from the input stream and creates a */
|
|
/* NODE_TYPE_BOOLEAN node */
|
|
/* */
|
|
/* boolean ::= and | and OR boolean */
|
|
|
|
{
|
|
RETCODE err;
|
|
LPUSTR old_lpszSqlStr;
|
|
SDWORD old_cbSqlStr;
|
|
LPSQLNODE lpSqlNode;
|
|
SQLNODEIDX idxLeft;
|
|
SQLNODEIDX idxRight;
|
|
|
|
/* Get left child */
|
|
err = ParseAnd(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql, &idxLeft,
|
|
lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Is there an "OR"? */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr, "OR", lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* No. Return left child */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
*lpIdx = idxLeft;
|
|
}
|
|
else {
|
|
|
|
/* Yes. Get right child */
|
|
err = ParseBoolean(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxRight, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Create the BOOLEAN node */
|
|
*lpIdx = AllocateNode(lplpSql, NODE_TYPE_BOOLEAN);
|
|
if (*lpIdx == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
lpSqlNode = ToNode(*lplpSql, *lpIdx);
|
|
lpSqlNode->node.boolean.Operator = OP_OR;
|
|
lpSqlNode->node.boolean.Left = idxLeft;
|
|
lpSqlNode->node.boolean.Right = idxRight;
|
|
}
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParseWhere(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, LPUSTR lpszToken)
|
|
|
|
/* Retrives an WHERE expression from the input stream and creates a */
|
|
/* NODE_TYPE_BOOLEAN node */
|
|
/* */
|
|
/* where ::= | WHERE boolean */
|
|
|
|
{
|
|
RETCODE err;
|
|
LPUSTR old_lpszSqlStr;
|
|
SDWORD old_cbSqlStr;
|
|
|
|
/* Is there a "WHERE"? */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr, "WHERE", lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* No. Return null */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
*lpIdx = NO_SQLNODE;
|
|
}
|
|
else {
|
|
|
|
/* Yes. Return boolean */
|
|
err = ParseBoolean(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
lpIdx, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParseHaving(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, LPUSTR lpszToken)
|
|
|
|
/* Retrives a HAVING expression from the input stream and creates a */
|
|
/* NODE_TYPE_BOOLEAN node */
|
|
/* */
|
|
/* having ::= | HAVING boolean */
|
|
|
|
{
|
|
RETCODE err;
|
|
LPUSTR old_lpszSqlStr;
|
|
SDWORD old_cbSqlStr;
|
|
|
|
/* Is there a "HAVING"? */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr, "HAVING", lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* No. Return null */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
*lpIdx = NO_SQLNODE;
|
|
}
|
|
else {
|
|
|
|
/* Yes. Return boolean */
|
|
err = ParseBoolean(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
lpIdx, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParseSelectlist(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, LPUSTR lpszToken)
|
|
|
|
/* Retrives a list of expressions from the input stream and creates a */
|
|
/* NODE_TYPE_VALUES node */
|
|
/* */
|
|
/* selectlist ::= selectlistitem , selectlist | selectlistitem */
|
|
/* selectlistitem ::= expression | expression alias | aliasname . * | expression AS alias */
|
|
|
|
{
|
|
RETCODE err;
|
|
LPUSTR old_lpszSqlStr;
|
|
SDWORD old_cbSqlStr;
|
|
LPSQLNODE lpSqlNode;
|
|
SQLNODEIDX idxValues;
|
|
SQLNODEIDX idxValue;
|
|
STRINGIDX idxAlias;
|
|
SQLNODEIDX idxValuesPrev;
|
|
|
|
/* Get list ofnodes */
|
|
idxValuesPrev = NO_SQLNODE;
|
|
while (TRUE) {
|
|
|
|
/* Look for aliasname.* */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
idxValue = NO_SQLNODE;
|
|
err = GetIdentifier(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxAlias, lpszToken);
|
|
if (err == ERR_SUCCESS) {
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, ".", lpszToken);
|
|
if (err == ERR_SUCCESS) {
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, "*",
|
|
lpszToken);
|
|
}
|
|
}
|
|
|
|
/* Find it? */
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* Get expression */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
err = ParseExpression(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxValue, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Is there an alias */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr, "AS", lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
err = GetIdentifier(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxAlias, lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
idxAlias = NO_STRING;
|
|
}
|
|
}
|
|
else {
|
|
err = GetIdentifier(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxAlias, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
|
|
}
|
|
|
|
/* Create the VALUES node */
|
|
idxValues = AllocateNode(lplpSql, NODE_TYPE_VALUES);
|
|
if (idxValues == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
lpSqlNode = ToNode(*lplpSql, idxValues);
|
|
lpSqlNode->node.values.Value = idxValue;
|
|
lpSqlNode->node.values.Alias = idxAlias;
|
|
lpSqlNode->node.values.Next = NO_SQLNODE;
|
|
|
|
/* Put node on list */
|
|
if (idxValuesPrev != NO_SQLNODE) {
|
|
lpSqlNode = ToNode(*lplpSql, idxValuesPrev);
|
|
lpSqlNode->node.values.Next = idxValues;
|
|
}
|
|
else
|
|
*lpIdx = idxValues;
|
|
idxValuesPrev = idxValues;
|
|
|
|
/* Is there a comma? */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, ",", lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* No. This is the last value */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
RETCODE INTFUNC ParseSelectcols(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, BOOL FAR *lpfDistinct,
|
|
LPUSTR lpszToken)
|
|
|
|
/* Retrives a select list from the input stream and creates a */
|
|
/* NODE_TYPE_VALUES node */
|
|
/* */
|
|
/* selectcols ::= selectallcols * | selectallcols selectlist */
|
|
/* selectallcols ::= | ALL | DISTINCT */
|
|
|
|
{
|
|
RETCODE err;
|
|
LPUSTR old_lpszSqlStr;
|
|
SDWORD old_cbSqlStr;
|
|
|
|
/* See if ALL or DISTINCT is specified */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr, NULL, lpszToken);
|
|
if (err == ERR_SUCCESS) {
|
|
if (!s_lstrcmpi(lpszToken, "ALL"))
|
|
*lpfDistinct = FALSE;
|
|
else if (!s_lstrcmpi(lpszToken, "DISTINCT"))
|
|
*lpfDistinct = TRUE;
|
|
else {
|
|
*lpfDistinct = FALSE;
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
}
|
|
}
|
|
else {
|
|
*lpfDistinct = FALSE;
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
}
|
|
|
|
/* Is '*' is specified? */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, "*", lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* No. Get list of values */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
err = ParseSelectlist(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
lpIdx, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
else {
|
|
*lpIdx = NO_SQLNODE;
|
|
}
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParseColumn(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, LPUSTR lpszToken)
|
|
|
|
/* Retrives an unqualified columnname from the input stream and creates a */
|
|
/* NODE_TYPE_COLUMN node */
|
|
/* */
|
|
/* column ::= columnname */
|
|
|
|
{
|
|
LPSQLNODE lpSqlNode;
|
|
STRINGIDX idxColumn;
|
|
RETCODE err;
|
|
|
|
/* Get the columnname */
|
|
err = GetIdentifier(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxColumn, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Create the COLUMN node */
|
|
*lpIdx = AllocateNode(lplpSql, NODE_TYPE_COLUMN);
|
|
if (*lpIdx == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
lpSqlNode = ToNode(*lplpSql, *lpIdx);
|
|
lpSqlNode->node.column.Tablealias = NO_STRING;
|
|
lpSqlNode->node.column.Column = idxColumn;
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParseColumnlist(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, LPUSTR lpszToken)
|
|
|
|
/* Retrives a list of columnnames from the input stream and creates a */
|
|
/* NODE_TYPE_COLUMNS node */
|
|
/* */
|
|
/* columnlist ::= column , columnlist | column */
|
|
|
|
{
|
|
RETCODE err;
|
|
LPUSTR old_lpszSqlStr;
|
|
SDWORD old_cbSqlStr;
|
|
LPSQLNODE lpSqlNode;
|
|
SQLNODEIDX idxColumns;
|
|
SQLNODEIDX idxColumn;
|
|
SQLNODEIDX idxColumnsPrev;
|
|
|
|
/* Get list of nodes */
|
|
idxColumnsPrev = NO_SQLNODE;
|
|
while (TRUE) {
|
|
|
|
/* Get columnname */
|
|
err = ParseColumn(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxColumn, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Create the COLUMNS node */
|
|
idxColumns = AllocateNode(lplpSql, NODE_TYPE_COLUMNS);
|
|
if (idxColumns == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
lpSqlNode = ToNode(*lplpSql, idxColumns);
|
|
lpSqlNode->node.columns.Column = idxColumn;
|
|
lpSqlNode->node.columns.Next = NO_SQLNODE;
|
|
|
|
/* Put node on list */
|
|
if (idxColumnsPrev != NO_SQLNODE) {
|
|
lpSqlNode = ToNode(*lplpSql, idxColumnsPrev);
|
|
lpSqlNode->node.columns.Next = idxColumns;
|
|
}
|
|
else
|
|
*lpIdx = idxColumns;
|
|
idxColumnsPrev = idxColumns;
|
|
|
|
/* Is there a comma? */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, ",", lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* No. This is the last one */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
break;
|
|
}
|
|
}
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParseInsertvals(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdxColumns,
|
|
SQLNODEIDX FAR *lpIdxValues, LPUSTR lpszToken)
|
|
|
|
/* Retrives an INSERT statement from the input stream and creates a */
|
|
/* NODE_TYPE_COLUMNS node and a NODE_TYPE_VALUES node */
|
|
/* */
|
|
/* insertvals ::= ( columnlist ) VALUES ( valuelist ) | */
|
|
/* ( columnlist ) VALUES ( SELECT select ) | */
|
|
/* VALUES ( valuelist ) | VALUES ( SELECT select ) */
|
|
|
|
{
|
|
LPUSTR old_lpszSqlStr;
|
|
SDWORD old_cbSqlStr;
|
|
RETCODE err;
|
|
|
|
/* Is there a column list? */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, "(", lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* No. Send null node back */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
*lpIdxColumns = NO_SQLNODE;
|
|
}
|
|
else {
|
|
|
|
/* Yes. Get the list */
|
|
err = ParseColumnlist(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
lpIdxColumns, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Error if no ending ')' */
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, ")", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
|
|
/* Make sure keyword is given */
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr, "VALUES", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Error if no starting '(' */
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, "(", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Sub-select? */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr, "SELECT", lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* No. Get the value list */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
err = ParseValuelist(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
lpIdxValues, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
else {
|
|
|
|
/* Yes. Get SELECT */
|
|
err = ParseSelect(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
lpIdxValues, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
|
|
/* Error if no ending ')' */
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, ")", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParseSet(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, LPUSTR lpszToken)
|
|
|
|
/* Retrives an update expressions from the input stream and creates a */
|
|
/* NODE_TYPE_UPDATEVALUES node */
|
|
/* */
|
|
/* set ::= column = NULL | column = expression */
|
|
|
|
{
|
|
LPSQLNODE lpSqlNode;
|
|
SQLNODEIDX idxColumn;
|
|
SQLNODEIDX idxValue;
|
|
LPUSTR old_lpszSqlStr;
|
|
SDWORD old_cbSqlStr;
|
|
RETCODE err;
|
|
|
|
/* Get the column name */
|
|
err = ParseColumn(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql, &idxColumn,
|
|
lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Get the '=' sign */
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, "=", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* NULL specified? */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr, "NULL", lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* No. Get value */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
err = ParseExpression(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxValue, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
else {
|
|
|
|
/* Yes. Create a NULL node */
|
|
idxValue = AllocateNode(lplpSql, NODE_TYPE_NULL);
|
|
if (idxValue == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
}
|
|
|
|
/* Create the UPDATEVALUES node */
|
|
*lpIdx = AllocateNode(lplpSql, NODE_TYPE_UPDATEVALUES);
|
|
if (*lpIdx == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
lpSqlNode = ToNode(*lplpSql, *lpIdx);
|
|
lpSqlNode->node.updatevalues.Column = idxColumn;
|
|
lpSqlNode->node.updatevalues.Value = idxValue;
|
|
lpSqlNode->node.updatevalues.Next = NO_SQLNODE;
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParseSetlist(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, LPUSTR lpszToken)
|
|
|
|
/* Retrives a list of update expressions from the input stream and creates */
|
|
/* a NODE_TYPE_UPDATEVALUES node */
|
|
/* */
|
|
/* setlist ::= set , setlist | set */
|
|
|
|
{
|
|
RETCODE err;
|
|
LPUSTR old_lpszSqlStr;
|
|
SDWORD old_cbSqlStr;
|
|
LPSQLNODE lpSqlNode;
|
|
SQLNODEIDX idxUpdateValues;
|
|
SQLNODEIDX idxUpdateValuesPrev;
|
|
|
|
/* Get list of nodes */
|
|
idxUpdateValuesPrev = NO_SQLNODE;
|
|
while (TRUE) {
|
|
|
|
/* Get update expression */
|
|
err = ParseSet(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxUpdateValues, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Put node on list */
|
|
if (idxUpdateValuesPrev != NO_SQLNODE) {
|
|
lpSqlNode = ToNode(*lplpSql, idxUpdateValuesPrev);
|
|
lpSqlNode->node.updatevalues.Next = idxUpdateValues;
|
|
}
|
|
else
|
|
*lpIdx = idxUpdateValues;
|
|
idxUpdateValuesPrev = idxUpdateValues;
|
|
|
|
/* Is there a comma? */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, ",", lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* No. This is the last update value */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParseUpdate(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, LPUSTR lpszToken)
|
|
|
|
/* Retrives an UPDATE statement from the input stream and creates a */
|
|
/* NODE_TYPE_UPDATE node */
|
|
/* */
|
|
/* update ::= table SET setlist where */
|
|
|
|
{
|
|
LPSQLNODE lpSqlNode;
|
|
SQLNODEIDX idxTable;
|
|
SQLNODEIDX idxUpdatevalues;
|
|
SQLNODEIDX idxPredicate;
|
|
RETCODE err;
|
|
|
|
/* Get tablename */
|
|
err = ParseTable(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql, &idxTable,
|
|
lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Make sure keyword is given */
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr, "SET", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Get update values */
|
|
err = ParseSetlist(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxUpdatevalues, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Get predicate */
|
|
err = ParseWhere(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql, &idxPredicate,
|
|
lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Create the UPDATE node */
|
|
*lpIdx = AllocateNode(lplpSql, NODE_TYPE_UPDATE);
|
|
if (*lpIdx == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
lpSqlNode = ToNode(*lplpSql, *lpIdx);
|
|
lpSqlNode->node.update.Table = idxTable;
|
|
lpSqlNode->node.update.Updatevalues = idxUpdatevalues;
|
|
lpSqlNode->node.update.Predicate = idxPredicate;
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParseInsert(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, LPUSTR lpszToken)
|
|
|
|
/* Retrives an INSERT statement from the input stream and creates a */
|
|
/* NODE_TYPE_INSERT node */
|
|
/* */
|
|
/* insert ::= INTO table insertvals */
|
|
|
|
{
|
|
LPSQLNODE lpSqlNode;
|
|
SQLNODEIDX idxTable;
|
|
SQLNODEIDX idxColumns;
|
|
SQLNODEIDX idxValues;
|
|
RETCODE err;
|
|
|
|
/* Make sure keyword is given */
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr, "INTO", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Get table */
|
|
err = ParseTable(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql, &idxTable,
|
|
lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Get insert values */
|
|
err = ParseInsertvals(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxColumns, &idxValues, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Create the INSERT node */
|
|
*lpIdx = AllocateNode(lplpSql, NODE_TYPE_INSERT);
|
|
if (*lpIdx == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
lpSqlNode = ToNode(*lplpSql, *lpIdx);
|
|
lpSqlNode->node.insert.Table = idxTable;
|
|
lpSqlNode->node.insert.Columns = idxColumns;
|
|
lpSqlNode->node.insert.Values = idxValues;
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParseDelete(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, LPUSTR lpszToken)
|
|
|
|
/* Retrives a DELETE statement from the input stream and creates a */
|
|
/* NODE_TYPE_DELETE node */
|
|
/* */
|
|
/* delete ::= FROM table where */
|
|
|
|
{
|
|
LPSQLNODE lpSqlNode;
|
|
SQLNODEIDX idxTable;
|
|
SQLNODEIDX idxPredicate;
|
|
RETCODE err;
|
|
|
|
/* Make sure "FROM" is specified */
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr, "FROM", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Get table */
|
|
err = ParseTable(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql, &idxTable,
|
|
lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Get predicate */
|
|
err = ParseWhere(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql, &idxPredicate,
|
|
lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Create the DELETE node */
|
|
*lpIdx = AllocateNode(lplpSql, NODE_TYPE_DELETE);
|
|
if (*lpIdx == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
lpSqlNode = ToNode(*lplpSql, *lpIdx);
|
|
lpSqlNode->node.delet.Table = idxTable;
|
|
lpSqlNode->node.delet.Predicate = idxPredicate;
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParseSelect(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, LPUSTR lpszToken)
|
|
|
|
/* Retrives a SELECT statement from the input stream and creates a */
|
|
/* NODE_TYPE_SELECT node */
|
|
/* */
|
|
/* select ::= selectcols FROM tablelist where groupby having */
|
|
|
|
{
|
|
LPSQLNODE lpSqlNode;
|
|
BOOL fDistinct;
|
|
SQLNODEIDX idxValues;
|
|
SQLNODEIDX idxTables;
|
|
SQLNODEIDX idxPredicate;
|
|
SQLNODEIDX idxGroupbycolumns;
|
|
SQLNODEIDX idxHaving;
|
|
RETCODE err;
|
|
|
|
/* Get selection columns */
|
|
err = ParseSelectcols(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxValues, &fDistinct, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Make sure "FROM" is specified */
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr, "FROM", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Get list of tables */
|
|
err = ParseTablelist(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql, &idxTables,
|
|
lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Get predicate */
|
|
err = ParseWhere(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql, &idxPredicate,
|
|
lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Get group by */
|
|
err = ParseGroupby(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxGroupbycolumns, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Get having */
|
|
err = ParseHaving(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql, &idxHaving,
|
|
lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Create the SELECT node */
|
|
*lpIdx = AllocateNode(lplpSql, NODE_TYPE_SELECT);
|
|
if (*lpIdx == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
lpSqlNode = ToNode(*lplpSql, *lpIdx);
|
|
lpSqlNode->node.select.Distinct = fDistinct;
|
|
lpSqlNode->node.select.Values = idxValues;
|
|
lpSqlNode->node.select.Tables = idxTables;
|
|
lpSqlNode->node.select.Predicate = idxPredicate;
|
|
lpSqlNode->node.select.Groupbycolumns = idxGroupbycolumns;
|
|
lpSqlNode->node.select.Having = idxHaving;
|
|
lpSqlNode->node.select.Sortcolumns = NO_SQLNODE;
|
|
lpSqlNode->node.select.SortDirective = NO_STRING;
|
|
lpSqlNode->node.select.SortRecordsize = 0;
|
|
lpSqlNode->node.select.SortBookmarks = 0;
|
|
lpSqlNode->node.select.Aggregates = NO_SQLNODE;
|
|
lpSqlNode->node.select.SortKeysize = 0;
|
|
lpSqlNode->node.select.DistinctDirective = NO_STRING;
|
|
lpSqlNode->node.select.DistinctRecordsize = 0;
|
|
lpSqlNode->node.select.ImplicitGroupby = FALSE;
|
|
lpSqlNode->node.select.EnclosingStatement = NO_SQLNODE;
|
|
lpSqlNode->node.select.fPushdownSort = FALSE;
|
|
lpSqlNode->node.select.fMSAccess = FALSE;
|
|
lpSqlNode->node.select.RowCount = -1;
|
|
lpSqlNode->node.select.CurrentRow = AFTER_LAST_ROW;
|
|
lpSqlNode->node.select.SortfileName = NO_STRING;
|
|
lpSqlNode->node.select.Sortfile = HFILE_ERROR;
|
|
lpSqlNode->node.select.ReturningDistinct = FALSE;
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParseDrop(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, LPUSTR lpszToken)
|
|
|
|
/* Retrives a DROP TABLE statement from the input stream and creates a */
|
|
/* NODE_TYPE_DROP node */
|
|
/* */
|
|
/* drop ::= TABLE tablename | INDEX indexname */
|
|
|
|
{
|
|
LPSQLNODE lpSqlNode;
|
|
STRINGIDX idxIndexname;
|
|
STRINGIDX idxTablename;
|
|
RETCODE err;
|
|
LPUSTR old_lpszSqlStr;
|
|
SDWORD old_cbSqlStr;
|
|
|
|
/* Is an index being dropped? */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr, "INDEX", lpszToken);
|
|
if (err == ERR_SUCCESS) {
|
|
|
|
/* Yes. Get index name */
|
|
err = GetIdentifier(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxIndexname, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Create the DROPINDEX node */
|
|
*lpIdx = AllocateNode(lplpSql, NODE_TYPE_DROPINDEX);
|
|
if (*lpIdx == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
lpSqlNode = ToNode(*lplpSql, *lpIdx);
|
|
lpSqlNode->node.dropindex.Index = idxIndexname;
|
|
}
|
|
else {
|
|
|
|
/* No. Make sure a table is being dropped */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr, "TABLE", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Get the tablename */
|
|
err = GetIdentifier(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxTablename, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Create the DROP node */
|
|
*lpIdx = AllocateNode(lplpSql, NODE_TYPE_DROP);
|
|
if (*lpIdx == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
lpSqlNode = ToNode(*lplpSql, *lpIdx);
|
|
lpSqlNode->node.drop.Table = idxTablename;
|
|
}
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParseCreatecol(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, LPUSTR lpszToken)
|
|
|
|
/* Retrives a list of column defintiions from the input stream and creates */
|
|
/* a NODE_TYPE_CREATECOLS node */
|
|
/* */
|
|
/* createcol ::= columnname datatype | columnname datatype ( integer ) | */
|
|
/* columnname datatype ( integer , integer ) */
|
|
|
|
{
|
|
LPUSTR old_lpszSqlStr;
|
|
SDWORD old_cbSqlStr;
|
|
LPSQLNODE lpSqlNode;
|
|
STRINGIDX idxName;
|
|
STRINGIDX idxType;
|
|
UWORD cParams;
|
|
UDWORD udParam1;
|
|
UDWORD udParam2;
|
|
RETCODE err;
|
|
|
|
/* Get the columnname */
|
|
err = GetIdentifier(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxName, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Get the type */
|
|
err = GetIdentifier(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxType, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Are there any create parameters? */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, "(", lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* No. */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
cParams = 0;
|
|
udParam1 = 0;
|
|
udParam2 = 0;
|
|
}
|
|
else {
|
|
|
|
/* Yes. Get the first create param */
|
|
cParams = 1;
|
|
err = GetInteger(lpstmt, lplpszSqlStr, pcbSqlStr, &udParam1,
|
|
lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Get the next token */
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, NULL, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Is there a second create param */
|
|
if (*lpszToken == ',') {
|
|
|
|
/* Yes. Get that one too */
|
|
cParams = 2;
|
|
err = GetInteger(lpstmt, lplpszSqlStr, pcbSqlStr, &udParam2,
|
|
lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Get the next token */
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, NULL, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
else
|
|
udParam2 = 0;
|
|
|
|
/* Error if terminating paren is missing */
|
|
if (*lpszToken != ')') {
|
|
s_lstrcpy(lpstmt->szError, ")");
|
|
return ERR_EXPECTEDOTHER;
|
|
}
|
|
}
|
|
|
|
/* Create the CREATECOLS node */
|
|
*lpIdx = AllocateNode(lplpSql, NODE_TYPE_CREATECOLS);
|
|
if (*lpIdx == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
lpSqlNode = ToNode(*lplpSql, *lpIdx);
|
|
lpSqlNode->node.createcols.Name = idxName;
|
|
lpSqlNode->node.createcols.Type = idxType;
|
|
lpSqlNode->node.createcols.Params = cParams;
|
|
lpSqlNode->node.createcols.Param1 = udParam1;
|
|
lpSqlNode->node.createcols.Param2 = udParam2;
|
|
lpSqlNode->node.createcols.Next = NO_SQLNODE;
|
|
lpSqlNode->node.createcols.iSqlType = (UWORD) -1;
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParseCreatecols(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, LPUSTR lpszToken)
|
|
|
|
/* Retrives a list of column defintiions from the input stream and creates */
|
|
/* a NODE_TYPE_CREATECOLS node */
|
|
/* */
|
|
/* createcols ::= createcol , createcols | createcol */
|
|
|
|
{
|
|
RETCODE err;
|
|
LPUSTR old_lpszSqlStr;
|
|
SDWORD old_cbSqlStr;
|
|
LPSQLNODE lpSqlNode;
|
|
SQLNODEIDX idxCreateCols;
|
|
SQLNODEIDX idxCreateColsPrev;
|
|
|
|
/* Getlist of nodes */
|
|
idxCreateColsPrev = NO_SQLNODE;
|
|
while (TRUE) {
|
|
|
|
/* Get create column */
|
|
err = ParseCreatecol(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxCreateCols, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Put node on list */
|
|
if (idxCreateColsPrev != NO_SQLNODE) {
|
|
lpSqlNode = ToNode(*lplpSql, idxCreateColsPrev);
|
|
lpSqlNode->node.createcols.Next = idxCreateCols;
|
|
}
|
|
else
|
|
*lpIdx = idxCreateCols;
|
|
idxCreateColsPrev = idxCreateCols;
|
|
|
|
/* Is there a comma? */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, ",", lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* No. This is the last create column */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParseIndexcolumn(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, BOOL FAR *lpfDescending,
|
|
LPUSTR lpszToken)
|
|
|
|
/* Retrives anindex column name from the input stream and creates a */
|
|
/* NODE_TYPE_COLUMN node */
|
|
/* */
|
|
/* indexcolumn ::= column asc */
|
|
/* asc ::= | ASC | DESC */
|
|
|
|
{
|
|
RETCODE err;
|
|
LPUSTR old_lpszSqlStr;
|
|
SDWORD old_cbSqlStr;
|
|
|
|
/* Get column name */
|
|
err = ParseColumn(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql, lpIdx,
|
|
lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Get ASC or DESC */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr, NULL, lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
*lpfDescending = FALSE;
|
|
}
|
|
else if (!s_lstrcmpi(lpszToken, "ASC")) {
|
|
*lpfDescending = FALSE;
|
|
}
|
|
else if (!s_lstrcmpi(lpszToken, "DESC")) {
|
|
*lpfDescending = TRUE;
|
|
}
|
|
else {
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
*lpfDescending = FALSE;
|
|
}
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParseIndexcolumns(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, LPUSTR lpszToken)
|
|
|
|
/* Retrives a column list from the input stream and creates a */
|
|
/* NODE_TYPE_SORTCOLUMNS node */
|
|
/* */
|
|
/* indexcolumns ::= indexcolumn | indexcolumn , indexcolumns */
|
|
|
|
{
|
|
RETCODE err;
|
|
LPUSTR old_lpszSqlStr;
|
|
SDWORD old_cbSqlStr;
|
|
LPSQLNODE lpSqlNode;
|
|
BOOL fDescending;
|
|
SQLNODEIDX idxColumn;
|
|
SQLNODEIDX idxSortcolumns;
|
|
SQLNODEIDX idxSortcolumnsPrev;
|
|
|
|
/* Get list of nodes */
|
|
idxSortcolumnsPrev = NO_SQLNODE;
|
|
while (TRUE) {
|
|
|
|
/* Get indexcolumn column */
|
|
err = ParseIndexcolumn(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxColumn, &fDescending, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Create the SORTCOLUMNS node */
|
|
idxSortcolumns = AllocateNode(lplpSql, NODE_TYPE_SORTCOLUMNS);
|
|
if (idxSortcolumns == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
lpSqlNode = ToNode(*lplpSql, idxSortcolumns);
|
|
lpSqlNode->node.sortcolumns.Column = idxColumn;
|
|
lpSqlNode->node.sortcolumns.Descending = fDescending;
|
|
lpSqlNode->node.sortcolumns.Next = NO_SQLNODE;
|
|
|
|
/* Put node on list */
|
|
if (idxSortcolumnsPrev != NO_SQLNODE) {
|
|
lpSqlNode = ToNode(*lplpSql, idxSortcolumnsPrev);
|
|
lpSqlNode->node.sortcolumns.Next = idxSortcolumns;
|
|
}
|
|
else
|
|
*lpIdx = idxSortcolumns;
|
|
idxSortcolumnsPrev = idxSortcolumns;
|
|
|
|
/* Is there a comma? */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, ",", lpszToken);
|
|
if (err != ERR_SUCCESS) {
|
|
|
|
/* No. This is the last indexcolumn */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParseCreate(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, LPUSTR lpszToken)
|
|
|
|
/* Retrives a CREATE TABLE statement from the input stream and creates a */
|
|
/* NODE_TYPE_CREATE or NODE_TYPE_CREATEINDEX node */
|
|
/* */
|
|
/* create ::= TABLE tablename ( createcols ) | */
|
|
/* INDEX indexname ON table ( indexcolumns ) */
|
|
{
|
|
LPUSTR old_lpszSqlStr;
|
|
SDWORD old_cbSqlStr;
|
|
LPSQLNODE lpSqlNode;
|
|
STRINGIDX idxIndexname;
|
|
RETCODE err;
|
|
STRINGIDX idxTablename;
|
|
SQLNODEIDX idxTable;
|
|
SQLNODEIDX idxColumns;
|
|
BOOL fUnique;
|
|
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr, "UNIQUE",
|
|
lpszToken);
|
|
if (err == ERR_SUCCESS) {
|
|
fUnique = TRUE;
|
|
}
|
|
else {
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
fUnique = FALSE;
|
|
}
|
|
|
|
/* Is an index being created? */
|
|
old_lpszSqlStr = *lplpszSqlStr;
|
|
old_cbSqlStr = *pcbSqlStr;
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr, "INDEX", lpszToken);
|
|
if (err == ERR_SUCCESS) {
|
|
|
|
/* Get index name */
|
|
err = GetIdentifier(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxIndexname, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Make sure "ON" is specified */
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr, "ON", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Get the table */
|
|
err = ParseTable(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql, &idxTable,
|
|
lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Get opening paren for column list */
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, "(", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Get indexcolumns */
|
|
err = ParseIndexcolumns(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxColumns, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Get closing paren for column list */
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, ")", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Create the CREATEINDEX node */
|
|
*lpIdx = AllocateNode(lplpSql, NODE_TYPE_CREATEINDEX);
|
|
if (*lpIdx == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
lpSqlNode = ToNode(*lplpSql, *lpIdx);
|
|
lpSqlNode->node.createindex.Index = idxIndexname;
|
|
lpSqlNode->node.createindex.Table = idxTable;
|
|
lpSqlNode->node.createindex.Columns = idxColumns;
|
|
lpSqlNode->node.createindex.Unique = fUnique;
|
|
}
|
|
else {
|
|
|
|
/* No. Make sure a table is being created */
|
|
*lplpszSqlStr = old_lpszSqlStr;
|
|
*pcbSqlStr = old_cbSqlStr;
|
|
err = GetKeyword(lpstmt, lplpszSqlStr, pcbSqlStr, "TABLE", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Error if UNIQUE specified */
|
|
if (fUnique) {
|
|
s_lstrcpy(lpstmt->szError, "UNIQUE");
|
|
return ERR_EXPECTEDOTHER;
|
|
}
|
|
|
|
/* Get the tablename */
|
|
err = GetIdentifier(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxTablename, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Get opening paren for column descriptions */
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, "(", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Get the columnlist */
|
|
err = ParseCreatecols(lpstmt, lplpszSqlStr, pcbSqlStr, lplpSql,
|
|
&idxColumns, lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Get closing paren for column descriptions */
|
|
err = GetSymbol(lpstmt, lplpszSqlStr, pcbSqlStr, ")", lpszToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Create the CREATE node */
|
|
*lpIdx = AllocateNode(lplpSql, NODE_TYPE_CREATE);
|
|
if (*lpIdx == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
lpSqlNode = ToNode(*lplpSql, *lpIdx);
|
|
lpSqlNode->node.create.Table = idxTablename;
|
|
lpSqlNode->node.create.Columns = idxColumns;
|
|
}
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ParsePassthrough(LPSTMT lpstmt, LPUSTR FAR *lplpszSqlStr,
|
|
SDWORD FAR *pcbSqlStr, LPSQLTREE FAR *lplpSql,
|
|
SQLNODEIDX FAR *lpIdx, LPUSTR lpszToken)
|
|
|
|
/* Retrives a "SQL" statement from the input stream and creates a */
|
|
/* NODE_TYPE_PASSTHROUGH node */
|
|
/* */
|
|
/* passthrough ::= */
|
|
|
|
{
|
|
/* Create the PASSTHROUGH node */
|
|
*lpIdx = AllocateNode(lplpSql, NODE_TYPE_PASSTHROUGH);
|
|
if (*lpIdx == NO_SQLNODE)
|
|
return ERR_MEMALLOCFAIL;
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC Parse(LPSTMT lpstmt, LPISAM lpISAM, LPUSTR lpszSqlStr,
|
|
SDWORD cbSqlStr, LPSQLTREE FAR *lplpSql)
|
|
|
|
/* Parses an SQL statement and returns a parse tree. The parse tree is */
|
|
/* stored in an array of nodes. The root of the tree is in node 0. */
|
|
/* String values (such as table names) are not stored in the parse tree, */
|
|
/* instead they are stored in a "string area". The parse tree array is */
|
|
/* returned in lplpSql. A pointer to the "string area" is returned in the */
|
|
/* root of the parse tree. The user must call FreeTree() to deallocate */
|
|
/* the space pointed to by lplpSql. */
|
|
/* */
|
|
/* statement ::= CREATE create | DROP drop | SELECT select orderby | */
|
|
/* INSERT insert | DELETE delete | UPDATE update | SQL */
|
|
|
|
{
|
|
LPSQLTREE lpSql;
|
|
HGLOBAL hParseArray;
|
|
UCHAR szToken[MAX_TOKEN_SIZE + 1];
|
|
RETCODE err;
|
|
SQLNODEIDX idxRoot;
|
|
UWORD fType;
|
|
SQLNODEIDX idxSortcolumns;
|
|
|
|
/* Get first token */
|
|
err = GetKeyword(lpstmt, &lpszSqlStr, &cbSqlStr, NULL, szToken);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Allocate initial space */
|
|
hParseArray = GlobalAlloc(GMEM_MOVEABLE,
|
|
sizeof(SQLNODE) * SQLNODE_ALLOC);
|
|
if (hParseArray == NULL)
|
|
return ERR_MEMALLOCFAIL;
|
|
lpSql = (LPSQLTREE) GlobalLock(hParseArray);
|
|
if (lpSql == NULL) {
|
|
GlobalFree(hParseArray);
|
|
return ERR_MEMALLOCFAIL;
|
|
}
|
|
|
|
/* Initialize the root node */
|
|
lpSql->sqlNodeType = NODE_TYPE_ROOT;
|
|
lpSql->node.root.lpISAM = lpISAM;
|
|
lpSql->node.root.hParseArray = hParseArray;
|
|
lpSql->node.root.cParseArray = SQLNODE_ALLOC;
|
|
lpSql->node.root.iParseArray = 0;
|
|
lpSql->node.root.hStringArea = NULL;
|
|
lpSql->node.root.lpStringArea = NULL;
|
|
lpSql->node.root.cbStringArea = 0;
|
|
lpSql->node.root.ibStringArea = -1;
|
|
lpSql->node.root.sql = NO_SQLNODE;
|
|
lpSql->node.root.parameters = NO_SQLNODE;
|
|
lpSql->node.root.passthroughParams = FALSE;
|
|
|
|
/* Parse statement */
|
|
if (!s_lstrcmpi(szToken, "CREATE")) {
|
|
err = ERR_CREATE_NOTSUPP;
|
|
// err = ParseCreate(lpstmt, &lpszSqlStr, &cbSqlStr, &lpSql, &idxRoot,
|
|
// szToken);
|
|
}
|
|
else if (!s_lstrcmpi(szToken, "DROP")) {
|
|
err = ERR_DROP_NOTSUPP;
|
|
// err = ParseDrop(lpstmt, &lpszSqlStr, &cbSqlStr, &lpSql, &idxRoot,
|
|
// szToken);
|
|
}
|
|
else if (!s_lstrcmpi(szToken, "SELECT")) {
|
|
err = ParseSelect(lpstmt, &lpszSqlStr, &cbSqlStr, &lpSql, &idxRoot,
|
|
szToken);
|
|
if (err == ERR_SUCCESS) {
|
|
err = ParseOrderby(lpstmt, &lpszSqlStr, &cbSqlStr, &lpSql,
|
|
&idxSortcolumns, szToken);
|
|
if (idxSortcolumns != NO_SQLNODE) {
|
|
ToNode(lpSql, idxRoot)->node.select.Sortcolumns =
|
|
idxSortcolumns;
|
|
}
|
|
}
|
|
}
|
|
else if (!s_lstrcmpi(szToken, "INSERT")) {
|
|
err = ERR_INSERT_NOTSUPP;
|
|
// err = ParseInsert(lpstmt, &lpszSqlStr, &cbSqlStr, &lpSql, &idxRoot,
|
|
// szToken);
|
|
}
|
|
else if (!s_lstrcmpi(szToken, "DELETE")) {
|
|
err = ERR_DELETE_NOTSUPP;
|
|
// err = ParseDelete(lpstmt, &lpszSqlStr, &cbSqlStr, &lpSql, &idxRoot,
|
|
// szToken);
|
|
}
|
|
else if (!s_lstrcmpi(szToken, "UPDATE")) {
|
|
err = ERR_UPDATE_NOTSUPP;
|
|
// err = ParseUpdate(lpstmt, &lpszSqlStr, &cbSqlStr, &lpSql, &idxRoot,
|
|
// szToken);
|
|
}
|
|
else if (!s_lstrcmpi(szToken, "SQL")) {
|
|
//Sai Wong
|
|
// err = ERR_NOTSUPPORTED;
|
|
err = ParsePassthrough(lpstmt, &lpszSqlStr, &cbSqlStr, &lpSql,
|
|
&idxRoot, szToken);
|
|
}
|
|
else if (!lstrcmpi((char*)szToken, "ALTER")) {
|
|
//Dr. DeeBee Driver does not cater for this command
|
|
err = ERR_ALTER_NOTSUPP;
|
|
}
|
|
else if (!lstrcmpi((char*)szToken, "GRANT")) {
|
|
//Dr. DeeBee Driver does not cater for this command
|
|
err = ERR_GRANT_NOTSUPP;
|
|
}
|
|
else if (!lstrcmpi((char*)szToken, "REVOKE")) {
|
|
//Dr. DeeBee Driver does not cater for this command
|
|
err = ERR_REVOKE_NOTSUPP;
|
|
}
|
|
else {
|
|
s_lstrcpy(lpstmt->szError, "CREATE, DROP, SELECT, INSERT, UPDATE, or DELETE");
|
|
err = ERR_EXPECTEDOTHER;
|
|
}
|
|
if (err != ERR_SUCCESS) {
|
|
FreeTree(lpSql);
|
|
return err;
|
|
}
|
|
|
|
/* Error if any unused tokens */
|
|
err = GetToken(lpstmt, lpszSqlStr, cbSqlStr, szToken, &fType,
|
|
&lpszSqlStr, &cbSqlStr);
|
|
if (err != ERR_SUCCESS) {
|
|
FreeTree(lpSql);
|
|
return err;
|
|
}
|
|
if (fType != TOKEN_TYPE_NONE) {
|
|
FreeTree(lpSql);
|
|
s_lstrcpy(lpstmt->szError, szToken);
|
|
return ERR_UNEXPECTEDTOKEN;
|
|
}
|
|
|
|
/* Fill in the root node */
|
|
lpSql->node.root.sql = idxRoot;
|
|
|
|
*lplpSql = lpSql;
|
|
return ERR_SUCCESS;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
//Generates string for a comparision expression
|
|
//The input parameters are the left and right values to compare and
|
|
//the comparision operator
|
|
void PredicateParser :: GenerateComparisonString(char* lpLeftString, char* lpRightString, UWORD wOperator, UWORD wSelectModifier, char** lpOutputStr)
|
|
{
|
|
*lpOutputStr = NULL;
|
|
|
|
//Check for valid parameters
|
|
if ( !lpLeftString || !lpRightString)
|
|
return;
|
|
|
|
//Lengths of strings
|
|
ULONG cLeftLen = lstrlen(lpLeftString);
|
|
ULONG cRightLen = lstrlen(lpRightString);
|
|
ULONG cOperatorLen = 0;
|
|
|
|
char* selectop = NULL;
|
|
|
|
//OK get the operator string
|
|
char* lpOperatorStr = NULL;
|
|
switch (wOperator)
|
|
{
|
|
case OP_EQ:
|
|
lpOperatorStr = "=";
|
|
break;
|
|
case OP_NE:
|
|
lpOperatorStr = "<>";
|
|
break;
|
|
case OP_LE:
|
|
lpOperatorStr = "<=";
|
|
break;
|
|
case OP_LT:
|
|
lpOperatorStr = "<";
|
|
break;
|
|
case OP_GE:
|
|
lpOperatorStr = ">=";
|
|
break;
|
|
case OP_GT:
|
|
lpOperatorStr = ">";
|
|
break;
|
|
case OP_LIKE:
|
|
lpOperatorStr = "LIKE";
|
|
break;
|
|
case OP_NOTLIKE:
|
|
lpOperatorStr = "NOT LIKE";
|
|
break;
|
|
case OP_IN:
|
|
lpOperatorStr = "IN";
|
|
break;
|
|
case OP_NOTIN:
|
|
lpOperatorStr = "NOT IN";
|
|
break;
|
|
default:
|
|
//error
|
|
return;
|
|
break;
|
|
}
|
|
|
|
if (lpOperatorStr)
|
|
cOperatorLen = lstrlen (lpOperatorStr);
|
|
|
|
//Double check for sub-select
|
|
if ( (wSelectModifier == SELECT_ALL) || (wSelectModifier == SELECT_ANY) )
|
|
{
|
|
if ( (wOperator != OP_IN) && (wOperator != OP_NOTIN) )
|
|
{
|
|
switch (wSelectModifier)
|
|
{
|
|
case SELECT_ALL:
|
|
selectop = "ALL";
|
|
cOperatorLen += 5;
|
|
break;
|
|
case SELECT_ANY:
|
|
selectop = "ANY";
|
|
cOperatorLen += 5;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//Generate output string in the format
|
|
// ( left operator right )
|
|
(*lpOutputStr) = new char [cLeftLen + cRightLen + cOperatorLen + 5];
|
|
(*lpOutputStr)[0] = 0;
|
|
|
|
if (selectop)
|
|
{
|
|
sprintf (*lpOutputStr, "(%s %s %s %s)", lpLeftString, lpOperatorStr, selectop, lpRightString);
|
|
}
|
|
else
|
|
{
|
|
sprintf (*lpOutputStr, "(%s %s %s)", lpLeftString, lpOperatorStr, lpRightString);
|
|
}
|
|
}
|
|
|
|
//Generates string for a boolean expression
|
|
//The input parameters are the left and right values of the boolean and
|
|
//the boolean operator
|
|
void PredicateParser :: GenerateBooleanString(char* lpLeftString, char* lpRightString, UWORD wOperator, char** lpOutputStr)
|
|
{
|
|
*lpOutputStr = NULL;
|
|
|
|
//Check for valid parameters
|
|
if ( !lpLeftString || !lpRightString)
|
|
return;
|
|
|
|
//Lengths of strings
|
|
ULONG cLeftLen = lstrlen(lpLeftString);
|
|
ULONG cRightLen = lstrlen(lpRightString);
|
|
ULONG cOperatorLen = 0;
|
|
|
|
//OK get the operator string
|
|
char* lpOperatorStr = NULL;
|
|
switch (wOperator)
|
|
{
|
|
case OP_NOT:
|
|
lpOperatorStr = "NOT";
|
|
break;
|
|
case OP_AND:
|
|
lpOperatorStr = "AND";
|
|
break;
|
|
case OP_OR:
|
|
lpOperatorStr = "OR";
|
|
break;
|
|
default:
|
|
//error
|
|
return;
|
|
break;
|
|
}
|
|
|
|
if (lpOperatorStr)
|
|
cOperatorLen = lstrlen (lpOperatorStr);
|
|
|
|
//Generate output string
|
|
if (wOperator != OP_NOT)
|
|
{
|
|
//Format of output string
|
|
// ( left operator right )
|
|
(*lpOutputStr) = new char [cLeftLen + cRightLen + cOperatorLen + 5];
|
|
(*lpOutputStr)[0] = 0;
|
|
sprintf (*lpOutputStr, "(%s %s %s)", lpLeftString, lpOperatorStr, lpRightString);
|
|
}
|
|
else
|
|
{
|
|
//Format of output string
|
|
// ( operator left )
|
|
(*lpOutputStr) = new char [cLeftLen + cOperatorLen + 4];
|
|
(*lpOutputStr)[0] = 0;
|
|
sprintf (*lpOutputStr, "(%s %s)", lpOperatorStr, lpLeftString);
|
|
}
|
|
}
|
|
|
|
//Generates string for an aggregate expression
|
|
//The input parameters are the expression and
|
|
//the operator
|
|
void PredicateParser :: GenerateAggregateString(char* lpLeftString, UWORD wOperator, char** lpOutputStr)
|
|
{
|
|
*lpOutputStr = NULL;
|
|
|
|
//Check for valid parameters
|
|
if ( !lpLeftString)
|
|
return;
|
|
|
|
//Lengths of strings
|
|
ULONG cLeftLen = lstrlen(lpLeftString);
|
|
ULONG cOperatorLen = 0;
|
|
|
|
//OK get the operator string
|
|
char* lpOperatorStr = NULL;
|
|
switch (wOperator)
|
|
{
|
|
case AGG_AVG:
|
|
lpOperatorStr = "AVG";
|
|
break;
|
|
case AGG_COUNT:
|
|
lpOperatorStr = "COUNT";
|
|
break;
|
|
case AGG_MAX:
|
|
lpOperatorStr = "MAX";
|
|
break;
|
|
case AGG_MIN:
|
|
lpOperatorStr = "MIN";
|
|
break;
|
|
case AGG_SUM:
|
|
lpOperatorStr = "SUM";
|
|
break;
|
|
default:
|
|
//error
|
|
return;
|
|
break;
|
|
}
|
|
|
|
if (lpOperatorStr)
|
|
cOperatorLen = lstrlen (lpOperatorStr);
|
|
|
|
//Generate output string in the format
|
|
// operator(left)
|
|
(*lpOutputStr) = new char [cLeftLen + cOperatorLen + 3];
|
|
(*lpOutputStr)[0] = 0;
|
|
sprintf (*lpOutputStr, "%s(%s)", lpOperatorStr, lpLeftString);
|
|
}
|
|
|
|
//Generates string for a algebraic expression
|
|
//The input parameters are the left and right values and
|
|
//the algebraic operator
|
|
void PredicateParser :: GenerateAlgebraicString(char* lpLeftString, char* lpRightString, UWORD wOperator, char** lpOutputStr)
|
|
{
|
|
*lpOutputStr = NULL;
|
|
|
|
//Check for valid parameters
|
|
if ( !lpLeftString)
|
|
return;
|
|
|
|
//Lengths of strings
|
|
ULONG cLeftLen = lstrlen(lpLeftString);
|
|
ULONG cRightLen = lstrlen(lpRightString);
|
|
ULONG cOperatorLen = 0;
|
|
|
|
//OK get the operator string
|
|
char* lpOperatorStr = NULL;
|
|
switch (wOperator)
|
|
{
|
|
case OP_PLUS:
|
|
case OP_NONE:
|
|
lpOperatorStr = "+";
|
|
break;
|
|
case OP_MINUS:
|
|
case OP_NEG:
|
|
lpOperatorStr = "-";
|
|
break;
|
|
case OP_TIMES:
|
|
lpOperatorStr = "*";
|
|
break;
|
|
case OP_DIVIDEDBY:
|
|
lpOperatorStr = "/";
|
|
break;
|
|
default:
|
|
//error
|
|
return;
|
|
break;
|
|
}
|
|
|
|
if (lpOperatorStr)
|
|
cOperatorLen = lstrlen (lpOperatorStr);
|
|
|
|
//Generate output string in the format
|
|
|
|
if (wOperator != OP_NEG)
|
|
{
|
|
if (!cRightLen)
|
|
//error
|
|
return;
|
|
|
|
// (left operator right)
|
|
(*lpOutputStr) = new char [cLeftLen + cRightLen + cOperatorLen + 5];
|
|
(*lpOutputStr)[0] = 0;
|
|
sprintf (*lpOutputStr, "(%s %s %s)", lpLeftString, lpOperatorStr, lpRightString);
|
|
}
|
|
else
|
|
{
|
|
// (operator left)
|
|
(*lpOutputStr) = new char [cLeftLen + cOperatorLen + 4];
|
|
(*lpOutputStr)[0] = 0;
|
|
sprintf (*lpOutputStr, "(%s %s)", lpOperatorStr, lpLeftString);
|
|
}
|
|
}
|
|
|
|
//Generates WHERE predicate string from NODE tree
|
|
void PredicateParser :: GeneratePredicateString(LPSQLNODE lpSqlNode, char ** lpOutputStr, BOOL fIsSQL89)
|
|
{
|
|
*lpOutputStr = NULL;
|
|
|
|
char* lpLeftString = NULL;
|
|
char* lpRightString = NULL;
|
|
|
|
//Now find out what type of node we have reached
|
|
switch (lpSqlNode->sqlNodeType)
|
|
{
|
|
case NODE_TYPE_BOOLEAN:
|
|
{
|
|
//Get the predicate string for the left node
|
|
GeneratePredicateString(ToNode(*lplpSql,lpSqlNode->node.boolean.Left), &lpLeftString, fIsSQL89);
|
|
|
|
//Get the predicate string for the right node, if it exists
|
|
if (lpSqlNode->node.boolean.Right != NO_SQLNODE)
|
|
{
|
|
GeneratePredicateString(ToNode(*lplpSql,lpSqlNode->node.boolean.Right), &lpRightString, fIsSQL89);
|
|
}
|
|
else
|
|
{
|
|
//return a blank dummy string
|
|
char* lpTemp = "<blank>";
|
|
lpRightString = new char [lstrlen(lpTemp) + 1];
|
|
lpRightString[0] = 0;
|
|
lstrcpy(lpRightString, lpTemp);
|
|
}
|
|
//If both the left and the right string are non-NULL generate the
|
|
//boolean string, else just return the string which is non-null
|
|
UWORD wOperator = lpSqlNode->node.boolean.Operator;
|
|
if (lpLeftString && lpRightString)
|
|
{
|
|
//Generate comparison string
|
|
GenerateBooleanString(lpLeftString, lpRightString, wOperator, lpOutputStr);
|
|
|
|
//Tidy up
|
|
delete lpLeftString;
|
|
delete lpRightString;
|
|
}
|
|
else
|
|
{
|
|
if (wOperator != OP_OR)
|
|
{
|
|
if (lpLeftString)
|
|
{
|
|
//Must be non-NULL
|
|
(*lpOutputStr) = lpLeftString;
|
|
}
|
|
else
|
|
{
|
|
//Can be non-NULL or NULL
|
|
(*lpOutputStr) = lpRightString;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
(*lpOutputStr) = NULL;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case NODE_TYPE_SELECT:
|
|
{
|
|
//Sub-SELECT statement
|
|
char* fullWQL = NULL;
|
|
TableColumnInfo subSelect (lplpSql, &lpSqlNode, WQL_MULTI_TABLE);
|
|
subSelect.BuildFullWQL(&fullWQL);
|
|
*lpOutputStr = fullWQL;
|
|
|
|
if (fullWQL)
|
|
{
|
|
int wqlLen = lstrlen(fullWQL);
|
|
char* modStr = new char [2 + wqlLen + 1];
|
|
modStr[0] = 0;
|
|
sprintf (modStr, "(%s)", fullWQL);
|
|
*lpOutputStr = modStr;
|
|
delete fullWQL;
|
|
}
|
|
}
|
|
break;
|
|
case NODE_TYPE_COMPARISON:
|
|
{
|
|
//Get the predicate string for the left node
|
|
GeneratePredicateString(ToNode(*lplpSql,lpSqlNode->node.comparison.Left), &lpLeftString, fIsSQL89);
|
|
|
|
//Get the predicate string for the right node
|
|
GeneratePredicateString(ToNode(*lplpSql,lpSqlNode->node.comparison.Right), &lpRightString, fIsSQL89);
|
|
|
|
//If both the left and the right string are non-NULL generate the
|
|
//comparison string, else return error
|
|
|
|
UWORD wSelectModifier = lpSqlNode->node.comparison.SelectModifier;
|
|
|
|
if (lpLeftString && lpRightString)
|
|
{
|
|
//Generate comparison string
|
|
UWORD wOperator = lpSqlNode->node.comparison.Operator;
|
|
|
|
//For the ALPHA drop we disable HMM Level 1 optimazation for
|
|
//SQL queries which contain "LIKE" or "NOT LIKE" with wildcards
|
|
BOOL fError = FALSE;
|
|
|
|
if (!fSupportLIKEs)
|
|
{
|
|
if ( (wOperator == OP_LIKE) || (wOperator == OP_NOTLIKE) )
|
|
{
|
|
(*lpOutputStr) = NULL;
|
|
fError = TRUE;
|
|
/*
|
|
//Check for wildcards etc ... "%" or "_"
|
|
char* pt = lpRightString;
|
|
long i = 0;
|
|
|
|
while (pt[i] && !fError)
|
|
{
|
|
switch (pt[i])
|
|
{
|
|
case '%':
|
|
{
|
|
//Wilcard detected, abort
|
|
(*lpOutputStr) = NULL;
|
|
fError = TRUE;
|
|
}
|
|
break;
|
|
case '_':
|
|
{
|
|
//Check if it has been escaped
|
|
if (i && (pt[i - 1] == '\\'))
|
|
{
|
|
//OK
|
|
}
|
|
else
|
|
{
|
|
(*lpOutputStr) = NULL;
|
|
fError = TRUE;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
*/
|
|
}
|
|
}
|
|
|
|
if (!fError)
|
|
GenerateComparisonString(lpLeftString, lpRightString, wOperator, wSelectModifier, lpOutputStr);
|
|
}
|
|
else
|
|
{
|
|
|
|
//Check for sub select
|
|
|
|
if ( lpLeftString && (wSelectModifier == SELECT_EXISTS) )
|
|
{
|
|
fError = FALSE;
|
|
int selectLen = lstrlen (lpLeftString);
|
|
(*lpOutputStr) = new char [7 + selectLen + 1];
|
|
(*lpOutputStr)[0] = 0;
|
|
sprintf (*lpOutputStr, "EXISTS %s", lpLeftString);
|
|
}
|
|
else
|
|
{
|
|
//error
|
|
(*lpOutputStr) = NULL;
|
|
}
|
|
}
|
|
|
|
//Tidy up
|
|
delete lpLeftString;
|
|
delete lpRightString;
|
|
|
|
}
|
|
break;
|
|
case NODE_TYPE_SCALAR:
|
|
{
|
|
//WQL does not support scalar functions
|
|
char* myString = ",SomeScalarFunctionThatWQLDoesNotSupport";
|
|
ULONG len = lstrlen(myString);
|
|
(*lpOutputStr) = new char [len + 1];
|
|
(*lpOutputStr)[0] = 0;
|
|
lstrcpy(*lpOutputStr, myString);
|
|
}
|
|
break;
|
|
case NODE_TYPE_AGGREGATE:
|
|
{
|
|
//Get the predicate string for the expression node
|
|
GeneratePredicateString(ToNode(*lplpSql,lpSqlNode->node.aggregate.Expression), &lpLeftString, fIsSQL89);
|
|
|
|
//If the left string is non-NULL generate the
|
|
//aggregate string, else return error
|
|
if (lpLeftString)
|
|
{
|
|
//Generate aggregate string
|
|
UWORD wOperator = lpSqlNode->node.aggregate.Operator;
|
|
GenerateAggregateString(lpLeftString, wOperator, lpOutputStr);
|
|
}
|
|
else
|
|
{
|
|
//error
|
|
(*lpOutputStr) = NULL;
|
|
}
|
|
|
|
//Tidy up
|
|
delete lpLeftString;
|
|
}
|
|
break;
|
|
case NODE_TYPE_ALGEBRAIC:
|
|
{
|
|
//Get the predicate string for the left node
|
|
GeneratePredicateString(ToNode(*lplpSql,lpSqlNode->node.algebraic.Left), &lpLeftString, fIsSQL89);
|
|
|
|
//Get the predicate string for the right node
|
|
if (lpSqlNode->node.algebraic.Right != NO_SQLNODE)
|
|
GeneratePredicateString(ToNode(*lplpSql,lpSqlNode->node.algebraic.Right), &lpRightString, fIsSQL89);
|
|
else
|
|
lpRightString = NULL;
|
|
|
|
//If the left string is non-NULL generate the
|
|
//comparison string, else return error
|
|
if (lpLeftString)
|
|
{
|
|
//Generate algebraic string
|
|
UWORD wOperator = lpSqlNode->node.algebraic.Operator;
|
|
GenerateAlgebraicString(lpLeftString, lpRightString, wOperator, lpOutputStr);
|
|
}
|
|
else
|
|
{
|
|
//error
|
|
(*lpOutputStr) = NULL;
|
|
}
|
|
|
|
//Tidy up
|
|
delete lpLeftString;
|
|
delete lpRightString;
|
|
}
|
|
break;
|
|
case NODE_TYPE_COLUMN:
|
|
{
|
|
//Return a copy of the column name
|
|
char* lpTemp = (LPSTR) ToString(*lplpSql, lpSqlNode->node.column.Column);
|
|
char* lpAlias = NULL;
|
|
SQLNODEIDX idxTblAlias = lpSqlNode->node.column.Tablealias;
|
|
|
|
if (idxTblAlias != NO_STRING)
|
|
{
|
|
lpAlias = (LPSTR) ToString(*lplpSql, idxTblAlias);
|
|
}
|
|
|
|
//Sai new
|
|
SQLNODEIDX idxTbl = lpSqlNode->node.column.Table;
|
|
LPSQLNODE myTableNode = ToNode(*lplpSql,idxTbl);
|
|
LPISAMTABLEDEF lpTDef = NULL;
|
|
|
|
if (idxTbl != NO_SQLNODE)
|
|
lpTDef = myTableNode->node.table.Handle;
|
|
|
|
//Sai Wong Here
|
|
// if (lpISAMTableDef == lpSqlNode->node.table.Handle)
|
|
if ((!lpISAMTableDef) || (lpISAMTableDef == lpTDef))
|
|
{
|
|
ULONG len = lstrlen(lpTemp);
|
|
|
|
if (lpAlias && !fIsSQL89)
|
|
{
|
|
len += lstrlen(lpAlias) + 1;
|
|
}
|
|
|
|
(*lpOutputStr) = new char [len + 1];
|
|
(*lpOutputStr)[0] = 0;
|
|
|
|
if (lpAlias && !fIsSQL89)
|
|
{
|
|
sprintf(*lpOutputStr, "%s.%s",lpAlias, lpTemp);
|
|
}
|
|
else
|
|
{
|
|
lstrcpy(*lpOutputStr, lpTemp);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//no match with desired table so return NULL
|
|
(*lpOutputStr) = NULL;
|
|
}
|
|
|
|
}
|
|
break;
|
|
case NODE_TYPE_STRING:
|
|
{
|
|
//Return a copy of the String in the format shown below
|
|
// 'string'
|
|
char* lpTemp = (LPSTR)ToString(*lplpSql, lpSqlNode->node.string.Value);
|
|
(*lpOutputStr) = new char [lstrlen(lpTemp) + 3];
|
|
(*lpOutputStr)[0] = 0;
|
|
sprintf (*lpOutputStr, "'%s'", lpTemp);
|
|
}
|
|
break;
|
|
case NODE_TYPE_TIMESTAMP:
|
|
{
|
|
//Return a copy of the Timestamp in UTC format shown below
|
|
// 'yyyymmddhhmmss.000000+000'
|
|
TIMESTAMP_STRUCT lpTimestamp = lpSqlNode->node.timestamp.Value;
|
|
(*lpOutputStr) = new char [DATETIME_FORMAT_LEN + 4 + 1 + 2];
|
|
(*lpOutputStr)[0] = 0;
|
|
sprintf (*lpOutputStr, "'%04d%02u%02u%02u%02u%02u.%06d+000'",
|
|
lpTimestamp.year, lpTimestamp.month, lpTimestamp.day,
|
|
lpTimestamp.hour, lpTimestamp.minute, lpTimestamp.second,
|
|
lpTimestamp.fraction);
|
|
}
|
|
break;
|
|
case NODE_TYPE_VALUES:
|
|
{
|
|
|
|
SQLNODEIDX idxValue = lpSqlNode->node.values.Value;
|
|
SQLNODEIDX idxValues = lpSqlNode->node.values.Next;
|
|
LPSQLNODE lpSqlNextNode;
|
|
|
|
ULONG cLenLeft = 0;
|
|
ULONG cLenOutput = 0;
|
|
ULONG cLenBuff = 0;
|
|
|
|
*lpOutputStr = NULL;
|
|
|
|
BOOL firstTime = TRUE;
|
|
BOOL fEndOfList = FALSE;
|
|
char* buff = NULL;
|
|
do
|
|
{
|
|
//Get next value in valuelist
|
|
GeneratePredicateString(ToNode(*lplpSql,idxValue), &lpLeftString, fIsSQL89);
|
|
|
|
//Add to list
|
|
cLenOutput = (*lpOutputStr) ? lstrlen (*lpOutputStr) : 0;
|
|
cLenLeft = lpLeftString ? lstrlen (lpLeftString) : 0;
|
|
cLenBuff = cLenOutput + cLenLeft;
|
|
|
|
//Check for errors
|
|
if (cLenLeft == 0)
|
|
{
|
|
//error
|
|
delete (*lpOutputStr);
|
|
*lpOutputStr = NULL;
|
|
return;
|
|
}
|
|
|
|
buff = new char [cLenBuff + 2];
|
|
buff[0] = 0;
|
|
if (firstTime)
|
|
{
|
|
sprintf (buff, "(%s", lpLeftString);
|
|
}
|
|
else
|
|
{
|
|
sprintf (buff, "%s,%s", *lpOutputStr, lpLeftString);
|
|
}
|
|
|
|
//Update output string and tidy up
|
|
delete (*lpOutputStr);
|
|
delete lpLeftString;
|
|
*lpOutputStr = buff;
|
|
|
|
//Increment for next loop
|
|
if (idxValues != NO_SQLNODE)
|
|
{
|
|
lpSqlNextNode = ToNode(*lplpSql,idxValues);
|
|
idxValue = lpSqlNextNode->node.values.Value;
|
|
idxValues = lpSqlNextNode->node.values.Next;
|
|
}
|
|
else
|
|
{
|
|
fEndOfList = TRUE;
|
|
}
|
|
|
|
firstTime = FALSE;
|
|
}
|
|
while (!fEndOfList);
|
|
|
|
//Now add closing bracket
|
|
cLenOutput = lstrlen (*lpOutputStr);
|
|
buff = new char [cLenOutput + 2];
|
|
buff[0] = 0;
|
|
sprintf(buff, "%s)", *lpOutputStr);
|
|
delete (*lpOutputStr);
|
|
*lpOutputStr = buff;
|
|
}
|
|
break;
|
|
case NODE_TYPE_NUMERIC:
|
|
{
|
|
//Return a copy of the number (as a string)
|
|
char* lpTemp = (LPSTR) ToString(*lplpSql, lpSqlNode->node.numeric.Numeric);
|
|
(*lpOutputStr) = new char [lstrlen(lpTemp) + 1];
|
|
(*lpOutputStr)[0] = 0;
|
|
lstrcpy(*lpOutputStr, lpTemp);
|
|
}
|
|
break;
|
|
case NODE_TYPE_PARAMETER:
|
|
break;
|
|
case NODE_TYPE_USER:
|
|
{
|
|
char* lpTemp = "USER";
|
|
(*lpOutputStr) = new char [lstrlen(lpTemp) + 1];
|
|
(*lpOutputStr)[0] = 0;
|
|
lstrcpy(*lpOutputStr, lpTemp);
|
|
}
|
|
break;
|
|
case NODE_TYPE_NULL:
|
|
{
|
|
char* lpTemp = "NULL";
|
|
(*lpOutputStr) = new char [lstrlen(lpTemp) + 1];
|
|
(*lpOutputStr)[0] = 0;
|
|
lstrcpy(*lpOutputStr, lpTemp);
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
//Error, could not recognize node
|
|
fError = TRUE;
|
|
*lpOutputStr = NULL;
|
|
}
|
|
break;
|
|
}
|
|
|
|
//If an error occured return a NULL output string
|
|
if (fError)
|
|
{
|
|
if (*lpOutputStr)
|
|
delete (*lpOutputStr);
|
|
|
|
(*lpOutputStr) = NULL;
|
|
}
|
|
}
|
|
|
|
//Generates ORDER BY string from NODE tree
|
|
void PredicateParser :: GenerateOrderByString(LPSQLNODE lpSqlNode, char ** lpOutputStr, BOOL fIsSQL89)
|
|
{
|
|
BOOL fContinue = TRUE;
|
|
BOOL fFirst = TRUE;
|
|
LPSQLNODE newNode = NULL;
|
|
while (fContinue)
|
|
{
|
|
char* columnRef = NULL;
|
|
LPSQLNODE myNode = ToNode(*lplpSql,lpSqlNode->node.sortcolumns.Column);
|
|
BOOL fDescending = lpSqlNode->node.sortcolumns.Descending;
|
|
|
|
ULONG len3 = 0;
|
|
char* sortOrder = "";
|
|
|
|
if (fDescending)
|
|
{
|
|
sortOrder = " DESC";
|
|
len3 = lstrlen(sortOrder);
|
|
}
|
|
|
|
GenerateColumnRef(myNode, &columnRef, fIsSQL89);
|
|
|
|
if (columnRef)
|
|
{
|
|
ULONG len1 = s_lstrlen(*lpOutputStr);
|
|
|
|
if (!fFirst)
|
|
{
|
|
//add a comma separator and column ref
|
|
ULONG len2 = s_lstrlen(columnRef) + 1;
|
|
char* buffer = new char [len1 + len2 + len3 + 1];
|
|
buffer[0] = 0;
|
|
|
|
//Add new column to end of list
|
|
if (len3)
|
|
sprintf (buffer, "%s,%s%s", *lpOutputStr, columnRef, sortOrder);
|
|
else
|
|
sprintf (buffer, "%s,%s", *lpOutputStr, columnRef);
|
|
|
|
delete (*lpOutputStr);
|
|
delete columnRef;
|
|
*lpOutputStr = buffer;
|
|
}
|
|
else
|
|
{
|
|
//add column ref
|
|
if (len3)
|
|
{
|
|
ULONG len2 = s_lstrlen(columnRef);
|
|
char* buffer = new char [len2 + len3 + 1];
|
|
buffer[0] = 0;
|
|
sprintf (buffer, "%s%s", columnRef, sortOrder);
|
|
delete columnRef;
|
|
*lpOutputStr = buffer;
|
|
}
|
|
else
|
|
{
|
|
*lpOutputStr = columnRef;
|
|
}
|
|
}
|
|
|
|
fFirst = FALSE;
|
|
}
|
|
|
|
//Check next node
|
|
if (lpSqlNode->node.sortcolumns.Next != NO_SQLNODE)
|
|
{
|
|
newNode = ToNode(*lplpSql,lpSqlNode->node.sortcolumns.Next);
|
|
lpSqlNode = newNode;
|
|
}
|
|
else
|
|
{
|
|
fContinue = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//Generates GROUP BY string from NODE tree
|
|
void PredicateParser :: GenerateGroupByString(LPSQLNODE lpSqlNode, char ** lpOutputStr, BOOL fIsSQL89)
|
|
{
|
|
BOOL fContinue = TRUE;
|
|
BOOL fFirst = TRUE;
|
|
LPSQLNODE newNode = NULL;
|
|
while (fContinue)
|
|
{
|
|
char* columnRef = NULL;
|
|
LPSQLNODE myNode = ToNode(*lplpSql,lpSqlNode->node.groupbycolumns.Column);
|
|
GenerateColumnRef(myNode, &columnRef, fIsSQL89);
|
|
|
|
if (columnRef)
|
|
{
|
|
ULONG len1 = s_lstrlen(*lpOutputStr);
|
|
|
|
if (!fFirst)
|
|
{
|
|
//add a comma separator and column ref
|
|
ULONG len2 = s_lstrlen(columnRef) + 1;
|
|
char* buffer = new char [len1 + len2 + 1];
|
|
buffer[0] = 0;
|
|
|
|
//Add new column to head of list
|
|
sprintf (buffer, "%s,%s", columnRef, *lpOutputStr);
|
|
delete (*lpOutputStr);
|
|
delete columnRef;
|
|
*lpOutputStr = buffer;
|
|
}
|
|
else
|
|
{
|
|
//add column ref
|
|
*lpOutputStr = columnRef;
|
|
}
|
|
|
|
fFirst = FALSE;
|
|
}
|
|
|
|
//Check next node
|
|
if (lpSqlNode->node.groupbycolumns.Next != NO_SQLNODE)
|
|
{
|
|
newNode = ToNode(*lplpSql,lpSqlNode->node.groupbycolumns.Next);
|
|
lpSqlNode = newNode;
|
|
}
|
|
else
|
|
{
|
|
fContinue = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
void PredicateParser :: GenerateColumnRef(LPSQLNODE lpSqlNode, char ** lpOutputStr, BOOL fIsSQL89)
|
|
{
|
|
*lpOutputStr = NULL;
|
|
|
|
//Now find out what type of node we have reached
|
|
switch (lpSqlNode->sqlNodeType)
|
|
{
|
|
case NODE_TYPE_COLUMN:
|
|
{
|
|
//Return a copy of the column name
|
|
char* lpTemp = (LPSTR) ToString(*lplpSql, lpSqlNode->node.column.Column);
|
|
char* lpAlias = NULL;
|
|
SQLNODEIDX idxTblAlias = lpSqlNode->node.column.Tablealias;
|
|
|
|
if (idxTblAlias != NO_STRING)
|
|
{
|
|
lpAlias = (LPSTR) ToString(*lplpSql, idxTblAlias);
|
|
}
|
|
|
|
|
|
ULONG len = lstrlen(lpTemp);
|
|
|
|
if (lpAlias && !fIsSQL89)
|
|
{
|
|
len += lstrlen(lpAlias) + 1;
|
|
}
|
|
|
|
(*lpOutputStr) = new char [len + 1];
|
|
(*lpOutputStr)[0] = 0;
|
|
|
|
if (lpAlias && !fIsSQL89)
|
|
{
|
|
sprintf(*lpOutputStr, "%s.%s",lpAlias, lpTemp);
|
|
}
|
|
else
|
|
{
|
|
lstrcpy(*lpOutputStr, lpTemp);
|
|
}
|
|
}
|
|
break;
|
|
case NODE_TYPE_NUMERIC:
|
|
{
|
|
SQLNODEIDX numIdx = lpSqlNode->node.numeric.Numeric;
|
|
|
|
char* lpTemp = NULL;
|
|
|
|
if (numIdx != NO_STRING)
|
|
{
|
|
lpTemp = (LPSTR) ToString(*lplpSql, numIdx);
|
|
|
|
ULONG len = lstrlen(lpTemp);
|
|
(*lpOutputStr) = new char [len + 1];
|
|
(*lpOutputStr)[0] = 0;
|
|
lstrcpy(*lpOutputStr, lpTemp);
|
|
}
|
|
};
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
//constructor for class to store column/table info for a SQL statement
|
|
TableColumnInfo :: TableColumnInfo(LPSQLTREE FAR *lplpTheSql, LPSQLTREE FAR * lpSqlN, ULONG mode)
|
|
{
|
|
//Initialize
|
|
lplpSql = lplpTheSql;
|
|
lplpSqlNode = lpSqlN;
|
|
lpList = NULL;
|
|
fIsValid = TRUE;
|
|
theMode = mode;
|
|
idxTableQualifer = NO_STRING;
|
|
fHasScalarFunction = FALSE;
|
|
m_aggregateExists = FALSE;
|
|
fDistinct = FALSE;
|
|
|
|
//Create the table/column list from SQL statement
|
|
|
|
//Check if it is SELECT * FROM ....
|
|
fIsSelectStar = ( (*lplpSqlNode)->node.select.Values == NO_SQLNODE ) ? TRUE : FALSE;
|
|
|
|
//Can't support SQL passthrough for SELECT * as the parse tree
|
|
//does not contain any column names
|
|
//[Please use SELECT <list of columns>]
|
|
if ( (*lplpSqlNode)->node.select.Values == NO_SQLNODE)
|
|
{
|
|
fIsValid = FALSE;
|
|
return;
|
|
}
|
|
|
|
//Look in the following sub-trees
|
|
|
|
//(1) select-list
|
|
LPSQLNODE lpSearchNode;
|
|
if (!fIsSelectStar)
|
|
{
|
|
lpSearchNode = ToNode(*lplpSql, (*lplpSqlNode)->node.select.Values);
|
|
Analize(lpSearchNode);
|
|
}
|
|
|
|
//(1b) SQL-92 ON predicate
|
|
{
|
|
SQLNODEIDX idxTheTables = (*lplpSqlNode)->node.select.Tables;
|
|
LPSQLNODE tableNode = ToNode(*lplpSql,idxTheTables);
|
|
|
|
BOOL fContinue = TRUE;
|
|
while (fContinue)
|
|
{
|
|
SQLNODEIDX tableIdx = tableNode->node.tables.Table;
|
|
LPSQLNODE singleTable = ToNode(*lplpSql,tableIdx);
|
|
|
|
SQLNODEIDX predicateidx = singleTable->node.table.OuterJoinPredicate;
|
|
|
|
if (predicateidx != NO_SQLNODE)
|
|
{
|
|
LPSQLNODE predicateNode = ToNode(*lplpSql,predicateidx);
|
|
Analize(predicateNode);
|
|
}
|
|
|
|
if (tableNode->node.tables.Next == NO_STRING)
|
|
{
|
|
fContinue = FALSE;
|
|
}
|
|
else
|
|
{
|
|
tableNode = ToNode(*lplpSql,tableNode->node.tables.Next);
|
|
}
|
|
}
|
|
}
|
|
|
|
//Check if DISTINCT
|
|
fDistinct = (*lplpSqlNode)->node.select.Distinct;
|
|
|
|
//For multi table passthrough, return now
|
|
if (mode == WQL_MULTI_TABLE)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//(2) WHERE
|
|
if ( (*lplpSqlNode)->node.select.Predicate != NO_SQLNODE)
|
|
{
|
|
lpSearchNode = ToNode(*lplpSql, (*lplpSqlNode)->node.select.Predicate);
|
|
Analize(lpSearchNode);
|
|
}
|
|
|
|
//(3) GROUP BY
|
|
BOOL fGroupbyDefined = FALSE;
|
|
if ( (*lplpSqlNode)->node.select.Groupbycolumns != NO_SQLNODE)
|
|
{
|
|
lpSearchNode = ToNode(*lplpSql, (*lplpSqlNode)->node.select.Groupbycolumns);
|
|
Analize(lpSearchNode);
|
|
|
|
//as our implementation of GROUP BY requires that all the columns
|
|
//be specified they is no need to imvestigate the ORDER BY node.
|
|
//Also if no SORT BY columns are defined the node is set to an undefined value anyway
|
|
fGroupbyDefined = TRUE;
|
|
}
|
|
|
|
//(4) HAVING
|
|
if ( (*lplpSqlNode)->node.select.Having != NO_SQLNODE)
|
|
{
|
|
lpSearchNode = ToNode(*lplpSql, (*lplpSqlNode)->node.select.Having);
|
|
Analize(lpSearchNode);
|
|
}
|
|
|
|
//(5) ORDER BY
|
|
if ( (!fGroupbyDefined) && (*lplpSqlNode)->node.select.Sortcolumns != NO_SQLNODE)
|
|
{
|
|
lpSearchNode = ToNode(*lplpSql, (*lplpSqlNode)->node.select.Sortcolumns);
|
|
Analize(lpSearchNode);
|
|
}
|
|
}
|
|
|
|
//Analizes parse tree and generates TableColumn list
|
|
void TableColumnInfo :: Analize(LPSQLNODE lpSqlNode)
|
|
{
|
|
//Now find out what type of node we have reached
|
|
switch (lpSqlNode->sqlNodeType)
|
|
{
|
|
case NODE_TYPE_BOOLEAN:
|
|
{
|
|
//Analize the left node
|
|
Analize(ToNode(*lplpSql,lpSqlNode->node.boolean.Left));
|
|
|
|
//Analize the right node, if it exists
|
|
if (lpSqlNode->node.boolean.Right != NO_SQLNODE)
|
|
{
|
|
Analize(ToNode(*lplpSql,lpSqlNode->node.boolean.Right));
|
|
}
|
|
}
|
|
break;
|
|
case NODE_TYPE_COMPARISON:
|
|
{
|
|
//Analize the left node
|
|
Analize(ToNode(*lplpSql,lpSqlNode->node.comparison.Left));
|
|
|
|
//Analize the right node
|
|
Analize(ToNode(*lplpSql,lpSqlNode->node.comparison.Right));
|
|
}
|
|
break;
|
|
case NODE_TYPE_VALUES:
|
|
{
|
|
//Analize the value node
|
|
Analize(ToNode(*lplpSql,lpSqlNode->node.values.Value));
|
|
|
|
//Analize the next node
|
|
if (lpSqlNode->node.values.Next != NO_SQLNODE)
|
|
{
|
|
Analize(ToNode(*lplpSql,lpSqlNode->node.values.Next));
|
|
}
|
|
}
|
|
break;
|
|
case NODE_TYPE_AGGREGATE:
|
|
{
|
|
//Analize the left node
|
|
Analize(ToNode(*lplpSql,lpSqlNode->node.aggregate.Expression));
|
|
m_aggregateExists = TRUE;
|
|
}
|
|
break;
|
|
case NODE_TYPE_ALGEBRAIC:
|
|
{
|
|
//Analize the left node
|
|
Analize(ToNode(*lplpSql,lpSqlNode->node.algebraic.Left));
|
|
|
|
//Analize the right node
|
|
if (lpSqlNode->node.algebraic.Right != NO_SQLNODE)
|
|
Analize(ToNode(*lplpSql,lpSqlNode->node.algebraic.Right));
|
|
}
|
|
break;
|
|
case NODE_TYPE_SCALAR:
|
|
{
|
|
//WQL cannot support scalar functions so we must
|
|
//flag this special case
|
|
fHasScalarFunction = TRUE;
|
|
|
|
//Analize the value list
|
|
Analize(ToNode(*lplpSql,lpSqlNode->node.scalar.Arguments));
|
|
}
|
|
break;
|
|
|
|
case NODE_TYPE_SORTCOLUMNS:
|
|
{
|
|
LPSQLNODE myNode = lpSqlNode;
|
|
|
|
SQLNODEIDX orderbyidx = myNode->node.sortcolumns.Column;
|
|
|
|
while (orderbyidx != NO_SQLNODE)
|
|
{
|
|
//Analize the first column
|
|
Analize(ToNode(*lplpSql,orderbyidx));
|
|
|
|
SQLNODEIDX next_idx = myNode->node.sortcolumns.Next;
|
|
|
|
if (next_idx != NO_SQLNODE)
|
|
{
|
|
//update to point to next sortcolumn node
|
|
myNode = ToNode(*lplpSql, next_idx);
|
|
|
|
orderbyidx = myNode->node.sortcolumns.Column;
|
|
}
|
|
else
|
|
{
|
|
orderbyidx = NO_SQLNODE;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case NODE_TYPE_COLUMN:
|
|
{
|
|
//Generate a node for the Column/Table list
|
|
|
|
SQLNODEIDX idxCol = lpSqlNode->node.column.Column;
|
|
SQLNODEIDX idxTblAlias = lpSqlNode->node.column.Tablealias;
|
|
SQLNODEIDX idxTbl = lpSqlNode->node.column.Table;
|
|
LPSQLNODE myTableNode = ToNode(*lplpSql,idxTbl);
|
|
|
|
//SAI
|
|
LPISAMTABLEDEF lpTDef = NULL;
|
|
|
|
if (idxTbl != NO_SQLNODE)
|
|
lpTDef = myTableNode->node.table.Handle;
|
|
|
|
//First check if it already exists in list
|
|
BOOL found = FALSE;
|
|
TableColumnList* ptr = lpList;
|
|
while (!found && ptr)
|
|
{
|
|
//Get column names
|
|
char* lpTemp1 = (LPSTR) ToString(*lplpSql, idxCol);
|
|
char* lpTemp2 = (LPSTR) ToString(*lplpSql, ptr->idxColumnName);
|
|
|
|
if ( (strcmp(lpTemp1, lpTemp2) == 0) && (lpTDef == ptr->lpISAMTableDef) )
|
|
{
|
|
//This could potentially be a match
|
|
if (lpTDef)
|
|
{
|
|
found = TRUE;
|
|
}
|
|
else
|
|
{
|
|
//Multi-table passthrough SQL checks
|
|
//Check Table aliases
|
|
if ( (idxTblAlias != NO_STRING) && (ptr->idxTableAlias != NO_STRING))
|
|
{
|
|
char* lpAlias1 = (LPSTR) ToString(*lplpSql, idxTblAlias);
|
|
char* lpAlias2 = (LPSTR) ToString(*lplpSql, ptr->idxTableAlias);
|
|
|
|
if (_stricmp(lpAlias1, lpAlias2) == 0)
|
|
{
|
|
found = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ptr = ptr->Next;
|
|
}
|
|
|
|
if (found)
|
|
return;
|
|
|
|
//Not in existing list, so add
|
|
TableColumnList* newNode = new TableColumnList(idxCol, idxTblAlias, lpTDef);
|
|
|
|
//Append new node to HEAD of existing list, if it exists
|
|
if (lpList)
|
|
{
|
|
//List already exists
|
|
newNode->Next = lpList;
|
|
lpList = newNode;
|
|
}
|
|
else
|
|
{
|
|
//List does not exist, so new node becomes list
|
|
lpList = newNode;
|
|
}
|
|
|
|
}
|
|
break;
|
|
case NODE_TYPE_STRING:
|
|
case NODE_TYPE_NUMERIC:
|
|
case NODE_TYPE_PARAMETER:
|
|
case NODE_TYPE_USER:
|
|
case NODE_TYPE_NULL:
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void TableColumnInfo :: BuildFullWQL(char** lpWQLStr, CMapWordToPtr** ppPassthroughMap)
|
|
{
|
|
//SELECT
|
|
StringConcat(lpWQLStr, "SELECT ");
|
|
|
|
//Get the table list first to find out if we are
|
|
//SQL-89 or SQL-92
|
|
char* tableList = NULL;
|
|
BOOL fIsSQL89 = TRUE;
|
|
BuildTableList(&tableList, fIsSQL89);
|
|
|
|
//Looking for distinct entries ?
|
|
if (IsDistinct())
|
|
{
|
|
StringConcat(lpWQLStr, " DISTINCT ");
|
|
}
|
|
|
|
/*
|
|
//Don't do this as we cannot support SELECT * for SQL passthrough
|
|
//[as the parse tree does not know the column names]
|
|
// and we will have problems with the virtual table mapping if
|
|
//the table has array data types
|
|
|
|
//this will cause the SQL passthrough query to fail
|
|
//and we will go the non-passthrough route ...
|
|
|
|
//Check if this is SELECT * FROM ...
|
|
if (IsSelectStar())
|
|
{
|
|
StringConcat(lpWQLStr, " * ");
|
|
}
|
|
*/
|
|
|
|
//Get the select list
|
|
char* selectList = NULL;
|
|
BuildSelectList(NULL, &selectList, fIsSQL89, ppPassthroughMap);
|
|
StringConcat(lpWQLStr, selectList);
|
|
delete selectList;
|
|
|
|
//Check for any scalar functions
|
|
if ( HasScalarFunction() )
|
|
{
|
|
StringConcat(lpWQLStr, ",SomeScalarFunctionThatWQLDoesNotSupport");
|
|
}
|
|
|
|
//FROM
|
|
StringConcat(lpWQLStr, " FROM ");
|
|
|
|
//Get table list (with LEFT OUTER JOIN and ON predicate)
|
|
// char* tableList = NULL;
|
|
// BOOL fIsSQL89 = TRUE;
|
|
// BuildTableList(&tableList, fIsSQL89);
|
|
StringConcat(lpWQLStr, tableList);
|
|
delete tableList;
|
|
|
|
//Get WHERE predicate
|
|
SQLNODEIDX whereidx = (*lplpSqlNode)->node.select.Predicate;
|
|
PredicateParser theParser (lplpSql, NULL);
|
|
if (whereidx != NO_SQLNODE)
|
|
{
|
|
LPSQLNODE lpPredicateNode = ToNode(*lplpSql, whereidx);
|
|
|
|
char* wherePredicate = NULL;
|
|
theParser.GeneratePredicateString(lpPredicateNode, &wherePredicate, fIsSQL89);
|
|
if (wherePredicate)
|
|
{
|
|
StringConcat(lpWQLStr, " WHERE ");
|
|
StringConcat(lpWQLStr, wherePredicate);
|
|
delete wherePredicate;
|
|
}
|
|
}
|
|
|
|
//Get GROUP BY
|
|
SQLNODEIDX groupbyidx = (*lplpSqlNode)->node.select.Groupbycolumns;
|
|
|
|
if (groupbyidx != NO_SQLNODE)
|
|
{
|
|
LPSQLNODE groupbyNode = ToNode(*lplpSql, groupbyidx);
|
|
char* groupByStr = NULL;
|
|
theParser.GenerateGroupByString(groupbyNode, &groupByStr, fIsSQL89);
|
|
if (groupByStr)
|
|
{
|
|
StringConcat(lpWQLStr, " GROUP BY ");
|
|
StringConcat(lpWQLStr, groupByStr);
|
|
delete groupByStr;
|
|
}
|
|
}
|
|
|
|
//Get HAVING
|
|
SQLNODEIDX havingidx = (*lplpSqlNode)->node.select.Having;
|
|
|
|
if (havingidx != NO_SQLNODE)
|
|
{
|
|
LPSQLNODE lpPredicateNode = ToNode(*lplpSql, havingidx);
|
|
|
|
char* HavingPredicate = NULL;
|
|
theParser.GeneratePredicateString(lpPredicateNode, &HavingPredicate, fIsSQL89);
|
|
if (HavingPredicate)
|
|
{
|
|
StringConcat(lpWQLStr, " HAVING ");
|
|
StringConcat(lpWQLStr, HavingPredicate);
|
|
delete HavingPredicate;
|
|
}
|
|
}
|
|
|
|
//Get ORDER BY
|
|
SQLNODEIDX orderbyidx = (*lplpSqlNode)->node.select.Sortcolumns;
|
|
|
|
if (orderbyidx != NO_SQLNODE)
|
|
{
|
|
LPSQLNODE orderbyNode = ToNode(*lplpSql, orderbyidx);
|
|
char* orderByStr = NULL;
|
|
theParser.GenerateOrderByString(orderbyNode, &orderByStr, fIsSQL89);
|
|
if (orderByStr)
|
|
{
|
|
StringConcat(lpWQLStr, " ORDER BY ");
|
|
StringConcat(lpWQLStr, orderByStr);
|
|
delete orderByStr;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void TableColumnInfo :: StringConcat(char** resultString, char* myStr)
|
|
{
|
|
if (myStr)
|
|
{
|
|
ULONG len = s_lstrlen(myStr);
|
|
|
|
ULONG oldLen = 0;
|
|
|
|
if (resultString && *resultString)
|
|
oldLen = s_lstrlen(*resultString);
|
|
|
|
char* buffer = new char [oldLen + len + 1];
|
|
buffer[0] = 0;
|
|
|
|
if (oldLen)
|
|
{
|
|
sprintf (buffer, "%s%s", *resultString, myStr);
|
|
}
|
|
else
|
|
{
|
|
s_lstrcpy(buffer, myStr);
|
|
}
|
|
|
|
//delete old string
|
|
if (resultString && *resultString)
|
|
delete (*resultString);
|
|
|
|
//replace with new string
|
|
*resultString = buffer;
|
|
}
|
|
}
|
|
|
|
|
|
//Info related to nodes of LEFT OUTER JOIN statement
|
|
class JoinInfo
|
|
{
|
|
public:
|
|
|
|
_bstr_t tableName;
|
|
_bstr_t tableAlias;
|
|
|
|
_bstr_t predicate;
|
|
|
|
JoinInfo() {;}
|
|
~JoinInfo() {;}
|
|
|
|
};
|
|
|
|
|
|
void TableColumnInfo :: BuildTableList(char** tableList, BOOL &fIsSQL89, SQLNODEIDX idx)
|
|
{
|
|
//We will build 2 different table lists in the
|
|
//SQL-89 and SQL-92 formats.
|
|
//We will discard the SQL-89 format if we find either
|
|
//(i) SQL-92 JOINS
|
|
//(ii) Multiple tables
|
|
//(iii) Sub-SELECTs
|
|
|
|
//tableList will start with the SQL-89 string
|
|
|
|
BOOL fFirst = TRUE;
|
|
BOOL noJoinTables = TRUE;
|
|
fIsSQL89 = TRUE;
|
|
SQLNODEIDX idxTables = 1;
|
|
|
|
//Constrcut maps for Table/Alias and predicates
|
|
CMapWordToPtr* tableAliasMap = new CMapWordToPtr(); //1 based map
|
|
WORD tableAliasCount = 0;
|
|
|
|
CMapWordToPtr* predicateMap = new CMapWordToPtr(); //1 based map
|
|
WORD predicateCount = 0;
|
|
|
|
char* lpSQL92 = NULL;
|
|
|
|
LPSQLNODE tableNode;
|
|
|
|
if (idx == NO_SQLNODE)
|
|
{
|
|
SQLNODEIDX idxTheTables = (*lplpSqlNode)->node.select.Tables;
|
|
tableNode = ToNode(*lplpSql,idxTheTables);
|
|
}
|
|
else
|
|
{
|
|
tableNode = ToNode(*lplpSql,idx);
|
|
}
|
|
|
|
|
|
BOOL fContinue = TRUE;
|
|
while (fContinue)
|
|
{
|
|
SQLNODEIDX tableIdx = tableNode->node.tables.Table;
|
|
LPSQLNODE singleTable = ToNode(*lplpSql,tableIdx);
|
|
|
|
char* aliasStr = NULL;
|
|
|
|
if (singleTable->node.table.Alias != NO_STRING)
|
|
{
|
|
aliasStr = (LPSTR) ToString(*lplpSql, singleTable->node.table.Alias); //e.g. t1
|
|
}
|
|
char* tableStr = NULL;
|
|
|
|
if (singleTable->node.table.Name != NO_STRING)
|
|
{
|
|
tableStr = (LPSTR) ToString(*lplpSql, singleTable->node.table.Name); //e.g. Table1
|
|
}
|
|
|
|
//This is when we add multi-namespace queries
|
|
if (idxTableQualifer == NO_STRING)
|
|
idxTableQualifer = singleTable->node.table.Qualifier;
|
|
|
|
//Check for outer join
|
|
SQLNODEIDX joinidx = singleTable->node.table.OuterJoinFromTables; //NODE_TYPE_TABLES
|
|
|
|
if (joinidx != NO_SQLNODE)
|
|
{
|
|
// StringConcat(&lpSQL92, " LEFT OUTER JOIN ");
|
|
noJoinTables = FALSE;
|
|
fIsSQL89 = FALSE;
|
|
}
|
|
|
|
|
|
//Add <table> <alias>
|
|
if (idx == NO_SQLNODE)
|
|
{
|
|
|
|
//Add info to tableAliasMap
|
|
JoinInfo* myTableAlias = new JoinInfo();
|
|
|
|
|
|
if (tableStr)
|
|
{
|
|
// if (fIsSQL89)
|
|
// StringConcat(tableList, tableStr);
|
|
|
|
// StringConcat(&lpSQL92, tableStr);
|
|
|
|
myTableAlias->tableName = tableStr;
|
|
}
|
|
|
|
if (aliasStr)
|
|
{
|
|
// StringConcat(&lpSQL92, " ");
|
|
// StringConcat(&lpSQL92, aliasStr);
|
|
// StringConcat(&lpSQL92, " ");
|
|
|
|
myTableAlias->tableAlias = aliasStr;
|
|
}
|
|
|
|
//Add to map
|
|
tableAliasMap->SetAt(++tableAliasCount, (void*)myTableAlias);
|
|
}
|
|
|
|
|
|
SQLNODEIDX predicateidx = singleTable->node.table.OuterJoinPredicate;
|
|
|
|
if (predicateidx != NO_SQLNODE)
|
|
{
|
|
fIsSQL89 = FALSE;
|
|
char* myPredicate = NULL;
|
|
AddJoinPredicate(&myPredicate, predicateidx);
|
|
// AddJoinPredicate(&lpSQL92, predicateidx);
|
|
|
|
|
|
JoinInfo* myPredicateInfo = new JoinInfo();
|
|
myPredicateInfo->predicate = myPredicate;
|
|
delete myPredicate;
|
|
|
|
//Add to map
|
|
predicateMap->SetAt(++predicateCount, (void*)myPredicateInfo);
|
|
}
|
|
|
|
//Check if there is another table in the table list
|
|
if (tableNode->node.tables.Next == NO_STRING)
|
|
{
|
|
fContinue = FALSE;
|
|
}
|
|
else
|
|
{
|
|
tableNode = ToNode(*lplpSql,tableNode->node.tables.Next);
|
|
|
|
//New
|
|
fIsSQL89 = FALSE;
|
|
/*
|
|
//Add a comma to separate table list
|
|
if ((!fFirst) && (idx == NO_SQLNODE) && (noJoinTables))
|
|
{
|
|
// StringConcat(&lpSQL92, ",");
|
|
fIsSQL89 = FALSE;
|
|
}
|
|
*/
|
|
}
|
|
|
|
fFirst = FALSE;
|
|
}
|
|
/*
|
|
//Are we going to use SQL-89 or SQL-92 ?
|
|
if (!fIsSQL89)
|
|
{
|
|
delete *tableList;
|
|
*tableList = lpSQL92;
|
|
}
|
|
else
|
|
{
|
|
delete lpSQL92;
|
|
}
|
|
*/
|
|
|
|
//Now build the table list
|
|
_bstr_t myTableList;
|
|
_bstr_t myFullTableDecl;
|
|
BOOL fFirstOuterJoin = TRUE;
|
|
|
|
//Note: Some providers will return an error if you supply
|
|
//a table alias on a single table WQL query.
|
|
//we should check for this and remove the table alias from
|
|
//the table list
|
|
BOOL fSingleTableDetected = TRUE;
|
|
if ( tableAliasCount > 1 )
|
|
{
|
|
fSingleTableDetected = FALSE;
|
|
}
|
|
|
|
|
|
//Add a table
|
|
JoinInfo* myJoinData = NULL;
|
|
tableAliasMap->Lookup(tableAliasCount--, (void*&)myJoinData);
|
|
|
|
myTableList = myJoinData->tableName;
|
|
|
|
if ( myJoinData->tableAlias.length() && !fSingleTableDetected )
|
|
{
|
|
myTableList += " " + myJoinData->tableAlias;
|
|
}
|
|
|
|
//Are there any predicates
|
|
if (predicateCount)
|
|
{
|
|
while (predicateCount)
|
|
{
|
|
if (fFirstOuterJoin)
|
|
{
|
|
//LEFT OUTER JOIN (add to right side)
|
|
myFullTableDecl = " LEFT OUTER JOIN ";
|
|
myFullTableDecl += myTableList;
|
|
myTableList = myFullTableDecl;
|
|
}
|
|
else
|
|
{
|
|
//RIGHT OUTER JOIN (add to left side)
|
|
myTableList += " RIGHT OUTER JOIN ";
|
|
}
|
|
|
|
//Add next table
|
|
myJoinData = NULL;
|
|
tableAliasMap->Lookup(tableAliasCount--, (void*&)myJoinData);
|
|
|
|
myFullTableDecl = myJoinData->tableName;
|
|
// myTableList += myJoinData->tableName;
|
|
|
|
if ( myJoinData->tableAlias.length() )
|
|
{
|
|
myFullTableDecl += " " + myJoinData->tableAlias;
|
|
}
|
|
|
|
if (fFirstOuterJoin)
|
|
{
|
|
//Add table to left side
|
|
myFullTableDecl += myTableList;
|
|
myTableList = myFullTableDecl;
|
|
}
|
|
else
|
|
{
|
|
//Add table to right side
|
|
myTableList += " " + myFullTableDecl;
|
|
}
|
|
|
|
fFirstOuterJoin = FALSE;
|
|
|
|
//Add predicate to end
|
|
myJoinData = NULL;
|
|
predicateMap->Lookup(predicateCount--, (void*&)myJoinData);
|
|
|
|
if ( myJoinData->predicate.length() )
|
|
{
|
|
myTableList += " " + myJoinData->predicate;
|
|
}
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
//Simple Table list
|
|
while (tableAliasCount)
|
|
{
|
|
//Add next table
|
|
myJoinData = NULL;
|
|
tableAliasMap->Lookup(tableAliasCount--, (void*&)myJoinData);
|
|
|
|
myTableList += ", " + myJoinData->tableName;
|
|
|
|
if ( myJoinData->tableAlias.length() )
|
|
{
|
|
myTableList += " " + myJoinData->tableAlias;
|
|
}
|
|
}
|
|
}
|
|
|
|
//Convert myTableList to string
|
|
long length = myTableList.length();
|
|
*tableList = new char [length + 1];
|
|
(*tableList)[0] = 0;
|
|
wcstombs(*tableList, (wchar_t*)myTableList, length);
|
|
(*tableList)[length] = 0;
|
|
|
|
//Tidy up
|
|
if (tableAliasMap && !(tableAliasMap->IsEmpty()))
|
|
{
|
|
for(POSITION pos = tableAliasMap->GetStartPosition(); pos != NULL; )
|
|
{
|
|
WORD key = 0; //not used
|
|
JoinInfo* pa = NULL;
|
|
tableAliasMap->GetNextAssoc( pos, key, (void*&)pa );
|
|
|
|
if (pa)
|
|
delete pa;
|
|
}
|
|
}
|
|
delete (tableAliasMap);
|
|
|
|
|
|
if (predicateMap && !(predicateMap->IsEmpty()))
|
|
{
|
|
for(POSITION pos = predicateMap->GetStartPosition(); pos != NULL; )
|
|
{
|
|
WORD key = 0; //not used
|
|
JoinInfo* pa = NULL;
|
|
predicateMap->GetNextAssoc( pos, key, (void*&)pa );
|
|
|
|
if (pa)
|
|
delete pa;
|
|
}
|
|
}
|
|
delete (predicateMap);
|
|
}
|
|
|
|
/*
|
|
void TableColumnInfo :: AddJoin(_bstr_t & tableList, SQLNODEIDX joinidx)
|
|
{
|
|
BOOL fContinue = TRUE;
|
|
LPSQLNODE tableNode = tableNode = ToNode(*lplpSql,joinidx);
|
|
|
|
while (fContinue)
|
|
{
|
|
SQLNODEIDX tableIdx = tableNode->node.tables.Table;
|
|
LPSQLNODE singleTable = ToNode(*lplpSql,tableIdx);
|
|
|
|
char* aliasStr = NULL;
|
|
|
|
if (singleTable->node.table.Alias != NO_STRING)
|
|
{
|
|
aliasStr = (LPSTR) ToString(*lplpSql, singleTable->node.table.Alias); //e.g. t1
|
|
}
|
|
char* tableStr = NULL;
|
|
|
|
if (singleTable->node.table.Name != NO_STRING)
|
|
{
|
|
tableStr = (LPSTR) ToString(*lplpSql, singleTable->node.table.Name); //e.g. Table1
|
|
}
|
|
|
|
//Add LEFT OUTER JOIN <table> <alias>
|
|
if (tableStr)
|
|
{
|
|
tableList += " LEFT OUTER JOIN ";
|
|
tableList += tableStr;
|
|
}
|
|
|
|
if (aliasStr)
|
|
{
|
|
tableList += " ";
|
|
tableList += aliasStr;
|
|
tableList += " ";
|
|
}
|
|
|
|
if (tableNode->node.tables.Next == NO_STRING)
|
|
{
|
|
fContinue = FALSE;
|
|
}
|
|
else
|
|
{
|
|
tableNode = ToNode(*lplpSql,tableNode->node.tables.Next);
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
|
|
void TableColumnInfo :: AddJoinPredicate(char** tableList, SQLNODEIDX predicateidx)
|
|
{
|
|
LPSQLNODE lpPredicateNode = ToNode(*lplpSql, predicateidx);
|
|
PredicateParser theParser (lplpSql, NULL);
|
|
|
|
char* buffer = NULL;
|
|
theParser.GeneratePredicateString(lpPredicateNode, &buffer, FALSE);
|
|
|
|
//Did we find a predicate
|
|
if (buffer)
|
|
{
|
|
StringConcat(tableList, " ON ");
|
|
StringConcat(tableList, buffer);
|
|
}
|
|
delete buffer;
|
|
}
|
|
|
|
void TableColumnInfo :: BuildSelectList(LPISAMTABLEDEF lpTableDef, char** lpSelectListStr, BOOL fIsSQL89, CMapWordToPtr** ppPassthroughMap)
|
|
{
|
|
*lpSelectListStr = NULL;
|
|
//Scan through list checking for nodes which match same LPISAMTABLEDEF
|
|
TableColumnList* ptr =lpList;
|
|
|
|
//For testing
|
|
CMapWordToPtr* passthroughMap = NULL;
|
|
|
|
if (ppPassthroughMap)
|
|
passthroughMap = new CMapWordToPtr();
|
|
|
|
WORD passthroughkey = 0; //zero based index
|
|
|
|
while (ptr)
|
|
{
|
|
if ( (theMode == WQL_MULTI_TABLE) || (lpTableDef == ptr->lpISAMTableDef) )
|
|
{
|
|
//Found a match, add to output select list
|
|
char* lpTemp = (LPSTR) ToString(*lplpSql, ptr->idxColumnName);
|
|
ULONG cColumnLen = lstrlen (lpTemp);
|
|
ULONG cOutputLen = (*lpSelectListStr) ? lstrlen(*lpSelectListStr) : 0;
|
|
|
|
//Get the qualifier (for WQL_MULTI_TABLE)
|
|
char* lpTemp2 = NULL;
|
|
ULONG cColumnLen2 = 0;
|
|
|
|
if ( (ptr->idxTableAlias != NO_STRING) && !fIsSQL89)
|
|
{
|
|
lpTemp2 = (LPSTR) ToString(*lplpSql, ptr->idxTableAlias);
|
|
cColumnLen2 = lstrlen (lpTemp2) + 1; //extra 1 for the dot
|
|
}
|
|
|
|
|
|
if (passthroughMap)
|
|
{
|
|
//Setup Passthrough Element
|
|
PassthroughLookupTable* mapElement = new PassthroughLookupTable();
|
|
mapElement->SetTableAlias(lpTemp2);
|
|
mapElement->SetColumnName(lpTemp);
|
|
|
|
//Add to Passthrough Map
|
|
passthroughMap->SetAt(passthroughkey++, (void*)mapElement);
|
|
}
|
|
|
|
if (*lpSelectListStr)
|
|
{
|
|
char* newStr = new char [cColumnLen2 + cColumnLen + cOutputLen + 2];
|
|
newStr[0] = 0;
|
|
|
|
if ( (theMode == WQL_MULTI_TABLE) && (cColumnLen2 > 0) )
|
|
{
|
|
//Add new entries to head of list
|
|
sprintf (newStr, "%s.%s,%s", lpTemp2, lpTemp, *lpSelectListStr);
|
|
}
|
|
else
|
|
{
|
|
sprintf (newStr, "%s,%s", *lpSelectListStr, lpTemp);
|
|
}
|
|
delete *lpSelectListStr;
|
|
*lpSelectListStr = newStr;
|
|
}
|
|
else
|
|
{
|
|
*lpSelectListStr = new char [cColumnLen2 + cColumnLen + 1];
|
|
(*lpSelectListStr)[0] = 0;
|
|
|
|
if ( (theMode == WQL_MULTI_TABLE) && (cColumnLen2 > 0) )
|
|
{
|
|
sprintf (*lpSelectListStr, "%s.%s", lpTemp2, lpTemp);
|
|
}
|
|
else
|
|
{
|
|
lstrcpy (*lpSelectListStr, lpTemp);
|
|
}
|
|
}
|
|
}
|
|
|
|
ptr = ptr->Next;
|
|
}
|
|
|
|
if (ppPassthroughMap)
|
|
{
|
|
//The order in the passthrough map is WRONG
|
|
//columns were added in reverse order
|
|
//we need to re-read the columns out and add back in correct order
|
|
CMapWordToPtr* passthroughMapNEW = new CMapWordToPtr();
|
|
|
|
PassthroughLookupTable* passthroughElement = NULL;
|
|
|
|
WORD myCount = (WORD) passthroughMap->GetCount();
|
|
|
|
for (WORD loop = 0; loop < myCount; loop++)
|
|
{
|
|
BOOL status = passthroughMap->Lookup(loop, (void*&)passthroughElement);
|
|
|
|
if (status)
|
|
{
|
|
WORD NewIndex = myCount - 1 - loop;
|
|
|
|
//For test purposes only
|
|
// char* newColumnName = passthroughElement->GetColumnName();
|
|
// char* newAlias = passthroughElement->GetTableAlias();
|
|
|
|
//Add to new passthrough map
|
|
passthroughMapNEW->SetAt(NewIndex, (void*)passthroughElement);
|
|
}
|
|
}
|
|
*ppPassthroughMap = passthroughMapNEW;
|
|
delete passthroughMap;
|
|
}
|
|
}
|
|
|
|
void TidyupPassthroughMap(CMapWordToPtr* passthroughMap)
|
|
{
|
|
//Test tidy up PassthroughMap
|
|
if (passthroughMap && !(passthroughMap->IsEmpty()))
|
|
{
|
|
for(POSITION pos = passthroughMap->GetStartPosition(); pos != NULL; )
|
|
{
|
|
WORD key = 0; //not used
|
|
PassthroughLookupTable* pa = NULL;
|
|
passthroughMap->GetNextAssoc( pos, key, (void*&)pa );
|
|
|
|
if (pa)
|
|
delete pa;
|
|
}
|
|
//delete passthroughMap;
|
|
//passthroughMap = NULL;
|
|
}
|
|
|
|
//SAI ADDED
|
|
delete passthroughMap;
|
|
passthroughMap = NULL;
|
|
}
|
|
|
|
BOOL TableColumnInfo :: IsTableReferenced(LPISAMTABLEDEF lpTableDef)
|
|
{
|
|
BOOL fStatus = FALSE;
|
|
|
|
TableColumnList* ptr =lpList;
|
|
|
|
while (!fStatus && ptr)
|
|
{
|
|
if (lpTableDef == ptr->lpISAMTableDef)
|
|
{
|
|
fStatus = TRUE;
|
|
}
|
|
|
|
ptr = ptr->Next;
|
|
}
|
|
|
|
return fStatus;
|
|
}
|
|
|
|
BOOL TableColumnInfo :: IsZeroOrOneList()
|
|
{
|
|
if ( (lpList == NULL) || (lpList->Next == NULL) )
|
|
return TRUE;
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
DateTimeParser ::DateTimeParser(BSTR dateTimeStr)
|
|
{
|
|
//Initialize
|
|
m_year = 0;
|
|
m_month = 0;
|
|
m_day = 0;
|
|
m_hour = 0;
|
|
m_min = 0;
|
|
m_sec = 0;
|
|
m_microSec = 0;
|
|
fIsValid = TRUE; //Indicates if dataTime string is valid
|
|
|
|
|
|
|
|
//Make a copy of the string
|
|
dateTimeBuff[0] = 0;
|
|
strcpy(dateTimeBuff, "00000000000000.000000");
|
|
// if ( dateTimeStr && ( wcslen(dateTimeStr) >= DATETIME_FORMAT_LEN) )
|
|
|
|
long lLength = wcslen(dateTimeStr);
|
|
if ( dateTimeStr && lLength )
|
|
{
|
|
//Copy string into buffer
|
|
if (lLength > DATETIME_FORMAT_LEN)
|
|
lLength = DATETIME_FORMAT_LEN;
|
|
|
|
wcstombs(dateTimeBuff, dateTimeStr, lLength);
|
|
dateTimeBuff[DATETIME_FORMAT_LEN] = 0;
|
|
|
|
//Now check each of the fields in the string
|
|
ValidateFields();
|
|
}
|
|
else
|
|
{
|
|
//error in dateTime string
|
|
fIsValid = FALSE;
|
|
}
|
|
}
|
|
|
|
BOOL DateTimeParser :: IsNumber(char bChar)
|
|
{
|
|
if ( (bChar >= '0') && (bChar <= '9'))
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void DateTimeParser :: FetchField(char* lpStr, WORD wFieldLen, ULONG &wValue)
|
|
{
|
|
tempBuff[0] = 0;
|
|
strncpy (tempBuff, lpStr, wFieldLen);
|
|
tempBuff[wFieldLen] = 0;
|
|
wValue = atoi(tempBuff);
|
|
}
|
|
|
|
void DateTimeParser :: ValidateFields()
|
|
{
|
|
//The dateTime string can represent a date, time or timestamp
|
|
//We need to identify which one it represents and that each
|
|
//field is valid
|
|
char* ptr = dateTimeBuff;
|
|
|
|
//The format of the datetime string is as follows
|
|
// YYYYMMDDHHMMSS.VVVVVV
|
|
|
|
//Check if the date fields are set
|
|
fValidDate = TRUE;
|
|
for (UWORD cIndex = 0; cIndex < DATETIME_DATE_LEN; cIndex++)
|
|
{
|
|
if ( ! IsNumber(dateTimeBuff[cIndex]) )
|
|
fValidDate = FALSE;
|
|
}
|
|
|
|
//If there is a valid date get the fields
|
|
if (fValidDate)
|
|
{
|
|
//Copy the year
|
|
FetchField(ptr, DATETIME_YEAR_LEN, m_year);
|
|
|
|
//Copy the month
|
|
ptr += DATETIME_YEAR_LEN;
|
|
FetchField(ptr, DATETIME_MONTH_LEN, m_month);
|
|
|
|
//Copy the day
|
|
ptr += DATETIME_MONTH_LEN;
|
|
FetchField(ptr, DATETIME_DAY_LEN, m_day);
|
|
}
|
|
|
|
ptr = dateTimeBuff + DATETIME_YEAR_LEN + DATETIME_MONTH_LEN + DATETIME_DAY_LEN;
|
|
fValidTime = TRUE;
|
|
for (cIndex = 0; cIndex < DATETIME_TIME_LEN; cIndex++)
|
|
{
|
|
if ( ! IsNumber(ptr[cIndex]) )
|
|
fValidTime = FALSE;
|
|
}
|
|
|
|
//If there is a valid time get the fields
|
|
if (fValidTime)
|
|
{
|
|
//Copy the hour
|
|
FetchField(ptr, DATETIME_HOUR_LEN, m_hour);
|
|
|
|
//Copy the min
|
|
ptr += DATETIME_HOUR_LEN;
|
|
FetchField(ptr, DATETIME_MIN_LEN, m_min);
|
|
|
|
//Copy the sec
|
|
ptr += DATETIME_MIN_LEN;
|
|
FetchField(ptr, DATETIME_SEC_LEN, m_sec);
|
|
}
|
|
|
|
//Set pointer to start of microseconds (just past the '.' character)
|
|
ptr = dateTimeBuff + DATETIME_YEAR_LEN + DATETIME_MONTH_LEN + DATETIME_DAY_LEN
|
|
+ DATETIME_HOUR_LEN + DATETIME_MIN_LEN + DATETIME_SEC_LEN + 1;
|
|
|
|
//Now we get the number of micro seconds
|
|
FetchField(ptr, DATETIME_MICROSEC_LEN, m_microSec);
|
|
|
|
//analyse what datetime string represents
|
|
fIsaTimestamp = FALSE;
|
|
fIsaDate = FALSE;
|
|
fIsaTime = FALSE;
|
|
|
|
if ( !fValidDate && !fValidTime)
|
|
{
|
|
fIsValid = FALSE;
|
|
}
|
|
else
|
|
{
|
|
if (fValidDate)
|
|
{
|
|
//This must represent a date or timestamp
|
|
if (fValidTime)
|
|
{
|
|
//This represents a timestamp
|
|
fIsaTimestamp = TRUE;
|
|
fIsaDate = TRUE;
|
|
fIsaTime = TRUE;
|
|
}
|
|
else
|
|
{
|
|
//this represents a date
|
|
fIsaDate = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//This must represent a Time
|
|
fIsaTime = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
|