summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArseny Kapoulkine <arseny.kapoulkine@gmail.com>2014-10-27 18:50:09 -0700
committerArseny Kapoulkine <arseny.kapoulkine@gmail.com>2014-10-27 18:50:09 -0700
commitc64d4820b142e6df93ccf612d0e4717159a36591 (patch)
tree67b41215c627fa43283fb1b9a3e14e1bd66fa946
parentdbfe85a717b7ac8b9bd30eb51796811c0c651da9 (diff)
XPath: Optimize [position()=expr] and [last()]
To get more benefits from constant predicate/filter optimization we rewrite [position()=expr] predicates into [expr] for numeric expressions. Right now the rewrite is only for entire expressions - it may be beneficial to split complex expressions like [position()=constant and expr] into [constant][expr] but that is more complicated. last() does not depend on the node set contents so is "constant" as far as our optimization is concerned so we can evaluate it once.
-rw-r--r--src/pugixml.cpp10
-rw-r--r--tests/test_xpath_paths.cpp8
2 files changed, 17 insertions, 1 deletions
diff --git a/src/pugixml.cpp b/src/pugixml.cpp
index 67591a3..f0bfc31 100644
--- a/src/pugixml.cpp
+++ b/src/pugixml.cpp
@@ -9765,6 +9765,14 @@ PUGI__NS_BEGIN
if (_right) _right->optimize(alloc);
if (_next) _next->optimize(alloc);
+ // Rewrite [position()=expr] with [expr]
+ // Note that this step has to go before classification to recognize [position()=1]
+ if ((_type == ast_filter || _type == ast_predicate) &&
+ _right->_type == ast_op_equal && _right->_left->_type == ast_func_position && _right->_right->_rettype == xpath_type_number)
+ {
+ _right = _right->_right;
+ }
+
// Classify filter/predicate ops to perform various optimizations during evaluation
if (_type == ast_filter || _type == ast_predicate)
{
@@ -9772,7 +9780,7 @@ PUGI__NS_BEGIN
if (_right->_type == ast_number_constant && _right->_data.number == 1.0)
_test = predicate_constant_one;
- else if (_right->_rettype == xpath_type_number && (_right->_type == ast_number_constant || _right->_type == ast_variable))
+ else if (_right->_rettype == xpath_type_number && (_right->_type == ast_number_constant || _right->_type == ast_variable || _right->_type == ast_func_last))
_test = predicate_constant;
else if (_right->_rettype != xpath_type_number && _right->is_posinv_expr())
_test = predicate_posinv;
diff --git a/tests/test_xpath_paths.cpp b/tests/test_xpath_paths.cpp
index 046592a..df0dfa4 100644
--- a/tests/test_xpath_paths.cpp
+++ b/tests/test_xpath_paths.cpp
@@ -475,6 +475,14 @@ TEST_XML(xpath_paths_predicate_constant_boolean, "<node><chapter/><chapter/><cha
CHECK_XPATH_NODESET_VAR(n, STR("following-sibling::chapter[$true]"), &set) % 6 % 7;
}
+TEST_XML(xpath_paths_predicate_position_eq, "<node><chapter/><chapter/><chapter>3</chapter><chapter/><chapter/></node>")
+{
+ CHECK_XPATH_NODESET(doc, STR("node/chapter[position()=1]")) % 3;
+ CHECK_XPATH_NODESET(doc, STR("node/chapter[position()=2+2]")) % 7;
+ CHECK_XPATH_NODESET(doc, STR("node/chapter[position()=last()]")) % 8;
+ CHECK_XPATH_NODESET(doc, STR("node/chapter[position()=string()]")) % 5;
+}
+
TEST_XML(xpath_paths_predicate_several, "<node><employee/><employee secretary=''/><employee assistant=''/><employee secretary='' assistant=''/><employee assistant='' secretary=''/></node>")
{
xml_node n = doc.child(STR("node"));