unireg.cc 26.2 KB
Newer Older
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3 4 5 6
   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; either version 2 of the License, or
   (at your option) any later version.
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
7

bk@work.mysql.com's avatar
bk@work.mysql.com committed
8 9 10 11
   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.
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
12

bk@work.mysql.com's avatar
bk@work.mysql.com committed
13 14 15 16 17 18 19 20
   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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */


/*
  Functions to create a unireg form-file from a FIELD and a fieldname-fieldinfo
  struct.
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
21
  In the following functions FIELD * is an ordinary field-structure with
bk@work.mysql.com's avatar
bk@work.mysql.com committed
22
  the following exeptions:
23
    sc_length,typepos,row,kol,dtype,regnr and field need not to be set.
bk@work.mysql.com's avatar
bk@work.mysql.com committed
24 25 26 27 28 29
    str is a (long) to record position where 0 is the first position.
*/

#define USES_TYPES
#include "mysql_priv.h"
#include <m_ctype.h>
monty@mysql.com's avatar
monty@mysql.com committed
30
#include <assert.h>
bk@work.mysql.com's avatar
bk@work.mysql.com committed
31

32
#define FCOMP			17		/* Bytes for a packed field */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
33 34 35

static uchar * pack_screens(List<create_field> &create_fields,
			    uint *info_length, uint *screens, bool small_file);
36 37
static uint pack_keys(uchar *keybuff,uint key_count, KEY *key_info,
                      ulong data_offset);
38
static bool pack_header(uchar *forminfo,enum legacy_db_type table_type,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
39 40
			List<create_field> &create_fields,
			uint info_length, uint screens, uint table_options,
41
			ulong data_offset, handler *file);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
42 43
static uint get_interval_id(uint *int_count,List<create_field> &create_fields,
			    create_field *last_field);
44 45
static bool pack_fields(File file, List<create_field> &create_fields,
                        ulong data_offset);
46
static bool make_empty_rec(THD *thd, int file, enum legacy_db_type table_type,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
47 48
			   uint table_options,
			   List<create_field> &create_fields,
49 50
			   uint reclength, ulong data_offset,
                           handler *handler);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
51

52 53 54 55 56 57
/*
  Create a frm (table definition) file

  SYNOPSIS
    mysql_create_frm()
    thd			Thread handler
58
    file_name		Path for file (including database and .frm)
59
    db                  Name of database
monty@mysql.com's avatar
monty@mysql.com committed
60
    table               Name of table
61 62 63 64 65
    create_info		create info parameters
    create_fields	Fields to create
    keys		number of keys to create
    key_info		Keys to create
    db_file		Handler to use. May be zero, in which case we use
serg@serg.mylan's avatar
serg@serg.mylan committed
66
			create_info->db_type
67 68 69 70
  RETURN
    0  ok
    1  error
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
71

72
bool mysql_create_frm(THD *thd, const char *file_name,
monty@mysql.com's avatar
monty@mysql.com committed
73
                      const char *db, const char *table,
74 75 76 77
		      HA_CREATE_INFO *create_info,
		      List<create_field> &create_fields,
		      uint keys, KEY *key_info,
		      handler *db_file)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
78
{
79
  LEX_STRING str_db_type;
80
  uint reclength,info_length,screens,key_info_length,maxlength,i;
monty@mysql.com's avatar
monty@mysql.com committed
81
  ulong key_buff_length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
82
  File file;
83
  ulong filepos, data_offset;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
84 85 86
  uchar fileinfo[64],forminfo[288],*keybuff;
  TYPELIB formnames;
  uchar *screen_buff;
serg@serg.mylan's avatar
serg@serg.mylan committed
87
  char buff[32];
88
#ifdef WITH_PARTITION_STORAGE_ENGINE
89 90
  partition_info *part_info= thd->lex->part_info;
#endif
91
  DBUG_ENTER("mysql_create_frm");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
92

93
  DBUG_ASSERT(*fn_rext((char*)file_name)); // Check .frm extension
bk@work.mysql.com's avatar
bk@work.mysql.com committed
94 95 96
  formnames.type_names=0;
  if (!(screen_buff=pack_screens(create_fields,&info_length,&screens,0)))
    DBUG_RETURN(1);
97
  DBUG_ASSERT(db_file != NULL);
98 99 100 101 102 103

 /* If fixed row records, we need one bit to check for deleted rows */
  if (!(create_info->table_options & HA_OPTION_PACK_RECORD))
    create_info->null_bits++;
  data_offset= (create_info->null_bits + 7) / 8;

104 105
  if (pack_header(forminfo, ha_legacy_type(create_info->db_type),
                  create_fields,info_length,
106 107
		  screens, create_info->table_options,
                  data_offset, db_file))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
108 109
  {
    my_free((gptr) screen_buff,MYF(0));
110
    if (thd->net.last_errno != ER_TOO_MANY_FIELDS)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
111 112 113
      DBUG_RETURN(1);

    // Try again without UNIREG screens (to get more columns)
114
    thd->net.last_error[0]=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
115 116
    if (!(screen_buff=pack_screens(create_fields,&info_length,&screens,1)))
      DBUG_RETURN(1);
117 118
    if (pack_header(forminfo, ha_legacy_type(create_info->db_type),
                    create_fields,info_length,
119
		    screens, create_info->table_options, data_offset, db_file))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
120 121 122 123 124 125 126
    {
      my_free((gptr) screen_buff,MYF(0));
      DBUG_RETURN(1);
    }
  }
  reclength=uint2korr(forminfo+266);

127
  /* Calculate extra data segment length */
128
  str_db_type.str= (char *) ha_resolve_storage_engine_name(create_info->db_type);
129
  str_db_type.length= strlen(str_db_type.str);
130
  /* str_db_type */
131 132
  create_info->extra_size= (2 + str_db_type.length +
                            2 + create_info->connect_string.length);
133
  /* Partition */
134
  create_info->extra_size+= 9;
135
#ifdef WITH_PARTITION_STORAGE_ENGINE
136
  if (part_info)
137
  {
138
    create_info->extra_size+= part_info->part_info_len;
139 140
    create_info->extra_size+= part_info->part_state_len;
  }
141 142 143 144 145 146 147
#endif

  for (i= 0; i < keys; i++)
  {
    if (key_info[i].parser_name)
      create_info->extra_size+= key_info[i].parser_name->length + 1;
  }
148

149
  if ((file=create_frm(thd, file_name, db, table, reclength, fileinfo,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
150 151 152 153 154 155
		       create_info, keys)) < 0)
  {
    my_free((gptr) screen_buff,MYF(0));
    DBUG_RETURN(1);
  }

monty@mysql.com's avatar
monty@mysql.com committed
156
  key_buff_length= uint4korr(fileinfo+47);
serg@serg.mylan's avatar
serg@serg.mylan committed
157
  keybuff=(uchar*) my_malloc(key_buff_length, MYF(0));
158
  key_info_length= pack_keys(keybuff, keys, key_info, data_offset);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
159 160 161 162 163
  VOID(get_form_pos(file,fileinfo,&formnames));
  if (!(filepos=make_new_entry(file,fileinfo,&formnames,"")))
    goto err;
  maxlength=(uint) next_io_size((ulong) (uint2korr(forminfo)+1000));
  int2store(forminfo+2,maxlength);
serg@serg.mylan's avatar
serg@serg.mylan committed
164
  int4store(fileinfo+10,(ulong) (filepos+maxlength));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
165 166 167
  fileinfo[26]= (uchar) test((create_info->max_rows == 1) &&
			     (create_info->min_rows == 1) && (keys == 0));
  int2store(fileinfo+28,key_info_length);
168
  strmake((char*) forminfo+47,create_info->comment ? create_info->comment : "",
bk@work.mysql.com's avatar
bk@work.mysql.com committed
169 170 171
	  60);
  forminfo[46]=(uchar) strlen((char*)forminfo+47);	// Length of comment

172
#ifdef WITH_PARTITION_STORAGE_ENGINE
173
  if (part_info)
174
  {
175
    fileinfo[61]= (uchar) ha_legacy_type(part_info->default_engine_type);
176 177
    DBUG_PRINT("info", ("part_db_type = %d", fileinfo[61]));
  }
178 179
#endif
  int2store(fileinfo+59,db_file->extra_rec_buf_length());
bk@work.mysql.com's avatar
bk@work.mysql.com committed
180 181 182 183 184 185 186
  if (my_pwrite(file,(byte*) fileinfo,64,0L,MYF_RW) ||
      my_pwrite(file,(byte*) keybuff,key_info_length,
		(ulong) uint2korr(fileinfo+6),MYF_RW))
    goto err;
  VOID(my_seek(file,
	       (ulong) uint2korr(fileinfo+6)+ (ulong) key_buff_length,
	       MY_SEEK_SET,MYF(0)));
187 188
  if (make_empty_rec(thd,file,ha_legacy_type(create_info->db_type),
                     create_info->table_options,
189
		     create_fields,reclength, data_offset, db_file))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
190
    goto err;
191

192
  int2store(buff, create_info->connect_string.length);
193
  if (my_write(file, (const byte*)buff, 2, MYF(MY_NABP)) ||
194 195
      my_write(file, (const byte*)create_info->connect_string.str,
               create_info->connect_string.length, MYF(MY_NABP)))
196
      goto err;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
197

198
  int2store(buff, str_db_type.length);
199
  if (my_write(file, (const byte*)buff, 2, MYF(MY_NABP)) ||
200 201 202
      my_write(file, (const byte*)str_db_type.str,
               str_db_type.length, MYF(MY_NABP)))
    goto err;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
203

204
#ifdef WITH_PARTITION_STORAGE_ENGINE
205 206
  if (part_info)
  {
207 208 209 210
    int4store(buff, part_info->part_info_len);
    if (my_write(file, (const byte*)buff, 4, MYF_RW) ||
        my_write(file, (const byte*)part_info->part_info_string,
                 part_info->part_info_len + 1, MYF_RW))
211
      goto err;
212 213 214 215 216 217
    DBUG_PRINT("info", ("Part state len = %d", part_info->part_state_len));
    int4store(buff, part_info->part_state_len);
    if (my_write(file, (const byte*)buff, 4, MYF_RW) ||
        my_write(file, (const byte*)part_info->part_state,
                 part_info->part_state_len, MYF_RW))
      goto err;
218
  }
219
  else
220
#endif
221
  {
222 223
    bzero(buff, 9);
    if (my_write(file, (byte*) buff, 9, MYF_RW))
224 225 226 227 228 229 230 231 232 233 234
      goto err;
  }
  for (i= 0; i < keys; i++)
  {
    if (key_info[i].parser_name)
    {
      if (my_write(file, (const byte*)key_info[i].parser_name->str,
                   key_info[i].parser_name->length + 1, MYF(MY_NABP)))
        goto err;
    }
  }
serg@serg.mylan's avatar
serg@serg.mylan committed
235

236 237 238 239 240 241
  VOID(my_seek(file,filepos,MY_SEEK_SET,MYF(0)));
  if (my_write(file,(byte*) forminfo,288,MYF_RW) ||
      my_write(file,(byte*) screen_buff,info_length,MYF_RW) ||
      pack_fields(file, create_fields, data_offset))
    goto err;

bk@work.mysql.com's avatar
bk@work.mysql.com committed
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
#ifdef HAVE_CRYPTED_FRM
  if (create_info->password)
  {
    char tmp=2,*disk_buff=0;
    SQL_CRYPT *crypted=new SQL_CRYPT(create_info->password);
    if (!crypted || my_pwrite(file,&tmp,1,26,MYF_RW))	// Mark crypted
      goto err;
    uint read_length=uint2korr(forminfo)-256;
    VOID(my_seek(file,filepos+256,MY_SEEK_SET,MYF(0)));
    if (read_string(file,(gptr*) &disk_buff,read_length))
      goto err;
    crypted->encode(disk_buff,read_length);
    delete crypted;
    if (my_pwrite(file,disk_buff,read_length,filepos+256,MYF_RW))
    {
      my_free(disk_buff,MYF(0));
      goto err;
    }
    my_free(disk_buff,MYF(0));
  }
#endif

  my_free((gptr) screen_buff,MYF(0));
serg@serg.mylan's avatar
serg@serg.mylan committed
265
  my_free((gptr) keybuff, MYF(0));
