diff options
Diffstat (limited to 'storage/pbxt/src/myxt_xt.cc')
-rw-r--r-- | storage/pbxt/src/myxt_xt.cc | 3434 |
1 files changed, 3434 insertions, 0 deletions
diff --git a/storage/pbxt/src/myxt_xt.cc b/storage/pbxt/src/myxt_xt.cc new file mode 100644 index 00000000000..410bf2d2f3c --- /dev/null +++ b/storage/pbxt/src/myxt_xt.cc @@ -0,0 +1,3434 @@ +/* Copyright (c) 2005 PrimeBase Technologies GmbH + * + * PrimeBase XT + * + * 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 + * + * 2006-05-16 Paul McCullagh + * + * H&G2JCtL + * + * These functions implement the parts of PBXT which must conform to the + * key and row format used by MySQL. + */ + +#include "xt_config.h" + +#ifdef DRIZZLED +#include <drizzled/server_includes.h> +#include <drizzled/plugin.h> +#include <drizzled/show.h> +#include <drizzled/field/blob.h> +#include <drizzled/field/enum.h> +#include <drizzled/field/varstring.h> +#include <drizzled/current_session.h> +#include <drizzled/sql_lex.h> +#include <drizzled/session.h> +//extern "C" struct charset_info_st *session_charset(Session *session); +extern pthread_key_t THR_Session; +#else +#include "mysql_priv.h" +#include <mysql/plugin.h> +#endif + +#ifdef HAVE_ISNAN +#include <math.h> +#endif + +#include "ha_pbxt.h" + +#include "myxt_xt.h" +#include "strutil_xt.h" +#include "database_xt.h" +#include "cache_xt.h" +#include "datalog_xt.h" +#include "memory_xt.h" + +static void myxt_bitmap_init(XTThreadPtr self, MX_BITMAP *map, u_int n_bits); +static void myxt_bitmap_free(XTThreadPtr self, MX_BITMAP *map); + +#ifdef DRIZZLED +#define swap_variables(TYPE, a, b) \ + do { \ + TYPE dummy; \ + dummy= a; \ + a= b; \ + b= dummy; \ + } while (0) + + +#define CMP_NUM(a,b) (((a) < (b)) ? -1 : ((a) == (b)) ? 0 : 1) +#else +#define get_rec_bits(bit_ptr, bit_ofs, bit_len) \ + (((((uint16) (bit_ptr)[1] << 8) | (uint16) (bit_ptr)[0]) >> (bit_ofs)) & \ + ((1 << (bit_len)) - 1)) +#endif + +#define FIX_LENGTH(cs, pos, length, char_length) \ + do { \ + if ((length) > char_length) \ + char_length= my_charpos(cs, pos, pos+length, char_length); \ + set_if_smaller(char_length,length); \ + } while(0) + +#ifdef store_key_length_inc +#undef store_key_length_inc +#endif +#define store_key_length_inc(key,length) \ +{ if ((length) < 255) \ + { *(key)++=(length); } \ + else \ + { *(key)=255; mi_int2store((key)+1,(length)); (key)+=3; } \ +} + +#define set_rec_bits(bits, bit_ptr, bit_ofs, bit_len) \ +{ \ + (bit_ptr)[0]= ((bit_ptr)[0] & ~(((1 << (bit_len)) - 1) << (bit_ofs))) | \ + ((bits) << (bit_ofs)); \ + if ((bit_ofs) + (bit_len) > 8) \ + (bit_ptr)[1]= ((bit_ptr)[1] & ~((1 << ((bit_len) - 8 + (bit_ofs))) - 1)) | \ + ((bits) >> (8 - (bit_ofs))); \ +} + +#define clr_rec_bits(bit_ptr, bit_ofs, bit_len) \ + set_rec_bits(0, bit_ptr, bit_ofs, bit_len) + +static ulong my_calc_blob_length(uint length, xtWord1 *pos) +{ + switch (length) { + case 1: + return (uint) (uchar) *pos; + case 2: + return (uint) uint2korr(pos); + case 3: + return uint3korr(pos); + case 4: + return uint4korr(pos); + default: + break; + } + return 0; /* Impossible */ +} + +static void my_store_blob_length(byte *pos,uint pack_length,uint length) +{ + switch (pack_length) { + case 1: + *pos= (uchar) length; + break; + case 2: + int2store(pos,length); + break; + case 3: + int3store(pos,length); + break; + case 4: + int4store(pos,length); + default: + break; + } + return; +} + +static int my_compare_text(MX_CONST_CHARSET_INFO *charset_info, uchar *a, uint a_length, + uchar *b, uint b_length, my_bool part_key, + my_bool XT_UNUSED(skip_end_space)) +{ + if (!part_key) + /* The last parameter is diff_if_only_endspace_difference, which means + * that end spaces are not ignored. We actually always want + * to ignore end spaces! + */ + return charset_info->coll->strnncollsp(charset_info, a, a_length, + b, b_length, /*(my_bool)!skip_end_space*/0); + return charset_info->coll->strnncoll(charset_info, a, a_length, + b, b_length, part_key); +} + +/* + * ----------------------------------------------------------------------- + * Create a key + */ + +/* + * Derived from _mi_pack_key() + */ +xtPublic u_int myxt_create_key_from_key(XTIndexPtr ind, xtWord1 *key, xtWord1 *old, u_int k_length) +{ + xtWord1 *start_key = key; + XTIndexSegRec *keyseg = ind->mi_seg; + + for (u_int i=0; i<ind->mi_seg_count && (int) k_length > 0; i++, old += keyseg->length, keyseg++) + { +#ifndef DRIZZLED + enum ha_base_keytype type = (enum ha_base_keytype) keyseg->type; +#endif + u_int length = keyseg->length < k_length ? keyseg->length : k_length; + u_int char_length; + xtWord1 *pos; + MX_CONST_CHARSET_INFO *cs = keyseg->charset; + + if (keyseg->null_bit) { + k_length--; + if (!(*key++ = (xtWord1) 1 - *old++)) { /* Copy null marker */ + k_length -= length; + if (keyseg->flag & (HA_VAR_LENGTH_PART | HA_BLOB_PART)) { + k_length -= 2; /* Skip length */ + old += 2; + } + continue; /* Found NULL */ + } + } + char_length= (cs && cs->mbmaxlen > 1) ? length/cs->mbmaxlen : length; + pos = old; + if (keyseg->flag & HA_SPACE_PACK) { + uchar *end = pos + length; +#ifndef DRIZZLED + if (type != HA_KEYTYPE_NUM) { +#endif + while (end > pos && end[-1] == ' ') + end--; +#ifndef DRIZZLED + } + else { + while (pos < end && pos[0] == ' ') + pos++; + } +#endif + k_length -= length; + length = (u_int) (end-pos); + FIX_LENGTH(cs, pos, length, char_length); + store_key_length_inc(key, char_length); + memcpy((byte*) key,pos,(size_t) char_length); + key += char_length; + continue; + } + if (keyseg->flag & (HA_VAR_LENGTH_PART | HA_BLOB_PART)) { + /* Length of key-part used with mi_rkey() always 2 */ + u_int tmp_length = uint2korr(pos); + k_length -= 2 + length; + pos += 2; + set_if_smaller(length, tmp_length); /* Safety */ + FIX_LENGTH(cs, pos, length, char_length); + store_key_length_inc(key,char_length); + old +=2; /* Skip length */ + memcpy((char *) key, pos, (size_t) char_length); + key += char_length; + continue; + } + if (keyseg->flag & HA_SWAP_KEY) + { /* Numerical column */ + pos+=length; + k_length-=length; + while (length--) { + *key++ = *--pos; + } + continue; + } + FIX_LENGTH(cs, pos, length, char_length); + memcpy((byte*) key, pos, char_length); + if (length > char_length) + cs->cset->fill(cs, (char *) (key + char_length), length - char_length, ' '); + key += length; + k_length -= length; + } + + return (u_int) (key - start_key); +} + +/* Derived from _mi_make_key */ +xtPublic u_int myxt_create_key_from_row(XTIndexPtr ind, xtWord1 *key, xtWord1 *record, xtBool *no_duplicate) +{ + register XTIndexSegRec *keyseg = ind->mi_seg; + xtWord1 *pos; + xtWord1 *end; + xtWord1 *start; + +#ifdef HAVE_valgrind + if (ind->mi_fix_key) + memset((byte*) key, 0,(size_t) (ind->mi_key_size) ); +#endif + + start = key; + for (u_int i=0; i<ind->mi_seg_count; i++, keyseg++) + { + enum ha_base_keytype type = (enum ha_base_keytype) keyseg->type; + u_int length = keyseg->length; + u_int char_length; + MX_CONST_CHARSET_INFO *cs = keyseg->charset; + + if (keyseg->null_bit) { + if (record[keyseg->null_pos] & keyseg->null_bit) { + *key++ = 0; /* NULL in key */ + + /* The point is, if a key contains a NULL value + * the duplicate checking must be disabled. + * This is because a NULL value is not considered + * equal to any other value. + */ + if (no_duplicate) + *no_duplicate = FALSE; + continue; + } + *key++ = 1; /* Not NULL */ + } + + char_length= ((cs && cs->mbmaxlen > 1) ? length/cs->mbmaxlen : length); + + pos = record + keyseg->start; +#ifndef DRIZZLED + if (type == HA_KEYTYPE_BIT) + { + if (keyseg->bit_length) + { + uchar bits = get_rec_bits((uchar*) record + keyseg->bit_pos, + keyseg->bit_start, keyseg->bit_length); + *key++ = bits; + length--; + } + memcpy((byte*) key, pos, length); + key+= length; + continue; + } +#endif + if (keyseg->flag & HA_SPACE_PACK) + { + end = pos + length; +#ifndef DRIZZLED + if (type != HA_KEYTYPE_NUM) { +#endif + while (end > pos && end[-1] == ' ') + end--; +#ifndef DRIZZLED + } + else { + while (pos < end && pos[0] == ' ') + pos++; + } +#endif + length = (u_int) (end-pos); + FIX_LENGTH(cs, pos, length, char_length); + store_key_length_inc(key,char_length); + memcpy((byte*) key,(byte*) pos,(size_t) char_length); + key += char_length; + continue; + } + if (keyseg->flag & HA_VAR_LENGTH_PART) { + uint pack_length= (keyseg->bit_start == 1 ? 1 : 2); + uint tmp_length= (pack_length == 1 ? (uint) *(uchar*) pos : + uint2korr(pos)); + pos += pack_length; /* Skip VARCHAR length */ + set_if_smaller(length,tmp_length); + FIX_LENGTH(cs, pos, length, char_length); + store_key_length_inc(key,char_length); + memcpy((byte*) key,(byte*) pos,(size_t) char_length); + key += char_length; + continue; + } + if (keyseg->flag & HA_BLOB_PART) + { + u_int tmp_length = my_calc_blob_length(keyseg->bit_start, pos); + memcpy((byte*) &pos,pos+keyseg->bit_start,sizeof(char*)); + set_if_smaller(length,tmp_length); + FIX_LENGTH(cs, pos, length, char_length); + store_key_length_inc(key,char_length); + memcpy((byte*) key,(byte*) pos,(size_t) char_length); + key+= char_length; + continue; + } + if (keyseg->flag & HA_SWAP_KEY) + { /* Numerical column */ +#ifdef HAVE_ISNAN +#ifndef DRIZZLED + if (type == HA_KEYTYPE_FLOAT) + { + float nr; + float4get(nr,pos); + if (isnan(nr)) + { + /* Replace NAN with zero */ + bzero(key,length); + key+=length; + continue; + } + } + else +#endif + if (type == HA_KEYTYPE_DOUBLE) { + double nr; + + float8get(nr,pos); + if (isnan(nr)) { + bzero(key,length); + key+=length; + continue; + } + } +#endif + pos+=length; + while (length--) { + *key++ = *--pos; + } + continue; + } + FIX_LENGTH(cs, pos, length, char_length); + memcpy((byte*) key, pos, char_length); + if (length > char_length) + cs->cset->fill(cs, (char *) key + char_length, length - char_length, ' '); + key += length; + } + + return ind->mi_fix_key ? ind->mi_key_size : (u_int) (key - start); /* Return keylength */ +} + +xtPublic u_int myxt_create_foreign_key_from_row(XTIndexPtr ind, xtWord1 *key, xtWord1 *record, XTIndexPtr fkey_ind, xtBool *no_null) +{ + register XTIndexSegRec *keyseg = ind->mi_seg; + register XTIndexSegRec *fkey_keyseg = fkey_ind->mi_seg; + xtWord1 *pos; + xtWord1 *end; + xtWord1 *start; + + start = key; + for (u_int i=0; i<ind->mi_seg_count; i++, keyseg++, fkey_keyseg++) + { + enum ha_base_keytype type = (enum ha_base_keytype) keyseg->type; + u_int length = keyseg->length; + u_int char_length; + MX_CONST_CHARSET_INFO *cs = keyseg->charset; + xtBool is_null = FALSE; + + if (keyseg->null_bit) { + if (record[keyseg->null_pos] & keyseg->null_bit) { + is_null = TRUE; + if (no_null) + *no_null = FALSE; + } + } + + if (fkey_keyseg->null_bit) { + if (is_null) { + *key++ = 0; /* NULL in key */ + + /* The point is, if a key contains a NULL value + * the duplicate checking must be disabled. + * This is because a NULL value is not considered + * equal to any other value. + */ + continue; + } + *key++ = 1; /* Not NULL */ + } + + char_length= ((cs && cs->mbmaxlen > 1) ? length/cs->mbmaxlen : length); + + pos = record + keyseg->start; +#ifndef DRIZZLED + if (type == HA_KEYTYPE_BIT) + { + if (keyseg->bit_length) + { + uchar bits = get_rec_bits((uchar*) record + keyseg->bit_pos, + keyseg->bit_start, keyseg->bit_length); + *key++ = bits; + length--; + } + memcpy((byte*) key, pos, length); + key+= length; + continue; + } +#endif + if (keyseg->flag & HA_SPACE_PACK) + { + end = pos + length; +#ifndef DRIZZLED + if (type != HA_KEYTYPE_NUM) { +#endif + while (end > pos && end[-1] == ' ') + end--; +#ifndef DRIZZLED + } + else { + while (pos < end && pos[0] == ' ') + pos++; + } +#endif + length = (u_int) (end-pos); + FIX_LENGTH(cs, pos, length, char_length); + store_key_length_inc(key,char_length); + memcpy((byte*) key,(byte*) pos,(size_t) char_length); + key += char_length; + continue; + } + if (keyseg->flag & HA_VAR_LENGTH_PART) { + uint pack_length= (keyseg->bit_start == 1 ? 1 : 2); + uint tmp_length= (pack_length == 1 ? (uint) *(uchar*) pos : + uint2korr(pos)); + pos += pack_length; /* Skip VARCHAR length */ + set_if_smaller(length,tmp_length); + FIX_LENGTH(cs, pos, length, char_length); + store_key_length_inc(key,char_length); + memcpy((byte*) key,(byte*) pos,(size_t) char_length); + key += char_length; + continue; + } + if (keyseg->flag & HA_BLOB_PART) + { + u_int tmp_length = my_calc_blob_length(keyseg->bit_start, pos); + memcpy((byte*) &pos,pos+keyseg->bit_start,sizeof(char*)); + set_if_smaller(length,tmp_length); + FIX_LENGTH(cs, pos, length, char_length); + store_key_length_inc(key,char_length); + memcpy((byte*) key,(byte*) pos,(size_t) char_length); + key+= char_length; + continue; + } + if (keyseg->flag & HA_SWAP_KEY) + { /* Numerical column */ +#ifdef HAVE_ISNAN +#ifndef DRIZZLED + if (type == HA_KEYTYPE_FLOAT) + { + float nr; + float4get(nr,pos); + if (isnan(nr)) + { + /* Replace NAN with zero */ + bzero(key,length); + key+=length; + continue; + } + } + else +#endif + if (type == HA_KEYTYPE_DOUBLE) { + double nr; + + float8get(nr,pos); + if (isnan(nr)) { + bzero(key,length); + key+=length; + continue; + } + } +#endif + pos+=length; + while (length--) { + *key++ = *--pos; + } + continue; + } + FIX_LENGTH(cs, pos, length, char_length); + memcpy((byte*) key, pos, char_length); + if (length > char_length) + cs->cset->fill(cs, (char *) key + char_length, length - char_length, ' '); + key += length; + } + + return (u_int) (key - start); +} + +/* I may be overcautious here, but can I assume that + * null_ptr refers to my buffer. If I cannot, then I + * cannot use the set_notnull() method. + */ +static void mx_set_notnull_in_record(Field *field, char *record) +{ + if (field->null_ptr) + record[(uint) (field->null_ptr - (uchar *) field->table->record[0])] &= (uchar) ~field->null_bit; +} + +static xtBool mx_is_null_in_record(Field *field, char *record) +{ + if (field->null_ptr) { + if (record[(uint) (field->null_ptr - (uchar *) field->table->record[0])] & (uchar) field->null_bit) + return TRUE; + } + return FALSE; +} + +/* + * PBXT uses a completely different disk format to MySQL so I need a + * method that just returns the byte length and + * pointer to the data in a row. + */ +static char *mx_get_length_and_data(Field *field, char *dest, xtWord4 *len) +{ + char *from; + +#if MYSQL_VERSION_ID < 50114 + from = dest + field->offset(); +#else + from = dest + field->offset(field->table->record[0]); +#endif + switch (field->real_type()) { +#ifndef DRIZZLED + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: +#endif + case MYSQL_TYPE_BLOB: { + /* TODO - Check: this was the original comment: I must set + * *data to non-NULL value, *data == 0, means SQL NULL value. + */ + char *data; + + /* GOTCHA: There is no way this can work! field is shared + * between threads. + char *save = field->ptr; + + field->ptr = (char *) from; + ((Field_blob *) field)->get_ptr(&data); + field->ptr = save; // Restore org row pointer + */ + + xtWord4 packlength = ((Field_blob *) field)->pack_length() - field->table->s->blob_ptr_size; + memcpy(&data, ((char *) from)+packlength, sizeof(char*)); + + *len = ((Field_blob *) field)->get_length((byte *) from); + return data; + } +#ifndef DRIZZLED + case MYSQL_TYPE_STRING: + /* To write this function you would think Field_string::pack + * would serve as a good example, but as far as I can tell + * it has a bug: the test from[length-1] == ' ' assumes + * 1-byte chars. + * + * But this is not relevant because I believe lengthsp + * will give me the correct answer! + */ + *len = field->charset()->cset->lengthsp(field->charset(), from, field->field_length); + return from; + case MYSQL_TYPE_VAR_STRING: { + uint length=uint2korr(from); + + *len = length; + return from+HA_KEY_BLOB_LENGTH; + } +#endif + case MYSQL_TYPE_VARCHAR: { + uint length; + + if (((Field_varstring *) field)->length_bytes == 1) + length = *((unsigned char *) from); + else + length = uint2korr(from); + + *len = length; + return from+((Field_varstring *) field)->length_bytes; + } +#ifndef DRIZZLED + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_TINY: + case MYSQL_TYPE_SHORT: + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_FLOAT: + case MYSQL_TYPE_DOUBLE: + case MYSQL_TYPE_NULL: + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_LONGLONG: + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_YEAR: + case MYSQL_TYPE_NEWDATE: + case MYSQL_TYPE_BIT: + case MYSQL_TYPE_NEWDECIMAL: + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_SET: + case MYSQL_TYPE_GEOMETRY: +#else + case DRIZZLE_TYPE_LONG: + case DRIZZLE_TYPE_DOUBLE: + case DRIZZLE_TYPE_NULL: + case DRIZZLE_TYPE_TIMESTAMP: + case DRIZZLE_TYPE_LONGLONG: + case DRIZZLE_TYPE_DATETIME: + case DRIZZLE_TYPE_DATE: + case DRIZZLE_TYPE_NEWDECIMAL: + case DRIZZLE_TYPE_ENUM: +#endif + break; + } + + *len = field->pack_length(); + return from; +} + +/* + * Set the length and data value of a field. + * + * If input data is NULL this is a NULL value. In this case + * we assume the null bit has been set and prepared + * the field as follows: + * + * According to the InnoDB implementation, we need + * to zero out the field data... + * "MySQL seems to assume the field for an SQL NULL + * value is set to zero or space. Not taking this into + * account caused seg faults with NULL BLOB fields, and + * bug number 154 in the MySQL bug database: GROUP BY + * and DISTINCT could treat NULL values inequal". + */ +static void mx_set_length_and_data(Field *field, char *dest, xtWord4 len, char *data) +{ + char *from; + +#if MYSQL_VERSION_ID < 50114 + from = dest + field->offset(); +#else + from = dest + field->offset(field->table->record[0]); +#endif + switch (field->real_type()) { +#ifndef DRIZZLED + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: +#endif + case MYSQL_TYPE_BLOB: { + /* GOTCHA: There is no way that this can work. + * field is shared, because table is shared! + char *save = field->ptr; + + field->ptr = (char *) from; + ((Field_blob *) field)->set_ptr(len, data); + field->ptr = save; // Restore org row pointer + */ + xtWord4 packlength = ((Field_blob *) field)->pack_length() - field->table->s->blob_ptr_size; + + ((Field_blob *) field)->store_length((byte *) from, packlength, len); + memcpy_fixed(((char *) from)+packlength, &data, sizeof(char*)); + + if (data) + mx_set_notnull_in_record(field, dest); + return; + } +#ifndef DRIZZLED + case MYSQL_TYPE_STRING: + if (data) { + mx_set_notnull_in_record(field, dest); + memcpy(from, data, len); + } + else + len = 0; + + /* And I think that fill will do this for me... */ + field->charset()->cset->fill(field->charset(), from + len, field->field_length - len, ' '); + return; + case MYSQL_TYPE_VAR_STRING: + int2store(from, len); + if (data) { + mx_set_notnull_in_record(field, dest); + memcpy(from+HA_KEY_BLOB_LENGTH, data, len); + } + return; +#endif + case MYSQL_TYPE_VARCHAR: + if (((Field_varstring *) field)->length_bytes == 1) + *((unsigned char *) from) = (unsigned char) len; + else + int2store(from, len); + if (data) { + mx_set_notnull_in_record(field, dest); + memcpy(from+((Field_varstring *) field)->length_bytes, data, len); + } + return; +#ifndef DRIZZLED + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_TINY: + case MYSQL_TYPE_SHORT: + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_FLOAT: + case MYSQL_TYPE_DOUBLE: + case MYSQL_TYPE_NULL: + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_LONGLONG: + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_YEAR: + case MYSQL_TYPE_NEWDATE: + case MYSQL_TYPE_BIT: + case MYSQL_TYPE_NEWDECIMAL: + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_SET: + case MYSQL_TYPE_GEOMETRY: +#else + case DRIZZLE_TYPE_LONG: + case DRIZZLE_TYPE_DOUBLE: + case DRIZZLE_TYPE_NULL: + case DRIZZLE_TYPE_TIMESTAMP: + case DRIZZLE_TYPE_LONGLONG: + case DRIZZLE_TYPE_DATETIME: + case DRIZZLE_TYPE_DATE: + case DRIZZLE_TYPE_NEWDECIMAL: + case DRIZZLE_TYPE_ENUM: +#endif + break; + } + + if (data) { + mx_set_notnull_in_record(field, dest); + memcpy(from, data, len); + } + else + bzero(from, field->pack_length()); +} + +xtPublic void myxt_set_null_row_from_key(XTOpenTablePtr XT_UNUSED(ot), XTIndexPtr ind, xtWord1 *record) +{ + register XTIndexSegRec *keyseg = ind->mi_seg; + + for (u_int i=0; i<ind->mi_seg_count; i++, keyseg++) { + ASSERT_NS(keyseg->null_bit); + record[keyseg->null_pos] |= keyseg->null_bit; + } +} + +xtPublic void myxt_set_default_row_from_key(XTOpenTablePtr ot, XTIndexPtr ind, xtWord1 *record) +{ + XTTableHPtr tab = ot->ot_table; + TABLE *table = tab->tab_dic.dic_my_table; + XTIndexSegRec *keyseg = ind->mi_seg; + + xt_lock_mutex_ns(&tab->tab_dic_field_lock); + + for (u_int i=0; i<ind->mi_seg_count; i++, keyseg++) { + + u_int col_idx = keyseg->col_idx; + Field *field = table->field[col_idx]; + byte *field_save = field->ptr; + + field->ptr = table->s->default_values + keyseg->start; + memcpy(record + keyseg->start, field->ptr, field->pack_length()); + record[keyseg->null_pos] &= ~keyseg->null_bit; + record[keyseg->null_pos] |= table->s->default_values[keyseg->null_pos] & keyseg->null_bit; + + field->ptr = field_save; + } + + xt_unlock_mutex_ns(&tab->tab_dic_field_lock); +} + +/* Derived from _mi_put_key_in_record */ +xtPublic xtBool myxt_create_row_from_key(XTOpenTablePtr XT_UNUSED(ot), XTIndexPtr ind, xtWord1 *b_value, u_int key_len, xtWord1 *dest_buff) +{ + byte *record = (byte *) dest_buff; + register byte *key; + byte *pos,*key_end; + register XTIndexSegRec *keyseg = ind->mi_seg; + + /* GOTCHA: When selecting from multiple + * indexes the key values are "merged" into the + * same buffer!! + * This means that this function must not affect + * the value of any other feilds. + * + * I was setting all to NULL: + memset(dest_buff, 0xFF, table->s->null_bytes); + */ + key = (byte *) b_value; + key_end = key + key_len; + for (u_int i=0; i<ind->mi_seg_count; i++, keyseg++) { + if (keyseg->null_bit) { + if (!*key++) + { + record[keyseg->null_pos] |= keyseg->null_bit; + continue; + } + record[keyseg->null_pos] &= ~keyseg->null_bit; + } +#ifndef DRIZZLED + if (keyseg->type == HA_KEYTYPE_BIT) + { + uint length = keyseg->length; + + if (keyseg->bit_length) + { + uchar bits= *key++; + set_rec_bits(bits, record + keyseg->bit_pos, keyseg->bit_start, + keyseg->bit_length); + length--; + } + else + { + clr_rec_bits(record + keyseg->bit_pos, keyseg->bit_start, + keyseg->bit_length); + } + memcpy(record + keyseg->start, (byte*) key, length); + key+= length; + continue; + } +#endif + if (keyseg->flag & HA_SPACE_PACK) + { + uint length; + get_key_length(length,key); +#ifdef CHECK_KEYS + if (length > keyseg->length || key+length > key_end) + goto err; +#endif + pos = record+keyseg->start; +#ifndef DRIZZLED + if (keyseg->type != (int) HA_KEYTYPE_NUM) + { +#endif + memcpy(pos,key,(size_t) length); + bfill(pos+length,keyseg->length-length,' '); +#ifndef DRIZZLED + } + else + { + bfill(pos,keyseg->length-length,' '); + memcpy(pos+keyseg->length-length,key,(size_t) length); + } +#endif + key+=length; + continue; + } + + if (keyseg->flag & HA_VAR_LENGTH_PART) + { + uint length; + get_key_length(length,key); +#ifdef CHECK_KEYS + if (length > keyseg->length || key+length > key_end) + goto err; +#endif + /* Store key length */ + if (keyseg->bit_start == 1) + *(uchar*) (record+keyseg->start)= (uchar) length; + else + int2store(record+keyseg->start, length); + /* And key data */ + memcpy(record+keyseg->start + keyseg->bit_start, (byte*) key, length); + key+= length; + } + else if (keyseg->flag & HA_BLOB_PART) + { + uint length; + get_key_length(length,key); +#ifdef CHECK_KEYS + if (length > keyseg->length || key+length > key_end) + goto err; +#endif + /* key is a pointer into ot_ind_rbuf, which should be + * safe until we move to the next index item! + */ + byte *key_ptr = key; // Cannot take the address of a register variable! + memcpy(record+keyseg->start+keyseg->bit_start, + (char*) &key_ptr,sizeof(char*)); + + my_store_blob_length(record+keyseg->start, + (uint) keyseg->bit_start,length); + key+=length; + } + else if (keyseg->flag & HA_SWAP_KEY) + { + byte *to= record+keyseg->start+keyseg->length; + byte *end= key+keyseg->length; +#ifdef CHECK_KEYS + if (end > key_end) + goto err; +#endif + do { + *--to= *key++; + } while (key != end); + continue; + } + else + { +#ifdef CHECK_KEYS + if (key+keyseg->length > key_end) + goto err; +#endif + memcpy(record+keyseg->start,(byte*) key, + (size_t) keyseg->length); + key+= keyseg->length; + } + + } + return OK; + +#ifdef CHECK_KEYS + err: + return FAILED; /* Crashed row */ +#endif +} + +/* + * ----------------------------------------------------------------------- + * Compare keys + */ + +static int my_compare_bin(uchar *a, uint a_length, uchar *b, uint b_length, + my_bool part_key, my_bool skip_end_space) +{ + uint length= a_length < b_length ? a_length : b_length; + uchar *end= a+ length; + int flag; + + while (a < end) + if ((flag= (int) *a++ - (int) *b++)) + return flag; + if (part_key && b_length < a_length) + return 0; + if (skip_end_space && a_length != b_length) + { + int swap= 1; + /* + We are using space compression. We have to check if longer key + has next character < ' ', in which case it's less than the shorter + key that has an implicite space afterwards. + + This code is identical to the one in + strings/ctype-simple.c:my_strnncollsp_simple + */ + if (a_length < b_length) + { + /* put shorter key in a */ + a_length= b_length; + a= b; + swap= -1; /* swap sign of result */ + } + for (end= a + a_length-length; a < end ; a++) + { + if (*a != ' ') + return (*a < ' ') ? -swap : swap; + } + return 0; + } + return (int) (a_length-b_length); +} + +xtPublic u_int myxt_get_key_length(XTIndexPtr ind, xtWord1 *key_buf) +{ + register XTIndexSegRec *keyseg = ind->mi_seg; + register uchar *key_data = (uchar *) key_buf; + uint seg_len; + uint pack_len; + + for (u_int i=0; i<ind->mi_seg_count; i++, keyseg++) { + /* Handle NULL part */ + if (keyseg->null_bit) { + if (!*key_data++) + continue; + } + + switch ((enum ha_base_keytype) keyseg->type) { + case HA_KEYTYPE_TEXT: /* Ascii; Key is converted */ + if (keyseg->flag & HA_SPACE_PACK) { + get_key_pack_length(seg_len, pack_len, key_data); + } + else + seg_len = keyseg->length; + key_data += seg_len; + break; + case HA_KEYTYPE_BINARY: + if (keyseg->flag & HA_SPACE_PACK) { + get_key_pack_length(seg_len, pack_len, key_data); + } + else + seg_len = keyseg->length; + key_data += seg_len; + break; + case HA_KEYTYPE_VARTEXT1: + case HA_KEYTYPE_VARTEXT2: + get_key_pack_length(seg_len, pack_len, key_data); + key_data += seg_len; + break; + case HA_KEYTYPE_VARBINARY1: + case HA_KEYTYPE_VARBINARY2: + get_key_pack_length(seg_len, pack_len, key_data); + key_data += seg_len; + break; +#ifndef DRIZZLED + case HA_KEYTYPE_NUM: { + /* Numeric key */ + if (keyseg->flag & HA_SPACE_PACK) + seg_len = *key_data++; + else + seg_len = keyseg->length; + key_data += seg_len; + break; + } + case HA_KEYTYPE_INT8: + case HA_KEYTYPE_SHORT_INT: + case HA_KEYTYPE_USHORT_INT: + case HA_KEYTYPE_INT24: + case HA_KEYTYPE_FLOAT: + case HA_KEYTYPE_BIT: +#endif + case HA_KEYTYPE_LONG_INT: + case HA_KEYTYPE_ULONG_INT: + case HA_KEYTYPE_UINT24: + case HA_KEYTYPE_DOUBLE: + case HA_KEYTYPE_LONGLONG: + case HA_KEYTYPE_ULONGLONG: + key_data += keyseg->length; + break; + case HA_KEYTYPE_END: + goto end; + } + } + + end: + u_int ilen = (xtWord1 *) key_data - key_buf; + if (ilen > XT_INDEX_MAX_KEY_SIZE) + ind->mi_key_corrupted = TRUE; + return ilen; +} + +/* Derived from ha_key_cmp */ +xtPublic int myxt_compare_key(XTIndexPtr ind, int search_flags, uint key_length, xtWord1 *key_value, xtWord1 *b_value) +{ + register XTIndexSegRec *keyseg = ind->mi_seg; + int flag; + register uchar *a = (uchar *) key_value; + uint a_length; + register uchar *b = (uchar *) b_value; + uint b_length; + uint next_key_length; + uchar *end; + uint piks; + uint pack_len; + + for (uint i=0; i < ind->mi_seg_count && (int) key_length > 0; key_length = next_key_length, keyseg++, i++) { + piks = !(keyseg->flag & HA_NO_SORT); + + /* Handle NULL part */ + if (keyseg->null_bit) { + /* 1 is not null, 0 is null */ + int b_not_null = (int) *b++; + + key_length--; + if ((int) *a != b_not_null && piks) + { + flag = (int) *a - b_not_null; + return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); + } + if (!*a++) { + /* If key was NULL */ + if (search_flags == (SEARCH_FIND | SEARCH_UPDATE)) + search_flags = SEARCH_SAME; /* Allow duplicate keys */ + else if (search_flags & SEARCH_NULL_ARE_NOT_EQUAL) + { + /* + * This is only used from mi_check() to calculate cardinality. + * It can't be used when searching for a key as this would cause + * compare of (a,b) and (b,a) to return the same value. + */ + return -1; + } + /* PMC - I don't know why I had next_key_length = key_length - keyseg->length; + * This was my comment: even when null we have the complete length + * + * The truth is, a NULL only takes up one byte in the key, and this has already + * been subtracted. + */ + next_key_length = key_length; + continue; /* To next key part */ + } + } + + /* Both components are not null... */ + if (keyseg->length < key_length) { + end = a + keyseg->length; + next_key_length = key_length - keyseg->length; + } + else { + end = a + key_length; + next_key_length = 0; + } + + switch ((enum ha_base_keytype) keyseg->type) { + case HA_KEYTYPE_TEXT: /* Ascii; Key is converted */ + if (keyseg->flag & HA_SPACE_PACK) { + get_key_pack_length(a_length, pack_len, a); + next_key_length = key_length - a_length - pack_len; + get_key_pack_length(b_length, pack_len, b); + + if (piks && (flag = my_compare_text(keyseg->charset, a, a_length, b, b_length, + (my_bool) ((search_flags & SEARCH_PREFIX) && next_key_length <= 0), + (my_bool)!(search_flags & SEARCH_PREFIX)))) + return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); + a += a_length; + } + else { + a_length = (uint) (end - a); + b_length = keyseg->length; + if (piks && (flag = my_compare_text(keyseg->charset, a, a_length, b, b_length, + (my_bool) ((search_flags & SEARCH_PREFIX) && next_key_length <= 0), + (my_bool)!(search_flags & SEARCH_PREFIX)))) + return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); + a = end; + } + b += b_length; + break; + case HA_KEYTYPE_BINARY: + if (keyseg->flag & HA_SPACE_PACK) { + get_key_pack_length(a_length, pack_len, a); + next_key_length = key_length - a_length - pack_len; + get_key_pack_length(b_length, pack_len, b); + + if (piks && (flag = my_compare_bin(a, a_length, b, b_length, + (my_bool) ((search_flags & SEARCH_PREFIX) && next_key_length <= 0), 1))) + return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); + } + else { + a_length = keyseg->length; + b_length = keyseg->length; + if (piks && (flag = my_compare_bin(a, a_length, b, b_length, + (my_bool) ((search_flags & SEARCH_PREFIX) && next_key_length <= 0), 0))) + return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); + } + a += a_length; + b += b_length; + break; + case HA_KEYTYPE_VARTEXT1: + case HA_KEYTYPE_VARTEXT2: + { + get_key_pack_length(a_length, pack_len, a); + next_key_length = key_length - a_length - pack_len; + get_key_pack_length(b_length, pack_len, b); + + if (piks && (flag = my_compare_text(keyseg->charset, a, a_length, b, b_length, + (my_bool) ((search_flags & SEARCH_PREFIX) && next_key_length <= 0), + (my_bool) ((search_flags & (SEARCH_FIND | SEARCH_UPDATE)) == SEARCH_FIND)))) + return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); + a += a_length; + b += b_length; + break; + } + case HA_KEYTYPE_VARBINARY1: + case HA_KEYTYPE_VARBINARY2: + { + get_key_pack_length(a_length, pack_len, a); + next_key_length = key_length - a_length - pack_len; + get_key_pack_length(b_length, pack_len, b); + + if (piks && (flag=my_compare_bin(a, a_length, b, b_length, + (my_bool) ((search_flags & SEARCH_PREFIX) && next_key_length <= 0), 0))) + return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); + a += a_length; + b += b_length; + break; + } +#ifndef DRIZZLED + case HA_KEYTYPE_INT8: + { + int i_1 = (int) *((signed char *) a); + int i_2 = (int) *((signed char *) b); + if (piks && (flag = CMP_NUM(i_1,i_2))) + return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); + a = end; + b += keyseg->length; + break; + } + case HA_KEYTYPE_SHORT_INT: { + int16 s_1 = sint2korr(a); + int16 s_2 = sint2korr(b); + if (piks && (flag = CMP_NUM(s_1, s_2))) + return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); + a = end; + b += keyseg->length; + break; + } + case HA_KEYTYPE_USHORT_INT: { + uint16 us_1= sint2korr(a); + uint16 us_2= sint2korr(b); + if (piks && (flag = CMP_NUM(us_1, us_2))) + return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); + a = end; + b += keyseg->length; + break; + } +#endif + case HA_KEYTYPE_LONG_INT: { + int32 l_1 = sint4korr(a); + int32 l_2 = sint4korr(b); + if (piks && (flag = CMP_NUM(l_1, l_2))) + return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); + a = end; + b += keyseg->length; + break; + } + case HA_KEYTYPE_ULONG_INT: { + uint32 u_1 = sint4korr(a); + uint32 u_2 = sint4korr(b); + if (piks && (flag = CMP_NUM(u_1, u_2))) + return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); + a = end; + b += keyseg->length; + break; + } +#ifndef DRIZZLED + case HA_KEYTYPE_INT24: { + int32 l_1 = sint3korr(a); + int32 l_2 = sint3korr(b); + if (piks && (flag = CMP_NUM(l_1, l_2))) + return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); + a = end; + b += keyseg->length; + break; + } +#endif + case HA_KEYTYPE_UINT24: { + int32 l_1 = uint3korr(a); + int32 l_2 = uint3korr(b); + if (piks && (flag = CMP_NUM(l_1, l_2))) + return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); + a = end; + b += keyseg->length; + break; + } +#ifndef DRIZZLED + case HA_KEYTYPE_FLOAT: { + float f_1, f_2; + + float4get(f_1, a); + float4get(f_2, b); + /* + * The following may give a compiler warning about floating point + * comparison not being safe, but this is ok in this context as + * we are bascily doing sorting + */ + if (piks && (flag = CMP_NUM(f_1, f_2))) + return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); + a = end; + b += keyseg->length; + break; + } +#endif + case HA_KEYTYPE_DOUBLE: { + double d_1, d_2; + + float8get(d_1, a); + float8get(d_2, b); + /* + * The following may give a compiler warning about floating point + * comparison not being safe, but this is ok in this context as + * we are bascily doing sorting + */ + if (piks && (flag = CMP_NUM(d_1, d_2))) + return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); + a = end; + b += keyseg->length; + break; + } +#ifndef DRIZZLED + case HA_KEYTYPE_NUM: { + /* Numeric key */ + if (keyseg->flag & HA_SPACE_PACK) { + a_length = *a++; + end = a + a_length; + next_key_length = key_length - a_length - 1; + b_length = *b++; + } + else { + a_length = (int) (end - a); + b_length = keyseg->length; + } + + /* remove pre space from keys */ + for ( ; a_length && *a == ' ' ; a++, a_length--) ; + for ( ; b_length && *b == ' ' ; b++, b_length--) ; + + if (keyseg->flag & HA_REVERSE_SORT) { + swap_variables(uchar *, a, b); + swap_variables(uint, a_length, b_length); + } + + if (piks) { + if (*a == '-') { + if (*b != '-') + return -1; + a++; b++; + swap_variables(uchar *, a, b); + swap_variables(uint, a_length, b_length); + a_length--; b_length--; + } + else if (*b == '-') + return 1; + while (a_length && (*a == '+' || *a == '0')) { + a++; a_length--; + } + + while (b_length && (*b == '+' || *b == '0')) { + b++; b_length--; + } + + if (a_length != b_length) + return (a_length < b_length) ? -1 : 1; + while (b_length) { + if (*a++ != *b++) + return ((int) a[-1] - (int) b[-1]); + b_length--; + } + } + a = end; + b += b_length; + break; + } +#endif +#ifdef HAVE_LONG_LONG + case HA_KEYTYPE_LONGLONG: { + longlong ll_a = sint8korr(a); + longlong ll_b = sint8korr(b); + if (piks && (flag = CMP_NUM(ll_a,ll_b))) + return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); + a = end; + b += keyseg->length; + break; + } + case HA_KEYTYPE_ULONGLONG: { + ulonglong ll_a = uint8korr(a); + ulonglong ll_b = uint8korr(b); + if (piks && (flag = CMP_NUM(ll_a,ll_b))) + return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); + a = end; + b += keyseg->length; + break; + } +#endif +#ifndef DRIZZLED + case HA_KEYTYPE_BIT: + /* TODO: What here? */ + break; +#endif + case HA_KEYTYPE_END: /* Ready */ + goto end; + } + } + + end: + return 0; +} + +xtPublic u_int myxt_key_seg_length(XTIndexSegRec *keyseg, u_int key_offset, xtWord1 *key_value) +{ + register xtWord1 *a = (xtWord1 *) key_value + key_offset; + u_int a_length; + u_int has_null = 0; + u_int key_length = 0; + u_int pack_len; + + /* Handle NULL part */ + if (keyseg->null_bit) { + has_null++; + /* If the value is null, then it only requires one byte: */ + if (!*a++) + return has_null; + } + + key_length = has_null + keyseg->length; + + switch ((enum ha_base_keytype) keyseg->type) { + case HA_KEYTYPE_TEXT: /* Ascii; Key is converted */ + if (keyseg->flag & HA_SPACE_PACK) { + get_key_pack_length(a_length, pack_len, a); + key_length = has_null + a_length + pack_len; + } + break; + case HA_KEYTYPE_BINARY: + if (keyseg->flag & HA_SPACE_PACK) { + get_key_pack_length(a_length, pack_len, a); + key_length = has_null + a_length + pack_len; + } + break; + case HA_KEYTYPE_VARTEXT1: + case HA_KEYTYPE_VARTEXT2: + case HA_KEYTYPE_VARBINARY1: + case HA_KEYTYPE_VARBINARY2: { + get_key_pack_length(a_length, pack_len, a); + key_length = has_null + a_length + pack_len; + break; + } +#ifndef DRIZZLED + case HA_KEYTYPE_INT8: + case HA_KEYTYPE_SHORT_INT: + case HA_KEYTYPE_USHORT_INT: + case HA_KEYTYPE_INT24: + case HA_KEYTYPE_FLOAT: +#endif + case HA_KEYTYPE_LONG_INT: + case HA_KEYTYPE_ULONG_INT: + case HA_KEYTYPE_UINT24: + case HA_KEYTYPE_DOUBLE: + break; +#ifndef DRIZZLED + case HA_KEYTYPE_NUM: { + /* Numeric key */ + if (keyseg->flag & HA_SPACE_PACK) { + a_length = *a++; + key_length = has_null + a_length + 1; + } + break; + } +#endif +#ifdef HAVE_LONG_LONG + case HA_KEYTYPE_LONGLONG: + case HA_KEYTYPE_ULONGLONG: + break; +#endif +#ifndef DRIZZLED + case HA_KEYTYPE_BIT: + /* TODO: What here? */ + break; +#endif + case HA_KEYTYPE_END: /* Ready */ + break; + } + + return key_length; +} + +/* + * ----------------------------------------------------------------------- + * Load and store rows + */ + +xtPublic xtWord4 myxt_store_row_length(XTOpenTablePtr ot, char *rec_buff) +{ + TABLE *table = ot->ot_table->tab_dic.dic_my_table; + char *sdata; + xtWord4 dlen; + xtWord4 item_size; + xtWord4 row_size = 0; + + for (Field **field=table->field ; *field ; field++) { + if ((*field)->is_null_in_record((const uchar *) rec_buff)) { + sdata = NULL; + dlen = 0; + item_size = 1; + } + else { + sdata = mx_get_length_and_data(*field, rec_buff, &dlen); + if (!dlen) { + /* Empty, but not null (blobs may return NULL, when + * length is 0. + */ + sdata = rec_buff; // Any valid pointer will do + item_size = 1 + dlen; + } + else if (dlen <= 240) + item_size = 1 + dlen; + else if (dlen <= 0xFFFF) + item_size = 3 + dlen; + else if (dlen <= 0xFFFFFF) + item_size = 4 + dlen; + else + item_size = 5 + dlen; + } + + row_size += item_size; + } + return row_size; +} + +xtPublic xtWord4 myxt_store_row_data(XTOpenTablePtr ot, xtWord4 row_size, char *rec_buff) +{ + TABLE *table = ot->ot_table->tab_dic.dic_my_table; + char *sdata; + xtWord4 dlen; + xtWord4 item_size; + + for (Field **field=table->field ; *field ; field++) { + if ((*field)->is_null_in_record((const uchar *) rec_buff)) { + sdata = NULL; + dlen = 0; + item_size = 1; + } + else { + sdata = mx_get_length_and_data(*field, rec_buff, &dlen); + if (!dlen) { + /* Empty, but not null (blobs may return NULL, when + * length is 0. + */ + sdata = rec_buff; // Any valid pointer will do + item_size = 1 + dlen; + } + else if (dlen <= 240) + item_size = 1 + dlen; + else if (dlen <= 0xFFFF) + item_size = 3 + dlen; + else if (dlen <= 0xFFFFFF) + item_size = 4 + dlen; + else + item_size = 5 + dlen; + } + + if (row_size + item_size > ot->ot_row_wbuf_size) { + if (!xt_realloc_ns((void **) &ot->ot_row_wbuffer, row_size + item_size)) + return 0; + ot->ot_row_wbuf_size = row_size + item_size; + } + + if (!sdata) + ot->ot_row_wbuffer[row_size] = 255; + else if (dlen <= 240) { + ot->ot_row_wbuffer[row_size] = (unsigned char) dlen; + memcpy(&ot->ot_row_wbuffer[row_size+1], sdata, dlen); + } + else if (dlen <= 0xFFFF) { + ot->ot_row_wbuffer[row_size] = 254; + XT_SET_DISK_2(&ot->ot_row_wbuffer[row_size+1], dlen); + memcpy(&ot->ot_row_wbuffer[row_size+3], sdata, dlen); + } + else if (dlen <= 0xFFFFFF) { + ot->ot_row_wbuffer[row_size] = 253; + XT_SET_DISK_3(&ot->ot_row_wbuffer[row_size+1], dlen); + memcpy(&ot->ot_row_wbuffer[row_size+4], sdata, dlen); + } + else { + ot->ot_row_wbuffer[row_size] = 252; + XT_SET_DISK_4(&ot->ot_row_wbuffer[row_size+1], dlen); + memcpy(&ot->ot_row_wbuffer[row_size+5], sdata, dlen); + } + + row_size += item_size; + } + return row_size; +} + +/* Count the number and size of whole columns in the given buffer. */ +xtPublic size_t myxt_load_row_length(XTOpenTablePtr ot, size_t buffer_size, xtWord1 *source_buf, u_int *ret_col_cnt) +{ + u_int col_cnt; + xtWord4 len; + size_t size = 0; + u_int i; + + col_cnt = ot->ot_table->tab_dic.dic_no_of_cols; + if (ret_col_cnt) + col_cnt = *ret_col_cnt; + for (i=0; i<col_cnt; i++) { + if (size + 1 > buffer_size) + goto done; + switch (*source_buf) { + case 255: // Indicate NULL value + size++; + source_buf++; + break; + case 254: // 2 bytes length + if (size + 3 > buffer_size) + goto done; + len = XT_GET_DISK_2(source_buf + 1); + if (size + 3 + len > buffer_size) + goto done; + size += 3 + len; + source_buf += 3 + len; + break; + case 253: // 3 bytes length + if (size + 4 > buffer_size) + goto done; + len = XT_GET_DISK_3(source_buf + 1); + if (size + 4 + len > buffer_size) + goto done; + size += 4 + len; + source_buf += 4 + len; + break; + case 252: // 4 bytes length + if (size + 5 > buffer_size) + goto done; + len = XT_GET_DISK_4(source_buf + 1); + if (size + 5 + len > buffer_size) + goto done; + size += 5 + len; + source_buf += 5 + len; + break; + default: // Length byte + len = *source_buf; + if (size + 1 + len > buffer_size) + goto done; + size += 1 + len; + source_buf += 1 + len; + break; + } + } + + done: + if (ret_col_cnt) + *ret_col_cnt = i; + return size; +} + +/* Unload from PBXT variable length format to the MySQL row format. */ +xtPublic xtWord4 myxt_load_row_data(XTOpenTablePtr ot, xtWord1 *source_buf, xtWord1 *dest_buff, u_int col_cnt) +{ + xtWord1 *input_buf = source_buf; + TABLE *table; + xtWord4 len; + Field *curr_field; + xtBool is_null; + u_int i = 0; + + if (!(table = ot->ot_table->tab_dic.dic_my_table)) { + xt_register_taberr(XT_REG_CONTEXT, XT_ERR_NO_DICTIONARY, ot->ot_table->tab_name); + return 0; + } + + /* According to the InnoDB implementation: + * "MySQL assumes that all columns + * have the SQL NULL bit set unless it + * is a nullable column with a non-NULL value". + */ + memset(dest_buff, 0xFF, table->s->null_bytes); + for (Field **field=table->field ; *field && (!col_cnt || i<col_cnt); field++, i++) { + curr_field = *field; + is_null = FALSE; + switch (*source_buf) { + case 255: // Indicate NULL value + is_null = TRUE; + len = 0; + source_buf++; + break; + case 254: // 2 bytes length + len = XT_GET_DISK_2(source_buf + 1); + source_buf += 3; + break; + case 253: // 3 bytes length + len = XT_GET_DISK_3(source_buf + 1); + source_buf += 4; + break; + case 252: // 4 bytes length + len = XT_GET_DISK_4(source_buf + 1); + source_buf += 5; + break; + default: // Length byte + if (*source_buf > 240) { + xt_register_xterr(XT_REG_CONTEXT, XT_ERR_BAD_RECORD_FORMAT); + return 0; + } + len = *source_buf; + source_buf++; + break; + } + + if (is_null) + mx_set_length_and_data(curr_field, (char *) dest_buff, 0, NULL); + else + mx_set_length_and_data(curr_field, (char *) dest_buff, len, (char *) source_buf); + + source_buf += len; + } + return (xtWord4) (source_buf - input_buf); +} + +xtPublic xtBool myxt_load_row(XTOpenTablePtr ot, xtWord1 *source_buf, xtWord1 *dest_buff, u_int col_cnt) +{ + return myxt_load_row_data(ot, source_buf, dest_buff, col_cnt) != 0; +} + +xtPublic xtBool myxt_find_column(XTOpenTablePtr ot, u_int *col_idx, const char *col_name) +{ + TABLE *table = ot->ot_table->tab_dic.dic_my_table; + u_int i=0; + + for (Field **field=table->field; *field; field++, i++) { + if (!my_strcasecmp(system_charset_info, (*field)->field_name, col_name)) { + *col_idx = i; + return OK; + } + } + return FALSE; +} + +xtPublic void myxt_get_column_name(XTOpenTablePtr ot, u_int col_idx, u_int len, char *col_name) +{ + TABLE *table = ot->ot_table->tab_dic.dic_my_table; + Field *field; + + field = table->field[col_idx]; + xt_strcpy(len, col_name, field->field_name); +} + +xtPublic void myxt_get_column_as_string(XTOpenTablePtr ot, char *buffer, u_int col_idx, u_int len, char *value) +{ + XTTableHPtr tab = ot->ot_table; + XTThreadPtr self = ot->ot_thread; + TABLE *table = tab->tab_dic.dic_my_table; + Field *field = table->field[col_idx]; + char buf_val[MAX_FIELD_WIDTH]; + String val(buf_val, sizeof(buf_val), &my_charset_bin); + + if (mx_is_null_in_record(field, buffer)) + xt_strcpy(len, value, "NULL"); + else { + byte *save; + + /* Required by store() - or an assertion will fail: */ + if (table->read_set) + MX_BIT_SET(table->read_set, col_idx); + + save = field->ptr; + xt_lock_mutex(self, &tab->tab_dic_field_lock); + pushr_(xt_unlock_mutex, &tab->tab_dic_field_lock); +#if MYSQL_VERSION_ID < 50114 + field->ptr = (byte *) buffer + field->offset(); +#else + field->ptr = (byte *) buffer + field->offset(field->table->record[0]); +#endif + field->val_str(&val); + field->ptr = save; // Restore org row pointer + freer_(); // xt_unlock_mutex(&tab->tab_dic_field_lock) + xt_strcpy(len, value, val.c_ptr()); + } +} + +xtPublic xtBool myxt_set_column(XTOpenTablePtr ot, char *buffer, u_int col_idx, const char *value, u_int len) +{ + XTTableHPtr tab = ot->ot_table; + XTThreadPtr self = ot->ot_thread; + TABLE *table = tab->tab_dic.dic_my_table; + Field *field = table->field[col_idx]; + byte *save; + int error; + + /* Required by store() - or an assertion will fail: */ + if (table->write_set) + MX_BIT_SET(table->write_set, col_idx); + + mx_set_notnull_in_record(field, buffer); + + save = field->ptr; + xt_lock_mutex(self, &tab->tab_dic_field_lock); + pushr_(xt_unlock_mutex, &tab->tab_dic_field_lock); +#if MYSQL_VERSION_ID < 50114 + field->ptr = (byte *) buffer + field->offset(); +#else + field->ptr = (byte *) buffer + field->offset(field->table->record[0]); +#endif + error = field->store(value, len, &my_charset_utf8_general_ci); + field->ptr = save; // Restore org row pointer + freer_(); // xt_unlock_mutex(&tab->tab_dic_field_lock) + return error ? FAILED : OK; +} + +xtPublic void myxt_get_column_data(XTOpenTablePtr ot, char *buffer, u_int col_idx, char **value, size_t *len) +{ + TABLE *table = ot->ot_table->tab_dic.dic_my_table; + Field *field = table->field[col_idx]; + char *sdata; + xtWord4 dlen; + + sdata = mx_get_length_and_data(field, buffer, &dlen); + *value = sdata; + *len = dlen; +} + +xtPublic xtBool myxt_store_row(XTOpenTablePtr ot, XTTabRecInfoPtr rec_info, char *rec_buff) +{ + if (ot->ot_rec_fixed) { + rec_info->ri_fix_rec_buf = (XTTabRecFixDPtr) ot->ot_row_wbuffer; + rec_info->ri_rec_buf_size = ot->ot_rec_size; + rec_info->ri_ext_rec = NULL; + + rec_info->ri_fix_rec_buf->tr_rec_type_1 = XT_TAB_STATUS_FIXED; + memcpy(rec_info->ri_fix_rec_buf->rf_data, rec_buff, ot->ot_rec_size - XT_REC_FIX_HEADER_SIZE); + } + else { + xtWord4 row_size; + + if (!(row_size = myxt_store_row_data(ot, XT_REC_EXT_HEADER_SIZE, rec_buff))) + return FAILED; + if (row_size - XT_REC_FIX_EXT_HEADER_DIFF <= ot->ot_rec_size) { + rec_info->ri_fix_rec_buf = (XTTabRecFixDPtr) &ot->ot_row_wbuffer[XT_REC_FIX_EXT_HEADER_DIFF]; + rec_info->ri_rec_buf_size = row_size - XT_REC_FIX_EXT_HEADER_DIFF; + rec_info->ri_ext_rec = NULL; + + rec_info->ri_fix_rec_buf->tr_rec_type_1 = XT_TAB_STATUS_VARIABLE; + } + else { + rec_info->ri_fix_rec_buf = (XTTabRecFixDPtr) ot->ot_row_wbuffer; + rec_info->ri_rec_buf_size = ot->ot_rec_size; + rec_info->ri_ext_rec = (XTTabRecExtDPtr) ot->ot_row_wbuffer; + rec_info->ri_log_data_size = row_size - ot->ot_rec_size; + rec_info->ri_log_buf = (XTactExtRecEntryDPtr) &ot->ot_row_wbuffer[ot->ot_rec_size - offsetof(XTactExtRecEntryDRec, er_data)]; + + rec_info->ri_ext_rec->tr_rec_type_1 = XT_TAB_STATUS_EXT_DLOG; + XT_SET_DISK_4(rec_info->ri_ext_rec->re_log_dat_siz_4, rec_info->ri_log_data_size); + } + } + return OK; +} + +static void mx_print_string(uchar *s, uint count) +{ + while (count > 0) { + if (s[count - 1] != ' ') + break; + count--; + } + printf("\""); + for (u_int i=0; i<count; i++, s++) + printf("%c", *s); + printf("\""); +} + +xtPublic void myxt_print_key(XTIndexPtr ind, xtWord1 *key_value) +{ + register XTIndexSegRec *keyseg = ind->mi_seg; + register uchar *b = (uchar *) key_value; + uint b_length; + uint pack_len; + + for (u_int i = 0; i < ind->mi_seg_count; i++, keyseg++) { + if (i!=0) + printf(" "); + if (keyseg->null_bit) { + if (!*b++) { + printf("NULL"); + continue; + } + } + switch ((enum ha_base_keytype) keyseg->type) { + case HA_KEYTYPE_TEXT: /* Ascii; Key is converted */ + if (keyseg->flag & HA_SPACE_PACK) { + get_key_pack_length(b_length, pack_len, b); + } + else + b_length = keyseg->length; + mx_print_string(b, b_length); + b += b_length; + break; + case HA_KEYTYPE_LONG_INT: { + int32 l_2 = sint4korr(b); + b += keyseg->length; + printf("%ld", (long) l_2); + break; + } + case HA_KEYTYPE_ULONG_INT: { + xtWord4 u_2 = sint4korr(b); + b += keyseg->length; + printf("%lu", (u_long) u_2); + break; + } + default: + break; + } + } +} + +/* + * ----------------------------------------------------------------------- + * MySQL Data Dictionary + */ + +#define TS(x) (x)->s + +static void my_close_table(TABLE *table) +{ +#ifdef DRIZZLED + TABLE_SHARE *share; + + share = (TABLE_SHARE *) ((char *) table + sizeof(TABLE)); + share->free_table_share(); +#else + closefrm(table, 1); // TODO: Q, why did Stewart remove this? +#endif + xt_free_ns(table); +} + +/* + * This function returns NULL if the table cannot be opened + * because this is not a MySQL thread. + */ +static TABLE *my_open_table(XTThreadPtr self, XTDatabaseHPtr XT_UNUSED(db), XTPathStrPtr tab_path) +{ + THD *thd = current_thd; + char path_buffer[PATH_MAX]; + char *table_name; + char database_name[XT_IDENTIFIER_NAME_SIZE]; + char *ptr; + size_t size; + char *buffer, *path, *db_name, *name; + TABLE_SHARE *share; + int error; + TABLE *table; + + /* If we have no MySQL thread, then we cannot open this table! + * What this means is the thread is probably the sweeper or the + * compactor. + */ + if (!thd) + return NULL; + + /* GOTCHA: Check if the table name is a partitian, + * if so we need to remove the partition + * extension, in order for this to work! + * + * Reason: the parts of a partition table do not + * have .frm files!! + */ + xt_strcpy(PATH_MAX, path_buffer, tab_path->ps_path); + table_name = xt_last_name_of_path(path_buffer); + if ((ptr = strstr(table_name, "#P#"))) + *ptr = 0; + + xt_2nd_last_name_of_path(XT_IDENTIFIER_NAME_SIZE, database_name, path_buffer); + + size = sizeof(TABLE) + sizeof(TABLE_SHARE) + + strlen(path_buffer) + 1 + + strlen(database_name) + 1 + strlen(table_name) + 1; + if (!(buffer = (char *) xt_malloc(self, size))) + return NULL; + table = (TABLE *) buffer; + buffer += sizeof(TABLE); + share = (TABLE_SHARE *) buffer; + buffer += sizeof(TABLE_SHARE); + + path = buffer; + strcpy(path, path_buffer); + buffer += strlen(path_buffer) + 1; + db_name = buffer; + strcpy(db_name, database_name); + buffer += strlen(database_name) + 1; + name = buffer; + strcpy(name, table_name); + + /* Required to call 'open_table_from_share'! */ + LEX *old_lex, new_lex; + + old_lex = thd->lex; + thd->lex = &new_lex; + new_lex.current_select= NULL; + lex_start(thd); + +#ifdef DRIZZLED + share->init(db_name, 0, name, path); + if ((error = open_table_def(*thd, share)) || + (error = open_table_from_share(thd, share, "", 0, (uint32_t) READ_ALL, 0, table, OTM_OPEN))) + { + xt_free(self, table); + lex_end(&new_lex); + thd->lex = old_lex; + xt_throw_sulxterr(XT_CONTEXT, XT_ERR_LOADING_MYSQL_DIC, tab_path->ps_path, (u_long) error); + return NULL; + } +#else +#if MYSQL_VERSION_ID < 60000 +#if MYSQL_VERSION_ID < 50123 + init_tmp_table_share(share, db_name, 0, name, path); +#else + init_tmp_table_share(thd, share, db_name, 0, name, path); +#endif +#else +#if MYSQL_VERSION_ID < 60004 + init_tmp_table_share(share, db_name, 0, name, path); +#else + init_tmp_table_share(thd, share, db_name, 0, name, path); +#endif +#endif + + /* If MySQL shutsdown while we are just starting up, they + * they kill the plugin sub-system before calling + * shutdown for the engine! + */ + if (!ha_resolve_by_legacy_type(thd, DB_TYPE_PBXT)) { + xt_free(self, table); + lex_end(&new_lex); + thd->lex = old_lex; + xt_throw_xterr(XT_CONTEXT, XT_ERR_MYSQL_SHUTDOWN); + return NULL; + } + + if ((error = open_table_def(thd, share, 0))) { + xt_free(self, table); + lex_end(&new_lex); + thd->lex = old_lex; + xt_throw_sulxterr(XT_CONTEXT, XT_ERR_LOADING_MYSQL_DIC, tab_path->ps_path, (u_long) error); + return NULL; + } + +#if MYSQL_VERSION_ID >= 50404 + if ((error = open_table_from_share(thd, share, "", 0, (uint) READ_ALL, 0, table, OTM_OPEN))) +#else + if ((error = open_table_from_share(thd, share, "", 0, (uint) READ_ALL, 0, table, FALSE))) +#endif + { + xt_free(self, table); + lex_end(&new_lex); + thd->lex = old_lex; + xt_throw_sulxterr(XT_CONTEXT, XT_ERR_LOADING_MYSQL_DIC, tab_path->ps_path, (u_long) error); + return NULL; + } +#endif + + lex_end(&new_lex); + thd->lex = old_lex; + + /* GOTCHA: I am the plug-in!!! Therefore, I should not hold + * a reference to myself. By holding this reference I prevent + * plugin_shutdown() and reap_plugins() in sql_plugin.cc + * from doing their job on shutdown! + */ +#ifndef DRIZZLED + plugin_unlock(NULL, table->s->db_plugin); + table->s->db_plugin = NULL; +#endif + return table; +} + +/* +static bool my_match_index(XTDDIndex *ind, KEY *index) +{ + KEY_PART_INFO *key_part; + KEY_PART_INFO *key_part_end; + u_int j; + XTDDColumnRef *cref; + + if (index->key_parts != ind->co_cols.size()) + return false; + + j=0; + key_part_end = index->key_part + index->key_parts; + for (key_part = index->key_part; key_part != key_part_end; key_part++, j++) { + if (!(cref = ind->co_cols.itemAt(j))) + return false; + if (myxt_strcasecmp(cref->cr_col_name, (char *) key_part->field->field_name) != 0) + return false; + } + + if (ind->co_type == XT_DD_KEY_PRIMARY) { + if (!(index->flags & HA_NOSAME)) + return false; + } + else { + if (ind->co_type == XT_DD_INDEX_UNIQUE) { + if (!(index->flags & HA_NOSAME)) + return false; + } + if (ind->co_ind_name) { + if (myxt_strcasecmp(ind->co_ind_name, index->name) != 0) + return false; + } + } + + return true; +} + +static XTDDIndex *my_find_index(XTDDTable *dd_tab, KEY *index) +{ + XTDDIndex *ind; + + for (u_int i=0; i<dd_tab->dt_indexes.size(); i++) + { + ind = dd_tab->dt_indexes.itemAt(i); + if (my_match_index(ind, index)) + return ind; + } + return NULL; +} +*/ + +static void my_deref_index_data(struct XTThread *self, XTIndexPtr mi) +{ + enter_(); + /* The dirty list of cache pages should be empty here! */ + /* This is not the case if we were not able to flush data. E.g. when running out of disk space */ + //ASSERT(!mi->mi_dirty_list); + ASSERT(!mi->mi_free_list); + + xt_spinlock_free(self, &mi->mi_dirty_lock); + XT_INDEX_FREE_LOCK(self, mi); + myxt_bitmap_free(self, &mi->mi_col_map); + if (mi->mi_free_list) + xt_free(self, mi->mi_free_list); + + xt_free(self, mi); + exit_(); +} + +static xtBool my_is_not_null_int4(XTIndexSegPtr seg) +{ + return (seg->type == HA_KEYTYPE_LONG_INT && !(seg->flag & HA_NULL_PART)); +} + +/* MY_BITMAP definition in Drizzle does not like if + * I use a NULL pointer to calculate the offset!? + */ +#define MX_OFFSETOF(x, y) ((size_t)(&((x *) 8)->y) - 8) + +/* Derived from ha_myisam::create and mi_create */ +static XTIndexPtr my_create_index(XTThreadPtr self, TABLE *table_arg, u_int idx, KEY *index) +{ + XTIndexPtr ind; + KEY_PART_INFO *key_part; + KEY_PART_INFO *key_part_end; + XTIndexSegRec *seg; + Field *field; + enum ha_base_keytype type; + uint options = 0; + u_int key_length = 0; + xtBool partial_field; + + enter_(); + + pushsr_(ind, my_deref_index_data, (XTIndexPtr) xt_calloc(self, MX_OFFSETOF(XTIndexRec, mi_seg) + sizeof(XTIndexSegRec) * index->key_parts)); + + XT_INDEX_INIT_LOCK(self, ind); + xt_spinlock_init_with_autoname(self, &ind->mi_dirty_lock); + ind->mi_index_no = idx; + ind->mi_flags = (index->flags & (HA_NOSAME | HA_NULL_ARE_EQUAL | HA_UNIQUE_CHECK)); + //ind->mi_low_byte_first = TS(table_arg)->db_low_byte_first; + ind->mi_key_corrupted = FALSE; + ind->mi_fix_key = TRUE; + ind->mi_select_total = 0; + ind->mi_subset_of = 0; + myxt_bitmap_init(self, &ind->mi_col_map, TS(table_arg)->fields); + + ind->mi_seg_count = (uint) index->key_parts; + key_part_end = index->key_part + index->key_parts; + seg = ind->mi_seg; + for (key_part = index->key_part; key_part != key_part_end; key_part++, seg++) { + partial_field = FALSE; + field = key_part->field; + + type = field->key_type(); + seg->flag = key_part->key_part_flag; + + if (options & HA_OPTION_PACK_KEYS || + (index->flags & (HA_PACK_KEY | HA_BINARY_PACK_KEY | HA_SPACE_PACK_USED))) + { + if (key_part->length > 8 && (type == HA_KEYTYPE_TEXT || +#ifndef DRIZZLED + type == HA_KEYTYPE_NUM || +#endif + (type == HA_KEYTYPE_BINARY && !field->zero_pack()))) + { + /* No blobs here */ + if (key_part == index->key_part) + ind->mi_flags |= HA_PACK_KEY; +#ifndef DRIZZLED + if (!(field->flags & ZEROFILL_FLAG) && + (field->type() == MYSQL_TYPE_STRING || + field->type() == MYSQL_TYPE_VAR_STRING || + ((int) (key_part->length - field->decimals())) >= 4)) + seg->flag |= HA_SPACE_PACK; +#endif + } + } + + seg->col_idx = field->field_index; + seg->is_recs_in_range = 1; + seg->is_selectivity = 1; + seg->type = (int) type; + seg->start = key_part->offset; + seg->length = key_part->length; + seg->bit_start = seg->bit_end = 0; + seg->bit_length = seg->bit_pos = 0; + seg->charset = field->charset(); + + if (field->null_ptr) { + key_length++; + seg->flag |= HA_NULL_PART; + seg->null_bit = field->null_bit; + seg->null_pos = (uint) (field->null_ptr - (uchar*) table_arg->record[0]); + } + else { + seg->null_bit = 0; + seg->null_pos = 0; + } + + if (field->real_type() == MYSQL_TYPE_ENUM +#ifndef DRIZZLED + || field->real_type() == MYSQL_TYPE_SET +#endif + ) { + /* This values are not indexed as string!! + * The index will not be built correctly if this value is non-NULL. + */ + seg->charset = NULL; + } + + if (field->type() == MYSQL_TYPE_BLOB +#ifndef DRIZZLED + || field->type() == MYSQL_TYPE_GEOMETRY +#endif + ) { + seg->flag |= HA_BLOB_PART; + /* save number of bytes used to pack length */ + seg->bit_start = (uint) (field->pack_length() - TS(table_arg)->blob_ptr_size); + } +#ifndef DRIZZLED + else if (field->type() == MYSQL_TYPE_BIT) { + seg->bit_length = ((Field_bit *) field)->bit_len; + seg->bit_start = ((Field_bit *) field)->bit_ofs; + seg->bit_pos = (uint) (((Field_bit *) field)->bit_ptr - (uchar*) table_arg->record[0]); + } +#else + /* Drizzle uses HA_KEYTYPE_ULONG_INT keys for enums > 1 byte, which is not consistent with MySQL, so we fix it here */ + else if (field->type() == MYSQL_TYPE_ENUM) { + switch (seg->length) { + case 2: +#ifdef DRIZZLED + ASSERT_NS(FALSE); +#else + seg->type = HA_KEYTYPE_USHORT_INT; + break; +#endif + case 3: + seg->type = HA_KEYTYPE_UINT24; + break; + } + } +#endif + + switch (seg->type) { + case HA_KEYTYPE_VARTEXT1: + case HA_KEYTYPE_VARTEXT2: + case HA_KEYTYPE_VARBINARY1: + case HA_KEYTYPE_VARBINARY2: + if (!(seg->flag & HA_BLOB_PART)) { + /* Make a flag that this is a VARCHAR */ + seg->flag |= HA_VAR_LENGTH_PART; + /* Store in bit_start number of bytes used to pack the length */ + seg->bit_start = ((seg->type == HA_KEYTYPE_VARTEXT1 || seg->type == HA_KEYTYPE_VARBINARY1) ? 1 : 2); + } + break; + } + + /* All packed fields start with a length (1 or 3 bytes): */ + if (seg->flag & (HA_VAR_LENGTH_PART | HA_BLOB_PART | HA_SPACE_PACK)) { + key_length++; /* At least one length byte */ + if (seg->length >= 255) /* prefix may be 3 bytes */ + key_length +=2; + } + + key_length += seg->length; + if (seg->length > 40) + ind->mi_fix_key = FALSE; + + /* Determine if only part of the field is in the key: + * This is important for index coverage! + * Note, BLOB fields are never retrieved from + * an index! + */ + if (field->type() == MYSQL_TYPE_BLOB) + partial_field = TRUE; + else if (field->real_type() == MYSQL_TYPE_VARCHAR // For varbinary type +#ifndef DRIZZLED + || field->real_type() == MYSQL_TYPE_VAR_STRING // For varbinary type + || field->real_type() == MYSQL_TYPE_STRING // For binary type +#endif + ) + { + Field *tab_field = table_arg->field[key_part->fieldnr-1]; + u_int field_len = tab_field->key_length(); + + if (key_part->length != field_len) + partial_field = TRUE; + } + + /* NOTE: do not set if the field is only partially in the index!!! */ + if (!partial_field) + MX_BIT_FAST_TEST_AND_SET(&ind->mi_col_map, field->field_index); + } + + if (key_length > XT_INDEX_MAX_KEY_SIZE) + xt_throw_sulxterr(XT_CONTEXT, XT_ERR_KEY_TOO_LARGE, index->name, (u_long) XT_INDEX_MAX_KEY_SIZE); + + /* This is the maximum size of the index on disk: */ + ind->mi_key_size = key_length; + ind->mi_max_items = (XT_INDEX_PAGE_SIZE-2) / (key_length+XT_RECORD_REF_SIZE); + + if (ind->mi_fix_key) { + /* Special case for not-NULL 4 byte int value: */ + switch (ind->mi_seg_count) { + case 1: + ind->mi_single_type = ind->mi_seg[0].type; + if (ind->mi_seg[0].type == HA_KEYTYPE_LONG_INT || + ind->mi_seg[0].type == HA_KEYTYPE_ULONG_INT) { + if (!(ind->mi_seg[0].flag & HA_NULL_PART)) + ind->mi_scan_branch = xt_scan_branch_single; + } + break; + case 2: + if (my_is_not_null_int4(&ind->mi_seg[0]) && + my_is_not_null_int4(&ind->mi_seg[1])) { + ind->mi_scan_branch = xt_scan_branch_fix_simple; + ind->mi_simple_comp_key = xt_compare_2_int4; + } + break; + case 3: + if (my_is_not_null_int4(&ind->mi_seg[0]) && + my_is_not_null_int4(&ind->mi_seg[1]) && + my_is_not_null_int4(&ind->mi_seg[2])) { + ind->mi_scan_branch = xt_scan_branch_fix_simple; + ind->mi_simple_comp_key = xt_compare_3_int4; + } + break; + } + if (!ind->mi_scan_branch) + ind->mi_scan_branch = xt_scan_branch_fix; + ind->mi_prev_item = xt_prev_branch_item_fix; + ind->mi_last_item = xt_last_branch_item_fix; + } + else { + ind->mi_scan_branch = xt_scan_branch_var; + ind->mi_prev_item = xt_prev_branch_item_var; + ind->mi_last_item = xt_last_branch_item_var; + } + ind->mi_lazy_delete = ind->mi_fix_key && ind->mi_max_items >= 4; + + XT_NODE_ID(ind->mi_root) = 0; + + popr_(); // Discard my_deref_index_data(ind) + + return_(ind); +} + +/* We estimate the size of BLOBs depending on the number + * of BLOBs in the table. + */ +static u_int mx_blob_field_size_total[] = { + 500, // 1 + 400, // 2 + 350, // 3 + 320, // 4 + 300, // 5 + 280, // 6 + 260, // 7 + 240, // 8 + 220, // 9 + 210 // 10 +}; + +static u_int mxvarchar_field_min_ave[] = { + 120, // 1 + 105, // 2 + 90, // 3 + 65, // 4 + 50, // 5 + 40, // 6 + 40, // 7 + 40, // 8 + 40, // 9 + 40 // 10 +}; + +xtPublic void myxt_setup_dictionary(XTThreadPtr self, XTDictionaryPtr dic) +{ + TABLE *my_tab = dic->dic_my_table; + u_int field_count; + u_int var_field_count = 0; + u_int varchar_field_count = 0; + u_int blob_field_count = 0; + u_int large_blob_field_count = 0; + xtWord8 min_data_size = 0; + xtWord8 max_data_size = 0; + xtWord8 ave_data_size = 0; + xtWord8 min_row_size = 0; + xtWord8 max_row_size = 0; + xtWord8 ave_row_size = 0; + xtWord8 min_ave_row_size = 0; + xtWord8 max_ave_row_size = 0; + u_int dic_rec_size; + xtBool dic_rec_fixed; + Field *curr_field; + Field **field; + + /* How many columns are required for all indexes. */ + KEY *index; + KEY_PART_INFO *key_part; + KEY_PART_INFO *key_part_end; + +#ifndef XT_USE_LAZY_DELETE + dic->dic_no_lazy_delete = TRUE; +#endif + + dic->dic_ind_cols_req = 0; + for (uint i=0; i<TS(my_tab)->keys; i++) { + index = &my_tab->key_info[i]; + + key_part_end = index->key_part + index->key_parts; + for (key_part = index->key_part; key_part != key_part_end; key_part++) { + curr_field = key_part->field; + + if ((u_int) curr_field->field_index+1 > dic->dic_ind_cols_req) + dic->dic_ind_cols_req = curr_field->field_index+1; + } + } + + /* We will work out how many columns are required for all blobs: */ + dic->dic_blob_cols_req = 0; + field_count = 0; + for (field=my_tab->field; (curr_field = *field); field++) { + field_count++; + min_data_size = curr_field->key_length(); + max_data_size = curr_field->key_length(); + enum_field_types tno = curr_field->type(); + + min_ave_row_size = 40; + max_ave_row_size = 128; + if (tno == MYSQL_TYPE_BLOB) { + blob_field_count++; + min_data_size = 0; + max_data_size = ((Field_blob *) curr_field)->max_data_length(); + /* Set the average length higher for BLOBs: */ + if (max_data_size == 0xFFFF || + max_data_size == 0xFFFFFF) { + if (large_blob_field_count < 10) + max_ave_row_size = mx_blob_field_size_total[large_blob_field_count]; + else + max_ave_row_size = 200; + large_blob_field_count++; + } + else if (max_data_size == 0xFFFFFFFF) { + /* Scale the estimated size of the blob depending on how many BLOBs + * are in the table! + */ + if (large_blob_field_count < 10) + max_ave_row_size = mx_blob_field_size_total[large_blob_field_count]; + else + max_ave_row_size = 200; + large_blob_field_count++; + if ((u_int) curr_field->field_index+1 > dic->dic_blob_cols_req) + dic->dic_blob_cols_req = curr_field->field_index+1; + dic->dic_blob_count++; + xt_realloc(self, (void **) &dic->dic_blob_cols, sizeof(Field *) * dic->dic_blob_count); + dic->dic_blob_cols[dic->dic_blob_count-1] = curr_field; + } + } + else if (tno == MYSQL_TYPE_VARCHAR +#ifndef DRIZZLED + || tno == MYSQL_TYPE_VAR_STRING +#endif + ) { + /* GOTCHA: MYSQL_TYPE_VAR_STRING does not exist as MYSQL_TYPE_VARCHAR define, but + * is used when creating a table with + * VARCHAR() + */ + min_data_size = 0; + if (varchar_field_count < 10) + min_ave_row_size = mxvarchar_field_min_ave[varchar_field_count]; + else + min_ave_row_size = 40; + varchar_field_count++; + } + + if (max_data_size == min_data_size) + ave_data_size = max_data_size; + else { + var_field_count++; + /* Take the average a 25% of the maximum: */ + ave_data_size = max_data_size / 4; + + /* Set the average based on min and max parameters: */ + if (ave_data_size < min_ave_row_size) + ave_data_size = min_ave_row_size; + else if (ave_data_size > max_ave_row_size) + ave_data_size = max_ave_row_size; + + if (ave_data_size > max_data_size) + ave_data_size = max_data_size; + } + + /* Add space for the length indicators: */ + if (min_data_size <= 240) + min_row_size += 1 + min_data_size; + else if (min_data_size <= 0xFFFF) + min_row_size += 3 + min_data_size; + else if (min_data_size <= 0xFFFFFF) + min_row_size += 4 + min_data_size; + else + min_row_size += 5 + min_data_size; + + if (max_data_size <= 240) + max_row_size += 1 + max_data_size; + else if (max_data_size <= 0xFFFF) + max_row_size += 3 + max_data_size; + else if (max_data_size <= 0xFFFFFF) + max_row_size += 4 + max_data_size; + else + max_row_size += 5 + max_data_size; + + if (ave_data_size <= 240) + ave_row_size += 1 + ave_data_size; + else /* Should not be more than this! */ + ave_row_size += 3 + ave_data_size; + + /* This is the length of the record required for all indexes: */ + /* This was calculated incorrectly. Not a serius bug because it + * is only used in the case of fixed length row, and in this + * case the dic_ind_rec_len is set correctly below. + */ + if (field_count == dic->dic_ind_cols_req) + dic->dic_ind_rec_len = max_row_size; + } + + dic->dic_min_row_size = min_row_size; + dic->dic_max_row_size = max_row_size; + dic->dic_ave_row_size = ave_row_size; + dic->dic_no_of_cols = field_count; + + if (dic->dic_def_ave_row_size) { + /* The average row size has been set: */ + dic_rec_size = offsetof(XTTabRecFix, rf_data) + TS(my_tab)->reclength; + + /* The conditions for a fixed record are: */ + if (dic->dic_def_ave_row_size >= (xtWord8) TS(my_tab)->reclength && + dic_rec_size <= XT_TAB_MAX_FIX_REC_LENGTH && + !blob_field_count) { + dic_rec_fixed = TRUE; + } + else { + xtWord8 new_rec_size; + + dic_rec_fixed = FALSE; + if (dic->dic_def_ave_row_size > max_row_size) + new_rec_size = offsetof(XTTabRecFix, rf_data) + max_row_size; + else + new_rec_size = offsetof(XTTabRecFix, rf_data) + dic->dic_def_ave_row_size; + + /* The maximum record size 64K for explicit AVG_ROW_LENGTH! */ + if (new_rec_size > XT_TAB_MAX_FIX_REC_LENGTH_SPEC) + new_rec_size = XT_TAB_MAX_FIX_REC_LENGTH_SPEC; + + dic_rec_size = (u_int) new_rec_size; + } + } + else { + /* If the average size is within 10% if of the maximum size, then we + * we handle these rows as fixed size rows. + * Fixed size rows use the internal MySQL format. + */ + dic_rec_size = offsetof(XTTabRecFix, rf_data) + TS(my_tab)->reclength; + /* Fixed length records must be less than 16K in size, + * have an average size which is very close (20%) to the maximum size or + * be less than a minimum size, + * and not contain any BLOBs: + */ + if (dic_rec_size <= XT_TAB_MAX_FIX_REC_LENGTH && + (ave_row_size + ave_row_size / 4 >= max_row_size || + dic_rec_size < XT_TAB_MIN_VAR_REC_LENGTH) && + !blob_field_count) { + dic_rec_fixed = TRUE; + } + else { + dic_rec_fixed = FALSE; + /* Note I add offsetof(XTTabRecFix, rf_data) insteard of + * offsetof(XTTabRecExt, re_data) here! + * The reason is that, we want to include the average size + * record in the fixed data part. To do this we only need to + * calculate a fixed header size, because in the cases in which + * it fits, we will only be using a fixed header! + */ + dic_rec_size = (u_int) (offsetof(XTTabRecFix, rf_data) + ave_row_size); + /* The maximum record size (16K for autorow sizing)! */ + if (dic_rec_size > XT_TAB_MAX_FIX_REC_LENGTH) + dic_rec_size = XT_TAB_MAX_FIX_REC_LENGTH; + } + } + + /* Ensure that handle data record size is big enough to + * include the extended record reference, in the case of + * variable length rows + */ + if (!dic_rec_fixed) { + if (dic_rec_size < offsetof(XTTabRecExtDRec, re_data)) + dic_rec_size = offsetof(XTTabRecExtDRec, re_data); + } +#ifdef DEBUG + else { + ASSERT_NS(dic_rec_size > offsetof(XTTabRecFix, rf_data)); + } +#endif + + if (!dic->dic_rec_size) { + dic->dic_rec_size = dic_rec_size; + dic->dic_rec_fixed = dic_rec_fixed; + } + else { + /* This just confirms that our original calculation on + * create table agrees with the current calculation. + * (i.e. if non-zero values were loaded from the table). + * + * It may be the criteria for calculating the data record size + * and whether to used a fixed or variable record has changed, + * but we need to stick to the current physical layout of the + * table. + * + * Note that this can occur in rename table when the + * method of calculation has changed. + * + * On rename, the format of the table does not change, so we + * will not take the calculated values. + */ + //ASSERT(dic->dic_rec_size == dic_rec_size); + //ASSERT(dic->dic_rec_fixed == dic_rec_fixed); + } + + if (dic_rec_fixed) { + /* Recalculate the length of the required required to address all + * index columns! + */ + if (field_count == dic->dic_ind_cols_req) + dic->dic_ind_rec_len = TS(my_tab)->reclength; + else { + field=my_tab->field; + + curr_field = field[dic->dic_ind_cols_req]; +#if MYSQL_VERSION_ID < 50114 + dic->dic_ind_rec_len = curr_field->offset(); +#else + dic->dic_ind_rec_len = curr_field->offset(curr_field->table->record[0]); +#endif + } + } + + /* We now calculate how many of the first columns in the row + * will definitely fit into the buffer, when the record is + * of type extended. + * + * In this way we can figure out if we need to load the extended + * record at all. + */ + dic->dic_fix_col_count = 0; + if (!dic_rec_fixed) { + xtWord8 max_rec_size = offsetof(XTTabRecExt, re_data); + + for (Field **f=my_tab->field; (curr_field = *f); f++) { + max_data_size = curr_field->key_length(); + enum_field_types tno = curr_field->type(); + if (tno == MYSQL_TYPE_BLOB) + max_data_size = ((Field_blob *) curr_field)->max_data_length(); + if (max_data_size <= 240) + max_rec_size += 1 + max_data_size; + else if (max_data_size <= 0xFFFF) + max_rec_size += 3 + max_data_size; + else if (max_data_size <= 0xFFFFFF) + max_rec_size += 4 + max_data_size; + else + max_rec_size += 5 + max_data_size; + if (max_rec_size > (xtWord8) dic_rec_size) + break; + dic->dic_fix_col_count++; + } + ASSERT(dic->dic_fix_col_count < dic->dic_no_of_cols); + } + + dic->dic_key_count = TS(my_tab)->keys; + dic->dic_mysql_buf_size = TS(my_tab)->rec_buff_length; + dic->dic_mysql_rec_size = TS(my_tab)->reclength; +} + +static u_int my_get_best_superset(XTThreadPtr XT_UNUSED(self), XTDictionaryPtr dic, XTIndexPtr ind) +{ + XTIndexPtr super_ind; + u_int super = 0; + u_int super_seg_count = ind->mi_seg_count; + + for (u_int i=0; i<dic->dic_key_count; i++) { + super_ind = dic->dic_keys[i]; + if (ind->mi_index_no != super_ind->mi_index_no && + super_seg_count < super_ind->mi_seg_count) { + for (u_int j=0; j<ind->mi_seg_count; j++) { + if (ind->mi_seg[j].col_idx != super_ind->mi_seg[j].col_idx) + goto next; + } + super_seg_count = super_ind->mi_seg_count; + super = i+1; + next:; + } + } + return super; +} + +/* + * Return FAILED if the MySQL dictionary is not available. + */ +xtPublic xtBool myxt_load_dictionary(XTThreadPtr self, XTDictionaryPtr dic, XTDatabaseHPtr db, XTPathStrPtr tab_path) +{ + TABLE *my_tab; + + if (!(my_tab = my_open_table(self, db, tab_path))) + return FAILED; + dic->dic_my_table = my_tab; +#ifdef DRIZZLED + dic->dic_def_ave_row_size = (xtWord8) my_tab->s->getAvgRowLength(); +#else + dic->dic_def_ave_row_size = (xtWord8) my_tab->s->avg_row_length; +#endif + myxt_setup_dictionary(self, dic); + dic->dic_keys = (XTIndexPtr *) xt_calloc(self, sizeof(XTIndexPtr) * TS(my_tab)->keys); + for (uint i=0; i<TS(my_tab)->keys; i++) + dic->dic_keys[i] = my_create_index(self, my_tab, i, &my_tab->key_info[i]); + + /* Check if any key is a subset of another: */ + for (u_int i=0; i<dic->dic_key_count; i++) + dic->dic_keys[i]->mi_subset_of = my_get_best_superset(self, dic, dic->dic_keys[i]); + + return OK; +} + +xtPublic void myxt_free_dictionary(XTThreadPtr self, XTDictionaryPtr dic) +{ + if (dic->dic_table) { + dic->dic_table->release(self); + dic->dic_table = NULL; + } + + if (dic->dic_my_table) { + my_close_table(dic->dic_my_table); + dic->dic_my_table = NULL; + } + + if (dic->dic_blob_cols) { + xt_free(self, dic->dic_blob_cols); + dic->dic_blob_cols = NULL; + } + dic->dic_blob_count = 0; + + /* If we have opened a table, then this data is freed with the dictionary: */ + if (dic->dic_keys) { + for (uint i=0; i<dic->dic_key_count; i++) { + if (dic->dic_keys[i]) + my_deref_index_data(self, (XTIndexPtr) dic->dic_keys[i]); + } + xt_free(self, dic->dic_keys); + dic->dic_key_count = 0; + dic->dic_keys = NULL; + } +} + +xtPublic void myxt_move_dictionary(XTDictionaryPtr dic, XTDictionaryPtr source_dic) +{ + dic->dic_my_table = source_dic->dic_my_table; + source_dic->dic_my_table = NULL; + + if (!dic->dic_rec_size) { + dic->dic_rec_size = source_dic->dic_rec_size; + dic->dic_rec_fixed = source_dic->dic_rec_fixed; + } + else { + /* This just confirms that our original calculation on + * create table agrees with the current calculation. + * (i.e. if non-zero values were loaded from the table). + * + * It may be the criteria for calculating the data record size + * and whether to used a fixed or variable record has changed, + * but we need to stick to the current physical layout of the + * table. + */ + ASSERT_NS(dic->dic_rec_size == source_dic->dic_rec_size); + ASSERT_NS(dic->dic_rec_fixed == source_dic->dic_rec_fixed); + } + + dic->dic_tab_flags = source_dic->dic_tab_flags; + dic->dic_blob_cols_req = source_dic->dic_blob_cols_req; + dic->dic_blob_count = source_dic->dic_blob_count; + dic->dic_blob_cols = source_dic->dic_blob_cols; + source_dic->dic_blob_cols = NULL; + + dic->dic_mysql_buf_size = source_dic->dic_mysql_buf_size; + dic->dic_mysql_rec_size = source_dic->dic_mysql_rec_size; + dic->dic_key_count = source_dic->dic_key_count; + dic->dic_keys = source_dic->dic_keys; + + /* Set this to zero, bcause later xt_flush_tables() may be called. + * This can occur when using the BLOB streaming engine, + * in command ALTER TABLE x ENGINE = PBXT; + */ + source_dic->dic_key_count = 0; + source_dic->dic_keys = NULL; + + dic->dic_min_row_size = source_dic->dic_min_row_size; + dic->dic_max_row_size = source_dic->dic_max_row_size; + dic->dic_ave_row_size = source_dic->dic_ave_row_size; + dic->dic_def_ave_row_size = source_dic->dic_def_ave_row_size; + + dic->dic_no_of_cols = source_dic->dic_no_of_cols; + dic->dic_fix_col_count = source_dic->dic_fix_col_count; + dic->dic_ind_cols_req = source_dic->dic_ind_cols_req; + dic->dic_ind_rec_len = source_dic->dic_ind_rec_len; +} + +static void my_free_dd_table(XTThreadPtr self, XTDDTable *dd_tab) +{ + if (dd_tab) + dd_tab->release(self); +} + +static void ha_create_dd_index(XTThreadPtr self, XTDDIndex *ind, KEY *key) +{ + KEY_PART_INFO *key_part; + KEY_PART_INFO *key_part_end; + XTDDColumnRef *cref; + + if (strcmp(key->name, "PRIMARY") == 0) + ind->co_type = XT_DD_KEY_PRIMARY; + else if (key->flags & HA_NOSAME) + ind->co_type = XT_DD_INDEX_UNIQUE; + else + ind->co_type = XT_DD_INDEX; + + if (ind->co_type == XT_DD_KEY_PRIMARY) + ind->co_name = xt_dup_string(self, key->name); + else + ind->co_ind_name = xt_dup_string(self, key->name); + + key_part_end = key->key_part + key->key_parts; + for (key_part = key->key_part; key_part != key_part_end; key_part++) { + if (!(cref = new XTDDColumnRef())) + xt_throw_errno(XT_CONTEXT, XT_ENOMEM); + cref->init(self); + ind->co_cols.append(self, cref); + cref->cr_col_name = xt_dup_string(self, (char *) key_part->field->field_name); + } +} + +static char *my_type_to_string(XTThreadPtr self, Field *field, TABLE *XT_UNUSED(my_tab)) +{ + char buffer[MAX_FIELD_WIDTH + 400]; + const char *ptr; + String type((char *) buffer, sizeof(buffer), system_charset_info); + xtWord4 len; + + /* GOTCHA: + * - Above sets the string length to the same as the buffer, + * so we must set the length to zero. + * - The result is not necessarilly zero terminated. + * - We cannot assume that the input buffer is the one + * we get back (for example text field). + */ + type.length(0); + field->sql_type(type); + ptr = type.ptr(); + len = type.length(); + + if (len >= sizeof(buffer)) + len = sizeof(buffer)-1; + + if (ptr != buffer) + xt_strcpy(sizeof(buffer), buffer, ptr); + + buffer[len] = 0; + + if (field->has_charset()) { + /* Always include the charset so that we can compare types + * for FK/PK releations. + */ + xt_strcat(sizeof(buffer), buffer, " CHARACTER SET "); + xt_strcat(sizeof(buffer), buffer, (char *) field->charset()->csname); + + /* For string types dump collation name only if + * collation is not primary for the given charset + */ + if (!(field->charset()->state & MY_CS_PRIMARY)) { + xt_strcat(sizeof(buffer), buffer, " COLLATE "); + xt_strcat(sizeof(buffer), buffer, (char *) field->charset()->name); + } + } + + return xt_dup_string(self, buffer); // type.length() +} + +xtPublic XTDDTable *myxt_create_table_from_table(XTThreadPtr self, TABLE *my_tab) +{ + XTDDTable *dd_tab; + Field *curr_field; + XTDDColumn *col; + XTDDIndex *ind; + + if (!(dd_tab = new XTDDTable())) + xt_throw_errno(XT_CONTEXT, XT_ENOMEM); + dd_tab->init(self); + pushr_(my_free_dd_table, dd_tab); + + for (Field **field=my_tab->field; (curr_field = *field); field++) { + col = XTDDColumnFactory::createFromMySQLField(self, my_tab, curr_field); + dd_tab->dt_cols.append(self, col); + } + + for (uint i=0; i<TS(my_tab)->keys; i++) { + if (!(ind = (XTDDIndex *) new XTDDIndex(XT_DD_UNKNOWN))) + xt_throw_errno(XT_CONTEXT, XT_ENOMEM); + dd_tab->dt_indexes.append(self, ind); + ind->co_table = dd_tab; + ind->in_index = i; + ha_create_dd_index(self, ind, &my_tab->key_info[i]); + } + + popr_(); // my_free_dd_table(dd_tab) + return dd_tab; +} + +/* + * ----------------------------------------------------------------------- + * MySQL CHARACTER UTILITIES + */ + +xtPublic void myxt_static_convert_identifier(XTThreadPtr XT_UNUSED(self), MX_CHARSET_INFO *cs, char *from, char *to, size_t to_len) +{ +#ifdef DRIZZLED + ((void *)cs); + xt_strcpy(to_len, to, from); +#else + uint errors; + + /* + * Bug#4417 + * Check that identifiers and strings are not converted + * when the client character set is binary. + */ + if (cs == &my_charset_utf8_general_ci || cs == &my_charset_bin) + xt_strcpy(to_len, to, from); + else + strconvert(cs, from, &my_charset_utf8_general_ci, to, to_len, &errors); +#endif +} + +// cs == current_thd->charset() +xtPublic char *myxt_convert_identifier(XTThreadPtr self, MX_CHARSET_INFO *cs, char *from) +{ +#ifdef DRIZZLED + char *to = xt_dup_string(self, from); + ((void *)cs); +#else + uint errors; + u_int len; + char *to; + + if (cs == &my_charset_utf8_general_ci || cs == &my_charset_bin) + to = xt_dup_string(self, from); + else { + len = strlen(from) * 3 + 1; + to = (char *) xt_malloc(self, len); + strconvert(cs, from, &my_charset_utf8_general_ci, to, len, &errors); + } +#endif + return to; +} + +xtPublic char *myxt_convert_table_name(XTThreadPtr self, char *from) +{ + u_int len; + char *to; + + len = strlen(from) * 5 + 1; + to = (char *) xt_malloc(self, len); + tablename_to_filename(from, to, len); + return to; +} + +xtPublic void myxt_static_convert_table_name(XTThreadPtr XT_UNUSED(self), char *from, char *to, size_t to_len) +{ + tablename_to_filename(from, to, to_len); +} + +xtPublic void myxt_static_convert_file_name(char *from, char *to, size_t to_len) +{ + filename_to_tablename(from, to, to_len); +} + +xtPublic int myxt_strcasecmp(char * a, char *b) +{ + return my_strcasecmp(&my_charset_utf8_general_ci, a, b); +} + +xtPublic int myxt_isspace(MX_CHARSET_INFO *cs, char a) +{ + return my_isspace(cs, a); +} + +xtPublic int myxt_ispunct(MX_CHARSET_INFO *cs, char a) +{ + return my_ispunct(cs, a); +} + +xtPublic int myxt_isdigit(MX_CHARSET_INFO *cs, char a) +{ + return my_isdigit(cs, a); +} + +xtPublic MX_CHARSET_INFO *myxt_getcharset(bool convert) +{ + if (convert) { + THD *thd = current_thd; + + if (thd) + return (MX_CHARSET_INFO *)thd_charset(thd); + } + return (MX_CHARSET_INFO *)&my_charset_utf8_general_ci; +} + +xtPublic void *myxt_create_thread() +{ +#ifdef DRIZZLED + return (void *) 1; +#else + THD *new_thd; + + if (my_thread_init()) { + xt_register_error(XT_REG_CONTEXT, XT_ERR_MYSQL_ERROR, 0, "Unable to initialize MySQL threading"); + return NULL; + } + + /* + * Unfortunately, if PBXT is the default engine, and we are shutting down + * then global_system_variables.table_plugin may be NULL. Which will cause + * a crash if we try to create a thread! + * + * The following call in plugin_shutdown() sets the global reference + * to NULL: + * + * unlock_variables(NULL, &global_system_variables); + * + * Later plugin_deinitialize() is called. + * + * The following stack is an example crash which occurs when I call + * myxt_create_thread() in ha_exit(), to force the error. + * + * if (pi->state & (PLUGIN_IS_READY | PLUGIN_IS_UNINITIALIZED)) + * pi is NULL! + * #0 0x002ff684 in intern_plugin_lock at sql_plugin.cc:617 + * #1 0x0030296d in plugin_thdvar_init at sql_plugin.cc:2432 + * #2 0x000db4a4 in THD::init at sql_class.cc:756 + * #3 0x000e02ed in THD::THD at sql_class.cc:638 + * #4 0x00e2678d in myxt_create_thread at myxt_xt.cc:2990 + * #5 0x00e05d43 in ha_exit at ha_pbxt.cc:1011 + * #6 0x00e065c2 in pbxt_end at ha_pbxt.cc:1330 + * #7 0x00e065df in pbxt_panic at ha_pbxt.cc:1343 + * #8 0x0023e57d in ha_finalize_handlerton at handler.cc:392 + * #9 0x002ffc8b in plugin_deinitialize at sql_plugin.cc:816 + * #10 0x003037d9 in plugin_shutdown at sql_plugin.cc:1572 + * #11 0x000f7b2b in clean_up at mysqld.cc:1266 + * #12 0x000f7fca in unireg_end at mysqld.cc:1192 + * #13 0x000fa021 in kill_server at mysqld.cc:1134 + * #14 0x000fa6df in kill_server_thread at mysqld.cc:1155 + * #15 0x91fdb155 in _pthread_start + * #16 0x91fdb012 in thread_start + */ + if (!global_system_variables.table_plugin) { + xt_register_xterr(XT_REG_CONTEXT, XT_ERR_MYSQL_NO_THREAD); + return NULL; + } + + if (!(new_thd = new THD)) { + my_thread_end(); + xt_register_error(XT_REG_CONTEXT, XT_ERR_MYSQL_ERROR, 0, "Unable to create MySQL thread (THD)"); + return NULL; + } + + /* + * If PBXT is the default storage engine, then creating any THD objects will add extra + * references to the PBXT plugin object. because the threads are created but PBXT + * this creates a self reference, and the reference count does not go to zero + * on shutdown. + * + * The server then issues a message that it is forcing shutdown of the plugin. + * + * However, the engine reference is not required by the THDs used by PBXT, so + * I just remove them here. + */ + plugin_unlock(NULL, new_thd->variables.table_plugin); + new_thd->variables.table_plugin = NULL; + + new_thd->thread_stack = (char *) &new_thd; + new_thd->store_globals(); + lex_start(new_thd); + + return (void *) new_thd; +#endif +} + +#ifdef DRIZZLED +xtPublic void myxt_destroy_thread(void *, xtBool) +{ +} + +xtPublic void myxt_delete_remaining_thread() +{ +} +#else +xtPublic void myxt_destroy_thread(void *thread, xtBool end_threads) +{ + THD *thd = (THD *) thread; + +#if MYSQL_VERSION_ID > 60005 + /* PMC - This is a HACK! It is required because + * MySQL shuts down MDL before shutting down the + * plug-ins. + */ + if (!pbxt_inited) + mdl_init(); + close_thread_tables(thd); + if (!pbxt_inited) + mdl_destroy(); +#else + close_thread_tables(thd); +#endif + + delete thd; + + /* Remember that we don't have a THD */ + my_pthread_setspecific_ptr(THR_THD, 0); + + if (end_threads) + my_thread_end(); +} + +xtPublic void myxt_delete_remaining_thread() +{ + THD *thd; + + if ((thd = current_thd)) + myxt_destroy_thread((void *) thd, TRUE); +} +#endif + +xtPublic XTThreadPtr myxt_get_self() +{ + THD *thd; + + if ((thd = current_thd)) + return xt_ha_thd_to_self(thd); + return NULL; +} + +/* + * ----------------------------------------------------------------------- + * INFORMATION SCHEMA FUNCTIONS + * + */ + +static int mx_put_record(THD *thd, TABLE *table) +{ + return schema_table_store_record(thd, table); +} + +#ifdef UNUSED_CODE +static void mx_put_int(TABLE *table, int column, int value) +{ + table->field[column]->store(value, false); +} + +static void mx_put_real8(TABLE *table, int column, xtReal8 value) +{ + table->field[column]->store(value); +} + +static void mx_put_string(TABLE *table, int column, const char *string, u_int len, charset_info_st *charset) +{ + table->field[column]->store(string, len, charset); +} +#endif + +static void mx_put_u_llong(TABLE *table, int column, u_llong value) +{ + table->field[column]->store(value, false); +} + +static void mx_put_string(TABLE *table, int column, const char *string, charset_info_st *charset) +{ + table->field[column]->store(string, strlen(string), charset); +} + +xtPublic int myxt_statistics_fill_table(XTThreadPtr self, void *th, void *ta, void *, MX_CONST void *ch) +{ + THD *thd = (THD *) th; + TABLE_LIST *tables = (TABLE_LIST *) ta; + charset_info_st *charset = (charset_info_st *) ch; + TABLE *table = (TABLE *) tables->table; + int err = 0; + int col; + const char *stat_name; + u_llong stat_value; + XTStatisticsRec statistics; + XTDatabaseHPtr db = self->st_database; + + xt_gather_statistics(&statistics); + for (u_int rec_id=0; !err && rec_id<XT_STAT_CURRENT_MAX; rec_id++) { + stat_name = xt_get_stat_meta_data(rec_id)->sm_name; + stat_value = xt_get_statistic(&statistics, db, rec_id); + + col=0; + mx_put_u_llong(table, col++, rec_id+1); + mx_put_string(table, col++, stat_name, charset); + mx_put_u_llong(table, col++, stat_value); + err = mx_put_record(thd, table); + } + + return err; +} + +xtPublic void myxt_get_status(XTThreadPtr self, XTStringBufferPtr strbuf) +{ + char string[200]; + + xt_sb_concat(self, strbuf, "\n"); + xt_get_now(string, 200); + xt_sb_concat(self, strbuf, string); + xt_sb_concat(self, strbuf, " PBXT "); + xt_sb_concat(self, strbuf, xt_get_version()); + xt_sb_concat(self, strbuf, " STATUS OUTPUT"); + xt_sb_concat(self, strbuf, "\n"); + + xt_sb_concat(self, strbuf, "Record cache usage: "); + xt_sb_concat_int8(self, strbuf, xt_tc_get_usage()); + xt_sb_concat(self, strbuf, "\n"); + xt_sb_concat(self, strbuf, "Record cache size: "); + xt_sb_concat_int8(self, strbuf, xt_tc_get_size()); + xt_sb_concat(self, strbuf, "\n"); + xt_sb_concat(self, strbuf, "Record cache high: "); + xt_sb_concat_int8(self, strbuf, xt_tc_get_high()); + xt_sb_concat(self, strbuf, "\n"); + xt_sb_concat(self, strbuf, "Index cache usage: "); + xt_sb_concat_int8(self, strbuf, xt_ind_get_usage()); + xt_sb_concat(self, strbuf, "\n"); + xt_sb_concat(self, strbuf, "Index cache size: "); + xt_sb_concat_int8(self, strbuf, xt_ind_get_size()); + xt_sb_concat(self, strbuf, "\n"); + xt_sb_concat(self, strbuf, "Log cache usage: "); + xt_sb_concat_int8(self, strbuf, xt_xlog_get_usage()); + xt_sb_concat(self, strbuf, "\n"); + xt_sb_concat(self, strbuf, "Log cache size: "); + xt_sb_concat_int8(self, strbuf, xt_xlog_get_size()); + xt_sb_concat(self, strbuf, "\n"); + + xt_ht_lock(self, xt_db_open_databases); + pushr_(xt_ht_unlock, xt_db_open_databases); + + XTDatabaseHPtr *dbptr; + size_t len = xt_sl_get_size(xt_db_open_db_by_id); + + if (len > 0) { + xt_sb_concat(self, strbuf, "Data log files:\n"); + for (u_int i=0; i<len; i++) { + dbptr = (XTDatabaseHPtr *) xt_sl_item_at(xt_db_open_db_by_id, i); + +#ifndef XT_USE_GLOBAL_DB + xt_sb_concat(self, strbuf, "Database: "); + xt_sb_concat(self, strbuf, (*dbptr)->db_name); + xt_sb_concat(self, strbuf, "\n"); +#endif + xt_dl_log_status(self, *dbptr, strbuf); + } + } + else + xt_sb_concat(self, strbuf, "No data logs in use\n"); + + freer_(); // xt_ht_unlock(xt_db_open_databases) +} + +/* + * ----------------------------------------------------------------------- + * MySQL Bit Maps + */ + +static void myxt_bitmap_init(XTThreadPtr self, MX_BITMAP *map, u_int n_bits) +{ + my_bitmap_map *buf; + uint size_in_bytes = (((n_bits) + 31) / 32) * 4; + + buf = (my_bitmap_map *) xt_malloc(self, size_in_bytes); + +#ifdef DRIZZLED + map->init(buf, n_bits); +#else + map->bitmap= buf; + map->n_bits= n_bits; + create_last_word_mask(map); + bitmap_clear_all(map); +#endif +} + +static void myxt_bitmap_free(XTThreadPtr self, MX_BITMAP *map) +{ +#ifdef DRIZZLED + my_bitmap_map *buf = map->getBitmap(); + if (buf) + xt_free(self, buf); + map->setBitmap(NULL); +#else + if (map->bitmap) { + xt_free(self, map->bitmap); + map->bitmap = NULL; + } +#endif +} + +/* + * ----------------------------------------------------------------------- + * XTDDColumnFactory methods + */ + +XTDDColumn *XTDDColumnFactory::createFromMySQLField(XTThread *self, TABLE *my_tab, Field *field) +{ + XTDDEnumerableColumn *en_col; + XTDDColumn *col; + xtBool is_enum = FALSE; + + switch(field->real_type()) { + case MYSQL_TYPE_ENUM: + is_enum = TRUE; + /* fallthrough */ + +#ifndef DRIZZLED + case MYSQL_TYPE_SET: +#endif + col = en_col = new XTDDEnumerableColumn(); + if (!col) + xt_throw_errno(XT_CONTEXT, XT_ENOMEM); + col->init(self); + en_col->enum_size = ((Field_enum *)field)->typelib->count; + en_col->is_enum = is_enum; + break; + + default: + col = new XTDDColumn(); + if (!col) + xt_throw_errno(XT_CONTEXT, XT_ENOMEM); + col->init(self); + } + + col->dc_name = xt_dup_string(self, (char *) field->field_name); + col->dc_data_type = my_type_to_string(self, field, my_tab); + col->dc_null_ok = field->null_ptr != NULL; + + return col; +} + +/* + * ----------------------------------------------------------------------- + * utilities + */ + +/* + * MySQL (not sure about Drizzle) first calls hton->init and then assigns the plugin a thread slot + * which is used by xt_get_self(). This is a problem as pbxt_init() starts a number of daemon threads + * which could try to use the slot before it is assigned. This code waits till slot is inited. + * We cannot directly check hton->slot as in some versions of MySQL it can be 0 before init which is a + * valid value. + */ +extern ulong total_ha; + +xtPublic void myxt_wait_pbxt_plugin_slot_assigned(XTThread *self) +{ +#ifdef DRIZZLED + static LEX_STRING plugin_name = { C_STRING_WITH_LEN("PBXT") }; + + while (!self->t_quit && !Registry::singleton().find(&plugin_name)) + xt_sleep_milli_second(1); +#else + while(!self->t_quit && (pbxt_hton->slot >= total_ha)) + xt_sleep_milli_second(1); +#endif +} |