mirror of
https://github.com/MariaDB/server.git
synced 2026-02-20 17:49:02 +01:00
- 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()
356 lines
11 KiB
C++
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);
|
|
}
|