/***************************************************************************/ /* SEMANTIC.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 //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" /***************************************************************************/ RETCODE INTFUNC FindColumn(LPSTMT lpstmt, LPSQLTREE lpSql, BOOL fCaseSensitive, LPSQLNODE lpSqlNodeColumn, SQLNODEIDX idxTable, STRINGIDX idxQualifier) /* Sees if the table identified by idxTable contains the column identifeid */ /* lpSqNodeColumn. If it does, the semantic information for the column */ /* is filled in. Otherwise an error is returned. */ { LPSQLNODE lpSqlNodeTable; LPISAMTABLEDEF lpISAMTableDef; LPSTR lpName; UWORD index; LPUSTR lpTableAlias; LPUSTR lpColumnAlias; LPUSTR lpTableName; BOOL matchedAlias = FALSE; BOOL fIsPassthroughTable = FALSE; /* Table/Column name given? */ lpName = (LPSTR) ToString(lpSql, lpSqlNodeColumn->node.column.Column); lpSqlNodeTable = ToNode(lpSql, idxTable); if (lpSqlNodeColumn->node.column.Tablealias != NO_STRING) { // this can be either a tablename or an tablename alias /* Yes. Make sure it matches */ int found = FALSE; if (lpSqlNodeTable->node.table.Alias != NO_STRING && lpSqlNodeColumn->node.column.Qualifier == NO_STRING) { // //Comparing Aliases // lpTableAlias = ToString(lpSql, lpSqlNodeTable->node.table.Alias); lpColumnAlias = ToString(lpSql, lpSqlNodeColumn->node.column.Tablealias); if (fCaseSensitive) { if (!s_lstrcmp(lpTableAlias, lpColumnAlias)) { found = TRUE; } } else { if (!s_lstrcmpi(lpTableAlias, lpColumnAlias)) { found = TRUE; } } if (!found) return ERR_COLUMNNOTFOUND; } // //Not comparing Aliases // if (!found) // try matching the column qualifier + alias to the table qualifier + table name { lpTableAlias = ToString(lpSql, lpSqlNodeTable->node.table.Qualifier); lpColumnAlias = ToString(lpSql, lpSqlNodeColumn->node.column.Tablealias); char *pszNewQualifier = NULL; if (lpSqlNodeColumn->node.column.Qualifier != NO_STRING) { LPSTR lpColumnQualifier = (LPSTR) ToString (lpSql, lpSqlNodeColumn->node.column.Qualifier); if ( (strlen(lpColumnQualifier) >= 4) && (_strnicmp("root", lpColumnQualifier, 4) == 0)) { // absolute qualifier (no need to change) pszNewQualifier = new char [s_lstrlen (lpColumnAlias)+ s_lstrlen (lpColumnQualifier) + 3]; s_lstrcpy (pszNewQualifier, lpColumnQualifier); s_lstrcat (pszNewQualifier, "."); s_lstrcat (pszNewQualifier, lpColumnAlias); } else { // concatenate the current namespace with the qualifier LPUSTR currQual = ISAMDatabase (lpSql->node.root.lpISAM); pszNewQualifier = new char [s_lstrlen (lpColumnAlias)+ s_lstrlen (currQual) + s_lstrlen (lpColumnQualifier) + 3 + 2]; s_lstrcpy (pszNewQualifier, currQual); s_lstrcat (pszNewQualifier, "\\"); s_lstrcat (pszNewQualifier, lpColumnQualifier); s_lstrcat (pszNewQualifier, "."); s_lstrcat (pszNewQualifier, lpColumnAlias); } } else { LPUSTR currQual = ISAMDatabase (lpSql->node.root.lpISAM); pszNewQualifier = new char [s_lstrlen (lpColumnAlias)+ s_lstrlen (currQual)+ 2 + 1]; s_lstrcpy (pszNewQualifier, currQual); s_lstrcat (pszNewQualifier, "."); s_lstrcat (pszNewQualifier, lpColumnAlias); } // get the table name and concatenate with the table qualifier // before the check lpTableName = ToString(lpSql, lpSqlNodeTable->node.table.Name); char *lpszFQTN = new char [s_lstrlen (lpTableName) + s_lstrlen (lpTableAlias) + 2 + 1]; s_lstrcpy (lpszFQTN, lpTableAlias); s_lstrcat (lpszFQTN, "."); s_lstrcat (lpszFQTN, lpTableName); if (fCaseSensitive) { if (!lstrcmp(lpszFQTN, pszNewQualifier)) { found = TRUE; } } else { if (!lstrcmpi(lpszFQTN, pszNewQualifier)) { found = TRUE; } } delete [] pszNewQualifier; delete lpszFQTN; } else { matchedAlias = TRUE; } //Check if this is the passthrough SQL table if (lpSqlNodeTable->node.table.Handle->fIsPassthroughSQL) found = TRUE; if (!found) return ERR_COLUMNNOTFOUND; } /* Search the table definition for a column with a matching name. */ lpISAMTableDef = lpSqlNodeTable->node.table.Handle; BOOL fPassthroughSQLWithAlias = FALSE; LPSTR lpAlias = NULL; if ( (lpSqlNodeColumn->node.column.Tablealias != NO_STRING) && lpSqlNodeTable->node.table.Handle->fIsPassthroughSQL ) { lpAlias = (LPSTR) ToString(lpSql, lpSqlNodeColumn->node.column.Tablealias); if ( lpAlias && lstrlen(lpAlias) ) fPassthroughSQLWithAlias = TRUE; } ClassColumnInfoBase* cInfoBase = lpISAMTableDef->pColumnInfo; if ( !cInfoBase->IsValid() ) { return ERR_COLUMNNOTFOUND; } //Sai //The current column is lpName, if this is a system property //and SYSPROPS=FALSE then return an error if (lpISAMTableDef && lpISAMTableDef->lpISAM) { if ( ! lpISAMTableDef->lpISAM->fSysProps) { //we are not asking for system properties //so a system property should not be in the SELECT list if (_strnicmp("__", lpName, 2) == 0) { return ERR_COLUMNNOTFOUND; } } } UWORD cNumberOfCols = cInfoBase->GetNumberOfColumns(); char pColumnName [MAX_COLUMN_NAME_LENGTH+1]; char pColumnAlias [MAX_COLUMN_NAME_LENGTH+1]; pColumnAlias[0] = 0; BOOL fMatch = FALSE; for (index = 0; index < cNumberOfCols; index++) { fMatch = FALSE; if ( FAILED(cInfoBase->GetColumnName(index, pColumnName, pColumnAlias)) ) { return ERR_COLUMNNOTFOUND; } if (fCaseSensitive) { if (!s_lstrcmp(lpName, pColumnName)) { fMatch = TRUE; // break; } } else { if (!s_lstrcmpi(lpName, pColumnName)) { fMatch = TRUE; // break; } } //Extra check for passthrough SQL if (fPassthroughSQLWithAlias && fMatch) { fMatch = FALSE; if (fCaseSensitive) { if (!s_lstrcmp(lpAlias, pColumnAlias)) break; } else { if (!s_lstrcmpi(lpAlias, pColumnAlias)) break; } } if (fMatch) break; } #ifdef TESTING if (idxQualifier != NO_STRING) { char* lpTestQual = ToString(lpSql, lpSqlNodeTable->table.Qualifier); char* lpOrgQual = ToString(lpSql, idxQualifier); if (lpTestQual && lpOrgQual && strcmp(lpTestQual, lpOrgQual)) return ERR_COLUMNNOTFOUND; } #endif /* Error if not found */ if (index == cNumberOfCols) { s_lstrcpy(lpstmt->szError, lpName); return ERR_COLUMNNOTFOUND; } /* Set the table and column information in the column node. */ lpSqlNodeColumn->node.column.Table = idxTable; lpSqlNodeColumn->node.column.Id = (UWORD) index; if (lpSqlNodeColumn->node.column.Tablealias == NO_STRING) { lpSqlNodeColumn->node.column.Tablealias = lpSqlNodeTable->node.table.Name; } // to keep this consistent I'm going to put a flag in to say if it matched // a table name or an alias and put the qualifier in too lpSqlNodeColumn->node.column.MatchedAlias = matchedAlias; lpSqlNodeColumn->node.column.Qualifier = lpSqlNodeTable->node.table.Qualifier; // go further and set the column table reference to the table index. In fact, // this is the best way of doing this. Not sure why SYWARE didn't go this way // originally. Probably as it ensured every table had an alias. It really // encapsualtes all the above info. lpSqlNodeColumn->node.column.TableIndex = idxTable; /* Figure out type of data */ cInfoBase->GetSQLType(index, lpSqlNodeColumn->sqlSqlType); switch (lpSqlNodeColumn->sqlSqlType) { case SQL_DECIMAL: case SQL_NUMERIC: { lpSqlNodeColumn->sqlDataType = TYPE_NUMERIC; UDWORD uwPrec = 0; cInfoBase->GetPrecision(index, uwPrec); lpSqlNodeColumn->sqlPrecision = (SWORD)uwPrec; cInfoBase->GetScale(index, lpSqlNodeColumn->sqlScale); } break; case SQL_BIGINT: { lpSqlNodeColumn->sqlDataType = TYPE_NUMERIC; UDWORD uwPrec = 0; cInfoBase->GetPrecision(index, uwPrec); lpSqlNodeColumn->sqlPrecision = (SWORD)uwPrec; lpSqlNodeColumn->sqlScale = 0; } break; case SQL_TINYINT: lpSqlNodeColumn->sqlDataType = TYPE_INTEGER; lpSqlNodeColumn->sqlPrecision = 3; lpSqlNodeColumn->sqlScale = 0; break; case SQL_SMALLINT: lpSqlNodeColumn->sqlDataType = TYPE_INTEGER; lpSqlNodeColumn->sqlPrecision = 5; lpSqlNodeColumn->sqlScale = 0; break; case SQL_INTEGER: lpSqlNodeColumn->sqlDataType = TYPE_INTEGER; lpSqlNodeColumn->sqlPrecision = 10; lpSqlNodeColumn->sqlScale = 0; break; case SQL_BIT: lpSqlNodeColumn->sqlDataType = TYPE_INTEGER; lpSqlNodeColumn->sqlPrecision = 1; lpSqlNodeColumn->sqlScale = 0; break; case SQL_REAL: lpSqlNodeColumn->sqlDataType = TYPE_DOUBLE; lpSqlNodeColumn->sqlPrecision = 7; lpSqlNodeColumn->sqlScale = NO_SCALE; break; case SQL_FLOAT: case SQL_DOUBLE: lpSqlNodeColumn->sqlDataType = TYPE_DOUBLE; lpSqlNodeColumn->sqlPrecision = 15; lpSqlNodeColumn->sqlScale = NO_SCALE; break; case SQL_CHAR: case SQL_VARCHAR: case SQL_LONGVARCHAR: { lpSqlNodeColumn->sqlDataType = TYPE_CHAR; UDWORD uwPrec = 0; cInfoBase->GetPrecision(index, uwPrec); lpSqlNodeColumn->sqlPrecision = (SWORD)uwPrec; if (lpSqlNodeColumn->sqlPrecision > MAX_CHAR_LITERAL_LENGTH) lpSqlNodeColumn->sqlPrecision = MAX_CHAR_LITERAL_LENGTH; lpSqlNodeColumn->sqlScale = NO_SCALE; } break; case SQL_BINARY: case SQL_VARBINARY: case SQL_LONGVARBINARY: { lpSqlNodeColumn->sqlDataType = TYPE_BINARY; UDWORD uwPrec = 0; cInfoBase->GetPrecision(index, uwPrec); lpSqlNodeColumn->sqlPrecision = (SWORD)uwPrec; lpSqlNodeColumn->sqlScale = NO_SCALE; } break; case SQL_DATE: lpSqlNodeColumn->sqlDataType = TYPE_DATE; lpSqlNodeColumn->sqlPrecision = 10; lpSqlNodeColumn->sqlScale = NO_SCALE; break; case SQL_TIME: lpSqlNodeColumn->sqlDataType = TYPE_TIME; lpSqlNodeColumn->sqlPrecision = 8; lpSqlNodeColumn->sqlScale = NO_SCALE; break; case SQL_TIMESTAMP: lpSqlNodeColumn->sqlDataType = TYPE_TIMESTAMP; if (TIMESTAMP_SCALE > 0) lpSqlNodeColumn->sqlPrecision = 20 + TIMESTAMP_SCALE; else lpSqlNodeColumn->sqlPrecision = 19; lpSqlNodeColumn->sqlScale = TIMESTAMP_SCALE; break; default: return ERR_NOTSUPPORTED; } return ERR_SUCCESS; } /***************************************************************************/ void INTFUNC ErrorOpCode(LPSTMT lpstmt, UWORD opCode) /* Puts opcode in szError (as a string) */ { switch (opCode) { case OP_NONE: s_lstrcpy(lpstmt->szError, ""); break; case OP_EQ: s_lstrcpy(lpstmt->szError, "="); break; case OP_NE: s_lstrcpy(lpstmt->szError, "<>"); break; case OP_LE: s_lstrcpy(lpstmt->szError, "<="); break; case OP_LT: s_lstrcpy(lpstmt->szError, "<"); break; case OP_GE: s_lstrcpy(lpstmt->szError, ">="); break; case OP_GT: s_lstrcpy(lpstmt->szError, ">"); break; case OP_IN: s_lstrcpy(lpstmt->szError, "IN"); break; case OP_NOTIN: s_lstrcpy(lpstmt->szError, "NOT IN"); break; case OP_LIKE: s_lstrcpy(lpstmt->szError, "LIKE"); break; case OP_NOTLIKE: s_lstrcpy(lpstmt->szError, "NOT LIKE"); break; case OP_NEG: s_lstrcpy(lpstmt->szError, "-"); break; case OP_PLUS: s_lstrcpy(lpstmt->szError, "+"); break; case OP_MINUS: s_lstrcpy(lpstmt->szError, "-"); break; case OP_TIMES: s_lstrcpy(lpstmt->szError, "*"); break; case OP_DIVIDEDBY: s_lstrcpy(lpstmt->szError, "/"); break; case OP_NOT: s_lstrcpy(lpstmt->szError, "NOT"); break; case OP_AND: s_lstrcpy(lpstmt->szError, "AND"); break; case OP_OR: s_lstrcpy(lpstmt->szError, "OR"); break; case OP_EXISTS: s_lstrcpy(lpstmt->szError, "EXISTS"); break; default: s_lstrcpy(lpstmt->szError, ""); break; } } /***************************************************************************/ void INTFUNC ErrorAggCode(LPSTMT lpstmt, UWORD aggCode) /* Puts aggreagate operator code in szError (as a string) */ { switch(aggCode) { case AGG_AVG: s_lstrcpy(lpstmt->szError, "AVG"); break; case AGG_COUNT: s_lstrcpy(lpstmt->szError, "COUNT"); break; case AGG_MAX: s_lstrcpy(lpstmt->szError, "MAX"); break; case AGG_MIN: s_lstrcpy(lpstmt->szError, "MIN"); break; case AGG_SUM: s_lstrcpy(lpstmt->szError, "SUM"); break; default: s_lstrcpy(lpstmt->szError, ""); break; } } /***************************************************************************/ RETCODE INTFUNC TypeCheck(LPSTMT lpstmt, LPSQLTREE lpSql, SQLNODEIDX idxLeft, SQLNODEIDX idxRight, UWORD opCode, SWORD FAR *pfDataType, SWORD FAR *pfSqlType, SDWORD FAR *pPrecision, SWORD FAR *pScale) /* Checks to see if the types of the two children nodes are compatible. */ /* If the type of one of the children is unknown (because it was a */ /* parameter or the value NULL), it set to the type of the other child. */ { LPSQLNODE lpSqlNodeLeft; LPSQLNODE lpSqlNodeRight; SWORD fDataTypeLeft; SWORD fSqlTypeLeft; SDWORD precisionLeft; SWORD scaleLeft; SWORD fDataTypeRight; SWORD fSqlTypeRight; SDWORD precisionRight; SWORD scaleRight; SWORD fDataType; SWORD fSqlType; SDWORD precision; SWORD scale; #define MAX(a, b) ((a) > (b) ? (a) : (b)) #define MIN(a, b) ((a) < (b) ? (a) : (b)) /* Get left type */ if (idxLeft == NO_SQLNODE) return ERR_INTERNAL; lpSqlNodeLeft = ToNode(lpSql, idxLeft); fDataTypeLeft = lpSqlNodeLeft->sqlDataType; fSqlTypeLeft = lpSqlNodeLeft->sqlSqlType; precisionLeft = lpSqlNodeLeft->sqlPrecision; scaleLeft = lpSqlNodeLeft->sqlScale; /* Is there a right epxression? */ if (idxRight != NO_SQLNODE) { /* Yes. Get right type. */ lpSqlNodeRight = ToNode(lpSql, idxRight); fDataTypeRight = lpSqlNodeRight->sqlDataType; fSqlTypeRight = lpSqlNodeRight->sqlSqlType; precisionRight = lpSqlNodeRight->sqlPrecision; scaleRight = lpSqlNodeRight->sqlScale; } else { /* No. This must be an arithmetic negation operator. Use left */ /* type as the right type */ if (opCode != OP_NEG) return ERR_INTERNAL; if (fDataTypeLeft == TYPE_UNKNOWN) return ERR_UNKNOWNTYPE; fDataTypeRight = fDataTypeLeft; fSqlTypeRight = fSqlTypeLeft; precisionRight = precisionLeft; scaleRight = scaleLeft; } /* Is left side unknown? */ if (fDataTypeLeft == TYPE_UNKNOWN) { /* Yes. Error if right side is unknown also */ if (fDataTypeRight == TYPE_UNKNOWN) return ERR_UNKNOWNTYPE; /* If right side is string value, make sure opcode is legal */ if (fDataTypeRight == TYPE_CHAR) { switch (opCode) { case OP_NONE: case OP_EQ: case OP_NE: case OP_LE: case OP_LT: case OP_GE: case OP_GT: case OP_IN: case OP_NOTIN: case OP_LIKE: case OP_NOTLIKE: break; case OP_NEG: ErrorOpCode(lpstmt, opCode); return ERR_INVALIDOPERAND; case OP_PLUS: if (fSqlTypeRight == SQL_LONGVARCHAR) { ErrorOpCode(lpstmt, opCode); return ERR_INVALIDOPERAND; } break; case OP_MINUS: case OP_TIMES: case OP_DIVIDEDBY: case OP_NOT: case OP_AND: case OP_OR: case OP_EXISTS: ErrorOpCode(lpstmt, opCode); return ERR_INVALIDOPERAND; } } /* If right side is date value, make sure opcode is legal */ else if (fDataTypeRight == TYPE_DATE) { switch (opCode) { case OP_NONE: case OP_EQ: case OP_NE: case OP_LE: case OP_LT: case OP_GE: case OP_GT: case OP_IN: case OP_NOTIN: break; case OP_LIKE: case OP_NOTLIKE: case OP_NEG: ErrorOpCode(lpstmt, opCode); return ERR_INVALIDOPERAND; case OP_PLUS: case OP_MINUS: break; case OP_TIMES: case OP_DIVIDEDBY: case OP_NOT: case OP_AND: case OP_OR: case OP_EXISTS: ErrorOpCode(lpstmt, opCode); return ERR_INVALIDOPERAND; } } /* Adding to a date? */ if ((fDataTypeRight == TYPE_DATE) && (opCode == OP_PLUS)) { /* Yes. The left side must be integer */ lpSqlNodeLeft->sqlDataType = TYPE_INTEGER; lpSqlNodeLeft->sqlSqlType = SQL_INTEGER; lpSqlNodeLeft->sqlPrecision = 10; lpSqlNodeLeft->sqlScale = 0; } else { /* No. Use type from right as type for left */ /* Note: This disallows ? from being a date in (? - 3) */ /* Note: This disallows ? from being a date in (? + 3) */ lpSqlNodeLeft->sqlDataType = fDataTypeRight; lpSqlNodeLeft->sqlSqlType = fSqlTypeRight; lpSqlNodeLeft->sqlPrecision = precisionRight; lpSqlNodeLeft->sqlScale = scaleRight; /* If string concatenation, adjust type and precision */ if ((fDataTypeRight == TYPE_CHAR) && (opCode == OP_PLUS)) { lpSqlNodeLeft->sqlSqlType = SQL_VARCHAR; lpSqlNodeLeft->sqlPrecision = MAX_CHAR_LITERAL_LENGTH; } } /* Adding to a date? */ if ((fDataTypeRight == TYPE_DATE) && (opCode == OP_PLUS)) { /* Yes. The result type is a date */ if (pfDataType != NULL) *pfDataType = lpSqlNodeRight->sqlDataType; if (pfSqlType != NULL) *pfSqlType = lpSqlNodeRight->sqlSqlType; if (pPrecision != NULL) *pPrecision = lpSqlNodeRight->sqlPrecision; if (pScale != NULL) *pScale = lpSqlNodeRight->sqlScale; } /* Calculating the differnce of two dates? */ else if ((lpSqlNodeLeft->sqlDataType == TYPE_DATE) && (opCode == OP_MINUS)) { /* Yes. The result type is integer */ if (pfDataType != NULL) *pfDataType = TYPE_INTEGER; if (pfSqlType != NULL) *pfSqlType = SQL_INTEGER; if (pPrecision != NULL) *pPrecision = 10; if (pScale != NULL) *pScale = 0; } else { /* No. The result type is the same as the left side */ if (pfDataType != NULL) *pfDataType = lpSqlNodeLeft->sqlDataType; if (pfSqlType != NULL) *pfSqlType = lpSqlNodeLeft->sqlSqlType; if (pPrecision != NULL) *pPrecision = lpSqlNodeLeft->sqlPrecision; if (pScale != NULL) *pScale = lpSqlNodeLeft->sqlScale; } } /* Is right side unknown? */ else if (fDataTypeRight == TYPE_UNKNOWN) { /* Yes. If left side is string value, make sure opcode is legal */ if (fDataTypeLeft == TYPE_CHAR) { switch (opCode) { case OP_NONE: case OP_EQ: case OP_NE: case OP_LE: case OP_LT: case OP_GE: case OP_GT: case OP_IN: case OP_NOTIN: case OP_LIKE: case OP_NOTLIKE: break; case OP_NEG: ErrorOpCode(lpstmt, opCode); return ERR_INVALIDOPERAND; case OP_PLUS: if (fSqlTypeLeft == SQL_LONGVARCHAR) { ErrorOpCode(lpstmt, opCode); return ERR_INVALIDOPERAND; } break; case OP_MINUS: case OP_TIMES: case OP_DIVIDEDBY: case OP_NOT: case OP_AND: case OP_OR: case OP_EXISTS: ErrorOpCode(lpstmt, opCode); return ERR_INVALIDOPERAND; } } /* If left side is date value, make sure opcode is legal */ else if (fDataTypeLeft == TYPE_DATE) { switch (opCode) { case OP_NONE: case OP_EQ: case OP_NE: case OP_LE: case OP_LT: case OP_GE: case OP_GT: case OP_IN: case OP_NOTIN: break; case OP_LIKE: case OP_NOTLIKE: case OP_NEG: ErrorOpCode(lpstmt, opCode); return ERR_INVALIDOPERAND; case OP_PLUS: case OP_MINUS: break; case OP_TIMES: case OP_DIVIDEDBY: case OP_NOT: case OP_AND: case OP_OR: case OP_EXISTS: ErrorOpCode(lpstmt, opCode); return ERR_INVALIDOPERAND; } } /* Adding to a date? */ if ((fDataTypeLeft == TYPE_DATE) && (opCode == OP_PLUS)) { /* Yes. The right side must be integer */ lpSqlNodeRight->sqlDataType = TYPE_INTEGER; lpSqlNodeRight->sqlSqlType = SQL_INTEGER; lpSqlNodeRight->sqlPrecision = 10; lpSqlNodeRight->sqlScale = 0; } else { /* No. Use type from left as type for right */ /* Note: This disallows ? from being a number in ( - ? ) */ /* Note: This disallows ? from being a date in ( + ? ) */ lpSqlNodeRight->sqlDataType = fDataTypeLeft; lpSqlNodeRight->sqlSqlType = fSqlTypeLeft; lpSqlNodeRight->sqlPrecision = precisionLeft; lpSqlNodeRight->sqlScale = scaleLeft; /* If string concatenation, adjust type and precision */ if ((fDataTypeLeft == TYPE_CHAR) && (opCode == OP_PLUS)) { lpSqlNodeRight->sqlSqlType = SQL_VARCHAR; lpSqlNodeRight->sqlPrecision = MAX_CHAR_LITERAL_LENGTH; } } /* Adding to a date? */ if ((fDataTypeLeft == TYPE_DATE) && (opCode == OP_PLUS)) { /* Yes. The result type is a date */ if (pfDataType != NULL) *pfDataType = TYPE_DATE; if (pfSqlType != NULL) *pfSqlType = SQL_DATE; if (pPrecision != NULL) *pPrecision = 10; if (pScale != NULL) *pScale = 0; } /* Calculating the difference of two dates? */ else if ((fDataTypeLeft == TYPE_DATE) && (opCode == OP_MINUS)) { /* Yes. The result type is integer */ if (pfDataType != NULL) *pfDataType = TYPE_INTEGER; if (pfSqlType != NULL) *pfSqlType = SQL_INTEGER; if (pPrecision != NULL) *pPrecision = 10; if (pScale != NULL) *pScale = 0; } else { /* No. The result type is the same as the right side */ if (pfDataType != NULL) *pfDataType = lpSqlNodeRight->sqlDataType; if (pfSqlType != NULL) *pfSqlType = lpSqlNodeRight->sqlSqlType; if (pPrecision != NULL) *pPrecision = lpSqlNodeRight->sqlPrecision; if (pScale != NULL) *pScale = lpSqlNodeRight->sqlScale; } } /* Do types match? */ else if ((fDataTypeLeft == fDataTypeRight) || ((fDataTypeLeft == TYPE_DOUBLE) && (fDataTypeRight == TYPE_NUMERIC)) || ((fDataTypeLeft == TYPE_DOUBLE) && (fDataTypeRight == TYPE_INTEGER)) || ((fDataTypeLeft == TYPE_NUMERIC) && (fDataTypeRight == TYPE_DOUBLE)) || ((fDataTypeLeft == TYPE_NUMERIC) && (fDataTypeRight == TYPE_INTEGER)) || ((fDataTypeLeft == TYPE_INTEGER) && (fDataTypeRight == TYPE_DOUBLE)) || ((fDataTypeLeft == TYPE_INTEGER) && (fDataTypeRight == TYPE_NUMERIC))) { /* Yes. If left side is string value, make sure opcode is legal */ if (fDataTypeLeft == TYPE_CHAR) { switch (opCode) { case OP_NONE: case OP_EQ: case OP_NE: case OP_LE: case OP_LT: case OP_GE: case OP_GT: case OP_IN: case OP_NOTIN: case OP_LIKE: case OP_NOTLIKE: break; case OP_NEG: ErrorOpCode(lpstmt, opCode); return ERR_INVALIDOPERAND; case OP_PLUS: if ((fSqlTypeRight == SQL_LONGVARCHAR) || (fSqlTypeLeft == SQL_LONGVARCHAR)) { ErrorOpCode(lpstmt, opCode); return ERR_INVALIDOPERAND; } break; case OP_MINUS: case OP_TIMES: case OP_DIVIDEDBY: case OP_NOT: case OP_AND: case OP_OR: case OP_EXISTS: ErrorOpCode(lpstmt, opCode); return ERR_INVALIDOPERAND; } } /* If left side is date value, make sure opcode is legal */ else if (fDataTypeLeft == TYPE_DATE) { switch (opCode) { case OP_NONE: case OP_EQ: case OP_NE: case OP_LE: case OP_LT: case OP_GE: case OP_GT: case OP_IN: case OP_NOTIN: break; case OP_LIKE: case OP_NOTLIKE: case OP_NEG: case OP_PLUS: ErrorOpCode(lpstmt, opCode); return ERR_INVALIDOPERAND; case OP_MINUS: break; case OP_TIMES: case OP_DIVIDEDBY: case OP_NOT: case OP_AND: case OP_OR: case OP_EXISTS: ErrorOpCode(lpstmt, opCode); return ERR_INVALIDOPERAND; } } /* Figure out resultant type */ if ((fDataTypeLeft == TYPE_NUMERIC) && (fDataTypeRight == TYPE_DOUBLE)) fDataType = TYPE_DOUBLE; else if ((fDataTypeLeft == TYPE_INTEGER) && (fDataTypeRight == TYPE_DOUBLE)) fDataType = TYPE_DOUBLE; else if ((fDataTypeLeft == TYPE_INTEGER) && (fDataTypeRight == TYPE_NUMERIC)) fDataType = TYPE_NUMERIC; else fDataType = fDataTypeLeft; if (pfDataType != NULL) *pfDataType = fDataType; /* Figure out resultant SQL type, precision, and scale */ switch (fSqlTypeLeft) { case SQL_DOUBLE: switch (fSqlTypeRight) { case SQL_DOUBLE: if (pfSqlType != NULL) *pfSqlType = fSqlTypeRight; if (pPrecision != NULL) *pPrecision = precisionRight; if (pScale != NULL) *pScale = NO_SCALE; break; case SQL_FLOAT: case SQL_REAL: case SQL_DECIMAL: case SQL_NUMERIC: case SQL_BIGINT: case SQL_INTEGER: case SQL_SMALLINT: case SQL_TINYINT: case SQL_BIT: if (pfSqlType != NULL) *pfSqlType = fSqlTypeLeft; if (pPrecision != NULL) *pPrecision = precisionLeft; if (pScale != NULL) *pScale = NO_SCALE; break; case SQL_LONGVARCHAR: case SQL_VARCHAR: case SQL_CHAR: case SQL_LONGVARBINARY: case SQL_VARBINARY: case SQL_BINARY: case SQL_DATE: case SQL_TIME: case SQL_TIMESTAMP: return ERR_INTERNAL; default: return ERR_NOTSUPPORTED; } break; case SQL_FLOAT: switch (fSqlTypeRight) { case SQL_DOUBLE: case SQL_FLOAT: if (pfSqlType != NULL) *pfSqlType = fSqlTypeRight; if (pPrecision != NULL) *pPrecision = precisionRight; if (pScale != NULL) *pScale = NO_SCALE; break; case SQL_REAL: case SQL_DECIMAL: case SQL_NUMERIC: case SQL_BIGINT: case SQL_INTEGER: case SQL_SMALLINT: case SQL_TINYINT: case SQL_BIT: if (pfSqlType != NULL) *pfSqlType = fSqlTypeLeft; if (pPrecision != NULL) *pPrecision = precisionLeft; if (pScale != NULL) *pScale = NO_SCALE; break; case SQL_LONGVARCHAR: case SQL_VARCHAR: case SQL_CHAR: case SQL_LONGVARBINARY: case SQL_VARBINARY: case SQL_BINARY: case SQL_DATE: case SQL_TIME: case SQL_TIMESTAMP: return ERR_INTERNAL; default: return ERR_NOTSUPPORTED; } break; case SQL_REAL: switch (fSqlTypeRight) { case SQL_DOUBLE: case SQL_FLOAT: case SQL_REAL: if (pfSqlType != NULL) *pfSqlType = fSqlTypeRight; if (pPrecision != NULL) *pPrecision = precisionRight; if (pScale != NULL) *pScale = NO_SCALE; break; case SQL_DECIMAL: case SQL_NUMERIC: case SQL_BIGINT: case SQL_INTEGER: case SQL_SMALLINT: case SQL_TINYINT: case SQL_BIT: if (pfSqlType != NULL) *pfSqlType = fSqlTypeLeft; if (pPrecision != NULL) *pPrecision = precisionLeft; if (pScale != NULL) *pScale = NO_SCALE; break; case SQL_LONGVARCHAR: case SQL_VARCHAR: case SQL_CHAR: case SQL_LONGVARBINARY: case SQL_VARBINARY: case SQL_BINARY: case SQL_DATE: case SQL_TIME: case SQL_TIMESTAMP: return ERR_INTERNAL; default: return ERR_NOTSUPPORTED; } break; case SQL_DECIMAL: case SQL_NUMERIC: case SQL_BIGINT: case SQL_INTEGER: case SQL_SMALLINT: case SQL_TINYINT: case SQL_BIT: switch (fSqlTypeRight) { case SQL_DOUBLE: case SQL_FLOAT: case SQL_REAL: if (pfSqlType != NULL) *pfSqlType = fSqlTypeRight; if (pPrecision != NULL) *pPrecision = precisionRight; if (pScale != NULL) *pScale = NO_SCALE; break; case SQL_DECIMAL: case SQL_NUMERIC: case SQL_BIGINT: case SQL_INTEGER: case SQL_SMALLINT: case SQL_TINYINT: case SQL_BIT: if ((fSqlTypeRight == SQL_DECIMAL) || (fSqlTypeLeft == SQL_DECIMAL)) fSqlType = SQL_DECIMAL; else if ((fSqlTypeRight == SQL_NUMERIC) || (fSqlTypeLeft == SQL_NUMERIC)) fSqlType = SQL_NUMERIC; else if ((fSqlTypeRight == SQL_BIGINT) || (fSqlTypeLeft == SQL_BIGINT)) fSqlType = SQL_BIGINT; else if ((fSqlTypeRight == SQL_INTEGER) || (fSqlTypeLeft == SQL_INTEGER)) fSqlType = SQL_INTEGER; else if ((fSqlTypeRight == SQL_SMALLINT) || (fSqlTypeLeft == SQL_SMALLINT)) fSqlType = SQL_SMALLINT; else if ((fSqlTypeRight == SQL_TINYINT) || (fSqlTypeLeft == SQL_TINYINT)) fSqlType = SQL_TINYINT; else fSqlType = SQL_BIT; if (pfSqlType != NULL) *pfSqlType = fSqlType; switch (opCode) { case OP_NONE: case OP_EQ: case OP_NE: case OP_LE: case OP_LT: case OP_GE: case OP_GT: case OP_IN: case OP_NOTIN: if (pPrecision != NULL) *pPrecision = precisionLeft; if (pScale != NULL) *pScale = scaleLeft; break; case OP_LIKE: case OP_NOTLIKE: ErrorOpCode(lpstmt, opCode); return ERR_INVALIDOPERAND; case OP_NEG: if (pPrecision != NULL) *pPrecision = precisionLeft; if (pScale != NULL) *pScale = scaleLeft; break; case OP_PLUS: case OP_MINUS: scale = MAX(scaleRight, scaleLeft); switch (fSqlType) { case SQL_DOUBLE: case SQL_FLOAT: case SQL_REAL: return ERR_INTERNAL; case SQL_DECIMAL: case SQL_NUMERIC: case SQL_BIGINT: precision = scale + MAX(precisionRight - scaleRight, precisionLeft - scaleLeft) + 1; break; case SQL_INTEGER: precision = 10; break; case SQL_SMALLINT: precision = 5; break; case SQL_TINYINT: precision = 3; break; case SQL_BIT: precision = 1; break; case SQL_LONGVARCHAR: case SQL_VARCHAR: case SQL_CHAR: case SQL_LONGVARBINARY: case SQL_VARBINARY: case SQL_BINARY: case SQL_DATE: case SQL_TIME: case SQL_TIMESTAMP: return ERR_INTERNAL; default: return ERR_NOTSUPPORTED; } if (pPrecision != NULL) *pPrecision = precision; if (pScale != NULL) *pScale = scale; break; case OP_TIMES: case OP_DIVIDEDBY: scale = scaleRight + scaleLeft; switch (fSqlType) { case SQL_DOUBLE: case SQL_FLOAT: case SQL_REAL: return ERR_INTERNAL; case SQL_DECIMAL: case SQL_NUMERIC: case SQL_BIGINT: precision = scale + (precisionRight - scaleRight) + (precisionLeft - scaleLeft); break; case SQL_INTEGER: precision = 10; break; case SQL_SMALLINT: precision = 5; break; case SQL_TINYINT: precision = 3; break; case SQL_BIT: precision = 1; break; case SQL_LONGVARCHAR: case SQL_VARCHAR: case SQL_CHAR: case SQL_LONGVARBINARY: case SQL_VARBINARY: case SQL_BINARY: case SQL_DATE: case SQL_TIME: case SQL_TIMESTAMP: return ERR_INTERNAL; default: return ERR_NOTSUPPORTED; } if (pPrecision != NULL) *pPrecision = precision; if (pScale != NULL) *pScale = scale; break; case OP_NOT: case OP_AND: case OP_OR: case OP_EXISTS: ErrorOpCode(lpstmt, opCode); return ERR_INVALIDOPERAND; } break; case SQL_LONGVARCHAR: case SQL_VARCHAR: case SQL_CHAR: case SQL_LONGVARBINARY: case SQL_VARBINARY: case SQL_BINARY: case SQL_DATE: case SQL_TIME: case SQL_TIMESTAMP: return ERR_INTERNAL; default: return ERR_NOTSUPPORTED; } break; case SQL_LONGVARCHAR: switch (fSqlTypeRight) { case SQL_DOUBLE: case SQL_FLOAT: case SQL_REAL: case SQL_DECIMAL: case SQL_NUMERIC: case SQL_BIGINT: case SQL_INTEGER: case SQL_SMALLINT: case SQL_TINYINT: case SQL_BIT: return ERR_INTERNAL; case SQL_LONGVARCHAR: if (pfSqlType != NULL) *pfSqlType = fSqlTypeRight; if (pPrecision != NULL) *pPrecision = precisionRight; if (pScale != NULL) *pScale = NO_SCALE; break; case SQL_VARCHAR: case SQL_CHAR: if (pfSqlType != NULL) *pfSqlType = fSqlTypeLeft; if (pPrecision != NULL) { if (precisionLeft > precisionRight) *pPrecision = precisionLeft; else *pPrecision = precisionRight; } if (pScale != NULL) *pScale = NO_SCALE; break; case SQL_LONGVARBINARY: case SQL_VARBINARY: case SQL_BINARY: case SQL_DATE: case SQL_TIME: case SQL_TIMESTAMP: return ERR_INTERNAL; default: return ERR_NOTSUPPORTED; } break; case SQL_VARCHAR: switch (fSqlTypeRight) { case SQL_DOUBLE: case SQL_FLOAT: case SQL_REAL: case SQL_DECIMAL: case SQL_NUMERIC: case SQL_BIGINT: case SQL_INTEGER: case SQL_SMALLINT: case SQL_TINYINT: case SQL_BIT: return ERR_INTERNAL; case SQL_LONGVARCHAR: case SQL_VARCHAR: if (pfSqlType != NULL) *pfSqlType = fSqlTypeRight; if (pPrecision != NULL) *pPrecision = precisionRight; if (pScale != NULL) *pScale = NO_SCALE; break; case SQL_CHAR: if (pfSqlType != NULL) *pfSqlType = fSqlTypeLeft; if (pPrecision != NULL) *pPrecision = precisionLeft; if (pScale != NULL) *pScale = NO_SCALE; break; case SQL_LONGVARBINARY: case SQL_VARBINARY: case SQL_BINARY: case SQL_DATE: case SQL_TIME: case SQL_TIMESTAMP: return ERR_INTERNAL; default: return ERR_NOTSUPPORTED; } /* Adjust precision for string concatenation operator */ if ((opCode == OP_PLUS) && (pPrecision != NULL)) *pPrecision = MIN(precisionRight + precisionLeft, MAX_CHAR_LITERAL_LENGTH); break; case SQL_CHAR: switch (fSqlTypeRight) { case SQL_DOUBLE: case SQL_FLOAT: case SQL_REAL: case SQL_DECIMAL: case SQL_NUMERIC: case SQL_BIGINT: case SQL_INTEGER: case SQL_SMALLINT: case SQL_TINYINT: case SQL_BIT: return ERR_INTERNAL; case SQL_LONGVARCHAR: case SQL_VARCHAR: case SQL_CHAR: if (pfSqlType != NULL) *pfSqlType = fSqlTypeRight; if (pPrecision != NULL) *pPrecision = precisionRight; if (pScale != NULL) *pScale = NO_SCALE; break; case SQL_LONGVARBINARY: case SQL_VARBINARY: case SQL_BINARY: case SQL_DATE: case SQL_TIME: case SQL_TIMESTAMP: return ERR_INTERNAL; default: return ERR_NOTSUPPORTED; } /* Adjust precision for string concatenation operator */ if ((opCode == OP_PLUS) && (pPrecision != NULL)) *pPrecision = MIN(precisionRight + precisionLeft, MAX_CHAR_LITERAL_LENGTH); break; case SQL_LONGVARBINARY: switch (fSqlTypeRight) { case SQL_DOUBLE: case SQL_FLOAT: case SQL_REAL: case SQL_DECIMAL: case SQL_NUMERIC: case SQL_BIGINT: case SQL_INTEGER: case SQL_SMALLINT: case SQL_TINYINT: case SQL_BIT: case SQL_LONGVARCHAR: case SQL_VARCHAR: case SQL_CHAR: return ERR_INTERNAL; case SQL_LONGVARBINARY: if (pfSqlType != NULL) *pfSqlType = fSqlTypeRight; if (pPrecision != NULL) *pPrecision = precisionRight; if (pScale != NULL) *pScale = NO_SCALE; break; case SQL_VARBINARY: case SQL_BINARY: if (pfSqlType != NULL) *pfSqlType = fSqlTypeLeft; if (pPrecision != NULL) *pPrecision = precisionLeft; if (pScale != NULL) *pScale = NO_SCALE; break; case SQL_DATE: case SQL_TIME: case SQL_TIMESTAMP: return ERR_INTERNAL; default: return ERR_NOTSUPPORTED; } break; case SQL_VARBINARY: switch (fSqlTypeRight) { case SQL_DOUBLE: case SQL_FLOAT: case SQL_REAL: case SQL_DECIMAL: case SQL_NUMERIC: case SQL_BIGINT: case SQL_INTEGER: case SQL_SMALLINT: case SQL_TINYINT: case SQL_BIT: case SQL_LONGVARCHAR: case SQL_VARCHAR: case SQL_CHAR: return ERR_INTERNAL; case SQL_LONGVARBINARY: case SQL_VARBINARY: if (pfSqlType != NULL) *pfSqlType = fSqlTypeRight; if (pPrecision != NULL) *pPrecision = precisionRight; if (pScale != NULL) *pScale = NO_SCALE; break; case SQL_BINARY: if (pfSqlType != NULL) *pfSqlType = fSqlTypeLeft; if (pPrecision != NULL) *pPrecision = precisionLeft; if (pScale != NULL) *pScale = NO_SCALE; break; case SQL_DATE: case SQL_TIME: case SQL_TIMESTAMP: return ERR_INTERNAL; default: return ERR_NOTSUPPORTED; } break; case SQL_BINARY: switch (fSqlTypeRight) { case SQL_DOUBLE: case SQL_FLOAT: case SQL_REAL: case SQL_DECIMAL: case SQL_NUMERIC: case SQL_BIGINT: case SQL_INTEGER: case SQL_SMALLINT: case SQL_TINYINT: case SQL_BIT: case SQL_LONGVARCHAR: case SQL_VARCHAR: case SQL_CHAR: return ERR_INTERNAL; case SQL_LONGVARBINARY: case SQL_VARBINARY: case SQL_BINARY: if (pfSqlType != NULL) *pfSqlType = fSqlTypeRight; if (pPrecision != NULL) *pPrecision = precisionRight; if (pScale != NULL) *pScale = NO_SCALE; break; case SQL_DATE: case SQL_TIME: case SQL_TIMESTAMP: return ERR_INTERNAL; default: return ERR_NOTSUPPORTED; } break; case SQL_DATE: switch (fSqlTypeRight) { case SQL_DOUBLE: case SQL_FLOAT: case SQL_REAL: case SQL_DECIMAL: case SQL_NUMERIC: case SQL_BIGINT: case SQL_INTEGER: case SQL_SMALLINT: case SQL_TINYINT: case SQL_BIT: case SQL_LONGVARCHAR: case SQL_VARCHAR: case SQL_CHAR: case SQL_LONGVARBINARY: case SQL_VARBINARY: case SQL_BINARY: return ERR_INTERNAL; case SQL_DATE: switch (opCode) { case OP_NONE: case OP_EQ: case OP_NE: case OP_LE: case OP_LT: case OP_GE: case OP_GT: case OP_IN: case OP_NOTIN: if (pfSqlType != NULL) *pfSqlType = fSqlTypeLeft; if (pPrecision != NULL) *pPrecision = precisionLeft; if (pScale != NULL) *pScale = scaleLeft; break; case OP_LIKE: case OP_NOTLIKE: case OP_NEG: case OP_PLUS: ErrorOpCode(lpstmt, opCode); return ERR_INVALIDOPERAND; case OP_MINUS: if (pfDataType != NULL) *pfDataType = TYPE_INTEGER; if (pPrecision != NULL) *pPrecision = 10; if (pfSqlType != NULL) *pfSqlType = SQL_INTEGER; if (pScale != NULL) *pScale = 0; break; case OP_TIMES: case OP_DIVIDEDBY: case OP_NOT: case OP_AND: case OP_OR: case OP_EXISTS: ErrorOpCode(lpstmt, opCode); return ERR_INVALIDOPERAND; } break; case SQL_TIME: case SQL_TIMESTAMP: return ERR_INTERNAL; default: return ERR_NOTSUPPORTED; } break; case SQL_TIME: switch (fSqlTypeRight) { case SQL_DOUBLE: case SQL_FLOAT: case SQL_REAL: case SQL_DECIMAL: case SQL_NUMERIC: case SQL_BIGINT: case SQL_INTEGER: case SQL_SMALLINT: case SQL_TINYINT: case SQL_BIT: case SQL_LONGVARCHAR: case SQL_VARCHAR: case SQL_CHAR: case SQL_LONGVARBINARY: case SQL_VARBINARY: case SQL_BINARY: case SQL_DATE: return ERR_INTERNAL; case SQL_TIME: if (pfSqlType != NULL) *pfSqlType = fSqlTypeRight; if (pPrecision != NULL) *pPrecision = precisionRight; if (pScale != NULL) *pScale = NO_SCALE; break; case SQL_TIMESTAMP: return ERR_INTERNAL; default: return ERR_NOTSUPPORTED; } break; case SQL_TIMESTAMP: switch (fSqlTypeRight) { case SQL_DOUBLE: case SQL_FLOAT: case SQL_REAL: case SQL_DECIMAL: case SQL_NUMERIC: case SQL_BIGINT: case SQL_INTEGER: case SQL_SMALLINT: case SQL_TINYINT: case SQL_BIT: case SQL_LONGVARCHAR: case SQL_VARCHAR: case SQL_CHAR: case SQL_LONGVARBINARY: case SQL_VARBINARY: case SQL_BINARY: case SQL_DATE: case SQL_TIME: return ERR_INTERNAL; case SQL_TIMESTAMP: if (pfSqlType != NULL) *pfSqlType = fSqlTypeRight; if (pPrecision != NULL) *pPrecision = precisionRight; if (pScale != NULL) *pScale = TIMESTAMP_SCALE; break; default: return ERR_NOTSUPPORTED; } break; default: return ERR_NOTSUPPORTED; } } else if ((fDataTypeLeft == TYPE_DATE) && ((fDataTypeRight == TYPE_DOUBLE) || (fDataTypeRight == TYPE_NUMERIC) || (fDataTypeRight == TYPE_INTEGER))) { switch (opCode) { case OP_NONE: case OP_EQ: case OP_NE: case OP_LE: case OP_LT: case OP_GE: case OP_GT: case OP_IN: case OP_NOTIN: case OP_LIKE: case OP_NOTLIKE: case OP_NEG: ErrorOpCode(lpstmt, opCode); return ERR_INVALIDOPERAND; case OP_PLUS: case OP_MINUS: if (pfDataType != NULL) *pfDataType = fDataTypeLeft; if (pfSqlType != NULL) *pfSqlType = fSqlTypeLeft; if (pPrecision != NULL) *pPrecision = precisionLeft; if (pScale != NULL) *pScale = scaleLeft; break; case OP_TIMES: case OP_DIVIDEDBY: case OP_NOT: case OP_AND: case OP_OR: case OP_EXISTS: ErrorOpCode(lpstmt, opCode); return ERR_INVALIDOPERAND; } } else if ((fDataTypeRight == TYPE_DATE) && ((fDataTypeLeft == TYPE_DOUBLE) || (fDataTypeLeft == TYPE_NUMERIC) || (fDataTypeLeft == TYPE_INTEGER))) { switch (opCode) { case OP_NONE: case OP_EQ: case OP_NE: case OP_LE: case OP_LT: case OP_GE: case OP_GT: case OP_IN: case OP_NOTIN: case OP_LIKE: case OP_NOTLIKE: case OP_NEG: ErrorOpCode(lpstmt, opCode); return ERR_INVALIDOPERAND; case OP_PLUS: if (pfDataType != NULL) *pfDataType = fDataTypeRight; if (pfSqlType != NULL) *pfSqlType = fSqlTypeRight; if (pPrecision != NULL) *pPrecision = precisionRight; if (pScale != NULL) *pScale = scaleRight; break; case OP_MINUS: case OP_TIMES: case OP_DIVIDEDBY: case OP_NOT: case OP_AND: case OP_OR: case OP_EXISTS: ErrorOpCode(lpstmt, opCode); return ERR_INVALIDOPERAND; } } else { ErrorOpCode(lpstmt, opCode); return ERR_INVALIDOPERAND; } return ERR_SUCCESS; } /***************************************************************************/ BOOL INTFUNC FindAggregate(LPSQLTREE lpSql, SQLNODEIDX idxNode) /* Looks for the next aggregate function on a select list */ { LPSQLNODE lpSqlNode; if (idxNode == NO_SQLNODE) return FALSE; lpSqlNode = ToNode(lpSql, idxNode); switch (lpSqlNode->sqlNodeType) { case NODE_TYPE_NONE: case NODE_TYPE_ROOT: case NODE_TYPE_CREATE: case NODE_TYPE_DROP: return FALSE; /* Internal error */ case NODE_TYPE_SELECT: return FALSE; 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: return FALSE; /* Internal error */ case NODE_TYPE_VALUES: while (TRUE) { if (FindAggregate(lpSql, lpSqlNode->node.values.Value)) return TRUE; if (lpSqlNode->node.values.Next == NO_SQLNODE) return FALSE; lpSqlNode = ToNode(lpSql, lpSqlNode->node.values.Next); } 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: return FALSE; /* Internal error */ case NODE_TYPE_ALGEBRAIC: if (FindAggregate(lpSql, lpSqlNode->node.algebraic.Left)) return TRUE; return (FindAggregate(lpSql, lpSqlNode->node.algebraic.Right)); case NODE_TYPE_SCALAR: return (FindAggregate(lpSql, lpSqlNode->node.scalar.Arguments)); case NODE_TYPE_AGGREGATE: return TRUE; case NODE_TYPE_TABLE: return FALSE; /* Internal error */ 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: return FALSE; default: return FALSE; /* Internal error */ } } /***************************************************************************/ RETCODE INTFUNC SelectCheck(LPSTMT lpstmt, LPSQLTREE FAR *lplpSql, SQLNODEIDX idxNode, BOOL fCaseSensitive, SQLNODEIDX idxEnclosingStatement) /* Walks a SELECT parse tree, checks it for semantic correctness, and */ /* fills in the semantic information. */ { LPSQLNODE lpSqlNode; RETCODE err; UWORD count; UDWORD offset; BOOL fIsGroupby; if (idxNode == NO_SQLNODE) return ERR_SUCCESS; lpSqlNode = ToNode(*lplpSql, idxNode); /* Save pointer to enclosing statement */ lpSqlNode->node.select.EnclosingStatement = idxEnclosingStatement; /* Check the list of tables */ err = SemanticCheck(lpstmt, lplpSql, lpSqlNode->node.select.Tables, FALSE, fCaseSensitive, NO_SQLNODE, idxNode); if (err != ERR_SUCCESS) return err; /* Check the GROUP BY columns */ err = SemanticCheck(lpstmt, lplpSql, lpSqlNode->node.select.Groupbycolumns, FALSE, fCaseSensitive, NO_SQLNODE, idxNode); if (err != ERR_SUCCESS) return err; /* Is there a GROUP BY clause? */ count = 0; offset = 1; if (lpSqlNode->node.select.Groupbycolumns != NO_SQLNODE) { UDWORD length; SQLNODEIDX idxGroupbycolumns; LPSQLNODE lpSqlNodeGroupbycolumns; LPSQLNODE lpSqlNodeColumn; /* Yes. Error if SELECT * */ if (lpSqlNode->node.select.Values == NO_SQLNODE) return ERR_NOSELECTSTAR; /* Figure out offset and length of each column when it */ /* is in the sort file */ idxGroupbycolumns = lpSqlNode->node.select.Groupbycolumns; while (idxGroupbycolumns != NO_SQLNODE) { /* Add this column to the count */ count++; /* Error if too many columns */ if (count > MAX_COLUMNS_IN_GROUP_BY) return ERR_GROUPBYTOOLARGE; /* Save column offset */ lpSqlNodeGroupbycolumns = ToNode(*lplpSql, idxGroupbycolumns); lpSqlNodeColumn = ToNode(*lplpSql, lpSqlNodeGroupbycolumns->node.groupbycolumns.Column); lpSqlNodeColumn->node.column.Offset = offset; /* Get length of the column */ switch (lpSqlNodeColumn->sqlDataType) { case TYPE_DOUBLE: length = sizeof(double); break; case TYPE_NUMERIC: length = 2 + lpSqlNodeColumn->sqlPrecision; break; case TYPE_INTEGER: length = sizeof(double); break; case TYPE_CHAR: length = lpSqlNodeColumn->sqlPrecision; if (length > MAX_CHAR_LITERAL_LENGTH) { s_lstrcpy((char*)lpstmt->szError, ToString(*lplpSql, lpSqlNodeColumn->node.column.Column)); return ERR_CANTGROUPBYONTHIS; } break; case TYPE_BINARY: s_lstrcpy((char*)lpstmt->szError, ToString(*lplpSql, lpSqlNodeColumn->node.column.Column)); return ERR_CANTGROUPBYONTHIS; case TYPE_DATE: length = 10; break; case TYPE_TIME: length = 8; break; case TYPE_TIMESTAMP: if (TIMESTAMP_SCALE > 0) length = 20 + TIMESTAMP_SCALE; else length = 19; break; default: s_lstrcpy((char*)lpstmt->szError, ToString(*lplpSql, lpSqlNodeColumn->node.column.Column)); return ERR_CANTGROUPBYONTHIS; } /* Put length of the column column description */ lpSqlNodeColumn->node.column.Length = length; /* Go to next column */ offset += (length); offset++; /* for the IS NULL flag */ idxGroupbycolumns = lpSqlNodeGroupbycolumns->node.groupbycolumns.Next; } /* Set flag */ fIsGroupby = TRUE; } else { /* No. Are there any aggregates in the select list? */ if (!FindAggregate(*lplpSql, lpSqlNode->node.select.Values)) { /* No. Error if there is a HAVING clause */ if (lpSqlNode->node.select.Having != NO_SQLNODE) return ERR_NOGROUPBY; /* Set flag */ fIsGroupby = FALSE; } else { /* Yes. Set flags */ lpSqlNode->node.select.ImplicitGroupby = TRUE; fIsGroupby = TRUE; } } /* SELECT * ? */ if (lpSqlNode->node.select.Values == NO_SQLNODE) { SQLNODEIDX idxTables; LPSQLNODE lpSqlNodeTables; SQLNODEIDX idxTable; LPSQLNODE lpSqlNodeTable; // STRINGIDX idxAlias; LPISAMTABLEDEF lpTableHandle; SQLNODEIDX idxValues; LPSQLNODE lpSqlNodeValues; SQLNODEIDX idxValuesPrev; LPSQLNODE lpSqlNodeValuesPrev; SQLNODEIDX idxColumn; LPSQLNODE lpSqlNodeColumn; SWORD idx; /* Yes. Loop though all the tables in the table list */ idxValuesPrev = NO_SQLNODE; idxTables = lpSqlNode->node.select.Tables; while (idxTables != NO_SQLNODE) { /* Get pointer to the next table */ lpSqlNodeTables = ToNode(*lplpSql, idxTables); idxTables = lpSqlNodeTables->node.tables.Next; /* Get pointer to this table */ idxTable = lpSqlNodeTables->node.tables.Table; lpSqlNodeTable = ToNode(*lplpSql, idxTable); /* Loop through all the columns of this table */ lpTableHandle = lpSqlNodeTable->node.table.Handle; ClassColumnInfoBase* cInfoBase = lpTableHandle->pColumnInfo; LPISAM myISAM = lpTableHandle->lpISAM; if ( !cInfoBase->IsValid() ) { return ERR_COLUMNNOTFOUND; } //Store able alias before allocating memory //in order to avoid bug STRINGIDX idxTheAlias = lpSqlNodeTable->node.table.Alias; STRINGIDX idxTheQual = lpSqlNodeTable->node.table.Qualifier; UWORD cNumberOfCols = cInfoBase->GetNumberOfColumns(); char pColumnName [MAX_COLUMN_NAME_LENGTH+1]; //Sai added - fetch the column alias //only filled in if using passthrough SQL, ignored otherwise char pAliasName [MAX_COLUMN_NAME_LENGTH+1]; pAliasName[0] = 0; for (idx = 0; idx < (SWORD) cNumberOfCols; idx++) { if ( FAILED(cInfoBase->GetColumnName(idx, pColumnName, pAliasName)) ) { return ERR_COLUMNNOTFOUND; } //Ignore any OLEMS specific columns which we want to hide if (myISAM && !(myISAM->fSysProps)) { if (_strnicmp("__", pColumnName, 2) == 0) { continue; } } //Ignore any 'lazy' columns BOOL fIsLazy = FALSE; if ( cInfoBase->IsLazy(idx, fIsLazy) ) { if (fIsLazy) { continue; } } //Check if this column type is support //if not we skip this column if (! ISAMGetColumnType(lpTableHandle, (UWORD) idx) ) { continue; } /* Create a node for this column */ idxColumn = AllocateNode(lplpSql, NODE_TYPE_COLUMN); if (idxColumn == NO_SQLNODE) return ERR_MEMALLOCFAIL; //Get new pointers lpSqlNodeColumn = ToNode(*lplpSql, idxColumn); lpSqlNodeTable = ToNode(*lplpSql, idxTable); lpSqlNodeColumn->node.column.Tablealias = idxTheAlias; if (lpSqlNodeTable->node.table.Alias != NO_STRING) { lpSqlNodeColumn->node.column.MatchedAlias = TRUE; } else { lpSqlNodeColumn->node.column.MatchedAlias = FALSE; } lpSqlNodeColumn->node.column.TableIndex = idxTable; lpSqlNodeColumn->node.column.Qualifier = idxTheQual; lpSqlNodeColumn->node.column.Column = AllocateString(lplpSql, (LPUSTR)pColumnName); //New - for identical column names when using passthrough SQL if ( pAliasName && lstrlen(pAliasName) ) { lpSqlNodeColumn->node.column.Tablealias = AllocateString(lplpSql, (LPUSTR)pAliasName); } //To avoid bug recalc lpSqlNodeColumn lpSqlNodeColumn = ToNode(*lplpSql, idxColumn); if (lpSqlNodeColumn->node.column.Column == NO_STRING) return ERR_MEMALLOCFAIL; lpSqlNodeColumn->node.table.Handle = NULL; lpSqlNodeColumn->node.column.Id = -1; lpSqlNodeColumn->node.column.Value = NO_STRING; lpSqlNodeColumn->node.column.InSortRecord = FALSE; lpSqlNodeColumn->node.column.Offset = 0; lpSqlNodeColumn->node.column.Length = 0; lpSqlNodeColumn->node.column.DistinctOffset = 0; lpSqlNodeColumn->node.column.DistinctLength = 0; lpSqlNodeColumn->node.column.EnclosingStatement = NO_SQLNODE; /* Put it on the list */ idxValues = AllocateNode(lplpSql, NODE_TYPE_VALUES); if (idxValues == NO_SQLNODE) return ERR_MEMALLOCFAIL; lpSqlNodeValues = ToNode(*lplpSql, idxValues); lpSqlNodeValues->node.values.Value = idxColumn; lpSqlNodeValues->node.values.Alias = NO_STRING; lpSqlNodeValues->node.values.Next = NO_SQLNODE; if (idxValuesPrev == NO_SQLNODE) { lpSqlNode = ToNode(*lplpSql, idxNode); lpSqlNode->node.select.Values = idxValues; } else { lpSqlNodeValuesPrev = ToNode(*lplpSql, idxValuesPrev); lpSqlNodeValuesPrev->node.values.Next = idxValues; } idxValuesPrev = idxValues; } } lpSqlNode = ToNode(*lplpSql, idxNode); } /* SELECT aliasname.* ? */ { SQLNODEIDX idxValuesPrev; SQLNODEIDX idxValues; LPSQLNODE lpSqlNodeValues; SQLNODEIDX idxTables; LPSQLNODE lpSqlNodeTables; SQLNODEIDX idxTable; LPSQLNODE lpSqlNodeTable; LPUSTR lpTableAlias; LPUSTR lpValuesAlias; LPSQLNODE lpSqlNodeValuesPrev; LPISAMTABLEDEF lpTableHandle; SQLNODEIDX idxColumn; LPSQLNODE lpSqlNodeColumn; SWORD idx; STRINGIDX idxAlias; SQLNODEIDX idxValuesNew; /* Yes. Loop though all the nodes in the select list */ idxValuesPrev = NO_SQLNODE; idxValues = lpSqlNode->node.select.Values; while (idxValues != NO_SQLNODE) { /* Get pointer to the values node */ lpSqlNodeValues = ToNode(*lplpSql, idxValues); /* Is this a .* node? */ if (lpSqlNodeValues->node.values.Value != NO_SQLNODE) { /* No. Go to next entry */ idxValuesPrev = idxValues; idxValues = lpSqlNodeValues->node.values.Next; continue; } idxValues = lpSqlNodeValues->node.values.Next; /* Find the table */ lpValuesAlias = ToString(*lplpSql, lpSqlNodeValues->node.values.Alias); idxTables = lpSqlNode->node.select.Tables; //First pass : check alias's while (idxTables != NO_SQLNODE) { /* Get pointer to the table */ lpSqlNodeTables = ToNode(*lplpSql, idxTables); /* Get pointer to this table */ idxTable = lpSqlNodeTables->node.tables.Table; lpSqlNodeTable = ToNode(*lplpSql, idxTable); /* Get Alias of this table */ idxAlias = lpSqlNodeTable->node.table.Alias; /* Leave loop if it matches */ lpTableAlias = ToString(*lplpSql, idxAlias); if (fCaseSensitive) { if (!s_lstrcmp(lpTableAlias, lpValuesAlias)) break; } else { if (!s_lstrcmpi(lpTableAlias, lpValuesAlias)) break; } /* It does not match. Try next table */ idxTables = lpSqlNodeTables->node.tables.Next; } //second pass : table name if (idxTables == NO_SQLNODE) { idxTables = lpSqlNode->node.select.Tables; while (idxTables != NO_SQLNODE) { /* Get pointer to the table */ lpSqlNodeTables = ToNode(*lplpSql, idxTables); /* Get pointer to this table */ idxTable = lpSqlNodeTables->node.tables.Table; lpSqlNodeTable = ToNode(*lplpSql, idxTable); /* Get Alias of this table */ idxAlias = lpSqlNodeTable->node.table.Name; /* Leave loop if it matches */ lpTableAlias = ToString(*lplpSql, idxAlias); if (fCaseSensitive) { if (!s_lstrcmp(lpTableAlias, lpValuesAlias)) { lpSqlNodeTable->node.table.Alias = idxAlias; break; } } else { if (!s_lstrcmpi(lpTableAlias, lpValuesAlias)) { lpSqlNodeTable->node.table.Alias = idxAlias; break; } } /* It does not match. Try next table */ idxTables = lpSqlNodeTables->node.tables.Next; } } /* Error if not tables match */ if (idxTables == NO_SQLNODE) { s_lstrcpy(lpstmt->szError, lpValuesAlias); return ERR_TABLENOTFOUND; } /* Remove the
.* node */ if (idxValuesPrev == NO_SQLNODE) lpSqlNode->node.select.Values = idxValues; else { lpSqlNodeValuesPrev = ToNode(*lplpSql, idxValuesPrev); lpSqlNodeValuesPrev->node.values.Next = idxValues; } /* Loop through all the columns of the table */ lpTableHandle = lpSqlNodeTable->node.table.Handle; ClassColumnInfoBase* cInfoBase = lpTableHandle->pColumnInfo; LPISAM myISAM = lpTableHandle->lpISAM; if ( !cInfoBase->IsValid() ) { return ERR_COLUMNNOTFOUND; } //Store able alias before allocating memory //in order to avoid bug STRINGIDX idxTheAlias = lpSqlNodeTable->node.table.Alias; STRINGIDX idxTheQual = lpSqlNodeTable->node.table.Qualifier; UWORD cNumberOfCols = cInfoBase->GetNumberOfColumns(); char pColumnName [MAX_COLUMN_NAME_LENGTH+1]; //Sai added - fetch the column alias //only filled in if using passthrough SQL, ignored otherwise char pAliasName [MAX_COLUMN_NAME_LENGTH+1]; pAliasName[0] = 0; for (idx = 0; idx < (SWORD) cNumberOfCols; idx++) { if ( FAILED(cInfoBase->GetColumnName(idx, pColumnName, pAliasName)) ) { return ERR_COLUMNNOTFOUND; } //Ignore any OLEMS specific columns which we want to hide if (myISAM && !(myISAM->fSysProps)) { if (_strnicmp("__", pColumnName, 2) == 0) { continue; } } //Ignore any 'lazy' columns BOOL fIsLazy = FALSE; if ( cInfoBase->IsLazy(idx, fIsLazy) ) { if (fIsLazy) { continue; } } //Check if this column type is support //if not we skip this column if (! ISAMGetColumnType(lpTableHandle, (UWORD) idx) ) { continue; } /* Create a node for this column */ idxColumn = AllocateNode(lplpSql, NODE_TYPE_COLUMN); if (idxColumn == NO_SQLNODE) return ERR_MEMALLOCFAIL; //Get new pointers lpSqlNodeColumn = ToNode(*lplpSql, idxColumn); lpSqlNodeTable = ToNode(*lplpSql, idxTable); lpSqlNodeColumn->node.column.Tablealias = idxAlias; lpSqlNodeColumn->node.column.Column = AllocateString(lplpSql, (LPUSTR)pColumnName); if (lpSqlNodeColumn->node.column.Column == NO_STRING) return ERR_MEMALLOCFAIL; if (lpSqlNodeTable->node.table.Alias != NO_STRING) { lpSqlNodeColumn->node.column.MatchedAlias = TRUE; } else { lpSqlNodeColumn->node.column.MatchedAlias = FALSE; } //New - for identical column names when using passthrough SQL if ( pAliasName && lstrlen(pAliasName) ) { lpSqlNodeColumn->node.column.Tablealias = AllocateString(lplpSql, (LPUSTR)pAliasName); } //To avoid bug recalc lpSqlNodeColumn lpSqlNodeColumn = ToNode(*lplpSql, idxColumn); lpSqlNodeColumn->node.column.TableIndex = idxTable; lpSqlNodeColumn->node.column.Qualifier = NO_STRING; //idxTheQual; lpSqlNodeColumn->node.table.Handle = NULL; lpSqlNodeColumn->node.column.Table = NO_SQLNODE; lpSqlNodeColumn->node.column.Id = -1; lpSqlNodeColumn->node.column.Value = NO_STRING; lpSqlNodeColumn->node.column.InSortRecord = FALSE; lpSqlNodeColumn->node.column.Offset = 0; lpSqlNodeColumn->node.column.Length = 0; lpSqlNodeColumn->node.column.DistinctOffset = 0; lpSqlNodeColumn->node.column.DistinctLength = 0; lpSqlNodeColumn->node.column.EnclosingStatement = NO_SQLNODE; /* Put it on the list */ idxValuesNew = AllocateNode(lplpSql, NODE_TYPE_VALUES); if (idxValuesNew == NO_SQLNODE) return ERR_MEMALLOCFAIL; lpSqlNodeValues = ToNode(*lplpSql, idxValuesNew); lpSqlNodeValues->node.values.Value = idxColumn; lpSqlNodeValues->node.values.Alias = NO_STRING; lpSqlNodeValues->node.values.Next = idxValues; if (idxValuesPrev == NO_SQLNODE) { lpSqlNode = ToNode(*lplpSql, idxNode); lpSqlNode->node.select.Values = idxValuesNew; } else { lpSqlNodeValuesPrev = ToNode(*lplpSql, idxValuesPrev); lpSqlNodeValuesPrev->node.values.Next = idxValuesNew; } idxValuesPrev = idxValuesNew; } } lpSqlNode = ToNode(*lplpSql, idxNode); } /* Check the ORDER BY columns */ err = SemanticCheck(lpstmt, lplpSql,lpSqlNode->node.select.Sortcolumns, fIsGroupby, fCaseSensitive, NO_SQLNODE, idxNode); if (err != ERR_SUCCESS) return err; /* Check that each group by column is on sort list (and put it */ /* there if it is not already there so the sort directive will */ /* be constructed correctly)... */ { SQLNODEIDX idxGroupbycolumns; LPSQLNODE lpSqlNodeGroupbycolumns; LPSQLNODE lpSqlNodeColumnGroupby; SQLNODEIDX idxSortcolumns; SQLNODEIDX idxSortcolumnsPrev; LPSQLNODE lpSqlNodeSortcolumns; LPSQLNODE lpSqlNodeColumnSort; SQLNODEIDX idxColumn; SQLNODEIDX idxTableGroupby; // LPUSTR lpszTableGroupby; LPUSTR lpszColumnGroupby; SQLNODEIDX idxTableSort; // LPUSTR lpszTableSort; LPUSTR lpszColumnSort; idxGroupbycolumns = lpSqlNode->node.select.Groupbycolumns; while (idxGroupbycolumns != NO_SQLNODE) { /* Get this group by column */ lpSqlNodeGroupbycolumns = ToNode(*lplpSql, idxGroupbycolumns); lpSqlNodeColumnGroupby = ToNode(*lplpSql, lpSqlNodeGroupbycolumns->node.groupbycolumns.Column); /* Get column name and table name of the group by column */ idxTableGroupby = lpSqlNodeColumnGroupby->node.column.TableIndex; lpszColumnGroupby = ToString(*lplpSql, lpSqlNodeColumnGroupby->node.column.Column); /* Look for this column on the sort list. For each */ /* column in the sort list... */ idxSortcolumns = lpSqlNode->node.select.Sortcolumns; idxSortcolumnsPrev = NO_SQLNODE; while (idxSortcolumns != NO_SQLNODE) { /* Get next element on the sort list */ lpSqlNodeSortcolumns = ToNode(*lplpSql, idxSortcolumns); lpSqlNodeColumnSort = ToNode(*lplpSql, lpSqlNodeSortcolumns->node.sortcolumns.Column); /* Is it a column reference? */ if (lpSqlNodeColumnSort->sqlNodeType == NODE_TYPE_COLUMN) { /* Yes. Get column name and table name of sort column */ idxTableSort = lpSqlNodeColumnSort->node.column.TableIndex; lpszColumnSort = ToString(*lplpSql, lpSqlNodeColumnSort->node.column.Column); /* Leave if this sort column is the group by column */ if (idxTableSort == idxTableGroupby) { if (fCaseSensitive) { // if (!s_lstrcmp(lpszTableSort, lpszTableGroupby) && // !s_lstrcmp(lpszColumnSort, lpszColumnGroupby)) if ( !s_lstrcmp(lpszColumnSort, lpszColumnGroupby) ) break; } else { // if (!s_lstrcmpi(lpszTableSort, lpszTableGroupby) && // !s_lstrcmpi(lpszColumnSort, lpszColumnGroupby)) if ( !s_lstrcmpi(lpszColumnSort, lpszColumnGroupby) ) break; } } } /* Get next sort column */ idxSortcolumnsPrev = idxSortcolumns; idxSortcolumns = lpSqlNodeSortcolumns->node.sortcolumns.Next; } /* Was group by columnn found in the sort list? */ if (idxSortcolumns == NO_SQLNODE) { /* No. A new entry will be needed. Create a node for */ /* the column */ idxColumn = AllocateNode(lplpSql, NODE_TYPE_COLUMN); if (idxColumn == NO_SQLNODE) return ERR_MEMALLOCFAIL; lpSqlNodeColumnSort = ToNode(*lplpSql, idxColumn); /* Recalculate these pointers since the Alloc above */ /* may have moved them */ /* addeed th enext line */ lpSqlNodeGroupbycolumns = ToNode (*lplpSql, idxGroupbycolumns); lpSqlNodeColumnGroupby = ToNode(*lplpSql, lpSqlNodeGroupbycolumns->node.groupbycolumns.Column); /* Fill in the node */ lpSqlNodeColumnSort->node.column.Tablealias = lpSqlNodeColumnGroupby->node.column.Tablealias; lpSqlNodeColumnSort->node.column.Column = lpSqlNodeColumnGroupby->node.column.Column; lpSqlNodeColumnSort->node.column.Qualifier = lpSqlNodeColumnGroupby->node.column.Qualifier; lpSqlNodeColumnSort->node.column.TableIndex = lpSqlNodeColumnGroupby->node.column.TableIndex; lpSqlNodeColumnSort->node.column.MatchedAlias = lpSqlNodeColumnGroupby->node.column.MatchedAlias; lpSqlNodeColumnSort->node.column.Table = NO_SQLNODE; lpSqlNodeColumnSort->node.column.Id = -1; lpSqlNodeColumnSort->node.column.Value = NO_STRING; lpSqlNodeColumnSort->node.column.InSortRecord = FALSE; lpSqlNodeColumnSort->node.column.Offset = 0; lpSqlNodeColumnSort->node.column.Length = 0; lpSqlNodeColumnSort->node.column.DistinctOffset = 0; lpSqlNodeColumnSort->node.column.DistinctLength = 0; lpSqlNodeColumnSort->node.column.EnclosingStatement = NO_SQLNODE; /* Create sort list element */ idxSortcolumns = AllocateNode(lplpSql, NODE_TYPE_SORTCOLUMNS); if (idxSortcolumns == NO_SQLNODE) return ERR_MEMALLOCFAIL; lpSqlNodeSortcolumns = ToNode(*lplpSql, idxSortcolumns); lpSqlNodeSortcolumns->node.sortcolumns.Column = idxColumn; lpSqlNodeSortcolumns->node.sortcolumns.Descending = FALSE; lpSqlNodeSortcolumns->node.sortcolumns.Next = NO_SQLNODE; /* Recalculate these pointers since the Alloc above */ /* may have moved them */ lpSqlNodeGroupbycolumns = ToNode(*lplpSql, idxGroupbycolumns); lpSqlNode = ToNode(*lplpSql, idxNode); /* Put it on the sort list */ if (idxSortcolumnsPrev == NO_SQLNODE) lpSqlNode->node.select.Sortcolumns = idxSortcolumns; else { lpSqlNodeSortcolumns = ToNode(*lplpSql, idxSortcolumnsPrev); lpSqlNodeSortcolumns->node.sortcolumns.Next = idxSortcolumns; } /* Semantic check the newly created nodes */ err = SemanticCheck(lpstmt, lplpSql, idxSortcolumns, fIsGroupby, fCaseSensitive, NO_SQLNODE, idxNode); } /* Look at next group by column */ idxGroupbycolumns = lpSqlNodeGroupbycolumns->node.groupbycolumns.Next; } } /* Check the select list */ err = SemanticCheck(lpstmt, lplpSql, lpSqlNode->node.select.Values, fIsGroupby, fCaseSensitive, NO_SQLNODE, idxNode); if (err != ERR_SUCCESS) return err; /* Check the WHERE clause */ err = SemanticCheck(lpstmt, lplpSql, lpSqlNode->node.select.Predicate, FALSE, fCaseSensitive, NO_SQLNODE, idxNode); if (err != ERR_SUCCESS) return err; /* Check the HAVING clause */ err = SemanticCheck(lpstmt, lplpSql, lpSqlNode->node.select.Having, fIsGroupby, fCaseSensitive, NO_SQLNODE, idxNode); if (err != ERR_SUCCESS) return err; /* Build the sorting directive and determine the overall key size. */ /* At the same time check to make sure that there are not too many */ /* columns in ORDER BY and that character or binary keys are not */ /* too big. */ if (lpSqlNode->node.select.Sortcolumns != NO_SQLNODE) { SQLNODEIDX idxSortcolumns; LPSQLNODE lpSqlNodeSortcolumns; LPSQLNODE lpSqlNodeColumn; UDWORD length; SQLNODEIDX idxTables; LPSQLNODE lpSqlNodeTables; #define NUM_LEN 5 UCHAR szSortDirective[10 + NUM_LEN + (MAX_COLUMNS_IN_ORDER_BY * (6 + (2 * NUM_LEN))) + 3 + MAX_PATHNAME_SIZE]; szSortDirective[0] = 0; SQLNODEIDX idxAggregate; LPSQLNODE lpSqlNodeAggregate; /* For each sort column... */ s_lstrcpy(szSortDirective, "S("); idxSortcolumns = lpSqlNode->node.select.Sortcolumns; while (idxSortcolumns != NO_SQLNODE) { /* Is this node on the group by list ? */ lpSqlNodeSortcolumns = ToNode(*lplpSql, idxSortcolumns); lpSqlNodeColumn = ToNode(*lplpSql, lpSqlNodeSortcolumns->node.sortcolumns.Column); if ((lpSqlNode->node.select.Groupbycolumns == NO_SQLNODE) || (lpSqlNodeColumn->sqlNodeType != NODE_TYPE_COLUMN)) { /* No. Add this sort column to the count */ count++; /* Error if too many sort columns */ if (count > MAX_COLUMNS_IN_ORDER_BY) return ERR_ORDERBYTOOLARGE; /* Get length of the column. */ switch (lpSqlNodeColumn->sqlDataType) { case TYPE_DOUBLE: length = sizeof(double); break; case TYPE_NUMERIC: length = 2 + lpSqlNodeColumn->sqlPrecision; break; case TYPE_INTEGER: length = sizeof(double); break; case TYPE_CHAR: length = lpSqlNodeColumn->sqlPrecision; if (length > MAX_CHAR_LITERAL_LENGTH) { if (lpSqlNodeColumn->sqlNodeType == NODE_TYPE_COLUMN) s_lstrcpy(lpstmt->szError, ToString(*lplpSql, lpSqlNodeColumn->node.column.Column)); else s_lstrcpy(lpstmt->szError, ""); return ERR_CANTORDERBYONTHIS; } break; case TYPE_BINARY: length = lpSqlNodeColumn->sqlPrecision; if (length > MAX_BINARY_LITERAL_LENGTH) { if (lpSqlNodeColumn->sqlNodeType == NODE_TYPE_COLUMN) s_lstrcpy((char*)lpstmt->szError, ToString(*lplpSql, lpSqlNodeColumn->node.column.Column)); else s_lstrcpy(lpstmt->szError, ""); return ERR_CANTORDERBYONTHIS; } break; case TYPE_DATE: length = 10; break; case TYPE_TIME: length = 8; break; case TYPE_TIMESTAMP: if (TIMESTAMP_SCALE > 0) length = 20 + TIMESTAMP_SCALE; else length = 19; break; default: if (lpSqlNodeColumn->sqlNodeType == NODE_TYPE_COLUMN) s_lstrcpy(lpstmt->szError, ToString(*lplpSql, lpSqlNodeColumn->node.column.Column)); else s_lstrcpy((char*)lpstmt->szError, ""); return ERR_CANTORDERBYONTHIS; } /* Put offset of the key column in the sort directive */ wsprintf((LPSTR) (szSortDirective + s_lstrlen((LPSTR)szSortDirective)), "%lu,", offset); /* Adjust offset */ offset += (length); offset++; /* for the IS NULL flag */ } else { /* Yes. Get length */ length = lpSqlNodeColumn->node.column.Length; /* Put offset of the key column in the sort directive */ wsprintf((LPSTR)(szSortDirective + s_lstrlen((LPSTR)szSortDirective)), "%lu,", lpSqlNodeColumn->node.column.Offset); } /* Put length of the key column in the sort directive */ wsprintf((LPSTR)(szSortDirective + s_lstrlen((LPSTR)szSortDirective)), "%lu,", length); /* Put type of the key column in the sort directive */ switch (lpSqlNodeColumn->sqlDataType) { case TYPE_DOUBLE: s_lstrcat((LPSTR)szSortDirective, "E,"); break; case TYPE_NUMERIC: s_lstrcat((LPSTR)szSortDirective, "N,"); break; case TYPE_INTEGER: s_lstrcat((LPSTR)szSortDirective, "E,"); break; case TYPE_CHAR: case TYPE_BINARY: case TYPE_DATE: case TYPE_TIME: case TYPE_TIMESTAMP: s_lstrcat((LPSTR)szSortDirective, "C,"); break; default: return ERR_INTERNAL; } /* Put direction of the key column in the sort directive */ if (lpSqlNodeSortcolumns->node.sortcolumns.Descending) s_lstrcat((char*)szSortDirective, "D"); else s_lstrcat((char*)szSortDirective, "A"); /* Go to next sort column */ idxSortcolumns = lpSqlNodeSortcolumns->node.sortcolumns.Next; if (idxSortcolumns != NO_SQLNODE) s_lstrcat((char*)szSortDirective, ","); } /* Save the sort key size */ lpSqlNode->node.select.SortKeysize = offset-1; /* Is there a GROUP BY statement? */ if ((lpSqlNode->node.select.Groupbycolumns == NO_SQLNODE) && (!lpSqlNode->node.select.ImplicitGroupby)) { /* No. Save offset of the bookmark */ lpSqlNode->node.select.SortBookmarks = offset; /* For each table in table list, add bookmark size to offset */ idxTables = lpSqlNode->node.select.Tables; while (idxTables != NO_SQLNODE) { /* Update offset */ offset += (sizeof(ISAMBOOKMARK)); /* Get pointer to the next table */ lpSqlNodeTables = ToNode(*lplpSql, idxTables); idxTables = lpSqlNodeTables->node.tables.Next; } } else { /* Yes. Calculate the offset of each aggregate field */ idxAggregate = lpSqlNode->node.select.Aggregates; while (idxAggregate != NO_SQLNODE) { /* Save offset of the aggregate */ lpSqlNodeAggregate = ToNode(*lplpSql, idxAggregate); lpSqlNodeAggregate->node.aggregate.Offset = offset; /* Update offset */ offset += (lpSqlNodeAggregate->node.aggregate.Length + 1); /* Get pointer to the next aggregate */ idxAggregate = lpSqlNodeAggregate->node.aggregate.Next; } } offset--; s_lstrcat((char*)szSortDirective, ")"); /* Put record size into sort directive */ s_lstrcat((char*)szSortDirective, "F(FIX,"); wsprintf((LPSTR)(szSortDirective + s_lstrlen((char*)szSortDirective)), "%lu", offset); lstrcat((char*)szSortDirective, ")"); /* Put in work drive */ s_lstrcat(szSortDirective, "W("); #ifdef WIN32 if (!GetTempPath(MAX_PATHNAME_SIZE+1, (LPSTR) szSortDirective + s_lstrlen(szSortDirective))) return ERR_SORT; if (szSortDirective[s_lstrlen(szSortDirective)-1] != '\\') s_lstrcat(szSortDirective, "\\"); #else GetTempFileName(NULL, "LEM", 0, (LPSTR) szSortDirective + s_lstrlen(szSortDirective)); while (szSortDirective[s_lstrlen(szSortDirective)-1] != '\\') szSortDirective[s_lstrlen(szSortDirective)-1] = '\0'; #endif s_lstrcat(szSortDirective, ")"); /* Save the directive */ lpSqlNode->node.select.SortDirective = AllocateString(lplpSql, (LPUSTR)szSortDirective); if (lpSqlNode->node.select.SortDirective == NO_STRING) return ERR_MEMALLOCFAIL; /* Save the sort record size */ lpSqlNode->node.select.SortRecordsize = offset; } /* If there is no group by list originally but there is a group by */ /* list now, there was an aggregate in the select list. Calculate */ /* the offset of each aggregate field */ if (lpSqlNode->node.select.ImplicitGroupby) { SQLNODEIDX idxAggregate; LPSQLNODE lpSqlNodeAggregate; idxAggregate = lpSqlNode->node.select.Aggregates; while (idxAggregate != NO_SQLNODE) { /* Save offset of the aggregate */ lpSqlNodeAggregate = ToNode(*lplpSql, idxAggregate); lpSqlNodeAggregate->node.aggregate.Offset = offset; /* Update offset */ offset += (lpSqlNodeAggregate->node.aggregate.Length + 1); /* Get pointer to the next aggregate */ idxAggregate = lpSqlNodeAggregate->node.aggregate.Next; } /* Adjust the offset */ offset--; /* Save the sort record size */ lpSqlNode->node.select.SortRecordsize = offset; } /* Build the sorting directive for SELECT DISTINCT and determine the */ /* overall key size. At the same time check to make sure that there */ /* are not too many columns in ORDER BY and that character or binary */ /* keys are not too big. */ if (lpSqlNode->node.select.Distinct) { SQLNODEIDX idxValues; LPSQLNODE lpSqlNodeValues; LPSQLNODE lpSqlNodeExpression; UDWORD length; #define NUM_LEN 5 UCHAR szSortDirective[19 + (4 * NUM_LEN) + (MAX_COLUMNS_IN_ORDER_BY * (6 + (2 * NUM_LEN))) + 3 + MAX_PATHNAME_SIZE]; /* For each sort column... */ count = 0; offset = NUM_LEN + 1; s_lstrcpy(szSortDirective, "S("); idxValues = lpSqlNode->node.select.Values; while (idxValues != NO_SQLNODE) { /* Is this node of interest? */ lpSqlNodeValues = ToNode(*lplpSql, idxValues); lpSqlNodeExpression = ToNode(*lplpSql, lpSqlNodeValues->node.values.Value); switch (lpSqlNodeExpression->sqlNodeType) { case NODE_TYPE_COLUMN: case NODE_TYPE_AGGREGATE: case NODE_TYPE_ALGEBRAIC: case NODE_TYPE_SCALAR: /* Yes. Add this sort column to the count */ count++; /* Error if too many sort columns */ if (count > MAX_COLUMNS_IN_ORDER_BY) return ERR_ORDERBYTOOLARGE; /* Get length of the column. */ switch (lpSqlNodeExpression->sqlDataType) { case TYPE_DOUBLE: length = sizeof(double); break; case TYPE_NUMERIC: length = 2 + lpSqlNodeExpression->sqlPrecision; break; case TYPE_INTEGER: length = sizeof(double); break; case TYPE_CHAR: length = lpSqlNodeExpression->sqlPrecision; if (length > MAX_CHAR_LITERAL_LENGTH) { if (lpSqlNodeExpression->sqlNodeType == NODE_TYPE_COLUMN) s_lstrcpy((char*)lpstmt->szError, ToString(*lplpSql, lpSqlNodeExpression->node.column.Column)); else s_lstrcpy((char*)lpstmt->szError, ""); return ERR_CANTORDERBYONTHIS; } break; case TYPE_BINARY: length = lpSqlNodeExpression->sqlPrecision; if (length > MAX_BINARY_LITERAL_LENGTH) { if (lpSqlNodeExpression->sqlNodeType == NODE_TYPE_COLUMN) s_lstrcpy((char*)lpstmt->szError, ToString(*lplpSql, lpSqlNodeExpression->node.column.Column)); else s_lstrcpy((char*)lpstmt->szError, ""); return ERR_CANTORDERBYONTHIS; } break; case TYPE_DATE: length = 10; break; case TYPE_TIME: length = 8; break; case TYPE_TIMESTAMP: if (TIMESTAMP_SCALE > 0) length = 20 + TIMESTAMP_SCALE; else length = 19; break; default: if (lpSqlNodeExpression->sqlNodeType == NODE_TYPE_COLUMN) s_lstrcpy((char*)lpstmt->szError, ToString(*lplpSql, lpSqlNodeExpression->node.column.Column)); else s_lstrcpy((char*)lpstmt->szError, ""); return ERR_CANTORDERBYONTHIS; } /* Put offset of the key column in the sort directive */ wsprintf((LPSTR)(szSortDirective + s_lstrlen((char*)szSortDirective)), "%lu,", offset); /* Save offset and length */ switch (lpSqlNodeExpression->sqlNodeType) { case NODE_TYPE_COLUMN: lpSqlNodeExpression->node.column.DistinctOffset = offset; lpSqlNodeExpression->node.column.DistinctLength = length; break; case NODE_TYPE_AGGREGATE: lpSqlNodeExpression->node.aggregate.DistinctOffset = offset; lpSqlNodeExpression->node.aggregate.DistinctLength = length; break; case NODE_TYPE_ALGEBRAIC: lpSqlNodeExpression->node.algebraic.DistinctOffset = offset; lpSqlNodeExpression->node.algebraic.DistinctLength = length; break; case NODE_TYPE_SCALAR: lpSqlNodeExpression->node.scalar.DistinctOffset = offset; lpSqlNodeExpression->node.scalar.DistinctLength = length; break; default: return ERR_INTERNAL; } /* Adjust offset */ offset += (length); offset++; /* for the IS NULL flag */ /* Put length of the key column in the sort directive */ wsprintf((LPSTR)(szSortDirective + s_lstrlen((char*)szSortDirective)), "%lu,", length); /* Put type of the key column in the sort directive */ switch (lpSqlNodeExpression->sqlDataType) { case TYPE_DOUBLE: s_lstrcat(szSortDirective, "E,"); break; case TYPE_NUMERIC: s_lstrcat(szSortDirective, "N,"); break; case TYPE_INTEGER: s_lstrcat(szSortDirective, "E,"); break; case TYPE_CHAR: case TYPE_BINARY: case TYPE_DATE: case TYPE_TIME: case TYPE_TIMESTAMP: s_lstrcat(szSortDirective, "C,"); break; default: return ERR_INTERNAL; } /* Put direction of the key column in the sort directive */ s_lstrcat(szSortDirective, "A"); 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: break; 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; } /* Go to next sort column */ idxValues = lpSqlNodeValues->node.values.Next; if (idxValues != NO_SQLNODE) s_lstrcat(szSortDirective, ","); } offset--; /* If no fields in sort record, put a constant in */ if (offset == NUM_LEN) { offset = 3; s_lstrcat(szSortDirective, "1,3,C,A"); } s_lstrcat(szSortDirective, ")"); /* Put duplicate removal into sort directive */ s_lstrcat(szSortDirective, "DUPO(B"); wsprintf((LPSTR) szSortDirective + s_lstrlen(szSortDirective), "%lu", offset); s_lstrcat(szSortDirective, ","); wsprintf((LPSTR) szSortDirective + s_lstrlen(szSortDirective), "%u", (WORD) NUM_LEN+1); s_lstrcat(szSortDirective, ","); wsprintf((LPSTR) szSortDirective + s_lstrlen(szSortDirective), "%lu", offset - NUM_LEN); s_lstrcat(szSortDirective, ")"); /* Put record size into sort directive */ s_lstrcat(szSortDirective, "F(FIX,"); wsprintf((LPSTR) szSortDirective + s_lstrlen(szSortDirective), "%lu", offset); s_lstrcat(szSortDirective, ")"); /* Put in work drive */ s_lstrcat(szSortDirective, "W("); #ifdef WIN32 if (!GetTempPath(MAX_PATHNAME_SIZE+1, (LPSTR) szSortDirective + s_lstrlen(szSortDirective))) return ERR_SORT; if (szSortDirective[s_lstrlen(szSortDirective)-1] != '\\') s_lstrcat(szSortDirective, "\\"); #else GetTempFileName(NULL, "LEM", 0, (LPSTR) szSortDirective + + s_lstrlen(szSortDirective)); while (szSortDirective[s_lstrlen(szSortDirective)-1] != '\\') szSortDirective[s_lstrlen(szSortDirective)-1] = '\0'; #endif s_lstrcat(szSortDirective, ")"); /* Save the directive */ lpSqlNode->node.select.DistinctDirective = AllocateString(lplpSql, szSortDirective); if (lpSqlNode->node.select.DistinctDirective == NO_STRING) return ERR_MEMALLOCFAIL; /* Save the sort record size */ lpSqlNode->node.select.DistinctRecordsize = offset; } /* Allocate space for the sortfile name if need be */ if ((lpSqlNode->node.select.Distinct) || (lpSqlNode->node.select.ImplicitGroupby) || (lpSqlNode->node.select.Groupbycolumns != NO_SQLNODE) || (lpSqlNode->node.select.Sortcolumns != NO_SQLNODE)) { lpSqlNode->node.select.SortfileName = AllocateSpace(lplpSql, MAX_PATHNAME_SIZE+1); if (lpSqlNode->node.select.SortfileName == NO_STRING) return ERR_MEMALLOCFAIL; } return ERR_SUCCESS; } /***************************************************************************/ RETCODE INTFUNC SemanticCheck(LPSTMT lpstmt, LPSQLTREE FAR *lplpSql, SQLNODEIDX idxNode, BOOL fIsGroupby, BOOL fCaseSensitive, SQLNODEIDX idxNodeTableOuterJoinFromTables, SQLNODEIDX idxEnclosingStatement) /* Walks a parse tree, checks it for semantic correctness, and fills in */ /* the semantic information. */ { LPSQLNODE lpSqlNode; RETCODE err; LPUSTR ptr; if (idxNode == NO_SQLNODE) return ERR_SUCCESS; lpSqlNode = ToNode(*lplpSql, idxNode); err = ERR_SUCCESS; switch (lpSqlNode->sqlNodeType) { case NODE_TYPE_NONE: break; case NODE_TYPE_ROOT: /* Check the statement */ err = SemanticCheck(lpstmt, lplpSql, lpSqlNode->node.root.sql, fIsGroupby, fCaseSensitive, idxNodeTableOuterJoinFromTables, idxEnclosingStatement); if (err != ERR_SUCCESS) return err; break; case NODE_TYPE_CREATE: /* Make sure table name is not too long */ ptr = ToString(*lplpSql, lpSqlNode->node.create.Table); if (s_lstrlen(ptr) > ISAMMaxTableNameLength(lpstmt->lpdbc->lpISAM)) { s_lstrcpy(lpstmt->szError, ptr); return ERR_INVALIDTABLENAME; } /* Check the new column definitions */ err = SemanticCheck(lpstmt, lplpSql, lpSqlNode->node.create.Columns, fIsGroupby, fCaseSensitive, idxNodeTableOuterJoinFromTables, idxNode); if (err != ERR_SUCCESS) return err; break; case NODE_TYPE_DROP: /* Make sure table name is not too long */ ptr = ToString(*lplpSql, lpSqlNode->node.drop.Table); if (s_lstrlen(ptr) > ISAMMaxTableNameLength(lpstmt->lpdbc->lpISAM)) { s_lstrcpy(lpstmt->szError, ptr); return ERR_INVALIDTABLENAME; } break; case NODE_TYPE_SELECT: err = SelectCheck(lpstmt, lplpSql, idxNode, fCaseSensitive, idxEnclosingStatement); break; case NODE_TYPE_INSERT: { /* Check the table */ err = SemanticCheck(lpstmt, lplpSql, lpSqlNode->node.insert.Table, fIsGroupby, fCaseSensitive, idxNodeTableOuterJoinFromTables, idxNode); if (err != ERR_SUCCESS) return err; /* Is there a column list? */ if (lpSqlNode->node.insert.Columns == NO_SQLNODE) { LPSQLNODE lpSqlNodeTable; STRINGIDX idxAlias; LPISAMTABLEDEF lpTableHandle; SQLNODEIDX idxColumns; LPSQLNODE lpSqlNodeColumns; SQLNODEIDX idxColumnsPrev; LPSQLNODE lpSqlNodeColumnsPrev; SQLNODEIDX idxColumn; LPSQLNODE lpSqlNodeColumn; SWORD idx; /* No. Get node Alias of this table */ lpSqlNodeTable = ToNode(*lplpSql, lpSqlNode->node.insert.Table); idxAlias = lpSqlNodeTable->node.table.Alias; /* Loop through all the columns of this table */ lpTableHandle = lpSqlNodeTable->node.table.Handle; idxColumnsPrev = NO_SQLNODE; ClassColumnInfoBase* cInfoBase = lpTableHandle->pColumnInfo; if ( !cInfoBase->IsValid() ) { return ERR_COLUMNNOTFOUND; } UWORD cNumberOfCols = cInfoBase->GetNumberOfColumns(); char pColumnName [MAX_COLUMN_NAME_LENGTH+1]; for (idx = 0; idx < (SWORD) cNumberOfCols; idx++) { if ( FAILED(cInfoBase->GetColumnName(idx, pColumnName)) ) { return ERR_COLUMNNOTFOUND; } /* Create a node for this column */ idxColumn = AllocateNode(lplpSql, NODE_TYPE_COLUMN); if (idxColumn == NO_SQLNODE) return ERR_MEMALLOCFAIL; lpSqlNodeColumn = ToNode(*lplpSql, idxColumn); lpSqlNodeColumn->node.column.Tablealias = idxAlias; lpSqlNodeColumn->node.column.Column = AllocateString(lplpSql, (LPUSTR)pColumnName); if (lpSqlNodeColumn->node.column.Column == NO_STRING) return ERR_MEMALLOCFAIL; lpSqlNodeColumn->node.column.Table = lpSqlNode->node.insert.Table; lpSqlNodeColumn->node.column.Id = idx; lpSqlNodeColumn->node.column.Value = NO_STRING; lpSqlNodeColumn->node.column.InSortRecord = FALSE; lpSqlNodeColumn->node.column.Offset = 0; lpSqlNodeColumn->node.column.Length = 0; lpSqlNodeColumn->node.column.DistinctOffset = 0; lpSqlNodeColumn->node.column.DistinctLength = 0; lpSqlNodeColumn->node.column.EnclosingStatement = NO_SQLNODE; /* Put it on the list */ idxColumns = AllocateNode(lplpSql, NODE_TYPE_COLUMNS); if (idxColumns == NO_SQLNODE) return ERR_MEMALLOCFAIL; lpSqlNodeColumns = ToNode(*lplpSql, idxColumns); lpSqlNodeColumns->node.columns.Column = idxColumn; lpSqlNodeColumns->node.columns.Next = NO_SQLNODE; if (idxColumnsPrev == NO_SQLNODE) { lpSqlNode = ToNode(*lplpSql, idxNode); lpSqlNode->node.insert.Columns = idxColumns; } else { lpSqlNodeColumnsPrev = ToNode(*lplpSql, idxColumnsPrev); lpSqlNodeColumnsPrev->node.columns.Next = idxColumns; } idxColumnsPrev = idxColumns; lpSqlNode = ToNode(*lplpSql, idxNode); } lpSqlNode = ToNode(*lplpSql, idxNode); } /* Check the column list */ err = SemanticCheck(lpstmt, lplpSql, lpSqlNode->node.insert.Columns, fIsGroupby, fCaseSensitive, idxNodeTableOuterJoinFromTables, idxNode); if (err != ERR_SUCCESS) return err; /* Check the value list */ err = SemanticCheck(lpstmt, lplpSql, lpSqlNode->node.insert.Values, fIsGroupby, fCaseSensitive, idxNodeTableOuterJoinFromTables, idxNode); if (err != ERR_SUCCESS) return err; /* Look at each value */ { SQLNODEIDX idxValues; LPSQLNODE lpSqlNodeValues; LPSQLNODE lpSqlNodeValue; SQLNODEIDX idxColumns; LPSQLNODE lpSqlNodeColumns; LPSQLNODE lpSqlNodeSelect; /* Get list of insert values */ lpSqlNodeSelect = ToNode(*lplpSql, lpSqlNode->node.insert.Values); if (lpSqlNodeSelect->sqlNodeType == NODE_TYPE_SELECT) idxValues = lpSqlNodeSelect->node.select.Values; else idxValues = lpSqlNode->node.insert.Values; /* For each value... */ idxColumns = lpSqlNode->node.insert.Columns; while (idxValues != NO_SQLNODE) { /* Error if no more columns */ if (idxColumns == NO_SQLNODE) return ERR_UNEQUALINSCOLS; /* Get the value */ lpSqlNodeValues = ToNode(*lplpSql, idxValues); lpSqlNodeValue = ToNode(*lplpSql, lpSqlNodeValues->node.values.Value); /* Get the column the value is for */ lpSqlNodeColumns = ToNode(*lplpSql, idxColumns); /* Sub-select? */ if (lpSqlNodeSelect->sqlNodeType != NODE_TYPE_SELECT) { /* No. Make sure the value is NUMERIC, STRING, */ /* PARAMETER, USER, NULL, DATE, TIME, or TIMESTAMP */ switch (lpSqlNodeValue->sqlNodeType) { case NODE_TYPE_ALGEBRAIC: case NODE_TYPE_SCALAR: case NODE_TYPE_AGGREGATE: case NODE_TYPE_COLUMN: return ERR_INVALIDINSVAL; case NODE_TYPE_PARAMETER: case NODE_TYPE_NULL: case NODE_TYPE_NUMERIC: case NODE_TYPE_STRING: case NODE_TYPE_USER: case NODE_TYPE_DATE: case NODE_TYPE_TIME: case NODE_TYPE_TIMESTAMP: break; default: return ERR_INTERNAL; } } /* Make sure value is of the proper type */ err = TypeCheck(lpstmt, *lplpSql, lpSqlNodeColumns->node.columns.Column, lpSqlNodeValues->node.values.Value, OP_NONE, NULL, NULL, NULL, NULL); if (err != ERR_SUCCESS) return err; /* Look at next element */ idxValues = lpSqlNodeValues->node.values.Next; idxColumns = lpSqlNodeColumns->node.columns.Next; } /* Error if extra columns */ if (idxColumns != NO_SQLNODE) return ERR_UNEQUALINSCOLS; } } break; case NODE_TYPE_DELETE: /* Check the table */ err = SemanticCheck(lpstmt, lplpSql, lpSqlNode->node.delet.Table, fIsGroupby, fCaseSensitive, idxNodeTableOuterJoinFromTables, idxNode); if (err != ERR_SUCCESS) return err; /* Check the predicate */ err = SemanticCheck(lpstmt, lplpSql, lpSqlNode->node.delet.Predicate, fIsGroupby, fCaseSensitive, idxNodeTableOuterJoinFromTables, idxNode); if (err != ERR_SUCCESS) return err; break; case NODE_TYPE_UPDATE: /* Check the table */ err = SemanticCheck(lpstmt, lplpSql, lpSqlNode->node.update.Table, fIsGroupby, fCaseSensitive, idxNodeTableOuterJoinFromTables, idxNode); if (err != ERR_SUCCESS) return err; /* Check the update values */ err = SemanticCheck(lpstmt, lplpSql, lpSqlNode->node.update.Updatevalues, fIsGroupby, fCaseSensitive, idxNodeTableOuterJoinFromTables, idxNode); if (err != ERR_SUCCESS) return err; /* Check the predicate */ err = SemanticCheck(lpstmt, lplpSql, lpSqlNode->node.update.Predicate, fIsGroupby, fCaseSensitive, idxNodeTableOuterJoinFromTables, idxNode); if (err != ERR_SUCCESS) return err; break; case NODE_TYPE_CREATEINDEX: { SQLNODEIDX idxSortcolumns; LPSQLNODE lpSqlNodeSortcolumns; UWORD count; /* Make sure index name is not too long */ ptr = ToString(*lplpSql, lpSqlNode->node.createindex.Index); if (s_lstrlen(ptr) > MAX_INDEX_NAME_LENGTH) { s_lstrcpy(lpstmt->szError, ptr); return ERR_INVALIDINDEXNAME; } /* Check the table */ err = SemanticCheck(lpstmt, lplpSql, lpSqlNode->node.createindex.Table, fIsGroupby, fCaseSensitive, idxNodeTableOuterJoinFromTables, idxNode); if (err != ERR_SUCCESS) return err; /* Check the list of columns */ err = SemanticCheck(lpstmt, lplpSql, lpSqlNode->node.createindex.Columns, fIsGroupby, fCaseSensitive, idxNodeTableOuterJoinFromTables, idxNode); if (err != ERR_SUCCESS) return err; /* Make sure there aren't too many columns */ idxSortcolumns = lpSqlNode->node.createindex.Columns; count = 0; while (idxSortcolumns != NO_SQLNODE) { lpSqlNodeSortcolumns = ToNode(lpstmt->lpSqlStmt, idxSortcolumns); count++; idxSortcolumns = lpSqlNodeSortcolumns->node.sortcolumns.Next; } if (count > MAX_COLUMNS_IN_INDEX) { return ERR_TOOMANYINDEXCOLS; } } break; case NODE_TYPE_DROPINDEX: /* Make sure index name is not too long */ ptr = ToString(*lplpSql, lpSqlNode->node.dropindex.Index); if (s_lstrlen(ptr) > MAX_INDEX_NAME_LENGTH) { s_lstrcpy(lpstmt->szError, ptr); return ERR_INVALIDINDEXNAME; } break; case NODE_TYPE_PASSTHROUGH: s_lstrcpy((LPSTR)lpstmt->szError, "CREATE, DROP, SELECT, INSERT, UPDATE, or DELETE"); return ERR_EXPECTEDOTHER; case NODE_TYPE_TABLES: /* Check the table */ err = SemanticCheck(lpstmt, lplpSql, lpSqlNode->node.tables.Table, fIsGroupby, fCaseSensitive, idxNodeTableOuterJoinFromTables, idxEnclosingStatement); if (err != ERR_SUCCESS) return err; /* Check the rest of the list */ err = SemanticCheck(lpstmt, lplpSql, lpSqlNode->node.tables.Next, fIsGroupby, fCaseSensitive, idxNodeTableOuterJoinFromTables, idxEnclosingStatement); if (err != ERR_SUCCESS) return err; /* Error if any correlation name reused */ { LPUSTR lpstrAlias; SQLNODEIDX idxTables; LPSQLNODE lpSqlNodeTables; LPSQLNODE lpSqlNodeTable; /* Get the alias for this table */ lpSqlNodeTable = ToNode(*lplpSql, lpSqlNode->node.tables.Table); if (lpSqlNodeTable->node.table.Alias != NO_STRING) { lpstrAlias = ToString(*lplpSql, lpSqlNodeTable->node.table.Alias); /* For each table on the rest of the list */ idxTables = lpSqlNode->node.tables.Next; while (idxTables != NO_SQLNODE) { /* Get pointer to table node */ lpSqlNodeTables = ToNode(*lplpSql, idxTables); lpSqlNodeTable = ToNode(*lplpSql, lpSqlNodeTables->node.tables.Table); /* Error if its name is the same */ if (lpSqlNodeTable->node.table.Alias != NO_STRING) { if (fCaseSensitive) { if (!s_lstrcmp(lpstrAlias, ToString(*lplpSql, lpSqlNodeTable->node.table.Alias))) { s_lstrcpy((char*)lpstmt->szError, lpstrAlias); return ERR_ALIASINUSE; } } else { if (!s_lstrcmpi(lpstrAlias, ToString(*lplpSql, lpSqlNodeTable->node.table.Alias))) { s_lstrcpy((char*)lpstmt->szError, lpstrAlias); return ERR_ALIASINUSE; } } } /* Go to next element */ idxTables = lpSqlNodeTables->node.tables.Next; } } { LPSQLNODE lpSqlNodeTable; /* Check the outer join predicate (if any) */ lpSqlNodeTable = ToNode(*lplpSql, lpSqlNode->node.tables.Table); err = SemanticCheck(lpstmt, lplpSql, lpSqlNodeTable->node.table.OuterJoinPredicate, FALSE, fCaseSensitive, lpSqlNodeTable->node.table.OuterJoinFromTables, idxEnclosingStatement); if (err != ERR_SUCCESS) return err; } } break; case NODE_TYPE_VALUES: while (TRUE) { /* Check this value */ err = SemanticCheck(lpstmt, lplpSql, lpSqlNode->node.values.Value, fIsGroupby, fCaseSensitive, idxNodeTableOuterJoinFromTables, idxEnclosingStatement); if (err != ERR_SUCCESS) return err; /* Check the rest of the list */ if (lpSqlNode->node.values.Next == NO_SQLNODE) break; lpSqlNode = ToNode(*lplpSql, lpSqlNode->node.values.Next); } break; case NODE_TYPE_COLUMNS: while (TRUE) { /* Check this column */ err = SemanticCheck(lpstmt, lplpSql, lpSqlNode->node.columns.Column, fIsGroupby, fCaseSensitive, idxNodeTableOuterJoinFromTables, idxEnclosingStatement); if (err != ERR_SUCCESS) return err; /* Check the rest of the list */ if (lpSqlNode->node.columns.Next == NO_SQLNODE) break; lpSqlNode = ToNode(*lplpSql, lpSqlNode->node.columns.Next); } lpSqlNode = ToNode(*lplpSql, idxNode); /* Error if any column on list more than once */ { LPUSTR lpstrName; SQLNODEIDX idxColumns; LPSQLNODE lpSqlNodeColumns; LPSQLNODE lpSqlNodeColumn; /* Get the name for this column */ lpSqlNodeColumn = ToNode(*lplpSql, lpSqlNode->node.columns.Column); lpstrName = ToString(*lplpSql, lpSqlNodeColumn->node.column.Column); /* For each column on the rest of the list */ idxColumns = lpSqlNode->node.columns.Next; while (idxColumns != NO_SQLNODE) { /* Get pointer to table node */ lpSqlNodeColumns = ToNode(*lplpSql, idxColumns); lpSqlNodeColumn = ToNode(*lplpSql, lpSqlNodeColumns->node.columns.Column); /* Error if its name is the same */ if (fCaseSensitive) { if (!s_lstrcmp(lpstrName, ToString(*lplpSql, lpSqlNodeColumn->node.column.Column))) { s_lstrcpy(lpstmt->szError, lpstrName); return ERR_COLUMNONLIST; } } else { if (!s_lstrcmpi(lpstrName, ToString(*lplpSql, lpSqlNodeColumn->node.column.Column))) { s_lstrcpy(lpstmt->szError, lpstrName); return ERR_COLUMNONLIST; } } /* Go to next element */ idxColumns = lpSqlNodeColumns->node.columns.Next; } } break; case NODE_TYPE_SORTCOLUMNS: while (TRUE) { LPSQLNODE lpSqlNodeSort; LPSQLNODE lpSqlNodeStmt; SQLNODEIDX idxValues; SWORD count; LPSQLNODE lpSqlNodeValues; LPSQLNODE lpSqlNodeColumn; /* Check this column */ err = SemanticCheck(lpstmt, lplpSql,lpSqlNode->node.sortcolumns.Column, fIsGroupby, fCaseSensitive, idxNodeTableOuterJoinFromTables, idxEnclosingStatement); if (err != ERR_SUCCESS) return err; /* Was an ordinal position in the select list given as sort? */ lpSqlNodeSort = ToNode(*lplpSql, lpSqlNode->node.sortcolumns.Column); if (lpSqlNodeSort->sqlNodeType == NODE_TYPE_NUMERIC) { /* No. Get the select list */ lpSqlNodeStmt = ToNode(*lplpSql, idxEnclosingStatement); if (lpSqlNodeStmt->sqlNodeType != NODE_TYPE_SELECT) return ERR_INTERNAL; /* Find the element in the select list */ idxValues = lpSqlNodeStmt->node.select.Values; count = 1; while (TRUE) { if (idxValues == NO_SQLNODE) return ERR_ORDINALTOOLARGE; lpSqlNodeValues = ToNode(*lplpSql, idxValues); if (count == (SWORD) lpSqlNodeSort->node.numeric.Value) break; idxValues = lpSqlNodeValues->node.values.Next; count++; } lpSqlNodeColumn = ToNode(*lplpSql, lpSqlNodeValues->node.values.Value); /* Error if a column reference was not given */ if (lpSqlNodeColumn->sqlNodeType != NODE_TYPE_COLUMN) return ERR_ORDERBYCOLUMNONLY; /* Convert the sort node to a column reference */ lpSqlNodeSort->sqlNodeType = NODE_TYPE_COLUMN; lpSqlNodeSort->sqlDataType = TYPE_UNKNOWN; lpSqlNodeSort->sqlSqlType = SQL_TYPE_NULL; lpSqlNodeSort->sqlPrecision = 0; lpSqlNodeSort->sqlScale = NO_SCALE; lpSqlNodeSort->value.String = NULL; lpSqlNodeSort->value.Double = 0.0; lpSqlNodeSort->value.Date.year = 0; lpSqlNodeSort->value.Date.month = 0; lpSqlNodeSort->value.Date.day = 0; lpSqlNodeSort->value.Time.hour = 0; lpSqlNodeSort->value.Time.minute = 0; lpSqlNodeSort->value.Time.second = 0; lpSqlNodeSort->value.Timestamp.year = 0; lpSqlNodeSort->value.Timestamp.month = 0; lpSqlNodeSort->value.Timestamp.day = 0; lpSqlNodeSort->value.Timestamp.hour = 0; lpSqlNodeSort->value.Timestamp.minute = 0; lpSqlNodeSort->value.Timestamp.second = 0; lpSqlNodeSort->value.Timestamp.fraction = 0; lpSqlNodeSort->value.Binary = NULL; lpSqlNodeSort->sqlIsNull = TRUE; lpSqlNodeSort->node.column.Tablealias = lpSqlNodeColumn->node.column.Tablealias; lpSqlNodeSort->node.column.Qualifier = lpSqlNodeColumn->node.column.Qualifier; lpSqlNodeSort->node.column.MatchedAlias = lpSqlNodeColumn->node.column.MatchedAlias; lpSqlNodeSort->node.column.Column = lpSqlNodeColumn->node.column.Column; lpSqlNodeSort->node.column.TableIndex = lpSqlNodeColumn->node.column.TableIndex; // lpSqlNodeSort->node.table.Handle = NULL; lpSqlNodeSort->node.column.Id = -1; lpSqlNodeSort->node.column.Value = NO_STRING; lpSqlNodeSort->node.column.InSortRecord = FALSE; lpSqlNodeSort->node.column.Offset = 0; lpSqlNodeSort->node.column.Length = 0; lpSqlNodeSort->node.column.DistinctOffset = 0; lpSqlNodeSort->node.column.DistinctLength = 0; lpSqlNodeSort->node.column.EnclosingStatement = NO_SQLNODE; /* Semantic check the newly created node */ err = SemanticCheck(lpstmt, lplpSql, lpSqlNode->node.sortcolumns.Column, fIsGroupby, fCaseSensitive, idxNodeTableOuterJoinFromTables, idxEnclosingStatement); if (err != ERR_SUCCESS) return err; } /* Check the rest of the list */ if (lpSqlNode->node.sortcolumns.Next == NO_SQLNODE) break; lpSqlNode = ToNode(*lplpSql, lpSqlNode->node.sortcolumns.Next); } break; case NODE_TYPE_GROUPBYCOLUMNS: while (TRUE) { /* Check this column */ err = SemanticCheck(lpstmt, lplpSql, lpSqlNode->node.groupbycolumns.Column, fIsGroupby, fCaseSensitive, idxNodeTableOuterJoinFromTables, idxEnclosingStatement); if (err != ERR_SUCCESS) return err; /* Check the rest of the list */ if (lpSqlNode->node.groupbycolumns.Next == NO_SQLNODE) break; lpSqlNode = ToNode(*lplpSql, lpSqlNode->node.groupbycolumns.Next); } break; case NODE_TYPE_UPDATEVALUES: while (TRUE) { /* Check this column */ err = SemanticCheck(lpstmt, lplpSql, lpSqlNode->node.updatevalues.Column, fIsGroupby, fCaseSensitive, idxNodeTableOuterJoinFromTables, idxEnclosingStatement); if (err != ERR_SUCCESS) return err; /* Check the value being assigned to the column */ err = SemanticCheck(lpstmt, lplpSql, lpSqlNode->node.updatevalues.Value, fIsGroupby, fCaseSensitive, idxNodeTableOuterJoinFromTables, idxEnclosingStatement); if (err != ERR_SUCCESS) return err; /* Error if value has incompatible type */ err = TypeCheck(lpstmt, *lplpSql, lpSqlNode->node.updatevalues.Column, lpSqlNode->node.updatevalues.Value, OP_NONE, NULL, NULL, NULL, NULL); if (err != ERR_SUCCESS) return err; /* Check the rest of the list */ if (lpSqlNode->node.updatevalues.Next == NO_SQLNODE) break; lpSqlNode = ToNode(*lplpSql, lpSqlNode->node.updatevalues.Next); } break; case NODE_TYPE_CREATECOLS: while (TRUE) { UWORD count; UWORD idx; /* Make sure column name is not too long */ ptr = ToString(*lplpSql, lpSqlNode->node.createcols.Name); if (s_lstrlen(ptr)>ISAMMaxColumnNameLength(lpstmt->lpdbc->lpISAM)) { s_lstrcpy(lpstmt->szError, ptr); return ERR_INVALIDCOLNAME; } /* Find data type */ ptr = ToString(*lplpSql, lpSqlNode->node.createcols.Type); for (idx = 0; idx < lpstmt->lpdbc->lpISAM->cSQLTypes; idx++) { if (lpstmt->lpdbc->lpISAM->SQLTypes[idx].supported && !s_lstrcmpi(lpstmt->lpdbc->lpISAM->SQLTypes[idx].name, ptr)) break; } if (idx >= lpstmt->lpdbc->lpISAM->cSQLTypes) { s_lstrcpy(lpstmt->szError, ptr); return ERR_NOSUCHTYPE; } lpSqlNode->node.createcols.iSqlType = idx; /* Count the number of create parameters */ count = 0; ptr = lpstmt->lpdbc->lpISAM->SQLTypes[idx].params; if (ptr != NULL) { count = 1; for (; *ptr; ptr++) { if (*ptr == ',') count++; } } /* Error if wrong number of parameters */ if (count != lpSqlNode->node.createcols.Params) { s_lstrcpy((char*)lpstmt->szError, ToString(*lplpSql, lpSqlNode->node.createcols.Type)); return ERR_BADPARAMCOUNT; } /* Check next one on list */ if (lpSqlNode->node.createcols.Next == NO_SQLNODE) break; lpSqlNode = ToNode(*lplpSql, lpSqlNode->node.createcols.Next); } break; case NODE_TYPE_BOOLEAN: while (TRUE) { /* Check left child */ err = SemanticCheck(lpstmt, lplpSql, lpSqlNode->node.boolean.Left, fIsGroupby, fCaseSensitive, idxNodeTableOuterJoinFromTables, idxEnclosingStatement); if (err != ERR_SUCCESS) return err; /* The result is always a boolean */ lpSqlNode->sqlDataType = TYPE_INTEGER; lpSqlNode->sqlSqlType = SQL_BIT; lpSqlNode->sqlPrecision = 1; lpSqlNode->sqlScale = 0; /* Leave loop if no right child */ if (lpSqlNode->node.boolean.Right == NO_SQLNODE) break; /* Is right child a NODE_TYPE_BOOLEAN node? */ if (ToNode(*lplpSql, lpSqlNode->node.boolean.Right)->sqlNodeType != NODE_TYPE_BOOLEAN) { /* No. Semantic check that and leave the loop */ err = SemanticCheck(lpstmt, lplpSql, lpSqlNode->node.boolean.Right, fIsGroupby, fCaseSensitive, idxNodeTableOuterJoinFromTables, idxEnclosingStatement); if (err != ERR_SUCCESS) return err; break; } /* Semantic check the right node on next iteration of the loop */ lpSqlNode = ToNode(*lplpSql, lpSqlNode->node.boolean.Right); } break; case NODE_TYPE_COMPARISON: /* Check left child */ err = SemanticCheck(lpstmt, lplpSql, lpSqlNode->node.comparison.Left, fIsGroupby, fCaseSensitive, idxNodeTableOuterJoinFromTables, idxEnclosingStatement); if (err != ERR_SUCCESS) return err; /* Check right child */ err = SemanticCheck(lpstmt, lplpSql, lpSqlNode->node.comparison.Right, fIsGroupby, fCaseSensitive, idxNodeTableOuterJoinFromTables, idxEnclosingStatement); if (err != ERR_SUCCESS) return err; /* Is this an IN or NOT IN comparison against a sub-select? */ if (((lpSqlNode->node.comparison.Operator == OP_IN) || (lpSqlNode->node.comparison.Operator == OP_NOTIN)) && (ToNode(*lplpSql, lpSqlNode->node.comparison.Right)->sqlNodeType == NODE_TYPE_SELECT)) { /* Yes. Convert it to a regular comparison */ if (lpSqlNode->node.comparison.Operator == OP_IN) { lpSqlNode->node.comparison.Operator = OP_EQ; lpSqlNode->node.comparison.SelectModifier = SELECT_ANY; } else if (lpSqlNode->node.comparison.Operator == OP_NOTIN) { lpSqlNode->node.comparison.Operator = OP_NE; lpSqlNode->node.comparison.SelectModifier = SELECT_ALL; } } /* Is this an EXISTS operation ? */ if (lpSqlNode->node.comparison.Operator == OP_EXISTS) { /* Yes. Nothing else to do */ ; } /* Is this an IN or NOT IN comparison? */ else if ((lpSqlNode->node.comparison.Operator != OP_IN) && (lpSqlNode->node.comparison.Operator != OP_NOTIN)) { /* No. Is the right child NODE_TYPE_SELECT? */ switch (lpSqlNode->node.comparison.SelectModifier) { case SELECT_NOTSELECT: break; case SELECT_ALL: case SELECT_ANY: case SELECT_ONE: { LPSQLNODE lpSqlNodeSelect; LPSQLNODE lpSqlNodeValues; LPSQLNODE lpSqlNodeValue; /* Yes. Get the select list */ lpSqlNodeSelect = ToNode(*lplpSql, lpSqlNode->node.comparison.Right); lpSqlNodeValues = ToNode(*lplpSql, lpSqlNodeSelect->node.select.Values); /* If more than one value on the list, error */ if (lpSqlNodeValues->node.values.Next != NO_SQLNODE) return ERR_MULTICOLUMNSELECT; /* If long value, error */ lpSqlNodeValue = ToNode(*lplpSql, lpSqlNodeValues->node.values.Value); if ((lpSqlNodeValue->sqlSqlType == SQL_LONGVARCHAR) || (lpSqlNodeValue->sqlSqlType == SQL_LONGVARBINARY)) { ErrorOpCode(lpstmt, lpSqlNode->node.comparison.Operator); return ERR_INVALIDOPERAND; } /* Set the type of the value returned by the sub-query */ lpSqlNodeSelect->sqlDataType = lpSqlNodeValue->sqlDataType; lpSqlNodeSelect->sqlSqlType = lpSqlNodeValue->sqlSqlType; lpSqlNodeSelect->sqlPrecision = lpSqlNodeValue->sqlPrecision; lpSqlNodeSelect->sqlScale = lpSqlNodeValue->sqlScale; } break; case SELECT_EXISTS: default: return ERR_INTERNAL; } /* Error if incompatible types */ err = TypeCheck(lpstmt, *lplpSql, lpSqlNode->node.comparison.Left, lpSqlNode->node.comparison.Right, lpSqlNode->node.comparison.Operator, &(lpSqlNode->sqlDataType), &(lpSqlNode->sqlSqlType), &(lpSqlNode->sqlPrecision), &(lpSqlNode->sqlScale)); if (err != ERR_SUCCESS) return err; /* Error if "LIKE" or "NOT LIKE" used for non-char data */ if ((lpSqlNode->sqlDataType != TYPE_CHAR) && ((lpSqlNode->node.comparison.Operator == OP_LIKE) || (lpSqlNode->node.comparison.Operator == OP_NOTLIKE))) { ErrorOpCode(lpstmt, lpSqlNode->node.comparison.Operator); return ERR_INVALIDOPERAND; } /* Error if binary data */ if (lpSqlNode->sqlDataType == TYPE_BINARY) { if (((lpSqlNode->node.comparison.Operator != OP_EQ) && (lpSqlNode->node.comparison.Operator != OP_NE)) || (lpSqlNode->sqlPrecision > MAX_BINARY_LITERAL_LENGTH)) { ErrorOpCode(lpstmt, lpSqlNode->node.comparison.Operator); return ERR_INVALIDOPERAND; } } } else { /* Yes. Look at each value */ SQLNODEIDX idxValues; LPSQLNODE lpSqlNodeValues; LPSQLNODE lpSqlNodeValue; /* For each value... */ idxValues = lpSqlNode->node.comparison.Right; while (idxValues != NO_SQLNODE) { /* Get the value */ lpSqlNodeValues = ToNode(*lplpSql, idxValues); lpSqlNodeValue = ToNode(*lplpSql, lpSqlNodeValues->node.values.Value); /* Make sure the value is NUMERIC, STRING, */ /* PARAMETER, USER, DATE, TIME, or TIMESTAMP */ switch (lpSqlNodeValue->sqlNodeType) { case NODE_TYPE_ALGEBRAIC: case NODE_TYPE_SCALAR: case NODE_TYPE_AGGREGATE: case NODE_TYPE_COLUMN: return ERR_INVALIDINVAL; case NODE_TYPE_PARAMETER: case NODE_TYPE_NUMERIC: case NODE_TYPE_STRING: case NODE_TYPE_USER: case NODE_TYPE_DATE: case NODE_TYPE_TIME: case NODE_TYPE_TIMESTAMP: /* Make sure value is of the proper type */ err = TypeCheck(lpstmt, *lplpSql, lpSqlNode->node.comparison.Left, lpSqlNodeValues->node.values.Value, lpSqlNode->node.comparison.Operator, &(lpSqlNode->sqlDataType), &(lpSqlNode->sqlSqlType), &(lpSqlNode->sqlPrecision), &(lpSqlNode->sqlScale)); if (err != ERR_SUCCESS) return err; break; case NODE_TYPE_NULL: return ERR_INVALIDINVAL; default: return ERR_INTERNAL; } /* Look at next element */ idxValues = lpSqlNodeValues->node.values.Next; } } /* The result is always a boolean */ lpSqlNode->sqlDataType = TYPE_INTEGER; lpSqlNode->sqlSqlType = SQL_BIT; lpSqlNode->sqlPrecision = 1; lpSqlNode->sqlScale = 0; break; case NODE_TYPE_ALGEBRAIC: /* Save pointer to enclosing statement */ lpSqlNode->node.algebraic.EnclosingStatement = idxEnclosingStatement; { LPSQLNODE lpSqlNodeLeft; LPUSTR lpszToken; UCHAR szTempToken[MAX_TOKEN_SIZE + 1]; /* Check left child */ err = SemanticCheck(lpstmt, lplpSql, lpSqlNode->node.algebraic.Left, fIsGroupby, fCaseSensitive, idxNodeTableOuterJoinFromTables, idxEnclosingStatement); if (err != ERR_SUCCESS) return err; /* Check right child */ err = SemanticCheck(lpstmt, lplpSql, lpSqlNode->node.algebraic.Right, fIsGroupby, fCaseSensitive, idxNodeTableOuterJoinFromTables, idxEnclosingStatement); if (err != ERR_SUCCESS) return err; /* Check data type compatibility */ err = TypeCheck(lpstmt, *lplpSql, lpSqlNode->node.algebraic.Left, lpSqlNode->node.algebraic.Right, lpSqlNode->node.algebraic.Operator, &(lpSqlNode->sqlDataType), &(lpSqlNode->sqlSqlType), &(lpSqlNode->sqlPrecision), &(lpSqlNode->sqlScale)); if (err != ERR_SUCCESS) return err; /* Allocate space for the column value and make sure the */ /* result is numeric, date, or string */ switch (lpSqlNode->sqlDataType) { case TYPE_DOUBLE: case TYPE_INTEGER: break; case TYPE_NUMERIC: lpSqlNode->node.algebraic.Value = AllocateSpace(lplpSql, (SWORD) (1 + 2 + lpSqlNode->sqlPrecision)); if (lpSqlNode->node.algebraic.Value == NO_STRING) return ERR_MEMALLOCFAIL; if (lpSqlNode->node.algebraic.Operator == OP_TIMES) { lpSqlNode->node.algebraic.WorkBuffer1 = AllocateSpace(lplpSql, (SWORD) (1 + 2 + lpSqlNode->sqlPrecision)); if (lpSqlNode->node.algebraic.WorkBuffer1 == NO_STRING) return ERR_MEMALLOCFAIL; } else if (lpSqlNode->node.algebraic.Operator == OP_DIVIDEDBY) { lpSqlNode->node.algebraic.WorkBuffer1 = AllocateSpace(lplpSql, (SWORD) (1 + 2 + lpSqlNode->sqlPrecision)); if (lpSqlNode->node.algebraic.WorkBuffer1 == NO_STRING) return ERR_MEMALLOCFAIL; lpSqlNode->node.algebraic.WorkBuffer2 = AllocateSpace(lplpSql, (SWORD) (1 + 2 + lpSqlNode->sqlPrecision)); if (lpSqlNode->node.algebraic.WorkBuffer2 == NO_STRING) return ERR_MEMALLOCFAIL; lpSqlNode->node.algebraic.WorkBuffer3 = AllocateSpace(lplpSql, (SWORD) (1 + 2 + lpSqlNode->sqlPrecision)); if (lpSqlNode->node.algebraic.WorkBuffer3 == NO_STRING) return ERR_MEMALLOCFAIL; } break; case TYPE_CHAR: lpSqlNode->node.algebraic.Value = AllocateSpace(lplpSql, (SWORD) (1 + lpSqlNode->sqlPrecision)); if (lpSqlNode->node.algebraic.Value == NO_STRING) return ERR_MEMALLOCFAIL; break; case TYPE_BINARY: ErrorOpCode(lpstmt, lpSqlNode->node.algebraic.Operator); return ERR_INVALIDOPERAND; case TYPE_DATE: break; case TYPE_TIME: case TYPE_TIMESTAMP: ErrorOpCode(lpstmt, lpSqlNode->node.algebraic.Operator); return ERR_INVALIDOPERAND; default: return ERR_NOTSUPPORTED; } /* If this is just negation of a numeric value, collapse the */ /* two nodes into one */ lpSqlNodeLeft = ToNode(*lplpSql, lpSqlNode->node.algebraic.Left); if ((lpSqlNode->node.algebraic.Operator == OP_NEG) && (lpSqlNodeLeft->sqlNodeType == NODE_TYPE_NUMERIC)) { *lpSqlNode = *lpSqlNodeLeft; lpSqlNode->node.numeric.Value = -(lpSqlNode->node.numeric.Value); lpszToken = ToString(*lplpSql, lpSqlNode->node.numeric.Numeric); if (*lpszToken != '-') { s_lstrcpy((char*)szTempToken, lpszToken); s_lstrcpy(lpszToken, "-"); s_lstrcat(lpszToken, (char*)szTempToken); BCDNormalize(lpszToken, s_lstrlen(lpszToken), lpszToken, s_lstrlen(lpszToken) + 1, lpSqlNode->sqlPrecision, lpSqlNode->sqlScale); } else { s_lstrcpy((char*)szTempToken, lpszToken); s_lstrcpy(lpszToken, (char*) (szTempToken+1)); } } } break; case NODE_TYPE_SCALAR: /* Save pointer to enclosing statement */ lpSqlNode->node.scalar.EnclosingStatement = idxEnclosingStatement; /* Check the node */ err = ScalarCheck(lpstmt, lplpSql, idxNode, fIsGroupby, fCaseSensitive, idxNodeTableOuterJoinFromTables, idxEnclosingStatement); if (err != ERR_SUCCESS) return err; /* Allocate space as need be */ switch (lpSqlNode->sqlDataType) { case TYPE_DOUBLE: break; case TYPE_NUMERIC: lpSqlNode->node.scalar.Value = AllocateSpace(lplpSql, (SWORD) (1 + 2 + lpSqlNode->sqlPrecision)); if (lpSqlNode->node.scalar.Value == NO_STRING) return ERR_MEMALLOCFAIL; break; case TYPE_INTEGER: break; case TYPE_CHAR: lpSqlNode->node.scalar.Value = AllocateSpace(lplpSql, (SWORD) (1 + lpSqlNode->sqlPrecision)); if (lpSqlNode->node.scalar.Value == NO_STRING) return ERR_MEMALLOCFAIL; break; case TYPE_BINARY: lpSqlNode->node.scalar.Value = AllocateSpace(lplpSql, (SWORD) (sizeof(SDWORD) + lpSqlNode->sqlPrecision)); if (lpSqlNode->node.scalar.Value == NO_STRING) return ERR_MEMALLOCFAIL; break; case TYPE_DATE: break; case TYPE_TIME: break; case TYPE_TIMESTAMP: break; default: return ERR_NOTSUPPORTED; } break; case NODE_TYPE_AGGREGATE: /* Save pointer to enclosing statement */ lpSqlNode->node.aggregate.EnclosingStatement = idxEnclosingStatement; { LPSQLNODE lpSqlNodeExpression; LPSQLNODE lpSqlNodeSelect; LPSQLNODE lpSqlNodeAggregate; SQLNODEIDX idxAggregate; /* Error if no group by */ if (!fIsGroupby) { ErrorAggCode(lpstmt, lpSqlNode->node.aggregate.Operator); return ERR_AGGNOTALLOWED; } /* Check expression */ err = SemanticCheck(lpstmt, lplpSql, lpSqlNode->node.aggregate.Expression, FALSE, fCaseSensitive, idxNodeTableOuterJoinFromTables, idxEnclosingStatement); if (err != ERR_SUCCESS) return err; /* Figure out type of result */ if (lpSqlNode->node.aggregate.Operator != AGG_COUNT) lpSqlNodeExpression = ToNode(*lplpSql, lpSqlNode->node.aggregate.Expression); switch(lpSqlNode->node.aggregate.Operator) { case AGG_AVG: switch (lpSqlNodeExpression->sqlDataType) { case TYPE_DOUBLE: case TYPE_NUMERIC: case TYPE_INTEGER: lpSqlNode->sqlDataType = TYPE_DOUBLE; lpSqlNode->sqlSqlType = SQL_DOUBLE; lpSqlNode->sqlPrecision = 15; lpSqlNode->sqlScale = NO_SCALE; break; case TYPE_CHAR: case TYPE_DATE: case TYPE_TIME: case TYPE_TIMESTAMP: case TYPE_BINARY: ErrorAggCode(lpstmt, lpSqlNode->node.aggregate.Operator); return ERR_INVALIDOPERAND; default: return ERR_NOTSUPPORTED; } break; case AGG_COUNT: lpSqlNode->sqlDataType = TYPE_INTEGER; lpSqlNode->sqlSqlType = SQL_INTEGER; lpSqlNode->sqlPrecision = 10; lpSqlNode->sqlScale = 0; break; case AGG_MAX: case AGG_MIN: switch (lpSqlNodeExpression->sqlDataType) { case TYPE_DOUBLE: case TYPE_NUMERIC: case TYPE_INTEGER: case TYPE_CHAR: case TYPE_DATE: case TYPE_TIME: case TYPE_TIMESTAMP: lpSqlNode->sqlDataType = lpSqlNodeExpression->sqlDataType; lpSqlNode->sqlSqlType = lpSqlNodeExpression->sqlSqlType; lpSqlNode->sqlPrecision = lpSqlNodeExpression->sqlPrecision; lpSqlNode->sqlScale = lpSqlNodeExpression->sqlScale; break; case TYPE_BINARY: ErrorAggCode(lpstmt, lpSqlNode->node.aggregate.Operator); return ERR_INVALIDOPERAND; default: return ERR_NOTSUPPORTED; } break; case AGG_SUM: switch (lpSqlNodeExpression->sqlDataType) { case TYPE_DOUBLE: case TYPE_NUMERIC: case TYPE_INTEGER: lpSqlNode->sqlDataType = lpSqlNodeExpression->sqlDataType; lpSqlNode->sqlSqlType = lpSqlNodeExpression->sqlSqlType; lpSqlNode->sqlPrecision = lpSqlNodeExpression->sqlPrecision; lpSqlNode->sqlScale = lpSqlNodeExpression->sqlScale; break; case TYPE_CHAR: case TYPE_DATE: case TYPE_TIME: case TYPE_TIMESTAMP: case TYPE_BINARY: ErrorAggCode(lpstmt, lpSqlNode->node.aggregate.Operator); return ERR_INVALIDOPERAND; default: return ERR_NOTSUPPORTED; } break; default: return ERR_INTERNAL; } /* Allocate space for the aggregate value */ switch (lpSqlNode->sqlDataType) { case TYPE_DOUBLE: lpSqlNode->node.aggregate.Length = sizeof(double); break; case TYPE_NUMERIC: switch (lpSqlNode->sqlSqlType) { case SQL_NUMERIC: case SQL_DECIMAL: lpSqlNode->node.aggregate.Length = lpSqlNodeExpression->sqlPrecision + 2; break; case SQL_BIGINT: lpSqlNode->node.aggregate.Length = lpSqlNodeExpression->sqlPrecision + 1; break; case SQL_TINYINT: case SQL_SMALLINT: case SQL_INTEGER: case SQL_BIT: case SQL_REAL: case SQL_FLOAT: case SQL_DOUBLE: case SQL_CHAR: case SQL_VARCHAR: case SQL_LONGVARCHAR: case SQL_BINARY: case SQL_VARBINARY: case SQL_LONGVARBINARY: case SQL_DATE: case SQL_TIME: case SQL_TIMESTAMP: default: return ERR_INTERNAL; } lpSqlNode->node.aggregate.Value = AllocateSpace(lplpSql, (SWORD) (lpSqlNode->node.aggregate.Length + 1)); if (lpSqlNode->node.aggregate.Value == NO_STRING) return ERR_MEMALLOCFAIL; break; case TYPE_INTEGER: lpSqlNode->node.aggregate.Length = sizeof(double); break; case TYPE_CHAR: lpSqlNode->node.aggregate.Length = lpSqlNodeExpression->sqlPrecision; lpSqlNode->node.aggregate.Value = AllocateSpace(lplpSql, (SWORD) (lpSqlNode->node.aggregate.Length + 1)); if (lpSqlNode->node.aggregate.Value == NO_STRING) return ERR_MEMALLOCFAIL; break; case TYPE_DATE: lpSqlNode->node.aggregate.Length = 10; break; case TYPE_TIME: lpSqlNode->node.aggregate.Length = 8; break; case TYPE_TIMESTAMP: if (TIMESTAMP_SCALE > 0) lpSqlNode->node.aggregate.Length = 20 + TIMESTAMP_SCALE; else lpSqlNode->node.aggregate.Length = 19; break; case TYPE_BINARY: return ERR_INTERNAL; default: return ERR_NOTSUPPORTED; } /* Get SELECT statement node */ lpSqlNodeSelect = ToNode(*lplpSql, idxEnclosingStatement); if (lpSqlNodeSelect->sqlNodeType != NODE_TYPE_SELECT) return ERR_INTERNAL; /* Add to list of AGG functions in the select node */ lpSqlNodeAggregate = NULL; idxAggregate = lpSqlNodeSelect->node.select.Aggregates; while (idxAggregate != NO_SQLNODE) { lpSqlNodeAggregate = ToNode(*lplpSql, idxAggregate); idxAggregate = lpSqlNodeAggregate->node.aggregate.Next; } if (lpSqlNodeAggregate != NULL) lpSqlNodeAggregate->node.aggregate.Next = idxNode; else lpSqlNodeSelect->node.select.Aggregates = idxNode; } break; case NODE_TYPE_TABLE: { LPSQLNODE lpSqlNodeRoot; LPSQLNODE lpSqlNodeStmt; BOOL fReadOnly; char tableNameBuff [MAX_TABLE_NAME_LENGTH + 1]; /* Make sure table name is not too long */ ptr = ToString(*lplpSql, lpSqlNode->node.table.Name); if (s_lstrlen(ptr) > ISAMMaxTableNameLength(lpstmt->lpdbc->lpISAM)){ s_lstrcpy(lpstmt->szError, ptr); return ERR_INVALIDTABLENAME; } else { //Copy table name tableNameBuff[0] = 0; s_lstrcpy (tableNameBuff, ptr); } /* Figure out if write access is needed */ lpSqlNodeStmt = ToNode(*lplpSql, idxEnclosingStatement); switch (lpSqlNodeStmt->sqlNodeType) { case NODE_TYPE_SELECT: fReadOnly = TRUE; break; case NODE_TYPE_INSERT: fReadOnly = FALSE; break; case NODE_TYPE_UPDATE: fReadOnly = FALSE; break; case NODE_TYPE_DELETE: fReadOnly = FALSE; break; case NODE_TYPE_CREATEINDEX: fReadOnly = FALSE; break; case NODE_TYPE_DROPINDEX: fReadOnly = FALSE; break; default: return ERR_INTERNAL; } /* resolve qualifier */ if (lpSqlNode->node.table.Qualifier != NO_STRING) { LPSTR pQual = (LPSTR) ToString(*lplpSql, lpSqlNode->node.table.Qualifier); if (lstrlen(pQual) > ISAMMaxTableNameLength(lpstmt->lpdbc->lpISAM)) { s_lstrcpy((char*)lpstmt->szError, ptr); return ERR_INVALIDTABLENAME; } if ( (lstrlen(pQual) >= 4) && (_strnicmp("root", pQual, 4) == 0) ) { // absolute qualifier [starts with 'root'] (no need to change) } else { // concatenate the current namespace with the qualifier LPUSTR currQual = ISAMDatabase (lpstmt->lpdbc->lpISAM); char * pszNewQualifier = new char [strlen (pQual)+ s_lstrlen (currQual)+2+1]; s_lstrcpy (pszNewQualifier, currQual); s_lstrcat (pszNewQualifier, "\\"); s_lstrcat (pszNewQualifier, pQual); lpSqlNode->node.table.Qualifier = AllocateString (lplpSql, (LPUSTR)pszNewQualifier); delete [] pszNewQualifier; } } else { // the current namespace is the qualifier LPUSTR currQual = ISAMDatabase (lpstmt->lpdbc->lpISAM); lpSqlNode->node.table.Qualifier = AllocateString (lplpSql, (LPUSTR)currQual); } #ifdef TESTING //Create extra test class with dummy values for testing // IMosProvider* pDummyProv = ISAMGetGatewayServer(lpstmt->lpdbc->lpISAM); // CreateClassAndInstance(pDummyProv); #endif /* Open the table */ LPUSTR pQual = ToString(*lplpSql, lpSqlNode->node.table.Qualifier); lpSqlNodeRoot = ToNode(*lplpSql, ROOT_SQLNODE); //If this the special Passthrough SQL table ? char* virTbl = WBEMDR32_VIRTUAL_TABLE2; if (s_lstrcmp(tableNameBuff, virTbl) == 0) { //Yes this is the Passthrough SQL table err = ISAMOpenTable(lpSqlNodeRoot->node.root.lpISAM, (LPUSTR) pQual, (SWORD) s_lstrlen (pQual), (LPUSTR) tableNameBuff, fReadOnly, &(lpSqlNode->node.table.Handle), lpstmt); } else { //No normal table err = ISAMOpenTable(lpSqlNodeRoot->node.root.lpISAM, (LPUSTR) pQual, (SWORD) s_lstrlen (pQual), (LPUSTR) tableNameBuff, fReadOnly, &(lpSqlNode->node.table.Handle)); } if (err != NO_ISAM_ERR) { lpSqlNode->node.table.Handle = NULL; ISAMGetErrorMessage(lpSqlNodeRoot->node.root.lpISAM, (LPUSTR)lpstmt->szISAMError); return err; } if (lpstmt->lpdbc->lpISAM->fSchemaInfoTransactioned) lpstmt->fISAMTxnStarted = TRUE; err = ERR_SUCCESS; } break; case NODE_TYPE_COLUMN: /* Save pointer to enclosing statement */ lpSqlNode->node.column.EnclosingStatement = idxEnclosingStatement; //Sai Wong table column info /* Is this column node in an outer join predicate? */ if (idxNodeTableOuterJoinFromTables == NO_SQLNODE) { /* No. Resolve it against the table lists */ LPSQLNODE lpSqlNodeStmt; LPSQLNODE lpSqlNodeTables; SQLNODEIDX idxTables; SQLNODEIDX idxStatement; STRINGIDX idxOriginalAlias; STRINGIDX idxAlias; BOOL fFoundMatch; STRINGIDX idxQualifier, idxOriginalQualifier; /* No. Search up all the enclosing statements for the column */ idxStatement = idxEnclosingStatement; while (idxStatement != NO_SQLNODE) { /* Get node of the statement */ lpSqlNodeStmt = ToNode(*lplpSql, idxStatement); /* Resolve column */ switch (lpSqlNodeStmt->sqlNodeType) { case NODE_TYPE_SELECT: /* Save the table alias and qualifier specified */ idxAlias = lpSqlNode->node.column.Tablealias; idxOriginalAlias = lpSqlNode->node.column.Tablealias; idxQualifier = idxOriginalQualifier = lpSqlNode->node.column.Qualifier; /* Look through the list of tables */ fFoundMatch = FALSE; err = ERR_COLUMNNOTFOUND; for (idxTables = lpSqlNodeStmt->node.select.Tables; idxTables != NO_SQLNODE; idxTables = lpSqlNodeTables->node.tables.Next) { /* Is the column in this table? */ lpSqlNodeTables = ToNode(*lplpSql, idxTables); err = FindColumn(lpstmt, *lplpSql, fCaseSensitive, lpSqlNode, lpSqlNodeTables->node.tables.Table, idxQualifier); if (err == ERR_SUCCESS) { /* Yes. Error if column also in another table */ if (fFoundMatch) { s_lstrcpy((char*)lpstmt->szError, ToString(*lplpSql, lpSqlNode->node.column.Column)); return ERR_COLUMNFOUND; } fFoundMatch = TRUE; /* If a table alias was specified for column, */ /* there is no need to check the other tables */ /* I'm going to remove this check as there is the */ /* possibility of a clash between an alias-qualified*/ /* name and a namespace qualified name. This clash */ /* is not picked up during the semantic check of the*/ /* table list */ //if (idxOriginalAlias != NO_STRING) // break; /* Save the alias of the table found */ idxAlias = lpSqlNode->node.column.Tablealias; idxQualifier = lpSqlNode->node.column.Qualifier; /* Restore the original alias and keep looking */ //Added by Sai Wong lpSqlNode->node.column.Tablealias = idxOriginalAlias; //NO_STRING; lpSqlNode->node.column.Qualifier = idxOriginalQualifier; } else if (err != ERR_COLUMNNOTFOUND) return err; } /* Restore the alias found */ lpSqlNode->node.column.Tablealias = idxAlias; lpSqlNode->node.column.Qualifier = idxQualifier; /* Was the column found in this statement? */ if (fFoundMatch) err = ERR_SUCCESS; else err = ERR_COLUMNNOTFOUND; break; case NODE_TYPE_INSERT: if (idxStatement == idxEnclosingStatement) err = FindColumn(lpstmt, *lplpSql, fCaseSensitive, lpSqlNode, lpSqlNodeStmt->node.insert.Table, NO_STRING); else err = ERR_COLUMNNOTFOUND; break; case NODE_TYPE_UPDATE: err = FindColumn(lpstmt, *lplpSql, fCaseSensitive, lpSqlNode, lpSqlNodeStmt->node.update.Table, NO_STRING); break; case NODE_TYPE_DELETE: err = FindColumn(lpstmt, *lplpSql, fCaseSensitive, lpSqlNode, lpSqlNodeStmt->node.delet.Table, NO_STRING); break; case NODE_TYPE_CREATEINDEX: err = FindColumn(lpstmt, *lplpSql, fCaseSensitive, lpSqlNode, lpSqlNodeStmt->node.createindex.Table, NO_STRING); break; default: return ERR_INTERNAL; } if ((err != ERR_SUCCESS) && (err != ERR_COLUMNNOTFOUND)) return err; /* If the column was found in this statement, use it */ if (err == ERR_SUCCESS) break; /* Try looking in the enclosing statement (if any) */ if (lpSqlNodeStmt->sqlNodeType != NODE_TYPE_SELECT) break; idxStatement = lpSqlNodeStmt->node.select.EnclosingStatement; } /* If column not found, error */ if (err == ERR_COLUMNNOTFOUND) { s_lstrcpy(lpstmt->szError, ToString(*lplpSql, lpSqlNode->node.column.Column)); return ERR_COLUMNNOTFOUND; } } else { /* Yes. Resolve it against the table lists */ STRINGIDX idxAlias; STRINGIDX idxOriginalAlias; BOOL fFoundMatch; SQLNODEIDX idxTables; LPSQLNODE lpSqlNodeTables; SQLNODEIDX idxTable; LPSQLNODE lpSqlNodeTable; STRINGIDX idxQualifier, idxOriginalQualifier; /* Save the table alias specified */ idxAlias = lpSqlNode->node.column.Tablealias; idxOriginalAlias = lpSqlNode->node.column.Tablealias; idxQualifier = idxOriginalQualifier = lpSqlNode->node.column.Qualifier; /* Is the column in a table in the outer join expression? */ idxTables = idxNodeTableOuterJoinFromTables; lpSqlNodeTables = ToNode(*lplpSql, idxTables); idxTable = lpSqlNodeTables->node.tables.Table; fFoundMatch = FALSE; while (TRUE) { /* Is it in this table? */ err = FindColumn(lpstmt, *lplpSql, fCaseSensitive, lpSqlNode, idxTable, lpSqlNodeTables->node.column.Qualifier); if (err == ERR_SUCCESS) { /* Yes. Error if it matches another table too */ if (fFoundMatch) { s_lstrcpy(lpstmt->szError, ToString(*lplpSql, lpSqlNode->node.column.Column)); return ERR_COLUMNFOUND; } /* Set flag */ fFoundMatch = TRUE; /* Save the alias of the table found */ idxAlias = lpSqlNode->node.column.Tablealias; idxQualifier = lpSqlNode->node.column.Qualifier; /* Restore the original alias if need be */ //Added by Sai Wong // if (idxOriginalAlias == NO_STRING) lpSqlNode->node.column.Tablealias = idxOriginalAlias; //NO_STRING; lpSqlNode->node.column.Qualifier = idxOriginalQualifier; } else if (err != ERR_COLUMNNOTFOUND) return err; /* Look in next table on list */ idxTables = lpSqlNodeTables->node.tables.Next; if (idxTables == NO_SQLNODE) break; lpSqlNodeTables = ToNode(*lplpSql, idxTables); idxTable = lpSqlNodeTables->node.tables.Table; lpSqlNodeTable = ToNode(*lplpSql, idxTable); /* If this table is not in the outer-join, stop looking */ if (lpSqlNodeTable->node.table.OuterJoinFromTables == NO_SQLNODE) break; } /* Error if column was not found */ if (!fFoundMatch) { s_lstrcpy(lpstmt->szError, ToString(*lplpSql, lpSqlNode->node.column.Column)); return ERR_COLUMNNOTFOUND; } /* Restore the alias found */ lpSqlNode->node.column.Tablealias = idxAlias; lpSqlNode->node.column.Qualifier = idxQualifier; err = ERR_SUCCESS; } /* Does the column have to be a group by column? */ if (fIsGroupby) { LPUSTR lpszColumn; LPUSTR lpszTable; LPSQLNODE lpSqlNodeStmt; SQLNODEIDX idxGroupbycolumns; LPSQLNODE lpSqlNodeGroupbycolumns; LPSQLNODE lpSqlNodeColumnGroupby; LPUSTR lpszColumnGroupby; LPUSTR lpszTableGroupby; BOOL fMatch; /* Yes. Get the name of the column and table */ lpszColumn = ToString(*lplpSql, lpSqlNode->node.column.Column); lpszTable = ToString(*lplpSql, lpSqlNode->node.column.Tablealias); /* Get list of group by columns */ lpSqlNodeStmt = ToNode(*lplpSql, idxEnclosingStatement); if (lpSqlNodeStmt->sqlNodeType != NODE_TYPE_SELECT) return ERR_INTERNAL; idxGroupbycolumns = lpSqlNodeStmt->node.select.Groupbycolumns; /* Error if implicit group by */ if (lpSqlNodeStmt->node.select.ImplicitGroupby) { s_lstrcpy(lpstmt->szError, ToString(*lplpSql, lpSqlNode->node.column.Column)); return ERR_COLUMNNOTFOUND; } /* Find the column in the group by lists */ while (TRUE) { /* Get this group by column */ lpSqlNodeGroupbycolumns = ToNode(*lplpSql, idxGroupbycolumns); if (lpSqlNodeGroupbycolumns->node.groupbycolumns.Column != NO_SQLNODE) { lpSqlNodeColumnGroupby = ToNode(*lplpSql, lpSqlNodeGroupbycolumns->node.groupbycolumns.Column); /* Get column name and table name of the group by column */ lpszTableGroupby = ToString(*lplpSql, lpSqlNodeColumnGroupby->node.column.Tablealias); lpszColumnGroupby = ToString(*lplpSql, lpSqlNodeColumnGroupby->node.column.Column); /* Do the column name match? */ fMatch = FALSE; if (fCaseSensitive) { if (!s_lstrcmp(lpszColumnGroupby, lpszColumn) && !s_lstrcmp(lpszTableGroupby, lpszTable)) fMatch = TRUE; } else { if (!s_lstrcmpi(lpszColumnGroupby, lpszColumn) && !s_lstrcmpi(lpszTableGroupby, lpszTable)) fMatch = TRUE; } } else fMatch = FALSE; /* Does this column match a group by column? */ if (fMatch) { /* Yes. Fill in semantic information for the column */ lpSqlNode->node.column.InSortRecord = TRUE; lpSqlNode->node.column.Offset = lpSqlNodeColumnGroupby->node.column.Offset; lpSqlNode->node.column.Length = lpSqlNodeColumnGroupby->node.column.Length; lpSqlNode->node.column.DistinctOffset = lpSqlNodeColumnGroupby->node.column.DistinctOffset; lpSqlNode->node.column.DistinctLength = lpSqlNodeColumnGroupby->node.column.DistinctLength; break; } /* Not found yet. Check next column */ idxGroupbycolumns = lpSqlNodeGroupbycolumns->node.groupbycolumns.Next; /* Error if column not found */ if (idxGroupbycolumns == NO_SQLNODE) { s_lstrcpy((char*)lpstmt->szError, ToString(*lplpSql, lpSqlNode->node.column.Column)); return ERR_COLUMNNOTFOUND; } } } /* Allocate space for the column value */ switch (lpSqlNode->sqlDataType) { case TYPE_DOUBLE: break; case TYPE_NUMERIC: lpSqlNode->node.column.Value = AllocateSpace(lplpSql, (SWORD) (1 + 2 + lpSqlNode->sqlPrecision)); if (lpSqlNode->node.column.Value == NO_STRING) return ERR_MEMALLOCFAIL; break; case TYPE_INTEGER: break; case TYPE_CHAR: lpSqlNode->node.column.Value = AllocateSpace(lplpSql, (SWORD) (1 + lpSqlNode->sqlPrecision)); if (lpSqlNode->node.column.Value == NO_STRING) return ERR_MEMALLOCFAIL; break; case TYPE_BINARY: lpSqlNode->node.column.Value = AllocateSpace(lplpSql, (SWORD) (sizeof(SDWORD) + lpSqlNode->sqlPrecision)); if (lpSqlNode->node.column.Value == NO_STRING) return ERR_MEMALLOCFAIL; break; case TYPE_DATE: break; case TYPE_TIME: break; case TYPE_TIMESTAMP: break; default: return ERR_NOTSUPPORTED; } break; case NODE_TYPE_STRING: lpSqlNode->sqlDataType = TYPE_CHAR; lpSqlNode->sqlSqlType = SQL_VARCHAR; ptr = ToString(*lplpSql, lpSqlNode->node.string.Value); lpSqlNode->sqlPrecision = s_lstrlen(ptr); lpSqlNode->sqlScale = NO_SCALE; break; case NODE_TYPE_NUMERIC: if (lpSqlNode->sqlDataType == TYPE_UNKNOWN) { lpSqlNode->sqlDataType = TYPE_DOUBLE; lpSqlNode->sqlSqlType = SQL_DOUBLE; lpSqlNode->sqlPrecision = 15; lpSqlNode->sqlScale = NO_SCALE; } break; case NODE_TYPE_PARAMETER: lpSqlNode->sqlDataType = TYPE_UNKNOWN; lpSqlNode->sqlSqlType = SQL_TYPE_NULL; lpSqlNode->sqlPrecision = 0; lpSqlNode->sqlScale = NO_SCALE; break; case NODE_TYPE_USER: lpSqlNode->sqlDataType = TYPE_CHAR; lpSqlNode->sqlSqlType = SQL_VARCHAR; lpSqlNode->sqlPrecision = s_lstrlen( ISAMUser(ToNode(*lplpSql, ROOT_SQLNODE)->node.root.lpISAM)); lpSqlNode->sqlScale = NO_SCALE; break; case NODE_TYPE_NULL: lpSqlNode->sqlDataType = TYPE_UNKNOWN; lpSqlNode->sqlSqlType = SQL_TYPE_NULL; lpSqlNode->sqlPrecision = 0; lpSqlNode->sqlScale = NO_SCALE; break; case NODE_TYPE_DATE: lpSqlNode->sqlDataType = TYPE_DATE; if (lpSqlNode->sqlSqlType == SQL_TYPE_NULL) { lpSqlNode->sqlSqlType = SQL_DATE; lpSqlNode->sqlPrecision = 10; lpSqlNode->sqlScale = NO_SCALE; } break; case NODE_TYPE_TIME: lpSqlNode->sqlDataType = TYPE_TIME; if (lpSqlNode->sqlSqlType == SQL_TYPE_NULL) { lpSqlNode->sqlSqlType = SQL_TIME; lpSqlNode->sqlPrecision = 8; lpSqlNode->sqlScale = NO_SCALE; } break; case NODE_TYPE_TIMESTAMP: lpSqlNode->sqlDataType = TYPE_TIMESTAMP; if (lpSqlNode->sqlSqlType == SQL_TYPE_NULL) { lpSqlNode->sqlSqlType = SQL_TIMESTAMP; if (TIMESTAMP_SCALE != 0) lpSqlNode->sqlPrecision = 20 + TIMESTAMP_SCALE; else lpSqlNode->sqlPrecision = 19; lpSqlNode->sqlScale = TIMESTAMP_SCALE; } break; default: return ERR_INTERNAL; } return err; } /***************************************************************************/ void INTFUNC FreeTreeSemantic(LPSQLTREE lpSql, SQLNODEIDX idxNode) /* Deallocates semantic information in a parse tree */ { LPSQLNODE lpSqlNode; SQLNODEIDX idxParameter; SQLNODEIDX idxParameterNext; if (idxNode == NO_SQLNODE) return; lpSqlNode = ToNode(lpSql, idxNode); switch (lpSqlNode->sqlNodeType) { case NODE_TYPE_NONE: break; case NODE_TYPE_ROOT: FreeTreeSemantic(lpSql, lpSqlNode->node.root.sql); if (lpSqlNode->node.root.passthroughParams) { idxParameter = lpSqlNode->node.root.parameters; while (idxParameter != NO_SQLNODE) { lpSqlNode = ToNode(lpSql, idxParameter); idxParameterNext = lpSqlNode->node.parameter.Next; FreeTreeSemantic(lpSql, idxParameter); idxParameter = idxParameterNext; } } break; case NODE_TYPE_CREATE: FreeTreeSemantic(lpSql, lpSqlNode->node.create.Columns); break; case NODE_TYPE_DROP: break; case NODE_TYPE_SELECT: FreeTreeSemantic(lpSql, lpSqlNode->node.select.Values); FreeTreeSemantic(lpSql, lpSqlNode->node.select.Tables); FreeTreeSemantic(lpSql, lpSqlNode->node.select.Predicate); FreeTreeSemantic(lpSql, lpSqlNode->node.select.Groupbycolumns); FreeTreeSemantic(lpSql, lpSqlNode->node.select.Sortcolumns); if (lpSqlNode->node.select.Sortfile != HFILE_ERROR) { LPUSTR lpszSortfile; _lclose(lpSqlNode->node.select.Sortfile); lpSqlNode->node.select.Sortfile = HFILE_ERROR; lpszSortfile = ToString(lpSql, lpSqlNode->node.select.SortfileName); DeleteFile((LPCSTR) lpszSortfile); s_lstrcpy(lpszSortfile, ""); } break; case NODE_TYPE_INSERT: FreeTreeSemantic(lpSql, lpSqlNode->node.insert.Table); FreeTreeSemantic(lpSql, lpSqlNode->node.insert.Columns); FreeTreeSemantic(lpSql, lpSqlNode->node.insert.Values); break; case NODE_TYPE_DELETE: FreeTreeSemantic(lpSql, lpSqlNode->node.delet.Table); FreeTreeSemantic(lpSql, lpSqlNode->node.delet.Predicate); break; case NODE_TYPE_UPDATE: FreeTreeSemantic(lpSql, lpSqlNode->node.update.Table); FreeTreeSemantic(lpSql, lpSqlNode->node.update.Updatevalues); FreeTreeSemantic(lpSql, lpSqlNode->node.update.Predicate); break; case NODE_TYPE_CREATEINDEX: FreeTreeSemantic(lpSql, lpSqlNode->node.createindex.Table); FreeTreeSemantic(lpSql, lpSqlNode->node.createindex.Columns); break; case NODE_TYPE_DROPINDEX: break; case NODE_TYPE_PASSTHROUGH: break; case NODE_TYPE_TABLES: FreeTreeSemantic(lpSql, lpSqlNode->node.tables.Table); FreeTreeSemantic(lpSql, lpSqlNode->node.tables.Next); break; case NODE_TYPE_VALUES: while (TRUE) { FreeTreeSemantic(lpSql, lpSqlNode->node.values.Value); if (lpSqlNode->node.values.Next == NO_SQLNODE) break; lpSqlNode = ToNode(lpSql, lpSqlNode->node.values.Next); } break; case NODE_TYPE_COLUMNS: while (TRUE) { FreeTreeSemantic(lpSql, lpSqlNode->node.columns.Column); if (lpSqlNode->node.columns.Next == NO_SQLNODE) break; lpSqlNode = ToNode(lpSql, lpSqlNode->node.columns.Next); } break; case NODE_TYPE_SORTCOLUMNS: while (TRUE) { FreeTreeSemantic(lpSql, lpSqlNode->node.sortcolumns.Column); if (lpSqlNode->node.sortcolumns.Next == NO_SQLNODE) break; lpSqlNode = ToNode(lpSql, lpSqlNode->node.sortcolumns.Next); } break; case NODE_TYPE_GROUPBYCOLUMNS: while (TRUE) { FreeTreeSemantic(lpSql, lpSqlNode->node.groupbycolumns.Column); if (lpSqlNode->node.groupbycolumns.Next == NO_SQLNODE) break; lpSqlNode = ToNode(lpSql, lpSqlNode->node.groupbycolumns.Next); } break; case NODE_TYPE_UPDATEVALUES: while (TRUE) { FreeTreeSemantic(lpSql, lpSqlNode->node.updatevalues.Column); FreeTreeSemantic(lpSql, lpSqlNode->node.updatevalues.Value); if (lpSqlNode->node.updatevalues.Next == NO_SQLNODE) break; lpSqlNode = ToNode(lpSql, lpSqlNode->node.updatevalues.Next); } break; case NODE_TYPE_CREATECOLS: while (TRUE) { if (lpSqlNode->node.createcols.Next == NO_SQLNODE) break; lpSqlNode = ToNode(lpSql, lpSqlNode->node.createcols.Next); } break; case NODE_TYPE_BOOLEAN: FreeTreeSemantic(lpSql, lpSqlNode->node.boolean.Left); FreeTreeSemantic(lpSql, lpSqlNode->node.boolean.Right); break; case NODE_TYPE_COMPARISON: FreeTreeSemantic(lpSql, lpSqlNode->node.comparison.Left); FreeTreeSemantic(lpSql, lpSqlNode->node.comparison.Right); break; case NODE_TYPE_ALGEBRAIC: FreeTreeSemantic(lpSql, lpSqlNode->node.algebraic.Left); FreeTreeSemantic(lpSql, lpSqlNode->node.algebraic.Right); break; case NODE_TYPE_SCALAR: FreeTreeSemantic(lpSql, lpSqlNode->node.scalar.Arguments); break; case NODE_TYPE_AGGREGATE: FreeTreeSemantic(lpSql, lpSqlNode->node.aggregate.Expression); break; case NODE_TYPE_TABLE: if (lpSqlNode->node.table.Handle != NULL) { ISAMCloseTable(lpSqlNode->node.table.Handle); lpSqlNode->node.table.Handle = NULL; } FreeTreeSemantic(lpSql, lpSqlNode->node.table.OuterJoinPredicate); break; case NODE_TYPE_COLUMN: break; case NODE_TYPE_STRING: break; case NODE_TYPE_NUMERIC: break; case NODE_TYPE_PARAMETER: break; case NODE_TYPE_USER: break; case NODE_TYPE_NULL: break; case NODE_TYPE_DATE: break; case NODE_TYPE_TIME: break; case NODE_TYPE_TIMESTAMP: break; default: break; } } /***************************************************************************/