commit 7e1ce93bf5887accf2d8ff7dc1daa5310cd68238
Author: SachinSetiya <sachinsetia1001@gmail.com>
Date:   Sat Dec 3 17:28:12 2016 +0530

    MDEV-11479 Improved wsrep_dirty_reads
    
    Tasks:-
        Changes in wsrep_dirty_reads variable
        1.) Global + Session scope (Current: session-only)
        2.) Can be Set using command line.
        3.) Allow all commands that do not change data (besides SELECT)
        4.) Allow prepared Statements that do not change data
        5.) Works with wsrep_sync_wait enabled

diff --git a/mysql-test/suite/galera/r/galera_var_dirty_reads.result b/mysql-test/suite/galera/r/galera_var_dirty_reads.result
index 6a2aa1e..cd132a0 100644
--- a/mysql-test/suite/galera/r/galera_var_dirty_reads.result
+++ b/mysql-test/suite/galera/r/galera_var_dirty_reads.result
@@ -3,6 +3,10 @@ INSERT INTO t1 VALUES(1);
 SELECT * FROM t1;
 i
 1
+create user user1;
+grant all privileges on *.* to user1;
+create user user2;
+grant all privileges on *.* to user2;
 SET @@global.wsrep_cluster_address = '';
 SET @@session.wsrep_dirty_reads=OFF;
 SET SESSION wsrep_sync_wait=0;
@@ -28,11 +32,81 @@ SELECT @@max_allowed_packet;
 SELECT 2+2 from DUAL;
 2+2
 4
-SELECT sysdate() from DUAL;
-sysdate()
-2016-10-28 23:13:06
+SET @@session.wsrep_dirty_reads=ON;
+SELECT * FROM t1;
+i
+1
+connect  con1, localhost, user1,,test,$NODE_MYPORT_2,$NODE_MYSOCK_2;
+SET SESSION wsrep_sync_wait=0;
+select @@session.wsrep_dirty_reads;
+@@session.wsrep_dirty_reads
+0
+set session wsrep_dirty_reads=1;
+prepare stmt_show from 'select 1';
+prepare stmt_select from 'select * from t1';
+prepare stmt_insert from 'insert into t1 values(1)';
+set session wsrep_dirty_reads=0;
+execute stmt_show;
+ERROR 08S01: WSREP has not yet prepared node for application use
+execute stmt_select;
+ERROR 08S01: WSREP has not yet prepared node for application use
+execute stmt_insert;
+ERROR 08S01: WSREP has not yet prepared node for application use
+SET wsrep_dirty_reads=ON;
+select @@session.wsrep_dirty_reads;
+@@session.wsrep_dirty_reads
+1
+execute stmt_show;
+1
+1
+execute stmt_select;
+i
+1
+execute stmt_insert;
+ERROR 08S01: WSREP has not yet prepared node for application use
+SET @@global.wsrep_dirty_reads=ON;
+connect  con2, localhost, user2,,test,$NODE_MYPORT_2,$NODE_MYSOCK_2;
+select @@session.wsrep_dirty_reads;
+@@session.wsrep_dirty_reads
+1
+prepare stmt_show from 'select 1';
+prepare stmt_select from 'select * from t1';
+prepare stmt_insert from 'insert into t1 values(1)';
+execute stmt_show;
+1
+1
+execute stmt_select;
+i
+1
+execute stmt_insert;
+ERROR 08S01: WSREP has not yet prepared node for application use
+SET SESSION wsrep_sync_wait=1;
+execute stmt_show;
+1
+1
+execute stmt_select;
+i
+1
+execute stmt_insert;
+ERROR 08S01: WSREP has not yet prepared node for application use
+SET SESSION wsrep_sync_wait=7;
+execute stmt_show;
+1
+1
+execute stmt_select;
+i
+1
+execute stmt_insert;
+ERROR 08S01: WSREP has not yet prepared node for application use
+connection node_2;
+SET @@global.wsrep_dirty_reads=OFF;
+connection node_1;
 SELECT * FROM t1;
 i
 1
 DROP TABLE t1;
+drop user user1;
+drop user user2;
+disconnect node_2;
+disconnect node_1;
 # End of test
