3400 Dmitry Lenev 2010-12-14
Prerequisite patch for Bug#27480 (Extend CREATE TEMPORARY
TABLES privilege to allow temp table operations).
Review fixes in progress. Simplify opening of temporary
tables. Get rid of duplicated code.
modified:
mysql-test/r/merge.result
mysql-test/t/merge.test
sql/sp_head.cc
sql/sql_admin.cc
sql/sql_base.cc
sql/sql_base.h
sql/sql_handler.cc
sql/sql_parse.cc
sql/sql_prepare.cc
sql/sql_show.cc
sql/sql_table.cc
sql/sql_update.cc
3399 Dmitry Lenev 2010-12-06
Prerequisite patch for Bug#27480 (Extend CREATE TEMPORARY
TABLES privilege to allow temp table operations).
Review fixes in progress. Fixed small issues,
removed indentation changes.
modified:
sql/sql_admin.cc
sql/sql_base.cc
sql/sql_class.h
sql/sql_parse.cc
sql/sql_prepare.cc
=== modified file 'mysql-test/r/merge.result'
--- a/mysql-test/r/merge.result 2010-12-02 08:07:11 +0000
+++ b/mysql-test/r/merge.result 2010-12-14 09:15:37 +0000
@@ -2718,7 +2718,10 @@ DROP TABLE tm1, t1, t2, t3, t4, t5;
CREATE TEMPORARY TABLE t1 (c1 INT);
ALTER TABLE t1 ENGINE=MERGE UNION(t_not_exists,t1);
OPTIMIZE TABLE t1;
-ERROR HY000: Can't reopen table: 't1'
+Table Op Msg_type Msg_text
+test.t1 optimize Error Table 'test.t_not_exists' doesn't exist
+test.t1 optimize Error Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist
+test.t1 optimize error Corrupt
DROP TABLE t1;
#
# Bug#36171 - CREATE TEMPORARY TABLE and MERGE engine
@@ -3673,4 +3676,78 @@ ALTER TABLE t1 engine=myisam;
ERROR HY000: Table 't1' was locked with a READ lock and can't be updated
UNLOCK TABLES;
DROP TABLE m1, t1;
+#
+# Additional coverage for refactoring which is made as part
+# of fix for bug #27480 "Extend CREATE TEMPORARY TABLES privilege
+# to allow temp table operations".
+#
+# Check that prelocking works correctly for various variants of
+# merge tables.
+drop table if exists t1, t2, m1;
+drop function if exists f1;
+create table t1 (j int);
+insert into t1 values (1);
+create function f1() returns int return (select count(*) from m1);
+create temporary table t2 (a int) engine=myisam;
+insert into t2 values (1);
+create temporary table m1 (a int) engine=merge union=(t2);
+select f1() from t1;
+f1()
+1
+drop tables t2, m1;
+create table t2 (a int) engine=myisam;
+insert into t2 values (1);
+create table m1 (a int) engine=merge union=(t2);
+select f1() from t1;
+f1()
+1
+drop table m1;
+create temporary table m1 (a int) engine=merge union=(t2);
+select f1() from t1;
+f1()
+1
+drop tables t1, t2, m1;
+drop function f1;
+#
+# Check that REPAIR/CHECK and CHECKSUM statements work correctly
+# for various variants of merge tables.
+create table t1 (a int) engine=myisam;
+insert into t1 values (1);
+create table m1 (a int) engine=merge union=(t1);
+check table m1;
+Table Op Msg_type Msg_text
+test.m1 check status OK
+repair table m1;
+Table Op Msg_type Msg_text
+test.m1 repair note The storage engine for the table doesn't support repair
+checksum table m1;
+Table Checksum
+test.m1 3459908756
+drop tables t1, m1;
+create temporary table t1 (a int) engine=myisam;
+insert into t1 values (1);
+create temporary table m1 (a int) engine=merge union=(t1);
+check table m1;
+Table Op Msg_type Msg_text
+test.m1 check status OK
+repair table m1;
+Table Op Msg_type Msg_text
+test.m1 repair note The storage engine for the table doesn't support repair
+checksum table m1;
+Table Checksum
+test.m1 3459908756
+drop tables t1, m1;
+create table t1 (a int) engine=myisam;
+insert into t1 values (1);
+create temporary table m1 (a int) engine=merge union=(t1);
+check table m1;
+Table Op Msg_type Msg_text
+test.m1 check status OK
+repair table m1;
+Table Op Msg_type Msg_text
+test.m1 repair note The storage engine for the table doesn't support repair
+checksum table m1;
+Table Checksum
+test.m1 3459908756
+drop tables t1, m1;
End of 6.0 tests
=== modified file 'mysql-test/t/merge.test'
--- a/mysql-test/t/merge.test 2010-11-29 14:13:07 +0000
+++ b/mysql-test/t/merge.test 2010-12-14 09:15:37 +0000
@@ -2195,7 +2195,6 @@ DROP TABLE tm1, t1, t2, t3, t4, t5;
--echo #
CREATE TEMPORARY TABLE t1 (c1 INT);
ALTER TABLE t1 ENGINE=MERGE UNION(t_not_exists,t1);
---error ER_CANT_REOPEN_TABLE
OPTIMIZE TABLE t1;
DROP TABLE t1;
@@ -2799,6 +2798,60 @@ UNLOCK TABLES;
DROP TABLE m1, t1;
+--echo #
+--echo # Additional coverage for refactoring which is made as part
+--echo # of fix for bug #27480 "Extend CREATE TEMPORARY TABLES privilege
+--echo # to allow temp table operations".
+--echo #
+--echo # Check that prelocking works correctly for various variants of
+--echo # merge tables.
+--disable_warnings
+drop table if exists t1, t2, m1;
+drop function if exists f1;
+--enable_warnings
+create table t1 (j int);
+insert into t1 values (1);
+create function f1() returns int return (select count(*) from m1);
+create temporary table t2 (a int) engine=myisam;
+insert into t2 values (1);
+create temporary table m1 (a int) engine=merge union=(t2);
+select f1() from t1;
+drop tables t2, m1;
+create table t2 (a int) engine=myisam;
+insert into t2 values (1);
+create table m1 (a int) engine=merge union=(t2);
+select f1() from t1;
+drop table m1;
+create temporary table m1 (a int) engine=merge union=(t2);
+select f1() from t1;
+drop tables t1, t2, m1;
+drop function f1;
+--echo #
+--echo # Check that REPAIR/CHECK and CHECKSUM statements work correctly
+--echo # for various variants of merge tables.
+create table t1 (a int) engine=myisam;
+insert into t1 values (1);
+create table m1 (a int) engine=merge union=(t1);
+check table m1;
+repair table m1;
+checksum table m1;
+drop tables t1, m1;
+create temporary table t1 (a int) engine=myisam;
+insert into t1 values (1);
+create temporary table m1 (a int) engine=merge union=(t1);
+check table m1;
+repair table m1;
+checksum table m1;
+drop tables t1, m1;
+create table t1 (a int) engine=myisam;
+insert into t1 values (1);
+create temporary table m1 (a int) engine=merge union=(t1);
+check table m1;
+repair table m1;
+checksum table m1;
+drop tables t1, m1;
+
+
--echo End of 6.0 tests
--disable_result_log
=== modified file 'sql/sp_head.cc'
--- a/sql/sp_head.cc 2010-12-02 08:07:11 +0000
+++ b/sql/sp_head.cc 2010-12-14 09:15:37 +0000
@@ -3041,7 +3041,7 @@ int sp_instr::exec_open_and_lock_tables(
Check whenever we have access to tables for this statement
and open and lock them before executing instructions core function.
*/
- if (open_and_process_temporary_table_list(thd, tables) ||
+ if (open_temporary_table_list(thd, tables) ||
check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE) ||
open_and_lock_tables(thd, tables, TRUE, 0))
result= -1;
=== modified file 'sql/sql_admin.cc'
--- a/sql/sql_admin.cc 2010-12-06 10:36:38 +0000
+++ b/sql/sql_admin.cc 2010-12-14 09:15:37 +0000
@@ -346,7 +346,7 @@ static bool mysql_admin_table(THD* thd,
if (view_operator_func == NULL)
table->required_type=FRMTYPE_TABLE;
- open_error= open_and_process_temporary_table_list(thd, table);
+ open_error= open_temporary_table_list(thd, table);
if (! open_error)
open_error= open_and_lock_tables(thd, table, TRUE, 0);
=== modified file 'sql/sql_base.cc'
--- a/sql/sql_base.cc 2010-12-06 10:36:38 +0000
+++ b/sql/sql_base.cc 2010-12-14 09:15:37 +0000
@@ -4299,7 +4299,7 @@ open_and_process_table(THD *thd, LEX *le
if (tables->table)
{
DBUG_ASSERT(is_temporary_table(tables->table));
- DBUG_RETURN(FALSE);
+ goto process_table;
}
/*
@@ -4328,7 +4328,7 @@ open_and_process_table(THD *thd, LEX *le
temporary tabletill the execution of substatement for several reasons:
- Temporary table can be a MERGE table with base underlying tables,
so its underlying tables has to be properly open and locked at
- prelocking stage. TODO/FIXME: Add test case for this scenario.
+ prelocking stage.
- Temporary table can be a MERGE table and we might be in PREPARE
phase for a prepared statement. In this case it is important to call
HA_ATTACH_CHILDREN for all merge children.
@@ -4341,11 +4341,14 @@ open_and_process_table(THD *thd, LEX *le
The problem is that since those attributes are not set in merge
children, another round of PREPARE will not help.
*/
- error= open_and_process_temporary_table(thd, tables);
+ error= open_temporary_table(thd, tables);
- if (error || tables->table)
+ if (error)
goto end;
+ if (tables->table)
+ goto process_table;
+
/*
For the tables added by the pre-locking code, attempt to open
the table but fail silently if the table does not exist.
@@ -4359,7 +4362,24 @@ open_and_process_table(THD *thd, LEX *le
safe_to_ignore_table= no_such_table_handler.safely_trapped_errors();
}
else
+ {
+ /*
+ Even if we are opening table not from the prelocking list we
+ still might need to look for a temporary table if this table
+ list element corresponds to underlying table of a merge table.
+ */
+ if (tables->parent_l)
+ {
+ error= open_temporary_table(thd, tables);
+
+ if (error)
+ goto end;
+
+ if (tables->table)
+ goto process_table;
+ }
error= open_table(thd, tables, new_frm_mem, ot_ctx);
+ }
free_root(new_frm_mem, MYF(MY_KEEP_PREALLOC));
@@ -4407,6 +4427,7 @@ open_and_process_table(THD *thd, LEX *le
goto process_view_routines;
}
+process_table:
/*
Special types of open can succeed but still don't set
TABLE_LIST::table to anything.
@@ -4832,7 +4853,7 @@ restart:
goto err;
/* Re-open temporary tables after close_tables_for_reopen(). */
- if (open_and_process_temporary_table_list(thd, *start))
+ if (open_temporary_table_list(thd, *start))
goto err;
error= FALSE;
@@ -4882,7 +4903,7 @@ restart:
goto err;
/* Re-open temporary tables after close_tables_for_reopen(). */
- if (open_and_process_temporary_table_list(thd, *start))
+ if (open_temporary_table_list(thd, *start))
goto err;
error= FALSE;
@@ -5957,14 +5978,13 @@ static void update_field_dependencies(TH
of this thread. Temporary tables are thread-local and "shadow" base
tables with the same name.
- open_temporary_table() should be considered as an internal function.
- Usually, open_and_process_temporary_table() should be used instead to
- open temporary table completely. The only case where
- open_temporary_table() is used directly is implementation of INSERT INTO
- statement (sql_insert.cc).
+ @note One should finalize process of opening temporary table for table
+ list element by calling open_and_process_table(). This function
+ is responsible for table version checking and handling of merge
+ tables.
- Note: we used to check global_read_lock before opening temporary tables.
- However, that limitation was artificial and is removed now.
+ @note We used to check global_read_lock before opening temporary tables.
+ However, that limitation was artificial and is removed now.
@return Error status.
@retval FALSE On success. If a temporary table exists for the given
@@ -5977,11 +5997,15 @@ bool open_temporary_table(THD *thd, TABL
DBUG_ENTER("open_temporary_table");
DBUG_PRINT("enter", ("table: '%s'.'%s'", tl->db, tl->table_name));
+ /*
+ Code in open_table() assumes that TABLE_LIST::table can
+ be non-zero only for pre-opened temporary tables.
+ */
+ DBUG_ASSERT(tl->table == NULL);
+
if (tl->open_type == OT_BASE_ONLY)
{
DBUG_PRINT("info", ("skip_temporary is set"));
-
- tl->table= NULL;
DBUG_RETURN(FALSE);
}
@@ -5992,7 +6016,6 @@ bool open_temporary_table(THD *thd, TABL
if (!table)
{
- tl->table= NULL;
DBUG_RETURN(FALSE);
}
@@ -6026,13 +6049,13 @@ bool open_temporary_table(THD *thd, TABL
}
-bool open_and_process_temporary_table_list(THD *thd, TABLE_LIST *tl_list)
+bool open_temporary_table_list(THD *thd, TABLE_LIST *tl_list)
{
- DBUG_ENTER("open_and_process_temporary_table_list");
+ DBUG_ENTER("open_temporary_table_list");
for (TABLE_LIST *tl= tl_list; tl; tl= tl->next_global)
{
- if (open_and_process_temporary_table(thd, tl))
+ if (open_temporary_table(thd, tl))
DBUG_RETURN(TRUE);
}
@@ -6040,62 +6063,6 @@ bool open_and_process_temporary_table_li
}
-/**
- Open a temporary table specified by TABLE_LIST instance.
-
- @return Error status.
- @retval FALSE On success. Opened temporary table is returned in
- TABLE_LIST::table member. If TABLE_LIST::table is NULL,
- the specified temporary table does not exist.
- @retval TRUE On error. my_error() has been called.
-*/
-
-bool open_and_process_temporary_table(THD *thd, TABLE_LIST *tl)
-{
- DBUG_ENTER("open_and_process_temporary_table");
-
- if (tl->schema_table || tl->derived)
- DBUG_RETURN(FALSE);
-
- if (tl->table)
- DBUG_RETURN(FALSE); // The table is already open.
-
- if (open_temporary_table(thd, tl))
- DBUG_RETURN(TRUE);
-
- /*
- If 'tl->table' is NULL, that means there is no temporary table for the
- table specified by 'tl'. This is not an error.
- */
- if (!tl->table)
- DBUG_RETURN(FALSE);
-
- /* Set appropriate TABLE::lock_type. */
- if (tl->lock_type != TL_UNLOCK && !thd->locked_tables_mode)
- {
- if (tl->lock_type == TL_WRITE_DEFAULT)
- tl->table->reginfo.lock_type= thd->update_lock_default;
- else if (tl->lock_type == TL_READ_DEFAULT)
- tl->table->reginfo.lock_type= read_lock_type_for_table(thd, thd->lex, tl);
- else
- tl->table->reginfo.lock_type= tl->lock_type;
- }
-
- /* Check and update metadata version of a base table. */
- if (check_and_update_table_version(thd, tl, tl->table->s))
- DBUG_RETURN(TRUE);
-
- /* MERGE tables need to access parent and child TABLE_LISTs. */
- DBUG_ASSERT(tl->table->pos_in_table_list == tl);
-
- /* Non-MERGE tables ignore this call. */
- if (tl->table->file->extra(HA_EXTRA_ADD_CHILDREN_LIST))
- DBUG_RETURN(TRUE);
-
- DBUG_RETURN(FALSE);
-}
-
-
/*
Find a field by name in a view that uses merge algorithm.
=== modified file 'sql/sql_base.h'
--- a/sql/sql_base.h 2010-11-29 14:13:07 +0000
+++ b/sql/sql_base.h 2010-12-14 09:15:37 +0000
@@ -267,8 +267,7 @@ void close_temporary_table(THD *thd, TAB
void close_temporary(TABLE *table, bool free_share, bool delete_table);
bool rename_temporary_table(THD* thd, TABLE *table, const char *new_db,
const char *table_name);
-bool open_and_process_temporary_table_list(THD *thd, TABLE_LIST *tl_list);
-bool open_and_process_temporary_table(THD *thd, TABLE_LIST *tl);
+bool open_temporary_table_list(THD *thd, TABLE_LIST *tl_list);
bool open_temporary_table(THD *thd, TABLE_LIST *tl);
bool is_equal(const LEX_STRING *a, const LEX_STRING *b);
=== modified file 'sql/sql_handler.cc'
--- a/sql/sql_handler.cc 2010-11-29 14:13:07 +0000
+++ b/sql/sql_handler.cc 2010-12-14 09:15:37 +0000
@@ -287,12 +287,9 @@ bool mysql_ha_open(THD *thd, TABLE_LIST
*/
DBUG_ASSERT(! hash_tables->table);
- error= open_and_process_temporary_table(thd, hash_tables);
+ error= open_temporary_table(thd, hash_tables);
- if (is_temporary_table(hash_tables))
- hash_tables->table->grant= hash_tables->grant;
-
- if (!error && !hash_tables->table)
+ if (!error)
error= open_tables(thd, &hash_tables, &counter, 0);
if (! error &&
=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc 2010-12-06 10:36:38 +0000
+++ b/sql/sql_parse.cc 2010-12-14 09:15:37 +0000
@@ -1252,7 +1252,7 @@ bool dispatch_command(enum enum_server_c
thd->set_query(fields, query_length);
general_log_print(thd, command, "%s %s", table_list.table_name, fields);
- if (open_and_process_temporary_table(thd, &table_list))
+ if (open_temporary_table(thd, &table_list))
break;
if (check_table_access(thd, SELECT_ACL, &table_list,
@@ -2077,7 +2077,7 @@ mysql_execute_command(THD *thd)
if (sql_command_flags[lex->sql_command] & CF_PREOPEN_TMP_TABLES)
{
- if (open_and_process_temporary_table_list(thd, all_tables))
+ if (open_temporary_table_list(thd, all_tables))
goto error;
}
@@ -2384,7 +2384,7 @@ case SQLCOM_PREPARE:
if (lex->create_info.merge_list.elements)
{
- if (open_and_process_temporary_table_list(
+ if (open_temporary_table_list(
thd, lex->create_info.merge_list.first))
{
res= 1;
@@ -2736,7 +2736,7 @@ end_with_restore_list:
Temporary tables should be opened for SHOW CREATE TABLE, but not
for SHOW CREATE VIEW.
*/
- if (open_and_process_temporary_table_list(thd, all_tables))
+ if (open_temporary_table_list(thd, all_tables))
goto error;
/*
@@ -3226,7 +3226,7 @@ end_with_restore_list:
usual way, they would be closed by close_thread_tables().
*/
- if (open_and_process_temporary_table_list(thd, all_tables))
+ if (open_temporary_table_list(thd, all_tables))
goto error;
if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables,
@@ -4630,10 +4630,6 @@ bool check_single_table_access(THD *thd,
&all_tables->grant.m_internal,
0, no_errors))
goto deny;
-
- if (is_temporary_table(all_tables))
- all_tables->table->grant= all_tables->grant;
-
/* Show only 1 table for check_grant */
if (!(all_tables->belong_to_view &&
(thd->lex->sql_command == SQLCOM_SHOW_FIELDS)) &&
@@ -5066,9 +5062,6 @@ check_table_access(THD *thd, ulong requi
&tables->grant.m_internal,
0, no_errors))
goto deny;
-
- if (is_temporary_table(tables))
- tables->table->grant= tables->grant;
}
thd->security_ctx= backup_ctx;
return check_grant(thd,requirements,org_tables,
=== modified file 'sql/sql_prepare.cc'
--- a/sql/sql_prepare.cc 2010-12-06 10:36:38 +0000
+++ b/sql/sql_prepare.cc 2010-12-14 09:15:37 +0000
@@ -1718,8 +1718,7 @@ static bool mysql_test_create_table(Prep
if (lex->create_info.merge_list.elements)
{
- if (open_and_process_temporary_table_list(
- thd, lex->create_info.merge_list.first))
+ if (open_temporary_table_list(thd, lex->create_info.merge_list.first))
{
DBUG_RETURN(TRUE);
}
@@ -1788,12 +1787,6 @@ static bool mysql_test_create_view(Prepa
if (create_view_precheck(thd, tables, view, lex->create_view_mode))
goto err;
- for (TABLE_LIST *tl= tables; tl; tl= tl->next_global)
- {
- if (is_temporary_table(tl))
- tl->table->grant= tl->grant;
- }
-
if (open_normal_and_derived_tables(thd, tables, MYSQL_OPEN_FORCE_SHARED_MDL))
goto err;
@@ -1989,7 +1982,7 @@ static bool check_prepared_statement(Pre
*/
if (sql_command_flags[sql_command] & CF_PREOPEN_TMP_TABLES)
{
- if (open_and_process_temporary_table_list(thd, tables))
+ if (open_temporary_table_list(thd, tables))
goto error;
}
=== modified file 'sql/sql_show.cc'
--- a/sql/sql_show.cc 2010-12-02 08:07:11 +0000
+++ b/sql/sql_show.cc 2010-12-14 09:15:37 +0000
@@ -3020,7 +3020,7 @@ fill_schema_show_cols_or_idxs(THD *thd,
*/
lex->sql_command= SQLCOM_SHOW_FIELDS;
- res= open_and_process_temporary_table_list(thd, show_table_list);
+ res= open_temporary_table_list(thd, show_table_list);
if (!res)
{
@@ -3635,7 +3635,7 @@ int get_all_tables(THD *thd, TABLE_LIST
show_table_list->i_s_requested_object=
schema_table->i_s_requested_object;
DEBUG_SYNC(thd, "before_open_in_get_all_tables");
- if (open_and_process_temporary_table_list(thd, show_table_list))
+ if (open_temporary_table_list(thd, show_table_list))
goto err;
res= open_normal_and_derived_tables(thd, show_table_list,
(MYSQL_OPEN_IGNORE_FLUSH |
=== modified file 'sql/sql_table.cc'
--- a/sql/sql_table.cc 2010-12-02 08:07:11 +0000
+++ b/sql/sql_table.cc 2010-12-14 09:15:37 +0000
@@ -7304,19 +7304,40 @@ bool mysql_checksum_table(THD *thd, TABL
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
+ /*
+ Close all temporary tables which were pre-open to simplify
+ privilege checking. Clear all references to closed tables.
+ */
+ close_thread_tables(thd);
+ for (table= tables; table; table= table->next_local)
+ table->table= NULL;
+
/* Open one table after the other to keep lock time as short as possible. */
for (table= tables; table; table= table->next_local)
{
- TABLE *t= table->table;
char table_name[NAME_LEN*2+2];
+ TABLE *t;
+ TABLE_LIST *save_next_global;
strxmov(table_name, table->db ,".", table->table_name, NullS);
- if (!table->table)
+ /* Remember old 'next' pointer and break the list. */
+ save_next_global= table->next_global;
+ table->next_global= NULL;
+ table->lock_type= TL_READ;
+ /* Allow to open real tables only. */
+ table->required_type= FRMTYPE_TABLE;
+
+ if (open_temporary_table(thd, table) ||
+ open_and_lock_tables(thd, table, FALSE, 0))
{
- t= table->table= open_n_lock_single_table(thd, table, TL_READ, 0);
- thd->clear_error(); // these errors shouldn't get client
+ t= NULL;
+ thd->clear_error(); // these errors shouldn't get client
}
+ else
+ t= table->table;
+
+ table->next_global= save_next_global;
protocol->prepare_for_resend();
protocol->store(table_name, system_charset_info);
@@ -7414,9 +7435,6 @@ bool mysql_checksum_table(THD *thd, TABL
if (! thd->in_sub_stmt)
trans_rollback_stmt(thd);
close_thread_tables(thd);
-
- if (open_and_process_temporary_table_list(thd, tables))
- goto err;
}
if (protocol->write())
goto err;
=== modified file 'sql/sql_update.cc'
--- a/sql/sql_update.cc 2010-12-02 09:21:50 +0000
+++ b/sql/sql_update.cc 2010-12-14 09:15:37 +0000
@@ -1130,10 +1130,6 @@ int mysql_multi_update_prepare(THD *thd)
0, 0) ||
check_grant(thd, want_privilege, tl, FALSE, 1, FALSE))
DBUG_RETURN(TRUE);
-
- if (is_temporary_table(tl))
- tl->table->grant= tl->grant;
-
}
}
No bundle (reason: useless for push emails).
| Thread |
|---|
| • bzr push into mysql-trunk-bugfixing branch (Dmitry.Lenev:3399 to 3400)Bug#27480 | Dmitry Lenev | 14 Dec |