diff --git a/mysql-test/suite/innodb/r/innodb_force_pk.result b/mysql-test/suite/innodb/r/innodb_force_pk.result new file mode 100644 index 00000000000..faf81a2e452 --- /dev/null +++ b/mysql-test/suite/innodb/r/innodb_force_pk.result @@ -0,0 +1,47 @@ +CREATE TABLE T1(A INTEGER) ENGINE=INNODB; +ERROR 42000: This table type requires a primary key +SHOW WARNINGS; +Level Code Message +Error 1173 This table type requires a primary key +CREATE TABLE T1(A INTEGER UNIQUE KEY) ENGINE=INNODB; +ERROR 42000: This table type requires a primary key +SHOW WARNINGS; +Level Code Message +Error 1173 This table type requires a primary key +CREATE TABLE T1(A INTEGER NOT NULL PRIMARY KEY) ENGINE=INNODB; +SHOW CREATE TABLE T1; +Table Create Table +T1 CREATE TABLE `T1` ( + `A` int(11) NOT NULL, + PRIMARY KEY (`A`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +SHOW WARNINGS; +Level Code Message +DROP TABLE T1; +CREATE TABLE T1(A INTEGER NOT NULL UNIQUE KEY) ENGINE=INNODB; +SHOW CREATE TABLE T1; +Table Create Table +T1 CREATE TABLE `T1` ( + `A` int(11) NOT NULL, + UNIQUE KEY `A` (`A`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +SHOW WARNINGS; +Level Code Message +DROP TABLE T1; +set global innodb_force_primary_key = 0; +CREATE TABLE T1(A INTEGER) ENGINE=INNODB; +SHOW WARNINGS; +Level Code Message +INSERT INTO T1 VALUES (1),(2),(3); +set global innodb_force_primary_key = 1; +SELECT * FROM T1; +A +1 +2 +3 +CREATE TABLE T2(A INTEGER) ENGINE=INNODB; +ERROR 42000: This table type requires a primary key +SHOW WARNINGS; +Level Code Message +Error 1173 This table type requires a primary key +DROP TABLE T1; diff --git a/mysql-test/suite/innodb/t/innodb_force_pk.opt b/mysql-test/suite/innodb/t/innodb_force_pk.opt new file mode 100644 index 00000000000..93d6393520c --- /dev/null +++ b/mysql-test/suite/innodb/t/innodb_force_pk.opt @@ -0,0 +1 @@ +--innodb_force_primary_key=1 diff --git a/mysql-test/suite/innodb/t/innodb_force_pk.test b/mysql-test/suite/innodb/t/innodb_force_pk.test new file mode 100644 index 00000000000..0462aa206f7 --- /dev/null +++ b/mysql-test/suite/innodb/t/innodb_force_pk.test @@ -0,0 +1,34 @@ +-- source include/have_innodb.inc + +let $force_pk=`select @@innodb_force_primary_key`; + +-- error 1173 +CREATE TABLE T1(A INTEGER) ENGINE=INNODB; +SHOW WARNINGS; +-- error 1173 +CREATE TABLE T1(A INTEGER UNIQUE KEY) ENGINE=INNODB; +SHOW WARNINGS; +CREATE TABLE T1(A INTEGER NOT NULL PRIMARY KEY) ENGINE=INNODB; +SHOW CREATE TABLE T1; +SHOW WARNINGS; +DROP TABLE T1; +CREATE TABLE T1(A INTEGER NOT NULL UNIQUE KEY) ENGINE=INNODB; +SHOW CREATE TABLE T1; +SHOW WARNINGS; +DROP TABLE T1; + +set global innodb_force_primary_key = 0; +CREATE TABLE T1(A INTEGER) ENGINE=INNODB; +SHOW WARNINGS; +INSERT INTO T1 VALUES (1),(2),(3); + +set global innodb_force_primary_key = 1; +SELECT * FROM T1; +-- error 1173 +CREATE TABLE T2(A INTEGER) ENGINE=INNODB; +SHOW WARNINGS; +DROP TABLE T1; + +--disable_query_log +eval set global innodb_force_primary_key=$force_pk; +--enable_query_log diff --git a/mysql-test/suite/sys_vars/r/innodb_force_primary_key_basic.result b/mysql-test/suite/sys_vars/r/innodb_force_primary_key_basic.result new file mode 100644 index 00000000000..eda72bd754d --- /dev/null +++ b/mysql-test/suite/sys_vars/r/innodb_force_primary_key_basic.result @@ -0,0 +1,44 @@ +SELECT COUNT(@@GLOBAL.innodb_force_primary_key); +COUNT(@@GLOBAL.innodb_force_primary_key) +1 +1 Expected +SET @@GLOBAL.innodb_force_primary_key=1; +Expected ok +SELECT COUNT(@@GLOBAL.innodb_force_primary_key); +COUNT(@@GLOBAL.innodb_force_primary_key) +1 +1 Expected +SELECT IF(@@GLOBAL.innodb_force_primary_key, 'ON', 'OFF') = VARIABLE_VALUE +FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES +WHERE VARIABLE_NAME='innodb_force_primary_key'; +IF(@@GLOBAL.innodb_force_primary_key, 'ON', 'OFF') = VARIABLE_VALUE +1 +1 Expected +SELECT COUNT(@@GLOBAL.innodb_force_primary_key); +COUNT(@@GLOBAL.innodb_force_primary_key) +1 +1 Expected +SELECT COUNT(VARIABLE_VALUE) +FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES +WHERE VARIABLE_NAME='innodb_force_primary_key'; +COUNT(VARIABLE_VALUE) +1 +1 Expected +SELECT @@innodb_force_primary_key = @@GLOBAL.innodb_force_primary_key; +@@innodb_force_primary_key = @@GLOBAL.innodb_force_primary_key +1 +1 Expected +SELECT COUNT(@@innodb_force_primary_key); +COUNT(@@innodb_force_primary_key) +1 +1 Expected +SELECT COUNT(@@local.innodb_force_primary_key); +ERROR HY000: Variable 'innodb_force_primary_key' is a GLOBAL variable +Expected Variable 'innodb_force_primary_key' is a GLOBAL variable +SELECT COUNT(@@SESSION.innodb_force_primary_key); +ERROR HY000: Variable 'innodb_force_primary_key' is a GLOBAL variable +Expected Variable 'innodb_force_primary_key' is a GLOBAL variable +SELECT COUNT(@@GLOBAL.innodb_force_primary_key); +COUNT(@@GLOBAL.innodb_force_primary_key) +1 +1 Expected diff --git a/mysql-test/suite/sys_vars/t/innodb_force_primary_key_basic.test b/mysql-test/suite/sys_vars/t/innodb_force_primary_key_basic.test new file mode 100644 index 00000000000..f760b3545fa --- /dev/null +++ b/mysql-test/suite/sys_vars/t/innodb_force_primary_key_basic.test @@ -0,0 +1,57 @@ +--source include/have_innodb.inc + +let $force_pk=`select @@innodb_force_primary_key`; + +SELECT COUNT(@@GLOBAL.innodb_force_primary_key); +--echo 1 Expected + +#################################################################### +# Check if Value can set # +#################################################################### + +SET @@GLOBAL.innodb_force_primary_key=1; +--echo Expected ok + +SELECT COUNT(@@GLOBAL.innodb_force_primary_key); +--echo 1 Expected + +################################################################# +# Check if the value in GLOBAL Table matches value in variable # +################################################################# + +SELECT IF(@@GLOBAL.innodb_force_primary_key, 'ON', 'OFF') = VARIABLE_VALUE +FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES +WHERE VARIABLE_NAME='innodb_force_primary_key'; +--echo 1 Expected + +SELECT COUNT(@@GLOBAL.innodb_force_primary_key); +--echo 1 Expected + +SELECT COUNT(VARIABLE_VALUE) +FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES +WHERE VARIABLE_NAME='innodb_force_primary_key'; +--echo 1 Expected + +################################################################################ +# Check if accessing variable with and without GLOBAL point to same variable # +################################################################################ +SELECT @@innodb_force_primary_key = @@GLOBAL.innodb_force_primary_key; +--echo 1 Expected + +SELECT COUNT(@@innodb_force_primary_key); +--echo 1 Expected + +--Error ER_INCORRECT_GLOBAL_LOCAL_VAR +SELECT COUNT(@@local.innodb_force_primary_key); +--echo Expected Variable 'innodb_force_primary_key' is a GLOBAL variable + +--Error ER_INCORRECT_GLOBAL_LOCAL_VAR +SELECT COUNT(@@SESSION.innodb_force_primary_key); +--echo Expected Variable 'innodb_force_primary_key' is a GLOBAL variable + +SELECT COUNT(@@GLOBAL.innodb_force_primary_key); +--echo 1 Expected + +--disable_query_log +eval set global innodb_force_primary_key=$force_pk; +--enable_query_log diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 7febafb2324..5f5299e88d1 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -2252,6 +2252,7 @@ ha_innobase::ha_innobase( HA_BINLOG_ROW_CAPABLE | HA_CAN_GEOMETRY | HA_PARTIAL_COLUMN_READ | HA_TABLE_SCAN_ON_INDEX | HA_CAN_FULLTEXT | + (srv_force_primary_key ? HA_REQUIRE_PRIMARY_KEY : 0 ) | HA_CAN_FULLTEXT_EXT | HA_CAN_EXPORT), start_of_scan(0), num_write_row(0) @@ -4227,12 +4228,19 @@ ha_innobase::table_flags() const /* Need to use tx_isolation here since table flags is (also) called before prebuilt is inited. */ ulong const tx_isolation = thd_tx_isolation(ha_thd()); + handler::Table_flags flags = int_table_flags; - if (tx_isolation <= ISO_READ_COMMITTED) { - return(int_table_flags); + if (srv_force_primary_key) { + flags |= HA_REQUIRE_PRIMARY_KEY; + } else { + flags &= ~HA_REQUIRE_PRIMARY_KEY; } - return(int_table_flags | HA_BINLOG_STMT_CAPABLE); + if (tx_isolation <= ISO_READ_COMMITTED) { + return(flags); + } + + return(flags | HA_BINLOG_STMT_CAPABLE); } /****************************************************************//** @@ -16622,6 +16630,12 @@ static MYSQL_SYSVAR_BOOL(trx_purge_view_update_only_debug, NULL, NULL, FALSE); #endif /* UNIV_DEBUG */ +static MYSQL_SYSVAR_BOOL(force_primary_key, + srv_force_primary_key, + PLUGIN_VAR_OPCMDARG, + "Do not allow to create table without primary key (off by default)", + NULL, NULL, FALSE); + static struct st_mysql_sys_var* innobase_system_variables[]= { MYSQL_SYSVAR(additional_mem_pool_size), MYSQL_SYSVAR(api_trx_level), @@ -16774,6 +16788,7 @@ static struct st_mysql_sys_var* innobase_system_variables[]= { MYSQL_SYSVAR(limit_optimistic_insert_debug), MYSQL_SYSVAR(trx_purge_view_update_only_debug), #endif /* UNIV_DEBUG */ + MYSQL_SYSVAR(force_primary_key), NULL }; diff --git a/storage/innobase/include/srv0srv.h b/storage/innobase/include/srv0srv.h index 1e98cf690d8..8cbf52e8629 100644 --- a/storage/innobase/include/srv0srv.h +++ b/storage/innobase/include/srv0srv.h @@ -353,6 +353,8 @@ extern ibool srv_use_atomic_writes; extern ibool srv_use_posix_fallocate; #endif +extern my_bool srv_force_primary_key; + extern ulong srv_max_buf_pool_modified_pct; extern ulong srv_max_purge_lag; extern ulong srv_max_purge_lag_delay; diff --git a/storage/innobase/srv/srv0srv.cc b/storage/innobase/srv/srv0srv.cc index a25469f35b7..7e2f7a8a049 100644 --- a/storage/innobase/srv/srv0srv.cc +++ b/storage/innobase/srv/srv0srv.cc @@ -383,6 +383,9 @@ UNIV_INTERN ibool srv_print_innodb_lock_monitor = FALSE; UNIV_INTERN ibool srv_print_innodb_tablespace_monitor = FALSE; UNIV_INTERN ibool srv_print_innodb_table_monitor = FALSE; +/** If this flag is set tables without primary key are not allowed */ +UNIV_INTERN my_bool srv_force_primary_key = FALSE; + /* Array of English strings describing the current state of an i/o handler thread */ diff --git a/storage/xtradb/handler/ha_innodb.cc b/storage/xtradb/handler/ha_innodb.cc index c18cab7510e..b66be2065a6 100644 --- a/storage/xtradb/handler/ha_innodb.cc +++ b/storage/xtradb/handler/ha_innodb.cc @@ -2544,6 +2544,7 @@ ha_innobase::ha_innobase( HA_BINLOG_ROW_CAPABLE | HA_CAN_GEOMETRY | HA_PARTIAL_COLUMN_READ | HA_TABLE_SCAN_ON_INDEX | HA_CAN_FULLTEXT | + (srv_force_primary_key ? HA_REQUIRE_PRIMARY_KEY : 0 ) | HA_CAN_FULLTEXT_EXT | HA_CAN_EXPORT), start_of_scan(0), num_write_row(0) @@ -4624,12 +4625,19 @@ ha_innobase::table_flags() const /* Need to use tx_isolation here since table flags is (also) called before prebuilt is inited. */ ulong const tx_isolation = thd_tx_isolation(ha_thd()); + handler::Table_flags flags = int_table_flags; - if (tx_isolation <= ISO_READ_COMMITTED) { - return(int_table_flags); + if (srv_force_primary_key) { + flags |= HA_REQUIRE_PRIMARY_KEY; + } else { + flags &= ~HA_REQUIRE_PRIMARY_KEY; } - return(int_table_flags | HA_BINLOG_STMT_CAPABLE); + if (tx_isolation <= ISO_READ_COMMITTED) { + return(flags); + } + + return(flags | HA_BINLOG_STMT_CAPABLE); } /****************************************************************//** @@ -17729,6 +17737,12 @@ static MYSQL_SYSVAR_BOOL(trx_purge_view_update_only_debug, NULL, NULL, FALSE); #endif /* UNIV_DEBUG */ +static MYSQL_SYSVAR_BOOL(force_primary_key, + srv_force_primary_key, + PLUGIN_VAR_OPCMDARG, + "Do not allow to create table without primary key (off by default)", + NULL, NULL, FALSE); + const char *corrupt_table_action_names[]= { "assert", /* 0 */ @@ -17951,6 +17965,7 @@ static struct st_mysql_sys_var* innobase_system_variables[]= { MYSQL_SYSVAR(fake_changes), MYSQL_SYSVAR(locking_fake_changes), MYSQL_SYSVAR(use_stacktrace), + MYSQL_SYSVAR(force_primary_key), NULL }; diff --git a/storage/xtradb/include/srv0srv.h b/storage/xtradb/include/srv0srv.h index d278782daa8..ff881b3fdff 100644 --- a/storage/xtradb/include/srv0srv.h +++ b/storage/xtradb/include/srv0srv.h @@ -431,6 +431,8 @@ extern ulong srv_pass_corrupt_table; extern ulong srv_log_checksum_algorithm; +extern my_bool srv_force_primary_key; + /* Helper macro to support srv_pass_corrupt_table checks. If 'cond' is FALSE, execute 'code' if srv_pass_corrupt_table is non-zero, or trigger a fatal error otherwise. The break statement in 'code' will obviously not work as diff --git a/storage/xtradb/srv/srv0srv.cc b/storage/xtradb/srv/srv0srv.cc index 953bbba11f7..df12fd2b43f 100644 --- a/storage/xtradb/srv/srv0srv.cc +++ b/storage/xtradb/srv/srv0srv.cc @@ -515,6 +515,9 @@ UNIV_INTERN ibool srv_print_innodb_lock_monitor = FALSE; UNIV_INTERN ibool srv_print_innodb_tablespace_monitor = FALSE; UNIV_INTERN ibool srv_print_innodb_table_monitor = FALSE; +/** If this flag is set tables without primary key are not allowed */ +UNIV_INTERN my_bool srv_force_primary_key = FALSE; + /* Array of English strings describing the current state of an i/o handler thread */