diff options
author | Otto Kekäläinen <otto@seravo.fi> | 2015-09-05 21:28:14 +0300 |
---|---|---|
committer | Otto Kekäläinen <otto@seravo.fi> | 2015-09-05 21:28:14 +0300 |
commit | d6c5e7e2cc16fa63875ef9e5a63caf5c9f147e7c (patch) | |
tree | c0c1c07edf6beeb1b8e97783934b5b9a778117da /storage | |
parent | b9fee60871264ea56a5582ca7a436aabdabea6fc (diff) | |
parent | 67dbfab3d7945886caf6ba8de6a17799e3db25aa (diff) | |
download | mariadb-git-d6c5e7e2cc16fa63875ef9e5a63caf5c9f147e7c.tar.gz |
Merge branch '10.1' of https://github.com/MariaDB/server into ok-debpkg-10.1
Diffstat (limited to 'storage')
163 files changed, 6345 insertions, 1595 deletions
diff --git a/storage/connect/CMakeLists.txt b/storage/connect/CMakeLists.txt index 02fe5ee8dad..2de00db1bfd 100644 --- a/storage/connect/CMakeLists.txt +++ b/storage/connect/CMakeLists.txt @@ -48,7 +48,7 @@ IF(UNIX) # Bar: -Wfatal-errors removed (does not present in gcc on solaris10) if(WITH_WARNINGS) add_definitions(-Wall -Wextra -Wmissing-declarations) - message(STATUS "CONNECT: GCC: All warnings enabled") + #message(STATUS "CONNECT: GCC: All warnings enabled") else() add_definitions(-Wall -Wmissing-declarations) add_definitions(-Wno-write-strings) @@ -69,7 +69,7 @@ IF(UNIX) # These switches are for C++ only # add_definitions(-Wno-reorder) - message(STATUS "CONNECT: GCC: Some warnings disabled") + #message(STATUS "CONNECT: GCC: Some warnings disabled") endif(WITH_WARNINGS) add_definitions( -DUNIX -DLINUX -DUBUNTU ) diff --git a/storage/connect/ha_connect.cc b/storage/connect/ha_connect.cc index f2f37936213..e296238f0c8 100644 --- a/storage/connect/ha_connect.cc +++ b/storage/connect/ha_connect.cc @@ -169,7 +169,7 @@ #define JSONMAX 10 // JSON Default max grp size extern "C" { - char version[]= "Version 1.03.0007 June 03, 2015"; + char version[]= "Version 1.03.0007 July 05, 2015"; #if defined(__WIN__) char compver[]= "Version 1.03.0007 " __DATE__ " " __TIME__; char slash= '\\'; @@ -1114,7 +1114,7 @@ int GetIntegerTableOption(PGLOBAL g, PTOS options, char *opname, int idef) else if (!stricmp(opname, "Compressed")) opval= (options->compressed); - if (opval == NO_IVAL) { + if (opval == (ulonglong)NO_IVAL) { char *pv; if ((pv= GetListOption(g, opname, options->oplist))) @@ -2237,7 +2237,9 @@ bool ha_connect::MakeKeyWhere(PGLOBAL g, PSTRG qry, OPVAL op, char q, case OP_EQ: case OP_GT: case OP_GE: - oom|= qry->Append((PSZ)GetValStr(op, false)); + case OP_LT: + case OP_LE: + oom |= qry->Append((PSZ)GetValStr(op, false)); break; default: oom|= qry->Append(" ??? "); @@ -4017,7 +4019,27 @@ bool ha_connect::check_privileges(THD *thd, PTOS options, char *dbn) case TAB_MAC: case TAB_WMI: case TAB_OEM: - return check_access(thd, FILE_ACL, db, NULL, NULL, 0, 0); +#ifdef NO_EMBEDDED_ACCESS_CHECKS + return false; +#endif + /* + If table or table->mdl_ticket is NULL - it's a DLL, e.g. CREATE TABLE. + if the table has an MDL_EXCLUSIVE lock - it's a DDL too, e.g. the + insert step of CREATE ... SELECT. + + Otherwise it's a DML, the table was normally opened, locked, + privilege were already checked, and table->grant.privilege is set. + With SQL SECURITY DEFINER, table->grant.privilege has definer's privileges. + */ + if (!table || !table->mdl_ticket || table->mdl_ticket->get_type() == MDL_EXCLUSIVE) + return check_access(thd, FILE_ACL, db, NULL, NULL, 0, 0); + if (table->grant.privilege & FILE_ACL) + return false; + status_var_increment(thd->status_var.access_denied_errors); + my_error(access_denied_error_code(thd->password), MYF(0), + thd->security_ctx->priv_user, thd->security_ctx->priv_host, + (thd->password ? ER(ER_YES) : ER(ER_NO))); + return true; // This is temporary until a solution is found case TAB_TBL: @@ -4404,15 +4426,16 @@ int ha_connect::external_lock(THD *thd, int lock_type) xmod= MODE_ANY; // For info commands DBUG_RETURN(rc); } // endif MODE_ANY - - DBUG_ASSERT(table && table->s); - + else if (check_privileges(thd, options, table->s->db.str)) { strcpy(g->Message, "This operation requires the FILE privilege"); htrc("%s\n", g->Message); DBUG_RETURN(HA_ERR_INTERNAL_ERROR); } // endif check_privileges + + DBUG_ASSERT(table && table->s); + // Table mode depends on the query type newmode= CheckMode(g, thd, newmode, &xcheck, &cras); @@ -5666,6 +5689,14 @@ int ha_connect::create(const char *name, TABLE *table_arg, PGLOBAL g= xp->g; DBUG_ENTER("ha_connect::create"); + /* + This assignment fixes test failures if some + "ALTER TABLE t1 ADD KEY(a)" query exits on ER_ACCESS_DENIED_ERROR + (e.g. on missing FILE_ACL). All following "CREATE TABLE" failed with + "ERROR 1105: CONNECT index modification should be in-place" + TODO: check with Olivier. + */ + g->Xchk= NULL; int sqlcom= thd_sql_command(table_arg->in_use); PTOS options= GetTableOptionStruct(table_arg->s); @@ -6155,10 +6186,6 @@ bool ha_connect::FileExists(const char *fn, bool bf) int n; struct stat info; - if (check_access(ha_thd(), FILE_ACL, table->s->db.str, - NULL, NULL, 0, 0)) - return true; - #if defined(__WIN__) s= "\\"; #else // !__WIN__ @@ -6648,6 +6675,6 @@ maria_declare_plugin(connect) NULL, /* status variables */ connect_system_variables, /* system variables */ "1.03.0007", /* string version */ - MariaDB_PLUGIN_MATURITY_BETA /* maturity */ + MariaDB_PLUGIN_MATURITY_GAMMA /* maturity */ } maria_declare_plugin_end; diff --git a/storage/connect/jsonudf.cpp b/storage/connect/jsonudf.cpp index ff4025ee0fb..b2d983712aa 100644 --- a/storage/connect/jsonudf.cpp +++ b/storage/connect/jsonudf.cpp @@ -261,7 +261,7 @@ static PJVAL MakeValue(PGLOBAL g, UDF_ARGS *args, int i) break; case INT_RESULT: - jvp->SetInteger(g, *(int*)sap); + jvp->SetInteger(g, (int)*(long long*)sap); break; case REAL_RESULT: jvp->SetFloat(g, *(double*)sap); diff --git a/storage/connect/mysql-test/connect/r/endian.result b/storage/connect/mysql-test/connect/r/endian.result new file mode 100644 index 00000000000..a4c81e43b6b --- /dev/null +++ b/storage/connect/mysql-test/connect/r/endian.result @@ -0,0 +1,105 @@ +SET time_zone='+00:00'; +# +# Testing little endian table +# +CREATE TABLE t1 +( +fig INT(4) NOT NULL FIELD_FORMAT='C', +name CHAR(10) NOT NULL, +birth DATE NOT NULL FIELD_FORMAT='L', +id CHAR(5) NOT NULL FIELD_FORMAT='L2', +salary DOUBLE(9,2) NOT NULL DEFAULT 0.00 FIELD_FORMAT='LF', +dept INT(4) NOT NULL FIELD_FORMAT='L2' +) ENGINE=CONNECT TABLE_TYPE=BIN BLOCK_SIZE=5 FILE_NAME='Testbal.dat'; +SELECT * FROM t1; +fig name birth id salary dept +5500 ARCHIBALD 1980-01-25 3789 4380.50 318 +123 OLIVER 1953-08-10 23456 3400.68 2158 +3123 FOO 2002-07-23 888 0.00 318 +INSERT INTO t1 VALUES (5555,'RONALD','1980-02-26','3333',4444.44,555); +SELECT * FROM t1; +fig name birth id salary dept +5500 ARCHIBALD 1980-01-25 3789 4380.50 318 +123 OLIVER 1953-08-10 23456 3400.68 2158 +3123 FOO 2002-07-23 888 0.00 318 +5555 RONALD 1980-02-26 3333 4444.44 555 +DROP TABLE t1; +CREATE TABLE t1 +( +fig INT(4) NOT NULL FIELD_FORMAT='C', +name CHAR(10) NOT NULL, +birth DATE NOT NULL, +id CHAR(5) NOT NULL FIELD_FORMAT='S', +salary DOUBLE(9,2) NOT NULL DEFAULT 0.00 FIELD_FORMAT='F', +dept INT(4) NOT NULL FIELD_FORMAT='S' +) ENGINE=CONNECT TABLE_TYPE=BIN BLOCK_SIZE=5 FILE_NAME='Testbal.dat' OPTION_LIST='Endian=Little'; +SELECT * FROM t1; +fig name birth id salary dept +5500 ARCHIBALD 1980-01-25 3789 4380.50 318 +123 OLIVER 1953-08-10 23456 3400.68 2158 +3123 FOO 2002-07-23 888 0.00 318 +5555 RONALD 1980-02-26 3333 4444.44 555 +# +# Testing big endian table +# +CREATE TABLE t2 ( +fig INT(4) NOT NULL FIELD_FORMAT='C', +name CHAR(10) NOT NULL, +birth DATE NOT NULL FIELD_FORMAT='B', +id CHAR(5) NOT NULL FIELD_FORMAT='BS', +salary DOUBLE(9,2) NOT NULL DEFAULT 0.00 FIELD_FORMAT='BF', +dept INT(4) NOT NULL FIELD_FORMAT='B2' +) ENGINE=CONNECT TABLE_TYPE=BIN BLOCK_SIZE=5 FILE_NAME='Testbal.bin'; +INSERT INTO t2 SELECT * FROM t1; +SELECT * FROM t2; +fig name birth id salary dept +5500 ARCHIBALD 1980-01-25 3789 4380.50 318 +123 OLIVER 1953-08-10 23456 3400.68 2158 +3123 FOO 2002-07-23 888 0.00 318 +5555 RONALD 1980-02-26 3333 4444.44 555 +DROP TABLE t2; +CREATE TABLE t2 ( +fig INT(4) NOT NULL FIELD_FORMAT='C', +name CHAR(10) NOT NULL, +birth DATE NOT NULL, +id CHAR(5) NOT NULL FIELD_FORMAT='S', +salary DOUBLE(9,2) NOT NULL DEFAULT 0.00 FIELD_FORMAT='F', +dept INT(4) NOT NULL FIELD_FORMAT='2' +) ENGINE=CONNECT TABLE_TYPE=BIN BLOCK_SIZE=5 FILE_NAME='Testbal.bin' OPTION_LIST='Endian=Big'; +SELECT * FROM t2; +fig name birth id salary dept +5500 ARCHIBALD 1980-01-25 3789 4380.50 318 +123 OLIVER 1953-08-10 23456 3400.68 2158 +3123 FOO 2002-07-23 888 0.00 318 +5555 RONALD 1980-02-26 3333 4444.44 555 +DROP TABLE t2; +CREATE TABLE t2 ( +fig CHAR(4) NOT NULL, +name CHAR(10) NOT NULL, +birth DATE NOT NULL, +id SMALLINT(5) NOT NULL, +salary DOUBLE(9,2) NOT NULL DEFAULT 0.00 FIELD_FORMAT='F', +dept SMALLINT(4) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=BIN BLOCK_SIZE=5 FILE_NAME='Testbal.bin' OPTION_LIST='Endian=Big'; +SELECT * FROM t2; +fig name birth id salary dept +5500 ARCHIBALD 1980-01-25 3789 4380.50 318 +123 OLIVER 1953-08-10 23456 3400.68 2158 +3123 FOO 2002-07-23 888 0.00 318 +5555 RONALD 1980-02-26 3333 4444.44 555 +DROP TABLE t2; +CREATE TABLE t2 ( +fig INT(4) NOT NULL FIELD_FORMAT='C', +name CHAR(10) NOT NULL, +birth DATE NOT NULL FIELD_FORMAT='B', +id CHAR(5) NOT NULL FIELD_FORMAT='BS', +salary DOUBLE(9,2) NOT NULL DEFAULT 0.00 FIELD_FORMAT='BF', +dept SMALLINT(4) NOT NULL FIELD_FORMAT='B' +) ENGINE=CONNECT TABLE_TYPE=BIN BLOCK_SIZE=5 FILE_NAME='Testbal.bin'; +SELECT * FROM t2; +fig name birth id salary dept +5500 ARCHIBALD 1980-01-25 3789 4380.50 318 +123 OLIVER 1953-08-10 23456 3400.68 2158 +3123 FOO 2002-07-23 888 0.00 318 +5555 RONALD 1980-02-26 3333 4444.44 555 +DROP TABLE t1, t2; diff --git a/storage/connect/mysql-test/connect/r/grant.result b/storage/connect/mysql-test/connect/r/grant.result index ba5728703a5..4e64b983ea7 100644 --- a/storage/connect/mysql-test/connect/r/grant.result +++ b/storage/connect/mysql-test/connect/r/grant.result @@ -1,3 +1,4 @@ +set sql_mode=""; # # Testing FILE privilege # @@ -46,7 +47,7 @@ ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) SELECT user(); user() root@localhost -CREATE VIEW v1 AS SELECT * FROM t1; +CREATE SQL SECURITY INVOKER VIEW v1 AS SELECT * FROM t1; SELECT user(); user() user@localhost @@ -70,6 +71,7 @@ DROP USER user@localhost; # # Beginning of grant.inc # +CREATE USER user@localhost; GRANT ALL PRIVILEGES ON *.* TO user@localhost; REVOKE FILE ON *.* FROM user@localhost; SELECT user(); @@ -130,7 +132,7 @@ ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) SELECT user(); user() root@localhost -CREATE VIEW v1 AS SELECT * FROM t1; +CREATE SQL SECURITY INVOKER VIEW v1 AS SELECT * FROM t1; SELECT user(); user() user@localhost @@ -164,6 +166,7 @@ DROP USER user@localhost; # # Beginning of grant.inc # +CREATE USER user@localhost; GRANT ALL PRIVILEGES ON *.* TO user@localhost; REVOKE FILE ON *.* FROM user@localhost; SELECT user(); @@ -224,7 +227,7 @@ ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) SELECT user(); user() root@localhost -CREATE VIEW v1 AS SELECT * FROM t1; +CREATE SQL SECURITY INVOKER VIEW v1 AS SELECT * FROM t1; SELECT user(); user() user@localhost @@ -258,6 +261,7 @@ DROP USER user@localhost; # # Beginning of grant.inc # +CREATE USER user@localhost; GRANT ALL PRIVILEGES ON *.* TO user@localhost; REVOKE FILE ON *.* FROM user@localhost; SELECT user(); @@ -318,7 +322,7 @@ ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) SELECT user(); user() root@localhost -CREATE VIEW v1 AS SELECT * FROM t1; +CREATE SQL SECURITY INVOKER VIEW v1 AS SELECT * FROM t1; SELECT user(); user() user@localhost @@ -352,6 +356,7 @@ DROP USER user@localhost; # # Beginning of grant.inc # +CREATE USER user@localhost; GRANT ALL PRIVILEGES ON *.* TO user@localhost; REVOKE FILE ON *.* FROM user@localhost; SELECT user(); @@ -412,7 +417,7 @@ ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) SELECT user(); user() root@localhost -CREATE VIEW v1 AS SELECT * FROM t1; +CREATE SQL SECURITY INVOKER VIEW v1 AS SELECT * FROM t1; SELECT user(); user() user@localhost @@ -446,6 +451,7 @@ DROP USER user@localhost; # # Beginning of grant.inc # +CREATE USER user@localhost; GRANT ALL PRIVILEGES ON *.* TO user@localhost; REVOKE FILE ON *.* FROM user@localhost; SELECT user(); @@ -506,7 +512,7 @@ ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) SELECT user(); user() root@localhost -CREATE VIEW v1 AS SELECT * FROM t1; +CREATE SQL SECURITY INVOKER VIEW v1 AS SELECT * FROM t1; SELECT user(); user() user@localhost @@ -537,3 +543,4 @@ DROP USER user@localhost; # # End of grant.inc # +set sql_mode=default; diff --git a/storage/connect/mysql-test/connect/r/grant2.result b/storage/connect/mysql-test/connect/r/grant2.result new file mode 100644 index 00000000000..acefe6df659 --- /dev/null +++ b/storage/connect/mysql-test/connect/r/grant2.result @@ -0,0 +1,691 @@ +# +# MDEV-7574 Security definer views don't work with CONNECT ODBC tables +# +CREATE USER user@localhost; +GRANT ALL PRIVILEGES ON *.* TO user@localhost; +REVOKE FILE ON *.* FROM user@localhost; +# Testing SQLCOM_SELECT +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10); +CREATE SQL SECURITY INVOKER VIEW v1_invoker AS SELECT * FROM t1; +CREATE SQL SECURITY DEFINER VIEW v1_definer AS SELECT * FROM t1; +CREATE DEFINER=user@localhost SQL SECURITY DEFINER VIEW v1_baddefiner AS SELECT * FROM t1; +SELECT * FROM t1; +a +10 +SELECT * FROM v1_invoker; +a +10 +SELECT * FROM v1_definer; +a +10 +SELECT * FROM v1_baddefiner; +ERROR 28000: Access denied for user 'root'@'localhost' (using password: NO) +SELECT * FROM t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +SELECT * FROM v1_invoker; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +SELECT * FROM v1_definer; +a +10 +DROP VIEW v1_invoker, v1_definer, v1_baddefiner; +DROP TABLE t1; +# Testing SQLCOM_UPDATE +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10); +CREATE SQL SECURITY INVOKER VIEW v1_invoker AS SELECT * FROM t1; +CREATE SQL SECURITY DEFINER VIEW v1_definer AS SELECT * FROM t1; +UPDATE t1 SET a=11; +UPDATE v1_invoker SET a=12; +UPDATE v1_definer SET a=13; +UPDATE t1 SET a=21; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE v1_invoker SET a=22; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE v1_definer SET a=23; +DROP VIEW v1_invoker, v1_definer; +DROP TABLE t1; +# Testing SQLCOM_INSERT +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10); +CREATE SQL SECURITY INVOKER VIEW v1_invoker AS SELECT * FROM t1; +CREATE SQL SECURITY DEFINER VIEW v1_definer AS SELECT * FROM t1; +INSERT INTO t1 VALUES (11); +INSERT INTO v1_invoker VALUES (12); +INSERT INTO v1_definer VALUES (13); +INSERT INTO t1 VALUES (21); +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +INSERT INTO v1_invoker VALUES (22); +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +INSERT INTO v1_definer VALUES (23); +DROP VIEW v1_invoker, v1_definer; +DROP TABLE t1; +# Testing SQLCOM_REPLACE +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10); +CREATE SQL SECURITY INVOKER VIEW v1_invoker AS SELECT * FROM t1; +CREATE SQL SECURITY DEFINER VIEW v1_definer AS SELECT * FROM t1; +REPLACE INTO t1 VALUES (11); +ERROR 42000: CONNECT Unsupported command +REPLACE INTO v1_invoker VALUES (12); +ERROR 42000: CONNECT Unsupported command +REPLACE INTO v1_definer VALUES (13); +ERROR 42000: CONNECT Unsupported command +REPLACE INTO t1 VALUES (21); +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +REPLACE INTO v1_invoker VALUES (22); +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +REPLACE INTO v1_definer VALUES (23); +ERROR 42000: CONNECT Unsupported command +DROP VIEW v1_invoker, v1_definer; +DROP TABLE t1; +# Testing SQLCOM_DELETE +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10),(11),(12),(13),(21),(22),(23); +CREATE SQL SECURITY INVOKER VIEW v1_invoker AS SELECT * FROM t1; +CREATE SQL SECURITY DEFINER VIEW v1_definer AS SELECT * FROM t1; +DELETE FROM t1 WHERE a=11; +DELETE FROM v1_invoker WHERE a=12; +DELETE FROM v1_definer WHERE a=13; +DELETE FROM t1 WHERE a=21; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE FROM v1_invoker WHERE a=22; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE FROM v1_definer WHERE a=23; +DROP VIEW v1_invoker, v1_definer; +DROP TABLE t1; +# Testing SQLCOM_LOAD +CREATE TABLE t1 (a VARCHAR(128)) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10); +CREATE SQL SECURITY INVOKER VIEW v1_invoker AS SELECT * FROM t1; +CREATE SQL SECURITY DEFINER VIEW v1_definer AS SELECT * FROM t1; +LOAD DATA LOCAL INFILE 'MTR_SUITE_DIR/std_data/boys.txt' INTO TABLE t1; +LOAD DATA LOCAL INFILE 'MTR_SUITE_DIR/std_data/boys.txt' INTO TABLE v1_invoker; +LOAD DATA LOCAL INFILE 'MTR_SUITE_DIR/std_data/boys.txt' INTO TABLE v1_definer; +LOAD DATA LOCAL INFILE 'MTR_SUITE_DIR/std_data/boys.txt' INTO TABLE t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +LOAD DATA LOCAL INFILE 'MTR_SUITE_DIR/std_data/boys.txt' INTO TABLE v1_invoker; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +LOAD DATA LOCAL INFILE 'MTR_SUITE_DIR/std_data/boys.txt' INTO TABLE v1_definer; +DROP VIEW v1_invoker, v1_definer; +DROP TABLE t1; +# Testing SQLCOM_TRUNCATE +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10); +TRUNCATE TABLE t1; +INSERT INTO t1 VALUES (11); +TRUNCATE TABLE t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DROP TABLE t1; +# Testing SQLCOM_DROP_TABLE +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10); +DROP TABLE t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DROP TABLE t1; +# Testing SQLCOM_DROP_VIEW +# DROP VIEW does not need FILE_ACL. +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10),(11),(12),(13),(21),(22),(23); +CREATE SQL SECURITY INVOKER VIEW v1_invoker AS SELECT * FROM t1; +CREATE SQL SECURITY DEFINER VIEW v1_definer AS SELECT * FROM t1; +DROP VIEW v1_invoker, v1_definer; +CREATE SQL SECURITY INVOKER VIEW v1_invoker AS SELECT * FROM t1; +CREATE SQL SECURITY DEFINER VIEW v1_definer AS SELECT * FROM t1; +DROP VIEW v1_invoker; +DROP VIEW v1_definer; +DROP TABLE t1; +# Testing SQLCOM_CREATE_TABLE +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +# Testing SQLCOM_LOCK_TABLES +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10); +CREATE SQL SECURITY INVOKER VIEW v1_invoker AS SELECT * FROM t1; +CREATE SQL SECURITY DEFINER VIEW v1_definer AS SELECT * FROM t1; +LOCK TABLE t1 READ; +UNLOCK TABLES; +LOCK TABLE t1 WRITE; +UNLOCK TABLES; +LOCK TABLE v1_invoker READ; +UNLOCK TABLES; +LOCK TABLE v1_invoker WRITE; +UNLOCK TABLES; +LOCK TABLE v1_definer READ; +UNLOCK TABLES; +LOCK TABLE v1_definer WRITE; +UNLOCK TABLES; +LOCK TABLE t1 READ; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +LOCK TABLE t1 WRITE; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +LOCK TABLE v1_invoker READ; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +LOCK TABLE v1_invoker WRITE; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +LOCK TABLE v1_definer READ; +UNLOCK TABLES; +LOCK TABLE v1_definer WRITE; +UNLOCK TABLES; +DROP VIEW v1_invoker, v1_definer; +DROP TABLE t1; +# Testing SQLCOM_UPDATE_MULTI +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +CREATE TABLE t2 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t2.fix'; +CREATE TABLE t3 (a INT); +INSERT INTO t1 VALUES (10); +INSERT INTO t2 VALUES (20); +INSERT INTO t3 VALUES (30); +CREATE SQL SECURITY INVOKER VIEW v1_invoker AS SELECT * FROM t1; +CREATE SQL SECURITY DEFINER VIEW v1_definer AS SELECT * FROM t1; +CREATE SQL SECURITY INVOKER VIEW v2_invoker AS SELECT * FROM t2; +CREATE SQL SECURITY DEFINER VIEW v2_definer AS SELECT * FROM t2; +UPDATE t1 a1,t1 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t1 a1,t2 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t1 a1,t3 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t1 a1,v1_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t1 a1,v1_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t1 a1,v2_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t1 a1,v2_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t2 a1,t1 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t2 a1,t2 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t2 a1,t3 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t2 a1,v1_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t2 a1,v1_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t2 a1,v2_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t2 a1,v2_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t3 a1,t1 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t3 a1,t2 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t3 a1,t3 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t3 a1,v1_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t3 a1,v1_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t3 a1,v2_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t3 a1,v2_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v1_invoker a1,t1 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v1_invoker a1,t2 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v1_invoker a1,t3 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v1_invoker a1,v1_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v1_invoker a1,v1_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v1_invoker a1,v2_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v1_invoker a1,v2_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v1_definer a1,t1 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v1_definer a1,t2 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v1_definer a1,t3 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v1_definer a1,v1_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v1_definer a1,v1_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v1_definer a1,v2_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v1_definer a1,v2_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v2_invoker a1,t1 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v2_invoker a1,t2 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v2_invoker a1,t3 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v2_invoker a1,v1_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v2_invoker a1,v1_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v2_invoker a1,v2_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v2_invoker a1,v2_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v2_definer a1,t1 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v2_definer a1,t2 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v2_definer a1,t3 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v2_definer a1,v1_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v2_definer a1,v1_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v2_definer a1,v2_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v2_definer a1,v2_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t1 a1,t1 a2 SET a1.a=50 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE t1 a1,t2 a2 SET a1.a=50 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE t1 a1,t3 a2 SET a1.a=50 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE t1 a1,v1_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE t1 a1,v1_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE t1 a1,v2_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE t1 a1,v2_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE t2 a1,t1 a2 SET a1.a=50 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE t2 a1,t2 a2 SET a1.a=50 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE t2 a1,t3 a2 SET a1.a=50 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE t2 a1,v1_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE t2 a1,v1_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE t2 a1,v2_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE t2 a1,v2_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE t3 a1,t1 a2 SET a1.a=50 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE t3 a1,t2 a2 SET a1.a=50 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE t3 a1,t3 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t3 a1,v1_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE t3 a1,v1_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t3 a1,v2_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE t3 a1,v2_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v1_invoker a1,t1 a2 SET a1.a=50 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE v1_invoker a1,t2 a2 SET a1.a=50 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE v1_invoker a1,t3 a2 SET a1.a=50 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE v1_invoker a1,v1_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE v1_invoker a1,v1_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE v1_invoker a1,v2_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE v1_invoker a1,v2_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE v1_definer a1,t1 a2 SET a1.a=50 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE v1_definer a1,t2 a2 SET a1.a=50 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE v1_definer a1,t3 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v1_definer a1,v1_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE v1_definer a1,v1_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v1_definer a1,v2_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE v1_definer a1,v2_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v2_invoker a1,t1 a2 SET a1.a=50 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE v2_invoker a1,t2 a2 SET a1.a=50 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE v2_invoker a1,t3 a2 SET a1.a=50 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE v2_invoker a1,v1_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE v2_invoker a1,v1_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE v2_invoker a1,v2_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE v2_invoker a1,v2_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE v2_definer a1,t1 a2 SET a1.a=50 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE v2_definer a1,t2 a2 SET a1.a=50 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE v2_definer a1,t3 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v2_definer a1,v1_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE v2_definer a1,v1_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v2_definer a1,v2_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE v2_definer a1,v2_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +DROP VIEW v1_invoker, v1_definer, v2_invoker, v2_definer; +DROP TABLE t1, t2, t3; +# Testing SQLCOM_DELETE_MULTI +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +CREATE TABLE t2 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t2.fix'; +CREATE TABLE t3 (a INT); +INSERT INTO t1 VALUES (10); +INSERT INTO t2 VALUES (20); +INSERT INTO t3 VALUES (30); +CREATE SQL SECURITY INVOKER VIEW v1_invoker AS SELECT * FROM t1; +CREATE SQL SECURITY DEFINER VIEW v1_definer AS SELECT * FROM t1; +CREATE SQL SECURITY INVOKER VIEW v2_invoker AS SELECT * FROM t2; +CREATE SQL SECURITY DEFINER VIEW v2_definer AS SELECT * FROM t2; +DELETE a1 FROM t1 a1,t1 a2 WHERE a1.a=a2.a; +DELETE a1 FROM t1 a1,t2 a2 WHERE a1.a=a2.a; +DELETE a1 FROM t1 a1,t3 a2 WHERE a1.a=a2.a; +DELETE a1 FROM t1 a1,v1_invoker a2 WHERE a1.a=a2.a; +DELETE a1 FROM t1 a1,v1_definer a2 WHERE a1.a=a2.a; +DELETE a1 FROM t1 a1,v2_invoker a2 WHERE a1.a=a2.a; +DELETE a1 FROM t1 a1,v2_definer a2 WHERE a1.a=a2.a; +DELETE a1 FROM t2 a1,t1 a2 WHERE a1.a=a2.a; +DELETE a1 FROM t2 a1,t2 a2 WHERE a1.a=a2.a; +DELETE a1 FROM t2 a1,t3 a2 WHERE a1.a=a2.a; +DELETE a1 FROM t2 a1,v1_invoker a2 WHERE a1.a=a2.a; +DELETE a1 FROM t2 a1,v1_definer a2 WHERE a1.a=a2.a; +DELETE a1 FROM t2 a1,v2_invoker a2 WHERE a1.a=a2.a; +DELETE a1 FROM t2 a1,v2_definer a2 WHERE a1.a=a2.a; +DELETE a1 FROM t3 a1,t1 a2 WHERE a1.a=a2.a; +DELETE a1 FROM t3 a1,t2 a2 WHERE a1.a=a2.a; +DELETE a1 FROM t3 a1,t3 a2 WHERE a1.a=a2.a; +DELETE a1 FROM t3 a1,v1_invoker a2 WHERE a1.a=a2.a; +DELETE a1 FROM t3 a1,v1_definer a2 WHERE a1.a=a2.a; +DELETE a1 FROM t3 a1,v2_invoker a2 WHERE a1.a=a2.a; +DELETE a1 FROM t3 a1,v2_definer a2 WHERE a1.a=a2.a; +DELETE a1 FROM v1_invoker a1,t1 a2 WHERE a1.a=a2.a; +DELETE a1 FROM v1_invoker a1,t2 a2 WHERE a1.a=a2.a; +DELETE a1 FROM v1_invoker a1,t3 a2 WHERE a1.a=a2.a; +DELETE a1 FROM v1_invoker a1,v1_invoker a2 WHERE a1.a=a2.a; +DELETE a1 FROM v1_invoker a1,v1_definer a2 WHERE a1.a=a2.a; +DELETE a1 FROM v1_invoker a1,v2_invoker a2 WHERE a1.a=a2.a; +DELETE a1 FROM v1_invoker a1,v2_definer a2 WHERE a1.a=a2.a; +DELETE a1 FROM v1_definer a1,t1 a2 WHERE a1.a=a2.a; +DELETE a1 FROM v1_definer a1,t2 a2 WHERE a1.a=a2.a; +DELETE a1 FROM v1_definer a1,t3 a2 WHERE a1.a=a2.a; +DELETE a1 FROM v1_definer a1,v1_invoker a2 WHERE a1.a=a2.a; +DELETE a1 FROM v1_definer a1,v1_definer a2 WHERE a1.a=a2.a; +DELETE a1 FROM v1_definer a1,v2_invoker a2 WHERE a1.a=a2.a; +DELETE a1 FROM v1_definer a1,v2_definer a2 WHERE a1.a=a2.a; +DELETE a1 FROM v2_invoker a1,t1 a2 WHERE a1.a=a2.a; +DELETE a1 FROM v2_invoker a1,t2 a2 WHERE a1.a=a2.a; +DELETE a1 FROM v2_invoker a1,t3 a2 WHERE a1.a=a2.a; +DELETE a1 FROM v2_invoker a1,v1_invoker a2 WHERE a1.a=a2.a; +DELETE a1 FROM v2_invoker a1,v1_definer a2 WHERE a1.a=a2.a; +DELETE a1 FROM v2_invoker a1,v2_invoker a2 WHERE a1.a=a2.a; +DELETE a1 FROM v2_invoker a1,v2_definer a2 WHERE a1.a=a2.a; +DELETE a1 FROM v2_definer a1,t1 a2 WHERE a1.a=a2.a; +DELETE a1 FROM v2_definer a1,t2 a2 WHERE a1.a=a2.a; +DELETE a1 FROM v2_definer a1,t3 a2 WHERE a1.a=a2.a; +DELETE a1 FROM v2_definer a1,v1_invoker a2 WHERE a1.a=a2.a; +DELETE a1 FROM v2_definer a1,v1_definer a2 WHERE a1.a=a2.a; +DELETE a1 FROM v2_definer a1,v2_invoker a2 WHERE a1.a=a2.a; +DELETE a1 FROM v2_definer a1,v2_definer a2 WHERE a1.a=a2.a; +DELETE a1 FROM t1 a1,t1 a2 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE a1 FROM t1 a1,t2 a2 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE a1 FROM t1 a1,t3 a2 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE a1 FROM t1 a1,v1_invoker a2 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE a1 FROM t1 a1,v1_definer a2 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE a1 FROM t1 a1,v2_invoker a2 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE a1 FROM t1 a1,v2_definer a2 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE a1 FROM t2 a1,t1 a2 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE a1 FROM t2 a1,t2 a2 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE a1 FROM t2 a1,t3 a2 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE a1 FROM t2 a1,v1_invoker a2 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE a1 FROM t2 a1,v1_definer a2 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE a1 FROM t2 a1,v2_invoker a2 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE a1 FROM t2 a1,v2_definer a2 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE a1 FROM t3 a1,t1 a2 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE a1 FROM t3 a1,t2 a2 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE a1 FROM t3 a1,t3 a2 WHERE a1.a=a2.a; +DELETE a1 FROM t3 a1,v1_invoker a2 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE a1 FROM t3 a1,v1_definer a2 WHERE a1.a=a2.a; +DELETE a1 FROM t3 a1,v2_invoker a2 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE a1 FROM t3 a1,v2_definer a2 WHERE a1.a=a2.a; +DELETE a1 FROM v1_invoker a1,t1 a2 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE a1 FROM v1_invoker a1,t2 a2 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE a1 FROM v1_invoker a1,t3 a2 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE a1 FROM v1_invoker a1,v1_invoker a2 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE a1 FROM v1_invoker a1,v1_definer a2 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE a1 FROM v1_invoker a1,v2_invoker a2 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE a1 FROM v1_invoker a1,v2_definer a2 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE a1 FROM v1_definer a1,t1 a2 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE a1 FROM v1_definer a1,t2 a2 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE a1 FROM v1_definer a1,t3 a2 WHERE a1.a=a2.a; +DELETE a1 FROM v1_definer a1,v1_invoker a2 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE a1 FROM v1_definer a1,v1_definer a2 WHERE a1.a=a2.a; +DELETE a1 FROM v1_definer a1,v2_invoker a2 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE a1 FROM v1_definer a1,v2_definer a2 WHERE a1.a=a2.a; +DELETE a1 FROM v2_invoker a1,t1 a2 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE a1 FROM v2_invoker a1,t2 a2 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE a1 FROM v2_invoker a1,t3 a2 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE a1 FROM v2_invoker a1,v1_invoker a2 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE a1 FROM v2_invoker a1,v1_definer a2 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE a1 FROM v2_invoker a1,v2_invoker a2 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE a1 FROM v2_invoker a1,v2_definer a2 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE a1 FROM v2_definer a1,t1 a2 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE a1 FROM v2_definer a1,t2 a2 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE a1 FROM v2_definer a1,t3 a2 WHERE a1.a=a2.a; +DELETE a1 FROM v2_definer a1,v1_invoker a2 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE a1 FROM v2_definer a1,v1_definer a2 WHERE a1.a=a2.a; +DELETE a1 FROM v2_definer a1,v2_invoker a2 WHERE a1.a=a2.a; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE a1 FROM v2_definer a1,v2_definer a2 WHERE a1.a=a2.a; +DROP VIEW v1_invoker, v1_definer, v2_invoker, v2_definer; +DROP TABLE t1, t2, t3; +# Testing SQLCOM_CREATE_VIEW +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10); +CREATE SQL SECURITY INVOKER VIEW v1_invoker AS SELECT * FROM t1; +CREATE SQL SECURITY DEFINER VIEW v1_definer AS SELECT * FROM t1; +CREATE VIEW v2 AS SELECT * FROM v1_invoker; +DROP VIEW v2; +CREATE VIEW v2 AS SELECT * FROM v1_definer; +DROP VIEW v2; +CREATE VIEW v2 AS SELECT * FROM t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +CREATE VIEW v2 AS SELECT * FROM v1_invoker; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +CREATE VIEW v2 AS SELECT * FROM v1_definer; +DROP VIEW v2; +DROP VIEW v1_invoker, v1_definer; +DROP TABLE t1; +# Testing SQLCOM_INSERT_SELECT +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10); +CREATE SQL SECURITY INVOKER VIEW v1_invoker AS SELECT * FROM t1; +CREATE SQL SECURITY DEFINER VIEW v1_definer AS SELECT * FROM t1; +INSERT INTO t1 SELECT * FROM t1 WHERE a=20; +INSERT INTO t1 SELECT * FROM v1_invoker WHERE a=20; +INSERT INTO t1 SELECT * FROM v1_definer WHERE a=20; +INSERT INTO v1_invoker SELECT * FROM t1 WHERE a=20; +INSERT INTO v1_invoker SELECT * FROM v1_invoker WHERE a=20; +INSERT INTO v1_invoker SELECT * FROM v1_definer WHERE a=20; +INSERT INTO v1_definer SELECT * FROM t1 WHERE a=20; +INSERT INTO v1_definer SELECT * FROM v1_invoker WHERE a=20; +INSERT INTO v1_definer SELECT * FROM v1_definer WHERE a=20; +INSERT INTO t1 SELECT * FROM t1 WHERE a=20; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +INSERT INTO t1 SELECT * FROM v1_invoker WHERE a=20; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +INSERT INTO t1 SELECT * FROM v1_definer WHERE a=20; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +INSERT INTO v1_invoker SELECT * FROM t1 WHERE a=20; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +INSERT INTO v1_invoker SELECT * FROM v1_invoker WHERE a=20; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +INSERT INTO v1_invoker SELECT * FROM v1_definer WHERE a=20; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +INSERT INTO v1_definer SELECT * FROM t1 WHERE a=20; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +INSERT INTO v1_definer SELECT * FROM v1_invoker WHERE a=20; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +INSERT INTO v1_definer SELECT * FROM v1_definer WHERE a=20; +DROP VIEW v1_invoker, v1_definer; +DROP TABLE t1; +# Testing SQLCOM_REPLACE_SELECT +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10); +CREATE SQL SECURITY INVOKER VIEW v1_invoker AS SELECT * FROM t1; +CREATE SQL SECURITY DEFINER VIEW v1_definer AS SELECT * FROM t1; +REPLACE INTO t1 SELECT * FROM t1 WHERE a=20; +ERROR 42000: CONNECT Unsupported command +REPLACE INTO t1 SELECT * FROM v1_invoker WHERE a=20; +ERROR 42000: CONNECT Unsupported command +REPLACE INTO t1 SELECT * FROM v1_definer WHERE a=20; +ERROR 42000: CONNECT Unsupported command +REPLACE INTO v1_invoker SELECT * FROM t1 WHERE a=20; +ERROR 42000: CONNECT Unsupported command +REPLACE INTO v1_invoker SELECT * FROM v1_invoker WHERE a=20; +ERROR 42000: CONNECT Unsupported command +REPLACE INTO v1_invoker SELECT * FROM v1_definer WHERE a=20; +ERROR 42000: CONNECT Unsupported command +REPLACE INTO v1_definer SELECT * FROM t1 WHERE a=20; +ERROR 42000: CONNECT Unsupported command +REPLACE INTO v1_definer SELECT * FROM v1_invoker WHERE a=20; +ERROR 42000: CONNECT Unsupported command +REPLACE INTO v1_definer SELECT * FROM v1_definer WHERE a=20; +ERROR 42000: CONNECT Unsupported command +REPLACE INTO t1 SELECT * FROM t1 WHERE a=20; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +REPLACE INTO t1 SELECT * FROM v1_invoker WHERE a=20; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +REPLACE INTO t1 SELECT * FROM v1_definer WHERE a=20; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +REPLACE INTO v1_invoker SELECT * FROM t1 WHERE a=20; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +REPLACE INTO v1_invoker SELECT * FROM v1_invoker WHERE a=20; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +REPLACE INTO v1_invoker SELECT * FROM v1_definer WHERE a=20; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +REPLACE INTO v1_definer SELECT * FROM t1 WHERE a=20; +ERROR 42000: CONNECT Unsupported command +REPLACE INTO v1_definer SELECT * FROM v1_invoker WHERE a=20; +ERROR 42000: CONNECT Unsupported command +REPLACE INTO v1_definer SELECT * FROM v1_definer WHERE a=20; +ERROR 42000: CONNECT Unsupported command +DROP VIEW v1_invoker, v1_definer; +DROP TABLE t1; +# Testing SQLCOM_RENAME_TABLE +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10); +RENAME TABLE t1 TO t2; +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` int(11) DEFAULT NULL +) ENGINE=CONNECT DEFAULT CHARSET=latin1 `TABLE_TYPE`=fix `FILE_NAME`='t1.fix' +RENAME TABLE t2 TO t1; +RENAME TABLE t1 TO t2; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DROP TABLE t1; +# Testing SQLCOM_ALTER_TABLE (for ALTER..RENAME) +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10); +ALTER TABLE t1 RENAME TO t2; +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` int(11) DEFAULT NULL +) ENGINE=CONNECT DEFAULT CHARSET=latin1 `TABLE_TYPE`=fix `FILE_NAME`='t1.fix' +ALTER TABLE t2 RENAME TO t1; +ALTER TABLE t1 RENAME TO t2; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DROP TABLE t1; +# Testing SQLCOM_ALTER_TABLE (changing ENGINE to non-CONNECT) +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10); +ALTER TABLE t1 ENGINE=MyISAM; +DROP TABLE t1; +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10); +ALTER TABLE t1 ENGINE=MyISAM; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DROP TABLE t1; +# Testing SQLCOM_ALTER_TABLE (changing ENGINE to CONNECT) +CREATE TABLE t1 (a INT) ENGINE=MyISAM; +INSERT INTO t1 VALUES (10); +SELECT * FROM t1; +a +10 +ALTER TABLE t1 ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +DROP TABLE t1; +CREATE TABLE t1 (a INT) ENGINE=MyISAM; +INSERT INTO t1 VALUES (10); +ALTER TABLE t1 ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DROP TABLE t1; +# Testing SQLCOM_OPTIMIZE +CREATE TABLE t1 (a INT NOT NULL, KEY(a)) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10); +OPTIMIZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 optimize status OK +OPTIMIZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 optimize Error Access denied for user 'user'@'localhost' (using password: NO) +test.t1 optimize Error Can't lock file (errno: 122 "Internal (unspecified) error in handler") +test.t1 optimize error Corrupt +DROP TABLE t1; +# Testing SQLCOM_ALTER_TABLE (adding columns) +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10); +ALTER TABLE t1 ADD b INT; +Warnings: +Warning 1105 This is an outward table, table data were not modified. +ALTER TABLE t1 ADD c INT; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DROP TABLE t1; +# Testing SQLCOM_ALTER_TABLE (removing columns) +CREATE TABLE t1 (a INT,b INT,c INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10,10,10); +ALTER TABLE t1 DROP b; +Warnings: +Warning 1105 This is an outward table, table data were not modified. +ALTER TABLE t1 DROP c; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DROP TABLE t1; +# Testing SQLCOM_ALTER_TABLE (adding keys) +CREATE TABLE t1 (a INT NOT NULL,b INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10,10); +ALTER TABLE t1 ADD KEY(a); +ALTER TABLE t1 ADD KEY(b); +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DROP TABLE t1; +# Testing SQLCOM_ALTER_TABLE (removing keys) +CREATE TABLE t1 (a INT NOT NULL,b INT NOT NULL, KEY a(a), KEY b(b)) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10,10); +ALTER TABLE t1 DROP KEY a; +ALTER TABLE t1 DROP KEY b; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DROP TABLE t1; +# Testing SQLCOM_CREATE_INDEX and SQLCOM_DROP_INDEX +CREATE TABLE t1 (a INT NOT NULL,b INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10,10); +CREATE INDEX a ON t1 (a); +DROP INDEX a ON t1; +CREATE INDEX a ON t1 (a); +CREATE INDEX b ON t1 (b); +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DROP INDEX a ON t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DROP TABLE t1; +# Testing stored procedures +CREATE PROCEDURE p_definer() SQL SECURITY DEFINER +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +CREATE PROCEDURE p_invoker() SQL SECURITY INVOKER +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +CREATE DEFINER=user@localhost PROCEDURE p_baddefiner() SQL SECURITY DEFINER +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +CALL p_definer(); +DROP TABLE t1; +CALL p_invoker(); +DROP TABLE t1; +CALL p_baddefiner(); +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +CALL p_invoker(); +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +CALL p_definer(); +DROP TABLE t1; +DROP PROCEDURE p_definer; +DROP PROCEDURE p_invoker; +DROP PROCEDURE p_baddefiner; +DROP USER user@localhost; diff --git a/storage/connect/mysql-test/connect/r/ini_grant.result b/storage/connect/mysql-test/connect/r/ini_grant.result index c3acf7c8dfc..68330278183 100644 --- a/storage/connect/mysql-test/connect/r/ini_grant.result +++ b/storage/connect/mysql-test/connect/r/ini_grant.result @@ -1,8 +1,10 @@ # # Checking FILE privileges # +set sql_mode=""; GRANT ALL PRIVILEGES ON *.* TO user@localhost; REVOKE FILE ON *.* FROM user@localhost; +set sql_mode=default; SELECT user(); user() user@localhost @@ -59,7 +61,7 @@ ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) SELECT user(); user() root@localhost -CREATE VIEW v1 AS SELECT * FROM t1; +CREATE SQL SECURITY INVOKER VIEW v1 AS SELECT * FROM t1; SELECT user(); user() user@localhost diff --git a/storage/connect/mysql-test/connect/r/mysql_grant.result b/storage/connect/mysql-test/connect/r/mysql_grant.result index f8d0ee6ad6f..5f630834392 100644 --- a/storage/connect/mysql-test/connect/r/mysql_grant.result +++ b/storage/connect/mysql-test/connect/r/mysql_grant.result @@ -1,8 +1,10 @@ # # Testing FILE privilege # +set sql_mode=""; GRANT ALL PRIVILEGES ON *.* TO user@localhost; REVOKE FILE ON *.* FROM user@localhost; +set sql_mode=default; SELECT user(); user() user@localhost @@ -40,7 +42,7 @@ ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) SELECT user(); user() root@localhost -CREATE VIEW v1 AS SELECT * FROM t1; +CREATE SQL SECURITY INVOKER VIEW v1 AS SELECT * FROM t1; SELECT user(); user() user@localhost diff --git a/storage/connect/mysql-test/connect/r/mysql_index.result b/storage/connect/mysql-test/connect/r/mysql_index.result new file mode 100644 index 00000000000..4ebf10802ae --- /dev/null +++ b/storage/connect/mysql-test/connect/r/mysql_index.result @@ -0,0 +1,114 @@ +# +# Make remote table +# +CREATE TABLE t1 ( +id int(11) NOT NULL, +msg char(100) DEFAULT NULL, +PRIMARY KEY (id) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; +INSERT INTO t1 VALUES(1,'Un'),(3,'Trois'),(5,'Cinq'); +INSERT INTO t1 VALUES(2,'Two'),(4,'Four'),(6,'Six'); +SELECT * FROM t1; +id msg +1 Un +3 Trois +5 Cinq +2 Two +4 Four +6 Six +# +# Make local MYSQL table with indexed id column +# +CREATE TABLE t2 ( +id int(11) NOT NULL, +msg char(100) DEFAULT NULL, +PRIMARY KEY (id) +) ENGINE=CONNECT DEFAULT CHARSET=latin1 TABLE_TYPE=MYSQL TABNAME=t1; +# +# Testing SELECT, etc. +# +SELECT * FROM t2; +id msg +1 Un +3 Trois +5 Cinq +2 Two +4 Four +6 Six +SELECT * FROM t2 WHERE id = 3; +id msg +3 Trois +SELECT * FROM t2 WHERE id IN (2,4); +id msg +2 Two +4 Four +SELECT * FROM t2 WHERE id IN (2,4) AND msg = 'Two'; +id msg +2 Two +SELECT * FROM t2 WHERE id > 3; +id msg +5 Cinq +4 Four +6 Six +SELECT * FROM t2 WHERE id >= 3; +id msg +3 Trois +5 Cinq +4 Four +6 Six +SELECT * FROM t2 WHERE id < 3; +id msg +1 Un +2 Two +SELECT * FROM t2 WHERE id < 3 OR id > 4; +id msg +1 Un +2 Two +5 Cinq +6 Six +SELECT * FROM t2 WHERE id <= 3; +id msg +1 Un +2 Two +3 Trois +SELECT * FROM t2 WHERE id BETWEEN 3 AND 5; +id msg +3 Trois +4 Four +5 Cinq +SELECT * FROM t2 WHERE id > 2 AND id < 6; +id msg +3 Trois +4 Four +5 Cinq +SELECT * FROM t2 ORDER BY id; +id msg +1 Un +2 Two +3 Trois +4 Four +5 Cinq +6 Six +UPDATE t2 SET msg = 'Five' WHERE id = 5; +Warnings: +Note 1105 t1: 1 affected rows +SELECT * FROM t2; +id msg +1 Un +3 Trois +5 Five +2 Two +4 Four +6 Six +DELETE FROM t2 WHERE id = 4; +Warnings: +Note 1105 t1: 1 affected rows +SELECT * FROM t2; +id msg +1 Un +3 Trois +5 Five +2 Two +6 Six +DROP TABLE t2; +DROP TABLE t1; diff --git a/storage/connect/mysql-test/connect/r/odbc_sqlite3_grant.result b/storage/connect/mysql-test/connect/r/odbc_sqlite3_grant.result index 364f340eddf..06b4239bd69 100644 --- a/storage/connect/mysql-test/connect/r/odbc_sqlite3_grant.result +++ b/storage/connect/mysql-test/connect/r/odbc_sqlite3_grant.result @@ -49,10 +49,11 @@ ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) CREATE VIEW v1 AS SELECT * FROM t1; ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) # Testing a VIEW created with FILE privileges but accessed with no FILE +# using SQL SECIRITY INVOKER SELECT user(); user() root@localhost -CREATE VIEW v1 AS SELECT * FROM t1; +CREATE SQL SECURITY INVOKER VIEW v1 AS SELECT * FROM t1; SELECT user(); user() user@localhost @@ -64,6 +65,19 @@ UPDATE v1 SET a=123; ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) DELETE FROM v1; ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +# Testing a VIEW created with FILE privileges but accessed with no FILE +# using SQL SECIRITY DEFINER +DROP VIEW v1; +SELECT user(); +user() +root@localhost +CREATE SQL SECURITY DEFINER VIEW v1 AS SELECT * FROM t1; +SELECT user(); +user() +user@localhost +SELECT * FROM v1 WHERE a='test1'; +a +test1 SELECT user(); user() root@localhost diff --git a/storage/connect/mysql-test/connect/r/xml_grant.result b/storage/connect/mysql-test/connect/r/xml_grant.result index ea38e57af86..9eb818bf32f 100644 --- a/storage/connect/mysql-test/connect/r/xml_grant.result +++ b/storage/connect/mysql-test/connect/r/xml_grant.result @@ -3,6 +3,7 @@ Warning 1105 No file name. Table will use t1.xml # # Beginning of grant.inc # +CREATE USER user@localhost; GRANT ALL PRIVILEGES ON *.* TO user@localhost; REVOKE FILE ON *.* FROM user@localhost; SELECT user(); @@ -63,7 +64,7 @@ ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) SELECT user(); user() root@localhost -CREATE VIEW v1 AS SELECT * FROM t1; +CREATE SQL SECURITY INVOKER VIEW v1 AS SELECT * FROM t1; SELECT user(); user() user@localhost diff --git a/storage/connect/mysql-test/connect/t/endian.test b/storage/connect/mysql-test/connect/t/endian.test new file mode 100644 index 00000000000..e5f4a24f52d --- /dev/null +++ b/storage/connect/mysql-test/connect/t/endian.test @@ -0,0 +1,88 @@ +let $MYSQLD_DATADIR= `select @@datadir`; + +--copy_file $MTR_SUITE_DIR/std_data/Testbal.dat $MYSQLD_DATADIR/test/Testbal.dat + +SET time_zone='+00:00'; + +--echo # +--echo # Testing little endian table +--echo # +CREATE TABLE t1 +( + fig INT(4) NOT NULL FIELD_FORMAT='C', + name CHAR(10) NOT NULL, + birth DATE NOT NULL FIELD_FORMAT='L', + id CHAR(5) NOT NULL FIELD_FORMAT='L2', + salary DOUBLE(9,2) NOT NULL DEFAULT 0.00 FIELD_FORMAT='LF', + dept INT(4) NOT NULL FIELD_FORMAT='L2' +) ENGINE=CONNECT TABLE_TYPE=BIN BLOCK_SIZE=5 FILE_NAME='Testbal.dat'; +SELECT * FROM t1; +INSERT INTO t1 VALUES (5555,'RONALD','1980-02-26','3333',4444.44,555); +SELECT * FROM t1; + +DROP TABLE t1; +CREATE TABLE t1 +( + fig INT(4) NOT NULL FIELD_FORMAT='C', + name CHAR(10) NOT NULL, + birth DATE NOT NULL, + id CHAR(5) NOT NULL FIELD_FORMAT='S', + salary DOUBLE(9,2) NOT NULL DEFAULT 0.00 FIELD_FORMAT='F', + dept INT(4) NOT NULL FIELD_FORMAT='S' +) ENGINE=CONNECT TABLE_TYPE=BIN BLOCK_SIZE=5 FILE_NAME='Testbal.dat' OPTION_LIST='Endian=Little'; +SELECT * FROM t1; + +--echo # +--echo # Testing big endian table +--echo # +CREATE TABLE t2 ( + fig INT(4) NOT NULL FIELD_FORMAT='C', + name CHAR(10) NOT NULL, + birth DATE NOT NULL FIELD_FORMAT='B', + id CHAR(5) NOT NULL FIELD_FORMAT='BS', + salary DOUBLE(9,2) NOT NULL DEFAULT 0.00 FIELD_FORMAT='BF', + dept INT(4) NOT NULL FIELD_FORMAT='B2' +) ENGINE=CONNECT TABLE_TYPE=BIN BLOCK_SIZE=5 FILE_NAME='Testbal.bin'; +INSERT INTO t2 SELECT * FROM t1; +SELECT * FROM t2; + +DROP TABLE t2; +CREATE TABLE t2 ( + fig INT(4) NOT NULL FIELD_FORMAT='C', + name CHAR(10) NOT NULL, + birth DATE NOT NULL, + id CHAR(5) NOT NULL FIELD_FORMAT='S', + salary DOUBLE(9,2) NOT NULL DEFAULT 0.00 FIELD_FORMAT='F', + dept INT(4) NOT NULL FIELD_FORMAT='2' +) ENGINE=CONNECT TABLE_TYPE=BIN BLOCK_SIZE=5 FILE_NAME='Testbal.bin' OPTION_LIST='Endian=Big'; +SELECT * FROM t2; + +DROP TABLE t2; +CREATE TABLE t2 ( + fig CHAR(4) NOT NULL, + name CHAR(10) NOT NULL, + birth DATE NOT NULL, + id SMALLINT(5) NOT NULL, + salary DOUBLE(9,2) NOT NULL DEFAULT 0.00 FIELD_FORMAT='F', + dept SMALLINT(4) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=BIN BLOCK_SIZE=5 FILE_NAME='Testbal.bin' OPTION_LIST='Endian=Big'; +SELECT * FROM t2; + +DROP TABLE t2; +CREATE TABLE t2 ( + fig INT(4) NOT NULL FIELD_FORMAT='C', + name CHAR(10) NOT NULL, + birth DATE NOT NULL FIELD_FORMAT='B', + id CHAR(5) NOT NULL FIELD_FORMAT='BS', + salary DOUBLE(9,2) NOT NULL DEFAULT 0.00 FIELD_FORMAT='BF', + dept SMALLINT(4) NOT NULL FIELD_FORMAT='B' +) ENGINE=CONNECT TABLE_TYPE=BIN BLOCK_SIZE=5 FILE_NAME='Testbal.bin'; +SELECT * FROM t2; + +DROP TABLE t1, t2; + +# +# Clean up +# +--remove_file $MYSQLD_DATADIR/test/Testbal.dat +--remove_file $MYSQLD_DATADIR/test/Testbal.bin diff --git a/storage/connect/mysql-test/connect/t/grant.inc b/storage/connect/mysql-test/connect/t/grant.inc index 7bb214dc9fd..6580c845b56 100644 --- a/storage/connect/mysql-test/connect/t/grant.inc +++ b/storage/connect/mysql-test/connect/t/grant.inc @@ -1,6 +1,7 @@ --echo # --echo # Beginning of grant.inc --echo # +CREATE USER user@localhost; GRANT ALL PRIVILEGES ON *.* TO user@localhost; REVOKE FILE ON *.* FROM user@localhost; --connect(user,localhost,user,,) @@ -53,7 +54,7 @@ CREATE VIEW v1 AS SELECT * FROM t1; --echo # Testing a VIEW created with FILE privileges but accessed with no FILE --connection default SELECT user(); -CREATE VIEW v1 AS SELECT * FROM t1; +CREATE SQL SECURITY INVOKER VIEW v1 AS SELECT * FROM t1; --connection user SELECT user(); --error ER_ACCESS_DENIED_ERROR diff --git a/storage/connect/mysql-test/connect/t/grant.test b/storage/connect/mysql-test/connect/t/grant.test index 909bb4117a1..738f156d8a4 100644 --- a/storage/connect/mysql-test/connect/t/grant.test +++ b/storage/connect/mysql-test/connect/t/grant.test @@ -1,4 +1,5 @@ -- source include/not_embedded.inc +set sql_mode=""; let $MYSQLD_DATADIR= `select @@datadir`; @@ -49,7 +50,7 @@ CREATE VIEW v1 AS SELECT * FROM t1; --echo # Testing a VIEW created with FILE privileges but accessed with no FILE --connection default SELECT user(); -CREATE VIEW v1 AS SELECT * FROM t1; +CREATE SQL SECURITY INVOKER VIEW v1 AS SELECT * FROM t1; --connection user SELECT user(); --error ER_ACCESS_DENIED_ERROR @@ -92,4 +93,4 @@ let $TABLE_OPTIONS=TABLE_TYPE=VEC MAX_ROWS=100; let $FILE_EXT=VEC; --source grant.inc - +set sql_mode=default; diff --git a/storage/connect/mysql-test/connect/t/grant2.test b/storage/connect/mysql-test/connect/t/grant2.test new file mode 100644 index 00000000000..8e7d9453e70 --- /dev/null +++ b/storage/connect/mysql-test/connect/t/grant2.test @@ -0,0 +1,869 @@ +-- source include/not_embedded.inc + +# Tests that involve SQL SECURITY DEFINER (e.g. in VIEWs) +# TODO: add test with stored routines eventually. + +let $MYSQLD_DATADIR= `select @@datadir`; + +--echo # +--echo # MDEV-7574 Security definer views don't work with CONNECT ODBC tables +--echo # + +CREATE USER user@localhost; +GRANT ALL PRIVILEGES ON *.* TO user@localhost; +REVOKE FILE ON *.* FROM user@localhost; + +--echo # Testing SQLCOM_SELECT +--connection default +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10); +CREATE SQL SECURITY INVOKER VIEW v1_invoker AS SELECT * FROM t1; +CREATE SQL SECURITY DEFINER VIEW v1_definer AS SELECT * FROM t1; +CREATE DEFINER=user@localhost SQL SECURITY DEFINER VIEW v1_baddefiner AS SELECT * FROM t1; +SELECT * FROM t1; +SELECT * FROM v1_invoker; +SELECT * FROM v1_definer; +--error ER_ACCESS_DENIED_ERROR +SELECT * FROM v1_baddefiner; + +--connect(user,localhost,user,,) +--error ER_ACCESS_DENIED_ERROR +SELECT * FROM t1; +--error ER_ACCESS_DENIED_ERROR +SELECT * FROM v1_invoker; +SELECT * FROM v1_definer; +--connection default +DROP VIEW v1_invoker, v1_definer, v1_baddefiner; +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t1.fix + +--echo # Testing SQLCOM_UPDATE +--connection default +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10); +CREATE SQL SECURITY INVOKER VIEW v1_invoker AS SELECT * FROM t1; +CREATE SQL SECURITY DEFINER VIEW v1_definer AS SELECT * FROM t1; +UPDATE t1 SET a=11; +UPDATE v1_invoker SET a=12; +UPDATE v1_definer SET a=13; +--connection user +--error ER_ACCESS_DENIED_ERROR +UPDATE t1 SET a=21; +--error ER_ACCESS_DENIED_ERROR +UPDATE v1_invoker SET a=22; +UPDATE v1_definer SET a=23; +--connection default +DROP VIEW v1_invoker, v1_definer; +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t1.fix + +--echo # Testing SQLCOM_INSERT +--connection default +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10); +CREATE SQL SECURITY INVOKER VIEW v1_invoker AS SELECT * FROM t1; +CREATE SQL SECURITY DEFINER VIEW v1_definer AS SELECT * FROM t1; +INSERT INTO t1 VALUES (11); +INSERT INTO v1_invoker VALUES (12); +INSERT INTO v1_definer VALUES (13); +--connection user +--error ER_ACCESS_DENIED_ERROR +INSERT INTO t1 VALUES (21); +--error ER_ACCESS_DENIED_ERROR +INSERT INTO v1_invoker VALUES (22); +INSERT INTO v1_definer VALUES (23); +--connection default +DROP VIEW v1_invoker, v1_definer; +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t1.fix + +--echo # Testing SQLCOM_REPLACE +# REPLACE is not supported by ConnectSE, so we're testing the difference +# between ER_ACCESS_DENIED_ERROR vs ER_NOT_ALLOWED_COMMAND +--connection default +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10); +CREATE SQL SECURITY INVOKER VIEW v1_invoker AS SELECT * FROM t1; +CREATE SQL SECURITY DEFINER VIEW v1_definer AS SELECT * FROM t1; +--error ER_NOT_ALLOWED_COMMAND +REPLACE INTO t1 VALUES (11); +--error ER_NOT_ALLOWED_COMMAND +REPLACE INTO v1_invoker VALUES (12); +--error ER_NOT_ALLOWED_COMMAND +REPLACE INTO v1_definer VALUES (13); +--connection user +--error ER_ACCESS_DENIED_ERROR +REPLACE INTO t1 VALUES (21); +--error ER_ACCESS_DENIED_ERROR +REPLACE INTO v1_invoker VALUES (22); +--error ER_NOT_ALLOWED_COMMAND +REPLACE INTO v1_definer VALUES (23); +--connection default +DROP VIEW v1_invoker, v1_definer; +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t1.fix + +--echo # Testing SQLCOM_DELETE +--connection default +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10),(11),(12),(13),(21),(22),(23); +CREATE SQL SECURITY INVOKER VIEW v1_invoker AS SELECT * FROM t1; +CREATE SQL SECURITY DEFINER VIEW v1_definer AS SELECT * FROM t1; +DELETE FROM t1 WHERE a=11; +DELETE FROM v1_invoker WHERE a=12; +DELETE FROM v1_definer WHERE a=13; +--connection user +--error ER_ACCESS_DENIED_ERROR +DELETE FROM t1 WHERE a=21; +--error ER_ACCESS_DENIED_ERROR +DELETE FROM v1_invoker WHERE a=22; +DELETE FROM v1_definer WHERE a=23; +--connection default +DROP VIEW v1_invoker, v1_definer; +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t1.fix + +--echo # Testing SQLCOM_LOAD +--connection default +CREATE TABLE t1 (a VARCHAR(128)) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10); +CREATE SQL SECURITY INVOKER VIEW v1_invoker AS SELECT * FROM t1; +CREATE SQL SECURITY DEFINER VIEW v1_definer AS SELECT * FROM t1; +--replace_result $MTR_SUITE_DIR MTR_SUITE_DIR +--eval LOAD DATA LOCAL INFILE '$MTR_SUITE_DIR/std_data/boys.txt' INTO TABLE t1 +--replace_result $MTR_SUITE_DIR MTR_SUITE_DIR +--eval LOAD DATA LOCAL INFILE '$MTR_SUITE_DIR/std_data/boys.txt' INTO TABLE v1_invoker +--replace_result $MTR_SUITE_DIR MTR_SUITE_DIR +--eval LOAD DATA LOCAL INFILE '$MTR_SUITE_DIR/std_data/boys.txt' INTO TABLE v1_definer +--connection user +--replace_result $MTR_SUITE_DIR MTR_SUITE_DIR +--error ER_ACCESS_DENIED_ERROR +--eval LOAD DATA LOCAL INFILE '$MTR_SUITE_DIR/std_data/boys.txt' INTO TABLE t1 +--replace_result $MTR_SUITE_DIR MTR_SUITE_DIR +--error ER_ACCESS_DENIED_ERROR +--eval LOAD DATA LOCAL INFILE '$MTR_SUITE_DIR/std_data/boys.txt' INTO TABLE v1_invoker +--replace_result $MTR_SUITE_DIR MTR_SUITE_DIR +--eval LOAD DATA LOCAL INFILE '$MTR_SUITE_DIR/std_data/boys.txt' INTO TABLE v1_definer +--connection default +DROP VIEW v1_invoker, v1_definer; +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t1.fix + +--echo # Testing SQLCOM_TRUNCATE +--connection default +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10); +TRUNCATE TABLE t1; +INSERT INTO t1 VALUES (11); +--connection user +--error ER_ACCESS_DENIED_ERROR +TRUNCATE TABLE t1; +--connection default +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t1.fix + +# TODO: Perhaps FILE_ACL is not needed for DROP TABLE. Discuss with Olivier. +--echo # Testing SQLCOM_DROP_TABLE +--connection default +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10); +--connection user +--error ER_ACCESS_DENIED_ERROR +DROP TABLE t1; +--connection default +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t1.fix + +--echo # Testing SQLCOM_DROP_VIEW +--echo # DROP VIEW does not need FILE_ACL. +--connection default +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10),(11),(12),(13),(21),(22),(23); +CREATE SQL SECURITY INVOKER VIEW v1_invoker AS SELECT * FROM t1; +CREATE SQL SECURITY DEFINER VIEW v1_definer AS SELECT * FROM t1; +DROP VIEW v1_invoker, v1_definer; +CREATE SQL SECURITY INVOKER VIEW v1_invoker AS SELECT * FROM t1; +CREATE SQL SECURITY DEFINER VIEW v1_definer AS SELECT * FROM t1; +--connection user +DROP VIEW v1_invoker; +DROP VIEW v1_definer; +--connection default +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t1.fix + +--echo # Testing SQLCOM_CREATE_TABLE +--connection user +--error ER_ACCESS_DENIED_ERROR +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +--connection default + +--echo # Testing SQLCOM_LOCK_TABLES +--connection default +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10); +CREATE SQL SECURITY INVOKER VIEW v1_invoker AS SELECT * FROM t1; +CREATE SQL SECURITY DEFINER VIEW v1_definer AS SELECT * FROM t1; +LOCK TABLE t1 READ; +UNLOCK TABLES; +LOCK TABLE t1 WRITE; +UNLOCK TABLES; +LOCK TABLE v1_invoker READ; +UNLOCK TABLES; +LOCK TABLE v1_invoker WRITE; +UNLOCK TABLES; +LOCK TABLE v1_definer READ; +UNLOCK TABLES; +LOCK TABLE v1_definer WRITE; +UNLOCK TABLES; +--connection user +--error ER_ACCESS_DENIED_ERROR +LOCK TABLE t1 READ; +--error ER_ACCESS_DENIED_ERROR +LOCK TABLE t1 WRITE; +--error ER_ACCESS_DENIED_ERROR +LOCK TABLE v1_invoker READ; +--error ER_ACCESS_DENIED_ERROR +LOCK TABLE v1_invoker WRITE; +LOCK TABLE v1_definer READ; +UNLOCK TABLES; +LOCK TABLE v1_definer WRITE; +UNLOCK TABLES; +--connection default +DROP VIEW v1_invoker, v1_definer; +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t1.fix + +--echo # Testing SQLCOM_UPDATE_MULTI +--connection default +# t1 and t2 require FILE_ACL, t3 does not +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +CREATE TABLE t2 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t2.fix'; +CREATE TABLE t3 (a INT); +INSERT INTO t1 VALUES (10); +INSERT INTO t2 VALUES (20); +INSERT INTO t3 VALUES (30); +CREATE SQL SECURITY INVOKER VIEW v1_invoker AS SELECT * FROM t1; +CREATE SQL SECURITY DEFINER VIEW v1_definer AS SELECT * FROM t1; +CREATE SQL SECURITY INVOKER VIEW v2_invoker AS SELECT * FROM t2; +CREATE SQL SECURITY DEFINER VIEW v2_definer AS SELECT * FROM t2; +UPDATE t1 a1,t1 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t1 a1,t2 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t1 a1,t3 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t1 a1,v1_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t1 a1,v1_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t1 a1,v2_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t1 a1,v2_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t2 a1,t1 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t2 a1,t2 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t2 a1,t3 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t2 a1,v1_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t2 a1,v1_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t2 a1,v2_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t2 a1,v2_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t3 a1,t1 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t3 a1,t2 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t3 a1,t3 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t3 a1,v1_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t3 a1,v1_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t3 a1,v2_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE t3 a1,v2_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v1_invoker a1,t1 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v1_invoker a1,t2 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v1_invoker a1,t3 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v1_invoker a1,v1_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v1_invoker a1,v1_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v1_invoker a1,v2_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v1_invoker a1,v2_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v1_definer a1,t1 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v1_definer a1,t2 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v1_definer a1,t3 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v1_definer a1,v1_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v1_definer a1,v1_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v1_definer a1,v2_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v1_definer a1,v2_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v2_invoker a1,t1 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v2_invoker a1,t2 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v2_invoker a1,t3 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v2_invoker a1,v1_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v2_invoker a1,v1_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v2_invoker a1,v2_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v2_invoker a1,v2_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v2_definer a1,t1 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v2_definer a1,t2 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v2_definer a1,t3 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v2_definer a1,v1_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v2_definer a1,v1_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v2_definer a1,v2_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v2_definer a1,v2_definer a2 SET a1.a=50 WHERE a1.a=a2.a; + +--connection user + +# All queries with t1 should fail +--error ER_ACCESS_DENIED_ERROR +UPDATE t1 a1,t1 a2 SET a1.a=50 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +UPDATE t1 a1,t2 a2 SET a1.a=50 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +UPDATE t1 a1,t3 a2 SET a1.a=50 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +UPDATE t1 a1,v1_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +UPDATE t1 a1,v1_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +UPDATE t1 a1,v2_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +UPDATE t1 a1,v2_definer a2 SET a1.a=50 WHERE a1.a=a2.a; + +# All queries with t2 should fail +--error ER_ACCESS_DENIED_ERROR +UPDATE t2 a1,t1 a2 SET a1.a=50 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +UPDATE t2 a1,t2 a2 SET a1.a=50 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +UPDATE t2 a1,t3 a2 SET a1.a=50 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +UPDATE t2 a1,v1_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +UPDATE t2 a1,v1_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +UPDATE t2 a1,v2_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +UPDATE t2 a1,v2_definer a2 SET a1.a=50 WHERE a1.a=a2.a; + +# t3 does not need FILE_ALC +--error ER_ACCESS_DENIED_ERROR +UPDATE t3 a1,t1 a2 SET a1.a=50 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +UPDATE t3 a1,t2 a2 SET a1.a=50 WHERE a1.a=a2.a; +# This is OK: +UPDATE t3 a1,t3 a2 SET a1.a=50 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +UPDATE t3 a1,v1_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +# This is OK: +UPDATE t3 a1,v1_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +UPDATE t3 a1,v2_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +# This is OK: +UPDATE t3 a1,v2_definer a2 SET a1.a=50 WHERE a1.a=a2.a; + +# All queries with v1_invoker should fail +--error ER_ACCESS_DENIED_ERROR +UPDATE v1_invoker a1,t1 a2 SET a1.a=50 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +UPDATE v1_invoker a1,t2 a2 SET a1.a=50 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +UPDATE v1_invoker a1,t3 a2 SET a1.a=50 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +UPDATE v1_invoker a1,v1_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +UPDATE v1_invoker a1,v1_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +UPDATE v1_invoker a1,v2_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +UPDATE v1_invoker a1,v2_definer a2 SET a1.a=50 WHERE a1.a=a2.a; + +# v1_definer does not need FILE_ACL from the invoker +--error ER_ACCESS_DENIED_ERROR +UPDATE v1_definer a1,t1 a2 SET a1.a=50 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +UPDATE v1_definer a1,t2 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v1_definer a1,t3 a2 SET a1.a=50 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +UPDATE v1_definer a1,v1_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v1_definer a1,v1_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +UPDATE v1_definer a1,v2_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v1_definer a1,v2_definer a2 SET a1.a=50 WHERE a1.a=a2.a; + +# All queries with v2_invoker should fail +--error ER_ACCESS_DENIED_ERROR +UPDATE v2_invoker a1,t1 a2 SET a1.a=50 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +UPDATE v2_invoker a1,t2 a2 SET a1.a=50 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +UPDATE v2_invoker a1,t3 a2 SET a1.a=50 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +UPDATE v2_invoker a1,v1_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +UPDATE v2_invoker a1,v1_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +UPDATE v2_invoker a1,v2_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +UPDATE v2_invoker a1,v2_definer a2 SET a1.a=50 WHERE a1.a=a2.a; + +# v2_definer does not need FILE_ACL from the invoker +--error ER_ACCESS_DENIED_ERROR +UPDATE v2_definer a1,t1 a2 SET a1.a=50 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +UPDATE v2_definer a1,t2 a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v2_definer a1,t3 a2 SET a1.a=50 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +UPDATE v2_definer a1,v1_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v2_definer a1,v1_definer a2 SET a1.a=50 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +UPDATE v2_definer a1,v2_invoker a2 SET a1.a=50 WHERE a1.a=a2.a; +UPDATE v2_definer a1,v2_definer a2 SET a1.a=50 WHERE a1.a=a2.a; + +--connection default +DROP VIEW v1_invoker, v1_definer, v2_invoker, v2_definer; +DROP TABLE t1, t2, t3; +--remove_file $MYSQLD_DATADIR/test/t1.fix +--remove_file $MYSQLD_DATADIR/test/t2.fix + +--echo # Testing SQLCOM_DELETE_MULTI +--connection default +# t1 and t2 require FILE_ACL, t3 does not +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +CREATE TABLE t2 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t2.fix'; +CREATE TABLE t3 (a INT); +INSERT INTO t1 VALUES (10); +INSERT INTO t2 VALUES (20); +INSERT INTO t3 VALUES (30); +CREATE SQL SECURITY INVOKER VIEW v1_invoker AS SELECT * FROM t1; +CREATE SQL SECURITY DEFINER VIEW v1_definer AS SELECT * FROM t1; +CREATE SQL SECURITY INVOKER VIEW v2_invoker AS SELECT * FROM t2; +CREATE SQL SECURITY DEFINER VIEW v2_definer AS SELECT * FROM t2; +DELETE a1 FROM t1 a1,t1 a2 WHERE a1.a=a2.a; +DELETE a1 FROM t1 a1,t2 a2 WHERE a1.a=a2.a; +DELETE a1 FROM t1 a1,t3 a2 WHERE a1.a=a2.a; +DELETE a1 FROM t1 a1,v1_invoker a2 WHERE a1.a=a2.a; +DELETE a1 FROM t1 a1,v1_definer a2 WHERE a1.a=a2.a; +DELETE a1 FROM t1 a1,v2_invoker a2 WHERE a1.a=a2.a; +DELETE a1 FROM t1 a1,v2_definer a2 WHERE a1.a=a2.a; +DELETE a1 FROM t2 a1,t1 a2 WHERE a1.a=a2.a; +DELETE a1 FROM t2 a1,t2 a2 WHERE a1.a=a2.a; +DELETE a1 FROM t2 a1,t3 a2 WHERE a1.a=a2.a; +DELETE a1 FROM t2 a1,v1_invoker a2 WHERE a1.a=a2.a; +DELETE a1 FROM t2 a1,v1_definer a2 WHERE a1.a=a2.a; +DELETE a1 FROM t2 a1,v2_invoker a2 WHERE a1.a=a2.a; +DELETE a1 FROM t2 a1,v2_definer a2 WHERE a1.a=a2.a; +DELETE a1 FROM t3 a1,t1 a2 WHERE a1.a=a2.a; +DELETE a1 FROM t3 a1,t2 a2 WHERE a1.a=a2.a; +DELETE a1 FROM t3 a1,t3 a2 WHERE a1.a=a2.a; +DELETE a1 FROM t3 a1,v1_invoker a2 WHERE a1.a=a2.a; +DELETE a1 FROM t3 a1,v1_definer a2 WHERE a1.a=a2.a; +DELETE a1 FROM t3 a1,v2_invoker a2 WHERE a1.a=a2.a; +DELETE a1 FROM t3 a1,v2_definer a2 WHERE a1.a=a2.a; +DELETE a1 FROM v1_invoker a1,t1 a2 WHERE a1.a=a2.a; +DELETE a1 FROM v1_invoker a1,t2 a2 WHERE a1.a=a2.a; +DELETE a1 FROM v1_invoker a1,t3 a2 WHERE a1.a=a2.a; +DELETE a1 FROM v1_invoker a1,v1_invoker a2 WHERE a1.a=a2.a; +DELETE a1 FROM v1_invoker a1,v1_definer a2 WHERE a1.a=a2.a; +DELETE a1 FROM v1_invoker a1,v2_invoker a2 WHERE a1.a=a2.a; +DELETE a1 FROM v1_invoker a1,v2_definer a2 WHERE a1.a=a2.a; +DELETE a1 FROM v1_definer a1,t1 a2 WHERE a1.a=a2.a; +DELETE a1 FROM v1_definer a1,t2 a2 WHERE a1.a=a2.a; +DELETE a1 FROM v1_definer a1,t3 a2 WHERE a1.a=a2.a; +DELETE a1 FROM v1_definer a1,v1_invoker a2 WHERE a1.a=a2.a; +DELETE a1 FROM v1_definer a1,v1_definer a2 WHERE a1.a=a2.a; +DELETE a1 FROM v1_definer a1,v2_invoker a2 WHERE a1.a=a2.a; +DELETE a1 FROM v1_definer a1,v2_definer a2 WHERE a1.a=a2.a; +DELETE a1 FROM v2_invoker a1,t1 a2 WHERE a1.a=a2.a; +DELETE a1 FROM v2_invoker a1,t2 a2 WHERE a1.a=a2.a; +DELETE a1 FROM v2_invoker a1,t3 a2 WHERE a1.a=a2.a; +DELETE a1 FROM v2_invoker a1,v1_invoker a2 WHERE a1.a=a2.a; +DELETE a1 FROM v2_invoker a1,v1_definer a2 WHERE a1.a=a2.a; +DELETE a1 FROM v2_invoker a1,v2_invoker a2 WHERE a1.a=a2.a; +DELETE a1 FROM v2_invoker a1,v2_definer a2 WHERE a1.a=a2.a; +DELETE a1 FROM v2_definer a1,t1 a2 WHERE a1.a=a2.a; +DELETE a1 FROM v2_definer a1,t2 a2 WHERE a1.a=a2.a; +DELETE a1 FROM v2_definer a1,t3 a2 WHERE a1.a=a2.a; +DELETE a1 FROM v2_definer a1,v1_invoker a2 WHERE a1.a=a2.a; +DELETE a1 FROM v2_definer a1,v1_definer a2 WHERE a1.a=a2.a; +DELETE a1 FROM v2_definer a1,v2_invoker a2 WHERE a1.a=a2.a; +DELETE a1 FROM v2_definer a1,v2_definer a2 WHERE a1.a=a2.a; + +--connection user + +# All queries with t1 should fail +--error ER_ACCESS_DENIED_ERROR +DELETE a1 FROM t1 a1,t1 a2 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +DELETE a1 FROM t1 a1,t2 a2 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +DELETE a1 FROM t1 a1,t3 a2 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +DELETE a1 FROM t1 a1,v1_invoker a2 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +DELETE a1 FROM t1 a1,v1_definer a2 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +DELETE a1 FROM t1 a1,v2_invoker a2 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +DELETE a1 FROM t1 a1,v2_definer a2 WHERE a1.a=a2.a; + +# All queries with t2 should fail +--error ER_ACCESS_DENIED_ERROR +DELETE a1 FROM t2 a1,t1 a2 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +DELETE a1 FROM t2 a1,t2 a2 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +DELETE a1 FROM t2 a1,t3 a2 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +DELETE a1 FROM t2 a1,v1_invoker a2 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +DELETE a1 FROM t2 a1,v1_definer a2 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +DELETE a1 FROM t2 a1,v2_invoker a2 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +DELETE a1 FROM t2 a1,v2_definer a2 WHERE a1.a=a2.a; + +# t3 does not need FILE_ALC +--error ER_ACCESS_DENIED_ERROR +DELETE a1 FROM t3 a1,t1 a2 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +DELETE a1 FROM t3 a1,t2 a2 WHERE a1.a=a2.a; +# This is OK: +DELETE a1 FROM t3 a1,t3 a2 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +DELETE a1 FROM t3 a1,v1_invoker a2 WHERE a1.a=a2.a; +# This is OK: +DELETE a1 FROM t3 a1,v1_definer a2 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +DELETE a1 FROM t3 a1,v2_invoker a2 WHERE a1.a=a2.a; +# This is OK: +DELETE a1 FROM t3 a1,v2_definer a2 WHERE a1.a=a2.a; + +# All queries with v1_invoker should fail +--error ER_ACCESS_DENIED_ERROR +DELETE a1 FROM v1_invoker a1,t1 a2 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +DELETE a1 FROM v1_invoker a1,t2 a2 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +DELETE a1 FROM v1_invoker a1,t3 a2 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +DELETE a1 FROM v1_invoker a1,v1_invoker a2 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +DELETE a1 FROM v1_invoker a1,v1_definer a2 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +DELETE a1 FROM v1_invoker a1,v2_invoker a2 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +DELETE a1 FROM v1_invoker a1,v2_definer a2 WHERE a1.a=a2.a; + +# v1_definer does not need FILE_ACL from the invoker +--error ER_ACCESS_DENIED_ERROR +DELETE a1 FROM v1_definer a1,t1 a2 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +DELETE a1 FROM v1_definer a1,t2 a2 WHERE a1.a=a2.a; +DELETE a1 FROM v1_definer a1,t3 a2 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +DELETE a1 FROM v1_definer a1,v1_invoker a2 WHERE a1.a=a2.a; +DELETE a1 FROM v1_definer a1,v1_definer a2 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +DELETE a1 FROM v1_definer a1,v2_invoker a2 WHERE a1.a=a2.a; +DELETE a1 FROM v1_definer a1,v2_definer a2 WHERE a1.a=a2.a; + +# All queries with v2_invoker should fail +--error ER_ACCESS_DENIED_ERROR +DELETE a1 FROM v2_invoker a1,t1 a2 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +DELETE a1 FROM v2_invoker a1,t2 a2 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +DELETE a1 FROM v2_invoker a1,t3 a2 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +DELETE a1 FROM v2_invoker a1,v1_invoker a2 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +DELETE a1 FROM v2_invoker a1,v1_definer a2 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +DELETE a1 FROM v2_invoker a1,v2_invoker a2 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +DELETE a1 FROM v2_invoker a1,v2_definer a2 WHERE a1.a=a2.a; + +# v2_definer does not need FILE_ACL from the invoker +--error ER_ACCESS_DENIED_ERROR +DELETE a1 FROM v2_definer a1,t1 a2 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +DELETE a1 FROM v2_definer a1,t2 a2 WHERE a1.a=a2.a; +DELETE a1 FROM v2_definer a1,t3 a2 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +DELETE a1 FROM v2_definer a1,v1_invoker a2 WHERE a1.a=a2.a; +DELETE a1 FROM v2_definer a1,v1_definer a2 WHERE a1.a=a2.a; +--error ER_ACCESS_DENIED_ERROR +DELETE a1 FROM v2_definer a1,v2_invoker a2 WHERE a1.a=a2.a; +DELETE a1 FROM v2_definer a1,v2_definer a2 WHERE a1.a=a2.a; + +--connection default +DROP VIEW v1_invoker, v1_definer, v2_invoker, v2_definer; +DROP TABLE t1, t2, t3; +--remove_file $MYSQLD_DATADIR/test/t1.fix +--remove_file $MYSQLD_DATADIR/test/t2.fix + +--echo # Testing SQLCOM_CREATE_VIEW +--connection default +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10); +CREATE SQL SECURITY INVOKER VIEW v1_invoker AS SELECT * FROM t1; +CREATE SQL SECURITY DEFINER VIEW v1_definer AS SELECT * FROM t1; +CREATE VIEW v2 AS SELECT * FROM v1_invoker; +DROP VIEW v2; +CREATE VIEW v2 AS SELECT * FROM v1_definer; +DROP VIEW v2; +--connection user +--error ER_ACCESS_DENIED_ERROR +CREATE VIEW v2 AS SELECT * FROM t1; +--error ER_ACCESS_DENIED_ERROR +CREATE VIEW v2 AS SELECT * FROM v1_invoker; +CREATE VIEW v2 AS SELECT * FROM v1_definer; +DROP VIEW v2; +--connection default +DROP VIEW v1_invoker, v1_definer; +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t1.fix + +--echo # Testing SQLCOM_INSERT_SELECT +--connection default +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10); +CREATE SQL SECURITY INVOKER VIEW v1_invoker AS SELECT * FROM t1; +CREATE SQL SECURITY DEFINER VIEW v1_definer AS SELECT * FROM t1; +INSERT INTO t1 SELECT * FROM t1 WHERE a=20; +INSERT INTO t1 SELECT * FROM v1_invoker WHERE a=20; +INSERT INTO t1 SELECT * FROM v1_definer WHERE a=20; +INSERT INTO v1_invoker SELECT * FROM t1 WHERE a=20; +INSERT INTO v1_invoker SELECT * FROM v1_invoker WHERE a=20; +INSERT INTO v1_invoker SELECT * FROM v1_definer WHERE a=20; +INSERT INTO v1_definer SELECT * FROM t1 WHERE a=20; +INSERT INTO v1_definer SELECT * FROM v1_invoker WHERE a=20; +INSERT INTO v1_definer SELECT * FROM v1_definer WHERE a=20; +--connection user +--error ER_ACCESS_DENIED_ERROR +INSERT INTO t1 SELECT * FROM t1 WHERE a=20; +--error ER_ACCESS_DENIED_ERROR +INSERT INTO t1 SELECT * FROM v1_invoker WHERE a=20; +--error ER_ACCESS_DENIED_ERROR +INSERT INTO t1 SELECT * FROM v1_definer WHERE a=20; +--error ER_ACCESS_DENIED_ERROR +INSERT INTO v1_invoker SELECT * FROM t1 WHERE a=20; +--error ER_ACCESS_DENIED_ERROR +INSERT INTO v1_invoker SELECT * FROM v1_invoker WHERE a=20; +--error ER_ACCESS_DENIED_ERROR +INSERT INTO v1_invoker SELECT * FROM v1_definer WHERE a=20; +--error ER_ACCESS_DENIED_ERROR +INSERT INTO v1_definer SELECT * FROM t1 WHERE a=20; +--error ER_ACCESS_DENIED_ERROR +INSERT INTO v1_definer SELECT * FROM v1_invoker WHERE a=20; +# This is OK: +INSERT INTO v1_definer SELECT * FROM v1_definer WHERE a=20; +--connection default +DROP VIEW v1_invoker, v1_definer; +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t1.fix + +--echo # Testing SQLCOM_REPLACE_SELECT +# REPLACE is not supported by CONNECT +# so we're testing ER_NOT_ALLOWED_COMMAND vs ER_ACCESS_DENIED_ERROR here +--connection default +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10); +CREATE SQL SECURITY INVOKER VIEW v1_invoker AS SELECT * FROM t1; +CREATE SQL SECURITY DEFINER VIEW v1_definer AS SELECT * FROM t1; +--error ER_NOT_ALLOWED_COMMAND +REPLACE INTO t1 SELECT * FROM t1 WHERE a=20; +--error ER_NOT_ALLOWED_COMMAND +REPLACE INTO t1 SELECT * FROM v1_invoker WHERE a=20; +--error ER_NOT_ALLOWED_COMMAND +REPLACE INTO t1 SELECT * FROM v1_definer WHERE a=20; +--error ER_NOT_ALLOWED_COMMAND +REPLACE INTO v1_invoker SELECT * FROM t1 WHERE a=20; +--error ER_NOT_ALLOWED_COMMAND +REPLACE INTO v1_invoker SELECT * FROM v1_invoker WHERE a=20; +--error ER_NOT_ALLOWED_COMMAND +REPLACE INTO v1_invoker SELECT * FROM v1_definer WHERE a=20; +--error ER_NOT_ALLOWED_COMMAND +REPLACE INTO v1_definer SELECT * FROM t1 WHERE a=20; +--error ER_NOT_ALLOWED_COMMAND +REPLACE INTO v1_definer SELECT * FROM v1_invoker WHERE a=20; +--error ER_NOT_ALLOWED_COMMAND +REPLACE INTO v1_definer SELECT * FROM v1_definer WHERE a=20; + +--connection user +--error ER_ACCESS_DENIED_ERROR +REPLACE INTO t1 SELECT * FROM t1 WHERE a=20; +--error ER_ACCESS_DENIED_ERROR +REPLACE INTO t1 SELECT * FROM v1_invoker WHERE a=20; +--error ER_ACCESS_DENIED_ERROR +REPLACE INTO t1 SELECT * FROM v1_definer WHERE a=20; +--error ER_ACCESS_DENIED_ERROR +REPLACE INTO v1_invoker SELECT * FROM t1 WHERE a=20; +--error ER_ACCESS_DENIED_ERROR +REPLACE INTO v1_invoker SELECT * FROM v1_invoker WHERE a=20; +--error ER_ACCESS_DENIED_ERROR +REPLACE INTO v1_invoker SELECT * FROM v1_definer WHERE a=20; +--error ER_NOT_ALLOWED_COMMAND +REPLACE INTO v1_definer SELECT * FROM t1 WHERE a=20; +--error ER_NOT_ALLOWED_COMMAND +REPLACE INTO v1_definer SELECT * FROM v1_invoker WHERE a=20; +--error ER_NOT_ALLOWED_COMMAND +REPLACE INTO v1_definer SELECT * FROM v1_definer WHERE a=20; +--connection default +DROP VIEW v1_invoker, v1_definer; +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t1.fix + +--echo # Testing SQLCOM_RENAME_TABLE +--connection default +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10); +RENAME TABLE t1 TO t2; +SHOW CREATE TABLE t2; +RENAME TABLE t2 TO t1; +--connection user +# TODO: Perhaps FILE_ACL is needed for RENAME. Discuss with Oliver. +--error ER_ACCESS_DENIED_ERROR +RENAME TABLE t1 TO t2; +--connection default +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t1.fix + +--echo # Testing SQLCOM_ALTER_TABLE (for ALTER..RENAME) +--connection default +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10); +ALTER TABLE t1 RENAME TO t2; +SHOW CREATE TABLE t2; +ALTER TABLE t2 RENAME TO t1; +--connection user +# TODO: Perhaps FILE_ACL is not needed for ALTER..RENAME. Discuss with Olivier. +--error ER_ACCESS_DENIED_ERROR +ALTER TABLE t1 RENAME TO t2; +--connection default +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t1.fix + +--echo # Testing SQLCOM_ALTER_TABLE (changing ENGINE to non-CONNECT) +--connection default +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10); +ALTER TABLE t1 ENGINE=MyISAM; +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t1.fix +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10); +--connection user +--error ER_ACCESS_DENIED_ERROR +ALTER TABLE t1 ENGINE=MyISAM; +--connection default +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t1.fix + +--echo # Testing SQLCOM_ALTER_TABLE (changing ENGINE to CONNECT) +--connection default +CREATE TABLE t1 (a INT) ENGINE=MyISAM; +INSERT INTO t1 VALUES (10); +SELECT * FROM t1; +# This should succeed, as 't1.fix' does not exists. +ALTER TABLE t1 ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +DROP TABLE t1; +CREATE TABLE t1 (a INT) ENGINE=MyISAM; +INSERT INTO t1 VALUES (10); +--connection user +--error ER_ACCESS_DENIED_ERROR +ALTER TABLE t1 ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +--connection default +DROP TABLE t1; + +--echo # Testing SQLCOM_OPTIMIZE +--connection default +CREATE TABLE t1 (a INT NOT NULL, KEY(a)) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10); +OPTIMIZE TABLE t1; +--connection user +# This command succeeds, but reports "Access denied" in the "Msg_text" column. +OPTIMIZE TABLE t1; +--connection default +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t1.fix + +--echo # Testing SQLCOM_ALTER_TABLE (adding columns) +--connection default +CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10); +ALTER TABLE t1 ADD b INT; +--connection user +--error ER_ACCESS_DENIED_ERROR +ALTER TABLE t1 ADD c INT; +--connection default +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t1.fix + +--echo # Testing SQLCOM_ALTER_TABLE (removing columns) +--connection default +CREATE TABLE t1 (a INT,b INT,c INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10,10,10); +ALTER TABLE t1 DROP b; +--connection user +--error ER_ACCESS_DENIED_ERROR +ALTER TABLE t1 DROP c; +--connection default +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t1.fix + +--echo # Testing SQLCOM_ALTER_TABLE (adding keys) +--connection default +CREATE TABLE t1 (a INT NOT NULL,b INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10,10); +ALTER TABLE t1 ADD KEY(a); +--connection user +--error ER_ACCESS_DENIED_ERROR +ALTER TABLE t1 ADD KEY(b); +--connection default +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t1.fix +--remove_file $MYSQLD_DATADIR/test/t1.fnx + +--echo # Testing SQLCOM_ALTER_TABLE (removing keys) +--connection default +CREATE TABLE t1 (a INT NOT NULL,b INT NOT NULL, KEY a(a), KEY b(b)) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10,10); +ALTER TABLE t1 DROP KEY a; +--connection user +--error ER_ACCESS_DENIED_ERROR +ALTER TABLE t1 DROP KEY b; +--connection default +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t1.fix +--remove_file $MYSQLD_DATADIR/test/t1.fnx + +--echo # Testing SQLCOM_CREATE_INDEX and SQLCOM_DROP_INDEX +--connection default +CREATE TABLE t1 (a INT NOT NULL,b INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +INSERT INTO t1 VALUES (10,10); +CREATE INDEX a ON t1 (a); +DROP INDEX a ON t1; +CREATE INDEX a ON t1 (a); +--connection user +--error ER_ACCESS_DENIED_ERROR +CREATE INDEX b ON t1 (b); +--error ER_ACCESS_DENIED_ERROR +DROP INDEX a ON t1; +--connection default +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t1.fix +--remove_file $MYSQLD_DATADIR/test/t1.fnx + +--echo # Testing stored procedures +CREATE PROCEDURE p_definer() SQL SECURITY DEFINER + CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +CREATE PROCEDURE p_invoker() SQL SECURITY INVOKER + CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; +CREATE DEFINER=user@localhost PROCEDURE p_baddefiner() SQL SECURITY DEFINER + CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=fix FILE_NAME='t1.fix'; + +CALL p_definer(); +DROP TABLE t1; +CALL p_invoker(); +DROP TABLE t1; +--error ER_ACCESS_DENIED_ERROR +CALL p_baddefiner(); + +--connection user +--error ER_ACCESS_DENIED_ERROR +CALL p_invoker(); +CALL p_definer(); + +--connection default +DROP TABLE t1; +DROP PROCEDURE p_definer; +DROP PROCEDURE p_invoker; +DROP PROCEDURE p_baddefiner; + +DROP USER user@localhost; diff --git a/storage/connect/mysql-test/connect/t/ini_grant.test b/storage/connect/mysql-test/connect/t/ini_grant.test index 30678645692..b0ddcb57979 100644 --- a/storage/connect/mysql-test/connect/t/ini_grant.test +++ b/storage/connect/mysql-test/connect/t/ini_grant.test @@ -5,8 +5,10 @@ let $MYSQLD_DATADIR= `select @@datadir`; --echo # --echo # Checking FILE privileges --echo # +set sql_mode=""; GRANT ALL PRIVILEGES ON *.* TO user@localhost; REVOKE FILE ON *.* FROM user@localhost; +set sql_mode=default; --connect(user,localhost,user,,) --connection user SELECT user(); @@ -54,7 +56,7 @@ CREATE VIEW v1 AS SELECT * FROM t1; --echo # Testing a VIEW created with FILE privileges but accessed with no FILE --connection default SELECT user(); -CREATE VIEW v1 AS SELECT * FROM t1; +CREATE SQL SECURITY INVOKER VIEW v1 AS SELECT * FROM t1; --connection user SELECT user(); --error ER_ACCESS_DENIED_ERROR diff --git a/storage/connect/mysql-test/connect/t/mysql_grant.test b/storage/connect/mysql-test/connect/t/mysql_grant.test index 7c75103ed3b..7d3d05cb8fd 100644 --- a/storage/connect/mysql-test/connect/t/mysql_grant.test +++ b/storage/connect/mysql-test/connect/t/mysql_grant.test @@ -19,8 +19,10 @@ DROP TABLE t1; --echo # --echo # Testing FILE privilege --echo # +set sql_mode=""; GRANT ALL PRIVILEGES ON *.* TO user@localhost; REVOKE FILE ON *.* FROM user@localhost; +set sql_mode=default; --connect(user,localhost,user,,) --connection user SELECT user(); @@ -54,7 +56,7 @@ CREATE VIEW v1 AS SELECT * FROM t1; --echo # Testing a VIEW created with FILE privileges but accessed with no FILE --connection default SELECT user(); -CREATE VIEW v1 AS SELECT * FROM t1; +CREATE SQL SECURITY INVOKER VIEW v1 AS SELECT * FROM t1; --connection user SELECT user(); --error ER_ACCESS_DENIED_ERROR diff --git a/storage/connect/mysql-test/connect/t/mysql_index.test b/storage/connect/mysql-test/connect/t/mysql_index.test new file mode 100644 index 00000000000..9a162b4d8e3 --- /dev/null +++ b/storage/connect/mysql-test/connect/t/mysql_index.test @@ -0,0 +1,66 @@ +-- source include/not_embedded.inc + +# +# TODO: consider a possibility to run this test +# against some remote MySQL server +# + +let $PORT= `select @@port`; + +--disable_query_log +--replace_result $PORT PORT +--error 0,ER_UNKNOWN_ERROR +--eval CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='tx1' OPTION_LIST='host=localhost,user=root,port=$PORT' +if (!`SELECT count(*) FROM INFORMATION_SCHEMA.TABLES + WHERE TABLE_SCHEMA='test' AND TABLE_NAME='t1' + AND ENGINE='CONNECT' + AND CREATE_OPTIONS LIKE '%`table_type`=MySQL%'`) +{ + Skip Need MySQL support; +} +DROP TABLE t1; +--enable_query_log + +--echo # +--echo # Make remote table +--echo # +CREATE TABLE t1 ( + id int(11) NOT NULL, + msg char(100) DEFAULT NULL, + PRIMARY KEY (id) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; +INSERT INTO t1 VALUES(1,'Un'),(3,'Trois'),(5,'Cinq'); +INSERT INTO t1 VALUES(2,'Two'),(4,'Four'),(6,'Six'); +SELECT * FROM t1; + +--echo # +--echo # Make local MYSQL table with indexed id column +--echo # +CREATE TABLE t2 ( + id int(11) NOT NULL, + msg char(100) DEFAULT NULL, + PRIMARY KEY (id) +) ENGINE=CONNECT DEFAULT CHARSET=latin1 TABLE_TYPE=MYSQL TABNAME=t1; + +--echo # +--echo # Testing SELECT, etc. +--echo # +SELECT * FROM t2; +SELECT * FROM t2 WHERE id = 3; +SELECT * FROM t2 WHERE id IN (2,4); +SELECT * FROM t2 WHERE id IN (2,4) AND msg = 'Two'; +SELECT * FROM t2 WHERE id > 3; +SELECT * FROM t2 WHERE id >= 3; +SELECT * FROM t2 WHERE id < 3; +SELECT * FROM t2 WHERE id < 3 OR id > 4; +SELECT * FROM t2 WHERE id <= 3; +SELECT * FROM t2 WHERE id BETWEEN 3 AND 5; +SELECT * FROM t2 WHERE id > 2 AND id < 6; +SELECT * FROM t2 ORDER BY id; +UPDATE t2 SET msg = 'Five' WHERE id = 5; +SELECT * FROM t2; +DELETE FROM t2 WHERE id = 4; +SELECT * FROM t2; + +DROP TABLE t2; +DROP TABLE t1; diff --git a/storage/connect/mysql-test/connect/t/odbc_postgresql.test b/storage/connect/mysql-test/connect/t/odbc_postgresql.test index f634b34323f..7fc16130713 100644 --- a/storage/connect/mysql-test/connect/t/odbc_postgresql.test +++ b/storage/connect/mysql-test/connect/t/odbc_postgresql.test @@ -27,6 +27,15 @@ #Servername=localhost #Port=5432 # +# 5. Allow user "mtr" to connect to the database "mtr" +# Add this line into the begginning of pg_hba.conf +# (usually /var/lib/pgsql/data/pg_hba.conf on Linux): +#host mtr mtr 127.0.0.1/32 password +# +# 6. Restart the server: +# sudo service postgresql restart +# +# SET NAMES utf8; diff --git a/storage/connect/mysql-test/connect/t/odbc_sqlite3_grant.test b/storage/connect/mysql-test/connect/t/odbc_sqlite3_grant.test index 7664a4473ba..887385af2dc 100644 --- a/storage/connect/mysql-test/connect/t/odbc_sqlite3_grant.test +++ b/storage/connect/mysql-test/connect/t/odbc_sqlite3_grant.test @@ -56,9 +56,10 @@ ALTER TABLE t1 READONLY=1; CREATE VIEW v1 AS SELECT * FROM t1; --echo # Testing a VIEW created with FILE privileges but accessed with no FILE +--echo # using SQL SECIRITY INVOKER --connection default SELECT user(); -CREATE VIEW v1 AS SELECT * FROM t1; +CREATE SQL SECURITY INVOKER VIEW v1 AS SELECT * FROM t1; --connection user SELECT user(); --error ER_ACCESS_DENIED_ERROR @@ -70,6 +71,17 @@ UPDATE v1 SET a=123; --error ER_ACCESS_DENIED_ERROR DELETE FROM v1; +--echo # Testing a VIEW created with FILE privileges but accessed with no FILE +--echo # using SQL SECIRITY DEFINER +--connection default +DROP VIEW v1; +SELECT user(); +CREATE SQL SECURITY DEFINER VIEW v1 AS SELECT * FROM t1; +--connection user +SELECT user(); +SELECT * FROM v1 WHERE a='test1'; + + --disconnect user --connection default SELECT user(); diff --git a/storage/connect/tabmysql.cpp b/storage/connect/tabmysql.cpp index 19a5dfd758f..f82cca3b514 100644 --- a/storage/connect/tabmysql.cpp +++ b/storage/connect/tabmysql.cpp @@ -1058,6 +1058,24 @@ bool TDBMYSQL::ReadKey(PGLOBAL g, OPVAL op, const void *key, int len) { bool oom; int oldlen = Query->GetLength(); + PHC hc = To_Def->GetHandler(); + + if (op == OP_FIRST && hc->end_range) { +#ifdef _DEBUG + assert(!key); +#endif + key_range *end_key = &hc->save_end_range; + + key = end_key->key; + len = end_key->length; + + switch (end_key->flag) { + case HA_READ_BEFORE_KEY: op = OP_LT; break; + case HA_READ_AFTER_KEY: op = OP_LE; break; + default: key = NULL; + } // endswitch flag + + } // endif OP_FIRST if (!key || op == OP_NEXT || Mode == MODE_UPDATE || Mode == MODE_DELETE) { @@ -1069,22 +1087,12 @@ bool TDBMYSQL::ReadKey(PGLOBAL g, OPVAL op, const void *key, int len) } // endif key return false; - } else if (op == OP_FIRST) { - if (To_CondFil) { - oom = Query->Append(" WHERE "); - - if ((oom |= Query->Append(To_CondFil->Body))) { - strcpy(g->Message, "Readkey: Out of memory"); - return true; - } // endif oom - - } // endif To_Condfil - } else { if (Myc.m_Res) Myc.FreeResult(); - To_Def->GetHandler()->MakeKeyWhere(g, Query, op, '`', key, len); + if (hc->MakeKeyWhere(g, Query, op, '`', key, len)) + return true; if (To_CondFil) { oom = Query->Append(" AND ("); diff --git a/storage/connect/tabodbc.cpp b/storage/connect/tabodbc.cpp index 307509848f4..098ed1ac114 100644 --- a/storage/connect/tabodbc.cpp +++ b/storage/connect/tabodbc.cpp @@ -1268,9 +1268,10 @@ void ODBCCOL::ReadColumn(PGLOBAL g) } // endif Buf_Type - // Handle null values - if (Value->IsZero()) - Value->SetNull(Nullable); + // Nulls are handled by StrLen[n] == SQL_NULL_DATA + // MDEV-8561 +//if (Value->IsZero()) +// Value->SetNull(Nullable); if (trace) { char buf[64]; diff --git a/storage/federatedx/federatedx_io_mysql.cc b/storage/federatedx/federatedx_io_mysql.cc index 1ff6abc4c77..6f551b41311 100644 --- a/storage/federatedx/federatedx_io_mysql.cc +++ b/storage/federatedx/federatedx_io_mysql.cc @@ -30,6 +30,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define MYSQL_SERVER 1 #include <my_global.h> #include "sql_priv.h" +#include <mysqld_error.h> #include "ha_federatedx.h" diff --git a/storage/innobase/api/api0api.cc b/storage/innobase/api/api0api.cc index 0fe21423232..739ea9f7572 100644 --- a/storage/innobase/api/api0api.cc +++ b/storage/innobase/api/api0api.cc @@ -245,7 +245,7 @@ ib_open_table_by_id( dict_mutex_enter_for_mysql(); } - table = dict_table_open_on_id(table_id, FALSE, DICT_TABLE_OP_NORMAL); + table = dict_table_open_on_id(table_id, TRUE, DICT_TABLE_OP_NORMAL); if (table != NULL && table->ibd_file_missing) { table = NULL; @@ -2116,6 +2116,10 @@ ib_cursor_moveto( n_fields = dict_index_get_n_ordering_defined_by_user(prebuilt->index); + if (n_fields > dtuple_get_n_fields(tuple->ptr)) { + n_fields = dtuple_get_n_fields(tuple->ptr); + } + dtuple_set_n_fields(search_tuple, n_fields); dtuple_set_n_fields_cmp(search_tuple, n_fields); @@ -3753,14 +3757,14 @@ ib_table_truncate( if (trunc_err == DB_SUCCESS) { ut_a(ib_trx_state(ib_trx) == static_cast<ib_trx_state_t>( TRX_STATE_NOT_STARTED)); - - err = ib_trx_release(ib_trx); - ut_a(err == DB_SUCCESS); } else { err = ib_trx_rollback(ib_trx); ut_a(err == DB_SUCCESS); } + err = ib_trx_release(ib_trx); + ut_a(err == DB_SUCCESS); + /* Set the memcached_sync_count back. */ if (table != NULL && memcached_sync != 0) { dict_mutex_enter_for_mysql(); diff --git a/storage/innobase/btr/btr0btr.cc b/storage/innobase/btr/btr0btr.cc index 92539ce1524..d271dc4dd60 100644 --- a/storage/innobase/btr/btr0btr.cc +++ b/storage/innobase/btr/btr0btr.cc @@ -2,6 +2,7 @@ Copyright (c) 1994, 2013, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2012, Facebook Inc. +Copyright (c) 2014, 2015, 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 @@ -734,6 +735,20 @@ btr_root_block_get( root_page_no = dict_index_get_page(index); block = btr_block_get(space, zip_size, root_page_no, mode, index, mtr); + + if (!block) { + index->table->is_encrypted = TRUE; + index->table->corrupted = FALSE; + + ib_push_warning(index->table->thd, DB_ENCRYPTED_DECRYPT_FAILED, + "Table %s in tablespace %lu is encrypted but encryption service or" + " used key_id is not available. " + " Can't continue reading table.", + index->table->name, space); + + return NULL; + } + btr_assert_not_corrupted(block, index); #ifdef UNIV_BTR_DEBUG if (!dict_index_is_ibuf(index)) { @@ -759,8 +774,10 @@ btr_root_get( const dict_index_t* index, /*!< in: index tree */ mtr_t* mtr) /*!< in: mtr */ { - return(buf_block_get_frame(btr_root_block_get(index, RW_X_LATCH, - mtr))); + buf_block_t* root = btr_root_block_get(index, RW_X_LATCH, + mtr); + + return(root ? buf_block_get_frame(root) : NULL); } /**************************************************************//** @@ -775,7 +792,7 @@ btr_height_get( dict_index_t* index, /*!< in: index tree */ mtr_t* mtr) /*!< in/out: mini-transaction */ { - ulint height; + ulint height=0; buf_block_t* root_block; ut_ad(mtr_memo_contains(mtr, dict_index_get_lock(index), @@ -786,13 +803,15 @@ btr_height_get( /* S latches the page */ root_block = btr_root_block_get(index, RW_S_LATCH, mtr); - height = btr_page_get_level(buf_block_get_frame(root_block), mtr); + if (root_block) { + height = btr_page_get_level(buf_block_get_frame(root_block), mtr); - /* Release the S latch on the root page. */ - mtr_memo_release(mtr, root_block, MTR_MEMO_PAGE_S_FIX); + /* Release the S latch on the root page. */ + mtr_memo_release(mtr, root_block, MTR_MEMO_PAGE_S_FIX); #ifdef UNIV_SYNC_DEBUG - sync_thread_reset_level(&root_block->lock); + sync_thread_reset_level(&root_block->lock); #endif /* UNIV_SYNC_DEBUG */ + } return(height); } @@ -1240,7 +1259,7 @@ btr_get_size_and_reserved( { fseg_header_t* seg_header; page_t* root; - ulint n; + ulint n=ULINT_UNDEFINED; ulint dummy; ut_ad(mtr_memo_contains(mtr, dict_index_get_lock(index), @@ -1254,17 +1273,21 @@ btr_get_size_and_reserved( } root = btr_root_get(index, mtr); + *used = 0; + + if (root) { - seg_header = root + PAGE_HEADER + PAGE_BTR_SEG_LEAF; + seg_header = root + PAGE_HEADER + PAGE_BTR_SEG_LEAF; - n = fseg_n_reserved_pages(seg_header, used, mtr); + n = fseg_n_reserved_pages(seg_header, used, mtr); - if (flag == BTR_TOTAL_SIZE) { - seg_header = root + PAGE_HEADER + PAGE_BTR_SEG_TOP; + if (flag == BTR_TOTAL_SIZE) { + seg_header = root + PAGE_HEADER + PAGE_BTR_SEG_TOP; - n += fseg_n_reserved_pages(seg_header, &dummy, mtr); - *used += dummy; + n += fseg_n_reserved_pages(seg_header, &dummy, mtr); + *used += dummy; + } } return(n); diff --git a/storage/innobase/btr/btr0cur.cc b/storage/innobase/btr/btr0cur.cc index 1a4eb347cd2..acf9ffc45eb 100644 --- a/storage/innobase/btr/btr0cur.cc +++ b/storage/innobase/btr/btr0cur.cc @@ -3,6 +3,7 @@ Copyright (c) 1994, 2014, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2008, Google Inc. Copyright (c) 2012, Facebook Inc. +Copyright (c) 2015, MariaDB Corporation. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described @@ -347,7 +348,7 @@ search tuple should be performed in the B-tree. InnoDB does an insert immediately after the cursor. Thus, the cursor may end up on a user record, or on a page infimum record. */ UNIV_INTERN -void +dberr_t btr_cur_search_to_nth_level( /*========================*/ dict_index_t* index, /*!< in: index */ @@ -397,6 +398,7 @@ btr_cur_search_to_nth_level( page_cur_t* page_cursor; btr_op_t btr_op; ulint root_height = 0; /* remove warning */ + dberr_t err = DB_SUCCESS; #ifdef BTR_CUR_ADAPT btr_search_t* info; @@ -513,7 +515,7 @@ btr_cur_search_to_nth_level( || mode != PAGE_CUR_LE); btr_cur_n_sea++; - return; + return err; } # endif /* BTR_CUR_HASH_ADAPT */ #endif /* BTR_CUR_ADAPT */ @@ -609,7 +611,21 @@ search_loop: retry_page_get: block = buf_page_get_gen( space, zip_size, page_no, rw_latch, guess, buf_mode, - file, line, mtr); + file, line, mtr, &err); + + if (err != DB_SUCCESS) { + if (err == DB_ENCRYPTED_DECRYPT_FAILED) { + ib_push_warning((void *)NULL, + DB_ENCRYPTED_DECRYPT_FAILED, + "Table %s is encrypted but encryption service or" + " used key_id is not available. " + " Can't continue reading table.", + index->table->name); + index->table->is_encrypted = true; + } + + goto func_exit; + } if (block == NULL) { /* This must be a search to perform an insert/delete @@ -822,12 +838,14 @@ func_exit: rw_lock_s_lock(&btr_search_latch); } + + return err; } /*****************************************************************//** Opens a cursor at either end of an index. */ UNIV_INTERN -void +dberr_t btr_cur_open_at_index_side_func( /*============================*/ bool from_left, /*!< in: true if open to the low end, @@ -853,6 +871,8 @@ btr_cur_open_at_index_side_func( mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; + dberr_t err = DB_SUCCESS; + rec_offs_init(offsets_); estimate = latch_mode & BTR_ESTIMATE; @@ -890,11 +910,26 @@ btr_cur_open_at_index_side_func( height = ULINT_UNDEFINED; for (;;) { - buf_block_t* block; - page_t* page; + buf_block_t* block=NULL; + page_t* page=NULL; + block = buf_page_get_gen(space, zip_size, page_no, RW_NO_LATCH, NULL, BUF_GET, - file, line, mtr); + file, line, mtr, &err); + if (err != DB_SUCCESS) { + if (err == DB_ENCRYPTED_DECRYPT_FAILED) { + ib_push_warning((void *)NULL, + DB_ENCRYPTED_DECRYPT_FAILED, + "Table %s is encrypted but encryption service or" + " used key_id is not available. " + " Can't continue reading table.", + index->table->name); + index->table->is_encrypted = true; + } + + goto exit_loop; + } + page = buf_block_get_frame(block); ut_ad(fil_page_get_type(page) == FIL_PAGE_INDEX); ut_ad(index->id == btr_page_get_index_id(page)); @@ -979,9 +1014,12 @@ btr_cur_open_at_index_side_func( page_no = btr_node_ptr_get_child_page_no(node_ptr, offsets); } + exit_loop: if (heap) { mem_heap_free(heap); } + + return err; } /**********************************************************************//** @@ -1029,10 +1067,25 @@ btr_cur_open_at_rnd_pos_func( for (;;) { buf_block_t* block; page_t* page; + dberr_t err=DB_SUCCESS; block = buf_page_get_gen(space, zip_size, page_no, RW_NO_LATCH, NULL, BUF_GET, - file, line, mtr); + file, line, mtr, &err); + + if (err != DB_SUCCESS) { + if (err == DB_ENCRYPTED_DECRYPT_FAILED) { + ib_push_warning((void *)NULL, + DB_ENCRYPTED_DECRYPT_FAILED, + "Table %s is encrypted but encryption service or" + " used key_id is not available. " + " Can't continue reading table.", + index->table->name); + index->table->is_encrypted = true; + } + goto exit_loop; + } + page = buf_block_get_frame(block); ut_ad(fil_page_get_type(page) == FIL_PAGE_INDEX); ut_ad(index->id == btr_page_get_index_id(page)); @@ -1066,6 +1119,7 @@ btr_cur_open_at_rnd_pos_func( page_no = btr_node_ptr_get_child_page_no(node_ptr, offsets); } + exit_loop: if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } @@ -3562,6 +3616,7 @@ btr_estimate_n_rows_in_range_on_level( mtr_t mtr; page_t* page; buf_block_t* block; + dberr_t err=DB_SUCCESS; mtr_start(&mtr); @@ -3572,7 +3627,23 @@ btr_estimate_n_rows_in_range_on_level( silence a debug assertion about this. */ block = buf_page_get_gen(space, zip_size, page_no, RW_S_LATCH, NULL, BUF_GET_POSSIBLY_FREED, - __FILE__, __LINE__, &mtr); + __FILE__, __LINE__, &mtr, &err); + + if (err != DB_SUCCESS) { + if (err == DB_ENCRYPTED_DECRYPT_FAILED) { + ib_push_warning((void *)NULL, + DB_ENCRYPTED_DECRYPT_FAILED, + "Table %s is encrypted but encryption service or" + " used key_id is not available. " + " Can't continue reading table.", + index->table->name); + index->table->is_encrypted = true; + } + + mtr_commit(&mtr); + goto inexact; + } + page = buf_block_get_frame(block); diff --git a/storage/innobase/buf/buf0buf.cc b/storage/innobase/buf/buf0buf.cc index 81e6839b500..bbe93dada4f 100644 --- a/storage/innobase/buf/buf0buf.cc +++ b/storage/innobase/buf/buf0buf.cc @@ -56,6 +56,7 @@ Created 11/5/1995 Heikki Tuuri #include "srv0mon.h" #include "buf0checksum.h" #include "fil0pagecompress.h" +#include "ha_prototypes.h" #include "ut0byte.h" #include <new> @@ -313,6 +314,18 @@ on the io_type */ : (counter##_WRITTEN)) /********************************************************************//** +Check if page is maybe compressed, encrypted or both when we encounter +corrupted page. Note that we can't be 100% sure if page is corrupted +or decrypt/decompress just failed. +*/ +static +ibool +buf_page_check_corrupt( +/*===================*/ + buf_page_t* bpage); /*!< in/out: buffer page read from + disk */ + +/********************************************************************//** Gets the smallest oldest_modification lsn for any page in the pool. Returns zero if all modified pages have been flushed to disk. @return oldest modification in pool, zero if none */ @@ -1052,6 +1065,9 @@ buf_block_init( block->page.key_version = 0; block->page.page_encrypted = false; block->page.page_compressed = false; + block->page.encrypted = false; + block->page.stored_checksum = BUF_NO_CHECKSUM_MAGIC; + block->page.calculated_checksum = BUF_NO_CHECKSUM_MAGIC; block->page.real_size = 0; block->page.write_size = 0; block->modify_clock = 0; @@ -2243,7 +2259,7 @@ lookup: /* Page not in buf_pool: needs to be read from file */ ut_ad(!hash_lock); - buf_read_page(space, zip_size, offset); + buf_read_page(space, zip_size, offset, NULL); #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG ut_a(++buf_dbg_counter % 5771 || buf_validate()); @@ -2718,7 +2734,8 @@ buf_page_get_gen( BUF_GET_IF_IN_POOL_OR_WATCH */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ - mtr_t* mtr) /*!< in: mini-transaction */ + mtr_t* mtr, /*!< in: mini-transaction */ + dberr_t* err) /*!< out: error code */ { buf_block_t* block; ulint fold; @@ -2735,6 +2752,11 @@ buf_page_get_gen( ut_ad((rw_latch == RW_S_LATCH) || (rw_latch == RW_X_LATCH) || (rw_latch == RW_NO_LATCH)); + + if (err) { + *err = DB_SUCCESS; + } + #ifdef UNIV_DEBUG switch (mode) { case BUF_GET_NO_LATCH: @@ -2795,6 +2817,8 @@ loop: } if (block == NULL) { + buf_page_t* bpage=NULL; + /* Page not in buf_pool: needs to be read from file */ if (mode == BUF_GET_IF_IN_POOL_OR_WATCH) { @@ -2827,36 +2851,77 @@ loop: return(NULL); } - if (buf_read_page(space, zip_size, offset)) { + if (buf_read_page(space, zip_size, offset, &bpage)) { buf_read_ahead_random(space, zip_size, offset, ibuf_inside(mtr)); retries = 0; } else if (retries < BUF_PAGE_READ_MAX_RETRIES) { ++retries; + + bool corrupted = true; + + if (bpage) { + corrupted = buf_page_check_corrupt(bpage); + } + + /* Do not try again for encrypted pages */ + if (!corrupted) { + ib_mutex_t* pmutex = buf_page_get_mutex(bpage); + buf_pool_mutex_enter(buf_pool); + mutex_enter(pmutex); + buf_page_set_io_fix(bpage, BUF_IO_NONE); + buf_pool_mutex_exit(buf_pool); + mutex_exit(pmutex); + + if (err) { + *err = DB_ENCRYPTED_DECRYPT_FAILED; + } + return (NULL); + } + DBUG_EXECUTE_IF( "innodb_page_corruption_retries", retries = BUF_PAGE_READ_MAX_RETRIES; ); } else { + bool corrupted = true; - fprintf(stderr, "InnoDB: Error: Unable" - " to read tablespace %lu page no" - " %lu into the buffer pool after" - " %lu attempts\n" - "InnoDB: The most probable cause" - " of this error may be that the" - " table has been corrupted.\n" - "InnoDB: You can try to fix this" - " problem by using" - " innodb_force_recovery.\n" - "InnoDB: Please see reference manual" - " for more details.\n" - "InnoDB: Aborting...\n", - space, offset, - BUF_PAGE_READ_MAX_RETRIES); + if (bpage) { + corrupted = buf_page_check_corrupt(bpage); + } - ut_error; + if (corrupted) { + fprintf(stderr, "InnoDB: Error: Unable" + " to read tablespace %lu page no" + " %lu into the buffer pool after" + " %lu attempts\n" + "InnoDB: The most probable cause" + " of this error may be that the" + " table has been corrupted.\n" + "InnoDB: You can try to fix this" + " problem by using" + " innodb_force_recovery.\n" + "InnoDB: Please see reference manual" + " for more details.\n" + "InnoDB: Aborting...\n", + space, offset, + BUF_PAGE_READ_MAX_RETRIES); + + ut_error; + } else { + ib_mutex_t* pmutex = buf_page_get_mutex(bpage); + buf_pool_mutex_enter(buf_pool); + mutex_enter(pmutex); + buf_page_set_io_fix(bpage, BUF_IO_NONE); + buf_pool_mutex_exit(buf_pool); + mutex_exit(pmutex); + + if (err) { + *err = DB_ENCRYPTED_DECRYPT_FAILED; + } + return (NULL); + } } #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG @@ -3591,8 +3656,11 @@ buf_page_init_low( bpage->oldest_modification = 0; bpage->write_size = 0; bpage->key_version = 0; + bpage->stored_checksum = BUF_NO_CHECKSUM_MAGIC; + bpage->calculated_checksum = BUF_NO_CHECKSUM_MAGIC; bpage->page_encrypted = false; bpage->page_compressed = false; + bpage->encrypted = false; bpage->real_size = 0; bpage->slot = NULL; @@ -4245,34 +4313,40 @@ buf_mark_space_corrupt( ulint space = bpage->space; ibool ret = TRUE; - /* First unfix and release lock on the bpage */ - buf_pool_mutex_enter(buf_pool); - mutex_enter(buf_page_get_mutex(bpage)); - ut_ad(buf_page_get_io_fix(bpage) == BUF_IO_READ); - ut_ad(bpage->buf_fix_count == 0); + if (!bpage->encrypted) { + /* First unfix and release lock on the bpage */ + buf_pool_mutex_enter(buf_pool); + mutex_enter(buf_page_get_mutex(bpage)); + ut_ad(buf_page_get_io_fix(bpage) == BUF_IO_READ); + ut_ad(bpage->buf_fix_count == 0); - /* Set BUF_IO_NONE before we remove the block from LRU list */ - buf_page_set_io_fix(bpage, BUF_IO_NONE); + /* Set BUF_IO_NONE before we remove the block from LRU list */ + buf_page_set_io_fix(bpage, BUF_IO_NONE); - if (uncompressed) { - rw_lock_x_unlock_gen( - &((buf_block_t*) bpage)->lock, - BUF_IO_READ); - } + if (uncompressed) { + rw_lock_x_unlock_gen( + &((buf_block_t*) bpage)->lock, + BUF_IO_READ); + } - mutex_exit(buf_page_get_mutex(bpage)); + mutex_exit(buf_page_get_mutex(bpage)); + } /* Find the table with specified space id, and mark it corrupted */ if (dict_set_corrupted_by_space(space)) { - buf_LRU_free_one_page(bpage); + if (!bpage->encrypted) { + buf_LRU_free_one_page(bpage); + } } else { ret = FALSE; } - ut_ad(buf_pool->n_pend_reads > 0); - buf_pool->n_pend_reads--; + if (!bpage->encrypted) { + ut_ad(buf_pool->n_pend_reads > 0); + buf_pool->n_pend_reads--; - buf_pool_mutex_exit(buf_pool); + buf_pool_mutex_exit(buf_pool); + } return(ret); } @@ -4283,42 +4357,77 @@ corrupted page. Note that we can't be 100% sure if page is corrupted or decrypt/decompress just failed. */ static -void +ibool buf_page_check_corrupt( /*===================*/ - const buf_page_t* bpage) /*!< in/out: buffer page read from disk */ + buf_page_t* bpage) /*!< in/out: buffer page read from disk */ { ulint zip_size = buf_page_get_zip_size(bpage); byte* dst_frame = (zip_size) ? bpage->zip.data : ((buf_block_t*) bpage)->frame; unsigned key_version = bpage->key_version; bool page_compressed = bpage->page_encrypted; + ulint stored_checksum = bpage->stored_checksum; + ulint calculated_checksum = bpage->stored_checksum; bool page_compressed_encrypted = bpage->page_compressed; ulint space_id = mach_read_from_4( dst_frame + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID); fil_space_crypt_t* crypt_data = fil_space_get_crypt_data(space_id); fil_space_t* space = fil_space_found_by_id(space_id); + bool corrupted = true; + + if (key_version != 0 || page_compressed_encrypted) { + bpage->encrypted = true; + } if (key_version != 0 || (crypt_data && crypt_data->type != CRYPT_SCHEME_UNENCRYPTED) || page_compressed || page_compressed_encrypted) { - ib_logf(IB_LOG_LEVEL_ERROR, - "Maybe corruption: Block space_id %lu in file %s maybe corrupted.", - space_id, space ? space->name : "NULL"); - ib_logf(IB_LOG_LEVEL_ERROR, - "Page based on contents %s encrypted.", - (key_version == 0 && page_compressed_encrypted == false) ? "not" : "maybe"); - ib_logf(IB_LOG_LEVEL_ERROR, - "Reason could be that key_version %u in page " - "or in crypt_data %p could not be found.", - key_version, crypt_data); - ib_logf(IB_LOG_LEVEL_ERROR, - "Reason could be also that key management plugin is not found or" - "used encryption algorithm or method does not match."); - ib_logf(IB_LOG_LEVEL_ERROR, - "Based on page page compressed %d, compressed and encrypted %d.", - page_compressed, page_compressed_encrypted); + + /* Page is really corrupted if post encryption stored + checksum does not match calculated checksum after page was + read. For pages compressed and then encrypted, there is no + checksum. */ + corrupted = (!page_compressed_encrypted && stored_checksum != calculated_checksum); + + if (corrupted) { + ib_logf(IB_LOG_LEVEL_ERROR, + "%s: Block in space_id %lu in file %s corrupted.", + page_compressed_encrypted ? "Maybe corruption" : "Corruption", + space_id, space ? space->name : "NULL"); + ib_logf(IB_LOG_LEVEL_ERROR, + "Page based on contents %s encrypted.", + (key_version == 0 && page_compressed_encrypted == false) ? "not" : "maybe"); + if (stored_checksum != BUF_NO_CHECKSUM_MAGIC || calculated_checksum != BUF_NO_CHECKSUM_MAGIC) { + ib_logf(IB_LOG_LEVEL_ERROR, + "Page stored checksum %lu but calculated checksum %lu.", + stored_checksum, calculated_checksum); + } + ib_logf(IB_LOG_LEVEL_ERROR, + "Reason could be that key_version %u in page " + "or in crypt_data %p could not be found.", + key_version, crypt_data); + ib_logf(IB_LOG_LEVEL_ERROR, + "Reason could be also that key management plugin is not found or" + " used encryption algorithm or method does not match."); + ib_logf(IB_LOG_LEVEL_ERROR, + "Based on page page compressed %d, compressed and encrypted %d.", + page_compressed, page_compressed_encrypted); + } else { + ib_logf(IB_LOG_LEVEL_ERROR, + "Block in space_id %lu in file %s encrypted.", + space_id, space ? space->name : "NULL"); + ib_logf(IB_LOG_LEVEL_ERROR, + "However key management plugin or used key_id %u is not found or" + " used encryption algorithm or method does not match.", + key_version); + ib_logf(IB_LOG_LEVEL_ERROR, + "Marking tablespace as missing. You may drop this table or" + " install correct key management plugin and key file."); + } } + + return corrupted; } /********************************************************************//** @@ -4437,44 +4546,46 @@ buf_page_io_complete( goto page_not_corrupt; ;); corrupt: - fil_system_enter(); - space = fil_space_get_by_id(bpage->space); - fil_system_exit(); - - ib_logf(IB_LOG_LEVEL_ERROR, - "Database page corruption on disk" - " or a failed"); - ib_logf(IB_LOG_LEVEL_ERROR, - "Space %lu file %s read of page %lu.", - (ulint)bpage->space, - space ? space->name : "NULL", - (ulong) bpage->offset); - ib_logf(IB_LOG_LEVEL_ERROR, - "You may have to recover" - " from a backup."); - - buf_page_check_corrupt(bpage); - - buf_page_print(frame, buf_page_get_zip_size(bpage), - BUF_PAGE_PRINT_NO_CRASH); - - ib_logf(IB_LOG_LEVEL_ERROR, - "It is also possible that your operating" - "system has corrupted its own file cache."); - ib_logf(IB_LOG_LEVEL_ERROR, - "and rebooting your computer removes the error."); - ib_logf(IB_LOG_LEVEL_ERROR, - "If the corrupt page is an index page you can also try to"); - ib_logf(IB_LOG_LEVEL_ERROR, - "fix the corruption by dumping, dropping, and reimporting"); - ib_logf(IB_LOG_LEVEL_ERROR, - "the corrupt table. You can use CHECK"); - ib_logf(IB_LOG_LEVEL_ERROR, - "TABLE to scan your table for corruption."); - ib_logf(IB_LOG_LEVEL_ERROR, - "See also " - REFMAN "forcing-innodb-recovery.html" - " about forcing recovery."); + bool corrupted = buf_page_check_corrupt(bpage); + + if (corrupted) { + fil_system_enter(); + space = fil_space_get_by_id(bpage->space); + fil_system_exit(); + ib_logf(IB_LOG_LEVEL_ERROR, + "Database page corruption on disk" + " or a failed"); + ib_logf(IB_LOG_LEVEL_ERROR, + "Space %lu file %s read of page %lu.", + (ulint)bpage->space, + space ? space->name : "NULL", + (ulong) bpage->offset); + ib_logf(IB_LOG_LEVEL_ERROR, + "You may have to recover" + " from a backup."); + + + buf_page_print(frame, buf_page_get_zip_size(bpage), + BUF_PAGE_PRINT_NO_CRASH); + + ib_logf(IB_LOG_LEVEL_ERROR, + "It is also possible that your operating" + "system has corrupted its own file cache."); + ib_logf(IB_LOG_LEVEL_ERROR, + "and rebooting your computer removes the error."); + ib_logf(IB_LOG_LEVEL_ERROR, + "If the corrupt page is an index page you can also try to"); + ib_logf(IB_LOG_LEVEL_ERROR, + "fix the corruption by dumping, dropping, and reimporting"); + ib_logf(IB_LOG_LEVEL_ERROR, + "the corrupt table. You can use CHECK"); + ib_logf(IB_LOG_LEVEL_ERROR, + "TABLE to scan your table for corruption."); + ib_logf(IB_LOG_LEVEL_ERROR, + "See also " + REFMAN "forcing-innodb-recovery.html" + " about forcing recovery."); + } if (srv_force_recovery < SRV_FORCE_IGNORE_CORRUPT) { /* If page space id is larger than TRX_SYS_SPACE @@ -4484,12 +4595,30 @@ corrupt: && buf_mark_space_corrupt(bpage)) { return(false); } else { - buf_page_check_corrupt(bpage); + corrupted = buf_page_check_corrupt(bpage); - ib_logf(IB_LOG_LEVEL_ERROR, - "Ending processing because of a corrupt database page."); + if (corrupted) { + ib_logf(IB_LOG_LEVEL_ERROR, + "Ending processing because of a corrupt database page."); - ut_error; + ut_error; + } + + ib_push_warning((void *)NULL, DB_ENCRYPTED_DECRYPT_FAILED, + "Table in tablespace %lu encrypted." + "However key management plugin or used key_id %lu is not found or" + " used encryption algorithm or method does not match." + " Can't continue opening the table.", + bpage->key_version); + + if (bpage->space > TRX_SYS_SPACE) { + if (corrupted) { + buf_mark_space_corrupt(bpage); + } + } else { + ut_error; + } + return(false); } } } @@ -4630,11 +4759,13 @@ buf_all_freed_instance( const buf_block_t* block = buf_chunk_not_freed(chunk); if (UNIV_LIKELY_NULL(block)) { - fprintf(stderr, - "Page %lu %lu still fixed or dirty\n", - (ulong) block->page.space, - (ulong) block->page.offset); - ut_error; + if (block->page.key_version == 0) { + fprintf(stderr, + "Page %lu %lu still fixed or dirty\n", + (ulong) block->page.space, + (ulong) block->page.offset); + ut_error; + } } } @@ -5950,6 +6081,11 @@ buf_page_decrypt_after_read( bool page_compressed_encrypted = fil_page_is_compressed_encrypted(dst_frame); buf_pool_t* buf_pool = buf_pool_from_bpage(bpage); + /* If page is encrypted read post-encryption checksum */ + if (!page_compressed_encrypted && key_version != 0) { + bpage->stored_checksum = mach_read_from_4(dst_frame + + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION + 4); + } + ut_ad(bpage->key_version == 0); if (bpage->offset == 0) { @@ -5994,6 +6130,13 @@ buf_page_decrypt_after_read( #ifdef UNIV_DEBUG fil_page_type_validate(dst_frame); #endif + + /* Calculate checksum before decrypt, this will be + used later to find out if incorrect key was used. */ + if (!page_compressed_encrypted) { + bpage->calculated_checksum = fil_crypt_calculate_checksum(zip_size, dst_frame); + } + /* decrypt using crypt_buf to dst_frame */ fil_space_decrypt(bpage->space, slot->crypt_buf, diff --git a/storage/innobase/buf/buf0flu.cc b/storage/innobase/buf/buf0flu.cc index d893d424b02..780230b38ba 100644 --- a/storage/innobase/buf/buf0flu.cc +++ b/storage/innobase/buf/buf0flu.cc @@ -1,8 +1,8 @@ /***************************************************************************** -Copyright (c) 1995, 2013, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2013, 2014, SkySQL Ab. All Rights Reserved. -Copyright (c) 2013, 2014, Fusion-io. All Rights Reserved. +Copyright (c) 1995, 2015, Oracle and/or its affiliates +Copyright (c) 2013, 2015, MariaDB Corporation +Copyright (c) 2013, 2014, Fusion-io 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 @@ -889,11 +889,11 @@ buf_flush_write_block_low( break; case BUF_BLOCK_ZIP_DIRTY: frame = bpage->zip.data; + mach_write_to_8(frame + FIL_PAGE_LSN, + bpage->newest_modification); ut_a(page_zip_verify_checksum(frame, zip_size)); - mach_write_to_8(frame + FIL_PAGE_LSN, - bpage->newest_modification); memset(frame + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION, 0, 8); break; case BUF_BLOCK_FILE_PAGE: diff --git a/storage/innobase/buf/buf0rea.cc b/storage/innobase/buf/buf0rea.cc index b4b474c547f..285fc465160 100644 --- a/storage/innobase/buf/buf0rea.cc +++ b/storage/innobase/buf/buf0rea.cc @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2013, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2015. 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 @@ -105,21 +106,22 @@ ulint buf_read_page_low( /*==============*/ dberr_t* err, /*!< out: DB_SUCCESS or DB_TABLESPACE_DELETED if we are - trying to read from a non-existent tablespace, or a - tablespace which is just now being dropped */ - bool sync, /*!< in: true if synchronous aio is desired */ - ulint mode, /*!< in: BUF_READ_IBUF_PAGES_ONLY, ..., - ORed to OS_AIO_SIMULATED_WAKE_LATER (see below - at read-ahead functions) */ - ulint space, /*!< in: space id */ - ulint zip_size,/*!< in: compressed page size, or 0 */ - ibool unzip, /*!< in: TRUE=request uncompressed page */ - ib_int64_t tablespace_version, /*!< in: if the space memory object has - this timestamp different from what we are giving here, - treat the tablespace as dropped; this is a timestamp we - use to stop dangling page reads from a tablespace - which we have DISCARDed + IMPORTed back */ - ulint offset) /*!< in: page number */ + trying to read from a non-existent tablespace, or a + tablespace which is just now being dropped */ + bool sync, /*!< in: true if synchronous aio is desired */ + ulint mode, /*!< in: BUF_READ_IBUF_PAGES_ONLY, ..., + ORed to OS_AIO_SIMULATED_WAKE_LATER (see below + at read-ahead functions) */ + ulint space, /*!< in: space id */ + ulint zip_size,/*!< in: compressed page size, or 0 */ + ibool unzip, /*!< in: TRUE=request uncompressed page */ + ib_int64_t tablespace_version, /*!< in: if the space memory object has + this timestamp different from what we are giving here, + treat the tablespace as dropped; this is a timestamp we + use to stop dangling page reads from a tablespace + which we have DISCARDed + IMPORTed back */ + ulint offset, /*!< in: page number */ + buf_page_t** rbpage) /*!< out: page */ { buf_page_t* bpage; ulint wake_later; @@ -214,10 +216,17 @@ buf_read_page_low( /* The i/o is already completed when we arrive from fil_read */ if (!buf_page_io_complete(bpage)) { + if (rbpage) { + *rbpage = bpage; + } return(0); } } + if (rbpage) { + *rbpage = bpage; + } + return(1); } @@ -348,7 +357,7 @@ read_ahead: &err, false, ibuf_mode | OS_AIO_SIMULATED_WAKE_LATER, space, zip_size, FALSE, - tablespace_version, i); + tablespace_version, i, NULL); if (err == DB_TABLESPACE_DELETED) { ut_print_timestamp(stderr); fprintf(stderr, @@ -398,7 +407,8 @@ buf_read_page( /*==========*/ ulint space, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size in bytes, or 0 */ - ulint offset) /*!< in: page number */ + ulint offset, /*!< in: page number */ + buf_page_t** bpage) /*!< out: page */ { ib_int64_t tablespace_version; ulint count; @@ -411,7 +421,7 @@ buf_read_page( count = buf_read_page_low(&err, true, BUF_READ_ANY_PAGE, space, zip_size, FALSE, - tablespace_version, offset); + tablespace_version, offset, bpage); srv_stats.buf_pool_reads.add(count); if (err == DB_TABLESPACE_DELETED) { ut_print_timestamp(stderr); @@ -459,7 +469,7 @@ buf_read_page_async( | OS_AIO_SIMULATED_WAKE_LATER | BUF_READ_IGNORE_NONEXISTENT_PAGES, space, zip_size, FALSE, - tablespace_version, offset); + tablespace_version, offset, NULL); srv_stats.buf_pool_reads.add(count); /* We do not increment number of I/O operations used for LRU policy @@ -718,7 +728,7 @@ buf_read_ahead_linear( count += buf_read_page_low( &err, false, ibuf_mode, - space, zip_size, FALSE, tablespace_version, i); + space, zip_size, FALSE, tablespace_version, i, NULL); if (err == DB_TABLESPACE_DELETED) { ut_print_timestamp(stderr); fprintf(stderr, @@ -808,7 +818,7 @@ buf_read_ibuf_merge_pages( buf_read_page_low(&err, sync && (i + 1 == n_stored), BUF_READ_ANY_PAGE, space_ids[i], zip_size, TRUE, space_versions[i], - page_nos[i]); + page_nos[i], NULL); if (UNIV_UNLIKELY(err == DB_TABLESPACE_DELETED)) { tablespace_deleted: @@ -903,12 +913,12 @@ buf_read_recv_pages( if ((i + 1 == n_stored) && sync) { buf_read_page_low(&err, true, BUF_READ_ANY_PAGE, space, zip_size, TRUE, tablespace_version, - page_nos[i]); + page_nos[i], NULL); } else { buf_read_page_low(&err, false, BUF_READ_ANY_PAGE | OS_AIO_SIMULATED_WAKE_LATER, space, zip_size, TRUE, - tablespace_version, page_nos[i]); + tablespace_version, page_nos[i], NULL); } } diff --git a/storage/innobase/dict/dict0crea.cc b/storage/innobase/dict/dict0crea.cc index 36a30cb75b7..6ba9be07348 100644 --- a/storage/innobase/dict/dict0crea.cc +++ b/storage/innobase/dict/dict0crea.cc @@ -1566,12 +1566,111 @@ dict_create_add_foreign_field_to_dictionary( } /********************************************************************//** +Construct foreign key constraint defintion from data dictionary information. +*/ +UNIV_INTERN +char* +dict_foreign_def_get( +/*=================*/ + dict_foreign_t* foreign,/*!< in: foreign */ + trx_t* trx) /*!< in: trx */ +{ + char* fk_def = (char *)mem_heap_alloc(foreign->heap, 4*1024); + const char* tbname; + char tablebuf[MAX_TABLE_NAME_LEN + 1] = ""; + int i; + char* bufend; + + tbname = dict_remove_db_name(foreign->id); + bufend = innobase_convert_name(tablebuf, MAX_TABLE_NAME_LEN, + tbname, strlen(tbname), trx->mysql_thd, FALSE); + tablebuf[bufend - tablebuf] = '\0'; + + sprintf(fk_def, + (char *)"CONSTRAINT %s FOREIGN KEY (", (char *)tablebuf); + + for(i = 0; i < foreign->n_fields; i++) { + char buf[MAX_TABLE_NAME_LEN + 1] = ""; + innobase_convert_name(buf, MAX_TABLE_NAME_LEN, + foreign->foreign_col_names[i], + strlen(foreign->foreign_col_names[i]), + trx->mysql_thd, FALSE); + strcat(fk_def, buf); + if (i < foreign->n_fields-1) { + strcat(fk_def, (char *)","); + } + } + + strcat(fk_def,(char *)") REFERENCES "); + + bufend = innobase_convert_name(tablebuf, MAX_TABLE_NAME_LEN, + foreign->referenced_table_name, + strlen(foreign->referenced_table_name), + trx->mysql_thd, TRUE); + tablebuf[bufend - tablebuf] = '\0'; + + strcat(fk_def, tablebuf); + strcat(fk_def, " ("); + + for(i = 0; i < foreign->n_fields; i++) { + char buf[MAX_TABLE_NAME_LEN + 1] = ""; + bufend = innobase_convert_name(buf, MAX_TABLE_NAME_LEN, + foreign->referenced_col_names[i], + strlen(foreign->referenced_col_names[i]), + trx->mysql_thd, FALSE); + buf[bufend - buf] = '\0'; + strcat(fk_def, buf); + if (i < foreign->n_fields-1) { + strcat(fk_def, (char *)","); + } + } + strcat(fk_def, (char *)")"); + + return fk_def; +} + +/********************************************************************//** +Convert foreign key column names from data dictionary to SQL-layer. +*/ +static +void +dict_foreign_def_get_fields( +/*========================*/ + dict_foreign_t* foreign,/*!< in: foreign */ + trx_t* trx, /*!< in: trx */ + char** field, /*!< out: foreign column */ + char** field2, /*!< out: referenced column */ + int col_no) /*!< in: column number */ +{ + char* bufend; + char* fieldbuf = (char *)mem_heap_alloc(foreign->heap, MAX_TABLE_NAME_LEN+1); + char* fieldbuf2 = (char *)mem_heap_alloc(foreign->heap, MAX_TABLE_NAME_LEN+1); + + bufend = innobase_convert_name(fieldbuf, MAX_TABLE_NAME_LEN, + foreign->foreign_col_names[col_no], + strlen(foreign->foreign_col_names[col_no]), + trx->mysql_thd, FALSE); + + fieldbuf[bufend - fieldbuf] = '\0'; + + bufend = innobase_convert_name(fieldbuf2, MAX_TABLE_NAME_LEN, + foreign->referenced_col_names[col_no], + strlen(foreign->referenced_col_names[col_no]), + trx->mysql_thd, FALSE); + + fieldbuf2[bufend - fieldbuf2] = '\0'; + *field = fieldbuf; + *field2 = fieldbuf2; +} + +/********************************************************************//** Add a foreign key definition to the data dictionary tables. @return error code or DB_SUCCESS */ UNIV_INTERN dberr_t dict_create_add_foreign_to_dictionary( /*==================================*/ + dict_table_t* table, /*!< in: table */ const char* name, /*!< in: table name */ const dict_foreign_t* foreign,/*!< in: foreign key */ trx_t* trx) /*!< in/out: dictionary transaction */ @@ -1599,6 +1698,29 @@ dict_create_add_foreign_to_dictionary( if (error != DB_SUCCESS) { + if (error == DB_DUPLICATE_KEY) { + char buf[MAX_TABLE_NAME_LEN + 1] = ""; + char tablename[MAX_TABLE_NAME_LEN + 1] = ""; + char* fk_def; + + innobase_convert_name(tablename, MAX_TABLE_NAME_LEN, + table->name, strlen(table->name), + trx->mysql_thd, TRUE); + + innobase_convert_name(buf, MAX_TABLE_NAME_LEN, + foreign->id, strlen(foreign->id), trx->mysql_thd, FALSE); + + fk_def = dict_foreign_def_get((dict_foreign_t*)foreign, trx); + + ib_push_warning(trx, error, + "Create or Alter table %s with foreign key constraint" + " failed. Foreign key constraint %s" + " already exists on data dictionary." + " Foreign key constraint names need to be unique in database." + " Error in foreign key definition: %s.", + tablename, buf, fk_def); + } + return(error); } @@ -1607,6 +1729,26 @@ dict_create_add_foreign_to_dictionary( i, name, foreign, trx); if (error != DB_SUCCESS) { + char buf[MAX_TABLE_NAME_LEN + 1] = ""; + char tablename[MAX_TABLE_NAME_LEN + 1] = ""; + char* field=NULL; + char* field2=NULL; + char* fk_def; + + innobase_convert_name(tablename, MAX_TABLE_NAME_LEN, + table->name, strlen(table->name), + trx->mysql_thd, TRUE); + innobase_convert_name(buf, MAX_TABLE_NAME_LEN, + foreign->id, strlen(foreign->id), trx->mysql_thd, FALSE); + fk_def = dict_foreign_def_get((dict_foreign_t*)foreign, trx); + dict_foreign_def_get_fields((dict_foreign_t*)foreign, trx, &field, &field2, i); + + ib_push_warning(trx, error, + "Create or Alter table %s with foreign key constraint" + " failed. Error adding foreign key constraint name %s" + " fields %s or %s to the dictionary." + " Error in foreign key definition: %s.", + tablename, buf, i+1, fk_def); return(error); } @@ -1653,7 +1795,7 @@ dict_create_add_foreigns_to_dictionary( foreign = *it; ut_ad(foreign->id != NULL); - error = dict_create_add_foreign_to_dictionary(table->name, + error = dict_create_add_foreign_to_dictionary((dict_table_t*)table, table->name, foreign, trx); if (error != DB_SUCCESS) { diff --git a/storage/innobase/dict/dict0dict.cc b/storage/innobase/dict/dict0dict.cc index 48053a954cf..dba03785f09 100644 --- a/storage/innobase/dict/dict0dict.cc +++ b/storage/innobase/dict/dict0dict.cc @@ -3276,6 +3276,11 @@ dict_index_build_internal_fts( } /*====================== FOREIGN KEY PROCESSING ========================*/ +#define DB_FOREIGN_KEY_IS_PREFIX_INDEX 200 +#define DB_FOREIGN_KEY_COL_NOT_NULL 201 +#define DB_FOREIGN_KEY_COLS_NOT_EQUAL 202 +#define DB_FOREIGN_KEY_INDEX_NOT_FOUND 203 + /*********************************************************************//** Checks if a table is referenced by foreign keys. @return TRUE if table is referenced by a foreign key */ @@ -3288,71 +3293,6 @@ dict_table_is_referenced_by_foreign_key( return(!table->referenced_set.empty()); } -/*********************************************************************//** -Check if the index is referenced by a foreign key, if TRUE return foreign -else return NULL -@return pointer to foreign key struct if index is defined for foreign -key, otherwise NULL */ -UNIV_INTERN -dict_foreign_t* -dict_table_get_referenced_constraint( -/*=================================*/ - dict_table_t* table, /*!< in: InnoDB table */ - dict_index_t* index) /*!< in: InnoDB index */ -{ - dict_foreign_t* foreign; - - ut_ad(index != NULL); - ut_ad(table != NULL); - - for (dict_foreign_set::iterator it = table->referenced_set.begin(); - it != table->referenced_set.end(); - ++it) { - - foreign = *it; - - if (foreign->referenced_index == index) { - - return(foreign); - } - } - - return(NULL); -} - -/*********************************************************************//** -Checks if a index is defined for a foreign key constraint. Index is a part -of a foreign key constraint if the index is referenced by foreign key -or index is a foreign key index. -@return pointer to foreign key struct if index is defined for foreign -key, otherwise NULL */ -UNIV_INTERN -dict_foreign_t* -dict_table_get_foreign_constraint( -/*==============================*/ - dict_table_t* table, /*!< in: InnoDB table */ - dict_index_t* index) /*!< in: InnoDB index */ -{ - dict_foreign_t* foreign; - - ut_ad(index != NULL); - ut_ad(table != NULL); - - for (dict_foreign_set::iterator it = table->foreign_set.begin(); - it != table->foreign_set.end(); - ++it) { - - foreign = *it; - - if (foreign->foreign_index == index) { - - return(foreign); - } - } - - return(NULL); -} - /**********************************************************************//** Removes a foreign constraint struct from the dictionary cache. */ UNIV_INTERN @@ -3430,15 +3370,26 @@ dict_foreign_find_index( /*!< in: whether to check charsets. only has an effect if types_idx != NULL */ - ulint check_null) + ulint check_null, /*!< in: nonzero if none of the columns must be declared NOT NULL */ + ulint* error, /*!< out: error code */ + ulint* err_col_no, + /*!< out: column number where + error happened */ + dict_index_t** err_index) + /*!< out: index where error + happened */ { dict_index_t* index; ut_ad(mutex_own(&dict_sys->mutex)); + if (error) { + *error = DB_FOREIGN_KEY_INDEX_NOT_FOUND; + } + index = dict_table_get_first_index(table); while (index != NULL) { @@ -3448,7 +3399,12 @@ dict_foreign_find_index( && dict_foreign_qualify_index( table, col_names, columns, n_cols, index, types_idx, - check_charsets, check_null)) { + check_charsets, check_null, + error, err_col_no,err_index)) { + if (error) { + *error = DB_SUCCESS; + } + return(index); } @@ -3477,7 +3433,7 @@ wsrep_dict_foreign_find_index( { return dict_foreign_find_index( table, col_names, columns, n_cols, types_idx, check_charsets, - check_null); + check_null, NULL, NULL, NULL); } #endif /* WITH_WSREP */ /**********************************************************************//** @@ -3575,11 +3531,15 @@ dict_foreign_add_to_cache( } if (ref_table && !for_in_cache->referenced_table) { + ulint index_error; + ulint err_col; + dict_index_t *err_index=NULL; + index = dict_foreign_find_index( ref_table, NULL, for_in_cache->referenced_col_names, for_in_cache->n_fields, for_in_cache->foreign_index, - check_charsets, false); + check_charsets, false, &index_error, &err_col, &err_index); if (index == NULL && !(ignore_err & DICT_ERR_IGNORE_FK_NOKEY)) { @@ -3611,6 +3571,10 @@ dict_foreign_add_to_cache( } if (for_table && !for_in_cache->foreign_table) { + ulint index_error; + ulint err_col; + dict_index_t *err_index=NULL; + index = dict_foreign_find_index( for_table, col_names, for_in_cache->foreign_col_names, @@ -3618,7 +3582,8 @@ dict_foreign_add_to_cache( for_in_cache->referenced_index, check_charsets, for_in_cache->type & (DICT_FOREIGN_ON_DELETE_SET_NULL - | DICT_FOREIGN_ON_UPDATE_SET_NULL)); + | DICT_FOREIGN_ON_UPDATE_SET_NULL), + &index_error, &err_col, &err_index); if (index == NULL && !(ignore_err & DICT_ERR_IGNORE_FK_NOKEY)) { @@ -4278,6 +4243,8 @@ static void dict_foreign_report_syntax_err( /*===========================*/ + const char* fmt, /*!< in: syntax err msg */ + const char* oper, /*!< in: operation */ const char* name, /*!< in: table name */ const char* start_of_latest_foreign, /*!< in: start of the foreign key clause @@ -4290,12 +4257,102 @@ dict_foreign_report_syntax_err( mutex_enter(&dict_foreign_err_mutex); dict_foreign_error_report_low(ef, name); - fprintf(ef, "%s:\nSyntax error close to:\n%s\n", - start_of_latest_foreign, ptr); + fprintf(ef, fmt, oper, name, start_of_latest_foreign, ptr); mutex_exit(&dict_foreign_err_mutex); } /*********************************************************************//** +Push warning message to SQL-layer based on foreign key constraint +index match error. */ +static +void +dict_foreign_push_index_error( +/*==========================*/ + trx_t* trx, /*!< in: trx */ + const char* operation, /*!< in: operation create or alter + */ + const char* create_name, /*!< in: table name in create or + alter table */ + const char* latest_foreign, /*!< in: start of latest foreign key + constraint name */ + const char** columns, /*!< in: foreign key columns */ + ulint index_error, /*!< in: error code */ + ulint err_col, /*!< in: column where error happened + */ + dict_index_t* err_index, /*!< in: index where error happened + */ + dict_table_t* table, /*!< in: table */ + FILE* ef) /*!< in: output stream */ +{ + switch (index_error) { + case DB_FOREIGN_KEY_INDEX_NOT_FOUND: { + fprintf(ef, + "%s table '%s' with foreign key constraint" + " failed. There is no index in the referenced" + " table where the referenced columns appear" + " as the first columns. Error close to %s.\n", + operation, create_name, latest_foreign); + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table '%s' with foreign key constraint" + " failed. There is no index in the referenced" + " table where the referenced columns appear" + " as the first columns. Error close to %s.", + operation, create_name, latest_foreign); + break; + } + case DB_FOREIGN_KEY_IS_PREFIX_INDEX: { + fprintf(ef, + "%s table '%s' with foreign key constraint" + " failed. There is only prefix index in the referenced" + " table where the referenced columns appear" + " as the first columns. Error close to %s.\n", + operation, create_name, latest_foreign); + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table '%s' with foreign key constraint" + " failed. There is only prefix index in the referenced" + " table where the referenced columns appear" + " as the first columns. Error close to %s.", + operation, create_name, latest_foreign); + break; + } + case DB_FOREIGN_KEY_COL_NOT_NULL: { + fprintf(ef, + "%s table %s with foreign key constraint" + " failed. You have defined a SET NULL condition but " + "field %s on index is defined as NOT NULL close to %s\n", + operation, create_name, columns[err_col], latest_foreign); + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. You have defined a SET NULL condition but " + "field %s on index is defined as NOT NULL close to %s", + operation, create_name, columns[err_col], latest_foreign); + break; + } + case DB_FOREIGN_KEY_COLS_NOT_EQUAL: { + dict_field_t* field; + const char* col_name; + field = dict_index_get_nth_field(err_index, err_col); + + col_name = dict_table_get_col_name( + table, dict_col_get_no(field->col)); + fprintf(ef, + "%s table %s with foreign key constraint" + " failed. Field type or character set for column %s " + "does not mach referenced column %s close to %s\n", + operation, create_name, columns[err_col], col_name, latest_foreign); + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Field type or character set for column %s " + "does not mach referenced column %s close to %s", + operation, create_name, columns[err_col], col_name, latest_foreign); + break; + } + default: + ut_error; + } +} + +/*********************************************************************//** Scans a table create SQL string and adds to the data dictionary the foreign key constraints declared in the string. This function should be called after the indexes for a table have been created. Each foreign key constraint must @@ -4323,16 +4380,21 @@ dict_create_foreign_constraints_low( DB_CANNOT_ADD_CONSTRAINT if any foreign keys are found. */ { - dict_table_t* table; - dict_table_t* referenced_table; - dict_table_t* table_to_alter; + dict_table_t* table = NULL; + dict_table_t* referenced_table = NULL; + dict_table_t* table_to_alter = NULL; + dict_table_t* table_to_create = NULL; ulint highest_id_so_far = 0; ulint number = 1; - dict_index_t* index; - dict_foreign_t* foreign; + dict_index_t* index = NULL; + dict_foreign_t* foreign = NULL; const char* ptr = sql_string; const char* start_of_latest_foreign = sql_string; + const char* start_of_latest_set = NULL; FILE* ef = dict_foreign_err_file; + ulint index_error = DB_SUCCESS; + dict_index_t* err_index = NULL; + ulint err_col; const char* constraint_name; ibool success; dberr_t error; @@ -4345,37 +4407,80 @@ dict_create_foreign_constraints_low( ulint n_on_updates; const dict_col_t*columns[500]; const char* column_names[500]; + const char* ref_column_names[500]; const char* referenced_table_name; dict_foreign_set local_fk_set; dict_foreign_set_free local_fk_set_free(local_fk_set); + const char* create_table_name; + const char* orig; + char create_name[MAX_TABLE_NAME_LEN + 1]; + char operation[8]; ut_ad(!srv_read_only_mode); ut_ad(mutex_own(&(dict_sys->mutex))); table = dict_table_get_low(name); + /* First check if we are actually doing an ALTER TABLE, and in that + case look for the table being altered */ + orig = ptr; + ptr = dict_accept(cs, ptr, "ALTER", &success); + + strcpy((char *)operation, success ? "Alter " : "Create "); + + if (!success) { + orig = ptr; + ptr = dict_scan_to(ptr, "CREATE"); + ptr = dict_scan_to(ptr, "TABLE"); + ptr = dict_accept(cs, ptr, "TABLE", &success); + + if (success) { + ptr = dict_scan_table_name(cs, ptr, &table_to_create, name, + &success, heap, &create_table_name); + } + + if (success) { + char *bufend; + bufend = innobase_convert_name((char *)create_name, MAX_TABLE_NAME_LEN, + create_table_name, strlen(create_table_name), + trx->mysql_thd, TRUE); + create_name[bufend-create_name]='\0'; + ptr = orig; + } else { + char *bufend; + ptr = orig; + bufend = innobase_convert_name((char *)create_name, MAX_TABLE_NAME_LEN, + name, strlen(name), trx->mysql_thd, TRUE); + create_name[bufend-create_name]='\0'; + } + + goto loop; + } if (table == NULL) { mutex_enter(&dict_foreign_err_mutex); - dict_foreign_error_report_low(ef, name); - fprintf(ef, - "Cannot find the table in the internal" - " data dictionary of InnoDB.\n" - "Create table statement:\n%s\n", sql_string); + dict_foreign_error_report_low(ef, create_name); + dict_foreign_error_report_low(ef, create_name); + fprintf(ef, "%s table %s with foreign key constraint" + " failed. Table %s not found from data dictionary." + " Error close to %s.\n", + operation, create_name, create_name, start_of_latest_foreign); mutex_exit(&dict_foreign_err_mutex); + ib_push_warning(trx, DB_ERROR, + "%s table %s with foreign key constraint" + " failed. Table %s not found from data dictionary." + " Error close to %s.", + operation, create_name, create_name, start_of_latest_foreign); return(DB_ERROR); } - /* First check if we are actually doing an ALTER TABLE, and in that - case look for the table being altered */ - - ptr = dict_accept(cs, ptr, "ALTER", &success); - + /* If not alter table jump to loop */ if (!success) { goto loop; } + orig = ptr; ptr = dict_accept(cs, ptr, "TABLE", &success); if (!success) { @@ -4385,13 +4490,40 @@ dict_create_foreign_constraints_low( /* We are doing an ALTER TABLE: scan the table name we are altering */ + orig = ptr; ptr = dict_scan_table_name(cs, ptr, &table_to_alter, name, &success, heap, &referenced_table_name); + + if (table_to_alter) { + char *bufend; + bufend = innobase_convert_name((char *)create_name, MAX_TABLE_NAME_LEN, + table_to_alter->name, strlen(table_to_alter->name), + trx->mysql_thd, TRUE); + create_name[bufend-create_name]='\0'; + } else { + char *bufend; + bufend = innobase_convert_name((char *)create_name, MAX_TABLE_NAME_LEN, + referenced_table_name, strlen(referenced_table_name), + trx->mysql_thd, TRUE); + create_name[bufend-create_name]='\0'; + + } + if (!success) { - fprintf(stderr, - "InnoDB: Error: could not find" - " the table being ALTERED in:\n%s\n", - sql_string); + mutex_enter(&dict_foreign_err_mutex); + dict_foreign_error_report_low(ef, create_name); + fprintf(ef, + "%s table %s with foreign key constraint" + " failed. Table %s not found from data dictionary." + " Error close to %s.\n", + operation, create_name, create_name, orig); + mutex_exit(&dict_foreign_err_mutex); + + ib_push_warning(trx, DB_ERROR, + "%s table %s with foreign key constraint" + " failed. Table %s not found from data dictionary." + " Error close to %s.", + operation, create_name, create_name, orig); return(DB_ERROR); } @@ -4428,6 +4560,7 @@ loop: of the constraint to system tables. */ ptr = ptr1; + orig = ptr; ptr = dict_accept(cs, ptr, "CONSTRAINT", &success); ut_a(success); @@ -4458,6 +4591,19 @@ loop: if so, immediately reject the command if the table is a temporary one. For now, this kludge will work. */ if (reject_fks && !local_fk_set.empty()) { + mutex_enter(&dict_foreign_err_mutex); + dict_foreign_error_report_low(ef, create_name); + fprintf(ef, "%s table %s with foreign key constraint" + " failed. Temporary tables can't have foreign key constraints." + " Error close to %s.\n", + operation, create_name, start_of_latest_foreign); + mutex_exit(&dict_foreign_err_mutex); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Temporary tables can't have foreign key constraints." + " Error close to %s.", + operation, create_name, start_of_latest_foreign); return(DB_CANNOT_ADD_CONSTRAINT); } @@ -4483,6 +4629,7 @@ loop: start_of_latest_foreign = ptr; + orig = ptr; ptr = dict_accept(cs, ptr, "FOREIGN", &success); if (!success) { @@ -4493,6 +4640,7 @@ loop: goto loop; } + orig = ptr; ptr = dict_accept(cs, ptr, "KEY", &success); if (!success) { @@ -4518,6 +4666,7 @@ loop: } } + orig = ptr; ptr = dict_accept(cs, ptr, "(", &success); if (!success) { @@ -4527,8 +4676,17 @@ loop: ptr = dict_skip_word(cs, ptr, &success); if (!success) { dict_foreign_report_syntax_err( - name, start_of_latest_foreign, ptr); - return(DB_CANNOT_ADD_CONSTRAINT); + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.\n", + operation, create_name, start_of_latest_foreign, orig); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.", + operation, create_name, start_of_latest_foreign, orig); + return(DB_CANNOT_ADD_CONSTRAINT); } } else { @@ -4555,15 +4713,26 @@ loop: /* Scan the columns in the first list */ col_loop1: ut_a(i < (sizeof column_names) / sizeof *column_names); + orig = ptr; ptr = dict_scan_col(cs, ptr, &success, table, columns + i, heap, column_names + i); if (!success) { mutex_enter(&dict_foreign_err_mutex); - dict_foreign_error_report_low(ef, name); - fprintf(ef, "%s:\nCannot resolve column name close to:\n%s\n", - start_of_latest_foreign, ptr); + dict_foreign_error_report_low(ef, create_name); + fprintf(ef, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.\n", + operation, create_name, start_of_latest_foreign, orig); + mutex_exit(&dict_foreign_err_mutex); + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.", + operation, create_name, start_of_latest_foreign, orig); + return(DB_CANNOT_ADD_CONSTRAINT); } @@ -4575,11 +4744,22 @@ col_loop1: goto col_loop1; } + orig = ptr; ptr = dict_accept(cs, ptr, ")", &success); if (!success) { dict_foreign_report_syntax_err( - name, start_of_latest_foreign, ptr); + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.\n", + operation, create_name, start_of_latest_foreign, orig); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.", + operation, create_name, start_of_latest_foreign, orig); + return(DB_CANNOT_ADD_CONSTRAINT); } @@ -4590,27 +4770,41 @@ col_loop1: set to 0 */ index = dict_foreign_find_index( - table, NULL, column_names, i, NULL, TRUE, FALSE); + table, NULL, column_names, i, + NULL, TRUE, FALSE, &index_error, &err_col, &err_index); if (!index) { mutex_enter(&dict_foreign_err_mutex); - dict_foreign_error_report_low(ef, name); + dict_foreign_error_report_low(ef, create_name); fputs("There is no index in table ", ef); - ut_print_name(ef, NULL, TRUE, name); + ut_print_name(ef, NULL, TRUE, create_name); fprintf(ef, " where the columns appear\n" "as the first columns. Constraint:\n%s\n" "See " REFMAN "innodb-foreign-key-constraints.html\n" "for correct foreign key definition.\n", start_of_latest_foreign); - mutex_exit(&dict_foreign_err_mutex); + dict_foreign_push_index_error(trx, operation, create_name, start_of_latest_foreign, + column_names, index_error, err_col, err_index, table, ef); - return(DB_CHILD_NO_INDEX); + mutex_exit(&dict_foreign_err_mutex); + return(DB_CANNOT_ADD_CONSTRAINT); } + + orig = ptr; ptr = dict_accept(cs, ptr, "REFERENCES", &success); if (!success || !my_isspace(cs, *ptr)) { dict_foreign_report_syntax_err( - name, start_of_latest_foreign, ptr); + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.\n", + operation, create_name, start_of_latest_foreign, orig); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.", + operation, create_name, start_of_latest_foreign, orig); return(DB_CANNOT_ADD_CONSTRAINT); } @@ -4679,21 +4873,46 @@ col_loop1: checking of foreign key constraints! */ if (!success || (!referenced_table && trx->check_foreigns)) { + char buf[MAX_TABLE_NAME_LEN + 1] = ""; + char* bufend; + + bufend = innobase_convert_name(buf, MAX_TABLE_NAME_LEN, + referenced_table_name, strlen(referenced_table_name), + trx->mysql_thd, TRUE); + buf[bufend - buf] = '\0'; + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint failed. Referenced table %s not found in the data dictionary " + "close to %s.", + operation, create_name, buf, start_of_latest_foreign); mutex_enter(&dict_foreign_err_mutex); - dict_foreign_error_report_low(ef, name); - fprintf(ef, "%s:\nCannot resolve table name close to:\n" - "%s\n", - start_of_latest_foreign, ptr); + dict_foreign_error_report_low(ef, create_name); + fprintf(ef, + "%s table %s with foreign key constraint failed. Referenced table %s not found in the data dictionary " + "close to %s.\n", + operation, create_name, buf, start_of_latest_foreign); + mutex_exit(&dict_foreign_err_mutex); return(DB_CANNOT_ADD_CONSTRAINT); } + orig = ptr; ptr = dict_accept(cs, ptr, "(", &success); if (!success) { - dict_foreign_report_syntax_err(name, start_of_latest_foreign, - ptr); + dict_foreign_report_syntax_err( + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.\n", + operation, create_name, start_of_latest_foreign, orig); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.", + operation, create_name, start_of_latest_foreign, orig); + return(DB_CANNOT_ADD_CONSTRAINT); } @@ -4701,34 +4920,54 @@ col_loop1: i = 0; col_loop2: + orig = ptr; ptr = dict_scan_col(cs, ptr, &success, referenced_table, columns + i, - heap, column_names + i); + heap, ref_column_names + i); i++; if (!success) { mutex_enter(&dict_foreign_err_mutex); - dict_foreign_error_report_low(ef, name); - fprintf(ef, "%s:\nCannot resolve column name close to:\n" - "%s\n", - start_of_latest_foreign, ptr); + dict_foreign_error_report_low(ef, create_name); + fprintf(ef, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.\n", + operation, create_name, start_of_latest_foreign, orig); mutex_exit(&dict_foreign_err_mutex); + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.", + operation, create_name, start_of_latest_foreign, orig); return(DB_CANNOT_ADD_CONSTRAINT); } + orig = ptr; ptr = dict_accept(cs, ptr, ",", &success); if (success) { goto col_loop2; } + orig = ptr; ptr = dict_accept(cs, ptr, ")", &success); if (!success || foreign->n_fields != i) { - dict_foreign_report_syntax_err(name, start_of_latest_foreign, - ptr); + dict_foreign_report_syntax_err( + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s. Too few referenced columns.\n", + operation, create_name, start_of_latest_foreign, orig); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s. Too few referenced columns, you have %d when you should have %d.", + operation, create_name, start_of_latest_foreign, orig, i, foreign->n_fields); + return(DB_CANNOT_ADD_CONSTRAINT); } @@ -4738,6 +4977,7 @@ col_loop2: scan_on_conditions: /* Loop here as long as we can find ON ... conditions */ + start_of_latest_set = ptr; ptr = dict_accept(cs, ptr, "ON", &success); if (!success) { @@ -4745,15 +4985,27 @@ scan_on_conditions: goto try_find_index; } + orig = ptr; ptr = dict_accept(cs, ptr, "DELETE", &success); if (!success) { + orig = ptr; ptr = dict_accept(cs, ptr, "UPDATE", &success); if (!success) { dict_foreign_report_syntax_err( - name, start_of_latest_foreign, ptr); + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.\n", + operation, create_name, start_of_latest_foreign, start_of_latest_set); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.", + operation, create_name, start_of_latest_foreign, start_of_latest_set); + return(DB_CANNOT_ADD_CONSTRAINT); } @@ -4764,12 +5016,14 @@ scan_on_conditions: n_on_deletes++; } + orig = ptr; ptr = dict_accept(cs, ptr, "RESTRICT", &success); if (success) { goto scan_on_conditions; } + orig = ptr; ptr = dict_accept(cs, ptr, "CASCADE", &success); if (success) { @@ -4782,14 +5036,25 @@ scan_on_conditions: goto scan_on_conditions; } + orig = ptr; ptr = dict_accept(cs, ptr, "NO", &success); if (success) { + orig = ptr; ptr = dict_accept(cs, ptr, "ACTION", &success); if (!success) { dict_foreign_report_syntax_err( - name, start_of_latest_foreign, ptr); + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.\n", + operation, create_name, start_of_latest_foreign, start_of_latest_set); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.", + operation, create_name, start_of_latest_foreign, start_of_latest_set); return(DB_CANNOT_ADD_CONSTRAINT); } @@ -4803,38 +5068,69 @@ scan_on_conditions: goto scan_on_conditions; } + orig = ptr; ptr = dict_accept(cs, ptr, "SET", &success); if (!success) { - dict_foreign_report_syntax_err(name, start_of_latest_foreign, - ptr); + dict_foreign_report_syntax_err( + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.\n", + operation, create_name, start_of_latest_foreign, start_of_latest_set); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.", + operation, create_name, start_of_latest_foreign, start_of_latest_set); return(DB_CANNOT_ADD_CONSTRAINT); } + orig = ptr; ptr = dict_accept(cs, ptr, "NULL", &success); if (!success) { - dict_foreign_report_syntax_err(name, start_of_latest_foreign, - ptr); + dict_foreign_report_syntax_err( + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.\n", + operation, create_name, start_of_latest_foreign, start_of_latest_set); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.", + operation, create_name, start_of_latest_foreign, start_of_latest_set); + return(DB_CANNOT_ADD_CONSTRAINT); } for (j = 0; j < foreign->n_fields; j++) { if ((dict_index_get_nth_col(foreign->foreign_index, j)->prtype) & DATA_NOT_NULL) { + const dict_col_t* col + = dict_index_get_nth_col(foreign->foreign_index, j); + const char* col_name = dict_table_get_col_name(foreign->foreign_index->table, + dict_col_get_no(col)); /* It is not sensible to define SET NULL if the column is not allowed to be NULL! */ mutex_enter(&dict_foreign_err_mutex); - dict_foreign_error_report_low(ef, name); - fprintf(ef, "%s:\n" - "You have defined a SET NULL condition" - " though some of the\n" - "columns are defined as NOT NULL.\n", - start_of_latest_foreign); + dict_foreign_error_report_low(ef, create_name); + fprintf(ef, + "%s table %s with foreign key constraint" + " failed. You have defined a SET NULL condition but column %s is defined as NOT NULL" + " in %s close to %s.\n", + operation, create_name, col_name, start_of_latest_foreign, start_of_latest_set); mutex_exit(&dict_foreign_err_mutex); + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. You have defined a SET NULL condition but column %s is defined as NOT NULL" + " in %s close to %s.", + operation, create_name, col_name, start_of_latest_foreign, start_of_latest_set); + return(DB_CANNOT_ADD_CONSTRAINT); } } @@ -4852,11 +5148,20 @@ try_find_index: /* It is an error to define more than 1 action */ mutex_enter(&dict_foreign_err_mutex); - dict_foreign_error_report_low(ef, name); - fprintf(ef, "%s:\n" - "You have twice an ON DELETE clause" - " or twice an ON UPDATE clause.\n", - start_of_latest_foreign); + dict_foreign_error_report_low(ef, create_name); + fprintf(ef, + "%s table %s with foreign key constraint" + " failed. You have more than one on delete or on update clause" + " in %s close to %s.\n", + operation, create_name, start_of_latest_foreign, start_of_latest_set); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. You have more than one on delete or on update clause" + " in %s close to %s.", + operation, create_name, start_of_latest_foreign, start_of_latest_set); + + dict_foreign_free(foreign); mutex_exit(&dict_foreign_err_mutex); return(DB_CANNOT_ADD_CONSTRAINT); @@ -4868,12 +5173,12 @@ try_find_index: if (referenced_table) { index = dict_foreign_find_index(referenced_table, NULL, - column_names, i, + ref_column_names, i, foreign->foreign_index, - TRUE, FALSE); + TRUE, FALSE, &index_error, &err_col, &err_index); if (!index) { mutex_enter(&dict_foreign_err_mutex); - dict_foreign_error_report_low(ef, name); + dict_foreign_error_report_low(ef, create_name); fprintf(ef, "%s:\n" "Cannot find an index in the" " referenced table where the\n" @@ -4891,9 +5196,13 @@ try_find_index: "innodb-foreign-key-constraints.html\n" "for correct foreign key definition.\n", start_of_latest_foreign); + + dict_foreign_push_index_error(trx, operation, create_name, start_of_latest_foreign, + column_names, index_error, err_col, err_index, referenced_table, ef); + mutex_exit(&dict_foreign_err_mutex); - return(DB_PARENT_NO_INDEX); + return(DB_CANNOT_ADD_CONSTRAINT); } } else { ut_a(trx->check_foreigns == FALSE); @@ -4912,11 +5221,12 @@ try_find_index: for (i = 0; i < foreign->n_fields; i++) { foreign->referenced_col_names[i] - = mem_heap_strdup(foreign->heap, column_names[i]); + = mem_heap_strdup(foreign->heap, ref_column_names[i]); } goto loop; } + /************************************************************************** Determines whether a string starts with the specified keyword. @return TRUE if str starts with keyword */ @@ -6068,7 +6378,8 @@ dict_foreign_replace_index( foreign->foreign_table, col_names, foreign->foreign_col_names, foreign->n_fields, index, - /*check_charsets=*/TRUE, /*check_null=*/FALSE); + /*check_charsets=*/TRUE, /*check_null=*/FALSE, + NULL, NULL, NULL); if (new_index) { ut_ad(new_index->table == index->table); ut_ad(!new_index->to_be_dropped); @@ -6092,7 +6403,8 @@ dict_foreign_replace_index( foreign->referenced_table, NULL, foreign->referenced_col_names, foreign->n_fields, index, - /*check_charsets=*/TRUE, /*check_null=*/FALSE); + /*check_charsets=*/TRUE, /*check_null=*/FALSE, + NULL, NULL, NULL); /* There must exist an alternative index, since this must have been checked earlier. */ if (new_index) { @@ -6661,10 +6973,17 @@ dict_foreign_qualify_index( /*!< in: whether to check charsets. only has an effect if types_idx != NULL */ - ulint check_null) + ulint check_null, /*!< in: nonzero if none of the columns must be declared NOT NULL */ + ulint* error, /*!< out: error code */ + ulint* err_col_no, + /*!< out: column number where + error happened */ + dict_index_t** err_index) + /*!< out: index where error + happened */ { if (dict_index_get_n_fields(index) < n_cols) { return(false); @@ -6681,11 +7000,21 @@ dict_foreign_qualify_index( if (field->prefix_len != 0) { /* We do not accept column prefix indexes here */ + if (error && err_col_no && err_index) { + *error = DB_FOREIGN_KEY_IS_PREFIX_INDEX; + *err_col_no = i; + *err_index = (dict_index_t*)index; + } return(false); } if (check_null && (field->col->prtype & DATA_NOT_NULL)) { + if (error && err_col_no && err_index) { + *error = DB_FOREIGN_KEY_COL_NOT_NULL; + *err_col_no = i; + *err_index = (dict_index_t*)index; + } return(false); } @@ -6701,6 +7030,12 @@ dict_foreign_qualify_index( dict_index_get_nth_col(index, i), dict_index_get_nth_col(types_idx, i), check_charsets)) { + if (error && err_col_no && err_index) { + *error = DB_FOREIGN_KEY_COLS_NOT_EQUAL; + *err_col_no = i; + *err_index = (dict_index_t*)index; + } + return(false); } } diff --git a/storage/innobase/dict/dict0load.cc b/storage/innobase/dict/dict0load.cc index 149811dab60..d8bd0a66ade 100644 --- a/storage/innobase/dict/dict0load.cc +++ b/storage/innobase/dict/dict0load.cc @@ -1168,7 +1168,7 @@ loop: dberr_t err = fil_open_single_table_tablespace( read_page_0, srv_read_only_mode ? false : true, space_id, dict_tf_to_fsp_flags(flags), - name, filepath); + name, filepath, NULL); if (err != DB_SUCCESS) { ib_logf(IB_LOG_LEVEL_ERROR, @@ -2412,7 +2412,7 @@ err_exit: err = fil_open_single_table_tablespace( true, false, table->space, dict_tf_to_fsp_flags(table->flags), - name, filepath); + name, filepath, table); if (err != DB_SUCCESS) { /* We failed to find a sensible diff --git a/storage/innobase/dict/dict0mem.cc b/storage/innobase/dict/dict0mem.cc index 9c186304d27..1724ac024fa 100644 --- a/storage/innobase/dict/dict0mem.cc +++ b/storage/innobase/dict/dict0mem.cc @@ -417,7 +417,8 @@ dict_mem_table_col_rename_low( dict_index_t* new_index = dict_foreign_find_index( foreign->foreign_table, NULL, foreign->foreign_col_names, - foreign->n_fields, NULL, true, false); + foreign->n_fields, NULL, true, false, + NULL, NULL, NULL); /* There must be an equivalent index in this case. */ ut_ad(new_index != NULL); diff --git a/storage/innobase/fil/fil0crypt.cc b/storage/innobase/fil/fil0crypt.cc index 2633bd96360..32fe58750e1 100644 --- a/storage/innobase/fil/fil0crypt.cc +++ b/storage/innobase/fil/fil0crypt.cc @@ -650,31 +650,8 @@ fil_space_encrypt( /* handle post encryption checksum */ ib_uint32_t checksum = 0; - srv_checksum_algorithm_t algorithm = - static_cast<srv_checksum_algorithm_t>(srv_checksum_algorithm); - if (zip_size == 0) { - switch (algorithm) { - case SRV_CHECKSUM_ALGORITHM_CRC32: - case SRV_CHECKSUM_ALGORITHM_STRICT_CRC32: - checksum = buf_calc_page_crc32(dst_frame); - break; - case SRV_CHECKSUM_ALGORITHM_INNODB: - case SRV_CHECKSUM_ALGORITHM_STRICT_INNODB: - checksum = (ib_uint32_t) buf_calc_page_new_checksum( - dst_frame); - break; - case SRV_CHECKSUM_ALGORITHM_NONE: - case SRV_CHECKSUM_ALGORITHM_STRICT_NONE: - checksum = BUF_NO_CHECKSUM_MAGIC; - break; - /* no default so the compiler will emit a warning - * if new enum is added and not handled here */ - } - } else { - checksum = page_zip_calc_checksum(dst_frame, zip_size, - algorithm); - } + checksum = fil_crypt_calculate_checksum(zip_size, dst_frame); // store the post-encryption checksum after the key-version mach_write_to_4(dst_frame + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION + 4, checksum); @@ -818,6 +795,47 @@ fil_space_decrypt( return src_frame; } +/****************************************************************** +Calculate post encryption checksum +@return page checksum or BUF_NO_CHECKSUM_MAGIC +not needed. */ +UNIV_INTERN +ulint +fil_crypt_calculate_checksum( +/*=========================*/ + ulint zip_size, /*!< in: zip_size or 0 */ + byte* dst_frame) /*!< in: page where to calculate */ +{ + ib_uint32_t checksum = 0; + srv_checksum_algorithm_t algorithm = + static_cast<srv_checksum_algorithm_t>(srv_checksum_algorithm); + + if (zip_size == 0) { + switch (algorithm) { + case SRV_CHECKSUM_ALGORITHM_CRC32: + case SRV_CHECKSUM_ALGORITHM_STRICT_CRC32: + checksum = buf_calc_page_crc32(dst_frame); + break; + case SRV_CHECKSUM_ALGORITHM_INNODB: + case SRV_CHECKSUM_ALGORITHM_STRICT_INNODB: + checksum = (ib_uint32_t) buf_calc_page_new_checksum( + dst_frame); + break; + case SRV_CHECKSUM_ALGORITHM_NONE: + case SRV_CHECKSUM_ALGORITHM_STRICT_NONE: + checksum = BUF_NO_CHECKSUM_MAGIC; + break; + /* no default so the compiler will emit a warning + * if new enum is added and not handled here */ + } + } else { + checksum = page_zip_calc_checksum(dst_frame, zip_size, + algorithm); + } + + return checksum; +} + /********************************************************************* Verify checksum for a page (iff it's encrypted) NOTE: currently this function can only be run in single threaded mode diff --git a/storage/innobase/fil/fil0fil.cc b/storage/innobase/fil/fil0fil.cc index 249e20da1bc..831a3319eff 100644 --- a/storage/innobase/fil/fil0fil.cc +++ b/storage/innobase/fil/fil0fil.cc @@ -1,7 +1,7 @@ /***************************************************************************** -Copyright (c) 1995, 2014, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2013, 2015, MariaDB Corporation. All Rights Reserved. +Copyright (c) 1995, 2015, Oracle and/or its affiliates. +Copyright (c) 2013, 2015, 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 @@ -1986,6 +1986,7 @@ fil_read_first_page( const char* check_msg = NULL; fil_space_crypt_t* cdata; + buf = static_cast<byte*>(ut_malloc(2 * UNIV_PAGE_SIZE)); /* Align the memory for a possible read from a raw device */ @@ -2015,6 +2016,10 @@ fil_read_first_page( fsp_flags_get_zip_size(*flags), NULL); cdata = fil_space_read_crypt_data(space, page, offset); + if (crypt_data) { + *crypt_data = cdata; + } + /* If file space is encrypted we need to have at least some encryption service available where to get keys */ if ((cdata && cdata->encryption == FIL_SPACE_ENCRYPTION_ON) || @@ -2022,16 +2027,14 @@ fil_read_first_page( cdata && cdata->encryption == FIL_SPACE_ENCRYPTION_DEFAULT)) { if (!encryption_key_id_exists(cdata->key_id)) { - ib_logf(IB_LOG_LEVEL_FATAL, - "Tablespace id %ld encrypted but encryption service" - " not available. Can't continue opening tablespace.\n", - space); - ut_error; - } - } + ib_logf(IB_LOG_LEVEL_ERROR, + "Tablespace id %ld is encrypted but encryption service" + " or used key_id %u is not available. Can't continue opening tablespace.", + space, cdata->key_id); - if (crypt_data) { - *crypt_data = cdata; + return ("table encrypted but encryption service not available."); + + } } ut_free(buf); @@ -3621,7 +3624,8 @@ fil_open_single_table_tablespace( ulint flags, /*!< in: tablespace flags */ const char* tablename, /*!< in: table name in the databasename/tablename format */ - const char* path_in) /*!< in: tablespace filepath */ + const char* path_in, /*!< in: tablespace filepath */ + dict_table_t* table) /*!< in: table */ { dberr_t err = DB_SUCCESS; bool dict_filepath_same_as_default = false; @@ -3738,6 +3742,10 @@ fil_open_single_table_tablespace( &def.lsn, &def.lsn, &def.crypt_data); def.valid = !def.check_msg; + if (table) { + table->crypt_data = def.crypt_data; + } + /* Validate this single-table-tablespace with SYS_TABLES, but do not compare the DATA_DIR flag, in case the tablespace was relocated. */ @@ -3763,6 +3771,10 @@ fil_open_single_table_tablespace( &remote.lsn, &remote.lsn, &remote.crypt_data); remote.valid = !remote.check_msg; + if (table) { + table->crypt_data = remote.crypt_data; + } + /* Validate this single-table-tablespace with SYS_TABLES, but do not compare the DATA_DIR flag, in case the tablespace was relocated. */ @@ -3789,6 +3801,10 @@ fil_open_single_table_tablespace( &dict.lsn, &dict.lsn, &dict.crypt_data); dict.valid = !dict.check_msg; + if (table) { + table->crypt_data = dict.crypt_data; + } + /* Validate this single-table-tablespace with SYS_TABLES, but do not compare the DATA_DIR flag, in case the tablespace was relocated. */ @@ -3970,7 +3986,9 @@ cleanup_and_exit: mem_free(remote.filepath); } if (remote.crypt_data && remote.crypt_data != crypt_data) { - fil_space_destroy_crypt_data(&remote.crypt_data); + if (err == DB_SUCCESS) { + fil_space_destroy_crypt_data(&remote.crypt_data); + } } if (dict.success) { os_file_close(dict.file); @@ -3985,7 +4003,9 @@ cleanup_and_exit: os_file_close(def.file); } if (def.crypt_data && def.crypt_data != crypt_data) { - fil_space_destroy_crypt_data(&def.crypt_data); + if (err == DB_SUCCESS) { + fil_space_destroy_crypt_data(&def.crypt_data); + } } mem_free(def.filepath); @@ -5730,9 +5750,10 @@ fil_io( space = fil_space_get_by_id(space_id); - /* If we are deleting a tablespace we don't allow any read - operations on that. However, we do allow write operations. */ - if (space == 0 || (type == OS_FILE_READ && space->stop_new_ops)) { + /* If we are deleting a tablespace we don't allow async read operations + on that. However, we do allow write and sync read operations */ + if (space == 0 + || (type == OS_FILE_READ && !sync && space->stop_new_ops)) { mutex_exit(&fil_system->mutex); ib_logf(IB_LOG_LEVEL_ERROR, @@ -5871,9 +5892,9 @@ fil_io( if (!ret) { return(DB_OUT_OF_FILE_SPACE); - } else { - return(DB_SUCCESS); } + + return(DB_SUCCESS); } #ifndef UNIV_HOTBACKUP diff --git a/storage/innobase/fsp/fsp0fsp.cc b/storage/innobase/fsp/fsp0fsp.cc index 5c5e2d69514..927763cec01 100644 --- a/storage/innobase/fsp/fsp0fsp.cc +++ b/storage/innobase/fsp/fsp0fsp.cc @@ -2721,6 +2721,8 @@ fsp_reserve_free_extents( ulint reserve; ibool success; ulint n_pages_added; + size_t total_reserved = 0; + ulint rounds = 0; ut_ad(mtr); *n_reserved = n_ext; @@ -2789,6 +2791,7 @@ try_again: } success = fil_space_reserve_free_extents(space, n_free, n_ext); + *n_reserved = n_ext; if (success) { return(TRUE); @@ -2796,8 +2799,19 @@ try_again: try_to_extend: success = fsp_try_extend_data_file(&n_pages_added, space, space_header, mtr); + if (success && n_pages_added > 0) { + rounds++; + total_reserved += n_pages_added; + + if (rounds > 50) { + ib_logf(IB_LOG_LEVEL_INFO, + "Space id %lu trying to reserve %lu extents actually reserved %lu " + " reserve %lu free %lu size %lu rounds %lu total_reserved %lu", + space, n_ext, n_pages_added, reserve, n_free, size, rounds, total_reserved); + } + goto try_again; } diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 4031ccd5b8f..514f9bec647 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -108,7 +108,7 @@ this program; if not, write to the Free Software Foundation, Inc., #define thd_get_trx_isolation(X) ((enum_tx_isolation)thd_tx_isolation(X)) #ifdef MYSQL_DYNAMIC_PLUGIN -#define tc_size 400 +#define tc_size 2000 #define tdc_size 400 #endif @@ -1747,6 +1747,7 @@ convert_error_code_to_mysql( case DB_TABLESPACE_DELETED: case DB_TABLE_NOT_FOUND: + case DB_ENCRYPTED_DECRYPT_FAILED: return(HA_ERR_NO_SUCH_TABLE); case DB_TABLESPACE_NOT_FOUND: @@ -2365,6 +2366,7 @@ check_trx_exists( if (trx == NULL) { trx = innobase_trx_allocate(thd); + thd_set_ha_data(thd, innodb_hton_ptr, trx); } else if (UNIV_UNLIKELY(trx->magic_n != TRX_MAGIC_N)) { mem_analyze_corruption(trx); ut_error; @@ -5591,7 +5593,14 @@ table_opened: innobase_copy_frm_flags_from_table_share(ib_table, table->s); - dict_stats_init(ib_table); + ib_table->thd = (void*)thd; + + /* No point to init any statistics if tablespace is still encrypted. */ + if (!ib_table->is_encrypted) { + dict_stats_init(ib_table); + } else { + ib_table->stat_initialized = 1; + } MONITOR_INC(MONITOR_TABLE_OPEN); @@ -5620,6 +5629,11 @@ table_opened: file, best to play it safe. */ no_tablespace = true; + } else if (ib_table->is_encrypted) { + /* This means that tablespace was found but we could not + decrypt encrypted page. */ + no_tablespace = true; + ib_table->ibd_file_missing = true; } else { no_tablespace = false; } @@ -5628,6 +5642,33 @@ table_opened: free_share(share); my_errno = ENOENT; + /* If table has no talespace but it has crypt data, check + is tablespace made unaccessible because encryption service + or used key_id is not available. */ + if (ib_table) { + fil_space_crypt_t* crypt_data = ib_table->crypt_data; + if ((crypt_data && crypt_data->encryption == FIL_SPACE_ENCRYPTION_ON) || + (srv_encrypt_tables && + crypt_data && crypt_data->encryption == FIL_SPACE_ENCRYPTION_DEFAULT)) { + + if (!encryption_key_id_exists(crypt_data->key_id)) { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + HA_ERR_NO_SUCH_TABLE, + "Table %s is encrypted but encryption service or" + " used key_id %u is not available. " + " Can't continue reading table.", + ib_table->name, crypt_data->key_id); + } + } else if (ib_table->is_encrypted) { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + HA_ERR_NO_SUCH_TABLE, + "Table %s is encrypted but encryption service or" + " used key_id is not available. " + " Can't continue reading table.", + ib_table->name); + } + } + dict_table_close(ib_table, FALSE, FALSE); DBUG_RETURN(HA_ERR_NO_SUCH_TABLE); @@ -7755,10 +7796,11 @@ no_commit: ; } else if (src_table == prebuilt->table) { #ifdef WITH_WSREP - if (wsrep_on(user_thd) && wsrep_load_data_splitting && + if (wsrep_on(user_thd) && + wsrep_load_data_splitting && sql_command == SQLCOM_LOAD && - !thd_test_options( - user_thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) + !thd_test_options(user_thd, + OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) { switch (wsrep_run_wsrep_commit(user_thd, 1)) { @@ -7786,10 +7828,11 @@ no_commit: prebuilt->sql_stat_start = TRUE; } else { #ifdef WITH_WSREP - if (wsrep_on(user_thd) && wsrep_load_data_splitting && + if (wsrep_on(user_thd) && + wsrep_load_data_splitting && sql_command == SQLCOM_LOAD && - !thd_test_options( - user_thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) + !thd_test_options(user_thd, + OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) { switch (wsrep_run_wsrep_commit(user_thd, 1)) { @@ -8011,14 +8054,15 @@ report_error: user_thd); #ifdef WITH_WSREP - if (!error_result && wsrep_thd_exec_mode(user_thd) == LOCAL_STATE && - wsrep_on(user_thd) && !wsrep_consistency_check(user_thd) && - (sql_command != SQLCOM_LOAD || - thd_binlog_format(user_thd) == BINLOG_FORMAT_ROW)) { - - if (wsrep_append_keys(user_thd, false, record, NULL)) { - DBUG_PRINT("wsrep", ("row key failed")); - error_result = HA_ERR_INTERNAL_ERROR; + if (!error_result && + wsrep_thd_exec_mode(user_thd) == LOCAL_STATE && + wsrep_on(user_thd) && + !wsrep_consistency_check(user_thd)) + { + if (wsrep_append_keys(user_thd, false, record, NULL)) + { + DBUG_PRINT("wsrep", ("row key failed")); + error_result = HA_ERR_INTERNAL_ERROR; goto wsrep_error; } } @@ -10052,6 +10096,27 @@ wsrep_append_key( DBUG_RETURN(0); } +static bool +referenced_by_foreign_key2(dict_table_t* table, + dict_index_t* index) { + ut_ad(table != NULL); + ut_ad(index != NULL); + + const dict_foreign_set* fks = &table->referenced_set; + for (dict_foreign_set::const_iterator it = fks->begin(); + it != fks->end(); + ++it) + { + dict_foreign_t* foreign = *it; + if (foreign->referenced_index != index) { + continue; + } + ut_ad(table == foreign->referenced_table); + return true; + } + return false; +} + int ha_innobase::wsrep_append_keys( /*==================*/ @@ -10131,7 +10196,7 @@ ha_innobase::wsrep_append_keys( /* !hasPK == table with no PK, must append all non-unique keys */ if (!hasPK || key_info->flags & HA_NOSAME || ((tab && - dict_table_get_referenced_constraint(tab, idx)) || + referenced_by_foreign_key2(tab, idx)) || (!tab && referenced_by_foreign_key()))) { len = wsrep_store_key_val_for_row( @@ -10346,7 +10411,8 @@ create_table_def( /* MySQL does the name length check. But we do additional check on the name length here */ - if (strlen(table_name) > MAX_FULL_NAME_LEN) { + const size_t table_name_len = strlen(table_name); + if (table_name_len > MAX_FULL_NAME_LEN) { push_warning_printf( thd, Sql_condition::WARN_LEVEL_WARN, ER_TABLE_NAME, @@ -10355,6 +10421,15 @@ create_table_def( DBUG_RETURN(ER_TABLE_NAME); } + if (table_name[table_name_len - 1] == '/') { + push_warning_printf( + thd, Sql_condition::WARN_LEVEL_WARN, + ER_TABLE_NAME, + "InnoDB: Table name is empty"); + + DBUG_RETURN(ER_WRONG_TABLE_NAME); + } + n_cols = form->s->fields; /* Check whether there already exists a FTS_DOC_ID column */ @@ -20131,3 +20206,59 @@ static void innodb_remember_check_sysvar_funcs() ut_ad((MYSQL_SYSVAR_NAME(checksum_algorithm).flags & 0x1FF) == PLUGIN_VAR_ENUM); check_sysvar_enum = MYSQL_SYSVAR_NAME(checksum_algorithm).check; } + +/********************************************************************//** +Helper function to push warnings from InnoDB internals to SQL-layer. */ +UNIV_INTERN +void +ib_push_warning( + trx_t* trx, /*!< in: trx */ + ulint error, /*!< in: error code to push as warning */ + const char *format,/*!< in: warning message */ + ...) +{ + va_list args; + THD *thd = (THD *)trx->mysql_thd; + char *buf; +#define MAX_BUF_SIZE 4*1024 + + va_start(args, format); + buf = (char *)my_malloc(MAX_BUF_SIZE, MYF(MY_WME)); + vsprintf(buf,format, args); + + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + convert_error_code_to_mysql((dberr_t)error, 0, thd), + buf); + my_free(buf); + va_end(args); +} + +/********************************************************************//** +Helper function to push warnings from InnoDB internals to SQL-layer. */ +UNIV_INTERN +void +ib_push_warning( + void* ithd, /*!< in: thd */ + ulint error, /*!< in: error code to push as warning */ + const char *format,/*!< in: warning message */ + ...) +{ + va_list args; + THD *thd = (THD *)ithd; + char *buf; +#define MAX_BUF_SIZE 4*1024 + + if (ithd == NULL) { + thd = current_thd; + } + + va_start(args, format); + buf = (char *)my_malloc(MAX_BUF_SIZE, MYF(MY_WME)); + vsprintf(buf,format, args); + + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + convert_error_code_to_mysql((dberr_t)error, 0, thd), + buf); + my_free(buf); + va_end(args); +} diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index 3d6c0ecac32..3ed0067f395 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -1,7 +1,7 @@ /***************************************************************************** -Copyright (c) 2005, 2014, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2013, 2014, SkySQL Ab. All Rights Reserved. +Copyright (c) 2005, 2015, Oracle and/or its affiliates +Copyright (c) 2013, 2015, SkySQL Ab 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 @@ -30,6 +30,7 @@ Smart ALTER TABLE #include <innodb_priv.h> #include <sql_alter.h> #include <sql_class.h> +#include <sql_table.h> #include "dict0crea.h" #include "dict0dict.h" @@ -396,6 +397,35 @@ ha_innobase::check_if_supported_inplace_alter( } } + /* If we have column that has changed from NULL -> NOT NULL + and column default has changed we need to do additional + check. */ + if ((ha_alter_info->handler_flags + & Alter_inplace_info::ALTER_COLUMN_NOT_NULLABLE) && + (ha_alter_info->handler_flags + & Alter_inplace_info::ALTER_COLUMN_DEFAULT)) { + Alter_info *alter_info = ha_alter_info->alter_info; + List_iterator<Create_field> def_it(alter_info->create_list); + Create_field *def; + while ((def=def_it++)) { + + /* If this is first column definition whose SQL type + is TIMESTAMP and it is defined as NOT NULL and + it has either constant default or function default + we must use "Copy" method. */ + if (is_timestamp_type(def->sql_type)) { + if ((def->flags & NOT_NULL_FLAG) != 0 && // NOT NULL + (def->def != NULL || // constant default ? + def->unireg_check != Field::NONE)) { // function default + ha_alter_info->unsupported_reason = innobase_get_err_msg( + ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_NOT_NULL); + DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED); + } + break; + } + } + } + /* We should be able to do the operation in-place. See if we can do it online (LOCK=NONE). */ bool online = true; @@ -523,12 +553,9 @@ ha_innobase::check_if_supported_inplace_alter( } else if (((ha_alter_info->handler_flags & Alter_inplace_info::ADD_PK_INDEX) || innobase_need_rebuild(ha_alter_info)) - && (innobase_fulltext_exist(altered_table) - || (prebuilt->table->flags2 - & DICT_TF2_FTS_HAS_DOC_ID))) { + && (innobase_fulltext_exist(altered_table))) { /* Refuse to rebuild the table online, if - fulltext indexes are to survive the rebuild, - or if the table contains a hidden FTS_DOC_ID column. */ + fulltext indexes are to survive the rebuild. */ online = false; /* If the table already contains fulltext indexes, refuse to rebuild the table natively altogether. */ @@ -819,7 +846,8 @@ innobase_find_fk_index( if (!(index->type & DICT_FTS) && dict_foreign_qualify_index( table, col_names, columns, n_cols, - index, NULL, true, 0)) { + index, NULL, true, 0, + NULL, NULL, NULL)) { for (ulint i = 0; i < n_drop_index; i++) { if (index == drop_index[i]) { /* Skip to-be-dropped indexes. */ @@ -886,10 +914,8 @@ innobase_get_foreign_key_info( char* tbl_namep = NULL; ulint db_name_len = 0; ulint tbl_name_len = 0; -#ifdef __WIN__ char db_name[MAX_DATABASE_NAME_LEN]; char tbl_name[MAX_TABLE_NAME_LEN]; -#endif fk_key = static_cast<Foreign_key*>(key); @@ -942,24 +968,29 @@ innobase_get_foreign_key_info( add_fk[num_fk] = dict_mem_foreign_create(); #ifndef __WIN__ - tbl_namep = fk_key->ref_table.str; - tbl_name_len = fk_key->ref_table.length; - db_namep = fk_key->ref_db.str; - db_name_len = fk_key->ref_db.length; + if(fk_key->ref_db.str) { + tablename_to_filename(fk_key->ref_db.str, db_name, + MAX_DATABASE_NAME_LEN); + db_namep = db_name; + db_name_len = strlen(db_name); + } + if (fk_key->ref_table.str) { + tablename_to_filename(fk_key->ref_table.str, tbl_name, + MAX_TABLE_NAME_LEN); + tbl_namep = tbl_name; + tbl_name_len = strlen(tbl_name); + } #else ut_ad(fk_key->ref_table.str); - - memcpy(tbl_name, fk_key->ref_table.str, - fk_key->ref_table.length); - tbl_name[fk_key->ref_table.length] = 0; + tablename_to_filename(fk_key->ref_table.str, tbl_name, + MAX_TABLE_NAME_LEN); innobase_casedn_str(tbl_name); tbl_name_len = strlen(tbl_name); tbl_namep = &tbl_name[0]; if (fk_key->ref_db.str != NULL) { - memcpy(db_name, fk_key->ref_db.str, - fk_key->ref_db.length); - db_name[fk_key->ref_db.length] = 0; + tablename_to_filename(fk_key->ref_db.str, db_name, + MAX_DATABASE_NAME_LEN); innobase_casedn_str(db_name); db_name_len = strlen(db_name); db_namep = &db_name[0]; @@ -1009,7 +1040,8 @@ innobase_get_foreign_key_info( referenced_table, 0, referenced_column_names, i, index, - TRUE, FALSE); + TRUE, FALSE, + NULL, NULL, NULL); DBUG_EXECUTE_IF( "innodb_test_no_reference_idx", @@ -3310,58 +3342,74 @@ innobase_check_foreign_key_index( ulint n_drop_fk) /*!< in: Number of foreign keys to drop */ { - dict_foreign_t* foreign; + ut_ad(index != NULL); + ut_ad(indexed_table != NULL); - /* Check if the index is referenced. */ - foreign = dict_table_get_referenced_constraint(indexed_table, index); - - ut_ad(!foreign || indexed_table - == foreign->referenced_table); - - if (foreign - && !dict_foreign_find_index( - indexed_table, col_names, - foreign->referenced_col_names, - foreign->n_fields, index, - /*check_charsets=*/TRUE, - /*check_null=*/FALSE) - && !innobase_find_equiv_index( - foreign->referenced_col_names, - foreign->n_fields, - ha_alter_info->key_info_buffer, - ha_alter_info->index_add_buffer, - ha_alter_info->index_add_count) - ) { - trx->error_info = index; - return(true); + const dict_foreign_set* fks = &indexed_table->referenced_set; + + /* Check for all FK references from other tables to the index. */ + for (dict_foreign_set::const_iterator it = fks->begin(); + it != fks->end(); ++it) { + + dict_foreign_t* foreign = *it; + if (foreign->referenced_index != index) { + continue; + } + ut_ad(indexed_table == foreign->referenced_table); + + if (NULL == dict_foreign_find_index( + indexed_table, col_names, + foreign->referenced_col_names, + foreign->n_fields, index, + /*check_charsets=*/TRUE, + /*check_null=*/FALSE, + NULL, NULL, NULL) + && NULL == innobase_find_equiv_index( + foreign->referenced_col_names, + foreign->n_fields, + ha_alter_info->key_info_buffer, + ha_alter_info->index_add_buffer, + ha_alter_info->index_add_count)) { + + /* Index cannot be dropped. */ + trx->error_info = index; + return(true); + } } - /* Check if this index references some - other table */ - foreign = dict_table_get_foreign_constraint( - indexed_table, index); - - ut_ad(!foreign || indexed_table - == foreign->foreign_table); - - if (foreign - && !innobase_dropping_foreign( - foreign, drop_fk, n_drop_fk) - && !dict_foreign_find_index( - indexed_table, col_names, - foreign->foreign_col_names, - foreign->n_fields, index, - /*check_charsets=*/TRUE, - /*check_null=*/FALSE) - && !innobase_find_equiv_index( - foreign->foreign_col_names, - foreign->n_fields, - ha_alter_info->key_info_buffer, - ha_alter_info->index_add_buffer, - ha_alter_info->index_add_count) - ) { - trx->error_info = index; - return(true); + fks = &indexed_table->foreign_set; + + /* Check for all FK references in current table using the index. */ + for (dict_foreign_set::const_iterator it = fks->begin(); + it != fks->end(); ++it) { + + dict_foreign_t* foreign = *it; + if (foreign->foreign_index != index) { + continue; + } + + ut_ad(indexed_table == foreign->foreign_table); + + if (!innobase_dropping_foreign( + foreign, drop_fk, n_drop_fk) + && NULL == dict_foreign_find_index( + indexed_table, col_names, + foreign->foreign_col_names, + foreign->n_fields, index, + /*check_charsets=*/TRUE, + /*check_null=*/FALSE, + NULL, NULL, NULL) + && NULL == innobase_find_equiv_index( + foreign->foreign_col_names, + foreign->n_fields, + ha_alter_info->key_info_buffer, + ha_alter_info->index_add_buffer, + ha_alter_info->index_add_count)) { + + /* Index cannot be dropped. */ + trx->error_info = index; + return(true); + } } return(false); @@ -3596,6 +3644,19 @@ check_if_ok_to_rename: if (index->type & DICT_FTS) { DBUG_ASSERT(index->type == DICT_FTS || (index->type & DICT_CORRUPT)); + + /* We need to drop any corrupted fts indexes + before we add a new fts index. */ + if (add_fts_idx && index->type & DICT_CORRUPT) { + ib_errf(user_thd, IB_LOG_LEVEL_ERROR, + ER_INNODB_INDEX_CORRUPT, + "Fulltext index '%s' is corrupt. " + "you should drop this index first.", + index->name); + + goto err_exit_no_heap; + } + continue; } @@ -4118,6 +4179,10 @@ oom: : ha_alter_info->key_info_buffer[ prebuilt->trx->error_key_num].name); break; + case DB_ENCRYPTED_DECRYPT_FAILED: + my_error(ER_NO_SUCH_TABLE_IN_ENGINE, MYF(0), + table_share->db.str, table_share->table_name.str); + break; default: my_error_innodb(error, table_share->table_name.str, @@ -4817,7 +4882,8 @@ innobase_update_foreign_try( fk->n_fields, fk->referenced_index, TRUE, fk->type & (DICT_FOREIGN_ON_DELETE_SET_NULL - | DICT_FOREIGN_ON_UPDATE_SET_NULL)); + | DICT_FOREIGN_ON_UPDATE_SET_NULL), + NULL, NULL, NULL); if (!fk->foreign_index) { my_error(ER_FK_INCORRECT_OPTION, MYF(0), table_name, fk->id); @@ -4829,7 +4895,7 @@ innobase_update_foreign_try( names, while the columns in ctx->old_table have not been renamed yet. */ error = dict_create_add_foreign_to_dictionary( - ctx->old_table->name, fk, trx); + (dict_table_t*)ctx->old_table,ctx->old_table->name, fk, trx); DBUG_EXECUTE_IF( "innodb_test_cannot_add_fk_system", diff --git a/storage/innobase/include/btr0btr.ic b/storage/innobase/include/btr0btr.ic index 40b468b200a..6604ac6a6f0 100644 --- a/storage/innobase/include/btr0btr.ic +++ b/storage/innobase/include/btr0btr.ic @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1994, 2012, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2015, 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 @@ -57,13 +58,15 @@ btr_block_get_func( buf_block_t* block; block = buf_page_get_gen(space, zip_size, page_no, mode, - NULL, BUF_GET, file, line, mtr); + NULL, BUF_GET, file, line, mtr); - if (mode != RW_NO_LATCH) { + if (block) { + if (mode != RW_NO_LATCH) { - buf_block_dbg_add_level( - block, index != NULL && dict_index_is_ibuf(index) - ? SYNC_IBUF_TREE_NODE : SYNC_TREE_NODE); + buf_block_dbg_add_level( + block, index != NULL && dict_index_is_ibuf(index) + ? SYNC_IBUF_TREE_NODE : SYNC_TREE_NODE); + } } return(block); diff --git a/storage/innobase/include/btr0cur.h b/storage/innobase/include/btr0cur.h index d0fd5c2158a..88e3b84e04b 100644 --- a/storage/innobase/include/btr0cur.h +++ b/storage/innobase/include/btr0cur.h @@ -136,7 +136,7 @@ Note that if mode is PAGE_CUR_LE, which is used in inserts, then cursor->up_match and cursor->low_match both will have sensible values. If mode is PAGE_CUR_GE, then up_match will a have a sensible value. */ UNIV_INTERN -void +dberr_t btr_cur_search_to_nth_level( /*========================*/ dict_index_t* index, /*!< in: index */ @@ -173,7 +173,7 @@ btr_cur_search_to_nth_level( /*****************************************************************//** Opens a cursor at either end of an index. */ UNIV_INTERN -void +dberr_t btr_cur_open_at_index_side_func( /*============================*/ bool from_left, /*!< in: true if open to the low end, diff --git a/storage/innobase/include/btr0pcur.h b/storage/innobase/include/btr0pcur.h index cfbaacf4de3..d8e7cf6b283 100644 --- a/storage/innobase/include/btr0pcur.h +++ b/storage/innobase/include/btr0pcur.h @@ -114,7 +114,7 @@ btr_pcur_open_low( Opens an persistent cursor to an index tree without initializing the cursor. */ UNIV_INLINE -void +dberr_t btr_pcur_open_with_no_init_func( /*============================*/ dict_index_t* index, /*!< in: index */ @@ -143,7 +143,7 @@ btr_pcur_open_with_no_init_func( /*****************************************************************//** Opens a persistent cursor at either end of an index. */ UNIV_INLINE -void +dberr_t btr_pcur_open_at_index_side( /*========================*/ bool from_left, /*!< in: true if open to the low end, diff --git a/storage/innobase/include/btr0pcur.ic b/storage/innobase/include/btr0pcur.ic index 7e355d3709d..1cd13824542 100644 --- a/storage/innobase/include/btr0pcur.ic +++ b/storage/innobase/include/btr0pcur.ic @@ -447,7 +447,7 @@ btr_pcur_open_low( Opens an persistent cursor to an index tree without initializing the cursor. */ UNIV_INLINE -void +dberr_t btr_pcur_open_with_no_init_func( /*============================*/ dict_index_t* index, /*!< in: index */ @@ -472,6 +472,7 @@ btr_pcur_open_with_no_init_func( mtr_t* mtr) /*!< in: mtr */ { btr_cur_t* btr_cursor; + dberr_t err = DB_SUCCESS; cursor->latch_mode = latch_mode; cursor->search_mode = mode; @@ -480,20 +481,21 @@ btr_pcur_open_with_no_init_func( btr_cursor = btr_pcur_get_btr_cur(cursor); - btr_cur_search_to_nth_level(index, 0, tuple, mode, latch_mode, - btr_cursor, has_search_latch, - file, line, mtr); + err = btr_cur_search_to_nth_level(index, 0, tuple, mode, latch_mode, + btr_cursor, has_search_latch, + file, line, mtr); cursor->pos_state = BTR_PCUR_IS_POSITIONED; cursor->old_stored = BTR_PCUR_OLD_NOT_STORED; cursor->trx_if_known = NULL; + return err; } /*****************************************************************//** Opens a persistent cursor at either end of an index. */ UNIV_INLINE -void +dberr_t btr_pcur_open_at_index_side( /*========================*/ bool from_left, /*!< in: true if open to the low end, @@ -506,6 +508,8 @@ btr_pcur_open_at_index_side( (0=leaf) */ mtr_t* mtr) /*!< in/out: mini-transaction */ { + dberr_t err = DB_SUCCESS; + pcur->latch_mode = BTR_LATCH_MODE_WITHOUT_FLAGS(latch_mode); pcur->search_mode = from_left ? PAGE_CUR_G : PAGE_CUR_L; @@ -514,13 +518,15 @@ btr_pcur_open_at_index_side( btr_pcur_init(pcur); } - btr_cur_open_at_index_side(from_left, index, latch_mode, - btr_pcur_get_btr_cur(pcur), level, mtr); + err = btr_cur_open_at_index_side(from_left, index, latch_mode, + btr_pcur_get_btr_cur(pcur), level, mtr); pcur->pos_state = BTR_PCUR_IS_POSITIONED; pcur->old_stored = BTR_PCUR_OLD_NOT_STORED; pcur->trx_if_known = NULL; + + return (err); } /**********************************************************************//** diff --git a/storage/innobase/include/buf0buf.h b/storage/innobase/include/buf0buf.h index 11cf7ebc8cc..6c8e8e00507 100644 --- a/storage/innobase/include/buf0buf.h +++ b/storage/innobase/include/buf0buf.h @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2014, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2013, 2014, SkySQL Ab. All Rights Reserved. +Copyright (c) 2013, 2015, 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 @@ -430,7 +430,8 @@ buf_page_get_gen( BUF_GET_IF_IN_POOL_OR_WATCH */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ - mtr_t* mtr); /*!< in: mini-transaction */ + mtr_t* mtr, /*!< in: mini-transaction */ + dberr_t* err = NULL); /*!< out: error code */ /********************************************************************//** Initializes a page to the buffer buf_pool. The page is usually not read from a file even if it cannot be found in the buffer buf_pool. This is one @@ -1582,9 +1583,14 @@ struct buf_page_t{ operation needed. */ unsigned key_version; /*!< key version for this block */ - bool page_encrypted; /*!< page is encrypted */ + bool page_encrypted; /*!< page is page encrypted */ bool page_compressed;/*!< page is page compressed */ - + ulint stored_checksum;/*!< stored page checksum if page + encrypted */ + bool encrypted; /*!< page is still encrypted */ + ulint calculated_checksum; + /*!< calculated checksum if page + encrypted */ ulint real_size; /*!< Real size of the page Normal pages == UNIV_PAGE_SIZE page compressed pages, payload diff --git a/storage/innobase/include/buf0buf.ic b/storage/innobase/include/buf0buf.ic index 6c128b097b0..a80a4c0a391 100644 --- a/storage/innobase/include/buf0buf.ic +++ b/storage/innobase/include/buf0buf.ic @@ -663,13 +663,18 @@ buf_block_get_frame( /*================*/ const buf_block_t* block) /*!< in: pointer to the control block */ { - ut_ad(block); + if (!block) { + return NULL; + } switch (buf_block_get_state(block)) { case BUF_BLOCK_POOL_WATCH: case BUF_BLOCK_ZIP_PAGE: case BUF_BLOCK_ZIP_DIRTY: case BUF_BLOCK_NOT_USED: + if (block->page.encrypted) { + goto ok; + } ut_error; break; case BUF_BLOCK_FILE_PAGE: diff --git a/storage/innobase/include/buf0rea.h b/storage/innobase/include/buf0rea.h index d2a1f264ff5..10714031710 100644 --- a/storage/innobase/include/buf0rea.h +++ b/storage/innobase/include/buf0rea.h @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2013, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2015, 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 @@ -41,7 +42,8 @@ buf_read_page( /*==========*/ ulint space, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size in bytes, or 0 */ - ulint offset);/*!< in: page number */ + ulint offset, /*!< in: page number */ + buf_page_t** bpage);/*!< out: page */ /********************************************************************//** High-level function which reads a page asynchronously from a file to the buffer buf_pool if it is not already there. Sets the io_fix flag and sets diff --git a/storage/innobase/include/db0err.h b/storage/innobase/include/db0err.h index 1e87ce3fdb8..24889a9b9c5 100644 --- a/storage/innobase/include/db0err.h +++ b/storage/innobase/include/db0err.h @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2014, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2015, 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 @@ -132,6 +133,11 @@ enum dberr_t { /*< Too many words in a phrase */ DB_TOO_BIG_FOR_REDO, /* Record length greater than 10% of redo log */ + DB_ENCRYPTED_DECRYPT_FAILED, /* Tablespace encrypted and + decrypt operaton failed because + of missing key management plugin, + or missing or incorrect key or + incorret AES method or algorithm. */ /* The following are partial failure codes */ DB_FAIL = 1000, DB_OVERFLOW, diff --git a/storage/innobase/include/dict0crea.h b/storage/innobase/include/dict0crea.h index c40e5356ece..564fad35748 100644 --- a/storage/innobase/include/dict0crea.h +++ b/storage/innobase/include/dict0crea.h @@ -113,6 +113,17 @@ UNIV_INTERN dberr_t dict_create_or_check_foreign_constraint_tables(void); /*================================================*/ + +/********************************************************************//** +Construct foreign key constraint defintion from data dictionary information. +*/ +UNIV_INTERN +char* +dict_foreign_def_get( +/*=================*/ + dict_foreign_t* foreign,/*!< in: foreign */ + trx_t* trx); /*!< in: trx */ + /********************************************************************//** Generate a foreign key constraint name when it was not named by the user. A generated constraint has a name of the format dbname/tablename_ibfk_NUMBER, @@ -177,11 +188,22 @@ UNIV_INTERN dberr_t dict_create_add_foreign_to_dictionary( /*==================================*/ + dict_table_t* table, /*!< in: table */ const char* name, /*!< in: table name */ const dict_foreign_t* foreign,/*!< in: foreign key */ trx_t* trx) /*!< in/out: dictionary transaction */ __attribute__((nonnull, warn_unused_result)); +/********************************************************************//** +Construct foreign key constraint defintion from data dictionary information. +*/ +UNIV_INTERN +char* +dict_foreign_def_get( +/*=================*/ + dict_foreign_t* foreign,/*!< in: foreign */ + trx_t* trx); /*!< in: trx */ + /* Table create node structure */ struct tab_node_t{ que_common_t common; /*!< node type: QUE_NODE_TABLE_CREATE */ diff --git a/storage/innobase/include/dict0dict.h b/storage/innobase/include/dict0dict.h index 820353a0c93..6b1856b8187 100644 --- a/storage/innobase/include/dict0dict.h +++ b/storage/innobase/include/dict0dict.h @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1996, 2014, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2012, Facebook Inc. Copyright (c) 2013, SkySQL Ab. All Rights Reserved. @@ -449,18 +449,6 @@ dict_foreign_add_to_cache( /*!< in: error to be ignored */ __attribute__((nonnull(1), warn_unused_result)); /*********************************************************************//** -Check if the index is referenced by a foreign key, if TRUE return the -matching instance NULL otherwise. -@return pointer to foreign key struct if index is defined for foreign -key, otherwise NULL */ -UNIV_INTERN -dict_foreign_t* -dict_table_get_referenced_constraint( -/*=================================*/ - dict_table_t* table, /*!< in: InnoDB table */ - dict_index_t* index) /*!< in: InnoDB index */ - __attribute__((nonnull, warn_unused_result)); -/*********************************************************************//** Checks if a table is referenced by foreign keys. @return TRUE if table is referenced by a foreign key */ UNIV_INTERN @@ -495,19 +483,6 @@ dict_str_starts_with_keyword( const char* keyword) /*!< in: keyword to look for */ __attribute__((nonnull, warn_unused_result)); /*********************************************************************//** -Checks if a index is defined for a foreign key constraint. Index is a part -of a foreign key constraint if the index is referenced by foreign key -or index is a foreign key index -@return pointer to foreign key struct if index is defined for foreign -key, otherwise NULL */ -UNIV_INTERN -dict_foreign_t* -dict_table_get_foreign_constraint( -/*==============================*/ - dict_table_t* table, /*!< in: InnoDB table */ - dict_index_t* index) /*!< in: InnoDB index */ - __attribute__((nonnull, warn_unused_result)); -/*********************************************************************//** Scans a table create SQL string and adds to the data dictionary the foreign key constraints declared in the string. This function should be called after the indexes for a table have been created. @@ -596,10 +571,18 @@ dict_foreign_find_index( /*!< in: whether to check charsets. only has an effect if types_idx != NULL */ - ulint check_null) + ulint check_null, /*!< in: nonzero if none of the columns must be declared NOT NULL */ + ulint* error, /*!< out: error code */ + ulint* err_col_no, + /*!< out: column number where + error happened */ + dict_index_t** err_index) + /*!< out: index where error + happened */ + __attribute__((nonnull(1,3), warn_unused_result)); /**********************************************************************//** Returns a column's name. @@ -691,10 +674,17 @@ dict_foreign_qualify_index( /*!< in: whether to check charsets. only has an effect if types_idx != NULL */ - ulint check_null) + ulint check_null, /*!< in: nonzero if none of the columns must be declared NOT NULL */ + ulint* error, /*!< out: error code */ + ulint* err_col_no, + /*!< out: column number where + error happened */ + dict_index_t** err_index) + /*!< out: index where error + happened */ __attribute__((nonnull(1,3), warn_unused_result)); #ifdef UNIV_DEBUG /********************************************************************//** diff --git a/storage/innobase/include/dict0dict.ic b/storage/innobase/include/dict0dict.ic index dd42b478c1f..a3a3446d507 100644 --- a/storage/innobase/include/dict0dict.ic +++ b/storage/innobase/include/dict0dict.ic @@ -1,7 +1,7 @@ /***************************************************************************** -Copyright (c) 1996, 2012, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2013, 2014, SkySQL Ab. All Rights Reserved. +Copyright (c) 1996, 2015, Oracle and/or its affiliates +Copyright (c) 2013, 2015, SkySQL Ab 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 diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h index 24db728ae08..53a5d6cb08b 100644 --- a/storage/innobase/include/dict0mem.h +++ b/storage/innobase/include/dict0mem.h @@ -2,7 +2,7 @@ Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2012, Facebook Inc. -Copyright (c) 2013, SkySQL Ab. All Rights Reserved. +Copyright (c) 2013, 2015, 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 @@ -48,6 +48,9 @@ Created 1/8/1996 Heikki Tuuri #include "trx0types.h" #include "fts0fts.h" #include "os0once.h" +#include "fil0fil.h" +#include <my_crypt.h> +#include "fil0crypt.h" #include <set> #include <algorithm> #include <iterator> @@ -1014,6 +1017,8 @@ struct dict_table_t{ table_id_t id; /*!< id of the table */ mem_heap_t* heap; /*!< memory heap */ char* name; /*!< table name */ + void* thd; /*!< thd */ + fil_space_crypt_t *crypt_data; /*!< crypt data if present */ const char* dir_path_of_temp_table;/*!< NULL or the directory path where a TEMPORARY table that was explicitly created by a user should be placed if @@ -1326,6 +1331,7 @@ struct dict_table_t{ locks; /*!< list of locks on the table; protected by lock_sys->mutex */ #endif /* !UNIV_HOTBACKUP */ + ibool is_encrypted; #ifdef UNIV_DEBUG ulint magic_n;/*!< magic number */ diff --git a/storage/innobase/include/fil0crypt.h b/storage/innobase/include/fil0crypt.h index 34452badc9f..30db095775f 100644 --- a/storage/innobase/include/fil0crypt.h +++ b/storage/innobase/include/fil0crypt.h @@ -382,6 +382,17 @@ fil_crypt_set_encrypt_tables( uint val); /*!< in: New srv_encrypt_tables setting */ +/****************************************************************** +Calculate post encryption checksum +@return page checksum or BUF_NO_CHECKSUM_MAGIC +not needed. */ +UNIV_INTERN +ulint +fil_crypt_calculate_checksum( +/*=========================*/ + ulint zip_size, /*!< in: zip_size or 0 */ + byte* dst_frame); /*!< in: page where to calculate */ + #ifndef UNIV_NONINL #include "fil0crypt.ic" #endif diff --git a/storage/innobase/include/fil0fil.h b/storage/innobase/include/fil0fil.h index 93ff999bcfe..e842049e734 100644 --- a/storage/innobase/include/fil0fil.h +++ b/storage/innobase/include/fil0fil.h @@ -213,7 +213,8 @@ struct fsp_open_info { #ifdef UNIV_LOG_ARCHIVE ulint arch_log_no; /*!< latest archived log file number */ #endif /* UNIV_LOG_ARCHIVE */ - fil_space_crypt_t* crypt_data; /*!< crypt data */ + fil_space_crypt_t* crypt_data; /*!< crypt data */ + dict_table_t* table; /*!< table */ }; struct fil_space_t; @@ -833,7 +834,8 @@ fil_open_single_table_tablespace( ulint flags, /*!< in: tablespace flags */ const char* tablename, /*!< in: table name in the databasename/tablename format */ - const char* filepath) /*!< in: tablespace filepath */ + const char* filepath, /*!< in: tablespace filepath */ + dict_table_t* table) /*!< in: table */ __attribute__((nonnull(5), warn_unused_result)); #endif /* !UNIV_HOTBACKUP */ diff --git a/storage/innobase/include/fts0fts.h b/storage/innobase/include/fts0fts.h index a2996ecacc8..d54ed281d9a 100644 --- a/storage/innobase/include/fts0fts.h +++ b/storage/innobase/include/fts0fts.h @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 2011, 2013, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2011, 2015, Oracle and/or its affiliates. All Rights Reserved. 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 diff --git a/storage/innobase/include/ha_prototypes.h b/storage/innobase/include/ha_prototypes.h index 4d523cf1289..59abb0863d9 100644 --- a/storage/innobase/include/ha_prototypes.h +++ b/storage/innobase/include/ha_prototypes.h @@ -625,5 +625,22 @@ innobase_convert_to_filename_charset( const char* from, /* in: identifier to convert */ ulint len); /* in: length of 'to', in bytes */ - +/********************************************************************//** +Helper function to push warnings from InnoDB internals to SQL-layer. */ +UNIV_INTERN +void +ib_push_warning( + trx_t* trx, /*!< in: trx */ + ulint error, /*!< in: error code to push as warning */ + const char *format,/*!< in: warning message */ + ...); +/********************************************************************//** +Helper function to push warnings from InnoDB internals to SQL-layer. */ +UNIV_INTERN +void +ib_push_warning( + void* ithd, /*!< in: thd */ + ulint error, /*!< in: error code to push as warning */ + const char *format,/*!< in: warning message */ + ...); #endif /* HA_INNODB_PROTOTYPES_H */ diff --git a/storage/innobase/include/page0cur.ic b/storage/innobase/include/page0cur.ic index 028d33b17aa..6e068d9f739 100644 --- a/storage/innobase/include/page0cur.ic +++ b/storage/innobase/include/page0cur.ic @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1994, 2013, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2015, 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 @@ -39,7 +40,10 @@ page_cur_get_page( page_cur_t* cur) /*!< in: page cursor */ { ut_ad(cur); - ut_ad(page_align(cur->rec) == cur->block->frame); + + if (cur->rec) { + ut_ad(page_align(cur->rec) == cur->block->frame); + } return(page_align(cur->rec)); } @@ -54,7 +58,11 @@ page_cur_get_block( page_cur_t* cur) /*!< in: page cursor */ { ut_ad(cur); - ut_ad(page_align(cur->rec) == cur->block->frame); + + if (cur->rec) { + ut_ad(page_align(cur->rec) == cur->block->frame); + } + return(cur->block); } @@ -80,7 +88,10 @@ page_cur_get_rec( page_cur_t* cur) /*!< in: page cursor */ { ut_ad(cur); - ut_ad(page_align(cur->rec) == cur->block->frame); + + if (cur->rec) { + ut_ad(page_align(cur->rec) == cur->block->frame); + } return(cur->rec); } diff --git a/storage/innobase/include/row0purge.h b/storage/innobase/include/row0purge.h index 93dcf9cf49b..888289a6c79 100644 --- a/storage/innobase/include/row0purge.h +++ b/storage/innobase/include/row0purge.h @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1997, 2012, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 1997, 2015, Oracle and/or its affiliates. All Rights Reserved. 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 @@ -119,6 +119,16 @@ struct purge_node_t{ clustered index record */ ibool done; /* Debug flag */ +#ifdef UNIV_DEBUG + /***********************************************************//** + Validate the persisent cursor. The purge node has two references + to the clustered index record - one via the ref member, and the + other via the persistent cursor. These two references must match + each other if the found_clust flag is set. + @return true if the persistent cursor is consistent with + the ref member.*/ + bool validate_pcur(); +#endif }; #ifndef UNIV_NONINL diff --git a/storage/innobase/include/univ.i b/storage/innobase/include/univ.i index 4732ac48290..8dd97cf0070 100644 --- a/storage/innobase/include/univ.i +++ b/storage/innobase/include/univ.i @@ -45,7 +45,7 @@ Created 1/20/1994 Heikki Tuuri #define INNODB_VERSION_MAJOR 5 #define INNODB_VERSION_MINOR 6 -#define INNODB_VERSION_BUGFIX 25 +#define INNODB_VERSION_BUGFIX 26 /* The following is the InnoDB version as shown in SELECT plugin_version FROM information_schema.plugins; diff --git a/storage/innobase/log/log0crypt.cc b/storage/innobase/log/log0crypt.cc index 6d0a2d6a1f8..8c4fd623fc6 100644 --- a/storage/innobase/log/log0crypt.cc +++ b/storage/innobase/log/log0crypt.cc @@ -131,15 +131,16 @@ log_blocks_crypt( const byte* block, /*!< in: blocks before encrypt/decrypt*/ ulint size, /*!< in: size of block */ byte* dst_block, /*!< out: blocks after encrypt/decrypt */ - bool is_encrypt) /*!< in: encrypt or decrypt*/ + int what) /*!< in: encrypt or decrypt*/ { byte *log_block = (byte*)block; Crypt_result rc = MY_AES_OK; uint dst_len; byte aes_ctr_counter[MY_AES_BLOCK_SIZE]; + byte is_encrypt= what == ENCRYPTION_FLAG_ENCRYPT; lsn_t lsn = is_encrypt ? log_sys->lsn : srv_start_lsn; - const int src_len = OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_HDR_SIZE; + const uint src_len = OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_HDR_SIZE; for (ulint i = 0; i < size ; i += OS_FILE_LOG_BLOCK_SIZE) { ulint log_block_no = log_block_get_hdr_no(log_block); lsn_t log_block_start_lsn = log_block_get_start_lsn( @@ -174,21 +175,13 @@ log_blocks_crypt( bzero(aes_ctr_counter + 15, 1); int rc; - if (is_encrypt) { - rc = encryption_encrypt(log_block + LOG_BLOCK_HDR_SIZE, src_len, - dst_block + LOG_BLOCK_HDR_SIZE, &dst_len, - (unsigned char*)(info->crypt_key), 16, - aes_ctr_counter, MY_AES_BLOCK_SIZE, 1, - LOG_DEFAULT_ENCRYPTION_KEY, - info->key_version); - } else { - rc = encryption_decrypt(log_block + LOG_BLOCK_HDR_SIZE, src_len, - dst_block + LOG_BLOCK_HDR_SIZE, &dst_len, - (unsigned char*)(info->crypt_key), 16, - aes_ctr_counter, MY_AES_BLOCK_SIZE, 1, - LOG_DEFAULT_ENCRYPTION_KEY, - info->key_version); - } + rc = encryption_crypt(log_block + LOG_BLOCK_HDR_SIZE, src_len, + dst_block + LOG_BLOCK_HDR_SIZE, &dst_len, + (unsigned char*)(info->crypt_key), 16, + aes_ctr_counter, MY_AES_BLOCK_SIZE, + what | ENCRYPTION_FLAG_NOPAD, + LOG_DEFAULT_ENCRYPTION_KEY, + info->key_version); ut_a(rc == MY_AES_OK); ut_a(dst_len == src_len); @@ -230,10 +223,11 @@ init_crypt_key( } uint dst_len; - int rc= my_aes_encrypt_ecb(info->crypt_msg, sizeof(info->crypt_msg), //src, srclen - info->crypt_key, &dst_len, //dst, &dstlen - (unsigned char*)&mysqld_key, sizeof(mysqld_key), - NULL, 0, 1); + int rc= my_aes_crypt(MY_AES_ECB, ENCRYPTION_FLAG_NOPAD|ENCRYPTION_FLAG_ENCRYPT, + info->crypt_msg, sizeof(info->crypt_msg), //src, srclen + info->crypt_key, &dst_len, //dst, &dstlen + (unsigned char*)&mysqld_key, sizeof(mysqld_key), + NULL, 0); if (rc != MY_AES_OK || dst_len != MY_AES_BLOCK_SIZE) { fprintf(stderr, @@ -296,7 +290,7 @@ log_blocks_encrypt( const ulint size, /*!< in: size of blocks, must be multiple of a log block */ byte* dst_block) /*!< out: blocks after encryption */ { - return log_blocks_crypt(block, size, dst_block, true); + return log_blocks_crypt(block, size, dst_block, ENCRYPTION_FLAG_ENCRYPT); } /*********************************************************************//** @@ -366,7 +360,7 @@ log_encrypt_before_write( byte* dst_frame = (byte*)malloc(size); //encrypt log blocks content - Crypt_result result = log_blocks_crypt(block, size, dst_frame, true); + Crypt_result result = log_blocks_crypt(block, size, dst_frame, ENCRYPTION_FLAG_ENCRYPT); if (result == MY_AES_OK) { ut_ad(block[0] == dst_frame[0]); @@ -392,7 +386,7 @@ log_decrypt_after_read( byte* dst_frame = (byte*)malloc(size); // decrypt log blocks content - Crypt_result result = log_blocks_crypt(frame, size, dst_frame, false); + Crypt_result result = log_blocks_crypt(frame, size, dst_frame, ENCRYPTION_FLAG_DECRYPT); if (result == MY_AES_OK) { memcpy(frame, dst_frame, size); diff --git a/storage/innobase/os/os0file.cc b/storage/innobase/os/os0file.cc index 80525a39d1e..7aa321cfddb 100644 --- a/storage/innobase/os/os0file.cc +++ b/storage/innobase/os/os0file.cc @@ -1571,6 +1571,7 @@ os_file_create_simple_no_error_handling_func( *success = (file != INVALID_HANDLE_VALUE); #else /* __WIN__ */ int create_flag; + const char* mode_str = NULL; ut_a(name); if (create_mode != OS_FILE_OPEN && create_mode != OS_FILE_OPEN_RAW) @@ -1581,6 +1582,8 @@ os_file_create_simple_no_error_handling_func( if (create_mode == OS_FILE_OPEN) { + mode_str = "OPEN"; + if (access_type == OS_FILE_READ_ONLY) { create_flag = O_RDONLY; @@ -1599,10 +1602,14 @@ os_file_create_simple_no_error_handling_func( } else if (srv_read_only_mode) { + mode_str = "OPEN"; + create_flag = O_RDONLY; } else if (create_mode == OS_FILE_CREATE) { + mode_str = "CREATE"; + create_flag = O_RDWR | O_CREAT | O_EXCL; } else { @@ -1617,6 +1624,17 @@ os_file_create_simple_no_error_handling_func( *success = file == -1 ? FALSE : TRUE; + /* This function is always called for data files, we should disable + OS caching (O_DIRECT) here as we do in os_file_create_func(), so + we open the same file in the same mode, see man page of open(2). */ + if (!srv_read_only_mode + && *success + && (srv_unix_file_flush_method == SRV_UNIX_O_DIRECT + || srv_unix_file_flush_method == SRV_UNIX_O_DIRECT_NO_FSYNC)) { + + os_file_set_nocache(file, name, mode_str); + } + #ifdef USE_FILE_LOCK if (!srv_read_only_mode && *success diff --git a/storage/innobase/row/row0import.cc b/storage/innobase/row/row0import.cc index d5f766ef51b..4034a228153 100644 --- a/storage/innobase/row/row0import.cc +++ b/storage/innobase/row/row0import.cc @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 2012, 2013, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2012, 2015, Oracle and/or its affiliates. All Rights Reserved. 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 @@ -139,14 +139,6 @@ struct row_import { ulint find_col(const char* name) const UNIV_NOTHROW; /** - Find the index field entry in in the cfg indexes fields. - @name - of the index to look for - @return instance if found else 0. */ - const dict_field_t* find_field( - const row_index_t* cfg_index, - const char* name) const UNIV_NOTHROW; - - /** Get the number of rows for which purge failed during the convert phase. @param name - index name @return number of rows for which purge failed. */ @@ -1141,30 +1133,6 @@ row_import::find_col( } /** -Find the index field entry in in the cfg indexes fields. -@name - of the index to look for -@return instance if found else 0. */ -const dict_field_t* -row_import::find_field( - const row_index_t* cfg_index, - const char* name) const UNIV_NOTHROW -{ - const dict_field_t* field = cfg_index->m_fields; - - for (ulint i = 0; i < cfg_index->m_n_fields; ++i, ++field) { - const char* field_name; - - field_name = reinterpret_cast<const char*>(field->name); - - if (strcmp(field_name, name) == 0) { - return(field); - } - } - - return(0); -} - -/** Check if the index schema that was read from the .cfg file matches the in memory index definition. @return DB_SUCCESS or error code. */ @@ -1187,51 +1155,60 @@ row_import::match_index_columns( return(DB_ERROR); } - cfg_index->m_srv_index = index; + if (cfg_index->m_n_fields != index->n_fields) { - const dict_field_t* field = index->fields; + ib_errf(thd, IB_LOG_LEVEL_ERROR, + ER_TABLE_SCHEMA_MISMATCH, + "Index field count %lu doesn't match" + " tablespace metadata file value %lu", + (ulong) index->n_fields, + (ulong) cfg_index->m_n_fields); - for (ulint i = 0; i < index->n_fields; ++i, ++field) { + return(DB_ERROR); + } - const dict_field_t* cfg_field; + cfg_index->m_srv_index = index; - cfg_field = find_field(cfg_index, field->name); + const dict_field_t* field = index->fields; + const dict_field_t* cfg_field = cfg_index->m_fields; - if (cfg_field == 0) { + for (ulint i = 0; i < index->n_fields; ++i, ++field, ++cfg_field) { + + if (strcmp(field->name, cfg_field->name) != 0) { ib_errf(thd, IB_LOG_LEVEL_ERROR, ER_TABLE_SCHEMA_MISMATCH, - "Index %s field %s not found in tablespace " - "meta-data file.", - index->name, field->name); + "Index field name %s doesn't match" + " tablespace metadata field name %s" + " for field position %lu", + field->name, cfg_field->name, (ulong) i); err = DB_ERROR; - } else { + } - if (cfg_field->prefix_len != field->prefix_len) { - ib_errf(thd, IB_LOG_LEVEL_ERROR, - ER_TABLE_SCHEMA_MISMATCH, - "Index %s field %s prefix len %lu " - "doesn't match meta-data file value " - "%lu", - index->name, field->name, - (ulong) field->prefix_len, - (ulong) cfg_field->prefix_len); + if (cfg_field->prefix_len != field->prefix_len) { + ib_errf(thd, IB_LOG_LEVEL_ERROR, + ER_TABLE_SCHEMA_MISMATCH, + "Index %s field %s prefix len %lu" + " doesn't match metadata file value" + " %lu", + index->name, field->name, + (ulong) field->prefix_len, + (ulong) cfg_field->prefix_len); - err = DB_ERROR; - } + err = DB_ERROR; + } - if (cfg_field->fixed_len != field->fixed_len) { - ib_errf(thd, IB_LOG_LEVEL_ERROR, - ER_TABLE_SCHEMA_MISMATCH, - "Index %s field %s fixed len %lu " - "doesn't match meta-data file value " - "%lu", - index->name, field->name, - (ulong) field->fixed_len, - (ulong) cfg_field->fixed_len); + if (cfg_field->fixed_len != field->fixed_len) { + ib_errf(thd, IB_LOG_LEVEL_ERROR, + ER_TABLE_SCHEMA_MISMATCH, + "Index %s field %s fixed len %lu" + " doesn't match metadata file value" + " %lu", + index->name, field->name, + (ulong) field->fixed_len, + (ulong) cfg_field->fixed_len); - err = DB_ERROR; - } + err = DB_ERROR; } } @@ -3651,7 +3628,7 @@ row_import_for_mysql( err = fil_open_single_table_tablespace( true, true, table->space, dict_tf_to_fsp_flags(table->flags), - table->name, filepath); + table->name, filepath, table); DBUG_EXECUTE_IF("ib_import_open_tablespace_failure", err = DB_TABLESPACE_NOT_FOUND;); diff --git a/storage/innobase/row/row0ins.cc b/storage/innobase/row/row0ins.cc index 44c9ac32d16..4f891e0f114 100644 --- a/storage/innobase/row/row0ins.cc +++ b/storage/innobase/row/row0ins.cc @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1996, 2014, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved. 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 @@ -2338,7 +2338,7 @@ row_ins_clust_index_entry_low( { btr_cur_t cursor; ulint* offsets = NULL; - dberr_t err; + dberr_t err = DB_SUCCESS; big_rec_t* big_rec = NULL; mtr_t mtr; mem_heap_t* offsets_heap = NULL; @@ -2361,9 +2361,16 @@ row_ins_clust_index_entry_low( the function will return in both low_match and up_match of the cursor sensible values */ - btr_cur_search_to_nth_level(index, 0, entry, PAGE_CUR_LE, mode, + err = btr_cur_search_to_nth_level(index, 0, entry, PAGE_CUR_LE, mode, &cursor, 0, __FILE__, __LINE__, &mtr); + if (err != DB_SUCCESS) { + index->table->is_encrypted = true; + index->table->ibd_file_missing = true; + mtr_commit(&mtr); + goto func_exit; + } + #ifdef UNIV_DEBUG { page_t* page = btr_cur_get_page(&cursor); @@ -2669,9 +2676,22 @@ row_ins_sec_index_entry_low( search_mode |= BTR_IGNORE_SEC_UNIQUE; } - btr_cur_search_to_nth_level(index, 0, entry, PAGE_CUR_LE, - search_mode, - &cursor, 0, __FILE__, __LINE__, &mtr); + err = btr_cur_search_to_nth_level(index, 0, entry, PAGE_CUR_LE, + search_mode, + &cursor, 0, __FILE__, __LINE__, &mtr); + + if (err != DB_SUCCESS) { + if (err == DB_ENCRYPTED_DECRYPT_FAILED) { + ib_push_warning(trx->mysql_thd, + DB_ENCRYPTED_DECRYPT_FAILED, + "Table %s is encrypted but encryption service or" + " used key_id is not available. " + " Can't continue reading table.", + index->table->name); + index->table->is_encrypted = true; + } + goto func_exit; + } if (cursor.flag == BTR_CUR_INSERT_TO_IBUF) { /* The insert was buffered during the search: we are done */ diff --git a/storage/innobase/row/row0merge.cc b/storage/innobase/row/row0merge.cc index c1d3e08beaa..ab2ea05b2f2 100644 --- a/storage/innobase/row/row0merge.cc +++ b/storage/innobase/row/row0merge.cc @@ -1431,6 +1431,13 @@ row_merge_read_clustered_index( row_ext_t* ext; page_cur_t* cur = btr_pcur_get_page_cur(&pcur); + /* Do not continue if table pages are still encrypted */ + if (old_table->is_encrypted || new_table->is_encrypted) { + err = DB_ENCRYPTED_DECRYPT_FAILED; + trx->error_key_num = 0; + goto func_exit; + } + page_cur_move_to_next(cur); if (page_cur_is_after_last(cur)) { @@ -3754,6 +3761,17 @@ row_merge_build_indexes( pct_cost = COST_READ_CLUSTERED_INDEX * 100 / (total_static_cost + total_dynamic_cost); + /* Do not continue if we can't encrypt table pages */ + if (old_table->is_encrypted || new_table->is_encrypted) { + error = DB_ENCRYPTED_DECRYPT_FAILED; + ib_push_warning(trx->mysql_thd, DB_ENCRYPTED_DECRYPT_FAILED, + "Table %s is encrypted but encryption service or" + " used key_id is not available. " + " Can't continue reading table.", + old_table->is_encrypted ? old_table->name : new_table->name); + goto func_exit; + } + /* Read clustered index of the table and create files for secondary index entries for merge sort */ diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc index 9ed14ffc9cb..d46e2b1c3ef 100644 --- a/storage/innobase/row/row0mysql.cc +++ b/storage/innobase/row/row0mysql.cc @@ -623,6 +623,8 @@ handle_new_error: case DB_FTS_INVALID_DOCID: case DB_INTERRUPTED: case DB_DICT_CHANGED: + case DB_TABLE_NOT_FOUND: + case DB_ENCRYPTED_DECRYPT_FAILED: if (savept) { /* Roll back the latest, possibly incomplete insertion or update */ @@ -1315,7 +1317,13 @@ row_insert_for_mysql( prebuilt->table->name); return(DB_TABLESPACE_NOT_FOUND); - + } else if (prebuilt->table->is_encrypted) { + ib_push_warning(trx, DB_ENCRYPTED_DECRYPT_FAILED, + "Table %s in tablespace %lu encrypted." + "However key management plugin or used key_id is not found or" + " used encryption algorithm or method does not match.", + prebuilt->table->name, prebuilt->table->space); + return(DB_ENCRYPTED_DECRYPT_FAILED); } else if (prebuilt->magic_n != ROW_PREBUILT_ALLOCATED) { fprintf(stderr, "InnoDB: Error: trying to free a corrupt\n" @@ -1710,6 +1718,13 @@ row_update_for_mysql( "InnoDB: how you can resolve the problem.\n", prebuilt->table->name); return(DB_ERROR); + } else if (prebuilt->table->is_encrypted) { + ib_push_warning(trx, DB_ENCRYPTED_DECRYPT_FAILED, + "Table %s in tablespace %lu encrypted." + "However key management plugin or used key_id is not found or" + " used encryption algorithm or method does not match.", + prebuilt->table->name, prebuilt->table->space); + return (DB_TABLE_NOT_FOUND); } if (UNIV_UNLIKELY(prebuilt->magic_n != ROW_PREBUILT_ALLOCATED)) { @@ -3919,6 +3934,19 @@ row_drop_table_for_mysql( goto funct_exit; } + /* If table is encrypted and table page encryption failed + mark this table read only. */ + if (table->is_encrypted) { + + if (table->can_be_evicted) { + dict_table_move_from_lru_to_non_lru(table); + } + + dict_table_close(table, TRUE, FALSE); + err = DB_READ_ONLY; + goto funct_exit; + } + /* Turn on this drop bit before we could release the dictionary latch */ table->to_be_dropped = true; diff --git a/storage/innobase/row/row0purge.cc b/storage/innobase/row/row0purge.cc index 8212a7b43e0..b26ba971a95 100644 --- a/storage/innobase/row/row0purge.cc +++ b/storage/innobase/row/row0purge.cc @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1997, 2013, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 1997, 2015, Oracle and/or its affiliates. All Rights Reserved. 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 @@ -84,7 +84,7 @@ row_purge_node_create( /***********************************************************//** Repositions the pcur in the purge node on the clustered index record, -if found. +if found. If the record is not found, close pcur. @return TRUE if the record was found */ static ibool @@ -95,11 +95,10 @@ row_purge_reposition_pcur( mtr_t* mtr) /*!< in: mtr */ { if (node->found_clust) { - ibool found; + ut_ad(node->validate_pcur()); - found = btr_pcur_restore_position(mode, &node->pcur, mtr); + node->found_clust = btr_pcur_restore_position(mode, &node->pcur, mtr); - return(found); } else { node->found_clust = row_search_on_row_ref( &node->pcur, mode, node->table, node->ref, mtr); @@ -109,6 +108,11 @@ row_purge_reposition_pcur( } } + /* Close the current cursor if we fail to position it correctly. */ + if (!node->found_clust) { + btr_pcur_close(&node->pcur); + } + return(node->found_clust); } @@ -182,7 +186,12 @@ func_exit: mem_heap_free(heap); } - btr_pcur_commit_specify_mtr(&node->pcur, &mtr); + /* Persistent cursor is closed if reposition fails. */ + if (node->found_clust) { + btr_pcur_commit_specify_mtr(&node->pcur, &mtr); + } else { + mtr_commit(&mtr); + } return(success); } @@ -251,7 +260,12 @@ row_purge_poss_sec( btr_pcur_get_rec(&node->pcur), &mtr, index, entry); - btr_pcur_commit_specify_mtr(&node->pcur, &mtr); + /* Persistent cursor is closed if reposition fails. */ + if (node->found_clust) { + btr_pcur_commit_specify_mtr(&node->pcur, &mtr); + } else { + mtr_commit(&mtr); + } return(can_delete); } @@ -831,6 +845,8 @@ row_purge_record_func( dict_index_t* clust_index; bool purged = true; + ut_ad(!node->found_clust); + clust_index = dict_table_get_first_index(node->table); node->index = dict_table_get_next_index(clust_index); @@ -986,3 +1002,52 @@ row_purge_step( return(thr); } + +#ifdef UNIV_DEBUG +/***********************************************************//** +Validate the persisent cursor. The purge node has two references +to the clustered index record - one via the ref member, and the +other via the persistent cursor. These two references must match +each other if the found_clust flag is set. +@return true if the stored copy of persistent cursor is consistent +with the ref member.*/ +bool +purge_node_t::validate_pcur() +{ + if (!found_clust) { + return(true); + } + + if (index == NULL) { + return(true); + } + + if (index->type == DICT_FTS) { + return(true); + } + + if (pcur.old_stored != BTR_PCUR_OLD_STORED) { + return(true); + } + + dict_index_t* clust_index = pcur.btr_cur.index; + + ulint* offsets = rec_get_offsets( + pcur.old_rec, clust_index, NULL, pcur.old_n_fields, &heap); + + /* Here we are comparing the purge ref record and the stored initial + part in persistent cursor. Both cases we store n_uniq fields of the + cluster index and so it is fine to do the comparison. We note this + dependency here as pcur and ref belong to different modules. */ + int st = cmp_dtuple_rec(ref, pcur.old_rec, offsets); + + if (st != 0) { + fprintf(stderr, "Purge node pcur validation failed\n"); + dtuple_print(stderr, ref); + rec_print(stderr, pcur.old_rec, clust_index); + return(false); + } + + return(true); +} +#endif /* UNIV_DEBUG */ diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc index b0e0c89b778..53ec30b1f42 100644 --- a/storage/innobase/row/row0sel.cc +++ b/storage/innobase/row/row0sel.cc @@ -1,7 +1,8 @@ /***************************************************************************** -Copyright (c) 1997, 2013, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 1997, 2015, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2008, Google Inc. +Copyright (c) 2015, MariaDB Corporation. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described @@ -3717,6 +3718,9 @@ row_search_for_mysql( return(DB_TABLESPACE_NOT_FOUND); + } else if (prebuilt->table->is_encrypted) { + + return(DB_ENCRYPTED_DECRYPT_FAILED); } else if (!prebuilt->index_usable) { return(DB_MISSING_HISTORY); @@ -4143,9 +4147,14 @@ wait_table_again: } else if (dtuple_get_n_fields(search_tuple) > 0) { - btr_pcur_open_with_no_init(index, search_tuple, mode, - BTR_SEARCH_LEAF, - pcur, 0, &mtr); + err = btr_pcur_open_with_no_init(index, search_tuple, mode, + BTR_SEARCH_LEAF, + pcur, 0, &mtr); + + if (err != DB_SUCCESS) { + rec = NULL; + goto lock_wait_or_error; + } pcur->trx_if_known = trx; @@ -4179,9 +4188,23 @@ wait_table_again: } } } else if (mode == PAGE_CUR_G || mode == PAGE_CUR_L) { - btr_pcur_open_at_index_side( + err = btr_pcur_open_at_index_side( mode == PAGE_CUR_G, index, BTR_SEARCH_LEAF, pcur, false, 0, &mtr); + + if (err != DB_SUCCESS) { + if (err == DB_ENCRYPTED_DECRYPT_FAILED) { + ib_push_warning(trx->mysql_thd, + DB_ENCRYPTED_DECRYPT_FAILED, + "Table %s is encrypted but encryption service or" + " used key_id is not available. " + " Can't continue reading table.", + prebuilt->table->name); + index->table->is_encrypted = true; + } + rec = NULL; + goto lock_wait_or_error; + } } rec_loop: @@ -4196,6 +4219,12 @@ rec_loop: /* PHASE 4: Look for matching records in a loop */ rec = btr_pcur_get_rec(pcur); + + if (!rec) { + err = DB_ENCRYPTED_DECRYPT_FAILED; + goto lock_wait_or_error; + } + ut_ad(!!page_rec_is_comp(rec) == comp); #ifdef UNIV_SEARCH_DEBUG /* @@ -5113,7 +5142,9 @@ lock_wait_or_error: /*-------------------------------------------------------------*/ - btr_pcur_store_position(pcur, &mtr); + if (rec) { + btr_pcur_store_position(pcur, &mtr); + } lock_table_wait: mtr_commit(&mtr); diff --git a/storage/innobase/ut/ut0ut.cc b/storage/innobase/ut/ut0ut.cc index 68446cc85ef..f19737aa123 100644 --- a/storage/innobase/ut/ut0ut.cc +++ b/storage/innobase/ut/ut0ut.cc @@ -824,6 +824,8 @@ ut_strerr( return("Too many words in a FTS phrase or proximity search"); case DB_TOO_BIG_FOR_REDO: return("BLOB record length is greater than 10%% of redo log"); + case DB_ENCRYPTED_DECRYPT_FAILED: + return("Table is encrypted but decrypt failed."); /* do not add default: in order to produce a warning if new code is added to the enum but not added here */ diff --git a/storage/maria/ma_blockrec.c b/storage/maria/ma_blockrec.c index 4a5c48a5891..2f2558d22c0 100644 --- a/storage/maria/ma_blockrec.c +++ b/storage/maria/ma_blockrec.c @@ -5187,8 +5187,7 @@ my_bool _ma_cmp_block_unique(MARIA_HA *info, MARIA_UNIQUEDEF *def, Don't allocate more than 16K on the stack to ensure we don't get stack overflow. */ - if (!(old_record= my_safe_alloca(info->s->base.reclength, - MARIA_MAX_RECORD_ON_STACK))) + if (!(old_record= my_safe_alloca(info->s->base.reclength))) DBUG_RETURN(1); /* Don't let the compare destroy blobs that may be in use */ @@ -5210,8 +5209,7 @@ my_bool _ma_cmp_block_unique(MARIA_HA *info, MARIA_UNIQUEDEF *def, info->rec_buff_size= org_rec_buff_size; } DBUG_PRINT("exit", ("result: %d", error)); - my_safe_afree(old_record, info->s->base.reclength, - MARIA_MAX_RECORD_ON_STACK); + my_safe_afree(old_record, info->s->base.reclength); DBUG_RETURN(error != 0); } diff --git a/storage/maria/ma_check_standalone.h b/storage/maria/ma_check_standalone.h index c240ca056e7..6626a62c4ef 100644 --- a/storage/maria/ma_check_standalone.h +++ b/storage/maria/ma_check_standalone.h @@ -30,7 +30,7 @@ static unsigned int no_key() struct encryption_service_st encryption_handler= { - no_key, 0, 0, 0, 0, 0 + no_key, 0, 0, 0, 0, 0, 0 }; int encryption_scheme_encrypt(const unsigned char* src __attribute__((unused)), diff --git a/storage/maria/ma_dynrec.c b/storage/maria/ma_dynrec.c index eb6352b7a87..2e17a95f390 100644 --- a/storage/maria/ma_dynrec.c +++ b/storage/maria/ma_dynrec.c @@ -250,8 +250,7 @@ my_bool _ma_write_blob_record(MARIA_HA *info, const uchar *record) MARIA_DYN_DELETE_BLOCK_HEADER+1); reclength= (info->s->base.pack_reclength + _ma_calc_total_blob_length(info,record)+ extra); - if (!(rec_buff=(uchar*) my_safe_alloca(reclength, - MARIA_MAX_RECORD_ON_STACK))) + if (!(rec_buff=(uchar*) my_safe_alloca(reclength))) { my_errno= HA_ERR_OUT_OF_MEM; /* purecov: inspected */ return(1); @@ -265,7 +264,7 @@ my_bool _ma_write_blob_record(MARIA_HA *info, const uchar *record) error= write_dynamic_record(info, rec_buff+ALIGN_SIZE(MARIA_MAX_DYN_BLOCK_HEADER), reclength2); - my_safe_afree(rec_buff, reclength, MARIA_MAX_RECORD_ON_STACK); + my_safe_afree(rec_buff, reclength); return(error != 0); } @@ -289,8 +288,7 @@ my_bool _ma_update_blob_record(MARIA_HA *info, MARIA_RECORD_POS pos, return 1; } #endif - if (!(rec_buff=(uchar*) my_safe_alloca(reclength, - MARIA_MAX_RECORD_ON_STACK))) + if (!(rec_buff=(uchar*) my_safe_alloca(reclength))) { my_errno= HA_ERR_OUT_OF_MEM; /* purecov: inspected */ return(1); @@ -300,7 +298,7 @@ my_bool _ma_update_blob_record(MARIA_HA *info, MARIA_RECORD_POS pos, error=update_dynamic_record(info,pos, rec_buff+ALIGN_SIZE(MARIA_MAX_DYN_BLOCK_HEADER), reclength); - my_safe_afree(rec_buff, reclength, MARIA_MAX_RECORD_ON_STACK); + my_safe_afree(rec_buff, reclength); return(error != 0); } @@ -1555,8 +1553,7 @@ my_bool _ma_cmp_dynamic_unique(MARIA_HA *info, MARIA_UNIQUEDEF *def, my_bool error; DBUG_ENTER("_ma_cmp_dynamic_unique"); - if (!(old_record= my_safe_alloca(info->s->base.reclength, - MARIA_MAX_RECORD_ON_STACK))) + if (!(old_record= my_safe_alloca(info->s->base.reclength))) DBUG_RETURN(1); /* Don't let the compare destroy blobs that may be in use */ @@ -1577,8 +1574,7 @@ my_bool _ma_cmp_dynamic_unique(MARIA_HA *info, MARIA_UNIQUEDEF *def, info->rec_buff= old_rec_buff; info->rec_buff_size= old_rec_buff_size; } - my_safe_afree(old_record, info->s->base.reclength, - MARIA_MAX_RECORD_ON_STACK); + my_safe_afree(old_record, info->s->base.reclength); DBUG_RETURN(error); } @@ -1613,8 +1609,7 @@ my_bool _ma_cmp_dynamic_record(register MARIA_HA *info, { buffer_length= (info->s->base.pack_reclength + _ma_calc_total_blob_length(info,record)); - if (!(buffer=(uchar*) my_safe_alloca(buffer_length, - MARIA_MAX_RECORD_ON_STACK))) + if (!(buffer=(uchar*) my_safe_alloca(buffer_length))) DBUG_RETURN(1); } reclength= _ma_rec_pack(info,buffer,record); @@ -1666,7 +1661,7 @@ my_bool _ma_cmp_dynamic_record(register MARIA_HA *info, error= 0; err: if (buffer != info->rec_buff) - my_safe_afree(buffer, buffer_length, MARIA_MAX_RECORD_ON_STACK); + my_safe_afree(buffer, buffer_length); DBUG_PRINT("exit", ("result: %d", error)); DBUG_RETURN(error); } diff --git a/storage/maria/ma_pagecache.c b/storage/maria/ma_pagecache.c index 8315c3136c5..bba99703119 100644 --- a/storage/maria/ma_pagecache.c +++ b/storage/maria/ma_pagecache.c @@ -833,17 +833,23 @@ ulong init_pagecache(PAGECACHE *pagecache, size_t use_mem, Allocate memory for blocks, hash_links and hash entries; For each block 2 hash links are allocated */ - if (my_multi_malloc(MYF(MY_ZEROFILL), - &pagecache->block_root, blocks * sizeof(PAGECACHE_BLOCK_LINK), - &pagecache->hash_root, - sizeof(PAGECACHE_HASH_LINK*) * pagecache->hash_entries, - &pagecache->hash_link_root, - hash_links * sizeof(PAGECACHE_HASH_LINK), - &pagecache->changed_blocks, - sizeof(PAGECACHE_BLOCK_LINK*) * changed_blocks_hash_size, - &pagecache->file_blocks, - sizeof(PAGECACHE_BLOCK_LINK*) * changed_blocks_hash_size, - NullS)) + if (my_multi_malloc_large(MYF(MY_ZEROFILL), + &pagecache->block_root, + (ulonglong) (blocks * + sizeof(PAGECACHE_BLOCK_LINK)), + &pagecache->hash_root, + (ulonglong) (sizeof(PAGECACHE_HASH_LINK*) * + pagecache->hash_entries), + &pagecache->hash_link_root, + (ulonglong) (hash_links * + sizeof(PAGECACHE_HASH_LINK)), + &pagecache->changed_blocks, + (ulonglong) (sizeof(PAGECACHE_BLOCK_LINK*) * + changed_blocks_hash_size), + &pagecache->file_blocks, + (ulonglong) (sizeof(PAGECACHE_BLOCK_LINK*) * + changed_blocks_hash_size), + NullS)) break; my_large_free(pagecache->block_mem); pagecache->block_mem= 0; diff --git a/storage/maria/maria_pack.c b/storage/maria/maria_pack.c index 7eca9e14e93..280c5ff8f0a 100644 --- a/storage/maria/maria_pack.c +++ b/storage/maria/maria_pack.c @@ -861,7 +861,7 @@ static int get_statistic(PACK_MRG_INFO *mrg,HUFF_COUNTS *huff_counts) reclength= mrg->file[0]->s->base.reclength; null_bytes= mrg->file[0]->s->base.null_bytes; - record=(uchar*) my_safe_alloca(reclength, MARIA_MAX_RECORD_ON_STACK); + record=(uchar*) my_safe_alloca(reclength); end_count=huff_counts+mrg->file[0]->s->base.fields; record_count=0; glob_crc=0; max_blob_length=0; @@ -1145,7 +1145,7 @@ static int get_statistic(PACK_MRG_INFO *mrg,HUFF_COUNTS *huff_counts) mrg->records=record_count; mrg->max_blob_length=max_blob_length; - my_safe_afree(record, reclength, MARIA_MAX_RECORD_ON_STACK); + my_safe_afree(record, reclength); DBUG_RETURN(error != HA_ERR_END_OF_FILE); } @@ -2415,8 +2415,7 @@ static int compress_maria_file(PACK_MRG_INFO *mrg, HUFF_COUNTS *huff_counts) DBUG_ENTER("compress_maria_file"); /* Allocate a buffer for the records (excluding blobs). */ - if (!(record=(uchar*) my_safe_alloca(isam_file->s->base.reclength, - MARIA_MAX_RECORD_ON_STACK))) + if (!(record=(uchar*) my_safe_alloca(isam_file->s->base.reclength))) return -1; end_count=huff_counts+isam_file->s->base.fields; @@ -2779,8 +2778,7 @@ static int compress_maria_file(PACK_MRG_INFO *mrg, HUFF_COUNTS *huff_counts) if (verbose >= 2) printf("wrote %s records.\n", llstr((longlong) record_count, llbuf)); - my_safe_afree(record, isam_file->s->base.reclength, - MARIA_MAX_RECORD_ON_STACK); + my_safe_afree(record, isam_file->s->base.reclength); mrg->ref_length=max_pack_length; mrg->min_pack_length=max_record_length ? min_record_length : 0; mrg->max_pack_length=max_record_length; diff --git a/storage/mroonga/CMakeLists.txt b/storage/mroonga/CMakeLists.txt index 3b88d942165..9aa5a4bd990 100644 --- a/storage/mroonga/CMakeLists.txt +++ b/storage/mroonga/CMakeLists.txt @@ -132,31 +132,6 @@ include(${MRN_SOURCE_DIR}/build/cmake_modules/ReadFileList.cmake) set(MRN_C_COMPILE_FLAGS "") set(MRN_CXX_COMPILE_FLAGS "") -macro(mrn_check_cflag flag) - string(REGEX REPLACE "[-=]" "_" temporary_variable_name ${flag}) - string(TOUPPER "${temporary_variable_name}" temporary_variable_name) - set(temporary_variable_name "CFLAG${temporary_variable_name}") - check_c_compiler_flag(${flag} ${temporary_variable_name}) - if(${temporary_variable_name}) - set(MRN_C_COMPILE_FLAGS "${MRN_C_COMPILE_FLAGS} ${flag}") - endif() -endmacro() - -macro(mrn_check_cxxflag flag) - string(REGEX REPLACE "[-=]" "_" temporary_variable_name ${flag}) - string(TOUPPER "${temporary_variable_name}" temporary_variable_name) - set(temporary_variable_name "CXXFLAG${temporary_variable_name}") - check_cxx_compiler_flag(${flag} ${temporary_variable_name}) - if(${temporary_variable_name}) - set(MRN_CXX_COMPILE_FLAGS "${MRN_CXX_COMPILE_FLAGS} ${flag}") - endif() -endmacro() - -macro(mrn_build_flag flag) - mrn_check_cflag(${flag}) - mrn_check_cxxflag(${flag}) -endmacro() - if(MRN_BUNDLED) set(MRN_RELATIVE_DIR_PREFIX "${MRN_SOURCE_DIR}/") else() @@ -360,19 +335,15 @@ else() endif() if(CMAKE_COMPILER_IS_GNUCXX) - mrn_build_flag("-Wall") - mrn_build_flag("-Wextra") - mrn_build_flag("-Wno-unused-parameter") - mrn_build_flag("-Wno-strict-aliasing") - mrn_build_flag("-Wno-deprecated") - mrn_check_cxxflag("-fno-implicit-templates") - if(("${MYSQL_VARIANT}" STREQUAL "MariaDB") OR - ("${MYSQL_VARIANT}" STREQUAL "MySQL" AND - ${MYSQL_VERSION} VERSION_LESS "5.7.0")) - mrn_check_cxxflag("-fno-exceptions") - mrn_check_cxxflag("-fno-rtti") - endif() - mrn_check_cxxflag("-felide-constructors") + MY_CHECK_AND_SET_COMPILER_FLAG("-Wall") + MY_CHECK_AND_SET_COMPILER_FLAG("-Wextra") + MY_CHECK_AND_SET_COMPILER_FLAG("-Wno-unused-parameter") + MY_CHECK_AND_SET_COMPILER_FLAG("-Wno-strict-aliasing") + MY_CHECK_AND_SET_COMPILER_FLAG("-Wno-deprecated") + MY_CHECK_AND_SET_COMPILER_FLAG("-fno-implicit-templates") + MY_CHECK_AND_SET_COMPILER_FLAG("-fno-exceptions") + MY_CHECK_AND_SET_COMPILER_FLAG("-fno-rtti") + MY_CHECK_AND_SET_COMPILER_FLAG("-felide-constructors") endif() set_source_files_properties(${MRN_SOURCES} PROPERTIES COMPILE_FLAGS "${MYSQL_CFLAGS} ${MRN_CXX_COMPILE_FLAGS}") @@ -392,11 +363,9 @@ else() endif() if(GROONGA_NORMALIZER_MYSQL_FOUND) - set_property(TARGET mroonga APPEND PROPERTY - COMPILE_DEFINITIONS "WITH_GROONGA_NORMALIZER_MYSQL=1") + add_definitions("-DWITH_GROONGA_NORMALIZER_MYSQL=1") if(MRN_GROONGA_NORMALIZER_MYSQL_EMBED) - set_property(TARGET mroonga APPEND PROPERTY - COMPILE_DEFINITIONS "MRN_GROONGA_NORMALIZER_MYSQL_EMBEDDED") + add_definitions("-DMRN_GROONGA_NORMALIZER_MYSQL_EMBEDDED") else() set_property(TARGET mroonga APPEND PROPERTY COMPILE_DEFINITIONS "GROONGA_NORMALIZER_MYSQL_PLUGIN_NAME=\"normalizers/mysql\"") @@ -404,8 +373,7 @@ if(GROONGA_NORMALIZER_MYSQL_FOUND) endif() if(MRN_GROONGA_EMBED) - set_property(TARGET mroonga APPEND PROPERTY - COMPILE_DEFINITIONS "MRN_GROONGA_EMBEDDED") + add_definitions("-DMRN_GROONGA_EMBEDDED") endif() set(MRN_DEFAULT_PARSER "" CACHE STRING diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/performance_schema-master.opt b/storage/mroonga/mysql-test/mroonga/wrapper/t/performance_schema-master.opt index 611d08f0c78..d2ed32ddf34 100644 --- a/storage/mroonga/mysql-test/mroonga/wrapper/t/performance_schema-master.opt +++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/performance_schema-master.opt @@ -1 +1 @@ ---performance-schema +--loose-performance-schema diff --git a/storage/mroonga/vendor/groonga/CMakeLists.txt b/storage/mroonga/vendor/groonga/CMakeLists.txt index ebe7f6b7fe5..625a65b48d0 100644 --- a/storage/mroonga/vendor/groonga/CMakeLists.txt +++ b/storage/mroonga/vendor/groonga/CMakeLists.txt @@ -143,59 +143,34 @@ if(CMAKE_COMPILER_IS_GNUCC) endif() -macro(check_cflag flag) - string(REGEX REPLACE "[-=]" "_" temporary_variable_name ${flag}) - string(TOUPPER "${temporary_variable_name}" temporary_variable_name) - set(temporary_variable_name "CFLAG${temporary_variable_name}") - check_c_compiler_flag(${flag} ${temporary_variable_name}) - if(${temporary_variable_name}) - set(GRN_C_COMPILE_FLAGS "${GRN_C_COMPILE_FLAGS} ${flag}") - endif() -endmacro() - -macro(check_cxxflag flag) - string(REGEX REPLACE "[-=]" "_" temporary_variable_name ${flag}) - string(TOUPPER "${temporary_variable_name}" temporary_variable_name) - set(temporary_variable_name "CXXFLAG${temporary_variable_name}") - check_cxx_compiler_flag(${flag} ${temporary_variable_name}) - if(${temporary_variable_name}) - set(GRN_CXX_COMPILE_FLAGS "${GRN_CXX_COMPILE_FLAGS} ${flag}") - endif() -endmacro() - -macro(check_build_flag flag) - check_cflag(${flag}) - check_cxxflag(${flag}) -endmacro() - if(CMAKE_COMPILER_IS_GNUCXX) - check_build_flag("-Wall") - check_build_flag("-Wextra") - check_build_flag("-Wno-unused-but-set-variable") - check_build_flag("-Wno-unused-parameter") - check_build_flag("-Wno-sign-compare") - check_cflag("-Wno-pointer-sign") - check_build_flag("-Wno-missing-field-initializers") - check_build_flag("-Wformat=2") - check_build_flag("-Wstrict-aliasing=2") - check_build_flag("-fno-strict-aliasing") - check_build_flag("-Wdisabled-optimization") - check_build_flag("-Wfloat-equal") - check_build_flag("-Wpointer-arith") - check_cflag("-Wdeclaration-after-statement") - check_cflag("-Wbad-function-cast") - check_build_flag("-Wcast-align") - # check_build_flag("-Wredundant-decls") - check_build_flag("-Wwrite-strings") - check_cxxflag("-fexceptions") - check_cxxflag("-fimplicit-templates") - check_build_flag("-Wno-clobbered") + MY_CHECK_AND_SET_COMPILER_FLAG("-Wall") + MY_CHECK_AND_SET_COMPILER_FLAG("-Wextra") + MY_CHECK_AND_SET_COMPILER_FLAG("-Wno-unused-but-set-variable") + MY_CHECK_AND_SET_COMPILER_FLAG("-Wno-unused-parameter") + MY_CHECK_AND_SET_COMPILER_FLAG("-Wno-sign-compare") + MY_CHECK_AND_SET_COMPILER_FLAG("-Wno-pointer-sign") + MY_CHECK_AND_SET_COMPILER_FLAG("-Wno-missing-field-initializers") + MY_CHECK_AND_SET_COMPILER_FLAG("-Wformat=2") + MY_CHECK_AND_SET_COMPILER_FLAG("-Wstrict-aliasing=2") + MY_CHECK_AND_SET_COMPILER_FLAG("-fno-strict-aliasing") + MY_CHECK_AND_SET_COMPILER_FLAG("-Wdisabled-optimization") + MY_CHECK_AND_SET_COMPILER_FLAG("-Wfloat-equal") + MY_CHECK_AND_SET_COMPILER_FLAG("-Wpointer-arith") + MY_CHECK_AND_SET_COMPILER_FLAG("-Wdeclaration-after-statement") + MY_CHECK_AND_SET_COMPILER_FLAG("-Wbad-function-cast") + MY_CHECK_AND_SET_COMPILER_FLAG("-Wcast-align") + #MY_CHECK_AND_SET_COMPILER_FLAG("-Wredundant-decls") + MY_CHECK_AND_SET_COMPILER_FLAG("-Wwrite-strings") + MY_CHECK_AND_SET_COMPILER_FLAG("-fexceptions") + MY_CHECK_AND_SET_COMPILER_FLAG("-fimplicit-templates") + MY_CHECK_AND_SET_COMPILER_FLAG("-Wno-clobbered") endif() if(NOT DEFINED CMAKE_C_COMPILE_OPTIONS_PIC) # For old CMake if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_CLANGCXX) - check_build_flag("-fPIC") + MY_CHECK_AND_SET_COMPILER_FLAG("-fPIC") endif() endif() @@ -366,7 +341,9 @@ endif() set(GRN_WITH_LZ4 "auto" CACHE STRING "Support data compression by LZ4.") if(NOT ${GRN_WITH_LZ4} STREQUAL "no") - pkg_check_modules(LIBLZ4 liblz4) + if(NOT DEFINED LIBLZ4_FOUND) + pkg_check_modules(LIBLZ4 liblz4) + endif() if(LIBLZ4_FOUND) set(GRN_WITH_LZ4 TRUE) else() @@ -417,7 +394,9 @@ endif() set(GRN_WITH_KYTEA "auto" CACHE STRING "use KyTea for morphological analysis") if(NOT ${GRN_WITH_KYTEA} STREQUAL "no") - pkg_check_modules(KYTEA kytea) + if(NOT DEFINED KYTEA_FOUND) + pkg_check_modules(KYTEA kytea) + endif() if(KYTEA_FOUND) set(GRN_WITH_KYTEA TRUE) else() @@ -465,7 +444,9 @@ endif() set(GRN_WITH_ZEROMQ "auto" CACHE STRING "use ZeroMQ for suggestion") if(NOT ${GRN_WITH_ZEROMQ} STREQUAL "no") - pkg_check_modules(ZEROMQ libzmq) + if(NOT DEFINED ZEROMQ_FOUND) + pkg_check_modules(ZEROMQ libzmq) + endif() if(ZEROMQ_FOUND) set(GRN_WITH_ZEROMQ TRUE) else() @@ -508,7 +489,9 @@ endif() set(GRN_WITH_MESSAGE_PACK "auto" CACHE STRING "use MessagePack for suggestion") if(NOT ${GRN_WITH_MESSAGE_PACK} STREQUAL "no") - pkg_check_modules(MESSAGE_PACK msgpack) + if(NOT DEFINED MESSAGE_PACK_FOUND) + pkg_check_modules(MESSAGE_PACK msgpack) + endif() if(MESSAGE_PACK_FOUND) set(GRN_WITH_MESSAGE_PACK TRUE) else() diff --git a/storage/oqgraph/CMakeLists.txt b/storage/oqgraph/CMakeLists.txt index 1cb5c828d92..772a8f61bd5 100644 --- a/storage/oqgraph/CMakeLists.txt +++ b/storage/oqgraph/CMakeLists.txt @@ -5,6 +5,7 @@ MESSAGE(STATUS "Configuring OQGraph") FIND_PACKAGE(Boost 1.40.0) IF(NOT Boost_FOUND) MESSAGE(STATUS "Boost not found. OQGraph will not be compiled") + SET(OQGRAPH_OK 0 CACHE INTERNAL "") RETURN() ENDIF() INCLUDE_DIRECTORIES(BEFORE ${Boost_INCLUDE_DIRS}) @@ -12,6 +13,7 @@ INCLUDE_DIRECTORIES(BEFORE ${Boost_INCLUDE_DIRS}) FIND_PACKAGE(Judy) IF(NOT Judy_FOUND) MESSAGE(STATUS "Judy not found. OQGraph will not be compiled") + SET(OQGRAPH_OK 0 CACHE INTERNAL "") RETURN() ENDIF() INCLUDE_DIRECTORIES(${Judy_INCLUDE_DIR}) @@ -19,9 +21,9 @@ INCLUDE_DIRECTORIES(${Judy_INCLUDE_DIR}) IF(MSVC) # # lp:756966 OQGRAPH on Win64 does not compile # IF (CMAKE_SIZEOF_VOID_P EQUAL 8) -# SET(BOOST_OK 0) +# SET(OQGRAPH_OK 0 CACHE INTERNAL "") # ELSE() - SET(BOOST_OK 1) + SET(OQGRAPH_OK 1 CACHE INTERNAL "") # ENDIF() ELSE() # See if that works. On old gcc it'll fail because of -fno-rtti @@ -33,10 +35,10 @@ CHECK_CXX_SOURCE_COMPILES( #include <boost/config.hpp> #include <boost/property_map/property_map.hpp> int main() { return 0; } -" BOOST_OK) +" OQGRAPH_OK) ENDIF() -IF(BOOST_OK) +IF(OQGRAPH_OK) ADD_DEFINITIONS(-DHAVE_OQGRAPH) IF(MSVC) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc" PARENT_SCOPE) @@ -50,7 +52,6 @@ IF(BOOST_OK) ENDIF(MSVC) ADD_DEFINITIONS(-DBOOST_NO_RTTI=1 -DBOOST_NO_TYPEID=1 -DBOOST_DISABLE_ASSERTS=1) - MESSAGE(STATUS "OQGraph OK") MYSQL_ADD_PLUGIN(oqgraph ha_oqgraph.cc graphcore.cc graphcore-graph.cc oqgraph_shim.cc oqgraph_thunk.cc oqgraph_judy.cc STORAGE_ENGINE @@ -58,9 +59,11 @@ IF(BOOST_OK) RECOMPILE_FOR_EMBEDDED COMPONENT oqgraph-engine LINK_LIBRARIES ${Judy_LIBRARIES}) -ELSE(BOOST_OK) +ELSE(OQGRAPH_OK) MESSAGE(STATUS "Requisites for OQGraph not met. OQGraph will not be compiled") -ENDIF(BOOST_OK) +ENDIF(OQGRAPH_OK) ENDFUNCTION() -CHECK_OQGRAPH() +IF(NOT DEFINED OQGRAPH_OK) + CHECK_OQGRAPH() +ENDIF() diff --git a/storage/perfschema/ha_perfschema.cc b/storage/perfschema/ha_perfschema.cc index 81592c85d0c..29e74271670 100644 --- a/storage/perfschema/ha_perfschema.cc +++ b/storage/perfschema/ha_perfschema.cc @@ -225,7 +225,7 @@ maria_declare_plugin(perfschema) 0x0001, pfs_status_vars, NULL, - "5.6.25", + "5.6.26", MariaDB_PLUGIN_MATURITY_STABLE } maria_declare_plugin_end; diff --git a/storage/perfschema/pfs_account.cc b/storage/perfschema/pfs_account.cc index 3eff670f44d..4e3a6d8d1d3 100644 --- a/storage/perfschema/pfs_account.cc +++ b/storage/perfschema/pfs_account.cc @@ -69,8 +69,8 @@ int init_account(const PFS_global_param *param) if (account_max > 0) { - account_array= PFS_MALLOC_ARRAY(account_max, PFS_account, - MYF(MY_ZEROFILL)); + account_array= PFS_MALLOC_ARRAY(account_max, sizeof(PFS_account), PFS_account, + MYF(MY_ZEROFILL)); if (unlikely(account_array == NULL)) return 1; } diff --git a/storage/perfschema/pfs_con_slice.cc b/storage/perfschema/pfs_con_slice.cc index 263f25c1c08..bd449df0a4b 100644 --- a/storage/perfschema/pfs_con_slice.cc +++ b/storage/perfschema/pfs_con_slice.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. 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 @@ -39,7 +39,8 @@ PFS_connection_slice::alloc_waits_slice(uint sizing) if (sizing > 0) { - slice= PFS_MALLOC_ARRAY(sizing, PFS_single_stat, MYF(MY_ZEROFILL)); + slice= PFS_MALLOC_ARRAY(sizing, sizeof(PFS_single_stat), PFS_single_stat, + MYF(MY_ZEROFILL)); if (unlikely(slice == NULL)) return NULL; @@ -58,7 +59,8 @@ PFS_connection_slice::alloc_stages_slice(uint sizing) if (sizing > 0) { - slice= PFS_MALLOC_ARRAY(sizing, PFS_stage_stat, MYF(MY_ZEROFILL)); + slice= PFS_MALLOC_ARRAY(sizing, sizeof(PFS_stage_stat), PFS_stage_stat, + MYF(MY_ZEROFILL)); if (unlikely(slice == NULL)) return NULL; @@ -77,7 +79,8 @@ PFS_connection_slice::alloc_statements_slice(uint sizing) if (sizing > 0) { - slice= PFS_MALLOC_ARRAY(sizing, PFS_statement_stat, MYF(MY_ZEROFILL)); + slice= PFS_MALLOC_ARRAY(sizing, sizeof(PFS_statement_stat), PFS_statement_stat, + MYF(MY_ZEROFILL)); if (unlikely(slice == NULL)) return NULL; diff --git a/storage/perfschema/pfs_digest.cc b/storage/perfschema/pfs_digest.cc index 6c0b41908e1..0a01ee08993 100644 --- a/storage/perfschema/pfs_digest.cc +++ b/storage/perfschema/pfs_digest.cc @@ -35,7 +35,7 @@ #include "sql_string.h" #include <string.h> -ulong digest_max= 0; +size_t digest_max= 0; ulong digest_lost= 0; /** EVENTS_STATEMENTS_HISTORY_LONG circular buffer. */ @@ -59,8 +59,6 @@ static bool digest_hash_inited= false; */ int init_digest(const PFS_global_param *param) { - unsigned int index; - /* Allocate memory for statements_digest_stat_array based on performance_schema_digests_size values @@ -75,6 +73,7 @@ int init_digest(const PFS_global_param *param) statements_digest_stat_array= PFS_MALLOC_ARRAY(digest_max, + sizeof(PFS_statements_digest_stat), PFS_statements_digest_stat, MYF(MY_ZEROFILL)); @@ -86,8 +85,12 @@ int init_digest(const PFS_global_param *param) if (pfs_max_digest_length > 0) { + /* Size of each digest array. */ + size_t digest_memory_size= pfs_max_digest_length * sizeof(unsigned char); + statements_digest_token_array= - PFS_MALLOC_ARRAY(digest_max * pfs_max_digest_length, + PFS_MALLOC_ARRAY(digest_max, + digest_memory_size, unsigned char, MYF(MY_ZEROFILL)); @@ -98,7 +101,7 @@ int init_digest(const PFS_global_param *param) } } - for (index= 0; index < digest_max; index++) + for (size_t index= 0; index < digest_max; index++) { statements_digest_stat_array[index].reset_data(statements_digest_token_array + index * pfs_max_digest_length, pfs_max_digest_length); @@ -336,8 +339,6 @@ void PFS_statements_digest_stat::reset_index(PFS_thread *thread) void reset_esms_by_digest() { - uint index; - if (statements_digest_stat_array == NULL) return; @@ -346,7 +347,7 @@ void reset_esms_by_digest() return; /* Reset statements_digest_stat_array. */ - for (index= 0; index < digest_max; index++) + for (size_t index= 0; index < digest_max; index++) { statements_digest_stat_array[index].reset_index(thread); statements_digest_stat_array[index].reset_data(statements_digest_token_array + index * pfs_max_digest_length, pfs_max_digest_length); diff --git a/storage/perfschema/pfs_digest.h b/storage/perfschema/pfs_digest.h index 9d021737c44..76d6c33d984 100644 --- a/storage/perfschema/pfs_digest.h +++ b/storage/perfschema/pfs_digest.h @@ -27,7 +27,7 @@ #include "sql_digest.h" extern bool flag_statements_digest; -extern ulong digest_max; +extern size_t digest_max; extern ulong digest_lost; struct PFS_thread; diff --git a/storage/perfschema/pfs_events_stages.cc b/storage/perfschema/pfs_events_stages.cc index 282071f830e..7351c95c9cb 100644 --- a/storage/perfschema/pfs_events_stages.cc +++ b/storage/perfschema/pfs_events_stages.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. 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 @@ -59,8 +59,8 @@ int init_events_stages_history_long(uint events_stages_history_long_sizing) return 0; events_stages_history_long_array= - PFS_MALLOC_ARRAY(events_stages_history_long_size, PFS_events_stages, - MYF(MY_ZEROFILL)); + PFS_MALLOC_ARRAY(events_stages_history_long_size, sizeof(PFS_events_stages), + PFS_events_stages, MYF(MY_ZEROFILL)); return (events_stages_history_long_array ? 0 : 1); } diff --git a/storage/perfschema/pfs_events_statements.cc b/storage/perfschema/pfs_events_statements.cc index dc34755d747..cecd0167c38 100644 --- a/storage/perfschema/pfs_events_statements.cc +++ b/storage/perfschema/pfs_events_statements.cc @@ -30,7 +30,7 @@ #include "pfs_atomic.h" #include "m_string.h" -ulong events_statements_history_long_size= 0; +size_t events_statements_history_long_size= 0; /** Consumer flag for table EVENTS_STATEMENTS_CURRENT. */ bool flag_events_statements_current= false; /** Consumer flag for table EVENTS_STATEMENTS_HISTORY. */ @@ -50,9 +50,8 @@ static unsigned char *h_long_stmts_digest_token_array= NULL; Initialize table EVENTS_STATEMENTS_HISTORY_LONG. @param events_statements_history_long_sizing table sizing */ -int init_events_statements_history_long(uint events_statements_history_long_sizing) +int init_events_statements_history_long(size_t events_statements_history_long_sizing) { - uint index; events_statements_history_long_size= events_statements_history_long_sizing; events_statements_history_long_full= false; PFS_atomic::store_u32(&events_statements_history_long_index, 0); @@ -61,8 +60,8 @@ int init_events_statements_history_long(uint events_statements_history_long_sizi return 0; events_statements_history_long_array= - PFS_MALLOC_ARRAY(events_statements_history_long_size, PFS_events_statements, - MYF(MY_ZEROFILL)); + PFS_MALLOC_ARRAY(events_statements_history_long_size, sizeof(PFS_events_statements), + PFS_events_statements, MYF(MY_ZEROFILL)); if (events_statements_history_long_array == NULL) { @@ -72,8 +71,11 @@ int init_events_statements_history_long(uint events_statements_history_long_sizi if (pfs_max_digest_length > 0) { + /* Size of each digest token array. */ + size_t digest_text_size= pfs_max_digest_length * sizeof(unsigned char); + h_long_stmts_digest_token_array= - PFS_MALLOC_ARRAY(events_statements_history_long_size * pfs_max_digest_length, + PFS_MALLOC_ARRAY(events_statements_history_long_size, digest_text_size, unsigned char, MYF(MY_ZEROFILL)); if (h_long_stmts_digest_token_array == NULL) { @@ -82,7 +84,7 @@ int init_events_statements_history_long(uint events_statements_history_long_sizi } } - for (index= 0; index < events_statements_history_long_size; index++) + for (size_t index= 0; index < events_statements_history_long_size; index++) { events_statements_history_long_array[index].m_digest_storage.reset(h_long_stmts_digest_token_array + index * pfs_max_digest_length, pfs_max_digest_length); diff --git a/storage/perfschema/pfs_events_statements.h b/storage/perfschema/pfs_events_statements.h index 3637e4ca764..b1e303e7021 100644 --- a/storage/perfschema/pfs_events_statements.h +++ b/storage/perfschema/pfs_events_statements.h @@ -106,9 +106,9 @@ extern bool flag_events_statements_history_long; extern bool events_statements_history_long_full; extern volatile uint32 events_statements_history_long_index; extern PFS_events_statements *events_statements_history_long_array; -extern ulong events_statements_history_long_size; +extern size_t events_statements_history_long_size; -int init_events_statements_history_long(uint events_statements_history_long_sizing); +int init_events_statements_history_long(size_t events_statements_history_long_sizing); void cleanup_events_statements_history_long(); void reset_events_statements_current(); diff --git a/storage/perfschema/pfs_events_waits.cc b/storage/perfschema/pfs_events_waits.cc index 2799550c81d..1120ac2a445 100644 --- a/storage/perfschema/pfs_events_waits.cc +++ b/storage/perfschema/pfs_events_waits.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. 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 @@ -63,8 +63,8 @@ int init_events_waits_history_long(uint events_waits_history_long_sizing) return 0; events_waits_history_long_array= - PFS_MALLOC_ARRAY(events_waits_history_long_size, PFS_events_waits, - MYF(MY_ZEROFILL)); + PFS_MALLOC_ARRAY(events_waits_history_long_size, sizeof(PFS_events_waits), + PFS_events_waits, MYF(MY_ZEROFILL)); return (events_waits_history_long_array ? 0 : 1); } diff --git a/storage/perfschema/pfs_global.cc b/storage/perfschema/pfs_global.cc index 0744bbab7d5..bd346f2b235 100644 --- a/storage/perfschema/pfs_global.cc +++ b/storage/perfschema/pfs_global.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. 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 @@ -52,7 +52,7 @@ void *pfs_malloc(size_t size, myf flags) DBUG_ASSERT(! pfs_initialized); DBUG_ASSERT(size > 0); - void *ptr; + void *ptr= NULL; #ifdef PFS_ALIGNEMENT #ifdef HAVE_POSIX_MEMALIGN @@ -128,6 +128,40 @@ void pfs_print_error(const char *format, ...) fflush(stderr); } +/** + Array allocation for the performance schema. + Checks for overflow of n * size before allocating. + @param n number of array elements + @param size element size + @param flags malloc flags + @return pointer to memory on success, else NULL +*/ +void *pfs_malloc_array(size_t n, size_t size, myf flags) +{ + DBUG_ASSERT(n > 0); + DBUG_ASSERT(size > 0); + size_t array_size= n * size; + /* Check for overflow before allocating. */ + if (is_overflow(array_size, n, size)) + return NULL; + return pfs_malloc(array_size, flags); +} + +/** + Detect multiplication overflow. + @param product multiplication product + @param n1 operand + @param n2 operand + @return true if multiplication caused an overflow. +*/ +bool is_overflow(size_t product, size_t n1, size_t n2) +{ + if (n1 != 0 && (product / n1 != n2)) + return true; + else + return false; +} + /** Convert raw ip address into readable format. Do not do a reverse DNS lookup. */ uint pfs_get_socket_address(char *host, diff --git a/storage/perfschema/pfs_global.h b/storage/perfschema/pfs_global.h index f9687524cd5..f3326c5589f 100644 --- a/storage/perfschema/pfs_global.h +++ b/storage/perfschema/pfs_global.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. 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 @@ -44,18 +44,24 @@ extern size_t pfs_allocated_memory; void *pfs_malloc(size_t size, myf flags); +/** Allocate an array of structures with overflow check. */ +void *pfs_malloc_array(size_t n, size_t size, myf flags); + /** Helper, to allocate an array of structures. - @param n number of elements in the array. - @param T type of an element. + @param n number of elements in the array + @param s size of array element + @param T type of an element @param f flags to use when allocating memory */ -#define PFS_MALLOC_ARRAY(n, T, f) \ - reinterpret_cast<T*> (pfs_malloc((n) * sizeof(T), (f))) +#define PFS_MALLOC_ARRAY(n, s, T, f) \ + reinterpret_cast<T*>(pfs_malloc_array((n), (s), (f))) /** Free memory allocated with @sa pfs_malloc. */ void pfs_free(void *ptr); +/** Detect multiplication overflow. */ +bool is_overflow(size_t product, size_t n1, size_t n2); uint pfs_get_socket_address(char *host, uint host_len, @@ -107,7 +113,7 @@ inline uint randomized_index(const void *ptr, uint max_size) value= (reinterpret_cast<intptr> (ptr)) >> 3; value*= 1789; value+= seed2 + seed1 + 1; - + result= (static_cast<uint> (value)) % max_size; seed2= seed1*seed1; diff --git a/storage/perfschema/pfs_host.cc b/storage/perfschema/pfs_host.cc index 80f3900275d..7da34a6d5f6 100644 --- a/storage/perfschema/pfs_host.cc +++ b/storage/perfschema/pfs_host.cc @@ -66,7 +66,7 @@ int init_host(const PFS_global_param *param) if (host_max > 0) { - host_array= PFS_MALLOC_ARRAY(host_max, PFS_host, + host_array= PFS_MALLOC_ARRAY(host_max, sizeof(PFS_host), PFS_host, MYF(MY_ZEROFILL)); if (unlikely(host_array == NULL)) return 1; diff --git a/storage/perfschema/pfs_instr.cc b/storage/perfschema/pfs_instr.cc index 5e75df4a16f..a9ea6faf212 100644 --- a/storage/perfschema/pfs_instr.cc +++ b/storage/perfschema/pfs_instr.cc @@ -94,7 +94,7 @@ ulong events_stages_history_per_thread; /** Number of EVENTS_STATEMENTS_HISTORY records per thread. */ ulong events_statements_history_per_thread; uint statement_stack_max; -uint pfs_max_digest_length= 0; +size_t pfs_max_digest_length= 0; /** Number of locker lost. @sa LOCKER_STACK_SIZE. */ ulong locker_lost= 0; /** Number of statement lost. @sa STATEMENT_STACK_SIZE. */ @@ -287,56 +287,56 @@ int init_instruments(const PFS_global_param *param) if (mutex_max > 0) { - mutex_array= PFS_MALLOC_ARRAY(mutex_max, PFS_mutex, MYF(MY_ZEROFILL)); + mutex_array= PFS_MALLOC_ARRAY(mutex_max, sizeof(PFS_mutex), PFS_mutex, MYF(MY_ZEROFILL)); if (unlikely(mutex_array == NULL)) return 1; } if (rwlock_max > 0) { - rwlock_array= PFS_MALLOC_ARRAY(rwlock_max, PFS_rwlock, MYF(MY_ZEROFILL)); + rwlock_array= PFS_MALLOC_ARRAY(rwlock_max, sizeof(PFS_rwlock), PFS_rwlock, MYF(MY_ZEROFILL)); if (unlikely(rwlock_array == NULL)) return 1; } if (cond_max > 0) { - cond_array= PFS_MALLOC_ARRAY(cond_max, PFS_cond, MYF(MY_ZEROFILL)); + cond_array= PFS_MALLOC_ARRAY(cond_max, sizeof(PFS_cond), PFS_cond, MYF(MY_ZEROFILL)); if (unlikely(cond_array == NULL)) return 1; } if (file_max > 0) { - file_array= PFS_MALLOC_ARRAY(file_max, PFS_file, MYF(MY_ZEROFILL)); + file_array= PFS_MALLOC_ARRAY(file_max, sizeof(PFS_file), PFS_file, MYF(MY_ZEROFILL)); if (unlikely(file_array == NULL)) return 1; } if (file_handle_max > 0) { - file_handle_array= PFS_MALLOC_ARRAY(file_handle_max, PFS_file*, MYF(MY_ZEROFILL)); + file_handle_array= PFS_MALLOC_ARRAY(file_handle_max, sizeof(PFS_file*), PFS_file*, MYF(MY_ZEROFILL)); if (unlikely(file_handle_array == NULL)) return 1; } if (table_max > 0) { - table_array= PFS_MALLOC_ARRAY(table_max, PFS_table, MYF(MY_ZEROFILL)); + table_array= PFS_MALLOC_ARRAY(table_max, sizeof(PFS_table), PFS_table, MYF(MY_ZEROFILL)); if (unlikely(table_array == NULL)) return 1; } if (socket_max > 0) { - socket_array= PFS_MALLOC_ARRAY(socket_max, PFS_socket, MYF(MY_ZEROFILL)); + socket_array= PFS_MALLOC_ARRAY(socket_max, sizeof(PFS_socket), PFS_socket, MYF(MY_ZEROFILL)); if (unlikely(socket_array == NULL)) return 1; } if (thread_max > 0) { - thread_array= PFS_MALLOC_ARRAY(thread_max, PFS_thread, MYF(MY_ZEROFILL)); + thread_array= PFS_MALLOC_ARRAY(thread_max, sizeof(PFS_thread), PFS_thread, MYF(MY_ZEROFILL)); if (unlikely(thread_array == NULL)) return 1; } @@ -344,7 +344,7 @@ int init_instruments(const PFS_global_param *param) if (thread_waits_history_sizing > 0) { thread_waits_history_array= - PFS_MALLOC_ARRAY(thread_waits_history_sizing, PFS_events_waits, + PFS_MALLOC_ARRAY(thread_waits_history_sizing, sizeof(PFS_events_waits), PFS_events_waits, MYF(MY_ZEROFILL)); if (unlikely(thread_waits_history_array == NULL)) return 1; @@ -354,7 +354,7 @@ int init_instruments(const PFS_global_param *param) { thread_instr_class_waits_array= PFS_MALLOC_ARRAY(thread_instr_class_waits_sizing, - PFS_single_stat, MYF(MY_ZEROFILL)); + sizeof(PFS_single_stat), PFS_single_stat, MYF(MY_ZEROFILL)); if (unlikely(thread_instr_class_waits_array == NULL)) return 1; @@ -365,7 +365,7 @@ int init_instruments(const PFS_global_param *param) if (thread_stages_history_sizing > 0) { thread_stages_history_array= - PFS_MALLOC_ARRAY(thread_stages_history_sizing, PFS_events_stages, + PFS_MALLOC_ARRAY(thread_stages_history_sizing, sizeof(PFS_events_stages), PFS_events_stages, MYF(MY_ZEROFILL)); if (unlikely(thread_stages_history_array == NULL)) return 1; @@ -375,7 +375,7 @@ int init_instruments(const PFS_global_param *param) { thread_instr_class_stages_array= PFS_MALLOC_ARRAY(thread_instr_class_stages_sizing, - PFS_stage_stat, MYF(MY_ZEROFILL)); + sizeof(PFS_stage_stat), PFS_stage_stat, MYF(MY_ZEROFILL)); if (unlikely(thread_instr_class_stages_array == NULL)) return 1; @@ -386,8 +386,8 @@ int init_instruments(const PFS_global_param *param) if (thread_statements_history_sizing > 0) { thread_statements_history_array= - PFS_MALLOC_ARRAY(thread_statements_history_sizing, PFS_events_statements, - MYF(MY_ZEROFILL)); + PFS_MALLOC_ARRAY(thread_statements_history_sizing, sizeof(PFS_events_statements), + PFS_events_statements, MYF(MY_ZEROFILL)); if (unlikely(thread_statements_history_array == NULL)) return 1; } @@ -395,8 +395,8 @@ int init_instruments(const PFS_global_param *param) if (thread_statements_stack_sizing > 0) { thread_statements_stack_array= - PFS_MALLOC_ARRAY(thread_statements_stack_sizing, PFS_events_statements, - MYF(MY_ZEROFILL)); + PFS_MALLOC_ARRAY(thread_statements_stack_sizing, sizeof(PFS_events_statements), + PFS_events_statements, MYF(MY_ZEROFILL)); if (unlikely(thread_statements_stack_array == NULL)) return 1; } @@ -405,7 +405,7 @@ int init_instruments(const PFS_global_param *param) { thread_instr_class_statements_array= PFS_MALLOC_ARRAY(thread_instr_class_statements_sizing, - PFS_statement_stat, MYF(MY_ZEROFILL)); + sizeof(PFS_statement_stat), PFS_statement_stat, MYF(MY_ZEROFILL)); if (unlikely(thread_instr_class_statements_array == NULL)) return 1; @@ -477,7 +477,7 @@ int init_instruments(const PFS_global_param *param) { global_instr_class_stages_array= PFS_MALLOC_ARRAY(stage_class_max, - PFS_stage_stat, MYF(MY_ZEROFILL)); + sizeof(PFS_stage_stat), PFS_stage_stat, MYF(MY_ZEROFILL)); if (unlikely(global_instr_class_stages_array == NULL)) return 1; @@ -489,7 +489,7 @@ int init_instruments(const PFS_global_param *param) { global_instr_class_statements_array= PFS_MALLOC_ARRAY(statement_class_max, - PFS_statement_stat, MYF(MY_ZEROFILL)); + sizeof(PFS_statement_stat), PFS_statement_stat, MYF(MY_ZEROFILL)); if (unlikely(global_instr_class_statements_array == NULL)) return 1; diff --git a/storage/perfschema/pfs_instr.h b/storage/perfschema/pfs_instr.h index a639f94fada..b25f5769b69 100644 --- a/storage/perfschema/pfs_instr.h +++ b/storage/perfschema/pfs_instr.h @@ -299,7 +299,7 @@ struct PFS_ALIGNED PFS_socket : public PFS_instr /** Max size of the statements stack. */ extern uint statement_stack_max; /** Max size of the digests token array. */ -extern uint pfs_max_digest_length; +extern size_t pfs_max_digest_length; /** @def PFS_MAX_ALLOC_RETRY diff --git a/storage/perfschema/pfs_instr_class.cc b/storage/perfschema/pfs_instr_class.cc index 84e0a0357a0..c67c120283d 100644 --- a/storage/perfschema/pfs_instr_class.cc +++ b/storage/perfschema/pfs_instr_class.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. 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 @@ -255,24 +255,24 @@ int init_sync_class(uint mutex_class_sizing, if (mutex_class_max > 0) { - mutex_class_array= PFS_MALLOC_ARRAY(mutex_class_max, PFS_mutex_class, - MYF(MY_ZEROFILL)); + mutex_class_array= PFS_MALLOC_ARRAY(mutex_class_max, sizeof(PFS_mutex_class), + PFS_mutex_class, MYF(MY_ZEROFILL)); if (unlikely(mutex_class_array == NULL)) return 1; } if (rwlock_class_max > 0) { - rwlock_class_array= PFS_MALLOC_ARRAY(rwlock_class_max, PFS_rwlock_class, - MYF(MY_ZEROFILL)); + rwlock_class_array= PFS_MALLOC_ARRAY(rwlock_class_max, sizeof(PFS_rwlock_class), + PFS_rwlock_class, MYF(MY_ZEROFILL)); if (unlikely(rwlock_class_array == NULL)) return 1; } if (cond_class_max > 0) { - cond_class_array= PFS_MALLOC_ARRAY(cond_class_max, PFS_cond_class, - MYF(MY_ZEROFILL)); + cond_class_array= PFS_MALLOC_ARRAY(cond_class_max, sizeof(PFS_cond_class), + PFS_cond_class, MYF(MY_ZEROFILL)); if (unlikely(cond_class_array == NULL)) return 1; } @@ -308,8 +308,8 @@ int init_thread_class(uint thread_class_sizing) if (thread_class_max > 0) { - thread_class_array= PFS_MALLOC_ARRAY(thread_class_max, PFS_thread_class, - MYF(MY_ZEROFILL)); + thread_class_array= PFS_MALLOC_ARRAY(thread_class_max, sizeof(PFS_thread_class), + PFS_thread_class, MYF(MY_ZEROFILL)); if (unlikely(thread_class_array == NULL)) result= 1; } @@ -341,8 +341,8 @@ int init_table_share(uint table_share_sizing) if (table_share_max > 0) { - table_share_array= PFS_MALLOC_ARRAY(table_share_max, PFS_table_share, - MYF(MY_ZEROFILL)); + table_share_array= PFS_MALLOC_ARRAY(table_share_max, sizeof(PFS_table_share), + PFS_table_share, MYF(MY_ZEROFILL)); if (unlikely(table_share_array == NULL)) result= 1; } @@ -481,8 +481,8 @@ int init_file_class(uint file_class_sizing) if (file_class_max > 0) { - file_class_array= PFS_MALLOC_ARRAY(file_class_max, PFS_file_class, - MYF(MY_ZEROFILL)); + file_class_array= PFS_MALLOC_ARRAY(file_class_max, sizeof(PFS_file_class), + PFS_file_class, MYF(MY_ZEROFILL)); if (unlikely(file_class_array == NULL)) return 1; } @@ -515,8 +515,8 @@ int init_stage_class(uint stage_class_sizing) if (stage_class_max > 0) { - stage_class_array= PFS_MALLOC_ARRAY(stage_class_max, PFS_stage_class, - MYF(MY_ZEROFILL)); + stage_class_array= PFS_MALLOC_ARRAY(stage_class_max, sizeof(PFS_stage_class), + PFS_stage_class, MYF(MY_ZEROFILL)); if (unlikely(stage_class_array == NULL)) return 1; } @@ -549,8 +549,8 @@ int init_statement_class(uint statement_class_sizing) if (statement_class_max > 0) { - statement_class_array= PFS_MALLOC_ARRAY(statement_class_max, PFS_statement_class, - MYF(MY_ZEROFILL)); + statement_class_array= PFS_MALLOC_ARRAY(statement_class_max, sizeof(PFS_statement_class), + PFS_statement_class, MYF(MY_ZEROFILL)); if (unlikely(statement_class_array == NULL)) return 1; } @@ -583,8 +583,8 @@ int init_socket_class(uint socket_class_sizing) if (socket_class_max > 0) { - socket_class_array= PFS_MALLOC_ARRAY(socket_class_max, PFS_socket_class, - MYF(MY_ZEROFILL)); + socket_class_array= PFS_MALLOC_ARRAY(socket_class_max, sizeof(PFS_socket_class), + PFS_socket_class, MYF(MY_ZEROFILL)); if (unlikely(socket_class_array == NULL)) return 1; } diff --git a/storage/perfschema/pfs_server.h b/storage/perfschema/pfs_server.h index e0fe71bc8c5..bc0c69e86b9 100644 --- a/storage/perfschema/pfs_server.h +++ b/storage/perfschema/pfs_server.h @@ -200,7 +200,7 @@ struct PFS_global_param /** Maximum number of session attribute strings per thread */ long m_session_connect_attrs_sizing; - uint m_max_digest_length; + long m_max_digest_length; /** Sizing hints, for auto tuning. */ PFS_sizing_hints m_hints; diff --git a/storage/perfschema/pfs_setup_actor.cc b/storage/perfschema/pfs_setup_actor.cc index ff45e4a0971..f12d70840c1 100644 --- a/storage/perfschema/pfs_setup_actor.cc +++ b/storage/perfschema/pfs_setup_actor.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. 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 @@ -60,8 +60,8 @@ int init_setup_actor(const PFS_global_param *param) if (setup_actor_max > 0) { - setup_actor_array= PFS_MALLOC_ARRAY(setup_actor_max, PFS_setup_actor, - MYF(MY_ZEROFILL)); + setup_actor_array= PFS_MALLOC_ARRAY(setup_actor_max, sizeof(PFS_setup_actor), + PFS_setup_actor, MYF(MY_ZEROFILL)); if (unlikely(setup_actor_array == NULL)) return 1; } diff --git a/storage/perfschema/pfs_setup_object.cc b/storage/perfschema/pfs_setup_object.cc index c77039663b2..809fe8edd24 100644 --- a/storage/perfschema/pfs_setup_object.cc +++ b/storage/perfschema/pfs_setup_object.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. 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 @@ -55,8 +55,8 @@ int init_setup_object(const PFS_global_param *param) if (setup_object_max > 0) { - setup_object_array= PFS_MALLOC_ARRAY(setup_object_max, PFS_setup_object, - MYF(MY_ZEROFILL)); + setup_object_array= PFS_MALLOC_ARRAY(setup_object_max, sizeof(PFS_setup_object), + PFS_setup_object, MYF(MY_ZEROFILL)); if (unlikely(setup_object_array == NULL)) return 1; } diff --git a/storage/perfschema/pfs_user.cc b/storage/perfschema/pfs_user.cc index a009e5d65ef..528457fe017 100644 --- a/storage/perfschema/pfs_user.cc +++ b/storage/perfschema/pfs_user.cc @@ -66,7 +66,7 @@ int init_user(const PFS_global_param *param) if (user_max > 0) { - user_array= PFS_MALLOC_ARRAY(user_max, PFS_user, + user_array= PFS_MALLOC_ARRAY(user_max, sizeof(PFS_user), PFS_user, MYF(MY_ZEROFILL)); if (unlikely(user_array == NULL)) return 1; diff --git a/storage/perfschema/table_events_stages.cc b/storage/perfschema/table_events_stages.cc index f98ff60d569..42761d92abf 100644 --- a/storage/perfschema/table_events_stages.cc +++ b/storage/perfschema/table_events_stages.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. 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 @@ -121,6 +121,7 @@ void table_events_stages_common::make_row(PFS_events_stages *stage) { const char *base; const char *safe_source_file; + ulonglong timer_end; m_row_exists= false; @@ -135,7 +136,16 @@ void table_events_stages_common::make_row(PFS_events_stages *stage) m_row.m_nesting_event_id= stage->m_nesting_event_id; m_row.m_nesting_event_type= stage->m_nesting_event_type; - m_normalizer->to_pico(stage->m_timer_start, stage->m_timer_end, + if (m_row.m_end_event_id == 0) + { + timer_end= get_timer_raw_value(stage_timer); + } + else + { + timer_end= stage->m_timer_end; + } + + m_normalizer->to_pico(stage->m_timer_start, timer_end, & m_row.m_timer_start, & m_row.m_timer_end, & m_row.m_timer_wait); m_row.m_name= klass->m_name; diff --git a/storage/perfschema/table_events_statements.cc b/storage/perfschema/table_events_statements.cc index 16f048a0a7f..84f30ce000f 100644 --- a/storage/perfschema/table_events_statements.cc +++ b/storage/perfschema/table_events_statements.cc @@ -215,6 +215,7 @@ void table_events_statements_common::make_row_part_1(PFS_events_statements *stat { const char *base; const char *safe_source_file; + ulonglong timer_end; m_row_exists= false; @@ -229,7 +230,16 @@ void table_events_statements_common::make_row_part_1(PFS_events_statements *stat m_row.m_nesting_event_id= statement->m_nesting_event_id; m_row.m_nesting_event_type= statement->m_nesting_event_type; - m_normalizer->to_pico(statement->m_timer_start, statement->m_timer_end, + if (m_row.m_end_event_id == 0) + { + timer_end= get_timer_raw_value(statement_timer); + } + else + { + timer_end= statement->m_timer_end; + } + + m_normalizer->to_pico(statement->m_timer_start, timer_end, & m_row.m_timer_start, & m_row.m_timer_end, & m_row.m_timer_wait); m_row.m_lock_time= statement->m_lock_time * MICROSEC_TO_PICOSEC; @@ -249,8 +259,8 @@ void table_events_statements_common::make_row_part_1(PFS_events_statements *stat return; base= base_name(safe_source_file); - m_row.m_source_length= my_snprintf(m_row.m_source, sizeof(m_row.m_source), - "%s:%d", base, statement->m_source_line); + m_row.m_source_length= (uint)my_snprintf(m_row.m_source, sizeof(m_row.m_source), + "%s:%d", base, statement->m_source_line); if (m_row.m_source_length > sizeof(m_row.m_source)) m_row.m_source_length= sizeof(m_row.m_source); @@ -291,7 +301,7 @@ void table_events_statements_common::make_row_part_2(const sql_digest_storage *d /* Filling up statement digest information. */ - uint safe_byte_count= digest->m_byte_count; + size_t safe_byte_count= digest->m_byte_count; if (safe_byte_count > 0 && safe_byte_count <= pfs_max_digest_length) { @@ -428,7 +438,7 @@ int table_events_statements_common::read_row_values(TABLE *table, f->set_null(); break; case 19: /* MESSAGE_TEXT */ - len= strlen(m_row.m_message_text); + len= (uint)strlen(m_row.m_message_text); if (len) set_field_varchar_utf8(f, m_row.m_message_text, len); else @@ -790,7 +800,7 @@ int table_events_statements_history_long::rnd_init(bool scan) int table_events_statements_history_long::rnd_next(void) { PFS_events_statements *statement; - uint limit; + size_t limit; if (events_statements_history_long_size == 0) return HA_ERR_END_OF_FILE; @@ -819,7 +829,7 @@ int table_events_statements_history_long::rnd_next(void) int table_events_statements_history_long::rnd_pos(const void *pos) { PFS_events_statements *statement; - uint limit; + size_t limit; if (events_statements_history_long_size == 0) return HA_ERR_RECORD_DELETED; diff --git a/storage/perfschema/table_events_waits.cc b/storage/perfschema/table_events_waits.cc index 8e2d0064538..cd32d81519e 100644 --- a/storage/perfschema/table_events_waits.cc +++ b/storage/perfschema/table_events_waits.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. 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 @@ -318,6 +318,8 @@ void table_events_waits_common::make_row(bool thread_own_wait, PFS_instr_class *safe_class; const char *base; const char *safe_source_file; + enum_timer_name timer_name= wait_timer; + ulonglong timer_end; m_row_exists= false; safe_thread= sanitize_thread(pfs_thread); @@ -356,6 +358,7 @@ void table_events_waits_common::make_row(bool thread_own_wait, case WAIT_CLASS_IDLE: clear_object_columns(); safe_class= sanitize_idle_class(wait->m_class); + timer_name= idle_timer; break; case WAIT_CLASS_MUTEX: clear_object_columns(); @@ -399,7 +402,17 @@ void table_events_waits_common::make_row(bool thread_own_wait, m_row.m_nesting_event_type= wait->m_nesting_event_type; get_normalizer(safe_class); - m_normalizer->to_pico(wait->m_timer_start, wait->m_timer_end, + + if (m_row.m_end_event_id == 0) + { + timer_end= get_timer_raw_value(timer_name); + } + else + { + timer_end= wait->m_timer_end; + } + + m_normalizer->to_pico(wait->m_timer_start, timer_end, & m_row.m_timer_start, & m_row.m_timer_end, & m_row.m_timer_wait); m_row.m_name= safe_class->m_name; diff --git a/storage/perfschema/table_helper.cc b/storage/perfschema/table_helper.cc index c9def1bfc74..d064333dc7e 100644 --- a/storage/perfschema/table_helper.cc +++ b/storage/perfschema/table_helper.cc @@ -110,7 +110,7 @@ int PFS_digest_row::make_row(PFS_statements_digest_stat* pfs) if (m_schema_name_length > 0) memcpy(m_schema_name, pfs->m_digest_key.m_schema_name, m_schema_name_length); - uint safe_byte_count= pfs->m_digest_storage.m_byte_count; + size_t safe_byte_count= pfs->m_digest_storage.m_byte_count; if (safe_byte_count > pfs_max_digest_length) safe_byte_count= 0; diff --git a/storage/perfschema/unittest/CMakeLists.txt b/storage/perfschema/unittest/CMakeLists.txt index e8b11e807bf..4c6b0b91021 100644 --- a/storage/perfschema/unittest/CMakeLists.txt +++ b/storage/perfschema/unittest/CMakeLists.txt @@ -28,5 +28,5 @@ ADD_CONVENIENCE_LIBRARY(pfs_server_stubs pfs_server_stubs.cc) ADD_DEPENDENCIES(pfs_server_stubs GenError) MY_ADD_TESTS(pfs_instr_class pfs_instr_class-oom pfs_instr pfs_instr-oom - pfs_account-oom pfs_host-oom pfs_timer pfs_user-oom pfs + pfs_account-oom pfs_host-oom pfs_timer pfs_user-oom pfs pfs_misc EXT "cc" LINK_LIBRARIES perfschema mysys pfs_server_stubs) diff --git a/storage/perfschema/unittest/pfs_misc-t.cc b/storage/perfschema/unittest/pfs_misc-t.cc new file mode 100644 index 00000000000..a4b11b9a727 --- /dev/null +++ b/storage/perfschema/unittest/pfs_misc-t.cc @@ -0,0 +1,72 @@ +/* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + + 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, + 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */ + +#include <my_global.h> +#include <pfs_instr.h> +#include <pfs_stat.h> +#include <pfs_global.h> +#include <pfs_instr_class.h> +#include <tap.h> + +#include <memory.h> + +void test_digest_length_overflow() +{ + if (sizeof(size_t) != 4) + { + skip(2, "digest length overflow requires a 32-bit environment"); + return; + } + + PFS_global_param param; + memset(¶m, 0, sizeof(param)); + param.m_enabled= true; + /* + Force 32-bit arithmetic overflow using the digest memory allocation + parameters. The Performance Schema should detect the overflow, free + allocated memory and abort initialization with a warning. + */ + + /* Max digest length, events_statements_history_long. */ + param.m_events_statements_history_long_sizing= 10000; + param.m_digest_sizing= 1000; + param.m_max_digest_length= (1024 * 1024); + pfs_max_digest_length= param.m_max_digest_length; + + int rc = init_events_statements_history_long(param.m_events_statements_history_long_sizing); + ok(rc == 1, "digest length overflow (init_events_statements_history_long"); + + /* Max digest length, events_statements_summary_by_digest. */ + param.m_max_digest_length= (1024 * 1024); + param.m_digest_sizing= 10000; + + rc = init_digest(¶m); + ok(rc == 1, "digest length overflow (init_digest)"); +} + +void do_all_tests() +{ + test_digest_length_overflow(); +} + +int main(int, char **) +{ + plan(2); + MY_INIT("pfs_misc-t"); + do_all_tests(); + my_end(0); + return exit_status(); +} + diff --git a/storage/perfschema/unittest/pfs_server_stubs.cc b/storage/perfschema/unittest/pfs_server_stubs.cc index a023414357a..7c0e275edc7 100644 --- a/storage/perfschema/unittest/pfs_server_stubs.cc +++ b/storage/perfschema/unittest/pfs_server_stubs.cc @@ -34,7 +34,11 @@ void compute_digest_md5(const sql_digest_storage *, unsigned char *) } class sys_var { public: enum where { AUTO }; }; -void mark_sys_var_value_origin(void *ptr, enum sys_var::where here) +void set_sys_var_value_origin(void *ptr, enum sys_var::where here) { } +enum sys_var::where get_sys_var_value_origin(void *ptr) +{ + return sys_var::AUTO; +} diff --git a/storage/perfschema/unittest/stub_pfs_global.h b/storage/perfschema/unittest/stub_pfs_global.h index 34c52e18b5a..8f204006f48 100644 --- a/storage/perfschema/unittest/stub_pfs_global.h +++ b/storage/perfschema/unittest/stub_pfs_global.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. 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 @@ -25,6 +25,11 @@ int stub_alloc_fails_after_count= 0; void *pfs_malloc(size_t size, myf) { + /* + Catch non initialized sizing parameter in the unit tests. + */ + DBUG_ASSERT(size <= 100*1024*1024); + if (stub_alloc_always_fails) return NULL; @@ -43,6 +48,23 @@ void pfs_free(void *ptr) free(ptr); } +void *pfs_malloc_array(size_t n, size_t size, myf flags) +{ + size_t array_size= n * size; + /* Check for overflow before allocating. */ + if (is_overflow(array_size, n, size)) + return NULL; + return pfs_malloc(array_size, flags); +} + +bool is_overflow(size_t product, size_t n1, size_t n2) +{ + if (n1 != 0 && (product / n1 != n2)) + return true; + else + return false; +} + void pfs_print_error(const char *format, ...) { } diff --git a/storage/perfschema/unittest/stub_print_error.h b/storage/perfschema/unittest/stub_print_error.h index caad24e5257..e9b8bc25548 100644 --- a/storage/perfschema/unittest/stub_print_error.h +++ b/storage/perfschema/unittest/stub_print_error.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. 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 @@ -33,6 +33,23 @@ void pfs_free(void *ptr) free(ptr); } +void *pfs_malloc_array(size_t n, size_t size, myf flags) +{ + size_t array_size= n * size; + /* Check for overflow before allocating. */ + if (is_overflow(array_size, n, size)) + return NULL; + return pfs_malloc(array_size, flags); +} + +bool is_overflow(size_t product, size_t n1, size_t n2) +{ + if (n1 != 0 && (product / n1 != n2)) + return true; + else + return false; +} + void pfs_print_error(const char *format, ...) { /* Do not pollute the unit test output with annoying messages. */ diff --git a/storage/spider/CMakeLists.txt b/storage/spider/CMakeLists.txt index 33786e2b85a..402c74b2cde 100644 --- a/storage/spider/CMakeLists.txt +++ b/storage/spider/CMakeLists.txt @@ -1,10 +1,7 @@ SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DHAVE_HANDLERSOCKET") SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DHAVE_HANDLERSOCKET") -IF(HAVE_WVLA) - SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wno-vla") - SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wno-vla") -ENDIF() +MY_CHECK_AND_SET_COMPILER_FLAG("-Wno-vla" DEBUG) SET(SPIDER_SOURCES spd_param.cc spd_sys_table.cc spd_trx.cc spd_db_conn.cc spd_conn.cc diff --git a/storage/tokudb/CMakeLists.txt b/storage/tokudb/CMakeLists.txt index a8a821e5ad6..7cbc8a119e4 100644 --- a/storage/tokudb/CMakeLists.txt +++ b/storage/tokudb/CMakeLists.txt @@ -17,12 +17,8 @@ IF(NOT LIBJEMALLOC) MESSAGE(WARNING "TokuDB is enabled, but jemalloc is not. This configuration is not supported") ENDIF() -IF (HAVE_WVLA) - SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-vla") - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-vla") - SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wno-vla") - SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wno-vla") -ENDIF() +MY_CHECK_AND_SET_COMPILER_FLAG("-Wno-vla") +MY_CHECK_AND_SET_COMPILER_FLAG("-Wno-vla" DEBUG) ############################################ SET(TOKUDB_DEB_FILES "usr/lib/mysql/plugin/ha_tokudb.so\netc/mysql/conf.d/tokudb.cnf\nusr/bin/tokuftdump\nusr/share/doc/mariadb-server-10.1/README-TOKUDB\nusr/share/doc/mariadb-server-10.1/README.md" PARENT_SCOPE) @@ -70,36 +66,7 @@ ENDIF() include(CheckCCompilerFlag) include(CheckCXXCompilerFlag) -macro(set_cflags_if_supported) - foreach(flag ${ARGN}) - string(REGEX REPLACE "-" "_" temp_flag ${flag}) - check_c_compiler_flag(${flag} HAVE_C_${temp_flag}) - if (HAVE_C_${temp_flag}) - set(CMAKE_C_FLAGS "${flag} ${CMAKE_C_FLAGS}") - endif () - check_cxx_compiler_flag(${flag} HAVE_CXX_${temp_flag}) - if (HAVE_CXX_${temp_flag}) - set(CMAKE_CXX_FLAGS "${flag} ${CMAKE_CXX_FLAGS}") - endif () - endforeach(flag) -endmacro(set_cflags_if_supported) - -macro(append_cflags_if_supported) - foreach(flag ${ARGN}) - string(REGEX REPLACE "-" "_" temp_flag ${flag}) - check_c_compiler_flag(${flag} HAVE_C_${temp_flag}) - if (HAVE_C_${temp_flag}) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${flag}") - endif () - check_cxx_compiler_flag(${flag} HAVE_CXX_${temp_flag}) - if (HAVE_CXX_${temp_flag}) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${flag}") - endif () - endforeach(flag) -endmacro(append_cflags_if_supported) - -set_cflags_if_supported(-Wno-missing-field-initializers) -append_cflags_if_supported(-Wno-vla) +MY_CHECK_AND_SET_COMPILER_FLAG(-Wno-missing-field-initializers) ADD_SUBDIRECTORY(ft-index) diff --git a/storage/tokudb/ft-index/cmake_modules/TokuSetupCompiler.cmake b/storage/tokudb/ft-index/cmake_modules/TokuSetupCompiler.cmake index 99629e40bdb..ee45e7cccf3 100644 --- a/storage/tokudb/ft-index/cmake_modules/TokuSetupCompiler.cmake +++ b/storage/tokudb/ft-index/cmake_modules/TokuSetupCompiler.cmake @@ -47,18 +47,6 @@ include(CheckCCompilerFlag) include(CheckCXXCompilerFlag) ## adds a compiler flag if the compiler supports it -macro(set_cflags_if_supported_named flag flagname) - check_c_compiler_flag("${flag}" HAVE_C_${flagname}) - if (HAVE_C_${flagname}) - set(CMAKE_C_FLAGS "${flag} ${CMAKE_C_FLAGS}") - endif () - check_cxx_compiler_flag("${flag}" HAVE_CXX_${flagname}) - if (HAVE_CXX_${flagname}) - set(CMAKE_CXX_FLAGS "${flag} ${CMAKE_CXX_FLAGS}") - endif () -endmacro(set_cflags_if_supported_named) - -## adds a compiler flag if the compiler supports it macro(set_cflags_if_supported) foreach(flag ${ARGN}) check_c_compiler_flag(${flag} HAVE_C_${flag}) @@ -84,21 +72,19 @@ macro(set_ldflags_if_supported) endmacro(set_ldflags_if_supported) ## disable some warnings -set_cflags_if_supported( - -Wno-missing-field-initializers - -Wstrict-null-sentinel - -Winit-self - -Wswitch - -Wtrampolines - -Wlogical-op - -Wmissing-format-attribute - -Wno-error=missing-format-attribute - -Wno-error=address-of-array-temporary - -Wno-error=tautological-constant-out-of-range-compare - -Wno-ignored-attributes - -fno-rtti - -fno-exceptions - ) +MY_CHECK_AND_SET_COMPILER_FLAG(-Wno-missing-field-initializers) +MY_CHECK_AND_SET_COMPILER_FLAG(-Wstrict-null-sentinel) +MY_CHECK_AND_SET_COMPILER_FLAG(-Winit-self) +MY_CHECK_AND_SET_COMPILER_FLAG(-Wswitch) +MY_CHECK_AND_SET_COMPILER_FLAG(-Wtrampolines) +MY_CHECK_AND_SET_COMPILER_FLAG(-Wlogical-op) +MY_CHECK_AND_SET_COMPILER_FLAG(-Wmissing-format-attribute) +MY_CHECK_AND_SET_COMPILER_FLAG(-Wno-error=missing-format-attribute) +MY_CHECK_AND_SET_COMPILER_FLAG(-Wno-error=address-of-array-temporary) +MY_CHECK_AND_SET_COMPILER_FLAG(-Wno-error=tautological-constant-out-of-range-compare) +MY_CHECK_AND_SET_COMPILER_FLAG(-Wno-ignored-attributes) +MY_CHECK_AND_SET_COMPILER_FLAG(-fno-rtti) +MY_CHECK_AND_SET_COMPILER_FLAG(-fno-exceptions) ## set_cflags_if_supported_named("-Weffc++" -Weffcpp) if (CMAKE_CXX_FLAGS MATCHES -fno-implicit-templates) @@ -111,19 +97,12 @@ endif() ## Clang has stricter POD checks. So, only enable this warning on our other builds (Linux + GCC) if (NOT CMAKE_CXX_COMPILER_ID MATCHES Clang) - set_cflags_if_supported( - -Wpacked - ) + MY_CHECK_AND_SET_COMPILER_FLAG(-Wpacked) endif () ## this hits with optimized builds somewhere in ftleaf_split, we don't ## know why but we don't think it's a big deal -set_cflags_if_supported( - -Wno-error=strict-overflow - ) -set_ldflags_if_supported( - -Wno-error=strict-overflow - ) +MY_CHECK_AND_SET_COMPILER_FLAG(-Wno-error=strict-overflow) ## set extra debugging flags and preprocessor definitions set(CMAKE_C_FLAGS_DEBUG "-g3 -O0 ${CMAKE_C_FLAGS_DEBUG}") @@ -154,16 +133,15 @@ else () endif () ## set warnings -set_cflags_if_supported( - -Wextra - -Wbad-function-cast - -Wno-missing-noreturn - -Wstrict-prototypes - -Wmissing-prototypes - -Wmissing-declarations - -Wpointer-arith - -Wmissing-format-attribute - -Wshadow + MY_CHECK_AND_SET_COMPILER_FLAG(-Wextra) + MY_CHECK_AND_SET_COMPILER_FLAG(-Wbad-function-cast) + MY_CHECK_AND_SET_COMPILER_FLAG(-Wno-missing-noreturn) + MY_CHECK_AND_SET_COMPILER_FLAG(-Wstrict-prototypes) + MY_CHECK_AND_SET_COMPILER_FLAG(-Wmissing-prototypes) + MY_CHECK_AND_SET_COMPILER_FLAG(-Wmissing-declarations) + MY_CHECK_AND_SET_COMPILER_FLAG(-Wpointer-arith) + MY_CHECK_AND_SET_COMPILER_FLAG(-Wmissing-format-attribute) + MY_CHECK_AND_SET_COMPILER_FLAG(-Wshadow) ## other flags to try: #-Wunsafe-loop-optimizations #-Wpointer-arith @@ -173,11 +151,10 @@ set_cflags_if_supported( #-Wzero-as-null-pointer-constant #-Wlogical-op #-Wvector-optimization-performance - ) if (NOT CMAKE_CXX_COMPILER_ID STREQUAL Clang) # Disabling -Wcast-align with clang. TODO: fix casting and re-enable it, someday. - set_cflags_if_supported(-Wcast-align) + MY_CHECK_AND_SET_COMPILER_FLAG(-Wcast-align) endif () ## always want these diff --git a/storage/tokudb/ha_tokudb.cc b/storage/tokudb/ha_tokudb.cc index e0e4bab2072..97fadae8b8e 100644 --- a/storage/tokudb/ha_tokudb.cc +++ b/storage/tokudb/ha_tokudb.cc @@ -172,7 +172,7 @@ static inline uint32_t get_len_of_offsets(KEY_AND_COL_INFO* kc_info, TABLE_SHARE } -#ifndef NOT_USED +#ifdef NOT_USED static int get_thread_query_string(my_thread_id id, String &qs) { mysql_mutex_lock(&LOCK_thread_count); I_List_iterator<THD> it(threads); diff --git a/storage/xtradb/CMakeLists.txt b/storage/xtradb/CMakeLists.txt index a6e2014e679..0ae3528e512 100644 --- a/storage/xtradb/CMakeLists.txt +++ b/storage/xtradb/CMakeLists.txt @@ -462,14 +462,6 @@ SET(INNOBASE_SOURCES ut/ut0wqueue.cc ut/ut0timer.cc) -# These files have unused result errors, so we skip Werror -CHECK_C_COMPILER_FLAG("-Werror" HAVE_WERROR) -IF(HAVE_WERROR) - INCLUDE(${MYSQL_CMAKE_SCRIPT_DIR}/compile_flags.cmake) - ADD_COMPILE_FLAGS(page/page0zip.c COMPILE_FLAGS "-Wno-error") - ADD_COMPILE_FLAGS(ut/ut0ut.c COMPILE_FLAGS "-Wno-error") -ENDIF() - IF(XTRADB_OK) MYSQL_ADD_PLUGIN(xtradb ${INNOBASE_SOURCES} STORAGE_ENGINE DEFAULT RECOMPILE_FOR_EMBEDDED diff --git a/storage/xtradb/api/api0api.cc b/storage/xtradb/api/api0api.cc index 8769fc47166..0fe21423232 100644 --- a/storage/xtradb/api/api0api.cc +++ b/storage/xtradb/api/api0api.cc @@ -595,6 +595,21 @@ ib_trx_begin( return(static_cast<ib_trx_t>(trx)); } + +/*****************************************************************//** +Check if transaction is read_only +@return transaction read_only status */ +UNIV_INTERN +ib_u32_t +ib_trx_read_only( +/*=============*/ + ib_trx_t ib_trx) /*!< in: trx handle */ +{ + trx_t* trx = (trx_t*) ib_trx; + + return(trx->read_only); +} + /*****************************************************************//** Get the transaction's state. @return transaction state */ diff --git a/storage/xtradb/btr/btr0btr.cc b/storage/xtradb/btr/btr0btr.cc index 2f248c98651..86e70109db5 100644 --- a/storage/xtradb/btr/btr0btr.cc +++ b/storage/xtradb/btr/btr0btr.cc @@ -2,6 +2,7 @@ Copyright (c) 1994, 2013, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2012, Facebook Inc. +Copyright (c) 2014, 2015, 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 @@ -741,6 +742,19 @@ btr_root_block_get( block = btr_block_get(space, zip_size, root_page_no, mode, index, mtr); + if (!block) { + index->table->is_encrypted = TRUE; + index->table->corrupted = FALSE; + + ib_push_warning(index->table->thd, DB_ENCRYPTED_DECRYPT_FAILED, + "Table %s in tablespace %lu is encrypted but encryption service or" + " used key_id is not available. " + " Can't continue reading table.", + index->table->name, space); + + return NULL; + } + SRV_CORRUPT_TABLE_CHECK(block, return(0);); btr_assert_not_corrupted(block, index); @@ -779,8 +793,10 @@ btr_root_get( const dict_index_t* index, /*!< in: index tree */ mtr_t* mtr) /*!< in: mtr */ { - return(buf_block_get_frame(btr_root_block_get(index, RW_X_LATCH, - mtr))); + buf_block_t* root = btr_root_block_get(index, RW_X_LATCH, + mtr); + + return(root ? buf_block_get_frame(root) : NULL); } /**************************************************************//** @@ -795,7 +811,7 @@ btr_height_get( dict_index_t* index, /*!< in: index tree */ mtr_t* mtr) /*!< in/out: mini-transaction */ { - ulint height; + ulint height=0; buf_block_t* root_block; ut_ad(mtr_memo_contains(mtr, dict_index_get_lock(index), @@ -806,13 +822,16 @@ btr_height_get( /* S latches the page */ root_block = btr_root_block_get(index, RW_S_LATCH, mtr); - height = btr_page_get_level(buf_block_get_frame_fast(root_block), mtr); + if (root_block) { + + height = btr_page_get_level(buf_block_get_frame_fast(root_block), mtr); - /* Release the S latch on the root page. */ - mtr_memo_release(mtr, root_block, MTR_MEMO_PAGE_S_FIX); + /* Release the S latch on the root page. */ + mtr_memo_release(mtr, root_block, MTR_MEMO_PAGE_S_FIX); #ifdef UNIV_SYNC_DEBUG - sync_thread_reset_level(&root_block->lock); + sync_thread_reset_level(&root_block->lock); #endif /* UNIV_SYNC_DEBUG */ + } return(height); } @@ -1260,7 +1279,7 @@ btr_get_size_and_reserved( { fseg_header_t* seg_header; page_t* root; - ulint n; + ulint n=ULINT_UNDEFINED; ulint dummy; ut_ad(mtr_memo_contains(mtr, dict_index_get_lock(index), @@ -1274,17 +1293,21 @@ btr_get_size_and_reserved( } root = btr_root_get(index, mtr); + *used = 0; + + if (root) { - seg_header = root + PAGE_HEADER + PAGE_BTR_SEG_LEAF; + seg_header = root + PAGE_HEADER + PAGE_BTR_SEG_LEAF; - n = fseg_n_reserved_pages(seg_header, used, mtr); + n = fseg_n_reserved_pages(seg_header, used, mtr); - if (flag == BTR_TOTAL_SIZE) { - seg_header = root + PAGE_HEADER + PAGE_BTR_SEG_TOP; + if (flag == BTR_TOTAL_SIZE) { + seg_header = root + PAGE_HEADER + PAGE_BTR_SEG_TOP; - n += fseg_n_reserved_pages(seg_header, &dummy, mtr); - *used += dummy; + n += fseg_n_reserved_pages(seg_header, &dummy, mtr); + *used += dummy; + } } return(n); diff --git a/storage/xtradb/btr/btr0cur.cc b/storage/xtradb/btr/btr0cur.cc index 4e5e6a713ed..316271077ae 100644 --- a/storage/xtradb/btr/btr0cur.cc +++ b/storage/xtradb/btr/btr0cur.cc @@ -3,6 +3,7 @@ Copyright (c) 1994, 2014, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2008, Google Inc. Copyright (c) 2012, Facebook Inc. +Copyright (c) 2015, MariaDB Corporation. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described @@ -386,7 +387,7 @@ search tuple should be performed in the B-tree. InnoDB does an insert immediately after the cursor. Thus, the cursor may end up on a user record, or on a page infimum record. */ UNIV_INTERN -void +dberr_t btr_cur_search_to_nth_level( /*========================*/ dict_index_t* index, /*!< in: index */ @@ -436,6 +437,7 @@ btr_cur_search_to_nth_level( page_cur_t* page_cursor; btr_op_t btr_op; ulint root_height = 0; /* remove warning */ + dberr_t err = DB_SUCCESS; #ifdef BTR_CUR_ADAPT btr_search_t* info; @@ -553,7 +555,7 @@ btr_cur_search_to_nth_level( || mode != PAGE_CUR_LE); btr_cur_n_sea++; - return; + return err; } # endif /* BTR_CUR_HASH_ADAPT */ #endif /* BTR_CUR_ADAPT */ @@ -649,7 +651,21 @@ search_loop: retry_page_get: block = buf_page_get_gen( space, zip_size, page_no, rw_latch, guess, buf_mode, - file, line, mtr); + file, line, mtr, &err); + + if (err != DB_SUCCESS) { + if (err == DB_ENCRYPTED_DECRYPT_FAILED) { + ib_push_warning((void *)NULL, + DB_ENCRYPTED_DECRYPT_FAILED, + "Table %s is encrypted but encryption service or" + " used key_id is not available. " + " Can't continue reading table.", + index->table->name); + index->table->is_encrypted = true; + } + + goto func_exit; + } if (block == NULL) { SRV_CORRUPT_TABLE_CHECK(buf_mode == BUF_GET_IF_IN_POOL || @@ -889,12 +905,14 @@ func_exit: rw_lock_s_lock(btr_search_get_latch(cursor->index)); } + + return err; } /*****************************************************************//** Opens a cursor at either end of an index. */ UNIV_INTERN -void +dberr_t btr_cur_open_at_index_side_func( /*============================*/ bool from_left, /*!< in: true if open to the low end, @@ -920,6 +938,8 @@ btr_cur_open_at_index_side_func( mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; + dberr_t err = DB_SUCCESS; + rec_offs_init(offsets_); estimate = latch_mode & BTR_ESTIMATE; @@ -957,11 +977,26 @@ btr_cur_open_at_index_side_func( height = ULINT_UNDEFINED; for (;;) { - buf_block_t* block; - page_t* page; + buf_block_t* block=NULL; + page_t* page=NULL; + block = buf_page_get_gen(space, zip_size, page_no, RW_NO_LATCH, NULL, BUF_GET, - file, line, mtr); + file, line, mtr, &err); + if (err != DB_SUCCESS) { + if (err == DB_ENCRYPTED_DECRYPT_FAILED) { + ib_push_warning((void *)NULL, + DB_ENCRYPTED_DECRYPT_FAILED, + "Table %s is encrypted but encryption service or" + " used key_id is not available. " + " Can't continue reading table.", + index->table->name); + index->table->is_encrypted = true; + } + + goto exit_loop; + } + page = buf_block_get_frame(block); SRV_CORRUPT_TABLE_CHECK(page, @@ -1066,6 +1101,8 @@ exit_loop: if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } + + return err; } /**********************************************************************//** @@ -1113,10 +1150,25 @@ btr_cur_open_at_rnd_pos_func( for (;;) { buf_block_t* block; page_t* page; + dberr_t err=DB_SUCCESS; block = buf_page_get_gen(space, zip_size, page_no, RW_NO_LATCH, NULL, BUF_GET, - file, line, mtr); + file, line, mtr, &err); + + if (err != DB_SUCCESS) { + if (err == DB_ENCRYPTED_DECRYPT_FAILED) { + ib_push_warning((void *)NULL, + DB_ENCRYPTED_DECRYPT_FAILED, + "Table %s is encrypted but encryption service or" + " used key_id is not available. " + " Can't continue reading table.", + index->table->name); + index->table->is_encrypted = true; + } + goto exit_loop; + } + page = buf_block_get_frame(block); SRV_CORRUPT_TABLE_CHECK(page, @@ -3749,6 +3801,7 @@ btr_estimate_n_rows_in_range_on_level( mtr_t mtr; page_t* page; buf_block_t* block; + dberr_t err=DB_SUCCESS; mtr_start(&mtr); @@ -3759,7 +3812,23 @@ btr_estimate_n_rows_in_range_on_level( silence a debug assertion about this. */ block = buf_page_get_gen(space, zip_size, page_no, RW_S_LATCH, NULL, BUF_GET_POSSIBLY_FREED, - __FILE__, __LINE__, &mtr); + __FILE__, __LINE__, &mtr, &err); + + if (err != DB_SUCCESS) { + if (err == DB_ENCRYPTED_DECRYPT_FAILED) { + ib_push_warning((void *)NULL, + DB_ENCRYPTED_DECRYPT_FAILED, + "Table %s is encrypted but encryption service or" + " used key_id is not available. " + " Can't continue reading table.", + index->table->name); + index->table->is_encrypted = true; + } + + mtr_commit(&mtr); + goto inexact; + } + page = buf_block_get_frame(block); diff --git a/storage/xtradb/buf/buf0buf.cc b/storage/xtradb/buf/buf0buf.cc index 361b0783482..31157aa737f 100644 --- a/storage/xtradb/buf/buf0buf.cc +++ b/storage/xtradb/buf/buf0buf.cc @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1995, 2014, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 1995, 2015, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2008, Google Inc. Copyright (c) 2013, 2015, MariaDB Corporation. All Rights Reserved. @@ -59,11 +59,24 @@ Created 11/5/1995 Heikki Tuuri #include "srv0start.h" #include "ut0byte.h" #include "fil0pagecompress.h" +#include "ha_prototypes.h" /* prototypes for new functions added to ha_innodb.cc */ trx_t* innobase_get_trx(); +/********************************************************************//** +Check if page is maybe compressed, encrypted or both when we encounter +corrupted page. Note that we can't be 100% sure if page is corrupted +or decrypt/decompress just failed. +*/ +static +ibool +buf_page_check_corrupt( +/*===================*/ + buf_page_t* bpage); /*!< in/out: buffer page read from + disk */ + static inline void _increment_page_get_statistics(buf_block_t* block, trx_t* trx) @@ -564,6 +577,79 @@ buf_page_is_zeroes( return(true); } +/** Checks if the page is in crc32 checksum format. +@param[in] read_buf database page +@param[in] checksum_field1 new checksum field +@param[in] checksum_field2 old checksum field +@return true if the page is in crc32 checksum format */ +UNIV_INLINE +bool +buf_page_is_checksum_valid_crc32( + const byte* read_buf, + ulint checksum_field1, + ulint checksum_field2) +{ + ib_uint32_t crc32 = buf_calc_page_crc32(read_buf); + + return(checksum_field1 == crc32 && checksum_field2 == crc32); +} + +/** Checks if the page is in innodb checksum format. +@param[in] read_buf database page +@param[in] checksum_field1 new checksum field +@param[in] checksum_field2 old checksum field +@return true if the page is in innodb checksum format */ +UNIV_INLINE +bool +buf_page_is_checksum_valid_innodb( + const byte* read_buf, + ulint checksum_field1, + ulint checksum_field2) +{ + /* There are 2 valid formulas for + checksum_field2 (old checksum field) which algo=innodb could have + written to the page: + + 1. Very old versions of InnoDB only stored 8 byte lsn to the + start and the end of the page. + + 2. Newer InnoDB versions store the old formula checksum + (buf_calc_page_old_checksum()). */ + + if (checksum_field2 != mach_read_from_4(read_buf + FIL_PAGE_LSN) + && checksum_field2 != buf_calc_page_old_checksum(read_buf)) { + return(false); + } + + /* old field is fine, check the new field */ + + /* InnoDB versions < 4.0.14 and < 4.1.1 stored the space id + (always equal to 0), to FIL_PAGE_SPACE_OR_CHKSUM */ + + if (checksum_field1 != 0 + && checksum_field1 != buf_calc_page_new_checksum(read_buf)) { + return(false); + } + + return(true); +} + +/** Checks if the page is in none checksum format. +@param[in] read_buf database page +@param[in] checksum_field1 new checksum field +@param[in] checksum_field2 old checksum field +@return true if the page is in none checksum format */ +UNIV_INLINE +bool +buf_page_is_checksum_valid_none( + const byte* read_buf, + ulint checksum_field1, + ulint checksum_field2) +{ + return(checksum_field1 == checksum_field2 + && checksum_field1 == BUF_NO_CHECKSUM_MAGIC); +} + /********************************************************************//** Checks if a page is corrupt. @return TRUE if corrupted */ @@ -580,8 +666,6 @@ buf_page_is_corrupted( ulint page_encrypted = fil_page_is_encrypted(read_buf); ulint checksum_field1; ulint checksum_field2; - ibool crc32_inited = FALSE; - ib_uint32_t crc32 = ULINT32_UNDEFINED; if (!page_encrypted && !zip_size && memcmp(read_buf + FIL_PAGE_LSN + 4, @@ -664,148 +748,121 @@ buf_page_is_corrupted( return(FALSE); } - switch ((srv_checksum_algorithm_t) srv_checksum_algorithm) { - case SRV_CHECKSUM_ALGORITHM_STRICT_CRC32: - - crc32 = buf_calc_page_crc32(read_buf); - - return(checksum_field1 != crc32 || checksum_field2 != crc32); - - case SRV_CHECKSUM_ALGORITHM_STRICT_INNODB: - - return(checksum_field1 - != buf_calc_page_new_checksum(read_buf) - || checksum_field2 - != buf_calc_page_old_checksum(read_buf)); - - case SRV_CHECKSUM_ALGORITHM_STRICT_NONE: + DBUG_EXECUTE_IF("buf_page_is_corrupt_failure", return(TRUE); ); - return(checksum_field1 != BUF_NO_CHECKSUM_MAGIC - || checksum_field2 != BUF_NO_CHECKSUM_MAGIC); + ulint page_no = mach_read_from_4(read_buf + FIL_PAGE_OFFSET); + ulint space_id = mach_read_from_4(read_buf + FIL_PAGE_SPACE_ID); + const srv_checksum_algorithm_t curr_algo = + static_cast<srv_checksum_algorithm_t>(srv_checksum_algorithm); + switch (curr_algo) { case SRV_CHECKSUM_ALGORITHM_CRC32: - case SRV_CHECKSUM_ALGORITHM_INNODB: - /* There are 3 valid formulas for - checksum_field2 (old checksum field): - - 1. Very old versions of InnoDB only stored 8 byte lsn to the - start and the end of the page. - - 2. InnoDB versions before MySQL 5.6.3 store the old formula - checksum (buf_calc_page_old_checksum()). - - 3. InnoDB versions 5.6.3 and newer with - innodb_checksum_algorithm=strict_crc32|crc32 store CRC32. */ - - /* since innodb_checksum_algorithm is not strict_* allow - any of the algos to match for the old field */ - - if (checksum_field2 - != mach_read_from_4(read_buf + FIL_PAGE_LSN) - && checksum_field2 != BUF_NO_CHECKSUM_MAGIC) { - - /* The checksum does not match any of the - fast to check. First check the selected algorithm - for writing checksums because we assume that the - chance of it matching is higher. */ - - if (srv_checksum_algorithm - == SRV_CHECKSUM_ALGORITHM_CRC32) { - - crc32 = buf_calc_page_crc32(read_buf); - crc32_inited = TRUE; - - if (checksum_field2 != crc32 - && checksum_field2 - != buf_calc_page_old_checksum(read_buf)) { + case SRV_CHECKSUM_ALGORITHM_STRICT_CRC32: - return(TRUE); - } - } else { - ut_ad(srv_checksum_algorithm - == SRV_CHECKSUM_ALGORITHM_INNODB); + if (buf_page_is_checksum_valid_crc32(read_buf, + checksum_field1, checksum_field2)) { + return(FALSE); + } - if (checksum_field2 - != buf_calc_page_old_checksum(read_buf)) { + if (buf_page_is_checksum_valid_none(read_buf, + checksum_field1, checksum_field2)) { + if (curr_algo + == SRV_CHECKSUM_ALGORITHM_STRICT_CRC32) { + page_warn_strict_checksum( + curr_algo, + SRV_CHECKSUM_ALGORITHM_NONE, + space_id, page_no); + } - crc32 = buf_calc_page_crc32(read_buf); - crc32_inited = TRUE; + return(FALSE); + } - if (checksum_field2 != crc32) { - return(TRUE); - } - } + if (buf_page_is_checksum_valid_innodb(read_buf, + checksum_field1, checksum_field2)) { + if (curr_algo + == SRV_CHECKSUM_ALGORITHM_STRICT_CRC32) { + page_warn_strict_checksum( + curr_algo, + SRV_CHECKSUM_ALGORITHM_INNODB, + space_id, page_no); } - } - /* old field is fine, check the new field */ + return(FALSE); + } - /* InnoDB versions < 4.0.14 and < 4.1.1 stored the space id - (always equal to 0), to FIL_PAGE_SPACE_OR_CHKSUM */ + return(TRUE); - if (checksum_field1 != 0 - && checksum_field1 != BUF_NO_CHECKSUM_MAGIC) { + case SRV_CHECKSUM_ALGORITHM_INNODB: + case SRV_CHECKSUM_ALGORITHM_STRICT_INNODB: - /* The checksum does not match any of the - fast to check. First check the selected algorithm - for writing checksums because we assume that the - chance of it matching is higher. */ + if (buf_page_is_checksum_valid_innodb(read_buf, + checksum_field1, checksum_field2)) { + return(FALSE); + } - if (srv_checksum_algorithm - == SRV_CHECKSUM_ALGORITHM_CRC32) { + if (buf_page_is_checksum_valid_none(read_buf, + checksum_field1, checksum_field2)) { + if (curr_algo + == SRV_CHECKSUM_ALGORITHM_STRICT_INNODB) { + page_warn_strict_checksum( + curr_algo, + SRV_CHECKSUM_ALGORITHM_NONE, + space_id, page_no); + } - if (!crc32_inited) { - crc32 = buf_calc_page_crc32(read_buf); - crc32_inited = TRUE; - } + return(FALSE); + } - if (checksum_field1 != crc32 - && checksum_field1 - != buf_calc_page_new_checksum(read_buf)) { + if (buf_page_is_checksum_valid_crc32(read_buf, + checksum_field1, checksum_field2)) { + if (curr_algo + == SRV_CHECKSUM_ALGORITHM_STRICT_INNODB) { + page_warn_strict_checksum( + curr_algo, + SRV_CHECKSUM_ALGORITHM_CRC32, + space_id, page_no); + } - return(TRUE); - } - } else { - ut_ad(srv_checksum_algorithm - == SRV_CHECKSUM_ALGORITHM_INNODB); + return(FALSE); + } - if (checksum_field1 - != buf_calc_page_new_checksum(read_buf)) { + return(TRUE); - if (!crc32_inited) { - crc32 = buf_calc_page_crc32( - read_buf); - crc32_inited = TRUE; - } + case SRV_CHECKSUM_ALGORITHM_STRICT_NONE: - if (checksum_field1 != crc32) { - return(TRUE); - } - } - } + if (buf_page_is_checksum_valid_none(read_buf, + checksum_field1, checksum_field2)) { + return(FALSE); } - /* If CRC32 is stored in at least one of the fields, then the - other field must also be CRC32 */ - if (crc32_inited - && ((checksum_field1 == crc32 - && checksum_field2 != crc32) - || (checksum_field1 != crc32 - && checksum_field2 == crc32))) { + if (buf_page_is_checksum_valid_crc32(read_buf, + checksum_field1, checksum_field2)) { + page_warn_strict_checksum( + curr_algo, + SRV_CHECKSUM_ALGORITHM_CRC32, + space_id, page_no); + return(FALSE); + } - return(TRUE); + if (buf_page_is_checksum_valid_innodb(read_buf, + checksum_field1, checksum_field2)) { + page_warn_strict_checksum( + curr_algo, + SRV_CHECKSUM_ALGORITHM_INNODB, + space_id, page_no); + return(FALSE); } - break; + return(TRUE); + case SRV_CHECKSUM_ALGORITHM_NONE: /* should have returned FALSE earlier */ - ut_error; + break; /* no default so the compiler will emit a warning if new enum is added and not handled here */ } - DBUG_EXECUTE_IF("buf_page_is_corrupt_failure", return(TRUE); ); - + ut_error; return(FALSE); } @@ -1084,6 +1141,9 @@ buf_block_init( block->page.key_version = 0; block->page.page_encrypted = false; block->page.page_compressed = false; + block->page.encrypted = false; + block->page.stored_checksum = BUF_NO_CHECKSUM_MAGIC; + block->page.calculated_checksum = BUF_NO_CHECKSUM_MAGIC; block->page.real_size = 0; block->page.write_size = 0; block->modify_clock = 0; @@ -1817,6 +1877,9 @@ page_found: goto page_found; } + /* The maximum number of purge threads should never exceed + BUF_POOL_WATCH_SIZE. So there is no way for purge thread + instance to hold a watch when setting another watch. */ for (i = 0; i < BUF_POOL_WATCH_SIZE; i++) { bpage = &buf_pool->watch[i]; @@ -2187,7 +2250,7 @@ lookup: /* Page not in buf_pool: needs to be read from file */ ut_ad(!hash_lock); - buf_read_page(space, zip_size, offset, trx); + buf_read_page(space, zip_size, offset, trx, NULL); #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG ut_a(++buf_dbg_counter % 5771 || buf_validate()); @@ -2706,7 +2769,8 @@ buf_page_get_gen( BUF_GET_IF_IN_POOL_OR_WATCH */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ - mtr_t* mtr) /*!< in: mini-transaction */ + mtr_t* mtr, /*!< in: mini-transaction */ + dberr_t* err) /*!< out: error code */ { buf_block_t* block; ulint fold; @@ -2724,6 +2788,11 @@ buf_page_get_gen( ut_ad((rw_latch == RW_S_LATCH) || (rw_latch == RW_X_LATCH) || (rw_latch == RW_NO_LATCH)); + + if (err) { + *err = DB_SUCCESS; + } + #ifdef UNIV_DEBUG switch (mode) { case BUF_GET_NO_LATCH: @@ -2787,6 +2856,8 @@ loop: } if (block == NULL) { + buf_page_t* bpage=NULL; + /* Page not in buf_pool: needs to be read from file */ if (mode == BUF_GET_IF_IN_POOL_OR_WATCH) { @@ -2821,36 +2892,77 @@ loop: return(NULL); } - if (buf_read_page(space, zip_size, offset, trx)) { + if (buf_read_page(space, zip_size, offset, trx, &bpage)) { buf_read_ahead_random(space, zip_size, offset, ibuf_inside(mtr), trx); retries = 0; } else if (retries < BUF_PAGE_READ_MAX_RETRIES) { ++retries; + + bool corrupted = true; + + if (bpage) { + corrupted = buf_page_check_corrupt(bpage); + } + + /* Do not try again for encrypted pages */ + if (!corrupted) { + ib_mutex_t* pmutex = buf_page_get_mutex(bpage); + mutex_enter(&buf_pool->LRU_list_mutex); + mutex_enter(pmutex); + buf_page_set_io_fix(bpage, BUF_IO_NONE); + buf_LRU_free_page(bpage, zip_size ? true : false); + mutex_exit(pmutex); + + if (err) { + *err = DB_ENCRYPTED_DECRYPT_FAILED; + } + return (NULL); + } + DBUG_EXECUTE_IF( "innodb_page_corruption_retries", retries = BUF_PAGE_READ_MAX_RETRIES; ); } else { + bool corrupted = true; - fprintf(stderr, "InnoDB: Error: Unable" - " to read tablespace %lu page no" - " %lu into the buffer pool after" - " %lu attempts\n" - "InnoDB: The most probable cause" - " of this error may be that the" - " table has been corrupted.\n" - "InnoDB: You can try to fix this" - " problem by using" - " innodb_force_recovery.\n" - "InnoDB: Please see reference manual" - " for more details.\n" - "InnoDB: Aborting...\n", - space, offset, - BUF_PAGE_READ_MAX_RETRIES); + if (bpage) { + corrupted = buf_page_check_corrupt(bpage); + } - ut_error; + if (corrupted) { + fprintf(stderr, "InnoDB: Error: Unable" + " to read tablespace %lu page no" + " %lu into the buffer pool after" + " %lu attempts\n" + "InnoDB: The most probable cause" + " of this error may be that the" + " table has been corrupted.\n" + "InnoDB: You can try to fix this" + " problem by using" + " innodb_force_recovery.\n" + "InnoDB: Please see reference manual" + " for more details.\n" + "InnoDB: Aborting...\n", + space, offset, + BUF_PAGE_READ_MAX_RETRIES); + + ut_error; + } else { + ib_mutex_t* pmutex = buf_page_get_mutex(bpage); + mutex_enter(&buf_pool->LRU_list_mutex); + mutex_enter(pmutex); + buf_page_set_io_fix(bpage, BUF_IO_NONE); + buf_LRU_free_page(bpage, zip_size ? true : false); + mutex_exit(pmutex); + + if (err) { + *err = DB_ENCRYPTED_DECRYPT_FAILED; + } + return (NULL); + } } #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG @@ -3623,8 +3735,11 @@ buf_page_init_low( bpage->oldest_modification = 0; bpage->write_size = 0; bpage->key_version = 0; + bpage->stored_checksum = BUF_NO_CHECKSUM_MAGIC; + bpage->calculated_checksum = BUF_NO_CHECKSUM_MAGIC; bpage->page_encrypted = false; bpage->page_compressed = false; + bpage->encrypted = false; bpage->real_size = 0; HASH_INVALIDATE(bpage, hash); @@ -4294,33 +4409,41 @@ buf_mark_space_corrupt( /* First unfix and release lock on the bpage */ ut_ad(!mutex_own(&buf_pool->LRU_list_mutex)); - mutex_enter(&buf_pool->LRU_list_mutex); - rw_lock_x_lock(hash_lock); - mutex_enter(buf_page_get_mutex(bpage)); - ut_ad(buf_page_get_io_fix(bpage) == BUF_IO_READ); - ut_ad(bpage->buf_fix_count == 0); - /* Set BUF_IO_NONE before we remove the block from LRU list */ - buf_page_set_io_fix(bpage, BUF_IO_NONE); + if (!bpage->encrypted) { + mutex_enter(&buf_pool->LRU_list_mutex); + rw_lock_x_lock(hash_lock); + mutex_enter(buf_page_get_mutex(bpage)); + ut_ad(buf_page_get_io_fix(bpage) == BUF_IO_READ); + ut_ad(bpage->buf_fix_count == 0); + + /* Set BUF_IO_NONE before we remove the block from LRU list */ + buf_page_set_io_fix(bpage, BUF_IO_NONE); - if (uncompressed) { - rw_lock_x_unlock_gen( - &((buf_block_t*) bpage)->lock, - BUF_IO_READ); + if (uncompressed) { + rw_lock_x_unlock_gen( + &((buf_block_t*) bpage)->lock, + BUF_IO_READ); + } } /* Find the table with specified space id, and mark it corrupted */ if (dict_set_corrupted_by_space(space)) { - buf_LRU_free_one_page(bpage); + if (!bpage->encrypted) { + buf_LRU_free_one_page(bpage); + } } else { - mutex_exit(buf_page_get_mutex(bpage)); + if (!bpage->encrypted) { + mutex_exit(buf_page_get_mutex(bpage)); + } ret = FALSE; } - mutex_exit(&buf_pool->LRU_list_mutex); - - ut_ad(buf_pool->n_pend_reads > 0); - os_atomic_decrement_ulint(&buf_pool->n_pend_reads, 1); + if(!bpage->encrypted) { + mutex_exit(&buf_pool->LRU_list_mutex); + ut_ad(buf_pool->n_pend_reads > 0); + os_atomic_decrement_ulint(&buf_pool->n_pend_reads, 1); + } return(ret); } @@ -4331,42 +4454,77 @@ corrupted page. Note that we can't be 100% sure if page is corrupted or decrypt/decompress just failed. */ static -void +ibool buf_page_check_corrupt( /*===================*/ - const buf_page_t* bpage) /*!< in/out: buffer page read from disk */ + buf_page_t* bpage) /*!< in/out: buffer page read from disk */ { ulint zip_size = buf_page_get_zip_size(bpage); byte* dst_frame = (zip_size) ? bpage->zip.data : ((buf_block_t*) bpage)->frame; unsigned key_version = bpage->key_version; bool page_compressed = bpage->page_encrypted; + ulint stored_checksum = bpage->stored_checksum; + ulint calculated_checksum = bpage->stored_checksum; bool page_compressed_encrypted = bpage->page_compressed; ulint space_id = mach_read_from_4( dst_frame + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID); fil_space_crypt_t* crypt_data = fil_space_get_crypt_data(space_id); fil_space_t* space = fil_space_found_by_id(space_id); + bool corrupted = true; + + if (key_version != 0 || page_compressed_encrypted) { + bpage->encrypted = true; + } if (key_version != 0 || (crypt_data && crypt_data->type != CRYPT_SCHEME_UNENCRYPTED) || page_compressed || page_compressed_encrypted) { - ib_logf(IB_LOG_LEVEL_ERROR, - "Maybe corruption: Block space_id %lu in file %s maybe corrupted.", - space_id, space ? space->name : "NULL"); - ib_logf(IB_LOG_LEVEL_ERROR, - "Page based on contents %s encrypted.", - (key_version == 0 && page_compressed_encrypted == false) ? "not" : "maybe"); - ib_logf(IB_LOG_LEVEL_ERROR, - "Reason could be that key_version %u in page " - "or in crypt_data %p could not be found.", - key_version, crypt_data); - ib_logf(IB_LOG_LEVEL_ERROR, - "Reason could be also that key management plugin is not found or" - "used encryption algorithm or method does not match."); - ib_logf(IB_LOG_LEVEL_ERROR, - "Based on page page compressed %d, compressed and encrypted %d.", - page_compressed, page_compressed_encrypted); + + /* Page is really corrupted if post encryption stored + checksum does not match calculated checksum after page was + read. For pages compressed and then encrypted, there is no + checksum. */ + corrupted = (!page_compressed_encrypted && stored_checksum != calculated_checksum); + + if (corrupted) { + ib_logf(IB_LOG_LEVEL_ERROR, + "%s: Block in space_id %lu in file %s corrupted.", + page_compressed_encrypted ? "Maybe corruption" : "Corruption", + space_id, space ? space->name : "NULL"); + ib_logf(IB_LOG_LEVEL_ERROR, + "Page based on contents %s encrypted.", + (key_version == 0 && page_compressed_encrypted == false) ? "not" : "maybe"); + if (stored_checksum != BUF_NO_CHECKSUM_MAGIC || calculated_checksum != BUF_NO_CHECKSUM_MAGIC) { + ib_logf(IB_LOG_LEVEL_ERROR, + "Page stored checksum %lu but calculated checksum %lu.", + stored_checksum, calculated_checksum); + } + ib_logf(IB_LOG_LEVEL_ERROR, + "Reason could be that key_version %u in page " + "or in crypt_data %p could not be found.", + key_version, crypt_data); + ib_logf(IB_LOG_LEVEL_ERROR, + "Reason could be also that key management plugin is not found or" + " used encryption algorithm or method does not match."); + ib_logf(IB_LOG_LEVEL_ERROR, + "Based on page page compressed %d, compressed and encrypted %d.", + page_compressed, page_compressed_encrypted); + } else { + ib_logf(IB_LOG_LEVEL_ERROR, + "Block in space_id %lu in file %s encrypted.", + space_id, space ? space->name : "NULL"); + ib_logf(IB_LOG_LEVEL_ERROR, + "However key management plugin or used key_id %u is not found or" + " used encryption algorithm or method does not match.", + key_version); + ib_logf(IB_LOG_LEVEL_ERROR, + "Marking tablespace as missing. You may drop this table or" + " install correct key management plugin and key file."); + } } + + return corrupted; } /********************************************************************//** @@ -4487,42 +4645,46 @@ buf_page_io_complete( ;); corrupt: - fil_system_enter(); - space = fil_space_get_by_id(bpage->space); - fil_system_exit(); - ib_logf(IB_LOG_LEVEL_ERROR, - "Database page corruption on disk" - " or a failed"); - ib_logf(IB_LOG_LEVEL_ERROR, - "Space %lu file %s read of page %lu.", - (ulint)bpage->space, - space ? space->name : "NULL", - (ulong) bpage->offset); - ib_logf(IB_LOG_LEVEL_ERROR, - "You may have to recover" - " from a backup."); - - buf_page_check_corrupt(bpage); - - buf_page_print(frame, buf_page_get_zip_size(bpage), - BUF_PAGE_PRINT_NO_CRASH); - ib_logf(IB_LOG_LEVEL_ERROR, - "It is also possible that your operating" - "system has corrupted its own file cache."); - ib_logf(IB_LOG_LEVEL_ERROR, - "and rebooting your computer removes the error."); - ib_logf(IB_LOG_LEVEL_ERROR, - "If the corrupt page is an index page you can also try to"); - ib_logf(IB_LOG_LEVEL_ERROR, - "fix the corruption by dumping, dropping, and reimporting"); - ib_logf(IB_LOG_LEVEL_ERROR, - "the corrupt table. You can use CHECK"); - ib_logf(IB_LOG_LEVEL_ERROR, - "TABLE to scan your table for corruption."); - ib_logf(IB_LOG_LEVEL_ERROR, - "See also " - REFMAN "forcing-innodb-recovery.html" - " about forcing recovery."); + bool corrupted = buf_page_check_corrupt(bpage); + + if (corrupted) { + fil_system_enter(); + space = fil_space_get_by_id(bpage->space); + fil_system_exit(); + ib_logf(IB_LOG_LEVEL_ERROR, + "Database page corruption on disk" + " or a failed"); + ib_logf(IB_LOG_LEVEL_ERROR, + "Space %lu file %s read of page %lu.", + (ulint)bpage->space, + space ? space->name : "NULL", + (ulong) bpage->offset); + ib_logf(IB_LOG_LEVEL_ERROR, + "You may have to recover" + " from a backup."); + + + buf_page_print(frame, buf_page_get_zip_size(bpage), + BUF_PAGE_PRINT_NO_CRASH); + + ib_logf(IB_LOG_LEVEL_ERROR, + "It is also possible that your operating" + "system has corrupted its own file cache."); + ib_logf(IB_LOG_LEVEL_ERROR, + "and rebooting your computer removes the error."); + ib_logf(IB_LOG_LEVEL_ERROR, + "If the corrupt page is an index page you can also try to"); + ib_logf(IB_LOG_LEVEL_ERROR, + "fix the corruption by dumping, dropping, and reimporting"); + ib_logf(IB_LOG_LEVEL_ERROR, + "the corrupt table. You can use CHECK"); + ib_logf(IB_LOG_LEVEL_ERROR, + "TABLE to scan your table for corruption."); + ib_logf(IB_LOG_LEVEL_ERROR, + "See also " + REFMAN "forcing-innodb-recovery.html" + " about forcing recovery."); + } if (srv_pass_corrupt_table && bpage->space != 0 && bpage->space < SRV_LOG_SPACE_FIRST_ID) { @@ -4550,12 +4712,30 @@ corrupt: && buf_mark_space_corrupt(bpage)) { return(false); } else { - buf_page_check_corrupt(bpage); + corrupted = buf_page_check_corrupt(bpage); - ib_logf(IB_LOG_LEVEL_ERROR, - "Ending processing because of a corrupt database page."); + if (corrupted) { + ib_logf(IB_LOG_LEVEL_ERROR, + "Ending processing because of a corrupt database page."); - ut_error; + ut_error; + } + + ib_push_warning(innobase_get_trx(), DB_ENCRYPTED_DECRYPT_FAILED, + "Table in tablespace %lu encrypted." + "However key management plugin or used key_id %lu is not found or" + " used encryption algorithm or method does not match." + " Can't continue opening the table.", + bpage->key_version); + + if (bpage->space > TRX_SYS_SPACE) { + if (corrupted) { + buf_mark_space_corrupt(bpage); + } + } else { + ut_error; + } + return(false); } } } @@ -4733,11 +4913,13 @@ buf_all_freed_instance( mutex_exit(&buf_pool->LRU_list_mutex); if (UNIV_LIKELY_NULL(block)) { - fprintf(stderr, - "Page %lu %lu still fixed or dirty\n", - (ulong) block->page.space, - (ulong) block->page.offset); - ut_error; + if (block->page.key_version == 0) { + fprintf(stderr, + "Page %lu %lu still fixed or dirty\n", + (ulong) block->page.space, + (ulong) block->page.offset); + ut_error; + } } } @@ -6077,6 +6259,11 @@ buf_page_decrypt_after_read( bool page_compressed_encrypted = fil_page_is_compressed_encrypted(dst_frame); buf_pool_t* buf_pool = buf_pool_from_bpage(bpage); + /* If page is encrypted read post-encryption checksum */ + if (!page_compressed_encrypted && key_version != 0) { + bpage->stored_checksum = mach_read_from_4(dst_frame + + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION + 4); + } + ut_ad(bpage->key_version == 0); if (bpage->offset == 0) { @@ -6121,6 +6308,13 @@ buf_page_decrypt_after_read( #ifdef UNIV_DEBUG fil_page_type_validate(dst_frame); #endif + + /* Calculate checksum before decrypt, this will be + used later to find out if incorrect key was used. */ + if (!page_compressed_encrypted) { + bpage->calculated_checksum = fil_crypt_calculate_checksum(zip_size, dst_frame); + } + /* decrypt using crypt_buf to dst_frame */ fil_space_decrypt(bpage->space, slot->crypt_buf, diff --git a/storage/xtradb/buf/buf0checksum.cc b/storage/xtradb/buf/buf0checksum.cc index 451fef2f82e..01b646a78e0 100644 --- a/storage/xtradb/buf/buf0checksum.cc +++ b/storage/xtradb/buf/buf0checksum.cc @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1995, 2011, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 1995, 2015, Oracle and/or its affiliates. All Rights Reserved. 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 @@ -27,11 +27,13 @@ Created Aug 11, 2011 Vasil Dimov #include "fil0fil.h" /* FIL_* */ #include "ut0crc32.h" /* ut_crc32() */ #include "ut0rnd.h" /* ut_fold_binary() */ +#include "buf0types.h" #ifndef UNIV_INNOCHECKSUM #include "srv0srv.h" /* SRV_CHECKSUM_* */ -#include "buf0types.h" + +#endif /* !UNIV_INNOCHECKSUM */ /** the macro MYSQL_SYSVAR_ENUM() requires "long unsigned int" and if we use srv_checksum_algorithm_t here then we get a compiler error: @@ -39,8 +41,6 @@ ha_innodb.cc:12251: error: cannot convert 'srv_checksum_algorithm_t*' to 'long unsigned int*' in initialization */ UNIV_INTERN ulong srv_checksum_algorithm = SRV_CHECKSUM_ALGORITHM_INNODB; -#endif /* !UNIV_INNOCHECKSUM */ - /********************************************************************//** Calculates a page CRC32 which is stored to the page when it is written to a file. Note that we must be careful to calculate the same value on @@ -127,8 +127,6 @@ buf_calc_page_old_checksum( return(checksum); } -#ifndef UNIV_INNOCHECKSUM - /********************************************************************//** Return a printable string describing the checksum algorithm. @return algorithm name */ @@ -140,18 +138,19 @@ buf_checksum_algorithm_name( { switch (algo) { case SRV_CHECKSUM_ALGORITHM_CRC32: - case SRV_CHECKSUM_ALGORITHM_STRICT_CRC32: return("crc32"); + case SRV_CHECKSUM_ALGORITHM_STRICT_CRC32: + return("strict_crc32"); case SRV_CHECKSUM_ALGORITHM_INNODB: - case SRV_CHECKSUM_ALGORITHM_STRICT_INNODB: return("innodb"); + case SRV_CHECKSUM_ALGORITHM_STRICT_INNODB: + return("strict_innodb"); case SRV_CHECKSUM_ALGORITHM_NONE: - case SRV_CHECKSUM_ALGORITHM_STRICT_NONE: return("none"); + case SRV_CHECKSUM_ALGORITHM_STRICT_NONE: + return("strict_none"); } ut_error; return(NULL); } - -#endif /* !UNIV_INNOCHECKSUM */ diff --git a/storage/xtradb/buf/buf0flu.cc b/storage/xtradb/buf/buf0flu.cc index 6443043310b..c891e53381a 100644 --- a/storage/xtradb/buf/buf0flu.cc +++ b/storage/xtradb/buf/buf0flu.cc @@ -1,8 +1,8 @@ /***************************************************************************** -Copyright (c) 1995, 2013, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2013, 2014, SkySQL Ab. All Rights Reserved. -Copyright (c) 2013, 2014, Fusion-io. All Rights Reserved. +Copyright (c) 1995, 2013, Oracle and/or its affiliates +Copyright (c) 2013, 2015, SkySQL Ab +Copyright (c) 2013, 2014, Fusion-io 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 @@ -932,11 +932,11 @@ buf_flush_write_block_low( case BUF_BLOCK_ZIP_DIRTY: frame = bpage->zip.data; - ut_a(page_zip_verify_checksum(frame, zip_size)); - mach_write_to_8(frame + FIL_PAGE_LSN, bpage->newest_modification); memset(frame + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION, 0, 8); + + ut_a(page_zip_verify_checksum(frame, zip_size)); break; case BUF_BLOCK_FILE_PAGE: frame = bpage->zip.data; diff --git a/storage/xtradb/buf/buf0rea.cc b/storage/xtradb/buf/buf0rea.cc index 6c74c30739e..15465699726 100644 --- a/storage/xtradb/buf/buf0rea.cc +++ b/storage/xtradb/buf/buf0rea.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2013, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2013, 2014, SkySQL Ab. All Rights Reserved. +Copyright (c) 2013, 2014, MariaDB Corporation. All Rights Reserved. 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 @@ -124,7 +124,8 @@ buf_read_page_low( use to stop dangling page reads from a tablespace which we have DISCARDed + IMPORTed back */ ulint offset, /*!< in: page number */ - trx_t* trx) + trx_t* trx, /*!< in: trx */ + buf_page_t** rbpage) /*!< out: page */ { buf_page_t* bpage; ulint wake_later; @@ -259,10 +260,17 @@ not_to_recover: /* The i/o is already completed when we arrive from fil_read */ if (!buf_page_io_complete(bpage)) { + if (rbpage) { + *rbpage = bpage; + } return(0); } } + if (rbpage) { + *rbpage = bpage; + } + return(1); } @@ -398,7 +406,7 @@ read_ahead: &err, false, ibuf_mode | OS_AIO_SIMULATED_WAKE_LATER, space, zip_size, FALSE, - tablespace_version, i, trx); + tablespace_version, i, trx, NULL); if (err == DB_TABLESPACE_DELETED) { ut_print_timestamp(stderr); fprintf(stderr, @@ -446,10 +454,11 @@ UNIV_INTERN ibool buf_read_page( /*==========*/ - ulint space, /*!< in: space id */ - ulint zip_size,/*!< in: compressed page size in bytes, or 0 */ - ulint offset, /*!< in: page number */ - trx_t* trx) + ulint space, /*!< in: space id */ + ulint zip_size, /*!< in: compressed page size in bytes, or 0 */ + ulint offset, /*!< in: page number */ + trx_t* trx, /*!< in: trx */ + buf_page_t** bpage) /*!< out: page */ { ib_int64_t tablespace_version; ulint count; @@ -462,7 +471,7 @@ buf_read_page( count = buf_read_page_low(&err, true, BUF_READ_ANY_PAGE, space, zip_size, FALSE, - tablespace_version, offset, trx); + tablespace_version, offset, trx, bpage); srv_stats.buf_pool_reads.add(count); if (err == DB_TABLESPACE_DELETED) { ut_print_timestamp(stderr); @@ -510,7 +519,7 @@ buf_read_page_async( | OS_AIO_SIMULATED_WAKE_LATER | BUF_READ_IGNORE_NONEXISTENT_PAGES, space, zip_size, FALSE, - tablespace_version, offset, NULL); + tablespace_version, offset, NULL,NULL); srv_stats.buf_pool_reads.add(count); /* We do not increment number of I/O operations used for LRU policy @@ -778,7 +787,7 @@ buf_read_ahead_linear( count += buf_read_page_low( &err, false, ibuf_mode, - space, zip_size, FALSE, tablespace_version, i, trx); + space, zip_size, FALSE, tablespace_version, i, trx, NULL); if (err == DB_TABLESPACE_DELETED) { ut_print_timestamp(stderr); fprintf(stderr, @@ -868,7 +877,7 @@ buf_read_ibuf_merge_pages( buf_read_page_low(&err, sync && (i + 1 == n_stored), BUF_READ_ANY_PAGE, space_ids[i], zip_size, TRUE, space_versions[i], - page_nos[i], NULL); + page_nos[i], NULL, NULL); if (UNIV_UNLIKELY(err == DB_TABLESPACE_DELETED)) { tablespace_deleted: @@ -1008,12 +1017,12 @@ not_to_recover: if ((i + 1 == n_stored) && sync) { buf_read_page_low(&err, true, BUF_READ_ANY_PAGE, space, zip_size, TRUE, tablespace_version, - page_nos[i], NULL); + page_nos[i], NULL, NULL); } else { buf_read_page_low(&err, false, BUF_READ_ANY_PAGE | OS_AIO_SIMULATED_WAKE_LATER, space, zip_size, TRUE, - tablespace_version, page_nos[i], NULL); + tablespace_version, page_nos[i], NULL, NULL); } } diff --git a/storage/xtradb/dict/dict0crea.cc b/storage/xtradb/dict/dict0crea.cc index 36a30cb75b7..fec4e27f058 100644 --- a/storage/xtradb/dict/dict0crea.cc +++ b/storage/xtradb/dict/dict0crea.cc @@ -1566,12 +1566,111 @@ dict_create_add_foreign_field_to_dictionary( } /********************************************************************//** +Construct foreign key constraint defintion from data dictionary information. +*/ +UNIV_INTERN +char* +dict_foreign_def_get( +/*=================*/ + dict_foreign_t* foreign,/*!< in: foreign */ + trx_t* trx) /*!< in: trx */ +{ + char* fk_def = (char *)mem_heap_alloc(foreign->heap, 4*1024); + const char* tbname; + char tablebuf[MAX_TABLE_NAME_LEN + 1] = ""; + int i; + char* bufend; + + tbname = dict_remove_db_name(foreign->id); + bufend = innobase_convert_name(tablebuf, MAX_TABLE_NAME_LEN, + tbname, strlen(tbname), trx->mysql_thd, FALSE); + tablebuf[bufend - tablebuf] = '\0'; + + sprintf(fk_def, + (char *)"CONSTRAINT %s FOREIGN KEY (", (char *)tablebuf); + + for(i = 0; i < foreign->n_fields; i++) { + char buf[MAX_TABLE_NAME_LEN + 1] = ""; + innobase_convert_name(buf, MAX_TABLE_NAME_LEN, + foreign->foreign_col_names[i], + strlen(foreign->foreign_col_names[i]), + trx->mysql_thd, FALSE); + strcat(fk_def, buf); + if (i < foreign->n_fields-1) { + strcat(fk_def, (char *)","); + } + } + + strcat(fk_def,(char *)") REFERENCES "); + + bufend = innobase_convert_name(tablebuf, MAX_TABLE_NAME_LEN, + foreign->referenced_table_name, + strlen(foreign->referenced_table_name), + trx->mysql_thd, TRUE); + tablebuf[bufend - tablebuf] = '\0'; + + strcat(fk_def, tablebuf); + strcat(fk_def, " ("); + + for(i = 0; i < foreign->n_fields; i++) { + char buf[MAX_TABLE_NAME_LEN + 1] = ""; + bufend = innobase_convert_name(buf, MAX_TABLE_NAME_LEN, + foreign->referenced_col_names[i], + strlen(foreign->referenced_col_names[i]), + trx->mysql_thd, FALSE); + buf[bufend - buf] = '\0'; + strcat(fk_def, buf); + if (i < foreign->n_fields-1) { + strcat(fk_def, (char *)","); + } + } + strcat(fk_def, (char *)")"); + + return fk_def; +} + +/********************************************************************//** +Convert foreign key column names from data dictionary to SQL-layer. +*/ +static +void +dict_foreign_def_get_fields( +/*========================*/ + dict_foreign_t* foreign,/*!< in: foreign */ + trx_t* trx, /*!< in: trx */ + char** field, /*!< out: foreign column */ + char** field2, /*!< out: referenced column */ + int col_no) /*!< in: column number */ +{ + char* bufend; + char* fieldbuf = (char *)mem_heap_alloc(foreign->heap, MAX_TABLE_NAME_LEN+1); + char* fieldbuf2 = (char *)mem_heap_alloc(foreign->heap, MAX_TABLE_NAME_LEN+1); + + bufend = innobase_convert_name(fieldbuf, MAX_TABLE_NAME_LEN, + foreign->foreign_col_names[col_no], + strlen(foreign->foreign_col_names[col_no]), + trx->mysql_thd, FALSE); + + fieldbuf[bufend - fieldbuf] = '\0'; + + bufend = innobase_convert_name(fieldbuf2, MAX_TABLE_NAME_LEN, + foreign->referenced_col_names[col_no], + strlen(foreign->referenced_col_names[col_no]), + trx->mysql_thd, FALSE); + + fieldbuf2[bufend - fieldbuf2] = '\0'; + *field = fieldbuf; + *field2 = fieldbuf2; +} + +/********************************************************************//** Add a foreign key definition to the data dictionary tables. @return error code or DB_SUCCESS */ UNIV_INTERN dberr_t dict_create_add_foreign_to_dictionary( /*==================================*/ + dict_table_t* table, const char* name, /*!< in: table name */ const dict_foreign_t* foreign,/*!< in: foreign key */ trx_t* trx) /*!< in/out: dictionary transaction */ @@ -1598,6 +1697,28 @@ dict_create_add_foreign_to_dictionary( , name, foreign->id, trx); if (error != DB_SUCCESS) { + if (error == DB_DUPLICATE_KEY) { + char buf[MAX_TABLE_NAME_LEN + 1] = ""; + char tablename[MAX_TABLE_NAME_LEN + 1] = ""; + char* fk_def; + + innobase_convert_name(tablename, MAX_TABLE_NAME_LEN, + table->name, strlen(table->name), + trx->mysql_thd, TRUE); + + innobase_convert_name(buf, MAX_TABLE_NAME_LEN, + foreign->id, strlen(foreign->id), trx->mysql_thd, FALSE); + + fk_def = dict_foreign_def_get((dict_foreign_t*)foreign, trx); + + ib_push_warning(trx, error, + "Create or Alter table %s with foreign key constraint" + " failed. Foreign key constraint %s" + " already exists on data dictionary." + " Foreign key constraint names need to be unique in database." + " Error in foreign key definition: %s.", + tablename, buf, fk_def); + } return(error); } @@ -1607,6 +1728,26 @@ dict_create_add_foreign_to_dictionary( i, name, foreign, trx); if (error != DB_SUCCESS) { + char buf[MAX_TABLE_NAME_LEN + 1] = ""; + char tablename[MAX_TABLE_NAME_LEN + 1] = ""; + char* field=NULL; + char* field2=NULL; + char* fk_def; + + innobase_convert_name(tablename, MAX_TABLE_NAME_LEN, + table->name, strlen(table->name), + trx->mysql_thd, TRUE); + innobase_convert_name(buf, MAX_TABLE_NAME_LEN, + foreign->id, strlen(foreign->id), trx->mysql_thd, FALSE); + fk_def = dict_foreign_def_get((dict_foreign_t*)foreign, trx); + dict_foreign_def_get_fields((dict_foreign_t*)foreign, trx, &field, &field2, i); + + ib_push_warning(trx, error, + "Create or Alter table %s with foreign key constraint" + " failed. Error adding foreign key constraint name %s" + " fields %s or %s to the dictionary." + " Error in foreign key definition: %s.", + tablename, buf, i+1, fk_def); return(error); } @@ -1653,7 +1794,7 @@ dict_create_add_foreigns_to_dictionary( foreign = *it; ut_ad(foreign->id != NULL); - error = dict_create_add_foreign_to_dictionary(table->name, + error = dict_create_add_foreign_to_dictionary((dict_table_t*)table, table->name, foreign, trx); if (error != DB_SUCCESS) { diff --git a/storage/xtradb/dict/dict0dict.cc b/storage/xtradb/dict/dict0dict.cc index 811d737e6bf..0e1906a1827 100644 --- a/storage/xtradb/dict/dict0dict.cc +++ b/storage/xtradb/dict/dict0dict.cc @@ -3280,6 +3280,11 @@ dict_index_build_internal_fts( } /*====================== FOREIGN KEY PROCESSING ========================*/ +#define DB_FOREIGN_KEY_IS_PREFIX_INDEX 200 +#define DB_FOREIGN_KEY_COL_NOT_NULL 201 +#define DB_FOREIGN_KEY_COLS_NOT_EQUAL 202 +#define DB_FOREIGN_KEY_INDEX_NOT_FOUND 203 + /*********************************************************************//** Checks if a table is referenced by foreign keys. @return TRUE if table is referenced by a foreign key */ @@ -3434,15 +3439,26 @@ dict_foreign_find_index( /*!< in: whether to check charsets. only has an effect if types_idx != NULL */ - ulint check_null) + ulint check_null, /*!< in: nonzero if none of the columns must be declared NOT NULL */ + ulint* error, /*!< out: error code */ + ulint* err_col_no, + /*!< out: column number where + error happened */ + dict_index_t** err_index) + /*!< out: index where error + happened */ { dict_index_t* index; ut_ad(mutex_own(&dict_sys->mutex)); + if (error) { + *error = DB_FOREIGN_KEY_INDEX_NOT_FOUND; + } + index = dict_table_get_first_index(table); while (index != NULL) { @@ -3452,7 +3468,12 @@ dict_foreign_find_index( && dict_foreign_qualify_index( table, col_names, columns, n_cols, index, types_idx, - check_charsets, check_null)) { + check_charsets, check_null, + error, err_col_no,err_index)) { + if (error) { + *error = DB_SUCCESS; + } + return(index); } @@ -3481,7 +3502,7 @@ wsrep_dict_foreign_find_index( { return dict_foreign_find_index( table, col_names, columns, n_cols, types_idx, check_charsets, - check_null); + check_null, NULL, NULL, NULL); } #endif /* WITH_WSREP */ /**********************************************************************//** @@ -3583,7 +3604,7 @@ dict_foreign_add_to_cache( ref_table, NULL, for_in_cache->referenced_col_names, for_in_cache->n_fields, for_in_cache->foreign_index, - check_charsets, false); + check_charsets, false, NULL, NULL, NULL); if (index == NULL && !(ignore_err & DICT_ERR_IGNORE_FK_NOKEY)) { @@ -3615,6 +3636,10 @@ dict_foreign_add_to_cache( } if (for_table && !for_in_cache->foreign_table) { + ulint index_error; + ulint err_col; + dict_index_t *err_index=NULL; + index = dict_foreign_find_index( for_table, col_names, for_in_cache->foreign_col_names, @@ -3622,7 +3647,8 @@ dict_foreign_add_to_cache( for_in_cache->referenced_index, check_charsets, for_in_cache->type & (DICT_FOREIGN_ON_DELETE_SET_NULL - | DICT_FOREIGN_ON_UPDATE_SET_NULL)); + | DICT_FOREIGN_ON_UPDATE_SET_NULL), + &index_error, &err_col, &err_index); if (index == NULL && !(ignore_err & DICT_ERR_IGNORE_FK_NOKEY)) { @@ -4282,6 +4308,8 @@ static void dict_foreign_report_syntax_err( /*===========================*/ + const char* fmt, /*!< in: syntax err msg */ + const char* oper, /*!< in: operation */ const char* name, /*!< in: table name */ const char* start_of_latest_foreign, /*!< in: start of the foreign key clause @@ -4294,12 +4322,102 @@ dict_foreign_report_syntax_err( mutex_enter(&dict_foreign_err_mutex); dict_foreign_error_report_low(ef, name); - fprintf(ef, "%s:\nSyntax error close to:\n%s\n", - start_of_latest_foreign, ptr); + fprintf(ef, fmt, oper, name, start_of_latest_foreign, ptr); mutex_exit(&dict_foreign_err_mutex); } /*********************************************************************//** +Push warning message to SQL-layer based on foreign key constraint +index match error. */ +static +void +dict_foreign_push_index_error( +/*==========================*/ + trx_t* trx, /*!< in: trx */ + const char* operation, /*!< in: operation create or alter + */ + const char* create_name, /*!< in: table name in create or + alter table */ + const char* latest_foreign, /*!< in: start of latest foreign key + constraint name */ + const char** columns, /*!< in: foreign key columns */ + ulint index_error, /*!< in: error code */ + ulint err_col, /*!< in: column where error happened + */ + dict_index_t* err_index, /*!< in: index where error happened + */ + dict_table_t* table, /*!< in: table */ + FILE* ef) /*!< in: output stream */ +{ + switch (index_error) { + case DB_FOREIGN_KEY_INDEX_NOT_FOUND: { + fprintf(ef, + "%s table '%s' with foreign key constraint" + " failed. There is no index in the referenced" + " table where the referenced columns appear" + " as the first columns. Error close to %s.\n", + operation, create_name, latest_foreign); + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table '%s' with foreign key constraint" + " failed. There is no index in the referenced" + " table where the referenced columns appear" + " as the first columns. Error close to %s.", + operation, create_name, latest_foreign); + break; + } + case DB_FOREIGN_KEY_IS_PREFIX_INDEX: { + fprintf(ef, + "%s table '%s' with foreign key constraint" + " failed. There is only prefix index in the referenced" + " table where the referenced columns appear" + " as the first columns. Error close to %s.\n", + operation, create_name, latest_foreign); + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table '%s' with foreign key constraint" + " failed. There is only prefix index in the referenced" + " table where the referenced columns appear" + " as the first columns. Error close to %s.", + operation, create_name, latest_foreign); + break; + } + case DB_FOREIGN_KEY_COL_NOT_NULL: { + fprintf(ef, + "%s table %s with foreign key constraint" + " failed. You have defined a SET NULL condition but " + "field %s on index is defined as NOT NULL close to %s\n", + operation, create_name, columns[err_col], latest_foreign); + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. You have defined a SET NULL condition but " + "field %s on index is defined as NOT NULL close to %s", + operation, create_name, columns[err_col], latest_foreign); + break; + } + case DB_FOREIGN_KEY_COLS_NOT_EQUAL: { + dict_field_t* field; + const char* col_name; + field = dict_index_get_nth_field(err_index, err_col); + + col_name = dict_table_get_col_name( + table, dict_col_get_no(field->col)); + fprintf(ef, + "%s table %s with foreign key constraint" + " failed. Field type or character set for column %s " + "does not mach referenced column %s close to %s\n", + operation, create_name, columns[err_col], col_name, latest_foreign); + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Field type or character set for column %s " + "does not mach referenced column %s close to %s", + operation, create_name, columns[err_col], col_name, latest_foreign); + break; + } + default: + ut_error; + } +} + +/*********************************************************************//** Scans a table create SQL string and adds to the data dictionary the foreign key constraints declared in the string. This function should be called after the indexes for a table have been created. Each foreign key constraint must @@ -4327,16 +4445,21 @@ dict_create_foreign_constraints_low( DB_CANNOT_ADD_CONSTRAINT if any foreign keys are found. */ { - dict_table_t* table; - dict_table_t* referenced_table; - dict_table_t* table_to_alter; + dict_table_t* table = NULL; + dict_table_t* referenced_table = NULL; + dict_table_t* table_to_alter = NULL; + dict_table_t* table_to_create = NULL; ulint highest_id_so_far = 0; ulint number = 1; - dict_index_t* index; - dict_foreign_t* foreign; + dict_index_t* index = NULL; + dict_foreign_t* foreign = NULL; const char* ptr = sql_string; const char* start_of_latest_foreign = sql_string; + const char* start_of_latest_set = NULL; FILE* ef = dict_foreign_err_file; + ulint index_error = DB_SUCCESS; + dict_index_t* err_index = NULL; + ulint err_col; const char* constraint_name; ibool success; dberr_t error; @@ -4349,37 +4472,80 @@ dict_create_foreign_constraints_low( ulint n_on_updates; const dict_col_t*columns[500]; const char* column_names[500]; + const char* ref_column_names[500]; const char* referenced_table_name; dict_foreign_set local_fk_set; dict_foreign_set_free local_fk_set_free(local_fk_set); + const char* create_table_name; + const char* orig; + char create_name[MAX_TABLE_NAME_LEN + 1]; + char operation[8]; ut_ad(!srv_read_only_mode); ut_ad(mutex_own(&(dict_sys->mutex))); table = dict_table_get_low(name); + /* First check if we are actually doing an ALTER TABLE, and in that + case look for the table being altered */ + orig = ptr; + ptr = dict_accept(cs, ptr, "ALTER", &success); + + strcpy((char *)operation, success ? "Alter " : "Create "); + + if (!success) { + orig = ptr; + ptr = dict_scan_to(ptr, "CREATE"); + ptr = dict_scan_to(ptr, "TABLE"); + ptr = dict_accept(cs, ptr, "TABLE", &success); + + if (success) { + ptr = dict_scan_table_name(cs, ptr, &table_to_create, name, + &success, heap, &create_table_name); + } + + if (success) { + char *bufend; + bufend = innobase_convert_name((char *)create_name, MAX_TABLE_NAME_LEN, + create_table_name, strlen(create_table_name), + trx->mysql_thd, TRUE); + create_name[bufend-create_name]='\0'; + ptr = orig; + } else { + char *bufend; + ptr = orig; + bufend = innobase_convert_name((char *)create_name, MAX_TABLE_NAME_LEN, + name, strlen(name), trx->mysql_thd, TRUE); + create_name[bufend-create_name]='\0'; + } + + goto loop; + } if (table == NULL) { mutex_enter(&dict_foreign_err_mutex); - dict_foreign_error_report_low(ef, name); - fprintf(ef, - "Cannot find the table in the internal" - " data dictionary of InnoDB.\n" - "Create table statement:\n%s\n", sql_string); + dict_foreign_error_report_low(ef, create_name); + dict_foreign_error_report_low(ef, create_name); + fprintf(ef, "%s table %s with foreign key constraint" + " failed. Table %s not found from data dictionary." + " Error close to %s.\n", + operation, create_name, create_name, start_of_latest_foreign); mutex_exit(&dict_foreign_err_mutex); + ib_push_warning(trx, DB_ERROR, + "%s table %s with foreign key constraint" + " failed. Table %s not found from data dictionary." + " Error close to %s.", + operation, create_name, create_name, start_of_latest_foreign); return(DB_ERROR); } - /* First check if we are actually doing an ALTER TABLE, and in that - case look for the table being altered */ - - ptr = dict_accept(cs, ptr, "ALTER", &success); - + /* If not alter table jump to loop */ if (!success) { goto loop; } + orig = ptr; ptr = dict_accept(cs, ptr, "TABLE", &success); if (!success) { @@ -4389,13 +4555,40 @@ dict_create_foreign_constraints_low( /* We are doing an ALTER TABLE: scan the table name we are altering */ + orig = ptr; ptr = dict_scan_table_name(cs, ptr, &table_to_alter, name, &success, heap, &referenced_table_name); + + if (table_to_alter) { + char *bufend; + bufend = innobase_convert_name((char *)create_name, MAX_TABLE_NAME_LEN, + table_to_alter->name, strlen(table_to_alter->name), + trx->mysql_thd, TRUE); + create_name[bufend-create_name]='\0'; + } else { + char *bufend; + bufend = innobase_convert_name((char *)create_name, MAX_TABLE_NAME_LEN, + referenced_table_name, strlen(referenced_table_name), + trx->mysql_thd, TRUE); + create_name[bufend-create_name]='\0'; + + } + if (!success) { - fprintf(stderr, - "InnoDB: Error: could not find" - " the table being ALTERED in:\n%s\n", - sql_string); + mutex_enter(&dict_foreign_err_mutex); + dict_foreign_error_report_low(ef, create_name); + fprintf(ef, + "%s table %s with foreign key constraint" + " failed. Table %s not found from data dictionary." + " Error close to %s.\n", + operation, create_name, create_name, orig); + mutex_exit(&dict_foreign_err_mutex); + + ib_push_warning(trx, DB_ERROR, + "%s table %s with foreign key constraint" + " failed. Table %s not found from data dictionary." + " Error close to %s.", + operation, create_name, create_name, orig); return(DB_ERROR); } @@ -4432,6 +4625,7 @@ loop: of the constraint to system tables. */ ptr = ptr1; + orig = ptr; ptr = dict_accept(cs, ptr, "CONSTRAINT", &success); ut_a(success); @@ -4462,6 +4656,19 @@ loop: if so, immediately reject the command if the table is a temporary one. For now, this kludge will work. */ if (reject_fks && !local_fk_set.empty()) { + mutex_enter(&dict_foreign_err_mutex); + dict_foreign_error_report_low(ef, create_name); + fprintf(ef, "%s table %s with foreign key constraint" + " failed. Temporary tables can't have foreign key constraints." + " Error close to %s.\n", + operation, create_name, start_of_latest_foreign); + mutex_exit(&dict_foreign_err_mutex); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Temporary tables can't have foreign key constraints." + " Error close to %s.", + operation, create_name, start_of_latest_foreign); return(DB_CANNOT_ADD_CONSTRAINT); } @@ -4487,6 +4694,7 @@ loop: start_of_latest_foreign = ptr; + orig = ptr; ptr = dict_accept(cs, ptr, "FOREIGN", &success); if (!success) { @@ -4497,6 +4705,7 @@ loop: goto loop; } + orig = ptr; ptr = dict_accept(cs, ptr, "KEY", &success); if (!success) { @@ -4522,6 +4731,7 @@ loop: } } + orig = ptr; ptr = dict_accept(cs, ptr, "(", &success); if (!success) { @@ -4531,8 +4741,17 @@ loop: ptr = dict_skip_word(cs, ptr, &success); if (!success) { dict_foreign_report_syntax_err( - name, start_of_latest_foreign, ptr); - return(DB_CANNOT_ADD_CONSTRAINT); + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.\n", + operation, create_name, start_of_latest_foreign, orig); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.", + operation, create_name, start_of_latest_foreign, orig); + return(DB_CANNOT_ADD_CONSTRAINT); } } else { @@ -4559,15 +4778,26 @@ loop: /* Scan the columns in the first list */ col_loop1: ut_a(i < (sizeof column_names) / sizeof *column_names); + orig = ptr; ptr = dict_scan_col(cs, ptr, &success, table, columns + i, heap, column_names + i); if (!success) { mutex_enter(&dict_foreign_err_mutex); - dict_foreign_error_report_low(ef, name); - fprintf(ef, "%s:\nCannot resolve column name close to:\n%s\n", - start_of_latest_foreign, ptr); + dict_foreign_error_report_low(ef, create_name); + fprintf(ef, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.\n", + operation, create_name, start_of_latest_foreign, orig); + mutex_exit(&dict_foreign_err_mutex); + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.", + operation, create_name, start_of_latest_foreign, orig); + return(DB_CANNOT_ADD_CONSTRAINT); } @@ -4579,11 +4809,22 @@ col_loop1: goto col_loop1; } + orig = ptr; ptr = dict_accept(cs, ptr, ")", &success); if (!success) { dict_foreign_report_syntax_err( - name, start_of_latest_foreign, ptr); + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.\n", + operation, create_name, start_of_latest_foreign, orig); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.", + operation, create_name, start_of_latest_foreign, orig); + return(DB_CANNOT_ADD_CONSTRAINT); } @@ -4594,27 +4835,41 @@ col_loop1: set to 0 */ index = dict_foreign_find_index( - table, NULL, column_names, i, NULL, TRUE, FALSE); + table, NULL, column_names, i, + NULL, TRUE, FALSE, &index_error, &err_col, &err_index); if (!index) { mutex_enter(&dict_foreign_err_mutex); - dict_foreign_error_report_low(ef, name); + dict_foreign_error_report_low(ef, create_name); fputs("There is no index in table ", ef); - ut_print_name(ef, NULL, TRUE, name); + ut_print_name(ef, NULL, TRUE, create_name); fprintf(ef, " where the columns appear\n" "as the first columns. Constraint:\n%s\n" "See " REFMAN "innodb-foreign-key-constraints.html\n" "for correct foreign key definition.\n", start_of_latest_foreign); - mutex_exit(&dict_foreign_err_mutex); + dict_foreign_push_index_error(trx, operation, create_name, start_of_latest_foreign, + column_names, index_error, err_col, err_index, table, ef); - return(DB_CHILD_NO_INDEX); + mutex_exit(&dict_foreign_err_mutex); + return(DB_CANNOT_ADD_CONSTRAINT); } + + orig = ptr; ptr = dict_accept(cs, ptr, "REFERENCES", &success); if (!success || !my_isspace(cs, *ptr)) { dict_foreign_report_syntax_err( - name, start_of_latest_foreign, ptr); + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.\n", + operation, create_name, start_of_latest_foreign, orig); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.", + operation, create_name, start_of_latest_foreign, orig); return(DB_CANNOT_ADD_CONSTRAINT); } @@ -4683,21 +4938,46 @@ col_loop1: checking of foreign key constraints! */ if (!success || (!referenced_table && trx->check_foreigns)) { + char buf[MAX_TABLE_NAME_LEN + 1] = ""; + char* bufend; + + bufend = innobase_convert_name(buf, MAX_TABLE_NAME_LEN, + referenced_table_name, strlen(referenced_table_name), + trx->mysql_thd, TRUE); + buf[bufend - buf] = '\0'; + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint failed. Referenced table %s not found in the data dictionary " + "close to %s.", + operation, create_name, buf, start_of_latest_foreign); mutex_enter(&dict_foreign_err_mutex); - dict_foreign_error_report_low(ef, name); - fprintf(ef, "%s:\nCannot resolve table name close to:\n" - "%s\n", - start_of_latest_foreign, ptr); + dict_foreign_error_report_low(ef, create_name); + fprintf(ef, + "%s table %s with foreign key constraint failed. Referenced table %s not found in the data dictionary " + "close to %s.\n", + operation, create_name, buf, start_of_latest_foreign); + mutex_exit(&dict_foreign_err_mutex); return(DB_CANNOT_ADD_CONSTRAINT); } + orig = ptr; ptr = dict_accept(cs, ptr, "(", &success); if (!success) { - dict_foreign_report_syntax_err(name, start_of_latest_foreign, - ptr); + dict_foreign_report_syntax_err( + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.\n", + operation, create_name, start_of_latest_foreign, orig); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.", + operation, create_name, start_of_latest_foreign, orig); + return(DB_CANNOT_ADD_CONSTRAINT); } @@ -4705,34 +4985,54 @@ col_loop1: i = 0; col_loop2: + orig = ptr; ptr = dict_scan_col(cs, ptr, &success, referenced_table, columns + i, - heap, column_names + i); + heap, ref_column_names + i); i++; if (!success) { mutex_enter(&dict_foreign_err_mutex); - dict_foreign_error_report_low(ef, name); - fprintf(ef, "%s:\nCannot resolve column name close to:\n" - "%s\n", - start_of_latest_foreign, ptr); + dict_foreign_error_report_low(ef, create_name); + fprintf(ef, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.\n", + operation, create_name, start_of_latest_foreign, orig); mutex_exit(&dict_foreign_err_mutex); + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.", + operation, create_name, start_of_latest_foreign, orig); return(DB_CANNOT_ADD_CONSTRAINT); } + orig = ptr; ptr = dict_accept(cs, ptr, ",", &success); if (success) { goto col_loop2; } + orig = ptr; ptr = dict_accept(cs, ptr, ")", &success); if (!success || foreign->n_fields != i) { - dict_foreign_report_syntax_err(name, start_of_latest_foreign, - ptr); + dict_foreign_report_syntax_err( + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s. Too few referenced columns.\n", + operation, create_name, start_of_latest_foreign, orig); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s. Too few referenced columns, you have %d when you should have %d.", + operation, create_name, start_of_latest_foreign, orig, i, foreign->n_fields); + return(DB_CANNOT_ADD_CONSTRAINT); } @@ -4742,6 +5042,7 @@ col_loop2: scan_on_conditions: /* Loop here as long as we can find ON ... conditions */ + start_of_latest_set = ptr; ptr = dict_accept(cs, ptr, "ON", &success); if (!success) { @@ -4749,15 +5050,27 @@ scan_on_conditions: goto try_find_index; } + orig = ptr; ptr = dict_accept(cs, ptr, "DELETE", &success); if (!success) { + orig = ptr; ptr = dict_accept(cs, ptr, "UPDATE", &success); if (!success) { dict_foreign_report_syntax_err( - name, start_of_latest_foreign, ptr); + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.\n", + operation, create_name, start_of_latest_foreign, start_of_latest_set); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.", + operation, create_name, start_of_latest_foreign, start_of_latest_set); + return(DB_CANNOT_ADD_CONSTRAINT); } @@ -4768,12 +5081,14 @@ scan_on_conditions: n_on_deletes++; } + orig = ptr; ptr = dict_accept(cs, ptr, "RESTRICT", &success); if (success) { goto scan_on_conditions; } + orig = ptr; ptr = dict_accept(cs, ptr, "CASCADE", &success); if (success) { @@ -4786,14 +5101,25 @@ scan_on_conditions: goto scan_on_conditions; } + orig = ptr; ptr = dict_accept(cs, ptr, "NO", &success); if (success) { + orig = ptr; ptr = dict_accept(cs, ptr, "ACTION", &success); if (!success) { dict_foreign_report_syntax_err( - name, start_of_latest_foreign, ptr); + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.\n", + operation, create_name, start_of_latest_foreign, start_of_latest_set); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.", + operation, create_name, start_of_latest_foreign, start_of_latest_set); return(DB_CANNOT_ADD_CONSTRAINT); } @@ -4807,38 +5133,69 @@ scan_on_conditions: goto scan_on_conditions; } + orig = ptr; ptr = dict_accept(cs, ptr, "SET", &success); if (!success) { - dict_foreign_report_syntax_err(name, start_of_latest_foreign, - ptr); + dict_foreign_report_syntax_err( + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.\n", + operation, create_name, start_of_latest_foreign, start_of_latest_set); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.", + operation, create_name, start_of_latest_foreign, start_of_latest_set); return(DB_CANNOT_ADD_CONSTRAINT); } + orig = ptr; ptr = dict_accept(cs, ptr, "NULL", &success); if (!success) { - dict_foreign_report_syntax_err(name, start_of_latest_foreign, - ptr); + dict_foreign_report_syntax_err( + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.\n", + operation, create_name, start_of_latest_foreign, start_of_latest_set); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Foreign key constraint parse error in %s" + " close to %s.", + operation, create_name, start_of_latest_foreign, start_of_latest_set); + return(DB_CANNOT_ADD_CONSTRAINT); } for (j = 0; j < foreign->n_fields; j++) { if ((dict_index_get_nth_col(foreign->foreign_index, j)->prtype) & DATA_NOT_NULL) { + const dict_col_t* col + = dict_index_get_nth_col(foreign->foreign_index, j); + const char* col_name = dict_table_get_col_name(foreign->foreign_index->table, + dict_col_get_no(col)); /* It is not sensible to define SET NULL if the column is not allowed to be NULL! */ mutex_enter(&dict_foreign_err_mutex); - dict_foreign_error_report_low(ef, name); - fprintf(ef, "%s:\n" - "You have defined a SET NULL condition" - " though some of the\n" - "columns are defined as NOT NULL.\n", - start_of_latest_foreign); + dict_foreign_error_report_low(ef, create_name); + fprintf(ef, + "%s table %s with foreign key constraint" + " failed. You have defined a SET NULL condition but column %s is defined as NOT NULL" + " in %s close to %s.\n", + operation, create_name, col_name, start_of_latest_foreign, start_of_latest_set); mutex_exit(&dict_foreign_err_mutex); + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. You have defined a SET NULL condition but column %s is defined as NOT NULL" + " in %s close to %s.", + operation, create_name, col_name, start_of_latest_foreign, start_of_latest_set); + return(DB_CANNOT_ADD_CONSTRAINT); } } @@ -4856,11 +5213,20 @@ try_find_index: /* It is an error to define more than 1 action */ mutex_enter(&dict_foreign_err_mutex); - dict_foreign_error_report_low(ef, name); - fprintf(ef, "%s:\n" - "You have twice an ON DELETE clause" - " or twice an ON UPDATE clause.\n", - start_of_latest_foreign); + dict_foreign_error_report_low(ef, create_name); + fprintf(ef, + "%s table %s with foreign key constraint" + " failed. You have more than one on delete or on update clause" + " in %s close to %s.\n", + operation, create_name, start_of_latest_foreign, start_of_latest_set); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. You have more than one on delete or on update clause" + " in %s close to %s.", + operation, create_name, start_of_latest_foreign, start_of_latest_set); + + dict_foreign_free(foreign); mutex_exit(&dict_foreign_err_mutex); return(DB_CANNOT_ADD_CONSTRAINT); @@ -4872,12 +5238,12 @@ try_find_index: if (referenced_table) { index = dict_foreign_find_index(referenced_table, NULL, - column_names, i, + ref_column_names, i, foreign->foreign_index, - TRUE, FALSE); + TRUE, FALSE, &index_error, &err_col, &err_index); if (!index) { mutex_enter(&dict_foreign_err_mutex); - dict_foreign_error_report_low(ef, name); + dict_foreign_error_report_low(ef, create_name); fprintf(ef, "%s:\n" "Cannot find an index in the" " referenced table where the\n" @@ -4895,9 +5261,13 @@ try_find_index: "innodb-foreign-key-constraints.html\n" "for correct foreign key definition.\n", start_of_latest_foreign); + + dict_foreign_push_index_error(trx, operation, create_name, start_of_latest_foreign, + column_names, index_error, err_col, err_index, referenced_table, ef); + mutex_exit(&dict_foreign_err_mutex); - return(DB_PARENT_NO_INDEX); + return(DB_CANNOT_ADD_CONSTRAINT); } } else { ut_a(trx->check_foreigns == FALSE); @@ -4916,11 +5286,12 @@ try_find_index: for (i = 0; i < foreign->n_fields; i++) { foreign->referenced_col_names[i] - = mem_heap_strdup(foreign->heap, column_names[i]); + = mem_heap_strdup(foreign->heap, ref_column_names[i]); } goto loop; } + /************************************************************************** Determines whether a string starts with the specified keyword. @return TRUE if str starts with keyword */ @@ -6110,7 +6481,8 @@ dict_foreign_replace_index( foreign->foreign_table, col_names, foreign->foreign_col_names, foreign->n_fields, index, - /*check_charsets=*/TRUE, /*check_null=*/FALSE); + /*check_charsets=*/TRUE, /*check_null=*/FALSE, + NULL, NULL, NULL); if (new_index) { ut_ad(new_index->table == index->table); ut_ad(!new_index->to_be_dropped); @@ -6134,7 +6506,8 @@ dict_foreign_replace_index( foreign->referenced_table, NULL, foreign->referenced_col_names, foreign->n_fields, index, - /*check_charsets=*/TRUE, /*check_null=*/FALSE); + /*check_charsets=*/TRUE, /*check_null=*/FALSE, + NULL, NULL, NULL); /* There must exist an alternative index, since this must have been checked earlier. */ if (new_index) { @@ -6695,10 +7068,15 @@ dict_foreign_qualify_index( /*!< in: whether to check charsets. only has an effect if types_idx != NULL */ - ulint check_null) + ulint check_null, /*!< in: nonzero if none of the columns must be declared NOT NULL */ + ulint* error, /*!< out: error code */ + ulint* err_col_no, + /*!< out: column number where error happened */ + dict_index_t** err_index) + /*!< out: index where error happened */ { if (dict_index_get_n_fields(index) < n_cols) { return(false); @@ -6715,11 +7093,21 @@ dict_foreign_qualify_index( if (field->prefix_len != 0) { /* We do not accept column prefix indexes here */ + if (error && err_col_no && err_index) { + *error = DB_FOREIGN_KEY_IS_PREFIX_INDEX; + *err_col_no = i; + *err_index = (dict_index_t*)index; + } return(false); } if (check_null && (field->col->prtype & DATA_NOT_NULL)) { + if (error && err_col_no && err_index) { + *error = DB_FOREIGN_KEY_COL_NOT_NULL; + *err_col_no = i; + *err_index = (dict_index_t*)index; + } return(false); } @@ -6735,6 +7123,12 @@ dict_foreign_qualify_index( dict_index_get_nth_col(index, i), dict_index_get_nth_col(types_idx, i), check_charsets)) { + if (error && err_col_no && err_index) { + *error = DB_FOREIGN_KEY_COLS_NOT_EQUAL; + *err_col_no = i; + *err_index = (dict_index_t*)index; + } + return(false); } } diff --git a/storage/xtradb/dict/dict0load.cc b/storage/xtradb/dict/dict0load.cc index 58521b833b6..d6ed8acb39e 100644 --- a/storage/xtradb/dict/dict0load.cc +++ b/storage/xtradb/dict/dict0load.cc @@ -1168,7 +1168,7 @@ loop: dberr_t err = fil_open_single_table_tablespace( read_page_0, srv_read_only_mode ? false : true, space_id, dict_tf_to_fsp_flags(flags), - name, filepath); + name, filepath, NULL); if (err != DB_SUCCESS) { ib_logf(IB_LOG_LEVEL_ERROR, @@ -2413,7 +2413,7 @@ err_exit: err = fil_open_single_table_tablespace( true, false, table->space, dict_tf_to_fsp_flags(table->flags), - name, filepath); + name, filepath, table); if (err != DB_SUCCESS) { /* We failed to find a sensible diff --git a/storage/xtradb/dict/dict0mem.cc b/storage/xtradb/dict/dict0mem.cc index ffdc5100558..a4f6cd6c91f 100644 --- a/storage/xtradb/dict/dict0mem.cc +++ b/storage/xtradb/dict/dict0mem.cc @@ -420,7 +420,8 @@ dict_mem_table_col_rename_low( dict_index_t* new_index = dict_foreign_find_index( foreign->foreign_table, NULL, foreign->foreign_col_names, - foreign->n_fields, NULL, true, false); + foreign->n_fields, NULL, true, false, + NULL, NULL, NULL); /* There must be an equivalent index in this case. */ ut_ad(new_index != NULL); diff --git a/storage/xtradb/fil/fil0crypt.cc b/storage/xtradb/fil/fil0crypt.cc index 289c4df60bf..f60a4fb3fcf 100644 --- a/storage/xtradb/fil/fil0crypt.cc +++ b/storage/xtradb/fil/fil0crypt.cc @@ -650,31 +650,8 @@ fil_space_encrypt( /* handle post encryption checksum */ ib_uint32_t checksum = 0; - srv_checksum_algorithm_t algorithm = - static_cast<srv_checksum_algorithm_t>(srv_checksum_algorithm); - if (zip_size == 0) { - switch (algorithm) { - case SRV_CHECKSUM_ALGORITHM_CRC32: - case SRV_CHECKSUM_ALGORITHM_STRICT_CRC32: - checksum = buf_calc_page_crc32(dst_frame); - break; - case SRV_CHECKSUM_ALGORITHM_INNODB: - case SRV_CHECKSUM_ALGORITHM_STRICT_INNODB: - checksum = (ib_uint32_t) buf_calc_page_new_checksum( - dst_frame); - break; - case SRV_CHECKSUM_ALGORITHM_NONE: - case SRV_CHECKSUM_ALGORITHM_STRICT_NONE: - checksum = BUF_NO_CHECKSUM_MAGIC; - break; - /* no default so the compiler will emit a warning - * if new enum is added and not handled here */ - } - } else { - checksum = page_zip_calc_checksum(dst_frame, zip_size, - algorithm); - } + checksum = fil_crypt_calculate_checksum(zip_size, dst_frame); // store the post-encryption checksum after the key-version mach_write_to_4(dst_frame + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION + 4, checksum); @@ -818,6 +795,47 @@ fil_space_decrypt( return src_frame; } +/****************************************************************** +Calculate post encryption checksum +@return page checksum or BUF_NO_CHECKSUM_MAGIC +not needed. */ +UNIV_INTERN +ulint +fil_crypt_calculate_checksum( +/*=========================*/ + ulint zip_size, /*!< in: zip_size or 0 */ + byte* dst_frame) /*!< in: page where to calculate */ +{ + ib_uint32_t checksum = 0; + srv_checksum_algorithm_t algorithm = + static_cast<srv_checksum_algorithm_t>(srv_checksum_algorithm); + + if (zip_size == 0) { + switch (algorithm) { + case SRV_CHECKSUM_ALGORITHM_CRC32: + case SRV_CHECKSUM_ALGORITHM_STRICT_CRC32: + checksum = buf_calc_page_crc32(dst_frame); + break; + case SRV_CHECKSUM_ALGORITHM_INNODB: + case SRV_CHECKSUM_ALGORITHM_STRICT_INNODB: + checksum = (ib_uint32_t) buf_calc_page_new_checksum( + dst_frame); + break; + case SRV_CHECKSUM_ALGORITHM_NONE: + case SRV_CHECKSUM_ALGORITHM_STRICT_NONE: + checksum = BUF_NO_CHECKSUM_MAGIC; + break; + /* no default so the compiler will emit a warning + * if new enum is added and not handled here */ + } + } else { + checksum = page_zip_calc_checksum(dst_frame, zip_size, + algorithm); + } + + return checksum; +} + /********************************************************************* Verify checksum for a page (iff it's encrypted) NOTE: currently this function can only be run in single threaded mode diff --git a/storage/xtradb/fil/fil0fil.cc b/storage/xtradb/fil/fil0fil.cc index 3fc9506a15a..47d8803710a 100644 --- a/storage/xtradb/fil/fil0fil.cc +++ b/storage/xtradb/fil/fil0fil.cc @@ -2023,6 +2023,7 @@ fil_read_first_page( const char* check_msg = NULL; fil_space_crypt_t* cdata; + buf = static_cast<byte*>(ut_malloc(2 * UNIV_PAGE_SIZE)); /* Align the memory for a possible read from a raw device */ @@ -2053,6 +2054,10 @@ fil_read_first_page( fsp_flags_get_zip_size(*flags), NULL); cdata = fil_space_read_crypt_data(space, page, offset); + if (crypt_data) { + *crypt_data = cdata; + } + /* If file space is encrypted we need to have at least some encryption service available where to get keys */ if ((cdata && cdata->encryption == FIL_SPACE_ENCRYPTION_ON) || @@ -2060,16 +2065,14 @@ fil_read_first_page( cdata && cdata->encryption == FIL_SPACE_ENCRYPTION_DEFAULT)) { if (!encryption_key_id_exists(cdata->key_id)) { - ib_logf(IB_LOG_LEVEL_FATAL, - "Tablespace id %ld encrypted but encryption service" - " not available. Can't continue opening tablespace.\n", - space); - ut_error; - } - } + ib_logf(IB_LOG_LEVEL_ERROR, + "Tablespace id %ld is encrypted but encryption service" + " or used key_id %u is not available. Can't continue opening tablespace.", + space, cdata->key_id); - if (crypt_data) { - *crypt_data = cdata; + return ("table encrypted but encryption service not available."); + + } } ut_free(buf); @@ -3655,7 +3658,8 @@ fil_open_single_table_tablespace( ulint flags, /*!< in: tablespace flags */ const char* tablename, /*!< in: table name in the databasename/tablename format */ - const char* path_in) /*!< in: tablespace filepath */ + const char* path_in, /*!< in: tablespace filepath */ + dict_table_t* table) /*!< in: table */ { dberr_t err = DB_SUCCESS; bool dict_filepath_same_as_default = false; @@ -3769,6 +3773,10 @@ fil_open_single_table_tablespace( &def.lsn, &def.lsn, &def.crypt_data); def.valid = !def.check_msg; + if (table) { + table->crypt_data = def.crypt_data; + } + /* Validate this single-table-tablespace with SYS_TABLES, but do not compare the DATA_DIR flag, in case the tablespace was relocated. */ @@ -3791,6 +3799,10 @@ fil_open_single_table_tablespace( &remote.lsn, &remote.lsn, &remote.crypt_data); remote.valid = !remote.check_msg; + if (table) { + table->crypt_data = remote.crypt_data; + } + /* Validate this single-table-tablespace with SYS_TABLES, but do not compare the DATA_DIR flag, in case the tablespace was relocated. */ @@ -3814,6 +3826,10 @@ fil_open_single_table_tablespace( &dict.lsn, &dict.lsn, &dict.crypt_data); dict.valid = !dict.check_msg; + if (table) { + table->crypt_data = dict.crypt_data; + } + /* Validate this single-table-tablespace with SYS_TABLES, but do not compare the DATA_DIR flag, in case the tablespace was relocated. */ @@ -3995,7 +4011,9 @@ cleanup_and_exit: mem_free(remote.filepath); } if (remote.crypt_data && remote.crypt_data != crypt_data) { - fil_space_destroy_crypt_data(&remote.crypt_data); + if (err == DB_SUCCESS) { + fil_space_destroy_crypt_data(&remote.crypt_data); + } } if (dict.success) { os_file_close(dict.file); @@ -4010,7 +4028,9 @@ cleanup_and_exit: os_file_close(def.file); } if (def.crypt_data && def.crypt_data != crypt_data) { - fil_space_destroy_crypt_data(&def.crypt_data); + if (err == DB_SUCCESS) { + fil_space_destroy_crypt_data(&def.crypt_data); + } } mem_free(def.filepath); @@ -5928,9 +5948,9 @@ _fil_io( if (!ret) { return(DB_OUT_OF_FILE_SPACE); - } else { - return(DB_SUCCESS); } + + return(DB_SUCCESS); } #ifndef UNIV_HOTBACKUP diff --git a/storage/xtradb/fsp/fsp0fsp.cc b/storage/xtradb/fsp/fsp0fsp.cc index 363a46d7fdf..1358fab90ea 100644 --- a/storage/xtradb/fsp/fsp0fsp.cc +++ b/storage/xtradb/fsp/fsp0fsp.cc @@ -2730,6 +2730,8 @@ fsp_reserve_free_extents( ulint reserve; ibool success; ulint n_pages_added; + size_t total_reserved = 0; + ulint rounds = 0; ut_ad(mtr); *n_reserved = n_ext; @@ -2743,7 +2745,7 @@ fsp_reserve_free_extents( try_again: size = mtr_read_ulint(space_header + FSP_SIZE, MLOG_4BYTES, mtr); - if (size < FSP_EXTENT_SIZE) { + if (size < FSP_EXTENT_SIZE / 2) { /* Use different rules for small single-table tablespaces */ *n_reserved = 0; return(fsp_reserve_free_pages(space, space_header, size, mtr)); @@ -2758,7 +2760,6 @@ try_again: some of them will contain extent descriptor pages, and therefore will not be free extents */ - ut_ad(size >= free_limit); n_free_up = (size - free_limit) / FSP_EXTENT_SIZE; if (n_free_up > 0) { @@ -2799,6 +2800,7 @@ try_again: } success = fil_space_reserve_free_extents(space, n_free, n_ext); + *n_reserved = n_ext; if (success) { return(TRUE); @@ -2808,6 +2810,16 @@ try_to_extend: space_header, mtr); if (success && n_pages_added > 0) { + rounds++; + total_reserved += n_pages_added; + + if (rounds > 50) { + ib_logf(IB_LOG_LEVEL_INFO, + "Space id %lu trying to reserve %lu extents actually reserved %lu " + " reserve %lu free %lu size %lu rounds %lu total_reserved %lu", + space, n_ext, n_pages_added, reserve, n_free, size, rounds, total_reserved); + } + goto try_again; } diff --git a/storage/xtradb/handler/ha_innodb.cc b/storage/xtradb/handler/ha_innodb.cc index a7f5653c389..227509a24cc 100644 --- a/storage/xtradb/handler/ha_innodb.cc +++ b/storage/xtradb/handler/ha_innodb.cc @@ -1,10 +1,10 @@ /***************************************************************************** -Copyright (c) 2000, 2015, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2000, 2015, Oracle and/or its affiliates. +Copyright (c) 2013, 2015, MariaDB Corporation. Copyright (c) 2008, 2009 Google Inc. Copyright (c) 2009, Percona Inc. Copyright (c) 2012, Facebook Inc. -Copyright (c) 2013, 2015, MariaDB Corporation. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described @@ -607,7 +607,8 @@ ib_cb_t innodb_api_cb[] = { (ib_cb_t) ib_get_idx_field_name, (ib_cb_t) ib_trx_get_start_time, (ib_cb_t) ib_cfg_bk_commit_interval, - (ib_cb_t) ib_cursor_stmt_begin + (ib_cb_t) ib_cursor_stmt_begin, + (ib_cb_t) ib_trx_read_only }; @@ -2005,6 +2006,7 @@ convert_error_code_to_mysql( case DB_TABLESPACE_DELETED: case DB_TABLE_NOT_FOUND: + case DB_ENCRYPTED_DECRYPT_FAILED: return(HA_ERR_NO_SUCH_TABLE); case DB_TABLESPACE_NOT_FOUND: @@ -2636,6 +2638,7 @@ check_trx_exists( if (trx == NULL) { trx = innobase_trx_allocate(thd); + thd_set_ha_data(thd, innodb_hton_ptr, trx); } else if (UNIV_UNLIKELY(trx->magic_n != TRX_MAGIC_N)) { mem_analyze_corruption(trx); ut_error; @@ -6041,7 +6044,14 @@ table_opened: innobase_copy_frm_flags_from_table_share(ib_table, table->s); - dict_stats_init(ib_table); + ib_table->thd = (void*)thd; + + /* No point to init any statistics if tablespace is still encrypted. */ + if (!ib_table->is_encrypted) { + dict_stats_init(ib_table); + } else { + ib_table->stat_initialized = 1; + } MONITOR_INC(MONITOR_TABLE_OPEN); @@ -6070,6 +6080,11 @@ table_opened: file, best to play it safe. */ no_tablespace = true; + } else if (ib_table->is_encrypted) { + /* This means that tablespace was found but we could not + decrypt encrypted page. */ + no_tablespace = true; + ib_table->ibd_file_missing = true; } else { no_tablespace = false; } @@ -6078,6 +6093,33 @@ table_opened: free_share(share); my_errno = ENOENT; + /* If table has no talespace but it has crypt data, check + is tablespace made unaccessible because encryption service + or used key_id is not available. */ + if (ib_table) { + fil_space_crypt_t* crypt_data = ib_table->crypt_data; + if ((crypt_data && crypt_data->encryption == FIL_SPACE_ENCRYPTION_ON) || + (srv_encrypt_tables && + crypt_data && crypt_data->encryption == FIL_SPACE_ENCRYPTION_DEFAULT)) { + + if (!encryption_key_id_exists(crypt_data->key_id)) { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + HA_ERR_NO_SUCH_TABLE, + "Table %s is encrypted but encryption service or" + " used key_id %u is not available. " + " Can't continue reading table.", + ib_table->name, crypt_data->key_id); + } + } else if (ib_table->is_encrypted) { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + HA_ERR_NO_SUCH_TABLE, + "Table %s is encrypted but encryption service or" + " used key_id is not available. " + " Can't continue reading table.", + ib_table->name); + } + } + dict_table_close(ib_table, FALSE, FALSE); DBUG_RETURN(HA_ERR_NO_SUCH_TABLE); @@ -8209,10 +8251,11 @@ no_commit: ; } else if (src_table == prebuilt->table) { #ifdef WITH_WSREP - if (wsrep_on(user_thd) && wsrep_load_data_splitting && + if (wsrep_on(user_thd) && + wsrep_load_data_splitting && sql_command == SQLCOM_LOAD && - !thd_test_options( - user_thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) + !thd_test_options(user_thd, + OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) { switch (wsrep_run_wsrep_commit(user_thd, 1)) { @@ -8240,10 +8283,11 @@ no_commit: prebuilt->sql_stat_start = TRUE; } else { #ifdef WITH_WSREP - if (wsrep_on(user_thd) && wsrep_load_data_splitting && + if (wsrep_on(user_thd) && + wsrep_load_data_splitting && sql_command == SQLCOM_LOAD && - !thd_test_options( - user_thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) + !thd_test_options(user_thd, + OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) { switch (wsrep_run_wsrep_commit(user_thd, 1)) { @@ -8474,14 +8518,15 @@ report_error: user_thd); #ifdef WITH_WSREP - if (!error_result && wsrep_thd_exec_mode(user_thd) == LOCAL_STATE && - wsrep_on(user_thd) && !wsrep_consistency_check(user_thd) && - (sql_command != SQLCOM_LOAD || - thd_binlog_format(user_thd) == BINLOG_FORMAT_ROW)) { - - if (wsrep_append_keys(user_thd, false, record, NULL)) { - DBUG_PRINT("wsrep", ("row key failed")); - error_result = HA_ERR_INTERNAL_ERROR; + if (!error_result && + wsrep_thd_exec_mode(user_thd) == LOCAL_STATE && + wsrep_on(user_thd) && + !wsrep_consistency_check(user_thd)) + { + if (wsrep_append_keys(user_thd, false, record, NULL)) + { + DBUG_PRINT("wsrep", ("row key failed")); + error_result = HA_ERR_INTERNAL_ERROR; goto wsrep_error; } } @@ -13135,6 +13180,13 @@ ha_innobase::estimate_rows_upper_bound() prebuilt->trx->op_info = ""; + /* Set num_rows less than MERGEBUFF to simulate the case where we do + not have enough space to merge the externally sorted file blocks. */ + DBUG_EXECUTE_IF("set_num_rows_lt_MERGEBUFF", + estimate = 2; + DBUG_SET("-d,set_num_rows_lt_MERGEBUFF"); + ); + DBUG_RETURN((ha_rows) estimate); } @@ -13400,7 +13452,6 @@ ha_innobase::info_low( dict_table_t* ib_table; ha_rows rec_per_key; ib_uint64_t n_rows; - char path[FN_REFLEN]; os_file_stat_t stat_info; DBUG_ENTER("info"); @@ -13457,6 +13508,7 @@ ha_innobase::info_low( prebuilt->trx->op_info = "returning various info to MySQL"; } + } if (flag & HA_STATUS_VARIABLE) { @@ -13588,6 +13640,7 @@ ha_innobase::info_low( if (flag & HA_STATUS_CONST) { ulong i; + char path[FN_REFLEN]; /* Verify the number of index in InnoDB and MySQL matches up. If prebuilt->clust_index_was_generated holds, InnoDB defines GEN_CLUST_INDEX internally */ @@ -21343,3 +21396,59 @@ static void innodb_remember_check_sysvar_funcs() ut_ad((MYSQL_SYSVAR_NAME(checksum_algorithm).flags & 0x1FF) == PLUGIN_VAR_ENUM); check_sysvar_enum = MYSQL_SYSVAR_NAME(checksum_algorithm).check; } + +/********************************************************************//** +Helper function to push warnings from InnoDB internals to SQL-layer. */ +UNIV_INTERN +void +ib_push_warning( + trx_t* trx, /*!< in: trx */ + ulint error, /*!< in: error code to push as warning */ + const char *format,/*!< in: warning message */ + ...) +{ + va_list args; + THD *thd = (THD *)trx->mysql_thd; + char *buf; +#define MAX_BUF_SIZE 4*1024 + + va_start(args, format); + buf = (char *)my_malloc(MAX_BUF_SIZE, MYF(MY_WME)); + vsprintf(buf,format, args); + + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + convert_error_code_to_mysql((dberr_t)error, 0, thd), + buf); + my_free(buf); + va_end(args); +} + +/********************************************************************//** +Helper function to push warnings from InnoDB internals to SQL-layer. */ +UNIV_INTERN +void +ib_push_warning( + void* ithd, /*!< in: thd */ + ulint error, /*!< in: error code to push as warning */ + const char *format,/*!< in: warning message */ + ...) +{ + va_list args; + THD *thd = (THD *)ithd; + char *buf; +#define MAX_BUF_SIZE 4*1024 + + if (ithd == NULL) { + thd = current_thd; + } + + va_start(args, format); + buf = (char *)my_malloc(MAX_BUF_SIZE, MYF(MY_WME)); + vsprintf(buf,format, args); + + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + convert_error_code_to_mysql((dberr_t)error, 0, thd), + buf); + my_free(buf); + va_end(args); +} diff --git a/storage/xtradb/handler/handler0alter.cc b/storage/xtradb/handler/handler0alter.cc index 7f5ee15049e..5a190e4ca9a 100644 --- a/storage/xtradb/handler/handler0alter.cc +++ b/storage/xtradb/handler/handler0alter.cc @@ -400,6 +400,35 @@ ha_innobase::check_if_supported_inplace_alter( } } + /* If we have column that has changed from NULL -> NOT NULL + and column default has changed we need to do additional + check. */ + if ((ha_alter_info->handler_flags + & Alter_inplace_info::ALTER_COLUMN_NOT_NULLABLE) && + (ha_alter_info->handler_flags + & Alter_inplace_info::ALTER_COLUMN_DEFAULT)) { + Alter_info *alter_info = ha_alter_info->alter_info; + List_iterator<Create_field> def_it(alter_info->create_list); + Create_field *def; + while ((def=def_it++)) { + + /* If this is first column definition whose SQL type + is TIMESTAMP and it is defined as NOT NULL and + it has either constant default or function default + we must use "Copy" method. */ + if (is_timestamp_type(def->sql_type)) { + if ((def->flags & NOT_NULL_FLAG) != 0 && // NOT NULL + (def->def != NULL || // constant default ? + def->unireg_check != Field::NONE)) { // function default + ha_alter_info->unsupported_reason = innobase_get_err_msg( + ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_NOT_NULL); + DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED); + } + break; + } + } + } + /* We should be able to do the operation in-place. See if we can do it online (LOCK=NONE). */ bool online = true; @@ -824,7 +853,7 @@ innobase_find_fk_index( if (!(index->type & DICT_FTS) && dict_foreign_qualify_index( table, col_names, columns, n_cols, - index, NULL, true, 0)) { + index, NULL, true, 0, NULL, NULL, NULL)) { for (ulint i = 0; i < n_drop_index; i++) { if (index == drop_index[i]) { /* Skip to-be-dropped indexes. */ @@ -1014,7 +1043,7 @@ innobase_get_foreign_key_info( referenced_table, 0, referenced_column_names, i, index, - TRUE, FALSE); + TRUE, FALSE, NULL, NULL, NULL); DBUG_EXECUTE_IF( "innodb_test_no_reference_idx", @@ -3341,7 +3370,7 @@ innobase_check_foreign_key_index( foreign->referenced_col_names, foreign->n_fields, index, /*check_charsets=*/TRUE, - /*check_null=*/FALSE) + /*check_null=*/FALSE, NULL, NULL, NULL) && !innobase_find_equiv_index( foreign->referenced_col_names, foreign->n_fields, @@ -3369,7 +3398,7 @@ innobase_check_foreign_key_index( foreign->foreign_col_names, foreign->n_fields, index, /*check_charsets=*/TRUE, - /*check_null=*/FALSE) + /*check_null=*/FALSE, NULL,NULL, NULL) && !innobase_find_equiv_index( foreign->foreign_col_names, foreign->n_fields, @@ -4139,6 +4168,10 @@ oom: : ha_alter_info->key_info_buffer[ prebuilt->trx->error_key_num].name); break; + case DB_ENCRYPTED_DECRYPT_FAILED: + my_error(ER_NO_SUCH_TABLE_IN_ENGINE, MYF(0), + table_share->db.str, table_share->table_name.str); + break; default: my_error_innodb(error, table_share->table_name.str, @@ -4838,7 +4871,8 @@ innobase_update_foreign_try( fk->n_fields, fk->referenced_index, TRUE, fk->type & (DICT_FOREIGN_ON_DELETE_SET_NULL - | DICT_FOREIGN_ON_UPDATE_SET_NULL)); + | DICT_FOREIGN_ON_UPDATE_SET_NULL), + NULL, NULL, NULL); if (!fk->foreign_index) { my_error(ER_FK_INCORRECT_OPTION, MYF(0), table_name, fk->id); @@ -4850,7 +4884,7 @@ innobase_update_foreign_try( names, while the columns in ctx->old_table have not been renamed yet. */ error = dict_create_add_foreign_to_dictionary( - ctx->old_table->name, fk, trx); + (dict_table_t*)ctx->old_table, ctx->old_table->name, fk, trx); DBUG_EXECUTE_IF( "innodb_test_cannot_add_fk_system", diff --git a/storage/xtradb/include/api0api.h b/storage/xtradb/include/api0api.h index d77d691becc..e4c9c941de5 100644 --- a/storage/xtradb/include/api0api.h +++ b/storage/xtradb/include/api0api.h @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 2011, 2013, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2011, 2015, Oracle and/or its affiliates. All Rights Reserved. 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 @@ -494,6 +494,14 @@ ib_trx_state( /*=========*/ ib_trx_t ib_trx); /*!< in: trx handle */ + +/*****************************************************************//** +Check if the transaction is read_only */ +ib_u32_t +ib_trx_read_only( +/*=============*/ + ib_trx_t ib_trx); /*!< in: trx handle */ + /*****************************************************************//** Release the resources of the transaction. If the transaction was selected as a victim by InnoDB and rolled back then use this function diff --git a/storage/xtradb/include/btr0btr.ic b/storage/xtradb/include/btr0btr.ic index 40b468b200a..6604ac6a6f0 100644 --- a/storage/xtradb/include/btr0btr.ic +++ b/storage/xtradb/include/btr0btr.ic @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1994, 2012, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2015, 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 @@ -57,13 +58,15 @@ btr_block_get_func( buf_block_t* block; block = buf_page_get_gen(space, zip_size, page_no, mode, - NULL, BUF_GET, file, line, mtr); + NULL, BUF_GET, file, line, mtr); - if (mode != RW_NO_LATCH) { + if (block) { + if (mode != RW_NO_LATCH) { - buf_block_dbg_add_level( - block, index != NULL && dict_index_is_ibuf(index) - ? SYNC_IBUF_TREE_NODE : SYNC_TREE_NODE); + buf_block_dbg_add_level( + block, index != NULL && dict_index_is_ibuf(index) + ? SYNC_IBUF_TREE_NODE : SYNC_TREE_NODE); + } } return(block); diff --git a/storage/xtradb/include/btr0cur.h b/storage/xtradb/include/btr0cur.h index f4b91b08fc5..2c60f3ad64c 100644 --- a/storage/xtradb/include/btr0cur.h +++ b/storage/xtradb/include/btr0cur.h @@ -136,7 +136,7 @@ Note that if mode is PAGE_CUR_LE, which is used in inserts, then cursor->up_match and cursor->low_match both will have sensible values. If mode is PAGE_CUR_GE, then up_match will a have a sensible value. */ UNIV_INTERN -void +dberr_t btr_cur_search_to_nth_level( /*========================*/ dict_index_t* index, /*!< in: index */ @@ -173,7 +173,7 @@ btr_cur_search_to_nth_level( /*****************************************************************//** Opens a cursor at either end of an index. */ UNIV_INTERN -void +dberr_t btr_cur_open_at_index_side_func( /*============================*/ bool from_left, /*!< in: true if open to the low end, diff --git a/storage/xtradb/include/btr0pcur.h b/storage/xtradb/include/btr0pcur.h index cfbaacf4de3..d8e7cf6b283 100644 --- a/storage/xtradb/include/btr0pcur.h +++ b/storage/xtradb/include/btr0pcur.h @@ -114,7 +114,7 @@ btr_pcur_open_low( Opens an persistent cursor to an index tree without initializing the cursor. */ UNIV_INLINE -void +dberr_t btr_pcur_open_with_no_init_func( /*============================*/ dict_index_t* index, /*!< in: index */ @@ -143,7 +143,7 @@ btr_pcur_open_with_no_init_func( /*****************************************************************//** Opens a persistent cursor at either end of an index. */ UNIV_INLINE -void +dberr_t btr_pcur_open_at_index_side( /*========================*/ bool from_left, /*!< in: true if open to the low end, diff --git a/storage/xtradb/include/btr0pcur.ic b/storage/xtradb/include/btr0pcur.ic index 7e355d3709d..1cd13824542 100644 --- a/storage/xtradb/include/btr0pcur.ic +++ b/storage/xtradb/include/btr0pcur.ic @@ -447,7 +447,7 @@ btr_pcur_open_low( Opens an persistent cursor to an index tree without initializing the cursor. */ UNIV_INLINE -void +dberr_t btr_pcur_open_with_no_init_func( /*============================*/ dict_index_t* index, /*!< in: index */ @@ -472,6 +472,7 @@ btr_pcur_open_with_no_init_func( mtr_t* mtr) /*!< in: mtr */ { btr_cur_t* btr_cursor; + dberr_t err = DB_SUCCESS; cursor->latch_mode = latch_mode; cursor->search_mode = mode; @@ -480,20 +481,21 @@ btr_pcur_open_with_no_init_func( btr_cursor = btr_pcur_get_btr_cur(cursor); - btr_cur_search_to_nth_level(index, 0, tuple, mode, latch_mode, - btr_cursor, has_search_latch, - file, line, mtr); + err = btr_cur_search_to_nth_level(index, 0, tuple, mode, latch_mode, + btr_cursor, has_search_latch, + file, line, mtr); cursor->pos_state = BTR_PCUR_IS_POSITIONED; cursor->old_stored = BTR_PCUR_OLD_NOT_STORED; cursor->trx_if_known = NULL; + return err; } /*****************************************************************//** Opens a persistent cursor at either end of an index. */ UNIV_INLINE -void +dberr_t btr_pcur_open_at_index_side( /*========================*/ bool from_left, /*!< in: true if open to the low end, @@ -506,6 +508,8 @@ btr_pcur_open_at_index_side( (0=leaf) */ mtr_t* mtr) /*!< in/out: mini-transaction */ { + dberr_t err = DB_SUCCESS; + pcur->latch_mode = BTR_LATCH_MODE_WITHOUT_FLAGS(latch_mode); pcur->search_mode = from_left ? PAGE_CUR_G : PAGE_CUR_L; @@ -514,13 +518,15 @@ btr_pcur_open_at_index_side( btr_pcur_init(pcur); } - btr_cur_open_at_index_side(from_left, index, latch_mode, - btr_pcur_get_btr_cur(pcur), level, mtr); + err = btr_cur_open_at_index_side(from_left, index, latch_mode, + btr_pcur_get_btr_cur(pcur), level, mtr); pcur->pos_state = BTR_PCUR_IS_POSITIONED; pcur->old_stored = BTR_PCUR_OLD_NOT_STORED; pcur->trx_if_known = NULL; + + return (err); } /**********************************************************************//** diff --git a/storage/xtradb/include/buf0buf.h b/storage/xtradb/include/buf0buf.h index 2e8e4139843..b35ab9df338 100644 --- a/storage/xtradb/include/buf0buf.h +++ b/storage/xtradb/include/buf0buf.h @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2014, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2013, 2014, SkySQL Ab. All Rights Reserved. +Copyright (c) 2013, 2015, 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 @@ -97,9 +97,6 @@ extern buf_block_t* back_block1; /*!< first block, for --apply-log */ extern buf_block_t* back_block2; /*!< second block, for page reorganize */ #endif /* !UNIV_HOTBACKUP */ -/** Magic value to use instead of checksums when they are disabled */ -#define BUF_NO_CHECKSUM_MAGIC 0xDEADBEEFUL - /** @brief States of a control block @see buf_page_t @@ -431,7 +428,8 @@ buf_page_get_gen( BUF_GET_IF_IN_POOL_OR_WATCH */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ - mtr_t* mtr); /*!< in: mini-transaction */ + mtr_t* mtr, /*!< in: mini-transaction */ + dberr_t* err = NULL); /*!< out: error code */ /********************************************************************//** Initializes a page to the buffer buf_pool. The page is usually not read from a file even if it cannot be found in the buffer buf_pool. This is one @@ -1614,8 +1612,14 @@ struct buf_page_t{ operation needed. */ unsigned key_version; /*!< key version for this block */ - bool page_encrypted; /*!< page is encrypted */ + bool page_encrypted; /*!< page is page encrypted */ bool page_compressed;/*!< page is page compressed */ + ulint stored_checksum;/*!< stored page checksum if page + encrypted */ + bool encrypted; /*!< page is still encrypted */ + ulint calculated_checksum; + /*!< calculated checksum if page + encrypted */ ulint real_size; /*!< Real size of the page Normal pages == UNIV_PAGE_SIZE diff --git a/storage/xtradb/include/buf0buf.ic b/storage/xtradb/include/buf0buf.ic index 89c35caa815..8c8a3c9f975 100644 --- a/storage/xtradb/include/buf0buf.ic +++ b/storage/xtradb/include/buf0buf.ic @@ -689,6 +689,10 @@ buf_block_get_frame( /*================*/ const buf_block_t* block) /*!< in: pointer to the control block */ { + if (!block) { + return NULL; + } + SRV_CORRUPT_TABLE_CHECK(block, return(0);); switch (buf_block_get_state(block)) { @@ -696,6 +700,9 @@ buf_block_get_frame( case BUF_BLOCK_ZIP_PAGE: case BUF_BLOCK_ZIP_DIRTY: case BUF_BLOCK_NOT_USED: + if (block->page.encrypted) { + goto ok; + } ut_error; break; case BUF_BLOCK_FILE_PAGE: diff --git a/storage/xtradb/include/buf0checksum.h b/storage/xtradb/include/buf0checksum.h index cd21781dc6e..6818345f965 100644 --- a/storage/xtradb/include/buf0checksum.h +++ b/storage/xtradb/include/buf0checksum.h @@ -28,11 +28,10 @@ Created Aug 11, 2011 Vasil Dimov #include "univ.i" -#ifndef UNIV_INNOCHECKSUM - #include "buf0types.h" -#endif /* !UNIV_INNOCHECKSUM */ +/** Magic value to use instead of checksums when they are disabled */ +#define BUF_NO_CHECKSUM_MAGIC 0xDEADBEEFUL /********************************************************************//** Calculates a page CRC32 which is stored to the page when it is written @@ -70,8 +69,6 @@ buf_calc_page_old_checksum( /*=======================*/ const byte* page); /*!< in: buffer page */ -#ifndef UNIV_INNOCHECKSUM - /********************************************************************//** Return a printable string describing the checksum algorithm. @return algorithm name */ @@ -83,6 +80,4 @@ buf_checksum_algorithm_name( extern ulong srv_checksum_algorithm; -#endif /* !UNIV_INNOCHECKSUM */ - #endif /* buf0checksum_h */ diff --git a/storage/xtradb/include/buf0lru.h b/storage/xtradb/include/buf0lru.h index 6415540178c..f421e329bf0 100644 --- a/storage/xtradb/include/buf0lru.h +++ b/storage/xtradb/include/buf0lru.h @@ -28,7 +28,9 @@ Created 11/5/1995 Heikki Tuuri #include "univ.i" #ifndef UNIV_HOTBACKUP +#ifndef UNIV_INNOCHECKSUM #include "ut0byte.h" +#endif #include "buf0types.h" // Forward declaration diff --git a/storage/xtradb/include/buf0rea.h b/storage/xtradb/include/buf0rea.h index 9adeaa7455a..f0652b5d2cd 100644 --- a/storage/xtradb/include/buf0rea.h +++ b/storage/xtradb/include/buf0rea.h @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2013, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2015, 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 @@ -39,10 +40,12 @@ UNIV_INTERN ibool buf_read_page( /*==========*/ - ulint space, /*!< in: space id */ - ulint zip_size,/*!< in: compressed page size in bytes, or 0 */ - ulint offset, /*!< in: page number */ - trx_t* trx); + ulint space, /*!< in: space id */ + ulint zip_size, /*!< in: compressed page size in bytes, or 0 */ + ulint offset, /*!< in: page number */ + trx_t* trx, /*!< in: trx */ + buf_page_t** bpage /*!< out: page */ +); /********************************************************************//** High-level function which reads a page asynchronously from a file to the buffer buf_pool if it is not already there. Sets the io_fix flag and sets diff --git a/storage/xtradb/include/db0err.h b/storage/xtradb/include/db0err.h index dab917e18db..bb206dde72b 100644 --- a/storage/xtradb/include/db0err.h +++ b/storage/xtradb/include/db0err.h @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2014, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2015, 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 @@ -132,6 +133,11 @@ enum dberr_t { /*< Too many words in a phrase */ DB_TOO_BIG_FOR_REDO, /* Record length greater than 10% of redo log */ + DB_ENCRYPTED_DECRYPT_FAILED, /* Tablespace encrypted and + decrypt operaton failed because + of missing key management plugin, + or missing or incorrect key or + incorret AES method or algorithm. */ /* The following are partial failure codes */ DB_FAIL = 1000, DB_OVERFLOW, diff --git a/storage/xtradb/include/dict0crea.h b/storage/xtradb/include/dict0crea.h index c40e5356ece..3b746fcf83c 100644 --- a/storage/xtradb/include/dict0crea.h +++ b/storage/xtradb/include/dict0crea.h @@ -113,6 +113,17 @@ UNIV_INTERN dberr_t dict_create_or_check_foreign_constraint_tables(void); /*================================================*/ + +/********************************************************************//** +Construct foreign key constraint defintion from data dictionary information. +*/ +UNIV_INTERN +char* +dict_foreign_def_get( +/*=================*/ + dict_foreign_t* foreign,/*!< in: foreign */ + trx_t* trx); /*!< in: trx */ + /********************************************************************//** Generate a foreign key constraint name when it was not named by the user. A generated constraint has a name of the format dbname/tablename_ibfk_NUMBER, @@ -177,6 +188,7 @@ UNIV_INTERN dberr_t dict_create_add_foreign_to_dictionary( /*==================================*/ + dict_table_t* table, /*!< in: table */ const char* name, /*!< in: table name */ const dict_foreign_t* foreign,/*!< in: foreign key */ trx_t* trx) /*!< in/out: dictionary transaction */ diff --git a/storage/xtradb/include/dict0dict.h b/storage/xtradb/include/dict0dict.h index 09b43fe0e71..5dfdba54f29 100644 --- a/storage/xtradb/include/dict0dict.h +++ b/storage/xtradb/include/dict0dict.h @@ -596,10 +596,17 @@ dict_foreign_find_index( /*!< in: whether to check charsets. only has an effect if types_idx != NULL */ - ulint check_null) + ulint check_null, /*!< in: nonzero if none of the columns must be declared NOT NULL */ + ulint* error, /*!< out: error code */ + ulint* err_col_no, + /*!< out: column number where + error happened */ + dict_index_t** err_index) + /*!< out: index where error + happened */ __attribute__((nonnull(1,3), warn_unused_result)); /**********************************************************************//** Returns a column's name. @@ -691,10 +698,18 @@ dict_foreign_qualify_index( /*!< in: whether to check charsets. only has an effect if types_idx != NULL */ - ulint check_null) + ulint check_null, /*!< in: nonzero if none of the columns must be declared NOT NULL */ + ulint* error, /*!< out: error code */ + ulint* err_col_no, + /*!< out: column number where + error happened */ + dict_index_t** err_index) + /*!< out: index where error + happened */ + __attribute__((nonnull(1,3), warn_unused_result)); #ifdef UNIV_DEBUG /********************************************************************//** diff --git a/storage/xtradb/include/dict0mem.h b/storage/xtradb/include/dict0mem.h index d5cd8c595c2..9c43829cecf 100644 --- a/storage/xtradb/include/dict0mem.h +++ b/storage/xtradb/include/dict0mem.h @@ -2,7 +2,7 @@ Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2012, Facebook Inc. -Copyright (c) 2013, SkySQL Ab. All Rights Reserved. +Copyright (c) 2013, 2015, 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 @@ -51,6 +51,9 @@ Created 1/8/1996 Heikki Tuuri #include "trx0types.h" #include "fts0fts.h" #include "os0once.h" +#include "fil0fil.h" +#include <my_crypt.h> +#include "fil0crypt.h" #include <set> #include <algorithm> #include <iterator> @@ -1030,6 +1033,8 @@ struct dict_table_t{ table_id_t id; /*!< id of the table */ mem_heap_t* heap; /*!< memory heap */ char* name; /*!< table name */ + void* thd; /*!< thd */ + fil_space_crypt_t *crypt_data; /*!< crypt data if present */ const char* dir_path_of_temp_table;/*!< NULL or the directory path where a TEMPORARY table that was explicitly created by a user should be placed if @@ -1342,6 +1347,7 @@ struct dict_table_t{ locks; /*!< list of locks on the table; protected by lock_sys->mutex */ ibool is_corrupt; + ibool is_encrypted; #endif /* !UNIV_HOTBACKUP */ #ifdef UNIV_DEBUG diff --git a/storage/xtradb/include/fil0crypt.h b/storage/xtradb/include/fil0crypt.h index 4fd41d4b789..a9491e3131f 100644 --- a/storage/xtradb/include/fil0crypt.h +++ b/storage/xtradb/include/fil0crypt.h @@ -381,6 +381,17 @@ fil_crypt_set_encrypt_tables( /*=========================*/ uint val); /*!< in: New srv_encrypt_tables setting */ +/****************************************************************** +Calculate post encryption checksum +@return page checksum or BUF_NO_CHECKSUM_MAGIC +not needed. */ +UNIV_INTERN +ulint +fil_crypt_calculate_checksum( +/*=========================*/ + ulint zip_size, /*!< in: zip_size or 0 */ + byte* dst_frame); /*!< in: page where to calculate */ + #ifndef UNIV_NONINL #include "fil0crypt.ic" #endif diff --git a/storage/xtradb/include/fil0fil.h b/storage/xtradb/include/fil0fil.h index 66ceffbed26..2a3995519a7 100644 --- a/storage/xtradb/include/fil0fil.h +++ b/storage/xtradb/include/fil0fil.h @@ -207,6 +207,7 @@ struct fsp_open_info { ulint flags; /*!< Tablespace flags */ ulint encryption_error; /*!< if an encryption error occurs */ fil_space_crypt_t* crypt_data; /*!< crypt data */ + dict_table_t* table; /*!< table */ }; struct fil_space_t; @@ -828,7 +829,8 @@ fil_open_single_table_tablespace( ulint flags, /*!< in: tablespace flags */ const char* tablename, /*!< in: table name in the databasename/tablename format */ - const char* filepath) /*!< in: tablespace filepath */ + const char* filepath, /*!< in: tablespace filepath */ + dict_table_t* table) /*!< in: table */ __attribute__((nonnull(5), warn_unused_result)); #endif /* !UNIV_HOTBACKUP */ diff --git a/storage/xtradb/include/ha_prototypes.h b/storage/xtradb/include/ha_prototypes.h index 22cb3eed42d..724453cb4f6 100644 --- a/storage/xtradb/include/ha_prototypes.h +++ b/storage/xtradb/include/ha_prototypes.h @@ -28,14 +28,17 @@ Created 5/11/2006 Osku Salerma #define HA_INNODB_PROTOTYPES_H #include "my_dbug.h" -#include "mysqld_error.h" #include "my_compare.h" #include "my_sys.h" #include "m_string.h" -#include "debug_sync.h" #include "my_base.h" +#ifndef UNIV_INNOCHECKSUM +#include "mysqld_error.h" +#include "debug_sync.h" #include "trx0types.h" +#endif + #include "m_ctype.h" /* CHARSET_INFO */ // Forward declarations @@ -81,6 +84,8 @@ innobase_raw_format( ulint buf_size); /*!< in: output buffer size in bytes */ +#ifndef UNIV_INNOCHECKSUM + /*****************************************************************//** Invalidates the MySQL query cache for the table. */ UNIV_INTERN @@ -97,6 +102,8 @@ innobase_invalidate_query_cache( ulint full_name_len); /*!< in: full name length where also the null chars count */ +#endif /* #ifndef UNIV_INNOCHECKSUM */ + /*****************************************************************//** Convert a table or index name to the MySQL system_charset_info (UTF-8) and quote it if needed. @@ -620,5 +627,22 @@ innobase_convert_to_filename_charset( const char* from, /* in: identifier to convert */ ulint len); /* in: length of 'to', in bytes */ - +/********************************************************************//** +Helper function to push warnings from InnoDB internals to SQL-layer. */ +UNIV_INTERN +void +ib_push_warning( + trx_t* trx, /*!< in: trx */ + ulint error, /*!< in: error code to push as warning */ + const char *format,/*!< in: warning message */ + ...); +/********************************************************************//** +Helper function to push warnings from InnoDB internals to SQL-layer. */ +UNIV_INTERN +void +ib_push_warning( + void* ithd, /*!< in: thd */ + ulint error, /*!< in: error code to push as warning */ + const char *format,/*!< in: warning message */ + ...); #endif /* HA_INNODB_PROTOTYPES_H */ diff --git a/storage/xtradb/include/os0file.h b/storage/xtradb/include/os0file.h index 88ba7e18ac3..e9faac0d945 100644 --- a/storage/xtradb/include/os0file.h +++ b/storage/xtradb/include/os0file.h @@ -1,6 +1,6 @@ /*********************************************************************** -Copyright (c) 1995, 2014, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 1995, 2015, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2009, Percona Inc. Copyright (c) 2013, 2015, MariaDB Corporation. @@ -405,10 +405,10 @@ to original un-instrumented file I/O APIs */ enum os_file_type_t { OS_FILE_TYPE_UNKNOWN = 0, - OS_FILE_TYPE_FILE, /* regular file */ + OS_FILE_TYPE_FILE, /* regular file + (or a character/block device) */ OS_FILE_TYPE_DIR, /* directory */ - OS_FILE_TYPE_LINK, /* symbolic link */ - OS_FILE_TYPE_BLOCK /* block device */ + OS_FILE_TYPE_LINK /* symbolic link */ }; /* Maximum path string length in bytes when referring to tables with in the diff --git a/storage/xtradb/include/page0cur.ic b/storage/xtradb/include/page0cur.ic index 028d33b17aa..6e068d9f739 100644 --- a/storage/xtradb/include/page0cur.ic +++ b/storage/xtradb/include/page0cur.ic @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1994, 2013, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2015, 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 @@ -39,7 +40,10 @@ page_cur_get_page( page_cur_t* cur) /*!< in: page cursor */ { ut_ad(cur); - ut_ad(page_align(cur->rec) == cur->block->frame); + + if (cur->rec) { + ut_ad(page_align(cur->rec) == cur->block->frame); + } return(page_align(cur->rec)); } @@ -54,7 +58,11 @@ page_cur_get_block( page_cur_t* cur) /*!< in: page cursor */ { ut_ad(cur); - ut_ad(page_align(cur->rec) == cur->block->frame); + + if (cur->rec) { + ut_ad(page_align(cur->rec) == cur->block->frame); + } + return(cur->block); } @@ -80,7 +88,10 @@ page_cur_get_rec( page_cur_t* cur) /*!< in: page cursor */ { ut_ad(cur); - ut_ad(page_align(cur->rec) == cur->block->frame); + + if (cur->rec) { + ut_ad(page_align(cur->rec) == cur->block->frame); + } return(cur->rec); } diff --git a/storage/xtradb/include/page0page.h b/storage/xtradb/include/page0page.h index 6940040a130..2da5d793fa9 100644 --- a/storage/xtradb/include/page0page.h +++ b/storage/xtradb/include/page0page.h @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1994, 2013, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 1994, 2015, Oracle and/or its affiliates. All Rights Reserved. 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 @@ -28,6 +28,10 @@ Created 2/2/1994 Heikki Tuuri #include "univ.i" +#include "buf0types.h" + +#ifndef UNIV_INNOCHECKSUM + #include "page0types.h" #include "fil0fil.h" #include "buf0buf.h" @@ -1110,6 +1114,24 @@ page_find_rec_with_heap_no( const rec_t* page_find_rec_max_not_deleted( const page_t* page); + +#endif /* #ifndef UNIV_INNOCHECKSUM */ + +/** Issue a warning when the checksum that is stored in the page is valid, +but different than the global setting innodb_checksum_algorithm. +@param[in] current_algo current checksum algorithm +@param[in] page_checksum page valid checksum +@param[in] space_id tablespace id +@param[in] page_no page number */ +void +page_warn_strict_checksum( + srv_checksum_algorithm_t curr_algo, + srv_checksum_algorithm_t page_checksum, + ulint space_id, + ulint page_no); + +#ifndef UNIV_INNOCHECKSUM + #ifdef UNIV_MATERIALIZE #undef UNIV_INLINE #define UNIV_INLINE UNIV_INLINE_ORIGINAL @@ -1119,4 +1141,6 @@ page_find_rec_max_not_deleted( #include "page0page.ic" #endif +#endif /* #ifndef UNIV_INNOCHECKSUM */ + #endif diff --git a/storage/xtradb/include/page0types.h b/storage/xtradb/include/page0types.h index 95143a4bb44..74ad6f72f7e 100644 --- a/storage/xtradb/include/page0types.h +++ b/storage/xtradb/include/page0types.h @@ -107,6 +107,8 @@ struct page_zip_stat_t { { } }; +#ifndef UNIV_INNOCHECKSUM + /** Compression statistics types */ typedef map<index_id_t, page_zip_stat_t> page_zip_stat_per_index_t; @@ -119,6 +121,8 @@ extern ib_mutex_t page_zip_stat_per_index_mutex; extern mysql_pfs_key_t page_zip_stat_per_index_mutex_key; #endif /* HAVE_PSI_INTERFACE */ +#endif /* !UNIV_INNOCHECKSUM */ + /**********************************************************************//** Write the "deleted" flag of a record on a compressed page. The flag must already have been written on the uncompressed page. */ diff --git a/storage/xtradb/include/page0zip.h b/storage/xtradb/include/page0zip.h index 2f9efc4a40c..41eb1e35d78 100644 --- a/storage/xtradb/include/page0zip.h +++ b/storage/xtradb/include/page0zip.h @@ -32,13 +32,17 @@ Created June 2005 by Marko Makela # define UNIV_INLINE #endif -#include "mtr0types.h" +#ifndef UNIV_INNOCHECKSUM #include "page0types.h" -#include "buf0types.h" +#include "mtr0types.h" #include "dict0types.h" #include "srv0srv.h" #include "trx0types.h" #include "mem0mem.h" +#else +#include "univ.i" +#endif /* !UNIV_INNOCHECKSUM */ +#include "buf0types.h" /* Compression level to be used by zlib. Settable by user. */ extern uint page_zip_level; @@ -50,6 +54,7 @@ extern uint page_zip_level; compression algorithm changes in zlib. */ extern my_bool page_zip_log_pages; +#ifndef UNIV_INNOCHECKSUM /**********************************************************************//** Determine the size of a compressed page in bytes. @return size in bytes */ @@ -159,6 +164,8 @@ page_zip_simple_validate( descriptor */ #endif /* UNIV_DEBUG */ +#endif /* !UNIV_INNOCHECKSUM */ + #ifdef UNIV_ZIP_DEBUG /**********************************************************************//** Check that the compressed and decompressed pages match. @@ -185,6 +192,7 @@ page_zip_validate( __attribute__((nonnull(1,2))); #endif /* UNIV_ZIP_DEBUG */ +#ifndef UNIV_INNOCHECKSUM /**********************************************************************//** Determine how big record can be inserted without recompressing the page. @return a positive number indicating the maximum size of a record @@ -452,6 +460,8 @@ page_zip_parse_compress( page_zip_des_t* page_zip)/*!< out: compressed page */ __attribute__((nonnull(1,2))); +#endif /* !UNIV_INNOCHECKSUM */ + /**********************************************************************//** Calculate the compressed page checksum. @return page checksum */ @@ -474,6 +484,9 @@ page_zip_verify_checksum( /*=====================*/ const void* data, /*!< in: compressed page */ ulint size); /*!< in: size of compressed page */ + +#ifndef UNIV_INNOCHECKSUM + /**********************************************************************//** Write a log record of compressing an index page without the data on the page. */ UNIV_INLINE @@ -506,6 +519,8 @@ void page_zip_reset_stat_per_index(); /*===========================*/ +#endif /* !UNIV_INNOCHECKSUM */ + #ifndef UNIV_HOTBACKUP /** Check if a pointer to an uncompressed page matches a compressed page. When we IMPORT a tablespace the blocks and accompanying frames are allocted @@ -531,8 +546,10 @@ from outside the buffer pool. # define UNIV_INLINE UNIV_INLINE_ORIGINAL #endif +#ifndef UNIV_INNOCHECKSUM #ifndef UNIV_NONINL # include "page0zip.ic" #endif +#endif /* !UNIV_INNOCHECKSUM */ #endif /* page0zip_h */ diff --git a/storage/xtradb/include/srv0srv.h b/storage/xtradb/include/srv0srv.h index 78a402d488c..45300bc2ded 100644 --- a/storage/xtradb/include/srv0srv.h +++ b/storage/xtradb/include/srv0srv.h @@ -514,7 +514,6 @@ extern my_bool srv_stats_sample_traditional; extern ibool srv_use_doublewrite_buf; extern ulong srv_doublewrite_batch_size; -extern ulong srv_checksum_algorithm; extern ulong srv_log_arch_expire_sec; diff --git a/storage/xtradb/include/univ.i b/storage/xtradb/include/univ.i index 241f210869f..8105d1f318b 100644 --- a/storage/xtradb/include/univ.i +++ b/storage/xtradb/include/univ.i @@ -45,10 +45,10 @@ Created 1/20/1994 Heikki Tuuri #define INNODB_VERSION_MAJOR 5 #define INNODB_VERSION_MINOR 6 -#define INNODB_VERSION_BUGFIX 24 +#define INNODB_VERSION_BUGFIX 25 #ifndef PERCONA_INNODB_VERSION -#define PERCONA_INNODB_VERSION 72.2 +#define PERCONA_INNODB_VERSION 73.1 #endif /* Enable UNIV_LOG_ARCHIVE in XtraDB */ diff --git a/storage/xtradb/lock/lock0lock.cc b/storage/xtradb/lock/lock0lock.cc index b3d9e470816..26d868ca94d 100644 --- a/storage/xtradb/lock/lock0lock.cc +++ b/storage/xtradb/lock/lock0lock.cc @@ -3027,7 +3027,8 @@ lock_rec_inherit_to_gap( && !((srv_locks_unsafe_for_binlog || lock->trx->isolation_level <= TRX_ISO_READ_COMMITTED) - && lock_get_mode(lock) == LOCK_X)) { + && lock_get_mode(lock) == + (lock->trx->duplicates ? LOCK_S : LOCK_X))) { lock_rec_add_to_queue( LOCK_REC | LOCK_GAP | lock_get_mode(lock), diff --git a/storage/xtradb/log/log0crypt.cc b/storage/xtradb/log/log0crypt.cc index 38b82d6ceb4..8c4fd623fc6 100644 --- a/storage/xtradb/log/log0crypt.cc +++ b/storage/xtradb/log/log0crypt.cc @@ -131,12 +131,13 @@ log_blocks_crypt( const byte* block, /*!< in: blocks before encrypt/decrypt*/ ulint size, /*!< in: size of block */ byte* dst_block, /*!< out: blocks after encrypt/decrypt */ - bool is_encrypt) /*!< in: encrypt or decrypt*/ + int what) /*!< in: encrypt or decrypt*/ { byte *log_block = (byte*)block; Crypt_result rc = MY_AES_OK; uint dst_len; byte aes_ctr_counter[MY_AES_BLOCK_SIZE]; + byte is_encrypt= what == ENCRYPTION_FLAG_ENCRYPT; lsn_t lsn = is_encrypt ? log_sys->lsn : srv_start_lsn; const uint src_len = OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_HDR_SIZE; @@ -174,21 +175,13 @@ log_blocks_crypt( bzero(aes_ctr_counter + 15, 1); int rc; - if (is_encrypt) { - rc = encryption_encrypt(log_block + LOG_BLOCK_HDR_SIZE, src_len, - dst_block + LOG_BLOCK_HDR_SIZE, &dst_len, - (unsigned char*)(info->crypt_key), 16, - aes_ctr_counter, MY_AES_BLOCK_SIZE, 1, - LOG_DEFAULT_ENCRYPTION_KEY, - info->key_version); - } else { - rc = encryption_decrypt(log_block + LOG_BLOCK_HDR_SIZE, src_len, - dst_block + LOG_BLOCK_HDR_SIZE, &dst_len, - (unsigned char*)(info->crypt_key), 16, - aes_ctr_counter, MY_AES_BLOCK_SIZE, 1, - LOG_DEFAULT_ENCRYPTION_KEY, - info->key_version); - } + rc = encryption_crypt(log_block + LOG_BLOCK_HDR_SIZE, src_len, + dst_block + LOG_BLOCK_HDR_SIZE, &dst_len, + (unsigned char*)(info->crypt_key), 16, + aes_ctr_counter, MY_AES_BLOCK_SIZE, + what | ENCRYPTION_FLAG_NOPAD, + LOG_DEFAULT_ENCRYPTION_KEY, + info->key_version); ut_a(rc == MY_AES_OK); ut_a(dst_len == src_len); @@ -230,10 +223,11 @@ init_crypt_key( } uint dst_len; - int rc= my_aes_encrypt_ecb(info->crypt_msg, sizeof(info->crypt_msg), //src, srclen - info->crypt_key, &dst_len, //dst, &dstlen - (unsigned char*)&mysqld_key, sizeof(mysqld_key), - NULL, 0, 1); + int rc= my_aes_crypt(MY_AES_ECB, ENCRYPTION_FLAG_NOPAD|ENCRYPTION_FLAG_ENCRYPT, + info->crypt_msg, sizeof(info->crypt_msg), //src, srclen + info->crypt_key, &dst_len, //dst, &dstlen + (unsigned char*)&mysqld_key, sizeof(mysqld_key), + NULL, 0); if (rc != MY_AES_OK || dst_len != MY_AES_BLOCK_SIZE) { fprintf(stderr, @@ -296,7 +290,7 @@ log_blocks_encrypt( const ulint size, /*!< in: size of blocks, must be multiple of a log block */ byte* dst_block) /*!< out: blocks after encryption */ { - return log_blocks_crypt(block, size, dst_block, true); + return log_blocks_crypt(block, size, dst_block, ENCRYPTION_FLAG_ENCRYPT); } /*********************************************************************//** @@ -366,7 +360,7 @@ log_encrypt_before_write( byte* dst_frame = (byte*)malloc(size); //encrypt log blocks content - Crypt_result result = log_blocks_crypt(block, size, dst_frame, true); + Crypt_result result = log_blocks_crypt(block, size, dst_frame, ENCRYPTION_FLAG_ENCRYPT); if (result == MY_AES_OK) { ut_ad(block[0] == dst_frame[0]); @@ -392,7 +386,7 @@ log_decrypt_after_read( byte* dst_frame = (byte*)malloc(size); // decrypt log blocks content - Crypt_result result = log_blocks_crypt(frame, size, dst_frame, false); + Crypt_result result = log_blocks_crypt(frame, size, dst_frame, ENCRYPTION_FLAG_DECRYPT); if (result == MY_AES_OK) { memcpy(frame, dst_frame, size); diff --git a/storage/xtradb/os/os0file.cc b/storage/xtradb/os/os0file.cc index cb074cdf4f5..9ef9adf5b74 100644 --- a/storage/xtradb/os/os0file.cc +++ b/storage/xtradb/os/os0file.cc @@ -1,6 +1,6 @@ /*********************************************************************** -Copyright (c) 1995, 2014, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 1995, 2015, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2009, Percona Inc. Copyright (c) 2013, 2015, MariaDB Corporation. @@ -3604,8 +3604,9 @@ os_file_get_status( stat_info->type = OS_FILE_TYPE_LINK; break; case S_IFBLK: - stat_info->type = OS_FILE_TYPE_BLOCK; - break; + /* Handle block device as regular file. */ + case S_IFCHR: + /* Handle character device as regular file. */ case S_IFREG: stat_info->type = OS_FILE_TYPE_FILE; break; @@ -3614,8 +3615,8 @@ os_file_get_status( } - if (check_rw_perm && (stat_info->type == OS_FILE_TYPE_FILE - || stat_info->type == OS_FILE_TYPE_BLOCK)) { + if (check_rw_perm && stat_info->type == OS_FILE_TYPE_FILE) { + int fh; int access; diff --git a/storage/xtradb/page/page0page.cc b/storage/xtradb/page/page0page.cc index 4aff88818bb..6adbdeb95a4 100644 --- a/storage/xtradb/page/page0page.cc +++ b/storage/xtradb/page/page0page.cc @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1994, 2013, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 1994, 2015, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2012, Facebook Inc. This program is free software; you can redistribute it and/or modify it under @@ -31,6 +31,11 @@ Created 2/2/1994 Heikki Tuuri #endif #undef THIS_MODULE +#include "ha_prototypes.h" +#include "buf0checksum.h" + +#ifndef UNIV_INNOCHECKSUM + #include "page0cur.h" #include "page0zip.h" #include "buf0buf.h" @@ -2819,3 +2824,51 @@ page_find_rec_max_not_deleted( } return(prev_rec); } + +#endif /* #ifndef UNIV_INNOCHECKSUM */ + +/** Issue a warning when the checksum that is stored in the page is valid, +but different than the global setting innodb_checksum_algorithm. +@param[in] current_algo current checksum algorithm +@param[in] page_checksum page valid checksum +@param[in] space_id tablespace id +@param[in] page_no page number */ +void +page_warn_strict_checksum( + srv_checksum_algorithm_t curr_algo, + srv_checksum_algorithm_t page_checksum, + ulint space_id, + ulint page_no) +{ + srv_checksum_algorithm_t curr_algo_nonstrict; + switch (curr_algo) { + case SRV_CHECKSUM_ALGORITHM_STRICT_CRC32: + curr_algo_nonstrict = SRV_CHECKSUM_ALGORITHM_CRC32; + break; + case SRV_CHECKSUM_ALGORITHM_STRICT_INNODB: + curr_algo_nonstrict = SRV_CHECKSUM_ALGORITHM_INNODB; + break; + case SRV_CHECKSUM_ALGORITHM_STRICT_NONE: + curr_algo_nonstrict = SRV_CHECKSUM_ALGORITHM_NONE; + break; + default: + ut_error; + } + +#ifdef UNIV_INNOCHECKSUM + fprintf(stderr, +#else + ib_logf(IB_LOG_LEVEL_WARN, +#endif + "innodb_checksum_algorithm is set to \"%s\"" + " but the page [page id: space=" ULINTPF "," + " page number=" ULINTPF "] contains a valid checksum \"%s\"." + " Accepting the page as valid. Change innodb_checksum_algorithm" + " to \"%s\" to silently accept such pages or rewrite all pages" + " so that they contain \"%s\" checksum.", + buf_checksum_algorithm_name(curr_algo), + space_id, page_no, + buf_checksum_algorithm_name(page_checksum), + buf_checksum_algorithm_name(curr_algo_nonstrict), + buf_checksum_algorithm_name(curr_algo_nonstrict)); +} diff --git a/storage/xtradb/page/page0zip.cc b/storage/xtradb/page/page0zip.cc index 58559cc438b..42ffd3668d8 100644 --- a/storage/xtradb/page/page0zip.cc +++ b/storage/xtradb/page/page0zip.cc @@ -37,30 +37,40 @@ using namespace std; # include "page0zip.ic" #endif #undef THIS_MODULE +#include "buf0checksum.h" #include "page0page.h" +#ifndef UNIV_INNOCHECKSUM #include "mtr0log.h" -#include "ut0sort.h" #include "dict0dict.h" #include "btr0cur.h" -#include "page0types.h" #include "log0recv.h" +#endif /* !UNIV_INNOCHECKSUM */ #include "zlib.h" +#include "fil0fil.h" +#include "ut0sort.h" +#include "page0types.h" #ifndef UNIV_HOTBACKUP +#ifndef UNIV_INNOCHECKSUM # include "buf0buf.h" -# include "buf0lru.h" # include "btr0sea.h" # include "dict0boot.h" # include "lock0lock.h" # include "srv0mon.h" # include "srv0srv.h" +#endif /* !UNIV_INNOCHECKSUM */ +# include "buf0lru.h" # include "ut0crc32.h" #else /* !UNIV_HOTBACKUP */ -# include "buf0checksum.h" # define lock_move_reorganize_page(block, temp_block) ((void) 0) # define buf_LRU_stat_inc_unzip() ((void) 0) #endif /* !UNIV_HOTBACKUP */ +#ifdef UNIV_INNOCHECKSUM +#include "mach0data.h" +#endif /* UNIV_INNOCHECKSUM */ + #ifndef UNIV_HOTBACKUP +#ifndef UNIV_INNOCHECKSUM /** Statistics on compression, indexed by page_zip_des_t::ssize - 1 */ UNIV_INTERN page_zip_stat_t page_zip_stat[PAGE_ZIP_SSIZE_MAX]; /** Statistics on compression, indexed by index->id */ @@ -70,6 +80,7 @@ UNIV_INTERN ib_mutex_t page_zip_stat_per_index_mutex; #ifdef HAVE_PSI_INTERFACE UNIV_INTERN mysql_pfs_key_t page_zip_stat_per_index_mutex_key; #endif /* HAVE_PSI_INTERFACE */ +#endif /* !UNIV_INNOCHECKSUM */ #endif /* !UNIV_HOTBACKUP */ /* Compression level to be used by zlib. Settable by user. */ @@ -82,6 +93,8 @@ UNIV_INTERN my_bool page_zip_log_pages = false; /* Please refer to ../include/page0zip.ic for a description of the compressed page format. */ +#ifndef UNIV_INNOCHECKSUM + /* The infimum and supremum records are omitted from the compressed page. On compress, we compare that the records are there, and on uncompress we restore the records. */ @@ -105,6 +118,8 @@ static const byte supremum_extra_data[] = { 0x65, 0x6d, 0x75, 0x6d /* "supremum" */ }; +#endif /* !UNIV_INNOCHECKSUM */ + /** Assert that a block of memory is filled with zero bytes. Compare at most sizeof(field_ref_zero) bytes. @param b in: memory block @@ -151,6 +166,7 @@ page_zip_fail_func( # define page_zip_fail(fmt_args) /* empty */ #endif /* UNIV_DEBUG || UNIV_ZIP_DEBUG */ +#ifndef UNIV_INNOCHECKSUM #ifndef UNIV_HOTBACKUP /**********************************************************************//** Determine the guaranteed free space on an empty page. @@ -4843,6 +4859,7 @@ corrupt: return(ptr + 8 + size + trailer_size); } +#endif /* !UNIV_INNOCHECKSUM */ /**********************************************************************//** Calculate the compressed page checksum. @@ -4918,6 +4935,10 @@ page_zip_verify_checksum( stored = static_cast<ib_uint32_t>(mach_read_from_4( static_cast<const unsigned char*>(data) + FIL_PAGE_SPACE_OR_CHKSUM)); + ulint page_no = mach_read_from_4(static_cast<const unsigned char*> (data) + FIL_PAGE_OFFSET); + ulint space_id = mach_read_from_4(static_cast<const unsigned char*> + (data) + FIL_PAGE_SPACE_ID); + #if FIL_PAGE_LSN % 8 #error "FIL_PAGE_LSN must be 64 bit aligned" #endif @@ -4938,40 +4959,113 @@ page_zip_verify_checksum( return(TRUE); } + const srv_checksum_algorithm_t curr_algo = + static_cast<srv_checksum_algorithm_t>(srv_checksum_algorithm); + + if (curr_algo == SRV_CHECKSUM_ALGORITHM_NONE) { + return(TRUE); + } + calc = static_cast<ib_uint32_t>(page_zip_calc_checksum( - data, size, static_cast<srv_checksum_algorithm_t>( - srv_checksum_algorithm))); + data, size, curr_algo)); if (stored == calc) { return(TRUE); } - switch ((srv_checksum_algorithm_t) srv_checksum_algorithm) { + switch (curr_algo) { case SRV_CHECKSUM_ALGORITHM_STRICT_CRC32: - case SRV_CHECKSUM_ALGORITHM_STRICT_INNODB: - case SRV_CHECKSUM_ALGORITHM_STRICT_NONE: - return(stored == calc); case SRV_CHECKSUM_ALGORITHM_CRC32: + if (stored == BUF_NO_CHECKSUM_MAGIC) { + if (curr_algo + == SRV_CHECKSUM_ALGORITHM_STRICT_CRC32) { + page_warn_strict_checksum( + curr_algo, + SRV_CHECKSUM_ALGORITHM_NONE, + space_id, page_no); + } + return(TRUE); } - crc32 = calc; + innodb = static_cast<ib_uint32_t>(page_zip_calc_checksum( data, size, SRV_CHECKSUM_ALGORITHM_INNODB)); + + if (stored == innodb) { + if (curr_algo + == SRV_CHECKSUM_ALGORITHM_STRICT_CRC32) { + page_warn_strict_checksum( + curr_algo, + SRV_CHECKSUM_ALGORITHM_INNODB, + space_id, page_no); + } + + return(TRUE); + } + break; + case SRV_CHECKSUM_ALGORITHM_STRICT_INNODB: case SRV_CHECKSUM_ALGORITHM_INNODB: + if (stored == BUF_NO_CHECKSUM_MAGIC) { + if (curr_algo + == SRV_CHECKSUM_ALGORITHM_STRICT_INNODB) { + page_warn_strict_checksum( + curr_algo, + SRV_CHECKSUM_ALGORITHM_NONE, + space_id, page_no); + } + return(TRUE); } + crc32 = static_cast<ib_uint32_t>(page_zip_calc_checksum( data, size, SRV_CHECKSUM_ALGORITHM_CRC32)); - innodb = calc; + + if (stored == crc32) { + if (curr_algo + == SRV_CHECKSUM_ALGORITHM_STRICT_INNODB) { + page_warn_strict_checksum( + curr_algo, + SRV_CHECKSUM_ALGORITHM_CRC32, + space_id, page_no); + } + + return(TRUE); + } + + break; + case SRV_CHECKSUM_ALGORITHM_STRICT_NONE: + + crc32 = static_cast<ib_uint32_t>(page_zip_calc_checksum( + data, size, SRV_CHECKSUM_ALGORITHM_CRC32)); + + if (stored == crc32) { + page_warn_strict_checksum( + curr_algo, SRV_CHECKSUM_ALGORITHM_CRC32, + space_id, page_no); + + return(TRUE); + } + + innodb = static_cast<ib_uint32_t>(page_zip_calc_checksum( + data, size, SRV_CHECKSUM_ALGORITHM_INNODB)); + + if (stored == innodb) { + page_warn_strict_checksum( + curr_algo, + SRV_CHECKSUM_ALGORITHM_INNODB, + space_id, page_no); + return(TRUE); + } + break; case SRV_CHECKSUM_ALGORITHM_NONE: - return(TRUE); + ut_error; /* no default so the compiler will emit a warning if new enum is added and not handled here */ } - return(stored == crc32 || stored == innodb); + return(FALSE); } diff --git a/storage/xtradb/row/row0import.cc b/storage/xtradb/row/row0import.cc index d5f766ef51b..88846976064 100644 --- a/storage/xtradb/row/row0import.cc +++ b/storage/xtradb/row/row0import.cc @@ -3651,7 +3651,7 @@ row_import_for_mysql( err = fil_open_single_table_tablespace( true, true, table->space, dict_tf_to_fsp_flags(table->flags), - table->name, filepath); + table->name, filepath, table); DBUG_EXECUTE_IF("ib_import_open_tablespace_failure", err = DB_TABLESPACE_NOT_FOUND;); diff --git a/storage/xtradb/row/row0ins.cc b/storage/xtradb/row/row0ins.cc index fb719266a16..3f22a7d422d 100644 --- a/storage/xtradb/row/row0ins.cc +++ b/storage/xtradb/row/row0ins.cc @@ -2350,7 +2350,7 @@ row_ins_clust_index_entry_low( { btr_cur_t cursor; ulint* offsets = NULL; - dberr_t err; + dberr_t err = DB_SUCCESS; big_rec_t* big_rec = NULL; mtr_t mtr; mem_heap_t* offsets_heap = NULL; @@ -2380,9 +2380,16 @@ row_ins_clust_index_entry_low( the function will return in both low_match and up_match of the cursor sensible values */ - btr_cur_search_to_nth_level(index, 0, entry, PAGE_CUR_LE, mode, + err = btr_cur_search_to_nth_level(index, 0, entry, PAGE_CUR_LE, mode, &cursor, 0, __FILE__, __LINE__, &mtr); + if (err != DB_SUCCESS) { + index->table->is_encrypted = true; + index->table->ibd_file_missing = true; + mtr_commit(&mtr); + goto func_exit; + } + #ifdef UNIV_DEBUG { page_t* page = btr_cur_get_page(&cursor); @@ -2696,9 +2703,22 @@ row_ins_sec_index_entry_low( search_mode |= BTR_IGNORE_SEC_UNIQUE; } - btr_cur_search_to_nth_level(index, 0, entry, PAGE_CUR_LE, - search_mode, - &cursor, 0, __FILE__, __LINE__, &mtr); + err = btr_cur_search_to_nth_level(index, 0, entry, PAGE_CUR_LE, + search_mode, + &cursor, 0, __FILE__, __LINE__, &mtr); + + if (err != DB_SUCCESS) { + if (err == DB_ENCRYPTED_DECRYPT_FAILED) { + ib_push_warning(trx->mysql_thd, + DB_ENCRYPTED_DECRYPT_FAILED, + "Table %s is encrypted but encryption service or" + " used key_id is not available. " + " Can't continue reading table.", + index->table->name); + index->table->is_encrypted = true; + } + goto func_exit; + } if (cursor.flag == BTR_CUR_INSERT_TO_IBUF) { /* The insert was buffered during the search: we are done */ @@ -2765,6 +2785,8 @@ row_ins_sec_index_entry_low( goto func_exit; } + DEBUG_SYNC_C("row_ins_sec_index_entry_dup_locks_created"); + /* We did not find a duplicate and we have now locked with s-locks the necessary records to prevent any insertion of a duplicate by another diff --git a/storage/xtradb/row/row0merge.cc b/storage/xtradb/row/row0merge.cc index 2df9c2bea82..20fc1768eda 100644 --- a/storage/xtradb/row/row0merge.cc +++ b/storage/xtradb/row/row0merge.cc @@ -1433,6 +1433,13 @@ row_merge_read_clustered_index( row_ext_t* ext; page_cur_t* cur = btr_pcur_get_page_cur(&pcur); + /* Do not continue if table pages are still encrypted */ + if (old_table->is_encrypted || new_table->is_encrypted) { + err = DB_ENCRYPTED_DECRYPT_FAILED; + trx->error_key_num = 0; + goto func_exit; + } + page_cur_move_to_next(cur); if (page_cur_is_after_last(cur)) { @@ -3762,6 +3769,17 @@ row_merge_build_indexes( pct_cost = COST_READ_CLUSTERED_INDEX * 100 / (total_static_cost + total_dynamic_cost); + /* Do not continue if we can't encrypt table pages */ + if (old_table->is_encrypted || new_table->is_encrypted) { + error = DB_ENCRYPTED_DECRYPT_FAILED; + ib_push_warning(trx->mysql_thd, DB_ENCRYPTED_DECRYPT_FAILED, + "Table %s is encrypted but encryption service or" + " used key_id is not available. " + " Can't continue reading table.", + old_table->is_encrypted ? old_table->name : new_table->name); + goto func_exit; + } + /* Read clustered index of the table and create files for secondary index entries for merge sort */ diff --git a/storage/xtradb/row/row0mysql.cc b/storage/xtradb/row/row0mysql.cc index efc5c568cd5..64c6783f1e4 100644 --- a/storage/xtradb/row/row0mysql.cc +++ b/storage/xtradb/row/row0mysql.cc @@ -622,6 +622,8 @@ handle_new_error: case DB_FTS_INVALID_DOCID: case DB_INTERRUPTED: case DB_DICT_CHANGED: + case DB_TABLE_NOT_FOUND: + case DB_ENCRYPTED_DECRYPT_FAILED: if (savept) { /* Roll back the latest, possibly incomplete insertion or update */ @@ -1314,7 +1316,13 @@ row_insert_for_mysql( prebuilt->table->name); return(DB_TABLESPACE_NOT_FOUND); - + } else if (prebuilt->table->is_encrypted) { + ib_push_warning(trx, DB_ENCRYPTED_DECRYPT_FAILED, + "Table %s in tablespace %lu encrypted." + "However key management plugin or used key_id is not found or" + " used encryption algorithm or method does not match.", + prebuilt->table->name, prebuilt->table->space); + return(DB_ENCRYPTED_DECRYPT_FAILED); } else if (prebuilt->magic_n != ROW_PREBUILT_ALLOCATED) { fprintf(stderr, "InnoDB: Error: trying to free a corrupt\n" @@ -1326,18 +1334,14 @@ row_insert_for_mysql( mem_analyze_corruption(prebuilt); ut_error; - } else if (srv_created_new_raw || srv_force_recovery) { - fputs("InnoDB: A new raw disk partition was initialized or\n" - "InnoDB: innodb_force_recovery is on: we do not allow\n" + } else if (srv_force_recovery) { + fputs("InnoDB: innodb_force_recovery is on: we do not allow\n" "InnoDB: database modifications by the user. Shut down\n" "InnoDB: mysqld and edit my.cnf so that" - " newraw is replaced\n" - "InnoDB: with raw, and innodb_force_... is removed.\n", + "InnoDB: innodb_force_... is removed.\n", stderr); - if(srv_force_recovery) { - return(DB_READ_ONLY); - } - return(DB_ERROR); + + return(DB_READ_ONLY); } trx->op_info = "inserting"; @@ -1717,6 +1721,13 @@ row_update_for_mysql( "InnoDB: how you can resolve the problem.\n", prebuilt->table->name); return(DB_ERROR); + } else if (prebuilt->table->is_encrypted) { + ib_push_warning(trx, DB_ENCRYPTED_DECRYPT_FAILED, + "Table %s in tablespace %lu encrypted." + "However key management plugin or used key_id is not found or" + " used encryption algorithm or method does not match.", + prebuilt->table->name, prebuilt->table->space); + return (DB_TABLE_NOT_FOUND); } if (UNIV_UNLIKELY(prebuilt->magic_n != ROW_PREBUILT_ALLOCATED)) { @@ -1732,18 +1743,14 @@ row_update_for_mysql( ut_error; } - if (UNIV_UNLIKELY(srv_created_new_raw || srv_force_recovery)) { - fputs("InnoDB: A new raw disk partition was initialized or\n" - "InnoDB: innodb_force_recovery is on: we do not allow\n" + if (UNIV_UNLIKELY(srv_force_recovery)) { + fputs("InnoDB: innodb_force_recovery is on: we do not allow\n" "InnoDB: database modifications by the user. Shut down\n" - "InnoDB: mysqld and edit my.cnf so that newraw" - " is replaced\n" - "InnoDB: with raw, and innodb_force_... is removed.\n", + "InnoDB: mysqld and edit my.cnf so that" + "InnoDB: innodb_force_... is removed.\n", stderr); - if(srv_force_recovery) { - return(DB_READ_ONLY); - } - return(DB_ERROR); + + return(DB_READ_ONLY); } DEBUG_SYNC_C("innodb_row_update_for_mysql_begin"); @@ -2254,22 +2261,6 @@ row_create_table_for_mysql( goto err_exit; ); - if (srv_created_new_raw) { - fputs("InnoDB: A new raw disk partition was initialized:\n" - "InnoDB: we do not allow database modifications" - " by the user.\n" - "InnoDB: Shut down mysqld and edit my.cnf so that newraw" - " is replaced with raw.\n", stderr); -err_exit: - dict_mem_table_free(table); - - if (commit) { - trx_commit_for_mysql(trx); - } - - return(DB_ERROR); - } - trx->op_info = "creating table"; if (row_mysql_is_system_table(table->name)) { @@ -2280,7 +2271,19 @@ err_exit: "InnoDB: MySQL system tables must be" " of the MyISAM type!\n", table->name); - goto err_exit; + +#ifndef DBUG_OFF +err_exit: +#endif /* !DBUG_OFF */ + dict_mem_table_free(table); + + if (commit) { + trx_commit_for_mysql(trx); + } + + trx->op_info = ""; + + return(DB_ERROR); } trx_start_if_not_started_xa(trx); @@ -3365,16 +3368,6 @@ row_truncate_table_for_mysql( ut_ad(table); - if (srv_created_new_raw) { - fputs("InnoDB: A new raw disk partition was initialized:\n" - "InnoDB: we do not allow database modifications" - " by the user.\n" - "InnoDB: Shut down mysqld and edit my.cnf so that newraw" - " is replaced with raw.\n", stderr); - - return(DB_ERROR); - } - if (dict_table_is_discarded(table)) { return(DB_TABLESPACE_DELETED); } else if (table->ibd_file_missing) { @@ -3864,16 +3857,6 @@ row_drop_table_for_mysql( ut_a(name != NULL); - if (srv_created_new_raw) { - fputs("InnoDB: A new raw disk partition was initialized:\n" - "InnoDB: we do not allow database modifications" - " by the user.\n" - "InnoDB: Shut down mysqld and edit my.cnf so that newraw" - " is replaced with raw.\n", stderr); - - DBUG_RETURN(DB_ERROR); - } - /* The table name is prefixed with the database name and a '/'. Certain table names starting with 'innodb_' have their special meaning regardless of the database name. Thus, we need to @@ -3964,6 +3947,19 @@ row_drop_table_for_mysql( goto funct_exit; } + /* If table is encrypted and table page encryption failed + mark this table read only. */ + if (table->is_encrypted) { + + if (table->can_be_evicted) { + dict_table_move_from_lru_to_non_lru(table); + } + + dict_table_close(table, TRUE, FALSE); + err = DB_READ_ONLY; + goto funct_exit; + } + /* Turn on this drop bit before we could release the dictionary latch */ table->to_be_dropped = true; @@ -4892,19 +4888,16 @@ row_rename_table_for_mysql( ut_a(new_name != NULL); ut_ad(trx->state == TRX_STATE_ACTIVE); - if (srv_created_new_raw || srv_force_recovery) { - fputs("InnoDB: A new raw disk partition was initialized or\n" - "InnoDB: innodb_force_recovery is on: we do not allow\n" + if (srv_force_recovery) { + fputs("InnoDB: innodb_force_recovery is on: we do not allow\n" "InnoDB: database modifications by the user. Shut down\n" - "InnoDB: mysqld and edit my.cnf so that newraw" - " is replaced\n" - "InnoDB: with raw, and innodb_force_... is removed.\n", + "InnoDB: mysqld and edit my.cnf so that" + "InnoDB: innodb_force_... is removed.\n", stderr); - if(srv_force_recovery) { - err = DB_READ_ONLY; - } + err = DB_READ_ONLY; goto funct_exit; + } else if (row_mysql_is_system_table(new_name)) { fprintf(stderr, diff --git a/storage/xtradb/row/row0sel.cc b/storage/xtradb/row/row0sel.cc index b1d64c16c60..a1edbca757c 100644 --- a/storage/xtradb/row/row0sel.cc +++ b/storage/xtradb/row/row0sel.cc @@ -2,6 +2,7 @@ Copyright (c) 1997, 2013, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2008, Google Inc. +Copyright (c) 2015, MariaDB Corporation. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described @@ -3726,6 +3727,9 @@ row_search_for_mysql( return(DB_TABLESPACE_NOT_FOUND); + } else if (prebuilt->table->is_encrypted) { + + return(DB_ENCRYPTED_DECRYPT_FAILED); } else if (!prebuilt->index_usable) { return(DB_MISSING_HISTORY); @@ -4137,9 +4141,14 @@ wait_table_again: } else if (dtuple_get_n_fields(search_tuple) > 0) { - btr_pcur_open_with_no_init(index, search_tuple, mode, - BTR_SEARCH_LEAF, - pcur, 0, &mtr); + err = btr_pcur_open_with_no_init(index, search_tuple, mode, + BTR_SEARCH_LEAF, + pcur, 0, &mtr); + + if (err != DB_SUCCESS) { + rec = NULL; + goto lock_wait_or_error; + } pcur->trx_if_known = trx; @@ -4173,9 +4182,23 @@ wait_table_again: } } } else if (mode == PAGE_CUR_G || mode == PAGE_CUR_L) { - btr_pcur_open_at_index_side( + err = btr_pcur_open_at_index_side( mode == PAGE_CUR_G, index, BTR_SEARCH_LEAF, pcur, false, 0, &mtr); + + if (err != DB_SUCCESS) { + if (err == DB_ENCRYPTED_DECRYPT_FAILED) { + ib_push_warning(trx->mysql_thd, + DB_ENCRYPTED_DECRYPT_FAILED, + "Table %s is encrypted but encryption service or" + " used key_id is not available. " + " Can't continue reading table.", + prebuilt->table->name); + index->table->is_encrypted = true; + } + rec = NULL; + goto lock_wait_or_error; + } } rec_loop: @@ -4191,6 +4214,11 @@ rec_loop: rec = btr_pcur_get_rec(pcur); + if (!rec) { + err = DB_ENCRYPTED_DECRYPT_FAILED; + goto lock_wait_or_error; + } + SRV_CORRUPT_TABLE_CHECK(rec, { err = DB_CORRUPTION; @@ -5132,7 +5160,9 @@ lock_wait_or_error: /*-------------------------------------------------------------*/ - btr_pcur_store_position(pcur, &mtr); + if (rec) { + btr_pcur_store_position(pcur, &mtr); + } lock_table_wait: mtr_commit(&mtr); diff --git a/storage/xtradb/srv/srv0start.cc b/storage/xtradb/srv/srv0start.cc index 3f558170f97..54ac04e7248 100644 --- a/storage/xtradb/srv/srv0start.cc +++ b/storage/xtradb/srv/srv0start.cc @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. +Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved. Copyright (c) 2008, Google Inc. Copyright (c) 2009, Percona Inc. Copyright (c) 2013, 2015, MariaDB Corporation @@ -264,8 +264,8 @@ srv_file_check_mode( /* Note: stat.rw_perm is only valid of files */ - if (stat.type == OS_FILE_TYPE_FILE - || stat.type == OS_FILE_TYPE_BLOCK) { + if (stat.type == OS_FILE_TYPE_FILE) { + if (!stat.rw_perm) { ib_logf(IB_LOG_LEVEL_ERROR, @@ -462,14 +462,18 @@ srv_parse_data_file_paths_and_sizes( && *(str + 1) == 'e' && *(str + 2) == 'w') { str += 3; - (srv_data_file_is_raw_partition)[i] = SRV_NEW_RAW; + /* Initialize new raw device only during bootstrap */ + (srv_data_file_is_raw_partition)[i] = + opt_bootstrap ? SRV_NEW_RAW : SRV_OLD_RAW; } if (*str == 'r' && *(str + 1) == 'a' && *(str + 2) == 'w') { str += 3; + /* Initialize new raw device only during bootstrap */ if ((srv_data_file_is_raw_partition)[i] == 0) { - (srv_data_file_is_raw_partition)[i] = SRV_OLD_RAW; + (srv_data_file_is_raw_partition)[i] = + opt_bootstrap ? SRV_NEW_RAW : SRV_OLD_RAW; } } @@ -945,6 +949,21 @@ open_or_create_data_files( return(DB_ERROR); } + + const char* check_msg; + check_msg = fil_read_first_page( + files[i], FALSE, &flags, &space, + min_flushed_lsn, max_flushed_lsn, NULL); + + /* If first page is valid, don't overwrite DB. + It prevents overwriting DB when mysql_install_db + starts mysqld multiple times during bootstrap. */ + if (check_msg == NULL) { + + srv_created_new_raw = FALSE; + ret = FALSE; + } + } else if (srv_data_file_is_raw_partition[i] == SRV_OLD_RAW) { srv_start_raw_disk_in_use = TRUE; diff --git a/storage/xtradb/sync/sync0arr.cc b/storage/xtradb/sync/sync0arr.cc index 01cae70a8ce..d881c5de2f5 100644 --- a/storage/xtradb/sync/sync0arr.cc +++ b/storage/xtradb/sync/sync0arr.cc @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1995, 2013, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 1995, 2015, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2008, Google Inc. Copyright (c) 2013, 2015, MariaDB Corporation. All Rights Reserved. @@ -1206,8 +1206,8 @@ sync_array_print_info_low( ulint count = 0; fprintf(file, - "OS WAIT ARRAY INFO: reservation count %ld\n", - (long) arr->res_count); + "OS WAIT ARRAY INFO: reservation count " ULINTPF "\n", + arr->res_count); for (i = 0; count < arr->n_reserved; ++i) { sync_cell_t* cell; @@ -1303,7 +1303,7 @@ sync_array_print( } fprintf(file, - "OS WAIT ARRAY INFO: signal count %ld\n", (long) sg_count); + "OS WAIT ARRAY INFO: signal count " ULINTPF "\n", sg_count); } diff --git a/storage/xtradb/trx/trx0sys.cc b/storage/xtradb/trx/trx0sys.cc index 32948d6847c..bebd28b0df3 100644 --- a/storage/xtradb/trx/trx0sys.cc +++ b/storage/xtradb/trx/trx0sys.cc @@ -1332,8 +1332,6 @@ trx_sys_close(void) /* Free the double write data structures. */ buf_dblwr_free(); - mutex_enter(&trx_sys->mutex); - ut_a(UT_LIST_GET_LEN(trx_sys->ro_trx_list) == 0); /* Only prepared transactions may be left in the system. Free them. */ @@ -1373,8 +1371,6 @@ trx_sys_close(void) ut_a(UT_LIST_GET_LEN(trx_sys->rw_trx_list) == 0); ut_a(UT_LIST_GET_LEN(trx_sys->mysql_trx_list) == 0); - mutex_exit(&trx_sys->mutex); - mutex_free(&trx_sys->mutex); ut_ad(trx_sys->descr_n_used == 0); diff --git a/storage/xtradb/trx/trx0trx.cc b/storage/xtradb/trx/trx0trx.cc index 5d3b86e158f..9bca6de9c26 100644 --- a/storage/xtradb/trx/trx0trx.cc +++ b/storage/xtradb/trx/trx0trx.cc @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1996, 2014, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved. 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 @@ -481,9 +481,7 @@ trx_free_prepared( ut_a(trx_state_eq(trx, TRX_STATE_PREPARED)); ut_a(trx->magic_n == TRX_MAGIC_N); - mutex_exit(&trx_sys->mutex); lock_trx_release_locks(trx); - mutex_enter(&trx_sys->mutex); trx_undo_free_prepared(trx); assert_trx_in_rw_list(trx); @@ -493,7 +491,9 @@ trx_free_prepared( UT_LIST_REMOVE(trx_list, trx_sys->rw_trx_list, trx); ut_d(trx->in_rw_trx_list = FALSE); + mutex_enter(&trx_sys->mutex); trx_release_descriptor(trx); + mutex_exit(&trx_sys->mutex); /* Undo trx_resurrect_table_locks(). */ UT_LIST_INIT(trx->lock.trx_locks); diff --git a/storage/xtradb/ut/ut0ut.cc b/storage/xtradb/ut/ut0ut.cc index 121cbdb7bc0..96f2c537deb 100644 --- a/storage/xtradb/ut/ut0ut.cc +++ b/storage/xtradb/ut/ut0ut.cc @@ -827,6 +827,8 @@ ut_strerr( return("Too many words in a FTS phrase or proximity search"); case DB_TOO_BIG_FOR_REDO: return("BLOB record length is greater than 10%% of redo log"); + case DB_ENCRYPTED_DECRYPT_FAILED: + return("Table is encrypted but decrypt failed."); /* do not add default: in order to produce a warning if new code is added to the enum but not added here */ |