diff --git a/mysql-test/r/read_only.result b/mysql-test/r/read_only.result
index 69d25fbef6f1ca94480930f823091d5aa2fc3b29..827a137f5b2d527bf0e7300bb944540abe28f0b4 100644
--- a/mysql-test/r/read_only.result
+++ b/mysql-test/r/read_only.result
@@ -46,4 +46,35 @@ Warnings:
 Note	1051	Unknown table 'ttt'
 drop table t1,t2;
 drop user test@localhost;
+#
+# Bug #27440 read_only allows create and drop database
+#
+drop database if exists mysqltest_db1;
+drop database if exists mysqltest_db2;
+delete from mysql.user where User like 'mysqltest_%';
+delete from mysql.db where User like 'mysqltest_%';
+delete from mysql.tables_priv where User like 'mysqltest_%';
+delete from mysql.columns_priv where User like 'mysqltest_%';
+flush privileges;
+grant all on mysqltest_db2.* to `mysqltest_u1`@`%`;
+create database mysqltest_db1;
+grant all on mysqltest_db1.* to `mysqltest_u1`@`%`;
+flush privileges;
+show grants for current_user();
+Grants for mysqltest_u1@%
+GRANT USAGE ON *.* TO 'mysqltest_u1'@'%'
+GRANT ALL PRIVILEGES ON `mysqltest_db2`.* TO 'mysqltest_u1'@'%'
+GRANT ALL PRIVILEGES ON `mysqltest_db1`.* TO 'mysqltest_u1'@'%'
+create database mysqltest_db2;
+ERROR HY000: The MySQL server is running with the --read-only option so it cannot execute this statement
+show databases like '%mysqltest_db2%';
+Database (%mysqltest_db2%)
+drop database mysqltest_db1;
+ERROR HY000: The MySQL server is running with the --read-only option so it cannot execute this statement
+delete from mysql.user where User like 'mysqltest_%';
+delete from mysql.db where User like 'mysqltest_%';
+delete from mysql.tables_priv where User like 'mysqltest_%';
+delete from mysql.columns_priv where User like 'mysqltest_%';
+flush privileges;
+drop database mysqltest_db1;
 set global read_only=0;
diff --git a/mysql-test/t/read_only.test b/mysql-test/t/read_only.test
index 8e14b310f4c85ef21733d6992011831bd9a3376b..5ec062bc10309878306a35d23dda10a1c53f8267 100644
--- a/mysql-test/t/read_only.test
+++ b/mysql-test/t/read_only.test
@@ -117,4 +117,38 @@ connection default;
 drop table t1,t2;
 drop user test@localhost;
 
+--echo #
+--echo # Bug #27440 read_only allows create and drop database
+--echo #
+--disable_warnings
+drop database if exists mysqltest_db1;
+drop database if exists mysqltest_db2;
+--enable_warnings
+
+delete from mysql.user where User like 'mysqltest_%';
+delete from mysql.db where User like 'mysqltest_%';
+delete from mysql.tables_priv where User like 'mysqltest_%';
+delete from mysql.columns_priv where User like 'mysqltest_%';
+flush privileges;
+
+grant all on mysqltest_db2.* to `mysqltest_u1`@`%`;
+create database mysqltest_db1;
+grant all on mysqltest_db1.* to `mysqltest_u1`@`%`;
+flush privileges;
+connect (con_bug27440,127.0.0.1,mysqltest_u1,,test,$MASTER_MYPORT,);
+connection con_bug27440;
+show grants for current_user();
+--error ER_OPTION_PREVENTS_STATEMENT
+create database mysqltest_db2;
+show databases like '%mysqltest_db2%';
+--error ER_OPTION_PREVENTS_STATEMENT
+drop database mysqltest_db1;
+disconnect con_bug27440;
+connection default;
+delete from mysql.user where User like 'mysqltest_%';
+delete from mysql.db where User like 'mysqltest_%';
+delete from mysql.tables_priv where User like 'mysqltest_%';
+delete from mysql.columns_priv where User like 'mysqltest_%';
+flush privileges;
+drop database mysqltest_db1;
 set global read_only=0;
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index e587a9f35610dfb5ef1bb1a0833264b2cad51998..a5ba486920d623abcb3c1ac695895263d1f7fa15 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -1597,6 +1597,74 @@ static bool do_command(THD *thd)
 #endif  /* EMBEDDED_LIBRARY */
 
 
+/**
+  @brief Determine if an attempt to update a non-temporary table while the
+    read-only option was enabled has been made.
+
+  This is a helper function to mysql_execute_command.
+
+  @note SQLCOM_MULTI_UPDATE is an exception and delt with elsewhere.
+
+  @see mysql_execute_command
+  @returns Status code
+    @retval TRUE The statement should be denied.
+    @retval FALSE The statement isn't updating any relevant tables.
+*/
+
+static my_bool deny_updates_if_read_only_option(THD *thd,
+                                                TABLE_LIST *all_tables)
+{
+  DBUG_ENTER("deny_updates_if_read_only_option");
+
+  if (!opt_readonly)
+    DBUG_RETURN(FALSE);
+
+  LEX *lex= thd->lex;
+
+  const my_bool user_is_super=
+    ((ulong)(thd->security_ctx->master_access & SUPER_ACL) ==
+     (ulong)SUPER_ACL);
+
+  if (user_is_super)
+    DBUG_RETURN(FALSE);
+
+  if (!uc_update_queries[lex->sql_command])
+    DBUG_RETURN(FALSE);
+
+  /* Multi update is an exception and is dealt with later. */
+  if (lex->sql_command == SQLCOM_UPDATE_MULTI)
+    DBUG_RETURN(FALSE);
+
+  const my_bool create_temp_tables= 
+    (lex->sql_command == SQLCOM_CREATE_TABLE) &&
+    (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE);
+
+  const my_bool drop_temp_tables= 
+    (lex->sql_command == SQLCOM_DROP_TABLE) &&
+    lex->drop_temporary;
+
+  const my_bool update_real_tables=
+    some_non_temp_table_to_be_updated(thd, all_tables) &&
+    !(create_temp_tables || drop_temp_tables);
+
+
+  const my_bool create_or_drop_databases=
+    (lex->sql_command == SQLCOM_CREATE_DB) ||
+    (lex->sql_command == SQLCOM_DROP_DB);
+
+  if (update_real_tables || create_or_drop_databases)
+  {
+      /*
+        An attempt was made to modify one or more non-temporary tables.
+      */
+      DBUG_RETURN(TRUE);
+  }
+
+
+  /* Assuming that only temporary tables are modified. */
+  DBUG_RETURN(FALSE);
+}
+
 /*
    Perform one connection-level (COM_XXXX) command.
 
@@ -2590,14 +2658,7 @@ mysql_execute_command(THD *thd)
       When option readonly is set deny operations which change non-temporary
       tables. Except for the replication thread and the 'super' users.
     */
-    if (opt_readonly &&
-        !(thd->security_ctx->master_access & SUPER_ACL) &&
-        uc_update_queries[lex->sql_command] &&
-        !((lex->sql_command == SQLCOM_CREATE_TABLE) &&
-          (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)) &&
-        !((lex->sql_command == SQLCOM_DROP_TABLE) && lex->drop_temporary) &&
-        ((lex->sql_command != SQLCOM_UPDATE_MULTI) &&
-          some_non_temp_table_to_be_updated(thd, all_tables)))
+    if (deny_updates_if_read_only_option(thd, all_tables))
     {
       my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only");
       DBUG_RETURN(-1);