Commit 8d63e069 authored by joreland@mysql.com's avatar joreland@mysql.com

Merge joreland@bk-internal.mysql.com:/home/bk/mysql-5.0

into mysql.com:/home/jonas/src/mysql-5.0
parents 1071273b 348c2ea8
...@@ -2896,7 +2896,7 @@ static int run_query_stmt(MYSQL *mysql, struct st_query *q, int flags) ...@@ -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 */ /* Allocate array with bind structs, lengths and NULL flags */
bind= (MYSQL_BIND*) my_malloc(num_fields * sizeof(MYSQL_BIND), 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), length= (unsigned long*) my_malloc(num_fields * sizeof(unsigned long),
MYF(MY_WME | MY_FAE)); MYF(MY_WME | MY_FAE));
is_null= (my_bool*) my_malloc(num_fields * sizeof(my_bool), is_null= (my_bool*) my_malloc(num_fields * sizeof(my_bool),
......
...@@ -612,6 +612,8 @@ extern lock_sys_t* lock_sys; ...@@ -612,6 +612,8 @@ extern lock_sys_t* lock_sys;
#define LOCK_TABLE 16 /* these type values should be so high that */ #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_REC 32 /* they can be ORed to the lock mode */
#define LOCK_TABLE_EXP 80 /* explicit table lock (80 = 16 + 64) */ #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 #define LOCK_TYPE_MASK 0xF0UL /* mask used to extract lock type from the
type_mode field in a lock */ type_mode field in a lock */
/* Waiting lock flag */ /* Waiting lock flag */
......
...@@ -464,6 +464,10 @@ struct trx_struct{ ...@@ -464,6 +464,10 @@ struct trx_struct{
ulint n_lock_table_exp;/* number of explicit table locks ulint n_lock_table_exp;/* number of explicit table locks
(LOCK TABLES) reserved by the (LOCK TABLES) reserved by the
transaction, stored in trx_locks */ 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) UT_LIST_NODE_T(trx_t)
trx_list; /* list of transactions */ trx_list; /* list of transactions */
UT_LIST_NODE_T(trx_t) UT_LIST_NODE_T(trx_t)
......
...@@ -2207,7 +2207,8 @@ lock_grant( ...@@ -2207,7 +2207,8 @@ lock_grant(
release it at the end of the SQL statement */ release it at the end of the SQL statement */
lock->trx->auto_inc_lock = lock; 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 ut_a(lock_get_mode(lock) == LOCK_S
|| lock_get_mode(lock) == LOCK_X); || lock_get_mode(lock) == LOCK_X);
} }
...@@ -3421,6 +3422,10 @@ lock_table_create( ...@@ -3421,6 +3422,10 @@ lock_table_create(
lock->trx->n_lock_table_exp++; 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; lock->un_member.tab_lock.table = table;
UT_LIST_ADD_LAST(un_member.tab_lock.locks, table->locks, lock); UT_LIST_ADD_LAST(un_member.tab_lock.locks, table->locks, lock);
...@@ -3458,7 +3463,11 @@ lock_table_remove_low( ...@@ -3458,7 +3463,11 @@ lock_table_remove_low(
} }
if (lock_get_type(lock) == LOCK_TABLE_EXP) { 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); UT_LIST_REMOVE(trx_locks, trx->trx_locks, lock);
...@@ -3592,7 +3601,8 @@ lock_table( ...@@ -3592,7 +3601,8 @@ lock_table(
DB_DEADLOCK, or DB_QUE_THR_SUSPENDED */ DB_DEADLOCK, or DB_QUE_THR_SUSPENDED */
ulint flags, /* in: if BTR_NO_LOCKING_FLAG bit is set, ulint flags, /* in: if BTR_NO_LOCKING_FLAG bit is set,
does nothing; does nothing;
if LOCK_TABLE_EXP bits are set, if LOCK_TABLE_EXP|LOCK_TABLE_TRANSACTIONAL
bits are set,
creates an explicit table lock */ creates an explicit table lock */
dict_table_t* table, /* in: database table in dictionary cache */ dict_table_t* table, /* in: database table in dictionary cache */
ulint mode, /* in: lock mode */ ulint mode, /* in: lock mode */
...@@ -3608,7 +3618,8 @@ lock_table( ...@@ -3608,7 +3618,8 @@ lock_table(
return(DB_SUCCESS); 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); trx = thr_get_trx(thr);
...@@ -3631,7 +3642,7 @@ lock_table( ...@@ -3631,7 +3642,7 @@ lock_table(
/* Another trx has a request on the table in an incompatible /* Another trx has a request on the table in an incompatible
mode: this trx may have to wait */ 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(); lock_mutex_exit_kernel();
...@@ -3722,7 +3733,8 @@ lock_table_dequeue( ...@@ -3722,7 +3733,8 @@ lock_table_dequeue(
ut_ad(mutex_own(&kernel_mutex)); ut_ad(mutex_own(&kernel_mutex));
#endif /* UNIV_SYNC_DEBUG */ #endif /* UNIV_SYNC_DEBUG */
ut_a(lock_get_type(in_lock) == LOCK_TABLE || 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); lock = UT_LIST_GET_NEXT(un_member.tab_lock.locks, in_lock);
...@@ -3826,7 +3838,9 @@ lock_release_off_kernel( ...@@ -3826,7 +3838,9 @@ lock_release_off_kernel(
} }
lock_table_dequeue(lock); 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 ut_a(lock_get_mode(lock) == LOCK_S
|| lock_get_mode(lock) == LOCK_X); || lock_get_mode(lock) == LOCK_X);
} }
...@@ -3850,6 +3864,7 @@ lock_release_off_kernel( ...@@ -3850,6 +3864,7 @@ lock_release_off_kernel(
ut_a(trx->auto_inc_lock == NULL); ut_a(trx->auto_inc_lock == NULL);
ut_a(trx->n_lock_table_exp == 0); 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( ...@@ -3915,6 +3930,7 @@ lock_release_tables_off_kernel(
} }
ut_a(trx->n_lock_table_exp == 0); ut_a(trx->n_lock_table_exp == 0);
ut_a(trx->n_lock_table_transactional == 0);
} }
/************************************************************************* /*************************************************************************
...@@ -4028,11 +4044,15 @@ lock_table_print( ...@@ -4028,11 +4044,15 @@ lock_table_print(
ut_ad(mutex_own(&kernel_mutex)); ut_ad(mutex_own(&kernel_mutex));
#endif /* UNIV_SYNC_DEBUG */ #endif /* UNIV_SYNC_DEBUG */
ut_a(lock_get_type(lock) == LOCK_TABLE || 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) { if (lock_get_type(lock) == LOCK_TABLE_EXP) {
fputs("EXPLICIT ", file); fputs("EXPLICIT ", file);
} else if (lock_get_type(lock) == LOCK_TABLE_TRANSACTIONAL) {
fputs("TRANSACTIONAL ", file);
} }
fputs("TABLE LOCK table ", file); fputs("TABLE LOCK table ", file);
ut_print_name(file, lock->trx, lock->un_member.tab_lock.table->name); ut_print_name(file, lock->trx, lock->un_member.tab_lock.table->name);
fprintf(file, " trx id %lu %lu", fprintf(file, " trx id %lu %lu",
...@@ -4418,6 +4438,7 @@ lock_table_queue_validate( ...@@ -4418,6 +4438,7 @@ lock_table_queue_validate(
while (lock) { while (lock) {
ut_a(((lock->trx)->conc_state == TRX_ACTIVE) ut_a(((lock->trx)->conc_state == TRX_ACTIVE)
|| ((lock->trx)->conc_state == TRX_PREPARED)
|| ((lock->trx)->conc_state == TRX_COMMITTED_IN_MEMORY)); || ((lock->trx)->conc_state == TRX_COMMITTED_IN_MEMORY));
if (!lock_get_wait(lock)) { if (!lock_get_wait(lock)) {
...@@ -4465,6 +4486,7 @@ lock_rec_queue_validate( ...@@ -4465,6 +4486,7 @@ lock_rec_queue_validate(
while (lock) { while (lock) {
ut_a(lock->trx->conc_state == TRX_ACTIVE ut_a(lock->trx->conc_state == TRX_ACTIVE
|| lock->trx->conc_state == TRX_PREPARED
|| lock->trx->conc_state || lock->trx->conc_state
== TRX_COMMITTED_IN_MEMORY); == TRX_COMMITTED_IN_MEMORY);
...@@ -4519,6 +4541,7 @@ lock_rec_queue_validate( ...@@ -4519,6 +4541,7 @@ lock_rec_queue_validate(
while (lock) { while (lock) {
ut_a(lock->trx->conc_state == TRX_ACTIVE ut_a(lock->trx->conc_state == TRX_ACTIVE
|| lock->trx->conc_state == TRX_PREPARED
|| lock->trx->conc_state == TRX_COMMITTED_IN_MEMORY); || lock->trx->conc_state == TRX_COMMITTED_IN_MEMORY);
ut_a(trx_in_trx_list(lock->trx)); ut_a(trx_in_trx_list(lock->trx));
...@@ -4601,6 +4624,7 @@ loop: ...@@ -4601,6 +4624,7 @@ loop:
ut_a(trx_in_trx_list(lock->trx)); ut_a(trx_in_trx_list(lock->trx));
ut_a(lock->trx->conc_state == TRX_ACTIVE ut_a(lock->trx->conc_state == TRX_ACTIVE
|| lock->trx->conc_state == TRX_PREPARED
|| lock->trx->conc_state == TRX_COMMITTED_IN_MEMORY); || lock->trx->conc_state == TRX_COMMITTED_IN_MEMORY);
for (i = nth_bit; i < lock_rec_get_n_bits(lock); i++) { for (i = nth_bit; i < lock_rec_get_n_bits(lock); i++) {
......
...@@ -784,7 +784,7 @@ row_lock_table_for_mysql( ...@@ -784,7 +784,7 @@ row_lock_table_for_mysql(
table handle */ table handle */
dict_table_t* table, /* in: table to lock, or NULL dict_table_t* table, /* in: table to lock, or NULL
if prebuilt->table should be if prebuilt->table should be
locked as LOCK_TABLE_EXP | locked or a
prebuilt->select_lock_type */ prebuilt->select_lock_type */
ulint mode) /* in: lock mode of table */ ulint mode) /* in: lock mode of table */
{ {
...@@ -822,8 +822,14 @@ run_again: ...@@ -822,8 +822,14 @@ run_again:
if (table) { if (table) {
err = lock_table(0, table, mode, thr); err = lock_table(0, table, mode, thr);
} else { } else {
err = lock_table(LOCK_TABLE_EXP, prebuilt->table, if (mode == LOCK_TABLE_TRANSACTIONAL) {
prebuilt->select_lock_type, thr); 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; trx->error_state = err;
......
...@@ -153,6 +153,7 @@ trx_create( ...@@ -153,6 +153,7 @@ trx_create(
trx->auto_inc_lock = NULL; trx->auto_inc_lock = NULL;
trx->n_lock_table_exp = 0; trx->n_lock_table_exp = 0;
trx->n_lock_table_transactional = 0;
trx->read_view_heap = mem_heap_create(256); trx->read_view_heap = mem_heap_create(256);
trx->read_view = NULL; trx->read_view = NULL;
...@@ -285,6 +286,7 @@ trx_free( ...@@ -285,6 +286,7 @@ trx_free(
ut_a(!trx->has_search_latch); ut_a(!trx->has_search_latch);
ut_a(!trx->auto_inc_lock); ut_a(!trx->auto_inc_lock);
ut_a(!trx->n_lock_table_exp); ut_a(!trx->n_lock_table_exp);
ut_a(!trx->n_lock_table_transactional);
ut_a(trx->dict_operation_lock_mode == 0); ut_a(trx->dict_operation_lock_mode == 0);
...@@ -1645,10 +1647,15 @@ trx_print( ...@@ -1645,10 +1647,15 @@ trx_print(
putc('\n', f); putc('\n', f);
if (trx->n_mysql_tables_in_use > 0 || trx->mysql_n_tables_locked > 0) { 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", if (trx->n_lock_table_transactional > 0 || trx->n_lock_table_exp > 0) {
(ulong) trx->n_mysql_tables_in_use, fprintf(f, "mysql explicit table locks %lu, transactional table locks %lu\n",
(ulong) trx->mysql_n_tables_locked); (ulong) trx->n_lock_table_exp,
(ulong) trx->n_lock_table_transactional);
} }
newline = TRUE; newline = TRUE;
......
...@@ -330,3 +330,8 @@ SELECT MIN(price) min, MAX(price) max, AVG(price) avg FROM (SELECT SUBSTRING( MA ...@@ -330,3 +330,8 @@ SELECT MIN(price) min, MAX(price) max, AVG(price) avg FROM (SELECT SUBSTRING( MA
min max avg min max avg
10.00 10.00 10 10.00 10.00 10
DROP TABLE t1; 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;
...@@ -214,3 +214,16 @@ CREATE TABLE `t1` ( `itemid` int(11) NOT NULL default '0', `grpid` varchar(15) N ...@@ -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); 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; 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; 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;
...@@ -1356,7 +1356,8 @@ innobase_commit( ...@@ -1356,7 +1356,8 @@ innobase_commit(
3. innobase_query_caching_of_table_permitted(), 3. innobase_query_caching_of_table_permitted(),
4. innobase_savepoint(), 4. innobase_savepoint(),
5. ::init_table_handle_for_HANDLER(), 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 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. there cannot be resources to be freed and we could return immediately.
...@@ -5134,8 +5135,9 @@ ha_innobase::start_stmt( ...@@ -5134,8 +5135,9 @@ ha_innobase::start_stmt(
select_lock_type value. The value of select_lock_type value. The value of
stored_select_lock_type was decided in: stored_select_lock_type was decided in:
1) ::store_lock(), 1) ::store_lock(),
2) ::external_lock(), and 2) ::external_lock(),
3) ::init_table_handle_for_HANDLER(). */ 3) ::init_table_handle_for_HANDLER(), and
4) :.transactional_table_lock(). */
prebuilt->select_lock_type = prebuilt->select_lock_type =
prebuilt->stored_select_lock_type; prebuilt->stored_select_lock_type;
...@@ -5326,6 +5328,94 @@ ha_innobase::external_lock( ...@@ -5326,6 +5328,94 @@ ha_innobase::external_lock(
DBUG_RETURN(0); 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. */ Here we export InnoDB status variables to MySQL. */
......
...@@ -150,6 +150,7 @@ class ha_innobase: public handler ...@@ -150,6 +150,7 @@ class ha_innobase: public handler
int discard_or_import_tablespace(my_bool discard); int discard_or_import_tablespace(my_bool discard);
int extra(enum ha_extra_function operation); int extra(enum ha_extra_function operation);
int external_lock(THD *thd, int lock_type); int external_lock(THD *thd, int lock_type);
int transactional_table_lock(THD *thd, int lock_type);
int start_stmt(THD *thd); int start_stmt(THD *thd);
void position(byte *record); void position(byte *record);
......
...@@ -454,6 +454,13 @@ public: ...@@ -454,6 +454,13 @@ public:
{ return extra(operation); } { return extra(operation); }
virtual int reset() { return extra(HA_EXTRA_RESET); } virtual int reset() { return extra(HA_EXTRA_RESET); }
virtual int external_lock(THD *thd, int lock_type) { return 0; } 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 void unlock_row() {}
virtual int start_stmt(THD *thd) {return 0;} virtual int start_stmt(THD *thd) {return 0;}
/* /*
......
...@@ -79,7 +79,7 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table,uint count, ...@@ -79,7 +79,7 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table,uint count,
bool unlock, TABLE **write_locked); bool unlock, TABLE **write_locked);
static int lock_external(THD *thd, TABLE **table,uint count); static int lock_external(THD *thd, TABLE **table,uint count);
static int unlock_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) 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) ...@@ -187,7 +187,7 @@ static int lock_external(THD *thd, TABLE **tables, uint count)
(*tables)->file->external_lock(thd, F_UNLCK); (*tables)->file->external_lock(thd, F_UNLCK);
(*tables)->current_lock=F_UNLCK; (*tables)->current_lock=F_UNLCK;
} }
print_lock_error(error); print_lock_error(error, (*tables)->file->table_type());
DBUG_RETURN(error); DBUG_RETURN(error);
} }
else else
...@@ -380,7 +380,7 @@ static int unlock_external(THD *thd, TABLE **table,uint count) ...@@ -380,7 +380,7 @@ static int unlock_external(THD *thd, TABLE **table,uint count)
table++; table++;
} while (--count); } while (--count);
if (error_code) if (error_code)
print_lock_error(error_code); print_lock_error(error_code, (*table)->file->table_type());
DBUG_RETURN(error_code); DBUG_RETURN(error_code);
} }
...@@ -683,7 +683,7 @@ void unlock_table_names(THD *thd, TABLE_LIST *table_list, ...@@ -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; int textno;
DBUG_ENTER("print_lock_error"); DBUG_ENTER("print_lock_error");
...@@ -695,11 +695,22 @@ static void print_lock_error(int error) ...@@ -695,11 +695,22 @@ static void print_lock_error(int error)
case HA_ERR_READ_ONLY_TRANSACTION: case HA_ERR_READ_ONLY_TRANSACTION:
textno=ER_READ_ONLY_TRANSACTION; textno=ER_READ_ONLY_TRANSACTION;
break; break;
case HA_ERR_LOCK_DEADLOCK:
textno=ER_LOCK_DEADLOCK;
break;
case HA_ERR_WRONG_COMMAND:
textno=ER_ILLEGAL_HA;
break;
default: default:
textno=ER_CANT_LOCK; textno=ER_CANT_LOCK;
break; 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; DBUG_VOID_RETURN;
} }
...@@ -927,3 +938,62 @@ bool make_global_read_lock_block_commit(THD *thd) ...@@ -927,3 +938,62 @@ bool make_global_read_lock_block_commit(THD *thd)
thd->exit_cond(old_message); thd->exit_cond(old_message);
DBUG_RETURN(error); 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); ...@@ -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); int simple_open_n_lock_tables(THD *thd,TABLE_LIST *tables);
bool open_and_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 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, TABLE *open_temporary_table(THD *thd, const char *path, const char *db,
const char *table_name, bool link_in_list); const char *table_name, bool link_in_list);
bool rm_temporary_table(enum db_type base, char *path); bool rm_temporary_table(enum db_type base, char *path);
......
...@@ -1864,8 +1864,7 @@ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables) ...@@ -1864,8 +1864,7 @@ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables)
static void relink_tables_for_multidelete(THD *thd) static void relink_tables_for_multidelete(THD *thd)
{ {
if (thd->lex->all_selects_list->next_select_in_list() || if (thd->lex->all_selects_list->next_select_in_list())
thd->lex->time_zone_tables_used)
{ {
for (SELECT_LEX *sl= thd->lex->all_selects_list; for (SELECT_LEX *sl= thd->lex->all_selects_list;
sl; sl;
......
...@@ -30,15 +30,6 @@ ...@@ -30,15 +30,6 @@
*/ */
sys_var_long_ptr trg_new_row_fake_var(0, 0); 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 */ /* Macros to look like lex */
#define yyGet() *(lex->ptr++) #define yyGet() *(lex->ptr++)
...@@ -1911,6 +1902,31 @@ void st_lex::first_lists_tables_same() ...@@ -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() Link table back that was unlinked with unlink_first_table()
......
...@@ -87,6 +87,7 @@ enum enum_sql_command { ...@@ -87,6 +87,7 @@ enum enum_sql_command {
SQLCOM_PREPARE, SQLCOM_EXECUTE, SQLCOM_DEALLOCATE_PREPARE, SQLCOM_PREPARE, SQLCOM_EXECUTE, SQLCOM_DEALLOCATE_PREPARE,
SQLCOM_CREATE_VIEW, SQLCOM_DROP_VIEW, SQLCOM_CREATE_VIEW, SQLCOM_DROP_VIEW,
SQLCOM_CREATE_TRIGGER, SQLCOM_DROP_TRIGGER, SQLCOM_CREATE_TRIGGER, SQLCOM_DROP_TRIGGER,
SQLCOM_LOCK_TABLES_TRANSACTIONAL,
/* This should be the last !!! */ /* This should be the last !!! */
SQLCOM_END SQLCOM_END
}; };
...@@ -735,9 +736,8 @@ typedef struct st_lex ...@@ -735,9 +736,8 @@ typedef struct st_lex
/* Names of user variables holding parameters (in EXECUTE) */ /* Names of user variables holding parameters (in EXECUTE) */
List<LEX_STRING> prepared_stmt_params; List<LEX_STRING> prepared_stmt_params;
/* /*
If points to fake_time_zone_tables_list indicates that time zone Points to part of global table list which contains time zone tables
tables are implicitly used by statement, also is used for holding implicitly used by the statement.
list of those tables after they are opened.
*/ */
TABLE_LIST *time_zone_tables_used; TABLE_LIST *time_zone_tables_used;
sp_head *sphead; sp_head *sphead;
...@@ -802,6 +802,7 @@ typedef struct st_lex ...@@ -802,6 +802,7 @@ typedef struct st_lex
*(table->prev_global= query_tables_last)= table; *(table->prev_global= query_tables_last)= table;
query_tables_last= &table->next_global; query_tables_last= &table->next_global;
} }
bool add_time_zone_tables_to_query_tables(THD *thd);
bool can_be_merged(); bool can_be_merged();
bool can_use_merged(); bool can_use_merged();
...@@ -810,7 +811,6 @@ typedef struct st_lex ...@@ -810,7 +811,6 @@ typedef struct st_lex
bool need_correct_ident(); bool need_correct_ident();
} LEX; } LEX;
extern TABLE_LIST fake_time_zone_tables_list;
struct st_lex_local: public st_lex struct st_lex_local: public st_lex
{ {
static void *operator new(size_t size) static void *operator new(size_t size)
......
...@@ -2123,19 +2123,6 @@ mysql_execute_command(THD *thd) ...@@ -2123,19 +2123,6 @@ mysql_execute_command(THD *thd)
} }
#endif /* !HAVE_REPLICATION */ #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. When option readonly is set deny operations which change tables.
Except for the replication thread and the 'super' users. Except for the replication thread and the 'super' users.
...@@ -3266,6 +3253,27 @@ create_error: ...@@ -3266,6 +3253,27 @@ create_error:
thd->options&= ~(ulong) (OPTION_TABLE_LOCK); thd->options&= ~(ulong) (OPTION_TABLE_LOCK);
thd->in_lock_tables=0; thd->in_lock_tables=0;
break; 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: case SQLCOM_CREATE_DB:
{ {
char *alias; char *alias;
......
...@@ -4269,7 +4269,8 @@ simple_expr: ...@@ -4269,7 +4269,8 @@ simple_expr:
{ $$= create_func_contains($3, $5); } { $$= create_func_contains($3, $5); }
| CONVERT_TZ_SYM '(' expr ',' expr ',' expr ')' | 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); $$= new Item_func_convert_tz($3, $5, $7);
} }
| CURDATE optional_braces | CURDATE optional_braces
...@@ -7307,8 +7308,9 @@ internal_variable_name: ...@@ -7307,8 +7308,9 @@ internal_variable_name:
If this is time_zone variable we should open time zone If this is time_zone variable we should open time zone
describing tables describing tables
*/ */
if (tmp == &sys_time_zone) if (tmp == &sys_time_zone &&
Lex->time_zone_tables_used= &fake_time_zone_tables_list; lex->add_time_zone_tables_to_query_tables(YYTHD))
YYABORT;
} }
else else
{ {
...@@ -7413,8 +7415,8 @@ lock: ...@@ -7413,8 +7415,8 @@ lock:
{ {
Lex->sql_command=SQLCOM_LOCK_TABLES; Lex->sql_command=SQLCOM_LOCK_TABLES;
} }
table_lock_list table_lock_list lock_engine_opt
{} {}
; ;
table_or_tables: table_or_tables:
...@@ -7440,6 +7442,15 @@ lock_option: ...@@ -7440,6 +7442,15 @@ lock_option:
| READ_SYM LOCAL_SYM { $$= TL_READ; } | 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:
UNLOCK_SYM table_or_tables { Lex->sql_command=SQLCOM_UNLOCK_TABLES; } 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) ...@@ -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 Create table list with time zone related tables and add it to the end
of global table list. of global table list.
......
...@@ -64,6 +64,7 @@ extern Time_zone * my_tz_find(const String *name, TABLE_LIST *tz_tables); ...@@ -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 my_bool my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap);
extern void my_tz_free(); 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 Check if we have pointer to the beggining of list of implictly used
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment