entity.py
changeset 7154 5e2f93b88d86
parent 7153 7df83a6d17c0
child 7177 0f2905cbe443
equal deleted inserted replaced
7153:7df83a6d17c0 7154:5e2f93b88d86
   155 
   155 
   156     @classmethod
   156     @classmethod
   157     def fetch_rql(cls, user, restriction=None, fetchattrs=None, mainvar='X',
   157     def fetch_rql(cls, user, restriction=None, fetchattrs=None, mainvar='X',
   158                   settype=True, ordermethod='fetch_order'):
   158                   settype=True, ordermethod='fetch_order'):
   159         """return a rql to fetch all entities of the class type"""
   159         """return a rql to fetch all entities of the class type"""
       
   160         # XXX update api and implementation to AST manipulation (see unrelated rql)
   160         restrictions = restriction or []
   161         restrictions = restriction or []
   161         if settype:
   162         if settype:
   162             restrictions.append('%s is %s' % (mainvar, cls.__regid__))
   163             restrictions.append('%s is %s' % (mainvar, cls.__regid__))
   163         if fetchattrs is None:
   164         if fetchattrs is None:
   164             fetchattrs = cls.fetch_attrs
   165             fetchattrs = cls.fetch_attrs
   751         return rql
   752         return rql
   752 
   753 
   753     # generic vocabulary methods ##############################################
   754     # generic vocabulary methods ##############################################
   754 
   755 
   755     def cw_unrelated_rql(self, rtype, targettype, role, ordermethod=None,
   756     def cw_unrelated_rql(self, rtype, targettype, role, ordermethod=None,
   756                       vocabconstraints=True):
   757                          vocabconstraints=True):
   757         """build a rql to fetch `targettype` entities unrelated to this entity
   758         """build a rql to fetch `targettype` entities unrelated to this entity
   758         using (rtype, role) relation.
   759         using (rtype, role) relation.
   759 
   760 
   760         Consider relation permissions so that returned entities may be actually
   761         Consider relation permissions so that returned entities may be actually
   761         linked by `rtype`.
   762         linked by `rtype`.
   762         """
   763         """
   763         ordermethod = ordermethod or 'fetch_unrelated_order'
   764         ordermethod = ordermethod or 'fetch_unrelated_order'
   764         if isinstance(rtype, basestring):
   765         if isinstance(rtype, basestring):
   765             rtype = self._cw.vreg.schema.rschema(rtype)
   766             rtype = self._cw.vreg.schema.rschema(rtype)
       
   767         rdef = rtype.role_rdef(self.e_schema, targettype, role)
       
   768         rewriter = RQLRewriter(self._cw)
       
   769         # initialize some variables according to the `role` of `self` in the
       
   770         # relation:
       
   771         # * variable for myself (`evar`) and searched entities (`searchvedvar`)
       
   772         # * entity type of the subject (`subjtype`) and of the object
       
   773         #   (`objtype`) of the relation
   766         if role == 'subject':
   774         if role == 'subject':
   767             evar, searchedvar = 'S', 'O'
   775             evar, searchedvar = 'S', 'O'
   768             subjtype, objtype = self.e_schema, targettype
   776             subjtype, objtype = self.e_schema, targettype
   769         else:
   777         else:
   770             searchedvar, evar = 'S', 'O'
   778             searchedvar, evar = 'S', 'O'
   771             objtype, subjtype = self.e_schema, targettype
   779             objtype, subjtype = self.e_schema, targettype
   772         rdef = rtype.role_rdef(self.e_schema, targettype, role)
   780         # initialize some variables according to `self` existance
   773         if self.has_eid():
   781         if self.has_eid():
   774             restriction = ['NOT S %s O' % rtype]
   782             restriction = ['NOT S %s O' % rtype]
   775             if rdef.role_cardinality(role) not in '?1':
   783             if rdef.role_cardinality(role) not in '?1':
   776                 # if cardinality in '1?', don't add restriction on eid
   784                 # if cardinality in '1?', don't add restriction on eid
   777                 restriction.append('%s eid %%(x)s' % evar)
   785                 restriction.append('%s eid %%(x)s' % evar)
   778             args = {'x': self.eid}
   786             args = {'x': self.eid}
   779             if role == 'subject':
   787             if role == 'subject':
   780                 securitycheck_args = {'fromeid': self.eid}
   788                 sec_check_args = {'fromeid': self.eid}
   781             else:
   789             else:
   782                 securitycheck_args = {'toeid': self.eid}
   790                 sec_check_args = {'toeid': self.eid}
       
   791             existant = None # instead of 'SO', improve perfs
   783         else:
   792         else:
   784             if rdef.role_cardinality(role) in '?1':
   793             if rdef.role_cardinality(role) in '?1':
   785                 restriction = ['NOT S %s O' % rtype]
   794                 restriction = ['NOT S %s O' % rtype]
   786             else:
   795             else:
   787                 restriction = []
   796                 restriction = []
   788             args = {}
   797             args = {}
   789             securitycheck_args = {}
   798             sec_check_args = {}
   790         insertsecurity = (rdef.has_local_role('add') and not
   799             existant = searchedvar
   791                           rdef.has_perm(self._cw, 'add', **securitycheck_args))
   800         # retreive entity class for targettype to compute base rql
   792         # XXX consider constraint.mainvars to check if constraint apply
   801         etypecls = self._cw.vreg['etypes'].etype_class(targettype)
       
   802         rql = etypecls.fetch_rql(self._cw.user, restriction,
       
   803                                  mainvar=searchedvar, ordermethod=ordermethod)
       
   804         select = self._cw.vreg.parse(self._cw, rql, args).children[0]
       
   805         # insert RQL expressions for schema constraints into the rql syntax tree
   793         if vocabconstraints:
   806         if vocabconstraints:
   794             # RQLConstraint is a subclass for RQLVocabularyConstraint, so they
   807             # RQLConstraint is a subclass for RQLVocabularyConstraint, so they
   795             # will be included as well
   808             # will be included as well
   796             cstrcls = RQLVocabularyConstraint
   809             cstrcls = RQLVocabularyConstraint
   797         else:
   810         else:
   798             cstrcls = RQLConstraint
   811             cstrcls = RQLConstraint
   799         for cstr in rdef.constraints:
   812         for cstr in rdef.constraints:
   800             if isinstance(cstr, RQLVocabularyConstraint) and searchedvar in cstr.mainvars:
   813             # consider constraint.mainvars to check if constraint apply
       
   814             if isinstance(cstr, cstrcls) and searchedvar in cstr.mainvars:
   801                 if not self.has_eid() and evar in cstr.mainvars:
   815                 if not self.has_eid() and evar in cstr.mainvars:
   802                     continue
   816                     continue
   803                 restriction.append(cstr.expression)
   817                 # compute a varmap suitable to RQLRewriter.rewrite argument
   804         etypecls = self._cw.vreg['etypes'].etype_class(targettype)
   818                 varmap = dict((v, v) for v in 'SO' if v in select.defined_vars
   805         rql = etypecls.fetch_rql(self._cw.user, restriction,
   819                               and v in cstr.mainvars)
   806                                  mainvar=searchedvar, ordermethod=ordermethod)
   820                 # rewrite constraint by constraint since we want a AND between
       
   821                 # expressions.
       
   822                 rewriter.rewrite(select, [(varmap, (cstr,))], select.solutions,
       
   823                                  args, existant)
       
   824         # insert security RQL expressions granting the permission to 'add' the
       
   825         # relation into the rql syntax tree, if necessary
       
   826         rqlexprs = rdef.get_rqlexprs('add')
       
   827         if rqlexprs and not rdef.has_perm(self._cw, 'add', **sec_check_args):
       
   828             # compute a varmap suitable to RQLRewriter.rewrite argument
       
   829             varmap = dict((v, v) for v in 'SO' if v in select.defined_vars)
       
   830             # rewrite all expressions at once since we want a OR between them.
       
   831             rewriter.rewrite(select, [(varmap, rqlexprs)], select.solutions,
       
   832                              args, existant)
   807         # ensure we have an order defined
   833         # ensure we have an order defined
   808         if not ' ORDERBY ' in rql:
   834         if not select.orderby:
   809             before, after = rql.split(' WHERE ', 1)
   835             select.add_sort_var(select.defined_vars[searchedvar])
   810             rql = '%s ORDERBY %s WHERE %s' % (before, searchedvar, after)
   836         # we're done, turn the rql syntax tree as a string
   811         if insertsecurity:
   837         rql = select.as_string()
   812             rqlexprs = rdef.get_rqlexprs('add')
       
   813             rewriter = RQLRewriter(self._cw)
       
   814             rqlst = self._cw.vreg.parse(self._cw, rql, args)
       
   815             if not self.has_eid():
       
   816                 existant = searchedvar
       
   817             else:
       
   818                 existant = None # instead of 'SO', improve perfs
       
   819             for select in rqlst.children:
       
   820                 varmap = {}
       
   821                 for var in 'SO':
       
   822                     if var in select.defined_vars:
       
   823                         varmap[var] = var
       
   824                 rewriter.rewrite(select, [(varmap, rqlexprs)],
       
   825                                  select.solutions, args, existant)
       
   826             rql = rqlst.as_string()
       
   827         return rql, args
   838         return rql, args
   828 
   839 
   829     def unrelated(self, rtype, targettype, role='subject', limit=None,
   840     def unrelated(self, rtype, targettype, role='subject', limit=None,
   830                   ordermethod=None): # XXX .cw_unrelated
   841                   ordermethod=None): # XXX .cw_unrelated
   831         """return a result set of target type objects that may be related
   842         """return a result set of target type objects that may be related
   833         """
   844         """
   834         try:
   845         try:
   835             rql, args = self.cw_unrelated_rql(rtype, targettype, role, ordermethod)
   846             rql, args = self.cw_unrelated_rql(rtype, targettype, role, ordermethod)
   836         except Unauthorized:
   847         except Unauthorized:
   837             return self._cw.empty_rset()
   848             return self._cw.empty_rset()
       
   849         # XXX should be set in unrelated rql when manipulating the AST
   838         if limit is not None:
   850         if limit is not None:
   839             before, after = rql.split(' WHERE ', 1)
   851             before, after = rql.split(' WHERE ', 1)
   840             rql = '%s LIMIT %s WHERE %s' % (before, limit, after)
   852             rql = '%s LIMIT %s WHERE %s' % (before, limit, after)
   841         return self._cw.execute(rql, args)
   853         return self._cw.execute(rql, args)
   842 
   854