10 """ |
10 """ |
11 __docformat__ = "restructuredtext en" |
11 __docformat__ = "restructuredtext en" |
12 |
12 |
13 from rql import nodes as n, stmts, TypeResolverException |
13 from rql import nodes as n, stmts, TypeResolverException |
14 |
14 |
15 from logilab.common.compat import any |
|
16 from logilab.common.graph import has_path |
15 from logilab.common.graph import has_path |
17 |
16 |
18 from cubicweb import Unauthorized, typed_eid |
17 from cubicweb import Unauthorized, typed_eid |
19 |
18 |
20 |
19 |
108 newsolutions.append(newsol) |
107 newsolutions.append(newsol) |
109 solutions.remove(newsol) |
108 solutions.remove(newsol) |
110 return newsolutions |
109 return newsolutions |
111 |
110 |
112 |
111 |
113 class Unsupported(Exception): pass |
112 class Unsupported(Exception): |
|
113 """raised when an rql expression can't be inserted in some rql query |
|
114 because it create an unresolvable query (eg no solutions found) |
|
115 """ |
114 |
116 |
115 |
117 |
116 class RQLRewriter(object): |
118 class RQLRewriter(object): |
117 """insert some rql snippets into another rql syntax tree |
119 """insert some rql snippets into another rql syntax tree |
118 |
120 |
289 self.insert_snippets([((varname, 'X'), rqlexprs)]) |
291 self.insert_snippets([((varname, 'X'), rqlexprs)]) |
290 |
292 |
291 def snippet_subquery(self, varmap, transformedsnippet): |
293 def snippet_subquery(self, varmap, transformedsnippet): |
292 """introduce the given snippet in a subquery""" |
294 """introduce the given snippet in a subquery""" |
293 subselect = stmts.Select() |
295 subselect = stmts.Select() |
294 selectvar, snippetvar = varmap |
296 selectvar = varmap[0] |
295 subselect.append_selected(n.VariableRef( |
297 subselect.append_selected(n.VariableRef( |
296 subselect.get_variable(selectvar))) |
298 subselect.get_variable(selectvar))) |
297 aliases = [selectvar] |
299 aliases = [selectvar] |
298 subselect.add_restriction(transformedsnippet.copy(subselect)) |
300 subselect.add_restriction(transformedsnippet.copy(subselect)) |
299 stinfo = self.varinfo['stinfo'] |
301 stinfo = self.varinfo['stinfo'] |
400 try: |
402 try: |
401 if target == 'object': |
403 if target == 'object': |
402 orel = self.varinfo['lhs_rels'][sniprel.r_type] |
404 orel = self.varinfo['lhs_rels'][sniprel.r_type] |
403 cardindex = 0 |
405 cardindex = 0 |
404 ttypes_func = rschema.objects |
406 ttypes_func = rschema.objects |
405 rprop = rschema.rproperty |
407 rdef = rschema.rdef |
406 else: # target == 'subject': |
408 else: # target == 'subject': |
407 orel = self.varinfo['rhs_rels'][sniprel.r_type] |
409 orel = self.varinfo['rhs_rels'][sniprel.r_type] |
408 cardindex = 1 |
410 cardindex = 1 |
409 ttypes_func = rschema.subjects |
411 ttypes_func = rschema.subjects |
410 rprop = lambda x, y, z: rschema.rproperty(y, x, z) |
412 rdef = lambda x, y: rschema.rdef(y, x) |
411 except KeyError, ex: |
413 except KeyError: |
412 # may be raised by self.varinfo['xhs_rels'][sniprel.r_type] |
414 # may be raised by self.varinfo['xhs_rels'][sniprel.r_type] |
413 return None |
415 return None |
414 # can't share neged relation or relations with different outer join |
416 # can't share neged relation or relations with different outer join |
415 if (orel.neged(strict=True) or sniprel.neged(strict=True) |
417 if (orel.neged(strict=True) or sniprel.neged(strict=True) |
416 or (orel.optional and orel.optional != sniprel.optional)): |
418 or (orel.optional and orel.optional != sniprel.optional)): |
417 return None |
419 return None |
418 # if cardinality is in '?1', we can ignore the snippet relation and use |
420 # if cardinality is in '?1', we can ignore the snippet relation and use |
419 # variable from the original query |
421 # variable from the original query |
420 for etype in self.varinfo['stinfo']['possibletypes']: |
422 for etype in self.varinfo['stinfo']['possibletypes']: |
421 for ttype in ttypes_func(etype): |
423 for ttype in ttypes_func(etype): |
422 if rprop(etype, ttype, 'cardinality')[cardindex] in '+*': |
424 if rdef(etype, ttype).cardinality[cardindex] in '+*': |
423 return None |
425 return None |
424 return orel |
426 return orel |
425 |
427 |
426 def _use_orig_term(self, snippet_varname, term): |
428 def _use_orig_term(self, snippet_varname, term): |
427 key = (self.current_expr, self.varmap, snippet_varname) |
429 key = (self.current_expr, self.varmap, snippet_varname) |
439 # generate an identifier for the substitution |
441 # generate an identifier for the substitution |
440 argname = select.allocate_varname() |
442 argname = select.allocate_varname() |
441 while argname in self.kwargs: |
443 while argname in self.kwargs: |
442 argname = select.allocate_varname() |
444 argname = select.allocate_varname() |
443 # insert "U eid %(u)s" |
445 # insert "U eid %(u)s" |
444 var = select.get_variable(self.u_varname) |
446 select.add_constant_restriction( |
445 select.add_constant_restriction(select.get_variable(self.u_varname), |
447 select.get_variable(self.u_varname), |
446 'eid', unicode(argname), 'Substitute') |
448 'eid', unicode(argname), 'Substitute') |
447 self.kwargs[argname] = self.session.user.eid |
449 self.kwargs[argname] = self.session.user.eid |
448 return self.u_varname |
450 return self.u_varname |
449 key = (self.current_expr, self.varmap, vname) |
451 key = (self.current_expr, self.varmap, vname) |
450 try: |
452 try: |
451 return self.rewritten[key] |
453 return self.rewritten[key] |