diff --git a/sql/src/flcreate.cpp b/sql/src/createdatabase.cpp similarity index 65% rename from sql/src/flcreate.cpp rename to sql/src/createdatabase.cpp index 5664928..5abf88b 100644 --- a/sql/src/flcreate.cpp +++ b/sql/src/createdatabase.cpp @@ -25,220 +25,6 @@ #include "flaimsys.h" -/*API~*********************************************************************** -Desc: Creates a new FLAIM database. -*END************************************************************************/ -RCODE F_DbSystem::dbCreate( - const char * pszFilePath, - const char * pszDataDir, - const char * pszRflDir, - SFLM_CREATE_OPTS * pCreateOpts, - FLMBOOL bTempDb, - F_Db ** ppDb) -{ - RCODE rc = NE_SFLM_OK; - F_Db * pDb = NULL; - F_Database * pDatabase = NULL; - FLMBOOL bDatabaseCreated = FALSE; - FLMBOOL bNewDatabase = FALSE; - FLMBOOL bMutexLocked = FALSE; - FLMUINT uiRflToken = 0; - - // Make sure the path looks valid - - if (!pszFilePath || !pszFilePath [0]) - { - rc = RC_SET( NE_FLM_IO_INVALID_FILENAME); - goto Exit; - } - - // Allocate and initialize an F_Db structure. - - if (RC_BAD( rc = allocDb( &pDb, FALSE))) - { - goto Exit; - } - - f_mutexLock( gv_SFlmSysData.hShareMutex); - bMutexLocked = TRUE; - - // Free any unused structures that have been unused for the maximum - // amount of time. May unlock and re-lock the global mutex. - - checkNotUsedObjects(); - - for( ;;) - { - - // See if we already have the file open. - // May unlock and re-lock the global mutex. - - if (RC_BAD( rc = findDatabase( pszFilePath, pszDataDir, &pDatabase))) - { - goto Exit; - } - - // Didn't find the database - - if (!pDatabase) - { - break; - } - - // See if file is open or being opened. - - if (pDatabase->m_uiOpenIFDbCount || (pDatabase->m_uiFlags & DBF_BEING_OPENED)) - { - rc = RC_SET( NE_FLM_IO_ACCESS_DENIED); - goto Exit; - } - - // Free the F_Database object. May temporarily unlock the global mutex. - // For this reason, we must call findDatabase again (see above) after - // freeing the object. - - pDatabase->freeDatabase(); - pDatabase = NULL; - } - - // Allocate a new F_Database object - - if (RC_BAD( rc = allocDatabase( pszFilePath, pszDataDir, bTempDb, &pDatabase))) - { - goto Exit; - } - bNewDatabase = TRUE; - - // Link the F_Db object to the F_Database object. - - rc = pDb->linkToDatabase( pDatabase); - f_mutexUnlock( gv_SFlmSysData.hShareMutex); - bMutexLocked = FALSE; - if (RC_BAD( rc)) - { - goto Exit; - } - - // If the database has not already been created, do so now. - - // Determine what to set file block size to. - - if (pCreateOpts != NULL) - { - pDb->m_pSFileHdl->setBlockSize( - flmAdjustBlkSize( pCreateOpts->uiBlockSize)); - } - else - { - pDb->m_pSFileHdl->setBlockSize( SFLM_DEFAULT_BLKSIZ); - } - - if (RC_OK( gv_SFlmSysData.pFileSystem->doesFileExist( pszFilePath))) - { - rc = RC_SET( NE_SFLM_DATABASE_EXISTS); - goto Exit; - } - - // Create the .db file. - - pDb->m_pSFileHdl->setMaxAutoExtendSize( gv_SFlmSysData.uiMaxFileSize); - pDb->m_pSFileHdl->setExtendSize( pDb->m_pDatabase->m_uiFileExtendSize); - if (RC_BAD( rc = pDb->m_pSFileHdl->createFile( 0))) - { - goto Exit; - } - bDatabaseCreated = TRUE; - - (void)flmStatGetDb( &pDb->m_Stats, pDatabase, - 0, &pDb->m_pDbStats, NULL, NULL); - - // We must have exclusive access. Create a lock file for that - // purpose, if there is not already a lock file. - // NOTE: No need for a lock file if this is a temporary database. - - if (!bTempDb) - { - if (RC_BAD( rc = pDatabase->getExclAccess( pszFilePath))) - { - goto Exit; - } - } - - if (RC_BAD( rc = pDb->initDbFiles( pszRflDir, pCreateOpts))) - { - goto Exit; - } - - // Disable RFL logging (m_pRfl was initialized in initDbFiles) - - if( pDatabase->m_pRfl) - { - pDatabase->m_pRfl->disableLogging( &uiRflToken); - } - - // Set FFILE stuff to same state as a completed checkpoint. - - pDatabase->m_uiFirstLogCPBlkAddress = 0; - pDatabase->m_uiLastCheckpointTime = (FLMUINT)FLM_GET_TIMER(); - - // Create a checkpoint thread - no need if this is a temporary - // database. - - if (!bTempDb) - { - if (RC_BAD( rc = pDatabase->startCPThread())) - { - goto Exit; - } - - if( RC_BAD( rc = pDatabase->startMaintThread())) - { - goto Exit; - } - } - -Exit: - - if (bMutexLocked) - { - f_mutexUnlock( gv_SFlmSysData.hShareMutex); - } - - if (pDb) - { - pDb->completeOpenOrCreate( rc, bNewDatabase); - - // completeOpenOrCreate will delete pDb if RC_BAD( rc) - - if (RC_BAD( rc)) - { - pDb = NULL; - } - else - { - *ppDb = pDb; - pDb = NULL; // This isn't strictly necessary, but it makes it - // obvious that we are no longer using the object. - } - } - - if (RC_BAD( rc)) - { - if (bDatabaseCreated) - { - F_DbSystem dbSystem; - - dbSystem.dbRemove( pszFilePath, pszDataDir, pszRflDir, TRUE); - } - } - else if( uiRflToken) - { - pDatabase->m_pRfl->enableLogging( &uiRflToken); - } - - return( rc); -} - /**************************************************************************** Desc: Create a database - initialize all physical areas & data dictionary. ****************************************************************************/ @@ -467,3 +253,464 @@ Exit: return( rc); } + +//------------------------------------------------------------------------------ +// Desc: Creates a new database. +//------------------------------------------------------------------------------ +RCODE F_DbSystem::createDatabase( + const char * pszFilePath, + const char * pszDataDir, + const char * pszRflDir, + SFLM_CREATE_OPTS * pCreateOpts, + FLMBOOL bTempDb, + F_Db ** ppDb) +{ + RCODE rc = NE_SFLM_OK; + F_Db * pDb = NULL; + F_Database * pDatabase = NULL; + FLMBOOL bDatabaseCreated = FALSE; + FLMBOOL bNewDatabase = FALSE; + FLMBOOL bMutexLocked = FALSE; + FLMUINT uiRflToken = 0; + + // Make sure the path looks valid + + if (!pszFilePath || !pszFilePath [0]) + { + rc = RC_SET( NE_FLM_IO_INVALID_FILENAME); + goto Exit; + } + + // Allocate and initialize an F_Db structure. + + if (RC_BAD( rc = allocDb( &pDb, FALSE))) + { + goto Exit; + } + + f_mutexLock( gv_SFlmSysData.hShareMutex); + bMutexLocked = TRUE; + + // Free any unused structures that have been unused for the maximum + // amount of time. May unlock and re-lock the global mutex. + + checkNotUsedObjects(); + + for( ;;) + { + + // See if we already have the file open. + // May unlock and re-lock the global mutex. + + if (RC_BAD( rc = findDatabase( pszFilePath, pszDataDir, &pDatabase))) + { + goto Exit; + } + + // Didn't find the database + + if (!pDatabase) + { + break; + } + + // See if file is open or being opened. + + if (pDatabase->m_uiOpenIFDbCount || (pDatabase->m_uiFlags & DBF_BEING_OPENED)) + { + rc = RC_SET( NE_FLM_IO_ACCESS_DENIED); + goto Exit; + } + + // Free the F_Database object. May temporarily unlock the global mutex. + // For this reason, we must call findDatabase again (see above) after + // freeing the object. + + pDatabase->freeDatabase(); + pDatabase = NULL; + } + + // Allocate a new F_Database object + + if (RC_BAD( rc = allocDatabase( pszFilePath, pszDataDir, bTempDb, &pDatabase))) + { + goto Exit; + } + bNewDatabase = TRUE; + + // Link the F_Db object to the F_Database object. + + rc = pDb->linkToDatabase( pDatabase); + f_mutexUnlock( gv_SFlmSysData.hShareMutex); + bMutexLocked = FALSE; + if (RC_BAD( rc)) + { + goto Exit; + } + + // If the database has not already been created, do so now. + + // Determine what to set file block size to. + + if (pCreateOpts != NULL) + { + pDb->m_pSFileHdl->setBlockSize( + flmAdjustBlkSize( pCreateOpts->uiBlockSize)); + } + else + { + pDb->m_pSFileHdl->setBlockSize( SFLM_DEFAULT_BLKSIZ); + } + + if (RC_OK( gv_SFlmSysData.pFileSystem->doesFileExist( pszFilePath))) + { + rc = RC_SET( NE_SFLM_DATABASE_EXISTS); + goto Exit; + } + + // Create the .db file. + + pDb->m_pSFileHdl->setMaxAutoExtendSize( gv_SFlmSysData.uiMaxFileSize); + pDb->m_pSFileHdl->setExtendSize( pDb->m_pDatabase->m_uiFileExtendSize); + if (RC_BAD( rc = pDb->m_pSFileHdl->createFile( 0))) + { + goto Exit; + } + bDatabaseCreated = TRUE; + + (void)flmStatGetDb( &pDb->m_Stats, pDatabase, + 0, &pDb->m_pDbStats, NULL, NULL); + + // We must have exclusive access. Create a lock file for that + // purpose, if there is not already a lock file. + // NOTE: No need for a lock file if this is a temporary database. + + if (!bTempDb) + { + if (RC_BAD( rc = pDatabase->getExclAccess( pszFilePath))) + { + goto Exit; + } + } + + if (RC_BAD( rc = pDb->initDbFiles( pszRflDir, pCreateOpts))) + { + goto Exit; + } + + // Disable RFL logging (m_pRfl was initialized in initDbFiles) + + if( pDatabase->m_pRfl) + { + pDatabase->m_pRfl->disableLogging( &uiRflToken); + } + + // Set FFILE stuff to same state as a completed checkpoint. + + pDatabase->m_uiFirstLogCPBlkAddress = 0; + pDatabase->m_uiLastCheckpointTime = (FLMUINT)FLM_GET_TIMER(); + + // Create a checkpoint thread - no need if this is a temporary + // database. + + if (!bTempDb) + { + if (RC_BAD( rc = pDatabase->startCPThread())) + { + goto Exit; + } + + if( RC_BAD( rc = pDatabase->startMaintThread())) + { + goto Exit; + } + } + +Exit: + + if (bMutexLocked) + { + f_mutexUnlock( gv_SFlmSysData.hShareMutex); + } + + if (pDb) + { + pDb->completeOpenOrCreate( rc, bNewDatabase); + + // completeOpenOrCreate will delete pDb if RC_BAD( rc) + + if (RC_BAD( rc)) + { + pDb = NULL; + } + else + { + *ppDb = pDb; + pDb = NULL; // This isn't strictly necessary, but it makes it + // obvious that we are no longer using the object. + } + } + + if (RC_BAD( rc)) + { + if (bDatabaseCreated) + { + F_DbSystem dbSystem; + + dbSystem.dbRemove( pszFilePath, pszDataDir, pszRflDir, TRUE); + } + } + else if( uiRflToken) + { + pDatabase->m_pRfl->enableLogging( &uiRflToken); + } + + return( rc); +} + +//------------------------------------------------------------------------------ +// Desc: Process the create database statement. The "CREATE DATABASE" keywords +// have already been parsed. +//------------------------------------------------------------------------------ +RCODE SQLStatement::processCreateDatabase( void) +{ + RCODE rc = NE_SFLM_OK; + char szDatabaseName [F_PATH_MAX_SIZE + 1]; + FLMUINT uiDatabaseNameLen; + char szDataDirName [F_PATH_MAX_SIZE + 1]; + FLMUINT uiDataDirNameLen; + char szRflDirName [F_PATH_MAX_SIZE + 1]; + FLMUINT uiRflDirNameLen; + char szLanguage [10]; + FLMUINT uiLanguageLen; + SFLM_CREATE_OPTS createOpts; + F_DbSystem dbSystem; + + // SYNTAX: CREATE DATABASE databasename [CREATE_OPTIONS( =value,...)] + + // Whitespace must follow the "CREATE DATABASE" + + if (RC_BAD( rc = skipWhitespace( TRUE))) + { + goto Exit; + } + + // Get the database name. + + if (RC_BAD( rc = getUTF8String( FALSE, (FLMBYTE *)szDatabaseName, + sizeof( szDatabaseName), + &uiDatabaseNameLen))) + { + goto Exit; + } + + szDataDirName [0] = 0; + szRflDirName [0] = 0; + + createOpts.uiBlockSize = SFLM_DEFAULT_BLKSIZ; + createOpts.uiVersionNum = SFLM_CURRENT_VERSION_NUM; + createOpts.uiMinRflFileSize = SFLM_DEFAULT_MIN_RFL_FILE_SIZE; + createOpts.uiMaxRflFileSize = SFLM_DEFAULT_MAX_RFL_FILE_SIZE; + createOpts.bKeepRflFiles = SFLM_DEFAULT_KEEP_RFL_FILES_FLAG; + createOpts.bLogAbortedTransToRfl = SFLM_DEFAULT_LOG_ABORTED_TRANS_FLAG; + createOpts.uiDefaultLanguage = SFLM_DEFAULT_LANG; + + // See if there are other create options. + + // Whitespace must follow the database name + + if (RC_BAD( rc = skipWhitespace( TRUE))) + { + if (rc == NE_SFLM_EOF_HIT) + { + rc = NE_SFLM_OK; + } + else + { + goto Exit; + } + } + + // See if there are "CREATE_OPTIONS" + + if (lineHasToken( "create_options")) + { + if (RC_BAD( rc = skipWhitespace( FALSE))) + { + goto Exit; + } + + // Left paren must follow "CREATE_OPTIONS" + + if (lineHasToken( "(")) + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset, + SQL_ERR_EXPECTING_LPAREN, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } + + // Get all of the create options + + for (;;) + { + if (RC_BAD( rc = skipWhitespace( FALSE))) + { + goto Exit; + } + + // See if we are at the end of the list of create options. + + if (lineHasToken( ")")) + { + break; + } + else if (lineHasToken( "data_dir")) + { + if (RC_BAD( rc = getUTF8String( TRUE, (FLMBYTE *)szDataDirName, + sizeof( szDataDirName), + &uiDataDirNameLen))) + { + goto Exit; + } + } + else if (lineHasToken( "rfl_dir")) + { + if (RC_BAD( rc = getUTF8String( TRUE, (FLMBYTE *)szRflDirName, + sizeof( szRflDirName), + &uiRflDirNameLen))) + { + goto Exit; + } + } + else if (lineHasToken( "block_size")) + { + if (RC_BAD( rc = getUINT( TRUE, &createOpts.uiBlockSize))) + { + goto Exit; + } + if (!dbSystem.validBlockSize( createOpts.uiBlockSize)) + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset, + SQL_ERR_INVALID_BLOCK_SIZE, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } + } + else if (lineHasToken( "min_rfl_size")) + { + if (RC_BAD( rc = getUINT( TRUE, &createOpts.uiMinRflFileSize))) + { + goto Exit; + } + } + else if (lineHasToken( "max_rfl_size")) + { + if (RC_BAD( rc = getUINT( TRUE, &createOpts.uiMaxRflFileSize))) + { + goto Exit; + } + } + else if (lineHasToken( "keep_rfl")) + { + if (RC_BAD( rc = getBool( TRUE, &createOpts.bKeepRflFiles))) + { + goto Exit; + } + } + else if (lineHasToken( "log_aborted_trans")) + { + if (RC_BAD( rc = getBool( TRUE, &createOpts.bLogAbortedTransToRfl))) + { + goto Exit; + } + } + else if (lineHasToken( "language")) + { + if (RC_BAD( rc = getUTF8String( TRUE, (FLMBYTE *)szLanguage, + sizeof( szLanguage), + &uiLanguageLen))) + { + goto Exit; + } + createOpts.uiDefaultLanguage = f_languageToNum( szLanguage); + + // If the default language was returned, make sure that the + // string corresponds to it. + + if (createOpts.uiDefaultLanguage == FLM_US_LANG) + { + if (f_stricmp( szLanguage, "US") != 0) + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset, + SQL_ERR_INVALID_LANGUAGE, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } + } + } + else + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset, + SQL_ERR_INVALID_DB_CREATE_PARAM, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } + + // Skip any white space + + if (RC_BAD( rc = skipWhitespace( FALSE))) + { + goto Exit; + } + + // See if we are at the end of the list of create options + + 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; + } + } + } + + if (m_pDb) + { + m_pDb->Release(); + m_pDb = NULL; + } + + // Create the database + + if (RC_BAD( rc = dbSystem.createDatabase( szDatabaseName, szDataDirName, + szRflDirName, &createOpts, &m_pDb))) + { + goto Exit; + } + +Exit: + + return( rc); +} + diff --git a/sql/src/createindex.cpp b/sql/src/createindex.cpp new file mode 100644 index 0000000..66fdf2e --- /dev/null +++ b/sql/src/createindex.cpp @@ -0,0 +1,867 @@ +//------------------------------------------------------------------------------ +// Desc: This module contains routines that will create an index. +// +// 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: Create a new index in the database. Caller should have already +// verified that the index name is unique and that the table number and +// column numbers are valid. This routine will assign an index number. +//------------------------------------------------------------------------------ +RCODE F_Db::createIndex( + FLMUINT uiTableNum, + FLMUINT uiIndexNum, + const char * pszIndexName, + FLMUINT uiIndexNameLen, + FLMUINT uiEncDefNum, + FLMUINT uiFlags, + F_INDEX_COL_DEF * pIxColDefs, + FLMUINT uiNumIxColDefs) +{ + RCODE rc = NE_SFLM_OK; + F_Row * pRow = NULL; + F_TABLE * pTable; + F_COLUMN * pColumn; + F_INDEX_COL_DEF * pIxColDef; + char szLanguage [10]; + FLMUINT uiLanguageLen; + FLMUINT uiKeyComponent; + const char * pszIndexOn; + FLMUINT uiIndexOnLen; + F_INDEX * pIndex; + + // Create a new dictionary, if we don't already have one. + + if (!(m_uiFlags & FDB_UPDATED_DICTIONARY)) + { + if (RC_BAD( rc = dictClone())) + { + goto Exit; + } + } + + // Create a row for the table in the table definition table. + + if (RC_BAD( rc = gv_SFlmSysData.pRowCacheMgr->createRow( this, + SFLM_TBLNUM_INDEXES, &pRow))) + { + goto Exit; + } + + // Determine the index number to use - find lowest non-used index + // number. + + if (!uiIndexNum) + { + uiIndexNum = 1; + while (uiIndexNum <= m_pDict->m_uiHighestIndexNum) + { + if (!m_pDict->m_pIndexTbl [uiIndexNum - 1].uiIndexNum) + { + break; + } + uiIndexNum++; + } + } + + // The call to addIndex will initialize either the empty slot we found, or + // the next slot at the end of the index table. It will reallocate the + // index array if necessary. + + // Remove the IXD_HAS_SUBSTRING flag - will reset later based on the + // column definitions for the index. Also remove the IXD_SYSTEM flag if + // it was set. + + uiFlags &= (~(IXD_HAS_SUBSTRING | IXD_SYSTEM)); + if (RC_BAD( rc = m_pDict->addIndex( uiIndexNum, pRow->m_ui64RowId, + pszIndexName, uiTableNum, uiEncDefNum, uiFlags, + uiNumIxColDefs, 0, + m_pDatabase->m_uiDefaultLanguage, 0))) + { + goto Exit; + } + + pTable = m_pDict->getTable( uiTableNum); + pIndex = m_pDict->getIndex( uiIndexNum); + + // Populate the columns for the row in the table definition table. + + if (RC_BAD( rc = pRow->setUTF8( this, SFLM_COLNUM_INDEXES_INDEX_NAME, + pszIndexName, uiIndexNameLen, uiIndexNameLen))) + { + goto Exit; + } + if (RC_BAD( rc = pRow->setUINT( this, SFLM_COLNUM_INDEXES_INDEX_NUM, + uiIndexNum))) + { + goto Exit; + } + if (RC_BAD( rc = pRow->setUINT( this, SFLM_COLNUM_INDEXES_TABLE_NUM, + uiTableNum))) + { + goto Exit; + } + if (RC_BAD( rc = pRow->setUINT( this, SFLM_COLNUM_INDEXES_ENCDEF_NUM, + uiEncDefNum))) + { + goto Exit; + } + f_languageToStr( pIndex->uiLanguage, szLanguage); + uiLanguageLen = f_strlen( szLanguage); + if (RC_BAD( rc = pRow->setUTF8( this, SFLM_COLNUM_INDEXES_LANGUAGE, + szLanguage, uiLanguageLen, uiLanguageLen))) + { + goto Exit; + } + if (RC_BAD( rc = pRow->setUINT( this, SFLM_COLNUM_INDEXES_NUM_KEY_COMPONENTS, + uiNumIxColDefs))) + { + goto Exit; + } + if (uiFlags & IXD_ABS_POS) + { + if (RC_BAD( rc = pRow->setUTF8( this, SFLM_COLNUM_INDEXES_KEEP_ABS_POS_INFO, + "yes", 3, 3))) + { + goto Exit; + } + } + if (uiFlags & IXD_KEYS_UNIQUE) + { + if (RC_BAD( rc = pRow->setUTF8( this, SFLM_COLNUM_INDEXES_KEYS_UNIQUE, + "yes", 3, 3))) + { + goto Exit; + } + } + if (uiFlags & IXD_SUSPENDED) + { + if (RC_BAD( rc = pRow->setUTF8( this, SFLM_COLNUM_INDEXES_INDEX_STATE, + SFLM_INDEX_SUSPENDED_STR, + SFLM_INDEX_SUSPENDED_STR_LEN, + SFLM_INDEX_SUSPENDED_STR_LEN))) + { + goto Exit; + } + } + else if (uiFlags & IXD_OFFLINE) + { + if (RC_BAD( rc = pRow->setUTF8( this, SFLM_COLNUM_INDEXES_INDEX_STATE, + SFLM_INDEX_OFFLINE_STR, + SFLM_INDEX_OFFLINE_STR_LEN, + SFLM_INDEX_OFFLINE_STR_LEN))) + { + goto Exit; + } + } + + // Add all of the index columns to the index column table. + + for (pIxColDef = pIxColDefs, uiKeyComponent = 1; + pIxColDef; + uiKeyComponent++, pIxColDef = pIxColDef->pNext) + { + + // Create a row for the column in the column definition table. + + if (pRow) + { + pRow->ReleaseRow(); + pRow = NULL; + } + if (RC_BAD( rc = gv_SFlmSysData.pRowCacheMgr->createRow( this, + SFLM_TBLNUM_INDEX_COMPONENTS, &pRow))) + { + goto Exit; + } + + // Verify that the column number is valid. Caller is responsible + // to pass in valid column numbers. + + pColumn = m_pDict->getColumn( pTable, pIxColDef->uiColumnNum); + flmAssert( pColumn); + + // Add the index component to the in-memory dictionary structures. + + if (RC_BAD( rc = m_pDict->addIndexComponent( uiIndexNum, pRow->m_ui64RowId, + pIxColDef->uiColumnNum, pIxColDef->uiFlags, + pIxColDef->uiCompareRules, + pIxColDef->uiLimit, uiKeyComponent, 0))) + { + goto Exit; + } + + // Populate the columns for the row in the column definition table. + + if (RC_BAD( rc = pRow->setUINT( this, SFLM_COLNUM_INDEX_COMP_INDEX_NUM, + uiIndexNum))) + { + goto Exit; + } + if (RC_BAD( rc = pRow->setUINT( this, SFLM_COLNUM_INDEX_COMP_COLUMN_NUM, + pIxColDef->uiColumnNum))) + { + goto Exit; + } + if (RC_BAD( rc = pRow->setUINT( this, SFLM_COLNUM_INDEX_COMP_KEY_COMPONENT, + uiKeyComponent))) + { + goto Exit; + } + if (pIxColDef->uiFlags & ICD_VALUE) + { + pszIndexOn = SFLM_VALUE_OPTION_STR; + uiIndexOnLen = SFLM_VALUE_OPTION_STR_LEN; + } + else if (pIxColDef->uiFlags & ICD_EACHWORD) + { + pszIndexOn = SFLM_EACHWORD_OPTION_STR; + uiIndexOnLen = SFLM_EACHWORD_OPTION_STR_LEN; + } + else if (pIxColDef->uiFlags & ICD_PRESENCE) + { + pszIndexOn = SFLM_PRESENCE_OPTION_STR; + uiIndexOnLen = SFLM_PRESENCE_OPTION_STR_LEN; + } + else if (pIxColDef->uiFlags & ICD_METAPHONE) + { + pszIndexOn = SFLM_METAPHONE_OPTION_STR; + uiIndexOnLen = SFLM_METAPHONE_OPTION_STR_LEN; + } + else + { + + // Assert here, because it has to be one of these five options to + // index on. + + flmAssert( pIxColDef->uiFlags & ICD_SUBSTRING); + pszIndexOn = SFLM_SUBSTRING_OPTION_STR; + uiIndexOnLen = SFLM_SUBSTRING_OPTION_STR_LEN; + } + if (RC_BAD( rc = pRow->setUTF8( this, SFLM_COLNUM_INDEX_COMP_INDEX_ON, + pszIndexOn, uiIndexOnLen, uiIndexOnLen))) + { + goto Exit; + } + + if (pIxColDef->uiCompareRules) + { + char szCompareRules [200]; + char * pszCompareRules = &szCompareRules [0]; + FLMUINT uiCompareRulesLen; + + if (pIxColDef->uiCompareRules & FLM_COMP_CASE_INSENSITIVE) + { + f_memcpy( pszCompareRules, SFLM_CASE_INSENSITIVE_OPTION_STR, + SFLM_CASE_INSENSITIVE_OPTION_STR_LEN); + pszCompareRules += SFLM_CASE_INSENSITIVE_OPTION_STR_LEN; + *pszCompareRules++ = ' '; + } + if (pIxColDef->uiCompareRules & FLM_COMP_COMPRESS_WHITESPACE) + { + f_memcpy( pszCompareRules, SFLM_COMPRESS_WHITESPACE_OPTION_STR, + SFLM_COMPRESS_WHITESPACE_OPTION_STR_LEN); + pszCompareRules += SFLM_COMPRESS_WHITESPACE_OPTION_STR_LEN; + *pszCompareRules++ = ' '; + } + if (pIxColDef->uiCompareRules & FLM_COMP_NO_WHITESPACE) + { + f_memcpy( pszCompareRules, SFLM_NO_WHITESPACE_OPTION_STR, + SFLM_NO_WHITESPACE_OPTION_STR_LEN); + pszCompareRules += SFLM_NO_WHITESPACE_OPTION_STR_LEN; + *pszCompareRules++ = ' '; + } + if (pIxColDef->uiCompareRules & FLM_COMP_NO_UNDERSCORES) + { + f_memcpy( pszCompareRules, SFLM_NOUNDERSCORE_OPTION_STR, + SFLM_NOUNDERSCORE_OPTION_STR_LEN); + pszCompareRules += SFLM_NOUNDERSCORE_OPTION_STR_LEN; + *pszCompareRules++ = ' '; + } + if (pIxColDef->uiCompareRules & FLM_COMP_NO_DASHES) + { + f_memcpy( pszCompareRules, SFLM_NODASH_OPTION_STR, + SFLM_NODASH_OPTION_STR_LEN); + pszCompareRules += SFLM_NODASH_OPTION_STR_LEN; + *pszCompareRules++ = ' '; + } + if (pIxColDef->uiCompareRules & FLM_COMP_WHITESPACE_AS_SPACE) + { + f_memcpy( pszCompareRules, SFLM_WHITESPACE_AS_SPACE_STR, + SFLM_WHITESPACE_AS_SPACE_STR_LEN); + pszCompareRules += SFLM_WHITESPACE_AS_SPACE_STR_LEN; + *pszCompareRules++ = ' '; + } + if (pIxColDef->uiCompareRules & FLM_COMP_IGNORE_LEADING_SPACE) + { + f_memcpy( pszCompareRules, SFLM_IGNORE_LEADINGSPACES_OPTION_STR, + SFLM_IGNORE_LEADINGSPACES_OPTION_STR_LEN); + pszCompareRules += SFLM_IGNORE_LEADINGSPACES_OPTION_STR_LEN; + *pszCompareRules++ = ' '; + } + if (pIxColDef->uiCompareRules & FLM_COMP_IGNORE_TRAILING_SPACE) + { + f_memcpy( pszCompareRules, SFLM_IGNORE_TRAILINGSPACES_OPTION_STR, + SFLM_IGNORE_TRAILINGSPACES_OPTION_STR_LEN); + pszCompareRules += SFLM_IGNORE_TRAILINGSPACES_OPTION_STR_LEN; + *pszCompareRules++ = ' '; + } + + // See if anything was set - extraneous bits are ignored. + + if (pszCompareRules != &szCompareRules [0]) + { + + // Get rid of the trailing space - every option added a trailing + // space to separate it from the next option, if any. + + pszCompareRules--; + *pszCompareRules = 0; + uiCompareRulesLen = (FLMUINT)(pszCompareRules - &szCompareRules [0]); + if (RC_BAD( rc = pRow->setUTF8( this, SFLM_COLNUM_INDEX_COMP_COMPARE_RULES, + szCompareRules, + uiCompareRulesLen, uiCompareRulesLen))) + { + goto Exit; + } + } + } + + if (pIxColDef->uiFlags & ICD_DESCENDING) + { + if (RC_BAD( rc = pRow->setUTF8( this, SFLM_COLNUM_INDEX_COMP_SORT_DESCENDING, + "yes", 3, 3))) + { + goto Exit; + } + } + if (pIxColDef->uiFlags & ICD_MISSING_HIGH) + { + if (RC_BAD( rc = pRow->setUTF8( this, SFLM_COLNUM_INDEX_COMP_SORT_MISSING_HIGH, + "yes", 3, 3))) + { + goto Exit; + } + } + if (pIxColDef->uiLimit) + { + if (RC_BAD( rc = pRow->setUINT( this, SFLM_COLNUM_INDEX_COMP_LIMIT, + pIxColDef->uiLimit))) + { + goto Exit; + } + } + } + + if (RC_BAD( rc = m_pDatabase->m_pRfl->logCreateIndex( this, uiTableNum, + uiIndexNum, pszIndexName, uiIndexNameLen, + uiEncDefNum, pIxColDefs, uiFlags))) + { + goto Exit; + } + + // Build the index - but only if this is not a replay of the + // roll-forward log. If we are replaying the roll-forward log, + // it will have packets for building the index. + + if (!(uiFlags & IXD_SUSPENDED) && !(m_uiFlags & FDB_REPLAYING_RFL)) + { + // Unique indexes cannot be built off-line, because we need to make + // sure there are no non-unique keys that will get built. + + if ((uiFlags & IXD_OFFLINE) && !(uiFlags & IXD_KEYS_UNIQUE)) + { + if (RC_BAD( rc = addToStartList( uiIndexNum))) + { + goto Exit; + } + } + else + { + + // There may be "new" rows in the row cache. + // Need to flush them to the database so that + // the B-Tree lookups done by the indexing code will + // work correctly + + if( RC_BAD( rc = flushDirtyRows())) + { + goto Exit; + } + + // Build index in foreground. + + if (RC_BAD( rc = indexSetOfRows( uiIndexNum, 1, + FLM_MAX_UINT64, m_pIxStatus, m_pIxClient, NULL, NULL, NULL))) + { + goto Exit; + } + } + } + +Exit: + + if (pRow) + { + pRow->ReleaseRow(); + } + + return( rc); +} + +//------------------------------------------------------------------------------ +// Desc: Process the create index statement. The "CREATE INDEX" keywords +// or "CREATE UNIQUE INDEX" keywords have already been parsed. +//------------------------------------------------------------------------------ +RCODE SQLStatement::processCreateIndex( + FLMBOOL bUnique) +{ + RCODE rc = NE_SFLM_OK; + FLMBOOL bStartedTrans = FALSE; + char szIndexName [MAX_SQL_NAME_LEN + 1]; + FLMUINT uiIndexNameLen; + char szTableName [MAX_SQL_NAME_LEN + 1]; + FLMUINT uiTableNameLen; + char szColumnName [MAX_SQL_NAME_LEN + 1]; + FLMUINT uiColumnNameLen; + F_INDEX_COL_DEF * pIxColDef; + F_INDEX_COL_DEF * pFirstIxColDef; + F_INDEX_COL_DEF * pLastIxColDef; + FLMUINT uiNumIxColumns; + F_TABLE * pTable; + F_INDEX * pIndex; + F_COLUMN * pColumn; + FLMBOOL bDone; + FLMUINT uiFlags; + + // 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: CREATE [UNIQUE] INDEX ON + // ( [DESC], ...) + + if (bUnique) + { + uiFlags = IXD_KEYS_UNIQUE; + } + else + { + uiFlags = 0; + } + + // Whitespace must follow the "CREATE [UNIQUE] INDEX" + + if (RC_BAD( rc = skipWhitespace( TRUE))) + { + goto Exit; + } + + // Get the index name - index name must NOT already exist + + if (RC_BAD( rc = getIndexName( FALSE, szIndexName, sizeof( szIndexName), + &uiIndexNameLen, &pIndex))) + { + goto Exit; + } + + // Whitespace must follow index name. + + if (RC_BAD( rc = skipWhitespace( TRUE))) + { + goto Exit; + } + + // The keyword "ON" must follow + + if (!lineHasToken( "on")) + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset, + SQL_ERR_EXPECTING_ON, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } + + // Whitespace must follow "ON" + + if (RC_BAD( rc = skipWhitespace( TRUE))) + { + goto Exit; + } + + // Get the table name - must exist. + + if (RC_BAD( rc = getTableName( TRUE, szTableName, sizeof( szTableName), + &uiTableNameLen, &pTable))) + { + goto Exit; + } + + // Skip any whitespace after the table name. + + if (RC_BAD( rc = skipWhitespace( FALSE))) + { + goto Exit; + } + + // Left paren must follow table name + + if (!lineHasToken( "(")) + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset, + SQL_ERR_EXPECTING_LPAREN, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } + + // Get the columns to be indexed. + + pFirstIxColDef = NULL; + pLastIxColDef = NULL; + uiNumIxColumns = 0; + for (;;) + { + + if (RC_BAD( rc = skipWhitespace( FALSE))) + { + goto Exit; + } + + // Get the column name + + if (RC_BAD( rc = getName( szColumnName, sizeof( szColumnName), + &uiColumnNameLen))) + { + goto Exit; + } + flmAssert( uiColumnNameLen); + + // Make sure it is a valid column for the table. + + if ((pColumn = m_pDb->m_pDict->findColumn( pTable, szColumnName)) == NULL) + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset - 1, + SQL_ERR_UNDEFINED_COLUMN, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } + + // Allocate an index column def structure + + if (RC_BAD( rc = m_tmpPool.poolAlloc( sizeof( F_INDEX_COL_DEF), + (void **)&pIxColDef))) + { + goto Exit; + } + uiNumIxColumns++; + + pIxColDef->uiColumnNum = pColumn->uiColumnNum; + pIxColDef->uiFlags = 0; + pIxColDef->uiCompareRules = 0; + pIxColDef->uiLimit = 0; + pIxColDef->pNext = NULL; + if (pLastIxColDef) + { + pLastIxColDef->pNext = pIxColDef; + } + else + { + pFirstIxColDef = pIxColDef; + } + pLastIxColDef = pIxColDef; + + // Get all of the index options and comparison rules. + + bDone = FALSE; + for (;;) + { + if (lineHasToken( ",")) + { + break; + } + if (lineHasToken( ")")) + { + bDone = TRUE; + break; + } + + // Better be whitespace, followed by a valid keyword or a comma + // or a right paren. + + if (RC_BAD( rc = skipWhitespace( TRUE))) + { + goto Exit; + } + + if (lineHasToken( ",")) + { + break; + } + if (lineHasToken( ")")) + { + bDone = TRUE; + break; + } + if (lineHasToken( SFLM_VALUE_OPTION_STR)) + { + if ((pIxColDef->uiFlags & + (ICD_VALUE | ICD_EACHWORD | ICD_PRESENCE | ICD_METAPHONE | ICD_SUBSTRING))) + { +Multiple_Ix_Options: + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset - 1, + SQL_ERR_MULTIPLE_INDEX_OPTIONS, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } + pIxColDef->uiFlags |= ICD_VALUE; + } + else if (lineHasToken( SFLM_EACHWORD_OPTION_STR)) + { + if ((pIxColDef->uiFlags & + (ICD_VALUE | ICD_EACHWORD | ICD_PRESENCE | ICD_METAPHONE | ICD_SUBSTRING))) + { + goto Multiple_Ix_Options; + } + if (pColumn->eDataTyp != SFLM_STRING_TYPE) + { +Invalid_Ix_Option: + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset - 1, + SQL_ERR_INVALID_COL_INDEX_OPTION, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } + pIxColDef->uiFlags |= ICD_EACHWORD; + } + else if (lineHasToken( SFLM_PRESENCE_OPTION_STR)) + { + if ((pIxColDef->uiFlags & + (ICD_VALUE | ICD_EACHWORD | ICD_PRESENCE | ICD_METAPHONE | ICD_SUBSTRING))) + { + goto Multiple_Ix_Options; + } + pIxColDef->uiFlags |= ICD_PRESENCE; + } + else if (lineHasToken( SFLM_METAPHONE_OPTION_STR)) + { + if ((pIxColDef->uiFlags & + (ICD_VALUE | ICD_EACHWORD | ICD_PRESENCE | ICD_METAPHONE | ICD_SUBSTRING))) + { + goto Multiple_Ix_Options; + } + if (pColumn->eDataTyp != SFLM_STRING_TYPE) + { + goto Invalid_Ix_Option; + } + pIxColDef->uiFlags |= ICD_METAPHONE; + } + else if (lineHasToken( SFLM_SUBSTRING_OPTION_STR)) + { + if ((pIxColDef->uiFlags & + (ICD_VALUE | ICD_EACHWORD | ICD_PRESENCE | ICD_METAPHONE | ICD_SUBSTRING))) + { + goto Multiple_Ix_Options; + } + if (pColumn->eDataTyp != SFLM_STRING_TYPE) + { + goto Invalid_Ix_Option; + } + pIxColDef->uiFlags |= ICD_SUBSTRING; + } + else if (lineHasToken( SFLM_DESCENDING_OPTION_STR) || + lineHasToken( "desc")) + { + pIxColDef->uiFlags |= ICD_DESCENDING; + } + else if (lineHasToken( SFLM_ASCENDING_OPTION_STR) || + lineHasToken( "asc")) + { + pIxColDef->uiFlags &= (~(ICD_DESCENDING)); + } + else if (lineHasToken( SFLM_SORT_MISSING_HIGH_OPTION_STR)) + { + pIxColDef->uiFlags |= ICD_MISSING_HIGH; + } + else if (lineHasToken( SFLM_SORT_MISSING_LOW_OPTION_STR)) + { + pIxColDef->uiFlags &= (~(ICD_MISSING_HIGH)); + } + else if (lineHasToken( SFLM_CASE_INSENSITIVE_OPTION_STR)) + { + if (pColumn->eDataTyp != SFLM_STRING_TYPE) + { + goto Invalid_Ix_Option; + } + pIxColDef->uiCompareRules |= FLM_COMP_CASE_INSENSITIVE; + } + else if (lineHasToken( SFLM_COMPRESS_WHITESPACE_OPTION_STR)) + { + if (pColumn->eDataTyp != SFLM_STRING_TYPE) + { + goto Invalid_Ix_Option; + } + pIxColDef->uiCompareRules |= FLM_COMP_COMPRESS_WHITESPACE; + } + else if (lineHasToken( SFLM_NO_WHITESPACE_OPTION_STR)) + { + if (pColumn->eDataTyp != SFLM_STRING_TYPE) + { + goto Invalid_Ix_Option; + } + pIxColDef->uiCompareRules |= FLM_COMP_NO_WHITESPACE; + } + else if (lineHasToken( SFLM_NOUNDERSCORE_OPTION_STR)) + { + if (pColumn->eDataTyp != SFLM_STRING_TYPE) + { + goto Invalid_Ix_Option; + } + pIxColDef->uiCompareRules |= FLM_COMP_NO_UNDERSCORES; + } + else if (lineHasToken( SFLM_NODASH_OPTION_STR)) + { + if (pColumn->eDataTyp != SFLM_STRING_TYPE) + { + goto Invalid_Ix_Option; + } + pIxColDef->uiCompareRules |= FLM_COMP_NO_DASHES; + } + else if (lineHasToken( SFLM_WHITESPACE_AS_SPACE_STR)) + { + if (pColumn->eDataTyp != SFLM_STRING_TYPE) + { + goto Invalid_Ix_Option; + } + pIxColDef->uiCompareRules |= FLM_COMP_WHITESPACE_AS_SPACE; + } + else if (lineHasToken( SFLM_IGNORE_LEADINGSPACES_OPTION_STR)) + { + if (pColumn->eDataTyp != SFLM_STRING_TYPE) + { + goto Invalid_Ix_Option; + } + pIxColDef->uiCompareRules |= FLM_COMP_IGNORE_LEADING_SPACE; + } + else if (lineHasToken( SFLM_IGNORE_TRAILINGSPACES_OPTION_STR)) + { + if (pColumn->eDataTyp != SFLM_STRING_TYPE) + { + goto Invalid_Ix_Option; + } + pIxColDef->uiCompareRules |= FLM_COMP_IGNORE_TRAILING_SPACE; + } + else if (lineHasToken( SFLM_LIMIT_OPTION_STR)) + { + if (pColumn->eDataTyp != SFLM_STRING_TYPE && + pColumn->eDataTyp != SFLM_BINARY_TYPE) + { + goto Invalid_Ix_Option; + } + if (RC_BAD( rc = getUINT( TRUE, &pIxColDef->uiLimit))) + { + goto Exit; + } + } + else + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset, + SQL_ERR_INVALID_INDEX_OPTION, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } + } + if (!(pIxColDef->uiFlags & + (ICD_VALUE | ICD_EACHWORD | ICD_PRESENCE | ICD_METAPHONE | ICD_SUBSTRING))) + { + pIxColDef->uiFlags |= ICD_VALUE; + } + if (!pIxColDef->uiLimit) + { + if (pIxColDef->uiFlags & ICD_SUBSTRING) + { + pIxColDef->uiLimit = ICD_DEFAULT_SUBSTRING_LIMIT; + } + else + { + pIxColDef->uiLimit = ICD_DEFAULT_LIMIT; + } + } + + if (bDone) + { + break; + } + } + + // Create the index. + + if (RC_BAD( rc = m_pDb->createIndex( pTable->uiTableNum, 0, + szIndexName, uiIndexNameLen, 0, uiFlags, + pFirstIxColDef, uiNumIxColumns))) + { + goto Exit; + } + + // Commit the transaction if we started it + + if (bStartedTrans) + { + bStartedTrans = FALSE; + if (RC_BAD( rc = m_pDb->transCommit())) + { + goto Exit; + } + } + +Exit: + + if (bStartedTrans) + { + m_pDb->transAbort(); + } + + return( rc); +} + diff --git a/sql/src/createtable.cpp b/sql/src/createtable.cpp new file mode 100644 index 0000000..22eb3ea --- /dev/null +++ b/sql/src/createtable.cpp @@ -0,0 +1,595 @@ +//------------------------------------------------------------------------------ +// Desc: This module contains the routines for creating 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" + +//------------------------------------------------------------------------------ +// Desc: Create a new table in the database. Caller should have already +// verified that the table name is unique. This routine will verify +// that column names are unique in the table, and it will assign +// the table number as well as the column numbers. +//------------------------------------------------------------------------------ +RCODE F_Db::createTable( + FLMUINT uiTableNum, + const char * pszTableName, + FLMUINT uiTableNameLen, + FLMUINT uiEncDefNum, + F_COLUMN_DEF * pColumnDefs, + FLMUINT uiNumColumnDefs) +{ + RCODE rc = NE_SFLM_OK; + F_Row * pRow = NULL; + F_TABLE * pTable; + F_COLUMN * pColumn; + const char * pszTmp; + FLMUINT uiLen; + F_COLUMN_DEF * pColumnDef; + FLMUINT uiColumnNum; + + // Create a new dictionary, if we don't already have one. + + if (!(m_uiFlags & FDB_UPDATED_DICTIONARY)) + { + if (RC_BAD( rc = dictClone())) + { + goto Exit; + } + } + + // Create a row for the table in the table definition table. + + if (RC_BAD( rc = gv_SFlmSysData.pRowCacheMgr->createRow( this, + SFLM_TBLNUM_TABLES, &pRow))) + { + goto Exit; + } + + // Determine the table number to use - find lowest non-used table + // number. + + if (!uiTableNum) + { + uiTableNum = 1; + while (uiTableNum <= m_pDict->m_uiHighestTableNum) + { + if (!m_pDict->m_pTableTbl [uiTableNum - 1].uiTableNum) + { + break; + } + uiTableNum++; + } + } + + // The call to addTable will initialize either the empty slot we found, or + // the next slot at the end of the table table. It will reallocate the + // table array if necessary. + + if (RC_BAD( rc = m_pDict->addTable( uiTableNum, pRow->m_ui64RowId, + pszTableName, FALSE, uiNumColumnDefs, uiEncDefNum))) + { + goto Exit; + } + pTable = m_pDict->getTable( uiTableNum); + + // Populate the columns for the row in the table definition table. + + if (RC_BAD( rc = pRow->setUTF8( this, SFLM_COLNUM_TABLES_TABLE_NAME, + pszTableName, uiTableNameLen, uiTableNameLen))) + { + goto Exit; + } + if (RC_BAD( rc = pRow->setUINT( this, SFLM_COLNUM_TABLES_TABLE_NUM, + uiTableNum))) + { + goto Exit; + } + if (RC_BAD( rc = pRow->setUINT( this, SFLM_COLNUM_TABLES_ENCDEF_NUM, + uiEncDefNum))) + { + goto Exit; + } + if (RC_BAD( rc = pRow->setUINT( this, SFLM_COLNUM_TABLES_NUM_COLUMNS, + uiNumColumnDefs))) + { + goto Exit; + } + + // Add all of the columns to the column definition table. + + for (uiColumnNum = 0, pColumnDef = pColumnDefs; + pColumnDef; + pColumnDef = pColumnDef->pNext) + { + + // Create a row for the column in the column definition table. + + if (pRow) + { + pRow->ReleaseRow(); + pRow = NULL; + } + if (RC_BAD( rc = gv_SFlmSysData.pRowCacheMgr->createRow( this, + SFLM_TBLNUM_COLUMNS, &pRow))) + { + goto Exit; + } + + // Verify that the column name is unique. + + if ((pColumn = m_pDict->findColumn( pTable, + pColumnDef->pszColumnName)) != NULL) + { + rc = RC_SET( NE_SFLM_COLUMN_NAME_ALREADY_DEFINED); + goto Exit; + } + + // Add the column definition to the in-memory dictionary structures. + + uiColumnNum++; + if (RC_BAD( rc = m_pDict->addColumn( uiTableNum, pRow->m_ui64RowId, + uiColumnNum, pColumnDef->pszColumnName, + pColumnDef->uiFlags, pColumnDef->eColumnDataType, + pColumnDef->uiMaxLen, pColumnDef->uiEncDefNum))) + { + goto Exit; + } + + // Populate the columns for the row in the column definition table. + + if (RC_BAD( rc = pRow->setUINT( this, SFLM_COLNUM_COLUMNS_TABLE_NUM, + uiTableNum))) + { + goto Exit; + } + if (RC_BAD( rc = pRow->setUTF8( this, SFLM_COLNUM_COLUMNS_COLUMN_NAME, + pColumnDef->pszColumnName, + pColumnDef->uiColumnNameLen, + pColumnDef->uiColumnNameLen))) + { + goto Exit; + } + if (RC_BAD( rc = pRow->setUINT( this, SFLM_COLNUM_COLUMNS_COLUMN_NUM, + uiColumnNum))) + { + goto Exit; + } + switch (pColumnDef->eColumnDataType) + { + case SFLM_STRING_TYPE: + pszTmp = SFLM_STRING_OPTION_STR; + uiLen = SFLM_STRING_OPTION_STR_LEN; + break; + case SFLM_NUMBER_TYPE: + pszTmp = SFLM_INTEGER_OPTION_STR; + uiLen = SFLM_INTEGER_OPTION_STR_LEN; + break; + case SFLM_BINARY_TYPE: + pszTmp = SFLM_BINARY_OPTION_STR; + uiLen = SFLM_BINARY_OPTION_STR_LEN; + break; + default: + flmAssert( 0); + pszTmp = "Bad"; + uiLen = 3; + break; + } + if (RC_BAD( rc = pRow->setUTF8( this, SFLM_COLNUM_COLUMNS_DATA_TYPE, + pszTmp, uiLen, uiLen))) + { + goto Exit; + } + if (pColumnDef->uiMaxLen) + { + if (RC_BAD( rc = pRow->setUINT( this, SFLM_COLNUM_COLUMNS_MAX_LEN, + pColumnDef->uiMaxLen))) + { + goto Exit; + } + } + if (pColumnDef->uiEncDefNum) + { + if (RC_BAD( rc = pRow->setUINT( this, SFLM_COLNUM_COLUMNS_ENCDEF_NUM, + pColumnDef->uiEncDefNum))) + { + goto Exit; + } + } + if (pColumnDef->uiFlags & COL_READ_ONLY) + { + if (RC_BAD( rc = pRow->setUTF8( this, SFLM_COLNUM_COLUMNS_READ_ONLY, + "yes", 3, 3))) + { + goto Exit; + } + } + if (pColumnDef->uiFlags & COL_NULL_ALLOWED) + { + if (RC_BAD( rc = pRow->setUTF8( this, SFLM_COLNUM_COLUMNS_NULL_ALLOWED, + "yes", 3, 3))) + { + goto Exit; + } + } + } + + flmAssert( uiColumnNum == uiNumColumnDefs); + + if (RC_BAD( rc = m_pDatabase->m_pRfl->logCreateTable( this, uiTableNum, + pszTableName, uiTableNameLen, + uiEncDefNum, pColumnDefs))) + { + goto Exit; + } + +Exit: + + if (pRow) + { + pRow->ReleaseRow(); + } + + return( rc); +} + +//------------------------------------------------------------------------------ +// Desc: Parse the data type for a column. +//------------------------------------------------------------------------------ +RCODE SQLStatement::getDataType( + eDataType * peDataType, + FLMUINT * puiMax) +{ + RCODE rc = NE_SFLM_OK; + + // Leading whitespace has already been skipped + + // Valid data types are: + // char(n) + // varchar(n) + // long varchar + // varwchar(n) - unicode + // longwvarchar - unicode + // smallint - 16 bit signed integer + // integer - 32 bit signed integer + // tinyint - 8 bit signed integer + // bigint - 64 bit signed integer + // binary(n) + // varbinary(n) + // long varbinary + + if (lineHasToken( "char") || + lineHasToken( "varchar") || + lineHasToken( "varwchar")) + { + if (RC_BAD( rc = skipWhitespace( FALSE))) + { + goto Exit; + } + if (!lineHasToken( "(")) + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset, + SQL_ERR_EXPECTING_LPAREN, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } + if (RC_BAD( rc = getUINT( FALSE, puiMax))) + { + goto Exit; + } + if (RC_BAD( rc = skipWhitespace( FALSE))) + { + goto Exit; + } + if (!lineHasToken( ")")) + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset, + SQL_ERR_EXPECTING_RPAREN, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } + *peDataType = SFLM_STRING_TYPE; + } + else if (lineHasToken( "longvarchar") || + lineHasToken( "longwvarchar")) + { + *peDataType = SFLM_STRING_TYPE; + *puiMax = 0; + } + else if (lineHasToken( "longvarbinary")) + { + *peDataType = SFLM_BINARY_TYPE; + *puiMax = 0; + } + else if (lineHasToken( "long")) + { + if (RC_BAD( rc = skipWhitespace( TRUE))) + { + goto Exit; + } + if (lineHasToken( "varchar") || + lineHasToken( "varwchar")) + { + *peDataType = SFLM_STRING_TYPE; + *puiMax = 0; + } + else if (lineHasToken( "varbinary")) + { + *peDataType = SFLM_BINARY_TYPE; + *puiMax = 0; + } + else + { + goto Invalid_Data_Type; + } + } + else if (lineHasToken( "smallint") || + lineHasToken( "integer") || + lineHasToken( "tinyint") || + lineHasToken( "bigint")) + { + *peDataType = SFLM_NUMBER_TYPE; + } + else if (lineHasToken( "binary") || + lineHasToken( "varbinary")) + { + if (RC_BAD( rc = skipWhitespace( FALSE))) + { + goto Exit; + } + if (!lineHasToken( "(")) + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset, + SQL_ERR_EXPECTING_LPAREN, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } + if (RC_BAD( rc = getUINT( FALSE, puiMax))) + { + goto Exit; + } + if (RC_BAD( rc = skipWhitespace( FALSE))) + { + goto Exit; + } + if (!lineHasToken( ")")) + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset, + SQL_ERR_EXPECTING_RPAREN, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } + *peDataType = SFLM_BINARY_TYPE; + } + else + { +Invalid_Data_Type: + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset, + SQL_ERR_INVALID_DATA_TYPE, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } + +Exit: + return( rc); +} + +//------------------------------------------------------------------------------ +// Desc: Process the create table statement. The "CREATE TABLE" keywords +// have already been parsed. +//------------------------------------------------------------------------------ +RCODE SQLStatement::processCreateTable( void) +{ + RCODE rc = NE_SFLM_OK; + FLMBOOL bStartedTrans = FALSE; + char szTableName [MAX_SQL_NAME_LEN + 1]; + FLMUINT uiTableNameLen; + char szColumnName [MAX_SQL_NAME_LEN + 1]; + FLMUINT uiColumnNameLen; + char * pszTmp; + F_COLUMN_DEF * pColumnDef; + F_COLUMN_DEF * pFirstColDef; + F_COLUMN_DEF * pLastColDef; + FLMUINT uiNumColumnDefs; + F_TABLE * pTable; + + // 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: CREATE TABLE ( pszColumnName = pszTmp; + pColumnDef->uiColumnNameLen = uiColumnNameLen; + pColumnDef->eColumnDataType = SFLM_UNKNOWN_TYPE; + pColumnDef->uiFlags = COL_NULL_ALLOWED; + pColumnDef->uiEncDefNum = 0; + pColumnDef->uiMaxLen = 0; + pColumnDef->pNext = NULL; + if (pLastColDef) + { + pLastColDef->pNext = pColumnDef; + } + else + { + pFirstColDef = pColumnDef; + } + pLastColDef = pColumnDef; + + // Must be whitespace after the column name. + + if (RC_BAD( rc = skipWhitespace( TRUE))) + { + goto Exit; + } + + // Data type must follow + + if (RC_BAD( rc = getDataType( &pColumnDef->eColumnDataType, + &pColumnDef->uiMaxLen))) + { + goto Exit; + } + + // Skip any white space + + 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; + } + } + + // Create the table + + if (RC_BAD( rc = m_pDb->createTable( 0, szTableName, uiTableNameLen, 0, + pFirstColDef, uiNumColumnDefs))) + { + goto Exit; + } + + // Commit the transaction if we started it + + if (bStartedTrans) + { + bStartedTrans = FALSE; + if (RC_BAD( rc = m_pDb->transCommit())) + { + goto Exit; + } + } + +Exit: + + if (bStartedTrans) + { + m_pDb->transAbort(); + } + + return( rc); +} + diff --git a/sql/src/fdbcopy.cpp b/sql/src/fdbcopy.cpp index ac2ced0..d927c80 100644 --- a/sql/src/fdbcopy.cpp +++ b/sql/src/fdbcopy.cpp @@ -88,7 +88,7 @@ RCODE F_DbSystem::dbCopy( // Open the source database so we can force a checkpoint. - if (RC_BAD( rc = openDb( pszSrcDbName, pszSrcDataDir, pszSrcRflDir, + if (RC_BAD( rc = openDatabase( pszSrcDbName, pszSrcDataDir, pszSrcRflDir, NULL, 0, &pDb))) { goto Exit; diff --git a/sql/src/fdict.cpp b/sql/src/fdict.cpp index cb7994e..4ad98e1 100644 --- a/sql/src/fdict.cpp +++ b/sql/src/fdict.cpp @@ -634,7 +634,8 @@ FSTATIC FLMBOOL validBooleanValue( const char * pszValue, FLMBOOL * pbTrue) { - if (f_stricmp( pszValue, "yes") == 0 || + if (f_stricmp( pszValue, "true") == 0 || + f_stricmp( pszValue, "yes") == 0 || f_stricmp( pszValue, "1") == 0 || f_stricmp( pszValue, "enabled") == 0 || f_stricmp( pszValue, "on") == 0) @@ -642,7 +643,8 @@ FSTATIC FLMBOOL validBooleanValue( *pbTrue = TRUE; return( TRUE); } - else if (f_stricmp( pszValue, "no") == 0 || + else if (f_stricmp( pszValue, "false") == 0 || + f_stricmp( pszValue, "no") == 0 || f_stricmp( pszValue, "0") == 0 || f_stricmp( pszValue, "disabled") == 0 || f_stricmp( pszValue, "off") == 0) @@ -940,6 +942,7 @@ RCODE F_Dict::addColumn( const char * pszColumnName, FLMUINT uiFlags, eDataType eDataTyp, + FLMUINT uiMaxLen, FLMUINT uiEncDefNum) { RCODE rc = NE_SFLM_OK; @@ -990,6 +993,7 @@ RCODE F_Dict::addColumn( pColumn->ui64DefRowId = ui64DefRowId; pColumn->uiFlags = uiFlags; pColumn->eDataTyp = eDataTyp; + pColumn->uiMaxLen = uiMaxLen; pColumn->uiEncDefNum = uiEncDefNum; pColumn->pFirstIcd = NULL; pColumn->pFirstDataIcd = NULL; @@ -1103,7 +1107,11 @@ RCODE F_Dict::addIndex( // Set other members of the index structure. pIndex->uiTableNum = uiTableNum; - pIndex->uiFlags = uiFlags; + + // NOTE: The substring flag should only be set when components are added + // We AND it off here in case it was set by the caller. + + pIndex->uiFlags = uiFlags & (~(IXD_HAS_SUBSTRING)); pIndex->uiLanguage = uiLanguage; pIndex->ui64LastRowIndexed = ui64LastRowIndexed; f_memset( &pIndex->lfInfo, 0, sizeof( LFILE)); @@ -1194,6 +1202,10 @@ RCODE F_Dict::addIndexComponent( pIcd->uiColumnNum = uiColumnNum; pIcd->ui64DefRowId = ui64DefRowId; pIcd->uiFlags = uiFlags; + if (uiFlags & ICD_SUBSTRING) + { + pIndex->uiFlags |= IXD_HAS_SUBSTRING; + } pIcd->uiCompareRules = uiCompareRules; pIcd->uiLimit = uiLimit; pIcd->pNextInChain = pColumn->pFirstIcd; @@ -1257,35 +1269,35 @@ RCODE F_Dict::setupEncDefTable( void) if (RC_BAD( rc = addColumn( SFLM_TBLNUM_ENCDEFS, 0, SFLM_COLNUM_ENCDEFS_ENCDEF_NAME, SFLM_COLNAM_ENCDEFS_ENCDEF_NAME, - 0, SFLM_STRING_TYPE, 0))) + 0, SFLM_STRING_TYPE, 0, 0))) { goto Exit; } if (RC_BAD( rc = addColumn( SFLM_TBLNUM_ENCDEFS, 0, SFLM_COLNUM_ENCDEFS_ENCDEF_NUM, SFLM_COLNAM_ENCDEFS_ENCDEF_NUM, - COL_READ_ONLY, SFLM_NUMBER_TYPE, 0))) + COL_READ_ONLY, SFLM_NUMBER_TYPE, 0, 0))) { goto Exit; } if (RC_BAD( rc = addColumn( SFLM_TBLNUM_ENCDEFS, 0, SFLM_COLNUM_ENCDEFS_ENC_ALGORITHM, SFLM_COLNAM_ENCDEFS_ENC_ALGORITHM, - COL_READ_ONLY, SFLM_STRING_TYPE, 0))) + COL_READ_ONLY, SFLM_STRING_TYPE, 0, 0))) { goto Exit; } if (RC_BAD( rc = addColumn( SFLM_TBLNUM_ENCDEFS, 0, SFLM_COLNUM_ENCDEFS_ENC_KEY_SIZE, SFLM_COLNAM_ENCDEFS_ENC_KEY_SIZE, - COL_READ_ONLY, SFLM_NUMBER_TYPE, 0))) + COL_READ_ONLY, SFLM_NUMBER_TYPE, 0, 0))) { goto Exit; } if (RC_BAD( rc = addColumn( SFLM_TBLNUM_ENCDEFS, 0, SFLM_COLNUM_ENCDEFS_ENC_KEY, SFLM_COLNAM_ENCDEFS_ENC_KEY, - COL_READ_ONLY, SFLM_BINARY_TYPE, 0))) + COL_READ_ONLY, SFLM_BINARY_TYPE, 0, 0))) { goto Exit; } @@ -1333,28 +1345,28 @@ RCODE F_Dict::setupTableTable( void) if (RC_BAD( rc = addColumn( SFLM_TBLNUM_TABLES, 0, SFLM_COLNUM_TABLES_TABLE_NAME, SFLM_COLNAM_TABLES_TABLE_NAME, - 0, SFLM_STRING_TYPE, 0))) + 0, SFLM_STRING_TYPE, 0, 0))) { goto Exit; } if (RC_BAD( rc = addColumn( SFLM_TBLNUM_TABLES, 0, SFLM_COLNUM_TABLES_TABLE_NUM, SFLM_COLNAM_TABLES_TABLE_NUM, - COL_READ_ONLY, SFLM_NUMBER_TYPE, 0))) + COL_READ_ONLY, SFLM_NUMBER_TYPE, 0, 0))) { goto Exit; } if (RC_BAD( rc = addColumn( SFLM_TBLNUM_TABLES, 0, SFLM_COLNUM_TABLES_ENCDEF_NUM, SFLM_COLNAM_TABLES_ENCDEF_NUM, - 0, SFLM_NUMBER_TYPE, 0))) + 0, SFLM_NUMBER_TYPE, 0, 0))) { goto Exit; } if (RC_BAD( rc = addColumn( SFLM_TBLNUM_TABLES, 0, SFLM_COLNUM_TABLES_NUM_COLUMNS, SFLM_COLNAM_TABLES_NUM_COLUMNS, - 0, SFLM_NUMBER_TYPE, 0))) + 0, SFLM_NUMBER_TYPE, 0, 0))) { goto Exit; } @@ -1419,49 +1431,57 @@ RCODE F_Dict::setupColumnTable( void) if (RC_BAD( rc = addColumn( SFLM_TBLNUM_COLUMNS, 0, SFLM_COLNUM_COLUMNS_TABLE_NUM, SFLM_COLNAM_COLUMNS_TABLE_NUM, - COL_READ_ONLY, SFLM_NUMBER_TYPE, 0))) + COL_READ_ONLY, SFLM_NUMBER_TYPE, 0, 0))) { goto Exit; } if (RC_BAD( rc = addColumn( SFLM_TBLNUM_COLUMNS, 0, SFLM_COLNUM_COLUMNS_COLUMN_NAME, SFLM_COLNAM_COLUMNS_COLUMN_NAME, - 0, SFLM_STRING_TYPE, 0))) + 0, SFLM_STRING_TYPE, 0, 0))) { goto Exit; } if (RC_BAD( rc = addColumn( SFLM_TBLNUM_COLUMNS, 0, SFLM_COLNUM_COLUMNS_COLUMN_NUM, SFLM_COLNAM_COLUMNS_COLUMN_NUM, - COL_READ_ONLY, SFLM_NUMBER_TYPE, 0))) + COL_READ_ONLY, SFLM_NUMBER_TYPE, 0, 0))) { goto Exit; } if (RC_BAD( rc = addColumn( SFLM_TBLNUM_COLUMNS, 0, SFLM_COLNUM_COLUMNS_DATA_TYPE, SFLM_COLNAM_COLUMNS_DATA_TYPE, - COL_READ_ONLY, SFLM_STRING_TYPE, 0))) + COL_READ_ONLY, SFLM_STRING_TYPE, 0, 0))) + { + goto Exit; + } + if (RC_BAD( rc = addColumn( SFLM_TBLNUM_COLUMNS, 0, + SFLM_COLNUM_COLUMNS_MAX_LEN, + SFLM_COLNAM_COLUMNS_MAX_LEN, + COL_READ_ONLY, SFLM_NUMBER_TYPE, 0, 0))) { goto Exit; } if (RC_BAD( rc = addColumn( SFLM_TBLNUM_COLUMNS, 0, SFLM_COLNUM_COLUMNS_ENCDEF_NUM, SFLM_COLNAM_COLUMNS_ENCDEF_NUM, - COL_READ_ONLY | COL_NULL_ALLOWED, SFLM_NUMBER_TYPE, 0))) + COL_READ_ONLY | COL_NULL_ALLOWED, + SFLM_NUMBER_TYPE, 0, 0))) { goto Exit; } if (RC_BAD( rc = addColumn( SFLM_TBLNUM_COLUMNS, 0, SFLM_COLNUM_COLUMNS_READ_ONLY, SFLM_COLNAM_COLUMNS_READ_ONLY, - COL_NULL_ALLOWED, SFLM_STRING_TYPE, 0))) + COL_NULL_ALLOWED, SFLM_STRING_TYPE, 0, 0))) { goto Exit; } if (RC_BAD( rc = addColumn( SFLM_TBLNUM_COLUMNS, 0, SFLM_COLNUM_COLUMNS_NULL_ALLOWED, SFLM_COLNAM_COLUMNS_NULL_ALLOWED, - COL_NULL_ALLOWED, SFLM_STRING_TYPE, 0))) + COL_NULL_ALLOWED, SFLM_STRING_TYPE, 0, 0))) { goto Exit; } @@ -1526,63 +1546,78 @@ RCODE F_Dict::setupIndexTable( void) if (RC_BAD( rc = addColumn( SFLM_TBLNUM_INDEXES, 0, SFLM_COLNUM_INDEXES_INDEX_NAME, SFLM_COLNAM_INDEXES_INDEX_NAME, - 0, SFLM_STRING_TYPE, 0))) + 0, SFLM_STRING_TYPE, 0, 0))) { goto Exit; } if (RC_BAD( rc = addColumn( SFLM_TBLNUM_INDEXES, 0, SFLM_COLNUM_INDEXES_INDEX_NUM, SFLM_COLNAM_INDEXES_INDEX_NUM, - COL_READ_ONLY, SFLM_NUMBER_TYPE, 0))) + COL_READ_ONLY, SFLM_NUMBER_TYPE, 0, 0))) { goto Exit; } if (RC_BAD( rc = addColumn( SFLM_TBLNUM_INDEXES, 0, SFLM_COLNUM_INDEXES_TABLE_NUM, SFLM_COLNAM_INDEXES_TABLE_NUM, - COL_READ_ONLY, SFLM_NUMBER_TYPE, 0))) + COL_READ_ONLY, SFLM_NUMBER_TYPE, 0, 0))) { goto Exit; } if (RC_BAD( rc = addColumn( SFLM_TBLNUM_INDEXES, 0, SFLM_COLNUM_INDEXES_ENCDEF_NUM, SFLM_COLNAM_INDEXES_ENCDEF_NUM, - COL_READ_ONLY | COL_NULL_ALLOWED, SFLM_NUMBER_TYPE, 0))) + COL_READ_ONLY | COL_NULL_ALLOWED, + SFLM_NUMBER_TYPE, 0, 0))) { goto Exit; } if (RC_BAD( rc = addColumn( SFLM_TBLNUM_INDEXES, 0, SFLM_COLNUM_INDEXES_LANGUAGE, SFLM_COLNAM_INDEXES_LANGUAGE, - COL_NULL_ALLOWED, SFLM_STRING_TYPE, 0))) + COL_NULL_ALLOWED, SFLM_STRING_TYPE, 0, 0))) { goto Exit; } if (RC_BAD( rc = addColumn( SFLM_TBLNUM_INDEXES, 0, SFLM_COLNUM_INDEXES_NUM_KEY_COMPONENTS, SFLM_COLNAM_INDEXES_NUM_KEY_COMPONENTS, - COL_NULL_ALLOWED, SFLM_NUMBER_TYPE, 0))) + COL_NULL_ALLOWED, SFLM_NUMBER_TYPE, 0, 0))) { goto Exit; } if (RC_BAD( rc = addColumn( SFLM_TBLNUM_INDEXES, 0, SFLM_COLNUM_INDEXES_NUM_DATA_COMPONENTS, SFLM_COLNAM_INDEXES_NUM_DATA_COMPONENTS, - COL_NULL_ALLOWED, SFLM_NUMBER_TYPE, 0))) + COL_NULL_ALLOWED, SFLM_NUMBER_TYPE, 0, 0))) { goto Exit; } if (RC_BAD( rc = addColumn( SFLM_TBLNUM_INDEXES, 0, SFLM_COLNUM_INDEXES_LAST_ROW_INDEXED, SFLM_COLNAM_INDEXES_LAST_ROW_INDEXED, - COL_NULL_ALLOWED, SFLM_NUMBER_TYPE, 0))) + COL_NULL_ALLOWED, SFLM_NUMBER_TYPE, 0, 0))) { goto Exit; } if (RC_BAD( rc = addColumn( SFLM_TBLNUM_INDEXES, 0, SFLM_COLNUM_INDEXES_INDEX_STATE, SFLM_COLNAM_INDEXES_INDEX_STATE, - COL_NULL_ALLOWED, SFLM_STRING_TYPE, 0))) + COL_NULL_ALLOWED, SFLM_STRING_TYPE, 0, 0))) + { + goto Exit; + } + if (RC_BAD( rc = addColumn( SFLM_TBLNUM_INDEXES, 0, + SFLM_COLNUM_INDEXES_KEEP_ABS_POS_INFO, + SFLM_COLNAM_INDEXES_KEEP_ABS_POS_INFO, + COL_NULL_ALLOWED, SFLM_STRING_TYPE, 0, 0))) + { + goto Exit; + } + if (RC_BAD( rc = addColumn( SFLM_TBLNUM_INDEXES, 0, + SFLM_COLNUM_INDEXES_KEYS_UNIQUE, + SFLM_COLNAM_INDEXES_KEYS_UNIQUE, + COL_NULL_ALLOWED, SFLM_STRING_TYPE, 0, 0))) { goto Exit; } @@ -1664,63 +1699,63 @@ RCODE F_Dict::setupIndexComponentTable( void) if (RC_BAD( rc = addColumn( SFLM_TBLNUM_INDEX_COMPONENTS, 0, SFLM_COLNUM_INDEX_COMP_INDEX_NUM, SFLM_COLNAM_INDEX_COMP_INDEX_NUM, - COL_READ_ONLY, SFLM_NUMBER_TYPE, 0))) + COL_READ_ONLY, SFLM_NUMBER_TYPE, 0, 0))) { goto Exit; } if (RC_BAD( rc = addColumn( SFLM_TBLNUM_INDEX_COMPONENTS, 0, SFLM_COLNUM_INDEX_COMP_COLUMN_NUM, SFLM_COLNAM_INDEX_COMP_COLUMN_NUM, - COL_READ_ONLY, SFLM_NUMBER_TYPE, 0))) + COL_READ_ONLY, SFLM_NUMBER_TYPE, 0, 0))) { goto Exit; } if (RC_BAD( rc = addColumn( SFLM_TBLNUM_INDEX_COMPONENTS, 0, SFLM_COLNUM_INDEX_COMP_KEY_COMPONENT, SFLM_COLNAM_INDEX_COMP_KEY_COMPONENT, - COL_NULL_ALLOWED, SFLM_NUMBER_TYPE, 0))) + COL_NULL_ALLOWED, SFLM_NUMBER_TYPE, 0, 0))) { goto Exit; } if (RC_BAD( rc = addColumn( SFLM_TBLNUM_INDEX_COMPONENTS, 0, SFLM_COLNUM_INDEX_COMP_DATA_COMPONENT, SFLM_COLNAM_INDEX_COMP_DATA_COMPONENT, - COL_NULL_ALLOWED, SFLM_NUMBER_TYPE, 0))) + COL_NULL_ALLOWED, SFLM_NUMBER_TYPE, 0, 0))) { goto Exit; } if (RC_BAD( rc = addColumn( SFLM_TBLNUM_INDEX_COMPONENTS, 0, SFLM_COLNUM_INDEX_COMP_INDEX_ON, SFLM_COLNAM_INDEX_COMP_INDEX_ON, - COL_NULL_ALLOWED, SFLM_STRING_TYPE, 0))) + COL_NULL_ALLOWED, SFLM_STRING_TYPE, 0, 0))) { goto Exit; } if (RC_BAD( rc = addColumn( SFLM_TBLNUM_INDEX_COMPONENTS, 0, SFLM_COLNUM_INDEX_COMP_COMPARE_RULES, SFLM_COLNAM_INDEX_COMP_COMPARE_RULES, - COL_NULL_ALLOWED, SFLM_STRING_TYPE, 0))) + COL_NULL_ALLOWED, SFLM_STRING_TYPE, 0, 0))) { goto Exit; } if (RC_BAD( rc = addColumn( SFLM_TBLNUM_INDEX_COMPONENTS, 0, SFLM_COLNUM_INDEX_COMP_SORT_DESCENDING, SFLM_COLNAM_INDEX_COMP_SORT_DESCENDING, - COL_NULL_ALLOWED, SFLM_STRING_TYPE, 0))) + COL_NULL_ALLOWED, SFLM_STRING_TYPE, 0, 0))) { goto Exit; } if (RC_BAD( rc = addColumn( SFLM_TBLNUM_INDEX_COMPONENTS, 0, SFLM_COLNUM_INDEX_COMP_SORT_MISSING_HIGH, SFLM_COLNAM_INDEX_COMP_SORT_MISSING_HIGH, - COL_NULL_ALLOWED, SFLM_STRING_TYPE, 0))) + COL_NULL_ALLOWED, SFLM_STRING_TYPE, 0, 0))) { goto Exit; } if (RC_BAD( rc = addColumn( SFLM_TBLNUM_INDEX_COMPONENTS, 0, SFLM_COLNUM_INDEX_COMP_LIMIT, SFLM_COLNAM_INDEX_COMP_LIMIT, - COL_NULL_ALLOWED, SFLM_NUMBER_TYPE, 0))) + COL_NULL_ALLOWED, SFLM_NUMBER_TYPE, 0, 0))) { goto Exit; } @@ -1768,7 +1803,7 @@ RCODE F_Dict::setupBlockChainTable( void) if (RC_BAD( rc = addColumn( SFLM_TBLNUM_BLOCK_CHAINS, 0, SFLM_COLNUM_BLOCK_CHAINS_BLOCK_ADDRESS, SFLM_COLNAM_BLOCK_CHAINS_BLOCK_ADDRESS, - 0, SFLM_NUMBER_TYPE, 0))) + 0, SFLM_NUMBER_TYPE, 0, 0))) { goto Exit; } @@ -2386,6 +2421,7 @@ RCODE F_Db::dictReadColumns( void) FLMUINT uiTmpLen; FLMBOOL bTmp; FLMBOOL bIsNull; + FLMUINT uiMaxLen; if ((pTableCursor = f_new FSTableCursor) == NULL) { @@ -2487,6 +2523,18 @@ RCODE F_Db::dictReadColumns( void) uiFlags = 0; + // Get the maximum length - optional + + if (RC_BAD( rc = pRow->getUINT( this, SFLM_COLNUM_COLUMNS_MAX_LEN, + &uiMaxLen, &bIsNull))) + { + goto Exit; + } + if (bIsNull) + { + uiMaxLen = 0; + } + // Get the read-only flag - optional. if (RC_BAD( rc = pRow->getUTF8( this, SFLM_COLNUM_COLUMNS_READ_ONLY, @@ -2533,7 +2581,7 @@ RCODE F_Db::dictReadColumns( void) if (RC_BAD( rc = m_pDict->addColumn( uiTableNum, ui64DefRowId, uiColumnNum, szColumnName, uiFlags, - eDataTyp, uiEncDefNum))) + eDataTyp, uiMaxLen, uiEncDefNum))) { goto Exit; } @@ -2598,6 +2646,7 @@ RCODE F_Db::dictReadIndexes( void) FLMUINT uiIndexNameLen; FLMUINT uiIndexNum; char szTmp [50]; + FLMBOOL bTmp; FLMUINT uiTmpLen; FLMUINT uiLanguage; FLMUINT64 ui64LastRowIndexed; @@ -2767,6 +2816,46 @@ RCODE F_Db::dictReadIndexes( void) } } + // Get the keep absolute positioning flag - optional. + + if (RC_BAD( rc = pRow->getUTF8( this, SFLM_COLNUM_INDEXES_KEEP_ABS_POS_INFO, + szTmp, sizeof( szTmp), &bIsNull, &uiTmpLen, NULL))) + { + goto Exit; + } + if (!bIsNull && uiTmpLen) + { + if (!validBooleanValue( szTmp, &bTmp)) + { + rc = RC_SET_AND_ASSERT( NE_SFLM_INVALID_KEEP_ABS_POS_INFO_VALUE); + goto Exit; + } + if (bTmp) + { + uiFlags |= IXD_ABS_POS; + } + } + + // Get the keys unique flag - optional. + + if (RC_BAD( rc = pRow->getUTF8( this, SFLM_COLNUM_INDEXES_KEYS_UNIQUE, + szTmp, sizeof( szTmp), &bIsNull, &uiTmpLen, NULL))) + { + goto Exit; + } + if (!bIsNull && uiTmpLen) + { + if (!validBooleanValue( szTmp, &bTmp)) + { + rc = RC_SET_AND_ASSERT( NE_SFLM_INVALID_KEYS_UNIQUE_VALUE); + goto Exit; + } + if (bTmp) + { + uiFlags |= IXD_KEYS_UNIQUE; + } + } + // Add the index to the the in-memory dictionary. if (RC_BAD( rc = m_pDict->addIndex( uiIndexNum, ui64DefRowId, @@ -2879,7 +2968,7 @@ FSTATIC FLMBOOL validCompareRulesValue( { (*puiCompareRules) |= FLM_COMP_CASE_INSENSITIVE; } - else if (f_stricmp( SFLM_MINSPACES_OPTION_STR, pszRuleStart) == 0) + else if (f_stricmp( SFLM_COMPRESS_WHITESPACE_OPTION_STR, pszRuleStart) == 0) { (*puiCompareRules) |= FLM_COMP_COMPRESS_WHITESPACE; } @@ -2899,7 +2988,7 @@ FSTATIC FLMBOOL validCompareRulesValue( { (*puiCompareRules) |= FLM_COMP_NO_UNDERSCORES; } - else if (f_stricmp( SFLM_NOSPACE_OPTION_STR, pszRuleStart) == 0) + else if (f_stricmp( SFLM_NO_WHITESPACE_OPTION_STR, pszRuleStart) == 0) { (*puiCompareRules) |= FLM_COMP_NO_WHITESPACE; } diff --git a/sql/src/fdict.h b/sql/src/fdict.h index 3e933d5..0dcbc66 100644 --- a/sql/src/fdict.h +++ b/sql/src/fdict.h @@ -62,6 +62,7 @@ typedef struct F_COLUMN #define COL_READ_ONLY 0x0001 #define COL_NULL_ALLOWED 0x0002 eDataType eDataTyp; // Column data type + FLMUINT uiMaxLen; // Maximum length (for strings or binary) FLMUINT uiEncDefNum; // If column is encrypted, this is the // encryption definition number. Zero if // column is not encrypted. @@ -134,6 +135,7 @@ typedef struct F_INDEX // not be suspended. #define IXD_SUSPENDED 0x00008 // IXD_OFFLINE should also be set #define IXD_SYSTEM 0x00010 // Index is an internal system index + #define IXD_KEYS_UNIQUE 0x00020 // Index keys must be unique FLMUINT uiLanguage; // Language for the index. LFILE lfInfo; // Logical file information. FLMUINT64 ui64LastRowIndexed; // Last row indexed, if indexing in @@ -149,18 +151,6 @@ typedef struct ICD FLMUINT uiColumnNum; // Column in table this component refers to FLMUINT64 ui64DefRowId; // Definition row ID. FLMUINT uiFlags; // Flags for component -#define ICD_VALUE 0x00000010 // Value agrees with parsing syntax -#define ICD_EACHWORD 0x00000020 // Index each and every word in the field -#define ICD_PRESENCE 0x00000040 // Index the tag and NOT the value -#define ICD_METAPHONE 0x00000080 // Index words of text strings using - // metaphone values -#define ICD_SUBSTRING 0x00000100 // Index all substrings pieces -#define ICD_DESCENDING 0x00000200 // Sort in descending order. -#define ICD_MISSING_HIGH 0x00000400 // Sort missing components high instead of low. -#define ICD_ESC_CHAR 0x00001000 // Placehold so that a query can parse the input - // string and find a literal '*' or '\\'. - // Not specified in dictionary def. or kept in ICD. - // Only a temporary flag. FLMUINT uiCompareRules; // Comparison rules for this component. FLMUINT uiLimit; // Limit for this component. #define ICD_DEFAULT_LIMIT 128 @@ -437,6 +427,7 @@ public: const char * pszColumnName, FLMUINT uiFlags, eDataType eDataTyp, + FLMUINT uiMaxLen, FLMUINT uiEncDefNum); RCODE addIndex( diff --git a/sql/src/flaimsql.h b/sql/src/flaimsql.h index cf622de..c496308 100644 --- a/sql/src/flaimsql.h +++ b/sql/src/flaimsql.h @@ -98,6 +98,25 @@ typedef enum SQL_ERR_ILLEGAL_HEX_DIGIT, ///< 18 = Illegal hex digit in number value. SQL_ERR_NUMBER_OVERFLOW, ///< 19 = Number exceeds limits for 64 bit numbers. SQL_ERR_BINARY_VALUE_EMPTY, ///< 20 = Binary value is empty. + SQL_ERR_INVALID_CREATE_OPTION, ///< 21 = Create option is invalid. + SQL_ERR_UTF8_STRING_TOO_LARGE, ///< 22 = UTF8 string value is too large for buffer. + SQL_ERR_INVALID_DB_CREATE_PARAM, ///< 23 = Parameter for CREATE_OPTIONS in CREATE DATABASE statement is invalid. + SQL_ERR_INVALID_BLOCK_SIZE, ///< 24 = Invalid block size specified. + SQL_ERR_INVALID_LANGUAGE, ///< 25 = Invalid language specified. + SQL_ERR_EXPECTING_EQUAL, ///< 26 = Expecting equal (=). + SQL_ERR_INVALID_NEGATIVE_NUM, ///< 27 = Negative numbers not allowed. + SQL_ERR_EXPECTING_BOOLEAN, ///< 28 = Expecting a boolean TRUE or FALSE. + SQL_ERR_INVALID_OPEN_OPTION, ///< 29 = Open option is invalid. + SQL_ERR_INVALID_DATA_TYPE, ///< 30 = Invalid data type specified. + SQL_ERR_STRING_TOO_LONG, ///< 31 = String specified for column value is too long. + SQL_ERR_BINARY_TOO_LONG, ///< 32 = Binary data specified for column value is too long. + SQL_ERR_EXPECTING_INDEX, ///< 33 = Expecting "INDEX" keyword. + SQL_ERR_EXPECTING_ON, ///< 34 = Expecting "ON" keyword. + SQL_ERR_UNDEFINED_INDEX, ///< 35 = Index name that was specified is not defined. + SQL_ERR_INDEX_ALREADY_DEFINED, ///< 36 = Index name is already defined in the database. + SQL_ERR_INVALID_INDEX_OPTION, ///< 37 = Invalid index column option specified. + SQL_ERR_INVALID_COL_INDEX_OPTION, ///< 38 = Index option cannot be used for the specified column. + SQL_ERR_MULTIPLE_INDEX_OPTIONS, ///< 39 = Cannot specify more than one of value, eachword, presence, substring, or metaphone indexing options. // IMPORTANT NOTE: If new codes are added, please update gv_SQLParseErrors in fshell.cpp SQL_NUM_ERRORS @@ -256,7 +275,7 @@ typedef struct SFLM_DB_HDR #define SFLM_DB_HDR_ucDbKey 256 } SFLM_DB_HDR; -// Flags used by openDb method +// Flags used by openDatabase method #define SFLM_DONT_REDO_LOG 0x0001 #define SFLM_DONT_RESUME_THREADS 0x0002 @@ -401,13 +420,15 @@ typedef enum #define SFLM_COLNAM_COLUMNS_COLUMN_NUM "column_num" #define SFLM_COLNUM_COLUMNS_DATA_TYPE 4 #define SFLM_COLNAM_COLUMNS_DATA_TYPE "data_type" -#define SFLM_COLNUM_COLUMNS_ENCDEF_NUM 5 +#define SFLM_COLNUM_COLUMNS_MAX_LEN 5 +#define SFLM_COLNAM_COLUMNS_MAX_LEN "max_len" +#define SFLM_COLNUM_COLUMNS_ENCDEF_NUM 6 #define SFLM_COLNAM_COLUMNS_ENCDEF_NUM "encdef_num" -#define SFLM_COLNUM_COLUMNS_READ_ONLY 6 +#define SFLM_COLNUM_COLUMNS_READ_ONLY 7 #define SFLM_COLNAM_COLUMNS_READ_ONLY "read_only" -#define SFLM_COLNUM_COLUMNS_NULL_ALLOWED 7 +#define SFLM_COLNUM_COLUMNS_NULL_ALLOWED 8 #define SFLM_COLNAM_COLUMNS_NULL_ALLOWED "null_allowed" -#define SFLM_COLUMNS_NUM_COLUMNS 7 +#define SFLM_COLUMNS_NUM_COLUMNS 8 // Column names and numbers for the indexes table @@ -429,7 +450,11 @@ typedef enum #define SFLM_COLNAM_INDEXES_LAST_ROW_INDEXED "last_row_indexed" #define SFLM_COLNUM_INDEXES_INDEX_STATE 9 #define SFLM_COLNAM_INDEXES_INDEX_STATE "index_state" -#define SFLM_INDEXES_NUM_COLUMNS 9 +#define SFLM_COLNUM_INDEXES_KEEP_ABS_POS_INFO 10 +#define SFLM_COLNAM_INDEXES_KEEP_ABS_POS_INFO "keep_abs_pos_info" +#define SFLM_COLNUM_INDEXES_KEYS_UNIQUE 11 +#define SFLM_COLNAM_INDEXES_KEYS_UNIQUE "keys_unique" +#define SFLM_INDEXES_NUM_COLUMNS 11 // Column names and numbers for the index components table @@ -459,29 +484,62 @@ typedef enum #define SFLM_COLNAM_BLOCK_CHAINS_BLOCK_ADDRESS "block_address" #define SFLM_BLOCK_CHAINS_NUM_COLUMNS 1 -#define SFLM_STRING_OPTION_STR "string" -#define SFLM_INTEGER_OPTION_STR "integer" -#define SFLM_BINARY_OPTION_STR "binary" -#define SFLM_CHECKING_OPTION_STR "checking" -#define SFLM_PURGE_OPTION_STR "purge" -#define SFLM_ACTIVE_OPTION_STR "active" -#define SFLM_INDEX_SUSPENDED_STR "suspended" -#define SFLM_INDEX_OFFLINE_STR "offline" -#define SFLM_INDEX_ONLINE_STR "online" -#define SFLM_CASE_INSENSITIVE_OPTION_STR "caseinsensitive" -#define SFLM_MINSPACES_OPTION_STR "minspaces" -#define SFLM_WHITESPACE_AS_SPACE_STR "whitespaceasspace" -#define SFLM_IGNORE_LEADINGSPACES_OPTION_STR "ignoreleadingspaces" -#define SFLM_IGNORE_TRAILINGSPACES_OPTION_STR "ignoretrailingspaces" -#define SFLM_NOUNDERSCORE_OPTION_STR "nounderscores" -#define SFLM_NOSPACE_OPTION_STR "nospaces" -#define SFLM_NODASH_OPTION_STR "nodashes" -#define SFLM_VALUE_OPTION_STR "value" -#define SFLM_PRESENCE_OPTION_STR "presence" -#define SFLM_SUBSTRING_OPTION_STR "substring" -#define SFLM_EACHWORD_OPTION_STR "eachword" -#define SFLM_ABS_POS_OPTION_STR "abspos" -#define SFLM_METAPHONE_OPTION_STR "metaphone" +#define SFLM_STRING_OPTION_STR "string" +#define SFLM_STRING_OPTION_STR_LEN 6 +#define SFLM_INTEGER_OPTION_STR "integer" +#define SFLM_INTEGER_OPTION_STR_LEN 7 +#define SFLM_BINARY_OPTION_STR "binary" +#define SFLM_BINARY_OPTION_STR_LEN 6 +#define SFLM_CHECKING_OPTION_STR "checking" +#define SFLM_CHECKING_OPTION_STR_LEN 8 +#define SFLM_PURGE_OPTION_STR "purge" +#define SFLM_PURGE_OPTION_STR_LEN 5 +#define SFLM_ACTIVE_OPTION_STR "active" +#define SFLM_ACTIVE_OPTION_STR_LEN 6 +#define SFLM_INDEX_SUSPENDED_STR "suspended" +#define SFLM_INDEX_SUSPENDED_STR_LEN 9 +#define SFLM_INDEX_OFFLINE_STR "offline" +#define SFLM_INDEX_OFFLINE_STR_LEN 7 +#define SFLM_INDEX_ONLINE_STR "online" +#define SFLM_INDEX_ONLINE_STR_LEN 6 +#define SFLM_CASE_INSENSITIVE_OPTION_STR "caseinsensitive" +#define SFLM_CASE_INSENSITIVE_OPTION_STR_LEN 15 +#define SFLM_COMPRESS_WHITESPACE_OPTION_STR "compresswhitespace" +#define SFLM_COMPRESS_WHITESPACE_OPTION_STR_LEN 18 +#define SFLM_WHITESPACE_AS_SPACE_STR "whitespaceasspace" +#define SFLM_WHITESPACE_AS_SPACE_STR_LEN 17 +#define SFLM_IGNORE_LEADINGSPACES_OPTION_STR "ignoreleadingspaces" +#define SFLM_IGNORE_LEADINGSPACES_OPTION_STR_LEN 19 +#define SFLM_IGNORE_TRAILINGSPACES_OPTION_STR "ignoretrailingspaces" +#define SFLM_IGNORE_TRAILINGSPACES_OPTION_STR_LEN 20 +#define SFLM_NOUNDERSCORE_OPTION_STR "nounderscores" +#define SFLM_NOUNDERSCORE_OPTION_STR_LEN 13 +#define SFLM_NO_WHITESPACE_OPTION_STR "nowhitespace" +#define SFLM_NO_WHITESPACE_OPTION_STR_LEN 12 +#define SFLM_NODASH_OPTION_STR "nodashes" +#define SFLM_NODASH_OPTION_STR_LEN 8 +#define SFLM_VALUE_OPTION_STR "value" +#define SFLM_VALUE_OPTION_STR_LEN 5 +#define SFLM_PRESENCE_OPTION_STR "presence" +#define SFLM_PRESENCE_OPTION_STR_LEN 8 +#define SFLM_SUBSTRING_OPTION_STR "substring" +#define SFLM_SUBSTRING_OPTION_STR_LEN 9 +#define SFLM_EACHWORD_OPTION_STR "eachword" +#define SFLM_EACHWORD_OPTION_STR_LEN 8 +#define SFLM_ABS_POS_OPTION_STR "abspos" +#define SFLM_ABS_POS_OPTION_STR_LEN 6 +#define SFLM_METAPHONE_OPTION_STR "metaphone" +#define SFLM_METAPHONE_OPTION_STR_LEN 9 +#define SFLM_DESCENDING_OPTION_STR "descending" +#define SFLM_DESCENDING_OPTION_STR_LEN 10 +#define SFLM_ASCENDING_OPTION_STR "ascending" +#define SFLM_ASCENDING_OPTION_STR_LEN 9 +#define SFLM_SORT_MISSING_HIGH_OPTION_STR "sortmissinghigh" +#define SFLM_SORT_MISSING_HIGH_OPTION_STR_LEN 15 +#define SFLM_SORT_MISSING_LOW_OPTION_STR "sortmissinglow" +#define SFLM_SORT_MISSING_LOW_OPTION_STR_LEN 14 +#define SFLM_LIMIT_OPTION_STR "limit" +#define SFLM_LIMIT_OPTION_STR_LEN 5 // Encryption Schemes @@ -1195,6 +1253,8 @@ typedef struct #define NE_SFLM_ROW_DELETED 0xE05A ///< 0xE05A = Row being accessed has been deleted. #define NE_SFLM_ROW_NOT_FOUND 0xE05B ///< 0xE05B = Row being retrieved does not exist. #define NE_SFLM_INVALID_SQL 0xE05C ///< 0xE05C = SQL statement is invalid. +#define NE_SFLM_STRING_TOO_LONG 0xE05D ///< 0xE05D = String specified for column exceeds maximum length for column. +#define NE_SFLM_BINARY_TOO_LONG 0xE05E ///< 0xE05E = Binary value specified for column exceeds maximum length for column. // Dictionary definition errors. @@ -1240,17 +1300,19 @@ typedef struct #define NE_SFLM_INVALID_COMPARE_RULE 0xE124 ///< 0xE124 = Invalid comparison rule in index definition.\ Dictionary is corrupt. #define NE_SFLM_INVALID_INDEX_OPTION 0xE125 ///< 0xE125 = Invalid index option specified on index definition. #define NE_SFLM_INVALID_INDEX_STATE 0xE126 ///< 0xE126 = Invalid index state specified for an index. -#define NE_SFLM_DUPLICATE_KEY_COMPONENT 0xE127 ///< 0xE127 = Duplicate key component in internal index definition.\ Data dictionary is corrupt. -#define NE_SFLM_INVALID_KEY_COMPONENT 0xE128 ///< 0xE128 = Invalid key component in internal index definition.\ Data dictionary is corrupt. -#define NE_SFLM_UNDEFINED_KEY_COMPONENT 0xE129 ///< 0xE129 = Undefined key component in internal index definition.\ Data dictionary is corrupt. -#define NE_SFLM_DUPLICATE_DATA_COMPONENT 0xE12A ///< 0xE12A = Duplicate data component in internal index definition.\ Data dictionary is corrupt. -#define NE_SFLM_INVALID_DATA_COMPONENT 0xE12B ///< 0xE12B = Invalid data component in internal index definition.\ Data dictionary is corrupt. -#define NE_SFLM_UNDEFINED_DATA_COMPONENT 0xE12C ///< 0xE12C = Undefined data component in internal index definition.\ Data dictionary is corrupt. -#define NE_SFLM_NO_COMP_NUM_FOR_INDEX_COLUMN 0xE12D ///< 0xE12D = Neither key component or data component specified for index column.\ Data dictionary is corrupt. -#define NE_SFLM_INVALID_INDEX_ON_VALUE 0xE12E ///< 0xE12E = Invalid value for the "index on" option in an index definition. -#define NE_SFLM_INVALID_COMPARE_RULES_VALUE 0xE12F ///< 0xE12F = Invalid value for the "compare rules" option in an index definition. -#define NE_SFLM_INVALID_SORT_DESCENDING_VALUE 0xE130 ///< 0xE130 = Invalid value for the "sort descending" option in an index definition. -#define NE_SFLM_INVALID_SORT_MISSING_HIGH_VALUE 0xE131 ///< 0xE131 = Invalid value for the "sort missing high" option in an index definition. +#define NE_SFLM_INVALID_KEEP_ABS_POS_INFO_VALUE 0xE127 ///< 0xE127 = Invalid keep absolute position value specified for an index. +#define NE_SFLM_INVALID_KEYS_UNIQUE_VALUE 0xE128 ///< 0xE128 = Invalid keys unique value specified for an index. +#define NE_SFLM_DUPLICATE_KEY_COMPONENT 0xE129 ///< 0xE129 = Duplicate key component in internal index definition.\ Data dictionary is corrupt. +#define NE_SFLM_INVALID_KEY_COMPONENT 0xE12A ///< 0xE12A = Invalid key component in internal index definition.\ Data dictionary is corrupt. +#define NE_SFLM_UNDEFINED_KEY_COMPONENT 0xE12B ///< 0xE12B = Undefined key component in internal index definition.\ Data dictionary is corrupt. +#define NE_SFLM_DUPLICATE_DATA_COMPONENT 0xE12C ///< 0xE12C = Duplicate data component in internal index definition.\ Data dictionary is corrupt. +#define NE_SFLM_INVALID_DATA_COMPONENT 0xE12D ///< 0xE12D = Invalid data component in internal index definition.\ Data dictionary is corrupt. +#define NE_SFLM_UNDEFINED_DATA_COMPONENT 0xE12E ///< 0xE12E = Undefined data component in internal index definition.\ Data dictionary is corrupt. +#define NE_SFLM_NO_COMP_NUM_FOR_INDEX_COLUMN 0xE12F ///< 0xE12F = Neither key component or data component specified for index column.\ Data dictionary is corrupt. +#define NE_SFLM_INVALID_INDEX_ON_VALUE 0xE130 ///< 0xE130 = Invalid value for the "index on" option in an index definition. +#define NE_SFLM_INVALID_COMPARE_RULES_VALUE 0xE131 ///< 0xE131 = Invalid value for the "compare rules" option in an index definition. +#define NE_SFLM_INVALID_SORT_DESCENDING_VALUE 0xE132 ///< 0xE132 = Invalid value for the "sort descending" option in an index definition. +#define NE_SFLM_INVALID_SORT_MISSING_HIGH_VALUE 0xE133 ///< 0xE133 = Invalid value for the "sort missing high" option in an index definition. // Query Errors @@ -1335,17 +1397,52 @@ flminterface IF_BackupStatus : public F_Object FLMUINT64 ui64BytesDone) = 0; }; -/// Structure for sending data in for table columns. +/// Structure for defining columns when creating a table. +typedef struct F_COLUMN_DEF +{ + const char * pszColumnName; ///< Name of column. + FLMUINT uiColumnNameLen; ///< Length of the column name. + eDataType eColumnDataType; ///< Column's data type. + FLMUINT uiMaxLen; ///< Maximum length for column.\ Only used for binary and strings. + FLMUINT uiFlags; ///< Column's definition flags. + FLMUINT uiEncDefNum; ///< Column's encryption definition number, if any. + F_COLUMN_DEF * pNext; ///< Next column in linked list. +} F_COLUMN_DEF; + +/// Structure for defining columns that belong to an index. +typedef struct F_INDEX_COL_DEF +{ + FLMUINT uiColumnNum; ///< Column number that is to be in key. + FLMUINT uiFlags; ///< Flags for this column +#define ICD_VALUE 0x00000010 // Value agrees with parsing syntax +#define ICD_EACHWORD 0x00000020 // Index each and every word in the field +#define ICD_PRESENCE 0x00000040 // Index the tag and NOT the value +#define ICD_METAPHONE 0x00000080 // Index words of text strings using + // metaphone values +#define ICD_SUBSTRING 0x00000100 // Index all substrings pieces +#define ICD_DESCENDING 0x00000200 // Sort in descending order. +#define ICD_MISSING_HIGH 0x00000400 // Sort missing components high instead of low. +#define ICD_ESC_CHAR 0x00001000 // Placehold so that a query can parse the input + // string and find a literal '*' or '\\'. + // Not specified in dictionary def. or kept in ICD. + // Only a temporary flag. + FLMUINT uiCompareRules; ///< Comparison rules for this column. + FLMUINT uiLimit; ///< Limit for this column. + F_INDEX_COL_DEF * pNext; ///< Next column in linked list. +} F_INDEX_COL_DEF; + +/// Structure for setting value for a column in a table. typedef struct F_COLUMN_VALUE { - FLMUINT uiColumnNum; ///< Column data is for. - eDataType eColumnDataType; ///< Column's data type. - FLMBYTE* pucColumnValue; ///< Column's value.\ For string data it is a SEN followed by a UTF8 string.\ The SEN - ///< gives the number of characters (as opposed to bytes) in the string.\ For number - ///< data there are two pieces.\ The first byte is a 1 or 0, indicating if the - ///< number is negative (1=negative, 0=positive).\ Following that byte is a - ///< SEN that contains the absolute value of the number.\ Binary data may be anything. - FLMUINT uiValueLen; ///< Length of column's value. + FLMUINT uiColumnNum; ///< Column number. + FLMBYTE* pucColumnValue; ///< Column's value.\ For string data it is a SEN followed by a UTF8 string.\ The SEN + ///< gives the number of characters (as opposed to bytes) in the string.\ For number + ///< data there are two pieces.\ The first byte is a 1 or 0, indicating if the + ///< number is negative (1=negative, 0=positive).\ Following that byte is a + ///< SEN that contains the absolute value of the number.\ Binary data may be anything.\ NOTE: + ///< The pucColumnValue and uiValueLen fields are NOT set for the CREATE TABLE statement. + FLMUINT uiValueLen; ///< Length of column's value.\ Not set for the CREATE TABLE statement. + F_COLUMN_VALUE * pNext; ///< Next column in linked list. } F_COLUMN_VALUE; /**************************************************************************** @@ -1435,6 +1532,32 @@ flminterface IF_RestoreStatus : public F_Object FLMUINT uiTableNum, F_COLUMN_VALUE * pColumnValues, FLMUINT uiNumColumnValues) = 0; + + virtual RCODE reportCreateTable( + eRestoreAction * peAction, + FLMUINT uiTableNum, + const char * pszTableName, + FLMUINT uiTableNameLen, + FLMUINT uiEncDefNum, + F_COLUMN_DEF * pColumnDefs, + FLMUINT uiNumColumnDefs) = 0; + + virtual RCODE reportCreateIndex( + eRestoreAction * peAction, + FLMUINT uiTableNum, + FLMUINT uiIndexNum, + const char * pszIndexName, + FLMUINT uiIndexNameLen, + FLMUINT uiEncDefNum, + FLMUINT uiFlags, + F_INDEX_COL_DEF * pIxColDefs, + FLMUINT uiNumIxColDefs) = 0; + + virtual RCODE reportIndexSet( + eRestoreAction * peAction, + FLMUINT uiIndexNum, + FLMUINT64 ui64StartRowId, + FLMUINT64 ui64EndRowId) = 0; }; /**************************************************************************** diff --git a/sql/src/flaimsys.h b/sql/src/flaimsys.h index 207db82..8885633 100644 --- a/sql/src/flaimsys.h +++ b/sql/src/flaimsys.h @@ -1487,7 +1487,7 @@ typedef struct KEY_GEN_INFO } KEY_GEN_INFO; /***************************************************************************** -Desc: Thread's database object - returned by dbOpen, dbCreate in F_DbSystem class +Desc: Thread's database object - returned by openDatabase, createDatabase in F_DbSystem class *****************************************************************************/ class F_Db : public F_Object { @@ -1907,8 +1907,25 @@ public: RCODE insertRow( FLMUINT uiTableNum, - F_COLUMN_VALUE * pColumnValues, - FLMUINT uiNumColumnValues); + F_COLUMN_VALUE * pColumnValues); + + RCODE createTable( + FLMUINT uiTableNum, + const char * pszTableName, + FLMUINT uiTableNameLen, + FLMUINT uiEncDefNum, + F_COLUMN_DEF * pColumnDefs, + FLMUINT uiNumColumnDefs); + + RCODE createIndex( + FLMUINT uiTableNum, + FLMUINT uiIndexNum, + const char * pszIndexName, + FLMUINT uiIndexNameLen, + FLMUINT uiEncDefNum, + FLMUINT uiFlags, + F_INDEX_COL_DEF * pIxColDefs, + FLMUINT uiNumIxColDefs); private: @@ -2195,10 +2212,6 @@ private: FLMBOOL * pbHitEnd, IF_Thread * pThread); - RCODE buildIndex( - FLMUINT uiIndexNum, - FLMUINT uiState); - RCODE readBlkHdr( FLMUINT uiBlkAddress, F_BLK_HDR * pBlkHdr, @@ -2897,10 +2910,11 @@ public: void exit(); + void getFileSystem( IF_FileSystem ** ppFileSystem); - RCODE dbCreate( + RCODE createDatabase( const char * pszDbFileName, const char * pszDataDir, const char * pszRflDir, @@ -2908,31 +2922,25 @@ public: FLMBOOL bTempDb, F_Db ** ppDb); - FINLINE RCODE dbCreate( + FINLINE RCODE createDatabase( const char * pszDbFileName, const char * pszDataDir, const char * pszRflDir, SFLM_CREATE_OPTS * pCreateOpts, F_Db ** ppDb) { - return( dbCreate( pszDbFileName, pszDataDir, pszRflDir, + return( createDatabase( pszDbFileName, pszDataDir, pszRflDir, pCreateOpts, FALSE, ppDb)); } - FINLINE RCODE dbOpen( - const char * pszDbFileName, - const char * pszDataDir, - const char * pszRflDir, - const char * pszPassword, - FLMBOOL bAllowLimited, - F_Db ** ppDb) - { - FLMUINT uiOpenFlags = bAllowLimited ? SFLM_ALLOW_LIMITED_MODE : 0; - - return( openDb( pszDbFileName, pszDataDir, pszRflDir, - pszPassword, uiOpenFlags, ppDb)); - } - + RCODE openDatabase( + const char * pszDbFileName, + const char * pszDataDir, + const char * pszRflDir, + const char * pszPassword, + FLMUINT uiOpenFlags, + F_Db ** ppDb); + RCODE dbRebuild( const char * pszSourceDbPath, const char * pszSourceDataDir, @@ -3208,14 +3216,6 @@ public: return( rc); } - RCODE openDb( - const char * pszDbFileName, - const char * pszDataDir, - const char * pszRflDir, - const char * pszPassword, - FLMUINT uiOpenFlags, - F_Db ** ppDb); - static FINLINE FLMBOOL validBlockSize( FLMUINT uiBlockSize) { @@ -3756,9 +3756,6 @@ private: FLMUINT uiResetKeyLen, FLMUINT64 ui64ResetNodeId); - RCODE buildIndexKeyList( - FLMUINT64 * pui64TotalKeys); - RCODE verifyBTrees( FLMBOOL * pbStartOverRV); diff --git a/sql/src/flkeyret.cpp b/sql/src/flkeyret.cpp index 8cfabc3..e16c494 100644 --- a/sql/src/flkeyret.cpp +++ b/sql/src/flkeyret.cpp @@ -96,6 +96,7 @@ RCODE F_Db::keyRetrieve( // Make sure it is a valid index definition pIndex = m_pDict->getIndex( uiIndex); + pLFile = &pIndex->lfInfo; if (RC_BAD( rc = flushKeys())) { diff --git a/sql/src/fslfileu.cpp b/sql/src/fslfileu.cpp index 38f775c..6216997 100644 --- a/sql/src/fslfileu.cpp +++ b/sql/src/fslfileu.cpp @@ -74,90 +74,6 @@ Exit: return( rc); } -/**************************************************************************** -Desc: Build an index. -****************************************************************************/ -RCODE F_Db::buildIndex( - FLMUINT uiIndexNum, - FLMUINT uiState) -{ - RCODE rc = NE_SFLM_OK; - LFILE * pIxLFile; - F_INDEX * pIndex; - - // Flush any KY keys and free the tables because they may grow! - - if (RC_BAD( rc = keysCommit( TRUE))) - { - goto Exit; - } - - if (RC_BAD( rc = krefCntrlCheck())) - { - goto Exit; - } - - pIndex = m_pDict->getIndex( uiIndexNum); - flmAssert( pIndex); - pIxLFile = &pIndex->lfInfo; - - // NON-BLOCKING INDEX BUILD - NOTE: The IXD_SUSPENDED flag may - // also be set, which indicates that we should NOT start the - // background maintenance thread right now. - - if (uiState & IXD_OFFLINE) - { - if (RC_BAD( rc = setIxStateInfo( uiIndexNum, 0, uiState))) - { - goto Exit; - } - - // setIxStateInfo may have changed to a new dictionary, so pIndex is no - // good after this point - - pIndex = NULL; - - // Don't schedule a maintenance thread if index is to start - // out life in a suspended state, or if we are replaying - // the roll-forward log. - - if (!(uiState & IXD_SUSPENDED) && !(m_uiFlags & FDB_REPLAYING_RFL)) - { - if (RC_BAD( rc = addToStartList( uiIndexNum))) - { - goto Exit; - } - } - - // Done - - goto Exit; - } - - // There may be "new" rows in the row cache. - // Need to flush them to the database so that - // the B-Tree lookups done by the indexing code will - // work correctly - - if( RC_BAD( rc = flushDirtyRows())) - { - goto Exit; - } - - // NORMAL INDEX BUILD - BLOCKING. uiIndexToBeUpdated better be - // zero at this point since we are not working in the background. - - if (RC_BAD( rc = indexSetOfRows( uiIndexNum, 1, - FLM_MAX_UINT64, m_pIxStatus, m_pIxClient, NULL, NULL, NULL))) - { - goto Exit; - } - -Exit: - - return( rc); -} - /**************************************************************************** Desc: Logs information about an index being built ****************************************************************************/ @@ -455,6 +371,17 @@ Commit_Keys: pIndex = NULL; } + + // Log the rows that were indexed, if any + + if (ui64LastRowId) + { + if (RC_BAD( rc = m_pDatabase->m_pRfl->logIndexSet( this, uiIndexNum, + ui64StartRowId, ui64LastRowId))) + { + goto Exit; + } + } Exit: diff --git a/sql/src/insertrow.cpp b/sql/src/insertrow.cpp new file mode 100644 index 0000000..f87fb5b --- /dev/null +++ b/sql/src/insertrow.cpp @@ -0,0 +1,449 @@ +//------------------------------------------------------------------------------ +// 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" + +//------------------------------------------------------------------------------ +// Desc: Insert a row into the database. +//------------------------------------------------------------------------------ +RCODE F_Db::insertRow( + FLMUINT uiTableNum, + F_COLUMN_VALUE * pColumnValues) +{ + RCODE rc = NE_SFLM_OK; + F_Row * pRow = NULL; + const FLMBYTE * pucValue; + const FLMBYTE * pucEnd; + FLMUINT64 ui64Num; + FLMUINT uiNumChars; + FLMBOOL bNeg; + F_COLUMN_VALUE * pColumnValue; + F_TABLE * pTable = m_pDict->getTable( uiTableNum); + F_COLUMN * pColumn; + + // 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 (pColumnValue = pColumnValues; + pColumnValue; + pColumnValue = pColumnValue->pNext) + { + if (!pColumnValue->uiValueLen) + { + continue; + } + pColumn = m_pDict->getColumn( pTable, pColumnValue->uiColumnNum); + switch (pColumn->eDataTyp) + { + case SFLM_STRING_TYPE: + pucValue = (const FLMBYTE *)pColumnValue->pucColumnValue; + pucEnd = pucValue + pColumnValue->uiValueLen; + if (RC_BAD( rc = f_decodeSEN( &pucValue, pucEnd, &uiNumChars))) + { + goto Exit; + } + if (RC_BAD( rc = pRow->setUTF8( this, + pColumnValue->uiColumnNum, + (const char *)pucValue, + (FLMUINT)(pucEnd - pucValue), + uiNumChars))) + { + goto Exit; + } + break; + case SFLM_NUMBER_TYPE: + pucValue = (const FLMBYTE *)pColumnValue->pucColumnValue; + pucEnd = pucValue + pColumnValue->uiValueLen; + + 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, + pColumnValue->uiColumnNum, ui64Num, bNeg))) + { + goto Exit; + } + break; + case SFLM_BINARY_TYPE: + if (RC_BAD( rc = pRow->setBinary( this, + pColumnValue->uiColumnNum, + (const void *)(pColumnValue->pucColumnValue), + pColumnValue->uiValueLen))) + { + goto Exit; + } + break; + default: + flmAssert( 0); + break; + } + } + + // Do whatever indexing needs to be done. + + if (RC_BAD( rc = updateIndexKeys( uiTableNum, NULL, pRow))) + { + goto Exit; + } + + // Log the insert row. + + if (RC_BAD( rc = m_pDatabase->m_pRfl->logInsertRow( this, uiTableNum, + pColumnValues))) + { + 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 * pFirstColValue; + F_COLUMN_VALUE * pLastColValue; + F_COLUMN_VALUE * pColumnValue; + F_COLUMN * pColumn; + FLMUINT uiLoop; + char szColumnName [MAX_SQL_NAME_LEN + 1]; + FLMUINT uiColumnNameLen; + char szTableName [MAX_SQL_NAME_LEN + 1]; + FLMUINT uiTableNameLen; + F_TABLE * pTable; + + // 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, 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_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. + + pFirstColValue = NULL; + pLastColValue = NULL; + 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( 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; + } + + // Allocate a column value. + + if (RC_BAD( rc = m_tmpPool.poolAlloc( sizeof( F_COLUMN_VALUE), + (void **)&pColumnValue))) + { + goto Exit; + } + + pColumnValue->uiColumnNum = pColumn->uiColumnNum; + pColumnValue->uiValueLen = 0; + pColumnValue->pNext = NULL; + if (pLastColValue) + { + pLastColValue->pNext = pColumnValue; + } + else + { + pFirstColValue = pColumnValue; + } + pLastColValue = pColumnValue; + } + + 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 + { + + for (uiLoop = 0, pColumn = pTable->pColumns; + uiLoop < pTable->uiNumColumns; + uiLoop++, pColumn++) + { + if (pColumn->uiColumnNum) + { + // Allocate a column value. + + if (RC_BAD( rc = m_tmpPool.poolAlloc( sizeof( F_COLUMN_VALUE), + (void **)&pColumnValue))) + { + goto Exit; + } + pColumnValue->uiColumnNum = pColumn->uiColumnNum; + pColumnValue->uiValueLen = 0; + pColumnValue->pNext = NULL; + if (pLastColValue) + { + pLastColValue->pNext = pColumnValue; + } + else + { + pFirstColValue = pColumnValue; + } + pLastColValue = pColumnValue; + } + } + } + + // Allow for no values to be specified if no columns were. + + if (pFirstColValue) + { + 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; + } + + pColumnValue = pFirstColValue; + for (;;) + { + pColumn = m_pDb->m_pDict->getColumn( pTable, pColumnValue->uiColumnNum); + if (RC_BAD( rc = skipWhitespace( FALSE))) + { + goto Exit; + } + + // Get the column value + + if (RC_BAD( rc = getValue( pColumn, pColumnValue))) + { + goto Exit; + } + + if (RC_BAD( rc = skipWhitespace( FALSE))) + { + goto Exit; + } + if ((pColumnValue = pColumnValue->pNext) == NULL) + { + 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( pTable->uiTableNum, pFirstColValue))) + { + goto Exit; + } + + // Commit the transaction if we started it + + if (bStartedTrans) + { + bStartedTrans = FALSE; + if (RC_BAD( rc = m_pDb->transCommit())) + { + goto Exit; + } + } + +Exit: + + if (bStartedTrans) + { + m_pDb->transAbort(); + } + + return( rc); +} + + diff --git a/sql/src/flopen.cpp b/sql/src/opendatabase.cpp similarity index 99% rename from sql/src/flopen.cpp rename to sql/src/opendatabase.cpp index b1ba469..4abfe08 100644 --- a/sql/src/flopen.cpp +++ b/sql/src/opendatabase.cpp @@ -1,5 +1,5 @@ //------------------------------------------------------------------------------ -// Desc: Contains the F_DbSystem::openDb method. +// Desc: Contains the F_DbSystem::openDatabase method. // // Tabs: 3 // @@ -37,7 +37,7 @@ FSTATIC RCODE flmCPThread( Desc: Does most of the actual work of opening an existing database, but doesn't provide COM interfaces... ****************************************************************************/ -RCODE F_DbSystem::openDb( +RCODE F_DbSystem::openDatabase( const char * pszDbFileName, const char * pszDataDir, const char * pszRflDir, diff --git a/sql/src/rfl.cpp b/sql/src/rfl.cpp index 0a30347..db4afb3 100644 --- a/sql/src/rfl.cpp +++ b/sql/src/rfl.cpp @@ -3891,36 +3891,895 @@ Exit: return( rc); } +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::logColumnDefs( + F_Db * pDb, + F_COLUMN_DEF * pColumnDefs) +{ + RCODE rc = NE_SFLM_OK; + F_COLUMN_DEF * pColumnDef; + FLMUINT uiPacketLen = RFL_PACKET_OVERHEAD; + FLMBYTE * pucOut; + + flmAssert( isLoggingEnabled()); + + if( !haveBuffSpace( RFL_PACKET_OVERHEAD)) + { + if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) + { + goto Exit; + } + } + + // Go through each column's definition + + for (pColumnDef = pColumnDefs; pColumnDef; pColumnDef = pColumnDef->pNext) + { + + if( RC_BAD( rc = makeRoom( pDb, + pColumnDef->uiColumnNameLen + FLM_MAX_SEN_LEN * 4 + 1, + &uiPacketLen, RFL_COLUMN_DEF_PACKET, NULL, NULL))) + { + goto Exit; + } + + pucOut = getPacketPtr() + uiPacketLen; + + // Output the column name length and column name. + + f_encodeSEN( pColumnDef->uiColumnNameLen, &pucOut); + f_memcpy( pucOut, pColumnDef->pszColumnName, pColumnDef->uiColumnNameLen); + pucOut += pColumnDef->uiColumnNameLen; + + // Output the data type. + + *pucOut++ = (FLMBYTE)pColumnDef->eColumnDataType; + + // Output the maximum length + + f_encodeSEN( pColumnDef->uiMaxLen, &pucOut); + + // Output the flags + + f_encodeSEN( pColumnDef->uiFlags, &pucOut); + + // Output the encryption definition number + + f_encodeSEN( pColumnDef->uiEncDefNum, &pucOut); + } + + // Add a column number of zero to terminate the columns. + + if (RC_BAD( rc = makeRoom( pDb, 1, &uiPacketLen, + RFL_COLUMN_DEF_PACKET, NULL, NULL))) + { + goto Exit; + } + + pucOut = getPacketPtr() + uiPacketLen; + *pucOut++ = 0; + uiPacketLen++; + + // Finish the packet. + + if (RC_BAD( rc = finishPacket( pDb, RFL_COLUMN_DEF_PACKET, + uiPacketLen - RFL_PACKET_OVERHEAD, FALSE))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::logCreateTable( + F_Db * pDb, + FLMUINT uiTableNum, + const char * pszTableName, + FLMUINT uiTableNameLen, + FLMUINT uiEncDefNum, + F_COLUMN_DEF * pColumnDefs) +{ + RCODE rc = NE_SFLM_OK; + FLMUINT uiPacketBodyLen; + FLMBYTE * pucPacketBody; + FLMBYTE * pucPacketStart; + + flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); + + // Do nothing if logging is disabled. + + if( !isLoggingEnabled()) + { + goto Exit; + } + + // Better be in the middle of a transaction. + + flmAssert( m_ui64CurrTransID); + + // Increment the operation count + + m_uiOperCount++; + + // Make sure we have space in the RFL buffer for a complete packet. NOTE: + // this is calculating the maximum packet body length that would be needed. + + if( !haveBuffSpace( uiTableNameLen + FLM_MAX_SEN_LEN * 3 + RFL_PACKET_OVERHEAD)) + { + if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = pucPacketStart = getPacketBodyPtr(); + + // Output the table number. + + f_encodeSEN( uiTableNum, &pucPacketBody); + + // Output the table name length and table name. + + f_encodeSEN( uiTableNameLen, &pucPacketBody); + f_memcpy( pucPacketBody, pszTableName, uiTableNameLen); + pucPacketBody += uiTableNameLen; + + // Output the encryption definition number. + + f_encodeSEN( uiEncDefNum, &pucPacketBody); + + // Finish the packet - calculate the actual packet body length. + + uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); + + if (RC_BAD( rc = finishPacket( pDb, RFL_CREATE_TABLE_PACKET, + uiPacketBodyLen, FALSE))) + { + goto Exit; + } + + // Now output the column definitions + + if (RC_BAD( rc = logColumnDefs( pDb, pColumnDefs))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::recovCreateTable( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction) +{ + RCODE rc = NE_SFLM_OK; + FLMUINT uiTableNum; + char * pszTableName; + FLMUINT uiTableNameLen; + FLMUINT uiEncDefNum; + char * pszTmp; + FLMUINT uiNumColumnDefs; + FLMUINT uiPacketType; + F_COLUMN_DEF * pColumnDef; + F_COLUMN_DEF * pFirstColDef; + F_COLUMN_DEF * pLastColDef; + FLMUINT uiColumnNameLen; + const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; + FLMBOOL bHitEnd; + + // Get the table number from the packet + + if (RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiTableNum))) + { + goto Exit; + } + + // Get the table name length and table name from the packet. + + if (RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiTableNameLen))) + { + goto Exit; + } + m_tmpPool.poolReset( NULL); + if (RC_BAD( rc = m_tmpPool.poolAlloc( uiTableNameLen + 1, + (void **)&pszTableName))) + { + goto Exit; + } + f_memcpy( pszTableName, pucPacketBody, uiTableNameLen); + pucPacketBody += uiTableNameLen; + pszTableName [uiTableNameLen] = 0; + + // Get the encryption definition number from the packet + + if (RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiEncDefNum))) + { + goto Exit; + } + // Get the column definitions. + + pucPacketBody = pucEnd = NULL; + uiNumColumnDefs = 0; + pFirstColDef = NULL; + pLastColDef = NULL; + for (;;) + { + + // If we have used everything in our current packet, get another. + + if (pucPacketBody == pucEnd) + { + if( RC_BAD( rc = getPacket( + pDb, FALSE, &uiPacketType, &pucPacketBody, &uiPacketBodyLen, + &bHitEnd))) + { + goto Exit; + } + + // Should not hit the end of the RFL here! + + if (bHitEnd) + { + rc = RC_SET( NE_SFLM_RFL_INCOMPLETE); + goto Exit; + } + + if (uiPacketType != RFL_COLUMN_DEF_PACKET) + { + rc = RC_SET( NE_SFLM_BAD_RFL_PACKET); + goto Exit; + } + pucEnd = pucPacketBody + uiPacketBodyLen; + } + + // Get the column name length and name. + + if (RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, + &uiColumnNameLen))) + { + goto Exit; + } + if (!uiColumnNameLen) + { + break; + } + if (RC_BAD( rc = m_tmpPool.poolAlloc( sizeof( F_COLUMN_DEF), + (void **)&pColumnDef))) + { + goto Exit; + } + uiNumColumnDefs++; + pColumnDef->pNext = NULL; + if (pLastColDef) + { + pLastColDef->pNext = pColumnDef; + } + else + { + pFirstColDef = pColumnDef; + } + pLastColDef = pColumnDef; + pColumnDef->uiColumnNameLen = uiColumnNameLen; + if (RC_BAD( rc = m_tmpPool.poolAlloc( uiColumnNameLen + 1, + (void **)&pszTmp))) + { + goto Exit; + } + f_memcpy( pszTmp, pucPacketBody, uiColumnNameLen); + pucPacketBody += uiColumnNameLen; + pszTmp [uiColumnNameLen] = 0; + pColumnDef->pszColumnName = pszTmp; + + // Get the data type. + + pColumnDef->eColumnDataType = (eDataType)(*pucPacketBody); + switch (pColumnDef->eColumnDataType) + { + case SFLM_STRING_TYPE: + case SFLM_NUMBER_TYPE: + case SFLM_BINARY_TYPE: + break; + default: + rc = RC_SET( NE_SFLM_BAD_RFL_PACKET); + goto Exit; + } + pucPacketBody++; + + // Get the maximum length + + if (RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, + &pColumnDef->uiMaxLen))) + { + goto Exit; + } + + // Get the flags + + if (RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, + &pColumnDef->uiFlags))) + { + goto Exit; + } + + // Get the encryption definition number + + if (RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, + &pColumnDef->uiEncDefNum))) + { + goto Exit; + } + } + + if (m_pRestoreStatus) + { + if( RC_BAD( rc = m_pRestoreStatus->reportCreateTable( + peAction, uiTableNum, pszTableName, uiTableNameLen, + uiEncDefNum, pFirstColDef, uiNumColumnDefs))) + { + goto Exit; + } + + if( *peAction == SFLM_RESTORE_ACTION_STOP) + { + m_ui64CurrTransID = 0; + goto Exit; + } + } + + if( RC_BAD( rc = pDb->createTable( uiTableNum, pszTableName, uiTableNameLen, + uiEncDefNum, pFirstColDef, uiNumColumnDefs))) + { + goto Exit; + } + +Exit: + + m_tmpPool.poolReset( NULL); + + m_ui64CurrTransID = 0; + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::logIndexColumnDefs( + F_Db * pDb, + F_INDEX_COL_DEF * pIxColDefs) +{ + RCODE rc = NE_SFLM_OK; + F_INDEX_COL_DEF * pIxColDef; + FLMUINT uiPacketLen = RFL_PACKET_OVERHEAD; + FLMBYTE * pucOut; + + flmAssert( isLoggingEnabled()); + + if( !haveBuffSpace( RFL_PACKET_OVERHEAD)) + { + if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) + { + goto Exit; + } + } + + // Go through each column's definition + + for (pIxColDef = pIxColDefs; pIxColDef; pIxColDef = pIxColDef->pNext) + { + if( RC_BAD( rc = makeRoom( pDb, + FLM_MAX_SEN_LEN * 4, + &uiPacketLen, RFL_INDEX_COLUMN_DEF_PACKET, NULL, NULL))) + { + goto Exit; + } + + pucOut = getPacketPtr() + uiPacketLen; + + // Output the column number + + f_encodeSEN( pIxColDef->uiColumnNum, &pucOut); + + // Output the flags. + + f_encodeSEN( pIxColDef->uiFlags, &pucOut); + + // Output the comparison rules + + f_encodeSEN( pIxColDef->uiCompareRules, &pucOut); + + // Output the limit + + f_encodeSEN( pIxColDef->uiLimit, &pucOut); + } + + // Add a column number of zero to terminate the columns. + + if (RC_BAD( rc = makeRoom( pDb, 1, &uiPacketLen, + RFL_INDEX_COLUMN_DEF_PACKET, NULL, NULL))) + { + goto Exit; + } + + pucOut = getPacketPtr() + uiPacketLen; + *pucOut++ = 0; + uiPacketLen++; + + // Finish the packet. + + if (RC_BAD( rc = finishPacket( pDb, RFL_INDEX_COLUMN_DEF_PACKET, + uiPacketLen - RFL_PACKET_OVERHEAD, FALSE))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::logCreateIndex( + F_Db * pDb, + FLMUINT uiTableNum, + FLMUINT uiIndexNum, + const char * pszIndexName, + FLMUINT uiIndexNameLen, + FLMUINT uiEncDefNum, + F_INDEX_COL_DEF * pIxColDefs, + FLMUINT uiFlags) +{ + RCODE rc = NE_SFLM_OK; + FLMUINT uiPacketBodyLen; + FLMBYTE * pucPacketBody; + FLMBYTE * pucPacketStart; + + flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); + + // Do nothing if logging is disabled. + + if( !isLoggingEnabled()) + { + goto Exit; + } + + // Better be in the middle of a transaction. + + flmAssert( m_ui64CurrTransID); + + // Increment the operation count + + m_uiOperCount++; + + // Make sure we have space in the RFL buffer for a complete packet. NOTE: + // this is calculating the maximum packet body length that would be needed. + + if( !haveBuffSpace( uiIndexNameLen + FLM_MAX_SEN_LEN * 5 + RFL_PACKET_OVERHEAD)) + { + if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = pucPacketStart = getPacketBodyPtr(); + + // Output the table number. + + f_encodeSEN( uiTableNum, &pucPacketBody); + + // Output the index number. + + f_encodeSEN( uiIndexNum, &pucPacketBody); + + // Output the index name length and index name. + + f_encodeSEN( uiIndexNameLen, &pucPacketBody); + f_memcpy( pucPacketBody, pszIndexName, uiIndexNameLen); + pucPacketBody += uiIndexNameLen; + + // Output the encryption definition number. + + f_encodeSEN( uiEncDefNum, &pucPacketBody); + + // Output the flags. + + f_encodeSEN( uiFlags, &pucPacketBody); + + // Finish the packet - calculate the actual packet body length. + + uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); + + if (RC_BAD( rc = finishPacket( pDb, RFL_CREATE_INDEX_PACKET, + uiPacketBodyLen, FALSE))) + { + goto Exit; + } + + // Now output the column definitions for the index + + if (RC_BAD( rc = logIndexColumnDefs( pDb, pIxColDefs))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::recovCreateIndex( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction) +{ + RCODE rc = NE_SFLM_OK; + FLMUINT uiTableNum; + FLMUINT uiIndexNum; + char * pszIndexName; + FLMUINT uiIndexNameLen; + FLMUINT uiEncDefNum; + FLMUINT uiFlags; + FLMUINT uiNumIxColDefs; + FLMUINT uiPacketType; + F_INDEX_COL_DEF * pIxColDef; + F_INDEX_COL_DEF * pFirstIxColDef; + F_INDEX_COL_DEF * pLastIxColDef; + const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; + FLMBOOL bHitEnd; + FLMUINT uiColumnNum; + + // Get the table number from the packet + + if (RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiTableNum))) + { + goto Exit; + } + + // Get the index number from the packet + + if (RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiIndexNum))) + { + goto Exit; + } + + // Get the index name length and index name from the packet. + + if (RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiIndexNameLen))) + { + goto Exit; + } + m_tmpPool.poolReset( NULL); + if (RC_BAD( rc = m_tmpPool.poolAlloc( uiIndexNameLen + 1, + (void **)&pszIndexName))) + { + goto Exit; + } + f_memcpy( pszIndexName, pucPacketBody, uiIndexNameLen); + pucPacketBody += uiIndexNameLen; + pszIndexName [uiIndexNameLen] = 0; + + // Get the encryption definition number from the packet + + if (RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiEncDefNum))) + { + goto Exit; + } + + // Get the index flags from the packet + + if (RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiFlags))) + { + goto Exit; + } + + // Get the index column definitions. + + pucPacketBody = pucEnd = NULL; + uiNumIxColDefs = 0; + pFirstIxColDef = NULL; + pLastIxColDef = NULL; + for (;;) + { + + // If we have used everything in our current packet, get another. + + if (pucPacketBody == pucEnd) + { + if( RC_BAD( rc = getPacket( + pDb, FALSE, &uiPacketType, &pucPacketBody, &uiPacketBodyLen, + &bHitEnd))) + { + goto Exit; + } + + // Should not hit the end of the RFL here! + + if (bHitEnd) + { + rc = RC_SET( NE_SFLM_RFL_INCOMPLETE); + goto Exit; + } + + if (uiPacketType != RFL_INDEX_COLUMN_DEF_PACKET) + { + rc = RC_SET( NE_SFLM_BAD_RFL_PACKET); + goto Exit; + } + pucEnd = pucPacketBody + uiPacketBodyLen; + } + + // Get the column number + + if (RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, + &uiColumnNum))) + { + goto Exit; + } + if (!uiColumnNum) + { + break; + } + if (RC_BAD( rc = m_tmpPool.poolAlloc( sizeof( F_INDEX_COL_DEF), + (void **)&pIxColDef))) + { + goto Exit; + } + uiNumIxColDefs++; + pIxColDef->pNext = NULL; + if (pLastIxColDef) + { + pLastIxColDef->pNext = pIxColDef; + } + else + { + pFirstIxColDef = pIxColDef; + } + pLastIxColDef = pIxColDef; + pIxColDef->uiColumnNum = uiColumnNum; + + // Get the flags + + if (RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, + &pIxColDef->uiFlags))) + { + goto Exit; + } + + // Get the comparison rules + + if (RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, + &pIxColDef->uiCompareRules))) + { + goto Exit; + } + + // Get the limit + + if (RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, + &pIxColDef->uiLimit))) + { + goto Exit; + } + } + + if (m_pRestoreStatus) + { + if( RC_BAD( rc = m_pRestoreStatus->reportCreateIndex( + peAction, uiTableNum, uiIndexNum, pszIndexName, uiIndexNameLen, + uiEncDefNum, uiFlags, pFirstIxColDef, uiNumIxColDefs))) + { + goto Exit; + } + + if( *peAction == SFLM_RESTORE_ACTION_STOP) + { + m_ui64CurrTransID = 0; + goto Exit; + } + } + + if( RC_BAD( rc = pDb->createIndex( uiTableNum, uiIndexNum, pszIndexName, uiIndexNameLen, + uiEncDefNum, uiFlags, pFirstIxColDef, uiNumIxColDefs))) + { + goto Exit; + } + +Exit: + + m_tmpPool.poolReset( NULL); + + m_ui64CurrTransID = 0; + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::logIndexSet( + F_Db * pDb, + FLMUINT uiIndexNum, + FLMUINT64 ui64StartRowId, + FLMUINT64 ui64EndRowId) +{ + RCODE rc = NE_SFLM_OK; + FLMUINT uiPacketBodyLen; + FLMBYTE * pucPacketBody; + FLMBYTE * pucPacketStart; + + flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); + + // Do nothing if logging is disabled. + + if( !isLoggingEnabled()) + { + goto Exit; + } + + // Better be in the middle of a transaction. + + flmAssert( m_ui64CurrTransID); + + // Increment the operation count + + m_uiOperCount++; + + // Make sure we have space in the RFL buffer for a complete packet. NOTE: + // this is calculating the maximum packet body length that would be needed. + + if (!haveBuffSpace( FLM_MAX_SEN_LEN * 3 + RFL_PACKET_OVERHEAD)) + { + if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = pucPacketStart = getPacketBodyPtr(); + + // Output the index number. + + f_encodeSEN( uiIndexNum, &pucPacketBody); + + // Output the start row id. + + f_encodeSEN( ui64StartRowId, &pucPacketBody); + + // Output the end row id. + + f_encodeSEN( ui64EndRowId, &pucPacketBody); + + // Finish the packet - calculate the actual packet body length. + + uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); + if (RC_BAD( rc = finishPacket( pDb, RFL_INDEX_SET_PACKET, + uiPacketBodyLen, FALSE))) + { + goto Exit; + } + +Exit: + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::recovIndexSet( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction) +{ + RCODE rc = NE_SFLM_OK; + FLMUINT uiIndexNum; + FLMUINT64 ui64StartRowId; + FLMUINT64 ui64EndRowId; + const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; + + // Get the index number from the packet + + if (RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiIndexNum))) + { + goto Exit; + } + + // Get the start row ID from the packet + + if (RC_BAD( rc = f_decodeSEN64( &pucPacketBody, pucEnd, &ui64StartRowId))) + { + goto Exit; + } + + // Get the end row ID from the packet + + if (RC_BAD( rc = f_decodeSEN64( &pucPacketBody, pucEnd, &ui64EndRowId))) + { + goto Exit; + } + + if (m_pRestoreStatus) + { + if( RC_BAD( rc = m_pRestoreStatus->reportIndexSet( + peAction, uiIndexNum, ui64StartRowId, ui64EndRowId))) + { + goto Exit; + } + + if( *peAction == SFLM_RESTORE_ACTION_STOP) + { + m_ui64CurrTransID = 0; + goto Exit; + } + } + + if (RC_BAD( rc = pDb->indexSetOfRows( uiIndexNum, ui64StartRowId, + ui64EndRowId, NULL, NULL, NULL, NULL, NULL))) + { + goto Exit; + } + +Exit: + + m_ui64CurrTransID = 0; + return( rc); +} + /******************************************************************** Desc: *********************************************************************/ RCODE F_Rfl::logColumnValues( F_Db * pDb, FLMUINT uiTableNum, - F_COLUMN_VALUE * pColumnValues, - FLMUINT uiNumColumnValues) + F_COLUMN_VALUE * pColumnValues) { - RCODE rc = NE_SFLM_OK; - FLMUINT uiPacketLen = RFL_PACKET_OVERHEAD; - FLMUINT uiBytesLeftToWrite; - FLMUINT uiBytesToWrite; - FLMBYTE * pucValue; - FLMBYTE * pucOut; - FLMBYTE * pucIV; - FLMBYTE * pucStart; - F_TABLE * pTable = pDb->m_pDict->getTable( uiTableNum); - F_COLUMN * pColumn; - F_ENCDEF * pTableEncDef = (pTable->lfInfo.uiEncDefNum) + RCODE rc = NE_SFLM_OK; + FLMUINT uiPacketLen = RFL_PACKET_OVERHEAD; + FLMUINT uiBytesLeftToWrite; + FLMUINT uiBytesToWrite; + FLMBYTE * pucValue; + FLMBYTE * pucOut; + FLMBYTE * pucIV; + FLMBYTE * pucStart; + F_TABLE * pTable = pDb->m_pDict->getTable( uiTableNum); + F_COLUMN * pColumn; + F_ENCDEF * pTableEncDef = (pTable->lfInfo.uiEncDefNum) ? pDb->m_pDict->getEncDef( pTable->lfInfo.uiEncDefNum) : (F_ENCDEF *)NULL; - F_ENCDEF * pEncDef; - FLMUINT uiIVLen; - FLMUINT uiEncLen; - FLMUINT uiSenLen1; - FLMUINT uiSenLen2; - FLMUINT uiSpaceNeeded; - FLMUINT uiEncOutputLen; - FLMUINT uiBytesAvail; + F_ENCDEF * pEncDef; + FLMUINT uiIVLen; + FLMUINT uiEncLen; + FLMUINT uiSenLen1; + FLMUINT uiSenLen2; + FLMUINT uiSpaceNeeded; + FLMUINT uiEncOutputLen; + FLMUINT uiBytesAvail; + F_COLUMN_VALUE * pColumnValue; flmAssert( isLoggingEnabled()); @@ -3934,11 +4793,11 @@ RCODE F_Rfl::logColumnValues( // Go through each column's data - while (uiNumColumnValues) + for (pColumnValue = pColumnValues; pColumnValue; pColumnValue = pColumnValue->pNext) { - pColumn = pDb->m_pDict->getColumn( pTable, pColumnValues->uiColumnNum); + pColumn = pDb->m_pDict->getColumn( pTable, pColumnValue->uiColumnNum); - if( RC_BAD( rc = makeRoom( pDb, FLM_MAX_SEN_LEN * 2 + 1, + if( RC_BAD( rc = makeRoom( pDb, FLM_MAX_SEN_LEN * 2, &uiPacketLen, RFL_COLUMN_DATA_PACKET, NULL, NULL))) { goto Exit; @@ -3948,20 +4807,16 @@ RCODE F_Rfl::logColumnValues( // Output the column number. - f_encodeSEN( pColumnValues->uiColumnNum, &pucOut); + f_encodeSEN( pColumnValue->uiColumnNum, &pucOut); - // Output the data type. - - *pucOut++ = (FLMBYTE)pColumnValues->eColumnDataType; - // Output the value length - f_encodeSEN( pColumnValues->uiValueLen, &pucOut); + f_encodeSEN( pColumnValue->uiValueLen, &pucOut); uiPacketLen += (FLMUINT)(pucOut - pucStart); - uiBytesLeftToWrite = pColumnValues->uiValueLen; - pucValue = pColumnValues->pucColumnValue; + uiBytesLeftToWrite = pColumnValue->uiValueLen; + pucValue = pColumnValue->pucColumnValue; while (uiBytesLeftToWrite) { @@ -4086,8 +4941,6 @@ Finish_Packet: uiPacketLen = RFL_PACKET_OVERHEAD; } - pColumnValues++; - uiNumColumnValues--; } // Add a column number of zero to terminate the columns. @@ -4121,8 +4974,7 @@ Desc: RCODE F_Rfl::logInsertRow( F_Db * pDb, FLMUINT uiTableNum, - F_COLUMN_VALUE * pColumnValues, - FLMUINT uiNumColumnValues) + F_COLUMN_VALUE * pColumnValues) { RCODE rc = NE_SFLM_OK; FLMUINT uiPacketBodyLen; @@ -4149,7 +5001,7 @@ RCODE F_Rfl::logInsertRow( // Make sure we have space in the RFL buffer for a complete packet. NOTE: // this is calculating the maximum packet body length that would be needed. - if( !haveBuffSpace( FLM_MAX_SEN_LEN * 2 + RFL_PACKET_OVERHEAD)) + if( !haveBuffSpace( FLM_MAX_SEN_LEN + RFL_PACKET_OVERHEAD)) { if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) { @@ -4165,10 +5017,6 @@ RCODE F_Rfl::logInsertRow( f_encodeSEN( uiTableNum, &pucPacketBody); - // Output the number of column values - - f_encodeSEN( uiNumColumnValues, &pucPacketBody); - // Finish the packet - calculate the actual packet body length. uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); @@ -4182,8 +5030,7 @@ RCODE F_Rfl::logInsertRow( // Now output the column data - if (RC_BAD( rc = logColumnValues( pDb, uiTableNum, - pColumnValues, uiNumColumnValues))) + if (RC_BAD( rc = logColumnValues( pDb, uiTableNum, pColumnValues))) { goto Exit; } @@ -4204,10 +5051,11 @@ RCODE F_Rfl::recovInsertRow( { RCODE rc = NE_SFLM_OK; FLMUINT uiTableNum; - FLMUINT uiNumColumnValues; FLMUINT uiPacketType; - F_COLUMN_VALUE * pColValues; F_COLUMN_VALUE * pColumnValue; + F_COLUMN_VALUE * pFirstColValue; + F_COLUMN_VALUE * pLastColValue; + FLMUINT uiNumColumnValues; const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; FLMUINT uiIVLen; FLMBOOL bHitEnd; @@ -4237,267 +5085,234 @@ RCODE F_Rfl::recovInsertRow( pTableEncDef = (pTable->lfInfo.uiEncDefNum) ? pDb->m_pDict->getEncDef( pTable->lfInfo.uiEncDefNum) : (F_ENCDEF *)NULL; - - // Get the number of column values from the packet. - - if (RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiNumColumnValues))) - { - goto Exit; - } - - // Get the column values. - - if (uiNumColumnValues) + + pFirstColValue = NULL; + pLastColValue = NULL; + pucPacketBody = pucEnd = NULL; + uiNumColumnValues = 0; + for (;;) { + + // If we have used everything in our current packet, get another. + + if (pucPacketBody == pucEnd) + { + if( RC_BAD( rc = getPacket( + pDb, FALSE, &uiPacketType, &pucPacketBody, &uiPacketBodyLen, + &bHitEnd))) + { + goto Exit; + } + + // Should not hit the end of the RFL here! + + if (bHitEnd) + { + rc = RC_SET( NE_SFLM_RFL_INCOMPLETE); + goto Exit; + } + + if (uiPacketType != RFL_COLUMN_DATA_PACKET) + { + rc = RC_SET( NE_SFLM_BAD_RFL_PACKET); + goto Exit; + } + pucEnd = pucPacketBody + uiPacketBodyLen; + } - // Allocate space for the values. + // Get the column number. - m_tmpPool.poolReset( NULL); - if (RC_BAD( rc = m_tmpPool.poolAlloc( - sizeof( F_COLUMN_VALUE) * uiNumColumnValues, - (void **)&pColValues))) + if (RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiColumnNum))) { goto Exit; } - // Go into a loop processing packets until we have retrieved all of - // the columns. At that point, we had better be at the end - // of the column data. + // A zero column number means we are at the end - pColumnValue = pColValues; - pucPacketBody = pucEnd = NULL; - for (;;) + if (!uiColumnNum) { - - // If we have used everything in our current packet, get another. - - if (pucPacketBody == pucEnd) - { - if( RC_BAD( rc = getPacket( - pDb, FALSE, &uiPacketType, &pucPacketBody, &uiPacketBodyLen, - &bHitEnd))) - { - goto Exit; - } - - // Should not hit the end of the RFL here! - - if (bHitEnd) - { - rc = RC_SET( NE_SFLM_RFL_INCOMPLETE); - goto Exit; - } - - if (uiPacketType != RFL_COLUMN_DATA_PACKET) - { - rc = RC_SET( NE_SFLM_BAD_RFL_PACKET); - goto Exit; - } - pucEnd = pucPacketBody + uiPacketBodyLen; - } - - // Get the column number. - - if (RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiColumnNum))) - { - goto Exit; - } - - // A zero column number means we are at the end - - if (!uiColumnNum) - { - - // Shouldn't have hit zero if we still have column values to - // process. - - if (uiNumColumnValues) - { - rc = RC_SET( NE_SFLM_BAD_RFL_PACKET); - goto Exit; - } - break; - } - if ((pColumn = pDb->m_pDict->getColumn( pTable, uiColumnNum)) == NULL) - { - rc = RC_SET( NE_SFLM_BAD_RFL_PACKET); - goto Exit; - } - pColumnValue->uiColumnNum = uiColumnNum; - - // Get the data type - - if (pucPacketBody >= pucEnd) - { - rc = RC_SET( NE_SFLM_BAD_RFL_PACKET); - goto Exit; - } - pColumnValue->eColumnDataType = (eDataType)(*pucPacketBody); - switch (pColumnValue->eColumnDataType) - { - case SFLM_STRING_TYPE: - case SFLM_NUMBER_TYPE: - case SFLM_BINARY_TYPE: - break; - default: - rc = RC_SET( NE_SFLM_BAD_RFL_PACKET); - goto Exit; - } - pucPacketBody++; - - // Get the value length. + break; + } + if ((pColumn = pDb->m_pDict->getColumn( pTable, uiColumnNum)) == NULL) + { + rc = RC_SET( NE_SFLM_BAD_RFL_PACKET); + goto Exit; + } + uiNumColumnValues++; + if (RC_BAD( rc = m_tmpPool.poolAlloc( sizeof( F_COLUMN_VALUE), + (void **)&pColumnValue))) + { + goto Exit; + } + pColumnValue->pNext = NULL; + if (pLastColValue) + { + pLastColValue->pNext = pColumnValue; + } + else + { + pFirstColValue = pColumnValue; + } + pLastColValue = pColumnValue; + pColumnValue->uiColumnNum = uiColumnNum; - if (RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, - &uiBytesLeftToRead))) - { - goto Exit; - } - - // Get the value, if there is one. + // Get the data type + + if (pucPacketBody >= pucEnd) + { + rc = RC_SET( NE_SFLM_BAD_RFL_PACKET); + goto Exit; + } + + // Get the value length. + + if (RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, + &uiBytesLeftToRead))) + { + goto Exit; + } + + // Get the value, if there is one. - if ((pColumnValue->uiValueLen = uiBytesLeftToRead) != 0) + if ((pColumnValue->uiValueLen = uiBytesLeftToRead) != 0) + { + pEncDef = (pColumn->uiEncDefNum) + ? pDb->m_pDict->getEncDef( pColumn->uiEncDefNum) + : pTableEncDef; + + // If data is encrypted, allocate enough so that we can + // decrypt in place - rounding up to nearest encryption boundary + // should ensure that. + + if (!pEncDef) { - pEncDef = (pColumn->uiEncDefNum) - ? pDb->m_pDict->getEncDef( pColumn->uiEncDefNum) - : pTableEncDef; - - // If data is encrypted, allocate enough so that we can - // decrypt in place - rounding up to nearest encryption boundary - // should ensure that. + if (RC_BAD( rc = m_tmpPool.poolAlloc( uiBytesLeftToRead, + (void **)&pucValue))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = m_tmpPool.poolAlloc( getEncLen( uiBytesLeftToRead), + (void **)&pucValue))) + { + goto Exit; + } + } + pColumnValue->pucColumnValue = pucValue; + + // Now get the value. + + while (uiBytesLeftToRead) + { + + // If we have used up our current packet, get another. + + if (pucPacketBody == pucEnd) + { + if( RC_BAD( rc = getPacket( + pDb, FALSE, &uiPacketType, &pucPacketBody, &uiPacketBodyLen, + &bHitEnd))) + { + goto Exit; + } + + // Should not hit the end of the RFL here! + + if (bHitEnd) + { + rc = RC_SET( NE_SFLM_RFL_INCOMPLETE); + goto Exit; + } + + if (uiPacketType != RFL_COLUMN_DATA_PACKET) + { + rc = RC_SET( NE_SFLM_BAD_RFL_PACKET); + goto Exit; + } + pucEnd = pucPacketBody + uiPacketBodyLen; + } + + // Getting data is simple if it is not encrypted. if (!pEncDef) { - if (RC_BAD( rc = m_tmpPool.poolAlloc( uiBytesLeftToRead, - (void **)&pucValue))) + uiBytesToRead = (FLMUINT)(pucEnd - pucPacketBody); + if (uiBytesToRead > uiBytesLeftToRead) { - goto Exit; + uiBytesToRead = uiBytesLeftToRead; } + f_memcpy( pucValue, pucPacketBody, uiBytesToRead); + pucPacketBody += uiBytesToRead; + pucValue += uiBytesToRead; + uiBytesLeftToRead -= uiBytesToRead; } else { - if (RC_BAD( rc = m_tmpPool.poolAlloc( getEncLen( uiBytesLeftToRead), - (void **)&pucValue))) + + // Encrypted data should have the encrypted length, the + // length of data encrypted, an IV, and the encrypted + // data - all in the same packet. + + if (RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, + &uiEncLen))) { goto Exit; } - } - pColumnValue->pucColumnValue = pucValue; - - // Now get the value. - - while (uiBytesLeftToRead) - { - - // If we have used up our current packet, get another. - - if (pucPacketBody == pucEnd) + if (RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, + &uiBytesToRead))) { - if( RC_BAD( rc = getPacket( - pDb, FALSE, &uiPacketType, &pucPacketBody, &uiPacketBodyLen, - &bHitEnd))) - { - goto Exit; - } - - // Should not hit the end of the RFL here! - - if (bHitEnd) - { - rc = RC_SET( NE_SFLM_RFL_INCOMPLETE); - goto Exit; - } - - if (uiPacketType != RFL_COLUMN_DATA_PACKET) - { - rc = RC_SET( NE_SFLM_BAD_RFL_PACKET); - goto Exit; - } - pucEnd = pucPacketBody + uiPacketBodyLen; + goto Exit; + } + if (uiBytesToRead > uiEncLen || uiBytesToRead > uiBytesLeftToRead) + { + rc = RC_SET( NE_SFLM_BAD_RFL_PACKET); + goto Exit; + } + uiIVLen = pEncDef->pCcs->getIVLen(); + flmAssert( uiIVLen == 8 || uiIVLen == 16); + + // Make sure packet has the IV and the encrypted data. + + if (pucPacketBody + uiIVLen + uiEncLen > pucEnd) + { + rc = RC_SET( NE_SFLM_BAD_RFL_PACKET); + goto Exit; } - // Getting data is simple if it is not encrypted. + // Get the IV from the packet. - if (!pEncDef) + f_memcpy( ucIV, pucPacketBody, uiIVLen); + pucPacketBody += uiIVLen; + + // Get the encrypted data into our buffer. + + f_memcpy( pucValue, pucPacketBody, uiEncLen); + + // Decrypt the data in place. + + if (RC_BAD( rc = pDb->decryptData( pEncDef->uiEncDefNum, ucIV, + pucValue, uiEncLen, pucValue, uiEncLen))) { - uiBytesToRead = (FLMUINT)(pucEnd - pucPacketBody); - if (uiBytesToRead > uiBytesLeftToRead) - { - uiBytesToRead = uiBytesLeftToRead; - } - f_memcpy( pucValue, pucPacketBody, uiBytesToRead); - pucPacketBody += uiBytesToRead; - pucValue += uiBytesToRead; - uiBytesLeftToRead -= uiBytesToRead; - } - else - { - - // Encrypted data should have the encrypted length, the - // length of data encrypted, an IV, and the encrypted - // data - all in the same packet. - - if (RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, - &uiEncLen))) - { - goto Exit; - } - if (RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, - &uiBytesToRead))) - { - goto Exit; - } - if (uiBytesToRead > uiEncLen || uiBytesToRead > uiBytesLeftToRead) - { - rc = RC_SET( NE_SFLM_BAD_RFL_PACKET); - goto Exit; - } - uiIVLen = pEncDef->pCcs->getIVLen(); - flmAssert( uiIVLen == 8 || uiIVLen == 16); - - // Make sure packet has the IV and the encrypted data. - - if (pucPacketBody + uiIVLen + uiEncLen > pucEnd) - { - rc = RC_SET( NE_SFLM_BAD_RFL_PACKET); - goto Exit; - } - - // Get the IV from the packet. - - f_memcpy( ucIV, pucPacketBody, uiIVLen); - pucPacketBody += uiIVLen; - - // Get the encrypted data into our buffer. - - f_memcpy( pucValue, pucPacketBody, uiEncLen); - - // Decrypt the data in place. - - if (RC_BAD( rc = pDb->decryptData( pEncDef->uiEncDefNum, ucIV, - pucValue, uiEncLen, pucValue, uiEncLen))) - { - goto Exit; - } - - // Increment our value buffer only past the decrypted data. - // Increment the packet body pointer past the encrypted data. - - pucValue += uiBytesToRead; - uiBytesLeftToRead -= uiBytesToRead; - pucPacketBody += uiEncLen; + goto Exit; } + + // Increment our value buffer only past the decrypted data. + // Increment the packet body pointer past the encrypted data. + + pucValue += uiBytesToRead; + uiBytesLeftToRead -= uiBytesToRead; + pucPacketBody += uiEncLen; } } - - uiNumColumnValues--; - pColumnValue++; } } if (m_pRestoreStatus) { if( RC_BAD( rc = m_pRestoreStatus->reportInsertRow( - peAction, uiTableNum, pColValues, uiNumColumnValues))) + peAction, uiTableNum, pFirstColValue, uiNumColumnValues))) { goto Exit; } @@ -4509,7 +5324,7 @@ RCODE F_Rfl::recovInsertRow( } } - if( RC_BAD( rc = pDb->insertRow( uiTableNum, pColValues, uiNumColumnValues))) + if( RC_BAD( rc = pDb->insertRow( uiTableNum, pFirstColValue))) { goto Exit; } @@ -5843,6 +6658,57 @@ Finish_Transaction: goto Finish_Transaction; } + case RFL_CREATE_TABLE_PACKET: + { + if( RC_BAD( rc = recovCreateTable( pDb, + pucPacketBody, uiPacketBodyLen, &eAction))) + { + goto Exit; + } + + if( eAction == SFLM_RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + + break; + } + + case RFL_CREATE_INDEX_PACKET: + { + if( RC_BAD( rc = recovCreateIndex( pDb, + pucPacketBody, uiPacketBodyLen, &eAction))) + { + goto Exit; + } + + if( eAction == SFLM_RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + + break; + } + + case RFL_INDEX_SET_PACKET: + { + if( RC_BAD( rc = recovIndexSet( pDb, + pucPacketBody, uiPacketBodyLen, &eAction))) + { + goto Exit; + } + + if( eAction == SFLM_RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + + break; + } + case RFL_INSERT_ROW_PACKET: { if( RC_BAD( rc = recovInsertRow( pDb, diff --git a/sql/src/rfl.h b/sql/src/rfl.h index 1c6c8d7..6206f0f 100644 --- a/sql/src/rfl.h +++ b/sql/src/rfl.h @@ -44,8 +44,13 @@ class IXKeyCompare; #define RFL_DATA_PACKET 11 #define RFL_ROLL_OVER_DB_KEY_PACKET 12 #define RFL_ENC_DEF_KEY_PACKET 13 -#define RFL_INSERT_ROW_PACKET 14 -#define RFL_COLUMN_DATA_PACKET 15 +#define RFL_CREATE_TABLE_PACKET 14 +#define RFL_COLUMN_DEF_PACKET 15 +#define RFL_CREATE_INDEX_PACKET 16 +#define RFL_INDEX_COLUMN_DEF_PACKET 17 +#define RFL_INDEX_SET_PACKET 18 +#define RFL_INSERT_ROW_PACKET 19 +#define RFL_COLUMN_DATA_PACKET 20 #define RFL_PACKET_TYPE_MASK 0x7F @@ -201,17 +206,65 @@ public: RCODE logRollOverDbKey( F_Db * pDb); + RCODE logColumnDefs( + F_Db * pDb, + F_COLUMN_DEF * pColumnDefs); + + RCODE logCreateTable( + F_Db * pDb, + FLMUINT uiTableNum, + const char * pszTableName, + FLMUINT uiTableNameLen, + FLMUINT uiEncDefNum, + F_COLUMN_DEF * pColumnDefs); + + RCODE recovCreateTable( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction); + + RCODE logIndexColumnDefs( + F_Db * pDb, + F_INDEX_COL_DEF * pIxColDefs); + + RCODE logCreateIndex( + F_Db * pDb, + FLMUINT uiTableNum, + FLMUINT uiIndexNum, + const char * pszIndexName, + FLMUINT uiIndexNameLen, + FLMUINT uiEncDefNum, + F_INDEX_COL_DEF * pIxColDefs, + FLMUINT uiFlags); + + RCODE recovCreateIndex( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction); + + RCODE logIndexSet( + F_Db * pDb, + FLMUINT uiIndexNum, + FLMUINT64 ui64StartRowId, + FLMUINT64 ui64EndRowId); + + RCODE recovIndexSet( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction); + RCODE logColumnValues( F_Db * pDb, FLMUINT uiTableNum, - F_COLUMN_VALUE * pColumnValues, - FLMUINT uiNumColumnValues); + F_COLUMN_VALUE * pColumnValues); RCODE logInsertRow( F_Db * pDb, FLMUINT uiTableNum, - F_COLUMN_VALUE * pColumnValues, - FLMUINT uiNumColumnValues); + F_COLUMN_VALUE * pColumnValues); RCODE recovInsertRow( F_Db * pDb, diff --git a/sql/src/sqloptimize.cpp b/sql/src/sqloptimize.cpp index 6c07a1d..c656ee8 100644 --- a/sql/src/sqloptimize.cpp +++ b/sql/src/sqloptimize.cpp @@ -936,7 +936,6 @@ RCODE SQLQuery::convertOperandsToPredicates( void) SQL_VALUE * pValue; SQL_TABLE * pTable; FLMUINT uiColumnNum; - eSQLQueryOperators eOperator; FLMUINT uiOperand; SQL_SUBQUERY * pSubQuery; SQL_SUBQUERY * pNextSubQuery; @@ -966,6 +965,7 @@ RCODE SQLQuery::convertOperandsToPredicates( void) (pSQLNode->pFirstChild->eNodeType == SQL_VALUE_NODE && pSQLNode->pLastChild->eNodeType == SQL_COLUMN_NODE))) { + eSQLQueryOperators eOperator = pSQLNode->nd.op.eOperator; // Have a Column,Op,Value or Value,Op,Column. Convert to a // predicate node and merge with other predicate nodes that @@ -976,7 +976,6 @@ RCODE SQLQuery::convertOperandsToPredicates( void) pTable = pSQLNode->pFirstChild->nd.column.pTable; uiColumnNum = pSQLNode->pFirstChild->nd.column.uiColumnNum; pValue = &pSQLNode->pLastChild->nd.value; - eOperator = pSQLNode->nd.op.eOperator; } else { @@ -990,7 +989,7 @@ RCODE SQLQuery::convertOperandsToPredicates( void) { case SQL_EQ_OP: case SQL_NE_OP: - // No change. + // No change break; case SQL_LT_OP: eOperator = SQL_GE_OP; @@ -1817,8 +1816,8 @@ RCODE SQLQuery::convertToDNF( void) // create a single subquery that has a single operand. if (m_pQuery->eNodeType != SQL_OPERATOR_NODE || - m_pQuery->nd.op.eOperator != SQL_AND_OP || - m_pQuery->nd.op.eOperator != SQL_OR_OP) + (m_pQuery->nd.op.eOperator != SQL_AND_OP && + m_pQuery->nd.op.eOperator != SQL_OR_OP)) { if (RC_BAD( rc = m_pool.poolCalloc( sizeof( SQL_SUBQUERY), (void **)&m_pFirstSubQuery))) diff --git a/sql/src/sqlstatement.cpp b/sql/src/sqlstatement.cpp index 8c0dd6e..8f8acdf 100644 --- a/sql/src/sqlstatement.cpp +++ b/sql/src/sqlstatement.cpp @@ -1,4 +1,5 @@ -// Desc: This module contains routines for doing database updates +//------------------------------------------------------------------------------ +// Desc: This module contains routines for parsing and executing SQL statements. // // Tabs: 3 // @@ -35,8 +36,6 @@ SQLStatement::SQLStatement() m_pucCurrLineBuf = NULL; m_uiCurrLineBufMaxBytes = 0; m_pXml = NULL; - m_pColumnValues = &m_columnValues [0]; - m_uiColumnValueArraySize = SQL_DEFAULT_COLUMNS; resetStatement(); } @@ -52,10 +51,6 @@ SQLStatement::~SQLStatement() f_free( &m_pucCurrLineBuf); } - if (m_pColumnValues != &m_columnValues [0]) - { - f_free( &m_pColumnValues); - } m_tmpPool.poolFree(); } @@ -72,8 +67,6 @@ void SQLStatement::resetStatement( void) m_pStream = NULL; m_uiFlags = 0; m_pDb = NULL; - m_pTable = NULL; - m_uiNumColumnValues = 0; if (m_pXml) { m_pXml->Release(); @@ -356,6 +349,527 @@ 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; + + // Skip leading whitespace + + if (RC_BAD( rc = skipWhitespace( FALSE))) + { + goto Exit; + } + if (bMustHaveEqual) + { + if (!lineHasToken( "=")) + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset, + SQL_ERR_EXPECTING_EQUAL, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + 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; + + // Skip leading whitespace + + if (RC_BAD( rc = skipWhitespace( FALSE))) + { + goto Exit; + } + if (bMustHaveEqual) + { + if (!lineHasToken( "=")) + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset, + SQL_ERR_EXPECTING_EQUAL, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + 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. //------------------------------------------------------------------------------ @@ -440,20 +954,22 @@ Exit: // Desc: Parse the table name for the current statement. Make sure it is valid. //------------------------------------------------------------------------------ RCODE SQLStatement::getTableName( - FLMBOOL bMustExist) + FLMBOOL bMustExist, + char * pszTableName, + FLMUINT uiTableNameBufSize, + FLMUINT * puiTableNameLen, + F_TABLE ** ppTable) { RCODE rc = NE_SFLM_OK; - char szTableName [MAX_SQL_NAME_LEN + 1]; - FLMUINT uiTableNameLen; - if (RC_BAD( rc = getName( szTableName, sizeof( szTableName), &uiTableNameLen))) + 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( szTableName, &m_pTable, TRUE))) + if (RC_BAD( rc = m_pDb->m_pDict->getTable( pszTableName, ppTable, TRUE))) { if (rc != NE_SFLM_BAD_TABLE) { @@ -494,45 +1010,59 @@ Exit: } //------------------------------------------------------------------------------ -// Desc: Reallocate the column value array if needed. +// Desc: Parse the index name for the current statement. Make sure it is valid. //------------------------------------------------------------------------------ -RCODE SQLStatement::allocColumnValueArray( - FLMUINT uiNumColumnsNeeded) +RCODE SQLStatement::getIndexName( + FLMBOOL bMustExist, + char * pszIndexName, + FLMUINT uiIndexNameBufSize, + FLMUINT * puiIndexNameLen, + F_INDEX ** ppIndex) { - RCODE rc = NE_SFLM_OK; - - if (uiNumColumnsNeeded > m_uiColumnValueArraySize) + RCODE rc = NE_SFLM_OK; + + if (RC_BAD( rc = getName( pszIndexName, uiIndexNameBufSize, puiIndexNameLen))) { - F_COLUMN_VALUE * pNewArray; - - // Increase the array size by at least 20. - - uiNumColumnsNeeded += 20; - if (m_pColumnValues == &m_columnValues [0]) + 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) { - 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; + 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 { - if (RC_BAD( rc = f_realloc( sizeof( F_COLUMN_VALUE) * uiNumColumnsNeeded, - &m_pColumnValues))) - { - goto Exit; - } + rc = NE_SFLM_OK; } - m_uiColumnValueArraySize = uiNumColumnsNeeded; } - + 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); @@ -542,6 +1072,7 @@ Exit: // Desc: Parse a string value from the input stream. //------------------------------------------------------------------------------ RCODE SQLStatement::getStringValue( + F_COLUMN * pColumn, F_COLUMN_VALUE * pColumnValue) { RCODE rc = NE_SFLM_OK; @@ -684,6 +1215,19 @@ RCODE SQLStatement::getStringValue( 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); @@ -712,186 +1256,12 @@ 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 (RC_BAD( rc = getNumber( FALSE, &ui64Value, &bNeg, TRUE))) { - - // 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; } @@ -920,6 +1290,7 @@ Exit: // Desc: Parse a binary value from the input stream. //------------------------------------------------------------------------------ RCODE SQLStatement::getBinaryValue( + F_COLUMN * pColumn, F_COLUMN_VALUE * pColumnValue) { RCODE rc = NE_SFLM_OK; @@ -1039,6 +1410,19 @@ RCODE SQLStatement::getBinaryValue( } } + // 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) @@ -1075,14 +1459,15 @@ Exit: // type. //------------------------------------------------------------------------------ RCODE SQLStatement::getValue( + F_COLUMN * pColumn, F_COLUMN_VALUE * pColumnValue) { RCODE rc = NE_SFLM_OK; - switch (pColumnValue->eColumnDataType) + switch (pColumn->eDataTyp) { case SFLM_STRING_TYPE: - if (RC_BAD( rc = getStringValue( pColumnValue))) + if (RC_BAD( rc = getStringValue( pColumn, pColumnValue))) { goto Exit; } @@ -1094,7 +1479,7 @@ RCODE SQLStatement::getValue( } break; case SFLM_BINARY_TYPE: - if (RC_BAD( rc = getBinaryValue( pColumnValue))) + if (RC_BAD( rc = getBinaryValue( pColumn, pColumnValue))) { goto Exit; } @@ -1109,392 +1494,6 @@ 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. //------------------------------------------------------------------------------ @@ -1532,11 +1531,95 @@ RCODE SQLStatement::executeSQL( if (lineHasToken( "insert")) { - if( RC_BAD( rc = processInsertRow())) + if (RC_BAD( rc = processInsertRow())) { goto Exit; } } + else if (lineHasToken( "open")) + { + if (RC_BAD( rc = skipWhitespace( TRUE))) + { + goto Exit; + } + if (lineHasToken( "database")) + { +// visit: Need to fix this up. +// if (RC_BAD( rc = processOpenDatabase())) +// { +// goto Exit; +// } + } + else + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset, + SQL_ERR_INVALID_OPEN_OPTION, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } + } + else if (lineHasToken( "create")) + { + if (RC_BAD( rc = skipWhitespace( TRUE))) + { + goto Exit; + } + if (lineHasToken( "database")) + { + if (RC_BAD( rc = processCreateDatabase())) + { + goto Exit; + } + } + else if (lineHasToken( "table")) + { + if (RC_BAD( rc = processCreateTable())) + { + goto Exit; + } + } + else if (lineHasToken( "index")) + { + if (RC_BAD( rc = processCreateIndex( FALSE))) + { + goto Exit; + } + } + else if (lineHasToken( "unique")) + { + if (RC_BAD( rc = skipWhitespace( TRUE))) + { + goto Exit; + } + if (!lineHasToken( "index")) + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset, + SQL_ERR_EXPECTING_INDEX, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } + if (RC_BAD( rc = processCreateIndex( TRUE))) + { + goto Exit; + } + } + else + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset, + SQL_ERR_INVALID_CREATE_OPTION, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } + } bWhitespaceRequired = TRUE; } diff --git a/sql/src/sqlstatement.h b/sql/src/sqlstatement.h index 8542a11..42a2ea9 100644 --- a/sql/src/sqlstatement.h +++ b/sql/src/sqlstatement.h @@ -136,42 +136,78 @@ private: m_sqlStats.uiErrLineBytes = uiErrLineBytes; } + RCODE getUTF8String( + FLMBOOL bMustHaveEqual, + FLMBYTE * pszStr, + FLMUINT uiStrBufSize, + FLMUINT * puiStrLen); + + RCODE getNumber( + FLMBOOL bMustHaveEqual, + FLMUINT64 * pui64Num, + FLMBOOL * pbNeg, + FLMBOOL bNegAllowed); + + RCODE getBool( + FLMBOOL bMustHaveEqual, + FLMBOOL * pbBool); + + RCODE getUINT( + FLMBOOL bMustHaveEqual, + FLMUINT * puiNum); + RCODE getName( char * pszName, FLMUINT uiNameBufSize, FLMUINT * puiNameLen); RCODE getTableName( - FLMBOOL bMustExist); + FLMBOOL bMustExist, + char * pszTableName, + FLMUINT uiTableNameBufSize, + FLMUINT * puiTableNameLen, + F_TABLE ** ppTable); - RCODE allocColumnValueArray( - FLMUINT uiNumColumns); + RCODE getIndexName( + FLMBOOL bMustExist, + char * pszIndexName, + FLMUINT uiIndexNameBufSize, + FLMUINT * puiIndexNameLen, + F_INDEX ** ppIndex); RCODE getStringValue( + F_COLUMN * pColumn, F_COLUMN_VALUE * pColumnValue); RCODE getNumberValue( F_COLUMN_VALUE * pColumnValue); RCODE getBinaryValue( + F_COLUMN * pColumn, F_COLUMN_VALUE * pColumnValue); RCODE getValue( + F_COLUMN * pColumn, F_COLUMN_VALUE * pColumnValue); RCODE insertRow( void); + RCODE processCreateDatabase( void); + + RCODE getDataType( + eDataType * peDataType, + FLMUINT * puiMax); + + RCODE processCreateTable( void); + + RCODE processCreateIndex( + FLMBOOL bUnique); + RCODE processInsertRow( void); - + // Data F_Db * m_pDb; - F_TABLE * m_pTable; -#define SQL_DEFAULT_COLUMNS 20 - F_COLUMN_VALUE m_columnValues [SQL_DEFAULT_COLUMNS]; - F_COLUMN_VALUE * m_pColumnValues; - FLMUINT m_uiColumnValueArraySize; - FLMUINT m_uiNumColumnValues; IF_XML * m_pXml; FLMBYTE m_ucUngetByte; FLMBYTE * m_pucCurrLineBuf;