You've already forked mariadb-columnstore-engine
							
							
				mirror of
				https://github.com/mariadb-corporation/mariadb-columnstore-engine.git
				synced 2025-10-31 18:30:33 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			2829 lines
		
	
	
		
			95 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			2829 lines
		
	
	
		
			95 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* Copyright (C) 2014 InfiniDB, Inc.
 | |
| 
 | |
|    This program is free software; you can redistribute it and/or
 | |
|    modify it under the terms of the GNU General Public License
 | |
|    as published by the Free Software Foundation; version 2 of
 | |
|    the License.
 | |
| 
 | |
|    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, write to the Free Software
 | |
|    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 | |
|    MA 02110-1301, USA. */
 | |
| 
 | |
| //  $Id: we_fileop.cpp 4737 2013-08-14 20:45:46Z bwilkinson $
 | |
| 
 | |
| #include "config.h"
 | |
| 
 | |
| #include <unistd.h>
 | |
| #include <stdio.h>
 | |
| #include <string.h>
 | |
| #include <errno.h>
 | |
| #include <sstream>
 | |
| #include <iostream>
 | |
| #include <memory>
 | |
| #include <string>
 | |
| #include <stdexcept>
 | |
| #if defined(__FreeBSD__)
 | |
| #include <sys/param.h>
 | |
| #include <sys/mount.h>
 | |
| #elif !defined(_MSC_VER)
 | |
| #include <sys/vfs.h>
 | |
| #endif
 | |
| #include <boost/filesystem/operations.hpp>
 | |
| #include <boost/filesystem/path.hpp>
 | |
| #include <boost/scoped_array.hpp>
 | |
| using namespace std;
 | |
| 
 | |
| #include "we_fileop.h"
 | |
| #include "we_convertor.h"
 | |
| #include "we_log.h"
 | |
| #include "we_config.h"
 | |
| #include "we_stats.h"
 | |
| #include "we_simplesyslog.h"
 | |
| 
 | |
| #include "idbcompress.h"
 | |
| using namespace compress;
 | |
| 
 | |
| #include "messagelog.h"
 | |
| using namespace logging;
 | |
| 
 | |
| #include "IDBDataFile.h"
 | |
| #include "IDBFileSystem.h"
 | |
| #include "IDBPolicy.h"
 | |
| using namespace idbdatafile;
 | |
| 
 | |
| namespace WriteEngine
 | |
