mirror of
https://github.com/MariaDB/server.git
synced 2025-01-31 11:01:52 +01:00
Merge poseidon.ndb.mysql.com:/home/tomas/mysql-5.0
into poseidon.ndb.mysql.com:/home/tomas/mysql-5.0-ndb
This commit is contained in:
commit
fde8d07de3
33 changed files with 589 additions and 121 deletions
|
@ -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),
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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. */
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;}
|
||||
/*
|
||||
|
|
80
sql/lock.cc
80
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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
114
sql/opt_range.cc
114
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_RANGE_SELECT> 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<QUICK_RANGE_SELECT> 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<QUICK_SELECT_I> 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<QUICK_SELECT_I> 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<QUICK_RANGE_SELECT> 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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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_RANGE_SELECT> quick_selects;
|
||||
|
||||
/* quick select which is currently used for rows retrieval */
|
||||
List_iterator_fast<QUICK_RANGE_SELECT> 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;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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<LEX_STRING> 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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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; }
|
||||
;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue