find_field_in_tables(THD *thd, Item_ident *item,
                     TABLE_LIST *first_table, TABLE_LIST *last_table,
		     Item **ref, find_item_error_report_type report_error,
                     bool check_privileges, bool register_tree_change)
{
  Field *found=0;
  const char *db= item->db_name;
  const char *table_name= item->table_name;
  const char *name= item->field_name;
  uint length=(uint) strlen(name);
  char name_buff[SAFE_NAME_LEN+1];
  TABLE_LIST *cur_table= first_table;
  TABLE_LIST *actual_table;
  bool allow_rowid;

  if (!table_name || !table_name[0])
  {
    table_name= 0;                              // For easier test
    db= 0;
  }

  allow_rowid= table_name || (cur_table && !cur_table->next_local);

  if (item->cached_table)
  {
    DBUG_PRINT("info", ("using cached table"));
    /*
      This shortcut is used by prepared statements. We assume that
      TABLE_LIST *first_table is not changed during query execution (which
      is true for all queries except RENAME but luckily RENAME doesn't
      use fields...) so we can rely on reusing pointer to its member.
      With this optimization we also miss case when addition of one more
      field makes some prepared query ambiguous and so erroneous, but we
      accept this trade off.
    */
    TABLE_LIST *table_ref= item->cached_table;
    /*
      The condition (table_ref->view == NULL) ensures that we will call
      find_field_in_table even in the case of information schema tables
      when table_ref->field_translation != NULL.
      */
    if (table_ref->table && !table_ref->view &&
        (!table_ref->is_merged_derived() ||
         (!table_ref->is_multitable() && table_ref->merged_for_insert)))
    {

      found= find_field_in_table(thd, table_ref->table, name, length,
                                 TRUE, &(item->cached_field_index));
#ifndef NO_EMBEDDED_ACCESS_CHECKS
      /* Check if there are sufficient access rights to the found field. */
      if (found && check_privileges && !is_temporary_table(table_ref) &&
          check_column_grant_in_table_ref(thd, table_ref, name, length))
        found= WRONG_GRANT;
#endif
    }
    else
      found= find_field_in_table_ref(thd, table_ref, name, length, item->name,
                                     NULL, NULL, ref, check_privileges,
                                     TRUE, &(item->cached_field_index),
                                     register_tree_change,
                                     &actual_table);
    if (found)
    {
      if (found == WRONG_GRANT)
	return (Field*) 0;

      /*
        Only views fields should be marked as dependent, not an underlying
        fields.
      */
      if (!table_ref->belong_to_view &&
          !table_ref->belong_to_derived)
      {
        SELECT_LEX *current_sel= item->context->select_lex;
        SELECT_LEX *last_select= table_ref->select_lex;
        bool all_merged= TRUE;
        for (SELECT_LEX *sl= current_sel; sl && sl!=last_select;
             sl=sl->outer_select())
        {
          Item *subs= sl->master_unit()->item;
          if (subs->type() == Item::SUBSELECT_ITEM &&
              ((Item_subselect*)subs)->substype() == Item_subselect::IN_SUBS &&
              ((Item_in_subselect*)subs)->test_strategy(SUBS_SEMI_JOIN))
          {
            continue;
          }
          all_merged= FALSE;
          break;
        }
        /*
          If the field was an outer referencee, mark all selects using this
          sub query as dependent on the outer query
        */
        if (!all_merged && current_sel != last_select)
        {
          mark_select_range_as_dependent(thd, last_select, current_sel,
                                         found, *ref, item, true);
        }
      }
      return found;
    }
  }
  else
    item->can_be_depended= TRUE;

  if (db && lower_case_table_names)
  {
    /*
      convert database to lower case for comparison.
      We can't do this in Item_field as this would change the
      'name' of the item which may be used in the select list
    */
    strmake_buf(name_buff, db);
    my_casedn_str(files_charset_info, name_buff);
    db= name_buff;
  }

  if (last_table)
    last_table= last_table->next_name_resolution_table;

  for (; cur_table != last_table ;
       cur_table= cur_table->next_name_resolution_table)
  {
    Field *cur_field= find_field_in_table_ref(thd, cur_table, name, length,
                                              item->name, db, table_name, ref,
                                              (thd->lex->sql_command ==
                                               SQLCOM_SHOW_FIELDS)
                                              ? false : check_privileges,
                                              allow_rowid,
                                              &(item->cached_field_index),
                                              register_tree_change,
                                              &actual_table);
    if (cur_field)
    {
      if (cur_field == WRONG_GRANT)
      {
        if (thd->lex->sql_command != SQLCOM_SHOW_FIELDS)
          return (Field*) 0;

        thd->clear_error();
        cur_field= find_field_in_table_ref(thd, cur_table, name, length,
                                           item->name, db, table_name, ref,
                                           false,
                                           allow_rowid,
                                           &(item->cached_field_index),
                                           register_tree_change,
                                           &actual_table);
        if (cur_field)
        {
          Field *nf=new Field_null(NULL,0,Field::NONE,
                                   cur_field->field_name,
                                   &my_charset_bin);
          nf->init(cur_table->table);
          cur_field= nf;
        }
      }

      /*
        Store the original table of the field, which may be different from
        cur_table in the case of NATURAL/USING join.
      */
      item->cached_table= (!actual_table->cacheable_table || found) ?
                          0 : actual_table;

      DBUG_ASSERT(thd->where);
      /*
        If we found a fully qualified field we return it directly as it can't
        have duplicates.
       */
      if (db)
        return cur_field;

      if (found)
      {
        if (report_error == REPORT_ALL_ERRORS ||
            report_error == IGNORE_EXCEPT_NON_UNIQUE)
          my_error(ER_NON_UNIQ_ERROR, MYF(0),
                   table_name ? item->full_name() : name, thd->where);
        return (Field*) 0;
      }
      found= cur_field;
    }
  }

  if (found)
    return found;

  /*
    If the field was qualified and there were no tables to search, issue
    an error that an unknown table was given. The situation is detected
    as follows: if there were no tables we wouldn't go through the loop
    and cur_table wouldn't be updated by the loop increment part, so it
    will be equal to the first table.
  */
  if (table_name && (cur_table == first_table) &&
      (report_error == REPORT_ALL_ERRORS ||
       report_error == REPORT_EXCEPT_NON_UNIQUE))
  {
    char buff[SAFE_NAME_LEN*2 + 2];
    if (db && db[0])
    {
      strxnmov(buff,sizeof(buff)-1,db,".",table_name,NullS);
      table_name=buff;
    }
    my_error(ER_UNKNOWN_TABLE, MYF(0), table_name, thd->where);
  }
  else
  {
    if (report_error == REPORT_ALL_ERRORS ||
        report_error == REPORT_EXCEPT_NON_UNIQUE)
      my_error(ER_BAD_FIELD_ERROR, MYF(0), item->full_name(), thd->where);
    else
      found= not_found_field;
  }
  return found;
}

MDEV-25766 Unused CTE lead to a crash in find_field_in_tables/find_order_in_list

Do not assume that subquery Item always present.


