schema.py
branchstable
changeset 3961 d1cbf77db999
parent 3827 c7142a4e3470
child 3963 0d592677e55f
equal deleted inserted replaced
3960:8cbf18c703be 3961:d1cbf77db999
   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: