• Dmitry Lenev's avatar
    A 5.1-only version of fix for bug #46947 "Embedded SELECT · 59d58220
    Dmitry Lenev authored
    without FOR UPDATE is causing a lock".
    
    SELECT statements with subqueries referencing InnoDB tables
    were acquiring shared locks on rows in these tables when they
    were executed in REPEATABLE-READ mode and with statement or
    mixed mode binary logging turned on.
    
    This was a regression which were introduced when fixing
    bug 39843.
    
    The problem was that for tables belonging to subqueries
    parser set TL_READ_DEFAULT as a lock type. In cases when
    statement/mixed binary logging at open_tables() time this
    type of lock was converted to TL_READ_NO_INSERT lock at
    open_tables() time and caused InnoDB engine to acquire
    shared locks on reads from these tables. Although in some
    cases such behavior was correct (e.g. for subqueries in
    DELETE) in case of SELECT it has caused unnecessary locking.
    
    This patch implements minimal version of the fix for the
    specific problem described in the bug-report which supposed
    to be not too risky for pushing into 5.1 tree.
    The 5.5 tree already contains a more appropriate solution
    which also addresses other related issues like bug 53921
    "Wrong locks for SELECTs used stored functions may lead
    to broken SBR".
    
    This patch tries to solve the problem by ensuring that
    TL_READ_DEFAULT lock which is set in the parser for
    tables participating in subqueries at open_tables()
    time is interpreted as TL_READ_NO_INSERT or TL_READ.
    TL_READ is used only if we know that this is a SELECT
    and that this particular table is not used by a stored
    function.
    
    Test coverage is added for both InnoDB and MyISAM.
    
    This patch introduces an "incompatible" change in locking
    scheme for subqueries used in SELECT ... FOR UPDATE and
    SELECT .. IN SHARE MODE.
    
    In 4.1 (as well as in 5.0 and 5.1 before fix for bug 39843)
    the server would use a snapshot InnoDB read for subqueries
    in SELECT FOR UPDATE and SELECT .. IN SHARE MODE statements,
    regardless of whether the binary log is on or off.
    
    If the user required a different type of read (i.e. locking
    read), he/she could request so explicitly by providing FOR
    UPDATE/IN SHARE MODE clause for each individual subquery.
    
    The patch for bug 39843 broke this behaviour (which was not
    documented or tested), and started to use locking reads for
    all subqueries in SELECT ... FOR UPDATE/IN SHARE MODE.
    This patch restores 4.1 behaviour.
    
    This patch should be mostly null-merged into 5.5 tree.
    
    mysql-test/include/check_concurrent_insert.inc:
      Added auxiliary script which allows to check if statement
      reading table allows concurrent inserts in it.
    mysql-test/include/check_no_concurrent_insert.inc:
      Added auxiliary script which allows to check that statement
      reading table doesn't allow concurrent inserts in it.
    mysql-test/include/check_no_row_lock.inc:
      Added auxiliary script which allows to check if statement
      reading table doesn't take locks on its rows.
    mysql-test/include/check_shared_row_lock.inc:
      Added auxiliary script which allows to check if statement
      reading table takes shared locks on some of its rows.
    mysql-test/r/bug39022.result:
      After bug #46947 'Embedded SELECT without FOR UPDATE is
      causing a lock' was fixed test case for bug 39022 has to
      be adjusted in order to trigger execution path on which
      original problem was encountered.
    mysql-test/r/innodb_mysql_lock2.result:
      Added coverage for handling of locking in various cases when
      we read data from InnoDB tables (includes test case for
      bug #46947 'Embedded SELECT without FOR UPDATE is causing a
      lock').
    mysql-test/r/lock_sync.result:
      Added coverage for handling of locking in various cases when
      we read data from MyISAM tables.
    mysql-test/t/bug39022.test:
      After bug #46947 'Embedded SELECT without FOR UPDATE is
      causing a lock' was fixed test case for bug 39022 has to
      be adjusted in order to trigger execution path on which
      original problem was encountered.
    mysql-test/t/innodb_mysql_lock2.test:
      Added coverage for handling of locking in various cases when
      we read data from InnoDB tables (includes test case for
      bug #46947 'Embedded SELECT without FOR UPDATE is causing a
      lock').
    mysql-test/t/lock_sync.test:
      Added coverage for handling of locking in various cases when
      we read data from MyISAM tables.
    sql/mysql_priv.h:
      Function read_lock_type_for_table() now takes pointers to
      LEX and TABLE_LIST elements as its arguments since to
      correctly determine lock type it needs to know what
      statement is being performed and whether table element for
      which lock type to be determined belongs to prelocking list.
    sql/sql_base.cc:
      Changed read_lock_type_for_table() to return a weak TL_READ
      type of lock in cases when we are executing SELECT (and so
      won't update tables directly) and table doesn't belong to
      statement's prelocking list and thus can't be used by a
      stored function. It is OK to do so since in this case table
      won't be used by statement or function call which will be
      written to the binary log, so serializability requirements
      for it can be relaxed.
      One of results from this change is that SELECTs on InnoDB
      tables no longer takes shared row locks for tables which
      are used in subqueries (i.e. bug #46947 is fixed).
      Another result is that for similar SELECTs on MyISAM tables
      concurrent inserts are allowed.
      In order to implement this change signature of
      read_lock_type_for_table() function was changed to
      take pointers to LEX and TABLE_LIST objects.
    sql/sql_update.cc:
      Function read_lock_type_for_table() now takes pointers to
      LEX and TABLE_LIST elements as its arguments since to
      correctly determine lock type it needs to know what
      statement is being performed and whether table element for
      which lock type to be determined belongs to prelocking list.
    59d58220
sql_base.cc 293 KB