示例#1
0
文件: rset.py 项目: zogzog/cubicweb
 def _limit_offset_rql(self, limit, offset):
     rqlst = self.syntax_tree()
     if len(rqlst.children) == 1:
         select = rqlst.children[0]
         olimit, ooffset = select.limit, select.offset
         select.limit, select.offset = limit, offset
         rql = rqlst.as_string(kwargs=self.args)
         # restore original limit/offset
         select.limit, select.offset = olimit, ooffset
     else:
         newselect = stmts.Select()
         newselect.limit = limit
         newselect.offset = offset
         aliases = [
             nodes.VariableRef(newselect.get_variable(chr(65 + i), i))
             for i in range(len(rqlst.children[0].selection))
         ]
         for vref in aliases:
             newselect.append_selected(nodes.VariableRef(vref.variable))
         newselect.set_with([nodes.SubQuery(aliases, rqlst)], check=False)
         newunion = stmts.Union()
         newunion.append(newselect)
         rql = newunion.as_string(kwargs=self.args)
         rqlst.parent = None
     return rql
示例#2
0
 def test_copy(self):
     tree = self._parse(
         "Any X,LOWER(Y) GROUPBY N ORDERBY N WHERE X is Person, X name N, X date >= TODAY"
     )
     select = stmts.Select()
     restriction = tree.children[0].where
     self.check_equal_but_not_same(restriction, restriction.copy(select))
     group = tree.children[0].groupby[0]
     self.check_equal_but_not_same(group, group.copy(select))
     sort = tree.children[0].orderby[0]
     self.check_equal_but_not_same(sort, sort.copy(select))
示例#3
0
 def test_copy_internals(self):
     root = self.parse(
         'Any X,U WHERE C owned_by U, NOT X owned_by U, X eid 1, C eid 2')
     self.simplify(root)
     stmt = root.children[0]
     self.assertEqual(stmt.defined_vars['U'].valuable_references(), 3)
     copy = stmts.Select()
     copy.append_selected(stmt.selection[0].copy(copy))
     copy.append_selected(stmt.selection[1].copy(copy))
     copy.set_where(stmt.where.copy(copy))
     newroot = stmts.Union()
     newroot.append(copy)
     self.annotate(newroot)
     self.simplify(newroot)
     self.assertEqual(
         newroot.as_string(),
         'Any 1,U WHERE 2 owned_by U, NOT EXISTS(1 owned_by U)',
     )
     self.assertEqual(copy.defined_vars['U'].valuable_references(), 3)
示例#4
0
 def snippet_subquery(self, varmap, transformedsnippet):
     """introduce the given snippet in a subquery"""
     subselect = stmts.Select()
     snippetrqlst = n.Exists(transformedsnippet.copy(subselect))
     get_rschema = self.schema.rschema
     aliases = []
     done = set()
     for i, (selectvar, _) in enumerate(varmap):
         need_null_test = False
         subselectvar = subselect.get_variable(selectvar)
         subselect.append_selected(n.VariableRef(subselectvar))
         aliases.append(selectvar)
         todo = [(selectvar, self.varinfos[i]['stinfo'])]
         while todo:
             varname, stinfo = todo.pop()
             done.add(varname)
             for rel in iter_relations(stinfo):
                 if rel in done:
                     continue
                 done.add(rel)
                 rschema = get_rschema(rel.r_type)
                 if rschema.final or rschema.inlined:
                     subselect_vrefs = []
                     rel.children[0].name = varname  # XXX explain why
                     subselect.add_restriction(rel.copy(subselect))
                     for vref in rel.children[1].iget_nodes(n.VariableRef):
                         if isinstance(vref.variable, n.ColumnAlias):
                             # XXX could probably be handled by generating the
                             # subquery into the detected subquery
                             raise BadSchemaDefinition(
                                 "cant insert security because of usage two inlined "
                                 "relations in this query. You should probably at "
                                 "least uninline %s" % rel.r_type)
                         subselect.append_selected(vref.copy(subselect))
                         aliases.append(vref.name)
                         subselect_vrefs.append(vref)
                     self.select.remove_node(rel)
                     # when some inlined relation has to be copied in the
                     # subquery and that relation is optional, we need to
                     # test that either value is NULL or that the snippet
                     # condition is satisfied
                     if varname == selectvar and rel.optional and rschema.inlined:
                         need_null_test = True
                     # also, if some attributes or inlined relation of the
                     # object variable are accessed, we need to get all those
                     # from the subquery as well
                     for vref in subselect_vrefs:
                         if vref.name not in done and rschema.inlined:
                             # we can use vref here define in above for loop
                             ostinfo = vref.variable.stinfo
                             for orel in iter_relations(ostinfo):
                                 orschema = get_rschema(orel.r_type)
                                 if orschema.final or orschema.inlined:
                                     todo.append((vref.name, ostinfo))
                                     break
         if need_null_test:
             snippetrqlst = n.Or(
                 n.make_relation(subselect.get_variable(selectvar), 'is',
                                 (None, None), n.Constant,
                                 operator='='),
                 snippetrqlst)
     subselect.add_restriction(snippetrqlst)
     if self.u_varname:
         # generate an identifier for the substitution
         argname = subselect.allocate_varname()
         while argname in self.kwargs:
             argname = subselect.allocate_varname()
         subselect.add_constant_restriction(subselect.get_variable(self.u_varname),
                                            'eid', argname, 'Substitute')
         self.kwargs[argname] = self.session.user.eid
     add_types_restriction(self.schema, subselect, subselect,
                           solutions=self.solutions)
     myunion = stmts.Union()
     myunion.append(subselect)
     aliases = [n.VariableRef(self.select.get_variable(name, i))
                for i, name in enumerate(aliases)]
     self.select.add_subquery(n.SubQuery(aliases, myunion), check=False)
     self._cleanup_inserted(transformedsnippet)
     try:
         self.compute_solutions()
     except Unsupported:
         # some solutions have been lost, can't apply this rql expr
         self.select.remove_subquery(self.select.with_[-1])
         raise
     return subselect, snippetrqlst
