mariadb/storage/innobase/fts/fts0config.cc
Thirunarayanan Balathandayuthapani dfa7fe347f MDEV-28730 Remove internal parser usage from InnoDB fulltext
- Introduce a class FTSQueryRunner to handle queries for fulltext
internal tables. Basically it creates a query, prepares the
fulltext internal table for read or write process. Build a tuple
based on the given table and assign the FTS_CONFIG, FTS_COMMON_TABLES
and FTS_AUX_TABLE fields based on the given value. This class
also handles INSERT, DELETE, REPLACE, UPDATE and
execute the function for each record (record_executor).

FTSQueryRunner::create_query_thread(): Create a query thread to execute
the statement on internal FULLTEXT tables

FTSQueryRunner::build_tuple(): Build a tuple for the operation

FTSQueryRunner::build_clust_ref(): Build a clustered index reference
for clustered index lookup for the secondary index record

FTSQueryRunner::assign_config_fields(): Assign the tuple for the
FTS CONFIG internal table

FTSQueryRunner::assign_common_table_fields(): Assign the tuple for
FTS_DELETED, FTS_DELETED_CACHE, FTS_BEGIN_DELETED,
FTS_BEGIN_DELETED_CACHE common tables

FTSQueryRunner::assign_aux_table_fields(): Assign the tuple for
FTS_PREFIX_INDEX tables.

FTSQueryRunner::handle_error(): Handling error for DB_LOCK_WAIT,
retry the operation

FTSQueryRunner::open_table(): Open the table based on the fulltext
auxiliary table name and FTS common table name

FTSQueryRunner::prepare_for_write(): Lock the table for write
process by taking Intention Exclusive lock

FTSQueryRunner::prepare_for_read(): Lock the table for read
process by taking Intention Shared lock

FTSQueryRunner::write_record(): Insert the tuple into the given table

FTSQueryRunner::lock_or_sees_rec(): Lock the record in case of
DELETE, SELECT_UPDATE operation. Fetch the correct version of record in
case of READ operation. It also does clustered index lookup in case
of search is on secondary index

fts_cmp_rec_dtuple_prefix(): Compare the record with given tuple field
for tuple field length

FTSQueryRunner::record_executor(): Read the record of the given index and
do call the callback function for each record

FTSQueryRunner::build_update_config(): Build the update vector for
FULLTEXT CONFIG table

FTSQueryRunner::update_record(): Update the record with update vector
exist in FTSQueryRunner

Removed the fts_parse_sql(), fts_eval_sql(), fts_get_select_columns_str()
and fts_get_docs_clear().

Moved fts_get_table_id() & fts_get_table_name() from fts0sql.cc to
fts0fts.cc and deleted the file fts0sql.cc

Removed ins_graph, sel_graph from fts_index_cache_t

Changed the callback function default read function parameter for
each clustered index record to

bool fts_sql_callback(dict_index_t*, const rec_t *, const rec_offs*,
                      void *);

Following parameters are changed to default read function parameter:

fts_read_stopword()
fts_fetch_store_doc_id()
fts_query_expansion_fetch_doc()
fts_read_count()
fts_get_rows_count()
fts_init_doc_id()
fts_init_recover_doc()
read_fts_config()
fts_optimize_read_node()
fts_optimize_index_fetch_node()
fts_index_fetch_nodes()
fts_fetch_index_words()
fts_index_fetch_words()
fts_fetch_doc_ids()
fts_table_fetch_doc_ids()
fts_read_ulint()
fts_copy_doc_ids()
fts_optimize_create_deleted_doc_id_snapshot()
fts_query_index_fetch_nodes()
fts_query_fetch_document()
fts_query_index_fetch_nodes()

row_upd_clust_rec_low(): Function does updates a clustered
index record of a row when ordering fields don't change.
Function doesn't have dependency on row_prebuilt_t. This can be
used by fulltext internal table update operation

Row_sel_get_clust_rec_for_mysql::operator(): Removed the
parameter row_prebuilt_t and caller does pass the prebuilt
related variables

