From 30bd367a3d838e5e4b4dce2cb787a53d721645ad Mon Sep 17 00:00:00 2001 From: dsandersoremutah Date: Wed, 5 Jul 2006 15:25:36 +0000 Subject: [PATCH] Modifications to add support for more SQL statements. git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@634 0109f412-320b-0410-ab79-c3e0c5ffbbe6 --- sql/src/createdatabase.cpp | 16 +- sql/src/createindex.cpp | 4 +- sql/src/createtable.cpp | 2 +- sql/src/deleterow.cpp | 192 ++++- sql/src/dropdatabase.cpp | 12 +- sql/src/fcache.h | 8 +- sql/src/flaimodbc.h | 137 +++- sql/src/flaimsql.h | 32 +- sql/src/flaimsys.h | 52 +- sql/src/frow.cpp | 35 +- sql/src/fslfileu.cpp | 2 +- sql/src/insertrow.cpp | 72 +- sql/src/kybldkey.cpp | 28 +- sql/src/kybuild.cpp | 269 +++++-- sql/src/opendatabase.cpp | 16 +- sql/src/rfl.cpp | 258 +++++- sql/src/rfl.h | 19 + sql/src/sqleval.cpp | 16 +- sql/src/sqloptimize.cpp | 17 +- sql/src/sqlparser.cpp | 577 ------------- sql/src/sqlstatement.cpp | 421 ++++++++-- sql/src/sqlstatement.h | 49 +- sql/src/updaterow.cpp | 1298 +++++++++++++++++++++++++++++ sql/src/whereclause.cpp | 1568 ++++++++++++++++++++++++++++++++++++ 24 files changed, 4222 insertions(+), 878 deletions(-) delete mode 100644 sql/src/sqlparser.cpp create mode 100644 sql/src/updaterow.cpp create mode 100644 sql/src/whereclause.cpp diff --git a/sql/src/createdatabase.cpp b/sql/src/createdatabase.cpp index f822a97..857ee18 100644 --- a/sql/src/createdatabase.cpp +++ b/sql/src/createdatabase.cpp @@ -499,9 +499,9 @@ RCODE SQLStatement::processCreateDatabase( void) // Get the database name. - if (RC_BAD( rc = getUTF8String( FALSE, (FLMBYTE *)szDatabaseName, + if (RC_BAD( rc = getUTF8String( FALSE, TRUE, (FLMBYTE *)szDatabaseName, sizeof( szDatabaseName), - &uiDatabaseNameLen))) + &uiDatabaseNameLen, NULL, NULL))) { goto Exit; } @@ -575,18 +575,18 @@ RCODE SQLStatement::processCreateDatabase( void) { if (f_stricmp( szToken, "data_dir") == 0) { - if (RC_BAD( rc = getUTF8String( TRUE, (FLMBYTE *)szDataDirName, + if (RC_BAD( rc = getUTF8String( TRUE, TRUE, (FLMBYTE *)szDataDirName, sizeof( szDataDirName), - &uiDataDirNameLen))) + &uiDataDirNameLen, NULL, NULL))) { goto Exit; } } else if (f_stricmp( szToken, "rfl_dir") == 0) { - if (RC_BAD( rc = getUTF8String( TRUE, (FLMBYTE *)szRflDirName, + if (RC_BAD( rc = getUTF8String( TRUE, TRUE, (FLMBYTE *)szRflDirName, sizeof( szRflDirName), - &uiRflDirNameLen))) + &uiRflDirNameLen, NULL, NULL))) { goto Exit; } @@ -638,9 +638,9 @@ RCODE SQLStatement::processCreateDatabase( void) } else if (f_stricmp( szToken, "language") == 0) { - if (RC_BAD( rc = getUTF8String( TRUE, (FLMBYTE *)szLanguage, + if (RC_BAD( rc = getUTF8String( TRUE, TRUE, (FLMBYTE *)szLanguage, sizeof( szLanguage), - &uiLanguageLen))) + &uiLanguageLen, NULL, NULL))) { goto Exit; } diff --git a/sql/src/createindex.cpp b/sql/src/createindex.cpp index 2308030..38ff424 100644 --- a/sql/src/createindex.cpp +++ b/sql/src/createindex.cpp @@ -561,7 +561,7 @@ RCODE SQLStatement::processCreateIndex( // Get the column name if (RC_BAD( rc = getName( szColumnName, sizeof( szColumnName), - &uiColumnNameLen))) + &uiColumnNameLen, &uiTokenLineOffset))) { goto Exit; } @@ -572,7 +572,7 @@ RCODE SQLStatement::processCreateIndex( if ((pColumn = m_pDb->m_pDict->findColumn( pTable, szColumnName)) == NULL) { setErrInfo( m_uiCurrLineNum, - m_uiCurrLineOffset - 1, + uiTokenLineOffset, SQL_ERR_UNDEFINED_COLUMN, m_uiCurrLineFilePos, m_uiCurrLineBytes); diff --git a/sql/src/createtable.cpp b/sql/src/createtable.cpp index d6543d5..7347360 100644 --- a/sql/src/createtable.cpp +++ b/sql/src/createtable.cpp @@ -498,7 +498,7 @@ RCODE SQLStatement::processCreateTable( void) // Get the column name if (RC_BAD( rc = getName( szColumnName, sizeof( szColumnName), - &uiColumnNameLen))) + &uiColumnNameLen, &uiTokenLineOffset))) { goto Exit; } diff --git a/sql/src/deleterow.cpp b/sql/src/deleterow.cpp index e861810..d759d19 100644 --- a/sql/src/deleterow.cpp +++ b/sql/src/deleterow.cpp @@ -70,7 +70,7 @@ RCODE F_Db::deleteRow( // Delete any index keys for the row. - if (RC_BAD( rc = updateIndexKeys( uiTableNum, pRow, NULL))) + if (RC_BAD( rc = updateIndexKeys( uiTableNum, pRow, FALSE, NULL))) { goto Exit; } @@ -136,3 +136,193 @@ Exit: return( rc); } +//------------------------------------------------------------------------------ +// Desc: Delete selected rows from the database. +//------------------------------------------------------------------------------ +RCODE F_Db::deleteSelectedRows( + FLMUINT uiTableNum, + SQLQuery * pSqlQuery) +{ + RCODE rc = NE_SFLM_OK; + FLMBOOL bStartedTrans = FALSE; + F_TABLE * pTable; + F_Row * pRow = NULL; + + // Make sure we are in an update transaction. + + if (RC_BAD( rc = checkTransaction( SFLM_UPDATE_TRANS, &bStartedTrans))) + { + goto Exit; + } + + // Cannot delete from internal system tables. + + pTable = m_pDict->getTable( uiTableNum); + if (pTable->bSystemTable) + { + rc = RC_SET( NE_SFLM_CANNOT_DELETE_IN_SYSTEM_TABLE); + goto Exit; + } + + // Execute the query + + for (;;) + { + if (RC_BAD( rc = pSqlQuery->getNext( &pRow))) + { + if (rc == NE_SFLM_EOF_HIT) + { + rc = NE_SFLM_OK; + break; + } + else + { + goto Exit; + } + } + + if (RC_BAD( rc = deleteRow( uiTableNum, pRow->m_ui64RowId, TRUE))) + { + goto Exit; + } + } + + // Commit the transaction if we started it + + if (bStartedTrans) + { + bStartedTrans = FALSE; + if (RC_BAD( rc = transCommit())) + { + goto Exit; + } + } + +Exit: + + if (pRow) + { + pRow->ReleaseRow(); + } + + if (bStartedTrans) + { + transAbort(); + } + + return( rc); +} + +//------------------------------------------------------------------------------ +// Desc: Process the DELETE statement. The "DELETE" keyword has already been +// parsed. +//------------------------------------------------------------------------------ +RCODE SQLStatement::processDeleteRows( void) +{ + RCODE rc = NE_SFLM_OK; + FLMBOOL bStartedTrans = FALSE; + char szTableName [MAX_SQL_NAME_LEN + 1]; + FLMUINT uiTableNameLen; + F_TABLE * pTable; + TABLE_ITEM tableList [2]; + SQLQuery sqlQuery; + + // If we are in a read transaction, we cannot do this operation + + if (RC_BAD( rc = m_pDb->checkTransaction( SFLM_UPDATE_TRANS, &bStartedTrans))) + { + goto Exit; + } + + // SYNTAX: DELETE FROM table_name WHERE select-criteria + + // FROM must follow the DELETE. + + if (RC_BAD( rc = haveToken( "from", FALSE, SQL_ERR_EXPECTING_FROM))) + { + goto Exit; + } + + // Whitespace must follow the "FROM" + + if (RC_BAD( rc = skipWhitespace( TRUE))) + { + goto Exit; + } + + // Get the table name. + + if (RC_BAD( rc = getTableName( TRUE, szTableName, sizeof( szTableName), + &uiTableNameLen, &pTable))) + { + goto Exit; + } + + if (pTable->bSystemTable) + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset, + SQL_ERR_CANNOT_UPDATE_SYSTEM_TABLE, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_CANNOT_DELETE_IN_SYSTEM_TABLE); + goto Exit; + } + + // See if we have a WHERE clause + + if (RC_BAD( rc = haveToken( "where", TRUE))) + { + if (rc == NE_SFLM_NOT_FOUND || rc == NE_SFLM_EOF_HIT) + { + if (RC_BAD( rc = sqlQuery.addTable( pTable->uiTableNum, NULL))) + { + goto Exit; + } + } + else + { + goto Exit; + } + } + else + { + + tableList [0].uiTableNum = pTable->uiTableNum; + tableList [0].pszTableAlias = pTable->pszTableName; + + // Null terminate the list. + + tableList [1].uiTableNum = 0; + if (RC_BAD( rc = parseCriteria( &tableList [0], FALSE, FALSE, &sqlQuery))) + { + goto Exit; + } + } + + if (RC_BAD( rc = m_pDb->deleteSelectedRows( pTable->uiTableNum, &sqlQuery))) + { + goto Exit; + } + + // Commit the transaction if we started it + + if (bStartedTrans) + { + bStartedTrans = FALSE; + if (RC_BAD( rc = m_pDb->transCommit())) + { + goto Exit; + } + } + +Exit: + + if (bStartedTrans) + { + m_pDb->transAbort(); + } + + return( rc); +} + diff --git a/sql/src/dropdatabase.cpp b/sql/src/dropdatabase.cpp index 62fd0e1..c7c3660 100644 --- a/sql/src/dropdatabase.cpp +++ b/sql/src/dropdatabase.cpp @@ -344,9 +344,9 @@ RCODE SQLStatement::processDropDatabase( void) // Get the database name. - if (RC_BAD( rc = getUTF8String( FALSE, (FLMBYTE *)szDatabaseName, + if (RC_BAD( rc = getUTF8String( FALSE, TRUE, (FLMBYTE *)szDatabaseName, sizeof( szDatabaseName), - &uiDatabaseNameLen))) + &uiDatabaseNameLen, NULL, NULL))) { goto Exit; } @@ -375,18 +375,18 @@ RCODE SQLStatement::processDropDatabase( void) if (f_stricmp( szToken, "data_dir") == 0) { - if (RC_BAD( rc = getUTF8String( TRUE, (FLMBYTE *)szDataDirName, + if (RC_BAD( rc = getUTF8String( TRUE, TRUE, (FLMBYTE *)szDataDirName, sizeof( szDataDirName), - &uiDataDirNameLen))) + &uiDataDirNameLen, NULL, NULL))) { goto Exit; } } else if (f_stricmp( szToken, "data_dir") == 0) { - if (RC_BAD( rc = getUTF8String( TRUE, (FLMBYTE *)szRflDirName, + if (RC_BAD( rc = getUTF8String( TRUE, TRUE, (FLMBYTE *)szRflDirName, sizeof( szRflDirName), - &uiRflDirNameLen))) + &uiRflDirNameLen, NULL, NULL))) { goto Exit; } diff --git a/sql/src/fcache.h b/sql/src/fcache.h index 9fe4121..c984721 100644 --- a/sql/src/fcache.h +++ b/sql/src/fcache.h @@ -1864,7 +1864,7 @@ private: RCODE _makeWriteCopy( F_Db * pDb, - F_Row ** ppRow); + F_Row ** ppRow); // Private Data @@ -2463,6 +2463,12 @@ public: return( setNumber64( pDb, uiColumnNum, ui64Value, bNeg)); } + RCODE setValue( + F_Db * pDb, + FLMUINT uiColumnNum, + const FLMBYTE * pucValue, + FLMUINT uiValueLen); + RCODE setUTF8( F_Db * pDb, FLMUINT uiColumnNum, diff --git a/sql/src/flaimodbc.h b/sql/src/flaimodbc.h index 2dfdf62..02c8fde 100644 --- a/sql/src/flaimodbc.h +++ b/sql/src/flaimodbc.h @@ -41,7 +41,8 @@ class FSTableCursor; //------------------------------------------------------------------------- typedef enum { - SQL_OPERATOR_NODE = 0, + SQL_NO_NODE = 0, + SQL_OPERATOR_NODE, SQL_VALUE_NODE, SQL_COLUMN_NODE, SQL_PRED_NODE @@ -134,6 +135,19 @@ typedef struct SQL_OP FLMUINT uiCompareRules; } SQL_OP; +typedef struct SQL_STRING_VALUE +{ + FLMBYTE * pszStr; // Should always be null-terminated. + FLMUINT uiByteLen; // Includes null-terminating character. + FLMUINT uiNumChars; // Does not count null-terminating character. +} SQL_STRING_VALUE; + +typedef struct SQL_BINARY_VALUE +{ + FLMBYTE * pucValue; + FLMUINT uiByteLen; +} SQL_BINARY_VALUE; + typedef struct SQL_VALUE { eSQLValTypes eValType; @@ -160,7 +174,8 @@ typedef struct SQL_VALUE FLMUINT64 ui64Val; FLMINT iVal; FLMINT64 i64Val; - FLMBYTE * pucBuf; + SQL_STRING_VALUE str; + SQL_BINARY_VALUE bin; IF_PosIStream * pIStream; } val; // Holds or points to the atom value. } SQL_VALUE; @@ -303,7 +318,7 @@ typedef struct SQL_DNF_NODE //------------------------------------------------------------------------- // Desc: SQLQuery class - for building up an SQL query. //------------------------------------------------------------------------- -class SQLQuery +class SQLQuery : public F_Object { public: @@ -311,8 +326,6 @@ public: ~SQLQuery(); -private: - FINLINE FLMBOOL expectingOperand( void) { return( !m_pCurrParseState->bExpectingOperator); @@ -322,6 +335,112 @@ private: { return( m_pCurrParseState->bExpectingOperator); } + + RCODE addOperator( + eSQLQueryOperators eOperator, + FLMUINT uiCompareRules); + + RCODE allocOperandNode( + eSQLNodeTypes eNodeType, + SQL_NODE ** ppSQLNode); + + RCODE addTable( + FLMUINT uiTableNum, + SQL_TABLE ** ppTable); + + RCODE addColumn( + FLMUINT uiTableNum, + FLMUINT uiColumnNum); + + RCODE addUTF8String( + const FLMBYTE * pszUTF8Str, + FLMUINT uiStrLen, + FLMUINT uiNumChars); + + RCODE addBinary( + const FLMBYTE * pucValue, + FLMUINT uiValueLen); + + RCODE addUINT64( + FLMUINT64 ui64Num); + + RCODE addINT64( + FLMINT64 i64Num); + + RCODE addUINT( + FLMUINT uiNum); + + RCODE addINT( + FLMINT iNum); + + FINLINE RCODE addNumber( + FLMUINT64 ui64Num, + FLMBOOL bNeg) + { + if (!bNeg) + { + if (ui64Num <= (FLMUINT64)(FLM_MAX_UINT)) + { + return( addUINT( (FLMUINT)ui64Num)); + } + else + { + return( addUINT64( ui64Num)); + } + } + else + { + if (ui64Num <= (FLMUINT64)(FLM_MAX_INT)) + { + return( addINT( (FLMINT)(-((FLMINT64)ui64Num)))); + } + else + { + return( addINT64( -((FLMINT64)ui64Num))); + } + } + } + + RCODE addBoolean( + FLMBOOL bValue, + FLMBOOL bUnknown); + + FINLINE FLMBOOL criteriaIsComplete( void) + { + // Make sure we have a completed expression + + if (m_pCurrParseState) + { + if (m_pCurrParseState->pPrev || + m_pCurrParseState->uiNestLevel || + (m_pCurrParseState->pLastNode && + m_pCurrParseState->pLastNode->eNodeType == SQL_OPERATOR_NODE)) + { + return( FALSE); + } + } + return( TRUE); + } + + RCODE getNext( + F_Row ** ppRow); + + RCODE getPrev( + F_Row ** ppRow); + + RCODE getFirst( + F_Row ** ppRow); + + RCODE getLast( + F_Row ** ppRow); + + RCODE evalCriteria( + SQL_VALUE * pSqlValue, + F_Pool * pPool, + F_Row * pRow); + +private: + RCODE allocParseState( void); RCODE allocValueNode( @@ -329,14 +448,6 @@ private: eSQLValTypes eValType, SQL_NODE ** ppSQLNode); - RCODE addOperator( - eSQLQueryOperators eOperator, - FLMUINT uiCompareRules); - - RCODE addColumn( - FLMUINT uiTableNum, - FLMUINT uiColumnNum); - RCODE intersectPredicates( SQL_PRED * pPred, eSQLQueryOperators eOperator, diff --git a/sql/src/flaimsql.h b/sql/src/flaimsql.h index 401697d..fe7d876 100644 --- a/sql/src/flaimsql.h +++ b/sql/src/flaimsql.h @@ -127,6 +127,20 @@ typedef enum SQL_ERR_CANNOT_DROP_SYSTEM_TABLE, ///< 47 = Cannot drop internal system tables. SQL_ERR_UNDEFINED_ENCDEF, ///< 48 = Encryption definition specified does not exist. SQL_ERR_ENCDEF_ALREADY_DEFINED, ///< 49 = Encryption definition name is already defined in the database. + SQL_ERR_INVALID_WHERE_TABLE, ///< 50 = Invalid table name or alias specified for column in where clause. + SQL_ERR_AMBIGUOUS_COLUMN_NAME, ///< 51 = Ambiguous column name specified in where clause - belongs to more than one table. + SQL_ERR_INVALID_WHERE_COLUMN, ///< 52 = Invalid column specified in where clause. + SQL_ERR_UNEXPECTED_NOT_OPERATOR, ///< 53 = Unexpected NOT operator in where clause. + SQL_ERR_EXPECTING_OPERATOR, ///< 54 = Expecting an operator in where clause. + SQL_ERR_INVALID_OPERAND, ///< 55 = Invalid operand specified in where clause. + SQL_ERR_UNEXPECTED_LPAREN, ///< 56 = Unexpected left parenthesis in where clause. + SQL_ERR_UNEXPECTED_RPAREN, ///< 57 = Unexpected right parenthesis in where clause. + SQL_ERR_UNMATCHED_RPAREN, ///< 58 = Right paren in where clause not matched with a left paren. + SQL_ERR_NON_HEX_CHAR_IN_BINARY, ///< 59 = Invalid non-hex character in binary value. + SQL_ERR_EXPECTING_FROM, ///< 60 = Expecting "FROM" keyword. + SQL_ERR_EXPECTING_WHERE, ///< 61 = Expecting "WHERE" keyword. + SQL_ERR_EXPECTING_SET, ///< 62 = Expecting "SET" keyword. + SQL_ERR_EXPECTING_PERIOD, ///< 63 = Expecting a period after the table name. // IMPORTANT NOTE: If new codes are added, please update gv_SQLParseErrors in fshell.cpp SQL_NUM_ERRORS @@ -1351,6 +1365,8 @@ typedef struct #define NE_SFLM_Q_ILLEGAL_OPERATOR 0xE211 ///< 0xE211 = Illegal operator specified in query expression. #define NE_SFLM_Q_ILLEGAL_COMPARE_RULES 0xE212 ///< 0xE212 = Illegal combination comparison rules specified in query expression. #define NE_SFLM_Q_UNEXPECTED_COLUMN 0xE213 ///< 0xE213 = Not expecting a column name in query expression. +#define NE_SFLM_Q_UNEXPECTED_CONSTANT 0xE214 ///< 0xE214 = Not expecting a constant in query expression. +#define NE_SFLM_Q_UNEXPECTED_BOOLEAN 0xE215 ///< 0xE215 = Not expecting a boolean constant in query expression. // Desc: NICI / Encryption Errors @@ -1450,13 +1466,8 @@ typedef struct F_INDEX_COL_DEF typedef struct F_COLUMN_VALUE { FLMUINT uiColumnNum; ///< Column number. - FLMBYTE* pucColumnValue; ///< Column's value.\ For string data it is a SEN followed by a UTF8 string.\ The SEN - ///< gives the number of characters (as opposed to bytes) in the string.\ For number - ///< data there are two pieces.\ The first byte is a 1 or 0, indicating if the - ///< number is negative (1=negative, 0=positive).\ Following that byte is a - ///< SEN that contains the absolute value of the number.\ Binary data may be anything.\ NOTE: - ///< The pucColumnValue and uiValueLen fields are NOT set for the CREATE TABLE statement. - FLMUINT uiValueLen; ///< Length of column's value.\ Not set for the CREATE TABLE statement. + FLMBYTE* pucColumnValue; ///< Column's value.\ Needs to already be in storage format. + FLMUINT uiValueLen; ///< Length of column's value. F_COLUMN_VALUE * pNext; ///< Next column in linked list. } F_COLUMN_VALUE; @@ -1548,6 +1559,13 @@ flminterface IF_RestoreStatus : public F_Object F_COLUMN_VALUE * pColumnValues, FLMUINT uiNumColumnValues) = 0; + virtual RCODE reportUpdateRow( + eRestoreAction * peAction, + FLMUINT uiTableNum, + FLMUINT64 ui64RowId, + F_COLUMN_VALUE * pColumnValues, + FLMUINT uiNumColumnValues) = 0; + virtual RCODE reportDeleteRow( eRestoreAction * peAction, FLMUINT uiTableNum, diff --git a/sql/src/flaimsys.h b/sql/src/flaimsys.h index f499647..059c815 100644 --- a/sql/src/flaimsys.h +++ b/sql/src/flaimsys.h @@ -1474,16 +1474,17 @@ public: typedef struct KEY_GEN_INFO { - F_TABLE * pTable; - F_INDEX * pIndex; - F_Row * pRow; - FLMBOOL bIsAsia; - FLMBOOL bIsCompound; - FLMBOOL bAddKeys; - FLMBYTE * pucKeyBuf; - FLMBYTE * pucData; - FLMUINT uiDataBufSize; - FLMBOOL bDataBufAllocated; + F_TABLE * pTable; + F_INDEX * pIndex; + F_Row * pRow; + F_COLUMN_VALUE * pColumnValues; + FLMBOOL bIsAsia; + FLMBOOL bIsCompound; + FLMBOOL bAddKeys; + FLMBYTE * pucKeyBuf; + FLMBYTE * pucData; + FLMUINT uiDataBufSize; + FLMBOOL bDataBufAllocated; } KEY_GEN_INFO; /***************************************************************************** @@ -1909,11 +1910,26 @@ public: FLMUINT uiTableNum, F_COLUMN_VALUE * pColumnValues); + RCODE updateRow( + FLMUINT uiTableNum, + F_Row ** ppRow, + F_COLUMN_VALUE * pColumnValues); + RCODE deleteRow( FLMUINT uiTableNum, FLMUINT64 ui64RowId, FLMBOOL bLogDelete); + RCODE deleteSelectedRows( + FLMUINT uiTableNum, + SQLQuery * pSqlQuery); + + RCODE updateSelectedRows( + FLMUINT uiTableNum, + SQLQuery * pSqlQuery, + COLUMN_SET * pFirstColumnSet, + FLMUINT uiNumColumnsToSet); + RCODE createTable( FLMUINT uiTableNum, const char * pszTableName, @@ -2180,15 +2196,17 @@ private: FLMUINT uiKeyLen); RCODE buildKeys( - F_INDEX * pIndex, - F_TABLE * pTable, - F_Row * pOldRow, - F_Row * pNewRow); + F_INDEX * pIndex, + F_TABLE * pTable, + F_Row * pRow, + FLMBOOL bAddKeys, + F_COLUMN_VALUE * pColumnValues); RCODE updateIndexKeys( - FLMUINT uiTableNum, - F_Row * pOldRow, - F_Row * pNewRow); + FLMUINT uiTableNum, + F_Row * pRow, + FLMBOOL bAddKeys, + F_COLUMN_VALUE * pColumnValues); void indexingAfterCommit( void); diff --git a/sql/src/frow.cpp b/sql/src/frow.cpp index 0c59933..13be9f6 100644 --- a/sql/src/frow.cpp +++ b/sql/src/frow.cpp @@ -3206,7 +3206,7 @@ RCODE F_Row::setNumber64( { case SFLM_NUMBER_TYPE: { - if (ui64Value <= 0x7F) + if (!bNeg && ui64Value <= 0x7F) { if (RC_BAD( rc = allocColumnDataSpace( pDb, uiColumnNum, 1, FALSE))) { @@ -3592,6 +3592,39 @@ void F_Row::getDataLen( } } +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_Row::setValue( + F_Db * pDb, + FLMUINT uiColumnNum, + const FLMBYTE * pucValue, + FLMUINT uiValueLen) +{ + RCODE rc = NE_SFLM_OK; + + if (RC_BAD( rc = allocColumnDataSpace( pDb, uiColumnNum, uiValueLen, FALSE))) + { + goto Exit; + } + if (uiValueLen) + { + FLMBYTE * pucTmp = getColumnDataPtr( uiColumnNum); + + f_memcpy( pucTmp, pucValue, uiValueLen); + } + setRowDirty( pDb, FALSE); + +Exit: + + if( RC_BAD( rc)) + { + pDb->setMustAbortTrans( rc); + } + + return( rc); +} + /***************************************************************************** Desc: ******************************************************************************/ diff --git a/sql/src/fslfileu.cpp b/sql/src/fslfileu.cpp index b0c0ccf..da778e0 100644 --- a/sql/src/fslfileu.cpp +++ b/sql/src/fslfileu.cpp @@ -211,7 +211,7 @@ RCODE F_Db::indexSetOfRows( goto Exit; } - if (RC_BAD( rc = buildKeys( pIndex, pTable, NULL, pRow))) + if (RC_BAD( rc = buildKeys( pIndex, pTable, pRow, TRUE, NULL))) { goto Exit; } diff --git a/sql/src/insertrow.cpp b/sql/src/insertrow.cpp index e9e25ef..8a13074 100644 --- a/sql/src/insertrow.cpp +++ b/sql/src/insertrow.cpp @@ -34,11 +34,6 @@ RCODE F_Db::insertRow( { RCODE rc = NE_SFLM_OK; F_Row * pRow = NULL; - const FLMBYTE * pucValue; - const FLMBYTE * pucEnd; - FLMUINT64 ui64Num; - FLMUINT uiNumChars; - FLMBOOL bNeg; F_COLUMN_VALUE * pColumnValue; F_TABLE * pTable; F_COLUMN * pColumn; @@ -76,60 +71,51 @@ RCODE F_Db::insertRow( { continue; } + + // Make sure the data does not exceed the maximum if it is string + // or binary. + pColumn = m_pDict->getColumn( pTable, pColumnValue->uiColumnNum); - switch (pColumn->eDataTyp) + if (pColumn->uiMaxLen) { - case SFLM_STRING_TYPE: - pucValue = (const FLMBYTE *)pColumnValue->pucColumnValue; - pucEnd = pucValue + pColumnValue->uiValueLen; - if (RC_BAD( rc = f_decodeSEN( &pucValue, pucEnd, &uiNumChars))) - { - goto Exit; - } - if (RC_BAD( rc = pRow->setUTF8( this, - pColumnValue->uiColumnNum, - (const char *)pucValue, - (FLMUINT)(pucEnd - pucValue), - uiNumChars))) - { - goto Exit; - } - break; - case SFLM_NUMBER_TYPE: - pucValue = (const FLMBYTE *)pColumnValue->pucColumnValue; - pucEnd = pucValue + pColumnValue->uiValueLen; + if (pColumn->eDataTyp == SFLM_STRING_TYPE) + { + FLMUINT uiNumChars; + const FLMBYTE * pucData = (const FLMBYTE *)pColumnValue->pucColumnValue; + const FLMBYTE * pucEnd = pucData + pColumnValue->uiValueLen; - bNeg = (FLMBOOL)(*pucValue ? (FLMBOOL)TRUE : (FLMBOOL)FALSE); - pucValue++; + // Number of characters is the first part of the value - if (RC_BAD( rc = f_decodeSEN64( &pucValue, pucEnd, &ui64Num))) + if (RC_BAD( rc = f_decodeSEN( &pucData, pucEnd, &uiNumChars))) { goto Exit; } - if (RC_BAD( rc = pRow->setNumber64( this, - pColumnValue->uiColumnNum, ui64Num, bNeg))) + if (pColumnValue->uiValueLen > uiNumChars) { + rc = RC_SET( NE_SFLM_STRING_TOO_LONG); goto Exit; } - break; - case SFLM_BINARY_TYPE: - if (RC_BAD( rc = pRow->setBinary( this, - pColumnValue->uiColumnNum, - (const void *)(pColumnValue->pucColumnValue), - pColumnValue->uiValueLen))) + } + else if (pColumn->eDataTyp == SFLM_BINARY_TYPE) + { + if (pColumnValue->uiValueLen > pColumn->uiMaxLen) { + rc = RC_SET( NE_SFLM_BINARY_TOO_LONG); goto Exit; } - break; - default: - flmAssert( 0); - break; + } + } + if (RC_BAD( rc = pRow->setValue( this, pColumnValue->uiColumnNum, + pColumnValue->pucColumnValue, + pColumnValue->uiValueLen))) + { + goto Exit; } } // Do whatever indexing needs to be done. - if (RC_BAD( rc = updateIndexKeys( uiTableNum, NULL, pRow))) + if (RC_BAD( rc = updateIndexKeys( uiTableNum, pRow, TRUE, NULL))) { goto Exit; } @@ -285,7 +271,7 @@ RCODE SQLStatement::processInsertRow( void) // Get the column name if (RC_BAD( rc = getName( szColumnName, sizeof( szColumnName), - &uiColumnNameLen))) + &uiColumnNameLen, &uiTokenLineOffset))) { goto Exit; } @@ -297,7 +283,7 @@ RCODE SQLStatement::processInsertRow( void) if ((pColumn = m_pDb->m_pDict->findColumn( pTable, szColumnName)) == NULL) { setErrInfo( m_uiCurrLineNum, - m_uiCurrLineOffset, + uiTokenLineOffset, SQL_ERR_UNDEFINED_COLUMN, m_uiCurrLineFilePos, m_uiCurrLineBytes); diff --git a/sql/src/kybldkey.cpp b/sql/src/kybldkey.cpp index 59ec3b4..4db1cd5 100644 --- a/sql/src/kybldkey.cpp +++ b/sql/src/kybldkey.cpp @@ -446,8 +446,8 @@ FSTATIC RCODE flmAddNonTextKeyPiece( break; case SQL_BINARY_VAL: - pucFromBuf = pFromValue->val.pucBuf; - uiFromBufLen = pFromValue->uiDataLen; + pucFromBuf = pFromValue->val.bin.pucValue; + uiFromBufLen = pFromValue->val.bin.uiByteLen; if (!bInclFrom) { @@ -584,8 +584,8 @@ FSTATIC RCODE flmAddNonTextKeyPiece( break; case SQL_BINARY_VAL: - pucUntilBuf = pUntilValue->val.pucBuf; - uiUntilBufLen = pUntilValue->uiDataLen; + pucUntilBuf = pUntilValue->val.bin.pucValue; + uiUntilBufLen = pUntilValue->val.bin.uiByteLen; if (!bInclUntil) { @@ -1353,8 +1353,8 @@ FSTATIC RCODE flmAddTextKeyPiece( case SQL_MATCH_OP: flmAssert( pPred->pFromValue->eValType == SQL_UTF8_VAL); - pucFromUTF8Buf = pPred->pFromValue->val.pucBuf; - uiFromBufLen = pPred->pFromValue->uiDataLen; + pucFromUTF8Buf = pPred->pFromValue->val.str.pszStr; + uiFromBufLen = pPred->pFromValue->val.str.uiByteLen; uiCompareRules = pIcd->uiCompareRules; if (RC_BAD( rc = flmUTF8FindWildcard( pucFromUTF8Buf, &uiWildcardPos, @@ -1450,8 +1450,8 @@ FSTATIC RCODE flmAddTextKeyPiece( if (pPred->pFromValue) { flmAssert( pPred->pFromValue->eValType == SQL_UTF8_VAL); - pucFromUTF8Buf = pPred->pFromValue->val.pucBuf; - uiFromBufLen = pPred->pFromValue->uiDataLen; + pucFromUTF8Buf = pPred->pFromValue->val.str.pszStr; + uiFromBufLen = pPred->pFromValue->val.str.uiByteLen; } else { @@ -1463,8 +1463,8 @@ FSTATIC RCODE flmAddTextKeyPiece( if (pPred->pUntilValue) { flmAssert( pPred->pUntilValue->eValType == SQL_UTF8_VAL); - pucUntilUTF8Buf = pPred->pUntilValue->val.pucBuf; - uiUntilBufLen = pPred->pUntilValue->uiDataLen; + pucUntilUTF8Buf = pPred->pUntilValue->val.str.pszStr; + uiUntilBufLen = pPred->pUntilValue->val.str.uiByteLen; } else { @@ -1487,8 +1487,8 @@ FSTATIC RCODE flmAddTextKeyPiece( if (pPred->pUntilValue) { flmAssert( pPred->pUntilValue->eValType == SQL_UTF8_VAL); - pucFromUTF8Buf = pPred->pUntilValue->val.pucBuf; - uiFromBufLen = pPred->pUntilValue->uiDataLen; + pucFromUTF8Buf = pPred->pUntilValue->val.str.pszStr; + uiFromBufLen = pPred->pUntilValue->val.str.uiByteLen; } else { @@ -1501,8 +1501,8 @@ FSTATIC RCODE flmAddTextKeyPiece( if (pPred->pFromValue) { flmAssert( pPred->pFromValue->eValType == SQL_UTF8_VAL); - pucUntilUTF8Buf = pPred->pFromValue->val.pucBuf; - uiUntilBufLen = pPred->pFromValue->uiDataLen; + pucUntilUTF8Buf = pPred->pFromValue->val.str.pszStr; + uiUntilBufLen = pPred->pFromValue->val.str.uiByteLen; } else { diff --git a/sql/src/kybuild.cpp b/sql/src/kybuild.cpp index 6fc4e48..abcbde2 100644 --- a/sql/src/kybuild.cpp +++ b/sql/src/kybuild.cpp @@ -36,6 +36,25 @@ FSTATIC RCODE kyAddRowIdToKey( FLMUINT uiIDBufSize, FLMUINT * puiIDLen); +FSTATIC RCODE getColumnIStream( + F_Db * pDb, + F_Row * pRow, + F_COLUMN_VALUE * pColumnValues, + FLMUINT uiColumnNum, + FLMBOOL * pbIsNull, + F_BufferIStream * pBufferIStream, + eDataType * peDataType, + FLMUINT * puiDataLength); + +FSTATIC RCODE getColumnTextIStream( + F_Db * pDb, + F_Row * pRow, + F_COLUMN_VALUE * pColumnValues, + FLMUINT uiColumnNum, + FLMBOOL * pbIsNull, + F_BufferIStream * pBufferIStream, + FLMUINT * puiNumChars); + /**************************************************************************** Desc: Append row ID to the key buffer. ****************************************************************************/ @@ -313,6 +332,118 @@ Exit: return( rc); } +/***************************************************************************** +Desc: +******************************************************************************/ +FSTATIC RCODE getColumnIStream( + F_Db * pDb, + F_Row * pRow, + F_COLUMN_VALUE * pColumnValues, + FLMUINT uiColumnNum, + FLMBOOL * pbIsNull, + F_BufferIStream * pBufferIStream, + eDataType * peDataType, + FLMUINT * puiDataLength) +{ + RCODE rc = NE_SFLM_OK; + F_TABLE * pTable; + F_COLUMN * pColumn; + F_COLUMN_VALUE * pColumnValue; + + // See if there is a column data item + + pColumnValue = pColumnValues; + while (pColumnValue && pColumnValue->uiColumnNum != uiColumnNum) + { + pColumnValue = pColumnValue->pNext; + } + + if (!pColumnValue) + { + rc = pRow->getIStream( pDb, uiColumnNum, pbIsNull, pBufferIStream, + peDataType, puiDataLength); + goto Exit; + } + + *pbIsNull = FALSE; + if (!pColumnValue->uiValueLen) + { + *pbIsNull = TRUE; + goto Exit; + } + if (puiDataLength) + { + *puiDataLength = pColumnValue->uiValueLen; + } + if (peDataType) + { + pTable = pDb->getDict()->getTable( pRow->getTableNum()); + pColumn = pDb->getDict()->getColumn( pTable, uiColumnNum); + *peDataType = pColumn->eDataTyp; + } + if (RC_BAD( rc = pBufferIStream->open( (const char *)pColumnValue->pucColumnValue, + pColumnValue->uiValueLen, NULL))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +FSTATIC RCODE getColumnTextIStream( + F_Db * pDb, + F_Row * pRow, + F_COLUMN_VALUE * pColumnValues, + FLMUINT uiColumnNum, + FLMBOOL * pbIsNull, + F_BufferIStream * pBufferIStream, + FLMUINT * puiNumChars) +{ + RCODE rc = NE_SFLM_OK; + eDataType eDataTyp; + FLMUINT uiDataLength; + + *puiNumChars = 0; + + if( RC_BAD( rc = getColumnIStream( pDb, pRow, pColumnValues, + uiColumnNum, pbIsNull, + pBufferIStream, &eDataTyp, &uiDataLength))) + { + goto Exit; + } + + if (eDataTyp != SFLM_STRING_TYPE) + { + rc = RC_SET_AND_ASSERT( NE_SFLM_BAD_DATA_TYPE); + goto Exit; + } + + if (*pbIsNull) + { + goto Exit; + } + + // Skip the leading SEN so that the stream is positioned to + // read raw utf8. + + if (pBufferIStream->remainingSize()) + { + if (RC_BAD( rc = f_readSEN( pBufferIStream, puiNumChars))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + /**************************************************************************** Desc: Generate the keys for a text component. ****************************************************************************/ @@ -346,9 +477,11 @@ RCODE F_Db::genTextKeyComponents( uiKeyLen += 2; uiSaveKeyLen = uiKeyLen; - - if (RC_BAD( rc = m_keyGenInfo.pRow->getTextIStream( this, pIcd->uiColumnNum, - &bIsNull, &columnBufferIStream, &uiNumChars))) + + if (RC_BAD( rc = getColumnTextIStream( this, m_keyGenInfo.pRow, + m_keyGenInfo.pColumnValues, + pIcd->uiColumnNum, &bIsNull, + &columnBufferIStream, &uiNumChars))) { goto Exit; } @@ -646,7 +779,9 @@ RCODE F_Db::genOtherKeyComponent( } else { - if (RC_BAD( rc = m_keyGenInfo.pRow->getIStream( this, + if (RC_BAD( rc = getColumnIStream( this, + m_keyGenInfo.pRow, + m_keyGenInfo.pColumnValues, pIcd->uiColumnNum, &bIsNull, &columnBufferIStream, &eDataTyp, &uiDataLength))) @@ -757,10 +892,11 @@ Exit: Desc: Build all keys from combinations of CDLs. Add keys to KREF table. ****************************************************************************/ RCODE F_Db::buildKeys( - F_INDEX * pIndex, - F_TABLE * pTable, - F_Row * pOldRow, - F_Row * pNewRow) + F_INDEX * pIndex, + F_TABLE * pTable, + F_Row * pRow, + FLMBOOL bAddKeys, + F_COLUMN_VALUE * pColumnValues) { RCODE rc = NE_SFLM_OK; FLMBYTE ucDataBuf [STACK_DATA_BUF_SIZE]; @@ -784,23 +920,27 @@ RCODE F_Db::buildKeys( m_keyGenInfo.uiDataBufSize = sizeof( ucDataBuf); m_keyGenInfo.bDataBufAllocated = FALSE; - // Build the keys for the old row. + // Build the keys for the row. - if (pOldRow) + m_keyGenInfo.pRow = pRow; + m_keyGenInfo.pColumnValues = NULL; + m_keyGenInfo.bAddKeys = bAddKeys; + if (RC_BAD( rc = buildKeys( pIndex->pKeyIcds, 1, 0))) { - m_keyGenInfo.pRow = pOldRow; - m_keyGenInfo.bAddKeys = FALSE; - if (RC_BAD( rc = buildKeys( pIndex->pKeyIcds, 1, 0))) - { - goto Exit; - } + goto Exit; } + + // Add the new keys, if this is an update operation. + // pColumnValues will be non-NULL for update operations. - // Build all of the keys for the new row - - if (pNewRow) + if (pColumnValues) { - m_keyGenInfo.pRow = pNewRow; + // The bAddKeys that was passed in better have been FALSE - to delete + // the old keys, and pRow should have been pointing to the old + // row before it was modified. + + flmAssert( !bAddKeys); + m_keyGenInfo.pColumnValues = pColumnValues; m_keyGenInfo.bAddKeys = TRUE; if (RC_BAD( rc = buildKeys( pIndex->pKeyIcds, 1, 0))) { @@ -835,32 +975,22 @@ Desc: Routine that is called when inserting, modifying, or removing a row from a table. ****************************************************************************/ RCODE F_Db::updateIndexKeys( - FLMUINT uiTableNum, - F_Row * pOldRow, - F_Row * pNewRow) + FLMUINT uiTableNum, + F_Row * pRow, + FLMBOOL bAddKeys, + F_COLUMN_VALUE * pColumnValues) { - RCODE rc = NE_SFLM_OK; - F_TABLE * pTable = m_pDict->getTable( uiTableNum); - F_INDEX * pIndex; - FLMUINT uiIndexNum; - FLMUINT64 ui64RowId; + RCODE rc = NE_SFLM_OK; + F_TABLE * pTable = m_pDict->getTable( uiTableNum); + F_INDEX * pIndex; + FLMUINT uiIndexNum; + FLMUINT uiColumnNum; + ICD * pIcd; + FLMUINT uiLoop; + FLMUINT64 ui64RowId; + F_COLUMN_VALUE * pColumnValue; - if (pOldRow) - { - ui64RowId = pOldRow->getRowId(); - - // New row, if any, should have same row ID as old row. - - flmAssert( !pNewRow || pNewRow->getRowId() == ui64RowId); - } - else - { - flmAssert( pNewRow); - ui64RowId = pNewRow->getRowId(); - } - -//VISIT - Don't call this routine on a modify operation if none of the columns -//that were modified are indexed. The higher level routine can make this check. + ui64RowId = pRow->getRowId(); // Go through each index on this table. @@ -872,11 +1002,58 @@ RCODE F_Db::updateIndexKeys( if (!(pIndex->uiFlags & (IXD_OFFLINE | IXD_SUSPENDED)) || ui64RowId <= pIndex->ui64LastRowIndexed) { - if (RC_BAD( rc = buildKeys( pIndex, pTable, pOldRow, pNewRow))) + FLMBOOL bBuildKeys = TRUE; + + // If we are doing a modify operation, see if any of the columns + // in this index are actually going to be modified. + + if (pColumnValues) { - goto Exit; + bBuildKeys = FALSE; + pColumnValue = pColumnValues; + while (pColumnValue && !bBuildKeys) + { + uiColumnNum = pColumnValue->uiColumnNum; + + // See if it is one of the key columns + + for (pIcd = pIndex->pKeyIcds, uiLoop = 0; + !bBuildKeys && uiLoop < pIndex->uiNumKeyComponents; + uiLoop++, pIcd++) + { + if (pIcd->uiColumnNum == uiColumnNum) + { + bBuildKeys = TRUE; + break; + } + } + + // See if it is one of the data columns + + for (pIcd = pIndex->pDataIcds, uiLoop = 0; + !bBuildKeys && uiLoop < pIndex->uiNumDataComponents; + uiLoop++, pIcd++) + { + if (pIcd->uiColumnNum == uiColumnNum) + { + bBuildKeys = TRUE; + break; + } + } + pColumnValue = pColumnValue->pNext; + } + } + + if (bBuildKeys) + { + if (RC_BAD( rc = buildKeys( pIndex, pTable, pRow, bAddKeys, + pColumnValues))) + { + goto Exit; + } } } + uiIndexNum = pIndex->uiNextIndexNum; } diff --git a/sql/src/opendatabase.cpp b/sql/src/opendatabase.cpp index ac1904c..9665074 100644 --- a/sql/src/opendatabase.cpp +++ b/sql/src/opendatabase.cpp @@ -1972,9 +1972,9 @@ RCODE SQLStatement::processOpenDatabase( void) // Get the database name. - if (RC_BAD( rc = getUTF8String( FALSE, (FLMBYTE *)szDatabaseName, + if (RC_BAD( rc = getUTF8String( FALSE, TRUE, (FLMBYTE *)szDatabaseName, sizeof( szDatabaseName), - &uiDatabaseNameLen))) + &uiDatabaseNameLen, NULL, NULL))) { goto Exit; } @@ -2004,27 +2004,27 @@ RCODE SQLStatement::processOpenDatabase( void) if (f_stricmp( szToken, "data_dir") == 0) { - if (RC_BAD( rc = getUTF8String( TRUE, (FLMBYTE *)szDataDirName, + if (RC_BAD( rc = getUTF8String( TRUE, TRUE, (FLMBYTE *)szDataDirName, sizeof( szDataDirName), - &uiDataDirNameLen))) + &uiDataDirNameLen, NULL, NULL))) { goto Exit; } } else if (f_stricmp( szToken, "data_dir") == 0) { - if (RC_BAD( rc = getUTF8String( TRUE, (FLMBYTE *)szRflDirName, + if (RC_BAD( rc = getUTF8String( TRUE, TRUE, (FLMBYTE *)szRflDirName, sizeof( szRflDirName), - &uiRflDirNameLen))) + &uiRflDirNameLen, NULL, NULL))) { goto Exit; } } else if (f_stricmp( szToken, "password") == 0) { - if (RC_BAD( rc = getUTF8String( TRUE, (FLMBYTE *)szPassword, + if (RC_BAD( rc = getUTF8String( TRUE, TRUE, (FLMBYTE *)szPassword, sizeof( szPassword), - &uiPasswordLen))) + &uiPasswordLen, NULL, NULL))) { goto Exit; } diff --git a/sql/src/rfl.cpp b/sql/src/rfl.cpp index 26cbd53..9259fc9 100644 --- a/sql/src/rfl.cpp +++ b/sql/src/rfl.cpp @@ -5267,24 +5267,24 @@ Exit: /******************************************************************** Desc: *********************************************************************/ -RCODE F_Rfl::recovInsertRow( +RCODE F_Rfl::getColumnValues( F_Db * pDb, - const FLMBYTE * pucPacketBody, - FLMUINT uiPacketBodyLen, - eRestoreAction * peAction) + F_TABLE * pTable, + F_COLUMN_VALUE ** ppFirstColValue, + FLMUINT * puiNumColumnValues) { RCODE rc = NE_SFLM_OK; - FLMUINT uiTableNum; FLMUINT uiPacketType; F_COLUMN_VALUE * pColumnValue; - F_COLUMN_VALUE * pFirstColValue; - F_COLUMN_VALUE * pLastColValue; - FLMUINT uiNumColumnValues; - const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; + F_COLUMN_VALUE * pFirstColValue = NULL; + F_COLUMN_VALUE * pLastColValue = NULL; + FLMUINT uiNumColumnValues = 0; + const FLMBYTE * pucPacketBody = NULL; + const FLMBYTE * pucEnd = NULL; + FLMUINT uiPacketBodyLen; FLMUINT uiIVLen; FLMBOOL bHitEnd; FLMUINT uiColumnNum; - F_TABLE * pTable; F_COLUMN * pColumn; F_ENCDEF * pTableEncDef; F_ENCDEF * pEncDef; @@ -5294,26 +5294,9 @@ RCODE F_Rfl::recovInsertRow( FLMUINT uiEncLen; FLMBYTE ucIV [16]; - // Get the table number from the packet - - if (RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiTableNum))) - { - goto Exit; - } - if ((pTable = pDb->m_pDict->getTable( uiTableNum)) == NULL || - pTable->bSystemTable) - { - rc = RC_SET( NE_SFLM_BAD_RFL_PACKET); - goto Exit; - } pTableEncDef = (pTable->lfInfo.uiEncDefNum) ? pDb->m_pDict->getEncDef( pTable->lfInfo.uiEncDefNum) : (F_ENCDEF *)NULL; - - pFirstColValue = NULL; - pLastColValue = NULL; - pucPacketBody = pucEnd = NULL; - uiNumColumnValues = 0; for (;;) { @@ -5532,6 +5515,49 @@ RCODE F_Rfl::recovInsertRow( } } } +Exit: + *ppFirstColValue = pFirstColValue; + *puiNumColumnValues = uiNumColumnValues; + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::recovInsertRow( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction) +{ + RCODE rc = NE_SFLM_OK; + FLMUINT uiTableNum; + F_TABLE * pTable; + F_COLUMN_VALUE * pFirstColValue; + FLMUINT uiNumColumnValues; + const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; + + // Get the table number from the packet + + if (RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiTableNum))) + { + goto Exit; + } + if ((pTable = pDb->m_pDict->getTable( uiTableNum)) == NULL || + pTable->bSystemTable) + { + rc = RC_SET( NE_SFLM_BAD_RFL_PACKET); + goto Exit; + } + + // Get the column values. + + if (RC_BAD( rc = getColumnValues( pDb, pTable, &pFirstColValue, + &uiNumColumnValues))) + { + goto Exit; + } if (m_pRestoreStatus) { @@ -5561,6 +5587,167 @@ Exit: return( rc); } +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::logUpdateRow( + F_Db * pDb, + FLMUINT uiTableNum, + FLMUINT64 ui64RowId, + F_COLUMN_VALUE * pColumnValues) +{ + RCODE rc = NE_SFLM_OK; + FLMUINT uiPacketBodyLen; + FLMBYTE * pucPacketBody; + FLMBYTE * pucPacketStart; + + flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); + + // Do nothing if logging is disabled. + + if( !isLoggingEnabled()) + { + goto Exit; + } + + // Better be in the middle of a transaction. + + flmAssert( m_ui64CurrTransID); + + // Increment the operation count + + m_uiOperCount++; + + // Make sure we have space in the RFL buffer for a complete packet. NOTE: + // this is calculating the maximum packet body length that would be needed. + + if( !haveBuffSpace( FLM_MAX_SEN_LEN * 2 + RFL_PACKET_OVERHEAD)) + { + if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = pucPacketStart = getPacketBodyPtr(); + + // Output the table number + + f_encodeSEN( uiTableNum, &pucPacketBody); + + // Output the row ID + + f_encodeSEN( ui64RowId, &pucPacketBody); + + // Finish the packet - calculate the actual packet body length. + + uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); + flmAssert( uiPacketBodyLen <= FLM_MAX_SEN_LEN * 2); + + if (RC_BAD( rc = finishPacket( pDb, RFL_UPDATE_ROW_PACKET, + uiPacketBodyLen, FALSE))) + { + goto Exit; + } + + // Now output the column data + + if (RC_BAD( rc = logColumnValues( pDb, uiTableNum, pColumnValues))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::recovUpdateRow( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction) +{ + RCODE rc = NE_SFLM_OK; + FLMUINT uiTableNum; + F_TABLE * pTable; + FLMUINT64 ui64RowId; + F_Row * pRow = NULL; + F_COLUMN_VALUE * pFirstColValue; + FLMUINT uiNumColumnValues; + const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; + + // Get the table number from the packet + + if (RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiTableNum))) + { + goto Exit; + } + if ((pTable = pDb->m_pDict->getTable( uiTableNum)) == NULL || + pTable->bSystemTable) + { + rc = RC_SET( NE_SFLM_BAD_RFL_PACKET); + goto Exit; + } + + // Get the row ID from the packet + + if (RC_BAD( rc = f_decodeSEN64( &pucPacketBody, pucEnd, &ui64RowId))) + { + goto Exit; + } + + // Get the column values. + + if (RC_BAD( rc = getColumnValues( pDb, pTable, &pFirstColValue, + &uiNumColumnValues))) + { + goto Exit; + } + if (m_pRestoreStatus) + { + if( RC_BAD( rc = m_pRestoreStatus->reportUpdateRow( + peAction, uiTableNum, ui64RowId, pFirstColValue, uiNumColumnValues))) + { + goto Exit; + } + + if( *peAction == SFLM_RESTORE_ACTION_STOP) + { + m_ui64CurrTransID = 0; + goto Exit; + } + } + + if (RC_BAD( rc = gv_SFlmSysData.pRowCacheMgr->retrieveRow( pDb, + uiTableNum, ui64RowId, &pRow))) + { + goto Exit; + } + + if( RC_BAD( rc = pDb->updateRow( uiTableNum, &pRow, pFirstColValue))) + { + goto Exit; + } + +Exit: + + m_tmpPool.poolReset( NULL); + + if (pRow) + { + pRow->ReleaseRow(); + } + + m_ui64CurrTransID = 0; + return( rc); +} + /******************************************************************** Desc: *********************************************************************/ @@ -7109,6 +7296,23 @@ Finish_Transaction: break; } + case RFL_UPDATE_ROW_PACKET: + { + if( RC_BAD( rc = recovUpdateRow( pDb, + pucPacketBody, uiPacketBodyLen, &eAction))) + { + goto Exit; + } + + if( eAction == SFLM_RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + + break; + } + case RFL_DELETE_ROW_PACKET: { if( RC_BAD( rc = recovDeleteRow( pDb, diff --git a/sql/src/rfl.h b/sql/src/rfl.h index 3152422..655e675 100644 --- a/sql/src/rfl.h +++ b/sql/src/rfl.h @@ -54,6 +54,7 @@ class IXKeyCompare; #define RFL_DELETE_ROW_PACKET 21 #define RFL_DROP_INDEX_PACKET 22 #define RFL_DROP_TABLE_PACKET 23 +#define RFL_UPDATE_ROW_PACKET 24 #define RFL_PACKET_TYPE_MASK 0x7F @@ -289,11 +290,29 @@ public: FLMUINT uiTableNum, F_COLUMN_VALUE * pColumnValues); + RCODE getColumnValues( + F_Db * pDb, + F_TABLE * pTable, + F_COLUMN_VALUE ** ppFirstColValue, + FLMUINT * puiNumColumnValues); + RCODE recovInsertRow( F_Db * pDb, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction); + + RCODE logUpdateRow( + F_Db * pDb, + FLMUINT uiTableNum, + FLMUINT64 ui64RowId, + F_COLUMN_VALUE * pColumnValues); + + RCODE recovUpdateRow( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction); RCODE logDeleteRow( F_Db * pDb, diff --git a/sql/src/sqleval.cpp b/sql/src/sqleval.cpp index b664ac4..96ccb97 100644 --- a/sql/src/sqleval.cpp +++ b/sql/src/sqleval.cpp @@ -212,8 +212,8 @@ FSTATIC RCODE sqlCompareText( if( !(pLValue->uiFlags & SQL_VAL_IS_STREAM)) { - if (RC_BAD( rc = bufferLStream.open( (const char *)pLValue->val.pucBuf, - pLValue->uiDataLen))) + if (RC_BAD( rc = bufferLStream.open( (const char *)pLValue->val.str.pszStr, + pLValue->val.str.uiByteLen))) { goto Exit; } @@ -227,8 +227,8 @@ FSTATIC RCODE sqlCompareText( if( !(pRValue->uiFlags & SQL_VAL_IS_STREAM)) { - if( RC_BAD( rc = bufferRStream.open( (const char *)pRValue->val.pucBuf, - pRValue->uiDataLen))) + if( RC_BAD( rc = bufferRStream.open( (const char *)pRValue->val.str.pszStr, + pRValue->val.str.uiByteLen))) { goto Exit; } @@ -288,8 +288,8 @@ FSTATIC RCODE sqlCompareBinary( if( !(pLValue->uiFlags & SQL_VAL_IS_STREAM)) { - if (RC_BAD( rc = bufferLStream.open( (const char *)pLValue->val.pucBuf, - pLValue->uiDataLen))) + if (RC_BAD( rc = bufferLStream.open( (const char *)pLValue->val.str.pszStr, + pLValue->val.str.uiByteLen))) { goto Exit; } @@ -303,8 +303,8 @@ FSTATIC RCODE sqlCompareBinary( if( !(pRValue->uiFlags & SQL_VAL_IS_STREAM)) { - if( RC_BAD( rc = bufferRStream.open( (const char *)pRValue->val.pucBuf, - pRValue->uiDataLen))) + if( RC_BAD( rc = bufferRStream.open( (const char *)pRValue->val.str.pszStr, + pRValue->val.str.uiByteLen))) { goto Exit; } diff --git a/sql/src/sqloptimize.cpp b/sql/src/sqloptimize.cpp index c656ee8..9283194 100644 --- a/sql/src/sqloptimize.cpp +++ b/sql/src/sqloptimize.cpp @@ -2397,7 +2397,7 @@ RCODE SQLQuery::optimizeTable( SQL_NODE * pOperand; SQL_PRED * pPred; void * pvMark = m_pool.poolMark(); - SQLTableCursor * pSQLTableCursor = NULL; + SQLTableCursor * pSQLTableCursor = NULL; // This routine should not be called if the table has already been // marked to do a table scan. @@ -2663,16 +2663,13 @@ RCODE SQLQuery::optimize( void) // Make sure we have a completed expression - if (m_pCurrParseState) + if (!criteriaIsComplete()) + { + rc = RC_SET( NE_SFLM_Q_INCOMPLETE_QUERY_EXPR); + goto Exit; + } + else if (m_pCurrParseState) { - if (m_pCurrParseState->pPrev || - m_pCurrParseState->uiNestLevel || - (m_pCurrParseState->pLastNode && - m_pCurrParseState->pLastNode->eNodeType == FLM_OPERATOR_NODE)) - { - rc = RC_SET( NE_SFLM_Q_INCOMPLETE_QUERY_EXPR); - goto Exit; - } m_pQuery = m_pCurrParseState->pRootNode; } diff --git a/sql/src/sqlparser.cpp b/sql/src/sqlparser.cpp deleted file mode 100644 index ae9e609..0000000 --- a/sql/src/sqlparser.cpp +++ /dev/null @@ -1,577 +0,0 @@ -//------------------------------------------------------------------------- -// Desc: Parse SQL -// Tabs: 3 -// -// Copyright (c) 2006 Novell, Inc. All Rights Reserved. -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of version 2 of the GNU General Public -// License as published by the Free Software Foundation. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, contact Novell, Inc. -// -// To contact Novell about this file by physical or electronic mail, -// you may find current contact information at www.novell.com -// -// $Id$ -//------------------------------------------------------------------------- - -#include "flaimsys.h" - -// Local function prototypes - -FSTATIC void sqlUnlinkFromParent( - SQL_NODE * pSQLNode); - -FSTATIC void sqlLinkLastChild( - SQL_NODE * pParent, - SQL_NODE * pChild); - -static FLMUINT uiSQLOpPrecedenceTable[ SQL_NEG_OP - SQL_AND_OP + 1] = -{ - 2, // SQL_AND_OP - 1, // SQL_OR_OP - 10, // SQL_NOT_OP - 6, // SQL_EQ_OP - 6, // SQL_NE_OP - 6, // SQL_APPROX_EQ_OP - 7, // SQL_LT_OP - 7, // SQL_LE_OP - 7, // SQL_GT_OP - 7, // SQL_GE_OP - 5, // SQL_BITAND_OP - 3, // SQL_BITOR_OP - 4, // SQL_BITXOR_OP - 9, // SQL_MULT_OP - 9, // SQL_DIV_OP - 9, // SQL_MOD_OP - 8, // SQL_PLUS_OP - 8, // SQL_MINUS_OP - 10 // SQL_NEG_OP -}; - -FINLINE FLMUINT getSQLOpPrecedence( - eSQLQueryOperators eOperator) -{ - return( uiSQLOpPrecedenceTable [eOperator - SQL_AND_OP]); -} - -//------------------------------------------------------------------------- -// Desc: Constructor -//------------------------------------------------------------------------- -SQLQuery::SQLQuery() -{ - m_uiLanguage = FLM_US_LANG; - m_pool.poolInit( 1024); - m_pFirstSubQuery = NULL; - m_pLastSubQuery = NULL; - m_pFirstTable = NULL; - m_pLastTable = NULL; - m_bOptimized = FALSE; - m_bScan = FALSE; - m_bScanIndex = FALSE; - m_bEmpty = FALSE; - m_uiIndexNum = 0; - m_bIndexSet = FALSE; - m_pQuery = NULL; - m_pDatabase = NULL; - m_pDb = NULL; - m_pNext = NULL; - m_pPrev = NULL; -} - -//------------------------------------------------------------------------- -// Desc: Destructor -//------------------------------------------------------------------------- -SQLQuery::~SQLQuery() -{ - m_pool.poolFree(); - if (m_pDatabase) - { - m_pDatabase->lockMutex(); - - // Unlink the query from the list off of the F_Database object. - - if (m_pPrev) - { - m_pPrev->m_pNext = m_pNext; - } - else - { - m_pDatabase->m_pFirstSQLQuery = m_pNext; - } - if (m_pNext) - { - m_pNext->m_pPrev = m_pPrev; - } - else - { - m_pDatabase->m_pLastSQLQuery = m_pPrev; - } - m_pDatabase->unlockMutex(); - } -} - -//------------------------------------------------------------------------- -// Desc: Allocate a structure for keeping track of the state of the -// current SQL query. -//------------------------------------------------------------------------- -RCODE SQLQuery::allocParseState( void) -{ - RCODE rc = NE_SFLM_OK; - SQL_PARSE_STATE * pParseState; - - if (!m_pCurrParseState || !m_pCurrParseState->pNext) - { - if (RC_BAD( rc = m_pool.poolCalloc( sizeof( SQL_PARSE_STATE), - (void **)&pParseState))) - { - goto Exit; - } - if ((pParseState->pPrev = m_pCurrParseState) != NULL) - { - m_pCurrParseState->pNext = pParseState; - } - m_pCurrParseState = pParseState; - } - else - { - SQL_PARSE_STATE * pSaveNext; - SQL_PARSE_STATE * pSavePrev; - - m_pCurrParseState = m_pCurrParseState->pNext; - - // Zero out everything except for the prev and next pointers - - pSaveNext = m_pCurrParseState->pNext; - pSavePrev = m_pCurrParseState->pPrev; - f_memset( m_pCurrParseState, 0, sizeof( SQL_PARSE_STATE)); - m_pCurrParseState->pNext = pSaveNext; - m_pCurrParseState->pPrev = pSavePrev; - } - -Exit: - - return( rc); -} - -//------------------------------------------------------------------------- -// Desc: Unlinks a node from its parent and siblings. This routine assumes -// that the test has already been made that the node has a parent. -//------------------------------------------------------------------------- -FSTATIC void sqlUnlinkFromParent( - SQL_NODE * pSQLNode) -{ - flmAssert( pSQLNode->pParent); - if (pSQLNode->pPrevSib) - { - pSQLNode->pPrevSib->pNextSib = pSQLNode->pNextSib; - } - else - { - pSQLNode->pParent->pFirstChild = pSQLNode->pNextSib; - } - if (pSQLNode->pNextSib) - { - pSQLNode->pNextSib->pPrevSib = pSQLNode->pPrevSib; - } - else - { - pSQLNode->pParent->pLastChild = pSQLNode->pPrevSib; - } - - pSQLNode->pParent = NULL; - pSQLNode->pPrevSib = NULL; - pSQLNode->pNextSib = NULL; -} - -//------------------------------------------------------------------------- -// Desc: Links one SQL_NODE as the last child of another. Will unlink the -// child node from any parent it may be linked to. -//------------------------------------------------------------------------- -FSTATIC void sqlLinkLastChild( - SQL_NODE * pParent, - SQL_NODE * pChild - ) -{ - - // If necessary, unlink the child from parent and siblings - - if (pChild->pParent) - { - sqlUnlinkFromParent( pChild); - } - - // Link child as the last child to parent - - pChild->pParent = pParent; - pChild->pNextSib = NULL; - if ((pChild->pPrevSib = pParent->pLastChild) != NULL) - { - pChild->pPrevSib->pNextSib = pChild; - } - else - { - pParent->pFirstChild = pChild; - } - pParent->pLastChild = pChild; -} - -//------------------------------------------------------------------------- -// Desc: Allocate a value node. -//------------------------------------------------------------------------- -RCODE SQLQuery::allocValueNode( - FLMUINT uiValLen, - eSQLValTypes eValType, - SQL_NODE ** ppSQLNode - ) -{ - RCODE rc = NE_SFLM_OK; - SQL_NODE * pSQLNode; - - if (!m_pCurrParseState) - { - if (RC_BAD( rc = allocParseState())) - { - goto Exit; - } - } - - if (m_pCurrParseState->bExpectingLParen) - { - rc = RC_SET( NE_SFLM_Q_EXPECTING_LPAREN); - goto Exit; - } - - if (!expectingOperand()) - { - rc = RC_SET( NE_SFLM_Q_UNEXPECTED_VALUE); - goto Exit; - } - - if (RC_BAD( rc = m_pool.poolCalloc( sizeof( SQL_NODE), - (void **)ppSQLNode))) - { - goto Exit; - } - pSQLNode = *ppSQLNode; - pSQLNode->eNodeType = SQL_VALUE_NODE; - pSQLNode->nd.value.eValType = eValType; - pSQLNode->nd.value.uiDataLen = uiValLen; - pSQLNode->nd.value.uiFlags = SQL_VAL_IS_CONSTANT; - - // For string and binary data, allocate a buffer. - - if (uiValLen && - (eValType == SQL_UTF8_VAL || eValType == SQL_BINARY_VAL)) - { - if (RC_BAD( rc = m_pool.poolAlloc( uiValLen, - (void **)&pSQLNode->nd.value.val.pucBuf))) - { - goto Exit; - } - } - - if (m_pCurrParseState->pRootNode) - { - sqlLinkLastChild( m_pCurrParseState->pCurOperatorNode, pSQLNode); - } - else - { - m_pCurrParseState->pRootNode = pSQLNode; - } - m_pCurrParseState->bExpectingOperator = TRUE; - m_pCurrParseState->pLastNode = pSQLNode; - -Exit: - - return( rc); -} - -//------------------------------------------------------------------------- -// Desc: Add an operator to the query expression -//------------------------------------------------------------------------- -RCODE SQLQuery::addOperator( - eSQLQueryOperators eOperator, - FLMUINT uiCompareRules) -{ - RCODE rc = NE_SFLM_OK; - SQL_NODE * pSQLNode; - SQL_NODE * pParentNode; - - if (!m_pCurrParseState) - { - if (RC_BAD( rc = allocParseState())) - { - goto Exit; - } - } - - // If we are expecting a left paren (for a function), that is - // the only thing that is acceptable at this point. - - if (m_pCurrParseState->bExpectingLParen && eOperator != SQL_LPAREN_OP) - { - rc = RC_SET( NE_SFLM_Q_EXPECTING_LPAREN); - goto Exit; - } - - switch (eOperator) - { - case SQL_LPAREN_OP: - - // If the operator is a left paren, increment the nesting level - - if (expectingOperator()) - { - rc = RC_SET( NE_SFLM_Q_UNEXPECTED_LPAREN); - goto Exit; - } - m_pCurrParseState->uiNestLevel++; - m_pCurrParseState->bExpectingLParen = FALSE; - goto Exit; - - case SQL_RPAREN_OP: - if (expectingOperand()) - { - rc = RC_SET( NE_SFLM_Q_UNEXPECTED_RPAREN); - goto Exit; - } - if (!m_pCurrParseState->uiNestLevel) - { - rc = RC_SET( NE_SFLM_Q_UNMATCHED_RPAREN); - goto Exit; - } - m_pCurrParseState->uiNestLevel--; - - goto Exit; - - case SQL_NEG_OP: - case SQL_NOT_OP: - if (expectingOperator()) - { - rc = RC_SET( NE_SFLM_Q_EXPECTING_OPERATOR); - goto Exit; - } - break; - - default: - - if (expectingOperand()) - { - rc = RC_SET( NE_SFLM_Q_EXPECTING_OPERAND); - goto Exit; - } - if (!isLegalSQLOperator( eOperator)) - { - rc = RC_SET( NE_SFLM_Q_ILLEGAL_OPERATOR); - goto Exit; - } - - break; - } - - // Cannot set both FLM_COMP_COMPRESS_WHITESPACE and FLM_COMP_NO_WHITESPACE - // in comparison rules. Also, cannot set FLM_COMP_IGNORE_LEADING_SPACE or - // FLM_COMP_IGNORE_TRAILING_SPACE with FLM_COMP_NO_WHITESPACE. - - if ((uiCompareRules & FLM_COMP_NO_WHITESPACE) && - (uiCompareRules & (FLM_COMP_COMPRESS_WHITESPACE | - FLM_COMP_IGNORE_LEADING_SPACE | - FLM_COMP_IGNORE_TRAILING_SPACE))) - { - rc = RC_SET_AND_ASSERT( NE_SFLM_Q_ILLEGAL_COMPARE_RULES); - goto Exit; - } - - // Make a QNODE and find a place for it in the query tree - - if (RC_BAD( rc = m_pool.poolCalloc( sizeof( SQL_NODE), - (void **)&pSQLNode))) - { - goto Exit; - } - pSQLNode->eNodeType = SQL_OPERATOR_NODE; - pSQLNode->nd.op.eOperator = eOperator; - pSQLNode->nd.op.uiCompareRules = uiCompareRules; - pSQLNode->uiNestLevel = m_pCurrParseState->uiNestLevel; - - // Go up the stack until an operator whose nest level or precedence is < - // this one's is encountered, then link this one in as the last child - - pParentNode = m_pCurrParseState->pCurOperatorNode; - while (pParentNode && - (pParentNode->uiNestLevel > pSQLNode->uiNestLevel || - (pParentNode->uiNestLevel == pSQLNode->uiNestLevel && - getSQLOpPrecedence( pParentNode->nd.op.eOperator) >= - getSQLOpPrecedence( eOperator)))) - { - pParentNode = pParentNode->pParent; - } - if (!pParentNode) - { - if (m_pCurrParseState->pRootNode) - { - sqlLinkLastChild( pSQLNode, m_pCurrParseState->pRootNode); - } - m_pCurrParseState->pRootNode = pSQLNode; - } - else if (eOperator == SQL_NOT_OP || eOperator == SQL_NEG_OP) - { - - // Need to treat NOT and NEG as if they were operands. - - // Parent better be an operator. - - flmAssert( pParentNode->eNodeType == SQL_OPERATOR_NODE); - -#ifdef FLM_DEBUG - if (pParentNode->nd.op.eOperator == SQL_NEG_OP || - pParentNode->nd.op.eOperator == SQL_NOT_OP) - { - - // Must have no children. - - flmAssert( pParentNode->pFirstChild == NULL); - } - else - { - - // Must only have one or zero children. - - flmAssert( pParentNode->pFirstChild == pParentNode->pLastChild); - } -#endif - - sqlLinkLastChild( pParentNode, pSQLNode); - flmAssert( !m_pCurrParseState->bExpectingOperator); - } - else - { - - // Parent better be an operator. - - flmAssert( pParentNode->eNodeType == SQL_OPERATOR_NODE); - - // Unlink last child of parent node and replace with this - // new node. The parent node better already have the correct - // number of children, or we are not parsing correctly. - - flmAssert( pParentNode->pFirstChild); - if (pParentNode->nd.op.eOperator == SQL_NEG_OP || - pParentNode->nd.op.eOperator == SQL_NOT_OP) - { - - // Better only be one child. - - flmAssert( !pParentNode->pFirstChild->pNextSib); - - sqlLinkLastChild( pSQLNode, pParentNode->pFirstChild); - } - else - { - - // Better only be two child nodes - - flmAssert( pParentNode->pFirstChild->pNextSib == - pParentNode->pLastChild); - sqlLinkLastChild( pSQLNode, pParentNode->pLastChild); - } - sqlLinkLastChild( pParentNode, pSQLNode); - } - - m_pCurrParseState->pCurOperatorNode = pSQLNode; - m_pCurrParseState->bExpectingOperator = FALSE; - m_pCurrParseState->pLastNode = pSQLNode; - -Exit: - - return( rc); -} - -//------------------------------------------------------------------------- -// Desc: Add a column name. -//------------------------------------------------------------------------- -RCODE SQLQuery::addColumn( - FLMUINT uiTableNum, - FLMUINT uiColumnNum) -{ - RCODE rc = NE_SFLM_OK; - SQL_NODE * pSQLNode; - SQL_TABLE * pTable; - - if (!m_pCurrParseState) - { - if (RC_BAD( rc = allocParseState())) - { - goto Exit; - } - } - - // Must be expecting an operand - - if (!expectingOperand()) - { - rc = RC_SET( NE_SFLM_Q_UNEXPECTED_COLUMN); - goto Exit; - } - - // Add or find the table structure for the node. - - pTable = m_pFirstTable; - while (pTable && pTable->uiTableNum != uiTableNum) - { - pTable = pTable->pNext; - } - if (!pTable) - { - if (RC_BAD( rc = m_pool.poolCalloc( sizeof( SQL_TABLE), - (void **)&pTable))) - { - goto Exit; - } - pTable->uiTableNum = uiTableNum; - if ((pTable->pPrev = m_pLastTable) != NULL) - { - m_pLastTable->pNext = pTable; - } - else - { - m_pFirstTable = pTable; - } - m_pLastTable = pTable; - } - - // Allocate a column node - - if (RC_BAD( rc = m_pool.poolCalloc( sizeof( SQL_NODE), - (void **)&pSQLNode))) - { - goto Exit; - } - pSQLNode->eNodeType = SQL_COLUMN_NODE; - pSQLNode->nd.column.pTable = pTable; - pSQLNode->nd.column.uiColumnNum = uiColumnNum; - - if (m_pCurrParseState->pRootNode) - { - sqlLinkLastChild( m_pCurrParseState->pCurOperatorNode, pSQLNode); - } - else - { - m_pCurrParseState->pRootNode = pSQLNode; - } - m_pCurrParseState->bExpectingOperator = TRUE; - m_pCurrParseState->pLastNode = pSQLNode; - -Exit: - - return( rc); -} - diff --git a/sql/src/sqlstatement.cpp b/sql/src/sqlstatement.cpp index 34311a2..9275f3e 100644 --- a/sql/src/sqlstatement.cpp +++ b/sql/src/sqlstatement.cpp @@ -564,19 +564,139 @@ Exit: return( rc); } +//------------------------------------------------------------------------------ +// Desc: Parse a binary value. +//------------------------------------------------------------------------------ +RCODE SQLStatement::getBinaryValue( + F_DynaBuf * pDynaBuf) +{ + RCODE rc = NE_SFLM_OK; + FLMBYTE ucChar; + FLMBOOL bHaveHighNibble = FALSE; + FLMBYTE ucByte = 0; + + for (;;) + { + + // If we hit end of line, just get the next line. + + if ((ucChar = getChar()) == 0) + { + if (RC_BAD( rc = getLine())) + { + goto Exit; + } + continue; + } + + // If we hit the right paren, we are done + + if (ucChar == ')') + { + break; + } + + if (ucChar >= '0' && ucChar <= '9') + { + if (bHaveHighNibble) + { + if (RC_BAD( rc = pDynaBuf->appendByte( ucByte | (FLMBYTE)(ucChar - '0')))) + { + goto Exit; + } + bHaveHighNibble = FALSE; + } + else + { + ucByte = (FLMBYTE)((ucChar - '0') << 4); + bHaveHighNibble = TRUE; + } + } + else if (ucChar >= 'a' && ucChar <= 'f') + { + if (bHaveHighNibble) + { + if (RC_BAD( rc = pDynaBuf->appendByte( ucByte | (FLMBYTE)(ucChar - 'a' + 10)))) + { + goto Exit; + } + bHaveHighNibble = FALSE; + } + else + { + ucByte = (FLMBYTE)((ucChar - 'a' + 10) << 4); + bHaveHighNibble = TRUE; + } + } + else if (ucChar >= 'A' && ucChar <= 'F') + { + if (bHaveHighNibble) + { + if (RC_BAD( rc = pDynaBuf->appendByte( ucByte | (FLMBYTE)(ucChar - 'A' + 10)))) + { + goto Exit; + } + bHaveHighNibble = FALSE; + } + else + { + ucByte = (FLMBYTE)((ucChar - 'A' + 10) << 4); + bHaveHighNibble = TRUE; + } + } + else if (ucChar == ' ' || ucChar == '\t' || ucChar == ',' || + ucChar == ';' || ucChar == ':') + { + + // Some characters we will just ignore - who really cares - lets' + // be forgiving. + + } + else + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset - 1, + SQL_ERR_NON_HEX_CHAR_IN_BINARY, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + + } + } + + // If we have an unprocessed high nibble, add it to the dynamic buffer. + + if (bHaveHighNibble) + { + if (RC_BAD( rc = pDynaBuf->appendByte( ucByte))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + //------------------------------------------------------------------------------ // Desc: Parse a UTF8 string from the input stream. //------------------------------------------------------------------------------ RCODE SQLStatement::getUTF8String( FLMBOOL bMustHaveEqual, + FLMBOOL bStripWildcardEscapes, FLMBYTE * pszStr, FLMUINT uiStrBufSize, - FLMUINT * puiStrLen) + FLMUINT * puiStrLen, + FLMUINT * puiNumChars, + F_DynaBuf * pDynaBuf) { RCODE rc = NE_SFLM_OK; FLMBYTE ucChar; FLMBYTE ucQuoteChar = 0; FLMBOOL bEscaped = FALSE; + FLMUINT uiStrLen = 0; FLMUINT uiNumChars = 0; if (bMustHaveEqual) @@ -590,10 +710,14 @@ RCODE SQLStatement::getUTF8String( { goto Exit; } - - // Always leave room for a null terminating character - - uiStrBufSize--; + + if (!pDynaBuf) + { + + // Always leave room for a null terminating character + + uiStrBufSize--; + } // See if we have a quote character. @@ -648,29 +772,70 @@ RCODE SQLStatement::getUTF8String( ucChar == ',' || ucChar == ')') { } + else if (ucChar == '*') + { + if (!bStripWildcardEscapes) + { + if (!pDynaBuf) + { + if (uiStrLen == uiStrBufSize) + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset - 1, + SQL_ERR_UTF8_STRING_TOO_LARGE, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } + + *pszStr++ = '\\'; + } + else + { + if (RC_BAD( rc = pDynaBuf->appendByte( '\\'))) + { + goto Exit; + } + } + uiStrLen++; + uiNumChars++; + } + } else { setErrInfo( m_uiCurrLineNum, - m_uiCurrLineOffset, + m_uiCurrLineOffset - 1, SQL_ERR_INVALID_ESCAPED_CHARACTER, m_uiCurrLineFilePos, m_uiCurrLineBytes); rc = RC_SET( NE_SFLM_INVALID_SQL); goto Exit; } - - if (uiNumChars == uiStrBufSize) + + if (!pDynaBuf) { - setErrInfo( m_uiCurrLineNum, - m_uiCurrLineOffset, - SQL_ERR_UTF8_STRING_TOO_LARGE, - m_uiCurrLineFilePos, - m_uiCurrLineBytes); - rc = RC_SET( NE_SFLM_INVALID_SQL); - goto Exit; + if (uiStrLen == uiStrBufSize) + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset - 1, + SQL_ERR_UTF8_STRING_TOO_LARGE, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } + + *pszStr++ = ucChar; } - - *pszStr++ = ucChar; + else + { + if (RC_BAD( rc = pDynaBuf->appendByte( ucChar))) + { + goto Exit; + } + } + uiStrLen++; uiNumChars++; } else if (ucChar == '\\') @@ -693,18 +858,29 @@ RCODE SQLStatement::getUTF8String( } break; } - if (uiNumChars == uiStrBufSize) + if (!pDynaBuf) { - setErrInfo( m_uiCurrLineNum, - m_uiCurrLineOffset, - SQL_ERR_UTF8_STRING_TOO_LARGE, - m_uiCurrLineFilePos, - m_uiCurrLineBytes); - rc = RC_SET( NE_SFLM_INVALID_SQL); - goto Exit; + if (uiStrLen == uiStrBufSize) + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset - 1, + SQL_ERR_UTF8_STRING_TOO_LARGE, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } + + *pszStr++ = ucChar; } - - *pszStr++ = ucChar; + else + { + if (RC_BAD( rc = pDynaBuf->appendByte( ucChar))) + { + goto Exit; + } + } + uiStrLen++; uiNumChars++; } @@ -719,20 +895,31 @@ RCODE SQLStatement::getUTF8String( } else { - if (uiNumChars == uiStrBufSize) + if (!pDynaBuf) { - setErrInfo( m_uiCurrLineNum, - m_uiCurrLineOffset, - SQL_ERR_UTF8_STRING_TOO_LARGE, - m_uiCurrLineFilePos, - m_uiCurrLineBytes); - rc = RC_SET( NE_SFLM_INVALID_SQL); - goto Exit; + if (uiStrLen == uiStrBufSize) + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset - 1, + SQL_ERR_UTF8_STRING_TOO_LARGE, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } + + // Save the character to our buffer. + + *pszStr++ = ucChar; } - - // Save the character to our buffer. - - *pszStr++ = ucChar; + else + { + if (RC_BAD( rc = pDynaBuf->appendByte( ucChar))) + { + goto Exit; + } + } + uiStrLen++; uiNumChars++; // Handle multi-byte UTF8 characters. The getLine() method has @@ -743,40 +930,61 @@ RCODE SQLStatement::getUTF8String( { // It is at least two bytes. - - if (uiNumChars == uiStrBufSize) - { - setErrInfo( m_uiCurrLineNum, - m_uiCurrLineOffset, - SQL_ERR_UTF8_STRING_TOO_LARGE, - m_uiCurrLineFilePos, - m_uiCurrLineBytes); - rc = RC_SET( NE_SFLM_INVALID_SQL); - goto Exit; - } + ucChar = getChar(); flmAssert( (ucChar >> 6) == 0x02); - *pszStr++ = ucChar; - uiNumChars++; - - // See if it is three bytes. - - if ((ucChar >> 5) != 0x06) + if (!pDynaBuf) { - if (uiNumChars == uiStrBufSize) + if (uiStrLen == uiStrBufSize) { setErrInfo( m_uiCurrLineNum, - m_uiCurrLineOffset, + m_uiCurrLineOffset - 1, SQL_ERR_UTF8_STRING_TOO_LARGE, m_uiCurrLineFilePos, m_uiCurrLineBytes); rc = RC_SET( NE_SFLM_INVALID_SQL); goto Exit; } + *pszStr++ = ucChar; + } + else + { + if (RC_BAD( rc = pDynaBuf->appendByte( ucChar))) + { + goto Exit; + } + } + + uiStrLen++; + + // See if it is three bytes. + + if ((ucChar >> 5) != 0x06) + { ucChar = getChar(); flmAssert( (ucChar >> 6) == 0x02); - *pszStr++ = ucChar; - uiNumChars++; + if (!pDynaBuf) + { + if (uiStrLen == uiStrBufSize) + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset - 1, + SQL_ERR_UTF8_STRING_TOO_LARGE, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } + *pszStr++ = ucChar; + } + else + { + if (RC_BAD( rc = pDynaBuf->appendByte( ucChar))) + { + goto Exit; + } + } + uiStrLen++; } } } @@ -785,7 +993,25 @@ RCODE SQLStatement::getUTF8String( // There will always be room for a null terminating byte if we // get to this point. - *pszStr = 0; + if (!pDynaBuf) + { + *pszStr = 0; + } + else + { + if (RC_BAD( rc = pDynaBuf->appendByte( 0))) + { + goto Exit; + } + } + if (puiStrLen) + { + *puiStrLen = uiStrLen; + } + if (puiNumChars) + { + *puiNumChars = uiNumChars; + } Exit: @@ -1028,8 +1254,8 @@ RCODE SQLStatement::getBool( char szBool [20]; FLMUINT uiBoolLen; - if (RC_BAD( rc = getUTF8String( bMustHaveEqual, (FLMBYTE *)szBool, - sizeof( szBool), &uiBoolLen))) + if (RC_BAD( rc = getUTF8String( bMustHaveEqual, TRUE, (FLMBYTE *)szBool, + sizeof( szBool), &uiBoolLen, NULL, NULL))) { goto Exit; } @@ -1097,7 +1323,8 @@ Exit: RCODE SQLStatement::getName( char * pszName, FLMUINT uiNameBufSize, - FLMUINT * puiNameLen) + FLMUINT * puiNameLen, + FLMUINT * puiTokenLineOffset) { RCODE rc = NE_SFLM_OK; FLMUINT uiCharCount = 0; @@ -1111,11 +1338,11 @@ RCODE SQLStatement::getName( // Always leave room for a null terminating character. uiNameBufSize--; + *puiTokenLineOffset = m_uiCurrLineOffset; // Get the first character - must be between A and Z ucChar = getChar(); - if ((ucChar >= 'a' && ucChar <= 'z') || (ucChar >= 'A' && ucChar <= 'Z')) { @@ -1188,8 +1415,10 @@ RCODE SQLStatement::getEncDefName( F_ENCDEF ** ppEncDef) { RCODE rc = NE_SFLM_OK; + FLMUINT uiTokenLineOffset; - if (RC_BAD( rc = getName( pszEncDefName, uiEncDefNameBufSize, puiEncDefNameLen))) + if (RC_BAD( rc = getName( pszEncDefName, uiEncDefNameBufSize, + puiEncDefNameLen, &uiTokenLineOffset))) { goto Exit; } @@ -1201,7 +1430,7 @@ RCODE SQLStatement::getEncDefName( if (bMustExist) { setErrInfo( m_uiCurrLineNum, - m_uiCurrLineOffset - 1, + uiTokenLineOffset, SQL_ERR_UNDEFINED_ENCDEF, m_uiCurrLineFilePos, m_uiCurrLineBytes); @@ -1218,7 +1447,7 @@ RCODE SQLStatement::getEncDefName( if (!bMustExist) { setErrInfo( m_uiCurrLineNum, - m_uiCurrLineOffset - 1, + uiTokenLineOffset, SQL_ERR_ENCDEF_ALREADY_DEFINED, m_uiCurrLineFilePos, m_uiCurrLineBytes); @@ -1243,8 +1472,10 @@ RCODE SQLStatement::getTableName( F_TABLE ** ppTable) { RCODE rc = NE_SFLM_OK; + FLMUINT uiTokenLineOffset; - if (RC_BAD( rc = getName( pszTableName, uiTableNameBufSize, puiTableNameLen))) + if (RC_BAD( rc = getName( pszTableName, uiTableNameBufSize, + puiTableNameLen, &uiTokenLineOffset))) { goto Exit; } @@ -1260,7 +1491,7 @@ RCODE SQLStatement::getTableName( if (bMustExist) { setErrInfo( m_uiCurrLineNum, - m_uiCurrLineOffset - 1, + uiTokenLineOffset, SQL_ERR_UNDEFINED_TABLE, m_uiCurrLineFilePos, m_uiCurrLineBytes); @@ -1277,7 +1508,7 @@ RCODE SQLStatement::getTableName( if (!bMustExist) { setErrInfo( m_uiCurrLineNum, - m_uiCurrLineOffset - 1, + uiTokenLineOffset, SQL_ERR_TABLE_ALREADY_DEFINED, m_uiCurrLineFilePos, m_uiCurrLineBytes); @@ -1302,8 +1533,10 @@ RCODE SQLStatement::getIndexName( F_INDEX ** ppIndex) { RCODE rc = NE_SFLM_OK; + FLMUINT uiTokenLineOffset; - if (RC_BAD( rc = getName( pszIndexName, uiIndexNameBufSize, puiIndexNameLen))) + if (RC_BAD( rc = getName( pszIndexName, uiIndexNameBufSize, + puiIndexNameLen, &uiTokenLineOffset))) { goto Exit; } @@ -1319,7 +1552,7 @@ RCODE SQLStatement::getIndexName( if (bMustExist) { setErrInfo( m_uiCurrLineNum, - m_uiCurrLineOffset - 1, + uiTokenLineOffset, SQL_ERR_UNDEFINED_INDEX, m_uiCurrLineFilePos, m_uiCurrLineBytes); @@ -1336,7 +1569,7 @@ RCODE SQLStatement::getIndexName( if (!bMustExist) { setErrInfo( m_uiCurrLineNum, - m_uiCurrLineOffset - 1, + uiTokenLineOffset, SQL_ERR_INDEX_ALREADY_DEFINED, m_uiCurrLineFilePos, m_uiCurrLineBytes); @@ -1522,7 +1755,8 @@ RCODE SQLStatement::getStringValue( pColumnValue->pucColumnValue = pucValue; f_encodeSEN( uiNumChars, &pucValue); - // Copy the string from the dynaBuf to the column. + // Copy the string from the dynaBuf to the column - NOTE: includes + // null terminating byte. f_memcpy( pucValue, dynaBuf.getBufferPtr(), dynaBuf.getDataLength()); @@ -1541,27 +1775,30 @@ RCODE SQLStatement::getNumberValue( FLMUINT64 ui64Value = 0; FLMBOOL bNeg = FALSE; FLMBYTE * pucValue; + FLMBYTE ucNumBuf [40]; + FLMUINT uiValLen; if (RC_BAD( rc = getNumber( FALSE, &ui64Value, &bNeg, TRUE))) { goto Exit; } - // Allocate space for ui64Value SEN plus one byte for the sign. + // Convert to storage format. - pColumnValue->uiValueLen = f_getSENByteCount( ui64Value) + 1; - if (RC_BAD( rc = m_tmpPool.poolAlloc( pColumnValue->uiValueLen, - (void **)&pucValue))) + uiValLen = sizeof( ucNumBuf); + if (RC_BAD( rc = flmNumber64ToStorage( ui64Value, &uiValLen, ucNumBuf, + bNeg, FALSE))) { goto Exit; } + + if (RC_BAD( rc = m_tmpPool.poolAlloc( uiValLen, (void **)&pucValue))) + { + goto Exit; + } + pColumnValue->uiValueLen = uiValLen; pColumnValue->pucColumnValue = pucValue; - - *pucValue++ = (FLMBYTE)(bNeg ? (FLMBYTE)1 : (FLMBYTE)0); - - // Set the number into the data. uiNumChars will hold bNeg. - - f_encodeSEN( ui64Value, &pucValue); + f_memcpy( pucValue, ucNumBuf, uiValLen); Exit: @@ -1814,6 +2051,20 @@ RCODE SQLStatement::executeSQL( goto Exit; } } + else if (f_stricmp( szToken, "update") == 0) + { + if (RC_BAD( rc = processUpdateRows())) + { + goto Exit; + } + } + else if (f_stricmp( szToken, "delete") == 0) + { + if (RC_BAD( rc = processDeleteRows())) + { + goto Exit; + } + } else if (f_stricmp( szToken, "open") == 0) { if (RC_BAD( rc = haveToken( "database", FALSE, SQL_ERR_INVALID_OPEN_OPTION))) diff --git a/sql/src/sqlstatement.h b/sql/src/sqlstatement.h index cd63fad..58fe4a6 100644 --- a/sql/src/sqlstatement.h +++ b/sql/src/sqlstatement.h @@ -30,6 +30,21 @@ #define MAX_SQL_NAME_LEN 128 +class SQLQuery; + +typedef struct COLUMN_SET +{ + FLMUINT uiColumnNum; + SQLQuery * pSqlQuery; + COLUMN_SET * pNext; +} COLUMN_SET; + +typedef struct TABLE_ITEM +{ + FLMUINT uiTableNum; + const char * pszTableAlias; +} TABLE_ITEM; + typedef enum { SQL_PARSE_STATS @@ -146,11 +161,17 @@ private: m_sqlStats.uiErrLineBytes = uiErrLineBytes; } + RCODE getBinaryValue( + F_DynaBuf * pDynaBuf); + RCODE getUTF8String( FLMBOOL bMustHaveEqual, + FLMBOOL bStripWildcardEscapes, FLMBYTE * pszStr, FLMUINT uiStrBufSize, - FLMUINT * puiStrLen); + FLMUINT * puiStrLen, + FLMUINT * puiNumChars, + F_DynaBuf * pDynaBuf); RCODE getNumber( FLMBOOL bMustHaveEqual, @@ -169,7 +190,8 @@ private: RCODE getName( char * pszName, FLMUINT uiNameBufSize, - FLMUINT * puiNameLen); + FLMUINT * puiNameLen, + FLMUINT * puiTokenLineOffset); RCODE getEncDefName( FLMBOOL bMustExist, @@ -231,6 +253,29 @@ private: RCODE processInsertRow( void); + RCODE parseSetColumns( + TABLE_ITEM * pTableList, + COLUMN_SET ** ppFirstColumnSet, + COLUMN_SET ** ppLastColumnSet, + FLMUINT * puiNumColumnsToSet); + + RCODE processUpdateRows( void); + + RCODE processDeleteRows( void); + + RCODE processAlphaToken( + TABLE_ITEM * pTableList, + FLMBOOL bSelectStatement, + FLMBOOL bUpdateExpression, + SQLQuery * pSqlQuery, + FLMBOOL * pbDone); + + RCODE parseCriteria( + TABLE_ITEM * pTableList, + FLMBOOL bSelectStatement, + FLMBOOL bUpdateExpression, + SQLQuery * pSqlQuery); + // Data F_Db * m_pDb; diff --git a/sql/src/updaterow.cpp b/sql/src/updaterow.cpp new file mode 100644 index 0000000..537ea29 --- /dev/null +++ b/sql/src/updaterow.cpp @@ -0,0 +1,1298 @@ +//------------------------------------------------------------------------------ +// Desc: This module contains the routines for inserting a row into a table. +// +// Tabs: 3 +// +// Copyright (c) 2006 Novell, Inc. All Rights Reserved. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of version 2 of the GNU General Public +// License as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, contact Novell, Inc. +// +// To contact Novell about this file by physical or electronic mail, +// you may find current contact information at www.novell.com +// +// $Id$ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +FSTATIC RCODE convertValueToStringFormat( + SQL_VALUE * pSqlValue, + F_COLUMN * pColumn, + F_COLUMN_VALUE * pColValue, + F_Pool * pPool); + +FSTATIC RCODE convertToNumber( + const char * pszStr, + FLMUINT64 * pui64Num, + FLMBOOL * pbNeg); + +FSTATIC RCODE convertValueToNumberFormat( + SQL_VALUE * pSqlValue, + F_COLUMN_VALUE * pColValue, + F_Pool * pPool); + +FSTATIC RCODE convertValueToBinaryFormat( + SQL_VALUE * pSqlValue, + F_COLUMN * pColumn, + F_COLUMN_VALUE * pColValue, + F_Pool * pPool); + +FSTATIC RCODE convertValueToStorageFormat( + SQL_VALUE * pSqlValue, + F_COLUMN * pColumn, + F_COLUMN_VALUE * pColValue, + F_Pool * pPool); + +//------------------------------------------------------------------------------ +// Desc: Update a row in the database. +//------------------------------------------------------------------------------ +RCODE F_Db::updateRow( + FLMUINT uiTableNum, + F_Row ** ppRow, + F_COLUMN_VALUE * pColumnValues) +{ + RCODE rc = NE_SFLM_OK; + F_COLUMN_VALUE * pColumnValue; + F_TABLE * pTable; + F_COLUMN * pColumn; + FLMBOOL bStartedTrans = FALSE; + F_Row * pRow; + + // Make sure we are in an update transaction. + + if (RC_BAD( rc = checkTransaction( SFLM_UPDATE_TRANS, &bStartedTrans))) + { + goto Exit; + } + + pTable = m_pDict->getTable( uiTableNum); + if (pTable->bSystemTable) + { + rc = RC_SET( NE_SFLM_CANNOT_UPDATE_IN_SYSTEM_TABLE); + goto Exit; + } + + // Make sure we have a write-copy of the row before we start + // modifying it. + + if (RC_BAD( rc = gv_SFlmSysData.pRowCacheMgr->makeWriteCopy( this, ppRow))) + { + goto Exit; + } + pRow = *ppRow; + + // Do whatever indexing needs to be done - BEFORE changing the values. + + if (RC_BAD( rc = updateIndexKeys( uiTableNum, pRow, FALSE, pColumnValues))) + { + goto Exit; + } + + // Set the column values into the row. + + for (pColumnValue = pColumnValues; + pColumnValue; + pColumnValue = pColumnValue->pNext) + { + pColumn = m_pDict->getColumn( pTable, pColumnValue->uiColumnNum); + if (pColumn->uiMaxLen) + { + if (pColumn->eDataTyp == SFLM_STRING_TYPE) + { + FLMUINT uiNumChars; + const FLMBYTE * pucData = (const FLMBYTE *)pColumnValue->pucColumnValue; + const FLMBYTE * pucEnd = pucData + pColumnValue->uiValueLen; + + // Number of characters is the first part of the value + + if (RC_BAD( rc = f_decodeSEN( &pucData, pucEnd, &uiNumChars))) + { + goto Exit; + } + if (pColumnValue->uiValueLen > uiNumChars) + { + rc = RC_SET( NE_SFLM_STRING_TOO_LONG); + goto Exit; + } + } + else if (pColumn->eDataTyp == SFLM_BINARY_TYPE) + { + if (pColumnValue->uiValueLen > pColumn->uiMaxLen) + { + rc = RC_SET( NE_SFLM_BINARY_TOO_LONG); + goto Exit; + } + } + } + if (RC_BAD( rc = pRow->setValue( this, pColumnValue->uiColumnNum, + pColumnValue->pucColumnValue, + pColumnValue->uiValueLen))) + { + goto Exit; + } + } + + // Log the insert row. + + if (RC_BAD( rc = m_pDatabase->m_pRfl->logUpdateRow( this, pRow->m_ui64RowId, + uiTableNum, pColumnValues))) + { + goto Exit; + } + + // Commit the transaction if we started it + + if (bStartedTrans) + { + bStartedTrans = FALSE; + if (RC_BAD( rc = transCommit())) + { + goto Exit; + } + } + +Exit: + + if (bStartedTrans) + { + transAbort(); + } + + return( rc); +} + +//------------------------------------------------------------------------------ +// Desc: Convert an SQL_VALUE to string format. +//------------------------------------------------------------------------------ +FSTATIC RCODE convertValueToStringFormat( + SQL_VALUE * pSqlValue, + F_COLUMN * pColumn, + F_COLUMN_VALUE * pColValue, + F_Pool * pPool) +{ + RCODE rc = NE_SFLM_OK; + FLMUINT uiSenLen; + FLMBYTE * pucValue; + FLMUINT uiLoop; + FLMBYTE * pucTmp; + char szTmp [100]; + + switch (pSqlValue->eValType) + { + case SQL_BOOL_VAL: + switch (pSqlValue->val.eBool) + { + case SQL_FALSE: + f_strcpy( szTmp, "FALSE"); + pSqlValue->val.str.pszStr = (FLMBYTE *)&szTmp [0]; + pSqlValue->val.str.uiByteLen = 6; + pSqlValue->val.str.uiNumChars = 5; + break; + case SQL_TRUE: + f_strcpy( szTmp, "TRUE"); + pSqlValue->val.str.pszStr = (FLMBYTE *)&szTmp [0]; + pSqlValue->val.str.uiByteLen = 5; + pSqlValue->val.str.uiNumChars = 4; + break; + case SQL_UNKNOWN: + default: + f_strcpy( szTmp, "UNKNOWN"); + pSqlValue->val.str.pszStr = (FLMBYTE *)&szTmp [0]; + pSqlValue->val.str.uiByteLen = 8; + pSqlValue->val.str.uiNumChars = 7; + break; + } + goto Output_Str; + case SQL_UINT_VAL: + f_sprintf( szTmp, "%u", (unsigned)pSqlValue->val.uiVal); + pSqlValue->val.str.pszStr = (FLMBYTE *)&szTmp [0]; + pSqlValue->val.str.uiNumChars = f_strlen( szTmp); + pSqlValue->val.str.uiByteLen = pSqlValue->val.str.uiNumChars + 1; + goto Output_Str; + case SQL_UINT64_VAL: + f_sprintf( szTmp, "%I64u", pSqlValue->val.ui64Val); + pSqlValue->val.str.pszStr = (FLMBYTE *)&szTmp [0]; + pSqlValue->val.str.uiNumChars = f_strlen( szTmp); + pSqlValue->val.str.uiByteLen = pSqlValue->val.str.uiNumChars + 1; + goto Output_Str; + case SQL_INT_VAL: + f_sprintf( szTmp, "%d", pSqlValue->val.iVal); + pSqlValue->val.str.pszStr = (FLMBYTE *)&szTmp [0]; + pSqlValue->val.str.uiNumChars = f_strlen( szTmp); + pSqlValue->val.str.uiByteLen = pSqlValue->val.str.uiNumChars + 1; + goto Output_Str; + case SQL_INT64_VAL: + f_sprintf( szTmp, "%I64d", pSqlValue->val.i64Val); + pSqlValue->val.str.pszStr = (FLMBYTE *)&szTmp [0]; + pSqlValue->val.str.uiNumChars = f_strlen( szTmp); + pSqlValue->val.str.uiByteLen = pSqlValue->val.str.uiNumChars + 1; + goto Output_Str; + case SQL_BINARY_VAL: + + // Output two HEX bytes for every one byte of binary data. + + // See if the string would be too long. + + if (pColumn->uiMaxLen && + pSqlValue->val.bin.uiByteLen * 2 > pColumn->uiMaxLen) + { + rc = RC_SET( NE_SFLM_STRING_TOO_LONG); + goto Exit; + } + + uiSenLen = f_getSENByteCount( pSqlValue->val.bin.uiByteLen * 2); + pColValue->uiValueLen = pSqlValue->val.bin.uiByteLen * 2 + 1 + uiSenLen; + if (RC_BAD( rc = pPool->poolAlloc( pColValue->uiValueLen, + (void **)&pucValue))) + { + goto Exit; + } + pColValue->pucColumnValue = pucValue; + f_encodeSEN( pSqlValue->val.bin.uiByteLen * 2, &pucValue); + + // Output the binary as hex - two bytes for every one byte. + + for (pucTmp = pSqlValue->val.bin.pucValue, uiLoop = 0; + uiLoop < pSqlValue->val.bin.uiByteLen; + uiLoop++, pucTmp++) + { + FLMBYTE ucChar = (*pucTmp) >> 4; + + if (ucChar <= 9) + { + *pucValue++ = '0' + ucChar; + } + else + { + *pucValue++ = 'A' + ucChar - 10; + } + ucChar = (*pucTmp) & 0x0F; + if (ucChar <= 9) + { + *pucValue++ = '0' + ucChar; + } + else + { + *pucValue++ = 'A' + ucChar - 10; + } + } + *pucValue = 0; + break; + case SQL_UTF8_VAL: +Output_Str: + + // See if the string is too long. + + if (pColumn->uiMaxLen && + pSqlValue->val.str.uiNumChars > pColumn->uiMaxLen) + { + rc = RC_SET( NE_SFLM_STRING_TOO_LONG); + goto Exit; + } + + uiSenLen = f_getSENByteCount( pSqlValue->val.str.uiNumChars); + pColValue->uiValueLen = pSqlValue->val.str.uiByteLen + uiSenLen; + if (RC_BAD( rc = pPool->poolAlloc( pColValue->uiValueLen, + (void **)&pucValue))) + { + goto Exit; + } + pColValue->pucColumnValue = pucValue; + f_encodeSEN( pSqlValue->val.str.uiNumChars, &pucValue); + + // Copy the string from the dynaBuf to the column. + + f_memcpy( pucValue, pSqlValue->val.str.pszStr, + pSqlValue->val.str.uiByteLen); + break; + default: + flmAssert( 0); + rc = RC_SET( NE_SFLM_FAILURE); + goto Exit; + } + +Exit: + + return( rc); +} + +//------------------------------------------------------------------------------ +// Desc: Convert a string to a number. +//------------------------------------------------------------------------------ +FSTATIC RCODE convertToNumber( + const char * pszStr, + FLMUINT64 * pui64Num, + FLMBOOL * pbNeg) +{ + RCODE rc = NE_SFLM_OK; + FLMBYTE ucChar; + FLMUINT64 ui64Value = 0; + FLMBOOL bHex = FALSE; + FLMUINT uiDigitValue = 0; + + // See if it is a negative number. + + if (*pszStr == '-') + { + *pbNeg = TRUE; + pszStr++; + } + else + { + *pbNeg = FALSE; + } + + // See if it is a hex number. + + if (*pszStr == 'x' || *pszStr == 'X') + { + pszStr++; + bHex = TRUE; + } + else if (*pszStr == '0' && (*(pszStr + 1) == 'x' || *(pszStr + 1) == 'X')) + { + pszStr++; + bHex = TRUE; + } + + // Go until we hit a character that is not a number. + + while (pszStr) + { + ucChar = (FLMBYTE)(*pszStr); + pszStr++; + + if (ucChar >= '0' && ucChar <= '9') + { + uiDigitValue = (FLMUINT)(ucChar - '0'); + } + else if (ucChar >= 'a' && ucChar <= 'f') + { + if (!bHex) + { + rc = RC_SET( NE_SFLM_CONV_BAD_DIGIT); + goto Exit; + } + uiDigitValue = (FLMUINT)(ucChar - 'a' + 10); + } + else if (ucChar >= 'A' && ucChar <= 'F') + { + if (!bHex) + { + rc = RC_SET( NE_SFLM_CONV_BAD_DIGIT); + goto Exit; + } + uiDigitValue = (FLMUINT)(ucChar - 'A' + 10); + } + else + { + rc = RC_SET( NE_SFLM_CONV_BAD_DIGIT); + goto Exit; + } + + if (bHex) + { + if (ui64Value > (FLM_MAX_UINT64 >> 4)) + { + rc = RC_SET( NE_SFLM_CONV_NUM_OVERFLOW); + goto Exit; + } + ui64Value <<= 4; + ui64Value += (FLMUINT64)uiDigitValue; + } + else + { + if (ui64Value > (FLM_MAX_UINT64 / 10)) + { + rc = RC_SET( NE_SFLM_CONV_NUM_OVERFLOW); + goto Exit; + } + ui64Value *= 10; + ui64Value += (FLMUINT64)uiDigitValue; + } + + // If it is a negative number, make sure we have not + // exceeded the maximum negative value. + + if (*pbNeg && ui64Value > ((FLMUINT64)1 << 63)) + { + rc = RC_SET( NE_SFLM_CONV_NUM_OVERFLOW); + goto Exit; + } + } + + if (!ui64Value) + { + *pbNeg = FALSE; + } + *pui64Num = ui64Value; + +Exit: + + return( rc); +} + +//------------------------------------------------------------------------------ +// Desc: Convert an SQL_VALUE to number format. +//------------------------------------------------------------------------------ +FSTATIC RCODE convertValueToNumberFormat( + SQL_VALUE * pSqlValue, + F_COLUMN_VALUE * pColValue, + F_Pool * pPool) +{ + RCODE rc = NE_SFLM_OK; + FLMBYTE ucNumBuf [40]; + FLMUINT uiValLen; + FLMBYTE * pucValue; + FLMBOOL bNeg; + FLMUINT64 ui64Value; + + switch (pSqlValue->eValType) + { + case SQL_BOOL_VAL: + bNeg = FALSE; + switch (pSqlValue->val.eBool) + { + case SQL_FALSE: + ui64Value = 0; + goto Output_Num; + case SQL_TRUE: + ui64Value = 1; + goto Output_Num; + case SQL_UNKNOWN: + default: + ui64Value = 2; + goto Output_Num; + } + case SQL_UINT_VAL: + ui64Value = (FLMUINT64)pSqlValue->val.uiVal; + bNeg = FALSE; +Output_Num: + uiValLen = sizeof( ucNumBuf); + if (RC_BAD( rc = flmNumber64ToStorage( ui64Value, &uiValLen, ucNumBuf, + bNeg, FALSE))) + { + goto Exit; + } + if (RC_BAD( rc = pPool->poolAlloc( uiValLen, (void **)&pucValue))) + { + goto Exit; + } + pColValue->uiValueLen = uiValLen; + pColValue->pucColumnValue = pucValue; + f_memcpy( pucValue, ucNumBuf, uiValLen); + break; + + case SQL_UINT64_VAL: + ui64Value = (FLMUINT64)pSqlValue->val.ui64Val; + bNeg = FALSE; + goto Output_Num; + + case SQL_INT_VAL: + if (pSqlValue->val.iVal < 0) + { + bNeg = TRUE; + ui64Value = (FLMUINT64)(-pSqlValue->val.iVal); + } + else + { + bNeg = FALSE; + ui64Value = (FLMUINT64)(pSqlValue->val.iVal); + } + goto Output_Num; + + case SQL_INT64_VAL: + if (pSqlValue->val.i64Val < 0) + { + bNeg = TRUE; + ui64Value = (FLMUINT64)(-pSqlValue->val.i64Val); + } + else + { + bNeg = FALSE; + ui64Value = (FLMUINT64)(pSqlValue->val.i64Val); + } + goto Output_Num; + + case SQL_UTF8_VAL: + if (RC_BAD( rc = convertToNumber( + (const char *)pSqlValue->val.str.pszStr, + &ui64Value, &bNeg))) + { + goto Exit; + } + goto Output_Num; + case SQL_BINARY_VAL: + if (pSqlValue->val.bin.uiByteLen > sizeof( FLMUINT64)) + { + rc = RC_SET( NE_SFLM_CONV_NUM_OVERFLOW); + goto Exit; + } + bNeg = FALSE; + if (pSqlValue->val.bin.uiByteLen) + { + f_memcpy( &ui64Value, pSqlValue->val.bin.pucValue, + pSqlValue->val.bin.uiByteLen); + if (pSqlValue->val.bin.uiByteLen < sizeof( FLMUINT64)) + { + ui64Value >>= (sizeof( FLMUINT64) - pSqlValue->val.bin.uiByteLen); + } + } + else + { + ui64Value = 0; + } + goto Output_Num; + default: + flmAssert( 0); + rc = RC_SET( NE_SFLM_FAILURE); + goto Exit; + } + +Exit: + + return( rc); +} + +//------------------------------------------------------------------------------ +// Desc: Convert an SQL_VALUE to binary format. +//------------------------------------------------------------------------------ +FSTATIC RCODE convertValueToBinaryFormat( + SQL_VALUE * pSqlValue, + F_COLUMN * pColumn, + F_COLUMN_VALUE * pColValue, + F_Pool * pPool) +{ + RCODE rc = NE_SFLM_OK; + FLMBYTE * pucValue; + FLMUINT64 ui64Val; + FLMUINT uiVal; + FLMINT iVal; + FLMINT64 i64Val; + FLMBYTE ucTmp; + + switch (pSqlValue->eValType) + { + case SQL_BOOL_VAL: + switch (pSqlValue->val.eBool) + { + case SQL_FALSE: + ucTmp = 0; + break; + case SQL_TRUE: + ucTmp = 1; + break; + case SQL_UNKNOWN: + default: + ucTmp = 2; + break; + } + pSqlValue->val.bin.pucValue = &ucTmp; + pSqlValue->val.bin.uiByteLen = 1; + goto Output_Binary; + case SQL_UINT_VAL: + uiVal = pSqlValue->val.uiVal; + pSqlValue->val.bin.pucValue = (FLMBYTE *)(&uiVal); + pSqlValue->val.bin.uiByteLen = sizeof( FLMUINT); + goto Output_Binary; + case SQL_UINT64_VAL: + ui64Val = pSqlValue->val.ui64Val; + pSqlValue->val.bin.pucValue = (FLMBYTE *)(&ui64Val); + pSqlValue->val.bin.uiByteLen = sizeof( FLMUINT64); + goto Output_Binary; + case SQL_INT_VAL: + iVal = pSqlValue->val.iVal; + pSqlValue->val.bin.pucValue = (FLMBYTE *)(&iVal); + pSqlValue->val.bin.uiByteLen = sizeof( FLMINT); + goto Output_Binary; + case SQL_INT64_VAL: + i64Val = pSqlValue->val.i64Val; + pSqlValue->val.bin.pucValue = (FLMBYTE *)(&i64Val); + pSqlValue->val.bin.uiByteLen = sizeof( FLMINT64); + goto Output_Binary; + case SQL_UTF8_VAL: + + // Try to convert the string to binary - assume it is a hex + // string, so the largest binary value will be half the string + // size - because we convert two bytes to a single binary byte. + // Ignore white space in the string. + + if (!pSqlValue->val.str.uiByteLen) + { + pColValue->pucColumnValue = NULL; + } + else + { + FLMBOOL bHaveHighNibble; + FLMBYTE ucBinChar; + FLMBYTE * pucTmp; + FLMBYTE ucStrChar; + + if (RC_BAD( rc = pPool->poolAlloc( pSqlValue->val.str.uiByteLen / 2 + 1, + (void **)&pucValue))) + { + goto Exit; + } + pColValue->pucColumnValue = pucValue; + pucTmp = (FLMBYTE *)pSqlValue->val.str.pszStr; + + bHaveHighNibble = FALSE; + ucBinChar = 0; + while (*pucTmp) + { + ucStrChar = *pucTmp; + pucTmp++; + if (ucStrChar >= '0' && ucStrChar <= '9') + { + if (!bHaveHighNibble) + { + ucBinChar = (FLMBYTE)(ucStrChar - '0') << 4; + bHaveHighNibble = TRUE; + } + else + { + *pucValue++ = ucBinChar | (FLMBYTE)(ucStrChar - '0'); + bHaveHighNibble = FALSE; + } + } + else if (ucStrChar >= 'a' && ucStrChar <= 'f') + { + if (!bHaveHighNibble) + { + ucBinChar = (FLMBYTE)(ucStrChar - 'a' + 10) << 4; + bHaveHighNibble = TRUE; + } + else + { + *pucValue++ = ucBinChar | (FLMBYTE)(ucStrChar - 'a' + 10); + bHaveHighNibble = FALSE; + } + } + else if (ucStrChar >= 'A' && ucStrChar <= 'F') + { + if (!bHaveHighNibble) + { + ucBinChar = (FLMBYTE)(ucStrChar - 'A' + 10) << 4; + bHaveHighNibble = TRUE; + } + else + { + *pucValue++ = ucBinChar | (FLMBYTE)(ucStrChar - 'A' + 10); + bHaveHighNibble = FALSE; + } + } + else if (ucStrChar == ' ' || ucStrChar == '\t' || + ucStrChar == '\n' || ucStrChar == '\r') + { + // Skip over white space. + } + else + { + rc = RC_SET( NE_SFLM_CONV_BAD_DIGIT); + goto Exit; + } + } + if (bHaveHighNibble) + { + *pucValue++ = ucBinChar; + } + + // See if we have too many bytes. + + pColValue->uiValueLen = (FLMUINT)(pucValue - pColValue->pucColumnValue); + if (pColumn->uiMaxLen && pColValue->uiValueLen > pColumn->uiMaxLen) + { + rc = RC_SET( NE_SFLM_BINARY_TOO_LONG); + goto Exit; + } + } + break; + case SQL_BINARY_VAL: +Output_Binary: + // See if the binary data is too long for the + // column. + + if (pColumn->uiMaxLen && + pSqlValue->val.bin.uiByteLen > pColumn->uiMaxLen) + { + rc = RC_SET( NE_SFLM_BINARY_TOO_LONG); + goto Exit; + } + if ((pColValue->uiValueLen = pSqlValue->val.bin.uiByteLen) > 0) + { + if (RC_BAD( rc = pPool->poolAlloc( pColValue->uiValueLen, + (void **)&pucValue))) + { + goto Exit; + } + pColValue->pucColumnValue = pucValue; + + // Copy the string from the dynaBuf to the column. + + f_memcpy( pucValue, pSqlValue->val.bin.pucValue, + pSqlValue->val.bin.uiByteLen); + } + else + { + pColValue->pucColumnValue = NULL; + } + break; + default: + flmAssert( 0); + rc = RC_SET( NE_SFLM_FAILURE); + goto Exit; + } + +Exit: + + return( rc); +} + +//------------------------------------------------------------------------------ +// Desc: Convert an SQL_VALUE to storage format. +//------------------------------------------------------------------------------ +FSTATIC RCODE convertValueToStorageFormat( + SQL_VALUE * pSqlValue, + F_COLUMN * pColumn, + F_COLUMN_VALUE * pColValue, + F_Pool * pPool) +{ + RCODE rc = NE_SFLM_OK; + + switch (pColumn->eDataTyp) + { + case SFLM_STRING_TYPE: + if (RC_BAD( rc = convertValueToStringFormat( pSqlValue, + pColumn, pColValue, pPool))) + { + goto Exit; + } + break; + case SFLM_NUMBER_TYPE: + if (RC_BAD( rc = convertValueToNumberFormat( pSqlValue, + pColValue, pPool))) + { + goto Exit; + } + break; + case SFLM_BINARY_TYPE: + if (RC_BAD( rc = convertValueToBinaryFormat( pSqlValue, + pColumn, pColValue, pPool))) + { + goto Exit; + } + break; + + default: + flmAssert( 0); + rc = RC_SET( NE_SFLM_FAILURE); + goto Exit; + } + +Exit: + + return( rc); +} + +//------------------------------------------------------------------------------ +// Desc: Update selected rows in the database. +//------------------------------------------------------------------------------ +RCODE F_Db::updateSelectedRows( + FLMUINT uiTableNum, + SQLQuery * pSqlQuery, + COLUMN_SET * pFirstColumnSet, + FLMUINT uiNumColumnsToSet) +{ + RCODE rc = NE_SFLM_OK; + FLMBOOL bStartedTrans = FALSE; + F_TABLE * pTable; + F_COLUMN * pColumn; + F_Row * pRow = NULL; + F_COLUMN_VALUE * pFirstColValue; + F_COLUMN_VALUE * pLastColValue; + F_COLUMN_VALUE * pColValue; + F_COLUMN_ITEM * pColItem; + COLUMN_SET * pColSet; + SQL_VALUE sqlValue; + F_Pool tmpPool; + FLMBOOL bValueChanged; + + tmpPool.poolInit( 2048); + + // Make sure we are in an update transaction. + + if (RC_BAD( rc = checkTransaction( SFLM_UPDATE_TRANS, &bStartedTrans))) + { + goto Exit; + } + + // Cannot update in internal system tables. + + pTable = m_pDict->getTable( uiTableNum); + if (pTable->bSystemTable) + { + rc = RC_SET( NE_SFLM_CANNOT_UPDATE_IN_SYSTEM_TABLE); + goto Exit; + } + + // Execute the query + + for (;;) + { + if (RC_BAD( rc = pSqlQuery->getNext( &pRow))) + { + if (rc == NE_SFLM_EOF_HIT) + { + rc = NE_SFLM_OK; + break; + } + else + { + goto Exit; + } + } + pFirstColValue = NULL; + pLastColValue = NULL; + + pColSet = pFirstColumnSet; + while (pColSet) + { + + // Allocate a column value and link it into the list of + // column values. + + if (RC_BAD( rc = tmpPool.poolAlloc( sizeof( F_COLUMN_VALUE), + (void **)&pColValue))) + { + goto Exit; + } + pColValue->uiColumnNum = pColSet->uiColumnNum; + bValueChanged = TRUE; + + // Evaluate the column value. + + if (!pColSet->pSqlQuery) + { +Set_Null_Value: + + // Set value to NULL + + pColValue->pucColumnValue = NULL; + pColValue->uiValueLen = 0; + + // If the value is already NULL, no need to set it again. + + if (pRow->getColumn( pColSet->uiColumnNum) == NULL) + { + bValueChanged = FALSE; + } + } + else + { + if (RC_BAD( rc = pColSet->pSqlQuery->evalCriteria( &sqlValue, + &tmpPool, pRow))) + { + goto Exit; + } + pColumn = m_pDict->getColumn( pTable, pColSet->uiColumnNum); + + if (RC_BAD( rc = convertValueToStorageFormat( &sqlValue, + pColumn, pColValue, &tmpPool))) + { + goto Exit; + } + + if (!pColValue->uiValueLen) + { + goto Set_Null_Value; + } + + // See if the value changed from what it was. If not, unlink + // it from the list. + + if ((pColItem = pRow->getColumn( pColSet->uiColumnNum)) != NULL) + { + FLMBYTE * pucColDataPtr = pRow->getColumnDataPtr( pColSet->uiColumnNum); + + if (pColItem->uiDataLen == pColValue->uiValueLen && + f_memcmp( pColValue->pucColumnValue, pucColDataPtr, + pColItem->uiDataLen) == 0) + { + bValueChanged = FALSE; + } + } + } + + // Only link the value into the list if it actually changed from + // what it was before. + + if (bValueChanged) + { + pColValue->pNext = NULL; + if (pLastColValue) + { + pLastColValue->pNext = pColValue; + } + else + { + pFirstColValue = pColValue; + } + pLastColValue = pColValue; + } + pColSet = pColSet->pNext; + } + + // No need to actually do an update if no columns changed on the row. + + if (pFirstColValue) + { + if (RC_BAD( rc = updateRow( uiTableNum, &pRow, pFirstColValue))) + { + goto Exit; + } + } + tmpPool.poolReset( NULL); + } + + // Commit the transaction if we started it + + if (bStartedTrans) + { + bStartedTrans = FALSE; + if (RC_BAD( rc = transCommit())) + { + goto Exit; + } + } + +Exit: + + if (pRow) + { + pRow->ReleaseRow(); + } + + if (bStartedTrans) + { + transAbort(); + } + + tmpPool.poolFree(); + + return( rc); +} + +//------------------------------------------------------------------------------ +// Desc: Process the UPDATE statement. The "UPDATE" keyword has already been +// parsed. +//------------------------------------------------------------------------------ +RCODE SQLStatement::parseSetColumns( + TABLE_ITEM * pTableList, + COLUMN_SET ** ppFirstColumnSet, + COLUMN_SET ** ppLastColumnSet, + FLMUINT * puiNumColumnsToSet) +{ + RCODE rc = NE_SFLM_OK; + char szToken [MAX_SQL_TOKEN_SIZE + 1]; + char szColumnName [MAX_SQL_NAME_LEN + 1]; + FLMUINT uiColumnNameLen; + F_COLUMN * pColumn; + F_TABLE * pTable = m_pDb->m_pDict->getTable( pTableList->uiTableNum); + FLMUINT uiTokenLineOffset; + COLUMN_SET * pColumnSet; + SQLQuery * pSqlQuery = NULL; + + // Must have the keyword "SET" + + if (RC_BAD( rc = haveToken( "set", FALSE, SQL_ERR_EXPECTING_SET))) + { + goto Exit; + } + + for (;;) + { + + // Get a column name. + + if (RC_BAD( rc = getName( szColumnName, sizeof( szColumnName), + &uiColumnNameLen, &uiTokenLineOffset))) + { + goto Exit; + } + + // See if the column is defined in the table. + + if ((pColumn = m_pDb->m_pDict->findColumn( pTable, szColumnName)) == NULL) + { + + // See if it is the table name. If so, the next token must be + // a period, followed by the column name. + + if (f_stricmp( szColumnName, pTable->pszTableName) == 0) + { + if (RC_BAD( rc = haveToken( ".", FALSE, SQL_ERR_EXPECTING_PERIOD))) + { + goto Exit; + } + if (RC_BAD( rc = getName( szColumnName, sizeof( szColumnName), + &uiColumnNameLen, &uiTokenLineOffset))) + { + goto Exit; + } + pColumn = m_pDb->m_pDict->findColumn( pTable, szColumnName); + } + } + if (!pColumn) + { + setErrInfo( m_uiCurrLineNum, + uiTokenLineOffset, + SQL_ERR_UNDEFINED_COLUMN, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } + + // Equal must follow the colum name + + if (RC_BAD( rc = haveToken( "=", FALSE, SQL_ERR_EXPECTING_EQUAL))) + { + goto Exit; + } + + // Allocate a column set structure + + if (RC_BAD( rc = m_tmpPool.poolAlloc( sizeof( COLUMN_SET), + (void **)&pColumnSet))) + { + goto Exit; + } + + // See if the next token is NULL. If so, there is no expression + // for this column - it is to be set to NULL. + + if (RC_OK( rc = haveToken( "null", FALSE))) + { + pSqlQuery = NULL; + } + else if (rc != NE_SFLM_NOT_FOUND) + { + goto Exit; + } + else + { + + // Allocate an SQLQuery object, have the pColumnSet structure + // point to it, and link the pColumnSet structure into the linked + // list. + + if ((pSqlQuery = f_new SQLQuery) == NULL) + { + rc = RC_SET( NE_SFLM_MEM); + goto Exit; + } + } + pColumnSet->pSqlQuery = pSqlQuery; + pColumnSet->uiColumnNum = pColumn->uiColumnNum; + pColumnSet->pNext = NULL; + if (*ppLastColumnSet) + { + (*ppLastColumnSet)->pNext = pColumnSet; + } + else + { + *ppFirstColumnSet = pColumnSet; + } + *ppLastColumnSet = pColumnSet; + (*puiNumColumnsToSet)++; + + // Now parse the criteria for the SET command, unless NULL has already + // been detected. + + if (pSqlQuery) + { + if (RC_BAD( rc = parseCriteria( pTableList, FALSE, TRUE, pSqlQuery))) + { + goto Exit; + } + } + + // Next token should either be a comma or the WHERE keyword, or we + // should have hit EOF. + + if (RC_BAD( rc = getToken( szToken, sizeof( szToken), TRUE, + &uiTokenLineOffset))) + { + if (rc == NE_SFLM_EOF_HIT) + { + rc = NE_SFLM_OK; + break; + } + else + { + goto Exit; + } + } + + if (f_stricmp( szToken, "where") == 0) + { + + // Push the WHERE token back into the input stream - so that + // the caller can handle it. + + m_uiCurrLineOffset = uiTokenLineOffset; + break; + } + else if (f_stricmp( szToken, ",") != 0) + { + setErrInfo( m_uiCurrLineNum, + uiTokenLineOffset, + SQL_ERR_EXPECTING_COMMA, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } + } + +Exit: + + return( rc); +} + +//------------------------------------------------------------------------------ +// Desc: Process the UPDATE statement. The "UPDATE" keyword has already been +// parsed. +//------------------------------------------------------------------------------ +RCODE SQLStatement::processUpdateRows( void) +{ + RCODE rc = NE_SFLM_OK; + FLMBOOL bStartedTrans = FALSE; + char szTableName [MAX_SQL_NAME_LEN + 1]; + FLMUINT uiTableNameLen; + F_TABLE * pTable; + TABLE_ITEM tableList [2]; + COLUMN_SET * pFirstColumnSet = NULL; + COLUMN_SET * pLastColumnSet = NULL; + FLMUINT uiNumColumnsToSet = 0; + SQLQuery sqlQuery; + + // If we are in a read transaction, we cannot do this operation + + if (RC_BAD( rc = m_pDb->checkTransaction( SFLM_UPDATE_TRANS, &bStartedTrans))) + { + goto Exit; + } + + // SYNTAX: UPDATE table_name + // SET column = {expression | NULL} + // [, column = {expression | NULL}]... + // [WHERE ] + + // Get the table name. + + if (RC_BAD( rc = getTableName( TRUE, szTableName, sizeof( szTableName), + &uiTableNameLen, &pTable))) + { + goto Exit; + } + + if (pTable->bSystemTable) + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset, + SQL_ERR_CANNOT_UPDATE_SYSTEM_TABLE, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_CANNOT_UPDATE_IN_SYSTEM_TABLE); + goto Exit; + } + + // Must SET at least one column - get list of columns to be set and + // the expressions for each column. + + tableList [0].uiTableNum = pTable->uiTableNum; + tableList [0].pszTableAlias = pTable->pszTableName; + + // Null terminate the list. + + tableList [1].uiTableNum = 0; + + if (RC_BAD( rc = parseSetColumns( &tableList [0], &pFirstColumnSet, + &pLastColumnSet, &uiNumColumnsToSet))) + { + goto Exit; + } + + // See if we have a WHERE clause + + if (RC_BAD( rc = haveToken( "where", TRUE))) + { + if (rc == NE_SFLM_NOT_FOUND || rc == NE_SFLM_EOF_HIT) + { + if (RC_BAD( rc = sqlQuery.addTable( pTable->uiTableNum, NULL))) + { + goto Exit; + } + } + else + { + goto Exit; + } + } + else + { + + if (RC_BAD( rc = parseCriteria( &tableList [0], FALSE, FALSE, &sqlQuery))) + { + goto Exit; + } + } + + if (RC_BAD( rc = m_pDb->updateSelectedRows( pTable->uiTableNum, &sqlQuery, + pFirstColumnSet, uiNumColumnsToSet))) + { + goto Exit; + } + + // Commit the transaction if we started it + + if (bStartedTrans) + { + bStartedTrans = FALSE; + if (RC_BAD( rc = m_pDb->transCommit())) + { + goto Exit; + } + } + +Exit: + + while (pFirstColumnSet) + { + if (pFirstColumnSet->pSqlQuery) + { + pFirstColumnSet->pSqlQuery->Release(); + pFirstColumnSet->pSqlQuery = NULL; + } + pFirstColumnSet = pFirstColumnSet->pNext; + } + + if (bStartedTrans) + { + m_pDb->transAbort(); + } + + return( rc); +} + diff --git a/sql/src/whereclause.cpp b/sql/src/whereclause.cpp new file mode 100644 index 0000000..393773d --- /dev/null +++ b/sql/src/whereclause.cpp @@ -0,0 +1,1568 @@ +//------------------------------------------------------------------------- +// Desc: Parse SQL +// Tabs: 3 +// +// Copyright (c) 2006 Novell, Inc. All Rights Reserved. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of version 2 of the GNU General Public +// License as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, contact Novell, Inc. +// +// To contact Novell about this file by physical or electronic mail, +// you may find current contact information at www.novell.com +// +// $Id$ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +// Local function prototypes + +FSTATIC void sqlUnlinkFromParent( + SQL_NODE * pSQLNode); + +FSTATIC void sqlLinkLastChild( + SQL_NODE * pParent, + SQL_NODE * pChild); + +static FLMUINT uiSQLOpPrecedenceTable[ SQL_NEG_OP - SQL_AND_OP + 1] = +{ + 2, // SQL_AND_OP + 1, // SQL_OR_OP + 10, // SQL_NOT_OP + 6, // SQL_EQ_OP + 6, // SQL_NE_OP + 6, // SQL_APPROX_EQ_OP + 7, // SQL_LT_OP + 7, // SQL_LE_OP + 7, // SQL_GT_OP + 7, // SQL_GE_OP + 5, // SQL_BITAND_OP + 3, // SQL_BITOR_OP + 4, // SQL_BITXOR_OP + 9, // SQL_MULT_OP + 9, // SQL_DIV_OP + 9, // SQL_MOD_OP + 8, // SQL_PLUS_OP + 8, // SQL_MINUS_OP + 10 // SQL_NEG_OP +}; + +FINLINE FLMUINT getSQLOpPrecedence( + eSQLQueryOperators eOperator) +{ + return( uiSQLOpPrecedenceTable [eOperator - SQL_AND_OP]); +} + +//------------------------------------------------------------------------- +// Desc: Constructor +//------------------------------------------------------------------------- +SQLQuery::SQLQuery() +{ + m_uiLanguage = FLM_US_LANG; + m_pool.poolInit( 1024); + m_pFirstSubQuery = NULL; + m_pLastSubQuery = NULL; + m_pFirstTable = NULL; + m_pLastTable = NULL; + m_bOptimized = FALSE; + m_bScan = FALSE; + m_bScanIndex = FALSE; + m_bEmpty = FALSE; + m_uiIndexNum = 0; + m_bIndexSet = FALSE; + m_pQuery = NULL; + m_pDatabase = NULL; + m_pDb = NULL; + m_pNext = NULL; + m_pPrev = NULL; +} + +//------------------------------------------------------------------------- +// Desc: Destructor +//------------------------------------------------------------------------- +SQLQuery::~SQLQuery() +{ + m_pool.poolFree(); + if (m_pDatabase) + { + m_pDatabase->lockMutex(); + + // Unlink the query from the list off of the F_Database object. + + if (m_pPrev) + { + m_pPrev->m_pNext = m_pNext; + } + else + { + m_pDatabase->m_pFirstSQLQuery = m_pNext; + } + if (m_pNext) + { + m_pNext->m_pPrev = m_pPrev; + } + else + { + m_pDatabase->m_pLastSQLQuery = m_pPrev; + } + m_pDatabase->unlockMutex(); + } +} + +//------------------------------------------------------------------------- +// Desc: Allocate a structure for keeping track of the state of the +// current SQL query. +//------------------------------------------------------------------------- +RCODE SQLQuery::allocParseState( void) +{ + RCODE rc = NE_SFLM_OK; + SQL_PARSE_STATE * pParseState; + + if (!m_pCurrParseState || !m_pCurrParseState->pNext) + { + if (RC_BAD( rc = m_pool.poolCalloc( sizeof( SQL_PARSE_STATE), + (void **)&pParseState))) + { + goto Exit; + } + if ((pParseState->pPrev = m_pCurrParseState) != NULL) + { + m_pCurrParseState->pNext = pParseState; + } + m_pCurrParseState = pParseState; + } + else + { + SQL_PARSE_STATE * pSaveNext; + SQL_PARSE_STATE * pSavePrev; + + m_pCurrParseState = m_pCurrParseState->pNext; + + // Zero out everything except for the prev and next pointers + + pSaveNext = m_pCurrParseState->pNext; + pSavePrev = m_pCurrParseState->pPrev; + f_memset( m_pCurrParseState, 0, sizeof( SQL_PARSE_STATE)); + m_pCurrParseState->pNext = pSaveNext; + m_pCurrParseState->pPrev = pSavePrev; + } + +Exit: + + return( rc); +} + +//------------------------------------------------------------------------- +// Desc: Unlinks a node from its parent and siblings. This routine assumes +// that the test has already been made that the node has a parent. +//------------------------------------------------------------------------- +FSTATIC void sqlUnlinkFromParent( + SQL_NODE * pSQLNode) +{ + flmAssert( pSQLNode->pParent); + if (pSQLNode->pPrevSib) + { + pSQLNode->pPrevSib->pNextSib = pSQLNode->pNextSib; + } + else + { + pSQLNode->pParent->pFirstChild = pSQLNode->pNextSib; + } + if (pSQLNode->pNextSib) + { + pSQLNode->pNextSib->pPrevSib = pSQLNode->pPrevSib; + } + else + { + pSQLNode->pParent->pLastChild = pSQLNode->pPrevSib; + } + + pSQLNode->pParent = NULL; + pSQLNode->pPrevSib = NULL; + pSQLNode->pNextSib = NULL; +} + +//------------------------------------------------------------------------- +// Desc: Links one SQL_NODE as the last child of another. Will unlink the +// child node from any parent it may be linked to. +//------------------------------------------------------------------------- +FSTATIC void sqlLinkLastChild( + SQL_NODE * pParent, + SQL_NODE * pChild + ) +{ + + // If necessary, unlink the child from parent and siblings + + if (pChild->pParent) + { + sqlUnlinkFromParent( pChild); + } + + // Link child as the last child to parent + + pChild->pParent = pParent; + pChild->pNextSib = NULL; + if ((pChild->pPrevSib = pParent->pLastChild) != NULL) + { + pChild->pPrevSib->pNextSib = pChild; + } + else + { + pParent->pFirstChild = pChild; + } + pParent->pLastChild = pChild; +} + +//------------------------------------------------------------------------- +// Desc: Allocate a value node. +//------------------------------------------------------------------------- +RCODE SQLQuery::allocValueNode( + FLMUINT uiValLen, + eSQLValTypes eValType, + SQL_NODE ** ppSQLNode + ) +{ + RCODE rc = NE_SFLM_OK; + SQL_NODE * pSQLNode; + + if (!m_pCurrParseState) + { + if (RC_BAD( rc = allocParseState())) + { + goto Exit; + } + } + + if (m_pCurrParseState->bExpectingLParen) + { + rc = RC_SET( NE_SFLM_Q_EXPECTING_LPAREN); + goto Exit; + } + + if (!expectingOperand()) + { + rc = RC_SET( NE_SFLM_Q_UNEXPECTED_VALUE); + goto Exit; + } + + if (RC_BAD( rc = m_pool.poolCalloc( sizeof( SQL_NODE), + (void **)ppSQLNode))) + { + goto Exit; + } + pSQLNode = *ppSQLNode; + pSQLNode->eNodeType = SQL_VALUE_NODE; + pSQLNode->nd.value.eValType = eValType; + pSQLNode->nd.value.uiFlags = SQL_VAL_IS_CONSTANT; + + // For string and binary data, allocate a buffer. + + if (uiValLen) + { + if (eValType == SQL_UTF8_VAL) + { + if (RC_BAD( rc = m_pool.poolAlloc( uiValLen, + (void **)&pSQLNode->nd.value.val.str.pszStr))) + { + goto Exit; + } + pSQLNode->nd.value.val.str.uiByteLen = uiValLen; + } + else if (eValType == SQL_BINARY_VAL) + { + if (RC_BAD( rc = m_pool.poolAlloc( uiValLen, + (void **)&pSQLNode->nd.value.val.bin.pucValue))) + { + goto Exit; + } + pSQLNode->nd.value.val.bin.uiByteLen = uiValLen; + } + } + + if (m_pCurrParseState->pRootNode) + { + sqlLinkLastChild( m_pCurrParseState->pCurOperatorNode, pSQLNode); + } + else + { + m_pCurrParseState->pRootNode = pSQLNode; + } + m_pCurrParseState->bExpectingOperator = TRUE; + m_pCurrParseState->pLastNode = pSQLNode; + +Exit: + + return( rc); +} + +//------------------------------------------------------------------------- +// Desc: Add an operator to the query expression +//------------------------------------------------------------------------- +RCODE SQLQuery::addOperator( + eSQLQueryOperators eOperator, + FLMUINT uiCompareRules) +{ + RCODE rc = NE_SFLM_OK; + SQL_NODE * pSQLNode; + SQL_NODE * pParentNode; + + if (!m_pCurrParseState) + { + if (RC_BAD( rc = allocParseState())) + { + goto Exit; + } + } + + // If we are expecting a left paren (for a function), that is + // the only thing that is acceptable at this point. + + if (m_pCurrParseState->bExpectingLParen && eOperator != SQL_LPAREN_OP) + { + rc = RC_SET( NE_SFLM_Q_EXPECTING_LPAREN); + goto Exit; + } + + if (eOperator == SQL_MINUS_OP && expectingOperand()) + { + eOperator = SQL_NEG_OP; + } + + switch (eOperator) + { + case SQL_LPAREN_OP: + + // If the operator is a left paren, increment the nesting level + + if (expectingOperator()) + { + rc = RC_SET( NE_SFLM_Q_UNEXPECTED_LPAREN); + goto Exit; + } + m_pCurrParseState->uiNestLevel++; + m_pCurrParseState->bExpectingLParen = FALSE; + goto Exit; + + case SQL_RPAREN_OP: + if (expectingOperand()) + { + rc = RC_SET( NE_SFLM_Q_UNEXPECTED_RPAREN); + goto Exit; + } + if (!m_pCurrParseState->uiNestLevel) + { + rc = RC_SET( NE_SFLM_Q_UNMATCHED_RPAREN); + goto Exit; + } + m_pCurrParseState->uiNestLevel--; + + goto Exit; + + case SQL_NEG_OP: + case SQL_NOT_OP: + if (expectingOperator()) + { + rc = RC_SET( NE_SFLM_Q_EXPECTING_OPERATOR); + goto Exit; + } + break; + + default: + + if (expectingOperand()) + { + rc = RC_SET( NE_SFLM_Q_EXPECTING_OPERAND); + goto Exit; + } + if (!isLegalSQLOperator( eOperator)) + { + rc = RC_SET( NE_SFLM_Q_ILLEGAL_OPERATOR); + goto Exit; + } + + break; + } + + // Cannot set both FLM_COMP_COMPRESS_WHITESPACE and FLM_COMP_NO_WHITESPACE + // in comparison rules. Also, cannot set FLM_COMP_IGNORE_LEADING_SPACE or + // FLM_COMP_IGNORE_TRAILING_SPACE with FLM_COMP_NO_WHITESPACE. + + if ((uiCompareRules & FLM_COMP_NO_WHITESPACE) && + (uiCompareRules & (FLM_COMP_COMPRESS_WHITESPACE | + FLM_COMP_IGNORE_LEADING_SPACE | + FLM_COMP_IGNORE_TRAILING_SPACE))) + { + rc = RC_SET_AND_ASSERT( NE_SFLM_Q_ILLEGAL_COMPARE_RULES); + goto Exit; + } + + // Make a QNODE and find a place for it in the query tree + + if (RC_BAD( rc = m_pool.poolCalloc( sizeof( SQL_NODE), + (void **)&pSQLNode))) + { + goto Exit; + } + pSQLNode->eNodeType = SQL_OPERATOR_NODE; + pSQLNode->nd.op.eOperator = eOperator; + pSQLNode->nd.op.uiCompareRules = uiCompareRules; + pSQLNode->uiNestLevel = m_pCurrParseState->uiNestLevel; + + // Go up the stack until an operator whose nest level or precedence is < + // this one's is encountered, then link this one in as the last child + + pParentNode = m_pCurrParseState->pCurOperatorNode; + while (pParentNode && + (pParentNode->uiNestLevel > pSQLNode->uiNestLevel || + (pParentNode->uiNestLevel == pSQLNode->uiNestLevel && + getSQLOpPrecedence( pParentNode->nd.op.eOperator) >= + getSQLOpPrecedence( eOperator)))) + { + pParentNode = pParentNode->pParent; + } + if (!pParentNode) + { + if (m_pCurrParseState->pRootNode) + { + sqlLinkLastChild( pSQLNode, m_pCurrParseState->pRootNode); + } + m_pCurrParseState->pRootNode = pSQLNode; + } + else if (eOperator == SQL_NOT_OP || eOperator == SQL_NEG_OP) + { + + // Need to treat NOT and NEG as if they were operands. + + // Parent better be an operator. + + flmAssert( pParentNode->eNodeType == SQL_OPERATOR_NODE); + +#ifdef FLM_DEBUG + if (pParentNode->nd.op.eOperator == SQL_NEG_OP || + pParentNode->nd.op.eOperator == SQL_NOT_OP) + { + + // Must have no children. + + flmAssert( pParentNode->pFirstChild == NULL); + } + else + { + + // Must only have one or zero children. + + flmAssert( pParentNode->pFirstChild == pParentNode->pLastChild); + } +#endif + + sqlLinkLastChild( pParentNode, pSQLNode); + flmAssert( !m_pCurrParseState->bExpectingOperator); + } + else + { + + // Parent better be an operator. + + flmAssert( pParentNode->eNodeType == SQL_OPERATOR_NODE); + + // Unlink last child of parent node and replace with this + // new node. The parent node better already have the correct + // number of children, or we are not parsing correctly. + + flmAssert( pParentNode->pFirstChild); + if (pParentNode->nd.op.eOperator == SQL_NEG_OP || + pParentNode->nd.op.eOperator == SQL_NOT_OP) + { + + // Better only be one child. + + flmAssert( !pParentNode->pFirstChild->pNextSib); + + sqlLinkLastChild( pSQLNode, pParentNode->pFirstChild); + } + else + { + + // Better only be two child nodes + + flmAssert( pParentNode->pFirstChild->pNextSib == + pParentNode->pLastChild); + sqlLinkLastChild( pSQLNode, pParentNode->pLastChild); + } + sqlLinkLastChild( pParentNode, pSQLNode); + } + + m_pCurrParseState->pCurOperatorNode = pSQLNode; + m_pCurrParseState->bExpectingOperator = FALSE; + m_pCurrParseState->pLastNode = pSQLNode; + +Exit: + + return( rc); +} + +//------------------------------------------------------------------------- +// Desc: Allocate a node for an operand. +//------------------------------------------------------------------------- +RCODE SQLQuery::allocOperandNode( + eSQLNodeTypes eNodeType, + SQL_NODE ** ppSQLNode) +{ + RCODE rc = NE_SFLM_OK; + SQL_NODE * pSQLNode; + + if (RC_BAD( rc = m_pool.poolCalloc( sizeof( SQL_NODE), + (void **)&pSQLNode))) + { + goto Exit; + } + pSQLNode->eNodeType = eNodeType; + + if (m_pCurrParseState->pRootNode) + { + sqlLinkLastChild( m_pCurrParseState->pCurOperatorNode, pSQLNode); + } + else + { + m_pCurrParseState->pRootNode = pSQLNode; + } + m_pCurrParseState->bExpectingOperator = TRUE; + m_pCurrParseState->pLastNode = *ppSQLNode = pSQLNode; + +Exit: + + return( rc); +} + +//------------------------------------------------------------------------- +// Desc: Add a table to the query. +//------------------------------------------------------------------------- +RCODE SQLQuery::addTable( + FLMUINT uiTableNum, + SQL_TABLE ** ppTable) +{ + RCODE rc = NE_SFLM_OK; + SQL_TABLE * pTable; + + if (!m_pCurrParseState) + { + if (RC_BAD( rc = allocParseState())) + { + goto Exit; + } + } + + // Add or find the table structure for the node. + + pTable = m_pFirstTable; + while (pTable && pTable->uiTableNum != uiTableNum) + { + pTable = pTable->pNext; + } + if (!pTable) + { + if (RC_BAD( rc = m_pool.poolCalloc( sizeof( SQL_TABLE), + (void **)&pTable))) + { + goto Exit; + } + pTable->uiTableNum = uiTableNum; + if ((pTable->pPrev = m_pLastTable) != NULL) + { + m_pLastTable->pNext = pTable; + } + else + { + m_pFirstTable = pTable; + } + m_pLastTable = pTable; + } + if (ppTable) + { + *ppTable = pTable; + } + +Exit: + + return( rc); +} + +//------------------------------------------------------------------------- +// Desc: Add a column name. +//------------------------------------------------------------------------- +RCODE SQLQuery::addColumn( + FLMUINT uiTableNum, + FLMUINT uiColumnNum) +{ + RCODE rc = NE_SFLM_OK; + SQL_NODE * pSQLNode; + SQL_TABLE * pTable; + + if (!m_pCurrParseState) + { + if (RC_BAD( rc = allocParseState())) + { + goto Exit; + } + } + + // Must be expecting an operand + + if (!expectingOperand()) + { + rc = RC_SET( NE_SFLM_Q_UNEXPECTED_COLUMN); + goto Exit; + } + + if (RC_BAD( rc = addTable( uiTableNum, &pTable))) + { + goto Exit; + } + + // Allocate a column node + + if (RC_BAD( rc = allocOperandNode( SQL_COLUMN_NODE, &pSQLNode))) + { + goto Exit; + } + pSQLNode->nd.column.pTable = pTable; + pSQLNode->nd.column.uiColumnNum = uiColumnNum; + +Exit: + + return( rc); +} + +//------------------------------------------------------------------------- +// Desc: Add a FLMUINT64 number constant. +//------------------------------------------------------------------------- +RCODE SQLQuery::addUINT64( + FLMUINT64 ui64Num) +{ + RCODE rc = NE_SFLM_OK; + SQL_NODE * pSQLNode; + + if (!m_pCurrParseState) + { + if (RC_BAD( rc = allocParseState())) + { + goto Exit; + } + } + + // Must be expecting an operand + + if (!expectingOperand()) + { + rc = RC_SET( NE_SFLM_Q_UNEXPECTED_CONSTANT); + goto Exit; + } + + // Allocate a value node + + if (RC_BAD( rc = allocOperandNode( SQL_VALUE_NODE, &pSQLNode))) + { + goto Exit; + } + pSQLNode->nd.value.eValType = SQL_UINT64_VAL; + pSQLNode->nd.value.uiFlags |= SQL_VAL_IS_CONSTANT; + pSQLNode->nd.value.val.ui64Val = ui64Num; + +Exit: + + return( rc); +} + +//------------------------------------------------------------------------- +// Desc: Add a FLMUINT number constant. +//------------------------------------------------------------------------- +RCODE SQLQuery::addUINT( + FLMUINT uiNum) +{ + RCODE rc = NE_SFLM_OK; + SQL_NODE * pSQLNode; + + if (!m_pCurrParseState) + { + if (RC_BAD( rc = allocParseState())) + { + goto Exit; + } + } + + // Must be expecting an operand + + if (!expectingOperand()) + { + rc = RC_SET( NE_SFLM_Q_UNEXPECTED_CONSTANT); + goto Exit; + } + + // Allocate a value node + + if (RC_BAD( rc = allocOperandNode( SQL_VALUE_NODE, &pSQLNode))) + { + goto Exit; + } + pSQLNode->nd.value.eValType = SQL_UINT_VAL; + pSQLNode->nd.value.uiFlags |= SQL_VAL_IS_CONSTANT; + pSQLNode->nd.value.val.uiVal = uiNum; + +Exit: + + return( rc); +} + +//------------------------------------------------------------------------- +// Desc: Add a FLMINT64 number constant. +//------------------------------------------------------------------------- +RCODE SQLQuery::addINT64( + FLMINT64 i64Num) +{ + RCODE rc = NE_SFLM_OK; + SQL_NODE * pSQLNode; + + if (!m_pCurrParseState) + { + if (RC_BAD( rc = allocParseState())) + { + goto Exit; + } + } + + // Must be expecting an operand + + if (!expectingOperand()) + { + rc = RC_SET( NE_SFLM_Q_UNEXPECTED_CONSTANT); + goto Exit; + } + + // Allocate a value node + + if (RC_BAD( rc = allocOperandNode( SQL_VALUE_NODE, &pSQLNode))) + { + goto Exit; + } + pSQLNode->nd.value.eValType = SQL_INT64_VAL; + pSQLNode->nd.value.uiFlags |= SQL_VAL_IS_CONSTANT; + pSQLNode->nd.value.val.i64Val = i64Num; + +Exit: + + return( rc); +} + +//------------------------------------------------------------------------- +// Desc: Add a FLMINT number constant. +//------------------------------------------------------------------------- +RCODE SQLQuery::addINT( + FLMINT iNum) +{ + RCODE rc = NE_SFLM_OK; + SQL_NODE * pSQLNode; + + if (!m_pCurrParseState) + { + if (RC_BAD( rc = allocParseState())) + { + goto Exit; + } + } + + // Must be expecting an operand + + if (!expectingOperand()) + { + rc = RC_SET( NE_SFLM_Q_UNEXPECTED_CONSTANT); + goto Exit; + } + + // Allocate a value node + + if (RC_BAD( rc = allocOperandNode( SQL_VALUE_NODE, &pSQLNode))) + { + goto Exit; + } + pSQLNode->nd.value.eValType = SQL_INT_VAL; + pSQLNode->nd.value.uiFlags |= SQL_VAL_IS_CONSTANT; + pSQLNode->nd.value.val.iVal = iNum; + +Exit: + + return( rc); +} + +//------------------------------------------------------------------------- +// Desc: Add a boolean constant. +//------------------------------------------------------------------------- +RCODE SQLQuery::addBoolean( + FLMBOOL bValue, + FLMBOOL bUnknown) +{ + RCODE rc = NE_SFLM_OK; + SQL_NODE * pSQLNode; + + if (!m_pCurrParseState) + { + if (RC_BAD( rc = allocParseState())) + { + goto Exit; + } + } + + // Must be expecting an operand + + if (!expectingOperand()) + { + rc = RC_SET( NE_SFLM_Q_UNEXPECTED_BOOLEAN); + goto Exit; + } + + // Allocate a value node + + if (RC_BAD( rc = allocOperandNode( SQL_VALUE_NODE, &pSQLNode))) + { + goto Exit; + } + pSQLNode->nd.value.eValType = SQL_BOOL_VAL; + pSQLNode->nd.value.uiFlags |= SQL_VAL_IS_CONSTANT; + if (bUnknown) + { + pSQLNode->nd.value.val.eBool = SQL_UNKNOWN; + } + else if (bValue) + { + pSQLNode->nd.value.val.eBool = SQL_TRUE; + } + else + { + pSQLNode->nd.value.val.eBool = SQL_FALSE; + } + +Exit: + + return( rc); +} + +//------------------------------------------------------------------------- +// Desc: Add a UTF8 string constant. +//------------------------------------------------------------------------- +RCODE SQLQuery::addUTF8String( + const FLMBYTE * pszUTF8Str, + FLMUINT uiStrLen, + FLMUINT uiNumChars) +{ + RCODE rc = NE_SFLM_OK; + SQL_NODE * pSQLNode; + FLMBYTE * pszTmp; + + if (!m_pCurrParseState) + { + if (RC_BAD( rc = allocParseState())) + { + goto Exit; + } + } + + // Must be expecting an operand + + if (!expectingOperand()) + { + rc = RC_SET( NE_SFLM_Q_UNEXPECTED_CONSTANT); + goto Exit; + } + + if (!uiStrLen) + { + uiStrLen = f_strlen( (const char *)pszUTF8Str); + } + + // Allocate a value node + + if (RC_BAD( rc = m_pool.poolCalloc( uiStrLen + 1, (void **)&pszTmp))) + { + goto Exit; + } + if (RC_BAD( rc = allocOperandNode( SQL_VALUE_NODE, &pSQLNode))) + { + goto Exit; + } + pSQLNode->nd.value.eValType = SQL_UTF8_VAL; + pSQLNode->nd.value.uiFlags |= SQL_VAL_IS_CONSTANT; + pSQLNode->nd.value.val.str.pszStr = pszTmp; + pSQLNode->nd.value.val.str.uiByteLen = uiStrLen + 1; + pSQLNode->nd.value.val.str.uiNumChars = uiNumChars; + f_memcpy( pszTmp, pszUTF8Str, uiStrLen); + pszTmp [uiStrLen] = 0; + + // See if there are any wildcards in the string. + + while (*pszTmp) + { + if (*pszTmp == '*') + { + pSQLNode->nd.value.uiFlags |= SQL_VAL_HAS_WILDCARDS; + break; + } + else if (*pszTmp == '\\') + { + + // Skip over whatever comes after a backslash - it is an + // escaped character, so it won't count as a wildcard. + + pszTmp++; + if (!(*pszTmp)) + { + break; + } + } + pszTmp++; + } + +Exit: + + return( rc); +} + +//------------------------------------------------------------------------- +// Desc: Add a binary constant. +//------------------------------------------------------------------------- +RCODE SQLQuery::addBinary( + const FLMBYTE * pucValue, + FLMUINT uiValueLen) +{ + RCODE rc = NE_SFLM_OK; + SQL_NODE * pSQLNode; + FLMBYTE * pucTmp; + + if (!m_pCurrParseState) + { + if (RC_BAD( rc = allocParseState())) + { + goto Exit; + } + } + + // Must be expecting an operand + + if (!expectingOperand()) + { + rc = RC_SET( NE_SFLM_Q_UNEXPECTED_CONSTANT); + goto Exit; + } + + // Allocate a value node + + if (RC_BAD( rc = m_pool.poolCalloc( uiValueLen, (void **)&pucTmp))) + { + goto Exit; + } + if (RC_BAD( rc = allocOperandNode( SQL_VALUE_NODE, &pSQLNode))) + { + goto Exit; + } + pSQLNode->nd.value.eValType = SQL_BINARY_VAL; + pSQLNode->nd.value.uiFlags |= SQL_VAL_IS_CONSTANT; + pSQLNode->nd.value.val.bin.uiByteLen = uiValueLen; + pSQLNode->nd.value.val.bin.pucValue = pucTmp; + f_memcpy( pucTmp, pucValue, uiValueLen); + +Exit: + + return( rc); +} + +//------------------------------------------------------------------------------ +// Desc: Process a token in the expression that begins with an alphabetic +// character. +//------------------------------------------------------------------------------ +RCODE SQLStatement::processAlphaToken( + TABLE_ITEM * pTableList, + FLMBOOL bSelectStatement, + FLMBOOL bUpdateExpression, + SQLQuery * pSqlQuery, + FLMBOOL * pbDone) +{ + RCODE rc = NE_SFLM_OK; + FLMUINT uiSaveLineNum; + FLMUINT uiSaveLineOffset; + FLMUINT uiSaveLineFilePos; + FLMUINT uiSaveLineBytes; + char szToken [MAX_SQL_TOKEN_SIZE + 1]; + FLMUINT uiTokenLineOffset; + TABLE_ITEM * pTableItem; + F_TABLE * pTable; + F_COLUMN * pColumn; + FLMBYTE ucBuffer [200]; + F_DynaBuf dynaBuf( ucBuffer, sizeof( ucBuffer)); + + *pbDone = FALSE; + + if (RC_BAD( rc = getToken( szToken, sizeof( szToken), TRUE, &uiTokenLineOffset))) + { + // Cannot be EOF hit at this point. + flmAssert( rc != NE_SFLM_EOF_HIT); + goto Exit; + } + uiSaveLineNum = m_uiCurrLineNum; + uiSaveLineOffset = uiTokenLineOffset; + uiSaveLineFilePos = m_uiCurrLineFilePos; + uiSaveLineBytes = m_uiCurrLineBytes; + + // If this is a select statement, see we have hit the "ORDER" keyword + // because that signals the beginning of the ORDER BY clause. + // If this is an expression in the SET part of an UPDATE statement, + // see if we have hit the "WHERE" keyword - which would signal the + // end of the criteria. + + if ((bSelectStatement && f_stricmp( szToken, "order") == 0) || + (bUpdateExpression && f_stricmp( szToken, "where") == 0)) + { + if (pSqlQuery->criteriaIsComplete()) + { + + // This essentially pushes the toke back into the stream so that + // it will be handled later. + + m_uiCurrLineOffset = uiTokenLineOffset; + *pbDone = TRUE; + goto Exit; + } + } + else if (f_stricmp( szToken, "and") == 0 || + f_stricmp( szToken, "or") == 0) + { + if (pSqlQuery->expectingOperator()) + { + + // Treat as AND operator + + rc = pSqlQuery->addOperator( + (eSQLQueryOperators)(f_toupper( szToken[0]) == 'A' + ? SQL_AND_OP + : SQL_OR_OP), 0); + goto Exit; + } + } + else if (f_stricmp( szToken, "not") == 0) + { + + // Interestingly, NOT operators should only appear when + // we are expecting an operand. + + if (pSqlQuery->expectingOperator()) + { + setErrInfo( uiSaveLineNum, + uiSaveLineOffset, + SQL_ERR_UNEXPECTED_NOT_OPERATOR, + uiSaveLineFilePos, + uiSaveLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } + else + { + rc = pSqlQuery->addOperator( SQL_NOT_OP, 0); + goto Exit; + } + } + else if (f_stricmp( szToken, "true") == 0 || + f_stricmp( szToken, "false") == 0 || + f_stricmp( szToken, "unknown") == 0) + { + if (pSqlQuery->expectingOperand()) + { + + // Treat as AND operator + + rc = pSqlQuery->addBoolean( + (FLMBOOL)(f_toupper( szToken[0]) == 'T' + ? TRUE + : FALSE), + (FLMBOOL)(f_toupper( szToken[0]) == 'U' + ? TRUE + : FALSE)); + goto Exit; + } + } + else if (f_stricmp( szToken, "binary") == 0) + { + if (pSqlQuery->expectingOperand()) + { + if (RC_BAD( rc = haveToken( "(", TRUE))) + { + if (rc != NE_SFLM_NOT_FOUND && rc != NE_SFLM_EOF_HIT) + { + goto Exit; + } + } + else + { + dynaBuf.truncateData( 0); + if (RC_BAD( rc = getBinaryValue( &dynaBuf))) + { + goto Exit; + } + rc = pSqlQuery->addBinary( dynaBuf.getBufferPtr(), + dynaBuf.getDataLength()); + goto Exit; + } + } + } + + // At this point, the only thing left is for it to be a column name + // or a tablename.columnname. + + // If we are expecting an operator, it is an error for + // a column name to be specified. + + if (pSqlQuery->expectingOperator()) + { + setErrInfo( uiSaveLineNum, + uiSaveLineOffset, + SQL_ERR_EXPECTING_OPERATOR, + uiSaveLineFilePos, + uiSaveLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } + + // If we fall through to here, treat the token as a column + // name or a table alias. See if there is a period for the + // next token. + + pTableItem = NULL; + if (RC_BAD( rc = haveToken( ".", TRUE))) + { + F_COLUMN * pFoundColumn; + F_TABLE * pFoundTable; + + if (rc != NE_SFLM_NOT_FOUND && rc != NE_SFLM_EOF_HIT) + { + goto Exit; + } + rc = NE_SFLM_OK; + + // See if the column name is valid for more than one + // of the tables. If so, it is ambiguous. + + pFoundColumn = NULL; + pFoundTable = NULL; + for (pTableItem = pTableList; pTableItem->uiTableNum; pTableItem++) + { + pTable = m_pDb->m_pDict->getTable( pTableItem->uiTableNum); + if ((pColumn = m_pDb->m_pDict->findColumn( pTable, szToken)) != NULL) + { + // Column name is ambiguous - belongs to more than one of + // the tables specified. + + if (pFoundColumn) + { + setErrInfo( uiSaveLineNum, + uiSaveLineOffset, + SQL_ERR_AMBIGUOUS_COLUMN_NAME, + uiSaveLineFilePos, + uiSaveLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } + pFoundColumn = pColumn; + pFoundTable = pTable; + } + } + + if (!pFoundColumn) + { + setErrInfo( uiSaveLineNum, + uiSaveLineOffset, + SQL_ERR_INVALID_WHERE_COLUMN, + uiSaveLineFilePos, + uiSaveLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } + if (RC_BAD( rc = pSqlQuery->addColumn( pFoundTable->uiTableNum, + pFoundColumn->uiColumnNum))) + { + goto Exit; + } + } + else + { + + // Token better be a table alias. + + for (pTableItem = pTableList; pTableItem->uiTableNum; pTableItem++) + { + if (f_stricmp( pTableItem->pszTableAlias, szToken) == 0) + { + break; + } + } + if (!pTableItem->uiTableNum) + { + setErrInfo( uiSaveLineNum, + uiSaveLineOffset, + SQL_ERR_INVALID_WHERE_TABLE, + uiSaveLineFilePos, + uiSaveLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } + pTable = m_pDb->m_pDict->getTable( pTableItem->uiTableNum); + + // Get next token - better be a column name for the table. + + if (RC_BAD( rc = getToken( szToken, sizeof( szToken), + TRUE, &uiTokenLineOffset))) + { + // Cannot be EOF hit at this point. + flmAssert( rc != NE_SFLM_EOF_HIT); + goto Exit; + } + if ((pColumn = m_pDb->m_pDict->findColumn( pTable, szToken)) == NULL) + { + setErrInfo( uiSaveLineNum, + uiSaveLineOffset, + SQL_ERR_INVALID_WHERE_COLUMN, + uiSaveLineFilePos, + uiSaveLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } + if (RC_BAD( rc = pSqlQuery->addColumn( pTable->uiTableNum, + pColumn->uiColumnNum))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +//------------------------------------------------------------------------------ +// Desc: Process the WHERE clause of a SELECT, DELETE, or UPDATE statement. +// The "WHERE" keyword has already been parsed. If it is the +// SELECT statement, the "ORDER BY" keyword can terminate the clause. +//------------------------------------------------------------------------------ +RCODE SQLStatement::parseCriteria( + TABLE_ITEM * pTableList, + FLMBOOL bSelectStatement, + FLMBOOL bUpdateExpression, + SQLQuery * pSqlQuery) +{ + RCODE rc = NE_SFLM_OK; + char cChar; + FLMUINT64 ui64Num; + FLMBOOL bNeg; + FLMBYTE ucBuffer [200]; + F_DynaBuf dynaBuf( ucBuffer, sizeof( ucBuffer)); + FLMUINT uiNumChars; + eSQLQueryOperators eOperator; + FLMUINT uiNestedParens = 0; + FLMUINT uiTokenLineOffset; + + // Process tokens + + for (;;) + { + if (RC_BAD( rc = skipWhitespace( FALSE))) + { + if (rc == NE_SFLM_EOF_HIT) + { + if (!pSqlQuery->criteriaIsComplete()) + { + goto Incomplete_Query; + } + else + { + rc = NE_SFLM_OK; + } + } + goto Exit; + } + + // See if we can figure out what kind of token it is. + + cChar = (char)m_pucCurrLineBuf [m_uiCurrLineOffset]; + uiTokenLineOffset = m_uiCurrLineOffset; + + switch (cChar) + { + case '"': + case '\'': + + // If we are expecting an operator, it is an error for + // a string constant to be specified. + + if (pSqlQuery->expectingOperator()) + { + setErrInfo( m_uiCurrLineNum, + uiTokenLineOffset, + SQL_ERR_EXPECTING_OPERATOR, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } + + // Better be a quoted string. + + dynaBuf.truncateData( 0); + if (RC_BAD( rc = getUTF8String( FALSE, FALSE, NULL, 0, NULL, + &uiNumChars, &dynaBuf))) + { + goto Exit; + } + + if (RC_BAD( rc = pSqlQuery->addUTF8String( dynaBuf.getBufferPtr(), + dynaBuf.getDataLength() - 1, + uiNumChars))) + { + goto Exit; + } + + break; + case '+': + + // A plus in front of an operand may be ignored. + + m_uiCurrLineOffset++; + if (pSqlQuery->expectingOperand()) + { + continue; + } + eOperator = SQL_PLUS_OP; + goto Add_Operator; + case '-': + eOperator = (pSqlQuery->expectingOperand()) + ? SQL_NEG_OP + : SQL_MINUS_OP; + m_uiCurrLineOffset++; + goto Add_Operator; + case '*': + eOperator = SQL_MULT_OP; + m_uiCurrLineOffset++; + goto Test_If_Expecting_Operand; + case '/': + eOperator = SQL_DIV_OP; + m_uiCurrLineOffset++; + goto Test_If_Expecting_Operand; + case '%': + eOperator = SQL_MOD_OP; + m_uiCurrLineOffset++; + goto Test_If_Expecting_Operand; + case '(': + if (pSqlQuery->expectingOperator()) + { + setErrInfo( m_uiCurrLineNum, + uiTokenLineOffset, + SQL_ERR_UNEXPECTED_LPAREN, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } + eOperator = SQL_LPAREN_OP; + m_uiCurrLineOffset++; + uiNestedParens++; + goto Add_Operator; + case ')': + if (pSqlQuery->expectingOperand()) + { + setErrInfo( m_uiCurrLineNum, + uiTokenLineOffset, + SQL_ERR_UNEXPECTED_RPAREN, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } + else if (!uiNestedParens) + { + setErrInfo( m_uiCurrLineNum, + uiTokenLineOffset, + SQL_ERR_UNMATCHED_RPAREN, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } + eOperator = SQL_RPAREN_OP; + m_uiCurrLineOffset++; + uiNestedParens--; + break; + case '!': + if (pSqlQuery->expectingOperator()) + { + setErrInfo( m_uiCurrLineNum, + uiTokenLineOffset, + SQL_ERR_UNEXPECTED_NOT_OPERATOR, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } + m_uiCurrLineOffset++; + goto Add_Operator; + case '=': + eOperator = SQL_EQ_OP; + if (m_uiCurrLineOffset + 1 < m_uiCurrLineBytes && + m_pucCurrLineBuf [m_uiCurrLineOffset + 1] == '&') + { + m_uiCurrLineOffset += 2; + } + else + { + m_uiCurrLineOffset++; + } + goto Test_If_Expecting_Operand; + case '&': + if (m_uiCurrLineOffset + 1 < m_uiCurrLineBytes && + m_pucCurrLineBuf [m_uiCurrLineOffset + 1] == '&') + { + eOperator = SQL_AND_OP; + m_uiCurrLineOffset += 2; + } + else + { + eOperator = SQL_BITAND_OP; + m_uiCurrLineOffset++; + } + goto Test_If_Expecting_Operand; + case '|': + if (m_uiCurrLineOffset + 1 < m_uiCurrLineBytes && + m_pucCurrLineBuf [m_uiCurrLineOffset + 1] == '|') + { + eOperator = SQL_OR_OP; + m_uiCurrLineOffset += 2; + } + else + { + eOperator = SQL_BITOR_OP; + m_uiCurrLineOffset++; + } + goto Test_If_Expecting_Operand; + case '^': + eOperator = SQL_BITXOR_OP; + m_uiCurrLineOffset++; + goto Test_If_Expecting_Operand; + +Test_If_Expecting_Operand: + if (pSqlQuery->expectingOperand()) + { + setErrInfo( m_uiCurrLineNum, + uiTokenLineOffset, + SQL_ERR_INVALID_OPERAND, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } +Add_Operator: + if (RC_BAD( rc = pSqlQuery->addOperator( eOperator, 0))) + { + goto Exit; + } + break; + + default: + if ((cChar >= 'a' && cChar <= 'z') || (cChar >= 'A' && cChar <= 'Z')) + { + FLMBOOL bDone; + + if (RC_BAD( rc = processAlphaToken( pTableList, + bSelectStatement, bUpdateExpression, + pSqlQuery, &bDone))) + { + goto Exit; + } + if (bDone) + { + goto Exit; + } + } + else if (cChar >= '0' && cChar <= '9') + { + + // If we are expecting an operator, it is an error for + // a number to be specified. + + if (pSqlQuery->expectingOperator()) + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset, + SQL_ERR_EXPECTING_OPERATOR, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } + + // Better be a number. + + if (RC_BAD( rc = getNumber( FALSE, &ui64Num, &bNeg, FALSE))) + { + goto Exit; + } + if (RC_BAD( rc = pSqlQuery->addNumber( ui64Num, bNeg))) + { + goto Exit; + } + } + else if (cChar == ',' && bUpdateExpression && + pSqlQuery->criteriaIsComplete()) + { + + // Comma is left in the stream, so it can be handled by + // the caller. + + goto Exit; + } + else + { +Incomplete_Query: + if (pSqlQuery->expectingOperator()) + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset, + SQL_ERR_EXPECTING_OPERATOR, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } + else + { + flmAssert( pSqlQuery->expectingOperand()); + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset, + SQL_ERR_INVALID_OPERAND, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } + } + break; + } + } + +Exit: + + return( rc); +} +