int maria_create(const char *name, enum data_file_type datafile_type,
                 uint keys,MARIA_KEYDEF *keydefs,
                 uint columns, MARIA_COLUMNDEF *columndef,
                 uint uniques, MARIA_UNIQUEDEF *uniquedefs,
                 MARIA_CREATE_INFO *ci,uint flags)
{
  register uint i,j;
  File UNINIT_VAR(dfile), UNINIT_VAR(file);
  int errpos,save_errno, create_mode= O_RDWR | O_TRUNC, res;
  myf create_flag;
  uint length,max_key_length,packed,pack_bytes,pointer,real_length_diff,
       key_length,info_length,key_segs,options,min_key_length,
       base_pos,long_varchar_count,
       unique_key_parts,fulltext_keys,offset, not_block_record_extra_length;
  uint max_field_lengths, extra_header_size, column_nr;
  uint internal_table= flags & HA_CREATE_INTERNAL_TABLE;
  ulong reclength, real_reclength,min_pack_length;
  char kfilename[FN_REFLEN], klinkname[FN_REFLEN], *klinkname_ptr= NullS;
  char dfilename[FN_REFLEN], dlinkname[FN_REFLEN], *dlinkname_ptr= 0;
  ulong pack_reclength;
  ulonglong tot_length,max_rows, tmp;
  enum en_fieldtype type;
  enum data_file_type org_datafile_type= datafile_type;
  MARIA_SHARE share;
  MARIA_KEYDEF *keydef,tmp_keydef;
  MARIA_UNIQUEDEF *uniquedef;
  HA_KEYSEG *keyseg,tmp_keyseg;
  MARIA_COLUMNDEF *column, *end_column;
  double *rec_per_key_part;
  ulong  *nulls_per_key_part;
  uint16 *column_array;
  my_off_t key_root[HA_MAX_POSSIBLE_KEY], kfile_size_before_extension;
  MARIA_CREATE_INFO tmp_create_info;
  my_bool tmp_table= FALSE; /* cache for presence of HA_OPTION_TMP_TABLE */
  my_bool forced_packed;
  myf     sync_dir=  0;
  uchar   *log_data= NULL;
  my_bool encrypted= maria_encrypt_tables && datafile_type == BLOCK_RECORD;
  my_bool insert_order= MY_TEST(flags & HA_PRESERVE_INSERT_ORDER);
  uint crypt_page_header_space= 0;
  DBUG_ENTER("maria_create");
  DBUG_PRINT("enter", ("keys: %u  columns: %u  uniques: %u  flags: %u",
                      keys, columns, uniques, flags));

  DBUG_ASSERT(maria_inited);

  if (!ci)
  {
    bzero((char*) &tmp_create_info,sizeof(tmp_create_info));
    ci=&tmp_create_info;
  }

  if (keys + uniques > MARIA_MAX_KEY)
  {
    DBUG_RETURN(my_errno=HA_WRONG_CREATE_OPTION);
  }
  errpos=0;
  options=0;
  bzero((uchar*) &share,sizeof(share));

  if (flags & HA_DONT_TOUCH_DATA)
  {
    /* We come here from recreate table */
    org_datafile_type= ci->org_data_file_type;
    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 |
                 HA_OPTION_LONG_BLOB_PTR | HA_OPTION_PAGE_CHECKSUM));
    else
    {
      /* Uncompressing rows */
      options= (ci->old_options &
                (HA_OPTION_CHECKSUM | HA_OPTION_TMP_TABLE |
                 HA_OPTION_DELAY_KEY_WRITE | HA_OPTION_LONG_BLOB_PTR |
                 HA_OPTION_PAGE_CHECKSUM));
    }
  }
  else
  {
    /* Transactional tables must be of type BLOCK_RECORD */
    if (ci->transactional)
      datafile_type= BLOCK_RECORD;
  }

  if (!(rec_per_key_part=
	(double*) my_malloc((keys + uniques)*HA_MAX_KEY_SEG*sizeof(double) +
                            (keys + uniques)*HA_MAX_KEY_SEG*sizeof(ulong) +
                            sizeof(uint16) * columns,
                            MYF(MY_WME | MY_ZEROFILL))))
    DBUG_RETURN(my_errno);
  nulls_per_key_part= (ulong*) (rec_per_key_part +
                                (keys + uniques) * HA_MAX_KEY_SEG);
  column_array= (uint16*) (nulls_per_key_part +
                           (keys + uniques) * HA_MAX_KEY_SEG);

  /* Start by checking fields and field-types used */
  long_varchar_count=packed= not_block_record_extra_length=
    pack_reclength= max_field_lengths= 0;
  reclength= min_pack_length= ci->null_bytes;
  forced_packed= 0;
  column_nr= 0;

  if (encrypted)
  {
    DBUG_ASSERT(datafile_type == BLOCK_RECORD);
    crypt_page_header_space= ma_crypt_get_data_page_header_space();
  }

  for (column= columndef, end_column= column + columns ;
       column != end_column ;
       column++)
  {
    /* Fill in not used struct parts */
    column->column_nr= column_nr++;
    column->offset= reclength;
    column->empty_pos= 0;
    column->empty_bit= 0;
    column->fill_length= column->length;
    if (column->null_bit)
      options|= HA_OPTION_NULL_FIELDS;

    reclength+= column->length;
    type= column->type;
    if (datafile_type == BLOCK_RECORD)
    {
      if (type == FIELD_SKIP_PRESPACE)
        type= column->type= FIELD_NORMAL; /* SKIP_PRESPACE not supported */
      if (type == FIELD_NORMAL &&
          column->length > FULL_PAGE_SIZE2(maria_block_size,
                                           crypt_page_header_space))
      {
        /* FIELD_NORMAL can't be split over many blocks, convert to a CHAR */
        type= column->type= FIELD_SKIP_ENDSPACE;
      }
    }

    if (type != FIELD_NORMAL && type != FIELD_CHECK)
    {
      column->empty_pos= packed/8;
      column->empty_bit= (1 << (packed & 7));
      if (type == FIELD_BLOB)
      {
        forced_packed= 1;
        packed++;
	share.base.blobs++;
	if (pack_reclength != INT_MAX32)
	{
	  if (column->length == 4+portable_sizeof_char_ptr)
	    pack_reclength= INT_MAX32;
	  else
          {
            /* Add max possible blob length */
	    pack_reclength+= (1 << ((column->length-
                                     portable_sizeof_char_ptr)*8));
          }
	}
        max_field_lengths+= (column->length - portable_sizeof_char_ptr);
      }
      else if (type == FIELD_SKIP_PRESPACE ||
	       type == FIELD_SKIP_ENDSPACE)
      {
        forced_packed= 1;
        max_field_lengths+= column->length > 255 ? 2 : 1;
        not_block_record_extra_length++;
        packed++;
      }
      else if (type == FIELD_VARCHAR)
      {
	pack_reclength++;
        not_block_record_extra_length++;
        max_field_lengths++;
        if (datafile_type != DYNAMIC_RECORD)
          packed++;
        column->fill_length= 1;
        options|= HA_OPTION_NULL_FIELDS;        /* Use ma_checksum() */

        /* We must test for 257 as length includes pack-length */
        if (MY_TEST(column->length >= 257))
	{
	  long_varchar_count++;
          max_field_lengths++;
          column->fill_length= 2;
	}
      }
      else if (type == FIELD_SKIP_ZERO)
        packed++;
      else
      {
        if (!column->null_bit)
          min_pack_length+= column->length;
        else
        {
          /* Only BLOCK_RECORD skips NULL fields for all field values */
          not_block_record_extra_length+= column->length;
        }
        column->empty_pos= 0;
        column->empty_bit= 0;
      }
    }
    else					/* FIELD_NORMAL */
    {
      if (!column->null_bit)
      {
        min_pack_length+= column->length;
        share.base.fixed_not_null_fields++;
        share.base.fixed_not_null_fields_length+= column->length;
      }
      else
        not_block_record_extra_length+= column->length;
    }
  }

  if (datafile_type == STATIC_RECORD && forced_packed)
  {
    /* Can't use fixed length records, revert to block records */
    datafile_type= BLOCK_RECORD;
  }

  if (datafile_type == NO_RECORD && uniques)
  {
    /* Can't do unique without data, revert to block records */
    datafile_type= BLOCK_RECORD;
  }

  if (encrypted)
  {
    /*
       datafile_type is set (finally?)
       update encryption that is only supported for BLOCK_RECORD
    */
    if (datafile_type != BLOCK_RECORD)
    {
      encrypted= FALSE;
      crypt_page_header_space= 0;
    }
  }

  if (datafile_type == DYNAMIC_RECORD)
    options|= HA_OPTION_PACK_RECORD;	/* Must use packed records */

  if (datafile_type == STATIC_RECORD || datafile_type == NO_RECORD)
  {
    /* We can't use checksum with static length rows */
    flags&= ~HA_CREATE_CHECKSUM;
    options&= ~HA_OPTION_CHECKSUM;
    min_pack_length= reclength;
    packed= 0;
  }
  else if (datafile_type != BLOCK_RECORD)
    min_pack_length+= not_block_record_extra_length;
  else
    min_pack_length+= 5;                        /* Min row overhead */

  if (flags & HA_CREATE_TMP_TABLE)
  {
    options|= HA_OPTION_TMP_TABLE;
    tmp_table= TRUE;
    create_mode|= O_NOFOLLOW | (internal_table ? 0 : O_EXCL);
    /* "CREATE TEMPORARY" tables are not crash-safe (dropped at restart) */
    ci->transactional= FALSE;
    flags&= ~HA_CREATE_PAGE_CHECKSUM;
  }
  share.base.null_bytes= ci->null_bytes;
  share.base.original_null_bytes= ci->null_bytes;
  share.base.born_transactional= ci->transactional;
  share.base.max_field_lengths= max_field_lengths;
  share.base.field_offsets= 0;                  /* for future */

  if (flags & HA_CREATE_CHECKSUM || (options & HA_OPTION_CHECKSUM))
  {
    options|= HA_OPTION_CHECKSUM;
    min_pack_length++;
    pack_reclength++;
  }
  if (pack_reclength < INT_MAX32)
    pack_reclength+= max_field_lengths + long_varchar_count;
  else
    pack_reclength= INT_MAX32;

  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;
  if (flags & HA_CREATE_PAGE_CHECKSUM)
    options|= HA_OPTION_PAGE_CHECKSUM;

  pack_bytes= (packed + 7) / 8;
  if (pack_reclength != INT_MAX32)
    pack_reclength+= reclength+pack_bytes +
      MY_TEST(test_all_bits(options, HA_OPTION_CHECKSUM |
                                     HA_OPTION_PACK_RECORD));
  min_pack_length+= pack_bytes;
  /* Calculate min possible row length for rows-in-block */
  extra_header_size= MAX_FIXED_HEADER_SIZE;
  if (ci->transactional)
  {
    extra_header_size= TRANS_MAX_FIXED_HEADER_SIZE;
    DBUG_PRINT("info",("creating a transactional table"));
  }
  share.base.min_block_length= (extra_header_size + share.base.null_bytes +
                                pack_bytes);
  if (!ci->data_file_length && ci->max_rows)
  {
    set_if_bigger(ci->max_rows, ci->reloc_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= _ma_safe_mul(ci->max_rows, pack_reclength);
      if (datafile_type == BLOCK_RECORD)
      {
        /* Assume that blocks are only half full (very pessimistic!) */
        ci->data_file_length= _ma_safe_mul(ci->data_file_length, 2);
        set_if_bigger(ci->data_file_length, maria_block_size*2);
      }
    }
  }
  else if (!ci->max_rows)
  {
    if (datafile_type == BLOCK_RECORD)
    {
      uint rows_per_page=
          ((maria_block_size - PAGE_OVERHEAD_SIZE_RAW - crypt_page_header_space)
           / (min_pack_length + extra_header_size + DIR_ENTRY_SIZE));
      ulonglong data_file_length= ci->data_file_length;
      if (!data_file_length)
        data_file_length= ((((ulonglong) 1 << ((BLOCK_RECORD_POINTER_SIZE-1) *
                                               8))/2 -1) * maria_block_size);
      if (rows_per_page > 0)
      {
        set_if_smaller(rows_per_page, MAX_ROWS_PER_PAGE);
        ci->max_rows= (data_file_length / maria_block_size+1) * rows_per_page;
      }
      else
        ci->max_rows= data_file_length / (min_pack_length +
                                          extra_header_size +
                                          DIR_ENTRY_SIZE);
    }
    else
      ci->max_rows=(ha_rows) (ci->data_file_length/(min_pack_length +
                                                    ((options &
                                                      HA_OPTION_PACK_RECORD) ?
                                                     3 : 0)));
    set_if_smaller(ci->reloc_rows, ci->max_rows);
  }
  max_rows= (ulonglong) ci->max_rows;
  if (datafile_type == BLOCK_RECORD)
  {
    /*
      The + 1 is for record position withing page
      The * 2 is because we need one bit for knowing if there is transid's
      after the row pointer
    */
    pointer= maria_get_pointer_length((ci->data_file_length /
                                       maria_block_size) * 2, 4) + 1;
    set_if_smaller(pointer, BLOCK_RECORD_POINTER_SIZE);

    if (!max_rows)
      max_rows= (((((ulonglong) 1 << ((pointer-1)*8)) -1) * maria_block_size) /
                 min_pack_length / 2);
                                      }
  else
  {
    if (datafile_type == NO_RECORD)
      pointer= 0;
    else if (datafile_type != STATIC_RECORD)
      pointer= maria_get_pointer_length(ci->data_file_length,
                                        maria_data_pointer_size);
    else
      pointer= maria_get_pointer_length(ci->max_rows, maria_data_pointer_size);
    if (!max_rows)
      max_rows= ((((ulonglong) 1 << (pointer*8)) -1) / min_pack_length);
  }

  real_reclength=reclength;
  if (datafile_type == STATIC_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;
  share.state.rec_per_key_part=   rec_per_key_part;
  share.state.nulls_per_key_part= nulls_per_key_part;
  share.state.key_root=key_root;
  share.state.key_del= HA_OFFSET_ERROR;
  if (uniques)
    max_key_length= MARIA_UNIQUE_HASH_LENGTH + pointer;

  for (i=0, keydef=keydefs ; i < keys ; i++ , keydef++)
  {
    share.state.key_root[i]= HA_OFFSET_ERROR;
    length= real_length_diff= 0;
    min_key_length= key_length= pointer;

    if (keydef->key_alg == HA_KEY_ALG_RTREE)
      keydef->flag|= HA_RTREE_INDEX;            /* For easier tests */

    if (keydef->flag & HA_SPATIAL)
    {
#ifdef HAVE_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 maria_chk - 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 maria_recreate_table() in _ma_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)
        {
          my_errno=HA_WRONG_CREATE_OPTION;
          goto err_no_lock;
        }
      }
      keydef->keysegs+=sp_segs;
      key_length+=SPLEN*sp_segs;
      length++;                              /* At least one length uchar */
      min_key_length++;
#else
      my_errno= HA_ERR_UNSUPPORTED;
      goto err_no_lock;
#endif /*HAVE_SPATIAL*/
    }
    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)
        {
          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 uchar */
      min_key_length+= 1 + HA_FT_WLEN;
      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 easyer 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)
	{
          DBUG_ASSERT(!(keyseg->flag & (HA_VAR_LENGTH_PART | HA_BLOB_PART)));
	  keydef->flag |= HA_SPACE_PACK_USED | HA_VAR_LENGTH_KEY;
	  options|=HA_OPTION_PACK_KEYS;		/* Using packed keys */
	  length++;				/* At least one length uchar */
          if (!keyseg->null_bit)
            min_key_length++;
          key_length+= keyseg->length;
	  if (keyseg->length >= 255)
	  {
            /* prefix may be 3 bytes */
	    length+= 2;
	  }
	}
	else if (keyseg->flag & (HA_VAR_LENGTH_PART | HA_BLOB_PART))
	{
          DBUG_ASSERT(!test_all_bits(keyseg->flag,
                                    (HA_VAR_LENGTH_PART | HA_BLOB_PART)));
	  keydef->flag|=HA_VAR_LENGTH_KEY;
	  length++;				/* At least one length uchar */
          if (!keyseg->null_bit)
            min_key_length++;
	  options|=HA_OPTION_PACK_KEYS;		/* Using packed keys */
          key_length+= keyseg->length;
	  if (keyseg->length >= 255)
	  {
            /* prefix may be 3 bytes */
	    length+= 2;
	  }
	}
        else
        {
          key_length+= keyseg->length;
          if (!keyseg->null_bit)
            min_key_length+= keyseg->length;
        }
	if (keyseg->null_bit)
	{
	  key_length++;
          /* min key part is 1 byte */
          min_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 > HA_MAX_KEY_SEG)
    {
      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;
    /*
      A key can't be longer than than half a index block (as we have
      to be able to put at least 2 keys on an index block for the key
      algorithms to work).
    */
    if (length > _ma_max_key_length())
    {
      my_errno=HA_WRONG_CREATE_OPTION;
      goto err_no_lock;
    }
    keydef->block_length= (uint16) maria_block_size;
    keydef->keylength= (uint16) key_length;
    keydef->minlength= (uint16) min_key_length;
    keydef->maxlength= (uint16) length;

    if (length > max_key_length)
      max_key_length= length;

    tot_length= update_tot_length(tot_length, max_rows, length);
  }

  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= update_tot_length(tot_length, max_rows, MARIA_UNIQUE_HASH_LENGTH + pointer);
  }
  keys+=uniques;				/* Each unique has 1 key */
  key_segs+=uniques;				/* Each unique has 1 key seg */

  base_pos=(MARIA_STATE_INFO_SIZE + keys * MARIA_STATE_KEY_SIZE +
	    key_segs * MARIA_STATE_KEYSEG_SIZE);
  info_length= base_pos+(uint) (MARIA_BASE_INFO_SIZE+
                                keys * MARIA_KEYDEF_SIZE+
                                uniques * MARIA_UNIQUEDEF_SIZE +
                                (key_segs + unique_key_parts)*HA_KEYSEG_SIZE+
                                columns*(MARIA_COLUMNDEF_SIZE + 2));

  if (encrypted)
  {
    share.base.extra_options|= MA_EXTRA_OPTIONS_ENCRYPTED;

    /* store crypt data in info */
    info_length+= ma_crypt_get_file_length();
  }

  if (insert_order)
  {
    share.base.extra_options|= MA_EXTRA_OPTIONS_INSERT_ORDER;
  }

  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(HA_WRONG_CREATE_OPTION,
                    "Aria table '%s' has too many columns and/or "
                    "indexes and/or unique constraints.",
                    MYF(0), name + dirname_length(name));
    my_errno= HA_WRONG_CREATE_OPTION;
    goto err_no_lock;
  }

  bmove(share.state.header.file_version, maria_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,MARIA_STATE_INFO_SIZE);
  mi_int2store(share.state.header.base_info_length,MARIA_BASE_INFO_SIZE);
  mi_int2store(share.state.header.base_pos,base_pos);
  share.state.header.data_file_type= share.data_file_type= datafile_type;
  share.state.header.org_data_file_type= org_datafile_type;
  share.state.header.not_used= 0;

  share.state.dellink = HA_OFFSET_ERROR;
  share.state.first_bitmap_with_space= 0;
