mirror of
https://github.com/MariaDB/server.git
synced 2025-01-17 20:42:30 +01:00
219682ef86
Removed all Attic directories. BitKeeper/deleted/.del-myisam.doc~519bb06ecc870298: Delete: Docs/Attic/myisam.doc BitKeeper/deleted/.del-libmysql.c~72949a7043113807: Delete: client/Attic/libmysql.c BitKeeper/deleted/.del-net.c~c18042da6fa4e693: Delete: client/Attic/net.c BitKeeper/deleted/.del-print_defaults.c~362952979aa7b330: Delete: extra/Attic/print_defaults.c BitKeeper/deleted/.del-config-win32.h~65db818ec7e8f21b: Delete: include/Attic/config-win32.h BitKeeper/deleted/.del-m_ctype.h.in~f671e3c2d611ba97: Delete: include/Attic/m_ctype.h.in BitKeeper/deleted/.del-mysql_com.h.in~85b1ea7ced528c32: Delete: include/Attic/mysql_com.h.in BitKeeper/deleted/.del-ft_global.h~fe46fb515f1e375: Delete: myisam/Attic/ft_global.h BitKeeper/deleted/.del-lex_hash.h~14f912771118b50c: Delete: sql/Attic/lex_hash.h BitKeeper/deleted/.del-mini_client.c~9a3778c387d06a81: Delete: sql/Attic/mini_client.c BitKeeper/deleted/.del-mini_client_errors.c~29edad51a5d0b068: Delete: sql/Attic/mini_client_errors.c BitKeeper/deleted/.del-mybinlogdump.cc~5dbdd2bde98d6169: Delete: sql/Attic/mybinlogdump.cc BitKeeper/deleted/.del-net_serv.c~52dabcd773a39e10: Delete: sql/Attic/net_serv.c BitKeeper/deleted/.del-bootstrap-ctype.c~6d2a8cda2d6a35ff: Delete: strings/Attic/bootstrap-ctype.c BitKeeper/deleted/.del-ct_init.c~f0948bdd35ceedc3: Delete: strings/Attic/ct_init.c BitKeeper/deleted/.del-ctype-cp1251.c~cdf74b9168408b3: Delete: strings/Attic/ctype-cp1251.c BitKeeper/deleted/.del-ctype-cp1257.c~732611cbc74aeafc: Delete: strings/Attic/ctype-cp1257.c BitKeeper/deleted/.del-ctype-croat.c~d2d805ee6f10cbcc: Delete: strings/Attic/ctype-croat.c BitKeeper/deleted/.del-ctype-danish.c~dc5451066eb272ae: Delete: strings/Attic/ctype-danish.c BitKeeper/deleted/.del-ctype-dec8.c~68f257dd2202d0c7: Delete: strings/Attic/ctype-dec8.c BitKeeper/deleted/.del-ctype-dos.c~f77bd08acf13a8c1: Delete: strings/Attic/ctype-dos.c BitKeeper/deleted/.del-ctype-estonia.c~fc8a69424f7cb66b: Delete: strings/Attic/ctype-estonia.c BitKeeper/deleted/.del-ctype-german1.c~f7830c509bb358f7: Delete: strings/Attic/ctype-german1.c BitKeeper/deleted/.del-ctype-greek.c~90acdff1195209ca: Delete: strings/Attic/ctype-greek.c BitKeeper/deleted/.del-ctype-hebrew.c~d3b4a000d51e76dc: Delete: strings/Attic/ctype-hebrew.c BitKeeper/deleted/.del-ctype-hp8.c~749e1be0f028d349: Delete: strings/Attic/ctype-hp8.c BitKeeper/deleted/.del-ctype-hungarian.c~5cf0bf7fa0312637: Delete: strings/Attic/ctype-hungarian.c BitKeeper/deleted/.del-ctype-koi8_ru.c~8ff4188c642c9bd: Delete: strings/Attic/ctype-koi8_ru.c BitKeeper/deleted/.del-ctype-koi8_ukr.c~a04aa14a6d62335a: Delete: strings/Attic/ctype-koi8_ukr.c BitKeeper/deleted/.del-ctype-latin1.c~cc63880f19c2303e: Delete: strings/Attic/ctype-latin1.c BitKeeper/deleted/.del-ctype-latin2.c~31895c4b83654342: Delete: strings/Attic/ctype-latin2.c BitKeeper/deleted/.del-ctype-swe7.c~bb1b012225d7d02c: Delete: strings/Attic/ctype-swe7.c BitKeeper/deleted/.del-ctype-usa7.c~d19d859dca5675f: Delete: strings/Attic/ctype-usa7.c BitKeeper/deleted/.del-ctype-win1250.c~1ce7a24255780a1: Delete: strings/Attic/ctype-win1250.c BitKeeper/deleted/.del-ctype-win1251.c~762607f4fd7d52ad: Delete: strings/Attic/ctype-win1251.c BitKeeper/deleted/.del-ctype-win1251ukr.c~b5a7cca889bbef58: Delete: strings/Attic/ctype-win1251ukr.c BitKeeper/deleted/.del-ctype.c.in~8bf48d4bcbc5f675: Delete: strings/Attic/ctype.c.in BitKeeper/deleted/.del-memory.h~450f586e82a26d99: Delete: strings/Attic/memory.h BitKeeper/deleted/.del-ptr_cmp.c~57e682a26e769597: Delete: strings/Attic/ptr_cmp.c BitKeeper/deleted/.del-my-example.cnf.sh~87a7e1f4d24b62a9: Delete: support-files/Attic/my-example.cnf.sh BitKeeper/deleted/.del-my-huge.cfg.sh~589bdcd2d2c4360b: Delete: support-files/Attic/my-huge.cfg.sh BitKeeper/deleted/.del-my-large.cfg.sh~842c8e76253c9396: Delete: support-files/Attic/my-large.cfg.sh BitKeeper/deleted/.del-my-medium.cfg.sh~c49880d26ef0648e: Delete: support-files/Attic/my-medium.cfg.sh BitKeeper/deleted/.del-my-small.cfg.sh~85023c559a1d96c: Delete: support-files/Attic/my-small.cfg.sh sql/ha_innobase.cc: Fix a crash with tables with many columns and a range query: memory corruption occurred Docs/manual.texi: Changelog mysql-test/r/innobase.result: New test case for innobase scripts/mysql_install_db.sh: Fixed typo sql/ha_berkeley.h: Fixed that 'have_bdb' is disabled if we can't initialize bdb sql/ha_gemini.cc: Removed include file that got compiliation to fail on some systems sql/handler.cc: Fixed that 'have_tabltype' is disabled if we can't initialize it. sql/mysqld.cc: Fix bug that could cause a fast connect/close to fail when using --debug
2683 lines
70 KiB
C++
2683 lines
70 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 "my_pthread.h"
|
|
#include "dbconfig.h"
|
|
#include "dsmpub.h"
|
|
#include "recpub.h"
|
|
#include "vststat.h"
|
|
|
|
#include <m_ctype.h>
|
|
#include <myisampack.h>
|
|
#include <assert.h>
|
|
#include <hash.h>
|
|
#include <stdarg.h>
|
|
#include "geminikey.h"
|
|
#include "ha_gemini.h"
|
|
|
|
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;
|
|
|
|
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 = MYMAXKEYSIZE * 2;
|
|
|
|
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;
|
|
}
|
|
|
|
/* Gotta connect to the database regardless of the operation */
|
|
rc = dsmContextCreate(&pfirstContext);
|
|
if( rc != 0 )
|
|
{
|
|
printf("dsmContextCreate failed %ld\n",rc);
|
|
goto badret;
|
|
}
|
|
rc = dsmContextSetString(pfirstContext, DSM_TAGDB_DBNAME,
|
|
strlen(gemini_dbname), (TEXT *)gemini_dbname);
|
|
if( rc != 0 )
|
|
{
|
|
printf("Dbname tag failed %ld\n", rc);
|
|
goto badret;
|
|
}
|
|
|
|
fn_format(pmsgsfile, GEM_MSGS_FILE, language, ".db", 2 | 4);
|
|
rc = dsmContextSetString(pfirstContext, DSM_TAGDB_MSGS_FILE,
|
|
strlen(pmsgsfile), (TEXT *)pmsgsfile);
|
|
if( rc != 0 )
|
|
{
|
|
printf("MSGS_DIR tag failed %ld\n", rc);
|
|
goto badret;
|
|
}
|
|
|
|
rc = dsmContextSetLong(pfirstContext,DSM_TAGDB_ACCESS_TYPE,DSM_ACCESS_STARTUP);
|
|
if ( rc != 0 )
|
|
{
|
|
printf("ACCESS TAG set failed %ld\n",rc);
|
|
goto badret;
|
|
}
|
|
rc = dsmContextSetLong(pfirstContext,DSM_TAGDB_ACCESS_ENV, DSM_SQL_ENGINE);
|
|
if( rc != 0 )
|
|
{
|
|
printf("ACCESS_ENV set failed %ld",rc);
|
|
goto badret;
|
|
}
|
|
|
|
rc = dsmContextSetString(pfirstContext, DSM_TAGDB_DATADIR,
|
|
strlen(mysql_real_data_home),
|
|
(TEXT *)mysql_real_data_home);
|
|
if( rc != 0 )
|
|
{
|
|
printf("Datadir tag failed %ld\n", rc);
|
|
goto badret;
|
|
}
|
|
|
|
rc = dsmContextSetLong(pfirstContext, DSM_TAGDB_MAX_USERS,
|
|
gemini_connection_limit);
|
|
if(rc != 0)
|
|
{
|
|
printf("MAX_USERS tag set failed %ld",rc);
|
|
goto badret;
|
|
}
|
|
|
|
rc = dsmContextSetLong(pfirstContext, DSM_TAGDB_DEFAULT_LOCK_TIMEOUT,
|
|
gemini_lock_wait_timeout);
|
|
if(rc != 0)
|
|
{
|
|
printf("MAX_LOCK_ENTRIES tag set failed %ld",rc);
|
|
goto badret;
|
|
}
|
|
|
|
rc = dsmContextSetLong(pfirstContext, DSM_TAGDB_MAX_LOCK_ENTRIES,
|
|
gemini_locktablesize);
|
|
if(rc != 0)
|
|
{
|
|
printf("MAX_LOCK_ENTRIES tag set failed %ld",rc);
|
|
goto badret;
|
|
}
|
|
|
|
rc = dsmContextSetLong(pfirstContext, DSM_TAGDB_SPIN_AMOUNT,
|
|
gemini_spin_retries);
|
|
if(rc != 0)
|
|
{
|
|
printf("SPIN_AMOUNT tag set failed %ld",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)
|
|
{
|
|
printf("DB_BUFFERS tag set failed %ld",rc);
|
|
goto badret;
|
|
}
|
|
|
|
rc = dsmContextSetLong(pfirstContext, DSM_TAGDB_FLUSH_AT_COMMIT,
|
|
((gemini_options & GEMOPT_FLUSH_LOG) ? 1 : 0));
|
|
if(rc != 0)
|
|
{
|
|
printf("FLush_Log_At_Commit tag set failed %ld",rc);
|
|
goto badret;
|
|
}
|
|
rc = dsmContextSetLong(pfirstContext, DSM_TAGDB_DIRECT_IO,
|
|
((gemini_options & GEMOPT_UNBUFFERED_IO) ? 1 : 0));
|
|
if(rc != 0)
|
|
{
|
|
printf("DIRECT_IO tag set failed %ld",rc);
|
|
goto badret;
|
|
}
|
|
|
|
rc = dsmContextSetLong(pfirstContext, DSM_TAGDB_CRASH_PROTECTION,
|
|
((gemini_recovery_options & GEMINI_RECOVERY_FULL) ? 1 : 0));
|
|
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)
|
|
{
|
|
printf("CRASH_PROTECTION tag set failed %ld",rc);
|
|
goto badret;
|
|
}
|
|
|
|
rc = dsmUserConnect(pfirstContext,(TEXT *)"Multi-user",
|
|
DSM_DB_OPENDB | DSM_DB_OPENFILE);
|
|
if( rc != 0 )
|
|
{
|
|
printf("dsmUserConnect failed rc = %ld\n",rc);
|
|
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);
|
|
DBUG_RETURN(0);
|
|
|
|
badret:
|
|
gemini_skip = 1;
|
|
DBUG_RETURN(0);
|
|
}
|
|
|
|
static int gemini_helper_threads(dsmContext_t *pContext)
|
|
{
|
|
int rc = 0;
|
|
pthread_t hThread;
|
|
DBUG_ENTER("gemini_helper_threads");
|
|
rc = pthread_create (&hThread, 0, gemini_watchdog, (void *)pContext);
|
|
if (rc)
|
|
{
|
|
printf("Can't create gemini watchdog thread");
|
|
goto done;
|
|
}
|
|
if(!gemini_io_threads)
|
|
goto done;
|
|
|
|
rc = pthread_create(&hThread, 0, gemini_rl_writer, (void *)pContext);
|
|
if(rc)
|
|
{
|
|
printf("Can't create gemini recovery log writer thread");
|
|
goto done;
|
|
}
|
|
|
|
for( int i = gemini_io_threads - 1;i;i--)
|
|
{
|
|
rc = pthread_create(&hThread, 0, gemini_apw, (void *)pContext);
|
|
if(rc)
|
|
{
|
|
printf("Can't create gemini 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 )
|
|
{
|
|
printf("dsmContextCopy failed for watchdog %d\n",rc);
|
|
|
|
return 0;
|
|
}
|
|
rc = dsmUserConnect(pmyContext,NULL,0);
|
|
|
|
if( rc != 0 )
|
|
{
|
|
printf("dsmUserConnect failed for watchdog %d\n",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 )
|
|
{
|
|
printf("dsmContextCopy failed for recovery log writer %d\n",rc);
|
|
|
|
return 0;
|
|
}
|
|
rc = dsmUserConnect(pmyContext,NULL,0);
|
|
|
|
if( rc != 0 )
|
|
{
|
|
printf("dsmUserConnect failed for recovery log writer %d\n",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 )
|
|
{
|
|
printf("dsmContextCopy failed for gemini page writer %d\n",rc);
|
|
my_thread_end();
|
|
return 0;
|
|
}
|
|
rc = dsmUserConnect(pmyContext,NULL,0);
|
|
|
|
if( rc != 0 )
|
|
{
|
|
printf("dsmUserConnect failed for gemini page writer %d\n",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)
|
|
{
|
|
printf("SPIN_AMOUNT tag set failed %ld",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 )
|
|
{
|
|
printf("dsmContextCopy failed %ld\n",rc);
|
|
|
|
return(rc);
|
|
}
|
|
rc = dsmUserConnect((dsmContext_t *)thd->gemini.context,NULL,0);
|
|
|
|
if( rc != 0 )
|
|
{
|
|
printf("dsmUserConnect failed %ld\n",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");
|
|
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);
|
|
}
|
|
|
|
/* 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;
|
|
thr_lock_init(&alock);
|
|
thr_lock_data_init(&alock,&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=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);
|
|
}
|
|
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)
|
|
tableStatus = HA_ERR_CRASHED;
|
|
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);
|
|
pindexNumbers[i] = objectNumber;
|
|
}
|
|
}
|
|
else
|
|
pindexNumbers = 0;
|
|
|
|
DBUG_RETURN(rc);
|
|
}
|
|
|
|
int ha_gemini::close(void)
|
|
{
|
|
DBUG_ENTER("ha_gemini::close");
|
|
thr_lock_delete(&alock);
|
|
my_free(rec_buff,MYF(MY_ALLOW_ZERO_PTR));
|
|
rec_buff = 0;
|
|
my_free((char *)pindexNumbers,MYF(MY_ALLOW_ZERO_PTR));
|
|
pindexNumbers = 0;
|
|
DBUG_RETURN(0);
|
|
}
|
|
|
|
|
|
int ha_gemini::write_row(byte * record)
|
|
{
|
|
int error = 0;
|
|
dsmRecord_t dsmRecord;
|
|
THD *thd;
|
|
|
|
DBUG_ENTER("write_row");
|
|
|
|
if(tableStatus)
|
|
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);
|
|
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->reclength;
|
|
|
|
if ((error=pack_row((byte **)&dsmRecord.pbuffer, (int *)&dsmRecord.recLength,
|
|
record)))
|
|
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;
|
|
}
|
|
}
|
|
|
|
DBUG_RETURN(error);
|
|
}
|
|
|
|
longlong ha_gemini::get_auto_increment()
|
|
{
|
|
longlong nr;
|
|
int error;
|
|
THD *thd=current_thd;
|
|
|
|
error = dsmTableAutoIncrement((dsmContext_t *)thd->gemini.context,
|
|
tableNumber, (ULONG64 *)&nr);
|
|
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 - 3;
|
|
theKey.akey.unknown_comp = 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;
|
|
|
|
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;
|
|
fieldType = gemDataType(key_part->field->type());
|
|
if(fieldType == 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(rec_buff, key_part->length);
|
|
key_part->field->ptr = ptr;
|
|
pos = (unsigned char *)rec_buff;
|
|
}
|
|
else
|
|
{
|
|
pos = (unsigned char *)record + key_part->offset;
|
|
}
|
|
|
|
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))
|
|
{
|
|
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->reclength;
|
|
|
|
if ((error=pack_row((byte **)&dsmRecord.pbuffer, (int *)&dsmRecord.recLength,
|
|
new_record)))
|
|
{
|
|
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;
|
|
DBUG_ENTER("delete_row");
|
|
|
|
statistic_increment(ha_delete_count,&LOCK_status);
|
|
|
|
if(thd->gemini.needSavepoint)
|
|
{
|
|
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;
|
|
}
|
|
|
|
dsmRecord.table = tableNumber;
|
|
dsmRecord.recid = lastRowid;
|
|
|
|
error = handleIndexEntries(record, dsmRecord.recid,KEY_DELETE);
|
|
if(!error)
|
|
{
|
|
error = dsmRecordDelete((dsmContext_t *)thd->gemini.context,
|
|
&dsmRecord, 0, NULL);
|
|
}
|
|
|
|
DBUG_RETURN(error);
|
|
}
|
|
|
|
int ha_gemini::index_init(uint keynr)
|
|
{
|
|
int error = 0;
|
|
int keyStringLen;
|
|
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];
|
|
pbracketLimit->keycomps = 1;
|
|
keyStringLen = 0;
|
|
error = gemKeyHigh(pbracketLimit->keystr, &keyStringLen,
|
|
pbracketLimit->index);
|
|
|
|
/* 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 */
|
|
pbracketLimit->keyLen = (COUNT)keyStringLen - 3;
|
|
|
|
pbracketBase->descending_key = pbracketLimit->descending_key = 0;
|
|
pbracketBase->ksubstr = pbracketLimit->ksubstr = 0;
|
|
|
|
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;
|
|
|
|
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());
|
|
if(fieldType == GEM_CHAR)
|
|
{
|
|
key_part->field->store(key_ptr + offset, key_part->length);
|
|
key_part->field->sort_string(rec_buff, key_part->length);
|
|
pos = (unsigned char *)rec_buff;
|
|
}
|
|
else
|
|
{
|
|
pos = (unsigned char *)key_ptr + offset;
|
|
}
|
|
|
|
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[7];
|
|
|
|
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->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 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 */
|
|
pbracketBase->keyLen -= 3;
|
|
pbracketLimit->keyLen -= 3;
|
|
|
|
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)
|
|
DBUG_RETURN(tableStatus);
|
|
|
|
thd = current_thd;
|
|
|
|
if(pbracketBase->index == 0)
|
|
{
|
|
error = gemKeyLow(pbracketBase->keystr, &keyStringLen,
|
|
pbracketLimit->index);
|
|
|
|
pbracketBase->keyLen = (COUNT)keyStringLen - 3;
|
|
pbracketBase->index = pbracketLimit->index;
|
|
pbracketBase->keycomps = 1;
|
|
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);
|
|
if(error)
|
|
goto errorReturn;
|
|
|
|
pbracketBase->keyLen = (COUNT)keyStringLen - 3;
|
|
pbracketBase->index = pbracketLimit->index;
|
|
pbracketBase->keycomps = 1;
|
|
|
|
error = findRow(thd,DSMFINDLAST,buf);
|
|
|
|
errorReturn:
|
|
if (error == DSM_S_ENDLOOP)
|
|
error = HA_ERR_END_OF_FILE;
|
|
|
|
table->status = error ? STATUS_NOT_FOUND : 0;
|
|
DBUG_RETURN(error);
|
|
|
|
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)
|
|
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->reclength;
|
|
|
|
error = dsmTableScan((dsmContext_t *)thd->gemini.context,
|
|
&dsmRecord, DSMFINDNEXT, lockMode, 0);
|
|
|
|
if(!error)
|
|
{
|
|
lastRowid = dsmRecord.recid;
|
|
unpack_row((char *)buf,(char *)dsmRecord.pbuffer);
|
|
}
|
|
if(!error)
|
|
;
|
|
else 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;
|
|
|
|
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->reclength;
|
|
|
|
rc = dsmRecordGet((dsmContext_t *)gemini_context,
|
|
&dsmRecord, 0);
|
|
|
|
if(!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(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 if(rc == DSM_S_RQSTREJ)
|
|
rc = HA_ERR_LOCK_WAIT_TIMEOUT;
|
|
else if (rc == DSM_S_LKTBFULL)
|
|
rc = HA_ERR_LOCK_TABLE_FULL;
|
|
|
|
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;
|
|
|
|
error = dsmRowCount((dsmContext_t *)thd->gemini.context,tableNumber,&rows);
|
|
records = (ha_rows)rows;
|
|
deleted = 0;
|
|
}
|
|
else if ((flag & HA_STATUS_CONST))
|
|
{
|
|
;
|
|
}
|
|
else if ((flag & HA_STATUS_ERRKEY))
|
|
{
|
|
errkey=last_dup_key;
|
|
}
|
|
else if ((flag & HA_STATUS_TIME))
|
|
{
|
|
;
|
|
}
|
|
else if ((flag & HA_STATUS_AUTO))
|
|
{
|
|
;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
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(thd->gemini.tx_isolation == ISO_READ_UNCOMMITTED)
|
|
lockMode = DSM_LK_NOLOCK;
|
|
else if(table->reginfo.lock_type > TL_WRITE_ALLOW_READ)
|
|
lockMode = DSM_LK_EXCL;
|
|
else
|
|
lockMode = DSM_LK_SHARE;
|
|
}
|
|
*to++= &lock;
|
|
return to;
|
|
}
|
|
|
|
|
|
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 )
|
|
{
|
|
printf("dsmAreaNew failed %ld\n",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 )
|
|
{
|
|
printf("dsmExtentCreate failed %ld\n",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 && 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 )
|
|
{
|
|
printf("dsmAreaNew failed %ld\n",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 )
|
|
{
|
|
printf("dsmExtentCreate failed %ld\n",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);
|
|
|
|
}
|
|
}
|
|
rc = dsmTableAutoIncrementSet(pcontext,tableNumber,
|
|
create_info->auto_increment_value);
|
|
|
|
|
|
|
|
/* 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)
|
|
{
|
|
printf("Cound not find table number for %s with string %s, %ld\n",
|
|
pname,name_buff,rc);
|
|
rc = gemini_rollback(thd);
|
|
if (rc)
|
|
{
|
|
printf("Error in rollback %ld\n",rc);
|
|
}
|
|
DBUG_RETURN(rc);
|
|
}
|
|
|
|
rc = dsmObjectInfo(pcontext, tableNum, DSMOBJECT_MIXTABLE, &tableArea,
|
|
&objectAttr, &associate, &associateType, &block, &root);
|
|
if (rc)
|
|
{
|
|
printf("Failed to get area number for table %d, %s, return %ld\n",
|
|
tableNum, pname, rc);
|
|
rc = gemini_rollback(thd);
|
|
if (rc)
|
|
{
|
|
printf("Error in rollback %ld\n",rc);
|
|
}
|
|
}
|
|
|
|
indexArea = DSMAREA_INVALID;
|
|
|
|
/* Delete the indexes and tables storage objects for with the table */
|
|
rc = dsmObjectDeleteAssociate(pcontext, tableNum, &indexArea);
|
|
if (rc)
|
|
{
|
|
printf("Error deleting storage objects for table number %d, return %ld\n",
|
|
(int)tableNum, rc);
|
|
|
|
/* roll back txn and return */
|
|
rc = gemini_rollback(thd);
|
|
if (rc)
|
|
{
|
|
printf("Error in rollback %ld\n",rc);
|
|
}
|
|
DBUG_RETURN(rc);
|
|
}
|
|
|
|
if (indexArea != DSMAREA_INVALID)
|
|
{
|
|
/* Delete the extents for both Index and Table */
|
|
rc = dsmExtentDelete(pcontext, indexArea, 0);
|
|
rc = dsmAreaDelete(pcontext, indexArea);
|
|
if (rc)
|
|
{
|
|
printf("Error deleting Index Area %ld, return %ld\n", indexArea, rc);
|
|
|
|
/* roll back txn and return */
|
|
rc = gemini_rollback(thd);
|
|
if (rc)
|
|
{
|
|
printf("Error in rollback %ld\n",rc);
|
|
}
|
|
DBUG_RETURN(rc);
|
|
}
|
|
}
|
|
|
|
rc = dsmExtentDelete(pcontext, tableArea, 0);
|
|
rc = dsmAreaDelete(pcontext, tableArea);
|
|
if (rc)
|
|
{
|
|
printf("Error deleting table Area %ld, name %s, return %ld\n",
|
|
tableArea, pname, rc);
|
|
/* roll back txn and return */
|
|
rc = gemini_rollback(thd);
|
|
if (rc)
|
|
{
|
|
printf("Error in rollback %ld\n",rc);
|
|
}
|
|
DBUG_RETURN(rc);
|
|
}
|
|
|
|
|
|
/* Commit the transaction */
|
|
rc = gemini_commit(thd);
|
|
if (rc)
|
|
{
|
|
printf("Failed to commit transaction %ld\n",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 tabname_buff[FN_REFLEN];
|
|
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;
|
|
|
|
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 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;
|
|
|
|
/* 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)
|
|
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);
|
|
if (rc)
|
|
goto errorReturn;
|
|
|
|
/* rename the physical table and index files (if necessary) */
|
|
rc = rename_file_ext(pfrom, pto, ha_gemini_ext);
|
|
if (!rc && indexArea)
|
|
{
|
|
rc = rename_file_ext(pfrom, pto, ha_gemini_idx_ext);
|
|
}
|
|
|
|
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 records / (gemini_blocksize / table->reclength);
|
|
}
|
|
|
|
int ha_gemini::check(THD* thd, HA_CHECK_OPT* check_opt)
|
|
{
|
|
int error;
|
|
int checkStatus = HA_ADMIN_OK;
|
|
ha_rows indexCount;
|
|
byte *buf = 0, *indexBuf = 0;
|
|
int errorCount = 0;
|
|
|
|
/* 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 = my_malloc(table->rec_buff_length,MYF(MY_WME));
|
|
indexBuf = my_malloc(table->rec_buff_length,MYF(MY_WME));
|
|
/* Lock the table */
|
|
error = dsmObjectLock((dsmContext_t *)thd->gemini.context,
|
|
(dsmObject_t)tableNumber,
|
|
DSMOBJECT_TABLE,0,
|
|
DSM_LK_SHARE, 1, 0);
|
|
if(error)
|
|
return error;
|
|
|
|
info(HA_STATUS_VARIABLE);
|
|
|
|
/* If quick option just scan along index converting and counting entries */
|
|
for (uint i = 0; i < table->keys; i++)
|
|
{
|
|
key_read = 1;
|
|
indexCount = 0;
|
|
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))
|
|
{
|
|
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;
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
if(!error)
|
|
error = index_next(indexBuf);
|
|
}
|
|
|
|
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;
|
|
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;
|
|
}
|
|
|
|
error_return:
|
|
my_free(buf,MYF(MY_ALLOW_ZERO_PTR));
|
|
index_end();
|
|
key_read = 0;
|
|
error = dsmObjectUnlock((dsmContext_t *)thd->gemini.context,
|
|
(dsmObject_t)tableNumber,
|
|
DSMOBJECT_TABLE,0,
|
|
DSM_LK_SHARE,0);
|
|
|
|
return checkStatus;
|
|
}
|
|
|
|
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)
|
|
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)
|
|
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);
|
|
|
|
buf = 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->reclength;
|
|
while(!error)
|
|
{
|
|
error = dsmTableScan((dsmContext_t *)thd->gemini.context,
|
|
&dsmRecord, DSMFINDNEXT, DSM_LK_NOLOCK,
|
|
1);
|
|
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);
|
|
my_free(buf,MYF(MY_ALLOW_ZERO_PTR));
|
|
|
|
error = dsmContextSetLong((dsmContext_t *)thd->gemini.context,
|
|
DSM_TAGCONTEXT_NO_LOGGING,0);
|
|
|
|
return error;
|
|
}
|
|
|
|
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 -= 3;
|
|
|
|
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 -= 3;
|
|
error = dsmIndexRowsInRange((dsmContext_t *)current_thd->gemini.context,
|
|
pbracketBase,pbracketLimit,
|
|
&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)
|
|
{
|
|
if (fixed_length_row)
|
|
{
|
|
*pprow = (byte *)record;
|
|
*ppackedLength=(int)table->reclength;
|
|
return 0;
|
|
}
|
|
if (table->blob_fields)
|
|
{
|
|
return HA_ERR_WRONG_COMMAND;
|
|
}
|
|
/* Copy null bits */
|
|
memcpy(rec_buff, record, table->null_bytes);
|
|
byte *ptr=rec_buff + table->null_bytes;
|
|
|
|
for (Field **field=table->field ; *field ; field++)
|
|
ptr=(byte*) (*field)->pack((char*) ptr,record + (*field)->offset());
|
|
|
|
*pprow=rec_buff;
|
|
*ppackedLength= (ptr - rec_buff);
|
|
return 0;
|
|
}
|
|
|
|
void ha_gemini::unpack_row(char *record, char *prow)
|
|
{
|
|
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++)
|
|
ptr= (*field)->unpack(record + (*field)->offset(), ptr);
|
|
}
|
|
}
|
|
|
|
int ha_gemini::key_cmp(uint keynr, const byte * old_row,
|
|
const byte * new_row)
|
|
{
|
|
KEY_PART_INFO *key_part=table->key_info[keynr].key_part;
|
|
KEY_PART_INFO *end=key_part+table->key_info[keynr].key_parts;
|
|
|
|
for ( ; key_part != end ; key_part++)
|
|
{
|
|
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))
|
|
return 1;
|
|
}
|
|
if (key_part->key_part_flag & (HA_BLOB_PART | HA_VAR_LENGTH))
|
|
{
|
|
|
|
if (key_part->field->cmp_binary(old_row + key_part->offset,
|
|
new_row + key_part->offset,
|
|
(ulong) key_part->length))
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
if (memcmp(old_row+key_part->offset, new_row+key_part->offset,
|
|
key_part->length))
|
|
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 = strchr(fullname + start_of_name, '/');
|
|
if (!namestart)
|
|
{
|
|
/* if on Windows, slashes go the other way */
|
|
namestart = strchr(fullname + start_of_name, '\\');
|
|
}
|
|
nameend = 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;
|
|
}
|
|
|
|
#endif /* HAVE_GEMINI_DB */
|