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(
/* out, own: SQL string stripped from
comments; the caller must free this
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;
const char* sptr;
const char* eptr = sql_string + sql_length;
char* ptr;
/* unclosed quote character (0 if none) */
char quote = 0;
str = mem_alloc(strlen(sql_string) + 1);
str = mem_alloc(sql_length + 1);
sptr = sql_string;
ptr = str;
for (;;) {
scan_more:
if (*sptr == '\0') {
if (sptr >= eptr || *sptr == '\0') {
end_of_string:
*ptr = '\0';
ut_a(ptr <= str + strlen(sql_string));
ut_a(ptr <= str + sql_length);
return(str);
}
......@@ -2623,30 +2626,35 @@ dict_strip_comments(
|| (sptr[0] == '-' && sptr[1] == '-'
&& sptr[2] == ' ')) {
for (;;) {
if (++sptr >= eptr) {
goto end_of_string;
}
/* In Unix a newline is 0x0A while in Windows
it is 0x0D followed by 0x0A */
if (*sptr == (char)0x0A
|| *sptr == (char)0x0D
|| *sptr == '\0') {
switch (*sptr) {
case (char) 0X0A:
case (char) 0x0D:
case '\0':
goto scan_more;
}
sptr++;
}
} else if (!quote && *sptr == '/' && *(sptr + 1) == '*') {
sptr += 2;
for (;;) {
if (*sptr == '*' && *(sptr + 1) == '/') {
sptr += 2;
goto scan_more;
if (sptr >= eptr) {
goto end_of_string;
}
if (*sptr == '\0') {
switch (*sptr) {
case '\0':
goto scan_more;
case '*':
if (sptr[1] == '/') {
sptr += 2;
goto scan_more;
}
}
sptr++;
......@@ -3348,6 +3356,7 @@ dict_create_foreign_constraints(
name before it: test.table2; the
default database id the database of
parameter name */
size_t sql_length, /* in: length of sql_string */
const char* name, /* in: table full name in the
normalized form
database_name/table_name */
......@@ -3362,7 +3371,7 @@ dict_create_foreign_constraints(
ut_a(trx);
ut_a(trx->mysql_thd);
str = dict_strip_comments(sql_string);
str = dict_strip_comments(sql_string, sql_length);
heap = mem_heap_create(10000);
err = dict_create_foreign_constraints_low(
......@@ -3411,7 +3420,8 @@ dict_foreign_parse_drop_constraints(
*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;
ut_ad(mutex_own(&(dict_sys->mutex)));
......
......@@ -1140,6 +1140,15 @@ innobase_next_autoinc(
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
an InnoDB transaction struct if the corresponding MySQL thread struct still
......@@ -1160,7 +1169,7 @@ check_trx_exists(
trx = trx_allocate_for_mysql();
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
CPU time */
......@@ -5578,7 +5587,7 @@ ha_innobase::create(
trx = trx_allocate_for_mysql();
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)) {
trx->check_foreigns = FALSE;
......@@ -5674,8 +5683,10 @@ ha_innobase::create(
}
if (*trx->mysql_query_str) {
error = row_table_add_foreign_constraints(trx,
*trx->mysql_query_str, norm_name,
error = row_table_add_foreign_constraints(
trx,
*trx->mysql_query_str, *trx->mysql_query_len,
norm_name,
create_info->options & HA_LEX_CREATE_TMP_TABLE);
error = convert_error_code_to_mysql(error, NULL);
......@@ -5866,7 +5877,7 @@ ha_innobase::delete_table(
trx = trx_allocate_for_mysql();
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)) {
trx->check_foreigns = FALSE;
......@@ -5955,7 +5966,7 @@ innobase_drop_database(
#endif
trx = trx_allocate_for_mysql();
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)) {
trx->check_foreigns = FALSE;
......@@ -6025,7 +6036,7 @@ ha_innobase::rename_table(
trx = trx_allocate_for_mysql();
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)) {
trx->check_foreigns = FALSE;
......
......@@ -210,7 +210,7 @@ the definitions are bracketed with #ifdef INNODB_COMPATIBILITY_HOOKS */
extern "C" {
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.
* @return the name of the binlog file
......
......@@ -309,6 +309,7 @@ dict_create_foreign_constraints(
name before it: test.table2; the
default database id the database of
parameter name */
size_t sql_length, /* in: length of sql_string */
const char* name, /* in: table full name in the
normalized form
database_name/table_name */
......
......@@ -366,6 +366,7 @@ row_table_add_foreign_constraints(
FOREIGN KEY (a, b) REFERENCES table2(c, d),
table2 can be written also with the
database name before it: test.table2 */
size_t sql_length, /* in: length of sql_string */
const char* name, /* in: table full name in the
normalized form
database_name/table_name */
......
......@@ -444,6 +444,8 @@ struct trx_struct{
char** mysql_query_str;/* pointer to the field in mysqld_thd
which contains the pointer to the
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;
/* if MySQL binlog is used, this field
contains a pointer to the latest file
......
......@@ -2103,6 +2103,7 @@ row_table_add_foreign_constraints(
FOREIGN KEY (a, b) REFERENCES table2(c, d),
table2 can be written also with the
database name before it: test.table2 */
size_t sql_length, /* in: length of sql_string */
const char* name, /* in: table full name in the
normalized form
database_name/table_name */
......@@ -2124,8 +2125,8 @@ row_table_add_foreign_constraints(
trx->dict_operation = TRUE;
err = dict_create_foreign_constraints(trx, sql_string, name,
reject_fks);
err = dict_create_foreign_constraints(trx, sql_string, sql_length,
name, reject_fks);
if (err == DB_SUCCESS) {
/* Check that also referencing constraints are ok */
......
......@@ -131,6 +131,8 @@ trx_create(
trx->mysql_thd = NULL;
trx->mysql_query_str = NULL;
trx->mysql_query_len = NULL;
trx->active_trans = 0;
trx->duplicates = 0;
......@@ -936,6 +938,7 @@ trx_commit_off_kernel(
trx->undo_no = ut_dulint_zero;
trx->last_sql_stat_start.least_undo_no = ut_dulint_zero;
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->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