#ifdef MARIA_EXTERNAL_LOCKING
  share.state.process=	(ulong) getpid();
#endif
  share.state.version=	(ulong) time((time_t*) 0);
  share.state.sortkey=  (ushort) ~0;
  share.state.auto_increment=ci->auto_increment;
  share.options=options;
  share.base.rec_reflength=pointer;
  share.base.block_size= maria_block_size;
  share.base.language= (ci->language ? ci->language :
                        default_charset_info->number);

  /*
    Get estimate for index file length (this may be wrong for FT keys)
    This is used for pointers to other key pages.
  */
  tmp= (tot_length / maria_block_size + keys * MARIA_INDEX_BLOCK_MARGIN);

  /*
    use maximum of key_file_length we calculated and key_file_length value we
    got from MAI file header (see also mariapack.c:save_state)
  */
  share.base.key_reflength=
    maria_get_pointer_length(MY_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);

  maria_set_all_keys_active(share.state.key_map, keys);

  share.base.keystart = share.state.state.key_file_length=
    MY_ALIGN(info_length, maria_block_size);
  share.base.max_key_block_length= maria_block_size;
  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 + MY_TEST(options & HA_OPTION_CHECKSUM);
  share.base.max_pack_length=pack_reclength;
  share.base.min_pack_length=min_pack_length;
  share.base.pack_bytes= pack_bytes;
  share.base.fields= columns;
  share.base.pack_fields= packed;

  if (share.data_file_type == BLOCK_RECORD)
  {
    /*
      we are going to create a first bitmap page, set data_file_length
      to reflect this, before the state goes to disk
    */
    share.state.state.data_file_length= maria_block_size;
    /* Add length of packed fields + length */
    share.base.pack_reclength+= share.base.max_field_lengths+3;
    share.base.max_pack_length= share.base.pack_reclength;

    /* Adjust max_pack_length, to be used if we have short rows */
    if (share.base.max_pack_length < maria_block_size)
    {
      share.base.max_pack_length+= FLAG_SIZE;
      if (ci->transactional)
        share.base.max_pack_length+= TRANSID_SIZE * 2;
    }
  }

  /* max_data_file_length and max_key_file_length are recalculated on open */
  if (tmp_table)
    share.base.max_data_file_length= (my_off_t) ci->data_file_length;
  else if (ci->transactional && translog_status == TRANSLOG_OK &&
           !maria_in_recovery)
  {
    /*
      we have checked translog_inited above, because maria_chk may call us
      (via maria_recreate_table()) and it does not have a log.
    */
    sync_dir= MY_SYNC_DIR;
    /*
      If crash between _ma_state_info_write_sub() and
      _ma_update_state__lsns_sub(), table should be ignored by Recovery (or
      old REDOs would fail), so we cannot let LSNs be 0:
    */
    share.state.skip_redo_lsn= share.state.is_of_horizon=
      share.state.create_rename_lsn= LSN_MAX;
  }

  if (datafile_type == DYNAMIC_RECORD)
  {
    share.base.min_block_length=
      (share.base.pack_reclength+3 < MARIA_EXTEND_BLOCK_LENGTH &&
       ! share.base.blobs) ?
      MY_MAX(share.base.pack_reclength,MARIA_MIN_BLOCK_LENGTH) :
      MARIA_EXTEND_BLOCK_LENGTH;
  }
  else if (datafile_type == STATIC_RECORD)
    share.base.min_block_length= share.base.pack_reclength;

  if (! (flags & HA_DONT_TOUCH_DATA))
    share.state.create_time= time((time_t*) 0);

  if (!internal_table)
    mysql_mutex_lock(&THR_LOCK_maria);

  /*
    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)
  {
    char *iext= strrchr(ci->index_file_name, '.');
    int have_iext= iext && !strcmp(iext, MARIA_NAME_IEXT);
    if (tmp_table)
    {
      char *path;
      /* chop off the table name, tempory tables use generated name */
      if ((path= strrchr(ci->index_file_name, FN_LIBCHAR)))
        *path= '\0';
      fn_format(kfilename, name, ci->index_file_name, MARIA_NAME_IEXT,
                MY_REPLACE_DIR | MY_UNPACK_FILENAME |
                MY_RETURN_REAL_PATH | MY_APPEND_EXT);
    }
    else
    {
      fn_format(kfilename, ci->index_file_name, "", MARIA_NAME_IEXT,
                MY_UNPACK_FILENAME | MY_RETURN_REAL_PATH |
                (have_iext ? MY_REPLACE_EXT : MY_APPEND_EXT));
    }
    fn_format(klinkname, name, "", MARIA_NAME_IEXT,
              MY_UNPACK_FILENAME|MY_APPEND_EXT);
    klinkname_ptr= klinkname;
    /*
      Don't create the table if the link or file exists to ensure that one
      doesn't accidently destroy another table.
      Don't sync dir now if the data file has the same path.
    */
    create_flag=
      (ci->data_file_name &&
       !strcmp(ci->index_file_name, ci->data_file_name)) ? 0 : sync_dir;
  }
  else
  {
    char *iext= strrchr(name, '.');
    int have_iext= iext && !strcmp(iext, MARIA_NAME_IEXT);
    fn_format(kfilename, name, "", MARIA_NAME_IEXT, MY_UNPACK_FILENAME |
              (internal_table ? 0 : MY_RETURN_REAL_PATH) |
              (have_iext ? MY_REPLACE_EXT : MY_APPEND_EXT));
    /*
      Replace the current file.
      Don't sync dir now if the data file has the same path.
    */
    create_flag=  (flags & HA_CREATE_KEEP_FILES) ? 0 : MY_DELETE_OLD;
    create_flag|= (!ci->data_file_name ? 0 : sync_dir);
  }

  /*
    If a MRG_MARIA table is in use, the mapped MARIA 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 && _ma_test_if_reopen(kfilename))
  {
    my_printf_error(HA_ERR_TABLE_EXIST, "Aria table '%s' is in use "
                    "(most likely by a MERGE table). Try FLUSH TABLES.",
                    MYF(0), name + dirname_length(name));
    my_errno= HA_ERR_TABLE_EXIST;
    goto err;
  }

  if ((file= mysql_file_create_with_symlink(key_file_kfile, klinkname_ptr,
                                            kfilename, 0, create_mode,
                                            MYF(MY_WME|create_flag))) < 0)
    goto err;
  errpos=1;

  DBUG_PRINT("info", ("write state info and base info"));
  if (_ma_state_info_write_sub(file, &share.state,
                               MA_STATE_INFO_WRITE_FULL_INFO) ||
      _ma_base_info_write(file, &share.base))
    goto err;
  DBUG_PRINT("info", ("base_pos: %d  base_info_size: %d",
                      base_pos, MARIA_BASE_INFO_SIZE));
  DBUG_ASSERT(mysql_file_tell(file,MYF(0)) == base_pos+ MARIA_BASE_INFO_SIZE);

  /* 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 (_ma_keydef_write(file, &keydefs[i]))
      goto err;
    for (j=0 ; j < keydefs[i].keysegs-sp_segs ; j++)
      if (_ma_keyseg_write(file, &keydefs[i].seg[j]))
       goto err;
#ifdef HAVE_SPATIAL
    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_length= 0;
      sseg.bit_pos= 0;
      sseg.length=SPLEN;
      sseg.null_pos=0;
      sseg.start=j*SPLEN;
      sseg.flag= HA_SWAP_KEY;
      if (_ma_keyseg_write(file, &sseg))
        goto err;
    }
#endif
  }
  /* Create extra keys for unique definitions */
  offset= real_reclength - uniques*MARIA_UNIQUE_HASH_LENGTH;
  bzero((char*) &tmp_keydef,sizeof(tmp_keydef));
  bzero((char*) &tmp_keyseg,sizeof(tmp_keyseg));
  for (i=0; i < uniques ; i++)
  {
    tmp_keydef.keysegs=1;
    tmp_keydef.flag=		HA_UNIQUE_CHECK;
    tmp_keydef.block_length=	(uint16) maria_block_size;
    tmp_keydef.keylength=	MARIA_UNIQUE_HASH_LENGTH + pointer;
    tmp_keydef.minlength=tmp_keydef.maxlength=tmp_keydef.keylength;
    tmp_keyseg.type=		MARIA_UNIQUE_HASH_TYPE;
    tmp_keyseg.length=		MARIA_UNIQUE_HASH_LENGTH;
    tmp_keyseg.start=		offset;
    offset+=			MARIA_UNIQUE_HASH_LENGTH;
    if (_ma_keydef_write(file,&tmp_keydef) ||
	_ma_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 (_ma_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:
        DBUG_ASSERT((keyseg->flag & HA_VAR_LENGTH_PART) == 0);
        break;
      }
      if (_ma_keyseg_write(file, keyseg))
	goto err;
    }
  }
  DBUG_PRINT("info", ("write field definitions"));
  if (datafile_type == BLOCK_RECORD)
  {
    /* Store columns in a more efficent order */
    MARIA_COLUMNDEF **col_order, **pos;
    if (!(col_order= (MARIA_COLUMNDEF**) my_malloc(share.base.fields *
                                                   sizeof(MARIA_COLUMNDEF*),
                                                   MYF(MY_WME))))
      goto err;
    for (column= columndef, pos= col_order ;
         column != end_column ;
         column++, pos++)
      *pos= column;
    qsort(col_order, share.base.fields, sizeof(*col_order),
          (qsort_cmp) compare_columns);
    for (i=0 ; i < share.base.fields ; i++)
    {
      column_array[col_order[i]->column_nr]= i;
      if (_ma_columndef_write(file, col_order[i]))
      {
        my_free(col_order);
        goto err;
      }
    }
    my_free(col_order);
  }
  else
  {
    for (i=0 ; i < share.base.fields ; i++)
    {
      column_array[i]= (uint16) i;
      if (_ma_columndef_write(file, &columndef[i]))
        goto err;
    }
  }
  if (_ma_column_nr_write(file, column_array, columns))
    goto err;

  if (encrypted)
  {
    if (ma_crypt_create(&share) ||
        ma_crypt_write(&share, file))
      goto err;
  }

  if ((kfile_size_before_extension= mysql_file_tell(file,MYF(0))) == MY_FILEPOS_ERROR)
    goto err;
