1 /*****************************************************************************
2 
3 Copyright (c) 2007, 2020, Oracle and/or its affiliates. All Rights Reserved.
4 Copyright (c) 2018, MariaDB Corporation.
5 
6 This program is free software; you can redistribute it and/or modify it under
7 the terms of the GNU General Public License as published by the Free Software
8 Foundation; version 2 of the License.
9 
10 This program is distributed in the hope that it will be useful, but WITHOUT
11 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13 
14 You should have received a copy of the GNU General Public License along with
15 this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
17 
18 *****************************************************************************/
19 
20 /**************************************************//**
21 @file fts/fts0ast.cc
22 Full Text Search parser helper file.
23 
24 Created 2007/3/16 Sunny Bains.
25 ***********************************************************************/
26 
27 #include "row0sel.h"
28 #include "fts0ast.h"
29 #include "fts0pars.h"
30 #include "fts0fts.h"
31 
32 /* The FTS ast visit pass. */
33 enum fts_ast_visit_pass_t {
34 	FTS_PASS_FIRST,		/*!< First visit pass,
35 				process operators excluding
36 				FTS_EXIST and FTS_IGNORE */
37 	FTS_PASS_EXIST,		/*!< Exist visit pass,
38 				process operator FTS_EXIST */
39 	FTS_PASS_IGNORE		/*!< Ignore visit pass,
40 				process operator FTS_IGNORE */
41 };
42 
43 /******************************************************************//**
44 Create an empty fts_ast_node_t.
45 @return Create a new node */
46 static
47 fts_ast_node_t*
fts_ast_node_create(void)48 fts_ast_node_create(void)
49 /*=====================*/
50 {
51 	fts_ast_node_t*	node;
52 
53 	node = (fts_ast_node_t*) ut_zalloc_nokey(sizeof(*node));
54 
55 	return(node);
56 }
57 
58 /** Track node allocations, in case there is an error during parsing. */
59 static
60 void
fts_ast_state_add_node(fts_ast_state_t * state,fts_ast_node_t * node)61 fts_ast_state_add_node(
62 	fts_ast_state_t*state,			/*!< in: ast instance */
63 	fts_ast_node_t*	node)			/*!< in: node to add to ast */
64 {
65 	if (!state->list.head) {
66 		ut_a(!state->list.tail);
67 
68 		state->list.head = state->list.tail = node;
69 	} else {
70 		state->list.tail->next_alloc = node;
71 		state->list.tail = node;
72 	}
73 }
74 
75 /******************************************************************//**
76 Create a operator fts_ast_node_t.
77 @return new node */
78 fts_ast_node_t*
fts_ast_create_node_oper(void * arg,fts_ast_oper_t oper)79 fts_ast_create_node_oper(
80 /*=====================*/
81 	void*		arg,			/*!< in: ast state instance */
82 	fts_ast_oper_t	oper)			/*!< in: ast operator */
83 {
84 	fts_ast_node_t*	node = fts_ast_node_create();
85 
86 	node->type = FTS_AST_OPER;
87 	node->oper = oper;
88 
89 	fts_ast_state_add_node((fts_ast_state_t*) arg, node);
90 
91 	return(node);
92 }
93 
94 /******************************************************************//**
95 This function takes ownership of the ptr and is responsible
96 for free'ing it
97 @return new node or a node list with tokenized words */
98 fts_ast_node_t*
fts_ast_create_node_term(void * arg,const fts_ast_string_t * ptr)99 fts_ast_create_node_term(
100 /*=====================*/
101 	void*			arg,		/*!< in: ast state instance */
102 	const fts_ast_string_t*	ptr)		/*!< in: ast term string */
103 {
104 	fts_ast_state_t*	state = static_cast<fts_ast_state_t*>(arg);
105 	ulint			len = ptr->len;
106 	ulint			cur_pos = 0;
107 	fts_ast_node_t*         node = NULL;
108 	fts_ast_node_t*		node_list = NULL;
109 	fts_ast_node_t*		first_node = NULL;
110 
111 	/* Scan the incoming string and filter out any "non-word" characters */
112 	while (cur_pos < len) {
113 		fts_string_t	str;
114 		ulint		cur_len;
115 
116 		cur_len = innobase_mysql_fts_get_token(
117 			state->charset,
118 			reinterpret_cast<const byte*>(ptr->str) + cur_pos,
119 			reinterpret_cast<const byte*>(ptr->str) + len, &str);
120 
121 		if (cur_len == 0) {
122 			break;
123 		}
124 
125 		cur_pos += cur_len;
126 
127 		if (str.f_n_char > 0) {
128 			/* If the subsequent term (after the first one)'s size
129 			is less than fts_min_token_size or the term is greater
130 			than fts_max_token_size, we shall ignore that. This is
131 			to make consistent with MyISAM behavior */
132 			if ((first_node && (str.f_n_char < fts_min_token_size))
133 			    || str.f_n_char > fts_max_token_size) {
134 				continue;
135 			}
136 
137 			node = fts_ast_node_create();
138 
139 			node->type = FTS_AST_TERM;
140 
141 			node->term.ptr = fts_ast_string_create(
142 						str.f_str, str.f_len);
143 
144 			fts_ast_state_add_node(
145 				static_cast<fts_ast_state_t*>(arg), node);
146 
147 			if (first_node) {
148 				/* There is more than one word, create
149 				a list to organize them */
150 				if (!node_list) {
151 					node_list = fts_ast_create_node_list(
152 						static_cast<fts_ast_state_t*>(
153 							arg),
154 						 first_node);
155 				}
156 
157 				fts_ast_add_node(node_list, node);
158 			} else {
159 				first_node = node;
160 			}
161 		}
162 	}
163 
164 	return((node_list != NULL) ? node_list : first_node);
165 }
166 
167 /******************************************************************//**
168 Create an AST term node, makes a copy of ptr for plugin parser
169 @return node */
170 fts_ast_node_t*
fts_ast_create_node_term_for_parser(void * arg,const char * ptr,const ulint len)171 fts_ast_create_node_term_for_parser(
172 /*================================*/
173 	void*		arg,		/*!< in: ast state */
174 	const char*	ptr,		/*!< in: term string */
175 	const ulint	len)		/*!< in: term string length */
176 {
177 	fts_ast_node_t*		node = NULL;
178 
179 	/* '%' as first char is forbidden for LIKE in internal SQL parser;
180 	'%' as last char is reserved for wildcard search;*/
181 	if (len == 0 || len > FTS_MAX_WORD_LEN
182 	    || ptr[0] == '%' || ptr[len - 1] == '%') {
183 		return(NULL);
184 	}
185 
186 	node = fts_ast_node_create();
187 
188 	node->type = FTS_AST_TERM;
189 
190 	node->term.ptr = fts_ast_string_create(
191 			reinterpret_cast<const byte*>(ptr), len);
192 
193 	fts_ast_state_add_node(static_cast<fts_ast_state_t*>(arg), node);
194 
195 	return(node);
196 }
197 
198 /******************************************************************//**
199 This function takes ownership of the ptr and is responsible
200 for free'ing it.
201 @return new node */
202 fts_ast_node_t*
fts_ast_create_node_text(void * arg,const fts_ast_string_t * ptr)203 fts_ast_create_node_text(
204 /*=====================*/
205 	void*			arg,	/*!< in: ast state instance */
206 	const fts_ast_string_t*	ptr)	/*!< in: ast text string */
207 {
208 	ulint		len = ptr->len;
209 	fts_ast_node_t*	node = NULL;
210 
211 	/* Once we come here, the string must have at least 2 quotes ""
212 	around the query string, which could be empty. Also the query
213 	string may contain 0x00 in it, we don't treat it as null-terminated. */
214 	ut_ad(len >= 2);
215 	ut_ad(ptr->str[0] == '\"' && ptr->str[len - 1] == '\"');
216 
217 	if (len == 2) {
218 		/* If the query string contains nothing except quotes,
219 		it's obviously an invalid query. */
220 		return(NULL);
221 	}
222 
223 	node = fts_ast_node_create();
224 
225 	/*!< We ignore the actual quotes "" */
226 	len -= 2;
227 
228 	node->type = FTS_AST_TEXT;
229 	/*!< Skip copying the first quote */
230 	node->text.ptr = fts_ast_string_create(
231 			reinterpret_cast<const byte*>(ptr->str + 1), len);
232 	node->text.distance = ULINT_UNDEFINED;
233 
234 	fts_ast_state_add_node((fts_ast_state_t*) arg, node);
235 
236 	return(node);
237 }
238 
239 /******************************************************************//**
240 Create an AST phrase list node for plugin parser
241 @return node */
242 fts_ast_node_t*
fts_ast_create_node_phrase_list(void * arg)243 fts_ast_create_node_phrase_list(
244 /*============================*/
245 	void*		arg)			/*!< in: ast state */
246 {
247 	fts_ast_node_t*		node = fts_ast_node_create();
248 
249 	node->type = FTS_AST_PARSER_PHRASE_LIST;
250 
251 	node->text.distance = ULINT_UNDEFINED;
252 	node->list.head = node->list.tail = NULL;
253 
254 	fts_ast_state_add_node(static_cast<fts_ast_state_t*>(arg), node);
255 
256 	return(node);
257 }
258 
259 /******************************************************************//**
260 This function takes ownership of the expr and is responsible
261 for free'ing it.
262 @return new node */
263 fts_ast_node_t*
fts_ast_create_node_list(void * arg,fts_ast_node_t * expr)264 fts_ast_create_node_list(
265 /*=====================*/
266 	void*		arg,			/*!< in: ast state instance */
267 	fts_ast_node_t*	expr)			/*!< in: ast expr instance */
268 {
269 	fts_ast_node_t*	node = fts_ast_node_create();
270 
271 	node->type = FTS_AST_LIST;
272 	node->list.head = node->list.tail = expr;
273 
274 	fts_ast_state_add_node((fts_ast_state_t*) arg, node);
275 
276 	return(node);
277 }
278 
279 /******************************************************************//**
280 Create a sub-expression list node. This function takes ownership of
281 expr and is responsible for deleting it.
282 @return new node */
283 fts_ast_node_t*
fts_ast_create_node_subexp_list(void * arg,fts_ast_node_t * expr)284 fts_ast_create_node_subexp_list(
285 /*============================*/
286 	void*		arg,			/*!< in: ast state instance */
287 	fts_ast_node_t*	expr)			/*!< in: ast expr instance */
288 {
289 	fts_ast_node_t*	node = fts_ast_node_create();
290 
291 	node->type = FTS_AST_SUBEXP_LIST;
292 	node->list.head = node->list.tail = expr;
293 
294 	fts_ast_state_add_node((fts_ast_state_t*) arg, node);
295 
296 	return(node);
297 }
298 
299 /******************************************************************//**
300 Free an expr list node elements. */
301 static
302 void
fts_ast_free_list(fts_ast_node_t * node)303 fts_ast_free_list(
304 /*==============*/
305 	fts_ast_node_t*	node)			/*!< in: ast node to free */
306 {
307 	ut_a(node->type == FTS_AST_LIST
308 	     || node->type == FTS_AST_SUBEXP_LIST
309 	     || node->type == FTS_AST_PARSER_PHRASE_LIST);
310 
311 	for (node = node->list.head;
312 	     node != NULL;
313 	     node = fts_ast_free_node(node)) {
314 
315 		/*!< No op */
316 	}
317 }
318 
319 /********************************************************************//**
320 Free a fts_ast_node_t instance.
321 @return next node to free */
322 fts_ast_node_t*
fts_ast_free_node(fts_ast_node_t * node)323 fts_ast_free_node(
324 /*==============*/
325 	fts_ast_node_t*	node)			/*!< in: the node to free */
326 {
327 	fts_ast_node_t*	next_node;
328 
329 	switch (node->type) {
330 	case FTS_AST_TEXT:
331 		if (node->text.ptr) {
332 			fts_ast_string_free(node->text.ptr);
333 			node->text.ptr = NULL;
334 		}
335 		break;
336 
337 	case FTS_AST_TERM:
338 		if (node->term.ptr) {
339 			fts_ast_string_free(node->term.ptr);
340 			node->term.ptr = NULL;
341 		}
342 		break;
343 
344 	case FTS_AST_LIST:
345 	case FTS_AST_SUBEXP_LIST:
346 	case FTS_AST_PARSER_PHRASE_LIST:
347 		fts_ast_free_list(node);
348 		node->list.head = node->list.tail = NULL;
349 		break;
350 
351 	case FTS_AST_OPER:
352 		break;
353 
354 	default:
355 		ut_error;
356 	}
357 
358 	/*!< Get next node before freeing the node itself */
359 	next_node = node->next;
360 
361 	ut_free(node);
362 
363 	return(next_node);
364 }
365 
366 /******************************************************************//**
367 This AST takes ownership of the expr and is responsible
368 for free'ing it.
369 @return in param "list" */
370 fts_ast_node_t*
fts_ast_add_node(fts_ast_node_t * node,fts_ast_node_t * elem)371 fts_ast_add_node(
372 /*=============*/
373 	fts_ast_node_t*	node,			/*!< in: list instance */
374 	fts_ast_node_t*	elem)			/*!< in: node to add to list */
375 {
376 	if (!elem) {
377 		return(NULL);
378 	}
379 
380 	ut_a(!elem->next);
381 	ut_a(node->type == FTS_AST_LIST
382 	     || node->type == FTS_AST_SUBEXP_LIST
383 	     || node->type == FTS_AST_PARSER_PHRASE_LIST);
384 
385 	if (!node->list.head) {
386 		ut_a(!node->list.tail);
387 
388 		node->list.head = node->list.tail = elem;
389 	} else {
390 		ut_a(node->list.tail);
391 
392 		node->list.tail->next = elem;
393 		node->list.tail = elem;
394 	}
395 
396 	return(node);
397 }
398 
399 /******************************************************************//**
400 Set the wildcard attribute of a term. */
401 void
fts_ast_term_set_wildcard(fts_ast_node_t * node)402 fts_ast_term_set_wildcard(
403 /*======================*/
404 	fts_ast_node_t*	node)			/*!< in/out: set attribute of
405 						a term node */
406 {
407 	if (!node) {
408 		return;
409 	}
410 
411 	/* If it's a node list, the wildcard should be set to the tail node*/
412 	if (node->type == FTS_AST_LIST)	{
413 		ut_ad(node->list.tail != NULL);
414 		node = node->list.tail;
415 	}
416 
417 	ut_a(node->type == FTS_AST_TERM);
418 	ut_a(!node->term.wildcard);
419 
420 	node->term.wildcard = TRUE;
421 }
422 
423 /******************************************************************//**
424 Set the proximity attribute of a text node. */
425 void
fts_ast_text_set_distance(fts_ast_node_t * node,ulint distance)426 fts_ast_text_set_distance(
427 /*======================*/
428 	fts_ast_node_t*	node,			/*!< in/out: text node */
429 	ulint		distance)		/*!< in: the text proximity
430 						distance */
431 {
432 	if (node == NULL) {
433 		return;
434 	}
435 
436 	ut_a(node->type == FTS_AST_TEXT);
437 	ut_a(node->text.distance == ULINT_UNDEFINED);
438 
439 	node->text.distance = distance;
440 }
441 
442 /******************************************************************//**
443 Free node and expr allocations. */
444 void
fts_ast_state_free(fts_ast_state_t * state)445 fts_ast_state_free(
446 /*===============*/
447 	fts_ast_state_t*state)			/*!< in: ast state to free */
448 {
449 	fts_ast_node_t*	node = state->list.head;
450 
451 	/* Free the nodes that were allocated during parsing. */
452 	while (node) {
453 		fts_ast_node_t*	next = node->next_alloc;
454 
455 		if (node->type == FTS_AST_TEXT && node->text.ptr) {
456 			fts_ast_string_free(node->text.ptr);
457 			node->text.ptr = NULL;
458 		} else if (node->type == FTS_AST_TERM && node->term.ptr) {
459 			fts_ast_string_free(node->term.ptr);
460 			node->term.ptr = NULL;
461 		}
462 
463 		ut_free(node);
464 		node = next;
465 	}
466 
467 	state->root = state->list.head = state->list.tail = NULL;
468 }
469 
470 /** Print the ast string
471 @param[in] str		string to print */
472 static
473 void
fts_ast_string_print(const fts_ast_string_t * ast_str)474 fts_ast_string_print(
475 	const fts_ast_string_t*	ast_str)
476 {
477 	for (ulint i = 0; i < ast_str->len; ++i) {
478 		printf("%c", ast_str->str[i]);
479 	}
480 
481 	printf("\n");
482 }
483 
484 /******************************************************************//**
485 Print an ast node recursively. */
486 static
487 void
fts_ast_node_print_recursive(fts_ast_node_t * node,ulint level)488 fts_ast_node_print_recursive(
489 /*=========================*/
490 	fts_ast_node_t*	node,			/*!< in: ast node to print */
491 	ulint		level)			/*!< in: recursive level */
492 {
493 	/* Print alignment blank */
494 	for (ulint i = 0; i < level; i++) {
495 		printf("  ");
496 	}
497 
498 	switch (node->type) {
499 	case FTS_AST_TEXT:
500 		printf("TEXT: ");
501 		fts_ast_string_print(node->text.ptr);
502 		break;
503 
504 	case FTS_AST_TERM:
505 		printf("TERM: ");
506 		fts_ast_string_print(node->term.ptr);
507 		break;
508 
509 	case FTS_AST_LIST:
510 		printf("LIST: \n");
511 
512 		for (node = node->list.head; node; node = node->next) {
513 			fts_ast_node_print_recursive(node, level + 1);
514 		}
515 		break;
516 
517 	case FTS_AST_SUBEXP_LIST:
518 		printf("SUBEXP_LIST: \n");
519 
520 		for (node = node->list.head; node; node = node->next) {
521 			fts_ast_node_print_recursive(node, level + 1);
522 		}
523 		break;
524 
525 	case FTS_AST_OPER:
526 		printf("OPER: %d\n", node->oper);
527 		break;
528 
529 	case FTS_AST_PARSER_PHRASE_LIST:
530 		printf("PARSER_PHRASE_LIST: \n");
531 
532 		for (node = node->list.head; node; node = node->next) {
533 			fts_ast_node_print_recursive(node, level + 1);
534 		}
535 		break;
536 
537 	default:
538 		ut_error;
539 	}
540 }
541 
542 /******************************************************************//**
543 Print an ast node */
544 void
fts_ast_node_print(fts_ast_node_t * node)545 fts_ast_node_print(
546 /*===============*/
547 	fts_ast_node_t* node)		/*!< in: ast node to print */
548 {
549 	fts_ast_node_print_recursive(node, 0);
550 }
551 
552 /** Check only union operation involved in the node
553 @param[in]	node	ast node to check
554 @return true if the node contains only union else false. */
555 bool
fts_ast_node_check_union(fts_ast_node_t * node)556 fts_ast_node_check_union(
557 	fts_ast_node_t*	node)
558 {
559 	if (node->type == FTS_AST_LIST
560 	    || node->type == FTS_AST_SUBEXP_LIST) {
561 
562 		for (node = node->list.head; node; node = node->next) {
563 			if (!fts_ast_node_check_union(node)) {
564 				return(false);
565 			}
566 		}
567 
568 	} else if (node->type == FTS_AST_PARSER_PHRASE_LIST) {
569 		/* Phrase search for plugin parser */
570 		return(false);
571 	} else if (node->type == FTS_AST_OPER
572 		   && (node->oper == FTS_IGNORE
573 		       || node->oper == FTS_EXIST)) {
574 
575 		return(false);
576 	} else if (node->type == FTS_AST_TEXT) {
577 		/* Distance or phrase search query. */
578 		return(false);
579 	}
580 
581 	return(true);
582 }
583 
584 /******************************************************************//**
585 Traverse the AST - in-order traversal, except for the FTX_EXIST and FTS_IGNORE
586 nodes, which will be ignored in the first pass of each level, and visited in a
587 second and third pass after all other nodes in the same level are visited.
588 @return DB_SUCCESS if all went well */
589 dberr_t
fts_ast_visit(fts_ast_oper_t oper,fts_ast_node_t * node,fts_ast_callback visitor,void * arg,bool * has_ignore)590 fts_ast_visit(
591 /*==========*/
592 	fts_ast_oper_t		oper,		/*!< in: current operator */
593 	fts_ast_node_t*		node,		/*!< in: current root node */
594 	fts_ast_callback	visitor,	/*!< in: callback function */
595 	void*			arg,		/*!< in: arg for callback */
596 	bool*			has_ignore)	/*!< out: true, if the operator
597 						was ignored during processing,
598 						currently we ignore FTS_EXIST
599 						and FTS_IGNORE operators */
600 {
601 	dberr_t			error = DB_SUCCESS;
602 	fts_ast_node_t*		oper_node = NULL;
603 	fts_ast_node_t*		start_node;
604 	bool			revisit = false;
605 	bool			will_be_ignored = false;
606 	fts_ast_visit_pass_t	visit_pass = FTS_PASS_FIRST;
607 	const trx_t*		trx = node->trx;
608 
609 	start_node = node->list.head;
610 
611 	ut_a(node->type == FTS_AST_LIST
612 	     || node->type == FTS_AST_SUBEXP_LIST);
613 
614 	if (oper == FTS_EXIST_SKIP) {
615 		visit_pass = FTS_PASS_EXIST;
616 	} else if (oper == FTS_IGNORE_SKIP) {
617 		visit_pass = FTS_PASS_IGNORE;
618 	}
619 
620 	/* In the first pass of the tree, at the leaf level of the
621 	tree, FTS_EXIST and FTS_IGNORE operation will be ignored.
622 	It will be repeated at the level above the leaf level.
623 
624 	The basic idea here is that when we encounter FTS_EXIST or
625 	FTS_IGNORE, we will change the operator node into FTS_EXIST_SKIP
626 	or FTS_IGNORE_SKIP, and term node & text node with the operators
627 	is ignored in the first pass. We have two passes during the revisit:
628 	We process nodes with FTS_EXIST_SKIP in the exist pass, and then
629 	process nodes with FTS_IGNORE_SKIP in the ignore pass.
630 
631 	The order should be restrictly followed, or we will get wrong results.
632 	For example, we have a query 'a +b -c d +e -f'.
633 	first pass: process 'a' and 'd' by union;
634 	exist pass: process '+b' and '+e' by intersection;
635 	ignore pass: process '-c' and '-f' by difference. */
636 
637 	for (node = node->list.head;
638 	     node && (error == DB_SUCCESS);
639 	     node = node->next) {
640 
641 		switch (node->type) {
642 		case FTS_AST_LIST:
643 			if (visit_pass != FTS_PASS_FIRST) {
644 				break;
645 			}
646 
647 			error = fts_ast_visit(oper, node, visitor,
648 					      arg, &will_be_ignored);
649 
650 			/* If will_be_ignored is set to true, then
651 			we encountered and ignored a FTS_EXIST or FTS_IGNORE
652 			operator. */
653 			if (will_be_ignored) {
654 				revisit = true;
655 				/* Remember oper for list in case '-abc&def',
656 				ignored oper is from previous node of list.*/
657 				node->oper = oper;
658 			}
659 
660 			break;
661 
662 		case FTS_AST_OPER:
663 			oper = node->oper;
664 			oper_node = node;
665 
666 			/* Change the operator for revisit */
667 			if (oper == FTS_EXIST) {
668 				oper_node->oper = FTS_EXIST_SKIP;
669 			} else if (oper == FTS_IGNORE) {
670 				oper_node->oper = FTS_IGNORE_SKIP;
671 			}
672 
673 			break;
674 
675 		default:
676 			if (node->visited) {
677 				continue;
678 			}
679 
680 			ut_a(oper == FTS_NONE || !oper_node
681 			     || oper_node->oper == oper
682 			     || oper_node->oper == FTS_EXIST_SKIP
683 			     || oper_node->oper == FTS_IGNORE_SKIP);
684 
685 			if (oper== FTS_EXIST || oper == FTS_IGNORE) {
686 				*has_ignore = true;
687 				continue;
688 			}
689 
690 			/* Process leaf node accroding to its pass.*/
691 			if (oper == FTS_EXIST_SKIP
692 			    && visit_pass == FTS_PASS_EXIST) {
693 				error = visitor(FTS_EXIST, node, arg);
694 				node->visited = true;
695 			} else if (oper == FTS_IGNORE_SKIP
696 				   && visit_pass == FTS_PASS_IGNORE) {
697 				error = visitor(FTS_IGNORE, node, arg);
698 				node->visited = true;
699 			} else if (visit_pass == FTS_PASS_FIRST) {
700 				error = visitor(oper, node, arg);
701 				node->visited = true;
702 			}
703 		}
704 	}
705 
706 	if (trx_is_interrupted(trx)) {
707 		return DB_INTERRUPTED;
708 	}
709 
710 	if (revisit) {
711 		/* Exist pass processes the skipped FTS_EXIST operation. */
712                 for (node = start_node;
713 		     node && error == DB_SUCCESS;
714 		     node = node->next) {
715 
716 			if (node->type == FTS_AST_LIST
717 			    && node->oper != FTS_IGNORE) {
718 				error = fts_ast_visit(FTS_EXIST_SKIP, node,
719 					visitor, arg, &will_be_ignored);
720 			}
721 		}
722 
723 		/* Ignore pass processes the skipped FTS_IGNORE operation. */
724 		for (node = start_node;
725 		     node && error == DB_SUCCESS;
726 		     node = node->next) {
727 
728 			if (node->type == FTS_AST_LIST) {
729 				error = fts_ast_visit(FTS_IGNORE_SKIP, node,
730 					visitor, arg, &will_be_ignored);
731 			}
732 		}
733 	}
734 
735 	return(error);
736 }
737 
738 /**
739 Create an ast string object, with NUL-terminator, so the string
740 has one more byte than len
741 @param[in] str		pointer to string
742 @param[in] len		length of the string
743 @return ast string with NUL-terminator */
744 fts_ast_string_t*
fts_ast_string_create(const byte * str,ulint len)745 fts_ast_string_create(
746 	const byte*	str,
747 	ulint		len)
748 {
749 	fts_ast_string_t*	ast_str;
750 
751 	ut_ad(len > 0);
752 
753 	ast_str = static_cast<fts_ast_string_t*>(
754 		ut_malloc_nokey(sizeof(fts_ast_string_t)));
755 
756 	ast_str->str = static_cast<byte*>(ut_malloc_nokey(len + 1));
757 
758 	ast_str->len = len;
759 	memcpy(ast_str->str, str, len);
760 	ast_str->str[len] = '\0';
761 
762 	return(ast_str);
763 }
764 
765 /**
766 Free an ast string instance
767 @param[in,out] ast_str		string to free */
768 void
fts_ast_string_free(fts_ast_string_t * ast_str)769 fts_ast_string_free(
770 	fts_ast_string_t*	ast_str)
771 {
772 	if (ast_str != NULL) {
773 		ut_free(ast_str->str);
774 		ut_free(ast_str);
775 	}
776 }
777 
778 /**
779 Translate ast string of type FTS_AST_NUMB to unsigned long by strtoul
780 @param[in] str		string to translate
781 @param[in] base		the base
782 @return translated number */
783 ulint
fts_ast_string_to_ul(const fts_ast_string_t * ast_str,int base)784 fts_ast_string_to_ul(
785 	const fts_ast_string_t*	ast_str,
786 	int			base)
787 {
788 	return(strtoul(reinterpret_cast<const char*>(ast_str->str),
789 		       NULL, base));
790 }
791 
792 #ifdef UNIV_DEBUG
793 const char*
fts_ast_node_type_get(fts_ast_type_t type)794 fts_ast_node_type_get(fts_ast_type_t	type)
795 {
796 	switch (type) {
797 	case FTS_AST_OPER:
798 		return("FTS_AST_OPER");
799 	case FTS_AST_NUMB:
800 		return("FTS_AST_NUMB");
801 	case FTS_AST_TERM:
802 		return("FTS_AST_TERM");
803 	case FTS_AST_TEXT:
804 		return("FTS_AST_TEXT");
805 	case FTS_AST_LIST:
806 		return("FTS_AST_LIST");
807 	case FTS_AST_SUBEXP_LIST:
808 		return("FTS_AST_SUBEXP_LIST");
809 	case FTS_AST_PARSER_PHRASE_LIST:
810 		return("FTS_AST_PARSER_PHRASE_LIST");
811 	}
812 	ut_ad(0);
813 	return("FTS_UNKNOWN");
814 }
815 #endif /* UNIV_DEBUG */
816