rqlrewrite.py
changeset 8307 8be58694f416
parent 8296 f23782a2cdee
child 8452 1ad42383a9ec
equal deleted inserted replaced
8306:4da49700b06a 8307:8be58694f416
     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
   226                     vi['rhs_rels'] = dict( (r.r_type, r) for r in sti['rhsrelations'])
   226                     vi['rhs_rels'] = dict( (r.r_type, r) for r in sti['rhsrelations'])
   227                     vi['lhs_rels'] = dict( (r.r_type, r) for r in sti['relations']
   227                     vi['lhs_rels'] = dict( (r.r_type, r) for r in sti['relations']
   228                                            if not r in sti['rhsrelations'])
   228                                            if not r in sti['rhsrelations'])
   229                 else:
   229                 else:
   230                     vi['rhs_rels'] = vi['lhs_rels'] = {}
   230                     vi['rhs_rels'] = vi['lhs_rels'] = {}
   231         parent = None
   231         previous = None
   232         inserted = False
   232         inserted = False
   233         for rqlexpr in rqlexprs:
   233         for rqlexpr in rqlexprs:
   234             self.current_expr = rqlexpr
   234             self.current_expr = rqlexpr
   235             if varexistsmap is None:
   235             if varexistsmap is None:
   236                 try:
   236                 try:
   237                     new = self.insert_snippet(varmap, rqlexpr.snippet_rqlst, parent)
   237                     new = self.insert_snippet(varmap, rqlexpr.snippet_rqlst, previous)
   238                 except Unsupported:
   238                 except Unsupported:
   239                     continue
   239                     continue
   240                 inserted = True
   240                 inserted = True
   241                 if new is not None and self._insert_scope is None:
   241                 if new is not None and self._insert_scope is None:
   242                     self.exists_snippet[rqlexpr] = new
   242                     self.exists_snippet[rqlexpr] = new
   243                 parent = parent or new
   243                 previous = previous or new
   244             else:
   244             else:
   245                 # called to reintroduce snippet due to ambiguity creation,
   245                 # called to reintroduce snippet due to ambiguity creation,
   246                 # so skip snippets which are not introducing this ambiguity
   246                 # so skip snippets which are not introducing this ambiguity
   247                 exists = varexistsmap[varmap]
   247                 exists = varexistsmap[varmap]
   248                 if self.exists_snippet[rqlexpr] is exists:
   248                 if self.exists_snippet.get(rqlexpr) is exists:
   249                     self.insert_snippet(varmap, rqlexpr.snippet_rqlst, exists)
   249                     self.insert_snippet(varmap, rqlexpr.snippet_rqlst, exists)
   250         if varexistsmap is None and not inserted:
   250         if varexistsmap is None and not inserted:
   251             # no rql expression found matching rql solutions. User has no access right
   251             # no rql expression found matching rql solutions. User has no access right
   252             raise Unauthorized() # XXX may also be because of bad constraints in schema definition
   252             raise Unauthorized() # XXX may also be because of bad constraints in schema definition
   253 
   253 
   254     def insert_snippet(self, varmap, snippetrqlst, parent=None):
   254     def insert_snippet(self, varmap, snippetrqlst, previous=None):
   255         new = snippetrqlst.where.accept(self)
   255         new = snippetrqlst.where.accept(self)
   256         existing = self.existingvars
   256         existing = self.existingvars
   257         self.existingvars = None
   257         self.existingvars = None
   258         try:
   258         try:
   259             return self._insert_snippet(varmap, parent, new)
   259             return self._insert_snippet(varmap, previous, new)
   260         finally:
   260         finally:
   261             self.existingvars = existing
   261             self.existingvars = existing
   262 
   262 
   263     def _insert_snippet(self, varmap, parent, new):
   263     def _insert_snippet(self, varmap, previous, new):
       
   264         """insert `new` snippet into the syntax tree, which have been rewritten
       
   265         using `varmap`. In cases where an action is protected by several rql
       
   266         expresssion, `previous` will be the first rql expression which has been
       
   267         inserted, and so should be ORed with the following expressions.
       
   268         """
   264         if new is not None:
   269         if new is not None:
   265             if self._insert_scope is None:
   270             if self._insert_scope is None:
   266                 insert_scope = None
   271                 insert_scope = None
   267                 for vi in self.varinfos:
   272                 for vi in self.varinfos:
   268                     scope = vi.get('stinfo', {}).get('scope', self.select)
   273                     scope = vi.get('stinfo', {}).get('scope', self.select)
   272                         insert_scope = common_parent(scope, insert_scope)
   277                         insert_scope = common_parent(scope, insert_scope)
   273             else:
   278             else:
   274                 insert_scope = self._insert_scope
   279                 insert_scope = self._insert_scope
   275             if self._insert_scope is None and any(vi.get('stinfo', {}).get('optrelations')
   280             if self._insert_scope is None and any(vi.get('stinfo', {}).get('optrelations')
   276                                                   for vi in self.varinfos):
   281                                                   for vi in self.varinfos):
   277                 assert parent is None
   282                 assert previous is None
   278                 self._insert_scope = self.snippet_subquery(varmap, new)
   283                 self._insert_scope, new = self.snippet_subquery(varmap, new)
   279                 self.insert_pending()
   284                 self.insert_pending()
   280                 #self._insert_scope = None
   285                 #self._insert_scope = None
   281                 return
   286                 return new
   282             if not isinstance(new, (n.Exists, n.Not)):
   287             if not isinstance(new, (n.Exists, n.Not)):
   283                 new = n.Exists(new)
   288                 new = n.Exists(new)
   284             if parent is None:
   289             if previous is None:
   285                 insert_scope.add_restriction(new)
   290                 insert_scope.add_restriction(new)
   286             else:
   291             else:
   287                 grandpa = parent.parent
   292                 grandpa = previous.parent
   288                 or_ = n.Or(parent, new)
   293                 or_ = n.Or(previous, new)
   289                 grandpa.replace(parent, or_)
   294                 grandpa.replace(previous, or_)
   290             if not self.removing_ambiguity:
   295             if not self.removing_ambiguity:
   291                 try:
   296                 try:
   292                     self.compute_solutions()
   297                     self.compute_solutions()
   293                 except Unsupported:
   298                 except Unsupported:
   294                     # some solutions have been lost, can't apply this rql expr
   299                     # some solutions have been lost, can't apply this rql expr
   295                     if parent is None:
   300                     if previous is None:
   296                         self.current_statement().remove_node(new, undefine=True)
   301                         self.current_statement().remove_node(new, undefine=True)
   297                     else:
   302                     else:
   298                         grandpa.replace(or_, parent)
   303                         grandpa.replace(or_, previous)
   299                         self._cleanup_inserted(new)
   304                         self._cleanup_inserted(new)
   300                     raise
   305                     raise
   301                 else:
   306                 else:
   302                     with tempattr(self, '_insert_scope', new):
   307                     with tempattr(self, '_insert_scope', new):
   303                         self.insert_pending()
   308                         self.insert_pending()
   417             self.compute_solutions()
   422             self.compute_solutions()
   418         except Unsupported:
   423         except Unsupported:
   419             # some solutions have been lost, can't apply this rql expr
   424             # some solutions have been lost, can't apply this rql expr
   420             self.select.remove_subquery(self.select.with_[-1])
   425             self.select.remove_subquery(self.select.with_[-1])
   421             raise
   426             raise
   422         return subselect
   427         return subselect, snippetrqlst
   423 
   428 
   424     def remove_ambiguities(self, snippets, newsolutions):
   429     def remove_ambiguities(self, snippets, newsolutions):
   425         # the snippet has introduced some ambiguities, we have to resolve them
   430         # the snippet has introduced some ambiguities, we have to resolve them
   426         # "manually"
   431         # "manually"
   427         variantes = self.build_variantes(newsolutions)
   432         variantes = self.build_variantes(newsolutions)
   474                     del variante[key]
   479                     del variante[key]
   475         return variantes
   480         return variantes
   476 
   481 
   477     def _cleanup_inserted(self, node):
   482     def _cleanup_inserted(self, node):
   478         # cleanup inserted variable references
   483         # cleanup inserted variable references
       
   484         removed = set()
   479         for vref in node.iget_nodes(n.VariableRef):
   485         for vref in node.iget_nodes(n.VariableRef):
   480             vref.unregister_reference()
   486             vref.unregister_reference()
   481             if not vref.variable.stinfo['references']:
   487             if not vref.variable.stinfo['references']:
   482                 # no more references, undefine the variable
   488                 # no more references, undefine the variable
   483                 del self.select.defined_vars[vref.name]
   489                 del self.select.defined_vars[vref.name]
       
   490                 removed.add(vref.name)
       
   491         for key, newvar in self.rewritten.items(): # I mean items we alter it
       
   492             if newvar in removed:
       
   493                 del self.rewritten[key]
       
   494 
   484 
   495 
   485     def _may_be_shared_with(self, sniprel, target):
   496     def _may_be_shared_with(self, sniprel, target):
   486         """if the snippet relation can be skipped to use a relation from the
   497         """if the snippet relation can be skipped to use a relation from the
   487         original query, return that relation node
   498         original query, return that relation node
   488         """
   499         """