mirror of
https://github.com/MariaDB/server.git
synced 2025-01-17 12:32:27 +01:00
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_();
|
|
}
|