mariadb/sql/ha_gemini.cc
2001-06-04 04:08:30 +03:00

3628 lines
98 KiB
C++

/* Copyright (C) 2000 NuSphere Corporation
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; either version 2 of the License, or
(at your option) any later version.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#ifdef __GNUC__
#pragma implementation // gcc: Class implementation
#endif
#include "mysql_priv.h"
#ifdef HAVE_GEMINI_DB
#include "ha_gemini.h"
#include "dbconfig.h"
#include "dsmpub.h"
#include "recpub.h"
#include "vststat.h"
#include <m_ctype.h>
#include <myisampack.h>
#include <m_string.h>
#include <assert.h>
#include <hash.h>
#include <stdarg.h>
#include "geminikey.h"
#define gemini_msg MSGD_CALLBACK
pthread_mutex_t gem_mutex;
static HASH gem_open_tables;
static GEM_SHARE *get_share(const char *table_name, TABLE *table);
static int free_share(GEM_SHARE *share, bool mutex_is_locked);
static byte* gem_get_key(GEM_SHARE *share,uint *length,
my_bool not_used __attribute__((unused)));
static void gemini_lock_table_overflow_error(dsmContext_t *pcontext);
const char *ha_gemini_ext=".gmd";
const char *ha_gemini_idx_ext=".gmi";
bool gemini_skip=0;
long gemini_options = 0;
long gemini_buffer_cache;
long gemini_io_threads;
long gemini_log_cluster_size;
long gemini_locktablesize;
long gemini_lock_wait_timeout;
long gemini_spin_retries;
long gemini_connection_limit;
char *gemini_basedir;
const char gemini_dbname[] = "gemini";
dsmContext_t *pfirstContext = NULL;
ulong gemini_recovery_options = GEMINI_RECOVERY_FULL;
/* bits in gemini_recovery_options */
const char *gemini_recovery_names[] =
{ "FULL", "NONE", "FORCE" };
TYPELIB gemini_recovery_typelib= {array_elements(gemini_recovery_names),"",
gemini_recovery_names};
const int start_of_name = 2; /* Name passed as ./<db>/<table-name>
and we're not interested in the ./ */
static const int keyBufSize = MAXKEYSZ + FULLKEYHDRSZ + MAX_REF_PARTS + 16;
static int gemini_tx_begin(THD *thd);
static void print_msg(THD *thd, const char *table_name, const char *op_name,
const char *msg_type, const char *fmt, ...);
static int gemini_helper_threads(dsmContext_t *pContext);
pthread_handler_decl(gemini_watchdog,arg );
pthread_handler_decl(gemini_rl_writer,arg );
pthread_handler_decl(gemini_apw,arg);
/* General functions */
bool gemini_init(void)
{
dsmStatus_t rc = 0;
char pmsgsfile[MAXPATHN];
DBUG_ENTER("gemini_init");
/* If datadir isn't set, bail out */
if (*mysql_real_data_home == '\0')
{
goto badret;
}
/* dsmContextCreate and dsmContextSetString(DSM_TAGDB_DBNAME) must
** be the first DSM calls we make so that we can log any errors which
** occur in subsequent DSM calls. DO NOT INSERT ANY DSM CALLS IN
** BETWEEN THIS COMMENT AND THE COMMENT THAT SAYS "END OF CODE..."
*/
/* Gotta connect to the database regardless of the operation */
rc = dsmContextCreate(&pfirstContext);
if( rc != 0 )
{
gemini_msg(pfirstContext, "dsmContextCreate failed %l",rc);
goto badret;
}
/* This call will also open the log file */
rc = dsmContextSetString(pfirstContext, DSM_TAGDB_DBNAME,
strlen(gemini_dbname), (TEXT *)gemini_dbname);
if( rc != 0 )
{
gemini_msg(pfirstContext, "Dbname tag failed %l", rc);
goto badret;
}
/* END OF CODE NOT TO MESS WITH */
fn_format(pmsgsfile, GEM_MSGS_FILE, language, ".db", 2 | 4);
rc = dsmContextSetString(pfirstContext, DSM_TAGDB_MSGS_FILE,
strlen(pmsgsfile), (TEXT *)pmsgsfile);
if( rc != 0 )
{
gemini_msg(pfirstContext, "MSGS_DIR tag failed %l", rc);
goto badret;
}
strxmov(pmsgsfile, gemini_basedir, GEM_SYM_FILE, NullS);
rc = dsmContextSetString(pfirstContext, DSM_TAGDB_SYMFILE,
strlen(pmsgsfile), (TEXT *)pmsgsfile);
if( rc != 0 )
{
gemini_msg(pfirstContext, "SYMFILE tag failed %l", rc);
goto badret;
}
rc = dsmContextSetLong(pfirstContext,DSM_TAGDB_ACCESS_TYPE,DSM_ACCESS_STARTUP);
if ( rc != 0 )
{
gemini_msg(pfirstContext, "ACCESS TAG set failed %l",rc);
goto badret;
}
rc = dsmContextSetLong(pfirstContext,DSM_TAGDB_ACCESS_ENV, DSM_SQL_ENGINE);
if( rc != 0 )
{
gemini_msg(pfirstContext, "ACCESS_ENV set failed %l",rc);
goto badret;
}
rc = dsmContextSetString(pfirstContext, DSM_TAGDB_DATADIR,
strlen(mysql_real_data_home),
(TEXT *)mysql_real_data_home);
if( rc != 0 )
{
gemini_msg(pfirstContext, "Datadir tag failed %l", rc);
goto badret;
}
rc = dsmContextSetLong(pfirstContext, DSM_TAGDB_MAX_USERS,
gemini_connection_limit);
if(rc != 0)
{
gemini_msg(pfirstContext, "MAX_USERS tag set failed %l",rc);
goto badret;
}
rc = dsmContextSetLong(pfirstContext, DSM_TAGDB_DEFAULT_LOCK_TIMEOUT,
gemini_lock_wait_timeout);
if(rc != 0)
{
gemini_msg(pfirstContext, "MAX_LOCK_ENTRIES tag set failed %l",rc);
goto badret;
}
rc = dsmContextSetLong(pfirstContext, DSM_TAGDB_MAX_LOCK_ENTRIES,
gemini_locktablesize);
if(rc != 0)
{
gemini_msg(pfirstContext, "MAX_LOCK_ENTRIES tag set failed %l",rc);
goto badret;
}
rc = dsmContextSetLong(pfirstContext, DSM_TAGDB_SPIN_AMOUNT,
gemini_spin_retries);
if(rc != 0)
{
gemini_msg(pfirstContext, "SPIN_AMOUNT tag set failed %l",rc);
goto badret;
}
/* blocksize is hardcoded to 8K. Buffer cache is in bytes
need to convert this to 8K blocks */
gemini_buffer_cache = gemini_buffer_cache / 8192;
rc = dsmContextSetLong(pfirstContext, DSM_TAGDB_DB_BUFFERS,
gemini_buffer_cache);
if(rc != 0)
{
gemini_msg(pfirstContext, "DB_BUFFERS tag set failed %l",rc);
goto badret;
}
rc = dsmContextSetLong(pfirstContext, DSM_TAGDB_FLUSH_AT_COMMIT,
((gemini_options & GEMOPT_FLUSH_LOG) ? 0 : 1));
if(rc != 0)
{
gemini_msg(pfirstContext, "FLush_Log_At_Commit tag set failed %l",rc);
goto badret;
}
rc = dsmContextSetLong(pfirstContext, DSM_TAGDB_DIRECT_IO,
((gemini_options & GEMOPT_UNBUFFERED_IO) ? 1 : 0));
if(rc != 0)
{
gemini_msg(pfirstContext, "DIRECT_IO tag set failed %l",rc);
goto badret;
}
rc = dsmContextSetLong(pfirstContext, DSM_TAGDB_CRASH_PROTECTION,
((gemini_recovery_options & GEMINI_RECOVERY_FULL) ? 1 : 0));
if(rc != 0)
{
gemini_msg(pfirstContext, "CRASH_PROTECTION tag set failed %l",rc);
goto badret;
}
if (gemini_recovery_options & GEMINI_RECOVERY_FORCE)
{
rc = dsmContextSetLong(pfirstContext, DSM_TAGDB_FORCE_ACCESS, 1);
if(rc != 0)
{
printf("CRASH_PROTECTION tag set failed %ld",rc);
goto badret;
}
}
/* cluster size will come in bytes, need to convert it to
16 K units. */
gemini_log_cluster_size = (gemini_log_cluster_size + 16383) / 16384;
rc = dsmContextSetLong(pfirstContext, DSM_TAGDB_BI_CLUSTER_SIZE,
gemini_log_cluster_size);
if(rc != 0)
{
gemini_msg(pfirstContext, "CRASH_PROTECTION tag set failed %l",rc);
goto badret;
}
rc = dsmUserConnect(pfirstContext,(TEXT *)"Multi-user",
DSM_DB_OPENDB | DSM_DB_OPENFILE);
if( rc != 0 )
{
/* Message is output in dbenv() */
goto badret;
}
/* Set access to shared for subsequent user connects */
rc = dsmContextSetLong(pfirstContext,DSM_TAGDB_ACCESS_TYPE,DSM_ACCESS_SHARED);
rc = gemini_helper_threads(pfirstContext);
(void) hash_init(&gem_open_tables,32,0,0,
(hash_get_key) gem_get_key,0,0);
pthread_mutex_init(&gem_mutex,NULL);
DBUG_RETURN(0);
badret:
gemini_skip = 1;
DBUG_RETURN(0);
}
static int gemini_helper_threads(dsmContext_t *pContext)
{
int rc = 0;
int i;
pthread_attr_t thr_attr;
pthread_t hThread;
DBUG_ENTER("gemini_helper_threads");
(void) pthread_attr_init(&thr_attr);
#if !defined(HAVE_DEC_3_2_THREADS)
pthread_attr_setscope(&thr_attr,PTHREAD_SCOPE_SYSTEM);
(void) pthread_attr_setdetachstate(&thr_attr,PTHREAD_CREATE_DETACHED);
pthread_attr_setstacksize(&thr_attr,32768);
#endif
rc = pthread_create (&hThread, &thr_attr, gemini_watchdog, (void *)pContext);
if (rc)
{
gemini_msg(pContext, "Can't Create gemini watchdog thread");
goto done;
}
if(!gemini_io_threads)
goto done;
rc = pthread_create(&hThread, &thr_attr, gemini_rl_writer, (void *)pContext);
if(rc)
{
gemini_msg(pContext, "Can't create Gemini recovery log writer thread");
goto done;
}
for(i = gemini_io_threads - 1;i;i--)
{
rc = pthread_create(&hThread, &thr_attr, gemini_apw, (void *)pContext);
if(rc)
{
gemini_msg(pContext, "Can't create Gemini database page writer thread");
goto done;
}
}
done:
DBUG_RETURN(rc);
}
pthread_handler_decl(gemini_watchdog,arg )
{
int rc = 0;
dsmContext_t *pcontext = (dsmContext_t *)arg;
dsmContext_t *pmyContext = NULL;
rc = dsmContextCopy(pcontext,&pmyContext, DSMCONTEXTDB);
if( rc != 0 )
{
gemini_msg(pcontext, "dsmContextCopy failed for Gemini watchdog %d",rc);
return 0;
}
rc = dsmUserConnect(pmyContext,NULL,0);
if( rc != 0 )
{
gemini_msg(pcontext, "dsmUserConnect failed for Gemini watchdog %d",rc);
return 0;
}
my_thread_init();
pthread_detach_this_thread();
while(rc == 0)
{
rc = dsmDatabaseProcessEvents(pmyContext);
if(!rc)
rc = dsmWatchdog(pmyContext);
sleep(1);
}
rc = dsmUserDisconnect(pmyContext,0);
my_thread_end();
return 0;
}
pthread_handler_decl(gemini_rl_writer,arg )
{
int rc = 0;
dsmContext_t *pcontext = (dsmContext_t *)arg;
dsmContext_t *pmyContext = NULL;
rc = dsmContextCopy(pcontext,&pmyContext, DSMCONTEXTDB);
if( rc != 0 )
{
gemini_msg(pcontext, "dsmContextCopy failed for Gemini recovery log writer %d",rc);
return 0;
}
rc = dsmUserConnect(pmyContext,NULL,0);
if( rc != 0 )
{
gemini_msg(pcontext, "dsmUserConnect failed for Gemini recovery log writer %d",rc);
return 0;
}
my_thread_init();
pthread_detach_this_thread();
while(rc == 0)
{
rc = dsmRLwriter(pmyContext);
}
rc = dsmUserDisconnect(pmyContext,0);
my_thread_end();
return 0;
}
pthread_handler_decl(gemini_apw,arg )
{
int rc = 0;
dsmContext_t *pcontext = (dsmContext_t *)arg;
dsmContext_t *pmyContext = NULL;
my_thread_init();
pthread_detach_this_thread();
rc = dsmContextCopy(pcontext,&pmyContext, DSMCONTEXTDB);
if( rc != 0 )
{
gemini_msg(pcontext, "dsmContextCopy failed for Gemini page writer %d",rc);
my_thread_end();
return 0;
}
rc = dsmUserConnect(pmyContext,NULL,0);
if( rc != 0 )
{
gemini_msg(pcontext, "dsmUserConnect failed for Gemini page writer %d",rc);
my_thread_end();
return 0;
}
while(rc == 0)
{
rc = dsmAPW(pmyContext);
}
rc = dsmUserDisconnect(pmyContext,0);
my_thread_end();
return 0;
}
int gemini_set_option_long(int optid, long optval)
{
dsmStatus_t rc = 0;
switch (optid)
{
case GEM_OPTID_SPIN_RETRIES:
/* If we don't have a context yet, skip the set and just save the
** value in gemini_spin_retries for a later gemini_init(). This
** may not ever happen, but we're covered if it does.
*/
if (pfirstContext)
{
rc = dsmContextSetLong(pfirstContext, DSM_TAGDB_SPIN_AMOUNT,
optval);
}
if (rc)
{
gemini_msg(pfirstContext, "SPIN_AMOUNT tag set failed %l",rc);
}
else
{
gemini_spin_retries = optval;
}
break;
}
return rc;
}
static int gemini_connect(THD *thd)
{
DBUG_ENTER("gemini_connect");
dsmStatus_t rc;
rc = dsmContextCopy(pfirstContext,(dsmContext_t **)&thd->gemini.context,
DSMCONTEXTDB);
if( rc != 0 )
{
gemini_msg(pfirstContext, "dsmContextCopy failed %l",rc);
return(rc);
}
rc = dsmUserConnect((dsmContext_t *)thd->gemini.context,NULL,0);
if( rc != 0 )
{
gemini_msg(pfirstContext, "dsmUserConnect failed %l",rc);
return(rc);
}
rc = (dsmStatus_t)gemini_tx_begin(thd);
DBUG_RETURN(rc);
}
void gemini_disconnect(THD *thd)
{
dsmStatus_t rc;
if(thd->gemini.context)
{
rc = dsmUserDisconnect((dsmContext_t *)thd->gemini.context,0);
}
return;
}
bool gemini_end(void)
{
dsmStatus_t rc;
THD *thd;
DBUG_ENTER("gemini_end");
hash_free(&gem_open_tables);
pthread_mutex_destroy(&gem_mutex);
if(pfirstContext)
{
rc = dsmShutdownSet(pfirstContext, DSM_SHUTDOWN_NORMAL);
sleep(2);
rc = dsmContextSetLong(pfirstContext,DSM_TAGDB_ACCESS_TYPE,DSM_ACCESS_STARTUP);
rc = dsmShutdown(pfirstContext, DSMNICEBIT,DSMNICEBIT);
}
DBUG_RETURN(0);
}
bool gemini_flush_logs()
{
DBUG_ENTER("gemini_flush_logs");
DBUG_RETURN(0);
}
static int gemini_tx_begin(THD *thd)
{
dsmStatus_t rc;
DBUG_ENTER("gemini_tx_begin");
thd->gemini.savepoint = 1;
rc = dsmTransaction((dsmContext_t *)thd->gemini.context,
&thd->gemini.savepoint,DSMTXN_START,0,NULL);
if(!rc)
thd->gemini.needSavepoint = 1;
thd->gemini.tx_isolation = thd->tx_isolation;
DBUG_PRINT("trans",("beginning transaction"));
DBUG_RETURN(rc);
}
int gemini_commit(THD *thd)
{
dsmStatus_t rc;
LONG txNumber = 0;
DBUG_ENTER("gemini_commit");
if(!thd->gemini.context)
DBUG_RETURN(0);
rc = dsmTransaction((dsmContext_t *)thd->gemini.context,
0,DSMTXN_COMMIT,0,NULL);
if(!rc)
rc = gemini_tx_begin(thd);
thd->gemini.lock_count = 0;
DBUG_PRINT("trans",("ending transaction"));
DBUG_RETURN(rc);
}
int gemini_rollback(THD *thd)
{
dsmStatus_t rc;
LONG txNumber;
DBUG_ENTER("gemini_rollback");
DBUG_PRINT("trans",("aborting transaction"));
if(!thd->gemini.context)
DBUG_RETURN(0);
thd->gemini.savepoint = 0;
rc = dsmTransaction((dsmContext_t *)thd->gemini.context,
&thd->gemini.savepoint,DSMTXN_ABORT,0,NULL);
if(!rc)
rc = gemini_tx_begin(thd);
thd->gemini.lock_count = 0;
DBUG_RETURN(rc);
}
int gemini_rollback_to_savepoint(THD *thd)
{
dsmStatus_t rc = 0;
DBUG_ENTER("gemini_rollback_to_savepoint");
if(thd->gemini.savepoint > 1)
{
rc = dsmTransaction((dsmContext_t *)thd->gemini.context,
&thd->gemini.savepoint,DSMTXN_UNSAVE,0,NULL);
}
DBUG_RETURN(rc);
}
int gemini_recovery_logging(THD *thd, bool on)
{
int error;
int noLogging;
if(!thd->gemini.context)
return 0;
if(on)
noLogging = 0;
else
noLogging = 1;
error = dsmContextSetLong((dsmContext_t *)thd->gemini.context,
DSM_TAGCONTEXT_NO_LOGGING,noLogging);
return error;
}
/* gemDataType - translates from mysql data type constant to gemini
key services data type contstant */
int gemDataType ( int mysqlType )
{
switch (mysqlType)
{
case FIELD_TYPE_LONG:
case FIELD_TYPE_TINY:
case FIELD_TYPE_SHORT:
case FIELD_TYPE_TIMESTAMP:
case FIELD_TYPE_LONGLONG:
case FIELD_TYPE_INT24:
case FIELD_TYPE_DATE:
case FIELD_TYPE_TIME:
case FIELD_TYPE_DATETIME:
case FIELD_TYPE_YEAR:
case FIELD_TYPE_NEWDATE:
case FIELD_TYPE_ENUM:
case FIELD_TYPE_SET:
return GEM_INT;
case FIELD_TYPE_DECIMAL:
return GEM_DECIMAL;
case FIELD_TYPE_FLOAT:
return GEM_FLOAT;
case FIELD_TYPE_DOUBLE:
return GEM_DOUBLE;
case FIELD_TYPE_TINY_BLOB:
return GEM_TINYBLOB;
case FIELD_TYPE_MEDIUM_BLOB:
return GEM_MEDIUMBLOB;
case FIELD_TYPE_LONG_BLOB:
return GEM_LONGBLOB;
case FIELD_TYPE_BLOB:
return GEM_BLOB;
case FIELD_TYPE_VAR_STRING:
case FIELD_TYPE_STRING:
return GEM_CHAR;
}
return -1;
}
/*****************************************************************************
** Gemini tables
*****************************************************************************/
const char **ha_gemini::bas_ext() const
{ static const char *ext[]= { ha_gemini_ext, ha_gemini_idx_ext, NullS };
return ext;
}
int ha_gemini::open(const char *name, int mode, uint test_if_locked)
{
dsmObject_t tableId = 0;
THD *thd;
char name_buff[FN_REFLEN];
char tabname_buff[FN_REFLEN];
char dbname_buff[FN_REFLEN];
unsigned i,nameLen;
LONG txNumber;
dsmStatus_t rc;
DBUG_ENTER("ha_gemini::open");
thd = current_thd;
/* Init shared structure */
if (!(share=get_share(name,table)))
{
DBUG_RETURN(1); /* purecov: inspected */
}
thr_lock_data_init(&share->lock,&lock,(void*) 0);
ref_length = sizeof(dsmRecid_t);
if(thd->gemini.context == NULL)
{
/* Need to get this thread a connection into the database */
rc = gemini_connect(thd);
if(rc)
return rc;
}
if (!(rec_buff=(byte*)my_malloc(table->rec_buff_length,
MYF(MY_WME))))
{
DBUG_RETURN(1);
}
/* separate out the name of the table and the database (a VST must be
** created in the mysql database)
*/
rc = gemini_parse_table_name(name, dbname_buff, tabname_buff);
if (rc == 0)
{
if (strcmp(dbname_buff, "mysql") == 0)
{
tableId = gemini_is_vst(tabname_buff);
}
}
sprintf(name_buff, "%s.%s", dbname_buff, tabname_buff);
/* if it's not a VST, get the table number the regular way */
if (!tableId)
{
rc = dsmObjectNameToNum((dsmContext_t *)thd->gemini.context,
(dsmText_t *)name_buff,
&tableId);
if (rc)
{
gemini_msg((dsmContext_t *)thd->gemini.context,
"Unable to find table number for %s", name_buff);
DBUG_RETURN(rc);
}
}
tableNumber = tableId;
if(!rc)
rc = index_open(name_buff);
fixed_length_row=!(table->db_create_options & HA_OPTION_PACK_RECORD);
key_read = 0;
using_ignore = 0;
/* Get the gemini table status -- we want to know if the table
crashed while being in the midst of a repair operation */
rc = dsmTableStatus((dsmContext_t *)thd->gemini.context,
tableNumber,&tableStatus);
if(tableStatus == DSM_OBJECT_IN_REPAIR)
tableStatus = HA_ERR_CRASHED;
pthread_mutex_lock(&share->mutex);
share->use_count++;
pthread_mutex_unlock(&share->mutex);
if (table->blob_fields)
{
/* Allocate room for the blob ids from an unpacked row. Note that
** we may not actually need all of this space because tiny blobs
** are stored in the packed row, not in a separate storage object
** like larger blobs. But we allocate an entry for all blobs to
** keep the code simpler.
*/
pBlobDescs = (gemBlobDesc_t *)my_malloc(
table->blob_fields * sizeof(gemBlobDesc_t),
MYF(MY_WME | MY_ZEROFILL));
}
else
{
pBlobDescs = 0;
}
get_index_stats(thd);
info(HA_STATUS_CONST);
DBUG_RETURN (rc);
}
/* Look up and store the object numbers for the indexes on this table */
int ha_gemini::index_open(char *tableName)
{
dsmStatus_t rc = 0;
int nameLen;
DBUG_ENTER("ha_gemini::index_open");
if(table->keys)
{
THD *thd = current_thd;
dsmObject_t objectNumber;
if (!(pindexNumbers=(dsmIndex_t *)my_malloc(table->keys*sizeof(dsmIndex_t),
MYF(MY_WME))))
{
DBUG_RETURN(1);
}
nameLen = strlen(tableName);
tableName[nameLen] = '.';
nameLen++;
for( uint i = 0; i < table->keys && !rc; i++)
{
strcpy(&tableName[nameLen],table->key_info[i].name);
rc = dsmObjectNameToNum((dsmContext_t *)thd->gemini.context,
(dsmText_t *)tableName,
&objectNumber);
if (rc)
{
gemini_msg((dsmContext_t *)thd->gemini.context,
"Unable to file Index number for %s", tableName);
DBUG_RETURN(rc);
}
pindexNumbers[i] = objectNumber;
}
}
else
pindexNumbers = 0;
DBUG_RETURN(rc);
}
int ha_gemini::close(void)
{
DBUG_ENTER("ha_gemini::close");
my_free((char*)rec_buff,MYF(MY_ALLOW_ZERO_PTR));
rec_buff = 0;
my_free((char *)pindexNumbers,MYF(MY_ALLOW_ZERO_PTR));
pindexNumbers = 0;
if (pBlobDescs)
{
for (uint i = 0; i < table->blob_fields; i++)
{
my_free((char*)pBlobDescs[i].pBlob, MYF(MY_ALLOW_ZERO_PTR));
}
my_free((char *)pBlobDescs, MYF(0));
pBlobDescs = 0;
}
DBUG_RETURN(free_share(share, 0));
}
int ha_gemini::write_row(byte * record)
{
int error = 0;
dsmRecord_t dsmRecord;
THD *thd;
DBUG_ENTER("write_row");
if(tableStatus == HA_ERR_CRASHED)
DBUG_RETURN(tableStatus);
thd = current_thd;
statistic_increment(ha_write_count,&LOCK_status);
if (table->time_stamp)
update_timestamp(record+table->time_stamp-1);
if(thd->gemini.needSavepoint || using_ignore)
{
thd->gemini.savepoint++;
error = dsmTransaction((dsmContext_t *)thd->gemini.context,
&thd->gemini.savepoint,
DSMTXN_SAVE, 0, 0);
if (error)
DBUG_RETURN(error);
thd->gemini.needSavepoint = 0;
}
if (table->next_number_field && record == table->record[0])
{
if(thd->next_insert_id)
{
ULONG64 nr;
/* A set insert-id statement so set the auto-increment value if this
value is higher than it's current value */
error = dsmTableAutoIncrement((dsmContext_t *)thd->gemini.context,
tableNumber, (ULONG64 *)&nr,1);
if(thd->next_insert_id > nr)
{
error = dsmTableAutoIncrementSet((dsmContext_t *)thd->gemini.context,
tableNumber,
(ULONG64)thd->next_insert_id);
}
}
update_auto_increment();
}
dsmRecord.table = tableNumber;
dsmRecord.maxLength = table->rec_buff_length;
if ((error=pack_row((byte **)&dsmRecord.pbuffer, (int *)&dsmRecord.recLength,
record, FALSE)))
{
DBUG_RETURN(error);
}
error = dsmRecordCreate((dsmContext_t *)thd->gemini.context,
&dsmRecord,0);
if(!error)
{
error = handleIndexEntries(record, dsmRecord.recid,KEY_CREATE);
if(error == HA_ERR_FOUND_DUPP_KEY && using_ignore)
{
dsmStatus_t rc;
rc = dsmTransaction((dsmContext_t *)thd->gemini.context,
&thd->gemini.savepoint,DSMTXN_UNSAVE,0,NULL);
thd->gemini.needSavepoint = 1;
}
}
if(error == DSM_S_RQSTREJ)
error = HA_ERR_LOCK_WAIT_TIMEOUT;
DBUG_RETURN(error);
}
longlong ha_gemini::get_auto_increment()
{
longlong nr;
int error;
int update;
THD *thd=current_thd;
if(thd->lex.sql_command == SQLCOM_SHOW_TABLES)
update = 0;
else
update = 1;
error = dsmTableAutoIncrement((dsmContext_t *)thd->gemini.context,
tableNumber, (ULONG64 *)&nr,
update);
return nr;
}
/* Put or delete index entries for a row */
int ha_gemini::handleIndexEntries(const byte * record, dsmRecid_t recid,
enum_key_string_options option)
{
dsmStatus_t rc = 0;
DBUG_ENTER("handleIndexEntries");
for (uint i = 0; i < table->keys && rc == 0; i++)
{
rc = handleIndexEntry(record, recid,option, i);
}
DBUG_RETURN(rc);
}
int ha_gemini::handleIndexEntry(const byte * record, dsmRecid_t recid,
enum_key_string_options option,uint keynr)
{
dsmStatus_t rc = 0;
KEY *key_info;
int keyStringLen;
bool thereIsAnull;
THD *thd;
AUTOKEY(theKey,keyBufSize);
DBUG_ENTER("handleIndexEntry");
thd = current_thd;
key_info=table->key_info+keynr;
thereIsAnull = false;
rc = createKeyString(record, key_info, theKey.akey.keystr,
sizeof(theKey.apad),&keyStringLen,
(short)pindexNumbers[keynr],
&thereIsAnull);
if(!rc)
{
theKey.akey.index = pindexNumbers[keynr];
theKey.akey.keycomps = (COUNT)key_info->key_parts;
/* We have to subtract three here since cxKeyPrepare
expects that the three lead bytes of the header are
not counted in this length -- But cxKeyPrepare also
expects that these three bytes are present in the keystr */
theKey.akey.keyLen = (COUNT)keyStringLen - FULLKEYHDRSZ;
theKey.akey.unknown_comp = (dsmBoolean_t)thereIsAnull;
theKey.akey.word_index = 0;
theKey.akey.descending_key =0;
if(option == KEY_CREATE)
{
rc = dsmKeyCreate((dsmContext_t *)thd->gemini.context, &theKey.akey,
(dsmTable_t)tableNumber, recid, NULL);
if(rc == DSM_S_IXDUPKEY)
{
last_dup_key=keynr;
rc = HA_ERR_FOUND_DUPP_KEY;
}
}
else if(option == KEY_DELETE)
{
rc = dsmKeyDelete((dsmContext_t *)thd->gemini.context, &theKey.akey,
(dsmTable_t)tableNumber, recid, 0, NULL);
}
else
{
/* KEY_CHECK */
dsmCursid_t aCursorId;
int error;
rc = dsmCursorCreate((dsmContext_t *)thd->gemini.context,
(dsmTable_t)tableNumber,
(dsmIndex_t)pindexNumbers[keynr],
&aCursorId,NULL);
rc = dsmCursorFind((dsmContext_t *)thd->gemini.context,
&aCursorId,&theKey.akey,NULL,DSMDBKEY,
DSMFINDFIRST,DSM_LK_SHARE,0,
&lastRowid,0);
error = dsmCursorDelete((dsmContext_t *)thd->gemini.context,
&aCursorId, 0);
}
}
DBUG_RETURN(rc);
}
int ha_gemini::createKeyString(const byte * record, KEY *pkeyinfo,
unsigned char *pkeyBuf, int bufSize,
int *pkeyStringLen,
short geminiIndexNumber,
bool *thereIsAnull)
{
dsmStatus_t rc = 0;
int componentLen;
int fieldType;
int isNull;
uint key_part_length;
KEY_PART_INFO *key_part;
DBUG_ENTER("createKeyString");
rc = gemKeyInit(pkeyBuf,pkeyStringLen, geminiIndexNumber);
for(uint i = 0; i < pkeyinfo->key_parts && rc == 0; i++)
{
unsigned char *pos;
key_part = pkeyinfo->key_part + i;
key_part_length = key_part->length;
fieldType = gemDataType(key_part->field->type());
switch (fieldType)
{
case GEM_CHAR:
{
/* Save the current ptr to the field in case we're building a key
to remove an old key value when an indexed character column
gets updated. */
char *ptr = key_part->field->ptr;
key_part->field->ptr = (char *)record + key_part->offset;
key_part->field->sort_string((char*)rec_buff, key_part->length);
key_part->field->ptr = ptr;
pos = (unsigned char *)rec_buff;
}
break;
case GEM_TINYBLOB:
case GEM_BLOB:
case GEM_MEDIUMBLOB:
case GEM_LONGBLOB:
((Field_blob*)key_part->field)->get_ptr((char**)&pos);
key_part_length = ((Field_blob*)key_part->field)->get_length(
(char*)record + key_part->offset);
break;
default:
pos = (unsigned char *)record + key_part->offset;
break;
}
isNull = record[key_part->null_offset] & key_part->null_bit;
if(isNull)
*thereIsAnull = true;
rc = gemFieldToIdxComponent(pos,
(unsigned long) key_part_length,
fieldType,
isNull ,
key_part->field->flags & UNSIGNED_FLAG,
pkeyBuf + *pkeyStringLen,
bufSize,
&componentLen);
*pkeyStringLen += componentLen;
}
DBUG_RETURN(rc);
}
int ha_gemini::update_row(const byte * old_record, byte * new_record)
{
int error = 0;
dsmRecord_t dsmRecord;
unsigned long savepoint;
THD *thd = current_thd;
DBUG_ENTER("update_row");
statistic_increment(ha_update_count,&LOCK_status);
if (table->time_stamp)
update_timestamp(new_record+table->time_stamp-1);
if(thd->gemini.needSavepoint || using_ignore)
{
thd->gemini.savepoint++;
error = dsmTransaction((dsmContext_t *)thd->gemini.context,
&thd->gemini.savepoint,
DSMTXN_SAVE, 0, 0);
if (error)
DBUG_RETURN(error);
thd->gemini.needSavepoint = 0;
}
for (uint keynr=0 ; keynr < table->keys ; keynr++)
{
if(key_cmp(keynr,old_record, new_record,false))
{
error = handleIndexEntry(old_record,lastRowid,KEY_DELETE,keynr);
if(error)
DBUG_RETURN(error);
error = handleIndexEntry(new_record, lastRowid, KEY_CREATE, keynr);
if(error)
{
if (using_ignore && error == HA_ERR_FOUND_DUPP_KEY)
{
dsmStatus_t rc;
rc = dsmTransaction((dsmContext_t *)thd->gemini.context,
&thd->gemini.savepoint,DSMTXN_UNSAVE,0,NULL);
thd->gemini.needSavepoint = 1;
}
DBUG_RETURN(error);
}
}
}
dsmRecord.table = tableNumber;
dsmRecord.recid = lastRowid;
dsmRecord.maxLength = table->rec_buff_length;
if ((error=pack_row((byte **)&dsmRecord.pbuffer, (int *)&dsmRecord.recLength,
new_record, TRUE)))
{
DBUG_RETURN(error);
}
error = dsmRecordUpdate((dsmContext_t *)thd->gemini.context,
&dsmRecord, 0, NULL);
DBUG_RETURN(error);
}
int ha_gemini::delete_row(const byte * record)
{
int error = 0;
dsmRecord_t dsmRecord;
THD *thd = current_thd;
dsmContext_t *pcontext = (dsmContext_t *)thd->gemini.context;
DBUG_ENTER("delete_row");
statistic_increment(ha_delete_count,&LOCK_status);
if(thd->gemini.needSavepoint)
{
thd->gemini.savepoint++;
error = dsmTransaction(pcontext, &thd->gemini.savepoint, DSMTXN_SAVE, 0, 0);
if (error)
DBUG_RETURN(error);
thd->gemini.needSavepoint = 0;
}
dsmRecord.table = tableNumber;
dsmRecord.recid = lastRowid;
error = handleIndexEntries(record, dsmRecord.recid,KEY_DELETE);
if(!error)
{
error = dsmRecordDelete(pcontext, &dsmRecord, 0, NULL);
}
/* Delete any blobs associated with this row */
if (table->blob_fields)
{
dsmBlob_t gemBlob;
gemBlob.areaType = DSMOBJECT_BLOB;
gemBlob.blobObjNo = tableNumber;
for (uint i = 0; i < table->blob_fields; i++)
{
if (pBlobDescs[i].blobId)
{
gemBlob.blobId = pBlobDescs[i].blobId;
my_free((char *)pBlobDescs[i].pBlob, MYF(MY_ALLOW_ZERO_PTR));
dsmBlobStart(pcontext, &gemBlob);
dsmBlobDelete(pcontext, &gemBlob, NULL);
/* according to DSM doc, no need to call dsmBlobEnd() */
}
}
}
DBUG_RETURN(error);
}
int ha_gemini::index_init(uint keynr)
{
int error = 0;
THD *thd;
DBUG_ENTER("index_init");
thd = current_thd;
lastRowid = 0;
active_index=keynr;
error = dsmCursorCreate((dsmContext_t *)thd->gemini.context,
(dsmTable_t)tableNumber,
(dsmIndex_t)pindexNumbers[keynr],
&cursorId,NULL);
pbracketBase = (dsmKey_t *)my_malloc(sizeof(dsmKey_t) + keyBufSize,
MYF(MY_WME));
if(!pbracketBase)
DBUG_RETURN(1);
pbracketLimit = (dsmKey_t *)my_malloc(sizeof(dsmKey_t) + keyBufSize,MYF(MY_WME));
if(!pbracketLimit)
{
my_free((char *)pbracketLimit,MYF(0));
DBUG_RETURN(1);
}
pbracketBase->index = 0;
pbracketLimit->index = (dsmIndex_t)pindexNumbers[keynr];
pbracketBase->descending_key = pbracketLimit->descending_key = 0;
pbracketBase->ksubstr = pbracketLimit->ksubstr = 0;
pbracketLimit->keycomps = pbracketBase->keycomps = 1;
pfoundKey = (dsmKey_t *)my_malloc(sizeof(dsmKey_t) + keyBufSize,MYF(MY_WME));
if(!pfoundKey)
{
my_free((char *)pbracketLimit,MYF(0));
my_free((char *)pbracketBase,MYF(0));
DBUG_RETURN(1);
}
DBUG_RETURN(error);
}
int ha_gemini::index_end()
{
int error = 0;
THD *thd;
DBUG_ENTER("index_end");
thd = current_thd;
error = dsmCursorDelete((dsmContext_t *)thd->gemini.context,
&cursorId, 0);
if(pbracketLimit)
my_free((char *)pbracketLimit,MYF(0));
if(pbracketBase)
my_free((char *)pbracketBase,MYF(0));
if(pfoundKey)
my_free((char *)pfoundKey,MYF(0));
pbracketLimit = 0;
pbracketBase = 0;
pfoundKey = 0;
DBUG_RETURN(error);
}
/* This is only used to read whole keys */
int ha_gemini::index_read_idx(byte * buf, uint keynr, const byte * key,
uint key_len, enum ha_rkey_function find_flag)
{
int error = 0;
DBUG_ENTER("index_read_idx");
statistic_increment(ha_read_key_count,&LOCK_status);
error = index_init(keynr);
if (!error)
error = index_read(buf,key,key_len,find_flag);
if(error == HA_ERR_END_OF_FILE)
error = HA_ERR_KEY_NOT_FOUND;
table->status = error ? STATUS_NOT_FOUND : 0;
DBUG_RETURN(error);
}
int ha_gemini::pack_key( uint keynr, dsmKey_t *pkey,
const byte *key_ptr, uint key_length)
{
KEY *key_info=table->key_info+keynr;
KEY_PART_INFO *key_part=key_info->key_part;
KEY_PART_INFO *end=key_part+key_info->key_parts;
int rc;
int componentLen;
DBUG_ENTER("pack_key");
rc = gemKeyInit(pkey->keystr,&componentLen,
(short)pindexNumbers[active_index]);
pkey->keyLen = componentLen;
for (; key_part != end && (int) key_length > 0 && !rc; key_part++)
{
uint offset=0;
unsigned char *pos;
uint key_part_length = key_part->length;
int fieldType;
if (key_part->null_bit)
{
offset=1;
if (*key_ptr != 0) // Store 0 if NULL
{
key_length-= key_part->store_length;
key_ptr+= key_part->store_length;
rc = gemFieldToIdxComponent(
(unsigned char *)key_ptr + offset,
(unsigned long) key_part_length,
0,
1 , /* Tells it to build a null component */
key_part->field->flags & UNSIGNED_FLAG,
pkey->keystr + pkey->keyLen,
keyBufSize,
&componentLen);
pkey->keyLen += componentLen;
continue;
}
}
fieldType = gemDataType(key_part->field->type());
switch (fieldType)
{
case GEM_CHAR:
key_part->field->store((char*)key_ptr + offset, key_part->length);
key_part->field->sort_string((char*)rec_buff, key_part->length);
pos = (unsigned char *)rec_buff;
break;
case GEM_TINYBLOB:
case GEM_BLOB:
case GEM_MEDIUMBLOB:
case GEM_LONGBLOB:
((Field_blob*)key_part->field)->get_ptr((char**)&pos);
key_part_length = ((Field_blob*)key_part->field)->get_length(
(char*)key_ptr + offset);
break;
default:
pos = (unsigned char *)key_ptr + offset;
break;
}
rc = gemFieldToIdxComponent(
pos,
(unsigned long) key_part_length,
fieldType,
0 ,
key_part->field->flags & UNSIGNED_FLAG,
pkey->keystr + pkey->keyLen,
keyBufSize,
&componentLen);
key_ptr+=key_part->store_length;
key_length-=key_part->store_length;
pkey->keyLen += componentLen;
}
DBUG_RETURN(rc);
}
void ha_gemini::unpack_key(char *record, dsmKey_t *key, uint index)
{
KEY *key_info=table->key_info+index;
KEY_PART_INFO *key_part= key_info->key_part,
*end=key_part+key_info->key_parts;
int fieldIsNull, fieldType;
int rc = 0;
char unsigned *pos= &key->keystr[FULLKEYHDRSZ+4/* 4 for the index number*/];
for ( ; key_part != end; key_part++)
{
fieldType = gemDataType(key_part->field->type());
if(fieldType == GEM_CHAR)
{
/* Can't get data from character indexes since the sort weights
are in the index and not the characters. */
key_read = 0;
}
rc = gemIdxComponentToField(pos, fieldType,
(unsigned char *)record + key_part->field->offset(),
//key_part->field->field_length,
key_part->length,
key_part->field->decimals(),
&fieldIsNull);
if(fieldIsNull)
{
record[key_part->null_offset] |= key_part->null_bit;
}
else if (key_part->null_bit)
{
record[key_part->null_offset]&= ~key_part->null_bit;
}
while(*pos++); /* Advance to next field in key by finding */
/* a null byte */
}
}
int ha_gemini::index_read(byte * buf, const byte * key,
uint key_len, enum ha_rkey_function find_flag)
{
int error = 0;
THD *thd;
int componentLen;
DBUG_ENTER("index_read");
statistic_increment(ha_read_key_count,&LOCK_status);
pbracketBase->index = (short)pindexNumbers[active_index];
pbracketBase->keycomps = 1;
/* Its a greater than operation so create a base bracket
from the input key data. */
error = pack_key(active_index, pbracketBase, key, key_len);
if(error)
goto errorReturn;
if(find_flag == HA_READ_AFTER_KEY)
{
/* A greater than operation */
error = gemKeyAddLow(pbracketBase->keystr + pbracketBase->keyLen,
&componentLen);
pbracketBase->keyLen += componentLen;
}
if(find_flag == HA_READ_KEY_EXACT)
{
/* Need to set up a high bracket for an equality operator
Which is a copy of the base bracket plus a hi lim term */
bmove(pbracketLimit,pbracketBase,(size_t)pbracketBase->keyLen + sizeof(dsmKey_t));
error = gemKeyAddHigh(pbracketLimit->keystr + pbracketLimit->keyLen,
&componentLen);
if(error)
goto errorReturn;
pbracketLimit->keyLen += componentLen;
}
else
{
/* Always add a high range -- except for HA_READ_KEY_EXACT this
is all we need for the upper index bracket */
error = gemKeyHigh(pbracketLimit->keystr, &componentLen,
pbracketLimit->index);
pbracketLimit->keyLen = componentLen;
}
/* We have to subtract the header size here since cxKeyPrepare
expects that the three lead bytes of the header are
not counted in this length -- But cxKeyPrepare also
expects that these three bytes are present in the keystr */
pbracketBase->keyLen -= FULLKEYHDRSZ;
pbracketLimit->keyLen -= FULLKEYHDRSZ;
thd = current_thd;
error = findRow(thd, DSMFINDFIRST, buf);
errorReturn:
if (error == DSM_S_ENDLOOP)
error = HA_ERR_KEY_NOT_FOUND;
table->status = error ? STATUS_NOT_FOUND : 0;
DBUG_RETURN(error);
}
int ha_gemini::index_next(byte * buf)
{
THD *thd;
int error = 1;
int keyStringLen=0;
dsmMask_t findMode;
DBUG_ENTER("index_next");
if(tableStatus == HA_ERR_CRASHED)
DBUG_RETURN(tableStatus);
thd = current_thd;
if(pbracketBase->index == 0)
{
error = gemKeyLow(pbracketBase->keystr, &keyStringLen,
pbracketLimit->index);
pbracketBase->keyLen = (COUNT)keyStringLen - FULLKEYHDRSZ;
pbracketBase->index = pbracketLimit->index;
error = gemKeyHigh(pbracketLimit->keystr, &keyStringLen,
pbracketLimit->index);
pbracketLimit->keyLen = (COUNT)keyStringLen - FULLKEYHDRSZ;
findMode = DSMFINDFIRST;
}
else
findMode = DSMFINDNEXT;
error = findRow(thd,findMode,buf);
if (error == DSM_S_ENDLOOP)
error = HA_ERR_END_OF_FILE;
table->status = error ? STATUS_NOT_FOUND : 0;
DBUG_RETURN(error);
}
int ha_gemini::index_next_same(byte * buf, const byte *key, uint keylen)
{
int error = 0;
DBUG_ENTER("index_next_same");
statistic_increment(ha_read_next_count,&LOCK_status);
DBUG_RETURN(index_next(buf));
}
int ha_gemini::index_prev(byte * buf)
{
int error = 0;
THD *thd = current_thd;
DBUG_ENTER("index_prev");
statistic_increment(ha_read_prev_count,&LOCK_status);
error = findRow(thd, DSMFINDPREV, buf);
if (error == DSM_S_ENDLOOP)
error = HA_ERR_END_OF_FILE;
table->status = error ? STATUS_NOT_FOUND : 0;
DBUG_RETURN(error);
}
int ha_gemini::index_first(byte * buf)
{
DBUG_ENTER("index_first");
statistic_increment(ha_read_first_count,&LOCK_status);
DBUG_RETURN(index_next(buf));
}
int ha_gemini::index_last(byte * buf)
{
int error = 0;
THD *thd;
int keyStringLen;
dsmMask_t findMode;
thd = current_thd;
DBUG_ENTER("index_last");
statistic_increment(ha_read_last_count,&LOCK_status);
error = gemKeyLow(pbracketBase->keystr, &keyStringLen,
pbracketLimit->index);
pbracketBase->keyLen = (COUNT)keyStringLen - FULLKEYHDRSZ;
pbracketBase->index = pbracketLimit->index;
error = gemKeyHigh(pbracketLimit->keystr, &keyStringLen,
pbracketLimit->index);
pbracketLimit->keyLen = (COUNT)keyStringLen - FULLKEYHDRSZ;
error = findRow(thd,DSMFINDLAST,buf);
if (error == DSM_S_ENDLOOP)
error = HA_ERR_END_OF_FILE;
table->status = error ? STATUS_NOT_FOUND : 0;
DBUG_RETURN(error);
}
int ha_gemini::rnd_init(bool scan)
{
THD *thd = current_thd;
lastRowid = 0;
return 0;
}
int ha_gemini::rnd_end()
{
/*
return gem_scan_end();
*/
return 0;
}
int ha_gemini::rnd_next(byte *buf)
{
int error = 0;
dsmRecord_t dsmRecord;
THD *thd;
DBUG_ENTER("rnd_next");
if(tableStatus == HA_ERR_CRASHED)
DBUG_RETURN(tableStatus);
thd = current_thd;
if(thd->gemini.tx_isolation == ISO_READ_COMMITTED && !(lockMode & DSM_LK_EXCL)
&& lastRowid)
error = dsmObjectUnlock((dsmContext_t *)thd->gemini.context,
tableNumber, DSMOBJECT_RECORD, lastRowid,
lockMode | DSM_UNLK_FREE, 0);
statistic_increment(ha_read_rnd_next_count,&LOCK_status);
dsmRecord.table = tableNumber;
dsmRecord.recid = lastRowid;
dsmRecord.pbuffer = (dsmBuffer_t *)rec_buff;
dsmRecord.recLength = table->reclength;
dsmRecord.maxLength = table->rec_buff_length;
error = dsmTableScan((dsmContext_t *)thd->gemini.context,
&dsmRecord, DSMFINDNEXT, lockMode, 0);
if(!error)
{
lastRowid = dsmRecord.recid;
error = unpack_row((char *)buf,(char *)dsmRecord.pbuffer);
}
if(!error)
;
else
{
lastRowid = 0;
if (error == DSM_S_ENDLOOP)
error = HA_ERR_END_OF_FILE;
else if (error == DSM_S_RQSTREJ)
error = HA_ERR_LOCK_WAIT_TIMEOUT;
else if (error == DSM_S_LKTBFULL)
{
error = HA_ERR_LOCK_TABLE_FULL;
gemini_lock_table_overflow_error((dsmContext_t *)thd->gemini.context);
}
}
table->status = error ? STATUS_NOT_FOUND : 0;
DBUG_RETURN(error);
}
int ha_gemini::rnd_pos(byte * buf, byte *pos)
{
int error;
int rc;
THD *thd;
statistic_increment(ha_read_rnd_count,&LOCK_status);
thd = current_thd;
memcpy((void *)&lastRowid,pos,ref_length);
if(thd->gemini.tx_isolation == ISO_READ_COMMITTED && !(lockMode & DSM_LK_EXCL))
{
/* Lock the row */
error = dsmObjectLock((dsmContext_t *)thd->gemini.context,
(dsmObject_t)tableNumber,DSMOBJECT_RECORD,lastRowid,
lockMode, 1, 0);
if ( error )
goto errorReturn;
}
error = fetch_row(thd->gemini.context, buf);
if(thd->gemini.tx_isolation == ISO_READ_COMMITTED && !(lockMode & DSM_LK_EXCL))
{
/* Unlock the row */
rc = dsmObjectUnlock((dsmContext_t *)thd->gemini.context,
(dsmObject_t)tableNumber,DSMOBJECT_RECORD,lastRowid,
lockMode | DSM_UNLK_FREE , 0);
}
if(error == DSM_S_RMNOTFND)
error = HA_ERR_RECORD_DELETED;
errorReturn:
table->status = error ? STATUS_NOT_FOUND : 0;
return error;
}
int ha_gemini::fetch_row(void *gemini_context,const byte *buf)
{
dsmStatus_t rc = 0;
dsmRecord_t dsmRecord;
DBUG_ENTER("fetch_row");
dsmRecord.table = tableNumber;
dsmRecord.recid = lastRowid;
dsmRecord.pbuffer = (dsmBuffer_t *)rec_buff;
dsmRecord.recLength = table->reclength;
dsmRecord.maxLength = table->rec_buff_length;
rc = dsmRecordGet((dsmContext_t *)gemini_context,
&dsmRecord, 0);
if(!rc)
{
rc = unpack_row((char *)buf,(char *)dsmRecord.pbuffer);
}
DBUG_RETURN(rc);
}
int ha_gemini::findRow(THD *thd, dsmMask_t findMode, byte *buf)
{
dsmStatus_t rc;
dsmKey_t *pkey;
DBUG_ENTER("findRow");
if(thd->gemini.tx_isolation == ISO_READ_COMMITTED && !(lockMode & DSM_LK_EXCL)
&& lastRowid)
rc = dsmObjectUnlock((dsmContext_t *)thd->gemini.context,
tableNumber, DSMOBJECT_RECORD, lastRowid,
lockMode | DSM_UNLK_FREE, 0);
if( key_read )
pkey = pfoundKey;
else
pkey = 0;
rc = dsmCursorFind((dsmContext_t *)thd->gemini.context,
&cursorId,
pbracketBase,
pbracketLimit,
DSMPARTIAL,
findMode,
lockMode,
NULL,
&lastRowid,
pkey);
if( rc )
goto errorReturn;
if(key_read)
{
unpack_key((char*)buf, pkey, active_index);
}
if(!key_read) /* unpack_key may have turned off key_read */
{
rc = fetch_row((dsmContext_t *)thd->gemini.context,buf);
}
errorReturn:
if(!rc)
;
else
{
lastRowid = 0;
if(rc == DSM_S_RQSTREJ)
rc = HA_ERR_LOCK_WAIT_TIMEOUT;
else if (rc == DSM_S_LKTBFULL)
{
rc = HA_ERR_LOCK_TABLE_FULL;
gemini_lock_table_overflow_error((dsmContext_t *)thd->gemini.context);
}
}
DBUG_RETURN(rc);
}
void ha_gemini::position(const byte *record)
{
memcpy(ref,&lastRowid,ref_length);
}
void ha_gemini::info(uint flag)
{
DBUG_ENTER("info");
if ((flag & HA_STATUS_VARIABLE))
{
THD *thd = current_thd;
dsmStatus_t error;
ULONG64 rows;
if(thd->gemini.context == NULL)
{
/* Need to get this thread a connection into the database */
error = gemini_connect(thd);
if(error)
DBUG_VOID_RETURN;
}
error = dsmRowCount((dsmContext_t *)thd->gemini.context,tableNumber,&rows);
records = (ha_rows)rows;
deleted = 0;
}
if ((flag & HA_STATUS_CONST))
{
ha_rows *rec_per_key = share->rec_per_key;
for (uint i = 0; i < table->keys; i++)
for(uint k=0;
k < table->key_info[i].key_parts; k++,rec_per_key++)
table->key_info[i].rec_per_key[k] = *rec_per_key;
}
if ((flag & HA_STATUS_ERRKEY))
{
errkey=last_dup_key;
}
if ((flag & HA_STATUS_TIME))
{
;
}
if ((flag & HA_STATUS_AUTO))
{
THD *thd = current_thd;
dsmStatus_t error;
error = dsmTableAutoIncrement((dsmContext_t *)thd->gemini.context,
tableNumber,
(ULONG64 *)&auto_increment_value,
0);
/* Should return the next auto-increment value that
will be given -- so we need to increment the one dsm
currently reports. */
auto_increment_value++;
}
DBUG_VOID_RETURN;
}
int ha_gemini::extra(enum ha_extra_function operation)
{
switch (operation)
{
case HA_EXTRA_RESET:
case HA_EXTRA_RESET_STATE:
key_read=0;
using_ignore=0;
break;
case HA_EXTRA_KEYREAD:
key_read=1; // Query satisfied with key
break;
case HA_EXTRA_NO_KEYREAD:
key_read=0;
break;
case HA_EXTRA_IGNORE_DUP_KEY:
using_ignore=1;
break;
case HA_EXTRA_NO_IGNORE_DUP_KEY:
using_ignore=0;
break;
default:
break;
}
return 0;
}
int ha_gemini::reset(void)
{
key_read=0; // Reset to state after open
return 0;
}
/*
As MySQL will execute an external lock for every new table it uses
we can use this to start the transactions.
*/
int ha_gemini::external_lock(THD *thd, int lock_type)
{
dsmStatus_t rc = 0;
LONG txNumber;
DBUG_ENTER("ha_gemini::external_lock");
if (lock_type != F_UNLCK)
{
if (!thd->gemini.lock_count)
{
thd->gemini.lock_count = 1;
thd->gemini.tx_isolation = thd->tx_isolation;
}
// lockMode has already been set in store_lock
// If the statement about to be executed calls for
// exclusive locks and we're running at read uncommitted
// isolation level then raise an error.
if(thd->gemini.tx_isolation == ISO_READ_UNCOMMITTED)
{
if(lockMode == DSM_LK_EXCL)
{
DBUG_RETURN(HA_ERR_READ_ONLY_TRANSACTION);
}
else
{
lockMode = DSM_LK_NOLOCK;
}
}
if(thd->gemini.context == NULL)
{
/* Need to get this thread a connection into the database */
rc = gemini_connect(thd);
if(rc)
return rc;
}
/* Set need savepoint flag */
thd->gemini.needSavepoint = 1;
if(rc)
DBUG_RETURN(rc);
if( thd->in_lock_tables || thd->gemini.tx_isolation == ISO_SERIALIZABLE )
{
rc = dsmObjectLock((dsmContext_t *)thd->gemini.context,
(dsmObject_t)tableNumber,DSMOBJECT_TABLE,0,
lockMode, 1, 0);
if(rc == DSM_S_RQSTREJ)
rc = HA_ERR_LOCK_WAIT_TIMEOUT;
}
}
else /* lock_type == F_UNLK */
{
/* Commit the tx if we're in auto-commit mode */
if (!(thd->options & OPTION_NOT_AUTO_COMMIT)&&
!(thd->options & OPTION_BEGIN))
gemini_commit(thd);
}
DBUG_RETURN(rc);
}
THR_LOCK_DATA **ha_gemini::store_lock(THD *thd, THR_LOCK_DATA **to,
enum thr_lock_type lock_type)
{
if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK)
{
/* If we are not doing a LOCK TABLE, then allow multiple writers */
if ((lock_type >= TL_WRITE_CONCURRENT_INSERT &&
lock_type <= TL_WRITE) &&
!thd->in_lock_tables)
lock_type = TL_WRITE_ALLOW_WRITE;
lock.type=lock_type;
}
if(table->reginfo.lock_type > TL_WRITE_ALLOW_READ)
lockMode = DSM_LK_EXCL;
else
lockMode = DSM_LK_SHARE;
*to++= &lock;
return to;
}
void ha_gemini::update_create_info(HA_CREATE_INFO *create_info)
{
table->file->info(HA_STATUS_AUTO | HA_STATUS_CONST);
if (!(create_info->used_fields & HA_CREATE_USED_AUTO))
{
create_info->auto_increment_value=auto_increment_value;
}
}
int ha_gemini::create(const char *name, register TABLE *form,
HA_CREATE_INFO *create_info)
{
THD *thd;
char name_buff[FN_REFLEN];
char dbname_buff[FN_REFLEN];
DBUG_ENTER("ha_gemini::create");
dsmContext_t *pcontext;
dsmStatus_t rc;
dsmArea_t areaNumber;
dsmObject_t tableNumber = 0;
dsmDbkey_t dummy = 0;
unsigned i;
int baseNameLen;
dsmObject_t indexNumber;
/* separate out the name of the table and the database (a VST must be
** created in the mysql database)
*/
rc = gemini_parse_table_name(name, dbname_buff, name_buff);
if (rc == 0)
{
/* If the table is a VST, don't create areas or extents */
if (strcmp(dbname_buff, "mysql") == 0)
{
tableNumber = gemini_is_vst(name_buff);
if (tableNumber)
{
return 0;
}
}
}
thd = current_thd;
if(thd->gemini.context == NULL)
{
/* Need to get this thread a connection into the database */
rc = gemini_connect(thd);
if(rc)
return rc;
}
pcontext = (dsmContext_t *)thd->gemini.context;
if(thd->gemini.needSavepoint || using_ignore)
{
thd->gemini.savepoint++;
rc = dsmTransaction((dsmContext_t *)thd->gemini.context,
&thd->gemini.savepoint,
DSMTXN_SAVE, 0, 0);
if (rc)
DBUG_RETURN(rc);
thd->gemini.needSavepoint = 0;
}
fn_format(name_buff, name, "", ha_gemini_ext, 2 | 4);
/* Create a storage area */
rc = dsmAreaNew(pcontext,gemini_blocksize,DSMAREA_TYPE_DATA,
&areaNumber, gemini_recbits,
(dsmText_t *)"gemini_data_area");
if( rc != 0 )
{
gemini_msg(pcontext, "dsmAreaNew failed %l",rc);
return(rc);
}
/* Create an extent */
/* Don't pass in leading ./ in name_buff */
rc = dsmExtentCreate(pcontext,areaNumber,1,15,5,
(dsmText_t *)&name_buff[start_of_name]);
if( rc != 0 )
{
gemini_msg(pcontext, "dsmExtentCreate failed %l",rc);
return(rc);
}
/* Create the table storage object */
/* Change slashes in the name to periods */
for( i = 0; i < strlen(name_buff); i++)
if(name_buff[i] == '/' || name_buff[i] == '\\')
name_buff[i] = '.';
/* Get rid of .gmd suffix */
name_buff[strlen(name_buff) - 4] = '\0';
rc = dsmObjectCreate(pcontext, areaNumber, &tableNumber,
DSMOBJECT_MIXTABLE,0,0,0,
(dsmText_t *)&name_buff[start_of_name],
&dummy,&dummy);
if (rc == 0 && table->blob_fields)
{
/* create a storage object record for blob fields */
rc = dsmObjectCreate(pcontext, areaNumber, &tableNumber,
DSMOBJECT_BLOB,0,0,0,
(dsmText_t *)&name_buff[start_of_name],
&dummy,&dummy);
if( rc != 0 )
{
gemini_msg(pcontext, "dsmObjectCreate for blob object failed %l",rc);
return(rc);
}
}
if(rc == 0 && form->keys)
{
fn_format(name_buff, name, "", ha_gemini_idx_ext, 2 | 4);
/* Create a storage area */
rc = dsmAreaNew(pcontext,gemini_blocksize,DSMAREA_TYPE_DATA,
&areaNumber, gemini_recbits,
(dsmText_t *)"gemini_index_area");
if( rc != 0 )
{
gemini_msg(pcontext, "dsmAreaNew failed %l",rc);
return(rc);
}
/* Create an extent */
/* Don't pass in leading ./ in name_buff */
rc = dsmExtentCreate(pcontext,areaNumber,1,15,5,
(dsmText_t *)&name_buff[start_of_name]);
if( rc != 0 )
{
gemini_msg(pcontext, "dsmExtentCreate failed %l",rc);
return(rc);
}
/* Change slashes in the name to periods */
for( i = 0; i < strlen(name_buff); i++)
if(name_buff[i] == '/' || name_buff[i] == '\\')
name_buff[i] = '.';
/* Get rid of .gmi suffix */
name_buff[strlen(name_buff) - 4] = '\0';
baseNameLen = strlen(name_buff);
name_buff[baseNameLen] = '.';
baseNameLen++;
for( i = 0; i < form->keys; i++)
{
dsmObjectAttr_t indexUnique;
indexNumber = DSMINDEX_INVALID;
/* Create a storage object record for each index */
/* Add the index name so the object name is in the form
<db>.<table>.<index_name> */
strcpy(&name_buff[baseNameLen],table->key_info[i].name);
if(table->key_info[i].flags & HA_NOSAME)
indexUnique = 1;
else
indexUnique = 0;
rc = dsmObjectCreate(pcontext, areaNumber, &indexNumber,
DSMOBJECT_MIXINDEX,indexUnique,tableNumber,
DSMOBJECT_MIXTABLE,
(dsmText_t *)&name_buff[start_of_name],
&dummy,&dummy);
}
}
/* The auto_increment value is the next one to be given
out so give dsm one less than this value */
if(create_info->auto_increment_value)
rc = dsmTableAutoIncrementSet(pcontext,tableNumber,
create_info->auto_increment_value-1);
/* Get a table lock on this table in case this table is being
created as part of an alter table statement. We don't want
the alter table statement to abort because of a lock table overflow
*/
if (thd->lex.sql_command == SQLCOM_CREATE_INDEX ||
thd->lex.sql_command == SQLCOM_ALTER_TABLE ||
thd->lex.sql_command == SQLCOM_DROP_INDEX)
{
rc = dsmObjectLock(pcontext,
(dsmObject_t)tableNumber,DSMOBJECT_TABLE,0,
DSM_LK_EXCL, 1, 0);
/* and don't commit so we won't release the table on the table number
of the table being altered */
}
else
{
if(!rc)
rc = gemini_commit(thd);
}
DBUG_RETURN(rc);
}
int ha_gemini::delete_table(const char *pname)
{
THD *thd;
dsmStatus_t rc;
dsmContext_t *pcontext;
unsigned i,nameLen;
dsmArea_t indexArea = 0;
dsmArea_t tableArea = 0;
dsmObjectAttr_t objectAttr;
dsmObject_t associate;
dsmObjectType_t associateType;
dsmDbkey_t block, root;
int need_txn = 0;
dsmObject_t tableNum = 0;
char name_buff[FN_REFLEN];
char dbname_buff[FN_REFLEN];
DBUG_ENTER("ha_gemini::delete_table");
/* separate out the name of the table and the database (a VST must be
** located in the mysql database)
*/
rc = gemini_parse_table_name(pname, dbname_buff, name_buff);
if (rc == 0)
{
/* If the table is a VST, there are no areas or extents to delete */
if (strcmp(dbname_buff, "mysql") == 0)
{
tableNum = gemini_is_vst(name_buff);
if (tableNum)
{
return 0;
}
}
}
thd = current_thd;
if(thd->gemini.context == NULL)
{
/* Need to get this thread a connection into the database */
rc = gemini_connect(thd);
if(rc)
{
DBUG_RETURN(rc);
}
}
pcontext = (dsmContext_t *)thd->gemini.context;
bzero(name_buff, FN_REFLEN);
nameLen = strlen(pname);
for( i = start_of_name; i < nameLen; i++)
{
if(pname[i] == '/' || pname[i] == '\\')
name_buff[i-start_of_name] = '.';
else
name_buff[i-start_of_name] = pname[i];
}
rc = dsmObjectNameToNum(pcontext, (dsmText_t *)name_buff,
(dsmObject_t *)&tableNum);
if (rc)
{
gemini_msg(pcontext, "Unable to find table number for %s", name_buff);
rc = gemini_rollback(thd);
if (rc)
{
gemini_msg(pcontext, "Error in rollback %l",rc);
}
DBUG_RETURN(rc);
}
rc = dsmObjectInfo(pcontext, tableNum, DSMOBJECT_MIXTABLE, tableNum,
&tableArea, &objectAttr, &associateType, &block, &root);
if (rc)
{
gemini_msg(pcontext, "Failed to get area number for table %d, %s, return %l",
tableNum, pname, rc);
rc = gemini_rollback(thd);
if (rc)
{
gemini_msg(pcontext, "Error in rollback %l",rc);
}
}
indexArea = DSMAREA_INVALID;
/* Delete the indexes and tables storage objects for with the table */
rc = dsmObjectDeleteAssociate(pcontext, tableNum, &indexArea);
if (rc)
{
gemini_msg(pcontext, "Error deleting storage objects for table number %d, return %l",
(int)tableNum, rc);
/* roll back txn and return */
rc = gemini_rollback(thd);
if (rc)
{
gemini_msg(pcontext, "Error in rollback %l",rc);
}
DBUG_RETURN(rc);
}
if (indexArea != DSMAREA_INVALID)
{
/* Delete the extents for both Index and Table */
rc = dsmExtentDelete(pcontext, indexArea);
rc = dsmAreaDelete(pcontext, indexArea);
if (rc)
{
gemini_msg(pcontext, "Error deleting Index Area %l, return %l", indexArea, rc);
/* roll back txn and return */
rc = gemini_rollback(thd);
if (rc)
{
gemini_msg(pcontext, "Error in rollback %l",rc);
}
DBUG_RETURN(rc);
}
}
rc = dsmExtentDelete(pcontext, tableArea);
rc = dsmAreaDelete(pcontext, tableArea);
if (rc)
{
gemini_msg(pcontext, "Error deleting table Area %l, name %s, return %l",
tableArea, pname, rc);
/* roll back txn and return */
rc = gemini_rollback(thd);
if (rc)
{
gemini_msg(pcontext, "Error in rollback %l",rc);
}
DBUG_RETURN(rc);
}
/* Commit the transaction */
rc = gemini_commit(thd);
if (rc)
{
gemini_msg(pcontext, "Failed to commit transaction %l",rc);
}
/* now remove all the files that need to be removed and
cause a checkpoint so recovery will work */
rc = dsmExtentUnlink(pcontext);
DBUG_RETURN(0);
}
int ha_gemini::rename_table(const char *pfrom, const char *pto)
{
THD *thd;
dsmContext_t *pcontext;
dsmStatus_t rc;
char dbname_buff[FN_REFLEN];
char name_buff[FN_REFLEN];
char newname_buff[FN_REFLEN];
char newextname_buff[FN_REFLEN];
char newidxextname_buff[FN_REFLEN];
unsigned i, nameLen;
dsmObject_t tableNum;
dsmArea_t indexArea = 0;
dsmArea_t tableArea = 0;
DBUG_ENTER("ha_gemini::rename_table");
/* don't allow rename of VSTs */
rc = gemini_parse_table_name(pfrom, dbname_buff, name_buff);
if (rc == 0)
{
/* If the table is a VST, don't create areas or extents */
if (strcmp(dbname_buff, "mysql") == 0)
{
if (gemini_is_vst(name_buff))
{
return DSM_S_CANT_RENAME_VST;
}
}
}
thd = current_thd;
if (thd->gemini.context == NULL)
{
/* Need to get this thread a connection into the database */
rc = gemini_connect(thd);
if (rc)
{
DBUG_RETURN(rc);
}
}
pcontext = (dsmContext_t *)thd->gemini.context;
/* change the slashes to dots in the old and new names */
nameLen = strlen(pfrom);
for( i = start_of_name; i < nameLen; i++)
{
if(pfrom[i] == '/' || pfrom[i] == '\\')
name_buff[i-start_of_name] = '.';
else
name_buff[i-start_of_name] = pfrom[i];
}
name_buff[i-start_of_name] = '\0';
nameLen = strlen(pto);
for( i = start_of_name; i < nameLen; i++)
{
if(pto[i] == '/' || pto[i] == '\\')
newname_buff[i-start_of_name] = '.';
else
newname_buff[i-start_of_name] = pto[i];
}
newname_buff[i-start_of_name] = '\0';
/* generate new extent names (for table and index extents) */
fn_format(newextname_buff, pto, "", ha_gemini_ext, 2 | 4);
fn_format(newidxextname_buff, pto, "", ha_gemini_idx_ext, 2 | 4);
rc = dsmObjectNameToNum(pcontext, (dsmText_t *)name_buff, &tableNum);
if (rc)
{
gemini_msg(pcontext, "Unable to file Table number for %s", name_buff);
goto errorReturn;
}
rc = dsmObjectRename(pcontext, tableNum,
(dsmText_t *)newname_buff,
(dsmText_t *)&newidxextname_buff[start_of_name],
(dsmText_t *)&newextname_buff[start_of_name],
&indexArea, &tableArea);
if (rc)
{
gemini_msg(pcontext, "Failed to rename %s to %s",name_buff,newname_buff);
goto errorReturn;
}
/* Rename the physical table and index files (if necessary).
** Close the file, rename it, and reopen it (have to do it this
** way so rename works on Windows).
*/
if (!(rc = dsmAreaClose(pcontext, tableArea)))
{
if (!(rc = rename_file_ext(pfrom, pto, ha_gemini_ext)))
{
rc = dsmAreaOpen(pcontext, tableArea, 0);
if (rc)
{
gemini_msg(pcontext, "Failed to reopen area %d",tableArea);
}
}
}
if (!rc && indexArea)
{
if (!(rc = dsmAreaClose(pcontext, indexArea)))
{
if (!(rc = rename_file_ext(pfrom, pto, ha_gemini_idx_ext)))
{
rc = dsmAreaOpen(pcontext, indexArea, 0);
if (rc)
{
gemini_msg(pcontext, "Failed to reopen area %d",tableArea);
}
}
}
}
errorReturn:
DBUG_RETURN(rc);
}
/*
How many seeks it will take to read through the table
This is to be comparable to the number returned by records_in_range so
that we can decide if we should scan the table or use keys.
*/
double ha_gemini::scan_time()
{
return (double)records /
(double)((gemini_blocksize / (double)table->reclength));
}
int ha_gemini::analyze(THD* thd, HA_CHECK_OPT* check_opt)
{
int error;
uint saveIsolation;
dsmMask_t saveLockMode;
check_opt->quick = true;
check_opt->optimize = true; // Tells check not to get table lock
saveLockMode = lockMode;
saveIsolation = thd->gemini.tx_isolation;
thd->gemini.tx_isolation = ISO_READ_UNCOMMITTED;
lockMode = DSM_LK_NOLOCK;
error = check(thd,check_opt);
lockMode = saveLockMode;
thd->gemini.tx_isolation = saveIsolation;
return (error);
}
int ha_gemini::check(THD* thd, HA_CHECK_OPT* check_opt)
{
int error = 0;
int checkStatus = HA_ADMIN_OK;
ha_rows indexCount;
byte *buf = 0, *indexBuf = 0, *prevBuf = 0;
int errorCount = 0;
info(HA_STATUS_VARIABLE); // Makes sure row count is up to date
/* Get a shared table lock */
if(thd->gemini.needSavepoint)
{
/* We don't really need a savepoint here but do it anyway
just to keep the savepoint number correct. */
thd->gemini.savepoint++;
error = dsmTransaction((dsmContext_t *)thd->gemini.context,
&thd->gemini.savepoint,
DSMTXN_SAVE, 0, 0);
if (error)
return(error);
thd->gemini.needSavepoint = 0;
}
buf = (byte*)my_malloc(table->rec_buff_length,MYF(MY_WME));
indexBuf = (byte*)my_malloc(table->rec_buff_length,MYF(MY_WME));
prevBuf = (byte*)my_malloc(table->rec_buff_length,MYF(MY_WME |MY_ZEROFILL ));
/* Lock the table */
if (!check_opt->optimize)
error = dsmObjectLock((dsmContext_t *)thd->gemini.context,
(dsmObject_t)tableNumber,
DSMOBJECT_TABLE,0,
DSM_LK_SHARE, 1, 0);
if(error)
{
gemini_msg((dsmContext_t *)thd->gemini.context,
"Failed to lock table %d, error %d",tableNumber, error);
return error;
}
ha_rows *rec_per_key = share->rec_per_key;
/* If quick option just scan along index converting and counting entries */
for (uint i = 0; i < table->keys; i++)
{
key_read = 1; // Causes data to be extracted from the keys
indexCount = 0;
// Clear the cardinality stats for this index
memset(table->key_info[i].rec_per_key,0,
sizeof(table->key_info[0].rec_per_key[0]) *
table->key_info[i].key_parts);
error = index_init(i);
error = index_first(indexBuf);
while(!error)
{
indexCount++;
if(!check_opt->quick)
{
/* Fetch row and compare to data produced from key */
error = fetch_row(thd->gemini.context,buf);
if(!error)
{
if(key_cmp(i,buf,indexBuf,false))
{
gemini_msg((dsmContext_t *)thd->gemini.context,
"Check Error! Key does not match row for rowid %d for index %s",
lastRowid,table->key_info[i].name);
print_msg(thd,table->real_name,"check","error",
"Key does not match row for rowid %d for index %s",
lastRowid,table->key_info[i].name);
checkStatus = HA_ADMIN_CORRUPT;
errorCount++;
if(errorCount > 1000)
goto error_return;
}
else if(error == DSM_S_RMNOTFND)
{
errorCount++;
checkStatus = HA_ADMIN_CORRUPT;
gemini_msg((dsmContext_t *)thd->gemini.context,
"Check Error! Key does not have a valid row pointer %d for index %s",
lastRowid,table->key_info[i].name);
print_msg(thd,table->real_name,"check","error",
"Key does not have a valid row pointer %d for index %s",
lastRowid,table->key_info[i].name);
if(errorCount > 1000)
goto error_return;
error = 0;
}
}
}
key_cmp(i,indexBuf,prevBuf,true);
bcopy((void *)indexBuf,(void *)prevBuf,table->rec_buff_length);
if(!error)
error = index_next(indexBuf);
}
for(uint j=1; j < table->key_info[i].key_parts; j++)
{
table->key_info[i].rec_per_key[j] += table->key_info[i].rec_per_key[j-1];
}
for(uint k=0; k < table->key_info[i].key_parts; k++)
{
if (table->key_info[i].rec_per_key[k])
table->key_info[i].rec_per_key[k] =
records / table->key_info[i].rec_per_key[k];
*rec_per_key = table->key_info[i].rec_per_key[k];
rec_per_key++;
}
if(error == HA_ERR_END_OF_FILE)
{
/* Check count of rows */
if(records != indexCount)
{
/* Number of index entries does not agree with the number of
rows in the index. */
checkStatus = HA_ADMIN_CORRUPT;
gemini_msg((dsmContext_t *)thd->gemini.context,
"Check Error! Total rows %d does not match total index entries %d for %s",
records, indexCount,
table->key_info[i].name);
print_msg(thd,table->real_name,"check","error",
"Total rows %d does not match total index entries %d for %s",
records, indexCount,
table->key_info[i].name);
}
}
else
{
checkStatus = HA_ADMIN_FAILED;
goto error_return;
}
index_end();
}
if(!check_opt->quick)
{
/* Now scan the table and for each row generate the keys
and find them in the index */
error = fullCheck(thd, buf);
if(error)
checkStatus = error;
}
// Store the key distribution information
error = saveKeyStats(thd);
error_return:
my_free((char*)buf,MYF(MY_ALLOW_ZERO_PTR));
my_free((char*)indexBuf,MYF(MY_ALLOW_ZERO_PTR));
my_free((char*)prevBuf,MYF(MY_ALLOW_ZERO_PTR));
index_end();
key_read = 0;
if(!check_opt->optimize)
{
error = dsmObjectUnlock((dsmContext_t *)thd->gemini.context,
(dsmObject_t)tableNumber,
DSMOBJECT_TABLE,0,
DSM_LK_SHARE,0);
if (error)
{
gemini_msg((dsmContext_t *)thd->gemini.context,
"Unable to unlock table %d", tableNumber);
}
}
return checkStatus;
}
int ha_gemini::saveKeyStats(THD *thd)
{
dsmStatus_t rc = 0;
/* Insert a row in the indexStats table for each column of
each index of the table */
for(uint i = 0; i < table->keys; i++)
{
for (uint j = 0; j < table->key_info[i].key_parts && !rc ;j++)
{
rc = dsmIndexStatsPut((dsmContext_t *)thd->gemini.context,
tableNumber, pindexNumbers[i],
j, (LONG64)table->key_info[i].rec_per_key[j]);
if (rc)
{
gemini_msg((dsmContext_t *)thd->gemini.context,
"Failed to update index stats for table %d, index %d",
tableNumber, pindexNumbers[i]);
}
}
}
return rc;
}
int ha_gemini::fullCheck(THD *thd,byte *buf)
{
int error;
int errorCount = 0;
int checkStatus = 0;
lastRowid = 0;
while(((error = rnd_next( buf)) != HA_ERR_END_OF_FILE) && errorCount <= 1000)
{
if(!error)
{
error = handleIndexEntries(buf,lastRowid,KEY_CHECK);
if(error)
{
/* Error finding an index entry for a row. */
print_msg(thd,table->real_name,"check","error",
"Unable to find all index entries for row %d",
lastRowid);
errorCount++;
checkStatus = HA_ADMIN_CORRUPT;
error = 0;
}
}
else
{
/* Error reading a row */
print_msg(thd,table->real_name,"check","error",
"Error reading row %d status = %d",
lastRowid,error);
errorCount++;
checkStatus = HA_ADMIN_CORRUPT;
error = 0;
}
}
return checkStatus;
}
int ha_gemini::repair(THD* thd, HA_CHECK_OPT* check_opt)
{
int error;
dsmRecord_t dsmRecord;
byte *buf;
if(thd->gemini.needSavepoint)
{
/* We don't really need a savepoint here but do it anyway
just to keep the savepoint number correct. */
thd->gemini.savepoint++;
error = dsmTransaction((dsmContext_t *)thd->gemini.context,
&thd->gemini.savepoint,
DSMTXN_SAVE, 0, 0);
if (error)
{
gemini_msg((dsmContext_t *)thd->gemini.context,
"Error setting savepoint number %d, error %d",
thd->gemini.savepoint++, error);
return(error);
}
thd->gemini.needSavepoint = 0;
}
/* Lock the table */
error = dsmObjectLock((dsmContext_t *)thd->gemini.context,
(dsmObject_t)tableNumber,
DSMOBJECT_TABLE,0,
DSM_LK_EXCL, 1, 0);
if(error)
{
gemini_msg((dsmContext_t *)thd->gemini.context,
"Failed to lock table %d, error %d",tableNumber, error);
return error;
}
error = dsmContextSetLong((dsmContext_t *)thd->gemini.context,
DSM_TAGCONTEXT_NO_LOGGING,1);
error = dsmTableReset((dsmContext_t *)thd->gemini.context,
(dsmTable_t)tableNumber, table->keys,
pindexNumbers);
if (error)
{
gemini_msg((dsmContext_t *)thd->gemini.context,
"dsmTableReset failed for table %d, error %d",tableNumber, error);
}
buf = (byte*)my_malloc(table->rec_buff_length,MYF(MY_WME));
dsmRecord.table = tableNumber;
dsmRecord.recid = 0;
dsmRecord.pbuffer = (dsmBuffer_t *)rec_buff;
dsmRecord.recLength = table->reclength;
dsmRecord.maxLength = table->rec_buff_length;
while(!error)
{
error = dsmTableScan((dsmContext_t *)thd->gemini.context,
&dsmRecord, DSMFINDNEXT, DSM_LK_NOLOCK,
1);
if(!error)
{
if (!(error = unpack_row((char *)buf,(char *)dsmRecord.pbuffer)))
{
error = handleIndexEntries(buf,dsmRecord.recid,KEY_CREATE);
if(error == HA_ERR_FOUND_DUPP_KEY)
{
/* We don't want to stop on duplicate keys -- we're repairing
here so let's get as much repaired as possible. */
error = 0;
}
}
}
}
error = dsmObjectUnlock((dsmContext_t *)thd->gemini.context,
(dsmObject_t)tableNumber,
DSMOBJECT_TABLE,0,
DSM_LK_EXCL,0);
if (error)
{
gemini_msg((dsmContext_t *)thd->gemini.context,
"Unable to unlock table %d", tableNumber);
}
my_free((char*)buf,MYF(MY_ALLOW_ZERO_PTR));
error = dsmContextSetLong((dsmContext_t *)thd->gemini.context,
DSM_TAGCONTEXT_NO_LOGGING,0);
return error;
}
int ha_gemini::restore(THD* thd, HA_CHECK_OPT *check_opt)
{
dsmContext_t *pcontext = (dsmContext_t *)thd->gemini.context;
char* backup_dir = thd->lex.backup_dir;
char src_path[FN_REFLEN], dst_path[FN_REFLEN];
char* table_name = table->real_name;
int error = 0;
int errornum;
const char* errmsg = "";
dsmArea_t tableArea = 0;
dsmObjectAttr_t objectAttr;
dsmObject_t associate;
dsmObjectType_t associateType;
dsmDbkey_t block, root;
dsmStatus_t rc;
rc = dsmObjectInfo(pcontext, tableNumber, DSMOBJECT_MIXTABLE, tableNumber,
&tableArea, &objectAttr, &associateType, &block, &root);
if (rc)
{
error = HA_ADMIN_FAILED;
errmsg = "Failed in dsmObjectInfo (.gmd) (Error %d)";
errornum = rc;
gemini_msg(pcontext, errmsg ,errornum);
goto err;
}
rc = dsmAreaFlush(pcontext, tableArea, FLUSH_BUFFERS | FLUSH_SYNC);
if (rc)
{
error = HA_ADMIN_FAILED;
errmsg = "Failed in dsmAreaFlush (.gmd) (Error %d)";
errornum = rc;
gemini_msg(pcontext, errmsg ,errornum);
goto err;
}
rc = dsmAreaClose(pcontext, tableArea);
if (rc)
{
error = HA_ADMIN_FAILED;
errmsg = "Failed in dsmAreaClose (.gmd) (Error %d)";
errornum = rc;
gemini_msg(pcontext, errmsg ,errornum);
goto err;
}
/* Restore the data file */
if (!fn_format(src_path, table_name, backup_dir, ha_gemini_ext, 4 + 64))
{
return HA_ADMIN_INVALID;
}
if (my_copy(src_path, fn_format(dst_path, table->path, "",
ha_gemini_ext, 4), MYF(MY_WME)))
{
error = HA_ADMIN_FAILED;
errmsg = "Failed in my_copy (.gmd) (Error %d)";
errornum = errno;
gemini_msg(pcontext, errmsg ,errornum);
goto err;
}
rc = dsmAreaFlush(pcontext, tableArea, FREE_BUFFERS);
if (rc)
{
error = HA_ADMIN_FAILED;
errmsg = "Failed in dsmAreaFlush (.gmd) (Error %d)";
errornum = rc;
gemini_msg(pcontext, errmsg ,errornum);
goto err;
}
rc = dsmAreaOpen(pcontext, tableArea, 1);
if (rc)
{
error = HA_ADMIN_FAILED;
errmsg = "Failed in dsmAreaOpen (.gmd) (Error %d)";
errornum = rc;
gemini_msg(pcontext, errmsg ,errornum);
goto err;
}
#ifdef GEMINI_BACKUP_IDX
dsmArea_t indexArea = 0;
rc = dsmObjectInfo(pcontext, tableNumber, DSMOBJECT_MIXINDEX, &indexArea,
&objectAttr, &associate, &associateType, &block, &root);
if (rc)
{
error = HA_ADMIN_FAILED;
errmsg = "Failed in dsmObjectInfo (.gmi) (Error %d)";
errornum = rc;
gemini_msg(pcontext, errmsg ,errornum);
goto err;
}
rc = dsmAreaClose(pcontext, indexArea);
if (rc)
{
error = HA_ADMIN_FAILED;
errmsg = "Failed in dsmAreaClose (.gmi) (Error %d)";
errornum = rc;
gemini_msg(pcontext, errmsg ,errornum);
goto err;
}
/* Restore the index file */
if (!fn_format(src_path, table_name, backup_dir, ha_gemini_idx_ext, 4 + 64))
{
return HA_ADMIN_INVALID;
}
if (my_copy(src_path, fn_format(dst_path, table->path, "",
ha_gemini_idx_ext, 4), MYF(MY_WME)))
{
error = HA_ADMIN_FAILED;
errmsg = "Failed in my_copy (.gmi) (Error %d)";
errornum = errno;
gemini_msg(pcontext, errmsg ,errornum);
goto err;
}
rc = dsmAreaOpen(pcontext, indexArea, 1);
if (rc)
{
error = HA_ADMIN_FAILED;
errmsg = "Failed in dsmAreaOpen (.gmi) (Error %d)";
errornum = rc;
gemini_msg(pcontext, errmsg ,errornum);
goto err;
}
return HA_ADMIN_OK;
#else /* #ifdef GEMINI_BACKUP_IDX */
HA_CHECK_OPT tmp_check_opt;
tmp_check_opt.init();
/* The following aren't currently implemented in ha_gemini::repair
** tmp_check_opt.quick = 1;
** tmp_check_opt.flags |= T_VERY_SILENT;
*/
return (repair(thd, &tmp_check_opt));
#endif /* #ifdef GEMINI_BACKUP_IDX */
err:
{
#if 0
/* mi_check_print_error is in ha_myisam.cc, so none of the informative
** error messages above is currently being printed
*/
MI_CHECK param;
myisamchk_init(&param);
param.thd = thd;
param.op_name = (char*)"restore";
param.table_name = table->table_name;
param.testflag = 0;
mi_check_print_error(&param,errmsg, errornum);
#endif
return error;
}
}
int ha_gemini::backup(THD* thd, HA_CHECK_OPT *check_opt)
{
dsmContext_t *pcontext = (dsmContext_t *)thd->gemini.context;
char* backup_dir = thd->lex.backup_dir;
char src_path[FN_REFLEN], dst_path[FN_REFLEN];
char* table_name = table->real_name;
int error = 0;
int errornum;
const char* errmsg = "";
dsmArea_t tableArea = 0;
dsmObjectAttr_t objectAttr;
dsmObject_t associate;
dsmObjectType_t associateType;
dsmDbkey_t block, root;
dsmStatus_t rc;
rc = dsmObjectInfo(pcontext, tableNumber, DSMOBJECT_MIXTABLE, tableNumber,
&tableArea, &objectAttr, &associateType, &block, &root);
if (rc)
{
error = HA_ADMIN_FAILED;
errmsg = "Failed in dsmObjectInfo (.gmd) (Error %d)";
errornum = rc;
goto err;
}
/* Flush the buffers before backing up the table */
dsmAreaFlush((dsmContext_t *)thd->gemini.context, tableArea,
FLUSH_BUFFERS | FLUSH_SYNC);
if (rc)
{
error = HA_ADMIN_FAILED;
errmsg = "Failed in dsmAreaFlush (.gmd) (Error %d)";
errornum = rc;
gemini_msg(pcontext, errmsg ,errornum);
goto err;
}
/* Backup the .FRM file */
if (!fn_format(dst_path, table_name, backup_dir, reg_ext, 4 + 64))
{
errmsg = "Failed in fn_format() for .frm file: errno = %d";
error = HA_ADMIN_INVALID;
errornum = errno;
gemini_msg(pcontext, errmsg ,errornum);
goto err;
}
if (my_copy(fn_format(src_path, table->path,"", reg_ext, 4),
dst_path,
MYF(MY_WME | MY_HOLD_ORIGINAL_MODES )))
{
error = HA_ADMIN_FAILED;
errmsg = "Failed copying .frm file: errno = %d";
errornum = errno;
gemini_msg(pcontext, errmsg ,errornum);
goto err;
}
/* Backup the data file */
if (!fn_format(dst_path, table_name, backup_dir, ha_gemini_ext, 4 + 64))
{
errmsg = "Failed in fn_format() for .GMD file: errno = %d";
error = HA_ADMIN_INVALID;
errornum = errno;
gemini_msg(pcontext, errmsg ,errornum);
goto err;
}
if (my_copy(fn_format(src_path, table->path,"", ha_gemini_ext, 4),
dst_path,
MYF(MY_WME | MY_HOLD_ORIGINAL_MODES )) )
{
errmsg = "Failed copying .GMD file: errno = %d";
error= HA_ADMIN_FAILED;
errornum = errno;
gemini_msg(pcontext, errmsg ,errornum);
goto err;
}
#ifdef GEMINI_BACKUP_IDX
dsmArea_t indexArea = 0;
rc = dsmObjectInfo(pcontext, tableNumber, DSMOBJECT_MIXINDEX, &indexArea,
&objectAttr, &associate, &associateType, &block, &root);
if (rc)
{
error = HA_ADMIN_FAILED;
errmsg = "Failed in dsmObjectInfo (.gmi) (Error %d)";
errornum = rc;
gemini_msg(pcontext, errmsg ,errornum);
goto err;
}
/* Backup the index file */
if (!fn_format(dst_path, table_name, backup_dir, ha_gemini_idx_ext, 4 + 64))
{
errmsg = "Failed in fn_format() for .GMI file: errno = %d";
error = HA_ADMIN_INVALID;
errornum = errno;
gemini_msg(pcontext, errmsg ,errornum);
goto err;
}
if (my_copy(fn_format(src_path, table->path,"", ha_gemini_idx_ext, 4),
dst_path,
MYF(MY_WME | MY_HOLD_ORIGINAL_MODES )) )
{
errmsg = "Failed copying .GMI file: errno = %d";
error= HA_ADMIN_FAILED;
errornum = errno;
gemini_msg(pcontext, errmsg ,errornum);
goto err;
}
#endif /* #ifdef GEMINI_BACKUP_IDX */
return HA_ADMIN_OK;
err:
{
#if 0
/* mi_check_print_error is in ha_myisam.cc, so none of the informative
** error messages above is currently being printed
*/
MI_CHECK param;
myisamchk_init(&param);
param.thd = thd;
param.op_name = (char*)"backup";
param.table_name = table->table_name;
param.testflag = 0;
mi_check_print_error(&param,errmsg, errornum);
#endif
return error;
}
}
int ha_gemini::optimize(THD* thd, HA_CHECK_OPT *check_opt)
{
return HA_ADMIN_ALREADY_DONE;
}
ha_rows ha_gemini::records_in_range(int keynr,
const byte *start_key,uint start_key_len,
enum ha_rkey_function start_search_flag,
const byte *end_key,uint end_key_len,
enum ha_rkey_function end_search_flag)
{
int error;
int componentLen;
float pctInrange;
ha_rows rows = 5;
DBUG_ENTER("records_in_range");
error = index_init(keynr);
if(error)
DBUG_RETURN(rows);
pbracketBase->index = (short)pindexNumbers[keynr];
pbracketBase->keycomps = 1;
if(start_key)
{
error = pack_key(keynr, pbracketBase, start_key, start_key_len);
if(start_search_flag == HA_READ_AFTER_KEY)
{
/* A greater than operation */
error = gemKeyAddLow(pbracketBase->keystr + pbracketBase->keyLen,
&componentLen);
pbracketBase->keyLen += componentLen;
}
}
else
{
error = gemKeyLow(pbracketBase->keystr, &componentLen,
pbracketBase->index);
pbracketBase->keyLen = componentLen;
}
pbracketBase->keyLen -= FULLKEYHDRSZ;
if(end_key)
{
error = pack_key(keynr, pbracketLimit, end_key, end_key_len);
if(!error && end_search_flag == HA_READ_AFTER_KEY)
{
error = gemKeyAddHigh(pbracketLimit->keystr + pbracketLimit->keyLen,
&componentLen);
pbracketLimit->keyLen += componentLen;
}
}
else
{
error = gemKeyHigh(pbracketLimit->keystr,&componentLen,
pbracketLimit->index);
pbracketLimit->keyLen = componentLen;
}
pbracketLimit->keyLen -= FULLKEYHDRSZ;
error = dsmIndexRowsInRange((dsmContext_t *)current_thd->gemini.context,
pbracketBase,pbracketLimit,
tableNumber,
&pctInrange);
if(pctInrange >= 1)
rows = (ha_rows)pctInrange;
else
{
rows = (ha_rows)(records * pctInrange);
if(!rows && pctInrange > 0)
rows = 1;
}
index_end();
DBUG_RETURN(rows);
}
/*
Pack a row for storage. If the row is of fixed length, just store the
row 'as is'.
If not, we will generate a packed row suitable for storage.
This will only fail if we don't have enough memory to pack the row, which;
may only happen in rows with blobs, as the default row length is
pre-allocated.
*/
int ha_gemini::pack_row(byte **pprow, int *ppackedLength, const byte *record,
bool update)
{
THD *thd = current_thd;
dsmContext_t *pcontext = (dsmContext_t *)thd->gemini.context;
gemBlobDesc_t *pBlobDesc = pBlobDescs;
if (fixed_length_row)
{
*pprow = (byte *)record;
*ppackedLength=(int)table->reclength;
return 0;
}
/* Copy null bits */
memcpy(rec_buff, record, table->null_bytes);
byte *ptr=rec_buff + table->null_bytes;
for (Field **field=table->field ; *field ; field++)
{
#ifdef GEMINI_TINYBLOB_IN_ROW
/* Tiny blobs (255 bytes or less) are stored in the row; larger
** blobs are stored in a separate storage object (see ha_gemini::create).
*/
if ((*field)->type() == FIELD_TYPE_BLOB &&
((Field_blob*)*field)->blobtype() != FIELD_TYPE_TINY_BLOB)
#else
if ((*field)->type() == FIELD_TYPE_BLOB)
#endif
{
dsmBlob_t gemBlob;
char *blobptr;
gemBlob.areaType = DSMOBJECT_BLOB;
gemBlob.blobObjNo = tableNumber;
gemBlob.blobId = 0;
gemBlob.totLength = gemBlob.segLength =
((Field_blob*)*field)->get_length((char*)record + (*field)->offset());
((Field_blob*)*field)->get_ptr((char**) &blobptr);
gemBlob.pBuffer = (dsmBuffer_t *)blobptr;
gemBlob.blobContext.blobOffset = 0;
if (gemBlob.totLength)
{
dsmBlobStart(pcontext, &gemBlob);
if (update && pBlobDesc->blobId)
{
gemBlob.blobId = pBlobDesc->blobId;
dsmBlobUpdate(pcontext, &gemBlob, NULL);
}
else
{
dsmBlobPut(pcontext, &gemBlob, NULL);
}
dsmBlobEnd(pcontext, &gemBlob);
}
ptr = (byte*)((Field_blob*)*field)->pack_id((char*) ptr,
(char*)record + (*field)->offset(), (longlong)gemBlob.blobId);
pBlobDesc++;
}
else
{
ptr=(byte*) (*field)->pack((char*) ptr, (char*)record + (*field)->offset());
}
}
*pprow=rec_buff;
*ppackedLength= (ptr - rec_buff);
return 0;
}
int ha_gemini::unpack_row(char *record, char *prow)
{
THD *thd = current_thd;
dsmContext_t *pcontext = (dsmContext_t *)thd->gemini.context;
gemBlobDesc_t *pBlobDesc = pBlobDescs;
if (fixed_length_row)
{
/* If the table is a VST, the row is in Gemini internal format.
** Convert the fields to MySQL format.
*/
if (RM_IS_VST(tableNumber))
{
int i = 2; /* VST fields are numbered sequentially starting at 2 */
long longValue;
char *fld;
unsigned long unknown;
for (Field **field = table->field; *field; field++, i++)
{
switch ((*field)->type())
{
case FIELD_TYPE_LONG:
case FIELD_TYPE_TINY:
case FIELD_TYPE_SHORT:
case FIELD_TYPE_TIMESTAMP:
case FIELD_TYPE_LONGLONG:
case FIELD_TYPE_INT24:
case FIELD_TYPE_DATE:
case FIELD_TYPE_TIME:
case FIELD_TYPE_DATETIME:
case FIELD_TYPE_YEAR:
case FIELD_TYPE_NEWDATE:
case FIELD_TYPE_ENUM:
case FIELD_TYPE_SET:
recGetLONG((dsmText_t *)prow, i, 0, &longValue, &unknown);
if (unknown)
{
(*field)->set_null();
}
else
{
(*field)->set_notnull();
(*field)->store((longlong)longValue);
}
break;
case FIELD_TYPE_DECIMAL:
case FIELD_TYPE_DOUBLE:
case FIELD_TYPE_TINY_BLOB:
case FIELD_TYPE_MEDIUM_BLOB:
case FIELD_TYPE_LONG_BLOB:
case FIELD_TYPE_BLOB:
case FIELD_TYPE_VAR_STRING:
break;
case FIELD_TYPE_STRING:
svcByteString_t stringFld;
fld = (char *)my_malloc((*field)->field_length, MYF(MY_WME));
stringFld.pbyte = (TEXT *)fld;
stringFld.size = (*field)->field_length;
recGetBYTES((dsmText_t *)prow, i, 0, &stringFld, &unknown);
if (unknown)
{
(*field)->set_null();
}
else
{
(*field)->set_notnull();
(*field)->store(fld, (*field)->field_length);
}
my_free(fld, MYF(MY_ALLOW_ZERO_PTR));
break;
default:
break;
}
}
}
else
{
memcpy(record,(char*) prow,table->reclength);
}
}
else
{
/* Copy null bits */
const char *ptr= (const char*) prow;
memcpy(record, ptr, table->null_bytes);
ptr+=table->null_bytes;
for (Field **field=table->field ; *field ; field++)
{
#ifdef GEMINI_TINYBLOB_IN_ROW
/* Tiny blobs (255 bytes or less) are stored in the row; larger
** blobs are stored in a separate storage object (see ha_gemini::create).
*/
if ((*field)->type() == FIELD_TYPE_BLOB &&
((Field_blob*)*field)->blobtype() != FIELD_TYPE_TINY_BLOB)
#else
if ((*field)->type() == FIELD_TYPE_BLOB)
#endif
{
dsmBlob_t gemBlob;
gemBlob.areaType = DSMOBJECT_BLOB;
gemBlob.blobObjNo = tableNumber;
gemBlob.blobId = (dsmBlobId_t)(((Field_blob*)*field)->get_id(ptr));
if (gemBlob.blobId)
{
gemBlob.totLength =
gemBlob.segLength = ((Field_blob*)*field)->get_length(ptr);
/* Allocate memory to store the blob. This memory is freed
** the next time unpack_row is called for this table.
*/
gemBlob.pBuffer = (dsmBuffer_t *)my_malloc(gemBlob.totLength,
MYF(0));
if (!gemBlob.pBuffer)
{
return HA_ERR_OUT_OF_MEM;
}
gemBlob.blobContext.blobOffset = 0;
dsmBlobStart(pcontext, &gemBlob);
dsmBlobGet(pcontext, &gemBlob, NULL);
dsmBlobEnd(pcontext, &gemBlob);
}
else
{
gemBlob.pBuffer = 0;
}
ptr = ((Field_blob*)*field)->unpack_id(record + (*field)->offset(),
ptr, (char *)gemBlob.pBuffer);
pBlobDesc->blobId = gemBlob.blobId;
my_free((char*)pBlobDesc->pBlob, MYF(MY_ALLOW_ZERO_PTR));
pBlobDesc->pBlob = gemBlob.pBuffer;
pBlobDesc++;
}
else
{
ptr= (*field)->unpack(record + (*field)->offset(), ptr);
}
}
}
return 0;
}
int ha_gemini::key_cmp(uint keynr, const byte * old_row,
const byte * new_row, bool updateStats)
{
KEY_PART_INFO *key_part=table->key_info[keynr].key_part;
KEY_PART_INFO *end=key_part+table->key_info[keynr].key_parts;
for ( uint i = 0 ; key_part != end ; key_part++, i++)
{
if (key_part->null_bit)
{
if ((old_row[key_part->null_offset] & key_part->null_bit) !=
(new_row[key_part->null_offset] & key_part->null_bit))
{
if(updateStats)
table->key_info[keynr].rec_per_key[i]++;
return 1;
}
else if((old_row[key_part->null_offset] & key_part->null_bit) &&
(new_row[key_part->null_offset] & key_part->null_bit))
/* Both are null */
continue;
}
if (key_part->key_part_flag & (HA_BLOB_PART | HA_VAR_LENGTH))
{
if (key_part->field->cmp_binary((char*)(old_row + key_part->offset),
(char*)(new_row + key_part->offset),
(ulong) key_part->length))
{
if(updateStats)
table->key_info[keynr].rec_per_key[i]++;
return 1;
}
}
else
{
if (memcmp(old_row+key_part->offset, new_row+key_part->offset,
key_part->length))
{
/* Check for special case of -0 which causes table check
to find an invalid key when comparing the the index
value of 0 to the -0 stored in the row */
if(key_part->field->type() == FIELD_TYPE_DECIMAL)
{
double fieldValue;
char *ptr = key_part->field->ptr;
key_part->field->ptr = (char *)old_row + key_part->offset;
fieldValue = key_part->field->val_real();
if(fieldValue == 0)
{
key_part->field->ptr = (char *)new_row + key_part->offset;
fieldValue = key_part->field->val_real();
if(fieldValue == 0)
{
key_part->field->ptr = ptr;
continue;
}
}
key_part->field->ptr = ptr;
}
if(updateStats)
{
table->key_info[keynr].rec_per_key[i]++;
}
return 1;
}
}
}
return 0;
}
int gemini_parse_table_name(const char *fullname, char *dbname, char *tabname)
{
char *namestart;
char *nameend;
/* separate out the name of the table and the database
*/
namestart = (char *)strchr(fullname + start_of_name, '/');
if (!namestart)
{
/* if on Windows, slashes go the other way */
namestart = (char *)strchr(fullname + start_of_name, '\\');
}
nameend = (char *)strchr(fullname + start_of_name, '.');
/* sometimes fullname has an extension, sometimes it doesn't */
if (!nameend)
{
nameend = (char *)fullname + strlen(fullname);
}
strncpy(dbname, fullname + start_of_name,
(namestart - fullname) - start_of_name);
dbname[(namestart - fullname) - start_of_name] = '\0';
strncpy(tabname, namestart + 1, (nameend - namestart) - 1);
tabname[nameend - namestart - 1] = '\0';
return 0;
}
/* PROGRAM: gemini_is_vst - if the name is the name of a VST, return
* its number
*
* RETURNS: Table number if a match is found
* 0 if not a VST
*/
int
gemini_is_vst(const char *pname) /* IN the name */
{
int tablenum = 0;
for (int i = 0; i < vstnumfils; i++)
{
if (strcmp(pname, vstfil[i].filename) == 0)
{
tablenum = vstfil[i].filnum;
break;
}
}
return tablenum;
}
static void print_msg(THD *thd, const char *table_name, const char *op_name,
const char *msg_type, const char *fmt, ...)
{
String* packet = &thd->packet;
packet->length(0);
char msgbuf[256];
msgbuf[0] = 0;
va_list args;
va_start(args,fmt);
my_vsnprintf(msgbuf, sizeof(msgbuf), fmt, args);
msgbuf[sizeof(msgbuf) - 1] = 0; // healthy paranoia
DBUG_PRINT(msg_type,("message: %s",msgbuf));
net_store_data(packet, table_name);
net_store_data(packet, op_name);
net_store_data(packet, msg_type);
net_store_data(packet, msgbuf);
if (my_net_write(&thd->net, (char*)thd->packet.ptr(),
thd->packet.length()))
thd->killed=1;
}
/* Load shared area with rows per key statistics */
void
ha_gemini::get_index_stats(THD *thd)
{
dsmStatus_t rc = 0;
ha_rows *rec_per_key = share->rec_per_key;
for(uint i = 0; i < table->keys && !rc; i++)
{
for (uint j = 0; j < table->key_info[i].key_parts && !rc;j++)
{
LONG64 rows_per_key;
rc = dsmIndexStatsGet((dsmContext_t *)thd->gemini.context,
tableNumber, pindexNumbers[i],(int)j,
&rows_per_key);
if (rc)
{
gemini_msg((dsmContext_t *)thd->gemini.context,
"Index Statistics faild for table %d index %d, error %d",
tableNumber, pindexNumbers[i], rc);
}
*rec_per_key = (ha_rows)rows_per_key;
rec_per_key++;
}
}
return;
}
/****************************************************************************
Handling the shared GEM_SHARE structure that is needed to provide
a global in memory storage location of the rec_per_key stats used
by the optimizer.
****************************************************************************/
static byte* gem_get_key(GEM_SHARE *share,uint *length,
my_bool not_used __attribute__((unused)))
{
*length=share->table_name_length;
return (byte*) share->table_name;
}
static GEM_SHARE *get_share(const char *table_name, TABLE *table)
{
GEM_SHARE *share;
pthread_mutex_lock(&gem_mutex);
uint length=(uint) strlen(table_name);
if (!(share=(GEM_SHARE*) hash_search(&gem_open_tables, (byte*) table_name,
length)))
{
ha_rows *rec_per_key;
char *tmp_name;
if ((share=(GEM_SHARE *)
my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
&share, sizeof(*share),
&rec_per_key, table->key_parts * sizeof(ha_rows),
&tmp_name, length+1,
NullS)))
{
share->rec_per_key = rec_per_key;
share->table_name = tmp_name;
share->table_name_length=length;
strcpy(share->table_name,table_name);
if (hash_insert(&gem_open_tables, (byte*) share))
{
pthread_mutex_unlock(&gem_mutex);
my_free((gptr) share,0);
return 0;
}
thr_lock_init(&share->lock);
pthread_mutex_init(&share->mutex,NULL);
}
}
pthread_mutex_unlock(&gem_mutex);
return share;
}
static int free_share(GEM_SHARE *share, bool mutex_is_locked)
{
pthread_mutex_lock(&gem_mutex);
if (mutex_is_locked)
pthread_mutex_unlock(&share->mutex);
if (!--share->use_count)
{
hash_delete(&gem_open_tables, (byte*) share);
thr_lock_delete(&share->lock);
pthread_mutex_destroy(&share->mutex);
my_free((gptr) share, MYF(0));
}
pthread_mutex_unlock(&gem_mutex);
return 0;
}
static void gemini_lock_table_overflow_error(dsmContext_t *pcontext)
{
gemini_msg(pcontext, "The total number of locks exceeds the lock table size");
gemini_msg(pcontext, "Either increase gemini_lock_table_size or use a");
gemini_msg(pcontext, "different transaction isolation level");
}
#endif /* HAVE_GEMINI_DB */