git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@504 0109f412-320b-0410-ab79-c3e0c5ffbbe6
1581 lines
32 KiB
C++
1581 lines
32 KiB
C++
// Desc: This module contains routines for doing database updates
|
|
//
|
|
// 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"
|
|
|
|
/****************************************************************************
|
|
Desc: Constructor
|
|
****************************************************************************/
|
|
SQLStatement::SQLStatement()
|
|
{
|
|
m_fnStatus = NULL;
|
|
m_pvCallbackData = NULL;
|
|
m_tmpPool.poolInit( 4096);
|
|
m_pucCurrLineBuf = NULL;
|
|
m_uiCurrLineBufMaxBytes = 0;
|
|
m_pXml = NULL;
|
|
m_pColumnValues = &m_columnValues [0];
|
|
m_uiColumnValueArraySize = SQL_DEFAULT_COLUMNS;
|
|
resetStatement();
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Destructor
|
|
****************************************************************************/
|
|
SQLStatement::~SQLStatement()
|
|
{
|
|
resetStatement();
|
|
|
|
if( m_pucCurrLineBuf)
|
|
{
|
|
f_free( &m_pucCurrLineBuf);
|
|
}
|
|
|
|
if (m_pColumnValues != &m_columnValues [0])
|
|
{
|
|
f_free( &m_pColumnValues);
|
|
}
|
|
m_tmpPool.poolFree();
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Resets member variables so the object can be reused
|
|
****************************************************************************/
|
|
void SQLStatement::resetStatement( void)
|
|
{
|
|
m_uiCurrLineNum = 0;
|
|
m_uiCurrLineOffset = 0;
|
|
m_ucUngetByte = 0;
|
|
m_uiCurrLineFilePos = 0;
|
|
m_uiCurrLineBytes = 0;
|
|
m_pStream = NULL;
|
|
m_uiFlags = 0;
|
|
m_pDb = NULL;
|
|
m_pTable = NULL;
|
|
m_uiNumColumnValues = 0;
|
|
if (m_pXml)
|
|
{
|
|
m_pXml->Release();
|
|
m_pXml = NULL;
|
|
}
|
|
f_memset( &m_sqlStats, 0, sizeof( SQL_STATS));
|
|
|
|
m_tmpPool.poolReset( NULL);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Initializes the SQL statement object (allocates buffers, etc.)
|
|
****************************************************************************/
|
|
RCODE SQLStatement::setupStatement( void)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
|
|
resetStatement();
|
|
|
|
if (RC_BAD( rc = FlmGetXMLObject( &m_pXml)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: See if the current line has the specified token in it starting
|
|
from the current offset.
|
|
****************************************************************************/
|
|
FLMBOOL SQLStatement::lineHasToken(
|
|
const char * pszToken)
|
|
{
|
|
FLMUINT uiOffset;
|
|
|
|
uiOffset = m_uiCurrLineOffset;
|
|
while (uiOffset < m_uiCurrLineBytes)
|
|
{
|
|
if (m_pucCurrLineBuf [uiOffset] != (char)(*pszToken))
|
|
{
|
|
|
|
// Do NOT change m_uiCurrLineOffset if we return FALSE.
|
|
|
|
return( FALSE);
|
|
}
|
|
pszToken++;
|
|
uiOffset++;
|
|
if (*pszToken == 0)
|
|
{
|
|
m_uiCurrLineOffset = uiOffset;
|
|
return( TRUE);
|
|
}
|
|
}
|
|
return( FALSE);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Get next byte from input stream.
|
|
****************************************************************************/
|
|
RCODE SQLStatement::getByte(
|
|
FLMBYTE * pucByte)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
|
|
if (m_ucUngetByte)
|
|
{
|
|
*pucByte = m_ucUngetByte;
|
|
m_ucUngetByte = 0;
|
|
}
|
|
else
|
|
{
|
|
if( RC_BAD( rc = m_pStream->read( (char *)pucByte, 1, NULL)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
m_sqlStats.uiChars++;
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Reads next line from the input stream.
|
|
****************************************************************************/
|
|
RCODE SQLStatement::getLine( void)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
FLMBYTE ucBytes [4];
|
|
FLMUINT uiNumBytes;
|
|
FLMUINT uiLoop;
|
|
|
|
m_uiCurrLineBytes = 0;
|
|
m_uiCurrLineOffset = 0;
|
|
m_uiCurrLineFilePos = m_sqlStats.uiChars;
|
|
|
|
for (;;)
|
|
{
|
|
if( RC_BAD( rc = getByte( &ucBytes [0])))
|
|
{
|
|
if (rc == NE_SFLM_EOF_HIT)
|
|
{
|
|
if (m_uiCurrLineBytes)
|
|
{
|
|
rc = NE_SFLM_OK;
|
|
}
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
// Keep count of the characters.
|
|
|
|
if( m_fnStatus && (m_sqlStats.uiChars % 1024) == 0)
|
|
{
|
|
m_fnStatus( SQL_PARSE_STATS,
|
|
(void *)&m_sqlStats, NULL, NULL, m_pvCallbackData);
|
|
}
|
|
|
|
// Convert CRLF->CR
|
|
|
|
if( ucBytes [0] == ASCII_CR)
|
|
{
|
|
if( RC_BAD( rc = getByte( &ucBytes [0])))
|
|
{
|
|
if (rc == NE_SFLM_EOF_HIT)
|
|
{
|
|
rc = NE_SFLM_OK;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if( ucBytes [0] != ASCII_NEWLINE)
|
|
{
|
|
ungetByte( ucBytes [0]);
|
|
}
|
|
|
|
// End of the line
|
|
|
|
break;
|
|
}
|
|
else if (ucBytes [0] == ASCII_NEWLINE)
|
|
{
|
|
|
|
// End of the line
|
|
|
|
break;
|
|
}
|
|
|
|
if( ucBytes [0] <= 0x7F)
|
|
{
|
|
uiNumBytes = 1;
|
|
}
|
|
else
|
|
{
|
|
|
|
if( RC_BAD( rc = getByte( &ucBytes [1])))
|
|
{
|
|
if (rc == NE_SFLM_EOF_HIT)
|
|
{
|
|
rc = RC_SET( NE_SFLM_BAD_UTF8);
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
if( (ucBytes [1] >> 6) != 0x02)
|
|
{
|
|
rc = RC_SET( NE_SFLM_BAD_UTF8);
|
|
goto Exit;
|
|
}
|
|
|
|
if( (ucBytes [0] >> 5) == 0x06)
|
|
{
|
|
uiNumBytes = 2;
|
|
}
|
|
else
|
|
{
|
|
if( RC_BAD( rc = getByte( &ucBytes [2])))
|
|
{
|
|
if (rc == NE_SFLM_EOF_HIT)
|
|
{
|
|
rc = RC_SET( NE_SFLM_BAD_UTF8);
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
if( (ucBytes [2] >> 6) != 0x02 || (ucBytes [0] >> 4) != 0x0E)
|
|
{
|
|
rc = RC_SET( NE_SFLM_BAD_UTF8);
|
|
goto Exit;
|
|
}
|
|
uiNumBytes = 3;
|
|
}
|
|
}
|
|
|
|
// We have a character, add it to the current line.
|
|
|
|
if (m_uiCurrLineBytes + uiNumBytes > m_uiCurrLineBufMaxBytes)
|
|
{
|
|
// Allocate more space for the line buffer
|
|
|
|
if (RC_BAD( rc = f_realloc( m_uiCurrLineBufMaxBytes + 512,
|
|
&m_pucCurrLineBuf)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
m_uiCurrLineBufMaxBytes += 512;
|
|
}
|
|
for (uiLoop = 0; uiLoop < uiNumBytes; uiLoop++)
|
|
{
|
|
m_pucCurrLineBuf [m_uiCurrLineBytes++] = ucBytes [uiLoop];
|
|
}
|
|
}
|
|
|
|
// Increment the line count
|
|
|
|
m_uiCurrLineNum++;
|
|
m_sqlStats.uiLines++;
|
|
if( m_fnStatus && (m_sqlStats.uiLines % 100) == 0)
|
|
{
|
|
m_fnStatus( SQL_PARSE_STATS,
|
|
(void *)&m_sqlStats, NULL, NULL, m_pvCallbackData);
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Skips any whitespace characters in the input stream
|
|
****************************************************************************/
|
|
RCODE SQLStatement::skipWhitespace(
|
|
FLMBOOL bRequired)
|
|
{
|
|
FLMBYTE ucChar;
|
|
FLMUINT uiCount = 0;
|
|
RCODE rc = NE_SFLM_OK;
|
|
|
|
for( ;;)
|
|
{
|
|
if ((ucChar = getChar()) == 0)
|
|
{
|
|
uiCount++;
|
|
if (RC_BAD( rc = getLine()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (ucChar != ASCII_SPACE && ucChar != ASCII_TAB)
|
|
{
|
|
ungetChar();
|
|
break;
|
|
}
|
|
uiCount++;
|
|
}
|
|
|
|
if( !uiCount && bRequired)
|
|
{
|
|
setErrInfo( m_uiCurrLineNum,
|
|
m_uiCurrLineOffset,
|
|
SQL_ERR_EXPECTING_WHITESPACE,
|
|
m_uiCurrLineFilePos,
|
|
m_uiCurrLineBytes);
|
|
rc = RC_SET( NE_SFLM_INVALID_SQL);
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Desc: Parse a table, column, or index name.
|
|
//------------------------------------------------------------------------------
|
|
RCODE SQLStatement::getName(
|
|
char * pszName,
|
|
FLMUINT uiNameBufSize,
|
|
FLMUINT * puiNameLen)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
FLMUINT uiCharCount = 0;
|
|
FLMBYTE ucChar;
|
|
|
|
// Always leave room for a null terminating character.
|
|
|
|
uiNameBufSize--;
|
|
|
|
// Get the first character - must be between A and Z
|
|
|
|
ucChar = getChar();
|
|
|
|
if ((ucChar >= 'a' && ucChar <= 'z') ||
|
|
(ucChar >= 'A' && ucChar <= 'Z'))
|
|
{
|
|
*pszName = (char)ucChar;
|
|
uiCharCount++;
|
|
}
|
|
else
|
|
{
|
|
setErrInfo( m_uiCurrLineNum,
|
|
m_uiCurrLineOffset - 1,
|
|
SQL_ERR_ILLEGAL_TABLE_NAME_CHAR,
|
|
m_uiCurrLineFilePos,
|
|
m_uiCurrLineBytes);
|
|
rc = RC_SET( NE_SFLM_INVALID_SQL);
|
|
goto Exit;
|
|
}
|
|
|
|
// Cannot go off of the current line
|
|
|
|
for (;;)
|
|
{
|
|
if ((ucChar = getChar()) == 0)
|
|
{
|
|
break;
|
|
}
|
|
if ((ucChar >= 'a' && ucChar <= 'z') ||
|
|
(ucChar >= 'A' && ucChar <= 'Z') ||
|
|
(ucChar >= '0' && ucChar <= '9') ||
|
|
(ucChar == '_'))
|
|
{
|
|
if (uiCharCount >= uiNameBufSize)
|
|
{
|
|
setErrInfo( m_uiCurrLineNum,
|
|
m_uiCurrLineOffset - 1,
|
|
SQL_ERR_TABLE_NAME_TOO_LONG,
|
|
m_uiCurrLineFilePos,
|
|
m_uiCurrLineBytes);
|
|
rc = RC_SET( NE_SFLM_INVALID_SQL);
|
|
goto Exit;
|
|
}
|
|
pszName [uiCharCount++] = (char)ucChar;
|
|
}
|
|
else
|
|
{
|
|
ungetChar();
|
|
break;
|
|
}
|
|
}
|
|
|
|
pszName [uiCharCount] = 0;
|
|
|
|
Exit:
|
|
|
|
if (puiNameLen)
|
|
{
|
|
*puiNameLen = uiCharCount;
|
|
}
|
|
return( rc);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Desc: Parse the table name for the current statement. Make sure it is valid.
|
|
//------------------------------------------------------------------------------
|
|
RCODE SQLStatement::getTableName(
|
|
FLMBOOL bMustExist)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
char szTableName [MAX_SQL_NAME_LEN + 1];
|
|
FLMUINT uiTableNameLen;
|
|
|
|
if (RC_BAD( rc = getName( szTableName, sizeof( szTableName), &uiTableNameLen)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// See if the table name is defined
|
|
|
|
if (RC_BAD( rc = m_pDb->m_pDict->getTable( szTableName, &m_pTable, TRUE)))
|
|
{
|
|
if (rc != NE_SFLM_BAD_TABLE)
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (bMustExist)
|
|
{
|
|
setErrInfo( m_uiCurrLineNum,
|
|
m_uiCurrLineOffset - 1,
|
|
SQL_ERR_UNDEFINED_TABLE,
|
|
m_uiCurrLineFilePos,
|
|
m_uiCurrLineBytes);
|
|
rc = RC_SET( NE_SFLM_INVALID_SQL);
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
rc = NE_SFLM_OK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!bMustExist)
|
|
{
|
|
setErrInfo( m_uiCurrLineNum,
|
|
m_uiCurrLineOffset - 1,
|
|
SQL_ERR_TABLE_ALREADY_DEFINED,
|
|
m_uiCurrLineFilePos,
|
|
m_uiCurrLineBytes);
|
|
rc = RC_SET( NE_SFLM_INVALID_SQL);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Desc: Reallocate the column value array if needed.
|
|
//------------------------------------------------------------------------------
|
|
RCODE SQLStatement::allocColumnValueArray(
|
|
FLMUINT uiNumColumnsNeeded)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
|
|
if (uiNumColumnsNeeded > m_uiColumnValueArraySize)
|
|
{
|
|
F_COLUMN_VALUE * pNewArray;
|
|
|
|
// Increase the array size by at least 20.
|
|
|
|
uiNumColumnsNeeded += 20;
|
|
if (m_pColumnValues == &m_columnValues [0])
|
|
{
|
|
if (RC_BAD( rc = f_alloc( sizeof( F_COLUMN_VALUE) * uiNumColumnsNeeded,
|
|
&pNewArray)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (m_uiNumColumnValues)
|
|
{
|
|
f_memcpy( pNewArray, m_pColumnValues,
|
|
m_uiNumColumnValues * sizeof( F_COLUMN_VALUE));
|
|
}
|
|
m_pColumnValues = pNewArray;
|
|
}
|
|
else
|
|
{
|
|
if (RC_BAD( rc = f_realloc( sizeof( F_COLUMN_VALUE) * uiNumColumnsNeeded,
|
|
&m_pColumnValues)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
m_uiColumnValueArraySize = uiNumColumnsNeeded;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Desc: Parse a string value from the input stream.
|
|
//------------------------------------------------------------------------------
|
|
RCODE SQLStatement::getStringValue(
|
|
F_COLUMN_VALUE * pColumnValue)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
FLMBYTE ucChar;
|
|
FLMBYTE ucQuoteChar = 0;
|
|
FLMBOOL bEscaped = FALSE;
|
|
FLMBYTE szTmpBuf [300];
|
|
F_DynaBuf dynaBuf( szTmpBuf, sizeof( szTmpBuf));
|
|
FLMUINT uiNumChars = 0;
|
|
FLMBYTE * pucValue;
|
|
FLMUINT uiSenLen;
|
|
|
|
// Leading white space has already been skipped.
|
|
|
|
// See if we have a quote character.
|
|
|
|
ucChar = getChar();
|
|
if (ucChar == '"' || ucChar == '\'')
|
|
{
|
|
ucQuoteChar = ucChar;
|
|
}
|
|
else
|
|
{
|
|
setErrInfo( m_uiCurrLineNum,
|
|
m_uiCurrLineOffset,
|
|
SQL_ERR_EXPECTING_QUOTE_CHAR,
|
|
m_uiCurrLineFilePos,
|
|
m_uiCurrLineBytes);
|
|
rc = RC_SET( NE_SFLM_INVALID_SQL);
|
|
goto Exit;
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
// Should not hit the end of the line if quoted.
|
|
|
|
if ((ucChar = getChar()) == 0)
|
|
{
|
|
setErrInfo( m_uiCurrLineNum,
|
|
m_uiCurrLineOffset,
|
|
SQL_ERR_MISSING_QUOTE,
|
|
m_uiCurrLineFilePos,
|
|
m_uiCurrLineBytes);
|
|
rc = RC_SET( NE_SFLM_INVALID_SQL);
|
|
goto Exit;
|
|
}
|
|
if (bEscaped)
|
|
{
|
|
// Can only escape backslash (the escape character), quotes, and
|
|
// a few other characters.
|
|
|
|
if (ucChar == 'n')
|
|
{
|
|
ucChar = ASCII_NEWLINE;
|
|
}
|
|
else if (ucChar == 't')
|
|
{
|
|
ucChar = ASCII_TAB;
|
|
}
|
|
else if (ucChar == 'r')
|
|
{
|
|
ucChar = ASCII_CR;
|
|
}
|
|
else if (ucChar == '\'' || ucChar == '"')
|
|
{
|
|
}
|
|
else
|
|
{
|
|
setErrInfo( m_uiCurrLineNum,
|
|
m_uiCurrLineOffset,
|
|
SQL_ERR_INVALID_ESCAPED_CHARACTER,
|
|
m_uiCurrLineFilePos,
|
|
m_uiCurrLineBytes);
|
|
rc = RC_SET( NE_SFLM_INVALID_SQL);
|
|
goto Exit;
|
|
}
|
|
|
|
// Save the escaped character to the buffer.
|
|
|
|
if (RC_BAD( rc = dynaBuf.appendByte( ucChar)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
uiNumChars++;
|
|
}
|
|
else if (ucChar == '\\')
|
|
{
|
|
bEscaped = TRUE;
|
|
}
|
|
else if (ucChar == ucQuoteChar)
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
|
|
// Save the character to our buffer.
|
|
|
|
if (RC_BAD( rc = dynaBuf.appendByte( ucChar)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Handle multi-byte UTF8 characters. The getLine() method has
|
|
// already checked for valid UTF8, so that is all we should be
|
|
// seeing here - thus the asserts.
|
|
|
|
if (ucChar > 0x7F)
|
|
{
|
|
|
|
// It is at least two bytes.
|
|
|
|
ucChar = getChar();
|
|
flmAssert( (ucChar >> 6) == 0x02);
|
|
if (RC_BAD( rc = dynaBuf.appendByte( ucChar)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// See if it is three bytes.
|
|
|
|
if ((ucChar >> 5) != 0x06)
|
|
{
|
|
ucChar = getChar();
|
|
flmAssert( (ucChar >> 6) == 0x02);
|
|
if (RC_BAD( rc = dynaBuf.appendByte( ucChar)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
uiNumChars++;
|
|
}
|
|
}
|
|
|
|
// Add a null terminating byte
|
|
|
|
if (RC_BAD( rc = dynaBuf.appendByte( 0)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Allocate space for the UTF8 string.
|
|
|
|
uiSenLen = f_getSENByteCount( uiNumChars);
|
|
pColumnValue->uiValueLen = dynaBuf.getDataLength() + uiSenLen;
|
|
if (RC_BAD( rc = m_tmpPool.poolAlloc( pColumnValue->uiValueLen,
|
|
(void **)&pucValue)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
pColumnValue->pucColumnValue = pucValue;
|
|
f_encodeSEN( uiNumChars, &pucValue);
|
|
|
|
// Copy the string from the dynaBuf to the column.
|
|
|
|
f_memcpy( pucValue, dynaBuf.getBufferPtr(), dynaBuf.getDataLength());
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Desc: Parse a numeric value from the input stream.
|
|
//------------------------------------------------------------------------------
|
|
RCODE SQLStatement::getNumberValue(
|
|
F_COLUMN_VALUE * pColumnValue)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
FLMBYTE ucChar;
|
|
FLMUINT64 ui64Value = 0;
|
|
FLMBOOL bNeg = FALSE;
|
|
FLMBOOL bHex = FALSE;
|
|
FLMUINT uiDigitCount = 0;
|
|
FLMUINT uiDigitValue = 0;
|
|
FLMUINT uiSavedLineNum = m_uiCurrLineNum;
|
|
FLMUINT uiSavedOffset = m_uiCurrLineOffset;
|
|
FLMUINT uiSavedFilePos = m_uiCurrLineFilePos;
|
|
FLMUINT uiSavedLineBytes = m_uiCurrLineBytes;
|
|
FLMBYTE * pucValue;
|
|
|
|
// Leading white space has already been skipped.
|
|
|
|
// Go until we hit a character that is not a number.
|
|
|
|
for (;;)
|
|
{
|
|
|
|
// If we hit the end of the line, we are done.
|
|
|
|
if ((ucChar = getChar()) == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// Ignore white space
|
|
|
|
{
|
|
continue;
|
|
}
|
|
if (ucChar >= '0' && ucChar <= '9')
|
|
{
|
|
uiDigitValue = (FLMUINT)(ucChar - '0');
|
|
uiDigitCount++;
|
|
}
|
|
else if (ucChar >= 'a' && ucChar <= 'f')
|
|
{
|
|
if (!bHex)
|
|
{
|
|
setErrInfo( m_uiCurrLineNum,
|
|
m_uiCurrLineOffset,
|
|
SQL_ERR_ILLEGAL_HEX_DIGIT,
|
|
m_uiCurrLineFilePos,
|
|
m_uiCurrLineBytes);
|
|
rc = RC_SET( NE_SFLM_INVALID_SQL);
|
|
goto Exit;
|
|
}
|
|
uiDigitValue = (FLMUINT)(ucChar - 'a' + 10);
|
|
uiDigitCount++;
|
|
}
|
|
else if (ucChar >= 'A' && ucChar <= 'F')
|
|
{
|
|
if (!bHex)
|
|
{
|
|
setErrInfo( m_uiCurrLineNum,
|
|
m_uiCurrLineOffset,
|
|
SQL_ERR_ILLEGAL_HEX_DIGIT,
|
|
m_uiCurrLineFilePos,
|
|
m_uiCurrLineBytes);
|
|
rc = RC_SET( NE_SFLM_INVALID_SQL);
|
|
goto Exit;
|
|
}
|
|
uiDigitValue = (FLMUINT)(ucChar - 'A' + 10);
|
|
uiDigitCount++;
|
|
}
|
|
else if (ucChar == ',' || ucChar == ')' ||
|
|
ucChar == ASCII_SPACE || ucChar == ASCII_TAB)
|
|
{
|
|
|
|
// terminate when we hit a comma or right paren or white
|
|
// space. Need to unget the character so the caller can handle it.
|
|
|
|
ungetChar();
|
|
break;
|
|
}
|
|
else if (ucChar == 'X' || ucChar == 'x')
|
|
{
|
|
if (bHex || uiDigitCount != 1 || ui64Value)
|
|
{
|
|
setErrInfo( m_uiCurrLineNum,
|
|
m_uiCurrLineOffset,
|
|
SQL_ERR_NON_NUMERIC_CHARACTER,
|
|
m_uiCurrLineFilePos,
|
|
m_uiCurrLineBytes);
|
|
rc = RC_SET( NE_SFLM_INVALID_SQL);
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
bHex = TRUE;
|
|
uiDigitCount = 0;
|
|
continue;
|
|
}
|
|
}
|
|
else if (ucChar == '-')
|
|
{
|
|
if (bHex || uiDigitCount)
|
|
{
|
|
setErrInfo( m_uiCurrLineNum,
|
|
m_uiCurrLineOffset,
|
|
SQL_ERR_NON_NUMERIC_CHARACTER,
|
|
m_uiCurrLineFilePos,
|
|
m_uiCurrLineBytes);
|
|
rc = RC_SET( NE_SFLM_INVALID_SQL);
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
bNeg = TRUE;
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
setErrInfo( m_uiCurrLineNum,
|
|
m_uiCurrLineOffset,
|
|
SQL_ERR_NON_NUMERIC_CHARACTER,
|
|
m_uiCurrLineFilePos,
|
|
m_uiCurrLineBytes);
|
|
rc = RC_SET( NE_SFLM_INVALID_SQL);
|
|
goto Exit;
|
|
}
|
|
|
|
if (bHex)
|
|
{
|
|
if (ui64Value > (FLM_MAX_UINT64 >> 4))
|
|
{
|
|
setErrInfo( m_uiCurrLineNum,
|
|
m_uiCurrLineOffset,
|
|
SQL_ERR_NUMBER_OVERFLOW,
|
|
m_uiCurrLineFilePos,
|
|
m_uiCurrLineBytes);
|
|
rc = RC_SET( NE_SFLM_INVALID_SQL);
|
|
goto Exit;
|
|
}
|
|
ui64Value <<= 4;
|
|
ui64Value += (FLMUINT64)uiDigitValue;
|
|
}
|
|
else
|
|
{
|
|
if (ui64Value > (FLM_MAX_UINT64 / 10))
|
|
{
|
|
setErrInfo( m_uiCurrLineNum,
|
|
m_uiCurrLineOffset,
|
|
SQL_ERR_NUMBER_OVERFLOW,
|
|
m_uiCurrLineFilePos,
|
|
m_uiCurrLineBytes);
|
|
rc = RC_SET( NE_SFLM_INVALID_SQL);
|
|
goto Exit;
|
|
}
|
|
ui64Value *= 10;
|
|
ui64Value += (FLMUINT64)uiDigitValue;
|
|
}
|
|
|
|
// If it is a negative number, make sure we have not
|
|
// exceeded the maximum negative value.
|
|
|
|
if (bNeg && ui64Value > ((FLMUINT64)1 << 63))
|
|
{
|
|
setErrInfo( m_uiCurrLineNum,
|
|
m_uiCurrLineOffset,
|
|
SQL_ERR_NUMBER_OVERFLOW,
|
|
m_uiCurrLineFilePos,
|
|
m_uiCurrLineBytes);
|
|
rc = RC_SET( NE_SFLM_INVALID_SQL);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// If we didn't hit any digits, we have an invalid number.
|
|
|
|
if (!uiDigitCount)
|
|
{
|
|
setErrInfo( uiSavedLineNum,
|
|
uiSavedOffset,
|
|
SQL_ERR_NUMBER_VALUE_EMPTY,
|
|
uiSavedFilePos,
|
|
uiSavedLineBytes);
|
|
rc = RC_SET( NE_SFLM_INVALID_SQL);
|
|
goto Exit;
|
|
}
|
|
|
|
// Allocate space for ui64Value SEN plus one byte for the sign.
|
|
|
|
pColumnValue->uiValueLen = f_getSENByteCount( ui64Value) + 1;
|
|
if (RC_BAD( rc = m_tmpPool.poolAlloc( pColumnValue->uiValueLen,
|
|
(void **)&pucValue)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
pColumnValue->pucColumnValue = pucValue;
|
|
|
|
*pucValue++ = (FLMBYTE)(bNeg ? (FLMBYTE)1 : (FLMBYTE)0);
|
|
|
|
// Set the number into the data. uiNumChars will hold bNeg.
|
|
|
|
f_encodeSEN( ui64Value, &pucValue);
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Desc: Parse a binary value from the input stream.
|
|
//------------------------------------------------------------------------------
|
|
RCODE SQLStatement::getBinaryValue(
|
|
F_COLUMN_VALUE * pColumnValue)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
FLMBYTE ucChar;
|
|
FLMBYTE szTmpBuf [300];
|
|
F_DynaBuf dynaBuf( szTmpBuf, sizeof( szTmpBuf));
|
|
FLMBYTE ucCurrByte;
|
|
FLMBOOL bGetHighNibble;
|
|
FLMUINT uiSavedLineNum = m_uiCurrLineNum;
|
|
FLMUINT uiSavedOffset = m_uiCurrLineOffset;
|
|
FLMUINT uiSavedFilePos = m_uiCurrLineFilePos;
|
|
FLMUINT uiSavedLineBytes = m_uiCurrLineBytes;
|
|
|
|
// Leading white space has already been skipped.
|
|
|
|
// Go until we hit a character that is not a hex digit.
|
|
|
|
ucCurrByte = 0;
|
|
bGetHighNibble = TRUE;
|
|
for (;;)
|
|
{
|
|
|
|
// It is OK for white space to be in the middle of a binary
|
|
// piece of data. It is also allowed to span multiple lines.
|
|
|
|
if ((ucChar = getChar()) == 0)
|
|
{
|
|
if (RC_BAD( rc = getLine()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// Ignore white space
|
|
|
|
if (ucChar == ASCII_SPACE || ucChar == ASCII_TAB)
|
|
{
|
|
continue;
|
|
}
|
|
if (ucChar >= '0' && ucChar <= '9')
|
|
{
|
|
if (bGetHighNibble)
|
|
{
|
|
ucCurrByte = (ucChar - '0') << 4;
|
|
}
|
|
else
|
|
{
|
|
ucCurrByte |= (ucChar - '0');
|
|
}
|
|
}
|
|
else if (ucChar >= 'a' && ucChar <= 'f')
|
|
{
|
|
if (bGetHighNibble)
|
|
{
|
|
ucCurrByte = (ucChar - 'a' + 10) << 4;
|
|
}
|
|
else
|
|
{
|
|
ucCurrByte |= (ucChar - 'a' + 10);
|
|
}
|
|
}
|
|
else if (ucChar >= 'A' && ucChar <= 'F')
|
|
{
|
|
if (bGetHighNibble)
|
|
{
|
|
ucCurrByte = (ucChar - 'A' + 10) << 4;
|
|
}
|
|
else
|
|
{
|
|
ucCurrByte |= (ucChar - 'A' + 10);
|
|
}
|
|
}
|
|
else if (ucChar == ',' || ucChar == ')')
|
|
{
|
|
|
|
// terminate when we hit a comma or right paren. Need to
|
|
// unget the character because the caller will be expecting
|
|
// to see it.
|
|
|
|
ungetChar();
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
setErrInfo( m_uiCurrLineNum,
|
|
m_uiCurrLineOffset,
|
|
SQL_ERR_NON_HEX_CHARACTER,
|
|
m_uiCurrLineFilePos,
|
|
m_uiCurrLineBytes);
|
|
rc = RC_SET( NE_SFLM_INVALID_SQL);
|
|
goto Exit;
|
|
}
|
|
if (bGetHighNibble)
|
|
{
|
|
bGetHighNibble = FALSE;
|
|
}
|
|
else
|
|
{
|
|
if (RC_BAD( rc = dynaBuf.appendByte( ucCurrByte)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
bGetHighNibble = TRUE;
|
|
ucCurrByte = 0;
|
|
}
|
|
}
|
|
|
|
// Add last byte if bGetHighNibble is FALSE - means we got the high nibble
|
|
// into the high four bits of ucCurrByte
|
|
|
|
if (!bGetHighNibble)
|
|
{
|
|
if (RC_BAD( rc = dynaBuf.appendByte( ucCurrByte)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// An empty binary value is invalid.
|
|
|
|
if ((pColumnValue->uiValueLen = dynaBuf.getDataLength()) == 0)
|
|
{
|
|
setErrInfo( uiSavedLineNum,
|
|
uiSavedOffset,
|
|
SQL_ERR_BINARY_VALUE_EMPTY,
|
|
uiSavedFilePos,
|
|
uiSavedLineBytes);
|
|
rc = RC_SET( NE_SFLM_INVALID_SQL);
|
|
goto Exit;
|
|
}
|
|
|
|
// Allocate space for the binary data.
|
|
|
|
if (RC_BAD( rc = m_tmpPool.poolAlloc( pColumnValue->uiValueLen,
|
|
(void **)&pColumnValue->pucColumnValue)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Copy the binary data from the dynaBuf to the column.
|
|
|
|
f_memcpy( pColumnValue->pucColumnValue, dynaBuf.getBufferPtr(),
|
|
pColumnValue->uiValueLen);
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Desc: Parse a value from the input stream. Value must be of the specified
|
|
// type.
|
|
//------------------------------------------------------------------------------
|
|
RCODE SQLStatement::getValue(
|
|
F_COLUMN_VALUE * pColumnValue)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
|
|
switch (pColumnValue->eColumnDataType)
|
|
{
|
|
case SFLM_STRING_TYPE:
|
|
if (RC_BAD( rc = getStringValue( pColumnValue)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
break;
|
|
case SFLM_NUMBER_TYPE:
|
|
if (RC_BAD( rc = getNumberValue( pColumnValue)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
break;
|
|
case SFLM_BINARY_TYPE:
|
|
if (RC_BAD( rc = getBinaryValue( pColumnValue)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
break;
|
|
default:
|
|
flmAssert( 0);
|
|
break;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Desc: Insert a row into the database.
|
|
//------------------------------------------------------------------------------
|
|
RCODE F_Db::insertRow(
|
|
FLMUINT uiTableNum,
|
|
F_COLUMN_VALUE * pColumnValues,
|
|
FLMUINT uiNumColumnValues)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
F_Row * pRow = NULL;
|
|
const FLMBYTE * pucValue;
|
|
const FLMBYTE * pucEnd;
|
|
FLMUINT64 ui64Num;
|
|
FLMUINT uiNumChars;
|
|
FLMBOOL bNeg;
|
|
|
|
// Create a row object.
|
|
|
|
if (RC_BAD( rc = gv_SFlmSysData.pRowCacheMgr->createRow( this,
|
|
uiTableNum, &pRow)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Set the column values into the row.
|
|
|
|
for (; uiNumColumnValues; uiNumColumnValues--, pColumnValues++)
|
|
{
|
|
if (!pColumnValues->uiValueLen)
|
|
{
|
|
continue;
|
|
}
|
|
switch (pColumnValues->eColumnDataType)
|
|
{
|
|
case SFLM_STRING_TYPE:
|
|
pucValue = (const FLMBYTE *)pColumnValues->pucColumnValue;
|
|
pucEnd = pucValue + pColumnValues->uiValueLen;
|
|
if (RC_BAD( rc = f_decodeSEN( &pucValue, pucEnd, &uiNumChars)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (RC_BAD( rc = pRow->setUTF8( this,
|
|
pColumnValues->uiColumnNum,
|
|
(const char *)pucValue,
|
|
(FLMUINT)(pucEnd - pucValue),
|
|
uiNumChars)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
break;
|
|
case SFLM_NUMBER_TYPE:
|
|
pucValue = (const FLMBYTE *)pColumnValues->pucColumnValue;
|
|
pucEnd = pucValue + pColumnValues->uiValueLen;
|
|
|
|
bNeg = (FLMBOOL)(*pucValue ? (FLMBOOL)TRUE : (FLMBOOL)FALSE);
|
|
pucValue++;
|
|
|
|
if (RC_BAD( rc = f_decodeSEN64( &pucValue, pucEnd, &ui64Num)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (RC_BAD( rc = pRow->setNumber64( this,
|
|
pColumnValues->uiColumnNum, ui64Num, bNeg)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
break;
|
|
case SFLM_BINARY_TYPE:
|
|
if (RC_BAD( rc = pRow->setBinary( this,
|
|
pColumnValues->uiColumnNum,
|
|
(const void *)(pColumnValues->pucColumnValue),
|
|
pColumnValues->uiValueLen)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
break;
|
|
default:
|
|
flmAssert( 0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (RC_BAD( rc = m_pDatabase->m_pRfl->logInsertRow( this, uiTableNum, pColumnValues,
|
|
uiNumColumnValues)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
if (pRow)
|
|
{
|
|
pRow->ReleaseRow();
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Desc: Process the insert statement. The "INSERT" keyword has already been
|
|
// parsed.
|
|
//------------------------------------------------------------------------------
|
|
RCODE SQLStatement::processInsertRow( void)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
FLMBOOL bStartedTrans = FALSE;
|
|
F_COLUMN_VALUE * pColumnValue;
|
|
F_COLUMN * pColumn;
|
|
FLMUINT uiLoop;
|
|
char szColumnName [MAX_SQL_NAME_LEN + 1];
|
|
FLMUINT uiColumnNameLen;
|
|
|
|
// 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: INSERT INTO table_name (column1,column2,...) VALUES (value1,value2,...)
|
|
// OR: INSERT INTO table_name VALUES (value1,value2,...)
|
|
|
|
// Whitespace must follow the "INSERT"
|
|
|
|
if (RC_BAD( rc = skipWhitespace( TRUE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// INTO must follow the INSERT.
|
|
|
|
if (!lineHasToken( "into"))
|
|
{
|
|
setErrInfo( m_uiCurrLineNum,
|
|
m_uiCurrLineOffset,
|
|
SQL_ERR_EXPECTING_INTO,
|
|
m_uiCurrLineFilePos,
|
|
m_uiCurrLineBytes);
|
|
rc = RC_SET( NE_SFLM_INVALID_SQL);
|
|
goto Exit;
|
|
}
|
|
|
|
// Whitespace must follow the "INTO"
|
|
|
|
if (RC_BAD( rc = skipWhitespace( TRUE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Get the table name.
|
|
|
|
if (RC_BAD( rc = getTableName( TRUE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (m_pTable->bSystemTable)
|
|
{
|
|
setErrInfo( m_uiCurrLineNum,
|
|
m_uiCurrLineOffset,
|
|
SQL_ERR_CANNOT_UPDATE_SYSTEM_TABLE,
|
|
m_uiCurrLineFilePos,
|
|
m_uiCurrLineBytes);
|
|
rc = RC_SET( NE_SFLM_INVALID_SQL);
|
|
goto Exit;
|
|
}
|
|
|
|
// Whitespace does not have to follow the table name
|
|
|
|
if (RC_BAD( rc = skipWhitespace( FALSE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// If left paren follows table name, then columns are being listed.
|
|
|
|
m_uiNumColumnValues = 0;
|
|
if (lineHasToken( "("))
|
|
{
|
|
|
|
// Get the list of columns for which there will be values.
|
|
|
|
for (;;)
|
|
{
|
|
if (RC_BAD( rc = skipWhitespace( FALSE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Get the column name
|
|
|
|
if (RC_BAD( rc = getName( szColumnName, sizeof( szColumnName),
|
|
&uiColumnNameLen)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// See if the column is defined in the table.
|
|
|
|
if (uiColumnNameLen)
|
|
{
|
|
if ((pColumn = m_pDb->m_pDict->findColumn( m_pTable, szColumnName)) == NULL)
|
|
{
|
|
setErrInfo( m_uiCurrLineNum,
|
|
m_uiCurrLineOffset,
|
|
SQL_ERR_UNDEFINED_COLUMN,
|
|
m_uiCurrLineFilePos,
|
|
m_uiCurrLineBytes);
|
|
rc = RC_SET( NE_SFLM_INVALID_SQL);
|
|
goto Exit;
|
|
}
|
|
|
|
// Make room in the column value table for this column
|
|
|
|
if (m_uiNumColumnValues == m_uiColumnValueArraySize)
|
|
{
|
|
if (RC_BAD( rc = allocColumnValueArray( m_uiNumColumnValues + 1)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
pColumnValue = &m_pColumnValues [m_uiNumColumnValues];
|
|
pColumnValue->uiColumnNum = pColumn->uiColumnNum;
|
|
pColumnValue->eColumnDataType = pColumn->eDataTyp;
|
|
pColumnValue->uiValueLen = 0;
|
|
m_uiNumColumnValues++;
|
|
}
|
|
|
|
if (RC_BAD( rc = skipWhitespace( FALSE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// See if we are at the end of the list of columns
|
|
|
|
if (lineHasToken( ")"))
|
|
{
|
|
break;
|
|
}
|
|
else if (!lineHasToken( ","))
|
|
{
|
|
setErrInfo( m_uiCurrLineNum,
|
|
m_uiCurrLineOffset,
|
|
SQL_ERR_EXPECTING_COMMA,
|
|
m_uiCurrLineFilePos,
|
|
m_uiCurrLineBytes);
|
|
rc = RC_SET( NE_SFLM_INVALID_SQL);
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
// Allocate the column value array
|
|
|
|
if (RC_BAD( rc = allocColumnValueArray( m_pTable->uiNumColumns)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
for (uiLoop = 0, pColumn = m_pTable->pColumns, pColumnValue = m_pColumnValues;
|
|
uiLoop < m_pTable->uiNumColumns;
|
|
uiLoop++, pColumn++, pColumnValue++)
|
|
{
|
|
if (pColumn->uiColumnNum)
|
|
{
|
|
pColumnValue->uiColumnNum = pColumn->uiColumnNum;
|
|
pColumnValue->eColumnDataType = pColumn->eDataTyp;
|
|
pColumnValue->uiValueLen = 0;
|
|
m_uiNumColumnValues++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Allow for no values to be specified if no columns were.
|
|
|
|
if (m_uiNumColumnValues)
|
|
{
|
|
if (!lineHasToken( "values"))
|
|
{
|
|
setErrInfo( m_uiCurrLineNum,
|
|
m_uiCurrLineOffset,
|
|
SQL_ERR_EXPECTING_INTO,
|
|
m_uiCurrLineFilePos,
|
|
m_uiCurrLineBytes);
|
|
rc = RC_SET( NE_SFLM_INVALID_SQL);
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_BAD( rc = skipWhitespace( FALSE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Should be a left paren
|
|
|
|
if (!lineHasToken( "("))
|
|
{
|
|
setErrInfo( m_uiCurrLineNum,
|
|
m_uiCurrLineOffset,
|
|
SQL_ERR_EXPECTING_LPAREN,
|
|
m_uiCurrLineFilePos,
|
|
m_uiCurrLineBytes);
|
|
rc = RC_SET( NE_SFLM_INVALID_SQL);
|
|
goto Exit;
|
|
}
|
|
|
|
for (uiLoop = 0, pColumnValue = m_pColumnValues;
|
|
uiLoop < m_uiNumColumnValues;
|
|
uiLoop++, pColumnValue++)
|
|
{
|
|
if (RC_BAD( rc = skipWhitespace( FALSE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Get the column value
|
|
|
|
if (RC_BAD( rc = getValue( pColumnValue)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_BAD( rc = skipWhitespace( FALSE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (uiLoop == m_uiNumColumnValues - 1)
|
|
{
|
|
if (!lineHasToken( ")"))
|
|
{
|
|
setErrInfo( m_uiCurrLineNum,
|
|
m_uiCurrLineOffset,
|
|
SQL_ERR_EXPECTING_RPAREN,
|
|
m_uiCurrLineFilePos,
|
|
m_uiCurrLineBytes);
|
|
rc = RC_SET( NE_SFLM_INVALID_SQL);
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else if (!lineHasToken( ","))
|
|
{
|
|
setErrInfo( m_uiCurrLineNum,
|
|
m_uiCurrLineOffset,
|
|
SQL_ERR_EXPECTING_COMMA,
|
|
m_uiCurrLineFilePos,
|
|
m_uiCurrLineBytes);
|
|
rc = RC_SET( NE_SFLM_INVALID_SQL);
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Insert the row.
|
|
|
|
if (RC_BAD( rc = m_pDb->insertRow( m_pTable->uiTableNum,
|
|
m_pColumnValues, m_uiNumColumnValues)))
|
|
{
|
|
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);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Desc: Parse and execute an SQL statement.
|
|
//------------------------------------------------------------------------------
|
|
RCODE SQLStatement::executeSQL(
|
|
IF_IStream * pStream,
|
|
F_Db * pDb,
|
|
SQL_STATS * pSQLStats)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
FLMBOOL bWhitespaceRequired = FALSE;
|
|
|
|
// Reset the state of the parser
|
|
|
|
if (RC_BAD( rc = setupStatement()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
m_pStream = pStream;
|
|
m_pDb = pDb;
|
|
|
|
// Process all of the statements.
|
|
|
|
for (;;)
|
|
{
|
|
if (RC_BAD( rc = skipWhitespace( bWhitespaceRequired)))
|
|
{
|
|
if (rc == NE_SFLM_EOF_HIT)
|
|
{
|
|
rc = NE_SFLM_OK;
|
|
break;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
if (lineHasToken( "insert"))
|
|
{
|
|
if( RC_BAD( rc = processInsertRow()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
bWhitespaceRequired = TRUE;
|
|
}
|
|
|
|
// Call the status hook one last time
|
|
|
|
if (m_fnStatus)
|
|
{
|
|
m_fnStatus( SQL_PARSE_STATS,
|
|
(void *)&m_sqlStats, NULL, NULL, m_pvCallbackData);
|
|
}
|
|
|
|
// Tally and return the SQL statistics
|
|
|
|
if( pSQLStats)
|
|
{
|
|
pSQLStats->uiLines += m_sqlStats.uiLines;
|
|
pSQLStats->uiChars += m_sqlStats.uiChars;
|
|
}
|
|
|
|
Exit:
|
|
|
|
if( RC_BAD( rc) && pSQLStats)
|
|
{
|
|
pSQLStats->uiErrLineNum = m_sqlStats.uiErrLineNum
|
|
? m_sqlStats.uiErrLineNum
|
|
: m_uiCurrLineNum;
|
|
|
|
pSQLStats->uiErrLineOffset = m_sqlStats.uiErrLineOffset
|
|
? m_sqlStats.uiErrLineOffset
|
|
: m_uiCurrLineOffset;
|
|
|
|
pSQLStats->eErrorType = m_sqlStats.eErrorType;
|
|
|
|
pSQLStats->uiErrLineFilePos = m_sqlStats.uiErrLineFilePos;
|
|
pSQLStats->uiErrLineBytes = m_sqlStats.uiErrLineBytes;
|
|
}
|
|
|
|
m_pDb = NULL;
|
|
|
|
return( rc);
|
|
}
|