diff options
Diffstat (limited to 'sql/ha_gemini.cc')
-rw-r--r-- | sql/ha_gemini.cc | 1365 |
1 files changed, 1155 insertions, 210 deletions
diff --git a/sql/ha_gemini.cc b/sql/ha_gemini.cc index 73241c60be7..c95a348f238 100644 --- a/sql/ha_gemini.cc +++ b/sql/ha_gemini.cc @@ -21,8 +21,7 @@ #include "mysql_priv.h" #ifdef HAVE_GEMINI_DB - -#include "my_pthread.h" +#include "ha_gemini.h" #include "dbconfig.h" #include "dsmpub.h" #include "recpub.h" @@ -30,11 +29,22 @@ #include <m_ctype.h> #include <myisampack.h> +#include <m_string.h> #include <assert.h> #include <hash.h> #include <stdarg.h> #include "geminikey.h" -#include "ha_gemini.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"; @@ -48,6 +58,7 @@ 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; @@ -61,7 +72,7 @@ TYPELIB gemini_recovery_typelib= {array_elements(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 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, @@ -87,40 +98,56 @@ bool gemini_init(void) 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 ) { - printf("dsmContextCreate failed %ld\n",rc); + 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 ) { - printf("Dbname tag failed %ld\n", rc); + 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 ) { - printf("MSGS_DIR tag failed %ld\n", rc); + 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 ) { - printf("ACCESS TAG set failed %ld\n",rc); + gemini_msg(pfirstContext, "ACCESS TAG set failed %l",rc); goto badret; } rc = dsmContextSetLong(pfirstContext,DSM_TAGDB_ACCESS_ENV, DSM_SQL_ENGINE); if( rc != 0 ) { - printf("ACCESS_ENV set failed %ld",rc); + gemini_msg(pfirstContext, "ACCESS_ENV set failed %l",rc); goto badret; } @@ -129,7 +156,7 @@ bool gemini_init(void) (TEXT *)mysql_real_data_home); if( rc != 0 ) { - printf("Datadir tag failed %ld\n", rc); + gemini_msg(pfirstContext, "Datadir tag failed %l", rc); goto badret; } @@ -137,7 +164,7 @@ bool gemini_init(void) gemini_connection_limit); if(rc != 0) { - printf("MAX_USERS tag set failed %ld",rc); + gemini_msg(pfirstContext, "MAX_USERS tag set failed %l",rc); goto badret; } @@ -145,7 +172,7 @@ bool gemini_init(void) gemini_lock_wait_timeout); if(rc != 0) { - printf("MAX_LOCK_ENTRIES tag set failed %ld",rc); + gemini_msg(pfirstContext, "MAX_LOCK_ENTRIES tag set failed %l",rc); goto badret; } @@ -153,7 +180,7 @@ bool gemini_init(void) gemini_locktablesize); if(rc != 0) { - printf("MAX_LOCK_ENTRIES tag set failed %ld",rc); + gemini_msg(pfirstContext, "MAX_LOCK_ENTRIES tag set failed %l",rc); goto badret; } @@ -161,7 +188,7 @@ bool gemini_init(void) gemini_spin_retries); if(rc != 0) { - printf("SPIN_AMOUNT tag set failed %ld",rc); + gemini_msg(pfirstContext, "SPIN_AMOUNT tag set failed %l",rc); goto badret; } @@ -172,22 +199,22 @@ bool gemini_init(void) gemini_buffer_cache); if(rc != 0) { - printf("DB_BUFFERS tag set failed %ld",rc); + 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) ? 1 : 0)); + ((gemini_options & GEMOPT_FLUSH_LOG) ? 0 : 1)); if(rc != 0) { - printf("FLush_Log_At_Commit tag set failed %ld",rc); + 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) { - printf("DIRECT_IO tag set failed %ld",rc); + gemini_msg(pfirstContext, "DIRECT_IO tag set failed %l",rc); goto badret; } @@ -195,10 +222,20 @@ bool gemini_init(void) ((gemini_recovery_options & GEMINI_RECOVERY_FULL) ? 1 : 0)); if(rc != 0) { - printf("CRASH_PROTECTION tag set failed %ld",rc); + 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; @@ -207,7 +244,7 @@ bool gemini_init(void) if(rc != 0) { - printf("CRASH_PROTECTION tag set failed %ld",rc); + gemini_msg(pfirstContext, "CRASH_PROTECTION tag set failed %l",rc); goto badret; } @@ -215,12 +252,20 @@ bool gemini_init(void) DSM_DB_OPENDB | DSM_DB_OPENFILE); if( rc != 0 ) { - printf("dsmUserConnect failed rc = %ld\n",rc); + /* 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: @@ -231,30 +276,40 @@ badret: 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"); - rc = pthread_create (&hThread, 0, gemini_watchdog, (void *)pContext); + + (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) { - printf("Can't create gemini watchdog thread"); + gemini_msg(pContext, "Can't Create gemini watchdog thread"); goto done; } if(!gemini_io_threads) goto done; - rc = pthread_create(&hThread, 0, gemini_rl_writer, (void *)pContext); + rc = pthread_create(&hThread, &thr_attr, gemini_rl_writer, (void *)pContext); if(rc) { - printf("Can't create gemini recovery log writer thread"); + gemini_msg(pContext, "Can't create Gemini recovery log writer thread"); goto done; } - for( int i = gemini_io_threads - 1;i;i--) + for(i = gemini_io_threads - 1;i;i--) { - rc = pthread_create(&hThread, 0, gemini_apw, (void *)pContext); + rc = pthread_create(&hThread, &thr_attr, gemini_apw, (void *)pContext); if(rc) { - printf("Can't create gemini page writer thread"); + gemini_msg(pContext, "Can't create Gemini database page writer thread"); goto done; } } @@ -273,7 +328,7 @@ pthread_handler_decl(gemini_watchdog,arg ) rc = dsmContextCopy(pcontext,&pmyContext, DSMCONTEXTDB); if( rc != 0 ) { - printf("dsmContextCopy failed for watchdog %d\n",rc); + gemini_msg(pcontext, "dsmContextCopy failed for Gemini watchdog %d",rc); return 0; } @@ -281,7 +336,7 @@ pthread_handler_decl(gemini_watchdog,arg ) if( rc != 0 ) { - printf("dsmUserConnect failed for watchdog %d\n",rc); + gemini_msg(pcontext, "dsmUserConnect failed for Gemini watchdog %d",rc); return 0; } @@ -311,7 +366,7 @@ pthread_handler_decl(gemini_rl_writer,arg ) rc = dsmContextCopy(pcontext,&pmyContext, DSMCONTEXTDB); if( rc != 0 ) { - printf("dsmContextCopy failed for recovery log writer %d\n",rc); + gemini_msg(pcontext, "dsmContextCopy failed for Gemini recovery log writer %d",rc); return 0; } @@ -319,7 +374,7 @@ pthread_handler_decl(gemini_rl_writer,arg ) if( rc != 0 ) { - printf("dsmUserConnect failed for recovery log writer %d\n",rc); + gemini_msg(pcontext, "dsmUserConnect failed for Gemini recovery log writer %d",rc); return 0; } @@ -348,7 +403,7 @@ pthread_handler_decl(gemini_apw,arg ) rc = dsmContextCopy(pcontext,&pmyContext, DSMCONTEXTDB); if( rc != 0 ) { - printf("dsmContextCopy failed for gemini page writer %d\n",rc); + gemini_msg(pcontext, "dsmContextCopy failed for Gemini page writer %d",rc); my_thread_end(); return 0; } @@ -356,7 +411,7 @@ pthread_handler_decl(gemini_apw,arg ) if( rc != 0 ) { - printf("dsmUserConnect failed for gemini page writer %d\n",rc); + gemini_msg(pcontext, "dsmUserConnect failed for Gemini page writer %d",rc); my_thread_end(); return 0; } @@ -388,7 +443,7 @@ int gemini_set_option_long(int optid, long optval) } if (rc) { - printf("SPIN_AMOUNT tag set failed %ld",rc); + gemini_msg(pfirstContext, "SPIN_AMOUNT tag set failed %l",rc); } else { @@ -410,7 +465,7 @@ static int gemini_connect(THD *thd) DSMCONTEXTDB); if( rc != 0 ) { - printf("dsmContextCopy failed %ld\n",rc); + gemini_msg(pfirstContext, "dsmContextCopy failed %l",rc); return(rc); } @@ -418,7 +473,7 @@ static int gemini_connect(THD *thd) if( rc != 0 ) { - printf("dsmUserConnect failed %ld\n",rc); + gemini_msg(pfirstContext, "dsmUserConnect failed %l",rc); return(rc); } @@ -444,6 +499,9 @@ bool gemini_end(void) THD *thd; DBUG_ENTER("gemini_end"); + + hash_free(&gem_open_tables); + pthread_mutex_destroy(&gem_mutex); if(pfirstContext) { rc = dsmShutdownSet(pfirstContext, DSM_SHUTDOWN_NORMAL); @@ -534,6 +592,24 @@ int gemini_rollback_to_savepoint(THD *thd) 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 ) @@ -599,8 +675,13 @@ int ha_gemini::open(const char *name, int mode, uint test_if_locked) DBUG_ENTER("ha_gemini::open"); thd = current_thd; - thr_lock_init(&alock); - thr_lock_data_init(&alock,&lock,(void*)0); + /* 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) @@ -610,7 +691,7 @@ int ha_gemini::open(const char *name, int mode, uint test_if_locked) if(rc) return rc; } - if (!(rec_buff=my_malloc(table->rec_buff_length, + if (!(rec_buff=(byte*)my_malloc(table->rec_buff_length, MYF(MY_WME)))) { DBUG_RETURN(1); @@ -635,6 +716,12 @@ int ha_gemini::open(const char *name, int mode, uint test_if_locked) 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; @@ -649,8 +736,33 @@ int ha_gemini::open(const char *name, int mode, uint test_if_locked) crashed while being in the midst of a repair operation */ rc = dsmTableStatus((dsmContext_t *)thd->gemini.context, tableNumber,&tableStatus); - if(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); } @@ -680,6 +792,12 @@ int ha_gemini::index_open(char *tableName) 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; } } @@ -692,12 +810,22 @@ int ha_gemini::index_open(char *tableName) int ha_gemini::close(void) { DBUG_ENTER("ha_gemini::close"); - thr_lock_delete(&alock); - my_free(rec_buff,MYF(MY_ALLOW_ZERO_PTR)); + my_free((char*)rec_buff,MYF(MY_ALLOW_ZERO_PTR)); rec_buff = 0; my_free((char *)pindexNumbers,MYF(MY_ALLOW_ZERO_PTR)); pindexNumbers = 0; - DBUG_RETURN(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)); } @@ -709,7 +837,7 @@ int ha_gemini::write_row(byte * record) DBUG_ENTER("write_row"); - if(tableStatus) + if(tableStatus == HA_ERR_CRASHED) DBUG_RETURN(tableStatus); thd = current_thd; @@ -737,10 +865,11 @@ int ha_gemini::write_row(byte * record) /* 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); + tableNumber, (ULONG64 *)&nr,1); if(thd->next_insert_id > nr) { - error = dsmTableAutoIncrementSet((dsmContext_t *)thd->gemini.context,tableNumber, + error = dsmTableAutoIncrementSet((dsmContext_t *)thd->gemini.context, + tableNumber, (ULONG64)thd->next_insert_id); } } @@ -749,11 +878,13 @@ int ha_gemini::write_row(byte * record) } dsmRecord.table = tableNumber; - dsmRecord.maxLength = table->reclength; + dsmRecord.maxLength = table->rec_buff_length; if ((error=pack_row((byte **)&dsmRecord.pbuffer, (int *)&dsmRecord.recLength, - record))) + record, FALSE))) + { DBUG_RETURN(error); + } error = dsmRecordCreate((dsmContext_t *)thd->gemini.context, &dsmRecord,0); @@ -769,6 +900,8 @@ int ha_gemini::write_row(byte * record) thd->gemini.needSavepoint = 1; } } + if(error == DSM_S_RQSTREJ) + error = HA_ERR_LOCK_WAIT_TIMEOUT; DBUG_RETURN(error); } @@ -777,10 +910,17 @@ 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); + tableNumber, (ULONG64 *)&nr, + update); return nr; } @@ -828,8 +968,8 @@ int ha_gemini::handleIndexEntry(const byte * record, dsmRecid_t recid, 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.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) @@ -880,6 +1020,7 @@ int ha_gemini::createKeyString(const byte * record, KEY *pkeyinfo, int componentLen; int fieldType; int isNull; + uint key_part_length; KEY_PART_INFO *key_part; @@ -892,21 +1033,35 @@ int ha_gemini::createKeyString(const byte * record, KEY *pkeyinfo, unsigned char *pos; key_part = pkeyinfo->key_part + i; + key_part_length = key_part->length; fieldType = gemDataType(key_part->field->type()); - if(fieldType == GEM_CHAR) + 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(rec_buff, key_part->length); + key_part->field->sort_string((char*)rec_buff, key_part->length); key_part->field->ptr = ptr; pos = (unsigned char *)rec_buff; - } - else - { + } + 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; @@ -914,7 +1069,7 @@ int ha_gemini::createKeyString(const byte * record, KEY *pkeyinfo, *thereIsAnull = true; rc = gemFieldToIdxComponent(pos, - (unsigned long) key_part->length, + (unsigned long) key_part_length, fieldType, isNull , key_part->field->flags & UNSIGNED_FLAG, @@ -951,7 +1106,7 @@ int ha_gemini::update_row(const byte * old_record, byte * new_record) } for (uint keynr=0 ; keynr < table->keys ; keynr++) { - if(key_cmp(keynr,old_record, new_record)) + if(key_cmp(keynr,old_record, new_record,false)) { error = handleIndexEntry(old_record,lastRowid,KEY_DELETE,keynr); if(error) @@ -973,10 +1128,10 @@ int ha_gemini::update_row(const byte * old_record, byte * new_record) dsmRecord.table = tableNumber; dsmRecord.recid = lastRowid; - dsmRecord.maxLength = table->reclength; + dsmRecord.maxLength = table->rec_buff_length; if ((error=pack_row((byte **)&dsmRecord.pbuffer, (int *)&dsmRecord.recLength, - new_record))) + new_record, TRUE))) { DBUG_RETURN(error); } @@ -992,6 +1147,7 @@ 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); @@ -999,9 +1155,7 @@ int ha_gemini::delete_row(const byte * record) if(thd->gemini.needSavepoint) { thd->gemini.savepoint++; - error = dsmTransaction((dsmContext_t *)thd->gemini.context, - &thd->gemini.savepoint, - DSMTXN_SAVE, 0, 0); + error = dsmTransaction(pcontext, &thd->gemini.savepoint, DSMTXN_SAVE, 0, 0); if (error) DBUG_RETURN(error); thd->gemini.needSavepoint = 0; @@ -1013,8 +1167,27 @@ int ha_gemini::delete_row(const byte * record) error = handleIndexEntries(record, dsmRecord.recid,KEY_DELETE); if(!error) { - error = dsmRecordDelete((dsmContext_t *)thd->gemini.context, - &dsmRecord, 0, NULL); + 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); @@ -1023,7 +1196,6 @@ int ha_gemini::delete_row(const byte * record) int ha_gemini::index_init(uint keynr) { int error = 0; - int keyStringLen; THD *thd; DBUG_ENTER("index_init"); thd = current_thd; @@ -1046,19 +1218,9 @@ int ha_gemini::index_init(uint keynr) } 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; + pbracketLimit->keycomps = pbracketBase->keycomps = 1; pfoundKey = (dsmKey_t *)my_malloc(sizeof(dsmKey_t) + keyBufSize,MYF(MY_WME)); if(!pfoundKey) @@ -1130,6 +1292,7 @@ int ha_gemini::pack_key( uint keynr, dsmKey_t *pkey, { uint offset=0; unsigned char *pos; + uint key_part_length = key_part->length; int fieldType; if (key_part->null_bit) @@ -1141,7 +1304,7 @@ int ha_gemini::pack_key( uint keynr, dsmKey_t *pkey, key_ptr+= key_part->store_length; rc = gemFieldToIdxComponent( (unsigned char *)key_ptr + offset, - (unsigned long) key_part->length, + (unsigned long) key_part_length, 0, 1 , /* Tells it to build a null component */ key_part->field->flags & UNSIGNED_FLAG, @@ -1153,20 +1316,31 @@ int ha_gemini::pack_key( uint keynr, dsmKey_t *pkey, } } fieldType = gemDataType(key_part->field->type()); - if(fieldType == GEM_CHAR) + switch (fieldType) { - key_part->field->store(key_ptr + offset, key_part->length); - key_part->field->sort_string(rec_buff, key_part->length); + 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; - } - else - { + 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, + (unsigned long) key_part_length, fieldType, 0 , key_part->field->flags & UNSIGNED_FLAG, @@ -1189,7 +1363,7 @@ void ha_gemini::unpack_key(char *record, dsmKey_t *key, uint index) int fieldIsNull, fieldType; int rc = 0; - char unsigned *pos= &key->keystr[7]; + char unsigned *pos= &key->keystr[FULLKEYHDRSZ+4/* 4 for the index number*/]; for ( ; key_part != end; key_part++) { @@ -1202,7 +1376,8 @@ void ha_gemini::unpack_key(char *record, dsmKey_t *key, uint index) } rc = gemIdxComponentToField(pos, fieldType, (unsigned char *)record + key_part->field->offset(), - key_part->field->field_length, + //key_part->field->field_length, + key_part->length, key_part->field->decimals(), &fieldIsNull); if(fieldIsNull) @@ -1266,12 +1441,12 @@ int ha_gemini::index_read(byte * buf, const byte * key, pbracketLimit->keyLen = componentLen; } - /* We have to subtract three here since cxKeyPrepare + /* 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 -= 3; - pbracketLimit->keyLen -= 3; + pbracketBase->keyLen -= FULLKEYHDRSZ; + pbracketLimit->keyLen -= FULLKEYHDRSZ; thd = current_thd; @@ -1294,7 +1469,7 @@ int ha_gemini::index_next(byte * buf) dsmMask_t findMode; DBUG_ENTER("index_next"); - if(tableStatus) + if(tableStatus == HA_ERR_CRASHED) DBUG_RETURN(tableStatus); thd = current_thd; @@ -1304,9 +1479,12 @@ int ha_gemini::index_next(byte * buf) error = gemKeyLow(pbracketBase->keystr, &keyStringLen, pbracketLimit->index); - pbracketBase->keyLen = (COUNT)keyStringLen - 3; + pbracketBase->keyLen = (COUNT)keyStringLen - FULLKEYHDRSZ; pbracketBase->index = pbracketLimit->index; - pbracketBase->keycomps = 1; + error = gemKeyHigh(pbracketLimit->keystr, &keyStringLen, + pbracketLimit->index); + pbracketLimit->keyLen = (COUNT)keyStringLen - FULLKEYHDRSZ; + findMode = DSMFINDFIRST; } else @@ -1369,24 +1547,20 @@ int ha_gemini::index_last(byte * buf) error = gemKeyLow(pbracketBase->keystr, &keyStringLen, pbracketLimit->index); - if(error) - goto errorReturn; - pbracketBase->keyLen = (COUNT)keyStringLen - 3; + pbracketBase->keyLen = (COUNT)keyStringLen - FULLKEYHDRSZ; pbracketBase->index = pbracketLimit->index; - pbracketBase->keycomps = 1; + error = gemKeyHigh(pbracketLimit->keystr, &keyStringLen, + pbracketLimit->index); + pbracketLimit->keyLen = (COUNT)keyStringLen - FULLKEYHDRSZ; 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) @@ -1414,7 +1588,7 @@ int ha_gemini::rnd_next(byte *buf) DBUG_ENTER("rnd_next"); - if(tableStatus) + if(tableStatus == HA_ERR_CRASHED) DBUG_RETURN(tableStatus); thd = current_thd; @@ -1429,7 +1603,7 @@ int ha_gemini::rnd_next(byte *buf) dsmRecord.recid = lastRowid; dsmRecord.pbuffer = (dsmBuffer_t *)rec_buff; dsmRecord.recLength = table->reclength; - dsmRecord.maxLength = table->reclength; + dsmRecord.maxLength = table->rec_buff_length; error = dsmTableScan((dsmContext_t *)thd->gemini.context, &dsmRecord, DSMFINDNEXT, lockMode, 0); @@ -1437,17 +1611,23 @@ int ha_gemini::rnd_next(byte *buf) if(!error) { lastRowid = dsmRecord.recid; - unpack_row((char *)buf,(char *)dsmRecord.pbuffer); + error = 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; - + 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); } @@ -1500,14 +1680,14 @@ int ha_gemini::fetch_row(void *gemini_context,const byte *buf) dsmRecord.recid = lastRowid; dsmRecord.pbuffer = (dsmBuffer_t *)rec_buff; dsmRecord.recLength = table->reclength; - dsmRecord.maxLength = table->reclength; + dsmRecord.maxLength = table->rec_buff_length; rc = dsmRecordGet((dsmContext_t *)gemini_context, &dsmRecord, 0); if(!rc) { - unpack_row((char *)buf,(char *)dsmRecord.pbuffer); + rc = unpack_row((char *)buf,(char *)dsmRecord.pbuffer); } DBUG_RETURN(rc); @@ -1544,7 +1724,7 @@ int ha_gemini::findRow(THD *thd, dsmMask_t findMode, byte *buf) if(key_read) { - unpack_key(buf, pkey, active_index); + unpack_key((char*)buf, pkey, active_index); } if(!key_read) /* unpack_key may have turned off key_read */ { @@ -1554,10 +1734,17 @@ int ha_gemini::findRow(THD *thd, dsmMask_t findMode, byte *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; + 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); } @@ -1578,25 +1765,47 @@ void ha_gemini::info(uint flag) 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; } - else if ((flag & HA_STATUS_CONST)) + 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; } - else if ((flag & HA_STATUS_ERRKEY)) + if ((flag & HA_STATUS_ERRKEY)) { errkey=last_dup_key; } - else if ((flag & HA_STATUS_TIME)) + if ((flag & HA_STATUS_TIME)) { ; } - else if ((flag & HA_STATUS_AUTO)) + 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; @@ -1658,7 +1867,22 @@ int ha_gemini::external_lock(THD *thd, int lock_type) 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 */ @@ -1678,6 +1902,8 @@ int ha_gemini::external_lock(THD *thd, int lock_type) 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 */ @@ -1703,18 +1929,24 @@ THR_LOCK_DATA **ha_gemini::store_lock(THD *thd, THR_LOCK_DATA **to, !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; } + 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) @@ -1777,7 +2009,7 @@ int ha_gemini::create(const char *name, register TABLE *form, (dsmText_t *)"gemini_data_area"); if( rc != 0 ) { - printf("dsmAreaNew failed %ld\n",rc); + gemini_msg(pcontext, "dsmAreaNew failed %l",rc); return(rc); } @@ -1787,7 +2019,7 @@ int ha_gemini::create(const char *name, register TABLE *form, (dsmText_t *)&name_buff[start_of_name]); if( rc != 0 ) { - printf("dsmExtentCreate failed %ld\n",rc); + gemini_msg(pcontext, "dsmExtentCreate failed %l",rc); return(rc); } @@ -1805,6 +2037,20 @@ int ha_gemini::create(const char *name, register TABLE *form, (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); @@ -1814,7 +2060,7 @@ int ha_gemini::create(const char *name, register TABLE *form, (dsmText_t *)"gemini_index_area"); if( rc != 0 ) { - printf("dsmAreaNew failed %ld\n",rc); + gemini_msg(pcontext, "dsmAreaNew failed %l",rc); return(rc); } /* Create an extent */ @@ -1823,7 +2069,7 @@ int ha_gemini::create(const char *name, register TABLE *form, (dsmText_t *)&name_buff[start_of_name]); if( rc != 0 ) { - printf("dsmExtentCreate failed %ld\n",rc); + gemini_msg(pcontext, "dsmExtentCreate failed %l",rc); return(rc); } @@ -1859,10 +2105,11 @@ int ha_gemini::create(const char *name, register TABLE *form, } } - rc = dsmTableAutoIncrementSet(pcontext,tableNumber, - create_info->auto_increment_value); - - + /* 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 @@ -1950,26 +2197,25 @@ int ha_gemini::delete_table(const char *pname) (dsmObject_t *)&tableNum); if (rc) { - printf("Cound not find table number for %s with string %s, %ld\n", - pname,name_buff,rc); + gemini_msg(pcontext, "Unable to find table number for %s", name_buff); rc = gemini_rollback(thd); if (rc) { - printf("Error in rollback %ld\n",rc); + gemini_msg(pcontext, "Error in rollback %l",rc); } DBUG_RETURN(rc); } - rc = dsmObjectInfo(pcontext, tableNum, DSMOBJECT_MIXTABLE, &tableArea, - &objectAttr, &associate, &associateType, &block, &root); + rc = dsmObjectInfo(pcontext, tableNum, DSMOBJECT_MIXTABLE, tableNum, + &tableArea, &objectAttr, &associateType, &block, &root); if (rc) { - printf("Failed to get area number for table %d, %s, return %ld\n", + gemini_msg(pcontext, "Failed to get area number for table %d, %s, return %l", tableNum, pname, rc); rc = gemini_rollback(thd); if (rc) { - printf("Error in rollback %ld\n",rc); + gemini_msg(pcontext, "Error in rollback %l",rc); } } @@ -1979,14 +2225,14 @@ int ha_gemini::delete_table(const char *pname) rc = dsmObjectDeleteAssociate(pcontext, tableNum, &indexArea); if (rc) { - printf("Error deleting storage objects for table number %d, return %ld\n", + 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) { - printf("Error in rollback %ld\n",rc); + gemini_msg(pcontext, "Error in rollback %l",rc); } DBUG_RETURN(rc); } @@ -1994,33 +2240,33 @@ int ha_gemini::delete_table(const char *pname) if (indexArea != DSMAREA_INVALID) { /* Delete the extents for both Index and Table */ - rc = dsmExtentDelete(pcontext, indexArea, 0); + rc = dsmExtentDelete(pcontext, indexArea); rc = dsmAreaDelete(pcontext, indexArea); if (rc) { - printf("Error deleting Index Area %ld, return %ld\n", indexArea, rc); + gemini_msg(pcontext, "Error deleting Index Area %l, return %l", indexArea, rc); /* roll back txn and return */ rc = gemini_rollback(thd); if (rc) { - printf("Error in rollback %ld\n",rc); + gemini_msg(pcontext, "Error in rollback %l",rc); } DBUG_RETURN(rc); } } - rc = dsmExtentDelete(pcontext, tableArea, 0); + rc = dsmExtentDelete(pcontext, tableArea); rc = dsmAreaDelete(pcontext, tableArea); if (rc) { - printf("Error deleting table Area %ld, name %s, return %ld\n", + 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) { - printf("Error in rollback %ld\n",rc); + gemini_msg(pcontext, "Error in rollback %l",rc); } DBUG_RETURN(rc); } @@ -2030,7 +2276,7 @@ int ha_gemini::delete_table(const char *pname) rc = gemini_commit(thd); if (rc) { - printf("Failed to commit transaction %ld\n",rc); + gemini_msg(pcontext, "Failed to commit transaction %l",rc); } @@ -2047,7 +2293,6 @@ 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]; @@ -2056,6 +2301,7 @@ int ha_gemini::rename_table(const char *pfrom, const char *pto) unsigned i, nameLen; dsmObject_t tableNum; dsmArea_t indexArea = 0; + dsmArea_t tableArea = 0; DBUG_ENTER("ha_gemini::rename_table"); @@ -2068,7 +2314,7 @@ int ha_gemini::rename_table(const char *pfrom, const char *pto) { if (gemini_is_vst(name_buff)) { - return 0; + return DSM_S_CANT_RENAME_VST; } } } @@ -2113,21 +2359,51 @@ int ha_gemini::rename_table(const char *pfrom, const char *pto) 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); + &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); + } + } + } - /* 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); + 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: @@ -2143,17 +2419,38 @@ errorReturn: double ha_gemini::scan_time() { - return records / (gemini_blocksize / table->reclength); + return (double)records / + (double)((gemini_blocksize / (double)table->reclength)); } -int ha_gemini::check(THD* thd, HA_CHECK_OPT* check_opt) +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; + 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) { @@ -2167,23 +2464,33 @@ int ha_gemini::check(THD* thd, HA_CHECK_OPT* check_opt) 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)); + 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 */ - error = dsmObjectLock((dsmContext_t *)thd->gemini.context, - (dsmObject_t)tableNumber, - DSMOBJECT_TABLE,0, - DSM_LK_SHARE, 1, 0); + 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; + } - info(HA_STATUS_VARIABLE); - + 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; + 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) @@ -2195,8 +2502,12 @@ int ha_gemini::check(THD* thd, HA_CHECK_OPT* check_opt) error = fetch_row(thd->gemini.context,buf); if(!error) { - if(key_cmp(i,buf,indexBuf)) + 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); @@ -2209,6 +2520,9 @@ int ha_gemini::check(THD* thd, HA_CHECK_OPT* check_opt) { 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); @@ -2218,10 +2532,27 @@ int ha_gemini::check(THD* thd, HA_CHECK_OPT* check_opt) } } } + + 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 */ @@ -2231,6 +2562,10 @@ int ha_gemini::check(THD* thd, HA_CHECK_OPT* check_opt) /* 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, @@ -2248,23 +2583,61 @@ int ha_gemini::check(THD* thd, HA_CHECK_OPT* check_opt) { /* Now scan the table and for each row generate the keys and find them in the index */ - error = fullCheck(thd, buf);\ + error = fullCheck(thd, buf); if(error) checkStatus = error; } + // Store the key distribution information + error = saveKeyStats(thd); error_return: - my_free(buf,MYF(MY_ALLOW_ZERO_PTR)); + 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; - error = dsmObjectUnlock((dsmContext_t *)thd->gemini.context, - (dsmObject_t)tableNumber, - DSMOBJECT_TABLE,0, - DSM_LK_SHARE,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; @@ -2319,7 +2692,12 @@ int ha_gemini::repair(THD* thd, HA_CHECK_OPT* check_opt) &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; } @@ -2330,7 +2708,11 @@ int ha_gemini::repair(THD* thd, HA_CHECK_OPT* check_opt) 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); @@ -2338,13 +2720,18 @@ int ha_gemini::repair(THD* thd, HA_CHECK_OPT* check_opt) 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 = my_malloc(table->rec_buff_length,MYF(MY_WME)); + 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->reclength; + dsmRecord.maxLength = table->rec_buff_length; while(!error) { error = dsmTableScan((dsmContext_t *)thd->gemini.context, @@ -2352,13 +2739,15 @@ int ha_gemini::repair(THD* thd, HA_CHECK_OPT* check_opt) 1); if(!error) { - unpack_row((char *)buf,(char *)dsmRecord.pbuffer); - error = handleIndexEntries(buf,dsmRecord.recid,KEY_CREATE); - if(error == HA_ERR_FOUND_DUPP_KEY) + if (!(error = unpack_row((char *)buf,(char *)dsmRecord.pbuffer))) { - /* 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 = 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; + } } } } @@ -2366,7 +2755,13 @@ int ha_gemini::repair(THD* thd, HA_CHECK_OPT* check_opt) (dsmObject_t)tableNumber, DSMOBJECT_TABLE,0, DSM_LK_EXCL,0); - my_free(buf,MYF(MY_ALLOW_ZERO_PTR)); + 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); @@ -2374,6 +2769,313 @@ int ha_gemini::repair(THD* thd, HA_CHECK_OPT* check_opt) 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(¶m); + param.thd = thd; + param.op_name = (char*)"restore"; + param.table_name = table->table_name; + param.testflag = 0; + mi_check_print_error(¶m,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(¶m); + param.thd = thd; + param.op_name = (char*)"backup"; + param.table_name = table->table_name; + param.testflag = 0; + mi_check_print_error(¶m,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, @@ -2412,7 +3114,7 @@ ha_rows ha_gemini::records_in_range(int keynr, pbracketBase->keyLen = componentLen; } - pbracketBase->keyLen -= 3; + pbracketBase->keyLen -= FULLKEYHDRSZ; if(end_key) { @@ -2431,9 +3133,10 @@ ha_rows ha_gemini::records_in_range(int keynr, pbracketLimit->keyLen = componentLen; } - pbracketLimit->keyLen -= 3; + pbracketLimit->keyLen -= FULLKEYHDRSZ; error = dsmIndexRowsInRange((dsmContext_t *)current_thd->gemini.context, pbracketBase,pbracketLimit, + tableNumber, &pctInrange); if(pctInrange >= 1) rows = (ha_rows)pctInrange; @@ -2457,32 +3160,82 @@ ha_rows ha_gemini::records_in_range(int keynr, 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) +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; } - 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()); + { +#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; } -void ha_gemini::unpack_row(char *record, char *prow) +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. @@ -2568,38 +3321,129 @@ void ha_gemini::unpack_row(char *record, char *prow) 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); + { +#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) + 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 ( ; key_part != end ; key_part++) + 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(old_row + key_part->offset, - new_row + key_part->offset, + 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; @@ -2612,13 +3456,13 @@ int gemini_parse_table_name(const char *fullname, char *dbname, char *tabname) /* separate out the name of the table and the database */ - namestart = strchr(fullname + start_of_name, '/'); + namestart = (char *)strchr(fullname + start_of_name, '/'); if (!namestart) { /* if on Windows, slashes go the other way */ - namestart = strchr(fullname + start_of_name, '\\'); + namestart = (char *)strchr(fullname + start_of_name, '\\'); } - nameend = strchr(fullname + start_of_name, '.'); + nameend = (char *)strchr(fullname + start_of_name, '.'); /* sometimes fullname has an extension, sometimes it doesn't */ if (!nameend) { @@ -2680,4 +3524,105 @@ static void print_msg(THD *thd, const char *table_name, const char *op_name, 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 */ |