diff --git a/BitKeeper/etc/logging_ok b/BitKeeper/etc/logging_ok
index fbf9c3702a3b7d4ae9368bb20c17f1e5697893cb..966f7a398de21fb3e3c491de97ca5d3e48ba9a7e 100644
--- a/BitKeeper/etc/logging_ok
+++ b/BitKeeper/etc/logging_ok
@@ -1,16 +1 @@
-heikki@donna.mysql.fi
-jani@janikt.pp.saunalahti.fi
-miguel@light.local
-monty@hundin.mysql.fi
-monty@tik.mysql.fi
-monty@work.mysql.com
-mwagner@evoq.mwagner.org
-paul@central.snake.net
-paul@teton.kitebird.com
-sasha@mysql.sashanet.com
-serg@serg.mysql.com
-tim@threads.polyesthetic.msg
-tim@white.box
-jcole@tetra.spaceapes.com
-davida@isil.mysql.com
-tonu@x153.internalnet
+jani@hynda.mysql.fi
diff --git a/Docs/manual.texi b/Docs/manual.texi
index f3ab70cf1800c679b11d59cbdc57b128cbadadd6..dad548f53f63e56a52feddfc2528c335f7716227 100644
--- a/Docs/manual.texi
+++ b/Docs/manual.texi
@@ -14751,6 +14751,13 @@ Disable using thread priorities for faster response time.
 Socket file to use for local connections instead of default
 @code{/tmp/mysql.sock}.
 
+@item --sql-mode=option[,option[,option...]]
+Option can be one of: REAL_AS_FLOAT, PIPES_AS_CONCAT, ANSI_QUOTES,
+IGNORE_SPACE, SERIALIZE, ONLY_FULL_GROUP_BY.
+
+By specifying all of the above options is same as using --ansi.
+With this option one can turn on only needed SQL modes. @xref{ANSI mode}.
+
 @item transaction-isolation= @{ READ-UNCOMMITTED | READ-COMMITTED | REPEATABLE-READ | SERIALIZABLE @}
 Sets the default transaction isolation level.  @xref{SET TRANSACTION}.
 
@@ -45896,6 +45903,8 @@ not yet 100% confident in this code.
 @node News-3.23.41, News-3.23.40, News-3.23.x, News-3.23.x
 @appendixsubsec Changes in release 3.23.41
 @itemize @bullet
+@item Added option @code{--sql-mode=option[,option[,option]]}. Please see
+@code{mysqld --help}爁or legal modes.
 @item
 Fixed optimizing bug in @code{ORDER BY} where some @code{ORDER BY} parts
 where wrongly removed.
diff --git a/sql/ha_myisam.cc b/sql/ha_myisam.cc
index 6409ec5d01918e537398605904859d3024439242..8be62920308f05e8c256139d331a9700b600c737 100644
--- a/sql/ha_myisam.cc
+++ b/sql/ha_myisam.cc
@@ -35,7 +35,7 @@ ulong myisam_recover_options= HA_RECOVER_NONE;
 
 /* bits in myisam_recover_options */
 const char *myisam_recover_names[] =
-{ "DEFAULT", "BACKUP", "FORCE", "QUICK"};
+{ "DEFAULT", "BACKUP", "FORCE", "QUICK", NullS};
 TYPELIB myisam_recover_typelib= {array_elements(myisam_recover_names),"",
 				 myisam_recover_names};
 
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index f21c635dbdfa4f63897b4385a21f008ba1fa098e..f8c0efd6eb2b8ac81c732db463845d7e970e2a11 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -156,8 +156,7 @@ void kill_one_thread(THD *thd, ulong id);
 #define OPTION_LOW_PRIORITY_UPDATES	8192
 #define OPTION_WARNINGS		16384
 #define OPTION_AUTO_IS_NULL	32768
-#define OPTION_ANSI_MODE	65536L
-#define OPTION_SAFE_UPDATES	OPTION_ANSI_MODE*2
+#define OPTION_SAFE_UPDATES	65536L*2
 #define OPTION_BUFFER_RESULT	OPTION_SAFE_UPDATES*2
 #define OPTION_BIN_LOG          OPTION_BUFFER_RESULT*2
 #define OPTION_NOT_AUTO_COMMIT	OPTION_BIN_LOG*2
@@ -173,6 +172,14 @@ void kill_one_thread(THD *thd, ulong id);
 #define QUERY_NO_INDEX_USED		OPTION_STATUS_NO_TRANS_UPDATE*2
 #define QUERY_NO_GOOD_INDEX_USED	QUERY_NO_INDEX_USED*2
 
