mariadb/storage/videx/ha_videx.cc
haibo 068a095750 MDEV-36737: Research and Estimation for Adapting VIDEX to MariaDB
VIDEX is a Disaggregated and Extensible Virtual Index Engine designed
to perform efficient and accurate what-if analysis for tasks such as
index recommendation.

Fix template linking error for gcc

debian: Add packaging for the VIDEX plugin

This commit adds the necessary files to build `mariadb-plugin-videx` as a separate Debian package.

- Add `COMPONENT videx-engine` to CMakeLists.txt to register the plugin.
- Define the `mariadb-plugin-videx` package in debian/control.
- Create `debian/mariadb-plugin-videx.install` to include the plugin .so and .cnf files.

debian: fix indent in debian/control

fix bugs from  empty table

videx: fix RPM autobake by adding CPACK summary/description
2025-12-22 10:21:29 +11:00

932 lines
28 KiB
C++

/* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License, version 2.0,
as published by the Free Software Foundation.
This program is also distributed with certain software (including
but not limited to OpenSSL) that is licensed under separate terms,
as designated in a particular file or component or in included license
documentation. The authors of MySQL hereby grant you an additional
permission to link the program and your derivative works with the
separately licensed software that they have included with MySQL.
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, version 2.0, 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 */
#include "my_global.h" /* ulonglong */
#include "thr_lock.h" /* THR_LOCK, THR_LOCK_DATA */
#include "handler.h" /* handler */
#include "my_base.h" /* ha_rows */
#include "table.h"
#include <sql_acl.h>
#include <sql_class.h>
#include <my_sys.h>
#include "scope.h"
#include "videx_utils.h"
#include <replication.h>
#include <curl/curl.h>
/** Shared state used by all open VIDEX handlers. */
class videx_share : public Handler_share
{
public:
mysql_mutex_t mutex;
THR_LOCK lock;
videx_share();
~videx_share()
{
thr_lock_delete(&lock);
mysql_mutex_destroy(&mutex);
}
};
/** Storage engine class. */
class ha_videx : public handler
{
THR_LOCK_DATA lock;
videx_share *share;
videx_share *get_share();
public:
ha_videx(handlerton *hton, TABLE_SHARE *table_arg);
~ha_videx() override;
const char *table_type() const override;
Table_flags table_flags() const override;
ulong index_flags(uint idx, uint part, bool all_parts) const override;
uint max_supported_keys() const override;
uint max_supported_key_length() const override;
uint max_supported_key_part_length() const override;
void column_bitmaps_signal() override;
int open(const char *name, int mode, uint test_if_locked) override;
int close(void) override;
handler *clone(const char *name, MEM_ROOT *mem_root) override;
int rnd_init(bool scan) override;
int rnd_next(uchar *buf) override;
int rnd_pos(uchar *buf, uchar *pos) override;
void position(const uchar *record) override;
int info(uint) override;
THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to,
thr_lock_type lock_type) override;
ha_rows records_in_range(uint inx, const key_range *min_key,
const key_range *max_key,
page_range *pages) override;
int create(const char *name, TABLE *table_arg,
HA_CREATE_INFO *create_info) override;
int delete_table(const char *name) override;
int rename_table(const char *from, const char *to) override;
int multi_range_read_init(RANGE_SEQ_IF *seq, void *seq_init_param,
uint n_ranges, uint mode,
HANDLER_BUFFER *buf) override;
int multi_range_read_next(range_id_t *range_info) override;
ha_rows multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq,
void *seq_init_param, uint n_ranges,
uint *bufsz, uint *flags, ha_rows limit,
Cost_estimate *cost) override;
ha_rows multi_range_read_info(uint keyno, uint n_ranges, uint keys,
uint key_parts, uint *bufsz, uint *flags,
Cost_estimate *cost) override;
int multi_range_read_explain_info(uint mrr_mode, char *str,
size_t size) override;
Item *idx_cond_push(uint keyno, Item *idx_cond) override;
int info_low(uint flag, bool is_analyze);
/** The multi range read session object */
DsMrr_impl m_ds_mrr;
/** Flags that specificy the handler instance (table) capability. */
Table_flags m_int_table_flags;
/** Index into the server's primary key meta-data table->key_info{} */
uint m_primary_key;
/** this is set to 1 when we are starting a table scan but have
not yet fetched any row, else false */
bool m_start_of_scan;
};
static MYSQL_THDVAR_STR(server_ip, PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC,
"VIDEX server address (host:port)", nullptr, nullptr,
"127.0.0.1:5001");
static MYSQL_THDVAR_STR(options, PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC,
"VIDEX connection options (JSON format)", nullptr,
nullptr, "{}");
static struct st_mysql_sys_var *videx_system_variables[]= {
MYSQL_SYSVAR(server_ip),
MYSQL_SYSVAR(options), NULL};
/**
* Write callback function for cURL.
*
* @param contents Pointer to the received data.
* @param size Size of each data element.
* @param nmemb Number of data elements.
* @param outString Pointer to the string where the data will be appended.
* @return The total size of the data processed.
*/
size_t write_callback(void *contents, size_t size, size_t nmemb,
std::string *outString)
{
size_t totalSize= size * nmemb;
outString->append((char *) contents, totalSize);
return totalSize;
}
/**
* Sends a request to the Videx HTTP server and validates the response.
* If the response is successful (code=200), the passed-in &request will be
* filled.
*
* @param request The VidexJsonItem object containing the request data.
* @param res_json The VidexStringMap object to store the response data.
* @param thd Pointer to the current thread's THD object.
* @return 0 if the request is successful, 1 otherwise.
*/
int ask_from_videx_http(VidexJsonItem &request, VidexStringMap &res_json,
THD *thd)
{
const char *host_ip= THDVAR(thd, server_ip);
if (!host_ip)
{
return 1;
}
const char *videx_options= THDVAR(thd, options);
if (!videx_options)
{
return 1;
}
DBUG_PRINT("info", ("VIDEX OPTIONS: %s IP: %s", videx_options, host_ip));
request.add_property("videx_options", videx_options);
std::string url= std::string("http://") + host_ip + "/ask_videx";
CURL *curl;
CURLcode res_code;
std::string readBuffer;
curl= curl_easy_init();
if (curl)
{
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_POST, 1);
std::string request_str= request.to_json();
DBUG_PRINT("info", ("request_str: %s", request_str.c_str()));
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request_str.c_str());
// Set the headers
struct curl_slist *headers= NULL;
headers= curl_slist_append(headers, "Content-Type: application/json");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
// Disallow connection reuse, so libcurl will close the connection
// immediately after completing a request.
curl_easy_setopt(curl, CURLOPT_FORBID_REUSE, 1L);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
res_code= curl_easy_perform(curl);
if (res_code != CURLE_OK)
{
sql_print_warning(
"VIDEX: access videx_server failed res_code != curle_ok: %s",
host_ip);
return 1;
}
else
{
int code;
std::string message;
int error=
videx_parse_simple_json(readBuffer.c_str(), code, message, res_json);
if (error)
{
sql_print_warning("VIDEX: JSON parse error: %s", message.c_str());
return 1;
}
else
{
if (message == "OK")
{
DBUG_PRINT("info", ("access videx_server success: %s", host_ip));
return 0;
}
else
{
sql_print_warning(
"VIDEX: access videx_server success but msg != OK: %s",
readBuffer.c_str());
return 1;
}
}
}
}
sql_print_warning("VIDEX: access videx_server failed curl = false: %s",
host_ip);
return 1;
}
/**
* Sends a request to the Videx HTTP server and aims to return an integer
* value. If the response is successful (code=200), it extracts the integer
* value from `response_json["value"]`.
*
* @param request The VidexJsonItem object containing the request data.
* @param result_str Reference to the string where the result value will be
* stored.
* @param thd Pointer to the current thread's THD object.
* @return 0 if the request is successful, 1 otherwise.
*/
int ask_from_videx_http(VidexJsonItem &request, std::string &result_str,
THD *thd)
{
VidexJsonItem result_item;
VidexStringMap res_json_string_map;
int error= ask_from_videx_http(request, res_json_string_map, thd);
if (error)
{
return error;
}
else if (videx_contains_key(res_json_string_map, "value"))
{
result_str= res_json_string_map["value"];
return error;
}
else
{
// HTTP request returned successfully, but the result does not contain a
// "value" field. This indicates an invalid format. Set the error_code to 1
// and return.
return 1;
}
}
static handler *videx_create_handler(handlerton *hton, TABLE_SHARE *table,
MEM_ROOT *mem_root);
handlerton *videx_hton;
static const char *ha_videx_exts[]= {NullS};
#ifdef HAVE_PSI_INTERFACE
static PSI_mutex_key ex_key_mutex_videx_share_mutex;
static PSI_mutex_info all_videx_mutexes[]= {
{&ex_key_mutex_videx_share_mutex, "videx_share::mutex", 0}};
static void init_videx_psi_keys()
{
const char *category= "videx";
int count;
count= array_elements(all_videx_mutexes);
mysql_mutex_register(category, all_videx_mutexes, count);
}
#else
static void init_videx_psi_keys() {}
#endif
videx_share::videx_share()
{
thr_lock_init(&lock);
mysql_mutex_init(ex_key_mutex_videx_share_mutex, &mutex, MY_MUTEX_INIT_FAST);
}
static void videx_update_optimizer_costs(OPTIMIZER_COSTS *costs)
{
/*
* The following values were taken from MariaDB Server 11.8 in the function
* innobase_update_optimizer_costs(OPTIMIZER_COSTS *costs). See more details
* in
* https://github.com/MariaDB/server/blob/11.8/storage/innobase/handler/ha_innodb.cc
*/
costs->row_next_find_cost= 0.00007013;
costs->row_lookup_cost= 0.00076597;
costs->key_next_find_cost= 0.00009900;
costs->key_lookup_cost= 0.00079112;
costs->row_copy_cost= 0.00006087;
}
static int videx_init(void *p)
{
DBUG_ENTER("videx_init");
init_videx_psi_keys();
videx_hton= static_cast<handlerton *>(p);
videx_hton->create= videx_create_handler;
videx_hton->flags= HTON_SUPPORTS_EXTENDED_KEYS | HTON_SUPPORTS_FOREIGN_KEYS |
HTON_NATIVE_SYS_VERSIONING | HTON_WSREP_REPLICATION |
HTON_REQUIRES_CLOSE_AFTER_TRUNCATE |
HTON_TRUNCATE_REQUIRES_EXCLUSIVE_USE |
HTON_REQUIRES_NOTIFY_TABLEDEF_CHANGED_AFTER_COMMIT;
videx_hton->update_optimizer_costs= videx_update_optimizer_costs;
videx_hton->tablefile_extensions= ha_videx_exts;
DBUG_RETURN(0);
}
videx_share *ha_videx::get_share()
{
videx_share *tmp_share;
DBUG_ENTER("ha_videx::get_share()");
lock_shared_ha_data();
if (!(tmp_share= static_cast<videx_share *>(get_ha_share_ptr())))
{
tmp_share= new videx_share;
if (!tmp_share)
goto err;
set_ha_share_ptr(static_cast<Handler_share *>(tmp_share));
}
err:
unlock_shared_ha_data();
DBUG_RETURN(tmp_share);
}
static handler *videx_create_handler(handlerton *hton, TABLE_SHARE *table,
MEM_ROOT *mem_root)
{
return new (mem_root) ha_videx(hton, table);
}
ha_videx::ha_videx(handlerton *hton, TABLE_SHARE *table_arg)
: handler(hton, table_arg),
m_int_table_flags(
HA_REC_NOT_IN_SEQ | HA_NULL_IN_KEY | HA_CAN_VIRTUAL_COLUMNS |
HA_CAN_INDEX_BLOBS | HA_CAN_SQL_HANDLER |
HA_REQUIRES_KEY_COLUMNS_FOR_DELETE |
HA_PRIMARY_KEY_REQUIRED_FOR_POSITION | HA_PRIMARY_KEY_IN_READ_INDEX |
HA_BINLOG_ROW_CAPABLE | HA_CAN_GEOMETRY | HA_PARTIAL_COLUMN_READ |
HA_TABLE_SCAN_ON_INDEX | HA_CAN_EXPORT | HA_ONLINE_ANALYZE |
HA_CAN_RTREEKEYS | HA_CAN_TABLES_WITHOUT_ROLLBACK |
HA_CAN_ONLINE_BACKUPS | HA_CONCURRENT_OPTIMIZE | HA_CAN_SKIP_LOCKED),
m_start_of_scan()
{
}
ha_videx::~ha_videx()= default;
const char *ha_videx::table_type() const { return "VIDEX"; }
handler::Table_flags ha_videx::table_flags() const
{
THD *thd= ha_thd();
handler::Table_flags flags= m_int_table_flags;
if (thd_sql_command(thd) == SQLCOM_CREATE_TABLE)
{
flags|= HA_REQUIRE_PRIMARY_KEY;
}
if (thd_tx_isolation(thd) <= ISO_READ_COMMITTED)
{
return (flags);
}
return (flags | HA_BINLOG_STMT_CAPABLE);
}
ulong ha_videx::index_flags(uint key, uint, bool) const
{
if (table_share->key_info[key].algorithm == HA_KEY_ALG_FULLTEXT)
{
return (0);
}
/* For spatial index, we don't support descending scan
and ICP so far. */
if (table_share->key_info[key].algorithm == HA_KEY_ALG_RTREE)
{
return HA_READ_NEXT | HA_READ_ORDER | HA_READ_RANGE | HA_KEYREAD_ONLY |
HA_KEY_SCAN_NOT_ROR;
}
ulong flags= key == table_share->primary_key
? HA_CLUSTERED_INDEX
: HA_KEYREAD_ONLY | HA_DO_RANGE_FILTER_PUSHDOWN;
flags|= HA_READ_NEXT | HA_READ_PREV | HA_READ_ORDER | HA_READ_RANGE |
HA_DO_INDEX_COND_PUSHDOWN;
return (flags);
}
uint ha_videx::max_supported_keys() const { return (MAX_KEY); }
uint ha_videx::max_supported_key_length() const
{
/*
* This value was taken from MariaDB Server 11.8 in the function
* innobase_max_supported_key_length() See more details in
* https://github.com/MariaDB/server/blob/11.8/storage/innobase/handler/ha_innodb.cc
*/
return (3500);
}
uint ha_videx::max_supported_key_part_length() const
{
/*
* This value was taken from MariaDB Server 11.8 in the function
* innobase_max_supported_key_part_length() See more details in
* https://github.com/MariaDB/server/blob/11.8/storage/innobase/handler/ha_innodb.cc
*/
return (3072);
}
void ha_videx::column_bitmaps_signal()
{
DBUG_ENTER("ha_videx::column_bitmaps_signal");
// TODO: handle indexed virtual columns for VIDEX engine
DBUG_VOID_RETURN;
}
/**
@brief
Used for opening tables. The name will be the name of the file.
@details
A table is opened when it needs to be opened; e.g. when a request comes
in for a SELECT on the table (tables are not open and closed for each
request, they are cached).
Called from handler.cc by handler::ha_open(). The server opens all
tables by calling ha_open() which then calls the handler specific open().
@see
handler::ha_open() in handler.cc
*/
int ha_videx::open(const char *name, int mode, uint test_if_locked)
{
DBUG_ENTER("ha_videx::open");
if (!(share= get_share()))
DBUG_RETURN(1);
thr_lock_data_init(&share->lock, &lock, NULL);
m_primary_key= table->s->primary_key;
if (m_primary_key >= MAX_KEY)
{
ref_length= 6; // DATA_ROW_ID_LEN;
}
else
{
ref_length= table->key_info[m_primary_key].key_length;
}
/*
* This value was taken from MariaDB Server 11.8 in the function open(),
* where stats.block_size is set to srv_page_size. See more details in
* https://github.com/MariaDB/server/blob/11.8/storage/innobase/handler/ha_innodb.cc
*/
stats.block_size= 16384;
info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST |
HA_STATUS_OPEN);
DBUG_RETURN(0);
}
/**
@brief
Closes a table.
@details
Called from sql_base.cc, sql_select.cc, and table.cc. In sql_select.cc
it is only used to close up temporary tables or during the process where a
temporary table is converted over to being a myisam table.
For sql_base.cc look at close_data_tables().
@see
sql_base.cc, sql_select.cc and table.cc
*/
int ha_videx::close(void)
{
DBUG_ENTER("ha_videx::close");
DBUG_RETURN(0);
}
handler *ha_videx::clone(const char *name, /*!< in: table name */
MEM_ROOT *mem_root) /*!< in: memory context */
{
DBUG_ENTER("ha_videx::clone");
DBUG_RETURN(NULL);
}
/**
rnd_init() is called when the system wants the storage engine to do a table
scan. Not required for VIDEX.
*/
int ha_videx::rnd_init(bool scan)
{
DBUG_ENTER("ha_videx::rnd_init");
DBUG_RETURN(0);
}
/**
This is called for each row of the table scan. Not required for VIDEX.
*/
int ha_videx::rnd_next(uchar *buf)
{
int rc;
DBUG_ENTER("ha_videx::rnd_next");
rc= HA_ERR_END_OF_FILE;
DBUG_RETURN(rc);
}
/**
This is like rnd_next, but you are given a position to use
to determine the row. Not required for VIDEX.
*/
int ha_videx::rnd_pos(uchar *buf, uchar *pos)
{
int rc;
DBUG_ENTER("ha_videx::rnd_pos");
rc= HA_ERR_WRONG_COMMAND;
DBUG_RETURN(rc);
}
/**
position() is called after each call to rnd_next() if the data needs
to be ordered. Not required for VIDEX.
*/
void ha_videx::position(const uchar *record)
{
DBUG_ENTER("ha_videx::position");
DBUG_VOID_RETURN;
}
/**
Returns table statistics to the server; fills fields in the handler object.
@return 0 on success or HA_ERR_*.
*/
int ha_videx::info(uint flag) { return (info_low(flag, false)); }
/**
Converts a MySQL table lock in 'lock' to the engine representation.
Not required for VIDEX.
@return pointer to the current element in 'to'.
*/
THR_LOCK_DATA **ha_videx::store_lock(THD *thd, THR_LOCK_DATA **to,
enum thr_lock_type lock_type)
{
if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK)
lock.type= lock_type;
*to++= &lock;
return to;
}
/**
* Estimates the number of index records in a specific range via VIDEX HTTP.
* Working principle: VIDEX will forward requests via HTTP to the external
* VIDEX-Statistic-Server (launched in a RESTful manner). If the request fails,
* a default value of 10 will be returned. Reference link: For an
* implementation of VIDEX-Statistic-Server, see
* https://github.com/bytedance/videx. We plan to introduce it into the
* official MariaDB repository in a subsequent PR.
* @param keynr: The index number of the key
* @param min_key: The minimum key value of the range
* @param max_key: The maximum key value of the range
* @param pages: Page range information (present as a parameter but not used in
* the function)
* @return estimated number of rows.
*/
ha_rows ha_videx::records_in_range(uint keynr, const key_range *min_key,
const key_range *max_key, page_range *pages)
{
DBUG_ENTER("ha_videx::records_in_range");
KEY *key;
active_index= keynr;
key= table->key_info + active_index;
VidexJsonItem request_item= construct_request(
table->s->db.str, table->s->table_name.str, __PRETTY_FUNCTION__);
serializeKeyRangeToJson(min_key, max_key, key, &request_item);
std::string n_rows_str;
ha_rows n_rows;
THD *thd= ha_thd();
int error= ask_from_videx_http(request_item, n_rows_str, thd);
if (error)
{
n_rows= 10; // default number to force index
}
else
{
n_rows= std::stoull(n_rows_str);
}
// Avoid returning 0 to the optimizer, similar to InnoDB's behavior
if (n_rows == 0)
{
n_rows= 1;
}
DBUG_RETURN(n_rows);
}
/**
Create a new table to an VIDEX database. Not required for VIDEX. */
int ha_videx::create(const char *name, TABLE *table_arg,
HA_CREATE_INFO *create_info)
{
DBUG_ENTER("ha_videx::create");
DBUG_PRINT("info", ("name: %s, table_arg: %p, create_info: %p", name,
table_arg, create_info));
DBUG_RETURN(0);
}
int ha_videx::delete_table(const char *name)
{
DBUG_ENTER("ha_videx::delete_table");
DBUG_RETURN(0);
}
int ha_videx::rename_table(const char *from, const char *to)
{
DBUG_ENTER("ha_videx::rename_table");
DBUG_RETURN(0);
}
/**
DS-MRR implementation.
*/
int ha_videx::multi_range_read_init(RANGE_SEQ_IF *seq, void *seq_init_param,
uint n_ranges, uint mode,
HANDLER_BUFFER *buf)
{
return (m_ds_mrr.dsmrr_init(this, seq, seq_init_param, n_ranges, mode, buf));
}
int ha_videx::multi_range_read_next(range_id_t *range_info)
{
return (m_ds_mrr.dsmrr_next(range_info));
}
ha_rows ha_videx::multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq,
void *seq_init_param,
uint n_ranges, uint *bufsz,
uint *flags, ha_rows limit,
Cost_estimate *cost)
{
m_ds_mrr.init(this, table);
return (m_ds_mrr.dsmrr_info_const(keyno, seq, seq_init_param, n_ranges,
bufsz, flags, limit, cost));
}
ha_rows ha_videx::multi_range_read_info(uint keyno, uint n_ranges, uint keys,
uint key_parts, uint *bufsz,
uint *flags, Cost_estimate *cost)
{
m_ds_mrr.init(this, table);
return (m_ds_mrr.dsmrr_info(keyno, n_ranges, keys, key_parts, bufsz, flags,
cost));
}
int ha_videx::multi_range_read_explain_info(uint mrr_mode, char *str,
size_t size)
{
return (m_ds_mrr.dsmrr_explain_info(mrr_mode, str, size));
}
/**
* Attempt to push down an index condition.
* @param keyno MySQL key number
* @param idx_cond Index condition to be checked
* @return Part of idx_cond which the handler will not evaluate
*/
class Item *ha_videx::idx_cond_push(uint keyno, class Item *idx_cond)
{
DBUG_ENTER("ha_videx::idx_cond_push");
DBUG_ASSERT(keyno != MAX_KEY);
DBUG_ASSERT(idx_cond != NULL);
pushed_idx_cond= idx_cond;
pushed_idx_cond_keyno= keyno;
in_range_check_pushed_down= TRUE;
DBUG_RETURN(NULL);
}
/**
* A very important function. When a query arrives, MariaDB calls this function
* to initialize the information of a table. In a session, this function is
* only called once by the MariaDB query optimizer. VIDEX requests the
* `videx_stats_server` to return various statistics of a single table,
* including: stat_n_rows: The number of rows in the table.
* stat_clustered_index_size: The size of the clustered index.
* stat_sum_of_other_index_sizes: The sum of sizes of other indexes.
* data_file_length: The size of the data file.
* index_file_length: The length of the index file.
* data_free_length: The length of free space in the data file.
*
* [Very Important]
* rec_per_key of several columns: require NDV algorithm
*
* Returns statistics information of the table to the MariaDB interpreter, in
* various fields of the handle object.
* @param[in] flag what information is requested
* @param[in] is_analyze True if called from "::analyze()".
* @return HA_ERR_* error code or 0
*/
int ha_videx::info_low(uint flag, bool is_analyze)
{
uint64_t n_rows;
DBUG_ENTER("ha_videx::info_low");
DEBUG_SYNC_C("ha_videx_info_low");
// construct request
VidexStringMap res_json;
VidexJsonItem request_item= construct_request(
table->s->db.str, table->s->table_name.str, __PRETTY_FUNCTION__);
for (uint i= 0; i < table->s->keys; i++)
{
KEY *key= &table->key_info[i];
VidexJsonItem *keyItem= request_item.create("key");
keyItem->add_property("name", key->name.str);
keyItem->add_property_nonan("key_length", key->key_length);
for (ulong j= 0; j < key->usable_key_parts; j++)
{
if ((key->flags & HA_KEY_ALG_FULLTEXT) ||
(key->flags & HA_SPATIAL_legacy))
{
continue;
}
VidexJsonItem *field= keyItem->create("field");
field->add_property("name", key->key_part[j].field->field_name.str);
field->add_property_nonan("store_length", key->key_part[j].store_length);
}
}
DBUG_PRINT("info", ("Request JSON: %s", request_item.to_json().c_str()));
THD *thd= ha_thd();
int error= ask_from_videx_http(request_item, res_json, thd);
if (error)
{
DBUG_RETURN(0);
}
else
{
// validate the returned json
// stat_n_rows, stat_clustered_index_size, stat_sum_of_other_index_sizes,
// data_file_length, index_file_length, data_free_length
if (!(videx_contains_key(res_json, "stat_n_rows") &&
videx_contains_key(res_json, "stat_clustered_index_size") &&
videx_contains_key(res_json, "stat_sum_of_other_index_sizes") &&
videx_contains_key(res_json, "data_file_length") &&
videx_contains_key(res_json, "index_file_length") &&
videx_contains_key(res_json, "data_free_length")))
{
sql_print_warning("VIDEX: res_json data error=0 but miss some key.");
DBUG_RETURN(0);
}
}
if (flag & HA_STATUS_VARIABLE)
{
n_rows= std::stoull(res_json["stat_n_rows"]);
if (n_rows == 0 && !(flag & (HA_STATUS_TIME | HA_STATUS_OPEN)))
{
n_rows++;
}
stats.records= (ha_rows) n_rows;
stats.deleted= 0;
stats.data_file_length= std::stoull(res_json["data_file_length"]);
stats.index_file_length= std::stoull(res_json["index_file_length"]);
if (flag & HA_STATUS_VARIABLE_EXTRA)
{
stats.delete_length= std::stoull(res_json["data_free_length"]);
}
stats.check_time= 0;
stats.mrr_length_per_rec=
(uint) ref_length + 8; // 8 = max(sizeof(void *));
if (stats.records == 0)
{
stats.mean_rec_length= 0;
}
else
{
stats.mean_rec_length= (ulong) (stats.data_file_length / stats.records);
}
}
if (flag & HA_STATUS_CONST)
{
for (uint i= 0; i < table->s->keys; i++)
{
ulong j;
KEY *key= &table->key_info[i];
for (j= 0; j < key->ext_key_parts; j++)
{
if ((key->algorithm == HA_KEY_ALG_FULLTEXT) ||
(key->algorithm == HA_KEY_ALG_RTREE))
{
continue;
}
// The #@# separator combines the index name and field name, making it
// easier to extract the corresponding statistical values from the JSON
// response.
std::string concat_key=
"rec_per_key #@# " + std::string(key->name.str) + " #@# " +
std::string(key->key_part[j].field->field_name.str);
ulong rec_per_key_int= 0;
if (videx_contains_key(res_json, concat_key.c_str()))
{
rec_per_key_int= std::stoul(res_json[concat_key.c_str()]);
}
else
{
rec_per_key_int= stats.records;
}
if (rec_per_key_int == 0)
{
rec_per_key_int= 1;
}
key->rec_per_key[j]= rec_per_key_int;
}
}
}
DBUG_RETURN(0);
}
struct st_mysql_storage_engine videx_storage_engine= {
MYSQL_HANDLERTON_INTERFACE_VERSION};
maria_declare_plugin(videx){
MYSQL_STORAGE_ENGINE_PLUGIN,
&videx_storage_engine,
"VIDEX",
"Rong Kang, Haibo Yang",
"Disaggregated, Extensible Virtual Index Engine for What-If Analysis",
PLUGIN_LICENSE_GPL,
videx_init, /* Plugin Init */
NULL, /* Plugin Deinit */
0x0001, /* version number (0.1) */
NULL, /* status variables */
videx_system_variables, /* system variables */
"0.1", /* string version */
MariaDB_PLUGIN_MATURITY_EXPERIMENTAL /* maturity */
} maria_declare_plugin_end;