test/unittest_rqlrewrite.py
changeset 9953 643b19d79e4a
parent 9674 96549de9dd70
child 9955 60a9cd1b3a4b
equal deleted inserted replaced
9952:0f3f965b6365 9953:643b19d79e4a
    17 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
    17 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
    18 
    18 
    19 from logilab.common.testlib import unittest_main, TestCase
    19 from logilab.common.testlib import unittest_main, TestCase
    20 from logilab.common.testlib import mock_object
    20 from logilab.common.testlib import mock_object
    21 from yams import BadSchemaDefinition
    21 from yams import BadSchemaDefinition
       
    22 from yams.buildobjs import RelationDefinition
    22 from rql import parse, nodes, RQLHelper
    23 from rql import parse, nodes, RQLHelper
    23 
    24 
    24 from cubicweb import Unauthorized, rqlrewrite
    25 from cubicweb import Unauthorized, rqlrewrite
    25 from cubicweb.schema import RRQLExpression, ERQLExpression
    26 from cubicweb.schema import RRQLExpression, ERQLExpression
    26 from cubicweb.devtools import repotest, TestServerConfiguration, BaseApptestConfiguration
    27 from cubicweb.devtools import repotest, TestServerConfiguration, BaseApptestConfiguration
    29 def setUpModule(*args):
    30 def setUpModule(*args):
    30     global rqlhelper, schema
    31     global rqlhelper, schema
    31     config = TestServerConfiguration(RQLRewriteTC.datapath('rewrite'))
    32     config = TestServerConfiguration(RQLRewriteTC.datapath('rewrite'))
    32     config.bootstrap_cubes()
    33     config.bootstrap_cubes()
    33     schema = config.load_schema()
    34     schema = config.load_schema()
    34     from yams.buildobjs import RelationDefinition
       
    35     schema.add_relation_def(RelationDefinition(subject='Card', name='in_state',
    35     schema.add_relation_def(RelationDefinition(subject='Card', name='in_state',
    36                                                object='State', cardinality='1*'))
    36                                                object='State', cardinality='1*'))
    37 
       
    38     rqlhelper = RQLHelper(schema, special_relations={'eid': 'uid',
    37     rqlhelper = RQLHelper(schema, special_relations={'eid': 'uid',
    39                                                      'has_text': 'fti'})
    38                                                      'has_text': 'fti'})
    40     repotest.do_monkey_patch()
    39     repotest.do_monkey_patch()
    41 
    40 
    42 def tearDownModule(*args):
    41 def tearDownModule(*args):
    47 def eid_func_map(eid):
    46 def eid_func_map(eid):
    48     return {1: 'CWUser',
    47     return {1: 'CWUser',
    49             2: 'Card',
    48             2: 'Card',
    50             3: 'Affaire'}[eid]
    49             3: 'Affaire'}[eid]
    51 
    50 
    52 def rewrite(rqlst, snippets_map, kwargs, existingvars=None):
    51 def _prepare_rewriter(rewriter_cls, kwargs):
    53     class FakeVReg:
    52     class FakeVReg:
    54         schema = schema
    53         schema = schema
    55         @staticmethod
    54         @staticmethod
    56         def solutions(sqlcursor, mainrqlst, kwargs):
    55         def solutions(sqlcursor, rqlst, kwargs):
    57             rqlhelper.compute_solutions(rqlst, {'eid': eid_func_map}, kwargs=kwargs)
    56             rqlhelper.compute_solutions(rqlst, {'eid': eid_func_map}, kwargs=kwargs)
    58         class rqlhelper:
    57         class rqlhelper:
    59             @staticmethod
    58             @staticmethod
    60             def annotate(rqlst):
    59             def annotate(rqlst):
    61                 rqlhelper.annotate(rqlst)
    60                 rqlhelper.annotate(rqlst)
    62             @staticmethod
    61             @staticmethod
    63             def simplify(mainrqlst, needcopy=False):
    62             def simplify(mainrqlst, needcopy=False):
    64                 rqlhelper.simplify(rqlst, needcopy)
    63                 rqlhelper.simplify(rqlst, needcopy)
    65     rewriter = rqlrewrite.RQLRewriter(
    64     return rewriter_cls(mock_object(vreg=FakeVReg, user=(mock_object(eid=1))))
    66         mock_object(vreg=FakeVReg, user=(mock_object(eid=1))))
    65 
       
    66 def rewrite(rqlst, snippets_map, kwargs, existingvars=None):
       
    67     rewriter = _prepare_rewriter(rqlrewrite.RQLRewriter, kwargs)
    67     snippets = []
    68     snippets = []
    68     for v, exprs in sorted(snippets_map.items()):
    69     for v, exprs in sorted(snippets_map.items()):
    69         rqlexprs = [isinstance(snippet, basestring)
    70         rqlexprs = [isinstance(snippet, basestring)
    70                     and mock_object(snippet_rqlst=parse('Any X WHERE '+snippet).children[0],
    71                     and mock_object(snippet_rqlst=parse('Any X WHERE '+snippet).children[0],
    71                                     expression='Any X WHERE '+snippet)
    72                                     expression='Any X WHERE '+snippet)
    85         try:
    86         try:
    86             vrefmaps[stmt].setdefault(vref.name, set()).add(vref)
    87             vrefmaps[stmt].setdefault(vref.name, set()).add(vref)
    87         except KeyError:
    88         except KeyError:
    88             vrefmaps[stmt] = {vref.name: set( (vref,) )}
    89             vrefmaps[stmt] = {vref.name: set( (vref,) )}
    89             selects.append(stmt)
    90             selects.append(stmt)
    90     assert node in selects
    91     assert node in selects, (node, selects)
    91     for stmt in selects:
    92     for stmt in selects:
    92         for var in stmt.defined_vars.itervalues():
    93         for var in stmt.defined_vars.itervalues():
    93             assert var.stinfo['references']
    94             assert var.stinfo['references']
    94             vrefmap = vrefmaps[stmt]
    95             vrefmap = vrefmaps[stmt]
    95             assert not (var.stinfo['references'] ^ vrefmap[var.name]), (node.as_string(), var, var.stinfo['references'], vrefmap[var.name])
    96             assert not (var.stinfo['references'] ^ vrefmap[var.name]), (node.as_string(), var, var.stinfo['references'], vrefmap[var.name])
   589                                  'A is Affaire, N is Note, EXISTS(A ref "blah"))',
   590                                  'A is Affaire, N is Note, EXISTS(A ref "blah"))',
   590                                  union.as_string())
   591                                  union.as_string())
   591         finally:
   592         finally:
   592             RQLRewriter.insert_snippets = orig_insert_snippets
   593             RQLRewriter.insert_snippets = orig_insert_snippets
   593 
   594 
       
   595 
       
   596 class RQLRelationRewriterTC(TestCase):
       
   597     # XXX valid rules: S and O specified, not in a SET, INSERT, DELETE scope
       
   598     #     valid uses: no outer join
       
   599 
       
   600     # Basic tests
       
   601     def test_base_rule(self):
       
   602         rules = {'participated_in': 'S contributor O'}
       
   603         rqlst = rqlhelper.parse('Any X WHERE X participated_in S')
       
   604         rule_rewrite(rqlst, rules)
       
   605         self.assertEqual('Any X WHERE X contributor S',
       
   606                          rqlst.as_string())
       
   607 
       
   608     def test_complex_rule_1(self):
       
   609         rules = {'illustrator_of': ('C is Contribution, C contributor S, '
       
   610                                     'C manifestation O, C role R, '
       
   611                                     'R name "illustrator"')}
       
   612         rqlst = rqlhelper.parse('Any A,B WHERE A illustrator_of B')
       
   613         rule_rewrite(rqlst, rules)
       
   614         self.assertEqual('Any A,B WHERE C is Contribution, '
       
   615                          'C contributor A, C manifestation B, '
       
   616                          'C role D, D name "illustrator"',
       
   617                          rqlst.as_string())
       
   618 
       
   619     def test_complex_rule_2(self):
       
   620         rules = {'illustrator_of': ('C is Contribution, C contributor S, '
       
   621                                     'C manifestation O, C role R, '
       
   622                                     'R name "illustrator"')}
       
   623         rqlst = rqlhelper.parse('Any A WHERE EXISTS(A illustrator_of B)')
       
   624         rule_rewrite(rqlst, rules)
       
   625         self.assertEqual('Any A WHERE EXISTS(C is Contribution, '
       
   626                          'C contributor A, C manifestation B, '
       
   627                          'C role D, D name "illustrator")',
       
   628                          rqlst.as_string())
       
   629 
       
   630 
       
   631     def test_rewrite2(self):
       
   632         rules = {'illustrator_of': 'C is Contribution, C contributor S, '
       
   633                 'C manifestation O, C role R, R name "illustrator"'}
       
   634         rqlst = rqlhelper.parse('Any A,B WHERE A illustrator_of B, C require_permission R, S'
       
   635                                 'require_state O')
       
   636         rule_rewrite(rqlst, rules)
       
   637         self.assertEqual('Any A,B WHERE C require_permission R, S require_state O, '
       
   638                          'D is Contribution, D contributor A, D manifestation B, D role E, '
       
   639                          'E name "illustrator"',
       
   640                           rqlst.as_string())
       
   641 
       
   642     def test_rewrite3(self):
       
   643         rules = {'illustrator_of': 'C is Contribution, C contributor S, '
       
   644                 'C manifestation O, C role R, R name "illustrator"'}
       
   645         rqlst = rqlhelper.parse('Any A,B WHERE E require_permission T, A illustrator_of B')
       
   646         rule_rewrite(rqlst, rules)
       
   647         self.assertEqual('Any A,B WHERE E require_permission T, '
       
   648                          'C is Contribution, C contributor A, C manifestation B, '
       
   649                          'C role D, D name "illustrator"',
       
   650                          rqlst.as_string())
       
   651 
       
   652     def test_rewrite4(self):
       
   653         rules = {'illustrator_of': 'C is Contribution, C contributor S, '
       
   654                 'C manifestation O, C role R, R name "illustrator"'}
       
   655         rqlst = rqlhelper.parse('Any A,B WHERE C require_permission R, A illustrator_of B')
       
   656         rule_rewrite(rqlst, rules)
       
   657         self.assertEqual('Any A,B WHERE C require_permission R, '
       
   658                          'D is Contribution, D contributor A, D manifestation B, '
       
   659                          'D role E, E name "illustrator"',
       
   660                          rqlst.as_string())
       
   661 
       
   662     def test_rewrite5(self):
       
   663         rules = {'illustrator_of': 'C is Contribution, C contributor S, '
       
   664                 'C manifestation O, C role R, R name "illustrator"'}
       
   665         rqlst = rqlhelper.parse('Any A,B WHERE C require_permission R, A illustrator_of B, '
       
   666                                 'S require_state O')
       
   667         rule_rewrite(rqlst, rules)
       
   668         self.assertEqual('Any A,B WHERE C require_permission R, S require_state O, '
       
   669                          'D is Contribution, D contributor A, D manifestation B, D role E, '
       
   670                          'E name "illustrator"',
       
   671                          rqlst.as_string())
       
   672 
       
   673     # Tests for the with clause
       
   674     def test_rewrite_with(self):
       
   675         rules = {'illustrator_of': 'C is Contribution, C contributor S, '
       
   676                 'C manifestation O, C role R, R name "illustrator"'}
       
   677         rqlst = rqlhelper.parse('Any A,B WITH A, B BEING(Any X, Y WHERE X illustrator_of Y)')
       
   678         rule_rewrite(rqlst, rules)
       
   679         self.assertEqual('Any A,B WITH A,B BEING '
       
   680                          '(Any X,Y WHERE A is Contribution, A contributor X, '
       
   681                          'A manifestation Y, A role B, B name "illustrator")',
       
   682                          rqlst.as_string())
       
   683 
       
   684     def test_rewrite_with2(self):
       
   685         rules = {'illustrator_of': 'C is Contribution, C contributor S, '
       
   686                 'C manifestation O, C role R, R name "illustrator"'}
       
   687         rqlst = rqlhelper.parse('Any A,B WHERE T require_permission C WITH A, B BEING(Any X, Y WHERE X illustrator_of Y)')
       
   688         rule_rewrite(rqlst, rules)
       
   689         self.assertEqual('Any A,B WHERE T require_permission C '
       
   690                          'WITH A,B BEING (Any X,Y WHERE A is Contribution, '
       
   691                          'A contributor X, A manifestation Y, A role B, B name "illustrator")',
       
   692                          rqlst.as_string())
       
   693 
       
   694     def test_rewrite_with3(self):
       
   695         rules = {'participated_in': 'S contributor O'}
       
   696         rqlst = rqlhelper.parse('Any A,B WHERE A participated_in B '
       
   697                                 'WITH A, B BEING(Any X,Y WHERE X contributor Y)')
       
   698         rule_rewrite(rqlst, rules)
       
   699         self.assertEqual('Any A,B WHERE A contributor B WITH A,B BEING '
       
   700                          '(Any X,Y WHERE X contributor Y)', 
       
   701                          rqlst.as_string())
       
   702 
       
   703     def test_rewrite_with4(self):
       
   704         rules = {'illustrator_of': 'C is Contribution, C contributor S, '
       
   705                 'C manifestation O, C role R, R name "illustrator"'}
       
   706         rqlst = rqlhelper.parse('Any A,B WHERE A illustrator_of B '
       
   707                                'WITH A, B BEING(Any X, Y WHERE X illustrator_of Y)')
       
   708         rule_rewrite(rqlst, rules)
       
   709         self.assertEqual('Any A,B WHERE C is Contribution, '
       
   710                          'C contributor A, C manifestation B, C role D, '
       
   711                          'D name "illustrator" WITH A,B BEING '
       
   712                          '(Any X,Y WHERE A is Contribution, A contributor X, '
       
   713                          'A manifestation Y, A role B, B name "illustrator")',
       
   714                           rqlst.as_string()) 
       
   715 
       
   716     # Tests for the union
       
   717     def test_rewrite_union(self):
       
   718         rules = {'illustrator_of': 'C is Contribution, C contributor S, '
       
   719                 'C manifestation O, C role R, R name "illustrator"'}
       
   720         rqlst = rqlhelper.parse('(Any A,B WHERE A illustrator_of B) UNION'
       
   721                                 '(Any X,Y WHERE X is CWUser, Z manifestation Y)')
       
   722         rule_rewrite(rqlst, rules)
       
   723         self.assertEqual('(Any A,B WHERE C is Contribution, '
       
   724                          'C contributor A, C manifestation B, C role D, '
       
   725                          'D name "illustrator") UNION (Any X,Y WHERE X is CWUser, Z manifestation Y)',
       
   726                          rqlst.as_string())
       
   727 
       
   728     def test_rewrite_union2(self):
       
   729         rules = {'illustrator_of': 'C is Contribution, C contributor S, '
       
   730                 'C manifestation O, C role R, R name "illustrator"'}
       
   731         rqlst = rqlhelper.parse('(Any Y WHERE Y match W) UNION '
       
   732                                 '(Any A WHERE A illustrator_of B) UNION '
       
   733                                 '(Any Y WHERE Y is ArtWork)')
       
   734         rule_rewrite(rqlst, rules)
       
   735         self.assertEqual('(Any Y WHERE Y match W) '
       
   736                          'UNION (Any A WHERE C is Contribution, C contributor A, '
       
   737                          'C manifestation B, C role D, D name "illustrator") '
       
   738                          'UNION (Any Y WHERE Y is ArtWork)',
       
   739                          rqlst.as_string())
       
   740 
       
   741     # Tests for the exists clause
       
   742     def test_rewrite_exists(self):
       
   743         rules = {'illustrator_of': 'C is Contribution, C contributor S, '
       
   744                 'C manifestation O, C role R, R name "illustrator"'}
       
   745         rqlst = rqlhelper.parse('(Any A,B WHERE A illustrator_of B, '
       
   746                      'EXISTS(B is ArtWork))')
       
   747         rule_rewrite(rqlst, rules)
       
   748         self.assertEqual('Any A,B WHERE EXISTS(B is ArtWork), '
       
   749                          'C is Contribution, C contributor A, C manifestation B, C role D, '
       
   750                          'D name "illustrator"',
       
   751                          rqlst.as_string())
       
   752 
       
   753     def test_rewrite_exists2(self):
       
   754         rules = {'illustrator_of': 'C is Contribution, C contributor S, '
       
   755                 'C manifestation O, C role R, R name "illustrator"'}
       
   756         rqlst = rqlhelper.parse('(Any A,B WHERE B contributor A, EXISTS(A illustrator_of W))')
       
   757         rule_rewrite(rqlst, rules)
       
   758         self.assertEqual('Any A,B WHERE B contributor A, '
       
   759                          'EXISTS(C is Contribution, C contributor A, C manifestation W, '
       
   760                          'C role D, D name "illustrator")',
       
   761                          rqlst.as_string())
       
   762 
       
   763     def test_rewrite_exists3(self):
       
   764         rules = {'illustrator_of': 'C is Contribution, C contributor S, '
       
   765                 'C manifestation O, C role R, R name "illustrator"'}
       
   766         rqlst = rqlhelper.parse('(Any A,B WHERE A illustrator_of B, EXISTS(A illustrator_of W))')
       
   767         rule_rewrite(rqlst, rules)
       
   768         self.assertEqual('Any A,B WHERE EXISTS(C is Contribution, C contributor A, '
       
   769                          'C manifestation W, C role D, D name "illustrator"), '
       
   770                          'E is Contribution, E contributor A, E manifestation B, E role F, '
       
   771                          'F name "illustrator"',
       
   772                          rqlst.as_string())
       
   773 
       
   774     # Test for GROUPBY
       
   775     def test_rewrite_groupby(self):
       
   776         rules = {'participated_in': 'S contributor O'}
       
   777         rqlst = rqlhelper.parse('Any SUM(SA) GROUPBY S WHERE P participated_in S, P manifestation SA')
       
   778         rule_rewrite(rqlst, rules)
       
   779         self.assertEqual('Any SUM(SA) GROUPBY S WHERE P manifestation SA, P contributor S',
       
   780                          rqlst.as_string())
       
   781 
       
   782 
       
   783 
       
   784 def rule_rewrite(rqlst, kwargs=None):
       
   785     rewriter = _prepare_rewriter(rqlrewrite.RQLRelationRewriter, kwargs)
       
   786     rqlhelper.compute_solutions(rqlst.children[0], {'eid': eid_func_map},
       
   787                                 kwargs=kwargs)
       
   788     rewriter.rewrite(rqlst)
       
   789     for select in rqlst.children:
       
   790         test_vrefs(select)
       
   791     return rewriter.rewritten
       
   792 
       
   793 
   594 if __name__ == '__main__':
   794 if __name__ == '__main__':
   595     unittest_main()
   795     unittest_main()