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 */ |
56 | typedef 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 */ |
68 | typedef 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 */ |
77 | typedef 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 */ |
86 | typedef 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 */ |
97 | typedef 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 ; /* 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 */ |
118 | class XPathFilter :public String |
119 | { |
120 | public: |
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 | */ |
157 | class Item_nodeset_func :public Item_str_func |
158 | { |
159 | protected: |
160 | String tmp_value, tmp2_value; |
161 | MY_XPATH_FLT *fltbeg, *fltend; |
162 | MY_XML_NODE *nodebeg, *nodeend; |
163 | uint numnodes; |
164 | public: |
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 */ |
243 | class Item_nodeset_func_rootelement :public Item_nodeset_func |
244 | { |
245 | public: |
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 */ |
256 | class Item_nodeset_func_union :public Item_nodeset_func |
257 | { |
258 | public: |
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 */ |
269 | class Item_nodeset_func_axisbyname :public Item_nodeset_func |
270 | { |
271 | const char *node_name; |
272 | uint node_namelen; |
273 | public: |
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 */ |
289 | class Item_nodeset_func_selfbyname: public Item_nodeset_func_axisbyname |
290 | { |
291 | public: |
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 */ |
303 | class Item_nodeset_func_childbyname: public Item_nodeset_func_axisbyname |
304 | { |
305 | public: |
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 */ |
317 | class Item_nodeset_func_descendantbyname: public Item_nodeset_func_axisbyname |
318 | { |
319 | bool need_self; |
320 | public: |
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 */ |
333 | class Item_nodeset_func_ancestorbyname: public Item_nodeset_func_axisbyname |
334 | { |
335 | bool need_self; |
336 | public: |
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 */ |
349 | class Item_nodeset_func_parentbyname: public Item_nodeset_func_axisbyname |
350 | { |
351 | public: |
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 */ |
363 | class Item_nodeset_func_attributebyname: public Item_nodeset_func_axisbyname |
364 | { |
365 | public: |
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 | */ |
381 | class Item_nodeset_func_predicate :public Item_nodeset_func |
382 | { |
383 | public: |
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 */ |
394 | class Item_nodeset_func_elementbyindex :public Item_nodeset_func |
395 | { |
396 | public: |
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 | */ |
412 | class Item_xpath_cast_bool :public Item_bool_func |
413 | { |
414 | String *pxml; |
415 | String tmp_value; |
416 | public: |
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 | */ |
437 | class Item_xpath_cast_number :public Item_real_func |
438 | { |
439 | public: |
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 | */ |
451 | class Item_nodeset_context_cache :public Item_nodeset_func |
452 | { |
453 | public: |
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 | |
465 | class Item_func_xpath_position :public Item_long_func |
466 | { |
467 | String *pxml; |
468 | String tmp_value; |
469 | public: |
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 | |
486 | class Item_func_xpath_count :public Item_long_func |
487 | { |
488 | String *pxml; |
489 | String tmp_value; |
490 | public: |
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 | |
509 | class Item_func_xpath_sum :public Item_real_func |
510 | { |
511 | String *pxml; |
512 | String tmp_value; |
513 | public: |
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 | */ |
557 | class Item_string_xml_non_const: public Item_string |
558 | { |
559 | public: |
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 | |
581 | class Item_nodeset_to_const_comparator :public Item_bool_func |
582 | { |
583 | String *pxml; |
584 | String tmp_nodeset; |
585 | public: |
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 | |
632 | String *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 | |
640 | String * 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 | |
671 | String *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 | |
685 | String *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 | |
706 | String *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 | |
728 | String *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 | |
770 | String *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 | |
793 | String *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 | |
814 | String *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 | |
834 | String *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 | */ |
858 | static 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 | */ |
932 | static 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 | */ |
960 | static 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 | */ |
986 | static 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 | */ |
1048 | static 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 | */ |
1101 | static 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 | */ |
1118 | struct my_xpath_keyword_names_st |
1119 | { |
1120 | int tok; |
1121 | const char *name; |
1122 | size_t length; |
1123 | int ; |
1124 | }; |
1125 | |
1126 | |
1127 | static 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 | |
1137 | static 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 | |
1156 | static 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 | */ |
1176 | static int |
1177 | my_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 | |
1199 | static 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 | |
1205 | static 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 | |
1211 | static 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 | |
1218 | static 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 | |
1224 | static 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 | |
1230 | static 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 | |
1237 | static 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 | |
1244 | static 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 | |
1253 | static 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 | |
1262 | static 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 | |
1271 | static 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 | |
1280 | static 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 | |
1290 | static 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 | |
1297 | static 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 | |
1306 | static 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 | |
1314 | static 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 | */ |
1327 | static 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 | |
1335 | static 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 | |
1345 | static 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 | |
1355 | static 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 */ |
1365 | static 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 | */ |
1398 | MY_XPATH_FUNC * |
1399 | my_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 */ |
1420 | static void |
1421 | my_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 */ |
1430 | static void |
1431 | my_xpath_init(MY_XPATH *xpath) |
1432 | { |
1433 | bzero((void*)xpath, sizeof(xpath[0])); |
1434 | } |
1435 | |
1436 | |
1437 | static int |
1438 | my_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 | */ |
1455 | static void |
1456 | my_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 | */ |
1569 | static int |
1570 | my_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 | */ |
1593 | static 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 | |
1615 | static int my_xpath_parse_LocationPath(MY_XPATH *xpath); |
1616 | static int my_xpath_parse_AbsoluteLocationPath(MY_XPATH *xpath); |
1617 | static int my_xpath_parse_RelativeLocationPath(MY_XPATH *xpath); |
1618 | static int my_xpath_parse_AbbreviatedStep(MY_XPATH *xpath); |
1619 | static int my_xpath_parse_Step(MY_XPATH *xpath); |
1620 | static int my_xpath_parse_AxisSpecifier(MY_XPATH *xpath); |
1621 | static int my_xpath_parse_NodeTest(MY_XPATH *xpath); |
1622 | static int my_xpath_parse_AbbreviatedAxisSpecifier(MY_XPATH *xpath); |
1623 | static int my_xpath_parse_NameTest(MY_XPATH *xpath); |
1624 | static int my_xpath_parse_FunctionCall(MY_XPATH *xpath); |
1625 | static int my_xpath_parse_Number(MY_XPATH *xpath); |
1626 | static int my_xpath_parse_FilterExpr(MY_XPATH *xpath); |
1627 | static int my_xpath_parse_PathExpr(MY_XPATH *xpath); |
1628 | static int my_xpath_parse_OrExpr(MY_XPATH *xpath); |
1629 | static int my_xpath_parse_UnaryExpr(MY_XPATH *xpath); |
1630 | static int my_xpath_parse_MultiplicativeExpr(MY_XPATH *xpath); |
1631 | static int my_xpath_parse_AdditiveExpr(MY_XPATH *xpath); |
1632 | static int my_xpath_parse_RelationalExpr(MY_XPATH *xpath); |
1633 | static int my_xpath_parse_AndExpr(MY_XPATH *xpath); |
1634 | static int my_xpath_parse_EqualityExpr(MY_XPATH *xpath); |
1635 | static 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 | */ |
1650 | static 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 | */ |
1683 | static 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 | */ |
1728 | static 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 | */ |
1763 | static int |
1764 | my_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 | |
1815 | static 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 | */ |
1834 | static 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 | */ |
1853 | static 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 | */ |
1871 | static 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 | */ |
1887 | static 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 | */ |
1907 | static 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 | */ |
1925 | static 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 | */ |
1952 | static 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 | } |
1958 | static 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 | } |
1968 | static 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 | */ |
1991 | static 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 | |
2022 | right_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 | */ |
2041 | static 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 | */ |
2079 | static int |
2080 | my_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 | } |
2118 | static 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 | */ |
2142 | static 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 | */ |
2159 | static 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 | */ |
2191 | static 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 | */ |
2228 | static 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 | } |
2242 | static 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 | } |
2256 | static 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 | */ |
2301 | static 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 | } |
2317 | static 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 | */ |
2355 | static 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 | } |
2360 | static 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 | */ |
2405 | static 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 | } |
2412 | static 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 | */ |
2455 | static 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 | */ |
2471 | class XPath_cstring_null_terminated: public LEX_CSTRING |
2472 | { |
2473 | public: |
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 | */ |
2506 | static 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 | |
2547 | static int |
2548 | my_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 | |
2568 | static int |
2569 | my_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 | |
2611 | static int |
2612 | my_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 | */ |
2676 | static int |
2677 | my_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 | } |
2687 | static int |
2688 | my_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 | } |
2696 | static int |
2697 | my_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 | */ |
2715 | static int |
2716 | my_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 | |
2731 | void 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 | |
2738 | bool 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 |
2813 | typedef 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 | |
2822 | static bool |
2823 | append_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 | */ |
2855 | extern "C" int xml_enter(MY_XML_PARSER *st,const char *attr, size_t len); |
2856 | |
2857 | int 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 | */ |
2890 | extern "C" int xml_value(MY_XML_PARSER *st,const char *attr, size_t len); |
2891 | |
2892 | int 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 | */ |
2918 | extern "C" int xml_leave(MY_XML_PARSER *st,const char *attr, size_t len); |
2919 | |
2920 | int 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 | */ |
2944 | bool 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 | */ |
2992 | bool 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 | |
3009 | const 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 | |
3017 | String *Item_func_xml_extractvalue::(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 | |
3031 | bool 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 | |
3049 | String *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 | |