示例#5
0
    def insert_local_checks(self, select, kwargs,
                            localchecks, restricted, noinvariant):
        """
        select: the rql syntax tree Select node
        kwargs: query arguments

        localchecks: {(('Var name', (rqlexpr1, rqlexpr2)),
                       ('Var name1', (rqlexpr1, rqlexpr23))): [solution]}

              (see querier._check_permissions docstring for more information)

        restricted: set of variable names to which an rql expression has to be
              applied

        noinvariant: set of variable names that can't be considered has
              invariant due to security reason (will be filed by this method)
        """
        nbtrees = len(localchecks)
        myunion = union = select.parent
        # transform in subquery when len(localchecks)>1 and groups
        if nbtrees > 1 and (select.orderby or select.groupby
                            or select.having or select.has_aggregat
                            or select.distinct
                            or select.limit or select.offset):
            newselect = stmts.Select()
            # only select variables in subqueries
            origselection = select.selection
            select.select_only_variables()
            select.has_aggregat = False
            # create subquery first so correct node are used on copy
            # (eg ColumnAlias instead of Variable)
            aliases = [n.VariableRef(newselect.get_variable(vref.name, i))
                       for i, vref in enumerate(select.selection)]
            selected = set(vref.name for vref in aliases)
            # now copy original selection and groups
            for term in origselection:
                newselect.append_selected(term.copy(newselect))
            if select.orderby:
                sortterms = []
                for sortterm in select.orderby:
                    sortterms.append(sortterm.copy(newselect))
                    for fnode in sortterm.get_nodes(n.Function):
                        if fnode.name == 'FTIRANK':
                            # we've to fetch the has_text relation as well
                            var = fnode.children[0].variable
                            rel = next(iter(var.stinfo['ftirels']))
                            assert not rel.ored(), 'unsupported'
                            newselect.add_restriction(rel.copy(newselect))
                            # remove relation from the orig select and
                            # cleanup variable stinfo
                            rel.parent.remove(rel)
                            var.stinfo['ftirels'].remove(rel)
                            var.stinfo['relations'].remove(rel)
                            # XXX not properly re-annotated after security insertion?
                            newvar = newselect.get_variable(var.name)
                            newvar.stinfo.setdefault('ftirels', set()).add(rel)
                            newvar.stinfo.setdefault('relations', set()).add(rel)
                newselect.set_orderby(sortterms)
                _expand_selection(select.orderby, selected, aliases, select, newselect)
                select.orderby = ()  # XXX dereference?
            if select.groupby:
                newselect.set_groupby([g.copy(newselect) for g in select.groupby])
                _expand_selection(select.groupby, selected, aliases, select, newselect)
                select.groupby = ()  # XXX dereference?
            if select.having:
                newselect.set_having([g.copy(newselect) for g in select.having])
                _expand_selection(select.having, selected, aliases, select, newselect)
                select.having = ()  # XXX dereference?
            if select.limit:
                newselect.limit = select.limit
                select.limit = None
            if select.offset:
                newselect.offset = select.offset
                select.offset = 0
            myunion = stmts.Union()
            newselect.set_with([n.SubQuery(aliases, myunion)], check=False)
            newselect.distinct = select.distinct
            solutions = [sol.copy() for sol in select.solutions]
            cleanup_solutions(newselect, solutions)
            newselect.set_possible_types(solutions)
            # if some solutions doesn't need rewriting, insert original
            # select as first union subquery
            if () in localchecks:
                myunion.append(select)
            # we're done, replace original select by the new select with
            # subqueries (more added in the loop below)
            union.replace(select, newselect)
        elif not () in localchecks:
            union.remove(select)
        for lcheckdef, lchecksolutions in localchecks.items():
            if not lcheckdef:
                continue
            myrqlst = select.copy(solutions=lchecksolutions)
            myunion.append(myrqlst)
            # in-place rewrite + annotation / simplification
            lcheckdef = [({v: 'X'}, rqlexprs) for v, rqlexprs in lcheckdef]
            self.rewrite(myrqlst, lcheckdef, kwargs)
            _add_noinvariant(noinvariant, restricted, myrqlst, nbtrees)
        if () in localchecks:
            select.set_possible_types(localchecks[()])
            add_types_restriction(self.schema, select)
            _add_noinvariant(noinvariant, restricted, select, nbtrees)
        self.annotate(union)