Merge lp:~sergei.glushchenko/percona-xtrabackup/xb-tools into lp:percona-xtrabackup/2.1

Proposed by Sergei Glushchenko
Status: Superseded
Proposed branch: lp:~sergei.glushchenko/percona-xtrabackup/xb-tools
Merge into: lp:percona-xtrabackup/2.1
Diff against target: 7171 lines (+6774/-24)
19 files modified
patches/innodb51.patch (+342/-7)
src/Makefile (+14/-5)
src/api_log.c (+1493/-0)
src/api_log.h (+213/-0)
src/api_page.c (+3522/-0)
src/api_page.h (+70/-0)
src/fil_cur.c (+3/-1)
src/fil_cur.h (+1/-0)
src/innodb_int.c (+75/-0)
src/innodb_int.h (+298/-8)
src/percona-pprint.c (+146/-0)
src/percona-redo.c (+216/-0)
src/xtrabackup.c (+18/-0)
test/inc/allpagetypes.sql (+147/-0)
test/inc/allrectypes.sql (+154/-0)
test/run.sh (+9/-1)
test/t/percona-pprint.sh (+27/-0)
test/t/percona-redo.sh (+20/-0)
utils/build.sh (+6/-2)
To merge this branch: bzr merge lp:~sergei.glushchenko/percona-xtrabackup/xb-tools
Reviewer Review Type Date Requested Status
Laurynas Biveinis (community) Needs Fixing
Alexey Kopytov (community) Needs Fixing
Review via email: mp+111345@code.launchpad.net

This proposal has been superseded by a proposal from 2012-12-05.

Description of the change

Log and printers are build inside XtraBackup tree. Following are how it been done:
innodb51-utils.patch - patch needed for utils to work inside XtraBackup
innodb51-utils-init.patch - patch needed for utils to work standalone (in addition to innodb-utils.patch)
api0internal.h - some innodb internals exposed
api0log.c - log printer code
api0page.c - page printer code
innodb_init.c - innodb initialization code needed for standalone utils
percona-pprint.cc - standalone page printer main function
percona-redo.cc - standalone log printer main function

to build standalone tools utils/build.sh utils
utils are also linked to xtrabackup_plugin

To post a comment you must log in.
Revision history for this message
Alexey Kopytov (akopytov) wrote :

Sergei,

I didn't review the actual code, just want to comment on a number of basic things that caught my eye:

   - large blocks of empty lines, i.e. after ib_page_page_print_list()
     declaration (and what does that "SOME IBUF STUFF" comment mean?),
     and after ib_page_ibuf_rec_get_size()
   - lots of lines breaking the 80 chars limit
   - I see absolutely no reasons to use C++ and Boost in percona-redo.cc
     and percona-pprint.cc. Option parsing is available in my_getopt.c,
     which is already used in xtrabackup.c and xbstream.c. And
     pprint/reado are very small utilities.
   - large block of "#if 0"ed code in percona-redo.cc. If it's a
     debug-only code, create appropriately named #defines and some way
     to compile a debug binary. If that code is not supposed to be used
     by anyone, remove it.
   - some commented out code in percona-pprint.c. Same comments as in
     the previous item.
   - what are those "BEGIN LICENSE" / "END LICENSE" for?
   - can innodb_init_param() be moved to innodb_int.[ch] so we don't
     create separate files for it, which are also confusingly similar to
     innodb_int*?

review: Needs Fixing
Revision history for this message
Alexey Kopytov (akopytov) wrote :

In addition to the above, I think api0internal.h has to be merged with
innodb_int.h, since the latter has the same purpose.

And api0log.c and api0print.c should have better name, I don't see a
point in following the InnoDB file naming conventions. Especially
because no one except Heikki knows the reason to name files like
xxx0yyy.c :)

review: Needs Fixing
Revision history for this message
Laurynas Biveinis (laurynas-biveinis) wrote :

Alexey -

I guess we want to minimize differences between the HailDB and XB versions of the utilities, and IMHO api0internal.h makes more sense in the former where there is no innodb_int.h. I agree we could name files better for XB, but for the HailDB modifications it makes sense to stick with foo0bar naming convention. But this is not a very strong opinion.

Revision history for this message
Alexey Kopytov (akopytov) wrote :

Laurynas,

On 07/05/2012 09:26 AM, Laurynas Biveinis wrote:
> I guess we want to minimize differences between the HailDB and XB versions of the utilities, and IMHO api0internal.h makes more sense in the former where there is no innodb_int.h. I agree we could name files better for XB, but for the HailDB modifications it makes sense to stick with foo0bar naming convention. But this is not a very strong opinion.
>

I remember we were discussing this, but I'm not sure about our current
plans about the HailDB version of the utilities. Do we have anything
specific set?

Revision history for this message
Sergei Glushchenko (sergei.glushchenko) wrote :

Following been done:

Formatted to meet 80 chars limitation.
C++ parts rewritten in C.
Some code cleanup, removed commented and #if 0'ed parts.
innodb_init_param moved to innodb_int.c
api0* files were renamed to api_*
api0internal.h was merged with innodb_int.h

I consider that it is ready for the next iteration.

Revision history for this message
Laurynas Biveinis (laurynas-biveinis) wrote :

This is a partial review only, I will continue later.

    Please create and link a blueprint for this work.

    Is it possible to merge the tools InnoDB patch with some existing
    patch? I guess for that some more effort would be required: to
    uncomment but conditionally skip the commented
    innobase_rec_to_mysql(), trx_i_s_cache_init() etc. calls. I don't
    know if this additional effort is justifiable or not.

    Instead of open_or_create_log_file_ex() perhaps better to have
    just open_log_file() that always sets the proposed log file from
    the actual file size? In general I don't like the _ex naming
    convention, because as time passes it is never apparent what is
    meant by it.

    What is the purpose of rec_get_offsets_old()? Its implementation
    does not have a header comment, and the one next to its
    declaration does not tell why "new" function is not good enough.

    Why there are separate rules for the utility functions in XB? I
    think we should just link them in the relevant xb configurations
    by default. If they are needed then the Makefile diff at lines
    591--599 contains $(CFLAGS), $(CXXFLAGS), $(CCFLAGS). I think
    only the first is correct, and then the rules can be merged.

    Makefile "utils" target should be marked as phony and IMHO it
    should be included in the default build.

    Can the INNODBOBJS definition for utils be reused from some other
    target instead of copy-paste?

    Line 677: please define MLOG_LSN conditionally with #ifndef
    MLOG_LSN

    The ib_log_rec_*, ib_log_sys_* structs and functions need
    comments. This was my work, please ping me on IRC and we will
    coordinate.

    The #if 0 blocks in fill_parse_buffer should go away. Likewise in
    ib_log_sys_g et_next_rec().

    The untested log record types still need testing. That's a non
    trivial task, need coordination with QA.

review: Needs Fixing
Revision history for this message
Stewart Smith (stewart) wrote :

Alexey Kopytov <email address hidden> writes:
> I remember we were discussing this, but I'm not sure about our current
> plans about the HailDB version of the utilities. Do we have anything
> specific set?

No, nothing solid. Although api0internal.h is more innodb-like naming.

--
Stewart Smith

Revision history for this message
Laurynas Biveinis (laurynas-biveinis) wrote :

Couple more comments:

The page printer utility starts & commits MTRs, for example i ib_page_load_table_for_id(). How does that work since we are not allowed to write to the log in the utility?

How do you plan automating testing this, both as standalone utility and GDB entry points in the XB binary?

review: Needs Fixing
Revision history for this message
Sergei Glushchenko (sergei.glushchenko) wrote :

Laurynas,

>
> Please create and link a blueprint for this work.
>

https://blueprints.launchpad.net/percona-xtrabackup/+spec/log-and-page-printers-for-xtrabackup
Please look at the BP above and tells whether it good or must be rewritten.

> Is it possible to merge the tools InnoDB patch with some existing
> patch? I guess for that some more effort would be required: to
> uncomment but conditionally skip the commented
> innobase_rec_to_mysql(), trx_i_s_cache_init() etc. calls. I don't
> know if this additional effort is justifiable or not.
>

Yep, there is nothing hard in merging utils patch with innodb51.patch.
innobase_rec_to_mysql(), trx_i_s_cache_init() etc. calls should be commented
for both xtrabackup and utils so there is no problem.
Patches been merged.

> Instead of open_or_create_log_file_ex() perhaps better to have
> just open_log_file() that always sets the proposed log file from
> the actual file size? In general I don't like the _ex naming
> convention, because as time passes it is never apparent what is
> meant by it.
>
Yep. I tend to agree with you.

> What is the purpose of rec_get_offsets_old()? Its implementation
> does not have a header comment, and the one next to its
> declaration does not tell why "new" function is not good enough.
>

In case when we deal with redundant page format we don't need to have
dict_index_t* for this page. However rec_get_offsets_new always require
this, so I made separate function rec_get_offsets_old. Another way is
to replace
ut_ad(index);
with something like
ut_ad(!page_is_comp(page_align(rec))||index);

> Why there are separate rules for the utility functions in XB? I
> think we should just link them in the relevant xb configurations
> by default. If they are needed then the Makefile diff at lines
> 591--599 contains $(CFLAGS), $(CXXFLAGS), $(CCFLAGS). I think
> only the first is correct, and then the rules can be merged.
>
> Makefile "utils" target should be marked as phony and IMHO it
> should be included in the default build.
>
> Can the INNODBOBJS definition for utils be reused from some other
> target instead of copy-paste?
>

I've removed target 'utils'. Now utils are build with 'innodb51' target.

> Line 677: please define MLOG_LSN conditionally with #ifndef
> MLOG_LSN
>

done

> The #if 0 blocks in fill_parse_buffer should go away. Likewise in
> ib_log_sys_g et_next_rec().
>

done

> The ib_log_rec_*, ib_log_sys_* structs and functions need
> comments. This was my work, please ping me on IRC and we will
> coordinate.
>

I'll try to write comments by myself first (it's useful as I will get more familiar with code) and
will send you email my version. Then you will correct them and reply back to me. Will this work?

Revision history for this message
Sergei Glushchenko (sergei.glushchenko) wrote :

> Couple more comments:
>
> The page printer utility starts & commits MTRs, for example i
> ib_page_load_table_for_id(). How does that work since we are not allowed to
> write to the log in the utility?
>

As long as page printer doesn't make changes to buffer pool pages, mtr_commit doesn't write to log.

> How do you plan automating testing this, both as standalone utility and GDB
> entry points in the XB binary?

We should think about it:)

Revision history for this message
Laurynas Biveinis (laurynas-biveinis) wrote :

> https://blueprints.launchpad.net/percona-xtrabackup/+spec/log-and-page-
> printers-for-xtrabackup
> Please look at the BP above and tells whether it good or must be rewritten.

Thanks, I commented on its whiteboard.

> > The ib_log_rec_*, ib_log_sys_* structs and functions need
> > comments. This was my work, please ping me on IRC and we will
> > coordinate.
> >
>
> I'll try to write comments by myself first (it's useful as I will get more
> familiar with code) and
> will send you email my version. Then you will correct them and reply back to
> me. Will this work?

Yes.

Revision history for this message
Laurynas Biveinis (laurynas-biveinis) wrote :

> > Couple more comments:
> >
> > The page printer utility starts & commits MTRs, for example i
> > ib_page_load_table_for_id(). How does that work since we are not allowed to
> > write to the log in the utility?
> >
>
> As long as page printer doesn't make changes to buffer pool pages, mtr_commit
> doesn't write to log.

No way to avoid creating those MTRs and passing NULL MTRs around? Or asserting that MTR did not write anything?

> > How do you plan automating testing this, both as standalone utility and GDB
> > entry points in the XB binary?
>
> We should think about it:)

I see two ways, with distinct advantages and disadvantages:
1) Provide entry to those functions through some new XB command-line options. Then test those options.
2) Script gdb to call these functions.

The latter is closer to the actual use case but the former might be enough perhaps?

Revision history for this message
Vlad Lesin (vlad-lesin) wrote :

