diff_tables.inc 6.6 KB
Newer Older
H
hustjieke 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
# ==== Purpose ====
#
# Check if all tables in the given list are equal. The tables may have
# different names, exist in different connections, and/or reside in
# different databases.
#
#
# ==== Usage ====
#
# --let $diff_tables= [con1:][db1.]t1, [con2:][db2.]t2, ... , [conN:][dbN.]tN
# [--let $rpl_debug= 1]
# [--let $diff_tables_silent= 1]
# --source include/diff_tables.inc
#
# Parameters:
#   $diff_tables
#     Comma-separated list of tables to compare. Each table has the form
#
#       [CONNECTION:][DATABASE.]table
#
#     If CONNECTION is given, then that connection is used. If
#     CONNECTION is not given, then the connection of the previous
#     table is used (or the current connection, if this is the first
#     table).  If DATABASE is given, the table is read in that
#     database. If DATABASE is not given, the table is read in the
#     connection's current database.
#
#   $diff_tables_silent
#     Do not print table names to result log.
#
#   $rpl_debug
#     See include/rpl_init.inc
#
#
# ==== Side effects ====
#
# - Prints "include/diff_tables.inc [$diff_tables]".
#
# - If the tables are different, prints the difference in a
#   system-specific format (unified diff if supported) and generates
#   an error.
#
#
# ==== Bugs ====
#
# - It is currently not possible to use this for tables that are
#   supposed to be different, because if the files are different:
#    - 'diff' produces system-dependent output,
#    - the output includes the absolute path of the compared files,
#    - the output includes a timestamp.
#   To fix that, we'd probably have to use SQL to compute the
#   symmetric difference between the tables.  I'm not sure how to do
#   that efficiently.  If we implement this, it would be nice to
#   compare the table definitions too.
#
# - It actually compares the result of "SELECT * FROM table ORDER BY
#   col1, col2, ..., colN INTO OUTFILE 'file'".  Hence, it is assumed
#   that the comparison orders for both tables are equal and that two
#   rows that are equal in the comparison order cannot differ, e.g.,
#   by character case.


--let $include_filename= diff_tables.inc
if (!$diff_tables_silent)
{
  --let $include_filename= diff_tables.inc [$diff_tables]
}
--source include/begin_include_file.inc


if (!$rpl_debug)
{
  --disable_query_log
}


# Check sanity
if (`SELECT LOCATE(',', '$diff_tables') = 0`)
{
  --die ERROR IN TEST: $diff_tables must contain at least two tables (separated by comma)
}


# ==== Save both tables to file ====

# Trim off whitespace
--let $_dt_tables= `SELECT REPLACE('$diff_tables', ' ', '')`

# Iterate over all tables
--let $_dt_outfile=
--let $_dt_prev_outfile=
while ($_dt_tables)
{
  --let $_dt_table= `SELECT SUBSTRING_INDEX('$_dt_tables', ',', 1)`
  --let $_dt_tables= `SELECT SUBSTRING('$_dt_tables', LENGTH('$_dt_table') + 2)`

  # Parse connection, if any
  --let $_dt_colon_index= `SELECT LOCATE(':', '$_dt_table')`
  if ($_dt_colon_index)
  {
    --let $_dt_connection= `SELECT SUBSTRING('$_dt_table', 1, $_dt_colon_index - 1)`
    --let $_dt_table= `SELECT SUBSTRING('$_dt_table', $_dt_colon_index + 1)`
    --let $rpl_connection_name= $_dt_connection
    --source include/rpl_connection.inc
  }

  # Parse database name, if any
  --let $_dt_database_index= `SELECT LOCATE('.', '$_dt_table')`
  if ($_dt_database_index)
  {
    --let $_dt_database= `SELECT SUBSTRING('$_dt_table', 1, $_dt_database_index - 1)`
    --let $_dt_table= `SELECT SUBSTRING('$_dt_table', $_dt_database_index + 1)`
  }
  if (!$_dt_database_index)
  {
    --let $_dt_database= `SELECT DATABASE()`
  }

  if ($rpl_debug)
  {
    --echo con='$_dt_connection' db='$_dt_database' table='$_dt_table'
    --echo rest of tables='$_dt_tables'
  }

  # We need to sort the output files so that diff_files does not think
  # the tables are different just because the rows are differently
  # ordered.  To this end, we first generate a string containing a
  # comma-separated list of all column names. This is used in the
  # ORDER BY clause of the following SELECT statement. We get the
  # column names from INFORMATION_SCHEMA.COLUMNS, and we concatenate
  # them with GROUP_CONCAT. Since GROUP_CONCAT is limited by the
  # @@SESSION.group_concat_max_len, which is only 1024 by default, we
  # first compute the total size of all columns and then increase this
  # limit if needed. We restore the limit afterwards so as not to
  # interfere with the test case.

  # Compute length of ORDER BY clause.
  let $_dt_order_by_length=
    `SELECT SUM(LENGTH(column_name) + 3) FROM information_schema.columns
            WHERE table_schema = '$_dt_database' AND table_name = '$_dt_table'`;
  if (!$_dt_order_by_length)
  {
    --echo ERROR IN TEST: table $_dt_database.$_dt_table not found in INFORMATION_SCHEMA.COLUMNS. Did you misspell it?
    --die ERROR IN TEST: table not found in INFORMATION_SCHEMA. Did you misspell it?
  }
  --let $_dt_old_group_concat_max_len=
  # Increase group_concat_max_len if needed.
  if (`SELECT $_dt_order_by_length > @@SESSION.group_concat_max_len`)
  {
    --let $_dt_old_group_concat_max_len= `SELECT @@SESSION.group_concat_max_len`
    --eval SET SESSION group_concat_max_len = $_dt_order_by_length;
    if ($rpl_debug)
    {
      --echo # increasing group_concat_max_len from $_dt_old_group_concat_max_len to $_dt_order_by_length
    }
  }
  # Generate ORDER BY clause.
  # It would be better to do GROUP_CONCAT(CONCAT('`', column_name, '`')) but
  # BUG#58087 prevents us from returning strings that begin with backticks.
  let $_dt_column_list=
    `SELECT GROUP_CONCAT(column_name ORDER BY ORDINAL_POSITION SEPARATOR '`,`')
            FROM information_schema.columns
            WHERE table_schema = '$_dt_database' AND table_name = '$_dt_table'`;
  # Restore group_concat_max_len.
  if ($_dt_old_group_concat_max_len)
  {
    --let $_dt_dummy= `SET SESSION group_concat_max_len = $_dt_old_group_concat_max_len
  }
  if ($rpl_debug)
  {
    --echo using ORDER BY clause '`$_dt_column_list`'
  }

  # Now that we have the comma-separated list of columns, we can write
  # the table to a file.
  --let $_dt_outfile= `SELECT @@datadir`
  --let $_dt_outfile= $_dt_outfile/diff_table-$_dt_connection-$_dt_database-$_dt_table
  eval SELECT * FROM $_dt_database.$_dt_table ORDER BY `$_dt_column_list` INTO OUTFILE '$_dt_outfile';

  # Compare files.
  if ($_dt_prev_outfile)
  {
    if ($rpl_debug)
    {
      --echo # diffing $_dt_prev_outfile vs $_dt_outfile
    }
    --diff_files $_dt_prev_outfile $_dt_outfile
    # Remove previous outfile. Keep current file for comparison with next table.
    --remove_file $_dt_prev_outfile
  }
  --let $_dt_prev_outfile= $_dt_outfile
}

--remove_file $_dt_prev_outfile


--let $include_filename= diff_tables.inc [$diff_tables]
--source include/end_include_file.inc