550 but this is not enforced at the repository level |
550 but this is not enforced at the repository level |
551 |
551 |
552 restriction is additional rql restriction that will be added to |
552 restriction is additional rql restriction that will be added to |
553 a predefined query, where the S and O variables respectivly represent |
553 a predefined query, where the S and O variables respectivly represent |
554 the subject and the object of the relation |
554 the subject and the object of the relation |
555 """ |
555 |
556 |
556 mainvars is a string that should be used as selection variable (eg |
557 def __init__(self, restriction): |
557 `'Any %s WHERE ...' % mainvars`). If not specified, an attempt will be |
|
558 done to guess it according to variable used in the expression. |
|
559 """ |
|
560 |
|
561 def __init__(self, restriction, mainvars=None): |
558 self.restriction = restriction |
562 self.restriction = restriction |
|
563 if mainvars is None: |
|
564 mainvars = guess_rrqlexpr_mainvars(restriction) |
|
565 self.mainvars = mainvars |
|
566 assert not ';' in mainvars # XXX check mainvars as for RQLExpression? |
559 |
567 |
560 def serialize(self): |
568 def serialize(self): |
561 return self.restriction |
569 # start with a comma for bw compat, see below |
|
570 return ';' + self.mainvars + ';' + self.restriction |
562 |
571 |
563 def deserialize(cls, value): |
572 def deserialize(cls, value): |
564 return cls(value) |
573 # XXX < 3.5.10 bw compat |
|
574 if not value.startswith(';'): |
|
575 return cls(value) |
|
576 _, mainvars, restriction = value.split(';', 2) |
|
577 return cls(restriction, mainvars) |
565 deserialize = classmethod(deserialize) |
578 deserialize = classmethod(deserialize) |
566 |
579 |
567 def check(self, entity, rtype, value): |
580 def check(self, entity, rtype, value): |
568 """return true if the value satisfy the constraint, else false""" |
581 """return true if the value satisfy the constraint, else false""" |
569 # implemented as a hook in the repository |
582 # implemented as a hook in the repository |
583 |
596 |
584 class RQLConstraint(RQLVocabularyConstraint): |
597 class RQLConstraint(RQLVocabularyConstraint): |
585 """the rql constraint is similar to the RQLVocabularyConstraint but |
598 """the rql constraint is similar to the RQLVocabularyConstraint but |
586 are also enforced at the repository level |
599 are also enforced at the repository level |
587 """ |
600 """ |
|
601 distinct_query = False |
|
602 |
588 def exec_query(self, session, eidfrom, eidto): |
603 def exec_query(self, session, eidfrom, eidto): |
589 if eidto is None: |
604 if eidto is None: |
590 rql = 'Any S WHERE S eid %(s)s, ' + self.restriction |
605 # checking constraint for an attribute relation |
591 return session.unsafe_execute(rql, {'s': eidfrom}, 's', |
606 restriction = 'S eid %(s)s, ' + self.restriction |
592 build_descr=False) |
607 args, ck = {'s': eidfrom}, 's' |
593 rql = 'Any S,O WHERE S eid %(s)s, O eid %(o)s, ' + self.restriction |
608 else: |
594 return session.unsafe_execute(rql, {'s': eidfrom, 'o': eidto}, |
609 restriction =rql = 'S eid %(s)s, O eid %(o)s, ' + self.restriction |
595 ('s', 'o'), build_descr=False) |
610 args, ck = {'s': eidfrom, 'o': eidto}, ('s', 'o') |
|
611 rql = 'Any %s WHERE %s' % (self.mainvars, restriction) |
|
612 if self.distinct_query: |
|
613 rql = 'DISTINCT ' + rql |
|
614 return session.unsafe_execute(rql, args, ck, build_descr=False) |
|
615 |
596 def error(self, eid, rtype, msg): |
616 def error(self, eid, rtype, msg): |
597 raise ValidationError(eid, {rtype: msg}) |
617 raise ValidationError(eid, {rtype: msg}) |
598 |
618 |
599 def repo_check(self, session, eidfrom, rtype, eidto=None): |
619 def repo_check(self, session, eidfrom, rtype, eidto=None): |
600 """raise ValidationError if the relation doesn't satisfy the constraint |
620 """raise ValidationError if the relation doesn't satisfy the constraint |
607 |
627 |
608 class RQLUniqueConstraint(RQLConstraint): |
628 class RQLUniqueConstraint(RQLConstraint): |
609 """the unique rql constraint check that the result of the query isn't |
629 """the unique rql constraint check that the result of the query isn't |
610 greater than one |
630 greater than one |
611 """ |
631 """ |
|
632 distinct_query = True |
|
633 |
612 def repo_check(self, session, eidfrom, rtype, eidto=None): |
634 def repo_check(self, session, eidfrom, rtype, eidto=None): |
613 """raise ValidationError if the relation doesn't satisfy the constraint |
635 """raise ValidationError if the relation doesn't satisfy the constraint |
614 """ |
636 """ |
615 if len(self.exec_query(session, eidfrom, eidto)) > 1: |
637 if len(self.exec_query(session, eidfrom, eidto)) > 1: |
616 # XXX at this point dunno if the validation error `occured` on |
638 # XXX at this point dunno if the validation error `occured` on |
795 return self._check(session, x=eid) |
817 return self._check(session, x=eid) |
796 return self._check(session) |
818 return self._check(session) |
797 |
819 |
798 PyFileReader.context['ERQLExpression'] = yobsolete(ERQLExpression) |
820 PyFileReader.context['ERQLExpression'] = yobsolete(ERQLExpression) |
799 |
821 |
|
822 def guess_rrqlexpr_mainvars(expression): |
|
823 defined = set(split_expression(expression)) |
|
824 mainvars = [] |
|
825 if 'S' in defined: |
|
826 mainvars.append('S') |
|
827 if 'O' in defined: |
|
828 mainvars.append('O') |
|
829 if 'U' in defined: |
|
830 mainvars.append('U') |
|
831 if not mainvars: |
|
832 raise Exception('unable to guess selection variables') |
|
833 return ','.join(mainvars) |
|
834 |
800 class RRQLExpression(RQLExpression): |
835 class RRQLExpression(RQLExpression): |
801 def __init__(self, expression, mainvars=None, eid=None): |
836 def __init__(self, expression, mainvars=None, eid=None): |
802 if mainvars is None: |
837 if mainvars is None: |
803 defined = set(split_expression(expression)) |
838 mainvars = guess_rrqlexpr_mainvars(expression) |
804 mainvars = [] |
|
805 if 'S' in defined: |
|
806 mainvars.append('S') |
|
807 if 'O' in defined: |
|
808 mainvars.append('O') |
|
809 if 'U' in defined: |
|
810 mainvars.append('U') |
|
811 if not mainvars: |
|
812 raise Exception('unable to guess selection variables') |
|
813 mainvars = ','.join(mainvars) |
|
814 RQLExpression.__init__(self, expression, mainvars, eid) |
839 RQLExpression.__init__(self, expression, mainvars, eid) |
815 # graph of links between variable, used by rql rewriter |
840 # graph of links between variable, used by rql rewriter |
816 self.vargraph = {} |
841 self.vargraph = {} |
817 for relation in self.rqlst.get_nodes(nodes.Relation): |
842 for relation in self.rqlst.get_nodes(nodes.Relation): |
818 try: |
843 try: |