################################################################################
#
# Verifies that queries in a REPEATABLE READ transaction is indeed repeatable.
# Wrong results are shown as a result set based on one or more specially crafted
# queries. Normally these result sets should be empty.
#
# We want to verify that reads (SELECTs) are indeed repeatable during a 
# REPEATABLE READ transaction.
#
# Generally, queries which should yield the same results at one moment in time 
# should also yield the same results later in the same transaction. In some 
# configurations, however, phantom reads are allowed (may e.g. depend on 
# settings such as falcon_consistent_read).
#
# The check will fail if rows are changed or missing when comparing later
# queries to earlier ones.
# TODO: Phantom reads.
#       Note: There is a separate test looking for `is_uncommitted` = 1.
#
# Assumptions:
#   - we are in a REPEATABLE READ transaction with autocommit OFF.
#   - queries include all columns of table (t1) (we SELECT columns by name)
#
# Requires/using the following variables:
#   $query_count - the number of queries to compare.
#                  Will also be used to deduce the name of the temp table in
#                  which the query results should be stored (see 
#                  record_query_all_columns.inc).
#
################################################################################

# Show results of next queries. Empty results is OK. Non-empty means failure.
--enable_result_log

# The mysqltest language is unfortunaltely not very flexible, but we try our
# best to compare query results this way:
#   - For each query, compare with previous query
#     - this requires that at least 2 queries have been stored
#   - Number of queries should be stored as $query_count
#   - Results should be stored in temp tables with names ending with the query
#     number, and with prefix "tmp". 
#   - E.g. compare "tmp2" with "tmp1", "tmp3" with "tmp2", "tmp4" with "tmp3" etc.
#   - Fail the test once we detect changed or missing or invalid extra rows in
#     latter query.
#   ?????
#   - Problem is that if one of the queries deadlocked or timed out, we may not
#     have enough result sets to compare, so output will vary depending on this.
#     Still we need the output from these checks to see which rows are missing or
#     changed.
#     So, if we don't have enough queries we fake "correct output" to make mysqltest
#     happy.
#
# Unfortunately, we need to utilize SQL and spend client-server roundtrips
# in order to do some computations that the mysqltest language does not handle.
# We try to use mysqltest variables instead where possible, as this should be 
# less expensive in terms of CPU usage and time spenditure.

