Commit 8b3820e2 authored by Marko Mäkelä's avatar Marko Mäkelä

Make the InnoDB FOREIGN KEY parser understand multi-statements. (Bug #48024)

Also make InnoDB thinks that /*/ only starts a comment. (Bug #53644).

This fixes the bugs in the InnoDB Plugin.

ha_innodb.h: Use trx_query_string() instead of trx_query() when
available (MySQL 5.1.42 or later).

innobase_get_stmt(): New function, to retrieve the currently running
SQL statement.

struct trx_struct: Remove mysql_query_str. Use innobase_get_stmt() instead.

dict_strip_comments(): Add and observe the parameter sql_length. Treat
/*/ as the start of a comment.

dict_create_foreign_constraints(), row_table_add_foreign_constraints():
Add the parameter sql_length.
parent b93e7892
CREATE TABLE bug48024(a int PRIMARY KEY,b int NOT NULL,KEY(b)) ENGINE=InnoDB;
CREATE TABLE bug48024_b(b int PRIMARY KEY) ENGINE=InnoDB;
ALTER TABLE bug48024 /*/ADD CONSTRAINT FOREIGN KEY(c) REFERENCES(a),/*/
ADD CONSTRAINT FOREIGN KEY(b) REFERENCES bug48024_b(b);
DROP TABLE bug48024,bug48024_b;
CREATE TABLE bug48024(a int PRIMARY KEY,b int NOT NULL,KEY(b)) ENGINE=InnoDB;
CREATE TABLE bug48024_b(b int PRIMARY KEY) ENGINE=InnoDB;
ALTER TABLE bug48024 /*/ADD CONSTRAINT FOREIGN KEY(c) REFERENCES(a),/*/
ADD CONSTRAINT FOREIGN KEY(b) REFERENCES bug48024_b(b)|
DROP TABLE bug48024,bug48024_b;
# Bug #48024 Innodb doesn't work with multi-statements
--source include/have_innodb_plugin.inc
CREATE TABLE bug48024(a int PRIMARY KEY,b int NOT NULL,KEY(b)) ENGINE=InnoDB;
CREATE TABLE bug48024_b(b int PRIMARY KEY) ENGINE=InnoDB;
# Bug #53644 InnoDB thinks that /*/ starts and ends a comment
ALTER TABLE bug48024 /*/ADD CONSTRAINT FOREIGN KEY(c) REFERENCES(a),/*/
ADD CONSTRAINT FOREIGN KEY(b) REFERENCES bug48024_b(b);
DROP TABLE bug48024,bug48024_b;
delimiter |;
CREATE TABLE bug48024(a int PRIMARY KEY,b int NOT NULL,KEY(b)) ENGINE=InnoDB;
CREATE TABLE bug48024_b(b int PRIMARY KEY) ENGINE=InnoDB;
ALTER TABLE bug48024 /*/ADD CONSTRAINT FOREIGN KEY(c) REFERENCES(a),/*/
ADD CONSTRAINT FOREIGN KEY(b) REFERENCES bug48024_b(b)|
delimiter ;|
DROP TABLE bug48024,bug48024_b;
...@@ -3008,25 +3008,28 @@ static ...@@ -3008,25 +3008,28 @@ static
char* char*
dict_strip_comments( dict_strip_comments(
/*================*/ /*================*/
const char* sql_string) /*!< in: SQL string */ const char* sql_string, /*!< in: SQL string */
size_t sql_length) /*!< in: length of sql_string */
{ {
char* str; char* str;
const char* sptr; const char* sptr;
const char* eptr = sql_string + sql_length;
char* ptr; char* ptr;
/* unclosed quote character (0 if none) */ /* unclosed quote character (0 if none) */
char quote = 0; char quote = 0;
str = mem_alloc(strlen(sql_string) + 1); str = mem_alloc(sql_length + 1);
sptr = sql_string; sptr = sql_string;
ptr = str; ptr = str;
for (;;) { for (;;) {
scan_more: scan_more:
if (*sptr == '\0') { if (sptr >= eptr || *sptr == '\0') {
end_of_string:
*ptr = '\0'; *ptr = '\0';
ut_a(ptr <= str + strlen(sql_string)); ut_a(ptr <= str + sql_length);
return(str); return(str);
} }
...@@ -3045,30 +3048,35 @@ scan_more: ...@@ -3045,30 +3048,35 @@ scan_more:
|| (sptr[0] == '-' && sptr[1] == '-' || (sptr[0] == '-' && sptr[1] == '-'
&& sptr[2] == ' ')) { && sptr[2] == ' ')) {
for (;;) { for (;;) {
if (++sptr >= eptr) {
goto end_of_string;
}
/* In Unix a newline is 0x0A while in Windows /* In Unix a newline is 0x0A while in Windows
it is 0x0D followed by 0x0A */ it is 0x0D followed by 0x0A */
if (*sptr == (char)0x0A switch (*sptr) {
|| *sptr == (char)0x0D case (char) 0X0A:
|| *sptr == '\0') { case (char) 0x0D:
case '\0':
goto scan_more; goto scan_more;
} }
sptr++;
} }
} else if (!quote && *sptr == '/' && *(sptr + 1) == '*') { } else if (!quote && *sptr == '/' && *(sptr + 1) == '*') {
sptr += 2;
for (;;) { for (;;) {
if (*sptr == '*' && *(sptr + 1) == '/') { if (sptr >= eptr) {
goto end_of_string;
sptr += 2;
goto scan_more;
} }
if (*sptr == '\0') { switch (*sptr) {
case '\0':
goto scan_more; goto scan_more;
case '*':
if (sptr[1] == '/') {
sptr += 2;
goto scan_more;
}
} }
sptr++; sptr++;
...@@ -3749,6 +3757,7 @@ dict_create_foreign_constraints( ...@@ -3749,6 +3757,7 @@ dict_create_foreign_constraints(
name before it: test.table2; the name before it: test.table2; the
default database id the database of default database id the database of
parameter name */ parameter name */
size_t sql_length, /*!< in: length of sql_string */
const char* name, /*!< in: table full name in the const char* name, /*!< in: table full name in the
normalized form normalized form
database_name/table_name */ database_name/table_name */
...@@ -3763,7 +3772,7 @@ dict_create_foreign_constraints( ...@@ -3763,7 +3772,7 @@ dict_create_foreign_constraints(
ut_a(trx); ut_a(trx);
ut_a(trx->mysql_thd); ut_a(trx->mysql_thd);
str = dict_strip_comments(sql_string); str = dict_strip_comments(sql_string, sql_length);
heap = mem_heap_create(10000); heap = mem_heap_create(10000);
err = dict_create_foreign_constraints_low( err = dict_create_foreign_constraints_low(
...@@ -3796,6 +3805,7 @@ dict_foreign_parse_drop_constraints( ...@@ -3796,6 +3805,7 @@ dict_foreign_parse_drop_constraints(
dict_foreign_t* foreign; dict_foreign_t* foreign;
ibool success; ibool success;
char* str; char* str;
size_t len;
const char* ptr; const char* ptr;
const char* id; const char* id;
FILE* ef = dict_foreign_err_file; FILE* ef = dict_foreign_err_file;
...@@ -3810,7 +3820,10 @@ dict_foreign_parse_drop_constraints( ...@@ -3810,7 +3820,10 @@ dict_foreign_parse_drop_constraints(
*constraints_to_drop = mem_heap_alloc(heap, 1000 * sizeof(char*)); *constraints_to_drop = mem_heap_alloc(heap, 1000 * sizeof(char*));
str = dict_strip_comments(*(trx->mysql_query_str)); ptr = innobase_get_stmt(trx->mysql_thd, &len);
str = dict_strip_comments(ptr, len);
ptr = str; ptr = str;
ut_ad(mutex_own(&(dict_sys->mutex))); ut_ad(mutex_own(&(dict_sys->mutex)));
......
...@@ -1004,6 +1004,29 @@ innobase_get_charset( ...@@ -1004,6 +1004,29 @@ innobase_get_charset(
return(thd_charset((THD*) mysql_thd)); return(thd_charset((THD*) mysql_thd));
} }
/**********************************************************************//**
Determines the current SQL statement.
@return SQL statement string */
extern "C" UNIV_INTERN
const char*
innobase_get_stmt(
/*==============*/
void* mysql_thd, /*!< in: MySQL thread handle */
size_t* length) /*!< out: length of the SQL statement */
{
#if MYSQL_VERSION_ID >= 50142
LEX_STRING* stmt;
stmt = thd_query_string((THD*) mysql_thd);
*length = stmt->length;
return(stmt->str);
#else
const char* stmt_str = thd_query((THD*) mysql_thd);
*length = strlen(stmt_str);
return(stmt_str);
#endif
}
#if defined (__WIN__) && defined (MYSQL_DYNAMIC_PLUGIN) #if defined (__WIN__) && defined (MYSQL_DYNAMIC_PLUGIN)
extern MYSQL_PLUGIN_IMPORT MY_TMPDIR mysql_tmpdir_list; extern MYSQL_PLUGIN_IMPORT MY_TMPDIR mysql_tmpdir_list;
/*******************************************************************//** /*******************************************************************//**
...@@ -1314,7 +1337,6 @@ innobase_trx_allocate( ...@@ -1314,7 +1337,6 @@ innobase_trx_allocate(
trx = trx_allocate_for_mysql(); trx = trx_allocate_for_mysql();
trx->mysql_thd = thd; trx->mysql_thd = thd;
trx->mysql_query_str = thd_query(thd);
innobase_trx_init(thd, trx); innobase_trx_init(thd, trx);
...@@ -6433,6 +6455,8 @@ ha_innobase::create( ...@@ -6433,6 +6455,8 @@ ha_innobase::create(
/* Cache the value of innodb_file_format, in case it is /* Cache the value of innodb_file_format, in case it is
modified by another thread while the table is being created. */ modified by another thread while the table is being created. */
const ulint file_format = srv_file_format; const ulint file_format = srv_file_format;
const char* stmt;
size_t stmt_len;
DBUG_ENTER("ha_innobase::create"); DBUG_ENTER("ha_innobase::create");
...@@ -6709,9 +6733,11 @@ ha_innobase::create( ...@@ -6709,9 +6733,11 @@ ha_innobase::create(
} }
} }
if (*trx->mysql_query_str) { stmt = innobase_get_stmt(thd, &stmt_len);
error = row_table_add_foreign_constraints(trx,
*trx->mysql_query_str, norm_name, if (stmt) {
error = row_table_add_foreign_constraints(
trx, stmt, stmt_len, norm_name,
create_info->options & HA_LEX_CREATE_TMP_TABLE); create_info->options & HA_LEX_CREATE_TMP_TABLE);
error = convert_error_code_to_mysql(error, flags, NULL); error = convert_error_code_to_mysql(error, flags, NULL);
...@@ -6996,7 +7022,6 @@ innobase_drop_database( ...@@ -6996,7 +7022,6 @@ innobase_drop_database(
/* In the Windows plugin, thd = current_thd is always NULL */ /* In the Windows plugin, thd = current_thd is always NULL */
trx = trx_allocate_for_mysql(); trx = trx_allocate_for_mysql();
trx->mysql_thd = NULL; trx->mysql_thd = NULL;
trx->mysql_query_str = NULL;
#else #else
trx = innobase_trx_allocate(thd); trx = innobase_trx_allocate(thd);
#endif #endif
......
...@@ -231,7 +231,11 @@ the definitions are bracketed with #ifdef INNODB_COMPATIBILITY_HOOKS */ ...@@ -231,7 +231,11 @@ the definitions are bracketed with #ifdef INNODB_COMPATIBILITY_HOOKS */
extern "C" { extern "C" {
struct charset_info_st *thd_charset(MYSQL_THD thd); struct charset_info_st *thd_charset(MYSQL_THD thd);
#if MYSQL_VERSION_ID >= 50142
LEX_STRING *thd_query_string(MYSQL_THD thd);
#else
char **thd_query(MYSQL_THD thd); char **thd_query(MYSQL_THD thd);
#endif
/** Get the file name of the MySQL binlog. /** Get the file name of the MySQL binlog.
* @return the name of the binlog file * @return the name of the binlog file
......
...@@ -352,6 +352,7 @@ dict_create_foreign_constraints( ...@@ -352,6 +352,7 @@ dict_create_foreign_constraints(
name before it: test.table2; the name before it: test.table2; the
default database id the database of default database id the database of
parameter name */ parameter name */
size_t sql_length, /*!< in: length of sql_string */
const char* name, /*!< in: table full name in the const char* name, /*!< in: table full name in the
normalized form normalized form
database_name/table_name */ database_name/table_name */
......
...@@ -215,11 +215,21 @@ innobase_casedn_str( ...@@ -215,11 +215,21 @@ innobase_casedn_str(
/**********************************************************************//** /**********************************************************************//**
Determines the connection character set. Determines the connection character set.
@return connection character set */ @return connection character set */
UNIV_INTERN
struct charset_info_st* struct charset_info_st*
innobase_get_charset( innobase_get_charset(
/*=================*/ /*=================*/
void* mysql_thd); /*!< in: MySQL thread handle */ void* mysql_thd); /*!< in: MySQL thread handle */
/**********************************************************************//**
Determines the current SQL statement.
@return SQL statement string */
UNIV_INTERN
const char*
innobase_get_stmt(
/*==============*/
void* mysql_thd, /*!< in: MySQL thread handle */
size_t* length) /*!< out: length of the SQL statement */
__attribute__((nonnull));
/******************************************************************//** /******************************************************************//**
This function is used to find the storage length in bytes of the first n This function is used to find the storage length in bytes of the first n
characters for prefix indexes using a multibyte character set. The function characters for prefix indexes using a multibyte character set. The function
......
...@@ -403,6 +403,7 @@ row_table_add_foreign_constraints( ...@@ -403,6 +403,7 @@ row_table_add_foreign_constraints(
FOREIGN KEY (a, b) REFERENCES table2(c, d), FOREIGN KEY (a, b) REFERENCES table2(c, d),
table2 can be written also with the table2 can be written also with the
database name before it: test.table2 */ database name before it: test.table2 */
size_t sql_length, /*!< in: length of sql_string */
const char* name, /*!< in: table full name in the const char* name, /*!< in: table full name in the
normalized form normalized form
database_name/table_name */ database_name/table_name */
......
...@@ -560,9 +560,6 @@ struct trx_struct{ ...@@ -560,9 +560,6 @@ struct trx_struct{
/*------------------------------*/ /*------------------------------*/
void* mysql_thd; /*!< MySQL thread handle corresponding void* mysql_thd; /*!< MySQL thread handle corresponding
to this trx, or NULL */ to this trx, or NULL */
char** mysql_query_str;/* pointer to the field in mysqld_thd
which contains the pointer to the
current SQL query string */
const char* mysql_log_file_name; const char* mysql_log_file_name;
/* if MySQL binlog is used, this field /* if MySQL binlog is used, this field
contains a pointer to the latest file contains a pointer to the latest file
......
...@@ -2059,6 +2059,7 @@ row_table_add_foreign_constraints( ...@@ -2059,6 +2059,7 @@ row_table_add_foreign_constraints(
FOREIGN KEY (a, b) REFERENCES table2(c, d), FOREIGN KEY (a, b) REFERENCES table2(c, d),
table2 can be written also with the table2 can be written also with the
database name before it: test.table2 */ database name before it: test.table2 */
size_t sql_length, /*!< in: length of sql_string */
const char* name, /*!< in: table full name in the const char* name, /*!< in: table full name in the
normalized form normalized form
database_name/table_name */ database_name/table_name */
...@@ -2080,8 +2081,8 @@ row_table_add_foreign_constraints( ...@@ -2080,8 +2081,8 @@ row_table_add_foreign_constraints(
trx_set_dict_operation(trx, TRX_DICT_OP_TABLE); trx_set_dict_operation(trx, TRX_DICT_OP_TABLE);
err = dict_create_foreign_constraints(trx, sql_string, name, err = dict_create_foreign_constraints(trx, sql_string, sql_length,
reject_fks); name, reject_fks);
if (err == DB_SUCCESS) { if (err == DB_SUCCESS) {
/* Check that also referencing constraints are ok */ /* Check that also referencing constraints are ok */
err = dict_load_foreigns(name, TRUE); err = dict_load_foreigns(name, TRUE);
......
...@@ -429,6 +429,9 @@ fill_trx_row( ...@@ -429,6 +429,9 @@ fill_trx_row(
which to copy volatile which to copy volatile
strings */ strings */
{ {
const char* stmt;
size_t stmt_len;
row->trx_id = trx_get_id(trx); row->trx_id = trx_get_id(trx);
row->trx_started = (ib_time_t) trx->start_time; row->trx_started = (ib_time_t) trx->start_time;
row->trx_state = trx_get_que_state_str(trx); row->trx_state = trx_get_que_state_str(trx);
...@@ -449,37 +452,32 @@ fill_trx_row( ...@@ -449,37 +452,32 @@ fill_trx_row(
row->trx_weight = (ullint) ut_conv_dulint_to_longlong(TRX_WEIGHT(trx)); row->trx_weight = (ullint) ut_conv_dulint_to_longlong(TRX_WEIGHT(trx));
if (trx->mysql_thd != NULL) { if (trx->mysql_thd == NULL) {
row->trx_mysql_thread_id
= thd_get_thread_id(trx->mysql_thd);
} else {
/* For internal transactions e.g., purge and transactions /* For internal transactions e.g., purge and transactions
being recovered at startup there is no associated MySQL being recovered at startup there is no associated MySQL
thread data structure. */ thread data structure. */
row->trx_mysql_thread_id = 0; row->trx_mysql_thread_id = 0;
row->trx_query = NULL;
return(TRUE);
} }
if (trx->mysql_query_str != NULL && *trx->mysql_query_str != NULL) { row->trx_mysql_thread_id = thd_get_thread_id(trx->mysql_thd);
stmt = innobase_get_stmt(trx->mysql_thd, &stmt_len);
if (strlen(*trx->mysql_query_str) if (stmt != NULL) {
> TRX_I_S_TRX_QUERY_MAX_LEN) {
char query[TRX_I_S_TRX_QUERY_MAX_LEN + 1]; char query[TRX_I_S_TRX_QUERY_MAX_LEN + 1];
memcpy(query, *trx->mysql_query_str, if (stmt_len > TRX_I_S_TRX_QUERY_MAX_LEN) {
TRX_I_S_TRX_QUERY_MAX_LEN); stmt_len = TRX_I_S_TRX_QUERY_MAX_LEN;
query[TRX_I_S_TRX_QUERY_MAX_LEN] = '\0'; }
row->trx_query = ha_storage_put_memlim( memcpy(query, stmt, stmt_len);
cache->storage, query, query[stmt_len] = '\0';
TRX_I_S_TRX_QUERY_MAX_LEN + 1,
MAX_ALLOWED_FOR_STORAGE(cache));
} else {
row->trx_query = ha_storage_put_str_memlim( row->trx_query = ha_storage_put_memlim(
cache->storage, *trx->mysql_query_str, cache->storage, stmt, stmt_len + 1,
MAX_ALLOWED_FOR_STORAGE(cache)); MAX_ALLOWED_FOR_STORAGE(cache));
}
if (row->trx_query == NULL) { if (row->trx_query == NULL) {
......
...@@ -119,7 +119,6 @@ trx_create( ...@@ -119,7 +119,6 @@ trx_create(
trx->table_id = ut_dulint_zero; trx->table_id = ut_dulint_zero;
trx->mysql_thd = NULL; trx->mysql_thd = NULL;
trx->mysql_query_str = NULL;
trx->active_trans = 0; trx->active_trans = 0;
trx->duplicates = 0; trx->duplicates = 0;
...@@ -940,7 +939,6 @@ trx_commit_off_kernel( ...@@ -940,7 +939,6 @@ trx_commit_off_kernel(
trx->rseg = NULL; trx->rseg = NULL;
trx->undo_no = ut_dulint_zero; trx->undo_no = ut_dulint_zero;
trx->last_sql_stat_start.least_undo_no = ut_dulint_zero; trx->last_sql_stat_start.least_undo_no = ut_dulint_zero;
trx->mysql_query_str = NULL;
ut_ad(UT_LIST_GET_LEN(trx->wait_thrs) == 0); ut_ad(UT_LIST_GET_LEN(trx->wait_thrs) == 0);
ut_ad(UT_LIST_GET_LEN(trx->trx_locks) == 0); ut_ad(UT_LIST_GET_LEN(trx->trx_locks) == 0);
......
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