unireg.cc 31.3 KB
Newer Older
1
/* Copyright (C) 2000-2006 MySQL 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
   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
5
   the Free Software Foundation; version 2 of the License.
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
6

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

bk@work.mysql.com's avatar
bk@work.mysql.com committed
12 13 14 15 16 17 18 19
   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
20
  In the following functions FIELD * is an ordinary field-structure with
bk@work.mysql.com's avatar
bk@work.mysql.com committed
21
  the following exeptions:
22
    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
23 24 25 26 27
    str is a (long) to record position where 0 is the first position.
*/

#include "mysql_priv.h"
#include <m_ctype.h>
monty@mysql.com's avatar
monty@mysql.com committed
28
#include <assert.h>
bk@work.mysql.com's avatar
bk@work.mysql.com committed
29

30
#define FCOMP			17		/* Bytes for a packed field */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
31

32
static uchar * pack_screens(List<Create_field> &create_fields,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
33
			    uint *info_length, uint *screens, bool small_file);
34 35
static uint pack_keys(uchar *keybuff,uint key_count, KEY *key_info,
                      ulong data_offset);
36
static bool pack_header(uchar *forminfo,enum legacy_db_type table_type,
37
			List<Create_field> &create_fields,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
38
			uint info_length, uint screens, uint table_options,
39
			ulong data_offset, handler *file);
40 41 42
static uint get_interval_id(uint *int_count,List<Create_field> &create_fields,
			    Create_field *last_field);
static bool pack_fields(File file, List<Create_field> &create_fields,
43
                        ulong data_offset);
44
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
45
			   uint table_options,
46
			   List<Create_field> &create_fields,
47 48
			   uint reclength, ulong data_offset,
                           handler *handler);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
49

50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
/**
  An interceptor to hijack ER_TOO_MANY_FIELDS error from
  pack_screens and retry again without UNIREG screens.

  XXX: what is a UNIREG  screen?
*/

struct Pack_header_error_handler: public Internal_error_handler
{
  virtual bool handle_error(uint sql_errno,
                            const char *message,
                            MYSQL_ERROR::enum_warning_level level,
                            THD *thd);
  bool is_handled;
  Pack_header_error_handler() :is_handled(FALSE) {}
};


bool
Pack_header_error_handler::
handle_error(uint sql_errno,
             const char * /* message */,
             MYSQL_ERROR::enum_warning_level /* level */,
             THD * /* thd */)
{
  is_handled= (sql_errno == ER_TOO_MANY_FIELDS);
  return is_handled;
}

79 80 81 82 83 84
/*
  Create a frm (table definition) file

  SYNOPSIS
    mysql_create_frm()
    thd			Thread handler
85
    file_name		Path for file (including database and .frm)
86
    db                  Name of database
monty@mysql.com's avatar
monty@mysql.com committed
87
    table               Name of table
88 89 90 91 92
    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
93
			create_info->db_type
94 95 96 97
  RETURN
    0  ok
    1  error
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
98

99
bool mysql_create_frm(THD *thd, const char *file_name,
monty@mysql.com's avatar
monty@mysql.com committed
100
                      const char *db, const char *table,
101
		      HA_CREATE_INFO *create_info,
102
		      List<Create_field> &create_fields,
103 104
		      uint keys, KEY *key_info,
		      handler *db_file)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
105
{
106
  LEX_STRING str_db_type;
107
  uint reclength, info_length, screens, key_info_length, maxlength, tmp_len, i;
monty@mysql.com's avatar
monty@mysql.com committed
108
  ulong key_buff_length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
109
  File file;
110
  ulong filepos, data_offset;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
111 112 113
  uchar fileinfo[64],forminfo[288],*keybuff;
  TYPELIB formnames;
  uchar *screen_buff;
114
  char buff[128];
115
#ifdef WITH_PARTITION_STORAGE_ENGINE
116
  partition_info *part_info= thd->work_part_info;
117
#endif
118 119
  Pack_header_error_handler pack_header_error_handler;
  int error;
120
  DBUG_ENTER("mysql_create_frm");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
121

122
  DBUG_ASSERT(*fn_rext((char*)file_name)); // Check .frm extension
bk@work.mysql.com's avatar
bk@work.mysql.com committed
123 124 125
  formnames.type_names=0;
  if (!(screen_buff=pack_screens(create_fields,&info_length,&screens,0)))
    DBUG_RETURN(1);
126
  DBUG_ASSERT(db_file != NULL);
127 128 129 130 131 132

 /* 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;

133 134 135 136 137 138 139 140 141 142
  thd->push_internal_handler(&pack_header_error_handler);

  error= pack_header(forminfo, ha_legacy_type(create_info->db_type),
                     create_fields,info_length,
                     screens, create_info->table_options,
                     data_offset, db_file);

  thd->pop_internal_handler();

  if (error)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
143
  {
144
    my_free(screen_buff, MYF(0));
145
    if (! pack_header_error_handler.is_handled)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
146 147 148 149 150
      DBUG_RETURN(1);

    // Try again without UNIREG screens (to get more columns)
    if (!(screen_buff=pack_screens(create_fields,&info_length,&screens,1)))
      DBUG_RETURN(1);
151 152
    if (pack_header(forminfo, ha_legacy_type(create_info->db_type),
                    create_fields,info_length,
153
		    screens, create_info->table_options, data_offset, db_file))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
154
    {
155
      my_free(screen_buff, MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
156 157 158 159 160
      DBUG_RETURN(1);
    }
  }
  reclength=uint2korr(forminfo+266);

161
  /* Calculate extra data segment length */
