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