#ifndef DBUG_OFF
  if (kfile_size_before_extension != info_length)
    DBUG_PRINT("warning",("info_length: %u  != used_length: %u",
			  info_length, (uint)kfile_size_before_extension));
#endif

  if (sync_dir)
  {
    /*
      we log the first bytes and then the size to which we extend; this is
      not log 1 KB of mostly zeroes if this is a small table.
    */
    char empty_string[]= "";
    LEX_CUSTRING log_array[TRANSLOG_INTERNAL_PARTS + 4];
    translog_size_t total_rec_length= 0;
    uint k;
    LSN lsn;
    log_array[TRANSLOG_INTERNAL_PARTS + 1].length= 1 + 2 + 2 +
      (uint) kfile_size_before_extension;
    /* we are needing maybe 64 kB, so don't use the stack */
    log_data= my_malloc(log_array[TRANSLOG_INTERNAL_PARTS + 1].length, MYF(0));
    if ((log_data == NULL) ||
        mysql_file_pread(file, 1 + 2 + 2 + log_data,
                 (size_t) kfile_size_before_extension, 0, MYF(MY_NABP)))
      goto err;
    /*
      remember if the data file was created or not, to know if Recovery can
      do it or not, in the future
    */
    log_data[0]= MY_TEST(flags & HA_DONT_TOUCH_DATA);
    int2store(log_data + 1, kfile_size_before_extension);
    int2store(log_data + 1 + 2, share.base.keystart);
    log_array[TRANSLOG_INTERNAL_PARTS + 0].str= (uchar *)name;
    /* we store the end-zero, for Recovery to just pass it to my_create() */
    log_array[TRANSLOG_INTERNAL_PARTS + 0].length= strlen(name) + 1;
    log_array[TRANSLOG_INTERNAL_PARTS + 1].str= log_data;
    /* symlink description is also needed for re-creation by Recovery: */
    {
      const char *s= ci->data_file_name ? ci->data_file_name : empty_string;
      log_array[TRANSLOG_INTERNAL_PARTS + 2].str= (uchar*)s;
      log_array[TRANSLOG_INTERNAL_PARTS + 2].length= strlen(s) + 1;
      s= ci->index_file_name ? ci->index_file_name : empty_string;
      log_array[TRANSLOG_INTERNAL_PARTS + 3].str= (uchar*)s;
      log_array[TRANSLOG_INTERNAL_PARTS + 3].length= strlen(s) + 1;
    }
    for (k= TRANSLOG_INTERNAL_PARTS;
         k < (sizeof(log_array)/sizeof(log_array[0])); k++)
      total_rec_length+= (translog_size_t) log_array[k].length;
    /**
       For this record to be of any use for Recovery, we need the upper
       MySQL layer to be crash-safe, which it is not now (that would require
       work using the ddl_log of sql/sql_table.cc); when it is, we should
       reconsider the moment of writing this log record (before or after op,
       under THR_LOCK_maria or not...), how to use it in Recovery.
       For now this record can serve when we apply logs to a backup,
       so we sync it. This happens before the data file is created. If the
       data file was created before, and we crashed before writing the log
       record, at restart the table may be used, so we would not have a
       trustable history in the log (impossible to apply this log to a
       backup). The way we do it, if we crash before writing the log record
       then there is no data file and the table cannot be used.
       @todo Note that in case of TRUNCATE TABLE we also come here; for
       Recovery to be able to finish TRUNCATE TABLE, instead of leaving a
       half-truncated table, we should log the record at start of
       maria_create(); for that we shouldn't write to the index file but to a
       buffer (DYNAMIC_STRING), put the buffer into the record, then put the
       buffer into the index file (so, change _ma_keydef_write() etc). That
       would also enable Recovery to finish a CREATE TABLE. The final result
       would be that we would be able to finish what the SQL layer has asked
       for: it would be atomic.
       When in CREATE/TRUNCATE (or DROP or RENAME or REPAIR) we have not
       called external_lock(), so have no TRN. It does not matter, as all
       these operations are non-transactional and sync their files.
    */
    if (unlikely(translog_write_record(&lsn,
                                       LOGREC_REDO_CREATE_TABLE,
                                       &dummy_transaction_object, NULL,
                                       total_rec_length,
                                       sizeof(log_array)/sizeof(log_array[0]),
                                       log_array, NULL, NULL) ||
                 translog_flush(lsn)))
      goto err;
    share.kfile.file= file;
    DBUG_EXECUTE_IF("maria_flush_whole_log",
                    {
                      DBUG_PRINT("maria_flush_whole_log", ("now"));
                      translog_flush(translog_get_horizon());
                    });
    DBUG_EXECUTE_IF("maria_crash_create_table",
                    {
                      DBUG_PRINT("maria_crash_create_table", ("now"));
                      DBUG_ABORT();
                    });
    /*
      store LSN into file, needed for Recovery to not be confused if a
      DROP+CREATE happened (applying REDOs to the wrong table).
    */
    if (_ma_update_state_lsns_sub(&share, lsn, trnman_get_min_safe_trid(),
                                  FALSE, TRUE))
      goto err;
    my_free(log_data);
  }

  if (!(flags & HA_DONT_TOUCH_DATA))
  {
    if (ci->data_file_name)
    {
      char *dext= strrchr(ci->data_file_name, '.');
      int have_dext= dext && !strcmp(dext, MARIA_NAME_DEXT);

      if (tmp_table)
      {
        char *path;
        /* chop off the table name, tempory tables use generated name */
        if ((path= strrchr(ci->data_file_name, FN_LIBCHAR)))
          *path= '\0';
        fn_format(dfilename, name, ci->data_file_name, MARIA_NAME_DEXT,
                  MY_REPLACE_DIR | MY_UNPACK_FILENAME | MY_APPEND_EXT);
      }
      else
      {
        fn_format(dfilename, ci->data_file_name, "", MARIA_NAME_DEXT,
                  MY_UNPACK_FILENAME |
                  (have_dext ? MY_REPLACE_EXT : MY_APPEND_EXT));
      }
      fn_format(dlinkname, name, "",MARIA_NAME_DEXT,
                MY_UNPACK_FILENAME | MY_APPEND_EXT);
      dlinkname_ptr= dlinkname;
      create_flag=0;
    }
    else
    {
      fn_format(dfilename,name,"", MARIA_NAME_DEXT,
                MY_UNPACK_FILENAME | MY_APPEND_EXT);
      create_flag= (flags & HA_CREATE_KEEP_FILES) ? 0 : MY_DELETE_OLD;
    }
    if ((dfile=
         mysql_file_create_with_symlink(key_file_dfile, dlinkname_ptr,
                                        dfilename, 0, create_mode,
                                        MYF(MY_WME | create_flag | sync_dir))) < 0)
      goto err;
    errpos=3;

    if (_ma_initialize_data_file(&share, dfile))
      goto err;
  }

	/* 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 (!internal_table && sync_dir && mysql_file_sync(file, MYF(0)))
    goto err;

  if (! (flags & HA_DONT_TOUCH_DATA))
  {
#ifdef USE_RELOC
    if (mysql_file_chsize(key_file_dfile, dfile,
                          share.base.min_pack_length*ci->reloc_rows,0,MYF(0)))
      goto err;
#endif
    if (!internal_table && sync_dir && mysql_file_sync(dfile, MYF(0)))
      goto err;
    if (mysql_file_close(dfile,MYF(0)))
      goto err;
  }
  if (!internal_table)
    mysql_mutex_unlock(&THR_LOCK_maria);
  res= 0;
  my_free((char*) rec_per_key_part);
  ma_crypt_free(&share);
  errpos=0;
  if (mysql_file_close(file,MYF(0)))
    res= my_errno;
  DBUG_RETURN(res);

err:
  if (!internal_table)
    mysql_mutex_unlock(&THR_LOCK_maria);

err_no_lock:
  save_errno=my_errno;
  switch (errpos) {
  case 3:
    mysql_file_close(dfile, MYF(0));
    if (! (flags & HA_DONT_TOUCH_DATA))
    {
      mysql_file_delete(key_file_dfile, dfilename, MYF(sync_dir));
      if (dlinkname_ptr)
        mysql_file_delete(key_file_dfile, dlinkname_ptr, MYF(sync_dir));
    }
    /* fall through */
  case 1:
    mysql_file_close(file, MYF(0));
    if (! (flags & HA_DONT_TOUCH_DATA))
    {
      mysql_file_delete(key_file_kfile, kfilename, MYF(sync_dir));
      if (klinkname_ptr)
        mysql_file_delete(key_file_kfile, klinkname_ptr, MYF(sync_dir));
    }
  }
  ma_crypt_free(&share);
  my_free(log_data);
  my_free(rec_per_key_part);
  DBUG_RETURN(my_errno=save_errno);		/* return the fatal errno */
}

MDEV-26351 segfault - (MARIA_HA *) 0x0 in ha_maria::extra

don't let Aria create a table that it cannot open


void end_read_record(READ_RECORD *info)
{
  /* free cache if used */
  free_cache(info);
  if (info->table)
  {
    if (info->table->is_created())
      (void) info->table->file->extra(HA_EXTRA_NO_CACHE);
    if (info->read_record != rr_quick) // otherwise quick_range does it
      (void) info->table->file->ha_index_or_rnd_end();
    info->table=0;
  }
}

MDEV-26351 segfault - (MARIA_HA *) 0x0 in ha_maria::extra

use the correct check. before invoking handler methods we
need to know that the table was opened, not only created.


bool st_select_lex::optimize_unflattened_subqueries(bool const_only)
{
  SELECT_LEX_UNIT *next_unit= NULL;
  for (SELECT_LEX_UNIT *un= first_inner_unit();
       un;
       un= next_unit ? next_unit : un->next_unit())
  {
    Item_subselect *subquery_predicate= un->item;
    next_unit= NULL;

    if (subquery_predicate)
    {
      if (!subquery_predicate->fixed)
      {
	/*
	 This subquery was excluded as part of some expression so it is
	 invisible from all prepared expression.
       */
	next_unit= un->next_unit();
	un->exclude_level();
	if (next_unit)
	  continue;
	break;
      }
      if (subquery_predicate->substype() == Item_subselect::IN_SUBS)
      {
        Item_in_subselect *in_subs= (Item_in_subselect*) subquery_predicate;
        if (in_subs->is_jtbm_merged)
          continue;
      }

      if (const_only && !subquery_predicate->const_item())
      {
        /* Skip non-constant subqueries if the caller asked so. */
        continue;
      }

      bool empty_union_result= true;
      bool is_correlated_unit= false;
      bool first= true;
      bool union_plan_saved= false;
      /*
        If the subquery is a UNION, optimize all the subqueries in the UNION. If
        there is no UNION, then the loop will execute once for the subquery.
      */
      for (SELECT_LEX *sl= un->first_select(); sl; sl= sl->next_select())
      {
        JOIN *inner_join= sl->join;
        if (first)
          first= false;
        else
        {
          if (!union_plan_saved)
          {
            union_plan_saved= true;
            if (un->save_union_explain(un->thd->lex->explain))
              return true; /* Failure */
          }
        }
        if (!inner_join)
          continue;
        SELECT_LEX *save_select= un->thd->lex->current_select;
        ulonglong save_options;
        int res;
        /* We need only 1 row to determine existence */
        un->set_limit(un->global_parameters());
        un->thd->lex->current_select= sl;
        save_options= inner_join->select_options;
        if (options & SELECT_DESCRIBE)
        {
          /* Optimize the subquery in the context of EXPLAIN. */
          sl->set_explain_type(FALSE);
          sl->options|= SELECT_DESCRIBE;
          inner_join->select_options|= SELECT_DESCRIBE;
        }
        if ((res= inner_join->optimize()))
          return TRUE;
        if (!inner_join->cleaned)
          sl->update_used_tables();
        sl->update_correlated_cache();
        is_correlated_unit|= sl->is_correlated;
        inner_join->select_options= save_options;
        un->thd->lex->current_select= save_select;

        Explain_query *eq;
        if ((eq= inner_join->thd->lex->explain))
        {
          Explain_select *expl_sel;
          if ((expl_sel= eq->get_select(inner_join->select_lex->select_number)))
          {
            sl->set_explain_type(TRUE);
            expl_sel->select_type= sl->type;
          }
        }

        if (empty_union_result)
        {
          /*
            If at least one subquery in a union is non-empty, the UNION result
            is non-empty. If there is no UNION, the only subquery is non-empy.
          */
          empty_union_result= inner_join->empty_result();
        }
        if (res)
          return TRUE;
      }
      if (empty_union_result)
        subquery_predicate->no_rows_in_result();
      if (!is_correlated_unit)
        un->uncacheable&= ~UNCACHEABLE_DEPENDENT;
      subquery_predicate->is_correlated= is_correlated_unit;
    }
  }
  return FALSE;
}

MDEV-25636: Bug report: abortion in sql/sql_parse.cc:6294

The asserion failure was caused by this query

  select /*id=1*/ from t1
  where
   col= ( select /*id=2*/ from ... where corr_cond1
          union
          select /*id=4*/ from ... where corr_cond2)

Here,
- select with id=2 was correlated due to corr_cond1.
- select with id=4 was initially correlated due to corr_cond2, but then
  the optimizer optimized away the correlation, making the select with id=4
  uncorrelated.

However, since select with id=2 remained correlated, the execution had to
re-compute the whole UNION. When it tried to execute select with id=4, it
hit an assertion  (join buffer already free'd).

This is because select with id=4 has freed its execution structures after
it has been executed once. The select is uncorrelated, so it did not expect
it would need to be executed for the second time.

Fixed this by adding this logic in
st_select_lex::optimize_unflattened_subqueries():

  If a member of a UNION is correlated, mark all its members as
  correlated, so that they are prepared to be executed multiple times.


bool st_select_lex::setup_ref_array(THD *thd, uint order_group_num)
{

  if (!((options & SELECT_DISTINCT) && !group_list.elements))
    hidden_bit_fields= 0;

  // find_order_in_list() may need some extra space, so multiply by two.
  order_group_num*= 2;

  /*
    We have to create array in prepared statement memory if it is a
    prepared statement
  */
  Query_arena *arena= thd->stmt_arena;
  const uint n_elems= (n_sum_items +
                       n_child_sum_items +
                       item_list.elements +
                       select_n_reserved +
                       select_n_having_items +
                       select_n_where_fields +
                       order_group_num +
                       hidden_bit_fields +
                       fields_in_window_functions) * 5;
  if (!ref_pointer_array.is_null())
  {
    /*
      We need to take 'n_sum_items' into account when allocating the array,
      and this may actually increase during the optimization phase due to
      MIN/MAX rewrite in Item_in_subselect::single_value_transformer.
      In the usual case we can reuse the array from the prepare phase.
      If we need a bigger array, we must allocate a new one.
     */
    if (ref_pointer_array.size() >= n_elems)
      return false;
   }
  Item **array= static_cast<Item**>(arena->alloc(sizeof(Item*) * n_elems));
  if (array != NULL)
    ref_pointer_array= Ref_ptr_array(array, n_elems);

  return array == NULL;
}

MDEV-26350: select_lex->ref_pointer_array.size() % 5 == 0

Due to an integer overflow an invalid size of ref_pointer_array could be
allocated.

Using size_t allows this continue. Allocation failures are
handled gracefully if the value is too big.

Thanks to Zuming Jiang for the bug report and fuzzing MariaDB.

Reviewer: Sanja


void ha_maria::drop_table(const char *name)
{
  DBUG_ASSERT(file->s->temporary);
  (void) ha_close();
  (void) maria_delete_table_files(name, 1, MY_WME);
}

MDEV-25787 Bug report: crash on SELECT DISTINCT thousands_blob_fields

fix a debug assert to account for not opened temp tables


bool open_table(THD *thd, TABLE_LIST *table_list, Open_table_context *ot_ctx)
{
  TABLE *table;
  const char *key;
  uint	key_length;
  const char *alias= table_list->alias.str;
  uint flags= ot_ctx->get_flags();
  MDL_ticket *mdl_ticket;
  TABLE_SHARE *share;
  uint gts_flags;
  bool from_share= false;
#ifdef WITH_PARTITION_STORAGE_ENGINE
  int part_names_error=0;
#endif
  DBUG_ENTER("open_table");

  /*
    The table must not be opened already. The table can be pre-opened for
    some statements if it is a temporary table.

    open_temporary_table() must be used to open temporary tables.
  */
  DBUG_ASSERT(!table_list->table);

  /* an open table operation needs a lot of the stack space */
  if (check_stack_overrun(thd, STACK_MIN_SIZE_FOR_OPEN, (uchar *)&alias))
    DBUG_RETURN(TRUE);

  if (!(flags & MYSQL_OPEN_IGNORE_KILLED) && thd->killed)
  {
    thd->send_kill_message();
    DBUG_RETURN(TRUE);
  }

  /*
    Check if we're trying to take a write lock in a read only transaction.

    Note that we allow write locks on log tables as otherwise logging
    to general/slow log would be disabled in read only transactions.
  */
  if (table_list->mdl_request.is_write_lock_request() &&
      thd->tx_read_only &&
      !(flags & (MYSQL_LOCK_LOG_TABLE | MYSQL_OPEN_HAS_MDL_LOCK)))
  {
    my_error(ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION, MYF(0));
    DBUG_RETURN(true);
  }

  if (!table_list->db.str)
  {
    my_error(ER_NO_DB_ERROR, MYF(0));
    DBUG_RETURN(true);
  }

  key_length= get_table_def_key(table_list, &key);

  /*
    If we're in pre-locked or LOCK TABLES mode, let's try to find the
    requested table in the list of pre-opened and locked tables. If the
    table is not there, return an error - we can't open not pre-opened
    tables in pre-locked/LOCK TABLES mode.
    TODO: move this block into a separate function.
  */
  if (thd->locked_tables_mode &&
      ! (flags & MYSQL_OPEN_GET_NEW_TABLE))
  {						// Using table locks
    TABLE *best_table= 0;
    int best_distance= INT_MIN;
    for (table=thd->open_tables; table ; table=table->next)
    {
      if (table->s->table_cache_key.length == key_length &&
	  !memcmp(table->s->table_cache_key.str, key, key_length))
      {
        if (!my_strcasecmp(system_charset_info, table->alias.c_ptr(), alias) &&
            table->query_id != thd->query_id && /* skip tables already used */
            (thd->locked_tables_mode == LTM_LOCK_TABLES ||
             table->query_id == 0))
        {
          int distance= ((int) table->reginfo.lock_type -
                         (int) table_list->lock_type);

          /*
            Find a table that either has the exact lock type requested,
            or has the best suitable lock. In case there is no locked
            table that has an equal or higher lock than requested,
            we us the closest matching lock to be able to produce an error
            message about wrong lock mode on the table. The best_table
            is changed if bd < 0 <= d or bd < d < 0 or 0 <= d < bd.

            distance <  0 - No suitable lock found
            distance >  0 - we have lock mode higher then we require
            distance == 0 - we have lock mode exactly which we need
          */
          if ((best_distance < 0 && distance > best_distance) ||
              (distance >= 0 && distance < best_distance))
          {
            best_distance= distance;
            best_table= table;
            if (best_distance == 0)
            {
              /*
                We have found a perfect match and can finish iterating
                through open tables list. Check for table use conflict
                between calling statement and SP/trigger is done in
                lock_tables().
              */
              break;
            }
          }
        }
      }
    }
    if (best_table)
    {
      table= best_table;
      table->query_id= thd->query_id;
      table->init(thd, table_list);
      DBUG_PRINT("info",("Using locked table"));
#ifdef WITH_PARTITION_STORAGE_ENGINE
      part_names_error= set_partitions_as_used(table_list, table);
#endif
      goto reset;
    }

    if (is_locked_view(thd, table_list))
    {
      if (table_list->sequence)
      {
        my_error(ER_NOT_SEQUENCE, MYF(0), table_list->db.str, table_list->alias.str);
        DBUG_RETURN(true);
      }
      DBUG_RETURN(FALSE); // VIEW
    }

    /*
      No table in the locked tables list. In case of explicit LOCK TABLES
      this can happen if a user did not include the table into the list.
      In case of pre-locked mode locked tables list is generated automatically,
      so we may only end up here if the table did not exist when
      locked tables list was created.
    */
    if (thd->locked_tables_mode == LTM_PRELOCKED)
      my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db.str, table_list->alias.str);
    else
      my_error(ER_TABLE_NOT_LOCKED, MYF(0), alias);
    DBUG_RETURN(TRUE);
  }

  /*
    Non pre-locked/LOCK TABLES mode, and the table is not temporary.
    This is the normal use case.
  */

  if (! (flags & MYSQL_OPEN_HAS_MDL_LOCK))
  {
    /*
      We are not under LOCK TABLES and going to acquire write-lock/
      modify the base table. We need to acquire protection against
      global read lock until end of this statement in order to have
      this statement blocked by active FLUSH TABLES WITH READ LOCK.

      We don't need to acquire this protection under LOCK TABLES as
      such protection already acquired at LOCK TABLES time and
      not released until UNLOCK TABLES.

      We don't block statements which modify only temporary tables
      as these tables are not preserved by any form of
      backup which uses FLUSH TABLES WITH READ LOCK.

      TODO: The fact that we sometimes acquire protection against
            GRL only when we encounter table to be write-locked
            slightly increases probability of deadlock.
            This problem will be solved once Alik pushes his
            temporary table refactoring patch and we can start
            pre-acquiring metadata locks at the beggining of
            open_tables() call.
    */
    if (table_list->mdl_request.is_write_lock_request() &&
        ! (flags & (MYSQL_OPEN_IGNORE_GLOBAL_READ_LOCK |
                    MYSQL_OPEN_FORCE_SHARED_MDL |
                    MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL |
                    MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK)) &&
        ! ot_ctx->has_protection_against_grl())
    {
      MDL_request protection_request;
      MDL_deadlock_handler mdl_deadlock_handler(ot_ctx);

      if (thd->global_read_lock.can_acquire_protection())
        DBUG_RETURN(TRUE);

      protection_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE,
                              MDL_STATEMENT);

      /*
        Install error handler which if possible will convert deadlock error
        into request to back-off and restart process of opening tables.
      */
      thd->push_internal_handler(&mdl_deadlock_handler);
      bool result= thd->mdl_context.acquire_lock(&protection_request,
                                                 ot_ctx->get_timeout());
      thd->pop_internal_handler();

      if (result)
        DBUG_RETURN(TRUE);

      ot_ctx->set_has_protection_against_grl();
    }

    if (open_table_get_mdl_lock(thd, ot_ctx, &table_list->mdl_request,
                                flags, &mdl_ticket) ||
        mdl_ticket == NULL)
    {
      DEBUG_SYNC(thd, "before_open_table_wait_refresh");
      DBUG_RETURN(TRUE);
    }
    DEBUG_SYNC(thd, "after_open_table_mdl_shared");
  }
  else
  {
    /*
      Grab reference to the MDL lock ticket that was acquired
      by the caller.
    */
    mdl_ticket= table_list->mdl_request.ticket;
  }

  if (table_list->open_strategy == TABLE_LIST::OPEN_IF_EXISTS)
  {
    if (!ha_table_exists(thd, &table_list->db, &table_list->table_name))
      DBUG_RETURN(FALSE);
  }
  else if (table_list->open_strategy == TABLE_LIST::OPEN_STUB)
    DBUG_RETURN(FALSE);

  /* Table exists. Let us try to open it. */

  if (table_list->i_s_requested_object & OPEN_TABLE_ONLY)
    gts_flags= GTS_TABLE;
  else if (table_list->i_s_requested_object &  OPEN_VIEW_ONLY)
    gts_flags= GTS_VIEW;
  else
    gts_flags= GTS_TABLE | GTS_VIEW;

retry_share:

  share= tdc_acquire_share(thd, table_list, gts_flags, &table);

  if (unlikely(!share))
  {
    /*
      Hide "Table doesn't exist" errors if the table belongs to a view.
      The check for thd->is_error() is necessary to not push an
      unwanted error in case the error was already silenced.
      @todo Rework the alternative ways to deal with ER_NO_SUCH TABLE.
    */
    if (thd->is_error())
    {
      if (table_list->parent_l)
      {
        thd->clear_error();
        my_error(ER_WRONG_MRG_TABLE, MYF(0));
      }
      else if (table_list->belong_to_view)
      {
        TABLE_LIST *view= table_list->belong_to_view;
        thd->clear_error();
        my_error(ER_VIEW_INVALID, MYF(0),
                 view->view_db.str, view->view_name.str);
      }
    }
    DBUG_RETURN(TRUE);
  }

  /*
    Check if this TABLE_SHARE-object corresponds to a view. Note, that there is
    no need to check TABLE_SHARE::tdc.flushed as we do for regular tables,
    because view shares are always up to date.
  */
  if (share->is_view)
  {
    /*
      If parent_l of the table_list is non null then a merge table
      has this view as child table, which is not supported.
    */
    if (table_list->parent_l)
    {
      my_error(ER_WRONG_MRG_TABLE, MYF(0));
      goto err_lock;
    }
    if (table_list->sequence)
    {
      my_error(ER_NOT_SEQUENCE, MYF(0), table_list->db.str,
               table_list->alias.str);
      goto err_lock;
    }
    /*
      This table is a view. Validate its metadata version: in particular,
      that it was a view when the statement was prepared.
    */
    if (check_and_update_table_version(thd, table_list, share))
      goto err_lock;

    /* Open view */
    if (mysql_make_view(thd, share, table_list, false))
      goto err_lock;

    /* TODO: Don't free this */
    tdc_release_share(share);

    DBUG_ASSERT(table_list->view);

    DBUG_RETURN(FALSE);
  }

#ifdef WITH_WSREP
  if (!((flags & MYSQL_OPEN_IGNORE_FLUSH) ||
        (thd->wsrep_applier)))
#else
  if (!(flags & MYSQL_OPEN_IGNORE_FLUSH))
#endif
  {
    if (share->tdc->flushed)
    {
      DBUG_PRINT("info", ("Found old share version: %lld  current: %lld",
                          share->tdc->version, tdc_refresh_version()));
      /*
        We already have an MDL lock. But we have encountered an old
        version of table in the table definition cache which is possible
        when someone changes the table version directly in the cache
        without acquiring a metadata lock (e.g. this can happen during
        "rolling" FLUSH TABLE(S)).
        Release our reference to share, wait until old version of
        share goes away and then try to get new version of table share.
      */
      if (table)
        tc_release_table(table);
      else
        tdc_release_share(share);

      MDL_deadlock_handler mdl_deadlock_handler(ot_ctx);
      bool wait_result;

      thd->push_internal_handler(&mdl_deadlock_handler);
      wait_result= tdc_wait_for_old_version(thd, table_list->db.str,
                                            table_list->table_name.str,
                                            ot_ctx->get_timeout(),
                                            mdl_ticket->get_deadlock_weight());
      thd->pop_internal_handler();

      if (wait_result)
        DBUG_RETURN(TRUE);

      goto retry_share;
    }

    if (thd->open_tables && thd->open_tables->s->tdc->flushed)
    {
      /*
        If the version changes while we're opening the tables,
        we have to back off, close all the tables opened-so-far,
        and try to reopen them. Note: refresh_version is currently
        changed only during FLUSH TABLES.
      */
      if (table)
        tc_release_table(table);
      else
        tdc_release_share(share);
      (void)ot_ctx->request_backoff_action(Open_table_context::OT_REOPEN_TABLES,
                                           NULL);
      DBUG_RETURN(TRUE);
    }
  }

  if (table)
  {
    DBUG_ASSERT(table->file != NULL);
    MYSQL_REBIND_TABLE(table->file);
#ifdef WITH_PARTITION_STORAGE_ENGINE
    part_names_error= set_partitions_as_used(table_list, table);
#endif
  }
  else
  {
    enum open_frm_error error;

    /* make a new table */
    if (!(table=(TABLE*) my_malloc(sizeof(*table),MYF(MY_WME))))
      goto err_lock;

    error= open_table_from_share(thd, share, &table_list->alias,
                                 HA_OPEN_KEYFILE | HA_TRY_READ_ONLY,
                                 EXTRA_RECORD,
                                 thd->open_options, table, FALSE,
                                 IF_PARTITIONING(table_list->partition_names,0));

    if (unlikely(error))
    {
      my_free(table);

      if (error == OPEN_FRM_DISCOVER)
        (void) ot_ctx->request_backoff_action(Open_table_context::OT_DISCOVER,
                                              table_list);
      else if (share->crashed)
      {
        if (!(flags & MYSQL_OPEN_IGNORE_REPAIR))
          (void) ot_ctx->request_backoff_action(Open_table_context::OT_REPAIR,
                                                table_list);
        else
          table_list->crashed= 1;  /* Mark that table was crashed */
      }
      goto err_lock;
    }
    if (open_table_entry_fini(thd, share, table))
    {
      closefrm(table);
      my_free(table);
      goto err_lock;
    }

    /* Add table to the share's used tables list. */
    tc_add_table(thd, table);
    from_share= true;
  }

  table->mdl_ticket= mdl_ticket;
  table->reginfo.lock_type=TL_READ;		/* Assume read */

  table->init(thd, table_list);

  table->next= thd->open_tables;		/* Link into simple list */
  thd->set_open_tables(table);

 reset:
  /*
    Check that there is no reference to a condition from an earlier query
    (cf. Bug#58553).
  */
  DBUG_ASSERT(table->file->pushed_cond == NULL);
  table_list->updatable= 1; // It is not derived table nor non-updatable VIEW
  table_list->table= table;

  if (!from_share && table->vcol_fix_expr(thd))
    goto err_lock;

#ifdef WITH_PARTITION_STORAGE_ENGINE
  if (unlikely(table->part_info))
  {
    /* Partitions specified were incorrect.*/
    if (part_names_error)
    {
      table->file->print_error(part_names_error, MYF(0));
      DBUG_RETURN(true);
    }
  }
  else if (table_list->partition_names)
  {
    /* Don't allow PARTITION () clause on a nonpartitioned table */
    my_error(ER_PARTITION_CLAUSE_ON_NONPARTITIONED, MYF(0));
    DBUG_RETURN(true);
  }
#endif
  if (table_list->sequence && table->s->table_type != TABLE_TYPE_SEQUENCE)
  {
    my_error(ER_NOT_SEQUENCE, MYF(0), table_list->db.str, table_list->alias.str);
    DBUG_RETURN(true);
  }

  DBUG_RETURN(FALSE);

err_lock:
  tdc_release_share(share);

  DBUG_PRINT("exit", ("failed"));
  DBUG_RETURN(TRUE);
}

MDEV-24176 fixup: GCC -Wmaybe-uninitialized


bool Item_equal::create_pushable_equalities(THD *thd,
                                            List<Item> *equalities,
                                            Pushdown_checker checker,
                                            uchar *arg,
                                            bool clone_const)
{
  Item *item;
  Item *left_item= NULL;
  Item *right_item = get_const();
  Item_equal_fields_iterator it(*this);

  while ((item=it++))
  {
    left_item= item;
    if (checker && !((item->*checker) (arg)))
      continue;
    break;
  }

  if (!left_item)
    return false;

  if (right_item)
  {
    Item_func_eq *eq= 0;
    Item *left_item_clone= left_item->build_clone(thd);
    Item *right_item_clone= !clone_const ?
                            right_item : right_item->build_clone(thd);
    if (!left_item_clone || !right_item_clone)
      return true;
    eq= new (thd->mem_root) Item_func_eq(thd,
                                         left_item_clone,
                                         right_item_clone);
    if (!eq ||  equalities->push_back(eq, thd->mem_root))
      return true;
    if (!clone_const)
      right_item->set_extraction_flag(IMMUTABLE_FL);
  }

  while ((item=it++))
  {
    if (checker && !((item->*checker) (arg)))
      continue;
    Item_func_eq *eq= 0;
    Item *left_item_clone= left_item->build_clone(thd);
    Item *right_item_clone= item->build_clone(thd);
    if (!(left_item_clone && right_item_clone))
      return true;
    left_item_clone->set_item_equal(NULL);
    right_item_clone->set_item_equal(NULL);
    eq= new (thd->mem_root) Item_func_eq(thd,
                                         right_item_clone,
                                         left_item_clone);
    if (!eq || equalities->push_back(eq, thd->mem_root))
      return true;
  }
  return false;
}

MDEV-26402: A SEGV in Item_field::used_tables/update_depend_map_for_order...

When doing condition pushdown from HAVING into WHERE,
Item_equal::create_pushable_equalities() calls
item->set_extraction_flag(IMMUTABLE_FL) for constant items.
Then, Item::cleanup_excluding_immutables_processor() checks for this flag
to see if it should call item->cleanup() or leave the item as-is.

The failure happens when a constant item has a non-constant one inside it,
like:

   (tbl.col=0 AND impossible_cond)

item->walk(cleanup_excluding_immutables_processor) works in a bottom-up
way so it
1. will call Item_func_eq(tbl.col=0)->cleanup()
2. will not call Item_cond_and->cleanup (as the AND is constant)

This creates an item tree where a fixed Item has an un-fixed Item inside
it which eventually causes an assertion failure.

Fixed by introducing this rule: instead of just calling

  item->set_extraction_flag(IMMUTABLE_FL);

we call Item::walk() to set the flag for all sub-items of the item.


static void fix_dl_name(MEM_ROOT *root, LEX_STRING *dl)
{
  const size_t so_ext_len= sizeof(SO_EXT) - 1;
  if (my_strcasecmp(&my_charset_latin1, dl->str + dl->length - so_ext_len,
                    SO_EXT))
  {
    char *s= (char*)alloc_root(root, dl->length + so_ext_len + 1);
    memcpy(s, dl->str, dl->length);
    strcpy(s + dl->length, SO_EXT);
    dl->str= s;
    dl->length+= so_ext_len;
  }
}

MDEV-26323 use-after-poison issue of MariaDB server


