summaryrefslogtreecommitdiff
path: root/sql/table.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/table.cc')
-rw-r--r--sql/table.cc1121
1 files changed, 1121 insertions, 0 deletions
diff --git a/sql/table.cc b/sql/table.cc
new file mode 100644
index 00000000000..98f7b089b19
--- /dev/null
+++ b/sql/table.cc
@@ -0,0 +1,1121 @@
+/* Copyright (C) 2000 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+
+/* Some general useful functions */
+
+#include "mysql_priv.h"
+#include <errno.h>
+#include <m_ctype.h>
+
+
+ /* Functions defined in this file */
+
+static void frm_error(int error,TABLE *form,const char *name,int errortype);
+static void fix_type_pointers(const char ***array, TYPELIB *point_to_type,
+ uint types, char **names);
+static uint find_field(TABLE *form,uint start,uint length);
+
+
+static byte* get_field_name(Field *buff,uint *length,
+ my_bool not_used __attribute__((unused)))
+{
+ *length= strlen(buff->field_name);
+ return (byte*) buff->field_name;
+}
+
+ /* Open a .frm file */
+
+int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag,
+ TABLE *outparam)
+{
+ reg1 uint i;
+ reg2 uchar *strpos;
+ int j,flag,error;
+ uint rec_buff_length,n_length,int_length,records,key_parts,keys,
+ interval_count,interval_parts,read_length,db_create_options;
+ ulong pos;
+ char index_file[FN_REFLEN], *names,*keynames;
+ uchar head[288],*disk_buff,new_field_pack_flag;
+ my_string record;
+ const char **int_array;
+ bool new_frm_ver,use_hash, null_field_first;
+ File file;
+ Field **field_ptr,*reg_field;
+ KEY *keyinfo;
+ KEY_PART_INFO *key_part;
+ uchar *null_pos;
+ uint null_bit;
+ SQL_CRYPT *crypted=0;
+ DBUG_ENTER("openfrm");
+ DBUG_PRINT("enter",("name: '%s' form: %lx",name,outparam));
+
+ bzero((char*) outparam,sizeof(*outparam));
+ outparam->blob_ptr_size=sizeof(char*);
+ disk_buff=NULL; record= NULL; keynames=NullS;
+ outparam->db_stat = db_stat;
+ error=1;
+
+ init_sql_alloc(&outparam->mem_root,1024);
+ MEM_ROOT *old_root=my_pthread_getspecific_ptr(MEM_ROOT*,THR_MALLOC);
+ my_pthread_setspecific_ptr(THR_MALLOC,&outparam->mem_root);
+
+ outparam->real_name=strdup_root(&outparam->mem_root,
+ name+dirname_length(name));
+ *fn_ext(outparam->real_name)='\0'; // Remove extension
+ outparam->table_name=my_strdup(alias,MYF(MY_WME));
+ if (!outparam->real_name || !outparam->table_name)
+ goto err_end;
+
+ flag= (prgflag & CHANGE_FRM) ? O_RDWR : O_RDONLY | O_SHARE;
+ if ((file=my_open(fn_format(index_file,name,"",reg_ext,4),flag,
+ MYF(0)))
+ < 0)
+ {
+ goto err_end; /* purecov: inspected */
+ }
+ error=4;
+ if (!(outparam->path= strdup_root(&outparam->mem_root,name)))
+ goto err_not_open;
+ *fn_ext(outparam->path)='\0'; // Remove extension
+
+ if (my_read(file,(byte*) head,64,MYF(MY_NABP))) goto err_not_open;
+ if (head[0] != (uchar) 254 || head[1] != 1 ||
+ (head[2] != FRM_VER && head[2] != FRM_VER+1))
+ goto err_not_open; /* purecov: inspected */
+ new_field_pack_flag=head[27];
+ new_frm_ver= (head[2] == FRM_VER+1);
+
+ error=3;
+ if (!(pos=get_form_pos(file,head,(TYPELIB*) 0)))
+ goto err_not_open; /* purecov: inspected */
+ *fn_ext(index_file)='\0'; // Remove .frm extension
+
+ outparam->db_type=ha_checktype((enum db_type) (uint) *(head+3));
+ outparam->db_create_options=db_create_options=uint2korr(head+30);
+ outparam->db_options_in_use=outparam->db_create_options;
+ null_field_first=0;
+ if (!head[32]) // New frm file in 3.23
+ {
+ outparam->avg_row_length=uint4korr(head+34);
+ outparam->row_type=(row_type) head[40];
+ outparam->raid_type= head[41];
+ outparam->raid_chunks= head[42];
+ outparam->raid_chunksize= uint4korr(head+43);
+ null_field_first=1;
+ }
+ outparam->db_record_offset=1;
+ if (db_create_options & HA_OPTION_LONG_BLOB_PTR)
+ outparam->blob_ptr_size=portable_sizeof_char_ptr;
+ outparam->db_low_byte_first=test(outparam->db_type == DB_TYPE_MYISAM ||
+ outparam->db_type == DB_TYPE_BERKELEY_DB ||
+ outparam->db_type == DB_TYPE_HEAP);
+
+ error=4;
+ outparam->max_rows=uint4korr(head+18);
+ outparam->min_rows=uint4korr(head+22);
+
+ /* Read keyinformation */
+ VOID(my_seek(file,(ulong) uint2korr(head+6),MY_SEEK_SET,MYF(0)));
+ if (read_string(file,(gptr*) &disk_buff,(uint) uint2korr(head+28)))
+ goto err_not_open; /* purecov: inspected */
+ outparam->keys=keys=disk_buff[0];
+ outparam->keys_in_use= (((key_map) 1) << keys)- (key_map) 1;
+
+ outparam->key_parts=key_parts=disk_buff[1];
+ n_length=keys*sizeof(KEY)+key_parts*sizeof(KEY_PART_INFO);
+ if (!(keyinfo = (KEY*) alloc_root(&outparam->mem_root,
+ n_length+uint2korr(disk_buff+4))))
+ goto err_not_open; /* purecov: inspected */
+ bzero((char*) keyinfo,n_length);
+ outparam->key_info=keyinfo;
+ outparam->max_key_length=0;
+ key_part= (KEY_PART_INFO*) (keyinfo+keys);
+ strpos=disk_buff+6;
+
+ ulong *rec_per_key;
+ if (!(rec_per_key= (ulong*) alloc_root(&outparam->mem_root,
+ sizeof(ulong*)*key_parts)))
+ goto err_not_open;
+
+ for (i=0 ; i < keys ; i++, keyinfo++)
+ {
+ uint null_parts=0;
+ keyinfo->flags= ((uint) strpos[0]) ^ HA_NOSAME;
+ keyinfo->key_length= (uint) uint2korr(strpos+1);
+ keyinfo->key_parts= (uint) strpos[3]; strpos+=4;
+ keyinfo->key_part= key_part;
+ keyinfo->rec_per_key= rec_per_key;
+ for (j=keyinfo->key_parts ; j-- ; key_part++)
+ {
+ *rec_per_key++=0;
+ key_part->fieldnr= (uint16) (uint2korr(strpos) & FIELD_NR_MASK);
+ key_part->offset= (uint) uint2korr(strpos+2)-1;
+ key_part->key_type= (uint) uint2korr(strpos+5);
+ // key_part->field= (Field*) 0; // Will be fixed later
+ if (new_frm_ver)
+ {
+ key_part->key_part_flag= *(strpos+4);
+ key_part->length= (uint) uint2korr(strpos+7);
+ strpos+=9;
+ }
+ else
+ {
+ key_part->length= *(strpos+4);
+ key_part->key_part_flag=0;
+ if (key_part->length > 128)
+ {
+ key_part->length&=127; /* purecov: inspected */
+ key_part->key_part_flag=HA_REVERSE_SORT; /* purecov: inspected */
+ }
+ strpos+=7;
+ }
+ key_part->store_length=key_part->length;
+ }
+ keyinfo->key_length+=null_parts;
+ set_if_bigger(outparam->max_key_length,keyinfo->key_length+
+ keyinfo->key_parts);
+ if (keyinfo->flags & HA_NOSAME)
+ set_if_bigger(outparam->max_unique_length,keyinfo->key_length);
+ }
+
+ (void) strmov(keynames= (char *) key_part,(char *) strpos);
+ outparam->reclength = uint2korr((head+16));
+ if (*(head+26) == 1)
+ outparam->system=1; /* one-record-database */
+#ifdef HAVE_CRYPTED_FRM
+ else if (*(head+26) == 2)
+ {
+ extern SQL_CRYPT *get_crypt_for_frm(void);
+ my_pthread_setspecific_ptr(THR_MALLOC,old_root);
+ crypted=get_crypt_for_frm();
+ my_pthread_setspecific_ptr(THR_MALLOC,&outparam->mem_root);
+ outparam->crypted=1;
+ }
+#endif
+
+ if (!(outparam->file= get_new_handler(outparam,outparam->db_type)))
+ goto err_not_open;
+ error=2;
+ if (db_stat)
+ {
+ if ((outparam->file->
+ ha_open(index_file,
+ (db_stat & HA_READ_ONLY ? O_RDONLY : O_RDWR),
+ (db_stat & HA_OPEN_TEMPORARY ? HA_OPEN_TMP_TABLE :
+ db_stat & HA_WAIT_IF_LOCKED ||
+ specialflag & SPECIAL_WAIT_IF_LOCKED ?
+ HA_OPEN_WAIT_IF_LOCKED :
+ (db_stat & (HA_ABORT_IF_LOCKED | HA_GET_INFO)) ?
+ HA_OPEN_ABORT_IF_LOCKED :
+ HA_OPEN_IGNORE_IF_LOCKED) | ha_open_options)))
+ goto err_not_open; /* purecov: inspected */
+ }
+ outparam->db_low_byte_first=outparam->file->low_byte_first();
+
+ error=4;
+ outparam->reginfo.lock_type= TL_UNLOCK;
+ outparam->current_lock=F_UNLCK;
+ if (db_stat & HA_OPEN_KEYFILE || (prgflag & DELAYED_OPEN)) records=2;
+ else records=1;
+ if (prgflag & (READ_ALL+EXTRA_RECORD)) records++;
+ rec_buff_length=ALIGN_SIZE(outparam->reclength+1);
+ if (!(outparam->record[0]= (byte*)
+ (record = (char *) alloc_root(&outparam->mem_root,
+ rec_buff_length * records))))
+ goto err; /* purecov: inspected */
+ record[outparam->reclength]=0; // For purify and ->c_ptr()
+ outparam->rec_buff_length=rec_buff_length;
+ if (my_pread(file,(byte*) record,(uint) outparam->reclength,
+ (ulong) (uint2korr(head+6)+uint2korr(head+14)),
+ MYF(MY_NABP)))
+ goto err; /* purecov: inspected */
+ for (i=0 ; i < records ; i++, record+=rec_buff_length)
+ {
+ outparam->record[i]=(byte*) record;
+ if (i)
+ memcpy(record,record-rec_buff_length,(uint) outparam->reclength);
+ }
+
+ if (records == 2)
+ { /* fix for select */
+ outparam->record[2]=outparam->record[1];
+ if (db_stat & HA_READ_ONLY)
+ outparam->record[1]=outparam->record[0]; /* purecov: inspected */
+ }
+
+ VOID(my_seek(file,pos,MY_SEEK_SET,MYF(0)));
+ if (my_read(file,(byte*) head,288,MYF(MY_NABP))) goto err;
+ if (crypted)
+ {
+ crypted->decode((char*) head+256,288-256);
+ if (sint2korr(head+284) != 0) // Should be 0
+ goto err; // Wrong password
+ }
+
+ outparam->fields= uint2korr(head+258);
+ pos=uint2korr(head+260); /* Length of all screens */
+ n_length=uint2korr(head+268);
+ interval_count=uint2korr(head+270);
+ interval_parts=uint2korr(head+272);
+ int_length=uint2korr(head+274);
+ outparam->null_fields=uint2korr(head+282);
+ outparam->comment=strdup_root(&outparam->mem_root,
+ (char*) head+47);
+
+ DBUG_PRINT("form",("i_count: %d i_parts: %d index: %d n_length: %d int_length: %d", interval_count,interval_parts, outparam->keys,n_length,int_length));
+
+ if (!(field_ptr = (Field **)
+ alloc_root(&outparam->mem_root,
+ (uint) ((outparam->fields+1)*sizeof(Field*)+
+ interval_count*sizeof(TYPELIB)+
+ (outparam->fields+interval_parts+
+ keys+3)*sizeof(my_string)+
+ (n_length+int_length)))))
+ goto err; /* purecov: inspected */
+
+ outparam->field=field_ptr;
+ read_length=((uint) (outparam->fields*11)+pos+
+ (uint) (n_length+int_length));
+ if (read_string(file,(gptr*) &disk_buff,read_length))
+ goto err; /* purecov: inspected */
+ if (crypted)
+ {
+ crypted->decode((char*) disk_buff,read_length);
+ delete crypted;
+ crypted=0;
+ }
+ strpos= disk_buff+pos;
+
+ outparam->intervals= (TYPELIB*) (field_ptr+outparam->fields+1);
+ int_array= (const char **) (outparam->intervals+interval_count);
+ names= (char*) (int_array+outparam->fields+interval_parts+keys+3);
+ if (!interval_count)
+ outparam->intervals=0; // For better debugging
+ memcpy((char*) names, strpos+(outparam->fields*11),
+ (uint) (n_length+int_length));
+
+ fix_type_pointers(&int_array,&outparam->fieldnames,1,&names);
+ fix_type_pointers(&int_array,outparam->intervals,interval_count,
+ &names);
+ if (keynames)
+ fix_type_pointers(&int_array,&outparam->keynames,1,&keynames);
+ VOID(my_close(file,MYF(MY_WME)));
+
+ record=(char*) outparam->record[0]-1; /* Fieldstart = 1 */
+ if (null_field_first)
+ {
+ outparam->null_flags=null_pos=(uchar*) record+1;
+ null_bit= (db_create_options & HA_OPTION_PACK_RECORD) ? 1 : 2;
+ outparam->null_bytes=(outparam->null_fields+null_bit+6)/8;
+ }
+ else
+ {
+ outparam->null_bytes=(outparam->null_fields+7)/8;
+ outparam->null_flags=null_pos=
+ (uchar*) (record+1+outparam->reclength-outparam->null_bytes);
+ null_bit=1;
+ }
+
+ use_hash= outparam->fields >= MAX_FIELDS_BEFORE_HASH;
+ if (use_hash)
+ use_hash= !hash_init(&outparam->name_hash,
+ outparam->fields,0,0,
+ (hash_get_key) get_field_name,0,
+ HASH_CASE_INSENSITIVE);
+
+ for (i=0 ; i < outparam->fields; i++, strpos+= 11, field_ptr++)
+ {
+ uint pack_flag= uint2korr(strpos+6);
+ uint interval_nr= (uint) strpos[10];
+
+ *field_ptr=reg_field=
+ make_field(record+uint2korr(strpos+4),
+ (uint32) strpos[3], // field_length
+ null_pos,null_bit,
+ pack_flag,
+ (Field::utype) MTYP_TYPENR((uint) strpos[8]),
+ (interval_nr ?
+ outparam->intervals+interval_nr-1 :
+ (TYPELIB*) 0),
+ outparam->fieldnames.type_names[i],
+ outparam);
+ if (!(reg_field->flags & NOT_NULL_FLAG))
+ {
+ if ((null_bit<<=1) == 256)
+ {
+ null_pos++;
+ null_bit=1;
+ }
+ }
+ if (reg_field->unireg_check == Field::NEXT_NUMBER)
+ {
+ if ((int) (outparam->next_number_index= (uint)
+ find_ref_key(outparam,reg_field,
+ &outparam->next_number_key_offset)) < 0)
+ reg_field->unireg_check=Field::NONE; /* purecov: inspected */
+ else
+ {
+ outparam->found_next_number_field=reg_field;
+ reg_field->flags|=AUTO_INCREMENT_FLAG;
+ }
+ }
+ if (outparam->timestamp_field == reg_field)
+ outparam->timestamp_field_offset=i;
+ if (use_hash)
+ (void) hash_insert(&outparam->name_hash,(byte*) *field_ptr); // Will never fail
+ }
+ *field_ptr=0; // End marker
+
+ /* Fix key->name and key_part->field */
+ if (key_parts)
+ {
+ uint primary_key=(uint) (find_type((char*) "PRIMARY",&outparam->keynames,
+ 3)-1);
+ uint ha_option=outparam->file->option_flag();
+ keyinfo=outparam->key_info;
+ key_part=keyinfo->key_part;
+
+ for (uint key=0 ; key < outparam->keys ; key++,keyinfo++)
+ {
+ uint usable_parts=0;
+ keyinfo->name=(char*) outparam->keynames.type_names[key];
+ if (primary_key >= MAX_KEY && (keyinfo->flags & HA_NOSAME))
+ {
+ /*
+ If the UNIQUE key don't have NULL columns, declare this as
+ a primary key.
+ */
+ primary_key=key;
+ for (i=0 ; i < keyinfo->key_parts ;i++)
+ {
+ if (!key_part[i].fieldnr ||
+ outparam->field[key_part[i].fieldnr-1]->null_ptr)
+ {
+ primary_key=MAX_KEY; // Can't be used
+ break;
+ }
+ }
+ }
+
+ for (i=0 ; i < keyinfo->key_parts ; key_part++,i++)
+ {
+ if (new_field_pack_flag <= 1)
+ key_part->fieldnr=(uint16) find_field(outparam,
+ (uint) key_part->offset,
+ (uint) key_part->length);
+#ifdef EXTRA_DEBUG
+ if (key_part->fieldnr > outparam->fields)
+ goto err; // sanity check
+#endif
+ if (key_part->fieldnr)
+ { // Should always be true !
+ Field *field=key_part->field=outparam->field[key_part->fieldnr-1];
+ if (field->null_ptr)
+ {
+ key_part->null_offset=(uint) ((byte*) field->null_ptr -
+ outparam->record[0]);
+ key_part->null_bit= field->null_bit;
+ key_part->store_length+=HA_KEY_NULL_LENGTH;
+ keyinfo->flags|=HA_NULL_PART_KEY;
+ keyinfo->extra_length+= HA_KEY_NULL_LENGTH;
+ }
+ if (field->type() == FIELD_TYPE_BLOB ||
+ field->real_type() == FIELD_TYPE_VAR_STRING)
+ {
+ if (field->type() == FIELD_TYPE_BLOB)
+ key_part->key_part_flag|= HA_BLOB_PART;
+ keyinfo->extra_length+=HA_KEY_BLOB_LENGTH;
+ key_part->store_length+=HA_KEY_BLOB_LENGTH;
+ }
+ if (i == 0 && key != primary_key)
+ field->flags |=
+ ((keyinfo->flags & HA_NOSAME) &&
+ field->key_length() ==
+ keyinfo->key_length ? UNIQUE_KEY_FLAG : MULTIPLE_KEY_FLAG);
+ if (i == 0)
+ field->key_start|= ((key_map) 1 << key);
+ if (ha_option & HA_HAVE_KEY_READ_ONLY &&
+ field->key_length() == key_part->length)
+ {
+ if (field->key_type() != HA_KEYTYPE_TEXT ||
+ !(ha_option & HA_KEY_READ_WRONG_STR))
+ field->part_of_key|= ((key_map) 1 << key);
+ }
+ if (!(key_part->key_part_flag & HA_REVERSE_SORT) &&
+ usable_parts == i)
+ usable_parts++; // For FILESORT
+ field->flags|= PART_KEY_FLAG;
+ if (key == primary_key)
+ {
+ field->flags|= PRI_KEY_FLAG;
+ if (ha_option & HA_PRIMARY_KEY_IN_READ_INDEX)
+ field->part_of_key|= ((key_map) 1 << primary_key);
+ }
+ if (field->key_length() != key_part->length)
+ {
+ key_part->key_part_flag|= HA_PART_KEY;
+ if (field->type() != FIELD_TYPE_BLOB)
+ { // Create a new field
+ field=key_part->field=field->new_field(outparam);
+ field->field_length=key_part->length;
+ }
+ }
+ }
+ else
+ { // Error: shorten key
+ keyinfo->key_parts=usable_parts;
+ keyinfo->flags=0;
+ }
+ }
+ keyinfo->usable_key_parts=usable_parts; // Filesort
+ }
+ if (primary_key < MAX_KEY &&
+ (outparam->keys_in_use & ((key_map) 1 << primary_key)))
+ {
+ outparam->primary_key=primary_key;
+ if (outparam->key_info[primary_key].key_parts == 1)
+ {
+ Field *field= outparam->key_info[primary_key].key_part[0].field;
+ if (field && field->result_type() == INT_RESULT)
+ outparam->rowid_field=field;
+ }
+ }
+ else
+ outparam->primary_key = MAX_KEY; // we do not have a primary key
+ }
+ x_free((gptr) disk_buff);
+ if (new_field_pack_flag <= 1)
+ { /* Old file format with default null */
+ uint null_length=(outparam->null_fields+7)/8;
+ bfill(outparam->null_flags,null_length,255);
+ bfill(outparam->null_flags+outparam->rec_buff_length,null_length,255);
+ if (records > 2)
+ bfill(outparam->null_flags+outparam->rec_buff_length*2,null_length,255);
+ }
+
+ if (outparam->blob_fields)
+ {
+ Field **ptr;
+ Field_blob **save;
+
+ if (!(outparam->blob_field=save=
+ (Field_blob**) alloc_root(&outparam->mem_root,
+ (uint) (outparam->blob_fields+1)*
+ sizeof(Field_blob*))))
+ goto err;
+ for (ptr=outparam->field ; *ptr ; ptr++)
+ {
+ if ((*ptr)->flags & BLOB_FLAG)
+ (*save++)= (Field_blob*) *ptr;
+ }
+ *save=0; // End marker
+ }
+ else
+ outparam->blob_field=
+ (Field_blob**) (outparam->field+outparam->fields); // Point at null ptr
+
+ my_pthread_setspecific_ptr(THR_MALLOC,old_root);
+ opened_tables++;
+#ifndef DBUG_OFF
+ if (use_hash)
+ (void) hash_check(&outparam->name_hash);
+#endif
+ if (db_stat)
+ outparam->file->initialize();
+ DBUG_RETURN (0);
+
+ err:
+ if (outparam->file && db_stat)
+ (void) outparam->file->close();
+ err_not_open:
+ x_free((gptr) disk_buff);
+ delete crypted;
+ VOID(my_close(file,MYF(MY_WME)));
+
+ err_end: /* Here when no file */
+ delete crypted;
+ my_pthread_setspecific_ptr(THR_MALLOC,old_root);
+ frm_error(error,outparam,name,ME_ERROR+ME_WAITTANG);
+ outparam->file=0; // For easyer errorchecking
+ free_root(&outparam->mem_root);
+ my_free(outparam->table_name,MYF(MY_ALLOW_ZERO_PTR));
+ DBUG_RETURN (error);
+} /* openfrm */
+
+
+ /* close a .frm file and it's tables */
+
+int closefrm(register TABLE *table)
+{
+ int error=0;
+ DBUG_ENTER("closefrm");
+ if (table->db_stat)
+ error=table->file->close();
+ if (table->table_name)
+ {
+ my_free(table->table_name,MYF(0));
+ table->table_name=0;
+ }
+ if (table->fields)
+ {
+ for (Field **ptr=table->field ; *ptr ; ptr++)
+ delete *ptr;
+ table->fields=0;
+ }
+ delete table->file;
+ table->file=0; /* For easyer errorchecking */
+ hash_free(&table->name_hash);
+ free_root(&table->mem_root);
+ DBUG_RETURN(error);
+}
+
+
+/* Deallocate temporary blob storage */
+
+void free_blobs(register TABLE *table)
+{
+ for (Field_blob **ptr=table->blob_field ; *ptr ; ptr++)
+ (*ptr)->free();
+}
+
+
+ /* Find where a form starts */
+ /* if formname is NullS then only formnames is read */
+
+ulong get_form_pos(File file, uchar *head, TYPELIB *save_names)
+{
+ uint a_length,names,length;
+ uchar *pos,*buf;
+ ulong ret_value=0;
+ DBUG_ENTER("get_form_pos");
+
+ names=uint2korr(head+8);
+ a_length=(names+2)*sizeof(my_string); /* Room for two extra */
+
+ if (!save_names)
+ a_length=0;
+ else
+ save_names->type_names=0; /* Clear if error */
+
+ if (names)
+ {
+ length=uint2korr(head+4);
+ VOID(my_seek(file,64L,MY_SEEK_SET,MYF(0)));
+ if (!(buf= (uchar*) my_malloc((uint) length+a_length+names*4,
+ MYF(MY_WME))) ||
+ my_read(file,(byte*) buf+a_length,(uint) (length+names*4),
+ MYF(MY_NABP)))
+ { /* purecov: inspected */
+ x_free((gptr) buf); /* purecov: inspected */
+ DBUG_RETURN(0L); /* purecov: inspected */
+ }
+ pos= buf+a_length+length;
+ ret_value=uint4korr(pos);
+ }
+ if (! save_names)
+ my_free((gptr) buf,MYF(0));
+ else if (!names)
+ bzero((char*) save_names,sizeof(save_names));
+ else
+ {
+ char *str;
+ str=(char *) (buf+a_length);
+ fix_type_pointers((const char ***) &buf,save_names,1,&str);
+ }
+ DBUG_RETURN(ret_value);
+}
+
+
+ /* Read string from a file with malloc */
+
+int read_string(File file, gptr *to, uint length)
+{
+ DBUG_ENTER("read_string");
+
+ x_free((gptr) *to);
+ if (!(*to= (gptr) my_malloc(length+1,MYF(MY_WME))) ||
+ my_read(file,(byte*) *to,length,MYF(MY_NABP)))
+ {
+ x_free((gptr) *to); /* purecov: inspected */
+ *to= 0; /* purecov: inspected */
+ DBUG_RETURN(1); /* purecov: inspected */
+ }
+ *((char*) *to+length)= '\0';
+ DBUG_RETURN (0);
+} /* read_string */
+
+
+ /* Add a new form to a form file */
+
+ulong make_new_entry(File file, uchar *fileinfo, TYPELIB *formnames,
+ const char *newname)
+{
+ uint i,bufflength,maxlength,n_length,length,names;
+ ulong endpos,newpos;
+ char buff[IO_SIZE];
+ uchar *pos;
+ DBUG_ENTER("make_new_entry");
+
+ length=strlen(newname)+1;
+ n_length=uint2korr(fileinfo+4);
+ maxlength=uint2korr(fileinfo+6);
+ names=uint2korr(fileinfo+8);
+ newpos=uint4korr(fileinfo+10);
+
+ if (64+length+n_length+(names+1)*4 > maxlength)
+ { /* Expand file */
+ newpos+=IO_SIZE;
+ int4store(fileinfo+10,newpos);
+ endpos=(ulong) my_seek(file,0L,MY_SEEK_END,MYF(0)); /* Copy from file-end */
+ bufflength= (uint) (endpos & (IO_SIZE-1)); /* IO_SIZE is a power of 2 */
+
+ while (endpos > maxlength)
+ {
+ VOID(my_seek(file,(ulong) (endpos-bufflength),MY_SEEK_SET,MYF(0)));
+ if (my_read(file,(byte*) buff,bufflength,MYF(MY_NABP+MY_WME)))
+ DBUG_RETURN(0L);
+ VOID(my_seek(file,(ulong) (endpos-bufflength+IO_SIZE),MY_SEEK_SET,
+ MYF(0)));
+ if ((my_write(file,(byte*) buff,bufflength,MYF(MY_NABP+MY_WME))))
+ DBUG_RETURN(0);
+ endpos-=bufflength; bufflength=IO_SIZE;
+ }
+ bzero(buff,IO_SIZE); /* Null new block */
+ VOID(my_seek(file,(ulong) maxlength,MY_SEEK_SET,MYF(0)));
+ if (my_write(file,(byte*) buff,bufflength,MYF(MY_NABP+MY_WME)))
+ DBUG_RETURN(0L);
+ maxlength+=IO_SIZE; /* Fix old ref */
+ int2store(fileinfo+6,maxlength);
+ for (i=names, pos= (uchar*) *formnames->type_names+n_length-1; i-- ;
+ pos+=4)
+ {
+ endpos=uint4korr(pos)+IO_SIZE;
+ int4store(pos,endpos);
+ }
+ }
+
+ if (n_length == 1 )
+ { /* First name */
+ length++;
+ VOID(strxmov(buff,"/",newname,"/",NullS));
+ }
+ else
+ VOID(strxmov(buff,newname,"/",NullS)); /* purecov: inspected */
+ VOID(my_seek(file,63L+(ulong) n_length,MY_SEEK_SET,MYF(0)));
+ if (my_write(file,(byte*) buff,(uint) length+1,MYF(MY_NABP+MY_WME)) ||
+ (names && my_write(file,(byte*) (*formnames->type_names+n_length-1),
+ names*4, MYF(MY_NABP+MY_WME))) ||
+ my_write(file,(byte*) fileinfo+10,(uint) 4,MYF(MY_NABP+MY_WME)))
+ DBUG_RETURN(0L); /* purecov: inspected */
+
+ int2store(fileinfo+8,names+1);
+ int2store(fileinfo+4,n_length+length);
+ VOID(my_chsize(file,newpos,MYF(MY_WME))); /* Append file with '\0' */
+ DBUG_RETURN(newpos);
+} /* make_new_entry */
+
+
+ /* error message when opening a form file */
+
+static void frm_error(int error, TABLE *form, const char *name, myf errortype)
+{
+ int err_no;
+ char buff[FN_REFLEN];
+ const char *form_dev="",*datext;
+ DBUG_ENTER("frm_error");
+
+ switch (error) {
+ case 1:
+ if (my_errno == ENOENT)
+ {
+ char *db;
+ uint length=dirname_part(buff,name);
+ buff[length-1]=0;
+ db=buff+dirname_length(buff);
+ my_error(ER_NO_SUCH_TABLE,MYF(0),db,form->real_name);
+ }
+ else
+ my_error(ER_FILE_NOT_FOUND,errortype,
+ fn_format(buff,name,form_dev,reg_ext,0),my_errno);
+ break;
+ case 2:
+ {
+ datext=form->file ? *form->file->bas_ext() : "";
+ err_no= (my_errno == ENOENT) ? ER_FILE_NOT_FOUND : (my_errno == EAGAIN) ?
+ ER_FILE_USED : ER_CANT_OPEN_FILE;
+ my_error(err_no,errortype,
+ fn_format(buff,form->real_name,form_dev,datext,2),my_errno);
+ break;
+ }
+ default: /* Better wrong error than none */
+ case 4:
+ my_error(ER_NOT_FORM_FILE,errortype,
+ fn_format(buff,name,form_dev,reg_ext,0));
+ break;
+ }
+ DBUG_VOID_RETURN;
+} /* frm_error */
+
+
+ /*
+ ** fix a str_type to a array type
+ ** typeparts sepearated with some char. differents types are separated
+ ** with a '\0'
+ */
+
+static void
+fix_type_pointers(const char ***array, TYPELIB *point_to_type, uint types,
+ char **names)
+{
+ char *type_name, *ptr;
+ char chr;
+
+ ptr= *names;
+ while (types--)
+ {
+ point_to_type->name=0;
+ point_to_type->type_names= *array;
+
+ if ((chr= *ptr)) /* Test if empty type */
+ {
+ while ((type_name=strchr(ptr+1,chr)) != NullS)
+ {
+ *((*array)++) = ptr+1;
+ *type_name= '\0'; /* End string */
+ ptr=type_name;
+ }
+ ptr+=2; /* Skipp end mark and last 0 */
+ }
+ else
+ ptr++;
+ point_to_type->count= (uint) (*array - point_to_type->type_names);
+ point_to_type++;
+ *((*array)++)= NullS; /* End of type */
+ }
+ *names=ptr; /* Update end */
+ return;
+} /* fix_type_pointers */
+
+
+TYPELIB *typelib(List<String> &strings)
+{
+ TYPELIB *result=(TYPELIB*) sql_alloc(sizeof(TYPELIB));
+ if (!result)
+ return 0;
+ result->count=strings.elements;
+ result->name="";
+ if (!(result->type_names=(const char **) sql_alloc(sizeof(char *)*
+ (result->count+1))))
+ return 0;
+ List_iterator<String> it(strings);
+ String *tmp;
+ for (uint i=0; (tmp=it++) ; i++)
+ result->type_names[i]=tmp->ptr();
+ result->type_names[result->count]=0; // End marker
+ return result;
+}
+
+
+ /*
+ ** Search after a field with given start & length
+ ** If an exact field isn't found, return longest field with starts
+ ** at right position.
+ ** Return 0 on error, else field number+1
+ ** This is needed because in some .frm fields 'fieldnr' was saved wrong
+ */
+
+static uint find_field(TABLE *form,uint start,uint length)
+{
+ Field **field;
+ uint i,pos;
+
+ pos=0;
+
+ for (field=form->field, i=1 ; i<= form->fields ; i++,field++)
+ {
+ if ((*field)->offset() == start)
+ {
+ if ((*field)->key_length() == length)
+ return (i);
+ if (!pos || form->field[pos-1]->pack_length() <
+ (*field)->pack_length())
+ pos=i;
+ }
+ }
+ return (pos);
+}
+
+
+ /* Check that the integer is in the internvall */
+
+int set_zone(register int nr, int min_zone, int max_zone)
+{
+ if (nr<=min_zone)
+ return (min_zone);
+ if (nr>=max_zone)
+ return (max_zone);
+ return (nr);
+} /* set_zone */
+
+ /* Adjust number to next larger disk buffer */
+
+ulong next_io_size(register ulong pos)
+{
+ reg2 ulong offset;
+ if ((offset= pos & (IO_SIZE-1)))
+ return pos-offset+IO_SIZE;
+ return pos;
+} /* next_io_size */
+
+
+void append_unescaped(String *res,const char *pos)
+{
+ for ( ; *pos ; pos++)
+ {
+ switch (*pos) {
+ case 0: /* Must be escaped for 'mysql' */
+ res->append('\\');
+ res->append('0');
+ break;
+ case '\n': /* Must be escaped for logs */
+ res->append('\\');
+ res->append('n');
+ break;
+ case '\r':
+ res->append('\\'); /* This gives better readbility */
+ res->append('r');
+ break;
+ case '\\':
+ res->append('\\'); /* Because of the sql syntax */
+ res->append('\\');
+ break;
+ case '\'':
+ res->append('\''); /* Because of the sql syntax */
+ res->append('\'');
+ break;
+ default:
+ res->append(*pos);
+ break;
+ }
+ }
+}
+
+ /* Create a .frm file */
+
+File create_frm(register my_string name, uint reclength, uchar *fileinfo,
+ HA_CREATE_INFO *create_info, uint keys)
+{
+ register File file;
+ uint key_length;
+ ulong length;
+ char fill[IO_SIZE];
+
+#if SIZEOF_OFF_T > 4
+ /* Fix this in MySQL 4.0; The current limit is 4G rows (QQ) */
+ if (create_info->max_rows > ~(ulong) 0)
+ create_info->max_rows= ~(ulong) 0;
+ if (create_info->min_rows > ~(ulong) 0)
+ create_info->min_rows= ~(ulong) 0;
+#endif
+
+ if ((file=my_create(name,CREATE_MODE,O_RDWR | O_TRUNC,MYF(MY_WME))) >= 0)
+ {
+ bzero((char*) fileinfo,64);
+ fileinfo[0]=(uchar) 254; fileinfo[1]= 1; fileinfo[2]= FRM_VER+1; // Header
+ fileinfo[3]= (uchar) ha_checktype(create_info->db_type);
+ fileinfo[4]=1;
+ int2store(fileinfo+6,IO_SIZE); /* Next block starts here */
+ key_length=keys*(7+NAME_LEN+MAX_REF_PARTS*9)+16;
+ length=(ulong) next_io_size((ulong) (IO_SIZE+key_length+reclength));
+ int4store(fileinfo+10,length);
+ int2store(fileinfo+14,key_length);
+ int2store(fileinfo+16,reclength);
+ int4store(fileinfo+18,create_info->max_rows);
+ int4store(fileinfo+22,create_info->min_rows);
+ fileinfo[27]=2; // Use long pack-fields
+ create_info->table_options|=HA_OPTION_LONG_BLOB_PTR; // Use portable blob pointers
+ int2store(fileinfo+30,create_info->table_options);
+ fileinfo[32]=0; // No filename anymore
+ int4store(fileinfo+34,create_info->avg_row_length);
+ fileinfo[40]= (uchar) create_info->row_type;
+ fileinfo[41]= (uchar) create_info->raid_type;
+ fileinfo[42]= (uchar) create_info->raid_chunks;
+ int4store(fileinfo+43,create_info->raid_chunksize);
+ bzero(fill,IO_SIZE);
+ for (; length > IO_SIZE ; length-= IO_SIZE)
+ {
+ if (my_write(file,(byte*) fill,IO_SIZE,MYF(MY_WME | MY_NABP)))
+ {
+ VOID(my_close(file,MYF(0)));
+ VOID(my_delete(name,MYF(0)));
+ return(-1);
+ }
+ }
+ }
+ return (file);
+} /* create_frm */
+
+
+void update_create_info_from_table(HA_CREATE_INFO *create_info, TABLE *table)
+{
+ create_info->max_rows=table->max_rows;
+ create_info->min_rows=table->min_rows;
+ create_info->table_options=table->db_create_options;
+ create_info->avg_row_length=table->avg_row_length;
+ create_info->row_type=table->row_type;
+ create_info->raid_type=table->raid_type;
+ create_info->raid_chunks=table->raid_chunks;
+ create_info->raid_chunksize=table->raid_chunksize;
+}
+
+int
+rename_file_ext(const char * from,const char * to,const char * ext)
+{
+ char from_b[FN_REFLEN],to_b[FN_REFLEN];
+ VOID(strxmov(from_b,from,ext,NullS));
+ VOID(strxmov(to_b,to,ext,NullS));
+ return (my_rename(from_b,to_b,MYF(MY_WME)));
+}
+
+
+/*
+ Alloc a value as a string and return it
+ If field is empty, return NULL
+*/
+
+char *get_field(MEM_ROOT *mem, TABLE *table, uint fieldnr)
+{
+ Field *field=table->field[fieldnr];
+ char buff[MAX_FIELD_WIDTH];
+ String str(buff,sizeof(buff));
+ field->val_str(&str,&str);
+ uint length=str.length();
+ if (!length)
+ return NullS;
+ char *to= (char*) alloc_root(mem,length+1);
+ memcpy(to,str.ptr(),(uint) length);
+ to[length]=0;
+ return to;
+}
+
+bool check_db_name(const char *name)
+{
+ while (*name)
+ {
+#if defined(USE_MB) && defined(USE_MB_IDENT)
+ if (use_mb(default_charset_info))
+ {
+ int len=my_ismbchar(default_charset_info, name, name+MBMAXLEN);
+ if (len)
+ {
+ name += len;
+ continue;
+ }
+ }
+#endif
+ if (*name == '/' || *name == FN_LIBCHAR)
+ return 1;
+ name++;
+ }
+ return 0;
+}
+
+
+/*
+ Allow anything as a table name, as long as it doesn't contain an
+ a '/', or a '.' character
+ returns 1 on error
+*/
+
+
+bool check_table_name(const char *name, uint length)
+{
+ const char *end= name+length;
+
+ while (name != end)
+ {
+#if defined(USE_MB) && defined(USE_MB_IDENT)
+ if (use_mb(default_charset_info))
+ {
+ int len=my_ismbchar(default_charset_info, name, end);
+ if (len)
+ {
+ name += len;
+ continue;
+ }
+ }
+#endif
+ if (*name == '/' || *name == FN_LIBCHAR || *name == FN_EXTCHAR)
+ return 1;
+ name++;
+ }
+ return 0;
+}
+
+bool check_column_name(const char *name)
+{
+ while (*name)
+ {
+#if defined(USE_MB) && defined(USE_MB_IDENT)
+ if (use_mb(default_charset_info))
+ {
+ int len=my_ismbchar(default_charset_info, name, name+MBMAXLEN);
+ if (len)
+ {
+ name += len;
+ continue;
+ }
+ }
+#endif
+ if (*name == NAMES_SEP_CHAR)
+ return 1;
+ name++;
+ }
+ return 0;
+}
+
+/*
+** Get type of table from .frm file
+*/
+
+db_type get_table_type(const char *name)
+{
+ File file;
+ uchar head[4];
+ int error;
+ DBUG_ENTER("get_table_type");
+ DBUG_PRINT("enter",("name: '%s'",name));
+
+ if ((file=my_open(name,O_RDONLY, MYF(0))) < 0)
+ DBUG_RETURN(DB_TYPE_UNKNOWN);
+ error=my_read(file,(byte*) head,4,MYF(MY_NABP));
+ my_close(file,MYF(0));
+ if (error || head[0] != (uchar) 254 || head[1] != 1 ||
+ (head[2] != FRM_VER && head[2] != FRM_VER+1))
+ DBUG_RETURN(DB_TYPE_UNKNOWN);
+ DBUG_RETURN(ha_checktype((enum db_type) (uint) *(head+3)));
+}
+
+
+/*****************************************************************************
+** Instansiate templates
+*****************************************************************************/
+
+#ifdef __GNUC__
+template class List<String>;
+template class List_iterator<String>;
+#endif