Files
mars-flaim/sql/src/sqlstatement.cpp
dsandersoremutah bf4e1236f9 Added support for more SQL statements.
git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@623 0109f412-320b-0410-ab79-c3e0c5ffbbe6
2006-06-30 21:46:27 +00:00

1944 lines
40 KiB
C++

//------------------------------------------------------------------------------
// Desc: This module contains routines for parsing and executing SQL statements.
//
// 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;
resetStatement();
}
/****************************************************************************
Desc: Destructor
****************************************************************************/
SQLStatement::~SQLStatement()
{
resetStatement();
if( m_pucCurrLineBuf)
{
f_free( &m_pucCurrLineBuf);
}
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;
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 next token in the stream is the specified keyword.
****************************************************************************/
RCODE SQLStatement::getToken(
char * pszToken,
FLMUINT uiTokenBufSize,
FLMBOOL bEofOK,
FLMUINT * puiTokenLineOffset)
{
RCODE rc = NE_SFLM_OK;
FLMUINT uiOffset;
char cChar;
// Always leave room for a null terminating character
uiTokenBufSize--;
// Token buffer must hold at least one character and a null terminating
// character!
if (!uiTokenBufSize)
{
rc = RC_SET( NE_SFLM_BUFFER_OVERFLOW);
goto Exit;
}
// Skip any whitespace preceding the token
if (RC_BAD( rc = skipWhitespace( FALSE)))
{
if (rc == NE_SFLM_EOF_HIT)
{
if (!bEofOK)
{
setErrInfo( m_uiCurrLineNum,
m_uiCurrLineOffset,
SQL_ERR_UNEXPECTED_EOF,
m_uiCurrLineFilePos,
m_uiCurrLineBytes);
rc = RC_SET( NE_SFLM_INVALID_SQL);
}
}
goto Exit;
}
// If the first character is alphanumeric, we need to parse until
// we hit a non-alphanumeric character.
*puiTokenLineOffset = uiOffset = m_uiCurrLineOffset;
cChar = (char)m_pucCurrLineBuf [uiOffset];
if ((cChar >= 'a' && cChar <= 'z') ||
(cChar >= 'A' && cChar <= 'Z') ||
(cChar >= '0' && cChar <= '9') ||
cChar == '_')
{
*pszToken++ = cChar;
uiTokenBufSize--;
uiOffset++;
while (uiOffset < m_uiCurrLineBytes)
{
cChar = (char)m_pucCurrLineBuf [uiOffset];
if ((cChar >= 'a' && cChar <= 'z') ||
(cChar >= 'A' && cChar <= 'Z') ||
(cChar >= '0' && cChar <= '9') ||
cChar == '_')
{
if (!uiTokenBufSize)
{
rc = RC_SET( NE_SFLM_BUFFER_OVERFLOW);
goto Exit;
}
*pszToken++ = cChar;
uiTokenBufSize--;
uiOffset++;
}
else
{
break;
}
}
*pszToken = 0;
m_uiCurrLineOffset = uiOffset;
}
else
{
switch (cChar)
{
case ',':
case '(':
case ')':
case '[':
case ']':
case '{':
case '}':
case '$':
case '@':
case '.':
case ':':
case ';':
case '"':
case '\'':
case '?':
case '#':
*pszToken++ = cChar;
*pszToken = 0;
m_uiCurrLineOffset = uiOffset + 1;
break;
case '>':
case '<':
case '+':
case '-':
case '*':
case '/':
case '=':
case '!':
case '%':
case '^':
*pszToken++ = cChar;
if (uiOffset + 1 < m_uiCurrLineBytes &&
m_pucCurrLineBuf [uiOffset + 1] == '=')
{
if (uiTokenBufSize < 2)
{
rc = RC_SET( NE_SFLM_BUFFER_OVERFLOW);
goto Exit;
}
*pszToken++ = '=';
m_uiCurrLineOffset = uiOffset + 2;
}
else
{
m_uiCurrLineOffset = uiOffset + 1;
}
*pszToken = 0;
break;
case '|':
*pszToken++ = cChar;
if (uiOffset + 1 < m_uiCurrLineBytes &&
(m_pucCurrLineBuf [uiOffset + 1] == '=' ||
m_pucCurrLineBuf [uiOffset + 1] == '|'))
{
if (uiTokenBufSize < 2)
{
rc = RC_SET( NE_SFLM_BUFFER_OVERFLOW);
goto Exit;
}
*pszToken++ = (char)m_pucCurrLineBuf [uiOffset + 1];
m_uiCurrLineOffset = uiOffset + 2;
}
else
{
m_uiCurrLineOffset = uiOffset + 1;
}
*pszToken = 0;
break;
case '&':
*pszToken++ = cChar;
if (uiOffset + 1 < m_uiCurrLineBytes &&
(m_pucCurrLineBuf [uiOffset + 1] == '=' ||
m_pucCurrLineBuf [uiOffset + 1] == '&'))
{
if (uiTokenBufSize < 2)
{
rc = RC_SET( NE_SFLM_BUFFER_OVERFLOW);
goto Exit;
}
*pszToken++ = (char)m_pucCurrLineBuf [uiOffset + 1];
m_uiCurrLineOffset = uiOffset + 2;
}
else
{
m_uiCurrLineOffset = uiOffset + 1;
}
*pszToken = 0;
break;
default:
setErrInfo( m_uiCurrLineNum,
m_uiCurrLineOffset,
SQL_ERR_INVALID_CHARACTER,
m_uiCurrLineFilePos,
m_uiCurrLineBytes);
rc = RC_SET( NE_SFLM_INVALID_SQL);
goto Exit;
}
}
Exit:
return( rc);
}
/****************************************************************************
Desc: See if the next token in the stream is the specified keyword.
****************************************************************************/
RCODE SQLStatement::haveToken(
const char * pszToken,
FLMBOOL bEofOK,
SQLParseError eNotHaveErr)
{
RCODE rc = NE_SFLM_OK;
char szToken [MAX_SQL_TOKEN_SIZE + 1];
FLMUINT uiTokenLineOffset;
if (RC_BAD( rc = getToken( szToken, sizeof( szToken), bEofOK,
&uiTokenLineOffset)))
{
if (rc == NE_SFLM_EOF_HIT)
{
flmAssert( bEofOK && eNotHaveErr == SQL_NO_ERROR);
rc = RC_SET( NE_SFLM_NOT_FOUND);
}
goto Exit;
}
if (f_stricmp( pszToken, szToken) != 0)
{
// Restore the position where the token started - so it can
// still be parsed.
m_uiCurrLineOffset = uiTokenLineOffset;
// At this point, we know we don't have the token
if (eNotHaveErr != SQL_NO_ERROR)
{
setErrInfo( m_uiCurrLineNum,
m_uiCurrLineOffset,
eNotHaveErr,
m_uiCurrLineFilePos,
m_uiCurrLineBytes);
rc = RC_SET( NE_SFLM_INVALID_SQL);
}
else
{
rc = RC_SET( NE_SFLM_NOT_FOUND);
}
goto Exit;
}
Exit:
return( rc);
}
/****************************************************************************
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 UTF8 string from the input stream.
//------------------------------------------------------------------------------
RCODE SQLStatement::getUTF8String(
FLMBOOL bMustHaveEqual,
FLMBYTE * pszStr,
FLMUINT uiStrBufSize,
FLMUINT * puiStrLen)
{
RCODE rc = NE_SFLM_OK;
FLMBYTE ucChar;
FLMBYTE ucQuoteChar = 0;
FLMBOOL bEscaped = FALSE;
FLMUINT uiNumChars = 0;
if (bMustHaveEqual)
{
if (RC_BAD( rc = haveToken( "=", FALSE, SQL_ERR_EXPECTING_EQUAL)))
{
goto Exit;
}
}
if (RC_BAD( rc = skipWhitespace( FALSE)))
{
goto Exit;
}
// Always leave room for a null terminating character
uiStrBufSize--;
// See if we have a quote character.
ucChar = getChar();
if (ucChar == '"' || ucChar == '\'')
{
ucQuoteChar = ucChar;
}
for (;;)
{
// If we hit end of line, it is invalid without hitting quote,
// it is an error.
if ((ucChar = getChar()) == 0)
{
if (ucQuoteChar)
{
setErrInfo( m_uiCurrLineNum,
m_uiCurrLineOffset,
SQL_ERR_MISSING_QUOTE,
m_uiCurrLineFilePos,
m_uiCurrLineBytes);
rc = RC_SET( NE_SFLM_INVALID_SQL);
goto Exit;
}
else
{
break;
}
}
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 == '"' ||
ucChar == ASCII_SPACE || ucChar == ASCII_TAB ||
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;
}
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;
}
*pszStr++ = ucChar;
uiNumChars++;
}
else if (ucChar == '\\')
{
bEscaped = TRUE;
}
else if (ucChar == ucQuoteChar)
{
break;
}
else if (ucChar == ASCII_SPACE && ucChar == ASCII_TAB ||
ucChar == ',' || ucChar == ')')
{
ungetChar();
break;
}
else
{
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;
}
// Save the character to our buffer.
*pszStr++ = ucChar;
uiNumChars++;
// 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.
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 (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++;
}
}
}
}
// There will always be room for a null terminating byte if we
// get to this point.
*pszStr = 0;
Exit:
return( rc);
}
//------------------------------------------------------------------------------
// Desc: Parse a numeric value from the input stream.
//------------------------------------------------------------------------------
RCODE SQLStatement::getNumber(
FLMBOOL bMustHaveEqual,
FLMUINT64 * pui64Num,
FLMBOOL * pbNeg,
FLMBOOL bNegAllowed)
{
RCODE rc = NE_SFLM_OK;
FLMBYTE ucChar;
FLMUINT64 ui64Value = 0;
FLMBOOL bHex = FALSE;
FLMUINT uiDigitCount = 0;
FLMUINT uiDigitValue = 0;
FLMUINT uiSavedLineNum;
FLMUINT uiSavedOffset;
FLMUINT uiSavedFilePos;
FLMUINT uiSavedLineBytes;
*pbNeg = FALSE;
if (bMustHaveEqual)
{
if (RC_BAD( rc = haveToken( "=", FALSE, SQL_ERR_EXPECTING_EQUAL)))
{
goto Exit;
}
}
if (RC_BAD( rc = skipWhitespace( FALSE)))
{
goto Exit;
}
uiSavedLineNum = m_uiCurrLineNum;
uiSavedOffset = m_uiCurrLineOffset;
uiSavedFilePos = m_uiCurrLineFilePos;
uiSavedLineBytes = m_uiCurrLineBytes;
// 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 if (!bNegAllowed)
{
setErrInfo( m_uiCurrLineNum,
m_uiCurrLineOffset,
SQL_ERR_INVALID_NEGATIVE_NUM,
m_uiCurrLineFilePos,
m_uiCurrLineBytes);
rc = RC_SET( NE_SFLM_INVALID_SQL);
goto Exit;
}
else
{
*pbNeg = 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 (*pbNeg && 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;
}
*pui64Num = ui64Value;
Exit:
return( rc);
}
//------------------------------------------------------------------------------
// Desc: Parse a boolean value from the input stream.
//------------------------------------------------------------------------------
RCODE SQLStatement::getBool(
FLMBOOL bMustHaveEqual,
FLMBOOL * pbBool)
{
RCODE rc = NE_SFLM_OK;
char szBool [20];
FLMUINT uiBoolLen;
if (RC_BAD( rc = getUTF8String( bMustHaveEqual, (FLMBYTE *)szBool,
sizeof( szBool), &uiBoolLen)))
{
goto Exit;
}
if (f_stricmp( szBool, "true") == 0)
{
*pbBool = TRUE;
}
else if (f_stricmp( szBool, "false") == 0)
{
*pbBool = FALSE;
}
else
{
setErrInfo( m_uiCurrLineNum,
m_uiCurrLineOffset,
SQL_ERR_EXPECTING_BOOLEAN,
m_uiCurrLineFilePos,
m_uiCurrLineBytes);
rc = RC_SET( NE_SFLM_INVALID_SQL);
goto Exit;
}
Exit:
return( rc);
}
//------------------------------------------------------------------------------
// Desc: Parse a FLMUINT numeric value from the input stream.
//------------------------------------------------------------------------------
RCODE SQLStatement::getUINT(
FLMBOOL bMustHaveEqual,
FLMUINT * puiNum)
{
RCODE rc = NE_SFLM_OK;
FLMBOOL bNeg;
FLMUINT64 ui64Value;
if (RC_BAD( rc = getNumber( bMustHaveEqual, &ui64Value, &bNeg, FALSE)))
{
goto Exit;
}
// Number must be less than FLM_MAX_UINT
if (ui64Value > (FLMUINT64)FLM_MAX_UINT)
{
setErrInfo( m_uiCurrLineNum,
m_uiCurrLineOffset,
SQL_ERR_NUMBER_OVERFLOW,
m_uiCurrLineFilePos,
m_uiCurrLineBytes);
rc = RC_SET( NE_SFLM_INVALID_SQL);
goto Exit;
}
*puiNum = (FLMUINT)ui64Value;
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;
if (RC_BAD( rc = skipWhitespace( FALSE)))
{
goto Exit;
}
// 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_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_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 encryption definition name for the current statement.
// Make sure it is valid.
//------------------------------------------------------------------------------
RCODE SQLStatement::getEncDefName(
FLMBOOL bMustExist,
char * pszEncDefName,
FLMUINT uiEncDefNameBufSize,
FLMUINT * puiEncDefNameLen,
F_ENCDEF ** ppEncDef)
{
RCODE rc = NE_SFLM_OK;
if (RC_BAD( rc = getName( pszEncDefName, uiEncDefNameBufSize, puiEncDefNameLen)))
{
goto Exit;
}
// See if the encryption definition name is defined
if ((*ppEncDef = m_pDb->m_pDict->findEncDef( pszEncDefName)) == NULL)
{
if (bMustExist)
{
setErrInfo( m_uiCurrLineNum,
m_uiCurrLineOffset - 1,
SQL_ERR_UNDEFINED_ENCDEF,
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_ENCDEF_ALREADY_DEFINED,
m_uiCurrLineFilePos,
m_uiCurrLineBytes);
rc = RC_SET( NE_SFLM_INVALID_SQL);
goto Exit;
}
}
Exit:
return( rc);
}
//------------------------------------------------------------------------------
// Desc: Parse the table name for the current statement. Make sure it is valid.
//------------------------------------------------------------------------------
RCODE SQLStatement::getTableName(
FLMBOOL bMustExist,
char * pszTableName,
FLMUINT uiTableNameBufSize,
FLMUINT * puiTableNameLen,
F_TABLE ** ppTable)
{
RCODE rc = NE_SFLM_OK;
if (RC_BAD( rc = getName( pszTableName, uiTableNameBufSize, puiTableNameLen)))
{
goto Exit;
}
// See if the table name is defined
if (RC_BAD( rc = m_pDb->m_pDict->getTable( pszTableName, ppTable, 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: Parse the index name for the current statement. Make sure it is valid.
//------------------------------------------------------------------------------
RCODE SQLStatement::getIndexName(
FLMBOOL bMustExist,
char * pszIndexName,
FLMUINT uiIndexNameBufSize,
FLMUINT * puiIndexNameLen,
F_INDEX ** ppIndex)
{
RCODE rc = NE_SFLM_OK;
if (RC_BAD( rc = getName( pszIndexName, uiIndexNameBufSize, puiIndexNameLen)))
{
goto Exit;
}
// See if the index name is defined
if (RC_BAD( rc = m_pDb->m_pDict->getIndex( pszIndexName, ppIndex, TRUE)))
{
if (rc != NE_SFLM_BAD_IX)
{
goto Exit;
}
if (bMustExist)
{
setErrInfo( m_uiCurrLineNum,
m_uiCurrLineOffset - 1,
SQL_ERR_UNDEFINED_INDEX,
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_INDEX_ALREADY_DEFINED,
m_uiCurrLineFilePos,
m_uiCurrLineBytes);
rc = RC_SET( NE_SFLM_INVALID_SQL);
goto Exit;
}
}
Exit:
return( rc);
}
//------------------------------------------------------------------------------
// Desc: Parse a string value from the input stream.
//------------------------------------------------------------------------------
RCODE SQLStatement::getStringValue(
F_COLUMN * pColumn,
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;
}
// See if the string is too long.
if (pColumn->uiMaxLen && uiNumChars > pColumn->uiMaxLen)
{
setErrInfo( m_uiCurrLineNum,
m_uiCurrLineOffset,
SQL_ERR_STRING_TOO_LONG,
m_uiCurrLineFilePos,
m_uiCurrLineBytes);
rc = RC_SET( NE_SFLM_STRING_TOO_LONG);
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;
FLMUINT64 ui64Value = 0;
FLMBOOL bNeg = FALSE;
FLMBYTE * pucValue;
if (RC_BAD( rc = getNumber( FALSE, &ui64Value, &bNeg, TRUE)))
{
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 * pColumn,
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;
}
}
// See if the binary data is too long.
if (pColumn->uiMaxLen && dynaBuf.getDataLength() > pColumn->uiMaxLen)
{
setErrInfo( m_uiCurrLineNum,
m_uiCurrLineOffset,
SQL_ERR_BINARY_TOO_LONG,
m_uiCurrLineFilePos,
m_uiCurrLineBytes);
rc = RC_SET( NE_SFLM_BINARY_TOO_LONG);
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 * pColumn,
F_COLUMN_VALUE * pColumnValue)
{
RCODE rc = NE_SFLM_OK;
switch (pColumn->eDataTyp)
{
case SFLM_STRING_TYPE:
if (RC_BAD( rc = getStringValue( pColumn, 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( pColumn, pColumnValue)))
{
goto Exit;
}
break;
default:
flmAssert( 0);
break;
}
Exit:
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;
char szToken [MAX_SQL_TOKEN_SIZE + 1];
FLMUINT uiTokenLineOffset;
// 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 = getToken( szToken, sizeof( szToken), TRUE,
&uiTokenLineOffset)))
{
goto Exit;
}
if (f_stricmp( szToken, "insert") == 0)
{
if (RC_BAD( rc = processInsertRow()))
{
goto Exit;
}
}
else if (f_stricmp( szToken, "open") == 0)
{
if (RC_BAD( rc = haveToken( "database", FALSE, SQL_ERR_INVALID_OPEN_OPTION)))
{
goto Exit;
}
if (RC_BAD( rc = processOpenDatabase()))
{
goto Exit;
}
}
else if (f_stricmp( szToken, "create") == 0)
{
if (RC_BAD( rc = getToken( szToken, sizeof( szToken), FALSE,
&uiTokenLineOffset)))
{
goto Exit;
}
if (f_stricmp( szToken, "database") == 0)
{
if (RC_BAD( rc = processCreateDatabase()))
{
goto Exit;
}
}
else if (f_stricmp( szToken, "table") == 0)
{
if (RC_BAD( rc = processCreateTable()))
{
goto Exit;
}
}
else if (f_stricmp( szToken, "index") == 0)
{
if (RC_BAD( rc = processCreateIndex( FALSE)))
{
goto Exit;
}
}
else if (f_stricmp( szToken, "unique") == 0)
{
if (RC_BAD( rc = haveToken( "index", FALSE,
SQL_ERR_EXPECTING_INDEX)))
{
goto Exit;
}
if (RC_BAD( rc = processCreateIndex( TRUE)))
{
goto Exit;
}
}
else
{
setErrInfo( m_uiCurrLineNum,
uiTokenLineOffset,
SQL_ERR_INVALID_CREATE_OPTION,
m_uiCurrLineFilePos,
m_uiCurrLineBytes);
rc = RC_SET( NE_SFLM_INVALID_SQL);
goto Exit;
}
}
else if (f_stricmp( szToken, "drop") == 0)
{
if (RC_BAD( rc = getToken( szToken, sizeof( szToken), FALSE,
&uiTokenLineOffset)))
{
goto Exit;
}
if (f_stricmp( szToken, "database") == 0)
{
if (RC_BAD( rc = processDropDatabase()))
{
goto Exit;
}
}
else if (f_stricmp( szToken, "table") == 0)
{
if (RC_BAD( rc = processDropTable()))
{
goto Exit;
}
}
else if (f_stricmp( szToken, "index") == 0)
{
if (RC_BAD( rc = processDropIndex()))
{
goto Exit;
}
}
else
{
setErrInfo( m_uiCurrLineNum,
uiTokenLineOffset,
SQL_ERR_INVALID_DROP_OPTION,
m_uiCurrLineFilePos,
m_uiCurrLineBytes);
rc = RC_SET( NE_SFLM_INVALID_SQL);
goto Exit;
}
}
else
{
setErrInfo( m_uiCurrLineNum,
uiTokenLineOffset,
SQL_ERR_INVALID_SQL_STATEMENT,
m_uiCurrLineFilePos,
m_uiCurrLineBytes);
rc = RC_SET( NE_SFLM_INVALID_SQL);
goto Exit;
}
}
// 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 == NE_SFLM_EOF_HIT)
{
rc = NE_SFLM_OK;
}
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);
}