Beispiel #1
0
 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
Beispiel #2
0
 def visit_variableref(self, node):
     """get the sql name for a variable reference"""
     stmt = self.current_statement()
     if node.name in self.revvarmap:
         selectvar, index = self.revvarmap[node.name]
         vi = self.varinfos[index]
         if vi.get('const') is not None:
             return n.Constant(vi['const'], 'Int')
         return n.VariableRef(stmt.get_variable(selectvar))
     vname_or_term = self._get_varname_or_term(node.name)
     if isinstance(vname_or_term, str):
         return n.VariableRef(stmt.get_variable(vname_or_term))
     # shared term
     return vname_or_term.copy(stmt)
Beispiel #3
0
 def wrap_selects(self):
     """return a new rqlst root containing the given union as a subquery"""
     child = Union()
     for select in self.children[:]:
         child.append(select)
         self.remove_select(select)
     newselect = Select()
     aliases = [
         nodes.VariableRef(newselect.make_variable())
         for i in range(len(select.selection))
     ]
     newselect.add_subquery(nodes.SubQuery(aliases, child), check=False)
     for vref in aliases:
         newselect.append_selected(nodes.VariableRef(vref.variable))
     self.append_select(newselect)
Beispiel #4
0
def _expand_selection(terms, selected, aliases, select, newselect):
    for term in terms:
        for vref in term.iget_nodes(n.VariableRef):
            if vref.name not in selected:
                select.append_selected(vref)
                colalias = newselect.get_variable(vref.name, len(aliases))
                aliases.append(n.VariableRef(colalias))
                selected.add(vref.name)
Beispiel #5
0
 def copy(self):
     new = Insert()
     for etype, var in self.main_variables:
         vref = nodes.VariableRef(new.get_variable(var.name))
         new.add_main_variable(etype, vref)
     for child in self.main_relations:
         new.add_main_relation(child.copy(new))
     if self.where:
         new.set_where(self.where.copy(new))
     if self.having:
         new.set_having([sq.copy(new) for sq in self.having])
     return new
Beispiel #6
0
 def test_undo_node_having(self):
     qs = u'Any X WHERE X name N'
     tree = sparse(qs)
     select = tree.children[0]
     select.save_state()
     namevar = select.where.relation().children[-1].children[-1].variable
     comp = nodes.Comparison('>')
     maxf = nodes.Function('MAX')
     maxf.append(nodes.VariableRef(namevar))
     comp.append(maxf)
     comp.append(nodes.Constant(1, 'Int'))
     select.set_having([comp])
     select.recover()
     self.assertEqual(select.as_string(), qs)
Beispiel #7
0
 def add_selected(self, term, index=None):
     """override Select.add_selected to memoize modification when needed"""
     if isinstance(term, nodes.Variable):
         term = nodes.VariableRef(term, noautoref=1)
         term.register_reference()
     else:
         for var in term.iget_nodes(nodes.VariableRef):
             var = nodes.variable_ref(var)
             var.register_reference()
     if index is not None:
         self.selection.insert(index, term)
         term.parent = self
     else:
         self.append_selected(term)
     if self.should_register_op:
         from rql.undo import SelectVarOperation
         self.undo_manager.add_operation(SelectVarOperation(term))
Beispiel #8
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
Beispiel #9
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)