diff --git a/flaim/util/ha_flaim.cpp b/flaim/util/ha_flaim.cpp new file mode 100644 index 0000000..4eb9478 --- /dev/null +++ b/flaim/util/ha_flaim.cpp @@ -0,0 +1,3352 @@ +//------------------------------------------------------------------------- +// Desc: FLAIM handler for MySQL +// Tabs: 3 +// +// Copyright (c) 2006 Novell, Inc. All Rights Reserved. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of version 2 of the GNU General Public +// License as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, contact Novell, Inc. +// +// To contact Novell about this file by physical or electronic mail, +// you may find current contact information at www.novell.com +// +// $Id$ +//------------------------------------------------------------------------- + +#ifdef USE_PRAGMA_IMPLEMENTATION + #pragma implementation +#endif + +#include "mysql_priv.h" + +//#ifdef HAVE_FLAIM + +#include "ha_flaim.h" + +#define FLAIM_DB_NAME "flaim.db" +#define FLM_TABLE_INFO_FIELD_NAME "table_info" +#define FLM_TABLE_INFO_FIELD_ID 1 +#define FLM_ROW_CONTEXT_FIELD_NAME "row" +#define FLM_ROW_CONTEXT_FIELD_ID 2 +#define FLM_ROW_NULL_BITS_FIELD_NAME "null_bits" +#define FLM_ROW_NULL_BITS_FIELD_ID 3 +#define FLM_ROW_COUNT_FIELD_NAME "row_count" +#define FLM_ROW_COUNT_FIELD_ID 4 + +const char * gv_pszBaseDict = + "0 @1@ field " FLM_TABLE_INFO_FIELD_NAME "\n" \ + " 1 type context\n" \ + "0 @2@ field " FLM_ROW_CONTEXT_FIELD_NAME "\n" \ + " 1 type context\n" \ + "0 @3@ field " FLM_ROW_NULL_BITS_FIELD_NAME "\n" \ + " 1 type binary\n" \ + "0 @4@ field " FLM_ROW_COUNT_FIELD_NAME "\n" \ + " 1 type number"; + +static int flaim_commit( + THD * pThread, + bool bAll); + +static int flaim_rollback( + THD * pThread, + bool bAll); + +FlmConnectionTable * gv_pConnTbl = NULL; +pthread_mutex_t gv_hShareMutex; +static HASH gv_openTables; + +/**************************************************************************** +Desc: +****************************************************************************/ +static const char * ha_flaim_exts[] = +{ + NullS +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +handlerton flaim_hton = +{ + "FLAIM", // name + SHOW_OPTION_YES, // state + "FLAIM storage engine", // comment + DB_TYPE_FLAIM, // db_type + flaim_init, // init + 0, // slot + 0, // savepoint size + NULL, // close connection + NULL, // savepoint + NULL, // rollback to savepoint + NULL, // release savepoint + flaim_commit, // commit + flaim_rollback, // rollback + NULL, // prepare + NULL, // recover + NULL, // commit_by_xid + NULL, // rollback_by_xid + NULL, // create_cursor_read_view + NULL, // set_cursor_read_view + NULL, // close_cursor_read_view + HTON_NO_FLAGS // flags +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +inline void buildDbPathFromTablePath( + const char * pszTablePath, + char * pszDbPath) +{ + f_pathReduce( pszTablePath, pszDbPath, NULL); + f_pathAppend( pszDbPath, FLAIM_DB_NAME); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +static byte * flmShareGetKey( + FLAIM_SHARE * pShare, + uint * puiLength, + my_bool not_used __attribute__((unused))) +{ + *puiLength = pShare->uiTablePathLen; + return( (byte *)pShare->pszTablePath); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +static RCODE flmGetShare( + FlmConnection * pConn, + const char * pszTablePath, + TABLE * pTable, + FLAIM_SHARE ** ppShare) +{ + RCODE rc = FERR_OK; + FLAIM_SHARE * pShare; + FLMBOOL bCreatedMutex = FALSE; + FLMBOOL bShareLocked = FALSE; + F_NameTable nameTable; + Field ** ppMyField; + FIELD_INFO * pColumnFields; + FIELD_INFO * pKeyFields; + INDEX_INFO * pIndexes; + FLMUINT uiLoop; + uint uiLength; + char * pszTmpPath; + char szTmpBuf[ 512]; + + *ppShare = NULL; + + pthread_mutex_lock( &gv_hShareMutex); + bShareLocked = TRUE; + + uiLength = (uint)strlen( pszTablePath); + + if( (pShare = (FLAIM_SHARE *)hash_search( &gv_openTables, + (byte *)pszTablePath, uiLength)) == NULL) + { + if( !(pShare = (FLAIM_SHARE *)my_multi_malloc( + MYF( MY_WME | MY_ZEROFILL), &pShare, sizeof( *pShare), + &pszTmpPath, uiLength + 1, + &pColumnFields, pTable->s->fields * sizeof( FIELD_INFO), + &pKeyFields, pTable->s->keys * sizeof( FIELD_INFO), + &pIndexes, pTable->s->keys * sizeof( INDEX_INFO), + NullS))) + { + rc = FERR_MEM; + goto Exit; + } + + pShare->uiUseCount = 0; + pShare->uiTablePathLen = uiLength; + pShare->pszTablePath = pszTmpPath; + strmov( pShare->pszTablePath, pszTablePath); + pShare->pColumnFields = pColumnFields; + pShare->pKeyFields = pKeyFields; + pShare->pIndexes = pIndexes; + + if( RC_BAD( rc = nameTable.setupFromDb( pConn->getDb()))) + { + goto Exit; + } + + sprintf( szTmpBuf, ":%s:", pTable->s->table_name); + + if( !nameTable.getFromTagTypeAndName( NULL, szTmpBuf, + FLM_CONTAINER_TAG, &pShare->uiTableContainer)) + { + rc = FERR_DATA_ERROR; + goto Exit; + } + + for( ppMyField = pTable->field; *ppMyField; ppMyField++) + { + FIELD_INFO * pFieldInfo = &pShare->pColumnFields[ + (*ppMyField)->field_index]; + + sprintf( szTmpBuf, ":%s:col:%s:", + pTable->s->table_name, (*ppMyField)->field_name); + + if( !nameTable.getFromTagTypeAndName( NULL, szTmpBuf, FLM_FIELD_TAG, + &pFieldInfo->uiDictNum, &pFieldInfo->uiDataType)) + { + rc = FERR_DATA_ERROR; + goto Exit; + } + } + + for( uiLoop = 0; uiLoop < pTable->s->keys; uiLoop++) + { + sprintf( szTmpBuf, ":%s:key:%u:", pTable->s->table_name, uiLoop); + + if( !nameTable.getFromTagTypeAndName( NULL, szTmpBuf, FLM_FIELD_TAG, + &pShare->pKeyFields[ uiLoop].uiDictNum, + &pShare->pKeyFields[ uiLoop].uiDataType)) + { + rc = FERR_DATA_ERROR; + goto Exit; + } + + sprintf( szTmpBuf, ":%s:index:%u:", pTable->s->table_name, uiLoop); + + if( !nameTable.getFromTagTypeAndName( NULL, szTmpBuf, FLM_INDEX_TAG, + &pShare->pIndexes[ uiLoop].uiDictNum, NULL)) + { + rc = FERR_DATA_ERROR; + goto Exit; + } + } + + if( my_hash_insert( &gv_openTables, (byte *)pShare)) + { + rc = FERR_FAILURE; + goto Exit; + } + + thr_lock_init( &pShare->lock); + bCreatedMutex = TRUE; + pthread_mutex_init( &pShare->hMutex, MY_MUTEX_INIT_FAST); + } + + pShare->uiUseCount++; + pthread_mutex_unlock( &gv_hShareMutex); + bShareLocked = FALSE; + + *ppShare = pShare; + +Exit: + + if( RC_BAD( rc)) + { + if( pShare) + { + if( bCreatedMutex) + { + pthread_mutex_destroy( &pShare->hMutex); + } + + my_free( (gptr)pShare, MYF(0)); + } + } + + if( bShareLocked) + { + pthread_mutex_unlock( &gv_hShareMutex); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +static int flmReleaseShare( + FLAIM_SHARE * pShare) +{ + pthread_mutex_lock( &gv_hShareMutex); + if( --pShare->uiUseCount == 0) + { + hash_delete( &gv_openTables, (byte *)pShare); + thr_lock_delete( &pShare->lock); + pthread_mutex_destroy( &pShare->hMutex); + my_free( (gptr)pShare, MYF(0)); + } + + pthread_mutex_unlock( &gv_hShareMutex); + return( 0); +} +/**************************************************************************** +Desc: +****************************************************************************/ +bool flaim_init( void) +{ + RCODE rc = FERR_OK; + FLMBOOL bCleanup = FALSE; + + DBUG_ENTER( "flmInit"); + + if( have_flaim != SHOW_OPTION_YES) + { + goto Exit; + } + + if( RC_BAD( rc = FlmStartup())) + { + goto Exit; + } + + bCleanup = TRUE; + + if( (gv_pConnTbl = new FlmConnectionTable) == NULL) + { + rc = FERR_FAILURE; + goto Exit; + } + + if( RC_BAD( rc = gv_pConnTbl->setup())) + { + goto Exit; + } + + if( (pthread_mutex_init( &gv_hShareMutex, MY_MUTEX_INIT_FAST)) != 0) + { + rc = FERR_FAILURE; + goto Exit; + } + + if( (hash_init( &gv_openTables, system_charset_info, 32, 0, 0, + (hash_get_key)flmShareGetKey, 0, 0)) != 0) + { + rc = FERR_FAILURE; + goto Exit; + } + + DBUG_RETURN( FALSE); + +Exit: + + if( bCleanup) + { + flaim_end(); + } + + have_flaim = SHOW_OPTION_DISABLED; + DBUG_RETURN( TRUE); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +bool flaim_end( void) +{ + DBUG_ENTER( "flaim_end"); + + if( gv_pConnTbl) + { + gv_pConnTbl->Release(); + gv_pConnTbl = NULL; + } + + FlmShutdown(); + DBUG_RETURN( FALSE); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +ha_flaim::ha_flaim( TABLE * table_arg) : handler( &flaim_hton, table_arg) +{ + m_pConn = NULL; + m_pShare = NULL; + m_uiCurrRowDrn = 0; + active_index = MAX_KEY; + m_pCurrKey = NULL; + m_szDbPath[ 0] = 0; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +const char ** ha_flaim::bas_ext( void) const +{ + return( ha_flaim_exts); +} + +/**************************************************************************** +Desc: Used for opening tables. The name will be the name of the file. + A table is opened when it needs to be opened. For instance + when a request comes in for a select on the table (tables are not + open and closed for each request, they are cached). +****************************************************************************/ +int ha_flaim::open( + const char * pszTablePath, + int iMode, + uint bTestIfLocked) +{ + RCODE rc = FERR_OK; + + DBUG_ENTER( "ha_flaim::open"); + + buildDbPathFromTablePath( pszTablePath, m_szDbPath); + + if( RC_BAD( rc = gv_pConnTbl->getConnection( m_szDbPath, &m_pConn))) + { + goto Exit; + } + + if( RC_BAD( rc = flmGetShare( m_pConn, pszTablePath, table, &m_pShare))) + { + goto Exit; + } + + thr_lock_data_init( &m_pShare->lock, &m_lockData, NULL); + ref_length = sizeof( FLMUINT32); + +Exit: + + DBUG_RETURN( rc); +} + +/**************************************************************************** +Desc: Closes a table +****************************************************************************/ +int ha_flaim::close(void) +{ + DBUG_ENTER( "ha_flaim::close"); + + flmReleaseShare( m_pShare); + m_pShare = NULL; + m_szDbPath[ 0] = 0; + m_uiCurrRowDrn = 0; + active_index = MAX_KEY; + + if( m_pConn) + { + m_pConn->Release(); + m_pConn = NULL; + } + + if( m_pCurrKey) + { + m_pCurrKey->Release(); + m_pCurrKey = NULL; + } + + DBUG_RETURN( 0); +} + +/**************************************************************************** +Desc: Inserts a row. The buffer is a byte array of data. The field + information can be used to extract the data from the native byte + array. +****************************************************************************/ +int ha_flaim::write_row( + byte * pucData) +{ + RCODE rc = FERR_OK; + FlmRecord * pRec = NULL; + FLMBOOL bMustAbortOnError = FALSE; + + DBUG_ENTER( "ha_flaim::write_row"); + + if( RC_BAD( rc = gv_pConnTbl->getConnection( m_szDbPath, &m_pConn))) + { + goto Exit; + } + + if( RC_BAD( rc = exportRowToRec( pucData, &pRec))) + { + goto Exit; + } + + bMustAbortOnError = TRUE; + + m_uiCurrRowDrn = 0; + if( RC_BAD( rc = FlmRecordAdd( m_pConn->getDb(), + m_pShare->uiTableContainer, &m_uiCurrRowDrn, pRec, 0))) + { + if( rc == FERR_NOT_UNIQUE) + { + rc = (RCODE)HA_ERR_FOUND_DUPP_KEY; + bMustAbortOnError = FALSE; + } + + goto Exit; + } + + if( RC_BAD( rc = m_pConn->incrementRowCount( + m_pShare->uiTableContainer))) + { + goto Exit; + } + + statistic_increment( table->in_use->status_var.ha_write_count, + &LOCK_status); + + // If we have a timestamp column, update it to the current time + + if( table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT) + { + table->timestamp_field->set_time(); + } + + // If we have an auto_increment column and we are writing a changed row + // or a new row, then update the auto_increment value in the record. + + if( table->next_number_field && pucData == table->record[ 0]) + { + update_auto_increment(); + } + +Exit: + + if( pRec) + { + pRec->Release(); + } + + if( RC_BAD( rc) && bMustAbortOnError) + { + m_pConn->setAbortFlag(); + } + + DBUG_RETURN( rc); +} + +/**************************************************************************** +Desc: Updates a row. old_data will have the previous row record in + it, while new_data will have the newest data in it. +****************************************************************************/ +int ha_flaim::update_row( + const byte * pucOldData, + byte * pucNewData) +{ + RCODE rc = FERR_OK; + FlmRecord * pRec = NULL; + FLMBOOL bMustAbortOnError = FALSE; + + DBUG_ENTER( "ha_flaim::update_row"); + + if( RC_BAD( rc = gv_pConnTbl->getConnection( m_szDbPath, &m_pConn))) + { + goto Exit; + } + + if( RC_BAD( rc = exportRowToRec( pucNewData, &pRec))) + { + goto Exit; + } + + bMustAbortOnError = TRUE; + + if( RC_BAD( rc = FlmRecordModify( m_pConn->getDb(), + m_pShare->uiTableContainer, m_uiCurrRowDrn, pRec, 0))) + { + if( rc == FERR_NOT_UNIQUE) + { + rc = (RCODE)HA_ERR_FOUND_DUPP_KEY; + bMustAbortOnError = FALSE; + } + + goto Exit; + } + + statistic_increment(table->in_use->status_var.ha_read_rnd_next_count, + &LOCK_status); + + // If we have a timestamp column, update it to the current time + + if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE) + { + table->timestamp_field->set_time(); + } + +Exit: + + if( pRec) + { + pRec->Release(); + } + + if( RC_BAD( rc) && bMustAbortOnError) + { + m_pConn->setAbortFlag(); + } + + DBUG_RETURN( rc); +} + +/**************************************************************************** +Desc: This will delete a row. pucData will contain a copy of the row to + be deleted. +****************************************************************************/ +int ha_flaim::delete_row( + const byte * pucData) +{ + RCODE rc = FERR_OK; + FLMBOOL bMustAbortOnError = FALSE; + + DBUG_ENTER( "ha_flaim::delete_row"); + + if( RC_BAD( rc = gv_pConnTbl->getConnection( m_szDbPath, &m_pConn))) + { + goto Exit; + } + + bMustAbortOnError = TRUE; + + if( RC_BAD( rc = FlmRecordDelete( m_pConn->getDb(), + m_pShare->uiTableContainer, m_uiCurrRowDrn, 0))) + { + goto Exit; + } + + if( RC_BAD( rc = m_pConn->decrementRowCount( + m_pShare->uiTableContainer))) + { + goto Exit; + } + + statistic_increment( table->in_use->status_var.ha_delete_count, + &LOCK_status); + +Exit: + + if( RC_BAD( rc) && bMustAbortOnError) + { + m_pConn->setAbortFlag(); + } + + DBUG_RETURN( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +int ha_flaim::index_init( + uint uiIndex) +{ + DBUG_ENTER( "ha_flaim::index_init"); + + active_index = uiIndex; + assert( m_pCurrKey == NULL); + + DBUG_RETURN( 0); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +int ha_flaim::index_end( void) +{ + DBUG_ENTER( "ha_flaim::index_end"); + + active_index = MAX_KEY; + + if( m_pCurrKey) + { + m_pCurrKey->Release(); + m_pCurrKey = NULL; + } + + DBUG_RETURN( 0); +} + +/**************************************************************************** +Desc: Positions an index cursor to the index specified in the handle. + Fetches the row if available. If the key value is null, begin at + the first key of the index. +****************************************************************************/ +int ha_flaim::index_read( + byte * pucData, + const byte * pucKey, + uint uiKeyLen, + enum ha_rkey_function eFindFlag) +{ + RCODE rc = FERR_OK; + FlmRecord * pKeyRec = NULL; + FlmRecord * pRec = NULL; + FLMUINT uiReference; + FLMUINT uiFindFlags = 0; + void * pvField; + + DBUG_ENTER( "ha_flaim::index_read"); + + if( RC_BAD( rc = gv_pConnTbl->getConnection( m_szDbPath, &m_pConn))) + { + goto Exit; + } + + if( pucKey) + { + if( RC_BAD( rc = exportKeyToTree( active_index, + pucKey, uiKeyLen, &pKeyRec))) + { + goto Exit; + } + + switch( eFindFlag) + { + case HA_READ_KEY_EXACT: + { + uiFindFlags = FO_INCL; + break; + } + + case HA_READ_KEY_OR_NEXT: + { + uiFindFlags = FO_INCL; + break; + } + + case HA_READ_AFTER_KEY: + { + uiFindFlags = FO_EXCL; + break; + } + + default: + { + rc = (RCODE)HA_ERR_WRONG_COMMAND; + goto Exit; + } + } + } + else + { + uiFindFlags = FO_FIRST; + } + + if( RC_BAD( rc = FlmKeyRetrieve( m_pConn->getDb(), + m_pShare->pIndexes[ active_index].uiDictNum, 0, + pKeyRec, 0, uiFindFlags, &m_pCurrKey, &uiReference))) + { + if( rc == FERR_NOT_FOUND) + { + rc = (RCODE)HA_ERR_KEY_NOT_FOUND; + table->status = STATUS_NOT_FOUND; + } + else if( rc == FERR_EOF_HIT) + { + rc = (RCODE)HA_ERR_END_OF_FILE; + table->status = STATUS_NOT_FOUND; + } + + goto Exit; + } + + if( eFindFlag == HA_READ_KEY_EXACT) + { + FLMUINT uiTmpKeyLen = uiKeyLen; + char * pucBuf = m_pucKeyBuf; + char * pucBufEnd = &m_pucKeyBuf[ FLM_MAX_KEY_LENGTH]; + KEY * pKey = table->key_info + active_index; + KEY_PART_INFO * pCurKeyPart = pKey->key_part; + KEY_PART_INFO * pEndKeyPart = pCurKeyPart + pKey->key_parts; + + if( (pvField = m_pCurrKey->find( m_pCurrKey->root(), + m_pShare->pKeyFields[ active_index].uiDictNum)) == NULL) + { + rc = FERR_DATA_ERROR; + goto Exit; + } + + for( ; pCurKeyPart != pEndKeyPart && uiTmpKeyLen > 0; pCurKeyPart++) + { + FLMUINT uiOffset = 0; + + if( pCurKeyPart->null_bit) + { + *pucBuf++ = *pucKey ? 1 : 0; + uiTmpKeyLen -= pCurKeyPart->store_length; + pucKey += pCurKeyPart->store_length; + uiOffset = 1; + } + + pucBuf = pCurKeyPart->field->pack_key_from_key_image( pucBuf, + (const char *)pucKey + uiOffset, pCurKeyPart->length); + pucKey += pCurKeyPart->store_length; + uiTmpKeyLen -= pCurKeyPart->store_length; + + assert( pucBuf <= pucBufEnd); + } + + uiTmpKeyLen = pucBuf - m_pucKeyBuf; + + if( m_pCurrKey->getDataLength( pvField) < uiTmpKeyLen || + memcmp( m_pCurrKey->getDataPtr( pvField), m_pucKeyBuf, uiTmpKeyLen) != 0) + { + rc = (RCODE)HA_ERR_END_OF_FILE; + table->status = STATUS_NOT_FOUND; + goto Exit; + } + } + + if( RC_BAD( rc = FlmRecordRetrieve( m_pConn->getDb(), + m_pShare->uiTableContainer, uiReference, + FO_EXACT, &pRec, &m_uiCurrRowDrn))) + { + goto Exit; + } + + if( RC_BAD( rc = importRowFromRec( pRec, pucData))) + { + goto Exit; + } + + table->status = 0; + +Exit: + + if( pRec) + { + pRec->Release(); + } + + if( pKeyRec) + { + pKeyRec->Release(); + } + + if( RC_BAD( rc)) + { + m_uiCurrRowDrn = 0; + } + + DBUG_RETURN( rc); +} + +/**************************************************************************** +Desc: Used to read forward through the index +****************************************************************************/ +int ha_flaim::index_next( + byte * pucData) +{ + RCODE rc = FERR_OK; + FLMUINT uiReference; + FlmRecord * pRec = NULL; + + DBUG_ENTER( "ha_flaim::index_read"); + + if( RC_BAD( rc = gv_pConnTbl->getConnection( m_szDbPath, &m_pConn))) + { + goto Exit; + } + + if( RC_BAD( rc = FlmKeyRetrieve( m_pConn->getDb(), + m_pShare->pIndexes[ active_index].uiDictNum, 0, + m_pCurrKey, 0, FO_EXCL, &m_pCurrKey, &uiReference))) + { + if( rc == FERR_NOT_FOUND) + { + rc = (RCODE)HA_ERR_KEY_NOT_FOUND; + table->status = STATUS_NOT_FOUND; + } + else if( rc == FERR_EOF_HIT) + { + rc = (RCODE)HA_ERR_END_OF_FILE; + table->status = STATUS_NOT_FOUND; + } + + goto Exit; + } + + if( RC_BAD( rc = FlmRecordRetrieve( m_pConn->getDb(), + m_pShare->uiTableContainer, uiReference, + FO_EXACT, &pRec, &m_uiCurrRowDrn))) + { + goto Exit; + } + + if( RC_BAD( rc = importRowFromRec( pRec, pucData))) + { + goto Exit; + } + + table->status = 0; + +Exit: + + if( pRec) + { + pRec->Release(); + } + + DBUG_RETURN( rc); +} + +/**************************************************************************** +Desc: Returns the first key in the index +****************************************************************************/ +int ha_flaim::index_first( + byte * pucData) +{ + RCODE rc = FERR_OK; + FlmRecord * pRec = NULL; + FLMUINT uiReference; + + DBUG_ENTER( "ha_flaim::index_first"); + + if( RC_BAD( rc = gv_pConnTbl->getConnection( m_szDbPath, &m_pConn))) + { + goto Exit; + } + + if( RC_BAD( rc = FlmKeyRetrieve( m_pConn->getDb(), + m_pShare->pIndexes[ active_index].uiDictNum, 0, + NULL, 0, FO_FIRST, &m_pCurrKey, &uiReference))) + { + if( rc == FERR_NOT_FOUND) + { + rc = (RCODE)HA_ERR_KEY_NOT_FOUND; + table->status = STATUS_NOT_FOUND; + } + else if( rc == FERR_EOF_HIT) + { + rc = (RCODE)HA_ERR_END_OF_FILE; + table->status = STATUS_NOT_FOUND; + } + + goto Exit; + } + + if( RC_BAD( rc = FlmRecordRetrieve( m_pConn->getDb(), + m_pShare->uiTableContainer, uiReference, + FO_EXACT, &pRec, &m_uiCurrRowDrn))) + { + goto Exit; + } + + if( RC_BAD( rc = importRowFromRec( pRec, pucData))) + { + goto Exit; + } + + table->status = 0; + +Exit: + + if( pRec) + { + pRec->Release(); + } + + if( RC_BAD( rc)) + { + m_uiCurrRowDrn = 0; + } + + DBUG_RETURN( rc); +} + +/**************************************************************************** +Desc: Returns the last key in the index +****************************************************************************/ +int ha_flaim::index_last( + byte * pucData) +{ + RCODE rc = FERR_OK; + FlmRecord * pRec = NULL; + FLMUINT uiReference; + + DBUG_ENTER( "ha_flaim::index_last"); + + if( RC_BAD( rc = gv_pConnTbl->getConnection( m_szDbPath, &m_pConn))) + { + goto Exit; + } + + if( RC_BAD( rc = FlmKeyRetrieve( m_pConn->getDb(), + m_pShare->pIndexes[ active_index].uiDictNum, 0, + NULL, 0, FO_LAST, &m_pCurrKey, &uiReference))) + { + if( rc == FERR_NOT_FOUND) + { + rc = (RCODE)HA_ERR_KEY_NOT_FOUND; + table->status = STATUS_NOT_FOUND; + } + else if( rc == FERR_EOF_HIT) + { + rc = (RCODE)HA_ERR_END_OF_FILE; + table->status = STATUS_NOT_FOUND; + } + + goto Exit; + } + + if( RC_BAD( rc = FlmRecordRetrieve( m_pConn->getDb(), + m_pShare->uiTableContainer, uiReference, + FO_EXACT, &pRec, &m_uiCurrRowDrn))) + { + goto Exit; + } + + if( RC_BAD( rc = importRowFromRec( pRec, pucData))) + { + goto Exit; + } + + table->status = 0; + +Exit: + + if( pRec) + { + pRec->Release(); + } + + if( RC_BAD( rc)) + { + m_uiCurrRowDrn = 0; + } + + DBUG_RETURN( rc); +} + +/**************************************************************************** +Desc: Called when the system wants the storage engine to do a table scan. +****************************************************************************/ +int ha_flaim::rnd_init( + bool scan) +{ + DBUG_ENTER( "ha_flaim::rnd_init"); + m_uiCurrRowDrn = 0; + DBUG_RETURN( 0); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +int ha_flaim::rnd_end( void) +{ + DBUG_ENTER( "ha_flaim::rnd_end"); + m_uiCurrRowDrn = 0; + DBUG_RETURN( 0); +} + +/**************************************************************************** +Desc: This is called for each row of the table scan. When the end of the + table is hit, HA_ERR_END_OF_FILE is returned. The buffer is filled + with the row information. +****************************************************************************/ +int ha_flaim::rnd_next( + byte * pucData) +{ + RCODE rc = FERR_OK; + FlmRecord * pRec = NULL; + + DBUG_ENTER( "ha_flaim::rnd_next"); + + if( RC_BAD( rc = gv_pConnTbl->getConnection( + m_szDbPath, &m_pConn))) + { + goto Exit; + } + + if( RC_BAD( rc = FlmRecordRetrieve( m_pConn->getDb(), + m_pShare->uiTableContainer, + m_uiCurrRowDrn, FO_EXCL, &pRec, &m_uiCurrRowDrn))) + { + if( rc == FERR_EOF_HIT) + { + m_uiCurrRowDrn = 0; + rc = (RCODE)HA_ERR_END_OF_FILE; + table->status = STATUS_NOT_FOUND; + } + + goto Exit; + } + + if( RC_BAD( rc = importRowFromRec( pRec, pucData))) + { + goto Exit; + } + + table->status = 0; + +Exit: + + if( pRec) + { + pRec->Release(); + } + + DBUG_RETURN( rc); +} + +/**************************************************************************** +Desc: Called after each call to rnd_next() if the data needs + to be ordered. +****************************************************************************/ +void ha_flaim::position( const byte *record) +{ + DBUG_ENTER( "ha_flaim::position"); + my_store_ptr( ref, ref_length, m_uiCurrRowDrn); + DBUG_VOID_RETURN; +} + +/**************************************************************************** +Desc: This is like rnd_next, but a position is specified and is used to + determine the row. The position will be of the type that was stored + in position(). +****************************************************************************/ +int ha_flaim::rnd_pos( + byte * pucData, + byte * pucPos) +{ + RCODE rc = FERR_OK; + FlmRecord * pRec = NULL; + FLMUINT uiLength; + + DBUG_ENTER( "ha_flaim::rnd_pos"); + + statistic_increment( table->in_use->status_var.ha_read_rnd_next_count, + &LOCK_status); + + if( RC_BAD( rc = gv_pConnTbl->getConnection( + m_szDbPath, &m_pConn))) + { + goto Exit; + } + + m_uiCurrRowDrn = (FLMUINT)my_get_ptr( pucPos, ref_length); + + if( RC_BAD( rc = FlmRecordRetrieve( m_pConn->getDb(), + m_pShare->uiTableContainer, + m_uiCurrRowDrn, FO_EXACT, &pRec, NULL))) + { + goto Exit; + } + + uiLength = table->s->reclength; + if( RC_BAD( rc = pRec->getBinary( pRec->root(), + pucData, &uiLength))) + { + goto Exit; + } + +Exit: + + if( pRec) + { + pRec->Release(); + } + + DBUG_RETURN( rc); +} + +/**************************************************************************** +Desc: Used to return information to the optimizer. +****************************************************************************/ +void ha_flaim::info( + uint uiFlag) +{ + RCODE rc = FERR_OK; + + DBUG_ENTER( "ha_flaim::info"); + + if( RC_BAD( rc = gv_pConnTbl->getConnection( m_szDbPath, &m_pConn))) + { + goto Exit; + } + + if( RC_BAD( rc = m_pConn->retrieveRowCount( + m_pShare->uiTableContainer, &records))) + { + goto Exit; + } + +Exit: + + if( RC_BAD( rc)) + { + records = 2; + } + + DBUG_VOID_RETURN; +} + +/**************************************************************************** +Desc: Called whenever the server wishes to send a hint to the storage + engine. +****************************************************************************/ +int ha_flaim::extra( + enum ha_extra_function eOperation) +{ + DBUG_ENTER( "ha_flaim::extra"); + DBUG_RETURN( 0); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +int ha_flaim::reset( void) +{ + DBUG_ENTER( "ha_flaim::reset"); + DBUG_RETURN( 0); +} + +/**************************************************************************** +Desc: Used to delete all rows in a table +****************************************************************************/ +int ha_flaim::delete_all_rows( void) +{ + RCODE rc = FERR_OK; + FlmRecord * pRec = NULL; + FLMBOOL bMustAbortOnError = FALSE; + + DBUG_ENTER( "ha_flaim::delete_all_rows"); + + if( RC_BAD( rc = gv_pConnTbl->getConnection( + m_szDbPath, &m_pConn))) + { + goto Exit; + } + + if( RC_BAD( rc = FlmRecordRetrieve( m_pConn->getDb(), + FLM_DICT_CONTAINER, + m_pShare->uiTableContainer, FO_EXACT, &pRec, NULL))) + { + goto Exit; + } + + bMustAbortOnError = TRUE; + + if( RC_BAD( rc = FlmRecordDelete( m_pConn->getDb(), + FLM_DICT_CONTAINER, + m_pShare->uiTableContainer, 0))) + { + goto Exit; + } + + if( pRec->isReadOnly()) + { + FlmRecord * pTmpRec; + + if( (pTmpRec = pRec->copy()) == NULL) + { + rc = FERR_MEM; + goto Exit; + } + + pRec->Release(); + pRec = pTmpRec; + } + + if( RC_BAD( rc = FlmRecordAdd( m_pConn->getDb(), + FLM_DICT_CONTAINER, + &m_pShare->uiTableContainer, pRec, 0))) + { + goto Exit; + } + + if( RC_BAD( rc = m_pConn->storeRowCount( + m_pShare->uiTableContainer, 0))) + { + goto Exit; + } + + m_uiCurrRowDrn = 0; + +Exit: + + if( pRec) + { + pRec->Release(); + } + + if( RC_BAD( rc) && bMustAbortOnError) + { + m_pConn->setAbortFlag(); + } + + DBUG_RETURN( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +int ha_flaim::external_lock( + THD * pThread, + int iLockType) +{ + RCODE rc = FERR_OK; + FLMBOOL bDecLockCountOnError = FALSE; + + DBUG_ENTER( "ha_flaim::external_lock"); + + if( RC_BAD( rc = gv_pConnTbl->getConnection( + m_szDbPath, &m_pConn))) + { + goto Exit; + } + + if( iLockType != F_UNLCK) + { + if( iLockType == F_WRLCK && + (m_pConn->getTransType() == FLM_READ_TRANS || + m_pConn->getTransTypeNeeded() == FLM_READ_TRANS)) + { + rc = (RCODE)HA_ERR_READ_ONLY_TRANSACTION; + goto Exit; + } + + if( m_pConn->incLockCount() == 1 && + m_pConn->getTransType() == FLM_NO_TRANS) + { + bDecLockCountOnError = TRUE; + + if( RC_BAD( rc = FlmDbTransBegin( m_pConn->getDb(), + m_pConn->getTransTypeNeeded(), FLM_NO_TIMEOUT))) + { + goto Exit; + } + + m_pConn->setTransTypeNeeded( FLM_NO_TRANS); + + if( pThread->options & (OPTION_BEGIN | OPTION_NOT_AUTOCOMMIT)) + { + trans_register_ha( pThread, TRUE, &flaim_hton); + + // Since this transaction has been registered with + // MySQL, flaim_commit or flaim_rollback will be called + // when appropriate. To prevent external_unlock from + // releasing the transaction too soon, increment the + // lock count again. This allows us to handle the case + // of a multi-statement transaction. + + m_pConn->incLockCount(); + } + } + } + else + { + m_lockData.type = TL_UNLOCK; + + if( m_pConn->getLockCount() != 0 && + m_pConn->decLockCount() == 0 && + m_pConn->getTransType() != FLM_NO_TRANS) + { + if( m_pConn->getAbortFlag()) + { + FlmDbTransAbort( m_pConn->getDb()); + m_pConn->clearAbortFlag(); + } + else + { + if( RC_BAD( rc = FlmDbTransCommit( m_pConn->getDb()))) + { + goto Exit; + } + } + } + } + +Exit: + + if( RC_BAD( rc)) + { + if( bDecLockCountOnError) + { + m_pConn->decLockCount(); + } + } + + DBUG_RETURN( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +int flaim_start_trx_and_assign_read_view( + THD * pThread) +{ + RCODE rc = FERR_OK; + FlmConnection * pConn = NULL; + char szDbPath[ F_PATH_MAX_SIZE]; + + DBUG_ENTER( "flaim_start_trx_and_assign_read_view"); + + szDbPath[ 0] = 0; + f_pathAppend( szDbPath, "."); + f_pathAppend( szDbPath, pThread->db); + f_pathAppend( szDbPath, FLAIM_DB_NAME); + + if( RC_BAD( rc = gv_pConnTbl->getConnection( szDbPath, &pConn))) + { + goto Exit; + } + + if( pConn->getLockCount() == 0 && + pConn->getTransType() == FLM_NO_TRANS) + { + if( RC_BAD( rc = FlmDbTransBegin( pConn->getDb(), + FLM_READ_TRANS, FLM_NO_TIMEOUT))) + { + goto Exit; + } + + pConn->setTransTypeNeeded( FLM_NO_TRANS); + trans_register_ha( pThread, TRUE, &flaim_hton); + pConn->incLockCount(); + } + +Exit: + + if( pConn) + { + pConn->Release(); + } + + DBUG_RETURN( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +THR_LOCK_DATA ** ha_flaim::store_lock( + THD * pThread, + THR_LOCK_DATA ** pLockData, + enum thr_lock_type eLockType) +{ + if( RC_BAD( gv_pConnTbl->getConnection( + m_szDbPath, &m_pConn))) + { + assert( 0); + } + + if( eLockType != TL_IGNORE && m_lockData.type == TL_UNLOCK) + { + if( eLockType >= TL_WRITE_ALLOW_WRITE && + eLockType <= TL_WRITE_ONLY) + { + eLockType = TL_WRITE_ALLOW_READ; + } + else if( eLockType >= TL_READ && eLockType <= TL_READ_NO_INSERT) + { + eLockType = TL_READ_HIGH_PRIORITY; + } + + if( eLockType == TL_WRITE_ALLOW_READ || + (pThread->options & (OPTION_BEGIN | OPTION_NOT_AUTOCOMMIT))) + { + eLockType = TL_WRITE_ALLOW_READ; + if( m_pConn->getTransType() == FLM_NO_TRANS) + { + m_pConn->setTransTypeNeeded( FLM_UPDATE_TRANS); + } + } + else if( m_pConn->getTransTypeNeeded() == FLM_NO_TRANS) + { + eLockType = TL_READ_HIGH_PRIORITY; + if( m_pConn->getTransType() == FLM_NO_TRANS) + { + m_pConn->setTransTypeNeeded( FLM_READ_TRANS); + } + } + + m_lockData.type = eLockType; + } + + *pLockData++ = &m_lockData; + return( pLockData); +} + +/**************************************************************************** +Desc: Renames a table +****************************************************************************/ +int ha_flaim::rename_table( + const char * pszOldName, + const char * pszNewName) +{ + RCODE rc = FERR_OK; + FLMUINT uiDictNum; + F_NameTable nameTable; + FlmRecord * pDictRec = NULL; + char szOldPrefix[ F_PATH_MAX_SIZE]; + char szNewPrefix[ F_PATH_MAX_SIZE]; + char szTmpBuf[ F_PATH_MAX_SIZE]; + char szNewName[ F_PATH_MAX_SIZE]; + int iCmp; + FLMUINT uiOldPrefixLen; + FLMUINT uiNewPrefixLen; + FLMUINT uiLoop; + FLMBOOL bStartedTrans = FALSE; + FLMBOOL bMustAbortOnError = FALSE; + + DBUG_ENTER( "ha_flaim::rename_table"); + + buildDbPathFromTablePath( pszOldName, m_szDbPath); + + if( RC_BAD( rc = gv_pConnTbl->getConnection( + m_szDbPath, &m_pConn))) + { + goto Exit; + } + + if( m_pConn->getTransType() != FLM_UPDATE_TRANS) + { + if( RC_BAD( rc = FlmDbTransBegin( m_pConn->getDb(), + FLM_UPDATE_TRANS, FLM_NO_TIMEOUT))) + { + goto Exit; + } + + bStartedTrans = TRUE; + } + + if( RC_BAD( rc = nameTable.setupFromDb( m_pConn->getDb()))) + { + goto Exit; + } + + f_pathReduce( pszOldName, NULL, szTmpBuf); + sprintf( szOldPrefix, ":%s:", szTmpBuf); + uiOldPrefixLen = strlen( szOldPrefix); + + f_pathReduce( pszNewName, NULL, szTmpBuf); + sprintf( szNewPrefix, ":%s:", szTmpBuf); + uiNewPrefixLen = strlen( szNewPrefix); + + // From this point forward, any errors must cause the + // transaction to abort + + bMustAbortOnError = TRUE; + + // Rename indexes, fields, and the table container + + uiLoop = 0; + for( ;;) + { + if( !nameTable.getNextTagNameOrder( &uiLoop, NULL, szTmpBuf, + sizeof( szTmpBuf), &uiDictNum, NULL, NULL)) + { + break; + } + + if( (iCmp = strnicmp( szTmpBuf, szOldPrefix, uiOldPrefixLen)) > 0) + { + break; + } + + if( iCmp == 0) + { + if( RC_BAD( rc = FlmRecordRetrieve( m_pConn->getDb(), + FLM_DICT_CONTAINER, uiDictNum, FO_EXACT, &pDictRec, NULL))) + { + goto Exit; + } + + if( pDictRec->isReadOnly()) + { + FlmRecord * pTmpRec; + + if( (pTmpRec = pDictRec->copy()) == NULL) + { + rc = FERR_MEM; + goto Exit; + } + + pDictRec->Release(); + pDictRec = pTmpRec; + } + + sprintf( szNewName, "%s%s", szNewPrefix, + strchr( &szTmpBuf[ 1], ':') + 1); + + if( RC_BAD( rc = pDictRec->setNative( + pDictRec->root(), szNewName))) + { + goto Exit; + } + + if( RC_BAD( rc = FlmRecordModify( m_pConn->getDb(), + FLM_DICT_CONTAINER, uiDictNum, pDictRec, 0))) + { + goto Exit; + } + } + } + + if( bStartedTrans) + { + bStartedTrans = FALSE; + + if( RC_BAD( rc = FlmDbTransCommit( m_pConn->getDb()))) + { + goto Exit; + } + } + +Exit: + + if( pDictRec) + { + pDictRec->Release(); + } + + if( RC_BAD( rc) && bMustAbortOnError) + { + m_pConn->setAbortFlag(); + } + + DBUG_RETURN( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +int ha_flaim::delete_table( + const char * pszTablePath) +{ + RCODE rc = FERR_OK; + FLMUINT uiDictNum; + F_NameTable nameTable; + FlmRecord * pDictRec = NULL; + char szPrefix[ F_PATH_MAX_SIZE]; + char szTmpBuf[ F_PATH_MAX_SIZE]; + int iCmp; + void * pvField; + FLMUINT uiPrefixLen; + FLMUINT uiLoop; + FLMUINT uiDefType; + FLMBOOL bSweep = FALSE; + FLMBOOL bStartedTrans = FALSE; + FLMBOOL bMustAbortOnError = FALSE; + + DBUG_ENTER( "ha_flaim::delete_table"); + + buildDbPathFromTablePath( pszTablePath, m_szDbPath); + + if( RC_BAD( rc = gv_pConnTbl->getConnection( + m_szDbPath, &m_pConn))) + { + goto Exit; + } + + if( m_pConn->getTransType() != FLM_UPDATE_TRANS) + { + if( RC_BAD( rc = FlmDbTransBegin( m_pConn->getDb(), + FLM_UPDATE_TRANS, FLM_NO_TIMEOUT))) + { + goto Exit; + } + + bStartedTrans = TRUE; + } + + if( RC_BAD( rc = nameTable.setupFromDb( m_pConn->getDb()))) + { + goto Exit; + } + + f_pathReduce( pszTablePath, NULL, szTmpBuf); + sprintf( szPrefix, ":%s:", szTmpBuf); + uiPrefixLen = strlen( szPrefix); + + // From this point forward, any errors must cause the + // transaction to abort + + bMustAbortOnError = TRUE; + + // Drop indexes and mark fields for deletion + + uiLoop = 0; + for( ;;) + { + if( !nameTable.getNextTagNameOrder( &uiLoop, NULL, szTmpBuf, + sizeof( szTmpBuf), &uiDictNum, &uiDefType, NULL)) + { + break; + } + + if( (iCmp = strnicmp( szTmpBuf, szPrefix, uiPrefixLen)) > 0) + { + break; + } + + if( iCmp == 0) + { + if( uiDefType == FLM_FIELD_TAG) + { + if( RC_BAD( rc = FlmRecordRetrieve( m_pConn->getDb(), + FLM_DICT_CONTAINER, uiDictNum, FO_EXACT, &pDictRec, NULL))) + { + goto Exit; + } + + if( pDictRec->isReadOnly()) + { + FlmRecord * pTmpRec; + + if( (pTmpRec = pDictRec->copy()) == NULL) + { + rc = FERR_MEM; + goto Exit; + } + + pDictRec->Release(); + pDictRec = pTmpRec; + } + + if( RC_BAD( rc = pDictRec->insertLast( 1, FLM_STATE_TAG, + FLM_TEXT_TYPE, &pvField))) + { + goto Exit; + } + + if( RC_BAD( rc = pDictRec->setNative( pvField, "purge"))) + { + goto Exit; + } + + if( RC_BAD( rc = FlmRecordModify( m_pConn->getDb(), + FLM_DICT_CONTAINER, uiDictNum, pDictRec, 0))) + { + goto Exit; + } + + bSweep = TRUE; + } + else if( uiDefType == FLM_INDEX_TAG) + { + if( RC_BAD( rc = FlmRecordDelete( m_pConn->getDb(), + FLM_DICT_CONTAINER, uiDictNum, 0))) + { + goto Exit; + } + } + } + } + + // Drop the table container + + if( !nameTable.getFromTagTypeAndName( NULL, szPrefix, + FLM_CONTAINER_TAG, &uiDictNum)) + { + rc = FERR_DATA_ERROR; + goto Exit; + } + + if( RC_BAD( rc = FlmRecordDelete( m_pConn->getDb(), + FLM_DICT_CONTAINER, uiDictNum, 0))) + { + goto Exit; + } + + // Delete the table info record + + if( RC_BAD( rc = FlmRecordDelete( m_pConn->getDb(), + FLM_DATA_CONTAINER, uiDictNum, 0))) + { + if( rc != FERR_NOT_FOUND) + { + goto Exit; + } + + rc = FERR_OK; + } + + if( bStartedTrans) + { + bStartedTrans = FALSE; + + if( RC_BAD( rc = FlmDbTransCommit( m_pConn->getDb()))) + { + goto Exit; + } + } + + if( bSweep) + { + if( RC_BAD( rc = FlmDbSweep( m_pConn->getDb(), + SWEEP_PURGED_FLDS, 0, NULL, NULL))) + { + goto Exit; + } + } + +Exit: + + if( pDictRec) + { + pDictRec->Release(); + } + + if( RC_BAD( rc) && bMustAbortOnError) + { + m_pConn->setAbortFlag(); + } + + DBUG_RETURN( rc); +} + +/**************************************************************************** +Desc: Given a starting key and an ending key, estimate the number of rows + that will exist between the two. The end key may be empty. +****************************************************************************/ +ha_rows ha_flaim::records_in_range( + uint uiIndex, + key_range * pMinKey, + key_range * pMaxKey) +{ + // VISIT: Implement + DBUG_ENTER( "ha_flaim::records_in_range"); + DBUG_RETURN( 2); +} + +/**************************************************************************** +Desc: Called to create a database and/or table. +****************************************************************************/ +int ha_flaim::create( + const char * pszFormFilePath, + TABLE * pTable, + HA_CREATE_INFO * pCreateInfo) +{ + RCODE rc = FERR_OK; + FlmRecord * pRec = NULL; + void * pvField; + char szTmpBuf[ 512]; + FLMUINT uiTableDefId; + FLMUINT uiLoop; + FLMUINT uiLastDrnUsed = 0; + FLMBOOL bStartedTrans = FALSE; + FLMBOOL bMustAbortOnError = FALSE; + FLMBOOL bCreatedDatabase = FALSE; + + DBUG_ENTER( "ha_flaim::create"); + +Retry: + + f_pathReduce( pszFormFilePath, m_szDbPath, NULL); + f_pathAppend( m_szDbPath, FLAIM_DB_NAME); + + assert( m_pConn == NULL); + + if( RC_BAD( rc = gv_pConnTbl->getConnection( m_szDbPath, &m_pConn))) + { + if( rc == FERR_IO_PATH_NOT_FOUND) + { + if( RC_BAD( rc = FlmDbCreate( m_szDbPath, NULL, NULL, NULL, + gv_pszBaseDict, NULL, NULL))) + { + goto Exit; + } + + bCreatedDatabase = TRUE; + } + + goto Retry; + } + + if( pTable && pTable->s->table_name) + { + if( m_pConn->getTransType() != FLM_UPDATE_TRANS) + { + if( RC_BAD( rc = FlmDbTransBegin( m_pConn->getDb(), + FLM_UPDATE_TRANS, FLM_NO_TIMEOUT))) + { + goto Exit; + } + + bStartedTrans = TRUE; + } + + // Create the table container + + if( (pRec = new FlmRecord) == NULL) + { + rc = FERR_MEM; + goto Exit; + } + + if( RC_BAD( rc = pRec->insertLast( 0, FLM_CONTAINER_TAG, + FLM_TEXT_TYPE, &pvField))) + { + goto Exit; + } + + sprintf( szTmpBuf, ":%s:", pTable->s->table_name); + + if( RC_BAD( rc = pRec->setNative( pvField, szTmpBuf))) + { + goto Exit; + } + + bMustAbortOnError = TRUE; + + if( RC_BAD( rc = FlmFindUnusedDictDrn( m_pConn->getDb(), + uiLastDrnUsed, FLM_RESERVED_TAG_NUMS - 1, &uiLastDrnUsed))) + { + goto Exit; + } + + uiTableDefId = uiLastDrnUsed; + + if( RC_BAD( rc = FlmRecordAdd( m_pConn->getDb(), + FLM_DICT_CONTAINER, &uiTableDefId, pRec, 0))) + { + goto Exit; + } + + pRec->Release(); + + // Create the table column fields + + for( Field ** ppField = table->field; *ppField; ppField++) + { + if( (pRec = new FlmRecord) == NULL) + { + rc = FERR_MEM; + goto Exit; + } + + if( RC_BAD( rc = pRec->insertLast( 0, FLM_FIELD_TAG, + FLM_TEXT_TYPE, &pvField))) + { + goto Exit; + } + + sprintf( szTmpBuf, ":%s:col:%s:", pTable->s->table_name, + (*ppField)->field_name); + + if( RC_BAD( rc = pRec->setNative( pvField, szTmpBuf))) + { + goto Exit; + } + + if( RC_BAD( rc = pRec->insertLast( 1, FLM_TYPE_TAG, + FLM_TEXT_TYPE, &pvField))) + { + goto Exit; + } + + switch( (*ppField)->type()) + { + case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_DECIMAL: + { + if( RC_BAD( rc = pRec->setNative( pvField, "text"))) + { + goto Exit; + } + + break; + } + + case MYSQL_TYPE_TINY: + case MYSQL_TYPE_SHORT: + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_YEAR: + case MYSQL_TYPE_NEWDATE: + { + if( RC_BAD( rc = pRec->setNative( pvField, "number"))) + { + goto Exit; + } + + break; + } + + case MYSQL_TYPE_FLOAT: + case MYSQL_TYPE_DOUBLE: + case MYSQL_TYPE_NULL: + case MYSQL_TYPE_LONGLONG: + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_BIT: + case MYSQL_TYPE_NEWDECIMAL: + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_SET: + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_GEOMETRY: + { + if( RC_BAD( rc = pRec->setNative( pvField, "binary"))) + { + goto Exit; + } + + break; + } + + default: + { + assert( 0); + rc = FERR_NOT_IMPLEMENTED; + goto Exit; + } + } + + if( RC_BAD( rc = FlmFindUnusedDictDrn( m_pConn->getDb(), + uiLastDrnUsed, FLM_RESERVED_TAG_NUMS - 1, &uiLastDrnUsed))) + { + goto Exit; + } + + if( RC_BAD( rc = FlmRecordAdd( m_pConn->getDb(), + FLM_DICT_CONTAINER, &uiLastDrnUsed, pRec, 0))) + { + if( rc != FERR_DUPLICATE_DICT_NAME) + { + goto Exit; + } + + rc = FERR_OK; + } + } + + // Create the table key fields + + for( uiLoop = 0; uiLoop < pTable->s->keys; uiLoop++) + { + KEY * pKey = table->key_info + uiLoop; + FLMUINT uiKeyField; + + if( (pRec = new FlmRecord) == NULL) + { + rc = FERR_MEM; + goto Exit; + } + + if( RC_BAD( rc = pRec->insertLast( 0, FLM_FIELD_TAG, + FLM_TEXT_TYPE, &pvField))) + { + goto Exit; + } + + sprintf( szTmpBuf, ":%s:key:%u:", pTable->s->table_name, uiLoop); + + if( RC_BAD( rc = pRec->setNative( pvField, szTmpBuf))) + { + goto Exit; + } + + if( RC_BAD( rc = pRec->insertLast( 1, FLM_TYPE_TAG, + FLM_TEXT_TYPE, &pvField))) + { + goto Exit; + } + + if( RC_BAD( rc = pRec->setNative( pvField, "binary"))) + { + goto Exit; + } + + if( RC_BAD( rc = FlmFindUnusedDictDrn( m_pConn->getDb(), + uiLastDrnUsed, FLM_RESERVED_TAG_NUMS - 1, &uiLastDrnUsed))) + { + goto Exit; + } + + uiKeyField = uiLastDrnUsed; + + if( RC_BAD( rc = FlmRecordAdd( m_pConn->getDb(), + FLM_DICT_CONTAINER, &uiKeyField, pRec, 0))) + { + if( rc != FERR_DUPLICATE_DICT_NAME) + { + goto Exit; + } + + rc = FERR_OK; + } + + pRec->Release(); + pRec = NULL; + + // Add the index + + if( (pRec = new FlmRecord) == NULL) + { + rc = FERR_MEM; + goto Exit; + } + + if( RC_BAD( rc = pRec->insertLast( 0, FLM_INDEX_TAG, + FLM_TEXT_TYPE, &pvField))) + { + goto Exit; + } + + sprintf( szTmpBuf, ":%s:index:%u:", pTable->s->table_name, uiLoop); + + if( RC_BAD( rc = pRec->setNative( pvField, szTmpBuf))) + { + goto Exit; + } + + if( RC_BAD( rc = pRec->insertLast( 1, FLM_CONTAINER_TAG, + FLM_TEXT_TYPE, &pvField))) + { + goto Exit; + } + + sprintf( szTmpBuf, "%u", uiTableDefId); + + if( RC_BAD( rc = pRec->setNative( pvField, szTmpBuf))) + { + goto Exit; + } + + if( RC_BAD( rc = pRec->insertLast( 1, FLM_KEY_TAG, + FLM_TEXT_TYPE, &pvField))) + { + goto Exit; + } + + if( (pKey->flags & (HA_NOSAME | HA_NULL_PART_KEY)) == HA_NOSAME) + { + if( RC_BAD( rc = pRec->insertLast( 2, FLM_UNIQUE_TAG, + FLM_TEXT_TYPE, &pvField))) + { + goto Exit; + } + } + + if( RC_BAD( rc = pRec->insertLast( 2, FLM_FIELD_TAG, + FLM_TEXT_TYPE, &pvField))) + { + goto Exit; + } + + sprintf( szTmpBuf, "%u", uiKeyField); + + if( RC_BAD( rc = pRec->setNative( pvField, szTmpBuf))) + { + goto Exit; + } + + if( RC_BAD( rc = FlmFindUnusedDictDrn( m_pConn->getDb(), + uiLastDrnUsed, FLM_RESERVED_TAG_NUMS - 1, &uiLastDrnUsed))) + { + goto Exit; + } + + if( RC_BAD( rc = FlmRecordAdd( m_pConn->getDb(), + FLM_DICT_CONTAINER, &uiLastDrnUsed, pRec, 0))) + { + goto Exit; + } + + pRec->Release(); + pRec = NULL; + } + + if( bStartedTrans) + { + bStartedTrans = FALSE; + + if( RC_BAD( rc = FlmDbTransCommit( m_pConn->getDb()))) + { + goto Exit; + } + } + } + +Exit: + + if( pRec) + { + pRec->Release(); + } + + if( bStartedTrans) + { + FlmDbTransAbort( m_pConn->getDb()); + bMustAbortOnError = FALSE; + } + + if( RC_BAD( rc)) + { + if( bMustAbortOnError) + { + assert( !bCreatedDatabase); + m_pConn->setAbortFlag(); + } + + if( bCreatedDatabase) + { + FlmDbRemove( m_szDbPath, NULL, NULL, TRUE); + } + } + + DBUG_RETURN( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE ha_flaim::importRowFromRec( + FlmRecord * pRec, + FLMBYTE * pucData) +{ + RCODE rc = FERR_OK; + Field ** ppMyField; + void * pvField; + FLMUINT uiLength; + FIELD_INFO * pColumnFields = m_pShare->pColumnFields; + FLMBYTE * pucAllocBuf = NULL; + + pvField = pRec->firstChild( pRec->root()); + + // Import NULL bitmap + + if( (uiLength = table->s->null_bytes) != 0) + { + if( (pvField = pRec->find( pvField, + FLM_ROW_NULL_BITS_FIELD_ID)) == NULL) + { + rc = FERR_DATA_ERROR; + goto Exit; + } + + if( RC_BAD( rc = pRec->getBinary( pvField, pucData, &uiLength))) + { + goto Exit; + } + } + + // Import each column's data + + for( ppMyField = table->field; *ppMyField; ppMyField++) + { + if( !isFieldNull( table, *ppMyField, (const char *)pucData)) + { + FIELD_INFO * pFieldInfo = &pColumnFields[ (*ppMyField)->field_index]; + char * pucFieldData = (char *)(pucData + (*ppMyField)->offset()); + + if( (pvField = pRec->find( pvField, pFieldInfo->uiDictNum)) == NULL) + { + rc = FERR_DATA_ERROR; + goto Exit; + } + + switch( pFieldInfo->uiDataType) + { + case FLM_TEXT_TYPE: + { + FLMUNICODE uzTmpBuf[ 256]; + FLMUNICODE * puzStr = uzTmpBuf; + FLMUINT uiStrLen; + + uiStrLen = sizeof( uzTmpBuf); + if( RC_BAD( rc = pRec->getUnicode( pvField, uzTmpBuf, &uiStrLen))) + { + if( rc != FERR_CONV_DEST_OVERFLOW) + { + goto Exit; + } + + if( RC_BAD( rc = pRec->getUnicode( pvField, NULL, &uiStrLen))) + { + goto Exit; + } + + if( !my_multi_malloc( MYF( MY_WME), &pucAllocBuf, uiStrLen + 2, NullS)) + { + rc = FERR_MEM; + goto Exit; + } + + uiStrLen += 2; + + if( RC_BAD( rc = pRec->getUnicode( pvField, + (FLMUNICODE *)pucAllocBuf, &uiStrLen))) + { + goto Exit; + } + + puzStr = (FLMUNICODE *)pucAllocBuf; + } + + #ifndef WORDS_BIGENDIAN + { + FLMUNICODE * puzTmp = puzStr; + + while( *puzTmp) + { + *puzTmp++ = (*puzTmp >> 8) | (*puzTmp << 8); + } + } + #endif + + String tmpStr( (const char *)puzStr, uiStrLen, &my_charset_ucs2_bin); + + if( convertString( &tmpStr, &my_charset_ucs2_bin, (*ppMyField)->charset())) + { + rc = FERR_MEM; + goto Exit; + } + + uiStrLen = tmpStr.length(); + + switch( (*ppMyField)->type()) + { + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_DECIMAL: + { + memcpy( pucFieldData, tmpStr.ptr(), uiStrLen); + break; + } + + case MYSQL_TYPE_VARCHAR: + { + if( ((Field_varstring *)(*ppMyField))->length_bytes == 1) + { + *pucFieldData++ = (uchar)uiStrLen; + } + else + { + int2store( pucFieldData, tmpStr.length()); + pucFieldData += 2; + } + + memcpy( pucFieldData, tmpStr.ptr(), uiStrLen); + break; + } + + default: + { + assert( 0); + rc = FERR_NOT_IMPLEMENTED; + goto Exit; + } + } + + break; + } + + case FLM_NUMBER_TYPE: + { + longlong i64Val; + + if( (*ppMyField)->flags & UNSIGNED_FLAG) + { + FLMUINT uiVal; + + if( RC_BAD( rc = pRec->getUINT( pvField, &uiVal))) + { + goto Exit; + } + + i64Val = (longlong)uiVal; + } + else + { + FLMINT iVal; + + if( RC_BAD( rc = pRec->getINT( pvField, &iVal))) + { + goto Exit; + } + + i64Val = (longlong)iVal; + } + + switch( (*ppMyField)->type()) + { + case MYSQL_TYPE_TINY: + case MYSQL_TYPE_YEAR: + { + *pucFieldData = (char)i64Val; + break; + } + + case MYSQL_TYPE_SHORT: + { + #ifdef WORDS_BIGENDIAN + if( table->s->db_low_byte_first) + { + int2store( pucFieldData, i64Val); + } + else + #endif + { + shortstore( pucFieldData, i64Val); + } + + break; + } + + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_NEWDATE: + { + int3store( pucFieldData, i64Val); + break; + } + + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_TIMESTAMP: + { + #ifdef WORDS_BIGENDIAN + if( table->s->db_low_byte_first) + { + int4store( pucFieldData, i64Val); + } + else + #endif + { + longstore( pucFieldData, i64Val); + } + + break; + } + + default: + { + assert( 0); + rc = FERR_NOT_IMPLEMENTED; + goto Exit; + } + } + + break; + } + + case FLM_BINARY_TYPE: + { + (*ppMyField)->unpack( pucFieldData, + (const char *)(pRec->getDataPtr( pvField))); + break; + } + + default: + { + assert( 0); + rc = FERR_NOT_IMPLEMENTED; + goto Exit; + } + } + } + } + +Exit: + + if( pucAllocBuf) + { + my_free( (gptr)pucAllocBuf, MYF(0)); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE ha_flaim::exportRowToRec( + FLMBYTE * pucData, + FlmRecord ** ppRec) +{ + RCODE rc = FERR_OK; + FLMUINT uiLoop; + FlmRecord * pRec = NULL; + Field ** ppMyField; + void * pvField; + FIELD_INFO * pColumnFields = m_pShare->pColumnFields; + FIELD_INFO * pKeyFields = m_pShare->pKeyFields; + + assert( *ppRec == NULL); + + if( (pRec = new FlmRecord) == NULL) + { + rc = FERR_MEM; + goto Exit; + } + + if( RC_BAD( rc = pRec->insertLast( 0, + FLM_ROW_CONTEXT_FIELD_ID, FLM_CONTEXT_TYPE, &pvField))) + { + goto Exit; + } + + // Store NULL bitmap + + if( table->s->null_bytes) + { + if( RC_BAD( rc = pRec->insertLast( 1, + FLM_ROW_NULL_BITS_FIELD_ID, FLM_BINARY_TYPE, &pvField))) + { + goto Exit; + } + + if( RC_BAD( rc = pRec->setBinary( pvField, + pucData, table->s->null_bytes))) + { + goto Exit; + } + } + + // Store each column's data + + for( ppMyField = table->field; *ppMyField; ppMyField++) + { + if( !isFieldNull( table, *ppMyField, (const char *)pucData)) + { + FIELD_INFO * pFieldInfo = &pColumnFields[ (*ppMyField)->field_index]; + + if( RC_BAD( rc = pRec->insertLast( 1, + pFieldInfo->uiDictNum, pFieldInfo->uiDataType, &pvField))) + { + goto Exit; + } + + switch( pFieldInfo->uiDataType) + { + case FLM_TEXT_TYPE: + { + String tmpStr; + + (*ppMyField)->val_str( &tmpStr); + + if( convertString( &tmpStr, (*ppMyField)->charset(), + &my_charset_ucs2_bin)) + { + rc = FERR_MEM; + goto Exit; + } + + if( tmpStr.append( "\0\0", 2, &my_charset_ucs2_bin)) + { + rc = FERR_MEM; + goto Exit; + } + + // FLAIM expects the Unicode string to have the + // same endian order as the host platform. MySQL + // always represents Unicode as big endian. Thus, + // we need to do some byte swapping if we are on + // a little-endian platform. + + #ifndef WORDS_BIGENDIAN + { + FLMUNICODE * puzTmp = (FLMUNICODE *)tmpStr.ptr(); + + while( *puzTmp) + { + *puzTmp++ = (*puzTmp >> 8) | (*puzTmp << 8); + } + } + #endif + + if( RC_BAD( rc = pRec->setUnicode( pvField, + (FLMUNICODE *)tmpStr.ptr()))) + { + goto Exit; + } + + break; + } + + case FLM_NUMBER_TYPE: + { + longlong iNum = (*ppMyField)->val_int(); + + if( (*ppMyField)->flags & UNSIGNED_FLAG) + { + if( RC_BAD( rc = pRec->setUINT( pvField, (FLMUINT)iNum))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = pRec->setINT( pvField, (FLMINT)iNum))) + { + goto Exit; + } + } + + break; + } + + case FLM_BINARY_TYPE: + { + if( RC_BAD( rc = pRec->setBinary( pvField, + &pucData[ (*ppMyField)->offset()], + (*ppMyField)->packed_col_length( + (const char *)&pucData[ (*ppMyField)->offset()], + (*ppMyField)->pack_length())))) + { + goto Exit; + } + + break; + } + + default: + { + assert( 0); + rc = FERR_NOT_IMPLEMENTED; + goto Exit; + } + } + } + } + + // Store the row's keys + + for( uiLoop = 0; uiLoop < table->s->keys; uiLoop++) + { + if( RC_BAD( rc = exportKeyToRec( uiLoop, pucData, pRec))) + { + goto Exit; + } + } + + *ppRec = pRec; + pRec = NULL; + +Exit: + + if( pRec) + { + pRec->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE ha_flaim::exportKeyToTree( + FLMUINT uiIndexOffset, + const byte * pucKey, + FLMUINT uiKeyLen, + FlmRecord ** ppKeyTree) +{ + RCODE rc = FERR_OK; + char * pucBuf = m_pucKeyBuf; + char * pucBufEnd = &m_pucKeyBuf[ FLM_MAX_KEY_LENGTH]; + FlmRecord * pKeyTree = NULL; + void * pvField; + KEY * pKey = table->key_info + uiIndexOffset; + KEY_PART_INFO * pCurKeyPart = pKey->key_part; + KEY_PART_INFO * pEndKeyPart = pCurKeyPart + pKey->key_parts; + + DBUG_ENTER( "ha_flaim::exportKeyToTree"); + + assert( *ppKeyTree == NULL); + + if( (pKeyTree = new FlmRecord) == NULL) + { + rc = FERR_MEM; + goto Exit; + } + + if( RC_BAD( rc = pKeyTree->insertLast( 0, + FLM_KEY_TAG, FLM_CONTEXT_TYPE, &pvField))) + { + goto Exit; + } + + if( RC_BAD( rc = pKeyTree->insertLast( 1, + m_pShare->pKeyFields[ uiIndexOffset].uiDictNum, + FLM_BINARY_TYPE, &pvField))) + { + goto Exit; + } + + for( ; pCurKeyPart != pEndKeyPart && uiKeyLen > 0; pCurKeyPart++) + { + FLMUINT uiOffset = 0; + + if( pCurKeyPart->null_bit) + { + *pucBuf++ = *pucKey ? 1 : 0; + uiKeyLen -= pCurKeyPart->store_length; + pucKey += pCurKeyPart->store_length; + uiOffset = 1; + } + + pucBuf = pCurKeyPart->field->pack_key_from_key_image( pucBuf, + (const char *)pucKey + uiOffset, pCurKeyPart->length); + pucKey += pCurKeyPart->store_length; + uiKeyLen -= pCurKeyPart->store_length; + + assert( pucBuf <= pucBufEnd); + } + + if( RC_BAD( rc = pKeyTree->setBinary( pvField, m_pucKeyBuf, + pucBuf - m_pucKeyBuf))) + { + goto Exit; + } + + *ppKeyTree = pKeyTree; + pKeyTree = NULL; + +Exit: + + if( pKeyTree) + { + pKeyTree->Release(); + } + + DBUG_RETURN( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FlmConnectionTable::getConnection( + const char * pszDbPath, + FlmConnection ** ppConnection) +{ + RCODE rc = FERR_OK; + HFDB hDb = HFDB_NULL; + FlmConnection * pConn = NULL; + FLMUINT uiThreadId = my_thread_id(); + FLMBOOL bMutexLocked = FALSE; + + if( *ppConnection) + { + if( (*ppConnection)->getThreadId() == uiThreadId) + { + goto Exit; + } + + (*ppConnection)->Release(); + *ppConnection = NULL; + } + + // Search the connection list for an existing connection + + pthread_mutex_lock( &m_hMutex); + bMutexLocked = TRUE; + + pConn = m_pConnList; + while( pConn) + { + if( pConn->getThreadId() == uiThreadId) + { + break; + } + + pConn = pConn->getNext(); + } + + if( pConn) + { + // VISIT: Add debug code to make sure this thread + // is still accessing the same database as the last + // time this connection was used. + + pConn->AddRef(); + *ppConnection = pConn; + pConn = NULL; + goto Exit; + } + + pthread_mutex_unlock( &m_hMutex); + bMutexLocked = FALSE; + + if( !pszDbPath) + { + rc = FERR_ILLEGAL_OP; + goto Exit; + } + + if( RC_BAD( rc = FlmDbOpen( pszDbPath, NULL, NULL, 0, NULL, &hDb))) + { + goto Exit; + } + + if( (pConn = new FlmConnection( gv_pConnTbl)) == NULL) + { + rc = FERR_MEM; + goto Exit; + } + + pConn->setThreadId( uiThreadId); + pConn->setDb( hDb); + hDb = HFDB_NULL; + + pthread_mutex_lock( &m_hMutex); + bMutexLocked = TRUE; + + if( (pConn->m_pNext = m_pConnList) != NULL) + { + pConn->m_pNext->m_pPrev = pConn; + } + + m_pConnList = pConn; + m_pConnList->AddRef(); + *ppConnection = pConn; + pConn = NULL; + +Exit: + + if( pConn) + { + pConn->Release(); + } + + if( bMutexLocked) + { + pthread_mutex_unlock( &m_hMutex); + } + + if( hDb != HFDB_NULL) + { + FlmDbClose( &hDb); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FlmConnectionTable::setup( void) +{ + RCODE rc = FERR_OK; + + if( pthread_mutex_init( &m_hMutex, MY_MUTEX_INIT_FAST)) + { + rc = FERR_MEM; + goto Exit; + } + + m_bFreeMutex = TRUE; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMUINT FlmConnection::Release( + FLMBOOL bMutexLocked) +{ + FLMUINT uiRefCount = --m_ui32RefCnt; + + if( uiRefCount == 1) + { + m_pConnTbl->closeConnection( this, bMutexLocked); + delete this; + } + + return( uiRefCount); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FlmConnectionTable::closeConnection( + FlmConnection * pConn, + FLMBOOL bMutexLocked) +{ + RCODE rc = FERR_OK; + FLMBOOL bLastConnection = FALSE; + + if( !bMutexLocked) + { + lockMutex(); + } + + if( pConn->m_pPrev) + { + pConn->m_pPrev->m_pNext = pConn->m_pNext; + } + else + { + if( (m_pConnList = pConn->m_pNext) == NULL) + { + bLastConnection = TRUE; + } + } + + if( pConn->m_pNext) + { + pConn->m_pNext->m_pPrev = pConn->m_pPrev; + } + + if( !bMutexLocked) + { + unlockMutex(); + } + + if( pConn->m_hDb != HFDB_NULL) + { + FLMBYTE szDbPath[ F_PATH_MAX_SIZE]; + + szDbPath[ 0] = 0; + + if( bLastConnection) + { + if( RC_BAD( FlmDbGetConfig( pConn->m_hDb, FDB_GET_PATH, szDbPath))) + { + szDbPath[ 0] = 0; + } + } + + FlmDbClose( &pConn->m_hDb); + + if( szDbPath[ 0]) + { + FlmConfig( FLM_CLOSE_FILE, szDbPath, NULL); + } + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FlmConnection::FlmConnection( FlmConnectionTable * pConnTbl) +{ + m_uiThreadId = 0; + m_hDb = HFDB_NULL; + m_pPrev = NULL; + m_pNext = NULL; + m_bAbortFlag = FALSE; + + m_uiLockCount = 0; + m_uiTransTypeNeeded = FLM_NO_TRANS; + + m_pConnTbl = pConnTbl; + m_pConnTbl->AddRef(); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FlmConnection::~FlmConnection() +{ + assert( !m_pPrev); + assert( !m_pNext); + + if( m_hDb != HFDB_NULL) + { + FlmDbClose( &m_hDb); + } + + if( m_pConnTbl) + { + m_pConnTbl->Release(); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +static int flaim_commit( + THD * pThread, + bool bAll) +{ + RCODE rc = FERR_OK; + FlmConnection * pConn = NULL; + + DBUG_ENTER( "flaim_commit"); + + if( bAll) + { + if( RC_BAD( rc = gv_pConnTbl->getConnection( + NULL, &pConn))) + { + goto Exit; + } + + assert( pConn->getTransType() == FLM_UPDATE_TRANS); + + if( pConn->getTransType() != FLM_NO_TRANS) + { + assert( pConn->getLockCount() == 1); + + pConn->setLockCount( 0); + pConn->setTransTypeNeeded( FLM_NO_TRANS); + + if( RC_BAD( rc = FlmDbTransCommit( pConn->getDb()))) + { + goto Exit; + } + } + } + +Exit: + + if( pConn) + { + pConn->Release(); + } + + DBUG_RETURN( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +static int flaim_rollback( + THD * pThread, + bool bAll) +{ + RCODE rc = FERR_OK; + FlmConnection * pConn = NULL; + + DBUG_ENTER( "flaim_rollback"); + + if( bAll) + { + if( RC_BAD( rc = gv_pConnTbl->getConnection( + NULL, &pConn))) + { + goto Exit; + } + + assert( pConn->getTransType() == FLM_UPDATE_TRANS); + + if( pConn->getTransType() != FLM_NO_TRANS) + { + assert( pConn->getLockCount() == 1); + + pConn->setLockCount( 0); + pConn->setTransTypeNeeded( FLM_NO_TRANS); + + if( RC_BAD( rc = FlmDbTransAbort( pConn->getDb()))) + { + goto Exit; + } + } + } + +Exit: + + if( pConn) + { + pConn->Release(); + } + + DBUG_RETURN( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FlmConnection::storeRowCount( + FLMUINT uiTableId, + FLMUINT uiRowCount) +{ + RCODE rc = FERR_OK; + FlmRecord * pRec = NULL; + void * pvField; + FLMBOOL bAddNewRec = FALSE; + + if( RC_BAD( rc = FlmRecordRetrieve( m_hDb, FLM_DATA_CONTAINER, + uiTableId, FO_EXACT, &pRec, NULL))) + { + if( rc != FERR_NOT_FOUND) + { + goto Exit; + } + + if( (pRec = new FlmRecord) == NULL) + { + rc = FERR_MEM; + goto Exit; + } + + if( RC_BAD( rc = pRec->insertLast( 0, FLM_TABLE_INFO_FIELD_ID, + FLM_CONTEXT_TYPE, &pvField))) + { + goto Exit; + } + + bAddNewRec = TRUE; + } + + if( pRec->isReadOnly()) + { + FlmRecord * pTmpRec; + + if( (pTmpRec = pRec->copy()) == NULL) + { + rc = FERR_MEM; + goto Exit; + } + + pRec->Release(); + pRec = pTmpRec; + } + + if( (pvField = pRec->find( pRec->root(), FLM_ROW_COUNT_FIELD_ID)) == NULL) + { + if( RC_BAD( rc = pRec->insertLast( 1, FLM_ROW_COUNT_FIELD_ID, + FLM_NUMBER_TYPE, &pvField))) + { + goto Exit; + } + } + + if( RC_BAD( rc = pRec->setUINT( pvField, uiRowCount))) + { + goto Exit; + } + + if( !bAddNewRec) + { + if( RC_BAD( rc = FlmRecordModify( m_hDb, + FLM_DATA_CONTAINER, uiTableId, pRec, 0))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = FlmRecordAdd( m_hDb, + FLM_DATA_CONTAINER, &uiTableId, pRec, 0))) + { + goto Exit; + } + } + +Exit: + + if( pRec) + { + pRec->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FlmConnection::retrieveRowCount( + FLMUINT uiTableId, + FLMUINT * puiRowCount) +{ + RCODE rc = FERR_OK; + FlmRecord * pRec = NULL; + void * pvField; + + *puiRowCount = 0; + + if( RC_BAD( rc = FlmRecordRetrieve( m_hDb, FLM_DATA_CONTAINER, + uiTableId, FO_EXACT, &pRec, NULL))) + { + if( rc == FERR_NOT_FOUND) + { + rc = FERR_OK; + } + + goto Exit; + } + + if( (pvField = pRec->find( pRec->root(), FLM_ROW_COUNT_FIELD_ID)) != NULL) + { + if( RC_BAD( rc = pRec->getUINT( pvField, puiRowCount))) + { + goto Exit; + } + } + +Exit: + + if( pRec) + { + pRec->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FlmConnection::incrementRowCount( + FLMUINT uiTableId) +{ + RCODE rc = FERR_OK; + FLMUINT uiRowCount; + + if( RC_BAD( rc = retrieveRowCount( uiTableId, &uiRowCount))) + { + goto Exit; + } + + if( RC_BAD( rc = storeRowCount( uiTableId, uiRowCount + 1))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FlmConnection::decrementRowCount( + FLMUINT uiTableId) +{ + RCODE rc = FERR_OK; + FLMUINT uiRowCount; + + if( RC_BAD( rc = retrieveRowCount( uiTableId, &uiRowCount))) + { + goto Exit; + } + + if( !uiRowCount) + { + rc = FERR_ILLEGAL_OP; + goto Exit; + } + + if( RC_BAD( rc = storeRowCount( uiTableId, uiRowCount - 1))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE ha_flaim::exportKeyToRec( + uint uiKeyNum, + const byte * pucRecord, + FlmRecord * pRec) +{ + RCODE rc = FERR_OK; + char * pucBuf = m_pucKeyBuf; + char * pucBufEnd = &m_pucKeyBuf[ FLM_MAX_KEY_LENGTH]; + FIELD_INFO * pKeyFields = m_pShare->pKeyFields; + void * pvField; + KEY * pKey = table->key_info + uiKeyNum; + KEY_PART_INFO * pCurKeyPart = pKey->key_part; + KEY_PART_INFO * pEndKeyPart = pCurKeyPart + pKey->key_parts; + + DBUG_ENTER( "ha_flaim::exportKeyToRec"); + + for( ; pCurKeyPart != pEndKeyPart && pucBuf < pucBufEnd; pCurKeyPart++) + { + if( pCurKeyPart->null_bit) + { + // Store 0 if the key part is a NULL part + + if( pucRecord[ pCurKeyPart->null_offset] & pCurKeyPart->null_bit) + { + *pucBuf++ = 0; + continue; + } + + // Store NOT NULL marker + + *pucBuf++ = 1; + } + + pucBuf = pCurKeyPart->field->pack_key( pucBuf, + (const char *)(pucRecord + pCurKeyPart->offset), + pCurKeyPart->length); + + assert( pucBuf <= pucBufEnd); + } + + if( RC_BAD( rc = pRec->insertLast( 1, + pKeyFields[ uiKeyNum].uiDictNum, FLM_BINARY_TYPE, &pvField))) + { + goto Exit; + } + + if( RC_BAD( rc = pRec->setBinary( pvField, m_pucKeyBuf, pucBuf - m_pucKeyBuf))) + { + goto Exit; + } + +Exit: + + DBUG_RETURN( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +bool ha_flaim::convertString( + String * pString, + CHARSET_INFO * pFromCharSet, + CHARSET_INFO * pToCharSet) +{ + uint dummy_errors; + + if( m_convertBuffer.copy( pString->ptr(), pString->length(), + pFromCharSet, pToCharSet, &dummy_errors)) + { + return( true); + } + + if( m_convertBuffer.alloced_length() >= m_convertBuffer.length() * 2 || + !pString->is_alloced()) + { + return( pString->copy( m_convertBuffer)); + } + + pString->swap( m_convertBuffer); + return( false); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +int ha_flaim::check( + THD * pThread, + HA_CHECK_OPT * pCheckOpt) +{ + RCODE rc = FERR_OK; + char szDbPath[ F_PATH_MAX_SIZE]; + + DBUG_ENTER( "ha_flaim::check"); + + szDbPath[ 0] = 0; + f_pathAppend( szDbPath, "."); + f_pathAppend( szDbPath, pThread->db); + f_pathAppend( szDbPath, FLAIM_DB_NAME); + + if( RC_BAD( rc = FlmDbCheck( NULL, szDbPath, NULL, + NULL, 0, NULL, NULL, NULL, NULL))) + { + goto Exit; + } + +Exit: + + if( rc == FERR_DATA_ERROR) + { + rc = (RCODE)HA_ADMIN_CORRUPT; + } + + DBUG_RETURN( rc); +} + +//#endif /* HAVE_FLAIM */ diff --git a/flaim/util/ha_flaim.h b/flaim/util/ha_flaim.h new file mode 100644 index 0000000..506125e --- /dev/null +++ b/flaim/util/ha_flaim.h @@ -0,0 +1,549 @@ +//------------------------------------------------------------------------- +// Desc: FLAIM handler for MySQL +// Tabs: 3 +// +// Copyright (c) 2006 Novell, Inc. All Rights Reserved. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of version 2 of the GNU General Public +// License as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, contact Novell, Inc. +// +// To contact Novell about this file by physical or electronic mail, +// you may find current contact information at www.novell.com +// +// $Id$ +//------------------------------------------------------------------------- + +#ifndef HA_FLAIM_H +#define HA_FLAIM_H + +#include "../flaim/flaim.h" + +#ifdef USE_PRAGMA_INTERFACE + #pragma interface +#endif + +#define FLM_MAX_KEY_LENGTH 256 + +typedef struct +{ + FLMUINT uiDictNum; + FLMUINT uiDataType; +} FIELD_INFO; + +typedef struct +{ + FLMUINT uiDictNum; +} INDEX_INFO; + +typedef struct +{ + char * pszTablePath; + uint uiTablePathLen; + uint uiUseCount; + FLMUINT uiTableContainer; + FIELD_INFO * pColumnFields; + FIELD_INFO * pKeyFields; + INDEX_INFO * pIndexes; + pthread_mutex_t hMutex; + THR_LOCK lock; +} FLAIM_SHARE; + +class FlmConnectionTable; + +/**************************************************************************** +Desc: +****************************************************************************/ +class FlmConnection : public F_Base +{ +public: + + FlmConnection( FlmConnectionTable * pConnTbl); + + virtual ~FlmConnection(); + + inline FLMUINT getThreadId( void) + { + return( m_uiThreadId); + } + + inline void setThreadId( + FLMUINT uiThreadId) + { + m_uiThreadId = uiThreadId; + } + + inline FlmConnection * getNext( void) + { + return( m_pNext); + } + + inline void setDb( + HFDB hDb) + { + assert( m_hDb == HFDB_NULL); + m_hDb = hDb; + } + + inline HFDB getDb( void) + { + return( m_hDb); + } + + FLMUINT Release( + FLMBOOL bMutexLocked); + + inline FLMUINT Release( void) + { + return( Release( FALSE)); + } + + inline FLMUINT getTransType( void) + { + FLMUINT uiTransType; + + if( m_hDb == HFDB_NULL || + RC_BAD( FlmDbGetTransType( m_hDb, &uiTransType))) + { + return( FLM_NO_TRANS); + } + + return( uiTransType); + } + + inline void setTransTypeNeeded( + FLMUINT uiTransTypeNeeded) + { + m_uiTransTypeNeeded = uiTransTypeNeeded; + } + + inline FLMUINT getTransTypeNeeded( void) + { + return( m_uiTransTypeNeeded); + } + + inline FLMUINT incLockCount( void) + { + return( ++m_uiLockCount); + } + + inline FLMUINT decLockCount( void) + { + assert( m_uiLockCount); + return( --m_uiLockCount); + } + + inline FLMUINT getLockCount( void) + { + return( m_uiLockCount); + } + + inline void setLockCount( + FLMUINT uiLockCount) + { + m_uiLockCount = uiLockCount; + } + + RCODE storeRowCount( + FLMUINT uiTableId, + FLMUINT uiRowCount); + + RCODE retrieveRowCount( + FLMUINT uiTableId, + FLMUINT * puiRowCount); + + RCODE incrementRowCount( + FLMUINT uiTableId); + + RCODE decrementRowCount( + FLMUINT uiTableId); + + inline FLMBOOL getAbortFlag( void) + { + return( m_bAbortFlag); + } + + inline void setAbortFlag( void) + { + m_bAbortFlag = TRUE; + } + + inline void clearAbortFlag( void) + { + m_bAbortFlag = FALSE; + } + +private: + + FLMUINT m_uiThreadId; + HFDB m_hDb; + FlmConnection * m_pPrev; + FlmConnection * m_pNext; + FlmConnectionTable * m_pConnTbl; + FLMUINT m_uiTransTypeNeeded; + FLMUINT m_uiLockCount; + FLMBOOL m_bAbortFlag; + +friend class FlmConnectionTable; +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +class FlmConnectionTable : public F_Base +{ +public: + + FlmConnectionTable() + { + m_pConnList = NULL; + m_bFreeMutex = FALSE; + } + + virtual ~FlmConnectionTable() + { + FlmConnection * pConn = m_pConnList; + + while( pConn) + { + pConn->Release( TRUE); + pConn = m_pConnList; + } + + if( m_bFreeMutex) + { + pthread_mutex_destroy( &m_hMutex); + } + } + + RCODE setup( void); + + RCODE getConnection( + const char * pszTablePath, + FlmConnection ** ppConnection); + + RCODE closeConnection( + FlmConnection * pConn, + FLMBOOL bMutexLocked); + +private: + + inline void lockMutex( void) + { + pthread_mutex_lock( &m_hMutex); + } + + inline void unlockMutex( void) + { + pthread_mutex_unlock( &m_hMutex); + } + + FlmConnection * m_pConnList; + pthread_mutex_t m_hMutex; + FLMBOOL m_bFreeMutex; + +friend class FlmConnection; +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +class ha_flaim : public handler +{ +public: + + ha_flaim( TABLE * table_arg); + + virtual ~ha_flaim() + { + if( m_pConn) + { + m_pConn->Release(); + } + + if( m_pCurrKey) + { + m_pCurrKey->Release(); + } + } + + // The name that will be used for display purposes + + inline const char * table_type( void) const + { + return( "FLAIM"); + } + + // The name of the index type that will be used for display + // don't implement this method unless you really have indexes + + inline const char * index_type( uint inx) + { + return( "BTREE"); + } + + const char ** bas_ext( void) const; + + // The storage engine supports transactions + + inline bool has_transactions( void) + { + return( true); + } + + // This is a list of flags that says what the storage engine + // implements. The current table flags are documented in + // handler.h + + inline ulong table_flags( void) const + { + return( 0); + } + + // This is a bitmap of flags that says how the storage engine + // implements indexes. The current index flags are documented in + // handler.h. + // + // part is the key part to check. First key part is 0 + // If all_parts it's set, MySQL want to know the flags for the combined + // index up to and including 'part'. + + inline ulong index_flags( + uint uiIndex, + uint uiPart, + bool bAllParts) const + { + return( HA_READ_NEXT | HA_READ_ORDER | HA_READ_RANGE); + } + + // unireg.cpp will call the following to make sure that the storage engine + // can handle the data it is about to send. + // + // Return *real* limits of your storage engine here. MySQL will do + // min( your_limits, MySQL_limits) automatically + // + // There is no need to implement ..._key_... methods if you don't support + // indexes. + + inline uint max_supported_record_length( void) const + { + return( HA_MAX_REC_LENGTH); + } + + inline uint max_supported_keys( void) const + { + return( ~0); + } + + inline uint max_supported_key_parts( void) const + { + return( 32); + } + + inline uint max_supported_key_length( void) const + { + return( FLM_MAX_KEY_LENGTH); + } + + // Called in test_quick_select to determine if indexes should be used. + + inline double scan_time( void) + { + return( 0); + } + + // The next method will never be called if you do not implement indexes. + + inline double read_time( + ha_rows rows) + { + return( 0); + } + + // Everything below are methods that we implment in ha_flaim.cpp. + // + // Most of these methods are not obligatory, skip them and + // MySQL will treat them as not implemented + + int open( + const char * name, + int mode, + uint test_if_locked); + + int close( void); + + int write_row( + byte * pucData); + + int update_row( + const byte * pucOldData, + byte * pucNewData); + + int delete_row( + const byte * pucData); + + int index_init( + uint uiIndex); + + int index_end( void); + + int index_read( + byte * pucData, + const byte * pucKey, + uint uiKeyLen, + enum ha_rkey_function eFindFlag); + + int index_next( + byte * pucData); + + int index_first( + byte * pucData); + + int index_last( + byte * pucData); + + // unlike index_init(), rnd_init() can be called two times + // without rnd_end() in between (it only makes sense if scan=1). + // then the second call should prepare for the new table scan + // (e.g if rnd_init allocates the cursor, second call should + // position it to the start of the table, no need to deallocate + // and allocate it again + + int rnd_init( + bool bScan); + + int rnd_end( void); + + int rnd_next( + byte * pucData); + + int rnd_pos( + byte * pucData, + byte * pucPos); + + void position( + const byte * pucRecord); + + void info( uint); + + int extra( + enum ha_extra_function eOperation); + + int reset( void); + + int external_lock( + THD * pThread, + int iLockType); + + int delete_all_rows( void); + + ha_rows records_in_range( + uint uiIndex, + key_range * pMinKey, + key_range * pMaxKey); + + int delete_table( + const char * pszTablePath); + + int rename_table( + const char * pszOldName, + const char * pszNewName); + + int create( + const char * pszTableFormPath, + TABLE * pTable, + HA_CREATE_INFO * pCreateInfo); + + THR_LOCK_DATA ** store_lock( + THD * pThread, + THR_LOCK_DATA ** pLockData, + enum thr_lock_type eLockType); + + RCODE exportRowToRec( + FLMBYTE * pucData, + FlmRecord ** ppRec); + + RCODE importRowFromRec( + FlmRecord * pRec, + FLMBYTE * pucData); + + RCODE exportKeyToRec( + uint uiKeyNum, + const byte * pucRecord, + FlmRecord * pRec); + + RCODE exportKeyToTree( + FLMUINT uiIndexOffset, + const byte * pucKey, + FLMUINT uiKeyLen, + FlmRecord ** ppKeyTree); + + inline FLMBOOL isFieldNull( + TABLE * pTable, + Field * pField, + const char * pucRow) + { + FLMUINT uiNullOffset; + + if( !pField->null_ptr) + { + return( FALSE); + } + + uiNullOffset = (FLMUINT)((char *)pField->null_ptr - + (char *)pTable->record[ 0]); + + assert( uiNullOffset == 0); + + if( pucRow[ uiNullOffset] & pField->null_bit) + { + return( TRUE); + } + + return( FALSE); + } + + bool convertString( + String * pString, + CHARSET_INFO * pFromCharSet, + CHARSET_INFO * pToCharSet); + + int check( + THD * pThread, + HA_CHECK_OPT * pCheckOpt); + + inline int backup( + THD * pThread, + HA_CHECK_OPT * pCheckOpt) + { + return( 0); + } + +private: + + FLAIM_SHARE * m_pShare; + THR_LOCK_DATA m_lockData; + FlmConnection * m_pConn; + FlmRecord * m_pCurrKey; + FLMUINT m_uiCurrRowDrn; + String m_convertBuffer; + char m_szDbPath[ F_PATH_MAX_SIZE]; + char m_pucKeyBuf[ FLM_MAX_KEY_LENGTH]; +}; + +bool flaim_init( void); + +bool flaim_end( void); + +int flaim_start_trx_and_assign_read_view( + THD * pThread); + +#endif