MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
diff_tables.inc
1 # ==== Purpose ====
2 #
3 # Check if all tables in the given list are equal. The tables may have
4 # different names, exist in different connections, and/or reside in
5 # different databases.
6 #
7 #
8 # ==== Usage ====
9 #
10 # --let $diff_tables= [con1:][db1.]t1, [con2:][db2.]t2, ... , [conN:][dbN.]tN
11 # [--let $rpl_debug= 1]
12 # [--let $diff_tables_silent= 1]
13 # --source include/diff_tables.inc
14 #
15 # Parameters:
16 # $diff_tables
17 # Comma-separated list of tables to compare. Each table has the form
18 #
19 # [CONNECTION:][DATABASE.]table
20 #
21 # If CONNECTION is given, then that connection is used. If
22 # CONNECTION is not given, then the connection of the previous
23 # table is used (or the current connection, if this is the first
24 # table). If DATABASE is given, the table is read in that
25 # database. If DATABASE is not given, the table is read in the
26 # connection's current database.
27 #
28 # $diff_tables_silent
29 # Do not print table names to result log.
30 #
31 # $rpl_debug
32 # See include/rpl_init.inc
33 #
34 #
35 # ==== Side effects ====
36 #
37 # - Prints "include/diff_tables.inc [$diff_tables]".
38 #
39 # - If the tables are different, prints the difference in a
40 # system-specific format (unified diff if supported) and generates
41 # an error.
42 #
43 #
44 # ==== Bugs ====
45 #
46 # - It is currently not possible to use this for tables that are
47 # supposed to be different, because if the files are different:
48 # - 'diff' produces system-dependent output,
49 # - the output includes the absolute path of the compared files,
50 # - the output includes a timestamp.
51 # To fix that, we'd probably have to use SQL to compute the
52 # symmetric difference between the tables. I'm not sure how to do
53 # that efficiently. If we implement this, it would be nice to
54 # compare the table definitions too.
55 #
56 # - It actually compares the result of "SELECT * FROM table ORDER BY
57 # col1, col2, ..., colN INTO OUTFILE 'file'". Hence, it is assumed
58 # that the comparison orders for both tables are equal and that two
59 # rows that are equal in the comparison order cannot differ, e.g.,
60 # by character case.
61 
62 
63 --let $include_filename= diff_tables.inc
64 if (!$diff_tables_silent)
65 {
66  --let $include_filename= diff_tables.inc [$diff_tables]
67 }
68 --source include/begin_include_file.inc
69 
70 
71 if (!$rpl_debug)
72 {
73  --disable_query_log
74 }
75 
76 
77 # Check sanity
78 if (`SELECT LOCATE(',', '$diff_tables') = 0`)
79 {
80  --die ERROR IN TEST: $diff_tables must contain at least two tables (separated by comma)
81 }
82 
83 
84 # ==== Save both tables to file ====
85 
86 # Trim off whitespace
87 --let $_dt_tables= `SELECT REPLACE('$diff_tables', ' ', '')`
88 
89 # Iterate over all tables
90 --let $_dt_outfile=
91 --let $_dt_prev_outfile=
92 while ($_dt_tables)
93 {
94  --let $_dt_table= `SELECT SUBSTRING_INDEX('$_dt_tables', ',', 1)`
95  --let $_dt_tables= `SELECT SUBSTRING('$_dt_tables', LENGTH('$_dt_table') + 2)`
96 
97  # Parse connection, if any
98  --let $_dt_colon_index= `SELECT LOCATE(':', '$_dt_table')`
99  if ($_dt_colon_index)
100  {
101  --let $_dt_connection= `SELECT SUBSTRING('$_dt_table', 1, $_dt_colon_index - 1)`
102  --let $_dt_table= `SELECT SUBSTRING('$_dt_table', $_dt_colon_index + 1)`
103  --let $rpl_connection_name= $_dt_connection
104  --source include/rpl_connection.inc
105  }
106 
107  # Parse database name, if any
108  --let $_dt_database_index= `SELECT LOCATE('.', '$_dt_table')`
109  if ($_dt_database_index)
110  {
111  --let $_dt_database= `SELECT SUBSTRING('$_dt_table', 1, $_dt_database_index - 1)`
112  --let $_dt_table= `SELECT SUBSTRING('$_dt_table', $_dt_database_index + 1)`
113  }
114  if (!$_dt_database_index)
115  {
116  --let $_dt_database= `SELECT DATABASE()`
117  }
118 
119  if ($rpl_debug)
120  {
121  --echo con='$_dt_connection' db='$_dt_database' table='$_dt_table'
122  --echo rest of tables='$_dt_tables'
123  }
124 
125  # We need to sort the output files so that diff_files does not think
126  # the tables are different just because the rows are differently
127  # ordered. To this end, we first generate a string containing a
128  # comma-separated list of all column names. This is used in the
129  # ORDER BY clause of the following SELECT statement. We get the
130  # column names from INFORMATION_SCHEMA.COLUMNS, and we concatenate
131  # them with GROUP_CONCAT. Since GROUP_CONCAT is limited by the
132  # @@SESSION.group_concat_max_len, which is only 1024 by default, we
133  # first compute the total size of all columns and then increase this
134  # limit if needed. We restore the limit afterwards so as not to
135  # interfere with the test case.
136 
137  # Compute length of ORDER BY clause.
138  let $_dt_order_by_length=
139  `SELECT SUM(LENGTH(column_name) + 3) FROM information_schema.columns
140  WHERE table_schema = '$_dt_database' AND table_name = '$_dt_table'`;
141  if (!$_dt_order_by_length)
142  {
143  --echo ERROR IN TEST: table $_dt_database.$_dt_table not found in INFORMATION_SCHEMA.COLUMNS. Did you misspell it?
144  --die ERROR IN TEST: table not found in INFORMATION_SCHEMA. Did you misspell it?
145  }
146  --let $_dt_old_group_concat_max_len=
147  # Increase group_concat_max_len if needed.
148  if (`SELECT $_dt_order_by_length > @@SESSION.group_concat_max_len`)
149  {
150  --let $_dt_old_group_concat_max_len= `SELECT @@SESSION.group_concat_max_len`
151  --eval SET SESSION group_concat_max_len = $_dt_order_by_length;
152  if ($rpl_debug)
153  {
154  --echo # increasing group_concat_max_len from $_dt_old_group_concat_max_len to $_dt_order_by_length
155  }
156  }
157  # Generate ORDER BY clause.
158  # It would be better to do GROUP_CONCAT(CONCAT('`', column_name, '`')) but
159  # BUG#58087 prevents us from returning strings that begin with backticks.
160  let $_dt_column_list=
161  `SELECT GROUP_CONCAT(column_name ORDER BY ORDINAL_POSITION SEPARATOR '`,`')
162  FROM information_schema.columns
163  WHERE table_schema = '$_dt_database' AND table_name = '$_dt_table'`;
164  # Restore group_concat_max_len.
165  if ($_dt_old_group_concat_max_len)
166  {
167  --let $_dt_dummy= `SET SESSION group_concat_max_len = $_dt_old_group_concat_max_len
168  }
169  if ($rpl_debug)
170  {
171  --echo using ORDER BY clause '`$_dt_column_list`'
172  }
173 
174  # Now that we have the comma-separated list of columns, we can write
175  # the table to a file.
176  --let $_dt_outfile= `SELECT @@datadir`
177  --let $_dt_outfile= $_dt_outfile/diff_table-$_dt_connection-$_dt_database-$_dt_table
178  eval SELECT * FROM $_dt_database.$_dt_table ORDER BY `$_dt_column_list` INTO OUTFILE '$_dt_outfile';
179 
180  # Compare files.
181  if ($_dt_prev_outfile)
182  {
183  if ($rpl_debug)
184  {
185  --echo # diffing $_dt_prev_outfile vs $_dt_outfile
186  }
187  --diff_files $_dt_prev_outfile $_dt_outfile
188  # Remove previous outfile. Keep current file for comparison with next table.
189  --remove_file $_dt_prev_outfile
190  }
191  --let $_dt_prev_outfile= $_dt_outfile
192 }
193 
194 --remove_file $_dt_prev_outfile
195 
196 
197 --let $include_filename= diff_tables.inc [$diff_tables]
198 --source include/end_include_file.inc