Skip to content

MDEV-40383:innodb_gis.point_basic fails on replay - #5446

Open
bsrikanth-mariadb wants to merge 1 commit into
bb-12.3-MDEV-39368-test-replay-preview-treefrom
13.1-MDEV-40383-innodb_gis.point_basic-fails-on-replay
Open

MDEV-40383:innodb_gis.point_basic fails on replay#5446
bsrikanth-mariadb wants to merge 1 commit into
bb-12.3-MDEV-39368-test-replay-preview-treefrom
13.1-MDEV-40383-innodb_gis.point_basic-fails-on-replay

Conversation

@bsrikanth-mariadb

@bsrikanth-mariadb bsrikanth-mariadb commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

There are 2 problems: -

  1. The REPLACE statement that is recorded doesn't store the
    value of geometry type field correctly.
  2. The table definition that got recorded has fields with non-null constraint,
    and no default value is specified.
    Also, the "REPLACE INTO" statement that gets stored in the context,
    doesn't have any value specified for these non-null fields.

Solution is to: -

  1. When using REPLACE INTO statement, store all the non-numeric values in HEX,
    whenever conversion from field's charset to output's charset is lossy.
  2. Instead of storing only the column values that were projected in the
    query, store all the non-virtual column values into the recorded
    REPLACE INTO statement.

Implementation details: -

  1. Introduce a new method is_charset_conversion_lossless() in filesort.cc,
    to check if the output charset to which field's data is being written to,
    results in a lossless conversion. If so, non-numeric values being witten
    using REPLACE INTO statement are stored in string representation,
    else they are converted to HEX.
  2. From join_read_const(), and join_read_system() methods in sql_select.cc,
    re-read the const row for all the non-virtual fields in the table.
    After the row is re-read and recorded, restore the table->read_set,
    table->status, and the const row, to the value that was before.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@bsrikanth-mariadb
bsrikanth-mariadb force-pushed the 13.1-MDEV-40383-innodb_gis.point_basic-fails-on-replay branch 2 times, most recently from d7ceeb9 to 12df834 Compare July 24, 2026 02:51
@mariadb-pavithrapandith

Copy link
Copy Markdown

Test failure is fixed with the changes

Comment thread sql/filesort.cc
if (require_quote && pref_str_in_hex && tmp.length())
{
output.append(STRING_WITH_LEN("0x"));
output.append_hex(tmp.ptr(), tmp.length());

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you know the charset of tmp and charset of output. You must always use hex if tmp cannot be converted to output charset without losses. Otherwise you don't need hex. It's not an external property that you need to pass as an argument.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May be I should narrow down the scope of change. For Geometry types I can use HEX, and for others, existing string representation should work, as it was already taken care to use charsets.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No! This is the whole point. Geometry is just a special case of a binary string that cannot necessarily be converted to the target charset. It is not a property of a type, it's a property of the charset. You must use hex in all cases where you cannot perform a lossless conversion from the source to the target charset.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated it now.

Comment thread sql/sql_select.cc
join_read_system()/join_read_const() only fetch the columns present in
table->read_set. That's sufficient for execution, but when we record the
const row for replay it yields an incomplete REPLACE INTO -- columns that
are NOT NULL and have no default then depend on a relaxed sql_mode (see

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just make sure all columns always have defaults and you won't need any of the below!

@Olernov Olernov Jul 24, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vuvova , do you suggest generating default values for any possible column type? How feasible is this given that there may be something fancy like GEOMETRY?

Comment thread sql/filesort.cc Outdated
{
Field *field= *pfield;
/* Generated columns cannot be assigned a value in REPLACE INTO */
if (field->vcol_info)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is also used by dbug_format_row() / dbug_print_row(), so this brings in a regression for those (same applies below too)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree. However, I believe dbug_format_row() / dbug_print_row() are only used when debug is enabled. Since it is low risk method, we clubbed our changes with this. How, about if we always set print_names=true?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At least, I'd suggest adding an argument like print_virtual_cols to format_and_store_row(), otherwise silently dropping vcols looks unexpected

@Olernov Olernov Jul 26, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we always set print_names=true

Yes, this looks more robust than positional printing

Comment thread sql/filesort.cc Outdated
Comment thread sql/sql_select.cc Outdated
Comment thread sql/sql_select.cc
Comment thread sql/sql_select.cc Outdated
@bsrikanth-mariadb
bsrikanth-mariadb force-pushed the 13.1-MDEV-40383-innodb_gis.point_basic-fails-on-replay branch from 12df834 to 4ebbfec Compare July 27, 2026 11:29
@bsrikanth-mariadb
bsrikanth-mariadb marked this pull request as draft July 27, 2026 11:47
@bsrikanth-mariadb
bsrikanth-mariadb force-pushed the 13.1-MDEV-40383-innodb_gis.point_basic-fails-on-replay branch 3 times, most recently from 9f50d3c to cf67fb5 Compare July 27, 2026 12:28
@bsrikanth-mariadb
bsrikanth-mariadb marked this pull request as ready for review July 27, 2026 12:29
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@bsrikanth-mariadb
bsrikanth-mariadb force-pushed the 13.1-MDEV-40383-innodb_gis.point_basic-fails-on-replay branch from cf67fb5 to d817206 Compare July 27, 2026 12:35
Comment thread sql/sql_select.cc Outdated
Comment thread sql/sql_select.cc Outdated
Comment thread sql/filesort.cc Outdated
return length;
}

static bool is_target_cs_superset(CHARSET_INFO *from_cs, CHARSET_INFO *to_cs)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This name is not descriptive, consider renaming to something like:

  • charset_conversion_is_lossless
  • can_convert_losslessly

Or even invert it and rename to charset_conversion_may_lose_data, so that the code on the call site becomes more readable:

if (require_quote && tmp.length() &&
    charset_conversion_may_lose_data(field->charset(), output.charset()))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, make sure @abarkov reviews it. And may be we already have a method to do it, no need to invent a new one

@bsrikanth-mariadb bsrikanth-mariadb Jul 30, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, tried using the function can_be_safely_converted_to() defined in sql_string.h. But, it doesn't work correctly.

@bsrikanth-mariadb
bsrikanth-mariadb force-pushed the 13.1-MDEV-40383-innodb_gis.point_basic-fails-on-replay branch from d817206 to b4d06f3 Compare July 30, 2026 04:21
@bsrikanth-mariadb
bsrikanth-mariadb marked this pull request as draft July 30, 2026 07:09
@bsrikanth-mariadb
bsrikanth-mariadb force-pushed the 13.1-MDEV-40383-innodb_gis.point_basic-fails-on-replay branch from b4d06f3 to 46c4a5e Compare July 30, 2026 10:24
@bsrikanth-mariadb
bsrikanth-mariadb marked this pull request as ready for review July 30, 2026 10:24
There are 2 problems: -
1. The REPLACE statement that is recorded doesn't store the
   value of geometry type field correctly.
2. The table definition that got recorded has fields with non-null constraint,
   and no default value is specified.
   Also, the "REPLACE INTO" statement that gets stored in the context,
   doesn't have any value specified for these non-null fields.

Solution is to: -
1. When using REPLACE INTO statement, store all the non-numeric values in HEX,
   whenever conversion from field's charset to output's charset is lossy.
2. Instead of storing only the column values that were projected in the
   query, store all the non-virtual column values into the recorded
   REPLACE INTO statement.

Implementation details: -
1. Introduce a new method is_charset_conversion_lossless() in filesort.cc,
   to check if the output charset to which field's data is being written to,
   results in a lossless conversion. If so, non-numeric values being witten
   using REPLACE INTO statement are stored in string representation,
   else they are converted to HEX.
2. From join_read_const(), and join_read_system() methods in sql_select.cc,
   re-read the const row for all the non-virtual fields in the table.
   After the row is re-read and recorded, restore the table->read_set,
   table->status, and the const row, to the value that was before.
@bsrikanth-mariadb
bsrikanth-mariadb force-pushed the 13.1-MDEV-40383-innodb_gis.point_basic-fails-on-replay branch from 46c4a5e to a1df1fb Compare August 1, 2026 02:50
@spetrunia

Copy link
Copy Markdown
Member

One missed case:
Context stores table rows not only for const tables but also for the MIN/MAX records in MIN/MAX optimization.

Saving stats for

EXPLAIN SELECT MAX(a) FROM t1;

I can see the same error:

REPLACE INTO db1.t1(a) VALUES (2)' failed: ER_CANT_CREATE_GEOMETRY_OBJECT (1416): Cannot get geometry object from data you send to the GEOMETRY field

Full diff:

diff --git a/mysql-test/main/opt_context_replay_basic.test b/mysql-test/main/opt_context_replay_basic.test
index 17a6354e276..ba2c12d11e9 100644
--- a/mysql-test/main/opt_context_replay_basic.test
+++ b/mysql-test/main/opt_context_replay_basic.test
@@ -392,6 +392,25 @@ set optimizer_replay_context='opt_context';
 --echo # Same query as above, must have same explain:
 EXPLAIN SELECT a, ST_AsText(p) FROM t1 WHERE a = 2 AND p = ST_GeomFromText('POINT(20 20)');
 
+
+--echo # More tests 
+set optimizer_replay_context=NULL;
+alter table t1 add index(a);
+select a from t1;
+SELECT MAX(a) FROM t1;
+set optimizer_record_context=1;
+EXPLAIN SELECT MAX(a) FROM t1;
+select context into dumpfile "../../tmp/dump2.sql"
+from information_schema.optimizer_context;
+set optimizer_record_context=0;
+drop table t1;
+--disable_query_log
+--disable_result_log
+--source "$MYSQLTEST_VARDIR/tmp/dump2.sql"
+--enable_query_log
+--enable_result_log
+set optimizer_replay_context='opt_context';
+
 set optimizer_replay_context='';
 --remove_file "$MYSQLTEST_VARDIR/tmp/dump1.sql"
 drop table t1;

@spetrunia

Copy link
Copy Markdown
Member

I think, dumping MIN/MAX rows can be done in a similar way.
The difference is that MIN/MAX row is typically not the only row in the table.
It should be possible to get the full row by re-reading the row we've just read:

  // save the handler state: is it inited for reading index
  //   (save h->active_index) or for full table scan? 
  h->position(); 
  // Set all needed columns in table->read_set
  h->rnd_int(scan=false);
  h->rnd_pos(h->ref);

@spetrunia

Copy link
Copy Markdown
Member

record_const_row_full() should be in opt_context_store_replay.*, not in sql_select.cc

@spetrunia

Copy link
Copy Markdown
Member

Please apply this cleanup diff

diff --git a/sql/filesort.cc b/sql/filesort.cc
index a2c828b41e2..e5c31f7eeaa 100644
--- a/sql/filesort.cc
+++ b/sql/filesort.cc
@@ -3024,8 +3024,8 @@ static uint make_packed_sortkey(Sort_param *param, uchar *to)
   return length;
 }
 
-static bool is_charset_conversion_lossless(CHARSET_INFO *from_cs,
-                                           CHARSET_INFO *to_cs)
+static bool is_charset_conversion_lossless(const CHARSET_INFO *from_cs,
+                                           const CHARSET_INFO *to_cs)
 {
   if (to_cs == &my_charset_bin)
     return true; // binary swallows any bytes
@@ -3182,8 +3182,7 @@ void format_and_store_row(TABLE *table, const uchar *rec, bool print_names,
       {
         if (require_quote)
           output.append('\'');
-        output.append_for_single_quote_opt_convert(tmp.ptr(), tmp.length(),
-                                                   field->charset());
+        output.append_for_single_quote_opt_convert(tmp);
         if (require_quote)
           output.append('\'');
       }

@spetrunia spetrunia left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the input above

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Development

Successfully merging this pull request may close these issues.

5 participants