diff --git a/mysql-test/suite/galera/t/galera_var_dirty_reads.test b/mysql-test/suite/galera/t/galera_var_dirty_reads.test
index 0c81779..9891849 100644
--- a/mysql-test/suite/galera/t/galera_var_dirty_reads.test
+++ b/mysql-test/suite/galera/t/galera_var_dirty_reads.test
@@ -17,6 +17,11 @@ CREATE TABLE t1(i INT) ENGINE=INNODB;
 INSERT INTO t1 VALUES(1);
 SELECT * FROM t1;
 
+create user user1;
+grant all privileges on *.* to user1;
+create user user2;
+grant all privileges on *.* to user2;
+
 SET @@global.wsrep_cluster_address = '';
 SET @@session.wsrep_dirty_reads=OFF;
 
@@ -41,7 +46,72 @@ SET @@session.wsrep_dirty_reads=OFF;
 SELECT 2;
 SELECT @@max_allowed_packet;
 SELECT 2+2 from DUAL;
-SELECT sysdate() from DUAL;
+
+#Query which does not change data should be allowed MDEV-11479
+SET @@session.wsrep_dirty_reads=ON;
+SELECT * FROM t1;
+
+--enable_connect_log
+--connect (con1, localhost, user1,,test,$NODE_MYPORT_2,$NODE_MYSOCK_2)
+#Just test the session behavior
+SET SESSION wsrep_sync_wait=0;
+select @@session.wsrep_dirty_reads;
+
+set session wsrep_dirty_reads=1;
+#Prepared statement creation should be allowed MDEV-11479
+prepare stmt_show from 'select 1';
+prepare stmt_select from 'select * from t1';
+prepare stmt_insert from 'insert into t1 values(1)';
+set session wsrep_dirty_reads=0;
+
+#No Preapare stmt/proceure will be allowed
+--error ER_UNKNOWN_COM_ERROR
+execute stmt_show;
+--error ER_UNKNOWN_COM_ERROR
+execute stmt_select;
+--error ER_UNKNOWN_COM_ERROR
+execute stmt_insert;
+
+SET wsrep_dirty_reads=ON;
+select @@session.wsrep_dirty_reads;
+#Only prepare statement which does not change data should be allowed
+execute stmt_show;
+execute stmt_select;
+--error ER_UNKNOWN_COM_ERROR
+execute stmt_insert;
+SET @@global.wsrep_dirty_reads=ON;
+
+--connect (con2, localhost, user2,,test,$NODE_MYPORT_2,$NODE_MYSOCK_2)
+#Just test the session behavior
+select @@session.wsrep_dirty_reads;
+
+prepare stmt_show from 'select 1';
+prepare stmt_select from 'select * from t1';
+prepare stmt_insert from 'insert into t1 values(1)';
+
+#Only prepare statement which does not change data should be allowed
+execute stmt_show;
+execute stmt_select;
+--error ER_UNKNOWN_COM_ERROR
+execute stmt_insert;
+
+#wsrep_dirty_read should work when wsrep_sync_wait is 1 or non zero
+#because we already are disconnected , So It does not make any sense
+#to wait for other nodes
+SET SESSION wsrep_sync_wait=1;
+execute stmt_show;
+execute stmt_select;
+--error ER_UNKNOWN_COM_ERROR
+execute stmt_insert;
+
+SET SESSION wsrep_sync_wait=7;
+execute stmt_show;
+execute stmt_select;
+--error ER_UNKNOWN_COM_ERROR
+execute stmt_insert;
+
+--connection node_2
+SET @@global.wsrep_dirty_reads=OFF;
 
 --disable_query_log
 --eval SET @@global.wsrep_cluster_address = '$wsrep_cluster_address_saved'
@@ -52,6 +122,8 @@ SELECT sysdate() from DUAL;
 SELECT * FROM t1;
 # Cleanup
 DROP TABLE t1;
+drop user user1;
+drop user user2;
 
 # Restore original auto_increment_offset values.
 --source include/auto_increment_offset_restore.inc
