diff options
Diffstat (limited to 'sql')
-rw-r--r-- | sql/CMakeLists.txt | 2 | ||||
-rw-r--r-- | sql/charset_collations.cc | 107 | ||||
-rw-r--r-- | sql/charset_collations.h | 265 | ||||
-rw-r--r-- | sql/field.h | 9 | ||||
-rw-r--r-- | sql/handler.h | 26 | ||||
-rw-r--r-- | sql/item_func.h | 4 | ||||
-rw-r--r-- | sql/item_strfunc.cc | 5 | ||||
-rw-r--r-- | sql/json_table.cc | 6 | ||||
-rw-r--r-- | sql/lex_charset.cc | 79 | ||||
-rw-r--r-- | sql/lex_charset.h | 88 | ||||
-rw-r--r-- | sql/log_event.cc | 15 | ||||
-rw-r--r-- | sql/log_event.h | 13 | ||||
-rw-r--r-- | sql/log_event_client.cc | 32 | ||||
-rw-r--r-- | sql/log_event_server.cc | 18 | ||||
-rw-r--r-- | sql/mysqld.cc | 22 | ||||
-rw-r--r-- | sql/simple_tokenizer.h | 83 | ||||
-rw-r--r-- | sql/sql_class.h | 29 | ||||
-rw-r--r-- | sql/sql_connect.cc | 3 | ||||
-rw-r--r-- | sql/sql_lex.h | 14 | ||||
-rw-r--r-- | sql/sql_parse.cc | 21 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 43 | ||||
-rw-r--r-- | sql/sql_show.cc | 15 | ||||
-rw-r--r-- | sql/sql_table.cc | 14 | ||||
-rw-r--r-- | sql/sql_type.cc | 22 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 86 | ||||
-rw-r--r-- | sql/structs.h | 7 | ||||
-rw-r--r-- | sql/sys_vars.cc | 109 |
27 files changed, 1041 insertions, 96 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 89b0bb21414..6f330e5bfe0 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -163,7 +163,7 @@ SET (SQL_SOURCE semisync.cc semisync_master.cc semisync_slave.cc semisync_master_ack_receiver.cc sql_schema.cc - lex_charset.cc + lex_charset.cc charset_collations.cc sql_type.cc sql_mode.cc sql_type_json.cc sql_type_string.cc sql_type_geom.cc diff --git a/sql/charset_collations.cc b/sql/charset_collations.cc new file mode 100644 index 00000000000..c2c2cc2e7d6 --- /dev/null +++ b/sql/charset_collations.cc @@ -0,0 +1,107 @@ +/* Copyright (c) 2023, MariaDB Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + + +#include "my_global.h" +#include "my_sys.h" +#include "lex_charset.h" +#include "mysqld_error.h" +#include "charset_collations.h" +#include "simple_tokenizer.h" + +bool Charset_collation_map_st::insert_or_replace( + const Lex_exact_charset &charset, + const Lex_extended_collation &collation, + bool error_on_conflicting_duplicate) +{ + Lex_exact_charset_opt_extended_collate res(charset); + Used used; + if (res.merge_collation_override(&used, *this, collation)) + return true; + + if (error_on_conflicting_duplicate) + { + const Elem_st *dup; + if ((dup= find_elem_by_charset_id(charset.charset_info()->number)) && + dup->collation() != res.collation().charset_info()) + { + my_error(ER_CONFLICTING_DECLARATIONS, MYF(0), + "", dup->collation()->coll_name.str, + "", res.collation().charset_info()->coll_name.str); + return true; + } + } + return insert_or_replace(Elem(charset.charset_info(), + res.collation().charset_info())); +} + + +bool Charset_collation_map_st::from_text(const LEX_CSTRING &str, myf utf8_flag) +{ + init(); + Simple_tokenizer stream(str.str, str.length); + + stream.get_spaces(); + if (stream.eof()) + return 0; /* Empty string */ + + for ( ; ; ) + { + LEX_CSTRING charset_name= stream.get_ident(); + if (!charset_name.length) + return true; + stream.get_spaces(); + if (stream.get_char('=')) + return true; + stream.get_spaces(); + LEX_CSTRING collation_name= stream.get_ident(); + if (!collation_name.length) + return true; + + char charset_name_c[MY_CS_CHARACTER_SET_NAME_SIZE + 1/*for '\0'*/]; + strmake(charset_name_c, charset_name.str, charset_name.length); + CHARSET_INFO *cs= get_charset_by_csname(charset_name_c, + MY_CS_PRIMARY, utf8_flag); + if (!cs) + { + my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), charset_name_c); + return true; + } + + char collation_name_c[MY_CS_COLLATION_NAME_SIZE + 1/*for '\0'*/]; + strmake(collation_name_c, collation_name.str, collation_name.length); + + Lex_exact_collation tmpec(&my_charset_bin); + Lex_extended_collation tmp(tmpec); + if (tmp.set_by_name(collation_name_c, utf8_flag)) + return true; + + /* + Don't allow duplicate conflicting declarations within the same string: + SET @@var='utf8mb3=utf8mb3_general_ci,utf8mb3=utf8mb3_bin'; + */ + if (insert_or_replace(Lex_exact_charset(cs), tmp, true/*err on dup*/)) + return true; + + stream.get_spaces(); + if (stream.eof()) + break; + if (stream.ptr()[0] != ',') + return true; + stream.get_char(','); + stream.get_spaces(); + } + return false; +} diff --git a/sql/charset_collations.h b/sql/charset_collations.h new file mode 100644 index 00000000000..6d1a96c4151 --- /dev/null +++ b/sql/charset_collations.h @@ -0,0 +1,265 @@ +/* Copyright (c) 2023, MariaDB Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef LEX_CHARSET_COLLATIONS_INCLUDED +#define LEX_CHARSET_COLLATIONS_INCLUDED + +struct Charset_collation_map_st +{ +public: + + class Used + { + public: + enum map_used_t + { + USED_NONE= 0, + USED_COMPILED_COLLATION= 1 << 0, + USED_MAPPED_COLLATION= 1 << 1 + }; + protected: + map_used_t m_used; + public: + Used() + :m_used(USED_NONE) + { } + void add(map_used_t flag) + { + m_used= (map_used_t) ((uint) m_used | (uint) flag); + } + }; + + struct Elem_st + { + protected: + CHARSET_INFO *m_charset; + CHARSET_INFO *m_collation; + static size_t print_lex_string(char *dst, const LEX_CSTRING &str) + { + memcpy(dst, str.str, str.length); + return str.length; + } + public: + /* + Size in text format: 'utf8mb4=utf8mb4_unicode_ai_ci' + */ + static constexpr size_t text_size_max() + { + return MY_CS_CHARACTER_SET_NAME_SIZE + 1 + + MY_CS_COLLATION_NAME_SIZE; + } + CHARSET_INFO *charset() const + { + return m_charset; + } + CHARSET_INFO *collation() const + { + return m_collation; + } + void set_collation(CHARSET_INFO *cl) + { + m_collation= cl; + } + size_t print(char *dst) const + { + const char *dst0= dst; + dst+= print_lex_string(dst, m_charset->cs_name); + *dst++= '='; + dst+= print_lex_string(dst, m_collation->coll_name); + return (size_t) (dst - dst0); + } + int cmp_by_charset_id(const Elem_st &rhs) const + { + return m_charset->number < rhs.m_charset->number ? -1 : + m_charset->number > rhs.m_charset->number ? +1 : 0; + } + }; + class Elem: public Elem_st + { + public: + Elem(CHARSET_INFO *charset, CHARSET_INFO *collation) + { + m_charset= charset; + m_collation= collation; + } + }; +protected: + Elem_st m_element[8]; // Should be enough for now + uint m_count; + uint m_version; + + static int cmp_by_charset_id(const void *a, const void *b) + { + return static_cast<const Elem_st*>(a)-> + cmp_by_charset_id(*static_cast<const Elem_st*>(b)); + } + + void sort() + { + qsort(m_element, m_count, sizeof(Elem_st), cmp_by_charset_id); + } + + const Elem_st *find_elem_by_charset_id(uint id) const + { + if (!m_count) + return NULL; + int first= 0, last= ((int) m_count) - 1; + for ( ; first <= last; ) + { + const int middle= (first + last) / 2; + DBUG_ASSERT(middle >= 0); + DBUG_ASSERT(middle < (int) m_count); + const uint middle_id= m_element[middle].charset()->number; + if (middle_id == id) + return &m_element[middle]; + if (middle_id < id) + first= middle + 1; + else + last= middle - 1; + } + return NULL; + } + + bool insert(const Elem_st &elem) + { + DBUG_ASSERT(elem.charset()->state & MY_CS_PRIMARY); + if (m_count >= array_elements(m_element)) + return true; + m_element[m_count]= elem; + m_count++; + sort(); + return false; + } + + bool insert_or_replace(const Elem_st &elem) + { + DBUG_ASSERT(elem.charset()->state & MY_CS_PRIMARY); + const Elem_st *found= find_elem_by_charset_id(elem.charset()->number); + if (found) + { + const_cast<Elem_st*>(found)->set_collation(elem.collation()); + return false; + } + return insert(elem); + } + +public: + void init() + { + m_count= 0; + m_version= 0; + } + uint count() const + { + return m_count; + } + uint version() const + { + return m_version; + } + void set(const Charset_collation_map_st &rhs, uint version_increment) + { + uint version= m_version; + *this= rhs; + m_version= version + version_increment; + } + const Elem_st & operator[](uint pos) const + { + DBUG_ASSERT(pos < m_count); + return m_element[pos]; + } + bool insert_or_replace(const class Lex_exact_charset &cs, + const class Lex_extended_collation &cl, + bool error_on_conflicting_duplicate); + CHARSET_INFO *get_collation_for_charset(Used *used, + CHARSET_INFO *cs) const + { + DBUG_ASSERT(cs->state & MY_CS_PRIMARY); + const Elem_st *elem= find_elem_by_charset_id(cs->number); + if (elem) + { + used->add(Used::USED_MAPPED_COLLATION); + return elem->collation(); + } + used->add(Used::USED_COMPILED_COLLATION); + return cs; + } + size_t text_format_nbytes_needed() const + { + return (Elem_st::text_size_max() + 1/* for ',' */) * m_count; + } + size_t print(char *dst, size_t nbytes_available) const + { + const char *dst0= dst; + const char *end= dst + nbytes_available; + for (uint i= 0; i < m_count; i++) + { + if (Elem_st::text_size_max() + 1/* for ',' */ > (size_t) (end - dst)) + break; + if (i > 0) + *dst++= ','; + dst+= m_element[i].print(dst); + } + return dst - dst0; + } + static constexpr size_t binary_size_max() + { + return 1/*count*/ + 4 * array_elements(m_element); + } + size_t to_binary(char *dst) const + { + const char *dst0= dst; + *dst++= (char) (uchar) m_count; + for (uint i= 0; i < m_count; i++) + { + int2store(dst, (uint16) m_element[i].charset()->number); + dst+= 2; + int2store(dst, (uint16) m_element[i].collation()->number); + dst+= 2; + } + return (size_t) (dst - dst0); + } + size_t from_binary(const char *src, size_t srclen) + { + const char *src0= src; + init(); + if (!srclen) + return 0; // Empty + uint count= (uchar) *src++; + if (srclen < 1 + 4 * count) + return 0; + for (uint i= 0; i < count; i++, src+= 4) + { + CHARSET_INFO *cs, *cl; + if (!(cs= get_charset(uint2korr(src), MYF(0))) || + !(cl= get_charset(uint2korr(src + 2), MYF(0)))) + { + /* + Unpacking from binary format happens on the slave side. + If for some reasons the slave does not know about a + character set or a collation, just skip the pair here. + This pair might not even be needed. + */ + continue; + } + insert_or_replace(Elem(cs, cl)); + } + return src - src0; + } + bool from_text(const LEX_CSTRING &str, myf utf8_flag); +}; + + +#endif // LEX_CHARSET_COLLATIONS_INCLUDED diff --git a/sql/field.h b/sql/field.h index 13d80099124..48dc8676699 100644 --- a/sql/field.h +++ b/sql/field.h @@ -5340,7 +5340,9 @@ public: - find a _bin collation if the BINARY comparison style was specified, e.g.: CREATE TABLE t1 (a VARCHAR(10) BINARY) CHARSET utf8; */ - bool prepare_charset_for_string(const Column_derived_attributes *dattr); + bool prepare_charset_for_string(Charset_collation_map_st::Used *used, + const Charset_collation_map_st &map, + const Column_derived_attributes *dattr); /** Prepare a SET/ENUM field. @@ -5497,10 +5499,11 @@ public: bool check_vcol_for_key(THD *thd) const; - void set_charset_collation_attrs(const + void set_charset_collation_attrs(Charset_collation_map_st::Used *used, + const Charset_collation_map_st &map, const Lex_column_charset_collation_attrs_st &lc) { - charset= lc.charset_info(); + charset= lc.charset_info(used, map); if (lc.is_contextually_typed_collation()) flags|= CONTEXT_COLLATION_FLAG; else diff --git a/sql/handler.h b/sql/handler.h index 77c77c83c0f..6d35d6fd953 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -2347,32 +2347,42 @@ struct Table_specification_st: public HA_CREATE_INFO, convert_charset_collation.init(); } - bool add_table_option_convert_charset(CHARSET_INFO *cs) + bool add_table_option_convert_charset(Charset_collation_map_st::Used *used, + const Charset_collation_map_st &map, + CHARSET_INFO *cs) { // cs can be NULL, e.g.: ALTER TABLE t1 CONVERT TO CHARACTER SET DEFAULT; used_fields|= (HA_CREATE_USED_CHARSET | HA_CREATE_USED_DEFAULT_CHARSET); return cs ? - convert_charset_collation.merge_exact_charset(Lex_exact_charset(cs)) : + convert_charset_collation.merge_exact_charset(used, map, + Lex_exact_charset(cs)) : convert_charset_collation.merge_charset_default(); } - bool add_table_option_convert_collation(const Lex_extended_collation_st &cl) + bool add_table_option_convert_collation(Charset_collation_map_st::Used *used, + const Charset_collation_map_st &map, + const Lex_extended_collation_st &cl) { used_fields|= (HA_CREATE_USED_CHARSET | HA_CREATE_USED_DEFAULT_CHARSET); - return convert_charset_collation.merge_collation(cl); + return convert_charset_collation.merge_collation(used, map, cl); } - bool add_table_option_default_charset(CHARSET_INFO *cs) + bool add_table_option_default_charset(Charset_collation_map_st::Used *used, + const Charset_collation_map_st &map, + CHARSET_INFO *cs) { // cs can be NULL, e.g.: CREATE TABLE t1 (..) CHARACTER SET DEFAULT; used_fields|= HA_CREATE_USED_DEFAULT_CHARSET; return cs ? - default_charset_collation.merge_exact_charset(Lex_exact_charset(cs)) : + default_charset_collation.merge_exact_charset(used, map, + Lex_exact_charset(cs)) : default_charset_collation.merge_charset_default(); } - bool add_table_option_default_collation(const Lex_extended_collation_st &cl) + bool add_table_option_default_collation(Charset_collation_map_st::Used *used, + const Charset_collation_map_st &map, + const Lex_extended_collation_st &cl) { used_fields|= HA_CREATE_USED_DEFAULT_CHARSET; - return default_charset_collation.merge_collation(cl); + return default_charset_collation.merge_collation(used, map, cl); } bool resolve_to_charset_collation_context(THD *thd, diff --git a/sql/item_func.h b/sql/item_func.h index 6e714814526..157eb88406e 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -3784,10 +3784,12 @@ public: } bool set(const Type_handler *handler, const Lex_length_and_dec_st & length_and_dec, + Charset_collation_map_st::Used *used, + const Charset_collation_map_st &map, const Lex_column_charset_collation_attrs_st &cscl, CHARSET_INFO *defcs) { - CHARSET_INFO *tmp= cscl.resolved_to_character_set(defcs); + CHARSET_INFO *tmp= cscl.resolved_to_character_set(used, map, defcs); if (!tmp) return true; set(handler, length_and_dec, tmp); diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index b38d7086548..f02f0ba6056 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -3830,7 +3830,10 @@ bool Item_func_set_collation::fix_length_and_dec(THD *thd) if (agg_arg_charsets_for_string_result(collation, args, 1)) return true; Lex_exact_charset_opt_extended_collate cl(collation.collation, true); - if (cl.merge_collation_override(m_set_collation)) + Character_set_collations_used used(thd); + if (cl.merge_collation_override(&used, + thd->variables.character_set_collations, + m_set_collation)) return true; collation.set(cl.collation().charset_info(), DERIVATION_EXPLICIT, args[0]->collation.repertoire); diff --git a/sql/json_table.cc b/sql/json_table.cc index 05ee83bd3d8..c4f03201654 100644 --- a/sql/json_table.cc +++ b/sql/json_table.cc @@ -935,7 +935,11 @@ int Json_table_column::set(THD *thd, enum_type ctype, const LEX_CSTRING &path, return set(thd, ctype, path, nullptr); CHARSET_INFO *tmp; - if (!(tmp= cl.resolved_to_character_set(&my_charset_utf8mb4_general_ci))) + Character_set_collations_used used(thd); + if (!(tmp= cl.resolved_to_character_set( + &used, + thd->variables.character_set_collations, + &my_charset_utf8mb4_general_ci))) return 1; return set(thd, ctype, path, tmp); } diff --git a/sql/lex_charset.cc b/sql/lex_charset.cc index cfb74a0bf04..0673901bc44 100644 --- a/sql/lex_charset.cc +++ b/sql/lex_charset.cc @@ -197,7 +197,9 @@ Lex_context_collation::raise_if_not_equal(const Lex_context_collation &cl) const CREATE DATABASE db1 COLLATE DEFAULT CHARACTER SET latin1; */ bool Lex_exact_charset_opt_extended_collate:: - merge_context_collation_override(const Lex_context_collation &cl) + merge_context_collation_override(Charset_collation_map_st::Used *used, + const Charset_collation_map_st &map, + const Lex_context_collation &cl) { DBUG_ASSERT(m_ci); @@ -215,7 +217,7 @@ bool Lex_exact_charset_opt_extended_collate:: // COLLATE DEFAULT if (cl.is_contextually_typed_collate_default()) { - CHARSET_INFO *ci= find_default_collation(); + CHARSET_INFO *ci= find_mapped_default_collation(used, map); DBUG_ASSERT(ci); if (!ci) return true; @@ -238,7 +240,9 @@ bool Lex_exact_charset_opt_extended_collate:: } -bool Lex_extended_collation_st::merge_exact_charset(const Lex_exact_charset &cs) +bool Lex_extended_collation_st::merge_exact_charset(Charset_collation_map_st::Used *used, + const Charset_collation_map_st &map, + const Lex_exact_charset &cs) { switch (m_type) { case TYPE_EXACT: @@ -250,7 +254,7 @@ bool Lex_extended_collation_st::merge_exact_charset(const Lex_exact_charset &cs) { // COLLATE DEFAULT .. CHARACTER SET latin1 Lex_exact_charset_opt_extended_collate tmp(cs); - if (tmp.merge_context_collation(Lex_context_collation(m_ci))) + if (tmp.merge_context_collation(used, map, Lex_context_collation(m_ci))) return true; *this= Lex_extended_collation(tmp.collation()); return false; @@ -419,7 +423,7 @@ CHARSET_INFO *Lex_exact_charset_opt_extended_collate::find_bin_collation() const CHARSET_INFO * -Lex_exact_charset_opt_extended_collate::find_default_collation() const +Lex_exact_charset_opt_extended_collate::find_compiled_default_collation() const { // See comments in find_bin_collation() DBUG_ASSERT(m_ci->cs_name.length !=4 || memcmp(m_ci->cs_name.str, "utf8", 4)); @@ -447,6 +451,17 @@ Lex_exact_charset_opt_extended_collate::find_default_collation() const } +CHARSET_INFO * +Lex_exact_charset_opt_extended_collate:: + find_mapped_default_collation(Charset_collation_map_st::Used *used, + const Charset_collation_map_st &map) const +{ + CHARSET_INFO *cs= find_compiled_default_collation(); + if (!cs) + return nullptr; + return map.get_collation_for_charset(used, cs); +} + /* Resolve an empty or a contextually typed collation according to the upper level default character set (and optionally a collation), e.g.: @@ -459,7 +474,9 @@ Lex_exact_charset_opt_extended_collate::find_default_collation() const "def" is the upper level CHARACTER SET clause (e.g. of a table) */ CHARSET_INFO *Lex_exact_charset_extended_collation_attrs_st:: - resolved_to_character_set(CHARSET_INFO *def) const + resolved_to_character_set(Charset_collation_map_st::Used *used, + const Charset_collation_map_st &map, + CHARSET_INFO *def) const { DBUG_ASSERT(def); @@ -467,6 +484,10 @@ CHARSET_INFO *Lex_exact_charset_extended_collation_attrs_st:: case TYPE_EMPTY: return def; case TYPE_CHARACTER_SET: + { + DBUG_ASSERT(m_ci); + return map.get_collation_for_charset(used, m_ci); + } case TYPE_CHARACTER_SET_COLLATE_EXACT: case TYPE_COLLATE_EXACT: DBUG_ASSERT(m_ci); @@ -474,7 +495,7 @@ CHARSET_INFO *Lex_exact_charset_extended_collation_attrs_st:: case TYPE_COLLATE_CONTEXTUALLY_TYPED: { Lex_exact_charset_opt_extended_collate tmp(def, true); - if (tmp.merge_context_collation_override(Lex_context_collation(m_ci))) + if (tmp.merge_context_collation_override(used, map, Lex_context_collation(m_ci))) return NULL; return tmp.collation().charset_info(); } @@ -526,7 +547,9 @@ bool Lex_exact_charset_extended_collation_attrs_st:: bool Lex_exact_charset_extended_collation_attrs_st:: - merge_context_collation(const Lex_context_collation &cl) + merge_context_collation(Charset_collation_map_st::Used *used, + const Charset_collation_map_st &map, + const Lex_context_collation &cl) { switch (m_type) { case TYPE_EMPTY: @@ -540,7 +563,7 @@ bool Lex_exact_charset_extended_collation_attrs_st:: { // CHARACTER SET latin1 .. COLLATE DEFAULT Lex_exact_charset_opt_extended_collate tmp(m_ci, false); - if (tmp.merge_context_collation(cl)) + if (tmp.merge_context_collation(used, map, cl)) return true; *this= Lex_exact_charset_extended_collation_attrs(tmp); return false; @@ -582,24 +605,29 @@ bool Lex_exact_charset_opt_extended_collate:: bool Lex_exact_charset_opt_extended_collate:: - merge_context_collation(const Lex_context_collation &cl) + merge_context_collation(Charset_collation_map_st::Used *used, + const Charset_collation_map_st &map, + const Lex_context_collation &cl) { // CHARACTER SET latin1 [COLLATE latin1_bin] .. COLLATE DEFAULT if (m_with_collate) return Lex_exact_collation(m_ci). raise_if_conflicts_with_context_collation(cl, false); - return merge_context_collation_override(cl); + return merge_context_collation_override(used, map, cl); } bool Lex_exact_charset_extended_collation_attrs_st:: - merge_collation(const Lex_extended_collation_st &cl) + merge_collation(Charset_collation_map_st::Used *used, + const Charset_collation_map_st &map, + const Lex_extended_collation_st &cl) { switch (cl.type()) { case Lex_extended_collation_st::TYPE_EXACT: return merge_exact_collation(Lex_exact_collation(cl.charset_info())); case Lex_extended_collation_st::TYPE_CONTEXTUALLY_TYPED: - return merge_context_collation(Lex_context_collation(cl.charset_info())); + return merge_context_collation(used, map, + Lex_context_collation(cl.charset_info())); } DBUG_ASSERT(0); return false; @@ -613,7 +641,9 @@ bool Lex_exact_charset_extended_collation_attrs_st:: @param cs - The "CHARACTER SET exact_charset_name". */ bool Lex_exact_charset_extended_collation_attrs_st:: - merge_exact_charset(const Lex_exact_charset &cs) + merge_exact_charset(Charset_collation_map_st::Used *used, + const Charset_collation_map_st &map, + const Lex_exact_charset &cs) { DBUG_ASSERT(cs.charset_info()); @@ -643,7 +673,7 @@ bool Lex_exact_charset_extended_collation_attrs_st:: // COLLATE DEFAULT .. CHARACTER SET cs { Lex_exact_charset_opt_extended_collate tmp(cs); - if (tmp.merge_context_collation(Lex_context_collation(m_ci))) + if (tmp.merge_context_collation(used, map, Lex_context_collation(m_ci))) return true; *this= Lex_exact_charset_extended_collation_attrs(tmp); return false; @@ -664,11 +694,14 @@ bool Lex_extended_charset_extended_collation_attrs_st::merge_charset_default() bool Lex_extended_charset_extended_collation_attrs_st:: - merge_exact_charset(const Lex_exact_charset &cs) + merge_exact_charset(Charset_collation_map_st::Used *used, + const Charset_collation_map_st &map, + const Lex_exact_charset &cs) { if (m_charset_order == CHARSET_TYPE_EMPTY) m_charset_order= CHARSET_TYPE_EXACT; - return Lex_exact_charset_extended_collation_attrs_st::merge_exact_charset(cs); + return Lex_exact_charset_extended_collation_attrs_st:: + merge_exact_charset(used, map, cs); } @@ -691,13 +724,16 @@ bool Lex_extended_charset_extended_collation_attrs_st:: CHARSET_INFO * Lex_extended_charset_extended_collation_attrs_st:: - resolved_to_context(const Charset_collation_context &ctx) const + resolved_to_context(Charset_collation_map_st::Used *used, + const Charset_collation_map_st &map, + const Charset_collation_context &ctx) const { if (Lex_opt_context_charset_st::is_empty()) { // Without CHARACTER SET DEFAULT return Lex_exact_charset_extended_collation_attrs_st:: - resolved_to_character_set(ctx.collate_default().charset_info()); + resolved_to_character_set(used, map, + ctx.collate_default().charset_info()); } // With CHARACTER SET DEFAULT @@ -767,8 +803,9 @@ Lex_extended_charset_extended_collation_attrs_st:: ALTER DATABASE db1 COLLATE DEFAULT CHARACTER SET DEFAULT; */ return Lex_exact_charset_extended_collation_attrs_st:: - resolved_to_character_set(ctx.charset_default(). - collation().charset_info()); + resolved_to_character_set(used, map, + ctx.charset_default(). + collation().charset_info()); } DBUG_ASSERT(0); return NULL; diff --git a/sql/lex_charset.h b/sql/lex_charset.h index 2bbeff8a4a6..07617f03d7e 100644 --- a/sql/lex_charset.h +++ b/sql/lex_charset.h @@ -16,6 +16,7 @@ #ifndef LEX_CHARSET_INCLUDED #define LEX_CHARSET_INCLUDED +#include "charset_collations.h" /* An extention for Charset_loader_mysys, @@ -296,7 +297,9 @@ public: bool set_by_name(const char *name, myf my_flags); // e.g. MY_UTF8_IS_UTF8MB3 bool raise_if_conflicts_with_context_collation(const Lex_context_collation &) const; - bool merge_exact_charset(const Lex_exact_charset &rhs); + bool merge_exact_charset(Charset_collation_map_st::Used *used, + const Charset_collation_map_st &map, + const Lex_exact_charset &rhs); bool merge_exact_collation(const Lex_exact_collation &rhs); bool merge(const Lex_extended_collation_st &rhs); }; @@ -347,7 +350,10 @@ public: } bool with_collate() const { return m_with_collate; } CHARSET_INFO *find_bin_collation() const; - CHARSET_INFO *find_default_collation() const; + CHARSET_INFO *find_compiled_default_collation() const; + CHARSET_INFO *find_mapped_default_collation( + Charset_collation_map_st::Used *used, + const Charset_collation_map_st &map) const; bool raise_if_charsets_differ(const Lex_exact_charset &cs) const; bool raise_if_not_applicable(const Lex_exact_collation &cl) const; /* @@ -355,18 +361,23 @@ public: So the full syntax looks like: CHARACTER SET cs [COLLATE cl] ... COLLATE cl2 */ - bool merge_collation(const Lex_extended_collation_st &cl) + bool merge_collation(Charset_collation_map_st::Used *used, + const Charset_collation_map_st &map, + const Lex_extended_collation_st &cl) { switch (cl.type()) { case Lex_extended_collation_st::TYPE_EXACT: return merge_exact_collation(Lex_exact_collation(cl.charset_info())); case Lex_extended_collation_st::TYPE_CONTEXTUALLY_TYPED: - return merge_context_collation(Lex_context_collation(cl.charset_info())); + return merge_context_collation(used, map, + Lex_context_collation(cl.charset_info())); } DBUG_ASSERT(0); return false; } - bool merge_collation_override(const Lex_extended_collation_st &cl) + bool merge_collation_override(Charset_collation_map_st::Used *used, + const Charset_collation_map_st &map, + const Lex_extended_collation_st &cl) { switch (cl.type()) { case Lex_extended_collation_st::TYPE_EXACT: @@ -374,7 +385,7 @@ public: Lex_exact_collation(cl.charset_info())); case Lex_extended_collation_st::TYPE_CONTEXTUALLY_TYPED: return merge_context_collation_override( - Lex_context_collation(cl.charset_info())); + used, map, Lex_context_collation(cl.charset_info())); } DBUG_ASSERT(0); return false; @@ -383,8 +394,12 @@ public: Add a context collation: CHARACTER SET cs [COLLATE cl] ... COLLATE DEFAULT */ - bool merge_context_collation(const Lex_context_collation &cl); - bool merge_context_collation_override(const Lex_context_collation &cl); + bool merge_context_collation(Charset_collation_map_st::Used *used, + const Charset_collation_map_st &map, + const Lex_context_collation &cl); + bool merge_context_collation_override(Charset_collation_map_st::Used *used, + const Charset_collation_map_st &map, + const Lex_context_collation &cl); /* Add an exact collation: CHARACTER SET cs [COLLATE cl] ... COLLATE latin1_bin @@ -399,7 +414,7 @@ public: { if ((m_ci->state & MY_CS_PRIMARY)) return Lex_exact_charset(m_ci); - return Lex_exact_charset(find_default_collation()); + return Lex_exact_charset(find_compiled_default_collation()); } }; @@ -507,11 +522,13 @@ public: m_ci= cs.charset_info(); m_type= TYPE_CHARACTER_SET; } - bool set_charset_collate_default(const Lex_exact_charset &cs) + bool set_charset_collate_default(Charset_collation_map_st::Used *used, + const Charset_collation_map_st &map, + const Lex_exact_charset &cs) { CHARSET_INFO *ci; if (!(ci= Lex_exact_charset_opt_extended_collate(cs). - find_default_collation())) + find_mapped_default_collation(used, map))) return true; m_ci= ci; m_type= TYPE_CHARACTER_SET_COLLATE_EXACT; @@ -544,6 +561,21 @@ public: { return m_ci; } + CHARSET_INFO *charset_info(Charset_collation_map_st::Used *used, + const Charset_collation_map_st &map) const + { + switch (m_type) + { + case TYPE_CHARACTER_SET: + return map.get_collation_for_charset(used, m_ci); + case TYPE_EMPTY: + case TYPE_CHARACTER_SET_COLLATE_EXACT: + case TYPE_COLLATE_CONTEXTUALLY_TYPED: + case TYPE_COLLATE_EXACT: + break; + } + return m_ci; + } Type type() const { return m_type; @@ -552,7 +584,9 @@ public: { return m_type == TYPE_COLLATE_CONTEXTUALLY_TYPED; } - CHARSET_INFO *resolved_to_character_set(CHARSET_INFO *cs) const; + CHARSET_INFO *resolved_to_character_set(Charset_collation_map_st::Used *used, + const Charset_collation_map_st &map, + CHARSET_INFO *cs) const; /* Merge the column CHARACTER SET clause to: - an exact collation name @@ -561,6 +595,8 @@ public: "cl" corresponds to the COLLATE clause */ bool merge_column_charset_clause_and_collate_clause( + Charset_collation_map_st::Used *used, + const Charset_collation_map_st &map, const Lex_exact_charset_extended_collation_attrs_st &cl) { switch (cl.type()) { @@ -569,7 +605,8 @@ public: case TYPE_COLLATE_EXACT: return merge_exact_collation(Lex_exact_collation(cl.charset_info())); case TYPE_COLLATE_CONTEXTUALLY_TYPED: - return merge_context_collation(Lex_context_collation(cl.charset_info())); + return merge_context_collation(used, map, + Lex_context_collation(cl.charset_info())); case TYPE_CHARACTER_SET: case TYPE_CHARACTER_SET_COLLATE_EXACT: break; @@ -584,6 +621,8 @@ public: in an independent COLLATE clause in a column attribute. */ bool merge_column_collate_clause_and_collate_clause( + Charset_collation_map_st::Used *used, + const Charset_collation_map_st &map, const Lex_exact_charset_extended_collation_attrs_st &cl) { DBUG_ASSERT(m_type != TYPE_CHARACTER_SET); @@ -593,7 +632,8 @@ public: case TYPE_COLLATE_EXACT: return merge_exact_collation(Lex_exact_collation(cl.charset_info())); case TYPE_COLLATE_CONTEXTUALLY_TYPED: - return merge_context_collation(Lex_context_collation(cl.charset_info())); + return merge_context_collation(used, map, + Lex_context_collation(cl.charset_info())); case TYPE_CHARACTER_SET: case TYPE_CHARACTER_SET_COLLATE_EXACT: break; @@ -601,10 +641,16 @@ public: DBUG_ASSERT(0); return false; } - bool merge_exact_charset(const Lex_exact_charset &cs); + bool merge_exact_charset(Charset_collation_map_st::Used *used, + const Charset_collation_map_st &map, + const Lex_exact_charset &cs); bool merge_exact_collation(const Lex_exact_collation &cl); - bool merge_context_collation(const Lex_context_collation &cl); - bool merge_collation(const Lex_extended_collation_st &cl); + bool merge_context_collation(Charset_collation_map_st::Used *used, + const Charset_collation_map_st &map, + const Lex_context_collation &cl); + bool merge_collation(Charset_collation_map_st::Used *used, + const Charset_collation_map_st &map, + const Lex_extended_collation_st &cl); }; @@ -713,9 +759,13 @@ public: } bool raise_if_charset_conflicts_with_default( const Lex_exact_charset_opt_extended_collate &def) const; - CHARSET_INFO *resolved_to_context(const Charset_collation_context &ctx) const; + CHARSET_INFO *resolved_to_context(Charset_collation_map_st::Used *used, + const Charset_collation_map_st &map, + const Charset_collation_context &ctx) const; bool merge_charset_default(); - bool merge_exact_charset(const Lex_exact_charset &cs); + bool merge_exact_charset(Charset_collation_map_st::Used *used, + const Charset_collation_map_st &map, + const Lex_exact_charset &cs); }; diff --git a/sql/log_event.cc b/sql/log_event.cc index 5e255646528..f9f52a88866 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -1379,6 +1379,7 @@ code_name(int code) case Q_HRNOW: return "Q_HRNOW"; case Q_XID: return "XID"; case Q_GTID_FLAGS3: return "Q_GTID_FLAGS3"; + case Q_CHARACTER_SET_COLLATIONS: return "Q_CHARACTER_SET_COLLATIONS"; } sprintf(buf, "CODE#%d", code); return buf; @@ -1424,7 +1425,8 @@ Query_log_event::Query_log_event(const uchar *buf, uint event_len, Log_event_type event_type) :Log_event(buf, description_event), data_buf(0), query(NullS), db(NullS), catalog_len(0), status_vars_len(0), - flags2_inited(0), sql_mode_inited(0), charset_inited(0), flags2(0), + flags2_inited(0), sql_mode_inited(0), charset_inited(0), + character_set_collations({0,0}), flags2(0), auto_increment_increment(1), auto_increment_offset(1), time_zone_len(0), lc_time_names_number(0), charset_database_number(0), table_map_for_update(0), xid(0), master_data_written(0), gtid_flags_extra(0), @@ -1552,6 +1554,17 @@ Query_log_event::Query_log_event(const uchar *buf, uint event_len, pos+= 6; break; } + case Q_CHARACTER_SET_COLLATIONS: + { + const uchar *pos0= pos; + CHECK_SPACE(pos, end, 1); + uint16 count= *pos++; + CHECK_SPACE(pos, end, count * 4); + pos+= count * 4; + character_set_collations= Lex_cstring((const char *) pos0, + (const char *) pos); + break; + } case Q_TIME_ZONE_CODE: { if (get_str_len_and_pointer(&pos, &time_zone_str, &time_zone_len, end)) diff --git a/sql/log_event.h b/sql/log_event.h index 0b1503a5b03..6942e4291b2 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -40,6 +40,7 @@ #include <functional> #include <memory> #include <map> +#include <lex_charset.h> #ifdef MYSQL_CLIENT #include "sql_const.h" @@ -229,7 +230,8 @@ class String; packet (i.e. a query) sent from client to master; First, an auxiliary log_event status vars estimation: */ -#define MAX_SIZE_LOG_EVENT_STATUS (1 + 4 /* type, flags2 */ + \ +#define MAX_SIZE_LOG_EVENT_STATUS (uint) \ + (1 + 4 /* type, flags2 */ + \ 1 + 8 /* type, sql_mode */ + \ 1 + 1 + 255 /* type, length, catalog */ + \ 1 + 4 /* type, auto_increment */ + \ @@ -241,7 +243,10 @@ class String; 1 + 4 /* type, master_data_written */ + \ 1 + 3 /* type, sec_part of NOW() */ + \ 1 + 16 + 1 + 60/* type, user_len, user, host_len, host */ + \ - 1 + 2 + 8 /* type, flags3, seq_no */) + 1 + 2 + 8 /* type, flags3, seq_no */ + \ + 1 + Charset_collation_map_st::binary_size_max() \ + /* type, map */ \ + ) #define MAX_LOG_EVENT_HEADER ( /* in order of Query_log_event::write */ \ LOG_EVENT_HEADER_LEN + /* write_header */ \ QUERY_HEADER_LEN + /* write_data */ \ @@ -323,6 +328,8 @@ class String; #define Q_XID 129 #define Q_GTID_FLAGS3 130 + +#define Q_CHARACTER_SET_COLLATIONS 131 /* Intvar event post-header */ /* Intvar event data */ @@ -2147,6 +2154,8 @@ public: bool sql_mode_inited; bool charset_inited; + LEX_CSTRING character_set_collations; + uint32 flags2; sql_mode_t sql_mode; ulong auto_increment_increment, auto_increment_offset; diff --git a/sql/log_event_client.cc b/sql/log_event_client.cc index 15d3ae8921b..e41e8aaf173 100644 --- a/sql/log_event_client.cc +++ b/sql/log_event_client.cc @@ -1982,6 +1982,38 @@ bool Query_log_event::print_query_header(IO_CACHE* file, memcpy(print_event_info->charset, charset, 6); print_event_info->charset_inited= 1; } + + if (character_set_collations.length) + { + Charset_collation_map_st map; + size_t length= map.from_binary(character_set_collations.str, + character_set_collations.length); + if (length == character_set_collations.length) + { + Binary_string str; + size_t nbytes= map.text_format_nbytes_needed(); + if (str.alloc(nbytes)) + goto err; + size_t text_length= map.print((char*) str.ptr(), nbytes); + str.length(text_length); + /* + my_b_printf() does not seem to support '%.*s' + so append a \0 terminator. + */ + str.append_char('\0'); + if (my_b_printf(file, "SET @@session.character_set_collations='%s'%s\n", + str.ptr(), print_event_info->delimiter)) + goto err; + } + else + { + if (my_b_printf(file, + "/* SET @@session.character_set_collations='%s' */\n", + "<format not recognized>")) + goto err; + } + } + if (time_zone_len) { if (memcmp(print_event_info->time_zone_str, diff --git a/sql/log_event_server.cc b/sql/log_event_server.cc index 535a2cf93de..7018f6a0d1d 100644 --- a/sql/log_event_server.cc +++ b/sql/log_event_server.cc @@ -1194,6 +1194,14 @@ bool Query_log_event::write() int2store(start+2, auto_increment_offset); start+= 4; } + + if (thd && (thd->used & THD::CHARACTER_SET_COLLATIONS_USED)) + { + *start++= Q_CHARACTER_SET_COLLATIONS; + size_t len= thd->variables.character_set_collations.to_binary((char*)start); + start+= len; + } + if (charset_inited) { *start++= Q_CHARSET_CODE; @@ -1989,6 +1997,16 @@ int Query_log_event::do_apply_event(rpl_group_info *rgi, thd->variables.sql_mode= (sql_mode_t) ((thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE) | (sql_mode & ~(sql_mode_t) MODE_NO_DIR_IN_CREATE)); + + size_t cslen= thd->variables.character_set_collations.from_binary( + character_set_collations.str, + character_set_collations.length); + if (cslen != character_set_collations.length) + { + thd->variables.character_set_collations.init(); + goto compare_errors; // QQ: report an error here? + } + if (charset_inited) { rpl_sql_thread_info *sql_info= thd->system_thread_info.rpl_sql_info; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 23f34dd0d84..f62f0dc1515 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -342,6 +342,7 @@ char *enforced_storage_engine=NULL; char *gtid_pos_auto_engines; plugin_ref *opt_gtid_pos_auto_plugins; static char compiled_default_collation_name[]= MYSQL_DEFAULT_COLLATION_NAME; +static const char *character_set_collations_str= ""; Thread_cache thread_cache; static bool binlog_format_used= false; LEX_STRING opt_init_connect, opt_init_slave; @@ -4271,6 +4272,18 @@ static int init_common_variables() */ myf utf8_flag= global_system_variables.old_behavior & OLD_MODE_UTF8_IS_UTF8MB3 ? MY_UTF8_IS_UTF8MB3 : 0; + + if (character_set_collations_str[0]) + { + Lex_cstring_strlen str(character_set_collations_str); + if (global_system_variables.character_set_collations. + from_text(str, utf8_flag)) + { + sql_print_error(ER_DEFAULT(ER_WRONG_VALUE_FOR_VAR), + "character_set_collations", character_set_collations_str); + } + } + for (;;) { char *next_character_set_name= strchr(default_character_set_name, ','); @@ -4289,7 +4302,13 @@ static int init_common_variables() return 1; // Eof of the list } else + { + Charset_collation_map_st::Used used; + default_charset_info= global_system_variables.character_set_collations. + get_collation_for_charset(&used, + default_charset_info); break; + } } if (default_collation_name) @@ -6469,6 +6488,9 @@ struct my_option my_long_options[]= {"collation-server", 0, "Set the default collation.", &default_collation_name, &default_collation_name, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, + {"character-set-collations", 0, "Set default collations for character sets.", + &character_set_collations_str, &character_set_collations_str, + 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, {"console", OPT_CONSOLE, "Write error output on screen; don't remove the console window on windows.", &opt_console, &opt_console, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, diff --git a/sql/simple_tokenizer.h b/sql/simple_tokenizer.h new file mode 100644 index 00000000000..e0a7990a522 --- /dev/null +++ b/sql/simple_tokenizer.h @@ -0,0 +1,83 @@ +/* Copyright (c) 2022, MariaDB Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef SIMPLE_TOKENIZER_INCLUDED +#define SIMPLE_TOKENIZER_INCLUDED + + +class Simple_tokenizer +{ + const char *m_ptr; + const char *m_end; +public: + Simple_tokenizer(const char *str, size_t length) + :m_ptr(str), m_end(str + length) + { } + const char *ptr() const + { + return m_ptr; + } + bool eof() const + { + return m_ptr >= m_end; + } + void get_spaces() + { + for ( ; !eof(); m_ptr++) + { + if (m_ptr[0] != ' ') + break; + } + } + bool is_ident_start(char ch) const + { + return (ch >= 'a' && ch <= 'z') || + (ch >= 'A' && ch <= 'Z') || + ch == '_'; + } + bool is_ident_body(char ch) const + { + return is_ident_start(ch) || + (ch >= '0' && ch <= '9'); + } + bool is_ident_start() const + { + return !eof() && is_ident_start(*m_ptr); + } + bool is_ident_body() const + { + return !eof() && is_ident_body(*m_ptr); + } + LEX_CSTRING get_ident() + { + if (!is_ident_start()) + return {m_ptr,0}; + const char *start= m_ptr++; + for ( ; is_ident_body(); m_ptr++) + { } + LEX_CSTRING res= {start, (size_t) (m_ptr - start)}; + return res; + } + bool get_char(char ch) + { + if (eof() || *m_ptr != ch) + return true; + m_ptr++; + return false; + } +}; + + +#endif // SIMPLE_TOKENIZER_INCLUDED diff --git a/sql/sql_class.h b/sql/sql_class.h index 54a213d8553..57eb31a3542 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -887,6 +887,8 @@ typedef struct system_variables vers_asof_timestamp_t vers_asof_timestamp; ulong vers_alter_history; my_bool binlog_alter_two_phase; + + Charset_collation_map_st character_set_collations; } SV; /** @@ -2925,7 +2927,9 @@ public: typedef uint used_t; enum { RAND_USED=1, TIME_ZONE_USED=2, QUERY_START_SEC_PART_USED=4, - THREAD_SPECIFIC_USED=8 }; + THREAD_SPECIFIC_USED=8, + CHARACTER_SET_COLLATIONS_USED= 16 + }; used_t used; @@ -5620,6 +5624,29 @@ public: }; +class Character_set_collations_used: public Charset_collation_map_st::Used +{ + THD *m_thd; +public: + Character_set_collations_used(THD *thd) + :m_thd(thd) + { } + ~Character_set_collations_used() + { + /* + Mark THD that the collation map was used, + no matter if a compiled or a mapped collation was + found during charset->collation resolution. + Even if the map was empty, we still need to print + SET @@session.character_set_collations=''; + in mariadb-binlog output. + */ + if (m_used) + m_thd->used|= THD::CHARACTER_SET_COLLATIONS_USED; + } +}; + + /* Start a new independent transaction for the THD. The old one is stored in this object and restored when calling diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index 6b195ac9fe7..f0baeae1b83 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -801,6 +801,9 @@ bool thd_init_client_charset(THD *thd, uint cs_number) cs->cs_name.str); return true; } + Charset_collation_map_st::Used used; + cs= global_system_variables.character_set_collations. + get_collation_for_charset(&used, cs); thd->org_charset= cs; thd->update_charset(cs,cs,cs); } diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 2ddd40568f9..44104c97e84 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -4405,19 +4405,23 @@ public: bool add_alter_list(LEX_CSTRING par_name, Virtual_column_info *expr, bool par_exists); bool add_alter_list(LEX_CSTRING name, LEX_CSTRING new_name, bool exists); - bool add_alter_list_item_convert_to_charset(CHARSET_INFO *cs) + bool add_alter_list_item_convert_to_charset(Charset_collation_map_st::Used *used, + const Charset_collation_map_st &map, + CHARSET_INFO *cs) { - if (create_info.add_table_option_convert_charset(cs)) + if (create_info.add_table_option_convert_charset(used, map, cs)) return true; alter_info.flags|= ALTER_CONVERT_TO; return false; } bool - add_alter_list_item_convert_to_charset(CHARSET_INFO *cs, + add_alter_list_item_convert_to_charset(Charset_collation_map_st::Used *used, + const Charset_collation_map_st &map, + CHARSET_INFO *cs, const Lex_extended_collation_st &cl) { - if (create_info.add_table_option_convert_charset(cs) || - create_info.add_table_option_convert_collation(cl)) + if (create_info.add_table_option_convert_charset(used, map, cs) || + create_info.add_table_option_convert_collation(used, map, cl)) return true; alter_info.flags|= ALTER_CONVERT_TO; return false; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index b5d93e6fd99..0e35beed8f7 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -6049,6 +6049,27 @@ finish: } thd->reset_kill_query(); } + + /* + If a non-default collation (in @@character_set_collations) + was used during the statement, the mysqlbinlog output for + the current statement will contain a sequence like this: + + SET character_set_collations='utf8mb3=utf8mb3_bin'; + INSERT INTO t1 VALUES (_utf8mb3'test'); + COMMIT; + + The statment (INSERT in this example) is already in binlog at this point, and the + and the "SET character_set_collations" is written inside a + Q_CHARACTER_SET_COLLATIONS chunk in its log entry header. + The flag CHARACTER_SET_COLLATIONS_USED is not needed any more. + + Let's suppress the flag to avoid a Q_CHARACTER_SET_COLLATIONS chunk + inside the COMMIT log entry header - it would be useless and would + only waste space in the binary log. + */ + thd->used&= ~THD::CHARACTER_SET_COLLATIONS_USED; + if (unlikely(thd->is_error()) || (thd->variables.option_bits & OPTION_MASTER_SQL_ERROR)) { diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 1e84471eef2..7c1930dcbe9 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -209,6 +209,7 @@ public: inline bool is_sql_prepare() const { return flags & (uint) IS_SQL_PREPARE; } void set_sql_prepare() { flags|= (uint) IS_SQL_PREPARE; } bool prepare(const char *packet, uint packet_length); + bool reprepare(); bool execute_loop(String *expanded_query, bool open_cursor, uchar *packet_arg, uchar *packet_end_arg); @@ -221,6 +222,10 @@ public: /* Destroy this statement */ void deallocate(); bool execute_immediate(const char *query, uint query_length); + uint prepare_time_charset_collation_map_version() const + { + return m_prepare_time_charset_collation_map_version; + } private: /** The memory root to allocate parsed tree elements (instances of Item, @@ -228,13 +233,14 @@ private: */ MEM_ROOT main_mem_root; sql_mode_t m_sql_mode; + THD::used_t m_prepare_time_thd_used_flags; + uint m_prepare_time_charset_collation_map_version; private: bool set_db(const LEX_CSTRING *db); bool set_parameters(String *expanded_query, uchar *packet, uchar *packet_end); bool execute(String *expanded_query, bool open_cursor); void deallocate_immediate(); - bool reprepare(); bool validate_metadata(Prepared_statement *copy); void swap_prepared_statement(Prepared_statement *copy); }; @@ -3538,6 +3544,13 @@ static void mysql_stmt_execute_common(THD *thd, DBUG_VOID_RETURN; } + if (stmt->prepare_time_charset_collation_map_version() != + thd->variables.character_set_collations.version()) + { + if (stmt->reprepare()) + DBUG_VOID_RETURN; + } + /* In case of direct execution application decides how many parameters to send. @@ -3628,6 +3641,13 @@ void mysql_sql_stmt_execute(THD *thd) DBUG_VOID_RETURN; } + if (stmt->prepare_time_charset_collation_map_version() != + thd->variables.character_set_collations.version()) + { + if (stmt->reprepare()) + DBUG_VOID_RETURN; + } + if (stmt->param_count != lex->prepared_stmt.param_count()) { my_error(ER_WRONG_ARGUMENTS, MYF(0), "EXECUTE"); @@ -4125,7 +4145,9 @@ Prepared_statement::Prepared_statement(THD *thd_arg) iterations(0), start_param(0), read_types(0), - m_sql_mode(thd->variables.sql_mode) + m_sql_mode(thd->variables.sql_mode), + m_prepare_time_thd_used_flags(0), + m_prepare_time_charset_collation_map_version(0) { init_sql_alloc(key_memory_prepared_statement_main_mem_root, &main_mem_root, thd_arg->variables.query_alloc_block_size, @@ -4505,6 +4527,9 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) } // The same format as for triggers to compare hr_prepare_time= my_hrtime(); + m_prepare_time_thd_used_flags= thd->used; + m_prepare_time_charset_collation_map_version= + thd->variables.character_set_collations.version(); DBUG_RETURN(error); } @@ -5060,6 +5085,13 @@ Prepared_statement::swap_prepared_statement(Prepared_statement *copy) /* Ditto */ swap_variables(LEX_CSTRING, db, copy->db); + swap_variables(uint, + m_prepare_time_charset_collation_map_version, + copy->m_prepare_time_charset_collation_map_version); + swap_variables(THD::used_t, + m_prepare_time_thd_used_flags, + copy->m_prepare_time_thd_used_flags); + DBUG_ASSERT(param_count == copy->param_count); DBUG_ASSERT(thd == copy->thd); last_error[0]= '\0'; @@ -5220,6 +5252,13 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor) MYSQL_QUERY_EXEC_START(thd->query(), thd->thread_id, thd->get_db(), &thd->security_ctx->priv_user[0], (char *) thd->security_ctx->host_or_ip, 1); + /* + If PREPARE used @@character_set_collations, + then we need to make sure binary log writes + the map in the event header. + */ + thd->used|= m_prepare_time_thd_used_flags & + THD::CHARACTER_SET_COLLATIONS_USED; error= mysql_execute_command(thd, true); MYSQL_QUERY_EXEC_DONE(error); thd->update_server_status(); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 320f9f4f97c..84617c47b7a 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -6238,7 +6238,10 @@ int fill_schema_charsets(THD *thd, TABLE_LIST *tables, COND *cond) const char *comment; restore_record(table, s->default_values); table->field[0]->store(&tmp_cs->cs_name, scs); - table->field[1]->store(&tmp_cs->coll_name, scs); + Character_set_collations_used used(thd); + CHARSET_INFO *def_cl= thd->variables.character_set_collations. + get_collation_for_charset(&used, tmp_cs); + table->field[1]->store(&def_cl->coll_name, scs); comment= tmp_cs->comment ? tmp_cs->comment : ""; table->field[2]->store(comment, strlen(comment), scs); table->field[3]->store((longlong) tmp_cs->mbmaxlen, TRUE); @@ -6341,6 +6344,9 @@ int fill_schema_collation(THD *thd, TABLE_LIST *tables, COND *cond) (tmp_cs->state & MY_CS_HIDDEN) || !(tmp_cs->state & MY_CS_PRIMARY)) continue; + Character_set_collations_used used(thd); + CHARSET_INFO *def_cl= thd->variables.character_set_collations. + get_collation_for_charset(&used, tmp_cs); for (cl= all_charsets; cl < all_charsets + array_elements(all_charsets) ; cl ++) @@ -6381,7 +6387,7 @@ int fill_schema_collation(THD *thd, TABLE_LIST *tables, COND *cond) table->field[2]->store((longlong) tmp_cl->number, TRUE); table->field[3]->set_notnull(); // IS_DEFAULT table->field[3]->store( - Show::Yes_or_empty::value(tmp_cl->default_flag()), scs); + Show::Yes_or_empty::value(def_cl == tmp_cl), scs); } table->field[4]->store( Show::Yes_or_empty::value(tmp_cl->compiled_flag()), scs); @@ -6409,6 +6415,9 @@ int fill_schema_coll_charset_app(THD *thd, TABLE_LIST *tables, COND *cond) if (!tmp_cs || !(tmp_cs->state & MY_CS_AVAILABLE) || !(tmp_cs->state & MY_CS_PRIMARY)) continue; + Character_set_collations_used used(thd); + CHARSET_INFO *def_cl= thd->variables.character_set_collations. + get_collation_for_charset(&used, tmp_cs); for (cl= all_charsets; cl < all_charsets + array_elements(all_charsets) ; cl ++) @@ -6428,7 +6437,7 @@ int fill_schema_coll_charset_app(THD *thd, TABLE_LIST *tables, COND *cond) table->field[2]->store(full_collation_name, scs); table->field[3]->store(tmp_cl->number); table->field[4]->store( - Show::Yes_or_empty::value(tmp_cl->default_flag()), scs); + Show::Yes_or_empty::value(def_cl == tmp_cl), scs); if (schema_table_store_record(thd, table)) return 1; } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 6f3f96d573f..924b6de0977 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -2200,10 +2200,12 @@ bool check_duplicates_in_interval(const char *set_or_name, Generates an error to the diagnostics area in case of a failure. */ bool Column_definition:: - prepare_charset_for_string(const Column_derived_attributes *dattr) + prepare_charset_for_string(Charset_collation_map_st::Used *used, + const Charset_collation_map_st &map, + const Column_derived_attributes *dattr) { CHARSET_INFO *tmp= charset_collation_attrs(). - resolved_to_character_set(dattr->charset()); + resolved_to_character_set(used, map, dattr->charset()); if (!tmp) return true; charset= tmp; @@ -12457,8 +12459,10 @@ bool HA_CREATE_INFO:: { // Make sure we don't do double resolution in direct SQL execution DBUG_ASSERT(!default_table_charset || thd->stmt_arena->is_stmt_execute()); + Character_set_collations_used used(thd); if (!(default_table_charset= - default_cscl.resolved_to_context(ctx))) + default_cscl.resolved_to_context(&used, + thd->variables.character_set_collations, ctx))) return true; } @@ -12469,8 +12473,10 @@ bool HA_CREATE_INFO:: // Make sure we don't do double resolution in direct SQL execution DBUG_ASSERT(!alter_table_convert_to_charset || thd->stmt_arena->is_stmt_execute()); + Character_set_collations_used used(thd); if (!(alter_table_convert_to_charset= - convert_cscl.resolved_to_context(ctx))) + convert_cscl.resolved_to_context(&used, + thd->variables.character_set_collations, ctx))) return true; } return false; diff --git a/sql/sql_type.cc b/sql/sql_type.cc index 3759c0ba02f..156db218f9b 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -2718,7 +2718,10 @@ Type_handler::Column_definition_set_attributes(THD *thd, column_definition_type_t type) const { - def->set_charset_collation_attrs(attr.charset_collation_attrs()); + Character_set_collations_used used(thd); + def->set_charset_collation_attrs(&used, + thd->variables.character_set_collations, + attr.charset_collation_attrs()); def->set_length_and_dec(attr); return false; } @@ -3028,7 +3031,10 @@ bool Type_handler_null:: *derived_attr) const { - def->prepare_charset_for_string(derived_attr); + Character_set_collations_used used(thd); + def->prepare_charset_for_string(&used, + thd->variables.character_set_collations, + derived_attr); def->create_length_to_internal_length_null(); return false; } @@ -3116,7 +3122,11 @@ bool Type_handler_typelib:: *derived_attr) const { - return def->prepare_charset_for_string(derived_attr) || + Character_set_collations_used used(thd); + return def->prepare_charset_for_string(&used, + thd->variables. + character_set_collations, + derived_attr) || def->prepare_stage1_typelib(thd, mem_root, file, table_flags); } @@ -3131,7 +3141,11 @@ bool Type_handler_string_result:: *derived_attr) const { - return def->prepare_charset_for_string(derived_attr) || + Character_set_collations_used used(thd); + return def->prepare_charset_for_string(&used, + thd->variables. + character_set_collations, + derived_attr) || def->prepare_stage1_string(thd, mem_root, file, table_flags); } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 0fed9186dd2..169a7e1a7ca 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -5536,7 +5536,9 @@ versioning_option: default_charset: opt_default charset opt_equal charset_name_or_default { - if (unlikely(Lex->create_info.add_table_option_default_charset($4))) + Character_set_collations_used used(thd); + if (unlikely(Lex->create_info.add_table_option_default_charset( + &used, thd->variables.character_set_collations, $4))) MYSQL_YYABORT; } ; @@ -5544,8 +5546,10 @@ default_charset: default_collation: opt_default COLLATE_SYM opt_equal collation_name_or_default { + Character_set_collations_used used(thd); Table_specification_st *cinfo= &Lex->create_info; - if (unlikely(cinfo->add_table_option_default_collation($4))) + if (unlikely(cinfo->add_table_option_default_collation( + &used, thd->variables.character_set_collations, $4))) MYSQL_YYABORT; } ; @@ -5797,10 +5801,14 @@ field_type_or_serial: } field_def { + Character_set_collations_used used(thd); auto tmp= $1.charset_collation_attrs(); - if (tmp.merge_column_charset_clause_and_collate_clause($3)) + if (tmp.merge_column_charset_clause_and_collate_clause( + &used, thd->variables.character_set_collations, $3)) MYSQL_YYABORT; - Lex->last_field->set_charset_collation_attrs(tmp); + Lex->last_field->set_charset_collation_attrs( + &used, thd->variables.character_set_collations, + tmp); } | SERIAL_SYM { @@ -5838,7 +5846,9 @@ field_def: | attribute_list compressed_deprecated_column_attribute { $$= $1; } | attribute_list compressed_deprecated_column_attribute attribute_list { - if (($$= $1).merge_column_collate_clause_and_collate_clause($3)) + Character_set_collations_used used(thd); + if (($$= $1).merge_column_collate_clause_and_collate_clause( + &used, thd->variables.character_set_collations, $3)) MYSQL_YYABORT; } | opt_generated_always AS virtual_column_func @@ -6318,8 +6328,10 @@ opt_precision: attribute_list: attribute_list attribute { - if (($$= $1).merge_column_collate_clause_and_collate_clause($2)) - MYSQL_YYABORT; + Character_set_collations_used used(thd); + if (($$= $1).merge_column_collate_clause_and_collate_clause( + &used, thd->variables.character_set_collations, $2)) + MYSQL_YYABORT; } | attribute ; @@ -6546,11 +6558,19 @@ binary: } | charset_or_alias COLLATE_SYM DEFAULT { - $$.set_charset_collate_default(Lex_exact_charset($1)); + Character_set_collations_used used(thd); + $$.set_charset_collate_default( + &used, + thd->variables.character_set_collations, + Lex_exact_charset($1)); } | charset_or_alias COLLATE_SYM collation_name { - if ($3.merge_exact_charset(Lex_exact_charset($1))) + Character_set_collations_used used(thd); + if ($3.merge_exact_charset( + &used, + thd->variables.character_set_collations, + Lex_exact_charset($1))) MYSQL_YYABORT; $$= Lex_exact_charset_extended_collation_attrs($3); } @@ -7630,13 +7650,17 @@ alter_list_item: } | CONVERT_SYM TO_SYM charset charset_name_or_default { - if (Lex->add_alter_list_item_convert_to_charset($4)) + Character_set_collations_used used(thd); + if (Lex->add_alter_list_item_convert_to_charset( + &used, thd->variables.character_set_collations, $4)) MYSQL_YYABORT; } | CONVERT_SYM TO_SYM charset charset_name_or_default COLLATE_SYM collation_name_or_default { - if (Lex->add_alter_list_item_convert_to_charset($4, $6)) + Character_set_collations_used used(thd); + if (Lex->add_alter_list_item_convert_to_charset( + &used, thd->variables.character_set_collations, $4, $6)) MYSQL_YYABORT; } | create_table_options_space_separated @@ -9507,7 +9531,10 @@ temporal_dyncol_type: string_dyncol_type: char opt_binary { - if ($$.set(DYN_COL_STRING, $2, thd->variables.collation_connection)) + Character_set_collations_used used(thd); + if ($$.set(DYN_COL_STRING, &used, + thd->variables.character_set_collations, + $2, thd->variables.collation_connection)) MYSQL_YYABORT; } | nchar @@ -9686,6 +9713,9 @@ column_default_non_parenthesized_expr: } | CONVERT_SYM '(' expr USING charset_name ')' { + Character_set_collations_used used(thd); + $5= thd->variables.character_set_collations. + get_collation_for_charset(&used, $5); $$= new (thd->mem_root) Item_func_conv_charset(thd, $3, $5); if (unlikely($$ == NULL)) MYSQL_YYABORT; @@ -9838,6 +9868,9 @@ function_call_keyword: } | CHAR_SYM '(' expr_list USING charset_name ')' { + Character_set_collations_used used(thd); + $5= thd->variables.character_set_collations. + get_collation_for_charset(&used, $5); $$= new (thd->mem_root) Item_func_char(thd, *$3, $5); if (unlikely($$ == NULL)) MYSQL_YYABORT; @@ -11128,19 +11161,25 @@ cast_type: { $$.set(&type_handler_long_blob, $2, &my_charset_bin); } | CHAR_SYM opt_field_length opt_binary { - if ($$.set(&type_handler_long_blob, $2, $3, + Character_set_collations_used used(thd); + if ($$.set(&type_handler_long_blob, + $2, &used, thd->variables.character_set_collations, $3, thd->variables.collation_connection)) MYSQL_YYABORT; } | VARCHAR field_length opt_binary { - if ($$.set(&type_handler_long_blob, $2, $3, + Character_set_collations_used used(thd); + if ($$.set(&type_handler_long_blob, + $2, &used, thd->variables.character_set_collations, $3, thd->variables.collation_connection)) MYSQL_YYABORT; } | VARCHAR2_ORACLE_SYM field_length opt_binary { - if ($$.set(&type_handler_long_blob, $2, $3, + Character_set_collations_used used(thd); + if ($$.set(&type_handler_long_blob, + $2, &used, thd->variables.character_set_collations, $3, thd->variables.collation_connection)) MYSQL_YYABORT; } @@ -14791,6 +14830,9 @@ text_literal: } | UNDERSCORE_CHARSET TEXT_STRING { + Character_set_collations_used used(thd); + $1= thd->variables.character_set_collations. + get_collation_for_charset(&used, $1); if (unlikely(!($$= thd->make_string_literal_charset($2, $1)))) MYSQL_YYABORT; } @@ -14930,6 +14972,9 @@ literal: Item_string_with_introducer *item_str; LEX_CSTRING tmp; $2->get_value(&tmp); + Character_set_collations_used used(thd); + $1= thd->variables.character_set_collations. + get_collation_for_charset(&used, $1); /* Pass NULL as name. Name will be set in the "select_item" rule and will include the introducer and the original hex/bin notation. @@ -16646,7 +16691,12 @@ option_value_no_option_type: { CHARSET_INFO *def= global_system_variables.character_set_client; Lex_exact_charset_opt_extended_collate tmp($2 ? $2 : def, false); - if (Lex->set_names($1.pos(), tmp, yychar == YYEMPTY)) + Lex_extended_collation_st cl; + cl.set_collate_default(); + Character_set_collations_used used(thd); + if (tmp.merge_collation(&used, thd->variables. + character_set_collations, cl) || + Lex->set_names($1.pos(), tmp, yychar == YYEMPTY)) MYSQL_YYABORT; } | NAMES_SYM charset_name_or_default @@ -16654,7 +16704,9 @@ option_value_no_option_type: { CHARSET_INFO *def= global_system_variables.character_set_client; Lex_exact_charset_opt_extended_collate tmp($2 ? $2 : def, false); - if (tmp.merge_collation($4) || + Character_set_collations_used used(thd); + if (tmp.merge_collation(&used, thd->variables. + character_set_collations, $4) || Lex->set_names($1.pos(), tmp, yychar == YYEMPTY)) MYSQL_YYABORT; } diff --git a/sql/structs.h b/sql/structs.h index 0a71719376c..6ec498c61d8 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -773,10 +773,13 @@ public: m_ci= cs; Lex_length_and_dec_st::reset(); } - bool set(int type, const Lex_column_charset_collation_attrs_st &collation, + bool set(int type, + Charset_collation_map_st::Used *used, + const Charset_collation_map_st &map, + const Lex_column_charset_collation_attrs_st &collation, CHARSET_INFO *charset) { - CHARSET_INFO *tmp= collation.resolved_to_character_set(charset); + CHARSET_INFO *tmp= collation.resolved_to_character_set(used, map, charset); if (!tmp) return true; set(type, tmp); diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 1ed3d61bcf8..c0943cd3048 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -436,6 +436,115 @@ static bool update_auto_increment_increment (sys_var *self, THD *thd, enum_var_t #endif /* WITH_WSREP */ + +class Sys_var_charset_collation_map: public sys_var +{ +public: + Sys_var_charset_collation_map(const char *name_arg, const char *comment, + int flag_args, ptrdiff_t off, size_t size, + CMD_LINE getopt, + enum binlog_status_enum binlog_status_arg) + :sys_var(&all_sys_vars, name_arg, comment, + flag_args, off, getopt.id, getopt.arg_type, + SHOW_CHAR, + DEFAULT(0), nullptr, binlog_status_arg, + nullptr, nullptr, nullptr) + { + option.var_type|= GET_STR; + } + +private: + + static bool charset_collation_map_from_item(Charset_collation_map_st *map, + Item *item, + myf utf8_flag) + { + String *value, buffer; + if (!(value= item->val_str_ascii(&buffer))) + return true; + return map->from_text(value->to_lex_cstring(), utf8_flag); + } + + static const uchar *make_value_ptr(THD *thd, + const Charset_collation_map_st &map) + { + size_t nbytes= map.text_format_nbytes_needed(); + char *buf= (char *) thd->alloc(nbytes); + size_t length= map.print(buf, nbytes); + return (uchar *) thd->strmake(buf, length); + } + + +private: + + bool do_check(THD *thd, set_var *var) override + { + Charset_collation_map_st map; + return charset_collation_map_from_item(&map, var->value, + thd->get_utf8_flag()); + } + + void session_save_default(THD *thd, set_var *var) override + { + thd->variables.character_set_collations.set( + global_system_variables.character_set_collations, 1); + } + + void global_save_default(THD *thd, set_var *var) override + { + global_system_variables.character_set_collations.init(); + } + + bool session_update(THD *thd, set_var *var) override + { + Charset_collation_map_st map; + if (!var->value) + { + session_save_default(thd, var); + return false; + } + if (charset_collation_map_from_item(&map, var->value, thd->get_utf8_flag())) + return true; + thd->variables.character_set_collations.set(map, 1); + return false; + } + + bool global_update(THD *thd, set_var *var) override + { + Charset_collation_map_st map; + if (!var->value) + { + global_save_default(thd, var); + return false; + } + if (charset_collation_map_from_item(&map, var->value, thd->get_utf8_flag())) + return true; + global_system_variables.character_set_collations= map; + return false; + } + + const uchar * + session_value_ptr(THD *thd, const LEX_CSTRING *base) const override + { + return make_value_ptr(thd, thd->variables.character_set_collations); + } + + const uchar * + global_value_ptr(THD *thd, const LEX_CSTRING *base) const override + { + return make_value_ptr(thd, global_system_variables. + character_set_collations); + } +}; + + +static Sys_var_charset_collation_map Sys_character_set_collations( + "character_set_collations", + "Default collations for character sets", + SESSION_VAR(character_set_collations), + NO_CMD_LINE, NOT_IN_BINLOG); + + static Sys_var_double Sys_analyze_sample_percentage( "analyze_sample_percentage", "Percentage of rows from the table ANALYZE TABLE will sample " |