mirror of
				https://github.com/MariaDB/server.git
				synced 2025-11-04 04:46:15 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			1528 lines
		
	
	
	
		
			51 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1528 lines
		
	
	
	
		
			51 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
 | 
						|
 | 
						|
   This program is free software; you can redistribute it and/or modify
 | 
						|
   it under the terms of the GNU General Public License as published by
 | 
						|
   the Free Software Foundation; version 2 of the License.
 | 
						|
 | 
						|
   This program is distributed in the hope that it will be useful,
 | 
						|
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
   GNU General Public License for more details.
 | 
						|
 | 
						|
   You should have received a copy of the GNU General Public License
 | 
						|
   along with this program; if not, write to the Free Software
 | 
						|
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
 | 
						|
 | 
						|
/* Create a MARIA table */
 | 
						|
 | 
						|
#include "ma_ftdefs.h"
 | 
						|
#include "ma_sp_defs.h"
 | 
						|
#include <my_bit.h>
 | 
						|
#include "ma_blockrec.h"
 | 
						|
#include "trnman_public.h"
 | 
						|
#include "trnman.h"
 | 
						|
#include "ma_crypt.h"
 | 
						|
 | 
						|
#ifdef _WIN32
 | 
						|
#include <fcntl.h>
 | 
						|
#endif
 | 
						|
#include <m_ctype.h>
 | 
						|
 | 
						|
static int compare_columns(const void *a, const void *b);
 | 
						|
 | 
						|
 | 
						|
