static int open_binary_frm()

in sql/table.cc [1436:2297]


static int open_binary_frm(THD *thd, TABLE_SHARE *share,
                           FRM_context *frm_context, 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;
  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
  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 = nullptr;
  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 = nullptr;
  const uint format_section_header_size = 8;
  uchar *format_section_fields = nullptr;
  bool has_vgc = false;
  DBUG_TRACE;

  new_field_pack_flag = head[27];
  new_frm_ver = (head[2] - FRM_VER);
  field_pack_length = new_frm_ver < 2 ? 11 : 17;
  disk_buff = nullptr;

  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;
  frm_context->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 (frm_context->frm_version == FRM_VER_TRUE_VARCHAR - 1 && head[33] == 5)
    frm_context->frm_version = FRM_VER_TRUE_VARCHAR;

  if (*(head + 61) &&
      !(frm_context->default_part_db_type = ha_checktype(
            thd, (enum legacy_db_type)(uint) * (head + 61), true, false)))
    goto err;
  DBUG_PRINT("info", ("default_part_db_type = %u", head[61]));
  legacy_db_type = (enum legacy_db_type)(uint) * (head + 3);
  assert(share->db_plugin == nullptr);
  /*
    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(
        nullptr, ha_checktype(thd, legacy_db_type, false, false));
  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);
  frm_context->null_field_first = false;
  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));
    frm_context->null_field_first = true;
    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 */
      LogErr(WARNING_LEVEL, ER_INVALID_CHARSET_AND_DEFAULT_IS_MB,
             share->path.str);
    }
    share->table_charset = default_charset_info;
  }
  /* Set temporarily a good value for db_low_byte_first */
  share->db_low_byte_first = (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->visible_indexes.init(0);
  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) {
    const 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;

  /*
    Allocate memory for the KEY object, the key part array, and the
    two rec_per_key arrays.
  */
  if (!multi_alloc_root(&share->mem_root, &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 */

  keyinfo = share->key_info = share->mem_root.ArrayAlloc<KEY>(keys);
  if (keyinfo == nullptr) goto err;

  key_part = share->mem_root.ArrayAlloc<KEY_PART_INFO>(total_key_parts);
  if (key_part == nullptr) goto err;

  for (i = 0; i < keys; i++, keyinfo++) {
    keyinfo->table = nullptr;  // 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];
      // The algorithm was HA_KEY_ALG_UNDEF in 5.7
      keyinfo->algorithm = HA_KEY_ALG_SE_SPECIFIC;
      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->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 = share->mem_root.ArrayAlloc<char>(uint2korr(disk_buff + 4));
  if (keynames == nullptr) goto err;
  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;
    }
    assert(((keyinfo->flags & HA_USES_COMMENT) != 0) ==
           (keyinfo->comment.length > 0));
  }

  share->reclength = uint2korr((head + 16));
  share->stored_rec_length = share->reclength;
  if (*(head + 26) == 1) share->system = true; /* one-record-database */

  record_offset = (ulong)(uint2korr(head + 6) + ((uint2korr(head + 14) == 0xffff
                                                      ? uint4korr(head + 47)
                                                      : uint2korr(head + 14))));

  uint n_length;
  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(PSI_NOT_INSTRUMENTED, 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) {
      const uint str_db_type_length = uint2korr(next_chunk);
      LEX_CSTRING 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 != nullptr &&
          !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(nullptr, share->db_plugin);
        share->db_plugin = my_plugin_lock(nullptr, &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 && 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:
          - 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 && name.length == 7 &&
                 !strncmp(name.str, "ndbinfo", name.length)) {
        /*
          When upgrading from MySQL Cluster 7.5 or 7.6, both MySQL 5.7 based,
          there may be FRM files for ndbinfo tables. If server is not compiled
          with ndbinfo storage engine or it is not enabled the table can not be
          created.  This is not a critical failure since ndbinfo tables are
          read only tables returning rows on demand about the current state of
          Ndb cluster and not row data is kept on file.  If ndbinfo engine
          later is enabled it will create its tables again.
        */
        DBUG_PRINT("info", ("ignoring ndbinfo table '%s.%s'", share->db.str,
                            share->table_name.str));
        error = 9;
        goto err;
      } else if (!tmp_plugin) {
        /* purecov: begin inspected */
        error = 8;
        const_cast<char *>(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) {
      const uint32 partition_info_str_len = uint4korr(next_chunk);
      if ((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;
        }
        const 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(nullptr, 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 = nullptr;
      if (tablespace_length) {
        Tablespace_name_error_handler error_handler;
        thd->push_internal_handler(&error_handler);
        const bool name_check_error =
            validate_tablespace_name_length(tablespace);
        thd->pop_internal_handler();
        if (!name_check_error &&
            !(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 *)share->mem_root.Alloc(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;
  frm_context->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 **)share->mem_root.Alloc((
            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 = nullptr;  // 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, &frm_context->fieldnames, 1, &names);
  if (frm_context->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 *)share->mem_root.Alloc(count)))
        goto err;
      for (count = 0; count < interval->count; count++) {
        const char *val = 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, share->partition_info_str_len != 0,
                            thd->mem_root, share->db_type())))
    goto err;

  if (handler_file->set_ha_share_ref(&share->ha_share)) goto err;

  if (frm_context->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;
  }

  for (i = 0; i < share->fields; i++, strpos += field_pack_length) {
    if (new_frm_ver >= 3 &&
        (strpos[10] &
         FRM_context::GENERATED_FIELD) &&   // former 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, frm_context, new_frm_ver, 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.
    assert(new_frm_ver >= 3);
    for (i = 0; i < share->fields; i++, strpos += field_pack_length) {
      if ((strpos[10] &
           FRM_context::GENERATED_FIELD) &&   // former Field::unireg_check
          !(bool)(uint)(gcol_screen_pos[3]))  // Field::stored_in_db
      {
        if ((error = make_field_from_frm(thd, share, frm_context, new_frm_ver,
                                         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] &
            FRM_context::GENERATED_FIELD)  // former Field::unireg_check
        {
          gcol_screen_pos +=
              uint2korr(gcol_screen_pos + 1) + FRM_GCOL_HEADER_SIZE;
        }
      }
    }
  }
  error = 4;
  share->field[share->fields] = nullptr;  // End marker
  /* Sanity checks: */
  assert(share->fields >= frm_context->stored_fields);
  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);

    const 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 = 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++) {
          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 definitely 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->is_nullable() &&
              table_field->type() == MYSQL_TYPE_BLOB &&
              table_field->field_length == key_part[i].length)
            continue;

          if (table_field->is_nullable() ||
              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->is_nullable()) {
          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, true);

        field->set_flag(PART_KEY_FLAG);
        if (key == primary_key) {
          field->set_flag(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->is_flag_set(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();
            LogErr(ERROR_LEVEL, ER_TABLE_WRONG_KEY_DEFINITION,
                   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 = true;  // Marker for CHECK TABLE
            continue;
          }
          key_part->key_part_flag |= HA_PART_KEY_SEG;
        }
      }

      if (primary_key < MAX_KEY && key != primary_key &&
          (ha_option & HA_PRIMARY_KEY_IN_READ_INDEX))
        key_part += add_pk_parts_to_sk(keyinfo, key, share->key_info,
                                       primary_key, share, handler_file,
                                       &usable_parts, use_extended_sk);

      /* Skip unused key parts if they exist */
      key_part += keyinfo->unused_key_parts;

      keyinfo->usable_key_parts = usable_parts;  // Filesort

      share->max_key_length =
          std::max(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))
        share->max_unique_length =
            std::max(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 = nullptr;
  if (new_field_pack_flag <= 1) {
    /* Old file format with default as not null */
    const 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->set_flag(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 *)share->mem_root.Alloc(
              (uint)(share->blob_fields * sizeof(uint)))))
      goto err;
    for (k = 0, ptr = share->field; *ptr; ptr++, k++) {
      if ((*ptr)->is_flag_set(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 *)share->mem_root.Alloc(share->column_bitmap_size)))
    goto err;
  bitmap_init(&share->all_set, bitmaps, share->fields);
  bitmap_set_all(&share->all_set);

  ::destroy_at(handler_file);
  my_free(extra_segment_buff);
  return 0;

err:
  my_free(disk_buff);
  my_free(extra_segment_buff);
  if (handler_file != nullptr) ::destroy_at(handler_file);

  open_table_error(thd, share, error, my_errno());
  return error;
} /*open_binary_frm*/