1) Building:
AUTO_DOWNLOAD=yes utils/build.sh innodb51
...
Building XtraBackup
Makefile:190: target `innodb_int.o' given more than once in the same rule.
...
api_page.c: In function ‘ib_page_rec_print_old’:
api_page.c:2715:24: warning: variable ‘ifield’ set but not used [-Wunused-but-set-variable]
api_page.c: In function ‘ib_page_page_print_list’:
api_page.c:2880:9: warning: variable ‘n_recs’ set but not used [-Wunused-but-set-variable]
api_log.c: In function ‘ib_log_sys_parse_rec_body’:
api_log.c:816:8: warning: variable ‘ret’ set but not used [-Wunused-but-set-variable]
innodb_int.c: In function ‘innodb_init_param_for_util’:
innodb_int.c:65:16: warning: assignment discards ‘const’ qualifier from pointer target type [enabled by default]
innodb_int.c:82:2: warning: passing argument 1 of ‘srv_parse_log_group_home_dirs’ discards ‘const’ qualifier from pointer target type [enabled by default]
/home/vlesin/src/work/xb-tools-sergey_gl/mysql-5.1/storage/innodb_plugin/include/srv0start.h:54:1: note: expected ‘char *’ but argument is of type ‘const char *’
api_log.c: In function ‘ib_log_sys_print’:
api_log.c:1449:1: warning: control reaches end of non-void function [-Wreturn-type]
...
cc: error: percona-pprint.o: No such file or directory

2) Code style:
a) 16, 35, 93 - innodb code style requires comment which underlines function name the same length as underlined function name;
b) 100, 251 - unaligned comment;
c) 150, 151, 243- function description is absent;
d) 14-15, 33-34, 60-61, 67-68, 2427, 2444, 2468 and other functions in api_page.c containing page pointer as an argument, - function argument description is absent;
e) I don't think _old and _new function names postfixes is a good idea. What if there is one more new format? As I see there are two variants of code for calculating record offsets. One is for "compact" and another is for "external" formats. We could just use _cmp_fmt and _ext_fmt postfixes for that functions instead of _old and _new.
f) I think api_page.c should correspond to innodb code style as api_log.c does to have some uniformity in api_* files. If so there should be explaining comments for function declarations and definitions. And the same remark as 2a.

3) Possible errors:
a) 330-338 - if "create_new_db" is false then "ret" is not initialized but it is compared with "FALSE" in 338. This comparison is unneeded because "ret" is initialized only if "create_new_db" is true. Otherwise the first argument of "or" operator is true and there is not need to check the second one.
339-340 - the "if" block will never work because if we are at this line the "create_new_db" is false. I think this block should be under "if (create_new_db)" condition.
It would be great if you comment your changes in open_or_create_log_file() function.
b) 2567-2569 it would be good if we checked "create_new_db" for false.

Revision history for this message
Sergei Glushchenko (sergei.glushchenko) wrote :

Vlad,

> a) 330-338 - if "create_new_db" is false then "ret" is not initialized but it is compared with "FALSE" in 338. This comparison is unneeded because "ret" is initialized only if "create_new_db" is true. Otherwise the first argument of "or" operator is true and there is not need to check the second one.

Just a little comment. I put the code below to not move eyes up and down while writing this.
 if (create_new_db) {

  files[i]
   = os_file_create(name, OS_FILE_CREATE, OS_FILE_NORMAL,
      OS_LOG_FILE, &ret);
 }
 if (!create_new_db || ret == FALSE) {
  if (create_new_db
      && os_file_get_last_error(FALSE) != OS_FILE_ALREADY_EXISTS
...
      ) {
...
   return(DB_ERROR);
  }

  files[i] = os_file_create(name, OS_FILE_OPEN, OS_FILE_AIO,
       OS_LOG_FILE, &ret);

I can't see any mistake in this lines.
Indeed, consider create_new_db is TRUE and we are trying to create new file. We have two options here. Creation succeeded and ret is set to TRUE, or creation failed and ret is set to FALSE.
Next we check whether we created file or not by using this condition (!create_new_db || ret == FALSE). Obviously if create_new_db is TRUE, the result depends only on the ret value. And we should report error (last error was not OS_FILE_ALREADY_EXISTS) or try to open existing file.

Now lets consider create_new_db is FALSE. In this case we go straight to opening file with OS_FILE_OPEN. Because all conditions in this case not depend on ret value, which is not set as you noticed before.

Revision history for this message
Sergei Glushchenko (sergei.glushchenko) wrote :

Vlad,
> e) I don't think _old and _new function names postfixes is a good idea. What if there is one more new format? As I see there are two variants of code for calculating record offsets. One is for "compact" and another is for "external" formats. We could just use _cmp_fmt and _ext_fmt postfixes for that functions instead of _old and _new.

Yes. There are two major physical row formats in InnoDB http://dev.mysql.com/doc/refman/5.0/en/innodb-physical-record.html. Redundant page keeps much more information inside the record itself, which allow us to print fields even if we have no information about the index which occupied this page. While compact page needs less space to store records. I don't like _old and _new suffixes as much as you do. But it is common practice to use these suffixes. Just look at rem0rec.h and you will see it. So I think we should use this convention too.

Revision history for this message
Sergei Glushchenko (sergei.glushchenko) wrote :

I did some testing of entry points in xtrabackup and here are my findings:

In 'backup' mode:
Page printer fails. In order for it to work following initialization should be done first:
 buf_pool_init();
 dict_ind_init();
 dict_boot(FALSE);
 dict_check_tablespaces_and_store_max_id(FALSE);
and IBUF should initialized in order to print IBUF pages
Log printer also fails.
 recv_sys_create()
is needed.

In 'prepare' mode:
For page printer to work:
 dict_ind_init();
 dict_boot(FALSE);
 dict_check_tablespaces_and_store_max_id(FALSE);
Log printer work just fine

In 'stats' mode:
Both page and log printers work OK.

I don't think we should initialize all these systems every time when running xtrabackup. However it would be nice to make log and page printers more useable inside xtrabackup. Maybe we should make some initialization function for these tools which used could call from gdb before using log or page printer.

I also found that by making create_new_db flag meaningful breaks second '--prepare' run (in open_or_create_log_file). XtraBackup doesn't create new log files because of when datafiles been found, create_new_db=FALSE is passed. So I reverted a change with create_new_db.

Revision history for this message
Laurynas Biveinis (laurynas-biveinis) wrote :

I think all required subsystems must be initialized on startup then, unless something precludes this.

Revision history for this message
Sergei Glushchenko (sergei.glushchenko) wrote :

Couple more bugs been found in initialization of log printer.
1. When compiled with debug, several failures occurred on ut_a(&(log_sys->mutex))
2. mem_init() call needed for debug builds
3. ib_log_sys_start opens only one log file from group.

Revision history for this message
Sergei Glushchenko (sergei.glushchenko) wrote :

Also it would be nice to have commandline argument --log-file-size

Revision history for this message
Sergei Glushchenko (sergei.glushchenko) wrote :

Comments for ib_page_* and ib_log_* fucntions, more InnoDB code conventions fixes.
Two testcases added for standalone tools.
Fixed number of initialization and locking errors.

Revision history for this message
Sergei Glushchenko (sergei.glushchenko) wrote :

Hey, guys!
Many of your comments were taken into account, many other things were spotted and fixed. Could you please review this once more.

Unmerged revisions

417. By Sergei Glushchenko

made extra initialization for innodb51 only

416. By Sergei Glushchenko

Additional initialization in xtrabackup in order to be able to run printers entry points.

415. By Sergei Glushchenko

Buffer overrun. Fix bug in release build

414. By Sergei Glushchenko

Fix percona*.sh tests

413. By Sergei Glushchenko

Fix build with builtin innodb. Missing percona-*.o deps in Makefile. Added missing .sql for tests

412. By Sergei Glushchenko

2 basic tests

411. By Sergei Glushchenko

Merge trunk

410. By Sergei Glushchenko

Debug build tested. Some initialization errors fixed. Some locking errors fixed.

409. By Sergei Glushchenko

Fixes after entry points testing. Do not use create_new_db flag in open_or_create_log_file_ex

408. By Sergei Glushchenko

InnoDB coding conventions, comments for functions and arguments.
Fixed some warnings.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'patches/innodb51.patch'
2--- patches/innodb51.patch 2012-02-22 16:47:17 +0000
3+++ patches/innodb51.patch 2012-10-15 14:16:23 +0000
4@@ -40,7 +40,26 @@
5 #endif
6 buf_pool->stat.n_page_gets++;
7 loop:
8-@@ -2894,7 +2896,7 @@
9+@@ -2075,7 +2077,8 @@
10+ access_time = buf_page_is_accessed(&block->page);
11+ buf_page_set_accessed_make_young(&block->page, access_time);
12+
13+- ut_ad(!ibuf_inside()
14++ ut_ad(srv_fake_write
15++ || !ibuf_inside()
16+ || ibuf_page(buf_block_get_space(block),
17+ buf_block_get_zip_size(block),
18+ buf_block_get_page_no(block), NULL));
19+@@ -2203,7 +2206,7 @@
20+ buf_pool_mutex_exit();
21+ }
22+
23+- ut_ad(!ibuf_inside() || (mode == BUF_KEEP_OLD));
24++ ut_ad(srv_fake_write || !ibuf_inside() || (mode == BUF_KEEP_OLD));
25+
26+ if (rw_latch == RW_S_LATCH) {
27+ success = rw_lock_s_lock_nowait(&(block->lock),
28+@@ -2894,7 +2897,7 @@
29 recv_recover_page(TRUE, (buf_block_t*) bpage);
30 }
31
32@@ -148,6 +167,49 @@
33 return;
34 }
35
36+--- a/storage/innodb_plugin/dict/dict0boot.c
37++++ b/storage/innodb_plugin/dict/dict0boot.c
38+@@ -241,8 +241,8 @@
39+ started. This function is also called when the data dictionary is created. */
40+ UNIV_INTERN
41+ void
42+-dict_boot(void)
43+-/*===========*/
44++dict_boot(ibool ibuf_init)
45++/*======================*/
46+ {
47+ dict_table_t* table;
48+ dict_index_t* index;
49+@@ -442,7 +442,8 @@
50+
51+ /* Initialize the insert buffer table and index for each tablespace */
52+
53+- ibuf_init_at_db_start();
54++ if (ibuf_init)
55++ ibuf_init_at_db_start();
56+
57+ /* Load definitions of other indexes on system tables */
58+
59+@@ -469,8 +470,8 @@
60+ Creates and initializes the data dictionary at the database creation. */
61+ UNIV_INTERN
62+ void
63+-dict_create(void)
64+-/*=============*/
65++dict_create(ibool ibuf_init)
66++/*========================*/
67+ {
68+ mtr_t mtr;
69+
70+@@ -480,7 +481,7 @@
71+
72+ mtr_commit(&mtr);
73+
74+- dict_boot();
75++ dict_boot(ibuf_init);
76+
77+ dict_insert_initial_data();
78+ }
79 --- a/storage/innodb_plugin/fil/fil0fil.c
80 +++ b/storage/innodb_plugin/fil/fil0fil.c
81 @@ -48,6 +48,8 @@
82@@ -632,6 +694,27 @@
83 while (sum_pages < n_pages) {
84 n_bytes = ibuf_contract_ext(&n_pag2, sync);
85
86+--- a/storage/innodb_plugin/include/dict0boot.h
87++++ b/storage/innodb_plugin/include/dict0boot.h
88+@@ -82,14 +82,14 @@
89+ started. This function is also called when the data dictionary is created. */
90+ UNIV_INTERN
91+ void
92+-dict_boot(void);
93+-/*===========*/
94++dict_boot(ibool ibuf_init);
95++/*=======================*/
96+ /*****************************************************************//**
97+ Creates and initializes the data dictionary at the database creation. */
98+ UNIV_INTERN
99+ void
100+-dict_create(void);
101+-/*=============*/
102++dict_create(ibool ibuf_init);
103++/*=========================*/
104+
105+
106+ /* Space id and page no where the dictionary header resides */
107 --- a/storage/innodb_plugin/include/mem0mem.ic
108 +++ b/storage/innodb_plugin/include/mem0mem.ic
109 @@ -367,7 +367,7 @@
110@@ -654,6 +737,41 @@
111
112 if ((object == slot->object) && (type == slot->type)) {
113
114+--- a/storage/innodb_plugin/include/rem0rec.h
115++++ b/storage/innodb_plugin/include/rem0rec.h
116+@@ -361,6 +361,32 @@
117+ #define rec_get_offsets(rec,index,offsets,n,heap) \
118+ rec_get_offsets_func(rec,index,offsets,n,heap,__FILE__,__LINE__)
119+
120++
121++/******************************************************//**
122++The following function determines the offsets to each field
123++in the record. Function can only calculate offsets for
124++redundant row format records.
125++It can reuse a previously allocated array.
126++@return the new offsets */
127++UNIV_INTERN
128++ulint*
129++rec_get_offsets_old_func(
130++/*=====================*/
131++ const rec_t* rec, /*!< in: physical record */
132++ ulint* offsets,/*!< in/out: array consisting of
133++ offsets[0] allocated elements,
134++ or an array from rec_get_offsets(),
135++ or NULL */
136++ ulint n_fields,/*!< in: maximum number of
137++ initialized fields
138++ (ULINT_UNDEFINED if all fields) */
139++ mem_heap_t** heap, /*!< in/out: memory heap */
140++ const char* file, /*!< in: file name where called */
141++ ulint line); /*!< in: line number where called */
142++
143++#define rec_get_offsets_old(rec,offsets,n,heap) \
144++ rec_get_offsets_old_func(rec,offsets,n,heap,__FILE__,__LINE__)
145++
146+ /******************************************************//**
147+ Determine the offset to each field in a leaf-page record
148+ in ROW_FORMAT=COMPACT. This is a special case of
149 --- a/storage/innodb_plugin/include/srv0srv.h
150 +++ b/storage/innodb_plugin/include/srv0srv.h
151 @@ -202,6 +202,10 @@
152@@ -858,6 +976,24 @@
153 || (recv_addr->state == RECV_BEING_PROCESSED)
154 || (recv_addr->state == RECV_PROCESSED)) {
155
156+@@ -2012,7 +2013,7 @@
157+ /*******************************************************************//**
158+ Tries to parse a single log record and returns its length.
159+ @return length of the record, or 0 if the record was not complete */
160+-static
161++UNIV_INTERN
162+ ulint
163+ recv_parse_log_rec(
164+ /*===============*/
165+@@ -2083,7 +2084,7 @@
166+
167+ /*******************************************************//**
168+ Calculates the new value for lsn when more data is added to the log. */
169+-static
170++UNIV_INTERN
171+ ib_uint64_t
172+ recv_calc_lsn_on_data_add(
173+ /*======================*/
174 @@ -2297,7 +2298,7 @@
175 || type == MLOG_FILE_RENAME
176 || type == MLOG_FILE_DELETE) {
177@@ -997,6 +1133,158 @@
178 #endif
179 }
180
181+--- a/storage/innodb_plugin/rem/rem0rec.c
182++++ b/storage/innodb_plugin/rem/rem0rec.c
183+@@ -344,6 +344,50 @@
184+ = (rec - (lens + 1)) | REC_OFFS_COMPACT | any_ext;
185+ }
186+
187++static void
188++rec_init_offsets_old(
189++/*=================*/
190++ const rec_t* rec, /*!< in: physical record */
191++ ulint* offsets)/*!< in/out: array of offsets;
192++ in: n=rec_offs_n_fields(offsets) */
193++{
194++ ulint i = 0;
195++ ulint offs;
196++
197++ /* Old-style record: determine extra size and end offsets */
198++ offs = REC_N_OLD_EXTRA_BYTES;
199++ if (rec_get_1byte_offs_flag(rec)) {
200++ offs += rec_offs_n_fields(offsets);
201++ *rec_offs_base(offsets) = offs;
202++ /* Determine offsets to fields */
203++ do {
204++ offs = rec_1_get_field_end_info(rec, i);
205++ if (offs & REC_1BYTE_SQL_NULL_MASK) {
206++ offs &= ~REC_1BYTE_SQL_NULL_MASK;
207++ offs |= REC_OFFS_SQL_NULL;
208++ }
209++ rec_offs_base(offsets)[1 + i] = offs;
210++ } while (++i < rec_offs_n_fields(offsets));
211++ } else {
212++ offs += 2 * rec_offs_n_fields(offsets);
213++ *rec_offs_base(offsets) = offs;
214++ /* Determine offsets to fields */
215++ do {
216++ offs = rec_2_get_field_end_info(rec, i);
217++ if (offs & REC_2BYTE_SQL_NULL_MASK) {
218++ offs &= ~REC_2BYTE_SQL_NULL_MASK;
219++ offs |= REC_OFFS_SQL_NULL;
220++ }
221++ if (offs & REC_2BYTE_EXTERN_MASK) {
222++ offs &= ~REC_2BYTE_EXTERN_MASK;
223++ offs |= REC_OFFS_EXTERNAL;
224++ *rec_offs_base(offsets) |= REC_OFFS_EXTERNAL;
225++ }
226++ rec_offs_base(offsets)[1 + i] = offs;
227++ } while (++i < rec_offs_n_fields(offsets));
228++ }
229++}
230++
231+ /******************************************************//**
232+ The following function determines the offsets to each field in the
233+ record. The offsets are written to a previously allocated array of
234+@@ -480,38 +524,7 @@
235+ *rec_offs_base(offsets)
236+ = (rec - (lens + 1)) | REC_OFFS_COMPACT;
237+ } else {
238+- /* Old-style record: determine extra size and end offsets */
239+- offs = REC_N_OLD_EXTRA_BYTES;
240+- if (rec_get_1byte_offs_flag(rec)) {
241+- offs += rec_offs_n_fields(offsets);
242+- *rec_offs_base(offsets) = offs;
243+- /* Determine offsets to fields */
244+- do {
245+- offs = rec_1_get_field_end_info(rec, i);
246+- if (offs & REC_1BYTE_SQL_NULL_MASK) {
247+- offs &= ~REC_1BYTE_SQL_NULL_MASK;
248+- offs |= REC_OFFS_SQL_NULL;
249+- }
250+- rec_offs_base(offsets)[1 + i] = offs;
251+- } while (++i < rec_offs_n_fields(offsets));
252+- } else {
253+- offs += 2 * rec_offs_n_fields(offsets);
254+- *rec_offs_base(offsets) = offs;
255+- /* Determine offsets to fields */
256+- do {
257+- offs = rec_2_get_field_end_info(rec, i);
258+- if (offs & REC_2BYTE_SQL_NULL_MASK) {
259+- offs &= ~REC_2BYTE_SQL_NULL_MASK;
260+- offs |= REC_OFFS_SQL_NULL;
261+- }
262+- if (offs & REC_2BYTE_EXTERN_MASK) {
263+- offs &= ~REC_2BYTE_EXTERN_MASK;
264+- offs |= REC_OFFS_EXTERNAL;
265+- *rec_offs_base(offsets) |= REC_OFFS_EXTERNAL;
266+- }
267+- rec_offs_base(offsets)[1 + i] = offs;
268+- } while (++i < rec_offs_n_fields(offsets));
269+- }
270++ rec_init_offsets_old(rec, offsets);
271+ }
272+ }
273+
274+@@ -587,6 +600,58 @@
275+ return(offsets);
276+ }
277+
278++/******************************************************//**
279++The following function determines the offsets to each field
280++in the record. Function can only calculate offsets for
281++redundant row format records.
282++It can reuse a previously allocated array.
283++@return the new offsets */
284++UNIV_INTERN
285++ulint*
286++rec_get_offsets_old_func(
287++/*=====================*/
288++ const rec_t* rec, /*!< in: physical record */
289++ ulint* offsets,/*!< in/out: array consisting of
290++ offsets[0] allocated elements,
291++ or an array from rec_get_offsets(),
292++ or NULL */
293++ ulint n_fields,/*!< in: maximum number of
294++ initialized fields
295++ (ULINT_UNDEFINED if all fields) */
296++ mem_heap_t** heap, /*!< in/out: memory heap */
297++ const char* file, /*!< in: file name where called */
298++ ulint line) /*!< in: line number where called */
299++{
300++ ulint n;
301++ ulint size;
302++
303++ ut_ad(rec);
304++ ut_ad(heap);
305++
306++ n = rec_get_n_fields_old(rec);
307++
308++ if (UNIV_UNLIKELY(n_fields < n)) {
309++ n = n_fields;
310++ }
311++
312++ size = n + (1 + REC_OFFS_HEADER_SIZE);
313++
314++ if (UNIV_UNLIKELY(!offsets)
315++ || UNIV_UNLIKELY(rec_offs_get_n_alloc(offsets) < size)) {
316++ if (UNIV_UNLIKELY(!*heap)) {
317++ *heap = mem_heap_create_func(size * sizeof(ulint),
318++ MEM_HEAP_DYNAMIC,
319++ file, line);
320++ }
321++ offsets = mem_heap_alloc(*heap, size * sizeof(ulint));
322++ rec_offs_set_n_alloc(offsets, size);
323++ }
324++
325++ rec_offs_set_n_fields(offsets, n);
326++ rec_init_offsets_old(rec, offsets);
327++ return(offsets);
328++}
329++
330+ /******************************************************//**
331+ The following function determines the offsets to each field
332+ in the record. It can reuse a previously allocated array. */
333 --- a/storage/innodb_plugin/row/row0merge.c
334 +++ b/storage/innodb_plugin/row/row0merge.c
335 @@ -453,7 +453,9 @@
336@@ -1074,6 +1362,15 @@
337 }
338
339 /*********************************************************************//**
340+@@ -1321,7 +1326,7 @@
341+ /*********************************************************************//**
342+ Normalizes init parameter values to use units we use inside InnoDB.
343+ @return DB_SUCCESS or error code */
344+-static
345++UNIV_INTERN
346+ ulint
347+ srv_normalize_init_values(void)
348+ /*===========================*/
349 --- a/storage/innodb_plugin/srv/srv0start.c
350 +++ b/storage/innodb_plugin/srv/srv0start.c
351 @@ -94,6 +94,8 @@
352@@ -1094,7 +1391,18 @@
353 ulint
354 open_or_create_log_file(
355 /*====================*/
356-@@ -702,7 +704,7 @@
357+@@ -611,6 +619,10 @@
358+ ret = os_file_get_size(files[i], &size, &size_high);
359+ ut_a(ret);
360+
361++ srv_log_file_size =
362++ ((ib_uint64_t)size_high << 32 | size)
363++ >> UNIV_PAGE_SIZE_SHIFT;
364++
365+ if (size != srv_calc_low32(srv_log_file_size)
366+ || size_high != srv_calc_high32(srv_log_file_size)) {
367+
368+@@ -702,7 +714,7 @@
369 /*********************************************************************//**
370 Creates or opens database data files and closes them.
371 @return DB_SUCCESS or error code */
372@@ -1103,7 +1411,7 @@
373 ulint
374 open_or_create_data_files(
375 /*======================*/
376-@@ -1359,7 +1361,7 @@
377+@@ -1359,7 +1371,7 @@
378 }
379 #endif /* UNIV_LOG_ARCHIVE */
380
381@@ -1112,7 +1420,34 @@
382 fprintf(stderr,
383 "InnoDB: Error: combined size of log files"
384 " must be < 4 GB\n");
385-@@ -1601,6 +1603,10 @@
386+@@ -1516,7 +1528,7 @@
387+ mtr_commit(&mtr);
388+
389+ trx_sys_create();
390+- dict_create();
391++ dict_create(TRUE);
392+ srv_startup_is_before_trx_rollback_phase = FALSE;
393+
394+ #ifdef UNIV_LOG_ARCHIVE
395+@@ -1534,7 +1546,7 @@
396+ /* Since ibuf init is in dict_boot, and ibuf is needed
397+ in any disk i/o, first call dict_boot */
398+
399+- dict_boot();
400++ dict_boot(TRUE);
401+ trx_sys_init_at_db_start();
402+ srv_startup_is_before_trx_rollback_phase = FALSE;
403+
404+@@ -1590,7 +1602,7 @@
405+ to access space 0, and the insert buffer at this stage already
406+ works for space 0. */
407+
408+- dict_boot();
409++ dict_boot(TRUE);
410+ trx_sys_init_at_db_start();
411+
412+ /* Initialize the fsp free limit global variable in the log
413+@@ -1601,6 +1613,10 @@
414 are initialized in trx_sys_init_at_db_start(). */
415
416 recv_recovery_from_checkpoint_finish();
417@@ -1123,7 +1458,7 @@
418 if (srv_force_recovery < SRV_FORCE_NO_IBUF_MERGE) {
419 /* The following call is necessary for the insert
420 buffer to work with multiple tablespaces. We must
421-@@ -1747,7 +1753,18 @@
422+@@ -1747,7 +1763,18 @@
423
424 if (srv_auto_extend_last_data_file
425 && sum_of_data_file_sizes < tablespace_size_in_header) {
426@@ -1142,7 +1477,7 @@
427 fprintf(stderr,
428 "InnoDB: Error: tablespace size stored in header"
429 " is %lu pages, but\n"
430-@@ -1772,6 +1789,7 @@
431+@@ -1772,6 +1799,7 @@
432
433 return(DB_ERROR);
434 }
435@@ -1150,7 +1485,7 @@
436 }
437
438 /* Check that os_fast_mutexes work as expected */
439-@@ -1867,6 +1885,7 @@
440+@@ -1867,6 +1895,7 @@
441 ibuf_update_max_tablespace_id();
442 }
443
444
445=== modified file 'src/Makefile'
446--- src/Makefile 2012-05-25 11:38:15 +0000
447+++ src/Makefile 2012-10-15 14:16:23 +0000
448@@ -35,6 +35,10 @@
449 xbstream_write.o \
450 quicklz/quicklz.o
451 XBSTREAMOBJS = xbstream.o xbstream_write.o xbstream_read.o
452+UTILSCOREOBJS = api_page.o api_log.o
453+UTILSCOBJS = innodb_int.o $(UTILSCOREOBJS)
454+REDOCOBJS = percona-redo.o
455+PAGECOBJS = percona-pprint.o
456
457 LIBARCHIVE_A = libarchive/libarchive/libarchive.a
458
459@@ -86,8 +90,9 @@
460
461 plugin: MYSQLOBJS = $(addprefix $(MYSQL_ROOT_DIR)/, mysys/libmysys.a \
462 strings/libmystrings.a zlib/.libs/libzlt.a dbug/libdbug.a)
463+plugin: EXTLINKOBJS := $(UTILSCOREOBJS)
464 plugin: TARGET := xtrabackup_plugin
465-plugin: $(TARGET) xbstream
466+plugin: utils $(TARGET) xbstream
467
468 # XtraBackup for MySQL 5.5
469 5.5: INC = $(COMMON_INC) $(addprefix -isystem$(MYSQL_ROOT_DIR)/, \
470@@ -179,7 +184,7 @@
471 xtradb55: TARGET := xtrabackup_55
472 xtradb55: $(TARGET) xbstream
473
474-$(XTRABACKUPOBJS): %.o: %.c
475+$(XTRABACKUPOBJS) $(UTILSCOREOBJS) $(REDOCOBJS) $(PAGECOBJS): %.o: %.c
476 $(CC) $(CFLAGS) $(INC) $(DEFS) -c $< -o $@
477
478 xbstream.o xbstream_read.o: %.o: %.c
479@@ -190,9 +195,13 @@
480
481 xtrabackup.o: xtrabackup.c xb_regex.h write_filt.h fil_cur.h xtrabackup.h
482
483-$(TARGET): $(XTRABACKUPOBJS) $(INNODBOBJS) $(MYSQLOBJS) $(LIBARCHIVE_A)
484- $(CC) $(CFLAGS) $(XTRABACKUPOBJS) $(INNODBOBJS) $(MYSQLOBJS) $(LIBS) \
485+$(TARGET): $(EXTLINKOBJS) $(XTRABACKUPOBJS) $(INNODBOBJS) $(MYSQLOBJS) $(LIBARCHIVE_A)
486+ $(CC) $(CFLAGS) $(EXTLINKOBJS) $(XTRABACKUPOBJS) $(INNODBOBJS) $(MYSQLOBJS) $(LIBS) \
487 $(LIBARCHIVE_A) -o $(TARGET)
488
489+utils: $(UTILSCOBJS) $(INNODBOBJS) $(MYSQLOBJS) $(REDOCOBJS) $(PAGECOBJS)
490+ $(CC) $(UTILSCOBJS) $(REDOCOBJS) $(INNODBOBJS) $(MYSQLOBJS) $(LIBS) -o percona-redo
491+ $(CC) $(UTILSCOBJS) $(PAGECOBJS) $(INNODBOBJS) $(MYSQLOBJS) $(LIBS) -o percona-pprint
492+
493 clean:
494- rm -f $(XTRABACKUPOBJS) $(XBSTREAMOBJS) xtrabackup xtrabackup_*
495+ rm -f $(UTILSCOBJS) $(XTRABACKUPOBJS) $(XBSTREAMOBJS) xtrabackup xtrabackup_*
496
497=== added file 'src/api_log.c'
498--- src/api_log.c 1970-01-01 00:00:00 +0000
499+++ src/api_log.c 2012-10-15 14:16:23 +0000
500@@ -0,0 +1,1493 @@
501+/*
502+ * Copyright (C) 2012 Percona Inc.
503+ * This program is free software: you can redistribute it and/or modify it
504+ * under the terms of the GNU General Public License version 3, as published
505+ * by the Free Software Foundation.
506+ *
507+ * This program is distributed in the hope that it will be useful, but
508+ * WITHOUT ANY WARRANTY; without even the implied warranties of
509+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
510+ * PURPOSE. See the GNU General Public License for more details.
511+ *
512+ * You should have received a copy of the GNU General Public License along
513+ * with this program. If not, see <http://www.gnu.org/licenses/>.
514+ */
515+
516+#define DULINT_STANDARD
517+
518+#include "api_log.h"
519+#include "fil0fil.h"
520+#include "log0log.h"
521+#include "log0recv.h"
522+#include "rem0rec.h"
523+#include "os0file.h"
524+#include "os0sync.h"
525+#include "sync0sync.h"
526+#include "srv0srv.h"
527+
528+#include "innodb_int.h"
529+
530+#define SRV_N_PENDING_IOS_PER_THREAD OS_AIO_N_PENDING_IOS_PER_THREAD
531+#define SRV_MAX_N_PENDING_SYNC_IOS 100
532+
533+#ifndef MLOG_LSN
534+ #define MLOG_LSN ((byte)28)
535+#endif
536+
537+static const char* mlog_type_names[] = {
538+ "undefined (0)",
539+ "MLOG_1BYTE",
540+ "MLOG_2BYTES",
541+ "undefined (3)",
542+ "MLOG_4BYTES",
543+ "undefined (5)",
544+ "undefined (6)",
545+ "undefined (7)",
546+ "MLOG_8BYTES",
547+ "MLOG_REC_INSERT",
548+ "MLOG_REC_CLUST_DELETE_MARK", /* 10 */
549+ "MLOG_REC_SEC_DELETE_MARK",
550+ "undefined (12)",
551+ "MLOG_REC_UPDATE_IN_PLACE",
552+ "MLOG_REC_DELETE",
553+ "MLOG_LIST_END_DELETE",
554+ "MLOG_LIST_START_DELETE",
555+ "MLOG_LIST_END_COPY_CREATED",
556+ "MLOG_PAGE_REORGANIZE",
557+ "MLOG_PAGE_CREATE",
558+ "MLOG_UNDO_INSERT", /* 20 */
559+ "MLOG_UNDO_ERASE_END",
560+ "MLOG_UNDO_INIT",
561+ "MLOG_UNDO_HDR_DISCARD",
562+ "MLOG_UNDO_HDR_REUSE",
563+ "MLOG_UNDO_HDR_CREATE",
564+ "MLOG_REC_MIN_MARK",
565+ "MLOG_IBUF_BITMAP_INIT",
566+ "MLOG_LSN",
567+ "MLOG_INIT_FILE_PAGE",
568+ "MLOG_WRITE_STRING", /* 30 */
569+ "MLOG_MULTI_REC_END",
570+ "MLOG_DUMMY_RECORD",
571+ "MLOG_FILE_CREATE",
572+ "MLOG_FILE_RENAME",
573+ "MLOG_FILE_DELETE",
574+ "MLOG_COMP_REC_MIN_MARK",
575+ "MLOG_COMP_PAGE_CREATE",
576+ "MLOG_COMP_REC_INSERT",
577+ "MLOG_COMP_REC_CLUST_DELETE_MARK",
578+ "MLOG_COMP_REC_SEC_DELETE_MARK", /* 40 */
579+ "MLOG_COMP_REC_UPDATE_IN_PLACE",
580+ "MLOG_COMP_REC_DELETE",
581+ "MLOG_COMP_LIST_END_DELETE",
582+ "MLOG_COMP_LIST_START_DELETE",
583+ "MLOG_COMP_LIST_END_COPY_CREATED",
584+ "MLOG_COMP_PAGE_REORGANIZE",
585+ "MLOG_FILE_CREATE2",
586+ "MLOG_ZIP_WRITE_NODE_PTR",
587+ "MLOG_ZIP_WRITE_BLOB_PTR",
588+ "MLOG_ZIP_WRITE_HEADER", /* 50 */
589+ "MLOG_ZIP_PAGE_COMPRESS"
590+};
591+
592+/* Informaton about single log record */
593+struct ib_log_rec_struct
594+{
595+ ib_uint64_t lsn; /* record's LSN */
596+ ulint len; /* length */
597+ byte type; /* type MLOG_* */
598+ ulint space; /* space id or ULINT_UNDEFINED */
599+ ulint page_no; /* page number or ULINT_UNDEFINED */
600+ char* body_str; /* log record's body as string */
601+ unsigned single_rec:1; /* set if MLOG_SINGLE_REC_FLAG
602+ is set for record */
603+};
604+
605+/* log iterator */
606+struct ib_log_rec_itr_struct
607+{
608+ ib_uint64_t start_lsn; /* starting LSN */
609+ ib_uint64_t end_lsn; /* end LSN */
610+ ib_uint64_t block_start_lsn; /* OS_FILE_LOG_BLOCK_SIZE aligned LSN */
611+ byte* read_buf; /* read buffer */
612+ byte* read_buf_ptr; /* pointer to current record in read buffer */
613+ byte* parse_buf; /* parse buffer */
614+ byte* parse_buf_ptr; /* pointer to current record in parse buffer */
615+ byte* parse_buf_end; /* end of parse buffer */
616+ ulint scanned_checkpoint_no; /* last checkpoint stored in the log data */
617+ ibool eof; /* TRUE if end of scanned interval reached */
618+ ib_uint64_t next_parse_lsn; /* LSN of next record to be parsed */
619+ struct ib_log_rec_struct current_rec; /* current log record */
620+};
621+
622+/*********************************************************************//**
623+Initialize InnoDB systems needed for the log printer
624+@return DB_SUCCESS if successfull */
625+HAILDB_API
626+ib_err_t
627+ib_log_sys_start(
628+/*=============*/
629+ ib_ulint_t log_buf_size, /*!< in: the size of the buffer */
630+ const char* log_dir) /*!< in: path to InnoDB log files. */
631+{
632+ ibool log_file_created = TRUE;
633+ ib_err_t err;
634+ ulint i;
635+
636+ innodb_init_param_for_util("./", log_dir, 1024*1024, log_buf_size, TRUE);
637+
638+ srv_max_n_threads = 1;
639+ srv_normalize_init_values();
640+ ut_mem_init();
641+
642+ srv_n_read_io_threads = srv_n_write_io_threads = 1;
643+ srv_n_file_io_threads = 2 + srv_n_read_io_threads +
644+ srv_n_write_io_threads;
645+ os_sync_init();
646+ sync_init();
647+ // os_io_init_simple();
648+ os_aio_init(8 * SRV_N_PENDING_IOS_PER_THREAD,
649+ srv_n_read_io_threads,
650+ srv_n_write_io_threads,
651+ SRV_MAX_N_PENDING_SYNC_IOS);
652+
653+ mem_init(1);
654+ fil_init(10, 10);
655+ log_init();
656+ recv_sys_create();
657+
658+ for (i = 0; i < srv_n_log_files; i++) {
659+ err = open_or_create_log_file(FALSE, &log_file_created, TRUE, 0, 0);
660+ if (err != DB_SUCCESS) {
661+ ib_log_sys_stop();
662+ return err;
663+ }
664+
665+ if (log_file_created) {
666+ ib_logger(ib_stream,
667+ "Something wrong with source files...\n");
668+ return DB_ERROR;
669+ }
670+ }
671+
672+ /* open_or_create_log_file will set srv_log_file_size. Normalize the
673+ init values again to get srv_log_file_size/srv_log_file_curr_size in
674+ sync. */
675+ srv_normalize_init_values();
676+
677+ return DB_SUCCESS;
678+}
679+
680+/*********************************************************************//**
681+Uninitialize InnoDB systems needed for the log printer */
682+HAILDB_API
683+void
684+ib_log_sys_stop(void)
685+{
686+ recv_sys_close();
687+ recv_sys_mem_free();
688+ fil_close_all_files();
689+ log_shutdown();
690+ log_mem_free();
691+ sync_close();
692+ os_sync_free();
693+ fil_close();
694+ mem_close();
695+ ut_free_all_mem();
696+}
697+
698+/*********************************************************************//**
699+Find last consistent checkpoint LSN in log groups */
700+HAILDB_API
701+ib_err_t
702+ib_log_sys_find_last_checkpoint_lsn(
703+/*================================*/
704+ ib_u64_t* last_checkpoint_lsn) /*!< out: last checkpoint LSN */
705+{
706+ log_group_t* max_cp_group;
707+ ulint max_cp_field;
708+
709+ mutex_enter(&log_sys->mutex);
710+
711+ uint err = recv_find_max_checkpoint(&max_cp_group, &max_cp_field);
712+ if (err != DB_SUCCESS)
713+ return err;
714+
715+ log_group_read_checkpoint_info(max_cp_group, max_cp_field);
716+ *last_checkpoint_lsn
717+ = mach_read_ull(log_sys->checkpoint_buf + LOG_CHECKPOINT_LSN);
718+
719+ mutex_exit(&log_sys->mutex);
720+
721+ return DB_SUCCESS;
722+}
723+
724+/*********************************************************************//**
725+Create iterator through log records */
726+HAILDB_API
727+ib_log_rec_itr_t
728+ib_log_sys_create_iterator(
729+/*=======================*/
730+ ib_u64_t start_lsn, /*!< in: starting LSN */
731+ ib_u64_t end_lsn) /*!< in: last LSN */
732+{
733+ ib_log_rec_itr_t result = mem_alloc(sizeof(*result));
734+
735+ result->start_lsn = start_lsn;
736+ result->end_lsn = end_lsn;
737+ result->block_start_lsn = ut_uint64_align_down(start_lsn,
738+ OS_FILE_LOG_BLOCK_SIZE);
739+ result->read_buf = mem_alloc(RECV_SCAN_SIZE);
740+ /* Pointing to the end of buffer will trigger the first read */
741+ result->read_buf_ptr = result->read_buf + RECV_SCAN_SIZE;
742+ result->parse_buf = mem_alloc(RECV_PARSING_BUF_SIZE);
743+ result->parse_buf_ptr = result->parse_buf;
744+ result->parse_buf_end = result->parse_buf;
745+ result->scanned_checkpoint_no = 0;
746+ result->eof = FALSE;
747+ result->next_parse_lsn = result->block_start_lsn;
748+
749+ result->current_rec.lsn = IB_ULONGLONG_MAX;
750+ result->current_rec.len = ULINT_UNDEFINED;
751+ result->current_rec.type = MLOG_BIGGEST_TYPE + 1;
752+ result->current_rec.space = ULINT_UNDEFINED;
753+ result->current_rec.page_no = ULINT_UNDEFINED;
754+ result->current_rec.body_str = NULL;
755+ return result;
756+}
757+
758+/*********************************************************************//**
759+For the given minilog record type determines if the record has (space; page)
760+associated with it.
761+@return TRUE if the record has (space; page) in it */
762+static
763+ibool
764+log_online_rec_has_page(
765+/*====================*/
766+ byte type) /*!<in: the minilog record type */
767+{
768+ return type != MLOG_MULTI_REC_END && type != MLOG_DUMMY_RECORD;
769+}
770+
771+
772+
773+/*********************************************************************//**
774+Copies new log data to the parse buffer while skipping log block header,
775+and trailer. The decision how much to copy here is taken from
776+recv_sys_add_to_parsing_buf but simplified. */
777+static
778+void
779+log_online_add_to_parse_buf(
780+/*========================*/
781+ const byte* log_block, /*!< in: read log data */
782+ ulint start_offset, /*!< in: data start offset */
783+ ulint data_len, /*!< in: length of read log data */
784+ byte* parse_buf, /*!< in: the parse buffer */
785+ byte** parse_buf_end) /*!< in/out: end of parse buffer */
786+{
787+ ulint end_offset
788+ = (data_len == OS_FILE_LOG_BLOCK_SIZE)
789+ ? data_len - LOG_BLOCK_TRL_SIZE
790+ : data_len;
791+ ulint actual_data_len = (end_offset >= start_offset)
792+ ? end_offset - start_offset : 0;
793+
794+ ut_a (start_offset >= LOG_BLOCK_HDR_SIZE);
795+
796+ if ((*parse_buf_end + actual_data_len - parse_buf)
797+ > RECV_PARSING_BUF_SIZE) {
798+
799+ ib_logger(ib_stream, "Parse buffer overflow! Most likely "
800+ "caused by corrupt log data. In the case of "
801+ "correct log increase RECV_PARSING_BUF_SIZE\n");
802+ ib_logger(ib_stream, "Discarding the overflowing data, some "
803+ "records will be skipped\n");
804+ actual_data_len
805+ = RECV_PARSING_BUF_SIZE - (*parse_buf_end - parse_buf);
806+ }
807+
808+ ut_memcpy(*parse_buf_end, log_block + start_offset, actual_data_len);
809+ *parse_buf_end += actual_data_len;
810+ ut_ad (*parse_buf_end - parse_buf <= RECV_PARSING_BUF_SIZE);
811+}
812+
813+/*********************************************************************//**
814+Checks if the log block checksum is OK. TODO: with comparisson to log0recv
815+checks, this one drops the scanned no vs. no in the log check.
816+TODO: copy-paste with innodb-changed-page-tracking log0online.c
817+@return TRUE if the log block checksum is OK, FALSE otherwise. */
818+static
819+ibool
820+log_online_is_valid_log_seg(
821+/*========================*/
822+ const byte* log_block) /*!< in: read log data */
823+{
824+ ibool checksum_is_ok
825+ = log_block_checksum_is_ok_or_old_format(log_block);
826+
827+ if (!checksum_is_ok) {
828+ /* Garbage or an incompletely written log block */
829+
830+ ib_logger(ib_stream,
831+ "Log block checksum mismatch: "
832+ "expected %lu, calculated checksum %lu\n",
833+ (ulong) log_block_get_checksum(log_block),
834+ (ulong) log_block_calc_checksum(log_block));
835+ }
836+
837+ return checksum_is_ok;
838+}
839+
840+/*********************************************************************//**
841+Checks if the log block is in fact garbage from a log buffer flush which was
842+made before the most recent database recovery.
843+@return TRUE if log block is garbage from the flush */
844+static
845+ibool
846+log_online_is_garbage_log_seg(
847+/*==========================*/
848+ const byte* log_block, /*!< in: read log data */
849+ ulint scanned_checkpoint_no) /*!< in: last checkpoint
850+ stored in the log data */
851+{
852+ return (scanned_checkpoint_no > 0)
853+ && (log_block_get_checkpoint_no(log_block)
854+ < scanned_checkpoint_no)
855+ && (scanned_checkpoint_no
856+ - log_block_get_checkpoint_no(log_block)
857+ > 0x80000000UL);
858+}
859+
860+/*********************************************************************//**
861+Copy consistent piece of data from the read buffer to the parse buffer.
862+@return TRUE if data been copied */
863+static
864+ibool
865+fill_parse_buffer(
866+/*==============*/
867+ ib_log_rec_itr_t itr) /*!< in: log records iterator */
868+{
869+ ulint block_data_len;
870+ ulint data_start_offset = LOG_BLOCK_HDR_SIZE;
871+
872+ if (itr->eof)
873+ return FALSE;
874+
875+ if (itr->read_buf_ptr == itr->read_buf + RECV_SCAN_SIZE) {
876+
877+ ib_uint64_t read_end_lsn;
878+ log_group_t* group;
879+
880+ /* Read buf empty, read more recs if there are any */
881+ read_end_lsn = itr->block_start_lsn + RECV_SCAN_SIZE;
882+ group = UT_LIST_GET_FIRST(log_sys->log_groups);
883+ ut_a(group);
884+
885+ /* Single-threaded operation, no mutexes */
886+ mutex_enter(&log_sys->mutex);
887+ log_group_read_log_seg(LOG_RECOVER, itr->read_buf, group,
888+ itr->block_start_lsn, read_end_lsn);
889+ mutex_exit(&log_sys->mutex);
890+ itr->read_buf_ptr = itr->read_buf;
891+ }
892+
893+ /* Read buffer has some data, process and move one block to the parse
894+ * buffer. */
895+ if (!log_online_is_valid_log_seg(itr->read_buf_ptr)) {
896+ return FALSE;
897+ }
898+
899+ if (log_online_is_garbage_log_seg(itr->read_buf_ptr,
900+ itr->scanned_checkpoint_no)) {
901+ return FALSE;
902+ }
903+
904+ block_data_len = log_block_get_data_len(itr->read_buf_ptr);
905+
906+ if (itr->next_parse_lsn == itr->block_start_lsn) {
907+
908+ /* First read, find the start of the first complete record
909+ group in this block */
910+ ut_a (itr->start_lsn
911+ < (itr->block_start_lsn + OS_FILE_LOG_BLOCK_SIZE));
912+ data_start_offset
913+ = log_block_get_first_rec_group(itr->read_buf_ptr);
914+ /* TODO: is this the correct way to handle 0 return value? */
915+ if (data_start_offset == 0)
916+ data_start_offset = LOG_BLOCK_HDR_SIZE;
917+ itr->next_parse_lsn += data_start_offset;
918+ }
919+ ut_ad (block_data_len % OS_FILE_LOG_BLOCK_SIZE == 0
920+ || block_data_len < OS_FILE_LOG_BLOCK_SIZE);
921+
922+ log_online_add_to_parse_buf(itr->read_buf_ptr, data_start_offset,
923+ block_data_len, itr->parse_buf,
924+ &itr->parse_buf_end);
925+
926+ /* TODO: check itr->scanned_checkpoint_no */
927+ itr->scanned_checkpoint_no
928+ = log_block_get_checkpoint_no(itr->read_buf_ptr);
929+
930+ if (block_data_len < OS_FILE_LOG_BLOCK_SIZE)
931+ itr->eof = TRUE;
932+
933+ if (!itr->eof) {
934+ itr->block_start_lsn += OS_FILE_LOG_BLOCK_SIZE;
935+ itr->read_buf_ptr += OS_FILE_LOG_BLOCK_SIZE;
936+ }
937+
938+ return TRUE;
939+}
940+
941+static char buf[RECV_SCAN_SIZE];
942+
943+/*********************************************************************//**
944+Parse index data and format it in human-readable form to static buffer.
945+@return end of data copied to buffer */
946+static
947+char *
948+ib_log_sys_parse_index(
949+/*===================*/
950+ byte** ptr) /*!< in/out: pointer to log data */
951+{
952+ char* buf_end = buf;
953+ ulint n_uniq;
954+ ulint n = mach_read_from_2(*ptr);
955+ unsigned i;
956+ *ptr += 2;
957+ n_uniq = mach_read_from_2(*ptr);
958+ *ptr += 2;
959+ buf_end += snprintf(buf, RECV_SCAN_SIZE, "n: %lu, n_unique: %lu",
960+ n, n_uniq);
961+ for (i = 0; i < n; i++) {
962+ ulint len = mach_read_from_2(*ptr);
963+ *ptr += 2;
964+ buf_end += snprintf(buf_end, RECV_SCAN_SIZE - (buf_end - buf),
965+ ", len%u: %lu", i, len);
966+ }
967+ return buf_end;
968+}
969+
970+/*********************************************************************//**
971+Parse system fields of log record. */
972+static
973+void
974+ib_log_sys_parse_sys_fields(
975+/*========================*/
976+ byte** ptr, /*!< in/out: pointer to log data */
977+ byte* body_end, /*!< in: pointer to end of
978+ a log record body */
979+ ulint* pos, /*!< out: position */
980+ trx_id_t* trx_id, /*!< out: trx id */
981+ roll_ptr_t* roll_ptr) /*!< out: roll pointer */
982+{
983+ *ptr = mach_parse_compressed(*ptr, body_end, pos);
984+ *roll_ptr = mach_read_from_7(*ptr);
985+ *ptr += 7;
986+ *ptr = mach_dulint_parse_compressed(*ptr, body_end, trx_id);
987+}
988+
989+/*********************************************************************//**
990+Parse the redo log record for delete marking or unmarking of a clustered
991+index record. */
992+static
993+void
994+ib_log_sys_parse_del_mark_set_clust_rec(
995+/*====================================*/
996+ ib_log_rec_itr_t itr, /*!< in/out: log iterator */
997+ byte** ptr, /*!< in/out: pointer to
998+ log data */
999+ byte* body_end) /*!< in: pointer to end of a
1000+ log record body */
1001+{
1002+ ulint val;
1003+ ulint pos;
1004+ roll_ptr_t roll_ptr;
1005+ trx_id_t trx_id;
1006+ ulint offset;
1007+ int ret;
1008+ ulint flags = mach_read_from_1(*ptr);
1009+ (*ptr)++;
1010+ val = mach_read_from_1(*ptr);
1011+ (*ptr)++;
1012+ ib_log_sys_parse_sys_fields(ptr, body_end, &pos, &trx_id, &roll_ptr);
1013+ offset = mach_read_from_2(*ptr);
1014+ *ptr += 2;
1015+ ret = asprintf(&itr->current_rec.body_str,
1016+ "%sflags: 0x%lX, value: 0x%lX, position: 0x%lX, "
1017+ "roll_ptr: 0x%llX, trx_id: 0x%llX, offset: 0x%lX",
1018+ buf, flags, val, pos,
1019+ ut_conv_dulint_to_longlong(roll_ptr),
1020+ ut_conv_dulint_to_longlong(trx_id), offset);
1021+ ut_a (ret > 0);
1022+}
1023+
1024+/*********************************************************************//**
1025+Dump a log record as a hex string */
1026+static
1027+void
1028+ib_log_sys_output_raw(
1029+/*==================*/
1030+ byte** ptr, /*!< in/out: pointer to log data */
1031+ byte* body_end, /*!< in: pointer to end of a log record body */
1032+ char** buf_end) /*!< in/out: pointer to end of output buffer */
1033+{
1034+ while (*ptr != body_end) {
1035+ *buf_end += snprintf(*buf_end,
1036+ RECV_SCAN_SIZE - (*buf_end - buf),
1037+ " %02X", **ptr);
1038+ (*ptr)++;
1039+ }
1040+}
1041+
1042+/*********************************************************************//**
1043+Parse update of a compact record */
1044+static
1045+void
1046+ib_log_sys_parse_update_in_place(
1047+/*=============================*/
1048+ ib_log_rec_itr_t itr, /*!< in/out: log iterator */
1049+ byte** ptr, /*!< in/out: pointer to
1050+ log data */
1051+ byte* body_end) /*!< in: pointer to end of
1052+ a log record body */
1053+{
1054+ char *buf_end = buf + strlen(buf);
1055+ ulint pos;
1056+ roll_ptr_t roll_ptr;
1057+ trx_id_t trx_id;
1058+ ulint offset;
1059+ ulint info_bits;
1060+ ulint n_fields;
1061+ unsigned i;
1062+ ulint flags = mach_read_from_1(*ptr);
1063+ (*ptr)++;
1064+ ib_log_sys_parse_sys_fields(ptr, body_end, &pos, &trx_id, &roll_ptr);
1065+ offset = mach_read_from_2(*ptr);
1066+ *ptr += 2;
1067+ info_bits = mach_read_from_1(*ptr);
1068+ (*ptr)++;
1069+ *ptr = mach_parse_compressed(*ptr, body_end, &n_fields);
1070+ buf_end += snprintf(buf_end, RECV_SCAN_SIZE - (buf_end - buf),
1071+ "flags: 0x%lX, position: 0x%lX, roll_ptr: 0x%llX, "
1072+ "trx_id: 0x%llX, offset: 0x%lX, info_bits: 0x%lX, "
1073+ "n_fields: %lu", flags, pos,
1074+ ut_conv_dulint_to_longlong(roll_ptr),
1075+ ut_conv_dulint_to_longlong(trx_id), offset,
1076+ info_bits, n_fields);
1077+ for (i = 0; i < n_fields; i++) {
1078+ ulint field_no;
1079+ ulint len;
1080+ *ptr = mach_parse_compressed(*ptr, body_end, &field_no);
1081+ buf_end += snprintf(buf_end, RECV_SCAN_SIZE - (buf_end - buf),
1082+ ", field_no_%d: %lu", i, field_no);
1083+ *ptr = mach_parse_compressed(*ptr, body_end, &len);
1084+ if (len != UNIV_SQL_NULL) {
1085+ buf_end += snprintf(buf_end,
1086+ RECV_SCAN_SIZE - (buf_end - buf),
1087+ ", data_len_%d: %lu, data:", i,
1088+ len);
1089+ ib_log_sys_output_raw(ptr, *ptr + len, &buf_end);
1090+ }
1091+ else {
1092+ buf_end += snprintf(buf_end,
1093+ RECV_SCAN_SIZE - (buf_end - buf),
1094+ ", data_len_%d: UNIV_SQL_NULL", i);
1095+ }
1096+ }
1097+ itr->current_rec.body_str = strdup(buf);
1098+}
1099+
1100+/*********************************************************************//**
1101+Parse delete record list */
1102+static
1103+void
1104+ib_log_sys_parse_delete_rec_list(
1105+/*=============================*/
1106+ ib_log_rec_itr_t itr, /*!< in/out: log iterator */
1107+ byte** ptr) /*!< in/out: pointer to log data */
1108+{
1109+ ulint offset = mach_read_from_2(*ptr);
1110+ char *buf_end = buf + strlen(buf);
1111+ *ptr += 2;
1112+ buf_end += snprintf(buf_end, RECV_SCAN_SIZE - (buf_end - buf),
1113+ "offset: %lu", offset);
1114+ itr->current_rec.body_str = strdup(buf);
1115+}
1116+
1117+/*********************************************************************//**
1118+Read info bits of insert record */
1119+static
1120+void
1121+ib_log_sys_parse_info_origin_mismatch(
1122+/*==================================*/
1123+ byte** ptr, /*!< in/out: pointer to log data */
1124+ byte* body_end, /*!< in: pointer to end of a log record body */
1125+ char** buf_end) /*!< in/out: pointer to end of output buffer */
1126+{
1127+ ulint origin_offset;
1128+ ulint mismatch_index;
1129+ ulint info_and_status_bits = mach_read_from_1(*ptr);
1130+ (*ptr)++;
1131+ *ptr = mach_parse_compressed(*ptr, body_end, &origin_offset);
1132+ *ptr = mach_parse_compressed(*ptr, body_end, &mismatch_index);
1133+ *buf_end += snprintf(*buf_end, RECV_SCAN_SIZE - (*buf_end - buf),
1134+ ", info_status_bits: 0x%lX, origin_offset: %lu, "
1135+ "mismatch_index: %lu",
1136+ info_and_status_bits, origin_offset,
1137+ mismatch_index);
1138+}
1139+
1140+/*********************************************************************//**
1141+Parse compact record insert */
1142+static
1143+void
1144+ib_log_sys_parse_rec_insert(
1145+/*========================*/
1146+ ib_log_rec_itr_t itr, /*!< in/out: log iterator */
1147+ byte** ptr, /*!< in/out: pointer to
1148+ log data */
1149+ byte* body_end) /*!< in: pointer to end of a log
1150+ record body */
1151+{
1152+ char* buf_end = buf;
1153+ ulint end_seg_len;
1154+ ulint offset = mach_read_from_2(*ptr);
1155+ *ptr += 2;
1156+ *ptr = mach_parse_compressed(*ptr, body_end, &end_seg_len);
1157+ buf_end += snprintf(buf_end, RECV_SCAN_SIZE - (buf_end - buf),
1158+ "offset: %lu, end_seg_len: %lu", offset,
1159+ end_seg_len);
1160+ if (end_seg_len & 0x1UL) {
1161+
1162+ ib_log_sys_parse_info_origin_mismatch(ptr, body_end,
1163+ &buf_end);
1164+ }
1165+ strcpy(buf_end, ", data:");
1166+ buf_end += strlen(", data:");
1167+ ib_log_sys_output_raw(ptr, *ptr + (end_seg_len >> 1), &buf_end);
1168+ itr->current_rec.body_str = strdup(buf);
1169+}
1170+
1171+/*********************************************************************//**
1172+Parse copy record list end to a new created index page */
1173+static
1174+void
1175+ib_log_sys_parse_end_list_copy(
1176+/*===========================*/
1177+ ib_log_rec_itr_t itr, /*!< in/out: log iterator */
1178+ byte** ptr, /*!< in/out: pointer to
1179+ log data */
1180+ byte* body_end) /*!< in: pointer to end of a
1181+ log record body */
1182+{
1183+ char *buf_end = buf + strlen(buf);
1184+ ulint data_len = mach_read_from_4(*ptr);
1185+ *ptr += 4;
1186+ buf_end += snprintf(buf_end, RECV_SCAN_SIZE - (buf_end - buf),
1187+ "len: %lu", data_len);
1188+ while (*ptr < body_end) {
1189+ ulint end_seg_len;
1190+ *ptr = mach_parse_compressed(*ptr, body_end, &end_seg_len);
1191+ buf_end += snprintf(buf_end, RECV_SCAN_SIZE - (buf_end - buf),
1192+ ", end_seg_len: %lu", end_seg_len);
1193+ if (end_seg_len & 0x1UL) {
1194+
1195+ ib_log_sys_parse_info_origin_mismatch(ptr, body_end,
1196+ &buf_end);
1197+ }
1198+ strcpy(buf_end, ", data:");
1199+ buf_end += strlen(", data:");
1200+ ib_log_sys_output_raw(ptr, *ptr + (end_seg_len >> 1),
1201+ &buf_end);
1202+ }
1203+ itr->current_rec.body_str = strdup(buf);
1204+}
1205+
1206+/*********************************************************************//**
1207+Parse delete record from the page */
1208+static
1209+void
1210+ib_log_sys_parse_delete_rec(
1211+/*========================*/
1212+ ib_log_rec_itr_t itr, /*!< in/out: log iterator */
1213+ byte** ptr) /*!< in/out: pointer to log data */
1214+{
1215+ char* buf_end = buf + strlen(buf);
1216+ ulint offset = mach_read_from_2(*ptr);
1217+ *ptr += 2;
1218+ buf_end += snprintf(buf, RECV_SCAN_SIZE, "offset: %lu", offset);
1219+ itr->current_rec.body_str = strdup(buf);
1220+}
1221+
1222+/*********************************************************************//**
1223+Parse a file operation record */
1224+static
1225+void
1226+ib_log_sys_parse_fil_op(
1227+/*====================*/
1228+ ib_log_rec_itr_t itr, /*!< in/out: log iterator */
1229+ byte** ptr, /*!< in/out: pointer to
1230+ log data */
1231+ char** buf_end) /*!< in: pointer to end of a
1232+ log record body */
1233+{
1234+ const char* name;
1235+ ulint name_len = mach_read_from_2(*ptr);
1236+ *ptr += 2;
1237+ name = (const char *)(*ptr);
1238+ *ptr += name_len;
1239+ *buf_end += snprintf(*buf_end, RECV_SCAN_SIZE - (*buf_end - buf),
1240+ "name_len: %lu, name: \"%s\"", name_len, name);
1241+ if (itr->current_rec.type == MLOG_FILE_RENAME) {
1242+ const char* new_name;
1243+ ulint new_name_len = mach_read_from_2(*ptr);
1244+ *ptr += 2;
1245+ new_name = (const char *)(*ptr);
1246+ *ptr += new_name_len;
1247+ *buf_end += snprintf(*buf_end,
1248+ RECV_SCAN_SIZE - (*buf_end - buf),
1249+ ", new_name_len: %lu, new_name: \"%s\"",
1250+ new_name_len, new_name);
1251+ }
1252+ itr->current_rec.body_str = strdup(buf);
1253+}
1254+
1255+/*********************************************************************//**
1256+Get name of operation with compressed page */
1257+static
1258+const char*
1259+ib_log_sys_get_zip_op_name(
1260+/*=======================*/
1261+ byte type) /*!< in: log record type */
1262+{
1263+ if (type == MLOG_ZIP_WRITE_NODE_PTR)
1264+ return "node_ptr";
1265+ if (type == MLOG_ZIP_WRITE_BLOB_PTR)
1266+ return "blob_ptr";
1267+ ut_a (0);
1268+ return "WRONG TYPE";
1269+}
1270+
1271+/*********************************************************************//**
1272+Get pointer length for operation with compressed page */
1273+static
1274+ulint
1275+ib_log_sys_get_zip_op_ptr_len(
1276+/*==========================*/
1277+ byte type) /*!< in: log record type */
1278+{
1279+ if (type == MLOG_ZIP_WRITE_NODE_PTR)
1280+ return REC_NODE_PTR_SIZE;
1281+ if (type == MLOG_ZIP_WRITE_BLOB_PTR)
1282+ return BTR_EXTERN_FIELD_REF_SIZE;
1283+ ut_a (0);
1284+ return ULINT_UNDEFINED;
1285+}
1286+
1287+/*********************************************************************//**
1288+Parse operation with compressed page */
1289+static
1290+void
1291+ib_log_sys_parse_zip_op(
1292+/*====================*/
1293+ ib_log_rec_itr_t itr, /*!< in/out: log iterator */
1294+ byte** ptr) /*!< in/out: pointer to log data */
1295+{
1296+ char* buf_end = buf;
1297+ ulint z_offset;
1298+ ulint offset = mach_read_from_2(*ptr);
1299+ *ptr += 2;
1300+ z_offset = mach_read_from_2(*ptr);
1301+ *ptr += 2;
1302+ buf_end += snprintf(buf_end,
1303+ RECV_SCAN_SIZE - (buf_end - buf),
1304+ "offset: %lu, zip_offset: %lu, "
1305+ "%s:",
1306+ offset, z_offset,
1307+ ib_log_sys_get_zip_op_name(itr->current_rec.type));
1308+ ib_log_sys_output_raw(ptr, *ptr + ib_log_sys_get_zip_op_ptr_len(
1309+ itr->current_rec.type), &buf_end);
1310+ itr->current_rec.body_str = strdup(buf);
1311+}
1312+
1313+/*********************************************************************//**
1314+Parse log record body */
1315+static
1316+void
1317+ib_log_sys_parse_rec_body(
1318+/*======================*/
1319+ ib_log_rec_itr_t itr, /*!< in/out: log iterator */
1320+ byte* body_start, /*!< in: pointer to start of
1321+ a log record body */
1322+ byte* body_end) /*!< in: pointer to end of a log
1323+ record body */
1324+{
1325+ byte* ptr = body_start;
1326+
1327+ ut_a (ib_log_sys_rec_has_body(&itr->current_rec) || ptr == NULL
1328+ || body_start == body_end);
1329+ if (ptr == NULL)
1330+ ptr = body_end;
1331+
1332+ buf[0] = '\0';
1333+
1334+ if (itr->current_rec.body_str) {
1335+
1336+ free (itr->current_rec.body_str);
1337+ itr->current_rec.body_str = NULL;
1338+ }
1339+
1340+ switch (itr->current_rec.type) {
1341+ case MLOG_1BYTE:
1342+ case MLOG_2BYTES:
1343+ case MLOG_4BYTES:
1344+ {
1345+ ulint offset = mach_read_from_2(ptr);
1346+ ulint val;
1347+ int ret;
1348+ ptr += 2;
1349+ ptr = mach_parse_compressed(ptr, body_end, &val);
1350+ ret = asprintf(&itr->current_rec.body_str,
1351+ "offset: %lu, value: 0x%lX", offset,
1352+ val);
1353+ ut_a (ret > 0);
1354+ break;
1355+ }
1356+ case MLOG_8BYTES:
1357+ {
1358+ ulint offset = mach_read_from_2(ptr);
1359+ dulint val;
1360+ int ret;
1361+ ptr += 2;
1362+ ptr = mach_dulint_parse_compressed(ptr, body_end,
1363+ &val);
1364+ ret = asprintf(&itr->current_rec.body_str,
1365+ "offset: %lu, value: 0x%llX", offset,
1366+ ut_conv_dulint_to_longlong(val));
1367+ ut_a (ret > 0);
1368+ break;
1369+ }
1370+ case MLOG_REC_INSERT:
1371+ ib_log_sys_parse_rec_insert(itr, &ptr, body_end);
1372+ break;
1373+ case MLOG_COMP_REC_INSERT:
1374+ ib_log_sys_parse_index(&ptr);
1375+ ib_log_sys_parse_rec_insert(itr, &ptr, body_end);
1376+ break;
1377+ case MLOG_COMP_PAGE_REORGANIZE:
1378+ ib_log_sys_parse_index(&ptr);
1379+ itr->current_rec.body_str = strdup(buf);
1380+ break;
1381+ case MLOG_COMP_REC_CLUST_DELETE_MARK:
1382+ {
1383+ char* buf_end = ib_log_sys_parse_index(&ptr);
1384+ *buf_end++ = ',';
1385+ *buf_end++ = ' ';
1386+ ib_log_sys_parse_del_mark_set_clust_rec(itr, &ptr,
1387+ body_end);
1388+ break;
1389+ }
1390+ case MLOG_REC_SEC_DELETE_MARK:
1391+ {
1392+ int ret;
1393+ ulint offset;
1394+ ulint val = mach_read_from_1(ptr);
1395+ ptr++;
1396+ offset = mach_read_from_2(ptr);
1397+ ptr += 2;
1398+ ret = asprintf(&itr->current_rec.body_str,
1399+ "value: %lu, offset: %lu", val, offset);
1400+ ut_a(ret > 0);
1401+ break;
1402+ }
1403+ case MLOG_REC_CLUST_DELETE_MARK:
1404+ ib_log_sys_parse_del_mark_set_clust_rec(itr, &ptr, body_end);
1405+ break;
1406+ case MLOG_REC_UPDATE_IN_PLACE:
1407+ ib_log_sys_parse_update_in_place(itr, &ptr, body_end);
1408+ break;
1409+ case MLOG_COMP_REC_UPDATE_IN_PLACE:
1410+ {
1411+ char* buf_end = ib_log_sys_parse_index(&ptr);
1412+ *buf_end++ = ',';
1413+ *buf_end++ = ' ';
1414+ ib_log_sys_parse_update_in_place(itr, &ptr, body_end);
1415+ break;
1416+ }
1417+ case MLOG_LIST_END_DELETE:
1418+ case MLOG_LIST_START_DELETE:
1419+ /* TODO: test */
1420+ ib_log_sys_parse_delete_rec_list(itr, &ptr);
1421+ break;
1422+ case MLOG_COMP_LIST_END_DELETE:
1423+ case MLOG_COMP_LIST_START_DELETE:
1424+ {
1425+ char* buf_end = ib_log_sys_parse_index(&ptr);
1426+ *buf_end++ = ','; *buf_end++ = ' '; *buf_end++ = '\0';
1427+ ib_log_sys_parse_delete_rec_list(itr, &ptr);
1428+ break;
1429+ }
1430+ case MLOG_LIST_END_COPY_CREATED:
1431+ /* TODO: test */
1432+ ib_log_sys_parse_end_list_copy(itr, &ptr, body_end);
1433+ break;
1434+ case MLOG_COMP_LIST_END_COPY_CREATED:
1435+ {
1436+ char* buf_end = ib_log_sys_parse_index(&ptr);
1437+ *buf_end++ = ',';
1438+ *buf_end++ = ' ';
1439+ ib_log_sys_parse_end_list_copy(itr, &ptr, body_end);
1440+ break;
1441+ }
1442+ case MLOG_UNDO_INSERT:
1443+ {
1444+ char* buf_end = buf;
1445+ ulint data_len = mach_read_from_2(ptr);
1446+ ptr += 2;
1447+ buf_end += snprintf(buf_end,
1448+ RECV_SCAN_SIZE - (buf_end - buf),
1449+ "data_len: %lu, data:", data_len);
1450+ ib_log_sys_output_raw(&ptr, ptr + data_len, &buf_end);
1451+ itr->current_rec.body_str = strdup(buf);
1452+ break;
1453+ }
1454+ case MLOG_UNDO_INIT:
1455+ {
1456+ int ret;
1457+ ulint undo_log_seg_type;
1458+ ptr = mach_parse_compressed(ptr, body_end,
1459+ &undo_log_seg_type);
1460+ ret = asprintf(&itr->current_rec.body_str,
1461+ "undo_log_seg_type: %lu",
1462+ undo_log_seg_type);;
1463+ ut_a (ret > 0);
1464+ break;
1465+ }
1466+ case MLOG_UNDO_HDR_CREATE:
1467+ case MLOG_UNDO_HDR_REUSE:
1468+ {
1469+ int ret;
1470+ trx_id_t trx_id;
1471+ /* TODO: test _REUSE */
1472+ ptr = mach_dulint_parse_compressed(ptr, body_end,
1473+ &trx_id);
1474+ ret = asprintf(&itr->current_rec.body_str,
1475+ "trx_id: 0x%llX",
1476+ ut_conv_dulint_to_longlong(trx_id));
1477+ ut_a (ret > 0);
1478+ break;
1479+ }
1480+ case MLOG_REC_MIN_MARK:
1481+ case MLOG_COMP_REC_MIN_MARK:
1482+ {
1483+ /* TODO: test MLOG_REC_MIN_MARK */
1484+ int ret;
1485+ ulint min_rec_mark_offset = mach_read_from_2(ptr);
1486+ ptr += 2;
1487+ ret = asprintf(&itr->current_rec.body_str,
1488+ "min_rec_mark_offset: %lu",
1489+ min_rec_mark_offset);
1490+ ut_a (ret > 0);
1491+ break;
1492+ }
1493+ case MLOG_REC_DELETE:
1494+ ib_log_sys_parse_delete_rec(itr, &ptr);
1495+ break;
1496+ case MLOG_COMP_REC_DELETE:
1497+ {
1498+ char* buf_end = ib_log_sys_parse_index(&ptr);
1499+ *buf_end++ = ',';
1500+ *buf_end++ = ' ';
1501+ ib_log_sys_parse_delete_rec(itr, &ptr);
1502+ break;
1503+ }
1504+ case MLOG_WRITE_STRING:
1505+ {
1506+ ulint data_len;
1507+ char* buf_end = buf;
1508+ ulint offset = mach_read_from_2(ptr);
1509+ ptr += 2;
1510+ data_len = mach_read_from_2(ptr);
1511+ ptr += 2;
1512+ buf_end += snprintf(buf_end,
1513+ RECV_SCAN_SIZE - (buf_end - buf),
1514+ "offset: %lu, data_len: %lu, "
1515+ "data:", offset, data_len);
1516+ ib_log_sys_output_raw(&ptr, ptr + data_len, &buf_end);
1517+ itr->current_rec.body_str = strdup(buf);
1518+ break;
1519+ }
1520+ case MLOG_FILE_CREATE2:
1521+ {
1522+ /* TODO: test */
1523+ char* buf_end = buf;
1524+ ulint flags = mach_read_from_4(ptr);
1525+ ptr += 4;
1526+ buf_end += snprintf(buf_end,
1527+ RECV_SCAN_SIZE - (buf_end - buf),
1528+ "flags: 0x%lX:", flags);
1529+ *buf_end++ = ',';
1530+ *buf_end++ = ' ';
1531+ ib_log_sys_parse_fil_op(itr, &ptr, &buf_end);
1532+ break;
1533+ }
1534+ case MLOG_FILE_CREATE:
1535+ case MLOG_FILE_RENAME:
1536+ case MLOG_FILE_DELETE:
1537+ {
1538+ char* buf_end = buf;
1539+ ib_log_sys_parse_fil_op(itr, &ptr, &buf_end);
1540+ break;
1541+ }
1542+ case MLOG_ZIP_WRITE_NODE_PTR:
1543+ case MLOG_ZIP_WRITE_BLOB_PTR:
1544+ /* TODO: test */
1545+ ib_log_sys_parse_zip_op(itr, &ptr);
1546+ break;
1547+ case MLOG_ZIP_WRITE_HEADER:
1548+ {
1549+ /* TODO: test */
1550+ ulint offset = *ptr++;
1551+ ulint data_len = *ptr++;
1552+ char* buf_end = buf;
1553+ buf_end += snprintf(buf_end,
1554+ RECV_SCAN_SIZE - (buf_end - buf),
1555+ "offset: %lu, data_len: %lu, "
1556+ "data:", offset, data_len);
1557+ ib_log_sys_output_raw(&ptr, ptr + data_len, &buf_end);
1558+ itr->current_rec.body_str = strdup(buf);
1559+ break;
1560+ }
1561+ case MLOG_ZIP_PAGE_COMPRESS:
1562+ {
1563+ /* TODO: test */
1564+ char* buf_end = buf;
1565+ ulint trailer_size;
1566+ ulint size = mach_read_from_2(ptr);
1567+ ptr += 2;
1568+ trailer_size = mach_read_from_2(ptr);
1569+ ptr += 2;
1570+ buf_end += snprintf(buf_end,
1571+ RECV_SCAN_SIZE - (buf_end - buf),
1572+ "data_size: %lu, "
1573+ "trailer_size: %lu, page_prev:",
1574+ size, trailer_size);
1575+ ib_log_sys_output_raw(&ptr, ptr + 4, &buf_end);
1576+ ptr += 4;
1577+ buf_end += snprintf(buf_end,
1578+ RECV_SCAN_SIZE - (buf_end - buf),
1579+ ", page_next:");
1580+ ib_log_sys_output_raw(&ptr, ptr + 4, &buf_end);
1581+ ptr += 4;
1582+ buf_end += snprintf(buf_end,
1583+ RECV_SCAN_SIZE - (buf_end - buf),
1584+ ", data:");
1585+ ib_log_sys_output_raw(&ptr, ptr + size, &buf_end);
1586+ ptr += size;
1587+ buf_end += snprintf(buf_end,
1588+ RECV_SCAN_SIZE - (buf_end - buf),
1589+ ", trailer_data:");
1590+ ib_log_sys_output_raw(&ptr, ptr + trailer_size,
1591+ &buf_end);
1592+ ptr += trailer_size;
1593+ itr->current_rec.body_str = strdup(buf);
1594+ break;
1595+ }
1596+ case MLOG_INIT_FILE_PAGE:
1597+ case MLOG_IBUF_BITMAP_INIT:
1598+ case MLOG_UNDO_HDR_DISCARD: /* TODO: test */
1599+ case MLOG_PAGE_REORGANIZE: /* TODO: test */
1600+ case MLOG_PAGE_CREATE:
1601+ case MLOG_COMP_PAGE_CREATE:
1602+ case MLOG_UNDO_ERASE_END:
1603+ case MLOG_MULTI_REC_END:
1604+ case MLOG_DUMMY_RECORD:
1605+ case MLOG_LSN:
1606+ /* No body */
1607+ break;
1608+ default: ;
1609+ ib_logger(ib_stream, "Corrupt log rec at LSN = %llu, ",
1610+ itr->current_rec.lsn);
1611+ ut_a(0);
1612+ }
1613+ ut_a (ptr == body_end);
1614+}
1615+
1616+/*********************************************************************//**
1617+Get next log record */
1618+HAILDB_API
1619+ib_log_rec_t
1620+ib_log_sys_get_next_rec(
1621+/*====================*/
1622+ ib_log_rec_itr_t itr) /*!< in: log iterator */
1623+{
1624+ ulint len;
1625+ byte type;
1626+ ulint space;
1627+ ulint page_no;
1628+ byte* body;
1629+
1630+ /* Already reached the end of requested LSN range? */
1631+ if (itr->next_parse_lsn >= itr->end_lsn)
1632+ return NULL;
1633+
1634+ if (itr->parse_buf_ptr == itr->parse_buf_end) {
1635+
1636+ /* Parse buf empty */
1637+ if (!fill_parse_buffer(itr))
1638+ return NULL;
1639+ }
1640+
1641+ /* Have some data in the parse buf, parse it */
1642+ recv_sys->found_corrupt_log = FALSE;
1643+ len = recv_parse_log_rec(itr->parse_buf_ptr, itr->parse_buf_end, &type,
1644+ &space, &page_no, &body);
1645+ if (recv_sys->found_corrupt_log) {
1646+
1647+ /* Uh-oh, corrupt log. Now what? */
1648+ ib_logger(ib_stream, "Corrupt log at LSN = %llu, "
1649+ "proceeding in 1 byte increments, "
1650+ "no correct parse guarantee and InnoDB may crash!\n",
1651+ itr->next_parse_lsn);
1652+ itr->parse_buf_ptr++;
1653+ itr->next_parse_lsn
1654+ = recv_calc_lsn_on_data_add(itr->next_parse_lsn, 1);
1655+ return ib_log_sys_get_next_rec(itr);
1656+ }
1657+ if (len > 0) {
1658+
1659+ /* Full rec, see if its LSN is inside the requested range */
1660+ if (itr->next_parse_lsn >= itr->start_lsn) {
1661+
1662+ itr->current_rec.single_rec
1663+ = (*itr->parse_buf_ptr & MLOG_SINGLE_REC_FLAG)
1664+ != 0;
1665+ itr->current_rec.lsn = itr->next_parse_lsn;
1666+ itr->current_rec.len = len;
1667+ itr->current_rec.type = type;
1668+
1669+ if (log_online_rec_has_page(type)) {
1670+
1671+ itr->current_rec.space = space;
1672+ itr->current_rec.page_no = page_no;
1673+ }
1674+ else {
1675+
1676+ itr->current_rec.space = ULINT_UNDEFINED;
1677+ itr->current_rec.page_no = ULINT_UNDEFINED;
1678+ }
1679+
1680+ itr->parse_buf_ptr += len;
1681+ ib_log_sys_parse_rec_body(itr, body,
1682+ itr->parse_buf_ptr);
1683+
1684+ itr->next_parse_lsn
1685+ = recv_calc_lsn_on_data_add(
1686+ itr->next_parse_lsn, len);
1687+
1688+ return &itr->current_rec;
1689+ }
1690+ else if (itr->next_parse_lsn < itr->start_lsn) {
1691+
1692+ /* Full rec but below the requested LSN range. Eat it,
1693+ try the next one. */
1694+ itr->parse_buf_ptr += len;
1695+ itr->next_parse_lsn
1696+ = recv_calc_lsn_on_data_add(
1697+ itr->next_parse_lsn, len);
1698+ return ib_log_sys_get_next_rec(itr);
1699+ }
1700+ else {
1701+
1702+ /* Rec past the requested LSN range, EOF */
1703+ ut_a (itr->next_parse_lsn > itr->end_lsn);
1704+ return NULL;
1705+ }
1706+ }
1707+
1708+ /* Incomplete rec. Shift it to the beginning of the parse buffer, get
1709+ more data and parse it again. */
1710+ ut_memmove(itr->parse_buf, itr->parse_buf_ptr,
1711+ itr->parse_buf_end - itr->parse_buf_ptr);
1712+ itr->parse_buf_end = itr->parse_buf
1713+ + (itr->parse_buf_end - itr->parse_buf_ptr);
1714+ itr->parse_buf_ptr = itr->parse_buf;
1715+
1716+ if (!fill_parse_buffer(itr)) {
1717+
1718+ ib_logger(ib_stream, "Incomplete record at LSN = %llu\n",
1719+ itr->next_parse_lsn);
1720+ return NULL;
1721+ }
1722+ return ib_log_sys_get_next_rec(itr);
1723+}
1724+
1725+/*********************************************************************//**
1726+Get LSN of record */
1727+HAILDB_API
1728+ib_u64_t
1729+ib_log_sys_get_rec_lsn(
1730+/*===================*/
1731+ const ib_log_rec_t log_rec) /*!< in: log record */
1732+{
1733+ return log_rec->lsn;
1734+}
1735+
1736+/*********************************************************************//**
1737+Get length of record */
1738+HAILDB_API
1739+ib_ulint_t
1740+ib_log_sys_get_rec_len(
1741+/*===================*/
1742+ const ib_log_rec_t log_rec) /*!< in: log record */
1743+{
1744+ return log_rec->len;
1745+}
1746+
1747+/*********************************************************************//**
1748+Get log record type (MLOG_*) */
1749+HAILDB_API
1750+ib_byte_t
1751+ib_log_sys_get_rec_type(
1752+/*====================*/
1753+ const ib_log_rec_t log_rec) /*!< in: log record */
1754+{
1755+ return log_rec->type;
1756+}
1757+
1758+/*********************************************************************//**
1759+@return TRUE if MLOG_SINGLE_REC_FLAG is set for log record */
1760+HAILDB_API
1761+ib_bool_t
1762+ib_log_sys_get_single_rec_flag(
1763+/*===========================*/
1764+ const ib_log_rec_t log_rec) /*!< in: log record */
1765+{
1766+ return log_rec->single_rec != 0;
1767+}
1768+
1769+/*********************************************************************//**
1770+@return space id associated with log record or ULINT_UNDEFINED if
1771+record doesn't have space id associated */
1772+HAILDB_API
1773+ib_ulint_t
1774+ib_log_sys_get_rec_space(
1775+/*=====================*/
1776+ const ib_log_rec_t log_rec) /*!< in: log record */
1777+{
1778+ if (!log_online_rec_has_page(log_rec->type))
1779+ ut_a (log_rec->space == ULINT_UNDEFINED);
1780+ return log_rec->space;
1781+}
1782+
1783+/*********************************************************************//**
1784+@return page id associated with log record or ULINT_UNDEFINED if
1785+record doesn't have page id associated */
1786+HAILDB_API
1787+ib_ulint_t
1788+ib_log_sys_get_rec_page_no(
1789+/*=======================*/
1790+ const ib_log_rec_t log_rec) /*!< in: log record */
1791+{
1792+ if (!log_online_rec_has_page(log_rec->type))
1793+ ut_a (log_rec->page_no == ULINT_UNDEFINED);
1794+ return log_rec->page_no;
1795+}
1796+
1797+/*********************************************************************//**
1798+Release log iterator */
1799+HAILDB_API
1800+void
1801+ib_log_sys_free_iterator(
1802+/*=====================*/
1803+ ib_log_rec_itr_t itr) /*!< in: log iterator */
1804+{
1805+ mem_free(itr->read_buf);
1806+ mem_free(itr->parse_buf);
1807+ mem_free(itr);
1808+}
1809+
1810+/*********************************************************************//**
1811+@return page id associated with log record or ULINT_UNDEFINED if
1812+record doesn't have page id associated */
1813+HAILDB_API
1814+const char *
1815+ib_log_sys_get_rec_name(
1816+/*====================*/
1817+ const ib_log_rec_t log_rec) /*!< in: log record */
1818+{
1819+ byte type = ib_log_sys_get_rec_type(log_rec);
1820+ if (type < sizeof(mlog_type_names) / sizeof(char*))
1821+ return mlog_type_names[type];
1822+ return "undefined";
1823+}
1824+
1825+/*********************************************************************//**
1826+@return TRUE if is log record about an .ibd file operation */
1827+HAILDB_API
1828+ib_bool_t
1829+ib_log_sys_rec_has_mlog_file_flag(
1830+/*==============================*/
1831+ const ib_log_rec_t log_rec) /*!< in: log record */
1832+{
1833+ return log_rec->type == MLOG_FILE_CREATE
1834+ || log_rec->type == MLOG_FILE_RENAME
1835+ || log_rec->type == MLOG_FILE_DELETE
1836+ || log_rec->type == MLOG_FILE_CREATE2;
1837+}
1838+
1839+/*********************************************************************//**
1840+@return TRUE if MLOG_FILE_FLAG_TEMP is set */
1841+HAILDB_API
1842+ib_bool_t
1843+ib_log_sys_get_mlog_file_flag_temp(
1844+/*===============================*/
1845+ const ib_log_rec_t log_rec) /*!< in: log record */
1846+{
1847+ ut_ad (ib_log_sys_rec_has_mlog_file_flag(log_rec));
1848+ return ib_log_sys_get_rec_page_no(log_rec) & MLOG_FILE_FLAG_TEMP;
1849+}
1850+
1851+/*********************************************************************//**
1852+@return TRUE if record of type MLOG_LSN (LSN pseudo-record) */
1853+HAILDB_API
1854+ib_bool_t
1855+ib_log_sys_is_mlog_lsn_rec(
1856+/*=======================*/
1857+ const ib_log_rec_t log_rec) /*!< in: log record */
1858+{
1859+ return log_rec->type == MLOG_LSN;
1860+}
1861+
1862+/*********************************************************************//**
1863+@return LSN from lsn pseudo-record */
1864+HAILDB_API
1865+ib_u64_t
1866+ib_log_sys_get_mlog_lsn(
1867+/*====================*/
1868+ const ib_log_rec_t log_rec) /*!< in: log record */
1869+{
1870+ ut_a (ib_log_sys_is_mlog_lsn_rec(log_rec));
1871+ return (ib_u64_t)log_rec->space << 32 | log_rec->page_no;
1872+}
1873+
1874+/*********************************************************************//**
1875+@return TRUE if log record has body */
1876+HAILDB_API
1877+ib_bool_t
1878+ib_log_sys_rec_has_body(
1879+/*====================*/
1880+ const ib_log_rec_t log_rec) /*!< in: log record */
1881+{
1882+ return log_rec->type != MLOG_MULTI_REC_END
1883+ && log_rec->type != MLOG_DUMMY_RECORD
1884+ && log_rec->type != MLOG_LSN
1885+ && log_rec->type != MLOG_PAGE_REORGANIZE
1886+ && log_rec->type != MLOG_PAGE_CREATE
1887+ && log_rec->type != MLOG_COMP_PAGE_CREATE
1888+ && log_rec->type != MLOG_UNDO_ERASE_END
1889+ && log_rec->type != MLOG_UNDO_HDR_DISCARD
1890+ && log_rec->type != MLOG_IBUF_BITMAP_INIT
1891+ && log_rec->type != MLOG_INIT_FILE_PAGE;
1892+}
1893+
1894+/*********************************************************************//**
1895+@return log record's body as string */
1896+HAILDB_API
1897+const char*
1898+ib_log_sys_get_rec_body_str(
1899+/*========================*/
1900+ const ib_log_rec_t log_rec) /*!< in: log record */
1901+{
1902+ ut_ad (ib_log_sys_rec_has_body(log_rec));
1903+ return log_rec->body_str;
1904+}
1905+
1906+/*********************************************************************//**
1907+Print log records in human-readable format */
1908+ibool
1909+ib_log_sys_print(
1910+/*=============*/
1911+ ib_uint64_t start_lsn, /*!< in: parse start LSN.
1912+ 0 for START_LSN_LAST_CP */
1913+ ib_uint64_t end_lsn, /*!< in: parse end LSN.
1914+ IB_ULONGLONG_MAX for log to be
1915+ parsed to the end */
1916+ ulint filter_space, /*!< in: Space ID to filter
1917+ records on or ULINT_UNDEFINED */
1918+ ulint filter_page, /*!< in: Page ID to filter
1919+ records on or ULINT_UNDEFINED */
1920+ ibool numeric, /*!< in: TRUE for output log
1921+ item type byte values instead of
1922+ mnemonics */
1923+ ibool print_rec_bodies) /*!< in: TRUE for print log
1924+ record body contents */
1925+{
1926+ ib_err_t ib_err;
1927+
1928+ if (start_lsn == 0) {
1929+ ib_logger(ib_stream, "Reading last checkpoint LSN\n");
1930+ ib_err = ib_log_sys_find_last_checkpoint_lsn(&start_lsn);
1931+ if (ib_err != DB_SUCCESS) {
1932+ ib_logger(ib_stream,
1933+ "ib_log_sys_find_last_checkpoint_lsn: %d\n",
1934+ ib_err);
1935+ return FALSE;
1936+ }
1937+ }
1938+
1939+ ib_logger(ib_stream, "Reading InnoDB redo logs "
1940+ "from LSN %llu to %llu\n",
1941+ start_lsn, end_lsn);
1942+
1943+ ib_log_rec_itr_t log_rec_iterator =
1944+ ib_log_sys_create_iterator(start_lsn, end_lsn);
1945+ ib_log_rec_t log_rec;
1946+
1947+ while ((log_rec = ib_log_sys_get_next_rec(log_rec_iterator)) != NULL) {
1948+
1949+ ib_ulint_t space = ib_log_sys_get_rec_space(log_rec);
1950+ ib_ulint_t page = ib_log_sys_get_rec_page_no(log_rec);
1951+
1952+ if ((filter_space != ULINT_UNDEFINED && space != filter_space)
1953+ || (filter_page != ULINT_UNDEFINED
1954+ && page != filter_page))
1955+ continue;
1956+
1957+ ib_logger(ib_stream, "{LSN: %llu, len: %lu, type: ",
1958+ ib_log_sys_get_rec_lsn(log_rec),
1959+ ib_log_sys_get_rec_len(log_rec));
1960+ if (numeric) {
1961+ ib_logger(ib_stream,
1962+ "%d", ib_log_sys_get_rec_type(log_rec));
1963+ } else {
1964+ ib_logger(ib_stream,
1965+ "%s", ib_log_sys_get_rec_name(log_rec));
1966+ }
1967+ ib_logger(ib_stream, ", singlerec: %d",
1968+ (ib_log_sys_get_single_rec_flag(log_rec) ? 1 : 0));
1969+
1970+ if (ib_log_sys_rec_has_mlog_file_flag(log_rec)) {
1971+ ib_logger(ib_stream, ", MLOG_FILE_FLAG_TEMP: %d",
1972+ (ib_log_sys_get_mlog_file_flag_temp(log_rec) ?
1973+ 1 : 0));
1974+ } else if (ib_log_sys_is_mlog_lsn_rec(log_rec)) {
1975+ ib_logger(ib_stream, ", mloglsn: %llu",
1976+ ib_log_sys_get_mlog_lsn(log_rec));
1977+ } else if (space != ULINT_UNDEFINED) {
1978+ ib_logger(ib_stream, ", space: %lu, page: %lu",
1979+ space, page);
1980+ } else {
1981+ assert(page == ULINT_UNDEFINED);
1982+ }
1983+
1984+ if (print_rec_bodies && ib_log_sys_rec_has_body(log_rec))
1985+ {
1986+ ib_logger(ib_stream, ", %s",
1987+ ib_log_sys_get_rec_body_str(log_rec));
1988+ }
1989+ ib_logger(ib_stream, "}\n");
1990+ }
1991+ ib_log_sys_free_iterator(log_rec_iterator);
1992+ return TRUE;
1993+}
1994
1995=== added file 'src/api_log.h'
1996--- src/api_log.h 1970-01-01 00:00:00 +0000
1997+++ src/api_log.h 2012-10-15 14:16:23 +0000
1998@@ -0,0 +1,213 @@
1999+/*
2000+ * Copyright (C) 2012 Percona Inc.
2001+ * This program is free software: you can redistribute it and/or modify it
2002+ * under the terms of the GNU General Public License version 3, as published
2003+ * by the Free Software Foundation.
2004+ *
2005+ * This program is distributed in the hope that it will be useful, but
2006+ * WITHOUT ANY WARRANTY; without even the implied warranties of
2007+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2008+ * PURPOSE. See the GNU General Public License for more details.
2009+ *
2010+ * You should have received a copy of the GNU General Public License along
2011+ * with this program. If not, see <http://www.gnu.org/licenses/>.
2012+ */
2013+
2014+#ifndef api_log_h
2015+#define api_log_h
2016+
2017+#include "univ.i"
2018+#include "innodb_int.h"
2019+
2020+#ifdef __cplusplus
2021+extern "C" {
2022+#endif /* __cplusplus */
2023+
2024+#define HAILDB_API UNIV_INTERN
2025+
2026+typedef struct ib_log_rec_itr_struct* ib_log_rec_itr_t;
2027+
2028+typedef struct ib_log_rec_struct* ib_log_rec_t;
2029+
2030+/*********************************************************************//**
2031+Initialize InnoDB systems needed for the log printer
2032+@return DB_SUCCESS if successfull */
2033+HAILDB_API
2034+ib_err_t
2035+ib_log_sys_start(
2036+/*=============*/
2037+ ib_ulint_t log_buf_size, /*!< in: the size of the buffer */
2038+ const char* log_dir); /*!< in: path to InnoDB log files. */
2039+
2040+/*********************************************************************//**
2041+Uninitialize InnoDB systems needed for the log printer */
2042+HAILDB_API
2043+void
2044+ib_log_sys_stop(void);
2045+
2046+/*********************************************************************//**
2047+Find last consistent checkpoint LSN in log groups */
2048+HAILDB_API
2049+ib_err_t
2050+ib_log_sys_find_last_checkpoint_lsn(
2051+/*================================*/
2052+ ib_u64_t* last_checkpoint_lsn); /*!< out: last checkpoint LSN */
2053+
2054+/*********************************************************************//**
2055+Create iterator through log records */
2056+HAILDB_API
2057+ib_log_rec_itr_t
2058+ib_log_sys_create_iterator(
2059+/*=======================*/
2060+ ib_u64_t start_lsn, /*!< in: starting LSN */
2061+ ib_u64_t end_lsn); /*!< in: last LSN */
2062+
2063+/*********************************************************************//**
2064+Get next log record */
2065+HAILDB_API
2066+ib_log_rec_t
2067+ib_log_sys_get_next_rec(
2068+/*====================*/
2069+ ib_log_rec_itr_t itr); /*!< in: log iterator */
2070+
2071+/*********************************************************************//**
2072+Release log iterator */
2073+HAILDB_API
2074+void
2075+ib_log_sys_free_iterator(
2076+/*=====================*/
2077+ ib_log_rec_itr_t itr); /*!< in: log iterator */
2078+
2079+/*********************************************************************//**
2080+Get LSN of record */
2081+HAILDB_API
2082+ib_u64_t
2083+ib_log_sys_get_rec_lsn(
2084+/*===================*/
2085+ const ib_log_rec_t log_rec); /*!< in: log record */
2086+
2087+/*********************************************************************//**
2088+Get length of record */
2089+HAILDB_API
2090+ib_ulint_t
2091+ib_log_sys_get_rec_len(
2092+/*===================*/
2093+ const ib_log_rec_t log_rec); /*!< in: log record */
2094+
2095+/*********************************************************************//**
2096+Get log record type (MLOG_*) */
2097+HAILDB_API
2098+ib_byte_t
2099+ib_log_sys_get_rec_type(
2100+/*====================*/
2101+ const ib_log_rec_t log_rec); /*!< in: log record */
2102+
2103+
2104+/*********************************************************************//**
2105+@return TRUE if MLOG_SINGLE_REC_FLAG is set for log record */
2106+HAILDB_API
2107+ib_bool_t
2108+ib_log_sys_get_single_rec_flag(
2109+/*===========================*/
2110+ const ib_log_rec_t log_rec); /*!< in: log record */
2111+
2112+/*********************************************************************//**
2113+@return space id associated with log record or ULINT_UNDEFINED if
2114+record doesn't have space id associated */
2115+HAILDB_API
2116+ib_ulint_t
2117+ib_log_sys_get_rec_space(
2118+/*=====================*/
2119+ const ib_log_rec_t log_rec); /*!< in: log record */
2120+
2121+/*********************************************************************//**
2122+@return page id associated with log record or ULINT_UNDEFINED if
2123+record doesn't have page id associated */
2124+HAILDB_API
2125+ib_ulint_t
2126+ib_log_sys_get_rec_page_no(
2127+/*=======================*/
2128+ const ib_log_rec_t log_rec); /*!< in: log record */
2129+
2130+/*********************************************************************//**
2131+@return page id associated with log record or ULINT_UNDEFINED if
2132+record doesn't have page id associated */
2133+HAILDB_API
2134+const char *
2135+ib_log_sys_get_rec_name(
2136+/*====================*/
2137+ const ib_log_rec_t log_rec); /*!< in: log record */
2138+
2139+/*********************************************************************//**
2140+@return TRUE if is log record about an .ibd file operation */
2141+HAILDB_API
2142+ib_bool_t
2143+ib_log_sys_rec_has_mlog_file_flag(
2144+/*==============================*/
2145+ const ib_log_rec_t log_rec); /*!< in: log record */
2146+
2147+/*********************************************************************//**
2148+@return TRUE if MLOG_FILE_FLAG_TEMP is set */
2149+HAILDB_API
2150+ib_bool_t
2151+ib_log_sys_get_mlog_file_flag_temp(
2152+/*===============================*/
2153+ const ib_log_rec_t log_rec); /*!< in: log record */
2154+
2155+/*********************************************************************//**
2156+@return TRUE if record of type MLOG_LSN (LSN pseudo-record) */
2157+HAILDB_API
2158+ib_bool_t
2159+ib_log_sys_is_mlog_lsn_rec(
2160+/*=======================*/
2161+ const ib_log_rec_t log_rec); /*!< in: log record */
2162+
2163+/*********************************************************************//**
2164+@return LSN from lsn pseudo-record */
2165+HAILDB_API
2166+ib_u64_t
2167+ib_log_sys_get_mlog_lsn(
2168+/*====================*/
2169+ const ib_log_rec_t log_rec); /*!< in: log record */
2170+
2171+/*********************************************************************//**
2172+@return TRUE if log record has body */
2173+HAILDB_API
2174+ib_bool_t
2175+ib_log_sys_rec_has_body(
2176+/*====================*/
2177+ const ib_log_rec_t log_rec); /*!< in: log record */
2178+
2179+/*********************************************************************//**
2180+@return log record's body as string */
2181+HAILDB_API
2182+const char*
2183+ib_log_sys_get_rec_body_str(
2184+/*========================*/
2185+ const ib_log_rec_t log_rec); /*!< in: log record */
2186+
2187+/*********************************************************************//**
2188+Print log records in human-readable format */
2189+ibool
2190+ib_log_sys_print(
2191+/*=============*/
2192+ ib_uint64_t start_lsn, /*!< in: parse start LSN.
2193+ 0 for START_LSN_LAST_CP */
2194+ ib_uint64_t end_lsn, /*!< in: parse end LSN.
2195+ IB_ULONGLONG_MAX for log to be
2196+ parsed to the end */
2197+ ulint filter_space, /*!< in: Space ID to filter
2198+ records on or ULINT_UNDEFINED */
2199+ ulint filter_page, /*!< in: Page ID to filter
2200+ records on or ULINT_UNDEFINED */
2201+ ibool numeric, /*!< in: TRUE for output log
2202+ item type byte values instead of
2203+ mnemonics */
2204+ ibool print_rec_bodies); /*!< in: TRUE for print log
2205+ record body contents */
2206+
2207+#ifdef __cplusplus
2208+} /* extern "C" */
2209+#endif /* __cplusplus */
2210+
2211+#endif
2212
2213=== added file 'src/api_page.c'
2214--- src/api_page.c 1970-01-01 00:00:00 +0000
2215+++ src/api_page.c 2012-10-15 14:16:23 +0000
2216@@ -0,0 +1,3522 @@
2217+/*
2218+ * Copyright (C) 2012 Percona Inc.
2219+ * This program is free software: you can redistribute it and/or modify it
2220+ * under the terms of the GNU General Public License version 3, as published
2221+ * by the Free Software Foundation.
2222+ *
2223+ * This program is distributed in the hope that it will be useful, but
2224+ * WITHOUT ANY WARRANTY; without even the implied warranties of
2225+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2226+ * PURPOSE. See the GNU General Public License for more details.
2227+ *
2228+ * You should have received a copy of the GNU General Public License along
2229+ * with this program. If not, see <http://www.gnu.org/licenses/>.
2230+ */
2231+
2232+#define DULINT_STANDARD
2233+
2234+#include <fil0fil.h>
2235+#include <log0log.h>
2236+#include <log0recv.h>
2237+#include <rem0rec.h>
2238+#include <os0file.h>
2239+#include <os0sync.h>
2240+#include <sync0sync.h>
2241+#include <srv0srv.h>
2242+#include <fsp0fsp.h>
2243+#include <buf0buf.h>
2244+#include <trx0undo.h>
2245+#include <btr0btr.h>
2246+#include <page0page.h>
2247+#include <page0zip.h>
2248+#include <lock0lock.h>
2249+#include <btr0pcur.h>
2250+#include <ibuf0ibuf.h>
2251+#include <dict0load.h>
2252+#include <dict0boot.h>
2253+#include <trx0rec.h>
2254+#include <row0upd.h>
2255+#include <row0row.h>
2256+#include <fsp0fsp.h>
2257+#include <data0type.h>
2258+#include <dict0boot.h>
2259+#include <srv0start.h>
2260+#include "api_page.h"
2261+#include "innodb_int.h"
2262+
2263+#ifndef DATA_VARCLIENT
2264+#define DATA_VARCLIENT DATA_VARMYSQL
2265+#define DATA_CLIENT DATA_MYSQL
2266+#endif
2267+
2268+#define SRV_N_PENDING_IOS_PER_THREAD OS_AIO_N_PENDING_IOS_PER_THREAD
2269+#define SRV_MAX_N_PENDING_SYNC_IOS 100
2270+
2271+#ifndef IBUF_PAGE_SIZE_PER_FREE_SPACE
2272+
2273+/** An index page must contain at least UNIV_PAGE_SIZE /
2274+IBUF_PAGE_SIZE_PER_FREE_SPACE bytes of free space for ibuf to try to
2275+buffer inserts to this page. If there is this much of free space, the
2276+corresponding bits are set in the ibuf bitmap. */
2277+#define IBUF_PAGE_SIZE_PER_FREE_SPACE 32
2278+
2279+/** Insert buffer struct */
2280+struct ibuf_struct{
2281+ ulint size; /*!< current size of the ibuf index
2282+ tree, in pages */
2283+ ulint max_size; /*!< recommended maximum size of the
2284+ ibuf index tree, in pages */
2285+ ulint seg_size; /*!< allocated pages of the file
2286+ segment containing ibuf header and
2287+ tree */
2288+ ibool empty; /*!< after an insert to the ibuf tree
2289+ is performed, this is set to FALSE,
2290+ and if a contract operation finds
2291+ the tree empty, this is set to
2292+ TRUE */
2293+ ulint free_list_len; /*!< length of the free list */
2294+ ulint height; /*!< tree height */
2295+ dict_index_t* index; /*!< insert buffer index */
2296+
2297+ ulint n_inserts; /*!< number of inserts made to
2298+ the insert buffer */
2299+ ulint n_merges; /*!< number of pages merged */
2300+ ulint n_merged_recs; /*!< number of records merged */
2301+};
2302+
2303+#endif
2304+
2305+/***************************************************************//**
2306+Print undo log page. */
2307+UNIV_INTERN
2308+void
2309+ib_page_print_undo(
2310+/*===============*/
2311+ const page_t* page); /*!< in: page */
2312+
2313+/***************************************************************//**
2314+Determine direction name.
2315+@return direction name. */
2316+UNIV_INTERN
2317+const char*
2318+ib_page_page_cursor_direction(
2319+/*==========================*/
2320+ ulint direction); /*!< in: direction */
2321+
2322+/***************************************************************//**
2323+Dump raw field as hex and ascii. */
2324+UNIV_INTERN
2325+void
2326+ib_page_print_field_hex_asc(
2327+/*========================*/
2328+ const rec_t* rec, /*!< in: physical record */
2329+ ulint i); /*!< in: field number */
2330+
2331+/***************************************************************//**
2332+Format externally stored field. */
2333+UNIV_INTERN
2334+void
2335+ib_page_format_ext(
2336+/*===============*/
2337+ char* buf, /*!< out: buffer */
2338+ ulint buf_len, /*!< in: buffer length */
2339+ const rec_t* rec, /*!< in: physical record */
2340+ ulint no, /*!< in: field number */
2341+ const ulint* offsets); /*!< in: array returned by
2342+ rec_get_offsets() */
2343+
2344+/***************************************************************//**
2345+Hex dump of page. */
2346+UNIV_INTERN
2347+void
2348+ib_page_print_hex_dump(
2349+/*===================*/
2350+ const page_t* page, /*!< in: page */
2351+ ulint zip_size); /*!< in: compressed page size, or
2352+ 0 for uncompressed pages */
2353+
2354+/***************************************************************//**
2355+Print page dictionary header. */
2356+UNIV_INTERN
2357+void
2358+ib_page_print_dict_header(
2359+/*======================*/
2360+ const page_t* page); /*!< in: page */
2361+
2362+/***************************************************************//**
2363+@return XDES state as string. */
2364+UNIV_INTERN
2365+const char*
2366+ib_page_xdes_state_str(
2367+/*===================*/
2368+ ulint state); /*!< in: XDES state */
2369+
2370+/***************************************************************//**
2371+Print XDES page. */
2372+UNIV_INTERN
2373+void
2374+ib_page_print_xdes(
2375+/*===============*/
2376+ const page_t* page, /*!< in: page */
2377+ ulint zip_size); /*!< in: compressed page size, or
2378+ 0 for uncompressed pages */
2379+
2380+/***************************************************************//**
2381+Create IBUF index in data dictionary. */
2382+UNIV_INTERN
2383+void
2384+ib_page_ibuf_index_create(void);
2385+/*============================*/
2386+
2387+/***************************************************************//**
2388+Load a table object based on the index id.
2389+@return table; NULL if table does not exist */
2390+UNIV_INTERN
2391+dict_table_t*
2392+ib_page_load_table_for_index(
2393+/*=========================*/
2394+ dulint index_id); /*!< in: index id */
2395+
2396+/***************************************************************//**
2397+Allocate signle aligned page.
2398+@return page */
2399+UNIV_INTERN
2400+page_t*
2401+ib_page_page_alloc();
2402+
2403+/***************************************************************//**
2404+Allocate signle aligned page.
2405+@return page */
2406+UNIV_INTERN
2407+page_t*
2408+ib_page_heap_page_alloc(
2409+/*====================*/
2410+ mem_heap_t* heap); /*!< in: memory heap where allocate in */
2411+
2412+/***************************************************************//**
2413+Get space id of given tablespace in file.
2414+@return space id if given tablespace file */
2415+UNIV_INTERN
2416+ulint
2417+ib_page_file_space_id(
2418+/*==================*/
2419+ os_file_t file); /*!< in: file */
2420+
2421+/***************************************************************//**
2422+Calculate the low 32 bits and the high 32 bits
2423+of the file offset. */
2424+UNIV_INTERN
2425+void
2426+ib_page_calc_offsets(
2427+/*=================*/
2428+ ulint zip_size, /*!< in: compressed page size, or
2429+ 0 for uncompressed pages */
2430+ ulint page_no, /*!< in: page number */
2431+ ulint* offset_low, /*!< out: low 32 bits */
2432+ ulint* offset_high); /*!< out: high 32 bits */
2433+
2434+/***************************************************************//**
2435+Load compressed page into memory.
2436+Space for page allocated in heap.
2437+@return loaded page; NULL if page was not loaded */
2438+UNIV_INTERN
2439+page_t*
2440+ib_page_load_compressed(
2441+/*====================*/
2442+ os_file_t file, /*!< in: file */
2443+ ulint page_no, /*!< in: page number */
2444+ ulint zip_size, /*!< in: compressed page size, or
2445+ 0 for uncompressed pages */
2446+ mem_heap_t* heap); /*!< in: memory heap */
2447+
2448+/***************************************************************//**
2449+Decompress compressed page.
2450+Space for decompressed page allocated in heap.
2451+@return loaded page; NULL if page was not loaded */
2452+UNIV_INTERN
2453+page_t*
2454+ib_page_decompress(
2455+/*===============*/
2456+ const page_t* compressed_page,/*!< in: compressed page */
2457+ ulint zip_size, /*!< in: compressed page size, or
2458+ 0 for uncompressed pages */
2459+ mem_heap_t* heap); /*!< in: memory heap */
2460+
2461+/***************************************************************//**
2462+Test if page if IBUF page.
2463+@return TRUE if page is IBUF page; FALE otherwise */
2464+UNIV_INTERN
2465+ibool
2466+ib_page_page_is_ibuf(
2467+/*=================*/
2468+ const page_t* page); /*!< in: page */
2469+
2470+/***************************************************************//**
2471+Test if page is UNDO INSERT page.
2472+@return TRUE if page is UNDO INSERT page; FALE otherwise */
2473+UNIV_INTERN
2474+ibool
2475+ib_page_is_undo_insert(
2476+/*===================*/
2477+ const page_t* page); /*!< in: page */
2478+
2479+/***************************************************************//**
2480+Test if page is UNDO UPDATE page.
2481+@return TRUE if page is UNDO UPDATE page; FALE otherwise */
2482+UNIV_INTERN
2483+ibool
2484+ib_page_is_undo_update(
2485+/*===================*/
2486+ const page_t* page); /*!< in: page */
2487+
2488+/***************************************************************//**
2489+Test if page is UNDO page.
2490+@return TRUE if page is UNDO page; FALE otherwise */
2491+UNIV_INTERN
2492+ibool
2493+ib_page_is_undo(
2494+/*============*/
2495+ const page_t* page); /*!< in: page */
2496+
2497+/***************************************************************//**
2498+Print file-based list address. */
2499+UNIV_INTERN
2500+void
2501+ib_page_print_flst_addr(
2502+/*====================*/
2503+ const byte* base); /*!< in: pointer to base node of list */
2504+
2505+/***************************************************************//**
2506+Print file-based list. */
2507+UNIV_INTERN
2508+void
2509+ib_page_print_flst(
2510+/*===============*/
2511+ const byte* base); /*!< in: pointer to base node of list */
2512+
2513+/***************************************************************//**
2514+Print filespace header page. */
2515+UNIV_INTERN
2516+void
2517+ib_page_print_fsp_header(
2518+/*=====================*/
2519+ const page_t* page); /*!< in: page */
2520+
2521+/***************************************************************//**
2522+Print ZBLOB page. */
2523+UNIV_INTERN
2524+void
2525+ib_page_print_zblob(
2526+/*================*/
2527+ const page_t* page, /*!< in: page */
2528+ ulint zip_size); /*!< in: compressed page size, or
2529+ 0 for uncompressed pages */
2530+
2531+/***************************************************************//**
2532+Print ZBLOB2 page. */
2533+UNIV_INTERN
2534+void
2535+ib_page_print_zblob2(
2536+/*=================*/
2537+ const page_t* page, /*!< in: page */
2538+ ulint zip_size); /*!< in: compressed page size, or
2539+ 0 for uncompressed pages */
2540+
2541+/***************************************************************//**
2542+Print BLOB page. */
2543+UNIV_INTERN
2544+void
2545+ib_page_print_blob(
2546+/*===============*/
2547+ const page_t* page); /*!< in: page */
2548+
2549+/***************************************************************//**
2550+Print inode page. */
2551+UNIV_INTERN
2552+void
2553+ib_page_print_inode(
2554+/*================*/
2555+ const page_t* page, /*!< in: page */
2556+ ulint zip_size); /*!< in: compressed page size, or
2557+ 0 for uncompressed pages */
2558+
2559+/***************************************************************//**
2560+Print a physical record. */
2561+UNIV_INTERN
2562+void
2563+ib_page_rec_print_new(
2564+/*==================*/
2565+ ib_stream_t ib_stream, /*!< in: stream where to print */
2566+ const page_t* page, /*!< in: page */
2567+ const rec_t* rec, /*!< in: physical record */
2568+ const ulint* offsets, /*!< in: array returned by
2569+ rec_get_offsets() */
2570+ dict_index_t* index); /*!< in: index */
2571+
2572+
2573+/***************************************************************//**
2574+Print an old-style physical record. */
2575+UNIV_INTERN
2576+void
2577+ib_page_rec_print_old(
2578+/*==================*/
2579+ ib_stream_t ib_stream, /*!< in: stream where to print */
2580+ const page_t* page,
2581+ const rec_t* rec, /*!< in: physical record */
2582+ const ulint* offsets, /*!< in: array returned by
2583+ rec_get_offsets() */
2584+ dict_index_t* index); /*!< in: index */
2585+
2586+/***************************************************************//**
2587+Prints a physical record in ROW_FORMAT=COMPACT. Ignores the
2588+record header. */
2589+UNIV_INTERN
2590+void
2591+ib_page_rec_print_comp(
2592+/*===================*/
2593+ ib_stream_t ib_stream, /*!< in: streamwhere to print */
2594+ const page_t* page,
2595+ const rec_t* rec, /*!< in: physical record */
2596+ const ulint* offsets, /*!< in: array returned by
2597+ rec_get_offsets() */
2598+ dict_index_t* index); /*!< in: index */
2599+
2600+/*******************************************************************//**
2601+Copy types of fields contained in index to tuple. */
2602+UNIV_INTERN
2603+void
2604+ib_page_dict_index_copy_types(
2605+/*==========================*/
2606+ dtuple_t* tuple, /*!< in/out: data tuple */
2607+ const dict_index_t* index, /*!< in: index */
2608+ ulint n_fields, /*!< in: number of
2609+ field types to copy */
2610+ ibool is_leaf_page); /*!< in: TRUE if leas page */
2611+
2612+/*******************************************************************//**
2613+Converts an index record to a typed data tuple.
2614+@return index entry built; does not set info_bits, and the data fields
2615+in the entry will point directly to rec */
2616+UNIV_INTERN
2617+dtuple_t*
2618+ib_page_row_rec_to_index_entry_low(
2619+/*===============================*/
2620+ const page_t* page,
2621+ const rec_t* rec, /*!< in: record in the index */
2622+ const dict_index_t* index, /*!< in: index */
2623+ const ulint* offsets,/*!< in: rec_get_offsets(rec, index) */
2624+ ulint* n_ext, /*!< out: number of externally
2625+ stored columns */
2626+ mem_heap_t* heap); /*!< in: memory heap from which
2627+ the memory needed is allocated */
2628+
2629+/*******************************************************************//**
2630+Converts an index record to a typed data tuple.
2631+@return index entry built; does not set info_bits, and the data fields
2632+in the entry will point directly to rec */
2633+UNIV_INTERN
2634+dtuple_t*
2635+ib_page_row_rec_to_index_entry_low(
2636+/*===============================*/
2637+ const page_t* page,
2638+ const rec_t* rec, /*!< in: record in the index */
2639+ const dict_index_t* index, /*!< in: index */
2640+ const ulint* offsets,/*!< in: rec_get_offsets(rec, index) */
2641+ ulint* n_ext, /*!< out: number of externally
2642+ stored columns */
2643+ mem_heap_t* heap); /*!< in: memory heap from which
2644+ the memory needed is allocated */
2645+
2646+
2647+/*******************************************************************//**
2648+Converts an index record to a typed data tuple. NOTE that externally
2649+stored (often big) fields are NOT copied to heap.
2650+@return own: index entry built; see the NOTE below! */
2651+UNIV_INTERN
2652+dtuple_t*
2653+ib_page_row_rec_to_index_entry(
2654+/*===========================*/
2655+ ulint type, /*!< in: ROW_COPY_DATA, or
2656+ ROW_COPY_POINTERS: the former
2657+ copies also the data fields to
2658+ heap as the latter only places
2659+ pointers to data fields on the
2660+ index page */
2661+ const page_t* page,
2662+ const rec_t* rec, /*!< in: record in the index;
2663+ NOTE: in the case
2664+ ROW_COPY_POINTERS the data
2665+ fields in the row will point
2666+ directly into this record,
2667+ therefore, the buffer page of
2668+ this record must be at least
2669+ s-latched and the latch held
2670+ as long as the dtuple is used! */
2671+ const dict_index_t* index, /*!< in: index */
2672+ ulint* offsets,/*!< in/out: rec_get_offsets(rec) */
2673+ ulint* n_ext, /*!< out: number of externally
2674+ stored columns */
2675+ mem_heap_t* heap); /*!< in: memory heap from which
2676+ the memory needed is allocated */
2677+
2678+/*******************************************************************//**
2679+Load page from file into memory and decompress it if needed.
2680+@return TRUE on success */
2681+ibool
2682+ib_page_load(
2683+/*=========*/
2684+ const char* ibd_file_name, /*!< in: file name */
2685+ ulint page_no, /*!< in: page number */
2686+ ulint* zip_size, /*!< in: compressed page size, or
2687+ 0 for uncompressed pages */
2688+ page_t** compressed_page, /*!< out: compressed page */
2689+ page_t** decompressed_page, /*!< out: decompressed page */
2690+ mem_heap_t* heap); /*!< out: memory heap */
2691+
2692+/*******************************************************************//**
2693+Print IBUF bitmap page. */
2694+UNIV_INTERN
2695+void
2696+ib_page_ibuf_bitmap_print(
2697+/*======================*/
2698+ const page_t* page, /*!< in: page */
2699+ ulint zip_size); /*!< in: compressed page size, or
2700+ 0 for uncompressed pages */
2701+
2702+/*******************************************************************//**
2703+Format SYS value. */
2704+UNIV_INTERN
2705+void
2706+ib_page_format_sys(
2707+/*===============*/
2708+ ulint prtype, /*!< in: precise type */
2709+ char* buf, /*!< out: buffer */
2710+ ulint buf_len, /*!< in: buffer length */
2711+ const byte* data); /*!< in: field data */
2712+
2713+/*******************************************************************//**
2714+Format INT value. */
2715+UNIV_INTERN
2716+void
2717+ib_page_format_int(
2718+/*===============*/
2719+ ulint prtype, /*!< in: precise type */
2720+ char* buf, /*!< out: buffer */
2721+ ulint buf_len, /*!< in: buffer length */
2722+ const byte* data, /*!< in: field data */
2723+ ulint data_len); /*!< in: length of data */
2724+
2725+/*******************************************************************//**
2726+Format string value. */
2727+UNIV_INTERN
2728+void
2729+ib_page_format_str(
2730+/*===============*/
2731+ char* buf, /*!< out: buffer */
2732+ ulint buf_len, /*!< in: buffer length */
2733+ const byte* data, /*!< in: field data */
2734+ ulint data_len); /*!< in: length of data */
2735+
2736+/*******************************************************************//**
2737+Format binalry value. */
2738+UNIV_INTERN
2739+void
2740+ib_page_format_bin(
2741+/*===============*/
2742+ char* buf, /*!< out: buffer */
2743+ ulint buf_len, /*!< in: buffer length */
2744+ const byte* data, /*!< in: field data */
2745+ ulint data_len); /*!< in: length of data */
2746+
2747+/*******************************************************************//**
2748+Print field. */
2749+UNIV_INTERN
2750+void
2751+ib_page_rec_print_field(
2752+/*====================*/
2753+ const dfield_t* field, /*!< in: field */
2754+ const rec_t* rec, /*!< in: physical record */
2755+ ulint no, /*!< in: field number */
2756+ const ulint* offsets); /*!< in: array returned by
2757+ rec_get_offsets() */
2758+/********************************************************************//**
2759+Print first page header. */
2760+UNIV_INTERN
2761+void
2762+ib_buf_page_print_header(
2763+/*=====================*/
2764+ const byte* read_buf, /*!< in: a database page */
2765+ ulint zip_size); /*!< in: compressed page size, or
2766+ 0 for uncompressed pages */
2767+
2768+/***************************************************************//**
2769+Print second page header. */
2770+UNIV_INTERN
2771+void
2772+ib_page_page_header_print(
2773+/*======================*/
2774+ const page_t* page); /*!< in: page */
2775+
2776+/***************************************************************//**
2777+Print the contents of the directory. */
2778+UNIV_INTERN
2779+void
2780+ib_page_page_dir_print(
2781+/*===================*/
2782+ const page_t* page); /*!< in: index page */
2783+
2784+/************************************************************//**
2785+Print record contents including the data relevant only in
2786+the index page context. */
2787+UNIV_INTERN
2788+void
2789+ib_page_page_rec_print(
2790+/*===================*/
2791+ const page_t* page,
2792+ const rec_t* rec, /*!< in: physical record */
2793+ const ulint* offsets,/*!< in: record descriptor */
2794+ dict_index_t* index); /*!< in: dictionary index of the page */
2795+
2796+/***************************************************************//**
2797+This is used to print the contents of the page record list for
2798+debugging purposes. */
2799+UNIV_INTERN
2800+void
2801+ib_page_page_print_list(
2802+/*====================*/
2803+ const page_t* page, /*!< in: index page */
2804+ dict_index_t* index); /*!< in: dictionary index of the page */
2805+
2806+
2807+/** @name Offsets to the per-page bits in the insert buffer bitmap */
2808+/* @{ */
2809+#define IBUF_BITMAP_FREE 0 /*!< Bits indicating the
2810+ amount of free space */
2811+#define IBUF_BITMAP_BUFFERED 2 /*!< TRUE if there are buffered
2812+ changes for the page */
2813+#define IBUF_BITMAP_IBUF 3 /*!< TRUE if page is a part of
2814+ the ibuf tree, excluding the
2815+ root page, or is in the free
2816+ list of the ibuf */
2817+/* @} */
2818+
2819+/* Various constants for checking the type of an ibuf record and extracting
2820+data from it. For details, see the description of the record format at the
2821+top of this file. */
2822+
2823+/** @name Format of the fourth column of an insert buffer record
2824+The fourth column in the MySQL 5.5 format contains an operation
2825+type, counter, and some flags. */
2826+/* @{ */
2827+#define IBUF_REC_INFO_SIZE 4 /*!< Combined size of info fields at
2828+ the beginning of the fourth field */
2829+#if IBUF_REC_INFO_SIZE >= DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE
2830+# error "IBUF_REC_INFO_SIZE >= DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE"
2831+#endif
2832+
2833+/* Offsets for the fields at the beginning of the fourth field */
2834+#define IBUF_REC_OFFSET_COUNTER 0 /*!< Operation counter */
2835+#define IBUF_REC_OFFSET_TYPE 2 /*!< Type of operation */
2836+#define IBUF_REC_OFFSET_FLAGS 3 /*!< Additional flags */
2837+
2838+/* Record flag masks */
2839+#define IBUF_REC_COMPACT 0x1 /*!< Set in
2840+ IBUF_REC_OFFSET_FLAGS if the
2841+ user index is in COMPACT
2842+ format or later */
2843+
2844+
2845+/** The area in pages from which contract looks for page numbers for merge */
2846+#define IBUF_MERGE_AREA 8
2847+
2848+/** Inside the merge area, pages which have at most 1 per this number less
2849+buffered entries compared to maximum volume that can buffered for a single
2850+page are merged along with the page whose buffer became full */
2851+#define IBUF_MERGE_THRESHOLD 4
2852+
2853+/** In ibuf_contract at most this number of pages is read to memory in one
2854+batch, in order to merge the entries for them in the insert buffer */
2855+#define IBUF_MAX_N_PAGES_MERGED IBUF_MERGE_AREA
2856+
2857+/** If the combined size of the ibuf trees exceeds ibuf->max_size by this
2858+many pages, we start to contract it in connection to inserts there, using
2859+non-synchronous contract */
2860+#define IBUF_CONTRACT_ON_INSERT_NON_SYNC 0
2861+
2862+/** If the combined size of the ibuf trees exceeds ibuf->max_size by this
2863+many pages, we start to contract it in connection to inserts there, using
2864+synchronous contract */
2865+#define IBUF_CONTRACT_ON_INSERT_SYNC 5
2866+
2867+/** If the combined size of the ibuf trees exceeds ibuf->max_size by
2868+this many pages, we start to contract it synchronous contract, but do
2869+not insert */
2870+#define IBUF_CONTRACT_DO_NOT_INSERT 10
2871+
2872+
2873+/* Possible operations buffered in the insert/whatever buffer. See
2874+ibuf_insert(). DO NOT CHANGE THE VALUES OF THESE, THEY ARE STORED ON DISK. */
2875+typedef enum {
2876+ IBUF_OP_INSERT = 0,
2877+ IBUF_OP_DELETE_MARK = 1,
2878+ IBUF_OP_DELETE = 2,
2879+
2880+ /* Number of different operation types. */
2881+ IBUF_OP_COUNT = 3
2882+} ibuf_op_t;
2883+
2884+/***************************************************************//**
2885+Load a index object based on the index id.
2886+@return index; NULL if table does not exist */
2887+dict_index_t*
2888+ib_dict_index_get_on_id(
2889+/*====================*/
2890+ dulint index_id); /*!< in: index id */
2891+
2892+/****************************************************************//**
2893+Read the first two bytes from a record's fourth field (counter field in new
2894+records; something else in older records).
2895+@return "counter" field, or ULINT_UNDEFINED if for some reason it
2896+can't be read */
2897+UNIV_INTERN
2898+ulint
2899+ib_page_ibuf_rec_get_counter(
2900+/*=========================*/
2901+ const rec_t* rec); /*!< in: ibuf record */
2902+
2903+
2904+/*******************************************************************//**
2905+Initialize necessary InndoDB systems for page printer
2906+to work properly. */
2907+ib_err_t
2908+ib_page_sys_start(
2909+/*==============*/
2910+ ulint buf_pool_size, /*!< in: size of buffer pool */
2911+ const char* datadir) /*!< in: InnoDB data directory */
2912+{
2913+ ulint err;
2914+
2915+ innodb_init_param_for_util(datadir, datadir,
2916+ buf_pool_size, 16*1024*1024, TRUE);
2917+
2918+ srv_max_n_threads = 1;
2919+ srv_normalize_init_values();
2920+
2921+ ut_mem_init();
2922+ os_sync_init();
2923+ sync_init();
2924+ srv_n_read_io_threads = srv_n_write_io_threads = 1;
2925+ srv_n_file_io_threads = 2 + srv_n_read_io_threads +
2926+ srv_n_write_io_threads;
2927+ // os_io_init_simple();
2928+ os_aio_init(8 * SRV_N_PENDING_IOS_PER_THREAD,
2929+ srv_n_read_io_threads,
2930+ srv_n_write_io_threads,
2931+ SRV_MAX_N_PENDING_SYNC_IOS);
2932+ mem_init(1);
2933+
2934+ kernel_mutex_temp = mem_alloc(sizeof(mutex_t));
2935+ mutex_create(&kernel_mutex, SYNC_KERNEL);
2936+
2937+ fil_init(10, 10);
2938+ buf_pool_init();
2939+ fsp_init();
2940+ lock_sys_create(1000);
2941+ recv_sys_create();
2942+
2943+ ibool create_new_db;
2944+ ib_uint64_t min_flushed_lsn, max_flushed_lsn;
2945+ ulint sum_of_new_sizes;
2946+
2947+ err = open_or_create_data_files(&create_new_db,
2948+ &min_flushed_lsn, &max_flushed_lsn,
2949+ &sum_of_new_sizes);
2950+
2951+ if (err != DB_SUCCESS) {
2952+ return err;
2953+ }
2954+
2955+ /* open_or_create_log_file will set srv_log_file_size. Normalize the
2956+ init values again to get srv_log_file_size/srv_log_file_curr_size in
2957+ sync. */
2958+ srv_normalize_init_values();
2959+
2960+ dict_ind_init();
2961+ dict_boot(FALSE);
2962+ mutex_enter(&(dict_sys->mutex));
2963+ ib_page_ibuf_index_create();
2964+ mutex_exit(&(dict_sys->mutex));
2965+ dict_check_tablespaces_and_store_max_id(FALSE);
2966+
2967+ return DB_SUCCESS;
2968+}
2969+
2970+/*******************************************************************//**
2971+Uninitialize InndoDB systems been initialized by ib_page_sys_start. */
2972+void
2973+ib_page_sys_stop(void)
2974+/*==================*/
2975+{
2976+ /* This must be disabled before closing the buffer pool
2977+ and closing the data dictionary. */
2978+ btr_search_disable();
2979+
2980+ recv_sys_close();
2981+ recv_sys_mem_free();
2982+ fil_close_all_files();
2983+ dict_close();
2984+ // buf_close();
2985+
2986+ mutex_free(&kernel_mutex);
2987+ mem_free(kernel_mutex_temp);
2988+ kernel_mutex_temp = NULL;
2989+
2990+ sync_close();
2991+ os_sync_free();
2992+ fil_close();
2993+
2994+ buf_pool_free();
2995+ ut_free_all_mem();
2996+}
2997+
2998+#define ASSERT_MTR_READONLY(mtr) ut_a(!(mtr.modifications && mtr.n_log_recs));
2999+
3000+/***************************************************************//**
3001+Load a table object based on the index id.
3002+@return table; NULL if table does not exist */
3003+UNIV_INTERN
3004+dict_table_t*
3005+ib_page_load_table_for_index(
3006+/*=========================*/
3007+ dulint index_id) /*!< in: index id */
3008+{
3009+ dict_table_t* sys_tables_table;
3010+ dict_index_t* sys_tables_index;
3011+
3012+ mtr_t mtr;
3013+ btr_pcur_t pcur;
3014+ dict_table_t* result = NULL;
3015+
3016+ sys_tables_table = dict_table_get_low("SYS_INDEXES");
3017+ sys_tables_index = UT_LIST_GET_FIRST(sys_tables_table->indexes);
3018+
3019+ mtr_start(&mtr);
3020+
3021+ btr_pcur_open_at_index_side(TRUE, sys_tables_index,
3022+ BTR_SEARCH_LEAF, &pcur,
3023+ TRUE, &mtr);
3024+
3025+ for (;;)
3026+ {
3027+ rec_t* rec;
3028+ ulint len;
3029+ byte* field;
3030+ dulint loc_index_id;
3031+ dulint loc_table_id;
3032+
3033+ btr_pcur_move_to_next_user_rec(&pcur, &mtr);
3034+ rec = btr_pcur_get_rec(&pcur);
3035+
3036+ if (!btr_pcur_is_on_user_rec(&pcur))
3037+ {
3038+ /* end of index */
3039+
3040+ break;
3041+ }
3042+
3043+ if (rec_get_deleted_flag(rec, 0))
3044+ continue;
3045+
3046+ field = rec_get_nth_field_old(rec, 1, &len);
3047+ loc_index_id = mach_read_from_8(field);
3048+
3049+ if (ut_dulint_cmp(loc_index_id, index_id) == 0) {
3050+ field = rec_get_nth_field_old(rec, 0, &len);
3051+ loc_table_id = mach_read_from_8(field);
3052+
3053+ btr_pcur_close(&pcur);
3054+
3055+ ASSERT_MTR_READONLY(mtr);
3056+ mtr_commit(&mtr);
3057+
3058+ return dict_table_get_on_id_low(loc_table_id);
3059+ }
3060+
3061+ }
3062+
3063+ btr_pcur_close(&pcur);
3064+
3065+ ASSERT_MTR_READONLY(mtr);
3066+ mtr_commit(&mtr);
3067+
3068+ return(result);
3069+}
3070+
3071+/** Table name for the insert buffer. */
3072+#define IBUF_TABLE_NAME "SYS_IBUF_TABLE"
3073+
3074+/***************************************************************//**
3075+Create IBUF index in data dictionary. */
3076+UNIV_INTERN
3077+void
3078+ib_page_ibuf_index_create(void)
3079+/*===========================*/
3080+{
3081+ dict_table_t* table;
3082+ mem_heap_t* heap;
3083+ dict_index_t* index;
3084+ ulint error;
3085+
3086+ ibuf = mem_alloc(sizeof(ibuf_t));
3087+
3088+ memset(ibuf, 0, sizeof(*ibuf));
3089+
3090+ heap = mem_heap_create(450);
3091+
3092+ /* Use old-style record format for the insert buffer. */
3093+ table = dict_mem_table_create(IBUF_TABLE_NAME, IBUF_SPACE_ID, 1, 0);
3094+
3095+ dict_mem_table_add_col(table, heap, "DUMMY_COLUMN", DATA_BINARY, 0, 0);
3096+
3097+ table->id = ut_dulint_add(DICT_IBUF_ID_MIN, IBUF_SPACE_ID);
3098+
3099+ dict_table_add_to_cache(table, heap);
3100+ mem_heap_free(heap);
3101+
3102+ index = dict_mem_index_create(
3103+ IBUF_TABLE_NAME, "CLUST_IND",
3104+ IBUF_SPACE_ID, DICT_CLUSTERED | DICT_UNIVERSAL | DICT_IBUF, 1);
3105+
3106+ dict_mem_index_add_field(index, "DUMMY_COLUMN", 0);
3107+
3108+ index->id = ut_dulint_add(DICT_IBUF_ID_MIN, IBUF_SPACE_ID);
3109+
3110+ error = dict_index_add_to_cache(table, index,
3111+ FSP_IBUF_TREE_ROOT_PAGE_NO, FALSE);
3112+ ut_a(error == DB_SUCCESS);
3113+
3114+ ibuf->index = dict_table_get_first_index(table);
3115+
3116+}
3117+
3118+/***************************************************************//**
3119+Load a index object based on the index id.
3120+@return index; NULL if table does not exist */
3121+dict_index_t*
3122+ib_dict_index_get_on_id(
3123+/*====================*/
3124+ dulint index_id) /*!< in: index id */
3125+{
3126+ dict_table_t* table;
3127+ dict_index_t* index = NULL;
3128+
3129+ const char* SYS_TABLES[] = {"SYS_TABLES", "SYS_INDEXES",
3130+ "SYS_COLUMNS", "SYS_FIELDS",
3131+ IBUF_TABLE_NAME, NULL};
3132+
3133+ mutex_enter(&(dict_sys->mutex));
3134+ for (const char** ptbl_name = SYS_TABLES; *ptbl_name; ptbl_name++)
3135+ {
3136+ table = dict_table_get_low(*ptbl_name);
3137+ if (table != NULL) {
3138+ index = dict_index_get_on_id_low(table, index_id);
3139+ if (index != NULL) {
3140+ mutex_exit(&(dict_sys->mutex));
3141+ return(index);
3142+ }
3143+ }
3144+ }
3145+
3146+ table = ib_page_load_table_for_index(index_id);
3147+ if (table != NULL) {
3148+ index = dict_index_get_on_id_low(table, index_id);
3149+ mutex_exit(&(dict_sys->mutex));
3150+ return index;
3151+ }
3152+
3153+ mutex_exit(&(dict_sys->mutex));
3154+
3155+ return(NULL);
3156+}
3157+
3158+/********************************************************************//**
3159+Returns the page number field of an ibuf record.
3160+@return page number */
3161+static
3162+ulint
3163+ib_page_ibuf_rec_get_page_no(
3164+/*=========================*/
3165+ const rec_t* rec) /*!< in: ibuf record */
3166+{
3167+ const byte* field;
3168+ ulint len;
3169+
3170+ ut_ad(rec_get_n_fields_old(rec) > 2);
3171+
3172+ field = rec_get_nth_field_old(rec, 1, &len);
3173+
3174+ if (len == 1) {
3175+ /* This is of the >= 4.1.x record format */
3176+
3177+ field = rec_get_nth_field_old(rec, 2, &len);
3178+ } else {
3179+
3180+ field = rec_get_nth_field_old(rec, 0, &len);
3181+ }
3182+
3183+ ut_a(len == 4);
3184+
3185+ return(mach_read_from_4(field));
3186+}
3187+
3188+/********************************************************************//**
3189+Returns the space id field of an ibuf record. For < 4.1.x format records
3190+returns 0.
3191+@return space id */
3192+static
3193+ulint
3194+ib_page_ibuf_rec_get_space(
3195+/*=======================*/
3196+ const rec_t* rec) /*!< in: ibuf record */
3197+{
3198+ const byte* field;
3199+ ulint len;
3200+
3201+ field = rec_get_nth_field_old(rec, 1, &len);
3202+
3203+ if (len == 1) {
3204+ /* This is of the >= 4.1.x record format */
3205+
3206+ field = rec_get_nth_field_old(rec, 0, &len);
3207+ ut_a(len == 4);
3208+
3209+ return(mach_read_from_4(field));
3210+ }
3211+
3212+ return(0);
3213+}
3214+
3215+/****************************************************************//**
3216+Get various information about an ibuf record in >= 4.1.x format. */
3217+static
3218+void
3219+ib_page_ibuf_rec_get_info(
3220+/*======================*/
3221+ const rec_t* rec, /*!< in: ibuf record */
3222+ ibuf_op_t* op, /*!< out: operation type, or NULL */
3223+ ibool* comp, /*!< out: compact flag, or NULL */
3224+ ulint* info_len, /*!< out: length of info fields at the
3225+ start of the fourth field, or
3226+ NULL */
3227+ ulint* counter) /*!< in: counter value, or NULL */
3228+{
3229+ const byte* types;
3230+ ulint fields;
3231+ ulint len;
3232+
3233+ /* Local variables to shadow arguments. */
3234+ ibuf_op_t op_local;
3235+ ibool comp_local;
3236+ ulint info_len_local;
3237+ ulint counter_local;
3238+
3239+ fields = rec_get_n_fields_old(rec);
3240+ ut_a(fields > 4);
3241+
3242+ types = rec_get_nth_field_old(rec, 3, &len);
3243+
3244+ info_len_local = len % DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE;
3245+
3246+ switch (info_len_local) {
3247+ case 0:
3248+ case 1:
3249+ op_local = IBUF_OP_INSERT;
3250+ comp_local = info_len_local;
3251+ ut_ad(!counter);
3252+ counter_local = ULINT_UNDEFINED;
3253+ break;
3254+
3255+ case IBUF_REC_INFO_SIZE:
3256+ op_local = (ibuf_op_t)types[IBUF_REC_OFFSET_TYPE];
3257+ comp_local = types[IBUF_REC_OFFSET_FLAGS] & IBUF_REC_COMPACT;
3258+ counter_local = mach_read_from_2(
3259+ types + IBUF_REC_OFFSET_COUNTER);
3260+ break;
3261+
3262+ default:
3263+ ut_error;
3264+ }
3265+
3266+ ut_a(op_local < IBUF_OP_COUNT);
3267+ ut_a((len - info_len_local) ==
3268+ (fields - 4) * DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE);
3269+
3270+ if (op) {
3271+ *op = op_local;
3272+ }
3273+
3274+ if (comp) {
3275+ *comp = comp_local;
3276+ }
3277+
3278+ if (info_len) {
3279+ *info_len = info_len_local;
3280+ }
3281+
3282+ if (counter) {
3283+ *counter = counter_local;
3284+ }
3285+}
3286+
3287+/****************************************************************//**
3288+Returns the operation type field of an ibuf record.
3289+@return operation type */
3290+static
3291+ibuf_op_t
3292+ib_page_ibuf_rec_get_op_type(
3293+/*=========================*/
3294+ const rec_t* rec) /*!< in: ibuf record */
3295+{
3296+ ulint len;
3297+
3298+ (void) rec_get_nth_field_old(rec, 1, &len);
3299+
3300+ if (len > 1) {
3301+ /* This is a < 4.1.x format record */
3302+
3303+ return(IBUF_OP_INSERT);
3304+ } else {
3305+ ibuf_op_t op;
3306+
3307+ ib_page_ibuf_rec_get_info(rec, &op, NULL, NULL, NULL);
3308+
3309+ return(op);
3310+ }
3311+}
3312+
3313+/****************************************************************//**
3314+Read the first two bytes from a record's fourth field (counter field in new
3315+records; something else in older records).
3316+@return "counter" field, or ULINT_UNDEFINED if for some reason it
3317+can't be read */
3318+UNIV_INTERN
3319+ulint
3320+ib_page_ibuf_rec_get_counter(
3321+/*=========================*/
3322+ const rec_t* rec) /*!< in: ibuf record */
3323+{
3324+ const byte* ptr;
3325+ ulint len;
3326+
3327+ if (rec_get_n_fields_old(rec) < 4) {
3328+
3329+ return(ULINT_UNDEFINED);
3330+ }
3331+
3332+ ptr = rec_get_nth_field_old(rec, 3, &len);
3333+
3334+ if (len >= 2) {
3335+
3336+ return(mach_read_from_2(ptr));
3337+ } else {
3338+
3339+ return(ULINT_UNDEFINED);
3340+ }
3341+}
3342+
3343+/********************************************************************//**
3344+Creates a dummy index for inserting a record to a non-clustered index.
3345+@return dummy index */
3346+static
3347+dict_index_t*
3348+ib_page_ibuf_dummy_index_create(
3349+/*============================*/
3350+ ulint n, /*!< in: number of fields */
3351+ ibool comp) /*!< in: TRUE=use compact record format */
3352+{
3353+ dict_table_t* table;
3354+ dict_index_t* index;
3355+
3356+ table = dict_mem_table_create("IBUF_DUMMY",
3357+ DICT_HDR_SPACE, n,
3358+ comp ? DICT_TF_COMPACT : 0);
3359+
3360+ index = dict_mem_index_create("IBUF_DUMMY", "IBUF_DUMMY",
3361+ DICT_HDR_SPACE, 0, n);
3362+
3363+ index->table = table;
3364+
3365+ /* avoid ut_ad(index->cached) in dict_index_get_n_unique_in_tree */
3366+ index->cached = TRUE;
3367+
3368+ return(index);
3369+}
3370+
3371+/********************************************************************//**
3372+Add a column to the dummy index */
3373+static
3374+void
3375+ib_page_ibuf_dummy_index_add_col(
3376+/*=============================*/
3377+ dict_index_t* index, /*!< in: dummy index */
3378+ const dtype_t* type, /*!< in: the data type of the column */
3379+ ulint len) /*!< in: length of the column */
3380+{
3381+ ulint i = index->table->n_def;
3382+ dict_mem_table_add_col(index->table, NULL, NULL,
3383+ dtype_get_mtype(type),
3384+ dtype_get_prtype(type),
3385+ dtype_get_len(type));
3386+ dict_index_add_col(index, index->table,
3387+ dict_table_get_nth_col(index->table, i), len);
3388+}
3389+/********************************************************************//**
3390+Deallocates a dummy index for inserting a record to a non-clustered index. */
3391+static
3392+void
3393+ib_page_ibuf_dummy_index_free(
3394+/*==========================*/
3395+ dict_index_t* index) /*!< in, own: dummy index */
3396+{
3397+ dict_table_t* table = index->table;
3398+
3399+ dict_mem_index_free(index);
3400+ dict_mem_table_free(table);
3401+}
3402+
3403+/*********************************************************************//**
3404+Builds the entry to insert into a non-clustered index when we have the
3405+corresponding record in an ibuf index.
3406+
3407+NOTE that as we copy pointers to fields in ibuf_rec, the caller must
3408+hold a latch to the ibuf_rec page as long as the entry is used!
3409+
3410+@return own: entry to insert to a non-clustered index */
3411+UNIV_INLINE
3412+dtuple_t*
3413+ib_page_ibuf_build_entry_pre_4_1_x(
3414+/*===============================*/
3415+ const rec_t* ibuf_rec, /*!< in: record in an insert buffer */
3416+ mem_heap_t* heap, /*!< in: heap where built */
3417+ dict_index_t** pindex) /*!< out, own: dummy index that
3418+ describes the entry */
3419+{
3420+ ulint i;
3421+ ulint len;
3422+ const byte* types;
3423+ dtuple_t* tuple;
3424+ ulint n_fields;
3425+
3426+ n_fields = rec_get_n_fields_old(ibuf_rec) - 2;
3427+ tuple = dtuple_create(heap, n_fields);
3428+ types = rec_get_nth_field_old(ibuf_rec, 1, &len);
3429+
3430+ ut_a(len == n_fields * DATA_ORDER_NULL_TYPE_BUF_SIZE);
3431+
3432+ for (i = 0; i < n_fields; i++) {
3433+ const byte* data;
3434+ dfield_t* field;
3435+
3436+ field = dtuple_get_nth_field(tuple, i);
3437+
3438+ data = rec_get_nth_field_old(ibuf_rec, i + 2, &len);
3439+
3440+ dfield_set_data(field, data, len);
3441+
3442+ dtype_read_for_order_and_null_size(
3443+ dfield_get_type(field),
3444+ types + i * DATA_ORDER_NULL_TYPE_BUF_SIZE);
3445+ }
3446+
3447+ *pindex = ib_page_ibuf_dummy_index_create(n_fields, FALSE);
3448+
3449+ return(tuple);
3450+}
3451+
3452+/*********************************************************************//**
3453+Builds the entry used to
3454+
3455+1) IBUF_OP_INSERT: insert into a non-clustered index
3456+
3457+2) IBUF_OP_DELETE_MARK: find the record whose delete-mark flag we need to
3458+ activate
3459+
3460+3) IBUF_OP_DELETE: find the record we need to delete
3461+
3462+when we have the corresponding record in an ibuf index.
3463+
3464+NOTE that as we copy pointers to fields in ibuf_rec, the caller must
3465+hold a latch to the ibuf_rec page as long as the entry is used!
3466+
3467+@return own: entry to insert to a non-clustered index */
3468+static
3469+dtuple_t*
3470+ib_page_ibuf_build_entry_from_ibuf_rec(
3471+/*===================================*/
3472+ const rec_t* ibuf_rec, /*!< in: record in an insert buffer */
3473+ mem_heap_t* heap, /*!< in: heap where built */
3474+ dict_index_t** pindex) /*!< out, own: dummy index that
3475+ describes the entry */
3476+{
3477+ dtuple_t* tuple;
3478+ dfield_t* field;
3479+ ulint n_fields;
3480+ const byte* types;
3481+ const byte* data;
3482+ ulint len;
3483+ ulint info_len;
3484+ ulint i;
3485+ ulint comp;
3486+ dict_index_t* index;
3487+
3488+ data = rec_get_nth_field_old(ibuf_rec, 1, &len);
3489+
3490+ if (len > 1) {
3491+ /* This a < 4.1.x format record */
3492+
3493+ return(ib_page_ibuf_build_entry_pre_4_1_x(
3494+ ibuf_rec, heap, pindex));
3495+ }
3496+
3497+ /* This a >= 4.1.x format record */
3498+
3499+ ut_a(*data == 0);
3500+ ut_a(rec_get_n_fields_old(ibuf_rec) > 4);
3501+
3502+ n_fields = rec_get_n_fields_old(ibuf_rec) - 4;
3503+
3504+ tuple = dtuple_create(heap, n_fields);
3505+
3506+ types = rec_get_nth_field_old(ibuf_rec, 3, &len);
3507+
3508+ ib_page_ibuf_rec_get_info(ibuf_rec, NULL, &comp, &info_len, NULL);
3509+
3510+ index = ib_page_ibuf_dummy_index_create(n_fields, comp);
3511+
3512+ len -= info_len;
3513+ types += info_len;
3514+
3515+ ut_a(len == n_fields * DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE);
3516+
3517+ for (i = 0; i < n_fields; i++) {
3518+ field = dtuple_get_nth_field(tuple, i);
3519+
3520+ data = rec_get_nth_field_old(ibuf_rec, i + 4, &len);
3521+
3522+ dfield_set_data(field, data, len);
3523+
3524+ dtype_new_read_for_order_and_null_size(
3525+ dfield_get_type(field),
3526+ types + i * DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE);
3527+
3528+ ib_page_ibuf_dummy_index_add_col(
3529+ index, dfield_get_type(field), len);
3530+ }
3531+
3532+ /* Prevent an ut_ad() failure in page_zip_write_rec() by
3533+ adding system columns to the dummy table pointed to by the
3534+ dummy secondary index. The insert buffer is only used for
3535+ secondary indexes, whose records never contain any system
3536+ columns, such as DB_TRX_ID. */
3537+ ut_d(dict_table_add_system_columns(index->table, index->table->heap));
3538+
3539+ *pindex = index;
3540+
3541+ return(tuple);
3542+}
3543+
3544+/******************************************************************//**
3545+Get the data size.
3546+@return size of fields */
3547+UNIV_INLINE
3548+ulint
3549+ib_page_ibuf_rec_get_size(
3550+/*======================*/
3551+ const rec_t* rec, /*!< in: ibuf record */
3552+ const byte* types, /*!< in: fields */
3553+ ulint n_fields, /*!< in: number of fields */
3554+ ibool pre_4_1, /*!< in: TRUE=pre-4.1 format,
3555+ FALSE=newer */
3556+ ulint comp) /*!< in: 0=ROW_FORMAT=REDUNDANT,
3557+ nonzero=ROW_FORMAT=COMPACT */
3558+{
3559+ ulint i;
3560+ ulint field_offset;
3561+ ulint types_offset;
3562+ ulint size = 0;
3563+
3564+ if (pre_4_1) {
3565+ field_offset = 2;
3566+ types_offset = DATA_ORDER_NULL_TYPE_BUF_SIZE;
3567+ } else {
3568+ field_offset = 4;
3569+ types_offset = DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE;
3570+ }
3571+
3572+ for (i = 0; i < n_fields; i++) {
3573+ ulint len;
3574+ dtype_t dtype;
3575+
3576+ rec_get_nth_field_offs_old(rec, i + field_offset, &len);
3577+
3578+ if (len != UNIV_SQL_NULL) {
3579+ size += len;
3580+ } else if (pre_4_1) {
3581+ dtype_read_for_order_and_null_size(&dtype, types);
3582+
3583+ size += dtype_get_sql_null_size(&dtype, comp);
3584+ } else {
3585+ dtype_new_read_for_order_and_null_size(&dtype, types);
3586+
3587+ size += dtype_get_sql_null_size(&dtype, comp);
3588+ }
3589+
3590+ types += types_offset;
3591+ }
3592+
3593+ return(size);
3594+}
3595+
3596+/*******************************************************************//**
3597+Copy types of fields contained in index to tuple. */
3598+UNIV_INTERN
3599+void
3600+ib_page_dict_index_copy_types(
3601+/*==========================*/
3602+ dtuple_t* tuple, /*!< in/out: data tuple */
3603+ const dict_index_t* index, /*!< in: index */
3604+ ulint n_fields, /*!< in: number of
3605+ field types to copy */
3606+ ibool is_leaf_page) /*!< in: TRUE if leas page */
3607+{
3608+ ulint i;
3609+
3610+ if (UNIV_UNLIKELY(index->type & DICT_UNIVERSAL)) {
3611+ dtuple_set_types_binary(tuple, n_fields);
3612+
3613+ return;
3614+ }
3615+
3616+ for (i = 0; i < n_fields; i++) {
3617+ const dict_field_t* ifield;
3618+ dtype_t* dfield_type;
3619+
3620+ ifield = dict_index_get_nth_field(index, i);
3621+ dfield_type = dfield_get_type(dtuple_get_nth_field(tuple, i));
3622+ if (i >= dict_index_get_n_unique(index) && !is_leaf_page) {
3623+ dfield_type->mtype = DATA_INT;
3624+ dfield_type->prtype = 0;
3625+ dfield_type->len = 8;
3626+ dfield_type->mbminlen = 0;
3627+ dfield_type->mbmaxlen = 0;
3628+ } else {
3629+ dict_col_copy_type(
3630+ dict_field_get_col(ifield), dfield_type);
3631+ }
3632+ }
3633+}
3634+
3635+/*******************************************************************//**
3636+Converts an index record to a typed data tuple.
3637+@return index entry built; does not set info_bits, and the data fields
3638+in the entry will point directly to rec */
3639+UNIV_INTERN
3640+dtuple_t*
3641+ib_page_row_rec_to_index_entry_low(
3642+/*===============================*/
3643+ const page_t* page,
3644+ const rec_t* rec, /*!< in: record in the index */
3645+ const dict_index_t* index, /*!< in: index */
3646+ const ulint* offsets,/*!< in: rec_get_offsets(rec, index) */
3647+ ulint* n_ext, /*!< out: number of externally
3648+ stored columns */
3649+ mem_heap_t* heap) /*!< in: memory heap from which
3650+ the memory needed is allocated */
3651+{
3652+ dtuple_t* entry;
3653+ dfield_t* dfield;
3654+ ulint i;
3655+ const byte* field;
3656+ ulint len;
3657+ ulint rec_len;
3658+
3659+ ut_ad(rec && heap && index);
3660+ ut_ad(n_ext);
3661+ *n_ext = 0;
3662+
3663+ rec_len = rec_offs_n_fields(offsets);
3664+
3665+ entry = dtuple_create(heap, rec_len);
3666+
3667+ dtuple_set_n_fields_cmp(entry,
3668+ dict_index_get_n_unique_in_tree(index));
3669+ ut_ad((!page_is_leaf(page) && rec_len == 2)
3670+ || (rec_len == dict_index_get_n_fields(index)));
3671+
3672+ ib_page_dict_index_copy_types(
3673+ entry, index, rec_len, page_is_leaf(page));
3674+
3675+ for (i = 0; i < rec_len; i++) {
3676+
3677+ dfield = dtuple_get_nth_field(entry, i);
3678+ field = rec_get_nth_field(rec, offsets, i, &len);
3679+
3680+ dfield_set_data(dfield, field, len);
3681+
3682+ if (rec_offs_nth_extern(offsets, i)) {
3683+ dfield_set_ext(dfield);
3684+ (*n_ext)++;
3685+ }
3686+ }
3687+
3688+ ut_ad(dtuple_check_typed(entry));
3689+
3690+ return(entry);
3691+}
3692+
3693+/*******************************************************************//**
3694+Converts an index record to a typed data tuple. NOTE that externally
3695+stored (often big) fields are NOT copied to heap.
3696+@return own: index entry built; see the NOTE below! */
3697+UNIV_INTERN
3698+dtuple_t*
3699+ib_page_row_rec_to_index_entry(
3700+/*===========================*/
3701+ ulint type, /*!< in: ROW_COPY_DATA, or
3702+ ROW_COPY_POINTERS: the former
3703+ copies also the data fields to
3704+ heap as the latter only places
3705+ pointers to data fields on the
3706+ index page */
3707+ const page_t* page,
3708+ const rec_t* rec, /*!< in: record in the index;
3709+ NOTE: in the case
3710+ ROW_COPY_POINTERS the data
3711+ fields in the row will point
3712+ directly into this record,
3713+ therefore, the buffer page of
3714+ this record must be at least
3715+ s-latched and the latch held
3716+ as long as the dtuple is used! */
3717+ const dict_index_t* index, /*!< in: index */
3718+ ulint* offsets,/*!< in/out: rec_get_offsets(rec) */
3719+ ulint* n_ext, /*!< out: number of externally
3720+ stored columns */
3721+ mem_heap_t* heap) /*!< in: memory heap from which
3722+ the memory needed is allocated */
3723+{
3724+ dtuple_t* entry;
3725+ byte* buf;
3726+
3727+ ut_ad(rec && heap && index);
3728+ ut_ad(rec_offs_validate(rec, index, offsets));
3729+
3730+ if (type == ROW_COPY_DATA) {
3731+ /* Take a copy of rec to heap */
3732+ buf = mem_heap_alloc(heap, rec_offs_size(offsets));
3733+ rec = rec_copy(buf, rec, offsets);
3734+ /* Avoid a debug assertion in rec_offs_validate(). */
3735+ rec_offs_make_valid(rec, index, offsets);
3736+ }
3737+
3738+ entry = ib_page_row_rec_to_index_entry_low(
3739+ page, rec, index, offsets, n_ext, heap);
3740+
3741+ dtuple_set_info_bits(entry,
3742+ rec_get_info_bits(rec, rec_offs_comp(offsets)));
3743+
3744+ return(entry);
3745+}
3746+
3747+/***************************************************************//**
3748+Allocate signle aligned page.
3749+@return page */
3750+UNIV_INTERN
3751+page_t*
3752+ib_page_heap_page_alloc(
3753+/*====================*/
3754+ mem_heap_t* heap) /*!< in: memory heap where allocate in */
3755+{
3756+ page_t* page = mem_heap_alloc(heap, UNIV_PAGE_SIZE * 2);
3757+ return page_align(page + UNIV_PAGE_SIZE);
3758+}
3759+
3760+/***************************************************************//**
3761+Get space id of given tablespace in file.
3762+@return space id if given tablespace file */
3763+UNIV_INTERN
3764+ulint
3765+ib_page_file_space_id(
3766+/*==================*/
3767+ os_file_t file) /*!< in: file */
3768+{
3769+ ulint space_id;
3770+ mem_heap_t* heap = mem_heap_create(100);
3771+ page_t* page = ib_page_heap_page_alloc(heap);
3772+
3773+ if (page == NULL)
3774+ {
3775+ mem_heap_free(heap);
3776+ ut_error;
3777+ }
3778+
3779+ if (!os_file_read(file, page, 0, 0, UNIV_PAGE_SIZE))
3780+ {
3781+ mem_heap_free(heap);
3782+ ut_error;
3783+ }
3784+
3785+ space_id = page_get_space_id(page);
3786+
3787+ mem_heap_free(heap);
3788+
3789+ return space_id;
3790+
3791+}
3792+
3793+/***************************************************************//**
3794+Calculate the low 32 bits and the high 32 bits
3795+of the file offset. */
3796+UNIV_INTERN
3797+void
3798+ib_page_calc_offsets(
3799+/*=================*/
3800+ ulint zip_size, /*!< in: compressed page size, or
3801+ 0 for uncompressed pages */
3802+ ulint page_no, /*!< in: page number */
3803+ ulint* offset_low, /*!< out: low 32 bits */
3804+ ulint* offset_high) /*!< out: high 32 bits */
3805+{
3806+ /* Calculate the low 32 bits and the high 32 bits of the file offset */
3807+
3808+ if (!zip_size) {
3809+ *offset_high = (page_no >> (32 - UNIV_PAGE_SIZE_SHIFT));
3810+ *offset_low = ((page_no << UNIV_PAGE_SIZE_SHIFT)
3811+ & 0xFFFFFFFFUL);
3812+ } else {
3813+ ulint zip_size_shift;
3814+ switch (zip_size) {
3815+ case 1024: zip_size_shift = 10; break;
3816+ case 2048: zip_size_shift = 11; break;
3817+ case 4096: zip_size_shift = 12; break;
3818+ case 8192: zip_size_shift = 13; break;
3819+ case 16384: zip_size_shift = 14; break;
3820+ default: ut_error;
3821+ }
3822+ *offset_high = page_no >> (32 - zip_size_shift);
3823+ *offset_low = (page_no << zip_size_shift & 0xFFFFFFFFUL);
3824+ }
3825+}
3826+
3827+/***************************************************************//**
3828+Load compressed page into memory.
3829+Space for page allocated in heap.
3830+@return loaded page; NULL if page was not loaded */
3831+UNIV_INTERN
3832+page_t*
3833+ib_page_load_compressed(
3834+/*====================*/
3835+ os_file_t file, /*!< in: file */
3836+ ulint page_no, /*!< in: page number */
3837+ ulint zip_size, /*!< in: compressed page size, or
3838+ 0 for uncompressed pages */
3839+ mem_heap_t* heap) /*!< in: memory heap */
3840+{
3841+ ulint offset_high;
3842+ ulint offset_low;
3843+ ulint page_size;
3844+ page_t* page;
3845+
3846+ page_size = zip_size ? zip_size : UNIV_PAGE_SIZE;
3847+ page = ib_page_heap_page_alloc(heap);
3848+
3849+ ib_page_calc_offsets(zip_size, page_no, &offset_low, &offset_high);
3850+ if (!os_file_read(file, page, offset_low, offset_high, page_size))
3851+ {
3852+ ib_logger(ib_stream,
3853+ "Failed to read %lu bytes from offset {%lu %lu}\n",
3854+ page_size, offset_low, offset_high);
3855+ ut_error;
3856+ }
3857+
3858+ return page;
3859+}
3860+
3861+/***************************************************************//**
3862+Decompress compressed page.
3863+Space for decompressed page allocated in heap.
3864+@return loaded page; NULL if page was not loaded */
3865+UNIV_INTERN
3866+page_t*
3867+ib_page_decompress(
3868+/*===============*/
3869+ const page_t* compressed_page,/*!< in: compressed page */
3870+ ulint zip_size, /*!< in: compressed page size, or
3871+ 0 for uncompressed pages */
3872+ mem_heap_t* heap) /*!< in: memory heap */
3873+{
3874+ page_t* decompressed_page = ib_page_heap_page_alloc(heap);
3875+
3876+ if (decompressed_page == NULL)
3877+ {
3878+ ut_error;
3879+ }
3880+
3881+ if (zip_size)
3882+ {
3883+ page_zip_des_t des;
3884+ des.data = (page_t*) compressed_page;
3885+ page_zip_set_size(&des, zip_size);
3886+
3887+ ulint stamp_checksum = mach_read_from_4(
3888+ compressed_page + FIL_PAGE_SPACE_OR_CHKSUM);
3889+ ulint calc_checksum = page_zip_calc_checksum(
3890+ compressed_page, zip_size);
3891+
3892+ if (UNIV_UNLIKELY(stamp_checksum != calc_checksum)) {
3893+ ut_print_timestamp(ib_stream);
3894+ ib_logger(ib_stream,
3895+ " compressed page checksum mismatch"
3896+ "%lu != %lu\n",
3897+ stamp_checksum, calc_checksum);
3898+ ut_error;
3899+ }
3900+
3901+ switch (fil_page_get_type(compressed_page)) {
3902+ case FIL_PAGE_INDEX:
3903+ if (page_zip_decompress(&des,
3904+ decompressed_page, TRUE)) {
3905+ return decompressed_page;
3906+ }
3907+
3908+ ib_logger(ib_stream,
3909+ " unable to decompress page\n");
3910+ ut_error;
3911+
3912+ case FIL_PAGE_TYPE_ALLOCATED:
3913+ case FIL_PAGE_INODE:
3914+ case FIL_PAGE_IBUF_BITMAP:
3915+ case FIL_PAGE_TYPE_FSP_HDR:
3916+ case FIL_PAGE_TYPE_XDES:
3917+ case FIL_PAGE_TYPE_ZBLOB:
3918+ case FIL_PAGE_TYPE_ZBLOB2:
3919+ /* Copy to uncompressed storage. */
3920+ memcpy(decompressed_page, compressed_page, zip_size);
3921+ return decompressed_page;
3922+ }
3923+
3924+ ut_print_timestamp(ib_stream);
3925+ ib_logger(ib_stream,
3926+ " unknown compressed page"
3927+ " type %lu\n",
3928+ fil_page_get_type(compressed_page));
3929+ ut_error;
3930+ }
3931+ else
3932+ {
3933+ ut_memcpy(decompressed_page, compressed_page, UNIV_PAGE_SIZE);
3934+ }
3935+
3936+ return decompressed_page;
3937+}
3938+
3939+/*******************************************************************//**
3940+Load page from file into memory and decompress it if needed.
3941+@return TRUE on success */
3942+ibool
3943+ib_page_load(
3944+/*=========*/
3945+ const char* ibd_file_name, /*!< in: file name */
3946+ ulint page_no, /*!< in: page number */
3947+ ulint* zip_size, /*!< in: compressed page size, or
3948+ 0 for uncompressed pages */
3949+ page_t** compressed_page, /*!< out: compressed page */
3950+ page_t** decompressed_page, /*!< out: decompressed page */
3951+ mem_heap_t* heap) /*!< out: memory heap */
3952+{
3953+ ibool success;
3954+ os_file_t file;
3955+
3956+ file = os_file_create(ibd_file_name,
3957+ OS_FILE_OPEN, OS_FILE_NORMAL,
3958+ OS_DATA_FILE, &success);
3959+
3960+ if (!success)
3961+ {
3962+ ib_logger(ib_stream, "Failed to open file %s\n", ibd_file_name);
3963+ return(FALSE);
3964+ }
3965+
3966+ ulint space_id = ib_page_file_space_id(file);
3967+ *zip_size = fil_space_get_zip_size(space_id);
3968+ if (*zip_size == ULINT_UNDEFINED) {
3969+ *zip_size = 0;
3970+ }
3971+
3972+ *compressed_page = ib_page_load_compressed(file, page_no,
3973+ *zip_size, heap);
3974+ if (*compressed_page == NULL)
3975+ {
3976+ return(FALSE);
3977+ }
3978+
3979+ *decompressed_page = ib_page_decompress(*compressed_page,
3980+ *zip_size, heap);
3981+ if (*decompressed_page == NULL)
3982+ {
3983+ return(FALSE);
3984+ }
3985+
3986+ os_file_close(file);
3987+
3988+ return(TRUE);
3989+
3990+}
3991+
3992+
3993+/*******************************************************************//**
3994+Print IBUF bitmap page. */
3995+UNIV_INTERN
3996+void
3997+ib_page_ibuf_bitmap_print(
3998+/*======================*/
3999+ const page_t* page, /*!< in: page */
4000+ ulint zip_size) /*!< in: compressed page size, or
4001+ 0 for uncompressed pages */
4002+{
4003+ ulint byte_offset;
4004+ ulint i;
4005+
4006+ enum { IBUF_BITS_PER_PAGE = 4 };
4007+
4008+ ulint page_no = page_get_page_no(page);
4009+
4010+ if (!zip_size) {
4011+ byte_offset = UT_BITS_IN_BYTES(UNIV_PAGE_SIZE
4012+ * IBUF_BITS_PER_PAGE);
4013+ } else {
4014+ byte_offset = UT_BITS_IN_BYTES(zip_size * IBUF_BITS_PER_PAGE);
4015+ }
4016+
4017+ for (i = 0; i < byte_offset; i++) {
4018+ uint bits;
4019+
4020+ bits = (uint)mach_read_from_1(page + PAGE_DATA + i / 2) & 0xF;
4021+ ib_logger(ib_stream, "%lu: %2x", page_no + 2*i - 1, bits);
4022+ ib_logger(ib_stream, " (%d free)", bits & 0x2);
4023+ if (bits & 2) {
4024+ ib_logger(ib_stream, ", buffered");
4025+ }
4026+ if (bits & 4) {
4027+ ib_logger(ib_stream, ", ibuf");
4028+ }
4029+ ib_logger(ib_stream, "\n");
4030+
4031+ bits = (uint)
4032+ (mach_read_from_1(page + PAGE_DATA + i / 2) >> 4) & 0xF;
4033+ ib_logger(ib_stream, "%lu: %2x", page_no + 2*i, bits);
4034+ ib_logger(ib_stream, " (%d free)", bits & 0x2);
4035+ if (bits & 2) {
4036+ ib_logger(ib_stream, ", buffered");
4037+ }
4038+ if (bits & 4) {
4039+ ib_logger(ib_stream, ", ibuf");
4040+ }
4041+ ib_logger(ib_stream, "\n");
4042+
4043+ }
4044+
4045+}
4046+
4047+/***************************************************************//**
4048+Print undo log page. */
4049+UNIV_INTERN
4050+void
4051+ib_page_print_undo(
4052+/*===============*/
4053+ const page_t* page) /*!< in: page */
4054+{
4055+ const trx_undo_rec_t* rec;
4056+ ulint rec_offs;
4057+ ulint prev_offs;
4058+ ulint first_free;
4059+ ulint type;
4060+ ulint cmpl_info;
4061+ ibool updated_extern;
4062+ undo_no_t undo_no;
4063+ dulint table_id;
4064+ byte* ptr;
4065+ trx_id_t trx_id;
4066+ roll_ptr_t roll_ptr;
4067+ ulint info_bits;
4068+ dict_table_t* table;
4069+ dict_index_t* index;
4070+ upd_t* update;
4071+
4072+ first_free = mach_read_from_2(page + TRX_UNDO_PAGE_HDR
4073+ + TRX_UNDO_PAGE_FREE);
4074+ ib_logger(ib_stream, "first free: %lu\n", first_free);
4075+ rec_offs = mach_read_from_2(page + first_free - 2);
4076+
4077+
4078+ while (rec_offs != first_free
4079+ && rec_offs >= TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_HDR_SIZE) {
4080+ rec = page + rec_offs;
4081+
4082+ ib_logger(ib_stream, "===============================\n");
4083+
4084+ ptr = trx_undo_rec_get_pars((trx_undo_rec_t*)rec,
4085+ &type, &cmpl_info,
4086+ &updated_extern, &undo_no,
4087+ &table_id);
4088+ ib_logger(ib_stream,
4089+ "Undo log record with offset: %lu,"
4090+ " compiler info: %ld, updated extern: %s,"
4091+ " undo log record number: {%lu %lu},"
4092+ " table id: {%lu %lu}\n",
4093+ rec_offs, cmpl_info,
4094+ updated_extern ? "YES" : "NO",
4095+ ut_dulint_get_high(undo_no),
4096+ ut_dulint_get_low(undo_no),
4097+ ut_dulint_get_high(table_id),
4098+ ut_dulint_get_low(table_id));
4099+
4100+ mutex_enter(&(dict_sys->mutex));
4101+ table = dict_table_get_on_id_low(table_id);
4102+ mutex_exit(&(dict_sys->mutex));
4103+ if (table)
4104+ ib_logger(ib_stream, "table name: %s\n", table->name);
4105+
4106+ switch (type) {
4107+ case TRX_UNDO_INSERT_REC:
4108+ ib_logger(ib_stream,
4109+ "fresh insert into clustered index\n");
4110+ break;
4111+ case TRX_UNDO_UPD_EXIST_REC:
4112+ ib_logger(ib_stream,
4113+ "update of a non-delete-marked record\n");
4114+ break;
4115+ case TRX_UNDO_UPD_DEL_REC:
4116+ ib_logger(ib_stream,
4117+ "update of a delete marked record to "
4118+ "a not delete marked record; also the "
4119+ "fields of the record can change\n");
4120+ break;
4121+ case TRX_UNDO_DEL_MARK_REC:
4122+ ib_logger(ib_stream,
4123+ "delete marking of a record; fields "
4124+ "do not change\n");
4125+ break;
4126+ }
4127+
4128+ if (type == TRX_UNDO_UPD_EXIST_REC ||
4129+ type == TRX_UNDO_UPD_DEL_REC ||
4130+ type == TRX_UNDO_DEL_MARK_REC) {
4131+ ptr = trx_undo_update_rec_get_sys_cols(ptr,
4132+ &trx_id,
4133+ &roll_ptr,
4134+ &info_bits);
4135+ }
4136+
4137+ ib_logger(ib_stream,
4138+ "trx_id: {%lu %lu}, roll_ptr: {%lu %lu}, "
4139+ "info bits: %lu\n",
4140+ ut_dulint_get_high(trx_id),
4141+ ut_dulint_get_low(trx_id),
4142+ ut_dulint_get_high(roll_ptr),
4143+ ut_dulint_get_low(roll_ptr),
4144+ info_bits);
4145+
4146+ if (table) {
4147+ index = dict_table_get_first_index(table);
4148+
4149+ mem_heap_t* heap = mem_heap_create(100);
4150+ dtuple_t* ref;
4151+
4152+ ptr = trx_undo_rec_get_row_ref(ptr, index, &ref, heap);
4153+
4154+ if (ptr != NULL) {
4155+
4156+ for (ulint i = 0;
4157+ i < dtuple_get_n_fields(ref);i++) {
4158+ dfield_t* dfield =
4159+ dtuple_get_nth_field(ref, i);
4160+
4161+ ib_logger(ib_stream,
4162+ "(%s/%s/%lu) %s:",
4163+ table->name, index->name,
4164+ i,
4165+ dict_table_get_col_name(table,
4166+ i));
4167+ ib_page_rec_print_field(dfield,
4168+ NULL, 0, NULL);
4169+ ib_logger(ib_stream, "\n");
4170+ }
4171+ }
4172+
4173+ if (type == TRX_UNDO_UPD_EXIST_REC ||
4174+ type == TRX_UNDO_UPD_DEL_REC ||
4175+ type == TRX_UNDO_DEL_MARK_REC) {
4176+
4177+ ptr = trx_undo_update_rec_get_update(ptr, index,
4178+ type, trx_id,
4179+ roll_ptr, info_bits,
4180+ NULL, heap, &update);
4181+
4182+ if (ptr != NULL) {
4183+
4184+ for (ulint i = 0;
4185+ i < upd_get_n_fields(update);
4186+ i++) {
4187+ upd_field_t* upd_field;
4188+ dfield_t* field;
4189+ upd_field = upd_get_nth_field(
4190+ update, i);
4191+ field = &upd_field->new_val;
4192+ ib_logger(ib_stream,
4193+ "(%s/%s/%u) %s:",
4194+ table->name,
4195+ index->name,
4196+ upd_field->field_no,
4197+ dict_table_get_col_name(
4198+ table,
4199+ upd_field->field_no));
4200+ ib_page_rec_print_field(field,
4201+ NULL, 0, NULL);
4202+ ib_logger(ib_stream, "\n");
4203+ }
4204+
4205+ }
4206+
4207+ }
4208+
4209+ mem_heap_free(heap);
4210+
4211+ }
4212+
4213+ prev_offs = rec_offs;
4214+ rec_offs = mach_read_from_2(rec - 2);
4215+
4216+ ib_logger(ib_stream, "prev_offs: %lu, rec_offs: %lu\n",
4217+ prev_offs, rec_offs);
4218+
4219+ }
4220+
4221+}
4222+
4223+/***************************************************************//**
4224+Test if page is UNDO INSERT page.
4225+@return TRUE if page is UNDO INSERT page; FALE otherwise */
4226+UNIV_INTERN
4227+ibool
4228+ib_page_is_undo_insert(
4229+/*===================*/
4230+ const page_t* page) /*!< in: page */
4231+{
4232+ return (mach_read_from_2(page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_TYPE)
4233+ == TRX_UNDO_INSERT);
4234+}
4235+
4236+/***************************************************************//**
4237+Test if page is UNDO UPDATE page.
4238+@return TRUE if page is UNDO UPDATE page; FALE otherwise */
4239+UNIV_INTERN
4240+ibool
4241+ib_page_is_undo_update(
4242+/*===================*/
4243+ const page_t* page) /*!< in: page */
4244+{
4245+ return (mach_read_from_2(page + TRX_UNDO_PAGE_HDR
4246+ + TRX_UNDO_PAGE_TYPE)
4247+ == TRX_UNDO_UPDATE);
4248+}
4249+
4250+/***************************************************************//**
4251+Test if page is UNDO page.
4252+@return TRUE if page is UNDO page; FALE otherwise */
4253+UNIV_INTERN
4254+ibool
4255+ib_page_is_undo(
4256+/*============*/
4257+ const page_t* page) /*!< in: page */
4258+{
4259+ return ib_page_is_undo_insert(page) || ib_page_is_undo_update(page);
4260+}
4261+
4262+/***************************************************************//**
4263+Print file-based list address. */
4264+UNIV_INTERN
4265+void
4266+ib_page_print_flst_addr(
4267+/*====================*/
4268+ const byte* base) /*!< in: pointer to base node of list */
4269+{
4270+ ulint page = mach_read_from_4(base + FIL_ADDR_PAGE);
4271+ ulint boffset = mach_read_from_2(base + FIL_ADDR_BYTE);
4272+ ib_logger(ib_stream, "page %lu, byte offset %lu\n",
4273+ page, boffset);
4274+}
4275+
4276+/***************************************************************//**
4277+Print file-based list. */
4278+UNIV_INTERN
4279+void
4280+ib_page_print_flst(
4281+/*===============*/
4282+ const byte* base) /*!< in: pointer to base node of list */
4283+{
4284+ ulint len = mach_read_from_4(base + FLST_LEN);
4285+ ib_logger(ib_stream, "len: %lu\n", len);
4286+ ib_logger(ib_stream, "first: ");
4287+ ib_page_print_flst_addr(base + FLST_FIRST);
4288+ ib_logger(ib_stream, "last: ");
4289+ ib_page_print_flst_addr(base + FLST_LAST);
4290+}
4291+
4292+/***************************************************************//**
4293+Print filespace header page. */
4294+UNIV_INTERN
4295+void
4296+ib_page_print_fsp_header(
4297+/*=====================*/
4298+ const page_t* page) /*!< in: page */
4299+{
4300+ const byte* header;
4301+
4302+ ut_a(mach_read_from_2(page + FIL_PAGE_TYPE) == FIL_PAGE_TYPE_FSP_HDR);
4303+
4304+ header = FSP_HEADER_OFFSET + page;
4305+
4306+ ib_logger(ib_stream,
4307+ "space id: %lu, not used: %lu, size: %lu\n"
4308+ "free limit: %lu, space flags: %lu, frag n used: %lu\n",
4309+ mach_read_from_4(header + FSP_SPACE_ID),
4310+ mach_read_from_4(header + FSP_NOT_USED),
4311+ mach_read_from_4(header + FSP_SIZE),
4312+ mach_read_from_4(header + FSP_FREE_LIMIT),
4313+ mach_read_from_4(header + FSP_SPACE_FLAGS),
4314+ mach_read_from_4(header + FSP_FRAG_N_USED));
4315+
4316+ dulint seg_id = mach_read_from_8(header + FSP_SEG_ID);
4317+ ib_logger(ib_stream, "seg_id: {%lu %lu}\n\n",
4318+ ut_dulint_get_high(seg_id),
4319+ ut_dulint_get_low(seg_id));
4320+
4321+ ib_logger(ib_stream, "FREE LIST:\n");
4322+ ib_page_print_flst(header + FSP_FREE);
4323+ ib_logger(ib_stream, "\n");
4324+ ib_logger(ib_stream, "FREE FRAG LIST:\n");
4325+ ib_page_print_flst(header + FSP_FREE_FRAG);
4326+ ib_logger(ib_stream, "\n");
4327+ ib_logger(ib_stream, "FREE FULL FRAG LIST:\n");
4328+ ib_page_print_flst(header + FSP_FULL_FRAG);
4329+ ib_logger(ib_stream, "\n");
4330+ ib_logger(ib_stream, "FREE FSP SEG INODES FULL LIST:\n");
4331+ ib_page_print_flst(header + FSP_SEG_INODES_FULL);
4332+ ib_logger(ib_stream, "\n");
4333+ ib_logger(ib_stream, "FREE FSP SEG INODES FREE LIST:\n");
4334+ ib_page_print_flst(header + FSP_SEG_INODES_FREE);
4335+ ib_logger(ib_stream, "\n");
4336+
4337+}
4338+
4339+/***************************************************************//**
4340+Print ZBLOB page. */
4341+UNIV_INTERN
4342+void
4343+ib_page_print_zblob(
4344+/*================*/
4345+ const page_t* page, /*!< in: page */
4346+ ulint zip_size) /*!< in: compressed page size, or
4347+ 0 for uncompressed pages */
4348+{
4349+ enum { buf_len = 80 };
4350+ const ulint offset = FIL_PAGE_NEXT;
4351+ char buf[buf_len];
4352+
4353+ ulint next_page_no = mach_read_from_4(page + offset);
4354+
4355+ ib_logger(ib_stream,
4356+ "compressed blob page, next page %lu\n", next_page_no);
4357+ ib_page_format_bin(buf, buf_len, page + FIL_PAGE_DATA, zip_size);
4358+ ib_logger(ib_stream, "data: %s\n", buf);
4359+
4360+ // TODO: we might uncompress page. do we need it?
4361+}
4362+
4363+/***************************************************************//**
4364+Print ZBLOB2 page. */
4365+UNIV_INTERN
4366+void
4367+ib_page_print_zblob2(
4368+/*=================*/
4369+ const page_t* page, /*!< in: page */
4370+ ulint zip_size) /*!< in: compressed page size, or
4371+ 0 for uncompressed pages */
4372+{
4373+ enum { buf_len = 80 };
4374+ const ulint offset = FIL_PAGE_NEXT;
4375+ char buf[buf_len];
4376+
4377+ ulint next_page_no = mach_read_from_4(page + offset);
4378+
4379+ ib_logger(ib_stream,
4380+ "compressed blob (continued) page, next page %lu\n",
4381+ next_page_no);
4382+ ib_page_format_bin(buf, buf_len, page + FIL_PAGE_DATA, zip_size);
4383+ ib_logger(ib_stream, "data: %s\n", buf);
4384+}
4385+
4386+/***************************************************************//**
4387+Print BLOB page. */
4388+UNIV_INTERN
4389+void
4390+ib_page_print_blob(
4391+/*===============*/
4392+ const page_t* page) /*!< in: page */
4393+{
4394+ enum { buf_len = 80 };
4395+ const ulint offset = FIL_PAGE_DATA;
4396+ char buf[buf_len];
4397+
4398+ ulint next_page_no = mach_read_from_4(
4399+ page + offset + BTR_BLOB_HDR_NEXT_PAGE_NO);
4400+ ulint part_len = mach_read_from_4(
4401+ page + offset + BTR_BLOB_HDR_PART_LEN);
4402+
4403+ ib_logger(ib_stream,
4404+ "blob page, next page %lu, part len %lu\n",
4405+ next_page_no, part_len);
4406+ ib_page_format_bin(buf, buf_len, page + offset + BTR_BLOB_HDR_SIZE,
4407+ part_len);
4408+ ib_logger(ib_stream, "data: %s\n", buf);
4409+}
4410+
4411+/***************************************************************//**
4412+Print inode page. */
4413+UNIV_INTERN
4414+void
4415+ib_page_print_inode(
4416+/*================*/
4417+ const page_t* page, /*!< in: page */
4418+ ulint zip_size) /*!< in: compressed page size, or
4419+ 0 for uncompressed pages */
4420+{
4421+ for (ulint i = 0; i < FSP_SEG_INODES_PER_PAGE(zip_size); i++)
4422+ {
4423+ const byte* inode;
4424+ inode = page + FSEG_ARR_OFFSET + FSEG_INODE_SIZE * i;
4425+
4426+ ib_logger(ib_stream, "====================================\n");
4427+ ib_logger(ib_stream, "Inode ");
4428+
4429+ dulint seg_id = mach_read_from_8(inode + FSEG_ID);
4430+ ib_logger(ib_stream, "segment id {%lu %lu}, ",
4431+ ut_dulint_get_high(seg_id), ut_dulint_get_low(seg_id));
4432+ dulint seg_not_full_n_used = mach_read_from_8(
4433+ inode + FSEG_NOT_FULL_N_USED);
4434+ ib_logger(ib_stream, "number of used segment pages in "
4435+ "FSEG_NOT_FULL list {%lu %lu}\n\n",
4436+ ut_dulint_get_low(seg_not_full_n_used),
4437+ ut_dulint_get_low(seg_not_full_n_used));
4438+
4439+ ib_logger(ib_stream, "List of free extents of this segment:\n");
4440+ ib_page_print_flst(inode + FSEG_FREE);
4441+
4442+ ib_logger(ib_stream, "\nList of partially free extents:\n");
4443+ ib_page_print_flst(inode + FSEG_NOT_FULL);
4444+
4445+ ib_logger(ib_stream, "\nList of full extents:\n");
4446+ ib_page_print_flst(inode + FSEG_FULL);
4447+
4448+ ulint magic_n = mach_read_from_4(inode + FSEG_MAGIC_N);
4449+ ib_logger(ib_stream, "\nMagic number %lu (should be %lu)\n\n",
4450+ magic_n, (ulint) FSEG_MAGIC_N_VALUE);
4451+
4452+ for (ulint i = 0; i < FSEG_FRAG_ARR_N_SLOTS; i++)
4453+ {
4454+ ulint frag_n = mach_read_from_4(inode + FSEG_FRAG_ARR
4455+ + i * FSEG_FRAG_SLOT_SIZE);
4456+ ib_logger(ib_stream, "%2lu frag slot: %lu", i, frag_n);
4457+ ib_logger(ib_stream, "%s\n",
4458+ frag_n == FIL_NULL ? " (null)" : "");
4459+ }
4460+ ib_logger(ib_stream, "\n");
4461+ }
4462+}
4463+
4464+/***************************************************************//**
4465+@return XDES state as string. */
4466+UNIV_INTERN
4467+const char*
4468+ib_page_xdes_state_str(
4469+/*===================*/
4470+ ulint state) /*!< in: XDES state */
4471+{
4472+ switch (state) {
4473+ case XDES_FREE:
4474+ return "extent is in free list of space";
4475+ case XDES_FREE_FRAG:
4476+ return "extent is in free fragment list of space";
4477+ case XDES_FULL_FRAG:
4478+ return "extent is in full fragment list of space";
4479+ case XDES_FSEG:
4480+ return "extent belongs to a segment";
4481+ }
4482+ return "unknown";
4483+}
4484+
4485+/***************************************************************//**
4486+Print XDES page. */
4487+UNIV_INTERN
4488+void
4489+ib_page_print_xdes(
4490+/*===============*/
4491+ const page_t* page, /*!< in: page */
4492+ ulint zip_size) /*!< in: compressed page size, or
4493+ 0 for uncompressed pages */
4494+{
4495+
4496+ for (ulint j = 0;
4497+ j < (zip_size ? zip_size : UNIV_PAGE_SIZE) / FSP_EXTENT_SIZE;
4498+ j++)
4499+ {
4500+ const byte* descr = page + XDES_ARR_OFFSET + XDES_SIZE * j;
4501+
4502+ dulint seg_id = mach_read_from_8(descr + XDES_ID);
4503+ ib_logger(ib_stream, "====================================\n");
4504+ ib_logger(ib_stream, "Segment ID {%lu %lu}\n\n",
4505+ ut_dulint_get_high(seg_id), ut_dulint_get_low(seg_id));
4506+
4507+ ib_logger(ib_stream, "List of descriptors:\n");
4508+ ib_page_print_flst(descr + XDES_FLST_NODE);
4509+
4510+ ulint state = mach_read_from_4(descr + XDES_STATE);
4511+ ib_logger(ib_stream, "\nState of descriptor %lu (%s)\n",
4512+ state, ib_page_xdes_state_str(state));
4513+
4514+ ib_logger(ib_stream, "\nXDES bitmap\n");
4515+ for (ulint i = 0; i < FSP_EXTENT_SIZE; i++)
4516+ {
4517+ ulint index;
4518+ ulint byte_index;
4519+ ulint bit_index;
4520+ byte byte_contents;
4521+
4522+ index = XDES_FREE_BIT + XDES_BITS_PER_PAGE * i;
4523+ byte_index = index / 8;
4524+ bit_index = index % 8;
4525+
4526+ ib_logger(ib_stream, "%2lu", i);
4527+
4528+ byte_contents = mach_read_from_1(descr
4529+ + XDES_BITMAP + byte_index);
4530+ ib_logger(ib_stream, "%12s", ut_bit_get_nth(
4531+ byte_contents,
4532+ bit_index) ? "free" : "not free");
4533+
4534+ index = XDES_CLEAN_BIT + XDES_BITS_PER_PAGE * i;
4535+ ib_logger(ib_stream, "%12s", ut_bit_get_nth(
4536+ byte_contents,
4537+ bit_index + 1) ? "clean" : "not clean");
4538+
4539+ ib_logger(ib_stream, "\n");
4540+ }
4541+ ib_logger(ib_stream, "\n");
4542+ }
4543+}
4544+
4545+#define DICT_HDR_MIX_ID DICT_HDR_MAX_SPACE_ID
4546+
4547+#define DICT_HDR_XTRADB_FLAG ut_dulint_create(0x58545241UL,0x44425F31UL) /* "XTRADB_1" */
4548+#define DICT_HDR_STATS 52 /* Root of the stats tree */
4549+#define DICT_HDR_XTRADB_MARK 256 /* Flag to distinguish expansion of XtraDB */
4550+
4551+/***************************************************************//**
4552+Print page dictionary header. */
4553+UNIV_INTERN
4554+void
4555+ib_page_print_dict_header(
4556+/*======================*/
4557+ const page_t* page) /*!< in: page */
4558+{
4559+ const page_t* header;
4560+ dulint row_id;
4561+ dulint table_id;
4562+ dulint index_id;
4563+ dulint mix_id;
4564+ ulint tables_root;
4565+ ulint table_ids_root;
4566+ ulint columns_root;
4567+ ulint indexes_root;
4568+ ulint fields_root;
4569+
4570+
4571+ header = page + DICT_HDR;
4572+ row_id = mach_read_from_8(header + DICT_HDR_ROW_ID);
4573+ table_id = mach_read_from_8(header + DICT_HDR_TABLE_ID);
4574+ index_id = mach_read_from_8(header + DICT_HDR_INDEX_ID);
4575+ mix_id = mach_read_from_8(header + DICT_HDR_MIX_ID);
4576+ tables_root = mach_read_from_4(header + DICT_HDR_TABLES);
4577+ table_ids_root = mach_read_from_4(header + DICT_HDR_TABLE_IDS);
4578+ columns_root = mach_read_from_4(header + DICT_HDR_COLUMNS);
4579+ indexes_root = mach_read_from_4(header + DICT_HDR_INDEXES);
4580+ fields_root = mach_read_from_4(header + DICT_HDR_FIELDS);
4581+
4582+ ib_logger(ib_stream, "latest assigned IDS:\n"
4583+ "row {%lu, %lu}, table {%lu, %lu}, "
4584+ "index {%lu, %lu}, mix {%lu, %lu}\n"
4585+ "root pages:\n"
4586+ "tables %lu, tables secondary %lu, columns %lu, "
4587+ "indexes %lu, fields %lu\n",
4588+ ut_dulint_get_high(row_id), ut_dulint_get_low(row_id),
4589+ ut_dulint_get_high(table_id), ut_dulint_get_low(table_id),
4590+ ut_dulint_get_high(index_id), ut_dulint_get_low(index_id),
4591+ ut_dulint_get_high(mix_id), ut_dulint_get_low(mix_id),
4592+ tables_root, table_ids_root, columns_root,
4593+ indexes_root, fields_root);
4594+
4595+
4596+ if (ut_dulint_cmp(mach_read_from_8(header + DICT_HDR_XTRADB_MARK),
4597+ DICT_HDR_XTRADB_FLAG) == 0)
4598+ {
4599+ ib_logger(ib_stream,
4600+ "XtraDB detected, SYS_STATS root page %lu\n",
4601+ mach_read_from_4(header + DICT_HDR_STATS));
4602+ }
4603+}
4604+
4605+/*******************************************************************//**
4606+Print page stored in memory. */
4607+ib_err_t
4608+ib_page_print_mem(
4609+ const page_t* page, /*!< in: page */
4610+ ulint zip_size, /*!< in: compressed page size, or
4611+ 0 for uncompressed pages */
4612+ ibool print_contents) /*!< in: TRUE if page print contents */
4613+{
4614+ ib_buf_page_print_header(page, zip_size);
4615+ ib_page_page_header_print(page);
4616+ if (print_contents)
4617+ {
4618+ switch (fil_page_get_type(page)) {
4619+ case FIL_PAGE_INDEX:
4620+ {
4621+ dict_index_t* index;
4622+ ib_page_page_dir_print(page);
4623+ ib_logger(ib_stream, "WILL PRINT RECORDS\n");
4624+ index = ib_dict_index_get_on_id(
4625+ btr_page_get_index_id(page));
4626+ ib_page_page_print_list(page, index);
4627+ break;
4628+ }
4629+ case FIL_PAGE_IBUF_BITMAP:
4630+ {
4631+ ib_logger(ib_stream, "PAGE BITS:\n");
4632+ ib_page_ibuf_bitmap_print(page, zip_size);
4633+ break;
4634+ }
4635+ case FIL_PAGE_UNDO_LOG:
4636+ {
4637+ ib_page_print_undo(page);
4638+ break;
4639+ }
4640+ case FIL_PAGE_TYPE_BLOB:
4641+ {
4642+ ib_page_print_blob(page);
4643+ break;
4644+ }
4645+ case FIL_PAGE_TYPE_ZBLOB:
4646+ {
4647+ ib_page_print_zblob(page, zip_size);
4648+ break;
4649+ }
4650+ case FIL_PAGE_TYPE_ZBLOB2:
4651+ {
4652+ ib_page_print_zblob2(page, zip_size);
4653+ break;
4654+ }
4655+ case FIL_PAGE_TYPE_FSP_HDR:
4656+ {
4657+ ib_page_print_fsp_header(page);
4658+ break;
4659+ }
4660+ case FIL_PAGE_INODE:
4661+ {
4662+ ib_page_print_inode(page, zip_size);
4663+ break;
4664+ }
4665+ case FIL_PAGE_TYPE_XDES:
4666+ {
4667+ ib_page_print_xdes(page, zip_size);
4668+ break;
4669+ }
4670+ case FIL_PAGE_TYPE_SYS:
4671+ {
4672+ if (page_get_page_no(page) == DICT_HDR_PAGE_NO)
4673+ {
4674+ ib_page_print_dict_header(page);
4675+ }
4676+ }
4677+ }
4678+ }
4679+ ib_logger(ib_stream, "PAGE PRINT END\n");
4680+
4681+ return DB_SUCCESS;
4682+}
4683+
4684+/*******************************************************************//**
4685+Print page stored in file. */
4686+ib_err_t
4687+ib_page_print_file(
4688+ const char* ibd_file_name, /*!< in: file name */
4689+ ulint page_no, /*!< in: number of page to print */
4690+ ibool print_contents) /*!< in: TRUE if page print contents */
4691+{
4692+ ib_err_t err;
4693+ mem_heap_t* heap = mem_heap_create(100);
4694+ ulint zip_size;
4695+ page_t* compressed_page;
4696+ page_t* decompressed_page;
4697+
4698+ if (!ib_page_load(ibd_file_name, page_no, &zip_size,
4699+ &compressed_page, &decompressed_page, heap))
4700+ {
4701+ mem_heap_free(heap);
4702+ return DB_ERROR;
4703+ }
4704+
4705+ if (!print_contents)
4706+ {
4707+ ib_logger(ib_stream, "Print page header only\n");
4708+ }
4709+
4710+ page_t* page = decompressed_page;
4711+
4712+ err = ib_page_print_mem(page, zip_size, print_contents);
4713+
4714+ mem_heap_free(heap);
4715+
4716+ return err;
4717+}
4718+
4719+/*******************************************************************//**
4720+Format SYS value. */
4721+UNIV_INTERN
4722+void
4723+ib_page_format_sys(
4724+/*===============*/
4725+ ulint prtype, /*!< in: precise type */
4726+ char* buf, /*!< out: buffer */
4727+ ulint buf_len, /*!< in: buffer length */
4728+ const byte* data) /*!< in: field data */
4729+{
4730+ dulint id;
4731+ switch (prtype & DATA_SYS_PRTYPE_MASK) {
4732+ case DATA_TRX_ID:
4733+ id = mach_read_from_6(data);
4734+
4735+ snprintf(buf, buf_len, "trx_id " TRX_ID_FMT,
4736+ TRX_ID_PREP_PRINTF(id));
4737+ break;
4738+
4739+ case DATA_ROLL_PTR:
4740+ id = mach_read_from_7(data);
4741+
4742+ snprintf(buf, buf_len, "roll_ptr {%lu %lu}",
4743+ ut_dulint_get_high(id), ut_dulint_get_low(id));
4744+ break;
4745+
4746+ case DATA_ROW_ID:
4747+ id = mach_read_from_6(data);
4748+
4749+ snprintf(buf, buf_len, "row_id {%lu %lu}",
4750+ ut_dulint_get_high(id), ut_dulint_get_low(id));
4751+ break;
4752+
4753+ default:
4754+ id = mach_dulint_read_compressed(data);
4755+
4756+ snprintf(buf, buf_len, "mix_id {%lu %lu}",
4757+ ut_dulint_get_high(id), ut_dulint_get_low(id));
4758+ }
4759+}
4760+
4761+/*******************************************************************//**
4762+Format INT value. */
4763+UNIV_INTERN
4764+void
4765+ib_page_format_int(
4766+/*===============*/
4767+ ulint prtype, /*!< in: precise type */
4768+ char* buf, /*!< out: buffer */
4769+ ulint buf_len, /*!< in: buffer length */
4770+ const byte* data, /*!< in: field data */
4771+ ulint data_len) /*!< in: length of data */
4772+{
4773+ ulint val;
4774+ dulint id;
4775+ switch (data_len) {
4776+ case 1:
4777+ val = mach_read_from_1(data);
4778+
4779+ if (!(prtype & DATA_UNSIGNED)) {
4780+ val &= ~0x80;
4781+ snprintf(buf, buf_len, "%ld", (long) val);
4782+ } else {
4783+ snprintf(buf, buf_len, "%lu", (ulong) val);
4784+ }
4785+ break;
4786+
4787+ case 2:
4788+ val = mach_read_from_2(data);
4789+
4790+ if (!(prtype & DATA_UNSIGNED)) {
4791+ val &= ~0x8000;
4792+ snprintf(buf, buf_len, "%ld", (long) val);
4793+ } else {
4794+ snprintf(buf, buf_len, "%lu", (ulong) val);
4795+ }
4796+ break;
4797+
4798+ case 3:
4799+ val = mach_read_from_3(data);
4800+
4801+ if (!(prtype & DATA_UNSIGNED)) {
4802+ val &= ~0x800000;
4803+ snprintf(buf, buf_len, "%ld", (long) val);
4804+ } else {
4805+ snprintf(buf, buf_len, "%lu", (ulong) val);
4806+ }
4807+ break;
4808+
4809+ case 4:
4810+ val = mach_read_from_4(data);
4811+
4812+ if (!(prtype & DATA_UNSIGNED)) {
4813+ val &= ~0x80000000;
4814+ snprintf(buf, buf_len, "%ld", (long) val);
4815+ } else {
4816+ snprintf(buf, buf_len, "%lu", (ulong) val);
4817+ }
4818+ break;
4819+
4820+ case 6:
4821+ id = mach_read_from_6(data);
4822+ snprintf(buf, buf_len, "{%lu %lu}",
4823+ ut_dulint_get_high(id),
4824+ ut_dulint_get_low(id));
4825+ break;
4826+
4827+ case 7:
4828+ id = mach_read_from_7(data);
4829+ snprintf(buf, buf_len, "{%lu %lu}",
4830+ ut_dulint_get_high(id),
4831+ ut_dulint_get_low(id));
4832+ break;
4833+ case 8:
4834+ id = mach_read_from_8(data);
4835+ snprintf(buf, buf_len, "{%lu %lu}",
4836+ ut_dulint_get_high(id),
4837+ ut_dulint_get_low(id));
4838+ break;
4839+ default:
4840+ break;
4841+ }
4842+}
4843+
4844+
4845+/*******************************************************************//**
4846+Format string value. */
4847+UNIV_INTERN
4848+void
4849+ib_page_format_str(
4850+/*===============*/
4851+ char* buf, /*!< out: buffer */
4852+ ulint buf_len, /*!< in: buffer length */
4853+ const byte* data, /*!< in: field data */
4854+ ulint data_len) /*!< in: length of data */
4855+{
4856+ if (data_len >= buf_len) {
4857+ strncpy(buf, (const char*)data, buf_len);
4858+ buf[buf_len - 4]= '.';
4859+ buf[buf_len - 3]= '.';
4860+ buf[buf_len - 2]= '.';
4861+ buf[buf_len - 1]= 0;
4862+ } else if (data_len != UNIV_SQL_NULL) {
4863+ strncpy(buf, (const char*)data, data_len);
4864+ buf[data_len]= 0;
4865+ }
4866+}
4867+
4868+
4869+/*******************************************************************//**
4870+Format binalry value. */
4871+UNIV_INTERN
4872+void ib_page_format_bin(
4873+/*====================*/
4874+ char* buf, /*!< out: buffer */
4875+ ulint buf_len, /*!< in: buffer length */
4876+ const byte* data, /*!< in: field data */
4877+ ulint data_len) /*!< in: length of data */
4878+{
4879+ ulint i;
4880+ for (i = 0; i < data_len * 2 && i + 2 < buf_len; i += 2)
4881+ sprintf(buf + i, "%02x", data[i / 2]);
4882+ if (data_len * 2 >= buf_len) {
4883+ buf[buf_len - 5]= '.';
4884+ buf[buf_len - 4]= '.';
4885+ buf[buf_len - 3]= '.';
4886+ buf[buf_len - 2]= '.';
4887+ }
4888+ buf[buf_len - 1]= 0;
4889+}
4890+
4891+
4892+/***************************************************************//**
4893+Hex dump of page. */
4894+UNIV_INTERN
4895+void
4896+ib_page_print_hex_dump(
4897+/*===================*/
4898+ const page_t* page, /*!< in: page */
4899+ ulint zip_size) /*!< in: compressed page size, or
4900+ 0 for uncompressed pages */
4901+{
4902+ const byte* s = page;
4903+ ulint addr;
4904+ const ulint width = 32; /* bytes per line */
4905+ ulint size;
4906+
4907+ size = zip_size ? zip_size : UNIV_PAGE_SIZE;
4908+
4909+ for (addr = 0; addr < size; addr += width) {
4910+ ulint i;
4911+
4912+ ib_logger(ib_stream, "%04lx ", (ulong) addr);
4913+
4914+ i = ut_min(width, size - addr);
4915+
4916+ while (i--) {
4917+ ib_logger(ib_stream, "%02x", *s++);
4918+ }
4919+
4920+ ib_logger(ib_stream, "\n");
4921+ }
4922+}
4923+
4924+
4925+/***************************************************************//**
4926+Format externally stored field. */
4927+UNIV_INTERN
4928+void
4929+ib_page_format_ext(
4930+/*===============*/
4931+ char* buf, /*!< out: buffer */
4932+ ulint buf_len, /*!< in: buffer length */
4933+ const rec_t* rec, /*!< in: physical record */
4934+ ulint no, /*!< in: field number */
4935+ const ulint* offsets) /*!< in: array returned by
4936+ rec_get_offsets() */
4937+{
4938+ char local_data[30];
4939+ ulint local_len;
4940+ ulint space_id;
4941+ ulint page_no;
4942+ ulint offset;
4943+ ulint extern_len;
4944+ const byte* data;
4945+
4946+ ut_a(rec_offs_nth_extern(offsets, no));
4947+
4948+ /* An externally stored field can contain some initial
4949+ data from the field, and in the last 20 bytes it has the
4950+ space id, page number, and offset where the rest of the
4951+ field data is stored, and the data length in addition to
4952+ the data stored locally. We may need to store some data
4953+ locally to get the local record length above the 128 byte
4954+ limit so that field offsets are stored in two bytes, and
4955+ the extern bit is available in those two bytes. */
4956+
4957+ data = rec_get_nth_field(rec, offsets, no, &local_len);
4958+
4959+ ib_page_format_bin(local_data, 30, data, local_len);
4960+
4961+ local_len -= BTR_EXTERN_FIELD_REF_SIZE;
4962+
4963+ space_id = mach_read_from_4(data + local_len + BTR_EXTERN_SPACE_ID);
4964+
4965+ page_no = mach_read_from_4(data + local_len + BTR_EXTERN_PAGE_NO);
4966+
4967+ offset = mach_read_from_4(data + local_len + BTR_EXTERN_OFFSET);
4968+
4969+ /* Currently a BLOB cannot be bigger than 4 GB; we
4970+ leave the 4 upper bytes in the length field unused */
4971+
4972+ extern_len = mach_read_from_4(data + local_len + BTR_EXTERN_LEN + 4);
4973+
4974+ snprintf(buf, buf_len,
4975+ "(loc len %lu, space %lu, page %lu,"
4976+ " offs %lu, ext len %lu) %s",
4977+ local_len, space_id, page_no,
4978+ offset, extern_len, local_data);
4979+
4980+}
4981+
4982+/*******************************************************************//**
4983+Print field. */
4984+UNIV_INTERN
4985+void
4986+ib_page_rec_print_field(
4987+/*====================*/
4988+ const dfield_t* field, /*!< in: field */
4989+ const rec_t* rec, /*!< in: physical record */
4990+ ulint no, /*!< in: field number */
4991+ const ulint* offsets) /*!< in: array returned by
4992+ rec_get_offsets() */
4993+{
4994+ enum { BUF_LEN = 80 };
4995+ char buf[BUF_LEN];
4996+ char type_buf[BUF_LEN];
4997+ const dtype_t* type;
4998+ const byte* data;
4999+ ulint len;
5000+ ulint mtype;
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches