← Back to team overview

maria-developers team mailing list archive

Re: 4670c85a48c: MDEV-16222 Assertion `0' failed in row_purge_remove_sec_if_poss_leaf on table with virtual columns and indexes


Hi, Aleksey!

Summary: looks ok, but please move clear_error() to the caller
(ha_innodb.cc) and remove the assert.

On Jul 04, Aleksey Midenkov wrote:
> revision-id: 4670c85a48c (mariadb-10.4.5-47-g4670c85a48c)
> parent(s): 0f55a9eb73b
> author: Aleksey Midenkov <midenok@xxxxxxxxx>
> committer: Aleksey Midenkov <midenok@xxxxxxxxx>
> timestamp: 2019-06-27 18:05:25 +0300
> message:
> MDEV-16222 Assertion `0' failed in row_purge_remove_sec_if_poss_leaf on table with virtual columns and indexes
> Cause
> Stale thd->m_stmt_da->m_sql_errno which is from different invocation.
> Fix
> Reset error state before attempt to open table.
> diff --git a/mysql-test/suite/innodb/r/purge_secondary_mdev-16222.result b/mysql-test/suite/innodb/r/purge_secondary_mdev-16222.result
> new file mode 100644
> index 00000000000..30e8f9800fb
> --- /dev/null
> +++ b/mysql-test/suite/innodb/r/purge_secondary_mdev-16222.result
> @@ -0,0 +1,34 @@
> +select * from t1 into outfile 'load.data';
> +Warnings:
> +Warning	1287	'<select expression> INTO <destination>;' is deprecated and will be removed in a future release. Please use 'SELECT <select list> INTO <destination> FROM...' instead

See the warning, better not to use the deprecated syntax, unless you
specifically want to test it,

> +load data infile 'load.data' replace into table t1;
> +set debug_sync= "now WAIT_FOR latch_released";
> +set global debug_dbug= "-d,ib_purge_virtual_mdev_16222_1";
> diff --git a/sql/sql_class.cc b/sql/sql_class.cc
> index d65b6b8ed8d..cb73b7db2de 100644
> --- a/sql/sql_class.cc
> +++ b/sql/sql_class.cc
> @@ -4762,7 +4762,10 @@ TABLE *open_purge_table(THD *thd, const char *db, size_t dblen,
>    DBUG_ASSERT(!error || !ot_ctx.can_recover_from_failed_open());
>    if (unlikely(error))
> +  {
>      close_thread_tables(thd);
> +    thd->clear_error();

We use thd->clear_error() in many places, but over years there were
quite a few problems with it, and now I'd prefer to avoid it whenever

The thing is, you want to remove a specific error on a specific code
path, but thd->clear_error() removes all errors, and no matter how you
got here.

Generally intercepting errors with Internal_error_handler is a much
safer approach.

In this case, though, thd->clear_error() might be okay, but better move
it up the stack, do it in the caller when there can be only one way to
reach this code. Not in a kind-of-generically-looking open table helper.

> +  }
>    DBUG_RETURN(error ? NULL : tl->table);
>  }
> diff --git a/sql/table.cc b/sql/table.cc
> index 9ac167b6adb..93c2c5e88a8 100644
> --- a/sql/table.cc
> +++ b/sql/table.cc
> @@ -8240,6 +8240,7 @@ int TABLE::update_virtual_fields(handler *h, enum_vcol_update_mode update_mode)
>  int TABLE::update_virtual_field(Field *vf)
>  {
> +  DBUG_ASSERT(!in_use->is_error());

Yes, that's what I mean above. There were cases when
update_virtual_field() was called when is_error() was true and
is_error() introduced a bug.

I don't quite remember details, but it was something like, after the
error has happened somewhere, on the way to returning an error, it was
calling update_virtual_field() and some code in a function down the
stack was, like

  if (thd->is_error()) // the error inside a function
  { ... handle it ... }

and the error in this case was not caused by anything in the function,
but existed from before.

The point is, is_error() and clear_error() are global, and you're
generally interested in local state. Whether _this block_ caused an
error.  Clear the error caused by _this function call_. And so on, not
clear an error that might've happened some time somewhere at the
undefined point in the past before this code line was reached.

>    Query_arena backup_arena;
>    DBUG_ENTER("TABLE::update_virtual_field");
>    in_use->set_n_backup_active_arena(expr_arena, &backup_arena);

VP of MariaDB Server Engineering
and security@xxxxxxxxxxx

Follow ups