← Back to team overview

maria-developers team mailing list archive

Please review: MDEV-8433 Make field<'broken-string' use indexes

 

Hi Sergei,

Please review my patch for MDEV-8433.


I had doubts about one detail: how to make Field::store() that we're currently creating a value for range search rather than regular doing a
INSERT/UPDATE.

I could think of two options:

1. Using sql_mode, similar to how temporal types pass MODE_INVALID_DATES.


2. Using a new parameter to this method:

  int Field::store(const char *str, uint length, CHARSET_INFO *cs);


The former needed less changes, so I chose to use sql_mode.
But probably N2 would be better. Please suggest.

Thanks.
diff --git a/include/m_ctype.h b/include/m_ctype.h
index c5be24b..6ec74fe 100644
--- a/include/m_ctype.h
+++ b/include/m_ctype.h
@@ -313,6 +313,34 @@ typedef struct my_charset_loader_st
 
 extern int (*my_string_stack_guard)(int);
 
+/*
+  A structure to return the statistics of a native string copying,
+  when no Unicode conversion is involved.
+
+  The stucture is OK to be unitialized before calling a copying routine.
+  A copying routine must populate the structure as follows:
+    - m_source_end_pos must be set by to a non-NULL value
+      in the range of the input string.
+    - m_well_formed_error_pos must be set to NULL if the string was
+      well formed, or to the position of the leftmost bad byte sequence.
+*/
+typedef struct
+{
+  const char *m_source_end_pos;        /* Position where reading stopped */
+  const char *m_well_formed_error_pos; /* Position where a bad byte was found*/
+} MY_STRCOPY_STATUS;
+
+
+/*
+  A structure to return the statistics of a Unicode string conversion.
+*/
+typedef struct
+{
+  MY_STRCOPY_STATUS m_native_copy_status;
+  const char *m_cannot_convert_error_pos;
+} MY_STRCONV_STATUS;
+
+
 /* See strings/CHARSET_INFO.txt for information about this structure  */
 struct my_collation_handler_st
 {
@@ -349,6 +377,47 @@ struct my_collation_handler_st
   void (*hash_sort)(CHARSET_INFO *cs, const uchar *key, size_t len,
 		    ulong *nr1, ulong *nr2); 
   my_bool (*propagate)(CHARSET_INFO *cs, const uchar *str, size_t len);
+  /*
+    Create min and max keys for range search for WHERE field OP 'string',
+    where OP is a non-equality comparison operation (<,<=,=>,>).
+    Note, 'string' can be malformed. Bad bytes are replaced
+    to cs->min_sort_char by make_min_range and
+    to cs->max_sort_char by make_max_range.
+    The result of make_min_range() and make_max_range() is a well-formed
+    string.
+
+    Not more than "dst_length" bytes are copied to "dst".
+    Not more than "nchars" characters are copied to "dst".
+
+    If the result is big enough (i.e. no limitation happened neigher
+    on "dst_length" nor on "nchars"), then:
+    - the result of make_min_range() is less or equal to the original string
+      according to the collation "cs".
+    - the result of make_max_range() is greater or equal to the original string
+      according to the collation "cs".
+
+    TODO: Correct prefix key handing.
+    If the result does not fit the entire string (i.e. limitation happened
+    either on "dst_length" or on "nchars", e.g. in case of prefix keys),
+    then the result string is still well-formed, but:
+    - make_min_range() does not guarantee that its result is less or equal
+      to the original string
+    - make_max_range() does not guarantee that its result is greater or equal
+      to the original string
+    This is a bug and should be eventually fixed.
+    See:
+    - MDEV-8639 Bad result set when using prefix keys and characters less than space
+    - MDEV-8625 Bad result set with ignorable characters when using a prefix key
+    - MDEV-8626 Bad result set with expansions when using a prefix key
+  */
+  size_t  (*make_min_range)(CHARSET_INFO *cs,
+                            char *dst, size_t dst_length,
+                            const char *src, size_t src_length,
+                            size_t nchars, MY_STRCOPY_STATUS *status);
+  size_t  (*make_max_range)(CHARSET_INFO *cs,
+                            char *dst, size_t dst_length,
+                            const char *src, size_t src_length,
+                            size_t nchars, MY_STRCOPY_STATUS *status);
 };
 
 extern MY_COLLATION_HANDLER my_collation_8bit_bin_handler;
@@ -363,34 +432,6 @@ typedef int (*my_charset_conv_wc_mb)(CHARSET_INFO *, my_wc_t,
 typedef size_t (*my_charset_conv_case)(CHARSET_INFO *,
                                        char *, size_t, char *, size_t);
 
-/*
-  A structure to return the statistics of a native string copying,
-  when no Unicode conversion is involved.
-
-  The stucture is OK to be unitialized before calling a copying routine.
-  A copying routine must populate the structure as follows:
-    - m_source_end_pos must be set by to a non-NULL value
-      in the range of the input string.
-    - m_well_formed_error_pos must be set to NULL if the string was
-      well formed, or to the position of the leftmost bad byte sequence.
-*/
-typedef struct
-{
-  const char *m_source_end_pos;        /* Position where reading stopped */
-  const char *m_well_formed_error_pos; /* Position where a bad byte was found*/
-} MY_STRCOPY_STATUS;
-
-
-/*
-  A structure to return the statistics of a Unicode string conversion.
-*/
-typedef struct
-{
-  MY_STRCOPY_STATUS m_native_copy_status;
-  const char *m_cannot_convert_error_pos;
-} MY_STRCONV_STATUS;
-
-
 /* See strings/CHARSET_INFO.txt about information on this structure  */
 struct my_charset_handler_st
 {
@@ -496,7 +537,7 @@ struct my_charset_handler_st
                                     MY_STRCOPY_STATUS *status);
 
   /*
-    copy_fix() - copy a string, replace bad bytes to '?'.
+    copy_fix() - copy a string, replace bad bytes to "replacement_character".
     Not more than "nchars" characters are copied.
 
     status->m_source_end_pos is set to a position in the range
@@ -510,7 +551,8 @@ struct my_charset_handler_st
   size_t  (*copy_fix)(CHARSET_INFO *,
                       char *dst, size_t dst_length,
                       const char *src, size_t src_length,
-                      size_t nchars, MY_STRCOPY_STATUS *status);
+                      size_t nchars, MY_STRCOPY_STATUS *status,
+                      my_wc_t replacement_character);
   /**
     Write a character to the target string, using its native code.
     For Unicode character sets (utf8, ucs2, utf16, utf16le, utf32, filename)
@@ -660,11 +702,13 @@ extern uint my_instr_simple(CHARSET_INFO *,
 size_t my_copy_8bit(CHARSET_INFO *,
                     char *dst, size_t dst_length,
                     const char *src, size_t src_length,
-                    size_t nchars, MY_STRCOPY_STATUS *);
+                    size_t nchars, MY_STRCOPY_STATUS *,
+                    my_wc_t replacement_character);
 size_t my_copy_fix_mb(CHARSET_INFO *cs,
                       char *dst, size_t dst_length,
                       const char *src, size_t src_length,
-                      size_t nchars, MY_STRCOPY_STATUS *);
+                      size_t nchars, MY_STRCOPY_STATUS *,
+                      my_wc_t replacement_character);
 
 /* Functions for 8bit */
 extern size_t my_caseup_str_8bit(CHARSET_INFO *, char *);
@@ -718,6 +762,25 @@ ulonglong my_strntoull10rnd_ucs2(CHARSET_INFO *cs,
 
 void my_fill_8bit(CHARSET_INFO *cs, char* to, size_t l, int fill);
 
+
+size_t my_make_min_range_8bit(CHARSET_INFO *cs,
+                              char *dst, size_t dst_length,
+                              const char *src, size_t src_length,
+                              size_t nchars, MY_STRCOPY_STATUS *);
+size_t my_make_max_range_8bit(CHARSET_INFO *cs,
+                              char *dst, size_t dst_length,
+                              const char *src, size_t src_length,
+                              size_t nchars, MY_STRCOPY_STATUS *);
+
+size_t my_make_min_range_mb(CHARSET_INFO *cs,
+                            char *dst, size_t dst_length,
+                            const char *src, size_t src_length,
+                            size_t nchars, MY_STRCOPY_STATUS *);
+size_t my_make_max_range_mb(CHARSET_INFO *cs,
+                            char *dst, size_t dst_length,
+                            const char *src, size_t src_length,
+                            size_t nchars, MY_STRCOPY_STATUS *);
+
 /* For 8-bit character set */
 my_bool  my_like_range_simple(CHARSET_INFO *cs,
 			      const char *ptr, size_t ptr_length,
diff --git a/mysql-test/r/ctype_uca.result b/mysql-test/r/ctype_uca.result
index c531aa2..5e33ba4 100644
--- a/mysql-test/r/ctype_uca.result
+++ b/mysql-test/r/ctype_uca.result
@@ -6567,7 +6567,7 @@ aÖ€
 EXPLAIN
 SELECT ch FROM t1 FORCE KEY (ch) WHERE ch<'a𝌆' ORDER BY ch;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	index	ch	ch	182	NULL	#	Using where; Using index
+1	SIMPLE	t1	range	ch	ch	182	NULL	#	Using where; Using index
 Warnings:
 Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86' for column 'ch' at row 1
 SELECT ch FROM t1 FORCE KEY (ch) WHERE ch<'a𝌆' ORDER BY ch;
@@ -6594,7 +6594,7 @@ Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86' for column 'ch' at row 1
 EXPLAIN
 SELECT ch FROM t1 FORCE KEY (ch) WHERE ch<'a𝌆b' ORDER BY ch;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	index	ch	ch	182	NULL	#	Using where; Using index
+1	SIMPLE	t1	range	ch	ch	182	NULL	#	Using where; Using index
 Warnings:
 Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86b' for column 'ch' at row 1
 SELECT ch FROM t1 FORCE KEY (ch) WHERE ch<'a𝌆b' ORDER BY ch;
@@ -6627,25 +6627,29 @@ z
 EXPLAIN
 SELECT ch FROM t1 FORCE KEY (ch) WHERE ch>'a𝌆' ORDER BY ch;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	index	ch	ch	182	NULL	#	Using where; Using index
+1	SIMPLE	t1	range	ch	ch	182	NULL	#	Using where; Using index
 Warnings:
 Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86' for column 'ch' at row 1
+Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86' for column 'ch' at row 1
 SELECT ch FROM t1 FORCE KEY (ch) WHERE ch>'a𝌆' ORDER BY ch;
 ch
 z
 Warnings:
 Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86' for column 'ch' at row 1
+Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86' for column 'ch' at row 1
 EXPLAIN
 SELECT ch FROM t1 FORCE KEY (ch) WHERE ch>'a𝌆b' ORDER BY ch;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	index	ch	ch	182	NULL	#	Using where; Using index
+1	SIMPLE	t1	range	ch	ch	182	NULL	#	Using where; Using index
 Warnings:
 Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86b' for column 'ch' at row 1
+Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86b' for column 'ch' at row 1
 SELECT ch FROM t1 FORCE KEY (ch) WHERE ch>'a𝌆b' ORDER BY ch;
 ch
 z
 Warnings:
 Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86b' for column 'ch' at row 1
+Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86b' for column 'ch' at row 1
 ALTER TABLE t1 DROP KEY ch;
 # 0xD18F would be a good 2-byte character, 0xD1 is an incomplete sequence
 SET @query=CONCAT('SELECT ch FROM t1 WHERE ch=''a', 0xD1,'''');
diff --git a/mysql-test/r/ctype_uca_innodb.result b/mysql-test/r/ctype_uca_innodb.result
index 8b507d9..eff50f5 100644
--- a/mysql-test/r/ctype_uca_innodb.result
+++ b/mysql-test/r/ctype_uca_innodb.result
@@ -103,9 +103,10 @@ aÖ€
 EXPLAIN
 SELECT ch FROM t1 FORCE KEY (ch) WHERE ch<'a𝌆' ORDER BY ch;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	index	ch	ch	182	NULL	#	Using where; Using index
+1	SIMPLE	t1	range	ch	ch	182	NULL	#	Using where; Using index
 Warnings:
 Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86' for column 'ch' at row 1
+Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86' for column 'ch' at row 1
 SELECT ch FROM t1 FORCE KEY (ch) WHERE ch<'a𝌆' ORDER BY ch;
 ch
 a
@@ -127,12 +128,14 @@ aЀ
 aÖ€
 Warnings:
 Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86' for column 'ch' at row 1
+Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86' for column 'ch' at row 1
 EXPLAIN
 SELECT ch FROM t1 FORCE KEY (ch) WHERE ch<'a𝌆b' ORDER BY ch;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	index	ch	ch	182	NULL	#	Using where; Using index
+1	SIMPLE	t1	range	ch	ch	182	NULL	#	Using where; Using index
 Warnings:
 Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86b' for column 'ch' at row 1
+Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86b' for column 'ch' at row 1
 SELECT ch FROM t1 FORCE KEY (ch) WHERE ch<'a𝌆b' ORDER BY ch;
 ch
 a
@@ -154,6 +157,7 @@ aЀ
 aÖ€
 Warnings:
 Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86b' for column 'ch' at row 1
+Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86b' for column 'ch' at row 1
 SELECT ch FROM t1 IGNORE KEY (ch) WHERE ch>'a𝌆' ORDER BY ch;
 ch
 z
@@ -163,25 +167,29 @@ z
 EXPLAIN
 SELECT ch FROM t1 FORCE KEY (ch) WHERE ch>'a𝌆' ORDER BY ch;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	index	ch	ch	182	NULL	#	Using where; Using index
+1	SIMPLE	t1	range	ch	ch	182	NULL	#	Using where; Using index
 Warnings:
 Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86' for column 'ch' at row 1
+Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86' for column 'ch' at row 1
 SELECT ch FROM t1 FORCE KEY (ch) WHERE ch>'a𝌆' ORDER BY ch;
 ch
 z
 Warnings:
 Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86' for column 'ch' at row 1
+Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86' for column 'ch' at row 1
 EXPLAIN
 SELECT ch FROM t1 FORCE KEY (ch) WHERE ch>'a𝌆b' ORDER BY ch;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	index	ch	ch	182	NULL	#	Using where; Using index
+1	SIMPLE	t1	range	ch	ch	182	NULL	#	Using where; Using index
 Warnings:
 Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86b' for column 'ch' at row 1
+Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86b' for column 'ch' at row 1
 SELECT ch FROM t1 FORCE KEY (ch) WHERE ch>'a𝌆b' ORDER BY ch;
 ch
 z
 Warnings:
 Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86b' for column 'ch' at row 1
+Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86b' for column 'ch' at row 1
 ALTER TABLE t1 DROP KEY ch;
 # 0xD18F would be a good 2-byte character, 0xD1 is an incomplete sequence
 SET @query=CONCAT('SELECT ch FROM t1 WHERE ch=''a', 0xD1,'''');
diff --git a/mysql-test/r/ctype_utf8.result b/mysql-test/r/ctype_utf8.result
index 216cea3..7f2b06d 100644
--- a/mysql-test/r/ctype_utf8.result
+++ b/mysql-test/r/ctype_utf8.result
@@ -5521,9 +5521,10 @@ aÖ€
 EXPLAIN
 SELECT ch FROM t1 FORCE KEY (ch) WHERE ch<'a𝌆' ORDER BY ch;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	index	ch	ch	182	NULL	#	Using where; Using index
+1	SIMPLE	t1	range	ch	ch	182	NULL	#	Using where; Using index
 Warnings:
 Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86' for column 'ch' at row 1
+Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86' for column 'ch' at row 1
 SELECT ch FROM t1 FORCE KEY (ch) WHERE ch<'a𝌆' ORDER BY ch;
 ch
 a
@@ -5545,12 +5546,14 @@ aЀ
 aÖ€
 Warnings:
 Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86' for column 'ch' at row 1
+Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86' for column 'ch' at row 1
 EXPLAIN
 SELECT ch FROM t1 FORCE KEY (ch) WHERE ch<'a𝌆b' ORDER BY ch;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	index	ch	ch	182	NULL	#	Using where; Using index
+1	SIMPLE	t1	range	ch	ch	182	NULL	#	Using where; Using index
 Warnings:
 Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86b' for column 'ch' at row 1
+Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86b' for column 'ch' at row 1
 SELECT ch FROM t1 FORCE KEY (ch) WHERE ch<'a𝌆b' ORDER BY ch;
 ch
 a
@@ -5572,6 +5575,7 @@ aЀ
 aÖ€
 Warnings:
 Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86b' for column 'ch' at row 1
+Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86b' for column 'ch' at row 1
 SELECT ch FROM t1 IGNORE KEY (ch) WHERE ch>'a𝌆' ORDER BY ch;
 ch
 z
@@ -5581,25 +5585,29 @@ z
 EXPLAIN
 SELECT ch FROM t1 FORCE KEY (ch) WHERE ch>'a𝌆' ORDER BY ch;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	index	ch	ch	182	NULL	#	Using where; Using index
+1	SIMPLE	t1	range	ch	ch	182	NULL	#	Using where; Using index
 Warnings:
 Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86' for column 'ch' at row 1
+Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86' for column 'ch' at row 1
 SELECT ch FROM t1 FORCE KEY (ch) WHERE ch>'a𝌆' ORDER BY ch;
 ch
 z
 Warnings:
 Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86' for column 'ch' at row 1
+Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86' for column 'ch' at row 1
 EXPLAIN
 SELECT ch FROM t1 FORCE KEY (ch) WHERE ch>'a𝌆b' ORDER BY ch;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	index	ch	ch	182	NULL	#	Using where; Using index
+1	SIMPLE	t1	range	ch	ch	182	NULL	#	Using where; Using index
 Warnings:
 Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86b' for column 'ch' at row 1
+Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86b' for column 'ch' at row 1
 SELECT ch FROM t1 FORCE KEY (ch) WHERE ch>'a𝌆b' ORDER BY ch;
 ch
 z
 Warnings:
 Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86b' for column 'ch' at row 1
+Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86b' for column 'ch' at row 1
 ALTER TABLE t1 DROP KEY ch;
 # 0xD18F would be a good 2-byte character, 0xD1 is an incomplete sequence
 SET @query=CONCAT('SELECT ch FROM t1 WHERE ch=''a', 0xD1,'''');
@@ -5765,7 +5773,7 @@ aÖ€
 EXPLAIN
 SELECT ch FROM t1 FORCE KEY (ch) WHERE ch<'a𝌆' ORDER BY ch;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	index	ch	ch	182	NULL	#	Using where; Using index
+1	SIMPLE	t1	range	ch	ch	182	NULL	#	Using where; Using index
 Warnings:
 Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86' for column 'ch' at row 1
 SELECT ch FROM t1 FORCE KEY (ch) WHERE ch<'a𝌆' ORDER BY ch;
@@ -5792,7 +5800,7 @@ Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86' for column 'ch' at row 1
 EXPLAIN
 SELECT ch FROM t1 FORCE KEY (ch) WHERE ch<'a𝌆b' ORDER BY ch;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	index	ch	ch	182	NULL	#	Using where; Using index
+1	SIMPLE	t1	range	ch	ch	182	NULL	#	Using where; Using index
 Warnings:
 Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86b' for column 'ch' at row 1
 SELECT ch FROM t1 FORCE KEY (ch) WHERE ch<'a𝌆b' ORDER BY ch;
@@ -5825,25 +5833,29 @@ z
 EXPLAIN
 SELECT ch FROM t1 FORCE KEY (ch) WHERE ch>'a𝌆' ORDER BY ch;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	index	ch	ch	182	NULL	#	Using where; Using index
+1	SIMPLE	t1	range	ch	ch	182	NULL	#	Using where; Using index
 Warnings:
 Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86' for column 'ch' at row 1
+Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86' for column 'ch' at row 1
 SELECT ch FROM t1 FORCE KEY (ch) WHERE ch>'a𝌆' ORDER BY ch;
 ch
 z
 Warnings:
 Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86' for column 'ch' at row 1
+Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86' for column 'ch' at row 1
 EXPLAIN
 SELECT ch FROM t1 FORCE KEY (ch) WHERE ch>'a𝌆b' ORDER BY ch;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	index	ch	ch	182	NULL	#	Using where; Using index
+1	SIMPLE	t1	range	ch	ch	182	NULL	#	Using where; Using index
 Warnings:
 Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86b' for column 'ch' at row 1
+Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86b' for column 'ch' at row 1
 SELECT ch FROM t1 FORCE KEY (ch) WHERE ch>'a𝌆b' ORDER BY ch;
 ch
 z
 Warnings:
 Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86b' for column 'ch' at row 1
+Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86b' for column 'ch' at row 1
 ALTER TABLE t1 DROP KEY ch;
 # 0xD18F would be a good 2-byte character, 0xD1 is an incomplete sequence
 SET @query=CONCAT('SELECT ch FROM t1 WHERE ch=''a', 0xD1,'''');
@@ -10116,5 +10128,85 @@ Warnings:
 Note	1003	select `test`.`t1`.`a` AS `a` from `test`.`t1` where ((`test`.`t1`.`a` = 10) and (`test`.`t1`.`a` = '1e1'))
 DROP TABLE t1;
 #
+# MDEV-8433 Make field<'broken-string' use indexes
+#
+SET NAMES 'utf8';
+CREATE TABLE t1 (
+id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
+a varchar(60) COLLATE utf8_general_ci NOT NULL DEFAULT '',
+KEY(a)
+);
+INSERT INTO t1 (a) VALUES ('a0'),('a1'),('a2'),('a3');
+INSERT INTO t1 (a) VALUES ('b0'),('b1'),('b2'),('b3');
+INSERT INTO t1 (a) VALUES ('c0'),('c1'),('c2'),('c3');
+INSERT INTO t1 (a) VALUES ('d0'),('d1'),('d2'),('d3');
+INSERT INTO t1 (a) VALUES ('e0'),('e1'),('e2'),('e3');
+INSERT INTO t1 (a) VALUES ('v0'),('v1'),('v2'),('v3');
+INSERT INTO t1 (a) VALUES ('w0'),('w1'),('w2'),('w3');
+INSERT INTO t1 (a) VALUES ('x0'),('x1'),('x2'),('x3');
+INSERT INTO t1 (a) VALUES ('y0'),('y1'),('y2'),('y3');
+INSERT INTO t1 (a) VALUES ('z0'),('z1'),('z2'),('z3');
+EXPLAIN SELECT * FROM t1 WHERE a <  'b𝌆';
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	range	a	a	182	NULL	9	Using index condition
+Warnings:
+Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86' for column 'a' at row 1
+EXPLAIN SELECT * FROM t1 WHERE a <= 'b𝌆';
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	range	a	a	182	NULL	9	Using index condition
+Warnings:
+Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86' for column 'a' at row 1
+EXPLAIN SELECT * FROM t1 WHERE a >  'y𝌆';
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	range	a	a	182	NULL	9	Using index condition
+Warnings:
+Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86' for column 'a' at row 1
+EXPLAIN SELECT * FROM t1 WHERE a >= 'y𝌆';
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	range	a	a	182	NULL	9	Using index condition
+Warnings:
+Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86' for column 'a' at row 1
+SELECT * FROM t1 WHERE a <  'b𝌆';
+id	a
+1	a0
+2	a1
+3	a2
+4	a3
+5	b0
+6	b1
+7	b2
+8	b3
+Warnings:
+Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86' for column 'a' at row 1
+SELECT * FROM t1 WHERE a <= 'b𝌆';
+id	a
+1	a0
+2	a1
+3	a2
+4	a3
+5	b0
+6	b1
+7	b2
+8	b3
+Warnings:
+Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86' for column 'a' at row 1
+SELECT * FROM t1 WHERE a >  'y𝌆';
+id	a
+37	z0
+38	z1
+39	z2
+40	z3
+Warnings:
+Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86' for column 'a' at row 1
+SELECT * FROM t1 WHERE a >= 'y𝌆';
+id	a
+37	z0
+38	z1
+39	z2
+40	z3
+Warnings:
+Warning	1366	Incorrect string value: '\xF0\x9D\x8C\x86' for column 'a' at row 1
+DROP TABLE t1;
+#
 # End of 10.1 tests
 #
diff --git a/mysql-test/r/range.result b/mysql-test/r/range.result
index a7fb28c..3bbafef 100644
--- a/mysql-test/r/range.result
+++ b/mysql-test/r/range.result
@@ -2087,7 +2087,7 @@ Warning	1366	Incorrect string value: '\xF0\x9F\x98\x81' for column 'fd' at row 1
 # The following must not use range access:
 explain select count(*) from t1 where fd <'😁';
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	index	ix_fd	ix_fd	63	NULL	#	Using where; Using index
+1	SIMPLE	t1	range	ix_fd	ix_fd	63	NULL	#	Using where; Using index
 Warnings:
 Warning	1366	Incorrect string value: '\xF0\x9F\x98\x81' for column 'fd' at row 1
 select count(*) from t1 where fd <'😁';
diff --git a/mysql-test/r/range_mrr_icp.result b/mysql-test/r/range_mrr_icp.result
index 54dd300..91e601c 100644
--- a/mysql-test/r/range_mrr_icp.result
+++ b/mysql-test/r/range_mrr_icp.result
@@ -2089,7 +2089,7 @@ Warning	1366	Incorrect string value: '\xF0\x9F\x98\x81' for column 'fd' at row 1
 # The following must not use range access:
 explain select count(*) from t1 where fd <'😁';
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	index	ix_fd	ix_fd	63	NULL	#	Using where; Using index
+1	SIMPLE	t1	range	ix_fd	ix_fd	63	NULL	#	Using where; Using index
 Warnings:
 Warning	1366	Incorrect string value: '\xF0\x9F\x98\x81' for column 'fd' at row 1
 select count(*) from t1 where fd <'😁';
diff --git a/mysql-test/t/ctype_utf8.test b/mysql-test/t/ctype_utf8.test
index e89247d..95a6d23 100644
--- a/mysql-test/t/ctype_utf8.test
+++ b/mysql-test/t/ctype_utf8.test
@@ -1818,6 +1818,34 @@ SELECT * FROM t1 WHERE (a,a)=(10,'1e1');
 EXPLAIN EXTENDED SELECT * FROM t1 WHERE (a,a)=(10,'1e1');
 DROP TABLE t1;
 
+--echo #
+--echo # MDEV-8433 Make field<'broken-string' use indexes
+--echo #
+SET NAMES 'utf8';
+CREATE TABLE t1 (
+  id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
+  a varchar(60) COLLATE utf8_general_ci NOT NULL DEFAULT '',
+  KEY(a)
+);
+INSERT INTO t1 (a) VALUES ('a0'),('a1'),('a2'),('a3');
+INSERT INTO t1 (a) VALUES ('b0'),('b1'),('b2'),('b3');
+INSERT INTO t1 (a) VALUES ('c0'),('c1'),('c2'),('c3');
+INSERT INTO t1 (a) VALUES ('d0'),('d1'),('d2'),('d3');
+INSERT INTO t1 (a) VALUES ('e0'),('e1'),('e2'),('e3');
+INSERT INTO t1 (a) VALUES ('v0'),('v1'),('v2'),('v3');
+INSERT INTO t1 (a) VALUES ('w0'),('w1'),('w2'),('w3');
+INSERT INTO t1 (a) VALUES ('x0'),('x1'),('x2'),('x3');
+INSERT INTO t1 (a) VALUES ('y0'),('y1'),('y2'),('y3');
+INSERT INTO t1 (a) VALUES ('z0'),('z1'),('z2'),('z3');
+EXPLAIN SELECT * FROM t1 WHERE a <  'b𝌆';
+EXPLAIN SELECT * FROM t1 WHERE a <= 'b𝌆';
+EXPLAIN SELECT * FROM t1 WHERE a >  'y𝌆';
+EXPLAIN SELECT * FROM t1 WHERE a >= 'y𝌆';
+SELECT * FROM t1 WHERE a <  'b𝌆';
+SELECT * FROM t1 WHERE a <= 'b𝌆';
+SELECT * FROM t1 WHERE a >  'y𝌆';
+SELECT * FROM t1 WHERE a >= 'y𝌆';
+DROP TABLE t1;
 
 --echo #
 --echo # End of 10.1 tests
diff --git a/sql/field.cc b/sql/field.cc
index 6ca0d4a..0fd12bf 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -6302,6 +6302,52 @@ bool Field_datetimef::get_TIME(MYSQL_TIME *ltime, const uchar *pos,
 ** A string may be varchar or binary
 ****************************************************************************/
 
+/**
+  Copy a string (on INSERT/SET) or make a search range key.
+*/
+uint
+Field_longstr::Copier::well_formed_copy(THD *thd,
+                                        CHARSET_INFO *to_cs,
+                                        char *to, uint to_length,
+                                        CHARSET_INFO *from_cs,
+                                        const char *from, uint from_length,
+                                        uint nchars)
+{
+  if (use_native_copy(to_cs, from_cs))
+  {
+    m_cannot_convert_error_pos= NULL;
+    if (thd->variables.sql_mode & MODE_GET_MM_LEAF_GT)
+    {
+      /*
+        We're in range optimizer, e.g.:
+          SELECT * FROM t1 WHERE a>'string'
+          SELECT * FROM t1 WHERE a>='string'
+      */
+      return to_cs->coll->make_min_range(to_cs, to, to_length, from, from_length,
+                                         nchars, &m_native_copy_status);
+    }
+    else if (thd->variables.sql_mode & MODE_GET_MM_LEAF_LT)
+    {
+      /*
+        We're in range optimizer, e.g.:
+          SELECT * FROM t1 WHERE a<'string'
+          SELECT * FROM t1 WHERE a<='string'
+      */
+      return to_cs->coll->make_max_range(to_cs, to, to_length, from, from_length,
+                                         nchars, &m_native_copy_status);
+    }
+    else
+    {
+      // A regular INSERT or SET.
+      return to_cs->cset->copy_fix(to_cs, to, to_length, from, from_length,
+                                   nchars, &m_native_copy_status, '?');
+    }
+  }
+  return my_convert_fix(to_cs, to, to_length, from_cs, from, from_length,
+                        nchars, this);
+}
+
+
 /*
   Report "not well formed" or "cannot convert" error
   after storing a character string info a field.
@@ -6395,12 +6441,12 @@ int Field_string::store(const char *from,uint length,CHARSET_INFO *cs)
 {
   ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
   uint copy_length;
-  String_copier copier;
+  Copier copier;
 
   /* See the comment for Field_long::store(long long) */
   DBUG_ASSERT(!table || table->in_use == current_thd);
 
-  copy_length= copier.well_formed_copy(field_charset,
+  copy_length= copier.well_formed_copy(get_thd(), field_charset,
                                        (char*) ptr, field_length,
                                        cs, from, length,
                                        field_length / field_charset->mbmaxlen);
@@ -6949,9 +6995,9 @@ int Field_varstring::store(const char *from,uint length,CHARSET_INFO *cs)
 {
   ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
   uint copy_length;
-  String_copier copier;
+  Copier copier;
 
-  copy_length= copier.well_formed_copy(field_charset,
+  copy_length= copier.well_formed_copy(get_thd(), field_charset,
                                        (char*) ptr + length_bytes,
                                        field_length,
                                        cs, from, length,
@@ -7418,7 +7464,7 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs)
 {
   ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
   uint copy_length, new_length;
-  String_copier copier;
+  Copier copier;
   const char *tmp;
   char buff[STRING_BUFFER_USUAL_SIZE];
   String tmpstr(buff,sizeof(buff), &my_charset_bin);
@@ -7467,9 +7513,9 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs)
     bmove(ptr + packlength, (uchar*) &tmp, sizeof(char*));
     return 0;
   }
-  copy_length= copier.well_formed_copy(field_charset,
+  copy_length= copier.well_formed_copy(get_thd(), field_charset,
                                        (char*) value.ptr(), new_length,
-                                       cs, from, length);
+                                       cs, from, length, length);
   Field_blob::store_length(copy_length);
   tmp= value.ptr();
   bmove(ptr+packlength,(uchar*) &tmp,sizeof(char*));
diff --git a/sql/field.h b/sql/field.h
index 8c42195..aa83a10 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -1194,6 +1194,19 @@ class Field_str :public Field {
 class Field_longstr :public Field_str
 {
 protected:
+
+  /*
+    A String_copier extension that can create keys for the range optimizer.
+  */
+  class Copier: public String_copier
+  {
+  public:
+    uint well_formed_copy(THD *,
+                          CHARSET_INFO *to_cs, char *to, uint to_length,
+                          CHARSET_INFO *from_cs, const char *from,
+                          uint from_length, uint nchars);
+  };
+
   int report_if_important_data(const char *ptr, const char *end,
                                bool count_spaces);
   bool check_string_copy_error(const String_copier *copier,
diff --git a/sql/item.cc b/sql/item.cc
index d14701d..a26e9be 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -1293,7 +1293,8 @@ CHARSET_INFO *Item::default_charset()
    for example in opt_range to adjust the key value to fit the column.
 */
 
-int Item::save_in_field_no_warnings(Field *field, bool no_conversions)
+int Item::save_in_field_no_warnings(Field *field, bool no_conversions,
+                                    ulonglong sql_mode_add)
 {
   int res;
   TABLE *table= field->table;
@@ -1302,7 +1303,7 @@ int Item::save_in_field_no_warnings(Field *field, bool no_conversions)
   my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
   ulonglong sql_mode= thd->variables.sql_mode;
   thd->variables.sql_mode&= ~(MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE);
-  thd->variables.sql_mode|= MODE_INVALID_DATES;
+  thd->variables.sql_mode|= MODE_INVALID_DATES | sql_mode_add;
   thd->count_cuted_fields= CHECK_FIELD_IGNORE;
 
   res= save_in_field(field, no_conversions);
diff --git a/sql/item.h b/sql/item.h
index b611194..b0d0623 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -733,7 +733,8 @@ class Item: public Type_std_attributes
   */
   virtual inline void quick_fix_field() { fixed= 1; }
   /* Function returns 1 on overflow and -1 on fatal errors */
-  int save_in_field_no_warnings(Field *field, bool no_conversions);
+  int save_in_field_no_warnings(Field *field, bool no_conversions,
+                                ulonglong sql_mode_add= 0);
   virtual int save_in_field(Field *field, bool no_conversions);
   virtual void save_org_in_field(Field *field,
                                  fast_field_copier data
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index af50fb8..7b12b65 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -7842,17 +7842,46 @@ Item_bool_func::get_mm_leaf(RANGE_OPT_PARAM *param,
   */
   if (field->cmp_type() == STRING_RESULT && value->cmp_type() != STRING_RESULT)
     goto end;
-  err= value->save_in_field_no_warnings(field, 1);
+  ulonglong sql_mode_add;
+  switch (type) {
+  case LT_FUNC:
+  case LE_FUNC:
+    sql_mode_add= MODE_GET_MM_LEAF_LT;
+    break;
+  case GE_FUNC:
+  case GT_FUNC:
+    sql_mode_add= MODE_GET_MM_LEAF_GT;
+    break;
+  default:
+    DBUG_ASSERT(0);
+    // Pass through
+  case EQ_FUNC:
+  case EQUAL_FUNC:
+    sql_mode_add= 0;
+  }
+  err= value->save_in_field_no_warnings(field, 1, sql_mode_add);
   if (err == 2 && field->cmp_type() == STRING_RESULT)
   {
     if (type == EQ_FUNC || type == EQUAL_FUNC)
     {
       tree= new (alloc) SEL_ARG(field, 0, 0);
       tree->type= SEL_ARG::IMPOSSIBLE;
+      goto end;
+    }
+    else
+    {
+      /*
+        Here we have Field_string, Field_varstring, or Field_blob
+        and a non-equality operation: LE_FUNC, LT_FUNC, GE_FUNC or GT_FUNC.
+        err==2 means that field->store(str, length, cs)
+        (that was called from value->save_in_field_no_warnings())
+        found some badly formed characters and replaced them
+        to min_sort_char (for GE_FUNC and GT_FUNC), or
+        to max_sort_char (for LE_FUNC and LT_FUNC).
+        Ignore the error and proceed with the SEL_ARG creation.
+      */
+      err= 0;
     }
-    else 
-      tree= NULL; /*  Cannot infer anything */
-    goto end;
   }
   if (err > 0)
   {
diff --git a/sql/sql_class.h b/sql/sql_class.h
index ae1d641..ceadecf 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -140,6 +140,9 @@ enum enum_binlog_row_image {
 #define MODE_NO_ENGINE_SUBSTITUTION     (1ULL << 30)
 #define MODE_PAD_CHAR_TO_FULL_LENGTH    (1ULL << 31)
 
+#define MODE_GET_MM_LEAF_LT             (1ULL << 32)
+#define MODE_GET_MM_LEAF_GT             (1ULL << 33)
+
 /* Bits for different old style modes */
 #define OLD_MODE_NO_DUP_KEY_WARNINGS_WITH_IGNORE	(1 << 0)
 #define OLD_MODE_NO_PROGRESS_INFO			(1 << 1)
diff --git a/sql/sql_string.cc b/sql/sql_string.cc
index 379609a..1f88eda 100644
--- a/sql/sql_string.cc
+++ b/sql/sql_string.cc
@@ -908,14 +908,11 @@ String_copier::well_formed_copy(CHARSET_INFO *to_cs,
                                 const char *from, uint from_length,
                                 uint nchars)
 {
-  if ((to_cs == &my_charset_bin) || 
-      (from_cs == &my_charset_bin) ||
-      (to_cs == from_cs) ||
-      my_charset_same(from_cs, to_cs))
+  if (use_native_copy(to_cs, from_cs))
   {
     m_cannot_convert_error_pos= NULL;
     return to_cs->cset->copy_fix(to_cs, to, to_length, from, from_length,
-                                 nchars, &m_native_copy_status);
+                                 nchars, &m_native_copy_status, '?');
   }
   return my_convert_fix(to_cs, to, to_length, from_cs, from, from_length,
                         nchars, this);
diff --git a/sql/sql_string.h b/sql/sql_string.h
index 46188ce..b09c2cc 100644
--- a/sql/sql_string.h
+++ b/sql/sql_string.h
@@ -43,8 +43,24 @@ inline uint32 copy_and_convert(char *to, uint32 to_length,
 }
 
 
-class String_copier: private MY_STRCONV_STATUS
+class String_copier: protected MY_STRCONV_STATUS
 {
+protected:
+  /*
+    Check if it's possible to copy between two character sets
+    using native copy methods.
+    @param to_cs   - character set to copy to
+    @param from_cs - character set to copy from
+    @return          true if copying can use native copy routines
+    @return          false if copying should use Unicode conversion routines
+  */
+  static bool use_native_copy(CHARSET_INFO *to_cs, CHARSET_INFO *from_cs)
+  {
+    return ((to_cs == &my_charset_bin) ||
+            (from_cs == &my_charset_bin) ||
+            (to_cs == from_cs) ||
+            my_charset_same(from_cs, to_cs));
+  }
 public:
   const char *source_end_pos() const
   { return m_native_copy_status.m_source_end_pos; }
diff --git a/strings/ctype-big5.c b/strings/ctype-big5.c
index d6a9695..19288b5 100644
--- a/strings/ctype-big5.c
+++ b/strings/ctype-big5.c
@@ -6795,7 +6795,9 @@ static MY_COLLATION_HANDLER my_collation_handler_big5_chinese_ci=
   my_strcasecmp_mb,
   my_instr_mb,
   my_hash_sort_simple,
-  my_propagate_simple
+  my_propagate_simple,
+  my_make_min_range_mb,
+  my_make_max_range_mb,
 };
 
 
@@ -6811,7 +6813,9 @@ static MY_COLLATION_HANDLER my_collation_handler_big5_bin=
   my_strcasecmp_mb_bin,
   my_instr_mb,
   my_hash_sort_mb_bin,
-  my_propagate_simple
+  my_propagate_simple,
+  my_make_min_range_mb,
+  my_make_max_range_mb,
 };
 
 
diff --git a/strings/ctype-bin.c b/strings/ctype-bin.c
index 4d42973..2e7880a 100644
--- a/strings/ctype-bin.c
+++ b/strings/ctype-bin.c
@@ -498,7 +498,9 @@ MY_COLLATION_HANDLER my_collation_8bit_bin_handler =
   my_strcasecmp_bin,
   my_instr_bin,
   my_hash_sort_8bit_bin,
-  my_propagate_simple
+  my_propagate_simple,
+  my_make_min_range_8bit,
+  my_make_max_range_8bit,
 };
 
 
@@ -514,7 +516,9 @@ static MY_COLLATION_HANDLER my_collation_binary_handler =
   my_strcasecmp_bin,
   my_instr_bin,
   my_hash_sort_bin,
-  my_propagate_simple
+  my_propagate_simple,
+  my_make_min_range_8bit,
+  my_make_max_range_8bit,
 };
 
 
diff --git a/strings/ctype-cp932.c b/strings/ctype-cp932.c
index 9bf206f..a3de1ea 100644
--- a/strings/ctype-cp932.c
+++ b/strings/ctype-cp932.c
@@ -34670,7 +34670,9 @@ static MY_COLLATION_HANDLER my_collation_handler_cp932_japanese_ci=
   my_strcasecmp_8bit,
   my_instr_mb,
   my_hash_sort_simple,
-  my_propagate_simple
+  my_propagate_simple,
+  my_make_min_range_mb,
+  my_make_max_range_mb,
 };
 
 
@@ -34686,7 +34688,9 @@ static MY_COLLATION_HANDLER my_collation_handler_cp932_bin=
   my_strcasecmp_mb_bin,
   my_instr_mb,
   my_hash_sort_mb_bin,
-  my_propagate_simple
+  my_propagate_simple,
+  my_make_min_range_mb,
+  my_make_max_range_mb,
 };
 
 
diff --git a/strings/ctype-czech.c b/strings/ctype-czech.c
index 6603bc7..875ac82 100644
--- a/strings/ctype-czech.c
+++ b/strings/ctype-czech.c
@@ -619,7 +619,9 @@ static MY_COLLATION_HANDLER my_collation_latin2_czech_ci_handler =
   my_strcasecmp_8bit,
   my_instr_simple,
   my_hash_sort_simple,
-  my_propagate_simple
+  my_propagate_simple,
+  my_make_min_range_8bit,
+  my_make_max_range_8bit,
 };
 
 struct charset_info_st my_charset_latin2_czech_ci =
diff --git a/strings/ctype-euc_kr.c b/strings/ctype-euc_kr.c
index 1f13ab6..55eb437 100644
--- a/strings/ctype-euc_kr.c
+++ b/strings/ctype-euc_kr.c
@@ -9964,7 +9964,9 @@ static MY_COLLATION_HANDLER my_collation_handler_euckr_korean_ci=
   my_strcasecmp_mb,
   my_instr_mb,
   my_hash_sort_simple,
-  my_propagate_simple
+  my_propagate_simple,
+  my_make_min_range_mb,
+  my_make_max_range_mb,
 };
 
 
@@ -9980,7 +9982,9 @@ static MY_COLLATION_HANDLER my_collation_handler_euckr_bin=
   my_strcasecmp_mb_bin,
   my_instr_mb,
   my_hash_sort_mb_bin,
-  my_propagate_simple
+  my_propagate_simple,
+  my_make_min_range_mb,
+  my_make_max_range_mb,
 };
 
 
diff --git a/strings/ctype-eucjpms.c b/strings/ctype-eucjpms.c
index 82c4bb5..69893db 100644
--- a/strings/ctype-eucjpms.c
+++ b/strings/ctype-eucjpms.c
@@ -67497,7 +67497,9 @@ static MY_COLLATION_HANDLER my_collation_eucjpms_japanese_ci_handler =
     my_strcasecmp_mb,
     my_instr_mb,
     my_hash_sort_simple,
-    my_propagate_simple
+    my_propagate_simple,
+    my_make_min_range_mb,
+    my_make_max_range_mb,
 };
 
 
@@ -67513,7 +67515,9 @@ static MY_COLLATION_HANDLER my_collation_eucjpms_bin_handler =
     my_strcasecmp_mb_bin,
     my_instr_mb,
     my_hash_sort_mb_bin,
-    my_propagate_simple
+    my_propagate_simple,
+    my_make_min_range_mb,
+    my_make_max_range_mb,
 };
 
 
diff --git a/strings/ctype-gb2312.c b/strings/ctype-gb2312.c
index b0e275f..c4beb9f 100644
--- a/strings/ctype-gb2312.c
+++ b/strings/ctype-gb2312.c
@@ -6367,7 +6367,9 @@ static MY_COLLATION_HANDLER my_collation_handler_gb2312_chinese_ci=
   my_strcasecmp_mb,     /* instr      */
   my_instr_mb,
   my_hash_sort_simple,
-  my_propagate_simple
+  my_propagate_simple,
+  my_make_min_range_mb,
+  my_make_max_range_mb,
 };
 
 
@@ -6383,7 +6385,9 @@ static MY_COLLATION_HANDLER my_collation_handler_gb2312_bin=
   my_strcasecmp_mb_bin,
   my_instr_mb,
   my_hash_sort_mb_bin,
-  my_propagate_simple
+  my_propagate_simple,
+  my_make_min_range_mb,
+  my_make_max_range_mb,
 };
 
 
diff --git a/strings/ctype-gbk.c b/strings/ctype-gbk.c
index 37b003f..d4c6c45 100644
--- a/strings/ctype-gbk.c
+++ b/strings/ctype-gbk.c
@@ -10679,7 +10679,9 @@ static MY_COLLATION_HANDLER my_collation_handler_gbk_chinese_ci=
   my_strcasecmp_mb,
   my_instr_mb,
   my_hash_sort_simple,
-  my_propagate_simple
+  my_propagate_simple,
+  my_make_min_range_mb,
+  my_make_max_range_mb,
 };
 
 
@@ -10695,7 +10697,9 @@ static MY_COLLATION_HANDLER my_collation_handler_gbk_bin=
   my_strcasecmp_mb_bin,
   my_instr_mb,
   my_hash_sort_mb_bin,
-  my_propagate_simple
+  my_propagate_simple,
+  my_make_min_range_mb,
+  my_make_max_range_mb,
 };
 
 
diff --git a/strings/ctype-latin1.c b/strings/ctype-latin1.c
index 8bc3ac3..1fd20a5 100644
--- a/strings/ctype-latin1.c
+++ b/strings/ctype-latin1.c
@@ -729,7 +729,9 @@ static MY_COLLATION_HANDLER my_collation_german2_ci_handler=
   my_strcasecmp_8bit,
   my_instr_simple,
   my_hash_sort_latin1_de,
-  my_propagate_complex
+  my_propagate_complex,
+  my_make_min_range_8bit,
+  my_make_max_range_8bit,
 };
 
 
diff --git a/strings/ctype-mb.c b/strings/ctype-mb.c
index eef283d..7c7fdb5 100644
--- a/strings/ctype-mb.c
+++ b/strings/ctype-mb.c
@@ -441,7 +441,8 @@ my_append_fix_badly_formed_tail(CHARSET_INFO *cs,
                                 char *to, char *to_end,
                                 const char *from, const char *from_end,
                                 size_t nchars,
-                                MY_STRCOPY_STATUS *status)
+                                MY_STRCOPY_STATUS *status,
+                                my_wc_t replacement_character)
 {
   char *to0= to;
 
@@ -475,7 +476,8 @@ my_append_fix_badly_formed_tail(CHARSET_INFO *cs,
     if (!status->m_well_formed_error_pos)
       status->m_well_formed_error_pos= from;
 
-    if ((chlen= cs->cset->wc_mb(cs, '?', (uchar*) to, (uchar *) to_end)) <= 0)
+    if ((chlen= cs->cset->wc_mb(cs, replacement_character,
+                                (uchar*) to, (uchar *) to_end)) <= 0)
       break; /* Question mark does not fit into the destination */
     to+= chlen;
     from++;
@@ -490,7 +492,8 @@ size_t
 my_copy_fix_mb(CHARSET_INFO *cs,
                char *dst, size_t dst_length,
                const char *src, size_t src_length,
-               size_t nchars, MY_STRCOPY_STATUS *status)
+               size_t nchars, MY_STRCOPY_STATUS *status,
+               my_wc_t replacement_character)
 {
   size_t well_formed_nchars;
   size_t well_formed_length;
@@ -511,11 +514,34 @@ my_copy_fix_mb(CHARSET_INFO *cs,
                                                 src + well_formed_length,
                                                 src + src_length,
                                                 nchars - well_formed_nchars,
-                                                status);
+                                                status,
+                                                replacement_character);
   return well_formed_length + fixed_length;
 }
 
 
+size_t
+my_make_min_range_mb(CHARSET_INFO *cs,
+                     char *dst, size_t dst_length,
+                     const char *src, size_t src_length,
+                     size_t nchars, MY_STRCOPY_STATUS *status)
+{
+  return cs->cset->copy_fix(cs, dst, dst_length, src, src_length, nchars,
+                            status, cs->min_sort_char);
+}
+
+
+size_t
+my_make_max_range_mb(CHARSET_INFO *cs,
+                     char *dst, size_t dst_length,
+                     const char *src, size_t src_length,
+                     size_t nchars, MY_STRCOPY_STATUS *status)
+{
+  return cs->cset->copy_fix(cs, dst, dst_length, src, src_length, nchars,
+                            status, cs->max_sort_char);
+}
+
+
 uint my_instr_mb(CHARSET_INFO *cs,
                  const char *b, size_t b_length, 
                  const char *s, size_t s_length,
diff --git a/strings/ctype-simple.c b/strings/ctype-simple.c
index 020bfd0..793a401 100644
--- a/strings/ctype-simple.c
+++ b/strings/ctype-simple.c
@@ -1135,7 +1135,8 @@ size_t
 my_copy_8bit(CHARSET_INFO *cs __attribute__((unused)),
              char *dst, size_t dst_length,
              const char *src, size_t src_length,
-             size_t nchars, MY_STRCOPY_STATUS *status)
+             size_t nchars, MY_STRCOPY_STATUS *status,
+             my_wc_t replacement_character __attribute__((unused)))
 {
   set_if_smaller(src_length, dst_length);
   set_if_smaller(src_length, nchars);
@@ -1146,6 +1147,25 @@ my_copy_8bit(CHARSET_INFO *cs __attribute__((unused)),
   return src_length;
 }
 
+size_t
+my_make_min_range_8bit(CHARSET_INFO *cs,
+                       char *dst, size_t dst_length,
+                       const char *src, size_t src_length,
+                       size_t nchars, MY_STRCOPY_STATUS *status)
+{
+  return my_copy_8bit(cs, dst, dst_length, src, src_length, nchars, status, 0);
+}
+
+
+size_t
+my_make_max_range_8bit(CHARSET_INFO *cs,
+                       char *dst, size_t dst_length,
+                       const char *src, size_t src_length,
+                       size_t nchars, MY_STRCOPY_STATUS *status)
+{
+  return my_copy_8bit(cs, dst, dst_length, src, src_length, nchars, status, 0);
+}
+
 
 size_t my_lengthsp_8bit(CHARSET_INFO *cs __attribute__((unused)),
                         const char *ptr, size_t length)
@@ -1965,5 +1985,7 @@ MY_COLLATION_HANDLER my_collation_8bit_simple_ci_handler =
     my_strcasecmp_8bit,
     my_instr_simple,
     my_hash_sort_simple,
-    my_propagate_simple
+    my_propagate_simple,
+    my_make_min_range_8bit,
+    my_make_max_range_8bit,
 };