162
  str_db_type.str= (char *) ha_resolve_storage_engine_name(create_info->db_type);
163
  str_db_type.length= strlen(str_db_type.str);
164
  /* str_db_type */
165 166
  create_info->extra_size= (2 + str_db_type.length +
                            2 + create_info->connect_string.length);
167 168 169 170 171 172 173 174
  /*
    Partition:
      Length of partition info = 4 byte
      Potential NULL byte at end of partition info string = 1 byte
      Indicator if auto-partitioned table = 1 byte
      => Total 6 byte
  */
  create_info->extra_size+= 6;
175
#ifdef WITH_PARTITION_STORAGE_ENGINE
176
  if (part_info)
177
  {
178
    create_info->extra_size+= part_info->part_info_len;
179
  }
180 181 182 183 184 185 186
#endif

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

188
  if ((file=create_frm(thd, file_name, db, table, reclength, fileinfo,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
189 190
		       create_info, keys)) < 0)
  {
191
    my_free(screen_buff, MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
192 193 194
    DBUG_RETURN(1);
  }

monty@mysql.com's avatar
monty@mysql.com committed
195
  key_buff_length= uint4korr(fileinfo+47);
serg@serg.mylan's avatar
serg@serg.mylan committed
196
  keybuff=(uchar*) my_malloc(key_buff_length, MYF(0));
197
  key_info_length= pack_keys(keybuff, keys, key_info, data_offset);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
198 199 200 201 202
  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
203
  int4store(fileinfo+10,(ulong) (filepos+maxlength));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
204 205 206 207
  fileinfo[26]= (uchar) test((create_info->max_rows == 1) &&
			     (create_info->min_rows == 1) && (keys == 0));
  int2store(fileinfo+28,key_info_length);

208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
  tmp_len= system_charset_info->cset->charpos(system_charset_info,
                                              create_info->comment.str,
                                              create_info->comment.str +
                                              create_info->comment.length, 60);
  if (tmp_len < create_info->comment.length)
  {
    (void) my_snprintf(buff, sizeof(buff), "Too long comment for table '%s'",
                       table);
    if ((thd->variables.sql_mode &
         (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES)))
    {
      my_message(ER_UNKNOWN_ERROR, buff, MYF(0));
      goto err;
    }
    push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                        ER_UNKNOWN_ERROR, ER(ER_UNKNOWN_ERROR), buff);
    create_info->comment.length= tmp_len;
  }

  strmake((char*) forminfo+47, create_info->comment.str ?
          create_info->comment.str : "", create_info->comment.length);
  forminfo[46]=(uchar) create_info->comment.length;
