mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
merge
sql/mysql_priv.h: Auto merged sql/sql_select.cc: Auto merged
This commit is contained in:
@ -417,6 +417,7 @@ typedef struct st_mi_sort_param
|
||||
#define MI_MIN_SIZE_BULK_INSERT_TREE 16384 /* this is per key */
|
||||
#define MI_MIN_ROWS_TO_USE_BULK_INSERT 100
|
||||
#define MI_MIN_ROWS_TO_DISABLE_INDEXES 100
|
||||
#define MI_MIN_ROWS_TO_USE_WRITE_CACHE 10
|
||||
|
||||
/* The UNIQUE check is done with a hashed long key */
|
||||
|
||||
|
@ -670,7 +670,7 @@ report_stats () {
|
||||
$ECHO "The log files in $MY_LOG_DIR may give you some hint"
|
||||
$ECHO "of what when wrong."
|
||||
$ECHO "If you want to report this error, please read first the documentation at"
|
||||
$ECHO "http://www.mysql.com/doc/M/y/MySQL_test_suite.html"
|
||||
$ECHO "http://www.mysql.com/doc/en/MySQL_test_suite.html"
|
||||
fi
|
||||
|
||||
if test -z "$USE_RUNNING_SERVER"
|
||||
|
@ -750,3 +750,10 @@ analyze table t1;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t1 analyze status OK
|
||||
drop table t1;
|
||||
CREATE TABLE t1 (
|
||||
fid INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
g GEOMETRY NOT NULL,
|
||||
SPATIAL KEY(g)
|
||||
) ENGINE=MyISAM;
|
||||
INSERT INTO t1 (g) VALUES (GeomFromText('LineString(1 2, 2 3)')),(GeomFromText('LineString(1 2, 2 4)'));
|
||||
drop table t1;
|
||||
|
@ -76,6 +76,8 @@ delete from mysql.db where user='mysqltest_1';
|
||||
delete from mysql.tables_priv where user='mysqltest_1';
|
||||
delete from mysql.columns_priv where user='mysqltest_1';
|
||||
flush privileges;
|
||||
show grants for mysqltest_1@localhost;
|
||||
ERROR 42000: There is no such grant defined for user 'mysqltest_1' on host 'localhost'
|
||||
create table t1 (a int);
|
||||
GRANT select,update,insert on t1 to mysqltest_1@localhost;
|
||||
GRANT select (a), update (a),insert(a), references(a) on t1 to mysqltest_1@localhost;
|
||||
|
@ -103,3 +103,16 @@ check table t1;
|
||||
analyze table t1;
|
||||
drop table t1;
|
||||
|
||||
#
|
||||
# The following crashed gis
|
||||
#
|
||||
|
||||
CREATE TABLE t1 (
|
||||
fid INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
g GEOMETRY NOT NULL,
|
||||
SPATIAL KEY(g)
|
||||
) ENGINE=MyISAM;
|
||||
|
||||
INSERT INTO t1 (g) VALUES (GeomFromText('LineString(1 2, 2 3)')),(GeomFromText('LineString(1 2, 2 4)'));
|
||||
#select * from t1 where g<GeomFromText('LineString(1 2, 2 3)');
|
||||
drop table t1;
|
||||
|
@ -53,6 +53,8 @@ delete from mysql.db where user='mysqltest_1';
|
||||
delete from mysql.tables_priv where user='mysqltest_1';
|
||||
delete from mysql.columns_priv where user='mysqltest_1';
|
||||
flush privileges;
|
||||
--error 1141
|
||||
show grants for mysqltest_1@localhost;
|
||||
|
||||
#
|
||||
# Test what happens when you have same table and colum level grants
|
||||
|
68
sql/field.cc
68
sql/field.cc
@ -4359,7 +4359,7 @@ int Field_varstring::store(const char *from,uint length,CHARSET_INFO *cs)
|
||||
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED);
|
||||
error= 1;
|
||||
}
|
||||
memcpy(ptr+2,from,length);
|
||||
memcpy(ptr+HA_KEY_BLOB_LENGTH,from,length);
|
||||
int2store(ptr, length);
|
||||
return error;
|
||||
}
|
||||
@ -4388,18 +4388,18 @@ int Field_varstring::store(longlong nr)
|
||||
double Field_varstring::val_real(void)
|
||||
{
|
||||
int not_used;
|
||||
uint length=uint2korr(ptr)+2;
|
||||
uint length=uint2korr(ptr)+HA_KEY_BLOB_LENGTH;
|
||||
CHARSET_INFO *cs=charset();
|
||||
return my_strntod(cs,ptr+2,length,(char**)0, ¬_used);
|
||||
return my_strntod(cs, ptr+HA_KEY_BLOB_LENGTH, length, (char**)0, ¬_used);
|
||||
}
|
||||
|
||||
|
||||
longlong Field_varstring::val_int(void)
|
||||
{
|
||||
int not_used;
|
||||
uint length=uint2korr(ptr)+2;
|
||||
uint length=uint2korr(ptr)+HA_KEY_BLOB_LENGTH;
|
||||
CHARSET_INFO *cs=charset();
|
||||
return my_strntoll(cs,ptr+2,length,10,NULL, ¬_used);
|
||||
return my_strntoll(cs,ptr+HA_KEY_BLOB_LENGTH,length,10,NULL, ¬_used);
|
||||
}
|
||||
|
||||
|
||||
@ -4407,7 +4407,7 @@ String *Field_varstring::val_str(String *val_buffer __attribute__((unused)),
|
||||
String *val_ptr)
|
||||
{
|
||||
uint length=uint2korr(ptr);
|
||||
val_ptr->set((const char*) ptr+2,length,field_charset);
|
||||
val_ptr->set((const char*) ptr+HA_KEY_BLOB_LENGTH,length,field_charset);
|
||||
return val_ptr;
|
||||
}
|
||||
|
||||
@ -4418,8 +4418,10 @@ int Field_varstring::cmp(const char *a_ptr, const char *b_ptr)
|
||||
uint b_length=uint2korr(b_ptr);
|
||||
int diff;
|
||||
diff= my_strnncoll(field_charset,
|
||||
(const uchar*)a_ptr+2,min(a_length,b_length),
|
||||
(const uchar*)b_ptr+2,min(a_length,b_length));
|
||||
(const uchar*) a_ptr+HA_KEY_BLOB_LENGTH,
|
||||
min(a_length,b_length),
|
||||
(const uchar*) b_ptr+HA_KEY_BLOB_LENGTH,
|
||||
min(a_length,b_length));
|
||||
return diff ? diff : (int) (a_length - b_length);
|
||||
}
|
||||
|
||||
@ -4427,8 +4429,9 @@ void Field_varstring::sort_string(char *to,uint length)
|
||||
{
|
||||
uint tot_length=uint2korr(ptr);
|
||||
tot_length= my_strnxfrm(field_charset,
|
||||
(unsigned char *) to, length,
|
||||
(unsigned char *)ptr+2, tot_length);
|
||||
(uchar*) to, length,
|
||||
(uchar*) ptr+HA_KEY_BLOB_LENGTH,
|
||||
tot_length);
|
||||
if (tot_length < length)
|
||||
field_charset->cset->fill(field_charset, to+tot_length,length-tot_length,
|
||||
binary() ? (char) 0 : ' ');
|
||||
@ -4453,7 +4456,7 @@ char *Field_varstring::pack(char *to, const char *from, uint max_length)
|
||||
if (max_length > 255)
|
||||
*to++= (char) (length >> 8);
|
||||
if (length)
|
||||
memcpy(to, from+2, length);
|
||||
memcpy(to, from+HA_KEY_BLOB_LENGTH, length);
|
||||
return to+length;
|
||||
}
|
||||
|
||||
@ -4473,7 +4476,7 @@ const char *Field_varstring::unpack(char *to, const char *from)
|
||||
to[1] = *from++;
|
||||
}
|
||||
if (length)
|
||||
memcpy(to+2, from, length);
|
||||
memcpy(to+HA_KEY_BLOB_LENGTH, from, length);
|
||||
return from+length;
|
||||
}
|
||||
|
||||
@ -4499,7 +4502,7 @@ int Field_varstring::pack_cmp(const char *a, const char *b, uint key_length)
|
||||
|
||||
int Field_varstring::pack_cmp(const char *b, uint key_length)
|
||||
{
|
||||
char *a=ptr+2;
|
||||
char *a= ptr+HA_KEY_BLOB_LENGTH;
|
||||
uint a_length= uint2korr(ptr);
|
||||
uint b_length;
|
||||
if (key_length > 255)
|
||||
@ -4518,7 +4521,7 @@ int Field_varstring::pack_cmp(const char *b, uint key_length)
|
||||
uint Field_varstring::packed_col_length(const char *data_ptr, uint length)
|
||||
{
|
||||
if (length > 255)
|
||||
return uint2korr(data_ptr)+2;
|
||||
return uint2korr(data_ptr)+HA_KEY_BLOB_LENGTH;
|
||||
else
|
||||
return (uint) ((uchar) *data_ptr)+1;
|
||||
}
|
||||
@ -4531,22 +4534,21 @@ uint Field_varstring::max_packed_col_length(uint max_length)
|
||||
void Field_varstring::get_key_image(char *buff, uint length, CHARSET_INFO *cs,
|
||||
imagetype type)
|
||||
{
|
||||
length-= HA_KEY_BLOB_LENGTH;
|
||||
uint f_length=uint2korr(ptr);
|
||||
if (f_length > length)
|
||||
f_length= length;
|
||||
int2store(buff,length);
|
||||
memcpy(buff+2,ptr+2,length);
|
||||
memcpy(buff+HA_KEY_BLOB_LENGTH, ptr+HA_KEY_BLOB_LENGTH, length);
|
||||
#ifdef HAVE_purify
|
||||
if (f_length < length)
|
||||
bzero(buff+2+f_length, (length-f_length));
|
||||
bzero(buff+HA_KEY_BLOB_LENGTH+f_length, (length-f_length));
|
||||
#endif
|
||||
}
|
||||
|
||||
void Field_varstring::set_key_image(char *buff,uint length, CHARSET_INFO *cs)
|
||||
{
|
||||
length=uint2korr(buff); // Real length is here
|
||||
(void) Field_varstring::store(buff+2, length, cs);
|
||||
(void) Field_varstring::store(buff+HA_KEY_BLOB_LENGTH, length, cs);
|
||||
}
|
||||
|
||||
|
||||
@ -4799,7 +4801,6 @@ int Field_blob::cmp_binary(const char *a_ptr, const char *b_ptr,
|
||||
void Field_blob::get_key_image(char *buff,uint length,
|
||||
CHARSET_INFO *cs, imagetype type)
|
||||
{
|
||||
length-= HA_KEY_BLOB_LENGTH;
|
||||
uint32 blob_length= get_length(ptr);
|
||||
char *blob;
|
||||
|
||||
@ -4838,18 +4839,18 @@ void Field_blob::get_key_image(char *buff,uint length,
|
||||
Must clear this as we do a memcmp in opt_range.cc to detect
|
||||
identical keys
|
||||
*/
|
||||
bzero(buff+2+blob_length, (length-blob_length));
|
||||
bzero(buff+HA_KEY_BLOB_LENGTH+blob_length, (length-blob_length));
|
||||
length=(uint) blob_length;
|
||||
}
|
||||
int2store(buff,length);
|
||||
get_ptr(&blob);
|
||||
memcpy(buff+2,blob,length);
|
||||
memcpy(buff+HA_KEY_BLOB_LENGTH, blob, length);
|
||||
}
|
||||
|
||||
void Field_blob::set_key_image(char *buff,uint length, CHARSET_INFO *cs)
|
||||
{
|
||||
length= uint2korr(buff);
|
||||
(void) Field_blob::store(buff+2,length,cs);
|
||||
(void) Field_blob::store(buff+HA_KEY_BLOB_LENGTH, length, cs);
|
||||
}
|
||||
|
||||
|
||||
@ -4857,16 +4858,16 @@ int Field_blob::key_cmp(const byte *key_ptr, uint max_key_length)
|
||||
{
|
||||
char *blob1;
|
||||
uint blob_length=get_length(ptr);
|
||||
max_key_length-=2;
|
||||
memcpy_fixed(&blob1,ptr+packlength,sizeof(char*));
|
||||
return Field_blob::cmp(blob1,min(blob_length, max_key_length),
|
||||
(char*) key_ptr+2,uint2korr(key_ptr));
|
||||
(char*) key_ptr+HA_KEY_BLOB_LENGTH,
|
||||
uint2korr(key_ptr));
|
||||
}
|
||||
|
||||
int Field_blob::key_cmp(const byte *a,const byte *b)
|
||||
{
|
||||
return Field_blob::cmp((char*) a+2,uint2korr(a),
|
||||
(char*) b+2,uint2korr(b));
|
||||
return Field_blob::cmp((char*) a+HA_KEY_BLOB_LENGTH, uint2korr(a),
|
||||
(char*) b+HA_KEY_BLOB_LENGTH, uint2korr(b));
|
||||
}
|
||||
|
||||
|
||||
@ -4882,8 +4883,8 @@ void Field_blob::sort_string(char *to,uint length)
|
||||
memcpy_fixed(&blob,ptr+packlength,sizeof(char*));
|
||||
|
||||
blob_length=my_strnxfrm(field_charset,
|
||||
(unsigned char *)to, length,
|
||||
(unsigned char *)blob, blob_length);
|
||||
(uchar*) to, length,
|
||||
(uchar*) blob, blob_length);
|
||||
if (blob_length < length)
|
||||
field_charset->cset->fill(field_charset, to+blob_length,
|
||||
length-blob_length,
|
||||
@ -5025,7 +5026,7 @@ char *Field_blob::pack_key_from_key_image(char *to, const char *from,
|
||||
if (max_length > 255)
|
||||
*to++= (char) (length >> 8);
|
||||
if (length)
|
||||
memcpy(to, from+2, length);
|
||||
memcpy(to, from+HA_KEY_BLOB_LENGTH, length);
|
||||
return to+length;
|
||||
}
|
||||
|
||||
@ -5048,11 +5049,12 @@ uint Field_blob::max_packed_col_length(uint max_length)
|
||||
void Field_geom::get_key_image(char *buff, uint length, CHARSET_INFO *cs,
|
||||
imagetype type)
|
||||
{
|
||||
length-= HA_KEY_BLOB_LENGTH;
|
||||
ulong blob_length= get_length(ptr);
|
||||
char *blob;
|
||||
const char *dummy;
|
||||
MBR mbr;
|
||||
ulong blob_length= get_length(ptr);
|
||||
Geometry_buffer buffer;
|
||||
Geometry *gobj;
|
||||
|
||||
if (blob_length < SRID_SIZE)
|
||||
{
|
||||
@ -5060,8 +5062,6 @@ void Field_geom::get_key_image(char *buff, uint length, CHARSET_INFO *cs,
|
||||
return;
|
||||
}
|
||||
get_ptr(&blob);
|
||||
Geometry_buffer buffer;
|
||||
Geometry *gobj;
|
||||
gobj= Geometry::create_from_wkb(&buffer,
|
||||
blob + SRID_SIZE, blob_length - SRID_SIZE);
|
||||
if (gobj->get_mbr(&mbr, &dummy))
|
||||
@ -5554,7 +5554,7 @@ uint32 calc_pack_length(enum_field_types type,uint32 length)
|
||||
switch (type) {
|
||||
case FIELD_TYPE_STRING:
|
||||
case FIELD_TYPE_DECIMAL: return (length);
|
||||
case FIELD_TYPE_VAR_STRING: return (length+2);
|
||||
case FIELD_TYPE_VAR_STRING: return (length+HA_KEY_BLOB_LENGTH);
|
||||
case FIELD_TYPE_YEAR:
|
||||
case FIELD_TYPE_TINY : return 1;
|
||||
case FIELD_TYPE_SHORT : return 2;
|
||||
|
@ -212,7 +212,8 @@ public:
|
||||
{ memcpy(buff,ptr,length); }
|
||||
inline void set_image(char *buff,uint length, CHARSET_INFO *cs)
|
||||
{ memcpy(ptr,buff,length); }
|
||||
virtual void get_key_image(char *buff,uint length, CHARSET_INFO *cs, imagetype type)
|
||||
virtual void get_key_image(char *buff,uint length, CHARSET_INFO *cs,
|
||||
imagetype type)
|
||||
{ get_image(buff,length,cs); }
|
||||
virtual void set_key_image(char *buff,uint length, CHARSET_INFO *cs)
|
||||
{ set_image(buff,length,cs); }
|
||||
|
@ -867,6 +867,8 @@ void ha_myisam::start_bulk_insert(ha_rows rows)
|
||||
THD *thd=current_thd;
|
||||
ulong size= min(thd->variables.read_buff_size, table->avg_row_length*rows);
|
||||
|
||||
/* don't enable row cache if too few rows */
|
||||
if (!rows && rows > MI_MIN_ROWS_TO_USE_WRITE_CACHE)
|
||||
mi_extra(file, HA_EXTRA_WRITE_CACHE, (void*) &size);
|
||||
|
||||
can_enable_indexes= (file->s->state.key_map ==
|
||||
|
138
sql/handler.cc
138
sql/handler.cc
@ -1287,3 +1287,141 @@ int ha_change_key_cache(KEY_CACHE *old_key_cache,
|
||||
mi_change_key_cache(old_key_cache, new_key_cache);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Read first row between two ranges.
|
||||
Store ranges for future calls to read_range_next
|
||||
|
||||
SYNOPSIS
|
||||
read_range_first()
|
||||
start_key Start key. Is 0 if no min range
|
||||
end_key End key. Is 0 if no max range
|
||||
sorted Set to 1 if result should be sorted per key
|
||||
|
||||
NOTES
|
||||
Record is read into table->record[0]
|
||||
|
||||
RETURN
|
||||
0 Found row
|
||||
HA_ERR_END_OF_FILE No rows in range
|
||||
# Error code
|
||||
*/
|
||||
|
||||
int handler::read_range_first(const key_range *start_key,
|
||||
const key_range *end_key,
|
||||
bool sorted)
|
||||
{
|
||||
int result;
|
||||
DBUG_ENTER("handler::read_range_first");
|
||||
|
||||
end_range= 0;
|
||||
if (end_key)
|
||||
{
|
||||
end_range= &save_end_range;
|
||||
save_end_range= *end_key;
|
||||
key_compare_result_on_equal= ((end_key->flag == HA_READ_BEFORE_KEY) ? 1 :
|
||||
(end_key->flag == HA_READ_AFTER_KEY) ? -1 : 0);
|
||||
}
|
||||
range_key_part= table->key_info[active_index].key_part;
|
||||
|
||||
|
||||
if (!start_key) // Read first record
|
||||
result= index_first(table->record[0]);
|
||||
else
|
||||
result= index_read(table->record[0],
|
||||
start_key->key,
|
||||
start_key->length,
|
||||
start_key->flag);
|
||||
if (result)
|
||||
DBUG_RETURN((result == HA_ERR_KEY_NOT_FOUND ||
|
||||
result == HA_ERR_END_OF_FILE) ? HA_ERR_END_OF_FILE :
|
||||
result);
|
||||
|
||||
DBUG_RETURN (compare_key(end_range) <= 0 ? 0 : HA_ERR_END_OF_FILE);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Read next row between two ranges.
|
||||
|
||||
SYNOPSIS
|
||||
read_range_next()
|
||||
eq_range Set to 1 if start_key == end_key
|
||||
|
||||
NOTES
|
||||
Record is read into table->record[0]
|
||||
|
||||
RETURN
|
||||
0 Found row
|
||||
HA_ERR_END_OF_FILE No rows in range
|
||||
# Error code
|
||||
*/
|
||||
|
||||
int handler::read_range_next(bool eq_range)
|
||||
{
|
||||
int result;
|
||||
DBUG_ENTER("handler::read_range_next");
|
||||
|
||||
if (eq_range)
|
||||
result= index_next_same(table->record[0],
|
||||
end_range->key,
|
||||
end_range->length);
|
||||
else
|
||||
result= index_next(table->record[0]);
|
||||
if (result)
|
||||
DBUG_RETURN(result);
|
||||
DBUG_RETURN(compare_key(end_range) <= 0 ? 0 : HA_ERR_END_OF_FILE);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Compare if found key is over max-value
|
||||
|
||||
SYNOPSIS
|
||||
compare_key
|
||||
range key to compare to row
|
||||
|
||||
NOTES
|
||||
For this to work, the row must be stored in table->record[0]
|
||||
|
||||
RETURN
|
||||
0 Key is equal to range or 'range' == 0 (no range)
|
||||
-1 Key is less than range
|
||||
1 Key is larger than range
|
||||
*/
|
||||
|
||||
int handler::compare_key(key_range *range)
|
||||
{
|
||||
KEY_PART_INFO *key_part= range_key_part;
|
||||
uint store_length;
|
||||
|
||||
if (!range)
|
||||
return 0; // No max range
|
||||
|
||||
for (const char *key=range->key, *end=key+range->length;
|
||||
key < end;
|
||||
key+= store_length, key_part++)
|
||||
{
|
||||
int cmp;
|
||||
store_length= key_part->store_length;
|
||||
if (key_part->null_bit)
|
||||
{
|
||||
if (*key)
|
||||
{
|
||||
if (!key_part->field->is_null())
|
||||
return 1;
|
||||
continue;
|
||||
}
|
||||
else if (key_part->field->is_null())
|
||||
return 0;
|
||||
key++; // Skip null byte
|
||||
store_length--;
|
||||
}
|
||||
if ((cmp=key_part->field->key_cmp((byte*) key, key_part->length)) < 0)
|
||||
return -1;
|
||||
if (cmp > 0)
|
||||
return 1;
|
||||
}
|
||||
return key_compare_result_on_equal;
|
||||
}
|
||||
|
@ -218,6 +218,14 @@ typedef struct st_ha_check_opt
|
||||
} HA_CHECK_OPT;
|
||||
|
||||
|
||||
typedef struct st_key_range
|
||||
{
|
||||
const byte *key;
|
||||
uint length;
|
||||
enum ha_rkey_function flag;
|
||||
} key_range;
|
||||
|
||||
|
||||
class handler :public Sql_alloc
|
||||
{
|
||||
protected:
|
||||
@ -239,6 +247,12 @@ public:
|
||||
time_t create_time; /* When table was created */
|
||||
time_t check_time;
|
||||
time_t update_time;
|
||||
|
||||
/* The following are for read_range() */
|
||||
key_range save_end_range, *end_range;
|
||||
KEY_PART_INFO *range_key_part;
|
||||
int key_compare_result_on_equal;
|
||||
|
||||
uint errkey; /* Last dup key */
|
||||
uint sortkey, key_used_on_scan;
|
||||
uint active_index;
|
||||
@ -250,6 +264,7 @@ public:
|
||||
bool auto_increment_column_changed;
|
||||
bool implicit_emptied; /* Can be !=0 only if HEAP */
|
||||
|
||||
|
||||
handler(TABLE *table_arg) :table(table_arg),
|
||||
ref(0), data_file_length(0), max_data_file_length(0), index_file_length(0),
|
||||
delete_length(0), auto_increment_value(0),
|
||||
@ -298,6 +313,11 @@ public:
|
||||
{
|
||||
return (my_errno=HA_ERR_WRONG_COMMAND);
|
||||
}
|
||||
virtual int handler::read_range_first(const key_range *start_key,
|
||||
const key_range *end_key,
|
||||
bool sorted);
|
||||
virtual int handler::read_range_next(bool eq_range);
|
||||
int handler::compare_key(key_range *range);
|
||||
virtual int ft_init()
|
||||
{ return -1; }
|
||||
virtual FT_INFO *ft_init_ext(uint flags,uint inx,const byte *key, uint keylen)
|
||||
|
@ -516,19 +516,7 @@ int mysql_alter_table(THD *thd, char *new_db, char *new_name,
|
||||
List<create_field> &fields,
|
||||
List<Key> &keys,List<Alter_drop> &drop_list,
|
||||
List<Alter_column> &alter_list,
|
||||
uint order_num, ORDER *order, int alter_flags,
|
||||
enum enum_duplicates handle_duplicates,
|
||||
enum enum_enable_or_disable keys_onoff=LEAVE_AS_IS,
|
||||
enum tablespace_op_type tablespace_op=NO_TABLESPACE_OP,
|
||||
bool simple_alter=0);
|
||||
int real_alter_table(THD *thd, char *new_db, char *new_name,
|
||||
HA_CREATE_INFO *create_info,
|
||||
TABLE_LIST *table_list,
|
||||
TABLE *table,
|
||||
List<create_field> &fields,
|
||||
List<Key> &keys,List<Alter_drop> &drop_list,
|
||||
List<Alter_column> &alter_list,
|
||||
uint order_num, ORDER *order, int alter_flags,
|
||||
uint order_num, ORDER *order, uint alter_flags,
|
||||
enum enum_duplicates handle_duplicates,
|
||||
enum enum_enable_or_disable keys_onoff=LEAVE_AS_IS,
|
||||
enum tablespace_op_type tablespace_op=NO_TABLESPACE_OP,
|
||||
@ -544,10 +532,6 @@ bool mysql_rename_table(enum db_type base,
|
||||
int mysql_create_index(THD *thd, TABLE_LIST *table_list, List<Key> &keys);
|
||||
int mysql_drop_index(THD *thd, TABLE_LIST *table_list,
|
||||
List<Alter_drop> &drop_list);
|
||||
int mysql_add_column(THD *thd, TABLE_LIST *table_list,
|
||||
List<create_field> &fields);
|
||||
int mysql_drop_column(THD *thd, TABLE_LIST *table_list,
|
||||
List<Alter_drop> &drop_list);
|
||||
int mysql_update(THD *thd,TABLE_LIST *tables,List<Item> &fields,
|
||||
List<Item> &values,COND *conds,
|
||||
uint order_num, ORDER *order, ha_rows limit,
|
||||
@ -951,7 +935,8 @@ void unlock_table_names(THD *thd, TABLE_LIST *table_list,
|
||||
|
||||
void unireg_init(ulong options);
|
||||
void unireg_end(void);
|
||||
int mysql_create_frm(THD *thd, my_string file_name,HA_CREATE_INFO *create_info,
|
||||
bool mysql_create_frm(THD *thd, my_string file_name,
|
||||
HA_CREATE_INFO *create_info,
|
||||
List<create_field> &create_field,
|
||||
uint key_count,KEY *key_info,handler *db_type);
|
||||
int rea_create_table(THD *thd, my_string file_name,HA_CREATE_INFO *create_info,
|
||||
|
269
sql/opt_range.cc
269
sql/opt_range.cc
@ -177,11 +177,11 @@ public:
|
||||
if (maybe_null && *min_value)
|
||||
{
|
||||
**min_key=1;
|
||||
bzero(*min_key+1,length);
|
||||
bzero(*min_key+1,length-1);
|
||||
}
|
||||
else
|
||||
memcpy(*min_key,min_value,length+(int) maybe_null);
|
||||
(*min_key)+= length+(int) maybe_null;
|
||||
memcpy(*min_key,min_value,length);
|
||||
(*min_key)+= length;
|
||||
}
|
||||
if (!(max_flag & NO_MAX_RANGE) &&
|
||||
!(max_key_flag & (NO_MAX_RANGE | NEAR_MAX)))
|
||||
@ -189,18 +189,18 @@ public:
|
||||
if (maybe_null && *max_value)
|
||||
{
|
||||
**max_key=1;
|
||||
bzero(*max_key+1,length);
|
||||
bzero(*max_key+1,length-1);
|
||||
}
|
||||
else
|
||||
memcpy(*max_key,max_value,length+(int) maybe_null);
|
||||
(*max_key)+= length+(int) maybe_null;
|
||||
memcpy(*max_key,max_value,length);
|
||||
(*max_key)+= length;
|
||||
}
|
||||
}
|
||||
|
||||
void store_min_key(KEY_PART *key,char **range_key, uint *range_key_flag)
|
||||
{
|
||||
SEL_ARG *key_tree= first();
|
||||
key_tree->store(key[key_tree->part].part_length,
|
||||
key_tree->store(key[key_tree->part].store_length,
|
||||
range_key,*range_key_flag,range_key,NO_MAX_RANGE);
|
||||
*range_key_flag|= key_tree->min_flag;
|
||||
if (key_tree->next_key_part &&
|
||||
@ -213,7 +213,7 @@ public:
|
||||
void store_max_key(KEY_PART *key,char **range_key, uint *range_key_flag)
|
||||
{
|
||||
SEL_ARG *key_tree= last();
|
||||
key_tree->store(key[key_tree->part].part_length,
|
||||
key_tree->store(key[key_tree->part].store_length,
|
||||
range_key, NO_MIN_RANGE, range_key,*range_key_flag);
|
||||
(*range_key_flag)|= key_tree->max_flag;
|
||||
if (key_tree->next_key_part &&
|
||||
@ -646,6 +646,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
|
||||
MEM_ROOT *old_root,alloc;
|
||||
SEL_TREE *tree;
|
||||
KEY_PART *key_parts;
|
||||
KEY *key_info;
|
||||
PARAM param;
|
||||
|
||||
/* set up parameter that is passed to all functions */
|
||||
@ -671,24 +672,26 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
|
||||
old_root=my_pthread_getspecific_ptr(MEM_ROOT*,THR_MALLOC);
|
||||
my_pthread_setspecific_ptr(THR_MALLOC,&alloc);
|
||||
|
||||
for (idx=0 ; idx < head->keys ; idx++)
|
||||
key_info= head->key_info;
|
||||
for (idx=0 ; idx < head->keys ; idx++, key_info++)
|
||||
{
|
||||
KEY_PART_INFO *key_part_info;
|
||||
if (!keys_to_use.is_set(idx))
|
||||
continue;
|
||||
KEY *key_info= &head->key_info[idx];
|
||||
if (key_info->flags & HA_FULLTEXT)
|
||||
continue; // ToDo: ft-keys in non-ft ranges, if possible SerG
|
||||
|
||||
param.key[param.keys]=key_parts;
|
||||
for (uint part=0 ; part < key_info->key_parts ; part++,key_parts++)
|
||||
key_part_info= key_info->key_part;
|
||||
for (uint part=0 ; part < key_info->key_parts ;
|
||||
part++, key_parts++, key_part_info++)
|
||||
{
|
||||
key_parts->key= param.keys;
|
||||
key_parts->part= part;
|
||||
key_parts->part_length= key_info->key_part[part].length;
|
||||
key_parts->field= key_info->key_part[part].field;
|
||||
key_parts->null_bit= key_info->key_part[part].null_bit;
|
||||
if (key_parts->field->type() == FIELD_TYPE_BLOB)
|
||||
key_parts->part_length+=HA_KEY_BLOB_LENGTH;
|
||||
key_parts->length= key_part_info->length;
|
||||
key_parts->store_length= key_part_info->store_length;
|
||||
key_parts->field= key_part_info->field;
|
||||
key_parts->null_bit= key_part_info->null_bit;
|
||||
key_parts->image_type =
|
||||
(key_info->flags & HA_SPATIAL) ? Field::itMBR : Field::itRAW;
|
||||
}
|
||||
@ -1043,16 +1046,24 @@ get_mm_leaf(PARAM *param, COND *conf_func, Field *field, KEY_PART *key_part,
|
||||
DBUG_RETURN(0); // Can only optimize strings
|
||||
|
||||
offset=maybe_null;
|
||||
length=key_part->part_length;
|
||||
if (field->type() == FIELD_TYPE_BLOB)
|
||||
length=key_part->store_length;
|
||||
|
||||
if (length != key_part->length + maybe_null)
|
||||
{
|
||||
/* key packed with length prefix */
|
||||
offset+= HA_KEY_BLOB_LENGTH;
|
||||
field_length=key_part->part_length-HA_KEY_BLOB_LENGTH;
|
||||
field_length= length - HA_KEY_BLOB_LENGTH;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (length < field_length)
|
||||
length=field_length; // Only if overlapping key
|
||||
if (unlikely(length < field_length))
|
||||
{
|
||||
/*
|
||||
This can only happen in a table created with UNIREG where one key
|
||||
overlaps many fields
|
||||
*/
|
||||
length= field_length;
|
||||
}
|
||||
else
|
||||
field_length= length;
|
||||
}
|
||||
@ -1067,7 +1078,7 @@ get_mm_leaf(PARAM *param, COND *conf_func, Field *field, KEY_PART *key_part,
|
||||
res->ptr(), res->length(),
|
||||
((Item_func_like*)(param->cond))->escape,
|
||||
wild_one, wild_many,
|
||||
field_length,
|
||||
field_length-maybe_null,
|
||||
min_str+offset, max_str+offset,
|
||||
&min_length, &max_length);
|
||||
if (like_error) // Can't optimize with LIKE
|
||||
@ -1105,12 +1116,12 @@ get_mm_leaf(PARAM *param, COND *conf_func, Field *field, KEY_PART *key_part,
|
||||
if (field->key_type() == HA_KEYTYPE_VARTEXT)
|
||||
copies= 2;
|
||||
str= str2= (char*) alloc_root(param->mem_root,
|
||||
(key_part->part_length+maybe_null)*copies+1);
|
||||
(key_part->store_length)*copies+1);
|
||||
if (!str)
|
||||
DBUG_RETURN(0);
|
||||
if (maybe_null)
|
||||
*str= (char) field->is_real_null(); // Set to 1 if null
|
||||
field->get_key_image(str+maybe_null,key_part->part_length,
|
||||
field->get_key_image(str+maybe_null, key_part->length,
|
||||
field->charset(), key_part->image_type);
|
||||
if (copies == 2)
|
||||
{
|
||||
@ -1120,16 +1131,17 @@ get_mm_leaf(PARAM *param, COND *conf_func, Field *field, KEY_PART *key_part,
|
||||
all rows between 'X' and 'X ...'
|
||||
*/
|
||||
uint length= uint2korr(str+maybe_null);
|
||||
str2= str+ key_part->part_length + maybe_null;
|
||||
str2= str+ key_part->store_length;
|
||||
/* remove end space */
|
||||
while (length > 0 && str[length+HA_KEY_BLOB_LENGTH+maybe_null-1] == ' ')
|
||||
length--;
|
||||
int2store(str+maybe_null, length);
|
||||
/* Create key that is space filled */
|
||||
memcpy(str2, str, length + HA_KEY_BLOB_LENGTH + maybe_null);
|
||||
bfill(str2+ length+ HA_KEY_BLOB_LENGTH +maybe_null,
|
||||
key_part->part_length-length - HA_KEY_BLOB_LENGTH, ' ');
|
||||
int2store(str2+maybe_null, key_part->part_length - HA_KEY_BLOB_LENGTH);
|
||||
my_fill_8bit(field->charset(),
|
||||
str2+ length+ HA_KEY_BLOB_LENGTH +maybe_null,
|
||||
key_part->length-length, ' ');
|
||||
int2store(str2+maybe_null, key_part->length);
|
||||
}
|
||||
if (!(tree=new SEL_ARG(field,str,str2)))
|
||||
DBUG_RETURN(0); // out of memory
|
||||
@ -2236,7 +2248,7 @@ check_quick_keys(PARAM *param,uint idx,SEL_ARG *key_tree,
|
||||
uint tmp_min_flag,tmp_max_flag,keynr;
|
||||
char *tmp_min_key=min_key,*tmp_max_key=max_key;
|
||||
|
||||
key_tree->store(param->key[idx][key_tree->part].part_length,
|
||||
key_tree->store(param->key[idx][key_tree->part].store_length,
|
||||
&tmp_min_key,min_key_flag,&tmp_max_key,max_key_flag);
|
||||
uint min_key_length= (uint) (tmp_min_key- param->min_key);
|
||||
uint max_key_length= (uint) (tmp_max_key- param->max_key);
|
||||
@ -2332,8 +2344,14 @@ get_quick_select(PARAM *param,uint idx,SEL_ARG *key_tree)
|
||||
{
|
||||
QUICK_SELECT *quick;
|
||||
DBUG_ENTER("get_quick_select");
|
||||
if ((quick=new QUICK_SELECT(param->thd, param->table,
|
||||
param->real_keynr[idx])))
|
||||
|
||||
if (param->table->key_info[param->real_keynr[idx]].flags & HA_SPATIAL)
|
||||
quick=new QUICK_SELECT_GEOM(param->thd, param->table, param->real_keynr[idx],
|
||||
0);
|
||||
else
|
||||
quick=new QUICK_SELECT(param->thd, param->table, param->real_keynr[idx]);
|
||||
|
||||
if (quick)
|
||||
{
|
||||
if (quick->error ||
|
||||
get_quick_keys(param,quick,param->key[idx],key_tree,param->min_key,0,
|
||||
@ -2373,7 +2391,7 @@ get_quick_keys(PARAM *param,QUICK_SELECT *quick,KEY_PART *key,
|
||||
return 1;
|
||||
}
|
||||
char *tmp_min_key=min_key,*tmp_max_key=max_key;
|
||||
key_tree->store(key[key_tree->part].part_length,
|
||||
key_tree->store(key[key_tree->part].store_length,
|
||||
&tmp_min_key,min_key_flag,&tmp_max_key,max_key_flag);
|
||||
|
||||
if (key_tree->next_key_part &&
|
||||
@ -2491,19 +2509,17 @@ static bool null_part_in_key(KEY_PART *key_part, const char *key, uint length)
|
||||
{
|
||||
for (const char *end=key+length ;
|
||||
key < end;
|
||||
key+= key_part++->part_length)
|
||||
key+= key_part++->store_length)
|
||||
{
|
||||
if (key_part->null_bit)
|
||||
{
|
||||
if (*key++)
|
||||
if (key_part->null_bit && *key)
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
** Create a QUICK RANGE based on a key
|
||||
Create a QUICK RANGE based on a key
|
||||
****************************************************************************/
|
||||
|
||||
QUICK_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table, TABLE_REF *ref)
|
||||
@ -2541,9 +2557,8 @@ QUICK_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table, TABLE_REF *ref)
|
||||
{
|
||||
key_part->part=part;
|
||||
key_part->field= key_info->key_part[part].field;
|
||||
key_part->part_length= key_info->key_part[part].length;
|
||||
if (key_part->field->type() == FIELD_TYPE_BLOB)
|
||||
key_part->part_length+=HA_KEY_BLOB_LENGTH;
|
||||
key_part->length= key_info->key_part[part].length;
|
||||
key_part->store_length= key_info->key_part[part].store_length;
|
||||
key_part->null_bit= key_info->key_part[part].null_bit;
|
||||
}
|
||||
if (quick->ranges.push_back(range))
|
||||
@ -2585,111 +2600,74 @@ int QUICK_SELECT::get_next()
|
||||
for (;;)
|
||||
{
|
||||
int result;
|
||||
key_range start_key, end_key;
|
||||
if (range)
|
||||
{ // Already read through key
|
||||
result=((range->flag & (EQ_RANGE | GEOM_FLAG)) ?
|
||||
file->index_next_same(record, (byte*) range->min_key,
|
||||
range->min_length) :
|
||||
file->index_next(record));
|
||||
|
||||
if (!result)
|
||||
{
|
||||
if ((range->flag & GEOM_FLAG) || !cmp_next(*it.ref()))
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
else if (result != HA_ERR_END_OF_FILE)
|
||||
// Already read through key
|
||||
result= file->read_range_next(test(range->flag & EQ_RANGE));
|
||||
if (result != HA_ERR_END_OF_FILE)
|
||||
DBUG_RETURN(result);
|
||||
}
|
||||
|
||||
if (!(range= it++))
|
||||
DBUG_RETURN(HA_ERR_END_OF_FILE); // All ranges used
|
||||
|
||||
if (range->flag & GEOM_FLAG)
|
||||
{
|
||||
if ((result = file->index_read(record,
|
||||
(byte*) (range->min_key),
|
||||
range->min_length,
|
||||
(ha_rkey_function)(range->flag ^
|
||||
GEOM_FLAG))))
|
||||
{
|
||||
if (result != HA_ERR_KEY_NOT_FOUND)
|
||||
DBUG_RETURN(result);
|
||||
range=0; // Not found, to next range
|
||||
continue;
|
||||
}
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
if (range->flag & NO_MIN_RANGE) // Read first record
|
||||
{
|
||||
int local_error;
|
||||
if ((local_error=file->index_first(record)))
|
||||
DBUG_RETURN(local_error); // Empty table
|
||||
if (cmp_next(range) == 0)
|
||||
DBUG_RETURN(0);
|
||||
range=0; // No matching records; go to next range
|
||||
continue;
|
||||
}
|
||||
if ((result = file->index_read(record,
|
||||
(byte*) (range->min_key +
|
||||
test(range->flag & GEOM_FLAG)),
|
||||
range->min_length,
|
||||
(range->flag & NEAR_MIN) ?
|
||||
HA_READ_AFTER_KEY:
|
||||
start_key.key= range->min_key;
|
||||
start_key.length= range->min_length;
|
||||
start_key.flag= ((range->flag & NEAR_MIN) ? HA_READ_AFTER_KEY :
|
||||
(range->flag & EQ_RANGE) ?
|
||||
HA_READ_KEY_EXACT :
|
||||
HA_READ_KEY_OR_NEXT)))
|
||||
HA_READ_KEY_EXACT : HA_READ_KEY_OR_NEXT);
|
||||
end_key.key= range->max_key;
|
||||
end_key.length= range->max_length;
|
||||
/*
|
||||
We use READ_AFTER_KEY here because if we are reading on a key
|
||||
prefix we want to find all keys with this prefix
|
||||
*/
|
||||
end_key.flag= (range->flag & NEAR_MAX ? HA_READ_BEFORE_KEY :
|
||||
HA_READ_AFTER_KEY);
|
||||
|
||||
{
|
||||
if (result != HA_ERR_KEY_NOT_FOUND)
|
||||
DBUG_RETURN(result);
|
||||
range=0; // Not found, to next range
|
||||
continue;
|
||||
}
|
||||
if (cmp_next(range) == 0)
|
||||
{
|
||||
result= file->read_range_first(range->min_length ? &start_key : 0,
|
||||
range->max_length ? &end_key : 0,
|
||||
sorted);
|
||||
if (range->flag == (UNIQUE_RANGE | EQ_RANGE))
|
||||
range=0; // Stop searching
|
||||
DBUG_RETURN(0); // Found key is in range
|
||||
}
|
||||
range=0; // To next range
|
||||
|
||||
if (result != HA_ERR_END_OF_FILE)
|
||||
DBUG_RETURN(result);
|
||||
range=0; // No matching rows; go to next range
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Compare if found key is over max-value
|
||||
Returns 0 if key <= range->max_key
|
||||
*/
|
||||
/* Get next for geometrical indexes */
|
||||
|
||||
int QUICK_SELECT::cmp_next(QUICK_RANGE *range_arg)
|
||||
int QUICK_SELECT_GEOM::get_next()
|
||||
{
|
||||
if (range_arg->flag & NO_MAX_RANGE)
|
||||
return 0; /* key can't be to large */
|
||||
DBUG_ENTER(" QUICK_SELECT_GEOM::get_next");
|
||||
|
||||
KEY_PART *key_part=key_parts;
|
||||
for (char *key=range_arg->max_key, *end=key+range_arg->max_length;
|
||||
key < end;
|
||||
key+= key_part++->part_length)
|
||||
for (;;)
|
||||
{
|
||||
int cmp;
|
||||
if (key_part->null_bit)
|
||||
int result;
|
||||
if (range)
|
||||
{
|
||||
if (*key++)
|
||||
{
|
||||
if (!key_part->field->is_null())
|
||||
return 1;
|
||||
continue;
|
||||
// Already read through key
|
||||
result= file->index_next_same(record, (byte*) range->min_key,
|
||||
range->min_length);
|
||||
if (result != HA_ERR_END_OF_FILE)
|
||||
DBUG_RETURN(result);
|
||||
}
|
||||
else if (key_part->field->is_null())
|
||||
return 0;
|
||||
|
||||
if (!(range= it++))
|
||||
DBUG_RETURN(HA_ERR_END_OF_FILE); // All ranges used
|
||||
|
||||
result= file->index_read(record,
|
||||
(byte*) range->min_key,
|
||||
range->min_length,
|
||||
(ha_rkey_function)(range->flag ^ GEOM_FLAG));
|
||||
if (result != HA_ERR_KEY_NOT_FOUND)
|
||||
DBUG_RETURN(result);
|
||||
range=0; // Not found, to next range
|
||||
}
|
||||
if ((cmp=key_part->field->key_cmp((byte*) key, key_part->part_length)) < 0)
|
||||
return 0;
|
||||
if (cmp > 0)
|
||||
return 1;
|
||||
}
|
||||
return (range_arg->flag & NEAR_MAX) ? 1 : 0; // Exact match
|
||||
}
|
||||
|
||||
|
||||
@ -2841,15 +2819,18 @@ int QUICK_SELECT_DESC::cmp_prev(QUICK_RANGE *range_arg)
|
||||
return 0; /* key can't be to small */
|
||||
|
||||
KEY_PART *key_part = key_parts;
|
||||
uint store_length;
|
||||
|
||||
for (char *key = range_arg->min_key, *end = key + range_arg->min_length;
|
||||
key < end;
|
||||
key += key_part++->part_length)
|
||||
key += store_length, key_part++)
|
||||
{
|
||||
int cmp;
|
||||
store_length= key_part->store_length;
|
||||
if (key_part->null_bit)
|
||||
{
|
||||
// this key part allows null values; NULL is lower than everything else
|
||||
if (*key++)
|
||||
if (*key)
|
||||
{
|
||||
// the range is expecting a null value
|
||||
if (!key_part->field->is_null())
|
||||
@ -2858,9 +2839,11 @@ int QUICK_SELECT_DESC::cmp_prev(QUICK_RANGE *range_arg)
|
||||
}
|
||||
else if (key_part->field->is_null())
|
||||
return 1; // null -- outside the range
|
||||
key++;
|
||||
store_length--;
|
||||
}
|
||||
if ((cmp = key_part->field->key_cmp((byte*) key,
|
||||
key_part->part_length)) > 0)
|
||||
key_part->length)) > 0)
|
||||
return 0;
|
||||
if (cmp < 0)
|
||||
return 1;
|
||||
@ -2894,17 +2877,14 @@ bool QUICK_SELECT_DESC::test_if_null_range(QUICK_RANGE *range_arg,
|
||||
|
||||
for (offset= 0, end = min(range_arg->min_length, range_arg->max_length) ;
|
||||
offset < end && key_part != key_part_end ;
|
||||
offset += key_part++->part_length)
|
||||
offset+= key_part++->store_length)
|
||||
{
|
||||
uint null_length=test(key_part->null_bit);
|
||||
if (!memcmp((char*) range_arg->min_key+offset,
|
||||
(char*) range_arg->max_key+offset,
|
||||
key_part->part_length + null_length))
|
||||
{
|
||||
offset+=null_length;
|
||||
key_part->store_length))
|
||||
continue;
|
||||
}
|
||||
if (null_length && range_arg->min_key[offset])
|
||||
|
||||
if (key_part->null_bit && range_arg->min_key[offset])
|
||||
return 1; // min_key is null and max_key isn't
|
||||
// Range doesn't cover NULL. This is ok if there is no more null parts
|
||||
break;
|
||||
@ -2946,33 +2926,34 @@ static void
|
||||
print_key(KEY_PART *key_part,const char *key,uint used_length)
|
||||
{
|
||||
char buff[1024];
|
||||
const char *key_end= key+used_length;
|
||||
String tmp(buff,sizeof(buff),&my_charset_bin);
|
||||
uint store_length;
|
||||
|
||||
for (uint length=0;
|
||||
length < used_length ;
|
||||
length+=key_part->part_length, key+=key_part->part_length, key_part++)
|
||||
for (; key < key_end; key+=store_length, key_part++)
|
||||
{
|
||||
Field *field= key_part->field;
|
||||
if (length != 0)
|
||||
fputc('/',DBUG_FILE);
|
||||
store_length= key_part->store_length;
|
||||
|
||||
if (field->real_maybe_null())
|
||||
{
|
||||
length++; // null byte is not in part_length
|
||||
if (*key++)
|
||||
if (*key)
|
||||
{
|
||||
fwrite("NULL",sizeof(char),4,DBUG_FILE);
|
||||
continue;
|
||||
}
|
||||
key++; // Skip null byte
|
||||
store_length--;
|
||||
}
|
||||
field->set_key_image((char*) key,key_part->part_length -
|
||||
((field->type() == FIELD_TYPE_BLOB) ?
|
||||
HA_KEY_BLOB_LENGTH : 0),
|
||||
field->charset());
|
||||
field->set_key_image((char*) key, key_part->length, field->charset());
|
||||
field->val_str(&tmp);
|
||||
fwrite(tmp.ptr(),sizeof(char),tmp.length(),DBUG_FILE);
|
||||
if (key+store_length < key_end)
|
||||
fputc('/',DBUG_FILE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void print_quick(QUICK_SELECT *quick,const key_map* needed_reg)
|
||||
{
|
||||
QUICK_RANGE *range;
|
||||
|
@ -35,7 +35,7 @@
|
||||
|
||||
|
||||
typedef struct st_key_part {
|
||||
uint16 key,part,part_length;
|
||||
uint16 key,part, store_length, length;
|
||||
uint8 null_bit;
|
||||
Field *field;
|
||||
Field::imagetype image_type;
|
||||
@ -68,7 +68,7 @@ class QUICK_RANGE :public Sql_alloc {
|
||||
|
||||
class QUICK_SELECT {
|
||||
public:
|
||||
bool next,dont_free;
|
||||
bool next,dont_free,sorted;
|
||||
int error;
|
||||
uint index, max_used_key_length, used_key_parts;
|
||||
TABLE *head;
|
||||
@ -89,11 +89,20 @@ public:
|
||||
int init() { return error=file->index_init(index); }
|
||||
virtual int get_next();
|
||||
virtual bool reverse_sorted() { return 0; }
|
||||
int cmp_next(QUICK_RANGE *range);
|
||||
bool unique_key_range();
|
||||
};
|
||||
|
||||
|
||||
class QUICK_SELECT_GEOM: public QUICK_SELECT
|
||||
{
|
||||
public:
|
||||
QUICK_SELECT_GEOM(THD *thd, TABLE *table, uint index_arg, bool no_alloc)
|
||||
:QUICK_SELECT(thd, table, index_arg, no_alloc)
|
||||
{};
|
||||
virtual int get_next();
|
||||
};
|
||||
|
||||
|
||||
class QUICK_SELECT_DESC: public QUICK_SELECT
|
||||
{
|
||||
public:
|
||||
|
@ -260,6 +260,14 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
|
||||
thd->proc_info="update";
|
||||
if (duplic != DUP_ERROR)
|
||||
table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
|
||||
/*
|
||||
let's *try* to start bulk inserts. It won't necessary
|
||||
start them as values_list.elements should be greater than
|
||||
some - handler dependent - threshold.
|
||||
So we call start_bulk_insert to perform nesessary checks on
|
||||
values_list.elements, and - if nothing else - to initialize
|
||||
the code to make the call of end_bulk_insert() below safe.
|
||||
*/
|
||||
if (lock_type != TL_WRITE_DELAYED)
|
||||
table->file->start_bulk_insert(values_list.elements);
|
||||
|
||||
|
@ -4907,6 +4907,52 @@ Item * all_any_subquery_creator(Item *left_expr,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
CREATE INDEX and DROP INDEX are implemented by calling ALTER TABLE with
|
||||
the proper arguments. This isn't very fast but it should work for most
|
||||
cases.
|
||||
|
||||
In the future ALTER TABLE will notice that only added indexes
|
||||
and create these one by one for the existing table without having to do
|
||||
a full rebuild.
|
||||
|
||||
One should normally create all indexes with CREATE TABLE or ALTER TABLE.
|
||||
*/
|
||||
|
||||
int mysql_create_index(THD *thd, TABLE_LIST *table_list, List<Key> &keys)
|
||||
{
|
||||
List<create_field> fields;
|
||||
List<Alter_drop> drop;
|
||||
List<Alter_column> alter;
|
||||
HA_CREATE_INFO create_info;
|
||||
DBUG_ENTER("mysql_create_index");
|
||||
bzero((char*) &create_info,sizeof(create_info));
|
||||
create_info.db_type=DB_TYPE_DEFAULT;
|
||||
create_info.default_table_charset= thd->variables.collation_database;
|
||||
DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->real_name,
|
||||
&create_info, table_list,
|
||||
fields, keys, drop, alter, 0, (ORDER*)0,
|
||||
ALTER_ADD_INDEX, DUP_ERROR));
|
||||
}
|
||||
|
||||
|
||||
int mysql_drop_index(THD *thd, TABLE_LIST *table_list, List<Alter_drop> &drop)
|
||||
{
|
||||
List<create_field> fields;
|
||||
List<Key> keys;
|
||||
List<Alter_column> alter;
|
||||
HA_CREATE_INFO create_info;
|
||||
DBUG_ENTER("mysql_drop_index");
|
||||
bzero((char*) &create_info,sizeof(create_info));
|
||||
create_info.db_type=DB_TYPE_DEFAULT;
|
||||
create_info.default_table_charset= thd->variables.collation_database;
|
||||
DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->real_name,
|
||||
&create_info, table_list,
|
||||
fields, keys, drop, alter, 0, (ORDER*)0,
|
||||
ALTER_DROP_INDEX, DUP_ERROR));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Multi update query pre-check
|
||||
|
||||
|
@ -3746,7 +3746,8 @@ make_join_readinfo(JOIN *join, uint options)
|
||||
table->key_read=1;
|
||||
table->file->extra(HA_EXTRA_KEYREAD);
|
||||
}
|
||||
else if (!table->used_keys.is_clear_all() && ! (tab->select && tab->select->quick))
|
||||
else if (!table->used_keys.is_clear_all() &&
|
||||
!(tab->select && tab->select->quick))
|
||||
{ // Only read index tree
|
||||
tab->index=find_shortest_key(table, & table->used_keys);
|
||||
tab->table->file->index_init(tab->index);
|
||||
@ -6907,6 +6908,7 @@ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx,
|
||||
key_part_end=key_part+table->key_info[idx].key_parts;
|
||||
key_part_map const_key_parts=table->const_key_parts[idx];
|
||||
int reverse=0;
|
||||
DBUG_ENTER("test_if_order_by_key");
|
||||
|
||||
for (; order ; order=order->next, const_key_parts>>=1)
|
||||
{
|
||||
@ -6917,25 +6919,24 @@ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx,
|
||||
Skip key parts that are constants in the WHERE clause.
|
||||
These are already skipped in the ORDER BY by const_expression_in_where()
|
||||
*/
|
||||
while (const_key_parts & 1)
|
||||
{
|
||||
key_part++; const_key_parts>>=1;
|
||||
}
|
||||
for (; const_key_parts & 1 ; const_key_parts>>= 1)
|
||||
key_part++;
|
||||
|
||||
if (key_part == key_part_end || key_part->field != field)
|
||||
return 0;
|
||||
DBUG_RETURN(0);
|
||||
|
||||
/* set flag to 1 if we can use read-next on key, else to -1 */
|
||||
flag=(order->asc == !(key_part->key_part_flag & HA_REVERSE_SORT))
|
||||
? 1 : -1;
|
||||
flag= ((order->asc == !(key_part->key_part_flag & HA_REVERSE_SORT)) ? 1 : -1);
|
||||
if (reverse && flag != reverse)
|
||||
return 0;
|
||||
DBUG_RETURN(0);
|
||||
reverse=flag; // Remember if reverse
|
||||
key_part++;
|
||||
}
|
||||
*used_key_parts= (uint) (key_part - table->key_info[idx].key_part);
|
||||
return reverse;
|
||||
DBUG_RETURN(reverse);
|
||||
}
|
||||
|
||||
|
||||
static uint find_shortest_key(TABLE *table, const key_map *usable_keys)
|
||||
{
|
||||
uint min_length= (uint) ~0;
|
||||
@ -6958,18 +6959,20 @@ static uint find_shortest_key(TABLE *table, const key_map *usable_keys)
|
||||
}
|
||||
|
||||
/*
|
||||
Test if a second key is the subkey of the first one.
|
||||
|
||||
SYNOPSIS
|
||||
is_subkey()
|
||||
key_part - first key parts
|
||||
ref_key_part - second key parts
|
||||
ref_key_part_end - last+1 part of the second key
|
||||
DESCRIPTION
|
||||
Test if a second key is the subkey of the first one.
|
||||
key_part First key parts
|
||||
ref_key_part Second key parts
|
||||
ref_key_part_end Last+1 part of the second key
|
||||
|
||||
NOTE
|
||||
Second key MUST be shorter than the first one.
|
||||
|
||||
RETURN
|
||||
1 - is the subkey
|
||||
0 - otherwise
|
||||
1 is a subkey
|
||||
0 no sub key
|
||||
*/
|
||||
|
||||
inline bool
|
||||
@ -6983,20 +6986,21 @@ is_subkey(KEY_PART_INFO *key_part, KEY_PART_INFO *ref_key_part,
|
||||
}
|
||||
|
||||
/*
|
||||
Test if we can use one of the 'usable_keys' instead of 'ref' key for sorting
|
||||
|
||||
SYNOPSIS
|
||||
test_if_subkey()
|
||||
ref - number of key, used for WHERE clause
|
||||
usable_keys - keys for testing
|
||||
DESCRIPTION
|
||||
Test if we can use one of the 'usable_keys' instead of 'ref' key.
|
||||
ref Number of key, used for WHERE clause
|
||||
usable_keys Keys for testing
|
||||
|
||||
RETURN
|
||||
MAX_KEY - if we can't use other key
|
||||
the number of found key - otherwise
|
||||
MAX_KEY If we can't use other key
|
||||
the number of found key Otherwise
|
||||
*/
|
||||
|
||||
static uint
|
||||
test_if_subkey(ORDER *order, TABLE *table, uint ref, uint ref_key_parts,
|
||||
const key_map& usable_keys)
|
||||
const key_map *usable_keys)
|
||||
{
|
||||
uint nr;
|
||||
uint min_length= (uint) ~0;
|
||||
@ -7007,7 +7011,7 @@ test_if_subkey(ORDER *order, TABLE *table, uint ref, uint ref_key_parts,
|
||||
|
||||
for (nr= 0 ; nr < table->keys ; nr++)
|
||||
{
|
||||
if (usable_keys.is_set(nr) &&
|
||||
if (usable_keys->is_set(nr) &&
|
||||
table->key_info[nr].key_length < min_length &&
|
||||
table->key_info[nr].key_parts >= ref_key_parts &&
|
||||
is_subkey(table->key_info[nr].key_part, ref_key_part,
|
||||
@ -7051,12 +7055,12 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
|
||||
if ((*tmp_order->item)->type() != Item::FIELD_ITEM)
|
||||
{
|
||||
usable_keys.clear_all();
|
||||
break;
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
usable_keys.intersect(
|
||||
((Item_field*) (*tmp_order->item))->field->part_of_sortkey);
|
||||
usable_keys.intersect(((Item_field*) (*tmp_order->item))->
|
||||
field->part_of_sortkey);
|
||||
if (usable_keys.is_clear_all())
|
||||
break; // No usable keys
|
||||
DBUG_RETURN(0); // No usable keys
|
||||
}
|
||||
|
||||
ref_key= -1;
|
||||
@ -7092,9 +7096,9 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
|
||||
keys
|
||||
*/
|
||||
if (table->used_keys.is_set(ref_key))
|
||||
usable_keys.merge(table->used_keys);
|
||||
usable_keys.intersect(table->used_keys);
|
||||
if ((new_ref_key= test_if_subkey(order, table, ref_key, ref_key_parts,
|
||||
usable_keys)) < MAX_KEY)
|
||||
&usable_keys)) < MAX_KEY)
|
||||
{
|
||||
/* Found key that can be used to retrieve data in sorted order */
|
||||
if (tab->ref.key >= 0)
|
||||
@ -7154,6 +7158,8 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
|
||||
/* fall through */
|
||||
}
|
||||
}
|
||||
else if (select && select->quick)
|
||||
select->quick->sorted= 1;
|
||||
DBUG_RETURN(1); /* No need to sort */
|
||||
}
|
||||
}
|
||||
@ -7292,9 +7298,9 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
|
||||
For impossible ranges (like when doing a lookup on NULL on a NOT NULL
|
||||
field, quick will contain an empty record set.
|
||||
*/
|
||||
if (!(select->quick= tab->type == JT_FT ?
|
||||
if (!(select->quick= (tab->type == JT_FT ?
|
||||
new FT_SELECT(thd, table, tab->ref.key) :
|
||||
get_quick_select_for_ref(thd, table, &tab->ref)))
|
||||
get_quick_select_for_ref(thd, table, &tab->ref))))
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
251
sql/sql_table.cc
251
sql/sql_table.cc
@ -289,15 +289,12 @@ int quick_rm_table(enum db_type base,const char *db,
|
||||
{
|
||||
char path[FN_REFLEN];
|
||||
int error=0;
|
||||
if (snprintf(path, sizeof(path), "%s/%s/%s%s",
|
||||
mysql_data_home, db, table_name, reg_ext)>= (int)sizeof(path))
|
||||
return 1;
|
||||
my_snprintf(path, sizeof(path), "%s/%s/%s%s",
|
||||
mysql_data_home, db, table_name, reg_ext);
|
||||
unpack_filename(path,path);
|
||||
if (my_delete(path,MYF(0)))
|
||||
error=1; /* purecov: inspected */
|
||||
if (snprintf(path, sizeof(path), "%s/%s/%s",
|
||||
mysql_data_home, db, table_name)>= (int)sizeof(path))
|
||||
return 1;
|
||||
my_snprintf(path, sizeof(path), "%s/%s/%s", mysql_data_home, db, table_name);
|
||||
unpack_filename(path,path);
|
||||
return ha_delete_table(base,path) || error;
|
||||
}
|
||||
@ -410,7 +407,7 @@ int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
|
||||
List<create_field> &fields,
|
||||
List<Key> &keys, bool tmp_table, uint &db_options,
|
||||
handler *file, KEY *&key_info_buffer,
|
||||
uint &key_count, int select_field_count)
|
||||
uint *key_count, int select_field_count)
|
||||
{
|
||||
const char *key_name;
|
||||
create_field *sql_field,*dup_field;
|
||||
@ -650,7 +647,7 @@ int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
|
||||
uint tmp, key_number;
|
||||
|
||||
/* Calculate number of key segements */
|
||||
key_count=0;
|
||||
*key_count= 0;
|
||||
|
||||
while ((key=key_iterator++))
|
||||
{
|
||||
@ -668,7 +665,7 @@ int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
|
||||
}
|
||||
continue;
|
||||
}
|
||||
key_count++;
|
||||
(*key_count)++;
|
||||
tmp=max(file->max_key_parts(),MAX_REF_PARTS);
|
||||
if (key->columns.elements > tmp)
|
||||
{
|
||||
@ -689,13 +686,13 @@ int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
|
||||
}
|
||||
}
|
||||
tmp=min(file->max_keys(), MAX_KEY);
|
||||
if (key_count > tmp)
|
||||
if (*key_count > tmp)
|
||||
{
|
||||
my_error(ER_TOO_MANY_KEYS,MYF(0),tmp);
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
|
||||
key_info_buffer=key_info=(KEY*) sql_calloc(sizeof(KEY)*key_count);
|
||||
key_info_buffer=key_info=(KEY*) sql_calloc(sizeof(KEY)* *key_count);
|
||||
key_part_info=(KEY_PART_INFO*) sql_calloc(sizeof(KEY_PART_INFO)*key_parts);
|
||||
if (!key_info_buffer || ! key_part_info)
|
||||
DBUG_RETURN(-1); // Out of memory
|
||||
@ -907,9 +904,8 @@ int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
|
||||
{
|
||||
/* not a critical problem */
|
||||
char warn_buff[MYSQL_ERRMSG_SIZE];
|
||||
if (snprintf(warn_buff, sizeof(warn_buff), ER(ER_TOO_LONG_KEY),
|
||||
length)>= (int)sizeof(warn_buff))
|
||||
DBUG_RETURN(-1);
|
||||
my_snprintf(warn_buff, sizeof(warn_buff), ER(ER_TOO_LONG_KEY),
|
||||
length);
|
||||
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
|
||||
ER_TOO_LONG_KEY, warn_buff);
|
||||
}
|
||||
@ -946,9 +942,8 @@ int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
|
||||
{
|
||||
/* not a critical problem */
|
||||
char warn_buff[MYSQL_ERRMSG_SIZE];
|
||||
if (snprintf(warn_buff, sizeof(warn_buff), ER(ER_TOO_LONG_KEY),
|
||||
length)>= (int)sizeof(warn_buff))
|
||||
DBUG_RETURN(-1);
|
||||
my_snprintf(warn_buff, sizeof(warn_buff), ER(ER_TOO_LONG_KEY),
|
||||
length);
|
||||
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
|
||||
ER_TOO_LONG_KEY, warn_buff);
|
||||
}
|
||||
@ -1026,11 +1021,13 @@ int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
/* Sort keys in optimized order */
|
||||
qsort((gptr) key_info_buffer, key_count, sizeof(KEY), (qsort_cmp) sort_keys);
|
||||
qsort((gptr) key_info_buffer, *key_count, sizeof(KEY),
|
||||
(qsort_cmp) sort_keys);
|
||||
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Create a table
|
||||
|
||||
@ -1105,23 +1102,21 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name,
|
||||
|
||||
if (mysql_prepare_table(thd, create_info, fields,
|
||||
keys, tmp_table, db_options, file,
|
||||
key_info_buffer, key_count,
|
||||
key_info_buffer, &key_count,
|
||||
select_field_count))
|
||||
DBUG_RETURN(-1);
|
||||
|
||||
/* Check if table exists */
|
||||
if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
|
||||
{
|
||||
if (snprintf(path, sizeof(path), "%s%s%lx_%lx_%x%s",
|
||||
my_snprintf(path, sizeof(path), "%s%s%lx_%lx_%x%s",
|
||||
mysql_tmpdir, tmp_file_prefix, current_pid, thd->thread_id,
|
||||
thd->tmp_table++, reg_ext)>= (int)sizeof(path))
|
||||
DBUG_RETURN(-1);
|
||||
thd->tmp_table++, reg_ext);
|
||||
create_info->table_options|=HA_CREATE_DELAY_KEY_WRITE;
|
||||
}
|
||||
else
|
||||
if (snprintf(path, sizeof(path), "%s/%s/%s%s", mysql_data_home, db,
|
||||
alias, reg_ext)>= (int)sizeof(path))
|
||||
DBUG_RETURN(-1);
|
||||
my_snprintf(path, sizeof(path), "%s/%s/%s%s", mysql_data_home, db,
|
||||
alias, reg_ext);
|
||||
unpack_filename(path,path);
|
||||
/* Check if table already exists */
|
||||
if ((create_info->options & HA_LEX_CREATE_TMP_TABLE)
|
||||
@ -1215,25 +1210,27 @@ static char *
|
||||
make_unique_key_name(const char *field_name,KEY *start,KEY *end)
|
||||
{
|
||||
char buff[MAX_FIELD_NAME],*buff_end;
|
||||
int remain;
|
||||
|
||||
if (!check_if_keyname_exists(field_name,start,end) &&
|
||||
my_strcasecmp(system_charset_info,field_name,primary_key_name))
|
||||
return (char*) field_name; // Use fieldname
|
||||
buff_end=strmake(buff,field_name,MAX_FIELD_NAME-4);
|
||||
/*ingo 2004-04-07 only 3 chars + '\0' left, so need to limit to 2 digit*/
|
||||
buff_end=strmake(buff,field_name, sizeof(buff)-4);
|
||||
|
||||
/*
|
||||
Only 3 chars + '\0' left, so need to limit to 2 digit
|
||||
This is ok as we can't have more than 100 keys anyway
|
||||
*/
|
||||
for (uint i=2 ; i< 100; i++)
|
||||
{
|
||||
remain= (int)sizeof(buff)- (buff_end- buff);
|
||||
if (snprintf(buff_end, remain, "_%d", i)>= remain)
|
||||
return NULL;
|
||||
*buff_end= '_';
|
||||
int10_to_str(i, buff_end+1, 10);
|
||||
if (!check_if_keyname_exists(buff,start,end))
|
||||
return sql_strdup(buff);
|
||||
}
|
||||
/*ingo 2004-04-07 dedicated return is inevitable*/
|
||||
return NULL;
|
||||
return (char*) "not_specified"; // Should never happen
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
** Create table from a list of fields and items
|
||||
****************************************************************************/
|
||||
@ -1329,12 +1326,10 @@ mysql_rename_table(enum db_type base,
|
||||
my_casedn_str(system_charset_info, tmp_to);
|
||||
new_name= tmp_to;
|
||||
}
|
||||
if (snprintf(from, sizeof(from), "%s/%s/%s",
|
||||
mysql_data_home, old_db, old_name)>= (int)sizeof(from))
|
||||
DBUG_RETURN(1);
|
||||
if (snprintf(to, sizeof(to), "%s/%s/%s",
|
||||
mysql_data_home, new_db, new_name)>= (int)sizeof(to))
|
||||
DBUG_RETURN(1);
|
||||
my_snprintf(from, sizeof(from), "%s/%s/%s",
|
||||
mysql_data_home, old_db, old_name);
|
||||
my_snprintf(to, sizeof(to), "%s/%s/%s",
|
||||
mysql_data_home, new_db, new_name);
|
||||
fn_format(from,from,"","",4);
|
||||
fn_format(to,to, "","",4);
|
||||
|
||||
@ -1469,9 +1464,8 @@ static int prepare_for_restore(THD* thd, TABLE_LIST* table,
|
||||
reg_ext))
|
||||
DBUG_RETURN(-1); // protect buffer overflow
|
||||
|
||||
if (snprintf(dst_path, sizeof(dst_path), "%s/%s/%s",
|
||||
mysql_real_data_home, db, table_name)>= (int)sizeof(dst_path))
|
||||
DBUG_RETURN(-1);
|
||||
my_snprintf(dst_path, sizeof(dst_path), "%s/%s/%s",
|
||||
mysql_real_data_home, db, table_name);
|
||||
|
||||
if (lock_and_wait_for_table_name(thd,table))
|
||||
DBUG_RETURN(-1);
|
||||
@ -1555,12 +1549,8 @@ static int prepare_for_repair(THD* thd, TABLE_LIST *table_list,
|
||||
if (!my_stat(from, &stat_info, MYF(0)))
|
||||
goto end; // Can't use USE_FRM flag
|
||||
|
||||
if (snprintf(tmp, sizeof(tmp), "%s-%lx_%lx",
|
||||
from, current_pid, thd->thread_id)>= (int)sizeof(tmp))
|
||||
{
|
||||
error= -1;
|
||||
goto end;
|
||||
}
|
||||
my_snprintf(tmp, sizeof(tmp), "%s-%lx_%lx",
|
||||
from, current_pid, thd->thread_id);
|
||||
|
||||
/* If we could open the table, close it */
|
||||
if (table_list->table)
|
||||
@ -1695,9 +1685,7 @@ static int mysql_admin_table(THD* thd, TABLE_LIST* tables,
|
||||
protocol->store(table_name, system_charset_info);
|
||||
protocol->store(operator_name, system_charset_info);
|
||||
protocol->store("error", 5, system_charset_info);
|
||||
if (snprintf(buff, sizeof(buff), ER(ER_OPEN_AS_READONLY),
|
||||
table_name)>= (int)sizeof(buff))
|
||||
goto err;
|
||||
my_snprintf(buff, sizeof(buff), ER(ER_OPEN_AS_READONLY), table_name);
|
||||
protocol->store(buff, system_charset_info);
|
||||
close_thread_tables(thd);
|
||||
table->table=0; // For query cache
|
||||
@ -2019,11 +2007,9 @@ int mysql_create_like_table(THD* thd, TABLE_LIST* table,
|
||||
{
|
||||
if (find_temporary_table(thd, db, table_name))
|
||||
goto table_exists;
|
||||
if (snprintf(dst_path, sizeof(dst_path), "%s%s%lx_%lx_%x%s",
|
||||
my_snprintf(dst_path, sizeof(dst_path), "%s%s%lx_%lx_%x%s",
|
||||
mysql_tmpdir, tmp_file_prefix, current_pid,
|
||||
thd->thread_id, thd->tmp_table++, reg_ext)>=
|
||||
(int)sizeof(dst_path))
|
||||
DBUG_RETURN(-1);
|
||||
thd->thread_id, thd->tmp_table++, reg_ext);
|
||||
create_info->table_options|= HA_CREATE_DELAY_KEY_WRITE;
|
||||
}
|
||||
else
|
||||
@ -2081,9 +2067,8 @@ table_exists:
|
||||
if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
|
||||
{
|
||||
char warn_buff[MYSQL_ERRMSG_SIZE];
|
||||
if (snprintf(warn_buff, sizeof(warn_buff),
|
||||
ER(ER_TABLE_EXISTS_ERROR), table_name)>= (int)sizeof(warn_buff))
|
||||
DBUG_RETURN(-1);
|
||||
my_snprintf(warn_buff, sizeof(warn_buff),
|
||||
ER(ER_TABLE_EXISTS_ERROR), table_name);
|
||||
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
|
||||
ER_TABLE_EXISTS_ERROR,warn_buff);
|
||||
res= 0;
|
||||
@ -2194,6 +2179,8 @@ err:
|
||||
DBUG_RETURN(error);
|
||||
}
|
||||
|
||||
|
||||
#ifdef NOT_USED
|
||||
/*
|
||||
CREATE INDEX and DROP INDEX are implemented by calling ALTER TABLE with
|
||||
the proper arguments. This isn't very fast but it should work for most
|
||||
@ -2201,7 +2188,7 @@ err:
|
||||
One should normally create all indexes with CREATE TABLE or ALTER TABLE.
|
||||
*/
|
||||
|
||||
int mysql_create_index(THD *thd, TABLE_LIST *table_list, List<Key> &keys)
|
||||
int mysql_create_indexes(THD *thd, TABLE_LIST *table_list, List<Key> &keys)
|
||||
{
|
||||
List<create_field> fields;
|
||||
List<Alter_drop> drop;
|
||||
@ -2214,7 +2201,7 @@ int mysql_create_index(THD *thd, TABLE_LIST *table_list, List<Key> &keys)
|
||||
TABLE *table;
|
||||
Field **f_ptr;
|
||||
KEY *key_info_buffer;
|
||||
char path[FN_REFLEN];
|
||||
char path[FN_REFLEN+1];
|
||||
DBUG_ENTER("mysql_create_index");
|
||||
|
||||
/*
|
||||
@ -2272,13 +2259,13 @@ int mysql_create_index(THD *thd, TABLE_LIST *table_list, List<Key> &keys)
|
||||
&create_info, table_list, table,
|
||||
fields, keys, drop, alter, 0, (ORDER*)0,
|
||||
ALTER_ADD_INDEX, DUP_ERROR))
|
||||
/*don't need to free((gptr) key_info_buffer);*/
|
||||
/* Don't need to free((gptr) key_info_buffer);*/
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (table->file->add_index(table, key_info_buffer, key_count)||
|
||||
(snprintf(path, sizeof(path), "%s/%s/%s%s", mysql_data_home,
|
||||
(my_snprintf(path, sizeof(path), "%s/%s/%s%s", mysql_data_home,
|
||||
table_list->db, (lower_case_table_names == 2) ?
|
||||
table_list->alias: table_list->real_name, reg_ext) >=
|
||||
(int) sizeof(path)) ||
|
||||
@ -2288,13 +2275,13 @@ int mysql_create_index(THD *thd, TABLE_LIST *table_list, List<Key> &keys)
|
||||
/* don't need to free((gptr) key_info_buffer);*/
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
|
||||
/* don't need to free((gptr) key_info_buffer);*/
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
int mysql_drop_index(THD *thd, TABLE_LIST *table_list, List<Alter_drop> &drop)
|
||||
int mysql_drop_indexes(THD *thd, TABLE_LIST *table_list,
|
||||
List<Alter_drop> &drop)
|
||||
{
|
||||
List<create_field> fields;
|
||||
List<Key> keys;
|
||||
@ -2394,47 +2381,12 @@ int mysql_drop_index(THD *thd, TABLE_LIST *table_list, List<Alter_drop> &drop)
|
||||
/*don't need to free((gptr) key_numbers);*/
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
#endif /* NOT_USED */
|
||||
|
||||
int mysql_add_column(THD *thd, TABLE_LIST *table_list,
|
||||
List<create_field> &fields)
|
||||
{
|
||||
List<Alter_drop> drop;
|
||||
List<Key> keys;
|
||||
List<Alter_column> alter;
|
||||
HA_CREATE_INFO create_info;
|
||||
DBUG_ENTER("mysql_add_column");
|
||||
bzero((char*) &create_info,sizeof(create_info));
|
||||
create_info.db_type=DB_TYPE_DEFAULT;
|
||||
create_info.default_table_charset= thd->variables.collation_database;
|
||||
TABLE *table;
|
||||
if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ)))
|
||||
DBUG_RETURN(-1);
|
||||
|
||||
DBUG_RETURN(real_alter_table(thd,table_list->db,table_list->real_name,
|
||||
&create_info, table_list, table,
|
||||
fields, keys, drop, alter, 0, (ORDER*)0,
|
||||
ALTER_ADD_COLUMN, DUP_ERROR));
|
||||
}
|
||||
|
||||
int mysql_drop_column(THD *thd, TABLE_LIST *table_list, List<Alter_drop> &drop)
|
||||
{
|
||||
List<create_field> fields;
|
||||
List<Key> keys;
|
||||
List<Alter_column> alter;
|
||||
HA_CREATE_INFO create_info;
|
||||
DBUG_ENTER("mysql_drop_column");
|
||||
bzero((char*) &create_info,sizeof(create_info));
|
||||
create_info.db_type=DB_TYPE_DEFAULT;
|
||||
create_info.default_table_charset= thd->variables.collation_database;
|
||||
TABLE *table;
|
||||
if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ)))
|
||||
DBUG_RETURN(-1);
|
||||
|
||||
DBUG_RETURN(real_alter_table(thd,table_list->db,table_list->real_name,
|
||||
&create_info, table_list, table,
|
||||
fields, keys, drop, alter, 0, (ORDER*)0,
|
||||
ALTER_DROP_COLUMN, DUP_ERROR));
|
||||
}
|
||||
/*
|
||||
Alter table
|
||||
*/
|
||||
|
||||
int mysql_alter_table(THD *thd,char *new_db, char *new_name,
|
||||
HA_CREATE_INFO *create_info,
|
||||
@ -2442,69 +2394,22 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name,
|
||||
List<create_field> &fields,
|
||||
List<Key> &keys,List<Alter_drop> &drop_list,
|
||||
List<Alter_column> &alter_list,
|
||||
uint order_num, ORDER *order, int alter_flags,
|
||||
uint order_num, ORDER *order, uint alter_flags,
|
||||
enum enum_duplicates handle_duplicates,
|
||||
enum enum_enable_or_disable keys_onoff,
|
||||
enum tablespace_op_type tablespace_op,
|
||||
bool simple_alter)
|
||||
{
|
||||
DBUG_ENTER("mysql_alter_table");
|
||||
|
||||
mysql_ha_closeall(thd, table_list);
|
||||
|
||||
/* DISCARD/IMPORT TABLESPACE is always alone in an ALTER TABLE */
|
||||
if (tablespace_op != NO_TABLESPACE_OP)
|
||||
DBUG_RETURN(mysql_discard_or_import_tablespace(thd,table_list,
|
||||
tablespace_op));
|
||||
|
||||
if (alter_flags == ALTER_ADD_INDEX)
|
||||
DBUG_RETURN(mysql_create_index(thd, table_list, keys));
|
||||
|
||||
if (alter_flags == ALTER_DROP_INDEX)
|
||||
DBUG_RETURN(mysql_drop_index(thd, table_list, drop_list));
|
||||
|
||||
if (alter_flags == ALTER_ADD_COLUMN)
|
||||
DBUG_RETURN(mysql_add_column(thd, table_list, fields));
|
||||
|
||||
if (alter_flags == ALTER_DROP_COLUMN)
|
||||
DBUG_RETURN(mysql_drop_column(thd, table_list, drop_list));
|
||||
|
||||
TABLE *table;
|
||||
if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ)))
|
||||
DBUG_RETURN(-1);
|
||||
|
||||
DBUG_RETURN(real_alter_table(thd, new_db, new_name,
|
||||
create_info, table_list, table, fields,
|
||||
keys, drop_list, alter_list,
|
||||
order_num, order, alter_flags,
|
||||
handle_duplicates, keys_onoff,
|
||||
tablespace_op, simple_alter));
|
||||
}
|
||||
|
||||
int real_alter_table(THD *thd,char *new_db, char *new_name,
|
||||
HA_CREATE_INFO *create_info,
|
||||
TABLE_LIST *table_list,
|
||||
TABLE *table,
|
||||
List<create_field> &fields,
|
||||
List<Key> &keys,List<Alter_drop> &drop_list,
|
||||
List<Alter_column> &alter_list,
|
||||
uint order_num, ORDER *order, int alter_flags,
|
||||
enum enum_duplicates handle_duplicates,
|
||||
enum enum_enable_or_disable keys_onoff,
|
||||
enum tablespace_op_type tablespace_op,
|
||||
bool simple_alter)
|
||||
{
|
||||
TABLE *new_table;
|
||||
TABLE *table,*new_table;
|
||||
int error;
|
||||
char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN];
|
||||
char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias;
|
||||
char index_file[FN_REFLEN], data_file[FN_REFLEN];
|
||||
bool use_timestamp=0;
|
||||
ha_rows copied,deleted;
|
||||
ulonglong next_insert_id;
|
||||
uint db_create_options, used_fields;
|
||||
enum db_type old_db_type,new_db_type;
|
||||
DBUG_ENTER("real_alter_table");
|
||||
DBUG_ENTER("mysql_alter_table");
|
||||
|
||||
thd->proc_info="init";
|
||||
table_name=table_list->real_name;
|
||||
@ -2512,11 +2417,18 @@ int real_alter_table(THD *thd,char *new_db, char *new_name,
|
||||
|
||||
db=table_list->db;
|
||||
if (!new_db || !my_strcasecmp(table_alias_charset, new_db, db))
|
||||
{
|
||||
new_db= db;
|
||||
}
|
||||
used_fields=create_info->used_fields;
|
||||
|
||||
mysql_ha_closeall(thd, table_list);
|
||||
|
||||
/* DISCARD/IMPORT TABLESPACE is always alone in an ALTER TABLE */
|
||||
if (tablespace_op != NO_TABLESPACE_OP)
|
||||
DBUG_RETURN(mysql_discard_or_import_tablespace(thd,table_list,
|
||||
tablespace_op));
|
||||
if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ)))
|
||||
DBUG_RETURN(-1);
|
||||
|
||||
/* Check that we are not trying to rename to an existing table */
|
||||
if (new_name)
|
||||
{
|
||||
@ -2716,8 +2628,6 @@ int real_alter_table(THD *thd,char *new_db, char *new_name,
|
||||
if (def)
|
||||
{ // Field is changed
|
||||
def->field=field;
|
||||
if (def->sql_type == FIELD_TYPE_TIMESTAMP)
|
||||
use_timestamp=1;
|
||||
if (!def->after)
|
||||
{
|
||||
create_list.push_back(def);
|
||||
@ -2727,8 +2637,6 @@ int real_alter_table(THD *thd,char *new_db, char *new_name,
|
||||
else
|
||||
{ // Use old field value
|
||||
create_list.push_back(def=new create_field(field,field));
|
||||
if (def->sql_type == FIELD_TYPE_TIMESTAMP)
|
||||
use_timestamp=1;
|
||||
alter_it.rewind(); // Change default if ALTER
|
||||
Alter_column *alter;
|
||||
while ((alter=alter_it++))
|
||||
@ -2891,9 +2799,8 @@ int real_alter_table(THD *thd,char *new_db, char *new_name,
|
||||
}
|
||||
|
||||
db_create_options=table->db_create_options & ~(HA_OPTION_PACK_RECORD);
|
||||
if (snprintf(tmp_name, sizeof(tmp_name), "%s-%lx_%lx", tmp_file_prefix,
|
||||
current_pid, thd->thread_id)>= (int)sizeof(tmp_name))
|
||||
goto err;
|
||||
my_snprintf(tmp_name, sizeof(tmp_name), "%s-%lx_%lx", tmp_file_prefix,
|
||||
current_pid, thd->thread_id);
|
||||
create_info->db_type=new_db_type;
|
||||
if (!create_info->comment)
|
||||
create_info->comment=table->comment;
|
||||
@ -2972,9 +2879,8 @@ int real_alter_table(THD *thd,char *new_db, char *new_name,
|
||||
else
|
||||
{
|
||||
char path[FN_REFLEN];
|
||||
if (snprintf(path, sizeof(path), "%s/%s/%s", mysql_data_home,
|
||||
new_db, tmp_name)>= (int)sizeof(path))
|
||||
goto err;
|
||||
my_snprintf(path, sizeof(path), "%s/%s/%s", mysql_data_home,
|
||||
new_db, tmp_name);
|
||||
fn_format(path,path,"","",4);
|
||||
new_table=open_temporary_table(thd, path, new_db, tmp_name,0);
|
||||
}
|
||||
@ -3057,9 +2963,8 @@ int real_alter_table(THD *thd,char *new_db, char *new_name,
|
||||
*/
|
||||
|
||||
thd->proc_info="rename result table";
|
||||
if (snprintf(old_name, sizeof(old_name), "%s2-%lx-%lx", tmp_file_prefix,
|
||||
current_pid, thd->thread_id)>= (int)sizeof(old_name))
|
||||
goto err;
|
||||
my_snprintf(old_name, sizeof(old_name), "%s2-%lx-%lx", tmp_file_prefix,
|
||||
current_pid, thd->thread_id);
|
||||
if (new_name != table_name || new_db != db)
|
||||
{
|
||||
if (!access(new_name_buff,F_OK))
|
||||
@ -3182,9 +3087,8 @@ int real_alter_table(THD *thd,char *new_db, char *new_name,
|
||||
shutdown.
|
||||
*/
|
||||
char path[FN_REFLEN];
|
||||
if (snprintf(path, sizeof(path), "%s/%s/%s", mysql_data_home,
|
||||
new_db, table_name)>= (int)sizeof(path))
|
||||
goto err;
|
||||
my_snprintf(path, sizeof(path), "%s/%s/%s", mysql_data_home,
|
||||
new_db, table_name);
|
||||
fn_format(path,path,"","",4);
|
||||
table=open_temporary_table(thd, path, new_db, tmp_name,0);
|
||||
if (table)
|
||||
@ -3202,10 +3106,9 @@ int real_alter_table(THD *thd,char *new_db, char *new_name,
|
||||
query_cache_invalidate3(thd, table_list, 0);
|
||||
|
||||
end_temporary:
|
||||
if (snprintf(tmp_name, sizeof(tmp_name), ER(ER_INSERT_INFO),
|
||||
my_snprintf(tmp_name, sizeof(tmp_name), ER(ER_INSERT_INFO),
|
||||
(ulong) (copied + deleted), (ulong) deleted,
|
||||
(ulong) thd->cuted_fields)>= (int)sizeof(tmp_name))
|
||||
goto err;
|
||||
(ulong) thd->cuted_fields);
|
||||
send_ok(thd,copied+deleted,0L,tmp_name);
|
||||
thd->some_tables_deleted=0;
|
||||
DBUG_RETURN(0);
|
||||
|
@ -553,7 +553,6 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag,
|
||||
keyinfo->key_length+= HA_KEY_NULL_LENGTH;
|
||||
}
|
||||
if (field->type() == FIELD_TYPE_BLOB ||
|
||||
field->type() == FIELD_TYPE_GEOMETRY ||
|
||||
field->real_type() == FIELD_TYPE_VAR_STRING)
|
||||
{
|
||||
if (field->type() == FIELD_TYPE_BLOB)
|
||||
|
@ -46,8 +46,25 @@ static bool make_empty_rec(int file, enum db_type table_type,
|
||||
List<create_field> &create_fields,
|
||||
uint reclength,uint null_fields);
|
||||
|
||||
/*
|
||||
Create a frm (table definition) file
|
||||
|
||||
int mysql_create_frm(THD *thd, my_string file_name,
|
||||
SYNOPSIS
|
||||
mysql_create_frm()
|
||||
thd Thread handler
|
||||
file_name Name of file (including database and .frm)
|
||||
create_info create info parameters
|
||||
create_fields Fields to create
|
||||
keys number of keys to create
|
||||
key_info Keys to create
|
||||
db_file Handler to use. May be zero, in which case we use
|
||||
create_info->db_type
|
||||
RETURN
|
||||
0 ok
|
||||
1 error
|
||||
*/
|
||||
|
||||
bool mysql_create_frm(THD *thd, my_string file_name,
|
||||
HA_CREATE_INFO *create_info,
|
||||
List<create_field> &create_fields,
|
||||
uint keys, KEY *key_info,
|
||||
@ -166,9 +183,29 @@ err:
|
||||
err2:
|
||||
VOID(my_close(file,MYF(MY_WME)));
|
||||
err3:
|
||||
my_delete(file_name,MYF(0));
|
||||
DBUG_RETURN(1);
|
||||
} /* mysql_create_frm */
|
||||
|
||||
|
||||
/*
|
||||
Create a frm (table definition) file and the tables
|
||||
|
||||
SYNOPSIS
|
||||
mysql_create_frm()
|
||||
thd Thread handler
|
||||
file_name Name of file (including database and .frm)
|
||||
create_info create info parameters
|
||||
create_fields Fields to create
|
||||
keys number of keys to create
|
||||
key_info Keys to create
|
||||
db_file Handler to use. May be zero, in which case we use
|
||||
create_info->db_type
|
||||
RETURN
|
||||
0 ok
|
||||
1 error
|
||||
*/
|
||||
|
||||
int rea_create_table(THD *thd, my_string file_name,
|
||||
HA_CREATE_INFO *create_info,
|
||||
List<create_field> &create_fields,
|
||||
@ -179,12 +216,8 @@ int rea_create_table(THD *thd, my_string file_name,
|
||||
if (mysql_create_frm(thd, file_name, create_info,
|
||||
create_fields, keys, key_info, NULL) ||
|
||||
ha_create_table(file_name,create_info,0))
|
||||
goto err;
|
||||
DBUG_RETURN(0);
|
||||
|
||||
err:
|
||||
my_delete(file_name,MYF(0));
|
||||
DBUG_RETURN(1);
|
||||
DBUG_RETURN(0);
|
||||
} /* rea_create_table */
|
||||
|
||||
|
||||
|
@ -25,8 +25,9 @@
|
||||
|
||||
IMPLEMENTION:
|
||||
Supports following formats:
|
||||
%#d
|
||||
%#u
|
||||
%#[l]d
|
||||
%#[l]u
|
||||
%#[l]x
|
||||
%#.#s Note #.# is skiped
|
||||
|
||||
RETURN
|
||||
@ -47,7 +48,7 @@ int my_snprintf(char* to, size_t n, const char* fmt, ...)
|
||||
int my_vsnprintf(char *to, size_t n, const char* fmt, va_list ap)
|
||||
{
|
||||
char *start=to, *end=to+n-1;
|
||||
uint length, num_state, pre_zero;
|
||||
uint length, num_state, pre_zero, have_long;
|
||||
|
||||
for (; *fmt ; fmt++)
|
||||
{
|
||||
@ -62,7 +63,7 @@ int my_vsnprintf(char *to, size_t n, const char* fmt, va_list ap)
|
||||
/* Read max fill size (only used with %d and %u) */
|
||||
if (*fmt == '-')
|
||||
fmt++;
|
||||
length= num_state= pre_zero= 0;
|
||||
length= num_state= pre_zero= have_long= 0;
|
||||
for (;; fmt++)
|
||||
{
|
||||
if (my_isdigit(&my_charset_latin1,*fmt))
|
||||
@ -80,7 +81,10 @@ int my_vsnprintf(char *to, size_t n, const char* fmt, va_list ap)
|
||||
num_state= 1;
|
||||
}
|
||||
if (*fmt == 'l')
|
||||
{
|
||||
fmt++;
|
||||
have_long= 1;
|
||||
}
|
||||
if (*fmt == 's') /* String parameter */
|
||||
{
|
||||
reg2 char *par = va_arg(ap, char *);
|
||||
@ -92,20 +96,29 @@ int my_vsnprintf(char *to, size_t n, const char* fmt, va_list ap)
|
||||
to=strnmov(to,par,plen);
|
||||
continue;
|
||||
}
|
||||
else if (*fmt == 'd' || *fmt == 'u') /* Integer parameter */
|
||||
else if (*fmt == 'd' || *fmt == 'u'|| *fmt== 'x') /* Integer parameter */
|
||||
{
|
||||
register int iarg;
|
||||
register long larg;
|
||||
uint res_length, to_length;
|
||||
char *store_start= to, *store_end;
|
||||
char buff[16];
|
||||
char buff[32];
|
||||
|
||||
if ((to_length= (uint) (end-to)) < 16 || length)
|
||||
store_start= buff;
|
||||
iarg = va_arg(ap, int);
|
||||
if (*fmt == 'd')
|
||||
store_end= int10_to_str((long) iarg, store_start, -10);
|
||||
if (have_long)
|
||||
larg = va_arg(ap, long);
|
||||
else
|
||||
store_end= int10_to_str((long) (uint) iarg, store_start, 10);
|
||||
if (*fmt == 'd')
|
||||
larg = va_arg(ap, int);
|
||||
else
|
||||
larg= (long) (uint) va_arg(ap, int);
|
||||
if (*fmt == 'd')
|
||||
store_end= int10_to_str(larg, store_start, -10);
|
||||
else
|
||||
if (*fmt== 'u')
|
||||
store_end= int10_to_str(larg, store_start, 10);
|
||||
else
|
||||
store_end= int2str(larg, store_start, 16);
|
||||
if ((res_length= (uint) (store_end - store_start)) > to_length)
|
||||
break; /* num doesn't fit in output */
|
||||
/* If %#d syntax was used, we have to pre-zero/pre-space the string */
|
||||
@ -146,7 +159,7 @@ static void my_printf(const char * fmt, ...)
|
||||
n = my_vsnprintf(buf, sizeof(buf)-1,fmt, ar);
|
||||
printf(buf);
|
||||
printf("n=%d, strlen=%d\n", n, strlen(buf));
|
||||
if (buf[sizeof(buf)-1] != OVERRUN_SENTRY)
|
||||
if ((uchar) buf[sizeof(buf)-1] != OVERRUN_SENTRY)
|
||||
{
|
||||
fprintf(stderr, "Buffer overrun\n");
|
||||
abort();
|
||||
@ -167,6 +180,7 @@ int main()
|
||||
my_printf("Hello '%s' hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh\n", "hack");
|
||||
my_printf("Hello hhhhhhhhhhhhhh %d sssssssssssssss\n", 1);
|
||||
my_printf("Hello %u\n", 1);
|
||||
my_printf("Hex: %lx '%6lx'\n", 32, 65);
|
||||
my_printf("conn %ld to: '%-.64s' user: '%-.32s' host:\
|
||||
`%-.64s' (%-.64s)", 1, 0,0,0,0);
|
||||
return 0;
|
||||
|
Reference in New Issue
Block a user