schema.py
changeset 9283 5f2c5eb1a820
parent 9253 77e31ede9b04
parent 9280 bae0caa8477a
child 9366 bcbc92223b35
equal deleted inserted replaced
9265:614762cdc357 9283:5f2c5eb1a820
    23 import re
    23 import re
    24 from os.path import join
    24 from os.path import join
    25 from logging import getLogger
    25 from logging import getLogger
    26 from warnings import warn
    26 from warnings import warn
    27 
    27 
    28 from logilab.common.decorators import cached, clear_cache, monkeypatch
    28 from logilab.common.decorators import cached, clear_cache, monkeypatch, cachedproperty
    29 from logilab.common.logging_ext import set_log_methods
    29 from logilab.common.logging_ext import set_log_methods
    30 from logilab.common.deprecation import deprecated, class_moved, moved
    30 from logilab.common.deprecation import deprecated, class_moved, moved
    31 from logilab.common.textutils import splitstrip
    31 from logilab.common.textutils import splitstrip
    32 from logilab.common.graph import get_cycles
    32 from logilab.common.graph import get_cycles
    33 from logilab.common.compat import any
    33 from logilab.common.compat import any
   694     """
   694     """
   695     # these are overridden by set_log_methods below
   695     # these are overridden by set_log_methods below
   696     # only defining here to prevent pylint from complaining
   696     # only defining here to prevent pylint from complaining
   697     info = warning = error = critical = exception = debug = lambda msg,*a,**kw: None
   697     info = warning = error = critical = exception = debug = lambda msg,*a,**kw: None
   698     # to be defined in concrete classes
   698     # to be defined in concrete classes
   699     full_rql = None
   699     rqlst = None
   700     predefined_variables = None
   700     predefined_variables = None
   701 
   701 
   702     def __init__(self, expression, mainvars, eid):
   702     def __init__(self, expression, mainvars, eid):
   703         """
   703         """
   704         :type mainvars: sequence of RQL variables' names. Can be provided as a
   704         :type mainvars: sequence of RQL variables' names. Can be provided as a
   713         elif not isinstance(mainvars, set):
   713         elif not isinstance(mainvars, set):
   714             mainvars = set(mainvars)
   714             mainvars = set(mainvars)
   715         self.mainvars = mainvars
   715         self.mainvars = mainvars
   716         self.expression = normalize_expression(expression)
   716         self.expression = normalize_expression(expression)
   717         try:
   717         try:
   718             self.rqlst = parse(self.full_rql, print_errors=False).children[0]
   718             self.full_rql = self.rqlst.as_string()
   719         except RQLSyntaxError:
   719         except RQLSyntaxError:
   720             raise RQLSyntaxError(expression)
   720             raise RQLSyntaxError(expression)
   721         for mainvar in mainvars:
   721         for mainvar in mainvars:
   722             # if variable is predefined, an extra reference is inserted
   722             # if variable is predefined, an extra reference is inserted
   723             # automatically (`VAR eid %(v)s`)
   723             # automatically (`VAR eid %(v)s`)
   728             if len(self.rqlst.defined_vars[mainvar].references()) < min_refs:
   728             if len(self.rqlst.defined_vars[mainvar].references()) < min_refs:
   729                 _LOGGER.warn('You did not use the %s variable in your RQL '
   729                 _LOGGER.warn('You did not use the %s variable in your RQL '
   730                              'expression %s', mainvar, self)
   730                              'expression %s', mainvar, self)
   731         # syntax tree used by read security (inserted in queries when necessary)
   731         # syntax tree used by read security (inserted in queries when necessary)
   732         self.snippet_rqlst = parse(self.minimal_rql, print_errors=False).children[0]
   732         self.snippet_rqlst = parse(self.minimal_rql, print_errors=False).children[0]
       
   733         # graph of links between variables, used by rql rewriter
       
   734         self.vargraph = vargraph(self.rqlst)
   733 
   735 
   734     def __str__(self):
   736     def __str__(self):
   735         return self.full_rql
   737         return self.full_rql
   736     def __repr__(self):
   738     def __repr__(self):
   737         return '%s(%s)' % (self.__class__.__name__, self.full_rql)
   739         return '%s(%s)' % (self.__class__.__name__, self.full_rql)
   753         return self.__class__(self.expression, self.mainvars)
   755         return self.__class__(self.expression, self.mainvars)
   754     def __getstate__(self):
   756     def __getstate__(self):
   755         return (self.expression, self.mainvars)
   757         return (self.expression, self.mainvars)
   756     def __setstate__(self, state):
   758     def __setstate__(self, state):
   757         self.__init__(*state)
   759         self.__init__(*state)
       
   760 
       
   761     @cachedproperty
       
   762     def rqlst(self):
       
   763         select = parse(self.minimal_rql, print_errors=False).children[0]
       
   764         defined = set(split_expression(self.expression))
       
   765         for varname in self.predefined_variables:
       
   766             if varname in defined:
       
   767                 select.add_eid_restriction(select.get_variable(varname), varname.lower(), 'Substitute')
       
   768         return select
   758 
   769 
   759     # permission rql expression specific stuff #################################
   770     # permission rql expression specific stuff #################################
   760 
   771 
   761     @cached
   772     @cached
   762     def transform_has_permission(self):
   773     def transform_has_permission(self):
   872                                     self.expression)
   883                                     self.expression)
   873 
   884 
   874 # rql expressions for use in permission definition #############################
   885 # rql expressions for use in permission definition #############################
   875 
   886 
   876 class ERQLExpression(RQLExpression):
   887 class ERQLExpression(RQLExpression):
   877     predefined_variables = 'UX'
   888     predefined_variables = 'XU'
   878 
   889 
   879     def __init__(self, expression, mainvars=None, eid=None):
   890     def __init__(self, expression, mainvars=None, eid=None):
   880         RQLExpression.__init__(self, expression, mainvars or 'X', eid)
   891         RQLExpression.__init__(self, expression, mainvars or 'X', eid)
   881 
       
   882     @property
       
   883     def full_rql(self):
       
   884         rql = self.minimal_rql
       
   885         rqlst = getattr(self, 'rqlst', None) # may be not set yet
       
   886         if rqlst is not None:
       
   887             defined = rqlst.defined_vars
       
   888         else:
       
   889             defined = set(split_expression(self.expression))
       
   890         if 'X' in defined:
       
   891             rql += ', X eid %(x)s'
       
   892         if 'U' in defined:
       
   893             rql += ', U eid %(u)s'
       
   894         return rql
       
   895 
   892 
   896     def check(self, _cw, eid=None, creating=False, **kwargs):
   893     def check(self, _cw, eid=None, creating=False, **kwargs):
   897         if 'X' in self.rqlst.defined_vars:
   894         if 'X' in self.rqlst.defined_vars:
   898             if eid is None:
   895             if eid is None:
   899                 if creating:
   896                 if creating:
   929         self.mainvars = mainvars
   926         self.mainvars = mainvars
   930         self.vargraph = vargraph(rqlst)
   927         self.vargraph = vargraph(rqlst)
   931 
   928 
   932 
   929 
   933 class RRQLExpression(RQLExpression):
   930 class RRQLExpression(RQLExpression):
   934     predefined_variables = 'USO'
   931     predefined_variables = 'SOU'
   935 
   932 
   936     def __init__(self, expression, mainvars=None, eid=None):
   933     def __init__(self, expression, mainvars=None, eid=None):
   937         if mainvars is None:
   934         if mainvars is None:
   938             mainvars = guess_rrqlexpr_mainvars(expression)
   935             mainvars = guess_rrqlexpr_mainvars(expression)
   939         RQLExpression.__init__(self, expression, mainvars, eid)
   936         RQLExpression.__init__(self, expression, mainvars, eid)
   940         # graph of links between variable, used by rql rewriter
       
   941         self.vargraph = vargraph(self.rqlst)
       
   942 
       
   943     @property
       
   944     def full_rql(self):
       
   945         rql = self.minimal_rql
       
   946         rqlst = getattr(self, 'rqlst', None) # may be not set yet
       
   947         if rqlst is not None:
       
   948             defined = rqlst.defined_vars
       
   949         else:
       
   950             defined = set(split_expression(self.expression))
       
   951         if 'S' in defined:
       
   952             rql += ', S eid %(s)s'
       
   953         if 'O' in defined:
       
   954             rql += ', O eid %(o)s'
       
   955         if 'U' in defined:
       
   956             rql += ', U eid %(u)s'
       
   957         return rql
       
   958 
   937 
   959     def check(self, _cw, fromeid=None, toeid=None):
   938     def check(self, _cw, fromeid=None, toeid=None):
   960         kwargs = {}
   939         kwargs = {}
   961         if 'S' in self.rqlst.defined_vars:
   940         if 'S' in self.rqlst.defined_vars:
   962             if fromeid is None:
   941             if fromeid is None: