Commit ec36b373 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).

struct trx_struct: Add mysql_query_len.

ha_innodb.cc: Use trx_query_string() instead of trx_query() and
initialize trx->mysql_query_len.

INNOBASE_COPY_STMT(thd, trx): New macro, to initialize
trx->mysql_query_str and trx->mysql_query_len.

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 5f15d5cb
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.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;
...@@ -2586,25 +2586,28 @@ dict_strip_comments( ...@@ -2586,25 +2586,28 @@ dict_strip_comments(
/* out, own: SQL string stripped from /* out, own: SQL string stripped from
comments; the caller must free this comments; the caller must free this
with mem_free()! */ with mem_free()! */
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);
} }
...@@ -2623,30 +2626,35 @@ dict_strip_comments( ...@@ -2623,30 +2626,35 @@ dict_strip_comments(
|| (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;
}
switch (*sptr) {
case '\0':
goto scan_more;
case '*':
if (sptr[1] == '/') {
sptr += 2; sptr += 2;
goto scan_more; goto scan_more;
} }
if (*sptr == '\0') {
goto scan_more;
} }
sptr++; sptr++;
...@@ -3348,6 +3356,7 @@ dict_create_foreign_constraints( ...@@ -3348,6 +3356,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 */
...@@ -3362,7 +3371,7 @@ dict_create_foreign_constraints( ...@@ -3362,7 +3371,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(
...@@ -3411,7 +3420,8 @@ dict_foreign_parse_drop_constraints( ...@@ -3411,7 +3420,8 @@ 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)); str = dict_strip_comments(*trx->mysql_query_str,
*trx->mysql_query_len);
ptr = str; ptr = str;
ut_ad(mutex_own(&(dict_sys->mutex))); ut_ad(mutex_own(&(dict_sys->mutex)));
......
...@@ -1140,6 +1140,15 @@ innobase_next_autoinc( ...@@ -1140,6 +1140,15 @@ innobase_next_autoinc(
return(next_value); return(next_value);
} }
/** Copy the current SQL statement.
* @param[in] thd MySQL client connection
* @param[in/out] trx InnoDB transaction */
#define INNOBASE_COPY_STMT(thd, trx) do { \
LEX_STRING* stmt = thd_query_string(thd); \
(trx)->mysql_query_str = &stmt->str; \
(trx)->mysql_query_len = &stmt->length; \
} while (0)
/************************************************************************* /*************************************************************************
Gets the InnoDB transaction handle for a MySQL handler object, creates Gets the InnoDB transaction handle for a MySQL handler object, creates
an InnoDB transaction struct if the corresponding MySQL thread struct still an InnoDB transaction struct if the corresponding MySQL thread struct still
...@@ -1160,7 +1169,7 @@ check_trx_exists( ...@@ -1160,7 +1169,7 @@ check_trx_exists(
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_COPY_STMT(thd, trx);
/* Update the info whether we should skip XA steps that eat /* Update the info whether we should skip XA steps that eat
CPU time */ CPU time */
...@@ -5578,7 +5587,7 @@ ha_innobase::create( ...@@ -5578,7 +5587,7 @@ ha_innobase::create(
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_COPY_STMT(thd, trx);
if (thd_test_options(thd, OPTION_NO_FOREIGN_KEY_CHECKS)) { if (thd_test_options(thd, OPTION_NO_FOREIGN_KEY_CHECKS)) {
trx->check_foreigns = FALSE; trx->check_foreigns = FALSE;
...@@ -5674,8 +5683,10 @@ ha_innobase::create( ...@@ -5674,8 +5683,10 @@ ha_innobase::create(
} }
if (*trx->mysql_query_str) { if (*trx->mysql_query_str) {
error = row_table_add_foreign_constraints(trx, error = row_table_add_foreign_constraints(
*trx->mysql_query_str, norm_name, trx,
*trx->mysql_query_str, *trx->mysql_query_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, NULL); error = convert_error_code_to_mysql(error, NULL);
...@@ -5866,7 +5877,7 @@ ha_innobase::delete_table( ...@@ -5866,7 +5877,7 @@ ha_innobase::delete_table(
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_COPY_STMT(thd, trx);
if (thd_test_options(thd, OPTION_NO_FOREIGN_KEY_CHECKS)) { if (thd_test_options(thd, OPTION_NO_FOREIGN_KEY_CHECKS)) {
trx->check_foreigns = FALSE; trx->check_foreigns = FALSE;
...@@ -5955,7 +5966,7 @@ innobase_drop_database( ...@@ -5955,7 +5966,7 @@ innobase_drop_database(
#endif #endif
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_COPY_STMT(thd, trx);
if (thd_test_options(thd, OPTION_NO_FOREIGN_KEY_CHECKS)) { if (thd_test_options(thd, OPTION_NO_FOREIGN_KEY_CHECKS)) {
trx->check_foreigns = FALSE; trx->check_foreigns = FALSE;
...@@ -6025,7 +6036,7 @@ ha_innobase::rename_table( ...@@ -6025,7 +6036,7 @@ ha_innobase::rename_table(
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_COPY_STMT(thd, trx);
if (thd_test_options(thd, OPTION_NO_FOREIGN_KEY_CHECKS)) { if (thd_test_options(thd, OPTION_NO_FOREIGN_KEY_CHECKS)) {
trx->check_foreigns = FALSE; trx->check_foreigns = FALSE;
......
...@@ -210,7 +210,7 @@ the definitions are bracketed with #ifdef INNODB_COMPATIBILITY_HOOKS */ ...@@ -210,7 +210,7 @@ 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);
char **thd_query(MYSQL_THD thd); LEX_STRING *thd_query_string(MYSQL_THD thd);
/** 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
......
...@@ -309,6 +309,7 @@ dict_create_foreign_constraints( ...@@ -309,6 +309,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 */
......
...@@ -366,6 +366,7 @@ row_table_add_foreign_constraints( ...@@ -366,6 +366,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 */
......
...@@ -444,6 +444,8 @@ struct trx_struct{ ...@@ -444,6 +444,8 @@ struct trx_struct{
char** mysql_query_str;/* pointer to the field in mysqld_thd char** mysql_query_str;/* pointer to the field in mysqld_thd
which contains the pointer to the which contains the pointer to the
current SQL query string */ current SQL query string */
size_t* mysql_query_len;/* pointer to the length of 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
......
...@@ -2103,6 +2103,7 @@ row_table_add_foreign_constraints( ...@@ -2103,6 +2103,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 */
...@@ -2124,8 +2125,8 @@ row_table_add_foreign_constraints( ...@@ -2124,8 +2125,8 @@ row_table_add_foreign_constraints(
trx->dict_operation = TRUE; trx->dict_operation = TRUE;
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 */
......
...@@ -131,6 +131,8 @@ trx_create( ...@@ -131,6 +131,8 @@ trx_create(
trx->mysql_thd = NULL; trx->mysql_thd = NULL;
trx->mysql_query_str = NULL; trx->mysql_query_str = NULL;
trx->mysql_query_len = NULL;
trx->active_trans = 0; trx->active_trans = 0;
trx->duplicates = 0; trx->duplicates = 0;
...@@ -936,6 +938,7 @@ trx_commit_off_kernel( ...@@ -936,6 +938,7 @@ trx_commit_off_kernel(
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; trx->mysql_query_str = NULL;
trx->mysql_query_len = 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