Field *create_tmp_field_from_field(THD *thd, Field *org_field,
                                   const char *name, TABLE *table,
                                   Item_field *item)
{
  Field *new_field;

  new_field= org_field->make_new_field(thd->mem_root, table,
                                       table == org_field->table);
  if (new_field)
  {
    new_field->init(table);
    new_field->orig_table= org_field->orig_table;
    if (item)
      item->result_field= new_field;
    else
      new_field->field_name= name;
    new_field->flags|= (org_field->flags & NO_DEFAULT_VALUE_FLAG);
    if (org_field->maybe_null() || (item && item->maybe_null))
      new_field->flags&= ~NOT_NULL_FLAG;	// Because of outer join
    if (org_field->type() == MYSQL_TYPE_VAR_STRING ||
        org_field->type() == MYSQL_TYPE_VARCHAR)
      table->s->db_create_options|= HA_OPTION_PACK_RECORD;
    else if (org_field->type() == FIELD_TYPE_DOUBLE)
      ((Field_double *) new_field)->not_fixed= TRUE;
    new_field->vcol_info= 0;
    new_field->cond_selectivity= 1.0;
    new_field->next_equal_field= NULL;
    new_field->option_list= NULL;
    new_field->option_struct= NULL;
  }
  return new_field;
}

MDEV-21028 Server crashes in Query_arena::set_query_arena upon SELECT from view

if the view has algorithm=temptable it is not updatable,
so DEFAULT() for its fields is meaningless,
and thus it's NULL or 0/'' for NOT NULL columns.


multi_update::initialize_tables(JOIN *join)
{
  TABLE_LIST *table_ref;
  DBUG_ENTER("initialize_tables");

  if (unlikely((thd->variables.option_bits & OPTION_SAFE_UPDATES) &&
               error_if_full_join(join)))
    DBUG_RETURN(1);
  main_table=join->join_tab->table;
  table_to_update= 0;

  /* Any update has at least one pair (field, value) */
  DBUG_ASSERT(fields->elements);
  /*
   Only one table may be modified by UPDATE of an updatable view.
   For an updatable view first_table_for_update indicates this
   table.
   For a regular multi-update it refers to some updated table.
  */
  TABLE *first_table_for_update= ((Item_field *) fields->head())->field->table;

  /* Create a temporary table for keys to all tables, except main table */
  for (table_ref= update_tables; table_ref; table_ref= table_ref->next_local)
  {
    TABLE *table=table_ref->table;
    uint cnt= table_ref->shared;
    List<Item> temp_fields;
    ORDER     group;
    TMP_TABLE_PARAM *tmp_param;

    if (ignore)
      table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
    if (table == main_table)			// First table in join
    {
      if (safe_update_on_fly(thd, join->join_tab, table_ref, all_tables))
      {
	table_to_update= table;			// Update table on the fly
        has_vers_fields= table->vers_check_update(*fields);
	continue;
      }
    }
    table->prepare_for_position();
    join->map2table[table->tablenr]->keep_current_rowid= true;

    /*
      enable uncacheable flag if we update a view with check option
      and check option has a subselect, otherwise, the check option
      can be evaluated after the subselect was freed as independent
      (See full_local in JOIN::join_free()).
    */
    if (table_ref->check_option && !join->select_lex->uncacheable)
    {
      SELECT_LEX_UNIT *tmp_unit;
      SELECT_LEX *sl;
      for (tmp_unit= join->select_lex->first_inner_unit();
           tmp_unit;
           tmp_unit= tmp_unit->next_unit())
      {
        for (sl= tmp_unit->first_select(); sl; sl= sl->next_select())
        {
          if (sl->master_unit()->item)
          {
            join->select_lex->uncacheable|= UNCACHEABLE_CHECKOPTION;
            goto loop_end;
          }
        }
      }
    }
loop_end:

    if (table == first_table_for_update && table_ref->check_option)
    {
      table_map unupdated_tables= table_ref->check_option->used_tables() &
                                  ~first_table_for_update->map;
      List_iterator<TABLE_LIST> ti(*leaves);
      TABLE_LIST *tbl_ref;
      while ((tbl_ref= ti++) && unupdated_tables)
      {
        if (unupdated_tables & tbl_ref->table->map)
          unupdated_tables&= ~tbl_ref->table->map;
        else
          continue;
        if (unupdated_check_opt_tables.push_back(tbl_ref->table))
          DBUG_RETURN(1);
      }
    }

    tmp_param= tmp_table_param+cnt;

    /*
      Create a temporary table to store all fields that are changed for this
      table. The first field in the temporary table is a pointer to the
      original row so that we can find and update it. For the updatable
      VIEW a few following fields are rowids of tables used in the CHECK
      OPTION condition.
    */

    List_iterator_fast<TABLE> tbl_it(unupdated_check_opt_tables);
    TABLE *tbl= table;
    do
    {
      LEX_CSTRING field_name;
      field_name.str= tbl->alias.c_ptr();
      field_name.length= strlen(field_name.str);
      /*
        Signal each table (including tables referenced by WITH CHECK OPTION
        clause) for which we will store row position in the temporary table
        that we need a position to be read first.
      */
      tbl->prepare_for_position();
      join->map2table[tbl->tablenr]->keep_current_rowid= true;

      Item_temptable_rowid *item=
        new (thd->mem_root) Item_temptable_rowid(tbl);
      if (!item)
         DBUG_RETURN(1);
      item->fix_fields(thd, 0);
      if (temp_fields.push_back(item, thd->mem_root))
        DBUG_RETURN(1);
    } while ((tbl= tbl_it++));

    temp_fields.append(fields_for_table[cnt]);

    /* Make an unique key over the first field to avoid duplicated updates */
    bzero((char*) &group, sizeof(group));
    group.direction= ORDER::ORDER_ASC;
    group.item= (Item**) temp_fields.head_ref();

    tmp_param->quick_group= 1;
    tmp_param->field_count= temp_fields.elements;
    tmp_param->func_count=  temp_fields.elements - 1;
    calc_group_buffer(tmp_param, &group);
    /* small table, ignore SQL_BIG_TABLES */
    my_bool save_big_tables= thd->variables.big_tables;
    thd->variables.big_tables= FALSE;
    tmp_tables[cnt]=create_tmp_table(thd, tmp_param, temp_fields,
                                     (ORDER*) &group, 0, 0,
                                     TMP_TABLE_ALL_COLUMNS, HA_POS_ERROR, &empty_clex_str);
    thd->variables.big_tables= save_big_tables;
    if (!tmp_tables[cnt])
      DBUG_RETURN(1);
    tmp_tables[cnt]->file->extra(HA_EXTRA_WRITE_CACHE);
  }
  join->tmp_table_keep_current_rowid= TRUE;
  DBUG_RETURN(0);
}

MDEV-28095 crash in multi-update and implicit grouping

disallow implicit grouping in multi-update.
explicit GROUP BY is not allowed by the grammar.


create_worker_threads(uint n)
{
	comp_thread_ctxt_t	*threads;
	uint 			i;

	threads = (comp_thread_ctxt_t *)
		my_malloc(sizeof(comp_thread_ctxt_t) * n, MYF(MY_FAE));

	for (i = 0; i < n; i++) {
		comp_thread_ctxt_t *thd = threads + i;

		thd->num = i + 1;
		thd->started = FALSE;
		thd->cancelled = FALSE;
		thd->data_avail = FALSE;

		thd->to = (char *) my_malloc(COMPRESS_CHUNK_SIZE +
						   MY_QLZ_COMPRESS_OVERHEAD,
						   MYF(MY_FAE));

		/* Initialize the control mutex and condition var */
		if (pthread_mutex_init(&thd->ctrl_mutex, NULL) ||
		    pthread_cond_init(&thd->ctrl_cond, NULL)) {
			goto err;
		}

		/* Initialize and data mutex and condition var */
		if (pthread_mutex_init(&thd->data_mutex, NULL) ||
		    pthread_cond_init(&thd->data_cond, NULL)) {
			goto err;
		}

		pthread_mutex_lock(&thd->ctrl_mutex);

		if (pthread_create(&thd->id, NULL, compress_worker_thread_func,
				   thd)) {
			msg("compress: pthread_create() failed: "
			    "errno = %d", errno);
			goto err;
		}
	}

	/* Wait for the threads to start */
	for (i = 0; i < n; i++) {
		comp_thread_ctxt_t *thd = threads + i;

		while (thd->started == FALSE)
			pthread_cond_wait(&thd->ctrl_cond, &thd->ctrl_mutex);
		pthread_mutex_unlock(&thd->ctrl_mutex);
	}

	return threads;

err:
	my_free(threads);
	return NULL;
}

MDEV-26561 Fix a bug due to unreleased lock

Fix a bug of unreleased lock ctrl_mutex in the method create_worker_threads


static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf,
                                      size_t size, bool no_rss)
{
    VirtIONet *n = qemu_get_nic_opaque(nc);
    VirtIONetQueue *q = virtio_net_get_subqueue(nc);
    VirtIODevice *vdev = VIRTIO_DEVICE(n);
    struct iovec mhdr_sg[VIRTQUEUE_MAX_SIZE];
    struct virtio_net_hdr_mrg_rxbuf mhdr;
    unsigned mhdr_cnt = 0;
    size_t offset, i, guest_offset;

    if (!virtio_net_can_receive(nc)) {
        return -1;
    }

    if (!no_rss && n->rss_data.enabled && n->rss_data.enabled_software_rss) {
        int index = virtio_net_process_rss(nc, buf, size);
        if (index >= 0) {
            NetClientState *nc2 = qemu_get_subqueue(n->nic, index);
            return virtio_net_receive_rcu(nc2, buf, size, true);
        }
    }

    /* hdr_len refers to the header we supply to the guest */
    if (!virtio_net_has_buffers(q, size + n->guest_hdr_len - n->host_hdr_len)) {
        return 0;
    }

    if (!receive_filter(n, buf, size))
        return size;

    offset = i = 0;

    while (offset < size) {
        VirtQueueElement *elem;
        int len, total;
        const struct iovec *sg;

        total = 0;

        elem = virtqueue_pop(q->rx_vq, sizeof(VirtQueueElement));
        if (!elem) {
            if (i) {
                virtio_error(vdev, "virtio-net unexpected empty queue: "
                             "i %zd mergeable %d offset %zd, size %zd, "
                             "guest hdr len %zd, host hdr len %zd "
                             "guest features 0x%" PRIx64,
                             i, n->mergeable_rx_bufs, offset, size,
                             n->guest_hdr_len, n->host_hdr_len,
                             vdev->guest_features);
            }
            return -1;
        }

        if (elem->in_num < 1) {
            virtio_error(vdev,
                         "virtio-net receive queue contains no in buffers");
            virtqueue_detach_element(q->rx_vq, elem, 0);
            g_free(elem);
            return -1;
        }

        sg = elem->in_sg;
        if (i == 0) {
            assert(offset == 0);
            if (n->mergeable_rx_bufs) {
                mhdr_cnt = iov_copy(mhdr_sg, ARRAY_SIZE(mhdr_sg),
                                    sg, elem->in_num,
                                    offsetof(typeof(mhdr), num_buffers),
                                    sizeof(mhdr.num_buffers));
            }

            receive_header(n, sg, elem->in_num, buf, size);
            if (n->rss_data.populate_hash) {
                offset = sizeof(mhdr);
                iov_from_buf(sg, elem->in_num, offset,
                             buf + offset, n->host_hdr_len - sizeof(mhdr));
            }
            offset = n->host_hdr_len;
            total += n->guest_hdr_len;
            guest_offset = n->guest_hdr_len;
        } else {
            guest_offset = 0;
        }

        /* copy in packet.  ugh */
        len = iov_from_buf(sg, elem->in_num, guest_offset,
                           buf + offset, size - offset);
        total += len;
        offset += len;
        /* If buffers can't be merged, at this point we
         * must have consumed the complete packet.
         * Otherwise, drop it. */
        if (!n->mergeable_rx_bufs && offset < size) {
            virtqueue_unpop(q->rx_vq, elem, total);
            g_free(elem);
            return size;
        }

        /* signal other side */
        virtqueue_fill(q->rx_vq, elem, total, i++);
        g_free(elem);
    }

    if (mhdr_cnt) {
        virtio_stw_p(vdev, &mhdr.num_buffers, i);
        iov_from_buf(mhdr_sg, mhdr_cnt,
                     0,
                     &mhdr.num_buffers, sizeof mhdr.num_buffers);
    }

    virtqueue_flush(q->rx_vq, i);
    virtio_notify(vdev, q->rx_vq);

    return size;
}