Removed the parser usage and execute the query directly on
fulltext internal tables in the following function:

fts_read_stopword()
fts_fetch_store_doc_id()
fts_query_expansion_fetch_doc()
fts_read_count()
fts_get_rows_count()
fts_init_doc_id()
fts_init_recover_doc()
read_fts_config()
fts_optimize_read_node()
fts_optimize_index_fetch_node()
fts_index_fetch_nodes()
fts_fetch_index_words()
fts_index_fetch_words()
fts_fetch_doc_ids()
fts_table_fetch_doc_ids()
fts_read_ulint()
fts_copy_doc_ids()
fts_optimize_create_deleted_doc_id_snapshot()
fts_query_index_fetch_nodes()
fts_query_fetch_document()
fts_query_index_fetch_nodes()
i_s_fts_deleted_generic_fill()
i_s_fts_index_table_fill_selected()
2025-05-26 16:35:18 +05:30

356 lines
11 KiB
C++

/*****************************************************************************
Copyright (c) 2007, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, 2021, 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 Street, Fifth Floor, Boston, MA 02110-1335 USA
*****************************************************************************/
/******************************************************************//**
@file fts/fts0config.cc
Full Text Search configuration table.
Created 2007/5/9 Sunny Bains
***********************************************************************/
#include "trx0roll.h"
#include "row0sel.h"
#include "fts0priv.h"
/** Get value from the config table. The caller must ensure that enough
space is allocated for value to hold the column contents.
@param trx transaction
@param fts_table indexed FTS table
@param name get config value for this parameter name
@param value Value read from config table
@return DB_SUCCESS or error code */
dberr_t fts_config_get_value(trx_t *trx, fts_table_t *fts_table,
const char *name, fts_string_t *value)
{
FTSQueryRunner sqlRunner(trx);
fts_table->suffix = "CONFIG";
dberr_t err= DB_SUCCESS;
dict_table_t *table= sqlRunner.open_table(fts_table, &err);
if (table)
{
ut_ad(UT_LIST_GET_LEN(table->indexes) == 1);
err= sqlRunner.prepare_for_read(table);
if (err == DB_SUCCESS)
{
dict_index_t *clust_index= dict_table_get_first_index(table);
sqlRunner.build_tuple(clust_index, 1, 1);
sqlRunner.assign_config_fields(name);
*value->f_str = '\0';
ut_a(value->f_len > 0);
/* Following record executor does the following command
SELECT value FROM $FTS_PREFIX_CONFIG WHERE key = :name;"
and stores the value field in fts_string_t value */
err= sqlRunner.record_executor(clust_index, READ, MATCH_UNIQUE,
PAGE_CUR_GE, &read_fts_config, value);
/* In case of empty table, error can be DB_END_OF_INDEX
and key doesn't exist, error can be DB_RECORD_NOT_FOUND */
if (err == DB_END_OF_INDEX || err == DB_RECORD_NOT_FOUND)
err= DB_SUCCESS;
}
table->release();
}
return err;
}
/*********************************************************************//**
Create the config table name for retrieving index specific value.
@return index config parameter name */
char*
fts_config_create_index_param_name(
/*===============================*/
const char* param, /*!< in: base name of param */
const dict_index_t* index) /*!< in: index for config */
{
ulint len;
char* name;
/* The format of the config name is: name_<index_id>. */
len = strlen(param);
/* Caller is responsible for deleting name. */
name = static_cast<char*>(ut_malloc_nokey(
len + FTS_AUX_MIN_TABLE_ID_LENGTH + 2));
::strcpy(name, param);
name[len] = '_';
fts_write_object_id(index->id, name + len + 1);
return(name);
}
/******************************************************************//**
Get value specific to an FTS index from the config table. The caller
must ensure that enough space is allocated for value to hold the
column contents.
@return DB_SUCCESS or error code */
dberr_t
fts_config_get_index_value(
/*=======================*/
trx_t* trx, /*!< transaction */
dict_index_t* index, /*!< in: index */
const char* param, /*!< in: get config value for
this parameter name */
fts_string_t* value) /*!< out: value read from
config table */
{
char* name;
dberr_t error;
fts_table_t fts_table;
FTS_INIT_FTS_TABLE(&fts_table, "CONFIG", FTS_COMMON_TABLE,
index->table);
/* We are responsible for free'ing name. */
name = fts_config_create_index_param_name(param, index);
error = fts_config_get_value(trx, &fts_table, name, value);
ut_free(name);
return(error);
}
dberr_t fts_config_set_value(trx_t *trx, fts_table_t *fts_table,
const char *name, const fts_string_t *value)
{
dberr_t err= DB_SUCCESS;
const bool dict_locked = fts_table->table->fts->dict_locked;
fts_table->suffix = "CONFIG";
FTSQueryRunner sqlRunner(trx);
dict_table_t *fts_config= sqlRunner.open_table(fts_table, &err, dict_locked);
if (fts_config)
{
err= sqlRunner.prepare_for_write(fts_config);
if (err == DB_SUCCESS)
{
ut_ad(UT_LIST_GET_LEN(fts_config->indexes) == 1);
dict_index_t *clust_index= dict_table_get_first_index(fts_config);
sqlRunner.build_tuple(clust_index, 1, 1);
/* We set the length of value to the max bytes it can hold. This
information is used by the callback that reads the value.*/
fts_string_t old_value;
old_value.f_len= FTS_MAX_CONFIG_VALUE_LEN;
old_value.f_str=
static_cast<byte*>(ut_malloc_nokey(old_value.f_len + 1));
/* REPLACE INTO $FTS_PREFIX_CONFIG VALUES (KEY, VALUE) */
sqlRunner.assign_config_fields(name);
err= sqlRunner.record_executor(clust_index, SELECT_UPDATE,
MATCH_UNIQUE, PAGE_CUR_GE,
&read_fts_config, &old_value);
if (err == DB_SUCCESS)
{
/* Record already exist. So Update the value field with new value */
if (old_value.f_len != value->f_len ||
memcmp(old_value.f_str, value->f_str, value->f_len) != 0)
{
/* Build update vector with new value for FTS_CONFIG table */
sqlRunner.build_update_config(fts_config, 3, value);
err= sqlRunner.update_record(
fts_config, static_cast<uint16_t>(old_value.f_len),
static_cast<uint16_t>(value->f_len));
}
}
else if (err == DB_END_OF_INDEX || err == DB_RECORD_NOT_FOUND)
{
/* Record doesn't exist. So do insert the key, value in config table */
sqlRunner.build_tuple(clust_index);
sqlRunner.assign_config_fields(name, value->f_str, value->f_len);
err= sqlRunner.write_record(fts_config);
}
ut_free(old_value.f_str);
}
fts_config->release();
}
return err;
}
/******************************************************************//**
Set the value specific to an FTS index in the config table.
@return DB_SUCCESS or error code */
dberr_t
fts_config_set_index_value(
/*=======================*/
trx_t* trx, /*!< transaction */
dict_index_t* index, /*!< in: index */
const char* param, /*!< in: get config value for
this parameter name */
fts_string_t* value) /*!< out: value read from
config table */
{
char* name;
dberr_t error;
fts_table_t fts_table;
FTS_INIT_FTS_TABLE(&fts_table, "CONFIG", FTS_COMMON_TABLE,
index->table);
/* We are responsible for free'ing name. */
name = fts_config_create_index_param_name(param, index);
error = fts_config_set_value(trx, &fts_table, name, value);
ut_free(name);
return(error);
}
#ifdef FTS_OPTIMIZE_DEBUG
/******************************************************************//**
Get an ulint value from the config table.
@return DB_SUCCESS if all OK else error code */
dberr_t
fts_config_get_index_ulint(
/*=======================*/
trx_t* trx, /*!< in: transaction */
dict_index_t* index, /*!< in: FTS index */
const char* name, /*!< in: param name */
ulint* int_value) /*!< out: value */
{
dberr_t error;
fts_string_t value;
/* We set the length of value to the max bytes it can hold. This
information is used by the callback that reads the value.*/
value.f_len = FTS_MAX_CONFIG_VALUE_LEN;
value.f_str = static_cast<byte*>(ut_malloc_nokey(value.f_len + 1));
error = fts_config_get_index_value(trx, index, name, &value);
if (UNIV_UNLIKELY(error != DB_SUCCESS)) {
ib::error() << "(" << error << ") reading `" << name << "'";
} else {
*int_value = strtoul((char*) value.f_str, NULL, 10);
}
ut_free(value.f_str);
return(error);
}
/******************************************************************//**
Set an ulint value in the config table.
@return DB_SUCCESS if all OK else error code */
dberr_t
fts_config_set_index_ulint(
/*=======================*/
trx_t* trx, /*!< in: transaction */
dict_index_t* index, /*!< in: FTS index */
const char* name, /*!< in: param name */
ulint int_value) /*!< in: value */
{
dberr_t error;
fts_string_t value;
/* We set the length of value to the max bytes it can hold. This
information is used by the callback that reads the value.*/
value.f_len = FTS_MAX_CONFIG_VALUE_LEN;
value.f_str = static_cast<byte*>(ut_malloc_nokey(value.f_len + 1));
// FIXME: Get rid of snprintf
ut_a(FTS_MAX_INT_LEN < FTS_MAX_CONFIG_VALUE_LEN);
value.f_len = snprintf(
(char*) value.f_str, FTS_MAX_INT_LEN, ULINTPF, int_value);
error = fts_config_set_index_value(trx, index, name, &value);
if (UNIV_UNLIKELY(error != DB_SUCCESS)) {
ib::error() << "(" << error << ") writing `" << name << "'";
}
ut_free(value.f_str);
return(error);
}
#endif /* FTS_OPTIMIZE_DEBUG */
/******************************************************************//**
Get an ulint value from the config table.
@return DB_SUCCESS if all OK else error code */
dberr_t
fts_config_get_ulint(
/*=================*/
trx_t* trx, /*!< in: transaction */
fts_table_t* fts_table, /*!< in: the indexed
FTS table */
const char* name, /*!< in: param name */
ulint* int_value) /*!< out: value */
{
dberr_t error;
fts_string_t value;
/* We set the length of value to the max bytes it can hold. This
information is used by the callback that reads the value.*/
value.f_len = FTS_MAX_CONFIG_VALUE_LEN;
value.f_str = static_cast<byte*>(ut_malloc_nokey(value.f_len + 1));
error = fts_config_get_value(trx, fts_table, name, &value);
if (UNIV_UNLIKELY(error != DB_SUCCESS)) {
ib::error() << "(" << error << ") reading `" << name << "'";
} else {
*int_value = strtoul((char*) value.f_str, NULL, 10);
}
ut_free(value.f_str);
return(error);
}
/******************************************************************//**
Set an ulint value in the config table.
@return DB_SUCCESS if all OK else error code */
dberr_t
fts_config_set_ulint(
/*=================*/
trx_t* trx, /*!< in: transaction */
fts_table_t* fts_table, /*!< in: the indexed
FTS table */
const char* name, /*!< in: param name */
ulint int_value) /*!< in: value */
{
dberr_t error;
fts_string_t value;
/* We set the length of value to the max bytes it can hold. This
information is used by the callback that reads the value.*/
value.f_len = FTS_MAX_CONFIG_VALUE_LEN;
value.f_str = static_cast<byte*>(ut_malloc_nokey(value.f_len + 1));
ut_a(FTS_MAX_INT_LEN < FTS_MAX_CONFIG_VALUE_LEN);
value.f_len = (ulint) snprintf(
(char*) value.f_str, FTS_MAX_INT_LEN, ULINTPF, int_value);
error = fts_config_set_value(trx, fts_table, name, &value);
if (UNIV_UNLIKELY(error != DB_SUCCESS)) {
ib::error() << "(" << error << ") writing `" << name << "'";
}
ut_free(value.f_str);
return(error);
}