in storage/myisam/mi_create.cc [55:774]
int mi_create(const char *name, uint keys, MI_KEYDEF *keydefs, uint columns,
MI_COLUMNDEF *recinfo, uint uniques, MI_UNIQUEDEF *uniquedefs,
MI_CREATE_INFO *ci, uint flags) {
uint i, j;
File dfile = 0, file = 0;
int errpos, save_errno, create_mode = O_RDWR | O_TRUNC;
myf create_flag;
uint fields, length, max_key_length, packed, pointer, real_length_diff,
key_length, info_length, key_segs, options, min_key_length_skip, base_pos,
long_varchar_count, varchar_length, max_key_block_length,
unique_key_parts, fulltext_keys, offset;
uint aligned_key_start;
uint internal_table = flags & HA_CREATE_INTERNAL_TABLE;
ulong reclength, real_reclength, min_pack_length;
char filename[FN_REFLEN], linkname[FN_REFLEN], *linkname_ptr;
ulong pack_reclength;
ulonglong tot_length, max_rows, tmp;
enum en_fieldtype type;
MYISAM_SHARE share;
MI_KEYDEF *keydef, tmp_keydef;
MI_UNIQUEDEF *uniquedef;
HA_KEYSEG *keyseg, tmp_keyseg;
MI_COLUMNDEF *rec;
ulong *rec_per_key_part;
my_off_t key_root[HA_MAX_POSSIBLE_KEY], key_del[MI_MAX_KEY_BLOCK_SIZE];
MI_CREATE_INFO tmp_create_info;
DBUG_TRACE;
DBUG_PRINT("enter", ("keys: %u columns: %u uniques: %u flags: %u", keys,
columns, uniques, flags));
if (!ci) {
memset(&tmp_create_info, 0, sizeof(tmp_create_info));
ci = &tmp_create_info;
}
if (keys + uniques > MI_MAX_KEY || columns == 0) {
set_my_errno(HA_WRONG_CREATE_OPTION);
return HA_WRONG_CREATE_OPTION;
}
errpos = 0;
options = 0;
memset(&share, 0, sizeof(share));
if (flags & HA_DONT_TOUCH_DATA) {
if (!(ci->old_options & HA_OPTION_TEMP_COMPRESS_RECORD))
options =
ci->old_options & (HA_OPTION_COMPRESS_RECORD | HA_OPTION_PACK_RECORD |
HA_OPTION_READ_ONLY_DATA | HA_OPTION_CHECKSUM |
HA_OPTION_TMP_TABLE | HA_OPTION_DELAY_KEY_WRITE);
else
options = ci->old_options & (HA_OPTION_CHECKSUM | HA_OPTION_TMP_TABLE |
HA_OPTION_DELAY_KEY_WRITE);
}
if (ci->reloc_rows > ci->max_rows)
ci->reloc_rows = ci->max_rows; /* Check if wrong parameter */
if (!(rec_per_key_part =
(ulong *)my_malloc(mi_key_memory_MYISAM_SHARE,
(keys + uniques) * MI_MAX_KEY_SEG * sizeof(long),
MYF(MY_WME | MY_ZEROFILL))))
return my_errno();
/* Start by checking fields and field-types used */
reclength = varchar_length = long_varchar_count = packed = min_pack_length =
pack_reclength = 0;
for (rec = recinfo, fields = 0; fields != columns; rec++, fields++) {
reclength += rec->length;
if ((type = (enum en_fieldtype)rec->type) != FIELD_NORMAL &&
type != FIELD_CHECK) {
packed++;
if (type == FIELD_BLOB) {
share.base.blobs++;
if (pack_reclength != INT_MAX32) {
if (rec->length == 4 + portable_sizeof_char_ptr)
pack_reclength = INT_MAX32;
else
pack_reclength += (1 << ((rec->length - portable_sizeof_char_ptr) *
8)); /* Max blob length */
}
} else if (type == FIELD_SKIP_PRESPACE || type == FIELD_SKIP_ENDSPACE) {
if (pack_reclength != INT_MAX32)
pack_reclength += rec->length > 255 ? 2 : 1;
min_pack_length++;
} else if (type == FIELD_VARCHAR) {
varchar_length += rec->length - 1; /* Used for min_pack_length */
packed--;
pack_reclength++;
min_pack_length++;
/* We must test for 257 as length includes pack-length */
if (rec->length >= 257) {
long_varchar_count++;
pack_reclength += 2; /* May be packed on 3 bytes */
}
} else if (type != FIELD_SKIP_ZERO) {
min_pack_length += rec->length;
packed--; /* Not a pack record type */
}
} else /* FIELD_NORMAL */
min_pack_length += rec->length;
}
if ((packed & 7) == 1) { /* Bad packing, try to remove a zero-field */
while (rec != recinfo) {
rec--;
if (rec->type == (int)FIELD_SKIP_ZERO && rec->length == 1) {
/*
NOTE1: here we change a field type FIELD_SKIP_ZERO ->
FIELD_NORMAL
*/
rec->type = (int)FIELD_NORMAL;
packed--;
min_pack_length++;
break;
}
}
}
if (packed || (flags & HA_PACK_RECORD))
options |= HA_OPTION_PACK_RECORD; /* Must use packed records */
/* We can't use checksum with static length rows */
if (!(options & HA_OPTION_PACK_RECORD)) options &= ~HA_OPTION_CHECKSUM;
if (!(options & (HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)))
min_pack_length += varchar_length;
if (flags & HA_CREATE_TMP_TABLE) {
options |= HA_OPTION_TMP_TABLE;
create_mode |= O_EXCL | O_NOFOLLOW;
}
if (flags & HA_CREATE_CHECKSUM || (options & HA_OPTION_CHECKSUM)) {
options |= HA_OPTION_CHECKSUM;
min_pack_length++;
}
if (flags & HA_CREATE_DELAY_KEY_WRITE) options |= HA_OPTION_DELAY_KEY_WRITE;
if (flags & HA_CREATE_RELIES_ON_SQL_LAYER)
options |= HA_OPTION_RELIES_ON_SQL_LAYER;
packed = (packed + 7) / 8;
if (pack_reclength != INT_MAX32)
pack_reclength +=
reclength + packed +
test_all_bits(options, HA_OPTION_CHECKSUM | HA_OPTION_PACK_RECORD);
min_pack_length += packed;
if (!ci->data_file_length && ci->max_rows) {
if (pack_reclength == INT_MAX32 ||
(~(ulonglong)0) / ci->max_rows < (ulonglong)pack_reclength)
ci->data_file_length = ~(ulonglong)0;
else
ci->data_file_length = (ulonglong)ci->max_rows * pack_reclength;
} else if (!ci->max_rows)
ci->max_rows = (ha_rows)(ci->data_file_length /
(min_pack_length +
((options & HA_OPTION_PACK_RECORD) ? 3 : 0)));
if (options & (HA_OPTION_COMPRESS_RECORD | HA_OPTION_PACK_RECORD))
pointer =
mi_get_pointer_length(ci->data_file_length, myisam_data_pointer_size);
else
pointer = mi_get_pointer_length(ci->max_rows, myisam_data_pointer_size);
if (!(max_rows = (ulonglong)ci->max_rows))
max_rows = ((((ulonglong)1 << (pointer * 8)) - 1) / min_pack_length);
real_reclength = reclength;
if (!(options & (HA_OPTION_COMPRESS_RECORD | HA_OPTION_PACK_RECORD))) {
if (reclength <= pointer)
reclength = pointer + 1; /* reserve place for delete link */
} else
reclength += long_varchar_count; /* We need space for varchar! */
max_key_length = 0;
tot_length = 0;
key_segs = 0;
fulltext_keys = 0;
max_key_block_length = 0;
share.state.rec_per_key_part = rec_per_key_part;
share.state.key_root = key_root;
share.state.key_del = key_del;
if (uniques) {
max_key_block_length = myisam_block_size;
max_key_length = MI_UNIQUE_HASH_LENGTH + pointer;
}
for (i = 0, keydef = keydefs; i < keys; i++, keydef++) {
share.state.key_root[i] = HA_OFFSET_ERROR;
min_key_length_skip = length = real_length_diff = 0;
key_length = pointer;
if (keydef->flag & HA_SPATIAL) {
/* BAR TODO to support 3D and more dimensions in the future */
uint sp_segs = SPDIMS * 2;
keydef->flag = HA_SPATIAL;
if (flags & HA_DONT_TOUCH_DATA) {
/*
called by myisamchk - i.e. table structure was taken from
MYI file and SPATIAL key *does have* additional sp_segs keysegs.
keydef->seg here points right at the GEOMETRY segment,
so we only need to decrease keydef->keysegs.
(see recreate_table() in mi_check.c)
*/
keydef->keysegs -= sp_segs - 1;
}
for (j = 0, keyseg = keydef->seg; (int)j < keydef->keysegs;
j++, keyseg++) {
if (keyseg->type != HA_KEYTYPE_BINARY &&
keyseg->type != HA_KEYTYPE_VARBINARY1 &&
keyseg->type != HA_KEYTYPE_VARBINARY2) {
set_my_errno(HA_WRONG_CREATE_OPTION);
goto err_no_lock;
}
}
keydef->keysegs += sp_segs;
key_length += SPLEN * sp_segs;
length++; /* At least one length byte */
min_key_length_skip += SPLEN * 2 * SPDIMS;
} else if (keydef->flag & HA_FULLTEXT) {
keydef->flag = HA_FULLTEXT | HA_PACK_KEY | HA_VAR_LENGTH_KEY;
options |= HA_OPTION_PACK_KEYS; /* Using packed keys */
for (j = 0, keyseg = keydef->seg; (int)j < keydef->keysegs;
j++, keyseg++) {
if (keyseg->type != HA_KEYTYPE_TEXT &&
keyseg->type != HA_KEYTYPE_VARTEXT1 &&
keyseg->type != HA_KEYTYPE_VARTEXT2) {
set_my_errno(HA_WRONG_CREATE_OPTION);
goto err_no_lock;
}
if (!(keyseg->flag & HA_BLOB_PART) &&
(keyseg->type == HA_KEYTYPE_VARTEXT1 ||
keyseg->type == HA_KEYTYPE_VARTEXT2)) {
/* Make a flag that this is a VARCHAR */
keyseg->flag |= HA_VAR_LENGTH_PART;
/* Store in bit_start number of bytes used to pack the length */
keyseg->bit_start = ((keyseg->type == HA_KEYTYPE_VARTEXT1) ? 1 : 2);
}
}
fulltext_keys++;
key_length += HA_FT_MAXBYTELEN + HA_FT_WLEN;
length++; /* At least one length byte */
min_key_length_skip += HA_FT_MAXBYTELEN;
real_length_diff = HA_FT_MAXBYTELEN - FT_MAX_WORD_LEN_FOR_SORT;
} else {
/* Test if prefix compression */
if (keydef->flag & HA_PACK_KEY) {
/* Can't use space_compression on number keys */
if ((keydef->seg[0].flag & HA_SPACE_PACK) &&
keydef->seg[0].type == (int)HA_KEYTYPE_NUM)
keydef->seg[0].flag &= ~HA_SPACE_PACK;
/* Only use HA_PACK_KEY when first segment is a variable length key */
if (!(keydef->seg[0].flag &
(HA_SPACE_PACK | HA_BLOB_PART | HA_VAR_LENGTH_PART))) {
/* pack relative to previous key */
keydef->flag &= ~HA_PACK_KEY;
keydef->flag |= HA_BINARY_PACK_KEY | HA_VAR_LENGTH_KEY;
} else {
keydef->seg[0].flag |= HA_PACK_KEY; /* for easier intern test */
keydef->flag |= HA_VAR_LENGTH_KEY;
options |= HA_OPTION_PACK_KEYS; /* Using packed keys */
}
}
if (keydef->flag & HA_BINARY_PACK_KEY)
options |= HA_OPTION_PACK_KEYS; /* Using packed keys */
if (keydef->flag & HA_AUTO_KEY && ci->with_auto_increment)
share.base.auto_key = i + 1;
for (j = 0, keyseg = keydef->seg; j < keydef->keysegs; j++, keyseg++) {
/* numbers are stored with high by first to make compression easier */
switch (keyseg->type) {
case HA_KEYTYPE_SHORT_INT:
case HA_KEYTYPE_LONG_INT:
case HA_KEYTYPE_FLOAT:
case HA_KEYTYPE_DOUBLE:
case HA_KEYTYPE_USHORT_INT:
case HA_KEYTYPE_ULONG_INT:
case HA_KEYTYPE_LONGLONG:
case HA_KEYTYPE_ULONGLONG:
case HA_KEYTYPE_INT24:
case HA_KEYTYPE_UINT24:
case HA_KEYTYPE_INT8:
keyseg->flag |= HA_SWAP_KEY;
break;
case HA_KEYTYPE_VARTEXT1:
case HA_KEYTYPE_VARTEXT2:
case HA_KEYTYPE_VARBINARY1:
case HA_KEYTYPE_VARBINARY2:
if (!(keyseg->flag & HA_BLOB_PART)) {
/* Make a flag that this is a VARCHAR */
keyseg->flag |= HA_VAR_LENGTH_PART;
/* Store in bit_start number of bytes used to pack the length */
keyseg->bit_start = ((keyseg->type == HA_KEYTYPE_VARTEXT1 ||
keyseg->type == HA_KEYTYPE_VARBINARY1)
? 1
: 2);
}
break;
default:
break;
}
if (keyseg->flag & HA_SPACE_PACK) {
assert(!(keyseg->flag & HA_VAR_LENGTH_PART));
keydef->flag |= HA_SPACE_PACK_USED | HA_VAR_LENGTH_KEY;
options |= HA_OPTION_PACK_KEYS; /* Using packed keys */
length++; /* At least one length byte */
min_key_length_skip += keyseg->length;
if (keyseg->length >= 255) { /* prefix may be 3 bytes */
min_key_length_skip += 2;
length += 2;
}
}
if (keyseg->flag & (HA_VAR_LENGTH_PART | HA_BLOB_PART)) {
assert(!test_all_bits(keyseg->flag,
(HA_VAR_LENGTH_PART | HA_BLOB_PART)));
keydef->flag |= HA_VAR_LENGTH_KEY;
length++; /* At least one length byte */
options |= HA_OPTION_PACK_KEYS; /* Using packed keys */
min_key_length_skip += keyseg->length;
if (keyseg->length >= 255) { /* prefix may be 3 bytes */
min_key_length_skip += 2;
length += 2;
}
}
key_length += keyseg->length;
if (keyseg->null_bit) {
key_length++;
options |= HA_OPTION_PACK_KEYS;
keyseg->flag |= HA_NULL_PART;
keydef->flag |= HA_VAR_LENGTH_KEY | HA_NULL_PART_KEY;
}
}
} /* if HA_FULLTEXT */
key_segs += keydef->keysegs;
if (keydef->keysegs > MI_MAX_KEY_SEG) {
set_my_errno(HA_WRONG_CREATE_OPTION);
goto err_no_lock;
}
/*
key_segs may be 0 in the case when we only want to be able to
add on row into the table. This can happen with some DISTINCT queries
in MySQL
*/
if ((keydef->flag & (HA_NOSAME | HA_NULL_PART_KEY)) == HA_NOSAME &&
key_segs)
share.state.rec_per_key_part[key_segs - 1] = 1L;
length += key_length;
/* Get block length for key, if defined by user */
uint block_length =
keydef->block_length != 0 ? keydef->block_length : myisam_block_size;
block_length = std::clamp(std::bit_ceil(block_length),
MI_MIN_KEY_BLOCK_LENGTH, MI_MAX_KEY_BLOCK_LENGTH);
keydef->block_length = (uint16)MI_BLOCK_SIZE(
length - real_length_diff, pointer, MI_MAX_KEYPTR_SIZE, block_length);
if (keydef->block_length > MI_MAX_KEY_BLOCK_LENGTH ||
length >= MI_MAX_KEY_BUFF) {
set_my_errno(HA_WRONG_CREATE_OPTION);
goto err_no_lock;
}
max_key_block_length =
std::max(max_key_block_length, uint(keydef->block_length));
keydef->keylength = (uint16)key_length;
keydef->minlength = (uint16)(length - min_key_length_skip);
keydef->maxlength = (uint16)length;
if (length > max_key_length) max_key_length = length;
tot_length +=
(max_rows / (ulong)(((uint)keydef->block_length - 5) / (length * 2))) *
(ulong)keydef->block_length;
}
for (i = max_key_block_length / MI_MIN_KEY_BLOCK_LENGTH; i--;)
key_del[i] = HA_OFFSET_ERROR;
unique_key_parts = 0;
for (i = 0, uniquedef = uniquedefs; i < uniques; i++, uniquedef++) {
uniquedef->key = keys + i;
unique_key_parts += uniquedef->keysegs;
share.state.key_root[keys + i] = HA_OFFSET_ERROR;
tot_length +=
(max_rows / (ulong)(((uint)myisam_block_size - 5) /
((MI_UNIQUE_HASH_LENGTH + pointer) * 2))) *
(ulong)myisam_block_size;
}
keys += uniques; /* Each unique has 1 key */
key_segs += uniques; /* Each unique has 1 key seg */
base_pos =
(MI_STATE_INFO_SIZE + keys * MI_STATE_KEY_SIZE +
max_key_block_length / MI_MIN_KEY_BLOCK_LENGTH * MI_STATE_KEYBLOCK_SIZE +
key_segs * MI_STATE_KEYSEG_SIZE);
info_length =
base_pos + (uint)(MI_BASE_INFO_SIZE + keys * MI_KEYDEF_SIZE +
uniques * MI_UNIQUEDEF_SIZE +
(key_segs + unique_key_parts) * HA_KEYSEG_SIZE +
columns * MI_COLUMNDEF_SIZE);
DBUG_PRINT("info", ("info_length: %u", info_length));
/* There are only 16 bits for the total header length. */
if (info_length > 65535) {
my_printf_error(0,
"MyISAM table '%s' has too many columns and/or "
"indexes and/or unique constraints.",
MYF(0), name + dirname_length(name));
set_my_errno(HA_WRONG_CREATE_OPTION);
goto err_no_lock;
}
memmove(share.state.header.file_version, (uchar *)myisam_file_magic, 4);
ci->old_options = options | (ci->old_options & HA_OPTION_TEMP_COMPRESS_RECORD
? HA_OPTION_COMPRESS_RECORD |
HA_OPTION_TEMP_COMPRESS_RECORD
: 0);
mi_int2store(share.state.header.options, ci->old_options);
mi_int2store(share.state.header.header_length, info_length);
mi_int2store(share.state.header.state_info_length, MI_STATE_INFO_SIZE);
mi_int2store(share.state.header.base_info_length, MI_BASE_INFO_SIZE);
mi_int2store(share.state.header.base_pos, base_pos);
share.state.header.language =
(ci->language ? ci->language : default_charset_info->number);
share.state.header.max_block_size_index =
max_key_block_length / MI_MIN_KEY_BLOCK_LENGTH;
share.state.dellink = HA_OFFSET_ERROR;
share.state.process = (ulong)getpid();
share.state.unique = (ulong)0;
share.state.update_count = (ulong)0;
share.state.version = (ulong)time((time_t *)nullptr);
share.state.sortkey = (ushort)~0;
share.state.auto_increment = ci->auto_increment;
share.options = options;
share.base.rec_reflength = pointer;
/* Get estimate for index file length (this may be wrong for FT keys) */
tmp = (tot_length + max_key_block_length * keys * MI_INDEX_BLOCK_MARGIN) /
MI_MIN_KEY_BLOCK_LENGTH;
/*
use maximum of key_file_length we calculated and key_file_length value we
got from MYI file header (see also myisampack.c:save_state)
*/
share.base.key_reflength =
mi_get_pointer_length(std::max(ci->key_file_length, tmp), 3);
share.base.keys = share.state.header.keys = keys;
share.state.header.uniques = uniques;
share.state.header.fulltext_keys = fulltext_keys;
mi_int2store(share.state.header.key_parts, key_segs);
mi_int2store(share.state.header.unique_key_parts, unique_key_parts);
mi_set_all_keys_active(share.state.key_map, keys);
aligned_key_start = std::bit_ceil(max_key_block_length ? max_key_block_length
: myisam_block_size);
share.base.keystart = share.state.state.key_file_length =
MY_ALIGN(info_length, aligned_key_start);
share.base.max_key_block_length = max_key_block_length;
share.base.max_key_length = ALIGN_SIZE(max_key_length + 4);
share.base.records = ci->max_rows;
share.base.reloc = ci->reloc_rows;
share.base.reclength = real_reclength;
share.base.pack_reclength =
reclength + (options & HA_OPTION_CHECKSUM ? 1 : 0);
share.base.max_pack_length = pack_reclength;
share.base.min_pack_length = min_pack_length;
share.base.pack_bits = packed;
share.base.fields = fields;
share.base.pack_fields = packed;
/* max_data_file_length and max_key_file_length are recalculated on open */
if (options & HA_OPTION_TMP_TABLE)
share.base.max_data_file_length = (my_off_t)ci->data_file_length;
share.base.min_block_length =
(share.base.pack_reclength + 3 < MI_EXTEND_BLOCK_LENGTH &&
!share.base.blobs)
? std::max(share.base.pack_reclength, ulong{MI_MIN_BLOCK_LENGTH})
: MI_EXTEND_BLOCK_LENGTH;
if (!(flags & HA_DONT_TOUCH_DATA))
share.state.create_time = (long)time((time_t *)nullptr);
if (!internal_table) mysql_mutex_lock(&THR_LOCK_myisam);
/*
NOTE: For test_if_reopen() we need a real path name. Hence we need
MY_RETURN_REAL_PATH for every fn_format(filename, ...).
*/
if (ci->index_file_name) {
const char *iext = strrchr(ci->index_file_name, '.');
int have_iext = iext && !strcmp(iext, MI_NAME_IEXT);
if (options & HA_OPTION_TMP_TABLE) {
char *path;
/* chop off the table name, temporary tables use generated name */
if ((path = strrchr(const_cast<char *>(ci->index_file_name), FN_LIBCHAR)))
*path = '\0';
fn_format(filename, name, ci->index_file_name, MI_NAME_IEXT,
MY_REPLACE_DIR | MY_UNPACK_FILENAME | MY_RETURN_REAL_PATH |
MY_APPEND_EXT);
} else {
fn_format(filename, ci->index_file_name, "", MI_NAME_IEXT,
MY_UNPACK_FILENAME | MY_RETURN_REAL_PATH |
(have_iext ? MY_REPLACE_EXT : MY_APPEND_EXT));
}
fn_format(linkname, name, "", MI_NAME_IEXT,
MY_UNPACK_FILENAME | MY_APPEND_EXT);
linkname_ptr = linkname;
/*
Don't create the table if the link or file exists to ensure that one
doesn't accidentally destroy another table.
*/
create_flag = 0;
} else {
const char *iext = strrchr(name, '.');
int have_iext = iext && !strcmp(iext, MI_NAME_IEXT);
fn_format(filename, name, "", MI_NAME_IEXT,
MY_UNPACK_FILENAME | MY_RETURN_REAL_PATH |
(have_iext ? MY_REPLACE_EXT : MY_APPEND_EXT));
linkname_ptr = nullptr;
/* Replace the current file */
create_flag = (flags & HA_CREATE_KEEP_FILES) ? 0 : MY_DELETE_OLD;
}
/*
If a MRG_MyISAM table is in use, the mapped MyISAM tables are open,
but no entry is made in the table cache for them.
A TRUNCATE command checks for the table in the cache only and could
be fooled to believe, the table is not open.
Pull the emergency brake in this situation. (Bug #8306)
NOTE: The filename is compared against unique_file_name of every
open table. Hence we need a real path here.
*/
if (!internal_table && test_if_reopen(filename)) {
my_printf_error(0,
"MyISAM table '%s' is in use "
"(most likely by a MERGE table). Try FLUSH TABLES.",
MYF(0), name + dirname_length(name));
set_my_errno(HA_ERR_TABLE_EXIST);
goto err;
}
if ((file = mysql_file_create_with_symlink(mi_key_file_kfile, linkname_ptr,
filename, 0, create_mode,
MYF(MY_WME | create_flag))) < 0)
goto err;
errpos = 1;
if (!(flags & HA_DONT_TOUCH_DATA)) {
{
if (ci->data_file_name) {
const char *dext = strrchr(ci->data_file_name, '.');
int have_dext = dext && !strcmp(dext, MI_NAME_DEXT);
if (options & HA_OPTION_TMP_TABLE) {
char *path;
/* chop off the table name, temporary tables use generated name */
if ((path =
strrchr(const_cast<char *>(ci->data_file_name), FN_LIBCHAR)))
*path = '\0';
fn_format(filename, name, ci->data_file_name, MI_NAME_DEXT,
MY_REPLACE_DIR | MY_UNPACK_FILENAME | MY_APPEND_EXT);
} else {
fn_format(filename, ci->data_file_name, "", MI_NAME_DEXT,
MY_UNPACK_FILENAME |
(have_dext ? MY_REPLACE_EXT : MY_APPEND_EXT));
}
fn_format(linkname, name, "", MI_NAME_DEXT,
MY_UNPACK_FILENAME | MY_APPEND_EXT);
linkname_ptr = linkname;
create_flag = 0;
} else {
fn_format(filename, name, "", MI_NAME_DEXT,
MY_UNPACK_FILENAME | MY_APPEND_EXT);
linkname_ptr = nullptr;
create_flag = (flags & HA_CREATE_KEEP_FILES) ? 0 : MY_DELETE_OLD;
}
if ((dfile = mysql_file_create_with_symlink(
mi_key_file_dfile, linkname_ptr, filename, 0, create_mode,
MYF(MY_WME | create_flag))) < 0)
goto err;
}
errpos = 3;
}
DBUG_PRINT("info", ("write state info and base info"));
if (mi_state_info_write(file, &share.state, 2) ||
mi_base_info_write(file, &share.base))
goto err;
#ifndef NDEBUG
if ((uint)mysql_file_tell(file, MYF(0)) != base_pos + MI_BASE_INFO_SIZE) {
uint pos = (uint)mysql_file_tell(file, MYF(0));
DBUG_PRINT("warning", ("base_length: %d != used_length: %d",
base_pos + MI_BASE_INFO_SIZE, pos));
}
#endif
/* Write key and keyseg definitions */
DBUG_PRINT("info", ("write key and keyseg definitions"));
for (i = 0; i < share.base.keys - uniques; i++) {
uint sp_segs = (keydefs[i].flag & HA_SPATIAL) ? 2 * SPDIMS : 0;
if (mi_keydef_write(file, &keydefs[i])) goto err;
for (j = 0; j < keydefs[i].keysegs - sp_segs; j++)
if (mi_keyseg_write(file, &keydefs[i].seg[j])) goto err;
for (j = 0; j < sp_segs; j++) {
HA_KEYSEG sseg;
sseg.type = SPTYPE;
sseg.language = 7; /* Binary */
sseg.null_bit = 0;
sseg.bit_start = 0;
sseg.bit_end = 0;
sseg.bit_length = 0;
sseg.bit_pos = 0;
sseg.length = SPLEN;
sseg.null_pos = 0;
sseg.start = j * SPLEN;
sseg.flag = HA_SWAP_KEY;
if (mi_keyseg_write(file, &sseg)) goto err;
}
}
/* Create extra keys for unique definitions */
offset = real_reclength - uniques * MI_UNIQUE_HASH_LENGTH;
memset(&tmp_keydef, 0, sizeof(tmp_keydef));
memset(&tmp_keyseg, 0, sizeof(tmp_keyseg));
for (i = 0; i < uniques; i++) {
tmp_keydef.keysegs = 1;
tmp_keydef.flag = HA_UNIQUE_CHECK;
tmp_keydef.block_length = (uint16)myisam_block_size;
tmp_keydef.keylength = MI_UNIQUE_HASH_LENGTH + pointer;
tmp_keydef.minlength = tmp_keydef.maxlength = tmp_keydef.keylength;
tmp_keyseg.type = MI_UNIQUE_HASH_TYPE;
tmp_keyseg.length = MI_UNIQUE_HASH_LENGTH;
tmp_keyseg.start = offset;
offset += MI_UNIQUE_HASH_LENGTH;
if (mi_keydef_write(file, &tmp_keydef) ||
mi_keyseg_write(file, (&tmp_keyseg)))
goto err;
}
/* Save unique definition */
DBUG_PRINT("info", ("write unique definitions"));
for (i = 0; i < share.state.header.uniques; i++) {
HA_KEYSEG *keyseg_end;
keyseg = uniquedefs[i].seg;
if (mi_uniquedef_write(file, &uniquedefs[i])) goto err;
for (keyseg = uniquedefs[i].seg,
keyseg_end = keyseg + uniquedefs[i].keysegs;
keyseg < keyseg_end; keyseg++) {
switch (keyseg->type) {
case HA_KEYTYPE_VARTEXT1:
case HA_KEYTYPE_VARTEXT2:
case HA_KEYTYPE_VARBINARY1:
case HA_KEYTYPE_VARBINARY2:
if (!(keyseg->flag & HA_BLOB_PART)) {
keyseg->flag |= HA_VAR_LENGTH_PART;
keyseg->bit_start = ((keyseg->type == HA_KEYTYPE_VARTEXT1 ||
keyseg->type == HA_KEYTYPE_VARBINARY1)
? 1
: 2);
}
break;
default:
break;
}
if (mi_keyseg_write(file, keyseg)) goto err;
}
}
DBUG_PRINT("info", ("write field definitions"));
for (i = 0; i < share.base.fields; i++)
if (mi_recinfo_write(file, &recinfo[i])) goto err;
#ifndef NDEBUG
if ((uint)mysql_file_tell(file, MYF(0)) != info_length) {
uint pos = (uint)mysql_file_tell(file, MYF(0));
DBUG_PRINT("warning",
("info_length: %d != used_length: %d", info_length, pos));
}
#endif
/* Enlarge files */
DBUG_PRINT("info", ("enlarge to keystart: %lu", (ulong)share.base.keystart));
if (mysql_file_chsize(file, (ulong)share.base.keystart, 0, MYF(0))) goto err;
if (!(flags & HA_DONT_TOUCH_DATA)) {
errpos = 2;
if (mysql_file_close(dfile, MYF(0))) goto err;
}
errpos = 0;
if (!internal_table) mysql_mutex_unlock(&THR_LOCK_myisam);
if (mysql_file_close(file, MYF(0))) goto err_no_lock;
my_free(rec_per_key_part);
return 0;
err:
if (!internal_table) mysql_mutex_unlock(&THR_LOCK_myisam);
err_no_lock:
save_errno = my_errno();
switch (errpos) {
case 3:
(void)mysql_file_close(dfile, MYF(0));
[[fallthrough]];
case 2:
if (!(flags & HA_DONT_TOUCH_DATA))
mysql_file_delete_with_symlink(
mi_key_file_dfile,
fn_format(filename, name, "", MI_NAME_DEXT,
MY_UNPACK_FILENAME | MY_APPEND_EXT),
MYF(0));
[[fallthrough]];
case 1:
(void)mysql_file_close(file, MYF(0));
if (!(flags & HA_DONT_TOUCH_DATA))
mysql_file_delete_with_symlink(
mi_key_file_kfile,
fn_format(filename, name, "", MI_NAME_IEXT,
MY_UNPACK_FILENAME | MY_APPEND_EXT),
MYF(0));
}
my_free(rec_per_key_part);
set_my_errno(save_errno);
return save_errno; /* return the fatal errno */
}