static ulonglong update_tot_length(ulonglong tot_length, ulonglong max_rows, uint length)
 | 
						|
{
 | 
						|
  ulonglong tot_length_part;
 | 
						|
 | 
						|
  if (tot_length == ULONGLONG_MAX)
 | 
						|
    return ULONGLONG_MAX;
 | 
						|
 | 
						|
  tot_length_part= (max_rows/(ulong) ((maria_block_size -
 | 
						|
                    MAX_KEYPAGE_HEADER_SIZE - KEYPAGE_CHECKSUM_SIZE)/
 | 
						|
                    (length*2)));
 | 
						|
  if (tot_length_part >=  ULONGLONG_MAX / maria_block_size)
 | 
						|
    return ULONGLONG_MAX;
 | 
						|
 | 
						|
  if (tot_length > ULONGLONG_MAX - tot_length_part * maria_block_size)
 | 
						|
    return ULONGLONG_MAX;
 | 
						|
 | 
						|
  return tot_length + tot_length_part * maria_block_size;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  Old options is used when recreating database, from maria_chk
 | 
						|
*/
 | 
						|
 | 
						|
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)
 | 
						|
{
 | 
						|
  uint i,j;
 | 
						|
  File UNINIT_VAR(dfile), UNINIT_VAR(file);
 | 
						|
  int errpos,save_errno, create_mode= O_RDWR | O_TRUNC, res;
 | 
						|
  myf create_flag, common_flag= MY_WME, sync_dir= 0;
 | 
						|
  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= 0;
 | 
						|
  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;
 | 
						|
  TRN tmp_transaction_object;
 | 
						|
  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;
 | 
						|
  uchar   *log_data= NULL;
 | 
						|
  my_bool encrypted= ci->encrypted && 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 ((flags & HA_CREATE_TMP_TABLE) && !(flags & HA_CREATE_GLOBAL_TMP_TABLE))
 | 
						|
    common_flag|= MY_THREAD_SPECIFIC;
 | 
						|
 | 
						|
  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(PSI_INSTRUMENT_ME,
 | 
						|
                            (keys + uniques)*HA_MAX_KEY_SEG*sizeof(double) +
 | 
						|
                            (keys + uniques)*HA_MAX_KEY_SEG*sizeof(ulong) +
 | 
						|
                            sizeof(uint16) * columns,
 | 
						|
                            MYF(common_flag | 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 */
 | 
						|
  share.base.compression_algorithm= ci->compression_algorithm;
 | 
						|
  share.base.s3_block_size=         ci->s3_block_size;
 | 
						|
 | 
						|
  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;
 | 
						|
  }
 | 
						|
 | 
						|
  share.state.state.key_file_length= MY_ALIGN(info_length, maria_block_size);
 | 
						|
  DBUG_PRINT("info", ("info_length: %u", info_length));
 | 
						|
  /* There are only 16 bits for the total header length. */
 | 
						|
  if (share.state.state.key_file_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;
 | 
						|
  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;
 | 
						|
    /*
 | 
						|
      We have to mark the table as not movable as the table will contain the
 | 
						|
      maria_uuid and create_rename_lsn
 | 
						|
    */
 | 
						|
    share.state.changed|= STATE_NOT_MOVABLE;
 | 
						|
  }
 | 
						|
 | 
						|
  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));
 | 
						|
    klinkname_ptr= NullS;
 | 
						|
    /*
 | 
						|
      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(common_flag|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.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(PSI_INSTRUMENT_ME,
 | 
						|
                                                   share.base.fields *
 | 
						|
                                                   sizeof(MARIA_COLUMNDEF*),
 | 
						|
                                                   common_flag)))
 | 
						|
      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)
 | 
						|
  {
 | 
						|
    DBUG_ASSERT(share.data_file_name.length == 0);
 | 
						|
    share.data_file_name.str= (char*) name;  /* For error reporting */
 | 
						|
    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
 | 
						|
      a log of about 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(PSI_INSTRUMENT_ME,
 | 
						|
                        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.
 | 
						|
    */
 | 
						|
    trnman_init_tmp_trn_for_logging_trid(&tmp_transaction_object);
 | 
						|
    if (unlikely(translog_write_record(&lsn,
 | 
						|
                                       LOGREC_REDO_CREATE_TABLE,
 | 
						|
                                       &tmp_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_SUICIDE();
 | 
						|
                    });
 | 
						|
    /*
 | 
						|
      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, tmp_transaction_object.trid,
 | 
						|
                                  FALSE, TRUE))
 | 
						|
      goto err;
 | 
						|
    my_free(log_data);
 | 
						|
    log_data= 0;
 | 
						|
  }
 | 
						|
 | 
						|
  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;
 | 
						|
    }
 | 
						|
    ma_debug_crash_here("storage_engine_middle_of_create");
 | 
						|
    if ((dfile=
 | 
						|
         mysql_file_create_with_symlink(key_file_dfile, dlinkname_ptr,
 | 
						|
                              dfilename, 0, create_mode,
 | 
						|
                              MYF(common_flag | 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 */
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
uint maria_get_pointer_length(ulonglong file_length, uint def)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(def >= 2 && def <= 7);
 | 
						|
  if (file_length)				/* If not default */
 | 
						|
  {
 | 
						|
#ifdef NOT_YET_READY_FOR_8_BYTE_POINTERS
 | 
						|
    if (file_length >= (1ULL << 56))
 | 
						|
      def=8;
 | 
						|
    else
 | 
						|
#endif
 | 
						|
      if (file_length >= (1ULL << 48))
 | 
						|
      def=7;
 | 
						|
    else if (file_length >= (1ULL << 40))
 | 
						|
      def=6;
 | 
						|
    else if (file_length >= (1ULL << 32))
 | 
						|
      def=5;
 | 
						|
    else if (file_length >= (1ULL << 24))
 | 
						|
      def=4;
 | 
						|
    else if (file_length >= (1ULL << 16))
 | 
						|
      def=3;
 | 
						|
    else
 | 
						|
      def=2;
 | 
						|
  }
 | 
						|
  return def;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  Sort columns for records-in-block
 | 
						|
 | 
						|
  IMPLEMENTATION
 | 
						|
   Sort columns in following order:
 | 
						|
 | 
						|
   Fixed size, not null columns
 | 
						|
   Fixed length, null fields
 | 
						|
   Numbers (zero fill fields)
 | 
						|
   Variable length fields (CHAR, VARCHAR) according to length
 | 
						|
   Blobs
 | 
						|
 | 
						|
   For same kind of fields, keep fields in original order
 | 
						|
*/
 | 
						|
 | 
						|
static inline int sign(long a)
 | 
						|
{
 | 
						|
  return a < 0 ? -1 : (a > 0 ? 1 : 0);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static int compare_columns(const void *a_ptr_, const void *b_ptr_)
 | 
						|
{
 | 
						|
  const MARIA_COLUMNDEF *const *a_ptr= a_ptr_;
 | 
						|
  const MARIA_COLUMNDEF *const *b_ptr= b_ptr_;
 | 
						|
  const MARIA_COLUMNDEF *a= *a_ptr, *b= *b_ptr;
 | 
						|
  enum en_fieldtype a_type, b_type;
 | 
						|
 | 
						|
  a_type= (a->type == FIELD_CHECK) ? FIELD_NORMAL : a->type;
 | 
						|
  b_type= (b->type == FIELD_CHECK) ? FIELD_NORMAL : b->type;
 | 
						|
 | 
						|
  if (a_type == FIELD_NORMAL && !a->null_bit)
 | 
						|
  {
 | 
						|
    if (b_type != FIELD_NORMAL || b->null_bit)
 | 
						|
      return -1;
 | 
						|
    return sign((long) a->offset - (long) b->offset);
 | 
						|
  }
 | 
						|
  if (b_type == FIELD_NORMAL && !b->null_bit)
 | 
						|
    return 1;
 | 
						|
  if (a_type == b_type)
 | 
						|
    return sign((long) a->offset - (long) b->offset);
 | 
						|
  if (a_type == FIELD_NORMAL)
 | 
						|
    return -1;
 | 
						|
  if (b_type == FIELD_NORMAL)
 | 
						|
    return 1;
 | 
						|
  if (a_type == FIELD_SKIP_ZERO)
 | 
						|
    return -1;
 | 
						|
  if (b_type == FIELD_SKIP_ZERO)
 | 
						|
    return 1;
 | 
						|
  if (a->type != FIELD_BLOB && b->type != FIELD_BLOB)
 | 
						|
    if (a->length != b->length)
 | 
						|
      return sign((long) a->length - (long) b->length);
 | 
						|
  if (a_type == FIELD_BLOB)
 | 
						|
    return 1;
 | 
						|
  if (b_type == FIELD_BLOB)
 | 
						|
    return -1;
 | 
						|
  return sign((long) a->offset - (long) b->offset);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
   @brief Initialize data file
 | 
						|
 | 
						|
   @note
 | 
						|
   In BLOCK_RECORD, a freshly created datafile is one page long; while in
 | 
						|
   other formats it is 0-byte long.
 | 
						|
 */
 | 
						|
 | 
						|
int _ma_initialize_data_file(MARIA_SHARE *share, File dfile)
 | 
						|
{
 | 
						|
  if (share->data_file_type == BLOCK_RECORD)
 | 
						|
  {
 | 
						|
    share->bitmap.block_size= share->base.block_size;
 | 
						|
    share->bitmap.file.file = dfile;
 | 
						|
    return _ma_bitmap_create_first(share);
 | 
						|
  }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
   @brief Writes create_rename_lsn, skip_redo_lsn and is_of_horizon to disk,
 | 
						|
   can force.
 | 
						|
 | 
						|
   This is for special cases where:
 | 
						|
   - we don't want to write the full state to disk (so, not call
 | 
						|
   _ma_state_info_write()) because some parts of the state may be
 | 
						|
   currently inconsistent, or because it would be overkill
 | 
						|
   - we must sync these LSNs immediately for correctness.
 | 
						|
   It acquires intern_lock to protect the LSNs and state write.
 | 
						|
 | 
						|
   @param  share           table's share
 | 
						|
   @param  lsn		   LSN to write to log files
 | 
						|
   @param  create_trid     Trid to be used as state.create_trid
 | 
						|
   @param  do_sync         if the write should be forced to disk
 | 
						|
   @param  update_create_rename_lsn if this LSN should be updated or not
 | 
						|
 | 
						|
   @return Operation status
 | 
						|
     @retval 0      ok
 | 
						|
     @retval 1      error (disk problem)
 | 
						|
*/
 | 
						|
 | 
						|
int _ma_update_state_lsns(MARIA_SHARE *share, LSN lsn, TrID create_trid,
 | 
						|
                          my_bool do_sync, my_bool update_create_rename_lsn)
 | 
						|
{
 | 
						|
  int res;
 | 
						|
  DBUG_ENTER("_ma_update_state_lsns");
 | 
						|
  mysql_mutex_lock(&share->intern_lock);
 | 
						|
  res= _ma_update_state_lsns_sub(share, lsn, create_trid, do_sync,
 | 
						|
                                 update_create_rename_lsn);
 | 
						|
  mysql_mutex_unlock(&share->intern_lock);
 | 
						|
  DBUG_RETURN(res);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
   @brief Writes create_rename_lsn, skip_redo_lsn and is_of_horizon to disk,
 | 
						|
   can force.
 | 
						|
 | 
						|
   Shortcut of _ma_update_state_lsns() when we know that intern_lock is not
 | 
						|
   needed (when creating a table or opening it for the first time).
 | 
						|
 | 
						|
   @param  share           table's share
 | 
						|
   @param  lsn             LSN to write to state; if LSN_IMPOSSIBLE, write
 | 
						|
                           a LOGREC_IMPORTED_TABLE and use its LSN as lsn.
 | 
						|
   @param  create_trid     Trid to be used as state.create_trid
 | 
						|
   @param  do_sync         if the write should be forced to disk
 | 
						|
   @param  update_create_rename_lsn if this LSN should be updated or not
 | 
						|
 | 
						|
   @return Operation status
 | 
						|
     @retval 0      ok
 | 
						|
     @retval 1      error (disk problem)
 | 
						|
*/
 | 
						|
 | 
						|
#if defined(_MSC_VER) && (_MSC_VER == 1310)
 | 
						|
/*
 | 
						|
 Visual Studio 2003 compiler produces internal compiler error
 | 
						|
 in this function. Disable optimizations to workaround.
 | 
						|
*/
 | 
						|
#pragma optimize("",off)
 | 
						|
#endif
 | 
						|
int _ma_update_state_lsns_sub(MARIA_SHARE *share, LSN lsn, TrID create_trid,
 | 
						|
                              my_bool do_sync,
 | 
						|
                              my_bool update_create_rename_lsn)
 | 
						|
{
 | 
						|
  uchar buf[LSN_STORE_SIZE * 3], *ptr;
 | 
						|
  uchar trid_buff[8];
 | 
						|
  File file= share->kfile.file;
 | 
						|
  DBUG_ENTER("_ma_update_state_lsns_sub");
 | 
						|
  DBUG_ASSERT(file >= 0);
 | 
						|
  CRASH_IF_S3_TABLE(share);
 | 
						|
 | 
						|
  if (lsn == LSN_IMPOSSIBLE)
 | 
						|
  {
 | 
						|
    int res;
 | 
						|
    LEX_CUSTRING log_array[TRANSLOG_INTERNAL_PARTS + 1];
 | 
						|
    /* table name is logged only for information */
 | 
						|
    log_array[TRANSLOG_INTERNAL_PARTS + 0].str=
 | 
						|
      (uchar *)(share->open_file_name.str);
 | 
						|
    log_array[TRANSLOG_INTERNAL_PARTS + 0].length=
 | 
						|
      share->open_file_name.length + 1;
 | 
						|
    if ((res= translog_write_record(&lsn, LOGREC_IMPORTED_TABLE,
 | 
						|
                                    &dummy_transaction_object, NULL,
 | 
						|
                                    (translog_size_t)
 | 
						|
                                    log_array[TRANSLOG_INTERNAL_PARTS +
 | 
						|
                                              0].length,
 | 
						|
                                    sizeof(log_array)/sizeof(log_array[0]),
 | 
						|
                                    log_array, NULL, NULL)))
 | 
						|
      DBUG_RETURN(res);
 | 
						|
  }
 | 
						|
 | 
						|
  for (ptr= buf; ptr < (buf + sizeof(buf)); ptr+= LSN_STORE_SIZE)
 | 
						|
    lsn_store(ptr, lsn);
 | 
						|
  share->state.skip_redo_lsn= share->state.is_of_horizon= lsn;
 | 
						|
  share->state.create_trid= create_trid;
 | 
						|
  mi_int8store(trid_buff, create_trid);
 | 
						|
 | 
						|
  /*
 | 
						|
    Update create_rename_lsn if update was requested or if the old one had an
 | 
						|
    impossible value.
 | 
						|
  */
 | 
						|
  if (update_create_rename_lsn ||
 | 
						|
      (share->state.create_rename_lsn > lsn && lsn != LSN_IMPOSSIBLE))
 | 
						|
  {
 | 
						|
    share->state.create_rename_lsn= lsn;
 | 
						|
    if (share->id != 0)
 | 
						|
    {
 | 
						|
      /*
 | 
						|
        If OP is the operation which is calling us, if table is later written,
 | 
						|
        we could see in the log:
 | 
						|
        FILE_ID ... REDO_OP ... REDO_INSERT.
 | 
						|
        (that can happen in real life at least with OP=REPAIR).
 | 
						|
        As FILE_ID will be ignored by Recovery because it is <
 | 
						|
        create_rename_lsn, REDO_INSERT would be ignored too, wrongly.
 | 
						|
        To avoid that, we force a LOGREC_FILE_ID to be logged at next write:
 | 
						|
      */
 | 
						|
      translog_deassign_id_from_share(share);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else
 | 
						|
    lsn_store(buf, share->state.create_rename_lsn);
 | 
						|
  DBUG_RETURN(my_pwrite(file, buf, sizeof(buf),
 | 
						|
                        sizeof(share->state.header) +
 | 
						|
                        MARIA_FILE_CREATE_RENAME_LSN_OFFSET, MYF(MY_NABP)) ||
 | 
						|
              my_pwrite(file, trid_buff, sizeof(trid_buff),
 | 
						|
                        sizeof(share->state.header) +
 | 
						|
                        MARIA_FILE_CREATE_TRID_OFFSET, MYF(MY_NABP)) ||
 | 
						|
              (do_sync && mysql_file_sync(file, MYF(0))));
 | 
						|
}
 | 
						|
#if defined(_MSC_VER) && (_MSC_VER == 1310)
 | 
						|
#pragma optimize("",on)
 | 
						|
#endif /*VS2003 compiler bug workaround*/
 |