1/*****************************************************************************
2
3Copyright (c) 2007, 2016, Oracle and/or its affiliates. All Rights Reserved.
4
5This program is free software; you can redistribute it and/or modify it under
6the terms of the GNU General Public License as published by the Free Software
7Foundation; version 2 of the License.
8
9This program is distributed in the hope that it will be useful, but WITHOUT
10ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12
13You should have received a copy of the GNU General Public License along with
14this program; if not, write to the Free Software Foundation, Inc.,
1551 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
16
17*****************************************************************************/
18
19/**************************************************//**
20@file fts/fts0sql.cc
21Full Text Search functionality.
22
23Created 2007-03-27 Sunny Bains
24*******************************************************/
25
26#include "que0que.h"
27#include "trx0roll.h"
28#include "pars0pars.h"
29#include "dict0dict.h"
30#include "fts0types.h"
31#include "fts0priv.h"
32
33/** SQL statements for creating the ancillary FTS tables. */
34
35/** Preamble to all SQL statements. */
36static const char* fts_sql_begin=
37 "PROCEDURE P() IS\n";
38
39/** Postamble to non-committing SQL statements. */
40static const char* fts_sql_end=
41 "\n"
42 "END;\n";
43
44/******************************************************************//**
45Get the table id.
46@return number of bytes written */
47int
48fts_get_table_id(
49/*=============*/
50 const fts_table_t*
51 fts_table, /*!< in: FTS Auxiliary table */
52 char* table_id) /*!< out: table id, must be at least
53 FTS_AUX_MIN_TABLE_ID_LENGTH bytes
54 long */
55{
56 int len;
57 bool hex_name = DICT_TF2_FLAG_IS_SET(fts_table->table,
58 DICT_TF2_FTS_AUX_HEX_NAME);
59
60 ut_a(fts_table->table != NULL);
61
62 switch (fts_table->type) {
63 case FTS_COMMON_TABLE:
64 len = fts_write_object_id(fts_table->table_id, table_id,
65 hex_name);
66 break;
67
68 case FTS_INDEX_TABLE:
69
70 len = fts_write_object_id(fts_table->table_id, table_id,
71 hex_name);
72
73 table_id[len] = '_';
74 ++len;
75 table_id += len;
76
77 len += fts_write_object_id(fts_table->index_id, table_id,
78 hex_name);
79 break;
80
81 default:
82 ut_error;
83 }
84
85 ut_a(len >= 16);
86 ut_a(len < FTS_AUX_MIN_TABLE_ID_LENGTH);
87
88 return(len);
89}
90
91/******************************************************************//**
92Construct the prefix name of an FTS table.
93@return own: table name, must be freed with ut_free() */
94char*
95fts_get_table_name_prefix(
96/*======================*/
97 const fts_table_t*
98 fts_table) /*!< in: Auxiliary table type */
99{
100 int len;
101 const char* slash;
102 char* prefix_name;
103 int dbname_len = 0;
104 int prefix_name_len;
105 char table_id[FTS_AUX_MIN_TABLE_ID_LENGTH];
106
107 slash = static_cast<const char*>(
108 memchr(fts_table->parent, '/', strlen(fts_table->parent)));
109
110 if (slash) {
111 /* Print up to and including the separator. */
112 dbname_len = static_cast<int>(slash - fts_table->parent) + 1;
113 }
114
115 len = fts_get_table_id(fts_table, table_id);
116
117 prefix_name_len = dbname_len + 4 + len + 1;
118
119 prefix_name = static_cast<char*>(
120 ut_malloc_nokey(unsigned(prefix_name_len)));
121
122 len = sprintf(prefix_name, "%.*sFTS_%s",
123 dbname_len, fts_table->parent, table_id);
124
125 ut_a(len > 0);
126 ut_a(len == prefix_name_len - 1);
127
128 return(prefix_name);
129}
130
131/******************************************************************//**
132Construct the name of an ancillary FTS table for the given table.
133Caller must allocate enough memory(usually size of MAX_FULL_NAME_LEN)
134for param 'table_name'. */
135void
136fts_get_table_name(
137/*===============*/
138 const fts_table_t* fts_table,
139 /*!< in: Auxiliary table type */
140 char* table_name)
141 /*!< in/out: aux table name */
142{
143 int len;
144 char* prefix_name;
145
146 prefix_name = fts_get_table_name_prefix(fts_table);
147
148 len = sprintf(table_name, "%s_%s", prefix_name, fts_table->suffix);
149
150 ut_a(len > 0);
151 ut_a(strlen(prefix_name) + 1 + strlen(fts_table->suffix)
152 == static_cast<uint>(len));
153
154 ut_free(prefix_name);
155}
156
157/******************************************************************//**
158Parse an SQL string.
159@return query graph */
160que_t*
161fts_parse_sql(
162/*==========*/
163 fts_table_t* fts_table, /*!< in: FTS auxiliarry table info */
164 pars_info_t* info, /*!< in: info struct, or NULL */
165 const char* sql) /*!< in: SQL string to evaluate */
166{
167 char* str;
168 que_t* graph;
169 ibool dict_locked;
170
171 str = ut_str3cat(fts_sql_begin, sql, fts_sql_end);
172
173 dict_locked = (fts_table && fts_table->table->fts
174 && (fts_table->table->fts->fts_status
175 & TABLE_DICT_LOCKED));
176
177 if (!dict_locked) {
178 ut_ad(!mutex_own(&dict_sys->mutex));
179
180 /* The InnoDB SQL parser is not re-entrant. */
181 mutex_enter(&dict_sys->mutex);
182 }
183
184 graph = pars_sql(info, str);
185 ut_a(graph);
186
187 if (!dict_locked) {
188 mutex_exit(&dict_sys->mutex);
189 }
190
191 ut_free(str);
192
193 return(graph);
194}
195
196/******************************************************************//**
197Parse an SQL string.
198@return query graph */
199que_t*
200fts_parse_sql_no_dict_lock(
201/*=======================*/
202 pars_info_t* info, /*!< in: info struct, or NULL */
203 const char* sql) /*!< in: SQL string to evaluate */
204{
205 char* str;
206 que_t* graph;
207
208 ut_ad(mutex_own(&dict_sys->mutex));
209
210 str = ut_str3cat(fts_sql_begin, sql, fts_sql_end);
211
212 //fprintf(stderr, "%s\n", str);
213
214 graph = pars_sql(info, str);
215 ut_a(graph);
216
217 ut_free(str);
218
219 return(graph);
220}
221
222/******************************************************************//**
223Evaluate an SQL query graph.
224@return DB_SUCCESS or error code */
225dberr_t
226fts_eval_sql(
227/*=========*/
228 trx_t* trx, /*!< in: transaction */
229 que_t* graph) /*!< in: Query graph to evaluate */
230{
231 que_thr_t* thr;
232
233 graph->trx = trx;
234 graph->fork_type = QUE_FORK_MYSQL_INTERFACE;
235
236 ut_a(thr = que_fork_start_command(graph));
237
238 que_run_threads(thr);
239
240 return(trx->error_state);
241}
242
243/******************************************************************//**
244Construct the column specification part of the SQL string for selecting the
245indexed FTS columns for the given table. Adds the necessary bound
246ids to the given 'info' and returns the SQL string. Examples:
247
248One indexed column named "text":
249
250 "$sel0",
251 info/ids: sel0 -> "text"
252
253Two indexed columns named "subject" and "content":
254
255 "$sel0, $sel1",
256 info/ids: sel0 -> "subject", sel1 -> "content",
257@return heap-allocated WHERE string */
258const char*
259fts_get_select_columns_str(
260/*=======================*/
261 dict_index_t* index, /*!< in: index */
262 pars_info_t* info, /*!< in/out: parser info */
263 mem_heap_t* heap) /*!< in: memory heap */
264{
265 ulint i;
266 const char* str = "";
267
268 for (i = 0; i < index->n_user_defined_cols; i++) {
269 char* sel_str;
270
271 dict_field_t* field = dict_index_get_nth_field(index, i);
272
273 sel_str = mem_heap_printf(heap, "sel%lu", (ulong) i);
274
275 /* Set copy_name to TRUE since it's dynamic. */
276 pars_info_bind_id(info, TRUE, sel_str, field->name);
277
278 str = mem_heap_printf(
279 heap, "%s%s$%s", str, (*str) ? ", " : "", sel_str);
280 }
281
282 return(str);
283}
284
285/******************************************************************//**
286Commit a transaction.
287@return DB_SUCCESS or error code */
288dberr_t
289fts_sql_commit(
290/*===========*/
291 trx_t* trx) /*!< in: transaction */
292{
293 dberr_t error;
294
295 error = trx_commit_for_mysql(trx);
296
297 /* Commit should always succeed */
298 ut_a(error == DB_SUCCESS);
299
300 return(DB_SUCCESS);
301}
302
303/******************************************************************//**
304Rollback a transaction.
305@return DB_SUCCESS or error code */
306dberr_t
307fts_sql_rollback(
308/*=============*/
309 trx_t* trx) /*!< in: transaction */
310{
311 return(trx_rollback_to_savepoint(trx, NULL));
312}
313