Skip to content

Commit dc23f97

Browse files
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()
1 parent 8868737 commit dc23f97

File tree

15 files changed

+2663
-3107
lines changed

15 files changed

+2663
-3107
lines changed

mysql-test/suite/innodb_fts/r/sync.result

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,9 @@ SET GLOBAL innodb_ft_aux_table=default;
8282
SELECT * FROM t1 WHERE MATCH(title) AGAINST('mysql database');
8383
FTS_DOC_ID title
8484
3 mysql database
85-
4 mysql database
86-
1 mysql
8785
2 database
86+
1 mysql
87+
4 mysql database
8888
connection default;
8989
disconnect con1;
9090
DROP TABLE t1;

storage/innobase/CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,6 @@ SET(INNOBASE_SOURCES
175175
fts/fts0opt.cc
176176
fts/fts0pars.cc
177177
fts/fts0que.cc
178-
fts/fts0sql.cc
179178
fts/fts0tlex.cc
180179
gis/gis0geo.cc
181180
gis/gis0rtree.cc

storage/innobase/fts/fts0config.cc

Lines changed: 90 additions & 163 deletions
Original file line numberDiff line numberDiff line change
@@ -29,98 +29,46 @@ Created 2007/5/9 Sunny Bains
2929

3030
#include "fts0priv.h"
3131

32-
/******************************************************************//**
33-
Callback function for fetching the config value.
34-
@return always returns TRUE */
35-
static
36-
ibool
37-
fts_config_fetch_value(
38-
/*===================*/
39-
void* row, /*!< in: sel_node_t* */
40-
void* user_arg) /*!< in: pointer to
41-
ib_vector_t */
42-
{
43-
sel_node_t* node = static_cast<sel_node_t*>(row);
44-
fts_string_t* value = static_cast<fts_string_t*>(user_arg);
45-
46-
dfield_t* dfield = que_node_get_val(node->select_list);
47-
dtype_t* type = dfield_get_type(dfield);
48-
ulint len = dfield_get_len(dfield);
49-
void* data = dfield_get_data(dfield);
50-
51-
ut_a(dtype_get_mtype(type) == DATA_VARCHAR);
52-
53-
if (len != UNIV_SQL_NULL) {
54-
ulint max_len = ut_min(value->f_len - 1, len);
55-
56-
memcpy(value->f_str, data, max_len);
57-
value->f_len = max_len;
58-
value->f_str[value->f_len] = '\0';
59-
}
60-
61-
return(TRUE);
62-
}
63-
64-
/******************************************************************//**
65-
Get value from the config table. The caller must ensure that enough
32+
/** Get value from the config table. The caller must ensure that enough
6633
space is allocated for value to hold the column contents.
34+
@param trx transaction
35+
@param fts_table indexed FTS table
36+
@param name get config value for this parameter name
37+
@param value Value read from config table
6738
@return DB_SUCCESS or error code */
68-
dberr_t
69-
fts_config_get_value(
70-
/*=================*/
71-
trx_t* trx, /*!< transaction */
72-
fts_table_t* fts_table, /*!< in: the indexed
73-
FTS table */
74-
const char* name, /*!< in: get config value for
75-
this parameter name */
76-
fts_string_t* value) /*!< out: value read from
77-
config table */
39+
dberr_t fts_config_get_value(trx_t *trx, fts_table_t *fts_table,
40+
const char *name, fts_string_t *value)
7841
{
79-
pars_info_t* info;
80-
que_t* graph;
81-
dberr_t error;
82-
ulint name_len = strlen(name);
83-
char table_name[MAX_FULL_NAME_LEN];
84-
85-
info = pars_info_create();
86-
87-
*value->f_str = '\0';
88-
ut_a(value->f_len > 0);
89-
90-
pars_info_bind_function(info, "my_func", fts_config_fetch_value,
91-
value);
92-
93-
/* The len field of value must be set to the max bytes that
94-
it can hold. On a successful read, the len field will be set
95-
to the actual number of bytes copied to value. */
96-
pars_info_bind_varchar_literal(info, "name", (byte*) name, name_len);
97-
98-
fts_table->suffix = "CONFIG";
99-
fts_get_table_name(fts_table, table_name);
100-
pars_info_bind_id(info, "table_name", table_name);
101-
102-
graph = fts_parse_sql(
103-
fts_table,
104-
info,
105-
"DECLARE FUNCTION my_func;\n"
106-
"DECLARE CURSOR c IS SELECT value FROM $table_name"
107-
" WHERE key = :name;\n"
108-
"BEGIN\n"
109-
""
110-
"OPEN c;\n"
111-
"WHILE 1 = 1 LOOP\n"
112-
" FETCH c INTO my_func();\n"
113-
" IF c % NOTFOUND THEN\n"
114-
" EXIT;\n"
115-
" END IF;\n"
116-
"END LOOP;\n"
117-
"CLOSE c;");
118-
119-
trx->op_info = "getting FTS config value";
120-
121-
error = fts_eval_sql(trx, graph);
122-
que_graph_free(graph);
123-
return(error);
42+
FTSQueryRunner sqlRunner(trx);
43+
fts_table->suffix = "CONFIG";
44+
dberr_t err= DB_SUCCESS;
45+
dict_table_t *table= sqlRunner.open_table(fts_table, &err);
46+
if (table)
47+
{
48+
ut_ad(UT_LIST_GET_LEN(table->indexes) == 1);
49+
err= sqlRunner.prepare_for_read(table);
50+
if (err == DB_SUCCESS)
51+
{
52+
dict_index_t *clust_index= dict_table_get_first_index(table);
53+
sqlRunner.build_tuple(clust_index, 1, 1);
54+
sqlRunner.assign_config_fields(name);
55+
56+
*value->f_str = '\0';
57+
ut_a(value->f_len > 0);
58+
59+
/* Following record executor does the following command
60+
SELECT value FROM $FTS_PREFIX_CONFIG WHERE key = :name;"
61+
and stores the value field in fts_string_t value */
62+
err= sqlRunner.record_executor(clust_index, READ, MATCH_UNIQUE,
63+
PAGE_CUR_GE, &read_fts_config, value);
64+
/* In case of empty table, error can be DB_END_OF_INDEX
65+
and key doesn't exist, error can be DB_RECORD_NOT_FOUND */
66+
if (err == DB_END_OF_INDEX || err == DB_RECORD_NOT_FOUND)
67+
err= DB_SUCCESS;
68+
}
69+
table->release();
70+
}
71+
return err;
12472
}
12573

12674
/*********************************************************************//**
@@ -181,81 +129,60 @@ fts_config_get_index_value(
181129
return(error);
182130
}
183131

184-
/******************************************************************//**
185-
Set the value in the config table for name.
186-
@return DB_SUCCESS or error code */
187-
dberr_t
188-
fts_config_set_value(
189-
/*=================*/
190-
trx_t* trx, /*!< transaction */
191-
fts_table_t* fts_table, /*!< in: the indexed
192-
FTS table */
193-
const char* name, /*!< in: get config value for
194-
this parameter name */
195-
const fts_string_t*
196-
value) /*!< in: value to update */
132+
dberr_t fts_config_set_value(trx_t *trx, fts_table_t *fts_table,
133+
const char *name, const fts_string_t *value)
197134
{
198-
pars_info_t* info;
199-
que_t* graph;
200-
dberr_t error;
201-
undo_no_t undo_no;
202-
undo_no_t n_rows_updated;
203-
ulint name_len = strlen(name);
204-
char table_name[MAX_FULL_NAME_LEN];
205-
206-
info = pars_info_create();
207-
208-
pars_info_bind_varchar_literal(info, "name", (byte*) name, name_len);
209-
pars_info_bind_varchar_literal(info, "value",
210-
value->f_str, value->f_len);
211-
212-
const bool dict_locked = fts_table->table->fts->dict_locked;
213-
214-
fts_table->suffix = "CONFIG";
215-
fts_get_table_name(fts_table, table_name, dict_locked);
216-
pars_info_bind_id(info, "table_name", table_name);
217-
218-
graph = fts_parse_sql(
219-
fts_table, info,
220-
"BEGIN UPDATE $table_name SET value = :value"
221-
" WHERE key = :name;");
222-
223-
trx->op_info = "setting FTS config value";
224-
225-
undo_no = trx->undo_no;
226-
227-
error = fts_eval_sql(trx, graph);
228-
229-
que_graph_free(graph);
230-
231-
n_rows_updated = trx->undo_no - undo_no;
232-
233-
/* Check if we need to do an insert. */
234-
if (error == DB_SUCCESS && n_rows_updated == 0) {
235-
info = pars_info_create();
236-
237-
pars_info_bind_varchar_literal(
238-
info, "name", (byte*) name, name_len);
239-
240-
pars_info_bind_varchar_literal(
241-
info, "value", value->f_str, value->f_len);
242-
243-
fts_get_table_name(fts_table, table_name, dict_locked);
244-
pars_info_bind_id(info, "table_name", table_name);
245-
246-
graph = fts_parse_sql(
247-
fts_table, info,
248-
"BEGIN\n"
249-
"INSERT INTO $table_name VALUES(:name, :value);");
250-
251-
trx->op_info = "inserting FTS config value";
252-
253-
error = fts_eval_sql(trx, graph);
254-
255-
que_graph_free(graph);
256-
}
257-
258-
return(error);
135+
dberr_t err= DB_SUCCESS;
136+
const bool dict_locked = fts_table->table->fts->dict_locked;
137+
fts_table->suffix = "CONFIG";
138+
139+
FTSQueryRunner sqlRunner(trx);
140+
dict_table_t *fts_config= sqlRunner.open_table(fts_table, &err, dict_locked);
141+
if (fts_config)
142+
{
143+
err= sqlRunner.prepare_for_write(fts_config);
144+
if (err == DB_SUCCESS)
145+
{
146+
dict_index_t *clust_index= dict_table_get_first_index(fts_config);
147+
sqlRunner.build_tuple(clust_index, 1, 1);
148+
/* We set the length of value to the max bytes it can hold. This
149+
information is used by the callback that reads the value.*/
150+
fts_string_t old_value;
151+
old_value.f_len= FTS_MAX_CONFIG_VALUE_LEN;
152+
old_value.f_str=
153+
static_cast<byte*>(ut_malloc_nokey(old_value.f_len + 1));
154+
155+
/* REPLACE INTO $FTS_PREFIX_CONFIG VALUES (KEY, VALUE) */
156+
sqlRunner.assign_config_fields(name);
157+
err= sqlRunner.record_executor(clust_index, SELECT_UPDATE,
158+
MATCH_UNIQUE, PAGE_CUR_GE,
159+
&read_fts_config, &old_value);
160+
if (err == DB_SUCCESS)
161+
{
162+
/* Record already exist. So Update the value field with new value */
163+
if (old_value.f_len != value->f_len ||
164+
memcmp(old_value.f_str, value->f_str, value->f_len) != 0)
165+
{
166+
/* Build update vector with new value for FTS_CONFIG table */
167+
sqlRunner.build_update_config(fts_config, 3, value);
168+
err= sqlRunner.update_record(
169+
fts_config, static_cast<uint16_t>(old_value.f_len),
170+
static_cast<uint16_t>(value->f_len));
171+
}
172+
}
173+
else if (err == DB_END_OF_INDEX || err == DB_RECORD_NOT_FOUND)
174+
{
175+
/* Record doesn't exist. So do insert the key, value in config table */
176+
sqlRunner.build_tuple(clust_index);
177+
sqlRunner.assign_config_fields(name, value->f_str, value->f_len);
178+
err= sqlRunner.write_record(fts_config);
179+
}
180+
ut_free(old_value.f_str);
181+
}
182+
fts_config->release();
183+
}
184+
185+
return err;
259186
}
260187

261188
/******************************************************************//**

0 commit comments

Comments
 (0)