230
#ifdef EXTRA_DEBUG
231 232 233 234 235
  /*
    EXTRA_DEBUG causes strmake() to initialize its buffer behind the
    payload with a magic value to detect wrong buffer-sizes. We
    explicitly zero that segment again.
  */
236 237
  memset((char*) forminfo+47 + forminfo[46], 0, 61 - forminfo[46]);
#endif
238
#ifdef WITH_PARTITION_STORAGE_ENGINE
239
  if (part_info)
240
  {
241
    fileinfo[61]= (uchar) ha_legacy_type(part_info->default_engine_type);
242 243
    DBUG_PRINT("info", ("part_db_type = %d", fileinfo[61]));
  }
244 245
#endif
  int2store(fileinfo+59,db_file->extra_rec_buf_length());
246

247 248
  if (my_pwrite(file, fileinfo, 64, 0L, MYF_RW) ||
      my_pwrite(file, keybuff, key_info_length,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
249 250 251 252 253
		(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)));
254 255
  if (make_empty_rec(thd,file,ha_legacy_type(create_info->db_type),
                     create_info->table_options,
256
		     create_fields,reclength, data_offset, db_file))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
257
    goto err;
258

259
  int2store(buff, create_info->connect_string.length);
260 261
  if (my_write(file, (const uchar*)buff, 2, MYF(MY_NABP)) ||
      my_write(file, (const uchar*)create_info->connect_string.str,
262
               create_info->connect_string.length, MYF(MY_NABP)))
263
      goto err;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
264

265
  int2store(buff, str_db_type.length);
266 267
  if (my_write(file, (const uchar*)buff, 2, MYF(MY_NABP)) ||
      my_write(file, (const uchar*)str_db_type.str,
268 269
               str_db_type.length, MYF(MY_NABP)))
    goto err;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
270

271
#ifdef WITH_PARTITION_STORAGE_ENGINE
272 273
  if (part_info)
  {
274
    char auto_partitioned= part_info->is_auto_partitioned ? 1 : 0;
275
    int4store(buff, part_info->part_info_len);
276 277
    if (my_write(file, (const uchar*)buff, 4, MYF_RW) ||
        my_write(file, (const uchar*)part_info->part_info_string,
278
                 part_info->part_info_len + 1, MYF_RW) ||
279
        my_write(file, (const uchar*)&auto_partitioned, 1, MYF_RW))
280 281
      goto err;
  }
282
  else
283
#endif
284
  {
285 286
    bzero((uchar*) buff, 6);
    if (my_write(file, (uchar*) buff, 6, MYF_RW))
287 288 289 290 291 292
      goto err;
  }
  for (i= 0; i < keys; i++)
  {
    if (key_info[i].parser_name)
    {
293
      if (my_write(file, (const uchar*)key_info[i].parser_name->str,
294 295 296 297
                   key_info[i].parser_name->length + 1, MYF(MY_NABP)))
        goto err;
    }
  }
serg@serg.mylan's avatar
serg@serg.mylan committed
298

299
  VOID(my_seek(file,filepos,MY_SEEK_SET,MYF(0)));
300 301
  if (my_write(file, forminfo, 288, MYF_RW) ||
      my_write(file, screen_buff, info_length, MYF_RW) ||
302 303 304
      pack_fields(file, create_fields, data_offset))
    goto err;

bk@work.mysql.com's avatar
bk@work.mysql.com committed
305 306 307 308 309 310 311 312 313
#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)));
314
    if (read_string(file,(uchar**) &disk_buff,read_length))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
315 316 317 318 319 320 321 322 323 324 325 326
      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

327 328
  my_free(screen_buff,MYF(0));
  my_free(keybuff, MYF(0));
329 330 331

  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
332
    goto err2;
333
  if (my_close(file,MYF(MY_WME)))
334
    goto err3;
335 336

  {
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
337 338 339 340
    /* 
      Restore all UCS2 intervals.
      HEX representation of them is not needed anymore.
    */
341 342
    List_iterator<Create_field> it(create_fields);
    Create_field *field;
343 344
    while ((field=it++))
    {
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
345 346 347 348 349
      if (field->save_interval)
      {
        field->interval= field->save_interval;
        field->save_interval= 0;
      }
350 351
    }
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
352 353 354
  DBUG_RETURN(0);

err:
355 356
  my_free(screen_buff, MYF(0));
  my_free(keybuff, MYF(0));
357
err2:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
358
  VOID(my_close(file,MYF(MY_WME)));
359
err3:
360
  my_delete(file_name,MYF(0));
361 362 363
  DBUG_RETURN(1);
} /* mysql_create_frm */

364 365 366 367 368

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

  SYNOPSIS
369
    rea_create_table()
370
    thd			Thread handler
371
    path		Name of file (including database, without .frm)
372 373
    db			Data base name
    table_name		Table name
374 375 376 377
    create_info		create info parameters
    create_fields	Fields to create
    keys		number of keys to create
    key_info		Keys to create
378 379
    file		Handler to use

380 381 382 383 384
  RETURN
    0  ok
    1  error
*/

385 386 387
int rea_create_table(THD *thd, const char *path,
                     const char *db, const char *table_name,
                     HA_CREATE_INFO *create_info,
388
                     List<Create_field> &create_fields,
389
                     uint keys, KEY *key_info, handler *file)
390 391 392
{
  DBUG_ENTER("rea_create_table");

393 394 395
  char frm_name[FN_REFLEN];
  strxmov(frm_name, path, reg_ext, NullS);
  if (mysql_create_frm(thd, frm_name, db, table_name, create_info,
396
                       create_fields, keys, key_info, file))
397

398
    DBUG_RETURN(1);
399 400 401

  // Make sure mysql_create_frm din't remove extension
  DBUG_ASSERT(*fn_rext(frm_name));
gkodinov/kgeorge@magare.gmz's avatar
gkodinov/kgeorge@magare.gmz committed
402 403
  if (thd->variables.keep_files_on_create)
    create_info->options|= HA_CREATE_KEEP_FILES;
404
  if (file->create_handler_files(path, NULL, CHF_CREATE_FLAG, create_info))
405
    goto err_handler;
406 407
  if (!create_info->frm_only && ha_create_table(thd, path, db, table_name,
                                                create_info,0))
408
    goto err_handler;
409
  DBUG_RETURN(0);
410

411
err_handler:
412
  VOID(file->create_handler_files(path, NULL, CHF_DELETE_FLAG, create_info));
413
  my_delete(frm_name, MYF(0));
414
  DBUG_RETURN(1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
415 416 417 418 419
} /* rea_create_table */


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

420
static uchar *pack_screens(List<Create_field> &create_fields,
421 422
                           uint *info_length, uint *screens,
                           bool small_file)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
423 424 425 426 427 428
{
  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;
429
  List_iterator<Create_field> it(create_fields);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
430 431 432 433 434 435 436
  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);

437
  Create_field *field;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
438
  while ((field=it++))
439
    length+=(uint) strlen(field->field_name)+1+TE_INFO_LENGTH+cols/2;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
440 441 442 443 444 445 446 447 448 449

  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++)
  {
450
    Create_field *cfield=it++;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
451 452 453 454 455 456 457 458 459 460 461 462 463 464 465
    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;
466
      strfill((char *) pos+3,(uint) (cols >> 1),' ');
bk@work.mysql.com's avatar
bk@work.mysql.com committed
467 468
      pos+=(cols >> 1)+4;
    }
