From c1b29aea3803e6e25287cfdf794f030538bdfa7c Mon Sep 17 00:00:00 2001 From: Gunnar Aastrand Grimnes Date: Wed, 18 Jan 2017 21:01:40 +0100 Subject: Fixed some BIND scoping issues Made Extend eval only forget vars not bound outside. Made Extend only collect vars not in "expr" This fixes #580 --- rdflib/plugins/sparql/algebra.py | 23 ++++++++++++++--------- rdflib/plugins/sparql/evaluate.py | 2 +- rdflib/plugins/sparql/sparql.py | 5 +++-- test/DAWG/rdflib/bindscope.rq | 6 ++++++ test/DAWG/rdflib/bindscope.srx | 22 ++++++++++++++++++++++ test/DAWG/rdflib/bindscope.ttl | 6 ++++++ test/DAWG/rdflib/bindscope2.rq | 9 +++++++++ test/DAWG/rdflib/bindscope2.tsv | 8 ++++++++ test/DAWG/rdflib/manifest.ttl | 30 ++++++++++++++++++++++++++++++ 9 files changed, 99 insertions(+), 12 deletions(-) create mode 100644 test/DAWG/rdflib/bindscope.rq create mode 100644 test/DAWG/rdflib/bindscope.srx create mode 100644 test/DAWG/rdflib/bindscope.ttl create mode 100644 test/DAWG/rdflib/bindscope2.rq create mode 100644 test/DAWG/rdflib/bindscope2.tsv diff --git a/rdflib/plugins/sparql/algebra.py b/rdflib/plugins/sparql/algebra.py index 4f24740f..026b58e4 100644 --- a/rdflib/plugins/sparql/algebra.py +++ b/rdflib/plugins/sparql/algebra.py @@ -435,16 +435,21 @@ def _addVars(x, children): if isinstance(x, Variable): return set([x]) elif isinstance(x, CompValue): - x["_vars"] = set(reduce(operator.or_, children, set())) - if x.name == "Bind": - return set([x.var]) - elif x.name == 'SubSelect': - if x.projection: - s = set(v.var or v.evar for v in x.projection) - else: - s = set() + if x.name == "Extend": + # vars only used in the expr for a bind should not be included + x["_vars"] = reduce(operator.or_, [ child for child,part in zip(children,x) if part!='expr' ], set()) + + return set([x.var]) # perhaps this should expose all vars here too? + else: + x["_vars"] = set(reduce(operator.or_, children, set())) + + if x.name == 'SubSelect': + if x.projection: + s = set(v.var or v.evar for v in x.projection) + else: + s = set() - return s + return s return reduce(operator.or_, children, set()) diff --git a/rdflib/plugins/sparql/evaluate.py b/rdflib/plugins/sparql/evaluate.py index aab3ca61..e79e5149 100644 --- a/rdflib/plugins/sparql/evaluate.py +++ b/rdflib/plugins/sparql/evaluate.py @@ -74,7 +74,7 @@ def evalExtend(ctx, extend): for c in evalPart(ctx, extend.p): try: - e = _eval(extend.expr, c.forget(ctx)) + e = _eval(extend.expr, c.forget(ctx, _except=extend._vars)) if isinstance(e, SPARQLError): raise e diff --git a/rdflib/plugins/sparql/sparql.py b/rdflib/plugins/sparql/sparql.py index 76eded8f..69505561 100644 --- a/rdflib/plugins/sparql/sparql.py +++ b/rdflib/plugins/sparql/sparql.py @@ -191,13 +191,14 @@ class FrozenBindings(FrozenDict): bnodes = property(_bnodes) now = property(_now) - def forget(self, before): + def forget(self, before, _except=None): """ return a frozen dict only of bindings made in self since before """ + if not _except : _except = [] - return FrozenBindings(self.ctx, (x for x in self.iteritems() if before[x[0]] is None)) + return FrozenBindings(self.ctx, (x for x in self.iteritems() if x[0] in _except or before[x[0]] is None)) def remember(self, these): """ diff --git a/test/DAWG/rdflib/bindscope.rq b/test/DAWG/rdflib/bindscope.rq new file mode 100644 index 00000000..35ae3904 --- /dev/null +++ b/test/DAWG/rdflib/bindscope.rq @@ -0,0 +1,6 @@ +PREFIX s: + +SELECT ?s ?o ?x WHERE { + ?x s:knows ?o . + { ?x a s:Person. BIND(?x as ?s) } +} diff --git a/test/DAWG/rdflib/bindscope.srx b/test/DAWG/rdflib/bindscope.srx new file mode 100644 index 00000000..91cd6c93 --- /dev/null +++ b/test/DAWG/rdflib/bindscope.srx @@ -0,0 +1,22 @@ + + + + + + + + + + + http://example.org/jane + + + http://example.org/john + + + http://example.org/jane + + + + + diff --git a/test/DAWG/rdflib/bindscope.ttl b/test/DAWG/rdflib/bindscope.ttl new file mode 100644 index 00000000..4f732820 --- /dev/null +++ b/test/DAWG/rdflib/bindscope.ttl @@ -0,0 +1,6 @@ +@prefix s: . +@prefix x: . + +x:john a s:Person ; s:name "John" . +x:jane a s:Person ; s:name "Jane" ; s:knows x:john. +x:ecorp a s:Organization ; s:name "Evil Corp" ; s:employee x:jane . \ No newline at end of file diff --git a/test/DAWG/rdflib/bindscope2.rq b/test/DAWG/rdflib/bindscope2.rq new file mode 100644 index 00000000..1e825d21 --- /dev/null +++ b/test/DAWG/rdflib/bindscope2.rq @@ -0,0 +1,9 @@ +PREFIX s: +PREFIX x: + +SELECT ?s ?p ?o ?x +WHERE { + ?x a s:Person . + { ?x ?p ?o . BIND (?x as ?s) } UNION + { ?s ?p ?x . BIND (?x as ?o) } +} diff --git a/test/DAWG/rdflib/bindscope2.tsv b/test/DAWG/rdflib/bindscope2.tsv new file mode 100644 index 00000000..8bed6717 --- /dev/null +++ b/test/DAWG/rdflib/bindscope2.tsv @@ -0,0 +1,8 @@ +?s ?p ?o ?x + + "John" + + + "Jane" + + \ No newline at end of file diff --git a/test/DAWG/rdflib/manifest.ttl b/test/DAWG/rdflib/manifest.ttl index 732c594f..144a8662 100644 --- a/test/DAWG/rdflib/manifest.ttl +++ b/test/DAWG/rdflib/manifest.ttl @@ -16,6 +16,8 @@ :whitespacedot :minusfilter :notexistsfilter + :bindscope + :bindscope2 ) . @@ -81,3 +83,31 @@ From https://github.com/RDFLib/rdflib/issues/615, contributed by https://github. qt:data ] ; mf:result . + + +:bindscope rdf:type mf:QueryEvaluationTest ; + mf:name "Scope of bind in groups"; + rdfs:comment """ + From https://github.com/RDFLib/rdflib/issues/580, contributed by https://github.com/pchampin +"""; + dawgt:approval dawgt:Approved ; + dawgt:approvedBy ; + mf:action + [ qt:query ; + qt:data ] ; + mf:result + . + + +:bindscope2 rdf:type mf:QueryEvaluationTest ; + mf:name "Scope of bind in groups2"; + rdfs:comment """ + From https://github.com/RDFLib/rdflib/issues/580, contributed by https://github.com/pchampin +"""; + dawgt:approval dawgt:Approved ; + dawgt:approvedBy ; + mf:action + [ qt:query ; + qt:data ] ; + mf:result + . -- cgit v1.2.1