rset.py
brancholdstable
changeset 7074 e4580e5f0703
parent 6915 99eb71b311e4
child 7285 39437617f3f0
child 7395 09ffcc04bd21
equal deleted inserted replaced
6749:48f468f33704 7074:e4580e5f0703
   384             # may have None values in case of outer join (or aggregat on eid
   384             # may have None values in case of outer join (or aggregat on eid
   385             # hacks)
   385             # hacks)
   386             if self.rows[i][col] is not None:
   386             if self.rows[i][col] is not None:
   387                 yield self.get_entity(i, col)
   387                 yield self.get_entity(i, col)
   388 
   388 
       
   389     def iter_rows_with_entities(self):
       
   390         """ iterates over rows, and for each row
       
   391         eids are converted to plain entities
       
   392         """
       
   393         for i, row in enumerate(self):
       
   394             _row = []
       
   395             for j, col in enumerate(row):
       
   396                 try:
       
   397                     _row.append(self.get_entity(i, j) if col is not None else col)
       
   398                 except NotAnEntity:
       
   399                     _row.append(col)
       
   400             yield _row
       
   401 
   389     def complete_entity(self, row, col=0, skip_bytes=True):
   402     def complete_entity(self, row, col=0, skip_bytes=True):
   390         """short cut to get an completed entity instance for a particular
   403         """short cut to get an completed entity instance for a particular
   391         row (all instance's attributes have been fetched)
   404         row (all instance's attributes have been fetched)
   392         """
   405         """
   393         entity = self.get_entity(row, col)
   406         entity = self.get_entity(row, col)
   399         """convenience method for query retrieving a single entity, returns a
   412         """convenience method for query retrieving a single entity, returns a
   400         partially initialized Entity instance.
   413         partially initialized Entity instance.
   401 
   414 
   402         .. warning::
   415         .. warning::
   403 
   416 
   404           Due to the cache wrapping this function, you should NEVER
   417           Due to the cache wrapping this function, you should NEVER give row as
   405           give row as a named parameter (i.e. rset.get_entity(req, 0)
   418           a named parameter (i.e. `rset.get_entity(0, 1)` is OK but
   406           is OK but rset.get_entity(row=0, req=req) isn't)
   419           `rset.get_entity(row=0, col=1)` isn't)
   407 
   420 
   408         :type row,col: int, int
   421         :type row,col: int, int
   409         :param row,col:
   422         :param row,col:
   410           row and col numbers localizing the entity among the result's table
   423           row and col numbers localizing the entity among the result's table
   411 
   424 
   419         except KeyError:
   432         except KeyError:
   420             raise NotAnEntity(etype)
   433             raise NotAnEntity(etype)
   421         return self._build_entity(row, col)
   434         return self._build_entity(row, col)
   422 
   435 
   423     def _build_entity(self, row, col):
   436     def _build_entity(self, row, col):
   424         """internal method to get a single entity, returns a
   437         """internal method to get a single entity, returns a partially
   425         partially initialized Entity instance.
   438         initialized Entity instance.
   426 
   439 
   427         partially means that only attributes selected in the RQL
   440         partially means that only attributes selected in the RQL query will be
   428         query will be directly assigned to the entity.
   441         directly assigned to the entity.
   429 
   442 
   430         :type row,col: int, int
   443         :type row,col: int, int
   431         :param row,col:
   444         :param row,col:
   432           row and col numbers localizing the entity among the result's table
   445           row and col numbers localizing the entity among the result's table
   433 
   446 
   472                 select, col = rqlst.locate_subquery(col, etype, self.args)
   485                 select, col = rqlst.locate_subquery(col, etype, self.args)
   473             else:
   486             else:
   474                 select = rqlst
   487                 select = rqlst
   475             # take care, due to outer join support, we may find None
   488             # take care, due to outer join support, we may find None
   476             # values for non final relation
   489             # values for non final relation
   477             for i, attr, role in attr_desc_iterator(select, col):
   490             for i, attr, role in attr_desc_iterator(select, col, entity.cw_col):
   478                 outerselidx = rqlst.subquery_selection_index(select, i)
       
   479                 if outerselidx is None:
       
   480                     continue
       
   481                 if role == 'subject':
   491                 if role == 'subject':
   482                     rschema = eschema.subjrels[attr]
   492                     rschema = eschema.subjrels[attr]
   483                     if rschema.final:
   493                     if rschema.final:
   484                         if attr == 'eid':
   494                         if attr == 'eid':
   485                             entity.eid = rowvalues[outerselidx]
   495                             entity.eid = rowvalues[i]
   486                         else:
   496                         else:
   487                             entity[attr] = rowvalues[outerselidx]
   497                             entity.cw_attr_cache[attr] = rowvalues[i]
   488                         continue
   498                         continue
   489                 else:
   499                 else:
   490                     rschema = eschema.objrels[attr]
   500                     rschema = eschema.objrels[attr]
   491                 rdef = eschema.rdef(attr, role)
   501                 rdef = eschema.rdef(attr, role)
   492                 # only keep value if it can't be multivalued
   502                 # only keep value if it can't be multivalued
   493                 if rdef.role_cardinality(role) in '1?':
   503                 if rdef.role_cardinality(role) in '1?':
   494                     if rowvalues[outerselidx] is None:
   504                     if rowvalues[i] is None:
   495                         if role == 'subject':
   505                         if role == 'subject':
   496                             rql = 'Any Y WHERE X %s Y, X eid %s'
   506                             rql = 'Any Y WHERE X %s Y, X eid %s'
   497                         else:
   507                         else:
   498                             rql = 'Any Y WHERE Y %s X, X eid %s'
   508                             rql = 'Any Y WHERE Y %s X, X eid %s'
   499                         rrset = ResultSet([], rql % (attr, entity.eid))
   509                         rrset = ResultSet([], rql % (attr, entity.eid))
   500                         rrset.req = req
   510                         rrset.req = req
   501                     else:
   511                     else:
   502                         rrset = self._build_entity(row, outerselidx).as_rset()
   512                         rrset = self._build_entity(row, i).as_rset()
   503                     entity.cw_set_relation_cache(attr, role, rrset)
   513                     entity.cw_set_relation_cache(attr, role, rrset)
   504         return entity
   514         return entity
   505 
   515 
   506     @cached
   516     @cached
   507     def syntax_tree(self):
   517     def syntax_tree(self):
   635             if rel.r_type == 'has_text':
   645             if rel.r_type == 'has_text':
   636                 __, rhs = rel.get_variable_parts()
   646                 __, rhs = rel.get_variable_parts()
   637                 return rhs.eval(self.args)
   647                 return rhs.eval(self.args)
   638         return None
   648         return None
   639 
   649 
   640 
   650 def _get_variable(term):
   641 def attr_desc_iterator(rqlst, index=0):
   651     # XXX rewritten const
       
   652     # use iget_nodes for (hack) case where we have things like MAX(V)
       
   653     for vref in term.iget_nodes(nodes.VariableRef):
       
   654         return vref.variable
       
   655 
       
   656 def attr_desc_iterator(select, selectidx, rootidx):
   642     """return an iterator on a list of 2-uple (index, attr_relation)
   657     """return an iterator on a list of 2-uple (index, attr_relation)
   643     localizing attribute relations of the main variable in a result's row
   658     localizing attribute relations of the main variable in a result's row
   644 
   659 
   645     :type rqlst: rql.stmts.Select
   660     :type rqlst: rql.stmts.Select
   646     :param rqlst: the RQL syntax tree to describe
   661     :param rqlst: the RQL syntax tree to describe
   647 
   662 
   648     :return:
   663     :return:
   649       a generator on (index, relation, target) describing column being
   664       a generator on (index, relation, target) describing column being
   650       attribute of the main variable
   665       attribute of the main variable
   651     """
   666     """
   652     main = rqlst.selection[index]
   667     rootselect = select
   653     for i, term in enumerate(rqlst.selection):
   668     while rootselect.parent.parent is not None:
   654         if i == index:
   669         rootselect = rootselect.parent.parent.parent
       
   670     rootmain = rootselect.selection[selectidx]
       
   671     rootmainvar = _get_variable(rootmain)
       
   672     assert rootmainvar
       
   673     root = rootselect.parent
       
   674     selectmain = select.selection[selectidx]
       
   675     for i, term in enumerate(rootselect.selection):
       
   676         rootvar = _get_variable(term)
       
   677         if rootvar is None:
   655             continue
   678             continue
   656         # XXX rewritten const
   679         if rootvar.name == rootmainvar.name:
   657         # use iget_nodes for (hack) case where we have things like MAX(V)
   680             continue
   658         for vref in term.iget_nodes(nodes.VariableRef):
   681         if select is not rootselect:
   659             var = vref.variable
   682             term = select.selection[root.subquery_selection_index(select, i)]
   660             break
   683         var = _get_variable(term)
   661         else:
   684         if var is None:
   662             continue
   685             continue
   663         for ref in var.references():
   686         for ref in var.references():
   664             rel = ref.relation()
   687             rel = ref.relation()
   665             if rel is None or rel.is_types_restriction():
   688             if rel is None or rel.is_types_restriction():
   666                 continue
   689                 continue
   667             lhs, rhs = rel.get_variable_parts()
   690             lhs, rhs = rel.get_variable_parts()
   668             if main.is_equivalent(lhs):
   691             if selectmain.is_equivalent(lhs):
   669                 if rhs.is_equivalent(term):
   692                 if rhs.is_equivalent(term):
   670                     yield (i, rel.r_type, 'subject')
   693                     yield (i, rel.r_type, 'subject')
   671             elif main.is_equivalent(rhs):
   694             elif selectmain.is_equivalent(rhs):
   672                 if lhs.is_equivalent(term):
   695                 if lhs.is_equivalent(term):
   673                     yield (i, rel.r_type, 'object')
   696                     yield (i, rel.r_type, 'object')