1 /* Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
22
23 #include "sql/dd/impl/upgrade/dd.h"
24 #include "sql/dd/impl/bootstrap/bootstrapper.h"
25 #include "sql/dd/impl/utils.h"
26
27 #include <set>
28
29 #include "my_dbug.h"
30 #include "mysql/components/services/log_builtins.h"
31 #include "mysql_version.h" // MYSQL_VERSION_ID
32 #include "mysqld_error.h"
33 #include "sql/dd/cache/dictionary_client.h" // dd::cache::Dictionary_client
34 #include "sql/dd/impl/bootstrap/bootstrap_ctx.h" // DD_bootstrap_ctx
35 #include "sql/dd/impl/cache/shared_dictionary_cache.h" // Shared_dictionary_cache
36 #include "sql/dd/impl/system_registry.h" // dd::System_tables
37 #include "sql/dd/impl/tables/columns.h" // dd::tables::Columns
38 #include "sql/dd/impl/tables/dd_properties.h" // dd::tables::DD_properties
39 #include "sql/dd/impl/tables/events.h" // dd::tables::Events
40 #include "sql/dd/impl/tables/foreign_key_column_usage.h" // dd::tables::Fore...
41 #include "sql/dd/impl/tables/foreign_keys.h" // dd::tables::Foreign_keys
42 #include "sql/dd/impl/tables/index_partitions.h" // dd::tables::Index_partitions
43 #include "sql/dd/impl/tables/indexes.h" // dd::tables::Indexes
44 #include "sql/dd/impl/tables/routines.h" // dd::tables::Routines
45 #include "sql/dd/impl/tables/schemata.h" // dd::tables::Schemata
46 #include "sql/dd/impl/tables/table_partitions.h" // dd::tables::Table_partitions
47 #include "sql/dd/impl/tables/tables.h" // dd::tables::Tables
48 #include "sql/dd/impl/tables/tablespaces.h" // dd::tables::Tablespaces
49 #include "sql/dd/impl/tables/triggers.h" // dd::tables::Triggers
50 #include "sql/dd/object_id.h"
51 #include "sql/dd/types/object_table.h" // dd::Object_table
52 #include "sql/dd/types/object_table_definition.h" // dd::Object_table_definition
53 #include "sql/dd/types/schema.h"
54 #include "sql/sd_notify.h" // sysd::notify
55 #include "sql/sql_class.h" // THD
56 #include "sql/table.h" // MYSQL_SCHEMA_NAME
57
58 namespace dd {
59
60 namespace {
61 /*
62 Create the temporary schemas needed during upgrade, and fetch their ids.
63 */
64 /* purecov: begin inspected */
create_temporary_schemas(THD * thd,Object_id * mysql_schema_id,Object_id * target_table_schema_id,String_type * target_table_schema_name,Object_id * actual_table_schema_id)65 bool create_temporary_schemas(THD *thd, Object_id *mysql_schema_id,
66 Object_id *target_table_schema_id,
67 String_type *target_table_schema_name,
68 Object_id *actual_table_schema_id) {
69 /*
70 Find an unused target schema name. Prepare a base name, and append
71 a counter, increment until a non-existing name is found
72 */
73 dd::cache::Dictionary_client::Auto_releaser releaser(thd->dd_client());
74 const dd::Schema *schema = nullptr;
75 std::stringstream ss;
76
77 DBUG_ASSERT(target_table_schema_name != nullptr);
78 *target_table_schema_name = String_type("");
79 ss << "dd_upgrade_targets_" << MYSQL_VERSION_ID;
80 String_type tmp_schema_name_base{ss.str().c_str()};
81 int count = 0;
82 do {
83 if (thd->dd_client()->acquire(ss.str().c_str(), &schema)) return true;
84 if (schema == nullptr) {
85 *target_table_schema_name = ss.str().c_str();
86 break;
87 }
88 ss.str("");
89 ss.clear();
90 ss << tmp_schema_name_base << "_" << count++;
91 } while (count < 1000);
92
93 if (target_table_schema_name->empty()) {
94 LogErr(ERROR_LEVEL, ER_DD_UPGRADE_SCHEMA_UNAVAILABLE, ss.str().c_str());
95 return true;
96 }
97
98 /*
99 Find an unused schema name where we can temporarily move the actual
100 tables to be removed or modified.
101 */
102 String_type actual_table_schema_name{""};
103 ss.str("");
104 ss.clear();
105 ss << "dd_upgrade_garbage_" << MYSQL_VERSION_ID;
106 tmp_schema_name_base = String_type(ss.str().c_str());
107 count = 0;
108 do {
109 if (thd->dd_client()->acquire(ss.str().c_str(), &schema)) return true;
110 if (schema == nullptr) {
111 actual_table_schema_name = ss.str().c_str();
112 break;
113 }
114 ss.str("");
115 ss.clear();
116 ss << tmp_schema_name_base << "_" << count++;
117 } while (count < 1000);
118
119 if (actual_table_schema_name.empty()) {
120 LogErr(ERROR_LEVEL, ER_DD_UPGRADE_SCHEMA_UNAVAILABLE, ss.str().c_str());
121 return true;
122 }
123
124 /*
125 Store the schema names in DD_properties and commit. The schemas will
126 now be removed on next restart.
127 */
128 if (dd::tables::DD_properties::instance().set(thd, "UPGRADE_TARGET_SCHEMA",
129 *target_table_schema_name) ||
130 dd::tables::DD_properties::instance().set(thd, "UPGRADE_ACTUAL_SCHEMA",
131 actual_table_schema_name)) {
132 return dd::end_transaction(thd, true);
133 }
134
135 if (dd::end_transaction(thd, false)) return true;
136
137 if (dd::execute_query(
138 thd, dd::String_type("CREATE SCHEMA ") + actual_table_schema_name +
139 dd::String_type(" DEFAULT COLLATE '") +
140 dd::String_type(default_charset_info->name) + "'") ||
141 dd::execute_query(
142 thd, dd::String_type("CREATE SCHEMA ") + *target_table_schema_name +
143 dd::String_type(" DEFAULT COLLATE '") +
144 dd::String_type(default_charset_info->name) + "'") ||
145 dd::execute_query(thd,
146 dd::String_type("USE ") + *target_table_schema_name)) {
147 return true;
148 }
149
150 /*
151 Get hold of the schema ids of the temporary target schema, the
152 temporary actual schema, and the mysql schema. These are needed
153 later in various situations in the upgrade execution.
154 */
155 if (thd->dd_client()->acquire(MYSQL_SCHEMA_NAME.str, &schema) ||
156 schema == nullptr)
157 return true;
158
159 DBUG_ASSERT(mysql_schema_id != nullptr);
160 *mysql_schema_id = schema->id();
161 DBUG_ASSERT(*mysql_schema_id == 1);
162
163 if (thd->dd_client()->acquire(*target_table_schema_name, &schema) ||
164 schema == nullptr)
165 return true;
166
167 DBUG_ASSERT(target_table_schema_id != nullptr);
168 *target_table_schema_id = schema->id();
169
170 if (thd->dd_client()->acquire(actual_table_schema_name, &schema) ||
171 schema == nullptr)
172 return true;
173
174 DBUG_ASSERT(actual_table_schema_id != nullptr);
175 *actual_table_schema_id = schema->id();
176
177 return false;
178 }
179 /* purecov: end */
180
181 /*
182 Establish the sets of names of tables to be created and/or removed.
183 */
184 /* purecov: begin inspected */
establish_table_name_sets(std::set<String_type> * create_set,std::set<String_type> * remove_set)185 void establish_table_name_sets(std::set<String_type> *create_set,
186 std::set<String_type> *remove_set) {
187 /*
188 Establish the table change sets:
189 - The 'remove' set contains the tables that will eventually be removed,
190 i.e., they are present in the actual version, and either abandoned
191 or replaced by another table definition in the target version.
192 - The 'create' set contains the tables that must be created, i.e., they
193 are either new tables in the target version, or they replace an
194 existing table in the actual version.
195 */
196 DBUG_ASSERT(create_set != nullptr && create_set->empty());
197 DBUG_ASSERT(remove_set != nullptr && remove_set->empty());
198 for (System_tables::Const_iterator it = System_tables::instance()->begin();
199 it != System_tables::instance()->end(); ++it) {
200 if (is_non_inert_dd_or_ddse_table((*it)->property())) {
201 /*
202 In this context, all tables should have an Object_table. Minor
203 downgrade is the only situation where an Object_table may not exist,
204 but minor upgrade will never enter this code path.
205 */
206 DBUG_ASSERT((*it)->entity() != nullptr);
207
208 String_type target_ddl_statement("");
209 const Object_table_definition *target_table_def =
210 (*it)->entity()->target_table_definition();
211 /*
212 The target table definition may not be present if the table
213 is abandoned.
214 */
215 if (target_table_def) {
216 target_ddl_statement = target_table_def->get_ddl();
217 }
218
219 String_type actual_ddl_statement("");
220 const Object_table_definition *actual_table_def =
221 (*it)->entity()->actual_table_definition();
222 /*
223 The actual definition may not be present if this is a new table
224 which has been added.
225 */
226 if (actual_table_def) {
227 actual_ddl_statement = actual_table_def->get_ddl();
228 }
229
230 /*
231 Remove and/or create as needed. If the definitions are non-null
232 and equal, no change has been done, and hence upgrade of the table
233 is irrelevant.
234 */
235 if (target_table_def == nullptr && actual_table_def != nullptr)
236 remove_set->insert((*it)->entity()->name());
237 else if (target_table_def != nullptr && actual_table_def == nullptr)
238 create_set->insert((*it)->entity()->name());
239 else if (target_ddl_statement != actual_ddl_statement) {
240 /*
241 Abandoned tables that are not present will have target and actual
242 statements == "", and will therefore not be added to the create
243 nor remove set.
244 */
245 remove_set->insert((*it)->entity()->name());
246 create_set->insert((*it)->entity()->name());
247 }
248 }
249 }
250 }
251 /* purecov: end */
252
253 /**
254 Adjust metadata in source DD tables in mysql schema. This is done by
255 mostly executing UPDATE queries on them, but we do not migrate data to
256 destination DD tables.
257
258 @param thd Thread context.
259
260 @returns false if success. otherwise true.
261 */
262 /* purecov: begin inspected */
update_meta_data(THD * thd)263 bool update_meta_data(THD *thd) {
264 /*
265 Turn off foreign key checks while migrating the meta data.
266 */
267 if (dd::execute_query(thd, "SET FOREIGN_KEY_CHECKS= 0"))
268 return dd::end_transaction(thd, true);
269
270 /* Version dependent migration of meta data can be added here. */
271
272 /*
273 8.0.11 allowed entries with 0 timestamps to be created. These must
274 be updated, otherwise, upgrade will fail since 0 timestamps are not
275 allowed with the default SQL mode.
276 */
277 if (bootstrap::DD_bootstrap_ctx::instance().is_dd_upgrade_from_before(
278 bootstrap::DD_VERSION_80012)) {
279 if (dd::execute_query(thd,
280 "UPDATE mysql.tables SET last_altered = "
281 "CURRENT_TIMESTAMP WHERE last_altered = 0"))
282 return dd::end_transaction(thd, true);
283 if (dd::execute_query(thd,
284 "UPDATE mysql.tables SET created = CURRENT_TIMESTAMP "
285 "WHERE created = 0"))
286 return dd::end_transaction(thd, true);
287 }
288
289 /* Upgrade from 80015. */
290 if (bootstrap::DD_bootstrap_ctx::instance().is_dd_upgrade_from_before(
291 bootstrap::DD_VERSION_80016)) {
292 // A) REMOVE 'ENCRYPTION' KEY FROM MYSQL.TABLESPACES.OPTIONS FOR
293 // NON-INNODB TABLESPACES/TABLES
294
295 /*
296 Remove ENCRYPTION clause for unencrypted non-InnoDB tablespaces.
297 Because its only InnoDB that support encryption in 8.0.16.
298 */
299 static_assert(dd::tables::Tablespaces::NUMBER_OF_FIELDS == 7,
300 "SQL statements rely on a specific table definition");
301 if (dd::execute_query(
302 thd,
303 "UPDATE mysql.tablespaces ts "
304 "SET ts.options=REMOVE_DD_PROPERTY_KEY(ts.options, 'encryption') "
305 "WHERE ts.engine!='InnoDB' AND "
306 "GET_DD_PROPERTY_KEY_VALUE(ts.options,'encryption') IS NOT "
307 "NULL")) {
308 return dd::end_transaction(thd, true);
309 }
310
311 // Remove ENCRYPTION clause for non-InnoDB tables.
312 static_assert(dd::tables::Tables::NUMBER_OF_FIELDS == 37,
313 "SQL statements rely on a specific table definition");
314 if (dd::execute_query(
315 thd,
316 "UPDATE mysql.tables tbl "
317 "SET tbl.options=REMOVE_DD_PROPERTY_KEY(tbl.options, "
318 "'encrypt_type') "
319 "WHERE tbl.tablespace_id IS NULL AND tbl.engine!='InnoDB' AND "
320 "GET_DD_PROPERTY_KEY_VALUE(tbl.options,'encrypt_type') IS NOT "
321 "NULL")) {
322 return dd::end_transaction(thd, true);
323 }
324
325 // B) UPDATE MYSQL.TABLESPACES.OPTIONS 'ENCRYPTION' KEY FOR INNODB SE.
326
327 /*
328 Add ENCRYPTION clause for InnoDB file-per-table tablespaces used by
329 partitioned table.
330
331 For a partitioned table using file-per-table tablespace, the
332 tablespace_id is stored in tables corresponding
333 mysql.index_partitions.tablespace_id. The following query finds the
334 partitioned tablespace by joining several DD tables and updates
335 the 'encryption' key same as that of tables encryption type.
336
337 This is done as we expect all innodb tablespaces to have proper
338 'encryption' flag set.
339 */
340 static_assert(dd::tables::Index_partitions::NUMBER_OF_FIELDS == 5,
341 "SQL statements rely on a specific table definition");
342 static_assert(dd::tables::Table_partitions::NUMBER_OF_FIELDS == 12,
343 "SQL statements rely on a specific table definition");
344 static_assert(dd::tables::Tables::NUMBER_OF_FIELDS == 37,
345 "SQL statements rely on a specific table definition");
346 static_assert(dd::tables::Indexes::NUMBER_OF_FIELDS == 17,
347 "SQL statements rely on a specific table definition");
348 if (dd::execute_query(
349 thd,
350 "UPDATE mysql.index_partitions ip "
351 "JOIN mysql.tablespaces ts ON ts.id = ip.tablespace_id "
352 "JOIN mysql.table_partitions p ON p.id = ip.partition_id "
353 "JOIN mysql.tables t ON t.id = p.table_id "
354 "JOIN mysql.indexes i ON i.table_id = t.id "
355 "SET ts.options=CONCAT(IFNULL(ts.options,''), "
356 "IF(LOWER(GET_DD_PROPERTY_KEY_VALUE(t.options,'encrypt_type'))='y' "
357 ", 'encryption=Y;','encryption=N;')) "
358 "WHERE t.tablespace_id IS NULL AND i.tablespace_id IS NULL AND "
359 "p.tablespace_id IS NULL AND ts.engine='InnoDB' AND "
360 "GET_DD_PROPERTY_KEY_VALUE(t.options,'encrypt_type') IS NOT NULL "
361 "AND "
362 "GET_DD_PROPERTY_KEY_VALUE(ts.options,'encryption') IS NULL ")) {
363 return dd::end_transaction(thd, true);
364 }
365
366 /*
367 Add ENCRYPTION clause for InnoDB file-per-table tablespaces same as
368 encryption type of the table.
369
370 For a tables using file-per-table tablespace, the tablespace_id is
371 stored in tables corresponding mysql.indexes.tablespace_id.
372 The following query finds the tablespace by joining
373 several DD tables and updates the 'encryption' key same as that of
374 tables encryption type.
375
376 This is done as we expect all innodb tablespaces to have proper
377 'encryption' flag set.
378 */
379 static_assert(dd::tables::Indexes::NUMBER_OF_FIELDS == 17,
380 "SQL statements rely on a specific table definition");
381 static_assert(dd::tables::Tablespaces::NUMBER_OF_FIELDS == 7,
382 "SQL statements rely on a specific table definition");
383 static_assert(dd::tables::Tables::NUMBER_OF_FIELDS == 37,
384 "SQL statements rely on a specific table definition");
385 if (dd::execute_query(
386 thd,
387 "UPDATE mysql.indexes i "
388 "JOIN mysql.tablespaces ts ON ts.id = i.tablespace_id "
389 "JOIN mysql.tables t ON t.id = i.table_id "
390 "SET ts.options=CONCAT(IFNULL(ts.options,''), "
391 "IF(LOWER(GET_DD_PROPERTY_KEY_VALUE(t.options,'encrypt_type'))='y'"
392 ", 'encryption=Y;','encryption=N;')) "
393 "WHERE ts.engine='InnoDB' AND t.tablespace_id IS NULL "
394 "AND GET_DD_PROPERTY_KEY_VALUE(ts.options,'encryption') IS NULL")) {
395 return dd::end_transaction(thd, true);
396 }
397
398 /*
399 Update ENCRYPTION clause for unencrypted InnoDB tablespaces.
400 Where the 'encryption' key value is empty string ''.
401 */
402 static_assert(dd::tables::Tablespaces::NUMBER_OF_FIELDS == 7,
403 "SQL statements rely on a specific table definition");
404 if (dd::execute_query(
405 thd,
406 "UPDATE mysql.tablespaces ts "
407 "SET ts.options=CONCAT(IFNULL(REMOVE_DD_PROPERTY_KEY(ts.options, "
408 "'encryption'),''), 'encryption=N;') "
409 "WHERE ts.engine='InnoDB' AND "
410 "GET_DD_PROPERTY_KEY_VALUE(ts.options,'encryption') = ''")) {
411 return dd::end_transaction(thd, true);
412 }
413
414 /*
415 Store ENCRYPTION clause for unencrypted InnoDB general tablespaces,
416 when the 'encryption' key is not yet present.
417 */
418 static_assert(dd::tables::Tablespaces::NUMBER_OF_FIELDS == 7,
419 "SQL statements rely on a specific table definition");
420 if (dd::execute_query(
421 thd,
422 "UPDATE mysql.tablespaces ts "
423 "SET ts.options=CONCAT(IFNULL(ts.options,''), 'encryption=N;') "
424 "WHERE ts.engine='InnoDB' AND "
425 "GET_DD_PROPERTY_KEY_VALUE(ts.options,'encryption') IS NULL ")) {
426 return dd::end_transaction(thd, true);
427 }
428
429 // C) UPDATE MYSQL.TABLES.OPTIONS 'ENCRYPT_TYPE' KEY FOR INNODB TABLES.
430
431 /*
432 Update 'encrypt_type' flag for innodb tables using general tablespace.
433 It is not possible to have general tablespaces used in partitioned
434 table as of 8.0.15, so we ignore to check for partitioned tables
435 using general tablespace.
436 */
437 static_assert(dd::tables::Tables::NUMBER_OF_FIELDS == 37,
438 "SQL statements rely on a specific table definition");
439 static_assert(dd::tables::Tablespaces::NUMBER_OF_FIELDS == 7,
440 "SQL statements rely on a specific table definition");
441 if (dd::execute_query(
442 thd,
443 "UPDATE mysql.tables t "
444 "JOIN mysql.tablespaces ts ON ts.id = t.tablespace_id "
445 "SET t.options=CONCAT(IFNULL(t.options,''), "
446 "IF(LOWER(GET_DD_PROPERTY_KEY_VALUE(ts.options,'encryption'))='y'"
447 ", 'encrypt_type=Y;','encrypt_type=N;')) "
448 "WHERE t.engine='InnoDB' AND t.tablespace_id IS NOT NULL AND "
449 "GET_DD_PROPERTY_KEY_VALUE(ts.options,'encryption') IS NOT NULL "
450 "AND GET_DD_PROPERTY_KEY_VALUE(t.options,'encrypt_type') IS "
451 "NULL")) {
452 return dd::end_transaction(thd, true);
453 }
454
455 /*
456 Store 'encrypt_type=N' for unencrypted InnoDB file-per-table tables,
457 for tables which does not have a 'encrypt_type' key stored already.
458 */
459 static_assert(dd::tables::Tables::NUMBER_OF_FIELDS == 37,
460 "SQL statements rely on a specific table definition");
461 if (dd::execute_query(
462 thd,
463 "UPDATE mysql.tables t "
464 "SET t.options=CONCAT(IFNULL(t.options,''), 'encrypt_type=N;') "
465 "WHERE t.tablespace_id IS NULL AND t.engine='InnoDB' AND "
466 "GET_DD_PROPERTY_KEY_VALUE(t.options,'encrypt_type') IS NULL")) {
467 return dd::end_transaction(thd, true);
468 }
469 }
470 /* Upgrade from 8.0.20 or previous. */
471 if (bootstrap::DD_bootstrap_ctx::instance().is_dd_upgrade_from_before(
472 bootstrap::DD_VERSION_80021)) {
473 /*
474 Fix discard attribute for partitioned Tables.
475
476 Due to bug, prior to 8.0.21, discard attribute for partitioned tables
477 doesn't reflect reality - that is, it can be set to true even if no
478 partition is discarded or it can be set to false, even if some
479 partitions are discarded.
480
481 Moreover, due to the bug, the attribute is only set for a whole Table,
482 whereas only some partitions of the table may be discarded.
483
484 However, Tablespace state==discarded reflect reality properly.
485
486 Since 8.0.21, discard attribute for partitioned tables follow the rules:
487 a) dd::Table for partitioned Table cannot have discard attribute
488 b) only leaf dd::Partitions can have discard attribute
489
490 We can recreate such state looking at Tablespace state in three steps:
491 a) Remove discard attribute from dd::Tables which have any partitions
492 b) Remove discard attribute from all dd::Partitions
493 c) For each leaf_partition (taken from index_partitions) set discard
494 attribute depending on whether its Tablespace state is discarded or not
495 */
496 static_assert(dd::tables::Index_partitions::NUMBER_OF_FIELDS == 5,
497 "SQL statements rely on a specific table definition");
498 static_assert(dd::tables::Table_partitions::NUMBER_OF_FIELDS == 12,
499 "SQL statements rely on a specific table definition");
500 static_assert(dd::tables::Tables::NUMBER_OF_FIELDS == 37,
501 "SQL statements rely on a specific table definition");
502 static_assert(dd::tables::Tablespaces::NUMBER_OF_FIELDS == 7,
503 "SQL statements rely on a specific table definition");
504
505 /*
506 Remove discard attribute from all Tables
507 Use mysql.index_partitions to find all partitions
508 */
509 if (dd::execute_query(
510 thd,
511 "UPDATE mysql.tables tbl "
512 "JOIN mysql.table_partitions tp ON tp.table_id = tbl.id "
513 "JOIN mysql.index_partitions ip ON ip.partition_id = tp.id "
514 "SET "
515 "tbl.se_private_data=NULLIF(REMOVE_DD_PROPERTY_KEY(tbl.se_private_"
516 "data, 'discard'), '') "
517 "WHERE tbl.engine='InnoDB' AND "
518 "GET_DD_PROPERTY_KEY_VALUE(tbl.se_private_data, 'discard') IS NOT "
519 "NULL ")) {
520 return dd::end_transaction(thd, true);
521 }
522
523 /*
524 Remove discard attribute from all table_partitions.
525
526 Even though we didn't store discard attribute in mysql.table_partitions,
527 it's still possible there is such attribute there: in versions 8.0.0 to
528 8.0.14 it was possible to upgrade from 5.7, when MySQL had discarded
529 partitions. Such upgrade would add discard attribute to
530 mysql.table_partitions
531 */
532 if (dd::execute_query(thd,
533 "UPDATE mysql.table_partitions tp "
534 "SET "
535 "tp.se_private_data=NULLIF(REMOVE_DD_PROPERTY_KEY(tp."
536 "se_private_data, 'discard'), '') "
537 "WHERE tp.engine='InnoDB' AND "
538 "GET_DD_PROPERTY_KEY_VALUE(tp.se_private_data, "
539 "'discard') IS NOT NULL ")) {
540 return dd::end_transaction(thd, true);
541 }
542
543 /* Set discard attribute for all leaf_partitions by looking at its
544 * Tablespace state. mysql.index_partitions is used to access Tablespaces */
545 if (dd::execute_query(
546 thd,
547 "UPDATE mysql.table_partitions tp "
548 "JOIN mysql.index_partitions ip ON ip.partition_id = tp.id "
549 "JOIN mysql.tablespaces ts ON ip.tablespace_id = ts.id "
550 "SET tp.se_private_data = "
551 "CONCAT('discard=1;',IFNULL(tp.se_private_data,'')) "
552 "WHERE tp.engine='InnoDB' AND "
553 "GET_DD_PROPERTY_KEY_VALUE(ts.se_private_data, "
554 "'state')='discarded' ")) {
555 return dd::end_transaction(thd, true);
556 }
557 }
558
559 /*
560 Turn foreign key checks back on and commit explicitly.
561 */
562 if (dd::execute_query(thd, "SET FOREIGN_KEY_CHECKS= 1"))
563 return dd::end_transaction(thd, true);
564
565 return false;
566 }
567 /* purecov: end */
568
569 /**
570 Copy meta data from the actual tables to the target tables.
571
572 The default is to copy all data. This is sufficient if we e.g. add a
573 new index in the new DD version. If there are changes to the table
574 columns, e.g. if we add or remove a column, we must add code to handle
575 each case specifically. Suppose e.g. we add a new column to allow defining
576 a default tablespace for each schema, and store the tablespace id
577 in that column. Then, we could migrate the meta data for 'schemata' and
578 set a default value for all existing schemas:
579
580 ...
581 migrated_set.insert("schemata");
582 if (dd::execute_query(thd, "INSERT INTO schemata "
583 "SELECT id, catalog_id, name, default_collation_id, 1, "
584 " created, last_altered, options FROM mysql.schemata"))
585 ...
586
587 The code block above would go into the 'Version dependent migration'
588 part of the function below.
589
590 @param thd Thread context.
591 @param create_set Set of new or modified tables to be created.
592 @param remove_set Set of abandoned or modified tables to be removed.
593
594 @returns false if success. otherwise true.
595 */
596 /* purecov: begin inspected */
migrate_meta_data(THD * thd,const std::set<String_type> & create_set,const std::set<String_type> & remove_set)597 bool migrate_meta_data(THD *thd, const std::set<String_type> &create_set,
598 const std::set<String_type> &remove_set) {
599 /*
600 Turn off foreign key checks while migrating the meta data.
601 */
602 if (dd::execute_query(thd, "SET FOREIGN_KEY_CHECKS= 0"))
603 return dd::end_transaction(thd, true);
604
605 /*
606 Explicitly migrate meta data for each table which has been modified.
607 Register the table name in the migrated_set to skip it in the default
608 handling below.
609 */
610 std::set<String_type> migrated_set{};
611
612 /*
613 Version dependent migration of meta data can be added here. The migration
614 should be grouped by table with a conditional expression for each table,
615 branching out to do one single migration step for each table. Note that
616 if more than one migration step (i.e., an INSERT into the target table)
617 being executed for a table, then each step will overwrite the result of
618 the previous one.
619 */
620 auto migrate_table = [&](const String_type &name, const String_type &stmt) {
621 DBUG_ASSERT(create_set.find(name) != create_set.end());
622 /* A table must be migrated only once. */
623 DBUG_ASSERT(migrated_set.find(name) == migrated_set.end());
624 migrated_set.insert(name);
625 if (dd::execute_query(thd, stmt)) {
626 return dd::end_transaction(thd, true);
627 }
628 return false;
629 };
630
631 auto is_dd_upgrade_from_before = [](uint dd_version) {
632 return bootstrap::DD_bootstrap_ctx::instance().is_dd_upgrade_from_before(
633 dd_version);
634 };
635
636 /********************* Migration of mysql.tables *********************/
637 /* Upgrade from 80012 or earlier. */
638 static_assert(dd::tables::Tables::NUMBER_OF_FIELDS == 37,
639 "SQL statements rely on a specific table definition");
640 if (is_dd_upgrade_from_before(bootstrap::DD_VERSION_80013)) {
641 /* Column 'last_checked_for_upgrade' was added. */
642 if (migrate_table(
643 "tables",
644 "INSERT INTO tables SELECT *, 0, NULL, NULL FROM mysql.tables")) {
645 return true;
646 }
647 } else if (is_dd_upgrade_from_before(bootstrap::DD_VERSION_80021)) {
648 /*
649 Upgrade from 80020 and before.
650 Store NULL for new columns mysql.tables.engine_attribute and
651 mysql.tables.secondary_engine_attribute
652 */
653
654 if (migrate_table(
655 "tables",
656 "INSERT INTO tables SELECT *, NULL, NULL FROM mysql.tables")) {
657 return true;
658 }
659 }
660 /********************* Migration of mysql.columns *********************/
661 /* Upgrade from 80020 or earlier. */
662 static_assert(dd::tables::Columns::NUMBER_OF_FIELDS == 32,
663 "SQL statements rely on a specific table definition");
664 if (is_dd_upgrade_from_before(bootstrap::DD_VERSION_80021)) {
665 /*
666 Upgrade from 80020 and before.
667 Store NULL for new columns mysql.columns.engine_attribute and
668 mysql.columns.secondary_engine_attribute
669 */
670 if (migrate_table(
671 "columns",
672 "INSERT INTO columns SELECT *, NULL, NULL FROM mysql.columns")) {
673 return true;
674 }
675 }
676
677 /********************* Migration of mysql.events *********************/
678 /* Upgrade from 80013 or earlier. */
679 static_assert(dd::tables::Events::NUMBER_OF_FIELDS == 24,
680 "SQL statements rely on a specific table definition");
681 if (is_dd_upgrade_from_before(bootstrap::DD_VERSION_80014)) {
682 /*
683 SQL mode 'INVALID_DATES' was renamed to 'ALLOW_INVALID_DATES'.
684 Migrate the SQL mode as an integer.
685 */
686 if (migrate_table(
687 "events",
688 "INSERT INTO events SELECT id, schema_id, name, definer, "
689 " time_zone, definition, definition_utf8, execute_at, "
690 " interval_value, interval_field, sql_mode+0, starts, ends, "
691 " status, on_completion, created, last_altered, "
692 " last_executed, comment, originator, client_collation_id, "
693 " connection_collation_id, schema_collation_id, options "
694 " FROM mysql.events")) {
695 return true;
696 }
697 }
698
699 /********************* Migration of mysql.routines *********************/
700 /* Upgrade from 80013 or earlier. */
701 static_assert(dd::tables::Routines::NUMBER_OF_FIELDS == 29,
702 "SQL statements rely on a specific table definition");
703 if (is_dd_upgrade_from_before(bootstrap::DD_VERSION_80014)) {
704 /*
705 SQL mode 'INVALID_DATES' was renamed to 'ALLOW_INVALID_DATES'.
706 Migrate the SQL mode as an integer.
707 */
708 if (migrate_table(
709 "routines",
710 "INSERT INTO routines SELECT id, schema_id, name, type, "
711 " result_data_type, result_data_type_utf8, result_is_zerofill, "
712 " result_is_unsigned, result_char_length, "
713 " result_numeric_precision, result_numeric_scale, "
714 " result_datetime_precision, result_collation_id, "
715 " definition, definition_utf8, parameter_str, is_deterministic, "
716 " sql_data_access, security_type, definer, sql_mode+0, "
717 " client_collation_id, connection_collation_id, "
718 " schema_collation_id, created, last_altered, comment, options, "
719 " external_language FROM mysql.routines")) {
720 return true;
721 }
722 }
723
724 /********************* Migration of mysql.triggers *********************/
725 /* Upgrade from 80013 or earlier. */
726 static_assert(dd::tables::Triggers::NUMBER_OF_FIELDS == 17,
727 "SQL statements rely on a specific table definition");
728 if (is_dd_upgrade_from_before(bootstrap::DD_VERSION_80014)) {
729 /*
730 SQL mode 'INVALID_DATES' was renamed to 'ALLOW_INVALID_DATES'.
731 Migrate the SQL mode as an integer.
732 */
733 if (migrate_table(
734 "triggers",
735 "INSERT INTO triggers SELECT id, schema_id, name, event_type, "
736 " table_id, action_timing, action_order, action_statement, "
737 " action_statement_utf8, created, last_altered, sql_mode+0, "
738 " definer, client_collation_id, connection_collation_id, "
739 " schema_collation_id, options FROM mysql.triggers")) {
740 return true;
741 }
742 }
743
744 /********************* Migration of mysql.schemata *********************/
745 /*
746 DD version 80016 adds a new column 'default_encryption' and
747 DD version 80017 adds a new column 'se_private_data' to the schemata table.
748 Handle them both during upgrade.
749 */
750 static_assert(dd::tables::Schemata::NUMBER_OF_FIELDS == 9,
751 "SQL statements rely on a specific table definition");
752 if (is_dd_upgrade_from_before(bootstrap::DD_VERSION_80016)) {
753 /*
754 Upgrade from 80014 and before.
755 Store 'NO' for new mysql.schemata.default_encryption column and
756 store NULL for new mysql.schemata.se_private_data column
757 */
758 if (migrate_table(
759 "schemata",
760 "INSERT INTO schemata SELECT *, 'NO', NULL FROM mysql.schemata")) {
761 return true;
762 }
763 } else if (is_dd_upgrade_from_before(bootstrap::DD_VERSION_80017)) {
764 /*
765 Upgrade from 80016.
766 Store NULL for new mysql.schemata.se_private_data column
767 */
768 if (migrate_table(
769 "schemata",
770 "INSERT INTO schemata SELECT *, NULL FROM mysql.schemata")) {
771 return true;
772 }
773 }
774
775 /********************* Migration of mysql.indexes *********************/
776 /*
777 DD version 80020 adds new columns 'engine_attribute' and
778 'secondary_engine_atribute'
779 */
780 static_assert(dd::tables::Indexes::NUMBER_OF_FIELDS == 17,
781 "SQL statements rely on a specific table definition");
782 if (is_dd_upgrade_from_before(bootstrap::DD_VERSION_80021)) {
783 /*
784 Upgrade from 80020 and before.
785 Store NULL for new columns mysql.indexes.engine_attribute and
786 mysql.indexes.secondary_engine_attribute
787 */
788 if (migrate_table(
789 "indexes",
790 "INSERT INTO indexes SELECT *, NULL, NULL FROM mysql.indexes")) {
791 return true;
792 }
793 }
794
795 /********************* Migration of mysql.tablespaces *********************/
796 /*
797 DD version 80020 adds new columns 'engine_attribute' and
798 'secondary_engine_atribute'
799 */
800 static_assert(dd::tables::Tablespaces::NUMBER_OF_FIELDS == 7,
801 "SQL statements rely on a specific table definition");
802 if (is_dd_upgrade_from_before(bootstrap::DD_VERSION_80021)) {
803 /*
804 Upgrade from 80021 and before.
805 Store NULL for new columns mysql.tablespaces.engine_attribute and
806 mysql.tablespaces.secondary_engine_attribute
807 */
808 if (migrate_table("tablespaces",
809 "INSERT INTO tablespaces SELECT *, NULL FROM "
810 "mysql.tablespaces")) {
811 return true;
812 }
813 }
814
815 /*
816 Default handling: Copy all meta data for the tables that have been
817 modified (i.e., all tables which are both in the remove- and create set),
818 unless they were migrated explicitly above.
819 */
820 for (std::set<String_type>::const_iterator it = create_set.begin();
821 it != create_set.end(); ++it) {
822 if (migrated_set.find(*it) == migrated_set.end() &&
823 remove_set.find(*it) != remove_set.end()) {
824 std::stringstream ss;
825 ss << "INSERT INTO " << (*it) << " SELECT * FROM "
826 << MYSQL_SCHEMA_NAME.str << "." << (*it);
827 if (dd::execute_query(thd, ss.str().c_str()))
828 return dd::end_transaction(thd, true);
829 }
830 }
831
832 /*
833 Turn foreign key checks back on and commit explicitly.
834 */
835 if (dd::execute_query(thd, "SET FOREIGN_KEY_CHECKS= 1"))
836 return dd::end_transaction(thd, true);
837
838 return false;
839 }
840 /* purecov: end */
841
842 /*
843 Adjust the object ids to "move" tables between schemas by using DML.
844
845 At this point, we have a set of old DD tables, in the 'remove_set', that
846 should be removed. These are a subset of the actual DD tables. And we
847 have a set of new DD tables, in the 'create_set', that should replace the
848 old ones. The tables in the 'create_set' are a subset of the target DD
849 tables.
850
851 What we want to do is to move the tables in the 'remove_set' out of the
852 'mysql' schema and into a different schema with id 'actual_table_schema_id',
853 and then move the tables in the 'create_set' (which are in a schema with
854 id 'target_table_schema_id' and name 'target_table_schema_name') out of
855 that schema and into the 'mysql' schema.
856
857 We could do this by 'RENAME TABLE' statements, but that would not be atomic
858 since the statements will be auto committing. So instead, we manipulate the
859 DD tables directly, and update the schema ids related to the relevant tables.
860 This is possible since the tables are stored in a general tablespace, and
861 moving them to a different schema will not affect the DDSE.
862
863 The updates we need to do on the DD tables are the following:
864
865 - For the tables in the 'remove_set' and the 'create_set', we must change
866 the schema id of the entry in the 'tables' table according to where we
867 want to move the tables.
868 - For the tables in the 'remove_set', we delete all foreign keys where the
869 table to be removed is a child.
870 - For the tables in the 'create_set', we change the schema id and name of
871 all foreign keys, where the table to be created is a child, from the
872 'target_table_schema_name' to that of the 'mysql' schema.
873
874 See also additional comments in the code below.
875
876 @param create_set Set of tables to be created.
877 @param remove_set Set of tables to be removed.
878 @param mysql_schema_id Id of the 'mysql' schema.
879 @param target_table_schema_id Id of the schema where the tables in the
880 create_set are located.
881 @param target_table_schema_name Name of the schema where the tables in the
882 create_set are located.
883 @param actual_table_schema_id Id of the schema where the tables in the
884 remove_set will be moved.
885
886 @returns false if success, true otherwise.
887 */
888 /* purecov: begin inspected */
update_object_ids(THD * thd,const std::set<String_type> & create_set,const std::set<String_type> & remove_set,Object_id mysql_schema_id,Object_id target_table_schema_id,const String_type & target_table_schema_name,Object_id actual_table_schema_id)889 bool update_object_ids(THD *thd, const std::set<String_type> &create_set,
890 const std::set<String_type> &remove_set,
891 Object_id mysql_schema_id,
892 Object_id target_table_schema_id,
893 const String_type &target_table_schema_name,
894 Object_id actual_table_schema_id) {
895 if (dd::execute_query(thd, "SET FOREIGN_KEY_CHECKS= 0"))
896 return dd::end_transaction(thd, true);
897
898 /*
899 If mysql.tables has been modified, do the change on the copy, otherwise
900 do the change on mysql.tables
901 */
902 String_type tables_table = tables::Tables::instance().name();
903 if (create_set.find(tables_table) != create_set.end()) {
904 tables_table = target_table_schema_name + String_type(".") + tables_table;
905 } else {
906 tables_table =
907 String_type(MYSQL_SCHEMA_NAME.str) + String_type(".") + tables_table;
908 }
909
910 /*
911 For each actual table to be removed (i.e., modified or abandoned),
912 change tables.schema_id to the actual table schema id.
913 */
914 for (std::set<String_type>::const_iterator it = remove_set.begin();
915 it != remove_set.end(); ++it) {
916 std::stringstream ss;
917 ss << "UPDATE " << tables_table
918 << " SET schema_id= " << actual_table_schema_id
919 << " WHERE schema_id= " << mysql_schema_id << " AND name LIKE '" << (*it)
920 << "'";
921
922 if (dd::execute_query(thd, ss.str().c_str()))
923 return dd::end_transaction(thd, true);
924 }
925
926 /*
927 For each target table to be created (i.e., modified or added),
928 change tables.schema_id to the mysql schema id, and set the hidden
929 property according to the corresponding Object_table.
930 */
931 for (std::set<String_type>::const_iterator it = create_set.begin();
932 it != create_set.end(); ++it) {
933 // Get the corresponding Object_table instance.
934 String_type hidden{""};
935 if (System_tables::instance()
936 ->find_table(MYSQL_SCHEMA_NAME.str, (*it))
937 ->is_hidden())
938 hidden = String_type(", hidden= 'System'");
939
940 std::stringstream ss;
941 ss << "UPDATE " << tables_table << " SET schema_id= " << mysql_schema_id
942 << hidden << " WHERE schema_id= " << target_table_schema_id
943 << " AND name LIKE '" << (*it) << "'";
944
945 if (dd::execute_query(thd, ss.str().c_str()))
946 return dd::end_transaction(thd, true);
947 }
948
949 /*
950 If mysql.foreign_keys has been modified, do the change on the copy,
951 otherwise do the change on mysql.foreign_keys. And likewise, if
952 mysql.foreign_key_column_usage has been modified, do the change on
953 the copy, otherwise do the change on mysql.foreign_key_column_usage.
954 */
955 String_type foreign_keys_table = tables::Foreign_keys::instance().name();
956 ;
957 if (create_set.find(foreign_keys_table) != create_set.end()) {
958 foreign_keys_table =
959 target_table_schema_name + String_type(".") + foreign_keys_table;
960 } else {
961 foreign_keys_table = String_type(MYSQL_SCHEMA_NAME.str) + String_type(".") +
962 foreign_keys_table;
963 }
964
965 String_type foreign_key_column_usage_table =
966 tables::Foreign_key_column_usage::instance().name();
967 ;
968 if (create_set.find(foreign_key_column_usage_table) != create_set.end()) {
969 foreign_key_column_usage_table = target_table_schema_name +
970 String_type(".") +
971 foreign_key_column_usage_table;
972 } else {
973 foreign_key_column_usage_table = String_type(MYSQL_SCHEMA_NAME.str) +
974 String_type(".") +
975 foreign_key_column_usage_table;
976 }
977
978 /*
979 For each actual (i.e., modified or abandoned) table to be removed,
980 remove the entries from the foreign_keys and foreign_key_column_usage
981 table. There is no point in trying to maintain the foreign keys since
982 the tables will be removed eventually anyway.
983 */
984 for (std::set<String_type>::const_iterator it = remove_set.begin();
985 it != remove_set.end(); ++it) {
986 std::stringstream ss;
987 ss << "DELETE FROM " << foreign_key_column_usage_table
988 << " WHERE foreign_key_id IN ("
989 << " SELECT id FROM " << foreign_keys_table
990 << " WHERE table_id= (SELECT id FROM " << tables_table
991 << " WHERE name LIKE '" << (*it) << "' AND "
992 << " schema_id= " << actual_table_schema_id << "))";
993 if (dd::execute_query(thd, ss.str().c_str()))
994
995 return dd::end_transaction(thd, true);
996
997 ss.str("");
998 ss.clear();
999 ss << "DELETE FROM " << foreign_keys_table
1000 << " WHERE table_id= (SELECT id FROM " << tables_table
1001 << " WHERE name LIKE '" << (*it) << "' AND "
1002 << " schema_id= " << actual_table_schema_id << ")";
1003 if (dd::execute_query(thd, ss.str().c_str()))
1004 return dd::end_transaction(thd, true);
1005 }
1006
1007 /*
1008 For each target (i.e., modified or added) table to be moved, change
1009 foreign_keys.schema_id and foreign_keys.referenced_schema_name to the
1010 mysql schema id and name. For the created tables, the target schema id
1011 and name are reflected in the foreign_keys tables, so we don't need a
1012 subquery based on table names.
1013 */
1014 std::stringstream ss;
1015 ss << "UPDATE " << foreign_keys_table << " SET schema_id= " << mysql_schema_id
1016 << ", "
1017 << " referenced_table_schema= '" << MYSQL_SCHEMA_NAME.str << "'"
1018 << " WHERE schema_id= " << target_table_schema_id
1019 << " AND referenced_table_schema= '" << target_table_schema_name
1020 << "'";
1021
1022 if (dd::execute_query(thd, ss.str().c_str()))
1023 return dd::end_transaction(thd, true);
1024
1025 if (dd::execute_query(thd, "SET FOREIGN_KEY_CHECKS= 1"))
1026 return dd::end_transaction(thd, true);
1027
1028 // Delay commit in the case of success, since we need to do this atomically.
1029 return false;
1030 }
1031 /* purecov: end */
1032
1033 } // namespace
1034
1035 namespace upgrade {
1036 // Create the target tables for upgrade and migrate the meta data.
1037 /* purecov: begin inspected */
upgrade_tables(THD * thd)1038 bool upgrade_tables(THD *thd) {
1039 if (!bootstrap::DD_bootstrap_ctx::instance().is_dd_upgrade()) return false;
1040
1041 /*
1042 Create the temporary schemas used for target and actual tables,
1043 and get hold of their ids.
1044 */
1045 Object_id mysql_schema_id = INVALID_OBJECT_ID;
1046 Object_id target_table_schema_id = INVALID_OBJECT_ID;
1047 Object_id actual_table_schema_id = INVALID_OBJECT_ID;
1048 String_type target_table_schema_name;
1049 if (create_temporary_schemas(thd, &mysql_schema_id, &target_table_schema_id,
1050 &target_table_schema_name,
1051 &actual_table_schema_id))
1052 return true;
1053
1054 /*
1055 Establish the sets of table names to be removed and/or created.
1056 */
1057 std::set<String_type> remove_set = {};
1058 std::set<String_type> create_set = {};
1059 establish_table_name_sets(&create_set, &remove_set);
1060
1061 /*
1062 Loop over all DD tables, and create the target tables. We may do version
1063 specific handling, but the default is to create the target table if it is
1064 different from the actual table (or if there is no corresponding actual
1065 table). The table creation is done by executing DDL statements that are
1066 auto committed.
1067 */
1068 if (create_tables(thd, &create_set)) return true;
1069
1070 /*
1071 Loop over all DD tables and migrate the meta data. We may do version
1072 specific handling, but the default is to just copy all meta data from
1073 the actual to the target table, assuming the number and type of columns
1074 are the same (e.g. if an index is added). The data migration is committed.
1075
1076 We achieve data migration in two steps:
1077
1078 1) update_meta_data() is used to adjust metadata in source DD tables in
1079 mysql schema. This is done by mostly executing UPDATE queries on
1080 them, but we do not migrate data to destination DD tables.
1081
1082 2) migrate_meta_data() is used to adjust metadata in destination DD
1083 tables using UPDATE command and also migrate data to destination DD
1084 tables using INSERT command.
1085
1086 Note that the changes done during migration of meta data are committed
1087 in next step at the end of 'atomic switch' described below.
1088 */
1089 if (update_meta_data(thd) || migrate_meta_data(thd, create_set, remove_set))
1090 return true;
1091
1092 /*
1093 We are now ready to do the atomic switch of the actual and target DD
1094 tables. Thus, the next three steps must be done without intermediate
1095 commits. Note that in case of failure, rollback is done immediately.
1096 In case of success, no commit is done until at the very end of
1097 update_versions(). The switch is done as follows:
1098
1099 - First, update the DD properties. Note that we must acquire the
1100 modified DD tables from the temporary target schema. This is done
1101 before the object ids are modified, because that step also may mess
1102 up object acquisition (if we change the schema id of a newly created
1103 table to that of the 'mysql' schema, and then try acquire(), we will
1104 get the table from the core registry in the storage adapter, and that
1105 is not what we want).
1106
1107 - Then, update the object ids and schema names to simulate altering the
1108 schema of the modified tables. The changes are done on the 'tables',
1109 'foreign_keys' and 'foreign_key_column_usage' tables. If these tables
1110 are modified, the changes must be done on the corresponding new table
1111 in the target schema. If not, the change must be done on the actual
1112 table in the 'mysql' schema.
1113
1114 - Finally, update the version numbers and commit. In update_versions(),
1115 the atomic switch will either be committed.
1116 */
1117 if (update_properties(thd, &create_set, &remove_set,
1118 target_table_schema_name) ||
1119 update_object_ids(thd, create_set, remove_set, mysql_schema_id,
1120 target_table_schema_id, target_table_schema_name,
1121 actual_table_schema_id) ||
1122 update_versions(thd, false))
1123 return true;
1124
1125 LogErr(SYSTEM_LEVEL, ER_DD_UPGRADE_COMPLETED,
1126 bootstrap::DD_bootstrap_ctx::instance().get_actual_dd_version(),
1127 dd::DD_VERSION);
1128 log_sink_buffer_check_timeout();
1129 sysd::notify("STATUS=Data Dictionary upgrade complete\n");
1130
1131 /*
1132 At this point, the DD upgrade is committed. Below, we will reset the
1133 DD cache and re-initialize based on 'mysql.dd_properties', hence,
1134 we will lose track of the fact that we have done a DD upgrade as part
1135 of this restart. Thus, we record this fact in the bootstrap context
1136 so we can check it e.g. when initializeing the information schema,
1137 where we need to regenerate the meta data if the underlying tables
1138 have changed.
1139 */
1140 bootstrap::DD_bootstrap_ctx::instance().set_dd_upgrade_done();
1141
1142 /*
1143 Flush tables, reset the shared dictionary cache and the storage adapter.
1144 Start over DD bootstrap from the beginning.
1145 */
1146 if (dd::execute_query(thd, "FLUSH TABLES")) return true;
1147
1148 dd::cache::Shared_dictionary_cache::instance()->reset(false);
1149
1150 /*
1151 Reset the encryption attribute in object table def since we will now
1152 start over by creating the scaffolding, which expectes an unencrypted
1153 DD tablespace.
1154 */
1155 Object_table_definition_impl::set_dd_tablespace_encrypted(false);
1156
1157 // Reset the DDSE local dictionary cache.
1158 handlerton *ddse = ha_resolve_by_legacy_type(thd, DB_TYPE_INNODB);
1159 if (ddse->dict_cache_reset == nullptr) return true;
1160
1161 for (System_tables::Const_iterator it =
1162 System_tables::instance()->begin(System_tables::Types::CORE);
1163 it != System_tables::instance()->end();
1164 it = System_tables::instance()->next(it, System_tables::Types::CORE)) {
1165 ddse->dict_cache_reset(MYSQL_SCHEMA_NAME.str,
1166 (*it)->entity()->name().c_str());
1167 ddse->dict_cache_reset(target_table_schema_name.c_str(),
1168 (*it)->entity()->name().c_str());
1169 }
1170
1171 /*
1172 We need to start over DD initialization. This is done by executing the
1173 first stages of the procedure followed at restart. Note that this
1174 will see and use the newly upgraded DD that was created above. Cleanup
1175 of the temporary schemas is done at the end of 'sync_meta_data()'.
1176 */
1177 bootstrap::DD_bootstrap_ctx::instance().set_stage(bootstrap::Stage::STARTED);
1178
1179 store_predefined_tablespace_metadata(thd);
1180 if (create_dd_schema(thd) || initialize_dd_properties(thd) ||
1181 create_tables(thd, nullptr) || sync_meta_data(thd)) {
1182 return true;
1183 }
1184
1185 bootstrap::DD_bootstrap_ctx::instance().set_stage(
1186 bootstrap::Stage::UPGRADED_TABLES);
1187
1188 return false;
1189 }
1190
1191 } // namespace upgrade
1192 /* purecov: end */
1193 } // namespace dd
1194