mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
Merge 10.4 into 10.5
This commit is contained in:
@ -32,8 +32,8 @@ COUNT(*)=8
|
||||
# Note: We only look for 00 because the 5c only served as an escape
|
||||
# in parsing.
|
||||
#
|
||||
# MYSQL_DUMP test tb --hex-blob | grep INSERT > MYSQL_TMP_DIR/dump.sql
|
||||
FOUND 10 /00/ in dump.sql
|
||||
# MYSQL_DUMP test tb --hex-blob > MYSQL_TMP_DIR/dump.sql
|
||||
FOUND 8 /\([0-9]+,0x([1-9][0-9])*00([1-9][0-9])*\)/ in dump.sql
|
||||
#
|
||||
# Ensure data consistency on mysqlbinlog replay
|
||||
#
|
||||
|
@ -114,9 +114,9 @@ SELECT COUNT(*)=8 from tb;
|
||||
--echo # Note: We only look for 00 because the 5c only served as an escape
|
||||
--echo # in parsing.
|
||||
--echo #
|
||||
--echo # MYSQL_DUMP test tb --hex-blob | grep INSERT > MYSQL_TMP_DIR/dump.sql
|
||||
--exec $MYSQL_DUMP test tb --hex-blob | grep INSERT > $MYSQL_TMP_DIR/dump.sql
|
||||
--let SEARCH_PATTERN= 00
|
||||
--echo # MYSQL_DUMP test tb --hex-blob > MYSQL_TMP_DIR/dump.sql
|
||||
--exec $MYSQL_DUMP test tb --hex-blob > $MYSQL_TMP_DIR/dump.sql
|
||||
--let SEARCH_PATTERN= \([0-9]+,0x([1-9][0-9])*00([1-9][0-9])*\)
|
||||
--let SEARCH_FILE= $MYSQL_TMP_DIR/dump.sql
|
||||
--source include/search_pattern_in_file.inc
|
||||
|
||||
|
@ -25,6 +25,7 @@ t1 CREATE TABLE `t1` (
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t1(f1 INT, f2 INT AS (f1) VIRTUAL)ENGINE=InnoDB;
|
||||
INSERT INTO t1(f1) VALUES(1);
|
||||
SET DEBUG_DBUG="+d,create_index_fail";
|
||||
SET DEBUG_SYNC="innodb_inplace_alter_table_enter SIGNAL con1_go WAIT_FOR alter_signal";
|
||||
ALTER TABLE t1 ADD COLUMN f3 INT AS (f1) VIRTUAL, ADD INDEX(f2, f3);
|
||||
@ -33,6 +34,7 @@ SET DEBUG_SYNC="now WAIT_FOR con1_go";
|
||||
BEGIN;
|
||||
SELECT * FROM t1;
|
||||
f1 f2
|
||||
1 1
|
||||
SET DEBUG_SYNC="now SIGNAL alter_signal";
|
||||
connection default;
|
||||
ERROR 23000: Duplicate entry '' for key '*UNKNOWN*'
|
||||
@ -47,6 +49,7 @@ t1 CREATE TABLE `t1` (
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t1(f1 INT, f2 INT AS (f1) VIRTUAL)ENGINE=InnoDB;
|
||||
INSERT INTO t1(f1) VALUES(1);
|
||||
SET DEBUG_DBUG="+d,create_index_fail";
|
||||
SET DEBUG_SYNC="innodb_inplace_alter_table_enter SIGNAL con1_go WAIT_FOR alter_signal";
|
||||
ALTER TABLE t1 ADD INDEX(f2);
|
||||
|
@ -29,6 +29,7 @@ DROP TABLE t1;
|
||||
# new_vcol_info in index when rollback of alter happens
|
||||
|
||||
CREATE TABLE t1(f1 INT, f2 INT AS (f1) VIRTUAL)ENGINE=InnoDB;
|
||||
INSERT INTO t1(f1) VALUES(1);
|
||||
SET DEBUG_DBUG="+d,create_index_fail";
|
||||
SET DEBUG_SYNC="innodb_inplace_alter_table_enter SIGNAL con1_go WAIT_FOR alter_signal";
|
||||
SEND ALTER TABLE t1 ADD COLUMN f3 INT AS (f1) VIRTUAL, ADD INDEX(f2, f3);
|
||||
@ -47,6 +48,7 @@ SHOW CREATE TABLE t1;
|
||||
DROP TABLE t1;
|
||||
|
||||
CREATE TABLE t1(f1 INT, f2 INT AS (f1) VIRTUAL)ENGINE=InnoDB;
|
||||
INSERT INTO t1(f1) VALUES(1);
|
||||
SET DEBUG_DBUG="+d,create_index_fail";
|
||||
SET DEBUG_SYNC="innodb_inplace_alter_table_enter SIGNAL con1_go WAIT_FOR alter_signal";
|
||||
send ALTER TABLE t1 ADD INDEX(f2);
|
||||
|
@ -34,6 +34,7 @@ CREATE TABLE t1(f1 INT, f2 INT,
|
||||
PRIMARY KEY(f1, f2),
|
||||
UNIQUE INDEX uidx2 (f1, f2),
|
||||
UNIQUE INDEX uidx1 (f2))ENGINE=InnoDB;
|
||||
INSERT INTO t1 VALUES(2, 2);
|
||||
ALTER TABLE t1 DROP PRIMARY KEY;
|
||||
SHOW CREATE TABLE t1;
|
||||
Table Create Table
|
||||
@ -66,6 +67,7 @@ test.t1 check status OK
|
||||
DROP TABLE t1;
|
||||
SET SQL_MODE= strict_trans_tables;
|
||||
CREATE TABLE t1(a INT UNIQUE) ENGINE=InnoDB;
|
||||
INSERT INTO t1 VALUES(1);
|
||||
SET DEBUG_SYNC='row_log_table_apply1_before SIGNAL dml WAIT_FOR dml_done';
|
||||
ALTER TABLE t1 MODIFY COLUMN a INT NOT NULL;
|
||||
connection con1;
|
||||
|
@ -80,6 +80,7 @@ SET DEBUG_SYNC='RESET';
|
||||
#
|
||||
CREATE TABLE t1 (f VARCHAR(8) CHARACTER SET latin1 COLLATE latin1_swedish_ci)
|
||||
ENGINE=InnoDB;
|
||||
INSERT INTO t1 VALUES('ZERO');
|
||||
connection con1;
|
||||
SET DEBUG_SYNC = 'row_log_table_apply1_before SIGNAL scanned WAIT_FOR insert_done';
|
||||
ALTER TABLE t1 MODIFY f VARCHAR(256) COLLATE latin1_german2_ci NOT NULL;
|
||||
@ -96,5 +97,6 @@ ALTER TABLE t1 CHANGE f eins VARCHAR(257) COLLATE latin1_german1_ci NOT NULL,
|
||||
ALGORITHM=INSTANT;
|
||||
SELECT * FROM t1;
|
||||
eins
|
||||
ZERO
|
||||
one
|
||||
DROP TABLE t1;
|
||||
|
@ -4,6 +4,7 @@
|
||||
#
|
||||
CREATE TABLE t0 (pk INT PRIMARY KEY) ENGINE=InnoDB;
|
||||
CREATE TABLE t1 (c CHAR(2) NOT NULL) ENGINE=InnoDB;
|
||||
INSERT INTO t1 VALUES('cd');
|
||||
connect con1,localhost,root,,;
|
||||
BEGIN;
|
||||
INSERT INTO t0 VALUES(1);
|
||||
@ -21,6 +22,7 @@ ERROR 23000: Duplicate entry 'a' for key 'PRIMARY'
|
||||
SET DEBUG_SYNC='RESET';
|
||||
SELECT * FROM t1;
|
||||
c
|
||||
cd
|
||||
ab
|
||||
ac
|
||||
DROP TABLE t0,t1;
|
||||
|
@ -1,5 +1,6 @@
|
||||
CREATE TABLE t0 (pk INT PRIMARY KEY) ENGINE=InnoDB;
|
||||
CREATE TABLE t1 (pk INT PRIMARY KEY, b INT) ENGINE=InnoDB;
|
||||
INSERT INTO t1 VALUES(100, 100);
|
||||
connect con1,localhost,root,,test;
|
||||
BEGIN;
|
||||
INSERT INTO t0 SET pk=1;
|
||||
|
@ -61,7 +61,7 @@ connect con1,localhost,root,,;
|
||||
BEGIN;
|
||||
DELETE FROM mysql.innodb_table_stats;
|
||||
connect con2,localhost,root,,;
|
||||
SET DEBUG_SYNC='inplace_after_index_build SIGNAL blocked WAIT_FOR ever';
|
||||
SET DEBUG_SYNC='innodb_inplace_alter_table_enter SIGNAL blocked WAIT_FOR ever';
|
||||
ALTER TABLE t1 FORCE;
|
||||
connection default;
|
||||
SET DEBUG_SYNC='now WAIT_FOR blocked';
|
||||
|
@ -81,6 +81,7 @@ COUNT(k1) k2 k3
|
||||
drop table t1;
|
||||
create table t1(k1 int auto_increment primary key,
|
||||
k2 char(200),k3 char(200))engine=innodb;
|
||||
INSERT INTO t1 VALUES(1, "test", "test");
|
||||
SET DEBUG_SYNC= 'row_merge_after_scan
|
||||
SIGNAL opened WAIT_FOR flushed';
|
||||
ALTER TABLE t1 FORCE, ADD COLUMN k4 int;
|
||||
@ -100,6 +101,7 @@ SELECT COUNT(k1),k2,k3 FROM t1 GROUP BY k2,k3;
|
||||
COUNT(k1) k2 k3
|
||||
480 aaa bbb
|
||||
480 aaaa bbbb
|
||||
1 test test
|
||||
disconnect con1;
|
||||
connection default;
|
||||
show create table t1;
|
||||
@ -109,7 +111,7 @@ t1 CREATE TABLE `t1` (
|
||||
`k2` char(200) DEFAULT NULL,
|
||||
`k3` char(200) DEFAULT NULL,
|
||||
PRIMARY KEY (`k1`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1023 DEFAULT CHARSET=latin1
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1024 DEFAULT CHARSET=latin1
|
||||
drop table t1;
|
||||
drop table t480;
|
||||
#
|
||||
@ -117,6 +119,7 @@ drop table t480;
|
||||
# in online table rebuild
|
||||
#
|
||||
CREATE TABLE t1 (j INT UNIQUE, i INT UNIQUE) ENGINE=InnoDB;
|
||||
INSERT INTO t1 VALUES(2, 2);
|
||||
connect con1,localhost,root,,test;
|
||||
SET DEBUG_SYNC='row_log_table_apply1_before SIGNAL built WAIT_FOR log';
|
||||
ALTER TABLE t1 DROP j, FORCE;
|
||||
|
@ -509,6 +509,7 @@ DROP TABLE t1;
|
||||
# MDEV-13205 assertion !dict_index_is_online_ddl(index) upon ALTER TABLE
|
||||
#
|
||||
CREATE TABLE t1 (c VARCHAR(64)) ENGINE=InnoDB;
|
||||
INSERT INTO t1 VALUES('foo');
|
||||
SET DEBUG_SYNC = 'row_log_apply_before SIGNAL t1u_created WAIT_FOR dup_done';
|
||||
ALTER TABLE t1 ADD UNIQUE(c);
|
||||
connection con1;
|
||||
|
@ -171,6 +171,7 @@ connect stop_purge,localhost,root;
|
||||
START TRANSACTION WITH CONSISTENT SNAPSHOT;
|
||||
connect ddl,localhost,root,,test;
|
||||
DELETE FROM t1;
|
||||
INSERT INTO t1 VALUES(1, 2);
|
||||
SET DEBUG_SYNC='row_log_table_apply1_before SIGNAL copied WAIT_FOR logged';
|
||||
ALTER TABLE t1 FORCE;
|
||||
connection default;
|
||||
@ -449,6 +450,7 @@ DROP TABLE t1;
|
||||
CREATE TABLE t1
|
||||
(a INT NOT NULL, b INT, c INT, d INT, e INT, f INT, g INT, h INT, i TEXT)
|
||||
ENGINE=InnoDB;
|
||||
INSERT INTO t1 VALUES(1, 2, 3, 4, 5, 6, 7, 8, "test");
|
||||
ALTER TABLE t1 MODIFY a INT NULL;
|
||||
SET DEBUG_SYNC='innodb_inplace_alter_table_enter SIGNAL alter WAIT_FOR go';
|
||||
ALTER TABLE t1 ADD PRIMARY KEY (a);
|
||||
@ -462,6 +464,7 @@ connection default;
|
||||
disconnect con1;
|
||||
SELECT * FROM t1;
|
||||
a b c d e f g h i
|
||||
1 2 3 4 5 6 7 8 test
|
||||
DROP TABLE t1;
|
||||
SET DEBUG_SYNC=RESET;
|
||||
# End of 10.4 tests
|
||||
|
@ -24,6 +24,7 @@ CREATE TABLE t1(f1 INT, f2 INT,
|
||||
PRIMARY KEY(f1, f2),
|
||||
UNIQUE INDEX uidx2 (f1, f2),
|
||||
UNIQUE INDEX uidx1 (f2))ENGINE=InnoDB;
|
||||
INSERT INTO t1 VALUES(2, 2);
|
||||
ALTER TABLE t1 DROP PRIMARY KEY;
|
||||
SHOW CREATE TABLE t1;
|
||||
SET DEBUG_SYNC = 'innodb_inplace_alter_table_enter
|
||||
@ -42,6 +43,7 @@ DROP TABLE t1;
|
||||
|
||||
SET SQL_MODE= strict_trans_tables;
|
||||
CREATE TABLE t1(a INT UNIQUE) ENGINE=InnoDB;
|
||||
INSERT INTO t1 VALUES(1);
|
||||
SET DEBUG_SYNC='row_log_table_apply1_before SIGNAL dml WAIT_FOR dml_done';
|
||||
--send ALTER TABLE t1 MODIFY COLUMN a INT NOT NULL
|
||||
connection con1;
|
||||
|
@ -75,7 +75,7 @@ SET DEBUG_SYNC='RESET';
|
||||
--echo #
|
||||
CREATE TABLE t1 (f VARCHAR(8) CHARACTER SET latin1 COLLATE latin1_swedish_ci)
|
||||
ENGINE=InnoDB;
|
||||
|
||||
INSERT INTO t1 VALUES('ZERO');
|
||||
connection con1;
|
||||
SET DEBUG_SYNC = 'row_log_table_apply1_before SIGNAL scanned WAIT_FOR insert_done';
|
||||
send ALTER TABLE t1 MODIFY f VARCHAR(256) COLLATE latin1_german2_ci NOT NULL;
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
CREATE TABLE t0 (pk INT PRIMARY KEY) ENGINE=InnoDB;
|
||||
CREATE TABLE t1 (c CHAR(2) NOT NULL) ENGINE=InnoDB;
|
||||
INSERT INTO t1 VALUES('cd');
|
||||
|
||||
connect (con1,localhost,root,,);
|
||||
BEGIN;
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
CREATE TABLE t0 (pk INT PRIMARY KEY) ENGINE=InnoDB;
|
||||
CREATE TABLE t1 (pk INT PRIMARY KEY, b INT) ENGINE=InnoDB;
|
||||
INSERT INTO t1 VALUES(100, 100);
|
||||
|
||||
--connect (con1,localhost,root,,test)
|
||||
BEGIN;
|
||||
|
@ -80,7 +80,7 @@ BEGIN;
|
||||
DELETE FROM mysql.innodb_table_stats;
|
||||
|
||||
connect (con2,localhost,root,,);
|
||||
SET DEBUG_SYNC='inplace_after_index_build SIGNAL blocked WAIT_FOR ever';
|
||||
SET DEBUG_SYNC='innodb_inplace_alter_table_enter SIGNAL blocked WAIT_FOR ever';
|
||||
send ALTER TABLE t1 FORCE;
|
||||
|
||||
connection default;
|
||||
|
@ -96,6 +96,7 @@ drop table t1;
|
||||
# Log file creation failure.
|
||||
create table t1(k1 int auto_increment primary key,
|
||||
k2 char(200),k3 char(200))engine=innodb;
|
||||
INSERT INTO t1 VALUES(1, "test", "test");
|
||||
SET DEBUG_SYNC= 'row_merge_after_scan
|
||||
SIGNAL opened WAIT_FOR flushed';
|
||||
send ALTER TABLE t1 FORCE, ADD COLUMN k4 int;
|
||||
@ -122,6 +123,7 @@ drop table t480;
|
||||
--echo #
|
||||
|
||||
CREATE TABLE t1 (j INT UNIQUE, i INT UNIQUE) ENGINE=InnoDB;
|
||||
INSERT INTO t1 VALUES(2, 2);
|
||||
--connect (con1,localhost,root,,test)
|
||||
SET DEBUG_SYNC='row_log_table_apply1_before SIGNAL built WAIT_FOR log';
|
||||
--send
|
||||
|
@ -477,6 +477,7 @@ DROP TABLE t1;
|
||||
--echo # MDEV-13205 assertion !dict_index_is_online_ddl(index) upon ALTER TABLE
|
||||
--echo #
|
||||
CREATE TABLE t1 (c VARCHAR(64)) ENGINE=InnoDB;
|
||||
INSERT INTO t1 VALUES('foo');
|
||||
SET DEBUG_SYNC = 'row_log_apply_before SIGNAL t1u_created WAIT_FOR dup_done';
|
||||
send ALTER TABLE t1 ADD UNIQUE(c);
|
||||
|
||||
|
@ -186,6 +186,7 @@ connect stop_purge,localhost,root;
|
||||
START TRANSACTION WITH CONSISTENT SNAPSHOT;
|
||||
connect ddl,localhost,root,,test;
|
||||
DELETE FROM t1;
|
||||
INSERT INTO t1 VALUES(1, 2);
|
||||
SET DEBUG_SYNC='row_log_table_apply1_before SIGNAL copied WAIT_FOR logged';
|
||||
send ALTER TABLE t1 FORCE;
|
||||
connection default;
|
||||
@ -519,7 +520,7 @@ DROP TABLE t1;
|
||||
CREATE TABLE t1
|
||||
(a INT NOT NULL, b INT, c INT, d INT, e INT, f INT, g INT, h INT, i TEXT)
|
||||
ENGINE=InnoDB;
|
||||
|
||||
INSERT INTO t1 VALUES(1, 2, 3, 4, 5, 6, 7, 8, "test");
|
||||
ALTER TABLE t1 MODIFY a INT NULL;
|
||||
|
||||
SET DEBUG_SYNC='innodb_inplace_alter_table_enter SIGNAL alter WAIT_FOR go';
|
||||
|
@ -30,6 +30,7 @@ DROP TABLE t2, t1;
|
||||
# MDEV-25200 Index count mismatch due to aborted FULLTEXT INDEX
|
||||
#
|
||||
CREATE TABLE t1(a INT, b TEXT, c TEXT, FULLTEXT INDEX(b)) ENGINE=InnoDB;
|
||||
INSERT INTO t1 VALUES(1, "test", "test_1");
|
||||
connect con1,localhost,root,,test;
|
||||
SET DEBUG_DBUG="+d,innodb_OOM_inplace_alter";
|
||||
SET DEBUG_SYNC='innodb_commit_inplace_alter_table_enter SIGNAL s2 WAIT_FOR g2';
|
||||
@ -39,6 +40,7 @@ SET DEBUG_SYNC='now WAIT_FOR s2';
|
||||
START TRANSACTION;
|
||||
SELECT * FROM t1;
|
||||
a b c
|
||||
1 test test_1
|
||||
SET DEBUG_SYNC='now SIGNAL g2';
|
||||
connection con1;
|
||||
ERROR HY000: Out of memory.
|
||||
|
@ -59,6 +59,7 @@ DROP TABLE t2, t1;
|
||||
--echo # MDEV-25200 Index count mismatch due to aborted FULLTEXT INDEX
|
||||
--echo #
|
||||
CREATE TABLE t1(a INT, b TEXT, c TEXT, FULLTEXT INDEX(b)) ENGINE=InnoDB;
|
||||
INSERT INTO t1 VALUES(1, "test", "test_1");
|
||||
connect(con1,localhost,root,,test);
|
||||
SET DEBUG_DBUG="+d,innodb_OOM_inplace_alter";
|
||||
SET DEBUG_SYNC='innodb_commit_inplace_alter_table_enter SIGNAL s2 WAIT_FOR g2';
|
||||
|
@ -2579,6 +2579,9 @@ public:
|
||||
/** true when InnoDB should abort the alter when table is not empty */
|
||||
bool error_if_not_empty;
|
||||
|
||||
/** True when DDL should avoid downgrading the MDL */
|
||||
bool mdl_exclusive_after_prepare= false;
|
||||
|
||||
Alter_inplace_info(HA_CREATE_INFO *create_info_arg,
|
||||
Alter_info *alter_info_arg,
|
||||
KEY *key_info_arg, uint key_count_arg,
|
||||
|
@ -18,6 +18,14 @@
|
||||
#include "sql_string.h"
|
||||
#include "my_json_writer.h"
|
||||
|
||||
#if !defined(NDEBUG) || defined(JSON_WRITER_UNIT_TEST)
|
||||
bool Json_writer::named_item_expected() const
|
||||
{
|
||||
return named_items_expectation.size()
|
||||
&& named_items_expectation.back();
|
||||
}
|
||||
#endif
|
||||
|
||||
void Json_writer::append_indent()
|
||||
{
|
||||
if (!document_start)
|
||||
@ -26,9 +34,21 @@ void Json_writer::append_indent()
|
||||
output.append(' ');
|
||||
}
|
||||
|
||||
inline void Json_writer::on_start_object()
|
||||
{
|
||||
#if !defined(NDEBUG) || defined(JSON_WRITER_UNIT_TEST)
|
||||
if(!fmt_helper.is_making_writer_calls())
|
||||
{
|
||||
VALIDITY_ASSERT(got_name == named_item_expected());
|
||||
named_items_expectation.push_back(true);
|
||||
}
|
||||
#endif
|
||||
fmt_helper.on_start_object();
|
||||
}
|
||||
|
||||
void Json_writer::start_object()
|
||||
{
|
||||
fmt_helper.on_start_object();
|
||||
on_start_object();
|
||||
|
||||
if (!element_started)
|
||||
start_element();
|
||||
@ -38,10 +58,22 @@ void Json_writer::start_object()
|
||||
first_child=true;
|
||||
element_started= false;
|
||||
document_start= false;
|
||||
#if !defined(NDEBUG) || defined(JSON_WRITER_UNIT_TEST)
|
||||
got_name= false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void Json_writer::start_array()
|
||||
{
|
||||
#if !defined(NDEBUG) || defined(JSON_WRITER_UNIT_TEST)
|
||||
if(!fmt_helper.is_making_writer_calls())
|
||||
{
|
||||
VALIDITY_ASSERT(got_name == named_item_expected());
|
||||
named_items_expectation.push_back(false);
|
||||
got_name= false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (fmt_helper.on_start_array())
|
||||
return;
|
||||
|
||||
@ -58,6 +90,12 @@ void Json_writer::start_array()
|
||||
|
||||
void Json_writer::end_object()
|
||||
{
|
||||
#if !defined(NDEBUG) || defined(JSON_WRITER_UNIT_TEST)
|
||||
VALIDITY_ASSERT(named_item_expected());
|
||||
named_items_expectation.pop_back();
|
||||
VALIDITY_ASSERT(!got_name);
|
||||
got_name= false;
|
||||
#endif
|
||||
indent_level-=INDENT_SIZE;
|
||||
if (!first_child)
|
||||
append_indent();
|
||||
@ -68,6 +106,11 @@ void Json_writer::end_object()
|
||||
|
||||
void Json_writer::end_array()
|
||||
{
|
||||
#if !defined(NDEBUG) || defined(JSON_WRITER_UNIT_TEST)
|
||||
VALIDITY_ASSERT(!named_item_expected());
|
||||
named_items_expectation.pop_back();
|
||||
got_name= false;
|
||||
#endif
|
||||
if (fmt_helper.on_end_array())
|
||||
return;
|
||||
indent_level-=INDENT_SIZE;
|
||||
@ -80,9 +123,13 @@ void Json_writer::end_array()
|
||||
Json_writer& Json_writer::add_member(const char *name)
|
||||
{
|
||||
size_t len= strlen(name);
|
||||
if (fmt_helper.on_add_member(name, len))
|
||||
return *this; // handled
|
||||
return add_member(name, len);
|
||||
}
|
||||
|
||||
Json_writer& Json_writer::add_member(const char *name, size_t len)
|
||||
{
|
||||
if (!fmt_helper.on_add_member(name, len))
|
||||
{
|
||||
// assert that we are in an object
|
||||
DBUG_ASSERT(!element_started);
|
||||
start_element();
|
||||
@ -90,21 +137,11 @@ Json_writer& Json_writer::add_member(const char *name)
|
||||
output.append('"');
|
||||
output.append(name, len);
|
||||
output.append("\": ", 3);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Json_writer& Json_writer::add_member(const char *name, size_t len)
|
||||
{
|
||||
if (fmt_helper.on_add_member(name, len))
|
||||
return *this; // handled
|
||||
|
||||
// assert that we are in an object
|
||||
DBUG_ASSERT(!element_started);
|
||||
start_element();
|
||||
|
||||
output.append('"');
|
||||
output.append(name, len);
|
||||
output.append("\": ");
|
||||
#if !defined(NDEBUG) || defined(JSON_WRITER_UNIT_TEST)
|
||||
if (!fmt_helper.is_making_writer_calls())
|
||||
got_name= true;
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -200,7 +237,14 @@ void Json_writer::add_null()
|
||||
void Json_writer::add_unquoted_str(const char* str)
|
||||
{
|
||||
size_t len= strlen(str);
|
||||
if (fmt_helper.on_add_str(str, len))
|
||||
add_unquoted_str(str, len);
|
||||
}
|
||||
|
||||
void Json_writer::add_unquoted_str(const char* str, size_t len)
|
||||
{
|
||||
VALIDITY_ASSERT(fmt_helper.is_making_writer_calls() ||
|
||||
got_name == named_item_expected());
|
||||
if (on_add_str(str, len))
|
||||
return;
|
||||
|
||||
if (!element_started)
|
||||
@ -210,31 +254,19 @@ void Json_writer::add_unquoted_str(const char* str)
|
||||
element_started= false;
|
||||
}
|
||||
|
||||
void Json_writer::add_unquoted_str(const char* str, size_t len)
|
||||
inline bool Json_writer::on_add_str(const char *str, size_t num_bytes)
|
||||
{
|
||||
if (fmt_helper.on_add_str(str, len))
|
||||
return;
|
||||
|
||||
if (!element_started)
|
||||
start_element();
|
||||
|
||||
output.append(str, len);
|
||||
element_started= false;
|
||||
#if !defined(NDEBUG) || defined(JSON_WRITER_UNIT_TEST)
|
||||
got_name= false;
|
||||
#endif
|
||||
bool helped= fmt_helper.on_add_str(str, num_bytes);
|
||||
return helped;
|
||||
}
|
||||
|
||||
void Json_writer::add_str(const char *str)
|
||||
{
|
||||
size_t len= strlen(str);
|
||||
if (fmt_helper.on_add_str(str, len))
|
||||
return;
|
||||
|
||||
if (!element_started)
|
||||
start_element();
|
||||
|
||||
output.append('"');
|
||||
output.append(str, len);
|
||||
output.append('"');
|
||||
element_started= false;
|
||||
add_str(str, len);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -243,7 +275,9 @@ void Json_writer::add_str(const char *str)
|
||||
|
||||
void Json_writer::add_str(const char* str, size_t num_bytes)
|
||||
{
|
||||
if (fmt_helper.on_add_str(str, num_bytes))
|
||||
VALIDITY_ASSERT(fmt_helper.is_making_writer_calls() ||
|
||||
got_name == named_item_expected());
|
||||
if (on_add_str(str, num_bytes))
|
||||
return;
|
||||
|
||||
if (!element_started)
|
||||
|
@ -17,9 +17,19 @@
|
||||
#define JSON_WRITER_INCLUDED
|
||||
|
||||
#include "my_base.h"
|
||||
#include "sql_select.h"
|
||||
|
||||
#if !defined(NDEBUG) || defined(JSON_WRITER_UNIT_TEST) || defined ENABLED_JSON_WRITER_CONSISTENCY_CHECKS
|
||||
#include <vector>
|
||||
#endif
|
||||
|
||||
#ifdef JSON_WRITER_UNIT_TEST
|
||||
#include "sql_string.h"
|
||||
// Also, mock objects are defined in my_json_writer-t.cc
|
||||
#define VALIDITY_ASSERT(x) if (!(x)) this->invalid_json= true;
|
||||
#else
|
||||
#include "sql_select.h"
|
||||
#define VALIDITY_ASSERT(x) DBUG_ASSERT(x)
|
||||
#endif
|
||||
|
||||
class Opt_trace_stmt;
|
||||
class Opt_trace_context;
|
||||
@ -99,6 +109,15 @@ public:
|
||||
|
||||
bool on_add_str(const char *str, size_t num_bytes);
|
||||
|
||||
/*
|
||||
Returns true if the helper is flushing its buffer and is probably
|
||||
making calls back to its Json_writer. (The Json_writer uses this
|
||||
function to avoid re-doing the processing that it has already done
|
||||
before making a call to fmt_helper)
|
||||
*/
|
||||
bool is_making_writer_calls() const { return state == DISABLED; }
|
||||
|
||||
private:
|
||||
void flush_on_one_line();
|
||||
void disable_and_flush();
|
||||
};
|
||||
@ -185,6 +204,25 @@ private:
|
||||
|
||||
class Json_writer
|
||||
{
|
||||
#if !defined(NDEBUG) || defined(JSON_WRITER_UNIT_TEST)
|
||||
/*
|
||||
In debug mode, Json_writer will fail and assertion if one attempts to
|
||||
produce an invalid JSON document (e.g. JSON array having named elements).
|
||||
*/
|
||||
std::vector<bool> named_items_expectation;
|
||||
|
||||
bool named_item_expected() const;
|
||||
|
||||
bool got_name;
|
||||
|
||||
#ifdef JSON_WRITER_UNIT_TEST
|
||||
public:
|
||||
// When compiled for unit test, creating invalid JSON will set this to true
|
||||
// instead of an assertion.
|
||||
bool invalid_json= false;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
public:
|
||||
/* Add a member. We must be in an object. */
|
||||
Json_writer& add_member(const char *name);
|
||||
@ -208,6 +246,10 @@ public:
|
||||
private:
|
||||
void add_unquoted_str(const char* val);
|
||||
void add_unquoted_str(const char* val, size_t len);
|
||||
|
||||
bool on_add_str(const char *str, size_t num_bytes);
|
||||
void on_start_object();
|
||||
|
||||
public:
|
||||
/* Start a child object */
|
||||
void start_object();
|
||||
@ -225,6 +267,9 @@ public:
|
||||
size_t get_truncated_bytes() { return output.get_truncated_bytes(); }
|
||||
|
||||
Json_writer() :
|
||||
#if !defined(NDEBUG) || defined(JSON_WRITER_UNIT_TEST)
|
||||
got_name(false),
|
||||
#endif
|
||||
indent_level(0), document_start(true), element_started(false),
|
||||
first_child(true)
|
||||
{
|
||||
@ -312,6 +357,9 @@ public:
|
||||
/* A common base for Json_writer_object and Json_writer_array */
|
||||
class Json_writer_struct
|
||||
{
|
||||
Json_writer_struct(const Json_writer_struct&)= delete;
|
||||
Json_writer_struct& operator=(const Json_writer_struct&)= delete;
|
||||
|
||||
#ifdef ENABLED_JSON_WRITER_CONSISTENCY_CHECKS
|
||||
static thread_local std::vector<bool> named_items_expectation;
|
||||
#endif
|
||||
@ -372,24 +420,18 @@ private:
|
||||
my_writer->add_member(name);
|
||||
}
|
||||
public:
|
||||
explicit Json_writer_object(THD *thd)
|
||||
: Json_writer_struct(thd, true)
|
||||
{
|
||||
#ifdef ENABLED_JSON_WRITER_CONSISTENCY_CHECKS
|
||||
DBUG_ASSERT(!named_item_expected());
|
||||
#endif
|
||||
if (unlikely(my_writer))
|
||||
my_writer->start_object();
|
||||
}
|
||||
|
||||
explicit Json_writer_object(THD* thd, const char *str)
|
||||
explicit Json_writer_object(THD* thd, const char *str= nullptr)
|
||||
: Json_writer_struct(thd, true)
|
||||
{
|
||||
#ifdef ENABLED_JSON_WRITER_CONSISTENCY_CHECKS
|
||||
DBUG_ASSERT(named_item_expected());
|
||||
#endif
|
||||
if (unlikely(my_writer))
|
||||
my_writer->add_member(str).start_object();
|
||||
{
|
||||
if (str)
|
||||
my_writer->add_member(str);
|
||||
my_writer->start_object();
|
||||
}
|
||||
}
|
||||
|
||||
~Json_writer_object()
|
||||
|
@ -370,7 +370,7 @@ TRP_ROR_INTERSECT *get_best_covering_ror_intersect(PARAM *param,
|
||||
double read_time);
|
||||
static
|
||||
TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge,
|
||||
double read_time);
|
||||
double read_time, bool named_trace= false);
|
||||
static
|
||||
TABLE_READ_PLAN *merge_same_index_scans(PARAM *param, SEL_IMERGE *imerge,
|
||||
TRP_INDEX_MERGE *imerge_trp,
|
||||
@ -5085,7 +5085,7 @@ double get_sweep_read_cost(const PARAM *param, ha_rows records)
|
||||
|
||||
static
|
||||
TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge,
|
||||
double read_time)
|
||||
double read_time, bool named_trace)
|
||||
{
|
||||
SEL_TREE **ptree;
|
||||
TRP_INDEX_MERGE *imerge_trp= NULL;
|
||||
@ -5133,7 +5133,8 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge,
|
||||
n_child_scans)))
|
||||
DBUG_RETURN(NULL);
|
||||
|
||||
Json_writer_object trace_best_disjunct(thd);
|
||||
const char* trace_best_disjunct_obj_name= named_trace ? "best_disjunct_quick" : nullptr;
|
||||
Json_writer_object trace_best_disjunct(thd, trace_best_disjunct_obj_name);
|
||||
Json_writer_array to_merge(thd, "indexes_to_merge");
|
||||
/*
|
||||
Collect best 'range' scan for each of disjuncts, and, while doing so,
|
||||
@ -5489,7 +5490,7 @@ TABLE_READ_PLAN *merge_same_index_scans(PARAM *param, SEL_IMERGE *imerge,
|
||||
DBUG_ASSERT(imerge->trees_next>imerge->trees);
|
||||
|
||||
if (imerge->trees_next-imerge->trees > 1)
|
||||
trp= get_best_disjunct_quick(param, imerge, read_time);
|
||||
trp= get_best_disjunct_quick(param, imerge, read_time, true);
|
||||
else
|
||||
{
|
||||
/*
|
||||
@ -5677,7 +5678,7 @@ void print_keyparts(THD *thd, KEY *key, uint key_parts)
|
||||
DBUG_ASSERT(thd->trace_started());
|
||||
|
||||
KEY_PART_INFO *part= key->key_part;
|
||||
Json_writer_array keyparts= Json_writer_array(thd, "keyparts");
|
||||
Json_writer_array keyparts(thd, "keyparts");
|
||||
for(uint i= 0; i < key_parts; i++, part++)
|
||||
keyparts.add(part->field->field_name);
|
||||
}
|
||||
|
@ -3827,9 +3827,9 @@ void fix_semijoin_strategies_for_picked_join_order(JOIN *join)
|
||||
Json_writer_array semijoin_plan(thd, "join_order");
|
||||
for (i= first + sjm->tables; i <= tablenr; i++)
|
||||
{
|
||||
Json_writer_object trace_one_table(thd);
|
||||
if (unlikely(thd->trace_started()))
|
||||
{
|
||||
Json_writer_object trace_one_table(thd);
|
||||
trace_one_table.add_table_name(join->best_positions[i].table);
|
||||
}
|
||||
best_access_path(join, join->best_positions[i].table, rem_tables,
|
||||
@ -3866,9 +3866,9 @@ void fix_semijoin_strategies_for_picked_join_order(JOIN *join)
|
||||
Json_writer_array semijoin_plan(thd, "join_order");
|
||||
for (idx= first; idx <= tablenr; idx++)
|
||||
{
|
||||
Json_writer_object trace_one_table(thd);
|
||||
if (unlikely(thd->trace_started()))
|
||||
{
|
||||
Json_writer_object trace_one_table(thd);
|
||||
trace_one_table.add_table_name(join->best_positions[idx].table);
|
||||
}
|
||||
if (join->best_positions[idx].use_join_buffer)
|
||||
@ -3905,9 +3905,9 @@ void fix_semijoin_strategies_for_picked_join_order(JOIN *join)
|
||||
Json_writer_array semijoin_plan(thd, "join_order");
|
||||
for (idx= first; idx <= tablenr; idx++)
|
||||
{
|
||||
Json_writer_object trace_one_table(thd);
|
||||
if (unlikely(thd->trace_started()))
|
||||
{
|
||||
Json_writer_object trace_one_table(thd);
|
||||
trace_one_table.add_table_name(join->best_positions[idx].table);
|
||||
}
|
||||
if (join->best_positions[idx].use_join_buffer || (idx == first))
|
||||
|
@ -8001,7 +8001,8 @@ static bool mysql_inplace_alter_table(THD *thd,
|
||||
lock for prepare phase under LOCK TABLES in the same way as when
|
||||
exclusive lock is required for duration of the whole statement.
|
||||
*/
|
||||
if (inplace_supported == HA_ALTER_INPLACE_EXCLUSIVE_LOCK ||
|
||||
if (!ha_alter_info->mdl_exclusive_after_prepare &&
|
||||
(inplace_supported == HA_ALTER_INPLACE_EXCLUSIVE_LOCK ||
|
||||
((inplace_supported == HA_ALTER_INPLACE_COPY_NO_LOCK ||
|
||||
inplace_supported == HA_ALTER_INPLACE_COPY_LOCK ||
|
||||
inplace_supported == HA_ALTER_INPLACE_NOCOPY_NO_LOCK ||
|
||||
@ -8009,7 +8010,7 @@ static bool mysql_inplace_alter_table(THD *thd,
|
||||
inplace_supported == HA_ALTER_INPLACE_INSTANT) &&
|
||||
(thd->locked_tables_mode == LTM_LOCK_TABLES ||
|
||||
thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES)) ||
|
||||
alter_info->requested_lock == Alter_info::ALTER_TABLE_LOCK_EXCLUSIVE)
|
||||
alter_info->requested_lock == Alter_info::ALTER_TABLE_LOCK_EXCLUSIVE))
|
||||
{
|
||||
if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
|
||||
goto cleanup;
|
||||
|
@ -1913,9 +1913,14 @@ innobase_fts_check_doc_id_col(
|
||||
|
||||
/** Check whether the table is empty.
|
||||
@param[in] table table to be checked
|
||||
@param[in] ignore_delete_marked Ignore the delete marked
|
||||
flag record
|
||||
@return true if table is empty */
|
||||
static bool innobase_table_is_empty(const dict_table_t *table)
|
||||
static bool innobase_table_is_empty(const dict_table_t *table,
|
||||
bool ignore_delete_marked=true)
|
||||
{
|
||||
if (!table->space)
|
||||
return false;
|
||||
dict_index_t *clust_index= dict_table_get_first_index(table);
|
||||
mtr_t mtr;
|
||||
btr_pcur_t pcur;
|
||||
@ -1953,12 +1958,16 @@ next_page:
|
||||
}
|
||||
|
||||
rec= page_cur_get_rec(cur);
|
||||
if (rec_get_deleted_flag(rec, dict_table_is_comp(table)));
|
||||
else if (!page_rec_is_supremum(rec))
|
||||
if (rec_get_deleted_flag(rec, dict_table_is_comp(table)))
|
||||
{
|
||||
if (ignore_delete_marked)
|
||||
goto scan_leaf;
|
||||
non_empty:
|
||||
mtr.commit();
|
||||
return false;
|
||||
}
|
||||
else if (!page_rec_is_supremum(rec))
|
||||
goto non_empty;
|
||||
else
|
||||
{
|
||||
next_page= true;
|
||||
@ -6764,6 +6773,7 @@ wrong_column_name:
|
||||
DBUG_ASSERT(num_fts_index <= 1);
|
||||
DBUG_ASSERT(!ctx->online || num_fts_index == 0);
|
||||
DBUG_ASSERT(!ctx->online
|
||||
|| !ha_alter_info->mdl_exclusive_after_prepare
|
||||
|| ctx->add_autoinc == ULINT_UNDEFINED);
|
||||
DBUG_ASSERT(!ctx->online
|
||||
|| !innobase_need_rebuild(ha_alter_info, old_table)
|
||||
@ -7582,6 +7592,20 @@ ha_innobase::prepare_inplace_alter_table(
|
||||
DBUG_RETURN(false);
|
||||
}
|
||||
|
||||
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
||||
if (table->part_info == NULL) {
|
||||
#endif
|
||||
/* Ignore the MDL downgrade when table is empty.
|
||||
This optimization is disabled for partition table. */
|
||||
ha_alter_info->mdl_exclusive_after_prepare =
|
||||
innobase_table_is_empty(m_prebuilt->table, false);
|
||||
if (ha_alter_info->online
|
||||
&& ha_alter_info->mdl_exclusive_after_prepare) {
|
||||
ha_alter_info->online = false;
|
||||
}
|
||||
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
||||
}
|
||||
#endif
|
||||
indexed_table = m_prebuilt->table;
|
||||
|
||||
/* ALTER TABLE will not implicitly move a table from a single-table
|
||||
@ -8366,7 +8390,9 @@ ha_innobase::inplace_alter_table(
|
||||
|
||||
DEBUG_SYNC(m_user_thd, "innodb_inplace_alter_table_enter");
|
||||
|
||||
if (!(ha_alter_info->handler_flags & INNOBASE_ALTER_DATA)) {
|
||||
/* Ignore the inplace alter phase when table is empty */
|
||||
if (!(ha_alter_info->handler_flags & INNOBASE_ALTER_DATA)
|
||||
|| ha_alter_info->mdl_exclusive_after_prepare) {
|
||||
ok_exit:
|
||||
DEBUG_SYNC(m_user_thd, "innodb_after_inplace_alter_table");
|
||||
DBUG_RETURN(false);
|
||||
|
@ -36,3 +36,8 @@ ADD_EXECUTABLE(mf_iocache-t mf_iocache-t.cc ../../sql/mf_iocache_encr.cc)
|
||||
TARGET_LINK_LIBRARIES(mf_iocache-t mysys mytap mysys_ssl)
|
||||
ADD_DEPENDENCIES(mf_iocache-t GenError)
|
||||
MY_ADD_TEST(mf_iocache)
|
||||
|
||||
# Json writer needs String which needs sql library
|
||||
ADD_EXECUTABLE(my_json_writer-t my_json_writer-t.cc dummy_builtins.cc)
|
||||
TARGET_LINK_LIBRARIES(my_json_writer-t sql mytap)
|
||||
MY_ADD_TEST(my_json_writer)
|
||||
|
128
unittest/sql/my_json_writer-t.cc
Normal file
128
unittest/sql/my_json_writer-t.cc
Normal file
@ -0,0 +1,128 @@
|
||||
/*
|
||||
Copyright (c) 2021, MariaDB Corporation.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
|
||||
|
||||
#include <my_global.h>
|
||||
#include <my_pthread.h>
|
||||
#include <my_sys.h>
|
||||
#include <stdio.h>
|
||||
#include <tap.h>
|
||||
|
||||
/*
|
||||
Unit tests for class Json_writer. At the moment there are only tests for the
|
||||
"Fail an assertion if one attempts to produce invalid JSON" feature.
|
||||
*/
|
||||
|
||||
struct TABLE;
|
||||
struct JOIN_TAB;
|
||||
class Json_writer;
|
||||
|
||||
|
||||
/* Several fake objects */
|
||||
class Opt_trace
|
||||
{
|
||||
public:
|
||||
void enable_tracing_if_required() {}
|
||||
void disable_tracing_if_required() {}
|
||||
Json_writer *get_current_json() { return nullptr; }
|
||||
};
|
||||
|
||||
class THD
|
||||
{
|
||||
public:
|
||||
Opt_trace opt_trace;
|
||||
};
|
||||
|
||||
#define JSON_WRITER_UNIT_TEST
|
||||
#include "../sql/my_json_writer.h"
|
||||
#include "../sql/my_json_writer.cc"
|
||||
|
||||
int main(int args, char **argv)
|
||||
{
|
||||
plan(NO_PLAN);
|
||||
diag("Testing Json_writer checks");
|
||||
|
||||
{
|
||||
Json_writer w;
|
||||
w.start_object();
|
||||
w.add_member("foo");
|
||||
w.end_object();
|
||||
ok(w.invalid_json, "Started a name but didn't add a value");
|
||||
}
|
||||
|
||||
{
|
||||
Json_writer w;
|
||||
w.start_object();
|
||||
w.add_ull(123);
|
||||
ok(w.invalid_json, "Unnamed value in an object");
|
||||
}
|
||||
|
||||
{
|
||||
Json_writer w;
|
||||
w.start_array();
|
||||
w.add_member("bebebe").add_ull(345);
|
||||
ok(w.invalid_json, "Named member in array");
|
||||
}
|
||||
|
||||
{
|
||||
Json_writer w;
|
||||
w.start_object();
|
||||
w.start_array();
|
||||
ok(w.invalid_json, "Unnamed array in an object");
|
||||
}
|
||||
|
||||
{
|
||||
Json_writer w;
|
||||
w.start_object();
|
||||
w.start_object();
|
||||
ok(w.invalid_json, "Unnamed object in an object");
|
||||
}
|
||||
|
||||
{
|
||||
Json_writer w;
|
||||
w.start_array();
|
||||
w.add_member("zzz");
|
||||
w.start_object();
|
||||
ok(w.invalid_json, "Named object in an array");
|
||||
}
|
||||
{
|
||||
Json_writer w;
|
||||
w.start_array();
|
||||
w.add_member("zzz");
|
||||
w.start_array();
|
||||
ok(w.invalid_json, "Named array in an array");
|
||||
}
|
||||
|
||||
{
|
||||
Json_writer w;
|
||||
w.start_array();
|
||||
w.end_object();
|
||||
ok(w.invalid_json, "JSON object end of array");
|
||||
}
|
||||
|
||||
{
|
||||
Json_writer w;
|
||||
w.start_object();
|
||||
w.end_array();
|
||||
ok(w.invalid_json, "JSON array end of object");
|
||||
}
|
||||
|
||||
|
||||
|
||||
diag("Done");
|
||||
|
||||
return exit_status();
|
||||
}
|
||||
|
Reference in New Issue
Block a user