469
    length=(uint) strlen(cfield->field_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495
    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. */

496 497
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
498 499
{
  uint key_parts,length;
500
  uchar *pos, *keyname_pos;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
501 502 503 504 505 506 507 508
  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++)
  {
509 510 511 512
    int2store(pos, (key->flags ^ HA_NOSAME));
    int2store(pos+2,key->key_length);
    pos[4]= (uchar) key->key_parts;
    pos[5]= (uchar) key->algorithm;
513
    int2store(pos+6, key->block_size);
514
    pos+=8;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
515
    key_parts+=key->key_parts;
516
    DBUG_PRINT("loop", ("flags: %lu  key_parts: %d at 0x%lx",
517 518
                        key->flags, key->key_parts,
                        (long) key->key_part));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
519 520 521 522 523
    for (key_part=key->key_part,key_part_end=key_part+key->key_parts ;
	 key_part != key_part_end ;
	 key_part++)

    {
524
      uint offset;
525
      DBUG_PRINT("loop",("field: %d  startpos: %lu  length: %d",
526 527
			 key_part->fieldnr, key_part->offset + data_offset,
                         key_part->length));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
528
      int2store(pos,key_part->fieldnr+1+FIELD_NAME_USED);
529 530
      offset= (uint) (key_part->offset+data_offset+1);
      int2store(pos+2, offset);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
531 532 533 534 535 536 537 538
      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;
539
  *pos++=(uchar) NAMES_SEP_CHAR;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
540 541 542
  for (key=keyinfo ; key != end ; key++)
  {
    uchar *tmp=(uchar*) strmov((char*) pos,key->name);
543
    *tmp++= (uchar) NAMES_SEP_CHAR;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
544 545 546 547 548
    *tmp=0;
    pos=tmp;
  }
  *(pos++)=0;

serg@serg.mylan's avatar
serg@serg.mylan committed
549 550
  if (key_count > 127 || key_parts > 127)
  {
serg@serg.mylan's avatar
serg@serg.mylan committed
551 552
    keybuff[0]= (key_count & 0x7f) | 0x80;
    keybuff[1]= key_count >> 7;
serg@serg.mylan's avatar
serg@serg.mylan committed
553 554 555 556 557 558
    int2store(keybuff+2,key_parts);
  }
  else
  {
    keybuff[0]=(uchar) key_count;
    keybuff[1]=(uchar) key_parts;
559
    keybuff[2]= keybuff[3]= 0;
serg@serg.mylan's avatar
serg@serg.mylan committed
560
  }
561
  length=(uint) (pos-keyname_pos);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
562 563 564 565 566 567 568
  int2store(keybuff+4,length);
  DBUG_RETURN((uint) (pos-keybuff));
} /* pack_keys */


	/* Make formheader */

569
static bool pack_header(uchar *forminfo, enum legacy_db_type table_type,
570
			List<Create_field> &create_fields,
571 572
                        uint info_length, uint screens, uint table_options,
                        ulong data_offset, handler *file)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
573
{
574
  uint length,int_count,int_length,no_empty, int_parts;
575 576
  uint time_stamp_pos,null_fields;
  ulong reclength, totlength, n_length, com_length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
577 578 579 580
  DBUG_ENTER("pack_header");

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

monty@mysql.com's avatar
monty@mysql.com committed
585 586
  totlength= 0L;
  reclength= data_offset;
587 588
  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
589 590 591 592
  n_length=2L;

	/* Check fields */

593 594
  List_iterator<Create_field> it(create_fields);
  Create_field *field;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
595 596
  while ((field=it++))
  {
597 598 599
    uint tmp_len= system_charset_info->cset->charpos(system_charset_info,
                                                     field->comment.str,
                                                     field->comment.str +
600 601
                                                     field->comment.length,
                                                     255);
602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617
    if (tmp_len < field->comment.length)
    {
      char buff[128];
      (void) my_snprintf(buff,sizeof(buff), "Too long comment for field '%s'",
                         field->field_name);
      if ((current_thd->variables.sql_mode &
	   (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES)))
      {
        my_message(ER_UNKNOWN_ERROR, buff, MYF(0));
	DBUG_RETURN(1);
      }
      push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                          ER_UNKNOWN_ERROR, ER(ER_UNKNOWN_ERROR), buff);
      field->comment.length= tmp_len;
    }

bk@work.mysql.com's avatar
bk@work.mysql.com committed
618
    totlength+= field->length;
619
    com_length+= field->comment.length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
620 621 622 623 624 625 626
    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++;
    }
627 628 629 630
    /* 
      We mark first TIMESTAMP field with NOW() in DEFAULT or ON UPDATE 
      as auto-update field.
    */
631
    if (field->sql_type == MYSQL_TYPE_TIMESTAMP &&
632
        MTYP_TYPENR(field->unireg_check) != Field::NONE &&
bk@work.mysql.com's avatar
bk@work.mysql.com committed
633
	!time_stamp_pos)
634
      time_stamp_pos= (uint) field->offset+ (uint) data_offset + 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
635
    length=field->pack_length;
monty@mysql.com's avatar
monty@mysql.com committed
636 637
    /* Ensure we don't have any bugs when generating offsets */
    DBUG_ASSERT(reclength == field->offset + data_offset);
638 639
    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
640 641
    n_length+= (ulong) strlen(field->field_name)+1;
    field->interval_id=0;
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
642
    field->save_interval= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
643 644 645
    if (field->interval)
    {
      uint old_int_count=int_count;
646 647 648

      if (field->charset->mbminlen > 1)
      {
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666
        /* 
          Escape UCS2 intervals using HEX notation to avoid
          problems with delimiters between enum elements.
          As the original representation is still needed in 
          the function make_empty_rec to create a record of
          filled with default values it is saved in save_interval
          The HEX representation is created from this copy.
        */
        field->save_interval= field->interval;
        field->interval= (TYPELIB*) sql_alloc(sizeof(TYPELIB));
        *field->interval= *field->save_interval; 
        field->interval->type_names= 
          (const char **) sql_alloc(sizeof(char*) * 
				    (field->interval->count+1));
        field->interval->type_names[field->interval->count]= 0;
        field->interval->type_lengths=
          (uint *) sql_alloc(sizeof(uint) * field->interval->count);
 
667 668 669
        for (uint pos= 0; pos < field->interval->count; pos++)
        {
          char *dst;
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
670
          const char *src= field->save_interval->type_names[pos];
671 672
          uint hex_length;
          length= field->save_interval->type_lengths[pos];
673 674
          hex_length= length * 2;
          field->interval->type_lengths[pos]= hex_length;
675 676
          field->interval->type_names[pos]= dst= (char*) sql_alloc(hex_length +
                                                                   1);
monty@mysql.com's avatar
monty@mysql.com committed
677
          octet2hex(dst, src, length);
678 679 680
        }
      }

bk@work.mysql.com's avatar
bk@work.mysql.com committed
681 682 683 684
      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++)
685
	  int_length+=(uint) strlen(*pos)+1;	// field + suffix prefix
bk@work.mysql.com's avatar
bk@work.mysql.com committed
686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703
	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+
704
      n_length+int_length+com_length > 65535L || int_count > 255)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
705
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
706
    my_message(ER_TOO_MANY_FIELDS, ER(ER_TOO_MANY_FIELDS), MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
707 708 709 710
    DBUG_RETURN(1);
  }

  bzero((char*)forminfo,288);
711 712
  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
713 714 715 716 717 718 719 720 721 722 723 724
  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);
725 726
  int2store(forminfo+278,80);			/* Columns needed */
  int2store(forminfo+280,22);			/* Rows needed */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
727
  int2store(forminfo+282,null_fields);
728
  int2store(forminfo+284,com_length);
729
  /* Up to forminfo+288 is free to use for additional information */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
730 731 732 733 734 735
  DBUG_RETURN(0);
} /* pack_header */


	/* get each unique interval each own id */

736 737
static uint get_interval_id(uint *int_count,List<Create_field> &create_fields,
			    Create_field *last_field)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
738
{
739 740
  List_iterator<Create_field> it(create_fields);
  Create_field *field;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763
  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 */

764
static bool pack_fields(File file, List<Create_field> &create_fields,
765
                        ulong data_offset)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
766 767
{
  reg2 uint i;
768
  uint int_count, comment_length=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
769
  uchar buff[MAX_FIELD_WIDTH];
770
  Create_field *field;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
771 772 773 774
  DBUG_ENTER("pack_fields");

	/* Write field info */

775
  List_iterator<Create_field> it(create_fields);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
776 777 778 779

  int_count=0;
  while ((field=it++))
  {
780
    uint recpos;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
781 782 783
    buff[0]= (uchar) field->row;
    buff[1]= (uchar) field->col;
    buff[2]= (uchar) field->sc_length;
784
    int2store(buff+3, field->length);
785 786
    /* The +1 is here becasue the col offset in .frm file have offset 1 */
    recpos= field->offset+1 + (uint) data_offset;
787 788 789 790 791
    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; 
792
    if (field->sql_type == MYSQL_TYPE_GEOMETRY)
hf@deer.(none)'s avatar
hf@deer.(none) committed
793
    {
794
      buff[14]= (uchar) field->geom_type;
hf@deer.(none)'s avatar
hf@deer.(none) committed
795 796 797 798
#ifndef HAVE_SPATIAL
      DBUG_ASSERT(0);                           // Should newer happen
#endif
    }
799
    else if (field->charset) 
800
      buff[14]= (uchar) field->charset->number;
801 802
    else
      buff[14]= 0;				// Numerical
803
    int2store(buff+15, field->comment.length);
804
    comment_length+= field->comment.length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
805
    set_if_bigger(int_count,field->interval_id);
806
    if (my_write(file, buff, FCOMP, MYF_RW))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
807 808 809 810
      DBUG_RETURN(1);
  }

	/* Write fieldnames */
811
  buff[0]=(uchar) NAMES_SEP_CHAR;
812
  if (my_write(file, buff, 1, MYF_RW))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
813 814 815 816 817 818 819 820 821
    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;
822
    if (my_write(file, buff, (size_t) (pos-(char*) buff),MYF_RW))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
823 824 825 826 827 828 829
      DBUG_RETURN(1);
    i++;
  }

	/* Write intervals */
  if (int_count)
  {
830
    String tmp((char*) buff,sizeof(buff), &my_charset_bin);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
831 832 833 834 835 836 837
    tmp.length(0);
    it.rewind();
    int_count=0;
    while ((field=it++))
    {
      if (field->interval_id > int_count)
      {
838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864
        unsigned char  sep= 0;
        unsigned char  occ[256];
        uint           i;
        unsigned char *val= NULL;

        bzero(occ, sizeof(occ));

        for (i=0; (val= (unsigned char*) field->interval->type_names[i]); i++)
          for (uint j = 0; j < field->interval->type_lengths[i]; j++)
            occ[(unsigned int) (val[j])]= 1;

        if (!occ[(unsigned char)NAMES_SEP_CHAR])
          sep= (unsigned char) NAMES_SEP_CHAR;
        else if (!occ[(unsigned int)','])
          sep= ',';
        else
        {
          for (uint i=1; i<256; i++)
          {
            if(!occ[i])
            {
              sep= i;
              break;
            }
          }

          if(!sep)    /* disaster, enum uses all characters, none left as separator */
865
          {
866 867 868
            my_message(ER_WRONG_FIELD_TERMINATORS,ER(ER_WRONG_FIELD_TERMINATORS),
                       MYF(0));
            DBUG_RETURN(1);
869
          }
870 871 872 873 874 875 876 877 878 879
        }

        int_count= field->interval_id;
        tmp.append(sep);
        for (const char **pos=field->interval->type_names ; *pos ; pos++)
        {
          tmp.append(*pos);
          tmp.append(sep);
        }
        tmp.append('\0');                      // End of intervall
bk@work.mysql.com's avatar
bk@work.mysql.com committed
880 881
      }
    }
882
    if (my_write(file,(uchar*) tmp.ptr(),tmp.length(),MYF_RW))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
883 884
      DBUG_RETURN(1);
  }
885 886 887 888 889 890 891
  if (comment_length)
  {
    it.rewind();
    int_count=0;
    while ((field=it++))
    {
      if (field->comment.length)
892
	if (my_write(file, (uchar*) field->comment.str, field->comment.length,
893 894 895 896
		     MYF_RW))
	  DBUG_RETURN(1);
    }
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
897 898 899 900
  DBUG_RETURN(0);
}


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

903
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
904
			   uint table_options,
905
			   List<Create_field> &create_fields,
monty@mysql.com's avatar
monty@mysql.com committed
906
			   uint reclength,
907 908
                           ulong data_offset,
                           handler *handler)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
