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: |