diff --git a/strings/ctype-sjis.c b/strings/ctype-sjis.c
index 629e1cd..45da0ee 100644
--- a/strings/ctype-sjis.c
+++ b/strings/ctype-sjis.c
@@ -34049,7 +34049,9 @@ static MY_COLLATION_HANDLER my_collation_handler_sjis_japanese_ci=
   my_strcasecmp_8bit,
   my_instr_mb,
   my_hash_sort_simple,
-  my_propagate_simple
+  my_propagate_simple,
+  my_make_min_range_mb,
+  my_make_max_range_mb,
 };
 
 
@@ -34065,7 +34067,9 @@ static MY_COLLATION_HANDLER my_collation_handler_sjis_bin=
   my_strcasecmp_mb_bin,
   my_instr_mb,
   my_hash_sort_mb_bin,
-  my_propagate_simple
+  my_propagate_simple,
+  my_make_min_range_mb,
+  my_make_max_range_mb,
 };
 
 
diff --git a/strings/ctype-tis620.c b/strings/ctype-tis620.c
index c62e588..e28c99a 100644
--- a/strings/ctype-tis620.c
+++ b/strings/ctype-tis620.c
@@ -854,7 +854,9 @@ static MY_COLLATION_HANDLER my_collation_ci_handler =
     my_strcasecmp_8bit,
     my_instr_simple,				/* QQ: To be fixed */
     my_hash_sort_simple,
