mirror of
https://github.com/MariaDB/server.git
synced 2025-01-18 04:53:01 +01:00
cfb08851f7
An UNIQUE KEY consisting of NOT NULL columns was displayed as PRIMARY KEY in "DESC t1". According to the code, that was intentional behaviour for some reasons unknown to me. This code was written before bitkeeper time, so I cannot check who and why made this. After discussing on dev-public, a decision was made to remove this code
1534 lines
44 KiB
C++
1534 lines
44 KiB
C++
/* 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, int errarg);
|
|
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= (uint) strlen((*buff)->field_name);
|
|
return (byte*) (*buff)->field_name;
|
|
}
|
|
|
|
/*
|
|
Open a .frm file
|
|
|
|
SYNOPSIS
|
|
openfrm()
|
|
|
|
name path to table-file "db/name"
|
|
alias alias for table
|
|
db_stat open flags (for example HA_OPEN_KEYFILE|HA_OPEN_RNDFILE..)
|
|
can be 0 (example in ha_example_table)
|
|
prgflag READ_ALL etc..
|
|
ha_open_flags HA_OPEN_ABORT_IF_LOCKED etc..
|
|
outparam result table
|
|
|
|
RETURN VALUES
|
|
0 ok
|
|
1 Error (see frm_error)
|
|
2 Error (see frm_error)
|
|
3 Wrong data in .frm file
|
|
4 Error (see frm_error)
|
|
5 Error (see frm_error: charset unavailable)
|
|
6 Unknown .frm version
|
|
*/
|
|
|
|
int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag,
|
|
uint ha_open_flags, TABLE *outparam)
|
|
{
|
|
reg1 uint i;
|
|
reg2 uchar *strpos;
|
|
int j,error, errarg= 0;
|
|
uint rec_buff_length,n_length,int_length,records,key_parts,keys,
|
|
interval_count,interval_parts,read_length,db_create_options;
|
|
uint key_info_length, com_length;
|
|
ulong pos;
|
|
char index_file[FN_REFLEN], *names, *keynames, *comment_pos;
|
|
uchar head[288],*disk_buff,new_field_pack_flag;
|
|
my_string record;
|
|
const char **int_array;
|
|
bool 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, new_frm_ver, field_pack_length;
|
|
SQL_CRYPT *crypted=0;
|
|
MEM_ROOT **root_ptr, *old_root;
|
|
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, TABLE_ALLOC_BLOCK_SIZE, 0);
|
|
root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**, THR_MALLOC);
|
|
old_root= *root_ptr;
|
|
*root_ptr= &outparam->mem_root;
|
|
|
|
outparam->real_name=strdup_root(&outparam->mem_root,
|
|
name+dirname_length(name));
|
|
outparam->table_name=my_strdup(alias,MYF(MY_WME));
|
|
if (!outparam->real_name || !outparam->table_name)
|
|
goto err_end;
|
|
*fn_ext(outparam->real_name)='\0'; // Remove extension
|
|
|
|
if ((file=my_open(fn_format(index_file,name,"",reg_ext,MY_UNPACK_FILENAME),
|
|
O_RDONLY | O_SHARE,
|
|
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)
|
|
goto err_not_open; /* purecov: inspected */
|
|
if (head[2] != FRM_VER && head[2] != FRM_VER+1 && head[2] != FRM_VER+3)
|
|
{
|
|
error= 6;
|
|
goto err_not_open; /* purecov: inspected */
|
|
}
|
|
new_field_pack_flag=head[27];
|
|
new_frm_ver= (head[2] - FRM_VER);
|
|
field_pack_length= new_frm_ver < 2 ? 11 : 17;
|
|
|
|
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->frm_version= head[2];
|
|
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);
|
|
outparam->table_charset=get_charset((uint) head[38],MYF(0));
|
|
null_field_first=1;
|
|
}
|
|
if (!outparam->table_charset)
|
|
{
|
|
/* unknown charset in head[38] or pre-3.23 frm */
|
|
if (use_mb(default_charset_info))
|
|
{
|
|
/* Warn that we may be changing the size of character columns */
|
|
sql_print_warning("'%s' had no or invalid character set, "
|
|
"and default character set is multi-byte, "
|
|
"so character column sizes may have changed",
|
|
name);
|
|
}
|
|
outparam->table_charset=default_charset_info;
|
|
}
|
|
outparam->db_record_offset=1;
|
|
if (db_create_options & HA_OPTION_LONG_BLOB_PTR)
|
|
outparam->blob_ptr_size=portable_sizeof_char_ptr;
|
|
/* Set temporaryly a good value for db_low_byte_first */
|
|
outparam->db_low_byte_first=test(outparam->db_type != DB_TYPE_ISAM);
|
|
error=4;
|
|
outparam->max_rows=uint4korr(head+18);
|
|
outparam->min_rows=uint4korr(head+22);
|
|
|
|
/* Read keyinformation */
|
|
key_info_length= (uint) uint2korr(head+28);
|
|
VOID(my_seek(file,(ulong) uint2korr(head+6),MY_SEEK_SET,MYF(0)));
|
|
if (read_string(file,(gptr*) &disk_buff,key_info_length))
|
|
goto err_not_open; /* purecov: inspected */
|
|
if (disk_buff[0] & 0x80)
|
|
{
|
|
outparam->keys= keys= (disk_buff[1] << 7) | (disk_buff[0] & 0x7f);
|
|
outparam->key_parts= key_parts= uint2korr(disk_buff+2);
|
|
}
|
|
else
|
|
{
|
|
outparam->keys= keys= disk_buff[0];
|
|
outparam->key_parts= key_parts= disk_buff[1];
|
|
}
|
|
outparam->keys_for_keyread.init(0);
|
|
outparam->keys_in_use.init(keys);
|
|
outparam->read_only_keys.init(keys);
|
|
outparam->quick_keys.init();
|
|
outparam->used_keys.init();
|
|
outparam->keys_in_use_for_query.init();
|
|
|
|
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;
|
|
key_part= my_reinterpret_cast(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++)
|
|
{
|
|
if (new_frm_ver == 3)
|
|
{
|
|
keyinfo->flags= (uint) uint2korr(strpos) ^ HA_NOSAME;
|
|
keyinfo->key_length= (uint) uint2korr(strpos+2);
|
|
keyinfo->key_parts= (uint) strpos[4];
|
|
keyinfo->algorithm= (enum ha_key_alg) strpos[5];
|
|
strpos+=8;
|
|
}
|
|
else
|
|
{
|
|
keyinfo->flags= ((uint) strpos[0]) ^ HA_NOSAME;
|
|
keyinfo->key_length= (uint) uint2korr(strpos+1);
|
|
keyinfo->key_parts= (uint) strpos[3];
|
|
keyinfo->algorithm= HA_KEY_ALG_UNDEF;
|
|
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 >= 1)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
keynames=(char*) key_part;
|
|
strpos+= (strmov(keynames, (char *) strpos) - keynames)+1;
|
|
|
|
outparam->reclength = uint2korr((head+16));
|
|
if (*(head+26) == 1)
|
|
outparam->system=1; /* one-record-database */
|
|
#ifdef HAVE_CRYPTED_FRM
|
|
else if (*(head+26) == 2)
|
|
{
|
|
*root_ptr= old_root
|
|
crypted=get_crypt_for_frm();
|
|
*root_ptr= &outparam->mem_root;
|
|
outparam->crypted=1;
|
|
}
|
|
#endif
|
|
|
|
/* Allocate handler */
|
|
if (!(outparam->file= get_new_handler(outparam,outparam->db_type)))
|
|
goto err_not_open;
|
|
|
|
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++;
|
|
/* QQ: TODO, remove the +1 from below */
|
|
rec_buff_length=ALIGN_SIZE(outparam->reclength+1+
|
|
outparam->file->extra_rec_buf_length());
|
|
if (!(outparam->record[0]= (byte*)
|
|
(record = (char *) alloc_root(&outparam->mem_root,
|
|
rec_buff_length * records))))
|
|
goto err_not_open; /* 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) == 0xffff ?
|
|
uint4korr(head+47) : uint2korr(head+14)))),
|
|
MYF(MY_NABP)))
|
|
goto err_not_open; /* purecov: inspected */
|
|
/* HACK: table->record[2] is used instead of table->default_values here */
|
|
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->default_values=outparam->record[1];
|
|
if (db_stat & HA_READ_ONLY)
|
|
outparam->record[1]=outparam->record[0]; /* purecov: inspected */
|
|
}
|
|
outparam->insert_values=0; /* for INSERT ... UPDATE */
|
|
|
|
VOID(my_seek(file,pos,MY_SEEK_SET,MYF(0)));
|
|
if (my_read(file,(byte*) head,288,MYF(MY_NABP))) goto err_not_open;
|
|
#ifdef HAVE_CRYPTED_FRM
|
|
if (crypted)
|
|
{
|
|
crypted->decode((char*) head+256,288-256);
|
|
if (sint2korr(head+284) != 0) // Should be 0
|
|
goto err_not_open; // Wrong password
|
|
}
|
|
#endif
|
|
|
|
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);
|
|
com_length=uint2korr(head+284);
|
|
outparam->comment=strdup_root(&outparam->mem_root,
|
|
(char*) head+47);
|
|
|
|
DBUG_PRINT("info",("i_count: %d i_parts: %d index: %d n_length: %d int_length: %d com_length: %d", interval_count,interval_parts, outparam->keys,n_length,int_length, com_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+com_length)))))
|
|
goto err_not_open; /* purecov: inspected */
|
|
|
|
outparam->field=field_ptr;
|
|
read_length=(uint) (outparam->fields * field_pack_length +
|
|
pos+ (uint) (n_length+int_length+com_length));
|
|
if (read_string(file,(gptr*) &disk_buff,read_length))
|
|
goto err_not_open; /* purecov: inspected */
|
|
#ifdef HAVE_CRYPTED_FRM
|
|
if (crypted)
|
|
{
|
|
crypted->decode((char*) disk_buff,read_length);
|
|
delete crypted;
|
|
crypted=0;
|
|
}
|
|
#endif
|
|
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*field_pack_length),
|
|
(uint) (n_length+int_length));
|
|
comment_pos=names+(n_length+int_length);
|
|
memcpy(comment_pos, disk_buff+read_length-com_length, com_length);
|
|
|
|
fix_type_pointers(&int_array,&outparam->fieldnames,1,&names);
|
|
fix_type_pointers(&int_array,outparam->intervals,interval_count,
|
|
&names);
|
|
|
|
{
|
|
/* Set ENUM and SET lengths */
|
|
TYPELIB *interval;
|
|
for (interval= outparam->intervals;
|
|
interval < outparam->intervals + interval_count;
|
|
interval++)
|
|
{
|
|
uint count= (uint) (interval->count + 1) * sizeof(uint);
|
|
if (!(interval->type_lengths= (uint *) alloc_root(&outparam->mem_root,
|
|
count)))
|
|
goto err_not_open;
|
|
for (count= 0; count < interval->count; count++)
|
|
interval->type_lengths[count]= strlen(interval->type_names[count]);
|
|
interval->type_lengths[count]= 0;
|
|
}
|
|
}
|
|
|
|
if (keynames)
|
|
fix_type_pointers(&int_array,&outparam->keynames,1,&keynames);
|
|
VOID(my_close(file,MYF(MY_WME)));
|
|
file= -1;
|
|
|
|
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,
|
|
system_charset_info,
|
|
outparam->fields,0,0,
|
|
(hash_get_key) get_field_name,0,0);
|
|
|
|
for (i=0 ; i < outparam->fields; i++, strpos+=field_pack_length, field_ptr++)
|
|
{
|
|
uint pack_flag, interval_nr, unireg_type, recpos, field_length;
|
|
enum_field_types field_type;
|
|
CHARSET_INFO *charset=NULL;
|
|
Field::geometry_type geom_type= Field::GEOM_GEOMETRY;
|
|
LEX_STRING comment;
|
|
|
|
if (new_frm_ver == 3)
|
|
{
|
|
/* new frm file in 4.1 */
|
|
field_length= uint2korr(strpos+3);
|
|
recpos= uint3korr(strpos+5);
|
|
pack_flag= uint2korr(strpos+8);
|
|
unireg_type= (uint) strpos[10];
|
|
interval_nr= (uint) strpos[12];
|
|
|
|
uint comment_length=uint2korr(strpos+15);
|
|
field_type=(enum_field_types) (uint) strpos[13];
|
|
|
|
// charset and geometry_type share the same byte in frm
|
|
if (field_type == FIELD_TYPE_GEOMETRY)
|
|
{
|
|
#ifdef HAVE_SPATIAL
|
|
geom_type= (Field::geometry_type) strpos[14];
|
|
charset= &my_charset_bin;
|
|
#else
|
|
error= 4; // unsupported field type
|
|
goto err_not_open;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
if (!strpos[14])
|
|
charset= &my_charset_bin;
|
|
else if (!(charset=get_charset((uint) strpos[14], MYF(0))))
|
|
{
|
|
error= 5; // Unknown or unavailable charset
|
|
errarg= (int) strpos[14];
|
|
goto err_not_open;
|
|
}
|
|
}
|
|
if (!comment_length)
|
|
{
|
|
comment.str= (char*) "";
|
|
comment.length=0;
|
|
}
|
|
else
|
|
{
|
|
comment.str= (char*) comment_pos;
|
|
comment.length= comment_length;
|
|
comment_pos+= comment_length;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
field_length= (uint) strpos[3];
|
|
recpos= uint2korr(strpos+4),
|
|
pack_flag= uint2korr(strpos+6);
|
|
unireg_type= (uint) strpos[8];
|
|
interval_nr= (uint) strpos[10];
|
|
|
|
/* old frm file */
|
|
field_type= (enum_field_types) f_packtype(pack_flag);
|
|
if (f_is_binary(pack_flag))
|
|
{
|
|
/*
|
|
Try to choose the best 4.1 type:
|
|
- for 4.0 "CHAR(N) BINARY" or "VARCHAR(N) BINARY"
|
|
try to find a binary collation for character set.
|
|
- for other types (e.g. BLOB) just use my_charset_bin.
|
|
*/
|
|
if (!f_is_blob(pack_flag))
|
|
{
|
|
// 3.23 or 4.0 string
|
|
if (!(charset= get_charset_by_csname(outparam->table_charset->csname,
|
|
MY_CS_BINSORT, MYF(0))))
|
|
charset= &my_charset_bin;
|
|
}
|
|
else
|
|
charset= &my_charset_bin;
|
|
}
|
|
else
|
|
charset= outparam->table_charset;
|
|
bzero((char*) &comment, sizeof(comment));
|
|
}
|
|
|
|
if (interval_nr && charset->mbminlen > 1)
|
|
{
|
|
/* Unescape UCS2 intervals from HEX notation */
|
|
TYPELIB *interval= outparam->intervals + interval_nr - 1;
|
|
unhex_type2(interval);
|
|
}
|
|
|
|
*field_ptr=reg_field=
|
|
make_field(record+recpos,
|
|
(uint32) field_length,
|
|
null_pos,null_bit,
|
|
pack_flag,
|
|
field_type,
|
|
charset,
|
|
geom_type,
|
|
(Field::utype) MTYP_TYPENR(unireg_type),
|
|
(interval_nr ?
|
|
outparam->intervals+interval_nr-1 :
|
|
(TYPELIB*) 0),
|
|
outparam->fieldnames.type_names[i],
|
|
outparam);
|
|
if (!reg_field) // Not supported field type
|
|
{
|
|
error= 4;
|
|
goto err_not_open; /* purecov: inspected */
|
|
}
|
|
reg_field->comment=comment;
|
|
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)
|
|
outparam->found_next_number_field= reg_field;
|
|
if (outparam->timestamp_field == reg_field)
|
|
outparam->timestamp_field_offset=i;
|
|
if (use_hash)
|
|
(void) my_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_key_name,
|
|
&outparam->keynames, 3) - 1);
|
|
uint ha_option=outparam->file->table_flags();
|
|
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];
|
|
/* Fix fulltext keys for old .frm files */
|
|
if (outparam->key_info[key].flags & HA_FULLTEXT)
|
|
outparam->key_info[key].algorithm= HA_KEY_ALG_FULLTEXT;
|
|
|
|
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_not_open; // 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;
|
|
keyinfo->key_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;
|
|
keyinfo->key_length+= HA_KEY_BLOB_LENGTH;
|
|
/*
|
|
Mark that there may be many matching values for one key
|
|
combination ('a', 'a ', 'a '...)
|
|
*/
|
|
if (!(field->flags & BINARY_FLAG))
|
|
keyinfo->flags|= HA_END_SPACE_KEY;
|
|
}
|
|
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.set_bit(key);
|
|
if (field->key_length() == key_part->length &&
|
|
!(field->flags & BLOB_FLAG))
|
|
{
|
|
if (outparam->file->index_flags(key, i, 0) & HA_KEYREAD_ONLY)
|
|
{
|
|
outparam->read_only_keys.clear_bit(key);
|
|
outparam->keys_for_keyread.set_bit(key);
|
|
field->part_of_key.set_bit(key);
|
|
}
|
|
if (outparam->file->index_flags(key, i, 1) & HA_READ_ORDER)
|
|
field->part_of_sortkey.set_bit(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 this field is part of the primary key and all keys contains
|
|
the primary key, then we can use any key to find this column
|
|
*/
|
|
if (ha_option & HA_PRIMARY_KEY_IN_READ_INDEX)
|
|
field->part_of_key= outparam->keys_in_use;
|
|
}
|
|
if (field->key_length() != key_part->length)
|
|
{
|
|
key_part->key_part_flag|= HA_PART_KEY_SEG;
|
|
if (!(field->flags & BLOB_FLAG))
|
|
{ // Create a new field
|
|
field=key_part->field=field->new_field(&outparam->mem_root,
|
|
outparam);
|
|
field->field_length=key_part->length;
|
|
}
|
|
}
|
|
/*
|
|
If the field can be NULL, don't optimize away the test
|
|
key_part_column = expression from the WHERE clause
|
|
as we need to test for NULL = NULL.
|
|
*/
|
|
if (field->real_maybe_null())
|
|
key_part->key_part_flag|= HA_PART_KEY_SEG;
|
|
}
|
|
else
|
|
{ // Error: shorten key
|
|
keyinfo->key_parts=usable_parts;
|
|
keyinfo->flags=0;
|
|
}
|
|
}
|
|
keyinfo->usable_key_parts=usable_parts; // Filesort
|
|
|
|
set_if_bigger(outparam->max_key_length,keyinfo->key_length+
|
|
keyinfo->key_parts);
|
|
outparam->total_key_length+= keyinfo->key_length;
|
|
/*
|
|
MERGE tables do not have unique indexes. But every key could be
|
|
an unique index on the underlying MyISAM table. (Bug #10400)
|
|
*/
|
|
if ((keyinfo->flags & HA_NOSAME) ||
|
|
(ha_option & HA_ANY_INDEX_MAY_BE_UNIQUE))
|
|
set_if_bigger(outparam->max_unique_length,keyinfo->key_length);
|
|
}
|
|
if (primary_key < MAX_KEY &&
|
|
(outparam->keys_in_use.is_set(primary_key)))
|
|
{
|
|
outparam->primary_key=primary_key;
|
|
/*
|
|
If we are using an integer as the primary key then allow the user to
|
|
refer to it as '_rowid'
|
|
*/
|
|
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
|
|
}
|
|
else
|
|
outparam->primary_key= MAX_KEY;
|
|
x_free((gptr) disk_buff);
|
|
disk_buff=0;
|
|
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 ((reg_field=outparam->found_next_number_field))
|
|
{
|
|
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 */
|
|
outparam->found_next_number_field=0;
|
|
}
|
|
else
|
|
reg_field->flags|=AUTO_INCREMENT_FLAG;
|
|
}
|
|
|
|
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_not_open;
|
|
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
|
|
|
|
/* The table struct is now initialzed; Open the table */
|
|
error=2;
|
|
if (db_stat)
|
|
{
|
|
int err;
|
|
unpack_filename(index_file,index_file);
|
|
if ((err=(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_flags))))
|
|
{
|
|
/* Set a flag if the table is crashed and it can be auto. repaired */
|
|
outparam->crashed=((err == HA_ERR_CRASHED_ON_USAGE) &&
|
|
outparam->file->auto_repair() &&
|
|
!(ha_open_flags & HA_OPEN_FOR_REPAIR));
|
|
|
|
if (err==HA_ERR_NO_SUCH_TABLE)
|
|
{
|
|
/* The table did not exists in storage engine, use same error message
|
|
as if the .frm file didn't exist */
|
|
error= 1;
|
|
my_errno= ENOENT;
|
|
}
|
|
goto err_not_open; /* purecov: inspected */
|
|
}
|
|
}
|
|
outparam->db_low_byte_first=outparam->file->low_byte_first();
|
|
|
|
*root_ptr= old_root;
|
|
opened_tables++;
|
|
#ifndef DBUG_OFF
|
|
if (use_hash)
|
|
(void) hash_check(&outparam->name_hash);
|
|
#endif
|
|
DBUG_RETURN (0);
|
|
|
|
err_not_open:
|
|
x_free((gptr) disk_buff);
|
|
if (file > 0)
|
|
VOID(my_close(file,MYF(MY_WME)));
|
|
|
|
err_end: /* Here when no file */
|
|
delete crypted;
|
|
*root_ptr= old_root;
|
|
frm_error(error, outparam, name, ME_ERROR + ME_WAITTANG, errarg);
|
|
delete outparam->file;
|
|
outparam->file=0; // For easyer errorchecking
|
|
outparam->db_stat=0;
|
|
hash_free(&outparam->name_hash);
|
|
free_root(&outparam->mem_root,MYF(0));
|
|
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,MYF(0));
|
|
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=(uint) 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, 0, 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 errarg)
|
|
{
|
|
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() : "";
|
|
datext= datext==NullS ? "" : datext;
|
|
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;
|
|
}
|
|
case 5:
|
|
{
|
|
const char *csname= get_charset_name((uint) errarg);
|
|
char tmp[10];
|
|
if (!csname || csname[0] =='?')
|
|
{
|
|
my_snprintf(tmp, sizeof(tmp), "#%d", errarg);
|
|
csname= tmp;
|
|
}
|
|
my_printf_error(ER_UNKNOWN_COLLATION,
|
|
"Unknown collation '%s' in table '%-.64s' definition",
|
|
MYF(0), csname, form->real_name);
|
|
break;
|
|
}
|
|
case 6:
|
|
my_printf_error(ER_NOT_FORM_FILE,
|
|
"Table '%-.64s' was created with a different version "
|
|
"of MySQL and cannot be read",
|
|
MYF(0), name);
|
|
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; /* Skip 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(MEM_ROOT *mem_root, List<String> &strings)
|
|
{
|
|
TYPELIB *result= (TYPELIB*) alloc_root(mem_root, sizeof(TYPELIB));
|
|
if (!result)
|
|
return 0;
|
|
result->count=strings.elements;
|
|
result->name="";
|
|
uint nbytes= (sizeof(char*) + sizeof(uint)) * (result->count + 1);
|
|
if (!(result->type_names= (const char**) alloc_root(mem_root, nbytes)))
|
|
return 0;
|
|
result->type_lengths= (uint*) (result->type_names + result->count + 1);
|
|
List_iterator<String> it(strings);
|
|
String *tmp;
|
|
for (uint i=0; (tmp=it++) ; i++)
|
|
{
|
|
result->type_names[i]= tmp->ptr();
|
|
result->type_lengths[i]= tmp->length();
|
|
}
|
|
result->type_names[result->count]= 0; // End marker
|
|
result->type_lengths[result->count]= 0;
|
|
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 */
|
|
|
|
|
|
/*
|
|
Store an SQL quoted string.
|
|
|
|
SYNOPSIS
|
|
append_unescaped()
|
|
res result String
|
|
pos string to be quoted
|
|
length it's length
|
|
|
|
NOTE
|
|
This function works correctly with utf8 or single-byte charset strings.
|
|
May fail with some multibyte charsets though.
|
|
*/
|
|
|
|
void append_unescaped(String *res, const char *pos, uint length)
|
|
{
|
|
const char *end= pos+length;
|
|
res->append('\'');
|
|
|
|
for (; pos != end ; pos++)
|
|
{
|
|
#if defined(USE_MB) && MYSQL_VERSION_ID < 40100
|
|
uint mblen;
|
|
if (use_mb(default_charset_info) &&
|
|
(mblen= my_ismbchar(default_charset_info, pos, end)))
|
|
{
|
|
res->append(pos, mblen);
|
|
pos+= mblen;
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
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;
|
|
}
|
|
}
|
|
res->append('\'');
|
|
}
|
|
|
|
/* Create a .frm file */
|
|
|
|
File create_frm(register my_string name, const char *db, const char *table,
|
|
uint reclength, uchar *fileinfo,
|
|
HA_CREATE_INFO *create_info, uint keys)
|
|
{
|
|
register File file;
|
|
uint key_length;
|
|
ulong length;
|
|
char fill[IO_SIZE];
|
|
int create_flags= O_RDWR | O_TRUNC;
|
|
|
|
if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
|
|
create_flags|= O_EXCL | O_NOFOLLOW;
|
|
|
|
#if SIZEOF_OFF_T > 4
|
|
/* Fix this when we have new .frm files; 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
|
|
/*
|
|
Ensure that raid_chunks can't be larger than 255, as this would cause
|
|
problems with drop database
|
|
*/
|
|
set_if_smaller(create_info->raid_chunks, 255);
|
|
|
|
if ((file= my_create(name, CREATE_MODE, create_flags, MYF(0))) >= 0)
|
|
{
|
|
bzero((char*) fileinfo,64);
|
|
fileinfo[0]=(uchar) 254; fileinfo[1]= 1; fileinfo[2]= FRM_VER+3; // 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);
|
|
if (key_length > 0xffff) key_length=0xffff;
|
|
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[38]= (create_info->default_table_charset ?
|
|
create_info->default_table_charset->number : 0);
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (my_errno == ENOENT)
|
|
my_error(ER_BAD_DB_ERROR,MYF(0),db);
|
|
else
|
|
my_error(ER_CANT_CREATE_TABLE,MYF(0),table,my_errno);
|
|
}
|
|
return (file);
|
|
} /* create_frm */
|
|
|
|
|
|
void update_create_info_from_table(HA_CREATE_INFO *create_info, TABLE *table)
|
|
{
|
|
DBUG_ENTER("update_create_info_from_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;
|
|
create_info->default_table_charset=table->table_charset;
|
|
create_info->table_charset= 0;
|
|
DBUG_VOID_RETURN;
|
|
}
|
|
|
|
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)));
|
|
}
|
|
|
|
|
|
/*
|
|
Allocate string field in MEM_ROOT and return it as String
|
|
|
|
SYNOPSIS
|
|
get_field()
|
|
mem MEM_ROOT for allocating
|
|
field Field for retrieving of string
|
|
res result String
|
|
|
|
RETURN VALUES
|
|
1 string is empty
|
|
0 all ok
|
|
*/
|
|
|
|
bool get_field(MEM_ROOT *mem, Field *field, String *res)
|
|
{
|
|
char buff[MAX_FIELD_WIDTH], *to;
|
|
String str(buff,sizeof(buff),&my_charset_bin);
|
|
uint length;
|
|
|
|
field->val_str(&str);
|
|
if (!(length= str.length()))
|
|
return 1;
|
|
to= strmake_root(mem, str.ptr(), length);
|
|
res->set(to, length, ((Field_str*)field)->charset());
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
Allocate string field in MEM_ROOT and return it as NULL-terminated string
|
|
|
|
SYNOPSIS
|
|
get_field()
|
|
mem MEM_ROOT for allocating
|
|
field Field for retrieving of string
|
|
|
|
RETURN VALUES
|
|
NullS string is empty
|
|
# pointer to NULL-terminated string value of field
|
|
*/
|
|
|
|
char *get_field(MEM_ROOT *mem, Field *field)
|
|
{
|
|
char buff[MAX_FIELD_WIDTH], *to;
|
|
String str(buff,sizeof(buff),&my_charset_bin);
|
|
uint length;
|
|
|
|
field->val_str(&str);
|
|
length= str.length();
|
|
if (!length || !(to= (char*) alloc_root(mem,length+1)))
|
|
return NullS;
|
|
memcpy(to,str.ptr(),(uint) length);
|
|
to[length]=0;
|
|
return to;
|
|
}
|
|
|
|
|
|
/*
|
|
Check if database name is valid
|
|
|
|
SYNPOSIS
|
|
check_db_name()
|
|
name Name of database
|
|
|
|
NOTES
|
|
If lower_case_table_names is set then database is converted to lower case
|
|
|
|
RETURN
|
|
0 ok
|
|
1 error
|
|
*/
|
|
|
|
bool check_db_name(char *name)
|
|
{
|
|
char *start=name;
|
|
/* Used to catch empty names and names with end space */
|
|
bool last_char_is_space= TRUE;
|
|
|
|
if (lower_case_table_names && name != any_db)
|
|
my_casedn_str(files_charset_info, name);
|
|
|
|
while (*name)
|
|
{
|
|
#if defined(USE_MB) && defined(USE_MB_IDENT)
|
|
last_char_is_space= my_isspace(system_charset_info, *name);
|
|
if (use_mb(system_charset_info))
|
|
{
|
|
int len=my_ismbchar(system_charset_info, name,
|
|
name+system_charset_info->mbmaxlen);
|
|
if (len)
|
|
{
|
|
name += len;
|
|
continue;
|
|
}
|
|
}
|
|
#else
|
|
last_char_is_space= *name==' ';
|
|
#endif
|
|
if (*name == '/' || *name == '\\' || *name == FN_LIBCHAR ||
|
|
*name == FN_EXTCHAR)
|
|
return 1;
|
|
name++;
|
|
}
|
|
return last_char_is_space || (uint) (name - start) > NAME_LEN;
|
|
}
|
|
|
|
|
|
/*
|
|
Allow anything as a table name, as long as it doesn't contain an
|
|
a '/', or a '.' character
|
|
or ' ' at the end
|
|
returns 1 on error
|
|
*/
|
|
|
|
|
|
bool check_table_name(const char *name, uint length)
|
|
{
|
|
const char *end= name+length;
|
|
if (!length || length > NAME_LEN)
|
|
return 1;
|
|
#if defined(USE_MB) && defined(USE_MB_IDENT)
|
|
bool last_char_is_space= FALSE;
|
|
#else
|
|
if (name[length-1]==' ')
|
|
return 1;
|
|
#endif
|
|
|
|
while (name != end)
|
|
{
|
|
#if defined(USE_MB) && defined(USE_MB_IDENT)
|
|
last_char_is_space= my_isspace(system_charset_info, *name);
|
|
if (use_mb(system_charset_info))
|
|
{
|
|
int len=my_ismbchar(system_charset_info, name, end);
|
|
if (len)
|
|
{
|
|
name += len;
|
|
continue;
|
|
}
|
|
}
|
|
#endif
|
|
if (*name == '/' || *name == '\\' || *name == FN_EXTCHAR)
|
|
return 1;
|
|
name++;
|
|
}
|
|
#if defined(USE_MB) && defined(USE_MB_IDENT)
|
|
return last_char_is_space;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
|
|
bool check_column_name(const char *name)
|
|
{
|
|
const char *start= name;
|
|
bool last_char_is_space= TRUE;
|
|
|
|
while (*name)
|
|
{
|
|
#if defined(USE_MB) && defined(USE_MB_IDENT)
|
|
last_char_is_space= my_isspace(system_charset_info, *name);
|
|
if (use_mb(system_charset_info))
|
|
{
|
|
int len=my_ismbchar(system_charset_info, name,
|
|
name+system_charset_info->mbmaxlen);
|
|
if (len)
|
|
{
|
|
name += len;
|
|
continue;
|
|
}
|
|
}
|
|
#else
|
|
last_char_is_space= *name==' ';
|
|
#endif
|
|
if (*name == NAMES_SEP_CHAR)
|
|
return 1;
|
|
name++;
|
|
}
|
|
/* Error if empty or too long column name */
|
|
return last_char_is_space || (uint) (name - start) > NAME_LEN;
|
|
}
|
|
|
|
/*
|
|
** 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 && head[2] != FRM_VER+3))
|
|
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
|