Fixed bug with UPDATE/DELETE on UNIQUE key which could be NULL
This commit is contained in:
parent
7f21a7a6da
commit
3c82d4a2e1
@ -27391,8 +27391,22 @@ master-slave relationship with @code{log-slave-updates} enabled.
|
||||
Note, however, that many queries will not work right in this kind of
|
||||
setup unless your client code is written to take care of the potential
|
||||
problems that can happen from updates that occur in different sequence
|
||||
on different servers. Note that the log format has changed in Version 3.23.26
|
||||
so that pre-3.23.26 slaves will not be able to read it.
|
||||
on different servers.
|
||||
|
||||
This means that you can do a setup like the following:
|
||||
|
||||
@example
|
||||
A -> B -> C -> A
|
||||
@end example
|
||||
|
||||
This setup will only works if you only do non conflicting updates
|
||||
between the tables. In other words, if you insert data in A and C, you
|
||||
should never insert a row in A that may have a conflicting key with a
|
||||
row insert in C. You should also not update the sam rows on two servers
|
||||
if the order in which the updates are applied matters.
|
||||
|
||||
Note that the log format has changed in Version 3.23.26 so that
|
||||
pre-3.23.26 slaves will not be able to read it.
|
||||
@item
|
||||
If the query on the slave gets an error, the slave thread will
|
||||
terminate, and a message will appear in the @code{.err} file. You should
|
||||
@ -28847,9 +28861,11 @@ explicitely lock the table with @code{LOCK TABLES} or execute a command that
|
||||
will modify every row in the table, like @code{ALTER TABLE}.
|
||||
|
||||
In @strong{MySQL} Version 3.23.7 and above, you can insert rows into
|
||||
@code{MyISAM} tables at the same time other threads are reading from
|
||||
the table. Note that currently this only works if there are no holes after
|
||||
deleted rows in the table at the time the insert is made.
|
||||
@code{MyISAM} tables at the same time other threads are reading from the
|
||||
table. Note that currently this only works if there are no holes after
|
||||
deleted rows in the table at the time the insert is made. When all holes
|
||||
has been filled with new data, concurrent inserts will automaticly be
|
||||
enabled again.
|
||||
|
||||
Table locking enables many threads to read from a table at the same
|
||||
time, but if a thread wants to write to a table, it must first get
|
||||
@ -42743,6 +42759,9 @@ not yet 100% confident in this code.
|
||||
@appendixsubsec Changes in release 3.23.37
|
||||
@itemize @bullet
|
||||
@item
|
||||
@code{UPDATE} and @code{DELETE} with @code{WHERE unique_key_part IS NULL}
|
||||
didn't update/delete all rows.
|
||||
@item
|
||||
Disabled @code{INSERT DELAYED} for tables that supports transactions.
|
||||
@item
|
||||
Fixed bug when using date functions on @code{TEXT}/@code{BLOB} column
|
||||
|
@ -92,3 +92,31 @@ NULL 9 0
|
||||
NULL 9 0
|
||||
a b c
|
||||
6 6 0
|
||||
table type possible_keys key key_len ref rows Extra
|
||||
t1 ref idx1 idx1 5 const 1 where used
|
||||
table type possible_keys key key_len ref rows Extra
|
||||
t1 const idx1 idx1 5 const 1
|
||||
id
|
||||
101
|
||||
102
|
||||
105
|
||||
106
|
||||
109
|
||||
110
|
||||
id
|
||||
101
|
||||
102
|
||||
105
|
||||
106
|
||||
109
|
||||
110
|
||||
id uniq_id
|
||||
3 1
|
||||
4 2
|
||||
7 3
|
||||
8 4
|
||||
id uniq_id
|
||||
3 1
|
||||
4 2
|
||||
7 3
|
||||
8 4
|
||||
|
@ -45,3 +45,49 @@ select * from t1 where (a is null or a = 7) and b=7 and c=0;
|
||||
select * from t1 where a is null and b=9 or a is null and b=7 limit 3;
|
||||
select * from t1 where b like "6%";
|
||||
drop table t1;
|
||||
|
||||
|
||||
#
|
||||
# The following failed for Matt Loschert
|
||||
#
|
||||
|
||||
DROP TABLE IF EXISTS t1,t2;
|
||||
CREATE TABLE t1 (
|
||||
id int(10) unsigned NOT NULL auto_increment,
|
||||
uniq_id int(10) unsigned default NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY idx1 (uniq_id)
|
||||
) TYPE=MyISAM;
|
||||
|
||||
CREATE TABLE t2 (
|
||||
id int(10) unsigned NOT NULL auto_increment,
|
||||
uniq_id int(10) unsigned default NULL,
|
||||
PRIMARY KEY (id)
|
||||
) TYPE=MyISAM;
|
||||
|
||||
INSERT INTO t1 VALUES (1,NULL),(2,NULL),(3,1),(4,2),(5,NULL),(6,NULL),(7,3),(8,4),(9,NULL),(10,NULL);
|
||||
INSERT INTO t2 VALUES (1,NULL),(2,NULL),(3,1),(4,2),(5,NULL),(6,NULL),(7,3),(8,4),(9,NULL),(10,NULL);
|
||||
|
||||
#
|
||||
# Check IS NULL optimization
|
||||
#
|
||||
explain select id from t1 where uniq_id is null;
|
||||
explain select id from t1 where uniq_id =1;
|
||||
#
|
||||
# Check updates
|
||||
#
|
||||
UPDATE t1 SET id=id+100 where uniq_id is null;
|
||||
UPDATE t2 SET id=id+100 where uniq_id is null;
|
||||
select id from t1 where uniq_id is null;
|
||||
select id from t2 where uniq_id is null;
|
||||
#
|
||||
# Delete all records from each table where the uniq_id field is null
|
||||
#
|
||||
DELETE FROM t1 WHERE uniq_id IS NULL;
|
||||
DELETE FROM t2 WHERE uniq_id IS NULL;
|
||||
#
|
||||
# Select what is left -- notice the difference
|
||||
#
|
||||
SELECT * FROM t1 ORDER BY uniq_id, id;
|
||||
SELECT * FROM t2 ORDER BY uniq_id, id;
|
||||
DROP table t1,t2;
|
||||
|
@ -49,9 +49,9 @@ EXTRA_DIST = thr_alarm.c thr_lock.c my_pthread.c my_thr_init.c \
|
||||
thr_mutex.c thr_rwlock.c
|
||||
libmysys_a_LIBADD = @THREAD_LOBJECTS@
|
||||
# test_fn removed 980815 since it not upp to date test_dir
|
||||
noinst_PROGRAMS = testhash test_charset @THREAD_LPROGRAMS@
|
||||
noinst_PROGRAMS = test_charset @THREAD_LPROGRAMS@
|
||||
# test_dir_DEPENDENCIES= $(LIBRARIES)
|
||||
testhash_DEPENDENCIES= $(LIBRARIES)
|
||||
# testhash_DEPENDENCIES= $(LIBRARIES)
|
||||
test_charset_DEPENDENCIES= $(LIBRARIES)
|
||||
EXTRA_PROGRAMS =
|
||||
DEFS = -DDEFAULT_BASEDIR=\"$(prefix)\" \
|
||||
@ -81,17 +81,17 @@ FLAGS=$(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) @NOINST_LDFLAGS@
|
||||
#
|
||||
|
||||
test_thr_alarm: thr_alarm.c $(LIBRARIES)
|
||||
$(CP) -f $(srcdir)/thr_alarm.c ./test_thr_alarm.c
|
||||
$(CP) $(srcdir)/thr_alarm.c ./test_thr_alarm.c
|
||||
$(LINK) $(FLAGS) -DMAIN ./test_thr_alarm.c $(LDADD) $(LIBS)
|
||||
$(RM) -f ./test_thr_alarm.*
|
||||
|
||||
test_thr_lock: thr_lock.c $(LIBRARIES)
|
||||
$(CP) -f $(srcdir)/thr_lock.c test_thr_lock.c
|
||||
$(CP) $(srcdir)/thr_lock.c test_thr_lock.c
|
||||
$(LINK) $(FLAGS) -DMAIN ./test_thr_lock.c $(LDADD) $(LIBS)
|
||||
$(RM) -f ./test_thr_lock.*
|
||||
|
||||
test_vsnprintf: my_vsnprintf.c $(LIBRARIES)
|
||||
$(CP) -f $(srcdir)/my_vsnprintf.c test_vsnprintf.c
|
||||
$(CP) $(srcdir)/my_vsnprintf.c test_vsnprintf.c
|
||||
$(LINK) $(FLAGS) -DMAIN ./test_vsnprintf.c $(LDADD) $(LIBS)
|
||||
$(RM) -f test_vsnprintf.*
|
||||
|
||||
@ -102,7 +102,7 @@ test_charset: test_charset.c $(LIBRARIES)
|
||||
$(LINK) $(FLAGS) -DMAIN $(srcdir)/test_charset.c $(LDADD) $(LIBS)
|
||||
|
||||
testhash: testhash.c $(LIBRARIES)
|
||||
$(LINK) $(FLAGS) -DMAIN $(srcdir)/test_dir.c $(LDADD) $(LIBS)
|
||||
$(LINK) $(FLAGS) -DMAIN $(srcdir)/testhash.c $(LDADD) $(LIBS)
|
||||
|
||||
# Don't update the files from bitkeeper
|
||||
%::SCCS/s.%
|
||||
|
@ -3891,7 +3891,7 @@ String *Field_blob::val_str(String *val_buffer __attribute__((unused)),
|
||||
char *blob;
|
||||
memcpy_fixed(&blob,ptr+packlength,sizeof(char*));
|
||||
if (!blob)
|
||||
val_ptr->length(0);
|
||||
val_ptr->set("",0); // A bit safer than ->length(0)
|
||||
else
|
||||
val_ptr->set((const char*) blob,get_length(ptr));
|
||||
return val_ptr;
|
||||
|
@ -321,7 +321,7 @@ static bool get_quick_keys(PARAM *param,QUICK_SELECT *quick,KEY_PART *key,
|
||||
static bool eq_tree(SEL_ARG* a,SEL_ARG *b);
|
||||
|
||||
static SEL_ARG null_element(SEL_ARG::IMPOSSIBLE);
|
||||
|
||||
static bool null_part_in_key(KEY_PART *key_part, const char *key, uint length);
|
||||
|
||||
/***************************************************************************
|
||||
** Basic functions for SQL_SELECT and QUICK_SELECT
|
||||
@ -2306,7 +2306,15 @@ get_quick_keys(PARAM *param,QUICK_SELECT *quick,KEY_PART *key,
|
||||
KEY *table_key=quick->head->key_info+quick->index;
|
||||
flag=EQ_RANGE;
|
||||
if (table_key->flags & HA_NOSAME && key->part == table_key->key_parts-1)
|
||||
flag|= UNIQUE_RANGE;
|
||||
{
|
||||
if (!(table_key->flags & HA_NULL_PART_KEY) ||
|
||||
!null_part_in_key(key,
|
||||
param->min_key,
|
||||
(uint) (tmp_min_key - param->min_key)))
|
||||
flag|= UNIQUE_RANGE;
|
||||
else
|
||||
flag|= NULL_RANGE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2339,7 +2347,7 @@ bool QUICK_SELECT::unique_key_range()
|
||||
if (ranges.elements == 1)
|
||||
{
|
||||
QUICK_RANGE *tmp;
|
||||
if ((tmp=ranges.head())->flag & EQ_RANGE)
|
||||
if (((tmp=ranges.head())->flag & (EQ_RANGE | NULL_RANGE)) == EQ_RANGE)
|
||||
{
|
||||
KEY *key=head->key_info+index;
|
||||
return ((key->flags & HA_NOSAME) &&
|
||||
@ -2349,6 +2357,24 @@ bool QUICK_SELECT::unique_key_range()
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Returns true if any part of the key is NULL */
|
||||
|
||||
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)
|
||||
{
|
||||
if (key_part->null_bit)
|
||||
{
|
||||
if (*key++)
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
** Create a QUICK RANGE based on a key
|
||||
****************************************************************************/
|
||||
|
@ -30,6 +30,7 @@
|
||||
#define NEAR_MAX 8
|
||||
#define UNIQUE_RANGE 16
|
||||
#define EQ_RANGE 32
|
||||
#define NULL_RANGE 64
|
||||
|
||||
typedef struct st_key_part {
|
||||
uint16 key,part,part_length;
|
||||
|
Loading…
x
Reference in New Issue
Block a user