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