test/unittest_rqlrewrite.py
brancholdstable
changeset 8462 a14b6562082b
parent 8342 7a5271182ef0
child 9168 0fb4b67bde58
equal deleted inserted replaced
8231:1bb43e31032d 8462:a14b6562082b
     1 # copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
     1 # copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
     2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
     2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
     3 #
     3 #
     4 # This file is part of CubicWeb.
     4 # This file is part of CubicWeb.
     5 #
     5 #
     6 # CubicWeb is free software: you can redistribute it and/or modify it under the
     6 # CubicWeb is free software: you can redistribute it and/or modify it under the
   108     def test_base_var(self):
   108     def test_base_var(self):
   109         constraint = ('X in_state S, U in_group G, P require_state S,'
   109         constraint = ('X in_state S, U in_group G, P require_state S,'
   110                            'P name "read", P require_group G')
   110                            'P name "read", P require_group G')
   111         rqlst = parse('Card C')
   111         rqlst = parse('Card C')
   112         rewrite(rqlst, {('C', 'X'): (constraint,)}, {})
   112         rewrite(rqlst, {('C', 'X'): (constraint,)}, {})
   113         self.failUnlessEqual(rqlst.as_string(),
   113         self.assertEqual(rqlst.as_string(),
   114                              u"Any C WHERE C is Card, B eid %(D)s, "
   114                          u"Any C WHERE C is Card, B eid %(D)s, "
   115                              "EXISTS(C in_state A, B in_group E, F require_state A, "
   115                          "EXISTS(C in_state A, B in_group E, F require_state A, "
   116                              "F name 'read', F require_group E, A is State, E is CWGroup, F is CWPermission)")
   116                          "F name 'read', F require_group E, A is State, E is CWGroup, F is CWPermission)")
   117 
   117 
   118     def test_multiple_var(self):
   118     def test_multiple_var(self):
   119         card_constraint = ('X in_state S, U in_group G, P require_state S,'
   119         card_constraint = ('X in_state S, U in_group G, P require_state S,'
   120                            'P name "read", P require_group G')
   120                            'P name "read", P require_group G')
   121         affaire_constraints = ('X ref LIKE "PUBLIC%"', 'U in_group G, G name "public"')
   121         affaire_constraints = ('X ref LIKE "PUBLIC%"', 'U in_group G, G name "public"')
   122         kwargs = {'u':2}
   122         kwargs = {'u':2}
   123         rqlst = parse('Any S WHERE S documented_by C, C eid %(u)s')
   123         rqlst = parse('Any S WHERE S documented_by C, C eid %(u)s')
   124         rewrite(rqlst, {('C', 'X'): (card_constraint,), ('S', 'X'): affaire_constraints},
   124         rewrite(rqlst, {('C', 'X'): (card_constraint,), ('S', 'X'): affaire_constraints},
   125                 kwargs)
   125                 kwargs)
   126         self.assertMultiLineEqual(rqlst.as_string(),
   126         self.assertMultiLineEqual(
   127                              "Any S WHERE S documented_by C, C eid %(u)s, B eid %(D)s, "
   127             rqlst.as_string(),
   128                              "EXISTS(C in_state A, B in_group E, F require_state A, "
   128             "Any S WHERE S documented_by C, C eid %(u)s, B eid %(D)s, "
   129                              "F name 'read', F require_group E, A is State, E is CWGroup, F is CWPermission), "
   129             "EXISTS(C in_state A, B in_group E, F require_state A, "
   130                              "(EXISTS(S ref LIKE 'PUBLIC%')) OR (EXISTS(B in_group G, G name 'public', G is CWGroup)), "
   130             "F name 'read', F require_group E, A is State, E is CWGroup, F is CWPermission), "
   131                              "S is Affaire")
   131             "(EXISTS(S ref LIKE 'PUBLIC%')) OR (EXISTS(B in_group G, G name 'public', G is CWGroup)), "
   132         self.failUnless('D' in kwargs)
   132             "S is Affaire")
       
   133         self.assertTrue('D' in kwargs)
   133 
   134 
   134     def test_or(self):
   135     def test_or(self):
   135         constraint = '(X identity U) OR (X in_state ST, CL identity U, CL in_state ST, ST name "subscribed")'
   136         constraint = '(X identity U) OR (X in_state ST, CL identity U, CL in_state ST, ST name "subscribed")'
   136         rqlst = parse('Any S WHERE S owned_by C, C eid %(u)s, S is in (CWUser, CWGroup)')
   137         rqlst = parse('Any S WHERE S owned_by C, C eid %(u)s, S is in (CWUser, CWGroup)')
   137         rewrite(rqlst, {('C', 'X'): (constraint,)}, {'u':1})
   138         rewrite(rqlst, {('C', 'X'): (constraint,)}, {'u':1})
   138         self.failUnlessEqual(rqlst.as_string(),
   139         self.assertEqual(rqlst.as_string(),
   139                              "Any S WHERE S owned_by C, C eid %(u)s, S is IN(CWUser, CWGroup), A eid %(B)s, "
   140                          "Any S WHERE S owned_by C, C eid %(u)s, S is IN(CWUser, CWGroup), A eid %(B)s, "
   140                              "EXISTS((C identity A) OR (C in_state D, E identity A, "
   141                          "EXISTS((C identity A) OR (C in_state D, E identity A, "
   141                              "E in_state D, D name 'subscribed'), D is State, E is CWUser)")
   142                          "E in_state D, D name 'subscribed'), D is State, E is CWUser)")
   142 
   143 
   143     def test_simplified_rqlst(self):
   144     def test_simplified_rqlst(self):
   144         constraint = ('X in_state S, U in_group G, P require_state S,'
   145         constraint = ('X in_state S, U in_group G, P require_state S,'
   145                            'P name "read", P require_group G')
   146                            'P name "read", P require_group G')
   146         rqlst = parse('Any 2') # this is the simplified rql st for Any X WHERE X eid 12
   147         rqlst = parse('Any 2') # this is the simplified rql st for Any X WHERE X eid 12
   147         rewrite(rqlst, {('2', 'X'): (constraint,)}, {})
   148         rewrite(rqlst, {('2', 'X'): (constraint,)}, {})
   148         self.failUnlessEqual(rqlst.as_string(),
   149         self.assertEqual(rqlst.as_string(),
   149                              u"Any 2 WHERE B eid %(C)s, "
   150                          u"Any 2 WHERE B eid %(C)s, "
   150                              "EXISTS(2 in_state A, B in_group D, E require_state A, "
   151                          "EXISTS(2 in_state A, B in_group D, E require_state A, "
   151                              "E name 'read', E require_group D, A is State, D is CWGroup, E is CWPermission)")
   152                          "E name 'read', E require_group D, A is State, D is CWGroup, E is CWPermission)")
   152 
   153 
   153     def test_optional_var_1(self):
   154     def test_optional_var_1(self):
   154         constraint = ('X in_state S, U in_group G, P require_state S,'
   155         constraint = ('X in_state S, U in_group G, P require_state S,'
   155                            'P name "read", P require_group G')
   156                            'P name "read", P require_group G')
   156         rqlst = parse('Any A,C WHERE A documented_by C?')
   157         rqlst = parse('Any A,C WHERE A documented_by C?')
   157         rewrite(rqlst, {('C', 'X'): (constraint,)}, {})
   158         rewrite(rqlst, {('C', 'X'): (constraint,)}, {})
   158         self.failUnlessEqual(rqlst.as_string(),
   159         self.assertEqual(rqlst.as_string(),
   159                              "Any A,C WHERE A documented_by C?, A is Affaire "
   160                          "Any A,C WHERE A documented_by C?, A is Affaire "
   160                              "WITH C BEING "
   161                          "WITH C BEING "
   161                              "(Any C WHERE EXISTS(C in_state B, D in_group F, G require_state B, G name 'read', "
   162                          "(Any C WHERE EXISTS(C in_state B, D in_group F, G require_state B, G name 'read', "
   162                              "G require_group F), D eid %(A)s, C is Card)")
   163                          "G require_group F), D eid %(A)s, C is Card)")
   163 
   164 
   164     def test_optional_var_2(self):
   165     def test_optional_var_2(self):
   165         constraint = ('X in_state S, U in_group G, P require_state S,'
   166         constraint = ('X in_state S, U in_group G, P require_state S,'
   166                            'P name "read", P require_group G')
   167                            'P name "read", P require_group G')
   167         rqlst = parse('Any A,C,T WHERE A documented_by C?, C title T')
   168         rqlst = parse('Any A,C,T WHERE A documented_by C?, C title T')
   168         rewrite(rqlst, {('C', 'X'): (constraint,)}, {})
   169         rewrite(rqlst, {('C', 'X'): (constraint,)}, {})
   169         self.failUnlessEqual(rqlst.as_string(),
   170         self.assertEqual(rqlst.as_string(),
   170                              "Any A,C,T WHERE A documented_by C?, A is Affaire "
   171                          "Any A,C,T WHERE A documented_by C?, A is Affaire "
   171                              "WITH C,T BEING "
   172                          "WITH C,T BEING "
   172                              "(Any C,T WHERE C title T, EXISTS(C in_state B, D in_group F, "
   173                          "(Any C,T WHERE C title T, EXISTS(C in_state B, D in_group F, "
   173                              "G require_state B, G name 'read', G require_group F), "
   174                          "G require_state B, G name 'read', G require_group F), "
   174                              "D eid %(A)s, C is Card)")
   175                          "D eid %(A)s, C is Card)")
   175 
   176 
   176     def test_optional_var_3(self):
   177     def test_optional_var_3(self):
   177         constraint1 = ('X in_state S, U in_group G, P require_state S,'
   178         constraint1 = ('X in_state S, U in_group G, P require_state S,'
   178                        'P name "read", P require_group G')
   179                        'P name "read", P require_group G')
   179         constraint2 = 'X in_state S, S name "public"'
   180         constraint2 = 'X in_state S, S name "public"'
   180         rqlst = parse('Any A,C,T WHERE A documented_by C?, C title T')
   181         rqlst = parse('Any A,C,T WHERE A documented_by C?, C title T')
   181         rewrite(rqlst, {('C', 'X'): (constraint1, constraint2)}, {})
   182         rewrite(rqlst, {('C', 'X'): (constraint1, constraint2)}, {})
   182         self.failUnlessEqual(rqlst.as_string(),
   183         self.assertEqual(rqlst.as_string(),
   183                              "Any A,C,T WHERE A documented_by C?, A is Affaire "
   184                          "Any A,C,T WHERE A documented_by C?, A is Affaire "
   184                              "WITH C,T BEING (Any C,T WHERE C title T, "
   185                          "WITH C,T BEING (Any C,T WHERE C title T, "
   185                              "EXISTS(C in_state B, D in_group F, G require_state B, G name 'read', G require_group F), "
   186                          "(EXISTS(C in_state B, D in_group F, G require_state B, G name 'read', G require_group F)) "
   186                              "D eid %(A)s, C is Card, "
   187                          "OR (EXISTS(C in_state E, E name 'public')), "
   187                              "EXISTS(C in_state E, E name 'public'))")
   188                          "D eid %(A)s, C is Card)")
   188 
   189 
   189     def test_optional_var_4(self):
   190     def test_optional_var_4(self):
   190         constraint1 = 'A created_by U, X documented_by A'
   191         constraint1 = 'A created_by U, X documented_by A'
   191         constraint2 = 'A created_by U, X concerne A'
   192         constraint2 = 'A created_by U, X concerne A'
   192         constraint3 = 'X created_by U'
   193         constraint3 = 'X created_by U'
   193         rqlst = parse('Any X,LA,Y WHERE LA? documented_by X, LA concerne Y')
   194         rqlst = parse('Any X,LA,Y WHERE LA? documented_by X, LA concerne Y')
   194         rewrite(rqlst, {('LA', 'X'): (constraint1, constraint2),
   195         rewrite(rqlst, {('LA', 'X'): (constraint1, constraint2),
   195                         ('X', 'X'): (constraint3,),
   196                         ('X', 'X'): (constraint3,),
   196                         ('Y', 'X'): (constraint3,)}, {})
   197                         ('Y', 'X'): (constraint3,)}, {})
   197         self.failUnlessEqual(rqlst.as_string(),
   198         self.assertEqual(rqlst.as_string(),
   198                              u'Any X,LA,Y WHERE LA? documented_by X, LA concerne Y, B eid %(C)s, '
   199                              u'Any X,LA,Y WHERE LA? documented_by X, LA concerne Y, B eid %(C)s, '
   199                              'EXISTS(X created_by B), EXISTS(Y created_by B), '
   200                              'EXISTS(X created_by B), EXISTS(Y created_by B), '
   200                              'X is Card, Y is IN(Division, Note, Societe) '
   201                              'X is Card, Y is IN(Division, Note, Societe) '
   201                              'WITH LA BEING (Any LA WHERE EXISTS(A created_by B, LA documented_by A), '
   202                              'WITH LA BEING (Any LA WHERE (EXISTS(A created_by B, LA documented_by A)) OR (EXISTS(E created_by B, LA concerne E)), '
   202                              'B eid %(D)s, LA is Affaire, EXISTS(E created_by B, LA concerne E))')
   203                              'B eid %(D)s, LA is Affaire)')
   203 
   204 
   204     def test_optional_var_inlined(self):
   205     def test_optional_var_inlined(self):
   205         c1 = ('X require_permission P')
   206         c1 = ('X require_permission P')
   206         c2 = ('X inlined_card O, O require_permission P')
   207         c2 = ('X inlined_card O, O require_permission P')
   207         rqlst = parse('Any C,A,R WHERE A? inlined_card C, A ref R')
   208         rqlst = parse('Any C,A,R WHERE A? inlined_card C, A ref R')
   208         rewrite(rqlst, {('C', 'X'): (c1,),
   209         rewrite(rqlst, {('C', 'X'): (c1,),
   209                         ('A', 'X'): (c2,),
   210                         ('A', 'X'): (c2,),
   210                         }, {})
   211                         }, {})
   211         # XXX suboptimal
   212         # XXX suboptimal
   212         self.failUnlessEqual(rqlst.as_string(),
   213         self.assertEqual(rqlst.as_string(),
   213                              "Any C,A,R WITH A,C,R BEING "
   214                          "Any C,A,R WITH A,C,R BEING "
   214                              "(Any A,C,R WHERE A? inlined_card C, A ref R, "
   215                          "(Any A,C,R WHERE A? inlined_card C, A ref R, "
   215                              "(A is NULL) OR (EXISTS(A inlined_card B, B require_permission D, "
   216                          "(A is NULL) OR (EXISTS(A inlined_card B, B require_permission D, "
   216                              "B is Card, D is CWPermission)), "
   217                          "B is Card, D is CWPermission)), "
   217                              "A is Affaire, C is Card, EXISTS(C require_permission E, E is CWPermission))")
   218                          "A is Affaire, C is Card, EXISTS(C require_permission E, E is CWPermission))")
   218 
   219 
   219     # def test_optional_var_inlined_has_perm(self):
   220     # def test_optional_var_inlined_has_perm(self):
   220     #     c1 = ('X require_permission P')
   221     #     c1 = ('X require_permission P')
   221     #     c2 = ('X inlined_card O, U has_read_permission O')
   222     #     c2 = ('X inlined_card O, U has_read_permission O')
   222     #     rqlst = parse('Any C,A,R WHERE A? inlined_card C, A ref R')
   223     #     rqlst = parse('Any C,A,R WHERE A? inlined_card C, A ref R')
   223     #     rewrite(rqlst, {('C', 'X'): (c1,),
   224     #     rewrite(rqlst, {('C', 'X'): (c1,),
   224     #                     ('A', 'X'): (c2,),
   225     #                     ('A', 'X'): (c2,),
   225     #                     }, {})
   226     #                     }, {})
   226     #     self.failUnlessEqual(rqlst.as_string(),
   227     #     self.assertEqual(rqlst.as_string(),
   227     #                          "")
   228     #                          "")
   228 
   229 
   229     def test_optional_var_inlined_imbricated_error(self):
   230     def test_optional_var_inlined_imbricated_error(self):
   230         c1 = ('X require_permission P')
   231         c1 = ('X require_permission P')
   231         c2 = ('X inlined_card O, O require_permission P')
   232         c2 = ('X inlined_card O, O require_permission P')
   240         c1 = ('X require_permission P')
   241         c1 = ('X require_permission P')
   241         c2 = ('X inlined_card O, O require_permission P')
   242         c2 = ('X inlined_card O, O require_permission P')
   242         rqlst = parse('Any A,W WHERE A inlined_card C?, C inlined_note N, '
   243         rqlst = parse('Any A,W WHERE A inlined_card C?, C inlined_note N, '
   243                       'N inlined_affaire W')
   244                       'N inlined_affaire W')
   244         rewrite(rqlst, {('C', 'X'): (c1,)}, {})
   245         rewrite(rqlst, {('C', 'X'): (c1,)}, {})
   245         self.failUnlessEqual(rqlst.as_string(),
   246         self.assertEqual(rqlst.as_string(),
   246                              'Any A,W WHERE A inlined_card C?, A is Affaire '
   247                          'Any A,W WHERE A inlined_card C?, A is Affaire '
   247                              'WITH C,N,W BEING (Any C,N,W WHERE C inlined_note N, '
   248                          'WITH C,N,W BEING (Any C,N,W WHERE C inlined_note N, '
   248                              'N inlined_affaire W, EXISTS(C require_permission B), '
   249                          'N inlined_affaire W, EXISTS(C require_permission B), '
   249                              'C is Card, N is Note, W is Affaire)')
   250                          'C is Card, N is Note, W is Affaire)')
   250 
   251 
   251     def test_relation_optimization_1_lhs(self):
   252     def test_relation_optimization_1_lhs(self):
   252         # since Card in_state State as monovalued cardinality, the in_state
   253         # since Card in_state State as monovalued cardinality, the in_state
   253         # relation used in the rql expression can be ignored and S replaced by
   254         # relation used in the rql expression can be ignored and S replaced by
   254         # the variable from the incoming query
   255         # the variable from the incoming query
   255         snippet = ('X in_state S, S name "hop"')
   256         snippet = ('X in_state S, S name "hop"')
   256         rqlst = parse('Card C WHERE C in_state STATE')
   257         rqlst = parse('Card C WHERE C in_state STATE')
   257         rewrite(rqlst, {('C', 'X'): (snippet,)}, {})
   258         rewrite(rqlst, {('C', 'X'): (snippet,)}, {})
   258         self.failUnlessEqual(rqlst.as_string(),
   259         self.assertEqual(rqlst.as_string(),
   259                              "Any C WHERE C in_state STATE, C is Card, "
   260                          "Any C WHERE C in_state STATE, C is Card, "
   260                              "EXISTS(STATE name 'hop'), STATE is State")
   261                          "EXISTS(STATE name 'hop'), STATE is State")
   261 
   262 
   262     def test_relation_optimization_1_rhs(self):
   263     def test_relation_optimization_1_rhs(self):
   263         snippet = ('TW subworkflow_exit X, TW name "hop"')
   264         snippet = ('TW subworkflow_exit X, TW name "hop"')
   264         rqlst = parse('WorkflowTransition C WHERE C subworkflow_exit EXIT')
   265         rqlst = parse('WorkflowTransition C WHERE C subworkflow_exit EXIT')
   265         rewrite(rqlst, {('EXIT', 'X'): (snippet,)}, {})
   266         rewrite(rqlst, {('EXIT', 'X'): (snippet,)}, {})
   266         self.failUnlessEqual(rqlst.as_string(),
   267         self.assertEqual(rqlst.as_string(),
   267                              "Any C WHERE C subworkflow_exit EXIT, C is WorkflowTransition, "
   268                          "Any C WHERE C subworkflow_exit EXIT, C is WorkflowTransition, "
   268                              "EXISTS(C name 'hop'), EXIT is SubWorkflowExitPoint")
   269                          "EXISTS(C name 'hop'), EXIT is SubWorkflowExitPoint")
   269 
   270 
   270     def test_relation_optimization_2_lhs(self):
   271     def test_relation_optimization_2_lhs(self):
   271         # optional relation can be shared if also optional in the snippet
   272         # optional relation can be shared if also optional in the snippet
   272         snippet = ('X in_state S?, S name "hop"')
   273         snippet = ('X in_state S?, S name "hop"')
   273         rqlst = parse('Card C WHERE C in_state STATE?')
   274         rqlst = parse('Card C WHERE C in_state STATE?')
   274         rewrite(rqlst, {('C', 'X'): (snippet,)}, {})
   275         rewrite(rqlst, {('C', 'X'): (snippet,)}, {})
   275         self.failUnlessEqual(rqlst.as_string(),
   276         self.assertEqual(rqlst.as_string(),
   276                              "Any C WHERE C in_state STATE?, C is Card, "
   277                          "Any C WHERE C in_state STATE?, C is Card, "
   277                              "EXISTS(STATE name 'hop'), STATE is State")
   278                          "EXISTS(STATE name 'hop'), STATE is State")
   278     def test_relation_optimization_2_rhs(self):
   279     def test_relation_optimization_2_rhs(self):
   279         snippet = ('TW? subworkflow_exit X, TW name "hop"')
   280         snippet = ('TW? subworkflow_exit X, TW name "hop"')
   280         rqlst = parse('SubWorkflowExitPoint EXIT WHERE C? subworkflow_exit EXIT')
   281         rqlst = parse('SubWorkflowExitPoint EXIT WHERE C? subworkflow_exit EXIT')
   281         rewrite(rqlst, {('EXIT', 'X'): (snippet,)}, {})
   282         rewrite(rqlst, {('EXIT', 'X'): (snippet,)}, {})
   282         self.failUnlessEqual(rqlst.as_string(),
   283         self.assertEqual(rqlst.as_string(),
   283                              "Any EXIT WHERE C? subworkflow_exit EXIT, EXIT is SubWorkflowExitPoint, "
   284                          "Any EXIT WHERE C? subworkflow_exit EXIT, EXIT is SubWorkflowExitPoint, "
   284                              "EXISTS(C name 'hop'), C is WorkflowTransition")
   285                          "EXISTS(C name 'hop'), C is WorkflowTransition")
   285 
   286 
   286     def test_relation_optimization_3_lhs(self):
   287     def test_relation_optimization_3_lhs(self):
   287         # optional relation in the snippet but not in the orig tree can be shared
   288         # optional relation in the snippet but not in the orig tree can be shared
   288         snippet = ('X in_state S?, S name "hop"')
   289         snippet = ('X in_state S?, S name "hop"')
   289         rqlst = parse('Card C WHERE C in_state STATE')
   290         rqlst = parse('Card C WHERE C in_state STATE')
   290         rewrite(rqlst, {('C', 'X'): (snippet,)}, {})
   291         rewrite(rqlst, {('C', 'X'): (snippet,)}, {})
   291         self.failUnlessEqual(rqlst.as_string(),
   292         self.assertEqual(rqlst.as_string(),
   292                              "Any C WHERE C in_state STATE, C is Card, "
   293                          "Any C WHERE C in_state STATE, C is Card, "
   293                              "EXISTS(STATE name 'hop'), STATE is State")
   294                          "EXISTS(STATE name 'hop'), STATE is State")
   294     def test_relation_optimization_3_rhs(self):
   295     def test_relation_optimization_3_rhs(self):
   295         snippet = ('TW? subworkflow_exit X, TW name "hop"')
   296         snippet = ('TW? subworkflow_exit X, TW name "hop"')
   296         rqlst = parse('WorkflowTransition C WHERE C subworkflow_exit EXIT')
   297         rqlst = parse('WorkflowTransition C WHERE C subworkflow_exit EXIT')
   297         rewrite(rqlst, {('EXIT', 'X'): (snippet,)}, {})
   298         rewrite(rqlst, {('EXIT', 'X'): (snippet,)}, {})
   298         self.failUnlessEqual(rqlst.as_string(),
   299         self.assertEqual(rqlst.as_string(),
   299                              "Any C WHERE C subworkflow_exit EXIT, C is WorkflowTransition, "
   300                          "Any C WHERE C subworkflow_exit EXIT, C is WorkflowTransition, "
   300                              "EXISTS(C name 'hop'), EXIT is SubWorkflowExitPoint")
   301                          "EXISTS(C name 'hop'), EXIT is SubWorkflowExitPoint")
   301 
   302 
   302     def test_relation_non_optimization_1_lhs(self):
   303     def test_relation_non_optimization_1_lhs(self):
   303         # but optional relation in the orig tree but not in the snippet can't be shared
   304         # but optional relation in the orig tree but not in the snippet can't be shared
   304         snippet = ('X in_state S, S name "hop"')
   305         snippet = ('X in_state S, S name "hop"')
   305         rqlst = parse('Card C WHERE C in_state STATE?')
   306         rqlst = parse('Card C WHERE C in_state STATE?')
   306         rewrite(rqlst, {('C', 'X'): (snippet,)}, {})
   307         rewrite(rqlst, {('C', 'X'): (snippet,)}, {})
   307         self.failUnlessEqual(rqlst.as_string(),
   308         self.assertEqual(rqlst.as_string(),
   308                              "Any C WHERE C in_state STATE?, C is Card, "
   309                          "Any C WHERE C in_state STATE?, C is Card, "
   309                              "EXISTS(C in_state A, A name 'hop', A is State), STATE is State")
   310                          "EXISTS(C in_state A, A name 'hop', A is State), STATE is State")
   310     def test_relation_non_optimization_1_rhs(self):
   311     def test_relation_non_optimization_1_rhs(self):
   311         snippet = ('TW subworkflow_exit X, TW name "hop"')
   312         snippet = ('TW subworkflow_exit X, TW name "hop"')
   312         rqlst = parse('SubWorkflowExitPoint EXIT WHERE C? subworkflow_exit EXIT')
   313         rqlst = parse('SubWorkflowExitPoint EXIT WHERE C? subworkflow_exit EXIT')
   313         rewrite(rqlst, {('EXIT', 'X'): (snippet,)}, {})
   314         rewrite(rqlst, {('EXIT', 'X'): (snippet,)}, {})
   314         self.failUnlessEqual(rqlst.as_string(),
   315         self.assertEqual(rqlst.as_string(),
   315                              "Any EXIT WHERE C? subworkflow_exit EXIT, EXIT is SubWorkflowExitPoint, "
   316                          "Any EXIT WHERE C? subworkflow_exit EXIT, EXIT is SubWorkflowExitPoint, "
   316                              "EXISTS(A subworkflow_exit EXIT, A name 'hop', A is WorkflowTransition), "
   317                          "EXISTS(A subworkflow_exit EXIT, A name 'hop', A is WorkflowTransition), "
   317                              "C is WorkflowTransition")
   318                          "C is WorkflowTransition")
   318 
   319 
   319     def test_unsupported_constraint_1(self):
   320     def test_unsupported_constraint_1(self):
   320         # CWUser doesn't have require_permission
   321         # CWUser doesn't have require_permission
   321         trinfo_constraint = ('X wf_info_for Y, Y require_permission P, P name "read"')
   322         trinfo_constraint = ('X wf_info_for Y, Y require_permission P, P name "read"')
   322         rqlst = parse('Any U,T WHERE U is CWUser, T wf_info_for U')
   323         rqlst = parse('Any U,T WHERE U is CWUser, T wf_info_for U')
   324 
   325 
   325     def test_unsupported_constraint_2(self):
   326     def test_unsupported_constraint_2(self):
   326         trinfo_constraint = ('X wf_info_for Y, Y require_permission P, P name "read"')
   327         trinfo_constraint = ('X wf_info_for Y, Y require_permission P, P name "read"')
   327         rqlst = parse('Any U,T WHERE U is CWUser, T wf_info_for U')
   328         rqlst = parse('Any U,T WHERE U is CWUser, T wf_info_for U')
   328         rewrite(rqlst, {('T', 'X'): (trinfo_constraint, 'X wf_info_for Y, Y in_group G, G name "managers"')}, {})
   329         rewrite(rqlst, {('T', 'X'): (trinfo_constraint, 'X wf_info_for Y, Y in_group G, G name "managers"')}, {})
   329         self.failUnlessEqual(rqlst.as_string(),
   330         self.assertEqual(rqlst.as_string(),
   330                              u"Any U,T WHERE U is CWUser, T wf_info_for U, "
   331                          u"Any U,T WHERE U is CWUser, T wf_info_for U, "
   331                              "EXISTS(U in_group B, B name 'managers', B is CWGroup), T is TrInfo")
   332                          "EXISTS(U in_group B, B name 'managers', B is CWGroup), T is TrInfo")
   332 
   333 
   333     def test_unsupported_constraint_3(self):
   334     def test_unsupported_constraint_3(self):
   334         self.skipTest('raise unauthorized for now')
   335         self.skipTest('raise unauthorized for now')
   335         trinfo_constraint = ('X wf_info_for Y, Y require_permission P, P name "read"')
   336         trinfo_constraint = ('X wf_info_for Y, Y require_permission P, P name "read"')
   336         rqlst = parse('Any T WHERE T wf_info_for X')
   337         rqlst = parse('Any T WHERE T wf_info_for X')
   337         rewrite(rqlst, {('T', 'X'): (trinfo_constraint, 'X in_group G, G name "managers"')}, {})
   338         rewrite(rqlst, {('T', 'X'): (trinfo_constraint, 'X in_group G, G name "managers"')}, {})
   338         self.failUnlessEqual(rqlst.as_string(),
   339         self.assertEqual(rqlst.as_string(),
   339                              u'XXX dunno what should be generated')
   340                          u'XXX dunno what should be generated')
   340 
   341 
   341     def test_add_ambiguity_exists(self):
   342     def test_add_ambiguity_exists(self):
   342         constraint = ('X concerne Y')
   343         constraint = ('X concerne Y')
   343         rqlst = parse('Affaire X')
   344         rqlst = parse('Affaire X')
   344         rewrite(rqlst, {('X', 'X'): (constraint,)}, {})
   345         rewrite(rqlst, {('X', 'X'): (constraint,)}, {})
   345         self.failUnlessEqual(rqlst.as_string(),
   346         self.assertEqual(rqlst.as_string(),
   346                              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))")
   347                          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))")
   347 
   348 
   348     def test_add_ambiguity_outerjoin(self):
   349     def test_add_ambiguity_outerjoin(self):
   349         constraint = ('X concerne Y')
   350         constraint = ('X concerne Y')
   350         rqlst = parse('Any X,C WHERE X? documented_by C')
   351         rqlst = parse('Any X,C WHERE X? documented_by C')
   351         rewrite(rqlst, {('X', 'X'): (constraint,)}, {})
   352         rewrite(rqlst, {('X', 'X'): (constraint,)}, {})
   352         # ambiguity are kept in the sub-query, no need to be resolved using OR
   353         # ambiguity are kept in the sub-query, no need to be resolved using OR
   353         self.failUnlessEqual(rqlst.as_string(),
   354         self.assertEqual(rqlst.as_string(),
   354                              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)")
   355                          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)")
   355 
   356 
   356 
   357 
   357     def test_rrqlexpr_nonexistant_subject_1(self):
   358     def test_rrqlexpr_nonexistant_subject_1(self):
   358         constraint = RRQLExpression('S owned_by U')
   359         constraint = RRQLExpression('S owned_by U')
   359         rqlst = parse('Card C')
   360         rqlst = parse('Card C')
   360         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'SU')
   361         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'SU')
   361         self.failUnlessEqual(rqlst.as_string(),
   362         self.assertEqual(rqlst.as_string(),
   362                              u"Any C WHERE C is Card, A eid %(B)s, EXISTS(C owned_by A)")
   363                          u"Any C WHERE C is Card, A eid %(B)s, EXISTS(C owned_by A)")
   363         rqlst = parse('Card C')
   364         rqlst = parse('Card C')
   364         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'OU')
   365         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'OU')
   365         self.failUnlessEqual(rqlst.as_string(),
   366         self.assertEqual(rqlst.as_string(),
   366                              u"Any C WHERE C is Card")
   367                          u"Any C WHERE C is Card")
   367         rqlst = parse('Card C')
   368         rqlst = parse('Card C')
   368         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'SOU')
   369         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'SOU')
   369         self.failUnlessEqual(rqlst.as_string(),
   370         self.assertEqual(rqlst.as_string(),
   370                              u"Any C WHERE C is Card, A eid %(B)s, EXISTS(C owned_by A)")
   371                          u"Any C WHERE C is Card, A eid %(B)s, EXISTS(C owned_by A)")
   371 
   372 
   372     def test_rrqlexpr_nonexistant_subject_2(self):
   373     def test_rrqlexpr_nonexistant_subject_2(self):
   373         constraint = RRQLExpression('S owned_by U, O owned_by U, O is Card')
   374         constraint = RRQLExpression('S owned_by U, O owned_by U, O is Card')
   374         rqlst = parse('Card C')
   375         rqlst = parse('Card C')
   375         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'SU')
   376         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'SU')
   376         self.failUnlessEqual(rqlst.as_string(),
   377         self.assertEqual(rqlst.as_string(),
   377                              'Any C WHERE C is Card, A eid %(B)s, EXISTS(C owned_by A)')
   378                          'Any C WHERE C is Card, A eid %(B)s, EXISTS(C owned_by A)')
   378         rqlst = parse('Card C')
   379         rqlst = parse('Card C')
   379         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'OU')
   380         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'OU')
   380         self.failUnlessEqual(rqlst.as_string(),
   381         self.assertEqual(rqlst.as_string(),
   381                              'Any C WHERE C is Card, B eid %(D)s, EXISTS(A owned_by B, A is Card)')
   382                          'Any C WHERE C is Card, B eid %(D)s, EXISTS(A owned_by B, A is Card)')
   382         rqlst = parse('Card C')
   383         rqlst = parse('Card C')
   383         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'SOU')
   384         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'SOU')
   384         self.failUnlessEqual(rqlst.as_string(),
   385         self.assertEqual(rqlst.as_string(),
   385                              'Any C WHERE C is Card, A eid %(B)s, EXISTS(C owned_by A, D owned_by A, D is Card)')
   386                          'Any C WHERE C is Card, A eid %(B)s, EXISTS(C owned_by A, D owned_by A, D is Card)')
   386 
   387 
   387     def test_rrqlexpr_nonexistant_subject_3(self):
   388     def test_rrqlexpr_nonexistant_subject_3(self):
   388         constraint = RRQLExpression('U in_group G, G name "users"')
   389         constraint = RRQLExpression('U in_group G, G name "users"')
   389         rqlst = parse('Card C')
   390         rqlst = parse('Card C')
   390         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'SU')
   391         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'SU')
   391         self.failUnlessEqual(rqlst.as_string(),
   392         self.assertEqual(rqlst.as_string(),
   392                              u'Any C WHERE C is Card, A eid %(B)s, EXISTS(A in_group D, D name "users", D is CWGroup)')
   393                          u'Any C WHERE C is Card, A eid %(B)s, EXISTS(A in_group D, D name "users", D is CWGroup)')
   393 
   394 
   394     def test_rrqlexpr_nonexistant_subject_4(self):
   395     def test_rrqlexpr_nonexistant_subject_4(self):
   395         constraint = RRQLExpression('U in_group G, G name "users", S owned_by U')
   396         constraint = RRQLExpression('U in_group G, G name "users", S owned_by U')
   396         rqlst = parse('Card C')
   397         rqlst = parse('Card C')
   397         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'SU')
   398         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'SU')
   398         self.failUnlessEqual(rqlst.as_string(),
   399         self.assertEqual(rqlst.as_string(),
   399                              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)')
   400                          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)')
   400         rqlst = parse('Card C')
   401         rqlst = parse('Card C')
   401         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'OU')
   402         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'OU')
   402         self.failUnlessEqual(rqlst.as_string(),
   403         self.assertEqual(rqlst.as_string(),
   403                              u'Any C WHERE C is Card, A eid %(B)s, EXISTS(A in_group D, D name "users", D is CWGroup)')
   404                          u'Any C WHERE C is Card, A eid %(B)s, EXISTS(A in_group D, D name "users", D is CWGroup)')
   404 
   405 
   405     def test_rrqlexpr_nonexistant_subject_5(self):
   406     def test_rrqlexpr_nonexistant_subject_5(self):
   406         constraint = RRQLExpression('S owned_by Z, O owned_by Z, O is Card')
   407         constraint = RRQLExpression('S owned_by Z, O owned_by Z, O is Card')
   407         rqlst = parse('Card C')
   408         rqlst = parse('Card C')
   408         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'S')
   409         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'S')
   409         self.failUnlessEqual(rqlst.as_string(),
   410         self.assertEqual(rqlst.as_string(),
   410                              u"Any C WHERE C is Card, EXISTS(C owned_by A, A is CWUser)")
   411                          u"Any C WHERE C is Card, EXISTS(C owned_by A, A is CWUser)")
   411 
   412 
   412     def test_rqlexpr_not_relation_1_1(self):
   413     def test_rqlexpr_not_relation_1_1(self):
   413         constraint = RRQLExpression('X owned_by Z, Z login "hop"', 'X')
   414         constraint = RRQLExpression('X owned_by Z, Z login "hop"', 'X')
   414         rqlst = parse('Affaire A WHERE NOT EXISTS(A documented_by C)')
   415         rqlst = parse('Affaire A WHERE NOT EXISTS(A documented_by C)')
   415         rewrite(rqlst, {('C', 'X'): (constraint,)}, {}, 'X')
   416         rewrite(rqlst, {('C', 'X'): (constraint,)}, {}, 'X')
   416         self.failUnlessEqual(rqlst.as_string(),
   417         self.assertEqual(rqlst.as_string(),
   417                              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')
   418                          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')
   418 
   419 
   419     def test_rqlexpr_not_relation_1_2(self):
   420     def test_rqlexpr_not_relation_1_2(self):
   420         constraint = RRQLExpression('X owned_by Z, Z login "hop"', 'X')
   421         constraint = RRQLExpression('X owned_by Z, Z login "hop"', 'X')
   421         rqlst = parse('Affaire A WHERE NOT EXISTS(A documented_by C)')
   422         rqlst = parse('Affaire A WHERE NOT EXISTS(A documented_by C)')
   422         rewrite(rqlst, {('A', 'X'): (constraint,)}, {}, 'X')
   423         rewrite(rqlst, {('A', 'X'): (constraint,)}, {}, 'X')
   423         self.failUnlessEqual(rqlst.as_string(),
   424         self.assertEqual(rqlst.as_string(),
   424                              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)')
   425                          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)')
   425 
   426 
   426     def test_rqlexpr_not_relation_2(self):
   427     def test_rqlexpr_not_relation_2(self):
   427         constraint = RRQLExpression('X owned_by Z, Z login "hop"', 'X')
   428         constraint = RRQLExpression('X owned_by Z, Z login "hop"', 'X')
   428         rqlst = rqlhelper.parse('Affaire A WHERE NOT A documented_by C', annotate=False)
   429         rqlst = rqlhelper.parse('Affaire A WHERE NOT A documented_by C', annotate=False)
   429         rewrite(rqlst, {('C', 'X'): (constraint,)}, {}, 'X')
   430         rewrite(rqlst, {('C', 'X'): (constraint,)}, {}, 'X')
   430         self.failUnlessEqual(rqlst.as_string(),
   431         self.assertEqual(rqlst.as_string(),
   431                              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')
   432                          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')
   432 
   433 
       
   434     def test_rqlexpr_multiexpr_outerjoin(self):
       
   435         c1 = RRQLExpression('X owned_by Z, Z login "hop"', 'X')
       
   436         c2 = RRQLExpression('X owned_by Z, Z login "hip"', 'X')
       
   437         c3 = RRQLExpression('X owned_by Z, Z login "momo"', 'X')
       
   438         rqlst = rqlhelper.parse('Any A WHERE A documented_by C?', annotate=False)
       
   439         rewrite(rqlst, {('C', 'X'): (c1, c2, c3)}, {}, 'X')
       
   440         self.assertEqual(rqlst.as_string(),
       
   441                          u'Any A WHERE A documented_by C?, A is Affaire '
       
   442                          'WITH C BEING (Any C WHERE ((EXISTS(C owned_by B, B login "hop")) '
       
   443                          'OR (EXISTS(C owned_by D, D login "momo"))) '
       
   444                          'OR (EXISTS(C owned_by A, A login "hip")), C is Card)')
       
   445 
       
   446     def test_multiple_erql_one_bad(self):
       
   447         #: reproduce bug #2236985
       
   448         #: (rqlrewrite fails to remove rewritten entry for unsupported constraint and then crash)
       
   449         #:
       
   450         #: This check a very rare code path triggered by the four condition below
       
   451 
       
   452         # 1. c_ok introduce an ambiguity
       
   453         c_ok = ERQLExpression('X concerne R')
       
   454         # 2. c_bad is just plain wrong and won't be kept
       
   455         # 3. but it declare a new variable
       
   456         # 4. this variable require a rewrite
       
   457         c_bad = ERQLExpression('X documented_by R, A in_state R')
       
   458 
       
   459         rqlst = parse('Any A, R WHERE A ref R, S is Affaire')
       
   460         rewrite(rqlst, {('A', 'X'): (c_ok, c_bad)}, {})
   433 
   461 
   434 if __name__ == '__main__':
   462 if __name__ == '__main__':
   435     unittest_main()
   463     unittest_main()