+/* Bits for different SQL modes modes (including ANSI mode) */
+#define MODE_REAL_AS_FLOAT      1
+#define MODE_PIPES_AS_CONCAT    2
+#define MODE_ANSI_QUOTES        4
+#define MODE_IGNORE_SPACE	8
+#define MODE_SERIALIZABLE	16
+#define MODE_ONLY_FULL_GROUP_BY	32
+
 #define RAID_BLOCK_SIZE 1024
 
 /* BINLOG_DUMP options */
@@ -530,7 +537,7 @@ extern ulong keybuff_size,sortbuff_size,max_item_sort_length,table_cache_size,
 	     what_to_log,flush_time,
 	     max_tmp_tables,max_heap_table_size,query_buff_size,
 	     lower_case_table_names,thread_stack,thread_stack_min,
-	     binlog_cache_size, max_binlog_cache_size;
+	     binlog_cache_size, max_binlog_cache_size, opt_sql_mode;
 extern ulong specialflag, current_pid;
 extern bool low_priority_updates, using_update_log;
 extern bool opt_sql_bin_update, opt_safe_show_db, opt_warnings;
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index a9771184b4b1341e8153059c5b55c3e1e4a4a1f4..36ee127580c50178aab981d7d34295aee8c80100 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -211,7 +211,7 @@ static char mysql_home[FN_REFLEN],pidfile_name[FN_REFLEN];
 static pthread_t select_thread;
 static bool opt_log,opt_update_log,opt_bin_log,opt_slow_log,opt_noacl,
 	    opt_disable_networking=0, opt_bootstrap=0,opt_skip_show_db=0,
-	    opt_ansi_mode=0,opt_myisam_log=0,
+	    opt_myisam_log=0,
             opt_large_files=sizeof(my_off_t) > 4;
 bool opt_sql_bin_update = 0, opt_log_slave_updates = 0, opt_safe_show_db=0;
 FILE *bootstrap_file=0;
@@ -307,6 +307,7 @@ char server_version[SERVER_VERSION_LENGTH]=MYSQL_SERVER_VERSION;
 const char *first_keyword="first";
 const char **errmesg;			/* Error messages */
 const char *myisam_recover_options_str="OFF";
+const char *sql_mode_str="OFF";
 const char *default_tx_isolation_name;
 enum_tx_isolation default_tx_isolation=ISO_READ_COMMITTED;
 
@@ -320,6 +321,12 @@ double log_10[32];			/* 10 potences */
 I_List<THD> threads,thread_cache;
 time_t start_time;
 
+ulong opt_sql_mode = 0L;
+const char *sql_mode_names[] =
+{ "REAL_AS_FLOAT", "PIPES_AS_CONCAT", "ANSI_QUOTES", "IGNORE_SPACE",
+  "SERIALIZE","ONLY_FULL_GROUP_BY", NullS };
+TYPELIB sql_mode_typelib= {array_elements(sql_mode_names),"",
+			   sql_mode_names};
 
 MY_BITMAP temp_pool;
 bool use_temp_pool=0;
