mariadb/sql/ha_gemini.cc
unknown d61418b94f Portability fixes.
Patches required by Gemini
Fix to properly detect if there is an active transaction in InnoDB
Fix to not lock thread structure when doing automatic rollback when thread ends
Allow -O lower_case_names=0 on UNIX


Docs/manual.texi:
  Some updates from mailing list.
  Changelog
client/mysqlbinlog.cc:
  Removed variables declared in net.c
configure.in:
  Added test for strtoll and fixed test for gethostname_r for AIX
mysql-test/t/innodb.test:
  Added test of active transactions
sql/field.cc:
  Patch required by Gemini
sql/field.h:
  Patch required by Gemini
sql/filesort.cc:
  Patch required by Gemini
sql/gen_lex_hash.cc:
  Update to support new syntax
sql/ha_gemini.cc:
  Patch required by Gemini
sql/ha_gemini.h:
  Patch required by Gemini
sql/ha_innobase.cc:
  Fix to properly detect if there is an active transaction in InnoDB
sql/handler.cc:
  Fix to properly detect if there is an active transaction in InnoDB
sql/handler.h:
  Fix to properly detect if there is an active transaction in InnoDB.
  Fix for Gemini
sql/lex.h:
  SHOW LOCKS
sql/mysqld.cc:
  Fix to not lock thread structure when doing automatic rollback when thread ends.
sql/share/portuguese/errmsg.txt:
  Update
sql/sql_class.cc:
  Fix to not lock thread structure when doing automatic rollback when thread ends.
sql/sql_class.h:
  Fix to properly detect if there is an active transaction in InnoDB
sql/sql_delete.cc:
  Fix for Gemini
sql/sql_parse.cc:
  Allow -O lower_case_names=0 on UNIX
sql/sql_select.cc:
  Fix for Gemini
sql/sql_table.cc:
  Allow -O lower_case_names=0 on UNIX
sql/sql_update.cc:
  Fix for Gemini
sql/sql_yacc.yy:
  For SHOW LOCKS
strings/strto.c:
  Portability fix
2001-08-21 20:06:00 +03:00

3630 lines
98 KiB
C++

/* Copyright (C) 2000 MySQL AB & 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 */
/* This file is based on ha_berkeley.cc */
#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");
gemini_basedir=mysql_home;
/* 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 */