#
# First, check that we have at least two query results stored.
# We need at least 2 to be able to compare.
# Some results may not have been stored due to locking errors (see record_query_all_columns.inc), so
# we cannot assume that we always have at least 2 query results stored.
# If less than 2 query results are stored, return to calling test/script.
#
if (`SELECT IF($query_count > 1, 1, 0)`)
{
    
    --echo ***************************************************************************
    --echo * Checking REPEATABLE READ by comparing result sets from same transaction
    --echo ***************************************************************************

    --echo *** Query log disabled. See include files used by test for query details.
    --disable_query_log

    let $queryA= 1;
    let $queryB= 2;
    let $more_queries= $query_count;

    # We start out by comparing the first 2 queries, so the while loop should run
    # $query_count - 1 times. (If we have 3 queries, compare 1 with 2, 2 and 3).
    --dec $more_queries

    while ($more_queries)
    {
        # We still have one or more queries that have not been compared to the
        # previous query.
        # Compare queryB ("current query") with queryA ("previous query")

        #--source suite/stress_tx_rr/include/compare_queries_with_pk.inc

        let $tableA= tmp$queryA;
        let $tableB= tmp$queryB;

        --echo *** Comparing query $queryA (A) with query $queryB (B):

        #
        # In the following queries, 'SELECT * ...' could have been used instead of
        # 'SELECT tmp1.pk AS ...' etc., but the latter makes it easier to compare the first
        # result set to the second in test/diff output.



        ###########################
        # Detect extra rows:
        # Allow phantoms in some configurations:
        #   - InnoDB default settings
        #   - Falcon's falcon_consistent_read=0 (non-default setting)
        #   (TODO: What about PBXT?)
        #
        ###########################
        # TODO: Execute a query against tmp1 and tmp2 which selects new rows (rows
        #       present in tmp2 that are not present in tmp1) that are of the uncommitted
        #       variety (field `is_uncommitted` = 1).
        # E.g. something like:
        #  SELECT ...
        #   FROM tmp2 LEFT JOIN tmp1
        #   ON tmp1.`pk` = tmp2.`pk`
        #   WHERE tmp1.`int1` IS NULL
        #   OR tmp1.`int1_key` IS NULL
        #   OR tmp1.`int1_unique` IS NULL
        #   OR tmp1.`int2` IS NULL
        #   OR tmp1.`int2_key` IS NULL
        #   OR tmp1.`int2_unique` IS NULL
        #   AND tmp2.`is_uncommitted` = 1;


        --echo ###########################
        --echo # Detect missing rows:
        --echo ###########################


        eval SELECT    $tableA.pk AS 'A.pk',
                      $tableB.pk AS 'B.pk',
                      $tableA.id AS 'A.id',
                      $tableB.id AS 'B.id',
                      $tableA.`int1` AS 'A.int1',
                      $tableB.`int1` AS 'B.int1',
                      $tableA.`int1_key` AS 'A.int1_key',
                      $tableB.`int1_key` AS 'B.int1_key',
                      $tableA.`int1_unique` AS 'A.int1_unique',
                      $tableB.`int1_unique` AS 'B.int1_unique',
                      $tableA.`int2` AS 'A.int2',
                      $tableB.`int2` AS 'B.int2',
                      $tableA.`int2_key` AS 'A.int2_key',
                      $tableB.`int2_key` AS 'B.int2_key',
                      $tableA.`int2_unique` AS 'A.int2_unique',
                      $tableB.`int2_unique` AS 'B.int2_unique',
                      $tableA.`for_update` AS 'A.for_update',
                      $tableB.`for_update` AS 'B.for_update',
                      $tableA.timestamp AS 'A.timestamp',
                      $tableB.timestamp AS 'B.timestamp',
                      $tableA.`connection_id` AS 'A.connection_id',
                      $tableB.`connection_id` AS 'B.connection_id',
                      $tableA.`thread_id` AS 'A.thread_id',
                      $tableB.`thread_id` AS 'B.thread_id',
                      $tableA.`is_uncommitted` AS 'A.is_uncommitted',
                      $tableB.`is_uncommitted` AS 'B.is_uncommitted',
                      $tableA.`is_consistent` AS 'A.is_consistent',
                      $tableB.`is_consistent` AS 'B.is_consistent'
                FROM $tableA LEFT JOIN $tableB
                  ON $tableA.`pk` = $tableB.`pk`
                WHERE $tableB.`pk` IS NULL;

    #
    #               OR $tableB.`int1_key` IS NULL
    #               OR $tableB.`int1_unique` IS NULL
    #               OR $tableB.`int2` IS NULL
    #               OR $tableB.`int2_key` IS NULL
    #               OR $tableB.`int2_unique` IS NULL;

        --echo
        --echo ###########################
        --echo # Detect changed rows:
        --echo ###########################

        eval SELECT  $tableA.pk AS 'A.pk',
                      $tableB.pk AS 'B.pk',
                      $tableA.id AS 'A.id',
                      $tableB.id AS 'B.id',
                      $tableA.`int1` AS 'A.int1',
                      $tableB.`int1` AS 'B.int1',
                      $tableA.`int1_key` AS 'A.int1_key',
                      $tableB.`int1_key` AS 'B.int1_key',
                      $tableA.`int1_unique` AS 'A.int1_unique',
                      $tableB.`int1_unique` AS 'B.int1_unique',
                      $tableA.`int2` AS 'A.int2',
                      $tableB.`int2` AS 'B.int2',
                      $tableA.`int2_key` AS 'A.int2_key',
                      $tableB.`int2_key` AS 'B.int2_key',
                      $tableA.`int2_unique` AS 'A.int2_unique',
                      $tableB.`int2_unique` AS 'B.int2_unique',
                      $tableA.`for_update` AS 'A.for_update',
                      $tableB.`for_update` AS 'B.for_update',
                      $tableA.timestamp AS 'A.timestamp',
                      $tableB.timestamp AS 'B.timestamp',
                      $tableA.`connection_id` AS 'A.connection_id',
                      $tableB.`connection_id` AS 'B.connection_id',
                      $tableA.`thread_id` AS 'A.thread_id',
                      $tableB.`thread_id` AS 'B.thread_id',
                      $tableA.`is_uncommitted` AS 'A.is_uncommitted',
                      $tableB.`is_uncommitted` AS 'B.is_uncommitted',
                      $tableA.`is_consistent` AS 'A.is_consistent',
                      $tableB.`is_consistent` AS 'B.is_consistent'
                FROM $tableB INNER JOIN $tableA
                  ON $tableB.`pk` = $tableA.`pk`
                WHERE $tableB.`int1` <> $tableA.`int1`
                  OR $tableB.`int1_key` <> $tableA.`int1_key`
                  OR $tableB.`int1_unique` <> $tableA.`int1_unique`
                  OR $tableB.`int2` <> $tableA.`int2`
                  OR $tableB.`int2_key` <> $tableA.`int2_key`
                  OR $tableB.`int2_unique` <> $tableA.`int2_unique`;

        --dec $more_queries
        --inc $queryA
        --inc $queryB

    }
    --enable_query_log
}


## Cleanup is skipped because temporary tables and prepared statements will
## be cleaned up automatically by the server when this session ends, and we
## want to have as few client-server roundtrips as possible (thus avoid 
## unnecessary SQL statement executions).
