From e1f31863ab255d3b5b7080cade58cc4e5983f6ce Mon Sep 17 00:00:00 2001 From: "holyfoot/hf@mysql.com/deer.(none)" <> Date: Sun, 1 Oct 2006 16:36:26 +0500 Subject: [PATCH 01/65] bug #21790 (UNKNOWN ERROR message in geometry) We issued UNKNOWN ERROR initially in this place and forgot to fix it when we implemented informative error message for this --- mysql-test/r/gis-rtree.result | 8 ++++++++ mysql-test/t/gis-rtree.test | 12 ++++++++++++ sql/handler.cc | 4 ++-- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/gis-rtree.result b/mysql-test/r/gis-rtree.result index b283d64395d..f872838f6f3 100644 --- a/mysql-test/r/gis-rtree.result +++ b/mysql-test/r/gis-rtree.result @@ -862,3 +862,11 @@ CHECK TABLE t1 EXTENDED; Table Op Msg_type Msg_text test.t1 check status OK DROP TABLE t1; +CREATE TABLE t1(foo GEOMETRY NOT NULL, SPATIAL INDEX(foo) ); +INSERT INTO t1(foo) VALUES (NULL); +ERROR 23000: Column 'foo' cannot be null +INSERT INTO t1() VALUES (); +ERROR 22003: Cannot get geometry object from data you send to the GEOMETRY field +INSERT INTO t1(foo) VALUES (''); +ERROR 22003: Cannot get geometry object from data you send to the GEOMETRY field +DROP TABLE t1; diff --git a/mysql-test/t/gis-rtree.test b/mysql-test/t/gis-rtree.test index 163f2806ad2..091811b05b3 100644 --- a/mysql-test/t/gis-rtree.test +++ b/mysql-test/t/gis-rtree.test @@ -232,3 +232,15 @@ CHECK TABLE t1 EXTENDED; DROP TABLE t1; # End of 4.1 tests + +# +# bug #21790 (UNKNOWN ERROR on NULLs in RTree) +# +CREATE TABLE t1(foo GEOMETRY NOT NULL, SPATIAL INDEX(foo) ); +--error 1048 +INSERT INTO t1(foo) VALUES (NULL); +--error 1416 +INSERT INTO t1() VALUES (); +--error 1416 +INSERT INTO t1(foo) VALUES (''); +DROP TABLE t1; diff --git a/sql/handler.cc b/sql/handler.cc index 4accc746664..3516b55feb5 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -1788,8 +1788,8 @@ void handler::print_error(int error, myf errflag) break; } case HA_ERR_NULL_IN_SPATIAL: - textno= ER_UNKNOWN_ERROR; - break; + my_error(ER_CANT_CREATE_GEOMETRY_OBJECT, MYF(0)); + DBUG_VOID_RETURN; case HA_ERR_FOUND_DUPP_UNIQUE: textno=ER_DUP_UNIQUE; break; From d8b6f46a390951d3da038bd366b38760ae5a38c9 Mon Sep 17 00:00:00 2001 From: "igor@rurik.mysql.com" <> Date: Thu, 19 Oct 2006 23:05:53 -0700 Subject: [PATCH 02/65] Fixed bug #23478. If elements a not top-level IN subquery were accessed by an index and the subquery result set included a NULL value then the quantified predicate that contained the subquery was evaluated to NULL when it should return a non-null value. --- mysql-test/r/subselect.result | 15 +++++++++++++++ mysql-test/t/subselect.test | 17 +++++++++++++++++ sql/item_subselect.cc | 3 +++ 3 files changed, 35 insertions(+) diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index ad847b5f156..28fbfc86657 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -2982,3 +2982,18 @@ field1 field2 1 1 1 3 DROP TABLE t1, t2; +CREATE TABLE t1(a int, INDEX (a)); +INSERT INTO t1 VALUES (1), (3), (5), (7); +INSERT INTO t1 VALUES (NULL); +CREATE TABLE t2(a int); +INSERT INTO t2 VALUES (1),(2),(3); +EXPLAIN SELECT a, a IN (SELECT a FROM t1) FROM t2; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 3 +2 DEPENDENT SUBQUERY t1 index_subquery a a 5 func 2 Using index +SELECT a, a IN (SELECT a FROM t1) FROM t2; +a a IN (SELECT a FROM t1) +1 1 +2 NULL +3 1 +DROP TABLE t1,t2; diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index 6defa8b16a5..ac035c72d18 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -1948,4 +1948,21 @@ SELECT field1, field2 DROP TABLE t1, t2; +# +# Bug #23478: not top-level IN subquery returning a non-empty result set +# with possible NULL values by index access from the outer query +# + +CREATE TABLE t1(a int, INDEX (a)); +INSERT INTO t1 VALUES (1), (3), (5), (7); +INSERT INTO t1 VALUES (NULL); + +CREATE TABLE t2(a int); +INSERT INTO t2 VALUES (1),(2),(3); + +EXPLAIN SELECT a, a IN (SELECT a FROM t1) FROM t2; +SELECT a, a IN (SELECT a FROM t1) FROM t2; + +DROP TABLE t1,t2; + # End of 4.1 tests diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index f3be0663af8..1ab81d1862d 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -610,6 +610,7 @@ double Item_in_subselect::val() */ DBUG_ASSERT(0); DBUG_ASSERT(fixed == 1); + null_value= 0; if (exec()) { reset(); @@ -625,6 +626,7 @@ double Item_in_subselect::val() longlong Item_in_subselect::val_int() { DBUG_ASSERT(fixed == 1); + null_value= 0; if (exec()) { reset(); @@ -645,6 +647,7 @@ String *Item_in_subselect::val_str(String *str) */ DBUG_ASSERT(0); DBUG_ASSERT(fixed == 1); + null_value= 0; if (exec()) { reset(); From d8a836945487660ee931ceff26e27233bde4e4cf Mon Sep 17 00:00:00 2001 From: "igor@rurik.mysql.com" <> Date: Fri, 20 Oct 2006 12:41:27 -0700 Subject: [PATCH 03/65] Adjustments after the merge for bug 23478. --- sql/item_subselect.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 78dae0aaa1e..7015f450aa7 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -729,6 +729,7 @@ String *Item_in_subselect::val_str(String *str) bool Item_in_subselect::val_bool() { DBUG_ASSERT(fixed == 1); + null_value= 0; if (exec()) { reset(); @@ -747,6 +748,7 @@ my_decimal *Item_in_subselect::val_decimal(my_decimal *decimal_value) method should not be used */ DBUG_ASSERT(0); + null_value= 0; DBUG_ASSERT(fixed == 1); if (exec()) { From 4d23559806b9380f985e42350b69bcfb03403208 Mon Sep 17 00:00:00 2001 From: "holyfoot/hf@mysql.com/deer.(none)" <> Date: Mon, 23 Oct 2006 15:02:51 +0500 Subject: [PATCH 04/65] WL#3475 (Threads for the embedded server in mysqltest) Necessary code added to mysqltest.c. Disabled tests are available now. --- client/mysqltest.c | 102 +++++++++++++++++++++++---- libmysql/libmysql.c | 7 -- mysql-test/t/bdb-deadlock.test | 8 --- mysql-test/t/flush.test | 8 --- mysql-test/t/flush_block_commit.test | 3 - mysql-test/t/innodb-deadlock.test | 2 - mysql-test/t/innodb-lock.test | 2 - mysql-test/t/lock_multi.test | 8 --- mysql-test/t/rename.test | 4 -- mysql-test/t/show_check.test | 3 +- mysql-test/t/status.test | 7 -- 11 files changed, 90 insertions(+), 64 deletions(-) diff --git a/client/mysqltest.c b/client/mysqltest.c index ad0f9f857bb..6889ae1a84c 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -23,6 +23,7 @@ * Matt Wagner * Monty * Jani + * Holyfoot **/ /********************************************************************** @@ -215,6 +216,12 @@ struct connection { MYSQL mysql; char *name; + + const char *cur_query; + int cur_query_len; + pthread_mutex_t mutex; + pthread_cond_t cond; + int query_done; }; typedef struct @@ -461,6 +468,57 @@ static void replace_dynstr_append_mem(DYNAMIC_STRING *ds, const char *val, int len); static int handle_no_error(struct st_query *q); +#ifdef EMBEDDED_LIBRARY +/* + send_one_query executes query in separate thread what is + necessary in embedded library to run 'send' in proper way. + This implementation doesn't handle errors returned + by mysql_send_query. It's technically possible, though + i don't see where it is needed. +*/ +pthread_handler_decl(send_one_query, arg) +{ + struct connection *cn= (struct connection*)arg; + + mysql_thread_init(); + VOID(mysql_send_query(&cn->mysql, cn->cur_query, cn->cur_query_len)); + + mysql_thread_end(); + pthread_mutex_lock(&cn->mutex); + cn->query_done= 1; + VOID(pthread_cond_signal(&cn->cond)); + pthread_mutex_unlock(&cn->mutex); + pthread_exit(0); + return 0; +} + +static int do_send_query(struct connection *cn, const char *q, int q_len, + int flags) +{ + pthread_t tid; + + if (flags & QUERY_REAP) + return mysql_send_query(&cn->mysql, q, q_len); + + if (pthread_mutex_init(&cn->mutex, NULL) || + pthread_cond_init(&cn->cond, NULL)) + die("Error in the thread library"); + + cn->cur_query= q; + cn->cur_query_len= q_len; + cn->query_done= 0; + if (pthread_create(&tid, NULL, send_one_query, (void*)cn)) + die("Cannot start new thread for query"); + + return 0; +} + +#else /*EMBEDDED_LIBRARY*/ + +#define do_send_query(cn,q,q_len,flags) mysql_send_query(&cn->mysql, q, q_len) + +#endif /*EMBEDDED_LIBRARY*/ + static void do_eval(DYNAMIC_STRING* query_eval, const char *query) { const char *p; @@ -1849,7 +1907,7 @@ int close_connection(struct st_query *q) #ifndef EMBEDDED_LIBRARY if (q->type == Q_DIRTY_CLOSE) { - if (con->mysql.net.vio) + while (con->mysql.net.vio) { vio_delete(con->mysql.net.vio); con->mysql.net.vio = 0; @@ -2767,15 +2825,17 @@ static void append_result(DYNAMIC_STRING *ds, MYSQL_RES *res) * the result will be read - for regular query, both bits must be on */ -static int run_query_normal(MYSQL *mysql, struct st_query *q, int flags); -static int run_query_stmt (MYSQL *mysql, struct st_query *q, int flags); +static int run_query_normal(struct connection *cn, struct st_query *q, + int flags); +static int run_query_stmt (struct connection *cn, struct st_query *q, + int flags); static void run_query_stmt_handle_warnings(MYSQL *mysql, DYNAMIC_STRING *ds); static int run_query_stmt_handle_error(char *query, struct st_query *q, MYSQL_STMT *stmt, DYNAMIC_STRING *ds); static void run_query_display_metadata(MYSQL_FIELD *field, uint num_fields, DYNAMIC_STRING *ds); -static int run_query(MYSQL *mysql, struct st_query *q, int flags) +static int run_query(struct connection *cn, struct st_query *q, int flags) { /* @@ -2791,13 +2851,15 @@ static int run_query(MYSQL *mysql, struct st_query *q, int flags) if (ps_protocol_enabled && disable_info && (flags & QUERY_SEND) && (flags & QUERY_REAP) && ps_match_re(q->query)) - return run_query_stmt(mysql, q, flags); - return run_query_normal(mysql, q, flags); + return run_query_stmt(cn, q, flags); + return run_query_normal(cn, q, flags); } -static int run_query_normal(MYSQL* mysql, struct st_query* q, int flags) +static int run_query_normal(struct connection *cn, struct st_query* q, + int flags) { + MYSQL *mysql= &cn->mysql; MYSQL_RES* res= 0; uint i; int error= 0, err= 0, counter= 0; @@ -2833,11 +2895,24 @@ static int run_query_normal(MYSQL* mysql, struct st_query* q, int flags) if (flags & QUERY_SEND) { - got_error_on_send= mysql_send_query(mysql, query, query_len); + got_error_on_send= do_send_query(cn, query, query_len, flags); if (got_error_on_send && q->expected_errno[0].type == ERR_EMPTY) die("unable to send query '%s' (mysql_errno=%d , errno=%d)", query, mysql_errno(mysql), errno); } +#ifdef EMBEDDED_LIBRARY + /* + Here we handle 'reap' command, so we need to check if the + query's thread was finished and probably wait + */ + else if (flags & QUERY_REAP) + { + pthread_mutex_lock(&cn->mutex); + if (!cn->query_done) + pthread_cond_wait(&cn->cond, &cn->mutex); + pthread_mutex_unlock(&cn->mutex); + } +#endif /*EMBEDDED_LIBRARY*/ do { @@ -3038,8 +3113,9 @@ end: complete SEND+REAP */ -static int run_query_stmt(MYSQL *mysql, struct st_query *q, int flags) +static int run_query_stmt(struct connection *cn, struct st_query *q, int flags) { + MYSQL *mysql= &cn->mysql; int error= 0; /* Function return code if "goto end;" */ int err; /* Temporary storage of return code from calls */ int query_len, got_error_on_execute; @@ -3095,7 +3171,7 @@ static int run_query_stmt(MYSQL *mysql, struct st_query *q, int flags) C API. */ if ((err= mysql_stmt_prepare(stmt, query, query_len)) == CR_NO_PREPARE_STMT) - return run_query_normal(mysql, q, flags); + return run_query_normal(cn, q, flags); if (err != 0) { @@ -3922,7 +3998,7 @@ int main(int argc, char **argv) q->require_file=require_file; save_file[0]=0; } - error|= run_query(&cur_con->mysql, q, QUERY_REAP|QUERY_SEND); + error|= run_query(cur_con, q, QUERY_REAP|QUERY_SEND); display_result_vertically= old_display_result_vertically; q->last_argument= q->end; query_executed= 1; @@ -3949,7 +4025,7 @@ int main(int argc, char **argv) q->require_file=require_file; save_file[0]=0; } - error |= run_query(&cur_con->mysql, q, flags); + error |= run_query(cur_con, q, flags); query_executed= 1; q->last_argument= q->end; break; @@ -3970,7 +4046,7 @@ int main(int argc, char **argv) query and read the result some time later when reap instruction is given on this connection. */ - error |= run_query(&cur_con->mysql, q, QUERY_SEND); + error |= run_query(cur_con, q, QUERY_SEND); query_executed= 1; q->last_argument= q->end; break; diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index 91c0b6b8864..5577ecdb556 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -4395,13 +4395,6 @@ int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt) set_stmt_error(stmt, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate); DBUG_RETURN(1); } - if (result->data) - { - free_root(&result->alloc, MYF(MY_KEEP_PREALLOC)); - result->data= NULL; - result->rows= 0; - stmt->data_cursor= NULL; - } if (stmt->update_max_length && !stmt->bind_result_done) { diff --git a/mysql-test/t/bdb-deadlock.test b/mysql-test/t/bdb-deadlock.test index 88243cfc860..b48648e0fd0 100644 --- a/mysql-test/t/bdb-deadlock.test +++ b/mysql-test/t/bdb-deadlock.test @@ -1,11 +1,3 @@ -# This test doesn't work with the embedded version as this code -# assumes that one query is running while we are doing queries on -# a second connection. -# This would work if mysqltest run would be threaded and handle each -# connection in a separate thread. -# - --- source include/not_embedded.inc -- source include/have_bdb.inc connect (con1,localhost,root,,); diff --git a/mysql-test/t/flush.test b/mysql-test/t/flush.test index aedf8e85b65..8fe62ecac01 100644 --- a/mysql-test/t/flush.test +++ b/mysql-test/t/flush.test @@ -1,11 +1,3 @@ -# This test doesn't work with the embedded version as this code -# assumes that one query is running while we are doing queries on -# a second connection. -# This would work if mysqltest run would be threaded and handle each -# connection in a separate thread. -# --- source include/not_embedded.inc - connect (con1,localhost,root,,); connect (con2,localhost,root,,); connection con1; diff --git a/mysql-test/t/flush_block_commit.test b/mysql-test/t/flush_block_commit.test index 1e7ecd2548c..0c1d2b82df6 100644 --- a/mysql-test/t/flush_block_commit.test +++ b/mysql-test/t/flush_block_commit.test @@ -3,9 +3,6 @@ # We verify that we did not introduce a deadlock. # This is intended to mimick how mysqldump and innobackup work. -# This test doesn't work with the embedded server --- source include/not_embedded.inc - # And it requires InnoDB -- source include/have_innodb.inc diff --git a/mysql-test/t/innodb-deadlock.test b/mysql-test/t/innodb-deadlock.test index 41741942963..81acfba5c93 100644 --- a/mysql-test/t/innodb-deadlock.test +++ b/mysql-test/t/innodb-deadlock.test @@ -1,6 +1,4 @@ -- source include/have_innodb.inc -# Can't test this with embedded server --- source include/not_embedded.inc connect (con1,localhost,root,,); connect (con2,localhost,root,,); diff --git a/mysql-test/t/innodb-lock.test b/mysql-test/t/innodb-lock.test index 55a712fef9b..eacf7e562be 100644 --- a/mysql-test/t/innodb-lock.test +++ b/mysql-test/t/innodb-lock.test @@ -1,6 +1,4 @@ -- source include/have_innodb.inc -# Can't test this with embedded server --- source include/not_embedded.inc # # Check and select innodb lock type diff --git a/mysql-test/t/lock_multi.test b/mysql-test/t/lock_multi.test index 2e40aeaccb7..32e7f4234c4 100644 --- a/mysql-test/t/lock_multi.test +++ b/mysql-test/t/lock_multi.test @@ -1,11 +1,3 @@ -# This test doesn't work with the embedded version as this code -# assumes that one query is running while we are doing queries on -# a second connection. -# This would work if mysqltest run would be threaded and handle each -# connection in a separate thread. -# --- source include/not_embedded.inc - --disable_warnings drop table if exists t1,t2; --enable_warnings diff --git a/mysql-test/t/rename.test b/mysql-test/t/rename.test index 5caecef176e..ad9921d2cf0 100644 --- a/mysql-test/t/rename.test +++ b/mysql-test/t/rename.test @@ -2,10 +2,6 @@ # Test of rename table # -# Test requires concurrent connections, which can't be tested on embedded -# server --- source include/not_embedded.inc - --disable_warnings drop table if exists t0,t1,t2,t3,t4; # Clear up from other tests (to ensure that SHOW TABLES below is right) diff --git a/mysql-test/t/show_check.test b/mysql-test/t/show_check.test index d70903adbc4..8be676d9a35 100644 --- a/mysql-test/t/show_check.test +++ b/mysql-test/t/show_check.test @@ -1,5 +1,4 @@ -# Requires use of multiple simultaneous connections, not supported with -# embedded server testing +# Uses GRANT commands that usually disabled in embedded server -- source include/not_embedded.inc # diff --git a/mysql-test/t/status.test b/mysql-test/t/status.test index 7fea51c9327..df8da26df57 100644 --- a/mysql-test/t/status.test +++ b/mysql-test/t/status.test @@ -1,10 +1,3 @@ -# This test doesn't work with the embedded version as this code -# assumes that one query is running while we are doing queries on -# a second connection. -# This would work if mysqltest run would be threaded and handle each -# connection in a separate thread. -# ---source include/not_embedded.inc # PS causes different statistics --disable_ps_protocol From 836e159ff3dba3071048380f5261058e3a024960 Mon Sep 17 00:00:00 2001 From: "holyfoot/hf@mysql.com/deer.(none)" <> Date: Tue, 24 Oct 2006 12:35:32 +0500 Subject: [PATCH 05/65] merging fix --- client/mysqltest.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/mysqltest.c b/client/mysqltest.c index 6889ae1a84c..3294612f7cc 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -1907,7 +1907,7 @@ int close_connection(struct st_query *q) #ifndef EMBEDDED_LIBRARY if (q->type == Q_DIRTY_CLOSE) { - while (con->mysql.net.vio) + if (con->mysql.net.vio) { vio_delete(con->mysql.net.vio); con->mysql.net.vio = 0; @@ -2908,7 +2908,7 @@ static int run_query_normal(struct connection *cn, struct st_query* q, else if (flags & QUERY_REAP) { pthread_mutex_lock(&cn->mutex); - if (!cn->query_done) + while (!cn->query_done) pthread_cond_wait(&cn->cond, &cn->mutex); pthread_mutex_unlock(&cn->mutex); } From 97df0fce16426f97accaaa50936d37bdc6880a1b Mon Sep 17 00:00:00 2001 From: "holyfoot/hf@mysql.com/deer.(none)" <> Date: Tue, 24 Oct 2006 15:28:16 +0500 Subject: [PATCH 06/65] WL#3475 merging --- client/mysqltest.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/client/mysqltest.c b/client/mysqltest.c index f04df7db863..261a2ae2870 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -496,7 +496,7 @@ static void handle_no_error(struct st_query *q); by mysql_send_query. It's technically possible, though i don't see where it is needed. */ -pthread_handler_decl(send_one_query, arg) +pthread_handler_t send_one_query(void *arg) { struct connection *cn= (struct connection*)arg; @@ -2097,7 +2097,7 @@ int close_connection(struct st_query *q) #ifndef EMBEDDED_LIBRARY if (q->type == Q_DIRTY_CLOSE) { - while (con->mysql.net.vio) + if (con->mysql.net.vio) { vio_delete(con->mysql.net.vio); con->mysql.net.vio = 0; @@ -3684,7 +3684,7 @@ static void run_query_normal(struct connection *cn, struct st_query *command, else if (flags & QUERY_REAP) { pthread_mutex_lock(&cn->mutex); - if (!cn->query_done) + while (!cn->query_done) pthread_cond_wait(&cn->cond, &cn->mutex); pthread_mutex_unlock(&cn->mutex); } @@ -3939,11 +3939,10 @@ static void handle_no_error(struct st_query *q) error - function will not return */ -static void run_query_stmt(struct connection *cn, struct st_query *command, +static void run_query_stmt(MYSQL *mysql, struct st_query *command, char *query, int query_len, DYNAMIC_STRING *ds, DYNAMIC_STRING *ds_warnings) { - MYSQL *mysql= &cn->mysql; MYSQL_RES *res= NULL; /* Note that here 'res' is meta data result set */ MYSQL_STMT *stmt; DYNAMIC_STRING ds_prepare_warnings; @@ -4175,8 +4174,10 @@ static int util_query(MYSQL* org_mysql, const char* query){ */ -static void run_query(MYSQL *mysql, struct st_query *command, int flags) +static void run_query(struct connection *cn, struct st_query *command, + int flags) { + MYSQL *mysql= &cn->mysql; DYNAMIC_STRING *ds; DYNAMIC_STRING ds_result; DYNAMIC_STRING ds_warnings; @@ -4329,7 +4330,7 @@ static void run_query(MYSQL *mysql, struct st_query *command, int flags) match_re(&ps_re, query)) run_query_stmt(mysql, command, query, query_len, ds, &ds_warnings); else - run_query_normal(mysql, command, flags, query, query_len, + run_query_normal(cn, command, flags, query, query_len, ds, &ds_warnings); if (sp_created) From d0ef58b6a481134610640857ec54d0259f151669 Mon Sep 17 00:00:00 2001 From: "holyfoot/hf@mysql.com/deer.(none)" <> Date: Tue, 24 Oct 2006 17:19:02 +0500 Subject: [PATCH 07/65] Bug #23427 (incompatible ABI change) the incompatibility was caused by current_stmt member added to the MYSQL structure. It's possible to move it to THD structure instead which saves ABI --- include/mysql.h | 6 ------ libmysqld/lib_sql.cc | 10 +++++----- sql/sql_class.h | 6 ++++++ 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/include/mysql.h b/include/mysql.h index 143f6752c46..89e861864df 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -270,12 +270,6 @@ typedef struct st_mysql from mysql_stmt_close if close had to cancel result set of this object. */ my_bool *unbuffered_fetch_owner; - /* - In embedded server it points to the statement that is processed - in the current query. We store some results directly in statement - fields then. - */ - struct st_mysql_stmt *current_stmt; } MYSQL; typedef struct st_mysql_res { diff --git a/libmysqld/lib_sql.cc b/libmysqld/lib_sql.cc index 1a3e10f08a8..64bc37fb40d 100644 --- a/libmysqld/lib_sql.cc +++ b/libmysqld/lib_sql.cc @@ -94,7 +94,7 @@ emb_advanced_command(MYSQL *mysql, enum enum_server_command command, mysql->affected_rows= ~(my_ulonglong) 0; mysql->field_count= 0; net->last_errno= 0; - mysql->current_stmt= stmt; + thd->current_stmt= stmt; thd->store_globals(); // Fix if more than one connect /* @@ -644,8 +644,8 @@ bool Protocol::send_fields(List *list, uint flag) DBUG_RETURN(0); field_count= list->elements; - field_alloc= mysql->current_stmt ? &mysql->current_stmt->mem_root : - &mysql->field_alloc; + field_alloc= thd->current_stmt ? &thd->current_stmt->mem_root : + &mysql->field_alloc; if (!(client_field= mysql->fields= (MYSQL_FIELD *)alloc_root(field_alloc, sizeof(MYSQL_FIELD) * field_count))) @@ -751,8 +751,8 @@ bool Protocol_prep::write() { MYSQL *mysql= thd->mysql; - if (mysql->current_stmt) - data= &mysql->current_stmt->result; + if (thd->current_stmt) + data= &thd->current_stmt->result; else { if (!(data= (MYSQL_DATA*) my_malloc(sizeof(MYSQL_DATA), diff --git a/sql/sql_class.h b/sql/sql_class.h index cc90de2a6ea..ed161de55de 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -686,6 +686,12 @@ public: char *extra_data; ulong extra_length; String query_rest; + /* + In embedded server it points to the statement that is processed + in the current query. We store some results directly in statement + fields then. + */ + struct st_mysql_stmt *current_stmt; #endif NET net; // client connection descriptor MEM_ROOT warn_root; // For warnings and errors From 932d86bbb93328d5916b2a9e5b1b788ddf820742 Mon Sep 17 00:00:00 2001 From: "gkodinov/kgeorge@macbook.gmz" <> Date: Tue, 24 Oct 2006 15:26:41 +0300 Subject: [PATCH 08/65] Bug #21809: Error 1356 while selecting from view with grouping though underlying select OK. The SQL parser was using Item::name to transfer user defined function attributes to the user defined function (udf). It was not distinguishing between user defined function call arguments and stored procedure call arguments. Setting Item::name was causing Item_ref::print() method to print the argument as quoted identifiers and caused views that reference aggregate functions as udf call arguments (and rely on Item::print() for the text of the view to store) to throw an undefined identifier error. Overloaded Item_ref::print to print aggregate functions as such when printing the references to aggregate functions taken out of context by split_sum_func2() Fixed the parser to properly detect using AS clause in stored procedure arguments as an error. Fixed printing the arguments of udf call to print properly the udf attribute. --- mysql-test/r/udf.result | 79 +++++++++++++++++++++++++++++++++++++++++ mysql-test/t/udf.test | 44 +++++++++++++++++++++++ sql/item.cc | 26 ++++++++++++-- sql/item_func.cc | 14 ++++++++ sql/item_func.h | 1 + sql/sql_lex.cc | 2 ++ sql/sql_lex.h | 2 ++ sql/sql_yacc.yy | 41 +++++++++++++++------ 8 files changed, 196 insertions(+), 13 deletions(-) diff --git a/mysql-test/r/udf.result b/mysql-test/r/udf.result index 8e37cca6aa9..396f1efa1b7 100644 --- a/mysql-test/r/udf.result +++ b/mysql-test/r/udf.result @@ -105,6 +105,85 @@ explain select myfunc_int(f1) from t1 order by 1; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 2 Using temporary; Using filesort drop table t1; +CREATE TABLE t1(a INT, b INT); +INSERT INTO t1 values (1,1),(2,2); +CREATE FUNCTION fn(a int) RETURNS int DETERMINISTIC +BEGIN +RETURN a; +END +|| +CREATE VIEW v1 AS SELECT a, fn(MIN(b)) as c FROM t1 GROUP BY a; +SELECT myfunc_int(a AS attr_name) FROM t1; +myfunc_int(a AS attr_name) +1 +2 +EXPLAIN EXTENDED SELECT myfunc_int(a AS attr_name) FROM t1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 +Warnings: +Note 1003 select myfunc_int(`test`.`t1`.`a` AS `attr_name`) AS `myfunc_int(a AS attr_name)` from `test`.`t1` +EXPLAIN EXTENDED SELECT myfunc_int(a) FROM t1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 +Warnings: +Note 1003 select myfunc_int(`test`.`t1`.`a` AS `a`) AS `myfunc_int(a)` from `test`.`t1` +SELECT a,c FROM v1; +a c +1 1 +2 2 +SELECT a, fn(MIN(b) xx) as c FROM t1 GROUP BY a; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'xx) as c FROM t1 GROUP BY a' at line 1 +SELECT myfunc_int(fn(MIN(b) xx)) as c FROM t1 GROUP BY a; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'xx)) as c FROM t1 GROUP BY a' at line 1 +SELECT myfunc_int(test.fn(MIN(b) xx)) as c FROM t1 GROUP BY a; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'xx)) as c FROM t1 GROUP BY a' at line 1 +SELECT myfunc_int(fn(MIN(b)) xx) as c FROM t1 GROUP BY a; +c +1 +2 +SELECT myfunc_int(test.fn(MIN(b)) xx) as c FROM t1 GROUP BY a; +c +1 +2 +EXPLAIN EXTENDED SELECT myfunc_int(MIN(b) xx) as c FROM t1 GROUP BY a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 Using temporary; Using filesort +Warnings: +Note 1003 select myfunc_int(min(`test`.`t1`.`b`) AS `xx`) AS `c` from `test`.`t1` group by `test`.`t1`.`a` +EXPLAIN EXTENDED SELECT test.fn(MIN(b)) as c FROM t1 GROUP BY a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 Using temporary; Using filesort +Warnings: +Note 1003 select `test`.`fn`(min(`test`.`t1`.`b`)) AS `c` from `test`.`t1` group by `test`.`t1`.`a` +EXPLAIN EXTENDED SELECT myfunc_int(fn(MIN(b))) as c FROM t1 GROUP BY a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 Using temporary; Using filesort +Warnings: +Note 1003 select myfunc_int(`test`.`fn`(min(`test`.`t1`.`b`)) AS `fn(MIN(b))`) AS `c` from `test`.`t1` group by `test`.`t1`.`a` +EXPLAIN EXTENDED SELECT myfunc_int(test.fn(MIN(b))) as c FROM t1 GROUP BY a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 Using temporary; Using filesort +Warnings: +Note 1003 select myfunc_int(`test`.`fn`(min(`test`.`t1`.`b`)) AS `test.fn(MIN(b))`) AS `c` from `test`.`t1` group by `test`.`t1`.`a` +SELECT myfunc_int(MIN(b) xx) as c FROM t1 GROUP BY a; +c +1 +2 +SELECT test.fn(MIN(b)) as c FROM t1 GROUP BY a; +c +1 +2 +SELECT myfunc_int(fn(MIN(b))) as c FROM t1 GROUP BY a; +c +1 +2 +SELECT myfunc_int(test.fn(MIN(b))) as c FROM t1 GROUP BY a; +c +1 +2 +DROP VIEW v1; +DROP TABLE t1; +DROP FUNCTION fn; End of 5.0 tests. DROP FUNCTION metaphon; DROP FUNCTION myfunc_double; diff --git a/mysql-test/t/udf.test b/mysql-test/t/udf.test index 96e559f5c05..37358a292be 100644 --- a/mysql-test/t/udf.test +++ b/mysql-test/t/udf.test @@ -127,6 +127,50 @@ create table t1(f1 int); insert into t1 values(1),(2); explain select myfunc_int(f1) from t1 order by 1; drop table t1; + +# +# Bug #21809: Error 1356 while selecting from view with grouping though +# underlying select OK. +# +CREATE TABLE t1(a INT, b INT); INSERT INTO t1 values (1,1),(2,2); + +DELIMITER ||; +CREATE FUNCTION fn(a int) RETURNS int DETERMINISTIC +BEGIN + RETURN a; +END +|| +DELIMITER ;|| + +CREATE VIEW v1 AS SELECT a, fn(MIN(b)) as c FROM t1 GROUP BY a; + +SELECT myfunc_int(a AS attr_name) FROM t1; +EXPLAIN EXTENDED SELECT myfunc_int(a AS attr_name) FROM t1; +EXPLAIN EXTENDED SELECT myfunc_int(a) FROM t1; +SELECT a,c FROM v1; + +--error ER_PARSE_ERROR +SELECT a, fn(MIN(b) xx) as c FROM t1 GROUP BY a; +--error ER_PARSE_ERROR +SELECT myfunc_int(fn(MIN(b) xx)) as c FROM t1 GROUP BY a; +--error ER_PARSE_ERROR +SELECT myfunc_int(test.fn(MIN(b) xx)) as c FROM t1 GROUP BY a; + +SELECT myfunc_int(fn(MIN(b)) xx) as c FROM t1 GROUP BY a; +SELECT myfunc_int(test.fn(MIN(b)) xx) as c FROM t1 GROUP BY a; + +EXPLAIN EXTENDED SELECT myfunc_int(MIN(b) xx) as c FROM t1 GROUP BY a; +EXPLAIN EXTENDED SELECT test.fn(MIN(b)) as c FROM t1 GROUP BY a; +EXPLAIN EXTENDED SELECT myfunc_int(fn(MIN(b))) as c FROM t1 GROUP BY a; +EXPLAIN EXTENDED SELECT myfunc_int(test.fn(MIN(b))) as c FROM t1 GROUP BY a; +SELECT myfunc_int(MIN(b) xx) as c FROM t1 GROUP BY a; +SELECT test.fn(MIN(b)) as c FROM t1 GROUP BY a; +SELECT myfunc_int(fn(MIN(b))) as c FROM t1 GROUP BY a; +SELECT myfunc_int(test.fn(MIN(b))) as c FROM t1 GROUP BY a; +DROP VIEW v1; +DROP TABLE t1; +DROP FUNCTION fn; + --echo End of 5.0 tests. # diff --git a/sql/item.cc b/sql/item.cc index a0eef7a19e9..78becb129d1 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -1147,6 +1147,28 @@ void Item_name_const::print(String *str) } +/* + need a special class to adjust printing : references to aggregate functions + must not be printed as refs because the aggregate functions that are added to + the front of select list are not printed as well. +*/ +class Item_aggregate_ref : public Item_ref +{ +public: + Item_aggregate_ref(Name_resolution_context *context_arg, Item **item, + const char *table_name_arg, const char *field_name_arg) + :Item_ref(context_arg, item, table_name_arg, field_name_arg) {} + + void print (String *str) + { + if (ref) + (*ref)->print(str); + else + Item_ident::print(str); + } +}; + + /* Move SUM items out from item tree and replace with reference @@ -1200,8 +1222,8 @@ void Item::split_sum_func2(THD *thd, Item **ref_pointer_array, Item *new_item, *real_itm= real_item(); ref_pointer_array[el]= real_itm; - if (!(new_item= new Item_ref(&thd->lex->current_select->context, - ref_pointer_array + el, 0, name))) + if (!(new_item= new Item_aggregate_ref(&thd->lex->current_select->context, + ref_pointer_array + el, 0, name))) return; // fatal_error is set fields.push_front(real_itm); thd->change_item_tree(ref, new_item); diff --git a/sql/item_func.cc b/sql/item_func.cc index 2e594c74031..823435dd1e5 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -2869,6 +2869,20 @@ void Item_udf_func::cleanup() } +void Item_udf_func::print(String *str) +{ + str->append(func_name()); + str->append('('); + for (uint i=0 ; i < arg_count ; i++) + { + if (i != 0) + str->append(','); + args[i]->print_item_w_name(str); + } + str->append(')'); +} + + double Item_func_udf_float::val_real() { DBUG_ASSERT(fixed == 1); diff --git a/sql/item_func.h b/sql/item_func.h index 177daf0311f..6bbbf2caabd 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -951,6 +951,7 @@ public: Item_result result_type () const { return udf.result_type(); } table_map not_null_tables() const { return 0; } bool is_expensive() { return 1; } + void print(String *str); }; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 788276ac654..a65d36cb07c 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -163,6 +163,7 @@ void lex_start(THD *thd, uchar *buf,uint length) lex->select_lex.ftfunc_list= &lex->select_lex.ftfunc_list_alloc; lex->select_lex.group_list.empty(); lex->select_lex.order_list.empty(); + lex->select_lex.udf_list.empty(); lex->current_select= &lex->select_lex; lex->yacc_yyss=lex->yacc_yyvs=0; lex->ignore_space=test(thd->variables.sql_mode & MODE_IGNORE_SPACE); @@ -1166,6 +1167,7 @@ void st_select_lex::init_select() braces= 0; when_list.empty(); expr_list.empty(); + udf_list.empty(); interval_list.empty(); use_index.empty(); ftfunc_list_alloc.empty(); diff --git a/sql/sql_lex.h b/sql/sql_lex.h index fdf14c691e9..d7d480dabc3 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -580,6 +580,8 @@ public: /* exclude this select from check of unique_table() */ bool exclude_from_table_unique_test; + List udf_list; /* udf function calls stack */ + void init_query(); void init_select(); st_select_lex_unit* master_unit(); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index cb105d05332..e5c11f6d437 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -650,11 +650,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token UNIX_TIMESTAMP %token UNKNOWN_SYM %token UNLOCK_SYM -%token UNLOCK_SYM %token UNSIGNED %token UNTIL_SYM -%token UNTIL_SYM -%token UPDATE_SYM %token UPDATE_SYM %token UPGRADE_SYM %token USAGE @@ -764,7 +761,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type expr_list udf_expr_list udf_expr_list2 when_list - ident_list ident_list_arg + ident_list ident_list_arg opt_expr_list %type option_type opt_var_type opt_var_ident_type @@ -4724,7 +4721,7 @@ simple_expr: { $$= new Item_func_trim($5,$3); } | TRUNCATE_SYM '(' expr ',' expr ')' { $$= new Item_func_round($3,$5,1); } - | ident '.' ident '(' udf_expr_list ')' + | ident '.' ident '(' opt_expr_list ')' { LEX *lex= Lex; sp_name *name= new sp_name($1, $3); @@ -4741,27 +4738,27 @@ simple_expr: { #ifdef HAVE_DLOPEN udf_func *udf= 0; + LEX *lex= Lex; if (using_udf_functions && (udf= find_udf($1.str, $1.length)) && udf->type == UDFTYPE_AGGREGATE) { - LEX *lex= Lex; if (lex->current_select->inc_in_sum_expr()) { yyerror(ER(ER_SYNTAX_ERROR)); YYABORT; } } - $$= udf; + lex->current_select->udf_list.push_front(udf); #endif } udf_expr_list ')' { #ifdef HAVE_DLOPEN - udf_func *udf= $3; - SELECT_LEX *sel= Select; + udf_func *udf; + LEX *lex= Lex; - if (udf) + if (NULL != (udf= lex->current_select->udf_list.pop())) { if (udf->type == UDFTYPE_AGGREGATE) Select->in_sum_expr--; @@ -4988,12 +4985,29 @@ udf_expr_list3: udf_expr: remember_name expr remember_end select_alias { + udf_func *udf= Select->udf_list.head(); + /* + Use Item::name as a storage for the attribute value of user + defined function argument. It is safe to use Item::name + because the syntax will not allow having an explicit name here. + See WL#1017 re. udf attributes. + */ if ($4.str) { + if (!udf) + { + /* + Disallow using AS to specify explicit names for the arguments + of stored routine calls + */ + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + } + $2->is_autogenerated_name= FALSE; $2->set_name($4.str, $4.length, system_charset_info); } - else + else if (udf) $2->set_name($1, (uint) ($3 - $1), YYTHD->charset()); $$= $2; } @@ -5114,6 +5128,11 @@ cast_type: | DECIMAL_SYM float_options { $$=ITEM_CAST_DECIMAL; Lex->charset= NULL; } ; +opt_expr_list: + /* empty */ { $$= NULL; } + | expr_list { $$= $1;} + ; + expr_list: { Select->expr_list.push_front(new List); } expr_list2 From 4fb00857ca27d66fee465c85d882c7edf46ef828 Mon Sep 17 00:00:00 2001 From: "holyfoot/hf@mysql.com/deer.(none)" <> Date: Wed, 25 Oct 2006 20:14:39 +0500 Subject: [PATCH 09/65] bug #19491 (CAST do DATETIME wrong result) --- mysql-test/r/type_datetime.result | 12 +++ mysql-test/r/type_newdecimal.result | 8 ++ mysql-test/t/type_datetime.test | 10 ++ mysql-test/t/type_newdecimal.test | 8 ++ sql/field.cc | 7 ++ sql/field.h | 1 + sql/item.cc | 47 +++++++++ sql/item.h | 5 + sql/item_timefunc.cc | 11 -- sql/item_timefunc.h | 151 +++++++++++++++++++++------- sql/my_decimal.cc | 19 ++++ sql/my_decimal.h | 7 +- 12 files changed, 236 insertions(+), 50 deletions(-) diff --git a/mysql-test/r/type_datetime.result b/mysql-test/r/type_datetime.result index 49e4827cb97..7fc1c4f398d 100644 --- a/mysql-test/r/type_datetime.result +++ b/mysql-test/r/type_datetime.result @@ -179,3 +179,15 @@ a 2006-06-06 15:55:55 DROP PREPARE s; DROP TABLE t1; +SELECT CAST(CAST('2006-08-10' AS DATE) AS DECIMAL(20,6)); +CAST(CAST('2006-08-10' AS DATE) AS DECIMAL(20,6)) +20060810.000000 +SELECT CAST(CAST('2006-08-10 10:11:12' AS DATETIME) AS DECIMAL(20,6)); +CAST(CAST('2006-08-10 10:11:12' AS DATETIME) AS DECIMAL(20,6)) +20060810101112.000000 +SELECT CAST(CAST('2006-08-10 10:11:12' AS DATETIME) + INTERVAL 14 MICROSECOND AS DECIMAL(20,6)); +CAST(CAST('2006-08-10 10:11:12' AS DATETIME) + INTERVAL 14 MICROSECOND AS DECIMAL(20,6)) +20060810101112.000014 +SELECT CAST(CAST('10:11:12.098700' AS TIME) AS DECIMAL(20,6)); +CAST(CAST('10:11:12.098700' AS TIME) AS DECIMAL(20,6)) +101112.098700 diff --git a/mysql-test/r/type_newdecimal.result b/mysql-test/r/type_newdecimal.result index 33f1ece0390..f24014da285 100644 --- a/mysql-test/r/type_newdecimal.result +++ b/mysql-test/r/type_newdecimal.result @@ -1412,3 +1412,11 @@ i2 count(distinct j) 1.0 2 2.0 2 drop table t1; +create table t1(f1 decimal(20,6)); +insert into t1 values (CAST('10:11:12' AS date) + interval 14 microsecond); +insert into t1 values (CAST('10:11:12' AS time)); +select * from t1; +f1 +20101112000000.000014 +20101112.000000 +drop table t1; diff --git a/mysql-test/t/type_datetime.test b/mysql-test/t/type_datetime.test index cdf73bf6c89..3ad6bdc53e4 100644 --- a/mysql-test/t/type_datetime.test +++ b/mysql-test/t/type_datetime.test @@ -125,3 +125,13 @@ PREPARE s FROM 'SELECT a FROM t1 WHERE a=(SELECT MAX(a) FROM t1) AND (a="2006060 EXECUTE s; DROP PREPARE s; DROP TABLE t1; + + +# +# Bug 19491 (CAST DATE AS DECIMAL returns incorrect result +# +SELECT CAST(CAST('2006-08-10' AS DATE) AS DECIMAL(20,6)); +SELECT CAST(CAST('2006-08-10 10:11:12' AS DATETIME) AS DECIMAL(20,6)); +SELECT CAST(CAST('2006-08-10 10:11:12' AS DATETIME) + INTERVAL 14 MICROSECOND AS DECIMAL(20,6)); +SELECT CAST(CAST('10:11:12.098700' AS TIME) AS DECIMAL(20,6)); + diff --git a/mysql-test/t/type_newdecimal.test b/mysql-test/t/type_newdecimal.test index de1ebd74d17..12da41dcfd5 100644 --- a/mysql-test/t/type_newdecimal.test +++ b/mysql-test/t/type_newdecimal.test @@ -1108,3 +1108,11 @@ insert into t1 values (1,1), (1,2), (2,3), (2,4); select i, count(distinct j) from t1 group by i; select i+0.0 as i2, count(distinct j) from t1 group by i2; drop table t1; + + +create table t1(f1 decimal(20,6)); +insert into t1 values (CAST('10:11:12' AS date) + interval 14 microsecond); +insert into t1 values (CAST('10:11:12' AS time)); +select * from t1; +drop table t1; + diff --git a/sql/field.cc b/sql/field.cc index 4fea6a085bb..9858e873aa2 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -2413,6 +2413,13 @@ int Field_new_decimal::store_decimal(const my_decimal *decimal_value) } +int Field_new_decimal::store_time(TIME *ltime, timestamp_type t_type) +{ + my_decimal decimal_value; + return store_value(date2my_decimal(ltime, &decimal_value)); +} + + double Field_new_decimal::val_real(void) { double dbl; diff --git a/sql/field.h b/sql/field.h index 65e747e9d2f..08d4a2c7a53 100644 --- a/sql/field.h +++ b/sql/field.h @@ -489,6 +489,7 @@ public: int store(const char *to, uint length, CHARSET_INFO *charset); int store(double nr); int store(longlong nr, bool unsigned_val); + int store_time(TIME *ltime, timestamp_type t_type); int store_decimal(const my_decimal *); double val_real(void); longlong val_int(void); diff --git a/sql/item.cc b/sql/item.cc index 96b20d0f0bb..1e70788d9f4 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -272,6 +272,34 @@ my_decimal *Item::val_decimal_from_string(my_decimal *decimal_value) } +my_decimal *Item::val_decimal_from_date(my_decimal *decimal_value) +{ + DBUG_ASSERT(fixed == 1); + TIME ltime; + longlong date; + if (get_date(<ime, TIME_FUZZY_DATE)) + { + my_decimal_set_zero(decimal_value); + return 0; + } + return date2my_decimal(<ime, decimal_value); +} + + +my_decimal *Item::val_decimal_from_time(my_decimal *decimal_value) +{ + DBUG_ASSERT(fixed == 1); + TIME ltime; + longlong date; + if (get_time(<ime)) + { + my_decimal_set_zero(decimal_value); + return 0; + } + return date2my_decimal(<ime, decimal_value); +} + + double Item::val_real_from_decimal() { /* Note that fix_fields may not be called for Item_avg_field items */ @@ -295,6 +323,25 @@ longlong Item::val_int_from_decimal() return result; } +int Item::save_time_in_field(Field *field) +{ + TIME ltime; + if (get_time(<ime)) + return set_field_to_null(field); + field->set_notnull(); + return field->store_time(<ime, MYSQL_TIMESTAMP_TIME); +} + + +int Item::save_date_in_field(Field *field) +{ + TIME ltime; + if (get_date(<ime, TIME_FUZZY_DATE)) + return set_field_to_null(field); + field->set_notnull(); + return field->store_time(<ime, MYSQL_TIMESTAMP_DATETIME); +} + Item::Item(): rsize(0), name(0), orig_name(0), name_length(0), fixed(0), diff --git a/sql/item.h b/sql/item.h index e3df0fdf389..a820f78719a 100644 --- a/sql/item.h +++ b/sql/item.h @@ -605,9 +605,14 @@ public: my_decimal *val_decimal_from_real(my_decimal *decimal_value); my_decimal *val_decimal_from_int(my_decimal *decimal_value); my_decimal *val_decimal_from_string(my_decimal *decimal_value); + my_decimal *val_decimal_from_date(my_decimal *decimal_value); + my_decimal *val_decimal_from_time(my_decimal *decimal_value); longlong val_int_from_decimal(); double val_real_from_decimal(); + int save_time_in_field(Field *field); + int save_date_in_field(Field *field); + virtual Field *get_tmp_table_field() { return 0; } /* This is also used to create fields in CREATE ... SELECT: */ virtual Field *tmp_table_field(TABLE *t_arg) { return 0; } diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 30230005f6e..24a0d12ee9a 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -1294,17 +1294,6 @@ String *Item_date::val_str(String *str) } -int Item_date::save_in_field(Field *field, bool no_conversions) -{ - TIME ltime; - if (get_date(<ime, TIME_FUZZY_DATE)) - return set_field_to_null(field); - field->set_notnull(); - field->store_time(<ime, MYSQL_TIMESTAMP_DATE); - return 0; -} - - longlong Item_date::val_int() { DBUG_ASSERT(fixed == 1); diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index d5d3efeeab4..29978cf60a3 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -339,12 +339,20 @@ public: decimals=0; max_length=MAX_DATE_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; } - int save_in_field(Field *to, bool no_conversions); Field *tmp_table_field(TABLE *t_arg) { return (new Field_date(maybe_null, name, t_arg, &my_charset_bin)); } bool result_as_longlong() { return TRUE; } + my_decimal *val_decimal(my_decimal *decimal_value) + { + DBUG_ASSERT(fixed == 1); + return val_decimal_from_date(decimal_value); + } + int save_in_field(Field *field, bool no_conversions) + { + return save_date_in_field(field); + } }; @@ -361,21 +369,57 @@ public: return (new Field_datetime(maybe_null, name, t_arg, &my_charset_bin)); } bool result_as_longlong() { return TRUE; } + my_decimal *val_decimal(my_decimal *decimal_value) + { + DBUG_ASSERT(fixed == 1); + return val_decimal_from_date(decimal_value); + } + int save_in_field(Field *field, bool no_conversions) + { + return save_date_in_field(field); + } +}; + + +class Item_str_timefunc :public Item_str_func +{ +public: + Item_str_timefunc() :Item_str_func() {} + Item_str_timefunc(Item *a) :Item_str_func(a) {} + Item_str_timefunc(Item *a,Item *b) :Item_str_func(a,b) {} + Item_str_timefunc(Item *a, Item *b, Item *c) :Item_str_func(a, b ,c) {} + enum_field_types field_type() const { return MYSQL_TYPE_TIME; } + void fix_length_and_dec() + { + decimals=0; + max_length=MAX_TIME_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; + } + Field *tmp_table_field(TABLE *t_arg) + { + return (new Field_time(maybe_null, name, t_arg, &my_charset_bin)); + } + my_decimal *val_decimal(my_decimal *decimal_value) + { + DBUG_ASSERT(fixed == 1); + return val_decimal_from_time(decimal_value); + } + int save_in_field(Field *field, bool no_conversions) + { + return save_time_in_field(field); + } }; /* Abstract CURTIME function. Children should define what time zone is used */ -class Item_func_curtime :public Item_func +class Item_func_curtime :public Item_str_timefunc { longlong value; char buff[9*2+32]; uint buff_length; public: - Item_func_curtime() :Item_func() {} - Item_func_curtime(Item *a) :Item_func(a) {} - enum Item_result result_type () const { return STRING_RESULT; } - enum_field_types field_type() const { return MYSQL_TYPE_TIME; } + Item_func_curtime() :Item_str_timefunc() {} + Item_func_curtime(Item *a) :Item_str_timefunc(a) {} double val_real() { DBUG_ASSERT(fixed == 1); return (double) value; } longlong val_int() { DBUG_ASSERT(fixed == 1); return value; } String *val_str(String *str); @@ -602,10 +646,10 @@ class Item_func_convert_tz :public Item_date_func }; -class Item_func_sec_to_time :public Item_str_func +class Item_func_sec_to_time :public Item_str_timefunc { public: - Item_func_sec_to_time(Item *item) :Item_str_func(item) {} + Item_func_sec_to_time(Item *item) :Item_str_timefunc(item) {} double val_real() { DBUG_ASSERT(fixed == 1); @@ -615,17 +659,12 @@ public: String *val_str(String *); void fix_length_and_dec() { + Item_str_timefunc::fix_length_and_dec(); collation.set(&my_charset_bin); maybe_null=1; decimals= DATETIME_DEC; - max_length=MAX_TIME_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; } - enum_field_types field_type() const { return MYSQL_TYPE_TIME; } const char *func_name() const { return "sec_to_time"; } - Field *tmp_table_field(TABLE *t_arg) - { - return (new Field_time(maybe_null, name, t_arg, &my_charset_bin)); - } bool result_as_longlong() { return TRUE; } }; @@ -759,6 +798,15 @@ public: } bool result_as_longlong() { return TRUE; } longlong val_int(); + my_decimal *val_decimal(my_decimal *decimal_value) + { + DBUG_ASSERT(fixed == 1); + return val_decimal_from_date(decimal_value); + } + int save_in_field(Field *field, bool no_conversions) + { + return save_date_in_field(field); + } }; @@ -777,6 +825,15 @@ public: } bool result_as_longlong() { return TRUE; } longlong val_int(); + my_decimal *val_decimal(my_decimal *decimal_value) + { + DBUG_ASSERT(fixed == 1); + return val_decimal_from_time(decimal_value); + } + int save_in_field(Field *field, bool no_conversions) + { + return save_time_in_field(field); + } }; @@ -794,12 +851,21 @@ public: } bool result_as_longlong() { return TRUE; } longlong val_int(); + my_decimal *val_decimal(my_decimal *decimal_value) + { + DBUG_ASSERT(fixed == 1); + return val_decimal_from_date(decimal_value); + } + int save_in_field(Field *field, bool no_conversions) + { + return save_date_in_field(field); + } }; -class Item_func_makedate :public Item_str_func +class Item_func_makedate :public Item_date_func { public: - Item_func_makedate(Item *a,Item *b) :Item_str_func(a,b) {} + Item_func_makedate(Item *a,Item *b) :Item_date_func(a,b) {} String *val_str(String *str); const char *func_name() const { return "makedate"; } enum_field_types field_type() const { return MYSQL_TYPE_DATE; } @@ -812,8 +878,16 @@ public: { return (new Field_date(maybe_null, name, t_arg, &my_charset_bin)); } - bool result_as_longlong() { return TRUE; } longlong val_int(); + my_decimal *val_decimal(my_decimal *decimal_value) + { + DBUG_ASSERT(fixed == 1); + return val_decimal_from_date(decimal_value); + } + int save_in_field(Field *field, bool no_conversions) + { + return save_date_in_field(field); + } }; @@ -845,45 +919,46 @@ public: } void print(String *str); const char *func_name() const { return "add_time"; } + my_decimal *val_decimal(my_decimal *decimal_value) + { + DBUG_ASSERT(fixed == 1); + if (cached_field_type == MYSQL_TYPE_TIME) + return val_decimal_from_time(decimal_value); + if (cached_field_type == MYSQL_TYPE_DATETIME) + return val_decimal_from_date(decimal_value); + return Item_str_func::val_decimal(decimal_value); + } + int save_in_field(Field *field, bool no_conversions) + { + if (cached_field_type == MYSQL_TYPE_TIME) + return save_time_in_field(field); + if (cached_field_type == MYSQL_TYPE_DATETIME) + return save_date_in_field(field); + return Item_str_func::save_in_field(field, no_conversions); + } }; -class Item_func_timediff :public Item_str_func +class Item_func_timediff :public Item_str_timefunc { public: Item_func_timediff(Item *a, Item *b) - :Item_str_func(a, b) {} + :Item_str_timefunc(a, b) {} String *val_str(String *str); const char *func_name() const { return "timediff"; } - enum_field_types field_type() const { return MYSQL_TYPE_TIME; } void fix_length_and_dec() { - decimals=0; - max_length=MAX_TIME_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; + Item_str_timefunc::fix_length_and_dec(); maybe_null= 1; } - Field *tmp_table_field(TABLE *t_arg) - { - return (new Field_time(maybe_null, name, t_arg, &my_charset_bin)); - } }; -class Item_func_maketime :public Item_str_func +class Item_func_maketime :public Item_str_timefunc { public: Item_func_maketime(Item *a, Item *b, Item *c) - :Item_str_func(a, b ,c) {} + :Item_str_timefunc(a, b ,c) {} String *val_str(String *str); const char *func_name() const { return "maketime"; } - enum_field_types field_type() const { return MYSQL_TYPE_TIME; } - void fix_length_and_dec() - { - decimals=0; - max_length=MAX_TIME_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; - } - Field *tmp_table_field(TABLE *t_arg) - { - return (new Field_time(maybe_null, name, t_arg, &my_charset_bin)); - } }; class Item_func_microsecond :public Item_int_func diff --git a/sql/my_decimal.cc b/sql/my_decimal.cc index 1bd16940b47..f33609e0168 100644 --- a/sql/my_decimal.cc +++ b/sql/my_decimal.cc @@ -15,6 +15,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "mysql_priv.h" +#include + #ifndef MYSQL_CLIENT /* @@ -190,6 +192,23 @@ int str2my_decimal(uint mask, const char *from, uint length, } +my_decimal *date2my_decimal(TIME *ltime, my_decimal *dec) +{ + longlong date; + date = (ltime->year*100L + ltime->month)*100L + ltime->day; + if (ltime->time_type > MYSQL_TIMESTAMP_DATE) + date= ((date*100L + ltime->hour)*100L+ ltime->minute)*100L + ltime->second; + if (int2my_decimal(E_DEC_FATAL_ERROR, date, FALSE, dec)) + return dec; + if (ltime->second_part) + { + dec->buf[(dec->intg-1) / 9 + 1]= ltime->second_part * 1000; + dec->frac= 6; + } + return dec; +} + + #ifndef DBUG_OFF /* routines for debugging print */ diff --git a/sql/my_decimal.h b/sql/my_decimal.h index b02abacf0a3..af3edade8d6 100644 --- a/sql/my_decimal.h +++ b/sql/my_decimal.h @@ -295,7 +295,12 @@ int string2my_decimal(uint mask, const String *str, my_decimal *d) { return str2my_decimal(mask, str->ptr(), str->length(), str->charset(), d); } -#endif + + +my_decimal *date2my_decimal(TIME *ltime, my_decimal *dec); + + +#endif /*defined(MYSQL_SERVER) || defined(EMBEDDED_LIBRARY) */ inline int double2my_decimal(uint mask, double val, my_decimal *d) From 18577a8e8f8d9a14cb0d64061d94c227d8520eef Mon Sep 17 00:00:00 2001 From: "holyfoot/hf@mysql.com/deer.(none)" <> Date: Mon, 30 Oct 2006 09:52:50 +0400 Subject: [PATCH 10/65] Bug #8663 (cant use bigint as input to CAST) decimal->ulong conversion fixed to assign max possible ULONG if decimal is bigger Item_func_unsigned now handles DECIMAL parameter separately as we can't rely on decimal::val_int result here. --- mysql-test/r/type_newdecimal.result | 5 +++++ mysql-test/t/type_newdecimal.test | 6 ++++++ sql/item_func.cc | 9 ++++++++- strings/decimal.c | 2 +- 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/type_newdecimal.result b/mysql-test/r/type_newdecimal.result index 33f1ece0390..cacb945df85 100644 --- a/mysql-test/r/type_newdecimal.result +++ b/mysql-test/r/type_newdecimal.result @@ -1412,3 +1412,8 @@ i2 count(distinct j) 1.0 2 2.0 2 drop table t1; +select cast(19999999999999999999 as unsigned); +cast(19999999999999999999 as unsigned) +18446744073709551615 +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' diff --git a/mysql-test/t/type_newdecimal.test b/mysql-test/t/type_newdecimal.test index de1ebd74d17..cb85f14ee47 100644 --- a/mysql-test/t/type_newdecimal.test +++ b/mysql-test/t/type_newdecimal.test @@ -1108,3 +1108,9 @@ insert into t1 values (1,1), (1,2), (2,3), (2,4); select i, count(distinct j) from t1 group by i; select i+0.0 as i2, count(distinct j) from t1 group by i2; drop table t1; + +# +# Bug #8663 (cant use bigint as input to CAST) +# +select cast(19999999999999999999 as unsigned); + diff --git a/sql/item_func.cc b/sql/item_func.cc index a294bbd7a71..38294d52a5b 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -964,7 +964,14 @@ longlong Item_func_unsigned::val_int() longlong value; int error; - if (args[0]->cast_to_int_type() != STRING_RESULT) + if (args[0]->cast_to_int_type() == DECIMAL_RESULT) + { + my_decimal tmp, *dec= args[0]->val_decimal(&tmp); + if (!(null_value= args[0]->null_value)) + my_decimal2int(E_DEC_FATAL_ERROR, dec, 1, &value); + return value; + } + else if (args[0]->cast_to_int_type() != STRING_RESULT) { value= args[0]->val_int(); null_value= args[0]->null_value; diff --git a/strings/decimal.c b/strings/decimal.c index 5a0bc0968b6..e0b06685521 100644 --- a/strings/decimal.c +++ b/strings/decimal.c @@ -1036,7 +1036,7 @@ int decimal2ulonglong(decimal_t *from, ulonglong *to) x=x*DIG_BASE + *buf++; if (unlikely(y > ((ulonglong) ULONGLONG_MAX/DIG_BASE) || x < y)) { - *to=y; + *to=ULONGLONG_MAX; return E_DEC_OVERFLOW; } } From 6cd1f7b2e507b4b108086d09d9d64a17db4d7f3e Mon Sep 17 00:00:00 2001 From: "gkodinov/kgeorge@macbook.gmz" <> Date: Tue, 31 Oct 2006 11:01:27 +0200 Subject: [PATCH 11/65] Bug #23184: SELECT causes server crash Item::val_xxx() may be called by the server several times at execute time for a single query. Calls to val_xxx() may be very expensive and sometimes (count(distinct), sum(distinct), avg(distinct)) not possible. To avoid that problem the results of calculation for these aggregate functions are cached so that val_xxx() methods just return the calculated value for the second and subsequent calls. --- mysql-test/r/func_group.result | 26 +++++++++++++++++++ mysql-test/t/func_group.test | 25 ++++++++++++++++++ sql/item_sum.cc | 47 ++++++++++++++++++++++++---------- sql/item_sum.h | 34 ++++++++++++++++++------ 4 files changed, 111 insertions(+), 21 deletions(-) diff --git a/mysql-test/r/func_group.result b/mysql-test/r/func_group.result index c6117053a60..23517f7b603 100644 --- a/mysql-test/r/func_group.result +++ b/mysql-test/r/func_group.result @@ -1029,3 +1029,29 @@ t1 CREATE TABLE `t1` ( `stddev(0)` double(8,4) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; +CREATE TABLE t1 (a INT, b INT); +INSERT INTO t1 VALUES (1,1),(1,2),(1,3),(1,4),(1,5),(1,6),(1,7),(1,8); +INSERT INTO t1 SELECT a, b+8 FROM t1; +INSERT INTO t1 SELECT a, b+16 FROM t1; +INSERT INTO t1 SELECT a, b+32 FROM t1; +INSERT INTO t1 SELECT a, b+64 FROM t1; +INSERT INTO t1 SELECT a, b+128 FROM t1; +INSERT INTO t1 SELECT a, b+256 FROM t1; +INSERT INTO t1 SELECT a, b+512 FROM t1; +INSERT INTO t1 SELECT a, b+1024 FROM t1; +INSERT INTO t1 SELECT a, b+2048 FROM t1; +INSERT INTO t1 SELECT a, b+4096 FROM t1; +INSERT INTO t1 SELECT a, b+8192 FROM t1; +INSERT INTO t1 SELECT a, b+16384 FROM t1; +INSERT INTO t1 SELECT a, b+32768 FROM t1; +SELECT a,COUNT(DISTINCT b) AS cnt FROM t1 GROUP BY a HAVING cnt > 50; +a cnt +1 65536 +SELECT a,SUM(DISTINCT b) AS sumation FROM t1 GROUP BY a HAVING sumation > 50; +a sumation +1 2147516416 +SELECT a,AVG(DISTINCT b) AS average FROM t1 GROUP BY a HAVING average > 50; +a average +1 32768.5000 +DROP TABLE t1; +End of 5.0 tests diff --git a/mysql-test/t/func_group.test b/mysql-test/t/func_group.test index 079d107fad8..089f5ed9911 100644 --- a/mysql-test/t/func_group.test +++ b/mysql-test/t/func_group.test @@ -700,3 +700,28 @@ create table t1 select stddev(0); show create table t1; drop table t1; +# +# Bug #23184: SELECT causes server crash +# +CREATE TABLE t1 (a INT, b INT); +INSERT INTO t1 VALUES (1,1),(1,2),(1,3),(1,4),(1,5),(1,6),(1,7),(1,8); +INSERT INTO t1 SELECT a, b+8 FROM t1; +INSERT INTO t1 SELECT a, b+16 FROM t1; +INSERT INTO t1 SELECT a, b+32 FROM t1; +INSERT INTO t1 SELECT a, b+64 FROM t1; +INSERT INTO t1 SELECT a, b+128 FROM t1; +INSERT INTO t1 SELECT a, b+256 FROM t1; +INSERT INTO t1 SELECT a, b+512 FROM t1; +INSERT INTO t1 SELECT a, b+1024 FROM t1; +INSERT INTO t1 SELECT a, b+2048 FROM t1; +INSERT INTO t1 SELECT a, b+4096 FROM t1; +INSERT INTO t1 SELECT a, b+8192 FROM t1; +INSERT INTO t1 SELECT a, b+16384 FROM t1; +INSERT INTO t1 SELECT a, b+32768 FROM t1; +SELECT a,COUNT(DISTINCT b) AS cnt FROM t1 GROUP BY a HAVING cnt > 50; +SELECT a,SUM(DISTINCT b) AS sumation FROM t1 GROUP BY a HAVING sumation > 50; +SELECT a,AVG(DISTINCT b) AS average FROM t1 GROUP BY a HAVING average > 50; + +DROP TABLE t1; + +--echo End of 5.0 tests diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 5ca1dbba94b..fccbdfa7925 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -893,6 +893,7 @@ bool Item_sum_distinct::setup(THD *thd) tree= new Unique(simple_raw_key_cmp, &tree_key_length, tree_key_length, thd->variables.max_heap_table_size); + is_evaluated= FALSE; DBUG_RETURN(tree == 0); } @@ -900,6 +901,7 @@ bool Item_sum_distinct::setup(THD *thd) bool Item_sum_distinct::add() { args[0]->save_in_field(table->field[0], FALSE); + is_evaluated= FALSE; if (!table->field[0]->is_null()) { DBUG_ASSERT(tree); @@ -929,6 +931,7 @@ void Item_sum_distinct::clear() DBUG_ASSERT(tree != 0); /* we always have a tree */ null_value= 1; tree->reset(); + is_evaluated= FALSE; DBUG_VOID_RETURN; } @@ -938,6 +941,7 @@ void Item_sum_distinct::cleanup() delete tree; tree= 0; table= 0; + is_evaluated= FALSE; } Item_sum_distinct::~Item_sum_distinct() @@ -949,16 +953,20 @@ Item_sum_distinct::~Item_sum_distinct() void Item_sum_distinct::calculate_val_and_count() { - count= 0; - val.traits->set_zero(&val); - /* - We don't have a tree only if 'setup()' hasn't been called; - this is the case of sql_select.cc:return_zero_rows. - */ - if (tree) + if (!is_evaluated) { - table->field[0]->set_notnull(); - tree->walk(item_sum_distinct_walk, (void*) this); + count= 0; + val.traits->set_zero(&val); + /* + We don't have a tree only if 'setup()' hasn't been called; + this is the case of sql_select.cc:return_zero_rows. + */ + if (tree) + { + table->field[0]->set_notnull(); + tree->walk(item_sum_distinct_walk, (void*) this); + } + is_evaluated= TRUE; } } @@ -1014,9 +1022,13 @@ Item_sum_avg_distinct::fix_length_and_dec() void Item_sum_avg_distinct::calculate_val_and_count() { - Item_sum_distinct::calculate_val_and_count(); - if (count) - val.traits->div(&val, count); + if (!is_evaluated) + { + Item_sum_distinct::calculate_val_and_count(); + if (count) + val.traits->div(&val, count); + is_evaluated= TRUE; + } } @@ -2477,6 +2489,7 @@ void Item_sum_count_distinct::cleanup() */ delete tree; tree= 0; + is_evaluated= FALSE; if (table) { free_tmp_table(table->in_use, table); @@ -2498,6 +2511,7 @@ void Item_sum_count_distinct::make_unique() original= 0; force_copy_fields= 1; tree= 0; + is_evaluated= FALSE; tmp_table_param= 0; always_null= FALSE; } @@ -2617,6 +2631,7 @@ bool Item_sum_count_distinct::setup(THD *thd) but this has to be handled - otherwise someone can crash the server with a DoS attack */ + is_evaluated= FALSE; if (! tree) return TRUE; } @@ -2633,8 +2648,11 @@ Item *Item_sum_count_distinct::copy_or_same(THD* thd) void Item_sum_count_distinct::clear() { /* tree and table can be both null only if always_null */ + is_evaluated= FALSE; if (tree) + { tree->reset(); + } else if (table) { table->file->extra(HA_EXTRA_NO_CACHE); @@ -2655,6 +2673,7 @@ bool Item_sum_count_distinct::add() if ((*field)->is_real_null(0)) return 0; // Don't count NULL + is_evaluated= FALSE; if (tree) { /* @@ -2680,12 +2699,14 @@ longlong Item_sum_count_distinct::val_int() return LL(0); if (tree) { - ulonglong count; + if (is_evaluated) + return count; if (tree->elements == 0) return (longlong) tree->elements_in_tree(); // everything fits in memory count= 0; tree->walk(count_distinct_walk, (void*) &count); + is_evaluated= TRUE; return (longlong) count; } table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); diff --git a/sql/item_sum.h b/sql/item_sum.h index f1ea95214de..a2b2f7cab92 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -321,12 +321,23 @@ public: class Item_sum_num :public Item_sum { +protected: + /* + val_xxx() functions may be called several times during the execution of a + query. Derived classes that require extensive calculation in val_xxx() + maintain cache of aggregate value. This variable governs the validity of + that cache. + */ + bool is_evaluated; public: - Item_sum_num() :Item_sum() {} - Item_sum_num(Item *item_par) :Item_sum(item_par) {} - Item_sum_num(Item *a, Item* b) :Item_sum(a,b) {} - Item_sum_num(List &list) :Item_sum(list) {} - Item_sum_num(THD *thd, Item_sum_num *item) :Item_sum(thd, item) {} + Item_sum_num() :Item_sum(),is_evaluated(FALSE) {} + Item_sum_num(Item *item_par) + :Item_sum(item_par), is_evaluated(FALSE) {} + Item_sum_num(Item *a, Item* b) :Item_sum(a,b),is_evaluated(FALSE) {} + Item_sum_num(List &list) + :Item_sum(list), is_evaluated(FALSE) {} + Item_sum_num(THD *thd, Item_sum_num *item) + :Item_sum(thd, item),is_evaluated(item->is_evaluated) {} bool fix_fields(THD *, Item **); longlong val_int() { @@ -508,6 +519,12 @@ class Item_sum_count_distinct :public Item_sum_int to help get things set up, but we insert nothing in it */ Unique *tree; + /* + Storage for the value of count between calls to val_int() so val_int() + will not recalculate on each call. Validitiy of the value is stored in + is_evaluated. + */ + longlong count; /* Following is 0 normal object and pointer to original one for copy (to correctly free resources) @@ -525,14 +542,15 @@ class Item_sum_count_distinct :public Item_sum_int public: Item_sum_count_distinct(List &list) :Item_sum_int(list), table(0), field_lengths(0), tmp_table_param(0), - force_copy_fields(0), tree(0), original(0), always_null(FALSE) + force_copy_fields(0), tree(0), count(0), + original(0), always_null(FALSE) { quick_group= 0; } Item_sum_count_distinct(THD *thd, Item_sum_count_distinct *item) :Item_sum_int(thd, item), table(item->table), field_lengths(item->field_lengths), tmp_table_param(item->tmp_table_param), - force_copy_fields(0), tree(item->tree), original(item), - tree_key_length(item->tree_key_length), + force_copy_fields(0), tree(item->tree), count(item->count), + original(item), tree_key_length(item->tree_key_length), always_null(item->always_null) {} ~Item_sum_count_distinct(); From 54a713aac54f97a19b3c90afdfca6122c506c1cf Mon Sep 17 00:00:00 2001 From: "sergefp@mysql.com" <> Date: Tue, 31 Oct 2006 20:51:09 +0300 Subject: [PATCH 12/65] BUG#8804: wrong results for NULL IN (SELECT ...) Evaluate "NULL IN (SELECT ...)" in a special way: Disable pushed-down conditions and their "consequences": = Do full table scans instead of unique_[index_subquery] lookups. = Change appropriate "ref_or_null" accesses to full table scans in subquery's joins. Also cache value of NULL IN (SELECT ...) if the SELECT is not correlated wrt any upper select. --- mysql-test/r/subselect.result | 10 +- mysql-test/r/subselect3.result | 153 ++++++++++++ mysql-test/t/subselect3.test | 137 +++++++++++ sql/item.h | 10 + sql/item_cmpfunc.cc | 34 ++- sql/item_cmpfunc.h | 48 +++- sql/item_subselect.cc | 414 ++++++++++++++++++++++++++++----- sql/item_subselect.h | 70 +++++- sql/records.cc | 5 +- sql/sql_lex.cc | 4 +- sql/sql_lex.h | 4 +- sql/sql_select.cc | 112 +++++++-- sql/sql_select.h | 11 +- 13 files changed, 904 insertions(+), 108 deletions(-) create mode 100644 mysql-test/r/subselect3.result create mode 100644 mysql-test/t/subselect3.test diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 55d48030a07..82c70e19f9c 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -744,7 +744,7 @@ id select_type table type possible_keys key key_len ref rows Extra 3 DEPENDENT UNION NULL NULL NULL NULL NULL NULL NULL No tables used NULL UNION RESULT ALL NULL NULL NULL NULL NULL Warnings: -Note 1003 select `test`.`t2`.`id` AS `id` from `test`.`t2` where (`test`.`t2`.`id`,(select 1 AS `1` having ((`test`.`t2`.`id`) = (1)) union select 3 AS `3` having ((`test`.`t2`.`id`) = (3)))) +Note 1003 select `test`.`t2`.`id` AS `id` from `test`.`t2` where (`test`.`t2`.`id`,(select 1 AS `1` having trigcond(((`test`.`t2`.`id`) = (1))) union select 3 AS `3` having trigcond(((`test`.`t2`.`id`) = (3))))) SELECT * FROM t2 WHERE id IN (SELECT 5 UNION SELECT 3); id SELECT * FROM t2 WHERE id IN (SELECT 5 UNION SELECT 2); @@ -907,7 +907,7 @@ id select_type table type possible_keys key key_len ref rows Extra 2 DEPENDENT SUBQUERY t2 ref_or_null a a 5 func 2 Using where; Using index 2 DEPENDENT SUBQUERY t3 ALL NULL NULL NULL NULL 3 Using where Warnings: -Note 1003 select `test`.`t1`.`a` AS `a`,(`test`.`t1`.`a`,(select 1 AS `Not_used` from `test`.`t2` join `test`.`t3` where ((`test`.`t3`.`a` = `test`.`t2`.`a`) and (((`test`.`t1`.`a`) = `test`.`t2`.`a`) or isnull(`test`.`t2`.`a`))) having (`test`.`t2`.`a`))) AS `t1.a in (select t2.a from t2,t3 where t3.a=t2.a)` from `test`.`t1` +Note 1003 select `test`.`t1`.`a` AS `a`,(`test`.`t1`.`a`,(select 1 AS `Not_used` from `test`.`t2` join `test`.`t3` where ((`test`.`t3`.`a` = `test`.`t2`.`a`) and trigcond((((`test`.`t1`.`a`) = `test`.`t2`.`a`) or isnull(`test`.`t2`.`a`)))) having trigcond((`test`.`t2`.`a`)))) AS `t1.a in (select t2.a from t2,t3 where t3.a=t2.a)` from `test`.`t1` drop table t1,t2,t3; create table t1 (a float); select 10.5 IN (SELECT * from t1 LIMIT 1); @@ -2817,19 +2817,19 @@ id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 8 2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 9 Using where Warnings: -Note 1003 select `test`.`t1`.`one` AS `one`,`test`.`t1`.`two` AS `two`,((`test`.`t1`.`one`,`test`.`t1`.`two`),(select `test`.`t2`.`one` AS `one`,`test`.`t2`.`two` AS `two` from `test`.`t2` where ((`test`.`t2`.`flag` = _latin1'0') and (((`test`.`t1`.`one`) = `test`.`t2`.`one`) or isnull(`test`.`t2`.`one`)) and (((`test`.`t1`.`two`) = `test`.`t2`.`two`) or isnull(`test`.`t2`.`two`))) having ((`test`.`t2`.`one`) and (`test`.`t2`.`two`)))) AS `test` from `test`.`t1` +Note 1003 select `test`.`t1`.`one` AS `one`,`test`.`t1`.`two` AS `two`,((`test`.`t1`.`one`,`test`.`t1`.`two`),(select `test`.`t2`.`one` AS `one`,`test`.`t2`.`two` AS `two` from `test`.`t2` where ((`test`.`t2`.`flag` = _latin1'0') and trigcond(((((`test`.`t1`.`one`) = `test`.`t2`.`one`) or isnull(`test`.`t2`.`one`)) and (((`test`.`t1`.`two`) = `test`.`t2`.`two`) or isnull(`test`.`t2`.`two`))))) having trigcond(((`test`.`t2`.`one`) and (`test`.`t2`.`two`))))) AS `test` from `test`.`t1` explain extended SELECT one,two from t1 where ROW(one,two) IN (SELECT one,two FROM t2 WHERE flag = 'N'); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 8 Using where 2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 9 Using where Warnings: -Note 1003 select `test`.`t1`.`one` AS `one`,`test`.`t1`.`two` AS `two` from `test`.`t1` where ((`test`.`t1`.`one`,`test`.`t1`.`two`),(select `test`.`t2`.`one` AS `one`,`test`.`t2`.`two` AS `two` from `test`.`t2` where ((`test`.`t2`.`flag` = _latin1'N') and ((`test`.`t1`.`one`) = `test`.`t2`.`one`) and ((`test`.`t1`.`two`) = `test`.`t2`.`two`)))) +Note 1003 select `test`.`t1`.`one` AS `one`,`test`.`t1`.`two` AS `two` from `test`.`t1` where ((`test`.`t1`.`one`,`test`.`t1`.`two`),(select `test`.`t2`.`one` AS `one`,`test`.`t2`.`two` AS `two` from `test`.`t2` where ((`test`.`t2`.`flag` = _latin1'N') and trigcond((((`test`.`t1`.`one`) = `test`.`t2`.`one`) and ((`test`.`t1`.`two`) = `test`.`t2`.`two`)))))) explain extended SELECT one,two,ROW(one,two) IN (SELECT one,two FROM t2 WHERE flag = '0' group by one,two) as 'test' from t1; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 8 2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 9 Using where; Using temporary; Using filesort Warnings: -Note 1003 select `test`.`t1`.`one` AS `one`,`test`.`t1`.`two` AS `two`,((`test`.`t1`.`one`,`test`.`t1`.`two`),(select `test`.`t2`.`one` AS `one`,`test`.`t2`.`two` AS `two` from `test`.`t2` where (`test`.`t2`.`flag` = _latin1'0') group by `test`.`t2`.`one`,`test`.`t2`.`two` having ((((`test`.`t1`.`one`) = `test`.`t2`.`one`) or isnull(`test`.`t2`.`one`)) and (((`test`.`t1`.`two`) = `test`.`t2`.`two`) or isnull(`test`.`t2`.`two`)) and (`test`.`t2`.`one`) and (`test`.`t2`.`two`)))) AS `test` from `test`.`t1` +Note 1003 select `test`.`t1`.`one` AS `one`,`test`.`t1`.`two` AS `two`,((`test`.`t1`.`one`,`test`.`t1`.`two`),(select `test`.`t2`.`one` AS `one`,`test`.`t2`.`two` AS `two` from `test`.`t2` where (`test`.`t2`.`flag` = _latin1'0') group by `test`.`t2`.`one`,`test`.`t2`.`two` having trigcond(((((`test`.`t1`.`one`) = `test`.`t2`.`one`) or isnull(`test`.`t2`.`one`)) and (((`test`.`t1`.`two`) = `test`.`t2`.`two`) or isnull(`test`.`t2`.`two`)) and (`test`.`t2`.`one`) and (`test`.`t2`.`two`))))) AS `test` from `test`.`t1` DROP TABLE t1,t2; CREATE TABLE t1 (a char(5), b char(5)); INSERT INTO t1 VALUES (NULL,'aaa'), ('aaa','aaa'); diff --git a/mysql-test/r/subselect3.result b/mysql-test/r/subselect3.result new file mode 100644 index 00000000000..5ab8e448b39 --- /dev/null +++ b/mysql-test/r/subselect3.result @@ -0,0 +1,153 @@ +drop table if exists t0, t1, t2, t3, t4; +create table t1 (oref int, grp int, ie int) ; +insert into t1 (oref, grp, ie) values +(1, 1, 1), +(1, 1, 1), +(1, 2, NULL), +(2, 1, 3), +(3, 1, 4), +(3, 2, NULL); +create table t2 (oref int, a int); +insert into t2 values +(1, 1), +(2, 2), +(3, 3), +(4, NULL), +(2, NULL); +select a, oref, a in (select max(ie) +from t1 where oref=t2.oref group by grp) from t2; +a oref a in (select max(ie) +from t1 where oref=t2.oref group by grp) +1 1 1 +2 2 0 +3 3 NULL +NULL 4 0 +NULL 2 NULL +explain extended +select a, oref, a in (select max(ie) +from t1 where oref=t2.oref group by grp) from t2; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 5 +2 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 6 Using where; Using temporary; Using filesort +Warnings: +Note 1276 Field or reference 't2.oref' of SELECT #2 was resolved in SELECT #1 +Note 1003 select `test`.`t2`.`a` AS `a`,`test`.`t2`.`oref` AS `oref`,(`test`.`t2`.`a`,(select max(`test`.`t1`.`ie`) AS `max(ie)` from `test`.`t1` where (`test`.`t1`.`oref` = `test`.`t2`.`oref`) group by `test`.`t1`.`grp` having trigcond(((`test`.`t2`.`a`) = (max(`test`.`t1`.`ie`)))))) AS `a in (select max(ie) +from t1 where oref=t2.oref group by grp)` from `test`.`t2` +explain extended +select a, oref from t2 +where a in (select max(ie) from t1 where oref=t2.oref group by grp); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 5 Using where +2 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 6 Using where; Using temporary; Using filesort +Warnings: +Note 1276 Field or reference 't2.oref' of SELECT #2 was resolved in SELECT #1 +Note 1003 select `test`.`t2`.`a` AS `a`,`test`.`t2`.`oref` AS `oref` from `test`.`t2` where (`test`.`t2`.`a`,(select max(`test`.`t1`.`ie`) AS `max(ie)` from `test`.`t1` where (`test`.`t1`.`oref` = `test`.`t2`.`oref`) group by `test`.`t1`.`grp` having ((`test`.`t2`.`a`) = (max(`test`.`t1`.`ie`))))) +create table t3 (a int); +insert into t3 values (NULL), (NULL); +flush status; +select a in (select max(ie) from t1 where oref=4 group by grp) from t3; +a in (select max(ie) from t1 where oref=4 group by grp) +0 +0 +show status like 'Handler_read_rnd_next'; +Variable_name Value +Handler_read_rnd_next 11 +select ' ^ This must show 11' Z; +Z + ^ This must show 11 +explain extended select a in (select max(ie) from t1 where oref=4 group by grp) from t3; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t3 ALL NULL NULL NULL NULL 2 +2 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 6 Using where; Using temporary; Using filesort +Warnings: +Note 1003 select (`test`.`t3`.`a`,(select max(`test`.`t1`.`ie`) AS `max(ie)` from `test`.`t1` where (`test`.`t1`.`oref` = 4) group by `test`.`t1`.`grp` having trigcond(((`test`.`t3`.`a`) = (max(`test`.`t1`.`ie`)))))) AS `a in (select max(ie) from t1 where oref=4 group by grp)` from `test`.`t3` +drop table t1, t2, t3; +create table t1 (a int, oref int, key(a)); +insert into t1 values +(1, 1), +(1, NULL), +(2, 3), +(2, NULL), +(3, NULL); +create table t2 (a int, oref int); +insert into t2 values (1, 1), (2,2), (NULL, 3), (NULL, 4); +select oref, a, a in (select a from t1 where oref=t2.oref) Z from t2; +oref a Z +1 1 1 +2 2 0 +3 NULL NULL +4 NULL 0 +explain extended +select oref, a, a in (select a from t1 where oref=t2.oref) Z from t2; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 4 +2 DEPENDENT SUBQUERY t1 index_subquery a a 5 func 2 Using index; Using where +Warnings: +Note 1276 Field or reference 't2.oref' of SELECT #2 was resolved in SELECT #1 +Note 1003 select `test`.`t2`.`oref` AS `oref`,`test`.`t2`.`a` AS `a`,(`test`.`t2`.`a`,(((`test`.`t2`.`a`) in t1 on a checking NULL where (`test`.`t1`.`oref` = `test`.`t2`.`oref`)))) AS `Z` from `test`.`t2` +flush status; +select oref, a from t2 where a in (select a from t1 where oref=t2.oref); +oref a +1 1 +show status like '%Handler_read_rnd_next'; +Variable_name Value +Handler_read_rnd_next 5 +delete from t2; +insert into t2 values (NULL, 0),(NULL, 0), (NULL, 0), (NULL, 0); +flush status; +select oref, a, a in (select a from t1 where oref=t2.oref) Z from t2; +oref a Z +0 NULL 0 +0 NULL 0 +0 NULL 0 +0 NULL 0 +show status like '%Handler_read%'; +Variable_name Value +Handler_read_first 0 +Handler_read_key 0 +Handler_read_next 0 +Handler_read_prev 0 +Handler_read_rnd 0 +Handler_read_rnd_next 29 +select 'No key lookups, seq reads: 29= 5 reads from t2 + 4 * 6 reads from t1.' Z; +Z +No key lookups, seq reads: 29= 5 reads from t2 + 4 * 6 reads from t1. +drop table t1, t2; +create table t1 (a int, b int, primary key (a)); +insert into t1 values (1,1), (3,1),(100,1); +create table t2 (a int, b int); +insert into t2 values (1,1),(2,1),(NULL,1),(NULL,0); +select a,b, a in (select a from t1 where t1.b = t2.b) Z from t2 ; +a b Z +1 1 1 +2 1 0 +NULL 1 NULL +NULL 0 0 +drop table t1, t2; +create table t1 (a int, b int, key(a)); +insert into t1 values +(0,0),(1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9); +create table t2 like t1; +insert into t2 select * from t1; +update t2 set b=1; +create table t3 (a int, oref int); +insert into t3 values (1, 1), (NULL,1), (NULL,0); +select a, oref, +t3.a in (select t1.a from t1, t2 where t1.b=t2.a and t2.b=t3.oref) Z +from t3; +a oref Z +1 1 1 +NULL 1 NULL +NULL 0 0 +explain extended +select a, oref, +t3.a in (select t1.a from t1, t2 where t1.b=t2.a and t2.b=t3.oref) Z +from t3; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t3 ALL NULL NULL NULL NULL 3 +2 DEPENDENT SUBQUERY t1 ref_or_null a a 5 func 4 Using where +2 DEPENDENT SUBQUERY t2 ref a a 5 test.t1.b 1 Using where +Warnings: +Note 1276 Field or reference 't3.oref' of SELECT #2 was resolved in SELECT #1 +Note 1003 select `test`.`t3`.`a` AS `a`,`test`.`t3`.`oref` AS `oref`,(`test`.`t3`.`a`,(select 1 AS `Not_used` from `test`.`t1` join `test`.`t2` where ((`test`.`t2`.`a` = `test`.`t1`.`b`) and (`test`.`t2`.`b` = `test`.`t3`.`oref`) and trigcond((((`test`.`t3`.`a`) = `test`.`t1`.`a`) or isnull(`test`.`t1`.`a`)))) having trigcond((`test`.`t1`.`a`)))) AS `Z` from `test`.`t3` +drop table t1, t2, t3; diff --git a/mysql-test/t/subselect3.test b/mysql-test/t/subselect3.test new file mode 100644 index 00000000000..f7fbafdd17f --- /dev/null +++ b/mysql-test/t/subselect3.test @@ -0,0 +1,137 @@ +--disable_warnings +drop table if exists t0, t1, t2, t3, t4; +--enable_warnings + +# +# 1. Subquery with GROUP/HAVING +# +create table t1 (oref int, grp int, ie int) ; +insert into t1 (oref, grp, ie) values + (1, 1, 1), + (1, 1, 1), + (1, 2, NULL), + + (2, 1, 3), + + (3, 1, 4), + (3, 2, NULL); + +# Ok, for +# select max(ie) from t1 where oref=PARAM group by grp +# we'll have: +# 1 -> (1, NULL) matching + NULL +# 2 -> (3) non-matching +# 3 -> (3, NULL) non-matching + NULL +# 4 -> () nothing. + +create table t2 (oref int, a int); +insert into t2 values + (1, 1), + (2, 2), + (3, 3), + (4, NULL), + (2, NULL); + +# true, false, null, false, null +select a, oref, a in (select max(ie) + from t1 where oref=t2.oref group by grp) from t2; + +# This must have a trigcond +explain extended +select a, oref, a in (select max(ie) + from t1 where oref=t2.oref group by grp) from t2; + +# This must not have a trigcond: +explain extended +select a, oref from t2 +where a in (select max(ie) from t1 where oref=t2.oref group by grp); + + +# Non-correlated subquery, 2 NULL evaluations +create table t3 (a int); +insert into t3 values (NULL), (NULL); +flush status; +select a in (select max(ie) from t1 where oref=4 group by grp) from t3; +show status like 'Handler_read_rnd_next'; +select ' ^ This must show 11' Z; + +# This must show trigcond: +explain extended select a in (select max(ie) from t1 where oref=4 group by grp) from t3; + +drop table t1, t2, t3; + +# +# 2. Subquery handled with 'index_subquery': +# +create table t1 (a int, oref int, key(a)); +insert into t1 values + (1, 1), + (1, NULL), + (2, 3), + (2, NULL), + (3, NULL); + +create table t2 (a int, oref int); +insert into t2 values (1, 1), (2,2), (NULL, 3), (NULL, 4); + +select oref, a, a in (select a from t1 where oref=t2.oref) Z from t2; + +# The next explain shows "using index" but that is just incorrect display +# (there is a bug filed about this). +explain extended +select oref, a, a in (select a from t1 where oref=t2.oref) Z from t2; + +flush status; +select oref, a from t2 where a in (select a from t1 where oref=t2.oref); +# This will only show access to t2: +show status like '%Handler_read_rnd_next'; + +# Check that repeated NULL-scans are not cached (subq. is not correlated): +delete from t2; +insert into t2 values (NULL, 0),(NULL, 0), (NULL, 0), (NULL, 0); + +flush status; +select oref, a, a in (select a from t1 where oref=t2.oref) Z from t2; +show status like '%Handler_read%'; +select 'No key lookups, seq reads: 29= 5 reads from t2 + 4 * 6 reads from t1.' Z; + +drop table t1, t2; + +# +# 3. Subquery handled with 'unique_index_subquery': +# +create table t1 (a int, b int, primary key (a)); +insert into t1 values (1,1), (3,1),(100,1); + +create table t2 (a int, b int); +insert into t2 values (1,1),(2,1),(NULL,1),(NULL,0); + +select a,b, a in (select a from t1 where t1.b = t2.b) Z from t2 ; + +drop table t1, t2; + +# +# 4. Subquery that is a join, with ref access +# +create table t1 (a int, b int, key(a)); +insert into t1 values + (0,0),(1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9); + +create table t2 like t1; +insert into t2 select * from t1; +update t2 set b=1; + +create table t3 (a int, oref int); +insert into t3 values (1, 1), (NULL,1), (NULL,0); +select a, oref, + t3.a in (select t1.a from t1, t2 where t1.b=t2.a and t2.b=t3.oref) Z +from t3; + +# This must have trigcond in WHERE and HAVING: +explain extended +select a, oref, + t3.a in (select t1.a from t1, t2 where t1.b=t2.a and t2.b=t3.oref) Z +from t3; + +drop table t1, t2, t3; + diff --git a/sql/item.h b/sql/item.h index 0cfb0b01fd8..566daa1aaee 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1959,6 +1959,16 @@ public: class Item_in_subselect; + +/* + An object of this class: + - Converts val_XXX() calls to ref->val_XXX_result() calls, like Item_ref. + - Sets owner->was_null=TRUE if it has returned a NULL value from any + val_XXX() function. This allows to inject an Item_ref_null_helper + object into subquery and then check if the subquery has produced a row + with NULL value. +*/ + class Item_ref_null_helper: public Item_ref { protected: diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 9a400d60ae6..540f67ba0ee 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -786,9 +786,41 @@ longlong Item_in_optimizer::val_int() { DBUG_ASSERT(fixed == 1); cache->store(args[0]); + if (cache->null_value) { - null_value= 1; + if (((Item_in_subselect*)args[1])->is_top_level_item()) + { + /* + We're evaluating "NULL IN (SELECT ...)". The result can be NULL or + FALSE, and we can return one instead of another. Just return NULL. + */ + null_value= 1; + } + else + { + if (!((Item_in_subselect*)args[1])->is_correlated && + result_for_null_param != UNKNOWN) + { + /* Use cached value from previous execution */ + null_value= result_for_null_param; + } + else + { + /* + We're evaluating "NULL IN (SELECT ...)". The result is: + FALSE if SELECT produces an empty set, or + NULL otherwise. + We disable the predicates we've pushed down into subselect, run the + subselect and see if it has produced any rows. + */ + ((Item_in_subselect*)args[1])->enable_pushed_conds= FALSE; + longlong tmp= args[1]->val_bool_result(); + result_for_null_param= null_value= + !((Item_in_subselect*)args[1])->engine->no_rows(); + ((Item_in_subselect*)args[1])->enable_pushed_conds= TRUE; + } + } return 0; } bool tmp= args[1]->val_bool_result(); diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index c8439cba303..acad1e51bc9 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -100,25 +100,44 @@ public: }; class Item_cache; +#define UNKNOWN ((my_bool)-1) + + +/* + Item_in_optimizer(left_expr, Item_in_subselect(...)) + + Item_in_optimizer is used to wrap an instance of Item_in_subselect. This + class does the following: + - Evaluate the left expression and store it in Item_cache_* object (to + avoid re-evaluating it many times during subquery execution) + - Shortcut the evaluation of "NULL IN (...)" to NULL in the cases where we + don't care if the result is NULL or FALSE. + + NOTE + It is not quite clear why the above listed functionality should be + placed into a separate class called 'Item_in_optimizer'. +*/ + class Item_in_optimizer: public Item_bool_func { protected: Item_cache *cache; bool save_cache; + /* + Stores the value of "NULL IN (SELECT ...)" for uncorrelated subqueries: + UNKNOWN - "NULL in (SELECT ...)" has not yet been evaluated + FALSE - result is FALSE + TRUE - result is NULL + */ + my_bool result_for_null_param; public: Item_in_optimizer(Item *a, Item_in_subselect *b): - Item_bool_func(a, my_reinterpret_cast(Item *)(b)), cache(0), save_cache(0) + Item_bool_func(a, my_reinterpret_cast(Item *)(b)), cache(0), + save_cache(0), result_for_null_param(UNKNOWN) {} bool fix_fields(THD *, Item **); bool fix_left(THD *thd, Item **ref); bool is_null(); - /* - Item_in_optimizer item is special boolean function. On value request - (one of val, val_int or val_str methods) it evaluate left expression - of IN by storing it value in cache item (one of Item_cache* items), - then it test cache is it NULL. If left expression (cache) is NULL then - Item_in_optimizer return NULL, else it evaluate Item_in_subselect. - */ longlong val_int(); void cleanup(); const char *func_name() const { return ""; } @@ -256,9 +275,11 @@ public: class Item_maxmin_subselect; /* + trigcond(arg) ::= param? arg : TRUE + The class Item_func_trig_cond is used for guarded predicates which are employed only for internal purposes. - A guarded predicates is an object consisting of an a regular or + A guarded predicate is an object consisting of an a regular or a guarded predicate P and a pointer to a boolean guard variable g. A guarded predicate P/g is evaluated to true if the value of the guard g is false, otherwise it is evaluated to the same value that @@ -276,6 +297,10 @@ class Item_maxmin_subselect; Objects of this class are built only for query execution after the execution plan has been already selected. That's why this class needs only val_int out of generic methods. + + Current uses of Item_func_trig_cond objects: + - To wrap selection conditions when executing outer joins + - To wrap condition that is pushed down into subquery */ class Item_func_trig_cond: public Item_bool_func @@ -1019,6 +1044,11 @@ public: /* Functions used by HAVING for rewriting IN subquery */ class Item_in_subselect; + +/* + This is like IS NOT NULL but it also remembers if it ever has + encountered a NULL. +*/ class Item_is_not_null_test :public Item_func_isnull { Item_in_subselect* owner; diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 7015f450aa7..489a647402e 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -37,7 +37,7 @@ inline Item * and_items(Item* cond, Item *item) Item_subselect::Item_subselect(): Item_result_field(), value_assigned(0), thd(0), substitution(0), engine(0), old_engine(0), used_tables_cache(0), have_to_be_excluded(0), - const_item_cache(1), engine_changed(0), changed(0) + const_item_cache(1), engine_changed(0), changed(0), is_correlated(FALSE) { with_subselect= 1; reset(); @@ -192,16 +192,16 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref) return res; } -bool Item_subselect::exec() +bool Item_subselect::exec(bool full_scan) { int res; - res= engine->exec(); + res= engine->exec(full_scan); if (engine_changed) { engine_changed= 0; - return exec(); + return exec(full_scan); } return (res); } @@ -441,13 +441,13 @@ bool Item_singlerow_subselect::null_inside() void Item_singlerow_subselect::bring_value() { - exec(); + exec(FALSE); } double Item_singlerow_subselect::val_real() { DBUG_ASSERT(fixed == 1); - if (!exec() && !value->null_value) + if (!exec(FALSE) && !value->null_value) { null_value= 0; return value->val_real(); @@ -462,7 +462,7 @@ double Item_singlerow_subselect::val_real() longlong Item_singlerow_subselect::val_int() { DBUG_ASSERT(fixed == 1); - if (!exec() && !value->null_value) + if (!exec(FALSE) && !value->null_value) { null_value= 0; return value->val_int(); @@ -476,7 +476,7 @@ longlong Item_singlerow_subselect::val_int() String *Item_singlerow_subselect::val_str(String *str) { - if (!exec() && !value->null_value) + if (!exec(FALSE) && !value->null_value) { null_value= 0; return value->val_str(str); @@ -491,7 +491,7 @@ String *Item_singlerow_subselect::val_str(String *str) my_decimal *Item_singlerow_subselect::val_decimal(my_decimal *decimal_value) { - if (!exec() && !value->null_value) + if (!exec(FALSE) && !value->null_value) { null_value= 0; return value->val_decimal(decimal_value); @@ -506,7 +506,7 @@ my_decimal *Item_singlerow_subselect::val_decimal(my_decimal *decimal_value) bool Item_singlerow_subselect::val_bool() { - if (!exec() && !value->null_value) + if (!exec(FALSE) && !value->null_value) { null_value= 0; return value->val_bool(); @@ -557,7 +557,8 @@ bool Item_in_subselect::test_limit(SELECT_LEX_UNIT *unit) Item_in_subselect::Item_in_subselect(Item * left_exp, st_select_lex *select_lex): - Item_exists_subselect(), optimizer(0), transformed(0), upper_item(0) + Item_exists_subselect(), optimizer(0), transformed(0), + enable_pushed_conds(TRUE), upper_item(0) { DBUG_ENTER("Item_in_subselect::Item_in_subselect"); left_expr= left_exp; @@ -602,7 +603,7 @@ void Item_exists_subselect::fix_length_and_dec() double Item_exists_subselect::val_real() { DBUG_ASSERT(fixed == 1); - if (exec()) + if (exec(FALSE)) { reset(); return 0; @@ -613,7 +614,7 @@ double Item_exists_subselect::val_real() longlong Item_exists_subselect::val_int() { DBUG_ASSERT(fixed == 1); - if (exec()) + if (exec(FALSE)) { reset(); return 0; @@ -624,7 +625,7 @@ longlong Item_exists_subselect::val_int() String *Item_exists_subselect::val_str(String *str) { DBUG_ASSERT(fixed == 1); - if (exec()) + if (exec(FALSE)) { reset(); return 0; @@ -637,7 +638,7 @@ String *Item_exists_subselect::val_str(String *str) my_decimal *Item_exists_subselect::val_decimal(my_decimal *decimal_value) { DBUG_ASSERT(fixed == 1); - if (exec()) + if (exec(FALSE)) { reset(); return 0; @@ -650,7 +651,7 @@ my_decimal *Item_exists_subselect::val_decimal(my_decimal *decimal_value) bool Item_exists_subselect::val_bool() { DBUG_ASSERT(fixed == 1); - if (exec()) + if (exec(FALSE)) { reset(); return 0; @@ -668,7 +669,7 @@ double Item_in_subselect::val_real() DBUG_ASSERT(0); DBUG_ASSERT(fixed == 1); null_value= 0; - if (exec()) + if (exec(!enable_pushed_conds)) { reset(); null_value= 1; @@ -689,7 +690,7 @@ longlong Item_in_subselect::val_int() DBUG_ASSERT(0); DBUG_ASSERT(fixed == 1); null_value= 0; - if (exec()) + if (exec(!enable_pushed_conds)) { reset(); null_value= 1; @@ -710,7 +711,7 @@ String *Item_in_subselect::val_str(String *str) DBUG_ASSERT(0); DBUG_ASSERT(fixed == 1); null_value= 0; - if (exec()) + if (exec(!enable_pushed_conds)) { reset(); null_value= 1; @@ -730,7 +731,7 @@ bool Item_in_subselect::val_bool() { DBUG_ASSERT(fixed == 1); null_value= 0; - if (exec()) + if (exec(!enable_pushed_conds)) { reset(); null_value= 1; @@ -750,7 +751,7 @@ my_decimal *Item_in_subselect::val_decimal(my_decimal *decimal_value) DBUG_ASSERT(0); null_value= 0; DBUG_ASSERT(fixed == 1); - if (exec()) + if (exec(!enable_pushed_conds)) { reset(); null_value= 1; @@ -763,7 +764,51 @@ my_decimal *Item_in_subselect::val_decimal(my_decimal *decimal_value) } -/* Rewrite a single-column IN/ALL/ANY subselect. */ +/* + Rewrite a single-column IN/ALL/ANY subselect + + SYNOPSIS + Item_in_subselect::single_value_transformer() + join + func + + DESCRIPTION + Rewrite a single-column subquery using rule-based approach. The subquery + + oe $cmp$ (SELECT sel FROM ... WHERE subq_where HAVING subq_having) + + First, try to convert the subquery to scalar-result subquery in one of + the forms: + + - oe $cmp$ (SELECT MAX(...) ) // handled by Item_singlerow_subselect + - oe $cmp$ (SELECT ...) // handled by Item_maxminsubselect + + If that fails, the subquery will be handled with class Item_in_optimizer, + Inject the predicates into subquery, i.e. convert it to: + + - If the subquery has aggregates, GROUP BY, or HAVING, convert to + + SELECT sel FROM ... HAVING subq_having AND + trigcond(oe $cmp$ ref_or_null_helper) + + the addition is wrapped into trigger only when we want to distinguish + between NULL and FALSE results. + + - Else, if we don't care if subquery result is NULL or FALSE, convert to + + SELECT 1 ... WHERE (oe $CMP$ ie) AND subq_where + + - Else convert to: + + SELECT 1 WHERE ... + WHERE subq_where AND trigcond((oe $CMP$ ie) OR ie IS NULL) + HAVING subq_having AND trigcond((ie)) + + RETURN + RES_OK - Transformed successfully (or done nothing?) + RES_REDUCE - The subquery was reduced to non-subquery + RES_ERROR - Error +*/ Item_subselect::trans_res Item_in_subselect::single_value_transformer(JOIN *join, @@ -896,8 +941,12 @@ Item_in_subselect::single_value_transformer(JOIN *join, select_lex->uncacheable|= UNCACHEABLE_DEPENDENT; /* Add the left part of a subselect to a WHERE or HAVING clause of - the right part, e.g. SELECT 1 IN (SELECT a FROM t1) => - SELECT Item_in_optimizer(1, SELECT a FROM t1 WHERE a=1) + the right part, e.g. + + SELECT 1 IN (SELECT a FROM t1) => + + SELECT Item_in_optimizer(1, SELECT a FROM t1 WHERE a=1) + HAVING is used only if the right part contains a SUM function, a GROUP BY or a HAVING clause. */ @@ -912,10 +961,15 @@ Item_in_subselect::single_value_transformer(JOIN *join, ref_pointer_array, (char *)"", this->full_name())); -#ifdef CORRECT_BUT_TOO_SLOW_TO_BE_USABLE - if (!abort_on_null && left_expr->maybe_null) - item= new Item_cond_or(new Item_func_isnull(left_expr), item); -#endif + if (!abort_on_null && ((Item*)select_lex->item_list.head())->maybe_null) + { + /* + We can encounter "NULL IN (SELECT ...)". Wrap the added condition + within a trigger. + */ + item= new Item_func_trig_cond(item, &enable_pushed_conds); + } + /* AND and comparison functions can't be changed during fix_fields() we can assign select_lex->having here, and pass 0 as last @@ -944,10 +998,13 @@ Item_in_subselect::single_value_transformer(JOIN *join, select_lex->item_list.push_back(new Item_int("Not_used", (longlong) 1, 21)); select_lex->ref_pointer_array[0]= select_lex->item_list.head(); + item= func->create(expr, item); if (!abort_on_null && orig_item->maybe_null) { - having= new Item_is_not_null_test(this, having); + having= + new Item_func_trig_cond(new Item_is_not_null_test(this, having), + &enable_pushed_conds); /* Item_is_not_null_test can't be changed during fix_fields() we can assign select_lex->having here, and pass 0 as last @@ -967,12 +1024,15 @@ Item_in_subselect::single_value_transformer(JOIN *join, select_lex->having_fix_field= 0; if (tmp) DBUG_RETURN(RES_ERROR); + /* + NOTE: It is important that we add this "IS NULL" here, even when + orig_item can't be NULL. This is needed so that this predicate is + only used by ref[_or_null] analyzer (and, e.g. is not used by const + propagation). + */ item= new Item_cond_or(item, new Item_func_isnull(orig_item)); -#ifdef CORRECT_BUT_TOO_SLOW_TO_BE_USABLE - if (left_expr->maybe_null) - item= new Item_cond_or(new Item_func_isnull(left_expr), item); -#endif + item= new Item_func_trig_cond(item, &enable_pushed_conds); } item->name= (char *)in_additional_cond; /* @@ -999,13 +1059,14 @@ Item_in_subselect::single_value_transformer(JOIN *join, we can assign select_lex->having here, and pass 0 as last argument (reference) to fix_fields() */ - select_lex->having= - join->having= - func->create(expr, + Item *new_having= + func->create(expr, new Item_ref_null_helper(&select_lex->context, this, select_lex->ref_pointer_array, (char *)"", (char *)"")); + new_having= new Item_func_trig_cond(new_having, &enable_pushed_conds); + select_lex->having= join->having= new_having; select_lex->having_fix_field= 1; /* @@ -1210,6 +1271,8 @@ Item_in_subselect::row_value_transformer(JOIN *join) where_item= and_items(where_item, item); } + if (where_item) + where_item= new Item_func_trig_cond(where_item, &enable_pushed_conds); /* AND can't be changed during fix_fields() we can assign select_lex->where here, and pass 0 as last @@ -1223,6 +1286,8 @@ Item_in_subselect::row_value_transformer(JOIN *join) if (having_item) { bool res; + having_item= new Item_func_trig_cond(having_item, &enable_pushed_conds); + select_lex->having= join->having= and_items(join->having, having_item); select_lex->having->top_level_item(); /* @@ -1439,6 +1504,27 @@ bool subselect_union_engine::is_executed() const } +/* + Check if last execution of the subquery engine produced any rows + + SYNOPSIS + subselect_union_engine::no_rows() + + DESCRIPTION + Check if last execution of the subquery engine produced any rows. The + return value is undefined if last execution ended in an error. + + RETURN + TRUE - Last subselect execution has produced no rows + FALSE - Otherwise +*/ + +bool subselect_union_engine::no_rows() +{ + /* Check if we got any rows when reading UNION result from temp. table: */ + return test(!unit->fake_select_lex->join->send_records); +} + void subselect_uniquesubquery_engine::cleanup() { DBUG_ENTER("subselect_uniquesubquery_engine::cleanup"); @@ -1504,6 +1590,29 @@ int subselect_uniquesubquery_engine::prepare() return 1; } + +/* + Check if last execution of the subquery engine produced any rows + + SYNOPSIS + subselect_single_select_engine::no_rows() + + DESCRIPTION + Check if last execution of the subquery engine produced any rows. The + return value is undefined if last execution ended in an error. + + RETURN + TRUE - Last subselect execution has produced no rows + FALSE - Otherwise +*/ + +bool subselect_single_select_engine::no_rows() +{ +// return test(!join->send_records); + return !item->assigned(); +} + + static Item_result set_row(List &item_list, Item *item, Item_cache **row, bool *maybe_null) { @@ -1557,7 +1666,11 @@ void subselect_uniquesubquery_engine::fix_length_and_dec(Item_cache **row) DBUG_ASSERT(0); } -int subselect_single_select_engine::exec() +int init_read_record_seq(JOIN_TAB *tab); +int join_read_always_key_or_null(JOIN_TAB *tab); +int join_read_next_same_or_null(READ_RECORD *info); + +int subselect_single_select_engine::exec(bool full_scan) { DBUG_ENTER("subselect_single_select_engine::exec"); char const *save_where= thd->where; @@ -1595,7 +1708,43 @@ int subselect_single_select_engine::exec() if (!executed) { item->reset_value_registration(); + if (full_scan) + { + /* + We should not apply optimizations based on the condition that was + pushed down into the subquery. Those optimizations are ref[_or_null] + acceses. Change them to be full table scans. + */ + for (uint i=join->const_tables ; i < join->tables ; i++) + { + JOIN_TAB *tab=join->join_tab+i; + if (tab->keyuse && tab->keyuse->outer_ref) + { + tab->read_first_record= init_read_record_seq; + tab->read_record.record= tab->table->record[0]; + tab->read_record.thd= join->thd; + tab->read_record.ref_length= tab->table->file->ref_length; + } + } + } + join->exec(); + + if (full_scan) + { + /* Enable the optimizations back */ + for (uint i=join->const_tables ; i < join->tables ; i++) + { + JOIN_TAB *tab=join->join_tab+i; + if (tab->keyuse && tab->keyuse->outer_ref) + { + tab->read_record.record= 0; + tab->read_record.ref_length= 0; + tab->read_first_record= join_read_always_key_or_null; + tab->read_record.read_record= join_read_next_same_or_null; + } + } + } executed= 1; thd->where= save_where; thd->lex->current_select= save_select; @@ -1606,29 +1755,161 @@ int subselect_single_select_engine::exec() DBUG_RETURN(0); } -int subselect_union_engine::exec() +int subselect_union_engine::exec(bool full_scan) { char const *save_where= thd->where; + /* + Ignore the full_scan parameter: the pushed down predicates are only used + for filtering, and the caller has disabled them if necessary. + */ int res= unit->exec(); thd->where= save_where; return res; } -int subselect_uniquesubquery_engine::exec() +/* + Search for at least on row satisfying select condition + + SYNOPSIS + subselect_uniquesubquery_engine::scan_table() + + DESCRIPTION + Scan the table using sequential access until we find at least one row + satisfying select condition. + + The result of this function (info about whether a row was found) is + stored in this->empty_result_set. + + RETURN + FALSE - OK + TRUE - Error +*/ + +int subselect_uniquesubquery_engine::scan_table() +{ + int error; + TABLE *table= tab->table; + DBUG_ENTER("subselect_uniquesubquery_engine::scan_table"); + + empty_result_set= TRUE; + bool is_uncorrelated= !cond || !(cond->used_tables() & OUTER_REF_TABLE_BIT); + + if (table->file->inited) + table->file->ha_index_end(); + + table->file->ha_rnd_init(1); + table->file->extra_opt(HA_EXTRA_CACHE, + current_thd->variables.read_buff_size); + table->null_row= 0; + for (;;) + { + error=table->file->rnd_next(table->record[0]); + if (error && error != HA_ERR_END_OF_FILE) + { + error= report_error(table, error); + break; + } + /* No more rows */ + if (table->status) + break; + + if (!cond || cond->val_int()) + { + empty_result_set= FALSE; + break; + } + } + + table->file->ha_rnd_end(); + DBUG_RETURN(error != 0); +} + + +/* + Copy ref key and check for null parts in it + + SYNOPSIS + subselect_uniquesubquery_engine::copy_ref_key() + + DESCRIPTION + Copy ref key and check for null parts in it. + + RETURN + FALSE - ok, index lookup key without keys copied. + TRUE - an error occured while copying the key +*/ + +bool subselect_uniquesubquery_engine::copy_ref_key() +{ + DBUG_ENTER("subselect_uniquesubquery_engine::copy_ref_key"); + + for (store_key **copy= tab->ref.key_copy ; *copy ; copy++) + { + tab->ref.key_err= (*copy)->copy(); + + /* + When there is a NULL part in the key we don't need to make index + lookup for such key thus we don't need to copy whole key. + If we later should do a sequential scan return OK. Fail otherwise. + + See also the comment for the subselect_uniquesubquery_engine::exec() + function. + */ + null_keypart= (*copy)->null_key; + bool top_level= ((Item_in_subselect *) item)->is_top_level_item(); + if (null_keypart && !top_level) + break; + if ((tab->ref.key_err) & 1 || (null_keypart && top_level)) + { + tab->table->status= STATUS_NOT_FOUND; + DBUG_RETURN(1); + } + } + DBUG_RETURN(0); +} + + +/* + Execute subselect + + SYNOPSIS + subselect_uniquesubquery_engine::exec() + + DESCRIPTION + Find rows corresponding to the ref key using index access. + If some part of the lookup key is NULL, then we're evaluating + NULL IN (SELECT ... ) + This is a special case, we don't need to search for NULL in the table, + instead, the result value is + - NULL if select produces empty row set + - FALSE otherwise. + + In some cases (IN subselect is a top level item, i.e. abort_on_null==TRUE) + the caller doesn't distinguish between NULL and FALSE result and we just + return FALSE. + Otherwise we make a full table scan to see if there is at least one matching row. + + NOTE + + RETURN + FALSE - ok + TRUE - an error occured while scanning +*/ + +int subselect_uniquesubquery_engine::exec(bool full_scan) { DBUG_ENTER("subselect_uniquesubquery_engine::exec"); int error; TABLE *table= tab->table; - for (store_key **copy=tab->ref.key_copy ; *copy ; copy++) - { - if ((tab->ref.key_err= (*copy)->copy()) & 1) - { - table->status= STATUS_NOT_FOUND; - DBUG_RETURN(1); - } - } + + /* TODO: change to use of 'full_scan' here? */ + if (copy_ref_key()) + DBUG_RETURN(1); + if (null_keypart) + DBUG_RETURN(scan_table()); + if (!table->file->inited) table->file->ha_index_init(tab->ref.key); error= table->file->index_read(table->record[0], @@ -1657,14 +1938,35 @@ subselect_uniquesubquery_engine::~subselect_uniquesubquery_engine() } -int subselect_indexsubquery_engine::exec() +/* + Index-lookup subselect 'engine' - run the subquery + + SYNOPSIS + subselect_uniquesubquery_engine:exec() + full_scan + + DESCRIPTION + Resolve subquery using index lookup(s). + First look for specified constant, + If not found and we need to check for NULLs, do that too. + + NULL IN (SELECT ...) is a special case. + + RETURN + 0 + 1 +*/ + +int subselect_indexsubquery_engine::exec(bool full_scan) { - DBUG_ENTER("subselect_indexsubselect_engine::exec"); + DBUG_ENTER("subselect_indexsubquery_engine::exec"); int error; bool null_finding= 0; TABLE *table= tab->table; ((Item_in_subselect *) item)->value= 0; + empty_result_set= TRUE; + null_keypart= 0; if (check_null) { @@ -1673,14 +1975,12 @@ int subselect_indexsubquery_engine::exec() ((Item_in_subselect *) item)->was_null= 0; } - for (store_key **copy=tab->ref.key_copy ; *copy ; copy++) - { - if ((tab->ref.key_err= (*copy)->copy()) & 1) - { - table->status= STATUS_NOT_FOUND; - DBUG_RETURN(1); - } - } + /* Copy the ref key and check for nulls... */ + if (copy_ref_key()) + DBUG_RETURN(1); + + if (null_keypart) + DBUG_RETURN(scan_table()); if (!table->file->inited) table->file->ha_index_init(tab->ref.key); diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 45df4f3880d..9410dbc465e 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -60,6 +60,9 @@ public: /* subquery is transformed */ bool changed; + /* TRUE <=> The underlying SELECT is correlated w.r.t some ancestor select */ + bool is_correlated; + enum trans_res {RES_OK, RES_REDUCE, RES_ERROR}; enum subs_type {UNKNOWN_SUBS, SINGLEROW_SUBS, EXISTS_SUBS, IN_SUBS, ALL_SUBS, ANY_SUBS}; @@ -92,7 +95,7 @@ public: return null_value; } bool fix_fields(THD *thd, Item **ref); - virtual bool exec(); + virtual bool exec(bool full_scan); virtual void fix_length_and_dec(); table_map used_tables() const; table_map not_null_tables() const { return 0; } @@ -215,7 +218,20 @@ public: friend class subselect_indexsubquery_engine; }; -/* IN subselect */ + +/* + IN subselect: this represents "left_exr IN (SELECT ...)" + + This class has: + - (as a descendant of Item_subselect) a "subquery execution engine" which + allows it to evaluate subqueries. (and this class participates in + execution by having was_null variable where part of execution result + is stored. + - Transformation methods (todo: more on this). + + This class is not used directly, it is "wrapped" into Item_in_optimizer + which provides some small bits of subquery evaluation. +*/ class Item_in_subselect :public Item_exists_subselect { @@ -231,12 +247,14 @@ protected: bool abort_on_null; bool transformed; public: + /* Used to trigger on/off conditions that were pushed down to subselect */ + bool enable_pushed_conds; Item_func_not_all *upper_item; // point on NOT/NOP before ALL/SOME subquery Item_in_subselect(Item * left_expr, st_select_lex *select_lex); Item_in_subselect() :Item_exists_subselect(), optimizer(0), abort_on_null(0), transformed(0), - upper_item(0) + enable_pushed_conds(TRUE), upper_item(0) {} subs_type substype() { return IN_SUBS; } @@ -256,6 +274,7 @@ public: my_decimal *val_decimal(my_decimal *); bool val_bool(); void top_level_item() { abort_on_null=1; } + inline bool is_top_level_item() { return abort_on_null; } bool test_limit(st_select_lex_unit *unit); void print(String *str); bool fix_fields(THD *thd, Item **ref); @@ -313,7 +332,28 @@ public: THD * get_thd() { return thd; } virtual int prepare()= 0; virtual void fix_length_and_dec(Item_cache** row)= 0; - virtual int exec()= 0; + /* + Execute the engine + + SYNOPSIS + exec() + full_scan TRUE - Pushed-down predicates are disabled, the engine + must disable made based on those predicates. + FALSE - Pushed-down predicates are in effect. + DESCRIPTION + Execute the engine. The result of execution is subquery value that is + either captured by previously set up select_result-based 'sink' or + stored somewhere by the exec() method itself. + + A required side effect: if full_scan==TRUE, subselect_engine->no_rows() + should return correct result. + + RETURN + 0 - OK + 1 - Either an execution error, or the engine was be "changed", and + caller should call exec() again for the new engine. + */ + virtual int exec(bool full_scan)= 0; virtual uint cols()= 0; /* return number of columns in select */ virtual uint8 uncacheable()= 0; /* query is uncacheable */ enum Item_result type() { return res_type; } @@ -325,6 +365,8 @@ public: virtual bool change_result(Item_subselect *si, select_subselect *result)= 0; virtual bool no_tables()= 0; virtual bool is_executed() const { return FALSE; } + /* Check if subquery produced any rows during last query execution */ + virtual bool no_rows() = 0; }; @@ -342,7 +384,7 @@ public: void cleanup(); int prepare(); void fix_length_and_dec(Item_cache** row); - int exec(); + int exec(bool full_scan); uint cols(); uint8 uncacheable(); void exclude(); @@ -351,6 +393,7 @@ public: bool change_result(Item_subselect *si, select_subselect *result); bool no_tables(); bool is_executed() const { return executed; } + bool no_rows(); }; @@ -364,7 +407,7 @@ public: void cleanup(); int prepare(); void fix_length_and_dec(Item_cache** row); - int exec(); + int exec(bool full_scan); uint cols(); uint8 uncacheable(); void exclude(); @@ -373,6 +416,7 @@ public: bool change_result(Item_subselect *si, select_subselect *result); bool no_tables(); bool is_executed() const; + bool no_rows(); }; @@ -382,6 +426,12 @@ class subselect_uniquesubquery_engine: public subselect_engine protected: st_join_table *tab; Item *cond; + /* + TRUE<=> last execution produced empty set. Valid only when left + expression is NULL. + */ + bool empty_result_set; + bool null_keypart; /* TRUE <=> constructed search tuple has a NULL */ public: // constructor can assign THD because it will be called after JOIN::prepare @@ -395,7 +445,7 @@ public: void cleanup(); int prepare(); void fix_length_and_dec(Item_cache** row); - int exec(); + int exec(bool full_scan); uint cols() { return 1; } uint8 uncacheable() { return UNCACHEABLE_DEPENDENT; } void exclude(); @@ -403,11 +453,15 @@ public: void print (String *str); bool change_result(Item_subselect *si, select_subselect *result); bool no_tables(); + int scan_table(); + bool copy_ref_key(); + bool no_rows() { return empty_result_set; } }; class subselect_indexsubquery_engine: public subselect_uniquesubquery_engine { + /* FALSE for 'ref', TRUE for 'ref-or-null'. */ bool check_null; public: @@ -418,7 +472,7 @@ public: :subselect_uniquesubquery_engine(thd, tab_arg, subs, where), check_null(chk_null) {} - int exec(); + int exec(bool full_scan); void print (String *str); }; diff --git a/sql/records.cc b/sql/records.cc index b352f9f395a..3e254fa3648 100644 --- a/sql/records.cc +++ b/sql/records.cc @@ -20,7 +20,7 @@ #include "mysql_priv.h" static int rr_quick(READ_RECORD *info); -static int rr_sequential(READ_RECORD *info); +int rr_sequential(READ_RECORD *info); static int rr_from_tempfile(READ_RECORD *info); static int rr_unpack_from_tempfile(READ_RECORD *info); static int rr_unpack_from_buffer(READ_RECORD *info); @@ -184,6 +184,7 @@ void init_read_record(READ_RECORD *info,THD *thd, TABLE *table, } /* init_read_record */ + void end_read_record(READ_RECORD *info) { /* free cache if used */ if (info->cache) @@ -289,7 +290,7 @@ static int rr_index(READ_RECORD *info) } -static int rr_sequential(READ_RECORD *info) +int rr_sequential(READ_RECORD *info) { int tmp; while ((tmp=info->file->rnd_next(info->record))) diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index af81960f9ef..5eb6ea25b68 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1179,7 +1179,7 @@ void st_select_lex::init_select() select_limit= 0; /* denotes the default limit = HA_POS_ERROR */ offset_limit= 0; /* denotes the default offset = 0 */ with_sum_func= 0; - + is_correlated= 0; } /* @@ -1373,6 +1373,8 @@ void st_select_lex::mark_as_dependent(SELECT_LEX *last) SELECT_LEX_UNIT *munit= s->master_unit(); munit->uncacheable|= UNCACHEABLE_DEPENDENT; } + is_correlated= TRUE; + this->master_unit()->item->is_correlated= TRUE; } bool st_select_lex_node::set_braces(bool value) { return 1; } diff --git a/sql/sql_lex.h b/sql/sql_lex.h index a3173b73d6d..027b012542e 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -470,7 +470,7 @@ public: void set_thd(THD *thd_arg) { thd= thd_arg; } friend void lex_start(THD *thd, uchar *buf, uint length); - friend int subselect_union_engine::exec(); + friend int subselect_union_engine::exec(bool); List *get_unit_column_types(); }; @@ -562,6 +562,8 @@ public: query processing end even if we use temporary table */ bool subquery_in_having; + /* TRUE <=> this SELECT is correlated w.r.t. some ancestor select */ + bool is_correlated; /* This variable is required to ensure proper work of subqueries and stored procedures. Generally, one should use the states of diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 0f0642280ce..df333e7c9ab 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -158,8 +158,8 @@ static int join_read_prev_same(READ_RECORD *info); static int join_read_prev(READ_RECORD *info); static int join_ft_read_first(JOIN_TAB *tab); static int join_ft_read_next(READ_RECORD *info); -static int join_read_always_key_or_null(JOIN_TAB *tab); -static int join_read_next_same_or_null(READ_RECORD *info); +int join_read_always_key_or_null(JOIN_TAB *tab); +int join_read_next_same_or_null(READ_RECORD *info); static COND *make_cond_for_table(COND *cond,table_map table, table_map used_table); static Item* part_of_refkey(TABLE *form,Field *field); @@ -512,11 +512,12 @@ err: DBUG_RETURN(-1); /* purecov: inspected */ } + /* test if it is known for optimisation IN subquery - SYNOPSYS - JOIN::test_in_subselect + SYNOPSIS + JOIN::test_in_subselect() where - pointer for variable in which conditions should be stored if subquery is known @@ -550,6 +551,35 @@ bool JOIN::test_in_subselect(Item **where) } +/* + Check if the passed HAVING clause is a clause added by subquery optimizer + + SYNOPSIS + is_having_subq_predicates() + having Having clause + + RETURN + TRUE The passed HAVING clause was added by the subquery optimizer + FALSE Otherwise +*/ + +bool is_having_subq_predicates(Item *having) +{ + if (having->type() == Item::FUNC_ITEM) + { + if (((Item_func *) having)->functype() == Item_func::ISNOTNULLTEST_FUNC) + return TRUE; + if (((Item_func *) having)->functype() == Item_func::TRIG_COND_FUNC) + { + having= ((Item_func*)having)->arguments()[0]; + if (((Item_func *) having)->functype() == Item_func::ISNOTNULLTEST_FUNC) + return TRUE; + } + return TRUE; + } + return FALSE; +} + /* global select optimisation. return 0 - success @@ -1016,9 +1046,7 @@ JOIN::optimize() } } else if (join_tab[0].type == JT_REF_OR_NULL && join_tab[0].ref.items[0]->name == in_left_expr_name && - having->type() == Item::FUNC_ITEM && - ((Item_func *) having)->functype() == - Item_func::ISNOTNULLTEST_FUNC) + is_having_subq_predicates(having)) { join_tab[0].type= JT_INDEX_SUBQUERY; error= 0; @@ -2512,6 +2540,9 @@ typedef struct key_field_t { // Used when finding key fields when val IS NULL. */ bool null_rejecting; + + /* TRUE<=> This ref access is an outer subquery reference access */ + bool outer_ref; } KEY_FIELD; /* Values in optimize */ @@ -2810,6 +2841,7 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond, cond->functype() == Item_func::MULT_EQUAL_FUNC) && ((*value)->type() == Item::FIELD_ITEM) && ((Item_field*)*value)->field->maybe_null()); + (*key_fields)->outer_ref= FALSE; (*key_fields)++; } @@ -2868,7 +2900,7 @@ add_key_equal_fields(KEY_FIELD **key_fields, uint and_level, } static void -add_key_fields(KEY_FIELD **key_fields,uint *and_level, +add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level, COND *cond, table_map usable_tables, SARGABLE_PARAM **sargables) { @@ -2881,28 +2913,54 @@ add_key_fields(KEY_FIELD **key_fields,uint *and_level, { Item *item; while ((item=li++)) - add_key_fields(key_fields,and_level,item,usable_tables,sargables); + add_key_fields(join, key_fields, and_level, item, usable_tables, + sargables); for (; org_key_fields != *key_fields ; org_key_fields++) org_key_fields->level= *and_level; } else { (*and_level)++; - add_key_fields(key_fields,and_level,li++,usable_tables,sargables); + add_key_fields(join, key_fields, and_level, li++, usable_tables, + sargables); Item *item; while ((item=li++)) { KEY_FIELD *start_key_fields= *key_fields; (*and_level)++; - add_key_fields(key_fields,and_level,item,usable_tables,sargables); + add_key_fields(join, key_fields, and_level, item, usable_tables, + sargables); *key_fields=merge_key_fields(org_key_fields,start_key_fields, *key_fields,++(*and_level)); } } return; } - /* If item is of type 'field op field/constant' add it to key_fields */ + /* + Subquery optimization: check if the encountered condition is one + added by condition push down into subquery. + */ + { + if (cond->type() == Item::FUNC_ITEM && + ((Item_func*)cond)->functype() == Item_func::TRIG_COND_FUNC) + { + cond= ((Item_func*)cond)->arguments()[0]; + if (!join->group_list && !join->order && + join->unit->item && + join->unit->item->substype() == Item_subselect::IN_SUBS && + !join->unit->first_select()->next_select()) + { + add_key_fields(join, key_fields, and_level, cond, usable_tables, + sargables); + // Indicate that this ref access candidate is for subquery lookup: + (*key_fields)[-1].outer_ref= TRUE; + } + return; + } + } + + /* If item is of type 'field op field/constant' add it to key_fields */ if (cond->type() != Item::FUNC_ITEM) return; Item_func *cond_func= (Item_func*) cond; @@ -3076,6 +3134,7 @@ add_key_part(DYNAMIC_ARRAY *keyuse_array,KEY_FIELD *key_field) keyuse.used_tables=key_field->val->used_tables(); keyuse.optimize= key_field->optimize & KEY_OPTIMIZE_REF_OR_NULL; keyuse.null_rejecting= key_field->null_rejecting; + keyuse.outer_ref= key_field->outer_ref; VOID(insert_dynamic(keyuse_array,(gptr) &keyuse)); } } @@ -3198,7 +3257,7 @@ sort_keyuse(KEYUSE *a,KEYUSE *b) Here we can add 'ref' access candidates for t1 and t2, but not for t3. */ -static void add_key_fields_for_nj(TABLE_LIST *nested_join_table, +static void add_key_fields_for_nj(JOIN *join, TABLE_LIST *nested_join_table, KEY_FIELD **end, uint *and_level, SARGABLE_PARAM **sargables) { @@ -3210,12 +3269,13 @@ static void add_key_fields_for_nj(TABLE_LIST *nested_join_table, while ((table= li++)) { if (table->nested_join) - add_key_fields_for_nj(table, end, and_level, sargables); + add_key_fields_for_nj(join, table, end, and_level, sargables); else if (!table->on_expr) tables |= table->table->map; } - add_key_fields(end, and_level, nested_join_table->on_expr, tables, sargables); + add_key_fields(join, end, and_level, nested_join_table->on_expr, tables, + sargables); } @@ -3290,7 +3350,8 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, return TRUE; if (cond) { - add_key_fields(&end,&and_level,cond,normal_tables,sargables); + add_key_fields(join_tab->join, &end, &and_level, cond, normal_tables, + sargables); for (; field != end ; field++) { add_key_part(keyuse,field); @@ -3312,8 +3373,9 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, into account as well. */ if (*join_tab[i].on_expr_ref) - add_key_fields(&end,&and_level,*join_tab[i].on_expr_ref, - join_tab[i].table->map,sargables); + add_key_fields(join_tab->join, &end, &and_level, + *join_tab[i].on_expr_ref, + join_tab[i].table->map, sargables); } /* Process ON conditions for the nested joins */ @@ -3323,7 +3385,8 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, while ((table= li++)) { if (table->nested_join) - add_key_fields_for_nj(table, &end, &and_level, sargables); + add_key_fields_for_nj(join_tab->join, table, &end, &and_level, + sargables); } } @@ -10784,6 +10847,13 @@ join_init_quick_read_record(JOIN_TAB *tab) } +int rr_sequential(READ_RECORD *info); +int init_read_record_seq(JOIN_TAB *tab) +{ + tab->read_record.read_record= rr_sequential; + return tab->read_record.file->ha_rnd_init(1); +} + static int test_if_quick_select(JOIN_TAB *tab) { @@ -10912,7 +10982,7 @@ join_ft_read_next(READ_RECORD *info) Reading of key with key reference and one part that may be NULL */ -static int +int join_read_always_key_or_null(JOIN_TAB *tab) { int res; @@ -10928,7 +10998,7 @@ join_read_always_key_or_null(JOIN_TAB *tab) } -static int +int join_read_next_same_or_null(READ_RECORD *info) { int error; diff --git a/sql/sql_select.h b/sql/sql_select.h index 30b8f834ddf..629b44538d8 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -36,6 +36,8 @@ typedef struct keyuse_t { satisfied if val has NULL 'value'. */ bool null_rejecting; + /* TRUE<=> This ref access is an outer subquery reference access */ + bool outer_ref; } KEYUSE; class store_key; @@ -455,10 +457,11 @@ class store_key :public Sql_alloc Field *to_field; // Store data here char *null_ptr; char err; - public: +public: + bool null_key; /* TRUE <=> the value of the key has a null part */ enum store_key_result { STORE_KEY_OK, STORE_KEY_FATAL, STORE_KEY_CONV }; store_key(THD *thd, Field *field_arg, char *ptr, char *null, uint length) - :null_ptr(null),err(0) + :null_ptr(null), err(0), null_key(0) { if (field_arg->type() == FIELD_TYPE_BLOB) { @@ -496,6 +499,7 @@ class store_key_field: public store_key enum store_key_result copy() { copy_field.do_copy(©_field); + null_key= to_field->is_null(); return err != 0 ? STORE_KEY_FATAL : STORE_KEY_OK; } const char *name() const { return field_name; } @@ -516,8 +520,8 @@ public: enum store_key_result copy() { int res= item->save_in_field(to_field, 1); + null_key= to_field->is_null() || item->null_value; return (err != 0 || res > 2 ? STORE_KEY_FATAL : (store_key_result) res); - } const char *name() const { return "func"; } }; @@ -547,6 +551,7 @@ public: err= res; } } + null_key= to_field->is_null() || item->null_value; return (err > 2 ? STORE_KEY_FATAL : (store_key_result) err); } const char *name() const { return "const"; } From cc05fbd7b08f247f84a760183d2961ecf229daf3 Mon Sep 17 00:00:00 2001 From: "sergefp@mysql.com" <> Date: Tue, 31 Oct 2006 21:30:40 +0300 Subject: [PATCH 13/65] BUG#8804: Better comment + TODO section with suggestion how to speedup the fix. --- sql/item_subselect.cc | 41 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 489a647402e..27cd376001e 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -1946,11 +1946,44 @@ subselect_uniquesubquery_engine::~subselect_uniquesubquery_engine() full_scan DESCRIPTION - Resolve subquery using index lookup(s). - First look for specified constant, - If not found and we need to check for NULLs, do that too. + The engine is used to resolve subqueries in form - NULL IN (SELECT ...) is a special case. + oe IN (SELECT key FROM tbl WHERE subq_where) + + The value of the predicate is calculated as follows: + 1. If oe IS NULL, this is a special case, do a full table scan on + table tbl and search for row that satisfies subq_where. If such + row is found, return NULL, otherwise return FALSE. + 2. Make an index lookup via key=oe, search for a row that satisfies + subq_where. If found, return TRUE. + 3. If check_null==TRUE, make another lookup via key=NULL, search for a + row that satisfies subq_where. If found, return NULL, otherwise + return FALSE. + + TODO + The step #1 can be optimized further when the index has several key + parts. Consider a subquery: + + (oe1, oe2) IN (SELECT keypart1, keypart2 FROM tbl WHERE subq_where) + + and suppose we need to evaluate it for {oe1, oe2}=={const1, NULL}. + Current code will do a full table scan and obtain correct result. There + is a better option: instead of evaluating + + SELECT keypart1, keypart2 FROM tbl WHERE subq_where (1) + + and checking if it has produced any matching rows, evaluate + + SELECT keypart2 FROM tbl WHERE subq_where AND keypart1=const1 (2) + + If this query produces a row, the result is NULL (as we're evaluating + "(const1, NULL) IN { (const1, X), ... }", which has a value of UNKNOWN, + i.e. NULL). If the query produces no rows, the result is FALSE. + + We currently evaluate (1) by doing a full table scan. (2) can be + evaluated by doing a "ref" scan on "keypart1=const1", which can be much + cheaper. We can use index statistics to quickly check whether "ref" scan + will be cheaper than full table scan. RETURN 0 From e59f19177e6a578339cbba8bfd23c1bde3cc6d5a Mon Sep 17 00:00:00 2001 From: "sergefp@mysql.com" <> Date: Wed, 1 Nov 2006 00:27:51 +0300 Subject: [PATCH 14/65] BUG#8804: Incorrect results for NULL IN (SELECT ...): review fixes: - Better comments - Remove redundant and dead code. --- sql/item_subselect.cc | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 27cd376001e..432a8882f5f 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -769,43 +769,47 @@ my_decimal *Item_in_subselect::val_decimal(my_decimal *decimal_value) SYNOPSIS Item_in_subselect::single_value_transformer() - join - func + join Join object of the subquery (i.e. 'child' join). + func Subquery comparison creator DESCRIPTION Rewrite a single-column subquery using rule-based approach. The subquery - oe $cmp$ (SELECT sel FROM ... WHERE subq_where HAVING subq_having) + oe $cmp$ (SELECT ie FROM ... WHERE subq_where ... HAVING subq_having) First, try to convert the subquery to scalar-result subquery in one of the forms: - oe $cmp$ (SELECT MAX(...) ) // handled by Item_singlerow_subselect - - oe $cmp$ (SELECT ...) // handled by Item_maxminsubselect + - oe $cmp$ (SELECT ...) // handled by Item_maxmin_subselect If that fails, the subquery will be handled with class Item_in_optimizer, Inject the predicates into subquery, i.e. convert it to: - If the subquery has aggregates, GROUP BY, or HAVING, convert to - SELECT sel FROM ... HAVING subq_having AND + SELECT ie FROM ... HAVING subq_having AND trigcond(oe $cmp$ ref_or_null_helper) the addition is wrapped into trigger only when we want to distinguish between NULL and FALSE results. - - Else, if we don't care if subquery result is NULL or FALSE, convert to - - SELECT 1 ... WHERE (oe $CMP$ ie) AND subq_where + - Otherwise (no aggregates/GROUP BY/HAVING) convert it to one of the + following: - - Else convert to: + = If we don't need to distinguish between NULL and FALSE subquery: + + SELECT 1 FROM ... WHERE (oe $cmp$ ie) AND subq_where - SELECT 1 WHERE ... - WHERE subq_where AND trigcond((oe $CMP$ ie) OR ie IS NULL) - HAVING subq_having AND trigcond((ie)) + = If we need to distinguish between those: + + SELECT 1 FROM ... + WHERE subq_where AND trigcond((oe $cmp$ ie) OR (ie IS NULL)) + HAVING trigcond((ie)) RETURN - RES_OK - Transformed successfully (or done nothing?) + RES_OK - OK, either subquery was transformed, or appopriate + predicates where injected into it. RES_REDUCE - The subquery was reduced to non-subquery RES_ERROR - Error */ @@ -1010,10 +1014,7 @@ Item_in_subselect::single_value_transformer(JOIN *join, we can assign select_lex->having here, and pass 0 as last argument (reference) to fix_fields() */ - select_lex->having= - join->having= (join->having ? - new Item_cond_and(having, join->having) : - having); + select_lex->having= join->having= having; select_lex->having_fix_field= 1; /* we do not check join->having->fixed, because Item_and (from @@ -1608,7 +1609,6 @@ int subselect_uniquesubquery_engine::prepare() bool subselect_single_select_engine::no_rows() { -// return test(!join->send_records); return !item->assigned(); } @@ -1791,9 +1791,7 @@ int subselect_uniquesubquery_engine::scan_table() int error; TABLE *table= tab->table; DBUG_ENTER("subselect_uniquesubquery_engine::scan_table"); - empty_result_set= TRUE; - bool is_uncorrelated= !cond || !(cond->used_tables() & OUTER_REF_TABLE_BIT); if (table->file->inited) table->file->ha_index_end(); From 2a7acba7e10197ec4a651ae828ff51c0a2ff4747 Mon Sep 17 00:00:00 2001 From: "igor@rurik.mysql.com" <> Date: Tue, 31 Oct 2006 17:31:56 -0800 Subject: [PATCH 15/65] Fixed bug #21727. This is a performance issue for queries with subqueries evaluation of which requires filesort. Allocation of memory for the sort buffer at each evaluation of a subquery may take a significant amount of time if the buffer is rather big. With the fix we allocate the buffer at the first evaluation of the subquery and reuse it at each subsequent evaluation. --- mysql-test/r/subselect.result | 16 ++++++++++++++ mysql-test/t/subselect.test | 37 +++++++++++++++++++++++++++++++ sql/filesort.cc | 41 +++++++++++++++++++++++++++++------ sql/item_subselect.h | 7 ++++++ sql/mysql_priv.h | 2 +- sql/records.cc | 2 +- sql/sql_base.cc | 2 ++ sql/sql_select.cc | 6 ++--- sql/sql_show.cc | 2 +- sql/sql_table.cc | 3 --- sql/table.cc | 17 +++++++++++++++ sql/table.h | 5 +++++ 12 files changed, 124 insertions(+), 16 deletions(-) diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 57d6199675d..d05ec36a24f 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -3545,3 +3545,19 @@ FROM t1 GROUP BY t1.a LIMIT 1) 2 2 DROP TABLE t1,t2; +CREATE TABLE t1 (a int, b int auto_increment, PRIMARY KEY (b)); +CREATE TABLE t2 (x int auto_increment, y int, z int, +PRIMARY KEY (x), FOREIGN KEY (y) REFERENCES t1 (b)); +SET SESSION sort_buffer_size = 32 * 1024; +SELECT SQL_NO_CACHE COUNT(*) +FROM (SELECT a, b, (SELECT x FROM t2 WHERE y=b ORDER BY z DESC LIMIT 1) c +FROM t1) t; +COUNT(*) +3000 +SET SESSION sort_buffer_size = 8 * 1024 * 1024; +SELECT SQL_NO_CACHE COUNT(*) +FROM (SELECT a, b, (SELECT x FROM t2 WHERE y=b ORDER BY z DESC LIMIT 1) c +FROM t1) t; +COUNT(*) +3000 +DROP TABLE t1,t2; diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index 6d5082c360b..2f3ae3347e8 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -2426,3 +2426,40 @@ SELECT ( FROM t1 t2 GROUP BY t2.a; DROP TABLE t1,t2; + +# +# Bug #21727: Correlated subquery that requires filesort: +# slow with big sort_buffer_size +# + +CREATE TABLE t1 (a int, b int auto_increment, PRIMARY KEY (b)); +CREATE TABLE t2 (x int auto_increment, y int, z int, + PRIMARY KEY (x), FOREIGN KEY (y) REFERENCES t1 (b)); + +disable_query_log; +let $1=3000; +while ($1) +{ + eval INSERT INTO t1(a) VALUES(RAND()*1000); + eval SELECT MAX(b) FROM t1 INTO @id; + let $2=10; + while ($2) + { + eval INSERT INTO t2(y,z) VALUES(@id,RAND()*1000); + dec $2; + } + dec $1; +} +enable_query_log; + +SET SESSION sort_buffer_size = 32 * 1024; +SELECT SQL_NO_CACHE COUNT(*) + FROM (SELECT a, b, (SELECT x FROM t2 WHERE y=b ORDER BY z DESC LIMIT 1) c + FROM t1) t; + +SET SESSION sort_buffer_size = 8 * 1024 * 1024; +SELECT SQL_NO_CACHE COUNT(*) + FROM (SELECT a, b, (SELECT x FROM t2 WHERE y=b ORDER BY z DESC LIMIT 1) c + FROM t1) t; + +DROP TABLE t1,t2; diff --git a/sql/filesort.cc b/sql/filesort.cc index f13354d5c72..6e74d978eda 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -109,6 +109,8 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, DBUG_PUSH(""); /* No DBUG here */ #endif FILESORT_INFO table_sort; + TABLE_LIST *tab= table->pos_in_table_list; + Item_subselect *subselect= tab ? tab->containing_subselect() : 0; /* Don't use table->sort in filesort as it is also used by QUICK_INDEX_MERGE_SELECT. Work with a copy and put it back at the end @@ -121,7 +123,6 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, my_b_clear(&tempfile); my_b_clear(&buffpek_pointers); buffpek=0; - sort_keys= (uchar **) NULL; error= 1; bzero((char*) ¶m,sizeof(param)); param.sort_length= sortlength(thd, sortorder, s_length, &multi_byte_charset); @@ -202,13 +203,15 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, ulong old_memavl; ulong keys= memavl/(param.rec_length+sizeof(char*)); param.keys=(uint) min(records+1, keys); - if ((sort_keys= (uchar **) make_char_array(param.keys, param.rec_length, - MYF(0)))) + if (table_sort.sort_keys || + (table_sort.sort_keys= (uchar **) make_char_array(param.keys, param.rec_length, + MYF(0)))) break; old_memavl=memavl; if ((memavl=memavl/4*3) < min_sort_memory && old_memavl > min_sort_memory) memavl= min_sort_memory; } + sort_keys= table_sort.sort_keys; if (memavl < min_sort_memory) { my_error(ER_OUTOFMEMORY,MYF(ME_ERROR+ME_WAITTANG), @@ -235,8 +238,12 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, } else { - if (!(buffpek=read_buffpek_from_file(&buffpek_pointers, maxbuffer))) + if (!table_sort.buffpek && table_sort.buffpek_len < maxbuffer && + !(table_sort.buffpek= + (byte *) read_buffpek_from_file(&buffpek_pointers, maxbuffer))) goto err; + buffpek= (BUFFPEK *) table_sort.buffpek; + table_sort.buffpek_len= maxbuffer; close_cached_file(&buffpek_pointers); /* Open cached file if it isn't open */ if (! my_b_inited(outfile) && @@ -269,8 +276,14 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, err: if (param.tmp_buffer) x_free(param.tmp_buffer); - x_free((gptr) sort_keys); - x_free((gptr) buffpek); + if (!subselect || !subselect->is_uncacheable()) + { + x_free((gptr) sort_keys); + table_sort.sort_keys= 0; + x_free((gptr) buffpek); + table_sort.buffpek= 0; + table_sort.buffpek_len= 0; + } close_cached_file(&tempfile); close_cached_file(&buffpek_pointers); if (my_b_inited(outfile)) @@ -301,13 +314,27 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, } /* filesort */ -void filesort_free_buffers(TABLE *table) +void filesort_free_buffers(TABLE *table, bool full) { if (table->sort.record_pointers) { my_free((gptr) table->sort.record_pointers,MYF(0)); table->sort.record_pointers=0; } + if (full) + { + if (table->sort.sort_keys ) + { + x_free((gptr) table->sort.sort_keys); + table->sort.sort_keys= 0; + } + if (table->sort.buffpek) + { + x_free((gptr) table->sort.buffpek); + table->sort.buffpek= 0; + table->sort.buffpek_len= 0; + } + } if (table->sort.addon_buf) { my_free((char *) table->sort.addon_buf, MYF(0)); diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 45df4f3880d..1a8111069e6 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -114,6 +114,7 @@ public: single select and union subqueries only. */ bool is_evaluated() const; + bool is_uncacheable() const; /* Used by max/min subquery to initialize value presence registration @@ -428,3 +429,9 @@ inline bool Item_subselect::is_evaluated() const return engine->is_executed(); } +inline bool Item_subselect::is_uncacheable() const +{ + return engine->uncacheable(); +} + + diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index ea3b3a9bd83..13e44b49b53 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1465,7 +1465,7 @@ void end_read_record(READ_RECORD *info); ha_rows filesort(THD *thd, TABLE *form,struct st_sort_field *sortorder, uint s_length, SQL_SELECT *select, ha_rows max_rows, ha_rows *examined_rows); -void filesort_free_buffers(TABLE *table); +void filesort_free_buffers(TABLE *table, bool full); void change_double_for_sort(double nr,byte *to); double my_double_round(double value, int dec, bool truncate); int get_quick_record(SQL_SELECT *select); diff --git a/sql/records.cc b/sql/records.cc index b352f9f395a..4fcbc25c10f 100644 --- a/sql/records.cc +++ b/sql/records.cc @@ -193,7 +193,7 @@ void end_read_record(READ_RECORD *info) } if (info->table) { - filesort_free_buffers(info->table); + filesort_free_buffers(info->table,0); (void) info->file->extra(HA_EXTRA_NO_CACHE); if (info->read_record != rr_quick) // otherwise quick_range does it (void) info->file->ha_index_or_rnd_end(); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 0939fb3a47e..3984ceac6a9 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1478,6 +1478,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, table->file->ft_handler= 0; if (table->timestamp_field) table->timestamp_field_type= table->timestamp_field->get_auto_set_type(); + table->pos_in_table_list= table_list; table_list->updatable= 1; // It is not derived table nor non-updatable VIEW DBUG_ASSERT(table->key_read == 0); DBUG_RETURN(table); @@ -2762,6 +2763,7 @@ TABLE *open_temporary_table(THD *thd, const char *path, const char *db, if (thd->slave_thread) slave_open_temp_tables++; } + tmp_table->pos_in_table_list= 0; DBUG_RETURN(tmp_table); } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 1dce7390ef1..d00795e0b14 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1260,14 +1260,14 @@ JOIN::reinit() exec_tmp_table1->file->extra(HA_EXTRA_RESET_STATE); exec_tmp_table1->file->delete_all_rows(); free_io_cache(exec_tmp_table1); - filesort_free_buffers(exec_tmp_table1); + filesort_free_buffers(exec_tmp_table1,0); } if (exec_tmp_table2) { exec_tmp_table2->file->extra(HA_EXTRA_RESET_STATE); exec_tmp_table2->file->delete_all_rows(); free_io_cache(exec_tmp_table2); - filesort_free_buffers(exec_tmp_table2); + filesort_free_buffers(exec_tmp_table2,0); } if (items0) set_items_ref_array(items0); @@ -6066,7 +6066,7 @@ void JOIN::cleanup(bool full) if (tables > const_tables) // Test for not-const tables { free_io_cache(table[const_tables]); - filesort_free_buffers(table[const_tables]); + filesort_free_buffers(table[const_tables],full); } if (full) diff --git a/sql/sql_show.cc b/sql/sql_show.cc index eb78f4fbdae..6367be4a1d4 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -3977,7 +3977,7 @@ bool get_schema_tables_result(JOIN *join) table_list->table->file->extra(HA_EXTRA_RESET_STATE); table_list->table->file->delete_all_rows(); free_io_cache(table_list->table); - filesort_free_buffers(table_list->table); + filesort_free_buffers(table_list->table,1); table_list->table->null_row= 0; } else diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 8864cf3c8bc..2803bfb9917 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -2266,7 +2266,6 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, goto send_result; } - table->table->pos_in_table_list= table; if ((table->table->db_stat & HA_READ_ONLY) && open_for_modify) { char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE]; @@ -4256,8 +4255,6 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, HA_CHECK_OPT *check_opt) } else { - t->pos_in_table_list= table; - if (t->file->table_flags() & HA_HAS_CHECKSUM && !(check_opt->flags & T_EXTEND)) protocol->store((ulonglong)t->file->checksum()); diff --git a/sql/table.cc b/sql/table.cc index 4dd3494f834..851b747dc83 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -3018,6 +3018,23 @@ void st_table_list::reinit_before_use(THD *thd) embedding->nested_join->join_list.head() == embedded); } +/* + Return subselect that contains the FROM list this table is taken from + + SYNOPSIS + st_table_list::containing_subselect() + + RETURN + Subselect item for the subquery that contains the FROM list + this table is taken from if there is any + 0 - otherwise + +*/ + +Item_subselect *st_table_list::containing_subselect() +{ + return (select_lex ? select_lex->master_unit()->item : 0); +} /***************************************************************************** ** Instansiate templates diff --git a/sql/table.h b/sql/table.h index 5136ac2c4db..f0190353328 100644 --- a/sql/table.h +++ b/sql/table.h @@ -18,6 +18,7 @@ /* Structs that defines the TABLE */ class Item; /* Needed by ORDER */ +class Item_subselect; class GRANT_TABLE; class st_select_lex_unit; class st_select_lex; @@ -68,6 +69,9 @@ enum frm_type_enum typedef struct st_filesort_info { IO_CACHE *io_cache; /* If sorted through filebyte */ + uchar **sort_keys; /* Buffer for sorting keys */ + byte *buffpek; /* Buffer for buffpek structures */ + uint buffpek_len; /* Max number of buffpeks in the buffer */ byte *addon_buf; /* Pointer to a buffer if sorted with fields */ uint addon_length; /* Length of the buffer */ struct st_sort_addon_field *addon_field; /* Pointer to the fields info */ @@ -678,6 +682,7 @@ typedef struct st_table_list procedure. */ void reinit_before_use(THD *thd); + Item_subselect *containing_subselect(); private: bool prep_check_option(THD *thd, uint8 check_opt_type); From d50bdf8fa7cfa0ccb7594710545a71026ec6e4b1 Mon Sep 17 00:00:00 2001 From: "holyfoot/hf@mysql.com/deer.(none)" <> Date: Wed, 1 Nov 2006 14:22:11 +0400 Subject: [PATCH 16/65] test result fixed --- mysql-test/r/type_newdecimal.result | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/r/type_newdecimal.result b/mysql-test/r/type_newdecimal.result index f24014da285..23e6f8266e9 100644 --- a/mysql-test/r/type_newdecimal.result +++ b/mysql-test/r/type_newdecimal.result @@ -1418,5 +1418,5 @@ insert into t1 values (CAST('10:11:12' AS time)); select * from t1; f1 20101112000000.000014 -20101112.000000 +101112.000000 drop table t1; From 77acba320d5d95bea1956e232d36cb438496a346 Mon Sep 17 00:00:00 2001 From: "gkodinov/kgeorge@macbook.gmz" <> Date: Fri, 3 Nov 2006 18:48:16 +0200 Subject: [PATCH 17/65] Bug #22457: Column alias in ORDER BY works, but not if in an expression The parser is allocating Item_field for references by name in ORDER BY expressions. Such expressions however may point not only to Item_field in the select list (or to a table column) but also to an arbitrary Item. This causes Item_field::fix_fields to throw an error about missing column. The fix substitutes Item_field for the reference with an Item_ref when not pointing to Item_field. --- mysql-test/r/order_by.result | 27 +++++++++++++++++++++++++++ mysql-test/t/order_by.test | 16 ++++++++++++++++ sql/item.cc | 33 ++++++++++++++++++++++++++++++--- 3 files changed, 73 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/order_by.result b/mysql-test/r/order_by.result index 8126e223f55..320bb89b62e 100644 --- a/mysql-test/r/order_by.result +++ b/mysql-test/r/order_by.result @@ -820,3 +820,30 @@ b a 20 1 10 2 DROP TABLE t1; +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (1),(2); +SELECT a + 1 AS num FROM t1 ORDER BY 30 - num; +num +3 +2 +SELECT CONCAT('test', a) AS str FROM t1 ORDER BY UPPER(str); +str +test1 +test2 +SELECT a + 1 AS num FROM t1 GROUP BY 30 - num; +num +3 +2 +SELECT a + 1 AS num FROM t1 HAVING 30 - num; +num +2 +3 +SELECT a + 1 AS num, num + 1 FROM t1; +ERROR 42S22: Unknown column 'num' in 'field list' +SELECT a + 1 AS num, (select num + 2 FROM t1 LIMIT 1) FROM t1; +num (select num + 2 FROM t1 LIMIT 1) +2 4 +3 5 +SELECT a.a + 1 AS num FROM t1 a JOIN t1 b ON num = b.a; +ERROR 42S22: Unknown column 'num' in 'on clause' +DROP TABLE t1; diff --git a/mysql-test/t/order_by.test b/mysql-test/t/order_by.test index 1664afc70f9..a8024be7032 100644 --- a/mysql-test/t/order_by.test +++ b/mysql-test/t/order_by.test @@ -559,4 +559,20 @@ INSERT INTO t1 VALUES (1,30), (2,20), (1,10), (2,30), (1,20), (2,10); DROP TABLE t1; +# +# Bug #22457: Column alias in ORDER BY works, but not if in an expression +# + +CREATE TABLE t1 (a INT); INSERT INTO t1 VALUES (1),(2); +SELECT a + 1 AS num FROM t1 ORDER BY 30 - num; +SELECT CONCAT('test', a) AS str FROM t1 ORDER BY UPPER(str); +SELECT a + 1 AS num FROM t1 GROUP BY 30 - num; +SELECT a + 1 AS num FROM t1 HAVING 30 - num; +--error 1054 +SELECT a + 1 AS num, num + 1 FROM t1; +SELECT a + 1 AS num, (select num + 2 FROM t1 LIMIT 1) FROM t1; +--error 1054 +SELECT a.a + 1 AS num FROM t1 a JOIN t1 b ON num = b.a; +DROP TABLE t1; + # End of 4.1 tests diff --git a/sql/item.cc b/sql/item.cc index 94f0a24fcc3..45d7856b2c1 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -1761,10 +1761,37 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) Item** res= find_item_in_list(this, thd->lex->current_select->item_list, &counter, REPORT_EXCEPT_NOT_FOUND, ¬_used); - if (res != (Item **)not_found_item && (*res)->type() == Item::FIELD_ITEM) + if (res != (Item **)not_found_item) { - set_field((*((Item_field**)res))->field); - return 0; + if ((*res)->type() == Item::FIELD_ITEM) + { + /* + It's an Item_field referencing another Item_field in the select + list. + use the field from the Item_field in the select list and leave + the Item_field instance in place. + */ + set_field((*((Item_field**)res))->field); + return 0; + } + else + { + /* + It's not an Item_field in the select list so we must make a new + Item_ref to point to the Item in the select list and replace the + Item_field created by the parser with the new Item_ref. + */ + Item_ref *rf= new Item_ref(db_name,table_name,field_name); + if (!rf) + return 1; + thd->change_item_tree(ref, rf); + /* + Because Item_ref never substitutes itself with other items + in Item_ref::fix_fields(), we can safely use the original + pointer to it even after fix_fields() + */ + return rf->fix_fields(thd, tables, ref) || rf->check_cols(1); + } } } From d947f1c84722b5ddfe9d0dcfe204b6bd30ba6d4e Mon Sep 17 00:00:00 2001 From: "holyfoot/hf@mysql.com/deer.(none)" <> Date: Sun, 5 Nov 2006 22:42:23 +0400 Subject: [PATCH 18/65] merging --- mysql-test/r/gis-rtree.result | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/mysql-test/r/gis-rtree.result b/mysql-test/r/gis-rtree.result index 61a96afd8dc..95211ad9133 100644 --- a/mysql-test/r/gis-rtree.result +++ b/mysql-test/r/gis-rtree.result @@ -862,13 +862,6 @@ CHECK TABLE t1 EXTENDED; Table Op Msg_type Msg_text test.t1 check status OK DROP TABLE t1; -CREATE TABLE t1(foo GEOMETRY NOT NULL, SPATIAL INDEX(foo) ); -INSERT INTO t1(foo) VALUES (NULL); -ERROR 23000: Column 'foo' cannot be null -INSERT INTO t1() VALUES (); -ERROR 22003: Cannot get geometry object from data you send to the GEOMETRY field -INSERT INTO t1(foo) VALUES (''); -ERROR 22003: Cannot get geometry object from data you send to the GEOMETRY field CREATE TABLE t1 (foo GEOMETRY NOT NULL, SPATIAL INDEX(foo) ); INSERT INTO t1 (foo) VALUES (PointFromWKB(POINT(1,1))); INSERT INTO t1 (foo) VALUES (PointFromWKB(POINT(1,0))); @@ -880,3 +873,11 @@ SELECT 1 FROM t1 WHERE foo != PointFromWKB(POINT(0,0)); 1 1 DROP TABLE t1; +CREATE TABLE t1(foo GEOMETRY NOT NULL, SPATIAL INDEX(foo) ); +INSERT INTO t1(foo) VALUES (NULL); +ERROR 23000: Column 'foo' cannot be null +INSERT INTO t1() VALUES (); +ERROR 22003: Cannot get geometry object from data you send to the GEOMETRY field +INSERT INTO t1(foo) VALUES (''); +ERROR 22003: Cannot get geometry object from data you send to the GEOMETRY field +DROP TABLE t1; From d3eb8070837ab8a555586ddbe9f0233d4c8177ab Mon Sep 17 00:00:00 2001 From: "holyfoot/hf@mysql.com/deer.(none)" <> Date: Mon, 6 Nov 2006 19:12:19 +0400 Subject: [PATCH 19/65] merging --- mysql-test/r/gis-rtree.result | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/mysql-test/r/gis-rtree.result b/mysql-test/r/gis-rtree.result index 64b21ca988d..e8134a50496 100644 --- a/mysql-test/r/gis-rtree.result +++ b/mysql-test/r/gis-rtree.result @@ -862,13 +862,6 @@ CHECK TABLE t1 EXTENDED; Table Op Msg_type Msg_text test.t1 check status OK DROP TABLE t1; -CREATE TABLE t1(foo GEOMETRY NOT NULL, SPATIAL INDEX(foo) ); -INSERT INTO t1(foo) VALUES (NULL); -ERROR 23000: Column 'foo' cannot be null -INSERT INTO t1() VALUES (); -ERROR 22003: Cannot get geometry object from data you send to the GEOMETRY field -INSERT INTO t1(foo) VALUES (''); -ERROR 22003: Cannot get geometry object from data you send to the GEOMETRY field CREATE TABLE t1 (foo GEOMETRY NOT NULL, SPATIAL INDEX(foo) ); INSERT INTO t1 (foo) VALUES (PointFromWKB(POINT(1,1))); INSERT INTO t1 (foo) VALUES (PointFromWKB(POINT(1,0))); @@ -880,3 +873,11 @@ SELECT 1 FROM t1 WHERE foo != PointFromWKB(POINT(0,0)); 1 1 DROP TABLE t1; +CREATE TABLE t1(foo GEOMETRY NOT NULL, SPATIAL INDEX(foo) ); +INSERT INTO t1(foo) VALUES (NULL); +ERROR 23000: Column 'foo' cannot be null +INSERT INTO t1() VALUES (); +ERROR 22003: Cannot get geometry object from data you send to the GEOMETRY field +INSERT INTO t1(foo) VALUES (''); +ERROR 22003: Cannot get geometry object from data you send to the GEOMETRY field +DROP TABLE t1; From 7ddb8b68faf64c16f50d13c65de8f5ab2df4a96c Mon Sep 17 00:00:00 2001 From: "holyfoot/hf@mysql.com/deer.(none)" <> Date: Mon, 6 Nov 2006 22:33:18 +0400 Subject: [PATCH 20/65] bug #19491 (5.0-related additional fixes) --- include/my_time.h | 2 ++ mysql-test/r/gis-rtree.result | 15 ++++++++------- sql-common/my_time.c | 4 ++-- sql/field.cc | 28 ++++++++++++++++++++++++++++ 4 files changed, 40 insertions(+), 9 deletions(-) diff --git a/include/my_time.h b/include/my_time.h index e52ef69475d..17cc10a0221 100644 --- a/include/my_time.h +++ b/include/my_time.h @@ -49,6 +49,8 @@ typedef long my_time_t; #define TIME_NO_ZERO_DATE (TIME_NO_ZERO_IN_DATE*2) #define TIME_INVALID_DATES (TIME_NO_ZERO_DATE*2) +my_bool check_date(const MYSQL_TIME *ltime, my_bool not_zero_date, + ulong flags, int *was_cut); enum enum_mysql_timestamp_type str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time, uint flags, int *was_cut); diff --git a/mysql-test/r/gis-rtree.result b/mysql-test/r/gis-rtree.result index 64b21ca988d..e8134a50496 100644 --- a/mysql-test/r/gis-rtree.result +++ b/mysql-test/r/gis-rtree.result @@ -862,13 +862,6 @@ CHECK TABLE t1 EXTENDED; Table Op Msg_type Msg_text test.t1 check status OK DROP TABLE t1; -CREATE TABLE t1(foo GEOMETRY NOT NULL, SPATIAL INDEX(foo) ); -INSERT INTO t1(foo) VALUES (NULL); -ERROR 23000: Column 'foo' cannot be null -INSERT INTO t1() VALUES (); -ERROR 22003: Cannot get geometry object from data you send to the GEOMETRY field -INSERT INTO t1(foo) VALUES (''); -ERROR 22003: Cannot get geometry object from data you send to the GEOMETRY field CREATE TABLE t1 (foo GEOMETRY NOT NULL, SPATIAL INDEX(foo) ); INSERT INTO t1 (foo) VALUES (PointFromWKB(POINT(1,1))); INSERT INTO t1 (foo) VALUES (PointFromWKB(POINT(1,0))); @@ -880,3 +873,11 @@ SELECT 1 FROM t1 WHERE foo != PointFromWKB(POINT(0,0)); 1 1 DROP TABLE t1; +CREATE TABLE t1(foo GEOMETRY NOT NULL, SPATIAL INDEX(foo) ); +INSERT INTO t1(foo) VALUES (NULL); +ERROR 23000: Column 'foo' cannot be null +INSERT INTO t1() VALUES (); +ERROR 22003: Cannot get geometry object from data you send to the GEOMETRY field +INSERT INTO t1(foo) VALUES (''); +ERROR 22003: Cannot get geometry object from data you send to the GEOMETRY field +DROP TABLE t1; diff --git a/sql-common/my_time.c b/sql-common/my_time.c index 93bf23ed284..eea36adcaf3 100644 --- a/sql-common/my_time.c +++ b/sql-common/my_time.c @@ -76,8 +76,8 @@ uint calc_days_in_year(uint year) 1 error */ -static my_bool check_date(const MYSQL_TIME *ltime, my_bool not_zero_date, - ulong flags, int *was_cut) +my_bool check_date(const MYSQL_TIME *ltime, my_bool not_zero_date, + ulong flags, int *was_cut) { if (not_zero_date) { diff --git a/sql/field.cc b/sql/field.cc index b05398afe75..a09c97fb356 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -5407,7 +5407,21 @@ int Field_newdate::store_time(TIME *ltime,timestamp_type type) long tmp; int error= 0; if (type == MYSQL_TIMESTAMP_DATE || type == MYSQL_TIMESTAMP_DATETIME) + { tmp=ltime->year*16*32+ltime->month*32+ltime->day; + if ((my_bool)check_date(ltime, tmp, + (TIME_FUZZY_DATE | + (current_thd->variables.sql_mode & + (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | + MODE_INVALID_DATES))), &error)) + { + char buff[12]; + String str(buff, sizeof(buff), &my_charset_latin1); + make_date((DATE_TIME_FORMAT *) 0, ltime, &str); + set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, + str.ptr(), str.length(), MYSQL_TIMESTAMP_DATE, 1); + } + } else { tmp=0; @@ -5616,8 +5630,22 @@ int Field_datetime::store_time(TIME *ltime,timestamp_type type) structure always fit into DATETIME range. */ if (type == MYSQL_TIMESTAMP_DATE || type == MYSQL_TIMESTAMP_DATETIME) + { tmp=((ltime->year*10000L+ltime->month*100+ltime->day)*LL(1000000)+ (ltime->hour*10000L+ltime->minute*100+ltime->second)); + if ((my_bool)check_date(ltime, tmp, + (TIME_FUZZY_DATE | + (current_thd->variables.sql_mode & + (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | + MODE_INVALID_DATES))), &error)) + { + char buff[12]; + String str(buff, sizeof(buff), &my_charset_latin1); + make_datetime((DATE_TIME_FORMAT *) 0, ltime, &str); + set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, + str.ptr(), str.length(), MYSQL_TIMESTAMP_DATETIME,1); + } + } else { tmp=0; From 28faf5e46884a06a71fa278b93c739fdd2c703f7 Mon Sep 17 00:00:00 2001 From: "gkodinov@dl145s.mysql.com" <> Date: Tue, 7 Nov 2006 14:39:20 +0100 Subject: [PATCH 21/65] item.cc: merge fixes --- sql/item.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sql/item.cc b/sql/item.cc index 6e243c3d603..dc92edd651d 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -3727,16 +3727,16 @@ bool Item_field::fix_fields(THD *thd, Item **reference) Item_ref to point to the Item in the select list and replace the Item_field created by the parser with the new Item_ref. */ - Item_ref *rf= new Item_ref(db_name,table_name,field_name); + Item_ref *rf= new Item_ref(context, db_name,table_name,field_name); if (!rf) return 1; - thd->change_item_tree(ref, rf); + thd->change_item_tree(reference, rf); /* Because Item_ref never substitutes itself with other items in Item_ref::fix_fields(), we can safely use the original pointer to it even after fix_fields() */ - return rf->fix_fields(thd, tables, ref) || rf->check_cols(1); + return rf->fix_fields(thd, reference) || rf->check_cols(1); } } } From cf1ca923fc4bdff10dcd539a4d3b521fdfba8a12 Mon Sep 17 00:00:00 2001 From: "gkodinov/kgeorge@macbook.gmz" <> Date: Tue, 7 Nov 2006 18:16:17 +0200 Subject: [PATCH 22/65] Bug #11032: getObject() returns a String for a sub-query of type datetime - When returning metadata for scalar subqueries the actual type of the column was calculated based on the value type, which limits the actual type of a scalar subselect to the set of (currently) 3 basic types : integer, double precision or string. This is the reason that columns of types other then the basic ones (e.g. date/time) are reported as being of the corresponding basic type. Fixed by storing/returning information for the column type in addition to the result type. --- mysql-test/r/subselect.result | 17 +++++++++++++++++ mysql-test/t/subselect.test | 16 ++++++++++++++++ sql/item_subselect.cc | 34 ++++++++++++++++++++++++---------- sql/item_subselect.h | 7 +++++++ 4 files changed, 64 insertions(+), 10 deletions(-) diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 28fbfc86657..a3d1bafcb0d 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -2997,3 +2997,20 @@ a a IN (SELECT a FROM t1) 2 NULL 3 1 DROP TABLE t1,t2; +CREATE TABLE t1 (a DATETIME); +INSERT INTO t1 VALUES ('1998-09-23'), ('2003-03-25'); +CREATE TABLE t2 AS SELECT +(SELECT a FROM t1 WHERE a < '2000-01-01') AS sub_a +FROM t1 WHERE a > '2000-01-01'; +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `sub_a` datetime default NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +CREATE TABLE t3 AS (SELECT a FROM t1 WHERE a < '2000-01-01') UNION (SELECT a FROM t1 WHERE a > '2000-01-01'); +SHOW CREATE TABLE t3; +Table Create Table +t3 CREATE TABLE `t3` ( + `a` datetime default NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1,t2,t3; diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index ac035c72d18..11b7fcc4d8f 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -1965,4 +1965,20 @@ SELECT a, a IN (SELECT a FROM t1) FROM t2; DROP TABLE t1,t2; +# +# Bug #11302: getObject() returns a String for a sub-query of type datetime +# +CREATE TABLE t1 (a DATETIME); +INSERT INTO t1 VALUES ('1998-09-23'), ('2003-03-25'); + +CREATE TABLE t2 AS SELECT + (SELECT a FROM t1 WHERE a < '2000-01-01') AS sub_a + FROM t1 WHERE a > '2000-01-01'; +SHOW CREATE TABLE t2; + +CREATE TABLE t3 AS (SELECT a FROM t1 WHERE a < '2000-01-01') UNION (SELECT a FROM t1 WHERE a > '2000-01-01'); +SHOW CREATE TABLE t3; + +DROP TABLE t1,t2,t3; + # End of 4.1 tests diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 1ab81d1862d..cd1f8f83821 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -391,6 +391,15 @@ enum Item_result Item_singlerow_subselect::result_type() const return engine->type(); } +/* + Don't rely on the result type to calculate field type. + Ask the engine instead. +*/ +enum_field_types Item_singlerow_subselect::field_type() const +{ + return engine->field_type(); +} + void Item_singlerow_subselect::fix_length_and_dec() { if ((max_columns= engine->cols()) == 1) @@ -1357,31 +1366,35 @@ int subselect_uniquesubquery_engine::prepare() return 1; } -static Item_result set_row(List &item_list, Item *item, - Item_cache **row, bool *maybe_null) +/* + makes storage for the output values for the subquery and calcuates + their data and column types and their nullability. +*/ +void subselect_engine::set_row(List &item_list, Item_cache **row) { - Item_result res_type= STRING_RESULT; Item *sel_item; List_iterator_fast li(item_list); + res_type= STRING_RESULT; + res_field_type= FIELD_TYPE_VAR_STRING; for (uint i= 0; (sel_item= li++); i++) { item->max_length= sel_item->max_length; res_type= sel_item->result_type(); + res_field_type= sel_item->field_type(); item->decimals= sel_item->decimals; - *maybe_null= sel_item->maybe_null; + maybe_null= sel_item->maybe_null; if (!(row[i]= Item_cache::get_cache(res_type))) - return STRING_RESULT; // we should return something + return; row[i]->setup(sel_item); } if (item_list.elements > 1) res_type= ROW_RESULT; - return res_type; } void subselect_single_select_engine::fix_length_and_dec(Item_cache **row) { DBUG_ASSERT(row || select_lex->item_list.elements==1); - res_type= set_row(select_lex->item_list, item, row, &maybe_null); + set_row(select_lex->item_list, row); item->collation.set(row[0]->collation); if (cols() != 1) maybe_null= 0; @@ -1393,13 +1406,14 @@ void subselect_union_engine::fix_length_and_dec(Item_cache **row) if (unit->first_select()->item_list.elements == 1) { - res_type= set_row(unit->types, item, row, &maybe_null); + set_row(unit->types, row); item->collation.set(row[0]->collation); } else { - bool fake= 0; - res_type= set_row(unit->types, item, row, &fake); + bool maybe_null_saved= maybe_null; + set_row(unit->types, row); + maybe_null= maybe_null_saved; } } diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 93171ad64a1..7b064bfe92c 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -142,6 +142,7 @@ public: longlong val_int (); String *val_str (String *); enum Item_result result_type() const; + enum_field_types field_type() const; void fix_length_and_dec(); uint cols(); @@ -273,6 +274,7 @@ protected: THD *thd; /* pointer to current THD */ Item_subselect *item; /* item, that use this engine */ enum Item_result res_type; /* type of results */ + enum_field_types res_field_type; /* column type of the results */ bool maybe_null; /* may be null (first item in select) */ public: @@ -282,6 +284,7 @@ public: result= res; item= si; res_type= STRING_RESULT; + res_field_type= FIELD_TYPE_VAR_STRING; maybe_null= 0; } virtual ~subselect_engine() {}; // to satisfy compiler @@ -296,6 +299,7 @@ public: virtual uint cols()= 0; /* return number of columnss in select */ virtual uint8 uncacheable()= 0; /* query is uncacheable */ enum Item_result type() { return res_type; } + enum_field_types field_type() { return res_field_type; } virtual void exclude()= 0; bool may_be_null() { return maybe_null; }; virtual table_map upper_select_const_tables()= 0; @@ -303,6 +307,9 @@ public: virtual void print(String *str)= 0; virtual int change_item(Item_subselect *si, select_subselect *result)= 0; virtual bool no_tables()= 0; + +protected: + void set_row(List &item_list, Item_cache **row); }; From 9c9bf046503323647a81e8e26aba1585bcbeebd0 Mon Sep 17 00:00:00 2001 From: "holyfoot/hf@mysql.com/deer.(none)" <> Date: Tue, 7 Nov 2006 21:02:41 +0400 Subject: [PATCH 23/65] bug fixed --- sql/field.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/field.cc b/sql/field.cc index a09c97fb356..61bbea5e615 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -5639,7 +5639,7 @@ int Field_datetime::store_time(TIME *ltime,timestamp_type type) (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | MODE_INVALID_DATES))), &error)) { - char buff[12]; + char buff[19]; String str(buff, sizeof(buff), &my_charset_latin1); make_datetime((DATE_TIME_FORMAT *) 0, ltime, &str); set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, From 26c30f2f7dbb0df7bd7caa226a4b0d9131c283eb Mon Sep 17 00:00:00 2001 From: "sergefp@mysql.com" <> Date: Wed, 8 Nov 2006 02:26:50 +0300 Subject: [PATCH 24/65] BUG#24056: Crash in subquery: Don't assume that condition that was pushed down into subquery has produced exactly one KEY_FIELD element - it could produce several or none at all, handle all of those cases. --- sql/sql_select.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sql/sql_select.cc b/sql/sql_select.cc index f92217302f8..cfc068cec86 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -2951,10 +2951,12 @@ add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level, join->unit->item->substype() == Item_subselect::IN_SUBS && !join->unit->first_select()->next_select()) { + KEY_FIELD *save= *key_fields; add_key_fields(join, key_fields, and_level, cond, usable_tables, sargables); // Indicate that this ref access candidate is for subquery lookup: - (*key_fields)[-1].outer_ref= TRUE; + for (; save != *key_fields; save++) + save->outer_ref= TRUE; } return; } From c7866bd569ccb0221c91ee206e4566876162f16f Mon Sep 17 00:00:00 2001 From: "gkodinov/kgeorge@macbook.gmz" <> Date: Wed, 8 Nov 2006 15:15:56 +0200 Subject: [PATCH 25/65] Make a new test target for autopush.pl to run memory based tests --- Makefile.am | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Makefile.am b/Makefile.am index 2aefbd05283..48f84269313 100644 --- a/Makefile.am +++ b/Makefile.am @@ -124,3 +124,10 @@ test-force-pl: cd mysql-test; \ ./mysql-test-run.pl --force && \ ./mysql-test-run.pl --ps-protocol --force + +#used by autopush.pl to run memory based tests +test-force-mem: + cd mysql-test; \ + ./mysql-test-run.pl --force --mem && \ + ./mysql-test-run.pl --ps-protocol --force --mem + From 620aee1ac80f94db1e6217a546f6c0c91c27b1f2 Mon Sep 17 00:00:00 2001 From: "holyfoot/hf@mysql.com/deer.(none)" <> Date: Wed, 8 Nov 2006 20:41:47 +0400 Subject: [PATCH 26/65] merging --- sql/field.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/field.cc b/sql/field.cc index 0af690608cc..802f3725f25 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -4926,7 +4926,7 @@ int Field_time::store_time(TIME *ltime, timestamp_type type) (ltime->minute * 100 + ltime->second); if (ltime->neg) tmp= -tmp; - return Field_time::store((longlong) tmp, TRUE); + return Field_time::store((longlong) tmp, FALSE); } From c56819e21605ad8a84876001e82db16702516b61 Mon Sep 17 00:00:00 2001 From: "holyfoot/hf@mysql.com/deer.(none)" <> Date: Thu, 9 Nov 2006 11:33:43 +0400 Subject: [PATCH 27/65] merging --- mysql-test/t/disabled.def | 1 + 1 file changed, 1 insertion(+) diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def index 8c4a76c78a9..44eec94f2db 100644 --- a/mysql-test/t/disabled.def +++ b/mysql-test/t/disabled.def @@ -44,3 +44,4 @@ rpl_multi_engine : BUG#22583 2006-09-23 lars #rpl_truncate_7ndb : BUG#21298 2006-07-27 msvensson ndb_binlog_discover : bug#21806 2006-08-24 ndb_autodiscover3 : bug#21806 +order_by : GKodinov - please fix bug #22457 in 5.1 also From 06dea508558594425a6e0b983d81b8b3255e9e9c Mon Sep 17 00:00:00 2001 From: "holyfoot/hf@mysql.com/deer.(none)" <> Date: Thu, 9 Nov 2006 12:41:15 +0400 Subject: [PATCH 28/65] merging --- mysql-test/r/subselect.result | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 0b72814797c..51d8227b9f3 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -3025,13 +3025,13 @@ FROM t1 WHERE a > '2000-01-01'; SHOW CREATE TABLE t2; Table Create Table t2 CREATE TABLE `t2` ( - `sub_a` datetime default NULL + `sub_a` datetime DEFAULT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 CREATE TABLE t3 AS (SELECT a FROM t1 WHERE a < '2000-01-01') UNION (SELECT a FROM t1 WHERE a > '2000-01-01'); SHOW CREATE TABLE t3; Table Create Table t3 CREATE TABLE `t3` ( - `a` datetime default NULL + `a` datetime DEFAULT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t1,t2,t3; create table t1 (df decimal(5,1)); From c40b292e84d4ac18e83105c99ae1d4273408ef48 Mon Sep 17 00:00:00 2001 From: "holyfoot/hf@mysql.com/deer.(none)" <> Date: Thu, 9 Nov 2006 12:49:39 +0400 Subject: [PATCH 29/65] merging --- mysql-test/r/subselect3.result | 32 ++++++++++++++++---------------- mysql-test/r/udf.result | 24 ++++++++++++------------ 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/mysql-test/r/subselect3.result b/mysql-test/r/subselect3.result index 5ab8e448b39..10cc2729f3e 100644 --- a/mysql-test/r/subselect3.result +++ b/mysql-test/r/subselect3.result @@ -26,9 +26,9 @@ NULL 2 NULL explain extended select a, oref, a in (select max(ie) from t1 where oref=t2.oref group by grp) from t2; -id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t2 ALL NULL NULL NULL NULL 5 -2 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 6 Using where; Using temporary; Using filesort +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 5 100.00 +2 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 6 100.00 Using where; Using temporary; Using filesort Warnings: Note 1276 Field or reference 't2.oref' of SELECT #2 was resolved in SELECT #1 Note 1003 select `test`.`t2`.`a` AS `a`,`test`.`t2`.`oref` AS `oref`,(`test`.`t2`.`a`,(select max(`test`.`t1`.`ie`) AS `max(ie)` from `test`.`t1` where (`test`.`t1`.`oref` = `test`.`t2`.`oref`) group by `test`.`t1`.`grp` having trigcond(((`test`.`t2`.`a`) = (max(`test`.`t1`.`ie`)))))) AS `a in (select max(ie) @@ -36,9 +36,9 @@ from t1 where oref=t2.oref group by grp)` from `test`.`t2` explain extended select a, oref from t2 where a in (select max(ie) from t1 where oref=t2.oref group by grp); -id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t2 ALL NULL NULL NULL NULL 5 Using where -2 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 6 Using where; Using temporary; Using filesort +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 5 100.00 Using where +2 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 6 100.00 Using where; Using temporary; Using filesort Warnings: Note 1276 Field or reference 't2.oref' of SELECT #2 was resolved in SELECT #1 Note 1003 select `test`.`t2`.`a` AS `a`,`test`.`t2`.`oref` AS `oref` from `test`.`t2` where (`test`.`t2`.`a`,(select max(`test`.`t1`.`ie`) AS `max(ie)` from `test`.`t1` where (`test`.`t1`.`oref` = `test`.`t2`.`oref`) group by `test`.`t1`.`grp` having ((`test`.`t2`.`a`) = (max(`test`.`t1`.`ie`))))) @@ -56,9 +56,9 @@ select ' ^ This must show 11' Z; Z ^ This must show 11 explain extended select a in (select max(ie) from t1 where oref=4 group by grp) from t3; -id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t3 ALL NULL NULL NULL NULL 2 -2 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 6 Using where; Using temporary; Using filesort +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL NULL NULL NULL NULL 2 100.00 +2 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 6 100.00 Using where; Using temporary; Using filesort Warnings: Note 1003 select (`test`.`t3`.`a`,(select max(`test`.`t1`.`ie`) AS `max(ie)` from `test`.`t1` where (`test`.`t1`.`oref` = 4) group by `test`.`t1`.`grp` having trigcond(((`test`.`t3`.`a`) = (max(`test`.`t1`.`ie`)))))) AS `a in (select max(ie) from t1 where oref=4 group by grp)` from `test`.`t3` drop table t1, t2, t3; @@ -79,9 +79,9 @@ oref a Z 4 NULL 0 explain extended select oref, a, a in (select a from t1 where oref=t2.oref) Z from t2; -id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t2 ALL NULL NULL NULL NULL 4 -2 DEPENDENT SUBQUERY t1 index_subquery a a 5 func 2 Using index; Using where +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 4 100.00 +2 DEPENDENT SUBQUERY t1 index_subquery a a 5 func 2 100.00 Using index; Using where Warnings: Note 1276 Field or reference 't2.oref' of SELECT #2 was resolved in SELECT #1 Note 1003 select `test`.`t2`.`oref` AS `oref`,`test`.`t2`.`a` AS `a`,(`test`.`t2`.`a`,(((`test`.`t2`.`a`) in t1 on a checking NULL where (`test`.`t1`.`oref` = `test`.`t2`.`oref`)))) AS `Z` from `test`.`t2` @@ -143,10 +143,10 @@ explain extended select a, oref, t3.a in (select t1.a from t1, t2 where t1.b=t2.a and t2.b=t3.oref) Z from t3; -id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t3 ALL NULL NULL NULL NULL 3 -2 DEPENDENT SUBQUERY t1 ref_or_null a a 5 func 4 Using where -2 DEPENDENT SUBQUERY t2 ref a a 5 test.t1.b 1 Using where +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL NULL NULL NULL NULL 3 100.00 +2 DEPENDENT SUBQUERY t1 ref_or_null a a 5 func 4 100.00 Using where +2 DEPENDENT SUBQUERY t2 ref a a 5 test.t1.b 1 100.00 Using where Warnings: Note 1276 Field or reference 't3.oref' of SELECT #2 was resolved in SELECT #1 Note 1003 select `test`.`t3`.`a` AS `a`,`test`.`t3`.`oref` AS `oref`,(`test`.`t3`.`a`,(select 1 AS `Not_used` from `test`.`t1` join `test`.`t2` where ((`test`.`t2`.`a` = `test`.`t1`.`b`) and (`test`.`t2`.`b` = `test`.`t3`.`oref`) and trigcond((((`test`.`t3`.`a`) = `test`.`t1`.`a`) or isnull(`test`.`t1`.`a`)))) having trigcond((`test`.`t1`.`a`)))) AS `Z` from `test`.`t3` diff --git a/mysql-test/r/udf.result b/mysql-test/r/udf.result index 30c335a12c7..16a1c73af80 100644 --- a/mysql-test/r/udf.result +++ b/mysql-test/r/udf.result @@ -118,13 +118,13 @@ myfunc_int(a AS attr_name) 1 2 EXPLAIN EXTENDED SELECT myfunc_int(a AS attr_name) FROM t1; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 2 +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 Warnings: Note 1003 select myfunc_int(`test`.`t1`.`a` AS `attr_name`) AS `myfunc_int(a AS attr_name)` from `test`.`t1` EXPLAIN EXTENDED SELECT myfunc_int(a) FROM t1; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 2 +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 Warnings: Note 1003 select myfunc_int(`test`.`t1`.`a` AS `a`) AS `myfunc_int(a)` from `test`.`t1` SELECT a,c FROM v1; @@ -146,23 +146,23 @@ c 1 2 EXPLAIN EXTENDED SELECT myfunc_int(MIN(b) xx) as c FROM t1 GROUP BY a; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 2 Using temporary; Using filesort +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 Using temporary; Using filesort Warnings: Note 1003 select myfunc_int(min(`test`.`t1`.`b`) AS `xx`) AS `c` from `test`.`t1` group by `test`.`t1`.`a` EXPLAIN EXTENDED SELECT test.fn(MIN(b)) as c FROM t1 GROUP BY a; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 2 Using temporary; Using filesort +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 Using temporary; Using filesort Warnings: Note 1003 select `test`.`fn`(min(`test`.`t1`.`b`)) AS `c` from `test`.`t1` group by `test`.`t1`.`a` EXPLAIN EXTENDED SELECT myfunc_int(fn(MIN(b))) as c FROM t1 GROUP BY a; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 2 Using temporary; Using filesort +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 Using temporary; Using filesort Warnings: Note 1003 select myfunc_int(`test`.`fn`(min(`test`.`t1`.`b`)) AS `fn(MIN(b))`) AS `c` from `test`.`t1` group by `test`.`t1`.`a` EXPLAIN EXTENDED SELECT myfunc_int(test.fn(MIN(b))) as c FROM t1 GROUP BY a; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 2 Using temporary; Using filesort +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 Using temporary; Using filesort Warnings: Note 1003 select myfunc_int(`test`.`fn`(min(`test`.`t1`.`b`)) AS `test.fn(MIN(b))`) AS `c` from `test`.`t1` group by `test`.`t1`.`a` SELECT myfunc_int(MIN(b) xx) as c FROM t1 GROUP BY a; From e7ff881c6bd23defd7d611a18a0a18bd58084055 Mon Sep 17 00:00:00 2001 From: "gkodinov/kgeorge@macbook.gmz" <> Date: Thu, 9 Nov 2006 16:55:42 +0200 Subject: [PATCH 30/65] Bug #20191: getTableName gives wrong or inconsistent result when using VIEWs When compiling GROUP BY Item_ref instances are dereferenced in setup_copy_fields(), i.e. replaced with the corresponding Item_field (if they point to one) or Item_copy_string for the other cases. Since the Item_ref (in the Item_field case) is no longer used the information about the aliases stored in it is lost. Fixed by preserving the column, table and DB alias on dereferencing Item_ref --- mysql-test/r/metadata.result | 34 ++++++++++++++++++++++++++++++++++ mysql-test/t/metadata.test | 19 +++++++++++++++++++ sql/item.cc | 4 ++++ sql/sql_select.cc | 11 +++++++++-- 4 files changed, 66 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/metadata.result b/mysql-test/r/metadata.result index 50b0b6ae294..34e961395c4 100644 --- a/mysql-test/r/metadata.result +++ b/mysql-test/r/metadata.result @@ -96,3 +96,37 @@ i 2 affected rows: 1 affected rows: 0 +create table t1 (id int(10)); +insert into t1 values (1); +CREATE VIEW v1 AS select t1.id as id from t1; +CREATE VIEW v2 AS select t1.id as renamed from t1; +CREATE VIEW v3 AS select t1.id + 12 as renamed from t1; +select * from v1 group by id limit 1; +Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr +def test t1 v1 id id 3 10 1 Y 32768 0 63 +id +1 +select * from v1 group by id limit 0; +Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr +def test t1 v1 id id 3 10 0 Y 32768 0 63 +id +select * from v1 where id=1000 group by id; +Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr +def test t1 v1 id id 3 10 0 Y 32768 0 63 +id +select * from v1 where id=1 group by id; +Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr +def test t1 v1 id id 3 10 1 Y 32768 0 63 +id +1 +select * from v2 where renamed=1 group by renamed; +Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr +def test t1 v2 id renamed 3 10 1 Y 32768 0 63 +renamed +1 +select * from v3 where renamed=1 group by renamed; +Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr +def v3 renamed 8 12 0 Y 32896 0 63 +renamed +drop table t1; +drop view v1,v2,v3; diff --git a/mysql-test/t/metadata.test b/mysql-test/t/metadata.test index 65338448555..a6ebfdc14c1 100644 --- a/mysql-test/t/metadata.test +++ b/mysql-test/t/metadata.test @@ -61,4 +61,23 @@ drop table t1;// delimiter ;// --disable_info +# +# Bug #20191: getTableName gives wrong or inconsistent result when using VIEWs +# +--enable_metadata +create table t1 (id int(10)); +insert into t1 values (1); +CREATE VIEW v1 AS select t1.id as id from t1; +CREATE VIEW v2 AS select t1.id as renamed from t1; +CREATE VIEW v3 AS select t1.id + 12 as renamed from t1; +select * from v1 group by id limit 1; +select * from v1 group by id limit 0; +select * from v1 where id=1000 group by id; +select * from v1 where id=1 group by id; +select * from v2 where renamed=1 group by renamed; +select * from v3 where renamed=1 group by renamed; +drop table t1; +drop view v1,v2,v3; +--disable_metadata + # End of 4.1 tests diff --git a/sql/item.cc b/sql/item.cc index dc92edd651d..76f0332b4ab 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -4202,6 +4202,10 @@ void Item_field::make_field(Send_field *tmp_field) DBUG_ASSERT(tmp_field->table_name != 0); if (name) tmp_field->col_name=name; // Use user supplied name + if (table_name) + tmp_field->table_name= table_name; + if (db_name) + tmp_field->db_name= db_name; } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index f92217302f8..db8091501d0 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -13594,9 +13594,16 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param, if (real_pos->type() == Item::FIELD_ITEM) { Item_field *item; - pos= real_pos; - if (!(item= new Item_field(thd, ((Item_field*) pos)))) + if (!(item= new Item_field(thd, ((Item_field*) real_pos)))) goto err; + if (pos->type() == Item::REF_ITEM) + { + /* preserve the names of the ref when dereferncing */ + Item_ref *ref= (Item_ref *) pos; + item->db_name= ref->db_name; + item->table_name= ref->table_name; + item->name= ref->name; + } pos= item; if (item->field->flags & BLOB_FLAG) { From 0357510dca406263d00a8bd1007e4f8784f658b8 Mon Sep 17 00:00:00 2001 From: "gkodinov/kgeorge@macbook.gmz" <> Date: Fri, 10 Nov 2006 12:43:44 +0200 Subject: [PATCH 31/65] fixed bad 5.0->5.1 merge --- mysql-test/t/disabled.def | 1 - sql/item.cc | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def index 44eec94f2db..8c4a76c78a9 100644 --- a/mysql-test/t/disabled.def +++ b/mysql-test/t/disabled.def @@ -44,4 +44,3 @@ rpl_multi_engine : BUG#22583 2006-09-23 lars #rpl_truncate_7ndb : BUG#21298 2006-07-27 msvensson ndb_binlog_discover : bug#21806 2006-08-24 ndb_autodiscover3 : bug#21806 -order_by : GKodinov - please fix bug #22457 in 5.1 also diff --git a/sql/item.cc b/sql/item.cc index 58c2cb94a19..ba289106f4f 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -3741,8 +3741,7 @@ bool Item_field::fix_fields(THD *thd, Item **reference) Item** res= find_item_in_list(this, thd->lex->current_select->item_list, &counter, REPORT_EXCEPT_NOT_FOUND, ¬_used); - if (res != (Item **)not_found_item && - (*res)->type() == Item::FIELD_ITEM) + if (res != (Item **)not_found_item) { if ((*res)->type() == Item::FIELD_ITEM) { From 9a41822bdd1486f2076302b6b1c6d88d66528583 Mon Sep 17 00:00:00 2001 From: "gkodinov/kgeorge@macbook.gmz" <> Date: Mon, 13 Nov 2006 12:28:55 +0200 Subject: [PATCH 32/65] Bug #19216: Client crashes on long SELECT The server sends a number of columns to the client. It uses a limited "fast" function for that instead of the general one. This fast function cannot send numbers larger than 2 bytes. This causes the client to expect smaller number of columns. The client writes outside of the allocated memory buffer as a result. Fixed the server to use the general function to send column count. Fixed the client to check the column count before writing column data. --- mysql-test/t/mysql_client.test | 18 ++++++++++++++++++ sql-common/client.c | 2 ++ sql/protocol.cc | 18 +++++++++--------- sql/protocol.h | 1 - 4 files changed, 29 insertions(+), 10 deletions(-) diff --git a/mysql-test/t/mysql_client.test b/mysql-test/t/mysql_client.test index b382357dacf..7bd7c762c5c 100644 --- a/mysql-test/t/mysql_client.test +++ b/mysql-test/t/mysql_client.test @@ -33,3 +33,21 @@ # --exec echo 'help' | $MYSQL > $MYSQLTEST_VARDIR/tmp/bug20328.tmp --exec echo 'help ' | $MYSQL > $MYSQLTEST_VARDIR/tmp/bug20328.tmp + +# +# Bug #19216: Client crashes on long SELECT +# +--exec echo "select" > $MYSQLTEST_VARDIR/tmp/b19216.tmp +# 3400 * 20 makes 68000 columns that is more than the max number that can fit +# in a 16 bit number. +let $i= 3400; +while ($i) +{ + --exec echo "'a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a'," >> $MYSQLTEST_VARDIR/tmp/b19216.tmp + dec $i; +} + +--exec echo "'b';" >> $MYSQLTEST_VARDIR/tmp/b19216.tmp +--disable_query_log +--exec $MYSQL < $MYSQLTEST_VARDIR/tmp/b19216.tmp >/dev/null +--enable_query_log diff --git a/sql-common/client.c b/sql-common/client.c index ff5f1ef150a..fb32eea33c7 100644 --- a/sql-common/client.c +++ b/sql-common/client.c @@ -1173,6 +1173,8 @@ unpack_fields(MYSQL_DATA *data,MEM_ROOT *alloc,uint fields, for (row=data->data; row ; row = row->next,field++) { uchar *pos; + /* fields count may be wrong */ + DBUG_ASSERT ((field - result) < fields); cli_fetch_lengths(&lengths[0], row->data, default_value ? 8 : 7); field->catalog = strdup_root(alloc,(char*) row->data[0]); field->db = strdup_root(alloc,(char*) row->data[1]); diff --git a/sql/protocol.cc b/sql/protocol.cc index a2287740f1e..7c7dfaf7bef 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -43,7 +43,7 @@ bool Protocol_prep::net_store_data(const char *from, uint length) packet->realloc(packet_length+9+length)) return 1; char *to=(char*) net_store_length((char*) packet->ptr()+packet_length, - (ulonglong) length); + length); memcpy(to,from,length); packet->length((uint) (to+length-packet->ptr())); return 0; @@ -297,8 +297,8 @@ send_ok(THD *thd, ha_rows affected_rows, ulonglong id, const char *message) DBUG_VOID_RETURN; buff[0]=0; // No fields - pos=net_store_length(buff+1,(ulonglong) affected_rows); - pos=net_store_length(pos, (ulonglong) id); + pos=net_store_length(buff+1,affected_rows); + pos=net_store_length(pos, id); if (thd->client_capabilities & CLIENT_PROTOCOL_41) { DBUG_PRINT("info", @@ -416,7 +416,7 @@ bool send_old_password_request(THD *thd) ulonglong for bigger numbers. */ -char *net_store_length(char *pkg, uint length) +static char *net_store_length_fast(char *pkg, uint length) { uchar *packet=(uchar*) pkg; if (length < 251) @@ -439,7 +439,7 @@ char *net_store_length(char *pkg, uint length) char *net_store_data(char *to,const char *from, uint length) { - to=net_store_length(to,length); + to=net_store_length_fast(to,length); memcpy(to,from,length); return to+length; } @@ -448,7 +448,7 @@ char *net_store_data(char *to,int32 from) { char buff[20]; uint length=(uint) (int10_to_str(from,buff,10)-buff); - to=net_store_length(to,length); + to=net_store_length_fast(to,length); memcpy(to,buff,length); return to+length; } @@ -457,7 +457,7 @@ char *net_store_data(char *to,longlong from) { char buff[22]; uint length=(uint) (longlong10_to_str(from,buff,10)-buff); - to=net_store_length(to,length); + to=net_store_length_fast(to,length); memcpy(to,buff,length); return to+length; } @@ -520,7 +520,7 @@ bool Protocol::send_fields(List *list, uint flag) if (flag & 1) { // Packet with number of elements - char *pos=net_store_length(buff, (uint) list->elements); + char *pos=net_store_length(buff, list->elements); (void) my_net_write(&thd->net, buff,(uint) (pos-buff)); } @@ -648,7 +648,7 @@ bool Protocol::send_records_num(List *list, ulonglong records) { char *pos; char buff[20]; - pos=net_store_length(buff, (uint) list->elements); + pos=net_store_length(buff, list->elements); pos=net_store_length(pos, records); return my_net_write(&thd->net, buff,(uint) (pos-buff)); } diff --git a/sql/protocol.h b/sql/protocol.h index 32d6acccddf..ce3adb41df5 100644 --- a/sql/protocol.h +++ b/sql/protocol.h @@ -177,7 +177,6 @@ void send_ok(THD *thd, ha_rows affected_rows=0L, ulonglong id=0L, const char *info=0); void send_eof(THD *thd, bool no_flush=0); bool send_old_password_request(THD *thd); -char *net_store_length(char *packet,uint length); char *net_store_data(char *to,const char *from, uint length); char *net_store_data(char *to,int32 from); char *net_store_data(char *to,longlong from); From cd7613b2658e917f959e8c344dd0eaf6060ed0f8 Mon Sep 17 00:00:00 2001 From: "gkodinov/kgeorge@macbook.gmz" <> Date: Mon, 13 Nov 2006 15:37:04 +0200 Subject: [PATCH 33/65] merge 4.1->5.0 of the test suite for bug 19216 --- mysql-test/t/mysql.test | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/mysql-test/t/mysql.test b/mysql-test/t/mysql.test index f3296e6f706..a1a4b40a3dd 100644 --- a/mysql-test/t/mysql.test +++ b/mysql-test/t/mysql.test @@ -142,6 +142,24 @@ drop table t1; --exec $MYSQL -e 'help ' > $MYSQLTEST_VARDIR/tmp/bug20328_2.result --exec diff $MYSQLTEST_VARDIR/tmp/bug20328_1.result $MYSQLTEST_VARDIR/tmp/bug20328_2.result +# +# Bug #19216: Client crashes on long SELECT +# +--exec echo "select" > $MYSQLTEST_VARDIR/tmp/b19216.tmp +# 3400 * 20 makes 68000 columns that is more than the max number that can fit +# in a 16 bit number. +let $i= 3400; +while ($i) +{ + --exec echo "'a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a'," >> $MYSQLTEST_VARDIR/tmp/b19216.tmp + dec $i; +} + +--exec echo "'b';" >> $MYSQLTEST_VARDIR/tmp/b19216.tmp +--disable_query_log +--exec $MYSQL < $MYSQLTEST_VARDIR/tmp/b19216.tmp >/dev/null +--enable_query_log + # # Bug #20103: Escaping with backslash does not work # From 5198354584e56682e5d5b87ee680582aac44ea61 Mon Sep 17 00:00:00 2001 From: "evgen@moonbone.local" <> Date: Tue, 14 Nov 2006 19:50:44 +0300 Subject: [PATCH 34/65] Bug#20045: Server crash on INSERT ... SELECT ... FROM non-mergeable view The regression is caused by the fix for bug 14767. When INSERT ... SELECT used a view in the SELECT list that was not inlined, and there was an active transaction, the server could crash in Query_cache::invalidate. On INSERT ... SELECT only the table being inserted into is invalidated. Thus views that can't be inlined are skipped from invalidation. The bug manifests itself in two ways so there is 2 test cases. One checks that the only the table being inserted into is invalidated. And the second one checks that there is no crash on INSERT ... SELECT. --- mysql-test/r/query_cache.result | 48 +++++++++++++++++++++++++++++++++ mysql-test/t/query_cache.test | 29 ++++++++++++++++++++ sql/sql_parse.cc | 4 +++ 3 files changed, 81 insertions(+) diff --git a/mysql-test/r/query_cache.result b/mysql-test/r/query_cache.result index 5224280e134..7645ad488cc 100644 --- a/mysql-test/r/query_cache.result +++ b/mysql-test/r/query_cache.result @@ -1274,3 +1274,51 @@ Variable_name Value Last_query_cost 0.000000 drop table t1; SET GLOBAL query_cache_size=0; +set global query_cache_size=1024*1024; +flush status; +create table t1 (a int); +insert into t1 (a) values (1), (2), (3); +select * from t1; +a +1 +2 +3 +show status like 'Qcache_hits'; +Variable_name Value +Qcache_hits 0 +select * from t1; +a +1 +2 +3 +show status like 'Qcache_hits'; +Variable_name Value +Qcache_hits 1 +create table t2 like t1; +select * from t1; +a +1 +2 +3 +show status like 'Qcache_hits'; +Variable_name Value +Qcache_hits 2 +insert into t2 select * from t1; +select * from t1; +a +1 +2 +3 +show status like 'Qcache_hits'; +Variable_name Value +Qcache_hits 3 +drop table t1, t2; +create table t1(c1 int); +create table t2(c1 int); +create table t3(c1 int); +create view v1 as select t3.c1 as c1 from t3,t2 where t3.c1 = t2.c1; +start transaction; +insert into t1(c1) select c1 from v1; +drop table t1, t2, t3; +drop view v1; +set global query_cache_size=0; diff --git a/mysql-test/t/query_cache.test b/mysql-test/t/query_cache.test index d416f34ce45..06eb76027b6 100644 --- a/mysql-test/t/query_cache.test +++ b/mysql-test/t/query_cache.test @@ -870,3 +870,32 @@ select * from t1 where a > 3; show status like 'last_query_cost'; drop table t1; SET GLOBAL query_cache_size=0; + +# +# Bug #20045: Server crash on INSERT ... SELECT ... FROM non-mergeable view +# +set global query_cache_size=1024*1024; +flush status; +create table t1 (a int); +insert into t1 (a) values (1), (2), (3); +select * from t1; +show status like 'Qcache_hits'; +select * from t1; +show status like 'Qcache_hits'; +create table t2 like t1; +select * from t1; +show status like 'Qcache_hits'; +insert into t2 select * from t1; +select * from t1; +show status like 'Qcache_hits'; +drop table t1, t2; + +create table t1(c1 int); +create table t2(c1 int); +create table t3(c1 int); +create view v1 as select t3.c1 as c1 from t3,t2 where t3.c1 = t2.c1; +start transaction; +insert into t1(c1) select c1 from v1; +drop table t1, t2, t3; +drop view v1; +set global query_cache_size=0; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index c62c286cfdb..191ba4d12fb 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3421,8 +3421,12 @@ end_with_restore_list: if (first_table->lock_type == TL_WRITE_CONCURRENT_INSERT && thd->lock) { + /* INSERT ... SELECT should invalidate only the very first table */ + TABLE_LIST *save_table= first_table->next_local; + first_table->next_local= 0; mysql_unlock_tables(thd, thd->lock); query_cache_invalidate3(thd, first_table, 1); + first_table->next_local= save_table; thd->lock=0; } delete result; From 65d6b24ed9d2aec30ae6584357ca5f916197399e Mon Sep 17 00:00:00 2001 From: "joerg@trift2." <> Date: Wed, 15 Nov 2006 20:27:02 +0100 Subject: [PATCH 35/65] support-files/mysql.spec.sh : Use "report features" in the first test run. --- support-files/mysql.spec.sh | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/support-files/mysql.spec.sh b/support-files/mysql.spec.sh index 1daac2d0e95..1547a5b578d 100644 --- a/support-files/mysql.spec.sh +++ b/support-files/mysql.spec.sh @@ -317,7 +317,10 @@ then cp -fp config.log "$MYSQL_MAXCONFLOG_DEST" fi -make -i test-force || true +( cd mysql-test + perl ./mysql-test-run.pl --force --report-features + perl ./mysql-test-run.pl --force --ps-protocol + true ) # Save mysqld-max ./libtool --mode=execute cp sql/mysqld sql/mysqld-max @@ -380,7 +383,10 @@ then cp -fp config.log "$MYSQL_CONFLOG_DEST" fi -make -i test-force || true +( cd mysql-test + perl ./mysql-test-run.pl --force --report-features + perl ./mysql-test-run.pl --force --ps-protocol + true ) %install RBR=$RPM_BUILD_ROOT @@ -716,6 +722,11 @@ fi # itself - note that they must be ordered by date (important when # merging BK trees) %changelog +* Wed Nov 15 2006 Joerg Bruehe + +- Switch from "make test*" to explicit calls of the test suite, + so that "report features" can be used. + * Tue Jun 27 2006 Joerg Bruehe - move "mysqldumpslow" from the client RPM to the server RPM (bug#20216) From 29dd8d2e588937545013fe42f46394cb19b5d533 Mon Sep 17 00:00:00 2001 From: "joerg@trift2." <> Date: Thu, 16 Nov 2006 14:01:31 +0100 Subject: [PATCH 36/65] support-files/mysql.spec.sh : Add an "Obsoletes" note relative to SuSE RPMs (bug#22081). --- support-files/mysql.spec.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/support-files/mysql.spec.sh b/support-files/mysql.spec.sh index 1547a5b578d..b14ffd24894 100644 --- a/support-files/mysql.spec.sh +++ b/support-files/mysql.spec.sh @@ -170,6 +170,8 @@ necessary to develop MySQL client applications. %package shared Summary: MySQL - Shared libraries Group: Applications/Databases +Provides: mysql-shared +Obsoletes: mysql-shared %description shared This package contains the shared libraries (*.so*) which certain @@ -722,6 +724,12 @@ fi # itself - note that they must be ordered by date (important when # merging BK trees) %changelog +* Thu Nov 16 2006 Joerg Bruehe + +- Explicitly note that the "MySQL-shared" RPMs (as built by MySQL AB) + replace "mysql-shared" (as distributed by SuSE) to allow easy upgrading + (bug#22081). + * Wed Nov 15 2006 Joerg Bruehe - Switch from "make test*" to explicit calls of the test suite, From 2963d8ddd2fcda1c014acd3d66b1870366fbf831 Mon Sep 17 00:00:00 2001 From: "holyfoot/hf@mysql.com/deer.(none)" <> Date: Thu, 16 Nov 2006 21:23:34 +0400 Subject: [PATCH 37/65] merging --- client/mysqltest.c | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/client/mysqltest.c b/client/mysqltest.c index f2f2dc85d72..b73ee831cf3 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -183,18 +183,6 @@ DYNAMIC_ARRAY q_lines; #include "sslopt-vars.h" -struct connection -{ - MYSQL mysql; - char *name; - - const char *cur_query; - int cur_query_len; - pthread_mutex_t mutex; - pthread_cond_t cond; - int query_done; -}; - struct { int read_lines,current_line; @@ -234,6 +222,12 @@ struct st_connection MYSQL* util_mysql; char *name; MYSQL_STMT* stmt; + + const char *cur_query; + int cur_query_len; + pthread_mutex_t mutex; + pthread_cond_t cond; + int query_done; }; struct st_connection connections[128]; struct st_connection* cur_con, *next_con, *connections_end; @@ -493,7 +487,7 @@ void handle_no_error(struct st_command*); */ pthread_handler_decl(send_one_query, arg) { - struct connection *cn= (struct connection*)arg; + struct st_connection *cn= (struct st_connection*)arg; mysql_thread_init(); VOID(mysql_send_query(&cn->mysql, cn->cur_query, cn->cur_query_len)); @@ -507,7 +501,7 @@ pthread_handler_decl(send_one_query, arg) return 0; } -static int do_send_query(struct connection *cn, const char *q, int q_len, +static int do_send_query(struct st_connection *cn, const char *q, int q_len, int flags) { pthread_t tid; @@ -4570,7 +4564,7 @@ int append_warnings(DYNAMIC_STRING *ds, MYSQL* mysql) error - function will not return */ -void run_query_normal(struct connection *cn, *mysql, struct st_command *command, +void run_query_normal(struct st_connection *cn, struct st_command *command, int flags, char *query, int query_len, DYNAMIC_STRING *ds, DYNAMIC_STRING *ds_warnings) { @@ -4598,7 +4592,7 @@ void run_query_normal(struct connection *cn, *mysql, struct st_command *command, Here we handle 'reap' command, so we need to check if the query's thread was finished and probably wait */ - else if (flags & QUERY_REAP) + else if (flags & QUERY_REAP_FLAG) { pthread_mutex_lock(&cn->mutex); while (!cn->query_done) @@ -5096,8 +5090,9 @@ int util_query(MYSQL* org_mysql, const char* query){ */ -void run_query(MYSQL *mysql, struct st_command *command, int flags) +void run_query(struct st_connection *cn, struct st_command *command, int flags) { + MYSQL *mysql= &cn->mysql; DYNAMIC_STRING *ds; DYNAMIC_STRING ds_result; DYNAMIC_STRING ds_warnings; @@ -5254,7 +5249,7 @@ void run_query(MYSQL *mysql, struct st_command *command, int flags) match_re(&ps_re, query)) run_query_stmt(mysql, command, query, query_len, ds, &ds_warnings); else - run_query_normal(mysql, command, flags, query, query_len, + run_query_normal(cn, command, flags, query, query_len, ds, &ds_warnings); if (sp_created) @@ -5746,7 +5741,7 @@ int main(int argc, char **argv) strmake(command->require_file, save_file, sizeof(save_file)); save_file[0]= 0; } - run_query(cur, command, flags); + run_query(cur_con, command, flags); command_executed++; command->last_argument= command->end; break; From e7cde7265138dcb6ded0a1309e331a52baaf44e5 Mon Sep 17 00:00:00 2001 From: "holyfoot/hf@mysql.com/deer.(none)" <> Date: Thu, 16 Nov 2006 23:00:48 +0400 Subject: [PATCH 38/65] merging --- Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.am b/Makefile.am index 48f84269313..12a867c1ad7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -126,7 +126,7 @@ test-force-pl: ./mysql-test-run.pl --ps-protocol --force #used by autopush.pl to run memory based tests -test-force-mem: +test-force-pl-mem: cd mysql-test; \ ./mysql-test-run.pl --force --mem && \ ./mysql-test-run.pl --ps-protocol --force --mem From 950665fc3bf538bc576f9e758c00f1306ed53864 Mon Sep 17 00:00:00 2001 From: "holyfoot/hf@mysql.com/deer.(none)" <> Date: Fri, 17 Nov 2006 11:15:40 +0400 Subject: [PATCH 39/65] merging --- sql/item_subselect.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index d2f8e092d00..551795acd53 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -1639,7 +1639,7 @@ void subselect_engine::set_row(List &item_list, Item_cache **row) res_field_type= sel_item->field_type(); item->decimals= sel_item->decimals; item->unsigned_flag= sel_item->unsigned_flag; - *maybe_null= sel_item->maybe_null; + maybe_null= sel_item->maybe_null; if (!(row[i]= Item_cache::get_cache(res_type))) return; row[i]->setup(sel_item); From dac2d0fcbaae370008175a992d1490c9169a3e89 Mon Sep 17 00:00:00 2001 From: "holyfoot/hf@mysql.com/deer.(none)" <> Date: Fri, 17 Nov 2006 12:02:36 +0400 Subject: [PATCH 40/65] merging --- mysql-test/r/order_by.result | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/mysql-test/r/order_by.result b/mysql-test/r/order_by.result index be29b310434..7b04c1acdc0 100644 --- a/mysql-test/r/order_by.result +++ b/mysql-test/r/order_by.result @@ -760,13 +760,6 @@ xxxxxxxxxxxxxxxxxxxaa xxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxz drop table t1; -create table t1 (a int not null, b int not null, c int not null); -insert t1 values (1,1,1),(1,1,2),(1,2,1); -select a, b from t1 group by a, b order by sum(c); -a b -1 2 -1 1 -drop table t1; create table t1 ( `sid` decimal(8,0) default null, `wnid` varchar(11) not null default '', @@ -881,6 +874,13 @@ num (select num + 2 FROM t1 LIMIT 1) SELECT a.a + 1 AS num FROM t1 a JOIN t1 b ON num = b.a; ERROR 42S22: Unknown column 'num' in 'on clause' DROP TABLE t1; +create table t1 (a int not null, b int not null, c int not null); +insert t1 values (1,1,1),(1,1,2),(1,2,1); +select a, b from t1 group by a, b order by sum(c); +a b +1 2 +1 1 +drop table t1; CREATE TABLE t1 (a int, b int, PRIMARY KEY (a)); INSERT INTO t1 VALUES (1,1), (2,2), (3,3); explain SELECT t1.b as a, t2.b as c FROM From 5906c3868fd5c989eb3deb6b25405af0b17fbccd Mon Sep 17 00:00:00 2001 From: "holyfoot/hf@mysql.com/deer.(none)" <> Date: Fri, 17 Nov 2006 14:30:08 +0400 Subject: [PATCH 41/65] ABI fix --- include/mysql_h.ic | 1 - 1 file changed, 1 deletion(-) diff --git a/include/mysql_h.ic b/include/mysql_h.ic index 30ef44a1ccb..44c36c84747 100644 --- a/include/mysql_h.ic +++ b/include/mysql_h.ic @@ -154,7 +154,6 @@ struct __attribute__((aligned(__alignof__(void *)), aligned(__alignof__(unsigned struct st_mysql_methods const * methods; void * thd; my_bool * unbuffered_fetch_owner; - struct st_mysql_stmt * current_stmt; }; # 571 "mysql.h" struct __attribute__((aligned(__alignof__(void *)), aligned(__alignof__(unsigned long int)))) st_mysql_bind From 0aee63517ea1a0725eaa9b79c1adda2594cf6480 Mon Sep 17 00:00:00 2001 From: "joerg@trift2." <> Date: Fri, 17 Nov 2006 16:09:37 +0100 Subject: [PATCH 42/65] Maintain the (old) VC project files, ehere yaSSL is concerned: "mySTL" has become a subdirectory of "taocrypt", was a sibling previously. --- extra/yassl/taocrypt/benchmark/benchmark.dsp | 4 ++-- extra/yassl/taocrypt/taocrypt.dsp | 4 ++-- extra/yassl/taocrypt/taocrypt.vcproj | 4 ++-- extra/yassl/taocrypt/test.dsp | 4 ++-- extra/yassl/testsuite/testsuite.dsp | 4 ++-- extra/yassl/yassl.dsp | 4 ++-- extra/yassl/yassl.vcproj | 4 ++-- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/extra/yassl/taocrypt/benchmark/benchmark.dsp b/extra/yassl/taocrypt/benchmark/benchmark.dsp index ed8fef316bb..878dc2b2783 100644 --- a/extra/yassl/taocrypt/benchmark/benchmark.dsp +++ b/extra/yassl/taocrypt/benchmark/benchmark.dsp @@ -42,7 +42,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\include" /I "..\..\mySTL" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\include" /I "..\mySTL" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe @@ -65,7 +65,7 @@ LINK32=link.exe # PROP Intermediate_Dir "Debug" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\include" /I "..\..\mySTL" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\include" /I "..\mySTL" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe diff --git a/extra/yassl/taocrypt/taocrypt.dsp b/extra/yassl/taocrypt/taocrypt.dsp index 19edf7b2f22..3f1b47990ad 100644 --- a/extra/yassl/taocrypt/taocrypt.dsp +++ b/extra/yassl/taocrypt/taocrypt.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "Release" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c -# ADD CPP /nologo /G6 /MT /W3 /O2 /I "include" /I "..\mySTL" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /G6 /MT /W3 /O2 /I "include" /I "mySTL" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe @@ -64,7 +64,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "Debug" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /Gm /ZI /Od /I "include" /I "..\mySTL" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /ZI /Od /I "include" /I "mySTL" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c # SUBTRACT CPP /Fr # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" diff --git a/extra/yassl/taocrypt/taocrypt.vcproj b/extra/yassl/taocrypt/taocrypt.vcproj index 7eef7b82db7..7ffcb664346 100755 --- a/extra/yassl/taocrypt/taocrypt.vcproj +++ b/extra/yassl/taocrypt/taocrypt.vcproj @@ -21,7 +21,7 @@ Date: Fri, 17 Nov 2006 16:52:21 +0100 Subject: [PATCH 43/65] MTR_BUILD_THREAD=auto selects a value for MTR_BUILD_THREAD from a pool (WL#2690) --- mysql-test/lib/mtr_unique.pl | 119 +++++++++++++++++++++++++++++++++++ mysql-test/mysql-test-run.pl | 10 +++ 2 files changed, 129 insertions(+) create mode 100644 mysql-test/lib/mtr_unique.pl diff --git a/mysql-test/lib/mtr_unique.pl b/mysql-test/lib/mtr_unique.pl new file mode 100644 index 00000000000..9bf86e26f4f --- /dev/null +++ b/mysql-test/lib/mtr_unique.pl @@ -0,0 +1,119 @@ +# +# This file is used from mysql-test-run.pl when choosing +# port numbers and directories to use for running mysqld. +# + +use strict; +use Fcntl ':flock'; + +# +# Requested IDs are stored in a hash and released upon END. +# +my %mtr_unique_assigned_ids = (); +END { + while(my ($id,$file) = each(%mtr_unique_assigned_ids)) { + print "Autoreleasing $file:$id\n"; + mtr_release_unique_id($file, $id); + } +} + +# +# Require a unique, numerical ID, given a file name (where all +# requested IDs are stored), a minimum and a maximum value. +# +# We use flock to implement locking for the ID file and ignore +# possible problems arising from lack of support for it on +# some platforms (it should work on most, and the possible +# race condition would occur rarely). The proper solution for +# this is a daemon that manages IDs, of course. +# +# If no unique ID within the specified parameters can be +# obtained, return undef. +# +sub mtr_require_unique_id($$$) { + my $file = shift; + my $min = shift; + my $max = shift; + my $ret = undef; + + chmod 0777, "$file.sem"; + open SEM, ">", "$file.sem" or die "can't write to $file.sem"; + flock SEM, LOCK_EX or die "can't lock $file.sem"; + if(! -e $file) { + open FILE, ">", $file or die "can't create $file"; + close FILE; + } + chmod 0777, $file; + open FILE, "+<", $file or die "can't open $file"; + select undef,undef,undef,0.2; + seek FILE, 0, 0; + my %taken = (); + while() { + chomp; + my ($id, $pid) = split / /; + $taken{$id} = $pid; + } + seek FILE, 0, 2; + for(my $i=$min; $i<=$max; ++$i) { + if(! exists $taken{$i}) { + print FILE "$i $$\n"; + $ret = $i; + last; + } + } + close FILE; + flock SEM, LOCK_UN or warn "can't unlock $file.sem"; + close SEM; + $mtr_unique_assigned_ids{$ret} = $file if defined $ret; + return $ret; +} + +# +# Require a unique ID like above, but sleep if no ID can be +# obtained immediately. +# +sub mtr_require_unique_id_and_wait($$$) { + my $ret = mtr_require_unique_id($_[0],$_[1],$_[2]); + while(! defined $ret) { + sleep 10; + $ret = mtr_require_unique_id($_[0],$_[1],$_[2]); + } + return $ret; +} + +# +# Release a unique ID. +# +sub mtr_release_unique_id($$) { + my $file = shift; + my $myid = shift; + + open SEM, ">", "$file.sem" or die "can't write to $file.sem"; + flock SEM, LOCK_EX or die "can't lock $file.sem"; + if(! -e $file) { + open FILE, ">", $file or die "can't create $file"; + close FILE; + } + open FILE, "+<", $file or die "can't open $file"; + select undef,undef,undef,0.2; + seek FILE, 0, 0; + my %taken = (); + while() { + chomp; + my ($id, $pid) = split / /; + $taken{$id} = $pid; + } + delete $taken{$myid}; + seek FILE, 0, 0; + truncate FILE, 0 or die "can't truncate $file"; + for my $k (keys %taken) { + print FILE $k . ' ' . $taken{$k} . "\n"; + } + close FILE; + flock SEM, LOCK_UN or warn "can't unlock $file.sem"; + close SEM; + delete $mtr_unique_assigned_ids{$myid}; +} + +1; + diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index ce9fd4344ea..f3981dc7c70 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -85,6 +85,7 @@ require "lib/mtr_diff.pl"; require "lib/mtr_match.pl"; require "lib/mtr_misc.pl"; require "lib/mtr_stress.pl"; +require "lib/mtr_unique.pl"; $Devel::Trace::TRACE= 1; @@ -449,6 +450,15 @@ sub initial_setup () { select(STDOUT); $| = 1; # Make unbuffered + # If so requested, we try to avail ourselves of a unique build thread number. + if ( $ENV{'MTR_BUILD_THREAD'} ) { + if ( lc($ENV{'MTR_BUILD_THREAD'}) eq 'auto' ) { + print "Requesting build thread... "; + $ENV{'MTR_BUILD_THREAD'} = mtr_require_unique_id_and_wait("/tmp/mysql-test-ports", 200, 299); + print "got ".$ENV{'MTR_BUILD_THREAD'}."\n"; + } + } + $glob_scriptname= basename($0); # We require that we are in the "mysql-test" directory From 40b22f39105af9e14b72bee2abd943d34bea822f Mon Sep 17 00:00:00 2001 From: "holyfoot/hf@mysql.com/deer.(none)" <> Date: Sat, 18 Nov 2006 21:49:59 +0400 Subject: [PATCH 44/65] merging --- mysql-test/t/disabled.def | 1 + sql/item_subselect.cc | 2 +- sql/sql_yacc.yy | 3 +-- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def index 0d3b7cdfdeb..63fa8440e43 100644 --- a/mysql-test/t/disabled.def +++ b/mysql-test/t/disabled.def @@ -32,3 +32,4 @@ rpl_multi_engine : BUG#22583 2006-09-23 lars #ndb_binlog_ddl_multi : BUG#18976 2006-04-10 kent CRBR: multiple binlog, second binlog may miss schema log events ndb_binlog_discover : bug#21806 2006-08-24 ndb_autodiscover3 : bug#21806 +udf : for GKodinov to fix. Your fix for #21809 stopped working in 5.1 after the merge. diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index bd0f4fc56c5..c0fa2c718bc 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -1682,7 +1682,7 @@ void subselect_engine::set_row(List &item_list, Item_cache **row) res_field_type= sel_item->field_type(); item->decimals= sel_item->decimals; item->unsigned_flag= sel_item->unsigned_flag; - *maybe_null= sel_item->maybe_null; + maybe_null= sel_item->maybe_null; if (!(row[i]= Item_cache::get_cache(res_type))) return; row[i]->setup(sel_item); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 4014192db02..28230fcf06d 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -798,7 +798,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type expr_list udf_expr_list udf_expr_list2 when_list ident_list ident_list_arg opt_expr_list - expr_list_opt %type option_type opt_var_type opt_var_ident_type @@ -6377,7 +6376,7 @@ function_call_generic: lex->current_select->udf_list.push_front(udf); #endif } - expr_list_opt ')' + opt_expr_list ')' { THD *thd= YYTHD; LEX *lex= Lex; From 2c82b5a97a2191124d8955d0c791992e650cf4fc Mon Sep 17 00:00:00 2001 From: "gkodinov/kgeorge@macbook.gmz" <> Date: Mon, 20 Nov 2006 12:46:47 +0200 Subject: [PATCH 45/65] 5.0-opt -> 5.1-opt merge fixed. --- mysql-test/t/disabled.def | 1 - sql/sql_yacc.yy | 7 +++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def index 63fa8440e43..0d3b7cdfdeb 100644 --- a/mysql-test/t/disabled.def +++ b/mysql-test/t/disabled.def @@ -32,4 +32,3 @@ rpl_multi_engine : BUG#22583 2006-09-23 lars #ndb_binlog_ddl_multi : BUG#18976 2006-04-10 kent CRBR: multiple binlog, second binlog may miss schema log events ndb_binlog_discover : bug#21806 2006-08-24 ndb_autodiscover3 : bug#21806 -udf : for GKodinov to fix. Your fix for #21809 stopped working in 5.1 after the merge. diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 28230fcf06d..26dc484059e 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -6372,11 +6372,10 @@ function_call_generic: } } /* Temporary placing the result of find_udf in $3 */ - $$= udf; lex->current_select->udf_list.push_front(udf); #endif } - opt_expr_list ')' + udf_expr_list ')' { THD *thd= YYTHD; LEX *lex= Lex; @@ -6401,7 +6400,7 @@ function_call_generic: { #ifdef HAVE_DLOPEN /* Retrieving the result of find_udf */ - udf_func *udf= $3; + udf_func *udf; LEX *lex= Lex; if (NULL != (udf= lex->current_select->udf_list.pop())) @@ -6427,7 +6426,7 @@ function_call_generic: YYABORT; } } - | ident '.' ident '(' udf_expr_list ')' + | ident '.' ident '(' opt_expr_list ')' { THD *thd= YYTHD; Create_qfunc *builder; From de08856720196fcbf6ab5bb05c4263f403fc7944 Mon Sep 17 00:00:00 2001 From: "gkodinov/kgeorge@macbook.gmz" <> Date: Mon, 20 Nov 2006 13:42:22 +0200 Subject: [PATCH 46/65] fix for the merge of --mem support in autopush.pl in 5.1-opt --- Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile.am b/Makefile.am index df5a81e77a9..c0798fbb5c7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -150,6 +150,7 @@ test-force-mem: test-pl: test test-full-pl: test-full test-force-pl: test-force +test-force-pl-mem: test-force-mem test-force-full-pl: test-force-full # Don't update the files from bitkeeper From 8809a5e27e4e2fa2e0fa0bf3359e750fae4128de Mon Sep 17 00:00:00 2001 From: "msvensson@neptunus.(none)" <> Date: Mon, 20 Nov 2006 13:02:49 +0100 Subject: [PATCH 47/65] mysql_client_test not found when running from binary dist Look in bin for myql_client_test executable --- mysql-test/mysql-test-run.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 74ec2a02c12..a1dfed2c57c 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -1499,7 +1499,7 @@ sub executable_setup () { $exe_mysql_client_test= mtr_exe_maybe_exists(vs_config_dirs('tests', 'mysql_client_test'), "$glob_basedir/tests/mysql_client_test", - "$glob_basedir/bin"); + "$glob_basedir/bin/mysql_client_test"); } } From ff38409128141c4ac9571ba927246d7cad956341 Mon Sep 17 00:00:00 2001 From: "gluh@mysql.com/gluh.(none)" <> Date: Tue, 21 Nov 2006 13:45:01 +0400 Subject: [PATCH 48/65] Bug#22413 EXPLAIN SELECT FROM view with ORDER BY yield server crash disable filling of I_S tables for EXPLAIN --- mysql-test/r/information_schema.result | 15 +++++++++++++++ mysql-test/t/information_schema.test | 14 ++++++++++++++ sql/sql_select.cc | 2 ++ 3 files changed, 31 insertions(+) diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result index 3fffce73aa9..43eedc19f12 100644 --- a/mysql-test/r/information_schema.result +++ b/mysql-test/r/information_schema.result @@ -1254,3 +1254,18 @@ COLUMN_NAME MD5(COLUMN_DEFAULT) LENGTH(COLUMN_DEFAULT) COLUMN_DEFAULT=get_value( fld1 7cf7a6782be951a1f2464a350da926a5 65532 1 DROP TABLE bug23037; DROP FUNCTION get_value; +create view v1 as +select table_schema as object_schema, +table_name as object_name, +table_type as object_type +from information_schema.tables +order by object_schema; +explain select * from v1; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY system NULL NULL NULL NULL 0 const row not found +2 DERIVED tables ALL NULL NULL NULL NULL 2 Using filesort +explain select * from (select table_name from information_schema.tables) as a; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY system NULL NULL NULL NULL 0 const row not found +2 DERIVED tables ALL NULL NULL NULL NULL 2 +drop view v1; diff --git a/mysql-test/t/information_schema.test b/mysql-test/t/information_schema.test index 27007bbe16a..dd203add344 100644 --- a/mysql-test/t/information_schema.test +++ b/mysql-test/t/information_schema.test @@ -973,4 +973,18 @@ DROP FUNCTION get_value; + +# +# Bug#22413: EXPLAIN SELECT FROM view with ORDER BY yield server crash +# +create view v1 as +select table_schema as object_schema, + table_name as object_name, + table_type as object_type +from information_schema.tables +order by object_schema; +explain select * from v1; +explain select * from (select table_name from information_schema.tables) as a; +drop view v1; + # End of 5.0 tests. diff --git a/sql/sql_select.cc b/sql/sql_select.cc index adb57e3e9f4..4d664edd5af 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1471,6 +1471,7 @@ JOIN::exec() curr_join->examined_rows= 0; if ((curr_join->select_lex->options & OPTION_SCHEMA_TABLE) && + !thd->lex->describe && get_schema_tables_result(curr_join)) { DBUG_VOID_RETURN; @@ -12278,6 +12279,7 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order, /* Fill schema tables with data before filesort if it's necessary */ if ((join->select_lex->options & OPTION_SCHEMA_TABLE) && + !thd->lex->describe && get_schema_tables_result(join)) goto err; From e37d08155bafd772c0d36115a2069ea4d1670b07 Mon Sep 17 00:00:00 2001 From: "gluh@mysql.com/gluh.(none)" <> Date: Tue, 21 Nov 2006 16:45:05 +0400 Subject: [PATCH 49/65] after merge fix --- mysql-test/r/information_schema.result | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result index 2caa11f245b..9db11883db4 100644 --- a/mysql-test/r/information_schema.result +++ b/mysql-test/r/information_schema.result @@ -1338,7 +1338,7 @@ order by object_schema; explain select * from v1; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY system NULL NULL NULL NULL 0 const row not found -2 DERIVED tables ALL NULL NULL NULL NULL 2 Using filesort +2 DERIVED tables ALL NULL NULL NULL NULL 0 Using filesort explain select * from (select table_name from information_schema.tables) as a; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY system NULL NULL NULL NULL 0 const row not found From 0421d489fbcdd1b9f3286f1981b7b3a275597b50 Mon Sep 17 00:00:00 2001 From: "sergefp@mysql.com" <> Date: Tue, 21 Nov 2006 21:52:39 +0300 Subject: [PATCH 50/65] Fix typo in comment --- sql/filesort.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/filesort.cc b/sql/filesort.cc index 12b17846fe4..6156d364b2b 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -974,7 +974,7 @@ uint read_to_buffer(IO_CACHE *fromfile, BUFFPEK *buffpek, Put all room used by freed buffer to use in adjacent buffer. Note, that we can't simply distribute memory evenly between all buffers, because new areas must not overlap with old ones. - SYNOPSYS + SYNOPSIS reuse_freed_buff() queue IN list of non-empty buffers, without freed buffer reuse IN empty buffer From 8e6cf24d3b766440fc506b12a7de63ff54441f70 Mon Sep 17 00:00:00 2001 From: "msvensson@neptunus.(none)" <> Date: Thu, 23 Nov 2006 09:56:50 +0100 Subject: [PATCH 51/65] Fix my_system_gmt_sec function declaration to be same as definition --- sql-common/my_time.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql-common/my_time.c b/sql-common/my_time.c index 2dd40c112de..003442e9330 100644 --- a/sql-common/my_time.c +++ b/sql-common/my_time.c @@ -780,7 +780,7 @@ long calc_daynr(uint year,uint month,uint day) */ my_time_t my_system_gmt_sec(const MYSQL_TIME *t_src, long *my_timezone, - bool *in_dst_time_gap) + my_bool *in_dst_time_gap) { uint loop; time_t tmp= 0; From 2aa0d085e35b0a2594d22c57b3402a3b686370c7 Mon Sep 17 00:00:00 2001 From: "msvensson@neptunus.(none)" <> Date: Thu, 23 Nov 2006 11:38:44 +0100 Subject: [PATCH 52/65] Build fixes for NetWare --- client/mysql_upgrade.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/client/mysql_upgrade.c b/client/mysql_upgrade.c index e2b5931c2eb..520d87b5ed7 100644 --- a/client/mysql_upgrade.c +++ b/client/mysql_upgrade.c @@ -373,22 +373,24 @@ static int comp_names(struct fileinfo *a, struct fileinfo *b) } -static int -find_file(const char *name, const char *root, uint flags, char *result, size_t len, ...) +static int find_file(const char *name, const char *root, + uint flags, char *result, size_t len, ...) { - int ret; + int ret= 1; va_list va; - FILEINFO key= { (char*)name, NULL }; const char *subdir; char *cp; - + FILEINFO key; + + /* Init key with name of the file to look for */ + key.name= (char*)name; + DBUG_ASSERT(root != NULL); cp= strmake(result, root, len); if (cp[-1] != FN_LIBCHAR) *cp++= FN_LIBCHAR; - ret= 1; va_start(va, len); subdir= (!(flags & MY_SEARCH_SELF)) ? va_arg(va, char *) : ""; while (subdir) From bc59087a9a97dc0c2aa83c93d0b6dc0039c3ae6e Mon Sep 17 00:00:00 2001 From: "msvensson@neptunus.(none)" <> Date: Thu, 23 Nov 2006 12:06:04 +0100 Subject: [PATCH 53/65] Add define for my_getpagesize to 8192(reworked in 5.0) --- include/my_sys.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/my_sys.h b/include/my_sys.h index ebb518314b2..65bfb0b7e6a 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -863,7 +863,12 @@ my_bool my_gethwaddr(uchar *to); #define HAVE_MMAP #endif +#ifndef __NETWARE__ int my_getpagesize(void); +#else +#define my_getpagesize() 8192 +#endif + void *my_mmap(void *, size_t, int, int, int, my_off_t); int my_munmap(void *, size_t); #endif From f1ec3372be7b3fa8bfdecfb666904888d7e533a7 Mon Sep 17 00:00:00 2001 From: "kent@mysql.com/c-634072d5.010-2112-6f72651.cust.bredbandsbolaget.se" <> Date: Thu, 23 Nov 2006 20:04:18 +0100 Subject: [PATCH 54/65] Many files: Adjuster project files to reflect move of "mySTL" directory --- extra/yassl/taocrypt/benchmark/benchmark.dsp | 4 ++-- extra/yassl/taocrypt/taocrypt.dsp | 4 ++-- extra/yassl/taocrypt/taocrypt.vcproj | 4 ++-- extra/yassl/taocrypt/test.dsp | 4 ++-- extra/yassl/testsuite/testsuite.dsp | 4 ++-- extra/yassl/yassl.dsp | 4 ++-- extra/yassl/yassl.vcproj | 4 ++-- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/extra/yassl/taocrypt/benchmark/benchmark.dsp b/extra/yassl/taocrypt/benchmark/benchmark.dsp index ed8fef316bb..878dc2b2783 100644 --- a/extra/yassl/taocrypt/benchmark/benchmark.dsp +++ b/extra/yassl/taocrypt/benchmark/benchmark.dsp @@ -42,7 +42,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\include" /I "..\..\mySTL" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\include" /I "..\mySTL" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe @@ -65,7 +65,7 @@ LINK32=link.exe # PROP Intermediate_Dir "Debug" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\include" /I "..\..\mySTL" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\include" /I "..\mySTL" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe diff --git a/extra/yassl/taocrypt/taocrypt.dsp b/extra/yassl/taocrypt/taocrypt.dsp index 19edf7b2f22..3f1b47990ad 100644 --- a/extra/yassl/taocrypt/taocrypt.dsp +++ b/extra/yassl/taocrypt/taocrypt.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "Release" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c -# ADD CPP /nologo /G6 /MT /W3 /O2 /I "include" /I "..\mySTL" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /G6 /MT /W3 /O2 /I "include" /I "mySTL" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe @@ -64,7 +64,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "Debug" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /Gm /ZI /Od /I "include" /I "..\mySTL" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /ZI /Od /I "include" /I "mySTL" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c # SUBTRACT CPP /Fr # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" diff --git a/extra/yassl/taocrypt/taocrypt.vcproj b/extra/yassl/taocrypt/taocrypt.vcproj index 7eef7b82db7..7ffcb664346 100755 --- a/extra/yassl/taocrypt/taocrypt.vcproj +++ b/extra/yassl/taocrypt/taocrypt.vcproj @@ -21,7 +21,7 @@ Date: Fri, 24 Nov 2006 15:36:04 +0100 Subject: [PATCH 55/65] fixes for mtr_unique.pl --- mysql-test/lib/mtr_unique.pl | 43 +++++++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/mysql-test/lib/mtr_unique.pl b/mysql-test/lib/mtr_unique.pl index 9bf86e26f4f..a8fb320c773 100644 --- a/mysql-test/lib/mtr_unique.pl +++ b/mysql-test/lib/mtr_unique.pl @@ -35,6 +35,13 @@ sub mtr_require_unique_id($$$) { my $min = shift; my $max = shift; my $ret = undef; + my $changed = 0; + + my $can_use_ps = `ps -e | grep '^[ ]*$$ '`; + + if(eval("readlink '$file'") || eval("readlink '$file.sem'")) { + die 'lock file is a symbolic link'; + } chmod 0777, "$file.sem"; open SEM, ">", "$file.sem" or die "can't write to $file.sem"; @@ -43,6 +50,11 @@ sub mtr_require_unique_id($$$) { open FILE, ">", $file or die "can't create $file"; close FILE; } + + if(eval("readlink '$file'") || eval("readlink '$file.sem'")) { + die 'lock file is a symbolic link'; + } + chmod 0777, $file; open FILE, "+<", $file or die "can't open $file"; select undef,undef,undef,0.2; @@ -52,15 +64,30 @@ sub mtr_require_unique_id($$$) { chomp; my ($id, $pid) = split / /; $taken{$id} = $pid; + if($can_use_ps) { + my $res = `ps -e | grep '^[ ]*$pid '`; + if(!$res) { + print "Ignoring slot $id used by missing process $pid.\n"; + delete $taken{$id}; + ++$changed; + } + } } - seek FILE, 0, 2; for(my $i=$min; $i<=$max; ++$i) { if(! exists $taken{$i}) { - print FILE "$i $$\n"; $ret = $i; + $taken{$i} = $$; + ++$changed; last; } } + if($changed) { + seek FILE, 0, 0; + truncate FILE, 0 or die "can't truncate $file"; + for my $k (keys %taken) { + print FILE $k . ' ' . $taken{$k} . "\n"; + } + } close FILE; flock SEM, LOCK_UN or warn "can't unlock $file.sem"; close SEM; @@ -75,8 +102,9 @@ sub mtr_require_unique_id($$$) { sub mtr_require_unique_id_and_wait($$$) { my $ret = mtr_require_unique_id($_[0],$_[1],$_[2]); while(! defined $ret) { - sleep 10; + sleep 30; $ret = mtr_require_unique_id($_[0],$_[1],$_[2]); + print "Waiting for unique id to become available...\n" unless $ret; } return $ret; } @@ -88,8 +116,17 @@ sub mtr_release_unique_id($$) { my $file = shift; my $myid = shift; + if(eval("readlink '$file'") || eval("readlink '$file.sem'")) { + die 'lock file is a symbolic link'; + } + open SEM, ">", "$file.sem" or die "can't write to $file.sem"; flock SEM, LOCK_EX or die "can't lock $file.sem"; + + if(eval("readlink '$file'") || eval("readlink '$file.sem'")) { + die 'lock file is a symbolic link'; + } + if(! -e $file) { open FILE, ">", $file or die "can't create $file"; close FILE; From 9d328d7d62774ba02dbe9e8b40ca023da9d5e0e7 Mon Sep 17 00:00:00 2001 From: "kent@mysql.com/c-634072d5.010-2112-6f72651.cust.bredbandsbolaget.se" <> Date: Fri, 24 Nov 2006 18:26:53 +0100 Subject: [PATCH 56/65] mysql-test-run.pl: Removed "use diagnostics", reduces Perl speed significantly. Can be enabled with "perl -Mdiagnostics mysql-test-run.pl". mtr_report.pl: Don't try output "skipped" comment if there is none (bug#24471) --- mysql-test/lib/mtr_report.pl | 6 +++++- mysql-test/mysql-test-run.pl | 1 - 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/mysql-test/lib/mtr_report.pl b/mysql-test/lib/mtr_report.pl index 8d7de9d1a4b..d0e836c1a90 100644 --- a/mysql-test/lib/mtr_report.pl +++ b/mysql-test/lib/mtr_report.pl @@ -89,10 +89,14 @@ sub mtr_report_test_skipped ($) { { print "[ disabled ] $tinfo->{'comment'}\n"; } - else + elsif ( $tinfo->{'comment'} ) { print "[ skipped ] $tinfo->{'comment'}\n"; } + else + { + print "[ skipped ]\n"; + } } sub mtr_report_tests_not_skipped_though_disabled ($) { diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 024d5c1c452..91be606a8bc 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -66,7 +66,6 @@ use IO::Socket::INET; use Data::Dumper; use strict; use warnings; -use diagnostics; select(STDOUT); $| = 1; # Automatically flush STDOUT From 6c85ba399d205ecbd87b7b512f9892bae28c4275 Mon Sep 17 00:00:00 2001 From: "kent@mysql.com/kent-amd64.(none)" <> Date: Mon, 27 Nov 2006 18:29:50 +0100 Subject: [PATCH 57/65] gen_rec.awk: Fix undefined behaviour. Many files: Reenabled build outside ource tree --- acinclude.m4 | 16 ++++++++-------- bdb/dist/gen_rec.awk | 2 +- configure.in | 6 ++++-- libmysql_r/Makefile.am | 10 +++++----- libmysqld/Makefile.am | 24 +++++++++++++++++++----- ndb/config/common.mk.am | 2 +- ndb/config/type_kernel.mk.am | 5 ++++- ndb/config/type_ndbapi.mk.am | 6 +++++- ndb/config/type_ndbapitest.mk.am | 5 ++++- ndb/config/type_ndbapitools.mk.am | 5 ++++- ndb/config/type_util.mk.am | 5 ++++- ndb/src/kernel/Makefile.am | 30 +++++++++++++++--------------- 12 files changed, 74 insertions(+), 42 deletions(-) diff --git a/acinclude.m4 b/acinclude.m4 index 0337b9de0cd..811e9c0b183 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -1448,20 +1448,20 @@ bdb_version_ok=yes ]) AC_DEFUN([MYSQL_TOP_BUILDDIR], [ + # Remove trailing "./" if any + [$1]=`echo $[$1] | sed -e 's,^\./,,'` case "$[$1]" in - /* ) ;; # don't do anything with an absolute path - "$srcdir"/* ) + "bdb" | "$srcdir/bdb" | "$top_srcdir/bdb" | "$abs_top_srcdir/bdb" ) # If BDB is under the source directory, we need to look under the # build directory for bdb/build_unix. - # NOTE: I'm being lazy, and assuming the user did not specify - # something like --with-berkeley-db=bdb (it would be missing "./"). - [$1]="\$(top_builddir)/"`echo "$[$1]" | sed -e "s,^$srcdir/,,"` + [$1]="\$(top_builddir)/bdb" ;; + /* ) ;; # Other absolute path is assume to be external BDB directory * ) AC_MSG_ERROR([The BDB directory must be directly under the MySQL source directory, or be specified using the full path. ('$srcdir'; '$[$1]')]) ;; esac - if test X"$[$1]" != "/" + if test X"$[$1]" != X"/" then [$1]=`echo $[$1] | sed -e 's,/$,,'` fi @@ -1493,7 +1493,7 @@ AC_DEFUN([MYSQL_CHECK_INNODB], [ AC_MSG_RESULT([Using Innodb]) AC_DEFINE([HAVE_INNOBASE_DB], [1], [Using Innobase DB]) have_innodb="yes" - innodb_includes="-I../innobase/include" + innodb_includes="-I\$(top_builddir)/innobase/include -I\$(top_srcdir)/innobase/include" innodb_system_libs="" dnl Some libs are listed several times, in order for gcc to sort out dnl circular references. @@ -1812,7 +1812,7 @@ AC_DEFUN([MYSQL_CHECK_NDBCLUSTER], [ AC_MSG_RESULT([Using NDB Cluster]) AC_DEFINE([HAVE_NDBCLUSTER_DB], [1], [Using Ndb Cluster DB]) have_ndbcluster="yes" - ndbcluster_includes="-I../ndb/include -I../ndb/include/ndbapi" + ndbcluster_includes="-I\$(top_builddir)/ndb/include -I\$(top_srcdir)/ndb/include -I\$(top_srcdir)/ndb/include/ndbapi" ndbcluster_libs="\$(top_builddir)/ndb/src/.libs/libndbclient.a" ndbcluster_system_libs="" ndb_mgmclient_libs="\$(top_builddir)/ndb/src/mgmclient/libndbmgmclient.la" diff --git a/bdb/dist/gen_rec.awk b/bdb/dist/gen_rec.awk index 75f2e86ca9e..e1b75699027 100644 --- a/bdb/dist/gen_rec.awk +++ b/bdb/dist/gen_rec.awk @@ -180,7 +180,7 @@ BEGIN { t = types[i]; if (modes[i] == "POINTER") { ndx = index(t, "*"); - t = substr(types[i], 0, ndx - 2); + t = substr(types[i], 1, ndx - 2); } printf("\t%s\t%s;\n", t, vars[i]) >> HFILE } diff --git a/configure.in b/configure.in index a8eed676756..4ac0637e42c 100644 --- a/configure.in +++ b/configure.in @@ -46,12 +46,14 @@ do case $host_os in netware* | modesto*) echo "$i/errmsg.sys: $i/errmsg.txt - \$(top_builddir)/extra/comp_err.linux -C\$(srcdir)/charsets/ $i/errmsg.txt $i/errmsg.sys" \ + mkdir -p $i + \$(top_builddir)/extra/comp_err.linux -C\$(srcdir)/charsets/ \$(srcdir)/$i/errmsg.txt $i/errmsg.sys" \ >> $AVAILABLE_LANGUAGES_ERRORS_RULES ;; *) echo "$i/errmsg.sys: $i/errmsg.txt - \$(top_builddir)/extra/comp_err -C\$(srcdir)/charsets/ $i/errmsg.txt $i/errmsg.sys" \ + mkdir -p $i + \$(top_builddir)/extra/comp_err -C\$(srcdir)/charsets/ \$(srcdir)/$i/errmsg.txt $i/errmsg.sys" \ >> $AVAILABLE_LANGUAGES_ERRORS_RULES ;; esac diff --git a/libmysql_r/Makefile.am b/libmysql_r/Makefile.am index f7cf00321cb..31f6fdffd53 100644 --- a/libmysql_r/Makefile.am +++ b/libmysql_r/Makefile.am @@ -31,8 +31,6 @@ INCLUDES = @MT_INCLUDES@ \ ## automake barfs if you don't use $(srcdir) or $(top_srcdir) in include include $(top_srcdir)/libmysql/Makefile.shared -libmysql_dir = $(top_srcdir)/libmysql - libmysqlclient_r_la_SOURCES = $(target_sources) libmysqlclient_r_la_LIBADD = $(target_libadd) libmysqlclient_r_la_LDFLAGS = $(target_ldflags) @@ -40,7 +38,9 @@ libmysqlclient_r_la_LDFLAGS = $(target_ldflags) # This is called from the toplevel makefile link_sources: set -x; \ - for f in `cd $(libmysql_dir) && echo *.[ch]`; do \ - rm -f $$f; \ - @LN_CP_F@ $(libmysql_dir)/$$f $$f; \ + for d in $(top_srcdir)/libmysql $(top_builddir)/libmysql; do \ + for f in `cd $$d && echo *.[ch]`; do \ + rm -f $$f; \ + @LN_CP_F@ $$d/$$f $$f; \ + done; \ done diff --git a/libmysqld/Makefile.am b/libmysqld/Makefile.am index e121e4b8d6e..9582084ba5d 100644 --- a/libmysqld/Makefile.am +++ b/libmysqld/Makefile.am @@ -25,9 +25,11 @@ DEFS = -DEMBEDDED_LIBRARY -DMYSQL_SERVER \ -DDEFAULT_MYSQL_HOME="\"$(MYSQLBASEdir)\"" \ -DDATADIR="\"$(MYSQLDATAdir)\"" \ -DSHAREDIR="\"$(MYSQLSHAREdir)\"" -INCLUDES= @MT_INCLUDES@ @bdb_includes@ \ +INCLUDES= @MT_INCLUDES@ @bdb_includes@ @innodb_includes@ @ndbcluster_includes@ \ -I$(top_builddir)/include -I$(top_srcdir)/include \ - -I$(top_srcdir)/sql -I$(top_srcdir)/sql/examples -I$(top_srcdir)/regex \ + -I$(top_builddir)/sql -I$(top_srcdir)/sql \ + -I$(top_srcdir)/sql/examples \ + -I$(top_srcdir)/regex \ $(openssl_includes) @ZLIB_INCLUDES@ noinst_LIBRARIES = libmysqld_int.a @@ -118,16 +120,28 @@ endif #libmysqld_la_LDFLAGS = -version-info @SHARED_LIB_VERSION@ #CLEANFILES = $(libmysqld_la_LIBADD) libmysqld.la -# This is called from the toplevel makefile +# This is called from the toplevel makefile. If we can link now +# to an existing file in source, we do that, else we assume it +# will show up in the build tree eventually (generated file). link_sources: set -x; \ for f in $(sqlsources); do \ rm -f $$f; \ - @LN_CP_F@ $(top_srcdir)/sql/$$f $$f; \ + if test -e $(top_srcdir)/sql/$$f ; \ + then \ + @LN_CP_F@ $(top_srcdir)/sql/$$f $$f; \ + else \ + @LN_CP_F@ $(top_builddir)/sql/$$f $$f; \ + fi ; \ done; \ for f in $(libmysqlsources); do \ rm -f $$f; \ - @LN_CP_F@ $(top_srcdir)/libmysql/$$f $$f; \ + if test -e $(top_srcdir)/libmysql/$$f ; \ + then \ + @LN_CP_F@ $(top_srcdir)/libmysql/$$f $$f; \ + else \ + @LN_CP_F@ $(top_builddir)/libmysql/$$f $$f; \ + fi ; \ done; \ for f in $(sqlexamplessources); do \ rm -f $$f; \ diff --git a/ndb/config/common.mk.am b/ndb/config/common.mk.am index 4df1b0e289a..d0b7e753285 100644 --- a/ndb/config/common.mk.am +++ b/ndb/config/common.mk.am @@ -7,6 +7,6 @@ ndbapiincludedir = "$(pkgincludedir)/ndb/ndbapi" mgmapiincludedir = "$(pkgincludedir)/ndb/mgmapi" INCLUDES = $(INCLUDES_LOC) -LDADD = $(LDADD_LOC) -L$(top_srcdir)/ndb/src/common/portlib -lmygcc +LDADD = $(LDADD_LOC) -L$(top_builddir)/ndb/src/common/portlib -lmygcc DEFS = @DEFS@ @NDB_DEFS@ $(DEFS_LOC) $(NDB_EXTRA_FLAGS) NDB_CXXFLAGS=@ndb_cxxflags_fix@ $(NDB_CXXFLAGS_LOC) diff --git a/ndb/config/type_kernel.mk.am b/ndb/config/type_kernel.mk.am index 703876ee2e9..ccb01709dfb 100644 --- a/ndb/config/type_kernel.mk.am +++ b/ndb/config/type_kernel.mk.am @@ -1,6 +1,9 @@ INCLUDES += \ - -I$(srcdir) -I$(top_srcdir)/include \ + -I$(srcdir) \ + -I$(top_builddir)/include \ + -I$(top_builddir)/ndb/include \ + -I$(top_srcdir)/include \ -I$(top_srcdir)/ndb/include \ -I$(top_srcdir)/ndb/src/kernel/vm \ -I$(top_srcdir)/ndb/src/kernel/error \ diff --git a/ndb/config/type_ndbapi.mk.am b/ndb/config/type_ndbapi.mk.am index ed648273aea..8b447bd693d 100644 --- a/ndb/config/type_ndbapi.mk.am +++ b/ndb/config/type_ndbapi.mk.am @@ -1,6 +1,10 @@ INCLUDES += \ - -I$(srcdir) -I$(top_srcdir)/include -I$(top_srcdir)/ndb/include \ + -I$(srcdir) \ + -I$(top_builddir)/include \ + -I$(top_builddir)/ndb/include \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/ndb/include \ -I$(top_srcdir)/ndb/include/kernel \ -I$(top_srcdir)/ndb/include/transporter \ -I$(top_srcdir)/ndb/include/debugger \ diff --git a/ndb/config/type_ndbapitest.mk.am b/ndb/config/type_ndbapitest.mk.am index 392c4e9fc70..e9a383eaad7 100644 --- a/ndb/config/type_ndbapitest.mk.am +++ b/ndb/config/type_ndbapitest.mk.am @@ -5,7 +5,10 @@ LDADD += $(top_builddir)/ndb/test/src/libNDBT.a \ $(top_builddir)/mysys/libmysys.a \ $(top_builddir)/strings/libmystrings.a @NDB_SCI_LIBS@ -INCLUDES += -I$(top_srcdir) -I$(top_srcdir)/include \ +INCLUDES += -I$(top_srcdir) \ + -I$(top_builddir)/include \ + -I$(top_builddir)/ndb/include \ + -I$(top_srcdir)/include \ -I$(top_srcdir)/ndb/include \ -I$(top_srcdir)/ndb/include/ndbapi \ -I$(top_srcdir)/ndb/include/util \ diff --git a/ndb/config/type_ndbapitools.mk.am b/ndb/config/type_ndbapitools.mk.am index 679dac09f47..1f4d93af618 100644 --- a/ndb/config/type_ndbapitools.mk.am +++ b/ndb/config/type_ndbapitools.mk.am @@ -5,7 +5,10 @@ LDADD += \ $(top_builddir)/mysys/libmysys.a \ $(top_builddir)/strings/libmystrings.a @NDB_SCI_LIBS@ -lmygcc -INCLUDES += -I$(srcdir) -I$(top_srcdir)/include \ +INCLUDES += -I$(srcdir) \ + -I$(top_builddir)/include \ + -I$(top_builddir)/ndb/include \ + -I$(top_srcdir)/include \ -I$(top_srcdir)/ndb/include \ -I$(top_srcdir)/ndb/include/ndbapi \ -I$(top_srcdir)/ndb/include/util \ diff --git a/ndb/config/type_util.mk.am b/ndb/config/type_util.mk.am index 0dfa77b7a7c..b92501faee9 100644 --- a/ndb/config/type_util.mk.am +++ b/ndb/config/type_util.mk.am @@ -1,5 +1,8 @@ -INCLUDES += -I$(srcdir) -I$(top_srcdir)/include \ +INCLUDES += -I$(srcdir) \ + -I$(top_builddir)/include \ + -I$(top_builddir)/ndb/include \ + -I$(top_srcdir)/include \ -I$(top_srcdir)/ndb/include \ -I$(top_srcdir)/ndb/include/util \ -I$(top_srcdir)/ndb/include/portlib \ diff --git a/ndb/src/kernel/Makefile.am b/ndb/src/kernel/Makefile.am index 5b55238c262..c9e590835db 100644 --- a/ndb/src/kernel/Makefile.am +++ b/ndb/src/kernel/Makefile.am @@ -9,21 +9,21 @@ ndbd_SOURCES = main.cpp SimBlockList.cpp include $(top_srcdir)/ndb/config/type_kernel.mk.am INCLUDES += \ - -Iblocks/cmvmi \ - -Iblocks/dbacc \ - -Iblocks/dbdict \ - -Iblocks/dbdih \ - -Iblocks/dblqh \ - -Iblocks/dbtc \ - -Iblocks/dbtup \ - -Iblocks/ndbfs \ - -Iblocks/ndbcntr \ - -Iblocks/qmgr \ - -Iblocks/trix \ - -Iblocks/backup \ - -Iblocks/dbutil \ - -Iblocks/suma \ - -Iblocks/dbtux + -I$(srcdir)/blocks/cmvmi \ + -I$(srcdir)/blocks/dbacc \ + -I$(srcdir)/blocks/dbdict \ + -I$(srcdir)/blocks/dbdih \ + -I$(srcdir)/blocks/dblqh \ + -I$(srcdir)/blocks/dbtc \ + -I$(srcdir)/blocks/dbtup \ + -I$(srcdir)/blocks/ndbfs \ + -I$(srcdir)/blocks/ndbcntr \ + -I$(srcdir)/blocks/qmgr \ + -I$(srcdir)/blocks/trix \ + -I$(srcdir)/blocks/backup \ + -I$(srcdir)/blocks/dbutil \ + -I$(srcdir)/blocks/suma \ + -I$(srcdir)/blocks/dbtux LDADD += \ blocks/cmvmi/libcmvmi.a \ From b7e107bf2d1a0ae6d96cda8efdba01ef4f92c8c4 Mon Sep 17 00:00:00 2001 From: "kent@mysql.com/kent-amd64.(none)" <> Date: Mon, 27 Nov 2006 19:04:57 +0100 Subject: [PATCH 58/65] Makefile.am: BSD compatibility --- Docs/Makefile.am | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Docs/Makefile.am b/Docs/Makefile.am index 685eaeef7d1..159dffddde8 100644 --- a/Docs/Makefile.am +++ b/Docs/Makefile.am @@ -40,22 +40,22 @@ CLEAN_FILES: $(TXT_FILES) GT = $(srcdir)/Support/generate-text-files.pl ../INSTALL-SOURCE: mysql.info $(GT) - perl -w $(GT) $< "installing-source" "windows-source-build" > $@ + perl -w $(GT) $(srcdir)/mysql.info "installing-source" "windows-source-build" > $@ ../INSTALL-WIN-SOURCE: mysql.info $(GT) - perl -w $(GT) $< "windows-source-build" "post-installation" > $@ + perl -w $(GT) $(srcdir)/mysql.info "windows-source-build" "post-installation" > $@ # We put the description for the binary installation here so that # people who download source wont have to see it. It is moved up to # the toplevel by the script that makes the binary tar files. INSTALL-BINARY: mysql.info $(GT) - perl -w $(GT) $< "installing-binary" "installing-source" > $@ + perl -w $(GT) $(srcdir)/mysql.info "installing-binary" "installing-source" > $@ ../EXCEPTIONS-CLIENT: mysql.info $(GT) - perl -w $(GT) $< "mysql-floss-license-exception" "function-index" > $@ + perl -w $(GT) $(srcdir)/mysql.info "mysql-floss-license-exception" "function-index" > $@ ../support-files/MacOSX/ReadMe.txt: mysql.info $(GT) - perl -w $(GT) $< "mac-os-x-installation" "netware-installation" > $@ + perl -w $(GT) $(srcdir)/mysql.info "mac-os-x-installation" "netware-installation" > $@ # Don't update the files from bitkeeper %::SCCS/s.% From 1b61612f8699d1468964e62af57849041ed0a085 Mon Sep 17 00:00:00 2001 From: "kent@mysql.com/kent-amd64.(none)" <> Date: Mon, 27 Nov 2006 21:15:25 +0100 Subject: [PATCH 59/65] ha_innodb.m4, Makefile.am, ha_ndbcluster.m4, Makefile.shared, ha_berkeley.m4: Reenabled build outside source tree --- config/ac-macros/ha_berkeley.m4 | 12 ++++++------ config/ac-macros/ha_innodb.m4 | 2 +- config/ac-macros/ha_ndbcluster.m4 | 2 +- extra/yassl/src/Makefile.am | 2 +- extra/yassl/taocrypt/benchmark/Makefile.am | 2 +- extra/yassl/taocrypt/src/Makefile.am | 2 +- extra/yassl/taocrypt/test/Makefile.am | 2 +- extra/yassl/testsuite/Makefile.am | 2 +- libmysql/Makefile.shared | 4 ++-- ndb/src/mgmsrv/Makefile.am | 2 +- 10 files changed, 16 insertions(+), 16 deletions(-) diff --git a/config/ac-macros/ha_berkeley.m4 b/config/ac-macros/ha_berkeley.m4 index 732c7730816..82786436720 100644 --- a/config/ac-macros/ha_berkeley.m4 +++ b/config/ac-macros/ha_berkeley.m4 @@ -243,20 +243,20 @@ bdb_version_ok=yes ]) AC_DEFUN([MYSQL_TOP_BUILDDIR], [ + # Remove trailing "./" if any + [$1]=`echo $[$1] | sed -e 's,^\./,,'` case "$[$1]" in - /* ) ;; # don't do anything with an absolute path - "$srcdir"/* ) + "bdb" | "$srcdir/bdb" | "$top_srcdir/bdb" | "$abs_top_srcdir/bdb" ) # If BDB is under the source directory, we need to look under the # build directory for bdb/build_unix. - # NOTE: I'm being lazy, and assuming the user did not specify - # something like --with-berkeley-db=bdb (it would be missing "./"). - [$1]="\$(top_builddir)/"`echo "$[$1]" | sed -e "s,^$srcdir/,,"` + [$1]="\$(top_builddir)/bdb" ;; + /* ) ;; # Other absolute path is assume to be external BDB directory * ) AC_MSG_ERROR([The BDB directory must be directly under the MySQL source directory, or be specified using the full path. ('$srcdir'; '$[$1]')]) ;; esac - if test X"$[$1]" != "/" + if test X"$[$1]" != X"/" then [$1]=`echo $[$1] | sed -e 's,/$,,'` fi diff --git a/config/ac-macros/ha_innodb.m4 b/config/ac-macros/ha_innodb.m4 index 17f0fab3e90..f670eeb9751 100644 --- a/config/ac-macros/ha_innodb.m4 +++ b/config/ac-macros/ha_innodb.m4 @@ -20,7 +20,7 @@ AC_DEFUN([MYSQL_CHECK_INNODB], [ AC_MSG_RESULT([Using Innodb]) AC_DEFINE([HAVE_INNOBASE_DB], [1], [Using Innobase DB]) have_innodb="yes" - innodb_includes="-I../innobase/include" + innodb_includes="-I\$(top_builddir)/innobase/include -I\$(top_srcdir)/innobase/include" innodb_system_libs="" dnl Some libs are listed several times, in order for gcc to sort out dnl circular references. diff --git a/config/ac-macros/ha_ndbcluster.m4 b/config/ac-macros/ha_ndbcluster.m4 index 2ff598242e4..8e953544df9 100644 --- a/config/ac-macros/ha_ndbcluster.m4 +++ b/config/ac-macros/ha_ndbcluster.m4 @@ -140,7 +140,7 @@ AC_DEFUN([MYSQL_CHECK_NDBCLUSTER], [ AC_MSG_RESULT([Using NDB Cluster]) AC_DEFINE([HAVE_NDBCLUSTER_DB], [1], [Using Ndb Cluster DB]) have_ndbcluster="yes" - ndbcluster_includes="-I../ndb/include -I../ndb/include/ndbapi -I../ndb/include/mgmapi" + ndbcluster_includes="-I\$(top_builddir)/ndb/include -I\$(top_srcdir)/ndb/include -I\$(top_srcdir)/ndb/include/ndbapi -I\$(top_srcdir)/ndb/include/mgmapi" ndbcluster_libs="\$(top_builddir)/ndb/src/.libs/libndbclient.a" ndbcluster_system_libs="" ndb_mgmclient_libs="\$(top_builddir)/ndb/src/mgmclient/libndbmgmclient.la" diff --git a/extra/yassl/src/Makefile.am b/extra/yassl/src/Makefile.am index 910bbbdd13f..bc57e7d05ba 100644 --- a/extra/yassl/src/Makefile.am +++ b/extra/yassl/src/Makefile.am @@ -1,4 +1,4 @@ -INCLUDES = -I../include -I../taocrypt/include -I../taocrypt/mySTL +INCLUDES = -I$(srcdir)/../include -I$(srcdir)/../taocrypt/include -I$(srcdir)/../taocrypt/mySTL noinst_LTLIBRARIES = libyassl.la libyassl_la_SOURCES = buffer.cpp cert_wrapper.cpp crypto_wrapper.cpp \ diff --git a/extra/yassl/taocrypt/benchmark/Makefile.am b/extra/yassl/taocrypt/benchmark/Makefile.am index a5b1713427c..674406d8ad6 100644 --- a/extra/yassl/taocrypt/benchmark/Makefile.am +++ b/extra/yassl/taocrypt/benchmark/Makefile.am @@ -1,4 +1,4 @@ -INCLUDES = -I../include -I../mySTL +INCLUDES = -I$(srcdir)/../include -I$(srcdir)/../mySTL bin_PROGRAMS = benchmark benchmark_SOURCES = benchmark.cpp benchmark_LDADD = $(top_builddir)/extra/yassl/taocrypt/src/libtaocrypt.la diff --git a/extra/yassl/taocrypt/src/Makefile.am b/extra/yassl/taocrypt/src/Makefile.am index 6d02a625275..61032d4c381 100644 --- a/extra/yassl/taocrypt/src/Makefile.am +++ b/extra/yassl/taocrypt/src/Makefile.am @@ -1,4 +1,4 @@ -INCLUDES = -I../include -I../mySTL +INCLUDES = -I$(srcdir)/../include -I$(srcdir)/../mySTL noinst_LTLIBRARIES = libtaocrypt.la diff --git a/extra/yassl/taocrypt/test/Makefile.am b/extra/yassl/taocrypt/test/Makefile.am index 988d00c7bef..25e1a98fc94 100644 --- a/extra/yassl/taocrypt/test/Makefile.am +++ b/extra/yassl/taocrypt/test/Makefile.am @@ -1,4 +1,4 @@ -INCLUDES = -I../include -I../mySTL +INCLUDES = -I$(srcdir)/../include -I$(srcdir)/../mySTL bin_PROGRAMS = test test_SOURCES = test.cpp test_LDADD = $(top_builddir)/extra/yassl/taocrypt/src/libtaocrypt.la diff --git a/extra/yassl/testsuite/Makefile.am b/extra/yassl/testsuite/Makefile.am index e8abffd6bb0..138077300f9 100644 --- a/extra/yassl/testsuite/Makefile.am +++ b/extra/yassl/testsuite/Makefile.am @@ -1,4 +1,4 @@ -INCLUDES = -I../include -I../taocrypt/include -I../taocrypt/mySTL +INCLUDES = -I$(srcdir)/../include -I$(srcdir)/../taocrypt/include -I$(srcdir)/../taocrypt/mySTL bin_PROGRAMS = testsuite testsuite_SOURCES = testsuite.cpp ../taocrypt/test/test.cpp \ ../examples/client/client.cpp ../examples/server/server.cpp \ diff --git a/libmysql/Makefile.shared b/libmysql/Makefile.shared index c2d98a81042..dc6d658fcdf 100644 --- a/libmysql/Makefile.shared +++ b/libmysql/Makefile.shared @@ -89,8 +89,8 @@ DEFS = -DDEFAULT_CHARSET_HOME="\"$(MYSQLBASEdir)\"" \ -DSHAREDIR="\"$(MYSQLSHAREdir)\"" $(target_defs) if HAVE_YASSL -yassl_las = $(top_srcdir)/extra/yassl/src/libyassl.la \ - $(top_srcdir)/extra/yassl/taocrypt/src/libtaocrypt.la +yassl_las = $(top_builddir)/extra/yassl/src/libyassl.la \ + $(top_builddir)/extra/yassl/taocrypt/src/libtaocrypt.la endif # The automatic dependencies miss this diff --git a/ndb/src/mgmsrv/Makefile.am b/ndb/src/mgmsrv/Makefile.am index 7fd3fa66b43..effad38be10 100644 --- a/ndb/src/mgmsrv/Makefile.am +++ b/ndb/src/mgmsrv/Makefile.am @@ -23,7 +23,7 @@ INCLUDES_LOC = -I$(top_srcdir)/ndb/src/ndbapi \ -I$(top_srcdir)/ndb/src/common/mgmcommon \ -I$(top_srcdir)/ndb/src/mgmclient -LDADD_LOC = $(top_srcdir)/ndb/src/mgmclient/CommandInterpreter.o \ +LDADD_LOC = $(top_builddir)/ndb/src/mgmclient/CommandInterpreter.o \ $(top_builddir)/ndb/src/libndbclient.la \ $(top_builddir)/dbug/libdbug.a \ $(top_builddir)/mysys/libmysys.a \ From b7e72c7ba917c9c445c14f0338032c2fc4351d72 Mon Sep 17 00:00:00 2001 From: "gkodinov@dl145s.mysql.com" <> Date: Tue, 28 Nov 2006 13:10:23 +0100 Subject: [PATCH 60/65] opt_range.cc: dummy commit to trigger pushbuild --- sql/opt_range.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 79b3e023a5f..b23c37704e0 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -11056,7 +11056,7 @@ void QUICK_GROUP_MIN_MAX_SELECT::dbug_dump(int indent, bool verbose) #endif /* NOT_USED */ /***************************************************************************** -** Instantiate templates +** Instantiate templates *****************************************************************************/ #ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION From 08e0e06eabd441b579face88fe7d80cd4639d223 Mon Sep 17 00:00:00 2001 From: "kent@mysql.com/kent-amd64.(none)" <> Date: Tue, 28 Nov 2006 18:04:10 +0100 Subject: [PATCH 61/65] Makefile.am: If using \$(srcdir)/mysql.info in action, use same in rule. --- Docs/Makefile.am | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Docs/Makefile.am b/Docs/Makefile.am index 159dffddde8..85a362133a1 100644 --- a/Docs/Makefile.am +++ b/Docs/Makefile.am @@ -26,7 +26,7 @@ all-local: $(TXT_FILES) # make sure that "make install" installs the info page, too # automake only seems to take care of this automatically, # if we're building the info page from texi directly. -install-data-hook: mysql.info +install-data-hook: $(srcdir)/mysql.info $(mkinstalldirs) $(DESTDIR)$(infodir) $(INSTALL_DATA) $(srcdir)/mysql.info $(DESTDIR)$(infodir) @@ -39,22 +39,22 @@ CLEAN_FILES: $(TXT_FILES) GT = $(srcdir)/Support/generate-text-files.pl -../INSTALL-SOURCE: mysql.info $(GT) +../INSTALL-SOURCE: $(srcdir)/mysql.info $(GT) perl -w $(GT) $(srcdir)/mysql.info "installing-source" "windows-source-build" > $@ -../INSTALL-WIN-SOURCE: mysql.info $(GT) +../INSTALL-WIN-SOURCE: $(srcdir)/mysql.info $(GT) perl -w $(GT) $(srcdir)/mysql.info "windows-source-build" "post-installation" > $@ # We put the description for the binary installation here so that # people who download source wont have to see it. It is moved up to # the toplevel by the script that makes the binary tar files. -INSTALL-BINARY: mysql.info $(GT) +INSTALL-BINARY: $(srcdir)/mysql.info $(GT) perl -w $(GT) $(srcdir)/mysql.info "installing-binary" "installing-source" > $@ -../EXCEPTIONS-CLIENT: mysql.info $(GT) +../EXCEPTIONS-CLIENT: $(srcdir)/mysql.info $(GT) perl -w $(GT) $(srcdir)/mysql.info "mysql-floss-license-exception" "function-index" > $@ -../support-files/MacOSX/ReadMe.txt: mysql.info $(GT) +../support-files/MacOSX/ReadMe.txt: $(srcdir)/mysql.info $(GT) perl -w $(GT) $(srcdir)/mysql.info "mac-os-x-installation" "netware-installation" > $@ # Don't update the files from bitkeeper From d3f73f27fa41bf40e55bf48686558c3d6c7e7039 Mon Sep 17 00:00:00 2001 From: "kent@mysql.com/kent-amd64.(none)" <> Date: Tue, 28 Nov 2006 18:08:30 +0100 Subject: [PATCH 62/65] Makefile.am: Handle the case "sql_yacc.cc" is pregenerated or not, and that the case where the source and build tree is the same or not. --- sql/Makefile.am | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sql/Makefile.am b/sql/Makefile.am index 6c685ba67c6..cbb87f16d80 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -148,12 +148,15 @@ mysql_tzinfo_to_sql.o: $(mysql_tzinfo_to_sql_SOURCES) sql_yacc.cc: sql_yacc.yy sql_yacc.h: sql_yacc.yy +# Be careful here, note that we use VPATH and might or might not have +# a pregenerated "sql_yacc.cc" in $(srcdir) or one we just built in +# $(builddir). And it has to work if $(srcdir) == $(builddir). sql_yacc.o: sql_yacc.cc sql_yacc.h $(HEADERS) - @SED@ -e 's/__attribute__ ((__unused__))//' sql_yacc.cc > sql_yacc.cc-new + @SED@ -e 's/__attribute__ ((__unused__))//' $< > sql_yacc.cc-new @MV@ sql_yacc.cc-new sql_yacc.cc @echo "Note: The following compile may take a long time." @echo "If it fails, re-run configure with --with-low-memory" - $(CXXCOMPILE) $(LM_CFLAGS) -c $< + $(CXXCOMPILE) $(LM_CFLAGS) -c sql_yacc.cc # This generates lex_hash.h # NOTE Built sources should depend on their sources not the tool From 648e86e10849f5d31c9592d6173ac8f1c2b453aa Mon Sep 17 00:00:00 2001 From: "joerg@trift2." <> Date: Tue, 28 Nov 2006 18:36:53 +0100 Subject: [PATCH 63/65] netware/BUILD/nwbootstrap : Editing "mwenv" is now obsolete and even plain wrong - drop it. --- netware/BUILD/nwbootstrap | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/netware/BUILD/nwbootstrap b/netware/BUILD/nwbootstrap index 48ff2a49667..e0c0b926619 100755 --- a/netware/BUILD/nwbootstrap +++ b/netware/BUILD/nwbootstrap @@ -25,7 +25,7 @@ revision="" rev="" build="" suffix="" -mwenv="" +#obsolete mwenv="" # show usage show_usage() @@ -151,14 +151,14 @@ echo "making files writable..." cd $target_dir chmod -R u+rw,g+rw . -# edit the mvenv file -echo "updating the mwenv environment file..." -mwenv="./netware/BUILD/mwenv" -mv -f $mwenv $mwenv.org -sed -e "s;WINE_BUILD_DIR;$wine_build_dir;g" \ - -e "s;BUILD_DIR;$build_dir;g" \ - -e "s;VERSION;$version;g" $mwenv.org > $mwenv -chmod +rwx $mwenv +#obsolete # edit the mvenv file +#obsolete echo "updating the mwenv environment file..." +#obsolete mwenv="./netware/BUILD/mwenv" +#obsolete mv -f $mwenv $mwenv.org +#obsolete sed -e "s;WINE_BUILD_DIR;$wine_build_dir;g" \ +#obsolete -e "s;BUILD_DIR;$build_dir;g" \ +#obsolete -e "s;VERSION;$version;g" $mwenv.org > $mwenv +#obsolete chmod +rwx $mwenv # edit the def file versions echo "updating *.def file versions..." From 1c46f7d841e9656d107a0885adec145c06d60589 Mon Sep 17 00:00:00 2001 From: "df@kahlann.erinye.com" <> Date: Wed, 29 Nov 2006 10:21:59 +0100 Subject: [PATCH 64/65] minor fix --- mysql-test/mysql-test-run.pl | 1 - 1 file changed, 1 deletion(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index f3981dc7c70..a734408e049 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -63,7 +63,6 @@ use Getopt::Long; use Sys::Hostname; use IO::Socket; use IO::Socket::INET; -use Data::Dumper; use strict; use warnings; use diagnostics; From ea5c7f975694cdf02393c67dff15a77a3590c309 Mon Sep 17 00:00:00 2001 From: "kent@mysql.com/kent-amd64.(none)" <> Date: Wed, 29 Nov 2006 18:36:51 +0100 Subject: [PATCH 65/65] Many files: Changed paths to ndb include directory to include "storage" Makefile.am: Adjusted path to yaSSL libtool libraries ssl.m4: Use libtool way of specifying yaSSL libraries --- config/ac-macros/ssl.m4 | 5 ++--- libmysqld/Makefile.am | 4 ++-- storage/ndb/config/type_kernel.mk.am | 2 +- storage/ndb/config/type_ndbapi.mk.am | 2 +- storage/ndb/config/type_ndbapitest.mk.am | 2 +- storage/ndb/config/type_ndbapitools.mk.am | 2 +- storage/ndb/config/type_util.mk.am | 2 +- 7 files changed, 9 insertions(+), 10 deletions(-) diff --git a/config/ac-macros/ssl.m4 b/config/ac-macros/ssl.m4 index 0f2f207c36f..4ee58318a62 100644 --- a/config/ac-macros/ssl.m4 +++ b/config/ac-macros/ssl.m4 @@ -28,10 +28,9 @@ AC_DEFUN([MYSQL_USE_BUNDLED_YASSL], [ yassl_dir="yassl" AC_SUBST([yassl_dir]) - yassl_libs="-L\$(top_srcdir)/extra/yassl/src -lyassl -L\$(top_srcdir)/extra/yassl/taocrypt/src -ltaocrypt" + yassl_libs="\$(top_builddir)/extra/yassl/src/libyassl.la \ + \$(top_builddir)/extra/yassl/taocrypt/src/libtaocrypt.la" AC_SUBST(yassl_libs) - yassl_includes="-I\$(top_srcdir)/extra/yassl/include" - AC_SUBST(yassl_includes) AC_DEFINE([HAVE_OPENSSL], [1], [Defined by configure. Using yaSSL for SSL.]) AC_DEFINE([HAVE_YASSL], [1], [Defined by configure. Using yaSSL for SSL.]) diff --git a/libmysqld/Makefile.am b/libmysqld/Makefile.am index 7eb160776e0..cf4f90d99c9 100644 --- a/libmysqld/Makefile.am +++ b/libmysqld/Makefile.am @@ -94,8 +94,8 @@ INC_LIB= $(top_builddir)/regex/libregex.a \ $(yassl_inc_libs) if HAVE_YASSL -yassl_inc_libs= $(top_srcdir)/extra/yassl/src/.libs/libyassl.a \ - $(top_srcdir)/extra/yassl/taocrypt/src/.libs/libtaocrypt.a +yassl_inc_libs= $(top_builddir)/extra/yassl/src/.libs/libyassl.a \ + $(top_builddir)/extra/yassl/taocrypt/src/.libs/libtaocrypt.a endif # Storage engine specific compilation options diff --git a/storage/ndb/config/type_kernel.mk.am b/storage/ndb/config/type_kernel.mk.am index 1b9d49d8088..c2a602d4e19 100644 --- a/storage/ndb/config/type_kernel.mk.am +++ b/storage/ndb/config/type_kernel.mk.am @@ -2,7 +2,7 @@ INCLUDES += \ -I$(srcdir) \ -I$(top_builddir)/include \ - -I$(top_builddir)/ndb/include \ + -I$(top_builddir)/storage/ndb/include \ -I$(top_srcdir)/include \ -I$(top_srcdir)/storage/ndb/include \ -I$(top_srcdir)/storage/ndb/src/kernel/vm \ diff --git a/storage/ndb/config/type_ndbapi.mk.am b/storage/ndb/config/type_ndbapi.mk.am index 9e1b83c0cad..258322d6d9a 100644 --- a/storage/ndb/config/type_ndbapi.mk.am +++ b/storage/ndb/config/type_ndbapi.mk.am @@ -2,7 +2,7 @@ INCLUDES += \ -I$(srcdir) \ -I$(top_builddir)/include \ - -I$(top_builddir)/ndb/include \ + -I$(top_builddir)/storage/ndb/include \ -I$(top_srcdir)/include \ -I$(top_srcdir)/mysys \ -I$(top_srcdir)/storage/ndb/include \ diff --git a/storage/ndb/config/type_ndbapitest.mk.am b/storage/ndb/config/type_ndbapitest.mk.am index e385e7bc07e..9a5a7d5a778 100644 --- a/storage/ndb/config/type_ndbapitest.mk.am +++ b/storage/ndb/config/type_ndbapitest.mk.am @@ -7,7 +7,7 @@ LDADD += $(top_builddir)/storage/ndb/test/src/libNDBT.a \ INCLUDES += -I$(top_srcdir) \ -I$(top_builddir)/include \ - -I$(top_builddir)/ndb/include \ + -I$(top_builddir)/storage/ndb/include \ -I$(top_srcdir)/include \ -I$(top_srcdir)/storage/ndb/include \ -I$(top_srcdir)/storage/ndb/include/ndbapi \ diff --git a/storage/ndb/config/type_ndbapitools.mk.am b/storage/ndb/config/type_ndbapitools.mk.am index 1bb8339a390..6003489f46e 100644 --- a/storage/ndb/config/type_ndbapitools.mk.am +++ b/storage/ndb/config/type_ndbapitools.mk.am @@ -7,7 +7,7 @@ LDADD += \ INCLUDES += -I$(srcdir) \ -I$(top_builddir)/include \ - -I$(top_builddir)/ndb/include \ + -I$(top_builddir)/storage/ndb/include \ -I$(top_srcdir)/include \ -I$(top_srcdir)/storage/ndb/include \ -I$(top_srcdir)/storage/ndb/include/ndbapi \ diff --git a/storage/ndb/config/type_util.mk.am b/storage/ndb/config/type_util.mk.am index 02ce2cfc969..2ce75a2ce31 100644 --- a/storage/ndb/config/type_util.mk.am +++ b/storage/ndb/config/type_util.mk.am @@ -1,7 +1,7 @@ INCLUDES += -I$(srcdir) \ -I$(top_builddir)/include \ - -I$(top_builddir)/ndb/include \ + -I$(top_builddir)/storage/ndb/include \ -I$(top_srcdir)/include \ -I$(top_srcdir)/mysys \ -I$(top_srcdir)/storage/ndb/include \