Files
mars-flaim/sql/src/updaterow.cpp

1333 lines
30 KiB
C++

//------------------------------------------------------------------------------
// 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"
static const char * gv_setExprTerminators [3] =
{
"where",
",",
NULL
};
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;
// Check for a missing value - return a NULL.
if (pSqlValue->eValType == SQL_MISSING_VAL)
{
pColValue->uiValueLen = 0;
pColValue->pucColumnValue = NULL;
goto Exit;
}
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 * pSqlValue;
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 = sqlEvalCriteria( this,
pSqlQuery->m_pQuery,
&pSqlValue,
&tmpPool, pRow,
m_pDatabase->m_uiDefaultLanguage)))
{
goto Exit;
}
pColumn = m_pDict->getColumn( pTable, pColSet->uiColumnNum);
if (RC_BAD( rc = convertValueToStorageFormat( pSqlValue,
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: Parse the columns that are to be set in the UPDATE statement.
//------------------------------------------------------------------------------
RCODE SQLStatement::parseSetColumns(
TABLE_ITEM * pTableList,
COLUMN_SET ** ppFirstColumnSet,
COLUMN_SET ** ppLastColumnSet,
FLMUINT * puiNumColumnsToSet,
FLMBOOL * pbHadWhere)
{
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;
*pbHadWhere = FALSE;
// 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 = getToken( szToken, sizeof( szToken), TRUE,
&uiTokenLineOffset, NULL)))
{
if (rc == NE_SFLM_EOF_HIT)
{
rc = NE_SFLM_OK;
break;
}
goto Exit;
}
else if (f_stricmp( szToken, "where") == 0)
{
*pbHadWhere = TRUE;
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;
}
}
else
{
const char * pszTerminator;
if (RC_BAD( rc = parseCriteria( pTableList,
&gv_setExprTerminators [0], TRUE,
&pszTerminator, pSqlQuery)))
{
goto Exit;
}
// Strip out NOT operators, resolve constant arithmetic expressions,
// and weed out boolean constants, but do not flatten the AND
// and OR operators in the query tree.
if (RC_BAD( rc = pSqlQuery->reduceTree( FALSE)))
{
goto Exit;
}
// Next token should either be a comma or the WHERE keyword, or we
// should have hit EOF. pszTerminator will return NULL if EOF was
// hit.
if (!pszTerminator)
{
break;
}
if (f_stricmp( pszTerminator, "where") == 0)
{
*pbHadWhere = TRUE;
break;
}
}
}
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;
FLMBOOL bHadWhere;
// 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 <search criteria>]
// 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,
&bHadWhere)))
{
goto Exit;
}
// See if we have a WHERE clause
if (!bHadWhere)
{
if (RC_BAD( rc = sqlQuery.addTable( pTable->uiTableNum, NULL)))
{
goto Exit;
}
}
else
{
if (RC_BAD( rc = parseCriteria( &tableList [0], NULL, TRUE, NULL, &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);
}