static void do_viewlog(HttpRequest req, HttpResponse res) {
        if (is_readonly(req)) {
                send_error(req, res, SC_FORBIDDEN, "You do not have sufficient privileges to access this page");
                return;
        }
        do_head(res, "_viewlog", "View log", 100);
        if ((Run.flags & Run_Log) && ! (Run.flags & Run_UseSyslog)) {
                FILE *f = fopen(Run.files.log, "r");
                if (f) {
                        size_t n;
                        char buf[512];
                        StringBuffer_append(res->outputbuffer, "

<form><textarea cols=120 rows=30 readonly>");
                        while ((n = fread(buf, sizeof(char), sizeof(buf) - 1, f)) > 0) {
                                buf[n] = 0;
                                StringBuffer_append(res->outputbuffer, "%s", buf);
                        }
                        fclose(f);
                        StringBuffer_append(res->outputbuffer, "</textarea></form>");
                } else {
                        StringBuffer_append(res->outputbuffer, "Error opening logfile: %s", STRERROR);
                }
        } else {
                StringBuffer_append(res->outputbuffer,
                                    "Cannot view logfile:
");
                if (! (Run.flags & Run_Log))
                        StringBuffer_append(res->outputbuffer, "Monit was started without logging");
                else
                        StringBuffer_append(res->outputbuffer, "Monit uses syslog");
        }
        do_foot(res);
}


Result ZipFile::uncompressEntry (int index, const File& targetDirectory, bool shouldOverwriteFiles)
{
    auto* zei = entries.getUnchecked (index);

   #if JUCE_WINDOWS
    auto entryPath = zei->entry.filename;
   #else
    auto entryPath = zei->entry.filename.replaceCharacter ('\\', '/');
   #endif

    if (entryPath.isEmpty())
        return Result::ok();

    auto targetFile = targetDirectory.getChildFile (entryPath);

    if (entryPath.endsWithChar ('/') || entryPath.endsWithChar ('\\'))
        return targetFile.createDirectory(); // (entry is a directory, not a file)

    std::unique_ptr<InputStream> in (createStreamForEntry (index));

    if (in == nullptr)
        return Result::fail ("Failed to open the zip file for reading");

    if (targetFile.exists())
    {
        if (! shouldOverwriteFiles)
            return Result::ok();

        if (! targetFile.deleteFile())
            return Result::fail ("Failed to write to target file: " + targetFile.getFullPathName());
    }

    if (! targetFile.getParentDirectory().createDirectory())
        return Result::fail ("Failed to create target folder: " + targetFile.getParentDirectory().getFullPathName());

    if (zei->entry.isSymbolicLink)
    {
        String originalFilePath (in->readEntireStreamAsString()
                                    .replaceCharacter (L'/', File::getSeparatorChar()));

        if (! File::createSymbolicLink (targetFile, originalFilePath, true))
            return Result::fail ("Failed to create symbolic link: " + originalFilePath);
    }
    else
    {
        FileOutputStream out (targetFile);

        if (out.failedToOpen())
            return Result::fail ("Failed to write to target file: " + targetFile.getFullPathName());

        out << *in;
    }

    targetFile.setCreationTime (zei->entry.fileTime);
    targetFile.setLastModificationTime (zei->entry.fileTime);
    targetFile.setLastAccessTime (zei->entry.fileTime);

    return Result::ok();
}


static char *clean_path(char *path)
{
	char *ch;
	char *ch2;
	char *str;
	str = xmalloc(strlen(path));
	ch = path;
	ch2 = str;
	while (true) {
		*ch2 = *ch;
		ch++;
		ch2++;
		if (!*(ch-1))
			break;
		while (*(ch - 1) == '/' && *ch == '/')
			ch++;
	}
	/* get rid of trailing / characters */
	while ((ch = strrchr(str, '/'))) {
		if (ch == str)
			break;
		if (!*(ch+1))
			*ch = 0;
		else
			break;
	}
	return str;
}


int64_t GmfOpenMesh(const char *FilNam, int mod, ...)
{
   int      KwdCod, res, *PtrVer, *PtrDim, err;
   int64_t  MshIdx;
   char     str[ GmfStrSiz ];
   va_list  VarArg;
   GmfMshSct *msh;

   /*---------------------*/
   /* MESH STRUCTURE INIT */
   /*---------------------*/

   if(!(msh = calloc(1, sizeof(GmfMshSct))))
      return(0);

   MshIdx = (int64_t)msh;

   // Save the current stack environment for longjmp
   if( (err = setjmp(msh->err)) != 0)
   {
#ifdef GMFDEBUG
      printf("libMeshb : mesh %p : error %d\n", msh, err);
#endif
      if(msh->hdl != NULL)
         fclose(msh->hdl);

      if(msh->FilDes != 0)
#ifdef GMF_WINDOWS
         _close(msh->FilDes);
#else
         close(msh->FilDes);
#endif

      free(msh);
      return(0);
   }

   // Copy the FilNam into the structure
   if(strlen(FilNam) + 7 >= GmfStrSiz)
      longjmp(msh->err, -4);

   strcpy(msh->FilNam, FilNam);

   // Store the opening mod (read or write) and guess
   // the filetype (binary or ascii) depending on the extension
   msh->mod = mod;
   msh->buf = (void *)msh->DblBuf;
   msh->FltBuf = (void *)msh->DblBuf;
   msh->IntBuf = (void *)msh->DblBuf;

   if(strstr(msh->FilNam, ".meshb"))
      msh->typ |= (Bin | MshFil);
   else if(strstr(msh->FilNam, ".mesh"))
      msh->typ |= (Asc | MshFil);
   else if(strstr(msh->FilNam, ".solb"))
      msh->typ |= (Bin | SolFil);
   else if(strstr(msh->FilNam, ".sol"))
      msh->typ |= (Asc | SolFil);
   else
      longjmp(msh->err, -5);

   // Open the file in the required mod and initialize the mesh structure
   if(msh->mod == GmfRead)
   {

      /*-----------------------*/
      /* OPEN FILE FOR READING */
      /*-----------------------*/

      va_start(VarArg, mod);
      PtrVer = va_arg(VarArg, int *);
      PtrDim = va_arg(VarArg, int *);
      va_end(VarArg);

      // Read the endian coding tag, the mesh version
      // and the mesh dimension (mandatory kwd)
      if(msh->typ & Bin)
      {
         // Create the name string and open the file
#ifdef WITH_GMF_AIO
         // [Bruno] added binary flag (necessary under Windows)
         msh->FilDes = open(msh->FilNam, OPEN_READ_FLAGS, OPEN_READ_MODE);

         if(msh->FilDes <= 0)
            longjmp(msh->err, -6);

         // Read the endian coding tag
         if(read(msh->FilDes, &msh->cod, WrdSiz) != WrdSiz)
            longjmp(msh->err, -7);
#else
         // [Bruno] added binary flag (necessary under Windows)
         if(!(msh->hdl = fopen(msh->FilNam, "rb")))
            longjmp(msh->err, -8);

         // Read the endian coding tag
         safe_fread(&msh->cod, WrdSiz, 1, msh->hdl, msh->err);
#endif

         // Read the mesh version and the mesh dimension (mandatory kwd)
         if( (msh->cod != 1) && (msh->cod != 16777216) )
            longjmp(msh->err, -9);

         ScaWrd(msh, (unsigned char *)&msh->ver);

         if( (msh->ver < 1) || (msh->ver > 4) )
            longjmp(msh->err, -10);

         if( (msh->ver >= 3) && (sizeof(int64_t) != 8) )
            longjmp(msh->err, -11);

         ScaWrd(msh, (unsigned char *)&KwdCod);

         if(KwdCod != GmfDimension)
            longjmp(msh->err, -12);

         GetPos(msh);
         ScaWrd(msh, (unsigned char *)&msh->dim);
      }
      else
      {
         // Create the name string and open the file
         if(!(msh->hdl = fopen(msh->FilNam, "rb")))
            longjmp(msh->err, -13);

         do
         {
            res = fscanf(msh->hdl, "%s", str);
         }while( (res != EOF) && strcmp(str, "MeshVersionFormatted") );

         if(res == EOF)
            longjmp(msh->err, -14);

         safe_fscanf(msh->hdl, "%d", &msh->ver, msh->err);

         if( (msh->ver < 1) || (msh->ver > 4) )
            longjmp(msh->err, -15);

         do
         {
            res = fscanf(msh->hdl, "%s", str);
         }while( (res != EOF) && strcmp(str, "Dimension") );

         if(res == EOF)
            longjmp(msh->err, -16);

         safe_fscanf(msh->hdl, "%d", &msh->dim, msh->err);
      }

      if( (msh->dim != 2) && (msh->dim != 3) )
         longjmp(msh->err, -17);

      (*PtrVer) = msh->ver;
      (*PtrDim) = msh->dim;

      // Set default real numbers size
      if(msh->ver == 1)
         msh->FltSiz = 32;
      else
         msh->FltSiz = 64;

      /*------------*/
      /* KW READING */
      /*------------*/

      // Read the list of kw present in the file
      if(!ScaKwdTab(msh))
         return(0);

      return(MshIdx);
   }
   else if(msh->mod == GmfWrite)
   {

      /*-----------------------*/
      /* OPEN FILE FOR WRITING */
      /*-----------------------*/

      msh->cod = 1;

      // Check if the user provided a valid version number and dimension
      va_start(VarArg, mod);
      msh->ver = va_arg(VarArg, int);
      msh->dim = va_arg(VarArg, int);
      va_end(VarArg);

      if( (msh->ver < 1) || (msh->ver > 4) )
         longjmp(msh->err, -18);

      if( (msh->ver >= 3) && (sizeof(int64_t) != 8) )
         longjmp(msh->err, -19);

      if( (msh->dim != 2) && (msh->dim != 3) )
         longjmp(msh->err, -20);

      // Set default real numbers size
      if(msh->ver == 1)
         msh->FltSiz = 32;
      else
         msh->FltSiz = 64;

      // Create the mesh file
      if(msh->typ & Bin)
      {
         /*
          * [Bruno] replaced previous call to creat():
          * with a call to open(), because Windows needs the
          * binary flag to be specified.
          */
#ifdef WITH_GMF_AIO
         msh->FilDes = open(msh->FilNam, OPEN_WRITE_FLAGS, OPEN_WRITE_MODE);

         if(msh->FilDes <= 0)
            longjmp(msh->err, -21);
#else
         if(!(msh->hdl = fopen(msh->FilNam, "wb")))
            longjmp(msh->err, -22);
#endif
      }
      else if(!(msh->hdl = fopen(msh->FilNam, "wb")))
         longjmp(msh->err, -23);

      /*------------*/
      /* KW WRITING */
      /*------------*/

      // Write the mesh version and dimension
      if(msh->typ & Asc)
      {
         fprintf(msh->hdl, "%s %d\n\n",
               GmfKwdFmt[ GmfVersionFormatted ][0], msh->ver);
         fprintf(msh->hdl, "%s %d\n",
               GmfKwdFmt[ GmfDimension ][0], msh->dim);
      }
      else
      {
         RecWrd(msh, (unsigned char *)&msh->cod);
         RecWrd(msh, (unsigned char *)&msh->ver);
         GmfSetKwd(MshIdx, GmfDimension, 0);
         RecWrd(msh, (unsigned char *)&msh->dim);
      }

      return(MshIdx);
   }
   else
   {
      free(msh);
      return(0);
   }
}