static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, File file) { int error, errarg= 0; uint new_frm_ver, field_pack_length, new_field_pack_flag; uint interval_count, interval_parts, read_length, int_length; uint db_create_options, keys, key_parts, n_length; uint key_info_length, com_length, null_bit_pos, gcol_screen_length; uint extra_rec_buf_length; uint i,j; bool use_extended_sk; // Supported extending of secondary keys with PK parts bool use_hash; char *keynames, *names, *comment_pos, *gcol_screen_pos; char *orig_comment_pos, *orig_gcol_screen_pos; uchar forminfo[288]; uchar *record; uchar *disk_buff, *strpos, *null_flags, *null_pos; ulong pos, record_offset, *rec_per_key, rec_buff_length; rec_per_key_t *rec_per_key_float; handler *handler_file= 0; KEY *keyinfo; KEY_PART_INFO *key_part; Field **field_ptr; const char **interval_array; enum legacy_db_type legacy_db_type; my_bitmap_map *bitmaps; uchar *extra_segment_buff= 0; const uint format_section_header_size= 8; uchar *format_section_fields= 0; bool has_vgc= false; DBUG_ENTER("open_binary_frm"); new_field_pack_flag= head[27]; new_frm_ver= (head[2] - FRM_VER); field_pack_length= new_frm_ver < 2 ? 11 : 17; disk_buff= 0; error= 3; /* Position of the form in the form file. */ if (!(pos= get_form_pos(file, head))) goto err; /* purecov: inspected */ mysql_file_seek(file,pos,MY_SEEK_SET,MYF(0)); if (mysql_file_read(file, forminfo,288,MYF(MY_NABP))) goto err; share->frm_version= head[2]; /* Check if .frm file created by MySQL 5.0. In this case we want to display CHAR fields as CHAR and not as VARCHAR. We do it this way as we want to keep the old frm version to enable MySQL 4.1 to read these files. */ if (share->frm_version == FRM_VER_TRUE_VARCHAR -1 && head[33] == 5) share->frm_version= FRM_VER_TRUE_VARCHAR; if (*(head+61) && !(share->default_part_db_type= ha_checktype(thd, (enum legacy_db_type) (uint) *(head+61), 1, 0))) goto err; DBUG_PRINT("info", ("default_part_db_type = %u", head[61])); legacy_db_type= (enum legacy_db_type) (uint) *(head+3); DBUG_ASSERT(share->db_plugin == NULL); /* if the storage engine is dynamic, no point in resolving it by its dynamically allocated legacy_db_type. We will resolve it later by name. */ if (legacy_db_type > DB_TYPE_UNKNOWN && legacy_db_type < DB_TYPE_FIRST_DYNAMIC) share->db_plugin= ha_lock_engine(NULL, ha_checktype(thd, legacy_db_type, 0, 0)); share->db_create_options= db_create_options= uint2korr(head+30); share->db_options_in_use= share->db_create_options; share->mysql_version= uint4korr(head+51); share->null_field_first= 0; if (!head[32]) // New frm file in 3.23 { share->avg_row_length= uint4korr(head+34); share->row_type= (row_type) head[40]; share->table_charset= get_charset((((uint) head[41]) << 8) + (uint) head[38],MYF(0)); share->null_field_first= 1; share->stats_sample_pages= uint2korr(head+42); share->stats_auto_recalc= static_cast<enum_stats_auto_recalc>(head[44]); } if (!share->table_charset) { /* unknown charset in head[38] or pre-3.23 frm */ if (use_mb(default_charset_info)) { /* Warn that we may be changing the size of character columns */ sql_print_warning("'%s' had no or invalid character set, " "and default character set is multi-byte, " "so character column sizes may have changed", share->path.str); } share->table_charset= default_charset_info; } share->db_record_offset= 1; /* Set temporarily a good value for db_low_byte_first */ share->db_low_byte_first= MY_TEST(legacy_db_type != DB_TYPE_ISAM); error=4; share->max_rows= uint4korr(head+18); share->min_rows= uint4korr(head+22); /* Read keyinformation */ key_info_length= (uint) uint2korr(head+28); mysql_file_seek(file, (ulong) uint2korr(head+6), MY_SEEK_SET, MYF(0)); if (read_string(file, &disk_buff,key_info_length)) goto err; /* purecov: inspected */ if (disk_buff[0] & 0x80) { share->keys= keys= (disk_buff[1] << 7) | (disk_buff[0] & 0x7f); share->key_parts= key_parts= uint2korr(disk_buff+2); } else { share->keys= keys= disk_buff[0]; share->key_parts= key_parts= disk_buff[1]; } share->keys_for_keyread.init(0); share->keys_in_use.init(keys); strpos=disk_buff+6; use_extended_sk= ha_check_storage_engine_flag(share->db_type(), HTON_SUPPORTS_EXTENDED_KEYS); uint total_key_parts; if (use_extended_sk) { uint primary_key_parts= keys ? (new_frm_ver >= 3) ? (uint) strpos[4] : (uint) strpos[3] : 0; total_key_parts= key_parts + primary_key_parts * (keys - 1); } else total_key_parts= key_parts; n_length= keys * sizeof(KEY) + total_key_parts * sizeof(KEY_PART_INFO); /* Allocate memory for the KEY object, the key part array, and the two rec_per_key arrays. */ if (!multi_alloc_root(&share->mem_root, &keyinfo, n_length + uint2korr(disk_buff + 4), &rec_per_key, sizeof(ulong) * total_key_parts, &rec_per_key_float, sizeof(rec_per_key_t) * total_key_parts, NULL)) goto err; /* purecov: inspected */ memset(keyinfo, 0, n_length); share->key_info= keyinfo; key_part= reinterpret_cast<KEY_PART_INFO*>(keyinfo+keys); for (i=0 ; i < keys ; i++, keyinfo++) { keyinfo->table= 0; // Updated in open_frm if (new_frm_ver >= 3) { keyinfo->flags= (uint) uint2korr(strpos) ^ HA_NOSAME; keyinfo->key_length= (uint) uint2korr(strpos+2); keyinfo->user_defined_key_parts= (uint) strpos[4]; keyinfo->algorithm= (enum ha_key_alg) strpos[5]; keyinfo->block_size= uint2korr(strpos+6); strpos+=8; } else { keyinfo->flags= ((uint) strpos[0]) ^ HA_NOSAME; keyinfo->key_length= (uint) uint2korr(strpos+1); keyinfo->user_defined_key_parts= (uint) strpos[3]; keyinfo->algorithm= HA_KEY_ALG_UNDEF; strpos+=4; } keyinfo->key_part= key_part; keyinfo->set_rec_per_key_array(rec_per_key, rec_per_key_float); keyinfo->set_in_memory_estimate(IN_MEMORY_ESTIMATE_UNKNOWN); for (j=keyinfo->user_defined_key_parts ; j-- ; key_part++) { *rec_per_key++ = 0; *rec_per_key_float++ = REC_PER_KEY_UNKNOWN; key_part->fieldnr= (uint16) (uint2korr(strpos) & FIELD_NR_MASK); key_part->offset= (uint) uint2korr(strpos+2)-1; key_part->key_type= (uint) uint2korr(strpos+5); // key_part->field= (Field*) 0; // Will be fixed later if (new_frm_ver >= 1) { key_part->key_part_flag= *(strpos+4); key_part->length= (uint) uint2korr(strpos+7); strpos+=9; } else { key_part->length= *(strpos+4); key_part->key_part_flag=0; if (key_part->length > 128) { key_part->length&=127; /* purecov: inspected */ key_part->key_part_flag=HA_REVERSE_SORT; /* purecov: inspected */ } strpos+=7; } key_part->store_length=key_part->length; } /* Add primary key parts if engine supports primary key extension for secondary keys. Here we add unique first key parts to the end of secondary key parts array and increase actual number of key parts. Note that primary key is always first if exists. Later if there is no primary key in the table then number of actual keys parts is set to user defined key parts. */ keyinfo->actual_key_parts= keyinfo->user_defined_key_parts; keyinfo->actual_flags= keyinfo->flags; if (use_extended_sk && i && !(keyinfo->flags & HA_NOSAME)) { const uint primary_key_parts= share->key_info->user_defined_key_parts; keyinfo->unused_key_parts= primary_key_parts; key_part+= primary_key_parts; rec_per_key+= primary_key_parts; rec_per_key_float+= primary_key_parts; share->key_parts+= primary_key_parts; } } keynames=(char*) key_part; strpos+= (my_stpcpy(keynames, (char *) strpos) - keynames)+1; //reading index comments for (keyinfo= share->key_info, i=0; i < keys; i++, keyinfo++) { if (keyinfo->flags & HA_USES_COMMENT) { keyinfo->comment.length= uint2korr(strpos); keyinfo->comment.str= strmake_root(&share->mem_root, (char*) strpos+2, keyinfo->comment.length); strpos+= 2 + keyinfo->comment.length; } DBUG_ASSERT(MY_TEST(keyinfo->flags & HA_USES_COMMENT) == (keyinfo->comment.length > 0)); } share->reclength = uint2korr((head+16)); share->stored_rec_length= share->reclength; if (*(head+26) == 1) share->system= 1; /* one-record-database */ record_offset= (ulong) (uint2korr(head+6)+ ((uint2korr(head+14) == 0xffff ? uint4korr(head+47) : uint2korr(head+14)))); if ((n_length= uint4korr(head+55))) { /* Read extra data segment */ uchar *next_chunk, *buff_end; DBUG_PRINT("info", ("extra segment size is %u bytes", n_length)); if (!(extra_segment_buff= (uchar*) my_malloc(key_memory_frm_extra_segment_buff, n_length, MYF(MY_WME)))) goto err; next_chunk= extra_segment_buff; if (mysql_file_pread(file, extra_segment_buff, n_length, record_offset + share->reclength, MYF(MY_NABP))) { goto err; } share->connect_string.length= uint2korr(next_chunk); if (!(share->connect_string.str= strmake_root(&share->mem_root, (char*) next_chunk + 2, share->connect_string. length))) { goto err; } next_chunk+= share->connect_string.length + 2; buff_end= extra_segment_buff + n_length; if (next_chunk + 2 < buff_end) { uint str_db_type_length= uint2korr(next_chunk); LEX_STRING name; name.str= (char*) next_chunk + 2; name.length= str_db_type_length; plugin_ref tmp_plugin= ha_resolve_by_name(thd, &name, FALSE); if (tmp_plugin != NULL && !plugin_equals(tmp_plugin, share->db_plugin)) { if (legacy_db_type > DB_TYPE_UNKNOWN && legacy_db_type < DB_TYPE_FIRST_DYNAMIC && legacy_db_type != ha_legacy_type(plugin_data<handlerton*>(tmp_plugin))) { /* bad file, legacy_db_type did not match the name */ goto err; } /* tmp_plugin is locked with a local lock. we unlock the old value of share->db_plugin before replacing it with a globally locked version of tmp_plugin */ plugin_unlock(NULL, share->db_plugin); share->db_plugin= my_plugin_lock(NULL, &tmp_plugin); DBUG_PRINT("info", ("setting dbtype to '%.*s' (%d)", str_db_type_length, next_chunk + 2, ha_legacy_type(share->db_type()))); } else if (!tmp_plugin && str_db_type_length == 9 && !strncmp((char *) next_chunk + 2, "partition", 9)) { /* Check if the partitioning engine is ready */ if (!ha_checktype(thd, DB_TYPE_PARTITION_DB, true, false)) { error= 8; my_error(ER_FEATURE_NOT_AVAILABLE, MYF(0), "partitioning", "--skip-partition", "-DWITH_PARTITION_STORAGE_ENGINE=1"); goto err; } /* Partition engine is ready, share->db_plugin must already contain a properly locked reference to it. */ DBUG_ASSERT(is_ha_partition_handlerton(plugin_data<handlerton*>( share->db_plugin))); DBUG_PRINT("info", ("setting dbtype to '%.*s' (%d)", str_db_type_length, next_chunk + 2, ha_legacy_type(share->db_type()))); } else if (!tmp_plugin && name.length == 18 && !strncmp(name.str, "PERFORMANCE_SCHEMA", name.length)) { /* A FRM file is present on disk, for a PERFORMANCE_SCHEMA table, but this server binary is not compiled with the performance_schema, as ha_resolve_by_name() did not find the storage engine. This can happen: - (a) during tests with mysql-test-run, because the same database installed image is used for regular builds (with P_S) and embedded builds (without P_S) - (b) in production, when random binaries (without P_S) are thrown on top of random installed database instances on disk (with P_S). For the sake of robustness, pretend the table simply does not exist, so that in particular it does not pollute the information_schema with errors when scanning the disk for FRM files. Note that ER_NO_SUCH_TABLE has a special treatment in fill_schema_table_by_open() */ error= 1; my_error(ER_NO_SUCH_TABLE, MYF(0), share->db.str, share->table_name.str); goto err; } else if (!tmp_plugin) { /* purecov: begin inspected */ error= 8; name.str[name.length]=0; my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), name.str); goto err; /* purecov: end */ } next_chunk+= str_db_type_length + 2; } if (next_chunk + 5 < buff_end) { uint32 partition_info_str_len = uint4korr(next_chunk); if ((share->partition_info_buffer_size= share->partition_info_str_len= partition_info_str_len)) { if (!(share->partition_info_str= (char*) memdup_root(&share->mem_root, next_chunk + 4, partition_info_str_len + 1))) { goto err; } } next_chunk+= 5 + partition_info_str_len; } if (share->mysql_version >= 50110 && next_chunk < buff_end) { /* New auto_partitioned indicator introduced in 5.1.11 */ share->auto_partitioned= *next_chunk; next_chunk++; } keyinfo= share->key_info; for (i= 0; i < keys; i++, keyinfo++) { if (keyinfo->flags & HA_USES_PARSER) { if (next_chunk >= buff_end) { DBUG_PRINT("error", ("fulltext key uses parser that is not defined in .frm")); goto err; } LEX_CSTRING parser_name= {reinterpret_cast<char*>(next_chunk), strlen(reinterpret_cast<char*>(next_chunk))}; next_chunk+= parser_name.length + 1; keyinfo->parser= my_plugin_lock_by_name(NULL, parser_name, MYSQL_FTPARSER_PLUGIN); if (! keyinfo->parser) { my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), parser_name.str); goto err; } } } if (forminfo[46] == (uchar)255) { //reading long table comment if (next_chunk + 2 > buff_end) { DBUG_PRINT("error", ("long table comment is not defined in .frm")); goto err; } share->comment.length = uint2korr(next_chunk); if (! (share->comment.str= strmake_root(&share->mem_root, (char*)next_chunk + 2, share->comment.length))) { goto err; } next_chunk+= 2 + share->comment.length; } if (next_chunk + format_section_header_size < buff_end) { /* New extra data segment called "format section" with additional table and column properties introduced by MySQL Cluster based on 5.1.20 Table properties: TABLESPACE <ts> and STORAGE [DISK|MEMORY] Column properties: COLUMN_FORMAT [DYNAMIC|FIXED] and STORAGE [DISK|MEMORY] */ DBUG_PRINT("info", ("Found format section")); /* header */ const uint format_section_length= uint2korr(next_chunk); const uint format_section_flags= uint4korr(next_chunk+2); /* 2 bytes unused */ if (next_chunk + format_section_length > buff_end) { DBUG_PRINT("error", ("format section length too long: %u", format_section_length)); goto err; } DBUG_PRINT("info", ("format_section_length: %u, format_section_flags: %u", format_section_length, format_section_flags)); share->default_storage_media= (enum ha_storage_media) (format_section_flags & 0x7); /* tablespace */ const char *tablespace= (const char*)next_chunk + format_section_header_size; const size_t tablespace_length= strlen(tablespace); share->tablespace= NULL; if (tablespace_length) { Tablespace_name_error_handler error_handler; thd->push_internal_handler(&error_handler); enum_ident_name_check name_check= check_tablespace_name(tablespace); thd->pop_internal_handler(); if (name_check == IDENT_NAME_OK && !(share->tablespace= strmake_root(&share->mem_root, tablespace, tablespace_length+1))) { goto err; } } DBUG_PRINT("info", ("tablespace: '%s'", share->tablespace ? share->tablespace : "<null>")); /* pointer to format section for fields */ format_section_fields= next_chunk + format_section_header_size + tablespace_length + 1; next_chunk+= format_section_length; } if (next_chunk + 2 <= buff_end) { share->compress.length = uint2korr(next_chunk); if (! (share->compress.str= strmake_root(&share->mem_root, (char*)next_chunk + 2, share->compress.length))) { goto err; } next_chunk+= 2 + share->compress.length; } if (next_chunk + 2 <= buff_end) { share->encrypt_type.length = uint2korr(next_chunk); if (! (share->encrypt_type.str= strmake_root(&share->mem_root, (char*)next_chunk + 2, share->encrypt_type.length))) { goto err; } next_chunk+= 2 + share->encrypt_type.length; } } share->key_block_size= uint2korr(head+62); error=4; extra_rec_buf_length= uint2korr(head+59); rec_buff_length= ALIGN_SIZE(share->reclength + 1 + extra_rec_buf_length); share->rec_buff_length= rec_buff_length; if (!(record= (uchar *) alloc_root(&share->mem_root, rec_buff_length))) goto err; /* purecov: inspected */ share->default_values= record; if (mysql_file_pread(file, record, (size_t) share->reclength, record_offset, MYF(MY_NABP))) goto err; /* purecov: inspected */ mysql_file_seek(file, pos+288, MY_SEEK_SET, MYF(0)); share->fields= uint2korr(forminfo+258); pos= uint2korr(forminfo+260); /* Length of all screens */ n_length= uint2korr(forminfo+268); interval_count= uint2korr(forminfo+270); interval_parts= uint2korr(forminfo+272); int_length= uint2korr(forminfo+274); share->null_fields= uint2korr(forminfo+282); com_length= uint2korr(forminfo+284); gcol_screen_length= uint2korr(forminfo+286); share->vfields= 0; share->stored_fields= share->fields; if (forminfo[46] != (uchar)255) { share->comment.length= (int) (forminfo[46]); share->comment.str= strmake_root(&share->mem_root, (char*) forminfo+47, share->comment.length); } DBUG_PRINT("info",("i_count: %d i_parts: %d index: %d n_length: %d int_length: %d com_length: %d gcol_screen_length: %d", interval_count,interval_parts, share->keys,n_length,int_length, com_length, gcol_screen_length)); if (!(field_ptr = (Field **) alloc_root(&share->mem_root, (uint) ((share->fields+1)*sizeof(Field*)+ interval_count*sizeof(TYPELIB)+ (share->fields+interval_parts+ keys+3)*sizeof(char *)+ (n_length+int_length+com_length+ gcol_screen_length))))) goto err; /* purecov: inspected */ share->field= field_ptr; read_length=(uint) (share->fields * field_pack_length + pos+ (uint) (n_length+int_length+com_length+ gcol_screen_length)); if (read_string(file, &disk_buff,read_length)) goto err; /* purecov: inspected */ strpos= disk_buff+pos; share->intervals= (TYPELIB*) (field_ptr+share->fields+1); interval_array= (const char **) (share->intervals+interval_count); names= (char*) (interval_array+share->fields+interval_parts+keys+3); if (!interval_count) share->intervals= 0; // For better debugging memcpy(names, strpos+(share->fields*field_pack_length), (uint) (n_length+int_length)); orig_comment_pos= comment_pos= names+(n_length+int_length); memcpy(comment_pos, disk_buff+read_length-com_length-gcol_screen_length, com_length); orig_gcol_screen_pos= gcol_screen_pos= names+(n_length+int_length+com_length); memcpy(gcol_screen_pos, disk_buff+read_length-gcol_screen_length, gcol_screen_length); fix_type_pointers(&interval_array, &share->fieldnames, 1, &names); if (share->fieldnames.count != share->fields) goto err; fix_type_pointers(&interval_array, share->intervals, interval_count, &names); { /* Set ENUM and SET lengths */ TYPELIB *interval; for (interval= share->intervals; interval < share->intervals + interval_count; interval++) { uint count= (uint) (interval->count + 1) * sizeof(uint); if (!(interval->type_lengths= (uint *) alloc_root(&share->mem_root, count))) goto err; for (count= 0; count < interval->count; count++) { char *val= (char*) interval->type_names[count]; interval->type_lengths[count]= strlen(val); } interval->type_lengths[count]= 0; } } if (keynames) fix_type_pointers(&interval_array, &share->keynames, 1, &keynames); /* Allocate handler */ if (!(handler_file= get_new_handler(share, thd->mem_root, share->db_type()))) goto err; if (handler_file->set_ha_share_ref(&share->ha_share)) goto err; if (share->null_field_first) { null_flags= null_pos= share->default_values; null_bit_pos= (db_create_options & HA_OPTION_PACK_RECORD) ? 0 : 1; /* null_bytes below is only correct under the condition that there are no bit fields. Correct values is set below after the table struct is initialized */ share->null_bytes= (share->null_fields + null_bit_pos + 7) / 8; } else { share->null_bytes= (share->null_fields+7)/8; null_flags= null_pos= share->default_values + share->reclength - share->null_bytes; null_bit_pos= 0; } use_hash= share->fields >= MAX_FIELDS_BEFORE_HASH; if (use_hash) use_hash= !my_hash_init(&share->name_hash, system_charset_info, share->fields,0,0, (my_hash_get_key) get_field_name,0,0, PSI_INSTRUMENT_ME); for (i=0 ; i < share->fields; i++, strpos+=field_pack_length) { if (new_frm_ver >= 3 && (strpos[10] & Field::GENERATED_FIELD) && // Field::unireg_check ! (bool) (uint) (gcol_screen_pos[3])) // Field::stored_in_db { /* Skip virtual generated columns as we will do separate pass for them. We still need to advance pointers to current comment and generated column info in for such fields. */ comment_pos+= uint2korr(strpos+15); gcol_screen_pos+= uint2korr(gcol_screen_pos + 1) + FRM_GCOL_HEADER_SIZE; has_vgc= true; } else { if ((error= make_field_from_frm(thd, share, new_frm_ver, use_hash, i, strpos, format_section_fields, &comment_pos, &gcol_screen_pos, &null_pos, &null_bit_pos, &errarg))) goto err; } } if (has_vgc) { /* We need to do separate pass through field descriptions for virtual generated columns to ensure that they get allocated null/leftover bits at the tail of record preamble. */ strpos= disk_buff+pos; comment_pos= orig_comment_pos; gcol_screen_pos= orig_gcol_screen_pos; // Generated columns can be present only in new .FRMs. DBUG_ASSERT(new_frm_ver >= 3); for (i=0 ; i < share->fields; i++, strpos+=field_pack_length) { if ((strpos[10] & Field::GENERATED_FIELD) && // Field::unireg_check !(bool) (uint) (gcol_screen_pos[3])) // Field::stored_in_db { if ((error= make_field_from_frm(thd, share, new_frm_ver, use_hash, i, strpos, format_section_fields, &comment_pos, &gcol_screen_pos, &null_pos, &null_bit_pos, &errarg))) goto err; } else { /* Advance pointers to current comment and generated columns info for stored fields. */ comment_pos+= uint2korr(strpos+15); if (strpos[10] & Field::GENERATED_FIELD) // Field::unireg_check { gcol_screen_pos+= uint2korr(gcol_screen_pos + 1) + FRM_GCOL_HEADER_SIZE; } } } } error= 4; share->field[share->fields]= 0; // End marker /* Sanity checks: */ DBUG_ASSERT(share->fields >= share->stored_fields); DBUG_ASSERT(share->reclength >= share->stored_rec_length); /* Fix key->name and key_part->field */ if (key_parts) { const int pk_off= find_type(primary_key_name, &share->keynames, FIND_TYPE_NO_PREFIX); uint primary_key= (pk_off > 0 ? pk_off-1 : MAX_KEY); longlong ha_option= handler_file->ha_table_flags(); keyinfo= share->key_info; key_part= keyinfo->key_part; for (uint key=0 ; key < share->keys ; key++,keyinfo++) { uint usable_parts= 0; keyinfo->name=(char*) share->keynames.type_names[key]; /* Fix fulltext keys for old .frm files */ if (share->key_info[key].flags & HA_FULLTEXT) share->key_info[key].algorithm= HA_KEY_ALG_FULLTEXT; if (primary_key >= MAX_KEY && (keyinfo->flags & HA_NOSAME)) { /* If the UNIQUE key doesn't have NULL columns and is not a part key declare this as a primary key. */ primary_key=key; for (i=0 ; i < keyinfo->user_defined_key_parts ;i++) { DBUG_ASSERT(key_part[i].fieldnr > 0); // Table field corresponding to the i'th key part. Field *table_field= share->field[key_part[i].fieldnr - 1]; // Index on virtual generated columns is not allowed to be PK // even when the conditions below are true, so this case must be // rejected here. if (table_field->is_virtual_gcol()) { primary_key= MAX_KEY; // Can't be used break; } /* If the key column is of NOT NULL BLOB type, then it will definitly have key prefix. And if key part prefix size is equal to the BLOB column max size, then we can promote it to primary key. */ if (!table_field->real_maybe_null() && table_field->type() == MYSQL_TYPE_BLOB && table_field->field_length == key_part[i].length) continue; /* If the key column is of NOT NULL GEOMETRY type, specifically POINT type whose length is known internally (which is 25). And key part prefix size is equal to the POINT column max size, then we can promote it to primary key. */ if (!table_field->real_maybe_null() && table_field->type() == MYSQL_TYPE_GEOMETRY && table_field->get_geometry_type() == Field::GEOM_POINT && key_part[i].length == MAX_LEN_GEOM_POINT_FIELD) continue; if (table_field->real_maybe_null() || table_field->key_length() != key_part[i].length) { primary_key= MAX_KEY; // Can't be used break; } } } for (i=0 ; i < keyinfo->user_defined_key_parts ; key_part++,i++) { Field *field; if (new_field_pack_flag <= 1) key_part->fieldnr= (uint16) find_field(share->field, share->default_values, (uint) key_part->offset, (uint) key_part->length); if (!key_part->fieldnr) { error= 4; // Wrong file goto err; } field= key_part->field= share->field[key_part->fieldnr-1]; key_part->type= field->key_type(); if (field->real_maybe_null()) { key_part->null_offset=field->null_offset(share->default_values); key_part->null_bit= field->null_bit; key_part->store_length+=HA_KEY_NULL_LENGTH; keyinfo->flags|=HA_NULL_PART_KEY; keyinfo->key_length+= HA_KEY_NULL_LENGTH; } if (field->type() == MYSQL_TYPE_BLOB || field->real_type() == MYSQL_TYPE_VARCHAR || field->type() == MYSQL_TYPE_GEOMETRY) { key_part->store_length+=HA_KEY_BLOB_LENGTH; if (i + 1 <= keyinfo->user_defined_key_parts) keyinfo->key_length+= HA_KEY_BLOB_LENGTH; } key_part->init_flags(); if (field->is_virtual_gcol()) keyinfo->flags|= HA_VIRTUAL_GEN_KEY; setup_key_part_field(share, handler_file, primary_key, keyinfo, key, i, &usable_parts); field->flags|= PART_KEY_FLAG; if (key == primary_key) { field->flags|= PRI_KEY_FLAG; /* If this field is part of the primary key and all keys contains the primary key, then we can use any key to find this column */ if (ha_option & HA_PRIMARY_KEY_IN_READ_INDEX) { if (field->key_length() == key_part->length && !(field->flags & BLOB_FLAG)) field->part_of_key= share->keys_in_use; if (field->part_of_sortkey.is_set(key)) field->part_of_sortkey= share->keys_in_use; } } if (field->key_length() != key_part->length) { if (field->type() == MYSQL_TYPE_NEWDECIMAL) { /* Fix a fatal error in decimal key handling that causes crashes on Innodb. We fix it by reducing the key length so that InnoDB never gets a too big key when searching. This allows the end user to do an ALTER TABLE to fix the error. */ keyinfo->key_length-= (key_part->length - field->key_length()); key_part->store_length-= (uint16)(key_part->length - field->key_length()); key_part->length= (uint16)field->key_length(); sql_print_error("Found wrong key definition in %s; " "Please do \"ALTER TABLE `%s` FORCE \" to fix it!", share->table_name.str, share->table_name.str); push_warning_printf(thd, Sql_condition::SL_WARNING, ER_CRASHED_ON_USAGE, "Found wrong key definition in %s; " "Please do \"ALTER TABLE `%s` FORCE\" to fix " "it!", share->table_name.str, share->table_name.str); share->crashed= 1; // Marker for CHECK TABLE continue; } key_part->key_part_flag|= HA_PART_KEY_SEG; } } if (use_extended_sk && primary_key < MAX_KEY && key && !(keyinfo->flags & HA_NOSAME)) key_part+= add_pk_parts_to_sk(keyinfo, key, share->key_info, primary_key, share, handler_file, &usable_parts); /* Skip unused key parts if they exist */ key_part+= keyinfo->unused_key_parts; keyinfo->usable_key_parts= usable_parts; // Filesort set_if_bigger(share->max_key_length,keyinfo->key_length+ keyinfo->user_defined_key_parts); share->total_key_length+= keyinfo->key_length; /* MERGE tables do not have unique indexes. But every key could be an unique index on the underlying MyISAM table. (Bug #10400) */ if ((keyinfo->flags & HA_NOSAME) || (ha_option & HA_ANY_INDEX_MAY_BE_UNIQUE)) set_if_bigger(share->max_unique_length,keyinfo->key_length); } if (primary_key < MAX_KEY && (share->keys_in_use.is_set(primary_key))) { share->primary_key= primary_key; /* If we are using an integer as the primary key then allow the user to refer to it as '_rowid' */ if (share->key_info[primary_key].user_defined_key_parts == 1) { Field *field= share->key_info[primary_key].key_part[0].field; if (field && field->result_type() == INT_RESULT) { /* note that fieldnr here (and rowid_field_offset) starts from 1 */ share->rowid_field_offset= (share->key_info[primary_key].key_part[0]. fieldnr); } } } else share->primary_key = MAX_KEY; // we do not have a primary key } else share->primary_key= MAX_KEY; my_free(disk_buff); disk_buff=0; if (new_field_pack_flag <= 1) { /* Old file format with default as not null */ uint null_length= (share->null_fields+7)/8; memset(share->default_values + (null_flags - record), 255, null_length); } if (share->found_next_number_field) { Field *reg_field= *share->found_next_number_field; if ((int) (share->next_number_index= (uint) find_ref_key(share->key_info, share->keys, share->default_values, reg_field, &share->next_number_key_offset, &share->next_number_keypart)) < 0) { /* Wrong field definition */ error= 4; goto err; } else reg_field->flags |= AUTO_INCREMENT_FLAG; } if (share->blob_fields) { Field **ptr; uint k, *save; /* Store offsets to blob fields to find them fast */ if (!(share->blob_field= save= (uint*) alloc_root(&share->mem_root, (uint) (share->blob_fields* sizeof(uint))))) goto err; for (k=0, ptr= share->field ; *ptr ; ptr++, k++) { if ((*ptr)->flags & BLOB_FLAG) (*save++)= k; } } /* the correct null_bytes can now be set, since bitfields have been taken into account */ share->null_bytes= (null_pos - null_flags + (null_bit_pos + 7) / 8); share->last_null_bit_pos= null_bit_pos; share->db_low_byte_first= handler_file->low_byte_first(); share->column_bitmap_size= bitmap_buffer_size(share->fields); if (!(bitmaps= (my_bitmap_map*) alloc_root(&share->mem_root, share->column_bitmap_size))) goto err; bitmap_init(&share->all_set, bitmaps, share->fields, FALSE); bitmap_set_all(&share->all_set); delete handler_file; #ifndef DBUG_OFF if (use_hash) (void) my_hash_check(&share->name_hash); #endif my_free(extra_segment_buff); DBUG_RETURN (0); err: share->error= error; share->open_errno= my_errno(); share->errarg= errarg; my_free(disk_buff); my_free(extra_segment_buff); delete handler_file; my_hash_free(&share->name_hash); open_table_error(share, error, share->open_errno, errarg); DBUG_RETURN(error); } /* open_binary_frm */
MySQL 底层实现