1 /*****************************************************************************
2
3 Copyright (c) 2017, 2019, Oracle and/or its affiliates. All Rights Reserved.
4
5 Portions of this file contain modifications contributed and copyrighted by
6 Google, Inc. Those modifications are gratefully acknowledged and are described
7 briefly in the InnoDB documentation. The contributions by Google are
8 incorporated with their permission, and subject to the conditions contained in
9 the file COPYING.Google.
10
11 This program is free software; you can redistribute it and/or modify it under
12 the terms of the GNU General Public License, version 2.0, as published by the
13 Free Software Foundation.
14
15 This program is also distributed with certain software (including but not
16 limited to OpenSSL) that is licensed under separate terms, as designated in a
17 particular file or component or in included license documentation. The authors
18 of MySQL hereby grant you an additional permission to link the program and
19 your derivative works with the separately licensed software that they have
20 included with MySQL.
21
22 This program is distributed in the hope that it will be useful, but WITHOUT
23 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
24 FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
25 for more details.
26
27 You should have received a copy of the GNU General Public License along with
28 this program; if not, write to the Free Software Foundation, Inc.,
29 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
30
31 *****************************************************************************/
32
33 /** @file include/log0ddl.h
34 DDL log
35
36 Created 12/1/2016 Shaohua Wang
37 *******************************************************/
38
39 #ifndef log0ddl_h
40 #define log0ddl_h
41
42 /** DDL log types defined as uint32_t because it costs 4 bytes in
43 mysql.innodb_ddl_log. */
44 enum class Log_Type : uint32_t {
45 /** Smallest log type */
46 SMALLEST_LOG = 1,
47
48 /** Drop an index tree */
49 FREE_TREE_LOG = 1,
50
51 /** Delete a file */
52 DELETE_SPACE_LOG,
53
54 /** Rename a file */
55 RENAME_SPACE_LOG,
56
57 /** Drop the entry in innodb_table_metadata */
58 DROP_LOG,
59
60 /** Rename table in dict cache. */
61 RENAME_TABLE_LOG,
62
63 /** Remove a table from dict cache */
64 REMOVE_CACHE_LOG,
65
66 /** Alter Encrypt a tablespace */
67 ALTER_ENCRYPT_TABLESPACE_LOG,
68
69 /** Biggest log type */
70 BIGGEST_LOG = ALTER_ENCRYPT_TABLESPACE_LOG
71 };
72
73 /** DDL log record */
74 class DDL_Record {
75 public:
76 /** Constructor. */
77 DDL_Record();
78
79 /** Destructor. */
80 ~DDL_Record();
81
82 /** Get the id of the DDL log record.
83 @return id of the record. */
get_id()84 ulint get_id() const { return (m_id); }
85
86 /** Set the id for the DDL log record.
87 @param[in] id id of the record. */
set_id(ulint id)88 void set_id(ulint id) { m_id = id; }
89
90 /** Get the type of operation to perform
91 for the DDL log record.
92 @return type of the record. */
get_type()93 Log_Type get_type() const { return (m_type); }
94
95 /** Set the type for the DDL log record.
96 @param[in] type set the record type.*/
set_type(Log_Type type)97 void set_type(Log_Type type) { m_type = type; }
98
99 /** Get the thread id for the DDL log record.
100 @return thread id of the DDL log record. */
get_thread_id()101 ulint get_thread_id() const { return (m_thread_id); }
102
103 /** Set the thread id for the DDL log record.
104 @param[in] thread_id thread id. */
set_thread_id(ulint thread_id)105 void set_thread_id(ulint thread_id) { m_thread_id = thread_id; }
106
107 /** Get the space_id present in the DDL log record.
108 @return space_id in the DDL log record. */
get_space_id()109 space_id_t get_space_id() const { return (m_space_id); }
110
111 /** Set the space id for the DDL log record.
112 @param[in] space space id. */
set_space_id(space_id_t space)113 void set_space_id(space_id_t space) { m_space_id = space; }
114
115 /** Get the page no present in the DDL log record.
116 @return page_no */
get_page_no()117 page_no_t get_page_no() const { return (m_page_no); }
118
119 /** Set the page number for the DDL log record.
120 @param[in] page_no page number. */
set_page_no(page_no_t page_no)121 void set_page_no(page_no_t page_no) { m_page_no = page_no; }
122
123 /** Get the index id present in the DDL log record.
124 @return index id. */
get_index_id()125 ulint get_index_id() const { return (m_index_id); }
126
127 /** Set the index id for the DDL log record.
128 @param[in] index_id index id. */
set_index_id(ulint index_id)129 void set_index_id(ulint index_id) { m_index_id = index_id; }
130
131 /** Get the table id present in the DDL log record.
132 @return table id from the record. */
get_table_id()133 table_id_t get_table_id() const { return (m_table_id); }
134
135 /** Set the table if for the DDL log record.
136 @param[in] table_id table id. */
set_table_id(table_id_t table_id)137 void set_table_id(table_id_t table_id) { m_table_id = table_id; }
138
139 /** Set deletability of this record.
140 @param[in] deletable deletability. */
set_deletable(bool deletable)141 void set_deletable(bool deletable) { m_deletable = deletable; }
142
143 /** If this record can be deleted.
144 @return true if record is deletable. */
get_deletable()145 bool get_deletable() const { return (m_deletable); }
146
147 /** Get the old file path/name present in the DDL log record.
148 @return old file path/name. */
get_old_file_path()149 const char *get_old_file_path() const { return (m_old_file_path); }
150
151 /** Set the old file path from the name for the DDL log record.
152 @param[in] name old file name. */
153 void set_old_file_path(const char *name);
154
155 /** Copy the data and set it in old file path
156 @param[in] data data to be set
157 @param[in] len length of the data. */
158 void set_old_file_path(const byte *data, ulint len);
159
160 /** Get the new file path/name present in the DDL log record.
161 @return new file path/name. */
get_new_file_path()162 const char *get_new_file_path() const { return (m_new_file_path); }
163
164 /** Set the new file path/name for the DDL log record.
165 @param[in] name name to be set. */
166 void set_new_file_path(const char *name);
167
168 /** Copy the data and set it in new file path.
169 @param[in] data data to be set
170 @param[in] len length of the data. */
171 void set_new_file_path(const byte *data, ulint len);
172
173 /** Print the DDL record to specified output stream
174 @param[in,out] out output stream
175 @return output stream */
176 std::ostream &print(std::ostream &out) const;
177
178 private:
179 /** Log id */
180 ulint m_id;
181
182 /** Log type */
183 Log_Type m_type;
184
185 /** Thread id */
186 ulint m_thread_id;
187
188 /** Tablespace id */
189 space_id_t m_space_id;
190
191 /** Index root page */
192 page_no_t m_page_no;
193
194 /** Index id */
195 ulint m_index_id;
196
197 /** Table id */
198 table_id_t m_table_id;
199
200 /** Tablespace file path for DELETE, Old tablespace file path
201 for RENAME */
202 char *m_old_file_path;
203
204 /** New tablespace file name for RENAME */
205 char *m_new_file_path;
206
207 /** memory heap object used for storing file name. */
208 mem_heap_t *m_heap;
209
210 /** If this record can be deleted */
211 bool m_deletable;
212 };
213
214 /** Forward declaration */
215 class THD;
216 struct que_thr_t;
217 struct dtuple_t;
218
219 /** Array of DDL records */
220 using DDL_Records = std::vector<DDL_Record *>;
221
222 /** Wrapper of mysql.innodb_ddl_log table. Accessing to this table doesn't
223 require row lock because thread could only access/modify its own ddl records. */
224 class DDL_Log_Table {
225 public:
226 /** Constructor. */
227 DDL_Log_Table();
228
229 /** Constructor and it initalizes transaction and query thread.
230 Once trx is passed in, make sure destructor is called before the
231 trx commits.
232 @param[in,out] trx Transaction */
233 explicit DDL_Log_Table(trx_t *trx);
234
235 /** Destructor. */
236 ~DDL_Log_Table();
237
238 /** Insert the DDL log record into the innodb_ddl_log table.
239 This is thread safe.
240 @param[in] record Record to be inserted.
241 @return DB_SUCCESS or error. */
242 dberr_t insert(const DDL_Record &record);
243
244 /** Search for all records of specified thread_id. The records
245 are kept in reverse order.
246 This is thread safe. Because different threads have different thread
247 ids, there should not be any conflict with update.
248 @param[in] thread_id thread id to search
249 @param[out] records DDL_Records of the specified thread id
250 @return DB_SUCCESS or error. */
251 dberr_t search(ulint thread_id, DDL_Records &records);
252
253 /** Do a reverse scan on the table to fetch all the record.
254 This is only called during recovery
255 @param[out] records DDL_Records of the whole table
256 @return DB_SUCCESS or error. */
257 dberr_t search_all(DDL_Records &records);
258
259 /** Delete the innodb_ddl_log record of specified ID.
260 This is thread safe. One thread will only remove its ddl record.
261 @param[in] id ID of the DDL_Record
262 @return DB_SUCCESS or error. */
263 dberr_t remove(ulint id);
264
265 /** Delete specified DDL_Records from innodb_ddl_log.
266 This is thread safe. Different threads have their own ddl records
267 to delete. And this could be called during recovery.
268 @param[in] records DDL_Record(s) to be deleted
269 @return DB_SUCCESS or error. */
270 dberr_t remove(const DDL_Records &records);
271
272 private:
273 /** Set the query thread using graph. */
274 void start_query_thread();
275
276 /** Stop the query thread. */
277 void stop_query_thread();
278
279 /** Create tuple for the innodb_ddl_log table.
280 It is used for insert operation.
281 @param[in] record DDL log record. */
282 void create_tuple(const DDL_Record &record);
283
284 /** Create tuple for the given index. Used for search by id
285 (and following delete)
286 @param[in] id Thread id/ id of the record
287 @param[in] index Clustered index or secondary index. */
288 void create_tuple(ulint id, const dict_index_t *index);
289
290 /** Convert the innodb_ddl_log index record to DDL_Record.
291 @param[in] is_clustered true if this is clustered index record,
292 otherwise the secondary index record
293 @param[in] rec index record
294 @param[in] offsets index record offset
295 @param[in,out] record to store the innodb_ddl_log record. */
296 void convert_to_ddl_record(bool is_clustered, rec_t *rec,
297 const ulint *offsets, DDL_Record &record);
298
299 /** Parse the index record and get 'ID'.
300 @param[in] index index where the record resides
301 @param[in] rec index rec
302 @param[in] offsets offsets of the index.
303 @return id of the record. */
304 ulint parse_id(const dict_index_t *index, rec_t *rec, const ulint *offsets);
305
306 /** Set the given field of the innodb_ddl_log record from given data.
307 @param[in] data data to be set
308 @param[in] offset column of the ddl record
309 @param[in] len length of the data
310 @param[in,out] record DDL_Record to set */
311 void set_field(const byte *data, ulint offset, ulint len, DDL_Record &record);
312
313 /** Fetch the value from given offset.
314 @param[in] data value to be retrieved from data
315 @param[in] offset offset of the column
316 @return value of the given offset. */
317 ulint fetch_value(const byte *data, ulint offset);
318
319 /** Seach specified index by specified ID
320 @param[in] id ID to search
321 @param[in] index index to search
322 @param[in,out] records DDL_Record(s) got by the search
323 @return DB_SUCCESS or error */
324 dberr_t search_by_id(ulint id, dict_index_t *index, DDL_Records &records);
325
326 private:
327 /** Column number of mysql.innodb_ddl_log.id. */
328 static constexpr unsigned s_id_col_no = 0;
329
330 /** Column length of mysql.innodb_ddl_log.id. */
331 static constexpr unsigned s_id_col_len = 8;
332
333 /** Column number of mysql.innodb_ddl_log.thread_id. */
334 static constexpr unsigned s_thread_id_col_no = 1;
335
336 /** Column length of mysql.innodb_ddl_log.thread_id. */
337 static constexpr unsigned s_thread_id_col_len = 8;
338
339 /** Column number of mysql.innodb_ddl_log.type. */
340 static constexpr unsigned s_type_col_no = 2;
341
342 /** Column length of mysql.innodb_ddl_log.type. */
343 static constexpr unsigned s_type_col_len = 4;
344
345 /** Column number of mysql.innodb_ddl_log.space_id. */
346 static constexpr unsigned s_space_id_col_no = 3;
347
348 /** Column length of mysql.innodb_ddl_log.space_id. */
349 static constexpr unsigned s_space_id_col_len = 4;
350
351 /** Column number of mysql.innodb_ddl_log.page_no. */
352 static constexpr unsigned s_page_no_col_no = 4;
353
354 /** Column length of mysql.innodb_ddl_log.page_no. */
355 static constexpr unsigned s_page_no_col_len = 4;
356
357 /** Column number of mysql.innodb_ddl_log.index_id. */
358 static constexpr unsigned s_index_id_col_no = 5;
359
360 /** Column length of mysql.innodb_ddl_log.index_id. */
361 static constexpr unsigned s_index_id_col_len = 8;
362
363 /** Column number of mysql.innodb_ddl_log.table_id. */
364 static constexpr unsigned s_table_id_col_no = 6;
365
366 /** Column length of mysql.innodb_ddl_log.table_id. */
367 static constexpr unsigned s_table_id_col_len = 8;
368
369 /** Column number of mysql.innodb_ddl_log.old_file_path. */
370 static constexpr unsigned s_old_file_path_col_no = 7;
371
372 /** Column number of mysql.innodb_ddl_log.new_file_path. */
373 static constexpr unsigned s_new_file_path_col_no = 8;
374
375 /** innodb_ddl_log table. */
376 dict_table_t *m_table;
377
378 /** Tuple used for insert, search, delete operation. */
379 dtuple_t *m_tuple;
380
381 /** Transaction used for insert, delete operation. */
382 trx_t *m_trx;
383
384 /** Dummy query thread. */
385 que_thr_t *m_thr;
386
387 /** Heap to store the m_tuple, m_thr and all
388 operation on mysql.innodb_ddl_log table. */
389 mem_heap_t *m_heap;
390 };
391
392 /** Class to write and replay ddl logs */
393 class Log_DDL {
394 public:
395 /** Constructor */
396 Log_DDL();
397
398 /** Deconstructor */
~Log_DDL()399 ~Log_DDL() {}
400
401 /** Write DDL log for freeing B-tree
402 @param[in,out] trx transaction
403 @param[in] index dict index
404 @param[in] is_drop_table true if this is drop table
405 @return DB_SUCCESS or error */
406 dberr_t write_free_tree_log(trx_t *trx, const dict_index_t *index,
407 bool is_drop_table);
408
409 /** Write DDL log for deleting tablespace file
410 @param[in,out] trx transaction
411 @param[in] table dict table
412 @param[in] space_id tablespace id
413 @param[in] file_path file path
414 @param[in] is_drop flag whether dropping tablespace
415 @param[in] dict_locked true if dict_sys mutex is held
416 @return DB_SUCCESS or error */
417 dberr_t write_delete_space_log(trx_t *trx, const dict_table_t *table,
418 space_id_t space_id, const char *file_path,
419 bool is_drop, bool dict_locked);
420
421 /** Write a RENAME log record
422 @param[in] space_id tablespace id
423 @param[in] old_file_path file path after rename
424 @param[in] new_file_path file path before rename
425 @return DB_SUCCESS or error */
426 dberr_t write_rename_space_log(space_id_t space_id, const char *old_file_path,
427 const char *new_file_path);
428
429 /** Write an ALTER ENCRYPT Tablespace DDL log record
430 @param[in] space_id tablespace id
431 @return DB_SUCCESS or error */
432 dberr_t write_alter_encrypt_space_log(space_id_t space_id);
433
434 /** Write a DROP log to indicate the entry in innodb_table_metadata
435 should be removed for specified table
436 @param[in,out] trx transaction
437 @param[in] table_id table ID
438 @return DB_SUCCESS or error */
439 dberr_t write_drop_log(trx_t *trx, const table_id_t table_id);
440
441 /** Write a RENAME table log record
442 @param[in] table dict table
443 @param[in] old_name table name after rename
444 @param[in] new_name table name before rename
445 @return DB_SUCCESS or error */
446 dberr_t write_rename_table_log(dict_table_t *table, const char *old_name,
447 const char *new_name);
448
449 /** Write a REMOVE cache log record
450 @param[in,out] trx transaction
451 @param[in] table dict table
452 @return DB_SUCCESS or error */
453 dberr_t write_remove_cache_log(trx_t *trx, dict_table_t *table);
454
455 /** Replay DDL log record
456 @param[in,out] record DDL log record
457 return DB_SUCCESS or error */
458 dberr_t replay(DDL_Record &record);
459
460 /** Replay and clean DDL logs after DDL transaction
461 commints or rollbacks.
462 @param[in] thd mysql thread
463 @return DB_SUCCESS or error */
464 dberr_t post_ddl(THD *thd);
465
466 /** Recover in server startup.
467 Scan innodb_ddl_log table, and replay all log entries.
468 Note: redo log should be applied, and DD transactions
469 should be recovered before calling this function.
470 @return DB_SUCCESS or error */
471 dberr_t recover();
472
473 /** Post tablespace (un)encryption recovery. Delete ddl logs
474 entry for the tablespaces for which (un)encyrption operation
475 was resumed.
476 NOTE: This is called by background thread doing resume (un)encryption.
477 param[in] records list of records to be deleted
478 @return InnoDB error code */
479 dberr_t post_ts_encryption(DDL_Records &records);
480
481 /** Is it in ddl recovery in server startup.
482 @return true if it's in ddl recover */
is_in_recovery()483 static bool is_in_recovery() { return (s_in_recovery); }
484
485 private:
486 /** Insert a FREE log record
487 @param[in,out] trx transaction
488 @param[in] index dict index
489 @param[in] id log id
490 @param[in] thread_id thread id
491 @return DB_SUCCESS or error */
492 dberr_t insert_free_tree_log(trx_t *trx, const dict_index_t *index,
493 uint64_t id, ulint thread_id);
494
495 /** Replay FREE log(free B-tree if exist)
496 @param[in] space_id tablespace id
497 @param[in] page_no root page no
498 @param[in] index_id index id */
499 void replay_free_tree_log(space_id_t space_id, page_no_t page_no,
500 ulint index_id);
501
502 /** Insert a DELETE log record
503 @param[in,out] trx transaction
504 @param[in] id log id
505 @param[in] thread_id thread id
506 @param[in] space_id tablespace id
507 @param[in] file_path file path
508 @param[in] dict_locked true if dict_sys mutex is held
509 @return DB_SUCCESS or error */
510 dberr_t insert_delete_space_log(trx_t *trx, uint64_t id, ulint thread_id,
511 space_id_t space_id, const char *file_path,
512 bool dict_locked);
513
514 /** Replay DELETE log(delete file if exist)
515 @param[in] space_id tablespace id
516 @param[in] file_path file path */
517 void replay_delete_space_log(space_id_t space_id, const char *file_path);
518
519 /** Insert a RENAME log record
520 @param[in] id log id
521 @param[in] thread_id thread id
522 @param[in] space_id tablespace id
523 @param[in] old_file_path file path after rename
524 @param[in] new_file_path file path before rename
525 @return DB_SUCCESS or error */
526 dberr_t insert_rename_space_log(uint64_t id, ulint thread_id,
527 space_id_t space_id,
528 const char *old_file_path,
529 const char *new_file_path);
530
531 /** Replay RENAME log
532 @param[in] space_id tablespace id
533 @param[in] old_file_path old file path
534 @param[in] new_file_path new file path */
535 void replay_rename_space_log(space_id_t space_id, const char *old_file_path,
536 const char *new_file_path);
537
538 /** Insert an ALTER ENCRYPT TABLESPACE log record
539 @param[in] id log id
540 @param[in] thread_id thread id
541 @param[in] space_id tablespace id
542 @return DB_SUCCESS or error */
543 dberr_t insert_alter_encrypt_space_log(uint64_t id, ulint thread_id,
544 space_id_t space_id);
545
546 /** Replay an ALTER ENCRYPT TABLESPACE log record
547 @param[in] space_id tablespace id */
548 void replay_alter_encrypt_space_log(space_id_t space_id);
549
550 /** Insert a DROP log record
551 @param[in,out] trx transaction
552 @param[in] id log id
553 @param[in] thread_id thread id
554 @param[in] table_id table id
555 @return DB_SUCCESS or error */
556 dberr_t insert_drop_log(trx_t *trx, uint64_t id, ulint thread_id,
557 const table_id_t table_id);
558
559 /** Replay DROP log
560 @param[in] table_id table id */
561 void replay_drop_log(const table_id_t table_id);
562
563 /** Insert a RENAME TABLE log record
564 @param[in] id log id
565 @param[in] thread_id thread id
566 @param[in] table_id table id
567 @param[in] old_name table name after rename
568 @param[in] new_name table name before rename
569 @return DB_SUCCESS or error */
570 dberr_t insert_rename_table_log(uint64_t id, ulint thread_id,
571 table_id_t table_id, const char *old_name,
572 const char *new_name);
573
574 /** Relay RENAME TABLE log
575 @param[in] table_id table id
576 @param[in] old_name old name
577 @param[in] new_name new name */
578 void replay_rename_table_log(table_id_t table_id, const char *old_name,
579 const char *new_name);
580
581 /** Insert a REMOVE cache log record
582 @param[in] id log id
583 @param[in] thread_id thread id
584 @param[in] table_id table id
585 @param[in] table_name table name
586 @return DB_SUCCESS or error */
587 dberr_t insert_remove_cache_log(uint64_t id, ulint thread_id,
588 table_id_t table_id, const char *table_name);
589
590 /** Relay remove cache log
591 @param[in] table_id table id
592 @param[in] table_name table name */
593 void replay_remove_cache_log(table_id_t table_id, const char *table_name);
594
595 /** Delete log record by id
596 @param[in] trx transaction instance
597 @param[in] id log id
598 @param[in] dict_locked true if dict_sys mutex is held,
599 otherwise false
600 @return DB_SUCCESS or error */
601 dberr_t delete_by_id(trx_t *trx, uint64_t id, bool dict_locked);
602
603 /** Scan, replay and delete log records by thread id
604 @param[in] thread_id thread id
605 @return DB_SUCCESS or error */
606 dberr_t replay_by_thread_id(ulint thread_id);
607
608 /** Delete the log records present in the list.
609 @param[in] records DDL_Records where the IDs are got
610 @return DB_SUCCESS or error. */
611 dberr_t delete_by_ids(DDL_Records &records);
612
613 /** Scan, replay and delete all log records
614 @return DB_SUCCESS or error */
615 dberr_t replay_all();
616
617 /** Get next autoinc counter by increasing 1 for innodb_ddl_log
618 @return new next counter */
619 inline uint64_t next_id();
620
621 /** Check if we need to skip ddl log for a table.
622 @param[in] table dict table
623 @param[in] thd mysql thread
624 @return true if should skip, otherwise false */
625 inline bool skip(const dict_table_t *table, THD *thd);
626
627 private:
628 /** Whether in recover(replay) ddl log in startup. */
629 static bool s_in_recovery;
630 };
631
632 /** Object to handle Log_DDL */
633 extern Log_DDL *log_ddl;
634
635 /** Close the DDL log system */
ddl_log_close()636 inline void ddl_log_close() { UT_DELETE(log_ddl); }
637
638 #ifdef UNIV_DEBUG
639 struct SYS_VAR;
640
641 /** Used by SET GLOBAL innodb_ddl_log_crash_counter_reset_debug = 1; */
642 extern bool innodb_ddl_log_crash_reset_debug;
643
644 /** Reset all crash injection counters. It's used by:
645 SET GLOBAL innodb_ddl_log_crash_reset_debug = 1 (0).
646 @param[in] thd thread handle
647 @param[in] var pointer to system variable
648 @param[in] var_ptr where the formal string goes
649 @param[in] save immediate result from check function */
650 void ddl_log_crash_reset(THD *thd, SYS_VAR *var, void *var_ptr,
651 const void *save);
652 #endif /* UNIV_DEBUG */
653
654 #endif /* log0ddl_h */
655