| 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 | |