1 | /***************************************************************************** |
2 | |
3 | Copyright (c) 2007, 2016, Oracle and/or its affiliates. All Rights Reserved. |
4 | |
5 | This program is free software; you can redistribute it and/or modify it under |
6 | the terms of the GNU General Public License as published by the Free Software |
7 | Foundation; version 2 of the License. |
8 | |
9 | This program is distributed in the hope that it will be useful, but WITHOUT |
10 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
11 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. |
12 | |
13 | You should have received a copy of the GNU General Public License along with |
14 | this program; if not, write to the Free Software Foundation, Inc., |
15 | 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA |
16 | |
17 | *****************************************************************************/ |
18 | |
19 | /**************************************************//** |
20 | @file fts/fts0sql.cc |
21 | Full Text Search functionality. |
22 | |
23 | Created 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. */ |
36 | static const char* fts_sql_begin= |
37 | "PROCEDURE P() IS\n" ; |
38 | |
39 | /** Postamble to non-committing SQL statements. */ |
40 | static const char* fts_sql_end= |
41 | "\n" |
42 | "END;\n" ; |
43 | |
44 | /******************************************************************//** |
45 | Get the table id. |
46 | @return number of bytes written */ |
47 | int |
48 | fts_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 | /******************************************************************//** |
92 | Construct the prefix name of an FTS table. |
93 | @return own: table name, must be freed with ut_free() */ |
94 | char* |
95 | fts_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 | /******************************************************************//** |
132 | Construct the name of an ancillary FTS table for the given table. |
133 | Caller must allocate enough memory(usually size of MAX_FULL_NAME_LEN) |
134 | for param 'table_name'. */ |
135 | void |
136 | fts_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 | /******************************************************************//** |
158 | Parse an SQL string. |
159 | @return query graph */ |
160 | que_t* |
161 | fts_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 | /******************************************************************//** |
197 | Parse an SQL string. |
198 | @return query graph */ |
199 | que_t* |
200 | fts_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 | /******************************************************************//** |
223 | Evaluate an SQL query graph. |
224 | @return DB_SUCCESS or error code */ |
225 | dberr_t |
226 | fts_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 | /******************************************************************//** |
244 | Construct the column specification part of the SQL string for selecting the |
245 | indexed FTS columns for the given table. Adds the necessary bound |
246 | ids to the given 'info' and returns the SQL string. Examples: |
247 | |
248 | One indexed column named "text": |
249 | |
250 | "$sel0", |
251 | info/ids: sel0 -> "text" |
252 | |
253 | Two indexed columns named "subject" and "content": |
254 | |
255 | "$sel0, $sel1", |
256 | info/ids: sel0 -> "subject", sel1 -> "content", |
257 | @return heap-allocated WHERE string */ |
258 | const char* |
259 | fts_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 | /******************************************************************//** |
286 | Commit a transaction. |
287 | @return DB_SUCCESS or error code */ |
288 | dberr_t |
289 | fts_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 | /******************************************************************//** |
304 | Rollback a transaction. |
305 | @return DB_SUCCESS or error code */ |
306 | dberr_t |
307 | fts_sql_rollback( |
308 | /*=============*/ |
309 | trx_t* trx) /*!< in: transaction */ |
310 | { |
311 | return(trx_rollback_to_savepoint(trx, NULL)); |
312 | } |
313 | |