6566 lines
209 KiB
C++
6566 lines
209 KiB
C++
/***************************************************************************/
|
|
/* EVALUATE.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 <time.h>
|
|
#include <memory.h>
|
|
/***************************************************************************/
|
|
#define SECONDS_PER_DAY (60L * 60L * 24L)
|
|
|
|
typedef struct HFILE_BUFFERtag {
|
|
HGLOBAL hGlobal;
|
|
HFILE hfile;
|
|
UWORD offset;
|
|
LPSTR ptr;
|
|
char buffer[32768];
|
|
} HF_BUFFER, FAR * HFILE_BUFFER;
|
|
|
|
HFILE_BUFFER _lcreat_buffer(LPCSTR szFilename, int arg)
|
|
{
|
|
HGLOBAL h;
|
|
HFILE_BUFFER hf;
|
|
|
|
h = GlobalAlloc(GMEM_MOVEABLE, sizeof(HF_BUFFER));
|
|
if (h == NULL)
|
|
return NULL;
|
|
hf = (HFILE_BUFFER) GlobalLock(h);
|
|
if (hf == NULL) {
|
|
GlobalFree(h);
|
|
return NULL;
|
|
}
|
|
hf->hGlobal = h;
|
|
hf->hfile = _lcreat(szFilename, arg);
|
|
if (hf->hfile == HFILE_ERROR) {
|
|
GlobalUnlock(h);
|
|
GlobalFree(h);
|
|
return NULL;
|
|
}
|
|
hf->offset = 0;
|
|
hf->ptr = hf->buffer;
|
|
return hf;
|
|
}
|
|
|
|
HFILE _lclose_buffer(HFILE_BUFFER hf)
|
|
{
|
|
HGLOBAL h;
|
|
|
|
if (hf->offset != 0) {
|
|
if ((UINT) hf->offset !=
|
|
_lwrite(hf->hfile, hf->buffer, (UINT) hf->offset)) {
|
|
h = hf->hGlobal;
|
|
_lclose(hf->hfile);
|
|
GlobalUnlock(h);
|
|
GlobalFree(h);
|
|
return HFILE_ERROR;
|
|
}
|
|
}
|
|
if (HFILE_ERROR == _lclose(hf->hfile)) {
|
|
h = hf->hGlobal;
|
|
GlobalUnlock(h);
|
|
GlobalFree(h);
|
|
return HFILE_ERROR;
|
|
}
|
|
h = hf->hGlobal;
|
|
GlobalUnlock(h);
|
|
GlobalFree(h);
|
|
return 0;
|
|
}
|
|
|
|
UINT _lwrite_buffer(HFILE_BUFFER hf, LPSTR ptr, UINT len)
|
|
{
|
|
UWORD total;
|
|
UWORD count;
|
|
|
|
total = 0;
|
|
while (TRUE) {
|
|
if (len > sizeof(hf->buffer) - hf->offset)
|
|
count = sizeof(hf->buffer) - hf->offset;
|
|
else
|
|
count = (UWORD) len;
|
|
_fmemcpy(hf->ptr, ptr, count);
|
|
hf->ptr += (count);
|
|
ptr += (count);
|
|
hf->offset += (count);
|
|
total += (count);
|
|
len -= (count);
|
|
if (len == 0)
|
|
break;
|
|
if (sizeof(hf->buffer) !=
|
|
_lwrite(hf->hfile, hf->buffer, sizeof(hf->buffer)))
|
|
return ((UINT) HFILE_ERROR);
|
|
hf->offset = 0;
|
|
hf->ptr = hf->buffer;
|
|
}
|
|
return total;
|
|
}
|
|
/***************************************************************************/
|
|
void INTFUNC DateAdd(DATE_STRUCT date, SWORD offset, DATE_STRUCT FAR *result)
|
|
|
|
/* Adds specified number of days to a date */
|
|
|
|
{
|
|
static struct tm tm_time;
|
|
time_t t;
|
|
|
|
/* Create time structure */
|
|
tm_time.tm_sec = 0;
|
|
tm_time.tm_min = 0;
|
|
tm_time.tm_hour = 0;
|
|
tm_time.tm_mday = date.day + offset;
|
|
tm_time.tm_mon = date.month - 1;
|
|
tm_time.tm_year = (date.year >= 1900 ? date.year - 1900 : 0);
|
|
tm_time.tm_wday = 0;
|
|
tm_time.tm_yday = 0;
|
|
tm_time.tm_isdst = 0;
|
|
|
|
/* Correct values */
|
|
t = mktime(&tm_time);
|
|
if (t == -1) {
|
|
result->year = 0;
|
|
result->month = 0;
|
|
result->day = 0;
|
|
return;
|
|
}
|
|
|
|
/* Return answer */
|
|
result->year = tm_time.tm_year + 1900;
|
|
result->month = tm_time.tm_mon + 1;
|
|
result->day = (WORD) tm_time.tm_mday;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
int INTFUNC DateDifference(DATE_STRUCT leftDate, DATE_STRUCT rightDate)
|
|
|
|
/* Compares two dates. */
|
|
|
|
{
|
|
static struct tm tm_time;
|
|
time_t left_t;
|
|
time_t right_t;
|
|
|
|
/* Create left value */
|
|
tm_time.tm_sec = 0;
|
|
tm_time.tm_min = 0;
|
|
tm_time.tm_hour = 0;
|
|
tm_time.tm_mday = leftDate.day;
|
|
tm_time.tm_mon = leftDate.month-1;
|
|
tm_time.tm_year = (leftDate.year >= 1900 ? leftDate.year - 1900 : 0);
|
|
tm_time.tm_wday = 0;
|
|
tm_time.tm_yday = 0;
|
|
tm_time.tm_isdst = 0;
|
|
left_t = mktime(&tm_time);
|
|
|
|
/* Create right value */
|
|
tm_time.tm_sec = 0;
|
|
tm_time.tm_min = 0;
|
|
tm_time.tm_hour = 0;
|
|
tm_time.tm_mday = rightDate.day;
|
|
tm_time.tm_mon = rightDate.month-1;
|
|
tm_time.tm_year = (rightDate.year >= 1900 ? rightDate.year - 1900 : 0);
|
|
tm_time.tm_wday = 0;
|
|
tm_time.tm_yday = 0;
|
|
tm_time.tm_isdst = 0;
|
|
right_t = mktime(&tm_time);
|
|
|
|
/* Return answer */
|
|
return (int) ((left_t - right_t) / SECONDS_PER_DAY);
|
|
}
|
|
/***************************************************************************/
|
|
int INTFUNC DateCompare(DATE_STRUCT leftDate, DATE_STRUCT rightDate)
|
|
|
|
/* Compares two dates. */
|
|
|
|
{
|
|
if ( (leftDate.year - rightDate.year) != 0 )
|
|
return (int) (leftDate.year -rightDate.year);
|
|
else if ( (leftDate.month - rightDate.month) != 0 )
|
|
return (int) (leftDate.month -rightDate.month);
|
|
else
|
|
return (int) (leftDate.day -rightDate.day);
|
|
}
|
|
|
|
/***************************************************************************/
|
|
int INTFUNC TimeCompare(TIME_STRUCT leftTime, TIME_STRUCT rightTime)
|
|
|
|
/* Compares two times. */
|
|
|
|
{
|
|
if ((leftTime.hour - rightTime.hour) != 0)
|
|
return (int) (leftTime.hour - rightTime.hour);
|
|
else if ((leftTime.minute - rightTime.minute) != 0)
|
|
return (int) (leftTime.minute - rightTime.minute);
|
|
else
|
|
return (int) (leftTime.second - rightTime.second);
|
|
}
|
|
/***************************************************************************/
|
|
int INTFUNC TimestampCompare(TIMESTAMP_STRUCT leftTimestamp,
|
|
TIMESTAMP_STRUCT rightTimestamp)
|
|
|
|
/* Compares two timestamp. */
|
|
|
|
{
|
|
if ((leftTimestamp.year - rightTimestamp.year) != 0)
|
|
return (int) (leftTimestamp.year - rightTimestamp.year);
|
|
else if ((leftTimestamp.month - rightTimestamp.month) != 0)
|
|
return (int) (leftTimestamp.month - rightTimestamp.month);
|
|
else if ((leftTimestamp.day - rightTimestamp.day) != 0)
|
|
return (int) (leftTimestamp.day - rightTimestamp.day);
|
|
else if ((leftTimestamp.hour - rightTimestamp.hour) != 0)
|
|
return (int) (leftTimestamp.hour - rightTimestamp.hour);
|
|
else if ((leftTimestamp.minute - rightTimestamp.minute) != 0)
|
|
return (int) (leftTimestamp.minute - rightTimestamp.minute);
|
|
else if ((leftTimestamp.second - rightTimestamp.second) != 0)
|
|
return (int) (leftTimestamp.second - rightTimestamp.second);
|
|
else if (leftTimestamp.fraction > rightTimestamp.fraction)
|
|
return 1;//-1;
|
|
else if (leftTimestamp.fraction < rightTimestamp.fraction)
|
|
return -1;//1;
|
|
else
|
|
return 0;
|
|
}
|
|
/***************************************************************************/
|
|
RETCODE INTFUNC NumericCompare(LPSQLNODE lpSqlNode, LPSQLNODE lpSqlNodeLeft,
|
|
UWORD Operator, LPSQLNODE lpSqlNodeRight)
|
|
|
|
/* Compares two numerical valuesas follows: */
|
|
/* lpSqlNode->value.Double = lpSqlNodeLeft Operator lpSqlNodeRight */
|
|
{
|
|
RETCODE err;
|
|
UWORD op;
|
|
SQLNODE sqlNode;
|
|
UCHAR szBuffer[32];
|
|
|
|
/* What is the type of the left side? */
|
|
switch (lpSqlNodeLeft->sqlDataType) {
|
|
case TYPE_DOUBLE:
|
|
case TYPE_INTEGER:
|
|
|
|
/* Left side is a double or integer. What is the right side? */
|
|
switch (lpSqlNodeRight->sqlDataType) {
|
|
case TYPE_DOUBLE:
|
|
case TYPE_INTEGER:
|
|
|
|
/* Right side is also a double or an integer. Compare them */
|
|
switch (Operator) {
|
|
case OP_EQ:
|
|
if (lpSqlNodeLeft->value.Double ==
|
|
lpSqlNodeRight->value.Double)
|
|
lpSqlNode->value.Double = TRUE;
|
|
else
|
|
lpSqlNode->value.Double = FALSE;
|
|
break;
|
|
case OP_NE:
|
|
if (lpSqlNodeLeft->value.Double !=
|
|
lpSqlNodeRight->value.Double)
|
|
lpSqlNode->value.Double = TRUE;
|
|
else
|
|
lpSqlNode->value.Double = FALSE;
|
|
break;
|
|
case OP_LE:
|
|
if (lpSqlNodeLeft->value.Double <=
|
|
lpSqlNodeRight->value.Double)
|
|
lpSqlNode->value.Double = TRUE;
|
|
else
|
|
lpSqlNode->value.Double = FALSE;
|
|
break;
|
|
case OP_LT:
|
|
if (lpSqlNodeLeft->value.Double <
|
|
lpSqlNodeRight->value.Double)
|
|
lpSqlNode->value.Double = TRUE;
|
|
else
|
|
lpSqlNode->value.Double = FALSE;
|
|
break;
|
|
case OP_GE:
|
|
if (lpSqlNodeLeft->value.Double >=
|
|
lpSqlNodeRight->value.Double)
|
|
lpSqlNode->value.Double = TRUE;
|
|
else
|
|
lpSqlNode->value.Double = FALSE;
|
|
break;
|
|
case OP_GT:
|
|
if (lpSqlNodeLeft->value.Double >
|
|
lpSqlNodeRight->value.Double)
|
|
lpSqlNode->value.Double = TRUE;
|
|
else
|
|
lpSqlNode->value.Double = FALSE;
|
|
break;
|
|
case OP_LIKE:
|
|
case OP_NOTLIKE:
|
|
default:
|
|
return ERR_INTERNAL;
|
|
}
|
|
break;
|
|
|
|
case TYPE_NUMERIC:
|
|
/* Right side is a numeric. Is left side a double? */
|
|
if (lpSqlNodeLeft->sqlDataType == TYPE_DOUBLE) {
|
|
|
|
/* Yes. Promote the right side to a double */
|
|
sqlNode = *lpSqlNodeRight;
|
|
sqlNode.sqlDataType = TYPE_DOUBLE;
|
|
sqlNode.sqlSqlType = SQL_DOUBLE;
|
|
sqlNode.sqlPrecision = 15;
|
|
sqlNode.sqlScale = NO_SCALE;
|
|
err = CharToDouble(lpSqlNodeRight->value.String,
|
|
s_lstrlen(lpSqlNodeRight->value.String), FALSE,
|
|
-1.7E308, 1.7E308, &(sqlNode.value.Double));
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Compare the two doubles */
|
|
err = NumericCompare(lpSqlNode, lpSqlNodeLeft, Operator,
|
|
&sqlNode);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
|
|
/* Right side is a numeric. Is left side an integer? */
|
|
else { /* (lpSqlNodeLeft->sqlDataType == TYPE_INTEGER) */
|
|
|
|
/* Yes. Promote the left side to a numeric */
|
|
sqlNode = *lpSqlNodeLeft;
|
|
sqlNode.sqlDataType = TYPE_NUMERIC;
|
|
sqlNode.sqlSqlType = SQL_NUMERIC;
|
|
sqlNode.sqlPrecision = 10;
|
|
sqlNode.sqlScale = 0;
|
|
if (lpSqlNodeLeft->value.Double != 0.0)
|
|
wsprintf((LPSTR)szBuffer, "%ld", (long) lpSqlNodeLeft->value.Double);
|
|
else
|
|
s_lstrcpy((char*)szBuffer, "");
|
|
sqlNode.value.String = (LPUSTR)szBuffer;
|
|
|
|
/* Compare the two numerics */
|
|
err = NumericCompare(lpSqlNode, &sqlNode, Operator,
|
|
lpSqlNodeRight);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
break;
|
|
|
|
case TYPE_CHAR:
|
|
case TYPE_BINARY:
|
|
case TYPE_DATE:
|
|
case TYPE_TIME:
|
|
case TYPE_TIMESTAMP:
|
|
return ERR_INTERNAL;
|
|
default:
|
|
return ERR_NOTSUPPORTED;
|
|
}
|
|
break;
|
|
case TYPE_NUMERIC:
|
|
|
|
/* Left side is a numeric. What is the right side? */
|
|
switch (lpSqlNodeRight->sqlDataType) {
|
|
case TYPE_DOUBLE:
|
|
case TYPE_INTEGER:
|
|
|
|
/* Right side is a double or integer. Swap left and right and */
|
|
/* then do the comaprison */
|
|
switch (Operator) {
|
|
case OP_EQ:
|
|
op = OP_EQ;
|
|
break;
|
|
case OP_NE:
|
|
op = OP_NE;
|
|
break;
|
|
case OP_LE:
|
|
op = OP_GE;
|
|
break;
|
|
case OP_LT:
|
|
op = OP_GT;
|
|
break;
|
|
case OP_GE:
|
|
op = OP_LE;
|
|
break;
|
|
case OP_GT:
|
|
op = OP_LT;
|
|
break;
|
|
case OP_LIKE:
|
|
case OP_NOTLIKE:
|
|
default:
|
|
return ERR_INTERNAL;
|
|
}
|
|
err = NumericCompare(lpSqlNode, lpSqlNodeRight, op,
|
|
lpSqlNodeLeft);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
break;
|
|
|
|
case TYPE_NUMERIC:
|
|
/* Right side is also numeric. Compare them as numeric */
|
|
err = BCDCompare(lpSqlNode, lpSqlNodeLeft, Operator, lpSqlNodeRight);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
break;
|
|
|
|
case TYPE_CHAR:
|
|
case TYPE_BINARY:
|
|
case TYPE_DATE:
|
|
case TYPE_TIME:
|
|
case TYPE_TIMESTAMP:
|
|
return ERR_INTERNAL;
|
|
default:
|
|
return ERR_NOTSUPPORTED;
|
|
}
|
|
break;
|
|
case TYPE_CHAR:
|
|
case TYPE_BINARY:
|
|
case TYPE_DATE:
|
|
case TYPE_TIME:
|
|
case TYPE_TIMESTAMP:
|
|
return ERR_INTERNAL;
|
|
default:
|
|
return ERR_NOTSUPPORTED;
|
|
}
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
SWORD INTFUNC ToInteger(LPSQLNODE lpSqlNode)
|
|
|
|
/* Returns the value of the node (as an integer). If the data type of the */
|
|
/* node is incompatible, zerois returned */
|
|
{
|
|
double dbl;
|
|
RETCODE err;
|
|
|
|
switch (lpSqlNode->sqlDataType) {
|
|
case TYPE_DOUBLE:
|
|
case TYPE_INTEGER:
|
|
return ((SWORD) lpSqlNode->value.Double);
|
|
case TYPE_NUMERIC:
|
|
err = CharToDouble(lpSqlNode->value.String,
|
|
s_lstrlen(lpSqlNode->value.String), FALSE,
|
|
-1.7E308, 1.7E308, &dbl);
|
|
if (err != ERR_SUCCESS)
|
|
return 0;
|
|
return ((SWORD) dbl);
|
|
case TYPE_CHAR:
|
|
case TYPE_BINARY:
|
|
case TYPE_DATE:
|
|
case TYPE_TIME:
|
|
case TYPE_TIMESTAMP:
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
/***************************************************************************/
|
|
RETCODE INTFUNC NumericAlgebra(LPSQLNODE lpSqlNode, LPSQLNODE lpSqlNodeLeft,
|
|
UWORD Operator, LPSQLNODE lpSqlNodeRight,
|
|
LPUSTR lpWorkBuffer1, LPUSTR lpWorkBuffer2,
|
|
LPUSTR lpWorkBuffer3)
|
|
|
|
/* Perfoms algebraic operation in two numerical or char values as follows: */
|
|
/* lpSqlNode lpSqlNodeLeft Operator lpSqlNodeRight */
|
|
|
|
{
|
|
RETCODE err;
|
|
SQLNODE sqlNode;
|
|
UCHAR szBuffer[32];
|
|
|
|
/* What is the type of the left side? */
|
|
switch (lpSqlNodeLeft->sqlDataType) {
|
|
case TYPE_DOUBLE:
|
|
case TYPE_INTEGER:
|
|
|
|
/* Left side is a double or integer. What is the right side? */
|
|
switch (lpSqlNodeRight != NULL ? lpSqlNodeRight->sqlDataType :
|
|
lpSqlNodeLeft->sqlDataType) {
|
|
case TYPE_DOUBLE:
|
|
case TYPE_INTEGER:
|
|
|
|
/* Right side is also a double or an integer. Do the operation */
|
|
switch (Operator) {
|
|
case OP_NEG:
|
|
lpSqlNode->value.Double = -(lpSqlNodeLeft->value.Double);
|
|
break;
|
|
case OP_PLUS:
|
|
lpSqlNode->value.Double = lpSqlNodeLeft->value.Double +
|
|
lpSqlNodeRight->value.Double;
|
|
break;
|
|
case OP_MINUS:
|
|
lpSqlNode->value.Double = lpSqlNodeLeft->value.Double -
|
|
lpSqlNodeRight->value.Double;
|
|
break;
|
|
case OP_TIMES:
|
|
lpSqlNode->value.Double = lpSqlNodeLeft->value.Double *
|
|
lpSqlNodeRight->value.Double;
|
|
break;
|
|
case OP_DIVIDEDBY:
|
|
if (lpSqlNodeRight->value.Double != 0.0)
|
|
lpSqlNode->value.Double = lpSqlNodeLeft->value.Double /
|
|
lpSqlNodeRight->value.Double;
|
|
else
|
|
return ERR_ZERODIVIDE;
|
|
break;
|
|
default:
|
|
return ERR_INTERNAL;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case TYPE_NUMERIC:
|
|
/* Right side is a numeric. Is left side a double? */
|
|
if (lpSqlNodeLeft->sqlDataType == TYPE_DOUBLE) {
|
|
|
|
/* Yes. Promote the right side to a double */
|
|
sqlNode = *lpSqlNodeRight;
|
|
sqlNode.sqlDataType = TYPE_DOUBLE;
|
|
sqlNode.sqlSqlType = SQL_DOUBLE;
|
|
sqlNode.sqlPrecision = 15;
|
|
sqlNode.sqlScale = NO_SCALE;
|
|
err = CharToDouble(lpSqlNodeRight->value.String,
|
|
s_lstrlen(lpSqlNodeRight->value.String), FALSE,
|
|
-1.7E308, 1.7E308, &(sqlNode.value.Double));
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Compute the result */
|
|
err = NumericAlgebra(lpSqlNode, lpSqlNodeLeft, Operator,
|
|
&sqlNode, lpWorkBuffer1, lpWorkBuffer2,
|
|
lpWorkBuffer3);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
|
|
/* Right side is a numeric. Is left side an integer? */
|
|
else { /* (lpSqlNodeLeft->sqlDataType == TYPE_INTEGER) */
|
|
|
|
/* Yes. Promote the left side to a numeric */
|
|
sqlNode = *lpSqlNodeLeft;
|
|
sqlNode.sqlDataType = TYPE_NUMERIC;
|
|
sqlNode.sqlSqlType = SQL_NUMERIC;
|
|
sqlNode.sqlPrecision = 10;
|
|
sqlNode.sqlScale = 0;
|
|
if (lpSqlNodeLeft->value.Double != 0.0)
|
|
wsprintf((LPSTR)szBuffer, "%ld", (long) lpSqlNodeLeft->value.Double);
|
|
else
|
|
s_lstrcpy((char*)szBuffer, "");
|
|
sqlNode.value.String = (LPUSTR)szBuffer;
|
|
|
|
/* Compute the result */
|
|
err = NumericAlgebra(lpSqlNode, &sqlNode, Operator,
|
|
lpSqlNodeRight, lpWorkBuffer1,
|
|
lpWorkBuffer2, lpWorkBuffer3);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
break;
|
|
|
|
case TYPE_DATE:
|
|
/* Right side is a date. Do the operation */
|
|
switch (Operator) {
|
|
case OP_PLUS:
|
|
DateAdd(lpSqlNodeRight->value.Date,
|
|
ToInteger(lpSqlNodeLeft),
|
|
&(lpSqlNode->value.Date));
|
|
break;
|
|
case OP_NEG:
|
|
case OP_MINUS:
|
|
case OP_TIMES:
|
|
case OP_DIVIDEDBY:
|
|
default:
|
|
return ERR_INTERNAL;
|
|
}
|
|
break;
|
|
case TYPE_CHAR:
|
|
case TYPE_BINARY:
|
|
case TYPE_TIME:
|
|
case TYPE_TIMESTAMP:
|
|
return ERR_INTERNAL;
|
|
default:
|
|
return ERR_NOTSUPPORTED;
|
|
}
|
|
break;
|
|
|
|
case TYPE_NUMERIC:
|
|
|
|
/* Left side is a numeric. What is the right side? */
|
|
switch (lpSqlNodeRight != NULL ? lpSqlNodeRight->sqlDataType :
|
|
lpSqlNodeLeft->sqlDataType) {
|
|
case TYPE_DOUBLE:
|
|
|
|
/* Right side is a double. Promote the left side to a double */
|
|
sqlNode = *lpSqlNodeLeft;
|
|
sqlNode.sqlDataType = TYPE_DOUBLE;
|
|
sqlNode.sqlSqlType = SQL_DOUBLE;
|
|
sqlNode.sqlPrecision = 15;
|
|
sqlNode.sqlScale = NO_SCALE;
|
|
err = CharToDouble(lpSqlNodeLeft->value.String,
|
|
s_lstrlen(lpSqlNodeLeft->value.String), FALSE,
|
|
-1.7E308, 1.7E308, &(sqlNode.value.Double));
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Compute the result */
|
|
err = NumericAlgebra(lpSqlNode, &sqlNode, Operator,
|
|
lpSqlNodeRight, lpWorkBuffer1,
|
|
lpWorkBuffer2, lpWorkBuffer3);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
break;
|
|
|
|
case TYPE_INTEGER:
|
|
/* Right side is an integer. Promote the right side to nuemric */
|
|
sqlNode = *lpSqlNodeRight;
|
|
sqlNode.sqlDataType = TYPE_NUMERIC;
|
|
sqlNode.sqlSqlType = SQL_NUMERIC;
|
|
sqlNode.sqlPrecision = 10;
|
|
sqlNode.sqlScale = 0;
|
|
if (lpSqlNodeRight->value.Double != 0.0)
|
|
wsprintf((LPSTR)szBuffer, "%ld", (long) lpSqlNodeRight->value.Double);
|
|
else
|
|
s_lstrcpy((char*)szBuffer, "");
|
|
sqlNode.value.String = szBuffer;
|
|
|
|
/* Compute the result */
|
|
err = NumericAlgebra(lpSqlNode, lpSqlNodeLeft, Operator,
|
|
&sqlNode, lpWorkBuffer1, lpWorkBuffer2,
|
|
lpWorkBuffer3);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
break;
|
|
|
|
case TYPE_NUMERIC:
|
|
/* Right side is also numeric. Do the operation */
|
|
err = BCDAlgebra(lpSqlNode, lpSqlNodeLeft, Operator,
|
|
lpSqlNodeRight, lpWorkBuffer1,
|
|
lpWorkBuffer2, lpWorkBuffer3);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
break;
|
|
|
|
case TYPE_CHAR:
|
|
case TYPE_BINARY:
|
|
case TYPE_DATE:
|
|
case TYPE_TIME:
|
|
case TYPE_TIMESTAMP:
|
|
return ERR_INTERNAL;
|
|
default:
|
|
return ERR_NOTSUPPORTED;
|
|
}
|
|
break;
|
|
|
|
case TYPE_DATE:
|
|
|
|
/* Left side is a date. What operator? */
|
|
switch (Operator) {
|
|
case OP_NEG:
|
|
return ERR_INTERNAL;
|
|
break;
|
|
case OP_PLUS:
|
|
DateAdd(lpSqlNodeLeft->value.Date,
|
|
ToInteger(lpSqlNodeRight),
|
|
&(lpSqlNode->value.Date));
|
|
break;
|
|
case OP_MINUS:
|
|
switch (lpSqlNodeRight->sqlDataType) {
|
|
case TYPE_DOUBLE:
|
|
case TYPE_INTEGER:
|
|
case TYPE_NUMERIC:
|
|
DateAdd(lpSqlNodeLeft->value.Date, (SWORD)
|
|
-(ToInteger(lpSqlNodeRight)),
|
|
&(lpSqlNode->value.Date));
|
|
break;
|
|
case TYPE_CHAR:
|
|
case TYPE_BINARY:
|
|
return ERR_INTERNAL;
|
|
break;
|
|
case TYPE_DATE:
|
|
lpSqlNode->value.Double = DateDifference(
|
|
lpSqlNodeLeft->value.Date,
|
|
lpSqlNodeRight->value.Date);
|
|
break;
|
|
case TYPE_TIME:
|
|
return ERR_INTERNAL;
|
|
break;
|
|
case TYPE_TIMESTAMP:
|
|
return ERR_NOTSUPPORTED;
|
|
break;
|
|
}
|
|
break;
|
|
case OP_TIMES:
|
|
case OP_DIVIDEDBY:
|
|
default:
|
|
return ERR_INTERNAL;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case TYPE_CHAR:
|
|
/* Left side is character. Concatenate */
|
|
if ((lpSqlNodeRight->sqlDataType != TYPE_CHAR) ||
|
|
(Operator != OP_PLUS))
|
|
return ERR_INTERNAL;
|
|
if ((s_lstrlen(lpSqlNodeLeft->value.String) +
|
|
s_lstrlen(lpSqlNodeRight->value.String)) > lpSqlNode->sqlPrecision)
|
|
return ERR_CONCATOVERFLOW;
|
|
s_lstrcpy(lpSqlNode->value.String, lpSqlNodeLeft->value.String);
|
|
s_lstrcat(lpSqlNode->value.String, lpSqlNodeRight->value.String);
|
|
break;
|
|
|
|
case TYPE_BINARY:
|
|
case TYPE_TIME:
|
|
case TYPE_TIMESTAMP:
|
|
return ERR_INTERNAL;
|
|
default:
|
|
return ERR_NOTSUPPORTED;
|
|
}
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC SetParameterValue(LPSTMT lpstmt,
|
|
LPSQLNODE lpSqlNodeParameter,
|
|
SDWORD FAR *pcbOffset,
|
|
SWORD fCType,
|
|
PTR rgbValue,
|
|
SDWORD cbValue)
|
|
|
|
/* Sets the value of a parameter node to the value given. If fCType is */
|
|
/* SQL_C_CHAR and the parameter is TYPE_CHAR (or if fCType is SQL_C_BINARY */
|
|
/* and the parameter is TYPE_BINARY), the parameter is written at offset */
|
|
/* (*pcbOffset) and (*pcbOffset) is incremented by the number of */
|
|
/* of characters written. */
|
|
|
|
{
|
|
SWORD err;
|
|
SDWORD cbOffset;
|
|
DATE_STRUCT FAR *lpDate;
|
|
TIME_STRUCT FAR *lpTime;
|
|
TIMESTAMP_STRUCT FAR *lpTimestamp;
|
|
static time_t t;
|
|
struct tm *ts;
|
|
UCHAR szValue[20];
|
|
LPUSTR lpszValue;
|
|
SDWORD cbVal;
|
|
LPUSTR lpszVal;
|
|
SDWORD i;
|
|
UCHAR nibble;
|
|
LPUSTR lpFrom;
|
|
LPUSTR lpTo;
|
|
|
|
/* Null data? */
|
|
err = SQL_SUCCESS;
|
|
if (cbValue == SQL_NULL_DATA) {
|
|
|
|
/* Yes. Set the value to NULL */
|
|
lpSqlNodeParameter->sqlIsNull = TRUE;
|
|
|
|
switch (lpSqlNodeParameter->sqlDataType) {
|
|
case TYPE_DOUBLE:
|
|
case TYPE_INTEGER:
|
|
lpSqlNodeParameter->value.Double = 0.0;
|
|
break;
|
|
case TYPE_NUMERIC:
|
|
lpSqlNodeParameter->value.String = ToString(
|
|
lpstmt->lpSqlStmt, lpSqlNodeParameter->node.parameter.Value);
|
|
s_lstrcpy(lpSqlNodeParameter->value.String, "");
|
|
break;
|
|
case TYPE_CHAR:
|
|
lpSqlNodeParameter->value.String = ToString(
|
|
lpstmt->lpSqlStmt, lpSqlNodeParameter->node.parameter.Value);
|
|
s_lstrcpy(lpSqlNodeParameter->value.String, "");
|
|
break;
|
|
case TYPE_DATE:
|
|
lpSqlNodeParameter->value.Date.year = 0;
|
|
lpSqlNodeParameter->value.Date.month = 0;
|
|
lpSqlNodeParameter->value.Date.day = 0;
|
|
break;
|
|
case TYPE_TIME:
|
|
lpSqlNodeParameter->value.Time.hour = 0;
|
|
lpSqlNodeParameter->value.Time.minute = 0;
|
|
lpSqlNodeParameter->value.Time.second = 0;
|
|
break;
|
|
case TYPE_TIMESTAMP:
|
|
lpSqlNodeParameter->value.Timestamp.year = 0;
|
|
lpSqlNodeParameter->value.Timestamp.month = 0;
|
|
lpSqlNodeParameter->value.Timestamp.day = 0;
|
|
lpSqlNodeParameter->value.Timestamp.hour = 0;
|
|
lpSqlNodeParameter->value.Timestamp.minute = 0;
|
|
lpSqlNodeParameter->value.Timestamp.second = 0;
|
|
lpSqlNodeParameter->value.Timestamp.fraction = 0;
|
|
break;
|
|
case TYPE_BINARY:
|
|
lpSqlNodeParameter->value.Binary = ToString(
|
|
lpstmt->lpSqlStmt, lpSqlNodeParameter->node.parameter.Value);
|
|
BINARY_LENGTH(lpSqlNodeParameter->value.Binary) = 0;
|
|
break;
|
|
default:
|
|
return ERR_NOTSUPPORTED;
|
|
}
|
|
}
|
|
else {
|
|
|
|
/* No. Set the value to not NULL */
|
|
lpSqlNodeParameter->sqlIsNull = FALSE;
|
|
|
|
/* Put the data value into the parse tree */
|
|
switch (lpSqlNodeParameter->sqlDataType) {
|
|
case TYPE_DOUBLE:
|
|
case TYPE_INTEGER:
|
|
if (fCType == SQL_C_BINARY) {
|
|
switch (lpSqlNodeParameter->sqlSqlType) {
|
|
case SQL_CHAR:
|
|
case SQL_VARCHAR:
|
|
case SQL_LONGVARCHAR:
|
|
return ERR_INTERNAL;
|
|
case SQL_BIT:
|
|
fCType = SQL_C_BIT;
|
|
break;
|
|
case SQL_TINYINT:
|
|
fCType = SQL_C_TINYINT;
|
|
break;
|
|
case SQL_SMALLINT:
|
|
fCType = SQL_C_SHORT;
|
|
break;
|
|
case SQL_INTEGER:
|
|
fCType = SQL_C_LONG;
|
|
break;
|
|
case SQL_REAL:
|
|
fCType = SQL_C_FLOAT;
|
|
break;
|
|
case SQL_DECIMAL:
|
|
case SQL_NUMERIC:
|
|
case SQL_BIGINT:
|
|
return ERR_INTERNAL;
|
|
case SQL_FLOAT:
|
|
case SQL_DOUBLE:
|
|
fCType = SQL_C_DOUBLE;
|
|
break;
|
|
case SQL_BINARY:
|
|
case SQL_VARBINARY:
|
|
case SQL_LONGVARBINARY:
|
|
case SQL_DATE:
|
|
case SQL_TIME:
|
|
case SQL_TIMESTAMP:
|
|
default:
|
|
return ERR_INTERNAL;
|
|
}
|
|
}
|
|
switch (fCType) {
|
|
case SQL_C_CHAR:
|
|
err = CharToDouble((LPUSTR)rgbValue, cbValue, FALSE, -1.7E308,
|
|
1.7E308, &(lpSqlNodeParameter->value.Double));
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
break;
|
|
case SQL_C_SSHORT:
|
|
lpSqlNodeParameter->value.Double = (double)
|
|
*((short far *) rgbValue);
|
|
break;
|
|
case SQL_C_USHORT:
|
|
lpSqlNodeParameter->value.Double = (double)
|
|
*((unsigned short far *) rgbValue);
|
|
break;
|
|
case SQL_C_SLONG:
|
|
lpSqlNodeParameter->value.Double = (double)
|
|
*((long far *) rgbValue);
|
|
break;
|
|
case SQL_C_ULONG:
|
|
lpSqlNodeParameter->value.Double = (double)
|
|
*((unsigned long far *) rgbValue);
|
|
break;
|
|
case SQL_C_FLOAT:
|
|
lpSqlNodeParameter->value.Double = (double)
|
|
*((float far *) rgbValue);
|
|
break;
|
|
case SQL_C_DOUBLE:
|
|
lpSqlNodeParameter->value.Double =
|
|
*((double far *) rgbValue);
|
|
break;
|
|
case SQL_C_BIT:
|
|
lpSqlNodeParameter->value.Double = (double)
|
|
*((unsigned char far *) rgbValue);
|
|
break;
|
|
case SQL_C_STINYINT:
|
|
lpSqlNodeParameter->value.Double = (double)
|
|
*((char far *) rgbValue);
|
|
break;
|
|
case SQL_C_UTINYINT:
|
|
lpSqlNodeParameter->value.Double = (double)
|
|
*((unsigned char far *) rgbValue);
|
|
break;
|
|
case SQL_C_DATE:
|
|
case SQL_C_TIME:
|
|
case SQL_C_TIMESTAMP:
|
|
return ERR_NOTCONVERTABLE;
|
|
case SQL_C_BINARY:
|
|
return ERR_INTERNAL;
|
|
default:
|
|
return ERR_NOTSUPPORTED;
|
|
}
|
|
break;
|
|
|
|
case TYPE_NUMERIC:
|
|
if (fCType == SQL_C_BINARY) {
|
|
switch (lpSqlNodeParameter->sqlSqlType) {
|
|
case SQL_CHAR:
|
|
case SQL_VARCHAR:
|
|
case SQL_LONGVARCHAR:
|
|
case SQL_BIT:
|
|
case SQL_TINYINT:
|
|
case SQL_SMALLINT:
|
|
case SQL_INTEGER:
|
|
case SQL_REAL:
|
|
return ERR_INTERNAL;
|
|
case SQL_DECIMAL:
|
|
case SQL_NUMERIC:
|
|
case SQL_BIGINT:
|
|
fCType = SQL_C_CHAR;
|
|
break;
|
|
case SQL_FLOAT:
|
|
case SQL_DOUBLE:
|
|
case SQL_BINARY:
|
|
case SQL_VARBINARY:
|
|
case SQL_LONGVARBINARY:
|
|
case SQL_DATE:
|
|
case SQL_TIME:
|
|
case SQL_TIMESTAMP:
|
|
default:
|
|
return ERR_INTERNAL;
|
|
}
|
|
}
|
|
|
|
lpSqlNodeParameter->value.String = ToString(
|
|
lpstmt->lpSqlStmt, lpSqlNodeParameter->node.parameter.Value);
|
|
switch (fCType) {
|
|
case SQL_C_CHAR:
|
|
|
|
/* Get true length of source string */
|
|
lpszValue = (LPUSTR) rgbValue;
|
|
if (cbValue == SQL_NTS)
|
|
cbValue = s_lstrlen(lpszValue);
|
|
else if (cbValue < 0)
|
|
cbValue = 0;
|
|
|
|
/* Make sure the number is well formed: Leading blanks... */
|
|
lpszVal = lpszValue;
|
|
cbVal = cbValue;
|
|
while ((cbVal > 0) && (*lpszVal == ' ')) {
|
|
lpszVal++;
|
|
cbVal--;
|
|
}
|
|
|
|
/* ...a minus sign... */
|
|
if ((cbVal > 0) && (*lpszVal == '-')) {
|
|
lpszVal++;
|
|
cbVal--;
|
|
}
|
|
|
|
/* ...some digits... */
|
|
while ((cbVal > 0) && (*lpszVal >= '0')&&(*lpszVal <= '9')) {
|
|
lpszVal++;
|
|
cbVal--;
|
|
}
|
|
|
|
/* ...a decimal point... */
|
|
if ((cbVal > 0) && (*lpszVal == '.')) {
|
|
lpszVal++;
|
|
cbVal--;
|
|
}
|
|
|
|
/* ...some more digits... */
|
|
while ((cbVal > 0) && (*lpszVal >= '0')&&(*lpszVal <= '9')) {
|
|
lpszVal++;
|
|
cbVal--;
|
|
}
|
|
|
|
/* ...some trailing blanks. */
|
|
while ((cbVal > 0) && (*lpszVal == ' ')) {
|
|
lpszVal++;
|
|
cbVal--;
|
|
}
|
|
|
|
/* If there is anything else, error */
|
|
if (cbVal != 0) {
|
|
if (cbValue <= MAX_TOKEN_SIZE) {
|
|
_fmemcpy(lpstmt->szError, lpszValue, (SWORD) cbValue);
|
|
lpstmt->szError[cbValue] = '\0';
|
|
}
|
|
else {
|
|
_fmemcpy(lpstmt->szError, lpszValue, MAX_TOKEN_SIZE);
|
|
lpstmt->szError[MAX_TOKEN_SIZE] = '\0';
|
|
}
|
|
return ERR_MALFORMEDNUMBER;
|
|
}
|
|
|
|
break;
|
|
|
|
case SQL_C_SSHORT:
|
|
wsprintf((LPSTR)szValue, "%d", *((short far *) rgbValue));
|
|
lpszValue = szValue;
|
|
cbValue = s_lstrlen((char*)szValue);
|
|
break;
|
|
case SQL_C_USHORT:
|
|
wsprintf((LPSTR)szValue, "%u", *((unsigned short far *) rgbValue));
|
|
lpszValue = szValue;
|
|
cbValue = s_lstrlen((char*)szValue);
|
|
break;
|
|
case SQL_C_SLONG:
|
|
wsprintf((LPSTR)szValue, "%ld", *((long far *) rgbValue));
|
|
lpszValue = szValue;
|
|
cbValue = lstrlen((char*)szValue);
|
|
break;
|
|
case SQL_C_ULONG:
|
|
wsprintf((LPSTR)szValue, "%lu", *((unsigned long far *) rgbValue));
|
|
lpszValue = szValue;
|
|
cbValue = s_lstrlen((char*)szValue);
|
|
break;
|
|
case SQL_C_FLOAT:
|
|
if (DoubleToChar((double) *((float far *) rgbValue),
|
|
FALSE,
|
|
lpSqlNodeParameter->value.String,
|
|
lpSqlNodeParameter->sqlPrecision + 2 + 1))
|
|
lpSqlNodeParameter->value.String[
|
|
lpSqlNodeParameter->sqlPrecision + 2 + 1 - 1] = '\0';
|
|
lpszValue = lpSqlNodeParameter->value.String;
|
|
cbValue = s_lstrlen(lpSqlNodeParameter->value.String);
|
|
break;
|
|
case SQL_C_DOUBLE:
|
|
if (DoubleToChar(*((double far *) rgbValue),
|
|
FALSE,
|
|
lpSqlNodeParameter->value.String,
|
|
lpSqlNodeParameter->sqlPrecision + 2 + 1))
|
|
lpSqlNodeParameter->value.String[
|
|
lpSqlNodeParameter->sqlPrecision + 2 + 1 - 1] = '\0';
|
|
lpszValue = lpSqlNodeParameter->value.String;
|
|
cbValue = s_lstrlen(lpSqlNodeParameter->value.String);
|
|
break;
|
|
case SQL_C_BIT:
|
|
wsprintf((LPSTR)szValue, "%d", (short)
|
|
*((unsigned char far *) rgbValue));
|
|
lpszValue = szValue;
|
|
cbValue = s_lstrlen((char*)szValue);
|
|
break;
|
|
case SQL_C_STINYINT:
|
|
wsprintf((LPSTR)szValue, "%d", (short)
|
|
*((char far *) rgbValue));
|
|
lpszValue = szValue;
|
|
cbValue = s_lstrlen((char*)szValue);
|
|
break;
|
|
case SQL_C_UTINYINT:
|
|
wsprintf((LPSTR)szValue, "%d", (short)
|
|
*((unsigned char far *) rgbValue));
|
|
lpszValue = szValue;
|
|
cbValue = s_lstrlen((char*)szValue);
|
|
break;
|
|
case SQL_C_DATE:
|
|
case SQL_C_TIME:
|
|
case SQL_C_TIMESTAMP:
|
|
return ERR_NOTCONVERTABLE;
|
|
break;
|
|
case SQL_C_BINARY:
|
|
return ERR_INTERNAL;
|
|
default:
|
|
return ERR_NOTSUPPORTED;
|
|
}
|
|
|
|
/* Normalize the result */
|
|
err = BCDNormalize(lpszValue, cbValue,
|
|
lpSqlNodeParameter->value.String,
|
|
lpSqlNodeParameter->sqlPrecision + 2 + 1,
|
|
lpSqlNodeParameter->sqlPrecision,
|
|
lpSqlNodeParameter->sqlScale);
|
|
break;
|
|
|
|
case TYPE_CHAR:
|
|
lpSqlNodeParameter->value.String = ToString(
|
|
lpstmt->lpSqlStmt, lpSqlNodeParameter->node.parameter.Value);
|
|
switch (fCType) {
|
|
case SQL_C_CHAR:
|
|
|
|
/* Get true lengthof source string */
|
|
if (cbValue == SQL_NTS)
|
|
cbValue = s_lstrlen((char*)rgbValue);
|
|
else if (cbValue < 0)
|
|
cbValue = 0;
|
|
|
|
/* Figure out the offset to write at */
|
|
if (pcbOffset != NULL)
|
|
cbOffset = *pcbOffset;
|
|
else
|
|
cbOffset = 0;
|
|
|
|
/* Make sure we don't write past the end of the buffer */
|
|
if (cbValue > (MAX_CHAR_LITERAL_LENGTH - cbOffset)) {
|
|
cbValue = MAX_CHAR_LITERAL_LENGTH - cbOffset;
|
|
err = ERR_DATATRUNCATED;
|
|
}
|
|
|
|
/* Copy the data */
|
|
_fmemcpy(lpSqlNodeParameter->value.String + cbOffset,
|
|
rgbValue, (SWORD) cbValue);
|
|
lpSqlNodeParameter->value.String[cbValue + cbOffset] = '\0';
|
|
if (pcbOffset != NULL)
|
|
*pcbOffset += (cbValue);
|
|
break;
|
|
|
|
case SQL_C_SSHORT:
|
|
wsprintf((LPSTR) lpSqlNodeParameter->value.String, "%d",
|
|
*((short far *) rgbValue));
|
|
break;
|
|
case SQL_C_USHORT:
|
|
wsprintf((LPSTR) lpSqlNodeParameter->value.String, "%u",
|
|
*((unsigned short far *) rgbValue));
|
|
break;
|
|
case SQL_C_SLONG:
|
|
wsprintf((LPSTR) lpSqlNodeParameter->value.String, "%ld",
|
|
*((long far *) rgbValue));
|
|
break;
|
|
case SQL_C_ULONG:
|
|
wsprintf((LPSTR) lpSqlNodeParameter->value.String, "%lu",
|
|
*((unsigned long far *) rgbValue));
|
|
break;
|
|
case SQL_C_FLOAT:
|
|
if (DoubleToChar((double) *((float far *) rgbValue), TRUE,
|
|
lpSqlNodeParameter->value.String,
|
|
MAX_CHAR_LITERAL_LENGTH))
|
|
lpSqlNodeParameter->value.String[
|
|
MAX_CHAR_LITERAL_LENGTH - 1] = '\0';
|
|
break;
|
|
case SQL_C_DOUBLE:
|
|
if (DoubleToChar(*((double far *) rgbValue), TRUE,
|
|
lpSqlNodeParameter->value.String,
|
|
MAX_CHAR_LITERAL_LENGTH))
|
|
lpSqlNodeParameter->value.String[
|
|
MAX_CHAR_LITERAL_LENGTH - 1] = '\0';
|
|
break;
|
|
case SQL_C_BIT:
|
|
wsprintf((LPSTR) lpSqlNodeParameter->value.String, "%d", (short)
|
|
*((unsigned char far *) rgbValue));
|
|
break;
|
|
case SQL_C_STINYINT:
|
|
wsprintf((LPSTR) lpSqlNodeParameter->value.String, "%d", (short)
|
|
*((char far *) rgbValue));
|
|
break;
|
|
case SQL_C_UTINYINT:
|
|
wsprintf((LPSTR) lpSqlNodeParameter->value.String, "%d", (short)
|
|
*((unsigned char far *) rgbValue));
|
|
break;
|
|
case SQL_C_DATE:
|
|
DateToChar((DATE_STRUCT far *) rgbValue,
|
|
lpSqlNodeParameter->value.String);
|
|
break;
|
|
case SQL_C_TIME:
|
|
TimeToChar((TIME_STRUCT far *) rgbValue,
|
|
lpSqlNodeParameter->value.String);
|
|
break;
|
|
case SQL_C_TIMESTAMP:
|
|
TimestampToChar((TIMESTAMP_STRUCT far *) rgbValue,
|
|
lpSqlNodeParameter->value.String);
|
|
break;
|
|
case SQL_C_BINARY:
|
|
|
|
/* Get true lengthof source string */
|
|
if (cbValue < 0)
|
|
cbValue = 0;
|
|
|
|
/* Figure out the offset to write at */
|
|
if (pcbOffset != NULL)
|
|
cbOffset = *pcbOffset;
|
|
else
|
|
cbOffset = 0;
|
|
|
|
/* Make sure we don't write past the end of the buffer */
|
|
if (cbValue > (MAX_CHAR_LITERAL_LENGTH - cbOffset)) {
|
|
cbValue = MAX_CHAR_LITERAL_LENGTH - cbOffset;
|
|
err = ERR_DATATRUNCATED;
|
|
}
|
|
|
|
/* Copy the data */
|
|
_fmemcpy(lpSqlNodeParameter->value.String + cbOffset,
|
|
rgbValue, (SWORD) cbValue);
|
|
lpSqlNodeParameter->value.String[cbValue + cbOffset] = '\0';
|
|
if (pcbOffset != NULL)
|
|
*pcbOffset += (cbValue);
|
|
break;
|
|
|
|
default:
|
|
return ERR_NOTSUPPORTED;
|
|
}
|
|
break;
|
|
|
|
case TYPE_DATE:
|
|
switch (fCType) {
|
|
case SQL_C_CHAR:
|
|
err = CharToDate((LPUSTR)rgbValue, cbValue,
|
|
&(lpSqlNodeParameter->value.Date));
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
break;
|
|
case SQL_C_SSHORT:
|
|
case SQL_C_USHORT:
|
|
case SQL_C_SLONG:
|
|
case SQL_C_ULONG:
|
|
case SQL_C_FLOAT:
|
|
case SQL_C_DOUBLE:
|
|
case SQL_C_BIT:
|
|
case SQL_C_STINYINT:
|
|
case SQL_C_UTINYINT:
|
|
return ERR_NOTCONVERTABLE;
|
|
case SQL_C_DATE:
|
|
lpSqlNodeParameter->value.Date =
|
|
*((DATE_STRUCT far *) rgbValue);
|
|
break;
|
|
case SQL_C_TIME:
|
|
return ERR_NOTCONVERTABLE;
|
|
case SQL_C_TIMESTAMP:
|
|
lpTimestamp = (TIMESTAMP_STRUCT far *) rgbValue;
|
|
lpSqlNodeParameter->value.Date.year = lpTimestamp->year;
|
|
lpSqlNodeParameter->value.Date.month = lpTimestamp->month;
|
|
lpSqlNodeParameter->value.Date.day = lpTimestamp->day;
|
|
break;
|
|
case SQL_C_BINARY:
|
|
lpSqlNodeParameter->value.Date =
|
|
*((DATE_STRUCT far *) rgbValue);
|
|
break;
|
|
default:
|
|
return ERR_NOTSUPPORTED;
|
|
}
|
|
break;
|
|
|
|
case TYPE_TIME:
|
|
switch (fCType) {
|
|
case SQL_C_CHAR:
|
|
err = CharToTime((LPUSTR)rgbValue, cbValue,
|
|
&(lpSqlNodeParameter->value.Time));
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
break;
|
|
case SQL_C_SSHORT:
|
|
case SQL_C_USHORT:
|
|
case SQL_C_SLONG:
|
|
case SQL_C_ULONG:
|
|
case SQL_C_FLOAT:
|
|
case SQL_C_DOUBLE:
|
|
case SQL_C_BIT:
|
|
case SQL_C_STINYINT:
|
|
case SQL_C_UTINYINT:
|
|
return ERR_NOTCONVERTABLE;
|
|
case SQL_C_DATE:
|
|
return ERR_NOTCONVERTABLE;
|
|
case SQL_C_TIME:
|
|
lpSqlNodeParameter->value.Time =
|
|
*((TIME_STRUCT far *) rgbValue);
|
|
break;
|
|
case SQL_C_TIMESTAMP:
|
|
lpTimestamp = (TIMESTAMP_STRUCT far *) rgbValue;
|
|
lpSqlNodeParameter->value.Time.hour = lpTimestamp->hour;
|
|
lpSqlNodeParameter->value.Time.minute = lpTimestamp->minute;
|
|
lpSqlNodeParameter->value.Time.second = lpTimestamp->second;
|
|
break;
|
|
case SQL_C_BINARY:
|
|
lpSqlNodeParameter->value.Time =
|
|
*((TIME_STRUCT far *) rgbValue);
|
|
break;
|
|
default:
|
|
return ERR_NOTSUPPORTED;
|
|
}
|
|
break;
|
|
|
|
case TYPE_TIMESTAMP:
|
|
switch (fCType) {
|
|
case SQL_C_CHAR:
|
|
err = CharToTimestamp((LPUSTR)rgbValue, cbValue,
|
|
&(lpSqlNodeParameter->value.Timestamp));
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
break;
|
|
case SQL_C_SSHORT:
|
|
case SQL_C_USHORT:
|
|
case SQL_C_SLONG:
|
|
case SQL_C_ULONG:
|
|
case SQL_C_FLOAT:
|
|
case SQL_C_DOUBLE:
|
|
case SQL_C_BIT:
|
|
case SQL_C_STINYINT:
|
|
case SQL_C_UTINYINT:
|
|
return ERR_NOTCONVERTABLE;
|
|
case SQL_C_DATE:
|
|
lpDate = (DATE_STRUCT far *) rgbValue;
|
|
lpSqlNodeParameter->value.Timestamp.year = lpDate->year;
|
|
lpSqlNodeParameter->value.Timestamp.month = lpDate-> month;
|
|
lpSqlNodeParameter->value.Timestamp.day = lpDate-> day;
|
|
lpSqlNodeParameter->value.Timestamp.hour = 0;
|
|
lpSqlNodeParameter->value.Timestamp.minute = 0;
|
|
lpSqlNodeParameter->value.Timestamp.second = 0;
|
|
lpSqlNodeParameter->value.Timestamp.fraction = 0;
|
|
break;
|
|
case SQL_C_TIME:
|
|
lpTime = (TIME_STRUCT far *) rgbValue;
|
|
|
|
time(&t);
|
|
ts = localtime(&t);
|
|
|
|
lpSqlNodeParameter->value.Timestamp.year = ts->tm_year + 1900;
|
|
lpSqlNodeParameter->value.Timestamp.month = ts->tm_mon + 1;
|
|
lpSqlNodeParameter->value.Timestamp.day = (UWORD) ts->tm_mday;
|
|
lpSqlNodeParameter->value.Timestamp.hour = (WORD) lpTime->hour;
|
|
lpSqlNodeParameter->value.Timestamp.minute = lpTime->minute;
|
|
lpSqlNodeParameter->value.Timestamp.second = lpTime->second;
|
|
lpSqlNodeParameter->value.Timestamp.fraction = 0;
|
|
break;
|
|
case SQL_C_TIMESTAMP:
|
|
lpSqlNodeParameter->value.Timestamp =
|
|
*((TIMESTAMP_STRUCT far *) rgbValue);
|
|
break;
|
|
case SQL_C_BINARY:
|
|
lpSqlNodeParameter->value.Timestamp =
|
|
*((TIMESTAMP_STRUCT far *) rgbValue);
|
|
break;
|
|
default:
|
|
return ERR_NOTSUPPORTED;
|
|
}
|
|
break;
|
|
|
|
case TYPE_BINARY:
|
|
lpSqlNodeParameter->value.Binary = ToString(
|
|
lpstmt->lpSqlStmt, lpSqlNodeParameter->node.parameter.Value);
|
|
switch (fCType) {
|
|
case SQL_C_CHAR:
|
|
|
|
/* Get true length of source string */
|
|
if (cbValue == SQL_NTS)
|
|
cbValue = s_lstrlen((char*)rgbValue);
|
|
else if (cbValue < 0)
|
|
cbValue = 0;
|
|
|
|
/* If am odd numbe of characters, discard the last one */
|
|
if (((cbValue/2) * 2) != cbValue)
|
|
cbValue--;
|
|
|
|
/* Figure out the offset to write at */
|
|
if (pcbOffset != NULL)
|
|
cbOffset = *pcbOffset;
|
|
else
|
|
cbOffset = 0;
|
|
|
|
/* Make sure we don't write past the end of the buffer */
|
|
if (cbValue > ((MAX_BINARY_LITERAL_LENGTH - cbOffset) * 2)) {
|
|
cbValue = (MAX_BINARY_LITERAL_LENGTH - cbOffset) * 2;
|
|
err = ERR_DATATRUNCATED;
|
|
}
|
|
|
|
/* Copy the data */
|
|
lpFrom = (LPUSTR) rgbValue;
|
|
lpTo = BINARY_DATA(lpSqlNodeParameter->value.Binary) +
|
|
cbOffset;
|
|
for (i=0; i < cbValue; i++) {
|
|
|
|
/* Convert the next character to a binary digit */
|
|
if ((*lpFrom >= '0') && (*lpFrom <= '9'))
|
|
nibble = *lpFrom - '0';
|
|
else if ((*lpFrom >= 'A') && (*lpFrom <= 'F'))
|
|
nibble = *lpFrom - 'A';
|
|
else if ((*lpFrom >= 'a') && (*lpFrom <= 'f'))
|
|
nibble = *lpFrom - 'a';
|
|
else
|
|
return ERR_NOTCONVERTABLE;
|
|
|
|
/* Are we writing the left or right side of the byte? */
|
|
if (((i/2) * 2) != i) {
|
|
|
|
/* The left side. Shift the nibble over */
|
|
*lpTo = nibble << 4;
|
|
}
|
|
else {
|
|
|
|
/* The right side. Add the nibble in */
|
|
*lpTo |= (nibble);
|
|
|
|
/* Point at next binary byte */
|
|
lpTo++;
|
|
}
|
|
|
|
/* Look at next character */
|
|
lpFrom++;
|
|
}
|
|
|
|
/* Set the new length */
|
|
if (cbOffset == 0)
|
|
BINARY_LENGTH(lpSqlNodeParameter->value.Binary) =
|
|
cbValue/2;
|
|
else
|
|
BINARY_LENGTH(lpSqlNodeParameter->value.Binary) =
|
|
(cbValue/2)+
|
|
BINARY_LENGTH(lpSqlNodeParameter->value.Binary);
|
|
|
|
/* Return new offset */
|
|
if (pcbOffset != NULL)
|
|
*pcbOffset += (cbValue/2);
|
|
break;
|
|
|
|
case SQL_C_SSHORT:
|
|
_fmemcpy(BINARY_DATA(lpSqlNodeParameter->value.Binary),
|
|
rgbValue, 2);
|
|
BINARY_LENGTH(lpSqlNodeParameter->value.Binary) = 2;
|
|
break;
|
|
case SQL_C_USHORT:
|
|
_fmemcpy(BINARY_DATA(lpSqlNodeParameter->value.Binary),
|
|
rgbValue, 2);
|
|
BINARY_LENGTH(lpSqlNodeParameter->value.Binary) = 2;
|
|
break;
|
|
case SQL_C_SLONG:
|
|
_fmemcpy(BINARY_DATA(lpSqlNodeParameter->value.Binary),
|
|
rgbValue, 4);
|
|
BINARY_LENGTH(lpSqlNodeParameter->value.Binary) = 4;
|
|
break;
|
|
case SQL_C_ULONG:
|
|
_fmemcpy(BINARY_DATA(lpSqlNodeParameter->value.Binary),
|
|
rgbValue, 4);
|
|
BINARY_LENGTH(lpSqlNodeParameter->value.Binary) = 4;
|
|
break;
|
|
case SQL_C_FLOAT:
|
|
_fmemcpy(BINARY_DATA(lpSqlNodeParameter->value.Binary),
|
|
rgbValue, 4);
|
|
BINARY_LENGTH(lpSqlNodeParameter->value.Binary) = 4;
|
|
break;
|
|
case SQL_C_DOUBLE:
|
|
_fmemcpy(BINARY_DATA(lpSqlNodeParameter->value.Binary),
|
|
rgbValue, 8);
|
|
BINARY_LENGTH(lpSqlNodeParameter->value.Binary) = 8;
|
|
break;
|
|
case SQL_C_BIT:
|
|
_fmemcpy(BINARY_DATA(lpSqlNodeParameter->value.Binary),
|
|
rgbValue, 1);
|
|
BINARY_LENGTH(lpSqlNodeParameter->value.Binary) = 1;
|
|
break;
|
|
case SQL_C_STINYINT:
|
|
_fmemcpy(BINARY_DATA(lpSqlNodeParameter->value.Binary),
|
|
rgbValue, 1);
|
|
BINARY_LENGTH(lpSqlNodeParameter->value.Binary) = 1;
|
|
break;
|
|
case SQL_C_UTINYINT:
|
|
_fmemcpy(BINARY_DATA(lpSqlNodeParameter->value.Binary),
|
|
rgbValue, 1);
|
|
BINARY_LENGTH(lpSqlNodeParameter->value.Binary) = 1;
|
|
break;
|
|
case SQL_C_DATE:
|
|
_fmemcpy(BINARY_DATA(lpSqlNodeParameter->value.Binary),
|
|
rgbValue, 6);
|
|
BINARY_LENGTH(lpSqlNodeParameter->value.Binary) = 6;
|
|
break;
|
|
case SQL_C_TIME:
|
|
_fmemcpy(BINARY_DATA(lpSqlNodeParameter->value.Binary),
|
|
rgbValue, 6);
|
|
BINARY_LENGTH(lpSqlNodeParameter->value.Binary) = 6;
|
|
break;
|
|
case SQL_C_TIMESTAMP:
|
|
_fmemcpy(BINARY_DATA(lpSqlNodeParameter->value.Binary),
|
|
rgbValue, 16);
|
|
BINARY_LENGTH(lpSqlNodeParameter->value.Binary) = 16;
|
|
break;
|
|
case SQL_C_BINARY:
|
|
|
|
/* Get true lengthof source string */
|
|
if (cbValue < 0)
|
|
cbValue = 0;
|
|
|
|
/* Figure out the offset to write at */
|
|
if (pcbOffset != NULL)
|
|
cbOffset = *pcbOffset;
|
|
else
|
|
cbOffset = 0;
|
|
|
|
/* Make sure we don't write past the end of the buffer */
|
|
if (cbValue > (MAX_BINARY_LITERAL_LENGTH - cbOffset)) {
|
|
cbValue = MAX_BINARY_LITERAL_LENGTH - cbOffset;
|
|
err = ERR_DATATRUNCATED;
|
|
}
|
|
|
|
/* Copy the data */
|
|
_fmemcpy(BINARY_DATA(lpSqlNodeParameter->value.Binary) +
|
|
cbOffset, rgbValue, (SWORD) cbValue);
|
|
if (cbOffset == 0)
|
|
BINARY_LENGTH(lpSqlNodeParameter->value.Binary) = cbValue;
|
|
else
|
|
BINARY_LENGTH(lpSqlNodeParameter->value.Binary) = cbValue+
|
|
BINARY_LENGTH(lpSqlNodeParameter->value.Binary);
|
|
if (pcbOffset != NULL)
|
|
*pcbOffset += (cbValue);
|
|
break;
|
|
|
|
default:
|
|
return ERR_NOTSUPPORTED;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return ERR_NOTSUPPORTED;
|
|
}
|
|
}
|
|
return err;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC Rewind(LPSTMT lpstmt, LPSQLNODE lpSqlNode,
|
|
BOOL fOuterJoinIsNull)
|
|
|
|
/* Rewinds the table or table list identified by lpSqlNode */
|
|
|
|
{
|
|
#define MAX_RESTRICTIONS 10
|
|
LPSQLTREE lpSql;
|
|
SWORD err;
|
|
LPSQLNODE lpSqlNodeComparison;
|
|
LPSQLNODE lpSqlNodeColumn;
|
|
LPSQLNODE lpSqlNodeValue;
|
|
LPSQLNODE lpSqlNodeTable;
|
|
LPSQLNODE lpSqlNodeTables;
|
|
SQLNODEIDX idxRestrict;
|
|
UWORD cRestrict;
|
|
UWORD fOperator[MAX_RESTRICTIONS];
|
|
SWORD fCType[MAX_RESTRICTIONS];
|
|
PTR rgbValue[MAX_RESTRICTIONS];
|
|
SDWORD cbValue[MAX_RESTRICTIONS];
|
|
UWORD icol[MAX_RESTRICTIONS];
|
|
|
|
lpSql = lpstmt->lpSqlStmt;
|
|
switch (lpSqlNode->sqlNodeType) {
|
|
case NODE_TYPE_TABLES:
|
|
|
|
/* Rewind this table */
|
|
lpSqlNodeTable = ToNode(lpSql, lpSqlNode->node.tables.Table);
|
|
err = Rewind(lpstmt, lpSqlNodeTable, fOuterJoinIsNull);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Are there other tables on the list? */
|
|
if (lpSqlNode->node.tables.Next != NO_SQLNODE) {
|
|
|
|
/* Yes. Find next record */
|
|
while (TRUE) {
|
|
|
|
/* Is this the right side of a NULL outer join? */
|
|
if ((lpSqlNodeTable->node.table.OuterJoinPredicate !=
|
|
NO_SQLNODE) && fOuterJoinIsNull)
|
|
|
|
/* Yes. This table has NULLs also */
|
|
err = ISAM_EOF;
|
|
|
|
else {
|
|
|
|
/* No. Position the current table to the next record */
|
|
err = ISAMNextRecord(lpSqlNodeTable->node.table.Handle, lpstmt);
|
|
if ((err == NO_ISAM_ERR) || (err == ISAM_EOF))
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
}
|
|
|
|
/* End of file? */
|
|
if (err == ISAM_EOF) {
|
|
|
|
/* Yes. Is this table the right side of an outer join */
|
|
if (lpSqlNodeTable->node.table.OuterJoinPredicate ==
|
|
NO_SQLNODE) {
|
|
|
|
/* No. No more records */
|
|
return ERR_NODATAFOUND;
|
|
}
|
|
else {
|
|
|
|
/* Yes. Use a record of all nulls */
|
|
err = ERR_SUCCESS;
|
|
lpSqlNodeTable->node.table.AllNull = TRUE;
|
|
fOuterJoinIsNull = TRUE;
|
|
}
|
|
}
|
|
else if (err != NO_ISAM_ERR) {
|
|
ISAMGetErrorMessage(lpstmt->lpdbc->lpISAM,
|
|
(LPUSTR)lpstmt->szISAMError);
|
|
return err;
|
|
}
|
|
|
|
/* Rewind the other tables */
|
|
lpSqlNodeTables = ToNode(lpSql, lpSqlNode->node.tables.Next);
|
|
err = Rewind(lpstmt, lpSqlNodeTables, fOuterJoinIsNull);
|
|
if (err == ERR_SUCCESS)
|
|
break;
|
|
if (err != ERR_NODATAFOUND)
|
|
return err;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case NODE_TYPE_TABLE:
|
|
|
|
/* Did the optimizer find any restrictions? */
|
|
if (lpSqlNode->node.table.Restrict != 0) {
|
|
|
|
/* Yes. For each restriction found... */
|
|
cRestrict = 0;
|
|
idxRestrict = lpSqlNode->node.table.Restrict;
|
|
while (idxRestrict != NO_SQLNODE) {
|
|
|
|
/* Get the restriction */
|
|
lpSqlNodeComparison = ToNode(lpSql, idxRestrict);
|
|
|
|
|
|
/* Get the column */
|
|
lpSqlNodeColumn = ToNode(lpSql, lpSqlNodeComparison->node.comparison.Left);
|
|
icol[cRestrict] = lpSqlNodeColumn->node.column.Id;
|
|
|
|
/* Get the operator */
|
|
switch (lpSqlNodeComparison->node.comparison.Operator) {
|
|
case OP_EQ:
|
|
fOperator[cRestrict] = ISAM_OP_EQ;
|
|
break;
|
|
case OP_NE:
|
|
fOperator[cRestrict] = ISAM_OP_NE;
|
|
break;
|
|
case OP_LE:
|
|
fOperator[cRestrict] = ISAM_OP_LE;
|
|
break;
|
|
case OP_LT:
|
|
fOperator[cRestrict] = ISAM_OP_LT;
|
|
break;
|
|
case OP_GE:
|
|
fOperator[cRestrict] = ISAM_OP_GE;
|
|
break;
|
|
case OP_GT:
|
|
fOperator[cRestrict] = ISAM_OP_GT;
|
|
break;
|
|
default:
|
|
return ERR_INTERNAL;
|
|
}
|
|
|
|
/* Calculate the key value */
|
|
lpSqlNodeValue = ToNode(lpSql,
|
|
lpSqlNodeComparison->node.comparison.Right);
|
|
err = EvaluateExpression(lpstmt, lpSqlNodeValue);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Put restriction on the list */
|
|
switch (lpSqlNodeValue->sqlDataType) {
|
|
case TYPE_DOUBLE:
|
|
case TYPE_INTEGER:
|
|
fCType[cRestrict] = SQL_C_DOUBLE;
|
|
rgbValue[cRestrict] = &(lpSqlNodeValue->value.Double);
|
|
cbValue[cRestrict] = lpSqlNodeValue->sqlIsNull ?
|
|
SQL_NULL_DATA : sizeof(double);
|
|
break;
|
|
case TYPE_NUMERIC:
|
|
fCType[cRestrict] = SQL_C_CHAR;
|
|
rgbValue[cRestrict] = lpSqlNodeValue->value.String;
|
|
cbValue[cRestrict] = lpSqlNodeValue->sqlIsNull ? SQL_NULL_DATA :
|
|
s_lstrlen(lpSqlNodeValue->value.String);
|
|
break;
|
|
case TYPE_CHAR:
|
|
fCType[cRestrict] = SQL_C_CHAR;
|
|
rgbValue[cRestrict] = lpSqlNodeValue->value.String;
|
|
cbValue[cRestrict] = lpSqlNodeValue->sqlIsNull ? SQL_NULL_DATA :
|
|
s_lstrlen(lpSqlNodeValue->value.String);
|
|
break;
|
|
case TYPE_DATE:
|
|
fCType[cRestrict] = SQL_C_DATE;
|
|
rgbValue[cRestrict] = &(lpSqlNodeValue->value.Date),
|
|
cbValue[cRestrict] = lpSqlNodeValue->sqlIsNull ?
|
|
(SDWORD) SQL_NULL_DATA : sizeof(DATE_STRUCT);
|
|
break;
|
|
case TYPE_TIME:
|
|
fCType[cRestrict] = SQL_C_TIME;
|
|
rgbValue[cRestrict] = &(lpSqlNodeValue->value.Time);
|
|
cbValue[cRestrict] = lpSqlNodeValue->sqlIsNull ?
|
|
(SDWORD) SQL_NULL_DATA : sizeof(TIME_STRUCT);
|
|
break;
|
|
case TYPE_TIMESTAMP:
|
|
fCType[cRestrict] = SQL_C_TIMESTAMP;
|
|
rgbValue[cRestrict] = &(lpSqlNodeValue->value.Timestamp);
|
|
cbValue[cRestrict] = lpSqlNodeValue->sqlIsNull ?
|
|
(SDWORD) SQL_NULL_DATA : sizeof(TIMESTAMP_STRUCT);
|
|
break;
|
|
case TYPE_BINARY:
|
|
fCType[cRestrict] = SQL_C_BINARY;
|
|
rgbValue[cRestrict] = BINARY_DATA(lpSqlNodeValue->value.Binary);
|
|
cbValue[cRestrict] = lpSqlNodeValue->sqlIsNull ? SQL_NULL_DATA :
|
|
BINARY_LENGTH(lpSqlNodeValue->value.Binary);
|
|
break;
|
|
default:
|
|
return ERR_NOTSUPPORTED;
|
|
}
|
|
|
|
/* Increase count */
|
|
cRestrict++;
|
|
|
|
/* Leave loop if maximum number of restrictions found */
|
|
if (cRestrict >= MAX_RESTRICTIONS)
|
|
break;
|
|
|
|
/* Point at next restriction */
|
|
idxRestrict = lpSqlNodeComparison->node.comparison.NextRestrict;
|
|
}
|
|
|
|
/* Apply the restriction */
|
|
err = ISAMRestrict(lpSqlNode->node.table.Handle, cRestrict,
|
|
icol, fOperator, fCType, rgbValue, cbValue);
|
|
if ((err != NO_ISAM_ERR) && (err != ISAM_NOTSUPPORTED)) {
|
|
ISAMGetErrorMessage(lpstmt->lpdbc->lpISAM,
|
|
(LPUSTR)lpstmt->szISAMError);
|
|
return err;
|
|
}
|
|
else if (err == NO_ISAM_ERR)
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
}
|
|
|
|
/* Rewind the table */
|
|
err = ISAMRewind(lpSqlNode->node.table.Handle);
|
|
if (err != NO_ISAM_ERR) {
|
|
ISAMGetErrorMessage(lpstmt->lpdbc->lpISAM, (LPUSTR)lpstmt->szISAMError);
|
|
return err;
|
|
}
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
lpSqlNode->node.table.Rewound = TRUE;
|
|
lpSqlNode->node.table.AllNull = FALSE;
|
|
|
|
break;
|
|
|
|
default:
|
|
return ERR_INTERNAL;
|
|
}
|
|
return ERR_SUCCESS;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC NextRawRecord (LPSTMT lpstmt,
|
|
LPSQLNODE lpSqlNode)
|
|
|
|
/* Finds the next record in in the table or table list. */
|
|
/* */
|
|
/* If a list of tables is given, the leftmost table spins the slowest */
|
|
/* and the rightmost table spins the fastest. In otherwords, for each */
|
|
/* row in a table, we iterate over the rows in the tables after that table */
|
|
|
|
{
|
|
LPSQLTREE lpSql;
|
|
SWORD err;
|
|
LPSQLNODE lpSqlNodeTable;
|
|
LPSQLNODE lpSqlNodeTables;
|
|
LPSQLNODE lpSqlNodeOuterJoinPredicate;
|
|
|
|
lpSql = lpstmt->lpSqlStmt;
|
|
switch (lpSqlNode->sqlNodeType) {
|
|
case NODE_TYPE_TABLES:
|
|
|
|
/* Are there any more tables on the list? */
|
|
lpSqlNodeTable = ToNode(lpSql, lpSqlNode->node.tables.Table);
|
|
if (lpSqlNode->node.tables.Next != NO_SQLNODE) {
|
|
|
|
/* Yes. Look for next record in the rest of the list */
|
|
while (TRUE) {
|
|
|
|
/* Move to the next record on rest of the table list */
|
|
lpSqlNodeTables = ToNode(lpSql, lpSqlNode->node.tables.Next);
|
|
err = NextRawRecord(lpstmt, lpSqlNodeTables);
|
|
if ((err != ERR_SUCCESS) && (err != ERR_NODATAFOUND))
|
|
return err;
|
|
|
|
/* If a no more records, continue below */
|
|
if (err == ERR_NODATAFOUND)
|
|
break;
|
|
|
|
/* Is this table on right side of an outer join? */
|
|
if (lpSqlNodeTable->node.table.OuterJoinPredicate !=
|
|
NO_SQLNODE) {
|
|
|
|
/* Yes. Are we on the row of NULLs? */
|
|
if (!lpSqlNodeTable->node.table.AllNull) {
|
|
|
|
/* No. Test ON predicate */
|
|
lpSqlNodeOuterJoinPredicate = ToNode(lpSql,
|
|
lpSqlNodeTable->node.table.OuterJoinPredicate);
|
|
|
|
/* Test outer join criteria */
|
|
err = EvaluateExpression(lpstmt,
|
|
lpSqlNodeOuterJoinPredicate);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* If record does not pass outer join criteria, */
|
|
/* try next one */
|
|
if ((lpSqlNodeOuterJoinPredicate->sqlIsNull) ||
|
|
(lpSqlNodeOuterJoinPredicate->value.Double !=
|
|
TRUE)) {
|
|
continue;
|
|
}
|
|
|
|
/* If record passes outer join criteria, use it */
|
|
else {
|
|
lpSqlNodeTable->node.table.Rewound = FALSE;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* If a record was found, return it */
|
|
if (err == ERR_SUCCESS)
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
/* Loop until a record is found */
|
|
while (TRUE) {
|
|
|
|
/* Position to the next record in this table. */
|
|
err = NextRawRecord(lpstmt, lpSqlNodeTable);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* More tables on the list? */
|
|
if (lpSqlNode->node.tables.Next != NO_SQLNODE) {
|
|
|
|
/* Yes. Rewind the other tables on the list */
|
|
lpSqlNodeTables = ToNode(lpSql, lpSqlNode->node.tables.Next);
|
|
err = Rewind(lpstmt, lpSqlNodeTables,
|
|
lpSqlNodeTable->node.table.AllNull);
|
|
if (err == ERR_NODATAFOUND)
|
|
continue;
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Get the first record from the other tables on the list */
|
|
err = NextRawRecord(lpstmt, lpSqlNodeTables);
|
|
if (err == ERR_NODATAFOUND)
|
|
continue;
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
|
|
/* Is this table on right side of an outer join? */
|
|
if (lpSqlNodeTable->node.table.OuterJoinPredicate !=
|
|
NO_SQLNODE) {
|
|
|
|
/* Yes. Are we on the row of NULLs? */
|
|
if (!lpSqlNodeTable->node.table.AllNull) {
|
|
|
|
/* No. Test ON predicate */
|
|
lpSqlNodeOuterJoinPredicate = ToNode(lpSql,
|
|
lpSqlNodeTable->node.table.OuterJoinPredicate);
|
|
|
|
/* Test outer join criteria */
|
|
err = EvaluateExpression(lpstmt,
|
|
lpSqlNodeOuterJoinPredicate);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* If record does not pass outer join criteria, */
|
|
/* try next one */
|
|
if ((lpSqlNodeOuterJoinPredicate->sqlIsNull) ||
|
|
(lpSqlNodeOuterJoinPredicate->value.Double != TRUE)) {
|
|
continue;
|
|
}
|
|
|
|
/* If record passes outer join criteria, use it */
|
|
else {
|
|
lpSqlNodeTable->node.table.Rewound = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case NODE_TYPE_TABLE:
|
|
|
|
/* If currently on a record of all NULLs, error */
|
|
if ((lpSqlNode->node.table.OuterJoinPredicate != NO_SQLNODE) &&
|
|
lpSqlNode->node.table.AllNull)
|
|
return ERR_NODATAFOUND;
|
|
|
|
/* Are we already on a record of all nulls? */
|
|
if (lpSqlNode->node.table.AllNull)
|
|
|
|
/* Yes. Stay there */
|
|
err = ISAM_EOF;
|
|
else {
|
|
|
|
/* No. Get the next record from the ISAM */
|
|
err = ISAMNextRecord(lpSqlNode->node.table.Handle, lpstmt);
|
|
if ((err == NO_ISAM_ERR) || (err == ISAM_EOF))
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
}
|
|
|
|
if (err == ISAM_EOF) {
|
|
|
|
/* End of table. If not an outer join, error */
|
|
if (lpSqlNode->node.table.OuterJoinPredicate == NO_SQLNODE)
|
|
return ERR_NODATAFOUND;
|
|
|
|
/* If this is not first read, error */
|
|
if (!(lpSqlNode->node.table.Rewound))
|
|
return ERR_NODATAFOUND;
|
|
|
|
/* Otherwise, use a record of all NULLs */
|
|
lpSqlNode->node.table.AllNull = TRUE;
|
|
}
|
|
else if (err != NO_ISAM_ERR) {
|
|
ISAMGetErrorMessage(lpstmt->lpdbc->lpISAM, (LPUSTR)lpstmt->szISAMError);
|
|
return err;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
return ERR_INTERNAL;
|
|
}
|
|
return ERR_SUCCESS;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC NextRecord (LPSTMT lpstmt,
|
|
LPSQLNODE lpSqlNode,
|
|
LPSQLNODE lpSqlNodePredicate)
|
|
|
|
/* Finds the next record in in the table or table list identified by */
|
|
/* lpSqlNode that satisfies the predicate lpSqlNodePredicte. */
|
|
/* */
|
|
/* If a list of tables is given, the leftmost table spins the slowest */
|
|
/* and the rightmost table spins the fastest. In otherwords, for each */
|
|
/* row in a table, we iterate over the rows in the tables after that table */
|
|
|
|
{
|
|
SWORD err;
|
|
|
|
/* Loop until a record is found */
|
|
while (TRUE) {
|
|
|
|
/* Go to next record */
|
|
err = NextRawRecord(lpstmt, lpSqlNode);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* If no predicate, this record qualifies. */
|
|
if (lpSqlNodePredicate == NULL)
|
|
break;
|
|
|
|
/* If the predicate is TRUE, return this record */
|
|
err = EvaluateExpression(lpstmt, lpSqlNodePredicate);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
if (!(lpSqlNodePredicate->sqlIsNull) &&
|
|
(lpSqlNodePredicate->value.Double == TRUE))
|
|
break;
|
|
}
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
int INTFUNC lstrcmp_pad(LPUSTR lpszLeft, LPUSTR lpszRight)
|
|
|
|
/* Compares two string (blank padding the shorter one to the same length */
|
|
/* as the longer one). */
|
|
{
|
|
int cbLeft;
|
|
int cbRight;
|
|
UCHAR chr;
|
|
int result;
|
|
|
|
/* Get the length of the two strings */
|
|
cbLeft = s_lstrlen(lpszLeft);
|
|
cbRight = s_lstrlen(lpszRight);
|
|
|
|
/* If the strings are the same length, use plain old lstrcmp */
|
|
if (cbLeft == cbRight)
|
|
result = s_lstrcmp(lpszLeft, lpszRight);
|
|
|
|
/* If the left one is shorter, swap the strings and try again */
|
|
else if (cbLeft < cbRight)
|
|
result = -(lstrcmp_pad(lpszRight, lpszLeft));
|
|
|
|
/* Otherwise the right one is shorter... */
|
|
else {
|
|
|
|
/* Truncate the left one to the size of the right one */
|
|
chr = lpszLeft[cbRight];
|
|
lpszLeft[cbRight] = '\0';
|
|
|
|
/* Are the strings equal? */
|
|
result = s_lstrcmp(lpszLeft, lpszRight);
|
|
lpszLeft[cbRight] = chr; /* Untruncate the left one */
|
|
if (result == 0) {
|
|
|
|
/* Yes. Compare remaining characters on the left string to blanks */
|
|
while (cbRight < cbLeft) {
|
|
result = lpszLeft[cbRight] - ' ';
|
|
if (result != 0)
|
|
break;
|
|
cbRight++;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
RETCODE INTFUNC ValueCompare(LPSQLNODE lpSqlNode, LPSQLNODE lpSqlNodeLeft,
|
|
UWORD Operator, LPSQLNODE lpSqlNodeRight)
|
|
|
|
/* Compares two values */
|
|
|
|
{
|
|
RETCODE err;
|
|
LONG idx;
|
|
|
|
/* Compare values */
|
|
err = ERR_SUCCESS;
|
|
lpSqlNode->sqlIsNull = FALSE;
|
|
switch (lpSqlNodeLeft->sqlDataType) {
|
|
case TYPE_DOUBLE:
|
|
case TYPE_INTEGER:
|
|
case TYPE_NUMERIC:
|
|
err = NumericCompare(lpSqlNode, lpSqlNodeLeft, Operator,
|
|
lpSqlNodeRight);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
break;
|
|
|
|
case TYPE_CHAR:
|
|
switch (Operator) {
|
|
case OP_EQ:
|
|
if (lstrcmp_pad(lpSqlNodeLeft->value.String,
|
|
lpSqlNodeRight->value.String) == 0)
|
|
lpSqlNode->value.Double = TRUE;
|
|
else
|
|
lpSqlNode->value.Double = FALSE;
|
|
break;
|
|
case OP_NE:
|
|
if (lstrcmp_pad(lpSqlNodeLeft->value.String,
|
|
lpSqlNodeRight->value.String) != 0)
|
|
lpSqlNode->value.Double = TRUE;
|
|
else
|
|
lpSqlNode->value.Double = FALSE;
|
|
break;
|
|
case OP_LE:
|
|
if (lstrcmp_pad(lpSqlNodeLeft->value.String,
|
|
lpSqlNodeRight->value.String) <= 0)
|
|
lpSqlNode->value.Double = TRUE;
|
|
else
|
|
lpSqlNode->value.Double = FALSE;
|
|
break;
|
|
case OP_LT:
|
|
if (lstrcmp_pad(lpSqlNodeLeft->value.String,
|
|
lpSqlNodeRight->value.String) < 0)
|
|
lpSqlNode->value.Double = TRUE;
|
|
else
|
|
lpSqlNode->value.Double = FALSE;
|
|
break;
|
|
case OP_GE:
|
|
if (lstrcmp_pad(lpSqlNodeLeft->value.String,
|
|
lpSqlNodeRight->value.String) >= 0)
|
|
lpSqlNode->value.Double = TRUE;
|
|
else
|
|
lpSqlNode->value.Double = FALSE;
|
|
break;
|
|
case OP_GT:
|
|
if (lstrcmp_pad(lpSqlNodeLeft->value.String,
|
|
lpSqlNodeRight->value.String) > 0)
|
|
lpSqlNode->value.Double = TRUE;
|
|
else
|
|
lpSqlNode->value.Double = FALSE;
|
|
break;
|
|
case OP_LIKE:
|
|
lpSqlNode->value.Double =
|
|
PatternMatch(FALSE, lpSqlNodeLeft->value.String,
|
|
s_lstrlen(lpSqlNodeLeft->value.String),
|
|
lpSqlNodeRight->value.String,
|
|
s_lstrlen(lpSqlNodeRight->value.String),
|
|
TRUE);
|
|
break;
|
|
case OP_NOTLIKE:
|
|
lpSqlNode->value.Double =
|
|
!(PatternMatch(FALSE, lpSqlNodeLeft->value.String,
|
|
s_lstrlen(lpSqlNodeLeft->value.String),
|
|
lpSqlNodeRight->value.String,
|
|
s_lstrlen(lpSqlNodeRight->value.String),
|
|
TRUE));
|
|
break;
|
|
default:
|
|
err = ERR_INTERNAL;
|
|
break;
|
|
}
|
|
break;
|
|
case TYPE_BINARY:
|
|
switch (Operator) {
|
|
case OP_EQ:
|
|
if (BINARY_LENGTH(lpSqlNodeLeft->value.Binary) ==
|
|
BINARY_LENGTH(lpSqlNodeRight->value.Binary)) {
|
|
lpSqlNode->value.Double = TRUE;
|
|
for (idx = 0;
|
|
idx < BINARY_LENGTH(lpSqlNodeRight->value.Binary);
|
|
idx++) {
|
|
if (BINARY_DATA(lpSqlNodeLeft->value.Binary)[idx] !=
|
|
BINARY_DATA(lpSqlNodeRight->value.Binary)[idx]) {
|
|
lpSqlNode->value.Double = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
lpSqlNode->value.Double = FALSE;
|
|
break;
|
|
case OP_NE:
|
|
if (BINARY_LENGTH(lpSqlNodeLeft->value.Binary) ==
|
|
BINARY_LENGTH(lpSqlNodeRight->value.Binary)) {
|
|
lpSqlNode->value.Double = FALSE;
|
|
for (idx = 0;
|
|
idx < BINARY_LENGTH(lpSqlNodeRight->value.Binary);
|
|
idx++) {
|
|
if (BINARY_DATA(lpSqlNodeLeft->value.Binary)[idx] !=
|
|
BINARY_DATA(lpSqlNodeRight->value.Binary)[idx]) {
|
|
lpSqlNode->value.Double = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
lpSqlNode->value.Double = TRUE;
|
|
break;
|
|
case OP_LE:
|
|
case OP_LT:
|
|
case OP_GE:
|
|
case OP_GT:
|
|
case OP_LIKE:
|
|
case OP_NOTLIKE:
|
|
default:
|
|
err = ERR_INTERNAL;
|
|
break;
|
|
}
|
|
break;
|
|
case TYPE_DATE:
|
|
switch (Operator) {
|
|
case OP_EQ:
|
|
if (DateCompare(lpSqlNodeLeft->value.Date,
|
|
lpSqlNodeRight->value.Date) == 0)
|
|
lpSqlNode->value.Double = TRUE;
|
|
else
|
|
lpSqlNode->value.Double = FALSE;
|
|
break;
|
|
case OP_NE:
|
|
if (DateCompare(lpSqlNodeLeft->value.Date,
|
|
lpSqlNodeRight->value.Date) != 0)
|
|
lpSqlNode->value.Double = TRUE;
|
|
else
|
|
lpSqlNode->value.Double = FALSE;
|
|
break;
|
|
case OP_LE:
|
|
if (DateCompare(lpSqlNodeLeft->value.Date,
|
|
lpSqlNodeRight->value.Date) <= 0)
|
|
lpSqlNode->value.Double = TRUE;
|
|
else
|
|
lpSqlNode->value.Double = FALSE;
|
|
break;
|
|
case OP_LT:
|
|
if (DateCompare(lpSqlNodeLeft->value.Date,
|
|
lpSqlNodeRight->value.Date) < 0)
|
|
lpSqlNode->value.Double = TRUE;
|
|
else
|
|
lpSqlNode->value.Double = FALSE;
|
|
break;
|
|
case OP_GE:
|
|
if (DateCompare(lpSqlNodeLeft->value.Date,
|
|
lpSqlNodeRight->value.Date) >= 0)
|
|
lpSqlNode->value.Double = TRUE;
|
|
else
|
|
lpSqlNode->value.Double = FALSE;
|
|
break;
|
|
case OP_GT:
|
|
if (DateCompare(lpSqlNodeLeft->value.Date,
|
|
lpSqlNodeRight->value.Date) > 0)
|
|
lpSqlNode->value.Double = TRUE;
|
|
else
|
|
lpSqlNode->value.Double = FALSE;
|
|
break;
|
|
case OP_LIKE:
|
|
case OP_NOTLIKE:
|
|
default:
|
|
err = ERR_INTERNAL;
|
|
break;
|
|
}
|
|
break;
|
|
case TYPE_TIME:
|
|
switch (Operator) {
|
|
case OP_EQ:
|
|
if (TimeCompare(lpSqlNodeLeft->value.Time,
|
|
lpSqlNodeRight->value.Time) == 0)
|
|
lpSqlNode->value.Double = TRUE;
|
|
else
|
|
lpSqlNode->value.Double = FALSE;
|
|
break;
|
|
case OP_NE:
|
|
if (TimeCompare(lpSqlNodeLeft->value.Time,
|
|
lpSqlNodeRight->value.Time) != 0)
|
|
lpSqlNode->value.Double = TRUE;
|
|
else
|
|
lpSqlNode->value.Double = FALSE;
|
|
break;
|
|
case OP_LE:
|
|
if (TimeCompare(lpSqlNodeLeft->value.Time,
|
|
lpSqlNodeRight->value.Time) <= 0)
|
|
lpSqlNode->value.Double = TRUE;
|
|
else
|
|
lpSqlNode->value.Double = FALSE;
|
|
break;
|
|
case OP_LT:
|
|
if (TimeCompare(lpSqlNodeLeft->value.Time,
|
|
lpSqlNodeRight->value.Time) < 0)
|
|
lpSqlNode->value.Double = TRUE;
|
|
else
|
|
lpSqlNode->value.Double = FALSE;
|
|
break;
|
|
case OP_GE:
|
|
if (TimeCompare(lpSqlNodeLeft->value.Time,
|
|
lpSqlNodeRight->value.Time) >= 0)
|
|
lpSqlNode->value.Double = TRUE;
|
|
else
|
|
lpSqlNode->value.Double = FALSE;
|
|
break;
|
|
case OP_GT:
|
|
if (TimeCompare(lpSqlNodeLeft->value.Time,
|
|
lpSqlNodeRight->value.Time) > 0)
|
|
lpSqlNode->value.Double = TRUE;
|
|
else
|
|
lpSqlNode->value.Double = FALSE;
|
|
break;
|
|
case OP_LIKE:
|
|
case OP_NOTLIKE:
|
|
default:
|
|
err = ERR_INTERNAL;
|
|
break;
|
|
}
|
|
break;
|
|
case TYPE_TIMESTAMP:
|
|
switch (Operator) {
|
|
case OP_EQ:
|
|
if (TimestampCompare(lpSqlNodeLeft->value.Timestamp,
|
|
lpSqlNodeRight->value.Timestamp) == 0)
|
|
lpSqlNode->value.Double = TRUE;
|
|
else
|
|
lpSqlNode->value.Double = FALSE;
|
|
break;
|
|
case OP_NE:
|
|
if (TimestampCompare(lpSqlNodeLeft->value.Timestamp,
|
|
lpSqlNodeRight->value.Timestamp) != 0)
|
|
lpSqlNode->value.Double = TRUE;
|
|
else
|
|
lpSqlNode->value.Double = FALSE;
|
|
break;
|
|
case OP_LE:
|
|
if (TimestampCompare(lpSqlNodeLeft->value.Timestamp,
|
|
lpSqlNodeRight->value.Timestamp) <= 0)
|
|
lpSqlNode->value.Double = TRUE;
|
|
else
|
|
lpSqlNode->value.Double = FALSE;
|
|
break;
|
|
case OP_LT:
|
|
if (TimestampCompare(lpSqlNodeLeft->value.Timestamp,
|
|
lpSqlNodeRight->value.Timestamp) < 0)
|
|
lpSqlNode->value.Double = TRUE;
|
|
else
|
|
lpSqlNode->value.Double = FALSE;
|
|
break;
|
|
case OP_GE:
|
|
if (TimestampCompare(lpSqlNodeLeft->value.Timestamp,
|
|
lpSqlNodeRight->value.Timestamp) >= 0)
|
|
lpSqlNode->value.Double = TRUE;
|
|
else
|
|
lpSqlNode->value.Double = FALSE;
|
|
break;
|
|
case OP_GT:
|
|
if (TimestampCompare(lpSqlNodeLeft->value.Timestamp,
|
|
lpSqlNodeRight->value.Timestamp) > 0)
|
|
lpSqlNode->value.Double = TRUE;
|
|
else
|
|
lpSqlNode->value.Double = FALSE;
|
|
break;
|
|
case OP_LIKE:
|
|
case OP_NOTLIKE:
|
|
default:
|
|
err = ERR_INTERNAL;
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
err = ERR_NOTSUPPORTED;
|
|
break;
|
|
}
|
|
return err;
|
|
}
|
|
/***************************************************************************/
|
|
RETCODE INTFUNC RetrieveSortRecordValue(LPSQLNODE lpSqlNodeStmt, LPSQLNODE lpSqlNode,
|
|
UDWORD Offset, UDWORD Length)
|
|
|
|
/* Retrieves a value from a sort record */
|
|
|
|
{
|
|
UCHAR cNullFlag;
|
|
UCHAR szBuffer[30];
|
|
UWORD len;
|
|
UDWORD SortRecordsize;
|
|
|
|
szBuffer[0] = 0;
|
|
/* Position to the value */
|
|
if (lpSqlNodeStmt->sqlNodeType != NODE_TYPE_SELECT)
|
|
return ERR_INTERNAL;
|
|
if (!(lpSqlNodeStmt->node.select.ReturningDistinct))
|
|
SortRecordsize = lpSqlNodeStmt->node.select.SortRecordsize;
|
|
else
|
|
SortRecordsize = lpSqlNodeStmt->node.select.DistinctRecordsize;
|
|
|
|
if (_llseek(lpSqlNodeStmt->node.select.Sortfile,
|
|
(lpSqlNodeStmt->node.select.CurrentRow * SortRecordsize)
|
|
+ Offset - 1, 0) == HFILE_ERROR)
|
|
return ERR_SORT;
|
|
|
|
/* Read value */
|
|
switch (lpSqlNode->sqlDataType) {
|
|
case TYPE_DOUBLE:
|
|
case TYPE_INTEGER:
|
|
if (_lread(lpSqlNodeStmt->node.select.Sortfile,
|
|
&(lpSqlNode->value.Double),
|
|
sizeof(double)) != sizeof(double))
|
|
return ERR_SORT;
|
|
break;
|
|
|
|
case TYPE_NUMERIC:
|
|
if (_lread(lpSqlNodeStmt->node.select.Sortfile,
|
|
lpSqlNode->value.String,
|
|
(UINT) Length) != (UINT) Length)
|
|
return ERR_SORT;
|
|
lpSqlNode->value.String[Length]='\0';
|
|
|
|
/* Remove trailing blanks */
|
|
len = (UWORD) s_lstrlen(lpSqlNode->value.String);
|
|
while ((len > 0) && (lpSqlNode->value.String[len-1] == ' ')) {
|
|
lpSqlNode->value.String[len-1] = '\0';
|
|
len--;
|
|
}
|
|
break;
|
|
|
|
case TYPE_CHAR:
|
|
if (_lread(lpSqlNodeStmt->node.select.Sortfile,
|
|
lpSqlNode->value.String,
|
|
(UINT) Length) != (UINT) Length)
|
|
return ERR_SORT;
|
|
lpSqlNode->value.String[Length]='\0';
|
|
|
|
/* Remove trailing blanks if need be */
|
|
if ((lpSqlNode->sqlSqlType == SQL_VARCHAR) ||
|
|
(lpSqlNode->sqlSqlType == SQL_LONGVARCHAR)) {
|
|
len = (UWORD) s_lstrlen(lpSqlNode->value.String);
|
|
while ((len > 0) && (lpSqlNode->value.String[len-1] == ' ')) {
|
|
lpSqlNode->value.String[len-1] = '\0';
|
|
len--;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case TYPE_DATE:
|
|
if (_lread(lpSqlNodeStmt->node.select.Sortfile, szBuffer,
|
|
(UINT) Length) != (UINT) Length)
|
|
return ERR_SORT;
|
|
CharToDate((LPUSTR)szBuffer, Length, &(lpSqlNode->value.Date));
|
|
break;
|
|
|
|
case TYPE_TIME:
|
|
if (_lread(lpSqlNodeStmt->node.select.Sortfile, szBuffer,
|
|
(UINT) Length) != (UINT) Length)
|
|
return ERR_SORT;
|
|
CharToTime((LPUSTR)szBuffer, Length, &(lpSqlNode->value.Time));
|
|
break;
|
|
|
|
case TYPE_TIMESTAMP:
|
|
if (_lread(lpSqlNodeStmt->node.select.Sortfile, szBuffer,
|
|
(UINT) Length) != (UINT) Length)
|
|
return ERR_SORT;
|
|
CharToTimestamp((LPUSTR)szBuffer, Length, &(lpSqlNode->value.Timestamp));
|
|
break;
|
|
|
|
case TYPE_BINARY:
|
|
return ERR_INTERNAL;
|
|
|
|
default:
|
|
return ERR_NOTSUPPORTED;
|
|
}
|
|
|
|
/* Read NULL flag */
|
|
if (_lread(lpSqlNodeStmt->node.select.Sortfile, &cNullFlag, 1) != 1)
|
|
return ERR_SORT;
|
|
switch (cNullFlag) {
|
|
case NULL_FLAG:
|
|
lpSqlNode->sqlIsNull = TRUE;
|
|
break;
|
|
case NOT_NULL_FLAG:
|
|
lpSqlNode->sqlIsNull = FALSE;
|
|
break;
|
|
default:
|
|
return ERR_SORT;
|
|
}
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
RETCODE INTFUNC TxnCapableForDDL(LPSTMT lpstmt)
|
|
|
|
/* Makes sure the DDL statements can be executed if DML statements were */
|
|
/* encountered */
|
|
|
|
{
|
|
LPSTMT lpstmtOther;
|
|
RETCODE err;
|
|
|
|
/* What transaction capabilities are allowed? */
|
|
switch (lpstmt->lpdbc->lpISAM->fTxnCapable) {
|
|
case SQL_TC_NONE:
|
|
|
|
/* No transactions. Allow all DDL statements all the time */
|
|
break;
|
|
|
|
case SQL_TC_DML:
|
|
|
|
/* Transactions can never DDL statements */
|
|
for (lpstmtOther = lpstmt->lpdbc->lpstmts;
|
|
lpstmtOther != NULL;
|
|
lpstmtOther = lpstmtOther->lpNext) {
|
|
if (lpstmtOther->fDMLTxn)
|
|
return ERR_DDLENCOUNTERD;
|
|
}
|
|
break;
|
|
|
|
case SQL_TC_DDL_COMMIT:
|
|
|
|
/* Transactions that contain DDL statements cause a commit */
|
|
for (lpstmtOther = lpstmt->lpdbc->lpstmts;
|
|
lpstmtOther != NULL;
|
|
lpstmtOther = lpstmtOther->lpNext) {
|
|
if (lpstmtOther->fDMLTxn) {
|
|
err = SQLTransact(SQL_NULL_HENV, (HDBC)lpstmt->lpdbc,
|
|
SQL_COMMIT);
|
|
if ((err != SQL_SUCCESS) && (err != SQL_SUCCESS_WITH_INFO)) {
|
|
lpstmt->errcode = lpstmt->lpdbc->errcode;
|
|
s_lstrcpy(lpstmt->szISAMError, lpstmt->lpdbc->szISAMError);
|
|
return err;
|
|
}
|
|
if (lpstmt->lpdbc->lpISAM->fSchemaInfoTransactioned)
|
|
return ERR_DDLSTATEMENTLOST;
|
|
else
|
|
return ERR_DDLCAUSEDACOMMIT;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case SQL_TC_DDL_IGNORE:
|
|
|
|
/* DDL statements are ignored within a transaction */
|
|
for (lpstmtOther = lpstmt->lpdbc->lpstmts;
|
|
lpstmtOther != NULL;
|
|
lpstmtOther = lpstmtOther->lpNext) {
|
|
if (lpstmtOther->fDMLTxn)
|
|
return ERR_DDLIGNORED;
|
|
}
|
|
break;
|
|
|
|
case SQL_TC_ALL:
|
|
|
|
/* Allow all DDL statements all the time */
|
|
break;
|
|
}
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
/***************************************************************************/
|
|
RETCODE INTFUNC FetchRow(LPSTMT lpstmt, LPSQLNODE lpSqlNode)
|
|
|
|
/* Fetch the next row from a SELECT */
|
|
{
|
|
LPSQLNODE lpSqlNodeTable;
|
|
LPSQLNODE lpSqlNodeTables;
|
|
LPSQLNODE lpSqlNodePredicate;
|
|
LPSQLNODE lpSqlNodeHaving;
|
|
SQLNODEIDX idxTables;
|
|
ISAMBOOKMARK bookmark;
|
|
RETCODE err;
|
|
|
|
// ODBCTRACE("\nWBEM ODBC Driver : FetchRow\n");
|
|
|
|
/* Error if after the last row */
|
|
if (lpSqlNode->node.select.CurrentRow == AFTER_LAST_ROW)
|
|
return ERR_NODATAFOUND;
|
|
|
|
/* Is there a sort file to read from */
|
|
if (lpSqlNode->node.select.Sortfile == HFILE_ERROR) {
|
|
|
|
/* No. Get the table list and predicate node. */
|
|
lpSqlNodeTables = ToNode(lpstmt->lpSqlStmt, lpSqlNode->node.select.Tables);
|
|
if (lpSqlNode->node.select.Predicate == NO_SQLNODE)
|
|
lpSqlNodePredicate = NULL;
|
|
else
|
|
lpSqlNodePredicate = ToNode(lpstmt->lpSqlStmt, lpSqlNode->node.select.Predicate);
|
|
|
|
/* Get the next record */
|
|
err = NextRecord(lpstmt, lpSqlNodeTables, lpSqlNodePredicate);
|
|
if (err == ERR_NODATAFOUND) {
|
|
lpSqlNode->node.select.CurrentRow = AFTER_LAST_ROW;
|
|
return err;
|
|
}
|
|
else if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Set row flag */
|
|
if (lpSqlNode->node.select.CurrentRow == BEFORE_FIRST_ROW)
|
|
lpSqlNode->node.select.CurrentRow = 0;
|
|
else
|
|
lpSqlNode->node.select.CurrentRow++;
|
|
}
|
|
else if (!(lpSqlNode->node.select.ReturningDistinct)) {
|
|
|
|
/* Yes. Look for next record in sort file */
|
|
if (lpSqlNode->node.select.Having == NO_SQLNODE)
|
|
lpSqlNodeHaving = NULL;
|
|
else
|
|
lpSqlNodeHaving =
|
|
ToNode(lpstmt->lpSqlStmt, lpSqlNode->node.select.Having);
|
|
while (TRUE) {
|
|
|
|
/* Set row flag */
|
|
if (lpSqlNode->node.select.CurrentRow == BEFORE_FIRST_ROW) {
|
|
if (lpSqlNode->node.select.RowCount != 0) {
|
|
lpSqlNode->node.select.CurrentRow = 0;
|
|
}
|
|
else {
|
|
lpSqlNode->node.select.CurrentRow = AFTER_LAST_ROW;
|
|
return ERR_NODATAFOUND;
|
|
}
|
|
}
|
|
else if (lpSqlNode->node.select.CurrentRow ==
|
|
lpSqlNode->node.select.RowCount-1) {
|
|
lpSqlNode->node.select.CurrentRow = AFTER_LAST_ROW;
|
|
return ERR_NODATAFOUND;
|
|
}
|
|
else
|
|
lpSqlNode->node.select.CurrentRow++;
|
|
|
|
/* If no HAVING clause, this record qualifies */
|
|
if (lpSqlNodeHaving == NULL)
|
|
break;
|
|
|
|
/* If HAVING condition is satisfied, this record qualifies */
|
|
err = EvaluateExpression(lpstmt, lpSqlNodeHaving);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
if (!(lpSqlNodeHaving->sqlIsNull) &&
|
|
(lpSqlNodeHaving->value.Double == TRUE))
|
|
break;
|
|
}
|
|
|
|
/* Is there a group by? */
|
|
if ((lpSqlNode->node.select.Groupbycolumns == NO_SQLNODE) &&
|
|
(!lpSqlNode->node.select.ImplicitGroupby)) {
|
|
|
|
/* No. Position to bookmarks in that row */
|
|
if (_llseek(lpSqlNode->node.select.Sortfile,
|
|
(lpSqlNode->node.select.CurrentRow *
|
|
lpSqlNode->node.select.SortRecordsize) +
|
|
lpSqlNode->node.select.SortBookmarks - 1, 0) == HFILE_ERROR)
|
|
return ERR_SORT;
|
|
|
|
/* For each table... */
|
|
idxTables = lpSqlNode->node.select.Tables;
|
|
while (idxTables != NO_SQLNODE) {
|
|
|
|
/* Get the table node */
|
|
lpSqlNodeTables = ToNode(lpstmt->lpSqlStmt, idxTables);
|
|
lpSqlNodeTable = ToNode(lpstmt->lpSqlStmt,
|
|
lpSqlNodeTables->node.tables.Table);
|
|
|
|
/* Read the bookmark for it */
|
|
if (_lread(lpSqlNode->node.select.Sortfile, &bookmark,
|
|
sizeof(ISAMBOOKMARK)) != sizeof(ISAMBOOKMARK))
|
|
return ERR_SORT;
|
|
|
|
/* Position to that record */
|
|
if (bookmark.currentRecord == NULL_BOOKMARK)
|
|
{
|
|
lpSqlNodeTable->node.table.AllNull = TRUE;
|
|
}
|
|
else
|
|
{
|
|
lpSqlNodeTable->node.table.AllNull = FALSE;
|
|
err = ISAMPosition(lpSqlNodeTable->node.table.Handle, &bookmark);
|
|
if (err != NO_ISAM_ERR) {
|
|
ISAMGetErrorMessage(lpstmt->lpdbc->lpISAM,
|
|
(LPUSTR)lpstmt->szISAMError);
|
|
return err;
|
|
}
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
}
|
|
/* Point at next table */
|
|
idxTables = lpSqlNodeTables->node.tables.Next;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
|
|
/* Yes. Look for next record in DISTINCT file. Set row flag */
|
|
if (lpSqlNode->node.select.CurrentRow == BEFORE_FIRST_ROW) {
|
|
if (lpSqlNode->node.select.RowCount != 0) {
|
|
lpSqlNode->node.select.CurrentRow = 0;
|
|
}
|
|
else {
|
|
lpSqlNode->node.select.CurrentRow = AFTER_LAST_ROW;
|
|
return ERR_NODATAFOUND;
|
|
}
|
|
}
|
|
else if (lpSqlNode->node.select.CurrentRow ==
|
|
lpSqlNode->node.select.RowCount-1) {
|
|
lpSqlNode->node.select.CurrentRow = AFTER_LAST_ROW;
|
|
return ERR_NODATAFOUND;
|
|
}
|
|
else
|
|
lpSqlNode->node.select.CurrentRow++;
|
|
}
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
RETCODE INTFUNC EvaluateExpression(LPSTMT lpstmt, LPSQLNODE lpSqlNode)
|
|
|
|
/* Eavluates an SQL expression by walking the parse tree, computing */
|
|
/* the value of each node with respect to a database record, and */
|
|
/* storing the result in the string area. */
|
|
|
|
{
|
|
// ODBCTRACE ("\nWBEM ODBC Driver : EvaluateExpression\n");
|
|
LPSQLTREE lpSql;
|
|
LPSQLNODE lpSqlNodeLeft = 0;
|
|
LPSQLNODE lpSqlNodeRight = 0;
|
|
RETCODE err;
|
|
SQLNODEIDX idxValues;
|
|
LPSQLNODE lpSqlNodeValues;
|
|
LPUSTR lpWorkBuffer1;
|
|
LPUSTR lpWorkBuffer2;
|
|
LPUSTR lpWorkBuffer3;
|
|
BOOL fReturningDistinct;
|
|
LPSQLNODE lpSqlNodeSelect;
|
|
BOOL fTruncation;
|
|
BOOL fIsNull;
|
|
|
|
lpSql = lpstmt->lpSqlStmt;
|
|
if (lpSqlNode == NULL)
|
|
return ERR_INTERNAL;
|
|
|
|
err = ERR_SUCCESS;
|
|
switch (lpSqlNode->sqlNodeType) {
|
|
case NODE_TYPE_NONE:
|
|
case NODE_TYPE_ROOT:
|
|
case NODE_TYPE_CREATE:
|
|
case NODE_TYPE_DROP:
|
|
case NODE_TYPE_SELECT:
|
|
case NODE_TYPE_INSERT:
|
|
case NODE_TYPE_DELETE:
|
|
case NODE_TYPE_UPDATE:
|
|
case NODE_TYPE_CREATEINDEX:
|
|
case NODE_TYPE_DROPINDEX:
|
|
case NODE_TYPE_PASSTHROUGH:
|
|
case NODE_TYPE_TABLES:
|
|
case NODE_TYPE_VALUES:
|
|
case NODE_TYPE_COLUMNS:
|
|
case NODE_TYPE_SORTCOLUMNS:
|
|
case NODE_TYPE_GROUPBYCOLUMNS:
|
|
case NODE_TYPE_UPDATEVALUES:
|
|
case NODE_TYPE_CREATECOLS:
|
|
case NODE_TYPE_TABLE:
|
|
err = ERR_INTERNAL;
|
|
break;
|
|
|
|
case NODE_TYPE_BOOLEAN:
|
|
|
|
/* Evaluate the left child node */
|
|
lpSqlNodeLeft = ToNode(lpSql, lpSqlNode->node.boolean.Left);
|
|
err = EvaluateExpression(lpstmt, lpSqlNodeLeft);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Is this an AND? */
|
|
if (lpSqlNode->node.boolean.Operator == OP_AND) {
|
|
|
|
LPSQLNODE lpSqlNodeAnd;
|
|
BOOL sqlIsNull;
|
|
BOOL fResult;
|
|
|
|
/* Yes. Look at each sub-expression */
|
|
lpSqlNodeAnd = lpSqlNode;
|
|
sqlIsNull = FALSE;
|
|
fResult = TRUE;
|
|
while (TRUE) {
|
|
|
|
/* If the left child is FALSE, return FALSE */
|
|
if (!((BOOL) lpSqlNodeLeft->value.Double)) {
|
|
sqlIsNull = FALSE;
|
|
fResult = FALSE;
|
|
break;
|
|
}
|
|
|
|
/* If the left child is NULL, the result may be NULL */
|
|
else if (lpSqlNodeLeft->sqlIsNull) {
|
|
sqlIsNull = TRUE;
|
|
fResult = FALSE;
|
|
}
|
|
|
|
/* Get the right child */
|
|
lpSqlNodeRight = ToNode(lpSql, lpSqlNode->node.boolean.Right);
|
|
|
|
/* Is it an AND node? */
|
|
if ((lpSqlNodeRight->sqlNodeType != NODE_TYPE_BOOLEAN) ||
|
|
(lpSqlNodeRight->node.boolean.Operator != OP_AND)) {
|
|
|
|
/* No. Evaluate it */
|
|
err = EvaluateExpression(lpstmt, lpSqlNodeRight);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* If the right child is FALSE, return FALSE */
|
|
if (!((BOOL) lpSqlNodeRight->value.Double)) {
|
|
sqlIsNull = FALSE;
|
|
fResult = FALSE;
|
|
}
|
|
|
|
/* If the right child is NULL, the result is NULL */
|
|
else if (lpSqlNodeRight->sqlIsNull) {
|
|
sqlIsNull = TRUE;
|
|
fResult = FALSE;
|
|
}
|
|
|
|
/* Leave the loop */
|
|
break;
|
|
|
|
}
|
|
|
|
/* Point to right and continue to walk down the AND nodes */
|
|
lpSqlNode = lpSqlNodeRight;
|
|
lpSqlNodeLeft = ToNode(lpSql, lpSqlNode->node.boolean.Left);
|
|
err = EvaluateExpression(lpstmt, lpSqlNodeLeft);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
|
|
/* Return result */
|
|
lpSqlNodeAnd->sqlIsNull = sqlIsNull;
|
|
lpSqlNodeAnd->value.Double = fResult;
|
|
break;
|
|
}
|
|
|
|
/* Is there a right child? */
|
|
if (lpSqlNode->node.boolean.Right != NO_SQLNODE) {
|
|
|
|
/* Yes. Evaluate it */
|
|
lpSqlNodeRight = ToNode(lpSql, lpSqlNode->node.boolean.Right);
|
|
err = EvaluateExpression(lpstmt, lpSqlNodeRight);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
|
|
if (lpSqlNode->node.boolean.Operator != OP_NOT && lpSqlNodeRight == 0)
|
|
return ERR_INTERNAL;
|
|
|
|
/* Perform the operation. */
|
|
switch (lpSqlNode->node.boolean.Operator) {
|
|
case OP_NOT:
|
|
|
|
/* If child is NULL, return NULL */
|
|
if (lpSqlNodeLeft->sqlIsNull) {
|
|
lpSqlNode->sqlIsNull = TRUE;
|
|
break;
|
|
}
|
|
|
|
/* Evaluate expression */
|
|
lpSqlNode->sqlIsNull = FALSE;
|
|
lpSqlNode->value.Double = !((BOOL) lpSqlNodeLeft->value.Double);
|
|
break;
|
|
|
|
case OP_AND:
|
|
|
|
/* Is left child NULL or TRUE? */
|
|
if ((lpSqlNodeLeft->sqlIsNull) ||
|
|
((BOOL) lpSqlNodeLeft->value.Double)) {
|
|
|
|
/* Yes. If right child is NULL, return NULL */
|
|
if (lpSqlNodeRight->sqlIsNull)
|
|
lpSqlNode->sqlIsNull = TRUE;
|
|
|
|
/* If right child TRUE, return left child */
|
|
else if ((BOOL) lpSqlNodeRight->value.Double) {
|
|
lpSqlNode->sqlIsNull = lpSqlNodeLeft->sqlIsNull;
|
|
lpSqlNode->value.Double = lpSqlNodeLeft->value.Double;
|
|
}
|
|
|
|
/* Otherwise right child must be FALSE, return FALSE */
|
|
else {
|
|
lpSqlNode->sqlIsNull = FALSE;
|
|
lpSqlNode->value.Double = FALSE;
|
|
}
|
|
}
|
|
|
|
/* Otherwise left child must be FALSE, return FALSE */
|
|
else {
|
|
lpSqlNode->sqlIsNull = FALSE;
|
|
lpSqlNode->value.Double = FALSE;
|
|
}
|
|
break;
|
|
|
|
case OP_OR:
|
|
|
|
/* Is left child NULL or FALSE? */
|
|
if ((lpSqlNodeLeft->sqlIsNull) ||
|
|
(!((BOOL) lpSqlNodeLeft->value.Double))) {
|
|
|
|
/* Yes. If right child is NULL, return NULL */
|
|
if (lpSqlNodeRight->sqlIsNull)
|
|
lpSqlNode->sqlIsNull = TRUE;
|
|
|
|
/* If right child FALSE, return left child */
|
|
else if (!((BOOL) lpSqlNodeRight->value.Double)) {
|
|
lpSqlNode->sqlIsNull = lpSqlNodeLeft->sqlIsNull;
|
|
lpSqlNode->value.Double = lpSqlNodeLeft->value.Double;
|
|
}
|
|
|
|
/* Otherwise right child must be TRUE, return TRUE */
|
|
else {
|
|
lpSqlNode->sqlIsNull = FALSE;
|
|
lpSqlNode->value.Double = TRUE;
|
|
}
|
|
}
|
|
|
|
/* Otherwise left child must be TRUE, return TRUE */
|
|
else {
|
|
lpSqlNode->sqlIsNull = FALSE;
|
|
lpSqlNode->value.Double = TRUE;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
err = ERR_INTERNAL;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case NODE_TYPE_COMPARISON:
|
|
|
|
// ODBCTRACE ("\nWBEM ODBC Driver : NODE_TYPE_COMPARISON\n");
|
|
/* Evaluate the left child */
|
|
if (lpSqlNode->node.comparison.Operator != OP_EXISTS) {
|
|
lpSqlNodeLeft = ToNode(lpSql, lpSqlNode->node.comparison.Left);
|
|
err = EvaluateExpression(lpstmt, lpSqlNodeLeft);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
|
|
/* EXISTS operator? */
|
|
if (lpSqlNode->node.comparison.Operator == OP_EXISTS) {
|
|
|
|
/* Do the sub-select */
|
|
lpSqlNodeSelect = ToNode(lpSql, lpSqlNode->node.comparison.Left);
|
|
err = ExecuteQuery(lpstmt, lpSqlNodeSelect);
|
|
if (err == ERR_DATATRUNCATED)
|
|
err = ERR_SUCCESS;
|
|
if ((err != ERR_SUCCESS) && (err != ERR_NODATAFOUND))
|
|
return err;
|
|
|
|
/* Try to fetch a row */
|
|
if (err == ERR_SUCCESS) {
|
|
err = FetchRow(lpstmt, lpSqlNodeSelect);
|
|
if ((err != ERR_SUCCESS) && (err != ERR_NODATAFOUND))
|
|
return err;
|
|
}
|
|
|
|
/* Return result */
|
|
lpSqlNode->sqlIsNull = FALSE;
|
|
if (err == ERR_NODATAFOUND)
|
|
lpSqlNode->value.Double = FALSE;
|
|
else
|
|
lpSqlNode->value.Double = TRUE;
|
|
}
|
|
|
|
/* Nested sub-select? */
|
|
else if (lpSqlNode->node.comparison.SelectModifier !=
|
|
SELECT_NOTSELECT) {
|
|
|
|
/* Yes. If left child is NULL, return NULL */
|
|
if (lpSqlNodeLeft->sqlIsNull) {
|
|
lpSqlNode->sqlIsNull = TRUE;
|
|
lpSqlNode->value.Double = 0;
|
|
break;
|
|
}
|
|
|
|
/* Do the sub-select */
|
|
fTruncation = FALSE;
|
|
lpSqlNodeSelect = ToNode(lpSql, lpSqlNode->node.comparison.Right);
|
|
lpSqlNodeRight = ToNode(lpSql, lpSqlNodeSelect->node.select.Values);
|
|
lpSqlNodeRight = ToNode(lpSql, lpSqlNodeRight->node.values.Value);
|
|
err = ExecuteQuery(lpstmt,
|
|
ToNode(lpSql, lpSqlNode->node.comparison.Right));
|
|
if (err == ERR_DATATRUNCATED) {
|
|
fTruncation = TRUE;
|
|
err = ERR_SUCCESS;
|
|
}
|
|
if ((err != ERR_SUCCESS) && (err != ERR_NODATAFOUND))
|
|
return err;
|
|
|
|
/* Get the first value */
|
|
if (err == ERR_SUCCESS) {
|
|
// ODBCTRACE ("\nWBEM ODBC Driver : Get the first value\n");
|
|
err = FetchRow(lpstmt, lpSqlNodeSelect);
|
|
if ((err != ERR_SUCCESS) && (err != ERR_NODATAFOUND))
|
|
return err;
|
|
}
|
|
|
|
/* Does the select return any rows? */
|
|
if (err == ERR_NODATAFOUND) {
|
|
|
|
/* No. Return result */
|
|
lpSqlNode->sqlIsNull = FALSE;
|
|
switch (lpSqlNode->node.comparison.SelectModifier) {
|
|
case SELECT_NOTSELECT:
|
|
return ERR_INTERNAL;
|
|
case SELECT_ALL:
|
|
lpSqlNode->value.Double = TRUE;
|
|
break;
|
|
case SELECT_ANY:
|
|
lpSqlNode->value.Double = FALSE;
|
|
break;
|
|
case SELECT_ONE:
|
|
return ERR_NOTSINGLESELECT;
|
|
break;
|
|
case SELECT_EXISTS:
|
|
default:
|
|
return ERR_INTERNAL;
|
|
}
|
|
}
|
|
else {
|
|
|
|
/* Yes. For each value returned */
|
|
fIsNull = FALSE;
|
|
while (TRUE) {
|
|
|
|
/* Get value */
|
|
err = EvaluateExpression(lpstmt, lpSqlNodeRight);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Null value? */
|
|
if (!lpSqlNodeRight->sqlIsNull) {
|
|
|
|
/* No. Compare the values */
|
|
err = ValueCompare(lpSqlNode, lpSqlNodeLeft,
|
|
lpSqlNode->node.comparison.Operator,
|
|
lpSqlNodeRight);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Add it into the result */
|
|
switch (lpSqlNode->node.comparison.SelectModifier) {
|
|
case SELECT_NOTSELECT:
|
|
return ERR_INTERNAL;
|
|
case SELECT_ALL:
|
|
if (lpSqlNode->value.Double == FALSE) {
|
|
lpSqlNode->sqlIsNull = FALSE;
|
|
if (fTruncation)
|
|
return ERR_DATATRUNCATED;
|
|
return ERR_SUCCESS;
|
|
}
|
|
break;
|
|
case SELECT_ANY:
|
|
if (lpSqlNode->value.Double == TRUE) {
|
|
lpSqlNode->sqlIsNull = FALSE;
|
|
if (fTruncation)
|
|
return ERR_DATATRUNCATED;
|
|
return ERR_SUCCESS;
|
|
}
|
|
break;
|
|
case SELECT_ONE:
|
|
break;
|
|
case SELECT_EXISTS:
|
|
default:
|
|
return ERR_INTERNAL;
|
|
}
|
|
}
|
|
else
|
|
fIsNull = TRUE;
|
|
|
|
/* Get next value */
|
|
// ODBCTRACE ("\nWBEM ODBC Driver : Get next value\n");
|
|
err = FetchRow(lpstmt, lpSqlNodeSelect);
|
|
if (err == ERR_NODATAFOUND)
|
|
break;
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Error if too mnay values */
|
|
if (lpSqlNode->node.comparison.SelectModifier==SELECT_ONE)
|
|
return ERR_NOTSINGLESELECT;
|
|
}
|
|
|
|
/* Figure out final answer */
|
|
err = ERR_SUCCESS;
|
|
if (fIsNull) {
|
|
lpSqlNode->sqlIsNull = TRUE;
|
|
lpSqlNode->value.Double = 0;
|
|
}
|
|
else {
|
|
lpSqlNode->sqlIsNull = FALSE;
|
|
switch (lpSqlNode->node.comparison.SelectModifier) {
|
|
case SELECT_NOTSELECT:
|
|
return ERR_INTERNAL;
|
|
case SELECT_ALL:
|
|
lpSqlNode->value.Double = TRUE;
|
|
break;
|
|
case SELECT_ANY:
|
|
lpSqlNode->value.Double = FALSE;
|
|
break;
|
|
case SELECT_ONE:
|
|
/* This was set by the call to ValueCompare above */
|
|
break;
|
|
case SELECT_EXISTS:
|
|
default:
|
|
return ERR_INTERNAL;
|
|
}
|
|
}
|
|
|
|
/* Return truncation error if need be */
|
|
if (fTruncation)
|
|
err = ERR_DATATRUNCATED;
|
|
}
|
|
}
|
|
|
|
/* IN or NOT IN operator? */
|
|
else if ((lpSqlNode->node.comparison.Operator != OP_IN) &&
|
|
(lpSqlNode->node.comparison.Operator != OP_NOTIN)) {
|
|
|
|
/* No. Evaluate the right child */
|
|
lpSqlNodeRight = ToNode(lpSql, lpSqlNode->node.comparison.Right);
|
|
err = EvaluateExpression(lpstmt, lpSqlNodeRight);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Could this be an "IS NULL" or "IS NOT NULL" expression? */
|
|
if (lpSqlNodeRight->sqlIsNull &&
|
|
((lpSqlNodeRight->sqlNodeType == NODE_TYPE_NULL) ||
|
|
(lpSqlNodeRight->sqlNodeType == NODE_TYPE_PARAMETER))) {
|
|
|
|
/* Possibly. Is this an "IS NULL" expression? */
|
|
if (lpSqlNode->node.comparison.Operator == OP_EQ) {
|
|
|
|
/* Yes. Return TRUE if the left child is NULL. */
|
|
lpSqlNode->sqlIsNull = FALSE;
|
|
if (lpSqlNodeLeft->sqlIsNull)
|
|
lpSqlNode->value.Double = TRUE;
|
|
else
|
|
lpSqlNode->value.Double = FALSE;
|
|
break;
|
|
}
|
|
|
|
/* Is this an "IS NOT NULL" expression? */
|
|
else if (lpSqlNode->node.comparison.Operator == OP_NE) {
|
|
|
|
/* Yes. Return FALSE if the left child is NULL. */
|
|
lpSqlNode->sqlIsNull = FALSE;
|
|
if (lpSqlNodeLeft->sqlIsNull)
|
|
lpSqlNode->value.Double = FALSE;
|
|
else
|
|
lpSqlNode->value.Double = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* If either child is NULL, return NULL */
|
|
if (lpSqlNodeLeft->sqlIsNull || lpSqlNodeRight->sqlIsNull) {
|
|
lpSqlNode->sqlIsNull = TRUE;
|
|
lpSqlNode->value.Double = 0;
|
|
break;
|
|
}
|
|
|
|
/* Compare values */
|
|
err = ValueCompare(lpSqlNode, lpSqlNodeLeft,
|
|
lpSqlNode->node.comparison.Operator, lpSqlNodeRight);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
else {
|
|
|
|
/* Yes. If test value is NULL, return NULL */
|
|
if (lpSqlNodeLeft->sqlIsNull) {
|
|
lpSqlNode->sqlIsNull = TRUE;
|
|
lpSqlNode->value.Double = 0;
|
|
break;
|
|
}
|
|
|
|
/* Set up the default answer */
|
|
lpSqlNode->sqlIsNull = FALSE;
|
|
lpSqlNode->value.Double = FALSE;
|
|
|
|
/* For each value on list... */
|
|
idxValues = lpSqlNode->node.comparison.Right;
|
|
while (idxValues != NO_SQLNODE) {
|
|
|
|
/* Get the value */
|
|
lpSqlNodeValues = ToNode(lpSql, idxValues);
|
|
lpSqlNodeRight = ToNode(lpSql, lpSqlNodeValues->node.values.Value);
|
|
err = EvaluateExpression(lpstmt, lpSqlNodeRight);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Point at next value */
|
|
idxValues = lpSqlNodeValues->node.values.Next;
|
|
|
|
/* If this value is NULL, go on to the next one */
|
|
if (lpSqlNodeRight->sqlIsNull)
|
|
continue;
|
|
|
|
/* Compare this value to the test value */
|
|
err = ValueCompare(lpSqlNode, lpSqlNodeLeft, OP_EQ,
|
|
lpSqlNodeRight);
|
|
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
|
|
/* If value was found, leave */
|
|
if (lpSqlNode->value.Double == TRUE)
|
|
break;
|
|
}
|
|
|
|
/* If NOT IN operator, negate the answer */
|
|
if (lpSqlNode->node.comparison.Operator == OP_NOTIN) {
|
|
if (lpSqlNode->value.Double == TRUE)
|
|
lpSqlNode->value.Double = FALSE;
|
|
else
|
|
lpSqlNode->value.Double = TRUE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case NODE_TYPE_ALGEBRAIC:
|
|
|
|
/* Set up return buffer */
|
|
switch (lpSqlNode->sqlDataType) {
|
|
case TYPE_NUMERIC:
|
|
lpSqlNode->value.String =
|
|
ToString(lpSql,lpSqlNode->node.algebraic.Value);
|
|
break;
|
|
case TYPE_CHAR:
|
|
lpSqlNode->value.String =
|
|
ToString(lpSql,lpSqlNode->node.algebraic.Value);
|
|
break;
|
|
case TYPE_DOUBLE:
|
|
case TYPE_INTEGER:
|
|
case TYPE_DATE:
|
|
case TYPE_TIME:
|
|
case TYPE_TIMESTAMP:
|
|
break;
|
|
case TYPE_BINARY:
|
|
return ERR_INTERNAL;
|
|
default:
|
|
return ERR_NOTSUPPORTED;
|
|
}
|
|
|
|
/* Is this value in the sort DISTINCT record? */
|
|
lpSqlNodeSelect = ToNode(lpSql,
|
|
lpSqlNode->node.algebraic.EnclosingStatement);
|
|
if (lpSqlNodeSelect->sqlNodeType != NODE_TYPE_SELECT)
|
|
fReturningDistinct = FALSE;
|
|
else
|
|
fReturningDistinct = lpSqlNodeSelect->node.select.ReturningDistinct;
|
|
if (!fReturningDistinct) {
|
|
|
|
/* No. Evaluate the left child */
|
|
lpSqlNodeLeft = ToNode(lpSql, lpSqlNode->node.algebraic.Left);
|
|
err = EvaluateExpression(lpstmt, lpSqlNodeLeft);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* If left child is NULL, the expression is null */
|
|
if (lpSqlNodeLeft->sqlIsNull) {
|
|
lpSqlNode->sqlIsNull = TRUE;
|
|
break;
|
|
}
|
|
|
|
/* Evaluate the right child (if any) */
|
|
if (lpSqlNode->node.algebraic.Right != NO_SQLNODE) {
|
|
lpSqlNodeRight = ToNode(lpSql, lpSqlNode->node.algebraic.Right);
|
|
err = EvaluateExpression(lpstmt, lpSqlNodeRight);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* If right child is NULL, the expression is null */
|
|
if (lpSqlNodeRight->sqlIsNull) {
|
|
lpSqlNode->sqlIsNull = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
lpSqlNodeRight = NULL;
|
|
|
|
/* Result is not null */
|
|
lpSqlNode->sqlIsNull = FALSE;
|
|
|
|
/* Perform the operation */
|
|
lpWorkBuffer1 = NULL;
|
|
lpWorkBuffer2 = NULL;
|
|
lpWorkBuffer3 = NULL;
|
|
switch (lpSqlNode->sqlDataType) {
|
|
case TYPE_DOUBLE:
|
|
case TYPE_INTEGER:
|
|
case TYPE_DATE:
|
|
case TYPE_TIME:
|
|
case TYPE_TIMESTAMP:
|
|
break;
|
|
case TYPE_NUMERIC:
|
|
if ((lpSqlNode->node.algebraic.Operator == OP_TIMES) ||
|
|
(lpSqlNode->node.algebraic.Operator == OP_DIVIDEDBY)) {
|
|
lpWorkBuffer1 =
|
|
ToString(lpSql,lpSqlNode->node.algebraic.WorkBuffer1);
|
|
if (lpSqlNode->node.algebraic.Operator == OP_DIVIDEDBY) {
|
|
lpWorkBuffer2 =
|
|
ToString(lpSql,lpSqlNode->node.algebraic.WorkBuffer2);
|
|
lpWorkBuffer3 =
|
|
ToString(lpSql,lpSqlNode->node.algebraic.WorkBuffer3);
|
|
}
|
|
}
|
|
break;
|
|
case TYPE_CHAR:
|
|
break;
|
|
case TYPE_BINARY:
|
|
return ERR_INTERNAL;
|
|
default:
|
|
return ERR_NOTSUPPORTED;
|
|
}
|
|
err = NumericAlgebra(lpSqlNode, lpSqlNodeLeft,
|
|
lpSqlNode->node.algebraic.Operator, lpSqlNodeRight,
|
|
lpWorkBuffer1, lpWorkBuffer2, lpWorkBuffer3);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
else {
|
|
/* Yes. Get the record */
|
|
err = RetrieveSortRecordValue(lpSqlNodeSelect, lpSqlNode,
|
|
lpSqlNode->node.algebraic.DistinctOffset,
|
|
lpSqlNode->node.algebraic.DistinctLength);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
break;
|
|
|
|
case NODE_TYPE_SCALAR:
|
|
|
|
/* Set up return buffer */
|
|
switch (lpSqlNode->sqlDataType) {
|
|
case TYPE_NUMERIC:
|
|
lpSqlNode->value.String =
|
|
ToString(lpSql,lpSqlNode->node.scalar.Value);
|
|
break;
|
|
case TYPE_CHAR:
|
|
lpSqlNode->value.String =
|
|
ToString(lpSql,lpSqlNode->node.scalar.Value);
|
|
break;
|
|
case TYPE_DOUBLE:
|
|
case TYPE_INTEGER:
|
|
case TYPE_DATE:
|
|
case TYPE_TIME:
|
|
case TYPE_TIMESTAMP:
|
|
break;
|
|
case TYPE_BINARY:
|
|
lpSqlNode->value.String =
|
|
ToString(lpSql,lpSqlNode->node.scalar.Value);
|
|
break;
|
|
default:
|
|
return ERR_NOTSUPPORTED;
|
|
}
|
|
|
|
/* Is this value in the sort DISTINCT record? */
|
|
lpSqlNodeSelect = ToNode(lpSql,
|
|
lpSqlNode->node.scalar.EnclosingStatement);
|
|
if (lpSqlNodeSelect->sqlNodeType != NODE_TYPE_SELECT)
|
|
fReturningDistinct = FALSE;
|
|
else
|
|
fReturningDistinct = lpSqlNodeSelect->node.select.ReturningDistinct;
|
|
if (!fReturningDistinct) {
|
|
|
|
/* Perform the operation */
|
|
err = EvaluateScalar(lpstmt, lpSqlNode);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
else {
|
|
/* Yes. Get the record */
|
|
err = RetrieveSortRecordValue(lpSqlNodeSelect, lpSqlNode,
|
|
lpSqlNode->node.scalar.DistinctOffset,
|
|
lpSqlNode->node.scalar.DistinctLength);
|
|
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
|
|
break;
|
|
|
|
case NODE_TYPE_AGGREGATE:
|
|
|
|
/* Set up return buffer */
|
|
switch (lpSqlNode->sqlDataType) {
|
|
case TYPE_NUMERIC:
|
|
lpSqlNode->value.String =
|
|
ToString(lpSql,lpSqlNode->node.aggregate.Value);
|
|
break;
|
|
case TYPE_CHAR:
|
|
lpSqlNode->value.String =
|
|
ToString(lpSql,lpSqlNode->node.aggregate.Value);
|
|
break;
|
|
case TYPE_DOUBLE:
|
|
case TYPE_INTEGER:
|
|
case TYPE_DATE:
|
|
case TYPE_TIME:
|
|
case TYPE_TIMESTAMP:
|
|
break;
|
|
case TYPE_BINARY:
|
|
return ERR_INTERNAL;
|
|
default:
|
|
return ERR_NOTSUPPORTED;
|
|
}
|
|
|
|
/* Retrieve the value */
|
|
lpSqlNodeSelect = ToNode(lpSql,
|
|
lpSqlNode->node.aggregate.EnclosingStatement);
|
|
if (lpSqlNodeSelect->sqlNodeType != NODE_TYPE_SELECT)
|
|
fReturningDistinct = FALSE;
|
|
else
|
|
fReturningDistinct = lpSqlNodeSelect->node.select.ReturningDistinct;
|
|
if (fReturningDistinct) {
|
|
err = RetrieveSortRecordValue(lpSqlNodeSelect, lpSqlNode,
|
|
lpSqlNode->node.aggregate.DistinctOffset,
|
|
lpSqlNode->node.aggregate.DistinctLength);
|
|
}
|
|
else {
|
|
err = RetrieveSortRecordValue(lpSqlNodeSelect, lpSqlNode,
|
|
lpSqlNode->node.aggregate.Offset,
|
|
lpSqlNode->node.aggregate.Length);
|
|
}
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
break;
|
|
|
|
case NODE_TYPE_COLUMN:
|
|
|
|
/* Set up return buffer */
|
|
switch (lpSqlNode->sqlDataType) {
|
|
case TYPE_NUMERIC:
|
|
lpSqlNode->value.String = ToString(lpSql,lpSqlNode->node.column.Value);
|
|
break;
|
|
case TYPE_CHAR:
|
|
lpSqlNode->value.String = ToString(lpSql,lpSqlNode->node.column.Value);
|
|
break;
|
|
case TYPE_DOUBLE:
|
|
case TYPE_INTEGER:
|
|
case TYPE_DATE:
|
|
case TYPE_TIME:
|
|
case TYPE_TIMESTAMP:
|
|
break;
|
|
case TYPE_BINARY:
|
|
lpSqlNode->value.Binary = ToString(lpSql,lpSqlNode->node.column.Value);
|
|
break;
|
|
default:
|
|
return ERR_NOTSUPPORTED;
|
|
}
|
|
|
|
/* Is this column in the sort DISTINCT record? */
|
|
lpSqlNodeSelect = ToNode(lpSql,
|
|
lpSqlNode->node.column.EnclosingStatement);
|
|
if (lpSqlNodeSelect->sqlNodeType != NODE_TYPE_SELECT)
|
|
fReturningDistinct = FALSE;
|
|
else
|
|
fReturningDistinct = lpSqlNodeSelect->node.select.ReturningDistinct;
|
|
if (fReturningDistinct) {
|
|
|
|
|
|
/* Yes. Retrieve the value */
|
|
err = RetrieveSortRecordValue(lpSqlNodeSelect, lpSqlNode,
|
|
lpSqlNode->node.column.DistinctOffset,
|
|
lpSqlNode->node.column.DistinctLength);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
|
|
/* Is this column in the sort record? */
|
|
else if (!(lpSqlNode->node.column.InSortRecord)) {
|
|
|
|
SDWORD size;
|
|
LPSQLNODE lpSqlNodeTable;
|
|
|
|
/* No. Get the column value from the current record */
|
|
lpSqlNodeTable = ToNode(lpSql, lpSqlNode->node.column.Table);
|
|
if (!(lpSqlNodeTable->node.table.AllNull)) {
|
|
switch (lpSqlNode->sqlDataType) {
|
|
case TYPE_DOUBLE:
|
|
case TYPE_INTEGER:
|
|
err = ISAMGetData(lpSqlNodeTable->node.table.Handle,
|
|
lpSqlNode->node.column.Id, 0, SQL_C_DOUBLE,
|
|
&(lpSqlNode->value.Double), sizeof(double),
|
|
&size);
|
|
if (err != NO_ISAM_ERR) {
|
|
ISAMGetErrorMessage(lpstmt->lpdbc->lpISAM,
|
|
(LPUSTR)lpstmt->szISAMError);
|
|
return err;
|
|
}
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
if (size == SQL_NULL_DATA)
|
|
lpSqlNode->sqlIsNull = TRUE;
|
|
else
|
|
lpSqlNode->sqlIsNull = FALSE;
|
|
break;
|
|
|
|
case TYPE_NUMERIC:
|
|
err = ISAMGetData(lpSqlNodeTable->node.table.Handle,
|
|
lpSqlNode->node.column.Id, 0, SQL_C_CHAR,
|
|
lpSqlNode->value.String,
|
|
(SDWORD) (1 + 2 + lpSqlNode->sqlPrecision),
|
|
&size);
|
|
if (err != NO_ISAM_ERR) {
|
|
ISAMGetErrorMessage(lpstmt->lpdbc->lpISAM,
|
|
(LPUSTR)lpstmt->szISAMError);
|
|
return err;
|
|
}
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
if (size == SQL_NULL_DATA)
|
|
lpSqlNode->sqlIsNull = TRUE;
|
|
else {
|
|
lpSqlNode->sqlIsNull = FALSE;
|
|
BCDNormalize(lpSqlNode->value.String,
|
|
s_lstrlen(lpSqlNode->value.String),
|
|
lpSqlNode->value.String,
|
|
lpSqlNode->sqlPrecision + 2 + 1,
|
|
lpSqlNode->sqlPrecision,
|
|
lpSqlNode->sqlScale);
|
|
}
|
|
break;
|
|
|
|
case TYPE_CHAR:
|
|
err = ISAMGetData(lpSqlNodeTable->node.table.Handle,
|
|
lpSqlNode->node.column.Id, 0, SQL_C_CHAR,
|
|
lpSqlNode->value.String,
|
|
(SDWORD) (1 + lpSqlNode->sqlPrecision),
|
|
&size);
|
|
if (err != NO_ISAM_ERR) {
|
|
ISAMGetErrorMessage(lpstmt->lpdbc->lpISAM,
|
|
(LPUSTR)lpstmt->szISAMError);
|
|
return err;
|
|
}
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
if (size == SQL_NULL_DATA)
|
|
lpSqlNode->sqlIsNull = TRUE;
|
|
else
|
|
lpSqlNode->sqlIsNull = FALSE;
|
|
break;
|
|
|
|
case TYPE_DATE:
|
|
err = ISAMGetData(lpSqlNodeTable->node.table.Handle,
|
|
lpSqlNode->node.column.Id, 0, SQL_C_DATE,
|
|
&(lpSqlNode->value.Date), sizeof(DATE_STRUCT),
|
|
&size);
|
|
if (err != NO_ISAM_ERR) {
|
|
ISAMGetErrorMessage(lpstmt->lpdbc->lpISAM,
|
|
(LPUSTR)lpstmt->szISAMError);
|
|
return err;
|
|
}
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
if (size == SQL_NULL_DATA)
|
|
lpSqlNode->sqlIsNull = TRUE;
|
|
else
|
|
lpSqlNode->sqlIsNull = FALSE;
|
|
break;
|
|
|
|
case TYPE_TIME:
|
|
err = ISAMGetData(lpSqlNodeTable->node.table.Handle,
|
|
lpSqlNode->node.column.Id, 0, SQL_C_TIME,
|
|
&(lpSqlNode->value.Time), sizeof(TIME_STRUCT),
|
|
&size);
|
|
if (err != NO_ISAM_ERR) {
|
|
ISAMGetErrorMessage(lpstmt->lpdbc->lpISAM,
|
|
(LPUSTR)lpstmt->szISAMError);
|
|
return err;
|
|
}
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
if (size == SQL_NULL_DATA)
|
|
lpSqlNode->sqlIsNull = TRUE;
|
|
else
|
|
lpSqlNode->sqlIsNull = FALSE;
|
|
break;
|
|
|
|
case TYPE_TIMESTAMP:
|
|
err = ISAMGetData(lpSqlNodeTable->node.table.Handle,
|
|
lpSqlNode->node.column.Id, 0, SQL_C_TIMESTAMP,
|
|
&(lpSqlNode->value.Timestamp), sizeof(TIMESTAMP_STRUCT),
|
|
&size);
|
|
if (err != NO_ISAM_ERR) {
|
|
ISAMGetErrorMessage(lpstmt->lpdbc->lpISAM,
|
|
(LPUSTR)lpstmt->szISAMError);
|
|
return err;
|
|
}
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
if (size == SQL_NULL_DATA)
|
|
lpSqlNode->sqlIsNull = TRUE;
|
|
else
|
|
lpSqlNode->sqlIsNull = FALSE;
|
|
break;
|
|
|
|
case TYPE_BINARY:
|
|
err = ISAMGetData(lpSqlNodeTable->node.table.Handle,
|
|
lpSqlNode->node.column.Id, 0, SQL_C_BINARY,
|
|
BINARY_DATA(lpSqlNode->value.Binary),
|
|
(SDWORD) lpSqlNode->sqlPrecision,
|
|
&size);
|
|
if (err != NO_ISAM_ERR) {
|
|
ISAMGetErrorMessage(lpstmt->lpdbc->lpISAM,
|
|
(LPUSTR)lpstmt->szISAMError);
|
|
return err;
|
|
}
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
if (size == SQL_NULL_DATA) {
|
|
lpSqlNode->sqlIsNull = TRUE;
|
|
BINARY_LENGTH(lpSqlNode->value.Binary) = 0;
|
|
}
|
|
else {
|
|
lpSqlNode->sqlIsNull = FALSE;
|
|
BINARY_LENGTH(lpSqlNode->value.Binary) = size;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
err = ERR_NOTSUPPORTED;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
err = NO_ISAM_ERR;
|
|
lpSqlNode->sqlIsNull = TRUE;
|
|
switch (lpSqlNode->sqlDataType) {
|
|
case TYPE_DOUBLE:
|
|
case TYPE_INTEGER:
|
|
lpSqlNode->value.Double = 0.0;
|
|
break;
|
|
case TYPE_NUMERIC:
|
|
s_lstrcpy(lpSqlNode->value.String, "");
|
|
break;
|
|
case TYPE_CHAR:
|
|
s_lstrcpy(lpSqlNode->value.String, "");
|
|
break;
|
|
case TYPE_DATE:
|
|
lpSqlNode->value.Date.year = 0;
|
|
lpSqlNode->value.Date.month = 0;
|
|
lpSqlNode->value.Date.day = 0;
|
|
break;
|
|
case TYPE_TIME:
|
|
lpSqlNode->value.Time.hour = 0;
|
|
lpSqlNode->value.Time.minute = 0;
|
|
lpSqlNode->value.Time.second = 0;
|
|
break;
|
|
case TYPE_TIMESTAMP:
|
|
lpSqlNode->value.Timestamp.year = 0;
|
|
lpSqlNode->value.Timestamp.month = 0;
|
|
lpSqlNode->value.Timestamp.day = 0;
|
|
lpSqlNode->value.Timestamp.hour = 0;
|
|
lpSqlNode->value.Timestamp.minute = 0;
|
|
lpSqlNode->value.Timestamp.second = 0;
|
|
lpSqlNode->value.Timestamp.fraction = 0;
|
|
break;
|
|
case TYPE_BINARY:
|
|
BINARY_LENGTH(lpSqlNode->value.Binary) = 0;
|
|
break;
|
|
default:
|
|
err = ERR_NOTSUPPORTED;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
|
|
/* Yes. Retrieve the value */
|
|
err = RetrieveSortRecordValue(lpSqlNodeSelect, lpSqlNode,
|
|
lpSqlNode->node.column.Offset,
|
|
lpSqlNode->node.column.Length);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
|
|
break;
|
|
|
|
case NODE_TYPE_STRING:
|
|
lpSqlNode->sqlIsNull = FALSE;
|
|
lpSqlNode->value.String = ToString(lpSql, lpSqlNode->node.string.Value);
|
|
break;
|
|
|
|
case NODE_TYPE_NUMERIC:
|
|
lpSqlNode->sqlIsNull = FALSE;
|
|
switch(lpSqlNode->sqlDataType) {
|
|
case TYPE_NUMERIC:
|
|
lpSqlNode->value.String =
|
|
ToString(lpSql, lpSqlNode->node.numeric.Numeric);
|
|
break;
|
|
case TYPE_DOUBLE:
|
|
case TYPE_INTEGER:
|
|
lpSqlNode->value.Double = lpSqlNode->node.numeric.Value;
|
|
break;
|
|
case TYPE_CHAR:
|
|
case TYPE_DATE:
|
|
case TYPE_TIME:
|
|
case TYPE_TIMESTAMP:
|
|
case TYPE_BINARY:
|
|
default:
|
|
return ERR_INTERNAL;
|
|
}
|
|
break;
|
|
|
|
case NODE_TYPE_PARAMETER:
|
|
break;
|
|
|
|
case NODE_TYPE_USER:
|
|
lpSqlNode->sqlIsNull = FALSE;
|
|
lpSqlNode->value.String =
|
|
(LPUSTR) ISAMUser(ToNode(lpSql, ROOT_SQLNODE)->node.root.lpISAM);
|
|
break;
|
|
|
|
case NODE_TYPE_NULL:
|
|
lpSqlNode->sqlIsNull = TRUE;
|
|
break;
|
|
|
|
case NODE_TYPE_DATE:
|
|
lpSqlNode->sqlIsNull = FALSE;
|
|
lpSqlNode->value.Date = lpSqlNode->node.date.Value;
|
|
break;
|
|
|
|
case NODE_TYPE_TIME:
|
|
lpSqlNode->sqlIsNull = FALSE;
|
|
lpSqlNode->value.Time = lpSqlNode->node.time.Value;
|
|
break;
|
|
|
|
case NODE_TYPE_TIMESTAMP:
|
|
lpSqlNode->sqlIsNull = FALSE;
|
|
lpSqlNode->value.Timestamp = lpSqlNode->node.timestamp.Value;
|
|
break;
|
|
|
|
default:
|
|
return ERR_INTERNAL;
|
|
}
|
|
return err;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC Sort(LPSTMT lpstmt, LPSQLNODE lpSqlNodeSelect)
|
|
|
|
/* Sorts results of a SELECT statement */
|
|
|
|
{
|
|
|
|
// ODBCTRACE("\nWBEM ODBC Driver : Sort\n");
|
|
#ifdef WIN32
|
|
UCHAR szTempDir[MAX_PATHNAME_SIZE+1];
|
|
szTempDir[0] = 0;
|
|
#endif
|
|
UCHAR szFilename[MAX_PATHNAME_SIZE+1];
|
|
szFilename[0] = 0;
|
|
HFILE_BUFFER hfSortfile;
|
|
HFILE hfSortfile2;
|
|
LPSQLNODE lpSqlNodeTables;
|
|
LPSQLNODE lpSqlNodePredicate;
|
|
RETCODE err;
|
|
SQLNODEIDX idxSortcolumns;
|
|
LPSQLNODE lpSqlNodeSortcolumns;
|
|
SQLNODEIDX idxGroupbycolumns;
|
|
LPSQLNODE lpSqlNodeGroupbycolumns;
|
|
LPSQLNODE lpSqlNodeColumn;
|
|
SDWORD len;
|
|
PTR ptr;
|
|
SQLNODEIDX idxTableList;
|
|
LPSQLNODE lpSqlNodeTableList;
|
|
LPSQLNODE lpSqlNodeTable;
|
|
ISAMBOOKMARK bookmark;
|
|
UCHAR szBuffer[20+TIMESTAMP_SCALE+1];
|
|
long recordCount;
|
|
int sortStatus;
|
|
UCHAR cNullFlag;
|
|
SQLNODEIDX idxAggregate;
|
|
LPSQLNODE lpSqlNodeAggregate;
|
|
LPSQLNODE lpSqlNodeExpression;
|
|
double dbl;
|
|
|
|
szBuffer[0] = 0;
|
|
|
|
/* Create temporary file */
|
|
#ifdef WIN32
|
|
if (!GetTempPath(MAX_PATHNAME_SIZE+1, (LPSTR)szTempDir))
|
|
return ERR_SORT;
|
|
if (!GetTempFileName((LPSTR)szTempDir, "LEM", 0, (LPSTR)szFilename))
|
|
return ERR_SORT;
|
|
#else
|
|
GetTempFileName(NULL, "LEM", 0, (LPSTR) szFilename);
|
|
#endif
|
|
hfSortfile = _lcreat_buffer((LPCSTR) szFilename, 0);
|
|
if (hfSortfile == NULL)
|
|
return ERR_SORT;
|
|
|
|
/* Get the table list and predicate node. */
|
|
lpSqlNodeTables = ToNode(lpstmt->lpSqlStmt,
|
|
lpSqlNodeSelect->node.select.Tables);
|
|
if (lpSqlNodeSelect->node.select.Predicate == NO_SQLNODE)
|
|
lpSqlNodePredicate = NULL;
|
|
else
|
|
lpSqlNodePredicate = ToNode(lpstmt->lpSqlStmt,
|
|
lpSqlNodeSelect->node.select.Predicate);
|
|
|
|
/* For each record of result set... */
|
|
lpSqlNodeSelect->node.select.RowCount = 0;
|
|
while (TRUE) {
|
|
|
|
/* Get the next record */
|
|
err = NextRecord(lpstmt, lpSqlNodeTables,
|
|
lpSqlNodePredicate);
|
|
if (err == ERR_NODATAFOUND) {
|
|
if (lpSqlNodeSelect->node.select.CurrentRow == BEFORE_FIRST_ROW) {
|
|
_lclose_buffer(hfSortfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
lpSqlNodeSelect->node.select.CurrentRow = AFTER_LAST_ROW;
|
|
return ERR_SUCCESS;
|
|
}
|
|
break;
|
|
}
|
|
else if (err != ERR_SUCCESS) {
|
|
_lclose_buffer(hfSortfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return err;
|
|
}
|
|
|
|
/* Set row flag */
|
|
if (lpSqlNodeSelect->node.select.CurrentRow == BEFORE_FIRST_ROW)
|
|
lpSqlNodeSelect->node.select.CurrentRow = 0;
|
|
else
|
|
lpSqlNodeSelect->node.select.CurrentRow++;
|
|
|
|
|
|
/* Increase row count */
|
|
(lpSqlNodeSelect->node.select.RowCount)++;
|
|
|
|
/* If there is a GROUP BY, puts the columns in the sort file in */
|
|
/* the order specified by the GROUP BY list. Otherwise put the */
|
|
/* the column in the sort file in the order specified by the */
|
|
/* ORDER BY list. For each sort key value or group by value... */
|
|
if (lpSqlNodeSelect->node.select.ImplicitGroupby) {
|
|
idxGroupbycolumns = NO_SQLNODE;
|
|
idxSortcolumns = NO_SQLNODE;
|
|
}
|
|
else {
|
|
idxGroupbycolumns = lpSqlNodeSelect->node.select.Groupbycolumns;
|
|
if (idxGroupbycolumns == NO_SQLNODE)
|
|
idxSortcolumns = lpSqlNodeSelect->node.select.Sortcolumns;
|
|
else
|
|
idxSortcolumns = NO_SQLNODE;
|
|
}
|
|
while ((idxSortcolumns != NO_SQLNODE) ||
|
|
(idxGroupbycolumns != NO_SQLNODE)) {
|
|
|
|
/* Get next column */
|
|
if (idxGroupbycolumns != NO_SQLNODE) {
|
|
lpSqlNodeGroupbycolumns =
|
|
ToNode(lpstmt->lpSqlStmt, idxGroupbycolumns);
|
|
lpSqlNodeColumn = ToNode(lpstmt->lpSqlStmt,
|
|
lpSqlNodeGroupbycolumns->node.groupbycolumns.Column);
|
|
}
|
|
else {
|
|
lpSqlNodeSortcolumns =
|
|
ToNode(lpstmt->lpSqlStmt, idxSortcolumns);
|
|
lpSqlNodeColumn = ToNode(lpstmt->lpSqlStmt,
|
|
lpSqlNodeSortcolumns->node.sortcolumns.Column);
|
|
}
|
|
|
|
/* Get its value */
|
|
err = EvaluateExpression(lpstmt, lpSqlNodeColumn);
|
|
if (err != ERR_SUCCESS) {
|
|
_lclose_buffer(hfSortfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return err;
|
|
}
|
|
|
|
/* Get length and pointer to key value */
|
|
switch (lpSqlNodeColumn->sqlDataType) {
|
|
case TYPE_DOUBLE:
|
|
case TYPE_INTEGER:
|
|
if (lpSqlNodeColumn->sqlIsNull)
|
|
lpSqlNodeColumn->value.Double = -1.7E308; //RAID 27433
|
|
len = sizeof(double);
|
|
ptr = &(lpSqlNodeColumn->value.Double);
|
|
break;
|
|
case TYPE_NUMERIC:
|
|
if (lpSqlNodeColumn->sqlIsNull)
|
|
(lpSqlNodeColumn->value.String)[0] = 0;
|
|
len = lpSqlNodeColumn->sqlPrecision + 2;
|
|
ptr = lpSqlNodeColumn->value.String;
|
|
|
|
/* (blank pad string values) */
|
|
while (s_lstrlen(lpSqlNodeColumn->value.String) < len)
|
|
s_lstrcat(lpSqlNodeColumn->value.String, " ");
|
|
break;
|
|
case TYPE_CHAR:
|
|
if (lpSqlNodeColumn->sqlIsNull)
|
|
(lpSqlNodeColumn->value.String)[0] = 0;
|
|
len = lpSqlNodeColumn->sqlPrecision;
|
|
ptr = lpSqlNodeColumn->value.String;
|
|
|
|
/* (blank pad string values) */
|
|
while (s_lstrlen(lpSqlNodeColumn->value.String) < len)
|
|
s_lstrcat(lpSqlNodeColumn->value.String, " ");
|
|
break;
|
|
case TYPE_BINARY:
|
|
if (lpSqlNodeColumn->sqlIsNull)
|
|
BINARY_LENGTH(lpSqlNodeColumn->value.Binary) = 0;
|
|
len = lpSqlNodeColumn->sqlPrecision;
|
|
ptr = BINARY_DATA(lpSqlNodeColumn->value.Binary);
|
|
|
|
/* (zero pad binary values) */
|
|
while (BINARY_LENGTH(lpSqlNodeColumn->value.Binary) < len) {
|
|
BINARY_DATA(lpSqlNodeColumn->value.Binary)[
|
|
BINARY_LENGTH(lpSqlNodeColumn->value.Binary)] = 0;
|
|
BINARY_LENGTH(lpSqlNodeColumn->value.Binary) =
|
|
BINARY_LENGTH(lpSqlNodeColumn->value.Binary) + 1;
|
|
}
|
|
break;
|
|
case TYPE_DATE:
|
|
if (lpSqlNodeColumn->sqlIsNull) {
|
|
lpSqlNodeColumn->value.Date.year = 0;
|
|
lpSqlNodeColumn->value.Date.month = 0;
|
|
lpSqlNodeColumn->value.Date.day = 0;
|
|
}
|
|
DateToChar(&(lpSqlNodeColumn->value.Date), (LPUSTR)szBuffer);
|
|
len = s_lstrlen((char*)szBuffer);
|
|
ptr = szBuffer;
|
|
break;
|
|
case TYPE_TIME:
|
|
if (lpSqlNodeColumn->sqlIsNull) {
|
|
lpSqlNodeColumn->value.Time.hour = 0;
|
|
lpSqlNodeColumn->value.Time.minute = 0;
|
|
lpSqlNodeColumn->value.Time.second = 0;
|
|
}
|
|
TimeToChar(&(lpSqlNodeColumn->value.Time), (LPUSTR)szBuffer);
|
|
len = s_lstrlen((char*)szBuffer);
|
|
ptr = szBuffer;
|
|
break;
|
|
case TYPE_TIMESTAMP:
|
|
if (lpSqlNodeColumn->sqlIsNull) {
|
|
lpSqlNodeColumn->value.Timestamp.year = 0;
|
|
lpSqlNodeColumn->value.Timestamp.month = 0;
|
|
lpSqlNodeColumn->value.Timestamp.day = 0;
|
|
lpSqlNodeColumn->value.Timestamp.hour = 0;
|
|
lpSqlNodeColumn->value.Timestamp.minute = 0;
|
|
lpSqlNodeColumn->value.Timestamp.second = 0;
|
|
lpSqlNodeColumn->value.Timestamp.fraction = 0;
|
|
}
|
|
TimestampToChar(&(lpSqlNodeColumn->value.Timestamp), (LPUSTR)szBuffer);
|
|
len = s_lstrlen((char*)szBuffer);
|
|
ptr = szBuffer;
|
|
break;
|
|
default:
|
|
_lclose_buffer(hfSortfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return ERR_INTERNAL;
|
|
}
|
|
|
|
/* Put value into the file */
|
|
if (_lwrite_buffer(hfSortfile, (LPSTR) ptr, (UINT) len) != (UINT) len) {
|
|
_lclose_buffer(hfSortfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return ERR_SORT;
|
|
}
|
|
|
|
/* Put inthe null flag */
|
|
if (lpSqlNodeColumn->sqlIsNull)
|
|
cNullFlag = NULL_FLAG;
|
|
else
|
|
cNullFlag = NOT_NULL_FLAG;
|
|
if (_lwrite_buffer(hfSortfile, (LPSTR) &cNullFlag, 1) != 1) {
|
|
_lclose_buffer(hfSortfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return ERR_SORT;
|
|
}
|
|
|
|
/* Point at next key value */
|
|
if (idxGroupbycolumns != NO_SQLNODE)
|
|
idxGroupbycolumns =
|
|
lpSqlNodeGroupbycolumns->node.groupbycolumns.Next;
|
|
else
|
|
idxSortcolumns = lpSqlNodeSortcolumns->node.sortcolumns.Next;
|
|
}
|
|
|
|
/* Put the AGG function values into the record */
|
|
idxAggregate = lpSqlNodeSelect->node.select.Aggregates;
|
|
while (idxAggregate != NO_SQLNODE) {
|
|
|
|
/* Get next aggregate */
|
|
lpSqlNodeAggregate = ToNode(lpstmt->lpSqlStmt, idxAggregate);
|
|
|
|
/* Get its value */
|
|
if (lpSqlNodeAggregate->node.aggregate.Operator != AGG_COUNT) {
|
|
lpSqlNodeExpression = ToNode(lpstmt->lpSqlStmt,
|
|
lpSqlNodeAggregate->node.aggregate.Expression);
|
|
err = EvaluateExpression(lpstmt, lpSqlNodeExpression);
|
|
if (err != ERR_SUCCESS) {
|
|
_lclose_buffer(hfSortfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return err;
|
|
}
|
|
}
|
|
|
|
/* Get length and pointer to key value */
|
|
switch (lpSqlNodeAggregate->sqlDataType) {
|
|
case TYPE_DOUBLE:
|
|
case TYPE_INTEGER:
|
|
if (lpSqlNodeAggregate->node.aggregate.Operator != AGG_COUNT) {
|
|
if (lpSqlNodeExpression->sqlDataType != TYPE_NUMERIC) {
|
|
if (lpSqlNodeExpression->sqlIsNull)
|
|
lpSqlNodeExpression->value.Double = 0.0;
|
|
len = sizeof(double);
|
|
ptr = &(lpSqlNodeExpression->value.Double);
|
|
}
|
|
else {
|
|
CharToDouble(lpSqlNodeExpression->value.String,
|
|
s_lstrlen(lpSqlNodeExpression->value.String),
|
|
FALSE, -1.7E308, 1.7E308, &dbl);
|
|
len = sizeof(double);
|
|
ptr = &dbl;
|
|
}
|
|
}
|
|
else {
|
|
lpSqlNodeAggregate->value.Double = 1.0;
|
|
len = sizeof(double);
|
|
ptr = &(lpSqlNodeAggregate->value.Double);
|
|
}
|
|
break;
|
|
case TYPE_NUMERIC:
|
|
if (lpSqlNodeExpression->sqlIsNull)
|
|
(lpSqlNodeExpression->value.String)[0] = 0;
|
|
len = lpSqlNodeAggregate->node.aggregate.Length;
|
|
ptr = lpSqlNodeExpression->value.String;
|
|
|
|
/* (blank pad string values) */
|
|
while (s_lstrlen(lpSqlNodeExpression->value.String) < len)
|
|
s_lstrcat(lpSqlNodeExpression->value.String, " ");
|
|
break;
|
|
case TYPE_CHAR:
|
|
if (lpSqlNodeExpression->sqlIsNull)
|
|
(lpSqlNodeExpression->value.String)[0] = 0;
|
|
len = lpSqlNodeAggregate->node.aggregate.Length;
|
|
ptr = lpSqlNodeExpression->value.String;
|
|
|
|
/* (blank pad string values) */
|
|
while (s_lstrlen(lpSqlNodeExpression->value.String) < len)
|
|
s_lstrcat(lpSqlNodeExpression->value.String, " ");
|
|
break;
|
|
case TYPE_BINARY:
|
|
_lclose_buffer(hfSortfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return ERR_INTERNAL;
|
|
case TYPE_DATE:
|
|
if (lpSqlNodeExpression->sqlIsNull) {
|
|
lpSqlNodeExpression->value.Date.year = 0;
|
|
lpSqlNodeExpression->value.Date.month = 0;
|
|
lpSqlNodeExpression->value.Date.day = 0;
|
|
}
|
|
DateToChar(&(lpSqlNodeExpression->value.Date), (LPUSTR)szBuffer);
|
|
len = s_lstrlen((char*)szBuffer);
|
|
ptr = szBuffer;
|
|
break;
|
|
case TYPE_TIME:
|
|
if (lpSqlNodeExpression->sqlIsNull) {
|
|
lpSqlNodeExpression->value.Time.hour = 0;
|
|
lpSqlNodeExpression->value.Time.minute = 0;
|
|
lpSqlNodeExpression->value.Time.second = 0;
|
|
}
|
|
TimeToChar(&(lpSqlNodeExpression->value.Time), (LPUSTR)szBuffer);
|
|
len = s_lstrlen((char*)szBuffer);
|
|
ptr = szBuffer;
|
|
break;
|
|
case TYPE_TIMESTAMP:
|
|
if (lpSqlNodeExpression->sqlIsNull) {
|
|
lpSqlNodeExpression->value.Timestamp.year = 0;
|
|
lpSqlNodeExpression->value.Timestamp.month = 0;
|
|
lpSqlNodeExpression->value.Timestamp.day = 0;
|
|
lpSqlNodeExpression->value.Timestamp.hour = 0;
|
|
lpSqlNodeExpression->value.Timestamp.minute = 0;
|
|
lpSqlNodeExpression->value.Timestamp.second = 0;
|
|
lpSqlNodeExpression->value.Timestamp.fraction = 0;
|
|
}
|
|
TimestampToChar(&(lpSqlNodeExpression->value.Timestamp), (LPUSTR)szBuffer);
|
|
len = s_lstrlen((char*)szBuffer);
|
|
ptr = szBuffer;
|
|
break;
|
|
default:
|
|
_lclose_buffer(hfSortfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return ERR_INTERNAL;
|
|
}
|
|
|
|
/* Put value into the file */
|
|
if (_lwrite_buffer(hfSortfile, (LPSTR) ptr, (UINT) len) != (UINT) len) {
|
|
_lclose_buffer(hfSortfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return ERR_SORT;
|
|
}
|
|
|
|
/* Put inthe null flag */
|
|
if (lpSqlNodeAggregate->node.aggregate.Operator == AGG_COUNT)
|
|
cNullFlag = NOT_NULL_FLAG;
|
|
else if (lpSqlNodeExpression->sqlIsNull)
|
|
cNullFlag = NULL_FLAG;
|
|
else
|
|
cNullFlag = NOT_NULL_FLAG;
|
|
if (_lwrite_buffer(hfSortfile, (LPSTR) &cNullFlag, 1) != 1) {
|
|
_lclose_buffer(hfSortfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return ERR_SORT;
|
|
}
|
|
|
|
/* Point at next value */
|
|
idxAggregate = lpSqlNodeAggregate->node.aggregate.Next;
|
|
}
|
|
|
|
/* For each record in table list... */
|
|
if (lpSqlNodeSelect->node.select.ImplicitGroupby)
|
|
idxTableList = NO_SQLNODE;
|
|
else if (lpSqlNodeSelect->node.select.Groupbycolumns == NO_SQLNODE)
|
|
idxTableList = lpSqlNodeSelect->node.select.Tables;
|
|
else
|
|
idxTableList = NO_SQLNODE;
|
|
while (idxTableList != NO_SQLNODE) {
|
|
|
|
/* Get table */
|
|
lpSqlNodeTableList = ToNode(lpstmt->lpSqlStmt, idxTableList);
|
|
lpSqlNodeTable = ToNode(lpstmt->lpSqlStmt,
|
|
lpSqlNodeTableList->node.tables.Table);
|
|
|
|
/* Get bookmark of current record */
|
|
if (lpSqlNodeTable->node.table.AllNull)
|
|
{
|
|
bookmark.currentRecord = NULL_BOOKMARK;
|
|
}
|
|
else
|
|
{
|
|
err = ISAMGetBookmark(lpSqlNodeTable->node.table.Handle, &bookmark);
|
|
if (err != ERR_SUCCESS) {
|
|
ISAMGetErrorMessage(lpstmt->lpdbc->lpISAM,
|
|
(LPUSTR)lpstmt->szISAMError);
|
|
_lclose_buffer(hfSortfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return err;
|
|
}
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
}
|
|
|
|
/* Put value into the file */
|
|
if (_lwrite_buffer(hfSortfile, (LPSTR) &bookmark, sizeof(ISAMBOOKMARK))
|
|
!= sizeof(ISAMBOOKMARK)) {
|
|
_lclose_buffer(hfSortfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return ERR_SORT;
|
|
}
|
|
|
|
/* Point at next table */
|
|
idxTableList = lpSqlNodeTableList->node.tables.Next;
|
|
}
|
|
}
|
|
if (_lclose_buffer(hfSortfile) == HFILE_ERROR) {
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return ERR_SORT;
|
|
}
|
|
|
|
/* Sort the file */
|
|
if (!(lpSqlNodeSelect->node.select.ImplicitGroupby)) {
|
|
|
|
/* Sort the file */
|
|
s_1mains((LPSTR) szFilename, (LPSTR) szFilename, (LPSTR)
|
|
ToString(lpstmt->lpSqlStmt,
|
|
lpSqlNodeSelect->node.select.SortDirective),
|
|
&recordCount, &sortStatus);
|
|
if (sortStatus != 0) {
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return ERR_SORT;
|
|
}
|
|
lpSqlNodeSelect->node.select.RowCount = recordCount;
|
|
}
|
|
|
|
/* Open destination file */
|
|
hfSortfile2 = _lopen((LPCSTR) szFilename, OF_READ);
|
|
if (hfSortfile2 == HFILE_ERROR) {
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return ERR_SORT;
|
|
}
|
|
|
|
/* Save name and handle to file of sorted records */
|
|
s_lstrcpy(ToString(lpstmt->lpSqlStmt,
|
|
lpSqlNodeSelect->node.select.SortfileName), szFilename);
|
|
lpSqlNodeSelect->node.select.Sortfile = hfSortfile2;
|
|
lpSqlNodeSelect->node.select.ReturningDistinct = FALSE;
|
|
|
|
/* Set up to read first row */
|
|
lpSqlNodeSelect->node.select.CurrentRow = BEFORE_FIRST_ROW;
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC GroupBy(LPSTMT lpstmt, LPSQLNODE lpSqlNodeSelect)
|
|
|
|
/* Does GROUP BY on results of a SELECT statement */
|
|
|
|
{
|
|
#ifdef WIN32
|
|
UCHAR szTempDir[MAX_PATHNAME_SIZE+1];
|
|
szTempDir[0] = 0;
|
|
#endif
|
|
UCHAR szFilename[MAX_PATHNAME_SIZE+1];
|
|
szFilename[0] = 0;
|
|
HFILE_BUFFER hfGroupbyfile;
|
|
HFILE hfGroupbyfile2;
|
|
HGLOBAL hGlobal;
|
|
LPUSTR lpResultRecord;
|
|
LPUSTR lpCurrentRecord;
|
|
SDWORD cRowCount;
|
|
SDWORD irow;
|
|
SQLNODEIDX idxAggregate;
|
|
LPSQLNODE lpSqlNodeAggregate;
|
|
LPUSTR lpValue;
|
|
LPUSTR lpValueNullFlag;
|
|
LPUSTR lpAggregateValue;
|
|
LPUSTR lpAggregateValueNullFlag;
|
|
|
|
/* If nothing to group, just return */
|
|
if (lpSqlNodeSelect->node.select.RowCount == 0)
|
|
return ERR_SUCCESS;
|
|
|
|
/* Create temporary file for the result of the group by */
|
|
#ifdef WIN32
|
|
if (!GetTempPath(MAX_PATHNAME_SIZE+1, (LPSTR)szTempDir))
|
|
return ERR_GROUPBY;
|
|
if (!GetTempFileName((LPSTR)szTempDir, "LEM", 0, (LPSTR)szFilename))
|
|
return ERR_GROUPBY;
|
|
#else
|
|
GetTempFileName(NULL, "LEM", 0, (LPSTR) szFilename);
|
|
#endif
|
|
hfGroupbyfile = _lcreat_buffer((LPCSTR) szFilename, 0);
|
|
if (hfGroupbyfile == NULL)
|
|
return ERR_GROUPBY;
|
|
|
|
/* Allocate space for a result record buffer */
|
|
hGlobal = GlobalAlloc(GMEM_MOVEABLE,
|
|
(2 * lpSqlNodeSelect->node.select.SortRecordsize));
|
|
if (hGlobal == NULL ||
|
|
(lpResultRecord = (LPUSTR) GlobalLock(hGlobal)) == NULL) {
|
|
if (hGlobal)
|
|
GlobalFree(hGlobal);
|
|
|
|
_lclose_buffer(hfGroupbyfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return ERR_MEMALLOCFAIL;
|
|
}
|
|
|
|
/* Allocate space for current record key buffer */
|
|
lpCurrentRecord = lpResultRecord + lpSqlNodeSelect->node.select.SortRecordsize;
|
|
|
|
/* Position to the first record */
|
|
if (_llseek(lpSqlNodeSelect->node.select.Sortfile, 0, 0) == HFILE_ERROR) {
|
|
GlobalUnlock(hGlobal);
|
|
GlobalFree(hGlobal);
|
|
_lclose_buffer(hfGroupbyfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return ERR_GROUPBY;
|
|
}
|
|
|
|
/* Read the first record into the result record buffer */
|
|
if (_lread(lpSqlNodeSelect->node.select.Sortfile, lpResultRecord,
|
|
(UINT) lpSqlNodeSelect->node.select.SortRecordsize) !=
|
|
(UINT) lpSqlNodeSelect->node.select.SortRecordsize) {
|
|
GlobalUnlock(hGlobal);
|
|
GlobalFree(hGlobal);
|
|
_lclose_buffer(hfGroupbyfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return ERR_GROUPBY;
|
|
}
|
|
|
|
/* Initialize the count for averages */
|
|
idxAggregate = lpSqlNodeSelect->node.select.Aggregates;
|
|
while (idxAggregate != NO_SQLNODE) {
|
|
|
|
/* Get the function */
|
|
lpSqlNodeAggregate = ToNode(lpstmt->lpSqlStmt, idxAggregate);
|
|
|
|
/* Average operator? */
|
|
if (lpSqlNodeAggregate->node.aggregate.Operator == AGG_AVG) {
|
|
|
|
/* Yes. Point to the value */
|
|
lpAggregateValue = lpResultRecord +
|
|
lpSqlNodeAggregate->node.aggregate.Offset - 1;
|
|
lpAggregateValueNullFlag = lpAggregateValue +
|
|
lpSqlNodeAggregate->node.aggregate.Length;
|
|
|
|
/* If not null, set count to one. Otherwise zero */
|
|
switch (*lpAggregateValueNullFlag) {
|
|
case NULL_FLAG:
|
|
lpSqlNodeAggregate->node.aggregate.Count = 0.0;
|
|
break;
|
|
case NOT_NULL_FLAG:
|
|
lpSqlNodeAggregate->node.aggregate.Count = 1.0;
|
|
break;
|
|
default:
|
|
GlobalUnlock(hGlobal);
|
|
GlobalFree(hGlobal);
|
|
_lclose_buffer(hfGroupbyfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return ERR_GROUPBY;
|
|
}
|
|
}
|
|
|
|
/* Do next aggregate */
|
|
idxAggregate = lpSqlNodeAggregate->node.aggregate.Next;
|
|
}
|
|
|
|
/* For each record... */
|
|
cRowCount = 0;
|
|
for (irow = 1; irow < lpSqlNodeSelect->node.select.RowCount; irow++) {
|
|
|
|
/* Read current record */
|
|
if (_lread(lpSqlNodeSelect->node.select.Sortfile, lpCurrentRecord,
|
|
(UINT) lpSqlNodeSelect->node.select.SortRecordsize) !=
|
|
(UINT) lpSqlNodeSelect->node.select.SortRecordsize) {
|
|
GlobalUnlock(hGlobal);
|
|
GlobalFree(hGlobal);
|
|
_lclose_buffer(hfGroupbyfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return ERR_GROUPBY;
|
|
}
|
|
|
|
/* Does group by key match the key in the result record buffer? */
|
|
if (_fmemcmp(lpResultRecord, lpCurrentRecord,
|
|
(size_t) lpSqlNodeSelect->node.select.SortKeysize)) {
|
|
|
|
/* No. Calculate averages */
|
|
idxAggregate = lpSqlNodeSelect->node.select.Aggregates;
|
|
while (idxAggregate != NO_SQLNODE) {
|
|
|
|
/* Get the function */
|
|
lpSqlNodeAggregate = ToNode(lpstmt->lpSqlStmt, idxAggregate);
|
|
|
|
/* Average operator? */
|
|
if (lpSqlNodeAggregate->node.aggregate.Operator == AGG_AVG) {
|
|
|
|
/* Yes. Point to the value */
|
|
lpAggregateValue = lpResultRecord +
|
|
lpSqlNodeAggregate->node.aggregate.Offset - 1;
|
|
lpAggregateValueNullFlag = lpAggregateValue +
|
|
lpSqlNodeAggregate->node.aggregate.Length;
|
|
|
|
/* If not null, calculate average */
|
|
if (*lpAggregateValueNullFlag == NOT_NULL_FLAG)
|
|
*((double FAR *) lpAggregateValue) /=
|
|
(lpSqlNodeAggregate->node.aggregate.Count);
|
|
}
|
|
|
|
/* Do next aggregate */
|
|
idxAggregate = lpSqlNodeAggregate->node.aggregate.Next;
|
|
}
|
|
|
|
/* Write result record buffer to the file */
|
|
if (_lwrite_buffer(hfGroupbyfile, (LPSTR) lpResultRecord,
|
|
(UINT) lpSqlNodeSelect->node.select.SortRecordsize) !=
|
|
(UINT) lpSqlNodeSelect->node.select.SortRecordsize) {
|
|
GlobalUnlock(hGlobal);
|
|
GlobalFree(hGlobal);
|
|
_lclose_buffer(hfGroupbyfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return ERR_GROUPBY;
|
|
}
|
|
|
|
/* Increase count of number of records written */
|
|
cRowCount++;
|
|
|
|
/* Copy current record to the result record buffer */
|
|
_fmemcpy(lpResultRecord, lpCurrentRecord,
|
|
(size_t) lpSqlNodeSelect->node.select.SortRecordsize);
|
|
|
|
/* Initialize the count for averages */
|
|
idxAggregate = lpSqlNodeSelect->node.select.Aggregates;
|
|
while (idxAggregate != NO_SQLNODE) {
|
|
|
|
/* Get the function */
|
|
lpSqlNodeAggregate = ToNode(lpstmt->lpSqlStmt, idxAggregate);
|
|
|
|
/* Average operator? */
|
|
if (lpSqlNodeAggregate->node.aggregate.Operator == AGG_AVG) {
|
|
|
|
/* Yes. Point to the value */
|
|
lpAggregateValue = lpResultRecord +
|
|
lpSqlNodeAggregate->node.aggregate.Offset - 1;
|
|
lpAggregateValueNullFlag = lpAggregateValue +
|
|
lpSqlNodeAggregate->node.aggregate.Length;
|
|
|
|
/* If not null, set count to one. Otherwise zero */
|
|
switch (*lpAggregateValueNullFlag) {
|
|
case NULL_FLAG:
|
|
lpSqlNodeAggregate->node.aggregate.Count = 0.0;
|
|
break;
|
|
case NOT_NULL_FLAG:
|
|
lpSqlNodeAggregate->node.aggregate.Count = 1.0;
|
|
break;
|
|
default:
|
|
GlobalUnlock(hGlobal);
|
|
GlobalFree(hGlobal);
|
|
_lclose_buffer(hfGroupbyfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return ERR_GROUPBY;
|
|
}
|
|
}
|
|
|
|
/* Do next aggregate */
|
|
idxAggregate = lpSqlNodeAggregate->node.aggregate.Next;
|
|
}
|
|
}
|
|
else {
|
|
|
|
/* Yes. For each aggregate function... */
|
|
idxAggregate = lpSqlNodeSelect->node.select.Aggregates;
|
|
while (idxAggregate != NO_SQLNODE) {
|
|
|
|
/* Get the function */
|
|
lpSqlNodeAggregate = ToNode(lpstmt->lpSqlStmt, idxAggregate);
|
|
|
|
/* Point to the value */
|
|
lpValue = lpCurrentRecord +
|
|
lpSqlNodeAggregate->node.aggregate.Offset - 1;
|
|
lpValueNullFlag = lpValue +
|
|
lpSqlNodeAggregate->node.aggregate.Length;
|
|
lpAggregateValue = lpResultRecord +
|
|
lpSqlNodeAggregate->node.aggregate.Offset - 1;
|
|
lpAggregateValueNullFlag = lpAggregateValue +
|
|
lpSqlNodeAggregate->node.aggregate.Length;
|
|
|
|
/* Null value? */
|
|
if (*lpValueNullFlag == NOT_NULL_FLAG) {
|
|
|
|
/* No. Is aggregate value null? */
|
|
if (*lpAggregateValueNullFlag == NOT_NULL_FLAG) {
|
|
|
|
/* No. Is a TYPE_NUMERIC involved? */
|
|
if (lpSqlNodeAggregate->sqlDataType != TYPE_NUMERIC) {
|
|
|
|
/* No. Incorporate field value into aggregate */
|
|
switch (lpSqlNodeAggregate->node.aggregate.Operator) {
|
|
case AGG_AVG:
|
|
case AGG_SUM:
|
|
switch (lpSqlNodeAggregate->sqlDataType) {
|
|
case TYPE_DOUBLE:
|
|
case TYPE_INTEGER:
|
|
*((double FAR *) lpAggregateValue) +=
|
|
(*((double FAR *) lpValue));
|
|
break;
|
|
case TYPE_NUMERIC:
|
|
case TYPE_CHAR:
|
|
case TYPE_DATE:
|
|
case TYPE_TIME:
|
|
case TYPE_TIMESTAMP:
|
|
case TYPE_BINARY:
|
|
default:
|
|
GlobalUnlock(hGlobal);
|
|
GlobalFree(hGlobal);
|
|
_lclose_buffer(hfGroupbyfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return ERR_INTERNAL;
|
|
}
|
|
|
|
/* Increase count */
|
|
if (lpSqlNodeAggregate->node.aggregate.Operator == AGG_AVG)
|
|
lpSqlNodeAggregate->node.aggregate.Count += (1.0);
|
|
|
|
break;
|
|
case AGG_COUNT:
|
|
*((double FAR *) lpAggregateValue) += (1.0);
|
|
break;
|
|
case AGG_MAX:
|
|
switch (lpSqlNodeAggregate->sqlDataType) {
|
|
case TYPE_DOUBLE:
|
|
case TYPE_INTEGER:
|
|
if (*((double FAR *) lpAggregateValue) <
|
|
*((double FAR *) lpValue))
|
|
*((double FAR *) lpAggregateValue) =
|
|
*((double FAR *) lpValue);
|
|
break;
|
|
case TYPE_CHAR:
|
|
case TYPE_DATE:
|
|
case TYPE_TIME:
|
|
case TYPE_TIMESTAMP:
|
|
if (_fmemcmp(lpValue,
|
|
lpAggregateValue, (size_t)
|
|
lpSqlNodeAggregate->node.aggregate.Length)
|
|
> 0)
|
|
_fmemcpy(lpAggregateValue,
|
|
lpValue, (size_t)
|
|
lpSqlNodeAggregate->node.aggregate.Length);
|
|
break;
|
|
case TYPE_NUMERIC:
|
|
case TYPE_BINARY:
|
|
default:
|
|
GlobalUnlock(hGlobal);
|
|
GlobalFree(hGlobal);
|
|
_lclose_buffer(hfGroupbyfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return ERR_INTERNAL;
|
|
}
|
|
break;
|
|
case AGG_MIN:
|
|
switch (lpSqlNodeAggregate->sqlDataType) {
|
|
case TYPE_DOUBLE:
|
|
case TYPE_INTEGER:
|
|
if (*((double FAR *) lpAggregateValue) >
|
|
*((double FAR *) lpValue))
|
|
*((double FAR *) lpAggregateValue) =
|
|
*((double FAR *) lpValue);
|
|
break;
|
|
case TYPE_CHAR:
|
|
case TYPE_DATE:
|
|
case TYPE_TIME:
|
|
case TYPE_TIMESTAMP:
|
|
if (_fmemcmp(lpValue,
|
|
lpAggregateValue, (size_t)
|
|
lpSqlNodeAggregate->node.aggregate.Length)
|
|
< 0)
|
|
_fmemcpy(lpAggregateValue,
|
|
lpValue, (size_t)
|
|
lpSqlNodeAggregate->node.aggregate.Length);
|
|
break;
|
|
case TYPE_NUMERIC:
|
|
case TYPE_BINARY:
|
|
default:
|
|
GlobalUnlock(hGlobal);
|
|
GlobalFree(hGlobal);
|
|
_lclose_buffer(hfGroupbyfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return ERR_INTERNAL;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
|
|
SQLNODE left;
|
|
SQLNODE right;
|
|
SQLNODE result;
|
|
/* These buffers must be large enough to */
|
|
/* accomodate the largest SQL_NUMERIC or */
|
|
/* SQL_DECIMAL */
|
|
UCHAR szBufferLeft[64];
|
|
UCHAR szBufferRight[64];
|
|
UWORD len;
|
|
UCHAR szBufferResult[64];
|
|
RETCODE err;
|
|
|
|
/* Yes. Incorporate field value into aggregate */
|
|
switch (lpSqlNodeAggregate->node.aggregate.Operator) {
|
|
case AGG_AVG:
|
|
case AGG_COUNT:
|
|
GlobalUnlock(hGlobal);
|
|
GlobalFree(hGlobal);
|
|
_lclose_buffer(hfGroupbyfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return ERR_INTERNAL;
|
|
|
|
case AGG_MAX:
|
|
|
|
/* Create left value */
|
|
left = *lpSqlNodeAggregate;
|
|
_fmemcpy(szBufferLeft, lpAggregateValue,
|
|
(size_t)
|
|
lpSqlNodeAggregate->node.aggregate.Length);
|
|
szBufferLeft[lpSqlNodeAggregate->node.
|
|
aggregate.Length] = '\0';
|
|
len = (UWORD) s_lstrlen(szBufferLeft);
|
|
while ((len > 0) &&
|
|
(szBufferLeft[len-1] == ' ')) {
|
|
szBufferLeft[len-1] = '\0';
|
|
len--;
|
|
}
|
|
left.value.String = (LPUSTR)szBufferLeft;
|
|
|
|
/* Create right value */
|
|
right = *lpSqlNodeAggregate;
|
|
_fmemcpy(szBufferRight, lpValue, (size_t)
|
|
lpSqlNodeAggregate->node.aggregate.Length);
|
|
szBufferRight[lpSqlNodeAggregate->node.
|
|
aggregate.Length] = '\0';
|
|
len = (UWORD) s_lstrlen(szBufferRight);
|
|
while ((len > 0) &&
|
|
(szBufferRight[len-1] == ' ')) {
|
|
szBufferRight[len-1] = '\0';
|
|
len--;
|
|
}
|
|
right.value.String = (LPUSTR)szBufferRight;
|
|
|
|
/* Compare the values */
|
|
result.sqlNodeType = NODE_TYPE_COMPARISON;
|
|
result.node.comparison.Operator = OP_LT;
|
|
result.node.comparison.SelectModifier = SELECT_NOTSELECT;
|
|
result.node.comparison.Left = NO_SQLNODE;
|
|
result.node.comparison.Right = NO_SQLNODE;
|
|
result.node.comparison.fSelectivity = 0;
|
|
result.node.comparison.NextRestrict = NO_SQLNODE;
|
|
result.sqlDataType = TYPE_INTEGER;
|
|
result.sqlSqlType = SQL_BIT;
|
|
result.sqlPrecision = 1;
|
|
result.sqlScale = 0;
|
|
err = NumericCompare(&result, &left,
|
|
OP_LT, &right);
|
|
if (err != ERR_SUCCESS) {
|
|
GlobalUnlock(hGlobal);
|
|
GlobalFree(hGlobal);
|
|
_lclose_buffer(hfGroupbyfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return err;
|
|
}
|
|
|
|
/* If this value is bigger, save it */
|
|
if (result.value.Double == TRUE)
|
|
_fmemcpy(lpAggregateValue, lpValue,
|
|
(size_t)
|
|
lpSqlNodeAggregate->node.aggregate.Length);
|
|
break;
|
|
|
|
case AGG_MIN:
|
|
|
|
/* Create left value */
|
|
left = *lpSqlNodeAggregate;
|
|
_fmemcpy(szBufferLeft, lpAggregateValue,
|
|
(size_t)
|
|
lpSqlNodeAggregate->node.aggregate.Length);
|
|
szBufferLeft[lpSqlNodeAggregate->node.
|
|
aggregate.Length] = '\0';
|
|
len = (UWORD) s_lstrlen(szBufferLeft);
|
|
while ((len > 0) &&
|
|
(szBufferLeft[len-1] == ' ')) {
|
|
szBufferLeft[len-1] = '\0';
|
|
len--;
|
|
}
|
|
left.value.String = (LPUSTR)szBufferLeft;
|
|
|
|
/* Create right value */
|
|
right = *lpSqlNodeAggregate;
|
|
_fmemcpy(szBufferRight, lpValue, (size_t)
|
|
lpSqlNodeAggregate->node.aggregate.Length);
|
|
szBufferRight[lpSqlNodeAggregate->node.
|
|
aggregate.Length] = '\0';
|
|
len = (UWORD) s_lstrlen(szBufferRight);
|
|
while ((len > 0) &&
|
|
(szBufferRight[len-1] == ' ')) {
|
|
szBufferRight[len-1] = '\0';
|
|
len--;
|
|
}
|
|
right.value.String = (LPUSTR)szBufferRight;
|
|
|
|
/* Compare the values */
|
|
result.sqlNodeType = NODE_TYPE_COMPARISON;
|
|
result.node.comparison.Operator = OP_GT;
|
|
result.node.comparison.SelectModifier = SELECT_NOTSELECT;
|
|
result.node.comparison.Left = NO_SQLNODE;
|
|
result.node.comparison.Right = NO_SQLNODE;
|
|
result.node.comparison.fSelectivity = 0;
|
|
result.node.comparison.NextRestrict = NO_SQLNODE;
|
|
result.sqlDataType = TYPE_INTEGER;
|
|
result.sqlSqlType = SQL_BIT;
|
|
result.sqlPrecision = 1;
|
|
result.sqlScale = 0;
|
|
err = NumericCompare(&result, &left,
|
|
OP_GT, &right);
|
|
if (err != ERR_SUCCESS) {
|
|
GlobalUnlock(hGlobal);
|
|
GlobalFree(hGlobal);
|
|
_lclose_buffer(hfGroupbyfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return err;
|
|
}
|
|
|
|
/* If this value is smaller, save it */
|
|
if (result.value.Double == TRUE)
|
|
_fmemcpy(lpAggregateValue, lpValue,
|
|
(size_t)
|
|
lpSqlNodeAggregate->node.aggregate.Length);
|
|
break;
|
|
|
|
case AGG_SUM:
|
|
|
|
/* Create left value */
|
|
left = *lpSqlNodeAggregate;
|
|
_fmemcpy(szBufferLeft, lpAggregateValue,
|
|
(size_t)
|
|
lpSqlNodeAggregate->node.aggregate.Length);
|
|
szBufferLeft[lpSqlNodeAggregate->node.
|
|
aggregate.Length] = '\0';
|
|
len = (UWORD) s_lstrlen(szBufferLeft);
|
|
while ((len > 0) &&
|
|
(szBufferLeft[len-1] == ' ')) {
|
|
szBufferLeft[len-1] = '\0';
|
|
len--;
|
|
}
|
|
left.value.String = (LPUSTR)szBufferLeft;
|
|
|
|
/* Create right value */
|
|
right = *lpSqlNodeAggregate;
|
|
_fmemcpy(szBufferRight, lpValue, (size_t)
|
|
lpSqlNodeAggregate->node.aggregate.Length);
|
|
szBufferRight[lpSqlNodeAggregate->node.
|
|
aggregate.Length] = '\0';
|
|
len = (UWORD) s_lstrlen(szBufferRight);
|
|
while ((len > 0) &&
|
|
(szBufferRight[len-1] == ' ')) {
|
|
szBufferRight[len-1] = '\0';
|
|
len--;
|
|
}
|
|
right.value.String = (LPUSTR)szBufferRight;
|
|
|
|
/* Add the left value to the right */
|
|
result = *lpSqlNodeAggregate;
|
|
result.value.String = (LPUSTR)szBufferResult;
|
|
err = NumericAlgebra(&result, &left,
|
|
OP_PLUS, &right, NULL, NULL, NULL);
|
|
if (err != ERR_SUCCESS) {
|
|
GlobalUnlock(hGlobal);
|
|
GlobalFree(hGlobal);
|
|
_lclose_buffer(hfGroupbyfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return err;
|
|
}
|
|
|
|
/* Save the result */
|
|
while (s_lstrlen(szBufferResult) < (SDWORD)
|
|
lpSqlNodeAggregate->node.aggregate.Length)
|
|
s_lstrcat(szBufferResult, " ");
|
|
_fmemcpy(lpAggregateValue, szBufferResult,
|
|
(size_t)
|
|
lpSqlNodeAggregate->node.aggregate.Length);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
else {
|
|
|
|
/* Yes. Copy value from current record */
|
|
_fmemcpy(lpAggregateValue, lpValue, (size_t)
|
|
lpSqlNodeAggregate->node.aggregate.Length);
|
|
*lpAggregateValueNullFlag = NOT_NULL_FLAG;
|
|
|
|
/* Initilize average count */
|
|
if (lpSqlNodeAggregate->node.aggregate.Operator == AGG_AVG)
|
|
lpSqlNodeAggregate->node.aggregate.Count = 1.0;
|
|
}
|
|
}
|
|
|
|
/* Do next aggregate */
|
|
idxAggregate = lpSqlNodeAggregate->node.aggregate.Next;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Calculate averages */
|
|
idxAggregate = lpSqlNodeSelect->node.select.Aggregates;
|
|
while (idxAggregate != NO_SQLNODE) {
|
|
|
|
/* Get the function */
|
|
lpSqlNodeAggregate = ToNode(lpstmt->lpSqlStmt, idxAggregate);
|
|
|
|
/* Average operator? */
|
|
if (lpSqlNodeAggregate->node.aggregate.Operator == AGG_AVG) {
|
|
|
|
/* Yes. Point to the value */
|
|
lpAggregateValue = lpResultRecord +
|
|
lpSqlNodeAggregate->node.aggregate.Offset - 1;
|
|
lpAggregateValueNullFlag = lpAggregateValue +
|
|
lpSqlNodeAggregate->node.aggregate.Length;
|
|
|
|
/* If not null, calculate average */
|
|
if (*lpAggregateValueNullFlag == NOT_NULL_FLAG)
|
|
*((double FAR *) lpAggregateValue) /=
|
|
(lpSqlNodeAggregate->node.aggregate.Count);
|
|
}
|
|
|
|
/* Do next aggregate */
|
|
idxAggregate = lpSqlNodeAggregate->node.aggregate.Next;
|
|
}
|
|
|
|
/* Copy last record to the result record buffer */
|
|
if (_lwrite_buffer(hfGroupbyfile, (LPSTR) lpResultRecord,
|
|
(UINT) lpSqlNodeSelect->node.select.SortRecordsize) !=
|
|
(UINT) lpSqlNodeSelect->node.select.SortRecordsize) {
|
|
GlobalUnlock(hGlobal);
|
|
GlobalFree(hGlobal);
|
|
_lclose_buffer(hfGroupbyfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return ERR_GROUPBY;
|
|
}
|
|
|
|
/* Increase count of number of records written */
|
|
cRowCount++;
|
|
|
|
/* Free buffers */
|
|
GlobalUnlock(hGlobal);
|
|
GlobalFree(hGlobal);
|
|
|
|
/* Close the file */
|
|
if (_lclose_buffer(hfGroupbyfile) == HFILE_ERROR) {
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return ERR_GROUPBY;
|
|
}
|
|
|
|
/* Reopen the file for reading */
|
|
hfGroupbyfile2 = _lopen((LPCSTR) szFilename, OF_READ);
|
|
if (hfGroupbyfile2 == HFILE_ERROR) {
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return ERR_GROUPBY;
|
|
}
|
|
|
|
/* Remove the sort file */
|
|
_lclose(lpSqlNodeSelect->node.select.Sortfile);
|
|
DeleteFile((LPCSTR) ToString(lpstmt->lpSqlStmt,
|
|
lpSqlNodeSelect->node.select.SortfileName));
|
|
|
|
|
|
/* Save name and handle to file of group by records */
|
|
s_lstrcpy(ToString(lpstmt->lpSqlStmt,
|
|
lpSqlNodeSelect->node.select.SortfileName), szFilename);
|
|
lpSqlNodeSelect->node.select.Sortfile = hfGroupbyfile2;
|
|
lpSqlNodeSelect->node.select.ReturningDistinct = FALSE;
|
|
|
|
/* Save total number of records in the group by file */
|
|
lpSqlNodeSelect->node.select.RowCount = cRowCount;
|
|
|
|
/* Set up to read first row */
|
|
lpSqlNodeSelect->node.select.CurrentRow = BEFORE_FIRST_ROW;
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC Distinct(LPSTMT lpstmt, LPSQLNODE lpSqlNodeSelect)
|
|
|
|
/* Generates results for SELECT DISTINCT */
|
|
|
|
{
|
|
// ODBCTRACE("\nWBEM ODBC Driver : Distinct\n");
|
|
|
|
UCHAR szTempDir[MAX_PATHNAME_SIZE+1];
|
|
szTempDir[0] = 0;
|
|
|
|
UCHAR szFilename[MAX_PATHNAME_SIZE+1];
|
|
UCHAR szFilename2[MAX_PATHNAME_SIZE+1];
|
|
szFilename[0] = 0;
|
|
szFilename2[0] = 0;
|
|
HFILE_BUFFER hfSortfile;
|
|
HFILE hfSortfile2;
|
|
LPSQLNODE lpSqlNodeTables;
|
|
LPSQLNODE lpSqlNodePredicate;
|
|
LPSQLNODE lpSqlNodeHaving;
|
|
RETCODE err;
|
|
SQLNODEIDX idxTableList;
|
|
LPSQLNODE lpSqlNodeTableList;
|
|
LPSQLNODE lpSqlNodeTable;
|
|
ISAMBOOKMARK bookmark;
|
|
SQLNODEIDX idxValues;
|
|
LPSQLNODE lpSqlNodeValues;
|
|
LPSQLNODE lpSqlNodeValue;
|
|
SDWORD len;
|
|
PTR ptr;
|
|
#define NUM_LEN 5
|
|
UCHAR szFormat[16];
|
|
UCHAR szBuffer[20+TIMESTAMP_SCALE+1];
|
|
UCHAR szSortDirective[64 + 3 + MAX_PATHNAME_SIZE];
|
|
long recordCount;
|
|
int sortStatus;
|
|
UCHAR cNullFlag;
|
|
BOOL fGotOne;
|
|
|
|
szBuffer[0] = 0;
|
|
|
|
/* Create temporary file */
|
|
#ifdef WIN32
|
|
if (!GetTempPath(MAX_PATHNAME_SIZE+1, (LPSTR)szTempDir))
|
|
return ERR_SORT;
|
|
if (!GetTempFileName((LPSTR)szTempDir, "LEM", 0, (LPSTR)szFilename))
|
|
return ERR_SORT;
|
|
#else
|
|
GetTempFileName(NULL, "LEM", 0, (LPSTR) szFilename);
|
|
#endif
|
|
hfSortfile = _lcreat_buffer((LPCSTR) szFilename, 0);
|
|
if (hfSortfile == NULL)
|
|
return ERR_SORT;
|
|
|
|
/* For each record of result set... */
|
|
lpSqlNodeTables = ToNode(lpstmt->lpSqlStmt,
|
|
lpSqlNodeSelect->node.select.Tables);
|
|
if (lpSqlNodeSelect->node.select.Predicate == NO_SQLNODE)
|
|
lpSqlNodePredicate = NULL;
|
|
else
|
|
lpSqlNodePredicate = ToNode(lpstmt->lpSqlStmt,
|
|
lpSqlNodeSelect->node.select.Predicate);
|
|
if (lpSqlNodeSelect->node.select.Having == NO_SQLNODE)
|
|
lpSqlNodeHaving = NULL;
|
|
else
|
|
lpSqlNodeHaving = ToNode(lpstmt->lpSqlStmt,
|
|
lpSqlNodeSelect->node.select.Having);
|
|
wsprintf((LPSTR) szFormat, "%%0%dd", (UWORD) NUM_LEN);
|
|
while (TRUE) {
|
|
|
|
/* Is there a sort file to read from? */
|
|
if (lpSqlNodeSelect->node.select.Sortfile == HFILE_ERROR) {
|
|
|
|
/* No. Get the next record */
|
|
try
|
|
{
|
|
err = NextRecord(lpstmt, lpSqlNodeTables, lpSqlNodePredicate);
|
|
}
|
|
catch(...)
|
|
{
|
|
_lclose_buffer(hfSortfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
|
|
return ERR_MEMALLOCFAIL;
|
|
}
|
|
|
|
if (err == ERR_NODATAFOUND) {
|
|
lpSqlNodeSelect->node.select.CurrentRow = AFTER_LAST_ROW;
|
|
break;
|
|
}
|
|
else if (err != ERR_SUCCESS) {
|
|
_lclose_buffer(hfSortfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return err;
|
|
}
|
|
|
|
/* Set row flag */
|
|
if (lpSqlNodeSelect->node.select.CurrentRow == BEFORE_FIRST_ROW)
|
|
lpSqlNodeSelect->node.select.CurrentRow = 0;
|
|
else
|
|
lpSqlNodeSelect->node.select.CurrentRow++;
|
|
}
|
|
else {
|
|
|
|
/* Yes. Look for next record in sort file */
|
|
while (TRUE) {
|
|
|
|
/* Set row flag */
|
|
if (lpSqlNodeSelect->node.select.CurrentRow == BEFORE_FIRST_ROW) {
|
|
if (lpSqlNodeSelect->node.select.RowCount != 0) {
|
|
lpSqlNodeSelect->node.select.CurrentRow = 0;
|
|
}
|
|
else {
|
|
lpSqlNodeSelect->node.select.CurrentRow = AFTER_LAST_ROW;
|
|
break;
|
|
}
|
|
}
|
|
else if (lpSqlNodeSelect->node.select.CurrentRow ==
|
|
lpSqlNodeSelect->node.select.RowCount-1) {
|
|
lpSqlNodeSelect->node.select.CurrentRow = AFTER_LAST_ROW;
|
|
break;
|
|
}
|
|
else
|
|
lpSqlNodeSelect->node.select.CurrentRow++;
|
|
|
|
/* If no HAVING clause, this record qualifies */
|
|
if (lpSqlNodeHaving == NULL)
|
|
break;
|
|
|
|
/* If HAVING condition is satisfied, this record qualifies */
|
|
err = EvaluateExpression(lpstmt, lpSqlNodeHaving);
|
|
if (err != ERR_SUCCESS) {
|
|
_lclose_buffer(hfSortfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return err;
|
|
}
|
|
if (!(lpSqlNodeHaving->sqlIsNull) &&
|
|
(lpSqlNodeHaving->value.Double == TRUE))
|
|
break;
|
|
}
|
|
if (lpSqlNodeSelect->node.select.CurrentRow == AFTER_LAST_ROW)
|
|
break;
|
|
|
|
/* Is there a group by? */
|
|
if ((lpSqlNodeSelect->node.select.Groupbycolumns == NO_SQLNODE) &&
|
|
(!lpSqlNodeSelect->node.select.ImplicitGroupby)) {
|
|
|
|
/* No. Position to bookmarks in that row */
|
|
if (_llseek(lpSqlNodeSelect->node.select.Sortfile,
|
|
(lpSqlNodeSelect->node.select.CurrentRow *
|
|
lpSqlNodeSelect->node.select.SortRecordsize) +
|
|
lpSqlNodeSelect->node.select.SortBookmarks - 1, 0)
|
|
== HFILE_ERROR) {
|
|
_lclose_buffer(hfSortfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return ERR_SORT;
|
|
}
|
|
|
|
/* For each table... */
|
|
idxTableList = lpSqlNodeSelect->node.select.Tables;
|
|
while (idxTableList != NO_SQLNODE) {
|
|
|
|
/* Get the table node */
|
|
lpSqlNodeTableList = ToNode(lpstmt->lpSqlStmt,
|
|
idxTableList);
|
|
lpSqlNodeTable = ToNode(lpstmt->lpSqlStmt,
|
|
lpSqlNodeTableList->node.tables.Table);
|
|
|
|
/* Read the bookmark for it */
|
|
if (_lread(lpSqlNodeSelect->node.select.Sortfile,
|
|
&bookmark, sizeof(ISAMBOOKMARK)) !=
|
|
sizeof(ISAMBOOKMARK)) {
|
|
_lclose_buffer(hfSortfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return ERR_SORT;
|
|
}
|
|
|
|
/* Position to that record */
|
|
if (bookmark.currentRecord == NULL_BOOKMARK)
|
|
{
|
|
lpSqlNodeTable->node.table.AllNull = TRUE;
|
|
}
|
|
else
|
|
{
|
|
lpSqlNodeTable->node.table.AllNull = FALSE;
|
|
err = ISAMPosition(lpSqlNodeTable->node.table.Handle, &bookmark);
|
|
if (err != NO_ISAM_ERR) {
|
|
ISAMGetErrorMessage(lpstmt->lpdbc->lpISAM,
|
|
(LPUSTR) lpstmt->szISAMError);
|
|
_lclose_buffer(hfSortfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return err;
|
|
}
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
}
|
|
|
|
/* Point at next table */
|
|
idxTableList = lpSqlNodeTableList->node.tables.Next;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Leave if no more records */
|
|
if (lpSqlNodeSelect->node.select.CurrentRow == AFTER_LAST_ROW)
|
|
break;
|
|
|
|
/* Put in the record number */
|
|
wsprintf((LPSTR) szBuffer, (LPSTR) szFormat,
|
|
(UWORD) lpSqlNodeSelect->node.select.CurrentRow);
|
|
|
|
CString myBuffer;
|
|
myBuffer.Format("WBEM ODBC Driver : Distinct : Record Number = %s\n", szBuffer);
|
|
ODBCTRACE(myBuffer);
|
|
|
|
if (_lwrite_buffer(hfSortfile, (LPSTR) szBuffer, NUM_LEN) != NUM_LEN) {
|
|
_lclose_buffer(hfSortfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return ERR_SORT;
|
|
}
|
|
|
|
/* Put the columns in the sort file in the order specified by the */
|
|
/* select list. For each value... */
|
|
idxValues = lpSqlNodeSelect->node.select.Values;
|
|
fGotOne = FALSE;
|
|
while (idxValues != NO_SQLNODE) {
|
|
|
|
/* Get next column */
|
|
lpSqlNodeValues = ToNode(lpstmt->lpSqlStmt, idxValues);
|
|
lpSqlNodeValue = ToNode(lpstmt->lpSqlStmt,
|
|
lpSqlNodeValues->node.values.Value);
|
|
|
|
/* Does the value need to be put in the file? */
|
|
switch (lpSqlNodeValue->sqlNodeType) {
|
|
case NODE_TYPE_COLUMN:
|
|
case NODE_TYPE_AGGREGATE:
|
|
case NODE_TYPE_ALGEBRAIC:
|
|
case NODE_TYPE_SCALAR:
|
|
/* Yes. Continue below */
|
|
fGotOne = TRUE;
|
|
break;
|
|
|
|
case NODE_TYPE_STRING:
|
|
case NODE_TYPE_NUMERIC:
|
|
case NODE_TYPE_PARAMETER:
|
|
case NODE_TYPE_USER:
|
|
case NODE_TYPE_NULL:
|
|
case NODE_TYPE_DATE:
|
|
case NODE_TYPE_TIME:
|
|
case NODE_TYPE_TIMESTAMP:
|
|
/* No. Go to next value */
|
|
idxValues = lpSqlNodeValues->node.values.Next;
|
|
continue;
|
|
|
|
case NODE_TYPE_CREATE:
|
|
case NODE_TYPE_DROP:
|
|
case NODE_TYPE_SELECT:
|
|
case NODE_TYPE_INSERT:
|
|
case NODE_TYPE_DELETE:
|
|
case NODE_TYPE_UPDATE:
|
|
case NODE_TYPE_CREATEINDEX:
|
|
case NODE_TYPE_DROPINDEX:
|
|
case NODE_TYPE_PASSTHROUGH:
|
|
case NODE_TYPE_TABLES:
|
|
case NODE_TYPE_VALUES:
|
|
case NODE_TYPE_COLUMNS:
|
|
case NODE_TYPE_SORTCOLUMNS:
|
|
case NODE_TYPE_GROUPBYCOLUMNS:
|
|
case NODE_TYPE_UPDATEVALUES:
|
|
case NODE_TYPE_CREATECOLS:
|
|
case NODE_TYPE_BOOLEAN:
|
|
case NODE_TYPE_COMPARISON:
|
|
case NODE_TYPE_TABLE:
|
|
default:
|
|
return ERR_INTERNAL;
|
|
}
|
|
|
|
/* Get its value */
|
|
err = EvaluateExpression(lpstmt, lpSqlNodeValue);
|
|
if (err != ERR_SUCCESS) {
|
|
_lclose_buffer(hfSortfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return err;
|
|
}
|
|
|
|
/* Get length and pointer to key value */
|
|
switch (lpSqlNodeValue->sqlDataType) {
|
|
case TYPE_DOUBLE:
|
|
case TYPE_INTEGER:
|
|
if (lpSqlNodeValue->sqlIsNull)
|
|
lpSqlNodeValue->value.Double = 0.0;
|
|
len = sizeof(double);
|
|
ptr = &(lpSqlNodeValue->value.Double);
|
|
break;
|
|
case TYPE_NUMERIC:
|
|
if (lpSqlNodeValue->sqlIsNull)
|
|
(lpSqlNodeValue->value.String)[0] = 0;
|
|
len = lpSqlNodeValue->sqlPrecision + 2;
|
|
ptr = lpSqlNodeValue->value.String;
|
|
|
|
/* (blank pad string values) */
|
|
while (s_lstrlen(lpSqlNodeValue->value.String) < len)
|
|
s_lstrcat(lpSqlNodeValue->value.String, " ");
|
|
break;
|
|
case TYPE_CHAR:
|
|
if (lpSqlNodeValue->sqlIsNull)
|
|
(lpSqlNodeValue->value.String)[0] = 0;
|
|
len = lpSqlNodeValue->sqlPrecision;
|
|
ptr = lpSqlNodeValue->value.String;
|
|
|
|
ODBCTRACE(_T("\nWBEM ODBC Driver : Distinct : String = "));
|
|
|
|
if ((LPSTR)ptr)
|
|
{
|
|
ODBCTRACE((LPSTR)ptr);
|
|
|
|
CString myString;
|
|
myString.Format(" length = %ld\n", s_lstrlen((LPSTR)ptr));
|
|
ODBCTRACE(myString);
|
|
}
|
|
else
|
|
{
|
|
ODBCTRACE(_T("NULL\n"));
|
|
}
|
|
|
|
/* (blank pad string values) */
|
|
while (s_lstrlen(lpSqlNodeValue->value.String) < len)
|
|
s_lstrcat(lpSqlNodeValue->value.String, " ");
|
|
break;
|
|
case TYPE_BINARY:
|
|
if (lpSqlNodeValue->sqlIsNull)
|
|
BINARY_LENGTH(lpSqlNodeValue->value.Binary) = 0;
|
|
len = lpSqlNodeValue->sqlPrecision;
|
|
ptr = BINARY_DATA(lpSqlNodeValue->value.Binary);
|
|
|
|
/* (zero pad binary values) */
|
|
while (BINARY_LENGTH(lpSqlNodeValue->value.Binary) < len) {
|
|
BINARY_DATA(lpSqlNodeValue->value.Binary)[
|
|
BINARY_LENGTH(lpSqlNodeValue->value.Binary)] = 0;
|
|
BINARY_LENGTH(lpSqlNodeValue->value.Binary) =
|
|
BINARY_LENGTH(lpSqlNodeValue->value.Binary) + 1;
|
|
}
|
|
break;
|
|
case TYPE_DATE:
|
|
if (lpSqlNodeValue->sqlIsNull) {
|
|
lpSqlNodeValue->value.Date.year = 0;
|
|
lpSqlNodeValue->value.Date.month = 0;
|
|
lpSqlNodeValue->value.Date.day = 0;
|
|
}
|
|
DateToChar(&(lpSqlNodeValue->value.Date), (LPUSTR)szBuffer);
|
|
len = s_lstrlen((char*)szBuffer);
|
|
ptr = szBuffer;
|
|
break;
|
|
case TYPE_TIME:
|
|
if (lpSqlNodeValue->sqlIsNull) {
|
|
lpSqlNodeValue->value.Time.hour = 0;
|
|
lpSqlNodeValue->value.Time.minute = 0;
|
|
lpSqlNodeValue->value.Time.second = 0;
|
|
}
|
|
TimeToChar(&(lpSqlNodeValue->value.Time), (LPUSTR)szBuffer);
|
|
len = s_lstrlen((char*)szBuffer);
|
|
ptr = szBuffer;
|
|
break;
|
|
case TYPE_TIMESTAMP:
|
|
if (lpSqlNodeValue->sqlIsNull) {
|
|
lpSqlNodeValue->value.Timestamp.year = 0;
|
|
lpSqlNodeValue->value.Timestamp.month = 0;
|
|
lpSqlNodeValue->value.Timestamp.day = 0;
|
|
lpSqlNodeValue->value.Timestamp.hour = 0;
|
|
lpSqlNodeValue->value.Timestamp.minute = 0;
|
|
lpSqlNodeValue->value.Timestamp.second = 0;
|
|
lpSqlNodeValue->value.Timestamp.fraction = 0;
|
|
}
|
|
TimestampToChar(&(lpSqlNodeValue->value.Timestamp), (LPUSTR)szBuffer);
|
|
len = s_lstrlen((char*)szBuffer);
|
|
ptr = szBuffer;
|
|
break;
|
|
default:
|
|
_lclose_buffer(hfSortfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return ERR_INTERNAL;
|
|
}
|
|
|
|
/* Put value into the file */
|
|
if (_lwrite_buffer(hfSortfile, (LPSTR) ptr, (UINT) len) != (UINT) len) {
|
|
_lclose_buffer(hfSortfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return ERR_SORT;
|
|
}
|
|
|
|
/* Put in the null flag */
|
|
if (lpSqlNodeValue->sqlIsNull)
|
|
cNullFlag = NULL_FLAG;
|
|
else
|
|
cNullFlag = NOT_NULL_FLAG;
|
|
if (_lwrite_buffer(hfSortfile, (LPSTR) &cNullFlag, 1) != 1) {
|
|
_lclose_buffer(hfSortfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return ERR_SORT;
|
|
}
|
|
|
|
/* Point at next value */
|
|
idxValues = lpSqlNodeValues->node.values.Next;
|
|
}
|
|
|
|
/* If no fields in sort record, put a constant in */
|
|
if (!fGotOne) {
|
|
if (_lwrite_buffer(hfSortfile, "LEM", 3) != 3) {
|
|
_lclose_buffer(hfSortfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return ERR_SORT;
|
|
}
|
|
}
|
|
}
|
|
if (_lclose_buffer(hfSortfile) == HFILE_ERROR) {
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return ERR_SORT;
|
|
}
|
|
|
|
/* Create a destination file */
|
|
#ifdef WIN32
|
|
if (!GetTempFileName((LPSTR) szTempDir, "LEM", 0, (LPSTR) szFilename2)) {
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return ERR_SORT;
|
|
}
|
|
#else
|
|
GetTempFileName(NULL, "LEM", 0, (LPSTR) szFilename2);
|
|
#endif
|
|
hfSortfile2 = _lcreat((LPCSTR) szFilename2, 0);
|
|
if (hfSortfile2 == HFILE_ERROR) {
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return ERR_SORT;
|
|
}
|
|
_lclose(hfSortfile2);
|
|
|
|
/* Sort the file */
|
|
s_1mains((LPSTR) szFilename, (LPSTR) szFilename2, (LPSTR)
|
|
ToString(lpstmt->lpSqlStmt,
|
|
lpSqlNodeSelect->node.select.DistinctDirective),
|
|
&recordCount, &sortStatus);
|
|
if (sortStatus != 0) {
|
|
DeleteFile((LPCSTR) szFilename2);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return ERR_SORT;
|
|
}
|
|
DeleteFile((LPCSTR) szFilename);
|
|
|
|
/* Sort the file again (to put the values back into the original order) */
|
|
if (lpSqlNodeSelect->node.select.Sortcolumns != NO_SQLNODE) {
|
|
#ifdef WIN32
|
|
if (szTempDir[s_lstrlen(szTempDir)-1] != '\\')
|
|
s_lstrcat(szTempDir, "\\");
|
|
#else
|
|
GetTempFileName(NULL, "LEM", 0, (LPSTR) szTempDir);
|
|
while (szTempDir[s_lstrlen(szTempDir)-1] != '\\')
|
|
szTempDir[s_lstrlen(szTempDir)-1] = '\0';
|
|
#endif
|
|
wsprintf((LPSTR) szSortDirective, "S(1,%d,N,A)F(FIX,%d)W(%s)", (WORD) NUM_LEN,
|
|
(WORD) lpSqlNodeSelect->node.select.DistinctRecordsize,
|
|
(LPSTR) szTempDir);
|
|
s_1mains((LPSTR) szFilename2, (LPSTR) szFilename2,
|
|
(LPSTR) szSortDirective, &recordCount, &sortStatus);
|
|
if (sortStatus != 0) {
|
|
DeleteFile((LPCSTR) szFilename2);
|
|
return ERR_SORT;
|
|
}
|
|
}
|
|
lpSqlNodeSelect->node.select.RowCount = recordCount;
|
|
|
|
|
|
/* Open destination file */
|
|
hfSortfile2 = _lopen((LPCSTR) szFilename2, OF_READ);
|
|
if (hfSortfile2 == HFILE_ERROR) {
|
|
DeleteFile((LPCSTR) szFilename2);
|
|
return ERR_SORT;
|
|
}
|
|
|
|
/* Save name and handle to file of sorted records */
|
|
s_lstrcpy(ToString(lpstmt->lpSqlStmt,
|
|
lpSqlNodeSelect->node.select.SortfileName), szFilename2);
|
|
lpSqlNodeSelect->node.select.Sortfile = hfSortfile2;
|
|
lpSqlNodeSelect->node.select.ReturningDistinct = TRUE;
|
|
|
|
/* Set up to read first row */
|
|
lpSqlNodeSelect->node.select.CurrentRow = BEFORE_FIRST_ROW;
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
RETCODE INTFUNC MSAccess(LPSTMT lpstmt, LPSQLNODE lpSqlNodeSelect,
|
|
SQLNODEIDX idxSelectStatement)
|
|
|
|
/* Evaluates optimized MSAccess statement */
|
|
|
|
{
|
|
// ODBCTRACE("\nWBEM ODBC Driver : MSAccess\n");
|
|
UCHAR szTempDir[MAX_PATHNAME_SIZE+1];
|
|
UCHAR szFilename[MAX_PATHNAME_SIZE+1];
|
|
UCHAR szFilename2[MAX_PATHNAME_SIZE+1];
|
|
HFILE hfSortfile;
|
|
LPSQLNODE lpSqlNodeTables;
|
|
LPSQLNODE lpSqlNodeTable;
|
|
LPSQLNODE lpSqlNodePredicate;
|
|
LPSQLNODE lpSqlNodeKeycondition;
|
|
SQLNODEIDX idxPredicate;
|
|
SQLNODEIDX idxKeycondition;
|
|
RETCODE err;
|
|
ISAMBOOKMARK bookmark;
|
|
long recordCount;
|
|
int sortStatus;
|
|
#define NUM_LEN 5
|
|
UCHAR szFormat[16];
|
|
UCHAR szBuffer[NUM_LEN+1];
|
|
UCHAR szSortDirective[64 + 3 + MAX_PATHNAME_SIZE];
|
|
|
|
/* Create temporary file */
|
|
#ifdef WIN32
|
|
if (!GetTempPath(MAX_PATHNAME_SIZE+1, (LPSTR) szTempDir))
|
|
return ERR_SORT;
|
|
if (!GetTempFileName((LPSTR) szTempDir, "LEM", 0, (LPSTR) szFilename))
|
|
return ERR_SORT;
|
|
#else
|
|
GetTempFileName(NULL, "LEM", 0, (LPSTR) szFilename);
|
|
#endif
|
|
hfSortfile = _lcreat((LPCSTR) szFilename, 0);
|
|
if (hfSortfile == HFILE_ERROR)
|
|
return ERR_SORT;
|
|
|
|
/* Get the table and predicate node. */
|
|
lpSqlNodeTables = ToNode(lpstmt->lpSqlStmt,
|
|
lpSqlNodeSelect->node.select.Tables);
|
|
lpSqlNodeTable = ToNode(lpstmt->lpSqlStmt, lpSqlNodeTables->node.tables.Table);
|
|
lpSqlNodePredicate = ToNode(lpstmt->lpSqlStmt,
|
|
lpSqlNodeSelect->node.select.Predicate);
|
|
|
|
/* For each of the key conditions...*/
|
|
wsprintf((LPSTR) szFormat, "%%0%dd", (UWORD) NUM_LEN);
|
|
lpSqlNodeSelect->node.select.RowCount = 0;
|
|
while (lpSqlNodePredicate != NULL) {
|
|
|
|
/* Get the next key condition */
|
|
if ((lpSqlNodePredicate->sqlNodeType == NODE_TYPE_BOOLEAN) &&
|
|
(lpSqlNodePredicate->node.boolean.Operator == OP_OR)) {
|
|
idxKeycondition = lpSqlNodePredicate->node.boolean.Left;
|
|
lpSqlNodeKeycondition = ToNode(lpstmt->lpSqlStmt,
|
|
idxKeycondition);
|
|
idxPredicate = lpSqlNodePredicate->node.boolean.Right;
|
|
lpSqlNodePredicate = ToNode(lpstmt->lpSqlStmt, idxPredicate);
|
|
}
|
|
else {
|
|
idxKeycondition = idxPredicate;
|
|
lpSqlNodeKeycondition = lpSqlNodePredicate;
|
|
idxPredicate = NO_SQLNODE;
|
|
lpSqlNodePredicate = NULL;
|
|
}
|
|
|
|
/* Set the optimization predicate */
|
|
lpSqlNodeTable->node.table.cRestrict = 0;
|
|
lpSqlNodeTable->node.table.Restrict = NO_SQLNODE;
|
|
err = FindRestriction(lpstmt->lpSqlStmt,
|
|
ISAMCaseSensitive(lpstmt->lpdbc->lpISAM),
|
|
lpSqlNodeTable, idxKeycondition, idxSelectStatement,
|
|
&(lpSqlNodeTable->node.table.cRestrict),
|
|
&(lpSqlNodeTable->node.table.Restrict));
|
|
if (err != ERR_SUCCESS) {
|
|
lpSqlNodeTable->node.table.cRestrict = 0;
|
|
lpSqlNodeTable->node.table.Restrict = NO_SQLNODE;
|
|
return err;
|
|
}
|
|
|
|
/* Rewind the table */
|
|
err = Rewind(lpstmt, lpSqlNodeTable, FALSE);
|
|
if (err != ERR_SUCCESS) {
|
|
lpSqlNodeTable->node.table.cRestrict = 0;
|
|
lpSqlNodeTable->node.table.Restrict = NO_SQLNODE;
|
|
return err;
|
|
}
|
|
|
|
/* Clear the optimization predicate */
|
|
lpSqlNodeTable->node.table.cRestrict = 0;
|
|
lpSqlNodeTable->node.table.Restrict = NO_SQLNODE;
|
|
|
|
/* Set up to read first row */
|
|
lpSqlNodeSelect->node.select.CurrentRow = BEFORE_FIRST_ROW;
|
|
|
|
/* For each record of result set... */
|
|
while (TRUE) {
|
|
|
|
/* Get the next record */
|
|
err = NextRecord(lpstmt, lpSqlNodeTables,
|
|
lpSqlNodeKeycondition);
|
|
if (err == ERR_NODATAFOUND)
|
|
break;
|
|
else if (err != ERR_SUCCESS) {
|
|
_lclose(hfSortfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return err;
|
|
}
|
|
|
|
/* Set row flag */
|
|
if (lpSqlNodeSelect->node.select.CurrentRow == BEFORE_FIRST_ROW)
|
|
lpSqlNodeSelect->node.select.CurrentRow = 0;
|
|
else
|
|
lpSqlNodeSelect->node.select.CurrentRow++;
|
|
|
|
/* Increase row count */
|
|
(lpSqlNodeSelect->node.select.RowCount)++;
|
|
|
|
/* Get bookmark of current record */
|
|
if (lpSqlNodeTable->node.table.AllNull)
|
|
{
|
|
bookmark.currentRecord = NULL_BOOKMARK;
|
|
}
|
|
else
|
|
{
|
|
err = ISAMGetBookmark(lpSqlNodeTable->node.table.Handle, &bookmark);
|
|
if (err != ERR_SUCCESS) {
|
|
ISAMGetErrorMessage(lpstmt->lpdbc->lpISAM,
|
|
(LPUSTR)lpstmt->szISAMError);
|
|
_lclose(hfSortfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return err;
|
|
}
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
}
|
|
|
|
/* Put value into the file */
|
|
wsprintf((LPSTR) szBuffer, (LPSTR) szFormat,
|
|
(UWORD) lpSqlNodeSelect->node.select.RowCount);
|
|
if (_lwrite(hfSortfile, (LPSTR) szBuffer, NUM_LEN) != NUM_LEN) {
|
|
_lclose(hfSortfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return ERR_SORT;
|
|
}
|
|
if (_lwrite(hfSortfile, (LPSTR) &bookmark, sizeof(ISAMBOOKMARK))
|
|
!= sizeof(ISAMBOOKMARK)) {
|
|
_lclose(hfSortfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return ERR_SORT;
|
|
}
|
|
}
|
|
}
|
|
if (lpSqlNodeSelect->node.select.RowCount == 0) {
|
|
_lclose(hfSortfile);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
lpSqlNodeSelect->node.select.CurrentRow = AFTER_LAST_ROW;
|
|
return ERR_SUCCESS;
|
|
}
|
|
_lclose(hfSortfile);
|
|
|
|
/* Create a destination file */
|
|
#ifdef WIN32
|
|
if (!GetTempFileName((LPSTR) szTempDir, "LEM", 0, (LPSTR) szFilename2)) {
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return ERR_SORT;
|
|
}
|
|
#else
|
|
GetTempFileName(NULL, "LEM", 0, (LPSTR) szFilename2);
|
|
#endif
|
|
hfSortfile = _lcreat((LPCSTR) szFilename2, 0);
|
|
if (hfSortfile == HFILE_ERROR) {
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return ERR_SORT;
|
|
}
|
|
_lclose(hfSortfile);
|
|
|
|
|
|
/* Get name of workspace directory */
|
|
#ifdef WIN32
|
|
if (szTempDir[s_lstrlen(szTempDir)-1] != '\\')
|
|
s_lstrcat(szTempDir, "\\");
|
|
#else
|
|
GetTempFileName(NULL, "LEM", 0, (LPSTR) szTempDir);
|
|
while (szTempDir[s_lstrlen(szTempDir)-1] != '\\')
|
|
szTempDir[s_lstrlen(szTempDir)-1] = '\0';
|
|
#endif
|
|
|
|
/* Sort the file and remove duplicate bookmarks */
|
|
wsprintf((LPSTR) szSortDirective, "S(%d,%d,W,A)DUPO(B%d,%d,%d)F(FIX,%d)W(%s)",
|
|
(WORD) NUM_LEN+1, (WORD) sizeof(ISAMBOOKMARK),
|
|
(WORD) (sizeof(ISAMBOOKMARK) + NUM_LEN), (WORD) NUM_LEN+1,
|
|
(WORD) sizeof(ISAMBOOKMARK),
|
|
(WORD) (sizeof(ISAMBOOKMARK) + NUM_LEN),
|
|
(LPSTR) szTempDir);
|
|
s_1mains((LPSTR) szFilename, (LPSTR) szFilename2,
|
|
(LPSTR) szSortDirective, &recordCount, &sortStatus);
|
|
if (sortStatus != 0) {
|
|
DeleteFile((LPCSTR) szFilename2);
|
|
DeleteFile((LPCSTR) szFilename);
|
|
return ERR_SORT;
|
|
}
|
|
DeleteFile((LPCSTR) szFilename);
|
|
|
|
/* Sort the file again (to put the values back into the original order) */
|
|
wsprintf((LPSTR) szSortDirective, "S(1,%d,N,A)F(FIX,%d)W(%s)", (WORD) NUM_LEN,
|
|
(WORD) (sizeof(ISAMBOOKMARK) + NUM_LEN),
|
|
(LPSTR) szTempDir);
|
|
s_1mains((LPSTR) szFilename2, (LPSTR) szFilename2,
|
|
(LPSTR) szSortDirective, &recordCount, &sortStatus);
|
|
if (sortStatus != 0) {
|
|
DeleteFile((LPCSTR) szFilename2);
|
|
return ERR_SORT;
|
|
}
|
|
lpSqlNodeSelect->node.select.RowCount = recordCount;
|
|
|
|
/* Open destination file */
|
|
hfSortfile = _lopen((LPCSTR) szFilename2, OF_READ);
|
|
if (hfSortfile == HFILE_ERROR) {
|
|
DeleteFile((LPCSTR) szFilename2);
|
|
return ERR_SORT;
|
|
}
|
|
|
|
/* Save name and handle to file of records found */
|
|
s_lstrcpy(ToString(lpstmt->lpSqlStmt,
|
|
lpSqlNodeSelect->node.select.SortfileName), szFilename2);
|
|
lpSqlNodeSelect->node.select.Sortfile = hfSortfile;
|
|
|
|
/* Set up to read first row */
|
|
lpSqlNodeSelect->node.select.CurrentRow = BEFORE_FIRST_ROW;
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
/***************************************************************************/
|
|
|
|
RETCODE INTFUNC ExecuteQuery(LPSTMT lpstmt, LPSQLNODE lpSqlNode)
|
|
|
|
/* Executes the current query */
|
|
|
|
{
|
|
LPUSTR lpstrTablename;
|
|
LPUSTR lpstrColumnname;
|
|
LPISAMTABLEDEF lpISAMTableDef;
|
|
SWORD err;
|
|
SWORD finalErr;
|
|
SQLNODEIDX idxTables;
|
|
SQLNODEIDX idxSqlNodeColumns;
|
|
SQLNODEIDX idxSqlNodeValues;
|
|
SQLNODEIDX idxSqlNodeSets;
|
|
SQLNODEIDX idxSortcolumns;
|
|
SQLNODEIDX idxParameter;
|
|
LPSQLNODE lpSqlNodeTables;
|
|
LPSQLNODE lpSqlNodeTable;
|
|
LPSQLNODE lpSqlNodeColumns;
|
|
LPSQLNODE lpSqlNodeColumn;
|
|
LPSQLNODE lpSqlNodeValues;
|
|
LPSQLNODE lpSqlNodeValue;
|
|
LPSQLNODE lpSqlNodePredicate;
|
|
LPSQLNODE lpSqlNodeSets;
|
|
LPSQLNODE lpSqlNodeSortcolumns;
|
|
LPSQLNODE lpSqlNodeParameter;
|
|
LPSQLNODE lpSqlNodeSelect;
|
|
UWORD idx;
|
|
BOOL fSelect;
|
|
BOOL fSubSelect;
|
|
|
|
// ODBCTRACE ("\nWBEM ODBC Driver : ExecuteQuery\n");
|
|
|
|
/* Nested sub-select? */
|
|
if (lpSqlNode == NULL) {
|
|
|
|
/* No. Count of rows is not available */
|
|
fSubSelect = FALSE;
|
|
lpstmt->cRowCount = -1;
|
|
|
|
/* If there are passthrough parameters, send them now */
|
|
lpSqlNode = ToNode(lpstmt->lpSqlStmt, ROOT_SQLNODE);
|
|
if (lpSqlNode->node.root.passthroughParams) {
|
|
idxParameter = lpSqlNode->node.root.parameters;
|
|
while (idxParameter != NO_SQLNODE) {
|
|
lpSqlNodeParameter = ToNode(lpstmt->lpSqlStmt, idxParameter);
|
|
switch (lpSqlNodeParameter->sqlDataType) {
|
|
case TYPE_DOUBLE:
|
|
case TYPE_INTEGER:
|
|
err = ISAMParameter(lpstmt->lpISAMStatement,
|
|
lpSqlNodeParameter->node.parameter.Id,
|
|
SQL_C_DOUBLE,
|
|
&(lpSqlNodeParameter->value.Double),
|
|
lpSqlNodeParameter->sqlIsNull ? SQL_NULL_DATA :
|
|
sizeof(double));
|
|
break;
|
|
case TYPE_NUMERIC:
|
|
case TYPE_CHAR:
|
|
err = ISAMParameter(lpstmt->lpISAMStatement,
|
|
lpSqlNodeParameter->node.parameter.Id,
|
|
SQL_C_CHAR, lpSqlNodeParameter->value.String,
|
|
lpSqlNodeParameter->sqlIsNull ? SQL_NULL_DATA :
|
|
s_lstrlen(lpSqlNodeParameter->value.String));
|
|
break;
|
|
case TYPE_BINARY:
|
|
err = ISAMParameter(lpstmt->lpISAMStatement,
|
|
lpSqlNodeParameter->node.parameter.Id,
|
|
SQL_C_BINARY,
|
|
BINARY_DATA(lpSqlNodeParameter->value.Binary),
|
|
lpSqlNodeParameter->sqlIsNull ? SQL_NULL_DATA :
|
|
BINARY_LENGTH(lpSqlNodeParameter->value.Binary));
|
|
break;
|
|
case TYPE_DATE:
|
|
err = ISAMParameter(lpstmt->lpISAMStatement,
|
|
lpSqlNodeParameter->node.parameter.Id,
|
|
SQL_C_DATE, &(lpSqlNodeParameter->value.Date),
|
|
lpSqlNodeParameter->sqlIsNull ? SQL_NULL_DATA :
|
|
sizeof(DATE_STRUCT));
|
|
break;
|
|
case TYPE_TIME:
|
|
err = ISAMParameter(lpstmt->lpISAMStatement,
|
|
lpSqlNodeParameter->node.parameter.Id,
|
|
SQL_C_TIME, &(lpSqlNodeParameter->value.Time),
|
|
lpSqlNodeParameter->sqlIsNull ? SQL_NULL_DATA :
|
|
sizeof(TIME_STRUCT));
|
|
break;
|
|
case TYPE_TIMESTAMP:
|
|
err = ISAMParameter(lpstmt->lpISAMStatement,
|
|
lpSqlNodeParameter->node.parameter.Id,
|
|
SQL_C_TIMESTAMP,
|
|
&(lpSqlNodeParameter->value.Timestamp),
|
|
lpSqlNodeParameter->sqlIsNull ? SQL_NULL_DATA :
|
|
sizeof(TIMESTAMP_STRUCT));
|
|
break;
|
|
default:
|
|
return ERR_NOTSUPPORTED;
|
|
}
|
|
if (err != NO_ISAM_ERR) {
|
|
ISAMGetErrorMessage(lpstmt->lpdbc->lpISAM,
|
|
(LPUSTR)lpstmt->szISAMError);
|
|
return err;
|
|
}
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
idxParameter = lpSqlNodeParameter->node.parameter.Next;
|
|
}
|
|
}
|
|
|
|
/* Get the root node of the operation */
|
|
lpSqlNode = ToNode(lpstmt->lpSqlStmt, ROOT_SQLNODE);
|
|
lpSqlNode = ToNode(lpstmt->lpSqlStmt, lpSqlNode->node.root.sql);
|
|
}
|
|
else {
|
|
|
|
/* Yes. It is a sub-select */
|
|
fSubSelect = TRUE;
|
|
}
|
|
|
|
finalErr = ERR_SUCCESS;
|
|
fSelect = FALSE;
|
|
|
|
switch (lpSqlNode->sqlNodeType) {
|
|
|
|
/* Create a table */
|
|
case NODE_TYPE_CREATE:
|
|
|
|
/* Make sure this DDL statement is allowed now */
|
|
err = TxnCapableForDDL(lpstmt);
|
|
if (err == ERR_DDLIGNORED)
|
|
return ERR_DDLIGNORED;
|
|
else if (err == ERR_DDLCAUSEDACOMMIT)
|
|
finalErr = ERR_DDLCAUSEDACOMMIT;
|
|
else if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Find the table to create */
|
|
lpstrTablename = ToString(lpstmt->lpSqlStmt, lpSqlNode->node.create.Table);
|
|
|
|
/* Create the table */
|
|
err = ISAMCreateTable(lpstmt->lpdbc->lpISAM,
|
|
(LPUSTR) lpstrTablename, &lpISAMTableDef);
|
|
if (err != NO_ISAM_ERR) {
|
|
ISAMGetErrorMessage(lpstmt->lpdbc->lpISAM, (LPUSTR)lpstmt->szISAMError);
|
|
return err;
|
|
}
|
|
if (lpstmt->lpdbc->lpISAM->fSchemaInfoTransactioned)
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
|
|
/* Add the columns to the table */
|
|
lpSqlNode = ToNode(lpstmt->lpSqlStmt, lpSqlNode->node.create.Columns);
|
|
while (TRUE) {
|
|
|
|
/* Error if wrong node type */
|
|
if (lpSqlNode->sqlNodeType != NODE_TYPE_CREATECOLS) {
|
|
ISAMCloseTable(lpISAMTableDef);
|
|
if (NO_ISAM_ERR ==
|
|
ISAMDeleteTable(lpstmt->lpdbc->lpISAM, (LPUSTR) lpstrTablename)) {
|
|
if (lpstmt->lpdbc->lpISAM->fSchemaInfoTransactioned)
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
}
|
|
return ERR_INTERNAL;
|
|
}
|
|
|
|
/* Get column name */
|
|
lpstrColumnname = ToString(lpstmt->lpSqlStmt,
|
|
lpSqlNode->node.createcols.Name);
|
|
|
|
/* Add the column to the table */
|
|
err = ISAMAddColumn(lpISAMTableDef, (LPUSTR) lpstrColumnname,
|
|
lpSqlNode->node.createcols.iSqlType,
|
|
lpSqlNode->node.createcols.Param1, lpSqlNode->node.createcols.Param2);
|
|
if (err != NO_ISAM_ERR) {
|
|
ISAMGetErrorMessage(lpstmt->lpdbc->lpISAM,
|
|
(LPUSTR)lpstmt->szISAMError);
|
|
ISAMCloseTable(lpISAMTableDef);
|
|
if (NO_ISAM_ERR ==
|
|
ISAMDeleteTable(lpstmt->lpdbc->lpISAM, (LPUSTR) lpstrTablename)) {
|
|
if (lpstmt->lpdbc->lpISAM->fSchemaInfoTransactioned)
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
}
|
|
return err;
|
|
}
|
|
if (lpstmt->lpdbc->lpISAM->fSchemaInfoTransactioned)
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
|
|
/* Find next column */
|
|
if (lpSqlNode->node.createcols.Next == NO_SQLNODE)
|
|
break;
|
|
lpSqlNode = ToNode(lpstmt->lpSqlStmt, lpSqlNode->node.createcols.Next);
|
|
}
|
|
|
|
/* Close the table */
|
|
err = ISAMCloseTable(lpISAMTableDef);
|
|
if (err != NO_ISAM_ERR) {
|
|
ISAMGetErrorMessage(lpstmt->lpdbc->lpISAM, (LPUSTR)lpstmt->szISAMError);
|
|
if (NO_ISAM_ERR ==
|
|
ISAMDeleteTable(lpstmt->lpdbc->lpISAM, (LPUSTR) lpstrTablename)) {
|
|
if (lpstmt->lpdbc->lpISAM->fSchemaInfoTransactioned)
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
}
|
|
return err;
|
|
}
|
|
|
|
break;
|
|
|
|
/* Drop a table */
|
|
case NODE_TYPE_DROP:
|
|
|
|
/* Make sure this DDL statement is allowed now */
|
|
err = TxnCapableForDDL(lpstmt);
|
|
if (err == ERR_DDLIGNORED)
|
|
return ERR_DDLIGNORED;
|
|
else if (err == ERR_DDLCAUSEDACOMMIT)
|
|
finalErr = ERR_DDLCAUSEDACOMMIT;
|
|
else if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Find the table to drop */
|
|
lpstrTablename = ToString(lpstmt->lpSqlStmt, lpSqlNode->node.drop.Table);
|
|
|
|
/* Drop the table */
|
|
err = ISAMDeleteTable(lpstmt->lpdbc->lpISAM, (LPUSTR) lpstrTablename);
|
|
if (err != NO_ISAM_ERR) {
|
|
ISAMGetErrorMessage(lpstmt->lpdbc->lpISAM, (LPUSTR)lpstmt->szISAMError);
|
|
return err;
|
|
}
|
|
if (lpstmt->lpdbc->lpISAM->fSchemaInfoTransactioned)
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
|
|
break;
|
|
|
|
/* SELECT statement */
|
|
case NODE_TYPE_SELECT:
|
|
|
|
/* Is there a passthrough SQL statement? */
|
|
lpstmt->fDMLTxn = TRUE;
|
|
fSelect = TRUE;
|
|
if (!fSubSelect) {
|
|
|
|
if (lpstmt->lpISAMStatement != NULL) {
|
|
|
|
/* Yes. Execute it now */
|
|
err = ISAMExecute(lpstmt->lpISAMStatement,
|
|
&(lpSqlNode->node.select.RowCount));
|
|
if (err != NO_ISAM_ERR) {
|
|
ISAMGetErrorMessage(lpstmt->lpdbc->lpISAM,
|
|
(LPUSTR)lpstmt->szISAMError);
|
|
return err;
|
|
}
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
}
|
|
else {
|
|
lpSqlNode->node.select.RowCount = -1;
|
|
|
|
}
|
|
}
|
|
|
|
/* Is there a pushdown sort? */
|
|
if ((lpSqlNode->node.select.Sortcolumns != NO_SQLNODE) &&
|
|
lpSqlNode->node.select.fPushdownSort) {
|
|
|
|
UWORD icol[MAX_COLUMNS_IN_ORDER_BY];
|
|
BOOL fDescending[MAX_COLUMNS_IN_ORDER_BY];
|
|
|
|
/* Yes. For each table... */
|
|
idxTables = lpSqlNode->node.select.Tables;
|
|
while (idxTables != NO_SQLNODE) {
|
|
|
|
/* If no pushdown sort for this table, leave the loop */
|
|
/* (since there will be no pushdowns for any of the */
|
|
/* subsequent tables either) */
|
|
lpSqlNodeTables = ToNode(lpstmt->lpSqlStmt, idxTables);
|
|
lpSqlNodeTable = ToNode(lpstmt->lpSqlStmt,
|
|
lpSqlNodeTables->node.tables.Table);
|
|
if (lpSqlNodeTable->node.table.Sortcount == 0)
|
|
break;
|
|
|
|
/* Create sort info arrays. For each column in sequence... */
|
|
idxSortcolumns = lpSqlNodeTable->node.table.Sortcolumns;
|
|
for (idx = 0; idx < lpSqlNodeTable->node.table.Sortcount; idx++) {
|
|
|
|
/* Get the column description */
|
|
lpSqlNodeSortcolumns = ToNode(lpstmt->lpSqlStmt,
|
|
idxSortcolumns);
|
|
lpSqlNodeColumn = ToNode(lpstmt->lpSqlStmt,
|
|
lpSqlNodeSortcolumns->node.sortcolumns.Column);
|
|
|
|
/* Put information into the array */
|
|
icol[idx] = lpSqlNodeColumn->node.column.Id;
|
|
fDescending[idx] =
|
|
lpSqlNodeSortcolumns->node.sortcolumns.Descending;
|
|
|
|
/* Look at next column */
|
|
idxSortcolumns = lpSqlNodeSortcolumns->node.sortcolumns.Next;
|
|
}
|
|
|
|
/* Get the records in sorted order */
|
|
err = ISAMSort(lpSqlNodeTable->node.table.Handle,
|
|
lpSqlNodeTable->node.table.Sortcount,
|
|
icol, fDescending);
|
|
|
|
/* Can this sort be pushed down? */
|
|
if (err == ISAM_NOTSUPPORTED) {
|
|
|
|
/* No. Turn off all the sorts */
|
|
idxTables = lpSqlNode->node.select.Tables;
|
|
while (idxTables != NO_SQLNODE) {
|
|
lpSqlNodeTables = ToNode(lpstmt->lpSqlStmt, idxTables);
|
|
lpSqlNodeTable = ToNode(lpstmt->lpSqlStmt,
|
|
lpSqlNodeTables->node.tables.Table);
|
|
if (lpSqlNodeTable->node.table.Sortcount == 0)
|
|
break;
|
|
err = ISAMSort(lpSqlNodeTable->node.table.Handle, 0,
|
|
icol, fDescending);
|
|
if ((err != NO_ISAM_ERR) &&
|
|
(err != ISAM_NOTSUPPORTED)) {
|
|
ISAMGetErrorMessage(lpstmt->lpdbc->lpISAM,
|
|
(LPUSTR)lpstmt->szISAMError);
|
|
return err;
|
|
}
|
|
if (err == NO_ISAM_ERR)
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
idxTables = lpSqlNodeTables->node.tables.Next;
|
|
}
|
|
|
|
/* Don't try to do pushdown sort again */
|
|
lpSqlNode->node.select.fPushdownSort = FALSE;
|
|
|
|
/* Leave this loop */
|
|
break;
|
|
}
|
|
else if (err != NO_ISAM_ERR) {
|
|
ISAMGetErrorMessage(lpstmt->lpdbc->lpISAM,
|
|
(LPUSTR)lpstmt->szISAMError);
|
|
return err;
|
|
}
|
|
else
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
|
|
/* Look at next table */
|
|
idxTables = lpSqlNodeTables->node.tables.Next;
|
|
}
|
|
}
|
|
|
|
/* Rewind the tables */
|
|
lpSqlNodeTables = ToNode(lpstmt->lpSqlStmt, lpSqlNode->node.select.Tables);
|
|
err = Rewind(lpstmt, lpSqlNodeTables, FALSE);
|
|
if (err == ERR_NODATAFOUND) {
|
|
if (!fSubSelect) {
|
|
|
|
lpstmt->fStmtType = STMT_TYPE_SELECT;
|
|
lpstmt->fStmtSubtype = STMT_SUBTYPE_NONE;
|
|
lpstmt->cRowCount = 0;
|
|
}
|
|
lpSqlNode->node.select.RowCount = 0;
|
|
lpSqlNode->node.select.CurrentRow = AFTER_LAST_ROW;
|
|
|
|
if (!fSubSelect) {
|
|
lpstmt->icol = NO_COLUMN;
|
|
lpstmt->cbOffset = 0;
|
|
}
|
|
break;
|
|
}
|
|
else if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Show a SELECT statement as the active statement */
|
|
if (!fSubSelect) {
|
|
lpstmt->fStmtType = STMT_TYPE_SELECT;
|
|
lpstmt->fStmtSubtype = STMT_SUBTYPE_NONE;
|
|
}
|
|
|
|
/* So far no data read */
|
|
lpSqlNode->node.select.CurrentRow = BEFORE_FIRST_ROW;
|
|
|
|
/* So far no column read */
|
|
if (!fSubSelect) {
|
|
lpstmt->icol = NO_COLUMN;
|
|
lpstmt->cbOffset = 0;
|
|
}
|
|
|
|
/* Is a sort needed? */
|
|
if (((lpSqlNode->node.select.Sortcolumns != NO_SQLNODE) ||
|
|
(lpSqlNode->node.select.ImplicitGroupby)) &&
|
|
(!lpSqlNode->node.select.fPushdownSort)) {
|
|
|
|
/* Yes. Do it */
|
|
err = Sort(lpstmt, lpSqlNode);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
}
|
|
|
|
/* Is group by needed? */
|
|
if ((lpSqlNode->node.select.Groupbycolumns != NO_SQLNODE) ||
|
|
(lpSqlNode->node.select.ImplicitGroupby)) {
|
|
|
|
/* Yes. Do it */
|
|
err = GroupBy(lpstmt, lpSqlNode);
|
|
if (err != ERR_SUCCESS) {
|
|
if (lpSqlNode->node.select.Sortfile != HFILE_ERROR) {
|
|
_lclose(lpSqlNode->node.select.Sortfile);
|
|
lpSqlNode->node.select.Sortfile = HFILE_ERROR;
|
|
lpSqlNode->node.select.ReturningDistinct = FALSE;
|
|
DeleteFile((LPCSTR) ToString(lpstmt->lpSqlStmt,
|
|
lpSqlNode->node.select.SortfileName));
|
|
s_lstrcpy(ToString(lpstmt->lpSqlStmt,
|
|
lpSqlNode->node.select.SortfileName), "");
|
|
}
|
|
return err;
|
|
}
|
|
}
|
|
|
|
/* Is DISTINCT specified? */
|
|
if (lpSqlNode->node.select.Distinct) {
|
|
|
|
/* Yes. Do it */
|
|
err = Distinct(lpstmt, lpSqlNode);
|
|
if (err != ERR_SUCCESS) {
|
|
if (lpSqlNode->node.select.Sortfile != HFILE_ERROR) {
|
|
_lclose(lpSqlNode->node.select.Sortfile);
|
|
lpSqlNode->node.select.Sortfile = HFILE_ERROR;
|
|
lpSqlNode->node.select.ReturningDistinct = FALSE;
|
|
DeleteFile((LPCSTR) ToString(lpstmt->lpSqlStmt,
|
|
lpSqlNode->node.select.SortfileName));
|
|
s_lstrcpy(ToString(lpstmt->lpSqlStmt,
|
|
lpSqlNode->node.select.SortfileName), "");
|
|
}
|
|
return err;
|
|
}
|
|
}
|
|
|
|
/* MSAccess optimization needed? */
|
|
if (lpSqlNode->node.select.fMSAccess) {
|
|
|
|
/* Yes. Do it */
|
|
err = MSAccess(lpstmt, lpSqlNode,
|
|
ToNode(lpstmt->lpSqlStmt, ROOT_SQLNODE)->node.root.sql);
|
|
if (err != ERR_SUCCESS) {
|
|
if (lpSqlNode->node.select.Sortfile != HFILE_ERROR) {
|
|
_lclose(lpSqlNode->node.select.Sortfile);
|
|
lpSqlNode->node.select.Sortfile = HFILE_ERROR;
|
|
lpSqlNode->node.select.ReturningDistinct = FALSE;
|
|
DeleteFile((LPCSTR) ToString(lpstmt->lpSqlStmt,
|
|
lpSqlNode->node.select.SortfileName));
|
|
s_lstrcpy(ToString(lpstmt->lpSqlStmt,
|
|
lpSqlNode->node.select.SortfileName), "");
|
|
}
|
|
return err;
|
|
}
|
|
}
|
|
|
|
/* Return the row count */
|
|
if (!fSubSelect) {
|
|
lpstmt->cRowCount = lpSqlNode->node.select.RowCount;
|
|
}
|
|
break;
|
|
|
|
/* INSERT statement */
|
|
case NODE_TYPE_INSERT:
|
|
{
|
|
/* Sub-select? */
|
|
lpSqlNodeSelect = ToNode(lpstmt->lpSqlStmt,
|
|
lpSqlNode->node.insert.Values);
|
|
if (lpSqlNodeSelect->sqlNodeType == NODE_TYPE_SELECT) {
|
|
err = ExecuteQuery(lpstmt, lpSqlNodeSelect);
|
|
if (err == ERR_DATATRUNCATED) {
|
|
finalErr = ERR_DATATRUNCATED;
|
|
err = ERR_SUCCESS;
|
|
}
|
|
if ((err != ERR_SUCCESS) && (err != ERR_NODATAFOUND))
|
|
return err;
|
|
}
|
|
else
|
|
lpSqlNodeSelect = NULL;
|
|
|
|
/* Insert rows into the table */
|
|
lpstmt->cRowCount = 0;
|
|
while (TRUE) {
|
|
|
|
/* Get next row to insert */
|
|
if (lpSqlNodeSelect != NULL) {
|
|
if (err == ERR_SUCCESS) {
|
|
// ODBCTRACE("\nWBEM ODBC Driver : Get next row to insert\n");
|
|
err = FetchRow(lpstmt, lpSqlNodeSelect);
|
|
if ((err != ERR_SUCCESS) && (err != ERR_NODATAFOUND))
|
|
return err;
|
|
}
|
|
|
|
/* Leave if no more rows */
|
|
if (err == ERR_NODATAFOUND)
|
|
break;
|
|
|
|
/* Get list of values */
|
|
idxSqlNodeValues = lpSqlNodeSelect->node.select.Values;
|
|
}
|
|
else {
|
|
idxSqlNodeValues = lpSqlNode->node.insert.Values;
|
|
}
|
|
|
|
/* Add a row to the table. */
|
|
lpstmt->fDMLTxn = TRUE;
|
|
lpSqlNodeTable =
|
|
ToNode(lpstmt->lpSqlStmt, lpSqlNode->node.insert.Table);
|
|
lpISAMTableDef = lpSqlNodeTable->node.table.Handle;
|
|
|
|
err = ISAMInsertRecord(lpISAMTableDef);
|
|
if (err != NO_ISAM_ERR) {
|
|
ISAMGetErrorMessage(lpstmt->lpdbc->lpISAM, (LPUSTR)lpstmt->szISAMError);
|
|
return err;
|
|
}
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
|
|
/* Evaluate each column value and copy it into the new record. */
|
|
idxSqlNodeColumns = lpSqlNode->node.insert.Columns;
|
|
while (idxSqlNodeColumns != NO_SQLNODE) {
|
|
lpSqlNodeColumns = ToNode(lpstmt->lpSqlStmt, idxSqlNodeColumns);
|
|
lpSqlNodeColumn = ToNode(lpstmt->lpSqlStmt, lpSqlNodeColumns->node.columns.Column);
|
|
lpSqlNodeValues = ToNode(lpstmt->lpSqlStmt, idxSqlNodeValues);
|
|
lpSqlNodeValue = ToNode(lpstmt->lpSqlStmt, lpSqlNodeValues->node.values.Value);
|
|
|
|
err = EvaluateExpression(lpstmt, lpSqlNodeValue);
|
|
if (err != ERR_SUCCESS) {
|
|
if (ERR_SUCCESS == ISAMDeleteRecord(lpISAMTableDef))
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
return err;
|
|
}
|
|
switch (lpSqlNodeColumn->sqlDataType) {
|
|
case TYPE_DOUBLE:
|
|
case TYPE_INTEGER:
|
|
{
|
|
switch (lpSqlNodeValue->sqlDataType) {
|
|
case TYPE_DOUBLE:
|
|
case TYPE_INTEGER:
|
|
lpSqlNodeColumn->value.Double =
|
|
lpSqlNodeValue->value.Double;
|
|
break;
|
|
case TYPE_NUMERIC:
|
|
if (!(lpSqlNodeValue->sqlIsNull))
|
|
CharToDouble(lpSqlNodeValue->value.String,
|
|
s_lstrlen(lpSqlNodeValue->value.String), FALSE,
|
|
-1.7E308, 1.7E308,
|
|
&(lpSqlNodeColumn->value.Double));
|
|
else
|
|
lpSqlNodeColumn->value.Double = 0.0;
|
|
break;
|
|
case TYPE_CHAR:
|
|
case TYPE_BINARY:
|
|
case TYPE_DATE:
|
|
case TYPE_TIME:
|
|
case TYPE_TIMESTAMP:
|
|
default:
|
|
return ERR_INTERNAL;
|
|
}
|
|
err = ISAMPutData(lpISAMTableDef,
|
|
lpSqlNodeColumn->node.column.Id, SQL_C_DOUBLE,
|
|
&(lpSqlNodeColumn->value.Double),
|
|
lpSqlNodeValue->sqlIsNull ?
|
|
(SDWORD) SQL_NULL_DATA : sizeof(double));
|
|
if ((err == NO_ISAM_ERR) || (err == ISAM_TRUNCATION))
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
}
|
|
break;
|
|
case TYPE_NUMERIC:
|
|
{
|
|
lpSqlNodeColumn->value.String = ToString(lpstmt->lpSqlStmt,
|
|
lpSqlNodeColumn->node.column.Value);
|
|
switch (lpSqlNodeValue->sqlDataType) {
|
|
case TYPE_DOUBLE:
|
|
case TYPE_INTEGER:
|
|
if (!(lpSqlNodeValue->sqlIsNull)) {
|
|
if (DoubleToChar(lpSqlNodeValue->value.Double,
|
|
FALSE, lpSqlNodeColumn->value.String,
|
|
1 + 2 + lpSqlNodeColumn->sqlPrecision)) {
|
|
lpSqlNodeColumn->value.String[1 + 2 +
|
|
lpSqlNodeColumn->sqlPrecision - 1] = '\0';
|
|
finalErr = ERR_DATATRUNCATED;
|
|
}
|
|
}
|
|
else
|
|
s_lstrcpy(lpSqlNodeColumn->value.String, "0");
|
|
break;
|
|
case TYPE_NUMERIC:
|
|
s_lstrcpy(lpSqlNodeColumn->value.String,
|
|
lpSqlNodeValue->value.String);
|
|
break;
|
|
case TYPE_CHAR:
|
|
case TYPE_BINARY:
|
|
case TYPE_DATE:
|
|
case TYPE_TIME:
|
|
case TYPE_TIMESTAMP:
|
|
default:
|
|
return ERR_INTERNAL;
|
|
}
|
|
err = BCDNormalize(lpSqlNodeColumn->value.String,
|
|
s_lstrlen(lpSqlNodeColumn->value.String),
|
|
lpSqlNodeColumn->value.String,
|
|
1 + 2 + lpSqlNodeColumn->sqlPrecision,
|
|
lpSqlNodeColumn->sqlPrecision,
|
|
lpSqlNodeColumn->sqlScale);
|
|
if (err == ERR_DATATRUNCATED) {
|
|
finalErr = ERR_DATATRUNCATED;
|
|
err = ERR_SUCCESS;
|
|
}
|
|
|
|
if (err == ERR_SUCCESS) {
|
|
|
|
err = ISAMPutData(lpISAMTableDef,
|
|
lpSqlNodeColumn->node.column.Id, SQL_C_CHAR,
|
|
lpSqlNodeColumn->value.String,
|
|
lpSqlNodeValue->sqlIsNull ? SQL_NULL_DATA :
|
|
s_lstrlen(lpSqlNodeColumn->value.String));
|
|
if ((err == NO_ISAM_ERR) || (err == ISAM_TRUNCATION))
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
}
|
|
}
|
|
break;
|
|
case TYPE_CHAR:
|
|
err = ISAMPutData(lpISAMTableDef,
|
|
lpSqlNodeColumn->node.column.Id,
|
|
SQL_C_CHAR, lpSqlNodeValue->value.String,
|
|
lpSqlNodeValue->sqlIsNull ? SQL_NULL_DATA :
|
|
s_lstrlen(lpSqlNodeValue->value.String));
|
|
if ((err == NO_ISAM_ERR) || (err == ISAM_TRUNCATION))
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
break;
|
|
case TYPE_BINARY:
|
|
err = ISAMPutData(lpISAMTableDef, lpSqlNodeColumn->node.column.Id,
|
|
SQL_C_BINARY, BINARY_DATA(lpSqlNodeValue->value.Binary),
|
|
lpSqlNodeValue->sqlIsNull ? SQL_NULL_DATA :
|
|
BINARY_LENGTH(lpSqlNodeValue->value.Binary));
|
|
if ((err == NO_ISAM_ERR) || (err == ISAM_TRUNCATION))
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
break;
|
|
case TYPE_DATE:
|
|
err = ISAMPutData(lpISAMTableDef, lpSqlNodeColumn->node.column.Id,
|
|
SQL_C_DATE, &(lpSqlNodeValue->value.Date),
|
|
lpSqlNodeValue->sqlIsNull ?
|
|
(SDWORD) SQL_NULL_DATA : sizeof(DATE_STRUCT));
|
|
if ((err == NO_ISAM_ERR) || (err == ISAM_TRUNCATION))
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
break;
|
|
case TYPE_TIME:
|
|
err = ISAMPutData(lpISAMTableDef, lpSqlNodeColumn->node.column.Id,
|
|
SQL_C_TIME, &(lpSqlNodeValue->value.Time),
|
|
lpSqlNodeValue->sqlIsNull ?
|
|
(SDWORD) SQL_NULL_DATA : sizeof(TIME_STRUCT));
|
|
if ((err == NO_ISAM_ERR) || (err == ISAM_TRUNCATION))
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
break;
|
|
case TYPE_TIMESTAMP:
|
|
err = ISAMPutData(lpISAMTableDef, lpSqlNodeColumn->node.column.Id,
|
|
SQL_C_TIMESTAMP, &(lpSqlNodeValue->value.Timestamp),
|
|
lpSqlNodeValue->sqlIsNull ?
|
|
(SDWORD) SQL_NULL_DATA : sizeof(TIMESTAMP_STRUCT));
|
|
if ((err == NO_ISAM_ERR) || (err == ISAM_TRUNCATION))
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
break;
|
|
default:
|
|
err = ERR_INTERNAL;
|
|
}
|
|
if (err == ISAM_TRUNCATION) {
|
|
finalErr = ERR_DATATRUNCATED;
|
|
err = NO_ISAM_ERR;
|
|
}
|
|
if (err != NO_ISAM_ERR) {
|
|
ISAMGetErrorMessage(lpstmt->lpdbc->lpISAM,
|
|
(LPUSTR)lpstmt->szISAMError);
|
|
if (ERR_SUCCESS == ISAMDeleteRecord(lpISAMTableDef))
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
return err;
|
|
}
|
|
|
|
idxSqlNodeColumns = lpSqlNodeColumns->node.columns.Next;
|
|
idxSqlNodeValues = lpSqlNodeValues->node.values.Next;
|
|
}
|
|
|
|
/* Write the updated row to the table. */
|
|
err = ISAMUpdateRecord(lpISAMTableDef);
|
|
if (err != NO_ISAM_ERR) {
|
|
ISAMGetErrorMessage(lpstmt->lpdbc->lpISAM, (LPUSTR)lpstmt->szISAMError);
|
|
if (ERR_SUCCESS == ISAMDeleteRecord(lpISAMTableDef))
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
return err;
|
|
}
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
|
|
/* Increment row count */
|
|
lpstmt->cRowCount++;
|
|
|
|
/* Leave if just inserting a single row */
|
|
if (lpSqlNodeSelect == NULL)
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
/* DELETE statement */
|
|
case NODE_TYPE_DELETE:
|
|
{
|
|
/* Get the table handle and predicate node. */
|
|
lpstmt->fDMLTxn = TRUE;
|
|
lpSqlNodeTable = ToNode (lpstmt->lpSqlStmt, lpSqlNode->node.delet.Table);
|
|
lpISAMTableDef = lpSqlNodeTable->node.table.Handle;
|
|
if (lpSqlNode->node.delet.Predicate == NO_SQLNODE)
|
|
lpSqlNodePredicate = NULL;
|
|
else
|
|
lpSqlNodePredicate = ToNode(lpstmt->lpSqlStmt,
|
|
lpSqlNode->node.delet.Predicate);
|
|
|
|
/* Rewind the table */
|
|
err = Rewind(lpstmt, lpSqlNodeTable, FALSE);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Delete each record that satisfies the WHERE clause. */
|
|
lpstmt->cRowCount = 0;
|
|
while (TRUE) {
|
|
|
|
/* Get next record */
|
|
err = NextRecord(lpstmt, lpSqlNodeTable,
|
|
lpSqlNodePredicate);
|
|
if (err == ERR_NODATAFOUND)
|
|
break;
|
|
else if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Delete record */
|
|
err = ISAMDeleteRecord(lpISAMTableDef);
|
|
if (err != NO_ISAM_ERR) {
|
|
ISAMGetErrorMessage(lpstmt->lpdbc->lpISAM,
|
|
(LPUSTR)lpstmt->szISAMError);
|
|
return err;
|
|
}
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
|
|
/* Increase count */
|
|
(lpstmt->cRowCount)++;
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
/* UPDATE statement */
|
|
case NODE_TYPE_UPDATE:
|
|
{
|
|
/* Get the table handle, predicate node. */
|
|
lpstmt->fDMLTxn = TRUE;
|
|
lpSqlNodeTable = ToNode(lpstmt->lpSqlStmt, lpSqlNode->node.update.Table);
|
|
lpISAMTableDef = lpSqlNodeTable->node.table.Handle;
|
|
if (lpSqlNode->node.update.Predicate == NO_SQLNODE)
|
|
lpSqlNodePredicate = NULL;
|
|
else
|
|
lpSqlNodePredicate = ToNode(lpstmt->lpSqlStmt,
|
|
lpSqlNode->node.update.Predicate);
|
|
|
|
/* Rewind the table */
|
|
err = Rewind(lpstmt, lpSqlNodeTable, FALSE);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Update each record that satisfies the WHERE clause. */
|
|
lpstmt->cRowCount = 0;
|
|
while (TRUE) {
|
|
|
|
/* Get next record */
|
|
err = NextRecord(lpstmt, lpSqlNodeTable,
|
|
lpSqlNodePredicate);
|
|
if (err == ERR_NODATAFOUND)
|
|
break;
|
|
else if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* For each set column... */
|
|
idxSqlNodeSets = lpSqlNode->node.update.Updatevalues;
|
|
while (idxSqlNodeSets != NO_SQLNODE) {
|
|
|
|
/* Get the column */
|
|
lpSqlNodeSets = ToNode(lpstmt->lpSqlStmt, idxSqlNodeSets);
|
|
lpSqlNodeColumn = ToNode(lpstmt->lpSqlStmt, lpSqlNodeSets->node.updatevalues.Column);
|
|
|
|
/* Get the value to set the column to */
|
|
lpSqlNodeValue = ToNode(lpstmt->lpSqlStmt, lpSqlNodeSets->node.updatevalues.Value);
|
|
err = EvaluateExpression(lpstmt, lpSqlNodeValue);
|
|
if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Put the data into the record */
|
|
switch (lpSqlNodeColumn->sqlDataType) {
|
|
case TYPE_DOUBLE:
|
|
case TYPE_INTEGER:
|
|
switch (lpSqlNodeValue->sqlDataType) {
|
|
case TYPE_DOUBLE:
|
|
case TYPE_INTEGER:
|
|
lpSqlNodeColumn->value.Double =
|
|
lpSqlNodeValue->value.Double;
|
|
break;
|
|
case TYPE_NUMERIC:
|
|
if (!(lpSqlNodeValue->sqlIsNull))
|
|
CharToDouble(lpSqlNodeValue->value.String,
|
|
s_lstrlen(lpSqlNodeValue->value.String), FALSE,
|
|
-1.7E308, 1.7E308,
|
|
&(lpSqlNodeColumn->value.Double));
|
|
else
|
|
lpSqlNodeColumn->value.Double = 0.0;
|
|
break;
|
|
case TYPE_CHAR:
|
|
case TYPE_BINARY:
|
|
case TYPE_DATE:
|
|
case TYPE_TIME:
|
|
case TYPE_TIMESTAMP:
|
|
default:
|
|
return ERR_INTERNAL;
|
|
}
|
|
err = ISAMPutData(lpISAMTableDef,
|
|
lpSqlNodeColumn->node.column.Id, SQL_C_DOUBLE,
|
|
&(lpSqlNodeColumn->value.Double),
|
|
lpSqlNodeValue->sqlIsNull ?
|
|
(SDWORD) SQL_NULL_DATA : sizeof(double));
|
|
if ((err == NO_ISAM_ERR) || (err == ISAM_TRUNCATION))
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
break;
|
|
case TYPE_NUMERIC:
|
|
lpSqlNodeColumn->value.String =ToString(lpstmt->lpSqlStmt,
|
|
lpSqlNodeColumn->node.column.Value);
|
|
switch (lpSqlNodeValue->sqlDataType) {
|
|
case TYPE_DOUBLE:
|
|
case TYPE_INTEGER:
|
|
if (!(lpSqlNodeValue->sqlIsNull)) {
|
|
if (DoubleToChar(lpSqlNodeValue->value.Double,
|
|
FALSE, lpSqlNodeColumn->value.String,
|
|
1 + 2 + lpSqlNodeColumn->sqlPrecision)) {
|
|
lpSqlNodeColumn->value.String[
|
|
1+2+lpSqlNodeColumn->sqlPrecision-1]='\0';
|
|
finalErr = ERR_DATATRUNCATED;
|
|
}
|
|
}
|
|
else
|
|
s_lstrcpy(lpSqlNodeColumn->value.String, "0");
|
|
break;
|
|
case TYPE_NUMERIC:
|
|
s_lstrcpy(lpSqlNodeColumn->value.String,
|
|
lpSqlNodeValue->value.String);
|
|
break;
|
|
case TYPE_CHAR:
|
|
case TYPE_BINARY:
|
|
case TYPE_DATE:
|
|
case TYPE_TIME:
|
|
case TYPE_TIMESTAMP:
|
|
default:
|
|
return ERR_INTERNAL;
|
|
}
|
|
err = BCDNormalize(lpSqlNodeColumn->value.String,
|
|
s_lstrlen(lpSqlNodeColumn->value.String),
|
|
lpSqlNodeColumn->value.String,
|
|
1 + 2 + lpSqlNodeColumn->sqlPrecision,
|
|
lpSqlNodeColumn->sqlPrecision,
|
|
lpSqlNodeColumn->sqlScale);
|
|
if (err == ERR_DATATRUNCATED) {
|
|
finalErr = ERR_DATATRUNCATED;
|
|
err = ERR_SUCCESS;
|
|
}
|
|
|
|
if (err == ERR_SUCCESS) {
|
|
|
|
err = ISAMPutData(lpISAMTableDef,
|
|
lpSqlNodeColumn->node.column.Id, SQL_C_CHAR,
|
|
lpSqlNodeColumn->value.String,
|
|
lpSqlNodeValue->sqlIsNull ? SQL_NULL_DATA :
|
|
s_lstrlen(lpSqlNodeColumn->value.String));
|
|
|
|
if ((err == NO_ISAM_ERR) || (err == ISAM_TRUNCATION))
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
|
|
}
|
|
break;
|
|
case TYPE_CHAR:
|
|
err = ISAMPutData(lpISAMTableDef,
|
|
lpSqlNodeColumn->node.column.Id, SQL_C_CHAR,
|
|
lpSqlNodeValue->value.String,
|
|
lpSqlNodeValue->sqlIsNull ? SQL_NULL_DATA :
|
|
s_lstrlen(lpSqlNodeValue->value.String));
|
|
if ((err == NO_ISAM_ERR) || (err == ISAM_TRUNCATION))
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
break;
|
|
case TYPE_BINARY:
|
|
err = ISAMPutData(lpISAMTableDef,
|
|
lpSqlNodeColumn->node.column.Id, SQL_C_BINARY,
|
|
BINARY_DATA(lpSqlNodeValue->value.Binary),
|
|
lpSqlNodeValue->sqlIsNull ? SQL_NULL_DATA :
|
|
BINARY_LENGTH(lpSqlNodeValue->value.Binary));
|
|
if ((err == NO_ISAM_ERR) || (err == ISAM_TRUNCATION))
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
break;
|
|
case TYPE_DATE:
|
|
err = ISAMPutData(lpISAMTableDef,
|
|
lpSqlNodeColumn->node.column.Id, SQL_C_DATE,
|
|
&(lpSqlNodeValue->value.Date),
|
|
lpSqlNodeValue->sqlIsNull ?
|
|
(SDWORD) SQL_NULL_DATA : sizeof(DATE_STRUCT));
|
|
if ((err == NO_ISAM_ERR) || (err == ISAM_TRUNCATION))
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
break;
|
|
case TYPE_TIME:
|
|
err = ISAMPutData(lpISAMTableDef,
|
|
lpSqlNodeColumn->node.column.Id, SQL_C_TIME,
|
|
&(lpSqlNodeValue->value.Time),
|
|
lpSqlNodeValue->sqlIsNull ?
|
|
(SDWORD) SQL_NULL_DATA : sizeof(TIME_STRUCT));
|
|
if ((err == NO_ISAM_ERR) || (err == ISAM_TRUNCATION))
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
break;
|
|
case TYPE_TIMESTAMP:
|
|
err = ISAMPutData(lpISAMTableDef,
|
|
lpSqlNodeColumn->node.column.Id, SQL_C_TIMESTAMP,
|
|
&(lpSqlNodeValue->value.Timestamp),
|
|
lpSqlNodeValue->sqlIsNull ?
|
|
(SDWORD) SQL_NULL_DATA : sizeof(TIMESTAMP_STRUCT));
|
|
if ((err == NO_ISAM_ERR) || (err == ISAM_TRUNCATION))
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
break;
|
|
default:
|
|
return ERR_INTERNAL;
|
|
}
|
|
if (err == ISAM_TRUNCATION) {
|
|
finalErr = ERR_DATATRUNCATED;
|
|
err = NO_ISAM_ERR;
|
|
}
|
|
|
|
if (err != NO_ISAM_ERR) {
|
|
ISAMGetErrorMessage(lpstmt->lpdbc->lpISAM,
|
|
(LPUSTR)lpstmt->szISAMError);
|
|
return err;
|
|
}
|
|
|
|
idxSqlNodeSets = lpSqlNodeSets->node.updatevalues.Next;
|
|
}
|
|
|
|
/* Write the updated row to the table. */
|
|
err = ISAMUpdateRecord (lpISAMTableDef);
|
|
if (err != NO_ISAM_ERR) {
|
|
ISAMGetErrorMessage(lpstmt->lpdbc->lpISAM,
|
|
(LPUSTR)lpstmt->szISAMError);
|
|
return err;
|
|
}
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
|
|
/* Increase count */
|
|
(lpstmt->cRowCount)++;
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
case NODE_TYPE_CREATEINDEX:
|
|
{
|
|
UWORD icol[MAX_COLUMNS_IN_INDEX];
|
|
BOOL fDescending[MAX_COLUMNS_IN_INDEX];
|
|
UWORD count;
|
|
|
|
/* Make sure this DDL statement is allowed now */
|
|
err = TxnCapableForDDL(lpstmt);
|
|
if (err == ERR_DDLIGNORED)
|
|
return ERR_DDLIGNORED;
|
|
else if (err == ERR_DDLCAUSEDACOMMIT)
|
|
finalErr = ERR_DDLCAUSEDACOMMIT;
|
|
else if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Get handle to table */
|
|
lpSqlNodeTable = ToNode(lpstmt->lpSqlStmt,
|
|
lpSqlNode->node.createindex.Table);
|
|
lpISAMTableDef = lpSqlNodeTable->node.table.Handle;
|
|
|
|
/* Get definition of the index */
|
|
idxSortcolumns = lpSqlNode->node.createindex.Columns;
|
|
count = 0;
|
|
while (idxSortcolumns != NO_SQLNODE) {
|
|
lpSqlNodeSortcolumns =
|
|
ToNode(lpstmt->lpSqlStmt, idxSortcolumns);
|
|
lpSqlNodeColumn = ToNode(lpstmt->lpSqlStmt,
|
|
lpSqlNodeSortcolumns->node.sortcolumns.Column);
|
|
icol[count] = lpSqlNodeColumn->node.column.Id;
|
|
fDescending[count] =
|
|
lpSqlNodeSortcolumns->node.sortcolumns.Descending;
|
|
count++;
|
|
idxSortcolumns = lpSqlNodeSortcolumns->node.sortcolumns.Next;
|
|
}
|
|
|
|
/* Create the index */
|
|
err = ISAMCreateIndex(lpISAMTableDef,
|
|
ToString(lpstmt->lpSqlStmt,
|
|
lpSqlNode->node.createindex.Index),
|
|
lpSqlNode->node.createindex.Unique,
|
|
count, icol, fDescending);
|
|
if (err != NO_ISAM_ERR) {
|
|
ISAMGetErrorMessage(lpstmt->lpdbc->lpISAM,
|
|
(LPUSTR) lpstmt->szISAMError);
|
|
return err;
|
|
}
|
|
if (lpstmt->lpdbc->lpISAM->fSchemaInfoTransactioned)
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
}
|
|
break;
|
|
|
|
case NODE_TYPE_DROPINDEX:
|
|
/* Make sure this DDL statement is allowed now */
|
|
err = TxnCapableForDDL(lpstmt);
|
|
if (err == ERR_DDLIGNORED)
|
|
return ERR_DDLIGNORED;
|
|
else if (err == ERR_DDLCAUSEDACOMMIT)
|
|
finalErr = ERR_DDLCAUSEDACOMMIT;
|
|
else if (err != ERR_SUCCESS)
|
|
return err;
|
|
|
|
/* Delete the index */
|
|
err = ISAMDeleteIndex(lpstmt->lpdbc->lpISAM,
|
|
ToString(lpstmt->lpSqlStmt,
|
|
lpSqlNode->node.dropindex.Index));
|
|
if (err != NO_ISAM_ERR) {
|
|
ISAMGetErrorMessage(lpstmt->lpdbc->lpISAM, (LPUSTR) lpstmt->szISAMError);
|
|
return err;
|
|
}
|
|
if (lpstmt->lpdbc->lpISAM->fSchemaInfoTransactioned)
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
break;
|
|
|
|
|
|
case NODE_TYPE_PASSTHROUGH:
|
|
err = ISAMExecute(lpstmt->lpISAMStatement, &(lpstmt->cRowCount));
|
|
if (err != NO_ISAM_ERR) {
|
|
ISAMGetErrorMessage(lpstmt->lpdbc->lpISAM, (LPUSTR)lpstmt->szISAMError);
|
|
return err;
|
|
}
|
|
lpstmt->fISAMTxnStarted = TRUE;
|
|
break;
|
|
|
|
/* Internal error if these nodes are hanging off the root */
|
|
case NODE_TYPE_TABLES:
|
|
case NODE_TYPE_VALUES:
|
|
case NODE_TYPE_COLUMNS:
|
|
case NODE_TYPE_SORTCOLUMNS:
|
|
case NODE_TYPE_GROUPBYCOLUMNS:
|
|
case NODE_TYPE_UPDATEVALUES:
|
|
case NODE_TYPE_CREATECOLS:
|
|
case NODE_TYPE_BOOLEAN:
|
|
case NODE_TYPE_COMPARISON:
|
|
case NODE_TYPE_ALGEBRAIC:
|
|
case NODE_TYPE_SCALAR:
|
|
case NODE_TYPE_AGGREGATE:
|
|
case NODE_TYPE_TABLE:
|
|
case NODE_TYPE_COLUMN:
|
|
case NODE_TYPE_STRING:
|
|
case NODE_TYPE_NUMERIC:
|
|
case NODE_TYPE_PARAMETER:
|
|
case NODE_TYPE_USER:
|
|
case NODE_TYPE_NULL:
|
|
case NODE_TYPE_DATE:
|
|
case NODE_TYPE_TIME:
|
|
case NODE_TYPE_TIMESTAMP:
|
|
default:
|
|
return ERR_INTERNAL;
|
|
}
|
|
if (!fSelect && (lpstmt->lpdbc->lpISAM->fTxnCapable != SQL_TC_NONE) &&
|
|
lpstmt->lpdbc->fAutoCommitTxn) {
|
|
err = SQLTransact(SQL_NULL_HENV, (HDBC)lpstmt->lpdbc, SQL_COMMIT);
|
|
if ((err != SQL_SUCCESS) && (err != SQL_SUCCESS_WITH_INFO)) {
|
|
lpstmt->errcode = lpstmt->lpdbc->errcode;
|
|
s_lstrcpy(lpstmt->szISAMError, lpstmt->lpdbc->szISAMError);
|
|
return lpstmt->errcode;
|
|
}
|
|
}
|
|
return finalErr;
|
|
}
|
|
|
|
/***************************************************************************/
|