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