1
0
mirror of https://github.com/MariaDB/server.git synced 2025-08-07 00:04:31 +03:00

MDEV-28310 Missing binlog data for INSERT .. ON DUPLICATE KEY UPDATE

MDEV-21810 MBR: Unexpected "Unsafe statement" warning for unsafe IODKU

MDEV-17614 fixes to replication unsafety for INSERT ON DUP KEY UPDATE
on two or more unique key table left a flaw. The fixes checked the
safety condition per each inserted record with the idea to catch a user-created
value to an autoincrement column and when that succeeds the autoincrement column
would become the source of unsafety too.
It was not expected that after a duplicate error the next record's
write_set may become different and the unsafe decision for that
specific record will be computed to screw the Query's binlogging
state and when @@binlog_format is MIXED nothing gets bin-logged.

This case has been already fixed in 10.5.2 by 91ab42a823 that
relocated/optimized THD::decide_logging_format_low() out of the record insert
loop. The safety decision is computed once and at the right time.
Pertinent parts of the commit are cherry-picked.

Also a spurious warning about unsafety is removed when MIXED
@@binlog_format; original MDEV-17614 test result corrected.
The original test of MDEV-17614 is extended and made more readable.
This commit is contained in:
Andrei
2022-05-03 22:38:20 +03:00
parent 141ab971d8
commit a5dc12eefd
10 changed files with 323 additions and 55 deletions

View File

@@ -6285,47 +6285,84 @@ int THD::decide_logging_format(TABLE_LIST *tables)
DBUG_RETURN(0);
}
int THD::decide_logging_format_low(TABLE *table)
/*
Reconsider logging format in case of INSERT...ON DUPLICATE KEY UPDATE
for tables with more than one unique keys in case of MIXED binlog format.
Unsafe means that a master could execute the statement differently than
the slave.
This could can happen in the following cases:
- The unique check are done in different order on master or slave
(different engine or different key order).
- There is a conflict on another key than the first and before the
statement is committed, another connection commits a row that conflicts
on an earlier unique key. Example follows:
Below a and b are unique keys, the table has a row (1,1,0)
connection 1:
INSERT INTO t1 set a=2,b=1,c=0 ON DUPLICATE KEY UPDATE c=1;
connection 2:
INSERT INTO t1 set a=2,b=2,c=0;
If 2 commits after 1 has been executed but before 1 has committed
(and are thus put before the other in the binary log), one will
get different data on the slave:
(1,1,1),(2,2,1) instead of (1,1,1),(2,2,0)
*/
void THD::reconsider_logging_format_for_iodup(TABLE *table)
{
/*
INSERT...ON DUPLICATE KEY UPDATE on a table with more than one unique keys
can be unsafe.
*/
if(wsrep_binlog_format() <= BINLOG_FORMAT_STMT &&
!is_current_stmt_binlog_format_row() &&
!lex->is_stmt_unsafe() &&
lex->sql_command == SQLCOM_INSERT &&
lex->duplicates == DUP_UPDATE)
DBUG_ENTER("reconsider_logging_format_for_iodup");
enum_binlog_format bf= (enum_binlog_format) wsrep_binlog_format();
DBUG_ASSERT(lex->duplicates == DUP_UPDATE);
if (bf <= BINLOG_FORMAT_STMT &&
!is_current_stmt_binlog_format_row())
{
KEY *end= table->s->key_info + table->s->keys;
uint unique_keys= 0;
uint keys= table->s->keys, i= 0;
Field *field;
for (KEY* keyinfo= table->s->key_info;
i < keys && unique_keys <= 1; i++, keyinfo++)
if (keyinfo->flags & HA_NOSAME &&
!(keyinfo->key_part->field->flags & AUTO_INCREMENT_FLAG &&
//User given auto inc can be unsafe
!keyinfo->key_part->field->val_int()))
for (KEY *keyinfo= table->s->key_info; keyinfo < end ; keyinfo++)
{
if (keyinfo->flags & HA_NOSAME)
{
/*
We assume that the following cases will guarantee that the
key is unique if a key part is not set:
- The key part is an autoincrement (autogenerated)
- The key part has a default value that is null and it not
a virtual field that will be calculated later.
*/
for (uint j= 0; j < keyinfo->user_defined_key_parts; j++)
{
field= keyinfo->key_part[j].field;
if(!bitmap_is_set(table->write_set,field->field_index))
goto exit;
Field *field= keyinfo->key_part[j].field;
if (!bitmap_is_set(table->write_set, field->field_index))
{
/* Check auto_increment */
if (field == table->next_number_field)
goto exit;
if (field->is_real_null() && !field->default_value)
goto exit;
}
}
unique_keys++;
if (unique_keys++)
break;
exit:;
}
}
if (unique_keys > 1)
{
lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_INSERT_TWO_KEYS);
binlog_unsafe_warning_flags|= lex->get_stmt_unsafe_flags();
if (bf == BINLOG_FORMAT_STMT && !lex->is_stmt_unsafe())
{
lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_INSERT_TWO_KEYS);
binlog_unsafe_warning_flags|= lex->get_stmt_unsafe_flags();
}
set_current_stmt_binlog_format_row_if_mixed();
return 1;
}
}
return 0;
DBUG_VOID_RETURN;
}
/*