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') |