diff --git a/mysql-test/suite/sys_vars/r/wsrep_dirty_reads_basic.result b/mysql-test/suite/sys_vars/r/wsrep_dirty_reads_basic.result
index d2a62d6..1968103 100644
--- a/mysql-test/suite/sys_vars/r/wsrep_dirty_reads_basic.result
+++ b/mysql-test/suite/sys_vars/r/wsrep_dirty_reads_basic.result
@@ -5,12 +5,13 @@
 SET @wsrep_dirty_reads_session_saved = @@session.wsrep_dirty_reads;
 # default
 SELECT @@global.wsrep_dirty_reads;
-ERROR HY000: Variable 'wsrep_dirty_reads' is a SESSION variable
+@@global.wsrep_dirty_reads
+0
 SELECT @@session.wsrep_dirty_reads;
 @@session.wsrep_dirty_reads
 0
 
-# scope and valid values
+# valid values for session
 SET @@session.wsrep_dirty_reads=OFF;
 SELECT @@session.wsrep_dirty_reads;
 @@session.wsrep_dirty_reads
@@ -24,11 +25,29 @@ SELECT @@session.wsrep_dirty_reads;
 @@session.wsrep_dirty_reads
 0
 
+# valid values for global
+SET @@global.wsrep_dirty_reads=OFF;
+SELECT @@global.wsrep_dirty_reads;
+@@global.wsrep_dirty_reads
+0
+SET @@global.wsrep_dirty_reads=ON;
+SELECT @@global.wsrep_dirty_reads;
+@@global.wsrep_dirty_reads
+1
+SET @@global.wsrep_dirty_reads=default;
+SELECT @@global.wsrep_dirty_reads;
+@@global.wsrep_dirty_reads
+0
+
 # invalid values
 SET @@session.wsrep_dirty_reads=NULL;
 ERROR 42000: Variable 'wsrep_dirty_reads' can't be set to the value of 'NULL'
 SET @@session.wsrep_dirty_reads='junk';
 ERROR 42000: Variable 'wsrep_dirty_reads' can't be set to the value of 'junk'
+SET @@global.wsrep_dirty_reads=NULL;
+ERROR 42000: Variable 'wsrep_dirty_reads' can't be set to the value of 'NULL'
+SET @@global.wsrep_dirty_reads='junk';
+ERROR 42000: Variable 'wsrep_dirty_reads' can't be set to the value of 'junk'
 
 # restore the initial values
 SET @@session.wsrep_dirty_reads = @wsrep_dirty_reads_session_saved;
diff --git a/mysql-test/suite/sys_vars/t/wsrep_dirty_reads_basic.test b/mysql-test/suite/sys_vars/t/wsrep_dirty_reads_basic.test
index a47524f..ffe767a 100644
--- a/mysql-test/suite/sys_vars/t/wsrep_dirty_reads_basic.test
+++ b/mysql-test/suite/sys_vars/t/wsrep_dirty_reads_basic.test
@@ -8,12 +8,12 @@
 SET @wsrep_dirty_reads_session_saved = @@session.wsrep_dirty_reads;
 
 --echo # default
---error ER_INCORRECT_GLOBAL_LOCAL_VAR
+
 SELECT @@global.wsrep_dirty_reads;
 SELECT @@session.wsrep_dirty_reads;
 
 --echo
---echo # scope and valid values
+--echo # valid values for session
 SET @@session.wsrep_dirty_reads=OFF;
 SELECT @@session.wsrep_dirty_reads;
 SET @@session.wsrep_dirty_reads=ON;
@@ -22,11 +22,24 @@ SET @@session.wsrep_dirty_reads=default;
 SELECT @@session.wsrep_dirty_reads;
 
 --echo
+--echo # valid values for global
+SET @@global.wsrep_dirty_reads=OFF;
+SELECT @@global.wsrep_dirty_reads;
+SET @@global.wsrep_dirty_reads=ON;
+SELECT @@global.wsrep_dirty_reads;
+SET @@global.wsrep_dirty_reads=default;
+SELECT @@global.wsrep_dirty_reads;
+
+--echo
 --echo # invalid values
 --error ER_WRONG_VALUE_FOR_VAR
 SET @@session.wsrep_dirty_reads=NULL;
 --error ER_WRONG_VALUE_FOR_VAR
 SET @@session.wsrep_dirty_reads='junk';
+--error ER_WRONG_VALUE_FOR_VAR
+SET @@global.wsrep_dirty_reads=NULL;
+--error ER_WRONG_VALUE_FOR_VAR
+SET @@global.wsrep_dirty_reads='junk';
 
 --echo
 --echo # restore the initial values
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 7d5c59f..008393f 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -2155,6 +2155,15 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
 }
 
 
+/*
+  See whether the command changes the data or not.
+ */
+static int inline
+command_changes_data(uint sql_command)
+{
+  return (sql_command_flags[sql_command] &
+          CF_CHANGES_DATA) != 0;
+}
 /**
   Read query from packet and store in thd->query.
   Used in COM_QUERY and COM_STMT_PREPARE.
@@ -2656,7 +2665,7 @@ mysql_execute_command(THD *thd)
     if (lex->sql_command != SQLCOM_SET_OPTION  &&
         !wsrep_is_show_query(lex->sql_command) &&
         !(thd->variables.wsrep_dirty_reads     &&
-          lex->sql_command == SQLCOM_SELECT)   &&
+       !command_changes_data(lex->sql_command))&&
         !(lex->sql_command == SQLCOM_SELECT    &&
           !all_tables)                         &&
         !wsrep_node_is_ready(thd))
@@ -2934,8 +2943,14 @@ mysql_execute_command(THD *thd)
   case SQLCOM_SHOW_FIELDS:
   case SQLCOM_SHOW_KEYS:
   case SQLCOM_SELECT:
-    if (WSREP_CLIENT(thd) && wsrep_sync_wait(thd))
-      goto error;
+   /*
+      wsrep_dirty_read should work when wsrep_sync_wait is 1 or  non-zero
+      because we already are disconnected. So, it does not make any sense
+      to wait for other nodes
+    */
+    if (WSREP_CLIENT(thd) && (wsrep_ready || !thd->variables.wsrep_dirty_reads)
+        && wsrep_sync_wait(thd))
+        goto error;
 
   case SQLCOM_SHOW_PLUGINS:
   case SQLCOM_SHOW_VARIABLES:
diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc
index 0518e43..42fd46c 100644
--- a/sql/sys_vars.cc
+++ b/sql/sys_vars.cc
@@ -4977,8 +4977,8 @@ static Sys_var_mybool Sys_wsrep_restart_slave(
 
 static Sys_var_mybool Sys_wsrep_dirty_reads(
        "wsrep_dirty_reads", "Do not reject SELECT queries even when the node "
-       "is not ready.", SESSION_ONLY(wsrep_dirty_reads), NO_CMD_LINE,
-       DEFAULT(FALSE), NO_MUTEX_GUARD, NOT_IN_BINLOG);
+       "is not ready.", SESSION_VAR(wsrep_dirty_reads),
+       CMD_LINE(OPT_ARG), DEFAULT(FALSE), NO_MUTEX_GUARD, NOT_IN_BINLOG);
 
 static Sys_var_uint Sys_wsrep_gtid_domain_id(
        "wsrep_gtid_domain_id", "When wsrep_gtid_mode is set, this value is "
diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc
index 0deb19d..a6f55b4 100644
--- a/sql/wsrep_mysqld.cc
+++ b/sql/wsrep_mysqld.cc
@@ -94,6 +94,8 @@ bool wsrep_new_cluster                 = false; // Bootstrap the cluster ?
 
 // Use wsrep_gtid_domain_id for galera transactions?
 bool wsrep_gtid_mode                   = 0;
+my_bool wsrep_dirty_reads              = false; // is dirty reads allowed for
+                                                // disconnected/non major node
 // gtid_domain_id for galera transactions.
 uint32 wsrep_gtid_domain_id            = 0;
 
diff --git a/sql/wsrep_mysqld.h b/sql/wsrep_mysqld.h
index 04ccc1a..8bdfdd5 100644
--- a/sql/wsrep_mysqld.h
+++ b/sql/wsrep_mysqld.h
@@ -88,6 +88,7 @@ extern my_bool     wsrep_slave_UK_checks;
 extern ulong       wsrep_running_threads;
 extern bool        wsrep_new_cluster;
 extern bool        wsrep_gtid_mode;
+extern my_bool     wsrep_dirty_reads;
 extern uint32      wsrep_gtid_domain_id;
 
 enum enum_wsrep_OSU_method {
