cubicweb/test/unittest_rqlrewrite.py
changeset 12176 c23aa49b2336
parent 12175 c0ceadfc8aee
child 12177 8cc3af4d9c3a
equal deleted inserted replaced
12175:c0ceadfc8aee 12176:c23aa49b2336
    18 
    18 
    19 from six import string_types
    19 from six import string_types
    20 
    20 
    21 from logilab.common.testlib import unittest_main, TestCase
    21 from logilab.common.testlib import unittest_main, TestCase
    22 from logilab.common.testlib import mock_object
    22 from logilab.common.testlib import mock_object
       
    23 from logilab.common.decorators import monkeypatch
    23 from yams import BadSchemaDefinition
    24 from yams import BadSchemaDefinition
    24 from yams.buildobjs import RelationDefinition
    25 from yams.buildobjs import RelationDefinition
    25 from rql import parse, nodes, RQLHelper
    26 from rql import parse, nodes, RQLHelper
    26 
    27 
    27 from cubicweb import Unauthorized, rqlrewrite, devtools
    28 from cubicweb import Unauthorized, rqlrewrite, devtools
       
    29 from cubicweb.rqlrewrite import RQLRewriter
    28 from cubicweb.schema import RRQLExpression, ERQLExpression
    30 from cubicweb.schema import RRQLExpression, ERQLExpression
    29 from cubicweb.devtools import repotest
    31 from cubicweb.devtools import repotest
       
    32 from cubicweb.devtools.testlib import CubicWebTC
    30 
    33 
    31 
    34 
    32 def setUpModule(*args):
    35 def setUpModule(*args):
    33     global rqlhelper, schema
    36     global rqlhelper, schema
    34     config = devtools.TestServerConfiguration('data-rewrite', __file__)
    37     config = devtools.TestServerConfiguration('data-rewrite', __file__)
    38                                                object='State', cardinality='1*'))
    41                                                object='State', cardinality='1*'))
    39     rqlhelper = RQLHelper(schema, special_relations={'eid': 'uid',
    42     rqlhelper = RQLHelper(schema, special_relations={'eid': 'uid',
    40                                                      'has_text': 'fti'})
    43                                                      'has_text': 'fti'})
    41     repotest.do_monkey_patch()
    44     repotest.do_monkey_patch()
    42 
    45 
       
    46 
    43 def tearDownModule(*args):
    47 def tearDownModule(*args):
    44     repotest.undo_monkey_patch()
    48     repotest.undo_monkey_patch()
    45     global rqlhelper, schema
    49     global rqlhelper, schema
    46     del rqlhelper, schema
    50     del rqlhelper, schema
    47 
    51 
       
    52 
    48 def eid_func_map(eid):
    53 def eid_func_map(eid):
    49     return {1: 'CWUser',
    54     return {1: 'CWUser',
    50             2: 'Card',
    55             2: 'Card',
    51             3: 'Affaire'}[eid]
    56             3: 'Affaire'}[eid]
    52 
    57 
       
    58 
    53 def _prepare_rewriter(rewriter_cls, kwargs):
    59 def _prepare_rewriter(rewriter_cls, kwargs):
    54     class FakeVReg:
    60     class FakeVReg:
    55         schema = schema
    61         schema = schema
       
    62 
    56         @staticmethod
    63         @staticmethod
    57         def solutions(sqlcursor, rqlst, kwargs):
    64         def solutions(sqlcursor, rqlst, kwargs):
    58             rqlhelper.compute_solutions(rqlst, {'eid': eid_func_map}, kwargs=kwargs)
    65             rqlhelper.compute_solutions(rqlst, {'eid': eid_func_map}, kwargs=kwargs)
       
    66 
    59         class rqlhelper:
    67         class rqlhelper:
    60             @staticmethod
    68             @staticmethod
    61             def annotate(rqlst):
    69             def annotate(rqlst):
    62                 rqlhelper.annotate(rqlst)
    70                 rqlhelper.annotate(rqlst)
    63             @staticmethod
    71             @staticmethod
    64             def simplify(mainrqlst, needcopy=False):
    72             def simplify(mainrqlst, needcopy=False):
    65                 rqlhelper.simplify(rqlst, needcopy)
    73                 rqlhelper.simplify(rqlst, needcopy)
    66     return rewriter_cls(mock_object(vreg=FakeVReg, user=(mock_object(eid=1))))
    74     return rewriter_cls(mock_object(vreg=FakeVReg, user=(mock_object(eid=1))))
       
    75 
    67 
    76 
    68 def rewrite(rqlst, snippets_map, kwargs, existingvars=None):
    77 def rewrite(rqlst, snippets_map, kwargs, existingvars=None):
    69     rewriter = _prepare_rewriter(rqlrewrite.RQLRewriter, kwargs)
    78     rewriter = _prepare_rewriter(rqlrewrite.RQLRewriter, kwargs)
    70     # turn {(V1, V2): constraints} into [(varmap, constraints)]
    79     # turn {(V1, V2): constraints} into [(varmap, constraints)]
    71     snippets = []
    80     snippets = []
    92     rqlhelper.compute_solutions(rqlst.children[0], {'eid': eid_func_map}, kwargs=kwargs)
   101     rqlhelper.compute_solutions(rqlst.children[0], {'eid': eid_func_map}, kwargs=kwargs)
    93     rewriter.rewrite(rqlst.children[0], snippets, kwargs, existingvars)
   102     rewriter.rewrite(rqlst.children[0], snippets, kwargs, existingvars)
    94     check_vrefs(rqlst.children[0])
   103     check_vrefs(rqlst.children[0])
    95     return rewriter.rewritten
   104     return rewriter.rewritten
    96 
   105 
       
   106 
    97 def check_vrefs(node):
   107 def check_vrefs(node):
    98     vrefmaps = {}
   108     vrefmaps = {}
    99     selects = []
   109     selects = []
   100     for vref in node.iget_nodes(nodes.VariableRef):
   110     for vref in node.iget_nodes(nodes.VariableRef):
   101         stmt = vref.stmt
   111         stmt = vref.stmt
   102         try:
   112         try:
   103             vrefmaps[stmt].setdefault(vref.name, set()).add(vref)
   113             vrefmaps[stmt].setdefault(vref.name, set()).add(vref)
   104         except KeyError:
   114         except KeyError:
   105             vrefmaps[stmt] = {vref.name: set( (vref,) )}
   115             vrefmaps[stmt] = {vref.name: set((vref,))}
   106             selects.append(stmt)
   116             selects.append(stmt)
   107     assert node in selects, (node, selects)
   117     assert node in selects, (node, selects)
   108     for stmt in selects:
   118     for stmt in selects:
   109         for var in stmt.defined_vars.values():
   119         for var in stmt.defined_vars.values():
   110             assert var.stinfo['references']
   120             assert var.stinfo['references']
   111             vrefmap = vrefmaps[stmt]
   121             vrefmap = vrefmaps[stmt]
   112             assert not (var.stinfo['references'] ^ vrefmap[var.name]), (node.as_string(), var, var.stinfo['references'], vrefmap[var.name])
   122             assert not (var.stinfo['references'] ^ vrefmap[var.name]), (
       
   123                 node.as_string(), var, var.stinfo['references'], vrefmap[var.name])
   113 
   124 
   114 
   125 
   115 class RQLRewriteTC(TestCase):
   126 class RQLRewriteTC(TestCase):
   116     """a faire:
   127     """a faire:
   117 
   128 
   121     * "has_<ACTION>_permission" ?
   132     * "has_<ACTION>_permission" ?
   122     """
   133     """
   123 
   134 
   124     def test_base_var(self):
   135     def test_base_var(self):
   125         constraint = ('X in_state S, U in_group G, P require_state S,'
   136         constraint = ('X in_state S, U in_group G, P require_state S,'
   126                            'P name "read", P require_group G')
   137                       'P name "read", P require_group G')
   127         rqlst = parse(u'Card C')
   138         rqlst = parse(u'Card C')
   128         rewrite(rqlst, {('C', 'X'): (constraint,)}, {})
   139         rewrite(rqlst, {('C', 'X'): (constraint,)}, {})
   129         self.assertEqual(rqlst.as_string(),
   140         self.assertEqual(
   130                          u'Any C WHERE C is Card, B eid %(D)s, '
   141             rqlst.as_string(),
   131                           'EXISTS(C in_state A, B in_group E, F require_state A, '
   142             u'Any C WHERE C is Card, B eid %(D)s, '
   132                           'F name "read", F require_group E, A is State, E is CWGroup, F is CWPermission)')
   143             'EXISTS(C in_state A, B in_group E, F require_state A, '
       
   144             'F name "read", F require_group E, A is State, E is CWGroup, F is CWPermission)')
   133 
   145 
   134     def test_multiple_var(self):
   146     def test_multiple_var(self):
   135         card_constraint = ('X in_state S, U in_group G, P require_state S,'
   147         card_constraint = ('X in_state S, U in_group G, P require_state S,'
   136                            'P name "read", P require_group G')
   148                            'P name "read", P require_group G')
   137         affaire_constraints = ('X ref LIKE "PUBLIC%"', 'U in_group G, G name "public"')
   149         affaire_constraints = ('X ref LIKE "PUBLIC%"', 'U in_group G, G name "public"')
   138         kwargs = {'u':2}
   150         kwargs = {'u': 2}
   139         rqlst = parse(u'Any S WHERE S documented_by C, C eid %(u)s')
   151         rqlst = parse(u'Any S WHERE S documented_by C, C eid %(u)s')
   140         rewrite(rqlst, {('C', 'X'): (card_constraint,), ('S', 'X'): affaire_constraints},
   152         rewrite(rqlst, {('C', 'X'): (card_constraint,), ('S', 'X'): affaire_constraints},
   141                 kwargs)
   153                 kwargs)
   142         self.assertMultiLineEqual(
   154         self.assertMultiLineEqual(
   143             rqlst.as_string(),
   155             rqlst.as_string(),
   144             u'Any S WHERE S documented_by C, C eid %(u)s, B eid %(D)s, '
   156             u'Any S WHERE S documented_by C, C eid %(u)s, B eid %(D)s, '
   145              'EXISTS(C in_state A, B in_group E, F require_state A, '
   157             'EXISTS(C in_state A, B in_group E, F require_state A, '
   146              'F name "read", F require_group E, A is State, E is CWGroup, F is CWPermission), '
   158             'F name "read", F require_group E, A is State, E is CWGroup, F is CWPermission), '
   147              '(EXISTS(S ref LIKE "PUBLIC%")) OR (EXISTS(B in_group G, G name "public", G is CWGroup)), '
   159             '(EXISTS(S ref LIKE "PUBLIC%")) '
   148              'S is Affaire')
   160             'OR (EXISTS(B in_group G, G name "public", G is CWGroup)), '
       
   161             'S is Affaire')
   149         self.assertIn('D', kwargs)
   162         self.assertIn('D', kwargs)
   150 
   163 
   151     def test_or(self):
   164     def test_or(self):
   152         constraint = '(X identity U) OR (X in_state ST, CL identity U, CL in_state ST, ST name "subscribed")'
   165         constraint = (
       
   166             '(X identity U) OR '
       
   167             '(X in_state ST, CL identity U, CL in_state ST, ST name "subscribed")'
       
   168         )
   153         rqlst = parse(u'Any S WHERE S owned_by C, C eid %(u)s, S is in (CWUser, CWGroup)')
   169         rqlst = parse(u'Any S WHERE S owned_by C, C eid %(u)s, S is in (CWUser, CWGroup)')
   154         rewrite(rqlst, {('C', 'X'): (constraint,)}, {'u':1})
   170         rewrite(rqlst, {('C', 'X'): (constraint,)}, {'u': 1})
   155         self.assertEqual(rqlst.as_string(),
   171         self.assertEqual(
   156                          'Any S WHERE S owned_by C, C eid %(u)s, S is IN(CWUser, CWGroup), A eid %(B)s, '
   172             rqlst.as_string(),
   157                          'EXISTS((C identity A) OR (C in_state D, E identity A, '
   173             'Any S WHERE S owned_by C, C eid %(u)s, S is IN(CWUser, CWGroup), A eid %(B)s, '
   158                          'E in_state D, D name "subscribed"), D is State, E is CWUser)')
   174             'EXISTS((C identity A) OR (C in_state D, E identity A, '
       
   175             'E in_state D, D name "subscribed"), D is State, E is CWUser)')
   159 
   176 
   160     def test_simplified_rqlst(self):
   177     def test_simplified_rqlst(self):
   161         constraint = ('X in_state S, U in_group G, P require_state S,'
   178         constraint = ('X in_state S, U in_group G, P require_state S,'
   162                            'P name "read", P require_group G')
   179                       'P name "read", P require_group G')
   163         rqlst = parse(u'Any 2') # this is the simplified rql st for Any X WHERE X eid 12
   180         rqlst = parse(u'Any 2')  # this is the simplified rql st for Any X WHERE X eid 12
   164         rewrite(rqlst, {('2', 'X'): (constraint,)}, {})
   181         rewrite(rqlst, {('2', 'X'): (constraint,)}, {})
   165         self.assertEqual(rqlst.as_string(),
   182         self.assertEqual(
   166                          u'Any 2 WHERE B eid %(C)s, '
   183             rqlst.as_string(),
   167                           'EXISTS(2 in_state A, B in_group D, E require_state A, '
   184             u'Any 2 WHERE B eid %(C)s, '
   168                           'E name "read", E require_group D, A is State, D is CWGroup, E is CWPermission)')
   185             'EXISTS(2 in_state A, B in_group D, E require_state A, '
       
   186             'E name "read", E require_group D, A is State, D is CWGroup, E is CWPermission)')
   169 
   187 
   170     def test_optional_var_1(self):
   188     def test_optional_var_1(self):
   171         constraint = ('X in_state S, U in_group G, P require_state S,'
   189         constraint = ('X in_state S, U in_group G, P require_state S,'
   172                            'P name "read", P require_group G')
   190                       'P name "read", P require_group G')
   173         rqlst = parse(u'Any A,C WHERE A documented_by C?')
   191         rqlst = parse(u'Any A,C WHERE A documented_by C?')
   174         rewrite(rqlst, {('C', 'X'): (constraint,)}, {})
   192         rewrite(rqlst, {('C', 'X'): (constraint,)}, {})
   175         self.assertEqual(rqlst.as_string(),
   193         self.assertEqual(
   176                          u'Any A,C WHERE A documented_by C?, A is Affaire '
   194             rqlst.as_string(),
   177                           'WITH C BEING '
   195             u'Any A,C WHERE A documented_by C?, A is Affaire '
   178                           '(Any C WHERE EXISTS(C in_state B, D in_group F, G require_state B, G name "read", '
   196             'WITH C BEING '
   179                           'G require_group F), D eid %(A)s, C is Card)')
   197             '(Any C WHERE EXISTS(C in_state B, D in_group F, G require_state B, G name "read", '
       
   198             'G require_group F), D eid %(A)s, C is Card)')
   180 
   199 
   181     def test_optional_var_2(self):
   200     def test_optional_var_2(self):
   182         constraint = ('X in_state S, U in_group G, P require_state S,'
   201         constraint = ('X in_state S, U in_group G, P require_state S,'
   183                            'P name "read", P require_group G')
   202                       'P name "read", P require_group G')
   184         rqlst = parse(u'Any A,C,T WHERE A documented_by C?, C title T')
   203         rqlst = parse(u'Any A,C,T WHERE A documented_by C?, C title T')
   185         rewrite(rqlst, {('C', 'X'): (constraint,)}, {})
   204         rewrite(rqlst, {('C', 'X'): (constraint,)}, {})
   186         self.assertEqual(rqlst.as_string(),
   205         self.assertEqual(
   187                          u'Any A,C,T WHERE A documented_by C?, A is Affaire '
   206             rqlst.as_string(),
   188                           'WITH C,T BEING '
   207             u'Any A,C,T WHERE A documented_by C?, A is Affaire '
   189                           '(Any C,T WHERE C title T, EXISTS(C in_state B, D in_group F, '
   208             'WITH C,T BEING '
   190                           'G require_state B, G name "read", G require_group F), '
   209             '(Any C,T WHERE C title T, EXISTS(C in_state B, D in_group F, '
   191                           'D eid %(A)s, C is Card)')
   210             'G require_state B, G name "read", G require_group F), '
       
   211             'D eid %(A)s, C is Card)')
   192 
   212 
   193     def test_optional_var_3(self):
   213     def test_optional_var_3(self):
   194         constraint1 = ('X in_state S, U in_group G, P require_state S,'
   214         constraint1 = ('X in_state S, U in_group G, P require_state S,'
   195                        'P name "read", P require_group G')
   215                        'P name "read", P require_group G')
   196         constraint2 = 'X in_state S, S name "public"'
   216         constraint2 = 'X in_state S, S name "public"'
   197         rqlst = parse(u'Any A,C,T WHERE A documented_by C?, C title T')
   217         rqlst = parse(u'Any A,C,T WHERE A documented_by C?, C title T')
   198         rewrite(rqlst, {('C', 'X'): (constraint1, constraint2)}, {})
   218         rewrite(rqlst, {('C', 'X'): (constraint1, constraint2)}, {})
   199         self.assertEqual(rqlst.as_string(),
   219         self.assertEqual(
   200                          u'Any A,C,T WHERE A documented_by C?, A is Affaire '
   220             rqlst.as_string(),
   201                           'WITH C,T BEING (Any C,T WHERE C title T, '
   221             u'Any A,C,T WHERE A documented_by C?, A is Affaire '
   202                           '(EXISTS(C in_state B, D in_group F, G require_state B, G name "read", G require_group F)) '
   222             'WITH C,T BEING (Any C,T WHERE C title T, '
   203                           'OR (EXISTS(C in_state E, E name "public")), '
   223             '(EXISTS(C in_state B, D in_group F, G require_state B, '
   204                           'D eid %(A)s, C is Card)')
   224             'G name "read", G require_group F)) '
       
   225             'OR (EXISTS(C in_state E, E name "public")), '
       
   226             'D eid %(A)s, C is Card)')
   205 
   227 
   206     def test_optional_var_4(self):
   228     def test_optional_var_4(self):
   207         constraint1 = 'A created_by U, X documented_by A'
   229         constraint1 = 'A created_by U, X documented_by A'
   208         constraint2 = 'A created_by U, X concerne A'
   230         constraint2 = 'A created_by U, X concerne A'
   209         constraint3 = 'X created_by U'
   231         constraint3 = 'X created_by U'
   210         rqlst = parse(u'Any X,LA,Y WHERE LA? documented_by X, LA concerne Y')
   232         rqlst = parse(u'Any X,LA,Y WHERE LA? documented_by X, LA concerne Y')
   211         rewrite(rqlst, {('LA', 'X'): (constraint1, constraint2),
   233         rewrite(rqlst, {('LA', 'X'): (constraint1, constraint2),
   212                         ('X', 'X'): (constraint3,),
   234                         ('X', 'X'): (constraint3,),
   213                         ('Y', 'X'): (constraint3,)}, {})
   235                         ('Y', 'X'): (constraint3,)}, {})
   214         self.assertEqual(rqlst.as_string(),
   236         self.assertEqual(
   215                              u'Any X,LA,Y WHERE LA? documented_by X, LA concerne Y, B eid %(C)s, '
   237             rqlst.as_string(),
   216                              'EXISTS(X created_by B), EXISTS(Y created_by B), '
   238             u'Any X,LA,Y WHERE LA? documented_by X, LA concerne Y, B eid %(C)s, '
   217                              'X is Card, Y is IN(Division, Note, Societe) '
   239             'EXISTS(X created_by B), EXISTS(Y created_by B), '
   218                              'WITH LA BEING (Any LA WHERE (EXISTS(A created_by B, LA documented_by A)) OR (EXISTS(E created_by B, LA concerne E)), '
   240             'X is Card, Y is IN(Division, Note, Societe) '
   219                              'B eid %(D)s, LA is Affaire)')
   241             'WITH LA BEING (Any LA WHERE (EXISTS(A created_by B, LA documented_by A)) '
   220 
   242             'OR (EXISTS(E created_by B, LA concerne E)), '
       
   243             'B eid %(D)s, LA is Affaire)')
   221 
   244 
   222     def test_ambiguous_optional_same_exprs(self):
   245     def test_ambiguous_optional_same_exprs(self):
   223         """See #3013535"""
   246         """See #3013535"""
   224         # see test of the same name in RewriteFullTC: original problem is
   247         # see test of the same name in RewriteFullTC: original problem is
   225         # unreproducible here because it actually lies in
   248         # unreproducible here because it actually lies in
   226         # RQLRewriter.insert_local_checks
   249         # RQLRewriter.insert_local_checks
   227         rqlst = parse(u'Any A,AR,X,CD WHERE A concerne X?, A ref AR, A eid %(a)s, X creation_date CD')
   250         rqlst = parse(u'Any A,AR,X,CD WHERE A concerne X?, A ref AR, '
   228         rewrite(rqlst, {('X', 'X'): ('X created_by U',),}, {'a': 3})
   251                       'A eid %(a)s, X creation_date CD')
   229         self.assertEqual(rqlst.as_string(),
   252         rewrite(rqlst, {('X', 'X'): ('X created_by U',)}, {'a': 3})
   230                          u'Any A,AR,X,CD WHERE A concerne X?, A ref AR, A eid %(a)s WITH X,CD BEING (Any X,CD WHERE X creation_date CD, EXISTS(X created_by B), B eid %(A)s, X is IN(Division, Note, Societe))')
   253         self.assertEqual(
       
   254             rqlst.as_string(),
       
   255             u'Any A,AR,X,CD WHERE A concerne X?, A ref AR, A eid %(a)s '
       
   256             'WITH X,CD BEING (Any X,CD WHERE X creation_date CD, '
       
   257             'EXISTS(X created_by B), B eid %(A)s, X is IN(Division, Note, Societe))')
   231 
   258 
   232     def test_ambiguous_optional_same_exprs_constant(self):
   259     def test_ambiguous_optional_same_exprs_constant(self):
   233         rqlst = parse(u'Any A,AR,X WHERE A concerne X?, A ref AR, A eid %(a)s, X creation_date TODAY')
   260         rqlst = parse(u'Any A,AR,X WHERE A concerne X?, A ref AR, '
   234         rewrite(rqlst, {('X', 'X'): ('X created_by U',),}, {'a': 3})
   261                       'A eid %(a)s, X creation_date TODAY')
   235         self.assertEqual(rqlst.as_string(),
   262         rewrite(rqlst, {('X', 'X'): ('X created_by U',)}, {'a': 3})
   236                          u'Any A,AR,X WHERE A concerne X?, A ref AR, A eid %(a)s WITH X BEING (Any X WHERE X creation_date TODAY, EXISTS(X created_by B), B eid %(A)s, X is IN(Division, Note, Societe))')
   263         self.assertEqual(
       
   264             rqlst.as_string(),
       
   265             u'Any A,AR,X WHERE A concerne X?, A ref AR, A eid %(a)s '
       
   266             'WITH X BEING (Any X WHERE X creation_date TODAY, '
       
   267             'EXISTS(X created_by B), B eid %(A)s, X is IN(Division, Note, Societe))')
   237 
   268 
   238     def test_optional_var_inlined(self):
   269     def test_optional_var_inlined(self):
   239         c1 = ('X require_permission P')
   270         c1 = ('X require_permission P')
   240         c2 = ('X inlined_card O, O require_permission P')
   271         c2 = ('X inlined_card O, O require_permission P')
   241         rqlst = parse(u'Any C,A,R WHERE A? inlined_card C, A ref R')
   272         rqlst = parse(u'Any C,A,R WHERE A? inlined_card C, A ref R')
   242         rewrite(rqlst, {('C', 'X'): (c1,),
   273         rewrite(rqlst, {('C', 'X'): (c1,),
   243                         ('A', 'X'): (c2,),
   274                         ('A', 'X'): (c2,),
   244                         }, {})
   275                         }, {})
   245         # XXX suboptimal
   276         # XXX suboptimal
   246         self.assertEqual(rqlst.as_string(),
   277         self.assertEqual(
   247                          "Any C,A,R WITH A,C,R BEING "
   278             rqlst.as_string(),
   248                          "(Any A,C,R WHERE A? inlined_card C, A ref R, "
   279             "Any C,A,R WITH A,C,R BEING "
   249                          "(A is NULL) OR (EXISTS(A inlined_card B, B require_permission D, "
   280             "(Any A,C,R WHERE A? inlined_card C, A ref R, "
   250                          "B is Card, D is CWPermission)), "
   281             "(A is NULL) OR (EXISTS(A inlined_card B, B require_permission D, "
   251                          "A is Affaire, C is Card, EXISTS(C require_permission E, E is CWPermission))")
   282             "B is Card, D is CWPermission)), "
       
   283             "A is Affaire, C is Card, EXISTS(C require_permission E, E is CWPermission))")
   252 
   284 
   253     # def test_optional_var_inlined_has_perm(self):
   285     # def test_optional_var_inlined_has_perm(self):
   254     #     c1 = ('X require_permission P')
   286     #     c1 = ('X require_permission P')
   255     #     c2 = ('X inlined_card O, U has_read_permission O')
   287     #     c2 = ('X inlined_card O, U has_read_permission O')
   256     #     rqlst = parse(u'Any C,A,R WHERE A? inlined_card C, A ref R')
   288     #     rqlst = parse(u'Any C,A,R WHERE A? inlined_card C, A ref R')
   261     #                          "")
   293     #                          "")
   262 
   294 
   263     def test_optional_var_inlined_imbricated_error(self):
   295     def test_optional_var_inlined_imbricated_error(self):
   264         c1 = ('X require_permission P')
   296         c1 = ('X require_permission P')
   265         c2 = ('X inlined_card O, O require_permission P')
   297         c2 = ('X inlined_card O, O require_permission P')
   266         rqlst = parse(u'Any C,A,R,A2,R2 WHERE A? inlined_card C, A ref R,A2? inlined_card C, A2 ref R2')
   298         rqlst = parse(u'Any C,A,R,A2,R2 WHERE A? inlined_card C, A ref R,'
       
   299                       'A2? inlined_card C, A2 ref R2')
   267         self.assertRaises(BadSchemaDefinition,
   300         self.assertRaises(BadSchemaDefinition,
   268                           rewrite, rqlst, {('C', 'X'): (c1,),
   301                           rewrite, rqlst, {('C', 'X'): (c1,),
   269                                            ('A', 'X'): (c2,),
   302                                            ('A', 'X'): (c2,),
   270                                            ('A2', 'X'): (c2,),
   303                                            ('A2', 'X'): (c2,),
   271                                            }, {})
   304                                            }, {})
   272 
   305 
   273     def test_optional_var_inlined_linked(self):
   306     def test_optional_var_inlined_linked(self):
   274         c1 = ('X require_permission P')
   307         c1 = ('X require_permission P')
   275         c2 = ('X inlined_card O, O require_permission P')
       
   276         rqlst = parse(u'Any A,W WHERE A inlined_card C?, C inlined_note N, '
   308         rqlst = parse(u'Any A,W WHERE A inlined_card C?, C inlined_note N, '
   277                       'N inlined_affaire W')
   309                       'N inlined_affaire W')
   278         rewrite(rqlst, {('C', 'X'): (c1,)}, {})
   310         rewrite(rqlst, {('C', 'X'): (c1,)}, {})
   279         self.assertEqual(rqlst.as_string(),
   311         self.assertEqual(rqlst.as_string(),
   280                          'Any A,W WHERE A inlined_card C?, A is Affaire '
   312                          'Any A,W WHERE A inlined_card C?, A is Affaire '
   307         rqlst = parse(u'Card C WHERE C in_state STATE?')
   339         rqlst = parse(u'Card C WHERE C in_state STATE?')
   308         rewrite(rqlst, {('C', 'X'): (snippet,)}, {})
   340         rewrite(rqlst, {('C', 'X'): (snippet,)}, {})
   309         self.assertEqual(rqlst.as_string(),
   341         self.assertEqual(rqlst.as_string(),
   310                          'Any C WHERE C in_state STATE?, C is Card, '
   342                          'Any C WHERE C in_state STATE?, C is Card, '
   311                          'EXISTS(STATE name "hop"), STATE is State')
   343                          'EXISTS(STATE name "hop"), STATE is State')
       
   344 
   312     def test_relation_optimization_2_rhs(self):
   345     def test_relation_optimization_2_rhs(self):
   313         snippet = ('TW? subworkflow_exit X, TW name "hop"')
   346         snippet = ('TW? subworkflow_exit X, TW name "hop"')
   314         rqlst = parse(u'SubWorkflowExitPoint EXIT WHERE C? subworkflow_exit EXIT')
   347         rqlst = parse(u'SubWorkflowExitPoint EXIT WHERE C? subworkflow_exit EXIT')
   315         rewrite(rqlst, {('EXIT', 'X'): (snippet,)}, {})
   348         rewrite(rqlst, {('EXIT', 'X'): (snippet,)}, {})
   316         self.assertEqual(rqlst.as_string(),
   349         self.assertEqual(rqlst.as_string(),
   374         self.assertRaises(Unauthorized, rewrite, rqlst, {('T', 'X'): (trinfo_constraint,)}, {})
   407         self.assertRaises(Unauthorized, rewrite, rqlst, {('T', 'X'): (trinfo_constraint,)}, {})
   375 
   408 
   376     def test_unsupported_constraint_2(self):
   409     def test_unsupported_constraint_2(self):
   377         trinfo_constraint = ('X wf_info_for Y, Y require_permission P, P name "read"')
   410         trinfo_constraint = ('X wf_info_for Y, Y require_permission P, P name "read"')
   378         rqlst = parse(u'Any U,T WHERE U is CWUser, T wf_info_for U')
   411         rqlst = parse(u'Any U,T WHERE U is CWUser, T wf_info_for U')
   379         rewrite(rqlst, {('T', 'X'): (trinfo_constraint, 'X wf_info_for Y, Y in_group G, G name "managers"')}, {})
   412         rewrite(rqlst, {('T', 'X'): (trinfo_constraint,
       
   413                                      'X wf_info_for Y, Y in_group G, G name "managers"')}, {})
   380         self.assertEqual(rqlst.as_string(),
   414         self.assertEqual(rqlst.as_string(),
   381                          u'Any U,T WHERE U is CWUser, T wf_info_for U, '
   415                          u'Any U,T WHERE U is CWUser, T wf_info_for U, '
   382                           'EXISTS(U in_group B, B name "managers", B is CWGroup), T is TrInfo')
   416                          u'EXISTS(U in_group B, B name "managers", B is CWGroup), T is TrInfo')
   383 
   417 
   384     def test_unsupported_constraint_3(self):
   418     def test_unsupported_constraint_3(self):
   385         self.skipTest('raise unauthorized for now')
   419         self.skipTest('raise unauthorized for now')
   386         trinfo_constraint = ('X wf_info_for Y, Y require_permission P, P name "read"')
   420         trinfo_constraint = ('X wf_info_for Y, Y require_permission P, P name "read"')
   387         rqlst = parse(u'Any T WHERE T wf_info_for X')
   421         rqlst = parse(u'Any T WHERE T wf_info_for X')
   391 
   425 
   392     def test_add_ambiguity_exists(self):
   426     def test_add_ambiguity_exists(self):
   393         constraint = ('X concerne Y')
   427         constraint = ('X concerne Y')
   394         rqlst = parse(u'Affaire X')
   428         rqlst = parse(u'Affaire X')
   395         rewrite(rqlst, {('X', 'X'): (constraint,)}, {})
   429         rewrite(rqlst, {('X', 'X'): (constraint,)}, {})
   396         self.assertEqual(rqlst.as_string(),
   430         self.assertEqual(
   397                          u"Any X WHERE X is Affaire, ((EXISTS(X concerne A, A is Division)) OR (EXISTS(X concerne C, C is Societe))) OR (EXISTS(X concerne B, B is Note))")
   431             rqlst.as_string(),
       
   432             u"Any X WHERE X is Affaire, ((EXISTS(X concerne A, A is Division)) "
       
   433             "OR (EXISTS(X concerne C, C is Societe))) OR (EXISTS(X concerne B, B is Note))")
   398 
   434 
   399     def test_add_ambiguity_outerjoin(self):
   435     def test_add_ambiguity_outerjoin(self):
   400         constraint = ('X concerne Y')
   436         constraint = ('X concerne Y')
   401         rqlst = parse(u'Any X,C WHERE X? documented_by C')
   437         rqlst = parse(u'Any X,C WHERE X? documented_by C')
   402         rewrite(rqlst, {('X', 'X'): (constraint,)}, {})
   438         rewrite(rqlst, {('X', 'X'): (constraint,)}, {})
   403         # ambiguity are kept in the sub-query, no need to be resolved using OR
   439         # ambiguity are kept in the sub-query, no need to be resolved using OR
   404         self.assertEqual(rqlst.as_string(),
   440         self.assertEqual(
   405                          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)")
   441             rqlst.as_string(),
   406 
   442             u"Any X,C WHERE X? documented_by C, C is Card "
       
   443             "WITH X BEING (Any X WHERE EXISTS(X concerne A), X is Affaire)")
   407 
   444 
   408     def test_rrqlexpr_nonexistant_subject_1(self):
   445     def test_rrqlexpr_nonexistant_subject_1(self):
   409         constraint = RRQLExpression('S owned_by U')
   446         constraint = RRQLExpression('S owned_by U')
   410         rqlst = parse(u'Card C')
   447         rqlst = parse(u'Card C')
   411         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'SU')
   448         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'SU')
   430         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'OU')
   467         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'OU')
   431         self.assertEqual(rqlst.as_string(),
   468         self.assertEqual(rqlst.as_string(),
   432                          'Any C WHERE C is Card, B eid %(D)s, EXISTS(A owned_by B, A is Card)')
   469                          'Any C WHERE C is Card, B eid %(D)s, EXISTS(A owned_by B, A is Card)')
   433         rqlst = parse(u'Card C')
   470         rqlst = parse(u'Card C')
   434         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'SOU')
   471         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'SOU')
   435         self.assertEqual(rqlst.as_string(),
   472         self.assertEqual(
   436                          'Any C WHERE C is Card, A eid %(B)s, EXISTS(C owned_by A, D owned_by A, D is Card)')
   473             rqlst.as_string(),
       
   474             'Any C WHERE C is Card, A eid %(B)s, EXISTS(C owned_by A, D owned_by A, D is Card)')
   437 
   475 
   438     def test_rrqlexpr_nonexistant_subject_3(self):
   476     def test_rrqlexpr_nonexistant_subject_3(self):
   439         constraint = RRQLExpression('U in_group G, G name "users"')
   477         constraint = RRQLExpression('U in_group G, G name "users"')
   440         rqlst = parse(u'Card C')
   478         rqlst = parse(u'Card C')
   441         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'SU')
   479         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'SU')
   442         self.assertEqual(rqlst.as_string(),
   480         self.assertEqual(
   443                          u'Any C WHERE C is Card, A eid %(B)s, EXISTS(A in_group D, D name "users", D is CWGroup)')
   481             rqlst.as_string(),
       
   482             u'Any C WHERE C is Card, A eid %(B)s, '
       
   483             'EXISTS(A in_group D, D name "users", D is CWGroup)')
   444 
   484 
   445     def test_rrqlexpr_nonexistant_subject_4(self):
   485     def test_rrqlexpr_nonexistant_subject_4(self):
   446         constraint = RRQLExpression('U in_group G, G name "users", S owned_by U')
   486         constraint = RRQLExpression('U in_group G, G name "users", S owned_by U')
   447         rqlst = parse(u'Card C')
   487         rqlst = parse(u'Card C')
   448         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'SU')
   488         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'SU')
   449         self.assertEqual(rqlst.as_string(),
   489         self.assertEqual(
   450                          u'Any C WHERE C is Card, A eid %(B)s, EXISTS(A in_group D, D name "users", C owned_by A, D is CWGroup)')
   490             rqlst.as_string(),
       
   491             u'Any C WHERE C is Card, A eid %(B)s, '
       
   492             'EXISTS(A in_group D, D name "users", C owned_by A, D is CWGroup)')
   451         rqlst = parse(u'Card C')
   493         rqlst = parse(u'Card C')
   452         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'OU')
   494         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'OU')
   453         self.assertEqual(rqlst.as_string(),
   495         self.assertEqual(
   454                          u'Any C WHERE C is Card, A eid %(B)s, EXISTS(A in_group D, D name "users", D is CWGroup)')
   496             rqlst.as_string(),
       
   497             u'Any C WHERE C is Card, A eid %(B)s, '
       
   498             'EXISTS(A in_group D, D name "users", D is CWGroup)')
   455 
   499 
   456     def test_rrqlexpr_nonexistant_subject_5(self):
   500     def test_rrqlexpr_nonexistant_subject_5(self):
   457         constraint = RRQLExpression('S owned_by Z, O owned_by Z, O is Card')
   501         constraint = RRQLExpression('S owned_by Z, O owned_by Z, O is Card')
   458         rqlst = parse(u'Card C')
   502         rqlst = parse(u'Card C')
   459         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'S')
   503         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'S')
   462 
   506 
   463     def test_rqlexpr_not_relation_1_1(self):
   507     def test_rqlexpr_not_relation_1_1(self):
   464         constraint = ERQLExpression('X owned_by Z, Z login "hop"', 'X')
   508         constraint = ERQLExpression('X owned_by Z, Z login "hop"', 'X')
   465         rqlst = parse(u'Affaire A WHERE NOT EXISTS(A documented_by C)')
   509         rqlst = parse(u'Affaire A WHERE NOT EXISTS(A documented_by C)')
   466         rewrite(rqlst, {('C', 'X'): (constraint,)}, {}, 'X')
   510         rewrite(rqlst, {('C', 'X'): (constraint,)}, {}, 'X')
   467         self.assertEqual(rqlst.as_string(),
   511         self.assertEqual(
   468                          u'Any A WHERE NOT EXISTS(A documented_by C, EXISTS(C owned_by B, B login "hop", B is CWUser), C is Card), A is Affaire')
   512             rqlst.as_string(),
       
   513             u'Any A WHERE NOT EXISTS(A documented_by C, '
       
   514             'EXISTS(C owned_by B, B login "hop", B is CWUser), C is Card), A is Affaire')
   469 
   515 
   470     def test_rqlexpr_not_relation_1_2(self):
   516     def test_rqlexpr_not_relation_1_2(self):
   471         constraint = ERQLExpression('X owned_by Z, Z login "hop"', 'X')
   517         constraint = ERQLExpression('X owned_by Z, Z login "hop"', 'X')
   472         rqlst = parse(u'Affaire A WHERE NOT EXISTS(A documented_by C)')
   518         rqlst = parse(u'Affaire A WHERE NOT EXISTS(A documented_by C)')
   473         rewrite(rqlst, {('A', 'X'): (constraint,)}, {}, 'X')
   519         rewrite(rqlst, {('A', 'X'): (constraint,)}, {}, 'X')
   474         self.assertEqual(rqlst.as_string(),
   520         self.assertEqual(
   475                          u'Any A WHERE NOT EXISTS(A documented_by C, C is Card), A is Affaire, EXISTS(A owned_by B, B login "hop", B is CWUser)')
   521             rqlst.as_string(),
       
   522             u'Any A WHERE NOT EXISTS(A documented_by C, C is Card), A is Affaire, '
       
   523             'EXISTS(A owned_by B, B login "hop", B is CWUser)')
   476 
   524 
   477     def test_rqlexpr_not_relation_2(self):
   525     def test_rqlexpr_not_relation_2(self):
   478         constraint = ERQLExpression('X owned_by Z, Z login "hop"', 'X')
   526         constraint = ERQLExpression('X owned_by Z, Z login "hop"', 'X')
   479         rqlst = rqlhelper.parse(u'Affaire A WHERE NOT A documented_by C', annotate=False)
   527         rqlst = rqlhelper.parse(u'Affaire A WHERE NOT A documented_by C', annotate=False)
   480         rewrite(rqlst, {('C', 'X'): (constraint,)}, {}, 'X')
   528         rewrite(rqlst, {('C', 'X'): (constraint,)}, {}, 'X')
   481         self.assertEqual(rqlst.as_string(),
   529         self.assertEqual(
   482                          u'Any A WHERE NOT EXISTS(A documented_by C, EXISTS(C owned_by B, B login "hop", B is CWUser), C is Card), A is Affaire')
   530             rqlst.as_string(),
       
   531             u'Any A WHERE NOT EXISTS(A documented_by C, '
       
   532             'EXISTS(C owned_by B, B login "hop", B is CWUser), C is Card), A is Affaire')
   483 
   533 
   484     def test_rqlexpr_multiexpr_outerjoin(self):
   534     def test_rqlexpr_multiexpr_outerjoin(self):
   485         c1 = ERQLExpression('X owned_by Z, Z login "hop"', 'X')
   535         c1 = ERQLExpression('X owned_by Z, Z login "hop"', 'X')
   486         c2 = ERQLExpression('X owned_by Z, Z login "hip"', 'X')
   536         c2 = ERQLExpression('X owned_by Z, Z login "hip"', 'X')
   487         c3 = ERQLExpression('X owned_by Z, Z login "momo"', 'X')
   537         c3 = ERQLExpression('X owned_by Z, Z login "momo"', 'X')
   488         rqlst = rqlhelper.parse(u'Any A WHERE A documented_by C?', annotate=False)
   538         rqlst = rqlhelper.parse(u'Any A WHERE A documented_by C?', annotate=False)
   489         rewrite(rqlst, {('C', 'X'): (c1, c2, c3)}, {}, 'X')
   539         rewrite(rqlst, {('C', 'X'): (c1, c2, c3)}, {}, 'X')
   490         self.assertEqual(rqlst.as_string(),
   540         self.assertEqual(
   491                          u'Any A WHERE A documented_by C?, A is Affaire '
   541             rqlst.as_string(),
   492                          'WITH C BEING (Any C WHERE ((EXISTS(C owned_by B, B login "hop")) '
   542             u'Any A WHERE A documented_by C?, A is Affaire '
   493                          'OR (EXISTS(C owned_by D, D login "momo"))) '
   543             'WITH C BEING (Any C WHERE ((EXISTS(C owned_by B, B login "hop")) '
   494                          'OR (EXISTS(C owned_by A, A login "hip")), C is Card)')
   544             'OR (EXISTS(C owned_by D, D login "momo"))) '
       
   545             'OR (EXISTS(C owned_by A, A login "hip")), C is Card)')
   495 
   546 
   496     def test_multiple_erql_one_bad(self):
   547     def test_multiple_erql_one_bad(self):
   497         #: reproduce bug #2236985
   548         #: reproduce bug #2236985
   498         #: (rqlrewrite fails to remove rewritten entry for unsupported constraint and then crash)
   549         #: (rqlrewrite fails to remove rewritten entry for unsupported constraint and then crash)
   499         #:
   550         #:
   545             u'Any P WHERE NOT P require_state S, '
   596             u'Any P WHERE NOT P require_state S, '
   546             '(NOT EXISTS(A require_permission P, A is IN(Card, Note))) '
   597             '(NOT EXISTS(A require_permission P, A is IN(Card, Note))) '
   547             'OR (EXISTS(B require_permission P, B is IN(Card, Note), S name "state1")), '
   598             'OR (EXISTS(B require_permission P, B is IN(Card, Note), S name "state1")), '
   548             'P is CWPermission, S is State')
   599             'P is CWPermission, S is State')
   549 
   600 
   550 from cubicweb.devtools.testlib import CubicWebTC
       
   551 
   601 
   552 class RewriteFullTC(CubicWebTC):
   602 class RewriteFullTC(CubicWebTC):
   553     appid = 'data-rewrite'
   603     appid = 'data-rewrite'
   554 
   604 
   555     def process(self, rql, args=None):
   605     def process(self, rql, args=None):
   556         if args is None:
   606         if args is None:
   557             args = {}
   607             args = {}
   558         querier = self.repo.querier
   608         querier = self.repo.querier
   559         union = parse(rql) # self.vreg.parse(rql, annotate=True)
   609         union = parse(rql)  # self.vreg.parse(rql, annotate=True)
   560         with self.admin_access.repo_cnx() as cnx:
   610         with self.admin_access.repo_cnx() as cnx:
   561             self.vreg.solutions(cnx, union, args)
   611             self.vreg.solutions(cnx, union, args)
   562             querier._annotate(union)
   612             querier._annotate(union)
   563             plan = querier.plan_factory(union, args, cnx)
   613             plan = querier.plan_factory(union, args, cnx)
   564             plan.preprocess(union)
   614             plan.preprocess(union)
   587         with self.temporary_permissions((edef1, {'read': (ERQLExpression('X created_by U'),)}),
   637         with self.temporary_permissions((edef1, {'read': (ERQLExpression('X created_by U'),)}),
   588                                         (edef2, {'read': ('users',)}),
   638                                         (edef2, {'read': ('users',)}),
   589                                         (edef3, {'read': (ERQLExpression('X owned_by U'),)})):
   639                                         (edef3, {'read': (ERQLExpression('X owned_by U'),)})):
   590             union = self.process('Any A,AR,X,CD WHERE A concerne X?, A ref AR, X creation_date CD')
   640             union = self.process('Any A,AR,X,CD WHERE A concerne X?, A ref AR, X creation_date CD')
   591             self.assertEqual(union.as_string(), 'not generated today')
   641             self.assertEqual(union.as_string(), 'not generated today')
   592 
       
   593 
   642 
   594     def test_xxxx(self):
   643     def test_xxxx(self):
   595         edef1 = self.schema['Societe']
   644         edef1 = self.schema['Societe']
   596         edef2 = self.schema['Division']
   645         edef2 = self.schema['Division']
   597         read_expr = ERQLExpression('X responsable E, U has_read_permission E')
   646         read_expr = ERQLExpression('X responsable E, U has_read_permission E')
   606                              'AC is CWUser, E is CWUser, X is IN(Division, Societe)',
   655                              'AC is CWUser, E is CWUser, X is IN(Division, Societe)',
   607                              union.as_string())
   656                              union.as_string())
   608 
   657 
   609     def test_question_mark_attribute_snippet(self):
   658     def test_question_mark_attribute_snippet(self):
   610         # see #3661918
   659         # see #3661918
   611         from cubicweb.rqlrewrite import RQLRewriter
       
   612         from logilab.common.decorators import monkeypatch
       
   613         repotest.undo_monkey_patch()
   660         repotest.undo_monkey_patch()
   614         orig_insert_snippets = RQLRewriter.insert_snippets
   661         orig_insert_snippets = RQLRewriter.insert_snippets
   615         # patch insert_snippets and not rewrite, insert_snippets is already
   662         # patch insert_snippets and not rewrite, insert_snippets is already
   616         # monkey patches (see above setupModule/repotest)
   663         # monkey patches (see above setupModule/repotest)
       
   664 
   617         @monkeypatch(RQLRewriter)
   665         @monkeypatch(RQLRewriter)
   618         def insert_snippets(self, snippets, varexistsmap=None):
   666         def insert_snippets(self, snippets, varexistsmap=None):
   619             # crash occurs if snippets are processed in a specific order, force
   667             # crash occurs if snippets are processed in a specific order, force
   620             # destiny
   668             # destiny
   621             if snippets[0][0] != {u'N': 'X'}:
   669             if snippets[0][0] != {u'N': 'X'}:
   674         self.assertEqual('Any A WHERE EXISTS(C is Contribution, '
   722         self.assertEqual('Any A WHERE EXISTS(C is Contribution, '
   675                          'C contributor A, C manifestation B, '
   723                          'C contributor A, C manifestation B, '
   676                          'C role D, D name "illustrator")',
   724                          'C role D, D name "illustrator")',
   677                          rqlst.as_string())
   725                          rqlst.as_string())
   678 
   726 
   679 
       
   680     def test_rewrite2(self):
   727     def test_rewrite2(self):
   681         rules = {'illustrator_of': 'C is Contribution, C contributor S, '
   728         rules = {'illustrator_of': 'C is Contribution, C contributor S, '
   682                 'C manifestation O, C role R, R name "illustrator"'}
   729                  'C manifestation O, C role R, R name "illustrator"'}
   683         rqlst = rqlhelper.parse(u'Any A,B WHERE A illustrator_of B, C require_permission R, S'
   730         rqlst = rqlhelper.parse(u'Any A,B WHERE A illustrator_of B, C require_permission R, S'
   684                                 'require_state O')
   731                                 'require_state O')
       
   732         rule_rewrite(rqlst, rules)
       
   733         self.assertEqual(
       
   734             'Any A,B WHERE C require_permission R, S require_state O, '
       
   735             'D is Contribution, D contributor A, D manifestation B, D role E, '
       
   736             'E name "illustrator"',
       
   737             rqlst.as_string())
       
   738 
       
   739     def test_rewrite3(self):
       
   740         rules = {'illustrator_of': 'C is Contribution, C contributor S, '
       
   741                  'C manifestation O, C role R, R name "illustrator"'}
       
   742         rqlst = rqlhelper.parse(u'Any A,B WHERE E require_permission T, A illustrator_of B')
       
   743         rule_rewrite(rqlst, rules)
       
   744         self.assertEqual('Any A,B WHERE E require_permission T, '
       
   745                          'C is Contribution, C contributor A, C manifestation B, '
       
   746                          'C role D, D name "illustrator"',
       
   747                          rqlst.as_string())
       
   748 
       
   749     def test_rewrite4(self):
       
   750         rules = {'illustrator_of': 'C is Contribution, C contributor S, '
       
   751                  'C manifestation O, C role R, R name "illustrator"'}
       
   752         rqlst = rqlhelper.parse(u'Any A,B WHERE C require_permission R, A illustrator_of B')
       
   753         rule_rewrite(rqlst, rules)
       
   754         self.assertEqual('Any A,B WHERE C require_permission R, '
       
   755                          'D is Contribution, D contributor A, D manifestation B, '
       
   756                          'D role E, E name "illustrator"',
       
   757                          rqlst.as_string())
       
   758 
       
   759     def test_rewrite5(self):
       
   760         rules = {'illustrator_of': 'C is Contribution, C contributor S, '
       
   761                  'C manifestation O, C role R, R name "illustrator"'}
       
   762         rqlst = rqlhelper.parse(u'Any A,B WHERE C require_permission R, A illustrator_of B, '
       
   763                                 'S require_state O')
   685         rule_rewrite(rqlst, rules)
   764         rule_rewrite(rqlst, rules)
   686         self.assertEqual('Any A,B WHERE C require_permission R, S require_state O, '
   765         self.assertEqual('Any A,B WHERE C require_permission R, S require_state O, '
   687                          'D is Contribution, D contributor A, D manifestation B, D role E, '
   766                          'D is Contribution, D contributor A, D manifestation B, D role E, '
   688                          'E name "illustrator"',
   767                          'E name "illustrator"',
   689                           rqlst.as_string())
       
   690 
       
   691     def test_rewrite3(self):
       
   692         rules = {'illustrator_of': 'C is Contribution, C contributor S, '
       
   693                 'C manifestation O, C role R, R name "illustrator"'}
       
   694         rqlst = rqlhelper.parse(u'Any A,B WHERE E require_permission T, A illustrator_of B')
       
   695         rule_rewrite(rqlst, rules)
       
   696         self.assertEqual('Any A,B WHERE E require_permission T, '
       
   697                          'C is Contribution, C contributor A, C manifestation B, '
       
   698                          'C role D, D name "illustrator"',
       
   699                          rqlst.as_string())
       
   700 
       
   701     def test_rewrite4(self):
       
   702         rules = {'illustrator_of': 'C is Contribution, C contributor S, '
       
   703                 'C manifestation O, C role R, R name "illustrator"'}
       
   704         rqlst = rqlhelper.parse(u'Any A,B WHERE C require_permission R, A illustrator_of B')
       
   705         rule_rewrite(rqlst, rules)
       
   706         self.assertEqual('Any A,B WHERE C require_permission R, '
       
   707                          'D is Contribution, D contributor A, D manifestation B, '
       
   708                          'D role E, E name "illustrator"',
       
   709                          rqlst.as_string())
       
   710 
       
   711     def test_rewrite5(self):
       
   712         rules = {'illustrator_of': 'C is Contribution, C contributor S, '
       
   713                 'C manifestation O, C role R, R name "illustrator"'}
       
   714         rqlst = rqlhelper.parse(u'Any A,B WHERE C require_permission R, A illustrator_of B, '
       
   715                                 'S require_state O')
       
   716         rule_rewrite(rqlst, rules)
       
   717         self.assertEqual('Any A,B WHERE C require_permission R, S require_state O, '
       
   718                          'D is Contribution, D contributor A, D manifestation B, D role E, '
       
   719                          'E name "illustrator"',
       
   720                          rqlst.as_string())
   768                          rqlst.as_string())
   721 
   769 
   722     # Tests for the with clause
   770     # Tests for the with clause
   723     def test_rewrite_with(self):
   771     def test_rewrite_with(self):
   724         rules = {'illustrator_of': 'C is Contribution, C contributor S, '
   772         rules = {'illustrator_of': 'C is Contribution, C contributor S, '
   725                 'C manifestation O, C role R, R name "illustrator"'}
   773                  'C manifestation O, C role R, R name "illustrator"'}
   726         rqlst = rqlhelper.parse(u'Any A,B WITH A, B BEING(Any X, Y WHERE X illustrator_of Y)')
   774         rqlst = rqlhelper.parse(u'Any A,B WITH A, B BEING(Any X, Y WHERE X illustrator_of Y)')
   727         rule_rewrite(rqlst, rules)
   775         rule_rewrite(rqlst, rules)
   728         self.assertEqual('Any A,B WITH A,B BEING '
   776         self.assertEqual('Any A,B WITH A,B BEING '
   729                          '(Any X,Y WHERE A is Contribution, A contributor X, '
   777                          '(Any X,Y WHERE A is Contribution, A contributor X, '
   730                          'A manifestation Y, A role B, B name "illustrator")',
   778                          'A manifestation Y, A role B, B name "illustrator")',
   731                          rqlst.as_string())
   779                          rqlst.as_string())
   732 
   780 
   733     def test_rewrite_with2(self):
   781     def test_rewrite_with2(self):
   734         rules = {'illustrator_of': 'C is Contribution, C contributor S, '
   782         rules = {'illustrator_of': 'C is Contribution, C contributor S, '
   735                 'C manifestation O, C role R, R name "illustrator"'}
   783                  'C manifestation O, C role R, R name "illustrator"'}
   736         rqlst = rqlhelper.parse(u'Any A,B WHERE T require_permission C WITH A, B BEING(Any X, Y WHERE X illustrator_of Y)')
   784         rqlst = rqlhelper.parse(u'Any A,B WHERE T require_permission C '
       
   785                                 'WITH A, B BEING(Any X, Y WHERE X illustrator_of Y)')
   737         rule_rewrite(rqlst, rules)
   786         rule_rewrite(rqlst, rules)
   738         self.assertEqual('Any A,B WHERE T require_permission C '
   787         self.assertEqual('Any A,B WHERE T require_permission C '
   739                          'WITH A,B BEING (Any X,Y WHERE A is Contribution, '
   788                          'WITH A,B BEING (Any X,Y WHERE A is Contribution, '
   740                          'A contributor X, A manifestation Y, A role B, B name "illustrator")',
   789                          'A contributor X, A manifestation Y, A role B, B name "illustrator")',
   741                          rqlst.as_string())
   790                          rqlst.as_string())
   749                          '(Any X,Y WHERE X contributor Y)',
   798                          '(Any X,Y WHERE X contributor Y)',
   750                          rqlst.as_string())
   799                          rqlst.as_string())
   751 
   800 
   752     def test_rewrite_with4(self):
   801     def test_rewrite_with4(self):
   753         rules = {'illustrator_of': 'C is Contribution, C contributor S, '
   802         rules = {'illustrator_of': 'C is Contribution, C contributor S, '
   754                 'C manifestation O, C role R, R name "illustrator"'}
   803                  'C manifestation O, C role R, R name "illustrator"'}
   755         rqlst = rqlhelper.parse(u'Any A,B WHERE A illustrator_of B '
   804         rqlst = rqlhelper.parse(u'Any A,B WHERE A illustrator_of B '
   756                                'WITH A, B BEING(Any X, Y WHERE X illustrator_of Y)')
   805                                 'WITH A, B BEING(Any X, Y WHERE X illustrator_of Y)')
   757         rule_rewrite(rqlst, rules)
   806         rule_rewrite(rqlst, rules)
   758         self.assertEqual('Any A,B WHERE C is Contribution, '
   807         self.assertEqual(
   759                          'C contributor A, C manifestation B, C role D, '
   808             'Any A,B WHERE C is Contribution, '
   760                          'D name "illustrator" WITH A,B BEING '
   809             'C contributor A, C manifestation B, C role D, '
   761                          '(Any X,Y WHERE A is Contribution, A contributor X, '
   810             'D name "illustrator" WITH A,B BEING '
   762                          'A manifestation Y, A role B, B name "illustrator")',
   811             '(Any X,Y WHERE A is Contribution, A contributor X, '
   763                           rqlst.as_string())
   812             'A manifestation Y, A role B, B name "illustrator")',
       
   813             rqlst.as_string())
   764 
   814 
   765     # Tests for the union
   815     # Tests for the union
   766     def test_rewrite_union(self):
   816     def test_rewrite_union(self):
   767         rules = {'illustrator_of': 'C is Contribution, C contributor S, '
   817         rules = {'illustrator_of': 'C is Contribution, C contributor S, '
   768                 'C manifestation O, C role R, R name "illustrator"'}
   818                  'C manifestation O, C role R, R name "illustrator"'}
   769         rqlst = rqlhelper.parse(u'(Any A,B WHERE A illustrator_of B) UNION'
   819         rqlst = rqlhelper.parse(u'(Any A,B WHERE A illustrator_of B) UNION'
   770                                 '(Any X,Y WHERE X is CWUser, Z manifestation Y)')
   820                                 '(Any X,Y WHERE X is CWUser, Z manifestation Y)')
   771         rule_rewrite(rqlst, rules)
   821         rule_rewrite(rqlst, rules)
   772         self.assertEqual('(Any A,B WHERE C is Contribution, '
   822         self.assertEqual(
   773                          'C contributor A, C manifestation B, C role D, '
   823             '(Any A,B WHERE C is Contribution, '
   774                          'D name "illustrator") UNION (Any X,Y WHERE X is CWUser, Z manifestation Y)',
   824             'C contributor A, C manifestation B, C role D, '
   775                          rqlst.as_string())
   825             'D name "illustrator") UNION (Any X,Y WHERE X is CWUser, Z manifestation Y)',
       
   826             rqlst.as_string())
   776 
   827 
   777     def test_rewrite_union2(self):
   828     def test_rewrite_union2(self):
   778         rules = {'illustrator_of': 'C is Contribution, C contributor S, '
   829         rules = {'illustrator_of': 'C is Contribution, C contributor S, '
   779                 'C manifestation O, C role R, R name "illustrator"'}
   830                  'C manifestation O, C role R, R name "illustrator"'}
   780         rqlst = rqlhelper.parse(u'(Any Y WHERE Y match W) UNION '
   831         rqlst = rqlhelper.parse(u'(Any Y WHERE Y match W) UNION '
   781                                 '(Any A WHERE A illustrator_of B) UNION '
   832                                 '(Any A WHERE A illustrator_of B) UNION '
   782                                 '(Any Y WHERE Y is ArtWork)')
   833                                 '(Any Y WHERE Y is ArtWork)')
   783         rule_rewrite(rqlst, rules)
   834         rule_rewrite(rqlst, rules)
   784         self.assertEqual('(Any Y WHERE Y match W) '
   835         self.assertEqual('(Any Y WHERE Y match W) '
   788                          rqlst.as_string())
   839                          rqlst.as_string())
   789 
   840 
   790     # Tests for the exists clause
   841     # Tests for the exists clause
   791     def test_rewrite_exists(self):
   842     def test_rewrite_exists(self):
   792         rules = {'illustrator_of': 'C is Contribution, C contributor S, '
   843         rules = {'illustrator_of': 'C is Contribution, C contributor S, '
   793                 'C manifestation O, C role R, R name "illustrator"'}
   844                  'C manifestation O, C role R, R name "illustrator"'}
   794         rqlst = rqlhelper.parse(u'(Any A,B WHERE A illustrator_of B, '
   845         rqlst = rqlhelper.parse(u'(Any A,B WHERE A illustrator_of B, '
   795                      'EXISTS(B is ArtWork))')
   846                                 'EXISTS(B is ArtWork))')
   796         rule_rewrite(rqlst, rules)
   847         rule_rewrite(rqlst, rules)
   797         self.assertEqual('Any A,B WHERE EXISTS(B is ArtWork), '
   848         self.assertEqual('Any A,B WHERE EXISTS(B is ArtWork), '
   798                          'C is Contribution, C contributor A, C manifestation B, C role D, '
   849                          'C is Contribution, C contributor A, C manifestation B, C role D, '
   799                          'D name "illustrator"',
   850                          'D name "illustrator"',
   800                          rqlst.as_string())
   851                          rqlst.as_string())
   801 
   852 
   802     def test_rewrite_exists2(self):
   853     def test_rewrite_exists2(self):
   803         rules = {'illustrator_of': 'C is Contribution, C contributor S, '
   854         rules = {'illustrator_of': 'C is Contribution, C contributor S, '
   804                 'C manifestation O, C role R, R name "illustrator"'}
   855                  'C manifestation O, C role R, R name "illustrator"'}
   805         rqlst = rqlhelper.parse(u'(Any A,B WHERE B contributor A, EXISTS(A illustrator_of W))')
   856         rqlst = rqlhelper.parse(u'(Any A,B WHERE B contributor A, EXISTS(A illustrator_of W))')
   806         rule_rewrite(rqlst, rules)
   857         rule_rewrite(rqlst, rules)
   807         self.assertEqual('Any A,B WHERE B contributor A, '
   858         self.assertEqual('Any A,B WHERE B contributor A, '
   808                          'EXISTS(C is Contribution, C contributor A, C manifestation W, '
   859                          'EXISTS(C is Contribution, C contributor A, C manifestation W, '
   809                          'C role D, D name "illustrator")',
   860                          'C role D, D name "illustrator")',
   810                          rqlst.as_string())
   861                          rqlst.as_string())
   811 
   862 
   812     def test_rewrite_exists3(self):
   863     def test_rewrite_exists3(self):
   813         rules = {'illustrator_of': 'C is Contribution, C contributor S, '
   864         rules = {'illustrator_of': 'C is Contribution, C contributor S, '
   814                 'C manifestation O, C role R, R name "illustrator"'}
   865                  'C manifestation O, C role R, R name "illustrator"'}
   815         rqlst = rqlhelper.parse(u'(Any A,B WHERE A illustrator_of B, EXISTS(A illustrator_of W))')
   866         rqlst = rqlhelper.parse(u'(Any A,B WHERE A illustrator_of B, EXISTS(A illustrator_of W))')
   816         rule_rewrite(rqlst, rules)
   867         rule_rewrite(rqlst, rules)
   817         self.assertEqual('Any A,B WHERE EXISTS(C is Contribution, C contributor A, '
   868         self.assertEqual('Any A,B WHERE EXISTS(C is Contribution, C contributor A, '
   818                          'C manifestation W, C role D, D name "illustrator"), '
   869                          'C manifestation W, C role D, D name "illustrator"), '
   819                          'E is Contribution, E contributor A, E manifestation B, E role F, '
   870                          'E is Contribution, E contributor A, E manifestation B, E role F, '
   821                          rqlst.as_string())
   872                          rqlst.as_string())
   822 
   873 
   823     # Test for GROUPBY
   874     # Test for GROUPBY
   824     def test_rewrite_groupby(self):
   875     def test_rewrite_groupby(self):
   825         rules = {'participated_in': 'S contributor O'}
   876         rules = {'participated_in': 'S contributor O'}
   826         rqlst = rqlhelper.parse(u'Any SUM(SA) GROUPBY S WHERE P participated_in S, P manifestation SA')
   877         rqlst = rqlhelper.parse(u'Any SUM(SA) GROUPBY S '
       
   878                                 'WHERE P participated_in S, P manifestation SA')
   827         rule_rewrite(rqlst, rules)
   879         rule_rewrite(rqlst, rules)
   828         self.assertEqual('Any SUM(SA) GROUPBY S WHERE P manifestation SA, P contributor S',
   880         self.assertEqual('Any SUM(SA) GROUPBY S WHERE P manifestation SA, P contributor S',
   829                          rqlst.as_string())
   881                          rqlst.as_string())
   830 
   882 
   831 
   883 
   832 class RQLRelationRewriterTC(CubicWebTC):
   884 class RQLRelationRewriterCWTC(CubicWebTC):
   833 
   885 
   834     appid = 'data-rewrite'
   886     appid = 'data-rewrite'
   835 
   887 
   836     def test_base_rule(self):
   888     def test_base_rule(self):
   837         with self.admin_access.client_cnx() as cnx:
   889         with self.admin_access.client_cnx() as cnx:
   838             art = cnx.create_entity('ArtWork', name=u'Les travailleurs de la Mer')
   890             art = cnx.create_entity('ArtWork', name=u'Les travailleurs de la Mer')
   839             role = cnx.create_entity('Role', name=u'illustrator')
   891             role = cnx.create_entity('Role', name=u'illustrator')
   840             vic = cnx.create_entity('Person', name=u'Victor Hugo')
   892             vic = cnx.create_entity('Person', name=u'Victor Hugo')
   841             contrib = cnx.create_entity('Contribution', code=96, contributor=vic,
   893             cnx.create_entity('Contribution', code=96, contributor=vic,
   842                                         manifestation=art, role=role)
   894                               manifestation=art, role=role)
   843             rset = cnx.execute('Any X WHERE X illustrator_of S')
   895             rset = cnx.execute('Any X WHERE X illustrator_of S')
   844             self.assertEqual([u'Victor Hugo'],
   896             self.assertEqual([u'Victor Hugo'],
   845                              [result.name for result in rset.entities()])
   897                              [result.name for result in rset.entities()])
   846             rset = cnx.execute('Any S WHERE X illustrator_of S, X eid %(x)s',
   898             rset = cnx.execute('Any S WHERE X illustrator_of S, X eid %(x)s',
   847                                {'x': vic.eid})
   899                                {'x': vic.eid})