diff --git a/client/mysqltest.c b/client/mysqltest.c index bd4de026acb..8ddcfb90cad 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -2896,7 +2896,7 @@ static int run_query_stmt(MYSQL *mysql, struct st_query *q, int flags) /* Allocate array with bind structs, lengths and NULL flags */ bind= (MYSQL_BIND*) my_malloc(num_fields * sizeof(MYSQL_BIND), - MYF(MY_WME | MY_FAE)); + MYF(MY_WME | MY_FAE | MY_ZEROFILL)); length= (unsigned long*) my_malloc(num_fields * sizeof(unsigned long), MYF(MY_WME | MY_FAE)); is_null= (my_bool*) my_malloc(num_fields * sizeof(my_bool), diff --git a/include/queues.h b/include/queues.h index ac15b09719b..02ab768198e 100644 --- a/include/queues.h +++ b/include/queues.h @@ -53,6 +53,7 @@ int resize_queue(QUEUE *queue, uint max_elements); void delete_queue(QUEUE *queue); void queue_insert(QUEUE *queue,byte *element); byte *queue_remove(QUEUE *queue,uint idx); +#define queue_remove_all(queue) { (queue)->elements= 0; } void _downheap(QUEUE *queue,uint idx); void queue_fix(QUEUE *queue); #define is_queue_inited(queue) ((queue)->root != 0) diff --git a/innobase/include/lock0lock.h b/innobase/include/lock0lock.h index e533ac08545..710c945375c 100644 --- a/innobase/include/lock0lock.h +++ b/innobase/include/lock0lock.h @@ -612,6 +612,8 @@ extern lock_sys_t* lock_sys; #define LOCK_TABLE 16 /* these type values should be so high that */ #define LOCK_REC 32 /* they can be ORed to the lock mode */ #define LOCK_TABLE_EXP 80 /* explicit table lock (80 = 16 + 64) */ +#define LOCK_TABLE_TRANSACTIONAL 144 + /* transactional table lock (144 = 16 + 128)*/ #define LOCK_TYPE_MASK 0xF0UL /* mask used to extract lock type from the type_mode field in a lock */ /* Waiting lock flag */ diff --git a/innobase/include/trx0trx.h b/innobase/include/trx0trx.h index 8eb71dac763..e305226f9e3 100644 --- a/innobase/include/trx0trx.h +++ b/innobase/include/trx0trx.h @@ -464,6 +464,10 @@ struct trx_struct{ ulint n_lock_table_exp;/* number of explicit table locks (LOCK TABLES) reserved by the transaction, stored in trx_locks */ + ulint n_lock_table_transactional; + /* number of transactional table locks + (LOCK TABLES..WHERE ENGINE) reserved by + the transaction, stored in trx_locks */ UT_LIST_NODE_T(trx_t) trx_list; /* list of transactions */ UT_LIST_NODE_T(trx_t) diff --git a/innobase/lock/lock0lock.c b/innobase/lock/lock0lock.c index 70075019389..05466764063 100644 --- a/innobase/lock/lock0lock.c +++ b/innobase/lock/lock0lock.c @@ -2207,7 +2207,8 @@ lock_grant( release it at the end of the SQL statement */ lock->trx->auto_inc_lock = lock; - } else if (lock_get_type(lock) == LOCK_TABLE_EXP) { + } else if (lock_get_type(lock) == LOCK_TABLE_EXP || + lock_get_type(lock) == LOCK_TABLE_TRANSACTIONAL) { ut_a(lock_get_mode(lock) == LOCK_S || lock_get_mode(lock) == LOCK_X); } @@ -3421,6 +3422,10 @@ lock_table_create( lock->trx->n_lock_table_exp++; } + if (lock_get_type(lock) == LOCK_TABLE_TRANSACTIONAL) { + lock->trx->n_lock_table_transactional++; + } + lock->un_member.tab_lock.table = table; UT_LIST_ADD_LAST(un_member.tab_lock.locks, table->locks, lock); @@ -3458,7 +3463,11 @@ lock_table_remove_low( } if (lock_get_type(lock) == LOCK_TABLE_EXP) { - lock->trx->n_lock_table_exp--; + trx->n_lock_table_exp--; + } + + if (lock_get_type(lock) == LOCK_TABLE_TRANSACTIONAL) { + trx->n_lock_table_transactional--; } UT_LIST_REMOVE(trx_locks, trx->trx_locks, lock); @@ -3592,7 +3601,8 @@ lock_table( DB_DEADLOCK, or DB_QUE_THR_SUSPENDED */ ulint flags, /* in: if BTR_NO_LOCKING_FLAG bit is set, does nothing; - if LOCK_TABLE_EXP bits are set, + if LOCK_TABLE_EXP|LOCK_TABLE_TRANSACTIONAL + bits are set, creates an explicit table lock */ dict_table_t* table, /* in: database table in dictionary cache */ ulint mode, /* in: lock mode */ @@ -3608,7 +3618,8 @@ lock_table( return(DB_SUCCESS); } - ut_a(flags == 0 || flags == LOCK_TABLE_EXP); + ut_a(flags == 0 || flags == LOCK_TABLE_EXP || + flags == LOCK_TABLE_TRANSACTIONAL); trx = thr_get_trx(thr); @@ -3631,7 +3642,7 @@ lock_table( /* Another trx has a request on the table in an incompatible mode: this trx may have to wait */ - err = lock_table_enqueue_waiting(mode, table, thr); + err = lock_table_enqueue_waiting(mode | flags, table, thr); lock_mutex_exit_kernel(); @@ -3722,7 +3733,8 @@ lock_table_dequeue( ut_ad(mutex_own(&kernel_mutex)); #endif /* UNIV_SYNC_DEBUG */ ut_a(lock_get_type(in_lock) == LOCK_TABLE || - lock_get_type(in_lock) == LOCK_TABLE_EXP); + lock_get_type(in_lock) == LOCK_TABLE_EXP || + lock_get_type(in_lock) == LOCK_TABLE_TRANSACTIONAL); lock = UT_LIST_GET_NEXT(un_member.tab_lock.locks, in_lock); @@ -3826,7 +3838,9 @@ lock_release_off_kernel( } lock_table_dequeue(lock); - if (lock_get_type(lock) == LOCK_TABLE_EXP) { + + if (lock_get_type(lock) == LOCK_TABLE_EXP || + lock_get_type(lock) == LOCK_TABLE_TRANSACTIONAL) { ut_a(lock_get_mode(lock) == LOCK_S || lock_get_mode(lock) == LOCK_X); } @@ -3850,6 +3864,7 @@ lock_release_off_kernel( ut_a(trx->auto_inc_lock == NULL); ut_a(trx->n_lock_table_exp == 0); + ut_a(trx->n_lock_table_transactional == 0); } /************************************************************************* @@ -3915,6 +3930,7 @@ lock_release_tables_off_kernel( } ut_a(trx->n_lock_table_exp == 0); + ut_a(trx->n_lock_table_transactional == 0); } /************************************************************************* @@ -4028,11 +4044,15 @@ lock_table_print( ut_ad(mutex_own(&kernel_mutex)); #endif /* UNIV_SYNC_DEBUG */ ut_a(lock_get_type(lock) == LOCK_TABLE || - lock_get_type(lock) == LOCK_TABLE_EXP); + lock_get_type(lock) == LOCK_TABLE_EXP || + lock_get_type(lock) == LOCK_TABLE_TRANSACTIONAL); if (lock_get_type(lock) == LOCK_TABLE_EXP) { fputs("EXPLICIT ", file); + } else if (lock_get_type(lock) == LOCK_TABLE_TRANSACTIONAL) { + fputs("TRANSACTIONAL ", file); } + fputs("TABLE LOCK table ", file); ut_print_name(file, lock->trx, lock->un_member.tab_lock.table->name); fprintf(file, " trx id %lu %lu", @@ -4418,6 +4438,7 @@ lock_table_queue_validate( while (lock) { ut_a(((lock->trx)->conc_state == TRX_ACTIVE) + || ((lock->trx)->conc_state == TRX_PREPARED) || ((lock->trx)->conc_state == TRX_COMMITTED_IN_MEMORY)); if (!lock_get_wait(lock)) { @@ -4465,6 +4486,7 @@ lock_rec_queue_validate( while (lock) { ut_a(lock->trx->conc_state == TRX_ACTIVE + || lock->trx->conc_state == TRX_PREPARED || lock->trx->conc_state == TRX_COMMITTED_IN_MEMORY); @@ -4519,6 +4541,7 @@ lock_rec_queue_validate( while (lock) { ut_a(lock->trx->conc_state == TRX_ACTIVE + || lock->trx->conc_state == TRX_PREPARED || lock->trx->conc_state == TRX_COMMITTED_IN_MEMORY); ut_a(trx_in_trx_list(lock->trx)); @@ -4601,6 +4624,7 @@ loop: ut_a(trx_in_trx_list(lock->trx)); ut_a(lock->trx->conc_state == TRX_ACTIVE + || lock->trx->conc_state == TRX_PREPARED || lock->trx->conc_state == TRX_COMMITTED_IN_MEMORY); for (i = nth_bit; i < lock_rec_get_n_bits(lock); i++) { diff --git a/innobase/row/row0mysql.c b/innobase/row/row0mysql.c index 4a65cbff8b5..e959c28f6bb 100644 --- a/innobase/row/row0mysql.c +++ b/innobase/row/row0mysql.c @@ -784,7 +784,7 @@ row_lock_table_for_mysql( table handle */ dict_table_t* table, /* in: table to lock, or NULL if prebuilt->table should be - locked as LOCK_TABLE_EXP | + locked or a prebuilt->select_lock_type */ ulint mode) /* in: lock mode of table */ { @@ -822,8 +822,14 @@ run_again: if (table) { err = lock_table(0, table, mode, thr); } else { - err = lock_table(LOCK_TABLE_EXP, prebuilt->table, - prebuilt->select_lock_type, thr); + if (mode == LOCK_TABLE_TRANSACTIONAL) { + err = lock_table(LOCK_TABLE_TRANSACTIONAL, + prebuilt->table, + prebuilt->select_lock_type, thr); + } else { + err = lock_table(LOCK_TABLE_EXP, prebuilt->table, + prebuilt->select_lock_type, thr); + } } trx->error_state = err; diff --git a/innobase/trx/trx0trx.c b/innobase/trx/trx0trx.c index ab8bd898dd6..f676c21934b 100644 --- a/innobase/trx/trx0trx.c +++ b/innobase/trx/trx0trx.c @@ -153,6 +153,7 @@ trx_create( trx->auto_inc_lock = NULL; trx->n_lock_table_exp = 0; + trx->n_lock_table_transactional = 0; trx->read_view_heap = mem_heap_create(256); trx->read_view = NULL; @@ -285,6 +286,7 @@ trx_free( ut_a(!trx->has_search_latch); ut_a(!trx->auto_inc_lock); ut_a(!trx->n_lock_table_exp); + ut_a(!trx->n_lock_table_transactional); ut_a(trx->dict_operation_lock_mode == 0); @@ -1645,10 +1647,15 @@ trx_print( putc('\n', f); if (trx->n_mysql_tables_in_use > 0 || trx->mysql_n_tables_locked > 0) { + fprintf(f, "mysql tables in use %lu, locked %lu\n", + (ulong) trx->n_mysql_tables_in_use, + (ulong) trx->mysql_n_tables_locked); + } - fprintf(f, "mysql tables in use %lu, locked %lu\n", - (ulong) trx->n_mysql_tables_in_use, - (ulong) trx->mysql_n_tables_locked); + if (trx->n_lock_table_transactional > 0 || trx->n_lock_table_exp > 0) { +fprintf(f, "mysql explicit table locks %lu, transactional table locks %lu\n", + (ulong) trx->n_lock_table_exp, + (ulong) trx->n_lock_table_transactional); } newline = TRUE; diff --git a/mysql-test/r/derived.result b/mysql-test/r/derived.result index ddd8f82de2c..da2bb9081de 100644 --- a/mysql-test/r/derived.result +++ b/mysql-test/r/derived.result @@ -330,3 +330,8 @@ SELECT MIN(price) min, MAX(price) max, AVG(price) avg FROM (SELECT SUBSTRING( MA min max avg 10.00 10.00 10 DROP TABLE t1; +CREATE TABLE t1 (a char(10), b char(10)); +INSERT INTO t1 VALUES ('root','localhost'), ('root','%'); +SELECT * FROM (SELECT (SELECT a.a FROM t1 AS a WHERE a.a = b.a) FROM t1 AS b) AS c; +ERROR 21000: Subquery returns more than 1 row +DROP TABLE t1; diff --git a/mysql-test/r/index_merge.result b/mysql-test/r/index_merge.result index 6b41992a733..4ccec5ef0d8 100644 --- a/mysql-test/r/index_merge.result +++ b/mysql-test/r/index_merge.result @@ -1,4 +1,4 @@ -drop table if exists t0, t1, t2, t3,t4; +drop table if exists t0, t1, t2, t3, t4; create table t0 ( key1 int not null, @@ -335,4 +335,55 @@ key1 key2 key3 key4 key5 key6 key7 key8 select count(*) from t0; count(*) 1021 +drop table t4; +create table t4 (a int); +insert into t4 values (1),(4),(3); +set @save_join_buffer_size=@@join_buffer_size; +set join_buffer_size= 4000; +show variables like 'join_buffer_size'; +Variable_name Value +join_buffer_size 8228 +explain select max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) +from t0 as A force index(i1,i2), t0 as B force index (i1,i2) +where (A.key1 < 500000 or A.key2 < 3) +and (B.key1 < 500000 or B.key2 < 3); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE A index_merge i1,i2 i1,i2 4,4 NULL 1016 Using sort_union(i1,i2); Using where +1 SIMPLE B index_merge i1,i2 i1,i2 4,4 NULL 1016 Using sort_union(i1,i2); Using where +select max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) +from t0 as A force index(i1,i2), t0 as B force index (i1,i2) +where (A.key1 < 500000 or A.key2 < 3) +and (B.key1 < 500000 or B.key2 < 3); +max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) +10240 +update t0 set key1=1; +explain select max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) +from t0 as A force index(i1,i2), t0 as B force index (i1,i2) +where (A.key1 = 1 or A.key2 = 1) +and (B.key1 = 1 or B.key2 = 1); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE A index_merge i1,i2 i1,i2 4,4 NULL 1020 Using union(i1,i2); Using where +1 SIMPLE B index_merge i1,i2 i1,i2 4,4 NULL 1020 Using union(i1,i2); Using where +select max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) +from t0 as A force index(i1,i2), t0 as B force index (i1,i2) +where (A.key1 = 1 or A.key2 = 1) +and (B.key1 = 1 or B.key2 = 1); +max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) +8194 +alter table t0 add filler1 char(200), add filler2 char(200), add filler3 char(200); +update t0 set key2=1, key3=1, key4=1, key5=1,key6=1,key7=1 where key7 < 500; +explain select max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) +from t0 as A, t0 as B +where (A.key1 = 1 and A.key2 = 1 and A.key3 = 1 and A.key4=1 and A.key5=1 and A.key6=1 and A.key7 = 1 or A.key8=1) +and (B.key1 = 1 and B.key2 = 1 and B.key3 = 1 and B.key4=1 and B.key5=1 and B.key6=1 and B.key7 = 1 or B.key8=1); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE A index_merge i1,i2,i3,i4,i5,i6,i7,i8 i2,i3,i4,i5,i6,i8 4,4,4,4,4,4 NULL 16 Using union(intersect(i2,i3,i4,i5,i6),i8); Using where +1 SIMPLE B index_merge i1,i2,i3,i4,i5,i6,i7,i8 i2,i3,i4,i5,i6,i8 4,4,4,4,4,4 NULL 16 Using union(intersect(i2,i3,i4,i5,i6),i8); Using where +select max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) +from t0 as A, t0 as B +where (A.key1 = 1 and A.key2 = 1 and A.key3 = 1 and A.key4=1 and A.key5=1 and A.key6=1 and A.key7 = 1 or A.key8=1) +and (B.key1 = 1 and B.key2 = 1 and B.key3 = 1 and B.key4=1 and B.key5=1 and B.key6=1 and B.key7 = 1 or B.key8=1); +max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) +8186 +set join_buffer_size= @save_join_buffer_size; drop table t0, t1, t2, t3, t4; diff --git a/mysql-test/r/join_outer.result b/mysql-test/r/join_outer.result index 2f420905195..b7343065c73 100644 --- a/mysql-test/r/join_outer.result +++ b/mysql-test/r/join_outer.result @@ -853,4 +853,19 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 const PRIMARY PRIMARY 4 const 1 Using index 1 SIMPLE t2 ALL NULL NULL NULL NULL 2 1 SIMPLE t3 ALL NULL NULL NULL NULL 2 +drop table t1,t2; +create table t1 (a int, b int); +insert into t1 values (1,1),(2,2),(3,3); +create table t2 (a int, b int); +insert into t2 values (1,1), (2,2); +select * from t2 right join t1 on t2.a=t1.a; +a b a b +1 1 1 1 +2 2 2 2 +NULL NULL 3 3 +select straight_join * from t2 right join t1 on t2.a=t1.a; +a b a b +1 1 1 1 +2 2 2 2 +NULL NULL 3 3 DROP TABLE t0,t1,t2,t3; diff --git a/mysql-test/t/derived.test b/mysql-test/t/derived.test index 64e3fe8929b..d204947b1fa 100644 --- a/mysql-test/t/derived.test +++ b/mysql-test/t/derived.test @@ -214,3 +214,16 @@ CREATE TABLE `t1` ( `itemid` int(11) NOT NULL default '0', `grpid` varchar(15) N insert into t1 values (128, 'rozn', 2, now(), 10),(128, 'rozn', 1, now(), 10); SELECT MIN(price) min, MAX(price) max, AVG(price) avg FROM (SELECT SUBSTRING( MAX(concat(date_,";",price)), 12) price FROM t1 WHERE itemid=128 AND grpid='rozn' GROUP BY itemid, grpid, vendor) lastprices; DROP TABLE t1; + +# +# Test for bug #7413 "Subquery with non-scalar results participating in +# select list of derived table crashes server" aka "VIEW with sub query can +# cause the MySQL server to crash". If we have encountered problem during +# filling of derived table we should report error and perform cleanup +# properly. +# +CREATE TABLE t1 (a char(10), b char(10)); +INSERT INTO t1 VALUES ('root','localhost'), ('root','%'); +--error 1242 +SELECT * FROM (SELECT (SELECT a.a FROM t1 AS a WHERE a.a = b.a) FROM t1 AS b) AS c; +DROP TABLE t1; diff --git a/mysql-test/t/index_merge.test b/mysql-test/t/index_merge.test index a4f7b71e5a3..3ab68d85125 100644 --- a/mysql-test/t/index_merge.test +++ b/mysql-test/t/index_merge.test @@ -1,9 +1,8 @@ # # Index merge tests # - --disable_warnings -drop table if exists t0, t1, t2, t3,t4; +drop table if exists t0, t1, t2, t3, t4; --enable_warnings # Create and fill a table with simple keys @@ -278,4 +277,48 @@ delete from t0 where key1 < 3 or key2 < 4; select * from t0 where key1 < 3 or key2 < 4; select count(*) from t0; -drop table t0, t1, t2, t3, t4; +# Test for BUG#4177 +drop table t4; +create table t4 (a int); +insert into t4 values (1),(4),(3); +set @save_join_buffer_size=@@join_buffer_size; +set join_buffer_size= 4000; +show variables like 'join_buffer_size'; +explain select max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) + from t0 as A force index(i1,i2), t0 as B force index (i1,i2) + where (A.key1 < 500000 or A.key2 < 3) + and (B.key1 < 500000 or B.key2 < 3); + +select max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) + from t0 as A force index(i1,i2), t0 as B force index (i1,i2) + where (A.key1 < 500000 or A.key2 < 3) + and (B.key1 < 500000 or B.key2 < 3); + +update t0 set key1=1; +explain select max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) + from t0 as A force index(i1,i2), t0 as B force index (i1,i2) + where (A.key1 = 1 or A.key2 = 1) + and (B.key1 = 1 or B.key2 = 1); + +select max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) + from t0 as A force index(i1,i2), t0 as B force index (i1,i2) + where (A.key1 = 1 or A.key2 = 1) + and (B.key1 = 1 or B.key2 = 1); + +alter table t0 add filler1 char(200), add filler2 char(200), add filler3 char(200); +update t0 set key2=1, key3=1, key4=1, key5=1,key6=1,key7=1 where key7 < 500; +explain select max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) + from t0 as A, t0 as B + where (A.key1 = 1 and A.key2 = 1 and A.key3 = 1 and A.key4=1 and A.key5=1 and A.key6=1 and A.key7 = 1 or A.key8=1) + and (B.key1 = 1 and B.key2 = 1 and B.key3 = 1 and B.key4=1 and B.key5=1 and B.key6=1 and B.key7 = 1 or B.key8=1); + +select max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) + from t0 as A, t0 as B + where (A.key1 = 1 and A.key2 = 1 and A.key3 = 1 and A.key4=1 and A.key5=1 and A.key6=1 and A.key7 = 1 or A.key8=1) + and (B.key1 = 1 and B.key2 = 1 and B.key3 = 1 and B.key4=1 and B.key5=1 and B.key6=1 and B.key7 = 1 or B.key8=1); + +set join_buffer_size= @save_join_buffer_size; +# Test for BUG#4177 ends + +drop table t0, t1, t2, t3, t4; + diff --git a/mysql-test/t/join_outer.test b/mysql-test/t/join_outer.test index 665b4fafbca..ce2ce577b46 100644 --- a/mysql-test/t/join_outer.test +++ b/mysql-test/t/join_outer.test @@ -606,4 +606,14 @@ INSERT INTO t1 VALUES (0); SELECT * FROM t0, t1 LEFT JOIN (t2,t3) ON a1=5 WHERE a0=a1 AND a0=1; EXPLAIN SELECT * FROM t0, t1 LEFT JOIN (t2,t3) ON a1=5 WHERE a0=a1 AND a0=1; +# Test for BUG#4480 +drop table t1,t2; +create table t1 (a int, b int); +insert into t1 values (1,1),(2,2),(3,3); +create table t2 (a int, b int); +insert into t2 values (1,1), (2,2); + +select * from t2 right join t1 on t2.a=t1.a; +select straight_join * from t2 right join t1 on t2.a=t1.a; + DROP TABLE t0,t1,t2,t3; diff --git a/ndb/include/ndbapi/NdbEventOperation.hpp b/ndb/include/ndbapi/NdbEventOperation.hpp index 056e9a58c74..6a2e7d56ca8 100644 --- a/ndb/include/ndbapi/NdbEventOperation.hpp +++ b/ndb/include/ndbapi/NdbEventOperation.hpp @@ -64,7 +64,7 @@ class NdbEventOperationImpl; * * Known issues: * - * When several NdbEventOperation s are tied to the same event in the same + * When several NdbEventOperation's are tied to the same event in the same * process they will share the circular buffer. The BufferLength will then * be the same for all and decided by the first NdbEventOperation * instantiation. Just make sure to instantiate the "largest" one first. @@ -84,7 +84,7 @@ class NdbEventOperationImpl; * replica. If a node fails events will not be received twice anymore * for data in corresponding fragment. Will be optimized in later versions. * - * If a nodefailiure has occured not all events will be recieved + * If a node failure has occured not all events will be recieved * anymore. Drop NdbEventOperation and Create again after nodes are up * again. Will be fixed in later versions. * @@ -97,7 +97,7 @@ class NdbEventOperationImpl; * * Useful API programs: * - * select_all -d sys 'NDB$EVENTS_0' + * ndb_select_all -d sys 'NDB$EVENTS_0' * Will show contents in the system table containing created events. * */ @@ -187,8 +187,19 @@ public: */ NdbDictionary::Event::TableEvent getEventType(); + /** + * + */ Uint32 getGCI(); + + /** + * + */ Uint32 getLatestGCI(); + + /* + * + */ void print(); private: diff --git a/server-tools/instance-manager/mysql_connection.cc b/server-tools/instance-manager/mysql_connection.cc index 83b046c1e5b..d3f58bf3771 100644 --- a/server-tools/instance-manager/mysql_connection.cc +++ b/server-tools/instance-manager/mysql_connection.cc @@ -296,7 +296,6 @@ int Mysql_connection_thread::check_user(const char *user, const char *password) int Mysql_connection_thread::do_command() { char *packet; - uint old_timeout; ulong packet_length; /* We start to count packets from 0 for each new command */ diff --git a/server-tools/instance-manager/parse.cc b/server-tools/instance-manager/parse.cc index 38e10c7f2f5..d029267f9b8 100644 --- a/server-tools/instance-manager/parse.cc +++ b/server-tools/instance-manager/parse.cc @@ -131,7 +131,6 @@ Command *parse_command(Command_factory *factory, const char *text) const char *instance_name; uint instance_name_len; Command *command; - const char *saved_text= text; Token tok1= shift_token(&text, &word_len); diff --git a/server-tools/instance-manager/thread_registry.cc b/server-tools/instance-manager/thread_registry.cc index 4037da71880..d0bf51f3d61 100644 --- a/server-tools/instance-manager/thread_registry.cc +++ b/server-tools/instance-manager/thread_registry.cc @@ -148,6 +148,7 @@ int Thread_registry::cond_wait(Thread_info *info, pthread_cond_t *cond, void Thread_registry::deliver_shutdown() { + Thread_info *info; struct timespec shutdown_time; set_timespec(shutdown_time, 1); @@ -161,7 +162,7 @@ void Thread_registry::deliver_shutdown() stopped alarm processing. */ process_alarm(THR_SERVER_ALARM); - for (Thread_info *info= head.next; info != &head; info= info->next) + for (info= head.next; info != &head; info= info->next) { pthread_kill(info->thread_id, THREAD_KICK_OFF_SIGNAL); /* @@ -190,7 +191,7 @@ void Thread_registry::deliver_shutdown() so this time everybody should be informed (presumably each worker can get CPU during shutdown_time.) */ - for (Thread_info *info= head.next; info != &head; info= info->next) + for (info= head.next; info != &head; info= info->next) { pthread_kill(info->thread_id, THREAD_KICK_OFF_SIGNAL); if (info->current_cond) diff --git a/server-tools/instance-manager/user_map.cc b/server-tools/instance-manager/user_map.cc index b921152d858..8cbceceac7c 100644 --- a/server-tools/instance-manager/user_map.cc +++ b/server-tools/instance-manager/user_map.cc @@ -123,7 +123,6 @@ int User_map::load(const char *password_file_name) 1 + /* for ':' */ 1 + /* for newline */ 1]; /* for trailing zero */ - uint line_length; User *user; int rc= 1; diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 7b3f9fb3d7c..cb23e31225b 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -1356,7 +1356,8 @@ innobase_commit( 3. innobase_query_caching_of_table_permitted(), 4. innobase_savepoint(), 5. ::init_table_handle_for_HANDLER(), - 6. innobase_start_trx_and_assign_read_view() + 6. innobase_start_trx_and_assign_read_view(), + 7. ::transactional_table_lock() and it is only set to 0 in a commit or a rollback. If it is 0 we know there cannot be resources to be freed and we could return immediately. @@ -5134,8 +5135,9 @@ ha_innobase::start_stmt( select_lock_type value. The value of stored_select_lock_type was decided in: 1) ::store_lock(), - 2) ::external_lock(), and - 3) ::init_table_handle_for_HANDLER(). */ + 2) ::external_lock(), + 3) ::init_table_handle_for_HANDLER(), and + 4) :.transactional_table_lock(). */ prebuilt->select_lock_type = prebuilt->stored_select_lock_type; @@ -5326,6 +5328,94 @@ ha_innobase::external_lock( DBUG_RETURN(0); } +/********************************************************************** +With this function MySQL request a transactional lock to a table when +user issued query LOCK TABLES..WHERE ENGINE = InnoDB. */ + +int +ha_innobase::transactional_table_lock( +/*==================================*/ + /* out: 0 */ + THD* thd, /* in: handle to the user thread */ + int lock_type) /* in: lock type */ +{ + row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; + trx_t* trx; + + DBUG_ENTER("ha_innobase::transactional_table_lock"); + DBUG_PRINT("enter",("lock_type: %d", lock_type)); + + /* We do not know if MySQL can call this function before calling + external_lock(). To be safe, update the thd of the current table + handle. */ + + update_thd(thd); + + if (prebuilt->table->ibd_file_missing && !current_thd->tablespace_op) { + ut_print_timestamp(stderr); + fprintf(stderr, " InnoDB error:\n" +"MySQL is trying to use a table handle but the .ibd file for\n" +"table %s does not exist.\n" +"Have you deleted the .ibd file from the database directory under\n" +"the MySQL datadir?" +"Look from section 15.1 of http://www.innodb.com/ibman.html\n" +"how you can resolve the problem.\n", + prebuilt->table->name); + DBUG_RETURN(HA_ERR_CRASHED); + } + + trx = prebuilt->trx; + + prebuilt->sql_stat_start = TRUE; + prebuilt->hint_need_to_fetch_extra_cols = 0; + + prebuilt->read_just_key = 0; + prebuilt->keep_other_fields_on_keyread = FALSE; + + if (lock_type == F_WRLCK) { + prebuilt->select_lock_type = LOCK_X; + prebuilt->stored_select_lock_type = LOCK_X; + } else if (lock_type == F_RDLCK) { + prebuilt->select_lock_type = LOCK_S; + prebuilt->stored_select_lock_type = LOCK_S; + } else { + ut_print_timestamp(stderr); + fprintf(stderr, " InnoDB error:\n" +"MySQL is trying to set transactional table lock with corrupted lock type\n" +"to table %s, lock type %d does not exist.\n", + prebuilt->table->name, lock_type); + DBUG_RETURN(HA_ERR_CRASHED); + } + + /* MySQL is setting a new transactional table lock */ + + /* Set the MySQL flag to mark that there is an active transaction */ + thd->transaction.all.innodb_active_trans = 1; + + if (thd->in_lock_tables && thd->variables.innodb_table_locks) { + ulint error = DB_SUCCESS; + + error = row_lock_table_for_mysql(prebuilt,NULL, + LOCK_TABLE_TRANSACTIONAL); + + if (error != DB_SUCCESS) { + error = convert_error_code_to_mysql(error, user_thd); + DBUG_RETURN(error); + } + + if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) { + + /* Store the current undo_no of the transaction + so that we know where to roll back if we have + to roll back the next SQL statement */ + + trx_mark_sql_stat_end(trx); + } + } + + DBUG_RETURN(0); +} + /**************************************************************************** Here we export InnoDB status variables to MySQL. */ diff --git a/sql/ha_innodb.h b/sql/ha_innodb.h index bc4e3db7467..bb8823fd1bb 100644 --- a/sql/ha_innodb.h +++ b/sql/ha_innodb.h @@ -150,6 +150,7 @@ class ha_innobase: public handler int discard_or_import_tablespace(my_bool discard); int extra(enum ha_extra_function operation); int external_lock(THD *thd, int lock_type); + int transactional_table_lock(THD *thd, int lock_type); int start_stmt(THD *thd); void position(byte *record); diff --git a/sql/handler.h b/sql/handler.h index 3ee66e4b3cf..f0faeff9234 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -454,6 +454,13 @@ public: { return extra(operation); } virtual int reset() { return extra(HA_EXTRA_RESET); } virtual int external_lock(THD *thd, int lock_type) { return 0; } + /* + This is called to set transactional table lock to a table. + If the handler don't support this, then this function will + return HA_ERR_WRONG_COMMAND and MySQL will give + ER_ILLEGAL_HA error message. + */ + virtual int transactional_table_lock(THD *thd, int lock_type) {return HA_ERR_WRONG_COMMAND;} virtual void unlock_row() {} virtual int start_stmt(THD *thd) {return 0;} /* diff --git a/sql/lock.cc b/sql/lock.cc index 8a3619b57dd..393cf4cf142 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -79,7 +79,7 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table,uint count, bool unlock, TABLE **write_locked); static int lock_external(THD *thd, TABLE **table,uint count); static int unlock_external(THD *thd, TABLE **table,uint count); -static void print_lock_error(int error); +static void print_lock_error(int error, const char *); MYSQL_LOCK *mysql_lock_tables(THD *thd,TABLE **tables,uint count) @@ -187,7 +187,7 @@ static int lock_external(THD *thd, TABLE **tables, uint count) (*tables)->file->external_lock(thd, F_UNLCK); (*tables)->current_lock=F_UNLCK; } - print_lock_error(error); + print_lock_error(error, (*tables)->file->table_type()); DBUG_RETURN(error); } else @@ -380,7 +380,7 @@ static int unlock_external(THD *thd, TABLE **table,uint count) table++; } while (--count); if (error_code) - print_lock_error(error_code); + print_lock_error(error_code, (*table)->file->table_type()); DBUG_RETURN(error_code); } @@ -683,7 +683,7 @@ void unlock_table_names(THD *thd, TABLE_LIST *table_list, } -static void print_lock_error(int error) +static void print_lock_error(int error, const char *table) { int textno; DBUG_ENTER("print_lock_error"); @@ -695,11 +695,22 @@ static void print_lock_error(int error) case HA_ERR_READ_ONLY_TRANSACTION: textno=ER_READ_ONLY_TRANSACTION; break; + case HA_ERR_LOCK_DEADLOCK: + textno=ER_LOCK_DEADLOCK; + break; + case HA_ERR_WRONG_COMMAND: + textno=ER_ILLEGAL_HA; + break; default: textno=ER_CANT_LOCK; break; } - my_error(textno,MYF(ME_BELL+ME_OLDWIN+ME_WAITTANG),error); + + if ( textno == ER_ILLEGAL_HA ) + my_error(textno, MYF(ME_BELL+ME_OLDWIN+ME_WAITTANG), table); + else + my_error(textno, MYF(ME_BELL+ME_OLDWIN+ME_WAITTANG), error); + DBUG_VOID_RETURN; } @@ -927,3 +938,62 @@ bool make_global_read_lock_block_commit(THD *thd) thd->exit_cond(old_message); DBUG_RETURN(error); } + +/* + Take transactional table lock for all tables in the list + + SYNOPSIS + transactional_lock_tables + thd Thread THD + tables list of tables + counter number of tables in the list + + NOTES + + RETURN + 0 - OK + -1 - error + +*/ +int transactional_lock_tables(THD *thd, TABLE_LIST *tables, uint counter) +{ + uint i; + int lock_type,error=0; + TABLE_LIST *table; + TABLE **start,**ptr; + + DBUG_ENTER("transactional_lock_tables"); + + if (!(ptr=start=(TABLE**) sql_alloc(sizeof(TABLE*) * counter))) + return -1; + + for (table= tables; table; table= table->next_global) + { + if (!table->placeholder() && !table->schema_table) + *(ptr++)= table->table; + } + + for (i=1 ; i <= counter ; i++, start++) + { + DBUG_ASSERT((*start)->reginfo.lock_type >= TL_READ); + lock_type=F_WRLCK; /* Lock exclusive */ + + if ((*start)->db_stat & HA_READ_ONLY || + ((*start)->reginfo.lock_type >= TL_READ && + (*start)->reginfo.lock_type <= TL_READ_NO_INSERT)) + lock_type=F_RDLCK; + + if ((error=(*start)->file->transactional_table_lock(thd, lock_type))) + { + print_lock_error(error, (*start)->file->table_type()); + DBUG_RETURN(-1); + } + else + { + (*start)->db_stat &= ~ HA_BLOCK_LOCK; + (*start)->current_lock= lock_type; + } + } + + DBUG_RETURN(0); +} diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index babec735b62..2fc82e05f31 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -828,6 +828,7 @@ int open_tables(THD *thd, TABLE_LIST *tables, uint *counter); int simple_open_n_lock_tables(THD *thd,TABLE_LIST *tables); bool open_and_lock_tables(THD *thd,TABLE_LIST *tables); int lock_tables(THD *thd, TABLE_LIST *tables, uint counter); +int transactional_lock_tables(THD *thd, TABLE_LIST *tables, uint counter); TABLE *open_temporary_table(THD *thd, const char *path, const char *db, const char *table_name, bool link_in_list); bool rm_temporary_table(enum db_type base, char *path); diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 80237766d29..e47c7e147a7 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -777,8 +777,7 @@ QUICK_RANGE_SELECT::~QUICK_RANGE_SELECT() QUICK_INDEX_MERGE_SELECT::QUICK_INDEX_MERGE_SELECT(THD *thd_param, TABLE *table) - :cur_quick_it(quick_selects),pk_quick_select(NULL),unique(NULL), - thd(thd_param) + :pk_quick_select(NULL), thd(thd_param) { DBUG_ENTER("QUICK_INDEX_MERGE_SELECT::QUICK_INDEX_MERGE_SELECT"); index= MAX_KEY; @@ -790,17 +789,14 @@ QUICK_INDEX_MERGE_SELECT::QUICK_INDEX_MERGE_SELECT(THD *thd_param, int QUICK_INDEX_MERGE_SELECT::init() { - cur_quick_it.rewind(); - cur_quick_select= cur_quick_it++; - return 0; + DBUG_ENTER("QUICK_INDEX_MERGE_SELECT::init"); + DBUG_RETURN(0); } int QUICK_INDEX_MERGE_SELECT::reset() { - int result; DBUG_ENTER("QUICK_INDEX_MERGE_SELECT::reset"); - result= cur_quick_select->reset() || prepare_unique(); - DBUG_RETURN(result); + DBUG_RETURN(read_keys_and_merge()); } bool @@ -820,8 +816,12 @@ QUICK_INDEX_MERGE_SELECT::push_quick_back(QUICK_RANGE_SELECT *quick_sel_range) QUICK_INDEX_MERGE_SELECT::~QUICK_INDEX_MERGE_SELECT() { + List_iterator_fast quick_it(quick_selects); + QUICK_RANGE_SELECT* quick; DBUG_ENTER("QUICK_INDEX_MERGE_SELECT::~QUICK_INDEX_MERGE_SELECT"); - delete unique; + quick_it.rewind(); + while ((quick= quick_it++)) + quick->file= NULL; quick_selects.delete_elements(); delete pk_quick_select; free_root(&alloc,MYF(0)); @@ -833,7 +833,8 @@ QUICK_ROR_INTERSECT_SELECT::QUICK_ROR_INTERSECT_SELECT(THD *thd_param, TABLE *table, bool retrieve_full_rows, MEM_ROOT *parent_alloc) - : cpk_quick(NULL), thd(thd_param), need_to_fetch_row(retrieve_full_rows) + : cpk_quick(NULL), thd(thd_param), need_to_fetch_row(retrieve_full_rows), + scans_inited(false) { index= MAX_KEY; head= table; @@ -859,8 +860,9 @@ QUICK_ROR_INTERSECT_SELECT::QUICK_ROR_INTERSECT_SELECT(THD *thd_param, int QUICK_ROR_INTERSECT_SELECT::init() { - /* Check if last_rowid was successfully allocated in ctor */ - return !last_rowid; + DBUG_ENTER("QUICK_ROR_INTERSECT_SELECT::init"); + /* Check if last_rowid was successfully allocated in ctor */ + DBUG_RETURN(!last_rowid); } @@ -953,7 +955,7 @@ int QUICK_ROR_INTERSECT_SELECT::init_ror_merged_scan(bool reuse_handler) DBUG_ENTER("QUICK_ROR_INTERSECT_SELECT::init_ror_merged_scan"); /* Initialize all merged "children" quick selects */ - DBUG_ASSERT(!(need_to_fetch_row && !reuse_handler)); + DBUG_ASSERT(!need_to_fetch_row || reuse_handler); if (!need_to_fetch_row && reuse_handler) { quick= quick_it++; @@ -995,7 +997,14 @@ int QUICK_ROR_INTERSECT_SELECT::init_ror_merged_scan(bool reuse_handler) int QUICK_ROR_INTERSECT_SELECT::reset() { DBUG_ENTER("QUICK_ROR_INTERSECT_SELECT::reset"); - DBUG_RETURN(init_ror_merged_scan(TRUE)); + if (!scans_inited && init_ror_merged_scan(TRUE)) + DBUG_RETURN(1); + scans_inited= true; + List_iterator_fast it(quick_selects); + QUICK_RANGE_SELECT *quick; + while ((quick= it++)) + quick->reset(); + DBUG_RETURN(0); } @@ -1034,7 +1043,7 @@ QUICK_ROR_INTERSECT_SELECT::~QUICK_ROR_INTERSECT_SELECT() QUICK_ROR_UNION_SELECT::QUICK_ROR_UNION_SELECT(THD *thd_param, TABLE *table) - :thd(thd_param) + : thd(thd_param), scans_inited(false) { index= MAX_KEY; head= table; @@ -1057,18 +1066,19 @@ QUICK_ROR_UNION_SELECT::QUICK_ROR_UNION_SELECT(THD *thd_param, int QUICK_ROR_UNION_SELECT::init() { + DBUG_ENTER("QUICK_ROR_UNION_SELECT::init"); if (init_queue(&queue, quick_selects.elements, 0, FALSE , QUICK_ROR_UNION_SELECT::queue_cmp, (void*) this)) { bzero(&queue, sizeof(QUEUE)); - return 1; + DBUG_RETURN(1); } if (!(cur_rowid= (byte*)alloc_root(&alloc, 2*head->file->ref_length))) - return 1; + DBUG_RETURN(1); prev_rowid= cur_rowid + head->file->ref_length; - return 0; + DBUG_RETURN(0); } @@ -1106,6 +1116,18 @@ int QUICK_ROR_UNION_SELECT::reset() int error; DBUG_ENTER("QUICK_ROR_UNION_SELECT::reset"); have_prev_rowid= FALSE; + if (!scans_inited) + { + QUICK_SELECT_I *quick; + List_iterator_fast it(quick_selects); + while ((quick= it++)) + { + if (quick->init_ror_merged_scan(FALSE)) + DBUG_RETURN(1); + } + scans_inited= true; + } + queue_remove_all(&queue); /* Initialize scans for merged quick selects and put all merged quick selects into the queue. @@ -1113,7 +1135,7 @@ int QUICK_ROR_UNION_SELECT::reset() List_iterator_fast it(quick_selects); while ((quick= it++)) { - if (quick->init_ror_merged_scan(FALSE)) + if (quick->reset()) DBUG_RETURN(1); if ((error= quick->get_next())) { @@ -1591,7 +1613,6 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, DBUG_PRINT("enter",("keys_to_use: %lu prev_tables: %lu const_tables: %lu", keys_to_use.to_ulonglong(), (ulong) prev_tables, (ulong) const_tables)); - delete quick; quick=0; needed_reg.clear_all(); @@ -5553,22 +5574,29 @@ err: /* - Fetch all row ids into unique. - + Perform key scans for all used indexes (except CPK), get rowids and merge + them into an ordered non-recurrent sequence of rowids. + + The merge/duplicate removal is performed using Unique class. We put all + rowids into Unique, get the sorted sequence and destroy the Unique. + If table has a clustered primary key that covers all rows (TRUE for bdb and innodb currently) and one of the index_merge scans is a scan on PK, then - primary key scan rowids are not put into Unique and also - rows that will be retrieved by PK scan are not put into Unique + rows that will be retrieved by PK scan are not put into Unique and + primary key scan is not performed here, it is performed later separately. RETURN 0 OK other error */ -int QUICK_INDEX_MERGE_SELECT::prepare_unique() +int QUICK_INDEX_MERGE_SELECT::read_keys_and_merge() { + List_iterator_fast cur_quick_it(quick_selects); + QUICK_RANGE_SELECT* cur_quick; int result; + Unique *unique; DBUG_ENTER("QUICK_INDEX_MERGE_SELECT::prepare_unique"); /* We're going to just read rowids. */ @@ -5583,7 +5611,17 @@ int QUICK_INDEX_MERGE_SELECT::prepare_unique() */ head->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS); - cur_quick_select->init(); + cur_quick_it.rewind(); + cur_quick= cur_quick_it++; + DBUG_ASSERT(cur_quick); + + /* + We reuse the same instance of handler so we need to call both init and + reset here. + */ + if (cur_quick->init()) + DBUG_RETURN(1); + cur_quick->reset(); unique= new Unique(refpos_order_cmp, (void *)head->file, head->file->ref_length, @@ -5592,24 +5630,28 @@ int QUICK_INDEX_MERGE_SELECT::prepare_unique() DBUG_RETURN(1); for (;;) { - while ((result= cur_quick_select->get_next()) == HA_ERR_END_OF_FILE) + while ((result= cur_quick->get_next()) == HA_ERR_END_OF_FILE) { - cur_quick_select->range_end(); - cur_quick_select= cur_quick_it++; - if (!cur_quick_select) + cur_quick->range_end(); + cur_quick= cur_quick_it++; + if (!cur_quick) break; - if (cur_quick_select->init()) + if (cur_quick->file->inited != handler::NONE) + cur_quick->file->ha_index_end(); + if (cur_quick->init()) DBUG_RETURN(1); - /* QUICK_RANGE_SELECT::reset never fails */ - cur_quick_select->reset(); + cur_quick->reset(); } if (result) { if (result != HA_ERR_END_OF_FILE) + { + cur_quick->range_end(); DBUG_RETURN(result); + } break; } @@ -5620,8 +5662,8 @@ int QUICK_INDEX_MERGE_SELECT::prepare_unique() if (pk_quick_select && pk_quick_select->row_in_ranges()) continue; - cur_quick_select->file->position(cur_quick_select->record); - result= unique->unique_add((char*)cur_quick_select->file->ref); + cur_quick->file->position(cur_quick->record); + result= unique->unique_add((char*)cur_quick->file->ref); if (result) DBUG_RETURN(1); @@ -5629,6 +5671,7 @@ int QUICK_INDEX_MERGE_SELECT::prepare_unique() /* ok, all row ids are in Unique */ result= unique->get(head); + delete unique; doing_pk_scan= FALSE; /* start table scan */ init_read_record(&read_record, thd, head, (SQL_SELECT*) 0, 1, 1); @@ -5668,6 +5711,7 @@ int QUICK_INDEX_MERGE_SELECT::get_next() doing_pk_scan= TRUE; if ((result= pk_quick_select->init())) DBUG_RETURN(result); + pk_quick_select->reset(); DBUG_RETURN(pk_quick_select->get_next()); } } diff --git a/sql/opt_range.h b/sql/opt_range.h index 19234f61ea2..74d388128c8 100644 --- a/sql/opt_range.h +++ b/sql/opt_range.h @@ -127,7 +127,8 @@ public: reset() should be called when it is certain that row retrieval will be necessary. This call may do heavyweight initialization like buffering first N records etc. If reset() call fails get_next() must not be called. - + Note that reset() may be called several times if this quick select + executes in a subselect. RETURN 0 OK other Error code @@ -274,6 +275,10 @@ public: next=0; range= NULL; cur_range= NULL; + /* + Note: in opt_range.cc there are places where it is assumed that this + function always succeeds + */ return 0; } int init(); @@ -388,21 +393,15 @@ public: /* range quick selects this index_merge read consists of */ List quick_selects; - /* quick select which is currently used for rows retrieval */ - List_iterator_fast cur_quick_it; - QUICK_RANGE_SELECT* cur_quick_select; - /* quick select that uses clustered primary key (NULL if none) */ QUICK_RANGE_SELECT* pk_quick_select; /* true if this select is currently doing a clustered PK scan */ bool doing_pk_scan; - Unique *unique; MEM_ROOT alloc; - THD *thd; - int prepare_unique(); + int read_keys_and_merge(); /* used to get rows collected in Unique */ READ_RECORD read_record; @@ -465,6 +464,8 @@ public: MEM_ROOT alloc; /* Memory pool for this and merged quick selects data. */ THD *thd; /* current thread */ bool need_to_fetch_row; /* if true, do retrieve full table records. */ + /* in top-level quick select, true if merged scans where initialized */ + bool scans_inited; }; @@ -514,6 +515,7 @@ public: uint rowid_length; /* table rowid length */ private: static int queue_cmp(void *arg, byte *val1, byte *val2); + bool scans_inited; }; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 2500769ee30..eef5bf73b95 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1864,8 +1864,7 @@ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables) static void relink_tables_for_multidelete(THD *thd) { - if (thd->lex->all_selects_list->next_select_in_list() || - thd->lex->time_zone_tables_used) + if (thd->lex->all_selects_list->next_select_in_list()) { for (SELECT_LEX *sl= thd->lex->all_selects_list; sl; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 2205ec504e9..001188d6fc5 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -30,15 +30,6 @@ */ sys_var_long_ptr trg_new_row_fake_var(0, 0); -/* - Fake table list object, pointer to which is used as special value for - st_lex::time_zone_tables_used indicating that we implicitly use time - zone tables in this statement but real table list was not yet created. - Pointer to it is also returned by my_tz_get_tables_list() as indication - of transient error; -*/ -TABLE_LIST fake_time_zone_tables_list; - /* Macros to look like lex */ #define yyGet() *(lex->ptr++) @@ -1911,6 +1902,31 @@ void st_lex::first_lists_tables_same() } +/* + Add implicitly used time zone description tables to global table list + (if needed). + + SYNOPSYS + st_lex::add_time_zone_tables_to_query_tables() + thd - pointer to current thread context + + RETURN VALUE + TRUE - error + FALSE - success +*/ + +bool st_lex::add_time_zone_tables_to_query_tables(THD *thd) +{ + /* We should not add these tables twice */ + if (!time_zone_tables_used) + { + time_zone_tables_used= my_tz_get_table_list(thd, &query_tables_last); + if (time_zone_tables_used == &fake_time_zone_tables_list) + return TRUE; + } + return FALSE; +} + /* Link table back that was unlinked with unlink_first_table() diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 1c0e3e2e02e..721febab548 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -87,6 +87,7 @@ enum enum_sql_command { SQLCOM_PREPARE, SQLCOM_EXECUTE, SQLCOM_DEALLOCATE_PREPARE, SQLCOM_CREATE_VIEW, SQLCOM_DROP_VIEW, SQLCOM_CREATE_TRIGGER, SQLCOM_DROP_TRIGGER, + SQLCOM_LOCK_TABLES_TRANSACTIONAL, /* This should be the last !!! */ SQLCOM_END }; @@ -735,9 +736,8 @@ typedef struct st_lex /* Names of user variables holding parameters (in EXECUTE) */ List prepared_stmt_params; /* - If points to fake_time_zone_tables_list indicates that time zone - tables are implicitly used by statement, also is used for holding - list of those tables after they are opened. + Points to part of global table list which contains time zone tables + implicitly used by the statement. */ TABLE_LIST *time_zone_tables_used; sp_head *sphead; @@ -802,6 +802,7 @@ typedef struct st_lex *(table->prev_global= query_tables_last)= table; query_tables_last= &table->next_global; } + bool add_time_zone_tables_to_query_tables(THD *thd); bool can_be_merged(); bool can_use_merged(); @@ -810,7 +811,6 @@ typedef struct st_lex bool need_correct_ident(); } LEX; -extern TABLE_LIST fake_time_zone_tables_list; struct st_lex_local: public st_lex { static void *operator new(size_t size) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index c076872b755..0dec6e820be 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2123,19 +2123,6 @@ mysql_execute_command(THD *thd) } #endif /* !HAVE_REPLICATION */ - if (lex->time_zone_tables_used) - { - TABLE_LIST *tmp; - if ((tmp= my_tz_get_table_list(thd, &lex->query_tables_last)) == - &fake_time_zone_tables_list) - { - DBUG_RETURN(-1); - } - lex->time_zone_tables_used= tmp; - if (!all_tables) - all_tables= tmp; - } - /* When option readonly is set deny operations which change tables. Except for the replication thread and the 'super' users. @@ -3266,6 +3253,27 @@ create_error: thd->options&= ~(ulong) (OPTION_TABLE_LOCK); thd->in_lock_tables=0; break; + case SQLCOM_LOCK_TABLES_TRANSACTIONAL: + { + uint counter = 0; + + if (check_db_used(thd, all_tables)) + goto error; + if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables, 0)) + goto error; + + thd->in_lock_tables=1; + thd->options|= OPTION_TABLE_LOCK; + + if (open_tables(thd, all_tables, &counter) == 0 && + transactional_lock_tables(thd, all_tables, counter) == 0) + send_ok(thd); + else + thd->options&= ~(ulong) (OPTION_TABLE_LOCK); + + thd->in_lock_tables=0; + break; + } case SQLCOM_CREATE_DB: { char *alias; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 44412cdc43a..570774c8054 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -64,6 +64,7 @@ static void best_extension_by_limited_search(JOIN *join, uint prune_level); static uint determine_search_depth(JOIN* join); static int join_tab_cmp(const void* ptr1, const void* ptr2); +static int join_tab_cmp_straight(const void* ptr1, const void* ptr2); /* TODO: 'find_best' is here only temporarily until 'greedy_search' is tested and approved. @@ -3678,22 +3679,26 @@ choose_plan(JOIN *join, table_map join_tables) { uint search_depth= join->thd->variables.optimizer_search_depth; uint prune_level= join->thd->variables.optimizer_prune_level; - + bool straight_join= join->select_options & SELECT_STRAIGHT_JOIN; DBUG_ENTER("choose_plan"); - if (join->select_options & SELECT_STRAIGHT_JOIN) + /* + if (SELECT_STRAIGHT_JOIN option is set) + reorder tables so dependent tables come after tables they depend + on, otherwise keep tables in the order they were specified in the query + else + Apply heuristic: pre-sort all access plans with respect to the number of + records accessed. + */ + qsort(join->best_ref + join->const_tables, join->tables - join->const_tables, + sizeof(JOIN_TAB*), straight_join?join_tab_cmp_straight:join_tab_cmp); + + if (straight_join) { optimize_straight_join(join, join_tables); } else { - /* - Heuristic: pre-sort all access plans with respect to the number of - records accessed. - */ - qsort(join->best_ref + join->const_tables, join->tables - join->const_tables, - sizeof(JOIN_TAB*), join_tab_cmp); - if (search_depth == MAX_TABLES+2) { /* TODO: 'MAX_TABLES+2' denotes the old implementation of find_best before @@ -3750,6 +3755,23 @@ join_tab_cmp(const void* ptr1, const void* ptr2) } +/* + Same as join_tab_cmp, but for use with SELECT_STRAIGHT_JOIN. +*/ + +static int +join_tab_cmp_straight(const void* ptr1, const void* ptr2) +{ + JOIN_TAB *jt1= *(JOIN_TAB**) ptr1; + JOIN_TAB *jt2= *(JOIN_TAB**) ptr2; + + if (jt1->dependent & jt2->table->map) + return 1; + if (jt2->dependent & jt1->table->map) + return -1; + return jt1 > jt2 ? 1 : (jt1 < jt2 ? -1 : 0); +} + /* Heuristic procedure to automatically guess a reasonable degree of exhaustiveness for the greedy search procedure. @@ -3832,7 +3854,7 @@ optimize_straight_join(JOIN *join, table_map join_tables) uint idx= join->const_tables; double record_count= 1.0; double read_time= 0.0; - + for (JOIN_TAB **pos= join->best_ref + idx ; (s= *pos) ; pos++) { /* Find the best access method from 's' to the current partial plan */ diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 98966374f2a..88cd3daf924 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -4269,7 +4269,8 @@ simple_expr: { $$= create_func_contains($3, $5); } | CONVERT_TZ_SYM '(' expr ',' expr ',' expr ')' { - Lex->time_zone_tables_used= &fake_time_zone_tables_list; + if (Lex->add_time_zone_tables_to_query_tables(YYTHD)) + YYABORT; $$= new Item_func_convert_tz($3, $5, $7); } | CURDATE optional_braces @@ -7307,8 +7308,9 @@ internal_variable_name: If this is time_zone variable we should open time zone describing tables */ - if (tmp == &sys_time_zone) - Lex->time_zone_tables_used= &fake_time_zone_tables_list; + if (tmp == &sys_time_zone && + lex->add_time_zone_tables_to_query_tables(YYTHD)) + YYABORT; } else { @@ -7413,8 +7415,8 @@ lock: { Lex->sql_command=SQLCOM_LOCK_TABLES; } - table_lock_list - {} + table_lock_list lock_engine_opt + {} ; table_or_tables: @@ -7440,6 +7442,15 @@ lock_option: | READ_SYM LOCAL_SYM { $$= TL_READ; } ; +lock_engine_opt: + /* empty */ + | WHERE + { + Lex->sql_command=SQLCOM_LOCK_TABLES_TRANSACTIONAL; + } + ENGINE_SYM opt_equal storage_engines + ; + unlock: UNLOCK_SYM table_or_tables { Lex->sql_command=SQLCOM_UNLOCK_TABLES; } ; diff --git a/sql/tztime.cc b/sql/tztime.cc index 50c496577e0..2c25c647a19 100644 --- a/sql/tztime.cc +++ b/sql/tztime.cc @@ -1439,6 +1439,12 @@ tz_init_table_list(TABLE_LIST *tz_tabs, TABLE_LIST ***global_next_ptr) } +/* + Fake table list object, pointer to which is returned by + my_tz_get_tables_list() as indication of error. +*/ +TABLE_LIST fake_time_zone_tables_list; + /* Create table list with time zone related tables and add it to the end of global table list. diff --git a/sql/tztime.h b/sql/tztime.h index 6d2388bb160..07e9146c6e9 100644 --- a/sql/tztime.h +++ b/sql/tztime.h @@ -64,6 +64,7 @@ extern Time_zone * my_tz_find(const String *name, TABLE_LIST *tz_tables); extern my_bool my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap); extern void my_tz_free(); +extern TABLE_LIST fake_time_zone_tables_list; /* Check if we have pointer to the beggining of list of implictly used