mariadb/sql/table.cc

3263 lines
93 KiB
C++
Raw Normal View History

/* Copyright (C) 2000-2006 MySQL AB
2000-07-31 21:29:14 +02:00
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
2000-07-31 21:29:14 +02:00
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.
2000-07-31 21:29:14 +02:00
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>
2004-07-16 00:15:55 +02:00
#include "md5.h"
2000-07-31 21:29:14 +02:00
/* Functions defined in this file */
static void frm_error(int error,TABLE *form,const char *name,
int errortype, int errarg);
2000-07-31 21:29:14 +02:00
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,
2000-07-31 21:29:14 +02:00
my_bool not_used __attribute__((unused)))
{
*length= (uint) strlen((*buff)->field_name);
return (byte*) (*buff)->field_name;
2000-07-31 21:29:14 +02:00
}
/*
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
*/
2000-07-31 21:29:14 +02:00
int openfrm(THD *thd, const char *name, const char *alias, uint db_stat,
uint prgflag, uint ha_open_flags, TABLE *outparam)
2000-07-31 21:29:14 +02:00
{
reg1 uint i;
reg2 uchar *strpos;
int j,error, errarg= 0;
2000-07-31 21:29:14 +02:00
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, record_offset;
char index_file[FN_REFLEN], *names, *keynames, *comment_pos;
2000-07-31 21:29:14 +02:00
uchar head[288],*disk_buff,new_field_pack_flag;
my_string record;
const char **int_array;
bool use_hash, null_field_first;
bool error_reported= FALSE;
2000-07-31 21:29:14 +02:00
File file;
Field **field_ptr,*reg_field;
KEY *keyinfo;
KEY_PART_INFO *key_part;
uchar *null_pos;
2004-12-17 15:06:05 +01:00
uint null_bit_pos, new_frm_ver, field_pack_length;
2000-07-31 21:29:14 +02:00
SQL_CRYPT *crypted=0;
MEM_ROOT **root_ptr, *old_root;
TABLE_SHARE *share;
2000-07-31 21:29:14 +02:00
DBUG_ENTER("openfrm");
DBUG_PRINT("enter",("name: '%s' form: 0x%lx", name, (long) outparam));
2000-07-31 21:29:14 +02:00
error= 1;
disk_buff= NULL;
root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**, THR_MALLOC);
old_root= *root_ptr;
2004-07-16 00:15:55 +02:00
bzero((char*) outparam,sizeof(*outparam));
outparam->in_use= thd;
outparam->s= share= &outparam->share_not_to_be_used;
2004-07-16 00:15:55 +02:00
if ((file=my_open(fn_format(index_file, name, "", reg_ext,
MY_UNPACK_FILENAME),
O_RDONLY | O_SHARE,
MYF(0)))
< 0)
goto err;
2004-07-16 00:15:55 +02:00
error= 4;
2004-07-16 00:15:55 +02:00
if (my_read(file,(byte*) head,64,MYF(MY_NABP)))
goto err;
2004-07-16 00:15:55 +02:00
if (memcmp(head, STRING_WITH_LEN("TYPE=")) == 0)
2004-07-16 00:15:55 +02:00
{
// new .frm
my_close(file,MYF(MY_WME));
if (db_stat & NO_ERR_ON_NEW_FRM)
DBUG_RETURN(5);
file= -1;
2004-07-16 00:15:55 +02:00
// caller can't process new .frm
goto err;
2004-07-16 00:15:55 +02:00
}
if (prgflag & OPEN_VIEW_NO_PARSE)
goto err;
2004-07-16 00:15:55 +02:00
share->blob_ptr_size= sizeof(char*);
outparam->db_stat= db_stat;
init_sql_alloc(&outparam->mem_root, TABLE_ALLOC_BLOCK_SIZE, 0);
*root_ptr= &outparam->mem_root;
2000-07-31 21:29:14 +02:00
share->table_name= strdup_root(&outparam->mem_root,
name+dirname_length(name));
share->path= strdup_root(&outparam->mem_root, name);
outparam->alias= my_strdup(alias, MYF(MY_WME));
if (!share->table_name || !share->path || !outparam->alias)
goto err;
*fn_ext(share->table_name)='\0'; // Remove extension
*fn_ext(share->path)='\0'; // Remove extension
2000-07-31 21:29:14 +02:00
if (head[0] != (uchar) 254 || head[1] != 1)
goto err; /* purecov: inspected */
2005-05-09 20:28:57 +02:00
if (head[2] != FRM_VER && head[2] != FRM_VER+1 &&
! (head[2] >= FRM_VER+3 && head[2] <= FRM_VER+4))
{
error= 6;
2005-05-09 20:28:57 +02:00
goto err; /* purecov: inspected */
}
2000-07-31 21:29:14 +02:00
new_field_pack_flag=head[27];
new_frm_ver= (head[2] - FRM_VER);
field_pack_length= new_frm_ver < 2 ? 11 : 17;
2000-07-31 21:29:14 +02:00
error=3;
if (!(pos=get_form_pos(file,head,(TYPELIB*) 0)))
goto err; /* purecov: inspected */
2000-07-31 21:29:14 +02:00
*fn_ext(index_file)='\0'; // Remove .frm extension
share->frm_version= head[2];
/*
Check if .frm file created by MySQL 5.0. In this case we want to
display CHAR fields as CHAR and not as VARCHAR.
We do it this way as we want to keep the old frm version to enable
MySQL 4.1 to read these files.
*/
if (share->frm_version == FRM_VER_TRUE_VARCHAR -1 && head[33] == 5)
share->frm_version= FRM_VER_TRUE_VARCHAR;
share->db_type= ha_checktype(thd,(enum db_type) (uint) *(head+3),0,0);
share->db_create_options= db_create_options=uint2korr(head+30);
share->db_options_in_use= share->db_create_options;
share->mysql_version= uint4korr(head+51);
null_field_first= 0;
2000-07-31 21:29:14 +02:00
if (!head[32]) // New frm file in 3.23
{
share->avg_row_length= uint4korr(head+34);
share-> row_type= (row_type) head[40];
share->raid_type= head[41];
share->raid_chunks= head[42];
share->raid_chunksize= uint4korr(head+43);
share->table_charset= get_charset((uint) head[38],MYF(0));
null_field_first= 1;
}
if (!share->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);
}
share->table_charset= default_charset_info;
2000-07-31 21:29:14 +02:00
}
share->db_record_offset= 1;
2000-07-31 21:29:14 +02:00
if (db_create_options & HA_OPTION_LONG_BLOB_PTR)
share->blob_ptr_size= portable_sizeof_char_ptr;
/* Set temporarily a good value for db_low_byte_first */
share->db_low_byte_first= test(share->db_type != DB_TYPE_ISAM);
2000-07-31 21:29:14 +02:00
error=4;
share->max_rows= uint4korr(head+18);
share->min_rows= uint4korr(head+22);
2000-07-31 21:29:14 +02:00
/* Read keyinformation */
key_info_length= (uint) uint2korr(head+28);
2000-07-31 21:29:14 +02:00
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; /* purecov: inspected */
2003-10-30 19:17:57 +01:00
if (disk_buff[0] & 0x80)
2003-10-21 00:13:17 +02:00
{
share->keys= keys= (disk_buff[1] << 7) | (disk_buff[0] & 0x7f);
share->key_parts= key_parts= uint2korr(disk_buff+2);
2003-10-21 00:13:17 +02:00
}
else
{
share->keys= keys= disk_buff[0];
share->key_parts= key_parts= disk_buff[1];
2003-10-21 00:13:17 +02:00
}
share->keys_for_keyread.init(0);
share->keys_in_use.init(keys);
2003-10-11 13:06:55 +02:00
outparam->quick_keys.init();
outparam->used_keys.init();
outparam->keys_in_use_for_query.init();
2000-07-31 21:29:14 +02:00
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; /* purecov: inspected */
2000-07-31 21:29:14 +02:00
bzero((char*) keyinfo,n_length);
outparam->key_info=keyinfo;
key_part= my_reinterpret_cast(KEY_PART_INFO*) (keyinfo+keys);
2000-07-31 21:29:14 +02:00
strpos=disk_buff+6;
ulong *rec_per_key;
if (!(rec_per_key= (ulong*) alloc_root(&outparam->mem_root,
sizeof(ulong*)*key_parts)))
goto err;
2000-07-31 21:29:14 +02:00
for (i=0 ; i < keys ; i++, keyinfo++)
{
keyinfo->table= outparam;
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;
}
2002-02-22 12:24:42 +01:00
2000-07-31 21:29:14 +02:00
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)
2000-07-31 21:29:14 +02:00
{
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;
share->reclength = uint2korr((head+16));
2000-07-31 21:29:14 +02:00
if (*(head+26) == 1)
share->system= 1; /* one-record-database */
2000-07-31 21:29:14 +02:00
#ifdef HAVE_CRYPTED_FRM
else if (*(head+26) == 2)
{
*root_ptr= old_root
2000-07-31 21:29:14 +02:00
crypted=get_crypt_for_frm();
*root_ptr= &outparam->mem_root;
2000-07-31 21:29:14 +02:00
outparam->crypted=1;
}
#endif
record_offset= (ulong) (uint2korr(head+6)+
((uint2korr(head+14) == 0xffff ?
uint4korr(head+47) : uint2korr(head+14))));
if ((n_length= uint2korr(head+55)))
{
/* Read extra data segment */
char *buff, *next_chunk, *buff_end;
if (!(next_chunk= buff= my_malloc(n_length, MYF(MY_WME))))
goto err;
2005-10-13 18:40:46 +02:00
buff_end= buff + n_length;
if (my_pread(file, (byte*)buff, n_length, record_offset + share->reclength,
MYF(MY_NABP)))
{
my_free(buff, MYF(0));
goto err;
}
2005-10-10 20:53:53 +02:00
share->connect_string.length= uint2korr(buff);
if (! (share->connect_string.str= strmake_root(&outparam->mem_root,
next_chunk + 2, share->connect_string.length)))
{
2005-10-10 20:53:53 +02:00
my_free(buff, MYF(0));
goto err;
}
2005-10-10 20:53:53 +02:00
next_chunk+= share->connect_string.length + 2;
if (next_chunk + 2 < buff_end)
{
uint str_db_type_length= uint2korr(next_chunk);
share->db_type= ha_resolve_by_name(next_chunk + 2, str_db_type_length);
2005-10-13 18:40:46 +02:00
DBUG_PRINT("enter", ("Setting dbtype to: %d - %d - '%.*s'\n",
share->db_type,
str_db_type_length, str_db_type_length,
next_chunk + 2));
next_chunk+= str_db_type_length + 2;
}
my_free(buff, MYF(0));
}
2000-10-16 01:29:48 +02:00
/* Allocate handler */
if (!(outparam->file= get_new_handler(outparam, &outparam->mem_root,
share->db_type)))
goto err;
2000-07-31 21:29:14 +02:00
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++;
2000-10-16 01:29:48 +02:00
/* QQ: TODO, remove the +1 from below */
rec_buff_length= ALIGN_SIZE(share->reclength + 1 +
outparam->file->extra_rec_buf_length());
share->rec_buff_length= rec_buff_length;
if (!(record= (char *) alloc_root(&outparam->mem_root,
rec_buff_length * records)))
goto err; /* purecov: inspected */
share->default_values= (byte *) record;
if (my_pread(file,(byte*) record, (uint) share->reclength,
record_offset, MYF(MY_NABP)))
goto err; /* purecov: inspected */
2000-07-31 21:29:14 +02:00
if (records == 1)
{
/* We are probably in hard repair, and the buffers should not be used */
outparam->record[0]= outparam->record[1]= share->default_values;
}
else
{
outparam->record[0]= (byte *) record+ rec_buff_length;
if (records > 2)
outparam->record[1]= (byte *) record+ rec_buff_length*2;
else
outparam->record[1]= outparam->record[0]; // Safety
}
#ifdef HAVE_purify
/*
We need this because when we read var-length rows, we are not updating
bytes after end of varchar
*/
if (records > 1)
{
memcpy(outparam->record[0], share->default_values, rec_buff_length);
if (records > 2)
memcpy(outparam->record[1], share->default_values, rec_buff_length);
}
#endif
2000-07-31 21:29:14 +02:00
VOID(my_seek(file,pos,MY_SEEK_SET,MYF(0)));
if (my_read(file,(byte*) head,288,MYF(MY_NABP)))
goto err;
#ifdef HAVE_CRYPTED_FRM
2000-07-31 21:29:14 +02:00
if (crypted)
{
crypted->decode((char*) head+256,288-256);
if (sint2korr(head+284) != 0) // Should be 0
goto err; // Wrong password
2000-07-31 21:29:14 +02:00
}
#endif
2000-07-31 21:29:14 +02:00
share->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);
share->null_fields= uint2korr(head+282);
com_length= uint2korr(head+284);
share->comment.length= (int) (head[46]);
share->comment.str= strmake_root(&outparam->mem_root, (char*) head+47,
share->comment.length);
2000-07-31 21:29:14 +02:00
DBUG_PRINT("info",("i_count: %d i_parts: %d index: %d n_length: %d int_length: %d com_length: %d", interval_count,interval_parts, share->keys,n_length,int_length, com_length));
2000-07-31 21:29:14 +02:00
if (!(field_ptr = (Field **)
alloc_root(&outparam->mem_root,
(uint) ((share->fields+1)*sizeof(Field*)+
2000-07-31 21:29:14 +02:00
interval_count*sizeof(TYPELIB)+
(share->fields+interval_parts+
2000-07-31 21:29:14 +02:00
keys+3)*sizeof(my_string)+
(n_length+int_length+com_length)))))
goto err; /* purecov: inspected */
2000-07-31 21:29:14 +02:00
outparam->field=field_ptr;
read_length=(uint) (share->fields * field_pack_length +
pos+ (uint) (n_length+int_length+com_length));
2000-07-31 21:29:14 +02:00
if (read_string(file,(gptr*) &disk_buff,read_length))
goto err; /* purecov: inspected */
#ifdef HAVE_CRYPTED_FRM
2000-07-31 21:29:14 +02:00
if (crypted)
{
crypted->decode((char*) disk_buff,read_length);
delete crypted;
crypted=0;
}
#endif
2000-07-31 21:29:14 +02:00
strpos= disk_buff+pos;
share->intervals= (TYPELIB*) (field_ptr+share->fields+1);
int_array= (const char **) (share->intervals+interval_count);
names= (char*) (int_array+share->fields+interval_parts+keys+3);
2000-07-31 21:29:14 +02:00
if (!interval_count)
share->intervals= 0; // For better debugging
memcpy((char*) names, strpos+(share->fields*field_pack_length),
2000-07-31 21:29:14 +02:00
(uint) (n_length+int_length));
comment_pos= names+(n_length+int_length);
memcpy(comment_pos, disk_buff+read_length-com_length, com_length);
2000-07-31 21:29:14 +02:00
fix_type_pointers(&int_array, &share->fieldnames, 1, &names);
if (share->fieldnames.count != share->fields)
goto err;
fix_type_pointers(&int_array, share->intervals, interval_count,
2000-07-31 21:29:14 +02:00
&names);
{
/* Set ENUM and SET lengths */
TYPELIB *interval;
for (interval= share->intervals;
interval < share->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;
for (count= 0; count < interval->count; count++)
{
char *val= (char*) interval->type_names[count];
interval->type_lengths[count]= strlen(val);
}
interval->type_lengths[count]= 0;
}
}
2000-07-31 21:29:14 +02:00
if (keynames)
fix_type_pointers(&int_array, &share->keynames, 1, &keynames);
2000-07-31 21:29:14 +02:00
VOID(my_close(file,MYF(MY_WME)));
2000-10-16 01:29:48 +02:00
file= -1;
2000-07-31 21:29:14 +02:00
record= (char*) outparam->record[0]-1; /* Fieldstart = 1 */
2000-07-31 21:29:14 +02:00
if (null_field_first)
{
outparam->null_flags=null_pos=(uchar*) record+1;
2004-12-17 15:06:05 +01:00
null_bit_pos= (db_create_options & HA_OPTION_PACK_RECORD) ? 0 : 1;
/*
null_bytes below is only correct under the condition that
there are no bit fields. Correct values is set below after the
table struct is initialized
*/
share->null_bytes= (share->null_fields + null_bit_pos + 7) / 8;
2000-07-31 21:29:14 +02:00
}
else
{
share->null_bytes= (share->null_fields+7)/8;
outparam->null_flags= null_pos=
(uchar*) (record+1+share->reclength-share->null_bytes);
2004-12-17 15:06:05 +01:00
null_bit_pos= 0;
2000-07-31 21:29:14 +02:00
}
use_hash= share->fields >= MAX_FIELDS_BEFORE_HASH;
2000-07-31 21:29:14 +02:00
if (use_hash)
use_hash= !hash_init(&share->name_hash,
2002-03-14 18:44:42 +01:00
system_charset_info,
share->fields,0,0,
(hash_get_key) get_field_name,0,0);
2000-07-31 21:29:14 +02:00
for (i=0 ; i < share->fields; i++, strpos+=field_pack_length, field_ptr++)
2000-07-31 21:29:14 +02:00
{
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;
2000-07-31 21:29:14 +02:00
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;
#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;
}
}
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);
pack_flag&= ~FIELDFLAG_NO_DEFAULT; // Safety for old files
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(share->table_charset->csname,
MY_CS_BINSORT, MYF(0))))
charset= &my_charset_bin;
}
else
charset= &my_charset_bin;
}
else
charset= share->table_charset;
bzero((char*) &comment, sizeof(comment));
}
if (interval_nr && charset->mbminlen > 1)
{
/* Unescape UCS2 intervals from HEX notation */
TYPELIB *interval= share->intervals + interval_nr - 1;
unhex_type2(interval);
}
#ifndef TO_BE_DELETED_ON_PRODUCTION
if (field_type == FIELD_TYPE_NEWDECIMAL && !share->mysql_version)
{
/*
Fix pack length of old decimal values from 5.0.3 -> 5.0.4
The difference is that in the old version we stored precision
in the .frm table while we now store the display_length
*/
uint decimals= f_decimals(pack_flag);
field_length= my_decimal_precision_to_length(field_length,
decimals,
f_is_dec(pack_flag) == 0);
sql_print_error("Found incompatible DECIMAL field '%s' in %s; Please do \"ALTER TABLE '%s' FORCE\" to fix it!", share->fieldnames.type_names[i], name, share->table_name);
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
ER_CRASHED_ON_USAGE,
"Found incompatible DECIMAL field '%s' in %s; Please do \"ALTER TABLE '%s' FORCE\" to fix it!", share->fieldnames.type_names[i], name, share->table_name);
share->crashed= 1; // Marker for CHECK TABLE
}
#endif
2000-07-31 21:29:14 +02:00
*field_ptr=reg_field=
make_field(record+recpos,
(uint32) field_length,
2004-12-17 15:06:05 +01:00
null_pos, null_bit_pos,
2000-07-31 21:29:14 +02:00
pack_flag,
field_type,
charset,
geom_type,
(Field::utype) MTYP_TYPENR(unireg_type),
2000-07-31 21:29:14 +02:00
(interval_nr ?
share->intervals+interval_nr-1 :
2000-07-31 21:29:14 +02:00
(TYPELIB*) 0),
share->fieldnames.type_names[i],
2000-07-31 21:29:14 +02:00
outparam);
2002-11-21 14:56:48 +01:00
if (!reg_field) // Not supported field type
{
error= 4;
goto err; /* purecov: inspected */
}
reg_field->field_index= i;
reg_field->comment=comment;
if (field_type == FIELD_TYPE_BIT && !f_bit_as_char(pack_flag))
2000-07-31 21:29:14 +02:00
{
2004-12-17 15:06:05 +01:00
if ((null_bit_pos+= field_length & 7) > 7)
2000-07-31 21:29:14 +02:00
{
2004-12-17 15:06:05 +01:00
null_pos++;
null_bit_pos-= 8;
2000-07-31 21:29:14 +02:00
}
}
2004-12-17 15:06:05 +01:00
if (!(reg_field->flags & NOT_NULL_FLAG))
{
if (!(null_bit_pos= (null_bit_pos + 1) & 7))
null_pos++;
}
if (f_no_default(pack_flag))
reg_field->flags|= NO_DEFAULT_VALUE_FLAG;
2000-07-31 21:29:14 +02:00
if (reg_field->unireg_check == Field::NEXT_NUMBER)
outparam->found_next_number_field= reg_field;
2000-07-31 21:29:14 +02:00
if (outparam->timestamp_field == reg_field)
share->timestamp_field_offset= i;
2000-07-31 21:29:14 +02:00
if (use_hash)
(void) my_hash_insert(&share->name_hash,(byte*) field_ptr); // never fail
2000-07-31 21:29:14 +02:00
}
*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,
&share->keynames, 3) - 1);
uint ha_option=outparam->file->table_flags();
2000-07-31 21:29:14 +02:00
keyinfo=outparam->key_info;
key_part=keyinfo->key_part;
for (uint key=0 ; key < share->keys ; key++,keyinfo++)
2000-07-31 21:29:14 +02:00
{
uint usable_parts=0;
keyinfo->name=(char*) share->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;
2006-06-30 17:29:27 +02:00
if (primary_key >= MAX_KEY && (keyinfo->flags & HA_NOSAME))
{
/*
If the UNIQUE key doesn't have NULL columns and is not a part key
declare this as a primary key.
*/
primary_key=key;
for (i=0 ; i < keyinfo->key_parts ;i++)
{
uint fieldnr= key_part[i].fieldnr;
if (!fieldnr ||
outparam->field[fieldnr-1]->null_ptr ||
outparam->field[fieldnr-1]->key_length() !=
key_part[i].length)
{
primary_key=MAX_KEY; // Can't be used
break;
}
}
}
2000-07-31 21:29:14 +02:00
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 > share->fields)
goto err; // sanity check
2000-07-31 21:29:14 +02:00
#endif
if (key_part->fieldnr)
{ // Should always be true !
Field *field=key_part->field=outparam->field[key_part->fieldnr-1];
key_part->type= field->key_type();
2000-07-31 21:29:14 +02:00
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;
2000-07-31 21:29:14 +02:00
}
if (field->type() == FIELD_TYPE_BLOB ||
field->real_type() == MYSQL_TYPE_VARCHAR)
2000-07-31 21:29:14 +02:00
{
if (field->type() == FIELD_TYPE_BLOB)
key_part->key_part_flag|= HA_BLOB_PART;
else
key_part->key_part_flag|= HA_VAR_LENGTH_PART;
2000-07-31 21:29:14 +02:00
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;
2000-07-31 21:29:14 +02:00
}
if (field->type() == MYSQL_TYPE_BIT)
key_part->key_part_flag|= HA_BIT_PART;
2000-07-31 21:29:14 +02:00
if (i == 0 && key != primary_key)
field->flags |= ((keyinfo->flags & HA_NOSAME) &&
(keyinfo->key_parts == 1)) ?
UNIQUE_KEY_FLAG : MULTIPLE_KEY_FLAG;
2000-07-31 21:29:14 +02:00
if (i == 0)
2003-10-11 13:06:55 +02:00
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)
2004-06-30 10:40:15 +02:00
{
share->keys_for_keyread.set_bit(key);
2003-10-11 13:06:55 +02:00
field->part_of_key.set_bit(key);
2004-06-30 10:40:15 +02:00
}
if (outparam->file->index_flags(key, i, 1) & HA_READ_ORDER)
2003-10-11 13:06:55 +02:00
field->part_of_sortkey.set_bit(key);
}
2000-07-31 21:29:14 +02:00
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
*/
2000-07-31 21:29:14 +02:00
if (ha_option & HA_PRIMARY_KEY_IN_READ_INDEX)
field->part_of_key= share->keys_in_use;
2000-07-31 21:29:14 +02:00
}
if (field->key_length() != key_part->length)
{
#ifndef TO_BE_DELETED_ON_PRODUCTION
if (field->type() == FIELD_TYPE_NEWDECIMAL)
{
/*
Fix a fatal error in decimal key handling that causes crashes
on Innodb. We fix it by reducing the key length so that
InnoDB never gets a too big key when searching.
This allows the end user to do an ALTER TABLE to fix the
error.
*/
keyinfo->key_length-= (key_part->length - field->key_length());
key_part->store_length-= (uint16)(key_part->length -
field->key_length());
key_part->length= (uint16)field->key_length();
sql_print_error("Found wrong key definition in %s; Please do \"ALTER TABLE '%s' FORCE \" to fix it!", name, share->table_name);
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
ER_CRASHED_ON_USAGE,
"Found wrong key definition in %s; Please do \"ALTER TABLE '%s' FORCE\" to fix it!", name, share->table_name);
share->crashed= 1; // Marker for CHECK TABLE
goto to_be_deleted;
}
#endif
key_part->key_part_flag|= HA_PART_KEY_SEG;
if (!(field->flags & BLOB_FLAG))
2000-07-31 21:29:14 +02:00
{ // Create a new field
field=key_part->field=field->new_field(&outparam->mem_root,
Bug#16218 - Crash on insert delayed Bug#17294 - INSERT DELAYED puting an \n before data Bug#16611 - INSERT DELAYED corrupts data Bug#13707 - Server crash with INSERT DELAYED on MyISAM table Combined as Bug#16218. INSERT DELAYED crashed in 5.0 on a table with a varchar that could be NULL and was created pre-5.0 (Bugs 16218 and 13707). INSERT DELAYED corrupted data in 5.0 on a table with varchar fields that was created pre-5.0 (Bugs 17294 and 16611). In case of INSERT DELAYED the open table is copied from the delayed insert thread to be able to create a record for the queue. When copying the fields, a method was used that did convert old varchar to new varchar fields and did not set up some pointers into the record buffer of the table. The field conversion was guilty for the misinterpretation of the record contents by the delayed insert thread. The wrong pointer setup was guilty for the crashes. For Bug 13707 (Server crash with INSERT DELAYED on MyISAM table) I fixed the above mentioned method to set up one of the pointers. For Bug 16218 I set up the other pointers too. But when looking at the corruptions I got aware that converting the field type was totally wrong for INSERT DELAYED. The copied table is used to create a record that is to be sent to the delayed insert thread. Of course it can interpret the record correctly only if all field types are the same in both table objects. So I revoked the fix for Bug 13707 and changed the new_field() method so that it can suppress conversions. No test case as this is a migration problem. One needs to create a table with 4.x and use it with 5.x. I added two test scripts to the bug report.
2006-06-26 20:57:18 +02:00
outparam,
outparam == field->table);
2000-07-31 21:29:14 +02:00
field->field_length=key_part->length;
}
}
to_be_deleted:
/*
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_NULL_PART;
2000-07-31 21:29:14 +02:00
}
else
{ // Error: shorten key
keyinfo->key_parts=usable_parts;
keyinfo->flags=0;
}
}
keyinfo->usable_key_parts=usable_parts; // Filesort
set_if_bigger(share->max_key_length,keyinfo->key_length+
keyinfo->key_parts);
share->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))
2005-05-18 22:54:36 +02:00
set_if_bigger(share->max_unique_length,keyinfo->key_length);
2000-07-31 21:29:14 +02:00
}
if (primary_key < MAX_KEY &&
(share->keys_in_use.is_set(primary_key)))
2000-07-31 21:29:14 +02:00
{
share->primary_key= primary_key;
2000-10-16 01:29:48 +02:00
/*
If we are using an integer as the primary key then allow the user to
refer to it as '_rowid'
*/
2000-07-31 21:29:14 +02:00
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
share->primary_key = MAX_KEY; // we do not have a primary key
2000-07-31 21:29:14 +02:00
}
else
share->primary_key= MAX_KEY;
2000-07-31 21:29:14 +02:00
x_free((gptr) disk_buff);
2000-10-16 01:29:48 +02:00
disk_buff=0;
2000-07-31 21:29:14 +02:00
if (new_field_pack_flag <= 1)
{
/* Old file format with default as not null */
uint null_length= (share->null_fields+7)/8;
bfill(share->default_values + (outparam->null_flags - (uchar*) record),
null_length, 255);
2000-07-31 21:29:14 +02:00
}
if ((reg_field=outparam->found_next_number_field))
{
if ((int) (share->next_number_index= (uint)
find_ref_key(outparam,reg_field,
&share->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 (share->blob_fields)
2000-07-31 21:29:14 +02:00
{
Field **ptr;
uint k, *save;
2000-07-31 21:29:14 +02:00
/* Store offsets to blob fields to find them fast */
if (!(share->blob_field= save=
(uint*) alloc_root(&outparam->mem_root,
(uint) (share->blob_fields* sizeof(uint)))))
goto err;
for (k=0, ptr= outparam->field ; *ptr ; ptr++, k++)
2000-07-31 21:29:14 +02:00
{
if ((*ptr)->flags & BLOB_FLAG)
(*save++)= k;
2000-07-31 21:29:14 +02:00
}
}
/*
the correct null_bytes can now be set, since bitfields have been taken
into account
*/
share->null_bytes= (null_pos - (uchar*) outparam->null_flags +
(null_bit_pos + 7) / 8);
share->last_null_bit_pos= null_bit_pos;
/* The table struct is now initialized; Open the table */
2000-10-16 01:29:48 +02:00
error=2;
if (db_stat)
{
int ha_err;
unpack_filename(index_file,index_file);
if ((ha_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))))
2000-10-16 01:29:48 +02:00
{
/* Set a flag if the table is crashed and it can be auto. repaired */
share->crashed= ((ha_err == HA_ERR_CRASHED_ON_USAGE) &&
outparam->file->auto_repair() &&
!(ha_open_flags & HA_OPEN_FOR_REPAIR));
if (ha_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;
}
else
{
outparam->file->print_error(ha_err, MYF(0));
error_reported= TRUE;
}
goto err; /* purecov: inspected */
2000-10-16 01:29:48 +02:00
}
}
share->db_low_byte_first= outparam->file->low_byte_first();
2000-10-16 01:29:48 +02:00
*root_ptr= old_root;
thd->status_var.opened_tables++;
2000-07-31 21:29:14 +02:00
#ifndef DBUG_OFF
if (use_hash)
(void) hash_check(&share->name_hash);
2000-07-31 21:29:14 +02:00
#endif
DBUG_RETURN (0);
err:
2000-07-31 21:29:14 +02:00
x_free((gptr) disk_buff);
2000-10-16 01:29:48 +02:00
if (file > 0)
VOID(my_close(file,MYF(MY_WME)));
2000-07-31 21:29:14 +02:00
delete crypted;
*root_ptr= old_root;
if (! error_reported)
2004-12-31 15:26:24 +01:00
frm_error(error,outparam,name,ME_ERROR+ME_WAITTANG, errarg);
delete outparam->file;
outparam->file=0; // For easier errorchecking
outparam->db_stat=0;
hash_free(&share->name_hash);
free_root(&outparam->mem_root, MYF(0)); // Safe to call on bzero'd root
my_free((char*) outparam->alias, MYF(MY_ALLOW_ZERO_PTR));
2000-07-31 21:29:14 +02:00
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();
my_free((char*) table->alias, MYF(MY_ALLOW_ZERO_PTR));
table->alias= 0;
if (table->field)
2000-07-31 21:29:14 +02:00
{
for (Field **ptr=table->field ; *ptr ; ptr++)
delete *ptr;
table->field= 0;
2000-07-31 21:29:14 +02:00
}
delete table->file;
table->file= 0; /* For easier errorchecking */
hash_free(&table->s->name_hash);
free_root(&table->mem_root, MYF(0));
2000-07-31 21:29:14 +02:00
DBUG_RETURN(error);
}
/* Deallocate temporary blob storage */
void free_blobs(register TABLE *table)
{
uint *ptr, *end;
for (ptr= table->s->blob_field, end=ptr + table->s->blob_fields ;
ptr != end ;
ptr++)
((Field_blob*) table->field[*ptr])->free();
2000-07-31 21:29:14 +02:00
}
/* 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)
{
if (names)
my_free((gptr) buf,MYF(0));
}
2000-07-31 21:29:14 +02:00
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;
2000-07-31 21:29:14 +02:00
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 */
2000-07-31 21:29:14 +02:00
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' */
2000-07-31 21:29:14 +02:00
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)
2000-07-31 21:29:14 +02:00
{
int err_no;
char buff[FN_REFLEN];
const char *form_dev="",*datext;
const char *real_name= (char*) name+dirname_length(name);
2000-07-31 21:29:14 +02:00
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, real_name);
2000-07-31 21:29:14 +02:00
}
else
my_error((my_errno == EMFILE) ? ER_CANT_OPEN_FILE : ER_FILE_NOT_FOUND,
errortype,
fn_format(buff, name, form_dev, reg_ext, 0), my_errno);
2000-07-31 21:29:14 +02:00
break;
case 2:
{
2004-04-15 09:14:14 +02:00
datext= form->file ? *form->file->bas_ext() : "";
datext= datext==NullS ? "" : datext;
2000-07-31 21:29:14 +02:00
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,real_name,form_dev,datext,2),my_errno);
2000-07-31 21:29:14 +02:00
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, real_name);
2000-07-31 21:29:14 +02:00
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;
2000-07-31 21:29:14 +02:00
default: /* Better wrong error than none */
case 4:
my_error(ER_NOT_FORM_FILE, errortype,
fn_format(buff, name, form_dev, reg_ext, 0));
2000-07-31 21:29:14 +02:00
break;
}
DBUG_VOID_RETURN;
} /* frm_error */
/*
** fix a str_type to a array type
** typeparts separated with some char. differents types are separated
2000-07-31 21:29:14 +02:00
** 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 */
2000-07-31 21:29:14 +02:00
}
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)
2000-07-31 21:29:14 +02:00
{
TYPELIB *result= (TYPELIB*) alloc_root(mem_root, sizeof(TYPELIB));
2000-07-31 21:29:14 +02:00
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)))
2000-07-31 21:29:14 +02:00
return 0;
result->type_lengths= (uint*) (result->type_names + result->count + 1);
2000-07-31 21:29:14 +02:00
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;
2000-07-31 21:29:14 +02:00
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.
NOTES
This is needed because in some .frm fields 'fieldnr' was saved wrong
RETURN
0 error
# field number +1
*/
2000-07-31 21:29:14 +02:00
static uint find_field(TABLE *form,uint start,uint length)
{
Field **field;
uint i, pos, fields;
2000-07-31 21:29:14 +02:00
pos=0;
fields= form->s->fields;
for (field=form->field, i=1 ; i<= fields ; i++,field++)
2000-07-31 21:29:14 +02:00
{
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 internal */
2000-07-31 21:29:14 +02:00
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)
2000-07-31 21:29:14 +02:00
{
const char *end= pos+length;
res->append('\'');
for (; pos != end ; pos++)
2000-07-31 21:29:14 +02:00
{
#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
2000-07-31 21:29:14 +02:00
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 readability */
2000-07-31 21:29:14 +02:00
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('\'');
2000-07-31 21:29:14 +02:00
}
/* Create a .frm file */
File create_frm(THD *thd, my_string name, const char *db,
const char *table, uint reclength, uchar *fileinfo,
2000-07-31 21:29:14 +02:00
HA_CREATE_INFO *create_info, uint keys)
{
register File file;
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;
2000-07-31 21:29:14 +02:00
/* Fix this when we have new .frm files; Current limit is 4G rows (QQ) */
if (create_info->max_rows > UINT_MAX32)
create_info->max_rows= UINT_MAX32;
if (create_info->min_rows > UINT_MAX32)
create_info->min_rows= UINT_MAX32;
/*
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);
2000-07-31 21:29:14 +02:00
if ((file= my_create(name, CREATE_MODE, create_flags, MYF(0))) >= 0)
2000-07-31 21:29:14 +02:00
{
uint key_length, tmp_key_length;
uint tmp;
2000-07-31 21:29:14 +02:00
bzero((char*) fileinfo,64);
/* header */
fileinfo[0]=(uchar) 254;
fileinfo[1]= 1;
fileinfo[2]= FRM_VER+3+ test(create_info->varchar);
fileinfo[3]= (uchar) ha_checktype(thd,create_info->db_type,0,0);
2000-07-31 21:29:14 +02:00
fileinfo[4]=1;
int2store(fileinfo+6,IO_SIZE); /* Next block starts here */
/*
Keep in sync with pack_keys() in unireg.cc
For each key:
8 bytes for the key header
9 bytes for each key-part (MAX_REF_PARTS)
NAME_LEN bytes for the name
1 byte for the NAMES_SEP_CHAR (before the name)
For all keys:
6 bytes for the header
1 byte for the NAMES_SEP_CHAR (after the last name)
9 extra bytes (padding for safety? alignment?)
*/
key_length= keys * (8 + MAX_REF_PARTS * 9 + NAME_LEN + 1) + 16;
length= next_io_size((ulong) (IO_SIZE+key_length+reclength+
create_info->extra_size));
2000-07-31 21:29:14 +02:00
int4store(fileinfo+10,length);
tmp_key_length= (key_length < 0xffff) ? key_length : 0xffff;
int2store(fileinfo+14,tmp_key_length);
2000-07-31 21:29:14 +02:00
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
fileinfo[33]=5; // Mark for 5.0 frm file
2000-07-31 21:29:14 +02:00
int4store(fileinfo+34,create_info->avg_row_length);
fileinfo[38]= (create_info->default_table_charset ?
create_info->default_table_charset->number : 0);
2000-07-31 21:29:14 +02:00
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);
int4store(fileinfo+47, key_length);
tmp= MYSQL_VERSION_ID; // Store to avoid warning from int4store
int4store(fileinfo+51, tmp);
int2store(fileinfo+55, create_info->extra_size);
2000-07-31 21:29:14 +02:00
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);
}
2000-07-31 21:29:14 +02:00
return (file);
} /* create_frm */
void update_create_info_from_table(HA_CREATE_INFO *create_info, TABLE *table)
{
TABLE_SHARE *share= table->s;
DBUG_ENTER("update_create_info_from_table");
create_info->max_rows= share->max_rows;
create_info->min_rows= share->min_rows;
create_info->table_options= share->db_create_options;
create_info->avg_row_length= share->avg_row_length;
create_info->row_type= share->row_type;
create_info->raid_type= share->raid_type;
create_info->raid_chunks= share->raid_chunks;
create_info->raid_chunksize= share->raid_chunksize;
create_info->default_table_charset= share->table_charset;
create_info->table_charset= 0;
DBUG_VOID_RETURN;
}
2000-07-31 21:29:14 +02:00
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)));
}
2003-05-29 23:47:31 +02:00
/*
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
2003-05-29 23:47:31 +02:00
*/
bool get_field(MEM_ROOT *mem, Field *field, String *res)
{
char buff[MAX_FIELD_WIDTH], *to;
2003-05-29 23:47:31 +02:00
String str(buff,sizeof(buff),&my_charset_bin);
uint length;
field->val_str(&str);
if (!(length= str.length()))
{
res->length(0);
return 1;
}
if (!(to= strmake_root(mem, str.ptr(), length)))
length= 0; // Safety fix
res->set(to, length, ((Field_str*)field)->charset());
return 0;
2003-05-29 23:47:31 +02:00
}
2000-07-31 21:29:14 +02:00
/*
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
2000-07-31 21:29:14 +02:00
*/
char *get_field(MEM_ROOT *mem, Field *field)
2000-07-31 21:29:14 +02:00
{
char buff[MAX_FIELD_WIDTH], *to;
String str(buff,sizeof(buff),&my_charset_bin);
uint length;
field->val_str(&str);
length= str.length();
2003-12-09 20:49:48 +01:00
if (!length || !(to= (char*) alloc_root(mem,length+1)))
2000-07-31 21:29:14 +02:00
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)
2000-07-31 21:29:14 +02:00
{
2006-09-27 14:49:16 +02:00
char *start= name;
2004-03-16 21:41:30 +01:00
/* Used to catch empty names and names with end space */
bool last_char_is_space= TRUE;
2004-04-06 12:31:25 +02:00
if (lower_case_table_names && name != any_db)
my_casedn_str(files_charset_info, name);
2000-07-31 21:29:14 +02:00
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))
2000-07-31 21:29:14 +02:00
{
int len=my_ismbchar(system_charset_info, name,
name+system_charset_info->mbmaxlen);
2000-07-31 21:29:14 +02:00
if (len)
{
name += len;
continue;
}
}
#else
last_char_is_space= *name==' ';
2000-07-31 21:29:14 +02:00
#endif
if (*name == '/' || *name == '\\' || *name == FN_LIBCHAR ||
*name == FN_EXTCHAR)
2000-07-31 21:29:14 +02:00
return 1;
name++;
}
2006-09-27 14:49:16 +02:00
return last_char_is_space || (uint) (name - start) > NAME_LEN;
2000-07-31 21:29:14 +02:00
}
/*
Allow anything as a table name, as long as it doesn't contain an
a '/', or a '.' character
or ' ' at the end
2000-07-31 21:29:14 +02:00
returns 1 on error
*/
bool check_table_name(const char *name, uint length)
{
const char *end= name+length;
2003-03-14 16:08:42 +01:00
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
2000-07-31 21:29:14 +02:00
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))
2000-07-31 21:29:14 +02:00
{
int len=my_ismbchar(system_charset_info, name, end);
2000-07-31 21:29:14 +02:00
if (len)
{
name += len;
continue;
}
}
#endif
if (*name == '/' || *name == '\\' || *name == FN_EXTCHAR)
2000-07-31 21:29:14 +02:00
return 1;
name++;
}
#if defined(USE_MB) && defined(USE_MB_IDENT)
return last_char_is_space;
#else
2000-07-31 21:29:14 +02:00
return 0;
#endif
2000-07-31 21:29:14 +02:00
}
2004-03-12 14:56:28 +01:00
2000-07-31 21:29:14 +02:00
bool check_column_name(const char *name)
{
2003-03-14 16:08:42 +01:00
const char *start= name;
2004-03-16 21:41:30 +01:00
bool last_char_is_space= TRUE;
2000-07-31 21:29:14 +02:00
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))
2000-07-31 21:29:14 +02:00
{
int len=my_ismbchar(system_charset_info, name,
name+system_charset_info->mbmaxlen);
2000-07-31 21:29:14 +02:00
if (len)
{
name += len;
continue;
}
}
#else
last_char_is_space= *name==' ';
2000-07-31 21:29:14 +02:00
#endif
if (*name == NAMES_SEP_CHAR)
return 1;
name++;
}
2003-03-14 16:08:42 +01:00
/* Error if empty or too long column name */
2004-03-16 21:41:30 +01:00
return last_char_is_space || (uint) (name - start) > NAME_LEN;
2000-07-31 21:29:14 +02:00
}
/*
Create Item_field for each column in the table.
SYNPOSIS
st_table::fill_item_list()
item_list a pointer to an empty list used to store items
DESCRIPTION
Create Item_field object for each column in the table and
initialize it with the corresponding Field. New items are
created in the current THD memory root.
RETURN VALUE
0 success
1 out of memory
*/
bool st_table::fill_item_list(List<Item> *item_list) const
{
/*
All Item_field's created using a direct pointer to a field
are fixed in Item_field constructor.
*/
for (Field **ptr= field; *ptr; ptr++)
{
Item_field *item= new Item_field(*ptr);
if (!item || item_list->push_back(item))
return TRUE;
}
return FALSE;
}
/*
Reset an existing list of Item_field items to point to the
Fields of this table.
SYNPOSIS
st_table::fill_item_list()
item_list a non-empty list with Item_fields
DESCRIPTION
This is a counterpart of fill_item_list used to redirect
Item_fields to the fields of a newly created table.
The caller must ensure that number of items in the item_list
is the same as the number of columns in the table.
*/
void st_table::reset_item_list(List<Item> *item_list) const
{
List_iterator_fast<Item> it(*item_list);
for (Field **ptr= field; *ptr; ptr++)
{
Item_field *item_field= (Item_field*) it++;
DBUG_ASSERT(item_field != 0);
item_field->reset_field(*ptr);
}
}
2000-07-31 21:29:14 +02:00
/**
Set the initial purpose of this TABLE_LIST object in the list of
used tables. We need to track this information on table-by-
table basis, since when this table becomes an element of the
pre-locked list, it's impossible to identify which SQL
sub-statement it has been originally used in.
E.g.:
User request: SELECT * FROM t1 WHERE f1();
FUNCTION f1(): DELETE FROM t2; RETURN 1;
BEFORE DELETE trigger on t2: INSERT INTO t3 VALUES (old.a);
For this user request, the pre-locked list will contain t1, t2, t3
table elements, each needed for different DML.
This method is called immediately after parsing for tables
of the table list of the top-level select lex.
The trigger event map is updated to reflect INSERT, UPDATE, DELETE,
REPLACE, LOAD DATA, CREATE TABLE .. SELECT, CREATE TABLE ..
REPLACE SELECT statements, and additionally ON DUPLICATE KEY UPDATE
clause.
*/
void
TABLE_LIST::set_trg_event_type(const st_lex *lex)
{
enum trg_event_type trg_event;
/*
Some auxiliary operations
(e.g. GRANT processing) create TABLE_LIST instances outside
the parser. Additionally, some commands (e.g. OPTIMIZE) change
the lock type for a table only after parsing is done. Luckily,
these do not fire triggers and do not need to pre-load them.
For these TABLE_LISTs set_trg_event_type is never called, and
trg_event_map is always empty. That means that the pre-locking
algorithm will ignore triggers defined on these tables, if
any, and the execution will either fail with an assert in
sql_trigger.cc or with an error that a used table was not
pre-locked, in case of a production build.
TODO: this usage pattern creates unnecessary module dependencies
and should be rewritten to go through the parser.
Table list instances created outside the parser in most cases
refer to mysql.* system tables. It is not allowed to have
a trigger on a system table, but keeping track of
initialization provides extra safety in case this limitation
is circumvented.
*/
/*
This is a fast check to filter out statements that do
not change data, or tables on the right side, in case of
INSERT .. SELECT, CREATE TABLE .. SELECT and so on.
Here we also filter out OPTIMIZE statement and non-updateable
views, for which lock_type is TL_UNLOCK or TL_READ after
parsing.
*/
if (static_cast<int>(lock_type) < static_cast<int>(TL_WRITE_ALLOW_WRITE))
return;
switch (lex->sql_command) {
/*
Basic INSERT. If there is an additional ON DUPLIATE KEY UPDATE
clause, it will be handled later in this method.
*/
case SQLCOM_INSERT: /* fall through */
case SQLCOM_INSERT_SELECT:
/*
LOAD DATA ... INFILE is expected to fire BEFORE/AFTER INSERT
triggers.
If the statement also has REPLACE clause, it will be
handled later in this method.
*/
case SQLCOM_LOAD: /* fall through */
/*
REPLACE is semantically equivalent to INSERT. In case
of a primary or unique key conflict, it deletes the old
record and inserts a new one. So we also may need to
fire ON DELETE triggers. This functionality is handled
later in this method.
*/
case SQLCOM_REPLACE: /* fall through */
case SQLCOM_REPLACE_SELECT:
/*
CREATE TABLE ... SELECT defaults to INSERT if the table or
view already exists. REPLACE option of CREATE TABLE ...
REPLACE SELECT is handled later in this method.
*/
case SQLCOM_CREATE_TABLE:
trg_event= TRG_EVENT_INSERT;
break;
/* Basic update and multi-update */
case SQLCOM_UPDATE: /* fall through */
case SQLCOM_UPDATE_MULTI:
trg_event= TRG_EVENT_UPDATE;
break;
/* Basic delete and multi-delete */
case SQLCOM_DELETE: /* fall through */
case SQLCOM_DELETE_MULTI:
trg_event= TRG_EVENT_DELETE;
break;
default:
/*
OK to return, since value of 'duplicates' is irrelevant
for non-updating commands.
*/
return;
}
trg_event_map|= static_cast<uint8>(1 << static_cast<int>(trg_event));
switch (lex->duplicates) {
case DUP_UPDATE:
trg_event= TRG_EVENT_UPDATE;
break;
case DUP_REPLACE:
trg_event= TRG_EVENT_DELETE;
break;
case DUP_ERROR:
default:
return;
}
trg_event_map|= static_cast<uint8>(1 << static_cast<int>(trg_event));
}
2004-07-16 00:15:55 +02:00
/*
calculate md5 of query
SYNOPSIS
TABLE_LIST::calc_md5()
2004-07-16 00:15:55 +02:00
buffer buffer for md5 writing
*/
void TABLE_LIST::calc_md5(char *buffer)
2004-07-16 00:15:55 +02:00
{
my_MD5_CTX context;
uchar digest[16];
my_MD5Init(&context);
my_MD5Update(&context,(uchar *) query.str, query.length);
my_MD5Final(digest, &context);
2004-07-16 00:15:55 +02:00
sprintf((char *) buffer,
"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
digest[0], digest[1], digest[2], digest[3],
digest[4], digest[5], digest[6], digest[7],
digest[8], digest[9], digest[10], digest[11],
digest[12], digest[13], digest[14], digest[15]);
}
/*
set underlying TABLE for table place holder of VIEW
2004-07-16 00:15:55 +02:00
2005-02-23 17:51:10 +01:00
DESCRIPTION
Replace all views that only uses one table with the table itself.
This allows us to treat the view as a simple table and even update
it (it is a kind of optimisation)
2005-02-23 17:51:10 +01:00
2004-07-16 00:15:55 +02:00
SYNOPSIS
TABLE_LIST::set_underlying_merge()
2004-07-16 00:15:55 +02:00
*/
void TABLE_LIST::set_underlying_merge()
2004-07-16 00:15:55 +02:00
{
2005-02-23 17:51:10 +01:00
TABLE_LIST *tbl;
if ((tbl= merge_underlying_list))
2004-11-21 19:08:12 +01:00
{
2005-02-23 17:51:10 +01:00
/* This is a view. Process all tables of view */
DBUG_ASSERT(view && effective_algorithm == VIEW_ALGORITHM_MERGE);
2005-02-23 17:51:10 +01:00
do
{
if (tbl->merge_underlying_list) // This is a view
2005-02-23 17:51:10 +01:00
{
DBUG_ASSERT(tbl->view &&
tbl->effective_algorithm == VIEW_ALGORITHM_MERGE);
2005-02-23 17:51:10 +01:00
/*
This is the only case where set_ancestor is called on an object
that may not be a view (in which case ancestor is 0)
*/
tbl->merge_underlying_list->set_underlying_merge();
2005-02-23 17:51:10 +01:00
}
} while ((tbl= tbl->next_local));
if (!multitable_view)
2005-02-23 17:51:10 +01:00
{
table= merge_underlying_list->table;
schema_table= merge_underlying_list->schema_table;
2005-02-23 17:51:10 +01:00
}
2004-11-21 19:08:12 +01:00
}
}
2004-07-16 00:15:55 +02:00
/*
setup fields of placeholder of merged VIEW
SYNOPSIS
TABLE_LIST::setup_underlying()
thd - thread handler
DESCRIPTION
It is:
- preparing translation table for view columns
If there are underlying view(s) procedure first will be called for them.
2004-07-16 00:15:55 +02:00
RETURN
2005-02-23 17:51:10 +01:00
FALSE - OK
TRUE - error
2004-07-16 00:15:55 +02:00
*/
bool TABLE_LIST::setup_underlying(THD *thd)
2004-07-16 00:15:55 +02:00
{
DBUG_ENTER("TABLE_LIST::setup_underlying");
if (!field_translation && merge_underlying_list)
{
Field_translator *transl;
SELECT_LEX *select= &view->select_lex;
Item *item;
TABLE_LIST *tbl;
List_iterator_fast<Item> it(select->item_list);
uint field_count= 0;
if (check_stack_overrun(thd, STACK_MIN_SIZE, (char *)&field_count))
{
2005-02-23 17:51:10 +01:00
DBUG_RETURN(TRUE);
}
2004-07-16 00:15:55 +02:00
for (tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
{
if (tbl->merge_underlying_list &&
tbl->setup_underlying(thd))
{
DBUG_RETURN(TRUE);
}
}
2005-02-23 17:51:10 +01:00
/* Create view fields translation table */
if (!(transl=
(Field_translator*)(thd->stmt_arena->
alloc(select->item_list.elements *
sizeof(Field_translator)))))
2004-07-16 00:15:55 +02:00
{
DBUG_RETURN(TRUE);
}
while ((item= it++))
{
transl[field_count].name= item->name;
transl[field_count++].item= item;
}
field_translation= transl;
field_translation_end= transl + field_count;
/* TODO: use hash for big number of fields */
/* full text function moving to current select */
if (view->select_lex.ftfunc_list->elements)
{
Item_func_match *ifm;
SELECT_LEX *current_select= thd->lex->current_select;
List_iterator_fast<Item_func_match>
li(*(view->select_lex.ftfunc_list));
while ((ifm= li++))
current_select->ftfunc_list->push_front(ifm);
2004-07-16 00:15:55 +02:00
}
}
DBUG_RETURN(FALSE);
}
2004-07-16 00:15:55 +02:00
2005-02-23 17:51:10 +01:00
/*
Prepare where expression of view
SYNOPSIS
TABLE_LIST::prep_where()
thd - thread handler
conds - condition of this JOIN
no_where_clause - do not build WHERE or ON outer qwery do not need it
(it is INSERT), we do not need conds if this flag is set
2004-07-23 08:20:58 +02:00
NOTE: have to be called befor CHECK OPTION preparation, because it makes
fix_fields for view WHERE clause
2004-07-16 00:15:55 +02:00
RETURN
FALSE - OK
TRUE - error
*/
2004-07-16 00:15:55 +02:00
bool TABLE_LIST::prep_where(THD *thd, Item **conds,
bool no_where_clause)
{
DBUG_ENTER("TABLE_LIST::prep_where");
for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
{
if (tbl->view && tbl->prep_where(thd, conds, no_where_clause))
{
DBUG_RETURN(TRUE);
}
}
if (where)
{
if (!where->fixed && where->fix_fields(thd, &where))
2004-07-16 00:15:55 +02:00
{
DBUG_RETURN(TRUE);
2004-07-16 00:15:55 +02:00
}
2004-10-04 12:45:35 +02:00
/*
check that it is not VIEW in which we insert with INSERT SELECT
(in this case we can't add view WHERE condition to main SELECT_LEX)
*/
if (!no_where_clause && !where_processed)
2004-07-16 00:15:55 +02:00
{
TABLE_LIST *tbl= this;
Query_arena *arena= thd->stmt_arena, backup;
arena= thd->activate_stmt_arena_if_needed(&backup); // For easier test
2004-10-04 12:45:35 +02:00
/* Go up to join tree and try to find left join */
for (; tbl; tbl= tbl->embedding)
2004-09-03 14:18:40 +02:00
{
2004-10-04 12:45:35 +02:00
if (tbl->outer_join)
{
/*
Store WHERE condition to ON expression for outer join, because
we can't use WHERE to correctly execute left joins on VIEWs and
2004-10-04 12:45:35 +02:00
this expression will not be moved to WHERE condition (i.e. will
be clean correctly for PS/SP)
*/
tbl->on_expr= and_conds(tbl->on_expr,
where->copy_andor_structure(thd));
2004-10-04 12:45:35 +02:00
break;
}
2004-09-03 14:18:40 +02:00
}
2004-10-04 12:45:35 +02:00
if (tbl == 0)
*conds= and_conds(*conds, where->copy_andor_structure(thd));
if (arena)
thd->restore_active_arena(arena, &backup);
where_processed= TRUE;
2004-07-16 00:15:55 +02:00
}
}
DBUG_RETURN(FALSE);
}
/*
Merge ON expressions for a view
SYNOPSIS
merge_on_conds()
thd thread handle
table table for the VIEW
is_cascaded TRUE <=> merge ON expressions from underlying views
DESCRIPTION
This function returns the result of ANDing the ON expressions
of the given view and all underlying views. The ON expressions
of the underlying views are added only if is_cascaded is TRUE.
RETURN
Pointer to the built expression if there is any.
Otherwise and in the case of a failure NULL is returned.
*/
static Item *
merge_on_conds(THD *thd, TABLE_LIST *table, bool is_cascaded)
{
DBUG_ENTER("merge_on_conds");
Item *cond= NULL;
DBUG_PRINT("info", ("alias: %s", table->alias));
if (table->on_expr)
cond= table->on_expr->copy_andor_structure(thd);
if (!table->nested_join)
DBUG_RETURN(cond);
List_iterator<TABLE_LIST> li(table->nested_join->join_list);
while (TABLE_LIST *tbl= li++)
{
if (tbl->view && !is_cascaded)
continue;
cond= and_conds(cond, merge_on_conds(thd, tbl, is_cascaded));
}
DBUG_RETURN(cond);
}
/*
Prepare check option expression of table
SYNOPSIS
TABLE_LIST::prep_check_option()
thd - thread handler
check_opt_type - WITH CHECK OPTION type (VIEW_CHECK_NONE,
VIEW_CHECK_LOCAL, VIEW_CHECK_CASCADED)
we use this parameter instead of direct check of
effective_with_check to change type of underlying
views to VIEW_CHECK_CASCADED if outer view have
such option and prevent processing of underlying
view check options if outer view have just
VIEW_CHECK_LOCAL option.
NOTE
This method builds check option condition to use it later on
every call (usual execution or every SP/PS call).
This method have to be called after WHERE preparation
(TABLE_LIST::prep_where)
2004-07-16 00:15:55 +02:00
RETURN
FALSE - OK
TRUE - error
*/
bool TABLE_LIST::prep_check_option(THD *thd, uint8 check_opt_type)
{
DBUG_ENTER("TABLE_LIST::prep_check_option");
bool is_cascaded= check_opt_type == VIEW_CHECK_CASCADED;
for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
{
/* see comment of check_opt_type parameter */
if (tbl->view && tbl->prep_check_option(thd, (is_cascaded ?
VIEW_CHECK_CASCADED :
VIEW_CHECK_NONE)))
DBUG_RETURN(TRUE);
}
2004-07-16 00:15:55 +02:00
if (check_opt_type && !check_option_processed)
{
Query_arena *arena= thd->stmt_arena, backup;
arena= thd->activate_stmt_arena_if_needed(&backup); // For easier test
if (where)
{
DBUG_ASSERT(where->fixed);
check_option= where->copy_andor_structure(thd);
}
if (is_cascaded)
{
for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
{
if (tbl->check_option)
check_option= and_conds(check_option, tbl->check_option);
}
}
check_option= and_conds(check_option,
merge_on_conds(thd, this, is_cascaded));
if (arena)
thd->restore_active_arena(arena, &backup);
check_option_processed= TRUE;
}
if (check_option)
{
const char *save_where= thd->where;
thd->where= "check option";
if (!check_option->fixed &&
check_option->fix_fields(thd, &check_option) ||
check_option->check_cols(1))
{
DBUG_RETURN(TRUE);
}
thd->where= save_where;
}
DBUG_RETURN(FALSE);
}
/*
Hide errors which show view underlying table information
SYNOPSIS
TABLE_LIST::hide_view_error()
thd thread handler
2004-07-16 00:15:55 +02:00
*/
void TABLE_LIST::hide_view_error(THD *thd)
{
/* Hide "Unknown column" or "Unknown function" error */
if (thd->net.last_errno == ER_BAD_FIELD_ERROR ||
thd->net.last_errno == ER_SP_DOES_NOT_EXIST ||
thd->net.last_errno == ER_PROCACCESS_DENIED_ERROR ||
thd->net.last_errno == ER_COLUMNACCESS_DENIED_ERROR ||
Bug#8407 (Stored functions/triggers ignore exception handler) Bug 18914 (Calling certain SPs from triggers fail) Bug 20713 (Functions will not not continue for SQLSTATE VALUE '42S02') Bug 21825 (Incorrect message error deleting records in a table with a trigger for inserting) Bug 22580 (DROP TABLE in nested stored procedure causes strange dependency error) Bug 25345 (Cursors from Functions) This fix resolves a long standing issue originally reported with bug 8407, which affect the behavior of Stored Procedures, Stored Functions and Trigger in many different ways, causing symptoms reported by all the bugs listed. In all cases, the root cause of the problem traces back to 8407 and how the server locks tables involved with sub statements. Prior to this fix, the implementation of stored routines would: - compute the transitive closure of all the tables referenced by a top level statement - open and lock all the tables involved - execute the top level statement "transitive closure of tables" means collecting: - all the tables, - all the stored functions, - all the views, - all the table triggers - all the stored procedures involved, and recursively inspect these objects definition to find more references to more objects, until the list of every object referenced does not grow any more. This mechanism is known as "pre-locking" tables before execution. The motivation for locking all the tables (possibly) used at once is to prevent dead locks. One problem with this approach is that, if the execution path the code really takes during runtime does not use a given table, and if the table is missing, the server would not execute the statement. This in particular has a major impact on triggers, since a missing table referenced by an update/delete trigger would prevent an insert trigger to run. Another problem is that stored routines might define SQL exception handlers to deal with missing tables, but the server implementation would never give user code a chance to execute this logic, since the routine is never executed when a missing table cause the pre-locking code to fail. With this fix, the internal implementation of the pre-locking code has been relaxed of some constraints, so that failure to open a table does not necessarily prevent execution of a stored routine. In particular, the pre-locking mechanism is now behaving as follows: 1) the first step, to compute the transitive closure of all the tables possibly referenced by a statement, is unchanged. 2) the next step, which is to open all the tables involved, only attempts to open the tables added by the pre-locking code, but silently fails without reporting any error or invoking any exception handler is the table is not present. This is achieved by trapping internal errors with Prelock_error_handler 3) the locking step only locks tables that were successfully opened. 4) when executing sub statements, the list of tables used by each statements is evaluated as before. The tables needed by the sub statement are expected to be already opened and locked. Statement referencing tables that were not opened in step 2) will fail to find the table in the open list, and only at this point will execution of the user code fail. 5) when a runtime exception is raised at 4), the instruction continuation destination (the next instruction to execute in case of SQL continue handlers) is evaluated. This is achieved with sp_instr::exec_open_and_lock_tables() 6) if a user exception handler is present in the stored routine, that handler is invoked as usual, so that ER_NO_SUCH_TABLE exceptions can be trapped by stored routines. If no handler exists, then the runtime execution will fail as expected. With all these changes, a side effect is that view security is impacted, in two different ways. First, a view defined as "select stored_function()", where the stored function references a table that may not exist, is considered valid. The rationale is that, because the stored function might trap exceptions during execution and still return a valid result, there is no way to decide when the view is created if a missing table really cause the view to be invalid. Secondly, testing for existence of tables is now done later during execution. View security, which consist of trapping errors and return a generic ER_VIEW_INVALID (to prevent disclosing information) was only implemented at very specific phases covering *opening* tables, but not covering the runtime execution. Because of this existing limitation, errors that were previously trapped and converted into ER_VIEW_INVALID are not trapped, causing table names to be reported to the user. This change is exposing an existing problem, which is independent and will be resolved separately.
2007-03-06 03:42:07 +01:00
thd->net.last_errno == ER_TABLEACCESS_DENIED_ERROR ||
thd->net.last_errno == ER_TABLE_NOT_LOCKED ||
thd->net.last_errno == ER_NO_SUCH_TABLE)
{
TABLE_LIST *top= top_table();
thd->clear_error();
my_error(ER_VIEW_INVALID, MYF(0), top->view_db.str, top->view_name.str);
}
else if (thd->net.last_errno == ER_NO_DEFAULT_FOR_FIELD)
{
TABLE_LIST *top= top_table();
thd->clear_error();
// TODO: make correct error message
my_error(ER_NO_DEFAULT_FOR_VIEW_FIELD, MYF(0),
top->view_db.str, top->view_name.str);
}
2004-07-16 00:15:55 +02:00
}
/*
Find underlying base tables (TABLE_LIST) which represent given
table_to_find (TABLE)
SYNOPSIS
TABLE_LIST::find_underlying_table()
table_to_find table to find
RETURN
0 table is not found
found table reference
*/
TABLE_LIST *TABLE_LIST::find_underlying_table(TABLE *table_to_find)
{
/* is this real table and table which we are looking for? */
if (table == table_to_find && merge_underlying_list == 0)
return this;
for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
{
TABLE_LIST *result;
if ((result= tbl->find_underlying_table(table_to_find)))
return result;
}
return 0;
}
2004-11-08 00:54:23 +01:00
/*
cleunup items belonged to view fields translation table
SYNOPSIS
TABLE_LIST::cleanup_items()
2004-11-08 00:54:23 +01:00
*/
void TABLE_LIST::cleanup_items()
2004-11-08 00:54:23 +01:00
{
if (!field_translation)
return;
for (Field_translator *transl= field_translation;
transl < field_translation_end;
transl++)
2004-11-21 18:33:49 +01:00
transl->item->walk(&Item::cleanup_processor, 0);
2004-11-08 00:54:23 +01:00
}
/*
check CHECK OPTION condition
SYNOPSIS
TABLE_LIST::view_check_option()
ignore_failure ignore check option fail
RETURN
VIEW_CHECK_OK OK
VIEW_CHECK_ERROR FAILED
VIEW_CHECK_SKIP FAILED, but continue
*/
int TABLE_LIST::view_check_option(THD *thd, bool ignore_failure)
{
if (check_option && check_option->val_int() == 0)
{
TABLE_LIST *main_view= top_table();
if (ignore_failure)
{
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
ER_VIEW_CHECK_FAILED, ER(ER_VIEW_CHECK_FAILED),
main_view->view_db.str, main_view->view_name.str);
return(VIEW_CHECK_SKIP);
}
my_error(ER_VIEW_CHECK_FAILED, MYF(0), main_view->view_db.str,
main_view->view_name.str);
return(VIEW_CHECK_ERROR);
}
return(VIEW_CHECK_OK);
}
/*
Find table in underlying tables by mask and check that only this
2004-11-21 18:33:49 +01:00
table belong to given mask
SYNOPSIS
TABLE_LIST::check_single_table()
table_arg reference on variable where to store found table
(should be 0 on call, to find table, or point to table for
unique test)
map bit mask of tables
view_arg view for which we are looking table
RETURN
2004-11-21 18:33:49 +01:00
FALSE table not found or found only one
TRUE found several tables
*/
bool TABLE_LIST::check_single_table(TABLE_LIST **table_arg,
table_map map,
TABLE_LIST *view_arg)
{
for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
{
if (tbl->table)
{
if (tbl->table->map & map)
{
if (*table_arg)
2004-11-21 18:33:49 +01:00
return TRUE;
*table_arg= tbl;
tbl->check_option= view_arg->check_option;
}
}
else if (tbl->check_single_table(table_arg, map, view_arg))
return TRUE;
}
2004-11-21 18:33:49 +01:00
return FALSE;
}
/*
Set insert_values buffer
SYNOPSIS
set_insert_values()
mem_root memory pool for allocating
RETURN
FALSE - OK
TRUE - out of memory
*/
bool TABLE_LIST::set_insert_values(MEM_ROOT *mem_root)
{
if (table)
{
if (!table->insert_values &&
!(table->insert_values= (byte *)alloc_root(mem_root,
table->s->rec_buff_length)))
return TRUE;
}
else
{
DBUG_ASSERT(view && merge_underlying_list);
for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
if (tbl->set_insert_values(mem_root))
return TRUE;
}
return FALSE;
}
/*
Test if this is a leaf with respect to name resolution.
SYNOPSIS
TABLE_LIST::is_leaf_for_name_resolution()
DESCRIPTION
A table reference is a leaf with respect to name resolution if
it is either a leaf node in a nested join tree (table, view,
schema table, subquery), or an inner node that represents a
NATURAL/USING join, or a nested join with materialized join
columns.
RETURN
TRUE if a leaf, FALSE otherwise.
*/
bool TABLE_LIST::is_leaf_for_name_resolution()
{
return (view || is_natural_join || is_join_columns_complete ||
!nested_join);
}
/*
Retrieve the first (left-most) leaf in a nested join tree with
respect to name resolution.
SYNOPSIS
TABLE_LIST::first_leaf_for_name_resolution()
DESCRIPTION
Given that 'this' is a nested table reference, recursively walk
down the left-most children of 'this' until we reach a leaf
table reference with respect to name resolution.
IMPLEMENTATION
The left-most child of a nested table reference is the last element
in the list of children because the children are inserted in
reverse order.
RETURN
If 'this' is a nested table reference - the left-most child of
the tree rooted in 'this',
else return 'this'
*/
TABLE_LIST *TABLE_LIST::first_leaf_for_name_resolution()
{
TABLE_LIST *cur_table_ref;
NESTED_JOIN *cur_nested_join;
LINT_INIT(cur_table_ref);
if (is_leaf_for_name_resolution())
return this;
DBUG_ASSERT(nested_join);
for (cur_nested_join= nested_join;
cur_nested_join;
cur_nested_join= cur_table_ref->nested_join)
{
List_iterator_fast<TABLE_LIST> it(cur_nested_join->join_list);
cur_table_ref= it++;
/*
If the current nested join is a RIGHT JOIN, the operands in
'join_list' are in reverse order, thus the first operand is
already at the front of the list. Otherwise the first operand
is in the end of the list of join operands.
*/
if (!(cur_table_ref->outer_join & JOIN_TYPE_RIGHT))
{
TABLE_LIST *next;
while ((next= it++))
cur_table_ref= next;
}
if (cur_table_ref->is_leaf_for_name_resolution())
break;
}
return cur_table_ref;
}
/*
Retrieve the last (right-most) leaf in a nested join tree with
respect to name resolution.
SYNOPSIS
TABLE_LIST::last_leaf_for_name_resolution()
DESCRIPTION
Given that 'this' is a nested table reference, recursively walk
down the right-most children of 'this' until we reach a leaf
table reference with respect to name resolution.
IMPLEMENTATION
The right-most child of a nested table reference is the first
element in the list of children because the children are inserted
in reverse order.
RETURN
- If 'this' is a nested table reference - the right-most child of
the tree rooted in 'this',
- else - 'this'
*/
TABLE_LIST *TABLE_LIST::last_leaf_for_name_resolution()
{
TABLE_LIST *cur_table_ref= this;
NESTED_JOIN *cur_nested_join;
if (is_leaf_for_name_resolution())
return this;
DBUG_ASSERT(nested_join);
for (cur_nested_join= nested_join;
cur_nested_join;
cur_nested_join= cur_table_ref->nested_join)
{
cur_table_ref= cur_nested_join->join_list.head();
/*
If the current nested is a RIGHT JOIN, the operands in
'join_list' are in reverse order, thus the last operand is in the
end of the list.
*/
if ((cur_table_ref->outer_join & JOIN_TYPE_RIGHT))
{
List_iterator_fast<TABLE_LIST> it(cur_nested_join->join_list);
TABLE_LIST *next;
cur_table_ref= it++;
while ((next= it++))
cur_table_ref= next;
}
if (cur_table_ref->is_leaf_for_name_resolution())
break;
}
return cur_table_ref;
}
/*
Register access mode which we need for underlying tables
SYNOPSIS
register_want_access()
want_access Acess which we require
*/
void TABLE_LIST::register_want_access(ulong want_access)
{
/* Remove SHOW_VIEW_ACL, because it will be checked during making view */
want_access&= ~SHOW_VIEW_ACL;
if (belong_to_view)
{
grant.want_privilege= want_access;
if (table)
table->grant.want_privilege= want_access;
}
for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
tbl->register_want_access(want_access);
}
/*
2005-10-27 23:56:44 +02:00
Load security context information for this view
SYNOPSIS
TABLE_LIST::prepare_view_securety_context()
thd [in] thread handler
RETURN
FALSE OK
TRUE Error
*/
#ifndef NO_EMBEDDED_ACCESS_CHECKS
bool TABLE_LIST::prepare_view_securety_context(THD *thd)
{
DBUG_ENTER("TABLE_LIST::prepare_view_securety_context");
DBUG_PRINT("enter", ("table: %s", alias));
DBUG_ASSERT(!prelocking_placeholder && view);
if (view_suid)
{
DBUG_PRINT("info", ("This table is suid view => load contest"));
DBUG_ASSERT(view && view_sctx);
if (acl_getroot_no_password(view_sctx,
definer.user.str,
definer.host.str,
definer.host.str,
thd->db))
{
if ((thd->lex->sql_command == SQLCOM_SHOW_CREATE) ||
(thd->lex->sql_command == SQLCOM_SHOW_FIELDS))
{
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_NO_SUCH_USER,
ER(ER_NO_SUCH_USER),
definer.user.str, definer.host.str);
}
else
{
if (thd->security_ctx->master_access & SUPER_ACL)
{
my_error(ER_NO_SUCH_USER, MYF(0), definer.user.str, definer.host.str);
}
else
{
my_error(ER_ACCESS_DENIED_ERROR, MYF(0),
thd->security_ctx->priv_user,
thd->security_ctx->priv_host,
(thd->password ? ER(ER_YES) : ER(ER_NO)));
}
DBUG_RETURN(TRUE);
}
}
}
DBUG_RETURN(FALSE);
}
#endif
/*
Find security context of current view
SYNOPSIS
TABLE_LIST::find_view_security_context()
thd [in] thread handler
*/
#ifndef NO_EMBEDDED_ACCESS_CHECKS
Security_context *TABLE_LIST::find_view_security_context(THD *thd)
{
Security_context *sctx;
TABLE_LIST *upper_view= this;
DBUG_ENTER("TABLE_LIST::find_view_security_context");
DBUG_ASSERT(view);
while (upper_view && !upper_view->view_suid)
{
DBUG_ASSERT(!upper_view->prelocking_placeholder);
upper_view= upper_view->referencing_view;
}
if (upper_view)
{
DBUG_PRINT("info", ("Securety context of view %s will be used",
upper_view->alias));
sctx= upper_view->view_sctx;
DBUG_ASSERT(sctx);
}
else
{
DBUG_PRINT("info", ("Current global context will be used"));
sctx= thd->security_ctx;
}
DBUG_RETURN(sctx);
}
#endif
/*
Prepare security context and load underlying tables priveleges for view
SYNOPSIS
TABLE_LIST::prepare_security()
thd [in] thread handler
RETURN
FALSE OK
TRUE Error
*/
bool TABLE_LIST::prepare_security(THD *thd)
{
List_iterator_fast<TABLE_LIST> tb(*view_tables);
TABLE_LIST *tbl;
DBUG_ENTER("TABLE_LIST::prepare_security");
#ifndef NO_EMBEDDED_ACCESS_CHECKS
Security_context *save_security_ctx= thd->security_ctx;
DBUG_ASSERT(!prelocking_placeholder);
if (prepare_view_securety_context(thd))
DBUG_RETURN(TRUE);
thd->security_ctx= find_view_security_context(thd);
while ((tbl= tb++))
{
DBUG_ASSERT(tbl->referencing_view);
char *local_db, *local_table_name;
if (tbl->view)
{
local_db= tbl->view_db.str;
local_table_name= tbl->view_name.str;
}
else
{
local_db= tbl->db;
local_table_name= tbl->table_name;
}
fill_effective_table_privileges(thd, &tbl->grant, local_db,
local_table_name);
if (tbl->table)
tbl->table->grant= grant;
}
thd->security_ctx= save_security_ctx;
#else
while ((tbl= tb++))
tbl->grant.privilege= ~NO_ACCESS;
#endif
2005-11-02 14:17:57 +01:00
DBUG_RETURN(FALSE);
}
Natural_join_column::Natural_join_column(Field_translator *field_param,
TABLE_LIST *tab)
{
DBUG_ASSERT(tab->field_translation);
view_field= field_param;
table_field= NULL;
table_ref= tab;
is_common= FALSE;
}
Natural_join_column::Natural_join_column(Field *field_param,
TABLE_LIST *tab)
{
DBUG_ASSERT(tab->table == field_param->table);
table_field= field_param;
view_field= NULL;
table_ref= tab;
is_common= FALSE;
}
const char *Natural_join_column::name()
{
if (view_field)
{
DBUG_ASSERT(table_field == NULL);
return view_field->name;
}
return table_field->field_name;
}
Item *Natural_join_column::create_item(THD *thd)
{
if (view_field)
{
DBUG_ASSERT(table_field == NULL);
return create_view_field(thd, table_ref, &view_field->item,
view_field->name);
}
return new Item_field(thd, &thd->lex->current_select->context, table_field);
}
Field *Natural_join_column::field()
{
if (view_field)
{
DBUG_ASSERT(table_field == NULL);
return NULL;
}
return table_field;
}
const char *Natural_join_column::table_name()
{
DBUG_ASSERT(table_ref);
return table_ref->alias;
}
const char *Natural_join_column::db_name()
{
if (view_field)
return table_ref->view_db.str;
/*
Test that TABLE_LIST::db is the same as st_table_share::db to
ensure consistency. An exception are I_S schema tables, which
are inconsistent in this respect.
*/
DBUG_ASSERT(!strcmp(table_ref->db,
table_ref->table->s->db) ||
(table_ref->schema_table &&
table_ref->table->s->db[0] == 0));
return table_ref->db;
}
GRANT_INFO *Natural_join_column::grant()
{
if (view_field)
return &(table_ref->grant);
return &(table_ref->table->grant);
}
2004-07-16 00:15:55 +02:00
void Field_iterator_view::set(TABLE_LIST *table)
{
DBUG_ASSERT(table->field_translation);
view= table;
2004-07-16 00:15:55 +02:00
ptr= table->field_translation;
array_end= table->field_translation_end;
2004-07-16 00:15:55 +02:00
}
const char *Field_iterator_table::name()
{
return (*ptr)->field_name;
}
Item *Field_iterator_table::create_item(THD *thd)
2004-07-16 00:15:55 +02:00
{
SELECT_LEX *select= thd->lex->current_select;
Item_field *item= new Item_field(thd, &select->context, *ptr);
if (item && thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY &&
!thd->lex->in_sum_func && select->cur_pos_in_select_list != UNDEF_POS)
{
select->non_agg_fields.push_back(item);
item->marker= select->cur_pos_in_select_list;
}
return item;
2004-07-16 00:15:55 +02:00
}
const char *Field_iterator_view::name()
{
return ptr->name;
2004-07-16 00:15:55 +02:00
}
Item *Field_iterator_view::create_item(THD *thd)
{
return create_view_field(thd, view, &ptr->item, ptr->name);
}
Item *create_view_field(THD *thd, TABLE_LIST *view, Item **field_ref,
const char *name)
{
bool save_wrapper= thd->lex->select_lex.no_wrap_view_item;
Item *field= *field_ref;
DBUG_ENTER("create_view_field");
if (view->schema_table_reformed)
{
/*
Translation table items are always Item_fields and already fixed
('mysql_schema_table' function). So we can return directly the
field. This case happens only for 'show & where' commands.
*/
DBUG_ASSERT(field && field->fixed);
DBUG_RETURN(field);
}
DBUG_ASSERT(field);
thd->lex->current_select->no_wrap_view_item= TRUE;
if (!field->fixed)
{
if (field->fix_fields(thd, field_ref))
{
thd->lex->current_select->no_wrap_view_item= save_wrapper;
DBUG_RETURN(0);
}
field= *field_ref;
}
thd->lex->current_select->no_wrap_view_item= save_wrapper;
if (thd->lex->current_select->no_wrap_view_item)
{
DBUG_RETURN(field);
}
Item *item= new Item_direct_view_ref(&view->view->select_lex.context,
field_ref, view->alias,
name);
DBUG_RETURN(item);
}
void Field_iterator_natural_join::set(TABLE_LIST *table_ref)
{
DBUG_ASSERT(table_ref->join_columns);
column_ref_it.init(*(table_ref->join_columns));
cur_column_ref= column_ref_it++;
}
void Field_iterator_natural_join::next()
{
cur_column_ref= column_ref_it++;
DBUG_ASSERT(!cur_column_ref || ! cur_column_ref->table_field ||
cur_column_ref->table_ref->table ==
cur_column_ref->table_field->table);
}
void Field_iterator_table_ref::set_field_iterator()
{
DBUG_ENTER("Field_iterator_table_ref::set_field_iterator");
/*
If the table reference we are iterating over is a natural join, or it is
an operand of a natural join, and TABLE_LIST::join_columns contains all
the columns of the join operand, then we pick the columns from
TABLE_LIST::join_columns, instead of the orginial container of the
columns of the join operator.
*/
if (table_ref->is_join_columns_complete)
{
/* Necesary, but insufficient conditions. */
DBUG_ASSERT(table_ref->is_natural_join ||
table_ref->nested_join ||
table_ref->join_columns &&
/* This is a merge view. */
((table_ref->field_translation &&
table_ref->join_columns->elements ==
(ulong)(table_ref->field_translation_end -
table_ref->field_translation)) ||
/* This is stored table or a tmptable view. */
(!table_ref->field_translation &&
table_ref->join_columns->elements ==
table_ref->table->s->fields)));
field_it= &natural_join_it;
DBUG_PRINT("info",("field_it for '%s' is Field_iterator_natural_join",
table_ref->alias));
}
/* This is a merge view, so use field_translation. */
else if (table_ref->field_translation)
{
DBUG_ASSERT(table_ref->view &&
table_ref->effective_algorithm == VIEW_ALGORITHM_MERGE);
field_it= &view_field_it;
DBUG_PRINT("info", ("field_it for '%s' is Field_iterator_view",
table_ref->alias));
}
/* This is a base table or stored view. */
else
{
DBUG_ASSERT(table_ref->table || table_ref->view);
field_it= &table_field_it;
DBUG_PRINT("info", ("field_it for '%s' is Field_iterator_table",
table_ref->alias));
}
field_it->set(table_ref);
DBUG_VOID_RETURN;
}
void Field_iterator_table_ref::set(TABLE_LIST *table)
{
DBUG_ASSERT(table);
first_leaf= table->first_leaf_for_name_resolution();
last_leaf= table->last_leaf_for_name_resolution();
DBUG_ASSERT(first_leaf && last_leaf);
table_ref= first_leaf;
set_field_iterator();
}
void Field_iterator_table_ref::next()
{
/* Move to the next field in the current table reference. */
field_it->next();
/*
If all fields of the current table reference are exhausted, move to
the next leaf table reference.
*/
if (field_it->end_of_fields() && table_ref != last_leaf)
{
table_ref= table_ref->next_name_resolution_table;
DBUG_ASSERT(table_ref);
set_field_iterator();
}
}
const char *Field_iterator_table_ref::table_name()
{
if (table_ref->view)
return table_ref->view_name.str;
else if (table_ref->is_natural_join)
return natural_join_it.column_ref()->table_name();
DBUG_ASSERT(!strcmp(table_ref->table_name,
table_ref->table->s->table_name));
return table_ref->table_name;
}
const char *Field_iterator_table_ref::db_name()
{
if (table_ref->view)
return table_ref->view_db.str;
else if (table_ref->is_natural_join)
return natural_join_it.column_ref()->db_name();
/*
Test that TABLE_LIST::db is the same as st_table_share::db to
ensure consistency. An exception are I_S schema tables, which
are inconsistent in this respect.
*/
DBUG_ASSERT(!strcmp(table_ref->db, table_ref->table->s->db) ||
(table_ref->schema_table &&
table_ref->table->s->db[0] == 0));
return table_ref->db;
}
GRANT_INFO *Field_iterator_table_ref::grant()
{
if (table_ref->view)
return &(table_ref->grant);
else if (table_ref->is_natural_join)
return natural_join_it.column_ref()->grant();
return &(table_ref->table->grant);
}
/*
Create new or return existing column reference to a column of a
natural/using join.
SYNOPSIS
Field_iterator_table_ref::get_or_create_column_ref()
parent_table_ref the parent table reference over which the
iterator is iterating
DESCRIPTION
Create a new natural join column for the current field of the
iterator if no such column was created, or return an already
created natural join column. The former happens for base tables or
views, and the latter for natural/using joins. If a new field is
created, then the field is added to 'parent_table_ref' if it is
given, or to the original table referene of the field if
parent_table_ref == NULL.
NOTES
This method is designed so that when a Field_iterator_table_ref
walks through the fields of a table reference, all its fields
are created and stored as follows:
- If the table reference being iterated is a stored table, view or
natural/using join, store all natural join columns in a list
attached to that table reference.
- If the table reference being iterated is a nested join that is
not natural/using join, then do not materialize its result
fields. This is OK because for such table references
Field_iterator_table_ref iterates over the fields of the nested
table references (recursively). In this way we avoid the storage
of unnecessay copies of result columns of nested joins.
RETURN
# Pointer to a column of a natural join (or its operand)
NULL No memory to allocate the column
*/
Natural_join_column *
Field_iterator_table_ref::get_or_create_column_ref(TABLE_LIST *parent_table_ref)
{
Natural_join_column *nj_col;
bool is_created= TRUE;
uint field_count;
TABLE_LIST *add_table_ref= parent_table_ref ?
parent_table_ref : table_ref;
LINT_INIT(field_count);
if (field_it == &table_field_it)
{
/* The field belongs to a stored table. */
Field *tmp_field= table_field_it.field();
nj_col= new Natural_join_column(tmp_field, table_ref);
field_count= table_ref->table->s->fields;
}
else if (field_it == &view_field_it)
{
/* The field belongs to a merge view or information schema table. */
Field_translator *translated_field= view_field_it.field_translator();
nj_col= new Natural_join_column(translated_field, table_ref);
field_count= table_ref->field_translation_end -
table_ref->field_translation;
}
else
{
/*
The field belongs to a NATURAL join, therefore the column reference was
already created via one of the two constructor calls above. In this case
we just return the already created column reference.
*/
DBUG_ASSERT(table_ref->is_join_columns_complete);
is_created= FALSE;
nj_col= natural_join_it.column_ref();
DBUG_ASSERT(nj_col);
}
DBUG_ASSERT(!nj_col->table_field ||
nj_col->table_ref->table == nj_col->table_field->table);
/*
If the natural join column was just created add it to the list of
natural join columns of either 'parent_table_ref' or to the table
reference that directly contains the original field.
*/
if (is_created)
{
/* Make sure not all columns were materialized. */
DBUG_ASSERT(!add_table_ref->is_join_columns_complete);
if (!add_table_ref->join_columns)
{
/* Create a list of natural join columns on demand. */
if (!(add_table_ref->join_columns= new List<Natural_join_column>))
return NULL;
add_table_ref->is_join_columns_complete= FALSE;
}
add_table_ref->join_columns->push_back(nj_col);
/*
If new fields are added to their original table reference, mark if
all fields were added. We do it here as the caller has no easy way
of knowing when to do it.
If the fields are being added to parent_table_ref, then the caller
must take care to mark when all fields are created/added.
*/
if (!parent_table_ref &&
add_table_ref->join_columns->elements == field_count)
add_table_ref->is_join_columns_complete= TRUE;
}
return nj_col;
}
/*
Return an existing reference to a column of a natural/using join.
SYNOPSIS
Field_iterator_table_ref::get_natural_column_ref()
DESCRIPTION
The method should be called in contexts where it is expected that
all natural join columns are already created, and that the column
being retrieved is a Natural_join_column.
RETURN
# Pointer to a column of a natural join (or its operand)
NULL No memory to allocate the column
*/
Natural_join_column *
Field_iterator_table_ref::get_natural_column_ref()
{
Natural_join_column *nj_col;
DBUG_ASSERT(field_it == &natural_join_it);
/*
The field belongs to a NATURAL join, therefore the column reference was
already created via one of the two constructor calls above. In this case
we just return the already created column reference.
*/
nj_col= natural_join_it.column_ref();
DBUG_ASSERT(nj_col &&
(!nj_col->table_field ||
nj_col->table_ref->table == nj_col->table_field->table));
return nj_col;
}
/*
Cleanup this table for re-execution.
SYNOPSIS
TABLE_LIST::reinit_before_use()
*/
void TABLE_LIST::reinit_before_use(THD *thd)
{
/*
Reset old pointers to TABLEs: they are not valid since the tables
were closed in the end of previous prepare or execute call.
*/
table= 0;
/* Reset is_schema_table_processed value(needed for I_S tables */
schema_table_state= NOT_PROCESSED;
TABLE_LIST *embedded; /* The table at the current level of nesting. */
TABLE_LIST *parent_embedding= this; /* The parent nested table reference. */
do
{
embedded= parent_embedding;
if (embedded->prep_on_expr)
embedded->on_expr= embedded->prep_on_expr->copy_andor_structure(thd);
parent_embedding= embedded->embedding;
}
while (parent_embedding &&
parent_embedding->nested_join->join_list.head() == embedded);
}
/*
Return subselect that contains the FROM list this table is taken from
SYNOPSIS
TABLE_LIST::containing_subselect()
RETURN
Subselect item for the subquery that contains the FROM list
this table is taken from if there is any
0 - otherwise
*/
Item_subselect *TABLE_LIST::containing_subselect()
{
return (select_lex ? select_lex->master_unit()->item : 0);
}
2000-07-31 21:29:14 +02:00
/*****************************************************************************
** Instansiate templates
*****************************************************************************/
#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
2000-07-31 21:29:14 +02:00
template class List<String>;
template class List_iterator<String>;
#endif