-    my_propagate_simple
+    my_propagate_simple,
+    my_make_min_range_8bit,
+    my_make_max_range_8bit,
 };
 
 static MY_CHARSET_HANDLER my_charset_handler=
diff --git a/strings/ctype-uca.c b/strings/ctype-uca.c
index 60bd96e..a15f1ca 100644
--- a/strings/ctype-uca.c
+++ b/strings/ctype-uca.c
@@ -22923,7 +22923,9 @@ MY_COLLATION_HANDLER my_collation_ucs2_uca_handler =
   NULL,
   my_instr_mb,
   my_hash_sort_ucs2_uca,
-  my_propagate_complex
+  my_propagate_complex,
+  my_make_min_range_mb,
+  my_make_max_range_mb,
 };
 
 struct charset_info_st my_charset_ucs2_unicode_ci=
@@ -23786,7 +23788,9 @@ MY_COLLATION_HANDLER my_collation_any_uca_handler =
     NULL,
     my_instr_mb,
     my_hash_sort_any_uca,
-    my_propagate_complex
+    my_propagate_complex,
+    my_make_min_range_mb,
+    my_make_max_range_mb,
 };
 
 /* 
@@ -25524,7 +25528,9 @@ MY_COLLATION_HANDLER my_collation_utf32_uca_handler =
     NULL,
     my_instr_mb,
     my_hash_sort_any_uca,
-    my_propagate_complex
+    my_propagate_complex,
+    my_make_min_range_mb,
+    my_make_max_range_mb,
 };
 
 extern MY_CHARSET_HANDLER my_charset_utf32_handler;
@@ -26387,7 +26393,9 @@ MY_COLLATION_HANDLER my_collation_utf16_uca_handler =
     NULL,
     my_instr_mb,
     my_hash_sort_any_uca,
-    my_propagate_complex
+    my_propagate_complex,
+    my_make_min_range_mb,
+    my_make_max_range_mb,
 };
 
 extern MY_CHARSET_HANDLER my_charset_utf16_handler;
diff --git a/strings/ctype-ucs2.c b/strings/ctype-ucs2.c
index e7ba5cb..4d7c08f 100644
--- a/strings/ctype-ucs2.c
+++ b/strings/ctype-ucs2.c
@@ -172,14 +172,16 @@ static size_t
 my_copy_fix_mb2_or_mb4(CHARSET_INFO *cs,
                        char *dst, size_t dst_length,
                        const char *src, size_t src_length,
-                       size_t nchars, MY_STRCOPY_STATUS *status)
+                       size_t nchars, MY_STRCOPY_STATUS *status,
+                       my_wc_t replacement_character)
 {
   size_t length2, src_offset= src_length % cs->mbminlen;
   my_char_copy_status_t padstatus;
   
   if (!src_offset)
     return  my_copy_fix_mb(cs, dst, dst_length,
-                               src, src_length, nchars, status);
+                               src, src_length, nchars, status,
+                               replacement_character);
   if ((padstatus= my_copy_incomplete_char(cs, dst, dst_length,
                                           src, src_length, nchars, TRUE)) ==
       MY_CHAR_COPY_ERROR)
@@ -189,7 +191,7 @@ my_copy_fix_mb2_or_mb4(CHARSET_INFO *cs,
   }
   length2= my_copy_fix_mb(cs, dst + cs->mbminlen, dst_length - cs->mbminlen,
                           src + src_offset, src_length - src_offset,
-                          nchars - 1, status);
+                          nchars - 1, status, replacement_character);
   if (padstatus == MY_CHAR_COPY_FIXED)
     status->m_well_formed_error_pos= src;
   return cs->mbminlen /* The left-padded character */ + length2;
@@ -1538,7 +1540,9 @@ static MY_COLLATION_HANDLER my_collation_utf16_general_ci_handler =
   my_strcasecmp_mb2_or_mb4,
   my_instr_mb,
   my_hash_sort_utf16,
-  my_propagate_simple
+  my_propagate_simple,
+  my_make_min_range_mb,
+  my_make_max_range_mb,
 };
 
 
@@ -1554,7 +1558,9 @@ static MY_COLLATION_HANDLER my_collation_utf16_bin_handler =
   my_strcasecmp_mb2_or_mb4,
   my_instr_mb,
   my_hash_sort_utf16_bin,
-  my_propagate_simple
+  my_propagate_simple,
+  my_make_min_range_mb,
+  my_make_max_range_mb,
 };
 
 
@@ -1761,7 +1767,9 @@ static MY_COLLATION_HANDLER my_collation_utf16le_general_ci_handler =
   my_strcasecmp_mb2_or_mb4,
   my_instr_mb,
   my_hash_sort_utf16,
-  my_propagate_simple
+  my_propagate_simple,
+  my_make_min_range_mb,
+  my_make_max_range_mb,
 };
 
 
@@ -1777,7 +1785,9 @@ static MY_COLLATION_HANDLER my_collation_utf16le_bin_handler =
   my_strcasecmp_mb2_or_mb4,
   my_instr_mb,
   my_hash_sort_utf16_bin,
