mirror of
				https://github.com/MariaDB/server.git
				synced 2025-10-31 19:06:14 +01:00 
			
		
		
		
	 fd247cc21f
			
		
	
	
	fd247cc21f
	
	
	
		
			
			This patch also fixes:
  MDEV-33050 Build-in schemas like oracle_schema are accent insensitive
  MDEV-33084 LASTVAL(t1) and LASTVAL(T1) do not work well with lower-case-table-names=0
  MDEV-33085 Tables T1 and t1 do not work well with ENGINE=CSV and lower-case-table-names=0
  MDEV-33086 SHOW OPEN TABLES IN DB1 -- is case insensitive with lower-case-table-names=0
  MDEV-33088 Cannot create triggers in the database `MYSQL`
  MDEV-33103 LOCK TABLE t1 AS t2 -- alias is not case sensitive with lower-case-table-names=0
  MDEV-33109 DROP DATABASE MYSQL -- does not drop SP with lower-case-table-names=0
  MDEV-33110 HANDLER commands are case insensitive with lower-case-table-names=0
  MDEV-33119 User is case insensitive in INFORMATION_SCHEMA.VIEWS
  MDEV-33120 System log table names are case insensitive with lower-cast-table-names=0
- Removing the virtual function strnncoll() from MY_COLLATION_HANDLER
- Adding a wrapper function CHARSET_INFO::streq(), to compare
  two strings for equality. For now it calls strnncoll() internally.
  In the future it will turn into a virtual function.
- Adding new accent sensitive case insensitive collations:
    - utf8mb4_general1400_as_ci
    - utf8mb3_general1400_as_ci
  They implement accent sensitive case insensitive comparison.
  The weight of a character is equal to the code point of its
  upper case variant. These collations use Unicode-14.0.0 casefolding data.
  The result of
     my_charset_utf8mb3_general1400_as_ci.strcoll()
  is very close to the former
     my_charset_utf8mb3_general_ci.strcasecmp()
  There is only a difference in a couple dozen rare characters, because:
    - the switch from "tolower" to "toupper" comparison, to make
      utf8mb3_general1400_as_ci closer to utf8mb3_general_ci
    - the switch from Unicode-3.0.0 to Unicode-14.0.0
  This difference should be tolarable. See the list of affected
  characters in the MDEV description.
  Note, utf8mb4_general1400_as_ci correctly handles non-BMP characters!
  Unlike utf8mb4_general_ci, it does not treat all BMP characters
  as equal.
- Adding classes representing names of the file based database objects:
    Lex_ident_db
    Lex_ident_table
    Lex_ident_trigger
  Their comparison collation depends on the underlying
  file system case sensitivity and on --lower-case-table-names
  and can be either my_charset_bin or my_charset_utf8mb3_general1400_as_ci.
- Adding classes representing names of other database objects,
  whose names have case insensitive comparison style,
  using my_charset_utf8mb3_general1400_as_ci:
  Lex_ident_column
  Lex_ident_sys_var
  Lex_ident_user_var
  Lex_ident_sp_var
  Lex_ident_ps
  Lex_ident_i_s_table
  Lex_ident_window
  Lex_ident_func
  Lex_ident_partition
  Lex_ident_with_element
  Lex_ident_rpl_filter
  Lex_ident_master_info
  Lex_ident_host
  Lex_ident_locale
  Lex_ident_plugin
  Lex_ident_engine
  Lex_ident_server
  Lex_ident_savepoint
  Lex_ident_charset
  engine_option_value::Name
- All the mentioned Lex_ident_xxx classes implement a method streq():
  if (ident1.streq(ident2))
     do_equal();
  This method works as a wrapper for CHARSET_INFO::streq().
- Changing a lot of "LEX_CSTRING name" to "Lex_ident_xxx name"
  in class members and in function/method parameters.
- Replacing all calls like
    system_charset_info->coll->strcasecmp(ident1, ident2)
  to
    ident1.streq(ident2)
- Taking advantage of the c++11 user defined literal operator
  for LEX_CSTRING (see m_strings.h) and Lex_ident_xxx (see lex_ident.h)
  data types. Use example:
  const Lex_ident_column primary_key_name= "PRIMARY"_Lex_ident_column;
  is now a shorter version of:
  const Lex_ident_column primary_key_name=
    Lex_ident_column({STRING_WITH_LEN("PRIMARY")});
		
	
			
		
			
				
	
	
		
			378 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			378 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|    Copyright (c) 2019, 2020, MariaDB
 | |
| 
 | |
|    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-1301  USA */
 | |
| 
 | |
| /* !!! For inclusion into ha_federatedx.cc */
 | |
| 
 | |
| 
 | |
| /*
 | |
|   This is a quick a dirty implemention of the derived_handler and select_handler
 | |
|   interfaces to be used to push select queries and the queries specifying
 | |
|   derived tables into FEDERATEDX engine.
 | |
|   The functions
 | |
|     create_federatedx_derived_handler and
 | |
|     create_federatedx_select_handler
 | |
|   that return the corresponding interfaces for pushdown capabilities do
 | |
|   not check a lot of things. In particular they do not check that the tables
 | |
|   of the pushed queries belong to the same foreign server.
 | |
| 
 | |
|   The implementation is provided purely for testing purposes.
 | |
|   The pushdown capabilities are enabled by turning on the plugin system
 | |
|   variable federated_pushdown:
 | |
|     set global federated_pushdown=1;
 | |
| */
 | |
| 
 | |
| /*
 | |
|   Check if table and database names are equal on local and remote servers
 | |
| 
 | |
|   SYNOPSIS
 | |
|     local_and_remote_names_match()
 | |
|     tbl_share      Pointer to current table TABLE_SHARE structure
 | |
|     fshare         Pointer to current table FEDERATEDX_SHARE structure
 | |
| 
 | |
|   DESCRIPTION
 | |
|     FederatedX table on the local server may refer to a table having another
 | |
|     name on the remote server. The remote table may even reside in a different
 | |
|     database. For example:
 | |
| 
 | |
|     -- Remote server
 | |
|     CREATE TABLE t1 (id int(32));
 | |
| 
 | |
|     -- Local server
 | |
|     CREATE TABLE t2 ENGINE="FEDERATEDX"
 | |
|     CONNECTION="mysql://joe:joespass@192.168.1.111:9308/federatedx/t1";
 | |
| 
 | |
|     It's not a problem while the federated_pushdown is disabled 'cause
 | |
|     the CONNECTION strings are being parsed for every table during
 | |
|     the execution, so the table names are translated from local to remote.
 | |
|     But in case of the federated_pushdown the whole query is pushed down
 | |
|     to the engine without any translation, so the remote server may try
 | |
|     to select data from a nonexistent table (for example, query
 | |
|     "SELECT * FROM t2" will try to retrieve data from nonexistent "t2").
 | |
| 
 | |
|     This function checks whether there is a mismatch between local and remote
 | |
|     table/database names
 | |
| 
 | |
|   RETURN VALUE
 | |
|     false           names are equal
 | |
|     true            names are not equal
 | |
| 
 | |
| */
 | |
| bool local_and_remote_names_mismatch(const TABLE_SHARE *tbl_share,
 | |
|                                      const FEDERATEDX_SHARE *fshare)
 | |
| {
 | |
|   return !tbl_share->db.streq(Lex_cstring_strlen(fshare->database)) ||
 | |
|          !tbl_share->table_name.streq(Lex_cstring_strlen(fshare->table_name));
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Check that all tables in the sel_lex use the FederatedX storage engine
 | |
|   and return one of them
 | |
|   @return
 | |
|     One of the tables from sel_lex
 | |
| */
 | |
| static TABLE *get_fed_table_for_pushdown(SELECT_LEX *sel_lex)
 | |
| {
 | |
|   TABLE *table= nullptr;
 | |
|   if (!sel_lex->join)
 | |
|     return nullptr;
 | |
|   for (TABLE_LIST *tbl= sel_lex->join->tables_list; tbl; tbl= tbl->next_local)
 | |
|   {
 | |
|     if (!tbl->table)
 | |
|       return nullptr;
 | |
|     if (tbl->derived)
 | |
|     {
 | |
|       /*
 | |
|         Skip derived table for now as they will be checked
 | |
|         in the subsequent loop
 | |
|       */
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     /*
 | |
|       We intentionally don't support partitioned federatedx tables here, so
 | |
|       use file->ht and not file->partition_ht().
 | |
|     */
 | |
|     if (tbl->table->file->ht != federatedx_hton)
 | |
|       return nullptr;
 | |
|     const FEDERATEDX_SHARE *fshare=
 | |
|         ((ha_federatedx *) tbl->table->file)->get_federatedx_share();
 | |
|     if (local_and_remote_names_mismatch(tbl->table->s, fshare))
 | |
|       return nullptr;
 | |
| 
 | |
|     if (!table)
 | |
|       table= tbl->table;
 | |
|   }
 | |
| 
 | |
|   for (SELECT_LEX_UNIT *un= sel_lex->first_inner_unit(); un;
 | |
|        un= un->next_unit())
 | |
|   {
 | |
|     for (SELECT_LEX *sl= un->first_select(); sl; sl= sl->next_select())
 | |
|     {
 | |
|       auto inner_tbl= get_fed_table_for_pushdown(sl);
 | |
|       if (!inner_tbl)
 | |
|         return nullptr;
 | |
|       if (!table)
 | |
|         table= inner_tbl;
 | |
|     }
 | |
|   }
 | |
|   return table;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Check that all tables in the lex_unit use the FederatedX storage engine
 | |
|   and return one of them
 | |
| 
 | |
|   @return
 | |
|     One of the tables from lex_unit
 | |
| */
 | |
| static TABLE *get_fed_table_for_unit_pushdown(SELECT_LEX_UNIT *lex_unit)
 | |
| {
 | |
|   TABLE *table= nullptr;
 | |
|   for (auto sel_lex= lex_unit->first_select(); sel_lex;
 | |
|        sel_lex= sel_lex->next_select())
 | |
|   {
 | |
|     auto next_tbl= get_fed_table_for_pushdown(sel_lex);
 | |
|     if (!next_tbl)
 | |
|       return nullptr;
 | |
|     if (!table)
 | |
|       table= next_tbl;
 | |
|   }
 | |
|   return table;
 | |
| }
 | |
| 
 | |
| 
 | |
| static derived_handler*
 | |
| create_federatedx_derived_handler(THD* thd, TABLE_LIST *derived)
 | |
| {
 | |
|   if (!use_pushdown)
 | |
|     return 0;
 | |
| 
 | |
|   SELECT_LEX_UNIT *unit= derived->derived;
 | |
| 
 | |
|   auto tbl= get_fed_table_for_unit_pushdown(unit);
 | |
|   if (!tbl)
 | |
|     return nullptr;
 | |
| 
 | |
|   return new ha_federatedx_derived_handler(thd, derived, tbl);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Implementation class of the derived_handler interface for FEDERATEDX:
 | |
|   class implementation
 | |
| */
 | |
| 
 | |
| ha_federatedx_derived_handler::ha_federatedx_derived_handler(THD *thd,
 | |
|                                                              TABLE_LIST *dt,
 | |
|                                                              TABLE *tbl)
 | |
|   : derived_handler(thd, federatedx_hton),
 | |
|     federatedx_handler_base(thd, tbl)
 | |
| {
 | |
|   derived= dt;
 | |
| 
 | |
|   query.length(0);
 | |
|   dt->derived->print(&query,
 | |
|                      enum_query_type(QT_VIEW_INTERNAL |
 | |
|                                      QT_ITEM_ORIGINAL_FUNC_NULLIF |
 | |
|                                      QT_PARSABLE));
 | |
| }
 | |
| 
 | |
| ha_federatedx_derived_handler::~ha_federatedx_derived_handler() = default;
 | |
| 
 | |
| int federatedx_handler_base::end_scan_()
 | |
| {
 | |
|   DBUG_ENTER("ha_federatedx_derived_handler::end_scan");
 | |
| 
 | |
|   (*iop)->free_result(stored_result);
 | |
| 
 | |
|   free_share(txn, share);
 | |
| 
 | |
|   DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Create FederatedX select handler for processing either a single select
 | |
|   (in this case sel_lex is initialized and lex_unit==NULL)
 | |
|   or a select that is part of a unit
 | |
|   (in this case both sel_lex and lex_unit are initialized)
 | |
| */
 | |
| static select_handler *
 | |
| create_federatedx_select_handler(THD *thd, SELECT_LEX *sel_lex,
 | |
|                                  SELECT_LEX_UNIT *lex_unit)
 | |
| {
 | |
|   if (!use_pushdown)
 | |
|     return nullptr;
 | |
| 
 | |
|   auto tbl= get_fed_table_for_pushdown(sel_lex);
 | |
|   if (!tbl)
 | |
|     return nullptr;
 | |
| 
 | |
|   if (sel_lex->uncacheable & UNCACHEABLE_SIDEEFFECT)
 | |
|     return NULL;
 | |
| 
 | |
|   return new ha_federatedx_select_handler(thd, sel_lex, lex_unit, tbl);
 | |
| }
 | |
| 
 | |
| /*
 | |
|   Create FederatedX select handler for processing a unit as a whole.
 | |
|   Term "unit" stands for multiple SELECTs combined with
 | |
|   UNION/EXCEPT/INTERSECT operators
 | |
| */
 | |
| static select_handler *
 | |
| create_federatedx_unit_handler(THD *thd, SELECT_LEX_UNIT *sel_unit)
 | |
| {
 | |
|   if (!use_pushdown)
 | |
|     return nullptr;
 | |
| 
 | |
|   auto tbl= get_fed_table_for_unit_pushdown(sel_unit);
 | |
|   if (!tbl)
 | |
|     return nullptr;
 | |
| 
 | |
|   if (sel_unit->uncacheable & UNCACHEABLE_SIDEEFFECT)
 | |
|     return nullptr;
 | |
| 
 | |
|   return new ha_federatedx_select_handler(thd, sel_unit, tbl);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Implementation class of the select_handler interface for FEDERATEDX:
 | |
|   class implementation
 | |
| */
 | |
| 
 | |
| federatedx_handler_base::federatedx_handler_base(THD *thd_arg, TABLE *tbl_arg)
 | |
|  : share(NULL), txn(NULL), iop(NULL), stored_result(NULL),
 | |
|    query(thd_arg->charset()),
 | |
|    query_table(tbl_arg)
 | |
| {}
 | |
| 
 | |
| ha_federatedx_select_handler::~ha_federatedx_select_handler() = default;
 | |
| 
 | |
| ha_federatedx_select_handler::ha_federatedx_select_handler(
 | |
|     THD *thd, SELECT_LEX_UNIT *lex_unit, TABLE *tbl)
 | |
|   : select_handler(thd, federatedx_hton, lex_unit), 
 | |
|     federatedx_handler_base(thd, tbl)
 | |
| {
 | |
|   query.length(0);
 | |
|   lex_unit->print(&query, PRINT_QUERY_TYPE);
 | |
| }
 | |
| 
 | |
| ha_federatedx_select_handler::ha_federatedx_select_handler(
 | |
|     THD *thd, SELECT_LEX *select_lex, SELECT_LEX_UNIT *lex_unit, TABLE *tbl)
 | |
|     : select_handler(thd, federatedx_hton, select_lex, lex_unit),
 | |
|       federatedx_handler_base(thd, tbl)
 | |
| {
 | |
|   query.length(0);
 | |
|   if (get_pushdown_type() == select_pushdown_type::SINGLE_SELECT)
 | |
|   {
 | |
|     /*
 | |
|       Must use SELECT_LEX_UNIT::print() instead of SELECT_LEX::print() here
 | |
|       to print possible CTEs which are stored at SELECT_LEX_UNIT::with_clause
 | |
|     */
 | |
|     select_lex->master_unit()->print(&query, PRINT_QUERY_TYPE);
 | |
|   }
 | |
|   else if (get_pushdown_type() == select_pushdown_type::PART_OF_UNIT)
 | |
|   {
 | |
|     /*
 | |
|       CTEs are not supported for partial select pushdown so use
 | |
|       SELECT_LEX::print() here
 | |
|     */
 | |
|     select_lex->print(thd, &query, PRINT_QUERY_TYPE);
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     /*
 | |
|       Other select_pushdown_types are not allowed in this constructor.
 | |
|       The case of select_pushdown_type::WHOLE_UNIT is handled at another
 | |
|       overload of the constuctor
 | |
|     */
 | |
|     DBUG_ASSERT(0);
 | |
|   }
 | |
| }
 | |
| 
 | |
| int federatedx_handler_base::init_scan_()
 | |
| {
 | |
|   THD *thd= query_table->in_use;
 | |
|   int rc= 0;
 | |
| 
 | |
|   DBUG_ENTER("ha_federatedx_select_handler::init_scan");
 | |
| 
 | |
|   ha_federatedx *h= (ha_federatedx *) query_table->file;
 | |
|   iop= &h->io;
 | |
|   share= get_share(query_table->s->table_name.str, query_table);
 | |
|   txn= h->get_txn(thd);
 | |
|   if ((rc= txn->acquire(share, thd, TRUE, iop)))
 | |
|     DBUG_RETURN(rc);
 | |
| 
 | |
|   if ((*iop)->query(query.ptr(), query.length()))
 | |
|     goto err;
 | |
| 
 | |
|   stored_result= (*iop)->store_result();
 | |
|   if (!stored_result)
 | |
|       goto err;
 | |
| 
 | |
|   DBUG_RETURN(0);
 | |
| 
 | |
| err:
 | |
|   DBUG_RETURN(HA_FEDERATEDX_ERROR_WITH_REMOTE_SYSTEM);
 | |
| }
 | |
| 
 | |
| int federatedx_handler_base::next_row_(TABLE *table)
 | |
| {
 | |
|   int rc= 0;
 | |
|   FEDERATEDX_IO_ROW *row;
 | |
|   ulong *lengths;
 | |
|   Field **field;
 | |
|   int column= 0;
 | |
|   Time_zone *saved_time_zone= table->in_use->variables.time_zone;
 | |
|   DBUG_ENTER("ha_federatedx_select_handler::next_row");
 | |
| 
 | |
|   if ((rc= txn->acquire(share, table->in_use, TRUE, iop)))
 | |
|     DBUG_RETURN(rc);
 | |
| 
 | |
|   if (!(row= (*iop)->fetch_row(stored_result)))
 | |
|     DBUG_RETURN(HA_ERR_END_OF_FILE);
 | |
| 
 | |
|   /* Convert row to internal format */
 | |
|   table->in_use->variables.time_zone= UTC;
 | |
|   lengths= (*iop)->fetch_lengths(stored_result);
 | |
| 
 | |
|   for (field= table->field; *field; field++, column++)
 | |
|   {
 | |
|     if ((*iop)->is_column_null(row, column))
 | |
|        (*field)->set_null();
 | |
|     else
 | |
|     {
 | |
|       (*field)->set_notnull();
 | |
|       (*field)->store((*iop)->get_column_data(row, column),
 | |
|                       lengths[column], &my_charset_bin);
 | |
|     }
 | |
|   }
 | |
|   table->in_use->variables.time_zone= saved_time_zone;
 | |
| 
 | |
|   DBUG_RETURN(rc);
 | |
| }
 | |
| 
 | |
| int ha_federatedx_select_handler::end_scan()
 | |
| {
 | |
|   free_tmp_table(thd, table);
 | |
|   table= 0;
 | |
| 
 | |
|   return federatedx_handler_base::end_scan_();
 | |
| }
 |