Commit 0efb424e authored by monty@donna.mysql.com's avatar monty@donna.mysql.com

Bugfix when using a multi-part unique key in the SET and WHERE part

Put PRIMARY KEY and UNIQUE keys before other keys
Fixes for auto_increment keys for BDB tables
parent 0dd9ec5a
......@@ -17896,6 +17896,11 @@ tables as one. This only works with MERGE tables. @xref{MERGE}.
For the moment you need to have @code{SELECT}, @code{UPDATE}, and
@code{DELETE} privileges on the tables you map to a @code{MERGE} table.
All mapped tables must be in the same database as the @code{MERGE} table.
@item
In the created table the @code{PRIMARY} key will be placed first, followed
by all @code{UNIQUE} keys and then the normal keys. This helps the
@strong{MySQL} optimizer to prioritize which key to use and also more quickly
detect duplicated @code{UNIQUE} keys.
@end itemize
@cindex silent column changes
......@@ -22597,7 +22602,7 @@ You may also want to change @code{binlog_cache_size} and
@itemize @bullet
@item
@strong{MySQL} requires a @code{PRIMARY KEY} in each BDB table to be
able to refer to previously read rows. If you don't create on,
able to refer to previously read rows. If you don't create one,
@strong{MySQL} will create an maintain a hidden @code{PRIMARY KEY} for
you. The hidden key has a length of 5 bytes and is incremented for each
insert attempt.
......@@ -22617,8 +22622,6 @@ you don't use @code{LOCK TABLE}, @strong{MYSQL} will issue an internal
multiple-write lock on the table to ensure that the table will be
properly locked if another thread issues a table lock.
@item
@code{ALTER TABLE} doesn't yet work on @code{BDB} tables.
@item
Internal locking in @code{BDB} tables is done on page level.
@item
@code{SELECT COUNT(*) FROM table_name} is slow as @code{BDB} tables doesn't
......@@ -22636,8 +22639,8 @@ tables. In other words, the key information will take a little more
space in @code{BDB} tables compared to MyISAM tables which don't use
@code{PACK_KEYS=0}.
@item
There is often holes in the BDB table to allow you to insert new rows
between different keys. This makes BDB tables somewhat larger than
There is often holes in the BDB table to allow you to insert new rows in
the middle of the key tree. This makes BDB tables somewhat larger than
MyISAM tables.
@item
@strong{MySQL} performs a checkpoint each time a new Berkeley DB log
......@@ -39636,6 +39639,14 @@ though, so Version 3.23 is not released as a stable version yet.
@appendixsubsec Changes in release 3.23.29
@itemize @bullet
@item
When creating a table, put @code{PRIMARY} keys first, followed by
@code{UNIQUE} keys.
@item
Fixed a bug in @code{UPDATE} involving multi-part keys where one
specified all key parts both in the update and the @code{WHERE} part. In
this case @strong{MySQL} could try to update a record that didn't match
the whole @code{WHERE} part.
@item
Changed drop table to first drop the tables and then the @code{.frm} file.
@item
Fixed a bug in the hostname cache which caused @code{mysqld} to report the
......@@ -1660,7 +1660,8 @@ longlong ha_berkeley::get_auto_increment()
}
else
{
DBT row;
DBT row,old_key;
DBC *auto_cursor;
bzero((char*) &row,sizeof(row));
uint key_len;
KEY *key_info= &table->key_info[active_index];
......@@ -1670,27 +1671,37 @@ longlong ha_berkeley::get_auto_increment()
key_buff, table->record[0],
table->next_number_key_offset);
/* Store for compare */
memcpy(key_buff2, key_buff, (key_len=last_key.size));
/* Modify the compare so that we will find the next key */
key_info->handler.bdb_return_if_eq= 1;
/* We lock the next key as the new key will probl. be on the same page */
error=cursor->c_get(cursor, &last_key, &row, DB_SET_RANGE | DB_RMW),
key_info->handler.bdb_return_if_eq= 0;
if (!error || error == DB_NOTFOUND)
memcpy(old_key.data=key_buff2, key_buff, (old_key.size=last_key.size));
error=1;
if (!(file->cursor(key_file[active_index], transaction, &auto_cursor, 0)))
{
/*
Now search go one step back and then we should have found the
biggest key with the given prefix
*/
if (read_row(cursor->c_get(cursor, &last_key, &row, DB_PREV | DB_RMW),
table->record[1], active_index, &row, (DBT*) 0, 0) ||
berkeley_key_cmp(table, key_info, key_buff2, key_len))
error=1; // Something went wrong or no such key
/* Modify the compare so that we will find the next key */
key_info->handler.bdb_return_if_eq= 1;
/* We lock the next key as the new key will probl. be on the same page */
error=auto_cursor->c_get(auto_cursor, &last_key, &row,
DB_SET_RANGE | DB_RMW);
key_info->handler.bdb_return_if_eq= 0;
if (!error || error == DB_NOTFOUND)
{
/*
Now search go one step back and then we should have found the
biggest key with the given prefix
*/
error=1;
if (!auto_cursor->c_get(auto_cursor, &last_key, &row, DB_PREV | DB_RMW)
&& !berkeley_cmp_packed_key(key_file[active_index], &old_key,
&last_key))
{
error=0; // Found value
unpack_key(table->record[1], &last_key, active_index);
}
}
auto_cursor->c_close(auto_cursor);
}
}
nr=(longlong)
table->next_number_field->val_int_offset(table->rec_buff_length)+1;
if (!error)
nr=(longlong)
table->next_number_field->val_int_offset(table->rec_buff_length)+1;
ha_berkeley::index_end();
(void) ha_berkeley::extra(HA_EXTRA_NO_KEYREAD);
return nr;
......
......@@ -327,18 +327,28 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name,
}
/* Create keys */
List_iterator<Key> key_iterator(keys);
uint key_parts=0,key_count=keys.elements;
bool primary_key=0,unique_key=0;
List<Key> keys_in_order; // Add new keys here
Key *primary_key=0;
bool unique_key=0;
Key *key;
uint tmp;
tmp=min(file->max_keys(), MAX_KEY);
if (key_count > tmp)
{
my_error(ER_TOO_MANY_KEYS,MYF(0),tmp);
DBUG_RETURN(-1);
}
/*
Check keys;
Put PRIMARY KEY first, then UNIQUE keys and other keys last
This will make checking for duplicated keys faster and ensure that
primary keys are prioritized.
*/
while ((key=key_iterator++))
{
tmp=max(file->max_key_parts(),MAX_REF_PARTS);
......@@ -353,17 +363,6 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name,
DBUG_RETURN(-1);
}
key_parts+=key->columns.elements;
}
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
key_iterator.rewind();
for (; (key=key_iterator++) ; key_info++)
{
uint key_length=0;
key_part_spec *column;
if (key->type == Key::PRIMARY)
{
if (primary_key)
......@@ -371,10 +370,39 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name,
my_error(ER_MULTIPLE_PRI_KEY,MYF(0));
DBUG_RETURN(-1);
}
primary_key=1;
primary_key=key;
}
else if (key->type == Key::UNIQUE)
{
unique_key=1;
if (keys_in_order.push_front(key))
DBUG_RETURN(-1);
}
else if (keys_in_order.push_back(key))
DBUG_RETURN(-1);
}
if (primary_key)
{
if (keys_in_order.push_front(primary_key))
DBUG_RETURN(-1);
}
else if (!unique_key && (file->option_flag() & HA_REQUIRE_PRIMARY_KEY))
{
my_error(ER_REQUIRES_PRIMARY_KEY,MYF(0));
DBUG_RETURN(-1);
}
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
List_iterator<Key> key_iterator_in_order(keys_in_order);
for (; (key=key_iterator_in_order++) ; key_info++)
{
uint key_length=0;
key_part_spec *column;
key_info->flags= (key->type == Key::MULTIPLE) ? 0 :
(key->type == Key::FULLTEXT) ? HA_FULLTEXT : HA_NOSAME;
key_info->key_parts=(uint8) key->columns.elements;
......@@ -508,12 +536,6 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name,
my_error(ER_WRONG_AUTO_KEY,MYF(0));
DBUG_RETURN(-1);
}
if (!primary_key && !unique_key &&
(file->option_flag() & HA_REQUIRE_PRIMARY_KEY))
{
my_error(ER_REQUIRES_PRIMARY_KEY,MYF(0));
DBUG_RETURN(-1);
}
/* Check if table exists */
if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
......
......@@ -75,8 +75,16 @@ int mysql_update(THD *thd,TABLE_LIST *table_list,List<Item> &fields,
if (table->timestamp_field && // Don't set timestamp if used
table->timestamp_field->query_id == thd->query_id)
table->time_stamp=0;
/* Reset the query_id string so that ->used_keys is based on the WHERE */
table->used_keys=table->keys_in_use;
table->quick_keys=0;
reg2 Item *item;
List_iterator<Item> it(fields);
ulong query_id=thd->query_id-1;
while ((item=it++))
((Item_field*) item)->field->query_id=query_id;
if (setup_fields(thd,table_list,values,0,0) ||
setup_conds(thd,table_list,&conds))
{
......@@ -84,7 +92,8 @@ int mysql_update(THD *thd,TABLE_LIST *table_list,List<Item> &fields,
DBUG_RETURN(-1); /* purecov: inspected */
}
old_used_keys=table->used_keys;
table->used_keys=0; // Can't use 'only index'
// Don't count on usage of 'only index' when calculating which key to use
table->used_keys=0;
select=make_select(table,0,0,conds,&error);
if (error ||
(select && select->check_quick(test(thd->options & SQL_SAFE_UPDATES),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment