diff options
author | unknown <kostja@bodhi.local> | 2006-11-02 01:08:39 +0300 |
---|---|---|
committer | unknown <kostja@bodhi.local> | 2006-11-02 01:08:39 +0300 |
commit | b6102f671398ac5ccf1b6f1ed0226ee6719e0935 (patch) | |
tree | 5d5deeab993fb48853f3373233849f8d87d0423a | |
parent | 1e64687b86f51a3606efe510cf97dcf6dd4bc8f8 (diff) | |
parent | 9280d33474adb8d5848f6467f18e578b740bd3cb (diff) | |
download | mariadb-git-b6102f671398ac5ccf1b6f1ed0226ee6719e0935.tar.gz |
Merge bk-internal.mysql.com:/home/bk/mysql-4.1clone-4.1.22-build
into bodhi.local:/opt/local/work/mysql-4.1-runtime
mysql-test/r/ps.result:
Auto merged
mysql-test/t/func_gconcat.test:
Auto merged
sql/item_func.cc:
Auto merged
sql/item_func.h:
Auto merged
sql/item_sum.cc:
Auto merged
sql/log_event.cc:
Auto merged
sql/mysql_priv.h:
Auto merged
sql/mysqld.cc:
Auto merged
sql/set_var.cc:
Auto merged
sql/sql_class.h:
Auto merged
sql/sql_delete.cc:
Auto merged
sql/sql_select.cc:
Auto merged
sql/sql_update.cc:
Auto merged
-rw-r--r-- | mysql-test/r/innodb_mysql.result | 16 | ||||
-rw-r--r-- | mysql-test/r/ps.result | 97 | ||||
-rw-r--r-- | mysql-test/r/rpl_insert_id.result | 27 | ||||
-rw-r--r-- | mysql-test/t/func_gconcat.test | 2 | ||||
-rw-r--r-- | mysql-test/t/innodb_mysql.test | 29 | ||||
-rw-r--r-- | mysql-test/t/ps.test | 75 | ||||
-rw-r--r-- | mysql-test/t/rpl_insert_id.test | 32 | ||||
-rw-r--r-- | sql/item_func.cc | 31 | ||||
-rw-r--r-- | sql/item_func.h | 1 | ||||
-rw-r--r-- | sql/item_sum.cc | 1 | ||||
-rw-r--r-- | sql/item_sum.h | 48 | ||||
-rw-r--r-- | sql/log_event.cc | 1 | ||||
-rw-r--r-- | sql/mysql_priv.h | 2 | ||||
-rw-r--r-- | sql/mysqld.cc | 42 | ||||
-rw-r--r-- | sql/set_var.cc | 8 | ||||
-rw-r--r-- | sql/sql_class.h | 48 | ||||
-rw-r--r-- | sql/sql_delete.cc | 3 | ||||
-rw-r--r-- | sql/sql_insert.cc | 8 | ||||
-rw-r--r-- | sql/sql_load.cc | 12 | ||||
-rw-r--r-- | sql/sql_parse.cc | 6 | ||||
-rw-r--r-- | sql/sql_select.cc | 11 | ||||
-rw-r--r-- | sql/sql_update.cc | 4 | ||||
-rw-r--r-- | tests/mysql_client_test.c | 38 |
23 files changed, 491 insertions, 51 deletions
diff --git a/mysql-test/r/innodb_mysql.result b/mysql-test/r/innodb_mysql.result index ee4c114087d..68995565752 100644 --- a/mysql-test/r/innodb_mysql.result +++ b/mysql-test/r/innodb_mysql.result @@ -104,3 +104,19 @@ SELECT `id1` FROM `t1` WHERE `id1` NOT IN (SELECT `id1` FROM `t2` WHERE `id2` = id1 2 DROP TABLE t1, t2; +DROP TABLE IF EXISTS t2, t1; +CREATE TABLE t1 (i INT NOT NULL PRIMARY KEY) ENGINE= InnoDB; +CREATE TABLE t2 ( +i INT NOT NULL, +FOREIGN KEY (i) REFERENCES t1 (i) ON DELETE NO ACTION +) ENGINE= InnoDB; +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (1); +DELETE IGNORE FROM t1 WHERE i = 1; +Warnings: +Error 1217 Cannot delete or update a parent row: a foreign key constraint fails +SELECT * FROM t1, t2; +i i +1 1 +DROP TABLE t2, t1; +End of 4.1 tests. diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index 4abc7bb7709..2bb5df6e6cf 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -936,3 +936,100 @@ GROUP_CONCAT(Track SEPARATOR ', ') CAD DEALLOCATE PREPARE STMT; DROP TABLE t1; +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 (i INT, INDEX(i)); +INSERT INTO t1 VALUES (1); +PREPARE stmt FROM "SELECT (COUNT(i) = 1), COUNT(i) FROM t1 WHERE i = ?"; +SET @a = 0; +EXECUTE stmt USING @a; +(COUNT(i) = 1) COUNT(i) +0 0 +SET @a = 1; +EXECUTE stmt USING @a; +(COUNT(i) = 1) COUNT(i) +1 1 +SET @a = 0; +EXECUTE stmt USING @a; +(COUNT(i) = 1) COUNT(i) +0 0 +PREPARE stmt FROM "SELECT (AVG(i) = 1), AVG(i) FROM t1 WHERE i = ?"; +SET @a = 0; +EXECUTE stmt USING @a; +(AVG(i) = 1) AVG(i) +NULL NULL +SET @a = 1; +EXECUTE stmt USING @a; +(AVG(i) = 1) AVG(i) +1 1.0000 +SET @a = 0; +EXECUTE stmt USING @a; +(AVG(i) = 1) AVG(i) +NULL NULL +PREPARE stmt FROM "SELECT (VARIANCE(i) = 1), VARIANCE(i) FROM t1 WHERE i = ?"; +SET @a = 0; +EXECUTE stmt USING @a; +(VARIANCE(i) = 1) VARIANCE(i) +NULL NULL +SET @a = 1; +EXECUTE stmt USING @a; +(VARIANCE(i) = 1) VARIANCE(i) +0 0.0000 +SET @a = 0; +EXECUTE stmt USING @a; +(VARIANCE(i) = 1) VARIANCE(i) +NULL NULL +PREPARE stmt FROM "SELECT (STDDEV(i) = 1), STDDEV(i) FROM t1 WHERE i = ?"; +SET @a = 0; +EXECUTE stmt USING @a; +(STDDEV(i) = 1) STDDEV(i) +NULL NULL +SET @a = 1; +EXECUTE stmt USING @a; +(STDDEV(i) = 1) STDDEV(i) +0 0.0000 +SET @a = 0; +EXECUTE stmt USING @a; +(STDDEV(i) = 1) STDDEV(i) +NULL NULL +PREPARE stmt FROM "SELECT (BIT_OR(i) = 1), BIT_OR(i) FROM t1 WHERE i = ?"; +SET @a = 0; +EXECUTE stmt USING @a; +(BIT_OR(i) = 1) BIT_OR(i) +0 0 +SET @a = 1; +EXECUTE stmt USING @a; +(BIT_OR(i) = 1) BIT_OR(i) +1 1 +SET @a = 0; +EXECUTE stmt USING @a; +(BIT_OR(i) = 1) BIT_OR(i) +0 0 +PREPARE stmt FROM "SELECT (BIT_AND(i) = 1), BIT_AND(i) FROM t1 WHERE i = ?"; +SET @a = 0; +EXECUTE stmt USING @a; +(BIT_AND(i) = 1) BIT_AND(i) +0 18446744073709551615 +SET @a = 1; +EXECUTE stmt USING @a; +(BIT_AND(i) = 1) BIT_AND(i) +1 1 +SET @a = 0; +EXECUTE stmt USING @a; +(BIT_AND(i) = 1) BIT_AND(i) +0 18446744073709551615 +PREPARE stmt FROM "SELECT (BIT_XOR(i) = 1), BIT_XOR(i) FROM t1 WHERE i = ?"; +SET @a = 0; +EXECUTE stmt USING @a; +(BIT_XOR(i) = 1) BIT_XOR(i) +0 0 +SET @a = 1; +EXECUTE stmt USING @a; +(BIT_XOR(i) = 1) BIT_XOR(i) +1 1 +SET @a = 0; +EXECUTE stmt USING @a; +(BIT_XOR(i) = 1) BIT_XOR(i) +0 0 +DEALLOCATE PREPARE stmt; +DROP TABLE t1; +End of 4.1 tests. diff --git a/mysql-test/r/rpl_insert_id.result b/mysql-test/r/rpl_insert_id.result index fbdc9dc06cf..43aba68d041 100644 --- a/mysql-test/r/rpl_insert_id.result +++ b/mysql-test/r/rpl_insert_id.result @@ -108,6 +108,33 @@ a 1 drop table t1; drop table t2; +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 ( +i INT NOT NULL AUTO_INCREMENT PRIMARY KEY, +j INT DEFAULT 0 +); +INSERT INTO t1 VALUES (NULL, -1); +INSERT INTO t1 VALUES (NULL, LAST_INSERT_ID()), (NULL, LAST_INSERT_ID(5)), +(NULL, @@LAST_INSERT_ID); +INSERT INTO t1 VALUES (NULL, 0), (NULL, LAST_INSERT_ID()); +UPDATE t1 SET j= -1 WHERE i IS NULL; +SELECT * FROM t1; +i j +1 -1 +2 1 +3 5 +4 1 +5 -1 +6 2 +SELECT * FROM t1; +i j +1 -1 +2 1 +3 5 +4 1 +5 -1 +6 2 +DROP TABLE t1; # # End of 4.1 tests # diff --git a/mysql-test/t/func_gconcat.test b/mysql-test/t/func_gconcat.test index d51d88d50ef..40179ae7f38 100644 --- a/mysql-test/t/func_gconcat.test +++ b/mysql-test/t/func_gconcat.test @@ -98,7 +98,7 @@ select ifnull(group_concat(concat(t1.id, ':', t1.name)), 'shortname') as 'withou select distinct ifnull(group_concat(concat(t1.id, ':', t1.name)), 'shortname') as 'with distinct: cutoff at length of shortname' from t1; drop table t1; -# check zero rows +# check zero rows (bug#836) create table t1(id int); create table t2(id int); insert into t1 values(0),(1); diff --git a/mysql-test/t/innodb_mysql.test b/mysql-test/t/innodb_mysql.test index a5fe248604f..9593982826b 100644 --- a/mysql-test/t/innodb_mysql.test +++ b/mysql-test/t/innodb_mysql.test @@ -117,3 +117,32 @@ INSERT INTO `t2`(`id1`,`id2`,`id3`,`id4`) VALUES SELECT `id1` FROM `t1` WHERE `id1` NOT IN (SELECT `id1` FROM `t2` WHERE `id2` = 1 AND `id3` = 2); DROP TABLE t1, t2; + + +# +# BUG#18819: DELETE IGNORE hangs on foreign key parent delete +# +# The bug itself does not relate to InnoDB, but we have to use foreign +# keys to reproduce it. +# +--disable_warnings +DROP TABLE IF EXISTS t2, t1; +--enable_warnings + +CREATE TABLE t1 (i INT NOT NULL PRIMARY KEY) ENGINE= InnoDB; +CREATE TABLE t2 ( + i INT NOT NULL, + FOREIGN KEY (i) REFERENCES t1 (i) ON DELETE NO ACTION +) ENGINE= InnoDB; + +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (1); + +DELETE IGNORE FROM t1 WHERE i = 1; + +SELECT * FROM t1, t2; + +DROP TABLE t2, t1; + + +--echo End of 4.1 tests. diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test index 7c2a42404ad..76fcb49d598 100644 --- a/mysql-test/t/ps.test +++ b/mysql-test/t/ps.test @@ -951,6 +951,7 @@ execute stmt; drop temporary table t1; deallocate prepare stmt; + # # BUG#22085: Crash on the execution of a prepared statement that # uses an IN subquery with aggregate functions in HAVING @@ -1003,4 +1004,76 @@ EXECUTE STMT USING @id,@id; DEALLOCATE PREPARE STMT; DROP TABLE t1; -# End of 4.1 tests + +# +# BUG#21354: (COUNT(*) = 1) not working in SELECT inside prepared +# statement +# +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +CREATE TABLE t1 (i INT, INDEX(i)); +INSERT INTO t1 VALUES (1); + +PREPARE stmt FROM "SELECT (COUNT(i) = 1), COUNT(i) FROM t1 WHERE i = ?"; +SET @a = 0; +EXECUTE stmt USING @a; +SET @a = 1; +EXECUTE stmt USING @a; +SET @a = 0; +EXECUTE stmt USING @a; + +PREPARE stmt FROM "SELECT (AVG(i) = 1), AVG(i) FROM t1 WHERE i = ?"; +SET @a = 0; +EXECUTE stmt USING @a; +SET @a = 1; +EXECUTE stmt USING @a; +SET @a = 0; +EXECUTE stmt USING @a; + +PREPARE stmt FROM "SELECT (VARIANCE(i) = 1), VARIANCE(i) FROM t1 WHERE i = ?"; +SET @a = 0; +EXECUTE stmt USING @a; +SET @a = 1; +EXECUTE stmt USING @a; +SET @a = 0; +EXECUTE stmt USING @a; + +PREPARE stmt FROM "SELECT (STDDEV(i) = 1), STDDEV(i) FROM t1 WHERE i = ?"; +SET @a = 0; +EXECUTE stmt USING @a; +SET @a = 1; +EXECUTE stmt USING @a; +SET @a = 0; +EXECUTE stmt USING @a; + +PREPARE stmt FROM "SELECT (BIT_OR(i) = 1), BIT_OR(i) FROM t1 WHERE i = ?"; +SET @a = 0; +EXECUTE stmt USING @a; +SET @a = 1; +EXECUTE stmt USING @a; +SET @a = 0; +EXECUTE stmt USING @a; + +PREPARE stmt FROM "SELECT (BIT_AND(i) = 1), BIT_AND(i) FROM t1 WHERE i = ?"; +SET @a = 0; +EXECUTE stmt USING @a; +SET @a = 1; +EXECUTE stmt USING @a; +SET @a = 0; +EXECUTE stmt USING @a; + +PREPARE stmt FROM "SELECT (BIT_XOR(i) = 1), BIT_XOR(i) FROM t1 WHERE i = ?"; +SET @a = 0; +EXECUTE stmt USING @a; +SET @a = 1; +EXECUTE stmt USING @a; +SET @a = 0; +EXECUTE stmt USING @a; + +DEALLOCATE PREPARE stmt; +DROP TABLE t1; + + +--echo End of 4.1 tests. diff --git a/mysql-test/t/rpl_insert_id.test b/mysql-test/t/rpl_insert_id.test index 327094a1394..7fb514fb7af 100644 --- a/mysql-test/t/rpl_insert_id.test +++ b/mysql-test/t/rpl_insert_id.test @@ -108,6 +108,38 @@ drop table t1; drop table t2; sync_slave_with_master; + +# +# BUG#21726: Incorrect result with multiple invocations of +# LAST_INSERT_ID +# +connection master; + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +CREATE TABLE t1 ( + i INT NOT NULL AUTO_INCREMENT PRIMARY KEY, + j INT DEFAULT 0 +); + +INSERT INTO t1 VALUES (NULL, -1); +INSERT INTO t1 VALUES (NULL, LAST_INSERT_ID()), (NULL, LAST_INSERT_ID(5)), + (NULL, @@LAST_INSERT_ID); +# Test replication of substitution "IS NULL" -> "= LAST_INSERT_ID". +INSERT INTO t1 VALUES (NULL, 0), (NULL, LAST_INSERT_ID()); +UPDATE t1 SET j= -1 WHERE i IS NULL; + +SELECT * FROM t1; + +sync_slave_with_master; +SELECT * FROM t1; + +connection master; +DROP TABLE t1; + + --echo # --echo # End of 4.1 tests --echo # diff --git a/sql/item_func.cc b/sql/item_func.cc index fafefd460f9..1af49179aee 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -2250,6 +2250,30 @@ longlong Item_func_release_lock::val_int() } +bool Item_func_last_insert_id::fix_fields(THD *thd, TABLE_LIST *tables, + Item **ref) +{ + DBUG_ASSERT(fixed == 0); + + if (Item_int_func::fix_fields(thd, tables, ref)) + return TRUE; + + if (arg_count == 0) + { + /* + As this statement calls LAST_INSERT_ID(), set + THD::last_insert_id_used. + */ + thd->last_insert_id_used= TRUE; + null_value= FALSE; + } + + thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); + + return FALSE; +} + + longlong Item_func_last_insert_id::val_int() { DBUG_ASSERT(fixed == 1); @@ -2259,12 +2283,13 @@ longlong Item_func_last_insert_id::val_int() longlong value=args[0]->val_int(); thd->insert_id(value); null_value=args[0]->null_value; + return value; } - else - thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); - return thd->last_insert_id_used ? thd->current_insert_id : thd->insert_id(); + + return thd->current_insert_id; } + /* This function is just used to test speed of different functions */ longlong Item_func_benchmark::val_int() diff --git a/sql/item_func.h b/sql/item_func.h index bc6c955b99f..467b88eda76 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -758,6 +758,7 @@ public: longlong val_int(); const char *func_name() const { return "last_insert_id"; } void fix_length_and_dec() { if (arg_count) max_length= args[0]->max_length; } + bool fix_fields(THD *thd, TABLE_LIST *tables, Item **ref); }; class Item_func_benchmark :public Item_int_func diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 1cd5f81afcd..5fd65fecbfc 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -312,6 +312,7 @@ longlong Item_sum_count::val_int() void Item_sum_count::cleanup() { DBUG_ENTER("Item_sum_count::cleanup"); + clear(); Item_sum_int::cleanup(); used_table_cache= ~(table_map) 0; DBUG_VOID_RETURN; diff --git a/sql/item_sum.h b/sql/item_sum.h index 0cc2a20faa3..ea0863fc41c 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -58,9 +58,30 @@ public: Item_sum(THD *thd, Item_sum *item); enum Type type() const { return SUM_FUNC_ITEM; } virtual enum Sumfunctype sum_func () const=0; + + /* + This method is similar to add(), but it is called when the current + aggregation group changes. Thus it performs a combination of + clear() and add(). + */ inline bool reset() { clear(); return add(); }; + + /* + Prepare this item for evaluation of an aggregate value. This is + called by reset() when a group changes, or, for correlated + subqueries, between subquery executions. E.g. for COUNT(), this + method should set count= 0; + */ virtual void clear()= 0; + + /* + This method is called for the next row in the same group. Its + purpose is to aggregate the new value to the previous values in + the group (i.e. since clear() was called last time). For example, + for COUNT(), do count++. + */ virtual bool add()=0; + /* Called when new group is started and results are being saved in a temporary table. Similar to reset(), but must also store value in @@ -86,7 +107,17 @@ public: void make_field(Send_field *field); void print(String *str); void fix_num_length_and_dec(); - void no_rows_in_result() { reset(); } + + /* + This function is called by the execution engine to assign 'NO ROWS + FOUND' value to an aggregate item, when the underlying result set + has no rows. Such value, in a general case, may be different from + the default value of the item after 'clear()': e.g. a numeric item + may be initialized to 0 by clear() and to NULL by + no_rows_in_result(). + */ + void no_rows_in_result() { clear(); } + virtual bool setup(THD *thd) {return 0;} virtual void make_unique() {} Item *get_tmp_table_item(THD *thd); @@ -304,6 +335,11 @@ class Item_sum_avg :public Item_sum_num void no_rows_in_result() {} const char *func_name() const { return "avg"; } Item *copy_or_same(THD* thd); + void cleanup() + { + clear(); + Item_sum_num::cleanup(); + } }; class Item_sum_variance; @@ -361,6 +397,11 @@ class Item_sum_variance : public Item_sum_num void no_rows_in_result() {} const char *func_name() const { return "variance"; } Item *copy_or_same(THD* thd); + void cleanup() + { + clear(); + Item_sum_num::cleanup(); + } }; class Item_sum_std; @@ -485,6 +526,11 @@ public: void update_field(); void fix_length_and_dec() { decimals=0; max_length=21; unsigned_flag=1; maybe_null=null_value=0; } + void cleanup() + { + clear(); + Item_sum_int::cleanup(); + } }; diff --git a/sql/log_event.cc b/sql/log_event.cc index 19c32b2d28e..412ebbce0ac 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -2255,7 +2255,6 @@ int Intvar_log_event::exec_event(struct st_relay_log_info* rli) { switch (type) { case LAST_INSERT_ID_EVENT: - thd->last_insert_id_used = 1; thd->last_insert_id = val; break; case INSERT_ID_EVENT: diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index c2c9f9b9d7b..b1b0016c06b 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -95,6 +95,8 @@ MY_LOCALE *my_locale_by_name(const char *name); #define MAX_ACCEPT_RETRY 10 // Test accept this many times #define MAX_FIELDS_BEFORE_HASH 32 #define USER_VARS_HASH_SIZE 16 +#define TABLE_OPEN_CACHE_MIN 64 +#define TABLE_OPEN_CACHE_DEFAULT 64 #define STACK_MIN_SIZE 8192 // Abort if less stack during eval. #define STACK_BUFF_ALLOC 64 // For stack overrun checks #ifndef MYSQLD_NET_RETRY_COUNT diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 805311df947..dbb1838c3d7 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -2526,19 +2526,43 @@ static int init_common_variables(const char *conf_file_name, int argc, /* connections and databases needs lots of files */ { - uint files, wanted_files; + uint files, wanted_files, max_open_files; - wanted_files= 10+(uint) max(max_connections*5, - max_connections+table_cache_size*2); - set_if_bigger(wanted_files, open_files_limit); - files= my_set_max_open_files(wanted_files); + /* MyISAM requires two file handles per table. */ + wanted_files= 10+max_connections+table_cache_size*2; + /* + We are trying to allocate no less than max_connections*5 file + handles (i.e. we are trying to set the limit so that they will + be available). In addition, we allocate no less than how much + was already allocated. However below we report a warning and + recompute values only if we got less file handles than were + explicitly requested. No warning and re-computation occur if we + can't get max_connections*5 but still got no less than was + requested (value of wanted_files). + */ + max_open_files= max(max(wanted_files, max_connections*5), + open_files_limit); + files= my_set_max_open_files(max_open_files); if (files < wanted_files) { if (!open_files_limit) { - max_connections= (ulong) min((files-10),max_connections); - table_cache_size= (ulong) max((files-10-max_connections)/2,64); + /* + If we have requested too much file handles than we bring + max_connections in supported bounds. + */ + max_connections= (ulong) min(files-10-TABLE_OPEN_CACHE_MIN*2, + max_connections); + /* + Decrease table_cache_size according to max_connections, but + not below TABLE_OPEN_CACHE_MIN. Outer min() ensures that we + never increase table_cache_size automatically (that could + happen if max_connections is decreased above). + */ + table_cache_size= (ulong) min(max((files-10-max_connections)/2, + TABLE_OPEN_CACHE_MIN), + table_cache_size); DBUG_PRINT("warning", ("Changed limits: max_open_files: %u max_connections: %ld table_cache: %ld", files, max_connections, table_cache_size)); @@ -5511,8 +5535,8 @@ The minimum value for this variable is 4096.", 0, 0, 0, 0}, {"table_cache", OPT_TABLE_CACHE, "The number of open tables for all threads.", (gptr*) &table_cache_size, - (gptr*) &table_cache_size, 0, GET_ULONG, REQUIRED_ARG, 64, 1, 512*1024L, - 0, 1, 0}, + (gptr*) &table_cache_size, 0, GET_ULONG, REQUIRED_ARG, + TABLE_OPEN_CACHE_DEFAULT, 1, 512*1024L, 0, 1, 0}, {"thread_cache_size", OPT_THREAD_CACHE_SIZE, "How many threads we should keep in a cache for reuse.", (gptr*) &thread_cache_size, (gptr*) &thread_cache_size, 0, GET_ULONG, diff --git a/sql/set_var.cc b/sql/set_var.cc index 21370821dff..4433b6bf7d8 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -2404,8 +2404,12 @@ bool sys_var_last_insert_id::update(THD *thd, set_var *var) byte *sys_var_last_insert_id::value_ptr(THD *thd, enum_var_type type, LEX_STRING *base) { - thd->sys_var_tmp.long_value= (long) thd->insert_id(); - return (byte*) &thd->last_insert_id; + /* + As this statement reads @@LAST_INSERT_ID, set + THD::last_insert_id_used. + */ + thd->last_insert_id_used= TRUE; + return (byte*) &thd->current_insert_id; } diff --git a/sql/sql_class.h b/sql/sql_class.h index cc90de2a6ea..7cf0b474ed7 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -835,17 +835,29 @@ public: generated auto_increment value in handler.cc */ ulonglong next_insert_id; + /* - The insert_id used for the last statement or set by SET LAST_INSERT_ID=# - or SELECT LAST_INSERT_ID(#). Used for binary log and returned by - LAST_INSERT_ID() + At the beginning of the statement last_insert_id holds the first + generated value of the previous statement. During statement + execution it is updated to the value just generated, but then + restored to the value that was generated first, so for the next + statement it will again be "the first generated value of the + previous statement". + + It may also be set with "LAST_INSERT_ID(expr)" or + "@@LAST_INSERT_ID= expr", but the effect of such setting will be + seen only in the next statement. */ ulonglong last_insert_id; + /* - Set to the first value that LAST_INSERT_ID() returned for the last - statement. When this is set, last_insert_id_used is set to true. + current_insert_id remembers the first generated value of the + previous statement, and does not change during statement + execution. Its value returned from LAST_INSERT_ID() and + @@LAST_INSERT_ID. */ ulonglong current_insert_id; + ulonglong limit_found_rows; ha_rows cuted_fields, sent_row_count, examined_row_count; @@ -896,7 +908,22 @@ public: bool locked, some_tables_deleted; bool last_cuted_field; bool no_errors, password, is_fatal_error; - bool query_start_used,last_insert_id_used,insert_id_used,rand_used; + bool query_start_used, rand_used; + + /* + last_insert_id_used is set when current statement calls + LAST_INSERT_ID() or reads @@LAST_INSERT_ID, so that binary log + LAST_INSERT_ID_EVENT be generated. + */ + bool last_insert_id_used; + + /* + insert_id_used is set when current statement updates + THD::last_insert_id, so that binary log INSERT_ID_EVENT be + generated. + */ + bool insert_id_used; + /* for IS NULL => = last_insert_id() fix in remove_eq_conds() */ bool substitute_null_with_insert_id; bool time_zone_used; @@ -996,15 +1023,6 @@ public: insert_id_used=1; substitute_null_with_insert_id= TRUE; } - inline ulonglong insert_id(void) - { - if (!last_insert_id_used) - { - last_insert_id_used=1; - current_insert_id=last_insert_id; - } - return last_insert_id; - } inline ulonglong found_rows(void) { return limit_found_rows; diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 017e9596b14..1a20b9b7d91 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -258,7 +258,8 @@ cleanup: mysql_unlock_tables(thd, thd->lock); thd->lock=0; } - if (error >= 0 || thd->net.report_error) + if ((error >= 0 || thd->net.report_error) && + (!thd->lex->ignore || thd->is_fatal_error)) send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN: 0); else { diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 283fe571d53..43c7e5d456f 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -374,10 +374,8 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, if (error) break; /* - If auto_increment values are used, save the first one - for LAST_INSERT_ID() and for the update log. - We can't use insert_id() as we don't want to touch the - last_insert_id_used flag. + If auto_increment values are used, save the first one for + LAST_INSERT_ID() and for the update log. */ if (! id && thd->insert_id_used) { // Get auto increment value @@ -1687,7 +1685,7 @@ bool select_insert::send_data(List<Item> &values) { table->next_number_field->reset(); if (! last_insert_id && thd->insert_id_used) - last_insert_id=thd->insert_id(); + last_insert_id= thd->last_insert_id; } } DBUG_RETURN(error); diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 4e6c458cc43..48862506729 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -466,10 +466,8 @@ read_fixed_length(THD *thd,COPY_INFO &info,TABLE *table,List<Item> &fields, if (write_record(table,&info)) DBUG_RETURN(1); /* - If auto_increment values are used, save the first one - for LAST_INSERT_ID() and for the binary/update log. - We can't use insert_id() as we don't want to touch the - last_insert_id_used flag. + If auto_increment values are used, save the first one for + LAST_INSERT_ID() and for the binary/update log. */ if (!id && thd->insert_id_used) id= thd->last_insert_id; @@ -572,10 +570,8 @@ read_sep_field(THD *thd,COPY_INFO &info,TABLE *table, if (write_record(table,&info)) DBUG_RETURN(1); /* - If auto_increment values are used, save the first one - for LAST_INSERT_ID() and for the binary/update log. - We can't use insert_id() as we don't want to touch the - last_insert_id_used flag. + If auto_increment values are used, save the first one for + LAST_INSERT_ID() and for the binary/update log. */ if (!id && thd->insert_id_used) id= thd->last_insert_id; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 98199ed22f1..cb2fa0f7014 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1978,6 +1978,12 @@ mysql_execute_command(THD *thd) DBUG_ENTER("mysql_execute_command"); /* + Remember first generated insert id value of the previous + statement. + */ + thd->current_insert_id= thd->last_insert_id; + + /* Reset warning count for each query that uses tables A better approach would be to reset this for any commands that is not a SHOW command or a select that only access local diff --git a/sql/sql_select.cc b/sql/sql_select.cc index d4f717396f3..2c92de560c8 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -4838,7 +4838,7 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value) Field *field=((Item_field*) args[0])->field; if (field->flags & AUTO_INCREMENT_FLAG && !field->table->maybe_null && (thd->options & OPTION_AUTO_IS_NULL) && - thd->insert_id() && thd->substitute_null_with_insert_id) + thd->current_insert_id && thd->substitute_null_with_insert_id) { #ifdef HAVE_QUERY_CACHE query_cache_abort(&thd->net); @@ -4846,9 +4846,16 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value) COND *new_cond; if ((new_cond= new Item_func_eq(args[0], new Item_int("last_insert_id()", - thd->insert_id(), + thd->current_insert_id, 21)))) { + /* + Set THD::last_insert_id_used manually, as this statement + uses LAST_INSERT_ID() in a sense, and should issue + LAST_INSERT_ID_EVENT. + */ + thd->last_insert_id_used= TRUE; + cond=new_cond; cond->fix_fields(thd, 0, &cond); } diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 6742fbad5c4..20df3b3ab4a 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -408,7 +408,7 @@ int mysql_update(THD *thd, (ulong) thd->cuted_fields); send_ok(thd, (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated, - thd->insert_id_used ? thd->insert_id() : 0L,buff); + thd->insert_id_used ? thd->last_insert_id : 0L,buff); DBUG_PRINT("info",("%d records updated",updated)); } thd->count_cuted_fields= CHECK_FIELD_IGNORE; /* calc cuted fields */ @@ -1324,6 +1324,6 @@ bool multi_update::send_eof() (ulong) thd->cuted_fields); ::send_ok(thd, (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated, - thd->insert_id_used ? thd->insert_id() : 0L,buff); + thd->insert_id_used ? thd->last_insert_id : 0L,buff); return 0; } diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 9fabde993b8..6ae9dcb9476 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -11909,6 +11909,43 @@ static void test_bug20152() /* + Bug#21726: Incorrect result with multiple invocations of + LAST_INSERT_ID + + Test that client gets updated value of insert_id on UPDATE that uses + LAST_INSERT_ID(expr). +*/ +static void test_bug21726() +{ + const char *update_query = "UPDATE t1 SET i= LAST_INSERT_ID(i + 1)"; + int rc; + my_ulonglong insert_id; + + DBUG_ENTER("test_bug21726"); + myheader("test_bug21726"); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + myquery(rc); + rc= mysql_query(mysql, "CREATE TABLE t1 (i INT)"); + myquery(rc); + rc= mysql_query(mysql, "INSERT INTO t1 VALUES (1)"); + myquery(rc); + + rc= mysql_query(mysql, update_query); + myquery(rc); + insert_id= mysql_insert_id(mysql); + DIE_UNLESS(insert_id == 2); + + rc= mysql_query(mysql, update_query); + myquery(rc); + insert_id= mysql_insert_id(mysql); + DIE_UNLESS(insert_id == 3); + + DBUG_VOID_RETURN; +} + + +/* Read and parse arguments and MySQL options from my.cnf */ @@ -12134,6 +12171,7 @@ static struct my_tests_st my_tests[]= { { "test_bug12925", test_bug12925 }, { "test_bug15613", test_bug15613 }, { "test_bug20152", test_bug20152 }, + { "test_bug21726", test_bug21726 }, { 0, 0 } }; |