# HG changeset patch # User Sylvain Thénault # Date 1268672874 -3600 # Node ID e623afd49356ca6ed89f2358e37967933ecaa919 # Parent 9a50539f01d10793ab37a12b00eee1af3474f95b [rql rewriting] handle case where we've and optional inlined relation in the original query. Also, we should append EXISTS even in subquery to avoid inserting duplicates in results diff -r 9a50539f01d1 -r e623afd49356 rqlrewrite.py --- a/rqlrewrite.py Mon Mar 15 18:06:32 2010 +0100 +++ b/rqlrewrite.py Mon Mar 15 18:07:54 2010 +0100 @@ -304,21 +304,33 @@ """introduce the given snippet in a subquery""" subselect = stmts.Select() selectvar = varmap[0] - subselect.append_selected(n.VariableRef( - subselect.get_variable(selectvar))) + subselectvar = subselect.get_variable(selectvar) + subselect.append_selected(n.VariableRef(subselectvar)) + snippetrqlst = n.Exists(transformedsnippet.copy(subselect)) aliases = [selectvar] - subselect.add_restriction(transformedsnippet.copy(subselect)) stinfo = self.varinfo['stinfo'] + need_null_test = False for rel in stinfo['relations']: rschema = self.schema.rschema(rel.r_type) if rschema.final or (rschema.inlined and - not rel in stinfo['rhsrelations']): - self.select.remove_node(rel) - rel.children[0].name = selectvar + not rel in stinfo['rhsrelations']): + rel.children[0].name = selectvar # XXX explain why subselect.add_restriction(rel.copy(subselect)) for vref in rel.children[1].iget_nodes(n.VariableRef): subselect.append_selected(vref.copy(subselect)) aliases.append(vref.name) + self.select.remove_node(rel) + # when some inlined relation has to be copied in the subquery, + # we need to test that either value is NULL or that the snippet + # condition is satisfied + if rschema.inlined and rel.optional: + need_null_test = True + if need_null_test: + snippetrqlst = n.Or( + n.make_relation(subselectvar, 'is', (None, None), n.Constant, + operator='IS'), + snippetrqlst) + subselect.add_restriction(snippetrqlst) if self.u_varname: # generate an identifier for the substitution argname = subselect.allocate_varname() diff -r 9a50539f01d1 -r e623afd49356 test/data/rewrite/schema.py --- a/test/data/rewrite/schema.py Mon Mar 15 18:06:32 2010 +0100 +++ b/test/data/rewrite/schema.py Mon Mar 15 18:07:54 2010 +0100 @@ -39,3 +39,10 @@ class require_state(RelationDefinition): subject = 'CWPermission' object = 'State' + + +class inlined_card(RelationDefinition): + subject = 'Affaire' + object = 'Card' + inlined = True + cardinality = '?*' diff -r 9a50539f01d1 -r e623afd49356 test/unittest_rqlrewrite.py --- a/test/unittest_rqlrewrite.py Mon Mar 15 18:06:32 2010 +0100 +++ b/test/unittest_rqlrewrite.py Mon Mar 15 18:07:54 2010 +0100 @@ -123,7 +123,7 @@ "EXISTS(2 in_state A, B in_group D, E require_state A, " "E name 'read', E require_group D, A is State, D is CWGroup, E is CWPermission)") - def test_optional_var(self): + def test_optional_var_base(self): card_constraint = ('X in_state S, U in_group G, P require_state S,' 'P name "read", P require_group G') rqlst = parse('Any A,C WHERE A documented_by C?') @@ -131,15 +131,41 @@ self.failUnlessEqual(rqlst.as_string(), "Any A,C WHERE A documented_by C?, A is Affaire " "WITH C BEING " - "(Any C WHERE C in_state B, D in_group F, G require_state B, G name 'read', " - "G require_group F, D eid %(A)s, C is Card)") + "(Any C WHERE EXISTS(C in_state B, D in_group F, G require_state B, G name 'read', " + "G require_group F), D eid %(A)s, C is Card)") rqlst = parse('Any A,C,T WHERE A documented_by C?, C title T') rewrite(rqlst, {('C', 'X'): (card_constraint,)}, {}) self.failUnlessEqual(rqlst.as_string(), "Any A,C,T WHERE A documented_by C?, A is Affaire " "WITH C,T BEING " - "(Any C,T WHERE C in_state B, D in_group F, G require_state B, G name 'read', " - "G require_group F, C title T, D eid %(A)s, C is Card)") + "(Any C,T WHERE C title T, EXISTS(C in_state B, D in_group F, " + "G require_state B, G name 'read', G require_group F), " + "D eid %(A)s, C is Card)") + + def test_optional_var_inlined(self): + c1 = ('X require_permission P') + c2 = ('X inlined_card O, O require_permission P') + rqlst = parse('Any C,A,R WHERE A? inlined_card C, A ref R') + rewrite(rqlst, {('C', 'X'): (c1,), + ('A', 'X'): (c2,), + }, {}) + # XXX suboptimal + self.failUnlessEqual(rqlst.as_string(), + "Any C,A,R WITH A,R,C BEING " + "(Any A,R,C WHERE A ref R, A? inlined_card C, " + "(A is NULL) OR (EXISTS(A inlined_card B, B require_permission D, " + "B is Card, D is CWPermission)), " + "A is Affaire, C is Card, EXISTS(C require_permission E, E is CWPermission))") + + # def test_optional_var_inlined_has_perm(self): + # c1 = ('X require_permission P') + # c2 = ('X inlined_card O, U has_read_permission O') + # rqlst = parse('Any C,A,R WHERE A? inlined_card C, A ref R') + # rewrite(rqlst, {('C', 'X'): (c1,), + # ('A', 'X'): (c2,), + # }, {}) + # self.failUnlessEqual(rqlst.as_string(), + # "") def test_relation_optimization_1_lhs(self): # since Card in_state State as monovalued cardinality, the in_state @@ -243,7 +269,7 @@ rewrite(rqlst, {('X', 'X'): (constraint,)}, {}) # ambiguity are kept in the sub-query, no need to be resolved using OR self.failUnlessEqual(rqlst.as_string(), - u"Any X,C WHERE X? documented_by C, C is Card WITH X BEING (Any X WHERE X concerne A, X is Affaire)") + u"Any X,C WHERE X? documented_by C, C is Card WITH X BEING (Any X WHERE EXISTS(X concerne A), X is Affaire)") def test_rrqlexpr_nonexistant_subject_1(self):