909
{
910
  int error= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
911
  Field::utype type;
912
  uint null_count;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
913 914
  uchar *buff,*null_pos;
  TABLE table;
915
  TABLE_SHARE share;
916
  Create_field *field;
917
  enum_check_fields old_count_cuted_fields= thd->count_cuted_fields;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
918 919 920
  DBUG_ENTER("make_empty_rec");

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

925
  if (!(buff=(uchar*) my_malloc((size_t) reclength,MYF(MY_WME | MY_ZEROFILL))))
926
  {
bk@work.mysql.com's avatar
bk@work.mysql.com committed
927
    DBUG_RETURN(1);
928 929
  }

930
  table.in_use= thd;
931 932
  table.s->db_low_byte_first= handler->low_byte_first();
  table.s->blob_ptr_size= portable_sizeof_char_ptr;
933

bk@work.mysql.com's avatar
bk@work.mysql.com committed
934 935 936
  null_count=0;
  if (!(table_options & HA_OPTION_PACK_RECORD))
  {
937
    null_count++;			// Need one bit for delete mark
938
    *buff|= 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
939
  }
940
  null_pos= buff;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
941

942
  List_iterator<Create_field> it(create_fields);
943
  thd->count_cuted_fields= CHECK_FIELD_WARN;    // To find wrong default values
bk@work.mysql.com's avatar
bk@work.mysql.com committed
944 945
  while ((field=it++))
  {
946 947 948
    /*
      regfield don't have to be deleted as it's allocated with sql_alloc()
    */
949
    Field *regfield= make_field(&share,
950
                                buff+field->offset + data_offset,
951 952 953 954 955 956 957 958
                                field->length,
                                null_pos + null_count / 8,
                                null_count & 7,
                                field->pack_flag,
                                field->sql_type,
                                field->charset,
                                field->geom_type,
                                field->unireg_check,
959 960
                                field->save_interval ? field->save_interval :
                                field->interval, 
961
                                field->field_name);
962
    if (!regfield)
963 964
    {
      error= 1;
965
      goto err;                                 // End of memory
966
    }
967

968 969 970
    /* save_in_field() will access regfield->table->in_use */
    regfield->init(&table);

bk@work.mysql.com's avatar
bk@work.mysql.com committed
971
    if (!(field->flags & NOT_NULL_FLAG))
972 973
    {
      *regfield->null_ptr|= regfield->null_bit;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
974
      null_count++;
975
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
976

977
    if (field->sql_type == MYSQL_TYPE_BIT && !f_bit_as_char(field->pack_flag))
978
      null_count+= field->length & 7;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
979 980 981 982

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

    if (field->def &&
983
	(regfield->real_type() != MYSQL_TYPE_YEAR ||
bk@work.mysql.com's avatar
bk@work.mysql.com committed
984
	 field->def->val_int() != 0))
985
    {
986 987 988
      int res= field->def->save_in_field(regfield, 1);
      /* If not ok or warning of level 'note' */
      if (res != 0 && res != 3)
989 990 991
      {
        my_error(ER_INVALID_DEFAULT, MYF(0), regfield->field_name);
        error= 1;
992
        delete regfield; //To avoid memory leak
993 994 995
        goto err;
      }
    }
996
    else if (regfield->real_type() == MYSQL_TYPE_ENUM &&
bk@work.mysql.com's avatar
bk@work.mysql.com committed
997 998 999
	     (field->flags & NOT_NULL_FLAG))
    {
      regfield->set_notnull();
1000
      regfield->store((longlong) 1, TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1001 1002
    }
    else if (type == Field::YES)		// Old unireg type
1003
      regfield->store(ER(ER_YES),(uint) strlen(ER(ER_YES)),system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1004
    else if (type == Field::NO)			// Old unireg type
1005
      regfield->store(ER(ER_NO), (uint) strlen(ER(ER_NO)),system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1006 1007 1008
    else
      regfield->reset();
  }
monty@mysql.com's avatar
monty@mysql.com committed
1009
  DBUG_ASSERT(data_offset == ((null_count + 7) / 8));
1010

1011 1012 1013 1014 1015
  /*
    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)
1016 1017
    *(null_pos + null_count / 8)|= ~(((uchar) 1 << (null_count & 7)) - 1);

1018
  error= my_write(file, buff, (size_t) reclength,MYF_RW) != 0;
1019 1020

err:
1021
  my_free(buff, MYF(MY_FAE));
1022
  thd->count_cuted_fields= old_count_cuted_fields;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1023 1024
  DBUG_RETURN(error);
} /* make_empty_rec */