@@ -2471,7 +2478,8 @@ enum options {
 	       OPT_GEMINI_FLUSH_LOG, OPT_GEMINI_RECOVER,
                OPT_GEMINI_UNBUFFERED_IO, OPT_SKIP_SAFEMALLOC,
 	       OPT_SKIP_STACK_TRACE, OPT_SKIP_SYMLINKS,
-	       OPT_MAX_BINLOG_DUMP_EVENTS, OPT_SPORADIC_BINLOG_DUMP_FAIL
+	       OPT_MAX_BINLOG_DUMP_EVENTS, OPT_SPORADIC_BINLOG_DUMP_FAIL,
+	       OPT_SQL_MODE
 };
 
 static struct option long_options[] = {
@@ -2604,6 +2612,7 @@ static struct option long_options[] = {
   {"skip-symlink",	    no_argument,       0, (int) OPT_SKIP_SYMLINKS},
   {"skip-thread-priority",  no_argument,       0, (int) OPT_SKIP_PRIOR},
   {"sql-bin-update-same",   no_argument,       0, (int) OPT_SQL_BIN_UPDATE_SAME},
+  {"sql-mode",              required_argument, 0, (int) OPT_SQL_MODE},
 #include "sslopt-longopts.h"
 #ifdef __WIN__
   {"standalone",            no_argument,       0, (int) OPT_STANDALONE},
@@ -2764,7 +2773,6 @@ CHANGEABLE_VAR changeable_vars[] = {
 
 
 struct show_var_st init_vars[]= {
-  {"ansi_mode",               (char*) &opt_ansi_mode,               SHOW_BOOL},
   {"back_log",                (char*) &back_log,                    SHOW_LONG},
   {"basedir",                 mysql_home,                           SHOW_CHAR},
 #ifdef HAVE_BERKELEY_DB
@@ -2866,6 +2874,7 @@ struct show_var_st init_vars[]= {
   {"slow_launch_time",        (char*) &slow_launch_time,            SHOW_LONG},
   {"socket",                  (char*) &mysql_unix_port,             SHOW_CHAR_PTR},
   {"sort_buffer",             (char*) &sortbuff_size,               SHOW_LONG},
+  {"sql_mode",                (char*) &sql_mode_str,                SHOW_CHAR_PTR},
   {"table_cache",             (char*) &table_cache_size,            SHOW_LONG},
   {"table_type",              (char*) &default_table_type_name,     SHOW_CHAR_PTR},
   {"thread_cache_size",       (char*) &thread_cache_size,           SHOW_LONG},
@@ -3049,6 +3058,9 @@ static void usage(void)
 			Don't give threads different priorities.\n\
   --socket=...		Socket file to use for connection\n\
   -t, --tmpdir=path	Path for temporary files\n\
+  --sql-mode=option[,option[,option...]] where option can be one of:\n\
+                        REAL_AS_FLOAT, PIPES_AS_CONCAT, ANSI_QUOTES,\n\
+                        IGNORE_SPACE, SERIALIZE, ONLY_FULL_GROUP_BY.\n\
   --transaction-isolation\n\
 		        Default transaction isolation level\n\
   --temp-pool           Use a pool of temporary files\n\
@@ -3202,8 +3214,9 @@ static void get_options(int argc,char **argv)
       opt_warnings=1;
       break;
     case 'a':
-      opt_ansi_mode=1;
-      thd_startup_options|=OPTION_ANSI_MODE;
+      opt_sql_mode = (MODE_REAL_AS_FLOAT | MODE_PIPES_AS_CONCAT |
+		      MODE_ANSI_QUOTES | MODE_IGNORE_SPACE | MODE_SERIALIZABLE
+		      | MODE_ONLY_FULL_GROUP_BY);
       default_tx_isolation= ISO_SERIALIZABLE;
       break;
     case 'b':
@@ -3726,6 +3739,19 @@ static void get_options(int argc,char **argv)
       ha_open_options|=HA_OPEN_ABORT_IF_CRASHED;
       break;
     }
+    case OPT_SQL_MODE:
+    {
+      sql_mode_str = optarg;
+      if ((opt_sql_mode =
+	   find_bit_type(optarg, &sql_mode_typelib)) == ~(ulong) 0)
+      {
+	fprintf(stderr, "Unknown option to sql-mode: %s\n", optarg);
+	exit(1);
+      }
+      if (opt_sql_mode & MODE_SERIALIZABLE)
+	default_tx_isolation= ISO_SERIALIZABLE;
+      break;
+    }
     case OPT_MASTER_HOST:
       master_host=optarg;
       break;
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 9ea3896fd788aa7d230f6ad4dac09b5af3b721e9..efe0d7864b993e24ab944c5115354c727f5b0050 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -120,6 +120,7 @@ THD::THD():user_time(0),fatal_error(0),last_insert_id_used(0),
   server_status=SERVER_STATUS_AUTOCOMMIT;
   update_lock_default= low_priority_updates ? TL_WRITE_LOW_PRIORITY : TL_WRITE;
   options=thd_startup_options;
+  sql_mode=(uint) opt_sql_mode;
   inactive_timeout=net_wait_timeout;
   open_options=ha_open_options;
   tx_isolation=session_tx_isolation=default_tx_isolation;
diff --git a/sql/sql_class.h b/sql/sql_class.h
index a092de2602dd7aaf2d9821c264eea64e8d79cb85..4ccbeb6f01f28d655ec7264ad781f3b1d1bf46b8 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -232,7 +232,7 @@ public:
   char	  *query,*thread_stack;
   char	  *host,*user,*priv_user,*db,*ip;
   const   char *proc_info;
-  uint	  client_capabilities,max_packet_length;
+  uint	  client_capabilities,sql_mode,max_packet_length;
   uint	  master_access,db_access;
   TABLE   *open_tables,*temporary_tables;
   MYSQL_LOCK *lock,*locked_tables;
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index a78fef626574db2dc355af1544043c3ee9543a6b..20bda932f2f0fa823633ee01c04e4b74e00e497e 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -121,7 +121,7 @@ void lex_init(void)
   state_map[(uchar)'*']= (uchar) STATE_END_LONG_COMMENT;
   state_map[(uchar)'@']= (uchar) STATE_USER_END;
   state_map[(uchar) '`']= (uchar) STATE_USER_VARIABLE_DELIMITER;
-  if (thd_startup_options & OPTION_ANSI_MODE)
+  if (opt_sql_mode & MODE_ANSI_QUOTES)
   {
     state_map[(uchar) '"'] = STATE_USER_VARIABLE_DELIMITER;
   }
@@ -149,7 +149,7 @@ LEX *lex_start(THD *thd, uchar *buf,uint length)
   lex->ftfunc_list.empty();
   lex->convert_set=(lex->thd=thd)->convert_set;
   lex->yacc_yyss=lex->yacc_yyvs=0;
-  lex->ignore_space=test(thd->client_capabilities & CLIENT_IGNORE_SPACE);
+  lex->ignore_space=test(thd->sql_mode & MODE_IGNORE_SPACE);
   return lex;
 }
 
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 87fbed47ef127f4d1eb731122b75d7ea197f5bc7..1c8c917babee482d8fed073b550a2d2856016552 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -412,6 +412,8 @@ check_connections(THD *thd)
     return(ER_OUT_OF_RESOURCES);
 
   thd->client_capabilities=uint2korr(net->read_pos);
+  if (thd->client_capabilities & CLIENT_IGNORE_SPACE)
+    thd->sql_mode|= MODE_IGNORE_SPACE;
 #ifdef HAVE_OPENSSL
   DBUG_PRINT("info",
 	     ("pkt_len:%d, client capabilities: %d",
@@ -538,8 +540,6 @@ pthread_handler_decl(handle_one_connection,arg)
       thd->options |= OPTION_BIG_SELECTS;
     if (thd->client_capabilities & CLIENT_COMPRESS)
       net->compress=1;				// Use compression
-    if (thd->options & OPTION_ANSI_MODE)
-      thd->client_capabilities|=CLIENT_IGNORE_SPACE;
 
     thd->proc_info=0;				// Remove 'login'
     thd->command=COM_SLEEP;
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 00f92e56b718eb70a8cff8411a085abc5f353cf9..5c3e7516eaf3dcc47af09967fc32f5e3fcf3dbb5 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -5937,7 +5937,7 @@ setup_group(THD *thd,TABLE_LIST *tables,List<Item> &fields,
   if (!order)
     return 0;				/* Everything is ok */
 
-  if (thd->options & OPTION_ANSI_MODE)
+  if (thd->sql_mode & MODE_ONLY_FULL_GROUP_BY)
   {
     Item *item;
     List_iterator<Item> li(fields);
@@ -5959,7 +5959,7 @@ setup_group(THD *thd,TABLE_LIST *tables,List<Item> &fields,
       return 1;
     }
   }
-  if (thd->options & OPTION_ANSI_MODE)
+  if (thd->sql_mode & MODE_ONLY_FULL_GROUP_BY)
   {
     /* Don't allow one to use fields that is not used in GROUP BY */
     Item *item;
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index c1069d917467d50045ec9c7bc87d3e9ea962d86d..9ab97d5265099d17edb2176a7cf3d3334075c0c5 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -34,7 +34,7 @@ int yylex(void *yylval);
 
 inline Item *or_or_concat(Item* A, Item* B)
 {
-  return (current_thd->options & OPTION_ANSI_MODE ?
+  return (current_thd->sql_mode & MODE_PIPES_AS_CONCAT ?
           (Item*) new Item_func_concat(A,B) : (Item*) new Item_cond_or(A,B));
 }
 
@@ -915,7 +915,7 @@ int_type:
 	| BIGINT	{ $$=FIELD_TYPE_LONGLONG; }
 
 real_type:
-	REAL		{ $$= current_thd->options & OPTION_ANSI_MODE ?
+	REAL		{ $$= current_thd->sql_mode & MODE_REAL_AS_FLOAT ?
 			      FIELD_TYPE_FLOAT : FIELD_TYPE_DOUBLE; }
 	| DOUBLE_SYM	{ $$=FIELD_TYPE_DOUBLE; }
 	| DOUBLE_SYM PRECISION { $$=FIELD_TYPE_DOUBLE; }