21 from yams import BadSchemaDefinition |
21 from yams import BadSchemaDefinition |
22 from rql import parse, nodes, RQLHelper |
22 from rql import parse, nodes, RQLHelper |
23 |
23 |
24 from cubicweb import Unauthorized, rqlrewrite |
24 from cubicweb import Unauthorized, rqlrewrite |
25 from cubicweb.schema import RRQLExpression, ERQLExpression |
25 from cubicweb.schema import RRQLExpression, ERQLExpression |
26 from cubicweb.devtools import repotest, TestServerConfiguration |
26 from cubicweb.devtools import repotest, TestServerConfiguration, BaseApptestConfiguration |
27 |
27 |
28 |
28 |
29 def setUpModule(*args): |
29 def setUpModule(*args): |
30 global rqlhelper, schema |
30 global rqlhelper, schema |
31 config = TestServerConfiguration(RQLRewriteTC.datapath('rewrite')) |
31 config = TestServerConfiguration(RQLRewriteTC.datapath('rewrite')) |
70 expression='Any X WHERE '+snippet) |
71 expression='Any X WHERE '+snippet) |
71 or snippet |
72 or snippet |
72 for snippet in exprs] |
73 for snippet in exprs] |
73 snippets.append((dict([v]), rqlexprs)) |
74 snippets.append((dict([v]), rqlexprs)) |
74 rqlhelper.compute_solutions(rqlst.children[0], {'eid': eid_func_map}, kwargs=kwargs) |
75 rqlhelper.compute_solutions(rqlst.children[0], {'eid': eid_func_map}, kwargs=kwargs) |
75 solutions = rqlst.children[0].solutions |
76 rewriter.rewrite(rqlst.children[0], snippets, kwargs, existingvars) |
76 rewriter.rewrite(rqlst.children[0], snippets, solutions, kwargs, |
|
77 existingvars) |
|
78 test_vrefs(rqlst.children[0]) |
77 test_vrefs(rqlst.children[0]) |
79 return rewriter.rewritten |
78 return rewriter.rewritten |
80 |
79 |
81 def test_vrefs(node): |
80 def test_vrefs(node): |
82 vrefmaps = {} |
81 vrefmaps = {} |
200 'EXISTS(X created_by B), EXISTS(Y created_by B), ' |
199 'EXISTS(X created_by B), EXISTS(Y created_by B), ' |
201 'X is Card, Y is IN(Division, Note, Societe) ' |
200 'X is Card, Y is IN(Division, Note, Societe) ' |
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)), ' |
201 'WITH LA BEING (Any LA WHERE (EXISTS(A created_by B, LA documented_by A)) OR (EXISTS(E created_by B, LA concerne E)), ' |
203 'B eid %(D)s, LA is Affaire)') |
202 'B eid %(D)s, LA is Affaire)') |
204 |
203 |
|
204 |
|
205 def test_ambiguous_optional_same_exprs(self): |
|
206 """See #3013535""" |
|
207 # see test of the same name in RewriteFullTC: original problem is |
|
208 # unreproducible here because it actually lies in |
|
209 # RQLRewriter.insert_local_checks |
|
210 rqlst = parse('Any A,AR,X,CD WHERE A concerne X?, A ref AR, A eid %(a)s, X creation_date CD') |
|
211 rewrite(rqlst, {('X', 'X'): ('X created_by U',),}, {'a': 3}) |
|
212 self.assertEqual(rqlst.as_string(), |
|
213 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))') |
|
214 |
205 def test_optional_var_inlined(self): |
215 def test_optional_var_inlined(self): |
206 c1 = ('X require_permission P') |
216 c1 = ('X require_permission P') |
207 c2 = ('X inlined_card O, O require_permission P') |
217 c2 = ('X inlined_card O, O require_permission P') |
208 rqlst = parse('Any C,A,R WHERE A? inlined_card C, A ref R') |
218 rqlst = parse('Any C,A,R WHERE A? inlined_card C, A ref R') |
209 rewrite(rqlst, {('C', 'X'): (c1,), |
219 rewrite(rqlst, {('C', 'X'): (c1,), |
290 rqlst = parse('Card C WHERE C in_state STATE') |
300 rqlst = parse('Card C WHERE C in_state STATE') |
291 rewrite(rqlst, {('C', 'X'): (snippet,)}, {}) |
301 rewrite(rqlst, {('C', 'X'): (snippet,)}, {}) |
292 self.assertEqual(rqlst.as_string(), |
302 self.assertEqual(rqlst.as_string(), |
293 "Any C WHERE C in_state STATE, C is Card, " |
303 "Any C WHERE C in_state STATE, C is Card, " |
294 "EXISTS(STATE name 'hop'), STATE is State") |
304 "EXISTS(STATE name 'hop'), STATE is State") |
|
305 |
295 def test_relation_optimization_3_rhs(self): |
306 def test_relation_optimization_3_rhs(self): |
296 snippet = ('TW? subworkflow_exit X, TW name "hop"') |
307 snippet = ('TW? subworkflow_exit X, TW name "hop"') |
297 rqlst = parse('WorkflowTransition C WHERE C subworkflow_exit EXIT') |
308 rqlst = parse('WorkflowTransition C WHERE C subworkflow_exit EXIT') |
298 rewrite(rqlst, {('EXIT', 'X'): (snippet,)}, {}) |
309 rewrite(rqlst, {('EXIT', 'X'): (snippet,)}, {}) |
299 self.assertEqual(rqlst.as_string(), |
310 self.assertEqual(rqlst.as_string(), |
306 rqlst = parse('Card C WHERE C in_state STATE?') |
317 rqlst = parse('Card C WHERE C in_state STATE?') |
307 rewrite(rqlst, {('C', 'X'): (snippet,)}, {}) |
318 rewrite(rqlst, {('C', 'X'): (snippet,)}, {}) |
308 self.assertEqual(rqlst.as_string(), |
319 self.assertEqual(rqlst.as_string(), |
309 "Any C WHERE C in_state STATE?, C is Card, " |
320 "Any C WHERE C in_state STATE?, C is Card, " |
310 "EXISTS(C in_state A, A name 'hop', A is State), STATE is State") |
321 "EXISTS(C in_state A, A name 'hop', A is State), STATE is State") |
|
322 |
311 def test_relation_non_optimization_1_rhs(self): |
323 def test_relation_non_optimization_1_rhs(self): |
312 snippet = ('TW subworkflow_exit X, TW name "hop"') |
324 snippet = ('TW subworkflow_exit X, TW name "hop"') |
313 rqlst = parse('SubWorkflowExitPoint EXIT WHERE C? subworkflow_exit EXIT') |
325 rqlst = parse('SubWorkflowExitPoint EXIT WHERE C? subworkflow_exit EXIT') |
314 rewrite(rqlst, {('EXIT', 'X'): (snippet,)}, {}) |
326 rewrite(rqlst, {('EXIT', 'X'): (snippet,)}, {}) |
315 self.assertEqual(rqlst.as_string(), |
327 self.assertEqual(rqlst.as_string(), |
316 "Any EXIT WHERE C? subworkflow_exit EXIT, EXIT is SubWorkflowExitPoint, " |
328 "Any EXIT WHERE C? subworkflow_exit EXIT, EXIT is SubWorkflowExitPoint, " |
317 "EXISTS(A subworkflow_exit EXIT, A name 'hop', A is WorkflowTransition), " |
329 "EXISTS(A subworkflow_exit EXIT, A name 'hop', A is WorkflowTransition), " |
318 "C is WorkflowTransition") |
330 "C is WorkflowTransition") |
|
331 |
|
332 def test_relation_non_optimization_2(self): |
|
333 """See #3024730""" |
|
334 # 'X inlined_note N' must not be shared with 'C inlined_note N' |
|
335 # previously inserted, else this may introduce duplicated results, as N |
|
336 # will then be shared by multiple EXISTS and so at SQL generation time, |
|
337 # the table will be in the FROM clause of the outermost query |
|
338 rqlst = parse('Any A,C WHERE A inlined_card C') |
|
339 rewrite(rqlst, {('A', 'X'): ('X inlined_card C, C inlined_note N, N owned_by U',), |
|
340 ('C', 'X'): ('X inlined_note N, N owned_by U',)}, {}) |
|
341 self.assertEqual(rqlst.as_string(), |
|
342 'Any A,C WHERE A inlined_card C, D eid %(E)s, ' |
|
343 'EXISTS(C inlined_note B, B owned_by D, B is Note), ' |
|
344 'EXISTS(C inlined_note F, F owned_by D, F is Note), ' |
|
345 'A is Affaire, C is Card') |
319 |
346 |
320 def test_unsupported_constraint_1(self): |
347 def test_unsupported_constraint_1(self): |
321 # CWUser doesn't have require_permission |
348 # CWUser doesn't have require_permission |
322 trinfo_constraint = ('X wf_info_for Y, Y require_permission P, P name "read"') |
349 trinfo_constraint = ('X wf_info_for Y, Y require_permission P, P name "read"') |
323 rqlst = parse('Any U,T WHERE U is CWUser, T wf_info_for U') |
350 rqlst = parse('Any U,T WHERE U is CWUser, T wf_info_for U') |
457 c_bad = ERQLExpression('X documented_by R, A in_state R') |
484 c_bad = ERQLExpression('X documented_by R, A in_state R') |
458 |
485 |
459 rqlst = parse('Any A, R WHERE A ref R, S is Affaire') |
486 rqlst = parse('Any A, R WHERE A ref R, S is Affaire') |
460 rewrite(rqlst, {('A', 'X'): (c_ok, c_bad)}, {}) |
487 rewrite(rqlst, {('A', 'X'): (c_ok, c_bad)}, {}) |
461 |
488 |
|
489 |
|
490 from cubicweb.devtools.testlib import CubicWebTC |
|
491 from logilab.common.decorators import classproperty |
|
492 |
|
493 class RewriteFullTC(CubicWebTC): |
|
494 @classproperty |
|
495 def config(cls): |
|
496 return BaseApptestConfiguration(apphome=cls.datapath('rewrite')) |
|
497 |
|
498 def process(self, rql, args=None): |
|
499 if args is None: |
|
500 args = {} |
|
501 querier = self.repo.querier |
|
502 union = querier.parse(rql) |
|
503 querier.solutions(self.session, union, args) |
|
504 querier._annotate(union) |
|
505 plan = querier.plan_factory(union, args, self.session) |
|
506 plan.preprocess(union) |
|
507 return union |
|
508 |
|
509 def test_ambiguous_optional_same_exprs(self): |
|
510 """See #3013535""" |
|
511 edef1 = self.schema['Societe'] |
|
512 edef2 = self.schema['Division'] |
|
513 edef3 = self.schema['Note'] |
|
514 with self.temporary_permissions((edef1, {'read': (ERQLExpression('X owned_by U'),)}), |
|
515 (edef2, {'read': (ERQLExpression('X owned_by U'),)}), |
|
516 (edef3, {'read': (ERQLExpression('X owned_by U'),)})): |
|
517 union = self.process('Any A,AR,X,CD WHERE A concerne X?, A ref AR, X creation_date CD') |
|
518 self.assertEqual('Any A,AR,X,CD WHERE A concerne X?, A ref AR, A is Affaire ' |
|
519 'WITH X,CD BEING (Any X,CD WHERE X creation_date CD, ' |
|
520 'EXISTS(X owned_by %(A)s), X is IN(Division, Note, Societe))', |
|
521 union.as_string()) |
|
522 |
|
523 |
|
524 def test_xxxx(self): |
|
525 edef1 = self.schema['Societe'] |
|
526 edef2 = self.schema['Division'] |
|
527 read_expr = ERQLExpression('X responsable E, U has_read_permission E') |
|
528 with self.temporary_permissions((edef1, {'read': (read_expr,)}), |
|
529 (edef2, {'read': (read_expr,)})): |
|
530 union = self.process('Any X,AA,AC,AD ORDERBY AD DESC ' |
|
531 'WHERE X responsable E, X nom AA, ' |
|
532 'X responsable AC?, AC modification_date AD') |
|
533 self.assertEqual('Any X,AA,AC,AD ORDERBY AD DESC ' |
|
534 'WHERE X responsable E, X nom AA, ' |
|
535 'X responsable AC?, AC modification_date AD, ' |
|
536 'AC is CWUser, E is CWUser, X is IN(Division, Societe)', |
|
537 union.as_string()) |
|
538 |
462 if __name__ == '__main__': |
539 if __name__ == '__main__': |
463 unittest_main() |
540 unittest_main() |