-  my_propagate_simple
+  my_propagate_simple,
+  my_make_min_range_mb,
+  my_make_max_range_mb,
 };
 
 
@@ -2506,7 +2516,9 @@ static MY_COLLATION_HANDLER my_collation_utf32_general_ci_handler =
   my_strcasecmp_mb2_or_mb4,
   my_instr_mb,
   my_hash_sort_utf32,
-  my_propagate_simple
+  my_propagate_simple,
+  my_make_min_range_mb,
+  my_make_max_range_mb,
 };
 
 
@@ -2522,7 +2534,9 @@ static MY_COLLATION_HANDLER my_collation_utf32_bin_handler =
   my_strcasecmp_mb2_or_mb4,
   my_instr_mb,
   my_hash_sort_utf32,
-  my_propagate_simple
+  my_propagate_simple,
+  my_make_min_range_mb,
+  my_make_max_range_mb,
 };
 
 
@@ -2993,7 +3007,9 @@ static MY_COLLATION_HANDLER my_collation_ucs2_general_ci_handler =
     my_strcasecmp_mb2_or_mb4,
     my_instr_mb,
     my_hash_sort_ucs2,
-    my_propagate_simple
+    my_propagate_simple,
+    my_make_min_range_mb,
+    my_make_max_range_mb,
 };
 
 
