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
|
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
|
setup unless your client code is written to take care of the potential
|
||||||
problems that can happen from updates that occur in different sequence
|
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
|
on different servers.
|
||||||
so that pre-3.23.26 slaves will not be able to read it.
|
|
||||||
|
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
|
@item
|
||||||
If the query on the slave gets an error, the slave thread will
|
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
|
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}.
|
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
|
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
|
@code{MyISAM} tables at the same time other threads are reading from the
|
||||||
the table. Note that currently this only works if there are no holes after
|
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.
|
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
|
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
|
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
|
@appendixsubsec Changes in release 3.23.37
|
||||||
@itemize @bullet
|
@itemize @bullet
|
||||||
@item
|
@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.
|
Disabled @code{INSERT DELAYED} for tables that supports transactions.
|
||||||
@item
|
@item
|
||||||
Fixed bug when using date functions on @code{TEXT}/@code{BLOB} column
|
Fixed bug when using date functions on @code{TEXT}/@code{BLOB} column
|
||||||
|
@ -92,3 +92,31 @@ NULL 9 0
|
|||||||
NULL 9 0
|
NULL 9 0
|
||||||
a b c
|
a b c
|
||||||
6 6 0
|
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 a is null and b=9 or a is null and b=7 limit 3;
|
||||||
select * from t1 where b like "6%";
|
select * from t1 where b like "6%";
|
||||||
drop table t1;
|
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
|
thr_mutex.c thr_rwlock.c
|
||||||
libmysys_a_LIBADD = @THREAD_LOBJECTS@
|
libmysys_a_LIBADD = @THREAD_LOBJECTS@
|
||||||
# test_fn removed 980815 since it not upp to date test_dir
|
# 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)
|
# test_dir_DEPENDENCIES= $(LIBRARIES)
|
||||||
testhash_DEPENDENCIES= $(LIBRARIES)
|
# testhash_DEPENDENCIES= $(LIBRARIES)
|
||||||
test_charset_DEPENDENCIES= $(LIBRARIES)
|
test_charset_DEPENDENCIES= $(LIBRARIES)
|
||||||
EXTRA_PROGRAMS =
|
EXTRA_PROGRAMS =
|
||||||
DEFS = -DDEFAULT_BASEDIR=\"$(prefix)\" \
|
DEFS = -DDEFAULT_BASEDIR=\"$(prefix)\" \
|
||||||
@ -81,17 +81,17 @@ FLAGS=$(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) @NOINST_LDFLAGS@
|
|||||||
#
|
#
|
||||||
|
|
||||||
test_thr_alarm: thr_alarm.c $(LIBRARIES)
|
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)
|
$(LINK) $(FLAGS) -DMAIN ./test_thr_alarm.c $(LDADD) $(LIBS)
|
||||||
$(RM) -f ./test_thr_alarm.*
|
$(RM) -f ./test_thr_alarm.*
|
||||||
|
|
||||||
test_thr_lock: thr_lock.c $(LIBRARIES)
|
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)
|
$(LINK) $(FLAGS) -DMAIN ./test_thr_lock.c $(LDADD) $(LIBS)
|
||||||
$(RM) -f ./test_thr_lock.*
|
$(RM) -f ./test_thr_lock.*
|
||||||
|
|
||||||
test_vsnprintf: my_vsnprintf.c $(LIBRARIES)
|
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)
|
$(LINK) $(FLAGS) -DMAIN ./test_vsnprintf.c $(LDADD) $(LIBS)
|
||||||
$(RM) -f test_vsnprintf.*
|
$(RM) -f test_vsnprintf.*
|
||||||
|
|
||||||
@ -102,7 +102,7 @@ test_charset: test_charset.c $(LIBRARIES)
|
|||||||
$(LINK) $(FLAGS) -DMAIN $(srcdir)/test_charset.c $(LDADD) $(LIBS)
|
$(LINK) $(FLAGS) -DMAIN $(srcdir)/test_charset.c $(LDADD) $(LIBS)
|
||||||
|
|
||||||
testhash: testhash.c $(LIBRARIES)
|
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
|
# Don't update the files from bitkeeper
|
||||||
%::SCCS/s.%
|
%::SCCS/s.%
|
||||||
|
@ -3891,7 +3891,7 @@ String *Field_blob::val_str(String *val_buffer __attribute__((unused)),
|
|||||||
char *blob;
|
char *blob;
|
||||||
memcpy_fixed(&blob,ptr+packlength,sizeof(char*));
|
memcpy_fixed(&blob,ptr+packlength,sizeof(char*));
|
||||||
if (!blob)
|
if (!blob)
|
||||||
val_ptr->length(0);
|
val_ptr->set("",0); // A bit safer than ->length(0)
|
||||||
else
|
else
|
||||||
val_ptr->set((const char*) blob,get_length(ptr));
|
val_ptr->set((const char*) blob,get_length(ptr));
|
||||||
return val_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 bool eq_tree(SEL_ARG* a,SEL_ARG *b);
|
||||||
|
|
||||||
static SEL_ARG null_element(SEL_ARG::IMPOSSIBLE);
|
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
|
** 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;
|
KEY *table_key=quick->head->key_info+quick->index;
|
||||||
flag=EQ_RANGE;
|
flag=EQ_RANGE;
|
||||||
if (table_key->flags & HA_NOSAME && key->part == table_key->key_parts-1)
|
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)
|
if (ranges.elements == 1)
|
||||||
{
|
{
|
||||||
QUICK_RANGE *tmp;
|
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;
|
KEY *key=head->key_info+index;
|
||||||
return ((key->flags & HA_NOSAME) &&
|
return ((key->flags & HA_NOSAME) &&
|
||||||
@ -2349,6 +2357,24 @@ bool QUICK_SELECT::unique_key_range()
|
|||||||
return 0;
|
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
|
** Create a QUICK RANGE based on a key
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
#define NEAR_MAX 8
|
#define NEAR_MAX 8
|
||||||
#define UNIQUE_RANGE 16
|
#define UNIQUE_RANGE 16
|
||||||
#define EQ_RANGE 32
|
#define EQ_RANGE 32
|
||||||
|
#define NULL_RANGE 64
|
||||||
|
|
||||||
typedef struct st_key_part {
|
typedef struct st_key_part {
|
||||||
uint16 key,part,part_length;
|
uint16 key,part,part_length;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user