diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-07-01 18:15:00 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-07-01 18:15:00 +0000 |
commit | a2a2e32c02643a0cec111511220227703fda1cd5 (patch) | |
tree | 69cc2b631234c2a8e026b9cd4d72676c61c594df /storage/federatedx | |
parent | Releasing progress-linux version 1:10.11.8-1~progress7.99u1. (diff) | |
download | mariadb-a2a2e32c02643a0cec111511220227703fda1cd5.tar.xz mariadb-a2a2e32c02643a0cec111511220227703fda1cd5.zip |
Merging upstream version 1:11.4.2.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'storage/federatedx')
-rw-r--r-- | storage/federatedx/federatedx_io_mysql.cc | 15 | ||||
-rw-r--r-- | storage/federatedx/federatedx_pushdown.cc | 333 | ||||
-rw-r--r-- | storage/federatedx/federatedx_pushdown.h | 57 | ||||
-rw-r--r-- | storage/federatedx/ha_federatedx.cc | 32 | ||||
-rw-r--r-- | storage/federatedx/ha_federatedx.h | 32 |
5 files changed, 262 insertions, 207 deletions
diff --git a/storage/federatedx/federatedx_io_mysql.cc b/storage/federatedx/federatedx_io_mysql.cc index cc234f1e..72b90298 100644 --- a/storage/federatedx/federatedx_io_mysql.cc +++ b/storage/federatedx/federatedx_io_mysql.cc @@ -429,6 +429,7 @@ int federatedx_io_mysql::actual_query(const char *buffer, size_t length) if (!mysql.net.vio) { my_bool my_true= 1; + my_bool my_false= 0; if (!(mysql_init(&mysql))) DBUG_RETURN(-1); @@ -440,15 +441,11 @@ int federatedx_io_mysql::actual_query(const char *buffer, size_t length) */ /* this sets the csname like 'set names utf8' */ mysql_options(&mysql, MYSQL_SET_CHARSET_NAME, get_charsetname()); - mysql_options(&mysql, MYSQL_OPT_USE_THREAD_SPECIFIC_MEMORY, - (char*) &my_true); - - if (!mysql_real_connect(&mysql, - get_hostname(), - get_username(), - get_password(), - get_database(), - get_port(), + mysql_options(&mysql, MYSQL_OPT_USE_THREAD_SPECIFIC_MEMORY, &my_true); + mysql_options(&mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, &my_false); + + if (!mysql_real_connect(&mysql, get_hostname(), get_username(), + get_password(), get_database(), get_port(), get_socket(), 0)) DBUG_RETURN(ER_CONNECT_TO_FOREIGN_DATA_SOURCE); diff --git a/storage/federatedx/federatedx_pushdown.cc b/storage/federatedx/federatedx_pushdown.cc index e9a9791a..c4d77a73 100644 --- a/storage/federatedx/federatedx_pushdown.cc +++ b/storage/federatedx/federatedx_pushdown.cc @@ -34,7 +34,6 @@ set global federated_pushdown=1; */ - /* Check if table and database names are equal on local and remote servers @@ -93,125 +92,123 @@ bool local_and_remote_names_mismatch(const TABLE_SHARE *tbl_share, } -static derived_handler* -create_federatedx_derived_handler(THD* thd, TABLE_LIST *derived) +/* + 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) { - if (!use_pushdown) - return 0; - - ha_federatedx_derived_handler* handler = NULL; - - SELECT_LEX_UNIT *unit= derived->derived; - - for (SELECT_LEX *sl= unit->first_select(); sl; sl= sl->next_select()) + TABLE *table= nullptr; + if (!sel_lex->join) + return nullptr; + for (TABLE_LIST *tbl= sel_lex->join->tables_list; tbl; tbl= tbl->next_local) { - if (!(sl->join)) - return 0; - for (TABLE_LIST *tbl= sl->join->tables_list; tbl; tbl= tbl->next_local) + if (!tbl->table) + return nullptr; + if (tbl->derived) { - if (!tbl->table) - return 0; /* - We intentionally don't support partitioned federatedx tables here, so - use file->ht and not file->partition_ht(). + Skip derived table for now as they will be checked + in the subsequent loop */ - if (tbl->table->file->ht != federatedx_hton) - return 0; - - const FEDERATEDX_SHARE *fshare= - ((ha_federatedx*)tbl->table->file)->get_federatedx_share(); - if (local_and_remote_names_mismatch(tbl->table->s, fshare)) - return 0; + continue; } - } - handler= new ha_federatedx_derived_handler(thd, derived); + /* + 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; - return handler; + 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; } /* - Implementation class of the derived_handler interface for FEDERATEDX: - class implementation -*/ + Check that all tables in the lex_unit use the FederatedX storage engine + and return one of them -ha_federatedx_derived_handler::ha_federatedx_derived_handler(THD *thd, - TABLE_LIST *dt) - : derived_handler(thd, federatedx_hton), - share(NULL), txn(NULL), iop(NULL), stored_result(NULL) + @return + One of the tables from lex_unit +*/ +static TABLE *get_fed_table_for_unit_pushdown(SELECT_LEX_UNIT *lex_unit) { - derived= dt; + 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; } -ha_federatedx_derived_handler::~ha_federatedx_derived_handler() = default; -int ha_federatedx_derived_handler::init_scan() +static derived_handler* +create_federatedx_derived_handler(THD* thd, TABLE_LIST *derived) { - THD *thd; - int rc= 0; - - DBUG_ENTER("ha_federatedx_derived_handler::init_scan"); - - TABLE *table= derived->get_first_table()->table; - ha_federatedx *h= (ha_federatedx *) table->file; - iop= &h->io; - share= get_share(table->s->table_name.str, table); - thd= table->in_use; - txn= h->get_txn(thd); - if ((rc= txn->acquire(share, thd, TRUE, iop))) - DBUG_RETURN(rc); - - if ((*iop)->query(derived->derived_spec.str, derived->derived_spec.length)) - goto err; + if (!use_pushdown) + return 0; - stored_result= (*iop)->store_result(); - if (!stored_result) - goto err; + SELECT_LEX_UNIT *unit= derived->derived; - DBUG_RETURN(0); + auto tbl= get_fed_table_for_unit_pushdown(unit); + if (!tbl) + return nullptr; -err: - DBUG_RETURN(HA_FEDERATEDX_ERROR_WITH_REMOTE_SYSTEM); + return new ha_federatedx_derived_handler(thd, derived, tbl); } -int ha_federatedx_derived_handler::next_row() -{ - int rc; - 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_derived_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); +/* + Implementation class of the derived_handler interface for FEDERATEDX: + class implementation +*/ - 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; +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; - DBUG_RETURN(rc); + query.length(0); + dt->derived->print(&query, + enum_query_type(QT_VIEW_INTERNAL | + QT_ITEM_ORIGINAL_FUNC_NULLIF | + QT_PARSABLE)); } -int ha_federatedx_derived_handler::end_scan() +ha_federatedx_derived_handler::~ha_federatedx_derived_handler() = default; + +int federatedx_handler_base::end_scan_() { DBUG_ENTER("ha_federatedx_derived_handler::end_scan"); @@ -222,89 +219,122 @@ int ha_federatedx_derived_handler::end_scan() DBUG_RETURN(0); } -void ha_federatedx_derived_handler::print_error(int, unsigned long) -{ -} - -static select_handler* -create_federatedx_select_handler(THD* thd, SELECT_LEX *sel) +/* + 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 0; + return nullptr; - ha_federatedx_select_handler* handler = NULL; + auto tbl= get_fed_table_for_pushdown(sel_lex); + if (!tbl) + return nullptr; - for (TABLE_LIST *tbl= thd->lex->query_tables; tbl; tbl= tbl->next_global) - { - if (!tbl->table) - return 0; - /* - 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 0; + if (sel_lex->uncacheable & UNCACHEABLE_SIDEEFFECT) + return NULL; - const FEDERATEDX_SHARE *fshare= - ((ha_federatedx*)tbl->table->file)->get_federatedx_share(); + return new ha_federatedx_select_handler(thd, sel_lex, lex_unit, tbl); +} - if (local_and_remote_names_mismatch(tbl->table->s, fshare)) - return 0; - } +/* + 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; - /* - Currently, ha_federatedx_select_handler::init_scan just takes the - thd->query and sends it to the backend. - This obviously won't work if the SELECT uses an "INTO @var" or - "INTO OUTFILE". It is also unlikely to work if the select has some - other kind of side effect. - */ - if (sel->uncacheable & UNCACHEABLE_SIDEEFFECT) - return NULL; + auto tbl= get_fed_table_for_unit_pushdown(sel_unit); + if (!tbl) + return nullptr; - handler= new ha_federatedx_select_handler(thd, sel); + if (sel_unit->uncacheable & UNCACHEABLE_SIDEEFFECT) + return nullptr; - return handler; + return new ha_federatedx_select_handler(thd, sel_unit, tbl); } + /* Implementation class of the select_handler interface for FEDERATEDX: class implementation */ -ha_federatedx_select_handler::ha_federatedx_select_handler(THD *thd, - SELECT_LEX *sel) - : select_handler(thd, federatedx_hton), - share(NULL), txn(NULL), iop(NULL), stored_result(NULL) +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) { - select= sel; + query.length(0); + lex_unit->print(&query, PRINT_QUERY_TYPE); } -ha_federatedx_select_handler::~ha_federatedx_select_handler() = default; +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 ha_federatedx_select_handler::init_scan() +int federatedx_handler_base::init_scan_() { + THD *thd= query_table->in_use; int rc= 0; DBUG_ENTER("ha_federatedx_select_handler::init_scan"); - TABLE *table= 0; - for (TABLE_LIST *tbl= thd->lex->query_tables; tbl; tbl= tbl->next_global) - { - if (!tbl->table) - continue; - table= tbl->table; - break; - } - ha_federatedx *h= (ha_federatedx *) table->file; + ha_federatedx *h= (ha_federatedx *) query_table->file; iop= &h->io; - share= get_share(table->s->table_name.str, table); + 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(thd->query(), thd->query_length())) + if ((*iop)->query(query.ptr(), query.length())) goto err; stored_result= (*iop)->store_result(); @@ -317,7 +347,7 @@ err: DBUG_RETURN(HA_FEDERATEDX_ERROR_WITH_REMOTE_SYSTEM); } -int ha_federatedx_select_handler::next_row() +int federatedx_handler_base::next_row_(TABLE *table) { int rc= 0; FEDERATEDX_IO_ROW *row; @@ -355,19 +385,8 @@ int ha_federatedx_select_handler::next_row() int ha_federatedx_select_handler::end_scan() { - DBUG_ENTER("ha_federatedx_derived_handler::end_scan"); - free_tmp_table(thd, table); table= 0; - (*iop)->free_result(stored_result); - - free_share(txn, share); - - DBUG_RETURN(0); -} - -void ha_federatedx_select_handler::print_error(int error, myf error_flag) -{ - select_handler::print_error(error, error_flag); + return federatedx_handler_base::end_scan_(); } diff --git a/storage/federatedx/federatedx_pushdown.h b/storage/federatedx/federatedx_pushdown.h index 673abcfc..1d5bdab1 100644 --- a/storage/federatedx/federatedx_pushdown.h +++ b/storage/federatedx/federatedx_pushdown.h @@ -14,29 +14,43 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "sql_string.h" #include "derived_handler.h" #include "select_handler.h" +class federatedx_handler_base +{ +protected: + FEDERATEDX_SHARE *share; + federatedx_txn *txn; + federatedx_io **iop; + FEDERATEDX_IO_RESULT *stored_result; + StringBuffer<512> query; + + TABLE *query_table; + + int next_row_(TABLE *tmp_tbl); + int init_scan_(); + int end_scan_(); +public: + federatedx_handler_base(THD *thd_arg, TABLE *tbl_arg); +}; + /* Implementation class of the derived_handler interface for FEDERATEDX: class declaration */ -class ha_federatedx_derived_handler: public derived_handler +class ha_federatedx_derived_handler: public derived_handler, public federatedx_handler_base { private: - FEDERATEDX_SHARE *share; - federatedx_txn *txn; - federatedx_io **iop; - FEDERATEDX_IO_RESULT *stored_result; public: - ha_federatedx_derived_handler(THD* thd_arg, TABLE_LIST *tbl); + ha_federatedx_derived_handler(THD* thd_arg, TABLE_LIST *tbl, TABLE *tbl_arg); ~ha_federatedx_derived_handler(); - int init_scan(); - int next_row(); - int end_scan(); - void print_error(int, unsigned long); + int init_scan() { return federatedx_handler_base::init_scan_(); } + int next_row() { return federatedx_handler_base::next_row_(table); } + int end_scan() { return federatedx_handler_base::end_scan_(); } }; @@ -45,19 +59,20 @@ public: class declaration */ -class ha_federatedx_select_handler: public select_handler +class ha_federatedx_select_handler: public select_handler, public federatedx_handler_base { -private: - FEDERATEDX_SHARE *share; - federatedx_txn *txn; - federatedx_io **iop; - FEDERATEDX_IO_RESULT *stored_result; - public: - ha_federatedx_select_handler(THD* thd_arg, SELECT_LEX *sel); + ha_federatedx_select_handler(THD *thd_arg, SELECT_LEX_UNIT *sel_unit, + TABLE *tbl); + ha_federatedx_select_handler(THD *thd_arg, SELECT_LEX *sel_lex, + SELECT_LEX_UNIT *sel_unit, TABLE *tbl); ~ha_federatedx_select_handler(); - int init_scan(); - int next_row(); + int init_scan() { return federatedx_handler_base::init_scan_(); } + int next_row() { return federatedx_handler_base::next_row_(table); } int end_scan(); - void print_error(int, unsigned long); + +private: + static constexpr auto PRINT_QUERY_TYPE= + enum_query_type(QT_VIEW_INTERNAL | QT_SELECT_ONLY | + QT_ITEM_ORIGINAL_FUNC_NULLIF | QT_PARSABLE); }; diff --git a/storage/federatedx/ha_federatedx.cc b/storage/federatedx/ha_federatedx.cc index 598886b8..e4c5bf8d 100644 --- a/storage/federatedx/ha_federatedx.cc +++ b/storage/federatedx/ha_federatedx.cc @@ -408,8 +408,26 @@ handlerton* federatedx_hton; static derived_handler* create_federatedx_derived_handler(THD* thd, TABLE_LIST *derived); + static select_handler* -create_federatedx_select_handler(THD* thd, SELECT_LEX *sel); +create_federatedx_select_handler(THD *thd, SELECT_LEX *sel_lex, + SELECT_LEX_UNIT *sel_unit); +static select_handler * +create_federatedx_unit_handler(THD *thd, SELECT_LEX_UNIT *sel_unit); + +/* + Federated doesn't need costs.disk_read_ratio as everything is one a remote + server and nothing is cached locally +*/ + +static void federatedx_update_optimizer_costs(OPTIMIZER_COSTS *costs) +{ + /* + Setting disk_read_ratios to 1.0, ensures we are using the costs + from rnd_pos_time() and scan_time() + */ + costs->disk_read_ratio= 0.0; +} /* Initialize the federatedx handler. @@ -443,6 +461,8 @@ int federatedx_db_init(void *p) federatedx_hton->flags= HTON_ALTER_NOT_SUPPORTED; federatedx_hton->create_derived= create_federatedx_derived_handler; federatedx_hton->create_select= create_federatedx_select_handler; + federatedx_hton->update_optimizer_costs= federatedx_update_optimizer_costs; + federatedx_hton->create_unit= create_federatedx_unit_handler; if (mysql_mutex_init(fe_key_mutex_federatedx, &federatedx_mutex, MY_MUTEX_INIT_FAST)) @@ -932,7 +952,7 @@ static bool emit_key_part_element(String *to, KEY_PART_INFO *part, *buf++= '0'; *buf++= 'x'; - buf= octet2hex(buf, (char*) ptr, len); + buf= octet2hex(buf, ptr, len); if (to->append((char*) buff, (uint)(buf - buff))) DBUG_RETURN(1); } @@ -3098,11 +3118,11 @@ int ha_federatedx::info(uint flag) if (flag & (HA_STATUS_VARIABLE | HA_STATUS_CONST)) { /* - size of IO operations (This is based on a good guess, no high science - involved) + Size of IO operations. This is used to calculate time to scan a table. + See handler.cc::keyread_time */ if (flag & HA_STATUS_CONST) - stats.block_size= 4096; + stats.block_size= 1500; // Typical size of an TCP packet if ((*iop)->table_metadata(&stats, share->table_name, (uint)share->table_name_length, flag)) @@ -3635,6 +3655,7 @@ int ha_federatedx::discover_assisted(handlerton *hton, THD* thd, MYSQL_ROW rdata; ulong *rlen; my_bool my_true= 1; + my_bool my_false= 0; if (parse_url(thd->mem_root, &tmp_share, table_s, 1)) return HA_WRONG_CREATE_OPTION; @@ -3642,6 +3663,7 @@ int ha_federatedx::discover_assisted(handlerton *hton, THD* thd, mysql_init(&mysql); mysql_options(&mysql, MYSQL_SET_CHARSET_NAME, cs->cs_name.str); mysql_options(&mysql, MYSQL_OPT_USE_THREAD_SPECIFIC_MEMORY, (char*)&my_true); + mysql_options(&mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, &my_false); if (!mysql_real_connect(&mysql, tmp_share.hostname, tmp_share.username, tmp_share.password, tmp_share.database, diff --git a/storage/federatedx/ha_federatedx.h b/storage/federatedx/ha_federatedx.h index 3573c658..cefd8859 100644 --- a/storage/federatedx/ha_federatedx.h +++ b/storage/federatedx/ha_federatedx.h @@ -222,7 +222,6 @@ public: virtual int seek_position(FEDERATEDX_IO_RESULT **io_result, const void *ref)=0; virtual void set_thd(void *thd) { } - }; @@ -365,29 +364,31 @@ public: Talk to Kostja about this - how to get the number of rows * ... disk scan time on other side (block size, size of the row) + network time ... - The reason for "records * 1000" is that such a large number forces - this to use indexes " + The reason for "1000" is that such a large number forces this to use indexes " */ - double scan_time() + IO_AND_CPU_COST scan_time() { DBUG_PRINT("info", ("records %lu", (ulong) stats.records)); - return (double)(stats.records*1000); + return + { + 0, + (double) (stats.mean_rec_length * stats.records)/8192 * DISK_READ_COST+ + 1000, + }; } - /* - The next method will never be called if you do not implement indexes. - */ - double read_time(uint index, uint ranges, ha_rows rows) + IO_AND_CPU_COST keyread_time(uint index, ulong ranges, ha_rows rows, + ulonglong blocks) + { + return {0, (double) (ranges + rows) * DISK_READ_COST }; + } + IO_AND_CPU_COST rnd_pos_time(ha_rows rows) { - /* - Per Brian, this number is bugus, but this method must be implemented, - and at a later date, he intends to document this issue for handler code - */ - return (double) rows / 20.0+1; + return {0, (double) rows * DISK_READ_COST }; } const key_map *keys_to_use_for_scanning() { return &key_map_full; } /* - Everything below are methods that we implment in ha_federatedx.cc. + Everything below are methods that we implement in ha_federatedx.cc. Most of these methods are not obligatory, skip them and MySQL will treat them as not implemented @@ -466,6 +467,7 @@ public: const FEDERATEDX_SHARE *get_federatedx_share() const { return share; } friend class ha_federatedx_derived_handler; friend class ha_federatedx_select_handler; + friend class federatedx_handler_base; }; extern const char ident_quote_char; // Character for quoting |