@@ -3009,7 +3025,9 @@ static MY_COLLATION_HANDLER my_collation_ucs2_bin_handler =
     my_strcasecmp_mb2_or_mb4,
     my_instr_mb,
     my_hash_sort_ucs2_bin,
-    my_propagate_simple
+    my_propagate_simple,
+    my_make_min_range_mb,
+    my_make_max_range_mb,
 };
 
 
diff --git a/strings/ctype-ujis.c b/strings/ctype-ujis.c
index 308f5f0..850b2ff 100644
--- a/strings/ctype-ujis.c
+++ b/strings/ctype-ujis.c
@@ -67241,7 +67241,9 @@ static MY_COLLATION_HANDLER my_collation_ujis_japanese_ci_handler =
     my_strcasecmp_mb,
     my_instr_mb,
     my_hash_sort_simple,
-    my_propagate_simple
+    my_propagate_simple,
+    my_make_min_range_mb,
+    my_make_max_range_mb,
 };
 
 
@@ -67257,7 +67259,9 @@ static MY_COLLATION_HANDLER my_collation_ujis_bin_handler =
     my_strcasecmp_mb_bin,
     my_instr_mb,
     my_hash_sort_mb_bin,
-    my_propagate_simple
+    my_propagate_simple,
+    my_make_min_range_mb,
+    my_make_max_range_mb,
 };
 
 
diff --git a/strings/ctype-utf8.c b/strings/ctype-utf8.c
index 3c2c812..137a813 100644
--- a/strings/ctype-utf8.c
+++ b/strings/ctype-utf8.c
@@ -5459,7 +5459,9 @@ static MY_COLLATION_HANDLER my_collation_utf8_general_ci_handler =
     my_strcasecmp_utf8,
     my_instr_mb,
     my_hash_sort_utf8,
-    my_propagate_complex
+    my_propagate_complex,
+    my_make_min_range_mb,
+    my_make_max_range_mb,
 };
 
 
@@ -5475,7 +5477,9 @@ static MY_COLLATION_HANDLER my_collation_utf8_general_mysql500_ci_handler =
     my_strcasecmp_utf8,
     my_instr_mb,
     my_hash_sort_utf8,
-    my_propagate_complex
+    my_propagate_complex,
+    my_make_min_range_mb,
+    my_make_max_range_mb,
 };
 
 
@@ -5491,7 +5495,9 @@ static MY_COLLATION_HANDLER my_collation_utf8_bin_handler =
     my_strcasecmp_mb_bin,
     my_instr_mb,
     my_hash_sort_mb_bin,
-    my_propagate_simple
+    my_propagate_simple,
+    my_make_min_range_mb,
+    my_make_max_range_mb,
 };
 
 MY_CHARSET_HANDLER my_charset_utf8_handler=
@@ -5775,7 +5781,9 @@ static MY_COLLATION_HANDLER my_collation_cs_handler =
     my_strcasecmp_utf8,
     my_instr_mb,
     my_hash_sort_utf8,
-    my_propagate_simple
+    my_propagate_simple,
+    my_make_min_range_mb,
+    my_make_max_range_mb,
 };
 
 struct charset_info_st my_charset_utf8_general_cs=
@@ -7075,7 +7083,9 @@ static MY_COLLATION_HANDLER my_collation_filename_handler =
     my_strcasecmp_utf8,
     my_instr_mb,
     my_hash_sort_utf8,
-    my_propagate_complex
+    my_propagate_complex,
+    my_make_min_range_mb,
+    my_make_max_range_mb,
 };
 
 static MY_CHARSET_HANDLER my_charset_filename_handler=
@@ -7829,7 +7839,9 @@ static MY_COLLATION_HANDLER my_collation_utf8mb4_general_ci_handler=
   my_strcasecmp_utf8mb4,
   my_instr_mb,
   my_hash_sort_utf8mb4,
-  my_propagate_complex
+  my_propagate_complex,
+  my_make_min_range_mb,
+  my_make_max_range_mb,
 };
 
 
@@ -7845,7 +7857,9 @@ static MY_COLLATION_HANDLER my_collation_utf8mb4_bin_handler =
     my_strcasecmp_mb_bin,
     my_instr_mb,
     my_hash_sort_mb_bin,
-    my_propagate_simple
+    my_propagate_simple,
+    my_make_min_range_mb,
+    my_make_max_range_mb,
 };
 
 
diff --git a/strings/ctype-win1250ch.c b/strings/ctype-win1250ch.c
index 27e6a94..55022b2 100644
--- a/strings/ctype-win1250ch.c
+++ b/strings/ctype-win1250ch.c
@@ -683,7 +683,9 @@ static MY_COLLATION_HANDLER my_collation_czech_ci_handler =
   my_strcasecmp_8bit,
   my_instr_simple,
   my_hash_sort_simple,
-  my_propagate_simple
+  my_propagate_simple,
+  my_make_min_range_8bit,
+  my_make_max_range_8bit,
 };