266 267 268

  if (opt_sync_frm && !(create_info->options & HA_LEX_CREATE_TMP_TABLE) &&
      my_sync(file, MYF(MY_WME)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
269
    goto err2;
270
  if (my_close(file,MYF(MY_WME)))
271
    goto err3;
272 273 274 275 276 277 278 279 280 281 282

  {
    /* Unescape all UCS2 intervals: were escaped in pack_headers */
    List_iterator<create_field> it(create_fields);
    create_field *field;
    while ((field=it++))
    {
      if (field->interval && field->charset->mbminlen > 1)
        unhex_type2(field->interval);
    }
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
283 284 285 286
  DBUG_RETURN(0);

err:
  my_free((gptr) screen_buff,MYF(0));
serg@serg.mylan's avatar
serg@serg.mylan committed
287
  my_free((gptr) keybuff, MYF(0));
288
err2:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
289
  VOID(my_close(file,MYF(MY_WME)));
290
err3:
291
  my_delete(file_name,MYF(0));
292 293 294
  DBUG_RETURN(1);
} /* mysql_create_frm */

295 296 297 298 299

/*
  Create a frm (table definition) file and the tables

  SYNOPSIS
300
    rea_create_table()
301
    thd			Thread handler
302
    path		Name of file (including database, without .frm)
303 304
    db			Data base name
    table_name		Table name
305 306 307 308
    create_info		create info parameters
    create_fields	Fields to create
    keys		number of keys to create
    key_info		Keys to create
309 310
    file		Handler to use

311 312 313 314 315
  RETURN
    0  ok
    1  error
*/

316 317 318 319 320
int rea_create_table(THD *thd, const char *path,
                     const char *db, const char *table_name,
                     HA_CREATE_INFO *create_info,
                     List<create_field> &create_fields,
                     uint keys, KEY *key_info, handler *file)
321 322 323
{
  DBUG_ENTER("rea_create_table");

324 325 326
  char frm_name[FN_REFLEN];
  strxmov(frm_name, path, reg_ext, NullS);
  if (mysql_create_frm(thd, frm_name, db, table_name, create_info,
327
                       create_fields, keys, key_info, file))
328

329
    DBUG_RETURN(1);
330 331 332

  // Make sure mysql_create_frm din't remove extension
  DBUG_ASSERT(*fn_rext(frm_name));
333
  if (file->create_handler_files(path))
334
    goto err_handler;
335 336
  if (!create_info->frm_only && ha_create_table(thd, path, db, table_name,
                                                create_info,0))
337
    goto err_handler;
338
  DBUG_RETURN(0);
339

340
err_handler:
341
  my_delete(frm_name, MYF(0));
342
  DBUG_RETURN(1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
} /* rea_create_table */


	/* Pack screens to a screen for save in a form-file */

static uchar * pack_screens(List<create_field> &create_fields,
			    uint *info_length, uint *screens,
			    bool small_file)
{
  reg1 uint i;
  uint row,start_row,end_row,fields_on_screen;
  uint length,cols;
  uchar *info,*pos,*start_screen;
  uint fields=create_fields.elements;
  List_iterator<create_field> it(create_fields);
  DBUG_ENTER("pack_screens");

  start_row=4; end_row=22; cols=80; fields_on_screen=end_row+1-start_row;

  *screens=(fields-1)/fields_on_screen+1;
  length= (*screens) * (SC_INFO_LENGTH+ (cols>> 1)+4);

  create_field *field;
  while ((field=it++))
367
    length+=(uint) strlen(field->field_name)+1+TE_INFO_LENGTH+cols/2;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396

  if (!(info=(uchar*) my_malloc(length,MYF(MY_WME))))
    DBUG_RETURN(0);

  start_screen=0;
  row=end_row;
  pos=info;
  it.rewind();
  for (i=0 ; i < fields ; i++)
  {
    create_field *cfield=it++;
    if (row++ == end_row)
    {
      if (i)
      {
	length=(uint) (pos-start_screen);
	int2store(start_screen,length);
	start_screen[2]=(uchar) (fields_on_screen+1);
	start_screen[3]=(uchar) (fields_on_screen);
      }
      row=start_row;
      start_screen=pos;
      pos+=4;
      pos[0]= (uchar) start_row-2;	/* Header string */
      pos[1]= (uchar) (cols >> 2);
      pos[2]= (uchar) (cols >> 1) +1;
      strfill((my_string) pos+3,(uint) (cols >> 1),' ');
      pos+=(cols >> 1)+4;
    }
397
    length=(uint) strlen(cfield->field_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423
    if (length > cols-3)
      length=cols-3;

    if (!small_file)
    {
      pos[0]=(uchar) row;
      pos[1]=0;
      pos[2]=(uchar) (length+1);
      pos=(uchar*) strmake((char*) pos+3,cfield->field_name,length)+1;
    }
    cfield->row=(uint8) row;
    cfield->col=(uint8) (length+1);
    cfield->sc_length=(uint8) min(cfield->length,cols-(length+2));
  }
  length=(uint) (pos-start_screen);
  int2store(start_screen,length);
  start_screen[2]=(uchar) (row-start_row+2);
  start_screen[3]=(uchar) (row-start_row+1);

  *info_length=(uint) (pos-info);
  DBUG_RETURN(info);
} /* pack_screens */


	/* Pack keyinfo and keynames to keybuff for save in form-file. */

424 425
static uint pack_keys(uchar *keybuff, uint key_count, KEY *keyinfo,
                      ulong data_offset)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
426 427
{
  uint key_parts,length;
428
  uchar *pos, *keyname_pos;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
429 430 431 432 433 434 435 436
  KEY *key,*end;
  KEY_PART_INFO *key_part,*key_part_end;
  DBUG_ENTER("pack_keys");

  pos=keybuff+6;
  key_parts=0;
  for (key=keyinfo,end=keyinfo+key_count ; key != end ; key++)
  {
437 438 439 440
    int2store(pos, (key->flags ^ HA_NOSAME));
    int2store(pos+2,key->key_length);
    pos[4]= (uchar) key->key_parts;
    pos[5]= (uchar) key->algorithm;
441
    pos[6]=pos[7]=0;				// For the future
442
    pos+=8;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
443
    key_parts+=key->key_parts;
444
    DBUG_PRINT("loop",("flags: %d  key_parts: %d at 0x%lx",
bk@work.mysql.com's avatar
bk@work.mysql.com committed
445 446 447 448 449 450 451
		       key->flags,key->key_parts,
		       key->key_part));
    for (key_part=key->key_part,key_part_end=key_part+key->key_parts ;
	 key_part != key_part_end ;
	 key_part++)

    {
452 453 454 455
      uint offset;
      DBUG_PRINT("loop",("field: %d  startpos: %lu  length: %ld",
			 key_part->fieldnr, key_part->offset + data_offset,
                         key_part->length));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
456
      int2store(pos,key_part->fieldnr+1+FIELD_NAME_USED);
457 458
      offset= (uint) (key_part->offset+data_offset+1);
      int2store(pos+2, offset);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
459 460 461 462 463 464 465 466
      pos[4]=0;					// Sort order
      int2store(pos+5,key_part->key_type);
      int2store(pos+7,key_part->length);
      pos+=9;
    }
  }
	/* Save keynames */
  keyname_pos=pos;
467
  *pos++=(uchar) NAMES_SEP_CHAR;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
468 469 470
  for (key=keyinfo ; key != end ; key++)
  {
    uchar *tmp=(uchar*) strmov((char*) pos,key->name);
471
    *tmp++= (uchar) NAMES_SEP_CHAR;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
472 473 474 475 476
    *tmp=0;
    pos=tmp;
  }
  *(pos++)=0;

serg@serg.mylan's avatar
serg@serg.mylan committed
477 478
  if (key_count > 127 || key_parts > 127)
  {
serg@serg.mylan's avatar
serg@serg.mylan committed
479 480
    keybuff[0]= (key_count & 0x7f) | 0x80;
    keybuff[1]= key_count >> 7;
serg@serg.mylan's avatar
serg@serg.mylan committed
481 482 483 484 485 486
    int2store(keybuff+2,key_parts);
  }
  else
  {
    keybuff[0]=(uchar) key_count;
    keybuff[1]=(uchar) key_parts;
487
    keybuff[2]= keybuff[3]= 0;
serg@serg.mylan's avatar
serg@serg.mylan committed
488
  }
489
  length=(uint) (pos-keyname_pos);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
490 491 492 493 494 495 496
  int2store(keybuff+4,length);
  DBUG_RETURN((uint) (pos-keybuff));
} /* pack_keys */


	/* Make formheader */

497
static bool pack_header(uchar *forminfo, enum legacy_db_type table_type,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
498
			List<create_field> &create_fields,
499 500
                        uint info_length, uint screens, uint table_options,
                        ulong data_offset, handler *file)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
501
{
502
  uint length,int_count,int_length,no_empty, int_parts;
503 504
  uint time_stamp_pos,null_fields;
  ulong reclength, totlength, n_length, com_length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
505 506 507 508
  DBUG_ENTER("pack_header");

  if (create_fields.elements > MAX_FIELDS)
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
509
    my_message(ER_TOO_MANY_FIELDS, ER(ER_TOO_MANY_FIELDS), MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
510 511 512
    DBUG_RETURN(1);
  }

monty@mysql.com's avatar
monty@mysql.com committed
513 514
  totlength= 0L;
  reclength= data_offset;
515 516
  no_empty=int_count=int_parts=int_length=time_stamp_pos=null_fields=
    com_length=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
517 518 519 520 521 522 523 524 525
  n_length=2L;

	/* Check fields */

  List_iterator<create_field> it(create_fields);
  create_field *field;
  while ((field=it++))
  {
    totlength+= field->length;
526
    com_length+= field->comment.length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
527 528 529 530 531 532 533
    if (MTYP_TYPENR(field->unireg_check) == Field::NOEMPTY ||
	field->unireg_check & MTYP_NOEMPTY_BIT)
    {
      field->unireg_check= (Field::utype) ((uint) field->unireg_check |
					   MTYP_NOEMPTY_BIT);
      no_empty++;
    }
534 535 536 537 538 539
    /* 
      We mark first TIMESTAMP field with NOW() in DEFAULT or ON UPDATE 
      as auto-update field.
    */
    if (field->sql_type == FIELD_TYPE_TIMESTAMP &&
        MTYP_TYPENR(field->unireg_check) != Field::NONE &&
bk@work.mysql.com's avatar
bk@work.mysql.com committed
540
	!time_stamp_pos)
541
      time_stamp_pos= (uint) field->offset+ (uint) data_offset + 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
542
    length=field->pack_length;
monty@mysql.com's avatar
monty@mysql.com committed
543 544
    /* Ensure we don't have any bugs when generating offsets */
    DBUG_ASSERT(reclength == field->offset + data_offset);
545 546
    if ((uint) field->offset+ (uint) data_offset+ length > reclength)
      reclength=(uint) (field->offset+ data_offset + length);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
547 548 549 550 551
    n_length+= (ulong) strlen(field->field_name)+1;
    field->interval_id=0;
    if (field->interval)
    {
      uint old_int_count=int_count;
552 553 554 555 556 557 558 559 560 561 562 563

      if (field->charset->mbminlen > 1)
      {
        /* Escape UCS2 intervals using HEX notation */
        for (uint pos= 0; pos < field->interval->count; pos++)
        {
          char *dst;
          uint length= field->interval->type_lengths[pos], hex_length;
          const char *src= field->interval->type_names[pos];
          hex_length= length * 2;
          field->interval->type_lengths[pos]= hex_length;
          field->interval->type_names[pos]= dst= sql_alloc(hex_length + 1);
monty@mysql.com's avatar
monty@mysql.com committed
564
          octet2hex(dst, src, length);
565 566 567
        }
      }

bk@work.mysql.com's avatar
bk@work.mysql.com committed
568 569 570 571
      field->interval_id=get_interval_id(&int_count,create_fields,field);
      if (old_int_count != int_count)
      {
	for (const char **pos=field->interval->type_names ; *pos ; pos++)
572
	  int_length+=(uint) strlen(*pos)+1;	// field + suffix prefix
bk@work.mysql.com's avatar
bk@work.mysql.com committed
573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590
	int_parts+=field->interval->count+1;
      }
    }
    if (f_maybe_null(field->pack_flag))
      null_fields++;
  }
  int_length+=int_count*2;			// 255 prefix + 0 suffix

	/* Save values in forminfo */

  if (reclength > (ulong) file->max_record_length())
  {
    my_error(ER_TOO_BIG_ROWSIZE, MYF(0), (uint) file->max_record_length());
    DBUG_RETURN(1);
  }
  /* Hack to avoid bugs with small static rows in MySQL */
  reclength=max(file->min_record_length(table_options),reclength);
  if (info_length+(ulong) create_fields.elements*FCOMP+288+
591
      n_length+int_length+com_length > 65535L || int_count > 255)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
592
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
593
    my_message(ER_TOO_MANY_FIELDS, ER(ER_TOO_MANY_FIELDS), MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
594 595 596 597
    DBUG_RETURN(1);
  }

  bzero((char*)forminfo,288);
598 599
  length=(info_length+create_fields.elements*FCOMP+288+n_length+int_length+
	  com_length);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
600 601 602 603 604 605 606 607 608 609 610 611
  int2store(forminfo,length);
  forminfo[256] = (uint8) screens;
  int2store(forminfo+258,create_fields.elements);
  int2store(forminfo+260,info_length);
  int2store(forminfo+262,totlength);
  int2store(forminfo+264,no_empty);
  int2store(forminfo+266,reclength);
  int2store(forminfo+268,n_length);
  int2store(forminfo+270,int_count);
  int2store(forminfo+272,int_parts);
  int2store(forminfo+274,int_length);
  int2store(forminfo+276,time_stamp_pos);
612 613
  int2store(forminfo+278,80);			/* Columns needed */
  int2store(forminfo+280,22);			/* Rows needed */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
614
  int2store(forminfo+282,null_fields);
615
  int2store(forminfo+284,com_length);
616
  /* Up to forminfo+288 is free to use for additional information */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650
  DBUG_RETURN(0);
} /* pack_header */


	/* get each unique interval each own id */

static uint get_interval_id(uint *int_count,List<create_field> &create_fields,
			    create_field *last_field)
{
  List_iterator<create_field> it(create_fields);
  create_field *field;
  TYPELIB *interval=last_field->interval;

  while ((field=it++) != last_field)
  {
    if (field->interval_id && field->interval->count == interval->count)
    {
      const char **a,**b;
      for (a=field->interval->type_names, b=interval->type_names ;
	   *a && !strcmp(*a,*b);
	   a++,b++) ;

      if (! *a)
      {
	return field->interval_id;		// Re-use last interval
      }
    }
  }
  return ++*int_count;				// New unique interval
}


	/* Save fields, fieldnames and intervals */

651 652
static bool pack_fields(File file, List<create_field> &create_fields,
                        ulong data_offset)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
653 654
{
  reg2 uint i;
655
  uint int_count, comment_length=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
656 657 658 659 660 661 662 663 664 665 666
  uchar buff[MAX_FIELD_WIDTH];
  create_field *field;
  DBUG_ENTER("pack_fields");

	/* Write field info */

  List_iterator<create_field> it(create_fields);

  int_count=0;
  while ((field=it++))
  {
667
    uint recpos;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
668 669 670
    buff[0]= (uchar) field->row;
    buff[1]= (uchar) field->col;
    buff[2]= (uchar) field->sc_length;
671
    int2store(buff+3, field->length);
672 673
    /* The +1 is here becasue the col offset in .frm file have offset 1 */
    recpos= field->offset+1 + (uint) data_offset;
674 675 676 677 678
    int3store(buff+5,recpos);
    int2store(buff+8,field->pack_flag);
    int2store(buff+10,field->unireg_check);
    buff[12]= (uchar) field->interval_id;
    buff[13]= (uchar) field->sql_type; 
679
    if (field->sql_type == FIELD_TYPE_GEOMETRY)
hf@deer.(none)'s avatar
hf@deer.(none) committed
680
    {
681
      buff[14]= (uchar) field->geom_type;
hf@deer.(none)'s avatar
hf@deer.(none) committed
682 683 684 685
#ifndef HAVE_SPATIAL
      DBUG_ASSERT(0);                           // Should newer happen
#endif
    }
686
    else if (field->charset) 
687
      buff[14]= (uchar) field->charset->number;
688 689
    else
      buff[14]= 0;				// Numerical
690
    int2store(buff+15, field->comment.length);
691
    comment_length+= field->comment.length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
692 693 694 695 696 697
    set_if_bigger(int_count,field->interval_id);
    if (my_write(file,(byte*) buff,FCOMP,MYF_RW))
      DBUG_RETURN(1);
  }

	/* Write fieldnames */
698
  buff[0]=(uchar) NAMES_SEP_CHAR;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716
  if (my_write(file,(byte*) buff,1,MYF_RW))
    DBUG_RETURN(1);
  i=0;
  it.rewind();
  while ((field=it++))
  {
    char *pos= strmov((char*) buff,field->field_name);
    *pos++=NAMES_SEP_CHAR;
    if (i == create_fields.elements-1)
      *pos++=0;
    if (my_write(file,(byte*) buff,(uint) (pos-(char*) buff),MYF_RW))
      DBUG_RETURN(1);
    i++;
  }

	/* Write intervals */
  if (int_count)
  {
717
    String tmp((char*) buff,sizeof(buff), &my_charset_bin);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
718 719 720 721 722 723 724 725
    tmp.length(0);
    it.rewind();
    int_count=0;
    while ((field=it++))
    {
      if (field->interval_id > int_count)
      {
	int_count=field->interval_id;
726
	tmp.append(NAMES_SEP_CHAR);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
727 728 729
	for (const char **pos=field->interval->type_names ; *pos ; pos++)
	{
	  tmp.append(*pos);
730
	  tmp.append(NAMES_SEP_CHAR);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
731 732 733 734 735 736 737
	}
	tmp.append('\0');			// End of intervall
      }
    }
    if (my_write(file,(byte*) tmp.ptr(),tmp.length(),MYF_RW))
      DBUG_RETURN(1);
  }
738 739 740 741 742 743 744 745 746 747 748 749
  if (comment_length)
  {
    it.rewind();
    int_count=0;
    while ((field=it++))
    {
      if (field->comment.length)
	if (my_write(file, (byte*) field->comment.str, field->comment.length,
		     MYF_RW))
	  DBUG_RETURN(1);
    }
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
750 751 752 753
  DBUG_RETURN(0);
}


monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
754
	/* save an empty record on start of formfile */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
755

756
static bool make_empty_rec(THD *thd, File file,enum legacy_db_type table_type,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
757 758
			   uint table_options,
			   List<create_field> &create_fields,
monty@mysql.com's avatar
monty@mysql.com committed
759
			   uint reclength,
760 761
                           ulong data_offset,
                           handler *handler)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
762
{
763
  int error= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
764
  Field::utype type;
765
  uint null_count;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
766 767
  uchar *buff,*null_pos;
  TABLE table;
768
  TABLE_SHARE share;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
769
  create_field *field;
770
  enum_check_fields old_count_cuted_fields= thd->count_cuted_fields;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
771 772 773
  DBUG_ENTER("make_empty_rec");

  /* We need a table to generate columns for default values */
774 775 776
  bzero((char*) &table, sizeof(table));
  bzero((char*) &share, sizeof(share));
  table.s= &share;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
777

778
  if (!(buff=(uchar*) my_malloc((uint) reclength,MYF(MY_WME | MY_ZEROFILL))))
779
  {
bk@work.mysql.com's avatar
bk@work.mysql.com committed
780
    DBUG_RETURN(1);
781 782
  }

783
  table.in_use= thd;
784 785
  table.s->db_low_byte_first= handler->low_byte_first();
  table.s->blob_ptr_size= portable_sizeof_char_ptr;
786

bk@work.mysql.com's avatar
bk@work.mysql.com committed
787 788 789
  null_count=0;
  if (!(table_options & HA_OPTION_PACK_RECORD))
  {
790
    null_count++;			// Need one bit for delete mark
791
    *buff|= 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
792
  }
793
  null_pos= buff;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
794 795

  List_iterator<create_field> it(create_fields);
796
  thd->count_cuted_fields= CHECK_FIELD_WARN;    // To find wrong default values
bk@work.mysql.com's avatar
bk@work.mysql.com committed
797 798
  while ((field=it++))
  {
799 800 801
    /*
      regfield don't have to be deleted as it's allocated with sql_alloc()
    */
802 803 804 805 806 807 808 809 810 811 812 813
    Field *regfield= make_field(&share,
                                (char*) buff+field->offset + data_offset,
                                field->length,
                                null_pos + null_count / 8,
                                null_count & 7,
                                field->pack_flag,
                                field->sql_type,
                                field->charset,
                                field->geom_type,
                                field->unireg_check,
                                field->interval,
                                field->field_name);
814 815
    if (!regfield)
      goto err;                                 // End of memory
816

817 818 819
    /* save_in_field() will access regfield->table->in_use */
    regfield->init(&table);

bk@work.mysql.com's avatar
bk@work.mysql.com committed
820
    if (!(field->flags & NOT_NULL_FLAG))
821 822
    {
      *regfield->null_ptr|= regfield->null_bit;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
823
      null_count++;
824
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
825

826 827
    if (field->sql_type == FIELD_TYPE_BIT && !f_bit_as_char(field->pack_flag))
      null_count+= field->length & 7;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
828 829 830 831 832 833

    type= (Field::utype) MTYP_TYPENR(field->unireg_check);

    if (field->def &&
	(regfield->real_type() != FIELD_TYPE_YEAR ||
	 field->def->val_int() != 0))
834 835 836 837 838
    {
      if (field->def->save_in_field(regfield, 1))
      {
        my_error(ER_INVALID_DEFAULT, MYF(0), regfield->field_name);
        error= 1;
839
        delete regfield; //To avoid memory leak
840 841 842
        goto err;
      }
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
843 844 845 846
    else if (regfield->real_type() == FIELD_TYPE_ENUM &&
	     (field->flags & NOT_NULL_FLAG))
    {
      regfield->set_notnull();
847
      regfield->store((longlong) 1, TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
848 849
    }
    else if (type == Field::YES)		// Old unireg type
850
      regfield->store(ER(ER_YES),(uint) strlen(ER(ER_YES)),system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
851
    else if (type == Field::NO)			// Old unireg type
852
      regfield->store(ER(ER_NO), (uint) strlen(ER(ER_NO)),system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
853 854 855
    else
      regfield->reset();
  }
monty@mysql.com's avatar
monty@mysql.com committed
856
  DBUG_ASSERT(data_offset == ((null_count + 7) / 8));
857

858 859 860 861 862
  /*
    We need to set the unused bits to 1. If the number of bits is a multiple
    of 8 there are no unused bits.
  */
  if (null_count & 7)
863 864
    *(null_pos + null_count / 8)|= ~(((uchar) 1 << (null_count & 7)) - 1);

865
  error=(int) my_write(file,(byte*) buff, (uint) reclength,MYF_RW);
866 867

err:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
868
  my_free((gptr) buff,MYF(MY_FAE));
869
  thd->count_cuted_fields= old_count_cuted_fields;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
870 871
  DBUG_RETURN(error);
} /* make_empty_rec */