| {
 | |
| 
 | |
| /*static*/ boost::mutex               FileOp::m_createDbRootMutexes;
 | |
| /*static*/ boost::mutex               FileOp::m_mkdirMutex;
 | |
| /*static*/ std::map<int, boost::mutex*> FileOp::m_DbRootAddExtentMutexes;
 | |
| const int MAX_NBLOCKS = 8192; // max number of blocks written to an extent
 | |
| // in 1 call to fwrite(), during initialization
 | |
| 
 | |
| //StopWatch timer;
 | |
| 
 | |
| /**
 | |
|  * Constructor
 | |
|  */
 | |
| FileOp::FileOp(bool doAlloc) : m_compressionType(0),
 | |
|     m_transId((TxnID)INVALID_NUM), m_buffer(0)
 | |
| {
 | |
|     if (doAlloc)
 | |
|     {
 | |
|         m_buffer = new char[DEFAULT_BUFSIZ];
 | |
|         memset(m_buffer, '\0', DEFAULT_BUFSIZ);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Default Destructor
 | |
|  */
 | |
| FileOp::~FileOp()
 | |
| {
 | |
|     if (m_buffer)
 | |
|     {
 | |
|         delete [] m_buffer;
 | |
|     }
 | |
| 
 | |
|     m_buffer = 0;
 | |
| }
 | |
| 
 | |
| /***********************************************************
 | |
|  * DESCRIPTION:
 | |
|  *    Close a file
 | |
|  * PARAMETERS:
 | |
|  *    pFile - file handle
 | |
|  * RETURN:
 | |
|  *    none
 | |
|  ***********************************************************/
 | |
| void FileOp::closeFile( IDBDataFile* pFile ) const
 | |
| {
 | |
|     delete pFile;
 | |
| }
 | |
| 
 | |
| /***********************************************************
 | |
|  * DESCRIPTION:
 | |
|  *    Create directory
 | |
|  *    Function uses mutex lock to prevent thread contention trying to create
 | |
|  *    2 subdirectories in the same directory at the same time.
 | |
|  * PARAMETERS:
 | |
|  *    dirName - directory name
 | |
|  *    mode - create mode
 | |
|  * RETURN:
 | |
|  *    NO_ERROR if success, otherwise if fail
 | |
|  ***********************************************************/
 | |
| int FileOp::createDir( const char* dirName, mode_t mode ) const
 | |
| {
 | |
|     boost::mutex::scoped_lock lk(m_mkdirMutex);
 | |
|     int rc = IDBPolicy::mkdir( dirName );
 | |
| 
 | |
|     if ( rc != 0 )
 | |
|     {
 | |
|         int errRc = errno;
 | |
| 
 | |
|         if (errRc == EEXIST)
 | |
|             return NO_ERROR; // ignore "File exists" error
 | |
| 
 | |
|         if ( getLogger() )
 | |
|         {
 | |
|             std::ostringstream oss;
 | |
|             std::string errnoMsg;
 | |
|             Convertor::mapErrnoToString(errRc, errnoMsg);
 | |
|             oss << "Error creating directory " << dirName << "; err-" <<
 | |
|                 errRc << "; " << errnoMsg;
 | |
|             getLogger()->logMsg( oss.str(), ERR_DIR_CREATE, MSGLVL_ERROR );
 | |
|         }
 | |
| 
 | |
|         return ERR_DIR_CREATE;
 | |
|     }
 | |
| 
 | |
|     return NO_ERROR;
 | |
| }
 | |
| 
 | |
| /***********************************************************
 | |
|  * DESCRIPTION:
 | |
|  *    Create the "first" segment file for a column with a fixed file size
 | |
|  *    Note: the file is created in binary mode
 | |
|  * PARAMETERS:
 | |
|  *    fileName - file name with complete path
 | |
|  *    numOfBlock - the total number of blocks to be initialized (written out)
 | |
|  *    compressionType - Compression Type
 | |
|  *    emptyVal - empty value used to initialize column values
 | |
|  *    width    - width of column in bytes
 | |
|  *    dbRoot   - DBRoot of column file we are creating
 | |
|  * RETURN:
 | |
|  *    NO_ERROR if success
 | |
|  *    ERR_FILE_EXIST if file exists
 | |
|  *    ERR_FILE_CREATE if can not create the file
 | |
|  ***********************************************************/
 | |
| int FileOp::createFile( const char* fileName, int numOfBlock,
 | |
|                         uint64_t emptyVal, int width,
 | |
|                         uint16_t dbRoot )
 | |
| {
 | |
|     IDBDataFile* pFile =
 | |
|         IDBDataFile::open(
 | |
|             IDBPolicy::getType( fileName, IDBPolicy::WRITEENG ),
 | |
|             fileName,
 | |
|             "w+b",
 | |
|             IDBDataFile::USE_VBUF,
 | |
|             width);
 | |
|     int rc = 0;
 | |
| 
 | |
|     if ( pFile != NULL )
 | |
|     {
 | |
| 
 | |
|         // Initialize the contents of the extent.
 | |
|         if (m_compressionType)
 | |
|         {
 | |
|             rc = initAbbrevCompColumnExtent( pFile,
 | |
|                                              dbRoot,
 | |
|                                              numOfBlock,
 | |
|                                              emptyVal,
 | |
|                                              width );
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             rc = initColumnExtent( pFile,
 | |
|                                    dbRoot,
 | |
|                                    numOfBlock,
 | |
|                                    emptyVal,
 | |
|                                    width,
 | |
|                                    true,    // new file
 | |
|                                    false,   // don't expand; add new extent
 | |
|                                    true );  // add abbreviated extent
 | |
|         }
 | |
| 
 | |
|         closeFile( pFile );
 | |
|     }
 | |
|     else
 | |
|         return ERR_FILE_CREATE;
 | |
| 
 | |
|     return rc;
 | |
| }
 | |
| 
 | |
| /***********************************************************
 | |
|  * DESCRIPTION:
 | |
|  *    Create the "first" segment file for a column with a fixed file size
 | |
|  *    Note: the file is created in binary mode
 | |
|  * PARAMETERS:
 | |
|  *    fid      - OID of the column file to be created
 | |
|  *    allocSize (out) - number of blocks allocated to the first extent
 | |
|  *    dbRoot   - DBRoot where file is to be located
 | |
|  *    partition- Starting partition number for segment file path
 | |
|  *    compressionType - Compression type
 | |
|  *    colDataType - the column data type
 | |
|  *    emptyVal - designated "empty" value for this OID
 | |
|  *    width    - width of column in bytes
 | |
|  * RETURN:
 | |
|  *    NO_ERROR if success
 | |
|  *    ERR_FILE_EXIST if file exists
 | |
|  *    ERR_FILE_CREATE if can not create the file
 | |
|  ***********************************************************/
 | |
| int FileOp::createFile(FID fid,
 | |
|                        int&     allocSize,
 | |
|                        uint16_t dbRoot,
 | |
|                        uint32_t partition,
 | |
|                        execplan::CalpontSystemCatalog::ColDataType colDataType,
 | |
|                        uint64_t  emptyVal,
 | |
|                        int      width)
 | |
| {
 | |
|     //std::cout << "Creating file oid: " << fid <<
 | |
|     //    "; compress: " << m_compressionType << std::endl;
 | |
|     char  fileName[FILE_NAME_SIZE];
 | |
|     int   rc;
 | |
| 
 | |
|     uint16_t segment = 0; // should always be 0 when starting a new column
 | |
|     RETURN_ON_ERROR( ( rc = oid2FileName( fid, fileName, true,
 | |
|                                           dbRoot, partition, segment ) ) );
 | |
| 
 | |
|     //@Bug 3196
 | |
|     if ( exists( fileName ) )
 | |
|         return ERR_FILE_EXIST;
 | |
| 
 | |
|     // allocatColExtent() treats dbRoot and partition as in/out
 | |
|     // arguments, so we need to pass in a non-const variable.
 | |
|     uint16_t dbRootx    = dbRoot;
 | |
|     uint32_t partitionx = partition;
 | |
| 
 | |
|     // Since we are creating a new column OID, we know partition
 | |
|     // and segment are 0, so we ignore their output values.
 | |
| //timer.start( "allocateColExtent" );
 | |
| 
 | |
|     BRM::LBID_t startLbid;
 | |
|     uint32_t startBlock;
 | |
|     RETURN_ON_ERROR( BRMWrapper::getInstance()->allocateColExtentExactFile(
 | |
|                          (const OID)fid, (uint32_t)width, dbRootx, partitionx, segment, colDataType,
 | |
|                          startLbid, allocSize, startBlock) );
 | |
| 
 | |
|     // We allocate a full extent from BRM, but only write an abbreviated 256K
 | |
|     // rows to disk for 1st extent, to conserve disk usage for small tables.
 | |
|     // One exception here is if we have rolled off partition 0, and we are
 | |
|     // adding a column to an existing table, then we are adding a column
 | |
|     // whose first partition is not 0.  In this case, we know we are not
 | |
|     // dealing with a small table, so we init a full extent for 1st extent.
 | |
|     int totalSize = 0;
 | |
| 
 | |
|     if (partition == 0)
 | |
|         totalSize = (INITIAL_EXTENT_ROWS_TO_DISK / BYTE_PER_BLOCK) * width;
 | |
|     else
 | |
|         totalSize = allocSize; // full extent if starting partition > 0
 | |
| 
 | |
|     // Note we can't pass full file name to isDiskSpaceAvail() because the
 | |
|     // file does not exist yet, but passing DBRoot directory should suffice.
 | |
|     if ( !isDiskSpaceAvail(Config::getDBRootByNum(dbRoot), totalSize) )
 | |
|     {
 | |
|         return ERR_FILE_DISK_SPACE;
 | |
|     }
 | |
| 
 | |
| //timer.stop( "allocateColExtent" );
 | |
| 
 | |
|     return createFile( fileName, totalSize, emptyVal, width, dbRoot );
 | |
| }
 | |
| 
 | |
| /***********************************************************
 | |
|  * DESCRIPTION:
 | |
|  *    Delete a file
 | |
|  * PARAMETERS:
 | |
|  *    fileName - file name with complete path
 | |
|  * RETURN:
 | |
|  *    NO_ERROR if success
 | |
|  *    ERR_FILE_NOT_EXIST if file does not exist
 | |
|  *    ERR_FILE_DELETE if can not delete a file
 | |
|  ***********************************************************/
 | |
| int FileOp::deleteFile( const char* fileName ) const
 | |
| {
 | |
|     if ( !exists( fileName ) )
 | |
|         return ERR_FILE_NOT_EXIST;
 | |
| 
 | |
|     return ( IDBPolicy::remove( fileName ) == -1 ) ? ERR_FILE_DELETE : NO_ERROR;
 | |
| }
 | |
| 
 | |
| /***********************************************************
 | |
|  * DESCRIPTION:
 | |
|  *    Deletes all the segment or dictionary store files associated with the
 | |
|  *    specified fid.
 | |
|  * PARAMETERS:
 | |
|  *    fid - OID of the column being deleted.
 | |
|  * RETURN:
 | |
|  *    NO_ERROR if success
 | |
|  *    ERR_DM_CONVERT_OID if error occurs converting OID to file name
 | |
|  ***********************************************************/
 | |
| int FileOp::deleteFile( FID fid ) const
 | |
| {
 | |
|     char tempFileName[FILE_NAME_SIZE];
 | |
|     char oidDirName  [FILE_NAME_SIZE];
 | |
|     char dbDir       [MAX_DB_DIR_LEVEL][MAX_DB_DIR_NAME_SIZE];
 | |
| 
 | |
|     RETURN_ON_ERROR((Convertor::oid2FileName(
 | |
|                          fid, tempFileName, dbDir, 0, 0)));
 | |
|     sprintf(oidDirName, "%s/%s/%s/%s",
 | |
|             dbDir[0], dbDir[1], dbDir[2], dbDir[3]);
 | |
|     //std::cout << "Deleting files for OID " << fid <<
 | |
|     //             "; dirpath: " << oidDirName << std::endl;
 | |
|     //need check return code.
 | |
|     RETURN_ON_ERROR(BRMWrapper::getInstance()->deleteOid(fid));
 | |
| 
 | |
|     std::vector<std::string> dbRootPathList;
 | |
|     Config::getDBRootPathList( dbRootPathList );
 | |
| 
 | |
|     for (unsigned i = 0; i < dbRootPathList.size(); i++)
 | |
|     {
 | |
|         char rootOidDirName[FILE_NAME_SIZE];
 | |
|         sprintf(rootOidDirName, "%s/%s", dbRootPathList[i].c_str(), oidDirName);
 | |
| 
 | |
|         if ( IDBPolicy::remove( rootOidDirName ) != 0 )
 | |
|         {
 | |
|             ostringstream oss;
 | |
|             oss << "Unable to remove " << rootOidDirName;
 | |
|             throw std::runtime_error( oss.str() );
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return NO_ERROR;
 | |
| }
 | |
| 
 | |
| /***********************************************************
 | |
|  * DESCRIPTION:
 | |
|  *    Deletes all the segment or dictionary store files associated with the
 | |
|  *    specified fid.
 | |
|  * PARAMETERS:
 | |
|  *    fid - OIDs of the column/dictionary being deleted.
 | |
|  * RETURN:
 | |
|  *    NO_ERROR if success
 | |
|  *    ERR_DM_CONVERT_OID if error occurs converting OID to file name
 | |
|  ***********************************************************/
 | |
| int FileOp::deleteFiles( const std::vector<int32_t>& fids ) const
 | |
| {
 | |
|     char tempFileName[FILE_NAME_SIZE];
 | |
|     char oidDirName  [FILE_NAME_SIZE];
 | |
|     char dbDir       [MAX_DB_DIR_LEVEL][MAX_DB_DIR_NAME_SIZE];
 | |
|     std::vector<std::string> dbRootPathList;
 | |
|     Config::getDBRootPathList( dbRootPathList );
 | |
| 
 | |
|     for ( unsigned n = 0; n < fids.size(); n++ )
 | |
|     {
 | |
|         RETURN_ON_ERROR((Convertor::oid2FileName(
 | |
|                              fids[n], tempFileName, dbDir, 0, 0)));
 | |
|         sprintf(oidDirName, "%s/%s/%s/%s",
 | |
|                 dbDir[0], dbDir[1], dbDir[2], dbDir[3]);
 | |
|         //std::cout << "Deleting files for OID " << fid <<
 | |
|         //             "; dirpath: " << oidDirName << std::endl;
 | |
| 
 | |
|         for (unsigned i = 0; i < dbRootPathList.size(); i++)
 | |
|         {
 | |
|             char rootOidDirName[FILE_NAME_SIZE];
 | |
|             sprintf(rootOidDirName, "%s/%s", dbRootPathList[i].c_str(),
 | |
|                     oidDirName);
 | |
| 
 | |
|             if ( IDBPolicy::remove( rootOidDirName ) != 0 )
 | |
|             {
 | |
|                 ostringstream oss;
 | |
|                 oss << "Unable to remove " << rootOidDirName;
 | |
|                 throw std::runtime_error( oss.str() );
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return NO_ERROR;
 | |
| }
 | |
| 
 | |
| /***********************************************************
 | |
|  * DESCRIPTION:
 | |
|  *    Deletes all the segment or dictionary store files associated with the
 | |
|  *    specified fid and partition.
 | |
|  * PARAMETERS:
 | |
|  *    fids - OIDs of the column/dictionary being deleted.
 | |
|  *    partition - the partition number
 | |
|  * RETURN:
 | |
|  *    NO_ERROR if success
 | |
|  *    ERR_DM_CONVERT_OID if error occurs converting OID to file name
 | |
|  ***********************************************************/
 | |
| int FileOp::deletePartitions( const std::vector<OID>& fids,
 | |
|                               const std::vector<BRM::PartitionInfo>& partitions ) const
 | |
| {
 | |
|     char tempFileName[FILE_NAME_SIZE];
 | |
|     char oidDirName  [FILE_NAME_SIZE];
 | |
|     char dbDir       [MAX_DB_DIR_LEVEL][MAX_DB_DIR_NAME_SIZE];
 | |
|     char rootOidDirName[FILE_NAME_SIZE];
 | |
|     char partitionDirName[FILE_NAME_SIZE];
 | |
| 
 | |
|     for (uint32_t i = 0; i < partitions.size(); i++)
 | |
|     {
 | |
|         RETURN_ON_ERROR((Convertor::oid2FileName(
 | |
|                              partitions[i].oid, tempFileName, dbDir,
 | |
|                              partitions[i].lp.pp, partitions[i].lp.seg)));
 | |
|         sprintf(oidDirName, "%s/%s/%s/%s/%s",
 | |
|                 dbDir[0], dbDir[1], dbDir[2], dbDir[3], dbDir[4]);
 | |
|         // config expects dbroot starting from 0
 | |
|         std::string rt( Config::getDBRootByNum(partitions[i].lp.dbroot) );
 | |
|         sprintf(rootOidDirName, "%s/%s",
 | |
|                 rt.c_str(), tempFileName);
 | |
|         sprintf(partitionDirName, "%s/%s",
 | |
|                 rt.c_str(), oidDirName);
 | |
| 
 | |
|         if ( IDBPolicy::remove( rootOidDirName ) != 0 )
 | |
|         {
 | |
|             ostringstream oss;
 | |
|             oss << "Unable to remove " << rootOidDirName;
 | |
|             throw std::runtime_error( oss.str() );
 | |
|         }
 | |
| 
 | |
|         list<string> dircontents;
 | |
| 
 | |
|         if ( IDBPolicy::listDirectory( partitionDirName, dircontents ) == 0 )
 | |
|         {
 | |
|             // the directory exists, now check if empty
 | |
|             if ( dircontents.size() == 0 )
 | |
|             {
 | |
|                 // empty directory
 | |
|                 if ( IDBPolicy::remove( partitionDirName ) != 0 )
 | |
|                 {
 | |
|                     ostringstream oss;
 | |
|                     oss << "Unable to remove " << rootOidDirName;
 | |
|                     throw std::runtime_error( oss.str() );
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return NO_ERROR;
 | |
| }
 | |
| 
 | |
| /***********************************************************
 | |
|  * DESCRIPTION:
 | |
|  *    Deletes the specified db segment file.
 | |
|  * PARAMETERS:
 | |
|  *    fid       - column OID of file to be deleted.
 | |
|  *    dbRoot    - DBRoot associated with segment file
 | |
|  *    partition - partition number of associated segment file
 | |
|  *    segment   - segment number of associated segment file
 | |
|  * RETURN:
 | |
|  *    NO_ERROR if success
 | |
|  ***********************************************************/
 | |
| int FileOp::deleteFile( FID fid, uint16_t dbRoot,
 | |
|                         uint32_t partition, uint16_t segment ) const
 | |
| {
 | |
|     char fileName[FILE_NAME_SIZE];
 | |
| 
 | |
|     RETURN_ON_ERROR( getFileName( fid, fileName,
 | |
|                                   dbRoot, partition, segment) );
 | |
| 
 | |
|     return ( deleteFile( fileName ) );
 | |
| }
 | |
| 
 | |
| /***********************************************************
 | |
|  * DESCRIPTION:
 | |
|  *    Check whether a file exists or not
 | |
|  * PARAMETERS:
 | |
|  *    fileName - file name with complete path
 | |
|  * RETURN:
 | |
|  *    true if exists, false otherwise
 | |
|  ***********************************************************/
 | |
| bool FileOp::exists( const char* fileName ) const
 | |
| {
 | |
|     return IDBPolicy::exists( fileName );
 | |
| }
 | |
| 
 | |
| /***********************************************************
 | |
|  * DESCRIPTION:
 | |
|  *    Check whether a file exists or not
 | |
|  * PARAMETERS:
 | |
|  *    fid       - OID of file to be checked
 | |
|  *    dbRoot    - DBRoot associated with segment file
 | |
|  *    partition - partition number of associated segment file
 | |
|  *    segment   - segment number of associated segment file
 | |
|  * RETURN:
 | |
|  *    true if exists, false otherwise
 | |
|  ***********************************************************/
 | |
| bool FileOp::exists( FID fid, uint16_t dbRoot,
 | |
|                      uint32_t partition, uint16_t segment ) const
 | |
| {
 | |
|     char fileName[FILE_NAME_SIZE];
 | |
| 
 | |
|     if (getFileName(fid, fileName, dbRoot, partition,
 | |
|                     segment) != NO_ERROR)
 | |
|         return false;
 | |
| 
 | |
|     return exists( fileName );
 | |
| }
 | |
| 
 | |
| /***********************************************************
 | |
|  * DESCRIPTION:
 | |
|  *    Check whether an OID directory exists or not
 | |
|  * PARAMETERS:
 | |
|  *    fid - column or dictionary store OID
 | |
|  * RETURN:
 | |
|  *    true if exists, false otherwise
 | |
|  ***********************************************************/
 | |
| bool FileOp::existsOIDDir( FID fid ) const
 | |
| {
 | |
|     char fileName[FILE_NAME_SIZE];
 | |
| 
 | |
|     if (oid2DirName( fid, fileName ) != NO_ERROR)
 | |
|     {
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     return exists( fileName );
 | |
| }
 | |
| 
 | |
| /***********************************************************
 | |
|  * DESCRIPTION:
 | |
|  *    Adds an extent to the specified column OID and DBRoot.
 | |
|  *    Function uses ExtentMap to add the extent and determine which
 | |
|  *    specific column segment file the extent is to be added to. If
 | |
|  *    the applicable column segment file does not exist, it is created.
 | |
|  *    If this is the very first file for the specified DBRoot, then the
 | |
|  *    partition and segment number must be specified, else the selected
 | |
|  *    partition and segment numbers are returned.
 | |
|  * PARAMETERS:
 | |
|  *    oid       - OID of the column to be extended
 | |
|  *    emptyVal  - Empty value to be used for oid
 | |
|  *    width     - Width of the column (in bytes)
 | |
|  *    hwm       - The HWM (or fbo) of the column segment file where the new
 | |
|  *                extent begins.
 | |
|  *    startLbid - The starting LBID for the new extent
 | |
|  *    allocSize - Number of blocks allocated to the extent.
 | |
|  *    dbRoot    - The DBRoot of the file with the new extent.
 | |
|  *    partition - The partition number of the file with the new extent.
 | |
|  *    segment   - The segment number of the file with the new extent.
 | |
|  *    segFile   - The name of the relevant column segment file.
 | |
|  *    pFile     - IDBDataFile ptr to the file where the extent is added.
 | |
|  *    newFile   - Indicates if the extent was added to a new or existing file
 | |
|  *    hdrs      - Contents of the headers if file is compressed.
 | |
|  * RETURN:
 | |
|  *    NO_ERROR if success
 | |
|  *    else the applicable error code is returned
 | |
|  ***********************************************************/
 | |
| int FileOp::extendFile(
 | |
|     OID          oid,
 | |
|     uint64_t     emptyVal,
 | |
|     int          width,
 | |
|     HWM          hwm,
 | |
|     BRM::LBID_t  startLbid,
 | |
|     int          allocSize,
 | |
|     uint16_t     dbRoot,
 | |
|     uint32_t     partition,
 | |
|     uint16_t     segment,
 | |
|     std::string& segFile,
 | |
|     IDBDataFile*& pFile,
 | |
|     bool&        newFile,
 | |
|     char*        hdrs)
 | |
| {
 | |
|     int rc = NO_ERROR;
 | |
|     pFile = 0;
 | |
|     segFile.clear();
 | |
|     newFile = false;
 | |
|     char fileName[FILE_NAME_SIZE];
 | |
| 
 | |
|     // If starting hwm or fbo is 0 then this is the first extent of a new file,
 | |
|     // else we are adding an extent to an existing segment file
 | |
|     if (hwm > 0) // db segment file should exist
 | |
|     {
 | |
|         RETURN_ON_ERROR( oid2FileName(oid, fileName, false,
 | |
|                                       dbRoot, partition, segment) );
 | |
|         segFile = fileName;
 | |
| 
 | |
|         if (!exists(fileName))
 | |
|         {
 | |
|             ostringstream oss;
 | |
|             oss << "oid: " << oid << " with path " << segFile;
 | |
|             logging::Message::Args args;
 | |
|             args.add("File not found ");
 | |
|             args.add(oss.str());
 | |
|             args.add("");
 | |
|             args.add("");
 | |
|             SimpleSysLog::instance()->logMsg(args,
 | |
|                                              logging::LOG_TYPE_ERROR,
 | |
|                                              logging::M0001);
 | |
|             return ERR_FILE_NOT_EXIST;
 | |
|         }
 | |
| 
 | |
|         pFile = openFile( oid, dbRoot, partition, segment, segFile, "r+b" );//old file
 | |
| 
 | |
|         if (pFile == 0)
 | |
|         {
 | |
|             ostringstream oss;
 | |
|             oss << "oid: " << oid << " with path " << segFile;
 | |
|             logging::Message::Args args;
 | |
|             args.add("Error opening file ");
 | |
|             args.add(oss.str());
 | |
|             args.add("");
 | |
|             args.add("");
 | |
|             SimpleSysLog::instance()->logMsg(args,
 | |
|                                              logging::LOG_TYPE_ERROR,
 | |
|                                              logging::M0001);
 | |
|             return ERR_FILE_OPEN;
 | |
|         }
 | |
| 
 | |
|         if ( isDebug(DEBUG_1) && getLogger() )
 | |
|         {
 | |
|             std::ostringstream oss;
 | |
|             oss << "Opening existing column file (extendFile)"  <<
 | |
|                 ": OID-"    << oid       <<
 | |
|                 "; DBRoot-" << dbRoot    <<
 | |
|                 "; part-"   << partition <<
 | |
|                 "; seg-"    << segment   <<
 | |
|                 "; LBID-"   << startLbid <<
 | |
|                 "; hwm-"    << hwm       <<
 | |
|                 "; file-"   << segFile;
 | |
|             getLogger()->logMsg( oss.str(), MSGLVL_INFO2 );
 | |
|         }
 | |
| 
 | |
|         // @bug 5349: check that new extent's fbo is not past current EOF
 | |
|         if (m_compressionType)
 | |
|         {
 | |
|             char hdrsIn[ compress::IDBCompressInterface::HDR_BUF_LEN * 2 ];
 | |
|             RETURN_ON_ERROR( readHeaders(pFile, hdrsIn) );
 | |
| 
 | |
|             IDBCompressInterface compressor;
 | |
|             unsigned int ptrCount   = compressor.getPtrCount(hdrsIn);
 | |
|             unsigned int chunkIndex = 0;
 | |
|             unsigned int blockOffsetWithinChunk = 0;
 | |
|             compressor.locateBlock((hwm - 1), chunkIndex, blockOffsetWithinChunk);
 | |
| 
 | |
|             //std::ostringstream oss1;
 | |
|             //oss1 << "Extending compressed column file"<<
 | |
|             //   ": OID-"    << oid       <<
 | |
|             //   "; LBID-"   << startLbid <<
 | |
|             //   "; fbo-"    << hwm       <<
 | |
|             //   "; file-"   << segFile   <<
 | |
|             //   "; chkidx-" << chunkIndex<<
 | |
|             //   "; numPtrs-"<< ptrCount;
 | |
|             //getLogger()->logMsg( oss1.str(), MSGLVL_INFO2 );
 | |
| 
 | |
|             if (chunkIndex >= ptrCount)
 | |
|             {
 | |
|                 ostringstream oss;
 | |
|                 oss << "oid: " << oid << " with path " << segFile <<
 | |
|                     "; new extent fbo " << hwm << "; number of "
 | |
|                     "compressed chunks " << ptrCount <<
 | |
|                     "; chunkIndex " << chunkIndex;
 | |
|                 logging::Message::Args args;
 | |
|                 args.add("compressed");
 | |
|                 args.add(oss.str());
 | |
|                 SimpleSysLog::instance()->logMsg(args,
 | |
|                                                  logging::LOG_TYPE_ERROR,
 | |
|                                                  logging::M0103);
 | |
| 
 | |
|                 // Expand the partial extent to full with emptyVal
 | |
|                 // Since fillCompColumnExtentEmptyChunks() messes with the
 | |
|                 // file on disk, we need to close it and reopen after or
 | |
|                 // the cache isn't updated.
 | |
|                 if ((pFile))
 | |
|                     closeFile( pFile );
 | |
| 
 | |
|                 pFile = NULL;
 | |
|                 string failedTask;  // For return error message, if any.
 | |
|                 rc = FileOp::fillCompColumnExtentEmptyChunks(oid,
 | |
|                         width,
 | |
|                         emptyVal,
 | |
|                         dbRoot,
 | |
|                         partition,
 | |
|                         segment,
 | |
|                         hwm,
 | |
|                         segFile,
 | |
|                         failedTask);
 | |
| 
 | |
|                 if (rc != NO_ERROR)
 | |
|                 {
 | |
|                     if (getLogger())
 | |
|                     {
 | |
|                         std::ostringstream oss;
 | |
|                         oss << "FileOp::extendFile: error padding partial compressed extent for " <<
 | |
|                             "column OID-" << oid       <<
 | |
|                             "; DBRoot-"   << dbRoot    <<
 | |
|                             "; part-"     << partition <<
 | |
|                             "; seg-"      << segment   <<
 | |
|                             "; hwm-"      << hwm       <<
 | |
|                             " " << failedTask.c_str();
 | |
|                         getLogger()->logMsg( oss.str(), rc, MSGLVL_CRITICAL );
 | |
|                     }
 | |
| 
 | |
|                     return rc;
 | |
|                 }
 | |
| 
 | |
|                 pFile = openFile( oid, dbRoot, partition, segment, segFile, "r+b" ); // modified file
 | |
|             }
 | |
| 
 | |
|             // Get the latest file header for the caller. If a partial extent was filled out,
 | |
|             // this will be different than when we first read the headers.
 | |
|             if (hdrs)
 | |
|             {
 | |
|                 RETURN_ON_ERROR( readHeaders(pFile, hdrs) );
 | |
|             }
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             long long fileSize;
 | |
|             RETURN_ON_ERROR( getFileSize(pFile, fileSize) );
 | |
|             long long calculatedFileSize = ((long long)hwm) * BYTE_PER_BLOCK;
 | |
| 
 | |
|             //std::ostringstream oss2;
 | |
|             //oss2 << "Extending uncompressed column file"<<
 | |
|             //   ": OID-"    << oid       <<
 | |
|             //   "; LBID-"   << startLbid <<
 | |
|             //   "; fbo-"    << hwm       <<
 | |
|             //   "; file-"   << segFile   <<
 | |
|             //   "; filesize-"<<fileSize;
 | |
|             //getLogger()->logMsg( oss2.str(), MSGLVL_INFO2 );
 | |
| 
 | |
|             if (calculatedFileSize > fileSize)
 | |
|             {
 | |
|                 ostringstream oss;
 | |
|                 oss << "oid: " << oid << " with path " << segFile <<
 | |
|                     "; new extent fbo " << hwm << "; file size (bytes) " <<
 | |
|                     fileSize;
 | |
|                 logging::Message::Args args;
 | |
|                 args.add("uncompressed");
 | |
|                 args.add(oss.str());
 | |
|                 SimpleSysLog::instance()->logMsg(args,
 | |
|                                                  logging::LOG_TYPE_ERROR,
 | |
|                                                  logging::M0103);
 | |
|                 // Expand the partial extent to full with emptyVal
 | |
|                 // This generally won't ever happen, as uncompressed files
 | |
|                 // are created with full extents.
 | |
|                 rc = FileOp::expandAbbrevColumnExtent( pFile, dbRoot,
 | |
|                                                        emptyVal, width);
 | |
| 
 | |
|                 if (rc != NO_ERROR)
 | |
|                 {
 | |
|                     if (getLogger())
 | |
|                     {
 | |
|                         std::ostringstream oss;
 | |
|                         oss << "FileOp::extendFile: error padding partial uncompressed extent for " <<
 | |
|                             "column OID-" << oid       <<
 | |
|                             "; DBRoot-"   << dbRoot    <<
 | |
|                             "; part-"     << partition <<
 | |
|                             "; seg-"      << segment   <<
 | |
|                             "; hwm-"      << hwm;
 | |
|                         getLogger()->logMsg( oss.str(), rc, MSGLVL_CRITICAL );
 | |
|                     }
 | |
| 
 | |
|                     return rc;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     else // db segment file should not exist
 | |
|     {
 | |
|         RETURN_ON_ERROR( oid2FileName(oid, fileName, true,
 | |
|                                       dbRoot, partition, segment) );
 | |
|         segFile = fileName;
 | |
| 
 | |
|         // if obsolete file exists, "w+b" will truncate and write over
 | |
|         pFile = openFile( fileName, "w+b" );//new file
 | |
| 
 | |
|         if (pFile == 0)
 | |
|             return ERR_FILE_CREATE;
 | |
| 
 | |
|         newFile = true;
 | |
| 
 | |
|         if ( isDebug(DEBUG_1) && getLogger() )
 | |
|         {
 | |
|             std::ostringstream oss;
 | |
|             oss << "Opening new column file" <<
 | |
|                 ": OID-"    << oid       <<
 | |
|                 "; DBRoot-" << dbRoot    <<
 | |
|                 "; part-"   << partition <<
 | |
|                 "; seg-"    << segment   <<
 | |
|                 "; LBID-"   << startLbid <<
 | |
|                 "; hwm-"    << hwm       <<
 | |
|                 "; file-"   << segFile;
 | |
|             getLogger()->logMsg( oss.str(), MSGLVL_INFO2 );
 | |
|         }
 | |
| 
 | |
|         if ((m_compressionType) && (hdrs))
 | |
|         {
 | |
|             IDBCompressInterface compressor;
 | |
|             compressor.initHdr(hdrs, m_compressionType);
 | |
|         }
 | |
|     }
 | |
| 
 | |
| #ifdef _MSC_VER
 | |
| 
 | |
|     //Need to call the win version with a dir, not a file
 | |
|     if (!isDiskSpaceAvail(Config::getDBRootByNum(dbRoot), allocSize))
 | |
| #else
 | |
|     if ( !isDiskSpaceAvail(segFile, allocSize) )
 | |
| #endif
 | |
|     {
 | |
|         return ERR_FILE_DISK_SPACE;
 | |
|     }
 | |
| 
 | |
|     // We set to EOF just before we start adding the blocks for the new extent.
 | |
|     // At one time, I considered changing this to seek to the HWM block, but
 | |
|     // with compressed files, this is murky; do I find and seek to the chunk
 | |
|     // containing the HWM block?  So I left as-is for now, seeking to EOF.
 | |
|     rc = setFileOffset(pFile, 0, SEEK_END);
 | |
| 
 | |
|     if (rc != NO_ERROR)
 | |
|         return rc;
 | |
| 
 | |
|     // Initialize the contents of the extent.
 | |
|     rc = initColumnExtent( pFile,
 | |
|                            dbRoot,
 | |
|                            allocSize,
 | |
|                            emptyVal,
 | |
|                            width,
 | |
|                            newFile, // new or existing file
 | |
|                            false,   // don't expand; new extent
 | |
|                            false ); // add full (not abbreviated) extent
 | |
| 
 | |
|     return rc;
 | |
| }
 | |
| 
 | |
| /***********************************************************
 | |
|  * DESCRIPTION:
 | |
|  *    Add an extent to the exact segment file specified by
 | |
|  *    the designated OID, DBRoot, partition, and segment.
 | |
|  * PARAMETERS:
 | |
|  *    oid       - OID of the column to be extended
 | |
|  *    emptyVal  - Empty value to be used for oid
 | |
|  *    width     - Width of the column (in bytes)
 | |
|  *    allocSize - Number of blocks allocated to the extent.
 | |
|  *    dbRoot    - The DBRoot of the file with the new extent.
 | |
|  *    partition - The partition number of the file with the new extent.
 | |
|  *    segment   - The segment number of the file with the new extent.
 | |
|  *    segFile   - The name of the relevant column segment file.
 | |
|  *    startLbid - The starting LBID for the new extent
 | |
|  *    newFile   - Indicates if the extent was added to a new or existing file
 | |
|  *    hdrs      - Contents of the headers if file is compressed.
 | |
|  * RETURN:
 | |
|  *    none
 | |
|  ***********************************************************/
 | |
| int FileOp::addExtentExactFile(
 | |
|     OID          oid,
 | |
|     uint64_t     emptyVal,
 | |
|     int          width,
 | |
|     int&         allocSize,
 | |
|     uint16_t     dbRoot,
 | |
|     uint32_t     partition,
 | |
|     uint16_t     segment,
 | |
|     execplan::CalpontSystemCatalog::ColDataType colDataType,
 | |
|     std::string& segFile,
 | |
|     BRM::LBID_t& startLbid,
 | |
|     bool&        newFile,
 | |
|     char*        hdrs)
 | |
| {
 | |
|     int rc = NO_ERROR;
 | |
|     IDBDataFile* pFile = 0;
 | |
|     segFile.clear();
 | |
|     newFile = false;
 | |
|     HWM         hwm;
 | |
| 
 | |
|     // Allocate the new extent in the ExtentMap
 | |
|     RETURN_ON_ERROR( BRMWrapper::getInstance()->allocateColExtentExactFile(
 | |
|                          oid, width, dbRoot, partition, segment, colDataType, startLbid, allocSize, hwm));
 | |
| 
 | |
|     // Determine the existence of the "next" segment file, and either open
 | |
|     // or create the segment file accordingly.
 | |
|     if (exists(oid, dbRoot, partition, segment))
 | |
|     {
 | |
|         pFile = openFile( oid, dbRoot, partition, segment,
 | |
|                           segFile, "r+b" );//old file
 | |
| 
 | |
|         if (pFile == 0)
 | |
|         {
 | |
|             ostringstream oss;
 | |
|             oss << "oid: " << oid << " with path " << segFile;
 | |
|             logging::Message::Args args;
 | |
|             args.add("Error opening file ");
 | |
|             args.add(oss.str());
 | |
|             args.add("");
 | |
|             args.add("");
 | |
|             SimpleSysLog::instance()->logMsg(args,
 | |
|                                              logging::LOG_TYPE_ERROR,
 | |
|                                              logging::M0001);
 | |
|             return ERR_FILE_OPEN;
 | |
|         }
 | |
| 
 | |
|         if ( isDebug(DEBUG_1) && getLogger() )
 | |
|         {
 | |
|             std::ostringstream oss;
 | |
|             oss << "Opening existing column file"  <<
 | |
|                 ": OID-"    << oid       <<
 | |
|                 "; DBRoot-" << dbRoot    <<
 | |
|                 "; part-"   << partition <<
 | |
|                 "; seg-"    << segment   <<
 | |
|                 "; LBID-"   << startLbid <<
 | |
|                 "; hwm-"    << hwm       <<
 | |
|                 "; file-"   << segFile;
 | |
|             getLogger()->logMsg( oss.str(), MSGLVL_INFO2 );
 | |
|         }
 | |
| 
 | |
|         if ((m_compressionType) && (hdrs))
 | |
|         {
 | |
|             rc = readHeaders(pFile, hdrs);
 | |
| 
 | |
|             if (rc != NO_ERROR)
 | |
|                 return rc;
 | |
|         }
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         char fileName[FILE_NAME_SIZE];
 | |
|         RETURN_ON_ERROR( oid2FileName(oid, fileName, true,
 | |
|                                       dbRoot, partition, segment) );
 | |
|         segFile = fileName;
 | |
| 
 | |
|         pFile = openFile( fileName, "w+b" );//new file
 | |
| 
 | |
|         if (pFile == 0)
 | |
|             return ERR_FILE_CREATE;
 | |
| 
 | |
|         newFile = true;
 | |
| 
 | |
|         if ( isDebug(DEBUG_1) && getLogger() )
 | |
|         {
 | |
|             std::ostringstream oss;
 | |
|             oss << "Opening new column file" <<
 | |
|                 ": OID-"    << oid       <<
 | |
|                 "; DBRoot-" << dbRoot    <<
 | |
|                 "; part-"   << partition <<
 | |
|                 "; seg-"    << segment   <<
 | |
|                 "; LBID-"   << startLbid <<
 | |
|                 "; hwm-"    << hwm       <<
 | |
|                 "; file-"   << segFile;
 | |
|             getLogger()->logMsg( oss.str(), MSGLVL_INFO2 );
 | |
|         }
 | |
| 
 | |
|         if ((m_compressionType) && (hdrs))
 | |
|         {
 | |
|             IDBCompressInterface compressor;
 | |
|             compressor.initHdr(hdrs, m_compressionType);
 | |
|         }
 | |
|     }
 | |
| 
 | |
| #ifdef _MSC_VER
 | |
| 
 | |
|     //Need to call the win version with a dir, not a file
 | |
|     if (!isDiskSpaceAvail(Config::getDBRootByNum(dbRoot), allocSize))
 | |
| #else
 | |
|     if ( !isDiskSpaceAvail(segFile, allocSize) )
 | |
| #endif
 | |
|     {
 | |
|         return ERR_FILE_DISK_SPACE;
 | |
|     }
 | |
| 
 | |
|     // We set to EOF just before we start adding the blocks for the new extent.
 | |
|     // At one time, I considered changing this to seek to the HWM block, but
 | |
|     // with compressed files, this is murky; do I find and seek to the chunk
 | |
|     // containing the HWM block?  So I left as-is for now, seeking to EOF.
 | |
|     rc = setFileOffset(pFile, 0, SEEK_END);
 | |
| 
 | |
|     if (rc != NO_ERROR)
 | |
|         return rc;
 | |
| 
 | |
|     // Initialize the contents of the extent.
 | |
|     rc = initColumnExtent( pFile,
 | |
|                            dbRoot,
 | |
|                            allocSize,
 | |
|                            emptyVal,
 | |
|                            width,
 | |
|                            newFile, // new or existing file
 | |
|                            false,   // don't expand; new extent
 | |
|                            false ); // add full (not abbreviated) extent
 | |
| 
 | |
|     closeFile( pFile );
 | |
|     return rc;
 | |
| }
 | |
| 
 | |
| /***********************************************************
 | |
|  * DESCRIPTION:
 | |
|  *    Write out (initialize) an extent in a column file.
 | |
|  *    A mutex is used for each DBRoot, to prevent contention between
 | |
|  *    threads, because if multiple threads are creating extents on
 | |
|  *    the same DBRoot at the same time, the extents can become
 | |
|  *    fragmented.  It is best to only create one extent at a time
 | |
|  *    on each DBRoot.
 | |
|  *    This function can be used to initialize an entirely new extent, or
 | |
|  *    to finish initializing an extent that has already been started.
 | |
|  *    nBlocks controls how many 8192-byte blocks are to be written out.
 | |
|  * PARAMETERS:
 | |
|  *    pFile   (in) - IDBDataFile* of column segment file to be written to
 | |
|  *    dbRoot  (in) - DBRoot of pFile
 | |
|  *    nBlocks (in) - number of blocks to be written for an extent
 | |
|  *    emptyVal(in) - empty value to be used for column data values
 | |
|  *    width   (in) - width of the applicable column
 | |
|  *    bNewFile(in) - are we adding an extent to a new file, in which case
 | |
|  *                   headers will be included "if" it is a compressed file.
 | |
|  *    bExpandExtent (in) - Expand existing extent, or initialize a new one
 | |
|  *    bAbbrevExtent(in) - if creating new extent, is it an abbreviated extent
 | |
|  * RETURN:
 | |
|  *    returns ERR_FILE_WRITE if an error occurs,
 | |
|  *    else returns NO_ERROR.
 | |
|  ***********************************************************/
 | |
| int FileOp::initColumnExtent(
 | |
|     IDBDataFile* pFile,
 | |
|     uint16_t dbRoot,
 | |
|     int      nBlocks,
 | |
|     uint64_t emptyVal,
 | |
|     int      width,
 | |
|     bool     bNewFile,
 | |
|     bool     bExpandExtent,
 | |
|     bool     bAbbrevExtent )
 | |
| {
 | |
|     if ((bNewFile) && (m_compressionType))
 | |
|     {
 | |
|         char hdrs[IDBCompressInterface::HDR_BUF_LEN * 2];
 | |
|         IDBCompressInterface compressor;
 | |
|         compressor.initHdr(hdrs, m_compressionType);
 | |
| 
 | |
|         if (bAbbrevExtent)
 | |
|             compressor.setBlockCount(hdrs, nBlocks);
 | |
| 
 | |
|         RETURN_ON_ERROR(writeHeaders(pFile, hdrs));
 | |
|     }
 | |
| 
 | |
|     // @bug5769 Don't initialize extents or truncate db files on HDFS
 | |
|     if (idbdatafile::IDBPolicy::useHdfs())
 | |
|     {
 | |
|         //@Bug 3219. update the compression header after the extent is expanded.
 | |
|         if ((!bNewFile) && (m_compressionType) && (bExpandExtent))
 | |
|         {
 | |
|             updateColumnExtent(pFile, nBlocks);
 | |
|         }
 | |
| 
 | |
|         // @bug 2378. Synchronize here to avoid write buffer pile up too much,
 | |
|         // which could cause controllernode to timeout later when it needs to
 | |
|         // save a snapshot.
 | |
|         pFile->flush();
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         // Create vector of mutexes used to serialize extent access per DBRoot
 | |
|         initDbRootExtentMutexes( );
 | |
| 
 | |
|         // Determine the number of blocks in each call to fwrite(), and the
 | |
|         // number of fwrite() calls to make, based on this.  In other words,
 | |
|         // we put a cap on the "writeSize" so that we don't allocate and write
 | |
|         // an entire extent at once for the 64M row extents.  If we are
 | |
|         // expanding an abbreviated 64M extent, we may not have an even
 | |
|         // multiple of MAX_NBLOCKS to write; remWriteSize is the number of
 | |
|         // blocks above and beyond loopCount*MAX_NBLOCKS.
 | |
|         int writeSize = nBlocks * BYTE_PER_BLOCK; // 1M and 8M row extent size
 | |
|         int loopCount = 1;
 | |
|         int remWriteSize = 0;
 | |
| 
 | |
|         if (nBlocks > MAX_NBLOCKS)                // 64M row extent size
 | |
|         {
 | |
|             writeSize = MAX_NBLOCKS * BYTE_PER_BLOCK;
 | |
|             loopCount = nBlocks / MAX_NBLOCKS;
 | |
|             remWriteSize = nBlocks - (loopCount * MAX_NBLOCKS);
 | |
|         }
 | |
| 
 | |
|         // Allocate a buffer, initialize it, and use it to create the extent
 | |
|         idbassert(dbRoot > 0);
 | |
| #ifdef PROFILE
 | |
| 
 | |
|         if (bExpandExtent)
 | |
|             Stats::startParseEvent(WE_STATS_WAIT_TO_EXPAND_COL_EXTENT);
 | |
|         else
 | |
|             Stats::startParseEvent(WE_STATS_WAIT_TO_CREATE_COL_EXTENT);
 | |
| 
 | |
| #endif
 | |
|         boost::mutex::scoped_lock lk(*m_DbRootAddExtentMutexes[dbRoot]);
 | |
| #ifdef PROFILE
 | |
| 
 | |
|         if (bExpandExtent)
 | |
|             Stats::stopParseEvent(WE_STATS_WAIT_TO_EXPAND_COL_EXTENT);
 | |
|         else
 | |
|             Stats::stopParseEvent(WE_STATS_WAIT_TO_CREATE_COL_EXTENT);
 | |
| 
 | |
|         Stats::startParseEvent(WE_STATS_INIT_COL_EXTENT);
 | |
| #endif
 | |
| 
 | |
|         // Allocate buffer, and store in scoped_array to insure it's deletion.
 | |
|         // Create scope {...} to manage deletion of writeBuf.
 | |
|         {
 | |
|             unsigned char* writeBuf = new unsigned char[writeSize];
 | |
|             boost::scoped_array<unsigned char> writeBufPtr( writeBuf );
 | |
| 
 | |
|             setEmptyBuf( writeBuf, writeSize, emptyVal, width );
 | |
| 
 | |
| #ifdef PROFILE
 | |
|             Stats::stopParseEvent(WE_STATS_INIT_COL_EXTENT);
 | |
| 
 | |
|             if (bExpandExtent)
 | |
|                 Stats::startParseEvent(WE_STATS_EXPAND_COL_EXTENT);
 | |
|             else
 | |
|                 Stats::startParseEvent(WE_STATS_CREATE_COL_EXTENT);
 | |
| 
 | |
| #endif
 | |
| 
 | |
|             //std::ostringstream oss;
 | |
|             //oss << "initColExtent: width-" << width <<
 | |
|             //"; loopCount-" << loopCount <<
 | |
|             //"; writeSize-" << writeSize;
 | |
|             //std::cout << oss.str() << std::endl;
 | |
|             if (remWriteSize > 0)
 | |
|             {
 | |
|                 if ( pFile->write( writeBuf, remWriteSize ) != remWriteSize )
 | |
|                 {
 | |
|                     return ERR_FILE_WRITE;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             for (int j = 0; j < loopCount; j++)
 | |
|             {
 | |
|                 if ( pFile->write( writeBuf, writeSize ) != writeSize )
 | |
|                 {
 | |
|                     return ERR_FILE_WRITE;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         //@Bug 3219. update the compression header after the extent is expanded.
 | |
|         if ((!bNewFile) && (m_compressionType) && (bExpandExtent))
 | |
|         {
 | |
|             updateColumnExtent(pFile, nBlocks);
 | |
|         }
 | |
| 
 | |
|         // @bug 2378. Synchronize here to avoid write buffer pile up too much,
 | |
|         // which could cause controllernode to timeout later when it needs to
 | |
|         // save a snapshot.
 | |
|         pFile->flush();
 | |
| 
 | |
| #ifdef PROFILE
 | |
| 
 | |
|         if (bExpandExtent)
 | |
|             Stats::stopParseEvent(WE_STATS_EXPAND_COL_EXTENT);
 | |
|         else
 | |
|             Stats::stopParseEvent(WE_STATS_CREATE_COL_EXTENT);
 | |
| 
 | |
| #endif
 | |
|     }
 | |
| 
 | |
|     return NO_ERROR;
 | |
| }
 | |
| 
 | |
| /***********************************************************
 | |
|  * DESCRIPTION:
 | |
|  *    Write (initialize) an abbreviated compressed extent in a column file.
 | |
|  *    nBlocks controls how many 8192-byte blocks are to be written out.
 | |
|  * PARAMETERS:
 | |
|  *    pFile   (in) - IDBDataFile* of column segment file to be written to
 | |
|  *    dbRoot  (in) - DBRoot of pFile
 | |
|  *    nBlocks (in) - number of blocks to be written for an extent
 | |
|  *    emptyVal(in) - empty value to be used for column data values
 | |
|  *    width   (in) - width of the applicable column
 | |
|  * RETURN:
 | |
|  *    returns ERR_FILE_WRITE or ERR_FILE_SEEK if an error occurs,
 | |
|  *    else returns NO_ERROR.
 | |
|  ***********************************************************/
 | |
| int FileOp::initAbbrevCompColumnExtent(
 | |
|     IDBDataFile* pFile,
 | |
|     uint16_t dbRoot,
 | |
|     int      nBlocks,
 | |
|     uint64_t emptyVal,
 | |
|     int      width)
 | |
| {
 | |
|     // Reserve disk space for full abbreviated extent
 | |
|     int rc = initColumnExtent( pFile,
 | |
|                                dbRoot,
 | |
|                                nBlocks,
 | |
|                                emptyVal,
 | |
|                                width,
 | |
|                                true,   // new file
 | |
|                                false,  // don't expand; add new extent
 | |
|                                true ); // add abbreviated extent
 | |
| 
 | |
|     if (rc != NO_ERROR)
 | |
|     {
 | |
|         return rc;
 | |
|     }
 | |
| 
 | |
| #ifdef PROFILE
 | |
|     Stats::startParseEvent(WE_STATS_COMPRESS_COL_INIT_ABBREV_EXT);
 | |
| #endif
 | |
| 
 | |
|     char hdrs[IDBCompressInterface::HDR_BUF_LEN * 2];
 | |
|     rc = writeInitialCompColumnChunk( pFile,
 | |
|                                       nBlocks,
 | |
|                                       INITIAL_EXTENT_ROWS_TO_DISK,
 | |
|                                       emptyVal,
 | |
|                                       width,
 | |
|                                       hdrs );
 | |
| 
 | |
|     if (rc != NO_ERROR)
 | |
|     {
 | |
|         return rc;
 | |
|     }
 | |
| 
 | |
| #ifdef PROFILE
 | |
|     Stats::stopParseEvent(WE_STATS_COMPRESS_COL_INIT_ABBREV_EXT);
 | |
| #endif
 | |
| 
 | |
|     return NO_ERROR;
 | |
| }
 | |
| 
 | |
| /***********************************************************
 | |
|  * DESCRIPTION:
 | |
|  *    Write (initialize) the first extent in a compressed db file.
 | |
|  * PARAMETERS:
 | |
|  *    pFile    - IDBDataFile* of column segment file to be written to
 | |
|  *    nBlocksAllocated - number of blocks allocated to the extent; should be
 | |
|  *        enough blocks for a full extent, unless it's the abbreviated extent
 | |
|  *    nRows    - number of rows to initialize, or write out to the file
 | |
|  *    emptyVal - empty value to be used for column data values
 | |
|  *    width    - width of the applicable column (in bytes)
 | |
|  *    hdrs     - (in/out) chunk pointer headers
 | |
|  * RETURN:
 | |
|  *    returns NO_ERROR on success.
 | |
|  ***********************************************************/
 | |
| int FileOp::writeInitialCompColumnChunk(
 | |
|     IDBDataFile* pFile,
 | |
|     int      nBlocksAllocated,
 | |
|     int      nRows,
 | |
|     uint64_t emptyVal,
 | |
|     int      width,
 | |
|     char*    hdrs)
 | |
| {
 | |
|     const int INPUT_BUFFER_SIZE     = nRows * width;
 | |
|     char* toBeCompressedInput       = new char[INPUT_BUFFER_SIZE];
 | |
|     unsigned int userPaddingBytes   = Config::getNumCompressedPadBlks() *
 | |
|                                       BYTE_PER_BLOCK;
 | |
|     const int OUTPUT_BUFFER_SIZE    = IDBCompressInterface::maxCompressedSize(INPUT_BUFFER_SIZE) +
 | |
|                                       userPaddingBytes;
 | |
|     unsigned char* compressedOutput = new unsigned char[OUTPUT_BUFFER_SIZE];
 | |
|     unsigned int outputLen          = OUTPUT_BUFFER_SIZE;
 | |
|     boost::scoped_array<char> toBeCompressedInputPtr( toBeCompressedInput );
 | |
|     boost::scoped_array<unsigned char> compressedOutputPtr(compressedOutput);
 | |
| 
 | |
|     setEmptyBuf( (unsigned char*)toBeCompressedInput,
 | |
|                  INPUT_BUFFER_SIZE, emptyVal, width);
 | |
| 
 | |
|     // Compress an initialized abbreviated extent
 | |
|     IDBCompressInterface compressor( userPaddingBytes );
 | |
|     int rc = compressor.compressBlock(toBeCompressedInput,
 | |
|                                       INPUT_BUFFER_SIZE, compressedOutput, outputLen );
 | |
| 
 | |
|     if (rc != 0)
 | |
|     {
 | |
|         return ERR_COMP_COMPRESS;
 | |
|     }
 | |
| 
 | |
|     // Round up the compressed chunk size
 | |
|     rc = compressor.padCompressedChunks( compressedOutput,
 | |
|                                          outputLen, OUTPUT_BUFFER_SIZE );
 | |
| 
 | |
|     if (rc != 0)
 | |
|     {
 | |
|         return ERR_COMP_PAD_DATA;
 | |
|     }
 | |
| 
 | |
| //  std::cout << "Uncompressed rowCount: " << nRows <<
 | |
| //      "; colWidth: "      << width   <<
 | |
| //      "; uncompByteCnt: " << INPUT_BUFFER_SIZE <<
 | |
| //      "; blkAllocCnt: "   << nBlocksAllocated  <<
 | |
| //      "; compressedByteCnt: "  << outputLen << std::endl;
 | |
| 
 | |
|     compressor.initHdr(hdrs, m_compressionType);
 | |
|     compressor.setBlockCount(hdrs, nBlocksAllocated);
 | |
| 
 | |
|     // Store compression pointers in the header
 | |
|     std::vector<uint64_t> ptrs;
 | |
|     ptrs.push_back( IDBCompressInterface::HDR_BUF_LEN * 2 );
 | |
|     ptrs.push_back( outputLen + (IDBCompressInterface::HDR_BUF_LEN * 2) );
 | |
|     compressor.storePtrs(ptrs, hdrs);
 | |
| 
 | |
|     RETURN_ON_ERROR( writeHeaders(pFile, hdrs) );
 | |
| 
 | |
|     // Write the compressed data
 | |
|     if ( pFile->write( compressedOutput, outputLen ) != outputLen )
 | |
|     {
 | |
|         return ERR_FILE_WRITE;
 | |
|     }
 | |
| 
 | |
|     return NO_ERROR;
 | |
| }
 | |
| 
 | |
| /***********************************************************
 | |
|  * DESCRIPTION:
 | |
|  *    Fill specified compressed extent with empty value chunks.
 | |
|  * PARAMETERS:
 | |
|  *    oid        - OID for relevant column
 | |
|  *    colWidth   - width in bytes of this column
 | |
|  *    emptyVal   - empty value to be used in filling empty chunks
 | |
|  *    dbRoot     - DBRoot of extent to be filled
 | |
|  *    partition  - partition of extent to be filled
 | |
|  *    segment    - segment file number of extent to be filled
 | |
|  *    hwm        - proposed new HWM of filled in extent
 | |
|  *    segFile    - (out) name of updated segment file
 | |
|  *    failedTask - (out) if error occurs, this is the task that failed
 | |
|  * RETURN:
 | |
|  *    returns NO_ERROR if success.
 | |
|  ***********************************************************/
 | |
| int FileOp::fillCompColumnExtentEmptyChunks(OID oid,
 | |
|         int          colWidth,
 | |
|         uint64_t     emptyVal,
 | |
|         uint16_t     dbRoot,
 | |
|         uint32_t     partition,
 | |
|         uint16_t     segment,
 | |
|         HWM          hwm,
 | |
|         std::string& segFile,
 | |
|         std::string& failedTask)
 | |
| {
 | |
|     int rc = NO_ERROR;
 | |
|     segFile.clear();
 | |
|     failedTask.clear();
 | |
| 
 | |
|     // Open the file and read the headers with the compression chunk pointers
 | |
|     // @bug 5572 - HDFS usage: incorporate *.tmp file backup flag
 | |
|     IDBDataFile* pFile = openFile( oid, dbRoot, partition, segment, segFile,
 | |
|                                    "r+b", DEFAULT_COLSIZ, true );
 | |
| 
 | |
|     if (!pFile)
 | |
|     {
 | |
|         failedTask = "Opening file";
 | |
|         ostringstream oss;
 | |
|         oss << "oid: " << oid << " with path " << segFile;
 | |
|         logging::Message::Args args;
 | |
|         args.add("Error opening file ");
 | |
|         args.add(oss.str());
 | |
|         args.add("");
 | |
|         args.add("");
 | |
|         SimpleSysLog::instance()->logMsg(args,
 | |
|                                          logging::LOG_TYPE_ERROR,
 | |
|                                          logging::M0001);
 | |
|         return ERR_FILE_OPEN;
 | |
|     }
 | |
| 
 | |
|     char hdrs[ IDBCompressInterface::HDR_BUF_LEN * 2 ];
 | |
|     rc = readHeaders( pFile, hdrs );
 | |
| 
 | |
|     if (rc != NO_ERROR)
 | |
|     {
 | |
|         failedTask = "Reading headers";
 | |
|         closeFile ( pFile );
 | |
|         return rc;
 | |
|     }
 | |
| 
 | |
|     int userPadBytes = Config::getNumCompressedPadBlks() * BYTE_PER_BLOCK;
 | |
|     IDBCompressInterface compressor( userPadBytes );
 | |
|     CompChunkPtrList chunkPtrs;
 | |
|     int rcComp = compressor.getPtrList( hdrs, chunkPtrs );
 | |
| 
 | |
|     if (rcComp != 0)
 | |
|     {
 | |
|         failedTask = "Getting header ptrs";
 | |
|         closeFile ( pFile );
 | |
|         return ERR_COMP_PARSE_HDRS;
 | |
|     }
 | |
| 
 | |
|     // Nothing to do if the proposed HWM is < the current block count
 | |
|     uint64_t blkCount = compressor.getBlockCount(hdrs);
 | |
| 
 | |
|     if (blkCount > (hwm + 1))
 | |
|     {
 | |
|         closeFile ( pFile );
 | |
|         return NO_ERROR;
 | |
|     }
 | |
| 
 | |
|     const unsigned int ROWS_PER_EXTENT   =
 | |
|         BRMWrapper::getInstance()->getInstance()->getExtentRows();
 | |
|     const unsigned int ROWS_PER_CHUNK    =
 | |
|         IDBCompressInterface::UNCOMPRESSED_INBUF_LEN / colWidth;
 | |
|     const unsigned int CHUNKS_PER_EXTENT = ROWS_PER_EXTENT / ROWS_PER_CHUNK;
 | |
| 
 | |
|     // If this is an abbreviated extent, we first expand to a full extent
 | |
|     // @bug 4340 - support moving the DBRoot with a single abbrev extent
 | |
|     if ( (chunkPtrs.size() == 1) &&
 | |
|             ((blkCount * BYTE_PER_BLOCK) ==
 | |
|              (uint64_t)(INITIAL_EXTENT_ROWS_TO_DISK * colWidth)) )
 | |
|     {
 | |
|         if ( getLogger() )
 | |
|         {
 | |
|             std::ostringstream oss;
 | |
|             oss << "Converting abbreviated partial extent to full extent for" <<
 | |
|                 ": OID-"    << oid       <<
 | |
|                 "; DBRoot-" << dbRoot    <<
 | |
|                 "; part-"   << partition <<
 | |
|                 "; seg-"    << segment   <<
 | |
|                 "; file-"   << segFile   <<
 | |
|                 "; wid-"    << colWidth  <<
 | |
|                 "; oldBlkCnt-" << blkCount <<
 | |
|                 "; newBlkCnt-" <<
 | |
|                 ((ROWS_PER_EXTENT * colWidth) / BYTE_PER_BLOCK);
 | |
|             getLogger()->logMsg( oss.str(), MSGLVL_INFO2 );
 | |
|         }
 | |
| 
 | |
|         off64_t   endHdrsOffset = pFile->tell();
 | |
|         rc = expandAbbrevColumnExtent( pFile, dbRoot, emptyVal, colWidth );
 | |
| 
 | |
|         if (rc != NO_ERROR)
 | |
|         {
 | |
|             failedTask = "Expanding abbreviated extent";
 | |
|             closeFile ( pFile );
 | |
|             return rc;
 | |
|         }
 | |
| 
 | |
|         CompChunkPtr chunkOutPtr;
 | |
|         rc = expandAbbrevColumnChunk( pFile, emptyVal, colWidth,
 | |
|                                       chunkPtrs[0], chunkOutPtr );
 | |
| 
 | |
|         if (rc != NO_ERROR)
 | |
|         {
 | |
|             failedTask = "Expanding abbreviated chunk";
 | |
|             closeFile ( pFile );
 | |
|             return rc;
 | |
|         }
 | |
| 
 | |
|         chunkPtrs[0] = chunkOutPtr; // update chunkPtrs with new chunk size
 | |
| 
 | |
|         rc = setFileOffset( pFile, endHdrsOffset );
 | |
| 
 | |
|         if (rc != NO_ERROR)
 | |
|         {
 | |
|             failedTask = "Positioning file to end of headers";
 | |
|             closeFile ( pFile );
 | |
|             return rc;
 | |
|         }
 | |
| 
 | |
|         // Update block count to reflect a full extent
 | |
|         blkCount = (ROWS_PER_EXTENT * colWidth) / BYTE_PER_BLOCK;
 | |
|         compressor.setBlockCount( hdrs, blkCount );
 | |
|     }
 | |
| 
 | |
|     // Calculate the number of empty chunks we need to add to fill this extent
 | |
|     unsigned numChunksToFill = 0;
 | |
|     ldiv_t ldivResult = ldiv(chunkPtrs.size(), CHUNKS_PER_EXTENT);
 | |
| 
 | |
|     if (ldivResult.rem != 0)
 | |
|     {
 | |
|         numChunksToFill = CHUNKS_PER_EXTENT - ldivResult.rem;
 | |
|     }
 | |
| 
 | |
| #if 0
 | |
|     std::cout << "Number of allocated blocks:     " <<
 | |
|               compressor.getBlockCount(hdrs) << std::endl;
 | |
|     std::cout << "Pointer Header Size (in bytes): " <<
 | |
|               (compressor.getHdrSize(hdrs) -
 | |
|                IDBCompressInterface::HDR_BUF_LEN) << std::endl;
 | |
|     std::cout << "Chunk Pointers (offset,length): " << std::endl;
 | |
| 
 | |
|     for (unsigned k = 0; k < chunkPtrs.size(); k++)
 | |
|     {
 | |
|         std::cout << "  " << k << ". " << chunkPtrs[k].first <<
 | |
|                   " , " << chunkPtrs[k].second << std::endl;
 | |
|     }
 | |
| 
 | |
|     std::cout << std::endl;
 | |
|     std::cout << "Number of chunks to fill in: " << numChunksToFill <<
 | |
|               std::endl << std::endl;
 | |
| #endif
 | |
| 
 | |
|     off64_t   endOffset = 0;
 | |
| 
 | |
|     // Fill in or add necessary remaining empty chunks
 | |
|     if (numChunksToFill > 0)
 | |
|     {
 | |
|         const int IN_BUF_LEN = IDBCompressInterface::UNCOMPRESSED_INBUF_LEN;
 | |
|         const int OUT_BUF_LEN = IDBCompressInterface::maxCompressedSize(IN_BUF_LEN) + userPadBytes;
 | |
| 
 | |
|         // Allocate buffer, and store in scoped_array to insure it's deletion.
 | |
|         // Create scope {...} to manage deletion of buffers
 | |
|         {
 | |
|             char*          toBeCompressedBuf = new char         [ IN_BUF_LEN  ];
 | |
|             unsigned char* compressedBuf     = new unsigned char[ OUT_BUF_LEN ];
 | |
|             boost::scoped_array<char> toBeCompressedInputPtr(toBeCompressedBuf);
 | |
|             boost::scoped_array<unsigned char>
 | |
|             compressedOutputPtr(compressedBuf);
 | |
| 
 | |
|             // Compress and then pad the compressed chunk
 | |
|             setEmptyBuf( (unsigned char*)toBeCompressedBuf,
 | |
|                          IN_BUF_LEN, emptyVal, colWidth );
 | |
|             unsigned int outputLen = OUT_BUF_LEN;
 | |
|             rcComp = compressor.compressBlock( toBeCompressedBuf,
 | |
|                                                IN_BUF_LEN, compressedBuf, outputLen );
 | |
| 
 | |
|             if (rcComp != 0)
 | |
|             {
 | |
|                 failedTask = "Compressing chunk";
 | |
|                 closeFile ( pFile );
 | |
|                 return ERR_COMP_COMPRESS;
 | |
|             }
 | |
| 
 | |
|             toBeCompressedInputPtr.reset(); // release memory
 | |
| 
 | |
|             rcComp = compressor.padCompressedChunks( compressedBuf,
 | |
|                      outputLen, OUT_BUF_LEN );
 | |
| 
 | |
|             if (rcComp != 0)
 | |
|             {
 | |
|                 failedTask = "Padding compressed chunk";
 | |
|                 closeFile ( pFile );
 | |
|                 return ERR_COMP_PAD_DATA;
 | |
|             }
 | |
| 
 | |
|             // Position file to write empty chunks; default to end of headers
 | |
|             // in case there are no chunks listed in the header
 | |
|             off64_t   startOffset = pFile->tell();
 | |
| 
 | |
|             if (chunkPtrs.size() > 0)
 | |
|             {
 | |
|                 startOffset = chunkPtrs[chunkPtrs.size() - 1].first +
 | |
|                               chunkPtrs[chunkPtrs.size() - 1].second;
 | |
|                 rc = setFileOffset( pFile, startOffset );
 | |
| 
 | |
|                 if (rc != NO_ERROR)
 | |
|                 {
 | |
|                     failedTask = "Positioning file to begin filling chunks";
 | |
|                     closeFile ( pFile );
 | |
|                     return rc;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             // Write chunks needed to fill out the current extent, add chunk ptr
 | |
|             for (unsigned k = 0; k < numChunksToFill; k++)
 | |
|             {
 | |
|                 rc = writeFile( pFile,
 | |
|                                 (unsigned char*)compressedBuf,
 | |
|                                 outputLen );
 | |
| 
 | |
|                 if (rc != NO_ERROR)
 | |
|                 {
 | |
|                     failedTask = "Writing  a chunk";
 | |
|                     closeFile ( pFile );
 | |
|                     return rc;
 | |
|                 }
 | |
| 
 | |
|                 CompChunkPtr compChunk( startOffset, outputLen );
 | |
|                 chunkPtrs.push_back( compChunk );
 | |
|                 startOffset = pFile->tell();
 | |
|             }
 | |
|         } // end of scope for boost scoped array pointers
 | |
| 
 | |
|         endOffset = pFile->tell();
 | |
| 
 | |
|         // Update the compressed chunk pointers in the header
 | |
|         std::vector<uint64_t> ptrs;
 | |
| 
 | |
|         for (unsigned i = 0; i < chunkPtrs.size(); i++)
 | |
|         {
 | |
|             ptrs.push_back( chunkPtrs[i].first );
 | |
|         }
 | |
| 
 | |
|         ptrs.push_back( chunkPtrs[chunkPtrs.size() - 1].first +
 | |
|                         chunkPtrs[chunkPtrs.size() - 1].second );
 | |
|         compressor.storePtrs( ptrs, hdrs );
 | |
| 
 | |
|         rc = writeHeaders( pFile, hdrs );
 | |
| 
 | |
|         if (rc != NO_ERROR)
 | |
|         {
 | |
|             failedTask = "Writing headers";
 | |
|             closeFile ( pFile );
 | |
|             return rc;
 | |
|         }
 | |
|     }  // end of "numChunksToFill > 0"
 | |
|     else
 | |
|     {
 | |
|         // if no chunks to add, then set endOffset to truncate the db file
 | |
|         // strictly based on the chunks that are already in the file
 | |
|         if (chunkPtrs.size() > 0)
 | |
|         {
 | |
|             endOffset = chunkPtrs[chunkPtrs.size() - 1].first +
 | |
|                         chunkPtrs[chunkPtrs.size() - 1].second;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // Truncate the file to release unused space for the extent we just filled
 | |
|     if (endOffset > 0)
 | |
|     {
 | |
|         rc = truncateFile(pFile, endOffset);
 | |
| 
 | |
|         if (rc != NO_ERROR)
 | |
|         {
 | |
|             failedTask = "Truncating file";
 | |
|             closeFile ( pFile );
 | |
|             return rc;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     closeFile ( pFile );
 | |
| 
 | |
|     return NO_ERROR;
 | |
| }
 | |
| 
 | |
| /***********************************************************
 | |
|  * DESCRIPTION:
 | |
|  *    Expand first chunk in pFile from an abbreviated chunk for an abbreviated
 | |
|  *    extent to a full compressed chunk for a full extent.
 | |
|  * PARAMETERS:
 | |
|  *    pFile      - file to be updated
 | |
|  *    colWidth   - width in bytes of this column
 | |
|  *    emptyVal   - empty value to be used in filling empty chunks
 | |
|  *    chunkInPtr - chunk pointer referencing first (abbrev) chunk
 | |
|  *    chunkOutPtr- (out) updated chunk ptr referencing first (full) chunk
 | |
|  * RETURN:
 | |
|  *    returns NO_ERROR if success.
 | |
|  ***********************************************************/
 | |
| int FileOp::expandAbbrevColumnChunk(
 | |
|     IDBDataFile* pFile,
 | |
|     uint64_t emptyVal,
 | |
|     int   colWidth,
 | |
|     const CompChunkPtr& chunkInPtr,
 | |
|     CompChunkPtr& chunkOutPtr )
 | |
| {
 | |
|     int userPadBytes = Config::getNumCompressedPadBlks() * BYTE_PER_BLOCK;
 | |
|     const int IN_BUF_LEN = IDBCompressInterface::UNCOMPRESSED_INBUF_LEN;
 | |
|     const int OUT_BUF_LEN = IDBCompressInterface::maxCompressedSize(IN_BUF_LEN) + userPadBytes;
 | |
| 
 | |
|     char* toBeCompressedBuf = new char[ IN_BUF_LEN  ];
 | |
|     boost::scoped_array<char> toBeCompressedPtr(toBeCompressedBuf);
 | |
| 
 | |
|     setEmptyBuf( (unsigned char*)toBeCompressedBuf,
 | |
|                  IN_BUF_LEN, emptyVal, colWidth );
 | |
| 
 | |
|     RETURN_ON_ERROR( setFileOffset(pFile, chunkInPtr.first, SEEK_SET) );
 | |
| 
 | |
|     char* compressedInBuf = new char[ chunkInPtr.second ];
 | |
|     boost::scoped_array<char> compressedInBufPtr(compressedInBuf);
 | |
|     RETURN_ON_ERROR( readFile(pFile, (unsigned char*)compressedInBuf,
 | |
|                               chunkInPtr.second) );
 | |
| 
 | |
|     // Uncompress an "abbreviated" chunk into our 4MB buffer
 | |
|     unsigned int outputLen = IN_BUF_LEN;
 | |
|     IDBCompressInterface compressor( userPadBytes );
 | |
|     int rc = compressor.uncompressBlock(
 | |
|                  compressedInBuf,
 | |
|                  chunkInPtr.second,
 | |
|                  (unsigned char*)toBeCompressedBuf,
 | |
|                  outputLen);
 | |
| 
 | |
|     if (rc != 0)
 | |
|     {
 | |
|         return ERR_COMP_UNCOMPRESS;
 | |
|     }
 | |
| 
 | |
|     compressedInBufPtr.reset(); // release memory
 | |
| 
 | |
|     RETURN_ON_ERROR( setFileOffset(pFile, chunkInPtr.first, SEEK_SET) );
 | |
| 
 | |
|     unsigned char* compressedOutBuf = new unsigned char[ OUT_BUF_LEN ];
 | |
|     boost::scoped_array<unsigned char> compressedOutBufPtr(compressedOutBuf);
 | |
| 
 | |
|     // Compress the data we just read, as a "full" 4MB chunk
 | |
|     outputLen = OUT_BUF_LEN;
 | |
|     rc = compressor.compressBlock(
 | |
|              reinterpret_cast<char*>(toBeCompressedBuf),
 | |
|              IN_BUF_LEN,
 | |
|              compressedOutBuf,
 | |
|              outputLen );
 | |
| 
 | |
|     if (rc != 0)
 | |
|     {
 | |
|         return ERR_COMP_COMPRESS;
 | |
|     }
 | |
| 
 | |
|     // Round up the compressed chunk size
 | |
|     rc = compressor.padCompressedChunks( compressedOutBuf,
 | |
|                                          outputLen, OUT_BUF_LEN );
 | |
| 
 | |
|     if (rc != 0)
 | |
|     {
 | |
|         return ERR_COMP_PAD_DATA;
 | |
|     }
 | |
| 
 | |
|     RETURN_ON_ERROR( writeFile(pFile, compressedOutBuf, outputLen) );
 | |
| 
 | |
|     chunkOutPtr.first  = chunkInPtr.first;
 | |
|     chunkOutPtr.second = outputLen;
 | |
| 
 | |
|     return NO_ERROR;
 | |
| }
 | |
| 
 | |
| /***********************************************************
 | |
|  * DESCRIPTION:
 | |
|  *    Write headers to a compressed column file.
 | |
|  * PARAMETERS:
 | |
|  *    pFile   (in) - IDBDataFile* of column segment file to be written to
 | |
|  *    hdr     (in) - header pointers to be written
 | |
|  * RETURN:
 | |
|  *    returns ERR_FILE_WRITE or ERR_FILE_SEEK if an error occurs,
 | |
|  *    else returns NO_ERROR.
 | |
|  ***********************************************************/
 | |
| int FileOp::writeHeaders(IDBDataFile* pFile, const char* hdr) const
 | |
| {
 | |
|     RETURN_ON_ERROR( setFileOffset(pFile, 0, SEEK_SET) );
 | |
| 
 | |
|     // Write the headers
 | |
|     if (pFile->write( hdr, IDBCompressInterface::HDR_BUF_LEN * 2 ) != IDBCompressInterface::HDR_BUF_LEN * 2)
 | |
|     {
 | |
|         return ERR_FILE_WRITE;
 | |
|     }
 | |
| 
 | |
|     return NO_ERROR;
 | |
| }
 | |
| 
 | |
| /***********************************************************
 | |
|  * DESCRIPTION:
 | |
|  *    Write headers to a compressed column or dictionary file.
 | |
|  * PARAMETERS:
 | |
|  *    pFile   (in) - IDBDataFile* of column segment file to be written to
 | |
|  *    controlHdr (in) - control header to be written
 | |
|  *    pointerHdr (in) - pointer header to be written
 | |
|  *    ptrHdrSize (in) - size (in bytes) of pointer header
 | |
|  * RETURN:
 | |
|  *    returns ERR_FILE_WRITE or ERR_FILE_SEEK if an error occurs,
 | |
|  *    else returns NO_ERROR.
 | |
|  ***********************************************************/
 | |
| int FileOp::writeHeaders(IDBDataFile* pFile, const char* controlHdr,
 | |
|                          const char* pointerHdr, uint64_t ptrHdrSize) const
 | |
| {
 | |
|     RETURN_ON_ERROR( setFileOffset(pFile, 0, SEEK_SET) );
 | |
| 
 | |
|     // Write the control header
 | |
|     if (pFile->write( controlHdr, IDBCompressInterface::HDR_BUF_LEN ) != IDBCompressInterface::HDR_BUF_LEN)
 | |
|     {
 | |
|         return ERR_FILE_WRITE;
 | |
|     }
 | |
| 
 | |
|     // Write the pointer header
 | |
|     if (pFile->write( pointerHdr, ptrHdrSize ) !=  (ssize_t) ptrHdrSize)
 | |
|     {
 | |
|         return ERR_FILE_WRITE;
 | |
|     }
 | |
| 
 | |
|     return NO_ERROR;
 | |
| }
 | |
| 
 | |
| /***********************************************************
 | |
|  * DESCRIPTION:
 | |
|  *    Write out (initialize) an extent in a dictionary store file.
 | |
|  *    A mutex is used for each DBRoot, to prevent contention between
 | |
|  *    threads, because if multiple threads are creating extents on
 | |
|  *    the same DBRoot at the same time, the extents can become
 | |
|  *    fragmented.  It is best to only create one extent at a time
 | |
|  *    on each DBRoot.
 | |
|  *    This function can be used to initialize an entirely new extent, or
 | |
|  *    to finish initializing an extent that has already been started.
 | |
|  *    nBlocks controls how many 8192-byte blocks are to be written out.
 | |
|  * PARAMETERS:
 | |
|  *    pFile   (in) - IDBDataFile* of column segment file to be written to
 | |
|  *    dbRoot  (in) - DBRoot of pFile
 | |
|  *    nBlocks (in) - number of blocks to be written for an extent
 | |
|  *    blockHdrInit(in) - data used to initialize each block
 | |
|  *    blockHdrInitSize(in) - number of bytes in blockHdrInit
 | |
|  *    bExpandExtent (in) - Expand existing extent, or initialize a new one
 | |
|  * RETURN:
 | |
|  *    returns ERR_FILE_WRITE if an error occurs,
 | |
|  *    else returns NO_ERROR.
 | |
|  ***********************************************************/
 | |
| int FileOp::initDctnryExtent(
 | |
|     IDBDataFile*   pFile,
 | |
|     uint16_t       dbRoot,
 | |
|     int            nBlocks,
 | |
|     unsigned char* blockHdrInit,
 | |
|     int            blockHdrInitSize,
 | |
|     bool           bExpandExtent )
 | |
| {
 | |
|     // @bug5769 Don't initialize extents or truncate db files on HDFS
 | |
|     if (idbdatafile::IDBPolicy::useHdfs())
 | |
|     {
 | |
|         if (m_compressionType)
 | |
|             updateDctnryExtent(pFile, nBlocks);
 | |
| 
 | |
|         // Synchronize to avoid write buffer pile up too much, which could cause
 | |
|         // controllernode to timeout later when it needs to save a snapshot.
 | |
|         pFile->flush();
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         // Create vector of mutexes used to serialize extent access per DBRoot
 | |
|         initDbRootExtentMutexes( );
 | |
| 
 | |
|         // Determine the number of blocks in each call to fwrite(), and the
 | |
|         // number of fwrite() calls to make, based on this.  In other words,
 | |
|         // we put a cap on the "writeSize" so that we don't allocate and write
 | |
|         // an entire extent at once for the 64M row extents.  If we are
 | |
|         // expanding an abbreviated 64M extent, we may not have an even
 | |
|         // multiple of MAX_NBLOCKS to write; remWriteSize is the number of
 | |
|         // blocks above and beyond loopCount*MAX_NBLOCKS.
 | |
|         int writeSize = nBlocks * BYTE_PER_BLOCK; // 1M and 8M row extent size
 | |
|         int loopCount = 1;
 | |
|         int remWriteSize = 0;
 | |
| 
 | |
|         if (nBlocks > MAX_NBLOCKS)                // 64M row extent size
 | |
|         {
 | |
|             writeSize = MAX_NBLOCKS * BYTE_PER_BLOCK;
 | |
|             loopCount = nBlocks / MAX_NBLOCKS;
 | |
|             remWriteSize = nBlocks - (loopCount * MAX_NBLOCKS);
 | |
|         }
 | |
| 
 | |
|         // Allocate a buffer, initialize it, and use it to create the extent
 | |
|         idbassert(dbRoot > 0);
 | |
| #ifdef PROFILE
 | |
| 
 | |
|         if (bExpandExtent)
 | |
|             Stats::startParseEvent(WE_STATS_WAIT_TO_EXPAND_DCT_EXTENT);
 | |
|         else
 | |
|             Stats::startParseEvent(WE_STATS_WAIT_TO_CREATE_DCT_EXTENT);
 | |
| 
 | |
| #endif
 | |
|         boost::mutex::scoped_lock lk(*m_DbRootAddExtentMutexes[dbRoot]);
 | |
| #ifdef PROFILE
 | |
| 
 | |
|         if (bExpandExtent)
 | |
|             Stats::stopParseEvent(WE_STATS_WAIT_TO_EXPAND_DCT_EXTENT);
 | |
|         else
 | |
|             Stats::stopParseEvent(WE_STATS_WAIT_TO_CREATE_DCT_EXTENT);
 | |
| 
 | |
|         Stats::startParseEvent(WE_STATS_INIT_DCT_EXTENT);
 | |
| #endif
 | |
| 
 | |
|         // Allocate buffer, and store in scoped_array to insure it's deletion.
 | |
|         // Create scope {...} to manage deletion of writeBuf.
 | |
|         {
 | |
|             unsigned char* writeBuf = new unsigned char[writeSize];
 | |
|             boost::scoped_array<unsigned char> writeBufPtr( writeBuf );
 | |
| 
 | |
|             memset(writeBuf, 0, writeSize);
 | |
| 
 | |
|             for (int i = 0; i < nBlocks; i++)
 | |
|             {
 | |
|                 memcpy( writeBuf + (i * BYTE_PER_BLOCK),
 | |
|                         blockHdrInit,
 | |
|                         blockHdrInitSize );
 | |
|             }
 | |
| 
 | |
| #ifdef PROFILE
 | |
|             Stats::stopParseEvent(WE_STATS_INIT_DCT_EXTENT);
 | |
| 
 | |
|             if (bExpandExtent)
 | |
|                 Stats::startParseEvent(WE_STATS_EXPAND_DCT_EXTENT);
 | |
|             else
 | |
|                 Stats::startParseEvent(WE_STATS_CREATE_DCT_EXTENT);
 | |
| 
 | |
| #endif
 | |
| 
 | |
|             //std::ostringstream oss;
 | |
|             //oss << "initDctnryExtent: width-8(assumed)" <<
 | |
|             //"; loopCount-" << loopCount <<
 | |
|             //"; writeSize-" << writeSize;
 | |
|             //std::cout << oss.str() << std::endl;
 | |
|             if (remWriteSize > 0)
 | |
|             {
 | |
|                 if (pFile->write( writeBuf, remWriteSize ) != remWriteSize)
 | |
|                 {
 | |
|                     return ERR_FILE_WRITE;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             for (int j = 0; j < loopCount; j++)
 | |
|             {
 | |
|                 if (pFile->write( writeBuf, writeSize ) != writeSize)
 | |
|                 {
 | |
|                     return ERR_FILE_WRITE;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if (m_compressionType)
 | |
|             updateDctnryExtent(pFile, nBlocks);
 | |
| 
 | |
|         // Synchronize to avoid write buffer pile up too much, which could cause
 | |
|         // controllernode to timeout later when it needs to save a snapshot.
 | |
|         pFile->flush();
 | |
| #ifdef PROFILE
 | |
| 
 | |
|         if (bExpandExtent)
 | |
|             Stats::stopParseEvent(WE_STATS_EXPAND_DCT_EXTENT);
 | |
|         else
 | |
|             Stats::stopParseEvent(WE_STATS_CREATE_DCT_EXTENT);
 | |
| 
 | |
| #endif
 | |
|     }
 | |
| 
 | |
|     return NO_ERROR;
 | |
| }
 | |
| 
 | |
| /***********************************************************
 | |
|  * DESCRIPTION:
 | |
|  *    Create a vector containing the mutexes used to serialize
 | |
|  *    extent creation per DBRoot.  Serializing extent creation
 | |
|  *    helps to prevent disk fragmentation.
 | |
|  ***********************************************************/
 | |
| /* static */
 | |
| void FileOp::initDbRootExtentMutexes( )
 | |
| {
 | |
|     boost::mutex::scoped_lock lk(m_createDbRootMutexes);
 | |
| 
 | |
|     if ( m_DbRootAddExtentMutexes.size() == 0 )
 | |
|     {
 | |
|         std::vector<uint16_t> rootIds;
 | |
|         Config::getRootIdList( rootIds );
 | |
| 
 | |
|         for (size_t i = 0; i < rootIds.size(); i++)
 | |
|         {
 | |
|             boost::mutex* pM = new boost::mutex;
 | |
|             m_DbRootAddExtentMutexes[ rootIds[i] ] = pM;
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| /***********************************************************
 | |
|  * DESCRIPTION:
 | |
|  *    Cleans up memory allocated to the DBRoot extent mutexes.  Calling
 | |
|  *    this function is not necessary, but it is provided for completeness,
 | |
|  *    to complement initDbRootExtentMutexes(), and to provide a way to
 | |
|  *    free up memory at the end of program execution.
 | |
|  ***********************************************************/
 | |
| /* static */
 | |
| void FileOp::removeDbRootExtentMutexes( )
 | |
| {
 | |
|     boost::mutex::scoped_lock lk(m_createDbRootMutexes);
 | |
| 
 | |
|     std::map<int, boost::mutex*>::iterator k = m_DbRootAddExtentMutexes.begin();
 | |
| 
 | |
|     while (k != m_DbRootAddExtentMutexes.end() )
 | |
|     {
 | |
|         delete k->second;
 | |
|         ++k;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /***********************************************************
 | |
|  * DESCRIPTION:
 | |
|  *    Write out (reinitialize) a partial extent in a column file.
 | |
|  *    A mutex is not used to prevent contention between threads,
 | |
|  *    because the extent should already be in place on disk; so
 | |
|  *    disk fragmentation is not an issue.
 | |
|  * PARAMETERS:
 | |
|  *    pFile   (in) - IDBDataFile* of column segment file to be written to
 | |
|  *    startOffset(in)-file offset where we are to begin writing blocks
 | |
|  *    nBlocks (in) - number of blocks to be written to the extent
 | |
|  *    emptyVal(in) - empty value to be used for column data values
 | |
|  *    width   (in) - width of the applicable column
 | |
|  * RETURN:
 | |
|  *    returns ERR_FILE_WRITE if an error occurs,
 | |
|  *    else returns NO_ERROR.
 | |
|  ***********************************************************/
 | |
| int FileOp::reInitPartialColumnExtent(
 | |
|     IDBDataFile* pFile,
 | |
|     long long startOffset,
 | |
|     int      nBlocks,
 | |
|     uint64_t emptyVal,
 | |
|     int      width )
 | |
| {
 | |
|     int rc = setFileOffset( pFile, startOffset, SEEK_SET );
 | |
| 
 | |
|     if (rc != NO_ERROR)
 | |
|         return rc;
 | |
| 
 | |
|     if (nBlocks == 0)
 | |
|         return NO_ERROR;
 | |
| 
 | |
|     // Determine the number of blocks in each call to fwrite(), and the
 | |
|     // number of fwrite() calls to make, based on this.  In other words,
 | |
|     // we put a cap on the "writeSize" so that we don't allocate and write
 | |
|     // an entire extent at once for the 64M row extents.
 | |
|     int writeSize = nBlocks * BYTE_PER_BLOCK; // 1M and 8M row extent size
 | |
|     int loopCount = 0;
 | |
|     int remainderSize = writeSize;
 | |
| 
 | |
|     if (nBlocks > MAX_NBLOCKS)                // 64M row extent size
 | |
|     {
 | |
|         writeSize = MAX_NBLOCKS * BYTE_PER_BLOCK;
 | |
|         loopCount = nBlocks / MAX_NBLOCKS;
 | |
|         remainderSize = nBlocks - (loopCount * MAX_NBLOCKS);
 | |
|     }
 | |
| 
 | |
|     // Allocate a buffer, initialize it, and use it to initialize the extent
 | |
|     // Store in scoped_array to insure it's deletion.
 | |
|     // Create scope {...} to manage deletion of writeBuf.
 | |
|     {
 | |
|         unsigned char* writeBuf = new unsigned char[writeSize];
 | |
|         boost::scoped_array<unsigned char> writeBufPtr( writeBuf );
 | |
| 
 | |
|         setEmptyBuf( writeBuf, writeSize, emptyVal, width );
 | |
| 
 | |
|         for (int j = 0; j < loopCount; j++)
 | |
|         {
 | |
|             if (pFile->write( writeBuf, writeSize ) != writeSize)
 | |
|             {
 | |
|                 return ERR_FILE_WRITE;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if (remainderSize > 0)
 | |
|         {
 | |
|             if (pFile->write( writeBuf, remainderSize ) != remainderSize)
 | |
|             {
 | |
|                 return ERR_FILE_WRITE;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // Synchronize here to avoid write buffer pile up too much, which could
 | |
|     // cause controllernode to timeout later when it needs to save a snapshot.
 | |
|     pFile->flush();
 | |
|     return NO_ERROR;
 | |
| }
 | |
| 
 | |
| /***********************************************************
 | |
|  * DESCRIPTION:
 | |
|  *    Write out (reinitialize) a partial extent in a dictionary store file.
 | |
|  *    A mutex is not used to prevent contention between threads,
 | |
|  *    because the extent should already be in place on disk; so
 | |
|  *    disk fragmentation is not an issue.
 | |
|  * PARAMETERS:
 | |
|  *    pFile   (in) - IDBDataFile* of column segment file to be written to
 | |
|  *    startOffset(in)-file offset where we are to begin writing blocks
 | |
|  *    nBlocks (in) - number of blocks to be written to the extent
 | |
|  *    blockHdrInit(in) - data used to initialize each block
 | |
|  *    blockHdrInitSize(in) - number of bytes in blockHdrInit
 | |
|  * RETURN:
 | |
|  *    returns ERR_FILE_WRITE if an error occurs,
 | |
|  *    else returns NO_ERROR.
 | |
|  ***********************************************************/
 | |
| int FileOp::reInitPartialDctnryExtent(
 | |
|     IDBDataFile*   pFile,
 | |
|     long long      startOffset,
 | |
|     int            nBlocks,
 | |
|     unsigned char* blockHdrInit,
 | |
|     int            blockHdrInitSize )
 | |
| {
 | |
|     int rc = setFileOffset( pFile, startOffset, SEEK_SET );
 | |
| 
 | |
|     if (rc != NO_ERROR)
 | |
|         return rc;
 | |
| 
 | |
|     if (nBlocks == 0)
 | |
|         return NO_ERROR;
 | |
| 
 | |
|     // Determine the number of blocks in each call to fwrite(), and the
 | |
|     // number of fwrite() calls to make, based on this.  In other words,
 | |
|     // we put a cap on the "writeSize" so that we don't allocate and write
 | |
|     // an entire extent at once for the 64M row extents.
 | |
|     int writeSize = nBlocks * BYTE_PER_BLOCK; // 1M and 8M row extent size
 | |
|     int loopCount = 0;
 | |
|     int remainderSize = writeSize;
 | |
| 
 | |
|     if (nBlocks > MAX_NBLOCKS)                // 64M row extent size
 | |
|     {
 | |
|         writeSize = MAX_NBLOCKS * BYTE_PER_BLOCK;
 | |
|         loopCount = nBlocks / MAX_NBLOCKS;
 | |
|         remainderSize = nBlocks - (loopCount * MAX_NBLOCKS);
 | |
|         nBlocks   = MAX_NBLOCKS;
 | |
|     }
 | |
| 
 | |
|     // Allocate a buffer, initialize it, and use it to initialize the extent
 | |
|     // Store in scoped_array to insure it's deletion.
 | |
|     // Create scope {...} to manage deletion of writeBuf.
 | |
|     {
 | |
|         unsigned char* writeBuf = new unsigned char[writeSize];
 | |
|         boost::scoped_array<unsigned char> writeBufPtr( writeBuf );
 | |
| 
 | |
|         memset(writeBuf, 0, writeSize);
 | |
| 
 | |
|         for (int i = 0; i < nBlocks; i++)
 | |
|         {
 | |
|             memcpy( writeBuf + (i * BYTE_PER_BLOCK),
 | |
|                     blockHdrInit,
 | |
|                     blockHdrInitSize );
 | |
|         }
 | |
| 
 | |
|         for (int j = 0; j < loopCount; j++)
 | |
|         {
 | |
|             if (pFile->write( writeBuf, writeSize ) != writeSize)
 | |
|             {
 | |
|                 return ERR_FILE_WRITE;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if (remainderSize > 0)
 | |
|         {
 | |
|             if (pFile->write( writeBuf, remainderSize ) != remainderSize)
 | |
|             {
 | |
|                 return ERR_FILE_WRITE;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // Synchronize here to avoid write buffer pile up too much, which could
 | |
|     // cause controllernode to timeout later when it needs to save a snapshot.
 | |
|     pFile->flush();
 | |
|     return NO_ERROR;
 | |
| }
 | |
| 
 | |
| /***********************************************************
 | |
|  * DESCRIPTION:
 | |
|  * PARAMETERS:
 | |
|  *    pFile - file handle
 | |
|  *    fileSize (out) - file size in bytes
 | |
|  * RETURN:
 | |
|  *    error code
 | |
|  ***********************************************************/
 | |
| int FileOp::getFileSize( IDBDataFile* pFile, long long& fileSize ) const
 | |
| {
 | |
|     fileSize = 0;
 | |
| 
 | |
|     if ( pFile == NULL )
 | |
|         return ERR_FILE_NULL;
 | |
| 
 | |
|     fileSize = pFile->size();
 | |
| 
 | |
|     if ( fileSize < 0 )
 | |
|     {
 | |
|         fileSize = 0;
 | |
|         return ERR_FILE_STAT;
 | |
|     }
 | |
| 
 | |
|     return NO_ERROR;
 | |
| }
 | |
| 
 | |
| /***********************************************************
 | |
|  * DESCRIPTION:
 | |
|  *    Get file size using file id
 | |
|  * PARAMETERS:
 | |
|  *    fid    - column OID
 | |
|  *    dbroot    - DBRoot    of applicable segment file
 | |
|  *    partition - partition of applicable segment file
 | |
|  *    segment   - segment   of applicable segment file
 | |
|  *    fileSize (out) - current file size for requested segment file
 | |
|  * RETURN:
 | |
|  *    NO_ERROR if okay, else an error return code.
 | |
|  ***********************************************************/
 | |
| int FileOp::getFileSize( FID fid, uint16_t dbRoot,
 | |
|                          uint32_t partition, uint16_t segment,
 | |
|                          long long& fileSize ) const
 | |
| {
 | |
|     fileSize = 0;
 | |
| 
 | |
|     char fileName[FILE_NAME_SIZE];
 | |
|     RETURN_ON_ERROR( getFileName(fid, fileName,
 | |
|                                  dbRoot, partition, segment) );
 | |
| 
 | |
|     fileSize = IDBPolicy::size( fileName );
 | |
| 
 | |
|     if ( fileSize < 0 )
 | |
|     {
 | |
|         fileSize = 0;
 | |
|         return ERR_FILE_STAT;
 | |
|     }
 | |
| 
 | |
|     return NO_ERROR;
 | |
| }
 | |
| 
 | |
| /***********************************************************
 | |
|  * DESCRIPTION:
 | |
|  *    Check whether it is a directory
 | |
|  * PARAMETERS:
 | |
|  *    dirName - directory name
 | |
|  * RETURN:
 | |
|  *    true if it is, false otherwise
 | |
|  ***********************************************************/
 | |
| bool  FileOp::isDir( const char* dirName ) const
 | |
| {
 | |
|     return IDBPolicy::isDir( dirName );
 | |
| }
 | |
| 
 | |
| /***********************************************************
 | |
|  * DESCRIPTION:
 | |
|  *    Convert an oid to a filename
 | |
|  * PARAMETERS:
 | |
|  *    fid - fid
 | |
|  *    fullFileName - file name
 | |
|  *    bCreateDir - whether need to create a directory
 | |
|  *    dbRoot     - DBRoot where file is to be located; 1->DBRoot1,
 | |
|  *                 2->DBRoot2, etc.  If bCreateDir is false, meaning we
 | |
|  *                 are not creating the file but only searching for an
 | |
|  *                 existing file, then dbRoot can be 0, and oid2FileName
 | |
|  *                 will search all the DBRoots for the applicable filename.
 | |
|  *    partition  - Partition number to be used in filepath subdirectory
 | |
|  *    segment    - Segment number to be used in filename
 | |
|  * RETURN:
 | |
|  *    NO_ERROR if success, other if fail
 | |
|  ***********************************************************/
 | |
| int FileOp::oid2FileName( FID fid,
 | |
|                           char* fullFileName,
 | |
|                           bool bCreateDir,
 | |
|                           uint16_t dbRoot,
 | |
|                           uint32_t partition,
 | |
|                           uint16_t segment) const
 | |
| {
 | |
| #ifdef SHARED_NOTHING_DEMO_2
 | |
| 
 | |
|     if (fid >= 10000)
 | |
|     {
 | |
|         char root[FILE_NAME_SIZE];
 | |
|         Config::getSharedNothingRoot(root);
 | |
|         sprintf(fullFileName, "%s/FILE%d", root, fid);
 | |
|         return NO_ERROR;
 | |
|     }
 | |
| 
 | |
| #endif
 | |
| 
 | |
|     /* If is a version buffer file, the format is different. */
 | |
|     if (fid < 1000)
 | |
|     {
 | |
|         /* Get the dbroot #
 | |
|          * Get the root of that dbroot
 | |
|          * Add "/versionbuffer.cdf"
 | |
|          */
 | |
|         BRM::DBRM dbrm;
 | |
|         int _dbroot = dbrm.getDBRootOfVBOID(fid);
 | |
| 
 | |
|         if (_dbroot < 0)
 | |
|             return ERR_INVALID_VBOID;
 | |
| 
 | |
|         snprintf(fullFileName, FILE_NAME_SIZE,
 | |
|                  "%s/versionbuffer.cdf", Config::getDBRootByNum(_dbroot).c_str());
 | |
|         return NO_ERROR;
 | |
|     }
 | |
| 
 | |
| //Get hashed part of the filename. This is the tail-end of the filename path,
 | |
| //  excluding the DBRoot.
 | |
|     char tempFileName[FILE_NAME_SIZE];
 | |
|     char dbDir[MAX_DB_DIR_LEVEL][MAX_DB_DIR_NAME_SIZE];
 | |
|     RETURN_ON_ERROR((Convertor::oid2FileName(
 | |
|                          fid, tempFileName, dbDir, partition, segment)));
 | |
| 
 | |
|     // see if file exists in specified DBRoot; return if found
 | |
|     if (dbRoot > 0)
 | |
|     {
 | |
|         sprintf(fullFileName, "%s/%s", Config::getDBRootByNum(dbRoot).c_str(),
 | |
|                 tempFileName);
 | |
| 
 | |
|         //std::cout << "oid2FileName() OID: " << fid <<
 | |
|         //   " searching for file: " << fullFileName <<std::endl;
 | |
|         // if (access(fullFileName, R_OK) == 0) return NO_ERROR;
 | |
|         //@Bug 5397
 | |
|         if (IDBPolicy::exists( fullFileName ))
 | |
|             return NO_ERROR;
 | |
| 
 | |
|         //file wasn't found, user doesn't want dirs to be created, we're done
 | |
|         if (!bCreateDir)
 | |
|             return NO_ERROR;
 | |
| 
 | |
|         //std::cout << "oid2FileName() OID: " << fid <<
 | |
|         //   " creating file: " << fullFileName <<std::endl;
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         //Now try to find the file in each of the DBRoots.
 | |
|         std::vector<std::string> dbRootPathList;
 | |
|         Config::getDBRootPathList( dbRootPathList );
 | |
| 
 | |
|         for (unsigned i = 0; i < dbRootPathList.size(); i++)
 | |
|         {
 | |
|             sprintf(fullFileName, "%s/%s", dbRootPathList[i].c_str(),
 | |
|                     tempFileName);
 | |
| 
 | |
|             //found it, nothing more to do, return
 | |
|             //if (access(fullFileName, R_OK) == 0) return NO_ERROR;
 | |
|             //@Bug 5397
 | |
|             if (IDBPolicy::exists( fullFileName ))
 | |
|                 return NO_ERROR;
 | |
|         }
 | |
| 
 | |
|         //file wasn't found, user didn't specify DBRoot so we can't create
 | |
|         return ERR_FILE_NOT_EXIST;
 | |
|     }
 | |
| 
 | |
|     /*
 | |
|     char dirName[FILE_NAME_SIZE];
 | |
| 
 | |
|     sprintf( dirName, "%s/%s", Config::getDBRootByNum(dbRoot).c_str(),
 | |
|         dbDir[0] );
 | |
|     if( !isDir( dirName ) )
 | |
|         RETURN_ON_ERROR( createDir( dirName ));
 | |
| 
 | |
|     sprintf( dirName, "%s/%s", dirName, dbDir[1] );
 | |
|     if( !isDir( dirName ) )
 | |
|         RETURN_ON_ERROR( createDir( dirName ));
 | |
| 
 | |
|     sprintf( dirName, "%s/%s", dirName, dbDir[2] );
 | |
|     if( !isDir( dirName ) )
 | |
|         RETURN_ON_ERROR( createDir( dirName ));
 | |
| 
 | |
|     sprintf( dirName, "%s/%s", dirName, dbDir[3] );
 | |
|     if( !isDir( dirName ) )
 | |
|         RETURN_ON_ERROR( createDir( dirName ));
 | |
| 
 | |
|     sprintf( dirName, "%s/%s", dirName, dbDir[4] );
 | |
|     if( !isDir( dirName ) )
 | |
|         RETURN_ON_ERROR( createDir( dirName ));
 | |
|     */
 | |
| 
 | |
|     std::stringstream aDirName;
 | |
| 
 | |
|     aDirName << Config::getDBRootByNum(dbRoot).c_str() << "/" << dbDir[0];
 | |
| 
 | |
|     if (!isDir((aDirName.str()).c_str()))
 | |
|         RETURN_ON_ERROR( createDir((aDirName.str()).c_str()) );
 | |
| 
 | |
|     aDirName << "/" << dbDir[1];
 | |
| 
 | |
|     if (!isDir(aDirName.str().c_str()))
 | |
|         RETURN_ON_ERROR( createDir(aDirName.str().c_str()) );
 | |
| 
 | |
|     aDirName << "/" << dbDir[2];
 | |
| 
 | |
|     if (!isDir(aDirName.str().c_str()))
 | |
|         RETURN_ON_ERROR( createDir(aDirName.str().c_str()) );
 | |
| 
 | |
|     aDirName << "/" << dbDir[3];
 | |
| 
 | |
|     if (!isDir(aDirName.str().c_str()))
 | |
|         RETURN_ON_ERROR( createDir(aDirName.str().c_str()) );
 | |
| 
 | |
|     aDirName << "/" << dbDir[4];
 | |
| 
 | |
|     if (!isDir(aDirName.str().c_str()))
 | |
|         RETURN_ON_ERROR( createDir(aDirName.str().c_str()) );
 | |
| 
 | |
|     return NO_ERROR;
 | |
| }
 | |
| 
 | |
| /***********************************************************
 | |
|  * DESCRIPTION:
 | |
|  *    Search for directory path associated with specified OID.
 | |
|  *    If the OID is a version buffer file, it returns the whole
 | |
|  *    filename.
 | |
|  * PARAMETERS:
 | |
|  *    fid   - (in)  OID to search for
 | |
|  *    pFile - (out) OID directory path (including DBRoot) that is found
 | |
|  * RETURN:
 | |
|  *    NO_ERROR if OID dir path found, else returns ERR_FILE_NOT_EXIST
 | |
|  ***********************************************************/
 | |
| int FileOp::oid2DirName( FID fid, char* oidDirName ) const
 | |
| {
 | |
|     char tempFileName[FILE_NAME_SIZE];
 | |
|     char dbDir[MAX_DB_DIR_LEVEL][MAX_DB_DIR_NAME_SIZE];
 | |
| 
 | |
|     /* If is a version buffer file, the format is different. */
 | |
|     if (fid < 1000)
 | |
|     {
 | |
|         /* Get the dbroot #
 | |
|          * Get the root of that dbroot
 | |
|          */
 | |
|         BRM::DBRM dbrm;
 | |
|         int _dbroot = dbrm.getDBRootOfVBOID(fid);
 | |
| 
 | |
|         if (_dbroot < 0)
 | |
|             return ERR_INVALID_VBOID;
 | |
| 
 | |
|         snprintf(oidDirName, FILE_NAME_SIZE, "%s",
 | |
|                  Config::getDBRootByNum(_dbroot).c_str());
 | |
|         return NO_ERROR;
 | |
|     }
 | |
| 
 | |
|     RETURN_ON_ERROR((Convertor::oid2FileName(
 | |
|                          fid, tempFileName, dbDir, 0, 0)));
 | |
| 
 | |
|     //Now try to find the directory in each of the DBRoots.
 | |
|     std::vector<std::string> dbRootPathList;
 | |
|     Config::getDBRootPathList( dbRootPathList );
 | |
| 
 | |
|     for (unsigned i = 0; i < dbRootPathList.size(); i++)
 | |
|     {
 | |
|         sprintf(oidDirName, "%s/%s/%s/%s/%s",
 | |
|                 dbRootPathList[i].c_str(),
 | |
|                 dbDir[0], dbDir[1], dbDir[2], dbDir[3]);
 | |
| 
 | |
|         //found it, nothing more to do, return
 | |
|         //@Bug 5397. use the new way to check
 | |
|         if (IDBPolicy::exists( oidDirName ))
 | |
|             return NO_ERROR;
 | |
|     }
 | |
| 
 | |
|     return ERR_FILE_NOT_EXIST;
 | |
| }
 | |
| 
 | |
| /***********************************************************
 | |
|  * DESCRIPTION:
 | |
|  *    Construct directory path for the specified fid (OID), DBRoot, and
 | |
|  *    partition number.  Directory path need not exist, nor is it created.
 | |
|  * PARAMETERS:
 | |
|  *    fid       - (in)  OID of interest
 | |
|  *    dbRoot    - (in)  DBRoot of interest
 | |
|  *    partition - (in)  partition of interest
 | |
|  *    dirName   - (out) constructed directory path
 | |
|  * RETURN:
 | |
|  *    NO_ERROR if path is successfully constructed.
 | |
|  ***********************************************************/
 | |
| int FileOp::getDirName( FID fid, uint16_t dbRoot,
 | |
|                         uint32_t partition,
 | |
|                         std::string& dirName) const
 | |
| {
 | |
|     char tempFileName[FILE_NAME_SIZE];
 | |
|     char dbDir[MAX_DB_DIR_LEVEL][MAX_DB_DIR_NAME_SIZE];
 | |
| 
 | |
|     RETURN_ON_ERROR((Convertor::oid2FileName(
 | |
|                          fid, tempFileName, dbDir, partition, 0)));
 | |
| 
 | |
|     std::string rootPath = Config::getDBRootByNum( dbRoot );
 | |
|     std::ostringstream oss;
 | |
|     oss << rootPath << '/' <<
 | |
|         dbDir[0] << '/' <<
 | |
|         dbDir[1] << '/' <<
 | |
|         dbDir[2] << '/' <<
 | |
|         dbDir[3] << '/' <<
 | |
|         dbDir[4];
 | |
|     dirName = oss.str();
 | |
| 
 | |
|     return NO_ERROR;
 | |
| }
 | |
| 
 | |
| /***********************************************************
 | |
|  * DESCRIPTION:
 | |
|  *    Open a file
 | |
|  * PARAMETERS:
 | |
|  *    fileName - file name with complete path
 | |
|  *    pFile - file handle
 | |
|  * RETURN:
 | |
|  *    true if exists, false otherwise
 | |
|  ***********************************************************/
 | |
| // @bug 5572 - HDFS usage: add *.tmp file backup flag
 | |
| IDBDataFile* FileOp::openFile( const char* fileName,
 | |
|                                const char* mode,
 | |
|                                const int ioColSize,
 | |
|                                bool useTmpSuffix ) const
 | |
| {
 | |
|     IDBDataFile* pFile;
 | |
|     errno = 0;
 | |
| 
 | |
|     unsigned opts;
 | |
| 
 | |
|     if (ioColSize > 0)
 | |
|         opts = IDBDataFile::USE_VBUF;
 | |
|     else
 | |
|         opts = IDBDataFile::USE_NOVBUF;
 | |
| 
 | |
|     if ((useTmpSuffix) && idbdatafile::IDBPolicy::useHdfs())
 | |
|         opts |= IDBDataFile::USE_TMPFILE;
 | |
| 
 | |
|     pFile = IDBDataFile::open(
 | |
|                 IDBPolicy::getType( fileName, IDBPolicy::WRITEENG ),
 | |
|                 fileName,
 | |
|                 mode,
 | |
|                 opts,
 | |
|                 ioColSize );
 | |
| 
 | |
|     if (pFile == NULL)
 | |
|     {
 | |
|         int errRc = errno;
 | |
|         std::ostringstream oss;
 | |
|         std::string errnoMsg;
 | |
|         Convertor::mapErrnoToString(errRc, errnoMsg);
 | |
|         oss << "FileOp::openFile(): fopen(" << fileName <<
 | |
|             ", " << mode << "): errno = " << errRc <<
 | |
|             ": " << errnoMsg;
 | |
|         logging::Message::Args args;
 | |
|         args.add(oss.str());
 | |
|         SimpleSysLog::instance()->logMsg(args,
 | |
|                                          logging::LOG_TYPE_CRITICAL,
 | |
|                                          logging::M0006);
 | |
|         SimpleSysLog::instance()->logMsg(args,
 | |
|                                          logging::LOG_TYPE_ERROR,
 | |
|                                          logging::M0006);
 | |
|     }
 | |
| 
 | |
|     return pFile;
 | |
| }
 | |
| 
 | |
| // @bug 5572 - HDFS usage: add *.tmp file backup flag
 | |
| IDBDataFile* FileOp::openFile( FID fid,
 | |
|                                uint16_t dbRoot,
 | |
|                                uint32_t partition,
 | |
|                                uint16_t segment,
 | |
|                                std::string&   segFile,
 | |
|                                const char* mode, int ioColSize,
 | |
|                                bool useTmpSuffix ) const
 | |
| {
 | |
|     char fileName[FILE_NAME_SIZE];
 | |
|     int  rc;
 | |
| 
 | |
|     //fid2FileName( fileName, fid );
 | |
|     RETURN_ON_WE_ERROR( ( rc = getFileName( fid, fileName,
 | |
|                                             dbRoot, partition, segment ) ), NULL );
 | |
| 
 | |
|     // disable buffering for versionbuffer file
 | |
|     if (fid < 1000)
 | |
|         ioColSize = 0;
 | |
| 
 | |
|     IDBDataFile* pF = openFile( fileName, mode, ioColSize, useTmpSuffix );
 | |
| 
 | |
|     segFile = fileName;
 | |
| 
 | |
|     return pF;
 | |
| }
 | |
| 
 | |
| /***********************************************************
 | |
|  * DESCRIPTION:
 | |
|  *    Read a portion of file to a buffer
 | |
|  * PARAMETERS:
 | |
|  *    pFile - file handle
 | |
|  *    readBuf - read buffer
 | |
|  *    readSize - the size to read
 | |
|  * RETURN:
 | |
|  *    NO_ERROR if success
 | |
|  *    ERR_FILE_NULL if file handle is NULL
 | |
|  *    ERR_FILE_READ if something wrong in reading the file
 | |
|  ***********************************************************/
 | |
| int FileOp::readFile( IDBDataFile* pFile, unsigned char* readBuf,
 | |
|                       int readSize ) const
 | |
| {
 | |
|     if ( pFile != NULL )
 | |
|     {
 | |
|         if ( pFile->read( readBuf, readSize ) != readSize )
 | |
|             return ERR_FILE_READ;
 | |
|     }
 | |
|     else
 | |
|         return ERR_FILE_NULL;
 | |
| 
 | |
|     return NO_ERROR;
 | |
| }
 | |
| 
 | |
| /***********************************************************
 | |
|  * Reads contents of headers from "pFile" and stores into "hdrs".
 | |
|  ***********************************************************/
 | |
| int FileOp::readHeaders( IDBDataFile* pFile, char* hdrs ) const
 | |
| {
 | |
|     RETURN_ON_ERROR( setFileOffset(pFile, 0) );
 | |
|     RETURN_ON_ERROR( readFile( pFile, reinterpret_cast<unsigned char*>(hdrs),
 | |
|                                (IDBCompressInterface::HDR_BUF_LEN * 2) ) );
 | |
|     IDBCompressInterface compressor;
 | |
|     int rc = compressor.verifyHdr( hdrs );
 | |
| 
 | |
|     if (rc != 0)
 | |
|     {
 | |
|         return ERR_COMP_VERIFY_HDRS;
 | |
|     }
 | |
| 
 | |
|     return NO_ERROR;
 | |
| }
 | |
| 
 | |
| /***********************************************************
 | |
|  * Reads contents of headers from "pFile" and stores into "hdr1" and "hdr2".
 | |
|  ***********************************************************/
 | |
| int FileOp::readHeaders( IDBDataFile* pFile, char* hdr1, char* hdr2 ) const
 | |
| {
 | |
|     unsigned char* hdrPtr = reinterpret_cast<unsigned char*>(hdr1);
 | |
|     RETURN_ON_ERROR( setFileOffset(pFile, 0) );
 | |
|     RETURN_ON_ERROR( readFile( pFile, hdrPtr,
 | |
|                                IDBCompressInterface::HDR_BUF_LEN ));
 | |
| 
 | |
|     IDBCompressInterface compressor;
 | |
|     int ptrSecSize = compressor.getHdrSize(hdrPtr) -
 | |
|                      IDBCompressInterface::HDR_BUF_LEN;
 | |
|     return readFile( pFile, reinterpret_cast<unsigned char*>(hdr2),
 | |
|                      ptrSecSize );
 | |
| }
 | |
| 
 | |
| /***********************************************************
 | |
|  * DESCRIPTION: No change Old signature
 | |
|  *    Read a portion of file to a buffer
 | |
|  * PARAMETERS:
 | |
|  *    pFile - file handle
 | |
|  *    offset - file offset
 | |
|  *    origin - can be SEEK_SET, or SEEK_CUR, or SEEK_END
 | |
|  * RETURN:
 | |
|  *    NO_ERROR if success
 | |
|  *    ERR_FILE_NULL if file handle is NULL
 | |
|  *    ERR_FILE_SEEK if something wrong in setting the position
 | |
|  ***********************************************************/
 | |
| int FileOp::setFileOffset( IDBDataFile* pFile, long long offset, int origin ) const
 | |
| {
 | |
|     int rc;
 | |
|     long long fboOffset = offset; // workaround solution to pass leakcheck error
 | |
| 
 | |
|     if ( pFile == NULL )
 | |
|         return ERR_FILE_NULL;
 | |
| 
 | |
|     if ( offset < 0 )
 | |
|         return ERR_FILE_FBO_NEG;
 | |
| 
 | |
|     rc = pFile->seek( fboOffset, origin );
 | |
| 
 | |
|     if (rc)
 | |
|         return ERR_FILE_SEEK;
 | |
| 
 | |
|     return NO_ERROR;
 | |
| }
 | |
| 
 | |
| /***********************************************************
 | |
|  * DESCRIPTION:
 | |
|  *    Read a portion of file to a buffer
 | |
|  * PARAMETERS:
 | |
|  *    pFile - file handle
 | |
|  *    offset - file offset
 | |
|  *    origin - can be SEEK_SET, or SEEK_CUR, or SEEK_END
 | |
|  * RETURN:
 | |
|  *    NO_ERROR if success
 | |
|  *    ERR_FILE_NULL if file handle is NULL
 | |
|  *    ERR_FILE_SEEK if something wrong in setting the position
 | |
|  ***********************************************************/
 | |
| int FileOp::setFileOffsetBlock( IDBDataFile* pFile, uint64_t lbid, int origin) const
 | |
| {
 | |
|     long long  fboOffset = 0;
 | |
|     int fbo = 0;
 | |
| 
 | |
|     // only when fboFlag is false, we get in here
 | |
|     uint16_t  dbRoot;
 | |
|     uint32_t  partition;
 | |
|     uint16_t  segment;
 | |
|     RETURN_ON_ERROR( BRMWrapper::getInstance()->getFboOffset(
 | |
|                          lbid, dbRoot, partition, segment, fbo ) );
 | |
|     fboOffset = ((long long)fbo) * (long)BYTE_PER_BLOCK;
 | |
| 
 | |
|     return setFileOffset( pFile, fboOffset, origin );
 | |
| }
 | |
| 
 | |
| /***********************************************************
 | |
|  * DESCRIPTION:
 | |
|  *    Truncate file to the specified size.
 | |
|  * PARAMETERS:
 | |
|  *    pFile - file handle
 | |
|  *    fileSize - size of file in bytes.
 | |
|  * RETURN:
 | |
|  *    NO_ERROR if success
 | |
|  *    ERR_FILE_NULL if file handle is NULL
 | |
|  *    ERR_FILE_SEEK if something wrong in setting the position
 | |
|  ***********************************************************/
 | |
| int FileOp::truncateFile( IDBDataFile* pFile, long long fileSize ) const
 | |
| {
 | |
|     if ( pFile == NULL )
 | |
|         return ERR_FILE_NULL;
 | |
| 
 | |
|     if ( pFile->truncate( fileSize ) != 0 )
 | |
|         return ERR_FILE_TRUNCATE;
 | |
| 
 | |
|     return NO_ERROR;
 | |
| }
 | |
| 
 | |
| /***********************************************************
 | |
|  * DESCRIPTION:
 | |
|  *    Write a buffer to a file at at current location
 | |
|  * PARAMETERS:
 | |
|  *    pFile - file handle
 | |
|  *    writeBuf - write buffer
 | |
|  *    writeSize - the write size
 | |
|  * RETURN:
 | |
|  *    NO_ERROR if success
 | |
|  *    ERR_FILE_NULL if file handle is NULL
 | |
|  *    ERR_FILE_WRITE if something wrong in writing to the file
 | |
|  ***********************************************************/
 | |
| int FileOp::writeFile( IDBDataFile* pFile, const unsigned char* writeBuf,
 | |
|                        int writeSize ) const
 | |
| {
 | |
|     if ( pFile != NULL )
 | |
|     {
 | |
|         if ( pFile->write( writeBuf, writeSize ) != writeSize )
 | |
|             return ERR_FILE_WRITE;
 | |
|     }
 | |
|     else
 | |
|         return ERR_FILE_NULL;
 | |
| 
 | |
|     return NO_ERROR;
 | |
| }
 | |
| 
 | |
| /***********************************************************
 | |
|  * DESCRIPTION:
 | |
|  *    Determine whether the applicable filesystem has room to add the
 | |
|  *    specified number of blocks (where the blocks contain BYTE_PER_BLOCK
 | |
|  *    bytes).
 | |
|  * PARAMETERS:
 | |
|  *    fileName - file whose file system is to be checked.  Does not have to
 | |
|  *               be a complete file name.  Dir path is sufficient.
 | |
|  *    nBlock   - number of 8192-byte blocks to be added
 | |
|  * RETURN:
 | |
|  *    true if there is room for the blocks or it can not be determined;
 | |
|  *    false if file system usage would exceed allowable threshold
 | |
|  ***********************************************************/
 | |
| bool FileOp::isDiskSpaceAvail(const std::string& fileName, int nBlocks) const
 | |
| {
 | |
|     bool bSpaceAvail = true;
 | |
| 
 | |
|     unsigned maxDiskUsage = Config::getMaxFileSystemDiskUsage();
 | |
| 
 | |
|     if (maxDiskUsage < 100) // 100% means to disable the check
 | |
|     {
 | |
| #ifdef _MSC_VER
 | |
|         ULARGE_INTEGER freeBytesAvail;
 | |
|         ULARGE_INTEGER totalBytesAvail;
 | |
| 
 | |
|         if (GetDiskFreeSpaceEx(fileName.c_str(), &freeBytesAvail,
 | |
|                                &totalBytesAvail, 0) != 0)
 | |
|         {
 | |
|             double avail = (double)freeBytesAvail.QuadPart;
 | |
|             double total = (double)totalBytesAvail.QuadPart;
 | |
|             double wanted = (double)nBlocks * (double)BYTE_PER_BLOCK;
 | |
| 
 | |
|             //If we want more than there is, return an error
 | |
|             if (wanted > avail)
 | |
|                 bSpaceAvail = false;
 | |
|             //If the remaining bytes would be too few, return an error
 | |
|             else if ((total - (avail - wanted)) / total * 100.0 > maxDiskUsage)
 | |
|                 bSpaceAvail = false;
 | |
|         }
 | |
| 
 | |
| #else
 | |
|         struct statfs fStats;
 | |
|         int rc = statfs( fileName.c_str(), &fStats );
 | |
| 
 | |
|         if (rc == 0)
 | |
|         {
 | |
|             double totalBlocks = fStats.f_blocks;
 | |
|             double blksToAlloc = (double)(nBlocks * BYTE_PER_BLOCK) /
 | |
|                                  fStats.f_bsize;
 | |
|             double freeBlocks  = fStats.f_bavail - blksToAlloc;
 | |
| 
 | |
|             if ((((totalBlocks - freeBlocks) / totalBlocks) * 100.0) > maxDiskUsage)
 | |
|                 bSpaceAvail = false;
 | |
| 
 | |
|             //std::cout         << "isDiskSpaceAvail"   <<
 | |
|             //": totalBlocks: " << totalBlocks          <<
 | |
|             //"; blkSize: "     << fStats.f_bsize       <<
 | |
|             //"; nBlocks: "     << nBlocks              <<
 | |
|             //"; freeBlks: "    << freeBlocks           <<
 | |
|             //"; pctUsed: " << (((totalBlocks-freeBlocks)/totalBlocks)*100.0) <<
 | |
|             //"; bAvail: "      << bSpaceAvail          << std::endl;
 | |
|         }
 | |
| 
 | |
| #endif
 | |
|     }
 | |
| 
 | |
|     return bSpaceAvail;
 | |
| }
 | |
| 
 | |
| //------------------------------------------------------------------------------
 | |
| // Virtual default functions follow; placeholders for derived class if they want
 | |
| // to override (see ColumnOpCompress1 and DctnryCompress1 in /wrapper).
 | |
| //------------------------------------------------------------------------------
 | |
| 
 | |
| //------------------------------------------------------------------------------
 | |
| // Expand current abbreviated extent to a full extent for column segment file
 | |
| // associated with pFile.  Function leaves fileposition at end of file after
 | |
| // extent is expanded.
 | |
| //------------------------------------------------------------------------------
 | |
| int FileOp::expandAbbrevColumnExtent(
 | |
|     IDBDataFile* pFile,   // FILE ptr to file where abbrev extent is to be expanded
 | |
|     uint16_t dbRoot,  // The DBRoot of the file with the abbreviated extent
 | |
|     uint64_t emptyVal,// Empty value to be used in expanding the extent
 | |
|     int      width )  // Width of the column (in bytes)
 | |
| {
 | |
|     // Based on extent size, see how many blocks to add to fill the extent
 | |
|     int blksToAdd = ( ((int)BRMWrapper::getInstance()->getExtentRows() -
 | |
|                        INITIAL_EXTENT_ROWS_TO_DISK) / BYTE_PER_BLOCK ) * width;
 | |
| 
 | |
|     // Make sure there is enough disk space to expand the extent.
 | |
|     RETURN_ON_ERROR( setFileOffset( pFile, 0, SEEK_END ) );
 | |
| 
 | |
|     // TODO-will have to address this DiskSpaceAvail check at some point
 | |
|     if ( !isDiskSpaceAvail(Config::getDBRootByNum(dbRoot), blksToAdd) )
 | |
|     {
 | |
|         return ERR_FILE_DISK_SPACE;
 | |
|     }
 | |
| 
 | |
|     // Add blocks to turn the abbreviated extent into a full extent.
 | |
|     int rc = FileOp::initColumnExtent(pFile, dbRoot, blksToAdd, emptyVal, width,
 | |
|                                       false,   // existing file
 | |
|                                       true,    // expand existing extent
 | |
|                                       false);  // n/a since not adding new extent
 | |
| 
 | |
|     return rc;
 | |
| }
 | |
| 
 | |
| void FileOp::setTransId(const TxnID& transId)
 | |
| {
 | |
|     m_transId = transId;
 | |
| }
 | |
| 
 | |
| void FileOp::setBulkFlag(bool isBulkLoad)
 | |
| {
 | |
|     m_isBulk = isBulkLoad;
 | |
| }
 | |
| 
 | |
| int FileOp::flushFile(int rc, std::map<FID, FID>& oids)
 | |
| {
 | |
|     return NO_ERROR;
 | |
| }
 | |
| 
 | |
| int FileOp::updateColumnExtent(IDBDataFile* pFile, int nBlocks)
 | |
| {
 | |
|     return NO_ERROR;
 | |
| }
 | |
| 
 | |
| int FileOp::updateDctnryExtent(IDBDataFile* pFile, int nBlocks)
 | |
| {
 | |
|     return NO_ERROR;
 | |
| }
 | |
| 
 | |
| void FileOp::setFixFlag(bool isFix)
 | |
| {
 | |
|     m_isFix = isFix;
 | |
| }
 | |
| } //end of namespace
 | |
| 
 |