# HG changeset patch # User Sylvain Thénault # Date 1269523573 -3600 # Node ID b3b0b808a0edc77b1b943696514daefdadb27a3a # Parent 4cc020ee70e274910afd6dda85fea1084efe357c# Parent 55e2602545cd88affa13a16daaed3485480bb3b1 backport stable diff -r 55e2602545cd -r b3b0b808a0ed devtools/testlib.py --- a/devtools/testlib.py Thu Mar 25 14:25:44 2010 +0100 +++ b/devtools/testlib.py Thu Mar 25 14:26:13 2010 +0100 @@ -5,6 +5,8 @@ :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses """ +from __future__ import with_statement + __docformat__ = "restructuredtext en" import os @@ -29,6 +31,7 @@ from cubicweb.dbapi import repo_connect, ConnectionProperties, ProgrammingError from cubicweb.sobjects import notification from cubicweb.web import Redirect, application +from cubicweb.server.session import security_enabled from cubicweb.devtools import SYSTEM_ENTITIES, SYSTEM_RELATIONS, VIEW_VALIDATORS from cubicweb.devtools import fake, htmlparser @@ -764,6 +767,10 @@ """this method populates the database with `how_many` entities of each possible type. It also inserts random relations between them """ + with security_enabled(self.session, read=False, write=False): + self._auto_populate(how_many) + + def _auto_populate(self, how_many): cu = self.cursor() self.custom_populate(how_many, cu) vreg = self.vreg diff -r 55e2602545cd -r b3b0b808a0ed rqlrewrite.py --- a/rqlrewrite.py Thu Mar 25 14:25:44 2010 +0100 +++ b/rqlrewrite.py Thu Mar 25 14:26:13 2010 +0100 @@ -41,15 +41,15 @@ except KeyError: continue stinfo = var.stinfo - if stinfo.get('uidrels'): + if stinfo.get('uidrel') is not None: continue # eid specified, no need for additional type specification try: - typerels = rqlst.defined_vars[varname].stinfo.get('typerels') + typerel = rqlst.defined_vars[varname].stinfo.get('typerel') except KeyError: assert varname in rqlst.aliases continue - if newroot is rqlst and typerels: - mytyperel = iter(typerels).next() + if newroot is rqlst and typerel is not None: + mytyperel = typerel else: for vref in newroot.defined_vars[varname].references(): rel = vref.relation() @@ -80,7 +80,7 @@ # tree is not annotated yet, no scope set so add the restriction # to the root rel = newroot.add_type_restriction(var, possibletypes) - stinfo['typerels'] = frozenset((rel,)) + stinfo['typerel'] = rel stinfo['possibletypes'] = possibletypes diff -r 55e2602545cd -r b3b0b808a0ed server/msplanner.py --- a/server/msplanner.py Thu Mar 25 14:25:44 2010 +0100 +++ b/server/msplanner.py Thu Mar 25 14:26:13 2010 +0100 @@ -309,21 +309,20 @@ # find for each source which variable/solution are supported for varname, varobj in self.rqlst.defined_vars.items(): # if variable has an eid specified, we can get its source directly - # NOTE: use uidrels and not constnode to deal with "X eid IN(1,2,3,4)" - if varobj.stinfo['uidrels']: - vrels = varobj.stinfo['relations'] - varobj.stinfo['uidrels'] - for rel in varobj.stinfo['uidrels']: - for const in rel.children[1].get_nodes(Constant): - eid = const.eval(self.plan.args) - source = self._session.source_from_eid(eid) - if vrels and not any(source.support_relation(r.r_type) - for r in vrels): - self._set_source_for_term(self.system_source, varobj) - else: - self._set_source_for_term(source, varobj) + # NOTE: use uidrel and not constnode to deal with "X eid IN(1,2,3,4)" + if varobj.stinfo['uidrel'] is not None: + rel = varobj.stinfo['uidrel'] + for const in rel.children[1].get_nodes(Constant): + eid = const.eval(self.plan.args) + source = self._session.source_from_eid(eid) + if vrels and not any(source.support_relation(r.r_type) + for r in vrels if not r is rel): + self._set_source_for_term(self.system_source, varobj) + else: + self._set_source_for_term(source, varobj) continue rels = varobj.stinfo['relations'] - if not rels and not varobj.stinfo['typerels']: + if not rels and varobj.stinfo['typerel'] is None: # (rare) case where the variable has no type specified nor # relation accessed ex. "Any MAX(X)" self._set_source_for_term(self.system_source, varobj) @@ -700,7 +699,7 @@ for var in select.defined_vars.itervalues(): if not var in terms: stinfo = var.stinfo - for ovar, rtype in stinfo['attrvars']: + for ovar, rtype in stinfo.get('attrvars', ()): if ovar in terms: needsel.add(var.name) terms.append(var) @@ -778,20 +777,19 @@ # variable is refed by an outer scope and should be substituted # using an 'identity' relation (else we'll get a conflict of # temporary tables) - if rhsvar in terms and not lhsvar in terms: + if rhsvar in terms and not lhsvar in terms and lhsvar.scope is lhsvar.stmt: self._identity_substitute(rel, lhsvar, terms, needsel) - elif lhsvar in terms and not rhsvar in terms: + elif lhsvar in terms and not rhsvar in terms and rhsvar.scope is rhsvar.stmt: self._identity_substitute(rel, rhsvar, terms, needsel) def _identity_substitute(self, relation, var, terms, needsel): newvar = self._insert_identity_variable(relation.scope, var) - if newvar is not None: - # ensure relation is using '=' operator, else we rely on a - # sqlgenerator side effect (it won't insert an inequality operator - # in this case) - relation.children[1].operator = '=' - terms.append(newvar) - needsel.add(newvar.name) + # ensure relation is using '=' operator, else we rely on a + # sqlgenerator side effect (it won't insert an inequality operator + # in this case) + relation.children[1].operator = '=' + terms.append(newvar) + needsel.add(newvar.name) def _choose_term(self, sourceterms): """pick one term among terms supported by a source, which will be used @@ -1418,7 +1416,7 @@ return False if not var in terms or used_in_outer_scope(var, self.current_scope): return False - if any(v for v, _ in var.stinfo['attrvars'] if not v in terms): + if any(v for v, _ in var.stinfo.get('attrvars', ()) if not v in terms): return False return True diff -r 55e2602545cd -r b3b0b808a0ed server/mssteps.py --- a/server/mssteps.py Thu Mar 25 14:25:44 2010 +0100 +++ b/server/mssteps.py Thu Mar 25 14:26:13 2010 +0100 @@ -61,7 +61,7 @@ if not isinstance(vref, VariableRef): continue var = vref.variable - if var.stinfo['attrvars']: + if var.stinfo.get('attrvars'): for lhsvar, rtype in var.stinfo['attrvars']: if lhsvar.name in srqlst.defined_vars: key = '%s.%s' % (lhsvar.name, rtype) diff -r 55e2602545cd -r b3b0b808a0ed server/querier.py --- a/server/querier.py Thu Mar 25 14:25:44 2010 +0100 +++ b/server/querier.py Thu Mar 25 14:26:13 2010 +0100 @@ -319,16 +319,9 @@ varkwargs = {} if not session.transaction_data.get('security-rqlst-cache'): for var in rqlst.defined_vars.itervalues(): - for rel in var.stinfo['uidrels']: - const = rel.children[1].children[0] - try: - varkwargs[var.name] = typed_eid(const.eval(self.args)) - break - except AttributeError: - #from rql.nodes import Function - #assert isinstance(const, Function) - # X eid IN(...) - pass + if var.stinfo['constnode'] is not None: + eid = var.stinfo['constnode'].eval(self.args) + varkwargs[var.name] = typed_eid(eid) # dictionnary of variables restricted for security reason localchecks = {} restricted_vars = set() diff -r 55e2602545cd -r b3b0b808a0ed server/rqlannotation.py --- a/server/rqlannotation.py Thu Mar 25 14:25:44 2010 +0100 +++ b/server/rqlannotation.py Thu Mar 25 14:26:13 2010 +0100 @@ -38,7 +38,7 @@ stinfo['invariant'] = False stinfo['principal'] = _select_main_var(stinfo['rhsrelations']) continue - if not stinfo['relations'] and not stinfo['typerels']: + if not stinfo['relations'] and stinfo['typerel'] is None: # Any X, Any MAX(X)... # those particular queries should be executed using the system # entities table unless there is some type restriction @@ -80,7 +80,7 @@ continue rschema = getrschema(rel.r_type) if rel.optional: - if rel in stinfo['optrelations']: + if rel in stinfo.get('optrelations', ()): # optional variable can't be invariant if this is the lhs # variable of an inlined relation if not rel in stinfo['rhsrelations'] and rschema.inlined: @@ -296,7 +296,7 @@ def compute(self, rqlst): # set domains for each variable for varname, var in rqlst.defined_vars.iteritems(): - if var.stinfo['uidrels'] or \ + if var.stinfo['uidrel'] is not None or \ self.eschema(rqlst.solutions[0][varname]).final: ptypes = var.stinfo['possibletypes'] else: @@ -339,7 +339,11 @@ def set_rel_constraint(self, term, rel, etypes_func): if isinstance(term, VariableRef) and self.is_ambiguous(term.variable): var = term.variable - if len(var.stinfo['relations'] - var.stinfo['typerels']) == 1 \ + if var.stinfo['typerel'] is not None: + sub = 1 + else: + sub = 0 + if len(var.stinfo['relations']) - sub == 1 \ or rel.sqlscope is var.sqlscope or rel.r_type == 'identity': self.restrict(var, frozenset(etypes_func())) try: @@ -356,7 +360,7 @@ if isinstance(other, VariableRef) and isinstance(other.variable, Variable): deambiguifier = other.variable if not var is self.deambification_map.get(deambiguifier): - if not var.stinfo['typerels']: + if var.stinfo['typerel'] is None: otheretypes = deambiguifier.stinfo['possibletypes'] elif not self.is_ambiguous(deambiguifier): otheretypes = self.varsols[deambiguifier] @@ -364,7 +368,7 @@ # we know variable won't be invariant, try to use # it to deambguify the current variable otheretypes = self.varsols[deambiguifier] - if not deambiguifier.stinfo['typerels']: + if deambiguifier.stinfo['typerel'] is None: # if deambiguifier has no type restriction using 'is', # don't record it deambiguifier = None diff -r 55e2602545cd -r b3b0b808a0ed server/sources/rql2sql.py --- a/server/sources/rql2sql.py Thu Mar 25 14:25:44 2010 +0100 +++ b/server/sources/rql2sql.py Thu Mar 25 14:26:13 2010 +0100 @@ -87,7 +87,7 @@ modified = False for varname in tuple(unstable): var = select.defined_vars[varname] - if not var.stinfo['optrelations']: + if not var.stinfo.get('optrelations'): continue modified = True unstable.remove(varname) @@ -114,13 +114,13 @@ var.stinfo['relations'].remove(rel) newvar.stinfo['relations'].add(newrel) if rel.optional in ('left', 'both'): - newvar.stinfo['optrelations'].add(newrel) + newvar.add_optional_relation(newrel) for vref in newrel.children[1].iget_nodes(VariableRef): var = vref.variable var.stinfo['relations'].add(newrel) var.stinfo['rhsrelations'].add(newrel) if rel.optional in ('right', 'both'): - var.stinfo['optrelations'].add(newrel) + var.add_optional_relation(newrel) # extract subquery solutions mysolutions = [sol.copy() for sol in solutions] cleanup_solutions(newselect, mysolutions) @@ -888,7 +888,7 @@ condition = '%s=%s' % (lhssql, rhsconst.accept(self)) if relation.r_type != 'identity': condition = '(%s OR %s IS NULL)' % (condition, lhssql) - if not lhsvar.stinfo['optrelations']: + if not lhsvar.stinfo.get('optrelations'): return condition self.add_outer_join_condition(lhsvar, t1, condition) return @@ -987,7 +987,7 @@ sql = '%s%s' % (lhssql, rhssql) except AttributeError: sql = '%s%s' % (lhssql, rhssql) - if lhs.variable.stinfo['optrelations']: + if lhs.variable.stinfo.get('optrelations'): self.add_outer_join_condition(lhs.variable, table, sql) else: return sql @@ -1002,7 +1002,7 @@ lhsvar = lhs.variable me_is_principal = lhsvar.stinfo.get('principal') is rel if me_is_principal: - if not lhsvar.stinfo['typerels']: + if lhsvar.stinfo['typerel'] is None: # the variable is using the fti table, no join needed jointo = None elif not lhsvar.name in self._varmap: @@ -1135,7 +1135,7 @@ vtablename = '_' + variable.name self.add_table('entities AS %s' % vtablename, vtablename) sql = '%s.eid' % vtablename - if variable.stinfo['typerels']: + if variable.stinfo['typerel'] is not None: # add additional restriction on entities.type column pts = variable.stinfo['possibletypes'] if len(pts) == 1: @@ -1297,7 +1297,7 @@ tablealias = self._state.outer_tables[table] actualtables = self._state.actual_tables[-1] except KeyError: - for rel in var.stinfo['optrelations']: + for rel in var.stinfo.get('optrelations'): self.visit_relation(rel) assert self._state.outer_tables self.add_outer_join_condition(var, table, condition) diff -r 55e2602545cd -r b3b0b808a0ed server/test/unittest_rql2sql.py --- a/server/test/unittest_rql2sql.py Thu Mar 25 14:25:44 2010 +0100 +++ b/server/test/unittest_rql2sql.py Thu Mar 25 14:26:13 2010 +0100 @@ -1605,8 +1605,8 @@ class removeUnsusedSolutionsTC(TestCase): def test_invariant_not_varying(self): rqlst = mock_object(defined_vars={}) - rqlst.defined_vars['A'] = mock_object(scope=rqlst, stinfo={'optrelations':False}, _q_invariant=True) - rqlst.defined_vars['B'] = mock_object(scope=rqlst, stinfo={'optrelations':False}, _q_invariant=False) + rqlst.defined_vars['A'] = mock_object(scope=rqlst, stinfo={}, _q_invariant=True) + rqlst.defined_vars['B'] = mock_object(scope=rqlst, stinfo={}, _q_invariant=False) self.assertEquals(remove_unused_solutions(rqlst, [{'A': 'RugbyGroup', 'B': 'RugbyTeam'}, {'A': 'FootGroup', 'B': 'FootTeam'}], {}, None), ([{'A': 'RugbyGroup', 'B': 'RugbyTeam'}, @@ -1616,8 +1616,8 @@ def test_invariant_varying(self): rqlst = mock_object(defined_vars={}) - rqlst.defined_vars['A'] = mock_object(scope=rqlst, stinfo={'optrelations':False}, _q_invariant=True) - rqlst.defined_vars['B'] = mock_object(scope=rqlst, stinfo={'optrelations':False}, _q_invariant=False) + rqlst.defined_vars['A'] = mock_object(scope=rqlst, stinfo={}, _q_invariant=True) + rqlst.defined_vars['B'] = mock_object(scope=rqlst, stinfo={}, _q_invariant=False) self.assertEquals(remove_unused_solutions(rqlst, [{'A': 'RugbyGroup', 'B': 'RugbyTeam'}, {'A': 'FootGroup', 'B': 'RugbyTeam'}], {}, None), ([{'A': 'RugbyGroup', 'B': 'RugbyTeam'}], {}, set()) diff -r 55e2602545cd -r b3b0b808a0ed web/facet.py --- a/web/facet.py Thu Mar 25 14:25:44 2010 +0100 +++ b/web/facet.py Thu Mar 25 14:26:13 2010 +0100 @@ -8,7 +8,6 @@ """ __docformat__ = "restructuredtext en" -from itertools import chain from copy import deepcopy from datetime import date, datetime, timedelta @@ -199,7 +198,7 @@ # add attribute variable to selection rqlst.add_selected(attrvar) # add is restriction if necessary - if not mainvar.stinfo['typerels']: + if mainvar.stinfo['typerel'] is None: etypes = frozenset(sol[mainvar.name] for sol in rqlst.solutions) rqlst.add_type_restriction(mainvar, etypes) return var @@ -228,12 +227,16 @@ for ovarname in linkedvars: vargraph[ovarname].remove(trvarname) # remove relation using this variable - for rel in chain(trvar.stinfo['relations'], trvar.stinfo['typerels']): + for rel in trvar.stinfo['relations']: if rel in removed: # already removed continue rqlst.remove_node(rel) removed.add(rel) + rel = trvar.stinfo['typerel'] + if rel is not None and not rel in removed: + rqlst.remove_node(rel) + removed.add(rel) # cleanup groupby clause if rqlst.groupby: for vref in rqlst.groupby[:]: