Commit 9d681150 authored by unknown's avatar unknown

WL#5370 Keep forward-compatibility when changing

        'CREATE TABLE IF NOT EXISTS ... SELECT' behaviour
BUG#55474, BUG#55499, BUG#55598, BUG#55616 and BUG#55777 are fixed
in this patch too.

This is the 5.1 part.
It implements:
- if the table exists, binlog two events: CREATE TABLE IF NOT EXISTS
  and INSERT ... SELECT

- Insert nothing and binlog nothing on master if the existing object
  is a view. It only generates a warning that table already exists.


mysql-test/r/trigger.result:
  Ather this patch, 'CREATE TABLE IF NOT EXISTS ... SELECT' will not
  insert anything if the creating table already exists and is a view.
sql/sql_class.h:
  Declare virtual function write_to_binlog() for select_insert.
  It's used to binlog 'create select'
sql/sql_insert.cc:
  Implement write_to_binlog();
  Use write_to_binlog() instead of binlog_query() to binlog the statement.
  if the table exists, binlog two events: CREATE TABLE IF NOT EXISTS
  and INSERT ... SELECT
sql/sql_lex.h:
  Declare create_select_start_with_brace and create_select_pos.
  They are helpful for binlogging 'create select'
sql/sql_parse.cc:
  Do nothing on master if the existing object is a view.
sql/sql_yacc.yy:
  Record the relative postion of 'SELECT' in the 'CREATE ...SELECT' statement.
  Record whether there is a '(' before the 'SELECT' clause.
parent 790852c0
--echo
--echo
connection master;
if ($is_temporary)
{
--let $_temporary=TEMPORARY
}
CREATE TABLE t2(c1 INT, c2 char(10));
INSERT INTO t2 VALUES(1, 'abc'), (2, 'abc');
--echo
--echo # The original query should be binlogged if the table does not exist.
--echo # ------------------------------------------------------------------
--echo
let binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1);
eval CREATE $_temporary TABLE IF NOT EXISTS t1 (c1 INT , c2 INT, c3 char(10), c4 INT KEY)
SELECT 'abc' AS c3, 1 AS c4;
source include/show_binlog_events.inc;
if (!$is_temporary)
{
let $diff_table= test.t1;
source include/rpl_diff_tables.inc;
}
--echo
--echo # The statement should be binlogged as two events. one is
--echo # 'CREATE $_temporary TABLE IF NOT EXISTS ..', another one is
--echo # 'INSERT ... SELECT'.
--echo # ------------------------------------------------------------------
--echo
let binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1);
eval CREATE $_temporary TABLE IF NOT EXISTS t1
SELECT 'abc', 2;
source include/show_binlog_events.inc;
if (!$is_temporary)
{
let $diff_table= test.t1;
source include/rpl_diff_tables.inc;
}
--echo
--echo # Verify if it can be binlogged with right database name when the table
--echo # is not in the default database
--echo
--disable_warnings
DROP DATABASE IF EXISTS db1;
--enable_warnings
CREATE DATABASE db1;
USE db1;
let binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1);
eval CREATE $_temporary TABLE IF NOT EXISTS test.t1
SELECT 'abc', 20;
source include/show_binlog_events.inc;
if (!$is_temporary)
{
let $diff_table= test.t1;
source include/rpl_diff_tables.inc;
}
USE test;
DROP DATABASE db1;
--echo
--echo # It should be binlogged as 'REPLACE ... SELECT'
--echo # if the original statement has option REPLACE
--echo
let binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1);
eval CREATE $_temporary TABLE IF NOT EXISTS t1
REPLACE SELECT '123', 2;
source include/show_binlog_events.inc;
if (!$is_temporary)
{
let $diff_table= test.t1;
source include/rpl_diff_tables.inc;
}
--echo
--echo # It should be binlogged as 'INSERT IGNORE... SELECT'
--echo # if the original statement has option IGNORE
--echo
let binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1);
eval CREATE $_temporary TABLE IF NOT EXISTS t1
IGNORE SELECT '123', 2;
source include/show_binlog_events.inc;
if (!$is_temporary)
{
let $diff_table= test.t1;
source include/rpl_diff_tables.inc;
}
--echo
--echo # Nothing should be binlogged if error happens and no any row is inserted
--echo
let binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1);
--error ER_DUP_ENTRY
eval CREATE $_temporary TABLE IF NOT EXISTS t1
SELECT '123', 2;
source include/show_binlog_events.inc;
if (!$is_temporary)
{
let $diff_table= test.t1;
source include/rpl_diff_tables.inc;
}
--echo
--echo # Verify it can binlog well when there are some braces('(')
--echo
let binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1);
eval CREATE $_temporary TABLE IF NOT EXISTS t1
(SELECT '123', 3) UNION (SELECT '123', 4);
eval CREATE $_temporary TABLE IF NOT EXISTS t1
REPLACE (SELECT 'abc', 3) UNION (SELECT 'abc', 4);
eval CREATE $_temporary TABLE IF NOT EXISTS t1
IGNORE (SELECT '123', 3) UNION (SELECT '123', 4);
source include/show_binlog_events.inc;
if (!$is_temporary)
{
let $diff_table= test.t1;
source include/rpl_diff_tables.inc;
}
if (!$is_temporary)
{
--echo
--echo # Throw a warning that table already exists and don't insert anything
--echo
CREATE VIEW t3 AS SELECT * FROM t2;
let binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1);
CREATE TABLE IF NOT EXISTS t3
SELECT '123', 2;
source include/show_binlog_events.inc;
DROP VIEW t3;
}
--echo
--echo # The statement can be binlogged correctly when it is in a SP/EVENT/TRIGGER
--echo
--disable_warnings
DROP PROCEDURE IF EXISTS p1;
--enable_warnings
eval CREATE PROCEDURE p1(IN a INT)
CREATE $_temporary TABLE IF NOT EXISTS t1 SELECT '123', a;
let binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1);
call p1(500);
call p1(600);
source include/show_binlog_events.inc;
if (!$is_temporary)
{
let $diff_table= test.t1;
source include/rpl_diff_tables.inc;
}
DROP PROCEDURE p1;
--echo
--echo # The statement can be binlogged correctly when it is in a prepared statement
--echo
eval PREPARE stm FROM "CREATE $_temporary TABLE IF NOT EXISTS t1 SELECT '123', ?";
let binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1);
SET @a= 700;
EXECUTE stm USING @a;
SET @a= 800;
EXECUTE stm USING @a;
source include/show_binlog_events.inc;
if (!$is_temporary)
{
let $diff_table= test.t1;
source include/rpl_diff_tables.inc;
}
--echo
--echo # The statement can be binlogged correctly when it is in a conditional comment
--echo
let binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1);
--echo # The whole statement in a conditional comment
eval /*!CREATE $_temporary TABLE IF NOT EXISTS t1
SELECT 'abc', 900*/;
source include/show_binlog_events.inc;
let binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1);
--echo
--echo # There is an long comment before SELECT
eval /*!CREATE $_temporary /*blabla*/ TABLE IF NOT EXISTS t1
SELECT 'abc', 901*/;
source include/show_binlog_events.inc;
let binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1);
--echo
--echo # Conditional comment starts just from SELECT
eval CREATE $_temporary TABLE IF NOT EXISTS t1
/*!SELECT 'abc',*/ 902;
source include/show_binlog_events.inc;
let binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1);
--echo
--echo # Only SELECT keyword is in the conditional comment
eval CREATE $_temporary TABLE IF NOT EXISTS t1
/*!SELECT*/ /*!'abc',*/ 904;
source include/show_binlog_events.inc;
let binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1);
--echo
--echo # Conditional comment is after SELECT keyword
eval CREATE $_temporary TABLE IF NOT EXISTS t1
SELECT /*!'abc',*/ 903;
source include/show_binlog_events.inc;
let binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1);
--echo
--echo # Conditional comment ends just before SELECT keyword
eval /*!CREATE $_temporary TABLE IF NOT EXISTS t1
*/SELECT 'abc', 905;
source include/show_binlog_events.inc;
if (!$is_temporary)
{
let $diff_table= test.t1;
source include/rpl_diff_tables.inc;
}
DROP TABLE t2;
eval DROP $_temporary TABLE t1;
...@@ -33,3 +33,4 @@ while (`SELECT "XX$_servers" <> "XX"`) ...@@ -33,3 +33,4 @@ while (`SELECT "XX$_servers" <> "XX"`)
--source include/diff_tables.inc --source include/diff_tables.inc
connection $_slave; connection $_slave;
} }
connection $_master;
...@@ -1824,11 +1824,8 @@ Note 1050 Table 'v1' already exists ...@@ -1824,11 +1824,8 @@ Note 1050 Table 'v1' already exists
set @id=last_insert_id(); set @id=last_insert_id();
select * from t1; select * from t1;
id operation id operation
1 CREATE TABLE ... SELECT, inserting a new key
select * from t1_op_log; select * from t1_op_log;
operation operation
Before INSERT, new=CREATE TABLE ... SELECT, inserting a new key
After INSERT, new=CREATE TABLE ... SELECT, inserting a new key
truncate t1_op_log; truncate t1_op_log;
create table if not exists v1 replace create table if not exists v1 replace
select @id, "CREATE TABLE ... REPLACE SELECT, deleting a duplicate key"; select @id, "CREATE TABLE ... REPLACE SELECT, deleting a duplicate key";
...@@ -1836,13 +1833,8 @@ Warnings: ...@@ -1836,13 +1833,8 @@ Warnings:
Note 1050 Table 'v1' already exists Note 1050 Table 'v1' already exists
select * from t1; select * from t1;
id operation id operation
1 CREATE TABLE ... REPLACE SELECT, deleting a duplicate key
select * from t1_op_log; select * from t1_op_log;
operation operation
Before INSERT, new=CREATE TABLE ... REPLACE SELECT, deleting a duplicate key
Before DELETE, old=CREATE TABLE ... SELECT, inserting a new key
After DELETE, old=CREATE TABLE ... SELECT, inserting a new key
After INSERT, new=CREATE TABLE ... REPLACE SELECT, deleting a duplicate key
truncate t1; truncate t1;
truncate t1_op_log; truncate t1_op_log;
insert into v1 (id, operation) insert into v1 (id, operation)
......
...@@ -65,3 +65,12 @@ c1 ...@@ -65,3 +65,12 @@ c1
DROP TABLE t1; DROP TABLE t1;
DROP TABLE t2; DROP TABLE t2;
DROP TABLE t3; DROP TABLE t3;
# Bug#55616 Killing thread or query during CREATE IF NOT EXISTS makes
# slave SQL thread abort
CREATE TABLE t1 ( i INT );
CREATE TABLE IF NOT EXISTS t1
AS SELECT SLEEP(3);
KILL QUERY master1;
DROP TABLE t1;
...@@ -19,4 +19,9 @@ master-bin.000001 # Query # # use `test`; CREATE TEMPORARY TABLE IF NOT EXISTS t ...@@ -19,4 +19,9 @@ master-bin.000001 # Query # # use `test`; CREATE TEMPORARY TABLE IF NOT EXISTS t
master-bin.000001 # Query # # use `test`; CREATE TEMPORARY TABLE IF NOT EXISTS tmp1 LIKE tmp master-bin.000001 # Query # # use `test`; CREATE TEMPORARY TABLE IF NOT EXISTS tmp1 LIKE tmp
master-bin.000001 # Query # # use `test`; CREATE TEMPORARY TABLE IF NOT EXISTS tmp1 LIKE tmp master-bin.000001 # Query # # use `test`; CREATE TEMPORARY TABLE IF NOT EXISTS tmp1 LIKE tmp
master-bin.000001 # Query # # use `test`; CREATE TEMPORARY TABLE IF NOT EXISTS tmp2 SELECT * FROM tmp master-bin.000001 # Query # # use `test`; CREATE TEMPORARY TABLE IF NOT EXISTS tmp2 SELECT * FROM tmp
master-bin.000001 # Query # # use `test`; CREATE TEMPORARY TABLE IF NOT EXISTS tmp2 SELECT * FROM tmp master-bin.000001 # Query # # BEGIN
master-bin.000001 # Query # # use `test`; CREATE TEMPORARY TABLE IF NOT EXISTS `tmp2` (
`c1` int(11) DEFAULT NULL
)
master-bin.000001 # Query # # use `test`; INSERT INTO `test`.`tmp2` (`c1`) SELECT * FROM tmp
master-bin.000001 # Query # # COMMIT
...@@ -467,4 +467,10 @@ DROP VIEW IF EXISTS bug48506_t1, bug48506_t2, bug48506_t3; ...@@ -467,4 +467,10 @@ DROP VIEW IF EXISTS bug48506_t1, bug48506_t2, bug48506_t3;
DROP TEMPORARY TABLES t7; DROP TEMPORARY TABLES t7;
DROP TABLES t4, t5; DROP TABLES t4, t5;
DROP TABLES IF EXISTS bug48506_t4; DROP TABLES IF EXISTS bug48506_t4;
CREATE TABLE t1 SELECT 1;
CREATE TABLE IF NOT EXISTS t1 SELECT 1;
Warnings:
Note 1050 Table 't1' already exists
Comparing tables master:test.t1 and slave:test.t1
DROP TABLE t1;
end of the tests end of the tests
This diff is collapsed.
...@@ -119,5 +119,32 @@ SELECT * FROM t2; ...@@ -119,5 +119,32 @@ SELECT * FROM t2;
DROP TABLE t1; DROP TABLE t1;
DROP TABLE t2; DROP TABLE t2;
DROP TABLE t3; DROP TABLE t3;
sync_slave_with_master;
--echo
--echo # Bug#55616 Killing thread or query during CREATE IF NOT EXISTS makes
--echo # slave SQL thread abort
--echo
--connection master1
let $con_id = `SELECT CONNECTION_ID()`;
CREATE TABLE t1 ( i INT );
send CREATE TABLE IF NOT EXISTS t1
AS SELECT SLEEP(3);
connection master;
let $wait_timeout = 3;
let $show_statement = SHOW PROCESSLIST;
let $field = State;
let $condition = = 'User sleep';
source include/wait_show_condition.inc;
--replace_result $con_id master1
eval KILL QUERY $con_id;
sync_slave_with_master;
connection master;
DROP TABLE t1;
source include/master-slave-end.inc; source include/master-slave-end.inc;
...@@ -299,5 +299,18 @@ DROP VIEW IF EXISTS bug48506_t1, bug48506_t2, bug48506_t3; ...@@ -299,5 +299,18 @@ DROP VIEW IF EXISTS bug48506_t1, bug48506_t2, bug48506_t3;
DROP TEMPORARY TABLES t7; DROP TEMPORARY TABLES t7;
DROP TABLES t4, t5; DROP TABLES t4, t5;
DROP TABLES IF EXISTS bug48506_t4; DROP TABLES IF EXISTS bug48506_t4;
sync_slave_with_master;
#
# Bug#55598 RBR: CREATE TABLE IF NOT EXISTS and INSERT written to binary log
# twice
#
connection master;
CREATE TABLE t1 SELECT 1;
CREATE TABLE IF NOT EXISTS t1 SELECT 1;
let $diff_table=test.t1;
source include/rpl_diff_tables.inc;
DROP TABLE t1;
source include/master-slave-end.inc; source include/master-slave-end.inc;
--echo end of the tests --echo end of the tests
#
--echo # WL#5370 Keep forward-compatibility when changing 'CREATE TABLE IF NOT
--echo # EXISTS ... SELECT' behaviour
--echo #
source include/master-slave.inc;
source include/have_binlog_format_statement.inc;
source extra/rpl_tests/rpl_stm_create_if_not_exists.test;
let $is_temporary=1;
source extra/rpl_tests/rpl_stm_create_if_not_exists.test;
source include/master-slave-end.inc;
...@@ -2622,7 +2622,9 @@ class select_dump :public select_to_file { ...@@ -2622,7 +2622,9 @@ class select_dump :public select_to_file {
class select_insert :public select_result_interceptor { class select_insert :public select_result_interceptor {
public: protected:
virtual int write_to_binlog(bool is_trans, int errcode);
public:
TABLE_LIST *table_list; TABLE_LIST *table_list;
TABLE *table; TABLE *table;
List<Item> *fields; List<Item> *fields;
...@@ -2658,6 +2660,8 @@ class select_create: public select_insert { ...@@ -2658,6 +2660,8 @@ class select_create: public select_insert {
MYSQL_LOCK *m_lock; MYSQL_LOCK *m_lock;
/* m_lock or thd->extra_lock */ /* m_lock or thd->extra_lock */
MYSQL_LOCK **m_plock; MYSQL_LOCK **m_plock;
virtual int write_to_binlog(bool is_trans, int errcode);
public: public:
select_create (TABLE_LIST *table_arg, select_create (TABLE_LIST *table_arg,
HA_CREATE_INFO *create_info_par, HA_CREATE_INFO *create_info_par,
...@@ -2673,7 +2677,7 @@ class select_create: public select_insert { ...@@ -2673,7 +2677,7 @@ class select_create: public select_insert {
{} {}
int prepare(List<Item> &list, SELECT_LEX_UNIT *u); int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
int binlog_show_create_table(TABLE **tables, uint count); int binlog_show_create_table(TABLE **tables, uint count, int errcode);
void store_values(List<Item> &values); void store_values(List<Item> &values);
void send_error(uint errcode,const char *err); void send_error(uint errcode,const char *err);
bool send_eof(); bool send_eof();
......
...@@ -3268,7 +3268,7 @@ bool select_insert::send_eof() ...@@ -3268,7 +3268,7 @@ bool select_insert::send_eof()
/* /*
Write to binlog before commiting transaction. No statement will Write to binlog before commiting transaction. No statement will
be written by the binlog_query() below in RBR mode. All the be written by the write_to_binlog() below in RBR mode. All the
events are in the transaction cache and will be written when events are in the transaction cache and will be written when
ha_autocommit_or_rollback() is issued below. ha_autocommit_or_rollback() is issued below.
*/ */
...@@ -3280,9 +3280,8 @@ bool select_insert::send_eof() ...@@ -3280,9 +3280,8 @@ bool select_insert::send_eof()
thd->clear_error(); thd->clear_error();
else else
errcode= query_error_code(thd, killed_status == THD::NOT_KILLED); errcode= query_error_code(thd, killed_status == THD::NOT_KILLED);
if (thd->binlog_query(THD::ROW_QUERY_TYPE,
thd->query(), thd->query_length(), if (write_to_binlog(trans_table, errcode))
trans_table, FALSE, errcode))
{ {
table->file->ha_release_auto_increment(); table->file->ha_release_auto_increment();
DBUG_RETURN(1); DBUG_RETURN(1);
...@@ -3356,9 +3355,7 @@ void select_insert::abort() { ...@@ -3356,9 +3355,7 @@ void select_insert::abort() {
{ {
int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED); int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED);
/* error of writing binary log is ignored */ /* error of writing binary log is ignored */
(void) thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query(), write_to_binlog(transactional_table, errcode);
thd->query_length(),
transactional_table, FALSE, errcode);
} }
if (!thd->current_stmt_binlog_row_based && !can_rollback_data()) if (!thd->current_stmt_binlog_row_based && !can_rollback_data())
thd->transaction.all.modified_non_trans_table= TRUE; thd->transaction.all.modified_non_trans_table= TRUE;
...@@ -3373,6 +3370,103 @@ void select_insert::abort() { ...@@ -3373,6 +3370,103 @@ void select_insert::abort() {
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
int select_insert::write_to_binlog(bool is_trans, int errcode)
{
/* It is only for statement mode */
if (thd->current_stmt_binlog_row_based)
return 0;
return thd->binlog_query(THD::ROW_QUERY_TYPE,
thd->query(), thd->query_length(),
is_trans, FALSE, errcode);
}
/* Override the select_insert::write_to_binlog */
int select_create::write_to_binlog(bool is_trans, int errcode)
{
/* It is only for statement mode */
if (thd->current_stmt_binlog_row_based)
return 0;
/*
WL#5370 Keep the compatibility between 5.1 master and 5.5 slave.
Binlog a 'INSERT ... SELECT' statement only when it has the option
'IF NOT EXISTS' and the table already exists as a base table.
*/
if ((create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS) &&
create_info->table_existed)
{
String query;
int result;
thd->binlog_start_trans_and_stmt();
/* Binlog the CREATE TABLE IF NOT EXISTS statement */
result= binlog_show_create_table(&table, 1, 0);
if (result)
return result;
uint db_len= strlen(create_table->db);
uint table_len= strlen(create_info->alias);
uint select_len= thd->query_length() - thd->lex->create_select_pos;
uint field_len= (table->s->fields - (field - table->field)) *
(MAX_FIELD_NAME + 3);
/*
pre-allocating memory reduces the times of reallocating memory,
when calling query.appen().
40bytes is enough for other words("INSERT IGNORE INTO", etc.).
*/
if (query.real_alloc(40 + db_len + table_len + field_len + select_len))
return 1;
if (thd->lex->create_select_in_comment)
query.append(STRING_WITH_LEN("/*! "));
if (thd->lex->ignore)
query.append(STRING_WITH_LEN("INSERT IGNORE INTO `"));
else if (thd->lex->duplicates == DUP_REPLACE)
query.append(STRING_WITH_LEN("REPLACE INTO `"));
else
query.append(STRING_WITH_LEN("INSERT INTO `"));
query.append(create_table->db, db_len);
query.append(STRING_WITH_LEN("`.`"));
query.append(create_info->alias, table_len);
query.append(STRING_WITH_LEN("` "));
/*
The insert items.
Field is the the rightmost columns that the rows are inster in.
*/
query.append(STRING_WITH_LEN("("));
for (Field **f= field ; *f ; f++)
{
if (f != field)
query.append(STRING_WITH_LEN(","));
query.append(STRING_WITH_LEN("`"));
query.append((*f)->field_name, strlen((*f)->field_name));
query.append(STRING_WITH_LEN("`"));
}
query.append(STRING_WITH_LEN(") "));
/* The SELECT clause*/
DBUG_ASSERT(thd->lex->create_select_pos);
if (thd->lex->create_select_start_with_brace)
query.append(STRING_WITH_LEN("("));
if (query.append(thd->query() + thd->lex->create_select_pos, select_len))
return 1;
/*
Avoid to use thd->binlog_query() twice, otherwise it will print the unsafe
warning twice.
*/
Query_log_event ev(thd, query.c_ptr_safe(), query.length(), is_trans,
FALSE, errcode);
return mysql_bin_log.write(&ev);
}
else
return select_insert::write_to_binlog(is_trans, errcode);
}
/*************************************************************************** /***************************************************************************
CREATE TABLE (SELECT) ... CREATE TABLE (SELECT) ...
...@@ -3613,7 +3707,8 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u) ...@@ -3613,7 +3707,8 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
!table->s->tmp_table && !table->s->tmp_table &&
!ptr->get_create_info()->table_existed) !ptr->get_create_info()->table_existed)
{ {
if (int error= ptr->binlog_show_create_table(tables, count)) int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED);
if (int error= ptr->binlog_show_create_table(tables, count, errcode))
return error; return error;
} }
return 0; return 0;
...@@ -3654,7 +3749,10 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u) ...@@ -3654,7 +3749,10 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR), ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR),
create_table->table_name); create_table->table_name);
if (thd->current_stmt_binlog_row_based) if (thd->current_stmt_binlog_row_based)
binlog_show_create_table(&(create_table->table), 1); {
int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED);
binlog_show_create_table(&(create_table->table), 1, errcode);
}
table= create_table->table; table= create_table->table;
} }
else else
...@@ -3722,10 +3820,10 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u) ...@@ -3722,10 +3820,10 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
} }
int int
select_create::binlog_show_create_table(TABLE **tables, uint count) select_create::binlog_show_create_table(TABLE **tables, uint count, int errcode)
{ {
/* /*
Note 1: In RBR mode, we generate a CREATE TABLE statement for the Note 1: We generate a CREATE TABLE statement for the
created table by calling store_create_info() (behaves as SHOW created table by calling store_create_info() (behaves as SHOW
CREATE TABLE). In the event of an error, nothing should be CREATE TABLE). In the event of an error, nothing should be
written to the binary log, even if the table is non-transactional; written to the binary log, even if the table is non-transactional;
...@@ -3741,7 +3839,6 @@ select_create::binlog_show_create_table(TABLE **tables, uint count) ...@@ -3741,7 +3839,6 @@ select_create::binlog_show_create_table(TABLE **tables, uint count)
schema that will do a close_thread_tables(), destroying the schema that will do a close_thread_tables(), destroying the
statement transaction cache. statement transaction cache.
*/ */
DBUG_ASSERT(thd->current_stmt_binlog_row_based);
DBUG_ASSERT(tables && *tables && count > 0); DBUG_ASSERT(tables && *tables && count > 0);
char buf[2048]; char buf[2048];
...@@ -3759,7 +3856,6 @@ select_create::binlog_show_create_table(TABLE **tables, uint count) ...@@ -3759,7 +3856,6 @@ select_create::binlog_show_create_table(TABLE **tables, uint count)
if (mysql_bin_log.is_open()) if (mysql_bin_log.is_open())
{ {
int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED);
result= thd->binlog_query(THD::STMT_QUERY_TYPE, result= thd->binlog_query(THD::STMT_QUERY_TYPE,
query.ptr(), query.length(), query.ptr(), query.length(),
/* is_trans */ TRUE, /* is_trans */ TRUE,
......
...@@ -1817,6 +1817,23 @@ typedef struct st_lex : public Query_tables_list ...@@ -1817,6 +1817,23 @@ typedef struct st_lex : public Query_tables_list
*/ */
bool protect_against_global_read_lock; bool protect_against_global_read_lock;
/*
The following three variables are used in 'CREATE TABLE IF NOT EXISTS ...
SELECT' statement. They are used to binlog the statement.
create_select_start_with_brace will be set if there is a '(' before
the first SELECT clause
create_select_pos records the relative position of the SELECT clause
in the whole statement.
create_select_in_comment will be set if SELECT keyword is in conditional
comment.
*/
bool create_select_start_with_brace;
uint create_select_pos;
bool create_select_in_comment;
st_lex(); st_lex();
virtual ~st_lex() virtual ~st_lex()
......
...@@ -2717,6 +2717,25 @@ mysql_execute_command(THD *thd) ...@@ -2717,6 +2717,25 @@ mysql_execute_command(THD *thd)
{ {
TABLE_LIST *duplicate; TABLE_LIST *duplicate;
create_table= lex->unlink_first_table(&link_to_local); create_table= lex->unlink_first_table(&link_to_local);
if (create_table->view)
{
if (create_info.options & HA_LEX_CREATE_IF_NOT_EXISTS)
{
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_TABLE_EXISTS_ERROR,
ER(ER_TABLE_EXISTS_ERROR),
create_info.alias);
my_ok(thd);
}
else
{
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), create_info.alias);
res= 1;
}
goto end_with_restore_list;
}
if ((duplicate= unique_table(thd, create_table, select_tables, 0))) if ((duplicate= unique_table(thd, create_table, select_tables, 0)))
{ {
update_non_unique_table_error(create_table, "CREATE", duplicate); update_non_unique_table_error(create_table, "CREATE", duplicate);
......
...@@ -3881,17 +3881,26 @@ create2a: ...@@ -3881,17 +3881,26 @@ create2a:
create3 {} create3 {}
| opt_partitioning | opt_partitioning
create_select ')' create_select ')'
{ Select->set_braces(1);} {
Select->set_braces(1);
Lex->create_select_start_with_brace= TRUE;
}
union_opt {} union_opt {}
; ;
create3: create3:
/* empty */ {} /* empty */ {}
| opt_duplicate opt_as create_select | opt_duplicate opt_as create_select
{ Select->set_braces(0);} {
Select->set_braces(0);
Lex->create_select_start_with_brace= FALSE;
}
union_clause {} union_clause {}
| opt_duplicate opt_as '(' create_select ')' | opt_duplicate opt_as '(' create_select ')'
{ Select->set_braces(1);} {
Select->set_braces(1);
Lex->create_select_start_with_brace= TRUE;
}
union_opt {} union_opt {}
; ;
...@@ -4516,6 +4525,19 @@ create_select: ...@@ -4516,6 +4525,19 @@ create_select:
lex->current_select->table_list.save_and_clear(&lex->save_list); lex->current_select->table_list.save_and_clear(&lex->save_list);
mysql_init_select(lex); mysql_init_select(lex);
lex->current_select->parsing_place= SELECT_LIST; lex->current_select->parsing_place= SELECT_LIST;
if (lex->sql_command == SQLCOM_CREATE_TABLE &&
(lex->create_info.options & HA_LEX_CREATE_IF_NOT_EXISTS))
{
Lex_input_stream *lip= YYLIP;
if (lex->spcont)
lex->create_select_pos= lip->get_tok_start() -
lex->sphead->m_tmp_query;
else
lex->create_select_pos= lip->get_tok_start() - lip->get_buf();
lex->create_select_in_comment= (lip->in_comment == DISCARD_COMMENT);
}
} }
select_options select_item_list select_options select_item_list
{ {
......
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