1/* Copyright (c) 2005, 2016, Oracle and/or its affiliates.
2 Copyright (c) 2009, 2017, MariaDB
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; version 2 of the License.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
16
17#include "mariadb.h"
18#include "sql_priv.h"
19/*
20 It is necessary to include set_var.h instead of item.h because there
21 are dependencies on include order for set_var.h and item.h. This
22 will be resolved later.
23*/
24#include "sql_class.h" // set_var.h: THD
25#include "set_var.h"
26#include "my_xml.h"
27#include "sp_pcontext.h"
28#include "sql_class.h" // THD
29
30/*
31 TODO: future development directions:
32 1. add real constants for XPATH_NODESET_CMP and XPATH_NODESET
33 into enum Type in item.h.
34 2. add nodeset_to_nodeset_comparator
35 3. add lacking functions:
36 - name()
37 - lang()
38 - string()
39 - id()
40 - translate()
41 - local-name()
42 - starts-with()
43 - namespace-uri()
44 - substring-after()
45 - normalize-space()
46 - substring-before()
47 4. add lacking axis:
48 - following-sibling
49 - following,
50 - preceding-sibling
51 - preceding
52*/
53
54
55/* Structure to store a parsed XML tree */
56typedef struct my_xml_node_st
57{
58 uint level; /* level in XML tree, 0 means root node */
59 enum my_xml_node_type type; /* node type: node, or attribute, or text */
60 uint parent; /* link to the parent */
61 const char *beg; /* beginning of the name or text */
62 const char *end; /* end of the name or text */
63 const char *tagend; /* where this tag ends */
64} MY_XML_NODE;
65
66
67/* Lexical analizer token */
68typedef struct my_xpath_lex_st
69{
70 int term; /* token type, see MY_XPATH_LEX_XXXXX below */
71 const char *beg; /* beginnign of the token */
72 const char *end; /* end of the token */
73} MY_XPATH_LEX;
74
75
76/* Structure to store nodesets */
77typedef struct my_xpath_flt_st
78{
79 uint num; /* absolute position in MY_XML_NODE array */
80 uint pos; /* relative position in context */
81 uint size; /* context size */
82} MY_XPATH_FLT;
83
84
85/* XPath function creator */
86typedef struct my_xpath_function_names_st
87{
88 const char *name; /* function name */
89 size_t length; /* function name length */
90 size_t minargs; /* min number of arguments */
91 size_t maxargs; /* max number of arguments */
92 Item *(*create)(struct my_xpath_st *xpath, Item **args, uint nargs);
93} MY_XPATH_FUNC;
94
95
96/* XPath query parser */
97typedef struct my_xpath_st
98{
99 THD *thd;
100 int debug;
101 MY_XPATH_LEX query; /* Whole query */
102 MY_XPATH_LEX lasttok; /* last scanned token */
103 MY_XPATH_LEX prevtok; /* previous scanned token */
104 int axis; /* last scanned axis */
105 int extra; /* last scanned "extra", context dependent */
106 MY_XPATH_FUNC *func; /* last scanned function creator */
107 Item *item; /* current expression */
108 Item *context; /* last scanned context */
109 Item *rootelement; /* The root element */
110 String *context_cache; /* last context provider */
111 String *pxml; /* Parsed XML, an array of MY_XML_NODE */
112 CHARSET_INFO *cs; /* character set/collation string comparison */
113 int error;
114} MY_XPATH;
115
116
117/* Dynamic array of MY_XPATH_FLT */
118class XPathFilter :public String
119{
120public:
121 XPathFilter() :String() {}
122 inline bool append_element(MY_XPATH_FLT *flt)
123 {
124 String *str= this;
125 return str->append((const char*)flt, (uint32) sizeof(MY_XPATH_FLT));
126 }
127 inline bool append_element(uint32 num, uint32 pos)
128 {
129 MY_XPATH_FLT add;
130 add.num= num;
131 add.pos= pos;
132 add.size= 0;
133 return append_element(&add);
134 }
135 inline bool append_element(uint32 num, uint32 pos, uint32 size)
136 {
137 MY_XPATH_FLT add;
138 add.num= num;
139 add.pos= pos;
140 add.size= size;
141 return append_element(&add);
142 }
143 inline MY_XPATH_FLT *element(uint i)
144 {
145 return (MY_XPATH_FLT*) (ptr() + i * sizeof(MY_XPATH_FLT));
146 }
147 inline uint32 numelements()
148 {
149 return length() / sizeof(MY_XPATH_FLT);
150 }
151};
152
153
154/*
155 Common features of the functions returning a node set.
156*/
157class Item_nodeset_func :public Item_str_func
158{
159protected:
160 String tmp_value, tmp2_value;
161 MY_XPATH_FLT *fltbeg, *fltend;
162 MY_XML_NODE *nodebeg, *nodeend;
163 uint numnodes;
164public:
165 String *pxml;
166 String context_cache;
167 Item_nodeset_func(THD *thd, String *pxml_arg):
168 Item_str_func(thd), pxml(pxml_arg) {}
169 Item_nodeset_func(THD *thd, Item *a, String *pxml_arg):
170 Item_str_func(thd, a), pxml(pxml_arg) {}
171 Item_nodeset_func(THD *thd, Item *a, Item *b, String *pxml_arg):
172 Item_str_func(thd, a, b), pxml(pxml_arg) {}
173 Item_nodeset_func(THD *thd, Item *a, Item *b, Item *c, String *pxml_arg):
174 Item_str_func(thd, a, b, c), pxml(pxml_arg) {}
175 void prepare_nodes()
176 {
177 nodebeg= (MY_XML_NODE*) pxml->ptr();
178 nodeend= (MY_XML_NODE*) (pxml->ptr() + pxml->length());
179 numnodes= (uint)(nodeend - nodebeg);
180 }
181 void prepare(String *nodeset)
182 {
183 prepare_nodes();
184 String *res= args[0]->val_nodeset(&tmp_value);
185 fltbeg= (MY_XPATH_FLT*) res->ptr();
186 fltend= (MY_XPATH_FLT*) (res->ptr() + res->length());
187 nodeset->length(0);
188 }
189 enum Type type() const { return XPATH_NODESET; }
190 String *val_str(String *str)
191 {
192 prepare_nodes();
193 String *res= val_nodeset(&tmp2_value);
194 fltbeg= (MY_XPATH_FLT*) res->ptr();
195 fltend= (MY_XPATH_FLT*) (res->ptr() + res->length());
196 String active;
197 active.alloc(numnodes);
198 bzero((char*) active.ptr(), numnodes);
199 for (MY_XPATH_FLT *flt= fltbeg; flt < fltend; flt++)
200 {
201 MY_XML_NODE *node;
202 uint j;
203 for (j=0, node= nodebeg ; j < numnodes; j++, node++)
204 {
205 if (node->type == MY_XML_NODE_TEXT &&
206 node->parent == flt->num)
207 active[j]= 1;
208 }
209 }
210
211 str->length(0);
212 str->set_charset(collation.collation);
213 for (uint i=0 ; i < numnodes; i++)
214 {
215 if(active[i])
216 {
217 if (str->length())
218 str->append(" ", 1, &my_charset_latin1);
219 str->append(nodebeg[i].beg, nodebeg[i].end - nodebeg[i].beg);
220 }
221 }
222 return str;
223 }
224 enum Item_result result_type () const { return STRING_RESULT; }
225 void fix_length_and_dec()
226 {
227 max_length= MAX_BLOB_WIDTH;
228 collation.collation= pxml->charset();
229 // To avoid premature evaluation, mark all nodeset functions as non-const.
230 used_tables_cache= RAND_TABLE_BIT;
231 const_item_cache= false;
232 }
233 const char *func_name() const { return "nodeset"; }
234 bool check_vcol_func_processor(void *arg)
235 {
236 return mark_unsupported_function(func_name(), arg, VCOL_IMPOSSIBLE);
237 }
238
239};
240
241
242/* Returns an XML root */
243class Item_nodeset_func_rootelement :public Item_nodeset_func
244{
245public:
246 Item_nodeset_func_rootelement(THD *thd, String *pxml):
247 Item_nodeset_func(thd, pxml) {}
248 const char *func_name() const { return "xpath_rootelement"; }
249 String *val_nodeset(String *nodeset);
250 Item *get_copy(THD *thd)
251 { return get_item_copy<Item_nodeset_func_rootelement>(thd, this); }
252};
253
254
255/* Returns a Union of two node sets */
256class Item_nodeset_func_union :public Item_nodeset_func
257{
258public:
259 Item_nodeset_func_union(THD *thd, Item *a, Item *b, String *pxml):
260 Item_nodeset_func(thd, a, b, pxml) {}
261 const char *func_name() const { return "xpath_union"; }
262 String *val_nodeset(String *nodeset);
263 Item *get_copy(THD *thd)
264 { return get_item_copy<Item_nodeset_func_union>(thd, this); }
265};
266
267
268/* Makes one step towards the given axis */
269class Item_nodeset_func_axisbyname :public Item_nodeset_func
270{
271 const char *node_name;
272 uint node_namelen;
273public:
274 Item_nodeset_func_axisbyname(THD *thd, Item *a, const char *n_arg, uint l_arg,
275 String *pxml):
276 Item_nodeset_func(thd, a, pxml), node_name(n_arg), node_namelen(l_arg) { }
277 const char *func_name() const { return "xpath_axisbyname"; }
278 bool validname(MY_XML_NODE *n)
279 {
280 if (node_name[0] == '*')
281 return 1;
282 return (node_namelen == (uint) (n->end - n->beg)) &&
283 !memcmp(node_name, n->beg, node_namelen);
284 }
285};
286
287
288/* Returns self */
289class Item_nodeset_func_selfbyname: public Item_nodeset_func_axisbyname
290{
291public:
292 Item_nodeset_func_selfbyname(THD *thd, Item *a, const char *n_arg, uint l_arg,
293 String *pxml):
294 Item_nodeset_func_axisbyname(thd, a, n_arg, l_arg, pxml) {}
295 const char *func_name() const { return "xpath_selfbyname"; }
296 String *val_nodeset(String *nodeset);
297 Item *get_copy(THD *thd)
298 { return get_item_copy<Item_nodeset_func_selfbyname>(thd, this); }
299};
300
301
302/* Returns children */
303class Item_nodeset_func_childbyname: public Item_nodeset_func_axisbyname
304{
305public:
306 Item_nodeset_func_childbyname(THD *thd, Item *a, const char *n_arg, uint l_arg,
307 String *pxml):
308 Item_nodeset_func_axisbyname(thd, a, n_arg, l_arg, pxml) {}
309 const char *func_name() const { return "xpath_childbyname"; }
310 String *val_nodeset(String *nodeset);
311 Item *get_copy(THD *thd)
312 { return get_item_copy<Item_nodeset_func_childbyname>(thd, this); }
313};
314
315
316/* Returns descendants */
317class Item_nodeset_func_descendantbyname: public Item_nodeset_func_axisbyname
318{
319 bool need_self;
320public:
321 Item_nodeset_func_descendantbyname(THD *thd, Item *a, const char *n_arg, uint l_arg,
322 String *pxml, bool need_self_arg):
323 Item_nodeset_func_axisbyname(thd, a, n_arg, l_arg, pxml),
324 need_self(need_self_arg) {}
325 const char *func_name() const { return "xpath_descendantbyname"; }
326 String *val_nodeset(String *nodeset);
327 Item *get_copy(THD *thd)
328 { return get_item_copy<Item_nodeset_func_descendantbyname>(thd, this); }
329};
330
331
332/* Returns ancestors */
333class Item_nodeset_func_ancestorbyname: public Item_nodeset_func_axisbyname
334{
335 bool need_self;
336public:
337 Item_nodeset_func_ancestorbyname(THD *thd, Item *a, const char *n_arg, uint l_arg,
338 String *pxml, bool need_self_arg):
339 Item_nodeset_func_axisbyname(thd, a, n_arg, l_arg, pxml),
340 need_self(need_self_arg) {}
341 const char *func_name() const { return "xpath_ancestorbyname"; }
342 String *val_nodeset(String *nodeset);
343 Item *get_copy(THD *thd)
344 { return get_item_copy<Item_nodeset_func_ancestorbyname>(thd, this); }
345};
346
347
348/* Returns parents */
349class Item_nodeset_func_parentbyname: public Item_nodeset_func_axisbyname
350{
351public:
352 Item_nodeset_func_parentbyname(THD *thd, Item *a, const char *n_arg, uint l_arg,
353 String *pxml):
354 Item_nodeset_func_axisbyname(thd, a, n_arg, l_arg, pxml) {}
355 const char *func_name() const { return "xpath_parentbyname"; }
356 String *val_nodeset(String *nodeset);
357 Item *get_copy(THD *thd)
358 { return get_item_copy<Item_nodeset_func_parentbyname>(thd, this); }
359};
360
361
362/* Returns attributes */
363class Item_nodeset_func_attributebyname: public Item_nodeset_func_axisbyname
364{
365public:
366 Item_nodeset_func_attributebyname(THD *thd, Item *a, const char *n_arg,
367 uint l_arg, String *pxml):
368 Item_nodeset_func_axisbyname(thd, a, n_arg, l_arg, pxml) {}
369 const char *func_name() const { return "xpath_attributebyname"; }
370 String *val_nodeset(String *nodeset);
371 Item *get_copy(THD *thd)
372 { return get_item_copy<Item_nodeset_func_attributebyname>(thd, this); }
373};
374
375
376/*
377 Condition iterator: goes through all nodes in the current
378 context and checks a condition, returning those nodes
379 giving TRUE condition result.
380*/
381class Item_nodeset_func_predicate :public Item_nodeset_func
382{
383public:
384 Item_nodeset_func_predicate(THD *thd, Item *a, Item *b, String *pxml):
385 Item_nodeset_func(thd, a, b, pxml) {}
386 const char *func_name() const { return "xpath_predicate"; }
387 String *val_nodeset(String *nodeset);
388 Item *get_copy(THD *thd)
389 { return get_item_copy<Item_nodeset_func_predicate>(thd, this); }
390};
391
392
393/* Selects nodes with a given position in context */
394class Item_nodeset_func_elementbyindex :public Item_nodeset_func
395{
396public:
397 Item_nodeset_func_elementbyindex(THD *thd, Item *a, Item *b, String *pxml):
398 Item_nodeset_func(thd, a, b, pxml) { }
399 const char *func_name() const { return "xpath_elementbyindex"; }
400 String *val_nodeset(String *nodeset);
401 Item *get_copy(THD *thd)
402 { return get_item_copy<Item_nodeset_func_elementbyindex>(thd, this); }
403};
404
405
406/*
407 Converts its argument into a boolean value.
408 * a number is true if it is non-zero
409 * a node-set is true if and only if it is non-empty
410 * a string is true if and only if its length is non-zero
411*/
412class Item_xpath_cast_bool :public Item_bool_func
413{
414 String *pxml;
415 String tmp_value;
416public:
417 Item_xpath_cast_bool(THD *thd, Item *a, String *pxml_arg):
418 Item_bool_func(thd, a), pxml(pxml_arg) {}
419 const char *func_name() const { return "xpath_cast_bool"; }
420 longlong val_int()
421 {
422 if (args[0]->type() == XPATH_NODESET)
423 {
424 String *flt= args[0]->val_nodeset(&tmp_value);
425 return flt->length() == sizeof(MY_XPATH_FLT) ? 1 : 0;
426 }
427 return args[0]->val_real() ? 1 : 0;
428 }
429 Item *get_copy(THD *thd)
430 { return get_item_copy<Item_xpath_cast_bool>(thd, this); }
431};
432
433
434/*
435 Converts its argument into a number
436*/
437class Item_xpath_cast_number :public Item_real_func
438{
439public:
440 Item_xpath_cast_number(THD *thd, Item *a): Item_real_func(thd, a) {}
441 const char *func_name() const { return "xpath_cast_number"; }
442 virtual double val_real() { return args[0]->val_real(); }
443 Item *get_copy(THD *thd)
444 { return get_item_copy<Item_xpath_cast_number>(thd, this); }
445};
446
447
448/*
449 Context cache, for predicate
450*/
451class Item_nodeset_context_cache :public Item_nodeset_func
452{
453public:
454 String *string_cache;
455 Item_nodeset_context_cache(THD *thd, String *str_arg, String *pxml):
456 Item_nodeset_func(thd, pxml), string_cache(str_arg) { }
457 String *val_nodeset(String *res)
458 { return string_cache; }
459 void fix_length_and_dec() { max_length= MAX_BLOB_WIDTH; }
460 Item *get_copy(THD *thd)
461 { return get_item_copy<Item_nodeset_context_cache>(thd, this); }
462};
463
464
465class Item_func_xpath_position :public Item_long_func
466{
467 String *pxml;
468 String tmp_value;
469public:
470 Item_func_xpath_position(THD *thd, Item *a, String *p):
471 Item_long_func(thd, a), pxml(p) {}
472 const char *func_name() const { return "xpath_position"; }
473 void fix_length_and_dec() { max_length=10; }
474 longlong val_int()
475 {
476 String *flt= args[0]->val_nodeset(&tmp_value);
477 if (flt->length() == sizeof(MY_XPATH_FLT))
478 return ((MY_XPATH_FLT*)flt->ptr())->pos + 1;
479 return 0;
480 }
481 Item *get_copy(THD *thd)
482 { return get_item_copy<Item_func_xpath_position>(thd, this); }
483};
484
485
486class Item_func_xpath_count :public Item_long_func
487{
488 String *pxml;
489 String tmp_value;
490public:
491 Item_func_xpath_count(THD *thd, Item *a, String *p):
492 Item_long_func(thd, a), pxml(p) {}
493 const char *func_name() const { return "xpath_count"; }
494 void fix_length_and_dec() { max_length=10; }
495 longlong val_int()
496 {
497 uint predicate_supplied_context_size;
498 String *res= args[0]->val_nodeset(&tmp_value);
499 if (res->length() == sizeof(MY_XPATH_FLT) &&
500 (predicate_supplied_context_size= ((MY_XPATH_FLT*)res->ptr())->size))
501 return predicate_supplied_context_size;
502 return res->length() / sizeof(MY_XPATH_FLT);
503 }
504 Item *get_copy(THD *thd)
505 { return get_item_copy<Item_func_xpath_count>(thd, this); }
506};
507
508
509class Item_func_xpath_sum :public Item_real_func
510{
511 String *pxml;
512 String tmp_value;
513public:
514 Item_func_xpath_sum(THD *thd, Item *a, String *p):
515 Item_real_func(thd, a), pxml(p) {}
516
517 const char *func_name() const { return "xpath_sum"; }
518 double val_real()
519 {
520 double sum= 0;
521 String *res= args[0]->val_nodeset(&tmp_value);
522 MY_XPATH_FLT *fltbeg= (MY_XPATH_FLT*) res->ptr();
523 MY_XPATH_FLT *fltend= (MY_XPATH_FLT*) (res->ptr() + res->length());
524 uint numnodes= pxml->length() / sizeof(MY_XML_NODE);
525 MY_XML_NODE *nodebeg= (MY_XML_NODE*) pxml->ptr();
526
527 for (MY_XPATH_FLT *flt= fltbeg; flt < fltend; flt++)
528 {
529 MY_XML_NODE *self= &nodebeg[flt->num];
530 for (uint j= flt->num + 1; j < numnodes; j++)
531 {
532 MY_XML_NODE *node= &nodebeg[j];
533 if (node->level <= self->level)
534 break;
535 if ((node->parent == flt->num) &&
536 (node->type == MY_XML_NODE_TEXT))
537 {
538 char *end;
539 int err;
540 double add= my_strntod(collation.collation, (char*) node->beg,
541 node->end - node->beg, &end, &err);
542 if (!err)
543 sum+= add;
544 }
545 }
546 }
547 return sum;
548 }
549 Item *get_copy(THD *thd)
550 { return get_item_copy<Item_func_xpath_sum>(thd, this); }
551};
552
553
554/**
555 A string whose value may be changed during execution.
556*/
557class Item_string_xml_non_const: public Item_string
558{
559public:
560 Item_string_xml_non_const(THD *thd, const char *str, uint length,
561 CHARSET_INFO *cs):
562 Item_string(thd, str, length, cs)
563 { }
564 bool const_item() const { return false ; }
565 bool basic_const_item() const { return false; }
566 void set_value(const char *str, uint length, CHARSET_INFO *cs)
567 {
568 str_value.set(str, length, cs);
569 }
570 Item *safe_charset_converter(THD *thd, CHARSET_INFO *tocs)
571 {
572 /*
573 Item_string::safe_charset_converter() does not accept non-constants.
574 Note, conversion is not really needed here anyway.
575 */
576 return this;
577 }
578};
579
580
581class Item_nodeset_to_const_comparator :public Item_bool_func
582{
583 String *pxml;
584 String tmp_nodeset;
585public:
586 Item_nodeset_to_const_comparator(THD *thd, Item *nodeset, Item *cmpfunc,
587 String *p):
588 Item_bool_func(thd, nodeset, cmpfunc), pxml(p) {}
589 enum Type type() const { return XPATH_NODESET_CMP; };
590 const char *func_name() const { return "xpath_nodeset_to_const_comparator"; }
591 bool check_vcol_func_processor(void *arg)
592 {
593 return mark_unsupported_function(func_name(), arg, VCOL_IMPOSSIBLE);
594 }
595
596 longlong val_int()
597 {
598 Item_func *comp= (Item_func*)args[1];
599 Item_string_xml_non_const *fake=
600 (Item_string_xml_non_const*)(comp->arguments()[0]);
601 String *res= args[0]->val_nodeset(&tmp_nodeset);
602 MY_XPATH_FLT *fltbeg= (MY_XPATH_FLT*) res->ptr();
603 MY_XPATH_FLT *fltend= (MY_XPATH_FLT*) (res->ptr() + res->length());
604 MY_XML_NODE *nodebeg= (MY_XML_NODE*) pxml->ptr();
605 uint numnodes= pxml->length() / sizeof(MY_XML_NODE);
606
607 for (MY_XPATH_FLT *flt= fltbeg; flt < fltend; flt++)
608 {
609 MY_XML_NODE *self= &nodebeg[flt->num];
610 for (uint j= flt->num + 1; j < numnodes; j++)
611 {
612 MY_XML_NODE *node= &nodebeg[j];
613 if (node->level <= self->level)
614 break;
615 if ((node->parent == flt->num) &&
616 (node->type == MY_XML_NODE_TEXT))
617 {
618 fake->set_value(node->beg, (uint)(node->end - node->beg),
619 collation.collation);
620 if (args[1]->val_int())
621 return 1;
622 }
623 }
624 }
625 return 0;
626 }
627 Item *get_copy(THD *thd)
628 { return get_item_copy<Item_nodeset_to_const_comparator>(thd, this); }
629};
630
631
632String *Item_nodeset_func_rootelement::val_nodeset(String *nodeset)
633{
634 nodeset->length(0);
635 ((XPathFilter*)nodeset)->append_element(0, 0);
636 return nodeset;
637}
638
639
640String * Item_nodeset_func_union::val_nodeset(String *nodeset)
641{
642 uint num_nodes= pxml->length() / sizeof(MY_XML_NODE);
643 String set0, *s0= args[0]->val_nodeset(&set0);
644 String set1, *s1= args[1]->val_nodeset(&set1);
645 String both_str;
646 both_str.alloc(num_nodes);
647 char *both= (char*) both_str.ptr();
648 bzero((void*)both, num_nodes);
649 MY_XPATH_FLT *flt;
650
651 fltbeg= (MY_XPATH_FLT*) s0->ptr();
652 fltend= (MY_XPATH_FLT*) (s0->ptr() + s0->length());
653 for (flt= fltbeg; flt < fltend; flt++)
654 both[flt->num]= 1;
655
656 fltbeg= (MY_XPATH_FLT*) s1->ptr();
657 fltend= (MY_XPATH_FLT*) (s1->ptr() + s1->length());
658 for (flt= fltbeg; flt < fltend; flt++)
659 both[flt->num]= 1;
660
661 nodeset->length(0);
662 for (uint i= 0, pos= 0; i < num_nodes; i++)
663 {
664 if (both[i])
665 ((XPathFilter*)nodeset)->append_element(i, pos++);
666 }
667 return nodeset;
668}
669
670
671String *Item_nodeset_func_selfbyname::val_nodeset(String *nodeset)
672{
673 prepare(nodeset);
674 for (MY_XPATH_FLT *flt= fltbeg; flt < fltend; flt++)
675 {
676 uint pos= 0;
677 MY_XML_NODE *self= &nodebeg[flt->num];
678 if (validname(self))
679 ((XPathFilter*)nodeset)->append_element(flt->num,pos++);
680 }
681 return nodeset;
682}
683
684
685String *Item_nodeset_func_childbyname::val_nodeset(String *nodeset)
686{
687 prepare(nodeset);
688 for (MY_XPATH_FLT *flt= fltbeg; flt < fltend; flt++)
689 {
690 MY_XML_NODE *self= &nodebeg[flt->num];
691 for (uint pos= 0, j= flt->num + 1 ; j < numnodes; j++)
692 {
693 MY_XML_NODE *node= &nodebeg[j];
694 if (node->level <= self->level)
695 break;
696 if ((node->parent == flt->num) &&
697 (node->type == MY_XML_NODE_TAG) &&
698 validname(node))
699 ((XPathFilter*)nodeset)->append_element(j, pos++);
700 }
701 }
702 return nodeset;
703}
704
705
706String *Item_nodeset_func_descendantbyname::val_nodeset(String *nodeset)
707{
708 prepare(nodeset);
709 for (MY_XPATH_FLT *flt= fltbeg; flt < fltend; flt++)
710 {
711 uint pos= 0;
712 MY_XML_NODE *self= &nodebeg[flt->num];
713 if (need_self && validname(self))
714 ((XPathFilter*)nodeset)->append_element(flt->num,pos++);
715 for (uint j= flt->num + 1 ; j < numnodes ; j++)
716 {
717 MY_XML_NODE *node= &nodebeg[j];
718 if (node->level <= self->level)
719 break;
720 if ((node->type == MY_XML_NODE_TAG) && validname(node))
721 ((XPathFilter*)nodeset)->append_element(j,pos++);
722 }
723 }
724 return nodeset;
725}
726
727
728String *Item_nodeset_func_ancestorbyname::val_nodeset(String *nodeset)
729{
730 char *active;
731 String active_str;
732 prepare(nodeset);
733 active_str.alloc(numnodes);
734 active= (char*) active_str.ptr();
735 bzero((void*)active, numnodes);
736 uint pos= 0;
737
738 for (MY_XPATH_FLT *flt= fltbeg; flt < fltend; flt++)
739 {
740 /*
741 Go to the root and add all nodes on the way.
742 Don't add the root if context is the root itelf
743 */
744 MY_XML_NODE *self= &nodebeg[flt->num];
745 if (need_self && validname(self))
746 {
747 active[flt->num]= 1;
748 pos++;
749 }
750
751 for (uint j= self->parent; nodebeg[j].parent != j; j= nodebeg[j].parent)
752 {
753 if (flt->num && validname(&nodebeg[j]))
754 {
755 active[j]= 1;
756 pos++;
757 }
758 }
759 }
760
761 for (uint j= 0; j < numnodes ; j++)
762 {
763 if (active[j])
764 ((XPathFilter*)nodeset)->append_element(j, --pos);
765 }
766 return nodeset;
767}
768
769
770String *Item_nodeset_func_parentbyname::val_nodeset(String *nodeset)
771{
772 char *active;
773 String active_str;
774 prepare(nodeset);
775 active_str.alloc(numnodes);
776 active= (char*) active_str.ptr();
777 bzero((void*)active, numnodes);
778 for (MY_XPATH_FLT *flt= fltbeg; flt < fltend; flt++)
779 {
780 uint j= nodebeg[flt->num].parent;
781 if (flt->num && validname(&nodebeg[j]))
782 active[j]= 1;
783 }
784 for (uint j= 0, pos= 0; j < numnodes ; j++)
785 {
786 if (active[j])
787 ((XPathFilter*)nodeset)->append_element(j, pos++);
788 }
789 return nodeset;
790}
791
792
793String *Item_nodeset_func_attributebyname::val_nodeset(String *nodeset)
794{
795 prepare(nodeset);
796 for (MY_XPATH_FLT *flt= fltbeg; flt < fltend; flt++)
797 {
798 MY_XML_NODE *self= &nodebeg[flt->num];
799 for (uint pos=0, j= flt->num + 1 ; j < numnodes; j++)
800 {
801 MY_XML_NODE *node= &nodebeg[j];
802 if (node->level <= self->level)
803 break;
804 if ((node->parent == flt->num) &&
805 (node->type == MY_XML_NODE_ATTR) &&
806 validname(node))
807 ((XPathFilter*)nodeset)->append_element(j, pos++);
808 }
809 }
810 return nodeset;
811}
812
813
814String *Item_nodeset_func_predicate::val_nodeset(String *str)
815{
816 Item_nodeset_func *nodeset_func= (Item_nodeset_func*) args[0];
817 Item_func *comp_func= (Item_func*)args[1];
818 uint pos= 0, size;
819 prepare(str);
820 size= (uint)(fltend - fltbeg);
821 for (MY_XPATH_FLT *flt= fltbeg; flt < fltend; flt++)
822 {
823 nodeset_func->context_cache.length(0);
824 ((XPathFilter*)(&nodeset_func->context_cache))->append_element(flt->num,
825 flt->pos,
826 size);
827 if (comp_func->val_int())
828 ((XPathFilter*)str)->append_element(flt->num, pos++);
829 }
830 return str;
831}
832
833
834String *Item_nodeset_func_elementbyindex::val_nodeset(String *nodeset)
835{
836 Item_nodeset_func *nodeset_func= (Item_nodeset_func*) args[0];
837 prepare(nodeset);
838 MY_XPATH_FLT *flt;
839 uint pos, size= (uint)(fltend - fltbeg);
840 for (pos= 0, flt= fltbeg; flt < fltend; flt++)
841 {
842 nodeset_func->context_cache.length(0);
843 ((XPathFilter*)(&nodeset_func->context_cache))->append_element(flt->num,
844 flt->pos,
845 size);
846 int index= (int) (args[1]->val_int()) - 1;
847 if (index >= 0 && (flt->pos == (uint) index || args[1]->is_bool_type()))
848 ((XPathFilter*)nodeset)->append_element(flt->num, pos++);
849 }
850 return nodeset;
851}
852
853
854/*
855 If item is a node set, then casts it to boolean,
856 otherwise returns the item itself.
857*/
858static Item* nodeset2bool(MY_XPATH *xpath, Item *item)
859{
860 if (item->type() == Item::XPATH_NODESET)
861 return new (xpath->thd->mem_root)
862 Item_xpath_cast_bool(xpath->thd, item, xpath->pxml);
863 return item;
864}
865
866
867/*
868 XPath lexical tokens
869*/
870#define MY_XPATH_LEX_DIGITS 'd'
871#define MY_XPATH_LEX_IDENT 'i'
872#define MY_XPATH_LEX_STRING 's'
873#define MY_XPATH_LEX_SLASH '/'
874#define MY_XPATH_LEX_LB '['
875#define MY_XPATH_LEX_RB ']'
876#define MY_XPATH_LEX_LP '('
877#define MY_XPATH_LEX_RP ')'
878#define MY_XPATH_LEX_EQ '='
879#define MY_XPATH_LEX_LESS '<'
880#define MY_XPATH_LEX_GREATER '>'
881#define MY_XPATH_LEX_AT '@'
882#define MY_XPATH_LEX_COLON ':'
883#define MY_XPATH_LEX_ASTERISK '*'
884#define MY_XPATH_LEX_DOT '.'
885#define MY_XPATH_LEX_VLINE '|'
886#define MY_XPATH_LEX_MINUS '-'
887#define MY_XPATH_LEX_PLUS '+'
888#define MY_XPATH_LEX_EXCL '!'
889#define MY_XPATH_LEX_COMMA ','
890#define MY_XPATH_LEX_DOLLAR '$'
891#define MY_XPATH_LEX_ERROR 'A'
892#define MY_XPATH_LEX_EOF 'B'
893#define MY_XPATH_LEX_AND 'C'
894#define MY_XPATH_LEX_OR 'D'
895#define MY_XPATH_LEX_DIV 'E'
896#define MY_XPATH_LEX_MOD 'F'
897#define MY_XPATH_LEX_FUNC 'G'
898#define MY_XPATH_LEX_NODETYPE 'H'
899#define MY_XPATH_LEX_AXIS 'I'
900#define MY_XPATH_LEX_LE 'J'
901#define MY_XPATH_LEX_GE 'K'
902
903
904/*
905 XPath axis type
906*/
907#define MY_XPATH_AXIS_ANCESTOR 0
908#define MY_XPATH_AXIS_ANCESTOR_OR_SELF 1
909#define MY_XPATH_AXIS_ATTRIBUTE 2
910#define MY_XPATH_AXIS_CHILD 3
911#define MY_XPATH_AXIS_DESCENDANT 4
912#define MY_XPATH_AXIS_DESCENDANT_OR_SELF 5
913#define MY_XPATH_AXIS_FOLLOWING 6
914#define MY_XPATH_AXIS_FOLLOWING_SIBLING 7
915#define MY_XPATH_AXIS_NAMESPACE 8
916#define MY_XPATH_AXIS_PARENT 9
917#define MY_XPATH_AXIS_PRECEDING 10
918#define MY_XPATH_AXIS_PRECEDING_SIBLING 11
919#define MY_XPATH_AXIS_SELF 12
920
921
922/*
923 Create scalar comparator
924
925 SYNOPSYS
926 Create a comparator function for scalar arguments,
927 for the given arguments and operation.
928
929 RETURN
930 The newly created item.
931*/
932static Item *eq_func(THD *thd, int oper, Item *a, Item *b)
933{
934 MEM_ROOT *mem_root= thd->mem_root;
935 switch (oper)
936 {
937 case '=': return new (mem_root) Item_func_eq(thd, a, b);
938 case '!': return new (mem_root) Item_func_ne(thd, a, b);
939 case MY_XPATH_LEX_GE: return new (mem_root) Item_func_ge(thd, a, b);
940 case MY_XPATH_LEX_LE: return new (mem_root) Item_func_le(thd, a, b);
941 case MY_XPATH_LEX_GREATER: return new (mem_root) Item_func_gt(thd, a, b);
942 case MY_XPATH_LEX_LESS: return new (mem_root) Item_func_lt(thd, a, b);
943 }
944 return 0;
945}
946
947
948/*
949 Create scalar comparator
950
951 SYNOPSYS
952 Create a comparator function for scalar arguments,
953 for the given arguments and reverse operation, e.g.
954
955 A > B is converted into B < A
956
957 RETURN
958 The newly created item.
959*/
960static Item *eq_func_reverse(THD *thd, int oper, Item *a, Item *b)
961{
962 MEM_ROOT *mem_root= thd->mem_root;
963 switch (oper)
964 {
965 case '=': return new (mem_root) Item_func_eq(thd, a, b);
966 case '!': return new (mem_root) Item_func_ne(thd, a, b);
967 case MY_XPATH_LEX_GE: return new (mem_root) Item_func_le(thd, a, b);
968 case MY_XPATH_LEX_LE: return new (mem_root) Item_func_ge(thd, a, b);
969 case MY_XPATH_LEX_GREATER: return new (mem_root) Item_func_lt(thd, a, b);
970 case MY_XPATH_LEX_LESS: return new (mem_root) Item_func_gt(thd, a, b);
971 }
972 return 0;
973}
974
975
976/*
977 Create a comparator
978
979 SYNOPSYS
980 Create a comparator for scalar or non-scalar arguments,
981 for the given arguments and operation.
982
983 RETURN
984 The newly created item.
985*/
986static Item *create_comparator(MY_XPATH *xpath,
987 int oper, MY_XPATH_LEX *context,
988 Item *a, Item *b)
989{
990 if (a->type() != Item::XPATH_NODESET &&
991 b->type() != Item::XPATH_NODESET)
992 {
993 return eq_func(xpath->thd, oper, a, b); // two scalar arguments
994 }
995 else if (a->type() == Item::XPATH_NODESET &&
996 b->type() == Item::XPATH_NODESET)
997 {
998 uint len= (uint)(xpath->query.end - context->beg);
999 set_if_smaller(len, 32);
1000 my_printf_error(ER_UNKNOWN_ERROR,
1001 "XPATH error: "
1002 "comparison of two nodesets is not supported: '%.*s'",
1003 MYF(0), len, context->beg);
1004
1005 return 0; // TODO: Comparison of two nodesets
1006 }
1007 else
1008 {
1009 /*
1010 Compare a node set to a scalar value.
1011 We just create a fake Item_string_xml_non_const() argument,
1012 which will be filled to the partular value
1013 in a loop through all of the nodes in the node set.
1014 */
1015
1016 THD *thd= xpath->thd;
1017 Item_string *fake= (new (thd->mem_root)
1018 Item_string_xml_non_const(thd, "", 0, xpath->cs));
1019 Item_nodeset_func *nodeset;
1020 Item *scalar, *comp;
1021 if (a->type() == Item::XPATH_NODESET)
1022 {
1023 nodeset= (Item_nodeset_func*) a;
1024 scalar= b;
1025 comp= eq_func(thd, oper, (Item*)fake, scalar);
1026 }
1027 else
1028 {
1029 nodeset= (Item_nodeset_func*) b;
1030 scalar= a;
1031 comp= eq_func_reverse(thd, oper, fake, scalar);
1032 }
1033 return (new (thd->mem_root)
1034 Item_nodeset_to_const_comparator(thd, nodeset, comp, xpath->pxml));
1035 }
1036}
1037
1038
1039/*
1040 Create a step
1041
1042 SYNOPSYS
1043 Create a step function for the given argument and axis.
1044
1045 RETURN
1046 The newly created item.
1047*/
1048static Item* nametestfunc(MY_XPATH *xpath,
1049 int type, Item *arg, const char *beg, uint len)
1050{
1051 THD *thd= xpath->thd;
1052 MEM_ROOT *mem_root= thd->mem_root;
1053
1054 DBUG_ASSERT(arg != 0);
1055 DBUG_ASSERT(arg->type() == Item::XPATH_NODESET);
1056 DBUG_ASSERT(beg != 0);
1057 DBUG_ASSERT(len > 0);
1058
1059 Item *res;
1060 switch (type)
1061 {
1062 case MY_XPATH_AXIS_ANCESTOR:
1063 res= new (mem_root) Item_nodeset_func_ancestorbyname(thd, arg, beg, len,
1064 xpath->pxml, 0);
1065 break;
1066 case MY_XPATH_AXIS_ANCESTOR_OR_SELF:
1067 res= new (mem_root) Item_nodeset_func_ancestorbyname(thd, arg, beg, len,
1068 xpath->pxml, 1);
1069 break;
1070 case MY_XPATH_AXIS_PARENT:
1071 res= new (mem_root) Item_nodeset_func_parentbyname(thd, arg, beg, len,
1072 xpath->pxml);
1073 break;
1074 case MY_XPATH_AXIS_DESCENDANT:
1075 res= new (mem_root) Item_nodeset_func_descendantbyname(thd, arg, beg, len,
1076 xpath->pxml, 0);
1077 break;
1078 case MY_XPATH_AXIS_DESCENDANT_OR_SELF:
1079 res= new (mem_root) Item_nodeset_func_descendantbyname(thd, arg, beg, len,
1080 xpath->pxml, 1);
1081 break;
1082 case MY_XPATH_AXIS_ATTRIBUTE:
1083 res= new (mem_root) Item_nodeset_func_attributebyname(thd, arg, beg, len,
1084 xpath->pxml);
1085 break;
1086 case MY_XPATH_AXIS_SELF:
1087 res= new (mem_root) Item_nodeset_func_selfbyname(thd, arg, beg, len,
1088 xpath->pxml);
1089 break;
1090 default:
1091 res= new (mem_root) Item_nodeset_func_childbyname(thd, arg, beg, len,
1092 xpath->pxml);
1093 }
1094 return res;
1095}
1096
1097
1098/*
1099 Tokens consisting of one character, for faster lexical analizer.
1100*/
1101static char simpletok[128]=
1102{
1103 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1104/*
1105 ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
1106 @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _
1107 ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~ \200
1108*/
1109 0,1,0,0,1,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1,0,
1110 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,
1111 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0
1112};
1113
1114
1115/*
1116 XPath keywords
1117*/
1118struct my_xpath_keyword_names_st
1119{
1120 int tok;
1121 const char *name;
1122 size_t length;
1123 int extra;
1124};
1125
1126
1127static struct my_xpath_keyword_names_st my_keyword_names[] =
1128{
1129 {MY_XPATH_LEX_AND , "and" , 3, 0 },
1130 {MY_XPATH_LEX_OR , "or" , 2, 0 },
1131 {MY_XPATH_LEX_DIV , "div" , 3, 0 },
1132 {MY_XPATH_LEX_MOD , "mod" , 3, 0 },
1133 {0,NULL,0,0}
1134};
1135
1136
1137static struct my_xpath_keyword_names_st my_axis_names[]=
1138{
1139 {MY_XPATH_LEX_AXIS,"ancestor" , 8,MY_XPATH_AXIS_ANCESTOR },
1140 {MY_XPATH_LEX_AXIS,"ancestor-or-self" ,16,MY_XPATH_AXIS_ANCESTOR_OR_SELF },
1141 {MY_XPATH_LEX_AXIS,"attribute" , 9,MY_XPATH_AXIS_ATTRIBUTE },
1142 {MY_XPATH_LEX_AXIS,"child" , 5,MY_XPATH_AXIS_CHILD },
1143 {MY_XPATH_LEX_AXIS,"descendant" ,10,MY_XPATH_AXIS_DESCENDANT },
1144 {MY_XPATH_LEX_AXIS,"descendant-or-self",18,MY_XPATH_AXIS_DESCENDANT_OR_SELF},
1145 {MY_XPATH_LEX_AXIS,"following" , 9,MY_XPATH_AXIS_FOLLOWING },
1146 {MY_XPATH_LEX_AXIS,"following-sibling" ,17,MY_XPATH_AXIS_FOLLOWING_SIBLING },
1147 {MY_XPATH_LEX_AXIS,"namespace" , 9,MY_XPATH_AXIS_NAMESPACE },
1148 {MY_XPATH_LEX_AXIS,"parent" , 6,MY_XPATH_AXIS_PARENT },
1149 {MY_XPATH_LEX_AXIS,"preceding" , 9,MY_XPATH_AXIS_PRECEDING },
1150 {MY_XPATH_LEX_AXIS,"preceding-sibling" ,17,MY_XPATH_AXIS_PRECEDING_SIBLING },
1151 {MY_XPATH_LEX_AXIS,"self" , 4,MY_XPATH_AXIS_SELF },
1152 {0,NULL,0,0}
1153};
1154
1155
1156static struct my_xpath_keyword_names_st my_nodetype_names[]=
1157{
1158 {MY_XPATH_LEX_NODETYPE, "comment" , 7, 0 },
1159 {MY_XPATH_LEX_NODETYPE, "text" , 4, 0 },
1160 {MY_XPATH_LEX_NODETYPE, "processing-instruction" , 22,0 },
1161 {MY_XPATH_LEX_NODETYPE, "node" , 4, 0 },
1162 {0,NULL,0,0}
1163};
1164
1165
1166/*
1167 Lookup a keyword
1168
1169 SYNOPSYS
1170 Check that the last scanned identifier is a keyword.
1171
1172 RETURN
1173 - Token type, on lookup success.
1174 - MY_XPATH_LEX_IDENT, on lookup failure.
1175*/
1176static int
1177my_xpath_keyword(MY_XPATH *x,
1178 struct my_xpath_keyword_names_st *keyword_names,
1179 const char *beg, const char *end)
1180{
1181 struct my_xpath_keyword_names_st *k;
1182 size_t length= end-beg;
1183 for (k= keyword_names; k->name; k++)
1184 {
1185 if (length == k->length && !strncasecmp(beg, k->name, length))
1186 {
1187 x->extra= k->extra;
1188 return k->tok;
1189 }
1190 }
1191 return MY_XPATH_LEX_IDENT;
1192}
1193
1194
1195/*
1196 Functions to create an item, a-la those in item_create.cc
1197*/
1198
1199static Item *create_func_true(MY_XPATH *xpath, Item **args, uint nargs)
1200{
1201 return new (xpath->thd->mem_root) Item_bool(xpath->thd, "xpath_bool", 1);
1202}
1203
1204
1205static Item *create_func_false(MY_XPATH *xpath, Item **args, uint nargs)
1206{
1207 return new (xpath->thd->mem_root) Item_bool(xpath->thd, "xpath_bool", 0);
1208}
1209
1210
1211static Item *create_func_not(MY_XPATH *xpath, Item **args, uint nargs)
1212{
1213 return new (xpath->thd->mem_root)
1214 Item_func_not(xpath->thd, nodeset2bool(xpath, args[0]));
1215}
1216
1217
1218static Item *create_func_ceiling(MY_XPATH *xpath, Item **args, uint nargs)
1219{
1220 return new (xpath->thd->mem_root) Item_func_ceiling(xpath->thd, args[0]);
1221}
1222
1223
1224static Item *create_func_floor(MY_XPATH *xpath, Item **args, uint nargs)
1225{
1226 return new (xpath->thd->mem_root) Item_func_floor(xpath->thd, args[0]);
1227}
1228
1229
1230static Item *create_func_bool(MY_XPATH *xpath, Item **args, uint nargs)
1231{
1232 return new (xpath->thd->mem_root)
1233 Item_xpath_cast_bool(xpath->thd, args[0], xpath->pxml);
1234}
1235
1236
1237static Item *create_func_number(MY_XPATH *xpath, Item **args, uint nargs)
1238{
1239 return new (xpath->thd->mem_root)
1240 Item_xpath_cast_number(xpath->thd, args[0]);
1241}
1242
1243
1244static Item *create_func_string_length(MY_XPATH *xpath, Item **args,
1245 uint nargs)
1246{
1247 Item *arg= nargs ? args[0] : xpath->context;
1248 return arg ? new (xpath->thd->mem_root)
1249 Item_func_char_length(xpath->thd, arg) : 0;
1250}
1251
1252
1253static Item *create_func_round(MY_XPATH *xpath, Item **args, uint nargs)
1254{
1255 return new (xpath->thd->mem_root)
1256 Item_func_round(xpath->thd, args[0],
1257 new (xpath->thd->mem_root)
1258 Item_int(xpath->thd, (char *) "0", 0, 1), 0);
1259}
1260
1261
1262static Item *create_func_last(MY_XPATH *xpath, Item **args, uint nargs)
1263{
1264 return (xpath->context ?
1265 new (xpath->thd->mem_root)
1266 Item_func_xpath_count(xpath->thd, xpath->context, xpath->pxml) :
1267 NULL);
1268}
1269
1270
1271static Item *create_func_position(MY_XPATH *xpath, Item **args, uint nargs)
1272{
1273 return (xpath->context ?
1274 new (xpath->thd->mem_root)
1275 Item_func_xpath_position(xpath->thd, xpath->context, xpath->pxml) :
1276 NULL);
1277}
1278
1279
1280static Item *create_func_contains(MY_XPATH *xpath, Item **args, uint nargs)
1281{
1282 return (new (xpath->thd->mem_root)
1283 Item_xpath_cast_bool(xpath->thd,
1284 new (xpath->thd->mem_root)
1285 Item_func_locate(xpath->thd, args[0], args[1]),
1286 xpath->pxml));
1287}
1288
1289
1290static Item *create_func_concat(MY_XPATH *xpath, Item **args, uint nargs)
1291{
1292 return new (xpath->thd->mem_root)
1293 Item_func_concat(xpath->thd, args[0], args[1]);
1294}
1295
1296
1297static Item *create_func_substr(MY_XPATH *xpath, Item **args, uint nargs)
1298{
1299 THD *thd= xpath->thd;
1300 if (nargs == 2)
1301 return new (thd->mem_root) Item_func_substr(thd, args[0], args[1]);
1302 return new (thd->mem_root) Item_func_substr(thd, args[0], args[1], args[2]);
1303}
1304
1305
1306static Item *create_func_count(MY_XPATH *xpath, Item **args, uint nargs)
1307{
1308 if (args[0]->type() != Item::XPATH_NODESET)
1309 return 0;
1310 return new (xpath->thd->mem_root) Item_func_xpath_count(xpath->thd, args[0], xpath->pxml);
1311}
1312
1313
1314static Item *create_func_sum(MY_XPATH *xpath, Item **args, uint nargs)
1315{
1316 if (args[0]->type() != Item::XPATH_NODESET)
1317 return 0;
1318 return new (xpath->thd->mem_root)
1319 Item_func_xpath_sum(xpath->thd, args[0], xpath->pxml);
1320}
1321
1322
1323/*
1324 Functions names. Separate lists for names with
1325 lengths 3,4,5 and 6 for faster lookups.
1326*/
1327static MY_XPATH_FUNC my_func_names3[]=
1328{
1329 {"sum", 3, 1 , 1 , create_func_sum},
1330 {"not", 3, 1 , 1 , create_func_not},
1331 {0 , 0, 0 , 0, 0}
1332};
1333
1334
1335static MY_XPATH_FUNC my_func_names4[]=
1336{
1337 {"last", 4, 0, 0, create_func_last},
1338 {"true", 4, 0, 0, create_func_true},
1339 {"name", 4, 0, 1, 0},
1340 {"lang", 4, 1, 1, 0},
1341 {0 , 0, 0, 0, 0}
1342};
1343
1344
1345static MY_XPATH_FUNC my_func_names5[]=
1346{
1347 {"count", 5, 1, 1, create_func_count},
1348 {"false", 5, 0, 0, create_func_false},
1349 {"floor", 5, 1, 1, create_func_floor},
1350 {"round", 5, 1, 1, create_func_round},
1351 {0 , 0, 0, 0, 0}
1352};
1353
1354
1355static MY_XPATH_FUNC my_func_names6[]=
1356{
1357 {"concat", 6, 2, 255, create_func_concat},
1358 {"number", 6, 0, 1 , create_func_number},
1359 {"string", 6, 0, 1 , 0},
1360 {0 , 0, 0, 0 , 0}
1361};
1362
1363
1364/* Other functions, with name longer than 6, all together */
1365static MY_XPATH_FUNC my_func_names[] =
1366{
1367 {"id" , 2 , 1 , 1 , 0},
1368 {"boolean" , 7 , 1 , 1 , create_func_bool},
1369 {"ceiling" , 7 , 1 , 1 , create_func_ceiling},
1370 {"position" , 8 , 0 , 0 , create_func_position},
1371 {"contains" , 8 , 2 , 2 , create_func_contains},
1372 {"substring" , 9 , 2 , 3 , create_func_substr},
1373 {"translate" , 9 , 3 , 3 , 0},
1374
1375 {"local-name" , 10 , 0 , 1 , 0},
1376 {"starts-with" , 11 , 2 , 2 , 0},
1377 {"namespace-uri" , 13 , 0 , 1 , 0},
1378 {"string-length" , 13 , 0 , 1 , create_func_string_length},
1379 {"substring-after" , 15 , 2 , 2 , 0},
1380 {"normalize-space" , 15 , 0 , 1 , 0},
1381 {"substring-before" , 16 , 2 , 2 , 0},
1382
1383 {NULL,0,0,0,0}
1384};
1385
1386
1387/*
1388 Lookup a function by name
1389
1390 SYNOPSYS
1391 Lookup a function by its name.
1392
1393 RETURN
1394 Pointer to a MY_XPATH_FUNC variable on success.
1395 0 - on failure.
1396
1397*/
1398MY_XPATH_FUNC *
1399my_xpath_function(const char *beg, const char *end)
1400{
1401 MY_XPATH_FUNC *k, *function_names;
1402 uint length= (uint)(end-beg);
1403 switch (length)
1404 {
1405 case 1: return 0;
1406 case 3: function_names= my_func_names3; break;
1407 case 4: function_names= my_func_names4; break;
1408 case 5: function_names= my_func_names5; break;
1409 case 6: function_names= my_func_names6; break;
1410 default: function_names= my_func_names;
1411 }
1412 for (k= function_names; k->name; k++)
1413 if (k->create && length == k->length && !strncasecmp(beg, k->name, length))
1414 return k;
1415 return NULL;
1416}
1417
1418
1419/* Initialize a lex analizer token */
1420static void
1421my_xpath_lex_init(MY_XPATH_LEX *lex,
1422 const char *str, const char *strend)
1423{
1424 lex->beg= str;
1425 lex->end= strend;
1426}
1427
1428
1429/* Initialize an XPath query parser */
1430static void
1431my_xpath_init(MY_XPATH *xpath)
1432{
1433 bzero((void*)xpath, sizeof(xpath[0]));
1434}
1435
1436
1437static int
1438my_xdigit(int c)
1439{
1440 return ((c) >= '0' && (c) <= '9');
1441}
1442
1443
1444/*
1445 Scan the next token
1446
1447 SYNOPSYS
1448 Scan the next token from the input.
1449 lex->term is set to the scanned token type.
1450 lex->beg and lex->end are set to the beginnig
1451 and to the end of the token.
1452 RETURN
1453 N/A
1454*/
1455static void
1456my_xpath_lex_scan(MY_XPATH *xpath,
1457 MY_XPATH_LEX *lex, const char *beg, const char *end)
1458{
1459 int ch, ctype, length;
1460 for ( ; beg < end && *beg == ' ' ; beg++) ; // skip leading spaces
1461 lex->beg= beg;
1462
1463 if (beg >= end)
1464 {
1465 lex->end= beg;
1466 lex->term= MY_XPATH_LEX_EOF; // end of line reached
1467 return;
1468 }
1469
1470 // Check ident, or a function call, or a keyword
1471 if ((length= xpath->cs->cset->ctype(xpath->cs, &ctype,
1472 (const uchar*) beg,
1473 (const uchar*) end)) > 0 &&
1474 ((ctype & (_MY_L | _MY_U)) || *beg == '_'))
1475 {
1476 // scan untill the end of the idenfitier
1477 for (beg+= length;
1478 (length= xpath->cs->cset->ctype(xpath->cs, &ctype,
1479 (const uchar*) beg,
1480 (const uchar*) end)) > 0 &&
1481 ((ctype & (_MY_L | _MY_U | _MY_NMR)) ||
1482 *beg == '_' || *beg == '-' || *beg == '.') ;
1483 beg+= length) /* no op */;
1484 lex->end= beg;
1485
1486 if (beg < end)
1487 {
1488 if (*beg == '(')
1489 {
1490 /*
1491 check if a function call, e.g.: count(/a/b)
1492 or a nodetype test, e.g.: /a/b/text()
1493 */
1494 if ((xpath->func= my_xpath_function(lex->beg, beg)))
1495 lex->term= MY_XPATH_LEX_FUNC;
1496 else
1497 lex->term= my_xpath_keyword(xpath, my_nodetype_names,
1498 lex->beg, beg);
1499 return;
1500 }
1501 // check if an axis specifier, e.g.: /a/b/child::*
1502 else if (*beg == ':' && beg + 1 < end && beg[1] == ':')
1503 {
1504 lex->term= my_xpath_keyword(xpath, my_axis_names,
1505 lex->beg, beg);
1506 return;
1507 }
1508 }
1509 // check if a keyword
1510 lex->term= my_xpath_keyword(xpath, my_keyword_names,
1511 lex->beg, beg);
1512 return;
1513 }
1514
1515
1516 ch= *beg++;
1517
1518 if (ch > 0 && ch < 128 && simpletok[ch])
1519 {
1520 // a token consisting of one character found
1521 lex->end= beg;
1522 lex->term= ch;
1523 return;
1524 }
1525
1526
1527 if (my_xdigit(ch)) // a sequence of digits
1528 {
1529 for ( ; beg < end && my_xdigit(*beg) ; beg++) ;
1530 lex->end= beg;
1531 lex->term= MY_XPATH_LEX_DIGITS;
1532 return;
1533 }
1534
1535 if (ch == '"' || ch == '\'') // a string: either '...' or "..."
1536 {
1537 for ( ; beg < end && *beg != ch ; beg++) ;
1538 if (beg < end)
1539 {
1540 lex->end= beg+1;
1541 lex->term= MY_XPATH_LEX_STRING;
1542 return;
1543 }
1544 else
1545 {
1546 // unexpected end-of-line, without closing quot sign
1547 lex->end= end;
1548 lex->term= MY_XPATH_LEX_ERROR;
1549 return;
1550 }
1551 }
1552
1553 lex->end= beg;
1554 lex->term= MY_XPATH_LEX_ERROR; // unknown character
1555 return;
1556}
1557
1558
1559/*
1560 Scan the given token
1561
1562 SYNOPSYS
1563 Scan the given token and rotate lasttok to prevtok on success.
1564
1565 RETURN
1566 1 - success
1567 0 - failure
1568*/
1569static int
1570my_xpath_parse_term(MY_XPATH *xpath, int term)
1571{
1572 if (xpath->lasttok.term == term && !xpath->error)
1573 {
1574 xpath->prevtok= xpath->lasttok;
1575 my_xpath_lex_scan(xpath, &xpath->lasttok,
1576 xpath->lasttok.end, xpath->query.end);
1577 return 1;
1578 }
1579 return 0;
1580}
1581
1582
1583/*
1584 Scan AxisName
1585
1586 SYNOPSYS
1587 Scan an axis name and store the scanned axis type into xpath->axis.
1588
1589 RETURN
1590 1 - success
1591 0 - failure
1592*/
1593static int my_xpath_parse_AxisName(MY_XPATH *xpath)
1594{
1595 int rc= my_xpath_parse_term(xpath, MY_XPATH_LEX_AXIS);
1596 xpath->axis= xpath->extra;
1597 return rc;
1598}
1599
1600
1601/*********************************************
1602** Grammar rules, according to http://www.w3.org/TR/xpath
1603** Implemented using recursive descendant method.
1604** All the following grammar processing functions accept
1605** a signle "xpath" argument and return 1 on success and 0 on error.
1606** They also modify "xpath" argument by creating new items.
1607*/
1608
1609/* [9] PredicateExpr ::= Expr */
1610#define my_xpath_parse_PredicateExpr(x) my_xpath_parse_Expr((x))
1611
1612/* [14] Expr ::= OrExpr */
1613#define my_xpath_parse_Expr(x) my_xpath_parse_OrExpr((x))
1614
1615static int my_xpath_parse_LocationPath(MY_XPATH *xpath);
1616static int my_xpath_parse_AbsoluteLocationPath(MY_XPATH *xpath);
1617static int my_xpath_parse_RelativeLocationPath(MY_XPATH *xpath);
1618static int my_xpath_parse_AbbreviatedStep(MY_XPATH *xpath);
1619static int my_xpath_parse_Step(MY_XPATH *xpath);
1620static int my_xpath_parse_AxisSpecifier(MY_XPATH *xpath);
1621static int my_xpath_parse_NodeTest(MY_XPATH *xpath);
1622static int my_xpath_parse_AbbreviatedAxisSpecifier(MY_XPATH *xpath);
1623static int my_xpath_parse_NameTest(MY_XPATH *xpath);
1624static int my_xpath_parse_FunctionCall(MY_XPATH *xpath);
1625static int my_xpath_parse_Number(MY_XPATH *xpath);
1626static int my_xpath_parse_FilterExpr(MY_XPATH *xpath);
1627static int my_xpath_parse_PathExpr(MY_XPATH *xpath);
1628static int my_xpath_parse_OrExpr(MY_XPATH *xpath);
1629static int my_xpath_parse_UnaryExpr(MY_XPATH *xpath);
1630static int my_xpath_parse_MultiplicativeExpr(MY_XPATH *xpath);
1631static int my_xpath_parse_AdditiveExpr(MY_XPATH *xpath);
1632static int my_xpath_parse_RelationalExpr(MY_XPATH *xpath);
1633static int my_xpath_parse_AndExpr(MY_XPATH *xpath);
1634static int my_xpath_parse_EqualityExpr(MY_XPATH *xpath);
1635static int my_xpath_parse_VariableReference(MY_XPATH *xpath);
1636
1637
1638/*
1639 Scan LocationPath
1640
1641 SYNOPSYS
1642
1643 [1] LocationPath ::= RelativeLocationPath
1644 | AbsoluteLocationPath
1645
1646 RETURN
1647 1 - success
1648 0 - failure
1649*/
1650static int my_xpath_parse_LocationPath(MY_XPATH *xpath)
1651{
1652 Item *context= xpath->context;
1653
1654 if (!xpath->context)
1655 xpath->context= xpath->rootelement;
1656 int rc= my_xpath_parse_RelativeLocationPath(xpath) ||
1657 my_xpath_parse_AbsoluteLocationPath(xpath);
1658
1659 xpath->item= xpath->context;
1660 xpath->context= context;
1661 return rc;
1662}
1663
1664
1665/*
1666 Scan Absolute Location Path
1667
1668 SYNOPSYS
1669
1670 [2] AbsoluteLocationPath ::= '/' RelativeLocationPath?
1671 | AbbreviatedAbsoluteLocationPath
1672 [10] AbbreviatedAbsoluteLocationPath ::= '//' RelativeLocationPath
1673
1674 We combine these two rules into one rule for better performance:
1675
1676 [2,10] AbsoluteLocationPath ::= '/' RelativeLocationPath?
1677 | '//' RelativeLocationPath
1678
1679 RETURN
1680 1 - success
1681 0 - failure
1682*/
1683static int my_xpath_parse_AbsoluteLocationPath(MY_XPATH *xpath)
1684{
1685 if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_SLASH))
1686 return 0;
1687
1688 xpath->context= xpath->rootelement;
1689
1690 if (my_xpath_parse_term(xpath, MY_XPATH_LEX_SLASH))
1691 {
1692 xpath->context= new (xpath->thd->mem_root)
1693 Item_nodeset_func_descendantbyname(xpath->thd,
1694 xpath->context,
1695 "*", 1,
1696 xpath->pxml, 1);
1697 return my_xpath_parse_RelativeLocationPath(xpath);
1698 }
1699
1700 my_xpath_parse_RelativeLocationPath(xpath);
1701
1702 return (xpath->error == 0);
1703}
1704
1705
1706/*
1707 Scan Relative Location Path
1708
1709 SYNOPSYS
1710
1711 For better performance we combine these two rules
1712
1713 [3] RelativeLocationPath ::= Step
1714 | RelativeLocationPath '/' Step
1715 | AbbreviatedRelativeLocationPath
1716 [11] AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step
1717
1718
1719 Into this one:
1720
1721 [3-11] RelativeLocationPath ::= Step
1722 | RelativeLocationPath '/' Step
1723 | RelativeLocationPath '//' Step
1724 RETURN
1725 1 - success
1726 0 - failure
1727*/
1728static int my_xpath_parse_RelativeLocationPath(MY_XPATH *xpath)
1729{
1730 if (!my_xpath_parse_Step(xpath))
1731 return 0;
1732 while (my_xpath_parse_term(xpath, MY_XPATH_LEX_SLASH))
1733 {
1734 if (my_xpath_parse_term(xpath, MY_XPATH_LEX_SLASH))
1735 xpath->context= new (xpath->thd->mem_root)
1736 Item_nodeset_func_descendantbyname(xpath->thd,
1737 xpath->context,
1738 "*", 1,
1739 xpath->pxml, 1);
1740 if (!my_xpath_parse_Step(xpath))
1741 {
1742 xpath->error= 1;
1743 return 0;
1744 }
1745 }
1746 return 1;
1747}
1748
1749
1750/*
1751 Scan non-abbreviated or abbreviated Step
1752
1753 SYNOPSYS
1754
1755 [4] Step ::= AxisSpecifier NodeTest Predicate*
1756 | AbbreviatedStep
1757 [8] Predicate ::= '[' PredicateExpr ']'
1758
1759 RETURN
1760 1 - success
1761 0 - failure
1762*/
1763static int
1764my_xpath_parse_AxisSpecifier_NodeTest_opt_Predicate_list(MY_XPATH *xpath)
1765{
1766 if (!my_xpath_parse_AxisSpecifier(xpath))
1767 return 0;
1768
1769 if (!my_xpath_parse_NodeTest(xpath))
1770 return 0;
1771
1772 while (my_xpath_parse_term(xpath, MY_XPATH_LEX_LB))
1773 {
1774 Item *prev_context= xpath->context;
1775 String *context_cache;
1776 context_cache= &((Item_nodeset_func*)xpath->context)->context_cache;
1777 xpath->context= new (xpath->thd->mem_root)
1778 Item_nodeset_context_cache(xpath->thd, context_cache, xpath->pxml);
1779 xpath->context_cache= context_cache;
1780
1781 if(!my_xpath_parse_PredicateExpr(xpath))
1782 {
1783 xpath->error= 1;
1784 return 0;
1785 }
1786
1787 if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_RB))
1788 {
1789 xpath->error= 1;
1790 return 0;
1791 }
1792
1793 xpath->item= nodeset2bool(xpath, xpath->item);
1794
1795 if (xpath->item->is_bool_type())
1796 {
1797 xpath->context= new (xpath->thd->mem_root)
1798 Item_nodeset_func_predicate(xpath->thd, prev_context,
1799 xpath->item,
1800 xpath->pxml);
1801 }
1802 else
1803 {
1804 xpath->context= new (xpath->thd->mem_root)
1805 Item_nodeset_func_elementbyindex(xpath->thd,
1806 prev_context,
1807 xpath->item,
1808 xpath->pxml);
1809 }
1810 }
1811 return 1;
1812}
1813
1814
1815static int my_xpath_parse_Step(MY_XPATH *xpath)
1816{
1817 return
1818 my_xpath_parse_AxisSpecifier_NodeTest_opt_Predicate_list(xpath) ||
1819 my_xpath_parse_AbbreviatedStep(xpath);
1820}
1821
1822
1823/*
1824 Scan Abbreviated Axis Specifier
1825
1826 SYNOPSYS
1827 [5] AxisSpecifier ::= AxisName '::'
1828 | AbbreviatedAxisSpecifier
1829
1830 RETURN
1831 1 - success
1832 0 - failure
1833*/
1834static int my_xpath_parse_AbbreviatedAxisSpecifier(MY_XPATH *xpath)
1835{
1836 if (my_xpath_parse_term(xpath, MY_XPATH_LEX_AT))
1837 xpath->axis= MY_XPATH_AXIS_ATTRIBUTE;
1838 else
1839 xpath->axis= MY_XPATH_AXIS_CHILD;
1840 return 1;
1841}
1842
1843
1844/*
1845 Scan non-abbreviated axis specifier
1846
1847 SYNOPSYS
1848
1849 RETURN
1850 1 - success
1851 0 - failure
1852*/
1853static int my_xpath_parse_AxisName_colon_colon(MY_XPATH *xpath)
1854{
1855 return my_xpath_parse_AxisName(xpath) &&
1856 my_xpath_parse_term(xpath, MY_XPATH_LEX_COLON) &&
1857 my_xpath_parse_term(xpath, MY_XPATH_LEX_COLON);
1858}
1859
1860
1861/*
1862 Scan Abbreviated AxisSpecifier
1863
1864 SYNOPSYS
1865 [13] AbbreviatedAxisSpecifier ::= '@'?
1866
1867 RETURN
1868 1 - success
1869 0 - failure
1870*/
1871static int my_xpath_parse_AxisSpecifier(MY_XPATH *xpath)
1872{
1873 return my_xpath_parse_AxisName_colon_colon(xpath) ||
1874 my_xpath_parse_AbbreviatedAxisSpecifier(xpath);
1875}
1876
1877
1878/*
1879 Scan NodeType followed by parens
1880
1881 SYNOPSYS
1882
1883 RETURN
1884 1 - success
1885 0 - failure
1886*/
1887static int my_xpath_parse_NodeTest_lp_rp(MY_XPATH *xpath)
1888{
1889 return my_xpath_parse_term(xpath, MY_XPATH_LEX_NODETYPE) &&
1890 my_xpath_parse_term(xpath, MY_XPATH_LEX_LP) &&
1891 my_xpath_parse_term(xpath, MY_XPATH_LEX_RP);
1892}
1893
1894
1895/*
1896 Scan NodeTest
1897
1898 SYNOPSYS
1899
1900 [7] NodeTest ::= NameTest
1901 | NodeType '(' ')'
1902 | 'processing-instruction' '(' Literal ')'
1903 RETURN
1904 1 - success
1905 0 - failure
1906*/
1907static int my_xpath_parse_NodeTest(MY_XPATH *xpath)
1908{
1909 return my_xpath_parse_NameTest(xpath) ||
1910 my_xpath_parse_NodeTest_lp_rp(xpath);
1911}
1912
1913
1914/*
1915 Scan Abbreviated Step
1916
1917 SYNOPSYS
1918
1919 [12] AbbreviatedStep ::= '.' | '..'
1920
1921 RETURN
1922 1 - success
1923 0 - failure
1924*/
1925static int my_xpath_parse_AbbreviatedStep(MY_XPATH *xpath)
1926{
1927 if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_DOT))
1928 return 0;
1929 if (my_xpath_parse_term(xpath, MY_XPATH_LEX_DOT))
1930 xpath->context= new (xpath->thd->mem_root)
1931 Item_nodeset_func_parentbyname(xpath->thd,
1932 xpath->context, "*",
1933 1, xpath->pxml);
1934 return 1;
1935}
1936
1937
1938/*
1939 Scan Primary Expression
1940
1941 SYNOPSYS
1942
1943 [15] PrimaryExpr ::= VariableReference
1944 | '(' Expr ')'
1945 | Literal
1946 | Number
1947 | FunctionCall
1948 RETURN
1949 1 - success
1950 0 - failure
1951*/
1952static int my_xpath_parse_lp_Expr_rp(MY_XPATH *xpath)
1953{
1954 return my_xpath_parse_term(xpath, MY_XPATH_LEX_LP) &&
1955 my_xpath_parse_Expr(xpath) &&
1956 my_xpath_parse_term(xpath, MY_XPATH_LEX_RP);
1957}
1958static int my_xpath_parse_PrimaryExpr_literal(MY_XPATH *xpath)
1959{
1960 if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_STRING))
1961 return 0;
1962 xpath->item= new (xpath->thd->mem_root)
1963 Item_string(xpath->thd, xpath->prevtok.beg + 1,
1964 (uint)(xpath->prevtok.end - xpath->prevtok.beg - 2),
1965 xpath->cs);
1966 return 1;
1967}
1968static int my_xpath_parse_PrimaryExpr(MY_XPATH *xpath)
1969{
1970 return
1971 my_xpath_parse_lp_Expr_rp(xpath) ||
1972 my_xpath_parse_VariableReference(xpath) ||
1973 my_xpath_parse_PrimaryExpr_literal(xpath) ||
1974 my_xpath_parse_Number(xpath) ||
1975 my_xpath_parse_FunctionCall(xpath);
1976}
1977
1978
1979/*
1980 Scan Function Call
1981
1982 SYNOPSYS
1983 [16] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument )* )? ')'
1984 [17] Argument ::= Expr
1985
1986 RETURN
1987 1 - success
1988 0 - failure
1989
1990*/
1991static int my_xpath_parse_FunctionCall(MY_XPATH *xpath)
1992{
1993 Item *args[256];
1994 uint nargs;
1995
1996 if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_FUNC))
1997 return 0;
1998
1999 MY_XPATH_FUNC *func= xpath->func;
2000
2001 if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_LP))
2002 return 0;
2003
2004 for (nargs= 0 ; nargs < func->maxargs; )
2005 {
2006 if (!my_xpath_parse_Expr(xpath))
2007 {
2008 if (nargs < func->minargs)
2009 return 0;
2010 goto right_paren;
2011 }
2012 args[nargs++]= xpath->item;
2013 if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_COMMA))
2014 {
2015 if (nargs < func->minargs)
2016 return 0;
2017 else
2018 break;
2019 }
2020 }
2021
2022right_paren:
2023 if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_RP))
2024 return 0;
2025
2026 return ((xpath->item= func->create(xpath, args, nargs))) ? 1 : 0;
2027}
2028
2029
2030/*
2031 Scan Union Expression
2032
2033 SYNOPSYS
2034 [18] UnionExpr ::= PathExpr
2035 | UnionExpr '|' PathExpr
2036
2037 RETURN
2038 1 - success
2039 0 - failure
2040*/
2041static int my_xpath_parse_UnionExpr(MY_XPATH *xpath)
2042{
2043 if (!my_xpath_parse_PathExpr(xpath))
2044 return 0;
2045
2046 while (my_xpath_parse_term(xpath, MY_XPATH_LEX_VLINE))
2047 {
2048 Item *prev= xpath->item;
2049 if (prev->type() != Item::XPATH_NODESET)
2050 return 0;
2051
2052 if (!my_xpath_parse_PathExpr(xpath)
2053 || xpath->item->type() != Item::XPATH_NODESET)
2054 {
2055 xpath->error= 1;
2056 return 0;
2057 }
2058 xpath->item= new (xpath->thd->mem_root)
2059 Item_nodeset_func_union(xpath->thd, prev, xpath->item,
2060 xpath->pxml);
2061 }
2062 return 1;
2063}
2064
2065
2066/*
2067 Scan Path Expression
2068
2069 SYNOPSYS
2070
2071 [19] PathExpr ::= LocationPath
2072 | FilterExpr
2073 | FilterExpr '/' RelativeLocationPath
2074 | FilterExpr '//' RelativeLocationPath
2075 RETURN
2076 1 - success
2077 0 - failure
2078*/
2079static int
2080my_xpath_parse_FilterExpr_opt_slashes_RelativeLocationPath(MY_XPATH *xpath)
2081{
2082 Item *context= xpath->context;
2083 int rc;
2084
2085 if (!my_xpath_parse_FilterExpr(xpath))
2086 return 0;
2087
2088 if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_SLASH))
2089 return 1;
2090
2091 if (xpath->item->type() != Item::XPATH_NODESET)
2092 {
2093 xpath->lasttok= xpath->prevtok;
2094 xpath->error= 1;
2095 return 0;
2096 }
2097
2098 /*
2099 The context for the next relative path is the nodeset
2100 returned by FilterExpr
2101 */
2102 xpath->context= xpath->item;
2103
2104 /* treat double slash (//) as /descendant-or-self::node()/ */
2105 if (my_xpath_parse_term(xpath, MY_XPATH_LEX_SLASH))
2106 xpath->context= new (xpath->thd->mem_root)
2107 Item_nodeset_func_descendantbyname(xpath->thd,
2108 xpath->context,
2109 "*", 1,
2110 xpath->pxml, 1);
2111 rc= my_xpath_parse_RelativeLocationPath(xpath);
2112
2113 /* push back the context and restore the item */
2114 xpath->item= xpath->context;
2115 xpath->context= context;
2116 return rc;
2117}
2118static int my_xpath_parse_PathExpr(MY_XPATH *xpath)
2119{
2120 return my_xpath_parse_LocationPath(xpath) ||
2121 my_xpath_parse_FilterExpr_opt_slashes_RelativeLocationPath(xpath);
2122}
2123
2124
2125
2126/*
2127 Scan Filter Expression
2128
2129 SYNOPSYS
2130 [20] FilterExpr ::= PrimaryExpr
2131 | FilterExpr Predicate
2132
2133 or in other words:
2134
2135 [20] FilterExpr ::= PrimaryExpr Predicate*
2136
2137 RETURN
2138 1 - success
2139 0 - failure
2140
2141*/
2142static int my_xpath_parse_FilterExpr(MY_XPATH *xpath)
2143{
2144 return my_xpath_parse_PrimaryExpr(xpath);
2145}
2146
2147
2148/*
2149 Scan Or Expression
2150
2151 SYNOPSYS
2152 [21] OrExpr ::= AndExpr
2153 | OrExpr 'or' AndExpr
2154
2155 RETURN
2156 1 - success
2157 0 - failure
2158*/
2159static int my_xpath_parse_OrExpr(MY_XPATH *xpath)
2160{
2161 if (!my_xpath_parse_AndExpr(xpath))
2162 return 0;
2163
2164 while (my_xpath_parse_term(xpath, MY_XPATH_LEX_OR))
2165 {
2166 Item *prev= xpath->item;
2167 if (!my_xpath_parse_AndExpr(xpath))
2168 {
2169 xpath->error= 1;
2170 return 0;
2171 }
2172 xpath->item= new (xpath->thd->mem_root)
2173 Item_cond_or(xpath->thd, nodeset2bool(xpath, prev),
2174 nodeset2bool(xpath, xpath->item));
2175 }
2176 return 1;
2177}
2178
2179
2180/*
2181 Scan And Expression
2182
2183 SYNOPSYS
2184 [22] AndExpr ::= EqualityExpr
2185 | AndExpr 'and' EqualityExpr
2186
2187 RETURN
2188 1 - success
2189 0 - failure
2190*/
2191static int my_xpath_parse_AndExpr(MY_XPATH *xpath)
2192{
2193 if (!my_xpath_parse_EqualityExpr(xpath))
2194 return 0;
2195
2196 while (my_xpath_parse_term(xpath, MY_XPATH_LEX_AND))
2197 {
2198 Item *prev= xpath->item;
2199 if (!my_xpath_parse_EqualityExpr(xpath))
2200 {
2201 xpath->error= 1;
2202 return 0;
2203 }
2204
2205 xpath->item= new (xpath->thd->mem_root)
2206 Item_cond_and(xpath->thd, nodeset2bool(xpath, prev),
2207 nodeset2bool(xpath, xpath->item));
2208 }
2209 return 1;
2210}
2211
2212
2213/*
2214 Scan Equality Expression
2215
2216 SYNOPSYS
2217 [23] EqualityExpr ::= RelationalExpr
2218 | EqualityExpr '=' RelationalExpr
2219 | EqualityExpr '!=' RelationalExpr
2220 or in other words:
2221
2222 [23] EqualityExpr ::= RelationalExpr ( EqualityOperator EqualityExpr )*
2223
2224 RETURN
2225 1 - success
2226 0 - failure
2227*/
2228static int my_xpath_parse_ne(MY_XPATH *xpath)
2229{
2230 MY_XPATH_LEX prevtok= xpath->prevtok;
2231 if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_EXCL))
2232 return 0;
2233 if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_EQ))
2234 {
2235 /* Unget the exclamation mark */
2236 xpath->lasttok= xpath->prevtok;
2237 xpath->prevtok= prevtok;
2238 return 0;
2239 }
2240 return 1;
2241}
2242static int my_xpath_parse_EqualityOperator(MY_XPATH *xpath)
2243{
2244 if (my_xpath_parse_ne(xpath))
2245 {
2246 xpath->extra= '!';
2247 return 1;
2248 }
2249 if (my_xpath_parse_term(xpath, MY_XPATH_LEX_EQ))
2250 {
2251 xpath->extra= '=';
2252 return 1;
2253 }
2254 return 0;
2255}
2256static int my_xpath_parse_EqualityExpr(MY_XPATH *xpath)
2257{
2258 MY_XPATH_LEX operator_context;
2259 if (!my_xpath_parse_RelationalExpr(xpath))
2260 return 0;
2261
2262 operator_context= xpath->lasttok;
2263 while (my_xpath_parse_EqualityOperator(xpath))
2264 {
2265 Item *prev= xpath->item;
2266 int oper= xpath->extra;
2267 if (!my_xpath_parse_RelationalExpr(xpath))
2268 {
2269 xpath->error= 1;
2270 return 0;
2271 }
2272
2273 if (!(xpath->item= create_comparator(xpath, oper, &operator_context,
2274 prev, xpath->item)))
2275 return 0;
2276
2277 operator_context= xpath->lasttok;
2278 }
2279 return 1;
2280}
2281
2282
2283/*
2284 Scan Relational Expression
2285
2286 SYNOPSYS
2287
2288 [24] RelationalExpr ::= AdditiveExpr
2289 | RelationalExpr '<' AdditiveExpr
2290 | RelationalExpr '>' AdditiveExpr
2291 | RelationalExpr '<=' AdditiveExpr
2292 | RelationalExpr '>=' AdditiveExpr
2293 or in other words:
2294
2295 [24] RelationalExpr ::= AdditiveExpr (RelationalOperator RelationalExpr)*
2296
2297 RETURN
2298 1 - success
2299 0 - failure
2300*/
2301static int my_xpath_parse_RelationalOperator(MY_XPATH *xpath)
2302{
2303 if (my_xpath_parse_term(xpath, MY_XPATH_LEX_LESS))
2304 {
2305 xpath->extra= my_xpath_parse_term(xpath, MY_XPATH_LEX_EQ) ?
2306 MY_XPATH_LEX_LE : MY_XPATH_LEX_LESS;
2307 return 1;
2308 }
2309 else if (my_xpath_parse_term(xpath, MY_XPATH_LEX_GREATER))
2310 {
2311 xpath->extra= my_xpath_parse_term(xpath, MY_XPATH_LEX_EQ) ?
2312 MY_XPATH_LEX_GE : MY_XPATH_LEX_GREATER;
2313 return 1;
2314 }
2315 return 0;
2316}
2317static int my_xpath_parse_RelationalExpr(MY_XPATH *xpath)
2318{
2319 MY_XPATH_LEX operator_context;
2320 if (!my_xpath_parse_AdditiveExpr(xpath))
2321 return 0;
2322 operator_context= xpath->lasttok;
2323 while (my_xpath_parse_RelationalOperator(xpath))
2324 {
2325 Item *prev= xpath->item;
2326 int oper= xpath->extra;
2327
2328 if (!my_xpath_parse_AdditiveExpr(xpath))
2329 {
2330 xpath->error= 1;
2331 return 0;
2332 }
2333
2334 if (!(xpath->item= create_comparator(xpath, oper, &operator_context,
2335 prev, xpath->item)))
2336 return 0;
2337 operator_context= xpath->lasttok;
2338 }
2339 return 1;
2340}
2341
2342
2343/*
2344 Scan Additive Expression
2345
2346 SYNOPSYS
2347
2348 [25] AdditiveExpr ::= MultiplicativeExpr
2349 | AdditiveExpr '+' MultiplicativeExpr
2350 | AdditiveExpr '-' MultiplicativeExpr
2351 RETURN
2352 1 - success
2353 0 - failure
2354*/
2355static int my_xpath_parse_AdditiveOperator(MY_XPATH *xpath)
2356{
2357 return my_xpath_parse_term(xpath, MY_XPATH_LEX_PLUS) ||
2358 my_xpath_parse_term(xpath, MY_XPATH_LEX_MINUS);
2359}
2360static int my_xpath_parse_AdditiveExpr(MY_XPATH *xpath)
2361{
2362 if (!my_xpath_parse_MultiplicativeExpr(xpath))
2363 return 0;
2364
2365 while (my_xpath_parse_AdditiveOperator(xpath))
2366 {
2367 int oper= xpath->prevtok.term;
2368 Item *prev= xpath->item;
2369 THD *thd= xpath->thd;
2370
2371 if (!my_xpath_parse_MultiplicativeExpr(xpath))
2372 {
2373 xpath->error= 1;
2374 return 0;
2375 }
2376
2377 if (oper == MY_XPATH_LEX_PLUS)
2378 xpath->item= new (thd->mem_root)
2379 Item_func_plus(thd, prev, xpath->item);
2380 else
2381 xpath->item= new (thd->mem_root)
2382 Item_func_minus(thd, prev, xpath->item);
2383 };
2384 return 1;
2385}
2386
2387
2388/*
2389 Scan Multiplicative Expression
2390
2391 SYNOPSYS
2392
2393 [26] MultiplicativeExpr ::= UnaryExpr
2394 | MultiplicativeExpr MultiplyOperator UnaryExpr
2395 | MultiplicativeExpr 'div' UnaryExpr
2396 | MultiplicativeExpr 'mod' UnaryExpr
2397 or in other words:
2398
2399 [26] MultiplicativeExpr ::= UnaryExpr (MulOper MultiplicativeExpr)*
2400
2401 RETURN
2402 1 - success
2403 0 - failure
2404*/
2405static int my_xpath_parse_MultiplicativeOperator(MY_XPATH *xpath)
2406{
2407 return
2408 my_xpath_parse_term(xpath, MY_XPATH_LEX_ASTERISK) ||
2409 my_xpath_parse_term(xpath, MY_XPATH_LEX_DIV) ||
2410 my_xpath_parse_term(xpath, MY_XPATH_LEX_MOD);
2411}
2412static int my_xpath_parse_MultiplicativeExpr(MY_XPATH *xpath)
2413{
2414 if (!my_xpath_parse_UnaryExpr(xpath))
2415 return 0;
2416
2417 THD *thd= xpath->thd;
2418 while (my_xpath_parse_MultiplicativeOperator(xpath))
2419 {
2420 int oper= xpath->prevtok.term;
2421 Item *prev= xpath->item;
2422 if (!my_xpath_parse_UnaryExpr(xpath))
2423 {
2424 xpath->error= 1;
2425 return 0;
2426 }
2427 switch (oper)
2428 {
2429 case MY_XPATH_LEX_ASTERISK:
2430 xpath->item= new (thd->mem_root) Item_func_mul(thd, prev, xpath->item);
2431 break;
2432 case MY_XPATH_LEX_DIV:
2433 xpath->item= new (thd->mem_root) Item_func_int_div(thd, prev, xpath->item);
2434 break;
2435 case MY_XPATH_LEX_MOD:
2436 xpath->item= new (thd->mem_root) Item_func_mod(thd, prev, xpath->item);
2437 break;
2438 }
2439 }
2440 return 1;
2441}
2442
2443
2444/*
2445 Scan Unary Expression
2446
2447 SYNOPSYS
2448
2449 [27] UnaryExpr ::= UnionExpr
2450 | '-' UnaryExpr
2451 RETURN
2452 1 - success
2453 0 - failure
2454*/
2455static int my_xpath_parse_UnaryExpr(MY_XPATH *xpath)
2456{
2457 if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_MINUS))
2458 return my_xpath_parse_UnionExpr(xpath);
2459 if (!my_xpath_parse_UnaryExpr(xpath))
2460 return 0;
2461 xpath->item= new (xpath->thd->mem_root)
2462 Item_func_neg(xpath->thd, xpath->item);
2463 return 1;
2464}
2465
2466
2467/**
2468 A helper class to make a null-terminated string from XPath fragments.
2469 The string is allocated on the THD memory root.
2470*/
2471class XPath_cstring_null_terminated: public LEX_CSTRING
2472{
2473public:
2474 XPath_cstring_null_terminated(THD *thd, const char *str, size_t length)
2475 {
2476 if (thd->make_lex_string(this, str, length))
2477 static_cast<LEX_CSTRING>(*this)= empty_clex_str;
2478 }
2479};
2480
2481
2482/*
2483 Scan Number
2484
2485 SYNOPSYS
2486
2487 [30] Number ::= Digits ('.' Digits?)? | '.' Digits)
2488
2489 or in other words:
2490
2491 [30] Number ::= Digits
2492 | Digits '.'
2493 | Digits '.' Digits
2494 | '.' Digits
2495
2496 Note: the last rule is not supported yet,
2497 as it is in conflict with abbreviated step.
2498 1 + .123 does not work,
2499 1 + 0.123 does.
2500 Perhaps it is better to move this code into lex analizer.
2501
2502 RETURN
2503 1 - success
2504 0 - failure
2505*/
2506static int my_xpath_parse_Number(MY_XPATH *xpath)
2507{
2508 const char *beg;
2509 THD *thd;
2510 if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_DIGITS))
2511 return 0;
2512 beg= xpath->prevtok.beg;
2513 thd= xpath->thd;
2514 if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_DOT))
2515 {
2516 XPath_cstring_null_terminated nr(thd, beg, xpath->prevtok.end - beg);
2517 xpath->item= new (thd->mem_root) Item_int(thd, nr.str, (uint) nr.length);
2518 }
2519 else
2520 {
2521 my_xpath_parse_term(xpath, MY_XPATH_LEX_DIGITS);
2522 XPath_cstring_null_terminated nr(thd, beg, xpath->prevtok.end - beg);
2523 xpath->item= new (thd->mem_root) Item_float(thd, nr.str, (uint) nr.length);
2524 }
2525 return 1;
2526}
2527
2528
2529/*
2530 Scan NCName.
2531
2532 SYNOPSYS
2533
2534 The keywords AND, OR, MOD, DIV are valid identitiers
2535 when they are in identifier context:
2536
2537 SELECT
2538 ExtractValue('<and><or><mod><div>VALUE</div></mod></or></and>',
2539 '/and/or/mod/div')
2540 -> VALUE
2541
2542 RETURN
2543 1 - success
2544 0 - failure
2545*/
2546
2547static int
2548my_xpath_parse_NCName(MY_XPATH *xpath)
2549{
2550 return
2551 my_xpath_parse_term(xpath, MY_XPATH_LEX_IDENT) ||
2552 my_xpath_parse_term(xpath, MY_XPATH_LEX_AND) ||
2553 my_xpath_parse_term(xpath, MY_XPATH_LEX_OR) ||
2554 my_xpath_parse_term(xpath, MY_XPATH_LEX_MOD) ||
2555 my_xpath_parse_term(xpath, MY_XPATH_LEX_DIV) ? 1 : 0;
2556}
2557
2558
2559/*
2560 QName grammar can be found in a separate document
2561 http://www.w3.org/TR/REC-xml-names/#NT-QName
2562
2563 [6] QName ::= (Prefix ':')? LocalPart
2564 [7] Prefix ::= NCName
2565 [8] LocalPart ::= NCName
2566*/
2567
2568static int
2569my_xpath_parse_QName(MY_XPATH *xpath)
2570{
2571 const char *beg;
2572 if (!my_xpath_parse_NCName(xpath))
2573 return 0;
2574 beg= xpath->prevtok.beg;
2575 if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_COLON))
2576 return 1; /* Non qualified name */
2577 if (!my_xpath_parse_NCName(xpath))
2578 return 0;
2579 xpath->prevtok.beg= beg;
2580 return 1;
2581}
2582
2583
2584/**
2585 Scan Variable reference
2586
2587 @details Implements parsing of two syntax structures:
2588
2589 1. Standard XPath syntax [36], for SP variables:
2590
2591 VariableReference ::= '$' QName
2592
2593 Finds a SP variable with the given name.
2594 If outside of a SP context, or variable with
2595 the given name doesn't exists, then error is returned.
2596
2597 2. Non-standard syntax - MySQL extension for user variables:
2598
2599 VariableReference ::= '$' '@' QName
2600
2601 Item, corresponding to the variable, is returned
2602 in xpath->item in both cases.
2603
2604 @param xpath pointer to XPath structure
2605
2606 @return Operation status
2607 @retval 1 Success
2608 @retval 0 Failure
2609*/
2610
2611static int
2612my_xpath_parse_VariableReference(MY_XPATH *xpath)
2613{
2614 LEX_CSTRING name;
2615 int user_var;
2616 const char *dollar_pos;
2617 THD *thd= xpath->thd;
2618 if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_DOLLAR) ||
2619 (!(dollar_pos= xpath->prevtok.beg)) ||
2620 (!((user_var= my_xpath_parse_term(xpath, MY_XPATH_LEX_AT) &&
2621 my_xpath_parse_term(xpath, MY_XPATH_LEX_IDENT))) &&
2622 !my_xpath_parse_term(xpath, MY_XPATH_LEX_IDENT)))
2623 return 0;
2624
2625 name.length= xpath->prevtok.end - xpath->prevtok.beg;
2626 name.str= (char*) xpath->prevtok.beg;
2627
2628 if (user_var)
2629 xpath->item= new (thd->mem_root) Item_func_get_user_var(thd, &name);
2630 else
2631 {
2632 sp_variable *spv;
2633 const Sp_rcontext_handler *rh;
2634 LEX *lex;
2635 /*
2636 We call lex->find_variable() rather than thd->lex->spcont->find_variable()
2637 to make sure package body variables are properly supported.
2638 */
2639 if ((lex= thd->lex) &&
2640 (spv= lex->find_variable(&name, &rh)))
2641 {
2642 Item_splocal *splocal= new (thd->mem_root)
2643 Item_splocal(thd, rh, &name, spv->offset, spv->type_handler(), 0);
2644#ifdef DBUG_ASSERT_EXISTS
2645 if (splocal)
2646 splocal->m_sp= lex->sphead;
2647#endif
2648 xpath->item= (Item*) splocal;
2649 }
2650 else
2651 {
2652 xpath->item= NULL;
2653 DBUG_ASSERT(xpath->query.end > dollar_pos);
2654 uint len= (uint)(xpath->query.end - dollar_pos);
2655 set_if_smaller(len, 32);
2656 my_printf_error(ER_UNKNOWN_ERROR, "Unknown XPATH variable at: '%.*s'",
2657 MYF(0), len, dollar_pos);
2658 }
2659 }
2660 return xpath->item ? 1 : 0;
2661}
2662
2663
2664/*
2665 Scan Name Test
2666
2667 SYNOPSYS
2668
2669 [37] NameTest ::= '*'
2670 | NCName ':' '*'
2671 | QName
2672 RETURN
2673 1 - success
2674 0 - failure
2675*/
2676static int
2677my_xpath_parse_NodeTest_QName(MY_XPATH *xpath)
2678{
2679 if (!my_xpath_parse_QName(xpath))
2680 return 0;
2681 DBUG_ASSERT(xpath->context);
2682 uint len= (uint)(xpath->prevtok.end - xpath->prevtok.beg);
2683 xpath->context= nametestfunc(xpath, xpath->axis, xpath->context,
2684 xpath->prevtok.beg, len);
2685 return 1;
2686}
2687static int
2688my_xpath_parse_NodeTest_asterisk(MY_XPATH *xpath)
2689{
2690 if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_ASTERISK))
2691 return 0;
2692 DBUG_ASSERT(xpath->context);
2693 xpath->context= nametestfunc(xpath, xpath->axis, xpath->context, "*", 1);
2694 return 1;
2695}
2696static int
2697my_xpath_parse_NameTest(MY_XPATH *xpath)
2698{
2699 return my_xpath_parse_NodeTest_asterisk(xpath) ||
2700 my_xpath_parse_NodeTest_QName(xpath);
2701}
2702
2703
2704/*
2705 Scan an XPath expression
2706
2707 SYNOPSYS
2708 Scan xpath expression.
2709 The expression is returned in xpath->expr.
2710
2711 RETURN
2712 1 - success
2713 0 - failure
2714*/
2715static int
2716my_xpath_parse(MY_XPATH *xpath, const char *str, const char *strend)
2717{
2718 my_xpath_lex_init(&xpath->query, str, strend);
2719 my_xpath_lex_init(&xpath->prevtok, str, strend);
2720 my_xpath_lex_scan(xpath, &xpath->lasttok, str, strend);
2721
2722 xpath->rootelement= new (xpath->thd->mem_root)
2723 Item_nodeset_func_rootelement(xpath->thd,
2724 xpath->pxml);
2725
2726 return (my_xpath_parse_Expr(xpath) &&
2727 my_xpath_parse_term(xpath, MY_XPATH_LEX_EOF));
2728}
2729
2730
2731void Item_xml_str_func::fix_length_and_dec()
2732{
2733 max_length= MAX_BLOB_WIDTH;
2734 agg_arg_charsets_for_comparison(collation, args, arg_count);
2735}
2736
2737
2738bool Item_xml_str_func::fix_fields(THD *thd, Item **ref)
2739{
2740 String *xp;
2741 MY_XPATH xpath;
2742 int rc;
2743
2744 if (Item_str_func::fix_fields(thd, ref))
2745 return true;
2746
2747 status_var_increment(current_thd->status_var.feature_xml);
2748
2749 nodeset_func= 0;
2750
2751
2752 if (collation.collation->mbminlen > 1)
2753 {
2754 /* UCS2 is not supported */
2755 my_printf_error(ER_UNKNOWN_ERROR,
2756 "Character set '%s' is not supported by XPATH",
2757 MYF(0), collation.collation->csname);
2758 return true;
2759 }
2760
2761 if (!args[1]->const_item())
2762 {
2763 my_printf_error(ER_UNKNOWN_ERROR,
2764 "Only constant XPATH queries are supported", MYF(0));
2765 return true;
2766 }
2767
2768 /*
2769 Get the XPath query text from args[1] and cache it in m_xpath_query.
2770 Its fragments will be referenced by items created during my_xpath_parse(),
2771 e.g. by Item_nodeset_func_axisbyname::node_name.
2772 */
2773 if (!(xp= args[1]->val_str(&m_xpath_query)) ||
2774 (xp != &m_xpath_query && m_xpath_query.copy(*xp)))
2775 return false; // Will return NULL
2776 my_xpath_init(&xpath);
2777 xpath.thd= thd;
2778 xpath.cs= collation.collation;
2779 xpath.debug= 0;
2780 xpath.pxml= xml.parsed();
2781 xml.set_charset(collation.collation);
2782
2783 rc= my_xpath_parse(&xpath, xp->ptr(), xp->ptr() + xp->length());
2784
2785 if (!rc)
2786 {
2787 uint clen= (uint)(xpath.query.end - xpath.lasttok.beg);
2788 set_if_smaller(clen, 32);
2789 my_printf_error(ER_UNKNOWN_ERROR, "XPATH syntax error: '%.*s'",
2790 MYF(0), clen, xpath.lasttok.beg);
2791 return true;
2792 }
2793
2794 /*
2795 Parsing XML is a heavy operation, so if the first argument is constant,
2796 then parse XML only one time and cache the parsed representation
2797 together with raw text representation.
2798
2799 Note, we cannot cache the entire function result even if
2800 the first and the second arguments are constants, because
2801 the XPath expression may have user and SP variable references,
2802 so the function result can vary between executions.
2803 */
2804 if ((args[0]->const_item() && get_xml(&xml, true)) ||
2805 !(nodeset_func= xpath.item))
2806 return false; // Will return NULL
2807
2808 return nodeset_func->fix_fields(thd, &nodeset_func);
2809}
2810
2811
2812#define MAX_LEVEL 256
2813typedef struct
2814{
2815 uint level;
2816 String *pxml; // parsed XML
2817 uint pos[MAX_LEVEL]; // Tag position stack
2818 uint parent; // Offset of the parent of the current node
2819} MY_XML_USER_DATA;
2820
2821
2822static bool
2823append_node(String *str, MY_XML_NODE *node)
2824{
2825 /*
2826 If "str" doesn't have space for a new node,
2827 it will allocate two times more space that it has had so far.
2828 (2*len+512) is a heuristic value,
2829 which gave the best performance during tests.
2830 The ideas behind this formula are:
2831 - It allows to have a very small number of reallocs:
2832 about 10 reallocs on a 1Mb-long XML value.
2833 - At the same time, it avoids excessive memory use.
2834 */
2835 if (str->reserve(sizeof(MY_XML_NODE), 2 * str->length() + 512))
2836 return TRUE;
2837 str->q_append((const char*) node, sizeof(MY_XML_NODE));
2838 return FALSE;
2839}
2840
2841
2842/*
2843 Process tag beginning
2844
2845 SYNOPSYS
2846
2847 A call-back function executed when XML parser
2848 is entering a tag or an attribue.
2849 Appends the new node into data->pxml.
2850 Increments data->level.
2851
2852 RETURN
2853 Currently only MY_XML_OK
2854*/
2855extern "C" int xml_enter(MY_XML_PARSER *st,const char *attr, size_t len);
2856
2857int xml_enter(MY_XML_PARSER *st,const char *attr, size_t len)
2858{
2859 MY_XML_USER_DATA *data= (MY_XML_USER_DATA*)st->user_data;
2860 uint numnodes= data->pxml->length() / sizeof(MY_XML_NODE);
2861 MY_XML_NODE node;
2862
2863 node.parent= data->parent; // Set parent for the new node to old parent
2864 data->parent= numnodes; // Remember current node as new parent
2865 DBUG_ASSERT(data->level < MAX_LEVEL);
2866 data->pos[data->level]= numnodes;
2867 if (data->level < MAX_LEVEL - 1)
2868 node.level= data->level++;
2869 else
2870 return MY_XML_ERROR;
2871 node.type= st->current_node_type; // TAG or ATTR
2872 node.beg= attr;
2873 node.end= attr + len;
2874 return append_node(data->pxml, &node) ? MY_XML_ERROR : MY_XML_OK;
2875}
2876
2877
2878/*
2879 Process text node
2880
2881 SYNOPSYS
2882
2883 A call-back function executed when XML parser
2884 is entering into a tag or an attribue textual value.
2885 The value is appended into data->pxml.
2886
2887 RETURN
2888 Currently only MY_XML_OK
2889*/
2890extern "C" int xml_value(MY_XML_PARSER *st,const char *attr, size_t len);
2891
2892int xml_value(MY_XML_PARSER *st,const char *attr, size_t len)
2893{
2894 MY_XML_USER_DATA *data= (MY_XML_USER_DATA*)st->user_data;
2895 MY_XML_NODE node;
2896
2897 node.parent= data->parent; // Set parent for the new text node to old parent
2898 node.level= data->level;
2899 node.type= MY_XML_NODE_TEXT;
2900 node.beg= attr;
2901 node.end= attr + len;
2902 return append_node(data->pxml, &node) ? MY_XML_ERROR : MY_XML_OK;
2903}
2904
2905
2906/*
2907 Leave a tag or an attribute
2908
2909 SYNOPSYS
2910
2911 A call-back function executed when XML parser
2912 is leaving a tag or an attribue.
2913 Decrements data->level.
2914
2915 RETURN
2916 Currently only MY_XML_OK
2917*/
2918extern "C" int xml_leave(MY_XML_PARSER *st,const char *attr, size_t len);
2919
2920int xml_leave(MY_XML_PARSER *st,const char *attr, size_t len)
2921{
2922 MY_XML_USER_DATA *data= (MY_XML_USER_DATA*)st->user_data;
2923 DBUG_ASSERT(data->level > 0);
2924 data->level--;
2925
2926 MY_XML_NODE *nodes= (MY_XML_NODE*) data->pxml->ptr();
2927 data->parent= nodes[data->parent].parent;
2928 nodes+= data->pos[data->level];
2929 nodes->tagend= st->cur;
2930
2931 return MY_XML_OK;
2932}
2933
2934
2935/*
2936 Parse raw XML
2937
2938 SYNOPSYS
2939
2940 RETURN
2941 false on success
2942 true on error
2943*/
2944bool Item_xml_str_func::XML::parse()
2945{
2946 MY_XML_PARSER p;
2947 MY_XML_USER_DATA user_data;
2948 int rc;
2949
2950 m_parsed_buf.length(0);
2951
2952 /* Prepare XML parser */
2953 my_xml_parser_create(&p);
2954 p.flags= MY_XML_FLAG_RELATIVE_NAMES | MY_XML_FLAG_SKIP_TEXT_NORMALIZATION;
2955 user_data.level= 0;
2956 user_data.pxml= &m_parsed_buf;
2957 user_data.parent= 0;
2958 my_xml_set_enter_handler(&p, xml_enter);
2959 my_xml_set_value_handler(&p, xml_value);
2960 my_xml_set_leave_handler(&p, xml_leave);
2961 my_xml_set_user_data(&p, (void*) &user_data);
2962
2963 /* Add root node */
2964 p.current_node_type= MY_XML_NODE_TAG;
2965 xml_enter(&p, m_raw_ptr->ptr(), 0);
2966
2967 /* Execute XML parser */
2968 if ((rc= my_xml_parse(&p, m_raw_ptr->ptr(), m_raw_ptr->length())) != MY_XML_OK)
2969 {
2970 THD *thd= current_thd;
2971 char buf[128];
2972 my_snprintf(buf, sizeof(buf)-1, "parse error at line %d pos %lu: %s",
2973 my_xml_error_lineno(&p) + 1,
2974 (ulong) my_xml_error_pos(&p) + 1,
2975 my_xml_error_string(&p));
2976 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
2977 ER_WRONG_VALUE,
2978 ER_THD(thd, ER_WRONG_VALUE), "XML", buf);
2979 m_raw_ptr= (String *) 0;
2980 }
2981 my_xml_parser_free(&p);
2982
2983 return rc != MY_XML_OK;
2984}
2985
2986
2987/*
2988 Parse the raw XML from the given source,
2989 optionally cache the raw XML,
2990 remember the pointer to the raw XML.
2991*/
2992bool Item_xml_str_func::XML::parse(String *raw_xml, bool cache)
2993{
2994 m_raw_ptr= raw_xml;
2995 if (cache)
2996 {
2997 m_cached= true;
2998 if (m_raw_ptr != &m_raw_buf && m_raw_buf.copy(*m_raw_ptr))
2999 {
3000 m_raw_ptr= (String *) 0;
3001 return true;
3002 }
3003 m_raw_ptr= &m_raw_buf;
3004 }
3005 return parse();
3006}
3007
3008
3009const MY_XML_NODE *Item_xml_str_func::XML::node(uint idx)
3010{
3011 const MY_XML_NODE *nodebeg= (MY_XML_NODE*) m_parsed_buf.ptr();
3012 DBUG_ASSERT(idx < m_parsed_buf.length() / sizeof (MY_XML_NODE));
3013 return nodebeg + idx;
3014}
3015
3016
3017String *Item_func_xml_extractvalue::val_str(String *str)
3018{
3019 String *res;
3020 null_value= 0;
3021 if (!nodeset_func || get_xml(&xml) ||
3022 !(res= nodeset_func->val_str(str)))
3023 {
3024 null_value= 1;
3025 return 0;
3026 }
3027 return res;
3028}
3029
3030
3031bool Item_func_xml_update::collect_result(String *str,
3032 const MY_XML_NODE *cut,
3033 const String *replace)
3034{
3035 uint offs= cut->type == MY_XML_NODE_TAG ? 1 : 0;
3036 const char *end= cut->tagend + offs;
3037 str->length(0);
3038 str->set_charset(collation.collation);
3039 return
3040 /* Put the XML part preceding the replaced piece */
3041 str->append(xml.raw()->ptr(), cut->beg - xml.raw()->ptr() - offs) ||
3042 /* Put the replacement */
3043 str->append(replace->ptr(), replace->length()) ||
3044 /* Put the XML part following the replaced piece */
3045 str->append(end, xml.raw()->ptr() + xml.raw()->length() - end);
3046}
3047
3048
3049String *Item_func_xml_update::val_str(String *str)
3050{
3051 String *nodeset, *rep;
3052
3053 null_value= 0;
3054 if (!nodeset_func || get_xml(&xml) ||
3055 !(rep= args[2]->val_str(&tmp_value3)) ||
3056 !(nodeset= nodeset_func->val_nodeset(&tmp_value2)))
3057 {
3058 null_value= 1;
3059 return 0;
3060 }
3061
3062 MY_XPATH_FLT *fltbeg= (MY_XPATH_FLT*) nodeset->ptr();
3063 MY_XPATH_FLT *fltend= (MY_XPATH_FLT*) (nodeset->ptr() + nodeset->length());
3064
3065 /* Allow replacing of one tag only */
3066 if (fltend - fltbeg != 1)
3067 {
3068 /* TODO: perhaps add a warning that more than one tag selected */
3069 return xml.raw();
3070 }
3071
3072 const MY_XML_NODE *nodebeg= xml.node(fltbeg->num);
3073
3074 if (!nodebeg->level)
3075 {
3076 /*
3077 Root element, without NameTest:
3078 UpdateXML(xml, '/', 'replacement');
3079 Just return the replacement string.
3080 */
3081 return rep;
3082 }
3083
3084 return collect_result(str, nodebeg, rep) ? (String *) NULL : str;
3085}
3086